Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: acgessler/assimp-gsoc2012-fbx
base: 05ec3c2e90
...
head fork: acgessler/assimp-gsoc2012-fbx
compare: 916947327f
Checking mergeability… Don't worry, you can still create the pull request.
  • 5 commits
  • 8 files changed
  • 0 commit comments
  • 1 contributor
View
5 code/FBXAnimation.cpp
@@ -150,7 +150,8 @@ AnimationCurveNode::AnimationCurveNode(uint64_t id, const Element& element, cons
continue;
}
- ai_assert(dynamic_cast<const Model*>(ob) || dynamic_cast<const NodeAttribute*>(ob));
+ // XXX support constraints as DOM class
+ //ai_assert(dynamic_cast<const Model*>(ob) || dynamic_cast<const NodeAttribute*>(ob));
target = ob;
if(!target) {
continue;
@@ -161,7 +162,7 @@ AnimationCurveNode::AnimationCurveNode(uint64_t id, const Element& element, cons
}
}
if(!target) {
- DOMError("failed to resolve target Model/NodeAttribute for AnimationCurveNode",&element);
+ DOMWarning("failed to resolve target Model/NodeAttribute/Constraint for AnimationCurveNode",&element);
}
}
View
171 code/FBXConverter.cpp
@@ -414,6 +414,10 @@ class Converter
ConvertMaterialForMesh(out_mesh,model,mesh,mindices[0]);
}
+ if(doc.Settings().readWeights && mesh.DeformerSkin() != NULL) {
+ ConvertWeights(out_mesh, model, mesh, NO_MATERIAL_SEPARATION);
+ }
+
return static_cast<unsigned int>(meshes.size() - 1);
}
@@ -448,6 +452,8 @@ class Converter
const std::vector<aiVector3D>& vertices = mesh.GetVertices();
const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts();
+ const bool process_weights = doc.Settings().readWeights && mesh.DeformerSkin() != NULL;
+
unsigned int count_faces = 0;
unsigned int count_vertices = 0;
@@ -463,7 +469,14 @@ class Converter
}
ai_assert(count_faces);
+ ai_assert(count_vertices);
+ // mapping from output indices to DOM indexing, needed to resolve weights
+ std::vector<unsigned int> reverseMapping;
+
+ if (process_weights) {
+ reverseMapping.resize(count_vertices);
+ }
// allocate output data arrays, but don't fill them yet
out_mesh->mNumVertices = count_vertices;
@@ -566,6 +579,10 @@ class Converter
for (unsigned int i = 0; i < pcount; ++i, ++cursor, ++in_cursor) {
f.mIndices[i] = cursor;
+ if(reverseMapping.size()) {
+ reverseMapping[cursor] = in_cursor;
+ }
+
out_mesh->mVertices[cursor] = vertices[in_cursor];
if(out_mesh->mNormals) {
@@ -590,9 +607,163 @@ class Converter
}
ConvertMaterialForMesh(out_mesh,model,mesh,index);
+
+ if(process_weights) {
+ ConvertWeights(out_mesh, model, mesh, index, &reverseMapping);
+ }
+
return static_cast<unsigned int>(meshes.size() - 1);
}
+ static const unsigned int NO_MATERIAL_SEPARATION = /* std::numeric_limits<unsigned int>::max() */
+ static_cast<unsigned int>(-1);
+
+
+ // ------------------------------------------------------------------------------------------------
+ /** - if materialIndex == NO_MATERIAL_SEPARATION, materials are not taken into
+ * account when determining which weights to include.
+ * - outputVertStartIndices is only used when a material index is specified, it gives for
+ * each output vertex the DOM index it maps to. */
+ void ConvertWeights(aiMesh* out, const Model& model, const MeshGeometry& geo,
+ unsigned int materialIndex = NO_MATERIAL_SEPARATION,
+ std::vector<unsigned int>* outputVertStartIndices = NULL)
+ {
+ 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 == NO_MATERIAL_SEPARATION;
+ ai_assert(!no_mat_check || outputVertStartIndices);
+
+ try {
+
+ BOOST_FOREACH(const Cluster* cluster, sk.Clusters()) {
+ ai_assert(cluster);
+
+ const WeightIndexArray& indices = cluster->GetIndices();
+ const WeightArray& weights = cluster->GetWeights();
+
+ if(indices.empty()) {
+ continue;
+ }
+
+ 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);
+ count_out_indices.push_back(0);
+
+ for(unsigned int i = 0; i < count; ++i) {
+ if (no_mat_check || mats[geo.FaceForVertexIndex(out_idx[i])] == materialIndex) {
+
+ if (index_out_indices.back() == no_index_sentinel) {
+ index_out_indices.back() = out_indices.size();
+
+ }
+
+ if (no_mat_check) {
+ out_indices.push_back(out_idx[i]);
+ }
+ else {
+ // this extra lookup is in O(logn), so the entire algorithm becomes O(nlogn)
+ const std::vector<unsigned int>::iterator it = std::lower_bound(
+ outputVertStartIndices->begin(),
+ outputVertStartIndices->end(),
+ out_idx[i]
+ );
+
+ out_indices.push_back(std::distance(outputVertStartIndices->begin(), it));
+ }
+
+ ++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->mOffsetMatrix = cl.TransformLink();
+ bone->mOffsetMatrix.Inverse();
+
+ 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
22 code/FBXDeformer.cpp
@@ -82,19 +82,31 @@ Cluster::Cluster(uint64_t id, const Element& element, const Document& doc, const
{
const Scope& sc = GetRequiredScope(element);
- const Element& Indexes = GetRequiredElement(sc,"Indexes",&element);
- const Element& Weights = GetRequiredElement(sc,"Weights",&element);
+ const Element* const Indexes = sc["Indexes"];
+ const Element* const Weights = sc["Weights"];
+
const Element& Transform = GetRequiredElement(sc,"Transform",&element);
const Element& TransformLink = GetRequiredElement(sc,"TransformLink",&element);
transform = ReadMatrix(Transform);
transformLink = ReadMatrix(TransformLink);
- ReadVectorDataArray(indices,Indexes);
- ReadVectorDataArray(weights,Weights);
+ // it is actually possible that there be Deformer's with no weights
+ if (!!Indexes != !!Weights) {
+ DOMError("either Indexes or Weights are missing from Cluster",&element);
+ }
+
+ if(Indexes) {
+ ReadVectorDataArray(indices,*Indexes);
+ ReadVectorDataArray(weights,*Weights);
+ }
+
+ if(indices.size() != weights.size()) {
+ DOMError("sizes of index and weight array don't match up",&element);
+ }
// read assigned node
- const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),"Deformer");
+ const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),"Model");
BOOST_FOREACH(const Connection* con, conns) {
const Model* const mod = ProcessSimpleConnection<Model>(*con, false, "Model -> Cluster", element);
if(mod) {
View
3  code/FBXDocument.cpp
@@ -388,7 +388,7 @@ aiMatrix4x4 ReadMatrix(const Element& element)
aiMatrix4x4 result;
- // XXX transposed or not, this is the question :-)
+
result.a1 = values[0];
result.a2 = values[1];
result.a3 = values[2];
@@ -409,6 +409,7 @@ aiMatrix4x4 ReadMatrix(const Element& element)
result.d3 = values[14];
result.d4 = values[15];
+ result.Transpose();
return result;
}
View
68 code/FBXDocument.h
@@ -387,6 +387,9 @@ class Geometry : public Object
};
+typedef std::vector<unsigned int> MatIndexArray;
+
+
/** DOM class for FBX geometry of type "Mesh"*/
class MeshGeometry : public Geometry
{
@@ -451,10 +454,52 @@ class MeshGeometry : public Geometry
/** 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() + 1, 0);
+
+ std::partial_sum(faces.begin(), faces.end(), facesVertexStartIndices.begin() + 1);
+ facesVertexStartIndices.pop_back();
+ }
+
+ ai_assert(facesVertexStartIndices.size() == faces.size());
+ const std::vector<unsigned int>::iterator it = std::upper_bound(
+ facesVertexStartIndices.begin(),
+ facesVertexStartIndices.end(),
+ in_index
+ );
+
+ return static_cast<unsigned int>(std::distance(facesVertexStartIndices.begin(), it - 1));
+ }
+
public:
private:
@@ -490,9 +535,10 @@ class MeshGeometry : public Geometry
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;
@@ -573,8 +619,9 @@ class AnimationCurveNode : public Object
return curves;
}
- /** Model or NodeAttribute the curve is assigned to, this is always non-NULL
- * and never an objects not deriving one of the two aforementioned classes.*/
+ /** Object the curve is assigned to, this can be NULL if the
+ * target object has no DOM representation or could not
+ * be read for other reasons.*/
const Object* Target() const {
return target;
}
@@ -680,7 +727,7 @@ class Deformer : public Object
boost::shared_ptr<const PropertyTable> props;
};
-typedef std::vector<float> WeightList;
+typedef std::vector<float> WeightArray;
typedef std::vector<unsigned int> WeightIndexArray;
@@ -694,13 +741,16 @@ class Cluster : public Deformer
public:
- /** get the list of deformer weights associated with this cluster */
- const WeightList& GetWeights() const {
+ /** get the list of deformer weights associated with this cluster.
+ * Use #GetIndices() to get the associated vertices. Both arrays
+ * have the same size (and may also be empty). */
+ const WeightArray& GetWeights() const {
return weights;
}
/** get indices into the vertex data of the geometry associated
- * with this cluster.*/
+ * with this cluster. Use #GetWeights() to get the associated weights.
+ * Both arrays have the same size (and may also be empty). */
const WeightIndexArray& GetIndices() const {
return indices;
}
@@ -720,7 +770,7 @@ class Cluster : public Deformer
private:
- WeightList weights;
+ WeightArray weights;
WeightIndexArray indices;
aiMatrix4x4 transform;
View
4 code/FBXDocumentUtil.h
@@ -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
7 code/FBXImportSettings.h
@@ -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;
};
View
2  code/FBXMeshGeometry.cpp
@@ -154,7 +154,7 @@ MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::strin
cursor = 0;
BOOST_FOREACH(int index, tempFaces) {
const int absi = index < 0 ? (-index - 1) : index;
- mappings[mapping_offsets[absi] + mapping_counts[absi]++] = cursor;
+ mappings[mapping_offsets[absi] + mapping_counts[absi]++] = cursor++;
}
// if settings.readAllLayers is true:

No commit comments for this range

Something went wrong with that request. Please try again.