浏览代码

Merge pull request #717 from rmitton/sib_import

Importer for Nevercenter Silo SIB files.
Kim Kulling 9 年之前
父节点
当前提交
db96007f77

+ 2 - 2
code/3DSLoader.cpp

@@ -86,8 +86,8 @@ static const aiImporterDesc desc = {
     int chunkSize = chunk.Size-sizeof(Discreet3DS::Chunk);               \
     if(chunkSize <= 0)                                                   \
         continue;                                                        \
-    const int oldReadLimit = stream->GetReadLimit();                     \
-    stream->SetReadLimit(stream->GetCurrentPos() + chunkSize);           \
+    const unsigned int oldReadLimit = stream->SetReadLimit(              \
+        stream->GetCurrentPos() + chunkSize);                            \
 
 
 // ------------------------------------------------------------------------------------------------

+ 5 - 0
code/CMakeLists.txt

@@ -530,6 +530,11 @@ ADD_ASSIMP_IMPORTER(RAW
   RawLoader.h
 )
 
+ADD_ASSIMP_IMPORTER(SIB
+  SIBImporter.cpp
+  SIBImporter.h
+)
+
 ADD_ASSIMP_IMPORTER(SMD
   SMDLoader.cpp
   SMDLoader.h

+ 6 - 0
code/ImporterRegistry.cpp

@@ -101,6 +101,9 @@ corresponding preprocessor flag to selectively disable formats.
 #ifndef ASSIMP_BUILD_NO_RAW_IMPORTER
 #   include "RawLoader.h"
 #endif
+#ifndef ASSIMP_BUILD_NO_SIB_IMPORTER
+#   include "SIBImporter.h"
+#endif
 #ifndef ASSIMP_BUILD_NO_OFF_IMPORTER
 #   include "OFFLoader.h"
 #endif
@@ -238,6 +241,9 @@ void GetImporterInstanceList(std::vector< BaseImporter* >& out)
 #if (!defined ASSIMP_BUILD_NO_RAW_IMPORTER)
     out.push_back( new RAWImporter());
 #endif
+#if (!defined ASSIMP_BUILD_NO_SIB_IMPORTER)
+    out.push_back( new SIBImporter());
+#endif
 #if (!defined ASSIMP_BUILD_NO_OFF_IMPORTER)
     out.push_back( new OFFImporter());
 #endif

+ 919 - 0
code/SIBImporter.cpp

@@ -0,0 +1,919 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2015, 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 "ByteSwapper.h"
+#include "StreamReader.h"
+#include "TinyFormatter.h"
+#include "../contrib/ConvertUTF/ConvertUTF.h"
+#include "../include/assimp/IOSystem.hpp"
+#include "../include/assimp/DefaultLogger.hpp"
+#include "../include/assimp/scene.h"
+
+
+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;
+static SIBPair makePair(uint32_t a, uint32_t b) { return (a<b) ? SIBPair(a, b) : SIBPair(b, a); }
+
+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] = 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())
+        DefaultLogger::get()->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[5] = { (chunk.Tag>>24)&0xff, (chunk.Tag>>16)&0xff, (chunk.Tag>>8)&0xff, chunk.Tag&0xff, '\0' };
+    
+    DefaultLogger::get()->warn((Formatter::format(), "SIB: Skipping unknown '",temp,"' chunk."));
+}
+
+// Reads a UTF-16LE string and returns it at UTF-8.
+static aiString ReadString(StreamReaderLE* stream, uint32_t numWChars)
+{
+    // Allocate buffers (max expansion is 1 byte -> 4 bytes for UTF-8)
+    UTF16* temp = new UTF16[numWChars];
+    UTF8* str = new UTF8[numWChars * 4 + 1];
+    for (uint32_t n=0;n<numWChars;n++)
+        temp[n] = stream->GetU2();
+
+    // Convert it and NUL-terminate.
+    const UTF16 *start = temp, *end = temp + numWChars;
+    UTF8 *dest = str, *limit = str + numWChars*4;
+    ConvertUTF16toUTF8(&start, end, &dest, limit, lenientConversion);
+    *dest = '\0';
+
+    // Return the final string.
+    aiString result = aiString((const char *)str);
+    delete[] str;
+    delete[] temp;
+    return result;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+SIBImporter::SIBImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+SIBImporter::~SIBImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool SIBImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const
+{
+    return SimpleExtensionCheck(pFile, "sib");
+}
+
+// ------------------------------------------------------------------------------------------------
+const aiImporterDesc* SIBImporter::GetInfo () const
+{
+    return &desc;
+}
+
+// ------------------------------------------------------------------------------------------------
+static void ReadVerts(SIBMesh* mesh, StreamReaderLE* stream, uint32_t count)
+{
+    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(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 = faceIdx;
+            else
+                edge.faceB = 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);
+
+                    // Move to whichever side we didn't just come from.
+                    if (!edge.creased) {
+                        if (edge.faceA != prevFaceIdx && edge.faceA != faceIdx)
+                            nextFaceIdx = edge.faceA;
+                        else if (edge.faceB != prevFaceIdx && edge.faceB != faceIdx)
+                            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, 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);
+    }
+
+    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())
+        {
+            DefaultLogger::get()->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] = 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 = src.faces.size();
+        mesh->mFaces = new aiFace[mesh->mNumFaces];
+        mesh->mNumVertices = 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 = 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);
+    float spotExponent = stream->GetF4();
+    float 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))
+    float E = 1.0f / std::max(spotExponent, 0.00001f);
+    float inner = acosf(powf(0.99f, E));
+    float outer = acosf(powf(0.01f, 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)
+        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)
+{
+    StreamReaderLE stream(pIOHandler->Open(pFile, "rb"));
+
+    // 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 = sib.mtls.size();
+    pScene->mNumMeshes = sib.meshes.size();
+    pScene->mNumLights = sib.lights.size();
+    pScene->mMaterials = new aiMaterial* [pScene->mNumMaterials];
+    pScene->mMeshes = new aiMesh* [pScene->mNumMeshes];
+    pScene->mLights = new aiLight* [pScene->mNumLights];
+    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 = sib.objs.size() + sib.lights.size();
+    root->mChildren = new aiNode* [root->mNumChildren];
+    pScene->mRootNode = root;
+
+    // Add nodes for each object.
+    for (size_t n=0;n<sib.objs.size();n++)
+    {
+        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 = obj.meshCount;
+        node->mMeshes = new unsigned[node->mNumMeshes];
+        for (unsigned i=0;i<node->mNumMeshes;i++)
+            node->mMeshes[i] = obj.meshIdx + i;
+
+        // Mark instanced objects as being so.
+        if (n >= firstInst)
+        {
+            node->mMetaData = new aiMetadata;
+            node->mMetaData->mNumProperties = 1;
+            node->mMetaData->mKeys = new aiString[1];
+            node->mMetaData->mValues = new aiMetadataEntry[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++)
+    {
+        aiLight* light = sib.lights[n];
+        aiNode* node = new aiNode;
+        root->mChildren[childIdx++] = node;
+        node->mName = light->mName;
+        node->mParent = root;
+    }
+}
+
+#endif // !! ASSIMP_BUILD_NO_SIB_IMPORTER

+ 119 - 0
code/SIBImporter.h

@@ -0,0 +1,119 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2015, 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.h
+ *  @brief Declaration of the SIB importer class.
+ */
+#ifndef AI_SIBIMPORTER_H_INCLUDED
+#define AI_SIBIMPORTER_H_INCLUDED
+
+#include "BaseImporter.h"
+#include "../include/assimp/types.h"
+#include <vector>
+
+namespace Assimp    {
+
+// ---------------------------------------------------------------------------
+/** Importer class for the Nevercenter Silo SIB scene format
+*/
+class SIBImporter : public BaseImporter
+{
+public:
+    SIBImporter();
+    ~SIBImporter();
+
+
+public:
+
+    // -------------------------------------------------------------------
+    /** Returns whether the class can handle the format of the given file.
+     * See BaseImporter::CanRead() for details.
+     */
+    bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+        bool checkSig) const;
+
+protected:
+
+    // -------------------------------------------------------------------
+    /** Return importer meta information.
+     * See #BaseImporter::GetInfo for the details
+     */
+    const aiImporterDesc* GetInfo () const;
+
+    // -------------------------------------------------------------------
+    /** Imports the given file into the given scene structure.
+    * See BaseImporter::InternReadFile() for details
+    */
+    void InternReadFile( const std::string& pFile, aiScene* pScene,
+        IOSystem* pIOHandler);
+
+private:
+
+    struct MeshInformation
+    {
+        explicit MeshInformation(const std::string& _name)
+            : name(_name)
+        {
+            vertices.reserve(100);
+            colors.reserve(100);
+        }
+
+        std::string name;
+
+        std::vector<aiVector3D> vertices;
+        std::vector<aiColor4D> colors;
+    };
+
+    struct GroupInformation
+    {
+        explicit GroupInformation(const std::string& _name)
+            : name(_name)
+        {
+            meshes.reserve(10);
+        }
+
+        std::string name;
+        std::vector<MeshInformation> meshes;
+    };
+};
+
+} // end of namespace Assimp
+
+#endif // AI_SIBIMPORTER_H_INCLUDED

+ 7 - 5
code/StreamReader.h

@@ -253,24 +253,26 @@ public:
      *
      *  @param limit Maximum number of bytes to be read from
      *    the beginning of the file. Specifying UINT_MAX
-     *    resets the limit to the original end of the stream. */
-    void SetReadLimit(unsigned int _limit)  {
-
+     *    resets the limit to the original end of the stream. 
+     *  Returns the previously set limit. */
+    unsigned int SetReadLimit(unsigned int _limit)  {
+        unsigned int prev = GetReadLimit();
         if (UINT_MAX == _limit) {
             limit = end;
-            return;
+            return prev;
         }
 
         limit = buffer + _limit;
         if (limit > end) {
             throw DeadlyImportError("StreamReader: Invalid read limit");
         }
+        return prev;
     }
 
     // ---------------------------------------------------------------------
     /** Get the current read limit in bytes. Reading over this limit
      *  accidentially raises an exception.  */
-    int GetReadLimit() const    {
+    unsigned int GetReadLimit() const    {
         return (unsigned int)(limit - buffer);
     }
 

二进制
test/models/SIB/This Way Up.png


二进制
test/models/SIB/UV Mapping.png


二进制
test/models/SIB/heffalump.sib


+ 3 - 0
test/models/SIB/readme.txt

@@ -0,0 +1,3 @@
+Made by me (Richard Mitton).
+I waive all rights to this and release it into the public domain.
+Do with it as you wish.