- fbx: convert fbx deformers to assimp bones.

pull/14/head
Alexander Gessler 2012-07-26 22:53:31 +02:00
parent 05ec3c2e90
commit 666f2776d7
5 changed files with 193 additions and 8 deletions

View File

@ -414,6 +414,10 @@ private:
ConvertMaterialForMesh(out_mesh,model,mesh,mindices[0]);
}
if(doc.Settings().readWeights && mesh.DeformerSkin() != NULL) {
ConvertWeights(out_mesh, model, mesh, std::numeric_limits<unsigned int>::max());
}
return static_cast<unsigned int>(meshes.size() - 1);
}
@ -594,6 +598,129 @@ private:
}
// ------------------------------------------------------------------------------------------------
void ConvertWeights(aiMesh* out, const Model& model, const MeshGeometry& geo, unsigned int materialIndex)
{
ai_assert(geo.DeformerSkin());
std::vector<size_t> out_indices;
std::vector<size_t> index_out_indices;
std::vector<size_t> count_out_indices;
const Skin& sk = *geo.DeformerSkin();
std::vector<aiBone*> bones;
bones.reserve(sk.Clusters().size());
const bool no_mat_check = materialIndex == std::numeric_limits<unsigned int>::max();
try {
BOOST_FOREACH(const Cluster* cluster, sk.Clusters()) {
ai_assert(cluster);
const WeightIndexArray& indices = cluster->GetIndices();
const WeightArray& weights = cluster->GetWeights();
const MatIndexArray& mats = geo.GetMaterialIndices();
bool ok = false;
const size_t no_index_sentinel = std::numeric_limits<size_t>::max();
count_out_indices.clear();
index_out_indices.clear();
out_indices.clear();
// now check if *any* of these weights is contained in the output mesh,
// taking notes so we don't need to do it twice.
BOOST_FOREACH(WeightIndexArray::value_type index, indices) {
unsigned int count;
const unsigned int* const out_idx = geo.ToOutputVertexIndex(index, count);
index_out_indices.push_back(no_index_sentinel);
for(unsigned int i = 0; i < count; ++i) {
const unsigned int out_face_idx = geo.FaceForVertexIndex(out_idx[i]);
ai_assert(out_face_idx <= mats.size());
if (no_mat_check || mats[out_face_idx] == materialIndex) {
if (index_out_indices.back() == no_index_sentinel) {
index_out_indices.back() = out_indices.size();
}
out_indices.push_back(out_idx[i]);
++count_out_indices.back();
ok = true;
}
}
}
// if we found at least one, generate the output bones
// XXX this could be heavily simplified by collecting the bone
// data in a single step.
if (ok) {
ConvertCluster(bones, *cluster, out_indices, index_out_indices, count_out_indices);
}
}
}
catch (std::exception&) {
std::for_each(bones.begin(),bones.end(),Util::delete_fun<aiBone>());
throw;
}
if(bones.empty()) {
return;
}
out->mBones = new aiBone*[bones.size()]();
out->mNumBones = static_cast<unsigned int>(bones.size());
std::swap_ranges(bones.begin(),bones.end(),out->mBones);
}
// ------------------------------------------------------------------------------------------------
void ConvertCluster(std::vector<aiBone*>& bones, const Cluster& cl,
std::vector<size_t>& out_indices,
std::vector<size_t>& index_out_indices,
std::vector<size_t>& count_out_indices
)
{
aiBone* const bone = new aiBone();
bones.push_back(bone);
bone->mName = FixNodeName(cl.TargetNode()->Name());
bone->mNumWeights = static_cast<unsigned int>(out_indices.size());
aiVertexWeight* cursor = bone->mWeights = new aiVertexWeight[out_indices.size()];
const size_t no_index_sentinel = std::numeric_limits<size_t>::max();
const WeightArray& weights = cl.GetWeights();
const size_t c = index_out_indices.size();
for (size_t i = 0; i < c; ++i) {
const size_t index_index = index_out_indices[i];
if (index_index == no_index_sentinel) {
continue;
}
const size_t cc = count_out_indices[i];
for (size_t j = 0; j < cc; ++j) {
aiVertexWeight& out_weight = *cursor++;
out_weight.mVertexId = static_cast<unsigned int>(out_indices[index_index + j]);
out_weight.mWeight = weights[i];
}
}
}
// ------------------------------------------------------------------------------------------------
void ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo, unsigned int materialIndex)
{

View File

@ -93,6 +93,14 @@ Cluster::Cluster(uint64_t id, const Element& element, const Document& doc, const
ReadVectorDataArray(indices,Indexes);
ReadVectorDataArray(weights,Weights);
if(indices.size() != weights.size()) {
DOMError("sizes of index and weight array don't match up",&element);
}
if(!indices.size()) {
DOMWarning("encountered empty deformer",&element);
}
// read assigned node
const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),"Deformer");
BOOST_FOREACH(const Connection* con, conns) {

View File

@ -387,6 +387,9 @@ private:
};
typedef std::vector<unsigned int> MatIndexArray;
/** DOM class for FBX geometry of type "Mesh"*/
class MeshGeometry : public Geometry
{
@ -451,10 +454,51 @@ public:
/** Get per-face-vertex material assignments */
const std::vector<unsigned int>& GetMaterialIndices() const {
const MatIndexArray& GetMaterialIndices() const {
return materials;
}
/** Convert from a fbx file vertex index (for example from a #Cluster weight) or NULL
* if the vertex index is not valid. */
const unsigned int* ToOutputVertexIndex(unsigned int in_index, unsigned int& count) const {
if(in_index >= mapping_counts.size()) {
return NULL;
}
ai_assert(mapping_counts.size() == mapping_offsets.size());
count = mapping_counts[in_index];
ai_assert(count != 0);
ai_assert(mapping_offsets[in_index] + count <= mappings.size());
return &mappings[mapping_offsets[in_index]];
}
/** Determine the face to which a particular output vertex index belongs.
* This mapping is always unique. */
unsigned int FaceForVertexIndex(unsigned int in_index) const {
ai_assert(in_index < vertices.size());
// in the current conversion pattern this will only be needed if
// weights are present, so no need to always pre-compute this table
if (facesVertexStartIndices.empty()) {
facesVertexStartIndices.resize(faces.size());
std::partial_sum(faces.begin(), faces.end(), facesVertexStartIndices.begin());
}
ai_assert(facesVertexStartIndices.size() == faces.size());
const std::vector<unsigned int>::const_iterator it = std::upper_bound(
facesVertexStartIndices.begin(),
facesVertexStartIndices.end(),
in_index
);
return *(it - 1);
}
public:
private:
@ -490,9 +534,10 @@ private:
private:
// cached data arrays
std::vector<unsigned int> materials;
MatIndexArray materials;
std::vector<aiVector3D> vertices;
std::vector<unsigned int> faces;
mutable std::vector<unsigned int> facesVertexStartIndices;
std::vector<aiVector3D> tangents;
std::vector<aiVector3D> binormals;
std::vector<aiVector3D> normals;
@ -680,7 +725,7 @@ private:
boost::shared_ptr<const PropertyTable> props;
};
typedef std::vector<float> WeightList;
typedef std::vector<float> WeightArray;
typedef std::vector<unsigned int> WeightIndexArray;
@ -695,7 +740,7 @@ public:
public:
/** get the list of deformer weights associated with this cluster */
const WeightList& GetWeights() const {
const WeightArray& GetWeights() const {
return weights;
}
@ -720,7 +765,7 @@ public:
private:
WeightList weights;
WeightArray weights;
WeightIndexArray indices;
aiMatrix4x4 transform;

View File

@ -119,14 +119,14 @@ inline const T* ProcessSimpleConnection(const Connection& con,
const Element& element,
const char** propNameOut = NULL)
{
if (is_object_property_conn && con.PropertyName().length()) {
if (is_object_property_conn && !con.PropertyName().length()) {
DOMWarning("expected incoming " + std::string(name) +
" link to be an object-object connection, ignoring",
&element
);
return NULL;
}
else if (!is_object_property_conn && !con.PropertyName().length()) {
else if (!is_object_property_conn && con.PropertyName().length()) {
DOMWarning("expected incoming " + std::string(name) +
" link to be an object-property connection, ignoring",
&element

View File

@ -58,6 +58,7 @@ struct ImportSettings
, readLights(true)
, readAnimations(true)
, strictMode(true)
, readWeights(true)
{}
@ -96,8 +97,12 @@ struct ImportSettings
bool readLights;
/** import animations (i.e. animation curves, the node
* skeleton is always imported)? Default value is true. */
* skeleton is always imported). Default value is true. */
bool readAnimations;
/** read bones (vertex weights and deform info).
* Default value is true. */
bool readWeights;
};