123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927 |
- /*
- ---------------------------------------------------------------------------
- Open Asset Import Library (assimp)
- ---------------------------------------------------------------------------
- Copyright (c) 2006-2016, 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 <assimp/IOSystem.hpp>
- #include <assimp/DefaultLogger.hpp>
- #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] = 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())
- 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] = {
- 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), '\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() {
- // 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& 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(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
- 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);
- // 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, 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())
- {
- 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] = 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 ) {
- 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 = 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] : NULL;
- pScene->mMeshes = pScene->mNumMeshes ? new aiMesh*[pScene->mNumMeshes] : NULL;
- pScene->mLights = pScene->mNumLights ? new aiLight*[pScene->mNumLights] : NULL;
- 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] : NULL;
- 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 = static_cast<unsigned int>(obj.meshCount);
- node->mMeshes = node->mNumMeshes ? new unsigned[node->mNumMeshes] : NULL;
- 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++)
- {
- 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
|