1
2 /*
3 ---------------------------------------------------------------------------
4 Open Asset Import Library (assimp)
5 ---------------------------------------------------------------------------
6
7 Copyright (c) 2006-2012, assimp team
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 Implementation of the AC3D importer class */
44
45 #include "AssimpPCH.h"
46
47 #ifndef ASSIMP_BUILD_NO_AC_IMPORTER
48
49 // internal headers
50 #include "ACLoader.h"
51 #include "ParsingUtils.h"
52 #include "fast_atof.h"
53 #include "Subdivision.h"
54
55 using namespace Assimp;
56
57
58 // ------------------------------------------------------------------------------------------------
59 // skip to the next token
60 #define AI_AC_SKIP_TO_NEXT_TOKEN() \
61 if (!SkipSpaces(&buffer)) \
62 { \
63 DefaultLogger::get()->error("AC3D: Unexpected EOF/EOL"); \
64 continue; \
65 }
66
67 // ------------------------------------------------------------------------------------------------
68 // read a string (may be enclosed in double quotation marks). buffer must point to "
69 #define AI_AC_GET_STRING(out) \
70 ++buffer; \
71 const char* sz = buffer; \
72 while ('\"' != *buffer) \
73 { \
74 if (IsLineEnd( *buffer )) \
75 { \
76 DefaultLogger::get()->error("AC3D: Unexpected EOF/EOL in string"); \
77 out = "ERROR"; \
78 break; \
79 } \
80 ++buffer; \
81 } \
82 if (IsLineEnd( *buffer ))continue; \
83 out = std::string(sz,(unsigned int)(buffer-sz)); \
84 ++buffer;
85
86
87 // ------------------------------------------------------------------------------------------------
88 // read 1 to n floats prefixed with an optional predefined identifier
89 #define AI_AC_CHECKED_LOAD_FLOAT_ARRAY(name,name_length,num,out) \
90 AI_AC_SKIP_TO_NEXT_TOKEN(); \
91 if (name_length) \
92 { \
93 if (strncmp(buffer,name,name_length) || !IsSpace(buffer[name_length])) \
94 { \
95 DefaultLogger::get()->error("AC3D: Unexpexted token. " name " was expected."); \
96 continue; \
97 } \
98 buffer += name_length+1; \
99 } \
100 for (unsigned int i = 0; i < num;++i) \
101 { \
102 AI_AC_SKIP_TO_NEXT_TOKEN(); \
103 buffer = fast_atoreal_move<float>(buffer,((float*)out)[i]); \
104 }
105
106
107 // ------------------------------------------------------------------------------------------------
108 // Constructor to be privately used by Importer
AC3DImporter()109 AC3DImporter::AC3DImporter()
110 {
111 // nothing to be done here
112 }
113
114 // ------------------------------------------------------------------------------------------------
115 // Destructor, private as well
~AC3DImporter()116 AC3DImporter::~AC3DImporter()
117 {
118 // nothing to be done here
119 }
120
121 // ------------------------------------------------------------------------------------------------
122 // Returns whether the class can handle the format of the given file.
CanRead(const std::string & pFile,IOSystem * pIOHandler,bool checkSig) const123 bool AC3DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
124 {
125 std::string extension = GetExtension(pFile);
126
127 // fixme: are acc and ac3d *really* used? Some sources say they are
128 if(extension == "ac" || extension == "ac3d" || extension == "acc") {
129 return true;
130 }
131 if (!extension.length() || checkSig) {
132 uint32_t token = AI_MAKE_MAGIC("AC3D");
133 return CheckMagicToken(pIOHandler,pFile,&token,1,0);
134 }
135 return false;
136 }
137
138 // ------------------------------------------------------------------------------------------------
139 // Get list of file extensions handled by this loader
GetExtensionList(std::set<std::string> & extensions)140 void AC3DImporter::GetExtensionList(std::set<std::string>& extensions)
141 {
142 extensions.insert("ac");
143 extensions.insert("acc");
144 extensions.insert("ac3d");
145 }
146
147 // ------------------------------------------------------------------------------------------------
148 // Get a pointer to the next line from the file
GetNextLine()149 bool AC3DImporter::GetNextLine( )
150 {
151 SkipLine(&buffer);
152 return SkipSpaces(&buffer);
153 }
154
155 // ------------------------------------------------------------------------------------------------
156 // Parse an object section in an AC file
LoadObjectSection(std::vector<Object> & objects)157 void AC3DImporter::LoadObjectSection(std::vector<Object>& objects)
158 {
159 if (!TokenMatch(buffer,"OBJECT",6))
160 return;
161
162 SkipSpaces(&buffer);
163
164 ++mNumMeshes;
165
166 objects.push_back(Object());
167 Object& obj = objects.back();
168
169 aiLight* light = NULL;
170 if (!ASSIMP_strincmp(buffer,"light",5))
171 {
172 // This is a light source. Add it to the list
173 mLights->push_back(light = new aiLight());
174
175 // Return a point light with no attenuation
176 light->mType = aiLightSource_POINT;
177 light->mColorDiffuse = light->mColorSpecular = aiColor3D(1.f,1.f,1.f);
178 light->mAttenuationConstant = 1.f;
179
180 // Generate a default name for both the light source and the node
181 // FIXME - what's the right way to print a size_t? Is 'zu' universally available? stick with the safe version.
182 light->mName.length = ::sprintf(light->mName.data,"ACLight_%i",static_cast<unsigned int>(mLights->size())-1);
183 obj.name = std::string( light->mName.data );
184
185 DefaultLogger::get()->debug("AC3D: Light source encountered");
186 obj.type = Object::Light;
187 }
188 else if (!ASSIMP_strincmp(buffer,"group",5))
189 {
190 obj.type = Object::Group;
191 }
192 else if (!ASSIMP_strincmp(buffer,"world",5))
193 {
194 obj.type = Object::World;
195 }
196 else obj.type = Object::Poly;
197 while (GetNextLine())
198 {
199 if (TokenMatch(buffer,"kids",4))
200 {
201 SkipSpaces(&buffer);
202 unsigned int num = strtoul10(buffer,&buffer);
203 GetNextLine();
204 if (num)
205 {
206 // load the children of this object recursively
207 obj.children.reserve(num);
208 for (unsigned int i = 0; i < num; ++i)
209 LoadObjectSection(obj.children);
210 }
211 return;
212 }
213 else if (TokenMatch(buffer,"name",4))
214 {
215 SkipSpaces(&buffer);
216 AI_AC_GET_STRING(obj.name);
217
218 // If this is a light source, we'll also need to store
219 // the name of the node in it.
220 if (light)
221 {
222 light->mName.Set(obj.name);
223 }
224 }
225 else if (TokenMatch(buffer,"texture",7))
226 {
227 SkipSpaces(&buffer);
228 AI_AC_GET_STRING(obj.texture);
229 }
230 else if (TokenMatch(buffer,"texrep",6))
231 {
232 SkipSpaces(&buffer);
233 AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,2,&obj.texRepeat);
234 if (!obj.texRepeat.x || !obj.texRepeat.y)
235 obj.texRepeat = aiVector2D (1.f,1.f);
236 }
237 else if (TokenMatch(buffer,"texoff",6))
238 {
239 SkipSpaces(&buffer);
240 AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,2,&obj.texOffset);
241 }
242 else if (TokenMatch(buffer,"rot",3))
243 {
244 SkipSpaces(&buffer);
245 AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,9,&obj.rotation);
246 }
247 else if (TokenMatch(buffer,"loc",3))
248 {
249 SkipSpaces(&buffer);
250 AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,3,&obj.translation);
251 }
252 else if (TokenMatch(buffer,"subdiv",6))
253 {
254 SkipSpaces(&buffer);
255 obj.subDiv = strtoul10(buffer,&buffer);
256 }
257 else if (TokenMatch(buffer,"crease",6))
258 {
259 SkipSpaces(&buffer);
260 obj.crease = fast_atof(buffer);
261 }
262 else if (TokenMatch(buffer,"numvert",7))
263 {
264 SkipSpaces(&buffer);
265
266 unsigned int t = strtoul10(buffer,&buffer);
267 obj.vertices.reserve(t);
268 for (unsigned int i = 0; i < t;++i)
269 {
270 if (!GetNextLine())
271 {
272 DefaultLogger::get()->error("AC3D: Unexpected EOF: not all vertices have been parsed yet");
273 break;
274 }
275 else if (!IsNumeric(*buffer))
276 {
277 DefaultLogger::get()->error("AC3D: Unexpected token: not all vertices have been parsed yet");
278 --buffer; // make sure the line is processed a second time
279 break;
280 }
281 obj.vertices.push_back(aiVector3D());
282 aiVector3D& v = obj.vertices.back();
283 AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,3,&v.x);
284 }
285 }
286 else if (TokenMatch(buffer,"numsurf",7))
287 {
288 SkipSpaces(&buffer);
289
290 bool Q3DWorkAround = false;
291
292 const unsigned int t = strtoul10(buffer,&buffer);
293 obj.surfaces.reserve(t);
294 for (unsigned int i = 0; i < t;++i)
295 {
296 GetNextLine();
297 if (!TokenMatch(buffer,"SURF",4))
298 {
299 // FIX: this can occur for some files - Quick 3D for
300 // example writes no surf chunks
301 if (!Q3DWorkAround)
302 {
303 DefaultLogger::get()->warn("AC3D: SURF token was expected");
304 DefaultLogger::get()->debug("Continuing with Quick3D Workaround enabled");
305 }
306 --buffer; // make sure the line is processed a second time
307 // break; --- see fix notes above
308
309 Q3DWorkAround = true;
310 }
311 SkipSpaces(&buffer);
312 obj.surfaces.push_back(Surface());
313 Surface& surf = obj.surfaces.back();
314 surf.flags = strtoul_cppstyle(buffer);
315
316 while (1)
317 {
318 if(!GetNextLine())
319 {
320 DefaultLogger::get()->error("AC3D: Unexpected EOF: surface is incomplete");
321 break;
322 }
323 if (TokenMatch(buffer,"mat",3))
324 {
325 SkipSpaces(&buffer);
326 surf.mat = strtoul10(buffer);
327 }
328 else if (TokenMatch(buffer,"refs",4))
329 {
330 // --- see fix notes above
331 if (Q3DWorkAround)
332 {
333 if (!surf.entries.empty())
334 {
335 buffer -= 6;
336 break;
337 }
338 }
339
340 SkipSpaces(&buffer);
341 const unsigned int m = strtoul10(buffer);
342 surf.entries.reserve(m);
343
344 obj.numRefs += m;
345
346 for (unsigned int k = 0; k < m; ++k)
347 {
348 if(!GetNextLine())
349 {
350 DefaultLogger::get()->error("AC3D: Unexpected EOF: surface references are incomplete");
351 break;
352 }
353 surf.entries.push_back(Surface::SurfaceEntry());
354 Surface::SurfaceEntry& entry = surf.entries.back();
355
356 entry.first = strtoul10(buffer,&buffer);
357 SkipSpaces(&buffer);
358 AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,2,&entry.second);
359 }
360 }
361 else
362 {
363
364 --buffer; // make sure the line is processed a second time
365 break;
366 }
367 }
368 }
369 }
370 }
371 DefaultLogger::get()->error("AC3D: Unexpected EOF: \'kids\' line was expected");
372 }
373
374 // ------------------------------------------------------------------------------------------------
375 // Convert a material from AC3DImporter::Material to aiMaterial
ConvertMaterial(const Object & object,const Material & matSrc,aiMaterial & matDest)376 void AC3DImporter::ConvertMaterial(const Object& object,
377 const Material& matSrc,
378 aiMaterial& matDest)
379 {
380 aiString s;
381
382 if (matSrc.name.length())
383 {
384 s.Set(matSrc.name);
385 matDest.AddProperty(&s,AI_MATKEY_NAME);
386 }
387 if (object.texture.length())
388 {
389 s.Set(object.texture);
390 matDest.AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(0));
391
392 // UV transformation
393 if (1.f != object.texRepeat.x || 1.f != object.texRepeat.y ||
394 object.texOffset.x || object.texOffset.y)
395 {
396 aiUVTransform transform;
397 transform.mScaling = object.texRepeat;
398 transform.mTranslation = object.texOffset;
399 matDest.AddProperty(&transform,1,AI_MATKEY_UVTRANSFORM_DIFFUSE(0));
400 }
401 }
402
403 matDest.AddProperty<aiColor3D>(&matSrc.rgb,1, AI_MATKEY_COLOR_DIFFUSE);
404 matDest.AddProperty<aiColor3D>(&matSrc.amb,1, AI_MATKEY_COLOR_AMBIENT);
405 matDest.AddProperty<aiColor3D>(&matSrc.emis,1,AI_MATKEY_COLOR_EMISSIVE);
406 matDest.AddProperty<aiColor3D>(&matSrc.spec,1,AI_MATKEY_COLOR_SPECULAR);
407
408 int n;
409 if (matSrc.shin)
410 {
411 n = aiShadingMode_Phong;
412 matDest.AddProperty<float>(&matSrc.shin,1,AI_MATKEY_SHININESS);
413 }
414 else n = aiShadingMode_Gouraud;
415 matDest.AddProperty<int>(&n,1,AI_MATKEY_SHADING_MODEL);
416
417 float f = 1.f - matSrc.trans;
418 matDest.AddProperty<float>(&f,1,AI_MATKEY_OPACITY);
419 }
420
421 // ------------------------------------------------------------------------------------------------
422 // Converts the loaded data to the internal verbose representation
ConvertObjectSection(Object & object,std::vector<aiMesh * > & meshes,std::vector<aiMaterial * > & outMaterials,const std::vector<Material> & materials,aiNode * parent)423 aiNode* AC3DImporter::ConvertObjectSection(Object& object,
424 std::vector<aiMesh*>& meshes,
425 std::vector<aiMaterial*>& outMaterials,
426 const std::vector<Material>& materials,
427 aiNode* parent)
428 {
429 aiNode* node = new aiNode();
430 node->mParent = parent;
431 if (object.vertices.size())
432 {
433 if (!object.surfaces.size() || !object.numRefs)
434 {
435 /* " An object with 7 vertices (no surfaces, no materials defined).
436 This is a good way of getting point data into AC3D.
437 The Vertex->create convex-surface/object can be used on these
438 vertices to 'wrap' a 3d shape around them "
439 (http://www.opencity.info/html/ac3dfileformat.html)
440
441 therefore: if no surfaces are defined return point data only
442 */
443
444 DefaultLogger::get()->info("AC3D: No surfaces defined in object definition, "
445 "a point list is returned");
446
447 meshes.push_back(new aiMesh());
448 aiMesh* mesh = meshes.back();
449
450 mesh->mNumFaces = mesh->mNumVertices = (unsigned int)object.vertices.size();
451 aiFace* faces = mesh->mFaces = new aiFace[mesh->mNumFaces];
452 aiVector3D* verts = mesh->mVertices = new aiVector3D[mesh->mNumVertices];
453
454 for (unsigned int i = 0; i < mesh->mNumVertices;++i,++faces,++verts)
455 {
456 *verts = object.vertices[i];
457 faces->mNumIndices = 1;
458 faces->mIndices = new unsigned int[1];
459 faces->mIndices[0] = i;
460 }
461
462 // use the primary material in this case. this should be the
463 // default material if all objects of the file contain points
464 // and no faces.
465 mesh->mMaterialIndex = 0;
466 outMaterials.push_back(new aiMaterial());
467 ConvertMaterial(object, materials[0], *outMaterials.back());
468 }
469 else
470 {
471 // need to generate one or more meshes for this object.
472 // find out how many different materials we have
473 typedef std::pair< unsigned int, unsigned int > IntPair;
474 typedef std::vector< IntPair > MatTable;
475 MatTable needMat(materials.size(),IntPair(0,0));
476
477 std::vector<Surface>::iterator it,end = object.surfaces.end();
478 std::vector<Surface::SurfaceEntry>::iterator it2,end2;
479
480 for (it = object.surfaces.begin(); it != end; ++it)
481 {
482 register unsigned int idx = (*it).mat;
483 if (idx >= needMat.size())
484 {
485 DefaultLogger::get()->error("AC3D: material index is out of range");
486 idx = 0;
487 }
488 if ((*it).entries.empty())
489 {
490 DefaultLogger::get()->warn("AC3D: surface her zero vertex references");
491 }
492
493 // validate all vertex indices to make sure we won't crash here
494 for (it2 = (*it).entries.begin(),
495 end2 = (*it).entries.end(); it2 != end2; ++it2)
496 {
497 if ((*it2).first >= object.vertices.size())
498 {
499 DefaultLogger::get()->warn("AC3D: Invalid vertex reference");
500 (*it2).first = 0;
501 }
502 }
503
504 if (!needMat[idx].first)++node->mNumMeshes;
505
506 switch ((*it).flags & 0xf)
507 {
508 // closed line
509 case 0x1:
510
511 needMat[idx].first += (unsigned int)(*it).entries.size();
512 needMat[idx].second += (unsigned int)(*it).entries.size()<<1u;
513 break;
514
515 // unclosed line
516 case 0x2:
517
518 needMat[idx].first += (unsigned int)(*it).entries.size()-1;
519 needMat[idx].second += ((unsigned int)(*it).entries.size()-1)<<1u;
520 break;
521
522 // 0 == polygon, else unknown
523 default:
524
525 if ((*it).flags & 0xf)
526 {
527 DefaultLogger::get()->warn("AC3D: The type flag of a surface is unknown");
528 (*it).flags &= ~(0xf);
529 }
530
531 // the number of faces increments by one, the number
532 // of vertices by surface.numref.
533 needMat[idx].first++;
534 needMat[idx].second += (unsigned int)(*it).entries.size();
535 };
536 }
537 unsigned int* pip = node->mMeshes = new unsigned int[node->mNumMeshes];
538 unsigned int mat = 0;
539 const size_t oldm = meshes.size();
540 for (MatTable::const_iterator cit = needMat.begin(), cend = needMat.end();
541 cit != cend; ++cit, ++mat)
542 {
543 if (!(*cit).first)continue;
544
545 // allocate a new aiMesh object
546 *pip++ = (unsigned int)meshes.size();
547 aiMesh* mesh = new aiMesh();
548 meshes.push_back(mesh);
549
550 mesh->mMaterialIndex = (unsigned int)outMaterials.size();
551 outMaterials.push_back(new aiMaterial());
552 ConvertMaterial(object, materials[mat], *outMaterials.back());
553
554 // allocate storage for vertices and normals
555 mesh->mNumFaces = (*cit).first;
556 aiFace* faces = mesh->mFaces = new aiFace[mesh->mNumFaces];
557
558 mesh->mNumVertices = (*cit).second;
559 aiVector3D* vertices = mesh->mVertices = new aiVector3D[mesh->mNumVertices];
560 unsigned int cur = 0;
561
562 // allocate UV coordinates, but only if the texture name for the
563 // surface is not empty
564 aiVector3D* uv = NULL;
565 if(object.texture.length())
566 {
567 uv = mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
568 mesh->mNumUVComponents[0] = 2;
569 }
570
571 for (it = object.surfaces.begin(); it != end; ++it)
572 {
573 if (mat == (*it).mat)
574 {
575 const Surface& src = *it;
576
577 // closed polygon
578 unsigned int type = (*it).flags & 0xf;
579 if (!type)
580 {
581 aiFace& face = *faces++;
582 if((face.mNumIndices = (unsigned int)src.entries.size()))
583 {
584 face.mIndices = new unsigned int[face.mNumIndices];
585 for (unsigned int i = 0; i < face.mNumIndices;++i,++vertices)
586 {
587 const Surface::SurfaceEntry& entry = src.entries[i];
588 face.mIndices[i] = cur++;
589
590 // copy vertex positions
591 *vertices = object.vertices[entry.first] + object.translation;
592
593
594 // copy texture coordinates
595 if (uv)
596 {
597 uv->x = entry.second.x;
598 uv->y = entry.second.y;
599 ++uv;
600 }
601 }
602 }
603 }
604 else
605 {
606
607 it2 = (*it).entries.begin();
608
609 // either a closed or an unclosed line
610 register unsigned int tmp = (unsigned int)(*it).entries.size();
611 if (0x2 == type)--tmp;
612 for (unsigned int m = 0; m < tmp;++m)
613 {
614 aiFace& face = *faces++;
615
616 face.mNumIndices = 2;
617 face.mIndices = new unsigned int[2];
618 face.mIndices[0] = cur++;
619 face.mIndices[1] = cur++;
620
621 // copy vertex positions
622 *vertices++ = object.vertices[(*it2).first];
623
624 // copy texture coordinates
625 if (uv)
626 {
627 uv->x = (*it2).second.x;
628 uv->y = (*it2).second.y;
629 ++uv;
630 }
631
632
633 if (0x1 == type && tmp-1 == m)
634 {
635 // if this is a closed line repeat its beginning now
636 it2 = (*it).entries.begin();
637 }
638 else ++it2;
639
640 // second point
641 *vertices++ = object.vertices[(*it2).first];
642
643 if (uv)
644 {
645 uv->x = (*it2).second.x;
646 uv->y = (*it2).second.y;
647 ++uv;
648 }
649 }
650 }
651 }
652 }
653 }
654
655 // Now apply catmull clark subdivision if necessary. We split meshes into
656 // materials which is not done by AC3D during smoothing, so we need to
657 // collect all meshes using the same material group.
658 if (object.subDiv) {
659 if (configEvalSubdivision) {
660 boost::scoped_ptr<Subdivider> div(Subdivider::Create(Subdivider::CATMULL_CLARKE));
661 DefaultLogger::get()->info("AC3D: Evaluating subdivision surface: "+object.name);
662
663 std::vector<aiMesh*> cpy(meshes.size()-oldm,NULL);
664 div->Subdivide(&meshes[oldm],cpy.size(),&cpy.front(),object.subDiv,true);
665 std::copy(cpy.begin(),cpy.end(),meshes.begin()+oldm);
666
667 // previous meshes are deleted vy Subdivide().
668 }
669 else {
670 DefaultLogger::get()->info("AC3D: Letting the subdivision surface untouched due to my configuration: "
671 +object.name);
672 }
673 }
674 }
675 }
676
677 if (object.name.length())
678 node->mName.Set(object.name);
679 else
680 {
681 // generate a name depending on the type of the node
682 switch (object.type)
683 {
684 case Object::Group:
685 node->mName.length = ::sprintf(node->mName.data,"ACGroup_%i",groups++);
686 break;
687 case Object::Poly:
688 node->mName.length = ::sprintf(node->mName.data,"ACPoly_%i",polys++);
689 break;
690 case Object::Light:
691 node->mName.length = ::sprintf(node->mName.data,"ACLight_%i",lights++);
692 break;
693
694 // there shouldn't be more than one world, but we don't care
695 case Object::World:
696 node->mName.length = ::sprintf(node->mName.data,"ACWorld_%i",worlds++);
697 break;
698 }
699 }
700
701
702 // setup the local transformation matrix of the object
703 // compute the transformation offset to the parent node
704 node->mTransformation = aiMatrix4x4 ( object.rotation );
705
706 if (object.type == Object::Group || !object.numRefs)
707 {
708 node->mTransformation.a4 = object.translation.x;
709 node->mTransformation.b4 = object.translation.y;
710 node->mTransformation.c4 = object.translation.z;
711 }
712
713 // add children to the object
714 if (object.children.size())
715 {
716 node->mNumChildren = (unsigned int)object.children.size();
717 node->mChildren = new aiNode*[node->mNumChildren];
718 for (unsigned int i = 0; i < node->mNumChildren;++i)
719 {
720 node->mChildren[i] = ConvertObjectSection(object.children[i],meshes,outMaterials,materials,node);
721 }
722 }
723
724 return node;
725 }
726
727 // ------------------------------------------------------------------------------------------------
SetupProperties(const Importer * pImp)728 void AC3DImporter::SetupProperties(const Importer* pImp)
729 {
730 configSplitBFCull = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_AC_SEPARATE_BFCULL,1) ? true : false;
731 configEvalSubdivision = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_AC_EVAL_SUBDIVISION,1) ? true : false;
732 }
733
734 // ------------------------------------------------------------------------------------------------
735 // Imports the given file into the given scene structure.
InternReadFile(const std::string & pFile,aiScene * pScene,IOSystem * pIOHandler)736 void AC3DImporter::InternReadFile( const std::string& pFile,
737 aiScene* pScene, IOSystem* pIOHandler)
738 {
739 boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
740
741 // Check whether we can read from the file
742 if( file.get() == NULL)
743 throw DeadlyImportError( "Failed to open AC3D file " + pFile + ".");
744
745 // allocate storage and copy the contents of the file to a memory buffer
746 std::vector<char> mBuffer2;
747 TextFileToBuffer(file.get(),mBuffer2);
748
749 buffer = &mBuffer2[0];
750 mNumMeshes = 0;
751
752 lights = polys = worlds = groups = 0;
753
754 if (::strncmp(buffer,"AC3D",4)) {
755 throw DeadlyImportError("AC3D: No valid AC3D file, magic sequence not found");
756 }
757
758 // print the file format version to the console
759 unsigned int version = HexDigitToDecimal( buffer[4] );
760 char msg[3];
761 ASSIMP_itoa10(msg,3,version);
762 DefaultLogger::get()->info(std::string("AC3D file format version: ") + msg);
763
764 std::vector<Material> materials;
765 materials.reserve(5);
766
767 std::vector<Object> rootObjects;
768 rootObjects.reserve(5);
769
770 std::vector<aiLight*> lights;
771 mLights = & lights;
772
773 while (GetNextLine())
774 {
775 if (TokenMatch(buffer,"MATERIAL",8))
776 {
777 materials.push_back(Material());
778 Material& mat = materials.back();
779
780 // manually parse the material ... sscanf would use the buldin atof ...
781 // Format: (name) rgb %f %f %f amb %f %f %f emis %f %f %f spec %f %f %f shi %d trans %f
782
783 AI_AC_SKIP_TO_NEXT_TOKEN();
784 if ('\"' == *buffer)
785 {
786 AI_AC_GET_STRING(mat.name);
787 AI_AC_SKIP_TO_NEXT_TOKEN();
788 }
789
790 AI_AC_CHECKED_LOAD_FLOAT_ARRAY("rgb",3,3,&mat.rgb);
791 AI_AC_CHECKED_LOAD_FLOAT_ARRAY("amb",3,3,&mat.amb);
792 AI_AC_CHECKED_LOAD_FLOAT_ARRAY("emis",4,3,&mat.emis);
793 AI_AC_CHECKED_LOAD_FLOAT_ARRAY("spec",4,3,&mat.spec);
794 AI_AC_CHECKED_LOAD_FLOAT_ARRAY("shi",3,1,&mat.shin);
795 AI_AC_CHECKED_LOAD_FLOAT_ARRAY("trans",5,1,&mat.trans);
796 }
797 LoadObjectSection(rootObjects);
798 }
799
800 if (rootObjects.empty() || !mNumMeshes)
801 {
802 throw DeadlyImportError("AC3D: No meshes have been loaded");
803 }
804 if (materials.empty())
805 {
806 DefaultLogger::get()->warn("AC3D: No material has been found");
807 materials.push_back(Material());
808 }
809
810 mNumMeshes += (mNumMeshes>>2u) + 1;
811 std::vector<aiMesh*> meshes;
812 meshes.reserve(mNumMeshes);
813
814 std::vector<aiMaterial*> omaterials;
815 materials.reserve(mNumMeshes);
816
817 // generate a dummy root if there are multiple objects on the top layer
818 Object* root;
819 if (1 == rootObjects.size())
820 root = &rootObjects[0];
821 else
822 {
823 root = new Object();
824 }
825
826 // now convert the imported stuff to our output data structure
827 pScene->mRootNode = ConvertObjectSection(*root,meshes,omaterials,materials);
828 if (1 != rootObjects.size())delete root;
829
830 if (!::strncmp( pScene->mRootNode->mName.data, "Node", 4))
831 pScene->mRootNode->mName.Set("<AC3DWorld>");
832
833 // copy meshes
834 if (meshes.empty())
835 {
836 throw DeadlyImportError("An unknown error occured during converting");
837 }
838 pScene->mNumMeshes = (unsigned int)meshes.size();
839 pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
840 ::memcpy(pScene->mMeshes,&meshes[0],pScene->mNumMeshes*sizeof(void*));
841
842 // copy materials
843 pScene->mNumMaterials = (unsigned int)omaterials.size();
844 pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
845 ::memcpy(pScene->mMaterials,&omaterials[0],pScene->mNumMaterials*sizeof(void*));
846
847 // copy lights
848 pScene->mNumLights = (unsigned int)lights.size();
849 if (lights.size())
850 {
851 pScene->mLights = new aiLight*[lights.size()];
852 ::memcpy(pScene->mLights,&lights[0],lights.size()*sizeof(void*));
853 }
854 }
855
856 #endif //!defined ASSIMP_BUILD_NO_AC_IMPORTER
857