vrshoot

view libs/assimp/COBLoader.cpp @ 0:b2f14e535253

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