vrshoot

view libs/assimp/TextureTransform.cpp @ 0:b2f14e535253

initial commit
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 01 Feb 2014 19:58:19 +0200
parents
children
line source
1 /*
2 Open Asset Import Library (assimp)
3 ----------------------------------------------------------------------
5 Copyright (c) 2006-2012, assimp team
6 All rights reserved.
8 Redistribution and use of this software in source and binary forms,
9 with or without modification, are permitted provided that the
10 following conditions are met:
12 * Redistributions of source code must retain the above
13 copyright notice, this list of conditions and the
14 following disclaimer.
16 * Redistributions in binary form must reproduce the above
17 copyright notice, this list of conditions and the
18 following disclaimer in the documentation and/or other
19 materials provided with the distribution.
21 * Neither the name of the assimp team, nor the names of its
22 contributors may be used to endorse or promote products
23 derived from this software without specific prior
24 written permission of the assimp team.
26 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 ----------------------------------------------------------------------
39 */
41 /** @file A helper class that processes texture transformations */
44 #include "AssimpPCH.h"
45 #include "TextureTransform.h"
47 using namespace Assimp;
50 // ------------------------------------------------------------------------------------------------
51 // Constructor to be privately used by Importer
52 TextureTransformStep::TextureTransformStep()
53 {
54 // nothing to do here
55 }
57 // ------------------------------------------------------------------------------------------------
58 // Destructor, private as well
59 TextureTransformStep::~TextureTransformStep()
60 {
61 // nothing to do here
62 }
64 // ------------------------------------------------------------------------------------------------
65 // Returns whether the processing step is present in the given flag field.
66 bool TextureTransformStep::IsActive( unsigned int pFlags) const
67 {
68 return (pFlags & aiProcess_TransformUVCoords) != 0;
69 }
71 // ------------------------------------------------------------------------------------------------
72 // Setup properties
73 void TextureTransformStep::SetupProperties(const Importer* pImp)
74 {
75 configFlags = pImp->GetPropertyInteger(AI_CONFIG_PP_TUV_EVALUATE,AI_UVTRAFO_ALL);
76 }
78 // ------------------------------------------------------------------------------------------------
79 void TextureTransformStep::PreProcessUVTransform(STransformVecInfo& info)
80 {
81 /* This function tries to simplify the input UV transformation.
82 * That's very important as it allows us to reduce the number
83 * of output UV channels. The oder in which the transformations
84 * are applied is - as always - scaling, rotation, translation.
85 */
87 char szTemp[512];
88 int rounded = 0;
91 /* Optimize the rotation angle. That's slightly difficult as
92 * we have an inprecise floating-point number (when comparing
93 * UV transformations we'll take that into account by using
94 * an epsilon of 5 degrees). If there is a rotation value, we can't
95 * perform any further optimizations.
96 */
97 if (info.mRotation)
98 {
99 float out = info.mRotation;
100 if ((rounded = (int)(info.mRotation / (float)AI_MATH_TWO_PI)))
101 {
102 out -= rounded*(float)AI_MATH_PI;
104 sprintf(szTemp,"Texture coordinate rotation %f can be simplified to %f",info.mRotation,out);
105 DefaultLogger::get()->info(szTemp);
106 }
108 // Next step - convert negative rotation angles to positives
109 if (out < 0.f)
110 out = (float)AI_MATH_TWO_PI * 2 + out;
112 info.mRotation = out;
113 return;
114 }
117 /* Optimize UV translation in the U direction. To determine whether
118 * or not we can optimize we need to look at the requested mapping
119 * type (e.g. if mirroring is active there IS a difference between
120 * offset 2 and 3)
121 */
122 if ((rounded = (int)info.mTranslation.x)) {
123 float out;
124 szTemp[0] = 0;
125 if (aiTextureMapMode_Wrap == info.mapU) {
126 // Wrap - simple take the fraction of the field
127 out = info.mTranslation.x-(float)rounded;
128 sprintf(szTemp,"[w] UV U offset %f can be simplified to %f",info.mTranslation.x,out);
129 }
130 else if (aiTextureMapMode_Mirror == info.mapU && 1 != rounded) {
131 // Mirror
132 if (rounded % 2)
133 rounded--;
134 out = info.mTranslation.x-(float)rounded;
136 sprintf(szTemp,"[m/d] UV U offset %f can be simplified to %f",info.mTranslation.x,out);
137 }
138 else if (aiTextureMapMode_Clamp == info.mapU || aiTextureMapMode_Decal == info.mapU) {
139 // Clamp - translations beyond 1,1 are senseless
140 sprintf(szTemp,"[c] UV U offset %f can be clamped to 1.0f",info.mTranslation.x);
142 out = 1.f;
143 }
144 if (szTemp[0]) {
145 DefaultLogger::get()->info(szTemp);
146 info.mTranslation.x = out;
147 }
148 }
150 /* Optimize UV translation in the V direction. To determine whether
151 * or not we can optimize we need to look at the requested mapping
152 * type (e.g. if mirroring is active there IS a difference between
153 * offset 2 and 3)
154 */
155 if ((rounded = (int)info.mTranslation.y)) {
156 float out;
157 szTemp[0] = 0;
158 if (aiTextureMapMode_Wrap == info.mapV) {
159 // Wrap - simple take the fraction of the field
160 out = info.mTranslation.y-(float)rounded;
161 sprintf(szTemp,"[w] UV V offset %f can be simplified to %f",info.mTranslation.y,out);
162 }
163 else if (aiTextureMapMode_Mirror == info.mapV && 1 != rounded) {
164 // Mirror
165 if (rounded % 2)
166 rounded--;
167 out = info.mTranslation.x-(float)rounded;
169 sprintf(szTemp,"[m/d] UV V offset %f can be simplified to %f",info.mTranslation.y,out);
170 }
171 else if (aiTextureMapMode_Clamp == info.mapV || aiTextureMapMode_Decal == info.mapV) {
172 // Clamp - translations beyond 1,1 are senseless
173 sprintf(szTemp,"[c] UV V offset %f canbe clamped to 1.0f",info.mTranslation.y);
175 out = 1.f;
176 }
177 if (szTemp[0]) {
178 DefaultLogger::get()->info(szTemp);
179 info.mTranslation.y = out;
180 }
181 }
182 return;
183 }
185 // ------------------------------------------------------------------------------------------------
186 void UpdateUVIndex(const std::list<TTUpdateInfo>& l, unsigned int n)
187 {
188 // Don't set if == 0 && wasn't set before
189 for (std::list<TTUpdateInfo>::const_iterator it = l.begin();it != l.end(); ++it) {
190 const TTUpdateInfo& info = *it;
192 if (info.directShortcut)
193 *info.directShortcut = n;
194 else if (!n)
195 {
196 info.mat->AddProperty<int>((int*)&n,1,AI_MATKEY_UVWSRC(info.semantic,info.index));
197 }
198 }
199 }
201 // ------------------------------------------------------------------------------------------------
202 inline const char* MappingModeToChar(aiTextureMapMode map)
203 {
204 if (aiTextureMapMode_Wrap == map)
205 return "-w";
207 if (aiTextureMapMode_Mirror == map)
208 return "-m";
210 return "-c";
211 }
213 // ------------------------------------------------------------------------------------------------
214 void TextureTransformStep::Execute( aiScene* pScene)
215 {
216 DefaultLogger::get()->debug("TransformUVCoordsProcess begin");
219 /* We build a per-mesh list of texture transformations we'll need
220 * to apply. To achieve this, we iterate through all materials,
221 * find all textures and get their transformations and UV indices.
222 * Then we search for all meshes using this material.
223 */
224 typedef std::list<STransformVecInfo> MeshTrafoList;
225 std::vector<MeshTrafoList> meshLists(pScene->mNumMeshes);
227 for (unsigned int i = 0; i < pScene->mNumMaterials;++i) {
229 aiMaterial* mat = pScene->mMaterials[i];
230 for (unsigned int a = 0; a < mat->mNumProperties;++a) {
232 aiMaterialProperty* prop = mat->mProperties[a];
233 if (!::strcmp( prop->mKey.data, "$tex.file")) {
234 STransformVecInfo info;
236 // Setup a shortcut structure to allow for a fast updating
237 // of the UV index later
238 TTUpdateInfo update;
239 update.mat = (aiMaterial*) mat;
240 update.semantic = prop->mSemantic;
241 update.index = prop->mIndex;
243 // Get textured properties and transform
244 for (unsigned int a2 = 0; a2 < mat->mNumProperties;++a2) {
245 aiMaterialProperty* prop2 = mat->mProperties[a2];
246 if (prop2->mSemantic != prop->mSemantic || prop2->mIndex != prop->mIndex) {
247 continue;
248 }
250 if ( !::strcmp( prop2->mKey.data, "$tex.uvwsrc")) {
251 info.uvIndex = *((int*)prop2->mData);
253 // Store a direct pointer for later use
254 update.directShortcut = (unsigned int*) prop2->mData;
255 }
257 else if ( !::strcmp( prop2->mKey.data, "$tex.mapmodeu")) {
258 info.mapU = *((aiTextureMapMode*)prop2->mData);
259 }
260 else if ( !::strcmp( prop2->mKey.data, "$tex.mapmodev")) {
261 info.mapV = *((aiTextureMapMode*)prop2->mData);
262 }
263 else if ( !::strcmp( prop2->mKey.data, "$tex.uvtrafo")) {
264 // ValidateDS should check this
265 ai_assert(prop2->mDataLength >= 20);
266 ::memcpy(&info.mTranslation.x,prop2->mData,sizeof(float)*5);
268 // Directly remove this property from the list
269 mat->mNumProperties--;
270 for (unsigned int a3 = a2; a3 < mat->mNumProperties;++a3) {
271 mat->mProperties[a3] = mat->mProperties[a3+1];
272 }
274 delete prop2;
276 // Warn: could be an underflow, but this does not invoke undefined behaviour
277 --a2;
278 }
279 }
281 // Find out which transformations are to be evaluated
282 if (!(configFlags & AI_UVTRAFO_ROTATION)) {
283 info.mRotation = 0.f;
284 }
285 if (!(configFlags & AI_UVTRAFO_SCALING)) {
286 info.mScaling = aiVector2D(1.f,1.f);
287 }
288 if (!(configFlags & AI_UVTRAFO_TRANSLATION)) {
289 info.mTranslation = aiVector2D(0.f,0.f);
290 }
292 // Do some preprocessing
293 PreProcessUVTransform(info);
294 info.uvIndex = std::min(info.uvIndex,AI_MAX_NUMBER_OF_TEXTURECOORDS -1u);
296 // Find out whether this material is used by more than
297 // one mesh. This will make our task much, much more difficult!
298 unsigned int cnt = 0;
299 for (unsigned int n = 0; n < pScene->mNumMeshes;++n) {
300 if (pScene->mMeshes[n]->mMaterialIndex == i)
301 ++cnt;
302 }
304 if (!cnt)
305 continue;
306 else if (1 != cnt) {
307 // This material is referenced by more than one mesh!
308 // So we need to make sure the UV index for the texture
309 // is identical for each of it ...
310 info.lockedPos = AI_TT_UV_IDX_LOCK_TBD;
311 }
313 // Get all coresponding meshes
314 for (unsigned int n = 0; n < pScene->mNumMeshes;++n) {
315 aiMesh* mesh = pScene->mMeshes[n];
316 if (mesh->mMaterialIndex != i || !mesh->mTextureCoords[0])
317 continue;
319 unsigned int uv = info.uvIndex;
320 if (!mesh->mTextureCoords[uv]) {
321 // If the requested UV index is not available, take the first one instead.
322 uv = 0;
323 }
325 if (mesh->mNumUVComponents[info.uvIndex] >= 3){
326 DefaultLogger::get()->warn("UV transformations on 3D mapping channels are not supported");
327 continue;
328 }
330 MeshTrafoList::iterator it;
332 // Check whether we have this transform setup already
333 for (it = meshLists[n].begin();it != meshLists[n].end(); ++it) {
335 if ((*it) == info && (*it).uvIndex == uv) {
336 (*it).updateList.push_back(update);
337 break;
338 }
339 }
341 if (it == meshLists[n].end()) {
342 meshLists[n].push_back(info);
343 meshLists[n].back().uvIndex = uv;
344 meshLists[n].back().updateList.push_back(update);
345 }
346 }
347 }
348 }
349 }
351 char buffer[1024]; // should be sufficiently large
352 unsigned int outChannels = 0, inChannels = 0, transformedChannels = 0;
354 // Now process all meshes. Important: we don't remove unreferenced UV channels.
355 // This is a job for the RemoveUnreferencedData-Step.
356 for (unsigned int q = 0; q < pScene->mNumMeshes;++q) {
358 aiMesh* mesh = pScene->mMeshes[q];
359 MeshTrafoList& trafo = meshLists[q];
361 inChannels += mesh->GetNumUVChannels();
363 if (!mesh->mTextureCoords[0] || trafo.empty() || (trafo.size() == 1 && trafo.begin()->IsUntransformed())) {
364 outChannels += mesh->GetNumUVChannels();
365 continue;
366 }
368 // Move untransformed UV channels to the first position in the list ....
369 // except if we need a new locked index which should be as small as possible
370 bool veto = false, need = false;
371 unsigned int cnt = 0;
372 unsigned int untransformed = 0;
374 MeshTrafoList::iterator it,it2;
375 for (it = trafo.begin();it != trafo.end(); ++it,++cnt) {
377 if (!(*it).IsUntransformed()) {
378 need = true;
379 }
381 if ((*it).lockedPos == AI_TT_UV_IDX_LOCK_TBD) {
382 // Lock this index and make sure it won't be changed
383 (*it).lockedPos = cnt;
384 veto = true;
385 continue;
386 }
388 if (!veto && it != trafo.begin() && (*it).IsUntransformed()) {
389 for (it2 = trafo.begin();it2 != it; ++it2) {
390 if (!(*it2).IsUntransformed())
391 break;
392 }
393 trafo.insert(it2,*it);
394 trafo.erase(it);
395 break;
396 }
397 }
398 if (!need)
399 continue;
401 // Find all that are not at their 'locked' position and move them to it.
402 // Conflicts are possible but quite unlikely.
403 cnt = 0;
404 for (it = trafo.begin();it != trafo.end(); ++it,++cnt) {
405 if ((*it).lockedPos != AI_TT_UV_IDX_LOCK_NONE && (*it).lockedPos != cnt) {
406 it2 = trafo.begin();unsigned int t = 0;
407 while (t != (*it).lockedPos)
408 ++it2;
410 if ((*it2).lockedPos != AI_TT_UV_IDX_LOCK_NONE) {
411 DefaultLogger::get()->error("Channel mismatch, can't compute all transformations properly [design bug]");
412 continue;
413 }
415 std::swap(*it2,*it);
416 if ((*it).lockedPos == untransformed)
417 untransformed = cnt;
418 }
419 }
421 // ... and add dummies for all unreferenced channels
422 // at the end of the list
423 bool ref[AI_MAX_NUMBER_OF_TEXTURECOORDS];
424 for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n)
425 ref[n] = (!mesh->mTextureCoords[n] ? true : false);
427 for (it = trafo.begin();it != trafo.end(); ++it)
428 ref[(*it).uvIndex] = true;
430 for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n) {
431 if (ref[n])
432 continue;
433 trafo.push_back(STransformVecInfo());
434 trafo.back().uvIndex = n;
435 }
437 // Then check whether this list breaks the channel limit.
438 // The unimportant ones are at the end of the list, so
439 // it shouldn't be too worse if we remove them.
440 unsigned int size = (unsigned int)trafo.size();
441 if (size > AI_MAX_NUMBER_OF_TEXTURECOORDS) {
443 if (!DefaultLogger::isNullLogger()) {
444 ::sprintf(buffer,"%u UV channels required but just %u available",
445 static_cast<unsigned int>(trafo.size()),AI_MAX_NUMBER_OF_TEXTURECOORDS);
447 DefaultLogger::get()->error(buffer);
448 }
449 size = AI_MAX_NUMBER_OF_TEXTURECOORDS;
450 }
453 aiVector3D* old[AI_MAX_NUMBER_OF_TEXTURECOORDS];
454 for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n)
455 old[n] = mesh->mTextureCoords[n];
457 // Now continue and generate the output channels. Channels
458 // that we're not going to need later can be overridden.
459 it = trafo.begin();
460 for (unsigned int n = 0; n < trafo.size();++n,++it) {
462 if (n >= size) {
463 // Try to use an untransformed channel for all channels we threw over board
464 UpdateUVIndex((*it).updateList,untransformed);
465 continue;
466 }
468 outChannels++;
470 // Write to the log
471 if (!DefaultLogger::isNullLogger()) {
472 sprintf(buffer,"Mesh %u, channel %u: t(%.3f,%.3f), s(%.3f,%.3f), r(%.3f), %s%s",
473 q,n,
474 (*it).mTranslation.x,
475 (*it).mTranslation.y,
476 (*it).mScaling.x,
477 (*it).mScaling.y,
478 AI_RAD_TO_DEG( (*it).mRotation),
479 MappingModeToChar ((*it).mapU),
480 MappingModeToChar ((*it).mapV));
482 DefaultLogger::get()->info(buffer);
483 }
485 // Check whether we need a new buffer here
486 if (mesh->mTextureCoords[n]) {
488 it2 = it;++it2;
489 for (unsigned int m = n+1; m < size;++m, ++it2) {
491 if ((*it2).uvIndex == n){
492 it2 = trafo.begin();
493 break;
494 }
495 }
496 if (it2 == trafo.begin()){
497 mesh->mTextureCoords[n] = new aiVector3D[mesh->mNumVertices];
498 }
499 }
500 else mesh->mTextureCoords[n] = new aiVector3D[mesh->mNumVertices];
502 aiVector3D* src = old[(*it).uvIndex];
503 aiVector3D* dest, *end;
504 dest = mesh->mTextureCoords[n];
506 ai_assert(NULL != src);
508 // Copy the data to the destination array
509 if (dest != src)
510 ::memcpy(dest,src,sizeof(aiVector3D)*mesh->mNumVertices);
512 end = dest + mesh->mNumVertices;
514 // Build a transformation matrix and transform all UV coords with it
515 if (!(*it).IsUntransformed()) {
516 const aiVector2D& trl = (*it).mTranslation;
517 const aiVector2D& scl = (*it).mScaling;
519 // fixme: simplify ..
520 ++transformedChannels;
521 aiMatrix3x3 matrix;
523 aiMatrix3x3 m2,m3,m4,m5;
525 m4.a1 = scl.x;
526 m4.b2 = scl.y;
528 m2.a3 = m2.b3 = 0.5f;
529 m3.a3 = m3.b3 = -0.5f;
531 if ((*it).mRotation > AI_TT_ROTATION_EPSILON )
532 aiMatrix3x3::RotationZ((*it).mRotation,matrix);
534 m5.a3 += trl.x; m5.b3 += trl.y;
535 matrix = m2 * m4 * matrix * m3 * m5;
537 for (src = dest; src != end; ++src) { /* manual homogenious divide */
538 src->z = 1.f;
539 *src = matrix * *src;
540 src->x /= src->z;
541 src->y /= src->z;
542 src->z = 0.f;
543 }
544 }
546 // Update all UV indices
547 UpdateUVIndex((*it).updateList,n);
548 }
549 }
551 // Print some detailled statistics into the log
552 if (!DefaultLogger::isNullLogger()) {
554 if (transformedChannels) {
555 ::sprintf(buffer,"TransformUVCoordsProcess end: %u output channels (in: %u, modified: %u)",
556 outChannels,inChannels,transformedChannels);
558 DefaultLogger::get()->info(buffer);
559 }
560 else DefaultLogger::get()->debug("TransformUVCoordsProcess finished");
561 }
562 }