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 Implementation of the XGL/ZGL importer class */
44
45
46#ifndef ASSIMP_BUILD_NO_XGL_IMPORTER
47
48#include "XGLLoader.h"
49#include "ParsingUtils.h"
50#include "fast_atof.h"
51
52#include "StreamReader.h"
53#include "MemoryIOWrapper.h"
54#include <assimp/mesh.h>
55#include <assimp/scene.h>
56#include <assimp/importerdesc.h>
57#include <cctype>
58#include <memory>
59
60using namespace Assimp;
61using namespace irr;
62using namespace irr::io;
63
64// zlib is needed for compressed XGL files
65#ifndef ASSIMP_BUILD_NO_COMPRESSED_XGL
66# ifdef ASSIMP_BUILD_NO_OWN_ZLIB
67# include <zlib.h>
68# else
69# include <contrib/zlib/zlib.h>
70# endif
71#endif
72
73
74// scopeguard for a malloc'ed buffer
75struct free_it
76{
77 free_it(void* free) : free(free) {}
78 ~free_it() {
79 ::free(this->free);
80 }
81
82 void* free;
83};
84
85namespace Assimp { // this has to be in here because LogFunctions is in ::Assimp
86 template<> const char* LogFunctions<XGLImporter>::Prefix()
87 {
88 static auto prefix = "XGL: ";
89 return prefix;
90 }
91}
92
93static const aiImporterDesc desc = {
94 "XGL Importer",
95 "",
96 "",
97 "",
98 aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportCompressedFlavour,
99 0,
100 0,
101 0,
102 0,
103 "xgl zgl"
104};
105
106
107// ------------------------------------------------------------------------------------------------
108// Constructor to be privately used by Importer
109XGLImporter::XGLImporter()
110: m_reader( nullptr )
111, m_scene( nullptr ) {
112 // empty
113}
114
115// ------------------------------------------------------------------------------------------------
116// Destructor, private as well
117XGLImporter::~XGLImporter() {
118 // empty
119}
120
121// ------------------------------------------------------------------------------------------------
122// Returns whether the class can handle the format of the given file.
123bool XGLImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
124{
125 /* NOTE: A simple check for the file extension is not enough
126 * here. XGL and ZGL are ok, but xml is too generic
127 * and might be collada as well. So open the file and
128 * look for typical signal tokens.
129 */
130 const std::string extension = GetExtension(pFile);
131
132 if (extension == "xgl" || extension == "zgl") {
133 return true;
134 }
135 else if (extension == "xml" || checkSig) {
136 ai_assert(pIOHandler != NULL);
137
138 const char* tokens[] = {"<world>","<World>","<WORLD>"};
139 return SearchFileHeaderForToken(pIOHandler,pFile,tokens,3);
140 }
141 return false;
142}
143
144// ------------------------------------------------------------------------------------------------
145// Get a list of all file extensions which are handled by this class
146const aiImporterDesc* XGLImporter::GetInfo () const
147{
148 return &desc;
149}
150
151// ------------------------------------------------------------------------------------------------
152// Imports the given file into the given scene structure.
153void XGLImporter::InternReadFile( const std::string& pFile,
154 aiScene* pScene, IOSystem* pIOHandler)
155{
156#ifndef ASSIMP_BUILD_NO_COMPRESSED_XGL
157 Bytef* dest = NULL;
158 free_it free_it_really(dest);
159#endif
160
161 m_scene = pScene;
162 std::shared_ptr<IOStream> stream( pIOHandler->Open( pFile, "rb"));
163
164 // check whether we can read from the file
165 if( stream.get() == NULL) {
166 throw DeadlyImportError( "Failed to open XGL/ZGL file " + pFile + "");
167 }
168
169 // see if its compressed, if so uncompress it
170 if (GetExtension(pFile) == "zgl") {
171#ifdef ASSIMP_BUILD_NO_COMPRESSED_XGL
172 ThrowException("Cannot read ZGL file since Assimp was built without compression support");
173#else
174 std::unique_ptr<StreamReaderLE> raw_reader(new StreamReaderLE(stream));
175
176 // build a zlib stream
177 z_stream zstream;
178 zstream.opaque = Z_NULL;
179 zstream.zalloc = Z_NULL;
180 zstream.zfree = Z_NULL;
181 zstream.data_type = Z_BINARY;
182
183 // raw decompression without a zlib or gzip header
184 inflateInit2(&zstream, -MAX_WBITS);
185
186 // skip two extra bytes, zgl files do carry a crc16 upfront (I think)
187 raw_reader->IncPtr(2);
188
189 zstream.next_in = reinterpret_cast<Bytef*>( raw_reader->GetPtr() );
190 zstream.avail_in = raw_reader->GetRemainingSize();
191
192 size_t total = 0l;
193
194 // and decompress the data .... do 1k chunks in the hope that we won't kill the stack
195 #define MYBLOCK 1024
196 Bytef block[MYBLOCK];
197 int ret;
198 do {
199 zstream.avail_out = MYBLOCK;
200 zstream.next_out = block;
201 ret = inflate(&zstream, Z_NO_FLUSH);
202
203 if (ret != Z_STREAM_END && ret != Z_OK) {
204 ThrowException("Failure decompressing this file using gzip, seemingly it is NOT a compressed .XGL file");
205 }
206 const size_t have = MYBLOCK - zstream.avail_out;
207 total += have;
208 dest = reinterpret_cast<Bytef*>( realloc(dest,total) );
209 memcpy(dest + total - have,block,have);
210 }
211 while (ret != Z_STREAM_END);
212
213 // terminate zlib
214 inflateEnd(&zstream);
215
216 // replace the input stream with a memory stream
217 stream.reset(new MemoryIOStream(reinterpret_cast<uint8_t*>(dest),total));
218#endif
219 }
220
221 // construct the irrXML parser
222 CIrrXML_IOStreamReader st(stream.get());
223 m_reader.reset( createIrrXMLReader( ( IFileReadCallBack* ) &st ) );
224
225 // parse the XML file
226 TempScope scope;
227
228 while (ReadElement()) {
229 if (!ASSIMP_stricmp(m_reader->getNodeName(),"world")) {
230 ReadWorld(scope);
231 }
232 }
233
234
235 std::vector<aiMesh*>& meshes = scope.meshes_linear;
236 std::vector<aiMaterial*>& materials = scope.materials_linear;
237 if(!meshes.size() || !materials.size()) {
238 ThrowException("failed to extract data from XGL file, no meshes loaded");
239 }
240
241 // copy meshes
242 m_scene->mNumMeshes = static_cast<unsigned int>(meshes.size());
243 m_scene->mMeshes = new aiMesh*[m_scene->mNumMeshes]();
244 std::copy(meshes.begin(),meshes.end(),m_scene->mMeshes);
245
246 // copy materials
247 m_scene->mNumMaterials = static_cast<unsigned int>(materials.size());
248 m_scene->mMaterials = new aiMaterial*[m_scene->mNumMaterials]();
249 std::copy(materials.begin(),materials.end(),m_scene->mMaterials);
250
251 if (scope.light) {
252 m_scene->mNumLights = 1;
253 m_scene->mLights = new aiLight*[1];
254 m_scene->mLights[0] = scope.light;
255
256 scope.light->mName = m_scene->mRootNode->mName;
257 }
258
259 scope.dismiss();
260}
261
262// ------------------------------------------------------------------------------------------------
263bool XGLImporter::ReadElement()
264{
265 while(m_reader->read()) {
266 if (m_reader->getNodeType() == EXN_ELEMENT) {
267 return true;
268 }
269 }
270 return false;
271}
272
273// ------------------------------------------------------------------------------------------------
274bool XGLImporter::ReadElementUpToClosing(const char* closetag)
275{
276 while(m_reader->read()) {
277 if (m_reader->getNodeType() == EXN_ELEMENT) {
278 return true;
279 }
280 else if (m_reader->getNodeType() == EXN_ELEMENT_END && !ASSIMP_stricmp(m_reader->getNodeName(),closetag)) {
281 return false;
282 }
283 }
284 LogError("unexpected EOF, expected closing <" + std::string(closetag) + "> tag");
285 return false;
286}
287
288// ------------------------------------------------------------------------------------------------
289bool XGLImporter::SkipToText()
290{
291 while(m_reader->read()) {
292 if (m_reader->getNodeType() == EXN_TEXT) {
293 return true;
294 }
295 else if (m_reader->getNodeType() == EXN_ELEMENT || m_reader->getNodeType() == EXN_ELEMENT_END) {
296 ThrowException("expected text contents but found another element (or element end)");
297 }
298 }
299 return false;
300}
301
302// ------------------------------------------------------------------------------------------------
303std::string XGLImporter::GetElementName()
304{
305 const char* s = m_reader->getNodeName();
306 size_t len = strlen(s);
307
308 std::string ret;
309 ret.resize(len);
310
311 std::transform(s,s+len,ret.begin(),::tolower);
312 return ret;
313}
314
315// ------------------------------------------------------------------------------------------------
316void XGLImporter::ReadWorld(TempScope& scope)
317{
318 while (ReadElementUpToClosing("world")) {
319 const std::string& s = GetElementName();
320 // XXX right now we'd skip <lighting> if it comes after
321 // <object> or <mesh>
322 if (s == "lighting") {
323 ReadLighting(scope);
324 }
325 else if (s == "object" || s == "mesh" || s == "mat") {
326 break;
327 }
328 }
329
330
331 aiNode* const nd = ReadObject(scope,true,"world");
332 if(!nd) {
333 ThrowException("failure reading <world>");
334 }
335 if(!nd->mName.length) {
336 nd->mName.Set("WORLD");
337 }
338
339 m_scene->mRootNode = nd;
340}
341
342// ------------------------------------------------------------------------------------------------
343void XGLImporter::ReadLighting(TempScope& scope)
344{
345 while (ReadElementUpToClosing("lighting")) {
346 const std::string& s = GetElementName();
347 if (s == "directionallight") {
348 scope.light = ReadDirectionalLight();
349 }
350 else if (s == "ambient") {
351 LogWarn("ignoring <ambient> tag");
352 }
353 else if (s == "spheremap") {
354 LogWarn("ignoring <spheremap> tag");
355 }
356 }
357}
358
359// ------------------------------------------------------------------------------------------------
360aiLight* XGLImporter::ReadDirectionalLight()
361{
362 std::unique_ptr<aiLight> l(new aiLight());
363 l->mType = aiLightSource_DIRECTIONAL;
364
365 while (ReadElementUpToClosing("directionallight")) {
366 const std::string& s = GetElementName();
367 if (s == "direction") {
368 l->mDirection = ReadVec3();
369 }
370 else if (s == "diffuse") {
371 l->mColorDiffuse = ReadCol3();
372 }
373 else if (s == "specular") {
374 l->mColorSpecular = ReadCol3();
375 }
376 }
377 return l.release();
378}
379
380// ------------------------------------------------------------------------------------------------
381aiNode* XGLImporter::ReadObject(TempScope& scope, bool skipFirst, const char* closetag)
382{
383 std::unique_ptr<aiNode> nd(new aiNode());
384 std::vector<aiNode*> children;
385 std::vector<unsigned int> meshes;
386
387 try {
388 while (skipFirst || ReadElementUpToClosing(closetag)) {
389 skipFirst = false;
390
391 const std::string& s = GetElementName();
392 if (s == "mesh") {
393 const size_t prev = scope.meshes_linear.size();
394 if(ReadMesh(scope)) {
395 const size_t newc = scope.meshes_linear.size();
396 for(size_t i = 0; i < newc-prev; ++i) {
397 meshes.push_back(static_cast<unsigned int>(i+prev));
398 }
399 }
400 }
401 else if (s == "mat") {
402 ReadMaterial(scope);
403 }
404 else if (s == "object") {
405 children.push_back(ReadObject(scope));
406 }
407 else if (s == "objectref") {
408 // XXX
409 }
410 else if (s == "meshref") {
411 const unsigned int id = static_cast<unsigned int>( ReadIndexFromText() );
412
413 std::multimap<unsigned int, aiMesh*>::iterator it = scope.meshes.find(id), end = scope.meshes.end();
414 if (it == end) {
415 ThrowException("<meshref> index out of range");
416 }
417
418 for(; it != end && (*it).first == id; ++it) {
419 // ok, this is n^2 and should get optimized one day
420 aiMesh* const m = (*it).second;
421
422 unsigned int i = 0, mcount = static_cast<unsigned int>(scope.meshes_linear.size());
423 for(; i < mcount; ++i) {
424 if (scope.meshes_linear[i] == m) {
425 meshes.push_back(i);
426 break;
427 }
428 }
429
430 ai_assert(i < mcount);
431 }
432 }
433 else if (s == "transform") {
434 nd->mTransformation = ReadTrafo();
435 }
436 }
437
438 } catch(...) {
439 for(aiNode* ch : children) {
440 delete ch;
441 }
442 throw;
443 }
444
445 // FIX: since we used std::multimap<> to keep meshes by id, mesh order now depends on the behaviour
446 // of the multimap implementation with respect to the ordering of entries with same values.
447 // C++11 gives the guarantee that it uses insertion order, before it is implementation-specific.
448 // Sort by material id to always guarantee a deterministic result.
449 std::sort(meshes.begin(), meshes.end(), SortMeshByMaterialId(scope));
450
451 // link meshes to node
452 nd->mNumMeshes = static_cast<unsigned int>(meshes.size());
453 if (nd->mNumMeshes) {
454 nd->mMeshes = new unsigned int[nd->mNumMeshes]();
455 for(unsigned int i = 0; i < nd->mNumMeshes; ++i) {
456 nd->mMeshes[i] = meshes[i];
457 }
458 }
459
460 // link children to parent
461 nd->mNumChildren = static_cast<unsigned int>(children.size());
462 if (nd->mNumChildren) {
463 nd->mChildren = new aiNode*[nd->mNumChildren]();
464 for(unsigned int i = 0; i < nd->mNumChildren; ++i) {
465 nd->mChildren[i] = children[i];
466 children[i]->mParent = nd.get();
467 }
468 }
469
470 return nd.release();
471}
472
473// ------------------------------------------------------------------------------------------------
474aiMatrix4x4 XGLImporter::ReadTrafo()
475{
476 aiVector3D forward, up, right, position;
477 float scale = 1.0f;
478
479 while (ReadElementUpToClosing("transform")) {
480 const std::string& s = GetElementName();
481 if (s == "forward") {
482 forward = ReadVec3();
483 }
484 else if (s == "up") {
485 up = ReadVec3();
486 }
487 else if (s == "position") {
488 position = ReadVec3();
489 }
490 if (s == "scale") {
491 scale = ReadFloat();
492 if(scale < 0.f) {
493 // this is wrong, but we can leave the value and pass it to the caller
494 LogError("found negative scaling in <transform>, ignoring");
495 }
496 }
497 }
498
499 aiMatrix4x4 m;
500 if(forward.SquareLength() < 1e-4 || up.SquareLength() < 1e-4) {
501 LogError("A direction vector in <transform> is zero, ignoring trafo");
502 return m;
503 }
504
505 forward.Normalize();
506 up.Normalize();
507
508 right = forward ^ up;
509 if (std::fabs(up * forward) > 1e-4) {
510 // this is definitely wrong - a degenerate coordinate space ruins everything
511 // so subtitute identity transform.
512 LogError("<forward> and <up> vectors in <transform> are skewing, ignoring trafo");
513 return m;
514 }
515
516 right *= scale;
517 up *= scale;
518 forward *= scale;
519
520 m.a1 = right.x;
521 m.b1 = right.y;
522 m.c1 = right.z;
523
524 m.a2 = up.x;
525 m.b2 = up.y;
526 m.c2 = up.z;
527
528 m.a3 = forward.x;
529 m.b3 = forward.y;
530 m.c3 = forward.z;
531
532 m.a4 = position.x;
533 m.b4 = position.y;
534 m.c4 = position.z;
535
536 return m;
537}
538
539// ------------------------------------------------------------------------------------------------
540aiMesh* XGLImporter::ToOutputMesh(const TempMaterialMesh& m)
541{
542 std::unique_ptr<aiMesh> mesh(new aiMesh());
543
544 mesh->mNumVertices = static_cast<unsigned int>(m.positions.size());
545 mesh->mVertices = new aiVector3D[mesh->mNumVertices];
546 std::copy(m.positions.begin(),m.positions.end(),mesh->mVertices);
547
548 if(m.normals.size()) {
549 mesh->mNormals = new aiVector3D[mesh->mNumVertices];
550 std::copy(m.normals.begin(),m.normals.end(),mesh->mNormals);
551 }
552
553 if(m.uvs.size()) {
554 mesh->mNumUVComponents[0] = 2;
555 mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
556
557 for(unsigned int i = 0; i < mesh->mNumVertices; ++i) {
558 mesh->mTextureCoords[0][i] = aiVector3D(m.uvs[i].x,m.uvs[i].y,0.f);
559 }
560 }
561
562 mesh->mNumFaces = static_cast<unsigned int>(m.vcounts.size());
563 mesh->mFaces = new aiFace[m.vcounts.size()];
564
565 unsigned int idx = 0;
566 for(unsigned int i = 0; i < mesh->mNumFaces; ++i) {
567 aiFace& f = mesh->mFaces[i];
568 f.mNumIndices = m.vcounts[i];
569 f.mIndices = new unsigned int[f.mNumIndices];
570 for(unsigned int c = 0; c < f.mNumIndices; ++c) {
571 f.mIndices[c] = idx++;
572 }
573 }
574
575 ai_assert(idx == mesh->mNumVertices);
576
577 mesh->mPrimitiveTypes = m.pflags;
578 mesh->mMaterialIndex = m.matid;
579 return mesh.release();
580}
581
582// ------------------------------------------------------------------------------------------------
583bool XGLImporter::ReadMesh(TempScope& scope)
584{
585 TempMesh t;
586
587 std::map<unsigned int, TempMaterialMesh> bymat;
588 const unsigned int mesh_id = ReadIDAttr();
589
590 while (ReadElementUpToClosing("mesh")) {
591 const std::string& s = GetElementName();
592
593 if (s == "mat") {
594 ReadMaterial(scope);
595 }
596 else if (s == "p") {
597 if (!m_reader->getAttributeValue("ID")) {
598 LogWarn("no ID attribute on <p>, ignoring");
599 }
600 else {
601 int id = m_reader->getAttributeValueAsInt("ID");
602 t.points[id] = ReadVec3();
603 }
604 }
605 else if (s == "n") {
606 if (!m_reader->getAttributeValue("ID")) {
607 LogWarn("no ID attribute on <n>, ignoring");
608 }
609 else {
610 int id = m_reader->getAttributeValueAsInt("ID");
611 t.normals[id] = ReadVec3();
612 }
613 }
614 else if (s == "tc") {
615 if (!m_reader->getAttributeValue("ID")) {
616 LogWarn("no ID attribute on <tc>, ignoring");
617 }
618 else {
619 int id = m_reader->getAttributeValueAsInt("ID");
620 t.uvs[id] = ReadVec2();
621 }
622 }
623 else if (s == "f" || s == "l" || s == "p") {
624 const unsigned int vcount = s == "f" ? 3 : (s == "l" ? 2 : 1);
625
626 unsigned int mid = ~0u;
627 TempFace tf[3];
628 bool has[3] = {0};
629
630 while (ReadElementUpToClosing(s.c_str())) {
631 const std::string& s = GetElementName();
632 if (s == "fv1" || s == "lv1" || s == "pv1") {
633 ReadFaceVertex(t,tf[0]);
634 has[0] = true;
635 }
636 else if (s == "fv2" || s == "lv2") {
637 ReadFaceVertex(t,tf[1]);
638 has[1] = true;
639 }
640 else if (s == "fv3") {
641 ReadFaceVertex(t,tf[2]);
642 has[2] = true;
643 }
644 else if (s == "mat") {
645 if (mid != ~0u) {
646 LogWarn("only one material tag allowed per <f>");
647 }
648 mid = ResolveMaterialRef(scope);
649 }
650 else if (s == "matref") {
651 if (mid != ~0u) {
652 LogWarn("only one material tag allowed per <f>");
653 }
654 mid = ResolveMaterialRef(scope);
655 }
656 }
657
658 if (mid == ~0u) {
659 ThrowException("missing material index");
660 }
661
662 bool nor = false;
663 bool uv = false;
664 for(unsigned int i = 0; i < vcount; ++i) {
665 if (!has[i]) {
666 ThrowException("missing face vertex data");
667 }
668
669 nor = nor || tf[i].has_normal;
670 uv = uv || tf[i].has_uv;
671 }
672
673 if (mid >= (1<<30)) {
674 LogWarn("material indices exhausted, this may cause errors in the output");
675 }
676 unsigned int meshId = mid | ((nor?1:0)<<31) | ((uv?1:0)<<30);
677
678 TempMaterialMesh& mesh = bymat[meshId];
679 mesh.matid = mid;
680
681 for(unsigned int i = 0; i < vcount; ++i) {
682 mesh.positions.push_back(tf[i].pos);
683 if(nor) {
684 mesh.normals.push_back(tf[i].normal);
685 }
686 if(uv) {
687 mesh.uvs.push_back(tf[i].uv);
688 }
689
690 mesh.pflags |= 1 << (vcount-1);
691 }
692
693 mesh.vcounts.push_back(vcount);
694 }
695 }
696
697 // finally extract output meshes and add them to the scope
698 typedef std::pair<unsigned int, TempMaterialMesh> pairt;
699 for(const pairt& p : bymat) {
700 aiMesh* const m = ToOutputMesh(p.second);
701 scope.meshes_linear.push_back(m);
702
703 // if this is a definition, keep it on the stack
704 if(mesh_id != ~0u) {
705 scope.meshes.insert(std::pair<unsigned int, aiMesh*>(mesh_id,m));
706 }
707 }
708
709 // no id == not a reference, insert this mesh right *here*
710 return mesh_id == ~0u;
711}
712
713// ----------------------------------------------------------------------------------------------
714unsigned int XGLImporter::ResolveMaterialRef(TempScope& scope)
715{
716 const std::string& s = GetElementName();
717 if (s == "mat") {
718 ReadMaterial(scope);
719 return static_cast<unsigned int>(scope.materials_linear.size()-1);
720 }
721
722 const int id = ReadIndexFromText();
723
724 std::map<unsigned int, aiMaterial*>::iterator it = scope.materials.find(id), end = scope.materials.end();
725 if (it == end) {
726 ThrowException("<matref> index out of range");
727 }
728
729 // ok, this is n^2 and should get optimized one day
730 aiMaterial* const m = (*it).second;
731
732 unsigned int i = 0, mcount = static_cast<unsigned int>(scope.materials_linear.size());
733 for(; i < mcount; ++i) {
734 if (scope.materials_linear[i] == m) {
735 return i;
736 }
737 }
738
739 ai_assert(false);
740 return 0;
741}
742
743// ------------------------------------------------------------------------------------------------
744void XGLImporter::ReadMaterial(TempScope& scope)
745{
746 const unsigned int mat_id = ReadIDAttr();
747
748 std::unique_ptr<aiMaterial> mat(new aiMaterial());
749 while (ReadElementUpToClosing("mat")) {
750 const std::string& s = GetElementName();
751 if (s == "amb") {
752 const aiColor3D c = ReadCol3();
753 mat->AddProperty(&c,1,AI_MATKEY_COLOR_AMBIENT);
754 }
755 else if (s == "diff") {
756 const aiColor3D c = ReadCol3();
757 mat->AddProperty(&c,1,AI_MATKEY_COLOR_DIFFUSE);
758 }
759 else if (s == "spec") {
760 const aiColor3D c = ReadCol3();
761 mat->AddProperty(&c,1,AI_MATKEY_COLOR_SPECULAR);
762 }
763 else if (s == "emiss") {
764 const aiColor3D c = ReadCol3();
765 mat->AddProperty(&c,1,AI_MATKEY_COLOR_EMISSIVE);
766 }
767 else if (s == "alpha") {
768 const float f = ReadFloat();
769 mat->AddProperty(&f,1,AI_MATKEY_OPACITY);
770 }
771 else if (s == "shine") {
772 const float f = ReadFloat();
773 mat->AddProperty(&f,1,AI_MATKEY_SHININESS);
774 }
775 }
776
777 scope.materials[mat_id] = mat.get();
778 scope.materials_linear.push_back(mat.release());
779}
780
781
782// ----------------------------------------------------------------------------------------------
783void XGLImporter::ReadFaceVertex(const TempMesh& t, TempFace& out)
784{
785 const std::string& end = GetElementName();
786
787 bool havep = false;
788 while (ReadElementUpToClosing(end.c_str())) {
789 const std::string& s = GetElementName();
790 if (s == "pref") {
791 const unsigned int id = ReadIndexFromText();
792 std::map<unsigned int, aiVector3D>::const_iterator it = t.points.find(id);
793 if (it == t.points.end()) {
794 ThrowException("point index out of range");
795 }
796
797 out.pos = (*it).second;
798 havep = true;
799 }
800 else if (s == "nref") {
801 const unsigned int id = ReadIndexFromText();
802 std::map<unsigned int, aiVector3D>::const_iterator it = t.normals.find(id);
803 if (it == t.normals.end()) {
804 ThrowException("normal index out of range");
805 }
806
807 out.normal = (*it).second;
808 out.has_normal = true;
809 }
810 else if (s == "tcref") {
811 const unsigned int id = ReadIndexFromText();
812 std::map<unsigned int, aiVector2D>::const_iterator it = t.uvs.find(id);
813 if (it == t.uvs.end()) {
814 ThrowException("uv index out of range");
815 }
816
817 out.uv = (*it).second;
818 out.has_uv = true;
819 }
820 else if (s == "p") {
821 out.pos = ReadVec3();
822 }
823 else if (s == "n") {
824 out.normal = ReadVec3();
825 }
826 else if (s == "tc") {
827 out.uv = ReadVec2();
828 }
829 }
830
831 if (!havep) {
832 ThrowException("missing <pref> in <fvN> element");
833 }
834}
835
836// ------------------------------------------------------------------------------------------------
837unsigned int XGLImporter::ReadIDAttr()
838{
839 for(int i = 0, e = m_reader->getAttributeCount(); i < e; ++i) {
840
841 if(!ASSIMP_stricmp(m_reader->getAttributeName(i),"id")) {
842 return m_reader->getAttributeValueAsInt(i);
843 }
844 }
845 return ~0u;
846}
847
848// ------------------------------------------------------------------------------------------------
849float XGLImporter::ReadFloat()
850{
851 if(!SkipToText()) {
852 LogError("unexpected EOF reading float element contents");
853 return 0.f;
854 }
855 const char* s = m_reader->getNodeData(), *se;
856
857 if(!SkipSpaces(&s)) {
858 LogError("unexpected EOL, failed to parse float");
859 return 0.f;
860 }
861
862 float t;
863 se = fast_atoreal_move(s,t);
864
865 if (se == s) {
866 LogError("failed to read float text");
867 return 0.f;
868 }
869
870 return t;
871}
872
873// ------------------------------------------------------------------------------------------------
874unsigned int XGLImporter::ReadIndexFromText()
875{
876 if(!SkipToText()) {
877 LogError("unexpected EOF reading index element contents");
878 return ~0u;
879 }
880 const char* s = m_reader->getNodeData(), *se;
881 if(!SkipSpaces(&s)) {
882 LogError("unexpected EOL, failed to parse index element");
883 return ~0u;
884 }
885
886 const unsigned int t = strtoul10(s,&se);
887
888 if (se == s) {
889 LogError("failed to read index");
890 return ~0u;
891 }
892
893 return t;
894}
895
896// ------------------------------------------------------------------------------------------------
897aiVector2D XGLImporter::ReadVec2()
898{
899 aiVector2D vec;
900
901 if(!SkipToText()) {
902 LogError("unexpected EOF reading vec2 contents");
903 return vec;
904 }
905 const char* s = m_reader->getNodeData();
906
907 for(int i = 0; i < 2; ++i) {
908 if(!SkipSpaces(&s)) {
909 LogError("unexpected EOL, failed to parse vec2");
910 return vec;
911 }
912 vec[i] = fast_atof(&s);
913
914 SkipSpaces(&s);
915 if (i != 1 && *s != ',') {
916 LogError("expected comma, failed to parse vec2");
917 return vec;
918 }
919 ++s;
920 }
921
922 return vec;
923}
924
925// ------------------------------------------------------------------------------------------------
926aiVector3D XGLImporter::ReadVec3()
927{
928 aiVector3D vec;
929
930 if(!SkipToText()) {
931 LogError("unexpected EOF reading vec3 contents");
932 return vec;
933 }
934 const char* s = m_reader->getNodeData();
935
936 for(int i = 0; i < 3; ++i) {
937 if(!SkipSpaces(&s)) {
938 LogError("unexpected EOL, failed to parse vec3");
939 return vec;
940 }
941 vec[i] = fast_atof(&s);
942
943 SkipSpaces(&s);
944 if (i != 2 && *s != ',') {
945 LogError("expected comma, failed to parse vec3");
946 return vec;
947 }
948 ++s;
949 }
950
951 return vec;
952}
953
954// ------------------------------------------------------------------------------------------------
955aiColor3D XGLImporter::ReadCol3()
956{
957 const aiVector3D& v = ReadVec3();
958 if (v.x < 0.f || v.x > 1.0f || v.y < 0.f || v.y > 1.0f || v.z < 0.f || v.z > 1.0f) {
959 LogWarn("color values out of range, ignoring");
960 }
961 return aiColor3D(v.x,v.y,v.z);
962}
963
964#endif
965