Added some tangent generation code to cmodfix. Modified makefile to default to building all tools.

This commit is contained in:
Chris Laurel 2004-06-28 17:46:59 +00:00
parent 35c0798aac
commit 60153e751c
2 changed files with 346 additions and 12 deletions

View file

@ -65,10 +65,13 @@ struct Face
};
class VertexComparison : public std::binary_function<const Vertex&, const Vertex&, bool>
typedef public std::binary_function<const Vertex&, const Vertex&, bool> VertexComparator;
class FullComparator : public VertexComparator
{
public:
VertexComparison(int _vertexSize) :
FullComparator(int _vertexSize) :
vertexSize(_vertexSize)
{
}
@ -93,10 +96,10 @@ private:
};
class VertexPointComparison : public std::binary_function<const Vertex&, const Vertex&, bool>
class PointComparator : public VertexComparator
{
public:
VertexPointComparison()
PointComparator()
{
}
@ -129,6 +132,74 @@ private:
};
class PointTexCoordComparator : public VertexComparator
{
public:
PointTexCoordComparator(uint32 _posOffset,
uint32 _texCoordOffset,
bool _wrap) :
posOffset(_posOffset),
texCoordOffset(_texCoordOffset),
wrap(_wrap)
{
}
bool operator()(const Vertex& a, const Vertex& b) const
{
const char* adata = reinterpret_cast<const char*>(a.attributes);
const char* bdata = reinterpret_cast<const char*>(b.attributes);
const Point3f* p0 = reinterpret_cast<const Point3f*>(adata + posOffset);
const Point3f* p1 = reinterpret_cast<const Point3f*>(bdata + posOffset);
const Point2f* tc0 = reinterpret_cast<const Point2f*>(adata + posOffset);
const Point2f* tc1 = reinterpret_cast<const Point2f*>(bdata + posOffset);
if (p0->x < p1->x)
{
return true;
}
else if (p0->x > p1->x)
{
return false;
}
else
{
if (p0->y < p1->y)
{
return true;
}
else if (p0->y > p1->y)
{
return false;
}
else
{
if (p0->z < p1->z)
{
return true;
}
else if (p0->z > p1->z)
{
return false;
}
else
{
if (tc0->x < tc1->x)
return true;
else if (tc0->x > tc1->x)
return false;
else
return tc0->y < tc1->y;
}
}
}
}
private:
uint32 posOffset;
uint32 texCoordOffset;
bool wrap;
};
bool equal(const Vertex& a, const Vertex& b, uint32 vertexSize)
{
const char* s0 = reinterpret_cast<const char*>(a.attributes);
@ -174,7 +245,7 @@ bool uniquifyVertices(Mesh& mesh)
}
// Sort the vertices so that identical ones will be ordered consecutively
sort(vertices.begin(), vertices.end(), VertexComparison(desc.stride));
sort(vertices.begin(), vertices.end(), FullComparator(desc.stride));
// Count the number of unique vertices
uint32 uniqueVertexCount = 0;
@ -229,6 +300,18 @@ getVertex(const void* vertexData,
}
Point2f
getTexCoord(const void* vertexData,
int texCoordOffset,
uint32 stride,
uint32 index)
{
const float* fdata = reinterpret_cast<const float*>(reinterpret_cast<const char*>(vertexData) + stride * index + texCoordOffset);
return Point2f(fdata[0], fdata[1]);
}
Vec3f
averageNormals(const vector<Face>& faces,
uint32 thisFace,
@ -325,10 +408,11 @@ augmentVertexDescription(Mesh::VertexDescription& desc,
}
void
weldVerticesForNormalGeneration(vector<Face>& faces,
const void* vertexData,
const Mesh::VertexDescription& desc)
template <typename T> void
mergeVertices(vector<Face>& faces,
const void* vertexData,
const Mesh::VertexDescription& desc,
T& comparator)
{
// Don't do anything if we're given no data
if (faces.size() == 0)
@ -359,15 +443,19 @@ weldVerticesForNormalGeneration(vector<Face>& faces,
}
// Sort the vertices so that identical ones will be ordered consecutively
sort(vertices.begin(), vertices.end(), VertexPointComparison());
sort(vertices.begin(), vertices.end(), comparator);
// Build the vertex merge map
vector<uint32> mergeMap(nVertices);
uint32 lastUnique = 0;
for (uint32 i = 0; i < nVertices; i++)
{
if (i == 0 || !equalPoint(vertices[i - 1], vertices[i]))
if (i == 0 ||
comparator.operator()(vertices[i - 1], vertices[i]) ||
comparator.operator()(vertices[i], vertices[i - 1]))
{
lastUnique = i;
}
mergeMap[vertices[i].index] = vertices[lastUnique].index;
}
@ -531,7 +619,7 @@ generateNormals(Mesh& mesh,
// as the attribute indices.
if (weld)
{
weldVerticesForNormalGeneration(faces, vertexData, desc);
mergeVertices(faces, vertexData, desc, PointComparator());
}
else
{
@ -668,6 +756,250 @@ generateNormals(Mesh& mesh,
}
Mesh*
generateTangents(Mesh& mesh,
bool weld)
{
uint32 nVertices = mesh.getVertexCount();
// In order to generate tangents, we require positions, normals, and
// 2D texture coordinates in the vertex description.
const Mesh::VertexDescription& desc = mesh.getVertexDescription();
if (desc.getAttribute(Mesh::Position) == NULL)
{
cerr << "Bad vertex format--no position!\n";
return NULL;
}
if (desc.getAttribute(Mesh::Position)->format != Mesh::Float3)
{
cerr << "Vertex position must be a float3\n";
return NULL;
}
if (desc.getAttribute(Mesh::Normal) == NULL)
{
cerr << "Normals must be present in mesh to generate tangents\n";
return NULL;
}
if (desc.getAttribute(Mesh::Normal)->format != Mesh::Float3)
{
cerr << "Vertex normal must be a float3\n";
return NULL;
}
if (desc.getAttribute(Mesh::Texture0) == NULL)
{
cerr << "Texture coordinates must be present in mesh to generate tangents\n";
return NULL;
}
if (desc.getAttribute(Mesh::Texture0)->format != Mesh::Float2)
{
cerr << "Texture coordinate must be a float2\n";
return NULL;
}
// Count the number of faces in the mesh.
// (All geometry should already converted to triangle lists)
uint32 i;
uint32 nFaces = 0;
for (i = 0; mesh.getGroup(i) != NULL; i++)
{
const Mesh::PrimitiveGroup* group = mesh.getGroup(i);
if (group->prim == Mesh::TriList)
{
assert(group->nIndices % 3 == 0);
nFaces += group->nIndices / 3;
}
else
{
cerr << "Mesh should contain just triangle lists\n";
return NULL;
}
}
// Build the array of faces; this may require decomposing triangle strips
// and fans into triangle lists.
vector<Face> faces(nFaces);
uint32 f = 0;
for (i = 0; mesh.getGroup(i) != NULL; i++)
{
const Mesh::PrimitiveGroup* group = mesh.getGroup(i);
switch (group->prim)
{
case Mesh::TriList:
{
for (uint32 j = 0; j < group->nIndices / 3; j++)
{
assert(f < nFaces);
faces[f].i[0] = group->indices[j * 3];
faces[f].i[1] = group->indices[j * 3 + 1];
faces[f].i[2] = group->indices[j * 3 + 2];
f++;
}
}
break;
}
}
uint32 posOffset = desc.getAttribute(Mesh::Position)->offset;
uint32 normOffset = desc.getAttribute(Mesh::Normal)->offset;
uint32 texCoordOffset = desc.getAttribute(Mesh::Texture0)->offset;
const void* vertexData = mesh.getVertexData();
// Compute tangents for faces
for (f = 0; f < nFaces; f++)
{
Face& face = faces[f];
Point3f p0 = getVertex(vertexData, posOffset, desc.stride, face.i[0]);
Point3f p1 = getVertex(vertexData, posOffset, desc.stride, face.i[1]);
Point3f p2 = getVertex(vertexData, posOffset, desc.stride, face.i[2]);
Point2f tc0 = getTexCoord(vertexData, texCoordOffset, desc.stride, face.i[0]);
Point2f tc1 = getTexCoord(vertexData, texCoordOffset, desc.stride, face.i[1]);
Point2f tc2 = getTexCoord(vertexData, texCoordOffset, desc.stride, face.i[2]);
float s1 = tc1.x - tc0.x;
float s2 = tc2.x - tc0.x;
float t1 = tc1.y - tc0.y;
float t2 = tc2.y - tc0.y;
float a = s1 * t2 - s2 * t1;
if (a != 0.0f)
face.normal = (t2 * (p1 - p0) - t1 * (p2 - p0)) * (1.0f / a);
else
face.normal = Vec3f(0.0f, 0.0f, 0.0f);
}
// For each vertex, create a list of faces that contain it
uint32* faceCounts = new uint32[nVertices];
uint32** vertexFaces = new uint32*[nVertices];
// Initialize the lists
for (i = 0; i < nVertices; i++)
{
faceCounts[i] = 0;
vertexFaces[i] = NULL;
}
// If we're welding vertices before generating normals, find identical
// points and merge them. Otherwise, the point indices will be the same
// as the attribute indices.
if (weld)
{
mergeVertices(faces, vertexData, desc, PointTexCoordComparator(0, 0, true));
}
else
{
for (f = 0; f < nFaces; f++)
{
faces[f].vi[0] = faces[f].i[0];
faces[f].vi[1] = faces[f].i[1];
faces[f].vi[2] = faces[f].i[2];
}
}
// Count the number of faces in which each vertex appears
for (f = 0; f < nFaces; f++)
{
Face& face = faces[f];
faceCounts[face.vi[0]]++;
faceCounts[face.vi[1]]++;
faceCounts[face.vi[2]]++;
}
// Allocate space for the per-vertex face lists
for (i = 0; i < nVertices; i++)
{
if (faceCounts[i] > 0)
{
vertexFaces[i] = new uint32[faceCounts[i] + 1];
vertexFaces[i][0] = faceCounts[i];
}
}
// Fill in the vertex/face lists
for (f = 0; f < nFaces; f++)
{
Face& face = faces[f];
vertexFaces[face.vi[0]][faceCounts[face.vi[0]]--] = f;
vertexFaces[face.vi[1]][faceCounts[face.vi[1]]--] = f;
vertexFaces[face.vi[2]][faceCounts[face.vi[2]]--] = f;
}
// Compute the vertex tangents by averaging
vector<Vec3f> vertexNormals(nFaces * 3);
for (f = 0; f < nFaces; f++)
{
Face& face = faces[f];
for (uint32 j = 0; j < 3; j++)
{
vertexNormals[f * 3 + j] =
averageNormals(faces, f,
&vertexFaces[face.vi[j]][1],
vertexFaces[face.vi[j]][0],
0.0f);
}
}
// Create the new vertex description
Mesh::VertexDescription newDesc(desc);
augmentVertexDescription(newDesc, Mesh::Tangent, Mesh::Float3);
// We need to convert the copy the old vertex attributes to the new
// mesh. In order to do this, we need the old offset of each attribute
// in the new vertex description. The fromOffsets array will contain
// this mapping.
uint32 tangentOffset = 0;
uint32 fromOffsets[16];
for (i = 0; i < newDesc.nAttributes; i++)
{
fromOffsets[i] = ~0;
if (newDesc.attributes[i].semantic == Mesh::Tangent)
{
tangentOffset = newDesc.attributes[i].offset;
}
else
{
for (uint32 j = 0; j < desc.nAttributes; j++)
{
if (desc.attributes[j].semantic == newDesc.attributes[i].semantic)
{
assert(desc.attributes[j].format == newDesc.attributes[i].format);
fromOffsets[i] = desc.attributes[j].offset;
break;
}
}
}
}
// Copy the old vertex data along with the generated tangents to the
// new vertex data buffer.
void* newVertexData = new char[newDesc.stride * nFaces * 3];
for (f = 0; f < nFaces; f++)
{
Face& face = faces[f];
for (uint32 j = 0; j < 3; j++)
{
char* newVertex = reinterpret_cast<char*>(newVertexData) +
(f * 3 + j) * newDesc.stride;
copyVertex(newVertex, newDesc,
vertexData, desc,
face.i[j],
fromOffsets);
memcpy(newVertex + tangentOffset, &vertexNormals[f * 3 + j],
Mesh::getVertexAttributeSize(Mesh::Float3));
}
}
return NULL;
}
bool parseCommandLine(int argc, char* argv[])
{
int i = 1;

View file

@ -108,6 +108,8 @@ RSC_FLAGS=/l 0x409 /d "_DEBUG"
<<
all : $(OUTDIR)\3dstocmod.exe $(OUTDIR)\cmodfix.exe
3dstocmod.exe : $(OUTDIR)\3dstocmod.exe
cmodfix.exe : $(OUTDIR)\cmodfix.exe