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