Skip to content

Commit 1184a68

Browse files
committed
IO_Assimp: fix handling of node scales
1 parent 3867266 commit 1184a68

File tree

2 files changed

+154
-77
lines changed

2 files changed

+154
-77
lines changed

src/io_assimp/io_assimp_reader.cpp

+154-74
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "../base/property.h"
1818
#include "../base/string_conv.h"
1919
#include "../base/task_progress.h"
20+
#include "../base/tkernel_utils.h"
2021
#include "../base/xcaf.h"
2122

2223
#include <assimp/scene.h>
@@ -29,6 +30,7 @@
2930
#include <cassert>
3031
#include <iostream>
3132

33+
#include <gp_Quaternion.hxx>
3234
#include <gp_Trsf.hxx>
3335
#include <BRep_Builder.hxx>
3436
#include <Image_Texture.hxx>
@@ -52,21 +54,11 @@ struct AssimpReaderI18N {
5254
// Retrieve the scaling component in assimp matrix 'trsf'
5355
aiVector3D aiMatrixScaling(const aiMatrix4x4& trsf)
5456
{
55-
const ai_real scalingX = aiVector3D(trsf.a1, trsf.a2, trsf.a3).Length();
56-
const ai_real scalingY = aiVector3D(trsf.b1, trsf.b2, trsf.b3).Length();
57-
const ai_real scalingZ = aiVector3D(trsf.c1, trsf.c2, trsf.c3).Length();
58-
return aiVector3D(scalingX, scalingY, scalingZ);
59-
}
60-
61-
bool hasScaleFactor(const gp_Trsf& trsf)
62-
{
63-
const double topLocationScalePrec =
64-
#if OCC_VERSION_HEX >= OCC_VERSION_CHECK(7, 6, 0)
65-
TopLoc_Location::ScalePrec();
66-
#else
67-
1.e-14;
68-
#endif
69-
return (Abs(Abs(trsf.ScaleFactor()) - 1.) > topLocationScalePrec) || trsf.IsNegative();
57+
aiVector3D scaling;
58+
aiQuaternion rotation;
59+
aiVector3D position;
60+
trsf.Decompose(scaling, rotation, position);
61+
return scaling;
7062
}
7163

7264
bool hasScaleFactor(const aiVector3D& scaling)
@@ -78,54 +70,142 @@ bool hasScaleFactor(const aiVector3D& scaling)
7870
);
7971
}
8072

81-
[[maybe_unused]] bool hasScaleFactor(const aiMatrix4x4& trsf)
73+
// Visit each node in Assimp tree and call 'fnCallback'
74+
void deep_aiNodeVisit(
75+
const aiNode* node,
76+
const std::function<void(const aiNode*)>& fnPreCallback,
77+
const std::function<void(const aiNode*)>& fnPostCallback = nullptr
78+
)
8279
{
83-
const aiVector3D scaling = aiMatrixScaling(trsf);
84-
return hasScaleFactor(scaling);
80+
if (fnPreCallback)
81+
fnPreCallback(node);
82+
83+
for (unsigned ichild = 0; ichild < node->mNumChildren; ++ichild)
84+
deep_aiNodeVisit(node->mChildren[ichild], fnPreCallback, fnPostCallback);
85+
86+
if (fnPostCallback)
87+
fnPostCallback(node);
8588
}
8689

8790
// Check if Assimp tree from 'node' contains a transformation having scale
8891
bool deep_aiNodeTransformationHasScaling(const aiNode* node)
8992
{
90-
const aiVector3D scaling = aiMatrixScaling(node->mTransformation);
91-
if (!MathUtils::fuzzyEqual(scaling.x, 1)
92-
|| !MathUtils::fuzzyEqual(scaling.y, 1)
93-
|| !MathUtils::fuzzyEqual(scaling.z, 1)
94-
|| scaling.x < 0. || scaling.y < 0. || scaling.z < 0.
95-
)
96-
{
97-
//std::cout << "[TRACE] hasScaling: " << scaling.x << " " << scaling.y << " " << scaling.z << std::endl;
98-
return true;
93+
bool hasScaling = false;
94+
deep_aiNodeVisit(node, [&](const aiNode* node) {
95+
if (!hasScaling) {
96+
const aiVector3D scaling = aiMatrixScaling(node->mTransformation);
97+
hasScaling =
98+
!MathUtils::fuzzyEqual(scaling.x, 1)
99+
|| !MathUtils::fuzzyEqual(scaling.y, 1)
100+
|| !MathUtils::fuzzyEqual(scaling.z, 1)
101+
|| scaling.x < 0. || scaling.y < 0. || scaling.z < 0.
102+
;
103+
}
104+
});
105+
106+
return hasScaling;
107+
}
108+
109+
void deep_aiScenePrint(std::ostream& outs, const aiScene* scene)
110+
{
111+
auto fnIndent = [](std::ostream& outs, int depth) -> std::ostream& {
112+
for (int i = 0; i < depth * 4; ++i)
113+
outs << ' ';
114+
return outs;
115+
};
116+
117+
outs << "#animation: " << scene->mNumAnimations << std::endl;
118+
outs << "#camera: " << scene->mNumCameras<< std::endl;
119+
outs << "#light: " << scene->mNumLights<< std::endl;
120+
outs << "#mesh: " << scene->mNumMeshes<< std::endl;
121+
outs << "#material: " << scene->mNumMaterials<< std::endl;
122+
outs << "#skeleton: " << scene->mNumSkeletons<< std::endl;
123+
outs << "#texture: " << scene->mNumTextures<< std::endl;
124+
outs << std::endl;
125+
126+
for (unsigned ianim = 0; ianim < scene->mNumAnimations; ++ianim) {
127+
const aiAnimation* anim = scene->mAnimations[ianim];
128+
outs << "Animation" << ianim << std::endl;
129+
outs << " name: '" << anim->mName.C_Str() << "'\n";
130+
outs << " duration: " << anim->mDuration << "\n";
131+
outs << " ticksPerSecond: " << anim->mTicksPerSecond << "\n";
132+
outs << " #channel: " << anim->mNumChannels << "\n";
133+
outs << " #meshChannel: " << anim->mNumMeshChannels << "\n";
134+
outs << " #morphMeshChannel: " << anim->mNumMorphMeshChannels << "\n";
135+
for (unsigned ichannel = 0; ichannel < anim->mNumChannels; ++ichannel) {
136+
const aiNodeAnim* iNodeAnim = anim->mChannels[ichannel];
137+
outs << " NodeAnim" << ichannel << "\n";
138+
outs << " nodeName: '" << iNodeAnim->mNodeName.C_Str() << "'\n";
139+
outs << " #posKey: " << iNodeAnim->mNumPositionKeys << "\n";
140+
outs << " #rotKey: " << iNodeAnim->mNumRotationKeys << "\n";
141+
outs << " #scaleKey: " << iNodeAnim->mNumScalingKeys << "\n";
142+
#if 0
143+
outs << " ScalingKeys" << "\n";
144+
for (unsigned iKey = 0; iKey < iNodeAnim->mNumScalingKeys; ++iKey) {
145+
const aiVector3D& vec = iNodeAnim->mScalingKeys[iKey].mValue;
146+
outs << " " << vec.x << ", " << vec.y << ", " << vec.z << "\n";
147+
}
148+
#endif
149+
}
150+
151+
outs << std::endl;
99152
}
100153

101-
for (unsigned ichild = 0; ichild < node->mNumChildren; ++ichild) {
102-
if (deep_aiNodeTransformationHasScaling(node->mChildren[ichild]))
103-
return true;
154+
for (unsigned imesh = 0; imesh < scene->mNumMeshes; ++imesh) {
155+
const aiMesh* mesh = scene->mMeshes[imesh];
156+
outs << "Mesh" << imesh << "\n"
157+
<< " name: '" << mesh->mName.C_Str() << "'\n"
158+
<< " materialid: " << mesh->mMaterialIndex << "\n"
159+
<< " #vert: " << mesh->mNumVertices << "\n"
160+
<< " #face: " << mesh->mNumFaces << "\n"
161+
<< " #bone: " << mesh->mNumBones << "\n";
162+
for (unsigned ibone = 0; ibone < mesh->mNumBones; ++ibone) {
163+
const aiBone* bone = mesh->mBones[ibone];
164+
outs << " Bone" << ibone << "\n"
165+
<< " name: '" << bone->mName.C_Str() << "'\n"
166+
<< " #weight: " << bone->mNumWeights
167+
<< "\n";
168+
}
104169
}
105170

106-
return false;
107-
}
171+
outs << "\nScene graph:\n" ;
172+
int nodeDepth = 0;
173+
deep_aiNodeVisit(
174+
scene->mRootNode,
175+
[&](const aiNode* node) {
176+
fnIndent(outs, nodeDepth);
177+
outs << node->mName.C_Str() << " {";
178+
if (node->mNumMeshes)
179+
outs << "#mesh:" << node->mNumMeshes << " ";
180+
181+
if (node->mNumChildren)
182+
outs << "#child:" << node->mNumChildren;
183+
184+
outs << "}\n";
185+
if (node->mMetaData) {
186+
fnIndent(outs, nodeDepth) << " ";
187+
outs << "->metada " << " #prop:" << node->mMetaData->mNumProperties;
188+
for (unsigned ikey = 0; ikey < node->mMetaData->mNumProperties; ++ikey) {
189+
outs << " key:'" << node->mMetaData->mKeys[ikey].C_Str() << "'";
190+
}
191+
192+
outs << "\n";
193+
}
108194

109-
// Visit each node in Assimp tree and call 'fnCallback'
110-
void deep_aiNodeVisit(const aiNode* node, const std::function<void(const aiNode*)>& fnCallback)
111-
{
112-
fnCallback(node);
113-
for (unsigned ichild = 0; ichild < node->mNumChildren; ++ichild)
114-
deep_aiNodeVisit(node->mChildren[ichild], fnCallback);
115-
}
195+
for (unsigned imesh = 0; imesh < node->mNumMeshes; ++imesh) {
196+
const aiMesh* mesh = scene->mMeshes[node->mMeshes[imesh]];
197+
fnIndent(outs, nodeDepth) << " ";
198+
outs << "->mesh" << imesh << " '" << mesh->mName.C_Str() << "'"
199+
<< " meshid:" << node->mMeshes[imesh]
200+
<< "\n";
201+
}
116202

117-
// Returns the OpenCascade transformation converted from assimp matrix
118-
gp_Trsf toOccTrsf(const aiMatrix4x4& matrix)
119-
{
120-
// TODO Check scaling != 0
121-
const aiVector3D scaling = aiMatrixScaling(matrix);
122-
gp_Trsf trsf;
123-
trsf.SetValues(
124-
matrix.a1 / scaling.x, matrix.a2 / scaling.x, matrix.a3 / scaling.x, matrix.a4,
125-
matrix.b1 / scaling.y, matrix.b2 / scaling.y, matrix.b3 / scaling.y, matrix.b4,
126-
matrix.c1 / scaling.z, matrix.c2 / scaling.z, matrix.c3 / scaling.z, matrix.c4
203+
++nodeDepth;
204+
},
205+
[&](const aiNode*) { --nodeDepth; }
127206
);
128-
return trsf;
207+
208+
outs << std::endl;
129209
}
130210

131211
// Returns the Quantity_Color object equivalent to assimp 'color'
@@ -239,7 +319,6 @@ bool AssimpReader::readFile(const FilePath& filepath, TaskProgress* progress)
239319
aiProcess_Triangulate
240320
| aiProcess_JoinIdenticalVertices
241321
//| aiProcess_SortByPType /* Crashes with assimp-5.3.1 on Windows */
242-
243322
//| aiProcess_OptimizeGraph
244323
//| aiProcess_TransformUVCoords
245324
//| aiProcess_FlipUVs
@@ -305,6 +384,9 @@ bool AssimpReader::readFile(const FilePath& filepath, TaskProgress* progress)
305384
m_vecMaterial.at(i) = this->createOccVisMaterial(material, filepath);
306385
}
307386

387+
#ifdef MAYO_ASSIMP_READER_HANDLE_SCALING
388+
deep_aiScenePrint(std::cout, m_scene);
389+
#endif
308390
return true;
309391
}
310392

@@ -314,21 +396,20 @@ TDF_LabelSequence AssimpReader::transfer(DocumentPtr doc, TaskProgress* progress
314396
return {};
315397

316398
m_mapNodeData.clear();
399+
317400
// Compute data for each aiNode object in the scene
318401
deep_aiNodeVisit(m_scene->mRootNode, [=](const aiNode* node) {
319402
aiNodeData nodeData;
320403
auto itParentData = m_mapNodeData.find(node->mParent);
321404
if (itParentData != m_mapNodeData.cend()) {
322405
const aiNodeData& parentData = itParentData->second;
323406
nodeData.aiAbsoluteTrsf = parentData.aiAbsoluteTrsf * node->mTransformation;
324-
nodeData.occAbsoluteTrsf = parentData.occAbsoluteTrsf * toOccTrsf(node->mTransformation);
325407
}
326408
else {
327409
nodeData.aiAbsoluteTrsf = node->mTransformation;
328-
nodeData.occAbsoluteTrsf = toOccTrsf(node->mTransformation);
329410
}
330411

331-
m_mapNodeData.insert({ node, nodeData });
412+
m_mapNodeData.insert_or_assign(node, std::move(nodeData));
332413
});
333414

334415
// Compute count of meshes in the scene
@@ -371,7 +452,7 @@ void AssimpReader::applyProperties(const PropertyGroup* group)
371452
}
372453

373454
OccHandle<Image_Texture> AssimpReader::findOccTexture(
374-
const std::string& strFilepath, const FilePath& modelFilepath
455+
const std::string& strFilepath, const FilePath& modelFilepath
375456
)
376457
{
377458
// Texture might be embedded
@@ -426,7 +507,7 @@ OccHandle<Image_Texture> AssimpReader::findOccTexture(
426507
}
427508

428509
OccHandle<XCAFDoc_VisMaterial> AssimpReader::createOccVisMaterial(
429-
const aiMaterial* material, const FilePath& modelFilepath
510+
const aiMaterial* material, const FilePath& modelFilepath
430511
)
431512
{
432513
auto mat = makeOccHandle<XCAFDoc_VisMaterial>();
@@ -610,10 +691,10 @@ OccHandle<XCAFDoc_VisMaterial> AssimpReader::createOccVisMaterial(
610691
}
611692

612693
void AssimpReader::transferSceneNode(
613-
const aiNode* node,
614-
DocumentPtr targetDoc,
615-
const TDF_Label& labelEntity,
616-
const std::function<void(const aiMesh*)>& fnCallbackMesh
694+
const aiNode* node,
695+
DocumentPtr targetDoc,
696+
const TDF_Label& labelEntity,
697+
const std::function<void(const aiMesh*)>& fnCallbackMesh
617698
)
618699
{
619700
if (!node)
@@ -622,14 +703,14 @@ void AssimpReader::transferSceneNode(
622703
const std::string nodeName = node->mName.C_Str();
623704

624705
const aiNodeData nodeData = Cpp::findValue(node, m_mapNodeData);
625-
gp_Trsf nodeAbsoluteTrsf = nodeData.occAbsoluteTrsf;
626-
if (hasScaleFactor(nodeAbsoluteTrsf))
627-
nodeAbsoluteTrsf.SetScaleFactor(1.);
706+
aiVector3D nodeScale;
707+
aiQuaternion nodeRot;
708+
aiVector3D nodePos;
709+
nodeData.aiAbsoluteTrsf.Decompose(nodeScale, nodeRot, nodePos);
628710

629-
#ifdef MAYO_ASSIMP_READER_HANDLE_SCALING
630-
const aiVector3D nodeScaling = aiMatrixScaling(nodeData.aiAbsoluteTrsf);
631-
const bool nodeHasScaling = hasScaleFactor(nodeScaling);
632-
#endif
711+
gp_Trsf nodeAbsoluteTrsf;
712+
nodeAbsoluteTrsf.SetRotationPart(gp_Quaternion{nodeRot.x, nodeRot.y, nodeRot.z, nodeRot.w});
713+
nodeAbsoluteTrsf.SetTranslationPart(gp_Vec{nodePos.x, nodePos.y, nodePos.z});
633714

634715
// Produce shape corresponding to the node
635716
for (unsigned imesh = 0; imesh < node->mNumMeshes; ++imesh) {
@@ -641,14 +722,13 @@ void AssimpReader::transferSceneNode(
641722
continue; // Skip
642723

643724
#ifdef MAYO_ASSIMP_READER_HANDLE_SCALING
644-
if (nodeHasScaling) {
725+
if (hasScaleFactor(nodeScale)) {
645726
triangulation = triangulation->Copy();
646-
for (int i = 1; i < triangulation->NbNodes(); ++i) {
647-
gp_Pnt pnt = triangulation->Node(i);
648-
pnt.SetX(pnt.X() * nodeScaling.x);
649-
pnt.SetY(pnt.Y() * nodeScaling.y);
650-
pnt.SetZ(pnt.Z() * nodeScaling.z);
651-
MeshUtils::setNode(triangulation, i , pnt);
727+
for (int i = 1; i <= triangulation->NbNodes(); ++i) {
728+
const gp_Pnt pnt = triangulation->Node(i);
729+
MeshUtils::setNode(
730+
triangulation, i, gp_Pnt{ pnt.X() * nodeScale.x, pnt.Y() * nodeScale.y, pnt.Z() * nodeScale.z }
731+
);
652732
}
653733
}
654734
#endif

src/io_assimp/io_assimp_reader.h

-3
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,8 @@
66

77
#pragma once
88

9-
#include "../base/document_ptr.h"
109
#include "../base/io_reader.h"
1110
#include "../base/occ_handle.h"
12-
#include "../base/tkernel_utils.h"
1311

1412
#include <assimp/Importer.hpp>
1513

@@ -57,7 +55,6 @@ class AssimpReader : public Reader {
5755
);
5856

5957
struct aiNodeData {
60-
gp_Trsf occAbsoluteTrsf;
6158
aiMatrix4x4 aiAbsoluteTrsf;
6259
};
6360

0 commit comments

Comments
 (0)