2112 lines
56 KiB
C++
2112 lines
56 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/editableheightfield.cpp $*
|
|
* *
|
|
* Author:: Patrick Smith *
|
|
* *
|
|
* $Modtime:: 3/11/02 5:01p $*
|
|
* *
|
|
* $Revision:: 5 $*
|
|
* *
|
|
*---------------------------------------------------------------------------------------------*
|
|
* Functions: *
|
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include "editableheightfield.h"
|
|
#include "utils.h"
|
|
#include "dx8vertexbuffer.h"
|
|
#include "dx8indexbuffer.h"
|
|
#include "dx8wrapper.h"
|
|
#include "sortingrenderer.h"
|
|
#include "rinfo.h"
|
|
#include "camera.h"
|
|
#include "dx8fvf.h"
|
|
#include "filemgr.h"
|
|
#include "vector2i.h"
|
|
#include "terrainmaterial.h"
|
|
#include "renegadeterrainpatch.h"
|
|
#include "renegadeterrainmaterialpass.h"
|
|
#include "terrainmaterial.h"
|
|
#include "staticphys.h"
|
|
#include "sceneeditor.h"
|
|
#include "filemgr.h"
|
|
#include "nodemgr.h"
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
// Local constants
|
|
////////////////////////////////////////////////////////////////
|
|
enum
|
|
{
|
|
CHUNKID_VARIABLES = 0x02261040,
|
|
CHUNKID_HEIGHTS,
|
|
CHUNKID_TEXTURE_WEIGHTS,
|
|
CHUNKID_MATERIAL_LIST,
|
|
CHUNKID_MATERIAL,
|
|
CHUNKID_EMPTY_MATERIAL_SLOT,
|
|
CHUNKID_QUAD_FLAGS
|
|
};
|
|
|
|
enum
|
|
{
|
|
VARID_GRID_PTS_X = 0x01,
|
|
VARID_GRID_PTS_Y,
|
|
VARID_GRID_PT_COUNT,
|
|
VARID_GRID_WIDTH,
|
|
VARID_GRID_HEIGHT,
|
|
VARID_GRID_DENSITY,
|
|
VARID_MAX_TEXTURE_PASSES,
|
|
};
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// EditableHeightfieldClass
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
EditableHeightfieldClass::EditableHeightfieldClass (void) :
|
|
Width (0),
|
|
Height (0),
|
|
Density (1.0F),
|
|
GridPointsX (0),
|
|
GridPointsY (0),
|
|
GridPointCount (0),
|
|
Grid (NULL),
|
|
GridNormals (NULL),
|
|
QuadFlags (NULL),
|
|
PatchGrid (NULL),
|
|
PhysObjGrid (NULL),
|
|
PatchesX (0),
|
|
PatchesY (0),
|
|
PatchGridPointsX (0),
|
|
PatchGridPointsY (0),
|
|
PatchWidth (0.0F),
|
|
PatchHeight (0.0F)
|
|
{
|
|
Initialize ();
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// EditableHeightfieldClass
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
EditableHeightfieldClass::EditableHeightfieldClass (const EditableHeightfieldClass &src) :
|
|
Width (0),
|
|
Height (0),
|
|
Density (1.0F),
|
|
GridPointsX (0),
|
|
GridPointsY (0),
|
|
GridPointCount (0),
|
|
Grid (NULL),
|
|
GridNormals (NULL),
|
|
QuadFlags (NULL),
|
|
PatchGrid (NULL),
|
|
PhysObjGrid (NULL),
|
|
PatchesX (0),
|
|
PatchesY (0),
|
|
PatchGridPointsX (0),
|
|
PatchGridPointsY (0),
|
|
PatchWidth (0.0F),
|
|
PatchHeight (0.0F)
|
|
{
|
|
Initialize ();
|
|
*this = src;
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ~EditableHeightfieldClass
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
EditableHeightfieldClass::~EditableHeightfieldClass (void)
|
|
{
|
|
Free_Grid ();
|
|
Free_Material ();
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// operator=
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
const EditableHeightfieldClass &
|
|
EditableHeightfieldClass::operator= (const EditableHeightfieldClass &src)
|
|
{
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Initialize
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
EditableHeightfieldClass::Initialize (void)
|
|
{
|
|
//
|
|
// Zero all the lists
|
|
//
|
|
::memset (MaterialList, 0, sizeof (MaterialList));
|
|
::memset (GridUVs, 0, sizeof (GridUVs));
|
|
::memset (TextureWeights, 0, sizeof (TextureWeights));
|
|
|
|
//
|
|
// Initialize the material settings
|
|
//
|
|
Initialize_Material ();
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Create
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
EditableHeightfieldClass::Create (float width, float height, float meters_per_point)
|
|
{
|
|
Set_Dimensions (width, height, meters_per_point);
|
|
Assign_Unique_IDs ();
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Set_Dimensions
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
EditableHeightfieldClass::Set_Dimensions (float width, float height, float meters_per_point)
|
|
{
|
|
Free_Grid ();
|
|
|
|
//
|
|
// Calculate how many grid points we will have in the x and y directions
|
|
//
|
|
Density = meters_per_point;
|
|
GridPointsX = int(width / Density);
|
|
GridPointsY = int(height / Density);
|
|
GridPointsX = max (1, GridPointsX);
|
|
GridPointsY = max (1, GridPointsY);
|
|
GridPointCount = (GridPointsX * GridPointsY);
|
|
Width = (GridPointsX * Density);
|
|
Height = (GridPointsY * Density);
|
|
|
|
//
|
|
// Allocate and initialize the grid and supporting data structures
|
|
//
|
|
Allocate_Patches ();
|
|
Allocate_Grid ();
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Allocate_Grid
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
EditableHeightfieldClass::Allocate_Grid (void)
|
|
{
|
|
//
|
|
// Allocate the per-texture data structures
|
|
//
|
|
for (int index = 0; index < MAX_TEXTURE_PASSES; index ++) {
|
|
|
|
//
|
|
// Allocate the per-texture UV arrays
|
|
//
|
|
GridUVs[index] = new Vector2 [GridPointCount];
|
|
|
|
//
|
|
// Allocate the amount of texture influences for each texture for each vertex
|
|
//
|
|
TextureWeights[index] = new float[GridPointCount];
|
|
::memset (TextureWeights[index], 0, sizeof (float) * GridPointCount);
|
|
}
|
|
|
|
//
|
|
// Allocate the arrays
|
|
//
|
|
GridNormals = new Vector3[GridPointCount];
|
|
Grid = new Vector3[GridPointCount];
|
|
|
|
//
|
|
// Allocate and initiailze the array of quad flags
|
|
//
|
|
int quad_count = (GridPointsX - 1) * (GridPointsY - 1);
|
|
QuadFlags = new uint8[quad_count];
|
|
::memset (QuadFlags, 0, sizeof (uint8) * quad_count);
|
|
|
|
//
|
|
// Initialize the grid and the uv array's
|
|
//
|
|
Initialize_Grid ();
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Initialize_Grid
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
EditableHeightfieldClass::Initialize_Grid (void)
|
|
{
|
|
float y_value = 0;
|
|
|
|
//
|
|
// Give each point on the grid a position
|
|
//
|
|
for (int y_pos = 0; y_pos < GridPointsY; y_pos ++) {
|
|
int start_index = (y_pos * GridPointsX);
|
|
float x_value = 0;
|
|
|
|
for (int x_pos = 0; x_pos < GridPointsX; x_pos ++) {
|
|
|
|
//
|
|
// Simply set the x,y,z position of this vertex
|
|
//
|
|
Grid[start_index + x_pos].Set (x_value, y_value, 0.0F);
|
|
x_value += Density;
|
|
|
|
//
|
|
// Also give the vertex a texture influence weight
|
|
//
|
|
TextureWeights[0][start_index + x_pos] = 1.0F;
|
|
|
|
//
|
|
// Calculate the UV coordinate for this texture at this vertex
|
|
//
|
|
for (int index = 0; index < MAX_TEXTURE_PASSES; index ++) {
|
|
if (MaterialList[index] != NULL) {
|
|
float meters_per_texture = MaterialList[index]->Get_Meters_Per_Tile ();
|
|
float u_value = Grid[start_index + x_pos].X / meters_per_texture;
|
|
float v_value = Grid[start_index + x_pos].Y / meters_per_texture;
|
|
GridUVs[index][start_index + x_pos].Set (u_value, v_value);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Move down to the next row
|
|
//
|
|
y_value += Density;
|
|
}
|
|
|
|
//
|
|
// Update the normals of the grid
|
|
//
|
|
Update_Normals (0, 0, GridPointsX - 1, GridPointsY - 1);
|
|
Update_Patch_Pos_And_Normals (0, 0, GridPointsX - 1, GridPointsY - 1);
|
|
Update_UVs ();
|
|
Update_Texture_Quad_List (0, 0, GridPointsX - 1, GridPointsY - 1);
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Free_Grid
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
EditableHeightfieldClass::Free_Grid (void)
|
|
{
|
|
SAFE_DELETE_ARRAY (Grid);
|
|
SAFE_DELETE_ARRAY (GridNormals);
|
|
SAFE_DELETE_ARRAY (QuadFlags);
|
|
|
|
//
|
|
// Free the texture weight arrays
|
|
//
|
|
for (int index = 0; index < MAX_TEXTURE_PASSES; index ++) {
|
|
SAFE_DELETE (TextureWeights[index]);
|
|
SAFE_DELETE (GridUVs[index]);
|
|
}
|
|
|
|
Width = 0;
|
|
Height = 0;
|
|
Density = 1.0F;
|
|
GridPointsX = 0;
|
|
GridPointsY = 0;
|
|
GridPointCount = 0;
|
|
|
|
//
|
|
// Now free each of the passes
|
|
//
|
|
Free_Patches ();
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Update_Texture_Quad_List
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
EditableHeightfieldClass::Update_Texture_Quad_List (int min_x, int min_y, int max_x, int max_y)
|
|
{
|
|
//
|
|
// Determine which patches this operation would possibly affect
|
|
//
|
|
int min_patch_x = min_x / (PatchGridPointsX - 1);
|
|
int min_patch_y = min_y / (PatchGridPointsY - 1);
|
|
int max_patch_x = max_x / (PatchGridPointsX - 1);
|
|
int max_patch_y = max_y / (PatchGridPointsY - 1);
|
|
max_patch_x = min (max_patch_x, (PatchesX - 1));
|
|
max_patch_y = min (max_patch_y, (PatchesY - 1));
|
|
|
|
int row_count = GridPointsY - 1;
|
|
int col_count = GridPointsX - 1;
|
|
|
|
int quads_per_patch_x = PatchGridPointsX - 1;
|
|
int quads_per_patch_y = PatchGridPointsY - 1;
|
|
|
|
//
|
|
// Loop over each quad in the heightfield and determine which pass(es)
|
|
// they should be rendered in...
|
|
//
|
|
for (int patch_y = min_patch_y; patch_y <= max_patch_y; patch_y ++) {
|
|
for (int patch_x = min_patch_x; patch_x <= max_patch_x; patch_x ++) {
|
|
|
|
//
|
|
// Reset the passes for this patch
|
|
//
|
|
int patch_index = (patch_y * PatchesX) + patch_x;
|
|
PatchGrid[patch_index]->Reset_Material_Passes ();
|
|
|
|
for (int patch_quad_y = 0; patch_quad_y < quads_per_patch_y; patch_quad_y ++) {
|
|
for (int patch_quad_x = 0; patch_quad_x < quads_per_patch_x; patch_quad_x ++) {
|
|
|
|
//
|
|
// Determine the quad index inside the patch
|
|
//
|
|
int patch_quad_index = (patch_quad_y * (PatchGridPointsX - 1)) + patch_quad_x;
|
|
|
|
//
|
|
// Calculate where in the overall heigtmap this quad is
|
|
//
|
|
int quad_x = (patch_x * quads_per_patch_x) + patch_quad_x;
|
|
int quad_y = (patch_y * quads_per_patch_y) + patch_quad_y;
|
|
int overall_quad_index = (quad_y * (GridPointsX - 1)) + quad_x;
|
|
|
|
//
|
|
// Copy the quad flags to the pathc
|
|
//
|
|
PatchGrid[patch_index]->Set_Quad_Flags (patch_quad_index, QuadFlags[overall_quad_index]);
|
|
|
|
//
|
|
// Skip this quad if its hidden
|
|
//
|
|
if (QuadFlags[overall_quad_index] & QF_HIDDEN) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Calculate what vertices define the four corners of this quad
|
|
//
|
|
int start_index = (quad_y * GridPointsX) + quad_x;
|
|
int v0_index = start_index;
|
|
int v1_index = start_index + 1;
|
|
int v2_index = start_index + GridPointsX + 1;
|
|
int v3_index = start_index + GridPointsX;
|
|
|
|
//
|
|
// Calculate what vertices define the four corners of this quad (in patch space)
|
|
//
|
|
int patch_start_index = (patch_quad_y * PatchGridPointsX) + patch_quad_x;
|
|
int patch_v0_index = patch_start_index;
|
|
int patch_v1_index = patch_start_index + 1;
|
|
int patch_v2_index = patch_start_index + PatchGridPointsX + 1;
|
|
int patch_v3_index = patch_start_index + PatchGridPointsX;
|
|
|
|
float v0_alpha_sum = 0.0F;
|
|
float v1_alpha_sum = 0.0F;
|
|
float v2_alpha_sum = 0.0F;
|
|
float v3_alpha_sum = 0.0F;
|
|
|
|
//
|
|
// Determine which passes this quad should be rendered in...
|
|
//
|
|
bool is_set = false;
|
|
for (int layer = 0; layer < MAX_TEXTURE_PASSES; layer ++) {
|
|
|
|
//
|
|
// Is there a significant influence from one of the textures on this quad?
|
|
//
|
|
if ( TextureWeights[layer][v0_index] > 0.0F ||
|
|
TextureWeights[layer][v1_index] > 0.0F ||
|
|
TextureWeights[layer][v2_index] > 0.0F ||
|
|
TextureWeights[layer][v3_index] > 0.0F)
|
|
{
|
|
//
|
|
// Get acces to the material pass for this layer
|
|
//
|
|
RenegadeTerrainMaterialPassClass *material_pass = NULL;
|
|
material_pass = PatchGrid[patch_index]->Get_Material_Pass (layer, MaterialList[layer]);
|
|
|
|
v0_alpha_sum += TextureWeights[layer][v0_index];
|
|
v1_alpha_sum += TextureWeights[layer][v1_index];
|
|
v2_alpha_sum += TextureWeights[layer][v2_index];
|
|
v3_alpha_sum += TextureWeights[layer][v3_index];
|
|
|
|
//
|
|
// Add this quad to either the alpha pass or the base pass
|
|
//
|
|
if (is_set) {
|
|
material_pass->QuadList[PASS_ALPHA].Add (patch_quad_index);
|
|
|
|
//
|
|
// Set the vertex alpha's for the verts inside the material layer
|
|
//
|
|
material_pass->VertexAlpha[patch_v0_index] = TextureWeights[layer][v0_index] / v0_alpha_sum;
|
|
material_pass->VertexAlpha[patch_v1_index] = TextureWeights[layer][v1_index] / v1_alpha_sum;
|
|
material_pass->VertexAlpha[patch_v2_index] = TextureWeights[layer][v2_index] / v2_alpha_sum;
|
|
material_pass->VertexAlpha[patch_v3_index] = TextureWeights[layer][v3_index] / v3_alpha_sum;
|
|
|
|
} else {
|
|
material_pass->QuadList[PASS_BASE].Add (patch_quad_index);
|
|
is_set = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now, make each patch update its internal rendering structures
|
|
//
|
|
PatchGrid[patch_index]->Update_Vertex_Render_Lists ();
|
|
PatchGrid[patch_index]->Update_UVs ();
|
|
}
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Set_Material
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
EditableHeightfieldClass::Set_Material (int index, TerrainMaterialClass *material)
|
|
{
|
|
if (index < 0 || index >= MAX_TEXTURE_PASSES) {
|
|
return ;
|
|
}
|
|
|
|
//
|
|
// Store this material in its slot
|
|
//
|
|
REF_PTR_SET (MaterialList[index], material);
|
|
|
|
//
|
|
// Update UV coords for this texture pass
|
|
//
|
|
On_Material_Changed (index);
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// On_Material_Changed
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
EditableHeightfieldClass::On_Material_Changed (int index)
|
|
{
|
|
if (index < 0 || index >= MAX_TEXTURE_PASSES || MaterialList[index] == NULL) {
|
|
return ;
|
|
}
|
|
|
|
//
|
|
// Loop over all the verts on the grid
|
|
//
|
|
/*for (int y_pos = 0; y_pos < GridPointsY; y_pos ++) {
|
|
int start_index = (y_pos * GridPointsX);
|
|
for (int x_pos = 0; x_pos < GridPointsX; x_pos ++) {
|
|
|
|
//
|
|
// Calculate the UV coordinate for this texture at this vertex
|
|
//
|
|
float meters_per_texture = MaterialList[index]->Get_Meters_Per_Tile ();
|
|
float u_value = Grid[start_index + x_pos].X / meters_per_texture;
|
|
float v_value = Grid[start_index + x_pos].Y / meters_per_texture;
|
|
GridUVs[index][start_index + x_pos].Set (u_value, v_value);
|
|
}
|
|
}*/
|
|
|
|
//
|
|
// Now, make each patch update its internal rendering structures
|
|
//
|
|
int patch_count = PatchesX * PatchesY;
|
|
for (int patch_index = 0; patch_index < patch_count; patch_index ++) {
|
|
PatchGrid[patch_index]->Update_UVs ();
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Update_UVs
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
EditableHeightfieldClass::Update_UVs (void)
|
|
{
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Update_Normals
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
EditableHeightfieldClass::Update_Normals (int min_x, int min_y, int max_x, int max_y)
|
|
{
|
|
//
|
|
// First, initialize each vertex normal to 0
|
|
//
|
|
for (int y_pos = min_y; y_pos <= max_y; y_pos ++) {
|
|
int vert_index = (y_pos * GridPointsX) + min_x;
|
|
for (int x_pos = min_x; x_pos <= max_x; x_pos ++) {
|
|
|
|
//
|
|
// Initialize this normal
|
|
//
|
|
GridNormals[vert_index ++].Set (0.0F, 0.0F, 0.0F);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Loop over each vertex that's affected and add the plane normal of the
|
|
// 6 triangles that are formed from this vertex .
|
|
//
|
|
for (y_pos = min_y; y_pos <= max_y; y_pos ++) {
|
|
int vert_index = (y_pos * GridPointsX) + min_x;
|
|
for (int x_pos = min_x; x_pos <= max_x; x_pos ++) {
|
|
|
|
//
|
|
// Upper-left quad
|
|
//
|
|
if (y_pos > 0 && x_pos > 0) {
|
|
int q0_v0_index = (vert_index - GridPointsX) - 1;
|
|
int q0_v1_index = vert_index - GridPointsX;
|
|
int q0_v2_index = vert_index;
|
|
int q0_v3_index = vert_index - 1;
|
|
const Vector3 &q0_v0 = Grid[q0_v0_index];
|
|
const Vector3 &q0_v1 = Grid[q0_v1_index];
|
|
const Vector3 &q0_v2 = Grid[q0_v2_index];
|
|
const Vector3 &q0_v3 = Grid[q0_v3_index];
|
|
Vector3 q0_normal1 = Vector3::Cross_Product (q0_v2 - q0_v1, q0_v0 - q0_v1);
|
|
Vector3 q0_normal2 = Vector3::Cross_Product (q0_v3 - q0_v2, q0_v0 - q0_v2);
|
|
GridNormals[vert_index] += q0_normal1;
|
|
GridNormals[vert_index] += q0_normal2;
|
|
}
|
|
|
|
//
|
|
// Upper-right quad
|
|
//
|
|
if (y_pos > 0 && x_pos < (GridPointsX - 1)) {
|
|
int q1_v0_index = vert_index - GridPointsX;
|
|
int q1_v1_index = (vert_index - GridPointsX) + 1;
|
|
int q1_v2_index = vert_index + 1;
|
|
int q1_v3_index = vert_index;
|
|
const Vector3 &q1_v0 = Grid[q1_v0_index];
|
|
const Vector3 &q1_v1 = Grid[q1_v1_index];
|
|
const Vector3 &q1_v2 = Grid[q1_v2_index];
|
|
const Vector3 &q1_v3 = Grid[q1_v3_index];
|
|
Vector3 q1_normal2 = Vector3::Cross_Product (q1_v3 - q1_v2, q1_v0 - q1_v2);
|
|
GridNormals[vert_index] += q1_normal2;
|
|
}
|
|
|
|
//
|
|
// Lower-right quad
|
|
//
|
|
if (y_pos < (GridPointsY - 1) && x_pos < (GridPointsX - 1)) {
|
|
int q2_v0_index = vert_index;
|
|
int q2_v1_index = vert_index + 1;
|
|
int q2_v2_index = vert_index + GridPointsX + 1;
|
|
int q2_v3_index = vert_index + GridPointsX;
|
|
const Vector3 &q2_v0 = Grid[q2_v0_index];
|
|
const Vector3 &q2_v1 = Grid[q2_v1_index];
|
|
const Vector3 &q2_v2 = Grid[q2_v2_index];
|
|
const Vector3 &q2_v3 = Grid[q2_v3_index];
|
|
Vector3 q2_normal1 = Vector3::Cross_Product (q2_v2 - q2_v1, q2_v0 - q2_v1);
|
|
Vector3 q2_normal2 = Vector3::Cross_Product (q2_v3 - q2_v2, q2_v0 - q2_v2);
|
|
GridNormals[vert_index] += q2_normal1;
|
|
GridNormals[vert_index] += q2_normal2;
|
|
}
|
|
|
|
//
|
|
// Lower-left quad
|
|
//
|
|
if (y_pos < (GridPointsY - 1) && x_pos > 0) {
|
|
int q3_v0_index = vert_index - 1;
|
|
int q3_v1_index = vert_index;
|
|
int q3_v2_index = vert_index + GridPointsX;
|
|
int q3_v3_index = vert_index + GridPointsX - 1;
|
|
const Vector3 &q3_v0 = Grid[q3_v0_index];
|
|
const Vector3 &q3_v1 = Grid[q3_v1_index];
|
|
const Vector3 &q3_v2 = Grid[q3_v2_index];
|
|
const Vector3 &q3_v3 = Grid[q3_v3_index];
|
|
Vector3 q3_normal1 = Vector3::Cross_Product (q3_v2 - q3_v1, q3_v0 - q3_v1);
|
|
GridNormals[vert_index] += q3_normal1;
|
|
}
|
|
|
|
GridNormals[vert_index ++].Normalize ();
|
|
}
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Initialize_Material
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
EditableHeightfieldClass::Initialize_Material (void)
|
|
{
|
|
//
|
|
// Allocate a test texture
|
|
//
|
|
CString full_path = ::Get_File_Mgr ()->Make_Full_Path ("Heightfield\\DIRT_01.TGA");
|
|
MaterialList[0] = new TerrainMaterialClass;
|
|
MaterialList[0]->Set_Texture (full_path);
|
|
MaterialList[0]->Set_Meters_Per_Tile (25.0F);
|
|
|
|
//
|
|
// Allocate a test texture
|
|
//
|
|
/*full_path = ::Get_File_Mgr ()->Make_Full_Path ("Heightfield\\L03_bushes.tga");
|
|
MaterialList[1] = new TerrainMaterialClass;
|
|
MaterialList[1]->Set_Texture (full_path);
|
|
MaterialList[1]->Set_Meters_Per_Tile (25.0F);
|
|
|
|
//
|
|
// Allocate a test texture
|
|
//
|
|
full_path = ::Get_File_Mgr ()->Make_Full_Path ("Heightfield\\cht_GryStn.tga");
|
|
MaterialList[2] = new TerrainMaterialClass;
|
|
MaterialList[2]->Set_Texture (full_path);
|
|
MaterialList[2]->Set_Meters_Per_Tile (25.0F);
|
|
|
|
//
|
|
// Allocate a test texture
|
|
//
|
|
full_path = ::Get_File_Mgr ()->Make_Full_Path ("Heightfield\\cht_snwmts.tga");
|
|
MaterialList[3] = new TerrainMaterialClass;
|
|
MaterialList[3]->Set_Texture (full_path);
|
|
MaterialList[3]->Set_Meters_Per_Tile (25.0F);
|
|
|
|
//
|
|
// Allocate a test texture
|
|
//
|
|
full_path = ::Get_File_Mgr ()->Make_Full_Path ("Heightfield\\lv8_dirt2.tga");
|
|
MaterialList[4] = new TerrainMaterialClass;
|
|
MaterialList[4]->Set_Texture (full_path);
|
|
MaterialList[4]->Set_Meters_Per_Tile (25.0F);
|
|
|
|
//
|
|
// Allocate a test texture
|
|
//
|
|
full_path = ::Get_File_Mgr ()->Make_Full_Path ("Heightfield\\L03_lav001.tga");
|
|
MaterialList[5] = new TerrainMaterialClass;
|
|
MaterialList[5]->Set_Texture (full_path);
|
|
MaterialList[5]->Set_Meters_Per_Tile (25.0F);
|
|
|
|
//
|
|
// Allocate a test texture
|
|
//
|
|
full_path = ::Get_File_Mgr ()->Make_Full_Path ("Heightfield\\L03_bchsnd.tga");
|
|
MaterialList[6] = new TerrainMaterialClass;
|
|
MaterialList[6]->Set_Texture (full_path);
|
|
MaterialList[6]->Set_Meters_Per_Tile (25.0F);*/
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Free_Material
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
EditableHeightfieldClass::Free_Material (void)
|
|
{
|
|
//
|
|
// Release the textures
|
|
//
|
|
for (int index = 0; index < MAX_TEXTURE_PASSES; index ++) {
|
|
REF_PTR_RELEASE (MaterialList[index]);
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Deform_Heightfield
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
EditableHeightfieldClass::Deform_Heightfield
|
|
(
|
|
const Vector3 & world_space_center,
|
|
float amount,
|
|
float inner_radius,
|
|
float outter_radius
|
|
)
|
|
{
|
|
//
|
|
// First, transform the center point into heightfield (object) space
|
|
//
|
|
const Matrix3D tm = Matrix3D (1);//Get_Transform ();
|
|
Matrix3D world_to_obj_tm;
|
|
tm.Get_Orthogonal_Inverse (world_to_obj_tm);
|
|
|
|
Vector3 center;
|
|
Matrix3D::Transform_Vector (world_to_obj_tm, world_space_center, ¢er);
|
|
|
|
//
|
|
// Offset the affect verts
|
|
//
|
|
float min_x_value = center.X - outter_radius;
|
|
float min_y_value = center.Y - outter_radius;
|
|
float max_x_value = center.X + outter_radius;
|
|
float max_y_value = center.Y + outter_radius;
|
|
|
|
int min_x = WWMath::Float_To_Int_Floor ((min_x_value / Density));
|
|
int min_y = WWMath::Float_To_Int_Floor ((min_y_value / Density));
|
|
int max_x = WWMath::Float_To_Int_Floor ((max_x_value / Density));
|
|
int max_y = WWMath::Float_To_Int_Floor ((max_y_value / Density));
|
|
min_x = WWMath::Clamp_Int (min_x, 0, GridPointsX - 1);
|
|
min_y = WWMath::Clamp_Int (min_y, 0, GridPointsY - 1);
|
|
max_x = WWMath::Clamp_Int (max_x, 0, GridPointsX - 1);
|
|
max_y = WWMath::Clamp_Int (max_y, 0, GridPointsY - 1);
|
|
|
|
float delta_radius = (outter_radius - inner_radius);
|
|
|
|
//
|
|
// Loop over all the verts in this region and apply the deformation
|
|
//
|
|
|
|
for (int y_pos = min_y; y_pos <= max_y; y_pos ++) {
|
|
int curr_offset = (y_pos * GridPointsX) + min_x;
|
|
|
|
for (int x_pos = min_x; x_pos <= max_x; x_pos ++) {
|
|
|
|
Vector3 &vert_pos = Grid[curr_offset];
|
|
|
|
//
|
|
// Calculate how far this vertex is from the center of the deformation
|
|
//
|
|
Vector2 delta (vert_pos.X - center.X, vert_pos.Y - center.Y);
|
|
float dist = WWMath::Sqrt ((delta.X * delta.X) + (delta.Y * delta.Y));
|
|
|
|
//
|
|
// Is this vertex inside the affected region?
|
|
//
|
|
if (dist <= outter_radius) {
|
|
|
|
//
|
|
// Calculate what percentage this vertex is affected by the deformation
|
|
//
|
|
float percent = 1.0F;
|
|
if (dist > inner_radius && delta_radius > 0.0F) {
|
|
percent = 1.0F - ((dist - inner_radius) / delta_radius);
|
|
}
|
|
|
|
//
|
|
// Scale the amount and apply it to the z-position of the vertex
|
|
//
|
|
float delta_amount = percent * amount;
|
|
vert_pos.Z += delta_amount;
|
|
}
|
|
|
|
//
|
|
// Advance to the next vertex
|
|
//
|
|
curr_offset ++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the normals of the affected verts
|
|
//
|
|
Update_Normals (min_x, min_y, max_x, max_y);
|
|
Update_Patch_Pos_And_Normals (min_x, min_y, max_x, max_y);
|
|
Update_UVs ();
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Smooth_Foundation_Heightfield
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
EditableHeightfieldClass::Smooth_Foundation_Heightfield
|
|
(
|
|
const Vector3 & world_space_center,
|
|
float amount,
|
|
float inner_radius,
|
|
float outter_radius
|
|
)
|
|
{
|
|
//
|
|
// First, transform the center point into heightfield (object) space
|
|
//
|
|
const Matrix3D tm = Matrix3D (1);
|
|
Matrix3D world_to_obj_tm;
|
|
tm.Get_Orthogonal_Inverse (world_to_obj_tm);
|
|
|
|
Vector3 center;
|
|
Matrix3D::Transform_Vector (world_to_obj_tm, world_space_center, ¢er);
|
|
|
|
//
|
|
// Offset the affect verts
|
|
//
|
|
float min_x_value = center.X - outter_radius;
|
|
float min_y_value = center.Y - outter_radius;
|
|
float max_x_value = center.X + outter_radius;
|
|
float max_y_value = center.Y + outter_radius;
|
|
|
|
int min_x = WWMath::Float_To_Int_Floor ((min_x_value / Density));
|
|
int min_y = WWMath::Float_To_Int_Floor ((min_y_value / Density));
|
|
int max_x = WWMath::Float_To_Int_Floor ((max_x_value / Density));
|
|
int max_y = WWMath::Float_To_Int_Floor ((max_y_value / Density));
|
|
min_x = WWMath::Clamp_Int (min_x, 0, GridPointsX - 1);
|
|
min_y = WWMath::Clamp_Int (min_y, 0, GridPointsY - 1);
|
|
max_x = WWMath::Clamp_Int (max_x, 0, GridPointsX - 1);
|
|
max_y = WWMath::Clamp_Int (max_y, 0, GridPointsY - 1);
|
|
|
|
float delta_radius = (outter_radius - inner_radius);
|
|
|
|
float avg_height = 0;
|
|
int count = 0;
|
|
|
|
//
|
|
// Loop over all the verts in this region and apply the deformation
|
|
//
|
|
for (int y_pos = min_y; y_pos <= max_y; y_pos ++) {
|
|
int curr_offset = (y_pos * GridPointsX) + min_x;
|
|
|
|
for (int x_pos = min_x; x_pos <= max_x; x_pos ++) {
|
|
|
|
Vector3 &vert_pos = Grid[curr_offset];
|
|
|
|
//
|
|
// Calculate how far this vertex is from the center of the deformation
|
|
//
|
|
Vector2 delta (vert_pos.X - center.X, vert_pos.Y - center.Y);
|
|
float dist = WWMath::Sqrt ((delta.X * delta.X) + (delta.Y * delta.Y));
|
|
|
|
//
|
|
// Is this vertex inside the affected region?
|
|
//
|
|
if (dist <= outter_radius) {
|
|
|
|
avg_height += vert_pos.Z;
|
|
count ++;
|
|
}
|
|
|
|
//
|
|
// Advance to the next vertex
|
|
//
|
|
curr_offset ++;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
if (count == 0) {
|
|
return ;
|
|
}
|
|
|
|
avg_height = avg_height / count;
|
|
|
|
|
|
//
|
|
// Loop over all the verts in this region and apply the deformation
|
|
//
|
|
for (y_pos = min_y; y_pos <= max_y; y_pos ++) {
|
|
int curr_offset = (y_pos * GridPointsX) + min_x;
|
|
|
|
for (int x_pos = min_x; x_pos <= max_x; x_pos ++) {
|
|
|
|
Vector3 &vert_pos = Grid[curr_offset];
|
|
|
|
//
|
|
// Calculate how far this vertex is from the center of the deformation
|
|
//
|
|
Vector2 delta (vert_pos.X - center.X, vert_pos.Y - center.Y);
|
|
float dist = WWMath::Sqrt ((delta.X * delta.X) + (delta.Y * delta.Y));
|
|
|
|
//
|
|
// Is this vertex inside the affected region?
|
|
//
|
|
if (dist <= outter_radius) {
|
|
|
|
//
|
|
// Calculate what percentage this vertex is affected by the deformation
|
|
//
|
|
float percent = 1.0F;
|
|
if (dist > inner_radius && delta_radius > 0.0F) {
|
|
percent = 1.0F - ((dist - inner_radius) / delta_radius);
|
|
}
|
|
|
|
//
|
|
// Scale the amount and apply it to the z-position of the vertex
|
|
//
|
|
float real_percent = percent * 0.5F;
|
|
float z_value = (vert_pos.Z - avg_height) * real_percent;
|
|
vert_pos.Z -= z_value;
|
|
}
|
|
|
|
//
|
|
// Advance to the next vertex
|
|
//
|
|
curr_offset ++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the normals of the affected verts
|
|
//
|
|
Update_Normals (min_x, min_y, max_x, max_y);
|
|
Update_Patch_Pos_And_Normals (min_x, min_y, max_x, max_y);
|
|
Update_UVs ();
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Smooth_Heightfield
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
EditableHeightfieldClass::Smooth_Heightfield
|
|
(
|
|
const Vector3 & world_space_center,
|
|
float amount,
|
|
float inner_radius,
|
|
float outter_radius
|
|
)
|
|
{
|
|
//
|
|
// First, transform the center point into heightfield (object) space
|
|
//
|
|
const Matrix3D tm = Matrix3D (1);
|
|
Matrix3D world_to_obj_tm;
|
|
tm.Get_Orthogonal_Inverse (world_to_obj_tm);
|
|
|
|
Vector3 center;
|
|
Matrix3D::Transform_Vector (world_to_obj_tm, world_space_center, ¢er);
|
|
|
|
//
|
|
// Offset the affect verts
|
|
//
|
|
float min_x_value = center.X - outter_radius;
|
|
float min_y_value = center.Y - outter_radius;
|
|
float max_x_value = center.X + outter_radius;
|
|
float max_y_value = center.Y + outter_radius;
|
|
|
|
int min_x = WWMath::Float_To_Int_Floor ((min_x_value / Density));
|
|
int min_y = WWMath::Float_To_Int_Floor ((min_y_value / Density));
|
|
int max_x = WWMath::Float_To_Int_Floor ((max_x_value / Density));
|
|
int max_y = WWMath::Float_To_Int_Floor ((max_y_value / Density));
|
|
min_x = WWMath::Clamp_Int (min_x, 0, GridPointsX - 1);
|
|
min_y = WWMath::Clamp_Int (min_y, 0, GridPointsY - 1);
|
|
max_x = WWMath::Clamp_Int (max_x, 0, GridPointsX - 1);
|
|
max_y = WWMath::Clamp_Int (max_y, 0, GridPointsY - 1);
|
|
|
|
float delta_radius = (outter_radius - inner_radius);
|
|
|
|
static const Vector2i INDEX_RING_OFFSETS[] =
|
|
{
|
|
Vector2i (-1, -1), Vector2i (0, -1), Vector2i (+1, -1),
|
|
Vector2i (-1, 0), Vector2i (+1, 0),
|
|
Vector2i (-1, +1), Vector2i (0, +1), Vector2i (+1, +1)
|
|
};
|
|
|
|
//
|
|
// Create a list we can use to keep track of the height changes
|
|
//
|
|
DynamicVectorClass<float> new_height_list;
|
|
DynamicVectorClass<int> new_height_list_pos;
|
|
new_height_list.Set_Growth_Step (1000);
|
|
new_height_list_pos.Set_Growth_Step (1000);
|
|
|
|
//
|
|
// Loop over all the verts in this region and apply the deformation
|
|
//
|
|
for (int y_pos = min_y; y_pos <= max_y; y_pos ++) {
|
|
int curr_offset = (y_pos * GridPointsX) + min_x;
|
|
|
|
for (int x_pos = min_x; x_pos <= max_x; x_pos ++) {
|
|
|
|
Vector3 &vert_pos = Grid[curr_offset];
|
|
|
|
//
|
|
// Calculate how far this vertex is from the center of the deformation
|
|
//
|
|
Vector2 delta (vert_pos.X - center.X, vert_pos.Y - center.Y);
|
|
float dist = WWMath::Sqrt ((delta.X * delta.X) + (delta.Y * delta.Y));
|
|
|
|
//
|
|
// Is this vertex inside the affected region?
|
|
//
|
|
if (dist <= outter_radius) {
|
|
|
|
//
|
|
// Calculate what percentage this vertex is affected by the deformation
|
|
//
|
|
float percent = 1.0F;
|
|
if (dist > inner_radius && delta_radius > 0.0F) {
|
|
percent = 1.0F - ((dist - inner_radius) / delta_radius);
|
|
}
|
|
|
|
//
|
|
// Scale the amount and apply it to the z-position of the vertex
|
|
//
|
|
float real_percent = percent * 0.5F;
|
|
|
|
|
|
//
|
|
// Now, average the height of this vertex with the surrounding heights
|
|
//
|
|
int count = 0;
|
|
float avg_height = 0;
|
|
|
|
for (int index = 0; index < 7; index ++) {
|
|
int new_x_pos = x_pos + INDEX_RING_OFFSETS[index].I;
|
|
int new_y_pos = y_pos + INDEX_RING_OFFSETS[index].J;
|
|
|
|
//
|
|
// If there is a vertex in this position, the take
|
|
// into consideration its height
|
|
//
|
|
if ( new_x_pos >= 0 && new_x_pos < GridPointsX &&
|
|
new_y_pos >= 0 && new_y_pos < GridPointsY)
|
|
{
|
|
int test_index = (new_y_pos * GridPointsX) + new_x_pos;
|
|
avg_height += Grid[test_index].Z;
|
|
count ++;
|
|
}
|
|
}
|
|
|
|
float z_value = vert_pos.Z;
|
|
|
|
//
|
|
// Use the average height value if possible
|
|
//
|
|
if (count > 0) {
|
|
avg_height = avg_height / count;
|
|
z_value -= (vert_pos.Z - avg_height) * real_percent;
|
|
}
|
|
|
|
new_height_list.Add (z_value);
|
|
new_height_list_pos.Add (curr_offset);
|
|
}
|
|
|
|
//
|
|
// Advance to the next vertex
|
|
//
|
|
curr_offset ++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now, loop over all the verts in this region and apply the new heights
|
|
//
|
|
for (int index = 0; index < new_height_list.Count (); index ++) {
|
|
|
|
//
|
|
// Set this vertex's height
|
|
//
|
|
Vector3 &vert_pos = Grid[new_height_list_pos[index]];
|
|
vert_pos.Z = new_height_list[index];
|
|
}
|
|
|
|
//
|
|
// Update the normals of the affected verts
|
|
//
|
|
Update_Normals (min_x, min_y, max_x, max_y);
|
|
Update_Patch_Pos_And_Normals (min_x, min_y, max_x, max_y);
|
|
Update_UVs ();
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Cutout_Heightfield
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
EditableHeightfieldClass::Cutout_Heightfield
|
|
(
|
|
const Vector3 & world_space_center,
|
|
float outter_radius,
|
|
bool subtract
|
|
)
|
|
{
|
|
//
|
|
// First, transform the center point into heightfield (object) space
|
|
//
|
|
const Matrix3D tm = Matrix3D (1);
|
|
Matrix3D world_to_obj_tm;
|
|
tm.Get_Orthogonal_Inverse (world_to_obj_tm);
|
|
|
|
Vector3 center;
|
|
Matrix3D::Transform_Vector (world_to_obj_tm, world_space_center, ¢er);
|
|
|
|
//
|
|
// Determine which quads are affected by this operation
|
|
//
|
|
float min_x_value = center.X - outter_radius;
|
|
float min_y_value = center.Y - outter_radius;
|
|
float max_x_value = center.X + outter_radius;
|
|
float max_y_value = center.Y + outter_radius;
|
|
int min_x = Get_Quad_Index_X (min_x_value);
|
|
int min_y = Get_Quad_Index_Y (min_y_value);
|
|
int max_x = Get_Quad_Index_X (max_x_value);
|
|
int max_y = Get_Quad_Index_Y (max_y_value);
|
|
|
|
//
|
|
// Loop over all the quads in this region...
|
|
//
|
|
for (int quad_y = min_y; quad_y <= max_y; quad_y ++) {
|
|
int quad_index = (quad_y * (GridPointsX - 1)) + min_x;
|
|
for (int quad_x = min_x; quad_x <= max_x; quad_x ++) {
|
|
|
|
//
|
|
// Set the bits that specify this quad is not to be rendered
|
|
//
|
|
if (subtract) {
|
|
QuadFlags[quad_index] |= QF_HIDDEN;
|
|
} else {
|
|
QuadFlags[quad_index] &= (~QF_HIDDEN);
|
|
}
|
|
|
|
//
|
|
// Advance to the next quad
|
|
//
|
|
quad_index ++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the texture rendering lists
|
|
//
|
|
Update_Texture_Quad_List (Get_Point_Index_X (min_x_value), Get_Point_Index_Y (min_y_value),
|
|
Get_Point_Index_X (max_x_value), Get_Point_Index_Y (max_y_value));
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Paint_Heightfield
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
EditableHeightfieldClass::Paint_Heightfield
|
|
(
|
|
const Vector3 & world_space_center,
|
|
int texture_index,
|
|
float amount,
|
|
float inner_radius,
|
|
float outter_radius
|
|
)
|
|
{
|
|
//
|
|
// First, transform the center point into heightfield (object) space
|
|
//
|
|
const Matrix3D tm = Matrix3D (1);
|
|
Matrix3D world_to_obj_tm;
|
|
tm.Get_Orthogonal_Inverse (world_to_obj_tm);
|
|
|
|
Vector3 center;
|
|
Matrix3D::Transform_Vector (world_to_obj_tm, world_space_center, ¢er);
|
|
|
|
//
|
|
// Offset the affect verts
|
|
//
|
|
float min_x_value = center.X - outter_radius;
|
|
float min_y_value = center.Y - outter_radius;
|
|
float max_x_value = center.X + outter_radius;
|
|
float max_y_value = center.Y + outter_radius;
|
|
|
|
int min_x = WWMath::Float_To_Int_Floor ((min_x_value / Density));
|
|
int min_y = WWMath::Float_To_Int_Floor ((min_y_value / Density));
|
|
int max_x = WWMath::Float_To_Int_Floor ((max_x_value / Density));
|
|
int max_y = WWMath::Float_To_Int_Floor ((max_y_value / Density));
|
|
min_x = WWMath::Clamp_Int (min_x, 0, GridPointsX - 1);
|
|
min_y = WWMath::Clamp_Int (min_y, 0, GridPointsY - 1);
|
|
max_x = WWMath::Clamp_Int (max_x, 0, GridPointsX - 1);
|
|
max_y = WWMath::Clamp_Int (max_y, 0, GridPointsY - 1);
|
|
|
|
float delta_radius = (outter_radius - inner_radius);
|
|
|
|
//
|
|
// Loop over all the verts in this region and apply the deformation
|
|
//
|
|
|
|
for (int y_pos = min_y; y_pos <= max_y; y_pos ++) {
|
|
int curr_offset = (y_pos * GridPointsX) + min_x;
|
|
|
|
for (int x_pos = min_x; x_pos <= max_x; x_pos ++) {
|
|
|
|
Vector3 &vert_pos = Grid[curr_offset];
|
|
|
|
//
|
|
// Calculate how far this vertex is from the center of the deformation
|
|
//
|
|
Vector2 delta (vert_pos.X - center.X, vert_pos.Y - center.Y);
|
|
float dist = WWMath::Sqrt ((delta.X * delta.X) + (delta.Y * delta.Y));
|
|
|
|
//
|
|
// Is this vertex inside the affected region?
|
|
//
|
|
if (dist <= outter_radius) {
|
|
|
|
//
|
|
// Calculate what percentage this vertex is affected by the deformation
|
|
//
|
|
float percent = 1.0F;
|
|
if (dist > inner_radius && delta_radius > 0.0F) {
|
|
percent = 1.0F - ((dist - inner_radius) / delta_radius);
|
|
}
|
|
|
|
//
|
|
// Scale the amount and apply it to the z-position of the vertex
|
|
//
|
|
float delta_amount = percent * amount;
|
|
|
|
//
|
|
// Determine how much influence each of the other texture channel had
|
|
//
|
|
float amount = TextureWeights[texture_index][curr_offset];
|
|
float old_remainder_amount = 1.0F - amount;
|
|
|
|
if (old_remainder_amount < 0 || old_remainder_amount > 1.0F) {
|
|
int test = 0;
|
|
}
|
|
|
|
//
|
|
// Increment the amount of influence for this texture channel
|
|
//
|
|
float new_amount = amount + delta_amount;
|
|
new_amount = WWMath::Clamp (new_amount, 0.0F, 1.0F);
|
|
|
|
//
|
|
// Calculate how much influence is left for the other texture channels
|
|
//
|
|
float remainder = (1.0F - new_amount);
|
|
|
|
if (old_remainder_amount != 0.0F) {
|
|
|
|
//
|
|
// Reduce the amount of influence each pass has after this operation...
|
|
//
|
|
float scale_factor = remainder / old_remainder_amount;
|
|
for (int index = 0; index < MAX_TEXTURE_PASSES; index ++) {
|
|
if (index != texture_index) {
|
|
TextureWeights[index][curr_offset] *= scale_factor;
|
|
}
|
|
}
|
|
}
|
|
|
|
TextureWeights[texture_index][curr_offset] = new_amount;
|
|
}
|
|
|
|
//
|
|
// Advance to the next vertex
|
|
//
|
|
curr_offset ++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the texture rendering lists
|
|
//
|
|
Update_Texture_Quad_List (min_x, min_y, max_x, max_y);
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Save
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
bool
|
|
EditableHeightfieldClass::Save (ChunkSaveClass &csave)
|
|
{
|
|
//
|
|
// Write the variables
|
|
//
|
|
csave.Begin_Chunk (CHUNKID_VARIABLES);
|
|
WRITE_MICRO_CHUNK (csave, VARID_GRID_PTS_X, GridPointsX);
|
|
WRITE_MICRO_CHUNK (csave, VARID_GRID_PTS_Y, GridPointsY);
|
|
WRITE_MICRO_CHUNK (csave, VARID_GRID_PT_COUNT, GridPointCount);
|
|
WRITE_MICRO_CHUNK (csave, VARID_GRID_WIDTH, Width);
|
|
WRITE_MICRO_CHUNK (csave, VARID_GRID_HEIGHT, Height);
|
|
WRITE_MICRO_CHUNK (csave, VARID_GRID_DENSITY, Density);
|
|
|
|
int texture_passes = MAX_TEXTURE_PASSES;
|
|
WRITE_MICRO_CHUNK (csave, VARID_MAX_TEXTURE_PASSES, texture_passes);
|
|
|
|
csave.End_Chunk ();
|
|
|
|
//
|
|
// Now, write out the "array" of heights
|
|
//
|
|
csave.Begin_Chunk (CHUNKID_HEIGHTS);
|
|
|
|
for (int index = 0; index < GridPointCount; index ++) {
|
|
csave.Write (&Grid[index].Z, sizeof (float));
|
|
}
|
|
|
|
csave.End_Chunk ();
|
|
|
|
//
|
|
// Now, write out the "array" of weights
|
|
//
|
|
csave.Begin_Chunk (CHUNKID_TEXTURE_WEIGHTS);
|
|
|
|
//
|
|
// Save the array of texture weights per vertex per pass
|
|
//
|
|
for (index = 0; index < GridPointCount; index ++) {
|
|
for (int pass = 0; pass < MAX_TEXTURE_PASSES; pass ++) {
|
|
csave.Write (&TextureWeights[pass][index], sizeof (float));
|
|
}
|
|
}
|
|
|
|
csave.End_Chunk ();
|
|
|
|
//
|
|
// Now, write out the array of quad flags
|
|
//
|
|
csave.Begin_Chunk (CHUNKID_QUAD_FLAGS);
|
|
|
|
int quad_count = (GridPointsX - 1) * (GridPointsY - 1);
|
|
csave.Write (QuadFlags, sizeof (uint8) * quad_count);
|
|
|
|
csave.End_Chunk ();
|
|
|
|
//
|
|
// Now, write out the list of materials
|
|
//
|
|
csave.Begin_Chunk (CHUNKID_MATERIAL_LIST);
|
|
|
|
for (index = 0; index < MAX_TEXTURE_PASSES; index ++) {
|
|
|
|
//
|
|
// Is there a material to save?
|
|
//
|
|
if (MaterialList[index] != NULL) {
|
|
|
|
//
|
|
// Save the material's definition to this chunk
|
|
//
|
|
csave.Begin_Chunk (CHUNKID_MATERIAL);
|
|
MaterialList[index]->Save (csave);
|
|
csave.End_Chunk ();
|
|
|
|
} else {
|
|
|
|
//
|
|
// Save an empty chunk so we know to leave this slot empty
|
|
//
|
|
csave.Begin_Chunk (CHUNKID_EMPTY_MATERIAL_SLOT);
|
|
csave.End_Chunk ();
|
|
}
|
|
}
|
|
|
|
csave.End_Chunk ();
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Load_Materials
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
EditableHeightfieldClass::Load_Materials (ChunkLoadClass &cload)
|
|
{
|
|
//
|
|
// Start fresh
|
|
//
|
|
Free_Material ();
|
|
int index = 0;
|
|
|
|
while (cload.Open_Chunk ()) {
|
|
switch (cload.Cur_Chunk_ID ()) {
|
|
|
|
//
|
|
// Load all the variables from this chunk
|
|
//
|
|
case CHUNKID_MATERIAL:
|
|
{
|
|
//
|
|
// Load the material
|
|
//
|
|
TerrainMaterialClass *material = new TerrainMaterialClass;
|
|
material->Load (cload);
|
|
|
|
//
|
|
// Reset the texture filename as a "relative" path
|
|
//
|
|
CString full_texture_path = material->Get_Texture_Name ();
|
|
CString rel_texture_path = ::Get_File_Mgr ()->Make_Relative_Path (full_texture_path);
|
|
material->Set_Texture (rel_texture_path);
|
|
|
|
|
|
//
|
|
// Insert the material into our list
|
|
//
|
|
MaterialList[index ++] = material;
|
|
break;
|
|
}
|
|
|
|
case CHUNKID_EMPTY_MATERIAL_SLOT:
|
|
index ++;
|
|
break;
|
|
}
|
|
|
|
cload.Close_Chunk ();
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Load
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
bool
|
|
EditableHeightfieldClass::Load (ChunkLoadClass &cload)
|
|
{
|
|
Free_Grid ();
|
|
|
|
while (cload.Open_Chunk ()) {
|
|
switch (cload.Cur_Chunk_ID ()) {
|
|
|
|
//
|
|
// Load all the variables from this chunk
|
|
//
|
|
case CHUNKID_VARIABLES:
|
|
Load_Variables (cload);
|
|
break;
|
|
|
|
case CHUNKID_HEIGHTS:
|
|
{
|
|
//
|
|
// Read the array of heights
|
|
//
|
|
for (int index = 0; index < GridPointCount; index ++) {
|
|
cload.Read (&Grid[index].Z, sizeof (float));
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case CHUNKID_TEXTURE_WEIGHTS:
|
|
{
|
|
//
|
|
// Read the array of texture weights per vertex per pass
|
|
//
|
|
for (int index = 0; index < GridPointCount; index ++) {
|
|
for (int pass = 0; pass < MAX_TEXTURE_PASSES; pass ++) {
|
|
cload.Read (&TextureWeights[pass][index], sizeof (float));
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case CHUNKID_QUAD_FLAGS:
|
|
{
|
|
//
|
|
// Read the array of quad flags
|
|
//
|
|
int quad_count = (GridPointsX - 1) * (GridPointsY - 1);
|
|
cload.Read (QuadFlags, sizeof (uint8) * quad_count);
|
|
break;
|
|
}
|
|
|
|
case CHUNKID_MATERIAL_LIST:
|
|
{
|
|
Load_Materials (cload);
|
|
break;
|
|
}
|
|
}
|
|
|
|
cload.Close_Chunk ();
|
|
}
|
|
|
|
//
|
|
// Update all our internal data structures from the loaded data
|
|
//
|
|
Update_Normals (0, 0, GridPointsX - 1, GridPointsY - 1);
|
|
Update_Patch_Pos_And_Normals (0, 0, GridPointsX - 1, GridPointsY - 1);
|
|
Update_UVs ();
|
|
Update_Texture_Quad_List (0, 0, GridPointsX - 1, GridPointsY - 1);
|
|
return true;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
//
|
|
// Load_Variables
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
void
|
|
EditableHeightfieldClass::Load_Variables (ChunkLoadClass &cload)
|
|
{
|
|
int texture_passes = MAX_TEXTURE_PASSES;
|
|
|
|
while (cload.Open_Micro_Chunk ()) {
|
|
switch (cload.Cur_Micro_Chunk_ID ()) {
|
|
|
|
//
|
|
// Read each of the microchunks
|
|
//
|
|
READ_MICRO_CHUNK (cload, VARID_GRID_PTS_X, GridPointsX);
|
|
READ_MICRO_CHUNK (cload, VARID_GRID_PTS_Y, GridPointsY);
|
|
READ_MICRO_CHUNK (cload, VARID_GRID_PT_COUNT, GridPointCount);
|
|
READ_MICRO_CHUNK (cload, VARID_GRID_WIDTH, Width);
|
|
READ_MICRO_CHUNK (cload, VARID_GRID_HEIGHT, Height);
|
|
READ_MICRO_CHUNK (cload, VARID_GRID_DENSITY, Density);
|
|
READ_MICRO_CHUNK (cload, VARID_MAX_TEXTURE_PASSES, texture_passes);
|
|
}
|
|
|
|
cload.Close_Micro_Chunk ();
|
|
}
|
|
|
|
WWASSERT (texture_passes == MAX_TEXTURE_PASSES);
|
|
|
|
//
|
|
// Allocate and initialize the grid and supporting data structures
|
|
//
|
|
Allocate_Patches ();
|
|
Allocate_Grid ();
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
//
|
|
// On_Post_Load
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
void
|
|
EditableHeightfieldClass::On_Post_Load (void)
|
|
{
|
|
//Assign_Unique_IDs ();
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
//
|
|
// Assign_Unique_IDs
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
void
|
|
EditableHeightfieldClass::Assign_Unique_IDs (void)
|
|
{
|
|
|
|
//
|
|
// Assign each patch a unique ID
|
|
//
|
|
int patch_count = PatchesX * PatchesY;
|
|
for (int index = 0; index < patch_count; index ++) {
|
|
PhysObjGrid[index]->Set_ID (NodeMgrClass::Get_Node_ID (NODE_TYPE_TERRAIN));
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
//
|
|
// Create
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
void
|
|
EditableHeightfieldClass::Create
|
|
(
|
|
const char * heightmap_filename,
|
|
float width,
|
|
float height,
|
|
float meters_per_point,
|
|
float scale
|
|
)
|
|
{
|
|
//
|
|
// Load the bitmap from disk
|
|
//
|
|
HBITMAP bitmap = (HBITMAP)::LoadImage (NULL, heightmap_filename, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION);
|
|
if (bitmap != NULL) {
|
|
|
|
//
|
|
// Initialize the heightfield from the bitmap
|
|
//
|
|
Create (bitmap, width, height, meters_per_point, scale);
|
|
::DeleteObject (bitmap);
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
//
|
|
// Create
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
void
|
|
EditableHeightfieldClass::Create
|
|
(
|
|
HBITMAP bitmap,
|
|
float width,
|
|
float height,
|
|
float meters_per_point,
|
|
float scale
|
|
)
|
|
{
|
|
if (bitmap == NULL) {
|
|
return ;
|
|
}
|
|
|
|
//
|
|
// Get the dimensions of the bitmap
|
|
//
|
|
BITMAP bmp_info = { 0 };
|
|
::GetObject (bitmap, sizeof (BITMAP), &bmp_info);
|
|
|
|
//
|
|
// Set-up the fields of the BITMAPINFOHEADER so we can retrieve the bitmap data in this format.
|
|
// Note: Top-down DIBs use negative height in Win32.
|
|
//
|
|
BITMAPINFOHEADER bitmap_info = { 0 };
|
|
bitmap_info.biSize = sizeof (BITMAPINFOHEADER);
|
|
bitmap_info.biWidth = bmp_info.bmWidth;
|
|
bitmap_info.biHeight = bmp_info.bmHeight;
|
|
bitmap_info.biPlanes = 1;
|
|
bitmap_info.biBitCount = 24;
|
|
bitmap_info.biCompression = BI_RGB;
|
|
bitmap_info.biSizeImage = ((bmp_info.bmWidth * bmp_info.bmHeight) * 3);
|
|
|
|
//
|
|
// Allocate a buffer to store the bitmap data int
|
|
//
|
|
int stride = (((bmp_info.bmWidth * 3) + 3) & ~3);
|
|
uint8 *bmp_bits = new uint8[stride * bmp_info.bmHeight];
|
|
|
|
//
|
|
// Get the bitmap data
|
|
//
|
|
HDC screen_dc = ::GetDC (NULL);
|
|
::GetDIBits (screen_dc, bitmap, 0, bmp_info.bmHeight,
|
|
(LPVOID)bmp_bits, (BITMAPINFO *)&bitmap_info, DIB_RGB_COLORS);
|
|
::ReleaseDC (NULL, screen_dc);
|
|
|
|
//
|
|
// First, create the grid
|
|
//
|
|
Set_Dimensions (width, height, meters_per_point);
|
|
|
|
float half_x_dist = ((float)bmp_info.bmWidth / (float)GridPointsX) / 2;
|
|
float half_y_dist = ((float)bmp_info.bmHeight / (float)GridPointsY) / 2;
|
|
|
|
//
|
|
// Now, loop over each point of the grid and determine what its height should
|
|
// be based on the intensity of the corresponding pixel in the bitmap.
|
|
//
|
|
for (int y_pos = 0; y_pos < GridPointsY; y_pos ++) {
|
|
int start_index = (y_pos * GridPointsX);
|
|
for (int x_pos = 0; x_pos < GridPointsX; x_pos ++) {
|
|
|
|
float x_percent = (float)x_pos / (float)(GridPointsX - 1);
|
|
float y_percent = (float)y_pos / (float)(GridPointsY - 1);
|
|
|
|
float pixel_x = ((bmp_info.bmWidth - 1) * x_percent) + 0.5F;
|
|
float pixel_y = ((bmp_info.bmHeight - 1) * y_percent) + 0.5F;
|
|
|
|
float z_value = 0.0F;
|
|
|
|
//
|
|
// Do we need to sub-sample or super-sample?
|
|
//
|
|
if ( ((bmp_info.bmWidth >= bmp_info.bmHeight) && (GridPointsX < bmp_info.bmWidth)) ||
|
|
((bmp_info.bmHeight >= bmp_info.bmWidth) && (GridPointsY < bmp_info.bmHeight)))
|
|
{
|
|
//
|
|
// Determine what range of pixels this vertex overlays
|
|
//
|
|
/*float next_x_percent = (float)(x_pos + 1) / (float)(GridPointsX - 1);
|
|
float next_y_percent = (float)(y_pos + 1) / (float)(GridPointsY - 1);
|
|
float next_pixel_x = ((bmp_info.bmWidth - 1) * next_x_percent) + 0.5F;
|
|
float next_pixel_y = ((bmp_info.bmHeight - 1) * next_y_percent) + 0.5F;*/
|
|
|
|
float pixel_x_start = pixel_x - half_x_dist;
|
|
float pixel_y_start = pixel_y - half_y_dist;
|
|
float pixel_x_end = pixel_x + half_x_dist;
|
|
float pixel_y_end = pixel_y + half_y_dist;
|
|
|
|
//
|
|
// Clip the region to the bitmap
|
|
//
|
|
int pixel_x0 = WWMath::Clamp (pixel_x_start, 0, (bmp_info.bmWidth - 1));
|
|
int pixel_y0 = WWMath::Clamp (pixel_y_start, 0, (bmp_info.bmHeight - 1));
|
|
int pixel_x1 = WWMath::Clamp (pixel_x_end, 0, (bmp_info.bmWidth - 1));
|
|
int pixel_y1 = WWMath::Clamp (pixel_y_end, 0, (bmp_info.bmHeight - 1));
|
|
float total_intensity = 0.0F;
|
|
int count = 0;
|
|
|
|
//
|
|
// Sum the pixels in this region
|
|
//
|
|
for (int curr_pixel_y = pixel_y0; curr_pixel_y <= pixel_y1; curr_pixel_y ++) {
|
|
for (int curr_pixel_x = pixel_x0; curr_pixel_x <= pixel_x1; curr_pixel_x ++) {
|
|
total_intensity += bmp_bits[(curr_pixel_y * stride) + (curr_pixel_x * 3)];
|
|
count ++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The height value is the average of the pixel intensities in this region
|
|
//
|
|
if (count > 0) {
|
|
float intensity = total_intensity / (count * 255);
|
|
z_value = intensity * scale;
|
|
}
|
|
|
|
} else {
|
|
|
|
int pixel_x0 = (int)pixel_x;
|
|
int pixel_x1 = min (pixel_x0 + 1, bmp_info.bmWidth - 1);
|
|
int pixel_y0 = (int)pixel_y;
|
|
int pixel_y1 = min (pixel_y0 + 1, bmp_info.bmHeight - 1);
|
|
|
|
//
|
|
// Get the z-values of the four corners of the pixel
|
|
//
|
|
float z0 = (float)bmp_bits[(pixel_y0 * stride) + (pixel_x0 * 3)];
|
|
float z1 = (float)bmp_bits[(pixel_y0 * stride) + (pixel_x1 * 3)];
|
|
float z2 = (float)bmp_bits[(pixel_y1 * stride) + (pixel_x1 * 3)];
|
|
float z3 = (float)bmp_bits[(pixel_y1 * stride) + (pixel_x0 * 3)];
|
|
|
|
//
|
|
// Determine how much weight we should give the z-value of each corner
|
|
//
|
|
float x0_percent = 1.0F - ((pixel_x - 0.5F) - pixel_x0);
|
|
float y0_percent = 1.0F - ((pixel_y - 0.5F) - pixel_y0);
|
|
float x1_percent = ((pixel_x - 0.5F) - pixel_x0);
|
|
float y1_percent = ((pixel_y - 0.5F) - pixel_y0);
|
|
|
|
//
|
|
// Calculate what value each corner will contribute
|
|
//
|
|
float z0_part = (x0_percent * y0_percent) * z0;
|
|
float z1_part = (x1_percent * y0_percent) * z1;
|
|
float z2_part = (x1_percent * y1_percent) * z2;
|
|
float z3_part = (x0_percent * y1_percent) * z3;
|
|
|
|
//
|
|
// Sum the contributions from each of the four corners
|
|
// of the grid cell to form the height at this point.
|
|
//
|
|
z_value = (z0_part + z1_part + z2_part + z3_part) / 255.0F;
|
|
z_value *= scale;
|
|
}
|
|
|
|
//
|
|
// Get the vertex at this position and re-assign its new Z value
|
|
//
|
|
Vector3 &vertex_pos = Grid[start_index++];
|
|
vertex_pos.Z = z_value;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now, recalculate the rendering information
|
|
//
|
|
Update_Normals (0, 0, GridPointsX - 1, GridPointsY - 1);
|
|
Update_Patch_Pos_And_Normals (0, 0, GridPointsX - 1, GridPointsY - 1);
|
|
Update_UVs ();
|
|
Update_Texture_Quad_List (0, 0, GridPointsX - 1, GridPointsY - 1);
|
|
Assign_Unique_IDs ();
|
|
|
|
//
|
|
// Free the bitmap buffer
|
|
//
|
|
SAFE_DELETE (bmp_bits);
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
//
|
|
// Allocate_Patches
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
void
|
|
EditableHeightfieldClass::Allocate_Patches (void)
|
|
{
|
|
PatchGridPointsX = 64;
|
|
PatchGridPointsY = 64;
|
|
PatchWidth = PatchGridPointsX * Density;
|
|
PatchHeight = PatchGridPointsY * Density;
|
|
|
|
//
|
|
// Determine how many patches will compose this grid
|
|
//
|
|
PatchesX = int(((float)GridPointsX / (float)PatchGridPointsX) + 0.5F);
|
|
PatchesY = int(((float)GridPointsY / (float)PatchGridPointsY) + 0.5F);
|
|
PatchesX = max (1, PatchesX);
|
|
PatchesY = max (1, PatchesY);
|
|
|
|
//
|
|
// Allocate the grid of patches
|
|
//
|
|
int patch_count = PatchesX * PatchesY;
|
|
PatchGrid = new RenegadeTerrainPatchClass *[patch_count];
|
|
PhysObjGrid = new StaticPhysClass *[patch_count];
|
|
for (int index = 0; index < patch_count; index ++) {
|
|
|
|
//
|
|
// Create this patch
|
|
//
|
|
PatchGrid[index] = new RenegadeTerrainPatchClass;
|
|
PatchGrid[index]->Allocate (PatchGridPointsX, PatchGridPointsY, Density);
|
|
|
|
//
|
|
// Calculate the coordinate for this patch
|
|
//
|
|
int patch_y_pos = (index / PatchesX);
|
|
int patch_x_pos = index - (patch_y_pos * PatchesX);
|
|
|
|
//
|
|
// Let the patch know what its bounding box is...
|
|
//
|
|
float patch_min_x = patch_x_pos * (PatchWidth - Density);
|
|
float patch_min_y = patch_y_pos * (PatchHeight - Density);
|
|
float patch_max_x = (patch_x_pos + 1) * (PatchWidth - Density);
|
|
float patch_max_y = (patch_y_pos + 1) * (PatchHeight - Density);
|
|
PatchGrid[index]->Set_Bounding_Box_Min (Vector3 (patch_min_x, patch_min_y, 0.0F));
|
|
PatchGrid[index]->Set_Bounding_Box_Max (Vector3 (patch_max_x, patch_max_y, 0.0F));
|
|
|
|
//
|
|
// Create the physics object for this heightfield
|
|
//
|
|
PhysObjGrid[index] = new StaticPhysClass;
|
|
PhysObjGrid[index]->Set_Model (PatchGrid[index]);
|
|
PhysObjGrid[index]->Set_Transform (Matrix3D (1));
|
|
|
|
//
|
|
// Add the heightfield to the scene
|
|
//
|
|
::Get_Scene_Editor ()->Add_Static_Object (PhysObjGrid[index]);
|
|
}
|
|
|
|
//
|
|
// Now, recompute the overall dimensions of the heightfield
|
|
//
|
|
GridPointsX = (PatchesX * PatchGridPointsX) - (PatchesX - 1);
|
|
GridPointsY = (PatchesY * PatchGridPointsY) - (PatchesY - 1);
|
|
GridPointCount = GridPointsX * GridPointsY;
|
|
Width = GridPointsX * Density;
|
|
Height = GridPointsY * Density;
|
|
|
|
//
|
|
// Force lighting to be updated...
|
|
//
|
|
::Get_Scene_Editor ()->Update_Lighting ();
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
//
|
|
// Update_Patch_Pos_And_Normals
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
void
|
|
EditableHeightfieldClass::Update_Patch_Pos_And_Normals
|
|
(
|
|
int min_x,
|
|
int min_y,
|
|
int max_x,
|
|
int max_y
|
|
)
|
|
{
|
|
//
|
|
// Determine which patches this operation would possibly affect
|
|
//
|
|
int min_patch_x = min_x / (PatchGridPointsX - 1);
|
|
int min_patch_y = min_y / (PatchGridPointsY - 1);
|
|
int max_patch_x = max_x / (PatchGridPointsX - 1);
|
|
int max_patch_y = max_y / (PatchGridPointsY - 1);
|
|
max_patch_x = min (max_patch_x, (PatchesX - 1));
|
|
max_patch_y = min (max_patch_y, (PatchesY - 1));
|
|
|
|
//
|
|
// Loop over all the patches (by rows)
|
|
//
|
|
for (int y_pos = min_patch_y; y_pos <= max_patch_y; y_pos ++) {
|
|
|
|
//
|
|
// Reset the vertical vertex patch
|
|
//
|
|
int min_vert_y = y_pos * (PatchGridPointsY - 1);
|
|
int max_vert_y = (y_pos + 1) * (PatchGridPointsY - 1);
|
|
|
|
for (int x_pos = min_patch_x; x_pos <= max_patch_x; x_pos ++) {
|
|
|
|
//
|
|
// Reset the horizontal vertex patch
|
|
//
|
|
int min_vert_x = x_pos * (PatchGridPointsX - 1);
|
|
int max_vert_x = (x_pos + 1) * (PatchGridPointsX - 1);
|
|
|
|
//
|
|
// Determine which patch this is
|
|
//
|
|
int patch_index = (y_pos * PatchesX) + x_pos;
|
|
|
|
//
|
|
// Loop over the verts in this patch
|
|
//
|
|
for (int vert_y = min_vert_y; vert_y <= max_vert_y; vert_y ++) {
|
|
for (int vert_x = min_vert_x; vert_x <= max_vert_x; vert_x ++) {
|
|
|
|
//
|
|
// Get the position and normal at this vertex
|
|
//
|
|
const Vector3 &pos = Grid[Grid_Index (vert_x, vert_y)];
|
|
const Vector3 &normal = GridNormals[Grid_Index (vert_x, vert_y)];
|
|
|
|
//
|
|
// Determine what vertex index inside the patch we are changing
|
|
//
|
|
int patch_vert_x = vert_x - min_vert_x;
|
|
int patch_vert_y = vert_y - min_vert_y;
|
|
|
|
//
|
|
// Update the vertex position and normal inside the patch
|
|
//
|
|
PatchGrid[patch_index]->Set_Vertex_Pos (patch_vert_x, patch_vert_y, pos);
|
|
PatchGrid[patch_index]->Set_Vertex_Normal (patch_vert_x, patch_vert_y, normal);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the culling box for this patch
|
|
//
|
|
PhysObjGrid[patch_index]->Update_Cull_Box ();
|
|
}
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
//
|
|
// Free_Patches
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
void
|
|
EditableHeightfieldClass::Free_Patches (void)
|
|
{
|
|
int patch_count = PatchesX * PatchesY;
|
|
|
|
//
|
|
// Release our hold on each patch in the grid
|
|
//
|
|
for (int index = 0; index < patch_count; index ++) {
|
|
::Get_Scene_Editor ()->Remove_Object (PhysObjGrid[index]);
|
|
REF_PTR_RELEASE (PhysObjGrid[index]);
|
|
REF_PTR_RELEASE (PatchGrid[index]);
|
|
}
|
|
|
|
//
|
|
// Free the grid itself
|
|
//
|
|
SAFE_DELETE_ARRAY (PatchGrid);
|
|
SAFE_DELETE_ARRAY (PhysObjGrid);
|
|
PatchGrid = NULL;
|
|
PhysObjGrid = NULL;
|
|
|
|
PatchesX = 0;
|
|
PatchesY = 0;
|
|
PatchGridPointsX = 0;
|
|
PatchGridPointsY = 0;
|
|
PatchWidth = 0;
|
|
PatchHeight = 0;
|
|
return ;
|
|
}
|