Assimp FBX Import support

Issues fixed:
- Updated assimp to latest and backported fixes into godot.
- Fixed file scale being ignored from FBX file.
- Fixed bone removal
- Implemented proper armature binding
- Fixed recursion not always going through the entire path
- Implemented assimp global scaling system
- Fixed assimp global scale process to support unit conversion
- Implemented proper fbx scaling
- Fixed asserts caused by missing faces in some models which could crash
- Fixed valid bone removal
- Fixed root node being overwriten by assimp which caused data loss
- Fixed armature construction so that it works with multiple roots
- Implemented basic support for FBX standard materials
- Refactoring to improve code quality and improve function reuse.
- Simplified node creation from assimp scene into subsections: create_light, create_mesh, create_bone.
- Creating meshes is now done after hierarchy is created so that the skeleton is always available.
- Added support to assimp to support file scale in all formats which call SetFileScale.
- Many other fixes provided into assimp.

Known issues:
- FBX pivots from Maya do not currently work. (workaround: for now use blender import and export to remove pivot tracks)
- Hierarchy creates an extra node for each mesh - this was done intentionally but we intended to do a pass to remove these as they're a required node.
- When an animated mesh has not executed any animation the rest pose is wrong.

Co-authored-by: K. S. Ernest (iFire) Lee <ernest.lee@chibifire.com>
This commit is contained in:
Gordon MacPherson 2019-08-30 02:21:40 +01:00
parent a5e0aa32d9
commit ad214c0356
22 changed files with 3267 additions and 1504 deletions

View file

@ -76,9 +76,25 @@ BaseImporter::~BaseImporter() {
// nothing to do here
}
void BaseImporter::UpdateImporterScale( Importer* pImp )
{
ai_assert(pImp != nullptr);
ai_assert(importerScale != 0.0);
ai_assert(fileScale != 0.0);
double activeScale = importerScale * fileScale;
// Set active scaling
pImp->SetPropertyFloat( AI_CONFIG_APP_SCALE_KEY, activeScale);
ASSIMP_LOG_DEBUG_F("UpdateImporterScale scale set: %f", activeScale );
}
// ------------------------------------------------------------------------------------------------
// Imports the given file and returns the imported data.
aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile, IOSystem* pIOHandler) {
aiScene* BaseImporter::ReadFile(Importer* pImp, const std::string& pFile, IOSystem* pIOHandler) {
m_progress = pImp->GetProgressHandler();
if (nullptr == m_progress) {
return nullptr;
@ -100,6 +116,11 @@ aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile,
{
InternReadFile( pFile, sc.get(), &filter);
// Calculate import scale hook - required because pImp not available anywhere else
// passes scale into ScaleProcess
UpdateImporterScale(pImp);
} catch( const std::exception& err ) {
// extract error description
m_ErrorText = err.what();
@ -112,7 +133,7 @@ aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile,
}
// ------------------------------------------------------------------------------------------------
void BaseImporter::SetupProperties(const Importer* /*pImp*/)
void BaseImporter::SetupProperties(const Importer* pImp)
{
// the default implementation does nothing
}
@ -588,6 +609,8 @@ aiScene* BatchLoader::GetImport( unsigned int which )
return nullptr;
}
// ------------------------------------------------------------------------------------------------
void BatchLoader::LoadAll()
{

View file

@ -66,6 +66,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <vector>
#include <sstream>
#include <iomanip>
#include <cstdint>
namespace Assimp {
@ -90,7 +91,6 @@ namespace Assimp {
, anim_fps()
, out(out)
, doc(doc)
, mRemoveEmptyBones( removeEmptyBones )
, mCurrentUnit(FbxUnit::cm) {
// animations need to be converted first since this will
// populate the node_anim_chain_bits map, which is needed
@ -119,7 +119,6 @@ namespace Assimp {
ConvertGlobalSettings();
TransferDataToScene();
ConvertToUnitScale(unit);
// if we didn't read any meshes set the AI_SCENE_FLAGS_INCOMPLETE
// to make sure the scene passes assimp's validation. FBX files
@ -685,30 +684,37 @@ namespace Assimp {
bool ok;
aiMatrix4x4 chain[TransformationComp_MAXIMUM];
ai_assert(TransformationComp_MAXIMUM < 32);
std::uint32_t chainBits = 0;
// A node won't need a node chain if it only has these.
const std::uint32_t chainMaskSimple = (1 << TransformationComp_Translation) + (1 << TransformationComp_Scaling) + (1 << TransformationComp_Rotation);
// A node will need a node chain if it has any of these.
const std::uint32_t chainMaskComplex = ((1 << (TransformationComp_MAXIMUM)) - 1) - chainMaskSimple;
std::fill_n(chain, static_cast<unsigned int>(TransformationComp_MAXIMUM), aiMatrix4x4());
// generate transformation matrices for all the different transformation components
const float zero_epsilon = 1e-6f;
const aiVector3D all_ones(1.0f, 1.0f, 1.0f);
bool is_complex = false;
const aiVector3D& PreRotation = PropertyGet<aiVector3D>(props, "PreRotation", ok);
if (ok && PreRotation.SquareLength() > zero_epsilon) {
is_complex = true;
chainBits = chainBits | (1 << TransformationComp_PreRotation);
GetRotationMatrix(Model::RotOrder::RotOrder_EulerXYZ, PreRotation, chain[TransformationComp_PreRotation]);
}
const aiVector3D& PostRotation = PropertyGet<aiVector3D>(props, "PostRotation", ok);
if (ok && PostRotation.SquareLength() > zero_epsilon) {
is_complex = true;
chainBits = chainBits | (1 << TransformationComp_PostRotation);
GetRotationMatrix(Model::RotOrder::RotOrder_EulerXYZ, PostRotation, chain[TransformationComp_PostRotation]);
}
const aiVector3D& RotationPivot = PropertyGet<aiVector3D>(props, "RotationPivot", ok);
if (ok && RotationPivot.SquareLength() > zero_epsilon) {
is_complex = true;
chainBits = chainBits | (1 << TransformationComp_RotationPivot) | (1 << TransformationComp_RotationPivotInverse);
aiMatrix4x4::Translation(RotationPivot, chain[TransformationComp_RotationPivot]);
aiMatrix4x4::Translation(-RotationPivot, chain[TransformationComp_RotationPivotInverse]);
@ -716,21 +722,21 @@ namespace Assimp {
const aiVector3D& RotationOffset = PropertyGet<aiVector3D>(props, "RotationOffset", ok);
if (ok && RotationOffset.SquareLength() > zero_epsilon) {
is_complex = true;
chainBits = chainBits | (1 << TransformationComp_RotationOffset);
aiMatrix4x4::Translation(RotationOffset, chain[TransformationComp_RotationOffset]);
}
const aiVector3D& ScalingOffset = PropertyGet<aiVector3D>(props, "ScalingOffset", ok);
if (ok && ScalingOffset.SquareLength() > zero_epsilon) {
is_complex = true;
chainBits = chainBits | (1 << TransformationComp_ScalingOffset);
aiMatrix4x4::Translation(ScalingOffset, chain[TransformationComp_ScalingOffset]);
}
const aiVector3D& ScalingPivot = PropertyGet<aiVector3D>(props, "ScalingPivot", ok);
if (ok && ScalingPivot.SquareLength() > zero_epsilon) {
is_complex = true;
chainBits = chainBits | (1 << TransformationComp_ScalingPivot) | (1 << TransformationComp_ScalingPivotInverse);
aiMatrix4x4::Translation(ScalingPivot, chain[TransformationComp_ScalingPivot]);
aiMatrix4x4::Translation(-ScalingPivot, chain[TransformationComp_ScalingPivotInverse]);
@ -738,22 +744,28 @@ namespace Assimp {
const aiVector3D& Translation = PropertyGet<aiVector3D>(props, "Lcl Translation", ok);
if (ok && Translation.SquareLength() > zero_epsilon) {
chainBits = chainBits | (1 << TransformationComp_Translation);
aiMatrix4x4::Translation(Translation, chain[TransformationComp_Translation]);
}
const aiVector3D& Scaling = PropertyGet<aiVector3D>(props, "Lcl Scaling", ok);
if (ok && (Scaling - all_ones).SquareLength() > zero_epsilon) {
chainBits = chainBits | (1 << TransformationComp_Scaling);
aiMatrix4x4::Scaling(Scaling, chain[TransformationComp_Scaling]);
}
const aiVector3D& Rotation = PropertyGet<aiVector3D>(props, "Lcl Rotation", ok);
if (ok && Rotation.SquareLength() > zero_epsilon) {
chainBits = chainBits | (1 << TransformationComp_Rotation);
GetRotationMatrix(rot, Rotation, chain[TransformationComp_Rotation]);
}
const aiVector3D& GeometricScaling = PropertyGet<aiVector3D>(props, "GeometricScaling", ok);
if (ok && (GeometricScaling - all_ones).SquareLength() > zero_epsilon) {
is_complex = true;
chainBits = chainBits | (1 << TransformationComp_GeometricScaling);
aiMatrix4x4::Scaling(GeometricScaling, chain[TransformationComp_GeometricScaling]);
aiVector3D GeometricScalingInverse = GeometricScaling;
bool canscale = true;
@ -768,13 +780,14 @@ namespace Assimp {
}
}
if (canscale) {
chainBits = chainBits | (1 << TransformationComp_GeometricScalingInverse);
aiMatrix4x4::Scaling(GeometricScalingInverse, chain[TransformationComp_GeometricScalingInverse]);
}
}
const aiVector3D& GeometricRotation = PropertyGet<aiVector3D>(props, "GeometricRotation", ok);
if (ok && GeometricRotation.SquareLength() > zero_epsilon) {
is_complex = true;
chainBits = chainBits | (1 << TransformationComp_GeometricRotation) | (1 << TransformationComp_GeometricRotationInverse);
GetRotationMatrix(rot, GeometricRotation, chain[TransformationComp_GeometricRotation]);
GetRotationMatrix(rot, GeometricRotation, chain[TransformationComp_GeometricRotationInverse]);
chain[TransformationComp_GeometricRotationInverse].Inverse();
@ -782,7 +795,7 @@ namespace Assimp {
const aiVector3D& GeometricTranslation = PropertyGet<aiVector3D>(props, "GeometricTranslation", ok);
if (ok && GeometricTranslation.SquareLength() > zero_epsilon) {
is_complex = true;
chainBits = chainBits | (1 << TransformationComp_GeometricTranslation) | (1 << TransformationComp_GeometricTranslationInverse);
aiMatrix4x4::Translation(GeometricTranslation, chain[TransformationComp_GeometricTranslation]);
aiMatrix4x4::Translation(-GeometricTranslation, chain[TransformationComp_GeometricTranslationInverse]);
}
@ -790,12 +803,12 @@ namespace Assimp {
// is_complex needs to be consistent with NeedsComplexTransformationChain()
// or the interplay between this code and the animation converter would
// not be guaranteed.
ai_assert(NeedsComplexTransformationChain(model) == is_complex);
ai_assert(NeedsComplexTransformationChain(model) == ((chainBits & chainMaskComplex) != 0));
// now, if we have more than just Translation, Scaling and Rotation,
// we need to generate a full node chain to accommodate for assimp's
// lack to express pivots and offsets.
if (is_complex && doc.Settings().preservePivots) {
if ((chainBits & chainMaskComplex) && doc.Settings().preservePivots) {
FBXImporter::LogInfo("generating full transformation chain for node: " + name);
// query the anim_chain_bits dictionary to find out which chain elements
@ -808,7 +821,7 @@ namespace Assimp {
for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i, bit <<= 1) {
const TransformationComp comp = static_cast<TransformationComp>(i);
if (chain[i].IsIdentity() && (anim_chain_bitmask & bit) == 0) {
if ((chainBits & bit) == 0 && (anim_chain_bitmask & bit) == 0) {
continue;
}
@ -1462,14 +1475,8 @@ namespace Assimp {
const WeightIndexArray& indices = cluster->GetIndices();
if (indices.empty() && mRemoveEmptyBones ) {
continue;
}
const MatIndexArray& mats = geo.GetMaterialIndices();
bool ok = false;
const size_t no_index_sentinel = std::numeric_limits<size_t>::max();
count_out_indices.clear();
@ -1509,8 +1516,7 @@ namespace Assimp {
out_indices.push_back(std::distance(outputVertStartIndices->begin(), it));
}
++count_out_indices.back();
ok = true;
++count_out_indices.back();
}
}
}
@ -1518,10 +1524,8 @@ namespace Assimp {
// if we found at least one, generate the output bones
// XXX this could be heavily simplified by collecting the bone
// data in a single step.
if (ok && mRemoveEmptyBones) {
ConvertCluster(bones, model, *cluster, out_indices, index_out_indices,
ConvertCluster(bones, model, *cluster, out_indices, index_out_indices,
count_out_indices, node_global_transform);
}
}
}
catch (std::exception&) {
@ -3532,46 +3536,6 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
out->mMetaData->Set(14, "CustomFrameRate", doc.GlobalSettings().CustomFrameRate());
}
void FBXConverter::ConvertToUnitScale( FbxUnit unit ) {
if (mCurrentUnit == unit) {
return;
}
ai_real scale = 1.0;
if (mCurrentUnit == FbxUnit::cm) {
if (unit == FbxUnit::m) {
scale = (ai_real)0.01;
} else if (unit == FbxUnit::km) {
scale = (ai_real)0.00001;
}
} else if (mCurrentUnit == FbxUnit::m) {
if (unit == FbxUnit::cm) {
scale = (ai_real)100.0;
} else if (unit == FbxUnit::km) {
scale = (ai_real)0.001;
}
} else if (mCurrentUnit == FbxUnit::km) {
if (unit == FbxUnit::cm) {
scale = (ai_real)100000.0;
} else if (unit == FbxUnit::m) {
scale = (ai_real)1000.0;
}
}
for (auto mesh : meshes) {
if (nullptr == mesh) {
continue;
}
if (mesh->HasPositions()) {
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
aiVector3D &pos = mesh->mVertices[i];
pos *= scale;
}
}
}
}
void FBXConverter::TransferDataToScene()
{
ai_assert(!out->mMeshes);

View file

@ -430,10 +430,6 @@ private:
void ConvertGlobalSettings();
// ------------------------------------------------------------------------------------------------
// Will perform the conversion from a given unit to the requested unit.
void ConvertToUnitScale(FbxUnit unit);
// ------------------------------------------------------------------------------------------------
// copy generated meshes, animations, lights, cameras and textures to the output scene
void TransferDataToScene();
@ -470,9 +466,6 @@ private:
aiScene* const out;
const FBX::Document& doc;
bool mRemoveEmptyBones;
FbxUnit mCurrentUnit;
};

View file

@ -90,14 +90,6 @@ const Object* LazyObject::Get(bool dieOnError)
return object.get();
}
// if this is the root object, we return a dummy since there
// is no root object int he fbx file - it is just referenced
// with id 0.
if(id == 0L) {
object.reset(new Object(id, element, "Model::RootNode"));
return object.get();
}
const Token& key = element.KeyToken();
const TokenList& tokens = element.Tokens();

View file

@ -1706,8 +1706,7 @@ void FBXExporter::WriteObjects ()
}
if (end) { break; }
}
limbnodes.insert(parent);
skeleton.insert(parent);
// if it was the skeleton root we can finish here
if (end) { break; }
}
@ -1848,44 +1847,10 @@ void FBXExporter::WriteObjects ()
inverse_bone_xform.Inverse();
aiMatrix4x4 tr = inverse_bone_xform * mesh_xform;
// this should be the same as the bone's mOffsetMatrix.
// if it's not the same, the skeleton isn't in the bind pose.
float epsilon = 1e-4f; // some error is to be expected
float epsilon_custom = mProperties->GetPropertyFloat("BINDPOSE_EPSILON", -1);
if(epsilon_custom > 0)
epsilon = epsilon_custom;
bool bone_xform_okay = true;
if (b && ! tr.Equal(b->mOffsetMatrix, epsilon)) {
not_in_bind_pose.insert(b);
bone_xform_okay = false;
}
sdnode.AddChild("Transform", tr);
// if we have a bone we should use the mOffsetMatrix,
// otherwise try to just use the calculated transform.
if (b) {
sdnode.AddChild("Transform", b->mOffsetMatrix);
} else {
sdnode.AddChild("Transform", tr);
}
// note: it doesn't matter if we mix these,
// because if they disagree we'll throw an exception later.
// it could be that the skeleton is not in the bone pose
// but all bones are still defined,
// in which case this would use the mOffsetMatrix for everything
// and a correct skeleton would still be output.
// transformlink should be the position of the bone in world space.
// if the bone is in the bind pose (or nonexistent),
// we can just use the matrix we already calculated
if (bone_xform_okay) {
sdnode.AddChild("TransformLink", bone_xform);
// otherwise we can only work it out using the mesh position.
} else {
aiMatrix4x4 trl = b->mOffsetMatrix;
trl.Inverse();
trl *= mesh_xform;
sdnode.AddChild("TransformLink", trl);
}
sdnode.AddChild("TransformLink", bone_xform);
// note: this means we ALWAYS rely on the mesh node transform
// being unchanged from the time the skeleton was bound.
// there's not really any way around this at the moment.

View file

@ -189,8 +189,16 @@ void FBXImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
if (settings.convertToMeters) {
unit = FbxUnit::m;
}
// convert the FBX DOM to aiScene
ConvertToAssimpScene(pScene,doc, settings.removeEmptyBones, unit);
ConvertToAssimpScene(pScene, doc, settings.removeEmptyBones, unit);
// size relative to cm
float size_relative_to_cm = doc.GlobalSettings().UnitScaleFactor();
// Set FBX file scale is relative to CM must be converted to M for
// assimp universal format (M)
SetFileScale( size_relative_to_cm * 0.01f);
std::for_each(tokens.begin(),tokens.end(),Util::delete_fun<Token>());
}

View file

@ -115,7 +115,6 @@ MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::strin
if(tempVerts.empty()) {
FBXImporter::LogWarn("encountered mesh with no vertices");
return;
}
std::vector<int> tempFaces;
@ -123,7 +122,6 @@ MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::strin
if(tempFaces.empty()) {
FBXImporter::LogWarn("encountered mesh with no faces");
return;
}
m_vertices.reserve(tempFaces.size());
@ -612,7 +610,10 @@ void MeshGeometry::ReadVertexDataMaterials(std::vector<int>& materials_out, cons
const std::string& ReferenceInformationType)
{
const size_t face_count = m_faces.size();
ai_assert(face_count);
if(face_count <= 0)
{
return;
}
// materials are handled separately. First of all, they are assigned per-face
// and not per polyvert. Secondly, ReferenceInformationType=IndexToDirect

View file

@ -212,7 +212,7 @@ bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
// project tangent and bitangent into the plane formed by the vertex' normal
aiVector3D localTangent = tangent - meshNorm[p] * (tangent * meshNorm[p]);
aiVector3D localBitangent = bitangent - meshNorm[p] * (bitangent * meshNorm[p]);
localTangent.Normalize(); localBitangent.Normalize();
localTangent.NormalizeSafe(); localBitangent.NormalizeSafe();
// reconstruct tangent/bitangent according to normal and bitangent/tangent when it's infinite or NaN.
bool invalid_tangent = is_special_float(localTangent.x) || is_special_float(localTangent.y) || is_special_float(localTangent.z);
@ -220,10 +220,10 @@ bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
if (invalid_tangent != invalid_bitangent) {
if (invalid_tangent) {
localTangent = meshNorm[p] ^ localBitangent;
localTangent.Normalize();
localTangent.NormalizeSafe();
} else {
localBitangent = localTangent ^ meshNorm[p];
localBitangent.Normalize();
localBitangent.NormalizeSafe();
}
}

View file

@ -39,19 +39,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
#ifndef ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS
#include "ScaleProcess.h"
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include <assimp/BaseImporter.h>
namespace Assimp {
ScaleProcess::ScaleProcess()
: BaseProcess()
, mScale( AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT ) {
// empty
}
ScaleProcess::~ScaleProcess() {
@ -71,10 +69,26 @@ bool ScaleProcess::IsActive( unsigned int pFlags ) const {
}
void ScaleProcess::SetupProperties( const Importer* pImp ) {
mScale = pImp->GetPropertyFloat( AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY, 0 );
// User scaling
mScale = pImp->GetPropertyFloat( AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY, 1.0f );
// File scaling * Application Scaling
float importerScale = pImp->GetPropertyFloat( AI_CONFIG_APP_SCALE_KEY, 1.0f );
// apply scale to the scale
// helps prevent bugs with backward compatibility for anyone using normal scaling.
mScale *= importerScale;
}
void ScaleProcess::Execute( aiScene* pScene ) {
if(mScale == 1.0f) {
return; // nothing to scale
}
ai_assert( mScale != 0 );
ai_assert( nullptr != pScene );
ai_assert( nullptr != pScene->mRootNode );
if ( nullptr == pScene ) {
return;
}
@ -82,22 +96,113 @@ void ScaleProcess::Execute( aiScene* pScene ) {
if ( nullptr == pScene->mRootNode ) {
return;
}
// Process animations and update position transform to new unit system
for( unsigned int animationID = 0; animationID < pScene->mNumAnimations; animationID++ )
{
aiAnimation* animation = pScene->mAnimations[animationID];
for( unsigned int animationChannel = 0; animationChannel < animation->mNumChannels; animationChannel++)
{
aiNodeAnim* anim = animation->mChannels[animationChannel];
for( unsigned int posKey = 0; posKey < anim->mNumPositionKeys; posKey++)
{
aiVectorKey& vectorKey = anim->mPositionKeys[posKey];
vectorKey.mValue *= mScale;
}
}
}
for( unsigned int meshID = 0; meshID < pScene->mNumMeshes; meshID++)
{
aiMesh *mesh = pScene->mMeshes[meshID];
// Reconstruct mesh vertexes to the new unit system
for( unsigned int vertexID = 0; vertexID < mesh->mNumVertices; vertexID++)
{
aiVector3D& vertex = mesh->mVertices[vertexID];
vertex *= mScale;
}
// bone placement / scaling
for( unsigned int boneID = 0; boneID < mesh->mNumBones; boneID++)
{
// Reconstruct matrix by transform rather than by scale
// This prevent scale values being changed which can
// be meaningful in some cases
// like when you want the modeller to see 1:1 compatibility.
aiBone* bone = mesh->mBones[boneID];
aiVector3D pos, scale;
aiQuaternion rotation;
bone->mOffsetMatrix.Decompose( scale, rotation, pos);
aiMatrix4x4 translation;
aiMatrix4x4::Translation( pos * mScale, translation );
aiMatrix4x4 scaling;
aiMatrix4x4::Scaling( aiVector3D(scale), scaling );
aiMatrix4x4 RotMatrix = aiMatrix4x4 (rotation.GetMatrix());
bone->mOffsetMatrix = translation * RotMatrix * scaling;
}
// animation mesh processing
// convert by position rather than scale.
for( unsigned int animMeshID = 0; animMeshID < mesh->mNumAnimMeshes; animMeshID++)
{
aiAnimMesh * animMesh = mesh->mAnimMeshes[animMeshID];
for( unsigned int vertexID = 0; vertexID < animMesh->mNumVertices; vertexID++)
{
aiVector3D& vertex = animMesh->mVertices[vertexID];
vertex *= mScale;
}
}
}
traverseNodes( pScene->mRootNode );
}
void ScaleProcess::traverseNodes( aiNode *node ) {
void ScaleProcess::traverseNodes( aiNode *node, unsigned int nested_node_id ) {
applyScaling( node );
for( size_t i = 0; i < node->mNumChildren; i++)
{
// recurse into the tree until we are done!
traverseNodes( node->mChildren[i], nested_node_id+1 );
}
}
void ScaleProcess::applyScaling( aiNode *currentNode ) {
if ( nullptr != currentNode ) {
currentNode->mTransformation.a1 = currentNode->mTransformation.a1 * mScale;
currentNode->mTransformation.b2 = currentNode->mTransformation.b2 * mScale;
currentNode->mTransformation.c3 = currentNode->mTransformation.c3 * mScale;
// Reconstruct matrix by transform rather than by scale
// This prevent scale values being changed which can
// be meaningful in some cases
// like when you want the modeller to
// see 1:1 compatibility.
aiVector3D pos, scale;
aiQuaternion rotation;
currentNode->mTransformation.Decompose( scale, rotation, pos);
aiMatrix4x4 translation;
aiMatrix4x4::Translation( pos * mScale, translation );
aiMatrix4x4 scaling;
// note: we do not use mScale here, this is on purpose.
aiMatrix4x4::Scaling( scale, scaling );
aiMatrix4x4 RotMatrix = aiMatrix4x4 (rotation.GetMatrix());
currentNode->mTransformation = translation * RotMatrix * scaling;
}
}
} // Namespace Assimp
#endif // !! ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS

View file

@ -39,7 +39,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
#pragma once
#ifndef SCALE_PROCESS_H_
#define SCALE_PROCESS_H_
#include "Common/BaseProcess.h"
@ -53,6 +54,11 @@ namespace Assimp {
// ---------------------------------------------------------------------------
/** ScaleProcess: Class to rescale the whole model.
* Now rescales animations, bones, and blend shapes properly.
* Please note this will not write to 'scale' transform it will rewrite mesh
* and matrixes so that your scale values
* from your model package are preserved, so this is completely intentional
* bugs should be reported as soon as they are found.
*/
class ASSIMP_API ScaleProcess : public BaseProcess {
public:
@ -78,7 +84,7 @@ public:
virtual void Execute( aiScene* pScene );
private:
void traverseNodes( aiNode *currentNode );
void traverseNodes( aiNode *currentNode, unsigned int nested_node_id = 0 );
void applyScaling( aiNode *currentNode );
private:
@ -86,3 +92,6 @@ private:
};
} // Namespace Assimp
#endif // SCALE_PROCESS_H_