- fbx: convert fbx deformers to assimp bones.
parent
05ec3c2e90
commit
666f2776d7
|
@ -414,6 +414,10 @@ private:
|
||||||
ConvertMaterialForMesh(out_mesh,model,mesh,mindices[0]);
|
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);
|
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)
|
void ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo, unsigned int materialIndex)
|
||||||
{
|
{
|
||||||
|
|
|
@ -93,6 +93,14 @@ Cluster::Cluster(uint64_t id, const Element& element, const Document& doc, const
|
||||||
ReadVectorDataArray(indices,Indexes);
|
ReadVectorDataArray(indices,Indexes);
|
||||||
ReadVectorDataArray(weights,Weights);
|
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
|
// read assigned node
|
||||||
const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),"Deformer");
|
const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),"Deformer");
|
||||||
BOOST_FOREACH(const Connection* con, conns) {
|
BOOST_FOREACH(const Connection* con, conns) {
|
||||||
|
|
|
@ -387,6 +387,9 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef std::vector<unsigned int> MatIndexArray;
|
||||||
|
|
||||||
|
|
||||||
/** DOM class for FBX geometry of type "Mesh"*/
|
/** DOM class for FBX geometry of type "Mesh"*/
|
||||||
class MeshGeometry : public Geometry
|
class MeshGeometry : public Geometry
|
||||||
{
|
{
|
||||||
|
@ -451,10 +454,51 @@ public:
|
||||||
|
|
||||||
|
|
||||||
/** Get per-face-vertex material assignments */
|
/** Get per-face-vertex material assignments */
|
||||||
const std::vector<unsigned int>& GetMaterialIndices() const {
|
const MatIndexArray& GetMaterialIndices() const {
|
||||||
return materials;
|
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:
|
public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -490,9 +534,10 @@ private:
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// cached data arrays
|
// cached data arrays
|
||||||
std::vector<unsigned int> materials;
|
MatIndexArray materials;
|
||||||
std::vector<aiVector3D> vertices;
|
std::vector<aiVector3D> vertices;
|
||||||
std::vector<unsigned int> faces;
|
std::vector<unsigned int> faces;
|
||||||
|
mutable std::vector<unsigned int> facesVertexStartIndices;
|
||||||
std::vector<aiVector3D> tangents;
|
std::vector<aiVector3D> tangents;
|
||||||
std::vector<aiVector3D> binormals;
|
std::vector<aiVector3D> binormals;
|
||||||
std::vector<aiVector3D> normals;
|
std::vector<aiVector3D> normals;
|
||||||
|
@ -680,7 +725,7 @@ private:
|
||||||
boost::shared_ptr<const PropertyTable> props;
|
boost::shared_ptr<const PropertyTable> props;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::vector<float> WeightList;
|
typedef std::vector<float> WeightArray;
|
||||||
typedef std::vector<unsigned int> WeightIndexArray;
|
typedef std::vector<unsigned int> WeightIndexArray;
|
||||||
|
|
||||||
|
|
||||||
|
@ -695,7 +740,7 @@ public:
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/** get the list of deformer weights associated with this cluster */
|
/** get the list of deformer weights associated with this cluster */
|
||||||
const WeightList& GetWeights() const {
|
const WeightArray& GetWeights() const {
|
||||||
return weights;
|
return weights;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -720,7 +765,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
WeightList weights;
|
WeightArray weights;
|
||||||
WeightIndexArray indices;
|
WeightIndexArray indices;
|
||||||
|
|
||||||
aiMatrix4x4 transform;
|
aiMatrix4x4 transform;
|
||||||
|
|
|
@ -119,14 +119,14 @@ inline const T* ProcessSimpleConnection(const Connection& con,
|
||||||
const Element& element,
|
const Element& element,
|
||||||
const char** propNameOut = NULL)
|
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) +
|
DOMWarning("expected incoming " + std::string(name) +
|
||||||
" link to be an object-object connection, ignoring",
|
" link to be an object-object connection, ignoring",
|
||||||
&element
|
&element
|
||||||
);
|
);
|
||||||
return NULL;
|
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) +
|
DOMWarning("expected incoming " + std::string(name) +
|
||||||
" link to be an object-property connection, ignoring",
|
" link to be an object-property connection, ignoring",
|
||||||
&element
|
&element
|
||||||
|
|
|
@ -58,6 +58,7 @@ struct ImportSettings
|
||||||
, readLights(true)
|
, readLights(true)
|
||||||
, readAnimations(true)
|
, readAnimations(true)
|
||||||
, strictMode(true)
|
, strictMode(true)
|
||||||
|
, readWeights(true)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
@ -96,8 +97,12 @@ struct ImportSettings
|
||||||
bool readLights;
|
bool readLights;
|
||||||
|
|
||||||
/** import animations (i.e. animation curves, the node
|
/** 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;
|
bool readAnimations;
|
||||||
|
|
||||||
|
/** read bones (vertex weights and deform info).
|
||||||
|
* Default value is true. */
|
||||||
|
bool readWeights;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue