vrshoot

view libs/assimp/NFFLoader.cpp @ 1:e7ca128b8713

looks nice :)
author John Tsiombikas <nuclear@member.fsf.org>
date Sun, 02 Feb 2014 00:35:22 +0200
parents
children
line source
1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
6 Copyright (c) 2006-2012, assimp team
8 All rights reserved.
10 Redistribution and use of this software in source and binary forms,
11 with or without modification, are permitted provided that the following
12 conditions are met:
14 * Redistributions of source code must retain the above
15 copyright notice, this list of conditions and the
16 following disclaimer.
18 * Redistributions in binary form must reproduce the above
19 copyright notice, this list of conditions and the
20 following disclaimer in the documentation and/or other
21 materials provided with the distribution.
23 * Neither the name of the assimp team, nor the names of its
24 contributors may be used to endorse or promote products
25 derived from this software without specific prior
26 written permission of the assimp team.
28 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 ---------------------------------------------------------------------------
40 */
42 /** @file Implementation of the STL importer class */
44 #include "AssimpPCH.h"
45 #ifndef ASSIMP_BUILD_NO_NFF_IMPORTER
47 // internal headers
48 #include "NFFLoader.h"
49 #include "ParsingUtils.h"
50 #include "StandardShapes.h"
51 #include "fast_atof.h"
52 #include "RemoveComments.h"
54 using namespace Assimp;
56 static const aiImporterDesc desc = {
57 "Neutral File Format Importer",
58 "",
59 "",
60 "",
61 aiImporterFlags_SupportBinaryFlavour,
62 0,
63 0,
64 0,
65 0,
66 "enff nff"
67 };
69 // ------------------------------------------------------------------------------------------------
70 // Constructor to be privately used by Importer
71 NFFImporter::NFFImporter()
72 {}
74 // ------------------------------------------------------------------------------------------------
75 // Destructor, private as well
76 NFFImporter::~NFFImporter()
77 {}
79 // ------------------------------------------------------------------------------------------------
80 // Returns whether the class can handle the format of the given file.
81 bool NFFImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const
82 {
83 return SimpleExtensionCheck(pFile,"nff","enff");
84 }
86 // ------------------------------------------------------------------------------------------------
87 // Get the list of all supported file extensions
88 const aiImporterDesc* NFFImporter::GetInfo () const
89 {
90 return &desc;
91 }
93 // ------------------------------------------------------------------------------------------------
94 #define AI_NFF_PARSE_FLOAT(f) \
95 SkipSpaces(&sz); \
96 if (!::IsLineEnd(*sz))sz = fast_atoreal_move<float>(sz, (float&)f);
98 // ------------------------------------------------------------------------------------------------
99 #define AI_NFF_PARSE_TRIPLE(v) \
100 AI_NFF_PARSE_FLOAT(v[0]) \
101 AI_NFF_PARSE_FLOAT(v[1]) \
102 AI_NFF_PARSE_FLOAT(v[2])
104 // ------------------------------------------------------------------------------------------------
105 #define AI_NFF_PARSE_SHAPE_INFORMATION() \
106 aiVector3D center, radius(1.0f,get_qnan(),get_qnan()); \
107 AI_NFF_PARSE_TRIPLE(center); \
108 AI_NFF_PARSE_TRIPLE(radius); \
109 if (is_qnan(radius.z))radius.z = radius.x; \
110 if (is_qnan(radius.y))radius.y = radius.x; \
111 currentMesh.radius = radius; \
112 currentMesh.center = center;
114 // ------------------------------------------------------------------------------------------------
115 #define AI_NFF2_GET_NEXT_TOKEN() \
116 do \
117 { \
118 if (!GetNextLine(buffer,line)) \
119 {DefaultLogger::get()->warn("NFF2: Unexpected EOF, can't read next token");break;} \
120 SkipSpaces(line,&sz); \
121 } \
122 while(IsLineEnd(*sz))
125 // ------------------------------------------------------------------------------------------------
126 // Loads the materail table for the NFF2 file format from an external file
127 void NFFImporter::LoadNFF2MaterialTable(std::vector<ShadingInfo>& output,
128 const std::string& path, IOSystem* pIOHandler)
129 {
130 boost::scoped_ptr<IOStream> file( pIOHandler->Open( path, "rb"));
132 // Check whether we can read from the file
133 if( !file.get()) {
134 DefaultLogger::get()->error("NFF2: Unable to open material library " + path + ".");
135 return;
136 }
138 // get the size of the file
139 const unsigned int m = (unsigned int)file->FileSize();
141 // allocate storage and copy the contents of the file to a memory buffer
142 // (terminate it with zero)
143 std::vector<char> mBuffer2(m+1);
144 TextFileToBuffer(file.get(),mBuffer2);
145 const char* buffer = &mBuffer2[0];
147 // First of all: remove all comments from the file
148 CommentRemover::RemoveLineComments("//",&mBuffer2[0]);
150 // The file should start with the magic sequence "mat"
151 if (!TokenMatch(buffer,"mat",3)) {
152 DefaultLogger::get()->error("NFF2: Not a valid material library " + path + ".");
153 return;
154 }
156 ShadingInfo* curShader = NULL;
158 // No read the file line per line
159 char line[4096];
160 const char* sz;
161 while (GetNextLine(buffer,line))
162 {
163 SkipSpaces(line,&sz);
165 // 'version' defines the version of the file format
166 if (TokenMatch(sz,"version",7))
167 {
168 DefaultLogger::get()->info("NFF (Sense8) material library file format: " + std::string(sz));
169 }
170 // 'matdef' starts a new material in the file
171 else if (TokenMatch(sz,"matdef",6))
172 {
173 // add a new material to the list
174 output.push_back( ShadingInfo() );
175 curShader = & output.back();
177 // parse the name of the material
178 }
179 else if (!TokenMatch(sz,"valid",5))
180 {
181 // check whether we have an active material at the moment
182 if (!IsLineEnd(*sz))
183 {
184 if (!curShader)
185 {
186 DefaultLogger::get()->error(std::string("NFF2 material library: Found element ") +
187 sz + "but there is no active material");
188 continue;
189 }
190 }
191 else continue;
193 // now read the material property and determine its type
194 aiColor3D c;
195 if (TokenMatch(sz,"ambient",7))
196 {
197 AI_NFF_PARSE_TRIPLE(c);
198 curShader->ambient = c;
199 }
200 else if (TokenMatch(sz,"diffuse",7) || TokenMatch(sz,"ambientdiffuse",14) /* correct? */)
201 {
202 AI_NFF_PARSE_TRIPLE(c);
203 curShader->diffuse = curShader->ambient = c;
204 }
205 else if (TokenMatch(sz,"specular",8))
206 {
207 AI_NFF_PARSE_TRIPLE(c);
208 curShader->specular = c;
209 }
210 else if (TokenMatch(sz,"emission",8))
211 {
212 AI_NFF_PARSE_TRIPLE(c);
213 curShader->emissive = c;
214 }
215 else if (TokenMatch(sz,"shininess",9))
216 {
217 AI_NFF_PARSE_FLOAT(curShader->shininess);
218 }
219 else if (TokenMatch(sz,"opacity",7))
220 {
221 AI_NFF_PARSE_FLOAT(curShader->opacity);
222 }
223 }
224 }
225 }
227 // ------------------------------------------------------------------------------------------------
228 // Imports the given file into the given scene structure.
229 void NFFImporter::InternReadFile( const std::string& pFile,
230 aiScene* pScene, IOSystem* pIOHandler)
231 {
232 boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
234 // Check whether we can read from the file
235 if( !file.get())
236 throw DeadlyImportError( "Failed to open NFF file " + pFile + ".");
238 unsigned int m = (unsigned int)file->FileSize();
240 // allocate storage and copy the contents of the file to a memory buffer
241 // (terminate it with zero)
242 std::vector<char> mBuffer2;
243 TextFileToBuffer(file.get(),mBuffer2);
244 const char* buffer = &mBuffer2[0];
246 // mesh arrays - separate here to make the handling of the pointers below easier.
247 std::vector<MeshInfo> meshes;
248 std::vector<MeshInfo> meshesWithNormals;
249 std::vector<MeshInfo> meshesWithUVCoords;
250 std::vector<MeshInfo> meshesLocked;
252 char line[4096];
253 const char* sz;
255 // camera parameters
256 aiVector3D camPos, camUp(0.f,1.f,0.f), camLookAt(0.f,0.f,1.f);
257 float angle = 45.f;
258 aiVector2D resolution;
260 bool hasCam = false;
262 MeshInfo* currentMeshWithNormals = NULL;
263 MeshInfo* currentMesh = NULL;
264 MeshInfo* currentMeshWithUVCoords = NULL;
266 ShadingInfo s; // current material info
268 // degree of tesselation
269 unsigned int iTesselation = 4;
271 // some temporary variables we need to parse the file
272 unsigned int sphere = 0,
273 cylinder = 0,
274 cone = 0,
275 numNamed = 0,
276 dodecahedron = 0,
277 octahedron = 0,
278 tetrahedron = 0,
279 hexahedron = 0;
281 // lights imported from the file
282 std::vector<Light> lights;
284 // check whether this is the NFF2 file format
285 if (TokenMatch(buffer,"nff",3))
286 {
287 const float qnan = get_qnan();
288 const aiColor4D cQNAN = aiColor4D (qnan,0.f,0.f,1.f);
289 const aiVector3D vQNAN = aiVector3D(qnan,0.f,0.f);
291 // another NFF file format ... just a raw parser has been implemented
292 // no support for further details, I don't think it is worth the effort
293 // http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/nff/nff2.html
294 // http://www.netghost.narod.ru/gff/graphics/summary/sense8.htm
296 // First of all: remove all comments from the file
297 CommentRemover::RemoveLineComments("//",&mBuffer2[0]);
299 while (GetNextLine(buffer,line))
300 {
301 SkipSpaces(line,&sz);
302 if (TokenMatch(sz,"version",7))
303 {
304 DefaultLogger::get()->info("NFF (Sense8) file format: " + std::string(sz));
305 }
306 else if (TokenMatch(sz,"viewpos",7))
307 {
308 AI_NFF_PARSE_TRIPLE(camPos);
309 hasCam = true;
310 }
311 else if (TokenMatch(sz,"viewdir",7))
312 {
313 AI_NFF_PARSE_TRIPLE(camLookAt);
314 hasCam = true;
315 }
316 // This starts a new object section
317 else if (!IsSpaceOrNewLine(*sz))
318 {
319 unsigned int subMeshIdx = 0;
321 // read the name of the object, skip all spaces
322 // at the end of it.
323 const char* sz3 = sz;
324 while (!IsSpaceOrNewLine(*sz))++sz;
325 std::string objectName = std::string(sz3,(unsigned int)(sz-sz3));
327 const unsigned int objStart = (unsigned int)meshes.size();
329 // There could be a material table in a separate file
330 std::vector<ShadingInfo> materialTable;
331 while (true)
332 {
333 AI_NFF2_GET_NEXT_TOKEN();
335 // material table - an external file
336 if (TokenMatch(sz,"mtable",6))
337 {
338 SkipSpaces(&sz);
339 sz3 = sz;
340 while (!IsSpaceOrNewLine(*sz))++sz;
341 const unsigned int diff = (unsigned int)(sz-sz3);
342 if (!diff)DefaultLogger::get()->warn("NFF2: Found empty mtable token");
343 else
344 {
345 // The material table has the file extension .mat.
346 // If it is not there, we need to append it
347 std::string path = std::string(sz3,diff);
348 if(std::string::npos == path.find_last_of(".mat"))
349 {
350 path.append(".mat");
351 }
353 // Now extract the working directory from the path to
354 // this file and append the material library filename
355 // to it.
356 std::string::size_type s;
357 if ((std::string::npos == (s = path.find_last_of('\\')) || !s) &&
358 (std::string::npos == (s = path.find_last_of('/')) || !s) )
359 {
360 s = pFile.find_last_of('\\');
361 if (std::string::npos == s)s = pFile.find_last_of('/');
362 if (std::string::npos != s)
363 {
364 path = pFile.substr(0,s+1) + path;
365 }
366 }
367 LoadNFF2MaterialTable(materialTable,path,pIOHandler);
368 }
369 }
370 else break;
371 }
373 // read the numbr of vertices
374 unsigned int num = ::strtoul10(sz,&sz);
376 // temporary storage
377 std::vector<aiColor4D> tempColors;
378 std::vector<aiVector3D> tempPositions,tempTextureCoords,tempNormals;
380 bool hasNormals = false,hasUVs = false,hasColor = false;
382 tempPositions.reserve (num);
383 tempColors.reserve (num);
384 tempNormals.reserve (num);
385 tempTextureCoords.reserve (num);
386 for (unsigned int i = 0; i < num; ++i)
387 {
388 AI_NFF2_GET_NEXT_TOKEN();
389 aiVector3D v;
390 AI_NFF_PARSE_TRIPLE(v);
391 tempPositions.push_back(v);
393 // parse all other attributes in the line
394 while (true)
395 {
396 SkipSpaces(&sz);
397 if (IsLineEnd(*sz))break;
399 // color definition
400 if (TokenMatch(sz,"0x",2))
401 {
402 hasColor = true;
403 register unsigned int numIdx = ::strtoul16(sz,&sz);
404 aiColor4D clr;
405 clr.a = 1.f;
407 // 0xRRGGBB
408 clr.r = ((numIdx >> 16u) & 0xff) / 255.f;
409 clr.g = ((numIdx >> 8u) & 0xff) / 255.f;
410 clr.b = ((numIdx) & 0xff) / 255.f;
411 tempColors.push_back(clr);
412 }
413 // normal vector
414 else if (TokenMatch(sz,"norm",4))
415 {
416 hasNormals = true;
417 AI_NFF_PARSE_TRIPLE(v);
418 tempNormals.push_back(v);
419 }
420 // UV coordinate
421 else if (TokenMatch(sz,"uv",2))
422 {
423 hasUVs = true;
424 AI_NFF_PARSE_FLOAT(v.x);
425 AI_NFF_PARSE_FLOAT(v.y);
426 v.z = 0.f;
427 tempTextureCoords.push_back(v);
428 }
429 }
431 // fill in dummies for all attributes that have not been set
432 if (tempNormals.size() != tempPositions.size())
433 tempNormals.push_back(vQNAN);
435 if (tempTextureCoords.size() != tempPositions.size())
436 tempTextureCoords.push_back(vQNAN);
438 if (tempColors.size() != tempPositions.size())
439 tempColors.push_back(cQNAN);
440 }
442 AI_NFF2_GET_NEXT_TOKEN();
443 if (!num)throw DeadlyImportError("NFF2: There are zero vertices");
444 num = ::strtoul10(sz,&sz);
446 std::vector<unsigned int> tempIdx;
447 tempIdx.reserve(10);
448 for (unsigned int i = 0; i < num; ++i)
449 {
450 AI_NFF2_GET_NEXT_TOKEN();
451 SkipSpaces(line,&sz);
452 unsigned int numIdx = ::strtoul10(sz,&sz);
454 // read all faces indices
455 if (numIdx)
456 {
457 // mesh.faces.push_back(numIdx);
458 // tempIdx.erase(tempIdx.begin(),tempIdx.end());
459 tempIdx.resize(numIdx);
461 for (unsigned int a = 0; a < numIdx;++a)
462 {
463 SkipSpaces(sz,&sz);
464 m = ::strtoul10(sz,&sz);
465 if (m >= (unsigned int)tempPositions.size())
466 {
467 DefaultLogger::get()->error("NFF2: Vertex index overflow");
468 m= 0;
469 }
470 // mesh.vertices.push_back (tempPositions[idx]);
471 tempIdx[a] = m;
472 }
473 }
475 // build a temporary shader object for the face.
476 ShadingInfo shader;
477 unsigned int matIdx = 0;
479 // white material color - we have vertex colors
480 shader.color = aiColor3D(1.f,1.f,1.f);
481 aiColor4D c = aiColor4D(1.f,1.f,1.f,1.f);
482 while (true)
483 {
484 SkipSpaces(sz,&sz);
485 if(IsLineEnd(*sz))break;
487 // per-polygon colors
488 if (TokenMatch(sz,"0x",2))
489 {
490 hasColor = true;
491 const char* sz2 = sz;
492 numIdx = ::strtoul16(sz,&sz);
493 const unsigned int diff = (unsigned int)(sz-sz2);
495 // 0xRRGGBB
496 if (diff > 3)
497 {
498 c.r = ((numIdx >> 16u) & 0xff) / 255.f;
499 c.g = ((numIdx >> 8u) & 0xff) / 255.f;
500 c.b = ((numIdx) & 0xff) / 255.f;
501 }
502 // 0xRGB
503 else
504 {
505 c.r = ((numIdx >> 8u) & 0xf) / 16.f;
506 c.g = ((numIdx >> 4u) & 0xf) / 16.f;
507 c.b = ((numIdx) & 0xf) / 16.f;
508 }
509 }
510 // TODO - implement texture mapping here
511 #if 0
512 // mirror vertex texture coordinate?
513 else if (TokenMatch(sz,"mirror",6))
514 {
515 }
516 // texture coordinate scaling
517 else if (TokenMatch(sz,"scale",5))
518 {
519 }
520 // texture coordinate translation
521 else if (TokenMatch(sz,"trans",5))
522 {
523 }
524 // texture coordinate rotation angle
525 else if (TokenMatch(sz,"rot",3))
526 {
527 }
528 #endif
530 // texture file name for this polygon + mapping information
531 else if ('_' == sz[0])
532 {
533 // get mapping information
534 switch (sz[1])
535 {
536 case 'v':
537 case 'V':
539 shader.shaded = false;
540 break;
542 case 't':
543 case 'T':
544 case 'u':
545 case 'U':
547 DefaultLogger::get()->warn("Unsupported NFF2 texture attribute: trans");
548 };
549 if (!sz[1] || '_' != sz[2])
550 {
551 DefaultLogger::get()->warn("NFF2: Expected underscore after texture attributes");
552 continue;
553 }
554 const char* sz2 = sz+3;
555 while (!IsSpaceOrNewLine( *sz ))++sz;
556 const unsigned int diff = (unsigned int)(sz-sz2);
557 if (diff)shader.texFile = std::string(sz2,diff);
558 }
560 // Two-sided material?
561 else if (TokenMatch(sz,"both",4))
562 {
563 shader.twoSided = true;
564 }
566 // Material ID?
567 else if (!materialTable.empty() && TokenMatch(sz,"matid",5))
568 {
569 SkipSpaces(&sz);
570 matIdx = ::strtoul10(sz,&sz);
571 if (matIdx >= materialTable.size())
572 {
573 DefaultLogger::get()->error("NFF2: Material index overflow.");
574 matIdx = 0;
575 }
577 // now combine our current shader with the shader we
578 // read from the material table.
579 ShadingInfo& mat = materialTable[matIdx];
580 shader.ambient = mat.ambient;
581 shader.diffuse = mat.diffuse;
582 shader.emissive = mat.emissive;
583 shader.opacity = mat.opacity;
584 shader.specular = mat.specular;
585 shader.shininess = mat.shininess;
586 }
587 else SkipToken(sz);
588 }
590 // search the list of all shaders we have for this object whether
591 // there is an identical one. In this case, we append our mesh
592 // data to it.
593 MeshInfo* mesh = NULL;
594 for (std::vector<MeshInfo>::iterator it = meshes.begin() + objStart, end = meshes.end();
595 it != end; ++it)
596 {
597 if ((*it).shader == shader && (*it).matIndex == matIdx)
598 {
599 // we have one, we can append our data to it
600 mesh = &(*it);
601 }
602 }
603 if (!mesh)
604 {
605 meshes.push_back(MeshInfo(PatchType_Simple,false));
606 mesh = &meshes.back();
607 mesh->matIndex = matIdx;
609 // We need to add a new mesh to the list. We assign
610 // an unique name to it to make sure the scene will
611 // pass the validation step for the moment.
612 // TODO: fix naming of objects in the scenegraph later
613 if (objectName.length())
614 {
615 ::strcpy(mesh->name,objectName.c_str());
616 ASSIMP_itoa10(&mesh->name[objectName.length()],30,subMeshIdx++);
617 }
619 // copy the shader to the mesh.
620 mesh->shader = shader;
621 }
623 // fill the mesh with data
624 if (!tempIdx.empty())
625 {
626 mesh->faces.push_back((unsigned int)tempIdx.size());
627 for (std::vector<unsigned int>::const_iterator it = tempIdx.begin(), end = tempIdx.end();
628 it != end;++it)
629 {
630 m = *it;
632 // copy colors -vertex color specifications override polygon color specifications
633 if (hasColor)
634 {
635 const aiColor4D& clr = tempColors[m];
636 mesh->colors.push_back((is_qnan( clr.r ) ? c : clr));
637 }
639 // positions should always be there
640 mesh->vertices.push_back (tempPositions[m]);
642 // copy normal vectors
643 if (hasNormals)
644 mesh->normals.push_back (tempNormals[m]);
646 // copy texture coordinates
647 if (hasUVs)
648 mesh->uvs.push_back (tempTextureCoords[m]);
649 }
650 }
651 }
652 if (!num)throw DeadlyImportError("NFF2: There are zero faces");
653 }
654 }
655 camLookAt = camLookAt + camPos;
656 }
657 else // "Normal" Neutral file format that is quite more common
658 {
659 while (GetNextLine(buffer,line))
660 {
661 sz = line;
662 if ('p' == line[0] || TokenMatch(sz,"tpp",3))
663 {
664 MeshInfo* out = NULL;
666 // 'tpp' - texture polygon patch primitive
667 if ('t' == line[0])
668 {
669 currentMeshWithUVCoords = NULL;
670 for (std::vector<MeshInfo>::iterator it = meshesWithUVCoords.begin(), end = meshesWithUVCoords.end();
671 it != end;++it)
672 {
673 if ((*it).shader == s)
674 {
675 currentMeshWithUVCoords = &(*it);
676 break;
677 }
678 }
680 if (!currentMeshWithUVCoords)
681 {
682 meshesWithUVCoords.push_back(MeshInfo(PatchType_UVAndNormals));
683 currentMeshWithUVCoords = &meshesWithUVCoords.back();
684 currentMeshWithUVCoords->shader = s;
685 }
686 out = currentMeshWithUVCoords;
687 }
688 // 'pp' - polygon patch primitive
689 else if ('p' == line[1])
690 {
691 currentMeshWithNormals = NULL;
692 for (std::vector<MeshInfo>::iterator it = meshesWithNormals.begin(), end = meshesWithNormals.end();
693 it != end;++it)
694 {
695 if ((*it).shader == s)
696 {
697 currentMeshWithNormals = &(*it);
698 break;
699 }
700 }
702 if (!currentMeshWithNormals)
703 {
704 meshesWithNormals.push_back(MeshInfo(PatchType_Normals));
705 currentMeshWithNormals = &meshesWithNormals.back();
706 currentMeshWithNormals->shader = s;
707 }
708 sz = &line[2];out = currentMeshWithNormals;
709 }
710 // 'p' - polygon primitive
711 else
712 {
713 currentMesh = NULL;
714 for (std::vector<MeshInfo>::iterator it = meshes.begin(), end = meshes.end();
715 it != end;++it)
716 {
717 if ((*it).shader == s)
718 {
719 currentMesh = &(*it);
720 break;
721 }
722 }
724 if (!currentMesh)
725 {
726 meshes.push_back(MeshInfo(PatchType_Simple));
727 currentMesh = &meshes.back();
728 currentMesh->shader = s;
729 }
730 sz = &line[1];out = currentMesh;
731 }
732 SkipSpaces(sz,&sz);
733 m = strtoul10(sz);
735 // ---- flip the face order
736 out->vertices.resize(out->vertices.size()+m);
737 if (out != currentMesh)
738 {
739 out->normals.resize(out->vertices.size());
740 }
741 if (out == currentMeshWithUVCoords)
742 {
743 out->uvs.resize(out->vertices.size());
744 }
745 for (unsigned int n = 0; n < m;++n)
746 {
747 if(!GetNextLine(buffer,line))
748 {
749 DefaultLogger::get()->error("NFF: Unexpected EOF was encountered. Patch definition incomplete");
750 continue;
751 }
753 aiVector3D v; sz = &line[0];
754 AI_NFF_PARSE_TRIPLE(v);
755 out->vertices[out->vertices.size()-n-1] = v;
757 if (out != currentMesh)
758 {
759 AI_NFF_PARSE_TRIPLE(v);
760 out->normals[out->vertices.size()-n-1] = v;
761 }
762 if (out == currentMeshWithUVCoords)
763 {
764 // FIX: in one test file this wraps over multiple lines
765 SkipSpaces(&sz);
766 if (IsLineEnd(*sz))
767 {
768 GetNextLine(buffer,line);
769 sz = line;
770 }
771 AI_NFF_PARSE_FLOAT(v.x);
772 SkipSpaces(&sz);
773 if (IsLineEnd(*sz))
774 {
775 GetNextLine(buffer,line);
776 sz = line;
777 }
778 AI_NFF_PARSE_FLOAT(v.y);
779 v.y = 1.f - v.y;
780 out->uvs[out->vertices.size()-n-1] = v;
781 }
782 }
783 out->faces.push_back(m);
784 }
785 // 'f' - shading information block
786 else if (TokenMatch(sz,"f",1))
787 {
788 float d;
790 // read the RGB colors
791 AI_NFF_PARSE_TRIPLE(s.color);
793 // read the other properties
794 AI_NFF_PARSE_FLOAT(s.diffuse.r);
795 AI_NFF_PARSE_FLOAT(s.specular.r);
796 AI_NFF_PARSE_FLOAT(d); // skip shininess and transmittance
797 AI_NFF_PARSE_FLOAT(d);
798 AI_NFF_PARSE_FLOAT(s.refracti);
800 // NFF2 uses full colors here so we need to use them too
801 // although NFF uses simple scaling factors
802 s.diffuse.g = s.diffuse.b = s.diffuse.r;
803 s.specular.g = s.specular.b = s.specular.r;
805 // if the next one is NOT a number we assume it is a texture file name
806 // this feature is used by some NFF files on the internet and it has
807 // been implemented as it can be really useful
808 SkipSpaces(&sz);
809 if (!IsNumeric(*sz))
810 {
811 // TODO: Support full file names with spaces and quotation marks ...
812 const char* p = sz;
813 while (!IsSpaceOrNewLine( *sz ))++sz;
815 unsigned int diff = (unsigned int)(sz-p);
816 if (diff)
817 {
818 s.texFile = std::string(p,diff);
819 }
820 }
821 else
822 {
823 AI_NFF_PARSE_FLOAT(s.ambient); // optional
824 }
825 }
826 // 'shader' - other way to specify a texture
827 else if (TokenMatch(sz,"shader",6))
828 {
829 SkipSpaces(&sz);
830 const char* old = sz;
831 while (!IsSpaceOrNewLine(*sz))++sz;
832 s.texFile = std::string(old, (uintptr_t)sz - (uintptr_t)old);
833 }
834 // 'l' - light source
835 else if (TokenMatch(sz,"l",1))
836 {
837 lights.push_back(Light());
838 Light& light = lights.back();
840 AI_NFF_PARSE_TRIPLE(light.position);
841 AI_NFF_PARSE_FLOAT (light.intensity);
842 AI_NFF_PARSE_TRIPLE(light.color);
843 }
844 // 's' - sphere
845 else if (TokenMatch(sz,"s",1))
846 {
847 meshesLocked.push_back(MeshInfo(PatchType_Simple,true));
848 MeshInfo& currentMesh = meshesLocked.back();
849 currentMesh.shader = s;
850 currentMesh.shader.mapping = aiTextureMapping_SPHERE;
852 AI_NFF_PARSE_SHAPE_INFORMATION();
854 // we don't need scaling or translation here - we do it in the node's transform
855 StandardShapes::MakeSphere(iTesselation, currentMesh.vertices);
856 currentMesh.faces.resize(currentMesh.vertices.size()/3,3);
858 // generate a name for the mesh
859 ::sprintf(currentMesh.name,"sphere_%i",sphere++);
860 }
861 // 'dod' - dodecahedron
862 else if (TokenMatch(sz,"dod",3))
863 {
864 meshesLocked.push_back(MeshInfo(PatchType_Simple,true));
865 MeshInfo& currentMesh = meshesLocked.back();
866 currentMesh.shader = s;
867 currentMesh.shader.mapping = aiTextureMapping_SPHERE;
869 AI_NFF_PARSE_SHAPE_INFORMATION();
871 // we don't need scaling or translation here - we do it in the node's transform
872 StandardShapes::MakeDodecahedron(currentMesh.vertices);
873 currentMesh.faces.resize(currentMesh.vertices.size()/3,3);
875 // generate a name for the mesh
876 ::sprintf(currentMesh.name,"dodecahedron_%i",dodecahedron++);
877 }
879 // 'oct' - octahedron
880 else if (TokenMatch(sz,"oct",3))
881 {
882 meshesLocked.push_back(MeshInfo(PatchType_Simple,true));
883 MeshInfo& currentMesh = meshesLocked.back();
884 currentMesh.shader = s;
885 currentMesh.shader.mapping = aiTextureMapping_SPHERE;
887 AI_NFF_PARSE_SHAPE_INFORMATION();
889 // we don't need scaling or translation here - we do it in the node's transform
890 StandardShapes::MakeOctahedron(currentMesh.vertices);
891 currentMesh.faces.resize(currentMesh.vertices.size()/3,3);
893 // generate a name for the mesh
894 ::sprintf(currentMesh.name,"octahedron_%i",octahedron++);
895 }
897 // 'tet' - tetrahedron
898 else if (TokenMatch(sz,"tet",3))
899 {
900 meshesLocked.push_back(MeshInfo(PatchType_Simple,true));
901 MeshInfo& currentMesh = meshesLocked.back();
902 currentMesh.shader = s;
903 currentMesh.shader.mapping = aiTextureMapping_SPHERE;
905 AI_NFF_PARSE_SHAPE_INFORMATION();
907 // we don't need scaling or translation here - we do it in the node's transform
908 StandardShapes::MakeTetrahedron(currentMesh.vertices);
909 currentMesh.faces.resize(currentMesh.vertices.size()/3,3);
911 // generate a name for the mesh
912 ::sprintf(currentMesh.name,"tetrahedron_%i",tetrahedron++);
913 }
915 // 'hex' - hexahedron
916 else if (TokenMatch(sz,"hex",3))
917 {
918 meshesLocked.push_back(MeshInfo(PatchType_Simple,true));
919 MeshInfo& currentMesh = meshesLocked.back();
920 currentMesh.shader = s;
921 currentMesh.shader.mapping = aiTextureMapping_BOX;
923 AI_NFF_PARSE_SHAPE_INFORMATION();
925 // we don't need scaling or translation here - we do it in the node's transform
926 StandardShapes::MakeHexahedron(currentMesh.vertices);
927 currentMesh.faces.resize(currentMesh.vertices.size()/3,3);
929 // generate a name for the mesh
930 ::sprintf(currentMesh.name,"hexahedron_%i",hexahedron++);
931 }
932 // 'c' - cone
933 else if (TokenMatch(sz,"c",1))
934 {
935 meshesLocked.push_back(MeshInfo(PatchType_Simple,true));
936 MeshInfo& currentMesh = meshesLocked.back();
937 currentMesh.shader = s;
938 currentMesh.shader.mapping = aiTextureMapping_CYLINDER;
940 if(!GetNextLine(buffer,line))
941 {
942 DefaultLogger::get()->error("NFF: Unexpected end of file (cone definition not complete)");
943 break;
944 }
945 sz = line;
947 // read the two center points and the respective radii
948 aiVector3D center1, center2; float radius1, radius2;
949 AI_NFF_PARSE_TRIPLE(center1);
950 AI_NFF_PARSE_FLOAT(radius1);
952 if(!GetNextLine(buffer,line))
953 {
954 DefaultLogger::get()->error("NFF: Unexpected end of file (cone definition not complete)");
955 break;
956 }
957 sz = line;
959 AI_NFF_PARSE_TRIPLE(center2);
960 AI_NFF_PARSE_FLOAT(radius2);
962 // compute the center point of the cone/cylinder -
963 // it is its local transformation origin
964 currentMesh.dir = center2-center1;
965 currentMesh.center = center1+currentMesh.dir/2.f;
967 float f;
968 if (( f = currentMesh.dir.Length()) < 10e-3f )
969 {
970 DefaultLogger::get()->error("NFF: Cone height is close to zero");
971 continue;
972 }
973 currentMesh.dir /= f; // normalize
975 // generate the cone - it consists of simple triangles
976 StandardShapes::MakeCone(f, radius1, radius2,
977 integer_pow(4, iTesselation), currentMesh.vertices);
979 // MakeCone() returns tris
980 currentMesh.faces.resize(currentMesh.vertices.size()/3,3);
982 // generate a name for the mesh. 'cone' if it a cone,
983 // 'cylinder' if it is a cylinder. Funny, isn't it?
984 if (radius1 != radius2)
985 ::sprintf(currentMesh.name,"cone_%i",cone++);
986 else ::sprintf(currentMesh.name,"cylinder_%i",cylinder++);
987 }
988 // 'tess' - tesselation
989 else if (TokenMatch(sz,"tess",4))
990 {
991 SkipSpaces(&sz);
992 iTesselation = strtoul10(sz);
993 }
994 // 'from' - camera position
995 else if (TokenMatch(sz,"from",4))
996 {
997 AI_NFF_PARSE_TRIPLE(camPos);
998 hasCam = true;
999 }
1000 // 'at' - camera look-at vector
1001 else if (TokenMatch(sz,"at",2))
1003 AI_NFF_PARSE_TRIPLE(camLookAt);
1004 hasCam = true;
1006 // 'up' - camera up vector
1007 else if (TokenMatch(sz,"up",2))
1009 AI_NFF_PARSE_TRIPLE(camUp);
1010 hasCam = true;
1012 // 'angle' - (half?) camera field of view
1013 else if (TokenMatch(sz,"angle",5))
1015 AI_NFF_PARSE_FLOAT(angle);
1016 hasCam = true;
1018 // 'resolution' - used to compute the screen aspect
1019 else if (TokenMatch(sz,"resolution",10))
1021 AI_NFF_PARSE_FLOAT(resolution.x);
1022 AI_NFF_PARSE_FLOAT(resolution.y);
1023 hasCam = true;
1025 // 'pb' - bezier patch. Not supported yet
1026 else if (TokenMatch(sz,"pb",2))
1028 DefaultLogger::get()->error("NFF: Encountered unsupported ID: bezier patch");
1030 // 'pn' - NURBS. Not supported yet
1031 else if (TokenMatch(sz,"pn",2) || TokenMatch(sz,"pnn",3))
1033 DefaultLogger::get()->error("NFF: Encountered unsupported ID: NURBS");
1035 // '' - comment
1036 else if ('#' == line[0])
1038 const char* sz;SkipSpaces(&line[1],&sz);
1039 if (!IsLineEnd(*sz))DefaultLogger::get()->info(sz);
1044 // copy all arrays into one large
1045 meshes.reserve (meshes.size()+meshesLocked.size()+meshesWithNormals.size()+meshesWithUVCoords.size());
1046 meshes.insert (meshes.end(),meshesLocked.begin(),meshesLocked.end());
1047 meshes.insert (meshes.end(),meshesWithNormals.begin(),meshesWithNormals.end());
1048 meshes.insert (meshes.end(),meshesWithUVCoords.begin(),meshesWithUVCoords.end());
1050 // now generate output meshes. first find out how many meshes we'll need
1051 std::vector<MeshInfo>::const_iterator it = meshes.begin(), end = meshes.end();
1052 for (;it != end;++it)
1054 if (!(*it).faces.empty())
1056 ++pScene->mNumMeshes;
1057 if ((*it).name[0])++numNamed;
1061 // generate a dummy root node - assign all unnamed elements such
1062 // as polygons and polygon patches to the root node and generate
1063 // sub nodes for named objects such as spheres and cones.
1064 aiNode* const root = new aiNode();
1065 root->mName.Set("<NFF_Root>");
1066 root->mNumChildren = numNamed + (hasCam ? 1 : 0) + (unsigned int) lights.size();
1067 root->mNumMeshes = pScene->mNumMeshes-numNamed;
1069 aiNode** ppcChildren = NULL;
1070 unsigned int* pMeshes = NULL;
1071 if (root->mNumMeshes)
1072 pMeshes = root->mMeshes = new unsigned int[root->mNumMeshes];
1073 if (root->mNumChildren)
1074 ppcChildren = root->mChildren = new aiNode*[root->mNumChildren];
1076 // generate the camera
1077 if (hasCam)
1079 aiNode* nd = *ppcChildren = new aiNode();
1080 nd->mName.Set("<NFF_Camera>");
1081 nd->mParent = root;
1083 // allocate the camera in the scene
1084 pScene->mNumCameras = 1;
1085 pScene->mCameras = new aiCamera*[1];
1086 aiCamera* c = pScene->mCameras[0] = new aiCamera;
1088 c->mName = nd->mName; // make sure the names are identical
1089 c->mHorizontalFOV = AI_DEG_TO_RAD( angle );
1090 c->mLookAt = camLookAt - camPos;
1091 c->mPosition = camPos;
1092 c->mUp = camUp;
1094 // If the resolution is not specified in the file, we
1095 // need to set 1.0 as aspect.
1096 c->mAspect = (!resolution.y ? 0.f : resolution.x / resolution.y);
1097 ++ppcChildren;
1100 // generate light sources
1101 if (!lights.empty())
1103 pScene->mNumLights = (unsigned int)lights.size();
1104 pScene->mLights = new aiLight*[pScene->mNumLights];
1105 for (unsigned int i = 0; i < pScene->mNumLights;++i,++ppcChildren)
1107 const Light& l = lights[i];
1109 aiNode* nd = *ppcChildren = new aiNode();
1110 nd->mParent = root;
1112 nd->mName.length = ::sprintf(nd->mName.data,"<NFF_Light%i>",i);
1114 // allocate the light in the scene data structure
1115 aiLight* out = pScene->mLights[i] = new aiLight();
1116 out->mName = nd->mName; // make sure the names are identical
1117 out->mType = aiLightSource_POINT;
1118 out->mColorDiffuse = out->mColorSpecular = l.color * l.intensity;
1119 out->mPosition = l.position;
1123 if (!pScene->mNumMeshes)throw DeadlyImportError("NFF: No meshes loaded");
1124 pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
1125 pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials = pScene->mNumMeshes];
1126 for (it = meshes.begin(), m = 0; it != end;++it)
1128 if ((*it).faces.empty())continue;
1130 const MeshInfo& src = *it;
1131 aiMesh* const mesh = pScene->mMeshes[m] = new aiMesh();
1132 mesh->mNumVertices = (unsigned int)src.vertices.size();
1133 mesh->mNumFaces = (unsigned int)src.faces.size();
1135 // Generate sub nodes for named meshes
1136 if (src.name[0])
1138 aiNode* const node = *ppcChildren = new aiNode();
1139 node->mParent = root;
1140 node->mNumMeshes = 1;
1141 node->mMeshes = new unsigned int[1];
1142 node->mMeshes[0] = m;
1143 node->mName.Set(src.name);
1145 // setup the transformation matrix of the node
1146 aiMatrix4x4::FromToMatrix(aiVector3D(0.f,1.f,0.f),
1147 src.dir,node->mTransformation);
1149 aiMatrix4x4& mat = node->mTransformation;
1150 mat.a1 *= src.radius.x; mat.b1 *= src.radius.x; mat.c1 *= src.radius.x;
1151 mat.a2 *= src.radius.y; mat.b2 *= src.radius.y; mat.c2 *= src.radius.y;
1152 mat.a3 *= src.radius.z; mat.b3 *= src.radius.z; mat.c3 *= src.radius.z;
1153 mat.a4 = src.center.x;
1154 mat.b4 = src.center.y;
1155 mat.c4 = src.center.z;
1157 ++ppcChildren;
1159 else *pMeshes++ = m;
1161 // copy vertex positions
1162 mesh->mVertices = new aiVector3D[mesh->mNumVertices];
1163 ::memcpy(mesh->mVertices,&src.vertices[0],
1164 sizeof(aiVector3D)*mesh->mNumVertices);
1166 // NFF2: there could be vertex colors
1167 if (!src.colors.empty())
1169 ai_assert(src.colors.size() == src.vertices.size());
1171 // copy vertex colors
1172 mesh->mColors[0] = new aiColor4D[mesh->mNumVertices];
1173 ::memcpy(mesh->mColors[0],&src.colors[0],
1174 sizeof(aiColor4D)*mesh->mNumVertices);
1177 if (!src.normals.empty())
1179 ai_assert(src.normals.size() == src.vertices.size());
1181 // copy normal vectors
1182 mesh->mNormals = new aiVector3D[mesh->mNumVertices];
1183 ::memcpy(mesh->mNormals,&src.normals[0],
1184 sizeof(aiVector3D)*mesh->mNumVertices);
1187 if (!src.uvs.empty())
1189 ai_assert(src.uvs.size() == src.vertices.size());
1191 // copy texture coordinates
1192 mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
1193 ::memcpy(mesh->mTextureCoords[0],&src.uvs[0],
1194 sizeof(aiVector3D)*mesh->mNumVertices);
1197 // generate faces
1198 unsigned int p = 0;
1199 aiFace* pFace = mesh->mFaces = new aiFace[mesh->mNumFaces];
1200 for (std::vector<unsigned int>::const_iterator it2 = src.faces.begin(),
1201 end2 = src.faces.end();
1202 it2 != end2;++it2,++pFace)
1204 pFace->mIndices = new unsigned int [ pFace->mNumIndices = *it2 ];
1205 for (unsigned int o = 0; o < pFace->mNumIndices;++o)
1206 pFace->mIndices[o] = p++;
1209 // generate a material for the mesh
1210 aiMaterial* pcMat = (aiMaterial*)(pScene->mMaterials[m] = new aiMaterial());
1212 mesh->mMaterialIndex = m++;
1214 aiString s;
1215 s.Set(AI_DEFAULT_MATERIAL_NAME);
1216 pcMat->AddProperty(&s, AI_MATKEY_NAME);
1218 // FIX: Ignore diffuse == 0
1219 aiColor3D c = src.shader.color * (src.shader.diffuse.r ? src.shader.diffuse : aiColor3D(1.f,1.f,1.f));
1220 pcMat->AddProperty(&c,1,AI_MATKEY_COLOR_DIFFUSE);
1221 c = src.shader.color * src.shader.specular;
1222 pcMat->AddProperty(&c,1,AI_MATKEY_COLOR_SPECULAR);
1224 // NFF2 - default values for NFF
1225 pcMat->AddProperty(&src.shader.ambient, 1,AI_MATKEY_COLOR_AMBIENT);
1226 pcMat->AddProperty(&src.shader.emissive,1,AI_MATKEY_COLOR_EMISSIVE);
1227 pcMat->AddProperty(&src.shader.opacity, 1,AI_MATKEY_OPACITY);
1229 // setup the first texture layer, if existing
1230 if (src.shader.texFile.length())
1232 s.Set(src.shader.texFile);
1233 pcMat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(0));
1235 if (aiTextureMapping_UV != src.shader.mapping) {
1237 aiVector3D v(0.f,-1.f,0.f);
1238 pcMat->AddProperty(&v, 1,AI_MATKEY_TEXMAP_AXIS_DIFFUSE(0));
1239 pcMat->AddProperty((int*)&src.shader.mapping, 1,AI_MATKEY_MAPPING_DIFFUSE(0));
1243 // setup the name of the material
1244 if (src.shader.name.length())
1246 s.Set(src.shader.texFile);
1247 pcMat->AddProperty(&s,AI_MATKEY_NAME);
1250 // setup some more material properties that are specific to NFF2
1251 int i;
1252 if (src.shader.twoSided)
1254 i = 1;
1255 pcMat->AddProperty(&i,1,AI_MATKEY_TWOSIDED);
1257 i = (src.shader.shaded ? aiShadingMode_Gouraud : aiShadingMode_NoShading);
1258 if (src.shader.shininess)
1260 i = aiShadingMode_Phong;
1261 pcMat->AddProperty(&src.shader.shininess,1,AI_MATKEY_SHININESS);
1263 pcMat->AddProperty(&i,1,AI_MATKEY_SHADING_MODEL);
1265 pScene->mRootNode = root;
1268 #endif // !! ASSIMP_BUILD_NO_NFF_IMPORTER