/* ** 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 . */ /*********************************************************************************************** *** 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/SceneEditor.cpp $* * * * Author:: Patrick Smith * * * * $Modtime:: 3/12/02 3:31p $* * * * $Revision:: 116 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "stdafx.h" #include "sceneeditor.h" #include "nodemgr.h" #include "groupmgr.h" #include "utils.h" #include "matrix3d.h" #include "vector.h" #include "leveleditdoc.h" #include "filemgr.h" #include "cameramgr.h" #include "leveleditview.h" #include "hittestinfo.h" #include "collisiongroups.h" #include "mainfrm.h" #include "wwaudio.h" #include "audiblesound.h" #include "mesh.h" #include "staticphys.h" #include "phys3.h" #include "_assetmgr.h" #include "editorassetmgr.h" #include "humanphys.h" #include "pathfindsectorbuilder.h" #include "pathfind.h" #include "node.h" #include "mover.h" #include "nodefunction.h" #include "chunkio.h" #include "decophys.h" #include "VisPointNode.h" #include "editorchunkids.h" #include "presetmgr.h" #include "combat.h" #include "ccamera.h" #include "visenum.h" #include "staticaabtreecull.h" #include "dynamicaabtreecull.h" #include "soundscene.h" #include "backgroundmgr.h" #include "texture.h" #include "smartgameobj.h" #include "objectnode.h" #include "mousemgr.h" #include "lightnode.h" #include "light.h" #include "euler.h" #include "w3d_file.h" #include "part_emt.h" #include "gameobjmanager.h" #include "selectpresetdialog.h" #include "actionparams.h" #include "generatingvisdialog.h" #include "generatingedgesampledvisdialog.h" #include "generatinglightvisdialog.h" #include "visgenprogress.h" #include "vissectorsampler.h" #include "vismgr.h" #include "generatingmanualvisdialog.h" #include "heightdb.h" #include "staticanimphys.h" #include "conversationmgr.h" #include "building.h" #include "buildingnode.h" #include "vistable.h" #include "rendobj.h" #include "pscene.h" #include "leveledit.h" #include "visrendercontext.h" #include "mapmgr.h" #include "editableheightfield.h" #include "heightfieldeditor.h" #include "heightfieldmgr.h" #include "pathmgr.h" ////////////////////////////////////////////////////////////////////// // // SceneEditorClass // ////////////////////////////////////////////////////////////////////// SceneEditorClass::SceneEditorClass (void) : m_SelectionMgr (NULL), m_bLightsOn (true), m_bAggregateChildrenVisible (true), m_bVisPointsDisplayed (0), m_TotalVisPoints (0), m_ManualVisPointsVisible (true), m_MovingObject (NULL), m_DisplaySoundSpheres (false), m_DisplayLightSpheres (false), m_ShowStaticAnimPhys (true), m_ShowEditorObjects (true), m_BuildingPowerEnabled (true), m_CreateProxies (true), PhysicsSceneClass () { // // Ensure that we are initialized // Initialize (); // // Set some default light settings // Enable_Sun_Light (true); Set_Ambient_Light (Vector3 (0.3F, 0.3F, 0.3F)); Set_Sun_Light_Orientation (0.0F, DEG_TO_RADF (50.0F)); Update_Lighting (); return ; } ////////////////////////////////////////////////////////////////////// // // ~SceneEditorClass // ////////////////////////////////////////////////////////////////////// SceneEditorClass::~SceneEditorClass (void) { Empty_Local_Clipboard (); Cleanup_Resources (); Remove_All_Vis_Points (); return ; } ////////////////////////////////////////////////////////////////////// // // Initialize // ////////////////////////////////////////////////////////////////////// void SceneEditorClass::Initialize (void) { // // Load the lighting mode from the registry // int prelit_mode = theApp.GetProfileInt ("Config", "PrelitMode", WW3D::PRELIT_MODE_LIGHTMAP_MULTI_PASS); WW3D::Set_Prelit_Mode ((WW3D::PrelitModeEnum)prelit_mode); // // Initialize some misc variables // ParticleEmitterClass::Set_Default_Remove_On_Complete (false); Cleanup_Resources (); m_ManualVisPointsVisible = true; m_BuildingPowerEnabled = true; PathfindClass::Get_Instance ()->Reset_Sectors (); // // Create the node and selection managers // m_SelectionMgr = new SelectionMgrClass; // // Reset our undo stack // m_UndoMgr.Purge_Buffers (); // // Set-up our collision groups // Disable_All_Collision_Detections (EDITOR_COLLISION_GROUP); Disable_All_Collision_Detections (DEF_COLLISION_GROUP); Enable_Collision_Detection (DEF_COLLISION_GROUP, DEF_COLLISION_GROUP); Enable_All_Collision_Detections (MOUSE_CLICK_COLLISION_GROUP); Enable_All_Collision_Detections (15); Disable_All_Collision_Detections (STATIC_OBJ_COLLISION_GROUP); Enable_Collision_Detection (STATIC_OBJ_COLLISION_GROUP, 15); Disable_Collision_Detection (MOUSE_CLICK_COLLISION_GROUP, 15); Enable_All_Collision_Detections (GAME_COLLISION_GROUP); Disable_Collision_Detection (GAME_COLLISION_GROUP, EDITOR_COLLISION_GROUP); // // Refresh the per-level include file list // STRING_LIST &level_includes =::Get_File_Mgr ()->Get_Include_File_List (); level_includes.Delete_All (); // // Reset the pathfind data // PathfindClass::Get_Instance ()->Reset_Sectors (); PathfindClass::Get_Instance ()->Reset_Portals (); // // Reset the list of conversations // ConversationMgrClass::Reset_Conversations (ConversationMgrClass::CATEGORY_LEVEL, true); // // Reset the map // MapMgrClass::Set_Map_Title (0); MapMgrClass::Set_Map_Texture (""); MapMgrClass::Set_Map_Center (Vector2 (0.0F, 0.0F)); MapMgrClass::Set_Map_Scale (Vector2 (0.0F, 0.0F)); MapMgrClass::Cloud_All_Cells (); // // Initialize the heightfield system // HeightfieldMgrClass::Initialize (); HeightfieldEditorClass::Initialize (); return ; } ////////////////////////////////////////////////////////////////////// // // Cleanup_Resources // ////////////////////////////////////////////////////////////////////// void SceneEditorClass::Cleanup_Resources (void) { // // Free the background sound // if (WWAudioClass::Get_Instance () != NULL) { WWAudioClass::Get_Instance ()->Set_Background_Music (NULL); } // // Cleanup the different managers // m_UndoMgr.Purge_Buffers (); SAFE_DELETE (m_SelectionMgr); NodeMgrClass::Free_Nodes (); NetworkObjectMgrClass::Delete_Pending (); // Remove all entries from the group list Reset_Global_Groups_List (); m_MovingObject = NULL; // // Shutdown the heightfield system // HeightfieldEditorClass::Shutdown (); HeightfieldMgrClass::Shutdown (); return ; } ////////////////////////////////////////////////////////////////////// // // Create_Node // ////////////////////////////////////////////////////////////////////// NodeClass * SceneEditorClass::Create_Node ( PresetClass * preset, Matrix3D * transform, DWORD node_id, bool add_to_scene ) { ::Get_Main_View ()->Allow_Repaint (false); CWaitCursor wait_cursor; // // Create the new node // NodeClass *node = NodeMgrClass::Create_Node (preset, node_id); ASSERT (node != NULL); if (node != NULL) { // // Find a good starting position for this node in the scene // if (transform != NULL) { node->Set_Transform (*transform); } else { MoverClass::Position_Node (node); } // // Add the new node to the scene // if (add_to_scene) { node->Add_To_Scene (); } // // Update the file manager's database // Update_File_Mgr (node); // // Repartition the static culling systems if we are dropping // in a terrain. // if (node->Get_Type () == NODE_TYPE_TERRAIN) { Re_Partition_Static_Lights (); Re_Partition_Audio_System (); } else if (node->Get_Type () == NODE_TYPE_WAYPATH) { ::Get_Mouse_Mgr ()->Set_Mouse_Mode (MouseMgrClass::MODE_WAYPATH_EDIT); MMWaypathEditClass *mode = (MMWaypathEditClass *)::Get_Mouse_Mgr ()->Get_Mode_Mgr (); if (mode != NULL) { mode->Set_Waypath ((WaypathNodeClass *)node); } } // // Create any linked nodes and put them into the scene // if (preset != NULL) { preset->Create_Linked_Nodes (node); } // // Release our hold on the node // node->Release_Ref (); } Set_Modified (true); ::Get_Main_View ()->Allow_Repaint (true); return node; } ////////////////////////////////////////////////////////////////////// // // Clone_Node // ////////////////////////////////////////////////////////////////////// NodeClass * SceneEditorClass::Clone_Node (NodeClass *node) { NodeClass *new_node = node->Clone (); if (new_node != NULL) { // // Make sure the new node has a name and ID // NodeMgrClass::Setup_Node_Identity (*new_node); NodeMgrClass::Add_Node (new_node); // // Add the new node to the scene // new_node->Add_To_Scene (); // // Update the file manager's database // Update_File_Mgr (new_node); } return new_node; } ////////////////////////////////////////////////////////////////////// // // Reload_Lightmap_Models // ////////////////////////////////////////////////////////////////////// void SceneEditorClass::Reload_Lightmap_Models (void) { // // Save the setting in the registry // theApp.WriteProfileInt ("Config", "PrelitMode", WW3D::Get_Prelit_Mode ()); // // Force free the assets... // WW3DAssetManager::Get_Instance ()->Free_Assets (); // // Reload the necessary "tool" assets // CLevelEditDoc *doc = ::Get_Current_Document (); if (doc != NULL) { doc->Preload_Human_Data (); doc->Preload_Tool_Assets (); } // // Reload all the nodes // NodeMgrClass::Reload_Nodes (); // // Make sure we don't toss vis // Validate_Vis (); return ; } ////////////////////////////////////////////////////////////////////// // // Add_Node // ////////////////////////////////////////////////////////////////////// void SceneEditorClass::Add_Node (NodeClass *node) { NodeMgrClass::Add_Node (node); node->Add_To_Scene (); Update_File_Mgr (node); return ; } ////////////////////////////////////////////////////////////////////// // // Delete_Node // ////////////////////////////////////////////////////////////////////// bool SceneEditorClass::Delete_Node (NodeClass *node, bool allow_undo) { // Add this operation to our undo manager if (allow_undo) { Begin_Operation (OPERATION_DELETE, node); } // // Remove all files that this node was dependent on from the file manager's list // Update_File_Mgr (node, false); // // Remove the node from the level // node->Remove_From_Scene (); node->Show_Selection_Box (false); node->On_Delete (); NodeMgrClass::Remove_Node (node); // Notify the undo manager that this operation is over if (allow_undo) { End_Operation (); } Set_Modified (true); return true; } ////////////////////////////////////////////////////////////////////// // // Delete_Nodes // ////////////////////////////////////////////////////////////////////// bool SceneEditorClass::Delete_Nodes (PresetClass *preset) { bool retval = true; // // Build a list of nodes to that are dependent on this preset // DynamicVectorClass node_removal_list; for ( NodeClass *node = NodeMgrClass::Get_First (); node != NULL; node = NodeMgrClass::Get_Next (node)) { if (node->Get_Preset () == preset) { node_removal_list.Add (node); } } // // Remove these nodes from the level // for (int index = 0; index < node_removal_list.Count (); index ++) { retval &= Delete_Node (node_removal_list[index], false); } return retval; } ////////////////////////////////////////////////////////////////////// // // Update_File_Mgr // ////////////////////////////////////////////////////////////////////// void SceneEditorClass::Update_File_Mgr ( NodeClass * node, bool add_node ) { ::Get_File_Mgr ()->Update (node, add_node); return ; } ////////////////////////////////////////////////////////////////////// // // Clone_Objects // ////////////////////////////////////////////////////////////////////// bool SceneEditorClass::Clone_Objects (void) { m_SelectionMgr->Clone_Group (); // // Loop through all the objects in the current selection // and update the file manager's information based on them // int count = m_SelectionMgr->Get_Count (); for (int index = 0; index < count; index ++) { NodeClass *node = m_SelectionMgr->Get_At (index); Update_File_Mgr (node); } Set_Modified (); return true; } ////////////////////////////////////////////////////////////////////// // // Add_Groups_To_List // ////////////////////////////////////////////////////////////////////// void SceneEditorClass::Add_Groups_To_List ( NodeClass & node, GROUP_LIST &group_list ) { /*group_list += node.Get_Groups (); // Loop through all the children of this node and // add their groups to the list as well for (NodeInstanceClass *child = node.Get_Child (); child != NULL; child = child->Get_Sibling ()) { Add_Groups_To_List (*child, group_list); }*/ return ; } ////////////////////////////////////////////////////////////////////// // // Build_Group_List // ////////////////////////////////////////////////////////////////////// void SceneEditorClass::Build_Group_List ( GROUP_LIST & group_list, NodeClass * node ) { if (node != NULL) { Add_Groups_To_List (*node, group_list); } else { // // Loop through all the objects in the current selection // and add thier groups to this list // for (int index = 0; index < m_SelectionMgr->Get_Count (); index ++) { NodeClass *node = m_SelectionMgr->Get_At (index); if (node != NULL) { Add_Groups_To_List (*node, group_list); } } } return ; } ////////////////////////////////////////////////////////////////////// // // Add_Nodes_To_List // ////////////////////////////////////////////////////////////////////// void SceneEditorClass::Add_Nodes_To_List ( NodeClass &node, NODE_LIST &node_list ) { // // Loop through all the children of this node and // add them and their children to the list as well // /*for (NodeInstanceClass *child = node.Get_Child (); child != NULL; child = child->Get_Sibling ()) { if (child->Is_Owner_Only () == false) { node_list.Add_Unique (child); } Add_Nodes_To_List (*child, node_list); }*/ return ; } ////////////////////////////////////////////////////////////////////// // // Build_Node_List // ////////////////////////////////////////////////////////////////////// Vector3 SceneEditorClass::Build_Node_List (NODE_LIST &node_list) { // // Add all the nodes from the selection set to the list // for (int index = 0; index < m_SelectionMgr->Get_Count (); index ++) { NodeClass *node = m_SelectionMgr->Get_At (index); if (node != NULL) { node_list.Add_Unique (node); } } m_SelectionMgr->Set_Dirty (); return m_SelectionMgr->Get_Center (); } ////////////////////////////////////////////////////////////////////// // // Build_Node_List // ////////////////////////////////////////////////////////////////////// void SceneEditorClass::Build_Node_List ( NODE_LIST & node_list, NodeClass * parent_node ) { // // Build a complete list of all the groups in the selection set // GROUP_LIST group_list; Build_Group_List (group_list, parent_node); // Loop through all the objects of all the groups in the selection // set and add them to the list for (int group = 0; group < group_list.Count (); group ++) { GroupMgrClass *group_mgr = group_list[group]; group_mgr->Set_Dirty (); for (int index = 0; index < group_mgr->Get_Count (); index ++) { NodeClass *node = group_mgr->Get_At (index); if (node != NULL) { node_list.Add_Unique (node); Add_Nodes_To_List (*node, node_list); } } } // // Don't forget to add the nodes from the selection group as well! // if (parent_node == NULL) { for (int index = 0; index < m_SelectionMgr->Get_Count (); index ++) { NodeClass *node = m_SelectionMgr->Get_At (index); if (node != NULL) { node_list.Add_Unique (node); Add_Nodes_To_List (*node, node_list); } } } else { /*if (parent_node->Is_Owner_Only () == false) { node_list.Add_Unique (pparent_node); } Add_Nodes_To_List (*pparent_node, node_list);*/ } return ; } /////////////////////////////////////////////////////////////// // // Delete_Nodes // /////////////////////////////////////////////////////////////// void SceneEditorClass::Delete_Nodes (void) { NODE_LIST affected_list; // // Build a list of all the nodes that are affected by this operation // for (int index = 0; index < m_SelectionMgr->Get_Count (); index ++) { NodeClass *node = m_SelectionMgr->Get_At (index); if (node != NULL) { // Add this node to our list of affected nodes affected_list.Add_Unique (node); } } // Add this operation to our undo manager Begin_Operation (OPERATION_DELETE, &affected_list); // // Perfrom the actual delete operation // for (index = 0; index < affected_list.Count (); index ++) { Delete_Node (affected_list[index], false); } // Notify the undo manager that this operation is over End_Operation (); // Reset the contents of the selection. // Note: We create a temporary group here to keep a refcount // locked on the selected nodes while we delete the selection group. // Remember, freeing the selection group decrements the node's refcounts. //GroupMgrClass temp_group = *m_SelectionMgr; m_SelectionMgr->Free_List (); return ; } /////////////////////////////////////////////////////////////// // // Set_Selection // /////////////////////////////////////////////////////////////// void SceneEditorClass::Set_Selection (NodeClass *node) { // Is this item already in the list? if (m_SelectionMgr->Is_Item_In_Group (node) == false) { // // Make this node the only selected node // m_SelectionMgr->Reset (); m_SelectionMgr->Add_Node (node); Update_Toolbars (); } return ; } /////////////////////////////////////////////////////////////// // // Toggle_Selection // /////////////////////////////////////////////////////////////// void SceneEditorClass::Toggle_Selection (NodeClass *node) { // // If this node isn't already in the selection set, then // add it. However, if its in the selection set, remove it. // if (m_SelectionMgr->Is_Item_In_Group (node)) { m_SelectionMgr->Remove_Node (node); } else { m_SelectionMgr->Add_Node (node); } Update_Toolbars (); return ; } /////////////////////////////////////////////////////////////// // // Update_Toolbars // /////////////////////////////////////////////////////////////// void SceneEditorClass::Update_Toolbars (void) { bool show_ani_toolbar = false; int frame = 0; int max_frames = 0; if (m_SelectionMgr->Get_Count () > 0) { // // Loop through all the objects in the current selection // and output their names to the debug window... // CString selection_set = "Selection set: "; int sel_count = min (m_SelectionMgr->Get_Count (), 4); for (int index = 0; index < sel_count; index ++) { NodeClass *node = m_SelectionMgr->Get_At (index); selection_set += node->Get_Name (); // // Print vis-id and occluder status if we're only printing one object // PhysClass *phys_obj = node->Peek_Physics_Obj (); if (m_SelectionMgr->Get_Count () == 1 && phys_obj != NULL) { StaticPhysClass *static_phys_obj = phys_obj->As_StaticPhysClass(); if (static_phys_obj != NULL) { selection_set += (" ("); CString visid_string; visid_string.Format(" VisObjectId = %d, ",static_phys_obj->Get_Vis_Object_ID()); selection_set += visid_string; visid_string.Format(" VisSectorId = %d, ",static_phys_obj->Get_Vis_Sector_ID()); selection_set += visid_string; AABTreeLinkClass *link = (AABTreeLinkClass *)static_phys_obj->Get_Cull_Link (); if (link != NULL) { visid_string.Format(" CullLink = %d, ",link->Node->Index); selection_set += visid_string; } if (!static_phys_obj->Is_Occluder()) { selection_set += CString(" Non-Occluder "); } selection_set += (")"); } else if (node->Get_Type () == NODE_TYPE_LIGHT) { selection_set += (" ("); CString visid_string; visid_string.Format(" VisSectorId = %d, ",((LightNodeClass *)node)->Get_Vis_Sector_ID()); selection_set += visid_string; selection_set += (")"); } else { selection_set += (" ("); CString visid_string; visid_string.Format(" VisObjectId = %d, ",phys_obj->Get_Vis_Object_ID()); selection_set += visid_string; selection_set += (")"); } } selection_set += ", "; } if (sel_count < m_SelectionMgr->Get_Count ()) { selection_set += "and many more...."; } // // Now output this string to the debug window // selection_set += "\r\n"; ::Output_Message (selection_set); } // Show/hide the animation toolbar depending on the current state CMainFrame *pmainwnd = (CMainFrame *)::AfxGetMainWnd (); if (pmainwnd != NULL) { pmainwnd->Show_Ani_Toolbar (show_ani_toolbar); pmainwnd->Update_Ani_Frame (frame, max_frames); } return ; } /////////////////////////////////////////////////////////////// // // Find_Node_At_Point // /////////////////////////////////////////////////////////////// NodeClass * SceneEditorClass::Find_Node_At_Point (CPoint point, Vector3 *intersect_pt) { // // Calculate the ray from the current camera pos through // the mouse cursor. // Vector3 ray_start; Vector3 ray_end; MoverClass::Get_Mouse_Ray (point, 250.0F, ray_start, ray_end); // // Cast the ray into the world // CastResultStruct res; if (intersect_pt != NULL) { res.ComputeContactPoint = true; } PhysClass *physobj = MoverClass::Cast_Ray (res, ray_start, ray_end, MOUSE_CLICK_COLLISION_GROUP); if ((physobj != NULL) && (physobj->Peek_Model() != NULL)) { WWDEBUG_SAY(("Picked model: %s\r\n",physobj->Peek_Model()->Get_Name())); } // // Get the node's pointer that was hit from the raycast // NodeClass *node = NULL; if (physobj != NULL) { HITTESTINFO *hit_info = (HITTESTINFO *)physobj->Peek_Model ()->Get_User_Data (); if (hit_info != NULL && hit_info->Type == HITTESTINFO::Node) { node = hit_info->node; // // Return the intersection point (if requested) // if (intersect_pt != NULL) { (*intersect_pt) = res.ContactPoint; } } } // Return a pointer to the node return node; } /////////////////////////////////////////////////////////////// // // Execute_Function_At_Point // /////////////////////////////////////////////////////////////// bool SceneEditorClass::Execute_Function_At_Point (CPoint point) { bool processed = false; // // Calculate the ray from the current camera pos through // the mouse cursor. // Vector3 ray_start; Vector3 ray_end; MoverClass::Get_Mouse_Ray (point, 250.0F, ray_start, ray_end); // // Cast the ray into the world // CastResultStruct res; PhysClass *physobj = MoverClass::Cast_Ray (res, ray_start, ray_end, MOUSE_CLICK_COLLISION_GROUP); // // Determine if the user clicked on a 'function' // NodeClass *node = NULL; if (physobj != NULL) { HITTESTINFO *hit_info = (HITTESTINFO *)physobj->Peek_Model ()->Get_User_Data (); if ((hit_info != NULL) && hit_info->Type == HITTESTINFO::Function) { hit_info->function->On_Click (); processed = true; } } // Return the true/false result code return processed; } /////////////////////////////////////////////////////////////// // // Move_Selected_Nodes // /////////////////////////////////////////////////////////////// void SceneEditorClass::Move_Selected_Nodes (void) { float speed_modifier = ::Get_Camera_Mgr ()->Get_Speed_Modifier (); float modifier = (::GetAsyncKeyState (VK_CONTROL) < 0) ? speed_modifier : 1.0F; Matrix3D coord_system = ::Get_Camera_Mgr ()->Get_Camera ()->Get_Transform (); bool move_object = false; Vector3 delta (0, 0, 0); if (::GetAsyncKeyState (VK_LEFT) < 0) { // // Move the objects along the x-axis of the current view // Vector3 movement (-0.05F * modifier, 0, 0); delta += coord_system.Rotate_Vector (movement); move_object = true; } else if (::GetAsyncKeyState (VK_RIGHT) < 0) { // // Move the objects along the x-axis of the current view // Vector3 movement (0.05F * modifier, 0, 0); delta += coord_system.Rotate_Vector (movement); move_object = true; } if (::GetAsyncKeyState (VK_UP) < 0) { // // Should we move the object 'up'? // if (::GetAsyncKeyState (VK_SHIFT) < 0) { delta += Vector3 (0, 0, 0.05F * modifier); } else { // // Move the object along the 'ground-plane' at an axis orthogonal to the view's x-axis. // Vector3 y_vector = Vector3::Cross_Product (coord_system.Get_X_Vector (), Vector3 (0, 0, 1)); y_vector.Normalize (); delta += y_vector * (-0.05F * modifier); } move_object = true; } else if (::GetAsyncKeyState (VK_DOWN) < 0) { // // Should we move the object 'up'? // if (::GetAsyncKeyState (VK_SHIFT) < 0) { delta += Vector3 (0, 0, -0.05F * modifier); } else { // // Move the object along the 'ground-plane' at an axis orthogonal to the view's x-axis. // Vector3 y_vector = Vector3::Cross_Product (coord_system.Get_X_Vector (), Vector3 (0, 0, 1)); y_vector.Normalize (); delta += y_vector * (0.05F * modifier); } move_object = true; } // // Move the selected nodes if necessary // if (move_object) { MoverClass::Move_Nodes (delta); } return ; } /////////////////////////////////////////////////////////////// // // On_Frame // /////////////////////////////////////////////////////////////// void SceneEditorClass::On_Frame (void) { // // Let the heightfield editor think // HeightfieldEditorClass::On_Frame_Update (); // // Get the currently 'focussed' process // HWND current_focus_wnd = ::GetFocus (); DWORD process_id = 0; if (current_focus_wnd != NULL) { ::GetWindowThreadProcessId (current_focus_wnd, &process_id); } // // If we are the current process, then move the nodes // if (process_id == ::GetCurrentProcessId ()) { Move_Selected_Nodes (); } if (m_MovingObject != NULL) { PathMgrClass::Resolve_Paths (Vector3 (0, 0, 0)); PhysicalGameObj *game_obj = ((ObjectNodeClass *)m_MovingObject)->Peek_Game_Obj (); ((SmartGameObj *)game_obj)->Generate_Control (); game_obj->Think (); PhysClass *phys_obj = game_obj->Peek_Physical_Object (); if (phys_obj != NULL) { phys_obj->Timestep (TimeManager::Get_Frame_Seconds ()); } game_obj->Post_Think (); #if 0 ActionClass *action = ((SmartGameObj *)game_obj)->Get_Action (); if (action != NULL && action->Get_Movement () == NULL) { //m_MovingObject = NULL; } #endif } return ; } /////////////////////////////////////////////////////////////// // // Record_Vis_Info // /////////////////////////////////////////////////////////////// void SceneEditorClass::Record_Vis_Info (const Matrix3D &view_transform, const Vector3 &sample_location) { // Put up an hour glass CWaitCursor wait_cursor; PresetClass *preset = PresetMgrClass::Get_First (CLASSID_VIS_POINT_DEF, PresetMgrClass::ID_CLASS); if (preset == NULL) { CString message = "Missing 'Manual Vis Point' preset. Please create a default preset under the 'Manual Vis Point' folder in the preset library."; ::MessageBox (::AfxGetMainWnd ()->m_hWnd, message, "Missing Preset", MB_OK | MB_ICONEXCLAMATION); } else { // // When doing a manual vis, use the FOV and clip settings of the in-game camera // CameraClass *scene_cam = CombatManager::Get_Camera (); WWASSERT (scene_cam != NULL); float scene_znear = 0; float scene_zfar = 0; scene_cam->Get_Clip_Planes (scene_znear, scene_zfar); scene_cam->Set_Clip_Planes (scene_znear, VIS_FAR_CLIP); // // Generate the 'vis' information // VisSampleClass vis_sample = Update_Vis (view_transform * Vector3 (0, 0, -scene_znear), view_transform, (VisDirBitsType)(VIS_FORWARD_BIT | VIS_DONT_RECENTER), scene_cam); if (vis_sample.Get_Bits_Changed () > 0 && vis_sample.Sample_Rejected () == false) { // // Convert the matrix to an obj-space transform // Matrix3D cam_to_world (Vector3 (0, 0, -1), Vector3 (-1, 0, 0), Vector3 (0, 1, 0), Vector3 (0, 0, 0)); Matrix3D obj_tm = view_transform * cam_to_world; // // Create the UI // Create_Vis_Point (view_transform); VisPointNodeClass *vis_point = new VisPointNodeClass (preset); vis_point->Set_ID (::Get_Node_Mgr ().Get_Node_ID (vis_point->Get_Type ())); vis_point->Initialize (); vis_point->Add_To_Scene (); vis_point->Set_Transform (obj_tm); vis_point->Set_Vis_Tile_Location (sample_location); vis_point->Save_Camera_Settings (*scene_cam); vis_point->Hide (!m_ManualVisPointsVisible); ::Get_Node_Mgr ().Add_Node (vis_point); vis_point->Release_Ref (); Set_Modified (true); } // // Put the normal settings back into the scene camera // scene_cam->Set_Clip_Planes (scene_znear, scene_zfar); } return ; } /////////////////////////////////////////////////////////////// // // Begin_Operation // /////////////////////////////////////////////////////////////// void SceneEditorClass::Begin_Operation ( OPERATION_TYPE type, NodeClass * node ) { if (node != NULL) { // Build a list containing the node NODE_LIST node_list; node_list.Add_Unique (node); // Pass this list onto the undo manager Begin_Operation (type, &node_list); } return ; } /////////////////////////////////////////////////////////////// // // Begin_Operation // /////////////////////////////////////////////////////////////// void SceneEditorClass::Begin_Operation ( OPERATION_TYPE type, NODE_LIST * affected_list ) { NODE_LIST node_list; // // If necessary, build a complete list of all the nodes that will be // affected by this operation // if (affected_list == NULL) { Build_Node_List (node_list, NULL); affected_list = &node_list; } m_UndoMgr.Begin_Operation (type, *affected_list); return ; } /////////////////////////////////////////////////////////////// // // End_Operation // /////////////////////////////////////////////////////////////// void SceneEditorClass::End_Operation (void) { m_UndoMgr.End_Operation (); return ; } /////////////////////////////////////////////////////////////// // // Undo // /////////////////////////////////////////////////////////////// bool SceneEditorClass::Undo (void) { m_UndoMgr.Perform_Undo (); return true; } /////////////////////////////////////////////////////////////// // // Cut_Objects // /////////////////////////////////////////////////////////////// bool SceneEditorClass::Cut_Objects (void) { bool retval = false; // Turn painting off CLevelEditView::Allow_Repaint (false); // Copy the objects to the clipboard, then delete them if (Copy_Objects ()) { Delete_Nodes (); } // Turn painting back on CLevelEditView::Allow_Repaint (true); // Return the true/false result code return retval; } /////////////////////////////////////////////////////////////// // // Copy_Objects // /////////////////////////////////////////////////////////////// bool SceneEditorClass::Copy_Objects (void) { Empty_Local_Clipboard (); // Loop through all the nodes in the current selection // and 'copy' them. int count = m_SelectionMgr->Get_Count (); for (int index = 0; index < count; index ++) { NodeClass *node = m_SelectionMgr->Get_At (index); // // Copy the node and add it to our 'clipboard' // NodeClass *node_copy = node->Clone (); ASSERT (node_copy != NULL); if (node_copy != NULL) { node_copy->Remove_From_Scene (); m_LocalClipboard.Add (node_copy); } } return true; } /////////////////////////////////////////////////////////////// // // Paste_Objects // /////////////////////////////////////////////////////////////// bool SceneEditorClass::Paste_Objects (void) { bool retval = (m_LocalClipboard.Count () > 0); // Turn painting off CLevelEditView::Allow_Repaint (false); // Reset the selection group m_SelectionMgr->Reset (); for (int index = 0; index < m_LocalClipboard.Count (); index ++) { // // Copy the node on the clipboard and add it to the scene // NodeClass *node = m_LocalClipboard[index]; NodeClass *node_copy = Clone_Node (node); if (node_copy != NULL) { m_SelectionMgr->Add_Node (node_copy); MEMBER_RELEASE (node_copy); } } // // Cast the nodes along the line-of-sight ray for 500 meters // Vector3 ray_start; Vector3 ray_end; MoverClass::Get_LOS_Ray (500.0F, ray_start, ray_end); MoverClass::Position_Nodes_Along_Ray (ray_start, ray_end); // Turn painting back on CLevelEditView::Allow_Repaint (true); Update_Toolbars (); // Return the true/false result code return retval; } /////////////////////////////////////////////////////////////// // // Can_Paste // /////////////////////////////////////////////////////////////// bool SceneEditorClass::Can_Paste (void) { // Return the true/false result code return bool(m_LocalClipboard.Count () > 0); } /////////////////////////////////////////////////////////////// // // Empty_Local_Clipboard // /////////////////////////////////////////////////////////////// void SceneEditorClass::Empty_Local_Clipboard (void) { // Loop through all the entries in the clipboard and delete them for (int index = 0; index < m_LocalClipboard.Count (); index ++) { MEMBER_RELEASE (m_LocalClipboard[index]); } // Reset the list m_LocalClipboard.Delete_All (); return ; } /////////////////////////////////////////////////////////////// // // Add_Global_Group // /////////////////////////////////////////////////////////////// GroupMgrClass * SceneEditorClass::Add_Global_Group ( const CString & name, NODE_LIST * initial_list ) { // Create a new group with the speicfied name UserGroupMgrClass *group = new UserGroupMgrClass; group->Set_Name (name); // If we have a list of nodes to add to this group, // then do so now. if (initial_list != NULL) { for (int index = 0; index < initial_list->Count (); index ++) { group->Add_Node ((*initial_list)[index]); } } // Add this new group manager to our global list m_GroupsList.Add_Unique (group); // Return a pointer to the new group return group; } /////////////////////////////////////////////////////////////// // // Remove_Global_Group // /////////////////////////////////////////////////////////////// void SceneEditorClass::Remove_Global_Group (GroupMgrClass *group) { // If this group is in our global list, then remove // it from the list, remove it from any UI, and free its memory. if (m_GroupsList.Is_Item_In_List (group)) { m_GroupsList.Remove (group); Remove_Group_From_Toolbar (group); SAFE_DELETE (group); } return ; } /////////////////////////////////////////////////////////////// // // Reset_Global_Groups_List // /////////////////////////////////////////////////////////////// void SceneEditorClass::Reset_Global_Groups_List (void) { // Loop through all the groups in our list and // free their associated memory. for (int index = 0; index < m_GroupsList.Count (); index ++) { GroupMgrClass *group = m_GroupsList[index]; if (group != NULL) { Remove_Group_From_Toolbar (group); SAFE_DELETE (group); } } // Remove all the entries from the list m_GroupsList.Delete_All (); return ; } //////////////////////////////////////////////////////////////// // // Add_Group_To_Toolbar // //////////////////////////////////////////////////////////////// void SceneEditorClass::Add_Group_To_Toolbar (GroupMgrClass *group) { ASSERT (group != NULL); // Get the form representing the groups list, and add this group to it /*GroupsPageClass *group_page = ::Get_Groups_Form (); if (group_page != NULL) { group_page->Add_Group (group); }*/ return ; } //////////////////////////////////////////////////////////////// // // Remove_Group_From_Toolbar // //////////////////////////////////////////////////////////////// void SceneEditorClass::Remove_Group_From_Toolbar (GroupMgrClass *group) { ASSERT (group != NULL); // Get the form representing the groups list, and remove this group from it /*GroupsPageClass *group_page = ::Get_Groups_Form (); if (group_page != NULL) { group_page->Remove_Group (group); }*/ return ; } //////////////////////////////////////////////////////////////// // // Turn_Lights_On // //////////////////////////////////////////////////////////////// void SceneEditorClass::Turn_Lights_On (bool onoff) { m_bLightsOn = onoff; // // Loop through all the lights in the world // /*LightInstanceClass *plight = NULL; for (plight = (LightInstanceClass *)::Get_Node_Mgr ().Get_First (NODE_TYPE_LIGHT); plight != NULL; plight = (LightInstanceClass *)::Get_Node_Mgr ().Get_Next (NODE_TYPE_LIGHT)) { plight->Turn_Light_On (onoff); }*/ ::Refresh_Main_View (); return ; } //////////////////////////////////////////////////////////////// // // Lock_Nodes // //////////////////////////////////////////////////////////////// void SceneEditorClass::Lock_Nodes (bool lock) { // // Loop through all the nodes in the current selection // for (int index = 0; index < m_SelectionMgr->Get_Count (); index ++) { NodeClass *node = m_SelectionMgr->Get_At (index); if (node != NULL) { // // Lock the node, this keep the node from moving during // drag operations. // node->Lock (lock); } } return ; } //////////////////////////////////////////////////////////////// // // Set_Background_Music // //////////////////////////////////////////////////////////////// void SceneEditorClass::Set_Background_Music (LPCTSTR filename) { // // Stop the old sound // WWAudioClass::Get_Instance ()->Set_Background_Music (NULL); // // Remove the sound file from the file manager // if (m_BackgroundSoundFilename.GetLength () > 0) { CString old_path = ::Get_File_Mgr ()->Make_Full_Path (m_BackgroundSoundFilename); ::Get_File_Mgr ()->Remove_File (::Get_File_Mgr ()->Find_File (old_path)); m_BackgroundSoundFilename.Empty (); } // // Set the new background music // if ((filename != NULL) && (filename[0] != 0)) { CString path = ::Get_File_Mgr ()->Make_Full_Path (filename); if (::Get_File_Mgr ()->Does_File_Exist (path, true)) { // // Set the background music // ::Set_Current_Directory (::Strip_Filename_From_Path (path)); WWAudioClass::Get_Instance ()->Set_Background_Music (::Get_Filename_From_Path (path)); // // Add this sound to the file manager // ::Get_File_Mgr ()->Add_File (path); m_BackgroundSoundFilename = path; } } return ; } //////////////////////////////////////////////////////////////// // // View_Aggregate_Children // //////////////////////////////////////////////////////////////// void SceneEditorClass::View_Aggregate_Children (bool view) { // Did the state change? /*if (view != m_bAggregateChildrenVisible) { m_bAggregateChildrenVisible = view; // Make sure to reset the selection for this to work m_SelectionMgr->Reset (); // Update all the aggregates in the level ::Get_Node_Mgr ().Update_Aggregate_Instances (); }*/ return ; } //////////////////////////////////////////////////////////////// // // Display_Vis_Points // //////////////////////////////////////////////////////////////// void SceneEditorClass::Display_Vis_Points (bool onoff) { // Did the state change? if (onoff != m_bVisPointsDisplayed) { m_bVisPointsDisplayed = onoff; // // Show/hide all the vis points // for (int index = 0; index < m_VisPoints.Count (); index ++) { DecorationPhysClass *vis_point = m_VisPoints[index]; if (vis_point != NULL) { if (onoff) { Add_Dynamic_Object (vis_point); } else { Remove_Object (vis_point); } } } } return ; } //////////////////////////////////////////////////////////////// // // Create_Vis_Point // //////////////////////////////////////////////////////////////// void SceneEditorClass::Create_Vis_Point (const Matrix3D &transform) { // // Create a new vis-point marker at the location // DecorationPhysClass *new_point = new DecorationPhysClass; new_point->Set_Model_By_Name ("WAYSTART"); new_point->Set_Transform (transform); new_point->Set_Collision_Group (0); // // Add this new point to our list // m_VisPoints.Add (new_point); if (m_bVisPointsDisplayed) { Add_Dynamic_Object (new_point); } return ; } //////////////////////////////////////////////////////////////// // // Remove_All_Vis_Points // //////////////////////////////////////////////////////////////// void SceneEditorClass::Remove_All_Vis_Points (void) { ::Get_Main_View ()->Allow_Repaint (false); CWaitCursor wait_cursor; // // Build a list of all the vis points in the world // for (int index = 0; index < m_VisPoints.Count (); index ++) { DecorationPhysClass *vis_point = m_VisPoints[index]; if (vis_point != NULL) { Remove_Object (vis_point); vis_point->Release_Ref (); } } m_VisPoints.Delete_All (); ::Get_Main_View ()->Allow_Repaint (true); return ; } //////////////////////////////////////////////////////////////// // // Discard_Vis // //////////////////////////////////////////////////////////////// void SceneEditorClass::Discard_Vis (void) { m_VisLog.Reset_Log (); Remove_All_Vis_Points (); PhysicsSceneClass::Internal_Vis_Reset (); return ; } //////////////////////////////////////////////////////////////// // // Reset_Vis // //////////////////////////////////////////////////////////////// void SceneEditorClass::Reset_Vis (bool doitnow) { m_VisLog.Reset_Log (); Remove_All_Vis_Points (); PhysicsSceneClass::Reset_Vis (doitnow); return ; } //////////////////////////////////////////////////////////////// // // Internal_Vis_Reset // //////////////////////////////////////////////////////////////// void SceneEditorClass::Internal_Vis_Reset (void) { if (VisResetNeeded) { PhysicsSceneClass::Internal_Vis_Reset (); // // Ask all the nodes // NodeClass *node = NULL; for ( node = NodeMgrClass::Get_First (); node != NULL; node = NodeMgrClass::Get_Next (node)) { node->Update_Cached_Vis_IDs (); } } return ; } //////////////////////////////////////////////////////////////// // // Get_Vis_Camera_FOV // //////////////////////////////////////////////////////////////// void SceneEditorClass::Get_Vis_Camera_FOV (double &hfov, double &vfov) { CameraClass *vis_camera = Get_Vis_Camera (); WWASSERT (vis_camera != NULL); if (vis_camera != NULL) { hfov = vis_camera->Get_Horizontal_FOV (); vfov = vis_camera->Get_Vertical_FOV (); MEMBER_RELEASE (vis_camera); } else { hfov = 0; vfov = 0; } return ; } //////////////////////////////////////////////////////////////// // // Get_Vis_Camera_Clip_Planes // //////////////////////////////////////////////////////////////// void SceneEditorClass::Get_Vis_Camera_Clip_Planes (float &znear, float &zfar) { CameraClass *vis_camera = Get_Vis_Camera (); WWASSERT (vis_camera != NULL); if (vis_camera != NULL) { vis_camera->Get_Clip_Planes (znear, zfar); MEMBER_RELEASE (vis_camera); } else { znear = 0; zfar = 0; } return ; } //////////////////////////////////////////////////////////////// // // Get_Render_Camera // //////////////////////////////////////////////////////////////// CameraClass * SceneEditorClass::Get_Render_Camera (void) { CameraClass *camera = NULL; if (::Get_Camera_Mgr ()->Get_Camera_Mode () == CameraMgr::MODE_WALK_THROUGH) { camera = CombatManager::Get_Camera (); } else { camera = ::Get_Camera_Mgr ()->Get_Camera (); } return camera; } //////////////////////////////////////////////////////////////// // // Generate_Pathfind_Portals // //////////////////////////////////////////////////////////////// void SceneEditorClass::Generate_Pathfind_Portals (void) { CWaitCursor wait_cursor; // // Create a pathfind floodfiller object // PathfindSectorBuilderClass builder; builder.Allow_Water_Floodfill (true); builder.Initialize (); // // Find all the pathfind start-points in the level and add // them to the floodfiller // NodeClass *node = NULL; for ( node = NodeMgrClass::Get_First (); node != NULL; node = NodeMgrClass::Get_Next (node)) { if (node->Get_Type () == NODE_TYPE_PATHFIND_START) { builder.Add_Start_Point (node->Get_Transform ().Get_Translation ()); } } // // Perform the floodfill from each of the start locations // builder.Generate_Sectors (); builder.Compress_Sectors_For_Pathfind (); // // Build height data for flying vehicles // HeightDBClass::Generate (); // // Cleanup // builder.Shutdown (); return ; } //////////////////////////////////////////////////////////////// // // Export_VIS // //////////////////////////////////////////////////////////////// void SceneEditorClass::Export_VIS (LPCTSTR filename) { // // Create the file // HANDLE hfile = ::CreateFile (filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0L, NULL); ASSERT (hfile != INVALID_HANDLE_VALUE); if (hfile != INVALID_HANDLE_VALUE) { RawFileClass file_obj; file_obj.Attach (hfile); ChunkSaveClass chunk_save (&file_obj); // // Export the data to this file // Export_Vis_Data (chunk_save); } return ; } //////////////////////////////////////////////////////////////// // // Update_Selection_Boxes // //////////////////////////////////////////////////////////////// void SceneEditorClass::Update_Selection_Boxes (void) { // // Loop through all the nodes in the current selection // for (int index = 0; index < m_SelectionMgr->Get_Count (); index ++) { NodeClass *node = m_SelectionMgr->Get_At (index); if (node != NULL) { // // Ask the node to update its selection box (color and pos) // node->Update_Selection_Box (); } } return ; } //////////////////////////////////////////////////////////////// // // Re_Partition_Audio_System // //////////////////////////////////////////////////////////////// void SceneEditorClass::Re_Partition_Audio_System (void) { const AABoxClass & level_bounds = StaticCullingSystem->Get_Bounding_Box(); Vector3 min,max; min = level_bounds.Center - level_bounds.Extent; max = level_bounds.Center + level_bounds.Extent; SoundSceneClass *sound_scene = WWAudioClass::Get_Instance ()->Get_Sound_Scene (); if (sound_scene != NULL) { sound_scene->Re_Partition (min, max); } return ; } //////////////////////////////////////////////////////////////// // // Are_Manual_Vis_Points_Visible // //////////////////////////////////////////////////////////////// bool SceneEditorClass::Are_Manual_Vis_Points_Visible (void) { return m_ManualVisPointsVisible; } //////////////////////////////////////////////////////////////// // // Show_Manual_Vis_Points // //////////////////////////////////////////////////////////////// void SceneEditorClass::Show_Manual_Vis_Points (bool show_points) { if (m_ManualVisPointsVisible != show_points) { m_ManualVisPointsVisible = show_points; NodeClass *node = NULL; for ( node = NodeMgrClass::Get_First (); node != NULL; node = NodeMgrClass::Get_Next (node)) { if (node->Get_Type () == NODE_TYPE_VIS_POINT) { node->Hide (!m_ManualVisPointsVisible); } } } return ; } //////////////////////////////////////////////////////////////// // // DoObjectGoto // //////////////////////////////////////////////////////////////// void SceneEditorClass::DoObjectGoto (NodeClass *node1, NodeClass *node2) { SANITY_CHECK(node1 != NULL && node2 != NULL && node1->Get_Type () == NODE_TYPE_OBJECT) { return ; } // // Register all the waypaths with the pathfinding system // for ( NodeClass *node = NodeMgrClass::Get_First (NODE_TYPE_WAYPATH); node != NULL; node = NodeMgrClass::Get_Next (node, NODE_TYPE_WAYPATH)) { node->Pre_Export (); } // // Force the pathfind system to use any available waypath data // in its evaluation // PathfindClass::Get_Instance ()->Generate_Waypath_Sectors_And_Portals (); m_MovingObject = node1; PhysicalGameObj *game_obj = ((ObjectNodeClass *)m_MovingObject)->Peek_Game_Obj (); if (game_obj != NULL && game_obj->As_SmartGameObj () != NULL) { ActionClass *action = ((SmartGameObj *)game_obj)->Get_Action (); ActionParamsStruct parameters; parameters.MoveLocation = node2->Get_Transform ().Get_Translation (); parameters.MoveArrivedDistance = 3.0F; action->Goto( parameters ); } // // Unregister all the waypaths with the pathfinding system // for ( node = NodeMgrClass::Get_First (NODE_TYPE_WAYPATH); node != NULL; node = NodeMgrClass::Get_Next (node, NODE_TYPE_WAYPATH)) { node->Post_Export (); } return ; } //////////////////////////////////////////////////////////////// // // Export_Lights // //////////////////////////////////////////////////////////////// void SceneEditorClass::Export_Lights (LPCTSTR filename) { FileClass *file = _TheFileFactory->Get_File (filename); if (file != NULL && file->Create () && file->Open (FileClass::WRITE)) { // // Write the first chunk header // ChunkSaveClass csave (file); csave.Begin_Chunk (W3D_CHUNK_LIGHTSCAPE); // // Loop over all the lights in the scene // for ( NodeClass *node = NodeMgrClass::Get_First (NODE_TYPE_LIGHT); node != NULL; node = NodeMgrClass::Get_Next (node, NODE_TYPE_LIGHT)) { LightNodeClass *light_node = (LightNodeClass *)node; // // Get the actual light from the editor object // LightClass *light = light_node->Peek_Light (); if (light != NULL) { // // Save this light // csave.Begin_Chunk (W3D_CHUNK_LIGHTSCAPE_LIGHT); light->Save_W3D (csave); // // Save the light's transform as well // csave.Begin_Chunk (W3D_CHUNK_LIGHT_TRANSFORM); Matrix3D tm = light->Get_Transform (); W3dLightTransformStruct w3d_tm ={ tm[0][0], tm[0][1], tm[0][2], tm[0][3], tm[1][0], tm[1][1], tm[1][2], tm[1][3], tm[2][0], tm[2][1], tm[2][2], tm[2][3] }; csave.Write (&w3d_tm, sizeof (w3d_tm)); csave.End_Chunk (); csave.End_Chunk (); } } csave.End_Chunk (); // // Close the file // file->Close (); } _TheFileFactory->Return_File (file); return ; } //////////////////////////////////////////////////////////////// // // Import_Lights // //////////////////////////////////////////////////////////////// void SceneEditorClass::Import_Lights ( DynamicVectorClass & filename_list, DynamicVectorClass * node_list ) { DynamicVectorClass light_list; ::Get_Main_View ()->Allow_Repaint (false); // // Build a list of light objects from the LIG files // for (int index = 0; index < filename_list.Count (); index ++) { FileClass *file = _TheFileFactory->Get_File (filename_list[index]); if (file != NULL && file->Open ()) { // // Read the lights from this file // ChunkLoadClass cload (file); int group_id = 0; while (cload.Open_Chunk ()) { switch (cload.Cur_Chunk_ID ()) { case W3D_CHUNK_LIGHTSCAPE: Build_Light_List (cload, light_list, group_id ++); break; default: ASSERT (0); TRACE ("Unrecognized chunk id found while importing lights!\n"); break; } cload.Close_Chunk (); } file->Close (); } // // Delete the file object // if (file != NULL) { _TheFileFactory->Return_File (file); } } // // Make sure we don't get duplicate lights // Filter_Lights (light_list); // // Get the preset we will use to derive each imported light-node from // PresetClass *preset = PresetMgrClass::Find_Preset ("Lightscape Imported"); ASSERT (preset != NULL); CString base_name = ::Asset_Name_From_Filename (filename_list[0]); // // Add the lights to the level // for (index = 0; index < light_list.Count (); index ++) { LightClass *light = light_list[index]; if (light != NULL) { // // Create an editor 'node' for this light and configure it from the // light render-object. // if (light->Get_Type () != LightClass::DIRECTIONAL) { Matrix3D tm = light->Get_Transform (); LightNodeClass *light_node = (LightNodeClass *)Create_Node (preset, &tm); light_node->Initialize_From_Light (light); light_node->Set_Group_ID ((int)light->Get_User_Data ()); CString light_name; light_name.Format ("%s%04d", (LPCTSTR)base_name, index + 1); light_node->Peek_Light_Phys ()->Set_Name (light_name); // // Add this light to the list the caller supplied us with (if necessary) // if (node_list != NULL) { node_list->Add (light_node); light_node->Add_Ref (); } } // // Free our hold on the light // MEMBER_RELEASE (light); } } ::Get_Main_View ()->Allow_Repaint (true); return ; } //////////////////////////////////////////////////////////////// // // Build_Light_List // //////////////////////////////////////////////////////////////// void SceneEditorClass::Build_Light_List ( ChunkLoadClass & cload, DynamicVectorClass & light_list, int group_id ) { while (cload.Open_Chunk ()) { switch (cload.Cur_Chunk_ID ()) { case W3D_CHUNK_LIGHTSCAPE_LIGHT: { // // Load the light and add it to the list // cload.Open_Chunk (); LightClass *light = new LightClass; light->Load_W3D (cload); light->Set_User_Data ((void *)group_id); light_list.Add (light); cload.Close_Chunk (); // // Read the light's transform from the file // cload.Open_Chunk (); ASSERT (cload.Cur_Chunk_ID () == W3D_CHUNK_LIGHT_TRANSFORM); if (cload.Cur_Chunk_ID () == W3D_CHUNK_LIGHT_TRANSFORM) { // // Read the transform // W3dLightTransformStruct w3d_tm = { 0 }; cload.Read (&w3d_tm, sizeof (w3d_tm)); // // Convert the persistent-safe transform to a // render-obj friendly transform; // Matrix3D tm ( w3d_tm.Transform [0][0], w3d_tm.Transform [0][1], w3d_tm.Transform [0][2], w3d_tm.Transform [0][3], w3d_tm.Transform [1][0], w3d_tm.Transform [1][1], w3d_tm.Transform [1][2], w3d_tm.Transform [1][3], w3d_tm.Transform [2][0], w3d_tm.Transform [2][1], w3d_tm.Transform [2][2], w3d_tm.Transform [2][3]); light->Set_Transform (tm); } cload.Close_Chunk (); } break; default: ::Output_Message ("Found invalid chunk in light importer.\r\n"); break; } cload.Close_Chunk (); } return ; } //////////////////////////////////////////////////////////////// // // Filter_Lights // //////////////////////////////////////////////////////////////// void SceneEditorClass::Filter_Lights (DynamicVectorClass &light_list) { // // Loop over every light // for (int index1 = 0; index1 < light_list.Count (); index1 ++) { LightClass *light1 = light_list[index1]; bool is_unique = true; // // Compare this light against every other light // for (int index2 = 0; index2 < light_list.Count () && is_unique; index2 ++) { // // Don't check against itself // if (index2 != index1) { LightClass *light2 = light_list[index2]; is_unique = (Compare_Lights (light1, light2) == false); } } // // Remove this light from the list if it is not unique // if (is_unique == false) { MEMBER_RELEASE (light1); light_list.Delete (index1); index1 --; } } return ; } //////////////////////////////////////////////////////////////// // // Display_Sound_Spheres // //////////////////////////////////////////////////////////////// void SceneEditorClass::Display_Sound_Spheres (bool onoff) { if (m_DisplaySoundSpheres != onoff) { m_DisplaySoundSpheres = onoff; // // Loop over all the sounds and tell them to turn the spheres // on or off. // for ( NodeClass *node = NodeMgrClass::Get_First (NODE_TYPE_SOUND); node != NULL; node = NodeMgrClass::Get_Next (node, NODE_TYPE_SOUND)) { node->Show_Attenuation_Spheres (m_DisplaySoundSpheres); } } return ; } //////////////////////////////////////////////////////////////// // // Display_Light_Spheres // //////////////////////////////////////////////////////////////// void SceneEditorClass::Display_Light_Spheres (bool onoff) { if (m_DisplayLightSpheres != onoff) { m_DisplayLightSpheres = onoff; // // Loop over all the lights and tell them to turn the spheres // on or off. // for ( NodeClass *node = NodeMgrClass::Get_First (NODE_TYPE_LIGHT); node != NULL; node = NodeMgrClass::Get_Next (node, NODE_TYPE_LIGHT)) { node->Show_Attenuation_Spheres (m_DisplayLightSpheres); } } return ; } //////////////////////////////////////////////////////////////// // // Import_Sunlight // //////////////////////////////////////////////////////////////// void SceneEditorClass::Import_Sunlight (LPCTSTR filename) { DynamicVectorClass light_list; // // Open the file // FileClass *file = _TheFileFactory->Get_File (filename); if (file != NULL && file->Open ()) { // // Read the lights from this file // ChunkLoadClass cload (file); cload.Open_Chunk (); ASSERT (cload.Cur_Chunk_ID () == W3D_CHUNK_LIGHTSCAPE); Build_Light_List (cload, light_list); cload.Close_Chunk (); file->Close (); _TheFileFactory->Return_File (file); } // // Try to find all the directional lights // LightClass *sunlight = NULL; bool more_than_one = false; for (int index = 0; index < light_list.Count () && !more_than_one; index ++) { LightClass *light = light_list[index]; if (light != NULL) { // // We assume that every directional light is the 'sunlight'. // if (light->Get_Type () == LightClass::DIRECTIONAL) { if (sunlight == NULL) { sunlight = light; } else { // // Check to make sure all directional lights are the same // if (Compare_Lights (sunlight, light) == false) { more_than_one = true; } MEMBER_RELEASE (light); } } } } bool apply_changes = (sunlight != NULL); if (more_than_one) { // // Ask the user if they wish to make these changes // if (::MessageBox ( ::AfxGetMainWnd ()->m_hWnd, "There are multiple sunlight definitions in this file which have different settings. Do you wish to continue?", "Ambiguous Lights", MB_ICONQUESTION | MB_YESNO) == IDNO) { apply_changes = false; } } // // Reconfigure the sunlight (if necessary) // if (apply_changes) { LightClass *global_sunlight_obj = Get_Sun_Light (); Vector3 diffuse; sunlight->Get_Diffuse (&diffuse); global_sunlight_obj->Set_Diffuse (diffuse); global_sunlight_obj->Set_Intensity (sunlight->Get_Intensity ()); global_sunlight_obj->Set_Transform (sunlight->Get_Transform ()); MEMBER_RELEASE (global_sunlight_obj); // // Convert the sun's 3x3 roational matrix to a // 'rotation' and 'elevation'. // Matrix3D tm = sunlight->Get_Transform (); Vector3 test_vector (0, 0, -1); test_vector = tm.Rotate_Vector (test_vector); test_vector.Normalize (); float rotation = ::atan2 (test_vector.Y, test_vector.X); float elevation = ::asin (test_vector.Z); Set_Sun_Light_Orientation (rotation, elevation); Update_Lighting (); } MEMBER_RELEASE (sunlight); return ; } //////////////////////////////////////////////////////////////// // // Compare_Lights // //////////////////////////////////////////////////////////////// bool SceneEditorClass::Compare_Lights (LightClass *light1, LightClass *light2) { bool retval = false; // // Are the positions and orientations approximately the same? // Are they both the same type of light? // const Matrix3D &tm1 = light1->Get_Transform (); const Matrix3D &tm2 = light2->Get_Transform (); if ( tm1 == tm2 && light1->Get_Type () == light2->Get_Type ()) { // // Are their color's and intensities approx the same? // Vector3 ambient1; Vector3 ambient2; Vector3 specular1; Vector3 specular2; Vector3 diffuse1; Vector3 diffuse2; light1->Get_Ambient (&ambient1); light2->Get_Ambient (&ambient2); light1->Get_Specular (&specular1); light2->Get_Specular (&specular2); light1->Get_Diffuse (&diffuse1); light2->Get_Diffuse (&diffuse2); float intensity1 = light1->Get_Intensity (); float intensity2 = light2->Get_Intensity (); if ( ambient1 == ambient2 && specular1 == specular2 && diffuse1 == diffuse2 && intensity1 == intensity2) { // // Are their attenuation ranges the same? // double inner1 = 0; double inner2 = 0; double outer1 = 0; double outer2 = 0; light1->Get_Far_Attenuation_Range (inner1, outer1); light2->Get_Far_Attenuation_Range (inner2, outer2); if (inner1 == inner2 && outer1 == outer2) { retval = true; } } } return retval; } //////////////////////////////////////////////////////////////// // // Replace_Selection // //////////////////////////////////////////////////////////////// void SceneEditorClass::Replace_Selection (void) { // // Is there a selection set to replace? // int count = m_SelectionMgr->Get_Count (); if (count > 0) { // // Configure a dialog that will allow the user to select a preset // SelectPresetDialogClass dialog (::AfxGetMainWnd ()); dialog.Set_Title ("Object Replace"); dialog.Set_Message ("Select the new preset you want to replace the selected objects with."); dialog.Allow_None_Selection (false); // // Let the user pick a preset // if (dialog.DoModal () == IDOK) { PresetClass *preset = dialog.Get_Selection (); DynamicVectorClass new_nodes; CWaitCursor wait_cursor; // // Loop over all the selected nodes // for (int index = 0; index < count; index ++) { // // Get the transform of this selected node // NodeClass *node = m_SelectionMgr->Get_At (index); Matrix3D tm = node->Get_Transform (); // // Create a new node with this same transform // NodeClass *new_node = Create_Node (preset, &tm, 0, true); new_node->Set_ID (node->Get_ID ()); new_nodes.Add (new_node); // // Test to see if both node's are game objects of some sort. // If they are, then we need to copy the scripts. // ObjectNodeClass *old_obj_node = node->As_ObjectNodeClass (); ObjectNodeClass *new_obj_node = new_node->As_ObjectNodeClass (); if (old_obj_node != NULL && new_obj_node != NULL) { new_obj_node->Copy_Scripts (*old_obj_node); } // // Delete the old node // Delete_Node (node, false); } // // Clear the selection // m_SelectionMgr->Reset (); // // Add the new nodes to the selection set // for (index = 0; index < new_nodes.Count (); index ++) { NodeClass *new_node = new_nodes[index]; m_SelectionMgr->Add_Node (new_node); } } } return ; } //////////////////////////////////////////////////////////////// // // Generate_Uniform_Sampled_Vis // //////////////////////////////////////////////////////////////// void SceneEditorClass::Generate_Uniform_Sampled_Vis ( float granularity, float sample_height, bool ignore_bias, bool selection_only, bool farm_mode, int farm_cpu_index, int farm_cpu_total ) { GeneratingVisDialogClass dialog (granularity, ::AfxGetMainWnd ()); dialog.Set_Sample_Height (sample_height); dialog.Set_Ignore_Bias (ignore_bias); dialog.Do_Selection_Only (selection_only); if (farm_mode) { dialog.Set_Farm_Mode (farm_cpu_index,farm_cpu_total); } dialog.DoModal (); return ; } //////////////////////////////////////////////////////////////// // // Generate_Edge_Sampled_Vis // //////////////////////////////////////////////////////////////// void SceneEditorClass::Generate_Edge_Sampled_Vis ( float granularity, bool ignore_bias, bool farm_mode, int farm_cpu_index, int farm_cpu_total ) { // // Build a list of nodes that should be vis-sampled // DynamicVectorClass node_list; VisMgrClass::Build_Node_List (node_list); // // Determine the first and last node that this cpu is to process. In farm // mode, the processing is split up amongst several computers... // int first_node = 0; int last_node = node_list.Count(); if (farm_mode) { int node_count = node_list.Count(); float nodes_per_processor = (float)node_count / (float)farm_cpu_total; first_node = (int)::floor (nodes_per_processor * (float)farm_cpu_index); last_node = (int)::ceil (nodes_per_processor * (float)(farm_cpu_index+1)); } VisGenProgressClass progress_obj; progress_obj.Set_Node_Count (last_node - first_node); GeneratingEdgeSampledVisDialogClass *dialog = GeneratingEdgeSampledVisDialogClass::Display (); dialog->Set_Progress_Obj (&progress_obj); dialog->Enable_Farm_Mode (farm_mode); // // Perform the per-node vis sampling // VisSectorSamplerClass sampler (this, &progress_obj, granularity, MOUSE_CLICK_COLLISION_GROUP); for ( int index = first_node; index < last_node && !progress_obj.Is_Cancel_Requested (); index ++) { NodeClass *node = node_list[index]; if (node != NULL) { // // Simply pass off the render object onto the sampler class // for point generation // RenderObjClass *render_obj = node->Peek_Render_Obj (); if (render_obj != NULL) { sampler.Process (render_obj); } } ::General_Pump_Messages (); } // // Now render any manual vis points // VisMgrClass::Render_Manual_Vis_Points (); dialog->Set_Progress_Obj (NULL); dialog->Set_Finished (true); return ; } //////////////////////////////////////////////////////////////// // // Generate_Light_Vis // //////////////////////////////////////////////////////////////// void SceneEditorClass::Generate_Light_Vis ( bool farm_mode, int farm_cpu_index, int farm_cpu_total ) { GeneratingLightVisDialogClass dialog (::AfxGetMainWnd ()); if (farm_mode) { dialog.Set_Farm_Mode (farm_cpu_index,farm_cpu_total); } dialog.DoModal (); return ; } //////////////////////////////////////////////////////////////// // // Generate_Manual_Vis // //////////////////////////////////////////////////////////////// void SceneEditorClass::Generate_Manual_Vis ( bool farm_mode, int farm_cpu_index, int farm_cpu_total ) { GeneratingManualVisDialogClass dialog (::AfxGetMainWnd ()); if (farm_mode) { dialog.Set_Farm_Mode (farm_cpu_index, farm_cpu_total); } dialog.DoModal (); return ; } //////////////////////////////////////////////////////////////// // // Display_Editor_Objects // //////////////////////////////////////////////////////////////// void SceneEditorClass::Display_Editor_Objects (bool onoff) { m_ShowEditorObjects = onoff; // // Loop over all the nodes, picking out the editor-only // nodes to show or hide. // DynamicVectorClass node_list; NodeMgrClass::Build_Full_Node_List (node_list); for (int index = 0; index < node_list.Count (); index ++) { NodeClass *node = node_list[index]; if (node != NULL) { int type = node->Get_Type (); // // Does this node have an editor-only display object? // if ( type == NODE_TYPE_ZONE || type == NODE_TYPE_WAYPATH || type == NODE_TYPE_WAYPOINT || type == NODE_TYPE_LIGHT || type == NODE_TYPE_TRANSITION || type == NODE_TYPE_TRANSITION_CHARACTER || type == NODE_TYPE_SOUND || type == NODE_TYPE_VIS || type == NODE_TYPE_VIS_POINT || type == NODE_TYPE_PATHFIND_START || type == NODE_TYPE_DUMMY_OBJECT || type == NODE_TYPE_COVER_SPOT || type == NODE_TYPE_COVER_ATTACK_POINT || type == NODE_TYPE_DAMAGE_ZONE || type == NODE_TYPE_BUILDING || type == NODE_TYPE_EDITOR_ONLY_OBJ) { node->Hide (!onoff); } } } return ; } //////////////////////////////////////////////////////////////// // // Display_Static_Anim_Phys // //////////////////////////////////////////////////////////////// void SceneEditorClass::Display_Static_Anim_Phys (bool onoff) { m_ShowStaticAnimPhys = onoff; // // 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 ()) { // // Is this object a static anim phys? // PhysClass *phys_obj = iterator.Peek_Obj (); StaticAnimPhysClass *static_phys_obj = phys_obj->As_StaticAnimPhysClass (); if (static_phys_obj != NULL && static_phys_obj->Peek_Model () != NULL) { // // Hide the static anim phys object and turn off its collision // static_phys_obj->Peek_Model ()->Set_Hidden (!onoff); if (onoff == false) { phys_obj->Inc_Ignore_Counter (); } else if (phys_obj->Is_Ignore_Me ()) { phys_obj->Dec_Ignore_Counter (); } } } /*for ( NodeClass *node = NodeMgrClass::Get_First (); node != NULL; node = NodeMgrClass::Get_Next (node)) { PhysClass *phys_obj = node->Peek_Physics_Obj (); if (phys_obj != NULL && phys_obj->As_StaticAnimPhysClass () != NULL) { node->Hide (!onoff); } }*/ return ; } //////////////////////////////////////////////////////////////// // // Enable_Building_Power // //////////////////////////////////////////////////////////////// void SceneEditorClass::Enable_Building_Power (bool onoff) { // // Make sure the buildings are in a good state // GameObjManager::Update_Building_Collection_Spheres (); GameObjManager::Init_Buildings (); // // Loop over all the lights and tell them to turn the spheres // on or off. // for ( NodeClass *node = NodeMgrClass::Get_First (NODE_TYPE_BUILDING); node != NULL; node = NodeMgrClass::Get_Next (node, NODE_TYPE_BUILDING)) { ((BuildingNodeClass *)node)->Enable_Power (onoff); ((BuildingNodeClass *)node)->Set_Normalized_Health (onoff * 1.0F); } m_BuildingPowerEnabled = onoff; return ; } //////////////////////////////////////////////////////////////// // // Reset_Dynamic_Object_Visibility_Status // //////////////////////////////////////////////////////////////// void SceneEditorClass::Reset_Dynamic_Object_Visibility_Status (void) { // // Get the list of all dynamic objects from the physics scene // RefPhysListIterator iterator = ::Get_Scene_Editor ()->Get_Dynamic_Object_Iterator (); for (iterator.First (); !iterator.Is_Done (); iterator.Next ()) { // // Make sure this phys obj is a dynamic object // PhysClass *phys_obj = iterator.Peek_Obj (); DynamicPhysClass *dyn_phys_obj = phys_obj->As_DynamicPhysClass (); if (dyn_phys_obj != NULL) { // // Reset this dynamic object's visibility // dyn_phys_obj->Update_Visibility_Status (); } } return ; } //////////////////////////////////////////////////////////////// // // Reset_Vis_For_Node // //////////////////////////////////////////////////////////////// void SceneEditorClass::Reset_Vis_For_Node (NodeClass *node) { if (node == NULL || node->Is_Static () == false) { return ; } // // Make sure this is a static phys object // PhysClass *phys = node->Peek_Physics_Obj (); if (phys != NULL && phys->As_StaticPhysClass () != NULL) { StaticPhysClass *static_phys = phys->As_StaticPhysClass (); // // Get this sector's id // int vis_id = static_phys->Get_Vis_Sector_ID (); if (vis_id > 0) { // // Get the vis table for this sector // VisTableClass *vis_table = VisTableManager.Get_Vis_Table(vis_id, false); if (vis_table != NULL) { // // Reset the visibility data for this sector // vis_table->Reset_All (); VisTableManager.Update_Vis_Table(vis_id,vis_table); REF_PTR_RELEASE(vis_table); } } } return ; } //////////////////////////////////////////////////////////////// // // Update_Lighting // //////////////////////////////////////////////////////////////// void SceneEditorClass::Update_Lighting (void) { Vector3 lev_min; Vector3 lev_max; Vector3 sundirection; Get_Level_Extents (lev_min, lev_max); AABoxClass box; box.Center = lev_min + ((lev_max - lev_min) * 0.5F); box.Extent = ((lev_max - lev_min) * 0.5F); Invalidate_Lighting_Caches (box); return ; } //////////////////////////////////////////////////////////////// // // Set_Sun_Light_Orientation // //////////////////////////////////////////////////////////////// void SceneEditorClass::Set_Sun_Light_Orientation (float yaw, float pitch) { PhysicsSceneClass::Set_Sun_Light_Orientation (yaw, pitch); } //////////////////////////////////////////////////////////////// // // Show_Vis_Window // //////////////////////////////////////////////////////////////// void SceneEditorClass::Show_Vis_Window (bool onoff) { if (m_VisWindow.m_hWnd == NULL) { m_VisWindow.Create (); } m_VisWindow.ShowWindow (onoff ? SW_SHOW : SW_HIDE); return ; } //////////////////////////////////////////////////////////////// // // Is_Vis_Window_Visible // //////////////////////////////////////////////////////////////// bool SceneEditorClass::Is_Vis_Window_Visible (void) { bool retval = true; if ( m_VisWindow.m_hWnd == NULL || m_VisWindow.IsWindowVisible () == false) { retval = false; } return retval; } //////////////////////////////////////////////////////////////// // // On_Vis_Occluders_Rendered // //////////////////////////////////////////////////////////////// void SceneEditorClass::On_Vis_Occluders_Rendered ( VisRenderContextClass & context, VisSampleClass & sample ) { m_VisWindow.Update_Display (*context.VisRasterizer); return ; }