1
0
forked from 0ad/0ad
0ad/source/collada/GeomReindex.cpp
Ykkrosh 3573c4a4e3 # Optimisations for Linux
GCC: Don't export symbols from DSOs by default - see
http://gcc.gnu.org/wiki/Visibility

This was SVN commit r4812.
2007-01-26 18:26:45 +00:00

201 lines
5.9 KiB
C++

#include "precompiled.h"
#include "GeomReindex.h"
#include "FCollada.h"
#include "FCDocument/FCDEntity.h"
#include "FCDocument/FCDGeometryPolygons.h"
#include "FCDocument/FCDGeometrySource.h"
#include "FCDocument/FCDSkinController.h"
#include <cassert>
struct VertexData
{
VertexData(const float* pos, const float* norm, const float* tex, const FCDJointWeightPairList& weights)
: x(pos[0]), y(pos[1]), z(pos[2]),
nx(norm[0]), ny(norm[1]), nz(norm[2]),
u(tex[0]), v(tex[1]),
weights(weights)
{
}
float x, y, z;
float nx, ny, nz;
float u, v;
FCDJointWeightPairList weights;
};
bool similar(float a, float b)
{
return (fabsf(a - b) < 0.000001f);
}
bool operator==(const FCDJointWeightPair& a, const FCDJointWeightPair& b)
{
return (a.jointIndex == b.jointIndex && similar(a.weight, b.weight));
}
bool operator<(const FCDJointWeightPair& a, const FCDJointWeightPair& b)
{
// Sort by decreasing weight, then by increasing joint ID
if (a.weight > b.weight)
return true;
else if (a.weight < b.weight)
return false;
else if (a.jointIndex < b.jointIndex)
return true;
else
return false;
}
bool operator==(const VertexData& a, const VertexData& b)
{
return (similar(a.x, b.x) && similar(a.y, b.y) && similar(a.z, b.z)
&& similar(a.nx, b.nx) && similar(a.ny, b.ny) && similar(a.nz, b.nz)
&& similar(a.u, b.u) && similar(a.v, b.v)
&& (a.weights == b.weights));
}
bool operator<(const VertexData& a, const VertexData& b)
{
#define CMP(f) if (a.f < b.f) return true; if (a.f > b.f) return false
CMP(x); CMP(y); CMP(z);
CMP(nx); CMP(ny); CMP(nz);
CMP(u); CMP(v);
CMP(weights);
#undef CMP
return false;
}
template <typename T>
struct InserterWithoutDuplicates
{
InserterWithoutDuplicates(std::vector<T>& vec) : vec(vec)
{
}
size_t add(const T& val)
{
typename std::map<T, size_t>::iterator it = btree.find(val);
if (it != btree.end())
return it->second;
size_t idx = vec.size();
vec.push_back(val);
btree.insert(std::make_pair(val, idx));
return idx;
}
std::vector<T>& vec;
std::map<T, size_t> btree; // for faster lookups (so we can build a duplicate-free list in O(n log n) instead of O(n^2))
private:
InserterWithoutDuplicates& operator=(const InserterWithoutDuplicates&);
};
void CanonicaliseWeights(FCDJointWeightPairList& weights)
{
// Convert weight-lists into a standard format, so simple vector equality
// can be used to determine equivalence
std::sort(weights.begin(), weights.end());
}
void ReindexGeometry(FCDGeometryPolygons* polys, FCDSkinController* skin)
{
// Given geometry with:
// positions, normals, texcoords, bone blends
// each with their own data array and index array, change it to
// have a single optimised index array shared by all vertexes.
FCDGeometryPolygonsInput* inputPosition = polys->FindInput(FUDaeGeometryInput::POSITION);
FCDGeometryPolygonsInput* inputNormal = polys->FindInput(FUDaeGeometryInput::NORMAL);
FCDGeometryPolygonsInput* inputTexcoord = polys->FindInput(FUDaeGeometryInput::TEXCOORD);
UInt32List* indicesPosition = polys->FindIndices(inputPosition);
UInt32List* indicesNormal = polys->FindIndices(inputNormal);
UInt32List* indicesTexcoord = polys->FindIndices(inputTexcoord);
assert(indicesPosition);
assert(indicesNormal);
assert(indicesTexcoord); // TODO - should be optional, because textureless meshes aren't unreasonable
size_t numVertices = polys->GetFaceVertexCount();
assert(indicesPosition->size() == numVertices);
assert(indicesNormal ->size() == numVertices);
assert(indicesTexcoord->size() == numVertices);
FCDGeometrySource* sourcePosition = inputPosition->GetSource();
FCDGeometrySource* sourceNormal = inputNormal ->GetSource();
FCDGeometrySource* sourceTexcoord = inputTexcoord->GetSource();
const FloatList& dataPosition = sourcePosition->GetSourceData();
const FloatList& dataNormal = sourceNormal ->GetSourceData();
const FloatList& dataTexcoord = sourceTexcoord->GetSourceData();
if (skin)
{
size_t numVertexPositions = dataPosition.size() / sourcePosition->GetSourceStride();
assert(skin->GetVertexInfluenceCount() == numVertexPositions);
}
uint32 stridePosition = sourcePosition->GetSourceStride();
uint32 strideNormal = sourceNormal ->GetSourceStride();
uint32 strideTexcoord = sourceTexcoord->GetSourceStride();
UInt32List indicesCombined;
std::vector<VertexData> vertexes;
InserterWithoutDuplicates<VertexData> inserter(vertexes);
for (size_t i = 0; i < numVertices; ++i)
{
FCDJointWeightPairList weights;
if (skin)
{
weights = *skin->GetInfluences((*indicesPosition)[i]);
CanonicaliseWeights(weights);
}
VertexData vtx (
&dataPosition[(*indicesPosition)[i]*stridePosition],
&dataNormal [(*indicesNormal )[i]*strideNormal],
&dataTexcoord[(*indicesTexcoord)[i]*strideTexcoord],
weights
);
size_t idx = inserter.add(vtx);//InsertWithoutDuplicates(vertexes, vtx);
indicesCombined.push_back((uint32)idx);
}
FloatList newDataPosition;
FloatList newDataNormal;
FloatList newDataTexcoord;
FCDWeightedMatches newWeightedMatches;
for (size_t i = 0; i < vertexes.size(); ++i)
{
newDataPosition.push_back(vertexes[i].x);
newDataPosition.push_back(vertexes[i].y);
newDataPosition.push_back(vertexes[i].z);
newDataNormal .push_back(vertexes[i].nx);
newDataNormal .push_back(vertexes[i].ny);
newDataNormal .push_back(vertexes[i].nz);
newDataTexcoord.push_back(vertexes[i].u);
newDataTexcoord.push_back(vertexes[i].v);
newWeightedMatches.push_back(vertexes[i].weights);
}
// (Slightly wasteful to duplicate this array so many times, but FCollada
// doesn't seem to support multiple inputs with the same source data)
*indicesPosition = indicesCombined;
*indicesNormal = indicesCombined;
*indicesTexcoord = indicesCombined;
sourcePosition->SetSourceData(newDataPosition, 3);
sourceNormal ->SetSourceData(newDataNormal, 3);
sourceTexcoord->SetSourceData(newDataTexcoord, 3);
if (skin)
skin->GetWeightedMatches() = newWeightedMatches;
}