diff --git a/code/3DSLoader.cpp b/code/3DSLoader.cpp index 3c80c320b..5582864cb 100644 --- a/code/3DSLoader.cpp +++ b/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); \ // ------------------------------------------------------------------------------------------------ diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index dd15e3ce4..a568ea8b1 100644 --- a/code/CMakeLists.txt +++ b/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 diff --git a/code/ImporterRegistry.cpp b/code/ImporterRegistry.cpp index f8382bcdc..ba1ea6337 100644 --- a/code/ImporterRegistry.cpp +++ b/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 diff --git a/code/SIBImporter.cpp b/code/SIBImporter.cpp new file mode 100644 index 000000000..bfb093512 --- /dev/null +++ b/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 SIBPair; +static SIBPair makePair(uint32_t a, uint32_t b) { return (a pos, nrm, uv; + std::vector idx; + std::vector faceStart; + std::vector mtls; + std::vector edges; + std::map edgeMap; +}; + +struct SIBObject +{ + aiString name; + aiMatrix4x4 axis; + size_t meshIdx, meshCount; +}; + +struct SIB +{ + std::vector mtls; + std::vector meshes; + std::vector lights; + std::vector 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::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;nGetU2(); + + // 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;npos[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;nGetU4(); + 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;nuv[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;faceIdxidx[mesh->faceStart[faceIdx]]; + uint32_t numPoints = *idx++; + uint32_t prev = idx[(numPoints-1)*N+POS]; + + for (uint32_t i=0;i& 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 0.000000001f) + vtxNormal /= len; + return vtxNormal; +} + +// ------------------------------------------------------------------------------------------------ +static void CalculateNormals(SIBMesh* mesh) +{ + size_t numFaces = mesh->faceStart.size(); + + // Calculate face normals. + std::vector faceNormals(numFaces); + for (size_t faceIdx=0;faceIdxidx[mesh->faceStart[faceIdx]]; + uint32_t numPoints = *idx++; + + aiVector3D faceNormal(0, 0, 0); + + uint32_t *prev = &idx[(numPoints-1)*N]; + + for (uint32_t i=0;ipos[prev[POS]] ^ mesh->pos[next[POS]]; + prev = next; + } + + faceNormals[faceIdx] = faceNormal; + } + + // Calculate vertex normals. + for (size_t faceIdx=0;faceIdxidx[mesh->faceStart[faceIdx]]; + uint32_t numPoints = *idx++; + + for (uint32_t i=0;inrm[nrm] = vtxNorm; + } + } +} + +// ------------------------------------------------------------------------------------------------ +struct TempMesh +{ + std::vector vtx; + std::vector nrm; + std::vector uv; + std::vector 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 meshes(sib->mtls.size()); + + // Un-index the source data and apply to each vertex. + for (unsigned fi=0;fi= 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;ptmeshes.size(); + + // Now that we know the size of everything, + // we can build the final one-material-per-mesh data. + for (size_t n=0;nmName = 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;imNumVertices;i++) + { + mesh->mVertices[i] = src.vtx[i]; + mesh->mNormals[i] = src.nrm[i]; + mesh->mTextureCoords[0][i] = src.uv[i]; + } + for (unsigned i=0;imNumFaces;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(""); + 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;nmChildren[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;imNumMeshes;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;nmChildren[childIdx++] = node; + node->mName = light->mName; + node->mParent = root; + } +} + +#endif // !! ASSIMP_BUILD_NO_SIB_IMPORTER diff --git a/code/SIBImporter.h b/code/SIBImporter.h new file mode 100644 index 000000000..b68972a81 --- /dev/null +++ b/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 + +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 vertices; + std::vector colors; + }; + + struct GroupInformation + { + explicit GroupInformation(const std::string& _name) + : name(_name) + { + meshes.reserve(10); + } + + std::string name; + std::vector meshes; + }; +}; + +} // end of namespace Assimp + +#endif // AI_SIBIMPORTER_H_INCLUDED diff --git a/code/StreamReader.h b/code/StreamReader.h index 0698d99f9..1f858b691 100644 --- a/code/StreamReader.h +++ b/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); } diff --git a/test/models/SIB/This Way Up.png b/test/models/SIB/This Way Up.png new file mode 100644 index 000000000..87c8ba1dc Binary files /dev/null and b/test/models/SIB/This Way Up.png differ diff --git a/test/models/SIB/UV Mapping.png b/test/models/SIB/UV Mapping.png new file mode 100644 index 000000000..3d8e235d4 Binary files /dev/null and b/test/models/SIB/UV Mapping.png differ diff --git a/test/models/SIB/heffalump.sib b/test/models/SIB/heffalump.sib new file mode 100644 index 000000000..00d6bfb51 Binary files /dev/null and b/test/models/SIB/heffalump.sib differ diff --git a/test/models/SIB/readme.txt b/test/models/SIB/readme.txt new file mode 100644 index 000000000..4a83c0ea4 --- /dev/null +++ b/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.