nuclear@0: /* nuclear@0: Open Asset Import Library (assimp) nuclear@0: ---------------------------------------------------------------------- nuclear@0: nuclear@0: Copyright (c) 2006-2012, assimp team nuclear@0: All rights reserved. nuclear@0: nuclear@0: Redistribution and use of this software in source and binary forms, nuclear@0: with or without modification, are permitted provided that the nuclear@0: following conditions are met: nuclear@0: nuclear@0: * Redistributions of source code must retain the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer. nuclear@0: nuclear@0: * Redistributions in binary form must reproduce the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer in the documentation and/or other nuclear@0: materials provided with the distribution. nuclear@0: nuclear@0: * Neither the name of the assimp team, nor the names of its nuclear@0: contributors may be used to endorse or promote products nuclear@0: derived from this software without specific prior nuclear@0: written permission of the assimp team. nuclear@0: nuclear@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS nuclear@0: "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT nuclear@0: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR nuclear@0: A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT nuclear@0: OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, nuclear@0: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT nuclear@0: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, nuclear@0: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY nuclear@0: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT nuclear@0: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE nuclear@0: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. nuclear@0: nuclear@0: ---------------------------------------------------------------------- nuclear@0: */ nuclear@0: nuclear@0: /** @file A helper class that processes texture transformations */ nuclear@0: nuclear@0: nuclear@0: #include "AssimpPCH.h" nuclear@0: #include "TextureTransform.h" nuclear@0: nuclear@0: using namespace Assimp; nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Constructor to be privately used by Importer nuclear@0: TextureTransformStep::TextureTransformStep() nuclear@0: { nuclear@0: // nothing to do here nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Destructor, private as well nuclear@0: TextureTransformStep::~TextureTransformStep() nuclear@0: { nuclear@0: // nothing to do here nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Returns whether the processing step is present in the given flag field. nuclear@0: bool TextureTransformStep::IsActive( unsigned int pFlags) const nuclear@0: { nuclear@0: return (pFlags & aiProcess_TransformUVCoords) != 0; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Setup properties nuclear@0: void TextureTransformStep::SetupProperties(const Importer* pImp) nuclear@0: { nuclear@0: configFlags = pImp->GetPropertyInteger(AI_CONFIG_PP_TUV_EVALUATE,AI_UVTRAFO_ALL); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void TextureTransformStep::PreProcessUVTransform(STransformVecInfo& info) nuclear@0: { nuclear@0: /* This function tries to simplify the input UV transformation. nuclear@0: * That's very important as it allows us to reduce the number nuclear@0: * of output UV channels. The oder in which the transformations nuclear@0: * are applied is - as always - scaling, rotation, translation. nuclear@0: */ nuclear@0: nuclear@0: char szTemp[512]; nuclear@0: int rounded = 0; nuclear@0: nuclear@0: nuclear@0: /* Optimize the rotation angle. That's slightly difficult as nuclear@0: * we have an inprecise floating-point number (when comparing nuclear@0: * UV transformations we'll take that into account by using nuclear@0: * an epsilon of 5 degrees). If there is a rotation value, we can't nuclear@0: * perform any further optimizations. nuclear@0: */ nuclear@0: if (info.mRotation) nuclear@0: { nuclear@0: float out = info.mRotation; nuclear@0: if ((rounded = (int)(info.mRotation / (float)AI_MATH_TWO_PI))) nuclear@0: { nuclear@0: out -= rounded*(float)AI_MATH_PI; nuclear@0: nuclear@0: sprintf(szTemp,"Texture coordinate rotation %f can be simplified to %f",info.mRotation,out); nuclear@0: DefaultLogger::get()->info(szTemp); nuclear@0: } nuclear@0: nuclear@0: // Next step - convert negative rotation angles to positives nuclear@0: if (out < 0.f) nuclear@0: out = (float)AI_MATH_TWO_PI * 2 + out; nuclear@0: nuclear@0: info.mRotation = out; nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: /* Optimize UV translation in the U direction. To determine whether nuclear@0: * or not we can optimize we need to look at the requested mapping nuclear@0: * type (e.g. if mirroring is active there IS a difference between nuclear@0: * offset 2 and 3) nuclear@0: */ nuclear@0: if ((rounded = (int)info.mTranslation.x)) { nuclear@0: float out; nuclear@0: szTemp[0] = 0; nuclear@0: if (aiTextureMapMode_Wrap == info.mapU) { nuclear@0: // Wrap - simple take the fraction of the field nuclear@0: out = info.mTranslation.x-(float)rounded; nuclear@0: sprintf(szTemp,"[w] UV U offset %f can be simplified to %f",info.mTranslation.x,out); nuclear@0: } nuclear@0: else if (aiTextureMapMode_Mirror == info.mapU && 1 != rounded) { nuclear@0: // Mirror nuclear@0: if (rounded % 2) nuclear@0: rounded--; nuclear@0: out = info.mTranslation.x-(float)rounded; nuclear@0: nuclear@0: sprintf(szTemp,"[m/d] UV U offset %f can be simplified to %f",info.mTranslation.x,out); nuclear@0: } nuclear@0: else if (aiTextureMapMode_Clamp == info.mapU || aiTextureMapMode_Decal == info.mapU) { nuclear@0: // Clamp - translations beyond 1,1 are senseless nuclear@0: sprintf(szTemp,"[c] UV U offset %f can be clamped to 1.0f",info.mTranslation.x); nuclear@0: nuclear@0: out = 1.f; nuclear@0: } nuclear@0: if (szTemp[0]) { nuclear@0: DefaultLogger::get()->info(szTemp); nuclear@0: info.mTranslation.x = out; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: /* Optimize UV translation in the V direction. To determine whether nuclear@0: * or not we can optimize we need to look at the requested mapping nuclear@0: * type (e.g. if mirroring is active there IS a difference between nuclear@0: * offset 2 and 3) nuclear@0: */ nuclear@0: if ((rounded = (int)info.mTranslation.y)) { nuclear@0: float out; nuclear@0: szTemp[0] = 0; nuclear@0: if (aiTextureMapMode_Wrap == info.mapV) { nuclear@0: // Wrap - simple take the fraction of the field nuclear@0: out = info.mTranslation.y-(float)rounded; nuclear@0: sprintf(szTemp,"[w] UV V offset %f can be simplified to %f",info.mTranslation.y,out); nuclear@0: } nuclear@0: else if (aiTextureMapMode_Mirror == info.mapV && 1 != rounded) { nuclear@0: // Mirror nuclear@0: if (rounded % 2) nuclear@0: rounded--; nuclear@0: out = info.mTranslation.x-(float)rounded; nuclear@0: nuclear@0: sprintf(szTemp,"[m/d] UV V offset %f can be simplified to %f",info.mTranslation.y,out); nuclear@0: } nuclear@0: else if (aiTextureMapMode_Clamp == info.mapV || aiTextureMapMode_Decal == info.mapV) { nuclear@0: // Clamp - translations beyond 1,1 are senseless nuclear@0: sprintf(szTemp,"[c] UV V offset %f canbe clamped to 1.0f",info.mTranslation.y); nuclear@0: nuclear@0: out = 1.f; nuclear@0: } nuclear@0: if (szTemp[0]) { nuclear@0: DefaultLogger::get()->info(szTemp); nuclear@0: info.mTranslation.y = out; nuclear@0: } nuclear@0: } nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void UpdateUVIndex(const std::list& l, unsigned int n) nuclear@0: { nuclear@0: // Don't set if == 0 && wasn't set before nuclear@0: for (std::list::const_iterator it = l.begin();it != l.end(); ++it) { nuclear@0: const TTUpdateInfo& info = *it; nuclear@0: nuclear@0: if (info.directShortcut) nuclear@0: *info.directShortcut = n; nuclear@0: else if (!n) nuclear@0: { nuclear@0: info.mat->AddProperty((int*)&n,1,AI_MATKEY_UVWSRC(info.semantic,info.index)); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: inline const char* MappingModeToChar(aiTextureMapMode map) nuclear@0: { nuclear@0: if (aiTextureMapMode_Wrap == map) nuclear@0: return "-w"; nuclear@0: nuclear@0: if (aiTextureMapMode_Mirror == map) nuclear@0: return "-m"; nuclear@0: nuclear@0: return "-c"; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void TextureTransformStep::Execute( aiScene* pScene) nuclear@0: { nuclear@0: DefaultLogger::get()->debug("TransformUVCoordsProcess begin"); nuclear@0: nuclear@0: nuclear@0: /* We build a per-mesh list of texture transformations we'll need nuclear@0: * to apply. To achieve this, we iterate through all materials, nuclear@0: * find all textures and get their transformations and UV indices. nuclear@0: * Then we search for all meshes using this material. nuclear@0: */ nuclear@0: typedef std::list MeshTrafoList; nuclear@0: std::vector meshLists(pScene->mNumMeshes); nuclear@0: nuclear@0: for (unsigned int i = 0; i < pScene->mNumMaterials;++i) { nuclear@0: nuclear@0: aiMaterial* mat = pScene->mMaterials[i]; nuclear@0: for (unsigned int a = 0; a < mat->mNumProperties;++a) { nuclear@0: nuclear@0: aiMaterialProperty* prop = mat->mProperties[a]; nuclear@0: if (!::strcmp( prop->mKey.data, "$tex.file")) { nuclear@0: STransformVecInfo info; nuclear@0: nuclear@0: // Setup a shortcut structure to allow for a fast updating nuclear@0: // of the UV index later nuclear@0: TTUpdateInfo update; nuclear@0: update.mat = (aiMaterial*) mat; nuclear@0: update.semantic = prop->mSemantic; nuclear@0: update.index = prop->mIndex; nuclear@0: nuclear@0: // Get textured properties and transform nuclear@0: for (unsigned int a2 = 0; a2 < mat->mNumProperties;++a2) { nuclear@0: aiMaterialProperty* prop2 = mat->mProperties[a2]; nuclear@0: if (prop2->mSemantic != prop->mSemantic || prop2->mIndex != prop->mIndex) { nuclear@0: continue; nuclear@0: } nuclear@0: nuclear@0: if ( !::strcmp( prop2->mKey.data, "$tex.uvwsrc")) { nuclear@0: info.uvIndex = *((int*)prop2->mData); nuclear@0: nuclear@0: // Store a direct pointer for later use nuclear@0: update.directShortcut = (unsigned int*) prop2->mData; nuclear@0: } nuclear@0: nuclear@0: else if ( !::strcmp( prop2->mKey.data, "$tex.mapmodeu")) { nuclear@0: info.mapU = *((aiTextureMapMode*)prop2->mData); nuclear@0: } nuclear@0: else if ( !::strcmp( prop2->mKey.data, "$tex.mapmodev")) { nuclear@0: info.mapV = *((aiTextureMapMode*)prop2->mData); nuclear@0: } nuclear@0: else if ( !::strcmp( prop2->mKey.data, "$tex.uvtrafo")) { nuclear@0: // ValidateDS should check this nuclear@0: ai_assert(prop2->mDataLength >= 20); nuclear@0: ::memcpy(&info.mTranslation.x,prop2->mData,sizeof(float)*5); nuclear@0: nuclear@0: // Directly remove this property from the list nuclear@0: mat->mNumProperties--; nuclear@0: for (unsigned int a3 = a2; a3 < mat->mNumProperties;++a3) { nuclear@0: mat->mProperties[a3] = mat->mProperties[a3+1]; nuclear@0: } nuclear@0: nuclear@0: delete prop2; nuclear@0: nuclear@0: // Warn: could be an underflow, but this does not invoke undefined behaviour nuclear@0: --a2; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // Find out which transformations are to be evaluated nuclear@0: if (!(configFlags & AI_UVTRAFO_ROTATION)) { nuclear@0: info.mRotation = 0.f; nuclear@0: } nuclear@0: if (!(configFlags & AI_UVTRAFO_SCALING)) { nuclear@0: info.mScaling = aiVector2D(1.f,1.f); nuclear@0: } nuclear@0: if (!(configFlags & AI_UVTRAFO_TRANSLATION)) { nuclear@0: info.mTranslation = aiVector2D(0.f,0.f); nuclear@0: } nuclear@0: nuclear@0: // Do some preprocessing nuclear@0: PreProcessUVTransform(info); nuclear@0: info.uvIndex = std::min(info.uvIndex,AI_MAX_NUMBER_OF_TEXTURECOORDS -1u); nuclear@0: nuclear@0: // Find out whether this material is used by more than nuclear@0: // one mesh. This will make our task much, much more difficult! nuclear@0: unsigned int cnt = 0; nuclear@0: for (unsigned int n = 0; n < pScene->mNumMeshes;++n) { nuclear@0: if (pScene->mMeshes[n]->mMaterialIndex == i) nuclear@0: ++cnt; nuclear@0: } nuclear@0: nuclear@0: if (!cnt) nuclear@0: continue; nuclear@0: else if (1 != cnt) { nuclear@0: // This material is referenced by more than one mesh! nuclear@0: // So we need to make sure the UV index for the texture nuclear@0: // is identical for each of it ... nuclear@0: info.lockedPos = AI_TT_UV_IDX_LOCK_TBD; nuclear@0: } nuclear@0: nuclear@0: // Get all coresponding meshes nuclear@0: for (unsigned int n = 0; n < pScene->mNumMeshes;++n) { nuclear@0: aiMesh* mesh = pScene->mMeshes[n]; nuclear@0: if (mesh->mMaterialIndex != i || !mesh->mTextureCoords[0]) nuclear@0: continue; nuclear@0: nuclear@0: unsigned int uv = info.uvIndex; nuclear@0: if (!mesh->mTextureCoords[uv]) { nuclear@0: // If the requested UV index is not available, take the first one instead. nuclear@0: uv = 0; nuclear@0: } nuclear@0: nuclear@0: if (mesh->mNumUVComponents[info.uvIndex] >= 3){ nuclear@0: DefaultLogger::get()->warn("UV transformations on 3D mapping channels are not supported"); nuclear@0: continue; nuclear@0: } nuclear@0: nuclear@0: MeshTrafoList::iterator it; nuclear@0: nuclear@0: // Check whether we have this transform setup already nuclear@0: for (it = meshLists[n].begin();it != meshLists[n].end(); ++it) { nuclear@0: nuclear@0: if ((*it) == info && (*it).uvIndex == uv) { nuclear@0: (*it).updateList.push_back(update); nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if (it == meshLists[n].end()) { nuclear@0: meshLists[n].push_back(info); nuclear@0: meshLists[n].back().uvIndex = uv; nuclear@0: meshLists[n].back().updateList.push_back(update); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: char buffer[1024]; // should be sufficiently large nuclear@0: unsigned int outChannels = 0, inChannels = 0, transformedChannels = 0; nuclear@0: nuclear@0: // Now process all meshes. Important: we don't remove unreferenced UV channels. nuclear@0: // This is a job for the RemoveUnreferencedData-Step. nuclear@0: for (unsigned int q = 0; q < pScene->mNumMeshes;++q) { nuclear@0: nuclear@0: aiMesh* mesh = pScene->mMeshes[q]; nuclear@0: MeshTrafoList& trafo = meshLists[q]; nuclear@0: nuclear@0: inChannels += mesh->GetNumUVChannels(); nuclear@0: nuclear@0: if (!mesh->mTextureCoords[0] || trafo.empty() || (trafo.size() == 1 && trafo.begin()->IsUntransformed())) { nuclear@0: outChannels += mesh->GetNumUVChannels(); nuclear@0: continue; nuclear@0: } nuclear@0: nuclear@0: // Move untransformed UV channels to the first position in the list .... nuclear@0: // except if we need a new locked index which should be as small as possible nuclear@0: bool veto = false, need = false; nuclear@0: unsigned int cnt = 0; nuclear@0: unsigned int untransformed = 0; nuclear@0: nuclear@0: MeshTrafoList::iterator it,it2; nuclear@0: for (it = trafo.begin();it != trafo.end(); ++it,++cnt) { nuclear@0: nuclear@0: if (!(*it).IsUntransformed()) { nuclear@0: need = true; nuclear@0: } nuclear@0: nuclear@0: if ((*it).lockedPos == AI_TT_UV_IDX_LOCK_TBD) { nuclear@0: // Lock this index and make sure it won't be changed nuclear@0: (*it).lockedPos = cnt; nuclear@0: veto = true; nuclear@0: continue; nuclear@0: } nuclear@0: nuclear@0: if (!veto && it != trafo.begin() && (*it).IsUntransformed()) { nuclear@0: for (it2 = trafo.begin();it2 != it; ++it2) { nuclear@0: if (!(*it2).IsUntransformed()) nuclear@0: break; nuclear@0: } nuclear@0: trafo.insert(it2,*it); nuclear@0: trafo.erase(it); nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: if (!need) nuclear@0: continue; nuclear@0: nuclear@0: // Find all that are not at their 'locked' position and move them to it. nuclear@0: // Conflicts are possible but quite unlikely. nuclear@0: cnt = 0; nuclear@0: for (it = trafo.begin();it != trafo.end(); ++it,++cnt) { nuclear@0: if ((*it).lockedPos != AI_TT_UV_IDX_LOCK_NONE && (*it).lockedPos != cnt) { nuclear@0: it2 = trafo.begin();unsigned int t = 0; nuclear@0: while (t != (*it).lockedPos) nuclear@0: ++it2; nuclear@0: nuclear@0: if ((*it2).lockedPos != AI_TT_UV_IDX_LOCK_NONE) { nuclear@0: DefaultLogger::get()->error("Channel mismatch, can't compute all transformations properly [design bug]"); nuclear@0: continue; nuclear@0: } nuclear@0: nuclear@0: std::swap(*it2,*it); nuclear@0: if ((*it).lockedPos == untransformed) nuclear@0: untransformed = cnt; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ... and add dummies for all unreferenced channels nuclear@0: // at the end of the list nuclear@0: bool ref[AI_MAX_NUMBER_OF_TEXTURECOORDS]; nuclear@0: for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n) nuclear@0: ref[n] = (!mesh->mTextureCoords[n] ? true : false); nuclear@0: nuclear@0: for (it = trafo.begin();it != trafo.end(); ++it) nuclear@0: ref[(*it).uvIndex] = true; nuclear@0: nuclear@0: for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n) { nuclear@0: if (ref[n]) nuclear@0: continue; nuclear@0: trafo.push_back(STransformVecInfo()); nuclear@0: trafo.back().uvIndex = n; nuclear@0: } nuclear@0: nuclear@0: // Then check whether this list breaks the channel limit. nuclear@0: // The unimportant ones are at the end of the list, so nuclear@0: // it shouldn't be too worse if we remove them. nuclear@0: unsigned int size = (unsigned int)trafo.size(); nuclear@0: if (size > AI_MAX_NUMBER_OF_TEXTURECOORDS) { nuclear@0: nuclear@0: if (!DefaultLogger::isNullLogger()) { nuclear@0: ::sprintf(buffer,"%u UV channels required but just %u available", nuclear@0: static_cast(trafo.size()),AI_MAX_NUMBER_OF_TEXTURECOORDS); nuclear@0: nuclear@0: DefaultLogger::get()->error(buffer); nuclear@0: } nuclear@0: size = AI_MAX_NUMBER_OF_TEXTURECOORDS; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: aiVector3D* old[AI_MAX_NUMBER_OF_TEXTURECOORDS]; nuclear@0: for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n) nuclear@0: old[n] = mesh->mTextureCoords[n]; nuclear@0: nuclear@0: // Now continue and generate the output channels. Channels nuclear@0: // that we're not going to need later can be overridden. nuclear@0: it = trafo.begin(); nuclear@0: for (unsigned int n = 0; n < trafo.size();++n,++it) { nuclear@0: nuclear@0: if (n >= size) { nuclear@0: // Try to use an untransformed channel for all channels we threw over board nuclear@0: UpdateUVIndex((*it).updateList,untransformed); nuclear@0: continue; nuclear@0: } nuclear@0: nuclear@0: outChannels++; nuclear@0: nuclear@0: // Write to the log nuclear@0: if (!DefaultLogger::isNullLogger()) { nuclear@0: sprintf(buffer,"Mesh %u, channel %u: t(%.3f,%.3f), s(%.3f,%.3f), r(%.3f), %s%s", nuclear@0: q,n, nuclear@0: (*it).mTranslation.x, nuclear@0: (*it).mTranslation.y, nuclear@0: (*it).mScaling.x, nuclear@0: (*it).mScaling.y, nuclear@0: AI_RAD_TO_DEG( (*it).mRotation), nuclear@0: MappingModeToChar ((*it).mapU), nuclear@0: MappingModeToChar ((*it).mapV)); nuclear@0: nuclear@0: DefaultLogger::get()->info(buffer); nuclear@0: } nuclear@0: nuclear@0: // Check whether we need a new buffer here nuclear@0: if (mesh->mTextureCoords[n]) { nuclear@0: nuclear@0: it2 = it;++it2; nuclear@0: for (unsigned int m = n+1; m < size;++m, ++it2) { nuclear@0: nuclear@0: if ((*it2).uvIndex == n){ nuclear@0: it2 = trafo.begin(); nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: if (it2 == trafo.begin()){ nuclear@0: mesh->mTextureCoords[n] = new aiVector3D[mesh->mNumVertices]; nuclear@0: } nuclear@0: } nuclear@0: else mesh->mTextureCoords[n] = new aiVector3D[mesh->mNumVertices]; nuclear@0: nuclear@0: aiVector3D* src = old[(*it).uvIndex]; nuclear@0: aiVector3D* dest, *end; nuclear@0: dest = mesh->mTextureCoords[n]; nuclear@0: nuclear@0: ai_assert(NULL != src); nuclear@0: nuclear@0: // Copy the data to the destination array nuclear@0: if (dest != src) nuclear@0: ::memcpy(dest,src,sizeof(aiVector3D)*mesh->mNumVertices); nuclear@0: nuclear@0: end = dest + mesh->mNumVertices; nuclear@0: nuclear@0: // Build a transformation matrix and transform all UV coords with it nuclear@0: if (!(*it).IsUntransformed()) { nuclear@0: const aiVector2D& trl = (*it).mTranslation; nuclear@0: const aiVector2D& scl = (*it).mScaling; nuclear@0: nuclear@0: // fixme: simplify .. nuclear@0: ++transformedChannels; nuclear@0: aiMatrix3x3 matrix; nuclear@0: nuclear@0: aiMatrix3x3 m2,m3,m4,m5; nuclear@0: nuclear@0: m4.a1 = scl.x; nuclear@0: m4.b2 = scl.y; nuclear@0: nuclear@0: m2.a3 = m2.b3 = 0.5f; nuclear@0: m3.a3 = m3.b3 = -0.5f; nuclear@0: nuclear@0: if ((*it).mRotation > AI_TT_ROTATION_EPSILON ) nuclear@0: aiMatrix3x3::RotationZ((*it).mRotation,matrix); nuclear@0: nuclear@0: m5.a3 += trl.x; m5.b3 += trl.y; nuclear@0: matrix = m2 * m4 * matrix * m3 * m5; nuclear@0: nuclear@0: for (src = dest; src != end; ++src) { /* manual homogenious divide */ nuclear@0: src->z = 1.f; nuclear@0: *src = matrix * *src; nuclear@0: src->x /= src->z; nuclear@0: src->y /= src->z; nuclear@0: src->z = 0.f; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // Update all UV indices nuclear@0: UpdateUVIndex((*it).updateList,n); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // Print some detailled statistics into the log nuclear@0: if (!DefaultLogger::isNullLogger()) { nuclear@0: nuclear@0: if (transformedChannels) { nuclear@0: ::sprintf(buffer,"TransformUVCoordsProcess end: %u output channels (in: %u, modified: %u)", nuclear@0: outChannels,inChannels,transformedChannels); nuclear@0: nuclear@0: DefaultLogger::get()->info(buffer); nuclear@0: } nuclear@0: else DefaultLogger::get()->debug("TransformUVCoordsProcess finished"); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: