1 /*
2 Open Asset Import Library (assimp)
3 ----------------------------------------------------------------------
4 
5 Copyright (c) 2006-2015, assimp team
6 All rights reserved.
7 
8 Redistribution and use of this software in source and binary forms,
9 with or without modification, are permitted provided that the
10 following conditions are met:
11 
12 * Redistributions of source code must retain the above
13   copyright notice, this list of conditions and the
14   following disclaimer.
15 
16 * Redistributions in binary form must reproduce the above
17   copyright notice, this list of conditions and the
18   following disclaimer in the documentation and/or other
19   materials provided with the distribution.
20 
21 * Neither the name of the assimp team, nor the names of its
22   contributors may be used to endorse or promote products
23   derived from this software without specific prior
24   written permission of the assimp team.
25 
26 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 
38 ----------------------------------------------------------------------
39 */
40 
41 /** @file  COBLoader.cpp
42  *  @brief Implementation of the TrueSpace COB/SCN importer class.
43  */
44 
45 
46 #ifndef ASSIMP_BUILD_NO_COB_IMPORTER
47 #include "COBLoader.h"
48 #include "COBScene.h"
49 
50 #include "StreamReader.h"
51 #include "ParsingUtils.h"
52 #include "fast_atof.h"
53 
54 #include "LineSplitter.h"
55 #include "TinyFormatter.h"
56 #include <boost/scoped_ptr.hpp>
57 #include <boost/foreach.hpp>
58 #include "../include/assimp/IOSystem.hpp"
59 #include "../include/assimp/DefaultLogger.hpp"
60 #include "../include/assimp/scene.h"
61 
62 
63 using namespace Assimp;
64 using namespace Assimp::COB;
65 using namespace Assimp::Formatter;
66 
67 #define for_each BOOST_FOREACH
68 
69 
70 static const float units[] = {
71     1000.f,
72     100.f,
73     1.f,
74     0.001f,
75     1.f/0.0254f,
76     1.f/0.3048f,
77     1.f/0.9144f,
78     1.f/1609.344f
79 };
80 
81 static const aiImporterDesc desc = {
82     "TrueSpace Object Importer",
83     "",
84     "",
85     "little-endian files only",
86     aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour,
87     0,
88     0,
89     0,
90     0,
91     "cob scn"
92 };
93 
94 
95 // ------------------------------------------------------------------------------------------------
96 // Constructor to be privately used by Importer
COBImporter()97 COBImporter::COBImporter()
98 {}
99 
100 // ------------------------------------------------------------------------------------------------
101 // Destructor, private as well
~COBImporter()102 COBImporter::~COBImporter()
103 {}
104 
105 // ------------------------------------------------------------------------------------------------
106 // Returns whether the class can handle the format of the given file.
CanRead(const std::string & pFile,IOSystem * pIOHandler,bool checkSig) const107 bool COBImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
108 {
109     const std::string& extension = GetExtension(pFile);
110     if (extension == "cob" || extension == "scn") {
111         return true;
112     }
113 
114     else if ((!extension.length() || checkSig) && pIOHandler)   {
115         const char* tokens[] = {"Caligary"};
116         return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
117     }
118     return false;
119 }
120 
121 // ------------------------------------------------------------------------------------------------
122 // Loader meta information
GetInfo() const123 const aiImporterDesc* COBImporter::GetInfo () const
124 {
125     return &desc;
126 }
127 
128 // ------------------------------------------------------------------------------------------------
129 // Setup configuration properties for the loader
SetupProperties(const Importer *)130 void COBImporter::SetupProperties(const Importer* /*pImp*/)
131 {
132     // nothing to be done for the moment
133 }
134 
135 // ------------------------------------------------------------------------------------------------
ThrowException(const std::string & msg)136 /*static*/ AI_WONT_RETURN void COBImporter::ThrowException(const std::string& msg)
137 {
138     throw DeadlyImportError("COB: "+msg);
139 }
140 
141 // ------------------------------------------------------------------------------------------------
142 // Imports the given file into the given scene structure.
InternReadFile(const std::string & pFile,aiScene * pScene,IOSystem * pIOHandler)143 void COBImporter::InternReadFile( const std::string& pFile,
144     aiScene* pScene, IOSystem* pIOHandler)
145 {
146     COB::Scene scene;
147     boost::scoped_ptr<StreamReaderLE> stream(new StreamReaderLE( pIOHandler->Open(pFile,"rb")) );
148 
149     // check header
150     char head[32];
151     stream->CopyAndAdvance(head,32);
152     if (strncmp(head,"Caligari ",9)) {
153         ThrowException("Could not found magic id: `Caligari`");
154     }
155 
156     DefaultLogger::get()->info("File format tag: "+std::string(head+9,6));
157     if (head[16]!='L') {
158         ThrowException("File is big-endian, which is not supported");
159     }
160 
161     // load data into intermediate structures
162     if (head[15]=='A') {
163         ReadAsciiFile(scene, stream.get());
164     }
165     else {
166         ReadBinaryFile(scene, stream.get());
167     }
168     if(scene.nodes.empty()) {
169         ThrowException("No nodes loaded");
170     }
171 
172     // sort faces by material indices
173     for_each(boost::shared_ptr< Node >& n,scene.nodes) {
174         if (n->type == Node::TYPE_MESH) {
175             Mesh& mesh = (Mesh&)(*n.get());
176             for_each(Face& f,mesh.faces) {
177                 mesh.temp_map[f.material].push_back(&f);
178             }
179         }
180     }
181 
182     // count meshes
183     for_each(boost::shared_ptr< Node >& n,scene.nodes) {
184         if (n->type == Node::TYPE_MESH) {
185             Mesh& mesh = (Mesh&)(*n.get());
186             if (mesh.vertex_positions.size() && mesh.texture_coords.size()) {
187                 pScene->mNumMeshes += mesh.temp_map.size();
188             }
189         }
190     }
191     pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]();
192     pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes]();
193     pScene->mNumMeshes = 0;
194 
195     // count lights and cameras
196     for_each(boost::shared_ptr< Node >& n,scene.nodes) {
197         if (n->type == Node::TYPE_LIGHT) {
198             ++pScene->mNumLights;
199         }
200         else if (n->type == Node::TYPE_CAMERA) {
201             ++pScene->mNumCameras;
202         }
203     }
204 
205     if (pScene->mNumLights) {
206         pScene->mLights  = new aiLight*[pScene->mNumLights]();
207     }
208     if (pScene->mNumCameras) {
209         pScene->mCameras = new aiCamera*[pScene->mNumCameras]();
210     }
211     pScene->mNumLights = pScene->mNumCameras = 0;
212 
213     // resolve parents by their IDs and build the output graph
214     boost::scoped_ptr<Node> root(new Group());
215     for(size_t n = 0; n < scene.nodes.size(); ++n) {
216         const Node& nn = *scene.nodes[n].get();
217         if(nn.parent_id==0) {
218             root->temp_children.push_back(&nn);
219         }
220 
221         for(size_t m = n; m < scene.nodes.size(); ++m) {
222             const Node& mm = *scene.nodes[m].get();
223             if (mm.parent_id == nn.id) {
224                 nn.temp_children.push_back(&mm);
225             }
226         }
227     }
228 
229     pScene->mRootNode = BuildNodes(*root.get(),scene,pScene);
230 }
231 
232 // ------------------------------------------------------------------------------------------------
ConvertTexture(boost::shared_ptr<Texture> tex,aiMaterial * out,aiTextureType type)233 void ConvertTexture(boost::shared_ptr< Texture > tex, aiMaterial* out, aiTextureType type)
234 {
235     const aiString path( tex->path );
236     out->AddProperty(&path,AI_MATKEY_TEXTURE(type,0));
237     out->AddProperty(&tex->transform,1,AI_MATKEY_UVTRANSFORM(type,0));
238 }
239 
240 // ------------------------------------------------------------------------------------------------
BuildNodes(const Node & root,const Scene & scin,aiScene * fill)241 aiNode* COBImporter::BuildNodes(const Node& root,const Scene& scin,aiScene* fill)
242 {
243     aiNode* nd = new aiNode();
244     nd->mName.Set(root.name);
245     nd->mTransformation = root.transform;
246 
247     // Note to everybody believing Voodoo is appropriate here:
248     // I know polymorphism, run as fast as you can ;-)
249     if (Node::TYPE_MESH == root.type) {
250         const Mesh& ndmesh = (const Mesh&)(root);
251         if (ndmesh.vertex_positions.size() && ndmesh.texture_coords.size()) {
252 
253             typedef std::pair<unsigned int,Mesh::FaceRefList> Entry;
254             for_each(const Entry& reflist,ndmesh.temp_map) {
255                 {   // create mesh
256                     size_t n = 0;
257                     for_each(Face* f, reflist.second) {
258                         n += f->indices.size();
259                     }
260                     if (!n) {
261                         continue;
262                     }
263                     aiMesh* outmesh = fill->mMeshes[fill->mNumMeshes++] = new aiMesh();
264                     ++nd->mNumMeshes;
265 
266                     outmesh->mVertices = new aiVector3D[n];
267                     outmesh->mTextureCoords[0] = new aiVector3D[n];
268 
269                     outmesh->mFaces = new aiFace[reflist.second.size()]();
270                     for_each(Face* f, reflist.second) {
271                         if (f->indices.empty()) {
272                             continue;
273                         }
274 
275                         aiFace& fout = outmesh->mFaces[outmesh->mNumFaces++];
276                         fout.mIndices = new unsigned int[f->indices.size()];
277 
278                         for_each(VertexIndex& v, f->indices) {
279                             if (v.pos_idx >= ndmesh.vertex_positions.size()) {
280                                 ThrowException("Position index out of range");
281                             }
282                             if (v.uv_idx >= ndmesh.texture_coords.size()) {
283                                 ThrowException("UV index out of range");
284                             }
285                             outmesh->mVertices[outmesh->mNumVertices] = ndmesh.vertex_positions[ v.pos_idx ];
286                             outmesh->mTextureCoords[0][outmesh->mNumVertices] = aiVector3D(
287                                 ndmesh.texture_coords[ v.uv_idx ].x,
288                                 ndmesh.texture_coords[ v.uv_idx ].y,
289                                 0.f
290                             );
291 
292                             fout.mIndices[fout.mNumIndices++] = outmesh->mNumVertices++;
293                         }
294                     }
295                     outmesh->mMaterialIndex = fill->mNumMaterials;
296                 }{  // create material
297                     const Material* min = NULL;
298                     for_each(const Material& m, scin.materials) {
299                         if (m.parent_id == ndmesh.id && m.matnum == reflist.first) {
300                             min = &m;
301                             break;
302                         }
303                     }
304                     boost::scoped_ptr<const Material> defmat;
305                     if(!min) {
306                         DefaultLogger::get()->debug(format()<<"Could not resolve material index "
307                             <<reflist.first<<" - creating default material for this slot");
308 
309                         defmat.reset(min=new Material());
310                     }
311 
312                     aiMaterial* mat = new aiMaterial();
313                     fill->mMaterials[fill->mNumMaterials++] = mat;
314 
315                     const aiString s(format("#mat_")<<fill->mNumMeshes<<"_"<<min->matnum);
316                     mat->AddProperty(&s,AI_MATKEY_NAME);
317 
318                     if(int tmp = ndmesh.draw_flags & Mesh::WIRED ? 1 : 0) {
319                         mat->AddProperty(&tmp,1,AI_MATKEY_ENABLE_WIREFRAME);
320                     }
321 
322                     {   int shader;
323                         switch(min->shader)
324                         {
325                         case Material::FLAT:
326                             shader = aiShadingMode_Gouraud;
327                             break;
328 
329                         case Material::PHONG:
330                             shader = aiShadingMode_Phong;
331                             break;
332 
333                         case Material::METAL:
334                             shader = aiShadingMode_CookTorrance;
335                             break;
336 
337                         default:
338                             ai_assert(false); // shouldn't be here
339                         }
340                         mat->AddProperty(&shader,1,AI_MATKEY_SHADING_MODEL);
341                         if(shader != aiShadingMode_Gouraud) {
342                             mat->AddProperty(&min->exp,1,AI_MATKEY_SHININESS);
343                         }
344                     }
345 
346                     mat->AddProperty(&min->ior,1,AI_MATKEY_REFRACTI);
347                     mat->AddProperty(&min->rgb,1,AI_MATKEY_COLOR_DIFFUSE);
348 
349                     aiColor3D c = aiColor3D(min->rgb)*min->ks;
350                     mat->AddProperty(&c,1,AI_MATKEY_COLOR_SPECULAR);
351 
352                     c = aiColor3D(min->rgb)*min->ka;
353                     mat->AddProperty(&c,1,AI_MATKEY_COLOR_AMBIENT);
354 
355                     // convert textures if some exist.
356                     if(min->tex_color) {
357                         ConvertTexture(min->tex_color,mat,aiTextureType_DIFFUSE);
358                     }
359                     if(min->tex_env) {
360                         ConvertTexture(min->tex_env  ,mat,aiTextureType_UNKNOWN);
361                     }
362                     if(min->tex_bump) {
363                         ConvertTexture(min->tex_bump ,mat,aiTextureType_HEIGHT);
364                     }
365                 }
366             }
367         }
368     }
369     else if (Node::TYPE_LIGHT == root.type) {
370         const Light& ndlight = (const Light&)(root);
371         aiLight* outlight = fill->mLights[fill->mNumLights++] = new aiLight();
372 
373         outlight->mName.Set(ndlight.name);
374         outlight->mColorDiffuse = outlight->mColorAmbient = outlight->mColorSpecular = ndlight.color;
375 
376         outlight->mAngleOuterCone = AI_DEG_TO_RAD(ndlight.angle);
377         outlight->mAngleInnerCone = AI_DEG_TO_RAD(ndlight.inner_angle);
378 
379         // XXX
380         outlight->mType = ndlight.ltype==Light::SPOT ? aiLightSource_SPOT : aiLightSource_DIRECTIONAL;
381     }
382     else if (Node::TYPE_CAMERA == root.type) {
383         const Camera& ndcam = (const Camera&)(root);
384         aiCamera* outcam = fill->mCameras[fill->mNumCameras++] = new aiCamera();
385 
386         outcam->mName.Set(ndcam.name);
387     }
388 
389     // add meshes
390     if (nd->mNumMeshes) { // mMeshes must be NULL if count is 0
391         nd->mMeshes = new unsigned int[nd->mNumMeshes];
392         for(unsigned int i = 0; i < nd->mNumMeshes;++i) {
393             nd->mMeshes[i] = fill->mNumMeshes-i-1;
394         }
395     }
396 
397     // add children recursively
398     nd->mChildren = new aiNode*[root.temp_children.size()]();
399     for_each(const Node* n, root.temp_children) {
400         (nd->mChildren[nd->mNumChildren++] = BuildNodes(*n,scin,fill))->mParent = nd;
401     }
402 
403     return nd;
404 }
405 
406 // ------------------------------------------------------------------------------------------------
407 // Read an ASCII file into the given scene data structure
ReadAsciiFile(Scene & out,StreamReaderLE * stream)408 void COBImporter::ReadAsciiFile(Scene& out, StreamReaderLE* stream)
409 {
410     ChunkInfo ci;
411     for(LineSplitter splitter(*stream);splitter;++splitter) {
412 
413         // add all chunks to be recognized here. /else ../ omitted intentionally.
414         if (splitter.match_start("PolH ")) {
415             ReadChunkInfo_Ascii(ci,splitter);
416             ReadPolH_Ascii(out,splitter,ci);
417         }
418         if (splitter.match_start("BitM ")) {
419             ReadChunkInfo_Ascii(ci,splitter);
420             ReadBitM_Ascii(out,splitter,ci);
421         }
422         if (splitter.match_start("Mat1 ")) {
423             ReadChunkInfo_Ascii(ci,splitter);
424             ReadMat1_Ascii(out,splitter,ci);
425         }
426         if (splitter.match_start("Grou ")) {
427             ReadChunkInfo_Ascii(ci,splitter);
428             ReadGrou_Ascii(out,splitter,ci);
429         }
430         if (splitter.match_start("Lght ")) {
431             ReadChunkInfo_Ascii(ci,splitter);
432             ReadLght_Ascii(out,splitter,ci);
433         }
434         if (splitter.match_start("Came ")) {
435             ReadChunkInfo_Ascii(ci,splitter);
436             ReadCame_Ascii(out,splitter,ci);
437         }
438         if (splitter.match_start("Bone ")) {
439             ReadChunkInfo_Ascii(ci,splitter);
440             ReadBone_Ascii(out,splitter,ci);
441         }
442         if (splitter.match_start("Chan ")) {
443             ReadChunkInfo_Ascii(ci,splitter);
444             ReadChan_Ascii(out,splitter,ci);
445         }
446         if (splitter.match_start("Unit ")) {
447             ReadChunkInfo_Ascii(ci,splitter);
448             ReadUnit_Ascii(out,splitter,ci);
449         }
450         if (splitter.match_start("END ")) {
451             // we don't need this, but I guess there is a reason this
452             // chunk has been implemented into COB for.
453             return;
454         }
455     }
456 }
457 
458 // ------------------------------------------------------------------------------------------------
ReadChunkInfo_Ascii(ChunkInfo & out,const LineSplitter & splitter)459 void COBImporter::ReadChunkInfo_Ascii(ChunkInfo& out, const LineSplitter& splitter)
460 {
461     const char* all_tokens[8];
462     splitter.get_tokens(all_tokens);
463 
464     out.version = (all_tokens[1][1]-'0')*100+(all_tokens[1][3]-'0')*10+(all_tokens[1][4]-'0');
465     out.id  = strtoul10(all_tokens[3]);
466     out.parent_id = strtoul10(all_tokens[5]);
467     out.size = strtol10(all_tokens[7]);
468 }
469 
470 // ------------------------------------------------------------------------------------------------
UnsupportedChunk_Ascii(LineSplitter & splitter,const ChunkInfo & nfo,const char * name)471 void COBImporter::UnsupportedChunk_Ascii(LineSplitter& splitter, const ChunkInfo& nfo, const char* name)
472 {
473     const std::string error = format("Encountered unsupported chunk: ") <<  name <<
474         " [version: "<<nfo.version<<", size: "<<nfo.size<<"]";
475 
476     // we can recover if the chunk size was specified.
477     if(nfo.size != static_cast<unsigned int>(-1)) {
478         DefaultLogger::get()->error(error);
479 
480         // (HACK) - our current position in the stream is the beginning of the
481         // head line of the next chunk. That's fine, but the caller is going
482         // to call ++ on `splitter`, which we need to swallow to avoid
483         // missing the next line.
484         splitter.get_stream().IncPtr(nfo.size);
485         splitter.swallow_next_increment();
486     }
487     else ThrowException(error);
488 }
489 
490 // ------------------------------------------------------------------------------------------------
LogWarn_Ascii(const LineSplitter & splitter,const format & message)491 void COBImporter::LogWarn_Ascii(const LineSplitter& splitter, const format& message)    {
492     LogWarn_Ascii(message << " [at line "<< splitter.get_index()<<"]");
493 }
494 
495 // ------------------------------------------------------------------------------------------------
LogError_Ascii(const LineSplitter & splitter,const format & message)496 void COBImporter::LogError_Ascii(const LineSplitter& splitter, const format& message)   {
497     LogError_Ascii(message << " [at line "<< splitter.get_index()<<"]");
498 }
499 
500 // ------------------------------------------------------------------------------------------------
LogInfo_Ascii(const LineSplitter & splitter,const format & message)501 void COBImporter::LogInfo_Ascii(const LineSplitter& splitter, const format& message)    {
502     LogInfo_Ascii(message << " [at line "<< splitter.get_index()<<"]");
503 }
504 
505 // ------------------------------------------------------------------------------------------------
LogDebug_Ascii(const LineSplitter & splitter,const format & message)506 void COBImporter::LogDebug_Ascii(const LineSplitter& splitter, const format& message)   {
507     LogDebug_Ascii(message << " [at line "<< splitter.get_index()<<"]");
508 }
509 
510 // ------------------------------------------------------------------------------------------------
LogWarn_Ascii(const Formatter::format & message)511 void COBImporter::LogWarn_Ascii(const Formatter::format& message)   {
512     DefaultLogger::get()->warn(std::string("COB: ")+=message);
513 }
514 
515 // ------------------------------------------------------------------------------------------------
LogError_Ascii(const Formatter::format & message)516 void COBImporter::LogError_Ascii(const Formatter::format& message)  {
517     DefaultLogger::get()->error(std::string("COB: ")+=message);
518 }
519 
520 // ------------------------------------------------------------------------------------------------
LogInfo_Ascii(const Formatter::format & message)521 void COBImporter::LogInfo_Ascii(const Formatter::format& message)   {
522     DefaultLogger::get()->info(std::string("COB: ")+=message);
523 }
524 
525 // ------------------------------------------------------------------------------------------------
LogDebug_Ascii(const Formatter::format & message)526 void COBImporter::LogDebug_Ascii(const Formatter::format& message)  {
527     DefaultLogger::get()->debug(std::string("COB: ")+=message);
528 }
529 
530 // ------------------------------------------------------------------------------------------------
ReadBasicNodeInfo_Ascii(Node & msh,LineSplitter & splitter,const ChunkInfo &)531 void COBImporter::ReadBasicNodeInfo_Ascii(Node& msh, LineSplitter& splitter, const ChunkInfo& /*nfo*/)
532 {
533     for(;splitter;++splitter) {
534         if (splitter.match_start("Name")) {
535             msh.name = std::string(splitter[1]);
536 
537             // make nice names by merging the dupe count
538             std::replace(msh.name.begin(),msh.name.end(),
539                 ',','_');
540         }
541         else if (splitter.match_start("Transform")) {
542             for(unsigned int y = 0; y < 4 && ++splitter; ++y) {
543                 const char* s = splitter->c_str();
544                 for(unsigned int x = 0; x < 4; ++x) {
545                     SkipSpaces(&s);
546                     msh.transform[y][x] = fast_atof(&s);
547                 }
548             }
549             // we need the transform chunk, so we won't return until we have it.
550             return;
551         }
552     }
553 }
554 
555 // ------------------------------------------------------------------------------------------------
556 template <typename T>
ReadFloat3Tuple_Ascii(T & fill,const char ** in)557 void COBImporter::ReadFloat3Tuple_Ascii(T& fill, const char** in)
558 {
559     const char* rgb = *in;
560     for(unsigned int i = 0; i < 3; ++i) {
561         SkipSpaces(&rgb);
562         if (*rgb == ',')++rgb;
563         SkipSpaces(&rgb);
564 
565         fill[i] = fast_atof(&rgb);
566     }
567     *in = rgb;
568 }
569 
570 // ------------------------------------------------------------------------------------------------
ReadMat1_Ascii(Scene & out,LineSplitter & splitter,const ChunkInfo & nfo)571 void COBImporter::ReadMat1_Ascii(Scene& out, LineSplitter& splitter, const ChunkInfo& nfo)
572 {
573     if(nfo.version > 8) {
574         return UnsupportedChunk_Ascii(splitter,nfo,"Mat1");
575     }
576 
577     ++splitter;
578     if (!splitter.match_start("mat# ")) {
579         LogWarn_Ascii(splitter,format()<<
580             "Expected `mat#` line in `Mat1` chunk "<<nfo.id);
581         return;
582     }
583 
584     out.materials.push_back(Material());
585     Material& mat = out.materials.back();
586     mat = nfo;
587 
588     mat.matnum = strtoul10(splitter[1]);
589     ++splitter;
590 
591     if (!splitter.match_start("shader: ")) {
592         LogWarn_Ascii(splitter,format()<<
593             "Expected `mat#` line in `Mat1` chunk "<<nfo.id);
594         return;
595     }
596     std::string shader = std::string(splitter[1]);
597     shader = shader.substr(0,shader.find_first_of(" \t"));
598 
599     if (shader == "metal") {
600         mat.shader = Material::METAL;
601     }
602     else if (shader == "phong") {
603         mat.shader = Material::PHONG;
604     }
605     else if (shader != "flat") {
606         LogWarn_Ascii(splitter,format()<<
607             "Unknown value for `shader` in `Mat1` chunk "<<nfo.id);
608     }
609 
610     ++splitter;
611     if (!splitter.match_start("rgb ")) {
612         LogWarn_Ascii(splitter,format()<<
613             "Expected `rgb` line in `Mat1` chunk "<<nfo.id);
614     }
615 
616     const char* rgb = splitter[1];
617     ReadFloat3Tuple_Ascii(mat.rgb,&rgb);
618 
619     ++splitter;
620     if (!splitter.match_start("alpha ")) {
621         LogWarn_Ascii(splitter,format()<<
622             "Expected `alpha` line in `Mat1` chunk "<<nfo.id);
623     }
624 
625     const char* tokens[10];
626     splitter.get_tokens(tokens);
627 
628     mat.alpha   = fast_atof( tokens[1] );
629     mat.ka      = fast_atof( tokens[3] );
630     mat.ks      = fast_atof( tokens[5] );
631     mat.exp     = fast_atof( tokens[7] );
632     mat.ior     = fast_atof( tokens[9] );
633 }
634 
635 // ------------------------------------------------------------------------------------------------
ReadUnit_Ascii(Scene & out,LineSplitter & splitter,const ChunkInfo & nfo)636 void COBImporter::ReadUnit_Ascii(Scene& out, LineSplitter& splitter, const ChunkInfo& nfo)
637 {
638     if(nfo.version > 1) {
639         return UnsupportedChunk_Ascii(splitter,nfo,"Unit");
640     }
641     ++splitter;
642     if (!splitter.match_start("Units ")) {
643         LogWarn_Ascii(splitter,format()<<
644             "Expected `Units` line in `Unit` chunk "<<nfo.id);
645         return;
646     }
647 
648     // parent chunks preceede their childs, so we should have the
649     // corresponding chunk already.
650     for_each(boost::shared_ptr< Node >& nd, out.nodes) {
651         if (nd->id == nfo.parent_id) {
652             const unsigned int t=strtoul10(splitter[1]);
653 
654             nd->unit_scale = t>=sizeof(units)/sizeof(units[0])?(
655                 LogWarn_Ascii(splitter,format()<<t<<" is not a valid value for `Units` attribute in `Unit chunk` "<<nfo.id)
656                 ,1.f):units[t];
657             return;
658         }
659     }
660     LogWarn_Ascii(splitter,format()<<"`Unit` chunk "<<nfo.id<<" is a child of "
661         <<nfo.parent_id<<" which does not exist");
662 }
663 
664 // ------------------------------------------------------------------------------------------------
ReadChan_Ascii(Scene &,LineSplitter & splitter,const ChunkInfo & nfo)665 void COBImporter::ReadChan_Ascii(Scene& /*out*/, LineSplitter& splitter, const ChunkInfo& nfo)
666 {
667     if(nfo.version > 8) {
668         return UnsupportedChunk_Ascii(splitter,nfo,"Chan");
669     }
670 }
671 
672 // ------------------------------------------------------------------------------------------------
ReadLght_Ascii(Scene & out,LineSplitter & splitter,const ChunkInfo & nfo)673 void COBImporter::ReadLght_Ascii(Scene& out, LineSplitter& splitter, const ChunkInfo& nfo)
674 {
675     if(nfo.version > 8) {
676         return UnsupportedChunk_Ascii(splitter,nfo,"Lght");
677     }
678 
679     out.nodes.push_back(boost::shared_ptr<Light>(new Light()));
680     Light& msh = (Light&)(*out.nodes.back().get());
681     msh = nfo;
682 
683     ReadBasicNodeInfo_Ascii(msh,++splitter,nfo);
684 
685     if (splitter.match_start("Infinite ")) {
686         msh.ltype = Light::INFINITE;
687     }
688     else if (splitter.match_start("Local ")) {
689         msh.ltype = Light::LOCAL;
690     }
691     else if (splitter.match_start("Spot ")) {
692         msh.ltype = Light::SPOT;
693     }
694     else {
695         LogWarn_Ascii(splitter,format()<<
696             "Unknown kind of light source in `Lght` chunk "<<nfo.id<<" : "<<*splitter);
697         msh.ltype = Light::SPOT;
698     }
699 
700     ++splitter;
701     if (!splitter.match_start("color ")) {
702         LogWarn_Ascii(splitter,format()<<
703             "Expected `color` line in `Lght` chunk "<<nfo.id);
704     }
705 
706     const char* rgb = splitter[1];
707     ReadFloat3Tuple_Ascii(msh.color ,&rgb);
708 
709     SkipSpaces(&rgb);
710     if (strncmp(rgb,"cone angle",10)) {
711         LogWarn_Ascii(splitter,format()<<
712             "Expected `cone angle` entity in `color` line in `Lght` chunk "<<nfo.id);
713     }
714     SkipSpaces(rgb+10,&rgb);
715     msh.angle = fast_atof(&rgb);
716 
717     SkipSpaces(&rgb);
718     if (strncmp(rgb,"inner angle",11)) {
719         LogWarn_Ascii(splitter,format()<<
720             "Expected `inner angle` entity in `color` line in `Lght` chunk "<<nfo.id);
721     }
722     SkipSpaces(rgb+11,&rgb);
723     msh.inner_angle = fast_atof(&rgb);
724 
725     // skip the rest for we can't handle this kind of physically-based lighting information.
726 }
727 
728 // ------------------------------------------------------------------------------------------------
ReadCame_Ascii(Scene & out,LineSplitter & splitter,const ChunkInfo & nfo)729 void COBImporter::ReadCame_Ascii(Scene& out, LineSplitter& splitter, const ChunkInfo& nfo)
730 {
731     if(nfo.version > 2) {
732         return UnsupportedChunk_Ascii(splitter,nfo,"Came");
733     }
734 
735     out.nodes.push_back(boost::shared_ptr<Camera>(new Camera()));
736     Camera& msh = (Camera&)(*out.nodes.back().get());
737     msh = nfo;
738 
739     ReadBasicNodeInfo_Ascii(msh,++splitter,nfo);
740 
741     // skip the next line, we don't know this differenciation between a
742     // standard camera and a panoramic camera.
743     ++splitter;
744 }
745 
746 // ------------------------------------------------------------------------------------------------
ReadBone_Ascii(Scene & out,LineSplitter & splitter,const ChunkInfo & nfo)747 void COBImporter::ReadBone_Ascii(Scene& out, LineSplitter& splitter, const ChunkInfo& nfo)
748 {
749     if(nfo.version > 5) {
750         return UnsupportedChunk_Ascii(splitter,nfo,"Bone");
751     }
752 
753     out.nodes.push_back(boost::shared_ptr<Bone>(new Bone()));
754     Bone& msh = (Bone&)(*out.nodes.back().get());
755     msh = nfo;
756 
757     ReadBasicNodeInfo_Ascii(msh,++splitter,nfo);
758 
759     // TODO
760 }
761 
762 // ------------------------------------------------------------------------------------------------
ReadGrou_Ascii(Scene & out,LineSplitter & splitter,const ChunkInfo & nfo)763 void COBImporter::ReadGrou_Ascii(Scene& out, LineSplitter& splitter, const ChunkInfo& nfo)
764 {
765     if(nfo.version > 1) {
766         return UnsupportedChunk_Ascii(splitter,nfo,"Grou");
767     }
768 
769     out.nodes.push_back(boost::shared_ptr<Group>(new Group()));
770     Group& msh = (Group&)(*out.nodes.back().get());
771     msh = nfo;
772 
773     ReadBasicNodeInfo_Ascii(msh,++splitter,nfo);
774 }
775 
776 // ------------------------------------------------------------------------------------------------
ReadPolH_Ascii(Scene & out,LineSplitter & splitter,const ChunkInfo & nfo)777 void COBImporter::ReadPolH_Ascii(Scene& out, LineSplitter& splitter, const ChunkInfo& nfo)
778 {
779     if(nfo.version > 8) {
780         return UnsupportedChunk_Ascii(splitter,nfo,"PolH");
781     }
782 
783     out.nodes.push_back(boost::shared_ptr<Mesh>(new Mesh()));
784     Mesh& msh = (Mesh&)(*out.nodes.back().get());
785     msh = nfo;
786 
787     ReadBasicNodeInfo_Ascii(msh,++splitter,nfo);
788 
789     // the chunk has a fixed order of components, but some are not interesting of us so
790     // we're just looking for keywords in arbitrary order. The end of the chunk is
791     // either the last `Face` or the `DrawFlags` attribute, depending on the format ver.
792     for(;splitter;++splitter) {
793         if (splitter.match_start("World Vertices")) {
794             const unsigned int cnt = strtoul10(splitter[2]);
795             msh.vertex_positions.resize(cnt);
796 
797             for(unsigned int cur = 0;cur < cnt && ++splitter;++cur) {
798                 const char* s = splitter->c_str();
799 
800                 aiVector3D& v = msh.vertex_positions[cur];
801 
802                 SkipSpaces(&s);
803                 v.x = fast_atof(&s);
804                 SkipSpaces(&s);
805                 v.y = fast_atof(&s);
806                 SkipSpaces(&s);
807                 v.z = fast_atof(&s);
808             }
809         }
810         else if (splitter.match_start("Texture Vertices")) {
811             const unsigned int cnt = strtoul10(splitter[2]);
812             msh.texture_coords.resize(cnt);
813 
814             for(unsigned int cur = 0;cur < cnt && ++splitter;++cur) {
815                 const char* s = splitter->c_str();
816 
817                 aiVector2D& v = msh.texture_coords[cur];
818 
819                 SkipSpaces(&s);
820                 v.x = fast_atof(&s);
821                 SkipSpaces(&s);
822                 v.y = fast_atof(&s);
823             }
824         }
825         else if (splitter.match_start("Faces")) {
826             const unsigned int cnt = strtoul10(splitter[1]);
827             msh.faces.reserve(cnt);
828 
829             for(unsigned int cur = 0; cur < cnt && ++splitter ;++cur) {
830                 if (splitter.match_start("Hole")) {
831                     LogWarn_Ascii(splitter,"Skipping unsupported `Hole` line");
832                     continue;
833                 }
834 
835                 if (!splitter.match_start("Face")) {
836                     ThrowException("Expected Face line");
837                 }
838 
839                 msh.faces.push_back(Face());
840                 Face& face = msh.faces.back();
841 
842                 face.indices.resize(strtoul10(splitter[2]));
843                 face.flags = strtoul10(splitter[4]);
844                 face.material = strtoul10(splitter[6]);
845 
846                 const char* s = (++splitter)->c_str();
847                 for(size_t i = 0; i < face.indices.size(); ++i) {
848                     if(!SkipSpaces(&s)) {
849                         ThrowException("Expected EOL token in Face entry");
850                     }
851                     if ('<' != *s++) {
852                         ThrowException("Expected < token in Face entry");
853                     }
854                     face.indices[i].pos_idx = strtoul10(s,&s);
855                     if (',' != *s++) {
856                         ThrowException("Expected , token in Face entry");
857                     }
858                     face.indices[i].uv_idx = strtoul10(s,&s);
859                     if ('>' != *s++) {
860                         ThrowException("Expected < token in Face entry");
861                     }
862                 }
863             }
864             if (nfo.version <= 4) {
865                 break;
866             }
867         }
868         else if (splitter.match_start("DrawFlags")) {
869             msh.draw_flags = strtoul10(splitter[1]);
870             break;
871         }
872     }
873 }
874 
875 // ------------------------------------------------------------------------------------------------
ReadBitM_Ascii(Scene &,LineSplitter & splitter,const ChunkInfo & nfo)876 void COBImporter::ReadBitM_Ascii(Scene& /*out*/, LineSplitter& splitter, const ChunkInfo& nfo)
877 {
878     if(nfo.version > 1) {
879         return UnsupportedChunk_Ascii(splitter,nfo,"BitM");
880     }
881 /*
882     "\nThumbNailHdrSize %ld"
883     "\nThumbHeader: %02hx 02hx %02hx "
884     "\nColorBufSize %ld"
885     "\nColorBufZipSize %ld"
886     "\nZippedThumbnail: %02hx 02hx %02hx "
887 */
888 
889     const unsigned int head = strtoul10((++splitter)[1]);
890     if (head != sizeof(Bitmap::BitmapHeader)) {
891         LogWarn_Ascii(splitter,"Unexpected ThumbNailHdrSize, skipping this chunk");
892         return;
893     }
894 
895     /*union {
896         Bitmap::BitmapHeader data;
897         char opaq[sizeof Bitmap::BitmapHeader()];
898     };*/
899 //  ReadHexOctets(opaq,head,(++splitter)[1]);
900 }
901 
902 // ------------------------------------------------------------------------------------------------
ReadString_Binary(std::string & out,StreamReaderLE & reader)903 void COBImporter::ReadString_Binary(std::string& out, StreamReaderLE& reader)
904 {
905     out.resize( reader.GetI2());
906     for_each(char& c,out) {
907         c = reader.GetI1();
908     }
909 }
910 
911 // ------------------------------------------------------------------------------------------------
ReadBasicNodeInfo_Binary(Node & msh,StreamReaderLE & reader,const ChunkInfo &)912 void COBImporter::ReadBasicNodeInfo_Binary(Node& msh, StreamReaderLE& reader, const ChunkInfo& /*nfo*/)
913 {
914     const unsigned int dupes = reader.GetI2();
915     ReadString_Binary(msh.name,reader);
916 
917     msh.name = format(msh.name)<<'_'<<dupes;
918 
919     // skip local axes for the moment
920     reader.IncPtr(48);
921 
922     msh.transform = aiMatrix4x4();
923     for(unsigned int y = 0; y < 3; ++y) {
924         for(unsigned int x =0; x < 4; ++x) {
925             msh.transform[y][x] = reader.GetF4();
926         }
927     }
928 }
929 
930 // ------------------------------------------------------------------------------------------------
UnsupportedChunk_Binary(StreamReaderLE & reader,const ChunkInfo & nfo,const char * name)931 void COBImporter::UnsupportedChunk_Binary( StreamReaderLE& reader, const ChunkInfo& nfo, const char* name)
932 {
933     const std::string error = format("Encountered unsupported chunk: ") <<  name <<
934         " [version: "<<nfo.version<<", size: "<<nfo.size<<"]";
935 
936     // we can recover if the chunk size was specified.
937     if(nfo.size != static_cast<unsigned int>(-1)) {
938         DefaultLogger::get()->error(error);
939         reader.IncPtr(nfo.size);
940     }
941     else ThrowException(error);
942 }
943 
944 // ------------------------------------------------------------------------------------------------
945 // tiny utility guard to aid me at staying within chunk boundaries.
946 class chunk_guard {
947 
948 public:
949 
chunk_guard(const COB::ChunkInfo & nfo,StreamReaderLE & reader)950     chunk_guard(const COB::ChunkInfo& nfo, StreamReaderLE& reader)
951         : nfo(nfo)
952         , reader(reader)
953         , cur(reader.GetCurrentPos())
954     {
955     }
956 
~chunk_guard()957     ~chunk_guard() {
958         // don't do anything if the size is not given
959         if(nfo.size != static_cast<unsigned int>(-1)) {
960             reader.IncPtr(static_cast<int>(nfo.size)-reader.GetCurrentPos()+cur);
961         }
962     }
963 
964 private:
965 
966     const COB::ChunkInfo& nfo;
967     StreamReaderLE& reader;
968     long cur;
969 };
970 
971 // ------------------------------------------------------------------------------------------------
ReadBinaryFile(Scene & out,StreamReaderLE * reader)972 void COBImporter::ReadBinaryFile(Scene& out, StreamReaderLE* reader)
973 {
974     while(1) {
975         std::string type;
976          type += reader -> GetI1()
977         ,type += reader -> GetI1()
978         ,type += reader -> GetI1()
979         ,type += reader -> GetI1()
980         ;
981 
982         ChunkInfo nfo;
983         nfo.version  = reader -> GetI2()*10;
984         nfo.version += reader -> GetI2();
985 
986         nfo.id = reader->GetI4();
987         nfo.parent_id = reader->GetI4();
988         nfo.size = reader->GetI4();
989 
990         if (type == "PolH") {
991             ReadPolH_Binary(out,*reader,nfo);
992         }
993         else if (type == "BitM") {
994             ReadBitM_Binary(out,*reader,nfo);
995         }
996         else if (type == "Grou") {
997             ReadGrou_Binary(out,*reader,nfo);
998         }
999         else if (type == "Lght") {
1000             ReadLght_Binary(out,*reader,nfo);
1001         }
1002         else if (type == "Came") {
1003             ReadCame_Binary(out,*reader,nfo);
1004         }
1005         else if (type == "Mat1") {
1006             ReadMat1_Binary(out,*reader,nfo);
1007         }
1008     /*  else if (type == "Bone") {
1009             ReadBone_Binary(out,*reader,nfo);
1010         }
1011         else if (type == "Chan") {
1012             ReadChan_Binary(out,*reader,nfo);
1013         }*/
1014         else if (type == "Unit") {
1015             ReadUnit_Binary(out,*reader,nfo);
1016         }
1017         else if (type == "OLay") {
1018             // ignore layer index silently.
1019             if(nfo.size != static_cast<unsigned int>(-1) ) {
1020                 reader->IncPtr(nfo.size);
1021             }
1022             else return UnsupportedChunk_Binary(*reader,nfo,type.c_str());
1023         }
1024         else if (type == "END ") {
1025             return;
1026         }
1027         else UnsupportedChunk_Binary(*reader,nfo,type.c_str());
1028     }
1029 }
1030 
1031 // ------------------------------------------------------------------------------------------------
ReadPolH_Binary(COB::Scene & out,StreamReaderLE & reader,const ChunkInfo & nfo)1032 void COBImporter::ReadPolH_Binary(COB::Scene& out, StreamReaderLE& reader, const ChunkInfo& nfo)
1033 {
1034     if(nfo.version > 8) {
1035         return UnsupportedChunk_Binary(reader,nfo,"PolH");
1036     }
1037     const chunk_guard cn(nfo,reader);
1038 
1039     out.nodes.push_back(boost::shared_ptr<Mesh>(new Mesh()));
1040     Mesh& msh = (Mesh&)(*out.nodes.back().get());
1041     msh = nfo;
1042 
1043     ReadBasicNodeInfo_Binary(msh,reader,nfo);
1044 
1045     msh.vertex_positions.resize(reader.GetI4());
1046     for_each(aiVector3D& v,msh.vertex_positions) {
1047         v.x = reader.GetF4();
1048         v.y = reader.GetF4();
1049         v.z = reader.GetF4();
1050     }
1051 
1052     msh.texture_coords.resize(reader.GetI4());
1053     for_each(aiVector2D& v,msh.texture_coords) {
1054         v.x = reader.GetF4();
1055         v.y = reader.GetF4();
1056     }
1057 
1058     const size_t numf = reader.GetI4();
1059     msh.faces.reserve(numf);
1060     for(size_t i = 0; i < numf; ++i) {
1061         // XXX backface culling flag is 0x10 in flags
1062 
1063         // hole?
1064         bool hole;
1065         if ((hole = (reader.GetI1() & 0x08) != 0)) {
1066             // XXX Basically this should just work fine - then triangulator
1067             // should output properly triangulated data even for polygons
1068             // with holes. Test data specific to COB is needed to confirm it.
1069             if (msh.faces.empty()) {
1070                 ThrowException(format("A hole is the first entity in the `PolH` chunk with id ") << nfo.id);
1071             }
1072         }
1073         else msh.faces.push_back(Face());
1074         Face& f = msh.faces.back();
1075 
1076         const size_t num = reader.GetI2();
1077         f.indices.reserve(f.indices.size() + num);
1078 
1079         if(!hole) {
1080             f.material = reader.GetI2();
1081             f.flags = 0;
1082         }
1083 
1084         for(size_t x = 0; x < num; ++x) {
1085             f.indices.push_back(VertexIndex());
1086 
1087             VertexIndex& v = f.indices.back();
1088             v.pos_idx = reader.GetI4();
1089             v.uv_idx = reader.GetI4();
1090         }
1091 
1092         if(hole) {
1093             std::reverse(f.indices.rbegin(),f.indices.rbegin()+num);
1094         }
1095     }
1096     if (nfo.version>4) {
1097         msh.draw_flags = reader.GetI4();
1098     }
1099     nfo.version>5 && nfo.version<8 ? reader.GetI4() : 0;
1100 }
1101 
1102 // ------------------------------------------------------------------------------------------------
ReadBitM_Binary(COB::Scene &,StreamReaderLE & reader,const ChunkInfo & nfo)1103 void COBImporter::ReadBitM_Binary(COB::Scene& /*out*/, StreamReaderLE& reader, const ChunkInfo& nfo)
1104 {
1105     if(nfo.version > 1) {
1106         return UnsupportedChunk_Binary(reader,nfo,"BitM");
1107     }
1108 
1109     const chunk_guard cn(nfo,reader);
1110 
1111     const uint32_t len = reader.GetI4();
1112     reader.IncPtr(len);
1113 
1114     reader.GetI4();
1115     reader.IncPtr(reader.GetI4());
1116 }
1117 
1118 // ------------------------------------------------------------------------------------------------
ReadMat1_Binary(COB::Scene & out,StreamReaderLE & reader,const ChunkInfo & nfo)1119 void COBImporter::ReadMat1_Binary(COB::Scene& out, StreamReaderLE& reader, const ChunkInfo& nfo)
1120 {
1121     if(nfo.version > 8) {
1122         return UnsupportedChunk_Binary(reader,nfo,"Mat1");
1123     }
1124 
1125     const chunk_guard cn(nfo,reader);
1126 
1127     out.materials.push_back(Material());
1128     Material& mat = out.materials.back();
1129     mat = nfo;
1130 
1131     mat.matnum = reader.GetI2();
1132     switch(reader.GetI1()) {
1133         case 'f':
1134             mat.type = Material::FLAT;
1135             break;
1136         case 'p':
1137             mat.type = Material::PHONG;
1138             break;
1139         case 'm':
1140             mat.type = Material::METAL;
1141             break;
1142         default:
1143             LogError_Ascii(format("Unrecognized shader type in `Mat1` chunk with id ")<<nfo.id);
1144             mat.type = Material::FLAT;
1145     }
1146 
1147     switch(reader.GetI1()) {
1148         case 'f':
1149             mat.autofacet = Material::FACETED;
1150             break;
1151         case 'a':
1152             mat.autofacet = Material::AUTOFACETED;
1153             break;
1154         case 's':
1155             mat.autofacet = Material::SMOOTH;
1156             break;
1157         default:
1158             LogError_Ascii(format("Unrecognized faceting mode in `Mat1` chunk with id ")<<nfo.id);
1159             mat.autofacet = Material::FACETED;
1160     }
1161     mat.autofacet_angle = static_cast<float>(reader.GetI1());
1162 
1163     mat.rgb.r = reader.GetF4();
1164     mat.rgb.g = reader.GetF4();
1165     mat.rgb.b = reader.GetF4();
1166 
1167     mat.alpha = reader.GetF4();
1168     mat.ka    = reader.GetF4();
1169     mat.ks    = reader.GetF4();
1170     mat.exp   = reader.GetF4();
1171     mat.ior   = reader.GetF4();
1172 
1173     char id[2];
1174     id[0] = reader.GetI1(),id[1] = reader.GetI1();
1175 
1176     if (id[0] == 'e' && id[1] == ':') {
1177         mat.tex_env.reset(new Texture());
1178 
1179         reader.GetI1();
1180         ReadString_Binary(mat.tex_env->path,reader);
1181 
1182         // advance to next texture-id
1183         id[0] = reader.GetI1(),id[1] = reader.GetI1();
1184     }
1185 
1186     if (id[0] == 't' && id[1] == ':') {
1187         mat.tex_color.reset(new Texture());
1188 
1189         reader.GetI1();
1190         ReadString_Binary(mat.tex_color->path,reader);
1191 
1192         mat.tex_color->transform.mTranslation.x = reader.GetF4();
1193         mat.tex_color->transform.mTranslation.y = reader.GetF4();
1194 
1195         mat.tex_color->transform.mScaling.x = reader.GetF4();
1196         mat.tex_color->transform.mScaling.y = reader.GetF4();
1197 
1198         // advance to next texture-id
1199         id[0] = reader.GetI1(),id[1] = reader.GetI1();
1200     }
1201 
1202     if (id[0] == 'b' && id[1] == ':') {
1203         mat.tex_bump.reset(new Texture());
1204 
1205         reader.GetI1();
1206         ReadString_Binary(mat.tex_bump->path,reader);
1207 
1208         mat.tex_bump->transform.mTranslation.x = reader.GetF4();
1209         mat.tex_bump->transform.mTranslation.y = reader.GetF4();
1210 
1211         mat.tex_bump->transform.mScaling.x = reader.GetF4();
1212         mat.tex_bump->transform.mScaling.y = reader.GetF4();
1213 
1214         // skip amplitude for I don't know its purpose.
1215         reader.GetF4();
1216     }
1217     reader.IncPtr(-2);
1218 }
1219 
1220 // ------------------------------------------------------------------------------------------------
ReadCame_Binary(COB::Scene & out,StreamReaderLE & reader,const ChunkInfo & nfo)1221 void COBImporter::ReadCame_Binary(COB::Scene& out, StreamReaderLE& reader, const ChunkInfo& nfo)
1222 {
1223     if(nfo.version > 2) {
1224         return UnsupportedChunk_Binary(reader,nfo,"Came");
1225     }
1226 
1227     const chunk_guard cn(nfo,reader);
1228 
1229     out.nodes.push_back(boost::shared_ptr<Camera>(new Camera()));
1230     Camera& msh = (Camera&)(*out.nodes.back().get());
1231     msh = nfo;
1232 
1233     ReadBasicNodeInfo_Binary(msh,reader,nfo);
1234 
1235     // the rest is not interesting for us, so we skip over it.
1236     if(nfo.version > 1) {
1237         if (reader.GetI2()==512) {
1238             reader.IncPtr(42);
1239         }
1240     }
1241 }
1242 
1243 // ------------------------------------------------------------------------------------------------
ReadLght_Binary(COB::Scene & out,StreamReaderLE & reader,const ChunkInfo & nfo)1244 void COBImporter::ReadLght_Binary(COB::Scene& out, StreamReaderLE& reader, const ChunkInfo& nfo)
1245 {
1246     if(nfo.version > 2) {
1247         return UnsupportedChunk_Binary(reader,nfo,"Lght");
1248     }
1249 
1250     const chunk_guard cn(nfo,reader);
1251 
1252     out.nodes.push_back(boost::shared_ptr<Light>(new Light()));
1253     Light& msh = (Light&)(*out.nodes.back().get());
1254     msh = nfo;
1255 
1256     ReadBasicNodeInfo_Binary(msh,reader,nfo);
1257 }
1258 
1259 // ------------------------------------------------------------------------------------------------
ReadGrou_Binary(COB::Scene & out,StreamReaderLE & reader,const ChunkInfo & nfo)1260 void COBImporter::ReadGrou_Binary(COB::Scene& out, StreamReaderLE& reader, const ChunkInfo& nfo)
1261 {
1262     if(nfo.version > 2) {
1263         return UnsupportedChunk_Binary(reader,nfo,"Grou");
1264     }
1265 
1266     const chunk_guard cn(nfo,reader);
1267 
1268     out.nodes.push_back(boost::shared_ptr<Group>(new Group()));
1269     Group& msh = (Group&)(*out.nodes.back().get());
1270     msh = nfo;
1271 
1272     ReadBasicNodeInfo_Binary(msh,reader,nfo);
1273 }
1274 
1275 // ------------------------------------------------------------------------------------------------
ReadUnit_Binary(COB::Scene & out,StreamReaderLE & reader,const ChunkInfo & nfo)1276 void COBImporter::ReadUnit_Binary(COB::Scene& out, StreamReaderLE& reader, const ChunkInfo& nfo)
1277 {
1278      if(nfo.version > 1) {
1279         return UnsupportedChunk_Binary(reader,nfo,"Unit");
1280     }
1281 
1282      const chunk_guard cn(nfo,reader);
1283 
1284     // parent chunks preceede their childs, so we should have the
1285     // corresponding chunk already.
1286     for_each(boost::shared_ptr< Node >& nd, out.nodes) {
1287         if (nd->id == nfo.parent_id) {
1288             const unsigned int t=reader.GetI2();
1289             nd->unit_scale = t>=sizeof(units)/sizeof(units[0])?(
1290                 LogWarn_Ascii(format()<<t<<" is not a valid value for `Units` attribute in `Unit chunk` "<<nfo.id)
1291                 ,1.f):units[t];
1292 
1293             return;
1294         }
1295     }
1296     LogWarn_Ascii(format()<<"`Unit` chunk "<<nfo.id<<" is a child of "
1297         <<nfo.parent_id<<" which does not exist");
1298 }
1299 
1300 
1301 #endif
1302