2018 lines
No EOL
96 KiB
C++
2018 lines
No EOL
96 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/meshgeometry.cpp $*
|
|
* *
|
|
* Original Author:: Greg Hjelstrom *
|
|
* *
|
|
* $Author:: Jani_p $*
|
|
* *
|
|
* $Modtime:: 11/24/01 5:34p $*
|
|
* *
|
|
* $Revision:: 14 $*
|
|
* *
|
|
*---------------------------------------------------------------------------------------------*
|
|
* Functions: *
|
|
* MeshGeometryClass::MeshGeometryClass -- Constructor *
|
|
* MeshGeometryClass::MeshGeometryClass -- Copy Constructor *
|
|
* -- assignment operator *
|
|
* MeshGeometryClass::~MeshGeometryClass -- destructor *
|
|
* MeshGeometryClass::Reset_Geometry -- releases current resources and allocates space if ne *
|
|
* MeshGeometryClass::Get_Name -- returns the name *
|
|
* MeshGeometryClass::Set_Name -- set the name of this model *
|
|
* MeshGeometryClass::Get_User_Text -- get the user-text buffer *
|
|
* MeshGeometryClass::Set_User_Text -- set the user text buffer *
|
|
* MeshGeometryClass::Get_Bounding_Box -- get the bounding box *
|
|
* MeshGeometryClass::Get_Bounding_Sphere -- get the bounding sphere *
|
|
* MeshGeometryClass::Generate_Rigid_APT -- generate active polygon table *
|
|
* MeshGeometryClass::Generate_Skin_APT -- generate an active polygon table *
|
|
* MeshGeometryClass::Contains -- test if the mesh contains the given point *
|
|
* MeshGeometryClass::Cast_Ray -- compute a ray intersection with this mesh *
|
|
* MeshGeometryClass::Cast_AABox -- cast an AABox against this mesh *
|
|
* MeshGeometryClass::Cast_OBBox -- Cast an obbox against this mesh *
|
|
* MeshGeometryClass::Intersect_OBBox -- test for intersection with the given OBBox *
|
|
* MeshGeometryClass::Cast_World_Space_AABox -- test for intersection with a worldspace AABox*
|
|
* MeshGeometryClass::cast_semi_infinite_axis_aligned_ray -- casts an axis aligned ray *
|
|
* MeshGeometryClass::cast_aabox_identity -- aligned aabox test *
|
|
* MeshGeometryClass::cast_aabox_z90 -- aabox test which is rotated about z by 90 *
|
|
* MeshGeometryClass::cast_aabox_z180 -- aabox test which is rotated about z by 180 *
|
|
* MeshGeometryClass::cast_aabox_z270 -- aabox test which is rotated about z by 270 *
|
|
* MeshGeometryClass::cast_ray_brute_force -- brute force ray-cast *
|
|
* MeshGeometryClass::cast_aabox_brute_force -- brute force aabox cast *
|
|
* MeshGeometryClass::cast_obbox_brute_force -- brute force obbox cast *
|
|
* MeshGeometryClass::intersect_obbox_brute_force -- brute force intersection check *
|
|
* MeshGeometryClass::Compute_Plane_Equations -- Recalculates the plane equations *
|
|
* MeshGeometryClass::Compute_Vertex_Normals -- recompute the vertex normals *
|
|
* MeshGeometryClass::Compute_Bounds -- recomputes the bounding volumes *
|
|
* MeshGeometryClass::get_vert_normals -- get the vertex normal array *
|
|
* MeshGeometryClass::Get_Vertex_Normal_Array -- validates and returns the vertex normal arr *
|
|
* MeshGeometryClass::get_planes -- get the plane array memory (internal) *
|
|
* MeshGeometryClass::Get_Plane_Array -- validates and returns the array of plane equations *
|
|
* MeshGeometryClass::Compute_Plane -- compute the plane equation of a single poly *
|
|
* MeshGeometryClass::Generate_Culling_Tree -- Generate an AABTree for this mesh *
|
|
* MeshGeometryClass::Load_W3D -- Load a mesh from a w3d *
|
|
* MeshGeometryClass::read_chunks -- read w3d chunks *
|
|
* MeshGeometryClass::read_vertices -- read the vertex chunk from a W3D file *
|
|
* MeshGeometryClass::read_vertex_normals -- read the vertex normals chunk from a w3d file *
|
|
* MeshGeometryClass::read_triangles -- read the triangles chunk from a w3d file *
|
|
* MeshGeometryClass::read_user_text -- read the user text chunk from a w3d file *
|
|
* MeshGeometryClass::read_vertex_influences -- read the vertex influences chunk from a w3d *
|
|
* MeshGeometryClass::read_vertex_shade_indices -- read the vertex shade indices chunk *
|
|
* MeshGeometryClass::read_aabtree -- read the AABTree chunk from a w3d file *
|
|
* MeshGeometryClass::Generate_APT -- generate an apt for a box and view direction *
|
|
* MeshGeometryClass::Generate_Rigid_APT -- generate an apt using backface culling *
|
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
|
|
#include "meshgeometry.h"
|
|
#include "aabtree.h"
|
|
#include "chunkio.h"
|
|
#include "aabox.h"
|
|
#include "obbox.h"
|
|
#include "sphere.h"
|
|
#include "plane.h"
|
|
#include "wwdebug.h"
|
|
#include "wwmemlog.h"
|
|
#include "w3d_file.h"
|
|
#include "vp.h"
|
|
|
|
|
|
#if (OPTIMIZE_PLANEEQ_RAM)
|
|
static SimpleVecClass<Vector4> _PlaneEQArray(1024);
|
|
#endif
|
|
|
|
|
|
#if (OPTIMIZE_VNORM_RAM)
|
|
static SimpleVecClass<Vector3> _VNormArray(1024);
|
|
#endif
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::MeshGeometryClass -- Constructor *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/9/2000 gth : Created. *
|
|
*=============================================================================================*/
|
|
MeshGeometryClass::MeshGeometryClass(void) :
|
|
MeshName(NULL),
|
|
UserText(NULL),
|
|
Flags(0),
|
|
SortLevel(SORT_LEVEL_NONE),
|
|
W3dAttributes(0),
|
|
PolyCount(0),
|
|
VertexCount(0),
|
|
Poly(NULL),
|
|
PolySurfaceType(NULL),
|
|
Vertex(NULL),
|
|
VertexNorm(NULL),
|
|
PlaneEq(NULL),
|
|
VertexShadeIdx(NULL),
|
|
VertexBoneLink(NULL),
|
|
BoundBoxMin(0,0,0),
|
|
BoundBoxMax(1,1,1),
|
|
BoundSphereCenter(0,0,0),
|
|
BoundSphereRadius(1),
|
|
CullTree(NULL)
|
|
{
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::MeshGeometryClass -- Copy Constructor *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/9/2000 gth : Created. *
|
|
*=============================================================================================*/
|
|
MeshGeometryClass::MeshGeometryClass(const MeshGeometryClass & that) :
|
|
MeshName(NULL),
|
|
UserText(NULL),
|
|
Flags(0),
|
|
SortLevel(SORT_LEVEL_NONE),
|
|
W3dAttributes(0),
|
|
PolyCount(0),
|
|
VertexCount(0),
|
|
Poly(NULL),
|
|
PolySurfaceType(NULL),
|
|
Vertex(NULL),
|
|
VertexNorm(NULL),
|
|
PlaneEq(NULL),
|
|
VertexShadeIdx(NULL),
|
|
VertexBoneLink(NULL),
|
|
BoundBoxMin(0,0,0),
|
|
BoundBoxMax(1,1,1),
|
|
BoundSphereCenter(0,0,0),
|
|
BoundSphereRadius(1),
|
|
CullTree(NULL)
|
|
{
|
|
*this = that;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* -- assignment operator *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/9/2000 gth : Created. *
|
|
*=============================================================================================*/
|
|
MeshGeometryClass & MeshGeometryClass::operator = (const MeshGeometryClass & that)
|
|
{
|
|
if (this != &that) {
|
|
Flags = that.Flags;
|
|
SortLevel = that.SortLevel;
|
|
W3dAttributes = that.W3dAttributes;
|
|
PolyCount = that.PolyCount;
|
|
VertexCount = that.VertexCount;
|
|
|
|
BoundBoxMin = that.BoundBoxMin;
|
|
BoundBoxMax = that.BoundBoxMax;
|
|
BoundSphereCenter = that.BoundSphereCenter;
|
|
BoundSphereRadius = that.BoundSphereRadius;
|
|
|
|
REF_PTR_SET(MeshName,that.MeshName);
|
|
REF_PTR_SET(UserText,that.UserText);
|
|
REF_PTR_SET(Poly,that.Poly);
|
|
REF_PTR_SET(PolySurfaceType,that.PolySurfaceType);
|
|
REF_PTR_SET(Vertex,that.Vertex);
|
|
REF_PTR_SET(VertexNorm,that.VertexNorm);
|
|
REF_PTR_SET(PlaneEq,that.PlaneEq);
|
|
REF_PTR_SET(VertexShadeIdx,that.VertexShadeIdx);
|
|
REF_PTR_SET(VertexBoneLink,that.VertexBoneLink);
|
|
REF_PTR_SET(CullTree,that.CullTree);
|
|
}
|
|
return * this;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::~MeshGeometryClass -- destructor *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/9/2000 gth : Created. *
|
|
*=============================================================================================*/
|
|
MeshGeometryClass::~MeshGeometryClass(void)
|
|
{
|
|
Reset_Geometry(0,0);
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::Reset_Geometry -- releases current resources and allocates space if need *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/9/2000 gth : Created. *
|
|
*=============================================================================================*/
|
|
void MeshGeometryClass::Reset_Geometry(int polycount,int vertcount)
|
|
{
|
|
// Release everything we have and reset to initial state
|
|
Flags = 0;
|
|
PolyCount = 0;
|
|
VertexCount = 0;
|
|
SortLevel = SORT_LEVEL_NONE;
|
|
|
|
REF_PTR_RELEASE(MeshName);
|
|
REF_PTR_RELEASE(UserText);
|
|
REF_PTR_RELEASE(Poly);
|
|
REF_PTR_RELEASE(PolySurfaceType);
|
|
REF_PTR_RELEASE(Vertex);
|
|
REF_PTR_RELEASE(VertexNorm);
|
|
REF_PTR_RELEASE(PlaneEq);
|
|
REF_PTR_RELEASE(VertexShadeIdx);
|
|
REF_PTR_RELEASE(VertexBoneLink);
|
|
REF_PTR_RELEASE(CullTree);
|
|
|
|
PolyCount = polycount;
|
|
VertexCount = vertcount;
|
|
|
|
// allocate new geometry arrays
|
|
if ((polycount != 0) && (vertcount != 0)) {
|
|
Poly = NEW_REF(ShareBufferClass<TriIndex>,(PolyCount));
|
|
PolySurfaceType = NEW_REF(ShareBufferClass<uint8>,(PolyCount));
|
|
Vertex = NEW_REF(ShareBufferClass<Vector3>,(VertexCount));
|
|
|
|
Poly->Clear();
|
|
PolySurfaceType->Clear();
|
|
Vertex->Clear();
|
|
|
|
#if (!OPTIMIZE_VNORM_RAM)
|
|
VertexNorm = NEW_REF(ShareBufferClass<Vector3>,(VertexCount));
|
|
VertexNorm->Clear();
|
|
#endif
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::Get_Name -- returns the name *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/9/2000 gth : Created. *
|
|
*=============================================================================================*/
|
|
const char * MeshGeometryClass::Get_Name(void) const
|
|
{
|
|
if (MeshName) {
|
|
return MeshName->Get_Array();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::Set_Name -- set the name of this model *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/9/2000 gth : Created. *
|
|
*=============================================================================================*/
|
|
void MeshGeometryClass::Set_Name(const char * newname)
|
|
{
|
|
if (MeshName) {
|
|
MeshName->Release_Ref();
|
|
}
|
|
if (newname) {
|
|
MeshName = NEW_REF(ShareBufferClass<char>,(strlen(newname)+1));
|
|
strcpy(MeshName->Get_Array(),newname);
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::Get_User_Text -- get the user-text buffer *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/9/2000 gth : Created. *
|
|
*=============================================================================================*/
|
|
const char * MeshGeometryClass::Get_User_Text(void)
|
|
{
|
|
if (UserText) {
|
|
return UserText->Get_Array();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::Set_User_Text -- set the user text buffer *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/9/2000 gth : Created. *
|
|
*=============================================================================================*/
|
|
void MeshGeometryClass::Set_User_Text(char * usertext)
|
|
{
|
|
if (UserText) {
|
|
UserText->Release_Ref();
|
|
}
|
|
if (usertext) {
|
|
UserText = NEW_REF(ShareBufferClass<char>,(strlen(usertext)+1));
|
|
strcpy(UserText->Get_Array(),usertext);
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::Get_Bounding_Box -- get the bounding box *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/9/2000 gth : Created. *
|
|
*=============================================================================================*/
|
|
void MeshGeometryClass::Get_Bounding_Box(AABoxClass * set_box)
|
|
{
|
|
WWASSERT(set_box != NULL);
|
|
set_box->Center = (BoundBoxMax + BoundBoxMin) * 0.5f;
|
|
set_box->Extent = (BoundBoxMax - BoundBoxMin) * 0.5f;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::Get_Bounding_Sphere -- get the bounding sphere *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/9/2000 gth : Created. *
|
|
*=============================================================================================*/
|
|
void MeshGeometryClass::Get_Bounding_Sphere(SphereClass * set_sphere)
|
|
{
|
|
WWASSERT(set_sphere != NULL);
|
|
set_sphere->Center = BoundSphereCenter;
|
|
set_sphere->Radius = BoundSphereRadius;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::Generate_Rigid_APT -- generate an apt using backface culling *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 5/10/2001 gth : Created. *
|
|
*=============================================================================================*/
|
|
void MeshGeometryClass::Generate_Rigid_APT(const Vector3 & view_dir, SimpleDynVecClass<uint32> & apt)
|
|
{
|
|
const Vector3 * loc = Get_Vertex_Array();
|
|
const Vector4 * norms = Get_Plane_Array();
|
|
const TriIndex * polys = Get_Polygon_Array();
|
|
TriClass tri;
|
|
|
|
for (int poly_counter = 0; poly_counter < PolyCount; poly_counter++) {
|
|
|
|
tri.V[0] = &(loc[ polys[poly_counter][0] ]);
|
|
tri.V[1] = &(loc[ polys[poly_counter][1] ]);
|
|
tri.V[2] = &(loc[ polys[poly_counter][2] ]);
|
|
tri.N = (Vector3*)&(norms[poly_counter]);
|
|
|
|
if (Vector3::Dot_Product(*tri.N,view_dir) < 0.0f) {
|
|
apt.Add(poly_counter);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::Generate_Rigid_APT -- generate active polygon table *
|
|
* *
|
|
* This function will cull the mesh against the specified volume *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/9/2000 gth : Created. *
|
|
*=============================================================================================*/
|
|
void MeshGeometryClass::Generate_Rigid_APT(const OBBoxClass & local_box, SimpleDynVecClass<uint32> & apt)
|
|
{
|
|
if (CullTree != NULL) {
|
|
CullTree->Generate_APT(local_box, apt);
|
|
} else {
|
|
|
|
// Beware, this is gonna be expensive!
|
|
const Vector3 * loc = Get_Vertex_Array();
|
|
const Vector4 * norms = Get_Plane_Array();
|
|
const TriIndex * polys = Get_Polygon_Array();
|
|
TriClass tri;
|
|
|
|
for (int poly_counter = 0; poly_counter < PolyCount; poly_counter++) {
|
|
|
|
tri.V[0] = &(loc[ polys[poly_counter][0] ]);
|
|
tri.V[1] = &(loc[ polys[poly_counter][1] ]);
|
|
tri.V[2] = &(loc[ polys[poly_counter][2] ]);
|
|
tri.N = (Vector3*)&(norms[poly_counter]);
|
|
|
|
if (CollisionMath::Intersection_Test(local_box, tri)) {;
|
|
apt.Add(poly_counter);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::Generate_APT -- generate an apt for a box and view direction *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 5/10/2001 gth : Created. *
|
|
*=============================================================================================*/
|
|
void MeshGeometryClass::Generate_Rigid_APT(const OBBoxClass & local_box,const Vector3 & viewdir,SimpleDynVecClass<uint32> & apt)
|
|
{
|
|
if (CullTree != NULL) {
|
|
CullTree->Generate_APT(local_box, viewdir,apt);
|
|
} else {
|
|
|
|
// Beware, this is gonna be expensive!
|
|
const Vector3 * loc = Get_Vertex_Array();
|
|
const Vector4 * norms = Get_Plane_Array();
|
|
const TriIndex * polys = Get_Polygon_Array();
|
|
TriClass tri;
|
|
|
|
for (int poly_counter = 0; poly_counter < PolyCount; poly_counter++) {
|
|
|
|
tri.V[0] = &(loc[ polys[poly_counter][0] ]);
|
|
tri.V[1] = &(loc[ polys[poly_counter][1] ]);
|
|
tri.V[2] = &(loc[ polys[poly_counter][2] ]);
|
|
tri.N = (Vector3*)&(norms[poly_counter]);
|
|
|
|
if (Vector3::Dot_Product(*tri.N,viewdir) < 0.0f) {
|
|
if (CollisionMath::Intersection_Test(local_box,tri)) {
|
|
apt.Add(poly_counter);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::Generate_Skin_APT -- generate an active polygon table *
|
|
* *
|
|
* This function culls the mesh against the specified volume *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/9/2000 gth : Created. *
|
|
*=============================================================================================*/
|
|
void MeshGeometryClass::Generate_Skin_APT(const OBBoxClass & world_box, SimpleDynVecClass<uint32> & apt, const Vector3 *world_vertex_locs)
|
|
{
|
|
WWASSERT(world_vertex_locs);
|
|
|
|
// Beware, this is gonna be expensive!
|
|
const TriIndex * polys = Get_Polygon_Array();
|
|
TriClass tri;
|
|
|
|
for (int poly_counter=0; poly_counter < PolyCount; poly_counter++) {
|
|
|
|
tri.V[0] = &(world_vertex_locs[ polys[poly_counter][0] ]);
|
|
tri.V[1] = &(world_vertex_locs[ polys[poly_counter][1] ]);
|
|
tri.V[2] = &(world_vertex_locs[ polys[poly_counter][2] ]);
|
|
|
|
// We would have to do a non-trivial amount of computation to get the triangle normal
|
|
// (since this is a skin we have no valid plane equations) and we know that N is not used
|
|
// in the Intersection_Test, so we set it to a dummy value.
|
|
static const Vector3 dummy_vec(0.0f, 0.0f, 1.0f);
|
|
tri.N = &dummy_vec;
|
|
|
|
if (CollisionMath::Intersection_Test(world_box,tri)) {;
|
|
apt.Add(poly_counter);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::Contains -- test if the mesh contains the given point *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/9/2000 gth : Created. *
|
|
*=============================================================================================*/
|
|
bool MeshGeometryClass::Contains(const Vector3 &point)
|
|
{
|
|
// To determine whether the mesh contains a point, we will use the parity method (cast a
|
|
// semi-infinite ray from the point and check the number of intersections with the mesh.
|
|
// An even number of intersections means the point is outside the mesh, and an odd number
|
|
// means the point is inside the mesh. Since we can cast the ray in any direction we will
|
|
// use axis-aligned rays for speed.
|
|
|
|
// The method fails if the ray goes through any triangle edge or corner. For robustness
|
|
// we will cast rays in all six axis-aligned directions, and take a majority vote. We will
|
|
// significantly reduce the weighting of rays which go through corners/edges.
|
|
// Also, if any raycast reports that the point is embedded in a triangle, we will stop and
|
|
// report that the point is contained in the mesh (using the normal algorithm in this case
|
|
// could lead to errors).
|
|
float yes = 0.0f; // weighted sum of rays indicating the point is contained in the mesh
|
|
float no = 0.0f; // weighted sum of rays indicating the point is not contained in the mesh
|
|
for (int axis_dir = 0; axis_dir < 6; axis_dir++) {
|
|
unsigned char flags = TRI_RAYCAST_FLAG_NONE;
|
|
int intersections = cast_semi_infinite_axis_aligned_ray(point, axis_dir, flags);
|
|
if (flags & TRI_RAYCAST_FLAG_START_IN_TRI) return true;
|
|
float weight = flags & TRI_RAYCAST_FLAG_HIT_EDGE ? 0.1f : 1.0f;
|
|
if (intersections & 0x01) {
|
|
yes += weight;
|
|
} else {
|
|
no += weight;
|
|
}
|
|
}
|
|
|
|
return yes > no;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::Cast_Ray -- compute a ray intersection with this mesh *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 3/1/2001 NH : Created. *
|
|
*=============================================================================================*/
|
|
bool MeshGeometryClass::Cast_Ray(RayCollisionTestClass & raytest)
|
|
{
|
|
bool hit = false;
|
|
|
|
if (CullTree) {
|
|
hit = CullTree->Cast_Ray(raytest);
|
|
} else {
|
|
hit = cast_ray_brute_force(raytest);
|
|
}
|
|
|
|
return hit;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::Cast_AABox -- cast an AABox against this mesh *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 3/1/2001 NH : Created. *
|
|
*=============================================================================================*/
|
|
bool MeshGeometryClass::Cast_AABox(AABoxCollisionTestClass & boxtest)
|
|
{
|
|
bool hit = false;
|
|
|
|
if (CullTree) {
|
|
hit = CullTree->Cast_AABox(boxtest);
|
|
} else {
|
|
hit = cast_aabox_brute_force(boxtest);
|
|
}
|
|
|
|
return hit;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::Cast_OBBox -- Cast an obbox against this mesh *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 3/1/2001 NH : Created. *
|
|
*=============================================================================================*/
|
|
bool MeshGeometryClass::Cast_OBBox(OBBoxCollisionTestClass & boxtest)
|
|
{
|
|
bool hit = false;
|
|
|
|
if (CullTree) {
|
|
hit = CullTree->Cast_OBBox(boxtest);
|
|
} else {
|
|
hit = cast_obbox_brute_force(boxtest);
|
|
}
|
|
|
|
return hit;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::Intersect_OBBox -- test for intersection with the given OBBox *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 3/1/2001 NH : Created. *
|
|
*=============================================================================================*/
|
|
bool MeshGeometryClass::Intersect_OBBox(OBBoxIntersectionTestClass & boxtest)
|
|
{
|
|
bool hit = false;
|
|
|
|
if (CullTree) {
|
|
hit = CullTree->Intersect_OBBox(boxtest);
|
|
} else {
|
|
hit = intersect_obbox_brute_force(boxtest);
|
|
}
|
|
|
|
return hit;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::Cast_World_Space_AABox -- test for intersection with a worldspace AABox*
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 3/1/2001 NH : Created. *
|
|
*=============================================================================================*/
|
|
bool MeshGeometryClass::Cast_World_Space_AABox(AABoxCollisionTestClass & boxtest, const Matrix3D &transform)
|
|
{
|
|
/*
|
|
** Attempt to classify the transform:
|
|
** NOTE: This code assumes that the matrix is orthogonal! Much code in W3D assumes
|
|
** orthogonal matrices, if that convention is broken this code is also broken.
|
|
** Basically what I'm doing here is doing the minimum number of compares needed
|
|
** to identify a transform which is a 90 degree rotation about the z axis.
|
|
** TODO: cache some transform flags somewhere to reduce the number of times
|
|
** that these compares are done
|
|
*/
|
|
bool hit = false;
|
|
|
|
if ((transform[0][0] == 1.0f) && (transform[1][1] == 1.0f)) {
|
|
|
|
hit = cast_aabox_identity(boxtest,-transform.Get_Translation());
|
|
|
|
} else if ((transform[0][1] == -1.0f) && (transform[1][0] == 1.0f)) {
|
|
|
|
// this mesh has been rotated 90 degrees about z
|
|
hit = cast_aabox_z90(boxtest,-transform.Get_Translation());
|
|
|
|
} else if ((transform[0][0] == -1.0f) && (transform[1][1] == -1.0f)) {
|
|
|
|
// this mesh has been rotated 180
|
|
hit = cast_aabox_z180(boxtest,-transform.Get_Translation());
|
|
|
|
} else if ((transform[0][1] == 1.0f) && (transform[1][0] == -1.0f)) {
|
|
|
|
// this mesh has been rotated 270
|
|
hit = cast_aabox_z270(boxtest,-transform.Get_Translation());
|
|
|
|
} else {
|
|
|
|
/*
|
|
** Ok, we fell through to here, that means there is a more general
|
|
** transform on this mesh. In this case, I create a new test which
|
|
** is an oriented box test in the coordinate system of the mesh and cast it.
|
|
*/
|
|
Matrix3D world_to_obj;
|
|
transform.Get_Orthogonal_Inverse(world_to_obj);
|
|
OBBoxCollisionTestClass obbox(boxtest, world_to_obj);
|
|
|
|
if (CullTree) {
|
|
hit = CullTree->Cast_OBBox(obbox);
|
|
} else {
|
|
hit = cast_obbox_brute_force(obbox);
|
|
}
|
|
|
|
/*
|
|
** now, we must transform the results of the test back to the original
|
|
** coordinate system.
|
|
*/
|
|
if (hit) {
|
|
Matrix3D::Rotate_Vector(transform, obbox.Result->Normal, &(obbox.Result->Normal));
|
|
if (boxtest.Result->ComputeContactPoint) {
|
|
Matrix3D::Transform_Vector(transform, obbox.Result->ContactPoint, &(obbox.Result->ContactPoint));
|
|
}
|
|
}
|
|
}
|
|
|
|
return hit;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::cast_semi_infinite_axis_aligned_ray -- casts an axis aligned ray *
|
|
* *
|
|
* This function is used by the 'Contains' function *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/9/2000 gth : Created. *
|
|
*=============================================================================================*/
|
|
int MeshGeometryClass::cast_semi_infinite_axis_aligned_ray(const Vector3 & start_point, int axis_dir,
|
|
unsigned char & flags)
|
|
{
|
|
int count = 0;
|
|
if (CullTree) {
|
|
count = CullTree->Cast_Semi_Infinite_Axis_Aligned_Ray(start_point, axis_dir, flags);
|
|
} else {
|
|
|
|
const Vector3 * loc = Get_Vertex_Array();
|
|
const Vector4 * plane = Get_Plane_Array();
|
|
const TriIndex * polyverts = Get_Polygon_Array();
|
|
|
|
// These tables translate between the axis_dir representation (which is an integer in which 0
|
|
// indicates a ray along the positive x axis, 1 along the negative x axis, 2 the positive y
|
|
// axis, 3 negative y axis, 4 positive z axis, 5 negative z axis) and a four-integer
|
|
// representation (axis_r is the axis number - 0, 1 or 2 - of the axis along which the ray is
|
|
// cast; axis_1 and axis_2 are the axis numbers of the other two axes; direction is 0 for
|
|
// negative and 1 for positive direction of the ray).
|
|
static const int axis_r[6] = { 0, 0, 1, 1, 2, 2 };
|
|
static const int axis_1[6] = { 1, 1, 2, 2, 0, 0 };
|
|
static const int axis_2[6] = { 2, 2, 0, 0, 1, 1 };
|
|
static const int direction[6] = { 1, 0, 1, 0, 1, 0 };
|
|
WWASSERT(axis_dir >= 0);
|
|
WWASSERT(axis_dir < 6);
|
|
|
|
// The functions called after this point will 'or' bits into this variable, so it needs to
|
|
// be initialized here to TRI_RAYCAST_FLAG_NONE.
|
|
flags = TRI_RAYCAST_FLAG_NONE;
|
|
|
|
/*
|
|
** Loop over each polygon
|
|
*/
|
|
int poly_count = Get_Polygon_Count();
|
|
for (int poly_counter=0; poly_counter < poly_count; poly_counter++) {
|
|
|
|
const Vector3 &v0 = loc[ polyverts[poly_counter][0] ];
|
|
const Vector3 &v1 = loc[ polyverts[poly_counter][1] ];
|
|
const Vector3 &v2 = loc[ polyverts[poly_counter][2] ];
|
|
const Vector4 &tri_plane = plane[poly_counter];
|
|
|
|
// Since (int)true is defined as 1, and (int)false as 0:
|
|
count += (unsigned int)Cast_Semi_Infinite_Axis_Aligned_Ray_To_Triangle(v0, v1, v2,
|
|
tri_plane, start_point, axis_r[axis_dir], axis_1[axis_dir], axis_2[axis_dir],
|
|
direction[axis_dir], flags);
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::cast_aabox_identity -- aligned aabox test *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 3/1/2001 NH : Created. *
|
|
*=============================================================================================*/
|
|
bool MeshGeometryClass::cast_aabox_identity(AABoxCollisionTestClass & boxtest, const Vector3 & translation)
|
|
{
|
|
// transform the test into the mesh's coordinate system
|
|
AABoxCollisionTestClass newbox(boxtest);
|
|
newbox.Translate(translation);
|
|
|
|
// cast the box against the mesh
|
|
if (CullTree) {
|
|
return CullTree->Cast_AABox(newbox);
|
|
} else {
|
|
return cast_aabox_brute_force(newbox);
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::cast_aabox_z90 -- aabox test which is rotated about z by 90 *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 3/1/2001 NH : Created. *
|
|
*=============================================================================================*/
|
|
bool MeshGeometryClass::cast_aabox_z90(AABoxCollisionTestClass & boxtest, const Vector3 & translation)
|
|
{
|
|
// transform the test into the mesh's coordinate system
|
|
AABoxCollisionTestClass newbox(boxtest);
|
|
newbox.Translate(translation);
|
|
newbox.Rotate(AABoxCollisionTestClass::ROTATE_Z270);
|
|
|
|
// cast the box against the mesh, using culling if possible
|
|
bool hit;
|
|
if (CullTree) {
|
|
hit = CullTree->Cast_AABox(newbox);
|
|
} else {
|
|
hit = cast_aabox_brute_force(newbox);
|
|
}
|
|
|
|
// if we hit something, we need to rotate the normal back out of the mesh coordinate system
|
|
if (hit) {
|
|
// rotating the normal by 90 degrees about Z
|
|
float tmp = boxtest.Result->Normal.X;
|
|
boxtest.Result->Normal.X = -boxtest.Result->Normal.Y;
|
|
boxtest.Result->Normal.Y = tmp;
|
|
}
|
|
|
|
return hit;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::cast_aabox_z180 -- aabox test which is rotated about z by 180 *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 3/1/2001 NH : Created. *
|
|
*=============================================================================================*/
|
|
bool MeshGeometryClass::cast_aabox_z180(AABoxCollisionTestClass & boxtest, const Vector3 & translation)
|
|
{
|
|
// transform the test into the meshes coordinate system
|
|
AABoxCollisionTestClass newbox(boxtest);
|
|
newbox.Translate(translation);
|
|
newbox.Rotate(AABoxCollisionTestClass::ROTATE_Z180);
|
|
|
|
// cast the box against the mesh, using culling if possible
|
|
bool hit;
|
|
if (CullTree) {
|
|
hit = CullTree->Cast_AABox(newbox);
|
|
} else {
|
|
hit = cast_aabox_brute_force(newbox);
|
|
}
|
|
|
|
// if we hit something, we need to rotate the normal back out of the mesh coordinate system
|
|
if (hit) {
|
|
// rotating the normal by 180 degrees about Z
|
|
boxtest.Result->Normal.X = -boxtest.Result->Normal.X;
|
|
boxtest.Result->Normal.Y = -boxtest.Result->Normal.Y;
|
|
}
|
|
|
|
return hit;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::cast_aabox_z270 -- aabox test which is rotated about z by 270 *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 3/1/2001 NH : Created. *
|
|
*=============================================================================================*/
|
|
bool MeshGeometryClass::cast_aabox_z270(AABoxCollisionTestClass & boxtest, const Vector3 & translation)
|
|
{
|
|
// transform the test into the mesh's coordinate system
|
|
AABoxCollisionTestClass newbox(boxtest);
|
|
newbox.Translate(translation);
|
|
newbox.Rotate(AABoxCollisionTestClass::ROTATE_Z90);
|
|
|
|
// cast the box against the mesh, using culling if possible
|
|
bool hit;
|
|
if (CullTree) {
|
|
hit = CullTree->Cast_AABox(newbox);
|
|
} else {
|
|
hit = cast_aabox_brute_force(newbox);
|
|
}
|
|
|
|
// if we hit something, we need to rotate the normal back out of the mesh coordinate system
|
|
if (hit) {
|
|
// rotating the normal by 270 degrees about Z
|
|
float tmp = boxtest.Result->Normal.X;
|
|
boxtest.Result->Normal.X = boxtest.Result->Normal.Y;
|
|
boxtest.Result->Normal.Y = -tmp;
|
|
}
|
|
|
|
return hit;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::intersect_obbox_brute_force -- brute force intersection check *
|
|
* *
|
|
* This function gets used if the mesh does not have a CullTree *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 3/1/2001 NH : Created. *
|
|
*=============================================================================================*/
|
|
bool MeshGeometryClass::intersect_obbox_brute_force(OBBoxIntersectionTestClass & localtest)
|
|
{
|
|
TriClass tri;
|
|
const Vector3 * loc = Get_Vertex_Array();
|
|
const TriIndex * polyverts = Get_Polygon_Array();
|
|
#ifndef COMPUTE_NORMALS
|
|
const Vector4 * norms = Get_Plane_Array();
|
|
#endif
|
|
|
|
/*
|
|
** Loop over each polygon
|
|
*/
|
|
for (int srtri=0; srtri < Get_Polygon_Count(); srtri++) {
|
|
|
|
tri.V[0] = &(loc[ polyverts[srtri][0] ]);
|
|
tri.V[1] = &(loc[ polyverts[srtri][1] ]);
|
|
tri.V[2] = &(loc[ polyverts[srtri][2] ]);
|
|
|
|
#ifdef COMPUTE_NORMALS
|
|
static Vector3 _normal;
|
|
tri.N = &_normal;
|
|
tri.Compute_Normal();
|
|
#else
|
|
tri.N = (Vector3 *)&(norms[srtri]);
|
|
#endif
|
|
|
|
if (CollisionMath::Intersection_Test(localtest.Box, tri)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::cast_ray_brute_force -- brute force ray-cast *
|
|
* *
|
|
* This function gets used if the mesh does not have a CullTree *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 3/1/2001 NH : Created. *
|
|
*=============================================================================================*/
|
|
bool MeshGeometryClass::cast_ray_brute_force(RayCollisionTestClass & raytest)
|
|
{
|
|
int srtri;
|
|
TriClass tri;
|
|
const Vector3 * loc = Get_Vertex_Array();
|
|
const TriIndex * polyverts = Get_Polygon_Array();
|
|
#ifndef COMPUTE_NORMALS
|
|
const Vector4 * norms = Get_Plane_Array();
|
|
#endif
|
|
|
|
/*
|
|
** Loop over each polygon
|
|
*/
|
|
bool hit = false;
|
|
for (srtri=0; srtri < Get_Polygon_Count(); srtri++) {
|
|
|
|
// TODO: find a better way to do this?
|
|
tri.V[0] = &(loc[ polyverts[srtri][0] ]);
|
|
tri.V[1] = &(loc[ polyverts[srtri][1] ]);
|
|
tri.V[2] = &(loc[ polyverts[srtri][2] ]);
|
|
|
|
#ifdef COMPUTE_NORMALS
|
|
static Vector3 _normal;
|
|
tri.N = &_normal;
|
|
tri.Compute_Normal();
|
|
#else
|
|
tri.N = (Vector3 *)&(norms[srtri]);
|
|
#endif
|
|
|
|
hit = hit | CollisionMath::Collide(raytest.Ray, tri, raytest.Result);
|
|
if (hit) {
|
|
raytest.Result->SurfaceType = Get_Poly_Surface_Type (srtri);
|
|
}
|
|
|
|
if (raytest.Result->StartBad) return true;
|
|
|
|
}
|
|
return hit;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::cast_aabox_brute_force -- brute force aabox cast *
|
|
* *
|
|
* This function gets used only if the mesh doesn't have a CullTree *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 3/1/2001 NH : Created. *
|
|
*=============================================================================================*/
|
|
bool MeshGeometryClass::cast_aabox_brute_force(AABoxCollisionTestClass & boxtest)
|
|
{
|
|
/*
|
|
** Loop over each polygon
|
|
*/
|
|
TriClass tri;
|
|
int polyhit = -1;
|
|
|
|
const Vector3 * loc = Get_Vertex_Array();
|
|
const TriIndex * polyverts = Get_Polygon_Array();
|
|
#ifndef COMPUTE_NORMALS
|
|
const Vector4 * norms = Get_Plane_Array();
|
|
#endif
|
|
|
|
for (int srtri = 0; srtri < Get_Polygon_Count(); srtri++) {
|
|
|
|
tri.V[0] = &(loc[ polyverts[srtri][0] ]);
|
|
tri.V[1] = &(loc[ polyverts[srtri][1] ]);
|
|
tri.V[2] = &(loc[ polyverts[srtri][2] ]);
|
|
|
|
#ifdef COMPUTE_NORMALS
|
|
static Vector3 _normal;
|
|
tri.N = &_normal;
|
|
tri.Compute_Normal();
|
|
#else
|
|
tri.N = (Vector3 *)&(norms[srtri]);
|
|
#endif
|
|
|
|
if (CollisionMath::Collide(boxtest.Box, boxtest.Move, tri, boxtest.Result)) {
|
|
polyhit = srtri;
|
|
}
|
|
|
|
if (boxtest.Result->StartBad) {
|
|
return true;
|
|
}
|
|
}
|
|
if (polyhit != -1) {
|
|
boxtest.Result->SurfaceType = Get_Poly_Surface_Type(polyhit);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::cast_obbox_brute_force -- brute force obbox cast *
|
|
* *
|
|
* This function gets used only if the mesh doesn't have a CullTree *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 3/1/2001 NH : Created. *
|
|
*=============================================================================================*/
|
|
bool MeshGeometryClass::cast_obbox_brute_force(OBBoxCollisionTestClass & boxtest)
|
|
{
|
|
/*
|
|
** Loop over each polygon
|
|
*/
|
|
TriClass tri;
|
|
int polyhit = -1;
|
|
|
|
const Vector3 * loc = Get_Vertex_Array();
|
|
const TriIndex * polyverts = Get_Polygon_Array();
|
|
#ifndef COMPUTE_NORMALS
|
|
const Vector4 * norms = Get_Plane_Array();
|
|
#endif
|
|
|
|
for (int srtri = 0; srtri < Get_Polygon_Count(); srtri++) {
|
|
|
|
tri.V[0] = &(loc[ polyverts[srtri][0] ]);
|
|
tri.V[1] = &(loc[ polyverts[srtri][1] ]);
|
|
tri.V[2] = &(loc[ polyverts[srtri][2] ]);
|
|
|
|
#ifdef COMPUTE_NORMALS
|
|
static Vector3 _normal;
|
|
tri.N = &_normal;
|
|
tri.Compute_Normal();
|
|
#else
|
|
tri.N = (Vector3 *)&(norms[srtri]);
|
|
#endif
|
|
|
|
if (CollisionMath::Collide(boxtest.Box, boxtest.Move, tri, Vector3(0,0,0), boxtest.Result)) {
|
|
polyhit = srtri;
|
|
}
|
|
|
|
if (boxtest.Result->StartBad) {
|
|
return true;
|
|
}
|
|
}
|
|
if (polyhit != -1) {
|
|
boxtest.Result->SurfaceType = Get_Poly_Surface_Type(polyhit);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::Compute_Plane_Equations -- Recalculates the plane equations *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* This function should not normally be needed during run-time. *
|
|
* The array pointer must point to enough memory to hold a plane equation per polygon *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/9/2000 gth : Created. *
|
|
*=============================================================================================*/
|
|
void MeshGeometryClass::Compute_Plane_Equations(Vector4 * peq)
|
|
{
|
|
WWASSERT(peq!=NULL);
|
|
|
|
TriIndex * poly = Poly->Get_Array();
|
|
Vector3 * vert = Vertex->Get_Array();
|
|
|
|
for(int pidx = 0; pidx < PolyCount; pidx++)
|
|
{
|
|
Vector3 a,b,normal;
|
|
const Vector3 & p0= vert[poly[pidx][0]];
|
|
|
|
Vector3::Subtract(vert[poly[pidx][1]],p0,&a);
|
|
Vector3::Subtract(vert[poly[pidx][2]],p0,&b);
|
|
Vector3::Cross_Product(a,b,&normal);
|
|
normal.Normalize();
|
|
|
|
peq[pidx].Set( normal.X, normal.Y, normal.Z, -(Vector3::Dot_Product(p0,normal)) );
|
|
}
|
|
Set_Flag(DIRTY_PLANES,false);
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::Compute_Vertex_Normals -- recompute the vertex normals *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* This function should not normally be needed during run-time. *
|
|
* The array pointer must point to an array of Vector3's of size NumVerts *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/9/2000 gth : Created. *
|
|
*=============================================================================================*/
|
|
void MeshGeometryClass::Compute_Vertex_Normals(Vector3 * vnorm)
|
|
{
|
|
WWASSERT(vnorm != NULL);
|
|
if ((PolyCount == 0)|| (VertexCount == 0)) {
|
|
return;
|
|
}
|
|
|
|
const Vector4 * peq = Get_Plane_Array();
|
|
TriIndex * poly = Poly->Get_Array();
|
|
const uint32 * shadeIx = Get_Vertex_Shade_Index_Array(false);
|
|
|
|
// Two cases, with or without vertex shade indices. The vertex shade indices
|
|
// implicitly contain the smoothing groups information from the original mesh.
|
|
// In their abscesnce, the entire mesh is smoothed.
|
|
if (!shadeIx) {
|
|
|
|
VectorProcessorClass::Clear(vnorm, VertexCount);
|
|
|
|
for(int pidx = 0; pidx < PolyCount; pidx++) {
|
|
|
|
vnorm[poly[pidx].I].X += peq[pidx].X;
|
|
vnorm[poly[pidx].I].Y += peq[pidx].Y;
|
|
vnorm[poly[pidx].I].Z += peq[pidx].Z;
|
|
|
|
vnorm[poly[pidx].J].X += peq[pidx].X;
|
|
vnorm[poly[pidx].J].Y += peq[pidx].Y;
|
|
vnorm[poly[pidx].J].Z += peq[pidx].Z;
|
|
|
|
vnorm[poly[pidx].K].X += peq[pidx].X;
|
|
vnorm[poly[pidx].K].Y += peq[pidx].Y;
|
|
vnorm[poly[pidx].K].Z += peq[pidx].Z;
|
|
}
|
|
|
|
} else {
|
|
|
|
VectorProcessorClass::Clear (vnorm, VertexCount);
|
|
|
|
for (int pidx = 0; pidx < PolyCount; pidx++) {
|
|
|
|
vnorm[shadeIx[poly[pidx].I]].X += peq[pidx].X;
|
|
vnorm[shadeIx[poly[pidx].I]].Y += peq[pidx].Y;
|
|
vnorm[shadeIx[poly[pidx].I]].Z += peq[pidx].Z;
|
|
|
|
vnorm[shadeIx[poly[pidx].J]].X += peq[pidx].X;
|
|
vnorm[shadeIx[poly[pidx].J]].Y += peq[pidx].Y;
|
|
vnorm[shadeIx[poly[pidx].J]].Z += peq[pidx].Z;
|
|
|
|
vnorm[shadeIx[poly[pidx].K]].X += peq[pidx].X;
|
|
vnorm[shadeIx[poly[pidx].K]].Y += peq[pidx].Y;
|
|
vnorm[shadeIx[poly[pidx].K]].Z += peq[pidx].Z;
|
|
}
|
|
|
|
// normalize the "master" vertex normals and copy the smoothed ones
|
|
// (note: we always encounter the "master" ones first)
|
|
for (unsigned vidx = 0; vidx < (unsigned)VertexCount; vidx ++) {
|
|
if (shadeIx[vidx] == vidx) {
|
|
vnorm[vidx].Normalize();
|
|
} else {
|
|
vnorm[vidx] = vnorm[shadeIx[vidx]];
|
|
}
|
|
}
|
|
}
|
|
|
|
VectorProcessorClass::Normalize(vnorm, VertexCount);
|
|
|
|
Set_Flag(DIRTY_VNORMALS,false);
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::Compute_Bounds -- recomputes the bounding volumes *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* This function should not normally be needed during run-time. *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/9/2000 gth : Created. *
|
|
*=============================================================================================*/
|
|
void MeshGeometryClass::Compute_Bounds(Vector3 * verts)
|
|
{
|
|
BoundBoxMin.Set(0,0,0);
|
|
BoundBoxMax.Set(0,0,0);
|
|
BoundSphereCenter.Set(0,0,0);
|
|
BoundSphereRadius = 0.0;
|
|
|
|
if (VertexCount == 0) {
|
|
return;
|
|
}
|
|
|
|
// find bounding box minimum and maximum
|
|
if (verts == NULL) {
|
|
verts = Vertex->Get_Array();
|
|
}
|
|
VectorProcessorClass::MinMax(verts,BoundBoxMin,BoundBoxMax,VertexCount);
|
|
|
|
// calculate the bounding sphere
|
|
BoundSphereCenter = (BoundBoxMin + BoundBoxMax)/2.0f;
|
|
BoundSphereRadius = (float)(BoundBoxMax-BoundSphereCenter).Length2();
|
|
BoundSphereRadius = ((float)sqrt(BoundSphereRadius))*1.00001f;
|
|
Set_Flag(DIRTY_BOUNDS,false);
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::get_vert_normals -- get the vertex normal array *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 6/14/2001 gth : Created. *
|
|
*=============================================================================================*/
|
|
Vector3 * MeshGeometryClass::get_vert_normals(void)
|
|
{
|
|
#if (OPTIMIZE_VNORM_RAM)
|
|
_VNormArray.Uninitialised_Grow(VertexCount);
|
|
return &(_VNormArray[0]);
|
|
#else
|
|
WWASSERT(VertexNorm);
|
|
return VertexNorm->Get_Array();
|
|
#endif
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::Get_Vertex_Normal_Array -- validates and returns the vertex normal array *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 6/14/2001 gth : Created. *
|
|
*=============================================================================================*/
|
|
const Vector3 * MeshGeometryClass::Get_Vertex_Normal_Array(void)
|
|
{
|
|
#if (OPTIMIZE_VNORM_RAM)
|
|
Compute_Vertex_Normals(get_vert_normals());
|
|
return get_vert_normals();
|
|
#else
|
|
if (Get_Flag(DIRTY_VNORMALS)) {
|
|
Compute_Vertex_Normals(get_vert_normals());
|
|
}
|
|
return get_vert_normals();
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::get_planes -- get the plane array memory (internal) *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 6/14/2001 gth : Created. *
|
|
*=============================================================================================*/
|
|
Vector4 * MeshGeometryClass::get_planes(bool create)
|
|
{
|
|
#if (OPTIMIZE_PLANEEQ_RAM)
|
|
_PlaneEQArray.Uninitialised_Grow(PolyCount);
|
|
return &(_PlaneEQArray[0]);
|
|
#else
|
|
if (create && !PlaneEq) {
|
|
PlaneEq = NEW_REF(ShareBufferClass<Vector4>,(PolyCount));
|
|
}
|
|
if (PlaneEq) {
|
|
return PlaneEq->Get_Array();
|
|
}
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::Get_Plane_Array -- validates and returns the array of plane equations *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 6/14/2001 gth : Created. *
|
|
*=============================================================================================*/
|
|
const Vector4 * MeshGeometryClass::Get_Plane_Array(bool create)
|
|
{
|
|
#if (OPTIMIZE_PLANEEQ_RAM)
|
|
Vector4 * planes = get_planes(create);
|
|
Compute_Plane_Equations(planes);
|
|
return planes;
|
|
#else
|
|
Vector4 * planes = get_planes(create);
|
|
if (planes && Get_Flag(DIRTY_PLANES)) {
|
|
Compute_Plane_Equations(planes);
|
|
}
|
|
return planes;
|
|
#endif
|
|
}
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::Compute_Plane -- compute the plane equation of a single poly *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 6/14/2001 gth : Created. *
|
|
*=============================================================================================*/
|
|
void MeshGeometryClass::Compute_Plane(int pidx,PlaneClass * set_plane) const
|
|
{
|
|
WWASSERT(pidx >= 0);
|
|
WWASSERT(pidx < PolyCount);
|
|
TriIndex & poly = Poly->Get_Array()[pidx];
|
|
Vector3 * verts = Vertex->Get_Array();
|
|
|
|
set_plane->Set(verts[poly.I],verts[poly.J],verts[poly.K]);
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::Generate_Culling_Tree -- Generate an AABTree for this mesh *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* This function should not normally be needed during run-time. *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/9/2000 gth : Created. *
|
|
*=============================================================================================*/
|
|
void MeshGeometryClass::Generate_Culling_Tree(void)
|
|
{
|
|
WWMEMLOG(MEM_CULLINGDATA);
|
|
{
|
|
AABTreeBuilderClass builder;
|
|
builder.Build_AABTree(PolyCount,Poly->Get_Array(),VertexCount,Vertex->Get_Array());
|
|
|
|
CullTree = NEW_REF(AABTreeClass,(&builder));
|
|
CullTree->Set_Mesh(this);
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::Load_W3D -- Load a mesh from a w3d file *
|
|
* *
|
|
* This function will extract just the geometry information from a W3D mesh. It must *
|
|
* be completely replaced by any derived classes that want to handle all of the chunks. *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/9/2000 gth : Created. *
|
|
*=============================================================================================*/
|
|
WW3DErrorType MeshGeometryClass::Load_W3D(ChunkLoadClass & cload)
|
|
{
|
|
/*
|
|
** This function will initialize this MeshGeometryClass from the contents of a W3D file.
|
|
** Note that derived classes need to completely replace this function; only re-using the individual
|
|
** chunk handling functions.
|
|
*/
|
|
|
|
/*
|
|
** Open the first chunk, it should be the mesh header
|
|
*/
|
|
cload.Open_Chunk();
|
|
|
|
if (cload.Cur_Chunk_ID() != W3D_CHUNK_MESH_HEADER3) {
|
|
WWDEBUG_SAY(("Old format mesh mesh, no longer supported.\n"));
|
|
goto Error;
|
|
}
|
|
|
|
W3dMeshHeader3Struct header;
|
|
if (cload.Read(&header,sizeof(W3dMeshHeader3Struct)) != sizeof(W3dMeshHeader3Struct)) {
|
|
goto Error;
|
|
}
|
|
cload.Close_Chunk();
|
|
|
|
/*
|
|
** Process the header
|
|
*/
|
|
char * tmpname;
|
|
int namelen;
|
|
|
|
Reset_Geometry(header.NumTris,header.NumVertices);
|
|
|
|
namelen = strlen(header.ContainerName);
|
|
namelen += strlen(header.MeshName);
|
|
namelen += 2;
|
|
W3dAttributes = header.Attributes;
|
|
SortLevel = header.SortLevel;
|
|
tmpname = new char[namelen];
|
|
memset(tmpname,0,namelen);
|
|
|
|
if (strlen(header.ContainerName) > 0) {
|
|
strcpy(tmpname,header.ContainerName);
|
|
strcat(tmpname,".");
|
|
}
|
|
strcat(tmpname,header.MeshName);
|
|
|
|
Set_Name(tmpname);
|
|
|
|
delete[] tmpname;
|
|
tmpname = NULL;
|
|
|
|
/*
|
|
** Set Bounding Info
|
|
*/
|
|
BoundBoxMin.Set(header.Min.X,header.Min.Y,header.Min.Z);
|
|
BoundBoxMax.Set(header.Max.X,header.Max.Y,header.Max.Z);
|
|
|
|
BoundSphereCenter.Set(header.SphCenter.X,header.SphCenter.Y,header.SphCenter.Z);
|
|
BoundSphereRadius = header.SphRadius;
|
|
|
|
/*
|
|
** Flags
|
|
*/
|
|
if (header.Version >= W3D_MAKE_VERSION(4,1)) {
|
|
int geometry_type = header.Attributes & W3D_MESH_FLAG_GEOMETRY_TYPE_MASK;
|
|
switch (geometry_type)
|
|
{
|
|
case W3D_MESH_FLAG_GEOMETRY_TYPE_NORMAL:
|
|
break;
|
|
case W3D_MESH_FLAG_GEOMETRY_TYPE_CAMERA_ALIGNED:
|
|
Set_Flag(ALIGNED,true);
|
|
break;
|
|
case W3D_MESH_FLAG_GEOMETRY_TYPE_CAMERA_ORIENTED:
|
|
Set_Flag(ORIENTED,true);
|
|
break;
|
|
case W3D_MESH_FLAG_GEOMETRY_TYPE_SKIN:
|
|
Set_Flag(SKIN,true);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (header.Attributes & W3D_MESH_FLAG_TWO_SIDED) {
|
|
Set_Flag(TWO_SIDED,true);
|
|
}
|
|
|
|
if (header.Attributes & W3D_MESH_FLAG_CAST_SHADOW) {
|
|
Set_Flag(CAST_SHADOW,true);
|
|
}
|
|
|
|
read_chunks(cload);
|
|
|
|
/*
|
|
** If this is a pre-3.0 mesh and it has vertex influences,
|
|
** fixup the bone indices to account for the new root node
|
|
*/
|
|
if ((header.Version < W3D_MAKE_VERSION(3,0)) && (Get_Flag(SKIN))) {
|
|
|
|
uint16 * links = get_bone_links();
|
|
WWASSERT(links);
|
|
|
|
for (int bi = 0; bi < Get_Vertex_Count(); bi++) {
|
|
links[bi] += 1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
** If this mesh is collideable and no AABTree was in the file, generate one now
|
|
*/
|
|
if ( (((W3dAttributes & W3D_MESH_FLAG_COLLISION_TYPE_MASK) >> W3D_MESH_FLAG_COLLISION_TYPE_SHIFT) != 0) &&
|
|
(CullTree == NULL))
|
|
{
|
|
Generate_Culling_Tree();
|
|
}
|
|
|
|
return WW3D_ERROR_OK;
|
|
|
|
Error:
|
|
|
|
return WW3D_ERROR_LOAD_FAILED;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::read_chunks -- read w3d chunks *
|
|
* *
|
|
* Again, derived classes must replace this function with one that handles all of their *
|
|
* chunks in addition to calling to this class for the individual chunks that it handles. *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/9/2000 gth : Created. *
|
|
*=============================================================================================*/
|
|
WW3DErrorType MeshGeometryClass::read_chunks(ChunkLoadClass & cload)
|
|
{
|
|
/*
|
|
** Read in the chunk header
|
|
** If there are no more chunks within the mesh chunk,
|
|
** we are done.
|
|
*/
|
|
while (cload.Open_Chunk()) {
|
|
|
|
/*
|
|
** Process the chunk
|
|
*/
|
|
WW3DErrorType error = WW3D_ERROR_OK;
|
|
|
|
switch (cload.Cur_Chunk_ID()) {
|
|
|
|
case W3D_CHUNK_VERTICES:
|
|
error = read_vertices(cload);
|
|
break;
|
|
|
|
case W3D_CHUNK_SURRENDER_NORMALS:
|
|
case W3D_CHUNK_VERTEX_NORMALS:
|
|
error = read_vertex_normals(cload);
|
|
break;
|
|
|
|
case W3D_CHUNK_TRIANGLES:
|
|
error = read_triangles(cload);
|
|
break;
|
|
|
|
case W3D_CHUNK_MESH_USER_TEXT:
|
|
error = read_user_text(cload);
|
|
break;
|
|
|
|
case W3D_CHUNK_VERTEX_INFLUENCES:
|
|
error = read_vertex_influences(cload);
|
|
break;
|
|
|
|
case W3D_CHUNK_VERTEX_SHADE_INDICES:
|
|
error = read_vertex_shade_indices(cload);
|
|
break;
|
|
|
|
case W3D_CHUNK_AABTREE:
|
|
read_aabtree(cload);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
|
|
}
|
|
|
|
cload.Close_Chunk();
|
|
|
|
if (error != WW3D_ERROR_OK) {
|
|
return error;
|
|
}
|
|
}
|
|
|
|
return WW3D_ERROR_OK;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::read_vertices -- read the vertex chunk from a W3D file *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/9/2000 gth : Created. *
|
|
*=============================================================================================*/
|
|
WW3DErrorType MeshGeometryClass::read_vertices(ChunkLoadClass & cload)
|
|
{
|
|
W3dVectorStruct vert;
|
|
Vector3 * loc = Vertex->Get_Array();
|
|
assert(loc);
|
|
|
|
for (int i=0; i<Get_Vertex_Count(); i++) {
|
|
|
|
if (cload.Read(&vert,sizeof(W3dVectorStruct)) != sizeof(W3dVectorStruct)) {
|
|
return WW3D_ERROR_LOAD_FAILED;
|
|
}
|
|
|
|
loc[i].X = vert.X;
|
|
loc[i].Y = vert.Y;
|
|
loc[i].Z = vert.Z;
|
|
}
|
|
|
|
return WW3D_ERROR_OK;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::read_vertex_normals -- read the vertex normals chunk from a w3d file *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/9/2000 gth : Created. *
|
|
*=============================================================================================*/
|
|
WW3DErrorType MeshGeometryClass::read_vertex_normals(ChunkLoadClass & cload)
|
|
{
|
|
W3dVectorStruct norm;
|
|
Vector3 * mdlnorms = get_vert_normals();
|
|
WWASSERT(mdlnorms);
|
|
|
|
for (int i=0; i<VertexCount; i++) {
|
|
if (cload.Read(&norm,sizeof(W3dVectorStruct)) != sizeof(W3dVectorStruct)) {
|
|
return WW3D_ERROR_LOAD_FAILED;
|
|
}
|
|
|
|
mdlnorms[i].Set(norm.X,norm.Y,norm.Z);
|
|
}
|
|
|
|
return WW3D_ERROR_OK;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::read_triangles -- read the triangles chunk from a w3d file *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/9/2000 gth : Created. *
|
|
*=============================================================================================*/
|
|
WW3DErrorType MeshGeometryClass::read_triangles(ChunkLoadClass & cload)
|
|
{
|
|
W3dTriStruct tri;
|
|
|
|
// cache pointers to various arrays in the surrender mesh
|
|
TriIndex * vi = get_polys();
|
|
Set_Flag(DIRTY_PLANES,false);
|
|
Vector4 * peq = get_planes();
|
|
uint8 * surface_types = Get_Poly_Surface_Type_Array();
|
|
|
|
// read in each polygon one by one
|
|
for (int i=0; i<Get_Polygon_Count(); i++) {
|
|
|
|
if (cload.Read(&tri,sizeof(W3dTriStruct)) != sizeof(W3dTriStruct)) {
|
|
return WW3D_ERROR_LOAD_FAILED;
|
|
}
|
|
|
|
// set the vertex indices
|
|
vi[i].I = tri.Vindex[0];
|
|
vi[i].J = tri.Vindex[1];
|
|
vi[i].K = tri.Vindex[2];
|
|
|
|
// set the normal
|
|
peq[i].X = tri.Normal.X;
|
|
peq[i].Y = tri.Normal.Y;
|
|
peq[i].Z = tri.Normal.Z;
|
|
peq[i].W = -tri.Dist;
|
|
|
|
// set the surface type
|
|
WWASSERT(tri.Attributes < 256);
|
|
surface_types[i] = (uint8)(tri.Attributes);
|
|
}
|
|
|
|
return WW3D_ERROR_OK;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::read_user_text -- read the user text chunk from a w3d file *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/9/2000 gth : Created. *
|
|
*=============================================================================================*/
|
|
WW3DErrorType MeshGeometryClass::read_user_text(ChunkLoadClass & cload)
|
|
{
|
|
unsigned int textlen = cload.Cur_Chunk_Length();
|
|
|
|
/*
|
|
** This shouldn't happen but if there are more than one
|
|
** USER_TEXT chunks in the mesh file, store only the first
|
|
** one. I am assuming that if the UserText buffer is not
|
|
** NULL, then a previous user text chunk has been read in...
|
|
*/
|
|
if (UserText != NULL) {
|
|
return WW3D_ERROR_OK;
|
|
}
|
|
|
|
/*
|
|
** Allocate the buffer and read in the text
|
|
*/
|
|
UserText = NEW_REF(ShareBufferClass<char>,(textlen));
|
|
|
|
if (cload.Read(UserText->Get_Array(),textlen) != textlen) {
|
|
return WW3D_ERROR_LOAD_FAILED;
|
|
}
|
|
|
|
return WW3D_ERROR_OK;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::read_vertex_influences -- read the vertex influences chunk from a w3d fi *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/9/2000 gth : Created. *
|
|
*=============================================================================================*/
|
|
WW3DErrorType MeshGeometryClass::read_vertex_influences(ChunkLoadClass & cload)
|
|
{
|
|
W3dVertInfStruct vinf;
|
|
uint16 * links = get_bone_links(true);
|
|
WWASSERT(links);
|
|
|
|
for (int i=0; i<Get_Vertex_Count(); i++) {
|
|
|
|
if (cload.Read(&vinf,sizeof(W3dVertInfStruct)) != sizeof(W3dVertInfStruct)) {
|
|
return WW3D_ERROR_LOAD_FAILED;
|
|
}
|
|
links[i] = vinf.BoneIdx;
|
|
}
|
|
Set_Flag(SKIN,true);
|
|
|
|
return WW3D_ERROR_OK;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::read_vertex_shade_indices -- read the vertex shade indices chunk *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/9/2000 gth : Created. *
|
|
*=============================================================================================*/
|
|
WW3DErrorType MeshGeometryClass::read_vertex_shade_indices(ChunkLoadClass & cload)
|
|
{
|
|
uint32 * shade_index = get_shade_indices(true);
|
|
uint32 si;
|
|
|
|
for (int i=0; i<Get_Vertex_Count(); i++) {
|
|
if (cload.Read(&si,sizeof(uint32)) != sizeof(uint32)) {
|
|
return WW3D_ERROR_LOAD_FAILED;
|
|
}
|
|
shade_index[i] = si;
|
|
}
|
|
return WW3D_ERROR_OK;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MeshGeometryClass::read_aabtree -- read the AABTree chunk from a w3d file *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/9/2000 gth : Created. *
|
|
*=============================================================================================*/
|
|
WW3DErrorType MeshGeometryClass::read_aabtree(ChunkLoadClass &cload)
|
|
{
|
|
REF_PTR_RELEASE(CullTree);
|
|
CullTree = NEW_REF(AABTreeClass,());
|
|
CullTree->Load_W3D(cload);
|
|
CullTree->Set_Mesh(this);
|
|
return (WW3D_ERROR_OK);
|
|
}
|
|
|
|
void MeshGeometryClass::Scale(const Vector3 &sc)
|
|
{
|
|
WWASSERT(Vertex);
|
|
Vector3 * vert = Vertex->Get_Array();
|
|
|
|
for (int i=0;i<VertexCount; i++) {
|
|
vert[i].X *= sc.X;
|
|
vert[i].Y *= sc.Y;
|
|
vert[i].Z *= sc.Z;
|
|
}
|
|
|
|
BoundBoxMin.Scale(sc);
|
|
BoundBoxMax.Scale(sc);
|
|
BoundSphereCenter.Scale(sc);
|
|
|
|
float max;
|
|
max = (sc.X > sc.Y) ? sc.X : sc.Y;
|
|
max = (max > sc.Z) ? max : sc.Z;
|
|
BoundSphereRadius *= max;
|
|
|
|
// If scaling uniformly normals are OK:
|
|
if (sc.X != sc.Y || sc.Y != sc.Z) {
|
|
Set_Flag(DIRTY_VNORMALS,true);
|
|
}
|
|
// pnormals are plane equations...
|
|
Set_Flag(DIRTY_PLANES,true);
|
|
} |