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