| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603 |
- /*
- ** 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/wwphys/heightdb.cpp $*
- * *
- * Author:: Patrick Smith *
- * *
- * $Modtime:: 1/19/01 1:48p $*
- * *
- * $Revision:: 5 $*
- * *
- *---------------------------------------------------------------------------------------------*
- * Functions: *
- * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- #include "heightdb.h"
- #include "pscene.h"
- #include "coltest.h"
- #include "phys.h"
- #include "physcoltest.h"
- #include "mesh.h"
- #include "meshmdl.h"
- #include "chunkio.h"
- /////////////////////////////////////////////////////////////////////////
- // Constants
- /////////////////////////////////////////////////////////////////////////
- const float HEIGHT_OFFSET = 2.0F;
- enum
- {
- CHUNKID_VARIABLES = 0x07310202,
- CHUNKID_HEIGHT_ARRAY
- };
- enum
- {
- VARID_NUM_POINTS_X = 0x01,
- VARID_NUM_POINTS_Y,
- VARID_PATCH_SIZE,
- VARID_LEV_MIN,
- VARID_LEV_MAX
- };
- /////////////////////////////////////////////////////////////////////////
- // Static member initialization
- /////////////////////////////////////////////////////////////////////////
- float * HeightDBClass::m_HeightArray = NULL;
- int HeightDBClass::m_NumPointsX = 0;
- int HeightDBClass::m_NumPointsY = 0;
- float HeightDBClass::m_PatchSize = 8;
- Vector3 HeightDBClass::m_LevelMin (0, 0, 0);
- Vector3 HeightDBClass::m_LevelMax (0, 0, 0);
- /////////////////////////////////////////////////////////////////////////
- //
- // HeightDBClass
- //
- /////////////////////////////////////////////////////////////////////////
- HeightDBClass::HeightDBClass (void)
- {
- return ;
- }
- /////////////////////////////////////////////////////////////////////////
- //
- // ~HeightDBClass
- //
- /////////////////////////////////////////////////////////////////////////
- HeightDBClass::~HeightDBClass (void)
- {
- return ;
- }
- /////////////////////////////////////////////////////////////////////////
- //
- // Initialize
- //
- /////////////////////////////////////////////////////////////////////////
- void
- HeightDBClass::Initialize (void)
- {
- return ;
- }
- /////////////////////////////////////////////////////////////////////////
- //
- // Shutdown
- //
- /////////////////////////////////////////////////////////////////////////
- void
- HeightDBClass::Shutdown (void)
- {
- Free_Data ();
- return ;
- }
- /////////////////////////////////////////////////////////////////////////
- //
- // Get_Height
- //
- /////////////////////////////////////////////////////////////////////////
- float
- HeightDBClass::Get_Height (const Vector3 &pos)
- {
- float height = pos.Z;
- if (m_HeightArray != NULL && m_NumPointsX > 0 && m_NumPointsY > 0) {
- float percent_x = (pos.X - m_LevelMin.X) / (m_LevelMax.X - m_LevelMin.X);
- float percent_y = (pos.Y - m_LevelMin.Y) / (m_LevelMax.Y - m_LevelMin.Y);
- //
- // Is the position inside our data set?
- //
- if (percent_x >= 0 && percent_x <= 1.0F && percent_y >= 0 && percent_y <= 1.0F) {
-
- int entry_ul_x = (m_NumPointsX - 1) * percent_x;
- int entry_ul_y = (m_NumPointsY - 1) * percent_y;
- int entry_ur_x = entry_ul_x + 1;
- int entry_ur_y = entry_ul_y;
- int entry_lr_x = entry_ul_x + 1;
- int entry_lr_y = entry_ul_y + 1;
- int entry_ll_x = entry_ul_x;
- int entry_ll_y = entry_ul_y + 1;
- if (entry_ul_x + 1 >= m_NumPointsX) {
- entry_ur_x = entry_ul_x;
- entry_lr_x = entry_ul_x;
- }
- if (entry_ul_y + 1 >= m_NumPointsY) {
- entry_lr_y = entry_ul_y;
- entry_ll_y = entry_ul_y;
- }
- float *ul_entry = Get_Height_Entry (entry_ul_y, entry_ul_x);
- float *ur_entry = Get_Height_Entry (entry_ur_y, entry_ur_x);
- float *lr_entry = Get_Height_Entry (entry_lr_y, entry_lr_x);
- float *ll_entry = Get_Height_Entry (entry_ll_y, entry_ll_x);
-
- float local_per_x = ((pos.X - m_LevelMin.X) - (entry_ul_x * m_PatchSize)) / m_PatchSize;
- float local_per_y = ((pos.Y - m_LevelMin.Y) - (entry_ul_y * m_PatchSize)) / m_PatchSize;
- //
- // Take the weighted average of the 4 corner values
- //
- float h1 = (1 - local_per_x) * (1 - local_per_y) * (*ul_entry);
- float h2 = (local_per_x) * (1 - local_per_y) * (*ur_entry);
- float h3 = (local_per_x) * (local_per_y) * (*lr_entry);
- float h4 = (1 - local_per_x) * (local_per_y) * (*ll_entry);
- height = h1 + h2 + h3 + h4;
- }
- }
- return height;
- }
- /////////////////////////////////////////////////////////////////////////
- //
- // Generate
- //
- /////////////////////////////////////////////////////////////////////////
- void
- HeightDBClass::Generate (void)
- {
- Free_Data ();
- PhysicsSceneClass::Get_Instance ()->Get_Level_Extents (m_LevelMin, m_LevelMax);
- float generate_patch_size = m_PatchSize / 2;
- //
- // Determine how big a heightmap we will need to store data for this level
- //
- m_NumPointsX = ((m_LevelMax.X - m_LevelMin.X) / generate_patch_size) + 1;
- m_NumPointsY = ((m_LevelMax.Y - m_LevelMin.Y) / generate_patch_size) + 1;
- int entries = m_NumPointsX * m_NumPointsY;
- m_HeightArray = new float[entries];
- //
- // Build a heightmap by casting rays through the level vertically at each grid intersection
- //
- int row = 0;
- int col = 0;
- for (float y_pos = m_LevelMin.Y; y_pos < m_LevelMax.Y; y_pos += generate_patch_size) {
- col = 0;
- for (float x_pos = m_LevelMin.X; x_pos < m_LevelMax.X; x_pos += generate_patch_size) {
- float z_pos = m_LevelMin.Z;
- float start_z = m_LevelMax.Z + 250.0F;
- float end_z = m_LevelMin.Z - 250.0F;
- //
- // Setup a raycast at this (x,y) position that goes through the world
- //
- CastResultStruct result;
- LineSegClass ray (Vector3 (x_pos, y_pos, start_z), Vector3 (x_pos, y_pos, end_z));
- PhysRayCollisionTestClass raytest (ray, &result, 0, COLLISION_TYPE_PROJECTILE);
- PhysicsSceneClass::Get_Instance ()->Cast_Ray (raytest);
- //
- // Return a pointer to the collided physics object if the
- // cast hit something
- //
- if (result.Fraction < 1.0F) {
- z_pos = start_z + (end_z - start_z) * result.Fraction;
- }
- //
- // Store this height value in our array
- //
- float *height_value = Get_Height_Entry (row, col);
- if (height_value != NULL) {
- (*height_value) = (z_pos + HEIGHT_OFFSET);
- }
-
- //
- // Move over one columne
- //
- col ++;
- }
- //
- // Move down one row
- //
- row ++;
- }
- Examine_Level_Geometry ();
-
- //
- // Now average out the height values so we don't get large jumps in height
- //
- for (row = 1; row < m_NumPointsY - 1; row ++) {
- for (col = 1; col < m_NumPointsX - 1; col ++) {
- //
- // Lookup the height values of the 9 entries surrounding the current entry
- //
- float *z_val1 = Get_Height_Entry (row - 1, col - 1);
- float *z_val2 = Get_Height_Entry (row - 1, col);
- float *z_val3 = Get_Height_Entry (row - 1, col + 1);
- float *z_val4 = Get_Height_Entry (row, col - 1);
- float *z_val5 = Get_Height_Entry (row, col);
- float *z_val6 = Get_Height_Entry (row, col + 1);
- float *z_val7 = Get_Height_Entry (row + 1, col - 1);
- float *z_val8 = Get_Height_Entry (row + 1, col);
- float *z_val9 = Get_Height_Entry (row + 1, col + 1);
- //
- // Average the 9 height values
- //
- float average = ( *z_val1 + *z_val2 + *z_val3 +
- *z_val4 + *z_val5 + *z_val6 +
- *z_val7 + *z_val8 + *z_val9) / 9.0F;
- //
- // Now store the larger of the old value and the average in each of these
- // 9 entries
- //
- float edge_avg = average * 0.98F;
- *z_val1 = max (*z_val1, edge_avg);
- *z_val2 = max (*z_val2, edge_avg);
- *z_val3 = max (*z_val3, edge_avg);
- *z_val4 = max (*z_val4, edge_avg);
- *z_val5 = max (*z_val5, average);
- *z_val6 = max (*z_val6, edge_avg);
- *z_val7 = max (*z_val7, edge_avg);
- *z_val8 = max (*z_val8, edge_avg);
- *z_val9 = max (*z_val9, edge_avg);
- }
- }
- int temp_points_x = m_NumPointsX / 2;
- int temp_points_y = m_NumPointsY / 2;
- float *temp_height_array = new float[temp_points_x * temp_points_y];
- for (row = 0; row < temp_points_y; row ++) {
- for (col = 0; col < temp_points_x; col ++) {
- float *z_val = Get_Height_Entry (row * 2, col * 2);
- if (z_val != NULL) {
- temp_height_array[(row * temp_points_x) + col] = *z_val;
- } else {
- temp_height_array[(row * temp_points_x) + col] = 0;
- }
- }
- }
- Free_Data ();
- m_HeightArray = temp_height_array;
- m_NumPointsX = temp_points_x;
- m_NumPointsY = temp_points_y;
- return ;
- }
- /////////////////////////////////////////////////////////////////////////
- //
- // Free_Data
- //
- /////////////////////////////////////////////////////////////////////////
- void
- HeightDBClass::Free_Data (void)
- {
- if (m_HeightArray != NULL) {
- delete [] m_HeightArray;
- m_HeightArray = NULL;
- }
- m_NumPointsX = 0;
- m_NumPointsY = 0;
- return ;
- }
- /////////////////////////////////////////////////////////////////////////
- //
- // Examine_Level_Geometry
- //
- /////////////////////////////////////////////////////////////////////////
- void
- HeightDBClass::Examine_Level_Geometry (void)
- {
- RefPhysListIterator it1 = PhysicsSceneClass::Get_Instance ()->Get_Static_Object_Iterator ();
- RefPhysListIterator it2 = PhysicsSceneClass::Get_Instance ()->Get_Static_Anim_Object_Iterator ();
-
- //
- // Process the static meshes
- //
- for (it1.First (); !it1.Is_Done (); it1.Next ()) {
- PhysClass *phys_obj = it1.Peek_Obj ();
- if (phys_obj != NULL) {
- RenderObjClass *model = phys_obj->Peek_Model ();
- if (model != NULL) {
- Process_Render_Obj (model);
- }
- }
- }
- //
- // Process the static animated meshes
- //
- for (it2.First (); !it2.Is_Done (); it2.Next ()) {
- PhysClass *phys_obj = it2.Peek_Obj ();
- if (phys_obj != NULL) {
- RenderObjClass *model = phys_obj->Peek_Model ();
- if (model != NULL) {
- Process_Render_Obj (model);
- }
- }
- }
- return ;
- }
- /////////////////////////////////////////////////////////////////////////
- //
- // Process_Render_Obj
- //
- /////////////////////////////////////////////////////////////////////////
- void
- HeightDBClass::Process_Render_Obj (RenderObjClass *render_obj)
- {
- //
- // Loop through all the render objects sub-objects
- //
- int count = render_obj->Get_Num_Sub_Objects ();
- for (int index = 0; index < count; index ++) {
-
- // Get a pointer to this subobject
- RenderObjClass *sub_object = render_obj->Get_Sub_Object (index);
- if (sub_object != NULL) {
-
- Process_Render_Obj (sub_object);
- sub_object->Release_Ref ();
- }
- }
- //
- // Is this render object a mesh?
- //
- if ((render_obj->Class_ID () == RenderObjClass::CLASSID_MESH) &&
- (render_obj->Get_Collision_Type () & COLLISION_TYPE_PHYSICAL))
- {
- //
- // Pass this mesh off to the generator
- //
- MeshClass *mesh = static_cast<MeshClass *> (render_obj);
- Submit_Mesh (*mesh);
- }
- return ;
- }
- /////////////////////////////////////////////////////////////////////////
- //
- // Submit_Mesh
- //
- /////////////////////////////////////////////////////////////////////////
- void
- HeightDBClass::Submit_Mesh (MeshClass &mesh)
- {
- //
- // Get this mesh's polygon information
- //
- MeshModelClass *model = mesh.Get_Model ();
- if (model != NULL) {
-
- const Vector3 *vertex_array = model->Get_Vertex_Array ();
- int count = model->Get_Vertex_Count ();
- //
- // Loop over all the vertices in the mesh, looking for the
- // highest z-value in each cell of our grid
- //
- for (int index = 0; index < count; index ++) {
- Vector3 vertex = mesh.Get_Transform () * vertex_array[index];
- float percent_x = (vertex.X - m_LevelMin.X) / (m_LevelMax.X - m_LevelMin.X);
- float percent_y = (vertex.Y - m_LevelMin.Y) / (m_LevelMax.Y - m_LevelMin.Y);
- //
- // Is the position inside our data set?
- //
- if (percent_x >= 0 && percent_x <= 1.0F && percent_y >= 0 && percent_y <= 1.0F) {
-
- int entry_ul_x = (m_NumPointsX - 1) * percent_x;
- int entry_ul_y = (m_NumPointsY - 1) * percent_y;
- int entry_ur_x = entry_ul_x + 1;
- int entry_ur_y = entry_ul_y;
- int entry_lr_x = entry_ul_x + 1;
- int entry_lr_y = entry_ul_y + 1;
- int entry_ll_x = entry_ul_x;
- int entry_ll_y = entry_ul_y + 1;
- if (entry_ul_x + 1 >= m_NumPointsX) {
- entry_ur_x = entry_ul_x;
- entry_lr_x = entry_ul_x;
- }
- if (entry_ul_y + 1 >= m_NumPointsY) {
- entry_lr_y = entry_ul_y;
- entry_ll_y = entry_ul_y;
- }
- float *ul_entry = Get_Height_Entry (entry_ul_y, entry_ul_x);
- float *ur_entry = Get_Height_Entry (entry_ur_y, entry_ur_x);
- float *lr_entry = Get_Height_Entry (entry_lr_y, entry_lr_x);
- float *ll_entry = Get_Height_Entry (entry_ll_y, entry_ll_x);
- //
- // Now, move each of the corner entries up to this new z-value if necessary
- //
- (*ul_entry) = max (*ul_entry, vertex.Z + HEIGHT_OFFSET);
- (*ur_entry) = max (*ur_entry, vertex.Z + HEIGHT_OFFSET);
- (*lr_entry) = max (*lr_entry, vertex.Z + HEIGHT_OFFSET);
- (*ll_entry) = max (*ll_entry, vertex.Z + HEIGHT_OFFSET);
- }
- }
- }
- return ;
- }
- ///////////////////////////////////////////////////////////////////////
- //
- // Save
- //
- ///////////////////////////////////////////////////////////////////////
- bool
- HeightDBClass::Save (ChunkSaveClass &csave)
- {
- bool retval = true;
- //
- // Save the variables to their own chunk
- //
- csave.Begin_Chunk (CHUNKID_VARIABLES);
- WRITE_MICRO_CHUNK (csave, VARID_NUM_POINTS_X, m_NumPointsX);
- WRITE_MICRO_CHUNK (csave, VARID_NUM_POINTS_Y, m_NumPointsY);
- WRITE_MICRO_CHUNK (csave, VARID_PATCH_SIZE, m_PatchSize);
- WRITE_MICRO_CHUNK (csave, VARID_LEV_MIN, m_LevelMin);
- WRITE_MICRO_CHUNK (csave, VARID_LEV_MAX, m_LevelMax);
-
- csave.End_Chunk ();
- //
- // Write the height data to a chunk
- //
- csave.Begin_Chunk (CHUNKID_HEIGHT_ARRAY);
- int entries = m_NumPointsX * m_NumPointsY;
- csave.Write (m_HeightArray, entries * sizeof (float));
- csave.End_Chunk ();
- return retval;
- }
- ///////////////////////////////////////////////////////////////////////
- //
- // Load
- //
- ///////////////////////////////////////////////////////////////////////
- bool
- HeightDBClass::Load (ChunkLoadClass &cload)
- {
- Free_Data ();
- bool retval = true;
- while (cload.Open_Chunk ()) {
- switch (cload.Cur_Chunk_ID ()) {
-
- //
- // Read the height data from the chunk
- //
- case CHUNKID_HEIGHT_ARRAY:
- {
- //
- // Allocate enough entries to hold the height data
- //
- int entries = m_NumPointsX * m_NumPointsY;
- m_HeightArray = new float[entries];
- cload.Read (m_HeightArray, entries * sizeof (float));
- }
- break;
- case CHUNKID_VARIABLES:
- retval &= Load_Variables (cload);
- break;
- }
- cload.Close_Chunk ();
- }
- return retval;
- }
- ///////////////////////////////////////////////////////////////////////
- //
- // Load_Variables
- //
- ///////////////////////////////////////////////////////////////////////
- bool
- HeightDBClass::Load_Variables (ChunkLoadClass &cload)
- {
- bool retval = true;
- while (cload.Open_Micro_Chunk ()) {
- switch (cload.Cur_Micro_Chunk_ID ()) {
- READ_MICRO_CHUNK (cload, VARID_NUM_POINTS_X, m_NumPointsX);
- READ_MICRO_CHUNK (cload, VARID_NUM_POINTS_Y, m_NumPointsY);
- READ_MICRO_CHUNK (cload, VARID_PATCH_SIZE, m_PatchSize);
- READ_MICRO_CHUNK (cload, VARID_LEV_MIN, m_LevelMin);
- READ_MICRO_CHUNK (cload, VARID_LEV_MAX, m_LevelMax);
- }
- cload.Close_Micro_Chunk ();
- }
- return retval;
- }
|