Implemented easy armature lookup
This lets you directly retrieve the node a bone links to and informs you of the armature directly This also fixes a bug with bone name being made unique which causes them to become not 1:1 what the modeller has imported.pull/2731/head
parent
29f7ea0235
commit
168ae22ad4
|
@ -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 {
|
||||||
|
@ -120,6 +121,46 @@ namespace Assimp {
|
||||||
ConvertGlobalSettings();
|
ConvertGlobalSettings();
|
||||||
TransferDataToScene();
|
TransferDataToScene();
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
std::cout << "Bone stack size: " << bone_stack.size() << std::endl;
|
||||||
|
|
||||||
|
for( std::pair<aiBone*, aiNode*> kvp : bone_stack )
|
||||||
|
{
|
||||||
|
aiBone *bone = kvp.first;
|
||||||
|
aiNode *bone_node = kvp.second;
|
||||||
|
std::cout << "active node lookup: " << bone->mName.C_Str() << std::endl;
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// apply full hierarchy to transform for basic offset
|
||||||
|
while( bone_node->mParent )
|
||||||
|
{
|
||||||
|
bone->mRestMatrix = bone_node->mTransformation * bone->mRestMatrix;
|
||||||
|
bone_node = bone_node->mParent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// if we didn't read any meshes set the AI_SCENE_FLAGS_INCOMPLETE
|
// if we didn't read any meshes set the AI_SCENE_FLAGS_INCOMPLETE
|
||||||
// to make sure the scene passes assimp's validation. FBX files
|
// to make sure the scene passes assimp's validation. FBX files
|
||||||
// need not contain geometry (i.e. camera animations, raw armatures).
|
// need not contain geometry (i.e. camera animations, raw armatures).
|
||||||
|
@ -138,6 +179,167 @@ namespace Assimp {
|
||||||
std::for_each(textures.begin(), textures.end(), Util::delete_fun<aiTexture>());
|
std::for_each(textures.begin(), textures.end(), Util::delete_fun<aiTexture>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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 * FBXConverter::GetArmatureRoot(aiNode *bone_node, std::vector<aiBone*> &bone_list)
|
||||||
|
{
|
||||||
|
while(bone_node)
|
||||||
|
{
|
||||||
|
if(!IsBoneNode(bone_node->mName, bone_list))
|
||||||
|
{
|
||||||
|
std::cout << "Found valid armature: " << bone_node->mName.C_Str() << std::endl;
|
||||||
|
return bone_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
bone_node = bone_node->mParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "can't find armature! node: " << bone_node << std::endl;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Simple IsBoneNode check if this could be a bone */
|
||||||
|
bool FBXConverter::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* FBXConverter::GetNodeFromStack(const aiString &node_name, std::vector<aiNode*> &nodes)
|
||||||
|
{
|
||||||
|
std::vector<aiNode*>::iterator iter;
|
||||||
|
aiNode *found = NULL;
|
||||||
|
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 != NULL) {
|
||||||
|
// now pop the element from the node list
|
||||||
|
nodes.erase(iter);
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prepare flat node list which can be used for non recursive lookups later */
|
||||||
|
void FBXConverter::BuildNodeList(aiNode *current_node, std::vector<aiNode *> &nodes)
|
||||||
|
{
|
||||||
|
assert(current_node);
|
||||||
|
|
||||||
|
for( unsigned int nodeId = 0; nodeId < current_node->mNumChildren; ++nodeId)
|
||||||
|
{
|
||||||
|
aiNode *child = current_node->mChildren[nodeId];
|
||||||
|
assert(child);
|
||||||
|
|
||||||
|
nodes.push_back(child);
|
||||||
|
|
||||||
|
BuildNodeList(child, nodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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 FBXConverter::BuildBoneList(aiNode *current_node, const aiNode * root_node, const aiScene *scene, std::vector<aiBone*> &bones )
|
||||||
|
{
|
||||||
|
assert(scene);
|
||||||
|
for( unsigned int nodeId = 0; nodeId < current_node->mNumChildren; ++nodeId)
|
||||||
|
{
|
||||||
|
aiNode *child = current_node->mChildren[nodeId];
|
||||||
|
assert(child);
|
||||||
|
|
||||||
|
// check for bones
|
||||||
|
for( unsigned int meshId = 0; meshId < child->mNumMeshes; ++meshId)
|
||||||
|
{
|
||||||
|
assert(child->mMeshes);
|
||||||
|
unsigned int mesh_index = child->mMeshes[meshId];
|
||||||
|
aiMesh *mesh = scene->mMeshes[ mesh_index ];
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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 FBXConverter::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 == NULL)
|
||||||
|
{
|
||||||
|
node_stack.clear();
|
||||||
|
BuildNodeList(out->mRootNode, node_stack );
|
||||||
|
std::cout << "Resetting bone stack: null element " << bone->mName.C_Str() << std::endl;
|
||||||
|
|
||||||
|
node = GetNodeFromStack(bone->mName, node_stack);
|
||||||
|
|
||||||
|
if(!node) {
|
||||||
|
std::cout << "serious import issue armature failed to be detected?" << std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Successfully added bone to stack and have valid armature: " << bone->mName.C_Str() << std::endl;
|
||||||
|
|
||||||
|
bone_stack.insert(std::pair<aiBone*, aiNode*>(bone, node));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void FBXConverter::ConvertRootNode() {
|
void FBXConverter::ConvertRootNode() {
|
||||||
out->mRootNode = new aiNode();
|
out->mRootNode = new aiNode();
|
||||||
std::string unique_name;
|
std::string unique_name;
|
||||||
|
@ -145,7 +347,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 +381,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 +396,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 +470,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 +492,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,10 +509,10 @@ 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (std::exception&) {
|
catch (std::exception&) {
|
||||||
|
@ -803,7 +1015,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 +1117,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 +1130,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 +1144,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 +1177,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 +1199,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 +1234,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 +1251,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();
|
||||||
|
@ -1164,7 +1380,8 @@ namespace Assimp {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (doc.Settings().readWeights && mesh.DeformerSkin() != NULL) {
|
if (doc.Settings().readWeights && mesh.DeformerSkin() != NULL) {
|
||||||
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 +1426,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 +1440,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1229,12 +1448,12 @@ namespace Assimp {
|
||||||
return indices;
|
return indices;
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
|
@ -1399,7 +1618,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;
|
||||||
|
@ -1449,10 +1668,10 @@ namespace Assimp {
|
||||||
return static_cast<unsigned int>(meshes.size() - 1);
|
return static_cast<unsigned int>(meshes.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 +1682,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,6 +1701,7 @@ 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) {
|
||||||
|
@ -1520,68 +1739,107 @@ 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->mNumBones = static_cast<unsigned int>(bones.size());
|
||||||
|
|
||||||
|
std::swap_ranges(bones.begin(), bones.end(), out->mBones);
|
||||||
}
|
}
|
||||||
|
|
||||||
out->mBones = new aiBone*[bones.size()]();
|
|
||||||
out->mNumBones = static_cast<unsigned int>(bones.size());
|
|
||||||
|
|
||||||
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) {
|
||||||
|
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 = NULL;
|
||||||
|
|
||||||
bone->mOffsetMatrix = cl.TransformLink();
|
if (bone_map.count(deformer_name)) {
|
||||||
bone->mOffsetMatrix.Inverse();
|
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;
|
||||||
|
|
||||||
bone->mOffsetMatrix = bone->mOffsetMatrix * node_global_transform;
|
// store local transform link for post processing
|
||||||
|
bone->mOffsetMatrix = cl->TransformLink();
|
||||||
|
bone->mOffsetMatrix.Inverse();
|
||||||
|
|
||||||
bone->mNumWeights = static_cast<unsigned int>(out_indices.size());
|
aiMatrix4x4 matrix = (aiMatrix4x4)absolute_transform;
|
||||||
aiVertexWeight* cursor = bone->mWeights = new aiVertexWeight[out_indices.size()];
|
|
||||||
|
|
||||||
const size_t no_index_sentinel = std::numeric_limits<size_t>::max();
|
bone->mOffsetMatrix = bone->mOffsetMatrix * matrix; // * mesh_offset
|
||||||
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;
|
// Now calculate the aiVertexWeights
|
||||||
|
//
|
||||||
|
|
||||||
|
aiVertexWeight *cursor = nullptr;
|
||||||
|
|
||||||
|
bone->mNumWeights = static_cast<unsigned int>(out_indices.size());
|
||||||
|
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) {
|
||||||
|
// cursor runs from first element relative to the start
|
||||||
|
// or relative to the start of the next indexes.
|
||||||
|
aiVertexWeight& out_weight = *cursor++;
|
||||||
|
|
||||||
|
out_weight.mVertexId = static_cast<unsigned int>(out_indices[index_index + j]);
|
||||||
|
out_weight.mWeight = weights[i];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const size_t cc = count_out_indices[i];
|
bone_map.insert(std::pair<const std::string, aiBone *>(deformer_name, bone));
|
||||||
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];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,26 +48,26 @@ 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 {
|
||||||
|
|
||||||
template<>
|
template <>
|
||||||
const char* LogFunctions<FBXImporter>::Prefix() {
|
const char *LogFunctions<FBXImporter>::Prefix() {
|
||||||
static auto prefix = "FBX: ";
|
static auto prefix = "FBX: ";
|
||||||
return prefix;
|
return prefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} // namespace Assimp
|
||||||
|
|
||||||
using namespace Assimp;
|
using namespace Assimp;
|
||||||
using namespace Assimp::Formatter;
|
using namespace Assimp::Formatter;
|
||||||
|
@ -76,131 +76,123 @@ using namespace Assimp::FBX;
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
static const aiImporterDesc desc = {
|
static const aiImporterDesc desc = {
|
||||||
"Autodesk FBX Importer",
|
"Autodesk FBX Importer",
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
aiImporterFlags_SupportTextFlavour,
|
aiImporterFlags_SupportTextFlavour,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
"fbx"
|
"fbx"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// 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;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
else if ((!extension.length() || checkSig) && pIOHandler) {
|
else if ((!extension.length() || checkSig) && pIOHandler) {
|
||||||
// at least ASCII-FBX files usually have a 'FBX' somewhere in their head
|
// at least ASCII-FBX files usually have a 'FBX' somewhere in their head
|
||||||
const char* tokens[] = {"fbx"};
|
const char *tokens[] = { "fbx" };
|
||||||
return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
|
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// 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);
|
settings.readTextures = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_TEXTURES, true);
|
||||||
settings.readTextures = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_TEXTURES, true);
|
settings.readCameras = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_CAMERAS, true);
|
||||||
settings.readCameras = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_CAMERAS, true);
|
settings.readLights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_LIGHTS, true);
|
||||||
settings.readLights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_LIGHTS, true);
|
settings.readAnimations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ANIMATIONS, true);
|
||||||
settings.readAnimations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ANIMATIONS, true);
|
settings.strictMode = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_STRICT_MODE, false);
|
||||||
settings.strictMode = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_STRICT_MODE, false);
|
settings.preservePivots = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true);
|
||||||
settings.preservePivots = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true);
|
settings.optimizeEmptyAnimationCurves = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, true);
|
||||||
settings.optimizeEmptyAnimationCurves = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, true);
|
settings.useLegacyEmbeddedTextureNaming = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING, false);
|
||||||
settings.useLegacyEmbeddedTextureNaming = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING, false);
|
settings.removeEmptyBones = pImp->GetPropertyBool(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, true);
|
||||||
settings.removeEmptyBones = pImp->GetPropertyBool(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, true);
|
settings.convertToMeters = pImp->GetPropertyBool(AI_CONFIG_FBX_CONVERT_TO_M, false);
|
||||||
settings.convertToMeters = pImp->GetPropertyBool(AI_CONFIG_FBX_CONVERT_TO_M, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// 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");
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// read entire file into memory - no streaming for this, fbx
|
// read entire file into memory - no streaming for this, fbx
|
||||||
// files can grow large, but the assimp output data structure
|
// files can grow large, but the assimp output data structure
|
||||||
// then becomes very large, too. Assimp doesn't support
|
// then becomes very large, too. Assimp doesn't support
|
||||||
// streaming for its output data structures so the net win with
|
// streaming for its output data structures so the net win with
|
||||||
// streaming input data would be very low.
|
// streaming input data would be very low.
|
||||||
std::vector<char> contents;
|
std::vector<char> contents;
|
||||||
contents.resize(stream->FileSize()+1);
|
contents.resize(stream->FileSize() + 1);
|
||||||
stream->Read( &*contents.begin(), 1, contents.size()-1 );
|
stream->Read(&*contents.begin(), 1, contents.size() - 1);
|
||||||
contents[ contents.size() - 1 ] = 0;
|
contents[contents.size() - 1] = 0;
|
||||||
const char* const begin = &*contents.begin();
|
const char *const begin = &*contents.begin();
|
||||||
|
|
||||||
// broadphase tokenizing pass in which we identify the core
|
// broadphase tokenizing pass in which we identify the core
|
||||||
// syntax elements of FBX (brackets, commas, key:value mappings)
|
// syntax elements of FBX (brackets, commas, key:value mappings)
|
||||||
TokenList tokens;
|
TokenList tokens;
|
||||||
try {
|
try {
|
||||||
|
|
||||||
bool is_binary = false;
|
bool is_binary = false;
|
||||||
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);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// use this information to construct a very rudimentary
|
// use this information to construct a very rudimentary
|
||||||
// parse-tree representing the FBX scope structure
|
// parse-tree representing the FBX scope structure
|
||||||
Parser parser(tokens, is_binary);
|
Parser parser(tokens, is_binary);
|
||||||
|
|
||||||
// take the raw parse-tree and convert it to a FBX DOM
|
// take the raw parse-tree and convert it to a FBX DOM
|
||||||
Document doc(parser,settings);
|
Document doc(parser, settings);
|
||||||
|
|
||||||
// convert the FBX DOM to aiScene
|
// convert the FBX DOM to aiScene
|
||||||
ConvertToAssimpScene(pScene, doc, settings.removeEmptyBones);
|
ConvertToAssimpScene(pScene, doc, settings.removeEmptyBones);
|
||||||
|
|
||||||
// size relative to cm
|
// size relative to cm
|
||||||
float size_relative_to_cm = doc.GlobalSettings().UnitScaleFactor();
|
float size_relative_to_cm = doc.GlobalSettings().UnitScaleFactor();
|
||||||
|
|
||||||
// Set FBX file scale is relative to CM must be converted to M for
|
// Set FBX file scale is relative to CM must be converted to M for
|
||||||
// assimp universal format (M)
|
// assimp universal format (M)
|
||||||
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;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // !ASSIMP_BUILD_NO_FBX_IMPORTER
|
#endif // !ASSIMP_BUILD_NO_FBX_IMPORTER
|
||||||
|
|
|
@ -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,12 @@ 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;
|
||||||
|
|
||||||
|
// The bone armature node - used for skeleton conversion
|
||||||
|
C_STRUCT aiNode* mArmature;
|
||||||
|
|
||||||
|
// The bone node in the scene - used for skeleton conversion
|
||||||
|
C_STRUCT aiNode* mNode;
|
||||||
|
|
||||||
//! 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;
|
||||||
|
|
||||||
|
@ -284,6 +293,11 @@ struct aiBone {
|
||||||
*/
|
*/
|
||||||
C_STRUCT aiMatrix4x4 mOffsetMatrix;
|
C_STRUCT aiMatrix4x4 mOffsetMatrix;
|
||||||
|
|
||||||
|
/** Matrix used for the global rest transform
|
||||||
|
* This tells you directly the rest without extending as required in most game engine implementations
|
||||||
|
* */
|
||||||
|
C_STRUCT aiMatrix4x4 mRestMatrix;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
||||||
//! Default constructor
|
//! Default constructor
|
||||||
|
@ -773,7 +787,10 @@ 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++) {
|
||||||
delete mBones[a];
|
if(mBones[a])
|
||||||
|
{
|
||||||
|
delete mBones[a];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
delete [] mBones;
|
delete [] mBones;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue