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