⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 edittrimesh.cpp

📁 3D数学基础:图形与游戏开发书籍源码,里面有很多实用的代码,对做3D的同志很有意义
💻 CPP
📖 第 1 页 / 共 4 页
字号:
/////////////////////////////////////////////////////////////////////////////
//
// 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 + -