- fbx: convert fbx deformers to assimp bones.
parent
05ec3c2e90
commit
666f2776d7
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue