1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5 
6 Copyright (c) 2006-2019, assimp team
7 
8 
9 
10 All rights reserved.
11 
12 Redistribution and use of this software in source and binary forms,
13 with or without modification, are permitted provided that the following
14 conditions are met:
15 
16 * Redistributions of source code must retain the above
17   copyright notice, this list of conditions and the
18   following disclaimer.
19 
20 * Redistributions in binary form must reproduce the above
21   copyright notice, this list of conditions and the
22   following disclaimer in the documentation and/or other
23   materials provided with the distribution.
24 
25 * Neither the name of the assimp team, nor the names of its
26   contributors may be used to endorse or promote products
27   derived from this software without specific prior
28   written permission of the assimp team.
29 
30 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
31 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
32 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
33 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
34 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
36 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
37 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
38 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
40 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 ---------------------------------------------------------------------------
42 */
43 
44 /** @file  MakeLeftHandedProcess.cpp
45  *  @brief Implementation of the post processing step to convert all
46  *  imported data to a left-handed coordinate system.
47  *
48  *  Face order & UV flip are also implemented here, for the sake of a
49  *  better location.
50  */
51 
52 
53 #include "ConvertToLHProcess.h"
54 #include <assimp/scene.h>
55 #include <assimp/postprocess.h>
56 #include <assimp/DefaultLogger.hpp>
57 
58 using namespace Assimp;
59 
60 #ifndef ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS
61 
62 namespace {
63 
64 template <typename aiMeshType>
flipUVs(aiMeshType * pMesh)65 void flipUVs(aiMeshType* pMesh) {
66     if (pMesh == nullptr) { return; }
67     // mirror texture y coordinate
68     for (unsigned int tcIdx = 0; tcIdx < AI_MAX_NUMBER_OF_TEXTURECOORDS; tcIdx++) {
69         if (!pMesh->HasTextureCoords(tcIdx)) {
70             break;
71         }
72 
73         for (unsigned int vIdx = 0; vIdx < pMesh->mNumVertices; vIdx++) {
74             pMesh->mTextureCoords[tcIdx][vIdx].y = 1.0f - pMesh->mTextureCoords[tcIdx][vIdx].y;
75         }
76     }
77 }
78 
79 } // namespace
80 
81 // ------------------------------------------------------------------------------------------------
82 // Constructor to be privately used by Importer
MakeLeftHandedProcess()83 MakeLeftHandedProcess::MakeLeftHandedProcess()
84 : BaseProcess() {
85     // empty
86 }
87 
88 // ------------------------------------------------------------------------------------------------
89 // Destructor, private as well
~MakeLeftHandedProcess()90 MakeLeftHandedProcess::~MakeLeftHandedProcess() {
91     // empty
92 }
93 
94 // ------------------------------------------------------------------------------------------------
95 // Returns whether the processing step is present in the given flag field.
IsActive(unsigned int pFlags) const96 bool MakeLeftHandedProcess::IsActive( unsigned int pFlags) const
97 {
98     return 0 != (pFlags & aiProcess_MakeLeftHanded);
99 }
100 
101 // ------------------------------------------------------------------------------------------------
102 // Executes the post processing step on the given imported data.
Execute(aiScene * pScene)103 void MakeLeftHandedProcess::Execute( aiScene* pScene)
104 {
105     // Check for an existent root node to proceed
106     ai_assert(pScene->mRootNode != NULL);
107     ASSIMP_LOG_DEBUG("MakeLeftHandedProcess begin");
108 
109     // recursively convert all the nodes
110     ProcessNode( pScene->mRootNode, aiMatrix4x4());
111 
112     // process the meshes accordingly
113     for ( unsigned int a = 0; a < pScene->mNumMeshes; ++a ) {
114         ProcessMesh( pScene->mMeshes[ a ] );
115     }
116 
117     // process the materials accordingly
118     for ( unsigned int a = 0; a < pScene->mNumMaterials; ++a ) {
119         ProcessMaterial( pScene->mMaterials[ a ] );
120     }
121 
122     // transform all animation channels as well
123     for( unsigned int a = 0; a < pScene->mNumAnimations; a++)
124     {
125         aiAnimation* anim = pScene->mAnimations[a];
126         for( unsigned int b = 0; b < anim->mNumChannels; b++)
127         {
128             aiNodeAnim* nodeAnim = anim->mChannels[b];
129             ProcessAnimation( nodeAnim);
130         }
131     }
132     ASSIMP_LOG_DEBUG("MakeLeftHandedProcess finished");
133 }
134 
135 // ------------------------------------------------------------------------------------------------
136 // Recursively converts a node, all of its children and all of its meshes
ProcessNode(aiNode * pNode,const aiMatrix4x4 & pParentGlobalRotation)137 void MakeLeftHandedProcess::ProcessNode( aiNode* pNode, const aiMatrix4x4& pParentGlobalRotation)
138 {
139     // mirror all base vectors at the local Z axis
140     pNode->mTransformation.c1 = -pNode->mTransformation.c1;
141     pNode->mTransformation.c2 = -pNode->mTransformation.c2;
142     pNode->mTransformation.c3 = -pNode->mTransformation.c3;
143     pNode->mTransformation.c4 = -pNode->mTransformation.c4;
144 
145     // now invert the Z axis again to keep the matrix determinant positive.
146     // The local meshes will be inverted accordingly so that the result should look just fine again.
147     pNode->mTransformation.a3 = -pNode->mTransformation.a3;
148     pNode->mTransformation.b3 = -pNode->mTransformation.b3;
149     pNode->mTransformation.c3 = -pNode->mTransformation.c3;
150     pNode->mTransformation.d3 = -pNode->mTransformation.d3; // useless, but anyways...
151 
152     // continue for all children
153     for( size_t a = 0; a < pNode->mNumChildren; ++a ) {
154         ProcessNode( pNode->mChildren[ a ], pParentGlobalRotation * pNode->mTransformation );
155     }
156 }
157 
158 // ------------------------------------------------------------------------------------------------
159 // Converts a single mesh to left handed coordinates.
ProcessMesh(aiMesh * pMesh)160 void MakeLeftHandedProcess::ProcessMesh( aiMesh* pMesh) {
161     if ( nullptr == pMesh ) {
162         ASSIMP_LOG_ERROR( "Nullptr to mesh found." );
163         return;
164     }
165     // mirror positions, normals and stuff along the Z axis
166     for( size_t a = 0; a < pMesh->mNumVertices; ++a)
167     {
168         pMesh->mVertices[a].z *= -1.0f;
169         if (pMesh->HasNormals()) {
170             pMesh->mNormals[a].z *= -1.0f;
171         }
172         if( pMesh->HasTangentsAndBitangents())
173         {
174             pMesh->mTangents[a].z *= -1.0f;
175             pMesh->mBitangents[a].z *= -1.0f;
176         }
177     }
178 
179     // mirror anim meshes positions, normals and stuff along the Z axis
180     for (size_t m = 0; m < pMesh->mNumAnimMeshes; ++m)
181     {
182         for (size_t a = 0; a < pMesh->mAnimMeshes[m]->mNumVertices; ++a)
183         {
184             pMesh->mAnimMeshes[m]->mVertices[a].z *= -1.0f;
185             if (pMesh->mAnimMeshes[m]->HasNormals()) {
186                 pMesh->mAnimMeshes[m]->mNormals[a].z *= -1.0f;
187             }
188             if (pMesh->mAnimMeshes[m]->HasTangentsAndBitangents())
189             {
190                 pMesh->mAnimMeshes[m]->mTangents[a].z *= -1.0f;
191                 pMesh->mAnimMeshes[m]->mBitangents[a].z *= -1.0f;
192             }
193         }
194     }
195 
196     // mirror offset matrices of all bones
197     for( size_t a = 0; a < pMesh->mNumBones; ++a)
198     {
199         aiBone* bone = pMesh->mBones[a];
200         bone->mOffsetMatrix.a3 = -bone->mOffsetMatrix.a3;
201         bone->mOffsetMatrix.b3 = -bone->mOffsetMatrix.b3;
202         bone->mOffsetMatrix.d3 = -bone->mOffsetMatrix.d3;
203         bone->mOffsetMatrix.c1 = -bone->mOffsetMatrix.c1;
204         bone->mOffsetMatrix.c2 = -bone->mOffsetMatrix.c2;
205         bone->mOffsetMatrix.c4 = -bone->mOffsetMatrix.c4;
206     }
207 
208     // mirror bitangents as well as they're derived from the texture coords
209     if( pMesh->HasTangentsAndBitangents())
210     {
211         for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
212             pMesh->mBitangents[a] *= -1.0f;
213     }
214 }
215 
216 // ------------------------------------------------------------------------------------------------
217 // Converts a single material to left handed coordinates.
ProcessMaterial(aiMaterial * _mat)218 void MakeLeftHandedProcess::ProcessMaterial( aiMaterial* _mat) {
219     if ( nullptr == _mat ) {
220         ASSIMP_LOG_ERROR( "Nullptr to aiMaterial found." );
221         return;
222     }
223 
224     aiMaterial* mat = (aiMaterial*)_mat;
225     for (unsigned int a = 0; a < mat->mNumProperties;++a)   {
226         aiMaterialProperty* prop = mat->mProperties[a];
227 
228         // Mapping axis for UV mappings?
229         if (!::strcmp( prop->mKey.data, "$tex.mapaxis"))    {
230             ai_assert( prop->mDataLength >= sizeof(aiVector3D)); /* something is wrong with the validation if we end up here */
231             aiVector3D* pff = (aiVector3D*)prop->mData;
232             pff->z *= -1.f;
233         }
234     }
235 }
236 
237 // ------------------------------------------------------------------------------------------------
238 // Converts the given animation to LH coordinates.
ProcessAnimation(aiNodeAnim * pAnim)239 void MakeLeftHandedProcess::ProcessAnimation( aiNodeAnim* pAnim)
240 {
241     // position keys
242     for( unsigned int a = 0; a < pAnim->mNumPositionKeys; a++)
243         pAnim->mPositionKeys[a].mValue.z *= -1.0f;
244 
245     // rotation keys
246     for( unsigned int a = 0; a < pAnim->mNumRotationKeys; a++)
247     {
248         /* That's the safe version, but the float errors add up. So we try the short version instead
249         aiMatrix3x3 rotmat = pAnim->mRotationKeys[a].mValue.GetMatrix();
250         rotmat.a3 = -rotmat.a3; rotmat.b3 = -rotmat.b3;
251         rotmat.c1 = -rotmat.c1; rotmat.c2 = -rotmat.c2;
252         aiQuaternion rotquat( rotmat);
253         pAnim->mRotationKeys[a].mValue = rotquat;
254         */
255         pAnim->mRotationKeys[a].mValue.x *= -1.0f;
256         pAnim->mRotationKeys[a].mValue.y *= -1.0f;
257     }
258 }
259 
260 #endif // !!  ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS
261 #ifndef  ASSIMP_BUILD_NO_FLIPUVS_PROCESS
262 // # FlipUVsProcess
263 
264 // ------------------------------------------------------------------------------------------------
265 // Constructor to be privately used by Importer
FlipUVsProcess()266 FlipUVsProcess::FlipUVsProcess()
267 {}
268 
269 // ------------------------------------------------------------------------------------------------
270 // Destructor, private as well
~FlipUVsProcess()271 FlipUVsProcess::~FlipUVsProcess()
272 {}
273 
274 // ------------------------------------------------------------------------------------------------
275 // Returns whether the processing step is present in the given flag field.
IsActive(unsigned int pFlags) const276 bool FlipUVsProcess::IsActive( unsigned int pFlags) const
277 {
278     return 0 != (pFlags & aiProcess_FlipUVs);
279 }
280 
281 // ------------------------------------------------------------------------------------------------
282 // Executes the post processing step on the given imported data.
Execute(aiScene * pScene)283 void FlipUVsProcess::Execute( aiScene* pScene)
284 {
285     ASSIMP_LOG_DEBUG("FlipUVsProcess begin");
286     for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
287         ProcessMesh(pScene->mMeshes[i]);
288 
289     for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
290         ProcessMaterial(pScene->mMaterials[i]);
291     ASSIMP_LOG_DEBUG("FlipUVsProcess finished");
292 }
293 
294 // ------------------------------------------------------------------------------------------------
295 // Converts a single material
ProcessMaterial(aiMaterial * _mat)296 void FlipUVsProcess::ProcessMaterial (aiMaterial* _mat)
297 {
298     aiMaterial* mat = (aiMaterial*)_mat;
299     for (unsigned int a = 0; a < mat->mNumProperties;++a)   {
300         aiMaterialProperty* prop = mat->mProperties[a];
301         if( !prop ) {
302             ASSIMP_LOG_DEBUG( "Property is null" );
303             continue;
304         }
305 
306         // UV transformation key?
307         if (!::strcmp( prop->mKey.data, "$tex.uvtrafo"))    {
308             ai_assert( prop->mDataLength >= sizeof(aiUVTransform));  /* something is wrong with the validation if we end up here */
309             aiUVTransform* uv = (aiUVTransform*)prop->mData;
310 
311             // just flip it, that's everything
312             uv->mTranslation.y *= -1.f;
313             uv->mRotation *= -1.f;
314         }
315     }
316 }
317 
318 // ------------------------------------------------------------------------------------------------
319 // Converts a single mesh
ProcessMesh(aiMesh * pMesh)320 void FlipUVsProcess::ProcessMesh( aiMesh* pMesh)
321 {
322     flipUVs(pMesh);
323     for (unsigned int idx = 0; idx < pMesh->mNumAnimMeshes; idx++) {
324         flipUVs(pMesh->mAnimMeshes[idx]);
325     }
326 }
327 
328 #endif // !ASSIMP_BUILD_NO_FLIPUVS_PROCESS
329 #ifndef  ASSIMP_BUILD_NO_FLIPWINDING_PROCESS
330 // # FlipWindingOrderProcess
331 
332 // ------------------------------------------------------------------------------------------------
333 // Constructor to be privately used by Importer
FlipWindingOrderProcess()334 FlipWindingOrderProcess::FlipWindingOrderProcess()
335 {}
336 
337 // ------------------------------------------------------------------------------------------------
338 // Destructor, private as well
~FlipWindingOrderProcess()339 FlipWindingOrderProcess::~FlipWindingOrderProcess()
340 {}
341 
342 // ------------------------------------------------------------------------------------------------
343 // Returns whether the processing step is present in the given flag field.
IsActive(unsigned int pFlags) const344 bool FlipWindingOrderProcess::IsActive( unsigned int pFlags) const
345 {
346     return 0 != (pFlags & aiProcess_FlipWindingOrder);
347 }
348 
349 // ------------------------------------------------------------------------------------------------
350 // Executes the post processing step on the given imported data.
Execute(aiScene * pScene)351 void FlipWindingOrderProcess::Execute( aiScene* pScene)
352 {
353     ASSIMP_LOG_DEBUG("FlipWindingOrderProcess begin");
354     for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
355         ProcessMesh(pScene->mMeshes[i]);
356     ASSIMP_LOG_DEBUG("FlipWindingOrderProcess finished");
357 }
358 
359 // ------------------------------------------------------------------------------------------------
360 // Converts a single mesh
ProcessMesh(aiMesh * pMesh)361 void FlipWindingOrderProcess::ProcessMesh( aiMesh* pMesh)
362 {
363     // invert the order of all faces in this mesh
364     for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
365     {
366         aiFace& face = pMesh->mFaces[a];
367         for (unsigned int b = 0; b < face.mNumIndices / 2; b++) {
368             std::swap(face.mIndices[b], face.mIndices[face.mNumIndices - 1 - b]);
369         }
370     }
371 
372     // invert the order of all components in this mesh anim meshes
373     for (unsigned int m = 0; m < pMesh->mNumAnimMeshes; m++) {
374         aiAnimMesh* animMesh = pMesh->mAnimMeshes[m];
375         unsigned int numVertices = animMesh->mNumVertices;
376         if (animMesh->HasPositions()) {
377             for (unsigned int a = 0; a < numVertices; a++)
378             {
379                 std::swap(animMesh->mVertices[a], animMesh->mVertices[numVertices - 1 - a]);
380             }
381         }
382         if (animMesh->HasNormals()) {
383             for (unsigned int a = 0; a < numVertices; a++)
384             {
385                 std::swap(animMesh->mNormals[a], animMesh->mNormals[numVertices - 1 - a]);
386             }
387         }
388         for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; i++) {
389             if (animMesh->HasTextureCoords(i)) {
390                 for (unsigned int a = 0; a < numVertices; a++)
391                 {
392                     std::swap(animMesh->mTextureCoords[i][a], animMesh->mTextureCoords[i][numVertices - 1 - a]);
393                 }
394             }
395         }
396         if (animMesh->HasTangentsAndBitangents()) {
397             for (unsigned int a = 0; a < numVertices; a++)
398             {
399                 std::swap(animMesh->mTangents[a], animMesh->mTangents[numVertices - 1 - a]);
400                 std::swap(animMesh->mBitangents[a], animMesh->mBitangents[numVertices - 1 - a]);
401             }
402         }
403         for (unsigned int v = 0; v < AI_MAX_NUMBER_OF_COLOR_SETS; v++) {
404             if (animMesh->HasVertexColors(v)) {
405                 for (unsigned int a = 0; a < numVertices; a++)
406                 {
407                     std::swap(animMesh->mColors[v][a], animMesh->mColors[v][numVertices - 1 - a]);
408                 }
409             }
410         }
411     }
412 }
413 
414 #endif // !! ASSIMP_BUILD_NO_FLIPWINDING_PROCESS
415