nuclear@0: /* nuclear@0: --------------------------------------------------------------------------- nuclear@0: Open Asset Import Library (assimp) nuclear@0: --------------------------------------------------------------------------- nuclear@0: nuclear@0: Copyright (c) 2006-2012, assimp team nuclear@0: nuclear@0: All rights reserved. nuclear@0: nuclear@0: Redistribution and use of this software in source and binary forms, nuclear@0: with or without modification, are permitted provided that the following nuclear@0: conditions are met: nuclear@0: nuclear@0: * Redistributions of source code must retain the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer. nuclear@0: nuclear@0: * Redistributions in binary form must reproduce the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer in the documentation and/or other nuclear@0: materials provided with the distribution. nuclear@0: nuclear@0: * Neither the name of the assimp team, nor the names of its nuclear@0: contributors may be used to endorse or promote products nuclear@0: derived from this software without specific prior nuclear@0: written permission of the assimp team. nuclear@0: nuclear@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS nuclear@0: "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT nuclear@0: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR nuclear@0: A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT nuclear@0: OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, nuclear@0: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT nuclear@0: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, nuclear@0: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY nuclear@0: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT nuclear@0: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE nuclear@0: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. nuclear@0: --------------------------------------------------------------------------- nuclear@0: */ nuclear@0: nuclear@0: /** @file Implementation of the XGL/ZGL importer class */ nuclear@0: nuclear@0: #include "AssimpPCH.h" nuclear@0: #ifndef ASSIMP_BUILD_NO_XGL_IMPORTER nuclear@0: nuclear@0: #include "XGLLoader.h" nuclear@0: #include "ParsingUtils.h" nuclear@0: #include "fast_atof.h" nuclear@0: nuclear@0: #include "StreamReader.h" nuclear@0: #include "MemoryIOWrapper.h" nuclear@0: nuclear@0: using namespace Assimp; nuclear@0: using namespace irr; nuclear@0: using namespace irr::io; nuclear@0: nuclear@0: nuclear@0: // zlib is needed for compressed XGL files nuclear@0: #ifndef ASSIMP_BUILD_NO_COMPRESSED_XGL nuclear@0: # ifdef ASSIMP_BUILD_NO_OWN_ZLIB nuclear@0: # include nuclear@0: # else nuclear@0: # include "../contrib/zlib/zlib.h" nuclear@0: # endif nuclear@0: #endif nuclear@0: nuclear@0: nuclear@0: // scopeguard for a malloc'ed buffer nuclear@0: struct free_it nuclear@0: { nuclear@0: free_it(void* free) : free(free) {} nuclear@0: ~free_it() { nuclear@0: ::free(this->free); nuclear@0: } nuclear@0: nuclear@0: void* free; nuclear@0: }; nuclear@0: nuclear@0: namespace Assimp { // this has to be in here because LogFunctions is in ::Assimp nuclear@0: template<> const std::string LogFunctions::log_prefix = "XGL: "; nuclear@0: nuclear@0: } nuclear@0: nuclear@0: static const aiImporterDesc desc = { nuclear@0: "XGL Importer", nuclear@0: "", nuclear@0: "", nuclear@0: "", nuclear@0: aiImporterFlags_SupportTextFlavour, nuclear@0: 0, nuclear@0: 0, nuclear@0: 0, nuclear@0: 0, nuclear@0: "xgl zgl" nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Constructor to be privately used by Importer nuclear@0: XGLImporter::XGLImporter() nuclear@0: {} nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Destructor, private as well nuclear@0: XGLImporter::~XGLImporter() nuclear@0: {} nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Returns whether the class can handle the format of the given file. nuclear@0: bool XGLImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const nuclear@0: { nuclear@0: /* NOTE: A simple check for the file extension is not enough nuclear@0: * here. XGL and ZGL are ok, but xml is too generic nuclear@0: * and might be collada as well. So open the file and nuclear@0: * look for typical signal tokens. nuclear@0: */ nuclear@0: const std::string extension = GetExtension(pFile); nuclear@0: nuclear@0: if (extension == "xgl" || extension == "zgl") { nuclear@0: return true; nuclear@0: } nuclear@0: else if (extension == "xml" || checkSig) { nuclear@0: ai_assert(pIOHandler != NULL); nuclear@0: nuclear@0: const char* tokens[] = {"","",""}; nuclear@0: return SearchFileHeaderForToken(pIOHandler,pFile,tokens,3); nuclear@0: } nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Get a list of all file extensions which are handled by this class nuclear@0: const aiImporterDesc* XGLImporter::GetInfo () const nuclear@0: { nuclear@0: return &desc; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Imports the given file into the given scene structure. nuclear@0: void XGLImporter::InternReadFile( const std::string& pFile, nuclear@0: aiScene* pScene, IOSystem* pIOHandler) nuclear@0: { nuclear@0: #ifndef ASSIMP_BUILD_NO_COMPRESSED_XGL nuclear@0: Bytef* dest = NULL; nuclear@0: free_it free_it_really(dest); nuclear@0: #endif nuclear@0: nuclear@0: scene = pScene; nuclear@0: boost::shared_ptr stream( pIOHandler->Open( pFile, "rb")); nuclear@0: nuclear@0: // check whether we can read from the file nuclear@0: if( stream.get() == NULL) { nuclear@0: throw DeadlyImportError( "Failed to open XGL/ZGL file " + pFile + ""); nuclear@0: } nuclear@0: nuclear@0: // see if its compressed, if so uncompress it nuclear@0: if (GetExtension(pFile) == "zgl") { nuclear@0: #ifdef ASSIMP_BUILD_NO_COMPRESSED_XGL nuclear@0: ThrowException("Cannot read ZGL file since Assimp was built without compression support"); nuclear@0: #else nuclear@0: boost::scoped_ptr raw_reader(new StreamReaderLE(stream)); nuclear@0: nuclear@0: // build a zlib stream nuclear@0: z_stream zstream; nuclear@0: zstream.opaque = Z_NULL; nuclear@0: zstream.zalloc = Z_NULL; nuclear@0: zstream.zfree = Z_NULL; nuclear@0: zstream.data_type = Z_BINARY; nuclear@0: nuclear@0: // raw decompression without a zlib or gzip header nuclear@0: inflateInit2(&zstream, -MAX_WBITS); nuclear@0: nuclear@0: // skip two extra bytes, zgl files do carry a crc16 upfront (I think) nuclear@0: raw_reader->IncPtr(2); nuclear@0: nuclear@0: zstream.next_in = reinterpret_cast( raw_reader->GetPtr() ); nuclear@0: zstream.avail_in = raw_reader->GetRemainingSize(); nuclear@0: nuclear@0: size_t total = 0l; nuclear@0: nuclear@0: // and decompress the data .... do 1k chunks in the hope that we won't kill the stack nuclear@0: #define MYBLOCK 1024 nuclear@0: Bytef block[MYBLOCK]; nuclear@0: int ret; nuclear@0: do { nuclear@0: zstream.avail_out = MYBLOCK; nuclear@0: zstream.next_out = block; nuclear@0: ret = inflate(&zstream, Z_NO_FLUSH); nuclear@0: nuclear@0: if (ret != Z_STREAM_END && ret != Z_OK) { nuclear@0: ThrowException("Failure decompressing this file using gzip, seemingly it is NOT a compressed .XGL file"); nuclear@0: } nuclear@0: const size_t have = MYBLOCK - zstream.avail_out; nuclear@0: total += have; nuclear@0: dest = reinterpret_cast( realloc(dest,total) ); nuclear@0: memcpy(dest + total - have,block,have); nuclear@0: } nuclear@0: while (ret != Z_STREAM_END); nuclear@0: nuclear@0: // terminate zlib nuclear@0: inflateEnd(&zstream); nuclear@0: nuclear@0: // replace the input stream with a memory stream nuclear@0: stream.reset(new MemoryIOStream(reinterpret_cast(dest),total)); nuclear@0: #endif nuclear@0: } nuclear@0: nuclear@0: // construct the irrXML parser nuclear@0: CIrrXML_IOStreamReader st(stream.get()); nuclear@0: boost::scoped_ptr read( createIrrXMLReader((IFileReadCallBack*) &st) ); nuclear@0: reader = read.get(); nuclear@0: nuclear@0: // parse the XML file nuclear@0: TempScope scope; nuclear@0: nuclear@0: while (ReadElement()) { nuclear@0: if (!ASSIMP_stricmp(reader->getNodeName(),"world")) { nuclear@0: ReadWorld(scope); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: std::vector& meshes = scope.meshes_linear; nuclear@0: std::vector& materials = scope.materials_linear; nuclear@0: if(!meshes.size() || !materials.size()) { nuclear@0: ThrowException("failed to extract data from XGL file, no meshes loaded"); nuclear@0: } nuclear@0: nuclear@0: // copy meshes nuclear@0: scene->mNumMeshes = static_cast(meshes.size()); nuclear@0: scene->mMeshes = new aiMesh*[scene->mNumMeshes](); nuclear@0: std::copy(meshes.begin(),meshes.end(),scene->mMeshes); nuclear@0: nuclear@0: // copy materials nuclear@0: scene->mNumMaterials = static_cast(materials.size()); nuclear@0: scene->mMaterials = new aiMaterial*[scene->mNumMaterials](); nuclear@0: std::copy(materials.begin(),materials.end(),scene->mMaterials); nuclear@0: nuclear@0: if (scope.light) { nuclear@0: scene->mNumLights = 1; nuclear@0: scene->mLights = new aiLight*[1]; nuclear@0: scene->mLights[0] = scope.light; nuclear@0: nuclear@0: scope.light->mName = scene->mRootNode->mName; nuclear@0: } nuclear@0: nuclear@0: scope.dismiss(); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: bool XGLImporter::ReadElement() nuclear@0: { nuclear@0: while(reader->read()) { nuclear@0: if (reader->getNodeType() == EXN_ELEMENT) { nuclear@0: return true; nuclear@0: } nuclear@0: } nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: bool XGLImporter::ReadElementUpToClosing(const char* closetag) nuclear@0: { nuclear@0: while(reader->read()) { nuclear@0: if (reader->getNodeType() == EXN_ELEMENT) { nuclear@0: return true; nuclear@0: } nuclear@0: else if (reader->getNodeType() == EXN_ELEMENT_END && !ASSIMP_stricmp(reader->getNodeName(),closetag)) { nuclear@0: return false; nuclear@0: } nuclear@0: } nuclear@0: LogError("unexpected EOF, expected closing <" + std::string(closetag) + "> tag"); nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: bool XGLImporter::SkipToText() nuclear@0: { nuclear@0: while(reader->read()) { nuclear@0: if (reader->getNodeType() == EXN_TEXT) { nuclear@0: return true; nuclear@0: } nuclear@0: else if (reader->getNodeType() == EXN_ELEMENT || reader->getNodeType() == EXN_ELEMENT_END) { nuclear@0: ThrowException("expected text contents but found another element (or element end)"); nuclear@0: } nuclear@0: } nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: std::string XGLImporter::GetElementName() nuclear@0: { nuclear@0: const char* s = reader->getNodeName(); nuclear@0: size_t len = strlen(s); nuclear@0: nuclear@0: std::string ret; nuclear@0: ret.resize(len); nuclear@0: nuclear@0: std::transform(s,s+len,ret.begin(),::tolower); nuclear@0: return ret; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void XGLImporter::ReadWorld(TempScope& scope) nuclear@0: { nuclear@0: while (ReadElementUpToClosing("world")) { nuclear@0: const std::string& s = GetElementName(); nuclear@0: // XXX right now we'd skip if it comes after nuclear@0: // or nuclear@0: if (s == "lighting") { nuclear@0: ReadLighting(scope); nuclear@0: } nuclear@0: else if (s == "object" || s == "mesh" || s == "mat") { nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: aiNode* const nd = ReadObject(scope,true,"world"); nuclear@0: if(!nd) { nuclear@0: ThrowException("failure reading "); nuclear@0: } nuclear@0: if(!nd->mName.length) { nuclear@0: nd->mName.Set("WORLD"); nuclear@0: } nuclear@0: nuclear@0: scene->mRootNode = nd; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void XGLImporter::ReadLighting(TempScope& scope) nuclear@0: { nuclear@0: while (ReadElementUpToClosing("lighting")) { nuclear@0: const std::string& s = GetElementName(); nuclear@0: if (s == "directionallight") { nuclear@0: scope.light = ReadDirectionalLight(); nuclear@0: } nuclear@0: else if (s == "ambient") { nuclear@0: LogWarn("ignoring tag"); nuclear@0: } nuclear@0: else if (s == "spheremap") { nuclear@0: LogWarn("ignoring tag"); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: aiLight* XGLImporter::ReadDirectionalLight() nuclear@0: { nuclear@0: ScopeGuard l(new aiLight()); nuclear@0: l->mType = aiLightSource_DIRECTIONAL; nuclear@0: nuclear@0: while (ReadElementUpToClosing("directionallight")) { nuclear@0: const std::string& s = GetElementName(); nuclear@0: if (s == "direction") { nuclear@0: l->mDirection = ReadVec3(); nuclear@0: } nuclear@0: else if (s == "diffuse") { nuclear@0: l->mColorDiffuse = ReadCol3(); nuclear@0: } nuclear@0: else if (s == "specular") { nuclear@0: l->mColorSpecular = ReadCol3(); nuclear@0: } nuclear@0: } nuclear@0: return l.dismiss(); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: aiNode* XGLImporter::ReadObject(TempScope& scope, bool skipFirst, const char* closetag) nuclear@0: { nuclear@0: ScopeGuard nd(new aiNode()); nuclear@0: std::vector children; nuclear@0: std::vector meshes; nuclear@0: nuclear@0: try { nuclear@0: while (skipFirst || ReadElementUpToClosing(closetag)) { nuclear@0: skipFirst = false; nuclear@0: nuclear@0: const std::string& s = GetElementName(); nuclear@0: if (s == "mesh") { nuclear@0: const size_t prev = scope.meshes_linear.size(); nuclear@0: if(ReadMesh(scope)) { nuclear@0: const size_t newc = scope.meshes_linear.size(); nuclear@0: for(size_t i = 0; i < newc-prev; ++i) { nuclear@0: meshes.push_back(static_cast(i+prev)); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: else if (s == "mat") { nuclear@0: ReadMaterial(scope); nuclear@0: } nuclear@0: else if (s == "object") { nuclear@0: children.push_back(ReadObject(scope)); nuclear@0: } nuclear@0: else if (s == "objectref") { nuclear@0: // XXX nuclear@0: } nuclear@0: else if (s == "meshref") { nuclear@0: const unsigned int id = static_cast( ReadIndexFromText() ); nuclear@0: nuclear@0: std::multimap::iterator it = scope.meshes.find(id), end = scope.meshes.end(); nuclear@0: if (it == end) { nuclear@0: ThrowException(" index out of range"); nuclear@0: } nuclear@0: nuclear@0: for(; it != end && (*it).first == id; ++it) { nuclear@0: // ok, this is n^2 and should get optimized one day nuclear@0: aiMesh* const m = (*it).second; nuclear@0: nuclear@0: unsigned int i = 0, mcount = static_cast(scope.meshes_linear.size()); nuclear@0: for(; i < mcount; ++i) { nuclear@0: if (scope.meshes_linear[i] == m) { nuclear@0: meshes.push_back(i); nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: ai_assert(i < mcount); nuclear@0: } nuclear@0: } nuclear@0: else if (s == "transform") { nuclear@0: nd->mTransformation = ReadTrafo(); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: } catch(...) { nuclear@0: BOOST_FOREACH(aiNode* ch, children) { nuclear@0: delete ch; nuclear@0: } nuclear@0: throw; nuclear@0: } nuclear@0: nuclear@0: // link meshes to node nuclear@0: nd->mNumMeshes = static_cast(meshes.size()); nuclear@0: if (nd->mNumMeshes) { nuclear@0: nd->mMeshes = new unsigned int[nd->mNumMeshes](); nuclear@0: for(unsigned int i = 0; i < nd->mNumMeshes; ++i) { nuclear@0: nd->mMeshes[i] = meshes[i]; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // link children to parent nuclear@0: nd->mNumChildren = static_cast(children.size()); nuclear@0: if (nd->mNumChildren) { nuclear@0: nd->mChildren = new aiNode*[nd->mNumChildren](); nuclear@0: for(unsigned int i = 0; i < nd->mNumChildren; ++i) { nuclear@0: nd->mChildren[i] = children[i]; nuclear@0: children[i]->mParent = nd; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: return nd.dismiss(); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: aiMatrix4x4 XGLImporter::ReadTrafo() nuclear@0: { nuclear@0: aiVector3D forward, up, right, position; nuclear@0: float scale = 1.0f; nuclear@0: nuclear@0: while (ReadElementUpToClosing("transform")) { nuclear@0: const std::string& s = GetElementName(); nuclear@0: if (s == "forward") { nuclear@0: forward = ReadVec3(); nuclear@0: } nuclear@0: else if (s == "up") { nuclear@0: up = ReadVec3(); nuclear@0: } nuclear@0: else if (s == "position") { nuclear@0: position = ReadVec3(); nuclear@0: } nuclear@0: if (s == "scale") { nuclear@0: scale = ReadFloat(); nuclear@0: if(scale < 0.f) { nuclear@0: // this is wrong, but we can leave the value and pass it to the caller nuclear@0: LogError("found negative scaling in , ignoring"); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: aiMatrix4x4 m; nuclear@0: if(forward.SquareLength() < 1e-4 || up.SquareLength() < 1e-4) { nuclear@0: LogError("A direction vector in is zero, ignoring trafo"); nuclear@0: return m; nuclear@0: } nuclear@0: nuclear@0: forward.Normalize(); nuclear@0: up.Normalize(); nuclear@0: nuclear@0: right = forward ^ up; nuclear@0: if (fabs(up * forward) > 1e-4) { nuclear@0: // this is definitely wrong - a degenerate coordinate space ruins everything nuclear@0: // so subtitute identity transform. nuclear@0: LogError(" and vectors in are skewing, ignoring trafo"); nuclear@0: return m; nuclear@0: } nuclear@0: nuclear@0: right *= scale; nuclear@0: up *= scale; nuclear@0: forward *= scale; nuclear@0: nuclear@0: m.a1 = right.x; nuclear@0: m.b1 = right.y; nuclear@0: m.c1 = right.z; nuclear@0: nuclear@0: m.a2 = up.x; nuclear@0: m.b2 = up.y; nuclear@0: m.c2 = up.z; nuclear@0: nuclear@0: m.a3 = forward.x; nuclear@0: m.b3 = forward.y; nuclear@0: m.c3 = forward.z; nuclear@0: nuclear@0: m.a4 = position.x; nuclear@0: m.b4 = position.y; nuclear@0: m.c4 = position.z; nuclear@0: nuclear@0: return m; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: aiMesh* XGLImporter::ToOutputMesh(const TempMaterialMesh& m) nuclear@0: { nuclear@0: ScopeGuard mesh(new aiMesh()); nuclear@0: nuclear@0: mesh->mNumVertices = static_cast(m.positions.size()); nuclear@0: mesh->mVertices = new aiVector3D[mesh->mNumVertices]; nuclear@0: std::copy(m.positions.begin(),m.positions.end(),mesh->mVertices); nuclear@0: nuclear@0: if(m.normals.size()) { nuclear@0: mesh->mNormals = new aiVector3D[mesh->mNumVertices]; nuclear@0: std::copy(m.normals.begin(),m.normals.end(),mesh->mNormals); nuclear@0: } nuclear@0: nuclear@0: if(m.uvs.size()) { nuclear@0: mesh->mNumUVComponents[0] = 2; nuclear@0: mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices]; nuclear@0: nuclear@0: for(unsigned int i = 0; i < mesh->mNumVertices; ++i) { nuclear@0: mesh->mTextureCoords[0][i] = aiVector3D(m.uvs[i].x,m.uvs[i].y,0.f); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: mesh->mNumFaces = static_cast(m.vcounts.size()); nuclear@0: mesh->mFaces = new aiFace[m.vcounts.size()]; nuclear@0: nuclear@0: unsigned int idx = 0; nuclear@0: for(unsigned int i = 0; i < mesh->mNumFaces; ++i) { nuclear@0: aiFace& f = mesh->mFaces[i]; nuclear@0: f.mNumIndices = m.vcounts[i]; nuclear@0: f.mIndices = new unsigned int[f.mNumIndices]; nuclear@0: for(unsigned int c = 0; c < f.mNumIndices; ++c) { nuclear@0: f.mIndices[c] = idx++; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: ai_assert(idx == mesh->mNumVertices); nuclear@0: nuclear@0: mesh->mPrimitiveTypes = m.pflags; nuclear@0: mesh->mMaterialIndex = m.matid; nuclear@0: return mesh.dismiss(); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: bool XGLImporter::ReadMesh(TempScope& scope) nuclear@0: { nuclear@0: TempMesh t; nuclear@0: nuclear@0: std::map bymat; nuclear@0: const unsigned int mesh_id = ReadIDAttr(); nuclear@0: nuclear@0: while (ReadElementUpToClosing("mesh")) { nuclear@0: const std::string& s = GetElementName(); nuclear@0: nuclear@0: if (s == "mat") { nuclear@0: ReadMaterial(scope); nuclear@0: } nuclear@0: else if (s == "p") { nuclear@0: if (!reader->getAttributeValue("ID")) { nuclear@0: LogWarn("no ID attribute on

