2210 lines
57 KiB
C++
2210 lines
57 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 : wwphys *
|
|
* *
|
|
* $Archive:: /Commando/Code/wwphys/renegadeterrainpatch.cpp $*
|
|
* *
|
|
* Author:: Patrick Smith *
|
|
* *
|
|
* $Modtime:: 3/12/02 2:33p $*
|
|
* *
|
|
* $Revision:: 5 $*
|
|
* *
|
|
*---------------------------------------------------------------------------------------------*
|
|
* Functions: *
|
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
#include "renegadeterrainpatch.h"
|
|
|
|
#include "dx8vertexbuffer.h"
|
|
#include "dx8indexbuffer.h"
|
|
#include "dx8wrapper.h"
|
|
#include "sortingrenderer.h"
|
|
#include "rinfo.h"
|
|
#include "camera.h"
|
|
#include "dx8fvf.h"
|
|
#include "vector2i.h"
|
|
#include "terrainmaterial.h"
|
|
#include "renegadeterrainmaterialpass.h"
|
|
#include "persistfactory.h"
|
|
#include "ww3dids.h"
|
|
#include "wwhack.h"
|
|
#include "inttest.h"
|
|
#include "matpass.h"
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
// WWHacks
|
|
////////////////////////////////////////////////////////////////
|
|
DECLARE_FORCE_LINK(RenegadeTerrainPatch);
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
// Local constants
|
|
////////////////////////////////////////////////////////////////
|
|
enum
|
|
{
|
|
CHUNKID_VARIABLES = 0x02261040,
|
|
CHUNKID_HEIGHTS,
|
|
CHUNKID_NORMALS,
|
|
CHUNKID_MATERIAL_LAYERS,
|
|
CHUNKID_MATERIAL_LAYER,
|
|
CHUNKID_QUAD_FLAGS,
|
|
CHUNKID_VERTEX_COLORS,
|
|
};
|
|
|
|
enum
|
|
{
|
|
VARID_GRID_PTS_X = 0x01,
|
|
VARID_GRID_PTS_Y,
|
|
VARID_GRID_PT_COUNT,
|
|
VARID_GRID_BBOX_MIN,
|
|
VARID_GRID_BBOX_MAX,
|
|
VARID_GRID_HEIGHT,
|
|
VARID_GRID_DENSITY,
|
|
VARID_MAX_TEXTURE_PASSES,
|
|
VARID_IS_PRELIT,
|
|
};
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// PersistFactory for RenegadeTerrainPatchClass
|
|
//////////////////////////////////////////////////////////////////////
|
|
SimplePersistFactoryClass<RenegadeTerrainPatchClass, WW3D_PERSIST_CHUNKID_RENEGADE_TERRAIN> _TerrainPatchFactory;
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// RenegadeTerrainPatchClass
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
RenegadeTerrainPatchClass::RenegadeTerrainPatchClass (void) :
|
|
Density (1.0F),
|
|
GridPointsX (0),
|
|
GridPointsY (0),
|
|
GridPointCount (0),
|
|
Grid (NULL),
|
|
GridNormals (NULL),
|
|
VertexColors (NULL),
|
|
QuadFlags (NULL),
|
|
BaseMaterial (NULL),
|
|
LayerMaterial (NULL),
|
|
BaseShader (0),
|
|
LayerShader (0),
|
|
BoundingBoxMin (0, 0, 0),
|
|
BoundingBoxMax (0, 0, 0),
|
|
AreBuffersDirty (true),
|
|
IsPreLit (false)
|
|
{
|
|
Initialize ();
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// RenegadeTerrainPatchClass
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
RenegadeTerrainPatchClass::RenegadeTerrainPatchClass (const RenegadeTerrainPatchClass &src) :
|
|
Density (1.0F),
|
|
GridPointsX (0),
|
|
GridPointsY (0),
|
|
GridPointCount (0),
|
|
Grid (NULL),
|
|
GridNormals (NULL),
|
|
VertexColors (NULL),
|
|
QuadFlags (NULL),
|
|
BaseMaterial (NULL),
|
|
LayerMaterial (NULL),
|
|
BaseShader (0),
|
|
LayerShader (0),
|
|
BoundingBoxMin (0, 0, 0),
|
|
BoundingBoxMax (0, 0, 0),
|
|
AreBuffersDirty (true),
|
|
IsPreLit (false),
|
|
RenderObjClass (src)
|
|
{
|
|
Initialize ();
|
|
*this = src;
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ~RenegadeTerrainPatchClass
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
RenegadeTerrainPatchClass::~RenegadeTerrainPatchClass (void)
|
|
{
|
|
REF_PTR_RELEASE (BaseMaterial);
|
|
REF_PTR_RELEASE (LayerMaterial);
|
|
|
|
Free_Rendering_Buffers ();
|
|
Free_Grid ();
|
|
Free_Materials ();
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// operator=
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
const RenegadeTerrainPatchClass &
|
|
RenegadeTerrainPatchClass::operator= (const RenegadeTerrainPatchClass &src)
|
|
{
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Initialize
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
RenegadeTerrainPatchClass::Initialize (void)
|
|
{
|
|
Set_Collision_Type (COLLISION_TYPE_PHYSICAL | COLLISION_TYPE_PROJECTILE | COLLISION_TYPE_CAMERA);
|
|
|
|
//
|
|
// Initialize the material settings
|
|
//
|
|
Initialize_Material ();
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Allocate
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
RenegadeTerrainPatchClass::Allocate (int points_x, int points_y, 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 = points_x;
|
|
GridPointsY = points_y;
|
|
GridPointsX = max (1, GridPointsX);
|
|
GridPointsY = max (1, GridPointsY);
|
|
GridPointCount = (GridPointsX * GridPointsY);
|
|
BoundingBoxMin.Z = 0.0F;
|
|
BoundingBoxMax.Z = 0.0F;
|
|
|
|
//
|
|
// Allocate and initialize the grid and supporting data structures
|
|
//
|
|
Allocate_Grid ();
|
|
|
|
Invalidate_Cached_Bounding_Volumes();
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Allocate_Grid
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
RenegadeTerrainPatchClass::Allocate_Grid (void)
|
|
{
|
|
//
|
|
// Allocate the per-vertex arrays
|
|
//
|
|
GridNormals = new Vector3[GridPointCount];
|
|
Grid = new Vector3[GridPointCount];
|
|
VertexColors = new Vector3[GridPointCount];
|
|
|
|
//
|
|
// Initialize the vertex color array to white
|
|
//
|
|
for (int index = 0; index < GridPointCount; index ++) {
|
|
VertexColors[index].Set (1.0F, 1.0F, 1.0F);
|
|
}
|
|
|
|
//
|
|
// 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);
|
|
|
|
AreBuffersDirty = true;
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Free_Grid
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
RenegadeTerrainPatchClass::Free_Grid (void)
|
|
{
|
|
if (Grid != NULL) {
|
|
delete [] Grid;
|
|
Grid = NULL;
|
|
}
|
|
|
|
if (GridNormals != NULL) {
|
|
delete [] GridNormals;
|
|
GridNormals = NULL;
|
|
}
|
|
|
|
if (VertexColors != NULL) {
|
|
delete [] VertexColors;
|
|
VertexColors = NULL;
|
|
}
|
|
|
|
if (QuadFlags != NULL) {
|
|
delete [] QuadFlags;
|
|
QuadFlags = NULL;
|
|
}
|
|
|
|
Free_Materials ();
|
|
|
|
BoundingBoxMin.Z = 0;
|
|
BoundingBoxMax.Z = 0;
|
|
Density = 1.0F;
|
|
GridPointsX = 0;
|
|
GridPointsY = 0;
|
|
GridPointCount = 0;
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Render
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
RenegadeTerrainPatchClass::Render (RenderInfoClass &rinfo)
|
|
{
|
|
//
|
|
// Make sure our vertex and index buffers are up to date.
|
|
//
|
|
if (AreBuffersDirty) {
|
|
Update_Rendering_Buffers ();
|
|
AreBuffersDirty = false;
|
|
}
|
|
|
|
if (IsPreLit) {
|
|
BaseMaterial->Set_Ambient_Color_Source (VertexMaterialClass::COLOR1);
|
|
BaseMaterial->Set_Diffuse_Color_Source (VertexMaterialClass::COLOR1);
|
|
}
|
|
|
|
//
|
|
// Install the mesh'es transform. NOTE, this could go wrong if someone changes the
|
|
// transform between the time that the mesh is rendered and the time that the decal
|
|
// mesh is rendered... It shouldn't happen though.
|
|
//
|
|
DX8Wrapper::Set_Transform (D3DTS_WORLD, Get_Transform ());
|
|
|
|
if (rinfo.light_environment != NULL) {
|
|
DX8Wrapper::Set_Light_Environment (rinfo.light_environment);
|
|
}
|
|
|
|
//
|
|
// If the object's inherent materials are not disabled, render the terrain
|
|
//
|
|
if ((rinfo.Current_Override_Flags() & RenderInfoClass::RINFO_OVERRIDE_ADDITIONAL_PASSES_ONLY) == 0)
|
|
{
|
|
//
|
|
// Render the base passes first
|
|
//
|
|
for (int index = 0; index < MaterialPassList.Count (); index ++) {
|
|
Render_By_Texture (index, RenegadeTerrainMaterialPassClass::PASS_BASE);
|
|
}
|
|
|
|
//
|
|
// Do a "z-bias" to offset the alpha polys by just a teeny bit. This
|
|
// avoids any z-fighting issues with the different passes.
|
|
//
|
|
//DX8Wrapper::Set_Pseudo_ZBias (1);
|
|
|
|
//
|
|
// Next render the alpha passes
|
|
//
|
|
for (index = 0; index < MaterialPassList.Count (); index ++) {
|
|
Render_By_Texture (index, RenegadeTerrainMaterialPassClass::PASS_ALPHA);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Render the procedural material passes
|
|
//
|
|
for (int i=0; i<rinfo.Additional_Pass_Count(); i++) {
|
|
|
|
MaterialPassClass * matpass = rinfo.Peek_Additional_Pass(i);
|
|
Render_Procedural_Material_Pass(matpass);
|
|
}
|
|
|
|
//
|
|
// Reset the z-bias
|
|
//
|
|
//DX8Wrapper::Set_Pseudo_ZBias (0);
|
|
|
|
//
|
|
// Reset the z-bias
|
|
//
|
|
//DX8Wrapper::Set_DX8_ZBias (0);
|
|
|
|
return ;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Render_Procedural_Material_Pass
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
RenegadeTerrainPatchClass::Render_Procedural_Material_Pass(MaterialPassClass * matpass)
|
|
{
|
|
#if 0
|
|
if ((pass->Get_Cull_Volume() != NULL) && (MaterialPassClass::Is_Per_Polygon_Culling_Enabled())) {
|
|
|
|
/*
|
|
** Generate the APT
|
|
*/
|
|
temp_apt.Delete_All(false);
|
|
|
|
Matrix3D modeltminv;
|
|
Get_Transform().Get_Orthogonal_Inverse(modeltminv);
|
|
|
|
OBBoxClass localbox;
|
|
OBBoxClass::Transform(modeltminv,*(pass->Get_Cull_Volume()),&localbox);
|
|
|
|
Vector3 view_dir;
|
|
localbox.Basis.Get_Z_Vector(&view_dir);
|
|
view_dir = -view_dir;
|
|
|
|
if (Model->Has_Cull_Tree()) {
|
|
Model->Generate_Rigid_APT(localbox,view_dir,temp_apt);
|
|
} else {
|
|
Model->Generate_Rigid_APT(view_dir,temp_apt);
|
|
}
|
|
|
|
if (temp_apt.Count() > 0) {
|
|
|
|
int buftype = BUFFER_TYPE_DYNAMIC_DX8;
|
|
if (Model->Get_Flag(MeshGeometryClass::SORT) && WW3D::Is_Sorting_Enabled()) {
|
|
buftype = BUFFER_TYPE_DYNAMIC_SORTING;
|
|
}
|
|
|
|
/*
|
|
** Spew triangles in the APT into the dynamic index buffer
|
|
*/
|
|
int min_v = Model->Get_Vertex_Count();
|
|
int max_v = 0;
|
|
|
|
DynamicIBAccessClass dynamic_ib(buftype,temp_apt.Count() * 3);
|
|
{
|
|
DynamicIBAccessClass::WriteLockClass lock(&dynamic_ib);
|
|
unsigned short * indices = lock.Get_Index_Array();
|
|
const TriIndex * polys = Model->Get_Polygon_Array();
|
|
|
|
for (int i=0; i < temp_apt.Count(); i++)
|
|
{
|
|
unsigned v0 = polys[temp_apt[i]].I;
|
|
unsigned v1 = polys[temp_apt[i]].J;
|
|
unsigned v2 = polys[temp_apt[i]].K;
|
|
|
|
indices[i*3 + 0] = (unsigned short)v0;
|
|
indices[i*3 + 1] = (unsigned short)v1;
|
|
indices[i*3 + 2] = (unsigned short)v2;
|
|
|
|
min_v = WWMath::Min(v0,min_v);
|
|
min_v = WWMath::Min(v1,min_v);
|
|
min_v = WWMath::Min(v2,min_v);
|
|
|
|
max_v = WWMath::Max(v0,max_v);
|
|
max_v = WWMath::Max(v1,max_v);
|
|
max_v = WWMath::Max(v2,max_v);
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Render
|
|
*/
|
|
int vertex_offset = PolygonRendererList.Peek_Head()->Get_Vertex_Offset();
|
|
pass->Install_Materials();
|
|
|
|
DX8Wrapper::Set_Transform(D3DTS_WORLD,Get_Transform());
|
|
DX8Wrapper::Set_Index_Buffer(dynamic_ib,vertex_offset);
|
|
|
|
DX8Wrapper::Draw_Triangles(
|
|
0,
|
|
temp_apt.Count(),
|
|
min_v,
|
|
max_v-min_v+1);
|
|
}
|
|
} else {
|
|
#endif
|
|
/*
|
|
** Normal mesh case, render polys with this mesh's transform
|
|
*/
|
|
matpass->Install_Materials();
|
|
|
|
//
|
|
// Render the base passes first
|
|
//
|
|
for (int index = 0; index < MaterialPassList.Count (); index ++) {
|
|
Submit_Rendering_Buffers (index, RenegadeTerrainMaterialPassClass::PASS_BASE);
|
|
|
|
//
|
|
// Alias some data
|
|
//
|
|
DynamicVectorClass<int> &vert_list = MaterialPassList[index]->VertexRenderList[RenegadeTerrainMaterialPassClass::PASS_BASE];
|
|
DynamicVectorClass<int> &quad_list = MaterialPassList[index]->QuadList[RenegadeTerrainMaterialPassClass::PASS_BASE];
|
|
|
|
//
|
|
// Determine how many polygons and verts to render
|
|
//
|
|
int quad_count = quad_list.Count ();
|
|
int vert_count = vert_list.Count ();
|
|
int poly_count = quad_count * 2;
|
|
|
|
//
|
|
// Draw the mesh!
|
|
//
|
|
DX8Wrapper::Draw_Triangles (BUFFER_TYPE_DYNAMIC_DX8, 0, poly_count, 0, vert_count);
|
|
}
|
|
// }
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Submit_Rendering_Buffers
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
RenegadeTerrainPatchClass::Submit_Rendering_Buffers (int texture_index, int pass_type)
|
|
{
|
|
//
|
|
// Don't render this layer if there isn't anything to render!
|
|
//
|
|
if ( MaterialPassList[texture_index]->VertexBuffers[pass_type] == NULL ||
|
|
MaterialPassList[texture_index]->IndexBuffers[pass_type] == NULL)
|
|
{
|
|
return ;
|
|
}
|
|
|
|
//
|
|
// Set vertex and index buffers
|
|
//
|
|
DX8Wrapper::Set_Vertex_Buffer (MaterialPassList[texture_index]->VertexBuffers[pass_type]);
|
|
DX8Wrapper::Set_Index_Buffer (MaterialPassList[texture_index]->IndexBuffers[pass_type], 0);
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Render_By_Texture
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
RenegadeTerrainPatchClass::Render_By_Texture (int texture_index, int pass_type)
|
|
{
|
|
//
|
|
// Don't render this layer if there isn't anything to render!
|
|
//
|
|
if ( MaterialPassList[texture_index]->VertexBuffers[pass_type] == NULL ||
|
|
MaterialPassList[texture_index]->IndexBuffers[pass_type] == NULL)
|
|
{
|
|
return ;
|
|
}
|
|
|
|
//
|
|
// Pass the vertex and index buffers onto DX8
|
|
//
|
|
Submit_Rendering_Buffers (texture_index, pass_type);
|
|
|
|
//
|
|
// Configure the texture
|
|
//
|
|
DX8Wrapper::Set_Texture (0, MaterialPassList[texture_index]->Material->Peek_Texture ());
|
|
DX8Wrapper::Set_Texture (1, NULL);
|
|
|
|
//
|
|
// Configure the material and shader
|
|
//
|
|
if (pass_type == RenegadeTerrainMaterialPassClass::PASS_BASE) {
|
|
DX8Wrapper::Set_Material (BaseMaterial);
|
|
DX8Wrapper::Set_Shader (BaseShader);
|
|
} else {
|
|
DX8Wrapper::Set_Material (LayerMaterial);
|
|
DX8Wrapper::Set_Shader (LayerShader);
|
|
}
|
|
|
|
//
|
|
// Alias some data
|
|
//
|
|
DynamicVectorClass<int> &vert_list = MaterialPassList[texture_index]->VertexRenderList[pass_type];
|
|
DynamicVectorClass<int> &quad_list = MaterialPassList[texture_index]->QuadList[pass_type];
|
|
|
|
//
|
|
// Determine how many polygons and verts to render
|
|
//
|
|
int quad_count = quad_list.Count ();
|
|
int vert_count = vert_list.Count ();
|
|
int poly_count = quad_count * 2;
|
|
|
|
//
|
|
// Draw the mesh!
|
|
//
|
|
DX8Wrapper::Draw_Triangles (BUFFER_TYPE_DYNAMIC_DX8, 0, poly_count, 0, vert_count);
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Free_Rendering_Buffers
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
RenegadeTerrainPatchClass::Free_Rendering_Buffers (void)
|
|
{
|
|
//
|
|
// Free each rendering layer
|
|
//
|
|
for (int index = 0; index < MaterialPassList.Count (); index ++) {
|
|
for (int pass = 0; pass < RenegadeTerrainMaterialPassClass::PASS_COUNT; pass ++) {
|
|
REF_PTR_RELEASE (MaterialPassList[index]->IndexBuffers[pass]);
|
|
REF_PTR_RELEASE (MaterialPassList[index]->VertexBuffers[pass]);
|
|
}
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Update_Rendering_Buffers
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
RenegadeTerrainPatchClass::Update_Rendering_Buffers (void)
|
|
{
|
|
Free_Rendering_Buffers ();
|
|
|
|
//
|
|
// Build the rendering buffers for each layer
|
|
//
|
|
for (int index = 0; index < MaterialPassList.Count (); index ++) {
|
|
Build_Rendering_Buffers (index, RenegadeTerrainMaterialPassClass::PASS_BASE);
|
|
Build_Rendering_Buffers (index, RenegadeTerrainMaterialPassClass::PASS_ALPHA);
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Build_Rendering_Buffers
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
RenegadeTerrainPatchClass::Build_Rendering_Buffers (int texture_index, int pass_type)
|
|
{
|
|
//
|
|
// Alias some data
|
|
//
|
|
RenegadeTerrainMaterialPassClass *material_pass = MaterialPassList[texture_index];
|
|
DynamicVectorClass<int> &vert_list = material_pass->VertexRenderList[pass_type];
|
|
DynamicVectorClass<int> &quad_list = material_pass->QuadList[pass_type];
|
|
int *vertex_index_map = material_pass->VertexIndexMap[pass_type];
|
|
|
|
//
|
|
// Determine the size of our data
|
|
//
|
|
int quad_count = quad_list.Count ();
|
|
int vert_count = vert_list.Count ();
|
|
int poly_count = quad_count * 2;
|
|
|
|
//
|
|
// Don't build the buffers if there's nothing to render in this layer
|
|
//
|
|
if (poly_count == 0 || vert_count == 0) {
|
|
return ;
|
|
}
|
|
|
|
//
|
|
// Allocate the vertex and index buffers
|
|
//
|
|
material_pass->IndexBuffers[pass_type] = new DX8IndexBufferClass (poly_count * 3);
|
|
material_pass->VertexBuffers[pass_type] = new DX8VertexBufferClass (DX8_FVF_XYZNDUV1, vert_count);
|
|
|
|
//
|
|
// Write index data to index buffers
|
|
//
|
|
{ // scope for lock
|
|
|
|
int col_count = (GridPointsX - 1);
|
|
|
|
//
|
|
// Lock the index buffer
|
|
//
|
|
IndexBufferClass::WriteLockClass lock (material_pass->IndexBuffers[pass_type]);
|
|
unsigned short * indices = lock.Get_Index_Array();
|
|
|
|
//
|
|
// Now, compose the triangles by indexing the verts into the vertex buffer
|
|
//
|
|
int ib_index = 0;
|
|
for (int index = 0; index < quad_count; index ++) {
|
|
|
|
//
|
|
// Determine which quad we're rendering
|
|
//
|
|
int quad_index = quad_list[index];
|
|
int quad_y_pos = (quad_index / col_count);
|
|
int quad_x_pos = quad_index - (quad_y_pos * col_count);
|
|
|
|
//
|
|
// Determine the "starting" vertex index from the current quad
|
|
//
|
|
int curr_src_index = (quad_y_pos * GridPointsX) + quad_x_pos;
|
|
|
|
//
|
|
// Calculate the 4 vertex indices that compose this quad
|
|
//
|
|
int v0_index = curr_src_index;
|
|
int v1_index = curr_src_index + 1;
|
|
int v2_index = curr_src_index + GridPointsX + 1;
|
|
int v3_index = curr_src_index + GridPointsX;
|
|
|
|
//
|
|
// Add the current quad to the index buffer
|
|
//
|
|
indices[ib_index ++] = (unsigned short)vertex_index_map[v0_index];
|
|
indices[ib_index ++] = (unsigned short)vertex_index_map[v2_index];
|
|
indices[ib_index ++] = (unsigned short)vertex_index_map[v3_index];
|
|
|
|
indices[ib_index ++] = (unsigned short)vertex_index_map[v2_index];
|
|
indices[ib_index ++] = (unsigned short)vertex_index_map[v0_index];
|
|
indices[ib_index ++] = (unsigned short)vertex_index_map[v1_index];
|
|
}
|
|
|
|
} // end scope for lock
|
|
|
|
const Vector3 white (1.0F, 1.0F, 1.0F);
|
|
|
|
{
|
|
//
|
|
// Lock the vertex buffer
|
|
//
|
|
VertexBufferClass::WriteLockClass lock (material_pass->VertexBuffers[pass_type]);
|
|
VertexFormatXYZNDUV1 *vertices = (VertexFormatXYZNDUV1 *)lock.Get_Vertex_Array ();
|
|
|
|
//
|
|
// Specify some default values
|
|
//
|
|
const static Vector3 default_normal (0.0F, 0.0F, 0.0F);
|
|
const static Vector2 default_uv (0.0F, 0.0F);
|
|
|
|
//
|
|
// Write each vertex's definition to the dynamic vertex buffer
|
|
//
|
|
for (int index = 0; index < vert_count; index ++) {
|
|
|
|
int vert_index = vert_list[index];
|
|
|
|
//
|
|
// Set the vertex position and normal
|
|
//
|
|
vertices[index].x = Grid[vert_index].X;
|
|
vertices[index].y = Grid[vert_index].Y;
|
|
vertices[index].z = Grid[vert_index].Z;
|
|
vertices[index].nx = GridNormals[vert_index].X;
|
|
vertices[index].ny = GridNormals[vert_index].Y;
|
|
vertices[index].nz = GridNormals[vert_index].Z;
|
|
|
|
//
|
|
// Set the UV mapping
|
|
//
|
|
vertices[index].u1 = material_pass->GridUVs[vert_index].X;
|
|
vertices[index].v1 = material_pass->GridUVs[vert_index].Y;
|
|
|
|
//
|
|
// Set the vertex color
|
|
//
|
|
if (pass_type == RenegadeTerrainMaterialPassClass::PASS_BASE) {
|
|
vertices[index].diffuse = DX8Wrapper::Convert_Color (VertexColors[vert_index], 1.0F);
|
|
} else {
|
|
|
|
//
|
|
// Compose a vertex color using the vertex alpha
|
|
//
|
|
float alpha = material_pass->VertexAlpha[vert_index];
|
|
vertices[index].diffuse = DX8Wrapper::Convert_Color (VertexColors[vert_index], alpha);
|
|
}
|
|
}
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Initialize_Material
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
RenegadeTerrainPatchClass::Initialize_Material (void)
|
|
{
|
|
//
|
|
// Allocate the vertex material
|
|
//
|
|
WWASSERT (BaseMaterial == NULL);
|
|
BaseMaterial = NEW_REF(VertexMaterialClass, ());
|
|
BaseMaterial->Set_Ambient (1.0F, 1.0F, 1.0F);
|
|
BaseMaterial->Set_Diffuse (1.0F, 1.0F, 1.0F);
|
|
BaseMaterial->Set_Specular (0, 0, 0);
|
|
BaseMaterial->Set_Emissive (0.0F, 0.0F, 0.0F);
|
|
BaseMaterial->Set_Opacity (1.0F);
|
|
BaseMaterial->Set_Shininess (0.0F);
|
|
BaseMaterial->Set_Lighting (true);
|
|
|
|
LayerMaterial = NEW_REF(VertexMaterialClass, ());
|
|
LayerMaterial->Set_Ambient (1.0F, 1.0F, 1.0F);
|
|
LayerMaterial->Set_Diffuse (1.0F, 1.0F, 1.0F);
|
|
LayerMaterial->Set_Specular (0, 0, 0);
|
|
LayerMaterial->Set_Emissive (0.0F, 0.0F, 0.0F);
|
|
LayerMaterial->Set_Opacity (1.0F);
|
|
LayerMaterial->Set_Shininess (0.0F);
|
|
LayerMaterial->Set_Lighting (true);
|
|
LayerMaterial->Set_Ambient_Color_Source (VertexMaterialClass::COLOR1);
|
|
LayerMaterial->Set_Diffuse_Color_Source (VertexMaterialClass::COLOR1);
|
|
|
|
//
|
|
// Initialize the shader
|
|
//
|
|
BaseShader = ShaderClass::_PresetOpaqueShader;
|
|
LayerShader = ShaderClass::_PresetAlphaShader;
|
|
|
|
BaseShader.Set_Fog_Func(ShaderClass::FOG_ENABLE);
|
|
LayerShader.Set_Fog_Func(ShaderClass::FOG_ENABLE);
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Free_Materials
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
RenegadeTerrainPatchClass::Free_Materials (void)
|
|
{
|
|
//
|
|
// Free each material pass
|
|
//
|
|
for (int index = 0; index < MaterialPassList.Count (); index ++) {
|
|
delete MaterialPassList[index];
|
|
}
|
|
|
|
MaterialPassList.Delete_All ();
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Get_Obj_Space_Bounding_Sphere
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
RenegadeTerrainPatchClass::Get_Obj_Space_Bounding_Sphere (SphereClass &sphere) const
|
|
{
|
|
float delta_x = BoundingBoxMax.X - BoundingBoxMin.X;
|
|
float delta_y = BoundingBoxMax.Y - BoundingBoxMin.Y;
|
|
float delta_z = BoundingBoxMax.Z - BoundingBoxMin.Z;
|
|
sphere.Center.X = BoundingBoxMin.X + (delta_x * 0.5F);
|
|
sphere.Center.Y = BoundingBoxMin.Y + (delta_y * 0.5F);
|
|
sphere.Center.Z = BoundingBoxMin.Z + (delta_z * 0.5F);
|
|
|
|
//
|
|
// Determine which radius to use as the largest delta
|
|
//
|
|
sphere.Radius = max (delta_x, delta_y);
|
|
sphere.Radius = max (sphere.Radius, delta_z);
|
|
sphere.Radius += 1.0F;
|
|
//sphere.Radius *= 2.0F;
|
|
|
|
// sphere.Center.Set (0, 0, 0);
|
|
// sphere.Radius = 1000.0F;
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Get_Obj_Space_Bounding_Box
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
RenegadeTerrainPatchClass::Get_Obj_Space_Bounding_Box (AABoxClass &box) const
|
|
{
|
|
float delta_x = BoundingBoxMax.X - BoundingBoxMin.X;
|
|
float delta_y = BoundingBoxMax.Y - BoundingBoxMin.Y;
|
|
float delta_z = BoundingBoxMax.Z - BoundingBoxMin.Z;
|
|
box.Center.X = BoundingBoxMin.X + (delta_x * 0.5F);
|
|
box.Center.Y = BoundingBoxMin.Y + (delta_y * 0.5F);
|
|
box.Center.Z = BoundingBoxMin.Z + (delta_z * 0.5F);
|
|
|
|
//
|
|
// Fill in the extents of this box (really they are half-extents)
|
|
//
|
|
box.Extent.X = (delta_x) + 1.0F;
|
|
box.Extent.Y = (delta_y) + 1.0F;
|
|
box.Extent.Z = (delta_z) + 1.0F;
|
|
|
|
// box.Center.Set (0, 0, 0);;
|
|
// box.Extent.Set (500.0F, 500.0F, 500.0F);
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Cast_OBBox
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
bool
|
|
RenegadeTerrainPatchClass::Cast_OBBox (OBBoxCollisionTestClass &boxtest)
|
|
{
|
|
//
|
|
// Skip this object if it doesn't match the collision type
|
|
//
|
|
if ((Get_Collision_Type() & boxtest.CollisionType) == 0) {
|
|
return false;
|
|
}
|
|
|
|
if (boxtest.Result->StartBad) {
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Get the world to object space transform
|
|
//
|
|
Matrix3D world_to_obj_tm;
|
|
Get_Inverse_Transform (world_to_obj_tm);
|
|
|
|
//
|
|
// Transform the OBBox into heightfield space
|
|
//
|
|
OBBoxClass obj_space_box;
|
|
OBBoxClass::Transform (world_to_obj_tm, boxtest.Box, &obj_space_box);
|
|
|
|
//
|
|
// Transform the movement vector into heightfield space
|
|
//
|
|
Vector3 obj_space_move = world_to_obj_tm * boxtest.Move;
|
|
|
|
//
|
|
// Create a new coliision box test object in heightfield space
|
|
//
|
|
OBBoxCollisionTestClass obj_space_test (obj_space_box, obj_space_move,
|
|
boxtest.Result, boxtest.CollisionType);
|
|
|
|
//
|
|
// Calculate what grid cells this box is "possibly" intersecting
|
|
//
|
|
int min_x = Get_Quad_Index_X (obj_space_test.SweepMin.X);
|
|
int min_y = Get_Quad_Index_Y (obj_space_test.SweepMin.Y);
|
|
int max_x = Get_Quad_Index_X (obj_space_test.SweepMax.X);
|
|
int max_y = Get_Quad_Index_Y (obj_space_test.SweepMax.Y);
|
|
|
|
int closest_index = 0;
|
|
|
|
//
|
|
// Now check all the quads in this region
|
|
//
|
|
bool retval = false;
|
|
for (int y_pos = min_y; y_pos <= max_y; y_pos ++) {
|
|
for (int x_pos = min_x; x_pos <= max_x; x_pos ++) {
|
|
|
|
int start_index = Grid_Index (x_pos, y_pos);
|
|
|
|
int v0_index = start_index;
|
|
int v1_index = start_index + 1;
|
|
int v2_index = start_index + GridPointsX + 1;
|
|
int v3_index = start_index + GridPointsX;
|
|
|
|
//
|
|
// Compose the two triangles for the collision check
|
|
//
|
|
TriClass tri1;
|
|
TriClass tri2;
|
|
Vector3 norm1 (0, 0, 0);
|
|
Vector3 norm2 (0, 0, 0);
|
|
tri1.N = &norm1;
|
|
tri2.N = &norm2;
|
|
tri1.V[0] = &Grid[v0_index];
|
|
tri1.V[1] = &Grid[v2_index];
|
|
tri1.V[2] = &Grid[v3_index];
|
|
tri2.V[0] = &Grid[v2_index];
|
|
tri2.V[1] = &Grid[v0_index];
|
|
tri2.V[2] = &Grid[v1_index];
|
|
tri1.Compute_Normal ();
|
|
tri2.Compute_Normal ();
|
|
|
|
//
|
|
// Do the collision test to determine if the box intersects either of these triangles
|
|
//
|
|
float percent = obj_space_test.Result->Fraction;
|
|
retval |= CollisionMath::Collide (obj_space_test.Box, obj_space_test.Move, tri1, Vector3 (0, 0, 0), obj_space_test.Result);
|
|
retval |= CollisionMath::Collide (obj_space_test.Box, obj_space_test.Move, tri2, Vector3 (0, 0, 0), obj_space_test.Result);
|
|
|
|
//
|
|
// Is this the closest collision yet?
|
|
//
|
|
if (obj_space_test.Result->Fraction < percent) {
|
|
closest_index = v0_index;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make sure to return our pointer to the caller
|
|
//
|
|
if (retval) {
|
|
|
|
//
|
|
// Determine which surface type was hit
|
|
//
|
|
if (MaterialPassList.Count () > 0) {
|
|
int best_pass = 0;
|
|
float best_alpha = 0;
|
|
for (int index = 0; index < MaterialPassList.Count (); index ++) {
|
|
float alpha = MaterialPassList[index]->VertexAlpha[closest_index];
|
|
if (alpha > best_alpha && alpha != 1.0F) {
|
|
best_alpha = alpha;
|
|
best_pass = index;
|
|
}
|
|
}
|
|
if (MaterialPassList[best_pass]->Material != NULL) {
|
|
boxtest.Result->SurfaceType = MaterialPassList[best_pass]->Material->Get_Surface_Type ();
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Transform the result back into world-space
|
|
//
|
|
if (boxtest.Result->ComputeContactPoint) {
|
|
boxtest.Result->ContactPoint = Get_Transform () * boxtest.Result->ContactPoint;
|
|
}
|
|
|
|
boxtest.CollidedRenderObj = this;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intersect_AABox
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
bool
|
|
RenegadeTerrainPatchClass::Intersect_AABox (AABoxIntersectionTestClass &boxtest)
|
|
{
|
|
//
|
|
// Skip this object if it doesn't match the collision type
|
|
//
|
|
if ((Get_Collision_Type() & boxtest.CollisionType) == 0) {
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// First, transform the box into heightfield (object) space
|
|
//
|
|
Matrix3D world_to_obj_tm;
|
|
Get_Inverse_Transform (world_to_obj_tm);
|
|
|
|
AABoxClass obj_space_box;
|
|
AABoxClass::Transform (world_to_obj_tm, boxtest.Box, &obj_space_box);
|
|
|
|
//
|
|
// Calculate what grid cells this box is "possibly" intersecting
|
|
//
|
|
int min_x = Get_Quad_Index_X (obj_space_box.Center.X - obj_space_box.Extent.X);
|
|
int min_y = Get_Quad_Index_Y (obj_space_box.Center.Y - obj_space_box.Extent.Y);
|
|
int max_x = Get_Quad_Index_X (obj_space_box.Center.X + obj_space_box.Extent.X);
|
|
int max_y = Get_Quad_Index_Y (obj_space_box.Center.Y + obj_space_box.Extent.Y);
|
|
|
|
//
|
|
// Now check all the quads in this region
|
|
//
|
|
bool retval = false;
|
|
for (int y_pos = min_y; y_pos <= max_y; y_pos ++) {
|
|
for (int x_pos = min_x; x_pos <= max_x; x_pos ++) {
|
|
|
|
int start_index = Grid_Index (x_pos, y_pos);
|
|
int quad_index = y_pos * (GridPointsX - 1) + x_pos;
|
|
|
|
//
|
|
// Skip this quad if its hidden
|
|
//
|
|
if (QuadFlags[quad_index] & QF_HIDDEN) {
|
|
continue;
|
|
}
|
|
|
|
int v0_index = start_index;
|
|
int v1_index = start_index + 1;
|
|
int v2_index = start_index + GridPointsX + 1;
|
|
int v3_index = start_index + GridPointsX;
|
|
|
|
//
|
|
// Compose the two triangles for the collision check
|
|
//
|
|
TriClass tri1;
|
|
TriClass tri2;
|
|
Vector3 norm1 (0, 0, 0);
|
|
Vector3 norm2 (0, 0, 0);
|
|
tri1.N = &norm1;
|
|
tri2.N = &norm2;
|
|
tri1.V[0] = &Grid[v0_index];
|
|
tri1.V[1] = &Grid[v2_index];
|
|
tri1.V[2] = &Grid[v3_index];
|
|
tri2.V[0] = &Grid[v2_index];
|
|
tri2.V[1] = &Grid[v0_index];
|
|
tri2.V[2] = &Grid[v1_index];
|
|
tri1.Compute_Normal ();
|
|
tri2.Compute_Normal ();
|
|
|
|
//
|
|
// Do the collision test to determine if the box intersects either of these triangles
|
|
//
|
|
retval |= (CollisionMath::Overlap_Test (obj_space_box, tri1) != CollisionMath::OUTSIDE);
|
|
retval |= (CollisionMath::Overlap_Test (obj_space_box, tri2) != CollisionMath::OUTSIDE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make sure to return our pointer to the caller
|
|
//
|
|
/*if (retval) {
|
|
|
|
boxtest.CollidedRenderObj = this;
|
|
}*/
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Intersect_OBBox
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
bool
|
|
RenegadeTerrainPatchClass::Intersect_OBBox (OBBoxIntersectionTestClass &boxtest)
|
|
{
|
|
//
|
|
// Skip this object if it doesn't match the collision type
|
|
//
|
|
if ((Get_Collision_Type() & boxtest.CollisionType) == 0) {
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// First, transform the box into heightfield (object) space
|
|
//
|
|
Matrix3D world_to_obj_tm;
|
|
Get_Inverse_Transform (world_to_obj_tm);
|
|
|
|
OBBoxClass obj_space_box;
|
|
OBBoxClass::Transform (world_to_obj_tm, boxtest.Box, &obj_space_box);
|
|
|
|
OBBoxIntersectionTestClass obj_space_test (obj_space_box, boxtest.CollisionType);
|
|
const AABoxClass &obj_space_aabox = obj_space_test.BoundingBox;
|
|
|
|
//
|
|
// Calculate what grid cells this box is "possibly" intersecting
|
|
//
|
|
int min_x = Get_Quad_Index_X (obj_space_aabox.Center.X - obj_space_aabox.Extent.X);
|
|
int min_y = Get_Quad_Index_Y (obj_space_aabox.Center.Y - obj_space_aabox.Extent.Y);
|
|
int max_x = Get_Quad_Index_X (obj_space_aabox.Center.X + obj_space_aabox.Extent.X);
|
|
int max_y = Get_Quad_Index_Y (obj_space_aabox.Center.Y + obj_space_aabox.Extent.Y);
|
|
|
|
//
|
|
// Now check all the quads in this region
|
|
//
|
|
bool retval = false;
|
|
for (int y_pos = min_y; y_pos <= max_y; y_pos ++) {
|
|
for (int x_pos = min_x; x_pos <= max_x; x_pos ++) {
|
|
|
|
int start_index = Grid_Index (x_pos, y_pos);
|
|
|
|
int v0_index = start_index;
|
|
int v1_index = start_index + 1;
|
|
int v2_index = start_index + GridPointsX + 1;
|
|
int v3_index = start_index + GridPointsX;
|
|
|
|
//
|
|
// Compose the two triangles for the collision check
|
|
//
|
|
TriClass tri1;
|
|
TriClass tri2;
|
|
Vector3 norm1 (0, 0, 0);
|
|
Vector3 norm2 (0, 0, 0);
|
|
tri1.N = &norm1;
|
|
tri2.N = &norm2;
|
|
tri1.V[0] = &Grid[v0_index];
|
|
tri1.V[1] = &Grid[v2_index];
|
|
tri1.V[2] = &Grid[v3_index];
|
|
tri2.V[0] = &Grid[v2_index];
|
|
tri2.V[1] = &Grid[v0_index];
|
|
tri2.V[2] = &Grid[v1_index];
|
|
tri1.Compute_Normal ();
|
|
tri2.Compute_Normal ();
|
|
|
|
//
|
|
// Do the collision test to determine if the box intersects either of these triangles
|
|
//
|
|
retval |= (CollisionMath::Overlap_Test (obj_space_box, tri1) != CollisionMath::OUTSIDE);
|
|
retval |= (CollisionMath::Overlap_Test (obj_space_box, tri2) != CollisionMath::OUTSIDE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make sure to return our pointer to the caller
|
|
//
|
|
/*if (retval) {
|
|
|
|
boxtest.CollidedRenderObj = this;
|
|
}*/
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Cast_AABox
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
bool
|
|
RenegadeTerrainPatchClass::Cast_AABox (AABoxCollisionTestClass &boxtest)
|
|
{
|
|
//
|
|
// Skip this object if it doesn't match the collision type
|
|
//
|
|
if ((Get_Collision_Type() & boxtest.CollisionType) == 0) {
|
|
return false;
|
|
}
|
|
|
|
if (boxtest.Result->StartBad) {
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// First, transform the boxtest into heightfield (object) space
|
|
//
|
|
Matrix3D world_to_obj_tm;
|
|
Get_Inverse_Transform (world_to_obj_tm);
|
|
boxtest.Transform (world_to_obj_tm);
|
|
|
|
//
|
|
// Calculate what grid cells this box is "possibly" intersecting
|
|
//
|
|
int min_x = Get_Quad_Index_X (boxtest.SweepMin.X);
|
|
int min_y = Get_Quad_Index_Y (boxtest.SweepMin.Y);
|
|
int max_x = Get_Quad_Index_X (boxtest.SweepMax.X);
|
|
int max_y = Get_Quad_Index_Y (boxtest.SweepMax.Y);
|
|
|
|
int closest_index = 0;
|
|
|
|
//
|
|
// Now check all the quads in this region
|
|
//
|
|
bool retval = false;
|
|
for (int y_pos = min_y; y_pos <= max_y; y_pos ++) {
|
|
for (int x_pos = min_x; x_pos <= max_x; x_pos ++) {
|
|
|
|
int start_index = Grid_Index (x_pos, y_pos);
|
|
int quad_index = y_pos * (GridPointsX - 1) + x_pos;
|
|
|
|
//
|
|
// Skip this quad if its hidden
|
|
//
|
|
if (QuadFlags[quad_index] & QF_HIDDEN) {
|
|
continue;
|
|
}
|
|
|
|
int v0_index = start_index;
|
|
int v1_index = start_index + 1;
|
|
int v2_index = start_index + GridPointsX + 1;
|
|
int v3_index = start_index + GridPointsX;
|
|
|
|
//
|
|
// Compose the two triangles for the collision check
|
|
//
|
|
TriClass tri1;
|
|
TriClass tri2;
|
|
Vector3 norm1 (0, 0, 0);
|
|
Vector3 norm2 (0, 0, 0);
|
|
tri1.N = &norm1;
|
|
tri2.N = &norm2;
|
|
tri1.V[0] = &Grid[v0_index];
|
|
tri1.V[1] = &Grid[v2_index];
|
|
tri1.V[2] = &Grid[v3_index];
|
|
tri2.V[0] = &Grid[v2_index];
|
|
tri2.V[1] = &Grid[v0_index];
|
|
tri2.V[2] = &Grid[v1_index];
|
|
tri1.Compute_Normal ();
|
|
tri2.Compute_Normal ();
|
|
|
|
//
|
|
// Do the collision test to determine if the box intersects either of these triangles
|
|
//
|
|
float percent = boxtest.Result->Fraction;
|
|
retval |= CollisionMath::Collide (boxtest.Box, boxtest.Move, tri1, boxtest.Result);
|
|
retval |= CollisionMath::Collide (boxtest.Box, boxtest.Move, tri2, boxtest.Result);
|
|
|
|
//
|
|
// Is this the closest collision yet?
|
|
//
|
|
if (boxtest.Result->Fraction < percent) {
|
|
closest_index = v0_index;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make sure to return our pointer to the caller
|
|
//
|
|
if (retval) {
|
|
|
|
//
|
|
// Determine which surface type was hit
|
|
//
|
|
if (MaterialPassList.Count () > 0) {
|
|
int best_pass = 0;
|
|
float best_alpha = 0;
|
|
for (int index = 0; index < MaterialPassList.Count (); index ++) {
|
|
float alpha = MaterialPassList[index]->VertexAlpha[closest_index];
|
|
if (alpha > best_alpha && alpha != 1.0F) {
|
|
best_alpha = alpha;
|
|
best_pass = index;
|
|
}
|
|
}
|
|
if (MaterialPassList[best_pass]->Material != NULL) {
|
|
boxtest.Result->SurfaceType = MaterialPassList[best_pass]->Material->Get_Surface_Type ();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Transform the result back into world-space
|
|
//
|
|
boxtest.Transform (Get_Transform ());
|
|
boxtest.CollidedRenderObj = this;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Cast_Ray
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
bool
|
|
RenegadeTerrainPatchClass::Cast_Ray (RayCollisionTestClass &raytest)
|
|
{
|
|
//
|
|
// Skip this object if it doesn't match the collision type
|
|
//
|
|
if ( (Get_Collision_Type() & raytest.CollisionType) == 0 ||
|
|
raytest.Result->StartBad)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool retval = false;
|
|
|
|
|
|
#if 0
|
|
CastResultStruct temp_result = (*raytest.Result);
|
|
|
|
retval = Brute_Force_Cast_Ray (raytest);
|
|
|
|
static bool do_it = false;
|
|
if (do_it) {
|
|
|
|
do_it_start:
|
|
|
|
CastResultStruct brute_result = (*raytest.Result);
|
|
(*raytest.Result) = temp_result;
|
|
bool retval2 = Cast_Non_Vertical_Ray (raytest);
|
|
if (do_it && (retval != retval2 || brute_result.Fraction != raytest.Result->Fraction)) {
|
|
(*raytest.Result) = temp_result;
|
|
goto do_it_start;
|
|
}
|
|
}
|
|
#else
|
|
|
|
retval = Cast_Non_Vertical_Ray (raytest);
|
|
|
|
#endif
|
|
|
|
|
|
//
|
|
// Test for completely vertical ray
|
|
//
|
|
/*if ( raytest.Ray.Get_DP ().X == 0.0F &&
|
|
raytest.Ray.Get_DP ().Y == 0.0F)
|
|
{
|
|
retval = Cast_Vertical_Ray (raytest);
|
|
} else {
|
|
retval = Cast_Non_Vertical_Ray (raytest);
|
|
}*/
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Cast_Vertical_Ray
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
bool
|
|
RenegadeTerrainPatchClass::Cast_Vertical_Ray (RayCollisionTestClass &raytest)
|
|
{
|
|
//
|
|
// Sanity check
|
|
//
|
|
if (raytest.Ray.Get_DP ().Z == 0) {
|
|
return false;
|
|
}
|
|
|
|
bool retval = false;
|
|
|
|
//
|
|
// First, transform the ray into heightfield (object) space
|
|
//
|
|
Matrix3D world_to_obj_tm;
|
|
Get_Inverse_Transform (world_to_obj_tm);
|
|
|
|
const Vector3 &world_p0 = raytest.Ray.Get_P0 ();
|
|
const Vector3 &world_p1 = raytest.Ray.Get_P1 ();
|
|
|
|
Vector3 p0;
|
|
Vector3 p1;
|
|
Matrix3D::Transform_Vector (world_to_obj_tm, world_p0, &p0);
|
|
Matrix3D::Transform_Vector (world_to_obj_tm, world_p1, &p1);
|
|
|
|
//
|
|
// Build a line segement from the transformed points that we'll use later
|
|
// for collision detection.
|
|
//
|
|
LineSegClass line_seg (p0, p1);
|
|
|
|
//
|
|
// Is this point somewhere over or under the grid?
|
|
//
|
|
if ( p0.X >= BoundingBoxMin.X && p0.Y >= BoundingBoxMin.Y &&
|
|
p0.X <= BoundingBoxMax.X && p0.Y <= BoundingBoxMax.Y)
|
|
{
|
|
int quad_index_x = Get_Quad_Index_X (p0.X);
|
|
int quad_index_y = Get_Quad_Index_Y (p0.Y);
|
|
|
|
//
|
|
// Simply test this quad to see if the ray collides with it...
|
|
//
|
|
retval = Collide_Quad (line_seg, quad_index_x, quad_index_y, *raytest.Result);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Brute_Force_Cast_Ray
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
bool
|
|
RenegadeTerrainPatchClass::Brute_Force_Cast_Ray (RayCollisionTestClass &raytest)
|
|
{
|
|
bool retval = false;
|
|
|
|
//
|
|
// First, transform the ray into heightfield (object) space
|
|
//
|
|
Matrix3D world_to_obj_tm;
|
|
Get_Inverse_Transform (world_to_obj_tm);
|
|
|
|
const Vector3 &world_p0 = raytest.Ray.Get_P0 ();
|
|
const Vector3 &world_p1 = raytest.Ray.Get_P1 ();
|
|
|
|
Vector3 p0;
|
|
Vector3 p1;
|
|
Matrix3D::Transform_Vector (world_to_obj_tm, world_p0, &p0);
|
|
Matrix3D::Transform_Vector (world_to_obj_tm, world_p1, &p1);
|
|
|
|
//
|
|
// Build a line segement from the transformed points that we'll use later
|
|
// for collision detection.
|
|
//
|
|
LineSegClass line_seg (p0, p1);
|
|
|
|
//
|
|
// Now check all the quads in this region
|
|
//
|
|
for (int y_pos = 0; y_pos < (GridPointsY-1); y_pos ++) {
|
|
for (int x_pos = 0; x_pos < (GridPointsX-1); x_pos ++) {
|
|
|
|
int quad_index = y_pos * (GridPointsX - 1) + x_pos;
|
|
|
|
//
|
|
// Skip this quad if its hidden
|
|
//
|
|
if (QuadFlags[quad_index] & QF_HIDDEN) {
|
|
continue;
|
|
}
|
|
|
|
Collide_Quad (line_seg, x_pos, y_pos, *raytest.Result);
|
|
}
|
|
}
|
|
|
|
if (retval) {
|
|
raytest.CollidedRenderObj = this;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Cast_Non_Vertical_Ray
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
bool
|
|
RenegadeTerrainPatchClass::Cast_Non_Vertical_Ray (RayCollisionTestClass &raytest)
|
|
{
|
|
bool retval = false;
|
|
|
|
//
|
|
// First, transform the ray into heightfield (object) space
|
|
//
|
|
Matrix3D world_to_obj_tm;
|
|
Get_Inverse_Transform (world_to_obj_tm);
|
|
|
|
const Vector3 &world_p0 = raytest.Ray.Get_P0 ();
|
|
const Vector3 &world_p1 = raytest.Ray.Get_P1 ();
|
|
|
|
Vector3 p0;
|
|
Vector3 p1;
|
|
Matrix3D::Transform_Vector (world_to_obj_tm, world_p0, &p0);
|
|
Matrix3D::Transform_Vector (world_to_obj_tm, world_p1, &p1);
|
|
|
|
//
|
|
// Build a line segement from the transformed points that we'll use later
|
|
// for collision detection.
|
|
//
|
|
LineSegClass line_seg (p0, p1);
|
|
const Vector3 &delta_p = line_seg.Get_DP ();
|
|
|
|
//
|
|
// Calculate where on the grid we need to search
|
|
//
|
|
int start_cell_x = Get_Quad_Index_X (p0.X);
|
|
int start_cell_y = Get_Quad_Index_Y (p0.Y);
|
|
int end_cell_x = Get_Quad_Index_X (p1.X);
|
|
int end_cell_y = Get_Quad_Index_Y (p1.Y);
|
|
|
|
//
|
|
// Determine how many vertical grid lines to search
|
|
//
|
|
int x_inc = 1;
|
|
if (start_cell_x > end_cell_x) {
|
|
x_inc = -1;
|
|
}
|
|
|
|
//
|
|
// Determine how many horizontal grid lines to search
|
|
//
|
|
int y_inc = 1;
|
|
if (start_cell_y > end_cell_y) {
|
|
y_inc = -1;
|
|
}
|
|
|
|
int cells_x = ::abs (end_cell_x - start_cell_x);
|
|
int cells_y = ::abs (end_cell_y - start_cell_y);
|
|
|
|
//
|
|
// Determine if we need to offset the cell we're searching each
|
|
// time we stop at a vertical or horizontal grid intersection
|
|
//
|
|
int cell_x_offset = 0;
|
|
int cell_y_offset = 0;
|
|
if (x_inc > 0) {
|
|
cell_x_offset = -1;
|
|
}
|
|
if (y_inc > 0) {
|
|
cell_y_offset = -1;
|
|
}
|
|
|
|
|
|
CastResultStruct vertical_result = *(raytest.Result);
|
|
CastResultStruct horizontal_result = *(raytest.Result);
|
|
bool did_vertical_collide = false;
|
|
bool did_horizontal_collide = false;
|
|
|
|
//
|
|
// Now, determine what the actual starting and ending cell coordinates are...
|
|
//
|
|
Vector2i cell_coord_p0 (start_cell_x, start_cell_y);
|
|
Vector2i cell_coord_p1 (end_cell_x, end_cell_x);
|
|
|
|
if (delta_p.X != 0.0F) {
|
|
float clamped_p0_x = WWMath::Clamp (p0.X, BoundingBoxMin.X, BoundingBoxMax.X);
|
|
float clamped_p1_x = WWMath::Clamp (p1.X, BoundingBoxMin.X, BoundingBoxMax.X);
|
|
float clamped_p0_y = p0.Y + (delta_p.Y * ((clamped_p0_x - p0.X) / delta_p.X));
|
|
float clamped_p1_y = p0.Y + (delta_p.Y * ((clamped_p1_x - p0.X) / delta_p.X));
|
|
|
|
cell_coord_p0.Set (Get_Quad_Index_X (clamped_p0_x), Get_Quad_Index_Y (clamped_p0_y));
|
|
cell_coord_p1.Set (Get_Quad_Index_X (clamped_p1_x), Get_Quad_Index_Y (clamped_p1_y));
|
|
} else if (delta_p.Y != 0.0F) {
|
|
float clamped_p0_y = WWMath::Clamp (p0.Y, BoundingBoxMin.Y, BoundingBoxMax.Y);
|
|
float clamped_p1_y = WWMath::Clamp (p1.Y, BoundingBoxMin.Y, BoundingBoxMax.Y);
|
|
float clamped_p0_x = p0.X + (delta_p.X * ((clamped_p0_y - p0.Y) / delta_p.Y));
|
|
float clamped_p1_x = p0.X + (delta_p.X * ((clamped_p1_y - p0.Y) / delta_p.Y));
|
|
|
|
cell_coord_p0.Set (Get_Quad_Index_X (clamped_p0_x), Get_Quad_Index_Y (clamped_p0_y));
|
|
cell_coord_p1.Set (Get_Quad_Index_X (clamped_p1_x), Get_Quad_Index_Y (clamped_p1_y));
|
|
}
|
|
|
|
//
|
|
// Test quad of triangles in the starting cell to see if you need go no further.
|
|
//
|
|
if (Collide_Quad (line_seg, cell_coord_p0.I, cell_coord_p0.J, vertical_result)) {
|
|
did_vertical_collide = true;
|
|
} else {
|
|
|
|
//
|
|
// Test vertical grid lines first
|
|
//
|
|
if (delta_p.X != 0.0F) {
|
|
|
|
//
|
|
// Find the first intersection point moving along vertical grid lines
|
|
//
|
|
for (int curr_x = start_cell_x; cells_x >= 0; cells_x --, curr_x += x_inc) {
|
|
|
|
//
|
|
// Determine where the ray intersects this grid line
|
|
//
|
|
float x_pos = Get_Grid_Line_Pos_X (curr_x);
|
|
float percent = (x_pos - p0.X) / delta_p.X;
|
|
|
|
//
|
|
// Don't test the cell if its outside the range of the line segment
|
|
//
|
|
if (percent >= 0 && percent <= 1.0F) {
|
|
float y_pos = p0.Y + (delta_p.Y * percent);
|
|
|
|
//
|
|
// Now determine what grid cell this is
|
|
//
|
|
int cell_x = curr_x + cell_x_offset;
|
|
int cell_y = Get_Quad_Index_Y (y_pos, false);
|
|
if (Is_Valid_Quad (cell_x, cell_y)) {
|
|
|
|
//
|
|
// Test this quad to see if either of its triangles intersect
|
|
//
|
|
if (Collide_Quad (line_seg, cell_x, cell_y, vertical_result)) {
|
|
did_vertical_collide = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Test horizontal grid lines next
|
|
//
|
|
if (delta_p.Y != 0.0F) {
|
|
|
|
//
|
|
// Find the first intersection point moving along horizontal grid lines
|
|
//
|
|
for (int curr_y = start_cell_y; cells_y >= 0; cells_y --, curr_y += y_inc) {
|
|
|
|
//
|
|
// Determine where the ray intersects this grid line
|
|
//
|
|
float y_pos = Get_Grid_Line_Pos_Y (curr_y);
|
|
float percent = (y_pos - p0.Y) / delta_p.Y;
|
|
|
|
//
|
|
// Don't test the cell if its outside the range of the line segment
|
|
//
|
|
if (percent >= 0 && percent <= 1.0F) {
|
|
float x_pos = p0.X + (delta_p.X * percent);
|
|
|
|
//
|
|
// Now determine what grid cell this is
|
|
//
|
|
int cell_y = curr_y + cell_y_offset;
|
|
int cell_x = Get_Quad_Index_X (x_pos, false);
|
|
if (Is_Valid_Quad (cell_x, cell_y)) {
|
|
|
|
//
|
|
// Test this quad to see if either of its triangles intersect
|
|
//
|
|
if (Collide_Quad (line_seg, cell_x, cell_y, horizontal_result)) {
|
|
did_horizontal_collide = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make sure to test the ending cell
|
|
//
|
|
if (did_vertical_collide == false && did_horizontal_collide == false) {
|
|
if (Collide_Quad (line_seg, cell_coord_p1.I, cell_coord_p1.J, vertical_result)) {
|
|
did_vertical_collide = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Did either collide?
|
|
//
|
|
if (did_vertical_collide || did_horizontal_collide) {
|
|
retval = true;
|
|
|
|
//
|
|
// Determine which result was hit first
|
|
//
|
|
if (did_vertical_collide && did_horizontal_collide) {
|
|
|
|
//
|
|
// Both triangles intersected, so find the one that hit first.
|
|
//
|
|
if (vertical_result.Fraction < horizontal_result.Fraction) {
|
|
*(raytest.Result) = vertical_result;
|
|
} else {
|
|
*(raytest.Result) = horizontal_result;
|
|
}
|
|
|
|
} else if (did_vertical_collide) {
|
|
*(raytest.Result) = vertical_result;
|
|
} else {
|
|
*(raytest.Result) = horizontal_result;
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Get_Factory
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
const PersistFactoryClass &
|
|
RenegadeTerrainPatchClass::Get_Factory (void) const
|
|
{
|
|
return _TerrainPatchFactory;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Save
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
bool
|
|
RenegadeTerrainPatchClass::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_BBOX_MIN, BoundingBoxMin);
|
|
WRITE_MICRO_CHUNK (csave, VARID_GRID_BBOX_MAX, BoundingBoxMax);
|
|
WRITE_MICRO_CHUNK (csave, VARID_GRID_DENSITY, Density);
|
|
WRITE_MICRO_CHUNK (csave, VARID_IS_PRELIT, IsPreLit);
|
|
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].X, sizeof (float) * 3);
|
|
}
|
|
|
|
csave.End_Chunk ();
|
|
|
|
//
|
|
// Now, write out the "array" of normals
|
|
//
|
|
csave.Begin_Chunk (CHUNKID_NORMALS);
|
|
|
|
for (index = 0; index < GridPointCount; index ++) {
|
|
csave.Write (&GridNormals[index].X, sizeof (float) * 3);
|
|
}
|
|
|
|
csave.End_Chunk ();
|
|
|
|
//
|
|
// Now, write out the "array" of vertex colors
|
|
//
|
|
csave.Begin_Chunk (CHUNKID_VERTEX_COLORS);
|
|
|
|
for (index = 0; index < GridPointCount; index ++) {
|
|
csave.Write (&VertexColors[index].X, sizeof (float) * 3);
|
|
}
|
|
|
|
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, save each material layer
|
|
//
|
|
csave.Begin_Chunk (CHUNKID_MATERIAL_LAYERS);
|
|
|
|
for (index = 0; index < MaterialPassList.Count (); index ++) {
|
|
|
|
//
|
|
// Don't save the material information if there' no material configured...
|
|
//
|
|
if (MaterialPassList[index]->Material != NULL) {
|
|
|
|
//
|
|
// Save this material layer to its own chunk
|
|
//
|
|
csave.Begin_Chunk (CHUNKID_MATERIAL_LAYER);
|
|
MaterialPassList[index]->Save (csave);
|
|
csave.End_Chunk ();
|
|
}
|
|
}
|
|
|
|
csave.End_Chunk ();
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Load
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
bool
|
|
RenegadeTerrainPatchClass::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].X, sizeof (float) * 3);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case CHUNKID_NORMALS:
|
|
{
|
|
//
|
|
// Read the array of normals
|
|
//
|
|
for (int index = 0; index < GridPointCount; index ++) {
|
|
cload.Read (&GridNormals[index].X, sizeof (float) * 3);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case CHUNKID_VERTEX_COLORS:
|
|
{
|
|
//
|
|
// Read the array of vertex colors
|
|
//
|
|
for (int index = 0; index < GridPointCount; index ++) {
|
|
cload.Read (&VertexColors[index].X, sizeof (float) * 3);
|
|
}
|
|
|
|
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_LAYERS:
|
|
Load_Materials (cload);
|
|
break;
|
|
}
|
|
|
|
cload.Close_Chunk ();
|
|
}
|
|
|
|
Invalidate_Cached_Bounding_Volumes();
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
//
|
|
// Load_Variables
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
void
|
|
RenegadeTerrainPatchClass::Load_Variables (ChunkLoadClass &cload)
|
|
{
|
|
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_BBOX_MIN, BoundingBoxMin);
|
|
READ_MICRO_CHUNK (cload, VARID_GRID_BBOX_MAX, BoundingBoxMax);
|
|
READ_MICRO_CHUNK (cload, VARID_GRID_DENSITY, Density);
|
|
READ_MICRO_CHUNK (cload, VARID_IS_PRELIT, IsPreLit);
|
|
}
|
|
|
|
cload.Close_Micro_Chunk ();
|
|
}
|
|
|
|
Set_Has_User_Lighting(IsPreLit);
|
|
|
|
//
|
|
// Allocate and initialize the grid and supporting data structures
|
|
//
|
|
Allocate_Grid ();
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
//
|
|
// Load_Materials
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
void
|
|
RenegadeTerrainPatchClass::Load_Materials (ChunkLoadClass &cload)
|
|
{
|
|
while (cload.Open_Chunk ()) {
|
|
switch (cload.Cur_Chunk_ID ()) {
|
|
|
|
case CHUNKID_MATERIAL_LAYER:
|
|
{
|
|
//
|
|
// Create and load the material
|
|
//
|
|
RenegadeTerrainMaterialPassClass *material = new RenegadeTerrainMaterialPassClass;
|
|
material->Load (cload);
|
|
|
|
//
|
|
// Add the material to our list
|
|
//
|
|
MaterialPassList.Add (material);
|
|
break;
|
|
}
|
|
}
|
|
|
|
cload.Close_Chunk ();
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
//
|
|
// Add_Material
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
int
|
|
RenegadeTerrainPatchClass::Add_Material (TerrainMaterialClass *material)
|
|
{
|
|
//
|
|
// Add a reference to the material
|
|
//
|
|
if (material != NULL) {
|
|
material->Add_Ref ();
|
|
}
|
|
|
|
//
|
|
// Allocate a new pass for this material
|
|
//
|
|
RenegadeTerrainMaterialPassClass *material_pass = new RenegadeTerrainMaterialPassClass;
|
|
material_pass->Allocate (GridPointCount);
|
|
material_pass->Material = material;
|
|
|
|
//
|
|
// Add this material
|
|
//
|
|
int retval = MaterialPassList.Count ();
|
|
MaterialPassList.Add (material_pass);
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
//
|
|
// Reset_Material_Passes
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
void
|
|
RenegadeTerrainPatchClass::Reset_Material_Passes (void)
|
|
{
|
|
//
|
|
// Reset each of the materials
|
|
//
|
|
for (int index = 0; index < MaterialPassList.Count (); index ++) {
|
|
MaterialPassList[index]->Reset ();
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
//
|
|
// Update_UVs
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
void
|
|
RenegadeTerrainPatchClass::Update_UVs (void)
|
|
{
|
|
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
|
|
//
|
|
for (int index = 0; index < MaterialPassList.Count (); index ++) {
|
|
if (MaterialPassList[index]->Material != NULL) {
|
|
float meters_per_texture = MaterialPassList[index]->Material->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;
|
|
|
|
//
|
|
// Handle mirrored UVs
|
|
//
|
|
if (MaterialPassList[index]->Material->Are_UVs_Mirrored ()) {
|
|
int u_int_value = WWMath::Float_To_Int_Floor (u_value);
|
|
int v_int_value = WWMath::Float_To_Int_Floor (v_value);
|
|
|
|
u_value = u_value - u_int_value;
|
|
v_value = v_value - v_int_value;
|
|
|
|
if (u_int_value & 1) {
|
|
u_value = 1.0F - u_value;
|
|
}
|
|
|
|
if (v_int_value & 1) {
|
|
v_value = 1.0F - v_value;
|
|
}
|
|
}
|
|
|
|
MaterialPassList[index]->GridUVs[start_index + x_pos].Set (u_value, v_value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
AreBuffersDirty = true;
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
//
|
|
// Update_Vertex_Render_Lists
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
void
|
|
RenegadeTerrainPatchClass::Update_Vertex_Render_Lists (void)
|
|
{
|
|
int col_count = GridPointsX - 1;
|
|
|
|
//
|
|
// Loop over each material layer
|
|
//
|
|
for (int index = 0; index < MaterialPassList.Count (); index ++) {
|
|
|
|
//
|
|
// Loop over each pass for this layer
|
|
//
|
|
RenegadeTerrainMaterialPassClass *material_pass = MaterialPassList[index];
|
|
for (int pass = 0; pass < RenegadeTerrainMaterialPassClass::PASS_COUNT; pass ++) {
|
|
|
|
//
|
|
// Loop over each quad that is rendered in this material pass
|
|
//
|
|
for (int quad_index = 0; quad_index < material_pass->QuadList[pass].Count (); quad_index ++) {
|
|
|
|
//
|
|
// Determine the coordinate for this quad
|
|
//
|
|
int real_quad_index = material_pass->QuadList[pass][quad_index];
|
|
int quad_y = (real_quad_index / col_count);
|
|
int quad_x = real_quad_index - (quad_y * col_count);
|
|
|
|
//
|
|
// Get the vertex indices that define the four corners of this quad
|
|
//
|
|
int curr_src_index = Grid_Index (quad_x, quad_y);
|
|
int v0_index = curr_src_index;
|
|
int v1_index = curr_src_index + 1;
|
|
int v2_index = curr_src_index + GridPointsX + 1;
|
|
int v3_index = curr_src_index + GridPointsX;
|
|
|
|
//
|
|
// Add the verts from this quad to the render list
|
|
//
|
|
if (material_pass->VertexIndexMap[pass][v0_index] == -1) {
|
|
material_pass->VertexIndexMap[pass][v0_index] = material_pass->VertexRenderList[pass].Count ();
|
|
material_pass->VertexRenderList[pass].Add (v0_index);
|
|
}
|
|
|
|
if (material_pass->VertexIndexMap[pass][v1_index] == -1) {
|
|
material_pass->VertexIndexMap[pass][v1_index] = material_pass->VertexRenderList[pass].Count ();
|
|
material_pass->VertexRenderList[pass].Add (v1_index);
|
|
}
|
|
|
|
if (material_pass->VertexIndexMap[pass][v2_index] == -1) {
|
|
material_pass->VertexIndexMap[pass][v2_index] = material_pass->VertexRenderList[pass].Count ();
|
|
material_pass->VertexRenderList[pass].Add (v2_index);
|
|
}
|
|
|
|
if (material_pass->VertexIndexMap[pass][v3_index] == -1) {
|
|
material_pass->VertexIndexMap[pass][v3_index] = material_pass->VertexRenderList[pass].Count ();
|
|
material_pass->VertexRenderList[pass].Add (v3_index);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
AreBuffersDirty = true;
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
//
|
|
// Peek_Material_Pass
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
RenegadeTerrainMaterialPassClass *
|
|
RenegadeTerrainPatchClass::Get_Material_Pass (int index, TerrainMaterialClass *material)
|
|
{
|
|
//
|
|
// Grow the list as necessary
|
|
//
|
|
while (index >= MaterialPassList.Count ()) {
|
|
Add_Material (NULL);
|
|
}
|
|
|
|
//
|
|
// Ensure the material is correct at this layer
|
|
//
|
|
if (MaterialPassList[index]->Material != material) {
|
|
REF_PTR_SET (MaterialPassList[index]->Material, material);
|
|
}
|
|
|
|
return MaterialPassList[index];
|
|
}
|