📄 edittrimesh.cpp
字号:
/////////////////////////////////////////////////////////////////////////////
//
// 3D Math Primer for Games and Graphics Development
//
// EditTriMesh.cpp - Implementation of class EditTriMesh
//
// Visit gamemath.com for the latest version of this file.
//
// For more details, see Chapter 13
//
/////////////////////////////////////////////////////////////////////////////
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "MathUtil.h"
#include "EditTriMesh.h"
#include "CommonStuff.h"
#include "Matrix4x3.h"
#include "AABB3.h"
/////////////////////////////////////////////////////////////////////////////
//
// Local utility stuff
//
/////////////////////////////////////////////////////////////////////////////
//---------------------------------------------------------------------------
// vertexCompareByMark
//
// Compare two vertices by their mark field. Used to sort using qsort.
static int vertexCompareByMark(const void *va, const void *vb) {
// Cast pointers
const EditTriMesh::Vertex *a = (const EditTriMesh::Vertex *)va;
const EditTriMesh::Vertex *b = (const EditTriMesh::Vertex *)vb;
// Return comparison result as per Qsort conventions:
//
// <0 a goes "before" b
// >0 a goes "after" b
// 0 doesn't matter
//
// We want the lower mark to come first
return a->mark - b->mark;
}
//---------------------------------------------------------------------------
// triCompareByMaterial
//
// Compare two triangles by their material field. Used to sort using qsort.
static int triCompareByMaterial(const void *va, const void *vb) {
// Cast pointers
const EditTriMesh::Tri *a = (const EditTriMesh::Tri *)va;
const EditTriMesh::Tri *b = (const EditTriMesh::Tri *)vb;
// Return comparison result as per Qsort conventions:
//
// <0 a goes "before" b
// >0 a goes "after" b
// 0 doesn't matter
//
// We want the lower material to come first
if (a->material < b->material) return -1;
if (a->material > b->material) return +1;
// Same material - use "mark" field, which contained the
// original index, so we'll have a stable sort
return a->mark - b->mark;
}
//---------------------------------------------------------------------------
// skipLine
//
// Skip a line of text from a file. Returns false on failure (EOF or error).
static bool skipLine(FILE *f) {
for (;;) {
int c = fgetc(f);
if (c < 0) {
return false;
}
if (c == '\n') {
return true;
}
}
}
/////////////////////////////////////////////////////////////////////////////
//
// EditTriMesh helper class members
//
/////////////////////////////////////////////////////////////////////////////
//---------------------------------------------------------------------------
// EditTriMesh::Vertex::setDefaults
//
// Reset vertex to a default state
void EditTriMesh::Vertex::setDefaults() {
// Reset everything
memset(this, 0, sizeof(this));
}
//---------------------------------------------------------------------------
// EditTriMesh::Tri::setDefaults
//
// Reset triangle to a default state
void EditTriMesh::Tri::setDefaults() {
// Reset everything
memset(this, 0, sizeof(this));
}
//---------------------------------------------------------------------------
// EditTriMesh::Tri::isDegenerate
//
// Return true if we are degenerate (any two vertex indices are the same)
bool EditTriMesh::Tri::isDegenerate() const {
return
(v[0].index == v[1].index) ||
(v[1].index == v[2].index) ||
(v[0].index == v[2].index);
}
//---------------------------------------------------------------------------
// EditTriMesh::Tri::findVertex
//
// Check if we use the vertex (by index into the master vertex list). Return
// the first face vertex index (0..2) if we reference it, or -1 otherwise
int EditTriMesh::Tri::findVertex(int vertexIndex) const {
// Search vertices. Let's unroll the loop here...
if (v[0].index == vertexIndex) return 0;
if (v[1].index == vertexIndex) return 1;
if (v[2].index == vertexIndex) return 2;
// Not found.
return -1;
}
//---------------------------------------------------------------------------
// EditTriMesh::Material::setDefaults
//
// Reset material to a default state
void EditTriMesh::Material::setDefaults() {
// Reset everything
memset(this, 0, sizeof(this));
}
//---------------------------------------------------------------------------
// EditTriMesh::Part::setDefaults
//
// Reset part to a default state
void EditTriMesh::Part::setDefaults() {
// Reset everything
memset(this, 0, sizeof(this));
}
//---------------------------------------------------------------------------
// EditTriMesh::OptimizationParameters::setDefaults
//
// Set options to reasonable default values. (*I* think they are reasonable
// you may not...)
void EditTriMesh::OptimizationParameters::setDefaults() {
// Weld vertices within 1/8 of an inch. (We use 1 unit = 1ft)
coincidentVertexTolerance = 1.0f / 12.0f / 8.0f;
// Weld vertices across edge if the edge is 80 degrees or more.
// If more (for example, the edges of a cube) then let's keep
// the edges detached
setEdgeAngleToleranceInDegrees(80.0f);
}
//---------------------------------------------------------------------------
// EditTriMesh::OptimizationParameters::setEdgeAngleToleranceInDegrees
//
// Set tolerance angle value used to determine if two vertices can be
// welded. If they share a very "sharp" edge we may not wish to weld them,
// since it destroys the lighting discontinuity that should be present at
// this geometric discontinuity.
//
// Pass in a really large number (> 180 degrees) to effectively
// weld all evrtices, regardless of angle tolerance
void EditTriMesh::OptimizationParameters::setEdgeAngleToleranceInDegrees(float degrees) {
// Check for a really big angle
if (degrees >= 180.0f) {
// Slam cosine to very small number
cosOfEdgeAngleTolerance = -999.0f;
} else {
// Compute the cosine
cosOfEdgeAngleTolerance = cos(degToRad(degrees));
}
}
/////////////////////////////////////////////////////////////////////////////
//
// EditTriMesh members - Standard class object maintenance
//
/////////////////////////////////////////////////////////////////////////////
//---------------------------------------------------------------------------
// EditTriMesh::EditTriMesh
//
// Default constructor - reset object to default, empty state.
EditTriMesh::EditTriMesh() {
construct();
}
//---------------------------------------------------------------------------
// EditTriMesh::EditTriMesh
//
// Copy constructor - make a copy of a mesh.
EditTriMesh::EditTriMesh(const EditTriMesh &x) {
// Initialize to empty state
construct();
// Use operator= to do the real work and make a copy
*this = x;
}
//---------------------------------------------------------------------------
// EditTriMesh::construct
//
// Do the real work of object construction
void EditTriMesh::construct() {
// Reset the lists
vAlloc = 0;
vCount = 0;
vList = NULL;
tAlloc = 0;
tCount = 0;
tList = NULL;
mCount = 0;
mList = NULL;
pCount = 0;
pList = NULL;
}
//---------------------------------------------------------------------------
// EditTriMesh::~EditTriMesh
//
// Destructor - make sure resources are freed
EditTriMesh::~EditTriMesh() {
empty();
}
//---------------------------------------------------------------------------
// EditTriMesh::operator=
//
// Assignment operator - make a copy of the mesh
EditTriMesh &EditTriMesh::operator=(const EditTriMesh &src) {
int i;
// Start by freeing up what we already have
empty();
// Copy materials and parts first. We copy these stupidly,
// since the lists probably won't be very big
setMaterialCount(src.materialCount());
for (i = 0 ; i < materialCount() ; ++i) {
material(i) = src.material(i);
}
setPartCount(src.partCount());
for (i = 0 ; i < partCount() ; ++i) {
part(i) = src.part(i);
}
// Make sure vertex list isn't empty
if (src.vertexCount() > 0) {
// Compute size in bytes
int bytes = src.vertexCount() * sizeof(*vList);
// Allocate it. We don't use setVertexCount(), since
// that initializes the list, which we don't need
vList = (Vertex *)::malloc(bytes);
if (vList == NULL) {
ABORT("Out of memory");
}
vCount = src.vertexCount();
vAlloc = vCount;
// Blast copy it
memcpy(vList, src.vList, bytes);
}
// Make sure face list isn't empty
if (src.triCount() > 0) {
// Compute size in bytes
int bytes = src.triCount() * sizeof(*tList);
// Allocate it. We don't use setVertexCount(), since
// that initializes the list, which we don't need
tList = (Tri *)::malloc(bytes);
if (tList == NULL) {
ABORT("Out of memory");
}
tCount = src.triCount();
tAlloc = tCount;
// Blast copy it
memcpy(tList, src.tList, bytes);
}
// Return reference to l-value, as per C convention
return *this;
}
/////////////////////////////////////////////////////////////////////////////
//
// EditTriMesh members - Accessors to the mesh data
//
// All of these functions act like an array operator, returning a reference
// to the element. They provide array bounds checking, and can therefore
// catch a number of common bugs. We have const and non-const versions,
// so you can only modify a non-const mesh.
//
/////////////////////////////////////////////////////////////////////////////
EditTriMesh::Vertex &EditTriMesh::vertex(int vertexIndex) {
assert(vertexIndex >= 0);
assert(vertexIndex < vCount);
return vList[vertexIndex];
}
const EditTriMesh::Vertex &EditTriMesh::vertex(int vertexIndex) const {
assert(vertexIndex >= 0);
assert(vertexIndex < vCount);
return vList[vertexIndex];
}
EditTriMesh::Tri &EditTriMesh::tri(int triIndex) {
assert(triIndex >= 0);
assert(triIndex < tCount);
return tList[triIndex];
}
const EditTriMesh::Tri &EditTriMesh::tri(int triIndex) const {
assert(triIndex >= 0);
assert(triIndex < tCount);
return tList[triIndex];
}
EditTriMesh::Material &EditTriMesh::material(int materialIndex) {
assert(materialIndex >= 0);
assert(materialIndex < mCount);
return mList[materialIndex];
}
const EditTriMesh::Material &EditTriMesh::material(int materialIndex) const {
assert(materialIndex >= 0);
assert(materialIndex < mCount);
return mList[materialIndex];
}
EditTriMesh::Part &EditTriMesh::part(int partIndex) {
assert(partIndex >= 0);
assert(partIndex < pCount);
return pList[partIndex];
}
const EditTriMesh::Part &EditTriMesh::part(int partIndex) const {
assert(partIndex >= 0);
assert(partIndex < pCount);
return pList[partIndex];
}
/////////////////////////////////////////////////////////////////////////////
//
// EditTriMesh members - Basic mesh operations
//
// All of these functions act like an array operator, returning a reference
// to the element. They provide array bounds checking, and can therefore
// catch a number of common bugs. We have const and non-const versions,
// so you can only modify a non-const mesh.
//
/////////////////////////////////////////////////////////////////////////////
//---------------------------------------------------------------------------
// EditTriMesh::empty
//
// Reset the mesh to empty state
void EditTriMesh::empty() {
// Free vertices
if (vList != NULL) {
::free(vList);
vList = NULL;
}
vAlloc = 0;
vCount = 0;
// Free triangles
if (tList != NULL) {
::free(tList);
tList = NULL;
}
tAlloc = 0;
tCount = 0;
// Free materials
if (mList != NULL) {
::free(mList);
mList = NULL;
}
mCount = 0;
// Free parts
if (pList != NULL) {
::free(pList);
pList = NULL;
}
pCount = 0;
}
//---------------------------------------------------------------------------
// EditTriMesh::setVertexCount
//
// Set the vertex count. If the list is grown, the new vertices at the end
// are initialized with default values. If the list is shrunk, any invalid
// faces are deleted.
void EditTriMesh::setVertexCount(int vc) {
assert(vc >= 0);
// Make sure we had enough allocated coming in
assert(vCount <= vAlloc);
// Check if growing or shrinking the list
if (vc > vCount) {
// Check if we need to allocate more
if (vc > vAlloc) {
// We need to grow the list. Figure out the
// new count. We don't want to constantly be
// allocating memory every time a single vertex
// is added, but yet we don't want to allocate
// too much memory and be wasteful. The system
// shown below seems to be a good compromise.
vAlloc = vc * 4 / 3 + 10;
vList = (Vertex *)::realloc(vList, vAlloc * sizeof(*vList));
// Check for out of memory. You may need more
// robust error handling...
if (vList == NULL) {
ABORT("Out of memory");
}
}
// Initilaize the new vertices
while (vCount < vc) {
vList[vCount].setDefaults();
++vCount;
}
} else if (vc < vCount) {
// Shrinking the list. Go through
// and mark invalid faces for deletion
for (int i = 0 ; i < triCount() ; ++i) {
Tri *t = &tri(i);
if (
(t->v[0].index >= vc) ||
(t->v[1].index >= vc) ||
(t->v[2].index >= vc)
) {
// Mark it for deletion
t->mark = 1;
} else {
// It's OK
t->mark = 0;
}
}
// Delete the marked triangles
deleteMarkedTris(1);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -