summaryrefslogtreecommitdiff
path: root/libs/assimp/code/AssetLib/SIB/SIBImporter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/assimp/code/AssetLib/SIB/SIBImporter.cpp')
-rw-r--r--libs/assimp/code/AssetLib/SIB/SIBImporter.cpp889
1 files changed, 889 insertions, 0 deletions
diff --git a/libs/assimp/code/AssetLib/SIB/SIBImporter.cpp b/libs/assimp/code/AssetLib/SIB/SIBImporter.cpp
new file mode 100644
index 0000000..7b66afa
--- /dev/null
+++ b/libs/assimp/code/AssetLib/SIB/SIBImporter.cpp
@@ -0,0 +1,889 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2022, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file SIBImporter.cpp
+ * @brief Implementation of the SIB importer class.
+ *
+ * The Nevercenter Silo SIB format is undocumented.
+ * All details here have been reverse engineered from
+ * studying the binary files output by Silo.
+ *
+ * Nevertheless, this implementation is reasonably complete.
+ */
+
+#ifndef ASSIMP_BUILD_NO_SIB_IMPORTER
+
+// internal headers
+#include "SIBImporter.h"
+#include <assimp/ByteSwapper.h>
+#include <assimp/StreamReader.h>
+#include <assimp/TinyFormatter.h>
+#ifdef ASSIMP_USE_HUNTER
+#include <utf8.h>
+#else
+#include "../contrib/utf8cpp/source/utf8.h"
+#endif
+#include <assimp/importerdesc.h>
+#include <assimp/scene.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/IOSystem.hpp>
+#include <assimp/StringUtils.h>
+
+#include <map>
+
+using namespace Assimp;
+
+static const aiImporterDesc desc = {
+ "Silo SIB Importer",
+ "Richard Mitton (http://www.codersnotes.com/about)",
+ "",
+ "Does not apply subdivision.",
+ aiImporterFlags_SupportBinaryFlavour,
+ 0, 0,
+ 0, 0,
+ "sib"
+};
+
+struct SIBChunk {
+ uint32_t Tag;
+ uint32_t Size;
+} PACK_STRUCT;
+
+enum {
+ POS,
+ NRM,
+ UV,
+ N
+};
+
+typedef std::pair<uint32_t, uint32_t> SIBPair;
+
+struct SIBEdge {
+ uint32_t faceA, faceB;
+ bool creased;
+};
+
+struct SIBMesh {
+ aiMatrix4x4 axis;
+ uint32_t numPts;
+ std::vector<aiVector3D> pos, nrm, uv;
+ std::vector<uint32_t> idx;
+ std::vector<uint32_t> faceStart;
+ std::vector<uint32_t> mtls;
+ std::vector<SIBEdge> edges;
+ std::map<SIBPair, uint32_t> edgeMap;
+};
+
+struct SIBObject {
+ aiString name;
+ aiMatrix4x4 axis;
+ size_t meshIdx, meshCount;
+};
+
+struct SIB {
+ std::vector<aiMaterial *> mtls;
+ std::vector<aiMesh *> meshes;
+ std::vector<aiLight *> lights;
+ std::vector<SIBObject> objs, insts;
+};
+
+// ------------------------------------------------------------------------------------------------
+static SIBEdge &GetEdge(SIBMesh *mesh, uint32_t posA, uint32_t posB) {
+ SIBPair pair = (posA < posB) ? SIBPair(posA, posB) : SIBPair(posB, posA);
+ std::map<SIBPair, uint32_t>::iterator it = mesh->edgeMap.find(pair);
+ if (it != mesh->edgeMap.end())
+ return mesh->edges[it->second];
+
+ SIBEdge edge;
+ edge.creased = false;
+ edge.faceA = edge.faceB = 0xffffffff;
+ mesh->edgeMap[pair] = static_cast<uint32_t>(mesh->edges.size());
+ mesh->edges.push_back(edge);
+ return mesh->edges.back();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Helpers for reading chunked data.
+
+#define TAG(A, B, C, D) ((A << 24) | (B << 16) | (C << 8) | D)
+
+static SIBChunk ReadChunk(StreamReaderLE *stream) {
+ SIBChunk chunk;
+ chunk.Tag = stream->GetU4();
+ chunk.Size = stream->GetU4();
+ if (chunk.Size > stream->GetRemainingSizeToLimit())
+ ASSIMP_LOG_ERROR("SIB: Chunk overflow");
+ ByteSwap::Swap4(&chunk.Tag);
+ return chunk;
+}
+
+static aiColor3D ReadColor(StreamReaderLE *stream) {
+ float r = stream->GetF4();
+ float g = stream->GetF4();
+ float b = stream->GetF4();
+ stream->GetU4(); // Colors have an unused(?) 4th component.
+ return aiColor3D(r, g, b);
+}
+
+static void UnknownChunk(StreamReaderLE * /*stream*/, const SIBChunk &chunk) {
+ char temp[4] = {
+ static_cast<char>((chunk.Tag >> 24) & 0xff),
+ static_cast<char>((chunk.Tag >> 16) & 0xff),
+ static_cast<char>((chunk.Tag >> 8) & 0xff),
+ static_cast<char>(chunk.Tag & 0xff)
+ };
+
+ ASSIMP_LOG_WARN("SIB: Skipping unknown '", ai_str_toprintable(temp, 4), "' chunk.");
+}
+
+// Reads a UTF-16LE string and returns it at UTF-8.
+static aiString ReadString(StreamReaderLE *stream, uint32_t numWChars) {
+ if (nullptr == stream || 0 == numWChars) {
+ return aiString();
+ }
+
+ // Allocate buffers (max expansion is 1 byte -> 4 bytes for UTF-8)
+ std::vector<unsigned char> str;
+ str.reserve(numWChars * 4 + 1);
+ uint16_t *temp = new uint16_t[numWChars];
+ for (uint32_t n = 0; n < numWChars; ++n) {
+ temp[n] = stream->GetU2();
+ }
+
+ // Convert it and NUL-terminate.
+ const uint16_t *start(temp), *end(temp + numWChars);
+ utf8::utf16to8(start, end, back_inserter(str));
+ str[str.size() - 1] = '\0';
+
+ // Return the final string.
+ aiString result = aiString((const char *)&str[0]);
+ delete[] temp;
+
+ return result;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+SIBImporter::SIBImporter() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+SIBImporter::~SIBImporter() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool SIBImporter::CanRead(const std::string &filename, IOSystem * /*pIOHandler*/, bool /*checkSig*/) const {
+ return SimpleExtensionCheck(filename, "sib");
+}
+
+// ------------------------------------------------------------------------------------------------
+const aiImporterDesc *SIBImporter::GetInfo() const {
+ return &desc;
+}
+
+// ------------------------------------------------------------------------------------------------
+static void ReadVerts(SIBMesh *mesh, StreamReaderLE *stream, uint32_t count) {
+ if (nullptr == mesh || nullptr == stream) {
+ return;
+ }
+
+ mesh->pos.resize(count);
+ for (uint32_t n = 0; n < count; ++n) {
+ mesh->pos[n].x = stream->GetF4();
+ mesh->pos[n].y = stream->GetF4();
+ mesh->pos[n].z = stream->GetF4();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+static void ReadFaces(SIBMesh *mesh, StreamReaderLE *stream) {
+ uint32_t ptIdx = 0;
+ while (stream->GetRemainingSizeToLimit() > 0) {
+ uint32_t numPoints = stream->GetU4();
+
+ // Store room for the N index channels, plus the point count.
+ size_t pos = mesh->idx.size() + 1;
+ mesh->idx.resize(pos + numPoints * N);
+ mesh->idx[pos - 1] = numPoints;
+ uint32_t *idx = &mesh->idx[pos];
+
+ mesh->faceStart.push_back(static_cast<uint32_t>(pos - 1));
+ mesh->mtls.push_back(0);
+
+ // Read all the position data.
+ // UV/normals will be supplied later.
+ // Positions are supplied indexed already, so we preserve that
+ // mapping. UVs are supplied uniquely, so we allocate unique indices.
+ for (uint32_t n = 0; n < numPoints; n++, idx += N, ptIdx++) {
+ uint32_t p = stream->GetU4();
+ if (p >= mesh->pos.size())
+ throw DeadlyImportError("Vertex index is out of range.");
+ idx[POS] = p;
+ idx[NRM] = ptIdx;
+ idx[UV] = ptIdx;
+ }
+ }
+
+ // Allocate data channels for normals/UVs.
+ mesh->nrm.resize(ptIdx, aiVector3D(0, 0, 0));
+ mesh->uv.resize(ptIdx, aiVector3D(0, 0, 0));
+
+ mesh->numPts = ptIdx;
+}
+
+// ------------------------------------------------------------------------------------------------
+static void ReadUVs(SIBMesh *mesh, StreamReaderLE *stream) {
+ while (stream->GetRemainingSizeToLimit() > 0) {
+ uint32_t faceIdx = stream->GetU4();
+ uint32_t numPoints = stream->GetU4();
+
+ if (faceIdx >= mesh->faceStart.size())
+ throw DeadlyImportError("Invalid face index.");
+
+ uint32_t pos = mesh->faceStart[faceIdx];
+ uint32_t *idx = &mesh->idx[pos + 1];
+
+ for (uint32_t n = 0; n < numPoints; n++, idx += N) {
+ uint32_t id = idx[UV];
+ mesh->uv[id].x = stream->GetF4();
+ mesh->uv[id].y = stream->GetF4();
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+static void ReadMtls(SIBMesh *mesh, StreamReaderLE *stream) {
+ // Material assignments are stored run-length encoded.
+ // Also, we add 1 to each material so that we can use mtl #0
+ // as the default material.
+ uint32_t prevFace = stream->GetU4();
+ uint32_t prevMtl = stream->GetU4() + 1;
+ while (stream->GetRemainingSizeToLimit() > 0) {
+ uint32_t face = stream->GetU4();
+ uint32_t mtl = stream->GetU4() + 1;
+ while (prevFace < face) {
+ if (prevFace >= mesh->mtls.size())
+ throw DeadlyImportError("Invalid face index.");
+ mesh->mtls[prevFace++] = prevMtl;
+ }
+
+ prevFace = face;
+ prevMtl = mtl;
+ }
+
+ while (prevFace < mesh->mtls.size())
+ mesh->mtls[prevFace++] = prevMtl;
+}
+
+// ------------------------------------------------------------------------------------------------
+static void ReadAxis(aiMatrix4x4 &axis, StreamReaderLE *stream) {
+ axis.a4 = stream->GetF4();
+ axis.b4 = stream->GetF4();
+ axis.c4 = stream->GetF4();
+ axis.d4 = 1;
+ axis.a1 = stream->GetF4();
+ axis.b1 = stream->GetF4();
+ axis.c1 = stream->GetF4();
+ axis.d1 = 0;
+ axis.a2 = stream->GetF4();
+ axis.b2 = stream->GetF4();
+ axis.c2 = stream->GetF4();
+ axis.d2 = 0;
+ axis.a3 = stream->GetF4();
+ axis.b3 = stream->GetF4();
+ axis.c3 = stream->GetF4();
+ axis.d3 = 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+static void ReadEdges(SIBMesh *mesh, StreamReaderLE *stream) {
+ while (stream->GetRemainingSizeToLimit() > 0) {
+ uint32_t posA = stream->GetU4();
+ uint32_t posB = stream->GetU4();
+ GetEdge(mesh, posA, posB);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+static void ReadCreases(SIBMesh *mesh, StreamReaderLE *stream) {
+ while (stream->GetRemainingSizeToLimit() > 0) {
+ uint32_t edge = stream->GetU4();
+ if (edge >= mesh->edges.size())
+ throw DeadlyImportError("SIB: Invalid edge index.");
+ mesh->edges[edge].creased = true;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+static void ConnectFaces(SIBMesh *mesh) {
+ // Find faces connected to each edge.
+ size_t numFaces = mesh->faceStart.size();
+ for (size_t faceIdx = 0; faceIdx < numFaces; faceIdx++) {
+ uint32_t *idx = &mesh->idx[mesh->faceStart[faceIdx]];
+ uint32_t numPoints = *idx++;
+ uint32_t prev = idx[(numPoints - 1) * N + POS];
+
+ for (uint32_t i = 0; i < numPoints; i++, idx += N) {
+ uint32_t next = idx[POS];
+
+ // Find this edge.
+ SIBEdge &edge = GetEdge(mesh, prev, next);
+
+ // Link this face onto it.
+ // This gives potentially undesirable normals when used
+ // with non-2-manifold surfaces, but then so does Silo to begin with.
+ if (edge.faceA == 0xffffffff)
+ edge.faceA = static_cast<uint32_t>(faceIdx);
+ else if (edge.faceB == 0xffffffff)
+ edge.faceB = static_cast<uint32_t>(faceIdx);
+
+ prev = next;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+static aiVector3D CalculateVertexNormal(SIBMesh *mesh, uint32_t faceIdx, uint32_t pos,
+ const std::vector<aiVector3D> &faceNormals) {
+ // Creased edges complicate this. We need to find the start/end range of the
+ // ring of faces that touch this position.
+ // We do this in two passes. The first pass is to find the end of the range,
+ // the second is to work backwards to the start and calculate the final normal.
+ aiVector3D vtxNormal;
+ for (int pass = 0; pass < 2; pass++) {
+ vtxNormal = aiVector3D(0, 0, 0);
+ uint32_t startFaceIdx = faceIdx;
+ uint32_t prevFaceIdx = faceIdx;
+
+ // Process each connected face.
+ while (true) {
+ // Accumulate the face normal.
+ vtxNormal += faceNormals[faceIdx];
+
+ uint32_t nextFaceIdx = 0xffffffff;
+
+ // Move to the next edge sharing this position.
+ uint32_t *idx = &mesh->idx[mesh->faceStart[faceIdx]];
+ uint32_t numPoints = *idx++;
+ uint32_t posA = idx[(numPoints - 1) * N + POS];
+ for (uint32_t n = 0; n < numPoints; n++, idx += N) {
+ uint32_t posB = idx[POS];
+
+ // Test if this edge shares our target position.
+ if (posA == pos || posB == pos) {
+ SIBEdge &edge = GetEdge(mesh, posA, posB);
+
+ // Non-manifold meshes can produce faces which share
+ // positions but have no edge entry, so check it.
+ if (edge.faceA == faceIdx || edge.faceB == faceIdx) {
+ // Move to whichever side we didn't just come from.
+ if (!edge.creased) {
+ if (edge.faceA != prevFaceIdx && edge.faceA != faceIdx && edge.faceA != 0xffffffff)
+ nextFaceIdx = edge.faceA;
+ else if (edge.faceB != prevFaceIdx && edge.faceB != faceIdx && edge.faceB != 0xffffffff)
+ nextFaceIdx = edge.faceB;
+ }
+ }
+ }
+
+ posA = posB;
+ }
+
+ // Stop once we hit either an creased/unconnected edge, or we
+ // wrapped around and hit our start point.
+ if (nextFaceIdx == 0xffffffff || nextFaceIdx == startFaceIdx)
+ break;
+
+ prevFaceIdx = faceIdx;
+ faceIdx = nextFaceIdx;
+ }
+ }
+
+ // Normalize it.
+ float len = vtxNormal.Length();
+ if (len > 0.000000001f)
+ vtxNormal /= len;
+ return vtxNormal;
+}
+
+// ------------------------------------------------------------------------------------------------
+static void CalculateNormals(SIBMesh *mesh) {
+ size_t numFaces = mesh->faceStart.size();
+
+ // Calculate face normals.
+ std::vector<aiVector3D> faceNormals(numFaces);
+ for (size_t faceIdx = 0; faceIdx < numFaces; faceIdx++) {
+ uint32_t *idx = &mesh->idx[mesh->faceStart[faceIdx]];
+ uint32_t numPoints = *idx++;
+
+ aiVector3D faceNormal(0, 0, 0);
+
+ uint32_t *prev = &idx[(numPoints - 1) * N];
+
+ for (uint32_t i = 0; i < numPoints; i++) {
+ uint32_t *next = &idx[i * N];
+
+ faceNormal += mesh->pos[prev[POS]] ^ mesh->pos[next[POS]];
+ prev = next;
+ }
+
+ faceNormals[faceIdx] = faceNormal;
+ }
+
+ // Calculate vertex normals.
+ for (size_t faceIdx = 0; faceIdx < numFaces; faceIdx++) {
+ uint32_t *idx = &mesh->idx[mesh->faceStart[faceIdx]];
+ uint32_t numPoints = *idx++;
+
+ for (uint32_t i = 0; i < numPoints; i++) {
+ uint32_t pos = idx[i * N + POS];
+ uint32_t nrm = idx[i * N + NRM];
+ aiVector3D vtxNorm = CalculateVertexNormal(mesh, static_cast<uint32_t>(faceIdx), pos, faceNormals);
+ mesh->nrm[nrm] = vtxNorm;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+struct TempMesh {
+ std::vector<aiVector3D> vtx;
+ std::vector<aiVector3D> nrm;
+ std::vector<aiVector3D> uv;
+ std::vector<aiFace> faces;
+};
+
+static void ReadShape(SIB *sib, StreamReaderLE *stream) {
+ SIBMesh smesh;
+ aiString name;
+
+ while (stream->GetRemainingSizeToLimit() >= sizeof(SIBChunk)) {
+ SIBChunk chunk = ReadChunk(stream);
+ unsigned oldLimit = stream->SetReadLimit(stream->GetCurrentPos() + chunk.Size);
+
+ switch (chunk.Tag) {
+ case TAG('M', 'I', 'R', 'P'): break; // mirror plane maybe?
+ case TAG('I', 'M', 'R', 'P'): break; // instance mirror? (not supported here yet)
+ case TAG('D', 'I', 'N', 'F'): break; // display info, not needed
+ case TAG('P', 'I', 'N', 'F'): break; // ?
+ case TAG('V', 'M', 'I', 'R'): break; // ?
+ case TAG('F', 'M', 'I', 'R'): break; // ?
+ case TAG('T', 'X', 'S', 'M'): break; // ?
+ case TAG('F', 'A', 'H', 'S'): break; // ?
+ case TAG('V', 'R', 'T', 'S'): ReadVerts(&smesh, stream, chunk.Size / 12); break;
+ case TAG('F', 'A', 'C', 'S'): ReadFaces(&smesh, stream); break;
+ case TAG('F', 'T', 'V', 'S'): ReadUVs(&smesh, stream); break;
+ case TAG('S', 'N', 'A', 'M'): name = ReadString(stream, chunk.Size / 2); break;
+ case TAG('F', 'A', 'M', 'A'): ReadMtls(&smesh, stream); break;
+ case TAG('A', 'X', 'I', 'S'): ReadAxis(smesh.axis, stream); break;
+ case TAG('E', 'D', 'G', 'S'): ReadEdges(&smesh, stream); break;
+ case TAG('E', 'C', 'R', 'S'): ReadCreases(&smesh, stream); break;
+ default: UnknownChunk(stream, chunk); break;
+ }
+
+ stream->SetCurrentPos(stream->GetReadLimit());
+ stream->SetReadLimit(oldLimit);
+ }
+
+ ai_assert(smesh.faceStart.size() == smesh.mtls.size()); // sanity check
+
+ // Silo doesn't store any normals in the file - we need to compute
+ // them ourselves. We can't let AssImp handle it as AssImp doesn't
+ // know about our creased edges.
+ ConnectFaces(&smesh);
+ CalculateNormals(&smesh);
+
+ // Construct the transforms.
+ aiMatrix4x4 worldToLocal = smesh.axis;
+ worldToLocal.Inverse();
+ aiMatrix4x4 worldToLocalN = worldToLocal;
+ worldToLocalN.a4 = worldToLocalN.b4 = worldToLocalN.c4 = 0.0f;
+ worldToLocalN.Inverse().Transpose();
+
+ // Allocate final mesh data.
+ // We'll allocate one mesh for each material. (we'll strip unused ones after)
+ std::vector<TempMesh> meshes(sib->mtls.size());
+
+ // Un-index the source data and apply to each vertex.
+ for (unsigned fi = 0; fi < smesh.faceStart.size(); fi++) {
+ uint32_t start = smesh.faceStart[fi];
+ uint32_t mtl = smesh.mtls[fi];
+ uint32_t *idx = &smesh.idx[start];
+
+ if (mtl >= meshes.size()) {
+ ASSIMP_LOG_ERROR("SIB: Face material index is invalid.");
+ mtl = 0;
+ }
+
+ TempMesh &dest = meshes[mtl];
+
+ aiFace face;
+ face.mNumIndices = *idx++;
+ face.mIndices = new unsigned[face.mNumIndices];
+ for (unsigned pt = 0; pt < face.mNumIndices; pt++, idx += N) {
+ size_t vtxIdx = dest.vtx.size();
+ face.mIndices[pt] = static_cast<unsigned int>(vtxIdx);
+
+ // De-index it. We don't need to validate here as
+ // we did it when creating the data.
+ aiVector3D pos = smesh.pos[idx[POS]];
+ aiVector3D nrm = smesh.nrm[idx[NRM]];
+ aiVector3D uv = smesh.uv[idx[UV]];
+
+ // The verts are supplied in world-space, so let's
+ // transform them back into the local space of this mesh:
+ pos = worldToLocal * pos;
+ nrm = worldToLocalN * nrm;
+
+ dest.vtx.push_back(pos);
+ dest.nrm.push_back(nrm);
+ dest.uv.push_back(uv);
+ }
+ dest.faces.push_back(face);
+ }
+
+ SIBObject obj;
+ obj.name = name;
+ obj.axis = smesh.axis;
+ obj.meshIdx = sib->meshes.size();
+
+ // Now that we know the size of everything,
+ // we can build the final one-material-per-mesh data.
+ for (size_t n = 0; n < meshes.size(); n++) {
+ TempMesh &src = meshes[n];
+ if (src.faces.empty())
+ continue;
+
+ aiMesh *mesh = new aiMesh;
+ mesh->mName = name;
+ mesh->mNumFaces = static_cast<unsigned int>(src.faces.size());
+ mesh->mFaces = new aiFace[mesh->mNumFaces];
+ mesh->mNumVertices = static_cast<unsigned int>(src.vtx.size());
+ mesh->mVertices = new aiVector3D[mesh->mNumVertices];
+ mesh->mNormals = new aiVector3D[mesh->mNumVertices];
+ mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
+ mesh->mNumUVComponents[0] = 2;
+ mesh->mMaterialIndex = static_cast<unsigned int>(n);
+
+ for (unsigned i = 0; i < mesh->mNumVertices; i++) {
+ mesh->mVertices[i] = src.vtx[i];
+ mesh->mNormals[i] = src.nrm[i];
+ mesh->mTextureCoords[0][i] = src.uv[i];
+ }
+ for (unsigned i = 0; i < mesh->mNumFaces; i++) {
+ mesh->mFaces[i] = src.faces[i];
+ }
+
+ sib->meshes.push_back(mesh);
+ }
+
+ obj.meshCount = sib->meshes.size() - obj.meshIdx;
+ sib->objs.push_back(obj);
+}
+
+// ------------------------------------------------------------------------------------------------
+static void ReadMaterial(SIB *sib, StreamReaderLE *stream) {
+ aiColor3D diff = ReadColor(stream);
+ aiColor3D ambi = ReadColor(stream);
+ aiColor3D spec = ReadColor(stream);
+ aiColor3D emis = ReadColor(stream);
+ float shiny = (float)stream->GetU4();
+
+ uint32_t nameLen = stream->GetU4();
+ aiString name = ReadString(stream, nameLen / 2);
+ uint32_t texLen = stream->GetU4();
+ aiString tex = ReadString(stream, texLen / 2);
+
+ aiMaterial *mtl = new aiMaterial();
+ mtl->AddProperty(&diff, 1, AI_MATKEY_COLOR_DIFFUSE);
+ mtl->AddProperty(&ambi, 1, AI_MATKEY_COLOR_AMBIENT);
+ mtl->AddProperty(&spec, 1, AI_MATKEY_COLOR_SPECULAR);
+ mtl->AddProperty(&emis, 1, AI_MATKEY_COLOR_EMISSIVE);
+ mtl->AddProperty(&shiny, 1, AI_MATKEY_SHININESS);
+ mtl->AddProperty(&name, AI_MATKEY_NAME);
+ if (tex.length > 0) {
+ mtl->AddProperty(&tex, AI_MATKEY_TEXTURE_DIFFUSE(0));
+ mtl->AddProperty(&tex, AI_MATKEY_TEXTURE_AMBIENT(0));
+ }
+
+ sib->mtls.push_back(mtl);
+}
+
+// ------------------------------------------------------------------------------------------------
+static void ReadLightInfo(aiLight *light, StreamReaderLE *stream) {
+ uint32_t type = stream->GetU4();
+ switch (type) {
+ case 0: light->mType = aiLightSource_POINT; break;
+ case 1: light->mType = aiLightSource_SPOT; break;
+ case 2: light->mType = aiLightSource_DIRECTIONAL; break;
+ default: light->mType = aiLightSource_UNDEFINED; break;
+ }
+
+ light->mPosition.x = stream->GetF4();
+ light->mPosition.y = stream->GetF4();
+ light->mPosition.z = stream->GetF4();
+ light->mDirection.x = stream->GetF4();
+ light->mDirection.y = stream->GetF4();
+ light->mDirection.z = stream->GetF4();
+ light->mColorDiffuse = ReadColor(stream);
+ light->mColorAmbient = ReadColor(stream);
+ light->mColorSpecular = ReadColor(stream);
+ ai_real spotExponent = stream->GetF4();
+ ai_real spotCutoff = stream->GetF4();
+ light->mAttenuationConstant = stream->GetF4();
+ light->mAttenuationLinear = stream->GetF4();
+ light->mAttenuationQuadratic = stream->GetF4();
+
+ // Silo uses the OpenGL default lighting model for it's
+ // spot cutoff/exponent. AssImp unfortunately, does not.
+ // Let's try and approximate it by solving for the
+ // 99% and 1% percentiles.
+ // OpenGL: I = cos(angle)^E
+ // Solving: angle = acos(I^(1/E))
+ ai_real E = ai_real(1.0) / std::max(spotExponent, (ai_real)0.00001);
+ ai_real inner = std::acos(std::pow((ai_real)0.99, E));
+ ai_real outer = std::acos(std::pow((ai_real)0.01, E));
+
+ // Apply the cutoff.
+ outer = std::min(outer, AI_DEG_TO_RAD(spotCutoff));
+
+ light->mAngleInnerCone = std::min(inner, outer);
+ light->mAngleOuterCone = outer;
+}
+
+static void ReadLight(SIB *sib, StreamReaderLE *stream) {
+ aiLight *light = new aiLight();
+
+ while (stream->GetRemainingSizeToLimit() >= sizeof(SIBChunk)) {
+ SIBChunk chunk = ReadChunk(stream);
+ unsigned oldLimit = stream->SetReadLimit(stream->GetCurrentPos() + chunk.Size);
+
+ switch (chunk.Tag) {
+ case TAG('L', 'N', 'F', 'O'): ReadLightInfo(light, stream); break;
+ case TAG('S', 'N', 'A', 'M'): light->mName = ReadString(stream, chunk.Size / 2); break;
+ default: UnknownChunk(stream, chunk); break;
+ }
+
+ stream->SetCurrentPos(stream->GetReadLimit());
+ stream->SetReadLimit(oldLimit);
+ }
+
+ sib->lights.push_back(light);
+}
+
+// ------------------------------------------------------------------------------------------------
+static void ReadScale(aiMatrix4x4 &axis, StreamReaderLE *stream) {
+ aiMatrix4x4 scale;
+ scale.a1 = stream->GetF4();
+ scale.b1 = stream->GetF4();
+ scale.c1 = stream->GetF4();
+ scale.d1 = stream->GetF4();
+ scale.a2 = stream->GetF4();
+ scale.b2 = stream->GetF4();
+ scale.c2 = stream->GetF4();
+ scale.d2 = stream->GetF4();
+ scale.a3 = stream->GetF4();
+ scale.b3 = stream->GetF4();
+ scale.c3 = stream->GetF4();
+ scale.d3 = stream->GetF4();
+ scale.a4 = stream->GetF4();
+ scale.b4 = stream->GetF4();
+ scale.c4 = stream->GetF4();
+ scale.d4 = stream->GetF4();
+
+ axis = axis * scale;
+}
+
+static void ReadInstance(SIB *sib, StreamReaderLE *stream) {
+ SIBObject inst;
+ uint32_t shapeIndex = 0;
+
+ while (stream->GetRemainingSizeToLimit() >= sizeof(SIBChunk)) {
+ SIBChunk chunk = ReadChunk(stream);
+ unsigned oldLimit = stream->SetReadLimit(stream->GetCurrentPos() + chunk.Size);
+
+ switch (chunk.Tag) {
+ case TAG('D', 'I', 'N', 'F'): break; // display info, not needed
+ case TAG('P', 'I', 'N', 'F'): break; // ?
+ case TAG('A', 'X', 'I', 'S'): ReadAxis(inst.axis, stream); break;
+ case TAG('I', 'N', 'S', 'I'): shapeIndex = stream->GetU4(); break;
+ case TAG('S', 'M', 'T', 'X'): ReadScale(inst.axis, stream); break;
+ case TAG('S', 'N', 'A', 'M'): inst.name = ReadString(stream, chunk.Size / 2); break;
+ default: UnknownChunk(stream, chunk); break;
+ }
+
+ stream->SetCurrentPos(stream->GetReadLimit());
+ stream->SetReadLimit(oldLimit);
+ }
+
+ if (shapeIndex >= sib->objs.size()) {
+ throw DeadlyImportError("SIB: Invalid shape index.");
+ }
+
+ const SIBObject &src = sib->objs[shapeIndex];
+ inst.meshIdx = src.meshIdx;
+ inst.meshCount = src.meshCount;
+ sib->insts.push_back(inst);
+}
+
+// ------------------------------------------------------------------------------------------------
+static void CheckVersion(StreamReaderLE *stream) {
+ uint32_t version = stream->GetU4();
+ if (version < 1 || version > 2) {
+ throw DeadlyImportError("SIB: Unsupported file version.");
+ }
+}
+
+static void ReadScene(SIB *sib, StreamReaderLE *stream) {
+ // Parse each chunk in turn.
+ while (stream->GetRemainingSizeToLimit() >= sizeof(SIBChunk)) {
+ SIBChunk chunk = ReadChunk(stream);
+ unsigned oldLimit = stream->SetReadLimit(stream->GetCurrentPos() + chunk.Size);
+
+ switch (chunk.Tag) {
+ case TAG('H', 'E', 'A', 'D'): CheckVersion(stream); break;
+ case TAG('S', 'H', 'A', 'P'): ReadShape(sib, stream); break;
+ case TAG('G', 'R', 'P', 'S'): break; // group assignment, we don't import this
+ case TAG('T', 'E', 'X', 'P'): break; // ?
+ case TAG('I', 'N', 'S', 'T'): ReadInstance(sib, stream); break;
+ case TAG('M', 'A', 'T', 'R'): ReadMaterial(sib, stream); break;
+ case TAG('L', 'G', 'H', 'T'): ReadLight(sib, stream); break;
+ default: UnknownChunk(stream, chunk); break;
+ }
+
+ stream->SetCurrentPos(stream->GetReadLimit());
+ stream->SetReadLimit(oldLimit);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void SIBImporter::InternReadFile(const std::string &pFile,
+ aiScene *pScene, IOSystem *pIOHandler) {
+
+ auto file = pIOHandler->Open(pFile, "rb");
+ if (!file)
+ throw DeadlyImportError("SIB: Could not open ", pFile);
+
+ StreamReaderLE stream(file);
+
+ // We should have at least one chunk
+ if (stream.GetRemainingSize() < 16)
+ throw DeadlyImportError("SIB file is either empty or corrupt: ", pFile);
+
+ SIB sib;
+
+ // Default material.
+ aiMaterial *defmtl = new aiMaterial;
+ aiString defname = aiString(AI_DEFAULT_MATERIAL_NAME);
+ defmtl->AddProperty(&defname, AI_MATKEY_NAME);
+ sib.mtls.push_back(defmtl);
+
+ // Read it all.
+ ReadScene(&sib, &stream);
+
+ // Join the instances and objects together.
+ size_t firstInst = sib.objs.size();
+ sib.objs.insert(sib.objs.end(), sib.insts.begin(), sib.insts.end());
+ sib.insts.clear();
+
+ // Transfer to the aiScene.
+ pScene->mNumMaterials = static_cast<unsigned int>(sib.mtls.size());
+ pScene->mNumMeshes = static_cast<unsigned int>(sib.meshes.size());
+ pScene->mNumLights = static_cast<unsigned int>(sib.lights.size());
+ pScene->mMaterials = pScene->mNumMaterials ? new aiMaterial *[pScene->mNumMaterials] : nullptr;
+ pScene->mMeshes = pScene->mNumMeshes ? new aiMesh *[pScene->mNumMeshes] : nullptr;
+ pScene->mLights = pScene->mNumLights ? new aiLight *[pScene->mNumLights] : nullptr;
+ if (pScene->mNumMaterials)
+ memcpy(pScene->mMaterials, &sib.mtls[0], sizeof(aiMaterial *) * pScene->mNumMaterials);
+ if (pScene->mNumMeshes)
+ memcpy(pScene->mMeshes, &sib.meshes[0], sizeof(aiMesh *) * pScene->mNumMeshes);
+ if (pScene->mNumLights)
+ memcpy(pScene->mLights, &sib.lights[0], sizeof(aiLight *) * pScene->mNumLights);
+
+ // Construct the root node.
+ size_t childIdx = 0;
+ aiNode *root = new aiNode();
+ root->mName.Set("<SIBRoot>");
+ root->mNumChildren = static_cast<unsigned int>(sib.objs.size() + sib.lights.size());
+ root->mChildren = root->mNumChildren ? new aiNode *[root->mNumChildren] : nullptr;
+ pScene->mRootNode = root;
+
+ // Add nodes for each object.
+ for (size_t n = 0; n < sib.objs.size(); n++) {
+ ai_assert(root->mChildren);
+ SIBObject &obj = sib.objs[n];
+ aiNode *node = new aiNode;
+ root->mChildren[childIdx++] = node;
+ node->mName = obj.name;
+ node->mParent = root;
+ node->mTransformation = obj.axis;
+
+ node->mNumMeshes = static_cast<unsigned int>(obj.meshCount);
+ node->mMeshes = node->mNumMeshes ? new unsigned[node->mNumMeshes] : nullptr;
+ for (unsigned i = 0; i < node->mNumMeshes; i++)
+ node->mMeshes[i] = static_cast<unsigned int>(obj.meshIdx + i);
+
+ // Mark instanced objects as being so.
+ if (n >= firstInst) {
+ node->mMetaData = aiMetadata::Alloc(1);
+ node->mMetaData->Set(0, "IsInstance", true);
+ }
+ }
+
+ // Add nodes for each light.
+ // (no transformation as the light is already in world space)
+ for (size_t n = 0; n < sib.lights.size(); n++) {
+ ai_assert(root->mChildren);
+ aiLight *light = sib.lights[n];
+ if (nullptr != light) {
+ aiNode *node = new aiNode;
+ root->mChildren[childIdx++] = node;
+ node->mName = light->mName;
+ node->mParent = root;
+ }
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_SIB_IMPORTER