1/*
2---------------------------------------------------------------------------
3Open Asset Import Library (assimp)
4---------------------------------------------------------------------------
5
6Copyright (c) 2006-2017, assimp team
7
8
9All rights reserved.
10
11Redistribution and use of this software in source and binary forms,
12with or without modification, are permitted provided that the following
13conditions are met:
14
15* Redistributions of source code must retain the above
16 copyright notice, this list of conditions and the
17 following disclaimer.
18
19* Redistributions in binary form must reproduce the above
20 copyright notice, this list of conditions and the
21 following disclaimer in the documentation and/or other
22 materials provided with the distribution.
23
24* Neither the name of the assimp team, nor the names of its
25 contributors may be used to endorse or promote products
26 derived from this software without specific prior
27 written permission of the assimp team.
28
29THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40---------------------------------------------------------------------------
41*/
42
43/** @file SIBImporter.cpp
44 * @brief Implementation of the SIB importer class.
45 *
46 * The Nevercenter Silo SIB format is undocumented.
47 * All details here have been reverse engineered from
48 * studying the binary files output by Silo.
49 *
50 * Nevertheless, this implementation is reasonably complete.
51 */
52
53
54#ifndef ASSIMP_BUILD_NO_SIB_IMPORTER
55
56// internal headers
57#include "SIBImporter.h"
58#include "ByteSwapper.h"
59#include "StreamReader.h"
60#include "TinyFormatter.h"
61//#include "../contrib/ConvertUTF/ConvertUTF.h"
62#include "../contrib/utf8cpp/source/utf8.h"
63#include <assimp/IOSystem.hpp>
64#include <assimp/DefaultLogger.hpp>
65#include <assimp/scene.h>
66#include <assimp/importerdesc.h>
67
68#include <map>
69
70using namespace Assimp;
71
72static const aiImporterDesc desc = {
73 "Silo SIB Importer",
74 "Richard Mitton (http://www.codersnotes.com/about)",
75 "",
76 "Does not apply subdivision.",
77 aiImporterFlags_SupportBinaryFlavour,
78 0, 0,
79 0, 0,
80 "sib"
81};
82
83struct SIBChunk {
84 uint32_t Tag;
85 uint32_t Size;
86} PACK_STRUCT;
87
88enum {
89 POS,
90 NRM,
91 UV,
92 N
93};
94
95typedef std::pair<uint32_t, uint32_t> SIBPair;
96
97struct SIBEdge {
98 uint32_t faceA, faceB;
99 bool creased;
100};
101
102struct SIBMesh {
103 aiMatrix4x4 axis;
104 uint32_t numPts;
105 std::vector<aiVector3D> pos, nrm, uv;
106 std::vector<uint32_t> idx;
107 std::vector<uint32_t> faceStart;
108 std::vector<uint32_t> mtls;
109 std::vector<SIBEdge> edges;
110 std::map<SIBPair, uint32_t> edgeMap;
111};
112
113struct SIBObject {
114 aiString name;
115 aiMatrix4x4 axis;
116 size_t meshIdx, meshCount;
117};
118
119struct SIB {
120 std::vector<aiMaterial*> mtls;
121 std::vector<aiMesh*> meshes;
122 std::vector<aiLight*> lights;
123 std::vector<SIBObject> objs, insts;
124};
125
126// ------------------------------------------------------------------------------------------------
127static SIBEdge& GetEdge(SIBMesh* mesh, uint32_t posA, uint32_t posB) {
128 SIBPair pair = (posA < posB) ? SIBPair(posA, posB) : SIBPair(posB, posA);
129 std::map<SIBPair, uint32_t>::iterator it = mesh->edgeMap.find(pair);
130 if (it != mesh->edgeMap.end())
131 return mesh->edges[it->second];
132
133 SIBEdge edge;
134 edge.creased = false;
135 edge.faceA = edge.faceB = 0xffffffff;
136 mesh->edgeMap[pair] = static_cast<uint32_t>(mesh->edges.size());
137 mesh->edges.push_back(edge);
138 return mesh->edges.back();
139}
140
141// ------------------------------------------------------------------------------------------------
142// Helpers for reading chunked data.
143
144#define TAG(A,B,C,D) ((A << 24) | (B << 16) | (C << 8) | D)
145
146static SIBChunk ReadChunk(StreamReaderLE* stream)
147{
148 SIBChunk chunk;
149 chunk.Tag = stream->GetU4();
150 chunk.Size = stream->GetU4();
151 if (chunk.Size > stream->GetRemainingSizeToLimit())
152 DefaultLogger::get()->error("SIB: Chunk overflow");
153 ByteSwap::Swap4(&chunk.Tag);
154 return chunk;
155}
156
157static aiColor3D ReadColor(StreamReaderLE* stream)
158{
159 float r = stream->GetF4();
160 float g = stream->GetF4();
161 float b = stream->GetF4();
162 stream->GetU4(); // Colors have an unused(?) 4th component.
163 return aiColor3D(r, g, b);
164}
165
166static void UnknownChunk(StreamReaderLE* /*stream*/, const SIBChunk& chunk)
167{
168 char temp[5] = {
169 static_cast<char>(( chunk.Tag>>24 ) & 0xff),
170 static_cast<char>(( chunk.Tag>>16 ) & 0xff),
171 static_cast<char>(( chunk.Tag>>8 ) & 0xff),
172 static_cast<char>(chunk.Tag & 0xff), '\0'
173 };
174
175 DefaultLogger::get()->warn((Formatter::format(), "SIB: Skipping unknown '",temp,"' chunk."));
176}
177
178// Reads a UTF-16LE string and returns it at UTF-8.
179static aiString ReadString(StreamReaderLE *stream, uint32_t numWChars) {
180 if ( nullptr == stream || 0 == numWChars ) {
181 static const aiString empty;
182 return empty;
183 }
184
185 // Allocate buffers (max expansion is 1 byte -> 4 bytes for UTF-8)
186 std::vector<unsigned char> str;
187 str.reserve( numWChars * 4 + 1 );
188 uint16_t *temp = new uint16_t[ numWChars ];
189 for ( uint32_t n = 0; n < numWChars; ++n ) {
190 temp[ n ] = stream->GetU2();
191 }
192
193 // Convert it and NUL-terminate.
194 const uint16_t *start( temp ), *end( temp + numWChars );
195 utf8::utf16to8( start, end, back_inserter( str ) );
196 str[ str.size() - 1 ] = '\0';
197
198 // Return the final string.
199 aiString result = aiString((const char *)&str[0]);
200 delete[] temp;
201
202 return result;
203}
204
205// ------------------------------------------------------------------------------------------------
206// Constructor to be privately used by Importer
207SIBImporter::SIBImporter() {
208 // empty
209}
210
211// ------------------------------------------------------------------------------------------------
212// Destructor, private as well
213SIBImporter::~SIBImporter() {
214 // empty
215}
216
217// ------------------------------------------------------------------------------------------------
218// Returns whether the class can handle the format of the given file.
219bool SIBImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const {
220 return SimpleExtensionCheck(pFile, "sib");
221}
222
223// ------------------------------------------------------------------------------------------------
224const aiImporterDesc* SIBImporter::GetInfo () const {
225 return &desc;
226}
227
228// ------------------------------------------------------------------------------------------------
229static void ReadVerts(SIBMesh* mesh, StreamReaderLE* stream, uint32_t count) {
230 if ( nullptr == mesh || nullptr == stream ) {
231 return;
232 }
233
234 mesh->pos.resize(count);
235 for ( uint32_t n=0; n<count; ++n ) {
236 mesh->pos[ n ].x = stream->GetF4();
237 mesh->pos[ n ].y = stream->GetF4();
238 mesh->pos[ n ].z = stream->GetF4();
239 }
240}
241
242// ------------------------------------------------------------------------------------------------
243static void ReadFaces(SIBMesh* mesh, StreamReaderLE* stream)
244{
245 uint32_t ptIdx = 0;
246 while (stream->GetRemainingSizeToLimit() > 0)
247 {
248 uint32_t numPoints = stream->GetU4();
249
250 // Store room for the N index channels, plus the point count.
251 size_t pos = mesh->idx.size() + 1;
252 mesh->idx.resize(pos + numPoints*N);
253 mesh->idx[pos-1] = numPoints;
254 uint32_t *idx = &mesh->idx[pos];
255
256 mesh->faceStart.push_back(static_cast<uint32_t>(pos-1));
257 mesh->mtls.push_back(0);
258
259 // Read all the position data.
260 // UV/normals will be supplied later.
261 // Positions are supplied indexed already, so we preserve that
262 // mapping. UVs are supplied uniquely, so we allocate unique indices.
263 for (uint32_t n=0;n<numPoints;n++,idx+=N,ptIdx++)
264 {
265 uint32_t p = stream->GetU4();
266 if (p >= mesh->pos.size())
267 throw DeadlyImportError("Vertex index is out of range.");
268 idx[POS] = p;
269 idx[NRM] = ptIdx;
270 idx[UV] = ptIdx;
271 }
272 }
273
274 // Allocate data channels for normals/UVs.
275 mesh->nrm.resize(ptIdx, aiVector3D(0,0,0));
276 mesh->uv.resize(ptIdx, aiVector3D(0,0,0));
277
278 mesh->numPts = ptIdx;
279}
280
281// ------------------------------------------------------------------------------------------------
282static void ReadUVs(SIBMesh* mesh, StreamReaderLE* stream)
283{
284 while (stream->GetRemainingSizeToLimit() > 0)
285 {
286 uint32_t faceIdx = stream->GetU4();
287 uint32_t numPoints = stream->GetU4();
288
289 if (faceIdx >= mesh->faceStart.size())
290 throw DeadlyImportError("Invalid face index.");
291
292 uint32_t pos = mesh->faceStart[faceIdx];
293 uint32_t *idx = &mesh->idx[pos + 1];
294
295 for (uint32_t n=0;n<numPoints;n++,idx+=N)
296 {
297 uint32_t id = idx[UV];
298 mesh->uv[id].x = stream->GetF4();
299 mesh->uv[id].y = stream->GetF4();
300 }
301 }
302}
303
304// ------------------------------------------------------------------------------------------------
305static void ReadMtls(SIBMesh* mesh, StreamReaderLE* stream)
306{
307 // Material assignments are stored run-length encoded.
308 // Also, we add 1 to each material so that we can use mtl #0
309 // as the default material.
310 uint32_t prevFace = stream->GetU4();
311 uint32_t prevMtl = stream->GetU4() + 1;
312 while (stream->GetRemainingSizeToLimit() > 0)
313 {
314 uint32_t face = stream->GetU4();
315 uint32_t mtl = stream->GetU4() + 1;
316 while (prevFace < face)
317 {
318 if (prevFace >= mesh->mtls.size())
319 throw DeadlyImportError("Invalid face index.");
320 mesh->mtls[prevFace++] = prevMtl;
321 }
322
323 prevFace = face;
324 prevMtl = mtl;
325 }
326
327 while (prevFace < mesh->mtls.size())
328 mesh->mtls[prevFace++] = prevMtl;
329}
330
331// ------------------------------------------------------------------------------------------------
332static void ReadAxis(aiMatrix4x4& axis, StreamReaderLE* stream)
333{
334 axis.a4 = stream->GetF4();
335 axis.b4 = stream->GetF4();
336 axis.c4 = stream->GetF4();
337 axis.d4 = 1;
338 axis.a1 = stream->GetF4();
339 axis.b1 = stream->GetF4();
340 axis.c1 = stream->GetF4();
341 axis.d1 = 0;
342 axis.a2 = stream->GetF4();
343 axis.b2 = stream->GetF4();
344 axis.c2 = stream->GetF4();
345 axis.d2 = 0;
346 axis.a3 = stream->GetF4();
347 axis.b3 = stream->GetF4();
348 axis.c3 = stream->GetF4();
349 axis.d3 = 0;
350}
351
352// ------------------------------------------------------------------------------------------------
353static void ReadEdges(SIBMesh* mesh, StreamReaderLE* stream)
354{
355 while (stream->GetRemainingSizeToLimit() > 0)
356 {
357 uint32_t posA = stream->GetU4();
358 uint32_t posB = stream->GetU4();
359 GetEdge(mesh, posA, posB);
360 }
361}
362
363// ------------------------------------------------------------------------------------------------
364static void ReadCreases(SIBMesh* mesh, StreamReaderLE* stream)
365{
366 while (stream->GetRemainingSizeToLimit() > 0)
367 {
368 uint32_t edge = stream->GetU4();
369 if (edge >= mesh->edges.size())
370 throw DeadlyImportError("SIB: Invalid edge index.");
371 mesh->edges[edge].creased = true;
372 }
373}
374
375// ------------------------------------------------------------------------------------------------
376static void ConnectFaces(SIBMesh* mesh)
377{
378 // Find faces connected to each edge.
379 size_t numFaces = mesh->faceStart.size();
380 for (size_t faceIdx=0;faceIdx<numFaces;faceIdx++)
381 {
382 uint32_t *idx = &mesh->idx[mesh->faceStart[faceIdx]];
383 uint32_t numPoints = *idx++;
384 uint32_t prev = idx[(numPoints-1)*N+POS];
385
386 for (uint32_t i=0;i<numPoints;i++,idx+=N)
387 {
388 uint32_t next = idx[POS];
389
390 // Find this edge.
391 SIBEdge& edge = GetEdge(mesh, prev, next);
392
393 // Link this face onto it.
394 // This gives potentially undesirable normals when used
395 // with non-2-manifold surfaces, but then so does Silo to begin with.
396 if (edge.faceA == 0xffffffff)
397 edge.faceA = static_cast<uint32_t>(faceIdx);
398 else if (edge.faceB == 0xffffffff)
399 edge.faceB = static_cast<uint32_t>(faceIdx);
400
401 prev = next;
402 }
403 }
404}
405
406// ------------------------------------------------------------------------------------------------
407static aiVector3D CalculateVertexNormal(SIBMesh* mesh, uint32_t faceIdx, uint32_t pos,
408 const std::vector<aiVector3D>& faceNormals)
409{
410 // Creased edges complicate this. We need to find the start/end range of the
411 // ring of faces that touch this position.
412 // We do this in two passes. The first pass is to find the end of the range,
413 // the second is to work backwards to the start and calculate the final normal.
414 aiVector3D vtxNormal;
415 for (int pass=0;pass<2;pass++)
416 {
417 vtxNormal = aiVector3D(0, 0, 0);
418 uint32_t startFaceIdx = faceIdx;
419 uint32_t prevFaceIdx = faceIdx;
420
421 // Process each connected face.
422 while (true)
423 {
424 // Accumulate the face normal.
425 vtxNormal += faceNormals[faceIdx];
426
427 uint32_t nextFaceIdx = 0xffffffff;
428
429 // Move to the next edge sharing this position.
430 uint32_t* idx = &mesh->idx[mesh->faceStart[faceIdx]];
431 uint32_t numPoints = *idx++;
432 uint32_t posA = idx[(numPoints-1)*N+POS];
433 for (uint32_t n=0;n<numPoints;n++,idx+=N)
434 {
435 uint32_t posB = idx[POS];
436
437 // Test if this edge shares our target position.
438 if (posA == pos || posB == pos)
439 {
440 SIBEdge& edge = GetEdge(mesh, posA, posB);
441
442 // Non-manifold meshes can produce faces which share
443 // positions but have no edge entry, so check it.
444 if (edge.faceA == faceIdx || edge.faceB == faceIdx)
445 {
446 // Move to whichever side we didn't just come from.
447 if (!edge.creased) {
448 if (edge.faceA != prevFaceIdx && edge.faceA != faceIdx && edge.faceA != 0xffffffff)
449 nextFaceIdx = edge.faceA;
450 else if (edge.faceB != prevFaceIdx && edge.faceB != faceIdx && edge.faceB != 0xffffffff)
451 nextFaceIdx = edge.faceB;
452 }
453 }
454 }
455
456 posA = posB;
457 }
458
459 // Stop once we hit either an creased/unconnected edge, or we
460 // wrapped around and hit our start point.
461 if (nextFaceIdx == 0xffffffff || nextFaceIdx == startFaceIdx)
462 break;
463
464 prevFaceIdx = faceIdx;
465 faceIdx = nextFaceIdx;
466 }
467 }
468
469 // Normalize it.
470 float len = vtxNormal.Length();
471 if (len > 0.000000001f)
472 vtxNormal /= len;
473 return vtxNormal;
474}
475
476// ------------------------------------------------------------------------------------------------
477static void CalculateNormals(SIBMesh* mesh)
478{
479 size_t numFaces = mesh->faceStart.size();
480
481 // Calculate face normals.
482 std::vector<aiVector3D> faceNormals(numFaces);
483 for (size_t faceIdx=0;faceIdx<numFaces;faceIdx++)
484 {
485 uint32_t* idx = &mesh->idx[mesh->faceStart[faceIdx]];
486 uint32_t numPoints = *idx++;
487
488 aiVector3D faceNormal(0, 0, 0);
489
490 uint32_t *prev = &idx[(numPoints-1)*N];
491
492 for (uint32_t i=0;i<numPoints;i++)
493 {
494 uint32_t *next = &idx[i*N];
495
496 faceNormal += mesh->pos[prev[POS]] ^ mesh->pos[next[POS]];
497 prev = next;
498 }
499
500 faceNormals[faceIdx] = faceNormal;
501 }
502
503 // Calculate vertex normals.
504 for (size_t faceIdx=0;faceIdx<numFaces;faceIdx++)
505 {
506 uint32_t* idx = &mesh->idx[mesh->faceStart[faceIdx]];
507 uint32_t numPoints = *idx++;
508
509 for (uint32_t i=0;i<numPoints;i++)
510 {
511 uint32_t pos = idx[i*N+POS];
512 uint32_t nrm = idx[i*N+NRM];
513 aiVector3D vtxNorm = CalculateVertexNormal(mesh, static_cast<uint32_t>(faceIdx), pos, faceNormals);
514 mesh->nrm[nrm] = vtxNorm;
515 }
516 }
517}
518
519// ------------------------------------------------------------------------------------------------
520struct TempMesh
521{
522 std::vector<aiVector3D> vtx;
523 std::vector<aiVector3D> nrm;
524 std::vector<aiVector3D> uv;
525 std::vector<aiFace> faces;
526};
527
528static void ReadShape(SIB* sib, StreamReaderLE* stream)
529{
530 SIBMesh smesh;
531 aiString name;
532
533 while (stream->GetRemainingSizeToLimit() >= sizeof(SIBChunk))
534 {
535 SIBChunk chunk = ReadChunk(stream);
536 unsigned oldLimit = stream->SetReadLimit(stream->GetCurrentPos() + chunk.Size);
537
538 switch (chunk.Tag)
539 {
540 case TAG('M','I','R','P'): break; // mirror plane maybe?
541 case TAG('I','M','R','P'): break; // instance mirror? (not supported here yet)
542 case TAG('D','I','N','F'): break; // display info, not needed
543 case TAG('P','I','N','F'): break; // ?
544 case TAG('V','M','I','R'): break; // ?
545 case TAG('F','M','I','R'): break; // ?
546 case TAG('T','X','S','M'): break; // ?
547 case TAG('F','A','H','S'): break; // ?
548 case TAG('V','R','T','S'): ReadVerts(&smesh, stream, chunk.Size/12); break;
549 case TAG('F','A','C','S'): ReadFaces(&smesh, stream); break;
550 case TAG('F','T','V','S'): ReadUVs(&smesh, stream); break;
551 case TAG('S','N','A','M'): name = ReadString(stream, chunk.Size/2); break;
552 case TAG('F','A','M','A'): ReadMtls(&smesh, stream); break;
553 case TAG('A','X','I','S'): ReadAxis(smesh.axis, stream); break;
554 case TAG('E','D','G','S'): ReadEdges(&smesh, stream); break;
555 case TAG('E','C','R','S'): ReadCreases(&smesh, stream); break;
556 default: UnknownChunk(stream, chunk); break;
557 }
558
559 stream->SetCurrentPos(stream->GetReadLimit());
560 stream->SetReadLimit(oldLimit);
561 }
562
563 ai_assert(smesh.faceStart.size() == smesh.mtls.size()); // sanity check
564
565 // Silo doesn't store any normals in the file - we need to compute
566 // them ourselves. We can't let AssImp handle it as AssImp doesn't
567 // know about our creased edges.
568 ConnectFaces(&smesh);
569 CalculateNormals(&smesh);
570
571 // Construct the transforms.
572 aiMatrix4x4 worldToLocal = smesh.axis;
573 worldToLocal.Inverse();
574 aiMatrix4x4 worldToLocalN = worldToLocal;
575 worldToLocalN.a4 = worldToLocalN.b4 = worldToLocalN.c4 = 0.0f;
576 worldToLocalN.Inverse().Transpose();
577
578 // Allocate final mesh data.
579 // We'll allocate one mesh for each material. (we'll strip unused ones after)
580 std::vector<TempMesh> meshes(sib->mtls.size());
581
582 // Un-index the source data and apply to each vertex.
583 for (unsigned fi=0;fi<smesh.faceStart.size();fi++)
584 {
585 uint32_t start = smesh.faceStart[fi];
586 uint32_t mtl = smesh.mtls[fi];
587 uint32_t *idx = &smesh.idx[start];
588
589 if (mtl >= meshes.size())
590 {
591 DefaultLogger::get()->error("SIB: Face material index is invalid.");
592 mtl = 0;
593 }
594
595 TempMesh& dest = meshes[mtl];
596
597 aiFace face;
598 face.mNumIndices = *idx++;
599 face.mIndices = new unsigned[face.mNumIndices];
600 for (unsigned pt=0;pt<face.mNumIndices;pt++,idx+=N)
601 {
602 size_t vtxIdx = dest.vtx.size();
603 face.mIndices[pt] = static_cast<unsigned int>(vtxIdx);
604
605 // De-index it. We don't need to validate here as
606 // we did it when creating the data.
607 aiVector3D pos = smesh.pos[idx[POS]];
608 aiVector3D nrm = smesh.nrm[idx[NRM]];
609 aiVector3D uv = smesh.uv[idx[UV]];
610
611 // The verts are supplied in world-space, so let's
612 // transform them back into the local space of this mesh:
613 pos = worldToLocal * pos;
614 nrm = worldToLocalN * nrm;
615
616 dest.vtx.push_back(pos);
617 dest.nrm.push_back(nrm);
618 dest.uv.push_back(uv);
619 }
620 dest.faces.push_back(face);
621 }
622
623 SIBObject obj;
624 obj.name = name;
625 obj.axis = smesh.axis;
626 obj.meshIdx = sib->meshes.size();
627
628 // Now that we know the size of everything,
629 // we can build the final one-material-per-mesh data.
630 for (size_t n=0;n<meshes.size();n++)
631 {
632 TempMesh& src = meshes[n];
633 if (src.faces.empty())
634 continue;
635
636 aiMesh* mesh = new aiMesh;
637 mesh->mName = name;
638 mesh->mNumFaces = static_cast<unsigned int>(src.faces.size());
639 mesh->mFaces = new aiFace[mesh->mNumFaces];
640 mesh->mNumVertices = static_cast<unsigned int>(src.vtx.size());
641 mesh->mVertices = new aiVector3D[mesh->mNumVertices];
642 mesh->mNormals = new aiVector3D[mesh->mNumVertices];
643 mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
644 mesh->mNumUVComponents[0] = 2;
645 mesh->mMaterialIndex = static_cast<unsigned int>(n);
646
647 for (unsigned i=0;i<mesh->mNumVertices;i++)
648 {
649 mesh->mVertices[i] = src.vtx[i];
650 mesh->mNormals[i] = src.nrm[i];
651 mesh->mTextureCoords[0][i] = src.uv[i];
652 }
653 for (unsigned i=0;i<mesh->mNumFaces;i++)
654 {
655 mesh->mFaces[i] = src.faces[i];
656 }
657
658 sib->meshes.push_back(mesh);
659 }
660
661 obj.meshCount = sib->meshes.size() - obj.meshIdx;
662 sib->objs.push_back(obj);
663}
664
665// ------------------------------------------------------------------------------------------------
666static void ReadMaterial(SIB* sib, StreamReaderLE* stream)
667{
668 aiColor3D diff = ReadColor(stream);
669 aiColor3D ambi = ReadColor(stream);
670 aiColor3D spec = ReadColor(stream);
671 aiColor3D emis = ReadColor(stream);
672 float shiny = (float)stream->GetU4();
673
674 uint32_t nameLen = stream->GetU4();
675 aiString name = ReadString(stream, nameLen/2);
676 uint32_t texLen = stream->GetU4();
677 aiString tex = ReadString(stream, texLen/2);
678
679 aiMaterial* mtl = new aiMaterial();
680 mtl->AddProperty(&diff, 1, AI_MATKEY_COLOR_DIFFUSE);
681 mtl->AddProperty(&ambi, 1, AI_MATKEY_COLOR_AMBIENT);
682 mtl->AddProperty(&spec, 1, AI_MATKEY_COLOR_SPECULAR);
683 mtl->AddProperty(&emis, 1, AI_MATKEY_COLOR_EMISSIVE);
684 mtl->AddProperty(&shiny, 1, AI_MATKEY_SHININESS);
685 mtl->AddProperty(&name, AI_MATKEY_NAME);
686 if (tex.length > 0) {
687 mtl->AddProperty(&tex, AI_MATKEY_TEXTURE_DIFFUSE(0));
688 mtl->AddProperty(&tex, AI_MATKEY_TEXTURE_AMBIENT(0));
689 }
690
691 sib->mtls.push_back(mtl);
692}
693
694// ------------------------------------------------------------------------------------------------
695static void ReadLightInfo(aiLight* light, StreamReaderLE* stream)
696{
697 uint32_t type = stream->GetU4();
698 switch (type) {
699 case 0: light->mType = aiLightSource_POINT; break;
700 case 1: light->mType = aiLightSource_SPOT; break;
701 case 2: light->mType = aiLightSource_DIRECTIONAL; break;
702 default: light->mType = aiLightSource_UNDEFINED; break;
703 }
704
705 light->mPosition.x = stream->GetF4();
706 light->mPosition.y = stream->GetF4();
707 light->mPosition.z = stream->GetF4();
708 light->mDirection.x = stream->GetF4();
709 light->mDirection.y = stream->GetF4();
710 light->mDirection.z = stream->GetF4();
711 light->mColorDiffuse = ReadColor(stream);
712 light->mColorAmbient = ReadColor(stream);
713 light->mColorSpecular = ReadColor(stream);
714 ai_real spotExponent = stream->GetF4();
715 ai_real spotCutoff = stream->GetF4();
716 light->mAttenuationConstant = stream->GetF4();
717 light->mAttenuationLinear = stream->GetF4();
718 light->mAttenuationQuadratic = stream->GetF4();
719
720 // Silo uses the OpenGL default lighting model for it's
721 // spot cutoff/exponent. AssImp unfortunately, does not.
722 // Let's try and approximate it by solving for the
723 // 99% and 1% percentiles.
724 // OpenGL: I = cos(angle)^E
725 // Solving: angle = acos(I^(1/E))
726 ai_real E = ai_real( 1.0 ) / std::max(spotExponent, (ai_real)0.00001);
727 ai_real inner = std::acos(std::pow((ai_real)0.99, E));
728 ai_real outer = std::acos(std::pow((ai_real)0.01, E));
729
730 // Apply the cutoff.
731 outer = std::min(outer, AI_DEG_TO_RAD(spotCutoff));
732
733 light->mAngleInnerCone = std::min(inner, outer);
734 light->mAngleOuterCone = outer;
735}
736
737static void ReadLight(SIB* sib, StreamReaderLE* stream)
738{
739 aiLight* light = new aiLight();
740
741 while (stream->GetRemainingSizeToLimit() >= sizeof(SIBChunk))
742 {
743 SIBChunk chunk = ReadChunk(stream);
744 unsigned oldLimit = stream->SetReadLimit(stream->GetCurrentPos() + chunk.Size);
745
746 switch (chunk.Tag)
747 {
748 case TAG('L','N','F','O'): ReadLightInfo(light, stream); break;
749 case TAG('S','N','A','M'): light->mName = ReadString(stream, chunk.Size/2); break;
750 default: UnknownChunk(stream, chunk); break;
751 }
752
753 stream->SetCurrentPos(stream->GetReadLimit());
754 stream->SetReadLimit(oldLimit);
755 }
756
757 sib->lights.push_back(light);
758}
759
760// ------------------------------------------------------------------------------------------------
761static void ReadScale(aiMatrix4x4& axis, StreamReaderLE* stream)
762{
763 aiMatrix4x4 scale;
764 scale.a1 = stream->GetF4();
765 scale.b1 = stream->GetF4();
766 scale.c1 = stream->GetF4();
767 scale.d1 = stream->GetF4();
768 scale.a2 = stream->GetF4();
769 scale.b2 = stream->GetF4();
770 scale.c2 = stream->GetF4();
771 scale.d2 = stream->GetF4();
772 scale.a3 = stream->GetF4();
773 scale.b3 = stream->GetF4();
774 scale.c3 = stream->GetF4();
775 scale.d3 = stream->GetF4();
776 scale.a4 = stream->GetF4();
777 scale.b4 = stream->GetF4();
778 scale.c4 = stream->GetF4();
779 scale.d4 = stream->GetF4();
780
781 axis = axis * scale;
782}
783
784static void ReadInstance(SIB* sib, StreamReaderLE* stream)
785{
786 SIBObject inst;
787 uint32_t shapeIndex = 0;
788
789 while (stream->GetRemainingSizeToLimit() >= sizeof(SIBChunk))
790 {
791 SIBChunk chunk = ReadChunk(stream);
792 unsigned oldLimit = stream->SetReadLimit(stream->GetCurrentPos() + chunk.Size);
793
794 switch (chunk.Tag)
795 {
796 case TAG('D','I','N','F'): break; // display info, not needed
797 case TAG('P','I','N','F'): break; // ?
798 case TAG('A','X','I','S'): ReadAxis(inst.axis, stream); break;
799 case TAG('I','N','S','I'): shapeIndex = stream->GetU4(); break;
800 case TAG('S','M','T','X'): ReadScale(inst.axis, stream); break;
801 case TAG('S','N','A','M'): inst.name = ReadString(stream, chunk.Size/2); break;
802 default: UnknownChunk(stream, chunk); break;
803 }
804
805 stream->SetCurrentPos(stream->GetReadLimit());
806 stream->SetReadLimit(oldLimit);
807 }
808
809 if ( shapeIndex >= sib->objs.size() ) {
810 throw DeadlyImportError( "SIB: Invalid shape index." );
811 }
812
813 const SIBObject& src = sib->objs[shapeIndex];
814 inst.meshIdx = src.meshIdx;
815 inst.meshCount = src.meshCount;
816 sib->insts.push_back(inst);
817}
818
819// ------------------------------------------------------------------------------------------------
820static void CheckVersion(StreamReaderLE* stream)
821{
822 uint32_t version = stream->GetU4();
823 if ( version < 1 || version > 2 ) {
824 throw DeadlyImportError( "SIB: Unsupported file version." );
825 }
826}
827
828static void ReadScene(SIB* sib, StreamReaderLE* stream)
829{
830 // Parse each chunk in turn.
831 while (stream->GetRemainingSizeToLimit() >= sizeof(SIBChunk))
832 {
833 SIBChunk chunk = ReadChunk(stream);
834 unsigned oldLimit = stream->SetReadLimit(stream->GetCurrentPos() + chunk.Size);
835
836 switch (chunk.Tag)
837 {
838 case TAG('H','E','A','D'): CheckVersion(stream); break;
839 case TAG('S','H','A','P'): ReadShape(sib, stream); break;
840 case TAG('G','R','P','S'): break; // group assignment, we don't import this
841 case TAG('T','E','X','P'): break; // ?
842 case TAG('I','N','S','T'): ReadInstance(sib, stream); break;
843 case TAG('M','A','T','R'): ReadMaterial(sib, stream); break;
844 case TAG('L','G','H','T'): ReadLight(sib, stream); break;
845 default: UnknownChunk(stream, chunk); break;
846 }
847
848 stream->SetCurrentPos(stream->GetReadLimit());
849 stream->SetReadLimit(oldLimit);
850 }
851}
852
853// ------------------------------------------------------------------------------------------------
854// Imports the given file into the given scene structure.
855void SIBImporter::InternReadFile(const std::string& pFile,
856 aiScene* pScene, IOSystem* pIOHandler)
857{
858 StreamReaderLE stream(pIOHandler->Open(pFile, "rb"));
859
860 // We should have at least one chunk
861 if (stream.GetRemainingSize() < 16)
862 throw DeadlyImportError("SIB file is either empty or corrupt: " + pFile);
863
864 SIB sib;
865
866 // Default material.
867 aiMaterial* defmtl = new aiMaterial;
868 aiString defname = aiString(AI_DEFAULT_MATERIAL_NAME);
869 defmtl->AddProperty(&defname, AI_MATKEY_NAME);
870 sib.mtls.push_back(defmtl);
871
872 // Read it all.
873 ReadScene(&sib, &stream);
874
875 // Join the instances and objects together.
876 size_t firstInst = sib.objs.size();
877 sib.objs.insert(sib.objs.end(), sib.insts.begin(), sib.insts.end());
878 sib.insts.clear();
879
880 // Transfer to the aiScene.
881 pScene->mNumMaterials = static_cast<unsigned int>(sib.mtls.size());
882 pScene->mNumMeshes = static_cast<unsigned int>(sib.meshes.size());
883 pScene->mNumLights = static_cast<unsigned int>(sib.lights.size());
884 pScene->mMaterials = pScene->mNumMaterials ? new aiMaterial*[pScene->mNumMaterials] : NULL;
885 pScene->mMeshes = pScene->mNumMeshes ? new aiMesh*[pScene->mNumMeshes] : NULL;
886 pScene->mLights = pScene->mNumLights ? new aiLight*[pScene->mNumLights] : NULL;
887 if (pScene->mNumMaterials)
888 memcpy(pScene->mMaterials, &sib.mtls[0], sizeof(aiMaterial*) * pScene->mNumMaterials);
889 if (pScene->mNumMeshes)
890 memcpy(pScene->mMeshes, &sib.meshes[0], sizeof(aiMesh*) * pScene->mNumMeshes);
891 if (pScene->mNumLights)
892 memcpy(pScene->mLights, &sib.lights[0], sizeof(aiLight*) * pScene->mNumLights);
893
894 // Construct the root node.
895 size_t childIdx = 0;
896 aiNode *root = new aiNode();
897 root->mName.Set("<SIBRoot>");
898 root->mNumChildren = static_cast<unsigned int>(sib.objs.size() + sib.lights.size());
899 root->mChildren = root->mNumChildren ? new aiNode*[root->mNumChildren] : NULL;
900 pScene->mRootNode = root;
901
902 // Add nodes for each object.
903 for (size_t n=0;n<sib.objs.size();n++)
904 {
905 ai_assert(root->mChildren);
906 SIBObject& obj = sib.objs[n];
907 aiNode* node = new aiNode;
908 root->mChildren[childIdx++] = node;
909 node->mName = obj.name;
910 node->mParent = root;
911 node->mTransformation = obj.axis;
912
913 node->mNumMeshes = static_cast<unsigned int>(obj.meshCount);
914 node->mMeshes = node->mNumMeshes ? new unsigned[node->mNumMeshes] : NULL;
915 for (unsigned i=0;i<node->mNumMeshes;i++)
916 node->mMeshes[i] = static_cast<unsigned int>(obj.meshIdx + i);
917
918 // Mark instanced objects as being so.
919 if (n >= firstInst)
920 {
921 node->mMetaData = aiMetadata::Alloc( 1 );
922 node->mMetaData->Set( 0, "IsInstance", true );
923 }
924 }
925
926 // Add nodes for each light.
927 // (no transformation as the light is already in world space)
928 for (size_t n=0;n<sib.lights.size();n++)
929 {
930 ai_assert(root->mChildren);
931 aiLight* light = sib.lights[n];
932 if ( nullptr != light ) {
933 aiNode* node = new aiNode;
934 root->mChildren[ childIdx++ ] = node;
935 node->mName = light->mName;
936 node->mParent = root;
937 }
938 }
939}
940
941#endif // !! ASSIMP_BUILD_NO_SIB_IMPORTER
942