1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5 
6 Copyright (c) 2006-2017, assimp team
7 
8 
9 All rights reserved.
10 
11 Redistribution and use of this software in source and binary forms,
12 with or without modification, are permitted provided that the following
13 conditions 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 
29 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39 OF 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 
70 using namespace Assimp;
71 
72 static 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 
83 struct SIBChunk {
84     uint32_t    Tag;
85     uint32_t    Size;
86 } PACK_STRUCT;
87 
88 enum {
89     POS,
90     NRM,
91     UV,
92     N
93 };
94 
95 typedef std::pair<uint32_t, uint32_t> SIBPair;
96 
97 struct SIBEdge {
98     uint32_t faceA, faceB;
99     bool creased;
100 };
101 
102 struct 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 
113 struct SIBObject {
114     aiString name;
115     aiMatrix4x4 axis;
116     size_t meshIdx, meshCount;
117 };
118 
119 struct 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 // ------------------------------------------------------------------------------------------------
GetEdge(SIBMesh * mesh,uint32_t posA,uint32_t posB)127 static 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 
ReadChunk(StreamReaderLE * stream)146 static 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 
ReadColor(StreamReaderLE * stream)157 static 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 
UnknownChunk(StreamReaderLE *,const SIBChunk & chunk)166 static 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.
ReadString(StreamReaderLE * stream,uint32_t numWChars)179 static 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
SIBImporter()207 SIBImporter::SIBImporter() {
208     // empty
209 }
210 
211 // ------------------------------------------------------------------------------------------------
212 // Destructor, private as well
~SIBImporter()213 SIBImporter::~SIBImporter() {
214     // empty
215 }
216 
217 // ------------------------------------------------------------------------------------------------
218 // Returns whether the class can handle the format of the given file.
CanRead(const std::string & pFile,IOSystem *,bool) const219 bool SIBImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const {
220     return SimpleExtensionCheck(pFile, "sib");
221 }
222 
223 // ------------------------------------------------------------------------------------------------
GetInfo() const224 const aiImporterDesc* SIBImporter::GetInfo () const {
225     return &desc;
226 }
227 
228 // ------------------------------------------------------------------------------------------------
ReadVerts(SIBMesh * mesh,StreamReaderLE * stream,uint32_t count)229 static 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 // ------------------------------------------------------------------------------------------------
ReadFaces(SIBMesh * mesh,StreamReaderLE * stream)243 static 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 // ------------------------------------------------------------------------------------------------
ReadUVs(SIBMesh * mesh,StreamReaderLE * stream)282 static 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 // ------------------------------------------------------------------------------------------------
ReadMtls(SIBMesh * mesh,StreamReaderLE * stream)305 static 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 // ------------------------------------------------------------------------------------------------
ReadAxis(aiMatrix4x4 & axis,StreamReaderLE * stream)332 static 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 // ------------------------------------------------------------------------------------------------
ReadEdges(SIBMesh * mesh,StreamReaderLE * stream)353 static 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 // ------------------------------------------------------------------------------------------------
ReadCreases(SIBMesh * mesh,StreamReaderLE * stream)364 static 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 // ------------------------------------------------------------------------------------------------
ConnectFaces(SIBMesh * mesh)376 static 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 // ------------------------------------------------------------------------------------------------
CalculateVertexNormal(SIBMesh * mesh,uint32_t faceIdx,uint32_t pos,const std::vector<aiVector3D> & faceNormals)407 static 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 // ------------------------------------------------------------------------------------------------
CalculateNormals(SIBMesh * mesh)477 static 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 // ------------------------------------------------------------------------------------------------
520 struct TempMesh
521 {
522     std::vector<aiVector3D> vtx;
523     std::vector<aiVector3D> nrm;
524     std::vector<aiVector3D> uv;
525     std::vector<aiFace>     faces;
526 };
527 
ReadShape(SIB * sib,StreamReaderLE * stream)528 static 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 // ------------------------------------------------------------------------------------------------
ReadMaterial(SIB * sib,StreamReaderLE * stream)666 static 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 // ------------------------------------------------------------------------------------------------
ReadLightInfo(aiLight * light,StreamReaderLE * stream)695 static 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 
ReadLight(SIB * sib,StreamReaderLE * stream)737 static 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 // ------------------------------------------------------------------------------------------------
ReadScale(aiMatrix4x4 & axis,StreamReaderLE * stream)761 static 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 
ReadInstance(SIB * sib,StreamReaderLE * stream)784 static 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 // ------------------------------------------------------------------------------------------------
CheckVersion(StreamReaderLE * stream)820 static 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 
ReadScene(SIB * sib,StreamReaderLE * stream)828 static 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.
InternReadFile(const std::string & pFile,aiScene * pScene,IOSystem * pIOHandler)855 void 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