/* ** Command & Conquer Generals Zero Hour(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ /*********************************************************************************************** *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** *********************************************************************************************** * * * Project Name : WW3D * * * * $Archive:: /Commando/Code/ww3d2/meshmdl.cpp $* * * * Org Author:: Greg Hjelstrom * * * * $Author:: Kenny Mitchell * * * * $Modtime:: 06/26/02 4:04p $* * * * $Revision:: 48 $* * * * 06/26/02 KM Matrix name change to avoid MAX conflicts * *---------------------------------------------------------------------------------------------* * 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 _TempVertexBuffer; static DynamicVectorClass _TempNormalBuffer; static DynamicVectorClass _TempTransformedVertexBuffer; static DynamicVectorClass _TempClipFlagBuffer; /* ** ** MeshModelClass Implementation ** */ MeshModelClass::MeshModelClass(void) : DefMatDesc(NULL), AlternateMatDesc(NULL), CurMatDesc(NULL), MatInfo(NULL), GapFiller(NULL) { Set_Flag(DIRTY_BOUNDS,true); DefMatDesc = W3DNEW 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 = W3DNEW MeshMatDescClass(*(that.DefMatDesc)); if (that.AlternateMatDesc != NULL) { AlternateMatDesc = W3DNEW MeshMatDescClass(*(that.AlternateMatDesc)); } CurMatDesc = DefMatDesc; clone_materials(that); return ; } MeshModelClass::~MeshModelClass(void) { // WWDEBUG_SAY(("Note: Mesh %s was never used\n",Get_Name())); TheDX8MeshRenderer.Unregister_Mesh_Type(this); 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) { // Remove all polygon renderers, this will remove the mesh from the rendering system. // The mesh will be initialized to rendering system the next time it is rendered. TheDX8MeshRenderer.Unregister_Mesh_Type(this); MeshGeometryClass::operator = (that); *DefMatDesc = *(that.DefMatDesc); CurMatDesc = DefMatDesc; if (AlternateMatDesc != NULL) { delete AlternateMatDesc; AlternateMatDesc = NULL; } if (that.AlternateMatDesc != NULL) { AlternateMatDesc = W3DNEW MeshMatDescClass(*(that.AlternateMatDesc)); } clone_materials(that); if (GapFiller) { // DMS - using approriate deallocation method delete GapFiller; GapFiller=NULL; } if (that.GapFiller) GapFiller=W3DNEW GapFillerClass(*that.GapFiller); } return * this; } void MeshModelClass::Reset(int polycount,int vertcount,int passcount) { //DMS - We must delete the gapfiller object BEFORE the geometry is reset. Otherwise, // the number of stages and passes gets reset and the gapfiller cannot deallocate properly. delete GapFiller; GapFiller=NULL; Reset_Geometry(polycount,vertcount); // Release everything we have and reset to initial state TheDX8MeshRenderer.Unregister_Mesh_Type(this); MatInfo->Reset(); DefMatDesc->Reset(polycount,vertcount,passcount); if (AlternateMatDesc != NULL) { delete AlternateMatDesc; AlternateMatDesc = NULL; } CurMatDesc = DefMatDesc; 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; } } TheDX8MeshRenderer.Register_Mesh_Type(this); } void MeshModelClass::Replace_Texture(TextureClass* texture,TextureClass* new_texture) { WWASSERT(texture); WWASSERT(new_texture); for (int stage=0;stageChange_Polygon_Renderer_Texture(PolygonRendererList,texture,new_texture,pass,stage); } } } } void MeshModelClass::Replace_VertexMaterial(VertexMaterialClass* vmat,VertexMaterialClass* new_vmat) { WWASSERT(vmat); WWASSERT(new_vmat); for (int pass=0;passChange_Polygon_Renderer_Material(PolygonRendererList,vmat,new_vmat,pass); } } } DX8FVFCategoryContainer* MeshModelClass::Peek_FVF_Category_Container() { if (PolygonRendererList.Is_Empty()) return NULL; DX8PolygonRendererClass* polygon_renderer=PolygonRendererList.Get_Head(); WWASSERT(polygon_renderer); DX8TextureCategoryClass* texture_category=polygon_renderer->Get_Texture_Category(); WWASSERT(texture_category); DX8FVFCategoryContainer* fvf_category=texture_category->Get_Container(); WWASSERT(fvf_category); return fvf_category; } 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(transf_ptr); Vector4* optr = transf_ptr; for (int a=0;aSet_Vertex_Locations(reinterpret_cast(transf_ptr),VertexCount); rinfo.BWRenderer->Render_Triangles(reinterpret_cast(Poly->Get_Array()),PolyCount*3); return; } } void MeshModelClass::Make_Geometry_Unique() { WWASSERT(Vertex); ShareBufferClass * unique_verts = NEW_REF(ShareBufferClass,(*Vertex)); REF_PTR_SET(Vertex,unique_verts); REF_PTR_RELEASE(unique_verts); ShareBufferClass * norms = NEW_REF(ShareBufferClass,(*VertexNorm)); REF_PTR_SET(VertexNorm,norms); REF_PTR_RELEASE(norms); #if (!OPTIMIZE_PLANEEQ_RAM) ShareBufferClass * peq = NEW_REF(ShareBufferClass,(*PlaneEq, "MeshModelClass::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(); if (WW3D::Is_Overbright_Modify_On_Load_Enabled()) modify_for_overbright(); // 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(); if (WW3D::Is_Overbright_Modify_On_Load_Enabled()) modify_for_overbright(); // 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 inline unsigned int HashTemplateKeyClass::Get_Hash_Value(const Vector3& location) { const unsigned char* buffer=(const unsigned char*)&location; unsigned int hval=0; for (unsigned int a=0;a inline unsigned int HashTemplateKeyClass::Get_Hash_Value(const TriangleSide& side) { const unsigned char* buffer=(const unsigned char*)&side; unsigned int hval=0; for (unsigned int a=0;a LocationHash; HashTemplateClass DuplicateLocationHash; HashTemplateClass 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) { //DMS - We cannot take a reference to the mesh model here! This is because the mesh model // class OWNS the GapFiller class (allocated via NEW). If we take a reference here, there // will be an extra reference on the parent object, which will result in the parent object // not being destroyed. // // REF_PTR_SET(mmc,mmc_); mmc = mmc_; ArraySize=mmc->Get_Polygon_Count()*6; // Each side of each triangle can have 2 polygons added, in the worst case PolygonArray=W3DNEWARRAY TriIndex[ArraySize]; for (int pass=0;passGet_Pass_Count();++pass) { for (int stage=0;stageHas_Texture_Array(pass,stage)) { TextureArray[pass][stage]=W3DNEWARRAY TextureClass*[ArraySize]; } else TextureArray[pass][stage]=NULL; } if (mmc->Has_Material_Array(pass)) { MaterialArray[pass]=W3DNEWARRAY VertexMaterialClass*[ArraySize]; } else MaterialArray[pass]=NULL; if (mmc->Has_Shader_Array(pass)) { ShaderArray[pass]=W3DNEWARRAY ShaderClass[ArraySize]; } else ShaderArray[pass]=NULL; } } GapFillerClass::GapFillerClass(const GapFillerClass& that) : mmc(NULL), PolygonCount(that.PolygonCount) { //DMS - We cannot take a reference to the mesh model here! This is because the mesh model // class OWNS the GapFiller class (allocated via NEW). If we take a reference here, there // will be an extra reference on the parent object, which will result in the parent object // not being destroyed. // // REF_PTR_SET(mmc,that.mmc); mmc = that.mmc; ArraySize=that.ArraySize; PolygonArray=W3DNEWARRAY TriIndex[ArraySize]; for (int pass=0;passGet_Pass_Count();++pass) { for (int stage=0;stageAdd_Ref(); } } else TextureArray[pass][stage]=NULL; } if (that.MaterialArray[pass]) { MaterialArray[pass]=W3DNEWARRAY VertexMaterialClass*[ArraySize]; for (unsigned i=0;iAdd_Ref(); } } else MaterialArray[pass]=NULL; if (that.ShaderArray[pass]) { ShaderArray[pass]=W3DNEWARRAY ShaderClass[ArraySize]; for (unsigned i=0;iGet_Pass_Count();++pass) { for (int stage=0;stageGet_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;passGet_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;stageHas_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=W3DNEWARRAY TriIndex[PolygonCount]; memcpy(new_polygon_array,PolygonArray,PolygonCount*sizeof(TriIndex)); delete[] PolygonArray; PolygonArray=new_polygon_array; for (int pass=0;passGet_Pass_Count();++pass) { for (int stage=0;stageSupport_NPatches()) return; if (!Get_Flag(MeshGeometryClass::ALLOW_NPATCHES)) return; 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;iAdd_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=W3DNEW 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=W3DNEW 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(); }