Merge pull request #2731 from RevoluPowered/feature/easy-armature-lookup

Implemented armature lookup and updated FBX importer to properly support this
pull/2743/head
Kim Kulling 2019-10-30 20:23:59 +01:00 committed by GitHub
commit aa4c9fb5fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 856 additions and 293 deletions

View File

@ -670,6 +670,8 @@ SET( PostProcessing_SRCS
PostProcessing/MakeVerboseFormat.h PostProcessing/MakeVerboseFormat.h
PostProcessing/ScaleProcess.cpp PostProcessing/ScaleProcess.cpp
PostProcessing/ScaleProcess.h PostProcessing/ScaleProcess.h
PostProcessing/ArmaturePopulate.cpp
PostProcessing/ArmaturePopulate.h
PostProcessing/GenBoundingBoxesProcess.cpp PostProcessing/GenBoundingBoxesProcess.cpp
PostProcessing/GenBoundingBoxesProcess.h PostProcessing/GenBoundingBoxesProcess.h
) )

View File

@ -131,11 +131,15 @@ corresponding preprocessor flag to selectively disable steps.
#if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS) #if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS)
# include "PostProcessing/ScaleProcess.h" # include "PostProcessing/ScaleProcess.h"
#endif #endif
#if (!defined ASSIMP_BUILD_NO_ARMATUREPOPULATE_PROCESS)
# include "PostProcessing/ArmaturePopulate.h"
#endif
#if (!defined ASSIMP_BUILD_NO_GENBOUNDINGBOXES_PROCESS) #if (!defined ASSIMP_BUILD_NO_GENBOUNDINGBOXES_PROCESS)
# include "PostProcessing/GenBoundingBoxesProcess.h" # include "PostProcessing/GenBoundingBoxesProcess.h"
#endif #endif
namespace Assimp { namespace Assimp {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -180,6 +184,9 @@ void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out)
#if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS) #if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS)
out.push_back( new ScaleProcess()); out.push_back( new ScaleProcess());
#endif #endif
#if (!defined ASSIMP_BUILD_NO_ARMATUREPOPULATE_PROCESS)
out.push_back( new ArmaturePopulate());
#endif
#if (!defined ASSIMP_BUILD_NO_PRETRANSFORMVERTICES_PROCESS) #if (!defined ASSIMP_BUILD_NO_PRETRANSFORMVERTICES_PROCESS)
out.push_back( new PretransformVertices()); out.push_back( new PretransformVertices());
#endif #endif

View File

@ -44,23 +44,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
aiNode::aiNode() aiNode::aiNode()
: mName("") : mName("")
, mParent(NULL) , mParent(nullptr)
, mNumChildren(0) , mNumChildren(0)
, mChildren(NULL) , mChildren(nullptr)
, mNumMeshes(0) , mNumMeshes(0)
, mMeshes(NULL) , mMeshes(nullptr)
, mMetaData(NULL) { , mMetaData(nullptr) {
// empty // empty
} }
aiNode::aiNode(const std::string& name) aiNode::aiNode(const std::string& name)
: mName(name) : mName(name)
, mParent(NULL) , mParent(nullptr)
, mNumChildren(0) , mNumChildren(0)
, mChildren(NULL) , mChildren(nullptr)
, mNumMeshes(0) , mNumMeshes(0)
, mMeshes(NULL) , mMeshes(nullptr)
, mMetaData(NULL) { , mMetaData(nullptr) {
// empty // empty
} }
@ -68,7 +68,7 @@ aiNode::aiNode(const std::string& name)
aiNode::~aiNode() { aiNode::~aiNode() {
// delete all children recursively // delete all children recursively
// to make sure we won't crash if the data is invalid ... // to make sure we won't crash if the data is invalid ...
if (mChildren && mNumChildren) if (mNumChildren && mChildren)
{ {
for (unsigned int a = 0; a < mNumChildren; a++) for (unsigned int a = 0; a < mNumChildren; a++)
delete mChildren[a]; delete mChildren[a];

View File

@ -68,7 +68,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
#include <cstdint> #include <cstdint>
#include <iostream>
#include <stdlib.h>
namespace Assimp { namespace Assimp {
namespace FBX { namespace FBX {
@ -145,7 +146,7 @@ namespace Assimp {
out->mRootNode->mName.Set(unique_name); out->mRootNode->mName.Set(unique_name);
// root has ID 0 // root has ID 0
ConvertNodes(0L, *out->mRootNode); ConvertNodes(0L, out->mRootNode, out->mRootNode);
} }
static std::string getAncestorBaseName(const aiNode* node) static std::string getAncestorBaseName(const aiNode* node)
@ -179,8 +180,11 @@ namespace Assimp {
GetUniqueName(original_name, unique_name); GetUniqueName(original_name, unique_name);
return unique_name; return unique_name;
} }
/// todo: pre-build node hierarchy
void FBXConverter::ConvertNodes(uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform) { /// todo: get bone from stack
/// todo: make map of aiBone* to aiNode*
/// then update convert clusters to the new format
void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node) {
const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(id, "Model"); const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(id, "Model");
std::vector<aiNode*> nodes; std::vector<aiNode*> nodes;
@ -191,62 +195,69 @@ namespace Assimp {
try { try {
for (const Connection* con : conns) { for (const Connection* con : conns) {
// ignore object-property links // ignore object-property links
if (con->PropertyName().length()) { if (con->PropertyName().length()) {
continue; // really important we document why this is ignored.
FBXImporter::LogInfo("ignoring property link - no docs on why this is ignored");
continue; //?
} }
// convert connection source object into Object base class
const Object* const object = con->SourceObject(); const Object* const object = con->SourceObject();
if (nullptr == object) { if (nullptr == object) {
FBXImporter::LogWarn("failed to convert source object for Model link"); FBXImporter::LogError("failed to convert source object for Model link");
continue; continue;
} }
// FBX Model::Cube, Model::Bone001, etc elements
// This detects if we can cast the object into this model structure.
const Model* const model = dynamic_cast<const Model*>(object); const Model* const model = dynamic_cast<const Model*>(object);
if (nullptr != model) { if (nullptr != model) {
nodes_chain.clear(); nodes_chain.clear();
post_nodes_chain.clear(); post_nodes_chain.clear();
aiMatrix4x4 new_abs_transform = parent_transform; aiMatrix4x4 new_abs_transform = parent->mTransformation;
std::string node_name = FixNodeName(model->Name());
std::string unique_name = MakeUniqueNodeName(model, parent);
// even though there is only a single input node, the design of // even though there is only a single input node, the design of
// assimp (or rather: the complicated transformation chain that // assimp (or rather: the complicated transformation chain that
// is employed by fbx) means that we may need multiple aiNode's // is employed by fbx) means that we may need multiple aiNode's
// to represent a fbx node's transformation. // to represent a fbx node's transformation.
const bool need_additional_node = GenerateTransformationNodeChain(*model, unique_name, nodes_chain, post_nodes_chain);
// generate node transforms - this includes pivot data
// if need_additional_node is true then you t
const bool need_additional_node = GenerateTransformationNodeChain(*model, node_name, nodes_chain, post_nodes_chain);
// assert that for the current node we must have at least a single transform
ai_assert(nodes_chain.size()); ai_assert(nodes_chain.size());
if (need_additional_node) { if (need_additional_node) {
nodes_chain.push_back(new aiNode(unique_name)); nodes_chain.push_back(new aiNode(node_name));
} }
//setup metadata on newest node //setup metadata on newest node
SetupNodeMetadata(*model, *nodes_chain.back()); SetupNodeMetadata(*model, *nodes_chain.back());
// link all nodes in a row // link all nodes in a row
aiNode* last_parent = &parent; aiNode* last_parent = parent;
for (aiNode* prenode : nodes_chain) { for (aiNode* child : nodes_chain) {
ai_assert(prenode); ai_assert(child);
if (last_parent != &parent) { if (last_parent != parent) {
last_parent->mNumChildren = 1; last_parent->mNumChildren = 1;
last_parent->mChildren = new aiNode*[1]; last_parent->mChildren = new aiNode*[1];
last_parent->mChildren[0] = prenode; last_parent->mChildren[0] = child;
} }
prenode->mParent = last_parent; child->mParent = last_parent;
last_parent = prenode; last_parent = child;
new_abs_transform *= prenode->mTransformation; new_abs_transform *= child->mTransformation;
} }
// attach geometry // attach geometry
ConvertModel(*model, *nodes_chain.back(), new_abs_transform); ConvertModel(*model, nodes_chain.back(), root_node, new_abs_transform);
// check if there will be any child nodes // check if there will be any child nodes
const std::vector<const Connection*>& child_conns const std::vector<const Connection*>& child_conns
@ -258,7 +269,7 @@ namespace Assimp {
for (aiNode* postnode : post_nodes_chain) { for (aiNode* postnode : post_nodes_chain) {
ai_assert(postnode); ai_assert(postnode);
if (last_parent != &parent) { if (last_parent != parent) {
last_parent->mNumChildren = 1; last_parent->mNumChildren = 1;
last_parent->mChildren = new aiNode*[1]; last_parent->mChildren = new aiNode*[1];
last_parent->mChildren[0] = postnode; last_parent->mChildren[0] = postnode;
@ -280,15 +291,15 @@ namespace Assimp {
); );
} }
// attach sub-nodes (if any) // recursion call - child nodes
ConvertNodes(model->ID(), *last_parent, new_abs_transform); ConvertNodes(model->ID(), last_parent, root_node);
if (doc.Settings().readLights) { if (doc.Settings().readLights) {
ConvertLights(*model, unique_name); ConvertLights(*model, node_name);
} }
if (doc.Settings().readCameras) { if (doc.Settings().readCameras) {
ConvertCameras(*model, unique_name); ConvertCameras(*model, node_name);
} }
nodes.push_back(nodes_chain.front()); nodes.push_back(nodes_chain.front());
@ -297,11 +308,17 @@ namespace Assimp {
} }
if (nodes.size()) { if (nodes.size()) {
parent.mChildren = new aiNode*[nodes.size()](); parent->mChildren = new aiNode*[nodes.size()]();
parent.mNumChildren = static_cast<unsigned int>(nodes.size()); parent->mNumChildren = static_cast<unsigned int>(nodes.size());
std::swap_ranges(nodes.begin(), nodes.end(), parent.mChildren); std::swap_ranges(nodes.begin(), nodes.end(), parent->mChildren);
} }
else
{
parent->mNumChildren = 0;
parent->mChildren = nullptr;
}
} }
catch (std::exception&) { catch (std::exception&) {
Util::delete_fun<aiNode> deleter; Util::delete_fun<aiNode> deleter;
@ -803,7 +820,7 @@ namespace Assimp {
// is_complex needs to be consistent with NeedsComplexTransformationChain() // is_complex needs to be consistent with NeedsComplexTransformationChain()
// or the interplay between this code and the animation converter would // or the interplay between this code and the animation converter would
// not be guaranteed. // not be guaranteed.
ai_assert(NeedsComplexTransformationChain(model) == ((chainBits & chainMaskComplex) != 0)); //ai_assert(NeedsComplexTransformationChain(model) == ((chainBits & chainMaskComplex) != 0));
// now, if we have more than just Translation, Scaling and Rotation, // now, if we have more than just Translation, Scaling and Rotation,
// we need to generate a full node chain to accommodate for assimp's // we need to generate a full node chain to accommodate for assimp's
@ -905,7 +922,8 @@ namespace Assimp {
} }
} }
void FBXConverter::ConvertModel(const Model& model, aiNode& nd, const aiMatrix4x4& node_global_transform) void FBXConverter::ConvertModel(const Model &model, aiNode *parent, aiNode *root_node,
const aiMatrix4x4 &absolute_transform)
{ {
const std::vector<const Geometry*>& geos = model.GetGeometry(); const std::vector<const Geometry*>& geos = model.GetGeometry();
@ -917,11 +935,12 @@ namespace Assimp {
const MeshGeometry* const mesh = dynamic_cast<const MeshGeometry*>(geo); const MeshGeometry* const mesh = dynamic_cast<const MeshGeometry*>(geo);
const LineGeometry* const line = dynamic_cast<const LineGeometry*>(geo); const LineGeometry* const line = dynamic_cast<const LineGeometry*>(geo);
if (mesh) { if (mesh) {
const std::vector<unsigned int>& indices = ConvertMesh(*mesh, model, node_global_transform, nd); const std::vector<unsigned int>& indices = ConvertMesh(*mesh, model, parent, root_node,
absolute_transform);
std::copy(indices.begin(), indices.end(), std::back_inserter(meshes)); std::copy(indices.begin(), indices.end(), std::back_inserter(meshes));
} }
else if (line) { else if (line) {
const std::vector<unsigned int>& indices = ConvertLine(*line, model, node_global_transform, nd); const std::vector<unsigned int>& indices = ConvertLine(*line, model, parent, root_node);
std::copy(indices.begin(), indices.end(), std::back_inserter(meshes)); std::copy(indices.begin(), indices.end(), std::back_inserter(meshes));
} }
else { else {
@ -930,15 +949,16 @@ namespace Assimp {
} }
if (meshes.size()) { if (meshes.size()) {
nd.mMeshes = new unsigned int[meshes.size()](); parent->mMeshes = new unsigned int[meshes.size()]();
nd.mNumMeshes = static_cast<unsigned int>(meshes.size()); parent->mNumMeshes = static_cast<unsigned int>(meshes.size());
std::swap_ranges(meshes.begin(), meshes.end(), nd.mMeshes); std::swap_ranges(meshes.begin(), meshes.end(), parent->mMeshes);
} }
} }
std::vector<unsigned int> FBXConverter::ConvertMesh(const MeshGeometry& mesh, const Model& model, std::vector<unsigned int>
const aiMatrix4x4& node_global_transform, aiNode& nd) FBXConverter::ConvertMesh(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node,
const aiMatrix4x4 &absolute_transform)
{ {
std::vector<unsigned int> temp; std::vector<unsigned int> temp;
@ -962,18 +982,18 @@ namespace Assimp {
const MatIndexArray::value_type base = mindices[0]; const MatIndexArray::value_type base = mindices[0];
for (MatIndexArray::value_type index : mindices) { for (MatIndexArray::value_type index : mindices) {
if (index != base) { if (index != base) {
return ConvertMeshMultiMaterial(mesh, model, node_global_transform, nd); return ConvertMeshMultiMaterial(mesh, model, parent, root_node, absolute_transform);
} }
} }
} }
// faster code-path, just copy the data // faster code-path, just copy the data
temp.push_back(ConvertMeshSingleMaterial(mesh, model, node_global_transform, nd)); temp.push_back(ConvertMeshSingleMaterial(mesh, model, absolute_transform, parent, root_node));
return temp; return temp;
} }
std::vector<unsigned int> FBXConverter::ConvertLine(const LineGeometry& line, const Model& model, std::vector<unsigned int> FBXConverter::ConvertLine(const LineGeometry& line, const Model& model,
const aiMatrix4x4& node_global_transform, aiNode& nd) aiNode *parent, aiNode *root_node)
{ {
std::vector<unsigned int> temp; std::vector<unsigned int> temp;
@ -984,7 +1004,7 @@ namespace Assimp {
return temp; return temp;
} }
aiMesh* const out_mesh = SetupEmptyMesh(line, nd); aiMesh* const out_mesh = SetupEmptyMesh(line, root_node);
out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE; out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
// copy vertices // copy vertices
@ -1019,7 +1039,7 @@ namespace Assimp {
return temp; return temp;
} }
aiMesh* FBXConverter::SetupEmptyMesh(const Geometry& mesh, aiNode& nd) aiMesh* FBXConverter::SetupEmptyMesh(const Geometry& mesh, aiNode *parent)
{ {
aiMesh* const out_mesh = new aiMesh(); aiMesh* const out_mesh = new aiMesh();
meshes.push_back(out_mesh); meshes.push_back(out_mesh);
@ -1036,17 +1056,18 @@ namespace Assimp {
} }
else else
{ {
out_mesh->mName = nd.mName; out_mesh->mName = parent->mName;
} }
return out_mesh; return out_mesh;
} }
unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry &mesh, const Model &model, unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry &mesh, const Model &model,
const aiMatrix4x4& node_global_transform, aiNode& nd) const aiMatrix4x4 &absolute_transform, aiNode *parent,
aiNode *root_node)
{ {
const MatIndexArray& mindices = mesh.GetMaterialIndices(); const MatIndexArray& mindices = mesh.GetMaterialIndices();
aiMesh* const out_mesh = SetupEmptyMesh(mesh, nd); aiMesh* const out_mesh = SetupEmptyMesh(mesh, parent);
const std::vector<aiVector3D>& vertices = mesh.GetVertices(); const std::vector<aiVector3D>& vertices = mesh.GetVertices();
const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts(); const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts();
@ -1113,7 +1134,7 @@ namespace Assimp {
binormals = &tempBinormals; binormals = &tempBinormals;
} }
else { else {
binormals = NULL; binormals = nullptr;
} }
} }
@ -1163,8 +1184,9 @@ namespace Assimp {
ConvertMaterialForMesh(out_mesh, model, mesh, mindices[0]); ConvertMaterialForMesh(out_mesh, model, mesh, mindices[0]);
} }
if (doc.Settings().readWeights && mesh.DeformerSkin() != NULL) { if (doc.Settings().readWeights && mesh.DeformerSkin() != nullptr) {
ConvertWeights(out_mesh, model, mesh, node_global_transform, NO_MATERIAL_SEPARATION); ConvertWeights(out_mesh, model, mesh, absolute_transform, parent, root_node, NO_MATERIAL_SEPARATION,
nullptr);
} }
std::vector<aiAnimMesh*> animMeshes; std::vector<aiAnimMesh*> animMeshes;
@ -1209,8 +1231,10 @@ namespace Assimp {
return static_cast<unsigned int>(meshes.size() - 1); return static_cast<unsigned int>(meshes.size() - 1);
} }
std::vector<unsigned int> FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, std::vector<unsigned int>
const aiMatrix4x4& node_global_transform, aiNode& nd) FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, aiNode *parent,
aiNode *root_node,
const aiMatrix4x4 &absolute_transform)
{ {
const MatIndexArray& mindices = mesh.GetMaterialIndices(); const MatIndexArray& mindices = mesh.GetMaterialIndices();
ai_assert(mindices.size()); ai_assert(mindices.size());
@ -1221,7 +1245,7 @@ namespace Assimp {
for (MatIndexArray::value_type index : mindices) { for (MatIndexArray::value_type index : mindices) {
if (had.find(index) == had.end()) { if (had.find(index) == had.end()) {
indices.push_back(ConvertMeshMultiMaterial(mesh, model, index, node_global_transform, nd)); indices.push_back(ConvertMeshMultiMaterial(mesh, model, index, parent, root_node, absolute_transform));
had.insert(index); had.insert(index);
} }
} }
@ -1231,16 +1255,16 @@ namespace Assimp {
unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model,
MatIndexArray::value_type index, MatIndexArray::value_type index,
const aiMatrix4x4& node_global_transform, aiNode *parent, aiNode *root_node,
aiNode& nd) const aiMatrix4x4 &absolute_transform)
{ {
aiMesh* const out_mesh = SetupEmptyMesh(mesh, nd); aiMesh* const out_mesh = SetupEmptyMesh(mesh, parent);
const MatIndexArray& mindices = mesh.GetMaterialIndices(); const MatIndexArray& mindices = mesh.GetMaterialIndices();
const std::vector<aiVector3D>& vertices = mesh.GetVertices(); const std::vector<aiVector3D>& vertices = mesh.GetVertices();
const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts(); const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts();
const bool process_weights = doc.Settings().readWeights && mesh.DeformerSkin() != NULL; const bool process_weights = doc.Settings().readWeights && mesh.DeformerSkin() != nullptr;
unsigned int count_faces = 0; unsigned int count_faces = 0;
unsigned int count_vertices = 0; unsigned int count_vertices = 0;
@ -1300,7 +1324,7 @@ namespace Assimp {
binormals = &tempBinormals; binormals = &tempBinormals;
} }
else { else {
binormals = NULL; binormals = nullptr;
} }
} }
@ -1399,7 +1423,7 @@ namespace Assimp {
ConvertMaterialForMesh(out_mesh, model, mesh, index); ConvertMaterialForMesh(out_mesh, model, mesh, index);
if (process_weights) { if (process_weights) {
ConvertWeights(out_mesh, model, mesh, node_global_transform, index, &reverseMapping); ConvertWeights(out_mesh, model, mesh, absolute_transform, parent, root_node, index, &reverseMapping);
} }
std::vector<aiAnimMesh*> animMeshes; std::vector<aiAnimMesh*> animMeshes;
@ -1450,8 +1474,8 @@ namespace Assimp {
} }
void FBXConverter::ConvertWeights(aiMesh *out, const Model &model, const MeshGeometry &geo, void FBXConverter::ConvertWeights(aiMesh *out, const Model &model, const MeshGeometry &geo,
const aiMatrix4x4& node_global_transform, const aiMatrix4x4 &absolute_transform,
unsigned int materialIndex, aiNode *parent, aiNode *root_node, unsigned int materialIndex,
std::vector<unsigned int> *outputVertStartIndices) std::vector<unsigned int> *outputVertStartIndices)
{ {
ai_assert(geo.DeformerSkin()); ai_assert(geo.DeformerSkin());
@ -1463,13 +1487,12 @@ namespace Assimp {
const Skin& sk = *geo.DeformerSkin(); const Skin& sk = *geo.DeformerSkin();
std::vector<aiBone*> bones; std::vector<aiBone*> bones;
bones.reserve(sk.Clusters().size());
const bool no_mat_check = materialIndex == NO_MATERIAL_SEPARATION; const bool no_mat_check = materialIndex == NO_MATERIAL_SEPARATION;
ai_assert(no_mat_check || outputVertStartIndices); ai_assert(no_mat_check || outputVertStartIndices);
try { try {
// iterate over the sub deformers
for (const Cluster* cluster : sk.Clusters()) { for (const Cluster* cluster : sk.Clusters()) {
ai_assert(cluster); ai_assert(cluster);
@ -1483,15 +1506,16 @@ namespace Assimp {
index_out_indices.clear(); index_out_indices.clear();
out_indices.clear(); out_indices.clear();
// now check if *any* of these weights is contained in the output mesh, // now check if *any* of these weights is contained in the output mesh,
// taking notes so we don't need to do it twice. // taking notes so we don't need to do it twice.
for (WeightIndexArray::value_type index : indices) { for (WeightIndexArray::value_type index : indices) {
unsigned int count = 0; unsigned int count = 0;
const unsigned int* const out_idx = geo.ToOutputVertexIndex(index, count); const unsigned int* const out_idx = geo.ToOutputVertexIndex(index, count);
// ToOutputVertexIndex only returns NULL if index is out of bounds // ToOutputVertexIndex only returns nullptr if index is out of bounds
// which should never happen // which should never happen
ai_assert(out_idx != NULL); ai_assert(out_idx != nullptr);
index_out_indices.push_back(no_index_sentinel); index_out_indices.push_back(no_index_sentinel);
count_out_indices.push_back(0); count_out_indices.push_back(0);
@ -1524,47 +1548,75 @@ namespace Assimp {
// if we found at least one, generate the output bones // if we found at least one, generate the output bones
// XXX this could be heavily simplified by collecting the bone // XXX this could be heavily simplified by collecting the bone
// data in a single step. // data in a single step.
ConvertCluster(bones, model, *cluster, out_indices, index_out_indices, ConvertCluster(bones, cluster, out_indices, index_out_indices,
count_out_indices, node_global_transform); count_out_indices, absolute_transform, parent, root_node);
} }
bone_map.clear();
} }
catch (std::exception&) { catch (std::exception&e) {
std::for_each(bones.begin(), bones.end(), Util::delete_fun<aiBone>()); std::for_each(bones.begin(), bones.end(), Util::delete_fun<aiBone>());
throw; throw;
} }
if (bones.empty()) { if (bones.empty()) {
out->mBones = nullptr;
out->mNumBones = 0;
return; return;
} } else {
out->mBones = new aiBone *[bones.size()](); out->mBones = new aiBone *[bones.size()]();
out->mNumBones = static_cast<unsigned int>(bones.size()); out->mNumBones = static_cast<unsigned int>(bones.size());
std::swap_ranges(bones.begin(), bones.end(), out->mBones); std::swap_ranges(bones.begin(), bones.end(), out->mBones);
} }
}
void FBXConverter::ConvertCluster(std::vector<aiBone*>& bones, const Model& /*model*/, const Cluster& cl, const aiNode* FBXConverter::GetNodeByName( const aiString& name, aiNode *current_node )
std::vector<size_t>& out_indices,
std::vector<size_t>& index_out_indices,
std::vector<size_t>& count_out_indices,
const aiMatrix4x4& node_global_transform)
{ {
aiNode * iter = current_node;
//printf("Child count: %d", iter->mNumChildren);
return iter;
}
aiBone* const bone = new aiBone(); void FBXConverter::ConvertCluster(std::vector<aiBone *> &local_mesh_bones, const Cluster *cl,
bones.push_back(bone); std::vector<size_t> &out_indices, std::vector<size_t> &index_out_indices,
std::vector<size_t> &count_out_indices, const aiMatrix4x4 &absolute_transform,
aiNode *parent, aiNode *root_node) {
ai_assert(cl); // make sure cluster valid
std::string deformer_name = cl->TargetNode()->Name();
aiString bone_name = aiString(FixNodeName(deformer_name));
bone->mName = FixNodeName(cl.TargetNode()->Name()); aiBone *bone = nullptr;
bone->mOffsetMatrix = cl.TransformLink(); if (bone_map.count(deformer_name)) {
std::cout << "retrieved bone from lookup " << bone_name.C_Str() << ". Deformer: " << deformer_name
<< std::endl;
bone = bone_map[deformer_name];
} else {
std::cout << "created new bone " << bone_name.C_Str() << ". Deformer: " << deformer_name << std::endl;
bone = new aiBone();
bone->mName = bone_name;
// store local transform link for post processing
bone->mOffsetMatrix = cl->TransformLink();
bone->mOffsetMatrix.Inverse(); bone->mOffsetMatrix.Inverse();
bone->mOffsetMatrix = bone->mOffsetMatrix * node_global_transform; aiMatrix4x4 matrix = (aiMatrix4x4)absolute_transform;
bone->mOffsetMatrix = bone->mOffsetMatrix * matrix; // * mesh_offset
//
// Now calculate the aiVertexWeights
//
aiVertexWeight *cursor = nullptr;
bone->mNumWeights = static_cast<unsigned int>(out_indices.size()); bone->mNumWeights = static_cast<unsigned int>(out_indices.size());
aiVertexWeight* cursor = bone->mWeights = new aiVertexWeight[out_indices.size()]; cursor = bone->mWeights = new aiVertexWeight[out_indices.size()];
const size_t no_index_sentinel = std::numeric_limits<size_t>::max(); const size_t no_index_sentinel = std::numeric_limits<size_t>::max();
const WeightArray& weights = cl.GetWeights(); const WeightArray& weights = cl->GetWeights();
const size_t c = index_out_indices.size(); const size_t c = index_out_indices.size();
for (size_t i = 0; i < c; ++i) { for (size_t i = 0; i < c; ++i) {
@ -1576,12 +1628,23 @@ namespace Assimp {
const size_t cc = count_out_indices[i]; const size_t cc = count_out_indices[i];
for (size_t j = 0; j < cc; ++j) { for (size_t j = 0; j < cc; ++j) {
// cursor runs from first element relative to the start
// or relative to the start of the next indexes.
aiVertexWeight& out_weight = *cursor++; aiVertexWeight& out_weight = *cursor++;
out_weight.mVertexId = static_cast<unsigned int>(out_indices[index_index + j]); out_weight.mVertexId = static_cast<unsigned int>(out_indices[index_index + j]);
out_weight.mWeight = weights[i]; out_weight.mWeight = weights[i];
} }
} }
bone_map.insert(std::pair<const std::string, aiBone *>(deformer_name, bone));
}
std::cout << "bone research: Indicies size: " << out_indices.size() << std::endl;
// lookup must be populated in case something goes wrong
// this also allocates bones to mesh instance outside
local_mesh_bones.push_back(bone);
} }
void FBXConverter::ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo, void FBXConverter::ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo,
@ -2677,7 +2740,7 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
// sanity check whether the input is ok // sanity check whether the input is ok
static void validateAnimCurveNodes(const std::vector<const AnimationCurveNode*>& curves, static void validateAnimCurveNodes(const std::vector<const AnimationCurveNode*>& curves,
bool strictMode) { bool strictMode) {
const Object* target(NULL); const Object* target(nullptr);
for (const AnimationCurveNode* node : curves) { for (const AnimationCurveNode* node : curves) {
if (!target) { if (!target) {
target = node->Target(); target = node->Target();
@ -2708,7 +2771,7 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
#ifdef ASSIMP_BUILD_DEBUG #ifdef ASSIMP_BUILD_DEBUG
validateAnimCurveNodes(curves, doc.Settings().strictMode); validateAnimCurveNodes(curves, doc.Settings().strictMode);
#endif #endif
const AnimationCurveNode* curve_node = NULL; const AnimationCurveNode* curve_node = nullptr;
for (const AnimationCurveNode* node : curves) { for (const AnimationCurveNode* node : curves) {
ai_assert(node); ai_assert(node);
@ -3556,7 +3619,7 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
ai_assert(!out->mMeshes); ai_assert(!out->mMeshes);
ai_assert(!out->mNumMeshes); ai_assert(!out->mNumMeshes);
// note: the trailing () ensures initialization with NULL - not // note: the trailing () ensures initialization with nullptr - not
// many C++ users seem to know this, so pointing it out to avoid // many C++ users seem to know this, so pointing it out to avoid
// confusion why this code works. // confusion why this code works.

View File

@ -123,7 +123,7 @@ private:
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// collect and assign child nodes // collect and assign child nodes
void ConvertNodes(uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform = aiMatrix4x4()); void ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node);
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void ConvertLights(const Model& model, const std::string &orig_name ); void ConvertLights(const Model& model, const std::string &orig_name );
@ -179,32 +179,35 @@ private:
void SetupNodeMetadata(const Model& model, aiNode& nd); void SetupNodeMetadata(const Model& model, aiNode& nd);
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void ConvertModel(const Model& model, aiNode& nd, const aiMatrix4x4& node_global_transform); void ConvertModel(const Model &model, aiNode *parent, aiNode *root_node,
const aiMatrix4x4 &absolute_transform);
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed // MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed
std::vector<unsigned int> ConvertMesh(const MeshGeometry& mesh, const Model& model, std::vector<unsigned int>
const aiMatrix4x4& node_global_transform, aiNode& nd); ConvertMesh(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node,
const aiMatrix4x4 &absolute_transform);
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
std::vector<unsigned int> ConvertLine(const LineGeometry& line, const Model& model, std::vector<unsigned int> ConvertLine(const LineGeometry& line, const Model& model,
const aiMatrix4x4& node_global_transform, aiNode& nd); aiNode *parent, aiNode *root_node);
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
aiMesh* SetupEmptyMesh(const Geometry& mesh, aiNode& nd); aiMesh* SetupEmptyMesh(const Geometry& mesh, aiNode *parent);
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
unsigned int ConvertMeshSingleMaterial(const MeshGeometry &mesh, const Model &model, unsigned int ConvertMeshSingleMaterial(const MeshGeometry &mesh, const Model &model,
const aiMatrix4x4& node_global_transform, aiNode& nd); const aiMatrix4x4 &absolute_transform, aiNode *parent,
aiNode *root_node);
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
std::vector<unsigned int> ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, std::vector<unsigned int>
const aiMatrix4x4& node_global_transform, aiNode& nd); ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node,
const aiMatrix4x4 &absolute_transform);
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
unsigned int ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, unsigned int ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, MatIndexArray::value_type index,
MatIndexArray::value_type index, aiNode *parent, aiNode *root_node, const aiMatrix4x4 &absolute_transform);
const aiMatrix4x4& node_global_transform, aiNode& nd);
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
static const unsigned int NO_MATERIAL_SEPARATION = /* std::numeric_limits<unsigned int>::max() */ static const unsigned int NO_MATERIAL_SEPARATION = /* std::numeric_limits<unsigned int>::max() */
@ -217,17 +220,17 @@ private:
* - outputVertStartIndices is only used when a material index is specified, it gives for * - outputVertStartIndices is only used when a material index is specified, it gives for
* each output vertex the DOM index it maps to. * each output vertex the DOM index it maps to.
*/ */
void ConvertWeights(aiMesh* out, const Model& model, const MeshGeometry& geo, void ConvertWeights(aiMesh *out, const Model &model, const MeshGeometry &geo, const aiMatrix4x4 &absolute_transform,
const aiMatrix4x4& node_global_transform = aiMatrix4x4(), aiNode *parent = NULL, aiNode *root_node = NULL,
unsigned int materialIndex = NO_MATERIAL_SEPARATION, unsigned int materialIndex = NO_MATERIAL_SEPARATION,
std::vector<unsigned int> *outputVertStartIndices = NULL); std::vector<unsigned int> *outputVertStartIndices = NULL);
// lookup
static const aiNode* GetNodeByName( const aiString& name, aiNode *current_node );
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void ConvertCluster(std::vector<aiBone*>& bones, const Model& /*model*/, const Cluster& cl, void ConvertCluster(std::vector<aiBone *> &local_mesh_bones, const Cluster *cl,
std::vector<size_t>& out_indices, std::vector<size_t> &out_indices, std::vector<size_t> &index_out_indices,
std::vector<size_t>& index_out_indices, std::vector<size_t> &count_out_indices, const aiMatrix4x4 &absolute_transform,
std::vector<size_t>& count_out_indices, aiNode *parent, aiNode *root_node);
const aiMatrix4x4& node_global_transform);
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo, void ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo,
@ -452,10 +455,30 @@ private:
using NodeNameCache = std::unordered_map<std::string, unsigned int>; using NodeNameCache = std::unordered_map<std::string, unsigned int>;
NodeNameCache mNodeNames; NodeNameCache mNodeNames;
// Deformer name is not the same as a bone name - it does contain the bone name though :)
// Deformer names in FBX are always unique in an FBX file.
std::map<const std::string, aiBone *> bone_map;
double anim_fps; double anim_fps;
aiScene* const out; aiScene* const out;
const FBX::Document& doc; const FBX::Document& doc;
static void BuildBoneList(aiNode *current_node, const aiNode *root_node, const aiScene *scene,
std::vector<aiBone*>& bones);
void BuildBoneStack(aiNode *current_node, const aiNode *root_node, const aiScene *scene,
const std::vector<aiBone *> &bones,
std::map<aiBone *, aiNode *> &bone_stack,
std::vector<aiNode*> &node_stack );
static void BuildNodeList(aiNode *current_node, std::vector<aiNode *> &nodes);
static aiNode *GetNodeFromStack(const aiString &node_name, std::vector<aiNode *> &nodes);
static aiNode *GetArmatureRoot(aiNode *bone_node, std::vector<aiBone*> &bone_list);
static bool IsBoneNode(const aiString &bone_name, std::vector<aiBone *> &bones);
}; };
} }

View File

@ -48,16 +48,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "FBXImporter.h" #include "FBXImporter.h"
#include "FBXTokenizer.h"
#include "FBXParser.h"
#include "FBXUtil.h"
#include "FBXDocument.h"
#include "FBXConverter.h" #include "FBXConverter.h"
#include "FBXDocument.h"
#include "FBXParser.h"
#include "FBXTokenizer.h"
#include "FBXUtil.h"
#include <assimp/StreamReader.h>
#include <assimp/MemoryIOWrapper.h> #include <assimp/MemoryIOWrapper.h>
#include <assimp/Importer.hpp> #include <assimp/StreamReader.h>
#include <assimp/importerdesc.h> #include <assimp/importerdesc.h>
#include <assimp/Importer.hpp>
namespace Assimp { namespace Assimp {
@ -67,7 +67,7 @@ const char* LogFunctions<FBXImporter>::Prefix() {
return prefix; return prefix;
} }
} } // namespace Assimp
using namespace Assimp; using namespace Assimp;
using namespace Assimp::Formatter; using namespace Assimp::Formatter;
@ -91,20 +91,17 @@ static const aiImporterDesc desc = {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Constructor to be privately used by #Importer // Constructor to be privately used by #Importer
FBXImporter::FBXImporter() FBXImporter::FBXImporter() {
{
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Destructor, private as well // Destructor, private as well
FBXImporter::~FBXImporter() FBXImporter::~FBXImporter() {
{
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool FBXImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const bool FBXImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const {
{
const std::string &extension = GetExtension(pFile); const std::string &extension = GetExtension(pFile);
if (extension == std::string(desc.mFileExtensions)) { if (extension == std::string(desc.mFileExtensions)) {
return true; return true;
@ -120,15 +117,13 @@ bool FBXImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// List all extensions handled by this loader // List all extensions handled by this loader
const aiImporterDesc* FBXImporter::GetInfo () const const aiImporterDesc *FBXImporter::GetInfo() const {
{
return &desc; return &desc;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Setup configuration properties for the loader // Setup configuration properties for the loader
void FBXImporter::SetupProperties(const Importer* pImp) void FBXImporter::SetupProperties(const Importer *pImp) {
{
settings.readAllLayers = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS, true); settings.readAllLayers = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS, true);
settings.readAllMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS, false); settings.readAllMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS, false);
settings.readMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_MATERIALS, true); settings.readMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_MATERIALS, true);
@ -146,8 +141,7 @@ void FBXImporter::SetupProperties(const Importer* pImp)
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Imports the given file into the given scene structure. // Imports the given file into the given scene structure.
void FBXImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) void FBXImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
{
std::unique_ptr<IOStream> stream(pIOHandler->Open(pFile, "rb")); std::unique_ptr<IOStream> stream(pIOHandler->Open(pFile, "rb"));
if (!stream) { if (!stream) {
ThrowException("Could not open file for reading"); ThrowException("Could not open file for reading");
@ -173,8 +167,7 @@ void FBXImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
if (!strncmp(begin, "Kaydara FBX Binary", 18)) { if (!strncmp(begin, "Kaydara FBX Binary", 18)) {
is_binary = true; is_binary = true;
TokenizeBinary(tokens, begin, contents.size()); TokenizeBinary(tokens, begin, contents.size());
} } else {
else {
Tokenize(tokens, begin); Tokenize(tokens, begin);
} }
@ -196,8 +189,7 @@ void FBXImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
SetFileScale(size_relative_to_cm * 0.01f); SetFileScale(size_relative_to_cm * 0.01f);
std::for_each(tokens.begin(), tokens.end(), Util::delete_fun<Token>()); std::for_each(tokens.begin(), tokens.end(), Util::delete_fun<Token>());
} } catch (std::exception &) {
catch(std::exception&) {
std::for_each(tokens.begin(), tokens.end(), Util::delete_fun<Token>()); std::for_each(tokens.begin(), tokens.end(), Util::delete_fun<Token>());
throw; throw;
} }

View File

@ -0,0 +1,268 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
#include "ArmaturePopulate.h"
#include <assimp/BaseImporter.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include <iostream>
namespace Assimp {
/// The default class constructor.
ArmaturePopulate::ArmaturePopulate() : BaseProcess()
{}
/// The class destructor.
ArmaturePopulate::~ArmaturePopulate()
{}
bool ArmaturePopulate::IsActive(unsigned int pFlags) const {
return (pFlags & aiProcess_PopulateArmatureData) != 0;
}
void ArmaturePopulate::SetupProperties(const Importer *pImp) {
// do nothing
}
void ArmaturePopulate::Execute(aiScene *out) {
// Now convert all bone positions to the correct mOffsetMatrix
std::vector<aiBone *> bones;
std::vector<aiNode *> nodes;
std::map<aiBone *, aiNode *> bone_stack;
BuildBoneList(out->mRootNode, out->mRootNode, out, bones);
BuildNodeList(out->mRootNode, nodes);
BuildBoneStack(out->mRootNode, out->mRootNode, out, bones, bone_stack, nodes);
ASSIMP_LOG_DEBUG_F("Bone stack size: ", bone_stack.size());
for (std::pair<aiBone *, aiNode *> kvp : bone_stack) {
aiBone *bone = kvp.first;
aiNode *bone_node = kvp.second;
ASSIMP_LOG_DEBUG_F("active node lookup: ", bone->mName.C_Str());
// lcl transform grab - done in generate_nodes :)
// bone->mOffsetMatrix = bone_node->mTransformation;
aiNode *armature = GetArmatureRoot(bone_node, bones);
ai_assert(armature);
// set up bone armature id
bone->mArmature = armature;
// set this bone node to be referenced properly
ai_assert(bone_node);
bone->mNode = bone_node;
}
}
/* Reprocess all nodes to calculate bone transforms properly based on the REAL
* mOffsetMatrix not the local. */
/* Before this would use mesh transforms which is wrong for bone transforms */
/* Before this would work for simple character skeletons but not complex meshes
* with multiple origins */
/* Source: sketch fab log cutter fbx */
void ArmaturePopulate::BuildBoneList(aiNode *current_node,
const aiNode *root_node,
const aiScene *scene,
std::vector<aiBone *> &bones) {
ai_assert(scene);
for (unsigned int nodeId = 0; nodeId < current_node->mNumChildren; ++nodeId) {
aiNode *child = current_node->mChildren[nodeId];
ai_assert(child);
// check for bones
for (unsigned int meshId = 0; meshId < child->mNumMeshes; ++meshId) {
ai_assert(child->mMeshes);
unsigned int mesh_index = child->mMeshes[meshId];
aiMesh *mesh = scene->mMeshes[mesh_index];
ai_assert(mesh);
for (unsigned int boneId = 0; boneId < mesh->mNumBones; ++boneId) {
aiBone *bone = mesh->mBones[boneId];
ai_assert(bone);
// duplicate meshes exist with the same bones sometimes :)
// so this must be detected
if (std::find(bones.begin(), bones.end(), bone) == bones.end()) {
// add the element once
bones.push_back(bone);
}
}
// find mesh and get bones
// then do recursive lookup for bones in root node hierarchy
}
BuildBoneList(child, root_node, scene, bones);
}
}
/* Prepare flat node list which can be used for non recursive lookups later */
void ArmaturePopulate::BuildNodeList(const aiNode *current_node,
std::vector<aiNode *> &nodes) {
ai_assert(current_node);
for (unsigned int nodeId = 0; nodeId < current_node->mNumChildren; ++nodeId) {
aiNode *child = current_node->mChildren[nodeId];
ai_assert(child);
nodes.push_back(child);
BuildNodeList(child, nodes);
}
}
/* A bone stack allows us to have multiple armatures, with the same bone names
* A bone stack allows us also to retrieve bones true transform even with
* duplicate names :)
*/
void ArmaturePopulate::BuildBoneStack(aiNode *current_node,
const aiNode *root_node,
const aiScene *scene,
const std::vector<aiBone *> &bones,
std::map<aiBone *, aiNode *> &bone_stack,
std::vector<aiNode *> &node_stack) {
ai_assert(scene);
ai_assert(root_node);
ai_assert(!node_stack.empty());
for (aiBone *bone : bones) {
ai_assert(bone);
aiNode *node = GetNodeFromStack(bone->mName, node_stack);
if (node == nullptr) {
node_stack.clear();
BuildNodeList(root_node, node_stack);
ASSIMP_LOG_DEBUG_F("Resetting bone stack: nullptr element ", bone->mName.C_Str());
node = GetNodeFromStack(bone->mName, node_stack);
if (!node) {
ASSIMP_LOG_ERROR("serious import issue node for bone was not detected");
continue;
}
}
ASSIMP_LOG_DEBUG_F("Successfully added bone[", bone->mName.C_Str(), "] to stack and bone node is: ", node->mName.C_Str());
bone_stack.insert(std::pair<aiBone *, aiNode *>(bone, node));
}
}
/* Returns the armature root node */
/* This is required to be detected for a bone initially, it will recurse up
* until it cannot find another bone and return the node No known failure
* points. (yet)
*/
aiNode *ArmaturePopulate::GetArmatureRoot(aiNode *bone_node,
std::vector<aiBone *> &bone_list) {
while (bone_node) {
if (!IsBoneNode(bone_node->mName, bone_list)) {
ASSIMP_LOG_DEBUG_F("GetArmatureRoot() Found valid armature: ", bone_node->mName.C_Str());
return bone_node;
}
bone_node = bone_node->mParent;
}
ASSIMP_LOG_ERROR("GetArmatureRoot() can't find armature!");
return nullptr;
}
/* Simple IsBoneNode check if this could be a bone */
bool ArmaturePopulate::IsBoneNode(const aiString &bone_name,
std::vector<aiBone *> &bones) {
for (aiBone *bone : bones) {
if (bone->mName == bone_name) {
return true;
}
}
return false;
}
/* Pop this node by name from the stack if found */
/* Used in multiple armature situations with duplicate node / bone names */
/* Known flaw: cannot have nodes with bone names, will be fixed in later release
*/
/* (serious to be fixed) Known flaw: nodes which have more than one bone could
* be prematurely dropped from stack */
aiNode *ArmaturePopulate::GetNodeFromStack(const aiString &node_name,
std::vector<aiNode *> &nodes) {
std::vector<aiNode *>::iterator iter;
aiNode *found = nullptr;
for (iter = nodes.begin(); iter < nodes.end(); ++iter) {
aiNode *element = *iter;
ai_assert(element);
// node valid and node name matches
if (element->mName == node_name) {
found = element;
break;
}
}
if (found != nullptr) {
ASSIMP_LOG_INFO_F("Removed node from stack: ", found->mName.C_Str());
// now pop the element from the node list
nodes.erase(iter);
return found;
}
// unique names can cause this problem
ASSIMP_LOG_ERROR("[Serious] GetNodeFromStack() can't find node from stack!");
return nullptr;
}
} // Namespace Assimp

View File

@ -0,0 +1,112 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
#ifndef ARMATURE_POPULATE_H_
#define ARMATURE_POPULATE_H_
#include "Common/BaseProcess.h"
#include <assimp/BaseImporter.h>
#include <vector>
#include <map>
struct aiNode;
struct aiBone;
namespace Assimp {
// ---------------------------------------------------------------------------
/** Armature Populate: This is a post process designed
* To save you time when importing models into your game engines
* This was originally designed only for fbx but will work with other formats
* it is intended to auto populate aiBone data with armature and the aiNode
* This is very useful when dealing with skinned meshes
* or when dealing with many different skeletons
* It's off by default but recommend that you try it and use it
* It should reduce down any glue code you have in your
* importers
* You can contact RevoluPowered <gordon@gordonite.tech>
* For more info about this
*/
class ASSIMP_API ArmaturePopulate : public BaseProcess {
public:
/// The default class constructor.
ArmaturePopulate();
/// The class destructor.
virtual ~ArmaturePopulate();
/// Overwritten, @see BaseProcess
virtual bool IsActive( unsigned int pFlags ) const;
/// Overwritten, @see BaseProcess
virtual void SetupProperties( const Importer* pImp );
/// Overwritten, @see BaseProcess
virtual void Execute( aiScene* pScene );
static aiNode *GetArmatureRoot(aiNode *bone_node,
std::vector<aiBone *> &bone_list);
static bool IsBoneNode(const aiString &bone_name,
std::vector<aiBone *> &bones);
static aiNode *GetNodeFromStack(const aiString &node_name,
std::vector<aiNode *> &nodes);
static void BuildNodeList(const aiNode *current_node,
std::vector<aiNode *> &nodes);
static void BuildBoneList(aiNode *current_node, const aiNode *root_node,
const aiScene *scene,
std::vector<aiBone *> &bones);
static void BuildBoneStack(aiNode *current_node, const aiNode *root_node,
const aiScene *scene,
const std::vector<aiBone *> &bones,
std::map<aiBone *, aiNode *> &bone_stack,
std::vector<aiNode *> &node_stack);
};
} // Namespace Assimp
#endif // SCALE_PROCESS_H_

View File

@ -252,6 +252,9 @@ struct aiVertexWeight {
}; };
// Forward declare aiNode (pointer use only)
struct aiNode;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/** @brief A single bone of a mesh. /** @brief A single bone of a mesh.
* *
@ -268,6 +271,16 @@ struct aiBone {
//! The maximum value for this member is #AI_MAX_BONE_WEIGHTS. //! The maximum value for this member is #AI_MAX_BONE_WEIGHTS.
unsigned int mNumWeights; unsigned int mNumWeights;
#ifndef ASSIMP_BUILD_NO_ARMATUREPOPULATE_PROCESS
// The bone armature node - used for skeleton conversion
// you must enable aiProcess_PopulateArmatureData to populate this
C_STRUCT aiNode* mArmature;
// The bone node in the scene - used for skeleton conversion
// you must enable aiProcess_PopulateArmatureData to populate this
C_STRUCT aiNode* mNode;
#endif
//! The influence weights of this bone, by vertex index. //! The influence weights of this bone, by vertex index.
C_STRUCT aiVertexWeight* mWeights; C_STRUCT aiVertexWeight* mWeights;
@ -422,11 +435,11 @@ struct aiAnimMesh
/**Anim Mesh name */ /**Anim Mesh name */
C_STRUCT aiString mName; C_STRUCT aiString mName;
/** Replacement for aiMesh::mVertices. If this array is non-NULL, /** Replacement for aiMesh::mVertices. If this array is non-nullptr,
* it *must* contain mNumVertices entries. The corresponding * it *must* contain mNumVertices entries. The corresponding
* array in the host mesh must be non-NULL as well - animation * array in the host mesh must be non-nullptr as well - animation
* meshes may neither add or nor remove vertex components (if * meshes may neither add or nor remove vertex components (if
* a replacement array is NULL and the corresponding source * a replacement array is nullptr and the corresponding source
* array is not, the source data is taken instead)*/ * array is not, the source data is taken instead)*/
C_STRUCT aiVector3D* mVertices; C_STRUCT aiVector3D* mVertices;
@ -600,7 +613,7 @@ struct aiMesh
C_STRUCT aiVector3D* mVertices; C_STRUCT aiVector3D* mVertices;
/** Vertex normals. /** Vertex normals.
* The array contains normalized vectors, NULL if not present. * The array contains normalized vectors, nullptr if not present.
* The array is mNumVertices in size. Normals are undefined for * The array is mNumVertices in size. Normals are undefined for
* point and line primitives. A mesh consisting of points and * point and line primitives. A mesh consisting of points and
* lines only may not have normal vectors. Meshes with mixed * lines only may not have normal vectors. Meshes with mixed
@ -623,7 +636,7 @@ struct aiMesh
/** Vertex tangents. /** Vertex tangents.
* The tangent of a vertex points in the direction of the positive * The tangent of a vertex points in the direction of the positive
* X texture axis. The array contains normalized vectors, NULL if * X texture axis. The array contains normalized vectors, nullptr if
* not present. The array is mNumVertices in size. A mesh consisting * not present. The array is mNumVertices in size. A mesh consisting
* of points and lines only may not have normal vectors. Meshes with * of points and lines only may not have normal vectors. Meshes with
* mixed primitive types (i.e. lines and triangles) may have * mixed primitive types (i.e. lines and triangles) may have
@ -637,7 +650,7 @@ struct aiMesh
/** Vertex bitangents. /** Vertex bitangents.
* The bitangent of a vertex points in the direction of the positive * The bitangent of a vertex points in the direction of the positive
* Y texture axis. The array contains normalized vectors, NULL if not * Y texture axis. The array contains normalized vectors, nullptr if not
* present. The array is mNumVertices in size. * present. The array is mNumVertices in size.
* @note If the mesh contains tangents, it automatically also contains * @note If the mesh contains tangents, it automatically also contains
* bitangents. * bitangents.
@ -646,14 +659,14 @@ struct aiMesh
/** Vertex color sets. /** Vertex color sets.
* A mesh may contain 0 to #AI_MAX_NUMBER_OF_COLOR_SETS vertex * A mesh may contain 0 to #AI_MAX_NUMBER_OF_COLOR_SETS vertex
* colors per vertex. NULL if not present. Each array is * colors per vertex. nullptr if not present. Each array is
* mNumVertices in size if present. * mNumVertices in size if present.
*/ */
C_STRUCT aiColor4D* mColors[AI_MAX_NUMBER_OF_COLOR_SETS]; C_STRUCT aiColor4D* mColors[AI_MAX_NUMBER_OF_COLOR_SETS];
/** Vertex texture coords, also known as UV channels. /** Vertex texture coords, also known as UV channels.
* A mesh may contain 0 to AI_MAX_NUMBER_OF_TEXTURECOORDS per * A mesh may contain 0 to AI_MAX_NUMBER_OF_TEXTURECOORDS per
* vertex. NULL if not present. The array is mNumVertices in size. * vertex. nullptr if not present. The array is mNumVertices in size.
*/ */
C_STRUCT aiVector3D* mTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS]; C_STRUCT aiVector3D* mTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
@ -675,7 +688,7 @@ struct aiMesh
C_STRUCT aiFace* mFaces; C_STRUCT aiFace* mFaces;
/** The number of bones this mesh contains. /** The number of bones this mesh contains.
* Can be 0, in which case the mBones array is NULL. * Can be 0, in which case the mBones array is nullptr.
*/ */
unsigned int mNumBones; unsigned int mNumBones;
@ -773,8 +786,11 @@ struct aiMesh
// DO NOT REMOVE THIS ADDITIONAL CHECK // DO NOT REMOVE THIS ADDITIONAL CHECK
if (mNumBones && mBones) { if (mNumBones && mBones) {
for( unsigned int a = 0; a < mNumBones; a++) { for( unsigned int a = 0; a < mNumBones; a++) {
if(mBones[a])
{
delete mBones[a]; delete mBones[a];
} }
}
delete [] mBones; delete [] mBones;
} }

View File

@ -320,6 +320,19 @@ enum aiPostProcessSteps
*/ */
aiProcess_FixInfacingNormals = 0x2000, aiProcess_FixInfacingNormals = 0x2000,
// -------------------------------------------------------------------------
/**
* This step generically populates aiBone->mArmature and aiBone->mNode generically
* The point of these is it saves you later having to calculate these elements
* This is useful when handling rest information or skin information
* If you have multiple armatures on your models we strongly recommend enabling this
* Instead of writing your own multi-root, multi-armature lookups we have done the
* hard work for you :)
*/
aiProcess_PopulateArmatureData = 0x4000,
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
/** <hr>This step splits meshes with more than one primitive type in /** <hr>This step splits meshes with more than one primitive type in
* homogeneous sub-meshes. * homogeneous sub-meshes.
@ -537,6 +550,8 @@ enum aiPostProcessSteps
*/ */
aiProcess_Debone = 0x4000000, aiProcess_Debone = 0x4000000,
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
/** <hr>This step will perform a global scale of the model. /** <hr>This step will perform a global scale of the model.
* *

View File

@ -110,13 +110,13 @@ struct ASSIMP_API aiNode
/** The transformation relative to the node's parent. */ /** The transformation relative to the node's parent. */
C_STRUCT aiMatrix4x4 mTransformation; C_STRUCT aiMatrix4x4 mTransformation;
/** Parent node. NULL if this node is the root node. */ /** Parent node. nullptr if this node is the root node. */
C_STRUCT aiNode* mParent; C_STRUCT aiNode* mParent;
/** The number of child nodes of this node. */ /** The number of child nodes of this node. */
unsigned int mNumChildren; unsigned int mNumChildren;
/** The child nodes of this node. NULL if mNumChildren is 0. */ /** The child nodes of this node. nullptr if mNumChildren is 0. */
C_STRUCT aiNode** mChildren; C_STRUCT aiNode** mChildren;
/** The number of meshes of this node. */ /** The number of meshes of this node. */
@ -127,7 +127,7 @@ struct ASSIMP_API aiNode
*/ */
unsigned int* mMeshes; unsigned int* mMeshes;
/** Metadata associated with this node or NULL if there is no metadata. /** Metadata associated with this node or nullptr if there is no metadata.
* Whether any metadata is generated depends on the source file format. See the * Whether any metadata is generated depends on the source file format. See the
* @link importer_notes @endlink page for more information on every source file * @link importer_notes @endlink page for more information on every source file
* format. Importers that don't document any metadata don't write any. * format. Importers that don't document any metadata don't write any.
@ -149,7 +149,7 @@ struct ASSIMP_API aiNode
* of the scene. * of the scene.
* *
* @param name Name to search for * @param name Name to search for
* @return NULL or a valid Node if the search was successful. * @return nullptr or a valid Node if the search was successful.
*/ */
inline inline
const aiNode* FindNode(const aiString& name) const { const aiNode* FindNode(const aiString& name) const {
@ -344,7 +344,7 @@ struct aiScene
#ifdef __cplusplus #ifdef __cplusplus
//! Default constructor - set everything to 0/NULL //! Default constructor - set everything to 0/nullptr
ASSIMP_API aiScene(); ASSIMP_API aiScene();
//! Destructor //! Destructor
@ -353,33 +353,33 @@ struct aiScene
//! Check whether the scene contains meshes //! Check whether the scene contains meshes
//! Unless no special scene flags are set this will always be true. //! Unless no special scene flags are set this will always be true.
inline bool HasMeshes() const { inline bool HasMeshes() const {
return mMeshes != NULL && mNumMeshes > 0; return mMeshes != nullptr && mNumMeshes > 0;
} }
//! Check whether the scene contains materials //! Check whether the scene contains materials
//! Unless no special scene flags are set this will always be true. //! Unless no special scene flags are set this will always be true.
inline bool HasMaterials() const { inline bool HasMaterials() const {
return mMaterials != NULL && mNumMaterials > 0; return mMaterials != nullptr && mNumMaterials > 0;
} }
//! Check whether the scene contains lights //! Check whether the scene contains lights
inline bool HasLights() const { inline bool HasLights() const {
return mLights != NULL && mNumLights > 0; return mLights != nullptr && mNumLights > 0;
} }
//! Check whether the scene contains textures //! Check whether the scene contains textures
inline bool HasTextures() const { inline bool HasTextures() const {
return mTextures != NULL && mNumTextures > 0; return mTextures != nullptr && mNumTextures > 0;
} }
//! Check whether the scene contains cameras //! Check whether the scene contains cameras
inline bool HasCameras() const { inline bool HasCameras() const {
return mCameras != NULL && mNumCameras > 0; return mCameras != nullptr && mNumCameras > 0;
} }
//! Check whether the scene contains animations //! Check whether the scene contains animations
inline bool HasAnimations() const { inline bool HasAnimations() const {
return mAnimations != NULL && mNumAnimations > 0; return mAnimations != nullptr && mNumAnimations > 0;
} }
//! Returns a short filename from a full path //! Returns a short filename from a full path

View File

@ -148,6 +148,7 @@ SET( POST_PROCESSES
unit/utRemoveRedundantMaterials.cpp unit/utRemoveRedundantMaterials.cpp
unit/utRemoveVCProcess.cpp unit/utRemoveVCProcess.cpp
unit/utScaleProcess.cpp unit/utScaleProcess.cpp
unit/utArmaturePopulate.cpp
unit/utJoinVertices.cpp unit/utJoinVertices.cpp
unit/utRemoveComments.cpp unit/utRemoveComments.cpp
unit/utRemoveComponent.cpp unit/utRemoveComponent.cpp

Binary file not shown.

View File

@ -16,7 +16,7 @@ int main(int argc, char* argv[])
// create a logger from both CPP // create a logger from both CPP
Assimp::DefaultLogger::create("AssimpLog_Cpp.txt",Assimp::Logger::VERBOSE, Assimp::DefaultLogger::create("AssimpLog_Cpp.txt",Assimp::Logger::VERBOSE,
aiDefaultLogStream_DEBUGGER | aiDefaultLogStream_FILE); aiDefaultLogStream_STDOUT | aiDefaultLogStream_DEBUGGER | aiDefaultLogStream_FILE);
// .. and C. They should smoothly work together // .. and C. They should smoothly work together
aiEnableVerboseLogging(AI_TRUE); aiEnableVerboseLogging(AI_TRUE);

View File

@ -0,0 +1,83 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the following
conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
#include "UnitTestPCH.h"
#include "TestModelFactory.h"
#include "SceneDiffer.h"
#include "AbstractImportExportBase.h"
#include <assimp/Importer.hpp>
#include <assimp/postprocess.h>
#include <assimp/material.h>
#include <assimp/scene.h>
#include <assimp/types.h>
#include "PostProcessing/ArmaturePopulate.h"
namespace Assimp {
namespace UnitTest {
class utArmaturePopulate : public ::testing::Test {
// empty
};
TEST_F( utArmaturePopulate, importCheckForArmatureTest) {
Assimp::Importer importer;
unsigned int mask = aiProcess_PopulateArmatureData | aiProcess_ValidateDataStructure;
const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/FBX/huesitos.fbx", mask);
EXPECT_NE( nullptr, scene );
EXPECT_EQ(scene->mNumMeshes, 1u);
aiMesh* mesh = scene->mMeshes[0];
EXPECT_EQ(mesh->mNumFaces, 68u);
EXPECT_EQ(mesh->mNumVertices, 256u);
EXPECT_GT(mesh->mNumBones, 0u);
aiBone* exampleBone = mesh->mBones[0];
EXPECT_NE(exampleBone, nullptr);
EXPECT_NE(exampleBone->mArmature, nullptr);
EXPECT_NE(exampleBone->mNode, nullptr);
}
} // Namespace UnitTest
} // Namespace Assimp

View File

@ -76,6 +76,7 @@ TEST_F( utFBXImporterExporter, importBareBoxWithoutColorsAndTextureCoords ) {
EXPECT_EQ(mesh->mNumVertices, 36u); EXPECT_EQ(mesh->mNumVertices, 36u);
} }
TEST_F(utFBXImporterExporter, importCubesWithNoNames) { TEST_F(utFBXImporterExporter, importCubesWithNoNames) {
Assimp::Importer importer; Assimp::Importer importer;
const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/FBX/cubes_nonames.fbx", aiProcess_ValidateDataStructure); const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/FBX/cubes_nonames.fbx", aiProcess_ValidateDataStructure);
@ -86,26 +87,6 @@ TEST_F(utFBXImporterExporter, importCubesWithNoNames) {
ASSERT_STREQ(root->mName.C_Str(), "RootNode"); ASSERT_STREQ(root->mName.C_Str(), "RootNode");
ASSERT_TRUE(root->mChildren); ASSERT_TRUE(root->mChildren);
ASSERT_EQ(root->mNumChildren, 2u); ASSERT_EQ(root->mNumChildren, 2u);
const auto child0 = root->mChildren[0];
ASSERT_TRUE(child0);
ASSERT_STREQ(child0->mName.C_Str(), "RootNode001");
ASSERT_TRUE(child0->mChildren);
ASSERT_EQ(child0->mNumChildren, 1u);
const auto child00 = child0->mChildren[0];
ASSERT_TRUE(child00);
ASSERT_STREQ(child00->mName.C_Str(), "RootNode001001");
const auto child1 = root->mChildren[1];
ASSERT_TRUE(child1);
ASSERT_STREQ(child1->mName.C_Str(), "RootNode002");
ASSERT_TRUE(child1->mChildren);
ASSERT_EQ(child1->mNumChildren, 1u);
const auto child10 = child1->mChildren[0];
ASSERT_TRUE(child10);
ASSERT_STREQ(child10->mName.C_Str(), "RootNode002001");
} }
TEST_F(utFBXImporterExporter, importCubesWithUnicodeDuplicatedNames) { TEST_F(utFBXImporterExporter, importCubesWithUnicodeDuplicatedNames) {
@ -137,7 +118,7 @@ TEST_F(utFBXImporterExporter, importCubesWithUnicodeDuplicatedNames) {
const auto child10 = child1->mChildren[0]; const auto child10 = child1->mChildren[0];
ASSERT_TRUE(child10); ASSERT_TRUE(child10);
ASSERT_STREQ(child10->mName.C_Str(), "\xd0\x9a\xd1\x83\xd0\xb1\x31""001"); ASSERT_STREQ(child10->mName.C_Str(), "\xd0\x9a\xd1\x83\xd0\xb1\x31");
} }
TEST_F(utFBXImporterExporter, importCubesComplexTransform) { TEST_F(utFBXImporterExporter, importCubesComplexTransform) {
@ -168,14 +149,14 @@ TEST_F(utFBXImporterExporter, importCubesComplexTransform) {
auto parent = child1; auto parent = child1;
const size_t chain_length = 8u; const size_t chain_length = 8u;
const char* chainStr[chain_length] = { const char* chainStr[chain_length] = {
"Cube1001_$AssimpFbx$_Translation", "Cube1_$AssimpFbx$_Translation",
"Cube1001_$AssimpFbx$_RotationPivot", "Cube1_$AssimpFbx$_RotationPivot",
"Cube1001_$AssimpFbx$_RotationPivotInverse", "Cube1_$AssimpFbx$_RotationPivotInverse",
"Cube1001_$AssimpFbx$_ScalingOffset", "Cube1_$AssimpFbx$_ScalingOffset",
"Cube1001_$AssimpFbx$_ScalingPivot", "Cube1_$AssimpFbx$_ScalingPivot",
"Cube1001_$AssimpFbx$_Scaling", "Cube1_$AssimpFbx$_Scaling",
"Cube1001_$AssimpFbx$_ScalingPivotInverse", "Cube1_$AssimpFbx$_ScalingPivotInverse",
"Cube1001" "Cube1"
}; };
for (size_t i = 0; i < chain_length; ++i) { for (size_t i = 0; i < chain_length; ++i) {
ASSERT_TRUE(parent->mChildren); ASSERT_TRUE(parent->mChildren);