1 | /* |
2 | --------------------------------------------------------------------------- |
3 | Open Asset Import Library (assimp) |
4 | --------------------------------------------------------------------------- |
5 | |
6 | Copyright (c) 2006-2017, assimp team |
7 | |
8 | |
9 | All rights reserved. |
10 | |
11 | Redistribution and use of this software in source and binary forms, |
12 | with or without modification, are permitted provided that the following |
13 | conditions are met: |
14 | |
15 | * Redistributions of source code must retain the above |
16 | copyright notice, this list of conditions and the |
17 | following disclaimer. |
18 | |
19 | * Redistributions in binary form must reproduce the above |
20 | copyright notice, this list of conditions and the |
21 | following disclaimer in the documentation and/or other |
22 | materials provided with the distribution. |
23 | |
24 | * Neither the name of the assimp team, nor the names of its |
25 | contributors may be used to endorse or promote products |
26 | derived from this software without specific prior |
27 | written permission of the assimp team. |
28 | |
29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
30 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
31 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
32 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
33 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
34 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
35 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
36 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
37 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
38 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
39 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
40 | --------------------------------------------------------------------------- |
41 | */ |
42 | |
43 | /** @file ASELoader.cpp |
44 | * @brief Implementation of the ASE importer class |
45 | */ |
46 | |
47 | #ifndef ASSIMP_BUILD_NO_ASE_IMPORTER |
48 | |
49 | #ifndef ASSIMP_BUILD_NO_3DS_IMPORTER |
50 | |
51 | // internal headers |
52 | #include "ASELoader.h" |
53 | #include "StringComparison.h" |
54 | #include "SkeletonMeshBuilder.h" |
55 | #include "TargetAnimation.h" |
56 | #include <assimp/Importer.hpp> |
57 | #include <assimp/IOSystem.hpp> |
58 | #include <assimp/DefaultLogger.hpp> |
59 | #include <assimp/scene.h> |
60 | #include <assimp/importerdesc.h> |
61 | |
62 | #include <memory> |
63 | |
64 | // utilities |
65 | #include "fast_atof.h" |
66 | |
67 | using namespace Assimp; |
68 | using namespace Assimp::ASE; |
69 | |
70 | static const aiImporterDesc desc = { |
71 | "ASE Importer" , |
72 | "" , |
73 | "" , |
74 | "Similar to 3DS but text-encoded" , |
75 | aiImporterFlags_SupportTextFlavour, |
76 | 0, |
77 | 0, |
78 | 0, |
79 | 0, |
80 | "ase ask" |
81 | }; |
82 | |
83 | // ------------------------------------------------------------------------------------------------ |
84 | // Constructor to be privately used by Importer |
85 | ASEImporter::ASEImporter() |
86 | : mParser(), |
87 | mBuffer(), |
88 | pcScene(), |
89 | configRecomputeNormals(), |
90 | noSkeletonMesh() |
91 | {} |
92 | |
93 | // ------------------------------------------------------------------------------------------------ |
94 | // Destructor, private as well |
95 | ASEImporter::~ASEImporter() |
96 | {} |
97 | |
98 | // ------------------------------------------------------------------------------------------------ |
99 | // Returns whether the class can handle the format of the given file. |
100 | bool ASEImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const |
101 | { |
102 | // check file extension |
103 | const std::string extension = GetExtension(pFile); |
104 | |
105 | if( extension == "ase" || extension == "ask" ) |
106 | return true; |
107 | |
108 | if ((!extension.length() || cs) && pIOHandler) { |
109 | const char* tokens[] = {"*3dsmax_asciiexport" }; |
110 | return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); |
111 | } |
112 | return false; |
113 | } |
114 | |
115 | // ------------------------------------------------------------------------------------------------ |
116 | // Loader meta information |
117 | const aiImporterDesc* ASEImporter::GetInfo () const |
118 | { |
119 | return &desc; |
120 | } |
121 | |
122 | // ------------------------------------------------------------------------------------------------ |
123 | // Setup configuration options |
124 | void ASEImporter::SetupProperties(const Importer* pImp) |
125 | { |
126 | configRecomputeNormals = (pImp->GetPropertyInteger( |
127 | AI_CONFIG_IMPORT_ASE_RECONSTRUCT_NORMALS,1) ? true : false); |
128 | |
129 | noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0; |
130 | } |
131 | |
132 | // ------------------------------------------------------------------------------------------------ |
133 | // Imports the given file into the given scene structure. |
134 | void ASEImporter::InternReadFile( const std::string& pFile, |
135 | aiScene* pScene, IOSystem* pIOHandler) |
136 | { |
137 | std::unique_ptr<IOStream> file( pIOHandler->Open( pFile, "rb" )); |
138 | |
139 | // Check whether we can read from the file |
140 | if( file.get() == NULL) { |
141 | throw DeadlyImportError( "Failed to open ASE file " + pFile + "." ); |
142 | } |
143 | |
144 | // Allocate storage and copy the contents of the file to a memory buffer |
145 | std::vector<char> mBuffer2; |
146 | TextFileToBuffer(file.get(),mBuffer2); |
147 | |
148 | this->mBuffer = &mBuffer2[0]; |
149 | this->pcScene = pScene; |
150 | |
151 | // ------------------------------------------------------------------ |
152 | // Guess the file format by looking at the extension |
153 | // ASC is considered to be the older format 110, |
154 | // ASE is the actual version 200 (that is currently written by max) |
155 | // ------------------------------------------------------------------ |
156 | unsigned int defaultFormat; |
157 | std::string::size_type s = pFile.length()-1; |
158 | switch (pFile.c_str()[s]) { |
159 | |
160 | case 'C': |
161 | case 'c': |
162 | defaultFormat = AI_ASE_OLD_FILE_FORMAT; |
163 | break; |
164 | default: |
165 | defaultFormat = AI_ASE_NEW_FILE_FORMAT; |
166 | }; |
167 | |
168 | // Construct an ASE parser and parse the file |
169 | ASE::Parser parser(mBuffer,defaultFormat); |
170 | mParser = &parser; |
171 | mParser->Parse(); |
172 | |
173 | //------------------------------------------------------------------ |
174 | // Check whether we god at least one mesh. If we did - generate |
175 | // materials and copy meshes. |
176 | // ------------------------------------------------------------------ |
177 | if ( !mParser->m_vMeshes.empty()) { |
178 | |
179 | // If absolutely no material has been loaded from the file |
180 | // we need to generate a default material |
181 | GenerateDefaultMaterial(); |
182 | |
183 | // process all meshes |
184 | bool tookNormals = false; |
185 | std::vector<aiMesh*> avOutMeshes; |
186 | avOutMeshes.reserve(mParser->m_vMeshes.size()*2); |
187 | for (std::vector<ASE::Mesh>::iterator i = mParser->m_vMeshes.begin();i != mParser->m_vMeshes.end();++i) { |
188 | if ((*i).bSkip) { |
189 | continue; |
190 | } |
191 | BuildUniqueRepresentation(*i); |
192 | |
193 | // Need to generate proper vertex normals if necessary |
194 | if(GenerateNormals(*i)) { |
195 | tookNormals = true; |
196 | } |
197 | |
198 | // Convert all meshes to aiMesh objects |
199 | ConvertMeshes(*i,avOutMeshes); |
200 | } |
201 | if (tookNormals) { |
202 | DefaultLogger::get()->debug("ASE: Taking normals from the file. Use " |
203 | "the AI_CONFIG_IMPORT_ASE_RECONSTRUCT_NORMALS setting if you " |
204 | "experience problems" ); |
205 | } |
206 | |
207 | // Now build the output mesh list. Remove dummies |
208 | pScene->mNumMeshes = (unsigned int)avOutMeshes.size(); |
209 | aiMesh** pp = pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; |
210 | for (std::vector<aiMesh*>::const_iterator i = avOutMeshes.begin();i != avOutMeshes.end();++i) { |
211 | if (!(*i)->mNumFaces) { |
212 | continue; |
213 | } |
214 | *pp++ = *i; |
215 | } |
216 | pScene->mNumMeshes = (unsigned int)(pp - pScene->mMeshes); |
217 | |
218 | // Build final material indices (remove submaterials and setup |
219 | // the final list) |
220 | BuildMaterialIndices(); |
221 | } |
222 | |
223 | // ------------------------------------------------------------------ |
224 | // Copy all scene graph nodes - lights, cameras, dummies and meshes |
225 | // into one huge list. |
226 | //------------------------------------------------------------------ |
227 | std::vector<BaseNode*> nodes; |
228 | nodes.reserve(mParser->m_vMeshes.size() +mParser->m_vLights.size() |
229 | + mParser->m_vCameras.size() + mParser->m_vDummies.size()); |
230 | |
231 | // Lights |
232 | for (auto &light : mParser->m_vLights)nodes.push_back(&light); |
233 | // Cameras |
234 | for (auto &camera : mParser->m_vCameras)nodes.push_back(&camera); |
235 | // Meshes |
236 | for (auto &mesh : mParser->m_vMeshes)nodes.push_back(&mesh); |
237 | // Dummies |
238 | for (auto &dummy : mParser->m_vDummies)nodes.push_back(&dummy); |
239 | |
240 | // build the final node graph |
241 | BuildNodes(nodes); |
242 | |
243 | // build output animations |
244 | BuildAnimations(nodes); |
245 | |
246 | // build output cameras |
247 | BuildCameras(); |
248 | |
249 | // build output lights |
250 | BuildLights(); |
251 | |
252 | // ------------------------------------------------------------------ |
253 | // If we have no meshes use the SkeletonMeshBuilder helper class |
254 | // to build a mesh for the animation skeleton |
255 | // FIXME: very strange results |
256 | // ------------------------------------------------------------------ |
257 | if (!pScene->mNumMeshes) { |
258 | pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; |
259 | if (!noSkeletonMesh) { |
260 | SkeletonMeshBuilder skeleton(pScene); |
261 | } |
262 | } |
263 | } |
264 | // ------------------------------------------------------------------------------------------------ |
265 | void ASEImporter::GenerateDefaultMaterial() |
266 | { |
267 | ai_assert(NULL != mParser); |
268 | |
269 | bool bHas = false; |
270 | for (std::vector<ASE::Mesh>::iterator i = mParser->m_vMeshes.begin();i != mParser->m_vMeshes.end();++i) { |
271 | if ((*i).bSkip)continue; |
272 | if (ASE::Face::DEFAULT_MATINDEX == (*i).iMaterialIndex) { |
273 | (*i).iMaterialIndex = (unsigned int)mParser->m_vMaterials.size(); |
274 | bHas = true; |
275 | } |
276 | } |
277 | if (bHas || mParser->m_vMaterials.empty()) { |
278 | // add a simple material without submaterials to the parser's list |
279 | mParser->m_vMaterials.push_back ( ASE::Material() ); |
280 | ASE::Material& mat = mParser->m_vMaterials.back(); |
281 | |
282 | mat.mDiffuse = aiColor3D(0.6f,0.6f,0.6f); |
283 | mat.mSpecular = aiColor3D(1.0f,1.0f,1.0f); |
284 | mat.mAmbient = aiColor3D(0.05f,0.05f,0.05f); |
285 | mat.mShading = Discreet3DS::Gouraud; |
286 | mat.mName = AI_DEFAULT_MATERIAL_NAME; |
287 | } |
288 | } |
289 | |
290 | // ------------------------------------------------------------------------------------------------ |
291 | void ASEImporter::BuildAnimations(const std::vector<BaseNode*>& nodes) |
292 | { |
293 | // check whether we have at least one mesh which has animations |
294 | std::vector<ASE::BaseNode*>::const_iterator i = nodes.begin(); |
295 | unsigned int iNum = 0; |
296 | for (;i != nodes.end();++i) { |
297 | |
298 | // TODO: Implement Bezier & TCB support |
299 | if ((*i)->mAnim.mPositionType != ASE::Animation::TRACK) { |
300 | DefaultLogger::get()->warn("ASE: Position controller uses Bezier/TCB keys. " |
301 | "This is not supported." ); |
302 | } |
303 | if ((*i)->mAnim.mRotationType != ASE::Animation::TRACK) { |
304 | DefaultLogger::get()->warn("ASE: Rotation controller uses Bezier/TCB keys. " |
305 | "This is not supported." ); |
306 | } |
307 | if ((*i)->mAnim.mScalingType != ASE::Animation::TRACK) { |
308 | DefaultLogger::get()->warn("ASE: Position controller uses Bezier/TCB keys. " |
309 | "This is not supported." ); |
310 | } |
311 | |
312 | // We compare against 1 here - firstly one key is not |
313 | // really an animation and secondly MAX writes dummies |
314 | // that represent the node transformation. |
315 | if ((*i)->mAnim.akeyPositions.size()>1 || (*i)->mAnim.akeyRotations.size()>1 || (*i)->mAnim.akeyScaling.size()>1){ |
316 | ++iNum; |
317 | } |
318 | if ((*i)->mTargetAnim.akeyPositions.size() > 1 && is_not_qnan( (*i)->mTargetPosition.x )) { |
319 | ++iNum; |
320 | } |
321 | } |
322 | if (iNum) { |
323 | // Generate a new animation channel and setup everything for it |
324 | pcScene->mNumAnimations = 1; |
325 | pcScene->mAnimations = new aiAnimation*[1]; |
326 | aiAnimation* pcAnim = pcScene->mAnimations[0] = new aiAnimation(); |
327 | pcAnim->mNumChannels = iNum; |
328 | pcAnim->mChannels = new aiNodeAnim*[iNum]; |
329 | pcAnim->mTicksPerSecond = mParser->iFrameSpeed * mParser->iTicksPerFrame; |
330 | |
331 | iNum = 0; |
332 | |
333 | // Now iterate through all meshes and collect all data we can find |
334 | for (i = nodes.begin();i != nodes.end();++i) { |
335 | |
336 | ASE::BaseNode* me = *i; |
337 | if ( me->mTargetAnim.akeyPositions.size() > 1 && is_not_qnan( me->mTargetPosition.x )) { |
338 | // Generate an extra channel for the camera/light target. |
339 | // BuildNodes() does also generate an extra node, named |
340 | // <baseName>.Target. |
341 | aiNodeAnim* nd = pcAnim->mChannels[iNum++] = new aiNodeAnim(); |
342 | nd->mNodeName.Set(me->mName + ".Target" ); |
343 | |
344 | // If there is no input position channel we will need |
345 | // to supply the default position from the node's |
346 | // local transformation matrix. |
347 | /*TargetAnimationHelper helper; |
348 | if (me->mAnim.akeyPositions.empty()) |
349 | { |
350 | aiMatrix4x4& mat = (*i)->mTransform; |
351 | helper.SetFixedMainAnimationChannel(aiVector3D( |
352 | mat.a4, mat.b4, mat.c4)); |
353 | } |
354 | else helper.SetMainAnimationChannel (&me->mAnim.akeyPositions); |
355 | helper.SetTargetAnimationChannel (&me->mTargetAnim.akeyPositions); |
356 | |
357 | helper.Process(&me->mTargetAnim.akeyPositions);*/ |
358 | |
359 | // Allocate the key array and fill it |
360 | nd->mNumPositionKeys = (unsigned int) me->mTargetAnim.akeyPositions.size(); |
361 | nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys]; |
362 | |
363 | ::memcpy(nd->mPositionKeys,&me->mTargetAnim.akeyPositions[0], |
364 | nd->mNumPositionKeys * sizeof(aiVectorKey)); |
365 | } |
366 | |
367 | if (me->mAnim.akeyPositions.size() > 1 || me->mAnim.akeyRotations.size() > 1 || me->mAnim.akeyScaling.size() > 1) { |
368 | // Begin a new node animation channel for this node |
369 | aiNodeAnim* nd = pcAnim->mChannels[iNum++] = new aiNodeAnim(); |
370 | nd->mNodeName.Set(me->mName); |
371 | |
372 | // copy position keys |
373 | if (me->mAnim.akeyPositions.size() > 1 ) |
374 | { |
375 | // Allocate the key array and fill it |
376 | nd->mNumPositionKeys = (unsigned int) me->mAnim.akeyPositions.size(); |
377 | nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys]; |
378 | |
379 | ::memcpy(nd->mPositionKeys,&me->mAnim.akeyPositions[0], |
380 | nd->mNumPositionKeys * sizeof(aiVectorKey)); |
381 | } |
382 | // copy rotation keys |
383 | if (me->mAnim.akeyRotations.size() > 1 ) { |
384 | // Allocate the key array and fill it |
385 | nd->mNumRotationKeys = (unsigned int) me->mAnim.akeyRotations.size(); |
386 | nd->mRotationKeys = new aiQuatKey[nd->mNumRotationKeys]; |
387 | |
388 | // -------------------------------------------------------------------- |
389 | // Rotation keys are offsets to the previous keys. |
390 | // We have the quaternion representations of all |
391 | // of them, so we just need to concatenate all |
392 | // (unit-length) quaternions to get the absolute |
393 | // rotations. |
394 | // Rotation keys are ABSOLUTE for older files |
395 | // -------------------------------------------------------------------- |
396 | |
397 | aiQuaternion cur; |
398 | for (unsigned int a = 0; a < nd->mNumRotationKeys;++a) { |
399 | aiQuatKey q = me->mAnim.akeyRotations[a]; |
400 | |
401 | if (mParser->iFileFormat > 110) { |
402 | cur = (a ? cur*q.mValue : q.mValue); |
403 | q.mValue = cur.Normalize(); |
404 | } |
405 | nd->mRotationKeys[a] = q; |
406 | |
407 | // need this to get to Assimp quaternion conventions |
408 | nd->mRotationKeys[a].mValue.w *= -1.f; |
409 | } |
410 | } |
411 | // copy scaling keys |
412 | if (me->mAnim.akeyScaling.size() > 1 ) { |
413 | // Allocate the key array and fill it |
414 | nd->mNumScalingKeys = (unsigned int) me->mAnim.akeyScaling.size(); |
415 | nd->mScalingKeys = new aiVectorKey[nd->mNumScalingKeys]; |
416 | |
417 | ::memcpy(nd->mScalingKeys,&me->mAnim.akeyScaling[0], |
418 | nd->mNumScalingKeys * sizeof(aiVectorKey)); |
419 | } |
420 | } |
421 | } |
422 | } |
423 | } |
424 | |
425 | // ------------------------------------------------------------------------------------------------ |
426 | // Build output cameras |
427 | void ASEImporter::BuildCameras() |
428 | { |
429 | if (!mParser->m_vCameras.empty()) { |
430 | pcScene->mNumCameras = (unsigned int)mParser->m_vCameras.size(); |
431 | pcScene->mCameras = new aiCamera*[pcScene->mNumCameras]; |
432 | |
433 | for (unsigned int i = 0; i < pcScene->mNumCameras;++i) { |
434 | aiCamera* out = pcScene->mCameras[i] = new aiCamera(); |
435 | ASE::Camera& in = mParser->m_vCameras[i]; |
436 | |
437 | // copy members |
438 | out->mClipPlaneFar = in.mFar; |
439 | out->mClipPlaneNear = (in.mNear ? in.mNear : 0.1f); |
440 | out->mHorizontalFOV = in.mFOV; |
441 | |
442 | out->mName.Set(in.mName); |
443 | } |
444 | } |
445 | } |
446 | |
447 | // ------------------------------------------------------------------------------------------------ |
448 | // Build output lights |
449 | void ASEImporter::BuildLights() |
450 | { |
451 | if (!mParser->m_vLights.empty()) { |
452 | pcScene->mNumLights = (unsigned int)mParser->m_vLights.size(); |
453 | pcScene->mLights = new aiLight*[pcScene->mNumLights]; |
454 | |
455 | for (unsigned int i = 0; i < pcScene->mNumLights;++i) { |
456 | aiLight* out = pcScene->mLights[i] = new aiLight(); |
457 | ASE::Light& in = mParser->m_vLights[i]; |
458 | |
459 | // The direction is encoded in the transformation matrix of the node. |
460 | // In 3DS MAX the light source points into negative Z direction if |
461 | // the node transformation is the identity. |
462 | out->mDirection = aiVector3D(0.f,0.f,-1.f); |
463 | |
464 | out->mName.Set(in.mName); |
465 | switch (in.mLightType) |
466 | { |
467 | case ASE::Light::TARGET: |
468 | out->mType = aiLightSource_SPOT; |
469 | out->mAngleInnerCone = AI_DEG_TO_RAD(in.mAngle); |
470 | out->mAngleOuterCone = (in.mFalloff ? AI_DEG_TO_RAD(in.mFalloff) : out->mAngleInnerCone); |
471 | break; |
472 | |
473 | case ASE::Light::DIRECTIONAL: |
474 | out->mType = aiLightSource_DIRECTIONAL; |
475 | break; |
476 | |
477 | default: |
478 | //case ASE::Light::OMNI: |
479 | out->mType = aiLightSource_POINT; |
480 | break; |
481 | }; |
482 | out->mColorDiffuse = out->mColorSpecular = in.mColor * in.mIntensity; |
483 | } |
484 | } |
485 | } |
486 | |
487 | // ------------------------------------------------------------------------------------------------ |
488 | void ASEImporter::AddNodes(const std::vector<BaseNode*>& nodes, |
489 | aiNode* pcParent,const char* szName) |
490 | { |
491 | aiMatrix4x4 m; |
492 | AddNodes(nodes,pcParent,szName,m); |
493 | } |
494 | |
495 | // ------------------------------------------------------------------------------------------------ |
496 | // Add meshes to a given node |
497 | void ASEImporter::AddMeshes(const ASE::BaseNode* snode,aiNode* node) |
498 | { |
499 | for (unsigned int i = 0; i < pcScene->mNumMeshes;++i) { |
500 | // Get the name of the mesh (the mesh instance has been temporarily stored in the third vertex color) |
501 | const aiMesh* pcMesh = pcScene->mMeshes[i]; |
502 | const ASE::Mesh* mesh = (const ASE::Mesh*)pcMesh->mColors[2]; |
503 | |
504 | if (mesh == snode) { |
505 | ++node->mNumMeshes; |
506 | } |
507 | } |
508 | |
509 | if(node->mNumMeshes) { |
510 | node->mMeshes = new unsigned int[node->mNumMeshes]; |
511 | for (unsigned int i = 0, p = 0; i < pcScene->mNumMeshes;++i) { |
512 | |
513 | const aiMesh* pcMesh = pcScene->mMeshes[i]; |
514 | const ASE::Mesh* mesh = (const ASE::Mesh*)pcMesh->mColors[2]; |
515 | if (mesh == snode) { |
516 | node->mMeshes[p++] = i; |
517 | |
518 | // Transform all vertices of the mesh back into their local space -> |
519 | // at the moment they are pretransformed |
520 | aiMatrix4x4 m = mesh->mTransform; |
521 | m.Inverse(); |
522 | |
523 | aiVector3D* pvCurPtr = pcMesh->mVertices; |
524 | const aiVector3D* pvEndPtr = pvCurPtr + pcMesh->mNumVertices; |
525 | while (pvCurPtr != pvEndPtr) { |
526 | *pvCurPtr = m * (*pvCurPtr); |
527 | pvCurPtr++; |
528 | } |
529 | |
530 | // Do the same for the normal vectors, if we have them. |
531 | // As always, inverse transpose. |
532 | if (pcMesh->mNormals) { |
533 | aiMatrix3x3 m3 = aiMatrix3x3( mesh->mTransform ); |
534 | m3.Transpose(); |
535 | |
536 | pvCurPtr = pcMesh->mNormals; |
537 | pvEndPtr = pvCurPtr + pcMesh->mNumVertices; |
538 | while (pvCurPtr != pvEndPtr) { |
539 | *pvCurPtr = m3 * (*pvCurPtr); |
540 | pvCurPtr++; |
541 | } |
542 | } |
543 | } |
544 | } |
545 | } |
546 | } |
547 | |
548 | // ------------------------------------------------------------------------------------------------ |
549 | // Add child nodes to a given parent node |
550 | void ASEImporter::AddNodes (const std::vector<BaseNode*>& nodes, |
551 | aiNode* pcParent, const char* szName, |
552 | const aiMatrix4x4& mat) |
553 | { |
554 | const size_t len = szName ? ::strlen(szName) : 0; |
555 | ai_assert(4 <= AI_MAX_NUMBER_OF_COLOR_SETS); |
556 | |
557 | // Receives child nodes for the pcParent node |
558 | std::vector<aiNode*> apcNodes; |
559 | |
560 | // Now iterate through all nodes in the scene and search for one |
561 | // which has *us* as parent. |
562 | for (std::vector<BaseNode*>::const_iterator it = nodes.begin(), end = nodes.end(); it != end; ++it) { |
563 | const BaseNode* snode = *it; |
564 | if (szName) { |
565 | if (len != snode->mParent.length() || ::strcmp(szName,snode->mParent.c_str())) |
566 | continue; |
567 | } |
568 | else if (snode->mParent.length()) |
569 | continue; |
570 | |
571 | (*it)->mProcessed = true; |
572 | |
573 | // Allocate a new node and add it to the output data structure |
574 | apcNodes.push_back(new aiNode()); |
575 | aiNode* node = apcNodes.back(); |
576 | |
577 | node->mName.Set((snode->mName.length() ? snode->mName.c_str() : "Unnamed_Node" )); |
578 | node->mParent = pcParent; |
579 | |
580 | // Setup the transformation matrix of the node |
581 | aiMatrix4x4 mParentAdjust = mat; |
582 | mParentAdjust.Inverse(); |
583 | node->mTransformation = mParentAdjust*snode->mTransform; |
584 | |
585 | // Add sub nodes - prevent stack overflow due to recursive parenting |
586 | if (node->mName != node->mParent->mName) { |
587 | AddNodes(nodes,node,node->mName.data,snode->mTransform); |
588 | } |
589 | |
590 | // Further processing depends on the type of the node |
591 | if (snode->mType == ASE::BaseNode::Mesh) { |
592 | // If the type of this node is "Mesh" we need to search |
593 | // the list of output meshes in the data structure for |
594 | // all those that belonged to this node once. This is |
595 | // slightly inconvinient here and a better solution should |
596 | // be used when this code is refactored next. |
597 | AddMeshes(snode,node); |
598 | } |
599 | else if (is_not_qnan( snode->mTargetPosition.x )) { |
600 | // If this is a target camera or light we generate a small |
601 | // child node which marks the position of the camera |
602 | // target (the direction information is contained in *this* |
603 | // node's animation track but the exact target position |
604 | // would be lost otherwise) |
605 | if (!node->mNumChildren) { |
606 | node->mChildren = new aiNode*[1]; |
607 | } |
608 | |
609 | aiNode* nd = new aiNode(); |
610 | |
611 | nd->mName.Set ( snode->mName + ".Target" ); |
612 | |
613 | nd->mTransformation.a4 = snode->mTargetPosition.x - snode->mTransform.a4; |
614 | nd->mTransformation.b4 = snode->mTargetPosition.y - snode->mTransform.b4; |
615 | nd->mTransformation.c4 = snode->mTargetPosition.z - snode->mTransform.c4; |
616 | |
617 | nd->mParent = node; |
618 | |
619 | // The .Target node is always the first child node |
620 | for (unsigned int m = 0; m < node->mNumChildren;++m) |
621 | node->mChildren[m+1] = node->mChildren[m]; |
622 | |
623 | node->mChildren[0] = nd; |
624 | node->mNumChildren++; |
625 | |
626 | // What we did is so great, it is at least worth a debug message |
627 | DefaultLogger::get()->debug("ASE: Generating separate target node (" +snode->mName+")" ); |
628 | } |
629 | } |
630 | |
631 | // Allocate enough space for the child nodes |
632 | // We allocate one slot more in case this is a target camera/light |
633 | pcParent->mNumChildren = (unsigned int)apcNodes.size(); |
634 | if (pcParent->mNumChildren) { |
635 | pcParent->mChildren = new aiNode*[apcNodes.size()+1 /* PLUS ONE !!! */]; |
636 | |
637 | // now build all nodes for our nice new children |
638 | for (unsigned int p = 0; p < apcNodes.size();++p) |
639 | pcParent->mChildren[p] = apcNodes[p]; |
640 | } |
641 | return; |
642 | } |
643 | |
644 | // ------------------------------------------------------------------------------------------------ |
645 | // Build the output node graph |
646 | void ASEImporter::BuildNodes(std::vector<BaseNode*>& nodes) { |
647 | ai_assert(NULL != pcScene); |
648 | |
649 | // allocate the one and only root node |
650 | aiNode* root = pcScene->mRootNode = new aiNode(); |
651 | root->mName.Set("<ASERoot>" ); |
652 | |
653 | // Setup the coordinate system transformation |
654 | pcScene->mRootNode->mNumChildren = 1; |
655 | pcScene->mRootNode->mChildren = new aiNode*[1]; |
656 | aiNode* ch = pcScene->mRootNode->mChildren[0] = new aiNode(); |
657 | ch->mParent = root; |
658 | |
659 | // Change the transformation matrix of all nodes |
660 | for (BaseNode *node : nodes) { |
661 | aiMatrix4x4& m = node->mTransform; |
662 | m.Transpose(); // row-order vs column-order |
663 | } |
664 | |
665 | // add all nodes |
666 | AddNodes(nodes,ch,NULL); |
667 | |
668 | // now iterate through al nodes and find those that have not yet |
669 | // been added to the nodegraph (= their parent could not be recognized) |
670 | std::vector<const BaseNode*> aiList; |
671 | for (std::vector<BaseNode*>::iterator it = nodes.begin(), end = nodes.end();it != end; ++it) { |
672 | if ((*it)->mProcessed) { |
673 | continue; |
674 | } |
675 | |
676 | // check whether our parent is known |
677 | bool bKnowParent = false; |
678 | |
679 | // search the list another time, starting *here* and try to find out whether |
680 | // there is a node that references *us* as a parent |
681 | for (std::vector<BaseNode*>::const_iterator it2 = nodes.begin();it2 != end; ++it2) { |
682 | if (it2 == it) { |
683 | continue; |
684 | } |
685 | |
686 | if ((*it2)->mName == (*it)->mParent) { |
687 | bKnowParent = true; |
688 | break; |
689 | } |
690 | } |
691 | if (!bKnowParent) { |
692 | aiList.push_back(*it); |
693 | } |
694 | } |
695 | |
696 | // Are there ane orphaned nodes? |
697 | if (!aiList.empty()) { |
698 | std::vector<aiNode*> apcNodes; |
699 | apcNodes.reserve(aiList.size() + pcScene->mRootNode->mNumChildren); |
700 | |
701 | for (unsigned int i = 0; i < pcScene->mRootNode->mNumChildren;++i) |
702 | apcNodes.push_back(pcScene->mRootNode->mChildren[i]); |
703 | |
704 | delete[] pcScene->mRootNode->mChildren; |
705 | for (std::vector<const BaseNode*>::/*const_*/iterator i = aiList.begin();i != aiList.end();++i) { |
706 | const ASE::BaseNode* src = *i; |
707 | |
708 | // The parent is not known, so we can assume that we must add |
709 | // this node to the root node of the whole scene |
710 | aiNode* pcNode = new aiNode(); |
711 | pcNode->mParent = pcScene->mRootNode; |
712 | pcNode->mName.Set(src->mName); |
713 | AddMeshes(src,pcNode); |
714 | AddNodes(nodes,pcNode,pcNode->mName.data); |
715 | apcNodes.push_back(pcNode); |
716 | } |
717 | |
718 | // Regenerate our output array |
719 | pcScene->mRootNode->mChildren = new aiNode*[apcNodes.size()]; |
720 | for (unsigned int i = 0; i < apcNodes.size();++i) |
721 | pcScene->mRootNode->mChildren[i] = apcNodes[i]; |
722 | |
723 | pcScene->mRootNode->mNumChildren = (unsigned int)apcNodes.size(); |
724 | } |
725 | |
726 | // Reset the third color set to NULL - we used this field to store a temporary pointer |
727 | for (unsigned int i = 0; i < pcScene->mNumMeshes;++i) |
728 | pcScene->mMeshes[i]->mColors[2] = NULL; |
729 | |
730 | // The root node should not have at least one child or the file is valid |
731 | if (!pcScene->mRootNode->mNumChildren) { |
732 | throw DeadlyImportError("ASE: No nodes loaded. The file is either empty or corrupt" ); |
733 | } |
734 | |
735 | // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system |
736 | pcScene->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f, |
737 | 0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f); |
738 | } |
739 | |
740 | // ------------------------------------------------------------------------------------------------ |
741 | // Convert the imported data to the internal verbose representation |
742 | void ASEImporter::BuildUniqueRepresentation(ASE::Mesh& mesh) { |
743 | // allocate output storage |
744 | std::vector<aiVector3D> mPositions; |
745 | std::vector<aiVector3D> amTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS]; |
746 | std::vector<aiColor4D> mVertexColors; |
747 | std::vector<aiVector3D> mNormals; |
748 | std::vector<BoneVertex> mBoneVertices; |
749 | |
750 | unsigned int iSize = (unsigned int)mesh.mFaces.size() * 3; |
751 | mPositions.resize(iSize); |
752 | |
753 | // optional texture coordinates |
754 | for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i) { |
755 | if (!mesh.amTexCoords[i].empty()) { |
756 | amTexCoords[i].resize(iSize); |
757 | } |
758 | } |
759 | // optional vertex colors |
760 | if (!mesh.mVertexColors.empty()) { |
761 | mVertexColors.resize(iSize); |
762 | } |
763 | |
764 | // optional vertex normals (vertex normals can simply be copied) |
765 | if (!mesh.mNormals.empty()) { |
766 | mNormals.resize(iSize); |
767 | } |
768 | // bone vertices. There is no need to change the bone list |
769 | if (!mesh.mBoneVertices.empty()) { |
770 | mBoneVertices.resize(iSize); |
771 | } |
772 | |
773 | // iterate through all faces in the mesh |
774 | unsigned int iCurrent = 0, fi = 0; |
775 | for (std::vector<ASE::Face>::iterator i = mesh.mFaces.begin();i != mesh.mFaces.end();++i,++fi) { |
776 | for (unsigned int n = 0; n < 3;++n,++iCurrent) |
777 | { |
778 | mPositions[iCurrent] = mesh.mPositions[(*i).mIndices[n]]; |
779 | |
780 | // add texture coordinates |
781 | for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) { |
782 | if (mesh.amTexCoords[c].empty())break; |
783 | amTexCoords[c][iCurrent] = mesh.amTexCoords[c][(*i).amUVIndices[c][n]]; |
784 | } |
785 | // add vertex colors |
786 | if (!mesh.mVertexColors.empty()) { |
787 | mVertexColors[iCurrent] = mesh.mVertexColors[(*i).mColorIndices[n]]; |
788 | } |
789 | // add normal vectors |
790 | if (!mesh.mNormals.empty()) { |
791 | mNormals[iCurrent] = mesh.mNormals[fi*3+n]; |
792 | mNormals[iCurrent].Normalize(); |
793 | } |
794 | |
795 | // handle bone vertices |
796 | if ((*i).mIndices[n] < mesh.mBoneVertices.size()) { |
797 | // (sometimes this will cause bone verts to be duplicated |
798 | // however, I' quite sure Schrompf' JoinVerticesStep |
799 | // will fix that again ...) |
800 | mBoneVertices[iCurrent] = mesh.mBoneVertices[(*i).mIndices[n]]; |
801 | } |
802 | (*i).mIndices[n] = iCurrent; |
803 | } |
804 | } |
805 | |
806 | // replace the old arrays |
807 | mesh.mNormals = mNormals; |
808 | mesh.mPositions = mPositions; |
809 | mesh.mVertexColors = mVertexColors; |
810 | |
811 | for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) |
812 | mesh.amTexCoords[c] = amTexCoords[c]; |
813 | } |
814 | |
815 | // ------------------------------------------------------------------------------------------------ |
816 | // Copy a texture from the ASE structs to the output material |
817 | void CopyASETexture(aiMaterial& mat, ASE::Texture& texture, aiTextureType type) |
818 | { |
819 | // Setup the texture name |
820 | aiString tex; |
821 | tex.Set( texture.mMapName); |
822 | mat.AddProperty( &tex, AI_MATKEY_TEXTURE(type,0)); |
823 | |
824 | // Setup the texture blend factor |
825 | if (is_not_qnan(texture.mTextureBlend)) |
826 | mat.AddProperty<ai_real>( &texture.mTextureBlend, 1, AI_MATKEY_TEXBLEND(type,0)); |
827 | |
828 | // Setup texture UV transformations |
829 | mat.AddProperty<ai_real>(&texture.mOffsetU,5,AI_MATKEY_UVTRANSFORM(type,0)); |
830 | } |
831 | |
832 | // ------------------------------------------------------------------------------------------------ |
833 | // Convert from ASE material to output material |
834 | void ASEImporter::ConvertMaterial(ASE::Material& mat) |
835 | { |
836 | // LARGE TODO: Much code her is copied from 3DS ... join them maybe? |
837 | |
838 | // Allocate the output material |
839 | mat.pcInstance = new aiMaterial(); |
840 | |
841 | // At first add the base ambient color of the |
842 | // scene to the material |
843 | mat.mAmbient.r += mParser->m_clrAmbient.r; |
844 | mat.mAmbient.g += mParser->m_clrAmbient.g; |
845 | mat.mAmbient.b += mParser->m_clrAmbient.b; |
846 | |
847 | aiString name; |
848 | name.Set( mat.mName); |
849 | mat.pcInstance->AddProperty( &name, AI_MATKEY_NAME); |
850 | |
851 | // material colors |
852 | mat.pcInstance->AddProperty( &mat.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT); |
853 | mat.pcInstance->AddProperty( &mat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE); |
854 | mat.pcInstance->AddProperty( &mat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR); |
855 | mat.pcInstance->AddProperty( &mat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE); |
856 | |
857 | // shininess |
858 | if (0.0f != mat.mSpecularExponent && 0.0f != mat.mShininessStrength) |
859 | { |
860 | mat.pcInstance->AddProperty( &mat.mSpecularExponent, 1, AI_MATKEY_SHININESS); |
861 | mat.pcInstance->AddProperty( &mat.mShininessStrength, 1, AI_MATKEY_SHININESS_STRENGTH); |
862 | } |
863 | // If there is no shininess, we can disable phong lighting |
864 | else if (D3DS::Discreet3DS::Metal == mat.mShading || |
865 | D3DS::Discreet3DS::Phong == mat.mShading || |
866 | D3DS::Discreet3DS::Blinn == mat.mShading) |
867 | { |
868 | mat.mShading = D3DS::Discreet3DS::Gouraud; |
869 | } |
870 | |
871 | // opacity |
872 | mat.pcInstance->AddProperty<ai_real>( &mat.mTransparency,1,AI_MATKEY_OPACITY); |
873 | |
874 | // Two sided rendering? |
875 | if (mat.mTwoSided) |
876 | { |
877 | int i = 1; |
878 | mat.pcInstance->AddProperty<int>(&i,1,AI_MATKEY_TWOSIDED); |
879 | } |
880 | |
881 | // shading mode |
882 | aiShadingMode eShading = aiShadingMode_NoShading; |
883 | switch (mat.mShading) |
884 | { |
885 | case D3DS::Discreet3DS::Flat: |
886 | eShading = aiShadingMode_Flat; break; |
887 | case D3DS::Discreet3DS::Phong : |
888 | eShading = aiShadingMode_Phong; break; |
889 | case D3DS::Discreet3DS::Blinn : |
890 | eShading = aiShadingMode_Blinn; break; |
891 | |
892 | // I don't know what "Wire" shading should be, |
893 | // assume it is simple lambertian diffuse (L dot N) shading |
894 | case D3DS::Discreet3DS::Wire: |
895 | { |
896 | // set the wireframe flag |
897 | unsigned int iWire = 1; |
898 | mat.pcInstance->AddProperty<int>( (int*)&iWire,1,AI_MATKEY_ENABLE_WIREFRAME); |
899 | } |
900 | case D3DS::Discreet3DS::Gouraud: |
901 | eShading = aiShadingMode_Gouraud; break; |
902 | case D3DS::Discreet3DS::Metal : |
903 | eShading = aiShadingMode_CookTorrance; break; |
904 | } |
905 | mat.pcInstance->AddProperty<int>( (int*)&eShading,1,AI_MATKEY_SHADING_MODEL); |
906 | |
907 | // DIFFUSE texture |
908 | if( mat.sTexDiffuse.mMapName.length() > 0) |
909 | CopyASETexture(*mat.pcInstance,mat.sTexDiffuse, aiTextureType_DIFFUSE); |
910 | |
911 | // SPECULAR texture |
912 | if( mat.sTexSpecular.mMapName.length() > 0) |
913 | CopyASETexture(*mat.pcInstance,mat.sTexSpecular, aiTextureType_SPECULAR); |
914 | |
915 | // AMBIENT texture |
916 | if( mat.sTexAmbient.mMapName.length() > 0) |
917 | CopyASETexture(*mat.pcInstance,mat.sTexAmbient, aiTextureType_AMBIENT); |
918 | |
919 | // OPACITY texture |
920 | if( mat.sTexOpacity.mMapName.length() > 0) |
921 | CopyASETexture(*mat.pcInstance,mat.sTexOpacity, aiTextureType_OPACITY); |
922 | |
923 | // EMISSIVE texture |
924 | if( mat.sTexEmissive.mMapName.length() > 0) |
925 | CopyASETexture(*mat.pcInstance,mat.sTexEmissive, aiTextureType_EMISSIVE); |
926 | |
927 | // BUMP texture |
928 | if( mat.sTexBump.mMapName.length() > 0) |
929 | CopyASETexture(*mat.pcInstance,mat.sTexBump, aiTextureType_HEIGHT); |
930 | |
931 | // SHININESS texture |
932 | if( mat.sTexShininess.mMapName.length() > 0) |
933 | CopyASETexture(*mat.pcInstance,mat.sTexShininess, aiTextureType_SHININESS); |
934 | |
935 | // store the name of the material itself, too |
936 | if( mat.mName.length() > 0) { |
937 | aiString tex;tex.Set( mat.mName); |
938 | mat.pcInstance->AddProperty( &tex, AI_MATKEY_NAME); |
939 | } |
940 | return; |
941 | } |
942 | |
943 | // ------------------------------------------------------------------------------------------------ |
944 | // Build output meshes |
945 | void ASEImporter::ConvertMeshes(ASE::Mesh& mesh, std::vector<aiMesh*>& avOutMeshes) |
946 | { |
947 | // validate the material index of the mesh |
948 | if (mesh.iMaterialIndex >= mParser->m_vMaterials.size()) { |
949 | mesh.iMaterialIndex = (unsigned int)mParser->m_vMaterials.size()-1; |
950 | DefaultLogger::get()->warn("Material index is out of range" ); |
951 | } |
952 | |
953 | // If the material the mesh is assigned to is consisting of submeshes, split it |
954 | if (!mParser->m_vMaterials[mesh.iMaterialIndex].avSubMaterials.empty()) { |
955 | std::vector<ASE::Material> vSubMaterials = mParser-> |
956 | m_vMaterials[mesh.iMaterialIndex].avSubMaterials; |
957 | |
958 | std::vector<unsigned int>* aiSplit = new std::vector<unsigned int>[vSubMaterials.size()]; |
959 | |
960 | // build a list of all faces per submaterial |
961 | for (unsigned int i = 0; i < mesh.mFaces.size();++i) { |
962 | // check range |
963 | if (mesh.mFaces[i].iMaterial >= vSubMaterials.size()) { |
964 | DefaultLogger::get()->warn("Submaterial index is out of range" ); |
965 | |
966 | // use the last material instead |
967 | aiSplit[vSubMaterials.size()-1].push_back(i); |
968 | } |
969 | else aiSplit[mesh.mFaces[i].iMaterial].push_back(i); |
970 | } |
971 | |
972 | // now generate submeshes |
973 | for (unsigned int p = 0; p < vSubMaterials.size();++p) { |
974 | if (!aiSplit[p].empty()) { |
975 | |
976 | aiMesh* p_pcOut = new aiMesh(); |
977 | p_pcOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; |
978 | |
979 | // let the sub material index |
980 | p_pcOut->mMaterialIndex = p; |
981 | |
982 | // we will need this material |
983 | mParser->m_vMaterials[mesh.iMaterialIndex].avSubMaterials[p].bNeed = true; |
984 | |
985 | // store the real index here ... color channel 3 |
986 | p_pcOut->mColors[3] = (aiColor4D*)(uintptr_t)mesh.iMaterialIndex; |
987 | |
988 | // store a pointer to the mesh in color channel 2 |
989 | p_pcOut->mColors[2] = (aiColor4D*) &mesh; |
990 | avOutMeshes.push_back(p_pcOut); |
991 | |
992 | // convert vertices |
993 | p_pcOut->mNumVertices = (unsigned int)aiSplit[p].size()*3; |
994 | p_pcOut->mNumFaces = (unsigned int)aiSplit[p].size(); |
995 | |
996 | // receive output vertex weights |
997 | std::vector<std::pair<unsigned int, float> > *avOutputBones = NULL; |
998 | if (!mesh.mBones.empty()) { |
999 | avOutputBones = new std::vector<std::pair<unsigned int, float> >[mesh.mBones.size()]; |
1000 | } |
1001 | |
1002 | // allocate enough storage for faces |
1003 | p_pcOut->mFaces = new aiFace[p_pcOut->mNumFaces]; |
1004 | |
1005 | unsigned int iBase = 0,iIndex; |
1006 | if (p_pcOut->mNumVertices) { |
1007 | p_pcOut->mVertices = new aiVector3D[p_pcOut->mNumVertices]; |
1008 | p_pcOut->mNormals = new aiVector3D[p_pcOut->mNumVertices]; |
1009 | for (unsigned int q = 0; q < aiSplit[p].size();++q) { |
1010 | |
1011 | iIndex = aiSplit[p][q]; |
1012 | |
1013 | p_pcOut->mFaces[q].mIndices = new unsigned int[3]; |
1014 | p_pcOut->mFaces[q].mNumIndices = 3; |
1015 | |
1016 | for (unsigned int t = 0; t < 3;++t, ++iBase) { |
1017 | const uint32_t iIndex2 = mesh.mFaces[iIndex].mIndices[t]; |
1018 | |
1019 | p_pcOut->mVertices[iBase] = mesh.mPositions [iIndex2]; |
1020 | p_pcOut->mNormals [iBase] = mesh.mNormals [iIndex2]; |
1021 | |
1022 | // convert bones, if existing |
1023 | if (!mesh.mBones.empty()) { |
1024 | ai_assert(avOutputBones); |
1025 | // check whether there is a vertex weight for this vertex index |
1026 | if (iIndex2 < mesh.mBoneVertices.size()) { |
1027 | |
1028 | for (std::vector<std::pair<int,float> >::const_iterator |
1029 | blubb = mesh.mBoneVertices[iIndex2].mBoneWeights.begin(); |
1030 | blubb != mesh.mBoneVertices[iIndex2].mBoneWeights.end();++blubb) { |
1031 | |
1032 | // NOTE: illegal cases have already been filtered out |
1033 | avOutputBones[(*blubb).first].push_back(std::pair<unsigned int, float>( |
1034 | iBase,(*blubb).second)); |
1035 | } |
1036 | } |
1037 | } |
1038 | p_pcOut->mFaces[q].mIndices[t] = iBase; |
1039 | } |
1040 | } |
1041 | } |
1042 | // convert texture coordinates (up to AI_MAX_NUMBER_OF_TEXTURECOORDS sets supported) |
1043 | for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) { |
1044 | if (!mesh.amTexCoords[c].empty()) |
1045 | { |
1046 | p_pcOut->mTextureCoords[c] = new aiVector3D[p_pcOut->mNumVertices]; |
1047 | iBase = 0; |
1048 | for (unsigned int q = 0; q < aiSplit[p].size();++q) { |
1049 | iIndex = aiSplit[p][q]; |
1050 | for (unsigned int t = 0; t < 3;++t) { |
1051 | p_pcOut->mTextureCoords[c][iBase++] = mesh.amTexCoords[c][mesh.mFaces[iIndex].mIndices[t]]; |
1052 | } |
1053 | } |
1054 | // Setup the number of valid vertex components |
1055 | p_pcOut->mNumUVComponents[c] = mesh.mNumUVComponents[c]; |
1056 | } |
1057 | } |
1058 | |
1059 | // Convert vertex colors (only one set supported) |
1060 | if (!mesh.mVertexColors.empty()){ |
1061 | p_pcOut->mColors[0] = new aiColor4D[p_pcOut->mNumVertices]; |
1062 | iBase = 0; |
1063 | for (unsigned int q = 0; q < aiSplit[p].size();++q) { |
1064 | iIndex = aiSplit[p][q]; |
1065 | for (unsigned int t = 0; t < 3;++t) { |
1066 | p_pcOut->mColors[0][iBase++] = mesh.mVertexColors[mesh.mFaces[iIndex].mIndices[t]]; |
1067 | } |
1068 | } |
1069 | } |
1070 | // Copy bones |
1071 | if (!mesh.mBones.empty()) { |
1072 | p_pcOut->mNumBones = 0; |
1073 | for (unsigned int mrspock = 0; mrspock < mesh.mBones.size();++mrspock) |
1074 | if (!avOutputBones[mrspock].empty())p_pcOut->mNumBones++; |
1075 | |
1076 | p_pcOut->mBones = new aiBone* [ p_pcOut->mNumBones ]; |
1077 | aiBone** pcBone = p_pcOut->mBones; |
1078 | for (unsigned int mrspock = 0; mrspock < mesh.mBones.size();++mrspock) |
1079 | { |
1080 | if (!avOutputBones[mrspock].empty()) { |
1081 | // we will need this bone. add it to the output mesh and |
1082 | // add all per-vertex weights |
1083 | aiBone* pc = *pcBone = new aiBone(); |
1084 | pc->mName.Set(mesh.mBones[mrspock].mName); |
1085 | |
1086 | pc->mNumWeights = (unsigned int)avOutputBones[mrspock].size(); |
1087 | pc->mWeights = new aiVertexWeight[pc->mNumWeights]; |
1088 | |
1089 | for (unsigned int captainkirk = 0; captainkirk < pc->mNumWeights;++captainkirk) |
1090 | { |
1091 | const std::pair<unsigned int,float>& ref = avOutputBones[mrspock][captainkirk]; |
1092 | pc->mWeights[captainkirk].mVertexId = ref.first; |
1093 | pc->mWeights[captainkirk].mWeight = ref.second; |
1094 | } |
1095 | ++pcBone; |
1096 | } |
1097 | } |
1098 | // delete allocated storage |
1099 | delete[] avOutputBones; |
1100 | } |
1101 | } |
1102 | } |
1103 | // delete storage |
1104 | delete[] aiSplit; |
1105 | } |
1106 | else |
1107 | { |
1108 | // Otherwise we can simply copy the data to one output mesh |
1109 | // This codepath needs less memory and uses fast memcpy()s |
1110 | // to do the actual copying. So I think it is worth the |
1111 | // effort here. |
1112 | |
1113 | aiMesh* p_pcOut = new aiMesh(); |
1114 | p_pcOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; |
1115 | |
1116 | // set an empty sub material index |
1117 | p_pcOut->mMaterialIndex = ASE::Face::DEFAULT_MATINDEX; |
1118 | mParser->m_vMaterials[mesh.iMaterialIndex].bNeed = true; |
1119 | |
1120 | // store the real index here ... in color channel 3 |
1121 | p_pcOut->mColors[3] = (aiColor4D*)(uintptr_t)mesh.iMaterialIndex; |
1122 | |
1123 | // store a pointer to the mesh in color channel 2 |
1124 | p_pcOut->mColors[2] = (aiColor4D*) &mesh; |
1125 | avOutMeshes.push_back(p_pcOut); |
1126 | |
1127 | // If the mesh hasn't faces or vertices, there are two cases |
1128 | // possible: 1. the model is invalid. 2. This is a dummy |
1129 | // helper object which we are going to remove later ... |
1130 | if (mesh.mFaces.empty() || mesh.mPositions.empty()) { |
1131 | return; |
1132 | } |
1133 | |
1134 | // convert vertices |
1135 | p_pcOut->mNumVertices = (unsigned int)mesh.mPositions.size(); |
1136 | p_pcOut->mNumFaces = (unsigned int)mesh.mFaces.size(); |
1137 | |
1138 | // allocate enough storage for faces |
1139 | p_pcOut->mFaces = new aiFace[p_pcOut->mNumFaces]; |
1140 | |
1141 | // copy vertices |
1142 | p_pcOut->mVertices = new aiVector3D[mesh.mPositions.size()]; |
1143 | memcpy(p_pcOut->mVertices,&mesh.mPositions[0], |
1144 | mesh.mPositions.size() * sizeof(aiVector3D)); |
1145 | |
1146 | // copy normals |
1147 | p_pcOut->mNormals = new aiVector3D[mesh.mNormals.size()]; |
1148 | memcpy(p_pcOut->mNormals,&mesh.mNormals[0], |
1149 | mesh.mNormals.size() * sizeof(aiVector3D)); |
1150 | |
1151 | // copy texture coordinates |
1152 | for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) { |
1153 | if (!mesh.amTexCoords[c].empty()) { |
1154 | p_pcOut->mTextureCoords[c] = new aiVector3D[mesh.amTexCoords[c].size()]; |
1155 | memcpy(p_pcOut->mTextureCoords[c],&mesh.amTexCoords[c][0], |
1156 | mesh.amTexCoords[c].size() * sizeof(aiVector3D)); |
1157 | |
1158 | // setup the number of valid vertex components |
1159 | p_pcOut->mNumUVComponents[c] = mesh.mNumUVComponents[c]; |
1160 | } |
1161 | } |
1162 | |
1163 | // copy vertex colors |
1164 | if (!mesh.mVertexColors.empty()) { |
1165 | p_pcOut->mColors[0] = new aiColor4D[mesh.mVertexColors.size()]; |
1166 | memcpy(p_pcOut->mColors[0],&mesh.mVertexColors[0], |
1167 | mesh.mVertexColors.size() * sizeof(aiColor4D)); |
1168 | } |
1169 | |
1170 | // copy faces |
1171 | for (unsigned int iFace = 0; iFace < p_pcOut->mNumFaces;++iFace) { |
1172 | p_pcOut->mFaces[iFace].mNumIndices = 3; |
1173 | p_pcOut->mFaces[iFace].mIndices = new unsigned int[3]; |
1174 | |
1175 | // copy indices |
1176 | p_pcOut->mFaces[iFace].mIndices[0] = mesh.mFaces[iFace].mIndices[0]; |
1177 | p_pcOut->mFaces[iFace].mIndices[1] = mesh.mFaces[iFace].mIndices[1]; |
1178 | p_pcOut->mFaces[iFace].mIndices[2] = mesh.mFaces[iFace].mIndices[2]; |
1179 | } |
1180 | |
1181 | // copy vertex bones |
1182 | if (!mesh.mBones.empty() && !mesh.mBoneVertices.empty()) { |
1183 | std::vector<std::vector<aiVertexWeight> > avBonesOut( mesh.mBones.size() ); |
1184 | |
1185 | // find all vertex weights for this bone |
1186 | unsigned int quak = 0; |
1187 | for (std::vector<BoneVertex>::const_iterator harrypotter = mesh.mBoneVertices.begin(); |
1188 | harrypotter != mesh.mBoneVertices.end();++harrypotter,++quak) { |
1189 | |
1190 | for (std::vector<std::pair<int,float> >::const_iterator |
1191 | ronaldweasley = (*harrypotter).mBoneWeights.begin(); |
1192 | ronaldweasley != (*harrypotter).mBoneWeights.end();++ronaldweasley) |
1193 | { |
1194 | aiVertexWeight weight; |
1195 | weight.mVertexId = quak; |
1196 | weight.mWeight = (*ronaldweasley).second; |
1197 | avBonesOut[(*ronaldweasley).first].push_back(weight); |
1198 | } |
1199 | } |
1200 | |
1201 | // now build a final bone list |
1202 | p_pcOut->mNumBones = 0; |
1203 | for (unsigned int jfkennedy = 0; jfkennedy < mesh.mBones.size();++jfkennedy) |
1204 | if (!avBonesOut[jfkennedy].empty())p_pcOut->mNumBones++; |
1205 | |
1206 | p_pcOut->mBones = new aiBone*[p_pcOut->mNumBones]; |
1207 | aiBone** pcBone = p_pcOut->mBones; |
1208 | for (unsigned int jfkennedy = 0; jfkennedy < mesh.mBones.size();++jfkennedy) { |
1209 | if (!avBonesOut[jfkennedy].empty()) { |
1210 | aiBone* pc = *pcBone = new aiBone(); |
1211 | pc->mName.Set(mesh.mBones[jfkennedy].mName); |
1212 | pc->mNumWeights = (unsigned int)avBonesOut[jfkennedy].size(); |
1213 | pc->mWeights = new aiVertexWeight[pc->mNumWeights]; |
1214 | ::memcpy(pc->mWeights,&avBonesOut[jfkennedy][0], |
1215 | sizeof(aiVertexWeight) * pc->mNumWeights); |
1216 | ++pcBone; |
1217 | } |
1218 | } |
1219 | } |
1220 | } |
1221 | } |
1222 | |
1223 | // ------------------------------------------------------------------------------------------------ |
1224 | // Setup proper material indices and build output materials |
1225 | void ASEImporter::BuildMaterialIndices() |
1226 | { |
1227 | ai_assert(NULL != pcScene); |
1228 | |
1229 | // iterate through all materials and check whether we need them |
1230 | for (unsigned int iMat = 0; iMat < mParser->m_vMaterials.size();++iMat) |
1231 | { |
1232 | ASE::Material& mat = mParser->m_vMaterials[iMat]; |
1233 | if (mat.bNeed) { |
1234 | // Convert it to the aiMaterial layout |
1235 | ConvertMaterial(mat); |
1236 | ++pcScene->mNumMaterials; |
1237 | } |
1238 | for (unsigned int iSubMat = 0; iSubMat < mat.avSubMaterials.size();++iSubMat) |
1239 | { |
1240 | ASE::Material& submat = mat.avSubMaterials[iSubMat]; |
1241 | if (submat.bNeed) { |
1242 | // Convert it to the aiMaterial layout |
1243 | ConvertMaterial(submat); |
1244 | ++pcScene->mNumMaterials; |
1245 | } |
1246 | } |
1247 | } |
1248 | |
1249 | // allocate the output material array |
1250 | pcScene->mMaterials = new aiMaterial*[pcScene->mNumMaterials]; |
1251 | D3DS::Material** pcIntMaterials = new D3DS::Material*[pcScene->mNumMaterials]; |
1252 | |
1253 | unsigned int iNum = 0; |
1254 | for (unsigned int iMat = 0; iMat < mParser->m_vMaterials.size();++iMat) { |
1255 | ASE::Material& mat = mParser->m_vMaterials[iMat]; |
1256 | if (mat.bNeed) |
1257 | { |
1258 | ai_assert(NULL != mat.pcInstance); |
1259 | pcScene->mMaterials[iNum] = mat.pcInstance; |
1260 | |
1261 | // Store the internal material, too |
1262 | pcIntMaterials[iNum] = &mat; |
1263 | |
1264 | // Iterate through all meshes and search for one which is using |
1265 | // this top-level material index |
1266 | for (unsigned int iMesh = 0; iMesh < pcScene->mNumMeshes;++iMesh) |
1267 | { |
1268 | aiMesh* mesh = pcScene->mMeshes[iMesh]; |
1269 | if (ASE::Face::DEFAULT_MATINDEX == mesh->mMaterialIndex && |
1270 | iMat == (uintptr_t)mesh->mColors[3]) |
1271 | { |
1272 | mesh->mMaterialIndex = iNum; |
1273 | mesh->mColors[3] = NULL; |
1274 | } |
1275 | } |
1276 | iNum++; |
1277 | } |
1278 | for (unsigned int iSubMat = 0; iSubMat < mat.avSubMaterials.size();++iSubMat) { |
1279 | ASE::Material& submat = mat.avSubMaterials[iSubMat]; |
1280 | if (submat.bNeed) { |
1281 | ai_assert(NULL != submat.pcInstance); |
1282 | pcScene->mMaterials[iNum] = submat.pcInstance; |
1283 | |
1284 | // Store the internal material, too |
1285 | pcIntMaterials[iNum] = &submat; |
1286 | |
1287 | // Iterate through all meshes and search for one which is using |
1288 | // this sub-level material index |
1289 | for (unsigned int iMesh = 0; iMesh < pcScene->mNumMeshes;++iMesh) { |
1290 | aiMesh* mesh = pcScene->mMeshes[iMesh]; |
1291 | |
1292 | if (iSubMat == mesh->mMaterialIndex && iMat == (uintptr_t)mesh->mColors[3]) { |
1293 | mesh->mMaterialIndex = iNum; |
1294 | mesh->mColors[3] = NULL; |
1295 | } |
1296 | } |
1297 | iNum++; |
1298 | } |
1299 | } |
1300 | } |
1301 | |
1302 | // Dekete our temporary array |
1303 | delete[] pcIntMaterials; |
1304 | } |
1305 | |
1306 | // ------------------------------------------------------------------------------------------------ |
1307 | // Generate normal vectors basing on smoothing groups |
1308 | bool ASEImporter::GenerateNormals(ASE::Mesh& mesh) { |
1309 | |
1310 | if (!mesh.mNormals.empty() && !configRecomputeNormals) |
1311 | { |
1312 | // Check whether there are only uninitialized normals. If there are |
1313 | // some, skip all normals from the file and compute them on our own |
1314 | for (std::vector<aiVector3D>::const_iterator qq = mesh.mNormals.begin();qq != mesh.mNormals.end();++qq) { |
1315 | if ((*qq).x || (*qq).y || (*qq).z) |
1316 | { |
1317 | return true; |
1318 | } |
1319 | } |
1320 | } |
1321 | // The array is reused. |
1322 | ComputeNormalsWithSmoothingsGroups<ASE::Face>(mesh); |
1323 | return false; |
1324 | } |
1325 | |
1326 | #endif // ASSIMP_BUILD_NO_3DS_IMPORTER |
1327 | |
1328 | #endif // !! ASSIMP_BUILD_NO_BASE_IMPORTER |
1329 | |