vrshoot

annotate libs/assimp/SplitByBoneCountProcess.cpp @ 0:b2f14e535253

initial commit
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 01 Feb 2014 19:58:19 +0200
parents
children
rev   line source
nuclear@0 1 /*
nuclear@0 2 Open Asset Import Library (assimp)
nuclear@0 3 ----------------------------------------------------------------------
nuclear@0 4
nuclear@0 5 Copyright (c) 2006-2012, assimp team
nuclear@0 6 All rights reserved.
nuclear@0 7
nuclear@0 8 Redistribution and use of this software in source and binary forms,
nuclear@0 9 with or without modification, are permitted provided that the
nuclear@0 10 following conditions are met:
nuclear@0 11
nuclear@0 12 * Redistributions of source code must retain the above
nuclear@0 13 copyright notice, this list of conditions and the
nuclear@0 14 following disclaimer.
nuclear@0 15
nuclear@0 16 * Redistributions in binary form must reproduce the above
nuclear@0 17 copyright notice, this list of conditions and the
nuclear@0 18 following disclaimer in the documentation and/or other
nuclear@0 19 materials provided with the distribution.
nuclear@0 20
nuclear@0 21 * Neither the name of the assimp team, nor the names of its
nuclear@0 22 contributors may be used to endorse or promote products
nuclear@0 23 derived from this software without specific prior
nuclear@0 24 written permission of the assimp team.
nuclear@0 25
nuclear@0 26 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
nuclear@0 27 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
nuclear@0 28 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
nuclear@0 29 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
nuclear@0 30 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
nuclear@0 31 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
nuclear@0 32 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
nuclear@0 33 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
nuclear@0 34 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
nuclear@0 35 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
nuclear@0 36 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
nuclear@0 37
nuclear@0 38 ----------------------------------------------------------------------
nuclear@0 39 */
nuclear@0 40
nuclear@0 41
nuclear@0 42 /// @file SplitByBoneCountProcess.cpp
nuclear@0 43 /// Implementation of the SplitByBoneCount postprocessing step
nuclear@0 44
nuclear@0 45 #include "AssimpPCH.h"
nuclear@0 46
nuclear@0 47 // internal headers of the post-processing framework
nuclear@0 48 #include "SplitByBoneCountProcess.h"
nuclear@0 49
nuclear@0 50 #include <limits>
nuclear@0 51
nuclear@0 52 using namespace Assimp;
nuclear@0 53
nuclear@0 54 // ------------------------------------------------------------------------------------------------
nuclear@0 55 // Constructor
nuclear@0 56 SplitByBoneCountProcess::SplitByBoneCountProcess()
nuclear@0 57 {
nuclear@0 58 // set default, might be overriden by importer config
nuclear@0 59 mMaxBoneCount = AI_SBBC_DEFAULT_MAX_BONES;
nuclear@0 60 }
nuclear@0 61
nuclear@0 62 // ------------------------------------------------------------------------------------------------
nuclear@0 63 // Destructor
nuclear@0 64 SplitByBoneCountProcess::~SplitByBoneCountProcess()
nuclear@0 65 {
nuclear@0 66 // nothing to do here
nuclear@0 67 }
nuclear@0 68
nuclear@0 69 // ------------------------------------------------------------------------------------------------
nuclear@0 70 // Returns whether the processing step is present in the given flag.
nuclear@0 71 bool SplitByBoneCountProcess::IsActive( unsigned int pFlags) const
nuclear@0 72 {
nuclear@0 73 return !!(pFlags & aiProcess_SplitByBoneCount);
nuclear@0 74 }
nuclear@0 75
nuclear@0 76 // ------------------------------------------------------------------------------------------------
nuclear@0 77 // Updates internal properties
nuclear@0 78 void SplitByBoneCountProcess::SetupProperties(const Importer* pImp)
nuclear@0 79 {
nuclear@0 80 mMaxBoneCount = pImp->GetPropertyInteger(AI_CONFIG_PP_SBBC_MAX_BONES,AI_SBBC_DEFAULT_MAX_BONES);
nuclear@0 81 }
nuclear@0 82
nuclear@0 83 // ------------------------------------------------------------------------------------------------
nuclear@0 84 // Executes the post processing step on the given imported data.
nuclear@0 85 void SplitByBoneCountProcess::Execute( aiScene* pScene)
nuclear@0 86 {
nuclear@0 87 DefaultLogger::get()->debug("SplitByBoneCountProcess begin");
nuclear@0 88
nuclear@0 89 // early out
nuclear@0 90 bool isNecessary = false;
nuclear@0 91 for( size_t a = 0; a < pScene->mNumMeshes; ++a)
nuclear@0 92 if( pScene->mMeshes[a]->mNumBones > mMaxBoneCount )
nuclear@0 93 isNecessary = true;
nuclear@0 94
nuclear@0 95 if( !isNecessary )
nuclear@0 96 {
nuclear@0 97 DefaultLogger::get()->debug( boost::str( boost::format( "SplitByBoneCountProcess early-out: no meshes with more than %d bones.") % mMaxBoneCount));
nuclear@0 98 return;
nuclear@0 99 }
nuclear@0 100
nuclear@0 101 // we need to do something. Let's go.
nuclear@0 102 mSubMeshIndices.clear();
nuclear@0 103 mSubMeshIndices.resize( pScene->mNumMeshes);
nuclear@0 104
nuclear@0 105 // build a new array of meshes for the scene
nuclear@0 106 std::vector<aiMesh*> meshes;
nuclear@0 107
nuclear@0 108 for( size_t a = 0; a < pScene->mNumMeshes; ++a)
nuclear@0 109 {
nuclear@0 110 aiMesh* srcMesh = pScene->mMeshes[a];
nuclear@0 111
nuclear@0 112 std::vector<aiMesh*> newMeshes;
nuclear@0 113 SplitMesh( pScene->mMeshes[a], newMeshes);
nuclear@0 114
nuclear@0 115 // mesh was split
nuclear@0 116 if( !newMeshes.empty() )
nuclear@0 117 {
nuclear@0 118 // store new meshes and indices of the new meshes
nuclear@0 119 for( size_t b = 0; b < newMeshes.size(); ++b)
nuclear@0 120 {
nuclear@0 121 mSubMeshIndices[a].push_back( meshes.size());
nuclear@0 122 meshes.push_back( newMeshes[b]);
nuclear@0 123 }
nuclear@0 124
nuclear@0 125 // and destroy the source mesh. It should be completely contained inside the new submeshes
nuclear@0 126 delete srcMesh;
nuclear@0 127 }
nuclear@0 128 else
nuclear@0 129 {
nuclear@0 130 // Mesh is kept unchanged - store it's new place in the mesh array
nuclear@0 131 mSubMeshIndices[a].push_back( meshes.size());
nuclear@0 132 meshes.push_back( srcMesh);
nuclear@0 133 }
nuclear@0 134 }
nuclear@0 135
nuclear@0 136 // rebuild the scene's mesh array
nuclear@0 137 pScene->mNumMeshes = meshes.size();
nuclear@0 138 delete [] pScene->mMeshes;
nuclear@0 139 pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
nuclear@0 140 std::copy( meshes.begin(), meshes.end(), pScene->mMeshes);
nuclear@0 141
nuclear@0 142 // recurse through all nodes and translate the node's mesh indices to fit the new mesh array
nuclear@0 143 UpdateNode( pScene->mRootNode);
nuclear@0 144
nuclear@0 145 DefaultLogger::get()->debug( boost::str( boost::format( "SplitByBoneCountProcess end: split %d meshes into %d submeshes.") % mSubMeshIndices.size() % meshes.size()));
nuclear@0 146 }
nuclear@0 147
nuclear@0 148 // ------------------------------------------------------------------------------------------------
nuclear@0 149 // Splits the given mesh by bone count.
nuclear@0 150 void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector<aiMesh*>& poNewMeshes) const
nuclear@0 151 {
nuclear@0 152 // skip if not necessary
nuclear@0 153 if( pMesh->mNumBones <= mMaxBoneCount )
nuclear@0 154 return;
nuclear@0 155
nuclear@0 156 // necessary optimisation: build a list of all affecting bones for each vertex
nuclear@0 157 // TODO: (thom) maybe add a custom allocator here to avoid allocating tens of thousands of small arrays
nuclear@0 158 typedef std::pair<size_t, float> BoneWeight;
nuclear@0 159 std::vector< std::vector<BoneWeight> > vertexBones( pMesh->mNumVertices);
nuclear@0 160 for( size_t a = 0; a < pMesh->mNumBones; ++a)
nuclear@0 161 {
nuclear@0 162 const aiBone* bone = pMesh->mBones[a];
nuclear@0 163 for( size_t b = 0; b < bone->mNumWeights; ++b)
nuclear@0 164 vertexBones[ bone->mWeights[b].mVertexId ].push_back( BoneWeight( a, bone->mWeights[b].mWeight));
nuclear@0 165 }
nuclear@0 166
nuclear@0 167 size_t numFacesHandled = 0;
nuclear@0 168 std::vector<bool> isFaceHandled( pMesh->mNumFaces, false);
nuclear@0 169 while( numFacesHandled < pMesh->mNumFaces )
nuclear@0 170 {
nuclear@0 171 // which bones are used in the current submesh
nuclear@0 172 size_t numBones = 0;
nuclear@0 173 std::vector<bool> isBoneUsed( pMesh->mNumBones, false);
nuclear@0 174 // indices of the faces which are going to go into this submesh
nuclear@0 175 std::vector<size_t> subMeshFaces;
nuclear@0 176 subMeshFaces.reserve( pMesh->mNumFaces);
nuclear@0 177 // accumulated vertex count of all the faces in this submesh
nuclear@0 178 size_t numSubMeshVertices = 0;
nuclear@0 179 // a small local array of new bones for the current face. State of all used bones for that face
nuclear@0 180 // can only be updated AFTER the face is completely analysed. Thanks to imre for the fix.
nuclear@0 181 std::vector<size_t> newBonesAtCurrentFace;
nuclear@0 182
nuclear@0 183 // add faces to the new submesh as long as all bones affecting the faces' vertices fit in the limit
nuclear@0 184 for( size_t a = 0; a < pMesh->mNumFaces; ++a)
nuclear@0 185 {
nuclear@0 186 // skip if the face is already stored in a submesh
nuclear@0 187 if( isFaceHandled[a] )
nuclear@0 188 continue;
nuclear@0 189
nuclear@0 190 const aiFace& face = pMesh->mFaces[a];
nuclear@0 191 // check every vertex if its bones would still fit into the current submesh
nuclear@0 192 for( size_t b = 0; b < face.mNumIndices; ++b )
nuclear@0 193 {
nuclear@0 194 const std::vector<BoneWeight>& vb = vertexBones[face.mIndices[b]];
nuclear@0 195 for( size_t c = 0; c < vb.size(); ++c)
nuclear@0 196 {
nuclear@0 197 size_t boneIndex = vb[c].first;
nuclear@0 198 // if the bone is already used in this submesh, it's ok
nuclear@0 199 if( isBoneUsed[boneIndex] )
nuclear@0 200 continue;
nuclear@0 201
nuclear@0 202 // if it's not used, yet, we would need to add it. Store its bone index
nuclear@0 203 if( std::find( newBonesAtCurrentFace.begin(), newBonesAtCurrentFace.end(), boneIndex) == newBonesAtCurrentFace.end() )
nuclear@0 204 newBonesAtCurrentFace.push_back( boneIndex);
nuclear@0 205 }
nuclear@0 206 }
nuclear@0 207
nuclear@0 208 // leave out the face if the new bones required for this face don't fit the bone count limit anymore
nuclear@0 209 if( numBones + newBonesAtCurrentFace.size() > mMaxBoneCount )
nuclear@0 210 continue;
nuclear@0 211
nuclear@0 212 // mark all new bones as necessary
nuclear@0 213 while( !newBonesAtCurrentFace.empty() )
nuclear@0 214 {
nuclear@0 215 size_t newIndex = newBonesAtCurrentFace.back();
nuclear@0 216 newBonesAtCurrentFace.pop_back(); // this also avoids the deallocation which comes with a clear()
nuclear@0 217 if( isBoneUsed[newIndex] )
nuclear@0 218 continue;
nuclear@0 219
nuclear@0 220 isBoneUsed[newIndex] = true;
nuclear@0 221 numBones++;
nuclear@0 222 }
nuclear@0 223
nuclear@0 224 // store the face index and the vertex count
nuclear@0 225 subMeshFaces.push_back( a);
nuclear@0 226 numSubMeshVertices += face.mNumIndices;
nuclear@0 227
nuclear@0 228 // remember that this face is handled
nuclear@0 229 isFaceHandled[a] = true;
nuclear@0 230 numFacesHandled++;
nuclear@0 231 }
nuclear@0 232
nuclear@0 233 // create a new mesh to hold this subset of the source mesh
nuclear@0 234 aiMesh* newMesh = new aiMesh;
nuclear@0 235 if( pMesh->mName.length > 0 )
nuclear@0 236 newMesh->mName.Set( boost::str( boost::format( "%s_sub%d") % pMesh->mName.data % poNewMeshes.size()));
nuclear@0 237 newMesh->mMaterialIndex = pMesh->mMaterialIndex;
nuclear@0 238 newMesh->mPrimitiveTypes = pMesh->mPrimitiveTypes;
nuclear@0 239 poNewMeshes.push_back( newMesh);
nuclear@0 240
nuclear@0 241 // create all the arrays for this mesh if the old mesh contained them
nuclear@0 242 newMesh->mNumVertices = numSubMeshVertices;
nuclear@0 243 newMesh->mNumFaces = subMeshFaces.size();
nuclear@0 244 newMesh->mVertices = new aiVector3D[newMesh->mNumVertices];
nuclear@0 245 if( pMesh->HasNormals() )
nuclear@0 246 newMesh->mNormals = new aiVector3D[newMesh->mNumVertices];
nuclear@0 247 if( pMesh->HasTangentsAndBitangents() )
nuclear@0 248 {
nuclear@0 249 newMesh->mTangents = new aiVector3D[newMesh->mNumVertices];
nuclear@0 250 newMesh->mBitangents = new aiVector3D[newMesh->mNumVertices];
nuclear@0 251 }
nuclear@0 252 for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a )
nuclear@0 253 {
nuclear@0 254 if( pMesh->HasTextureCoords( a) )
nuclear@0 255 newMesh->mTextureCoords[a] = new aiVector3D[newMesh->mNumVertices];
nuclear@0 256 newMesh->mNumUVComponents[a] = pMesh->mNumUVComponents[a];
nuclear@0 257 }
nuclear@0 258 for( size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a )
nuclear@0 259 {
nuclear@0 260 if( pMesh->HasVertexColors( a) )
nuclear@0 261 newMesh->mColors[a] = new aiColor4D[newMesh->mNumVertices];
nuclear@0 262 }
nuclear@0 263
nuclear@0 264 // and copy over the data, generating faces with linear indices along the way
nuclear@0 265 newMesh->mFaces = new aiFace[subMeshFaces.size()];
nuclear@0 266 size_t nvi = 0; // next vertex index
nuclear@0 267 std::vector<size_t> previousVertexIndices( numSubMeshVertices, std::numeric_limits<size_t>::max()); // per new vertex: its index in the source mesh
nuclear@0 268 for( size_t a = 0; a < subMeshFaces.size(); ++a )
nuclear@0 269 {
nuclear@0 270 const aiFace& srcFace = pMesh->mFaces[subMeshFaces[a]];
nuclear@0 271 aiFace& dstFace = newMesh->mFaces[a];
nuclear@0 272 dstFace.mNumIndices = srcFace.mNumIndices;
nuclear@0 273 dstFace.mIndices = new unsigned int[dstFace.mNumIndices];
nuclear@0 274
nuclear@0 275 // accumulate linearly all the vertices of the source face
nuclear@0 276 for( size_t b = 0; b < dstFace.mNumIndices; ++b )
nuclear@0 277 {
nuclear@0 278 size_t srcIndex = srcFace.mIndices[b];
nuclear@0 279 dstFace.mIndices[b] = nvi;
nuclear@0 280 previousVertexIndices[nvi] = srcIndex;
nuclear@0 281
nuclear@0 282 newMesh->mVertices[nvi] = pMesh->mVertices[srcIndex];
nuclear@0 283 if( pMesh->HasNormals() )
nuclear@0 284 newMesh->mNormals[nvi] = pMesh->mNormals[srcIndex];
nuclear@0 285 if( pMesh->HasTangentsAndBitangents() )
nuclear@0 286 {
nuclear@0 287 newMesh->mTangents[nvi] = pMesh->mTangents[srcIndex];
nuclear@0 288 newMesh->mBitangents[nvi] = pMesh->mBitangents[srcIndex];
nuclear@0 289 }
nuclear@0 290 for( size_t c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++c )
nuclear@0 291 {
nuclear@0 292 if( pMesh->HasTextureCoords( c) )
nuclear@0 293 newMesh->mTextureCoords[c][nvi] = pMesh->mTextureCoords[c][srcIndex];
nuclear@0 294 }
nuclear@0 295 for( size_t c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS; ++c )
nuclear@0 296 {
nuclear@0 297 if( pMesh->HasVertexColors( c) )
nuclear@0 298 newMesh->mColors[c][nvi] = pMesh->mColors[c][srcIndex];
nuclear@0 299 }
nuclear@0 300
nuclear@0 301 nvi++;
nuclear@0 302 }
nuclear@0 303 }
nuclear@0 304
nuclear@0 305 ai_assert( nvi == numSubMeshVertices );
nuclear@0 306
nuclear@0 307 // Create the bones for the new submesh: first create the bone array
nuclear@0 308 newMesh->mNumBones = 0;
nuclear@0 309 newMesh->mBones = new aiBone*[numBones];
nuclear@0 310
nuclear@0 311 std::vector<size_t> mappedBoneIndex( pMesh->mNumBones, std::numeric_limits<size_t>::max());
nuclear@0 312 for( size_t a = 0; a < pMesh->mNumBones; ++a )
nuclear@0 313 {
nuclear@0 314 if( !isBoneUsed[a] )
nuclear@0 315 continue;
nuclear@0 316
nuclear@0 317 // create the new bone
nuclear@0 318 const aiBone* srcBone = pMesh->mBones[a];
nuclear@0 319 aiBone* dstBone = new aiBone;
nuclear@0 320 mappedBoneIndex[a] = newMesh->mNumBones;
nuclear@0 321 newMesh->mBones[newMesh->mNumBones++] = dstBone;
nuclear@0 322 dstBone->mName = srcBone->mName;
nuclear@0 323 dstBone->mOffsetMatrix = srcBone->mOffsetMatrix;
nuclear@0 324 dstBone->mNumWeights = 0;
nuclear@0 325 }
nuclear@0 326
nuclear@0 327 ai_assert( newMesh->mNumBones == numBones );
nuclear@0 328
nuclear@0 329 // iterate over all new vertices and count which bones affected its old vertex in the source mesh
nuclear@0 330 for( size_t a = 0; a < numSubMeshVertices; ++a )
nuclear@0 331 {
nuclear@0 332 size_t oldIndex = previousVertexIndices[a];
nuclear@0 333 const std::vector<BoneWeight>& bonesOnThisVertex = vertexBones[oldIndex];
nuclear@0 334
nuclear@0 335 for( size_t b = 0; b < bonesOnThisVertex.size(); ++b )
nuclear@0 336 {
nuclear@0 337 size_t newBoneIndex = mappedBoneIndex[ bonesOnThisVertex[b].first ];
nuclear@0 338 if( newBoneIndex != std::numeric_limits<size_t>::max() )
nuclear@0 339 newMesh->mBones[newBoneIndex]->mNumWeights++;
nuclear@0 340 }
nuclear@0 341 }
nuclear@0 342
nuclear@0 343 // allocate all bone weight arrays accordingly
nuclear@0 344 for( size_t a = 0; a < newMesh->mNumBones; ++a )
nuclear@0 345 {
nuclear@0 346 aiBone* bone = newMesh->mBones[a];
nuclear@0 347 ai_assert( bone->mNumWeights > 0 );
nuclear@0 348 bone->mWeights = new aiVertexWeight[bone->mNumWeights];
nuclear@0 349 bone->mNumWeights = 0; // for counting up in the next step
nuclear@0 350 }
nuclear@0 351
nuclear@0 352 // now copy all the bone vertex weights for all the vertices which made it into the new submesh
nuclear@0 353 for( size_t a = 0; a < numSubMeshVertices; ++a)
nuclear@0 354 {
nuclear@0 355 // find the source vertex for it in the source mesh
nuclear@0 356 size_t previousIndex = previousVertexIndices[a];
nuclear@0 357 // these bones were affecting it
nuclear@0 358 const std::vector<BoneWeight>& bonesOnThisVertex = vertexBones[previousIndex];
nuclear@0 359 // all of the bones affecting it should be present in the new submesh, or else
nuclear@0 360 // the face it comprises shouldn't be present
nuclear@0 361 for( size_t b = 0; b < bonesOnThisVertex.size(); ++b)
nuclear@0 362 {
nuclear@0 363 size_t newBoneIndex = mappedBoneIndex[ bonesOnThisVertex[b].first ];
nuclear@0 364 ai_assert( newBoneIndex != std::numeric_limits<size_t>::max() );
nuclear@0 365 aiVertexWeight* dstWeight = newMesh->mBones[newBoneIndex]->mWeights + newMesh->mBones[newBoneIndex]->mNumWeights;
nuclear@0 366 newMesh->mBones[newBoneIndex]->mNumWeights++;
nuclear@0 367
nuclear@0 368 dstWeight->mVertexId = a;
nuclear@0 369 dstWeight->mWeight = bonesOnThisVertex[b].second;
nuclear@0 370 }
nuclear@0 371 }
nuclear@0 372
nuclear@0 373 // I have the strange feeling that this will break apart at some point in time...
nuclear@0 374 }
nuclear@0 375 }
nuclear@0 376
nuclear@0 377 // ------------------------------------------------------------------------------------------------
nuclear@0 378 // Recursively updates the node's mesh list to account for the changed mesh list
nuclear@0 379 void SplitByBoneCountProcess::UpdateNode( aiNode* pNode) const
nuclear@0 380 {
nuclear@0 381 // rebuild the node's mesh index list
nuclear@0 382 if( pNode->mNumMeshes > 0 )
nuclear@0 383 {
nuclear@0 384 std::vector<size_t> newMeshList;
nuclear@0 385 for( size_t a = 0; a < pNode->mNumMeshes; ++a)
nuclear@0 386 {
nuclear@0 387 size_t srcIndex = pNode->mMeshes[a];
nuclear@0 388 const std::vector<size_t>& replaceMeshes = mSubMeshIndices[srcIndex];
nuclear@0 389 newMeshList.insert( newMeshList.end(), replaceMeshes.begin(), replaceMeshes.end());
nuclear@0 390 }
nuclear@0 391
nuclear@0 392 delete pNode->mMeshes;
nuclear@0 393 pNode->mNumMeshes = newMeshList.size();
nuclear@0 394 pNode->mMeshes = new unsigned int[pNode->mNumMeshes];
nuclear@0 395 std::copy( newMeshList.begin(), newMeshList.end(), pNode->mMeshes);
nuclear@0 396 }
nuclear@0 397
nuclear@0 398 // do that also recursively for all children
nuclear@0 399 for( size_t a = 0; a < pNode->mNumChildren; ++a )
nuclear@0 400 {
nuclear@0 401 UpdateNode( pNode->mChildren[a]);
nuclear@0 402 }
nuclear@0 403 }