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