1/*
2---------------------------------------------------------------------------
3Open Asset Import Library (assimp)
4---------------------------------------------------------------------------
5
6Copyright (c) 2006-2017, assimp team
7
8
9All rights reserved.
10
11Redistribution and use of this software in source and binary forms,
12with or without modification, are permitted provided that the following
13conditions 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
29THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39OF 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
67using namespace Assimp;
68using namespace Assimp::ASE;
69
70static 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
85ASEImporter::ASEImporter()
86 : mParser(),
87 mBuffer(),
88 pcScene(),
89 configRecomputeNormals(),
90 noSkeletonMesh()
91{}
92
93// ------------------------------------------------------------------------------------------------
94// Destructor, private as well
95ASEImporter::~ASEImporter()
96{}
97
98// ------------------------------------------------------------------------------------------------
99// Returns whether the class can handle the format of the given file.
100bool 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
117const aiImporterDesc* ASEImporter::GetInfo () const
118{
119 return &desc;
120}
121
122// ------------------------------------------------------------------------------------------------
123// Setup configuration options
124void 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.
134void 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// ------------------------------------------------------------------------------------------------
265void 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// ------------------------------------------------------------------------------------------------
291void 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
427void 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
449void 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// ------------------------------------------------------------------------------------------------
488void 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
497void 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
550void 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
646void 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
742void 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
817void 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
834void 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
945void 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
1225void 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
1308bool 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