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