This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/ww3d2/meshmdl.cpp

855 lines
26 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 : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/meshmdl.cpp $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 3/14/02 1:31p $*
* *
* $Revision:: 48 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "meshmdl.h"
#include "matinfo.h"
#include "aabtree.h"
#include "htree.h"
#include "vp.h"
#include "visrasterizer.h"
#include "dx8polygonrenderer.h"
#include "bwrender.h"
#include "camera.h"
#include "dx8renderer.h"
#include "hashtemplate.h"
/*
** Temporary Buffers
** These buffers are used by the skin code for temporary storage of the deformed vertices and
** vertex normals.
*/
static DynamicVectorClass<Vector3> _TempVertexBuffer;
static DynamicVectorClass<Vector3> _TempNormalBuffer;
static DynamicVectorClass<Vector4> _TempTransformedVertexBuffer;
static DynamicVectorClass<unsigned long> _TempClipFlagBuffer;
/*
**
** MeshModelClass Implementation
**
*/
MeshModelClass::MeshModelClass(void) :
DefMatDesc(NULL),
AlternateMatDesc(NULL),
CurMatDesc(NULL),
MatInfo(NULL),
GapFiller(NULL)
{
Set_Flag(DIRTY_BOUNDS,true);
DefMatDesc = new MeshMatDescClass;
CurMatDesc = DefMatDesc;
MatInfo = NEW_REF( MaterialInfoClass, () );
return ;
}
MeshModelClass::MeshModelClass(const MeshModelClass & that) :
MeshGeometryClass(that),
DefMatDesc(NULL),
AlternateMatDesc(NULL),
CurMatDesc(NULL),
MatInfo(NULL),
GapFiller(NULL),
HasBeenInUse(false)
{
DefMatDesc = new MeshMatDescClass(*(that.DefMatDesc));
if (that.AlternateMatDesc != NULL) {
AlternateMatDesc = new MeshMatDescClass(*(that.AlternateMatDesc));
}
CurMatDesc = DefMatDesc;
clone_materials(that);
return ;
}
MeshModelClass::~MeshModelClass(void)
{
// WWDEBUG_SAY(("Note: Mesh %s was never used\n",Get_Name()));
Reset(0,0,0);
REF_PTR_RELEASE(MatInfo);
if (DefMatDesc != NULL) {
delete DefMatDesc;
}
if (AlternateMatDesc != NULL) {
delete AlternateMatDesc;
}
return ;
}
MeshModelClass & MeshModelClass::operator = (const MeshModelClass & that)
{
if (this != &that) {
MeshGeometryClass::operator = (that);
*DefMatDesc = *(that.DefMatDesc);
CurMatDesc = DefMatDesc;
if (AlternateMatDesc != NULL) {
delete AlternateMatDesc;
AlternateMatDesc = NULL;
}
if (that.AlternateMatDesc != NULL) {
AlternateMatDesc = new MeshMatDescClass(*(that.AlternateMatDesc));
}
clone_materials(that);
if (GapFiller) {
delete[] GapFiller;
GapFiller=NULL;
}
if (that.GapFiller) GapFiller=new GapFillerClass(*that.GapFiller);
}
return * this;
}
void MeshModelClass::Reset(int polycount,int vertcount,int passcount)
{
Reset_Geometry(polycount,vertcount);
// Release everything we have and reset to initial state
MatInfo->Reset();
DefMatDesc->Reset(polycount,vertcount,passcount);
if (AlternateMatDesc != NULL) {
delete AlternateMatDesc;
AlternateMatDesc = NULL;
}
CurMatDesc = DefMatDesc;
delete GapFiller;
GapFiller=NULL;
return ;
}
void MeshModelClass::Register_For_Rendering()
{
HasBeenInUse=true;
//WW3D::Set_NPatches_Level(1);
if (WW3D::Get_NPatches_Level()>1) {
if (WW3D::Get_NPatches_Gap_Filling_Mode()!=WW3D::NPATCHES_GAP_FILLING_DISABLED) {
Init_For_NPatch_Rendering();
}
else if (GapFiller) {
delete GapFiller;
GapFiller=NULL;
}
}
else {
if (WW3D::Get_NPatches_Gap_Filling_Mode()==WW3D::NPATCHES_GAP_FILLING_FORCE) {
Init_For_NPatch_Rendering();
}
else if (GapFiller) {
delete GapFiller;
GapFiller=NULL;
}
}
}
void MeshModelClass::Replace_Texture(TextureClass* texture,TextureClass* new_texture)
{
#pragma message("gth - TEMPORARILY REMOVING Replace_Texture")
#if 0
WWASSERT(texture);
WWASSERT(new_texture);
for (int stage=0;stage<MeshMatDescClass::MAX_TEX_STAGES;++stage) {
for (int pass=0;pass<Get_Pass_Count();++pass) {
if (Has_Texture_Array(pass,stage)) {
for (int i=0;i<Get_Polygon_Count();++i) {
if (Peek_Texture(i,pass,stage)==texture) {
Set_Texture(i,new_texture,pass,stage);
}
}
}
else {
if (Peek_Single_Texture(pass,stage)==texture) {
Set_Single_Texture(new_texture,pass,stage);
}
}
// If this mesh model has been initialized for rendering we need to tell the rendering
// system to change texturing as well.
DX8FVFCategoryContainer* fvf_category=Peek_FVF_Category_Container();
if (fvf_category) {
fvf_category->Change_Polygon_Renderer_Texture(PolygonRendererList,texture,new_texture,pass,stage);
}
}
}
#endif
}
void MeshModelClass::Replace_VertexMaterial(VertexMaterialClass* vmat,VertexMaterialClass* new_vmat)
{
#pragma message("gth - TEMPORARILY REMOVING Replace_Texture")
#if 0
WWASSERT(vmat);
WWASSERT(new_vmat);
for (int pass=0;pass<Get_Pass_Count();++pass) {
if (Has_Material_Array(pass)) {
for (int i=0;i<Get_Vertex_Count();++i) {
if (Peek_Material(i,pass)==vmat) {
Set_Material(i,new_vmat,pass);
}
}
}
else {
if (Peek_Single_Material(pass)==vmat) {
Set_Single_Material(new_vmat,pass);
}
}
// If this mesh model has been initialized for rendering we need to tell the rendering
// system to change texturing as well.
DX8FVFCategoryContainer* fvf_category=Peek_FVF_Category_Container();
if (fvf_category) {
fvf_category->Change_Polygon_Renderer_Material(PolygonRendererList,vmat,new_vmat,pass);
}
}
#endif
}
void MeshModelClass::Shadow_Render(SpecialRenderInfoClass & rinfo,const Matrix3D & tm,const HTreeClass * htree)
{
if (rinfo.BWRenderer != NULL) {
if (_TempTransformedVertexBuffer.Length() < VertexCount) _TempTransformedVertexBuffer.Resize(VertexCount);
Vector4* transf_ptr=&(_TempTransformedVertexBuffer[0]);
get_deformed_screenspace_vertices(transf_ptr,rinfo,tm,htree);
Vector2* tptr = reinterpret_cast<Vector2 *>(transf_ptr);
Vector4* optr = transf_ptr;
for (int a=0;a<VertexCount;++a,++optr) *tptr++=Vector2((*optr)[0],-(*optr)[1]);
rinfo.BWRenderer->Set_Vertex_Locations(reinterpret_cast<Vector2*>(transf_ptr),VertexCount);
rinfo.BWRenderer->Render_Triangles(reinterpret_cast<const unsigned long*>(Poly->Get_Array()),PolyCount*3);
return;
}
}
// Destination pointers MUST point to arrays large enough to hold all vertices
void MeshModelClass::get_deformed_vertices(Vector3 *dst_vert,const HTreeClass * htree)
{
Vector3 * src_vert = Vertex->Get_Array();
uint16 * bonelink = VertexBoneLink->Get_Array();
for (int vi = 0; vi < Get_Vertex_Count(); vi++) {
const Matrix3D & tm = htree->Get_Transform(bonelink[vi]);
Matrix3D::Transform_Vector(tm, src_vert[vi], &(dst_vert[vi]));
}
}
// Destination pointers MUST point to arrays large enough to hold all vertices
void MeshModelClass::get_deformed_vertices(Vector3 *dst_vert, Vector3 *dst_norm,const HTreeClass * htree)
{
int vi;
int vertex_count=Get_Vertex_Count();
Vector3 * src_vert = Vertex->Get_Array();
#if (OPTIMIZE_VNORMS)
Vector3 * src_norm = (Vector3 *)Get_Vertex_Normal_Array();
#else
Vector3 * src_norm = VertexNorm->Get_Array();
#endif
uint16 * bonelink = VertexBoneLink->Get_Array();
for (vi = 0; vi < vertex_count;) {
const Matrix3D & tm = htree->Get_Transform(bonelink[vi]);
// Make a copy so we can set the translation to zero
Matrix3D mytm=tm;
int idx=bonelink[vi];
int cnt;
for (cnt = vi; cnt < vertex_count; cnt++) {
if (idx!=bonelink[cnt]) {
break;
}
}
VectorProcessorClass::Transform(dst_vert+vi,src_vert+vi,mytm,cnt-vi);
mytm.Set_Translation(Vector3(0.0f,0.0f,0.0f));
VectorProcessorClass::Transform(dst_norm+vi,src_norm+vi,mytm,cnt-vi);
vi=cnt;
}
}
// Destination pointer MUST point to arrays large enough to hold all vertices
void MeshModelClass::compose_deformed_vertex_buffer(
VertexFormatXYZNDUV2* verts,
const Vector2* uv0,
const Vector2* uv1,
const unsigned* diffuse,
const HTreeClass * htree)
{
int vi;
int vertex_count=Get_Vertex_Count();
Vector3 * src_vert = Vertex->Get_Array();
#if (OPTIMIZE_VNORMS)
Vector3 * src_norm = (Vector3 *)Get_Vertex_Normal_Array();
#else
Vector3 * src_norm = VertexNorm->Get_Array();
#endif
uint16 * bonelink = VertexBoneLink->Get_Array();
for (vi = 0; vi < vertex_count;) {
const Matrix3D & tm = htree->Get_Transform(bonelink[vi]);
// Make a copy so we can set the translation to zero
Matrix3D mytm=tm;
int idx=bonelink[vi];
int cnt;
for (cnt = vi; cnt < vertex_count; cnt++) {
if (idx!=bonelink[cnt]) {
break;
}
}
for (int pidx=0;pidx<cnt-vi;++pidx) {
const Matrix3D& A=mytm;
VertexFormatXYZNDUV2* out=verts+vi+pidx;
const Vector3& v=*(src_vert+vi+pidx);
out->x = (A[0][0] * v.X + A[0][1] * v.Y + A[0][2] * v.Z + A[0][3]);
out->y = (A[1][0] * v.X + A[1][1] * v.Y + A[1][2] * v.Z + A[1][3]);
out->z = (A[2][0] * v.X + A[2][1] * v.Y + A[2][2] * v.Z + A[2][3]);
const Vector3& n=*(src_norm+vi+pidx);
out->nx = (A[0][0] * n.X + A[0][1] * n.Y + A[0][2] * n.Z);
out->ny = (A[1][0] * n.X + A[1][1] * n.Y + A[1][2] * n.Z);
out->nz = (A[2][0] * n.X + A[2][1] * n.Y + A[2][2] * n.Z);
if (diffuse) out->diffuse=diffuse[vi+pidx];
else out->diffuse=0;
if (uv0) reinterpret_cast<Vector2&>(verts[vi+pidx].u1)=uv0[vi+pidx];
else reinterpret_cast<Vector2&>(verts[vi+pidx].u2)=Vector2(0.0f,0.0f);
}
vi=cnt;
}
}
// Destination pointers MUST point to arrays large enough to hold all vertices
void MeshModelClass::get_deformed_screenspace_vertices(Vector4 *dst_vert,const RenderInfoClass & rinfo,const Matrix3D & mesh_transform,const HTreeClass * htree)
{
Matrix4 prj = rinfo.Camera.Get_Projection_Matrix() * rinfo.Camera.Get_View_Matrix() * mesh_transform;
Vector3 * src_vert = Vertex->Get_Array();
int vertex_count=Get_Vertex_Count();
if (Get_Flag(SKIN) && VertexBoneLink && htree) {
uint16 * bonelink = VertexBoneLink->Get_Array();
for (int vi = 0; vi < vertex_count;) {
int idx=bonelink[vi];
Matrix4 tm = prj * htree->Get_Transform(idx);
// Count equal matrices (the vertices should be pre-sorted by matrices they use)
for (int cnt = vi; cnt < vertex_count; cnt++) if (idx!=bonelink[cnt]) break;
// Transform to screenspace (x,y,z,w)
VectorProcessorClass::Transform(
dst_vert+vi,
src_vert+vi,
tm,
cnt-vi);
vi=cnt;
}
} else {
VectorProcessorClass::Transform(
dst_vert,
src_vert,
prj,
vertex_count);
}
}
void MeshModelClass::Make_Geometry_Unique()
{
WWASSERT(Vertex);
ShareBufferClass<Vector3> * unique_verts = NEW_REF(ShareBufferClass<Vector3>,(*Vertex));
REF_PTR_SET(Vertex,unique_verts);
REF_PTR_RELEASE(unique_verts);
ShareBufferClass<Vector3> * norms = NEW_REF(ShareBufferClass<Vector3>,(*VertexNorm));
REF_PTR_SET(VertexNorm,norms);
REF_PTR_RELEASE(norms);
#if (!OPTIMIZE_PLANEEQ_RAM)
ShareBufferClass<Vector4> * peq = NEW_REF(ShareBufferClass<Vector4>,(*PlaneEq));
REF_PTR_SET(PlaneEq,peq);
REF_PTR_RELEASE(peq);
#endif
}
void MeshModelClass::Make_UV_Array_Unique(int pass,int stage)
{
CurMatDesc->Make_UV_Array_Unique(pass,stage);
}
void MeshModelClass::Make_Color_Array_Unique(int array_index)
{
CurMatDesc->Make_Color_Array_Unique(array_index);
}
void MeshModelClass::Enable_Alternate_Material_Description(bool onoff)
{
if ((onoff == true) && (AlternateMatDesc != NULL)) {
if (CurMatDesc != AlternateMatDesc) {
CurMatDesc = AlternateMatDesc;
if (Get_Flag(SORT) && WW3D::Is_Munge_Sort_On_Load_Enabled())
compute_static_sort_levels();
// TODO: Invalidate just this meshes DX8 data!!!
TheDX8MeshRenderer.Invalidate();
}
} else {
if (CurMatDesc != DefMatDesc) {
CurMatDesc = DefMatDesc;
if (Get_Flag(SORT) && WW3D::Is_Munge_Sort_On_Load_Enabled())
compute_static_sort_levels();
// TODO: Invalidate this meshes DX8 data!!!
TheDX8MeshRenderer.Invalidate();
}
}
}
bool MeshModelClass::Is_Alternate_Material_Description_Enabled(void)
{
return CurMatDesc == AlternateMatDesc;
}
/*
void MeshModelClass::Process_Texture_Reduction(void)
{
MatInfo->Process_Texture_Reduction();
}
*/
bool MeshModelClass::Needs_Vertex_Normals(void)
{
if (Get_Flag(MeshModelClass::PRELIT_MASK) == 0) {
return true;
}
return CurMatDesc->Do_Mappers_Need_Normals();
}
struct TriangleSide
{
Vector3 loc1;
Vector3 loc2;
TriangleSide(const Vector3& l1,const Vector3& l2)
{
int i1=*(int*)&l1[0];
i1=37*i1+*(int*)&l1[1];
i1=37*i1+*(int*)&l1[2];
int i2=*(int*)&l2[0];
i2=37*i2+*(int*)&l2[1];
i2=37*i2+*(int*)&l2[2];
if (i1<i2) {
loc1=l1;
loc2=l2;
}
else {
loc2=l1;
loc1=l2;
}
}
TriangleSide() {}
bool operator== (const TriangleSide& s)
{
unsigned i=*(unsigned*)&loc1[0]^*(unsigned*)&s.loc1[0];
i|=*(unsigned*)&loc1[1]^*(unsigned*)&s.loc1[1];
i|=*(unsigned*)&loc1[2]^*(unsigned*)&s.loc1[2];
i|=*(unsigned*)&loc2[0]^*(unsigned*)&s.loc2[0];
i|=*(unsigned*)&loc2[1]^*(unsigned*)&s.loc2[1];
i|=*(unsigned*)&loc2[2]^*(unsigned*)&s.loc2[2];
return !i;
}
};
// Get_Hash_Value specialization for Vector3.
template <> inline unsigned int HashTemplateKeyClass<Vector3>::Get_Hash_Value(const Vector3& location)
{
const unsigned char* buffer=(const unsigned char*)&location;
unsigned int hval=0;
for (unsigned int a=0;a<sizeof(Vector3);++a) {
hval+=37*hval+buffer[a];
}
return hval;
}
// Get_Hash_Value specialization for TriangleSide.
template <> inline unsigned int HashTemplateKeyClass<TriangleSide>::Get_Hash_Value(const TriangleSide& side)
{
const unsigned char* buffer=(const unsigned char*)&side;
unsigned int hval=0;
for (unsigned int a=0;a<sizeof(TriangleSide);++a) {
hval+=37*hval+buffer[a];
}
return hval;
}
struct SideIndexInfo
{
unsigned short vidx1;
unsigned short vidx2;
unsigned polygon_index;
SideIndexInfo() {}
SideIndexInfo(int i) { WWASSERT(0); }
};
HashTemplateClass<Vector3, unsigned> LocationHash;
HashTemplateClass<Vector3, unsigned> DuplicateLocationHash;
HashTemplateClass<TriangleSide,SideIndexInfo> SideHash;
// ----------------------------------------------------------------------------
//
// Allocate a gap-filler object. The constructor allocates memory for the
// maximum possible amount of gap polygons, which is quite much. After all
// the gap polygons have been added to the arrays, Shrink_Arrays() should
// be called to free up all unneeded memory.
//
// ----------------------------------------------------------------------------
GapFillerClass::GapFillerClass(MeshModelClass* mmc_) : mmc(NULL), PolygonCount(0)
{
REF_PTR_SET(mmc,mmc_);
ArraySize=mmc->Get_Polygon_Count()*6; // Each side of each triangle can have 2 polygons added, in the worst case
PolygonArray=new TriIndex[ArraySize];
for (int pass=0;pass<mmc->Get_Pass_Count();++pass) {
for (int stage=0;stage<MeshMatDescClass::MAX_TEX_STAGES;++stage) {
if (mmc->Has_Texture_Array(pass,stage)) {
TextureArray[pass][stage]=new TextureClass*[ArraySize];
}
else TextureArray[pass][stage]=NULL;
}
if (mmc->Has_Material_Array(pass)) {
MaterialArray[pass]=new VertexMaterialClass*[ArraySize];
}
else MaterialArray[pass]=NULL;
if (mmc->Has_Shader_Array(pass)) {
ShaderArray[pass]=new ShaderClass[ArraySize];
}
else ShaderArray[pass]=NULL;
}
}
GapFillerClass::GapFillerClass(const GapFillerClass& that) : mmc(NULL), PolygonCount(that.PolygonCount)
{
REF_PTR_SET(mmc,that.mmc);
ArraySize=that.ArraySize;
PolygonArray=new TriIndex[ArraySize];
for (int pass=0;pass<mmc->Get_Pass_Count();++pass) {
for (int stage=0;stage<MeshMatDescClass::MAX_TEX_STAGES;++stage) {
if (that.TextureArray[pass][stage]) {
TextureArray[pass][stage]=new TextureClass*[ArraySize];
for (unsigned i=0;i<PolygonCount;++i) {
TextureArray[pass][stage][i]=that.TextureArray[pass][stage][i];
TextureArray[pass][stage][i]->Add_Ref();
}
}
else TextureArray[pass][stage]=NULL;
}
if (that.MaterialArray[pass]) {
MaterialArray[pass]=new VertexMaterialClass*[ArraySize];
for (unsigned i=0;i<PolygonCount;++i) {
MaterialArray[pass][i]=that.MaterialArray[pass][i];
MaterialArray[pass][i]->Add_Ref();
}
}
else MaterialArray[pass]=NULL;
if (that.ShaderArray[pass]) {
ShaderArray[pass]=new ShaderClass[ArraySize];
for (unsigned i=0;i<PolygonCount;++i) {
ShaderArray[pass][i]=that.ShaderArray[pass][i];
}
}
else ShaderArray[pass]=NULL;
}
}
// ----------------------------------------------------------------------------
//
// Destruct gap-filler object. Release references to all textures and release
// the arrays.
//
// ----------------------------------------------------------------------------
GapFillerClass::~GapFillerClass()
{
delete[] PolygonArray;
for (int pass=0;pass<mmc->Get_Pass_Count();++pass) {
for (int stage=0;stage<MeshMatDescClass::MAX_TEX_STAGES;++stage) {
if (TextureArray[pass][stage]) {
for (unsigned i=0;i<PolygonCount;++i) {
REF_PTR_RELEASE(TextureArray[pass][stage][i]);
}
delete[] TextureArray[pass][stage];
}
}
if (MaterialArray[pass]) {
for (unsigned i=0;i<PolygonCount;++i) {
REF_PTR_RELEASE(MaterialArray[pass][i]);
}
delete[] MaterialArray[pass];
}
delete[] ShaderArray[pass];
}
REF_PTR_RELEASE(mmc);
}
// ----------------------------------------------------------------------------
//
// Add polygon to gap filler.
//
// ----------------------------------------------------------------------------
void GapFillerClass::Add_Polygon(unsigned polygon_index,unsigned vidx1,unsigned vidx2, unsigned vidx3)
{
WWASSERT(PolygonCount<ArraySize);
WWASSERT(vidx1!=vidx2 && vidx1!=vidx3 && vidx2!=vidx3);
Vector3 loc1=mmc->Get_Vertex_Array()[vidx1];
Vector3 loc2=mmc->Get_Vertex_Array()[vidx2];
Vector3 loc3=mmc->Get_Vertex_Array()[vidx3];
WWASSERT(loc1==loc2 || loc1==loc3 || loc2==loc3);
//sdflksdjflsdkf
//vidx1=mmc->Get_Polygon_Array()[polygon_index][0];
//vidx2=mmc->Get_Polygon_Array()[polygon_index][1];
//vidx3=mmc->Get_Polygon_Array()[polygon_index][2];
PolygonArray[PolygonCount]=TriIndex(vidx1,vidx2,vidx3);
for (int pass=0;pass<mmc->Get_Pass_Count();++pass) {
if (mmc->Has_Shader_Array(pass)) {
ShaderArray[pass][PolygonCount]=mmc->Get_Shader(polygon_index,pass);
}
if (mmc->Has_Material_Array(pass)) {
// MaterialArray[pass][PolygonCount]=mmc->Get_Material(polygon_index,pass);
MaterialArray[pass][PolygonCount]=mmc->Get_Material(mmc->Get_Polygon_Array()[polygon_index][0],pass);
}
for (int stage=0;stage<MeshMatDescClass::MAX_TEX_STAGES;++stage) {
if (mmc->Has_Texture_Array(pass,stage)) {
TextureArray[pass][stage][PolygonCount]=mmc->Get_Texture(polygon_index,pass,stage);
}
}
}
PolygonCount++;
}
// ----------------------------------------------------------------------------
//
// Resize buffers to match the polygon count exatly. After this call no more
// polygons can be added to the buffers.
//
// ----------------------------------------------------------------------------
void GapFillerClass::Shrink_Buffers()
{
if (PolygonCount==ArraySize) return;
// Shrink the polygon array
TriIndex* new_polygon_array=new TriIndex[PolygonCount];
memcpy(new_polygon_array,PolygonArray,PolygonCount*sizeof(TriIndex));
delete[] PolygonArray;
PolygonArray=new_polygon_array;
for (int pass=0;pass<mmc->Get_Pass_Count();++pass) {
for (int stage=0;stage<MeshMatDescClass::MAX_TEX_STAGES;++stage) {
if (TextureArray[pass][stage]) {
// Shrink the texture array
TextureClass** new_texture_array=new TextureClass*[PolygonCount];
memcpy(new_texture_array,TextureArray[pass][stage],PolygonCount*sizeof(TextureClass*));
delete[] TextureArray[pass][stage];
TextureArray[pass][stage]=new_texture_array;
}
}
if (MaterialArray[pass]) {
// Shrink the material array
VertexMaterialClass** new_material_array=new VertexMaterialClass*[PolygonCount];
memcpy(new_material_array,MaterialArray[pass],PolygonCount*sizeof(VertexMaterialClass*));
delete[] MaterialArray[pass];
MaterialArray[pass]=new_material_array;
}
if (ShaderArray[pass]) {
// Shrink the shader array
ShaderClass* new_shader_array=new ShaderClass[PolygonCount];
memcpy(new_shader_array,ShaderArray[pass],PolygonCount*sizeof(ShaderClass));
delete[] ShaderArray[pass];
ShaderArray[pass]=new_shader_array;
}
}
ArraySize=PolygonCount;
}
// ----------------------------------------------------------------------------
//
// Hard edges cause gaps to n-patches meshes. This code searches for hard edges
// and adds gaps filler polygons, using existing vertices.
//
// ----------------------------------------------------------------------------
void MeshModelClass::Init_For_NPatch_Rendering()
{
if (GapFiller) return;
const Vector3* locations=Get_Vertex_Array();
unsigned vertex_count=Get_Vertex_Count();
const TriIndex* polygon_indices=Get_Polygon_Array();
unsigned polygon_count=Get_Polygon_Count();
LocationHash.Remove_All();
DuplicateLocationHash.Remove_All();
SideHash.Remove_All();
for (unsigned i=0;i<vertex_count;++i) {
if (LocationHash.Exists(locations[i])) {
if (!DuplicateLocationHash.Exists(locations[i])) {
DuplicateLocationHash.Insert(locations[i],i);
}
}
else {
LocationHash.Insert(locations[i],i);
}
}
for (i=0;i<polygon_count;++i) {
bool duplicates[3];
duplicates[0]=DuplicateLocationHash.Exists(locations[polygon_indices[i][0]]);
duplicates[1]=DuplicateLocationHash.Exists(locations[polygon_indices[i][1]]);
duplicates[2]=DuplicateLocationHash.Exists(locations[polygon_indices[i][2]]);
if (duplicates[0] && duplicates[1]) {
TriangleSide tri(locations[polygon_indices[i][0]],locations[polygon_indices[i][1]]);
if (SideHash.Exists(tri)) {
SideIndexInfo side_index=SideHash.Get(tri);
unsigned idx1=side_index.vidx1;
unsigned idx2=side_index.vidx2;
unsigned idx3=polygon_indices[i][0];
unsigned idx4=polygon_indices[i][1];
bool diff=!(idx1^idx3)|!(idx1^idx4)|!(idx2^idx3)|!(idx2^idx4);
if (!diff) {
if (!GapFiller) GapFiller=new GapFillerClass(this);
GapFiller->Add_Polygon(i,idx4,idx2,idx1);
GapFiller->Add_Polygon(side_index.polygon_index,idx3,idx2,idx4);
}
}
else {
SideIndexInfo side_index;
side_index.vidx1=polygon_indices[i][0];
side_index.vidx2=polygon_indices[i][1];
side_index.polygon_index=i;
SideHash.Insert(tri,side_index);
}
}
if (duplicates[1] && duplicates[2]) {
TriangleSide tri(locations[polygon_indices[i][1]],locations[polygon_indices[i][2]]);
if (SideHash.Exists(tri)) {
SideIndexInfo side_index=SideHash.Get(tri);
unsigned idx1=side_index.vidx1;
unsigned idx2=side_index.vidx2;
unsigned idx3=polygon_indices[i][1];
unsigned idx4=polygon_indices[i][2];
bool diff=!(idx1^idx3)|!(idx1^idx4)|!(idx2^idx3)|!(idx2^idx4);
if (!diff) {
if (!GapFiller) GapFiller=new GapFillerClass(this);
GapFiller->Add_Polygon(i,idx4,idx2,idx1);
GapFiller->Add_Polygon(side_index.polygon_index,idx3,idx2,idx4);
}
}
else {
SideIndexInfo side_index;
side_index.vidx1=polygon_indices[i][1];
side_index.vidx2=polygon_indices[i][2];
side_index.polygon_index=i;
SideHash.Insert(tri,side_index);
}
}
if (duplicates[2] && duplicates[0]) {
TriangleSide tri(locations[polygon_indices[i][2]],locations[polygon_indices[i][0]]);
if (SideHash.Exists(tri)) {
SideIndexInfo side_index=SideHash.Get(tri);
unsigned idx1=side_index.vidx1;
unsigned idx2=side_index.vidx2;
unsigned idx3=polygon_indices[i][2];
unsigned idx4=polygon_indices[i][0];
bool diff=!(idx1^idx3)|!(idx1^idx4)|!(idx2^idx3)|!(idx2^idx4);
if (!diff) {
if (!GapFiller) GapFiller=new GapFillerClass(this);
GapFiller->Add_Polygon(i,idx4,idx2,idx1);
GapFiller->Add_Polygon(side_index.polygon_index,idx3,idx2,idx4);
}
}
else {
SideIndexInfo side_index;
side_index.vidx1=polygon_indices[i][2];
side_index.vidx2=polygon_indices[i][0];
side_index.polygon_index=i;
SideHash.Insert(tri,side_index);
}
}
}
LocationHash.Remove_All();
DuplicateLocationHash.Remove_All();
SideHash.Remove_All();
if (GapFiller) GapFiller->Shrink_Buffers();
}