, ignoring"); nuclear@0: } nuclear@0: else { nuclear@0: int id = reader->getAttributeValueAsInt("ID"); nuclear@0: t.points[id] = ReadVec3(); nuclear@0: } nuclear@0: } nuclear@0: else if (s == "n") { nuclear@0: if (!reader->getAttributeValue("ID")) { nuclear@0: LogWarn("no ID attribute on , ignoring"); nuclear@0: } nuclear@0: else { nuclear@0: int id = reader->getAttributeValueAsInt("ID"); nuclear@0: t.normals[id] = ReadVec3(); nuclear@0: } nuclear@0: } nuclear@0: else if (s == "tc") { nuclear@0: if (!reader->getAttributeValue("ID")) { nuclear@0: LogWarn("no ID attribute on , ignoring"); nuclear@0: } nuclear@0: else { nuclear@0: int id = reader->getAttributeValueAsInt("ID"); nuclear@0: t.uvs[id] = ReadVec2(); nuclear@0: } nuclear@0: } nuclear@0: else if (s == "f" || s == "l" || s == "p") { nuclear@0: const unsigned int vcount = s == "f" ? 3 : (s == "l" ? 2 : 1); nuclear@0: nuclear@0: unsigned int mid = ~0u; nuclear@0: TempFace tf[3]; nuclear@0: bool has[3] = {0}; nuclear@0: nuclear@0: while (ReadElementUpToClosing(s.c_str())) { nuclear@0: const std::string& s = GetElementName(); nuclear@0: if (s == "fv1" || s == "lv1" || s == "pv1") { nuclear@0: ReadFaceVertex(t,tf[0]); nuclear@0: has[0] = true; nuclear@0: } nuclear@0: else if (s == "fv2" || s == "lv2") { nuclear@0: ReadFaceVertex(t,tf[1]); nuclear@0: has[1] = true; nuclear@0: } nuclear@0: else if (s == "fv3") { nuclear@0: ReadFaceVertex(t,tf[2]); nuclear@0: has[2] = true; nuclear@0: } nuclear@0: else if (s == "mat") { nuclear@0: if (mid != ~0u) { nuclear@0: LogWarn("only one material tag allowed per "); nuclear@0: } nuclear@0: mid = ResolveMaterialRef(scope); nuclear@0: } nuclear@0: else if (s == "matref") { nuclear@0: if (mid != ~0u) { nuclear@0: LogWarn("only one material tag allowed per "); nuclear@0: } nuclear@0: mid = ResolveMaterialRef(scope); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if (mid == ~0u) { nuclear@0: ThrowException("missing material index"); nuclear@0: } nuclear@0: nuclear@0: bool nor = false; nuclear@0: bool uv = false; nuclear@0: for(unsigned int i = 0; i < vcount; ++i) { nuclear@0: if (!has[i]) { nuclear@0: ThrowException("missing face vertex data"); nuclear@0: } nuclear@0: nuclear@0: nor = nor || tf[i].has_normal; nuclear@0: uv = uv || tf[i].has_uv; nuclear@0: } nuclear@0: nuclear@0: if (mid >= (1<<30)) { nuclear@0: LogWarn("material indices exhausted, this may cause errors in the output"); nuclear@0: } nuclear@0: unsigned int meshId = mid | ((nor?1:0)<<31) | ((uv?1:0)<<30); nuclear@0: nuclear@0: TempMaterialMesh& mesh = bymat[meshId]; nuclear@0: mesh.matid = mid; nuclear@0: nuclear@0: for(unsigned int i = 0; i < vcount; ++i) { nuclear@0: mesh.positions.push_back(tf[i].pos); nuclear@0: if(nor) { nuclear@0: mesh.normals.push_back(tf[i].normal); nuclear@0: } nuclear@0: if(uv) { nuclear@0: mesh.uvs.push_back(tf[i].uv); nuclear@0: } nuclear@0: nuclear@0: mesh.pflags |= 1 << (vcount-1); nuclear@0: } nuclear@0: nuclear@0: mesh.vcounts.push_back(vcount); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // finally extract output meshes and add them to the scope nuclear@0: typedef std::pair pairt; nuclear@0: BOOST_FOREACH(const pairt& p, bymat) { nuclear@0: aiMesh* const m = ToOutputMesh(p.second); nuclear@0: scope.meshes_linear.push_back(m); nuclear@0: nuclear@0: // if this is a definition, keep it on the stack nuclear@0: if(mesh_id != ~0u) { nuclear@0: scope.meshes.insert(std::pair(mesh_id,m)); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // no id == not a reference, insert this mesh right *here* nuclear@0: return mesh_id == ~0u; nuclear@0: } nuclear@0: nuclear@0: // ---------------------------------------------------------------------------------------------- nuclear@0: unsigned int XGLImporter::ResolveMaterialRef(TempScope& scope) nuclear@0: { nuclear@0: const std::string& s = GetElementName(); nuclear@0: if (s == "mat") { nuclear@0: ReadMaterial(scope); nuclear@0: return scope.materials_linear.size()-1; nuclear@0: } nuclear@0: nuclear@0: const int id = ReadIndexFromText(); nuclear@0: nuclear@0: std::map::iterator it = scope.materials.find(id), end = scope.materials.end(); nuclear@0: if (it == end) { nuclear@0: ThrowException(" index out of range"); nuclear@0: } nuclear@0: nuclear@0: // ok, this is n^2 and should get optimized one day nuclear@0: aiMaterial* const m = (*it).second; nuclear@0: nuclear@0: unsigned int i = 0, mcount = static_cast(scope.materials_linear.size()); nuclear@0: for(; i < mcount; ++i) { nuclear@0: if (scope.materials_linear[i] == m) { nuclear@0: return i; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: ai_assert(false); nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void XGLImporter::ReadMaterial(TempScope& scope) nuclear@0: { nuclear@0: const unsigned int mat_id = ReadIDAttr(); nuclear@0: nuclear@0: ScopeGuard mat(new aiMaterial()); nuclear@0: while (ReadElementUpToClosing("mat")) { nuclear@0: const std::string& s = GetElementName(); nuclear@0: if (s == "amb") { nuclear@0: const aiColor3D c = ReadCol3(); nuclear@0: mat->AddProperty(&c,1,AI_MATKEY_COLOR_AMBIENT); nuclear@0: } nuclear@0: else if (s == "diff") { nuclear@0: const aiColor3D c = ReadCol3(); nuclear@0: mat->AddProperty(&c,1,AI_MATKEY_COLOR_DIFFUSE); nuclear@0: } nuclear@0: else if (s == "spec") { nuclear@0: const aiColor3D c = ReadCol3(); nuclear@0: mat->AddProperty(&c,1,AI_MATKEY_COLOR_SPECULAR); nuclear@0: } nuclear@0: else if (s == "emiss") { nuclear@0: const aiColor3D c = ReadCol3(); nuclear@0: mat->AddProperty(&c,1,AI_MATKEY_COLOR_EMISSIVE); nuclear@0: } nuclear@0: else if (s == "alpha") { nuclear@0: const float f = ReadFloat(); nuclear@0: mat->AddProperty(&f,1,AI_MATKEY_OPACITY); nuclear@0: } nuclear@0: else if (s == "shine") { nuclear@0: const float f = ReadFloat(); nuclear@0: mat->AddProperty(&f,1,AI_MATKEY_SHININESS); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: scope.materials[mat_id] = mat; nuclear@0: scope.materials_linear.push_back(mat.dismiss()); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ---------------------------------------------------------------------------------------------- nuclear@0: void XGLImporter::ReadFaceVertex(const TempMesh& t, TempFace& out) nuclear@0: { nuclear@0: const std::string& end = GetElementName(); nuclear@0: nuclear@0: bool havep = false; nuclear@0: while (ReadElementUpToClosing(end.c_str())) { nuclear@0: const std::string& s = GetElementName(); nuclear@0: if (s == "pref") { nuclear@0: const unsigned int id = ReadIndexFromText(); nuclear@0: std::map::const_iterator it = t.points.find(id); nuclear@0: if (it == t.points.end()) { nuclear@0: ThrowException("point index out of range"); nuclear@0: } nuclear@0: nuclear@0: out.pos = (*it).second; nuclear@0: havep = true; nuclear@0: } nuclear@0: else if (s == "nref") { nuclear@0: const unsigned int id = ReadIndexFromText(); nuclear@0: std::map::const_iterator it = t.normals.find(id); nuclear@0: if (it == t.normals.end()) { nuclear@0: ThrowException("normal index out of range"); nuclear@0: } nuclear@0: nuclear@0: out.normal = (*it).second; nuclear@0: out.has_normal = true; nuclear@0: } nuclear@0: else if (s == "tcref") { nuclear@0: const unsigned int id = ReadIndexFromText(); nuclear@0: std::map::const_iterator it = t.uvs.find(id); nuclear@0: if (it == t.uvs.end()) { nuclear@0: ThrowException("uv index out of range"); nuclear@0: } nuclear@0: nuclear@0: out.uv = (*it).second; nuclear@0: out.has_uv = true; nuclear@0: } nuclear@0: else if (s == "p") { nuclear@0: out.pos = ReadVec3(); nuclear@0: } nuclear@0: else if (s == "n") { nuclear@0: out.normal = ReadVec3(); nuclear@0: } nuclear@0: else if (s == "tc") { nuclear@0: out.uv = ReadVec2(); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if (!havep) { nuclear@0: ThrowException("missing in element"); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: unsigned int XGLImporter::ReadIDAttr() nuclear@0: { nuclear@0: for(int i = 0, e = reader->getAttributeCount(); i < e; ++i) { nuclear@0: nuclear@0: if(!ASSIMP_stricmp(reader->getAttributeName(i),"id")) { nuclear@0: return reader->getAttributeValueAsInt(i); nuclear@0: } nuclear@0: } nuclear@0: return ~0u; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: float XGLImporter::ReadFloat() nuclear@0: { nuclear@0: if(!SkipToText()) { nuclear@0: LogError("unexpected EOF reading float element contents"); nuclear@0: return 0.f; nuclear@0: } nuclear@0: const char* s = reader->getNodeData(), *se; nuclear@0: nuclear@0: if(!SkipSpaces(&s)) { nuclear@0: LogError("unexpected EOL, failed to parse float"); nuclear@0: return 0.f; nuclear@0: } nuclear@0: nuclear@0: float t; nuclear@0: se = fast_atoreal_move(s,t); nuclear@0: nuclear@0: if (se == s) { nuclear@0: LogError("failed to read float text"); nuclear@0: return 0.f; nuclear@0: } nuclear@0: nuclear@0: return t; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: unsigned int XGLImporter::ReadIndexFromText() nuclear@0: { nuclear@0: if(!SkipToText()) { nuclear@0: LogError("unexpected EOF reading index element contents"); nuclear@0: return ~0u; nuclear@0: } nuclear@0: const char* s = reader->getNodeData(), *se; nuclear@0: if(!SkipSpaces(&s)) { nuclear@0: LogError("unexpected EOL, failed to parse index element"); nuclear@0: return ~0u; nuclear@0: } nuclear@0: nuclear@0: const unsigned int t = strtoul10(s,&se); nuclear@0: nuclear@0: if (se == s) { nuclear@0: LogError("failed to read index"); nuclear@0: return ~0u; nuclear@0: } nuclear@0: nuclear@0: return t; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: aiVector2D XGLImporter::ReadVec2() nuclear@0: { nuclear@0: aiVector2D vec; nuclear@0: nuclear@0: if(!SkipToText()) { nuclear@0: LogError("unexpected EOF reading vec2 contents"); nuclear@0: return vec; nuclear@0: } nuclear@0: const char* s = reader->getNodeData(); nuclear@0: nuclear@0: for(int i = 0; i < 2; ++i) { nuclear@0: if(!SkipSpaces(&s)) { nuclear@0: LogError("unexpected EOL, failed to parse vec2"); nuclear@0: return vec; nuclear@0: } nuclear@0: vec[i] = fast_atof(&s); nuclear@0: nuclear@0: SkipSpaces(&s); nuclear@0: if (i != 1 && *s != ',') { nuclear@0: LogError("expected comma, failed to parse vec2"); nuclear@0: return vec; nuclear@0: } nuclear@0: ++s; nuclear@0: } nuclear@0: nuclear@0: return vec; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: aiVector3D XGLImporter::ReadVec3() nuclear@0: { nuclear@0: aiVector3D vec; nuclear@0: nuclear@0: if(!SkipToText()) { nuclear@0: LogError("unexpected EOF reading vec3 contents"); nuclear@0: return vec; nuclear@0: } nuclear@0: const char* s = reader->getNodeData(); nuclear@0: nuclear@0: for(int i = 0; i < 3; ++i) { nuclear@0: if(!SkipSpaces(&s)) { nuclear@0: LogError("unexpected EOL, failed to parse vec3"); nuclear@0: return vec; nuclear@0: } nuclear@0: vec[i] = fast_atof(&s); nuclear@0: nuclear@0: SkipSpaces(&s); nuclear@0: if (i != 2 && *s != ',') { nuclear@0: LogError("expected comma, failed to parse vec3"); nuclear@0: return vec; nuclear@0: } nuclear@0: ++s; nuclear@0: } nuclear@0: nuclear@0: return vec; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: aiColor3D XGLImporter::ReadCol3() nuclear@0: { nuclear@0: const aiVector3D& v = ReadVec3(); nuclear@0: if (v.x < 0.f || v.x > 1.0f || v.y < 0.f || v.y > 1.0f || v.z < 0.f || v.z > 1.0f) { nuclear@0: LogWarn("color values out of range, ignoring"); nuclear@0: } nuclear@0: return aiColor3D(v.x,v.y,v.z); nuclear@0: } nuclear@0: nuclear@0: #endif