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