Added some tangent generation code to cmodfix. Modified makefile to default to building all tools.
This commit is contained in:
parent
35c0798aac
commit
60153e751c
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue