/* ** 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 . */ /*********************************************************************************************** *** 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 _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 = 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;stageChange_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;passChange_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(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; } } // 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;pidxx = (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(verts[vi+pidx].u1)=uv0[vi+pidx]; else reinterpret_cast(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 * 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)); 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 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) { 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;passGet_Pass_Count();++pass) { for (int stage=0;stageHas_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;passGet_Pass_Count();++pass) { for (int stage=0;stageAdd_Ref(); } } else TextureArray[pass][stage]=NULL; } if (that.MaterialArray[pass]) { MaterialArray[pass]=new VertexMaterialClass*[ArraySize]; for (unsigned i=0;iAdd_Ref(); } } else MaterialArray[pass]=NULL; if (that.ShaderArray[pass]) { ShaderArray[pass]=new 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=new 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;stageAdd_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(); }