diff options
author | sanine <sanine.not@pm.me> | 2022-04-16 11:55:09 -0500 |
---|---|---|
committer | sanine <sanine.not@pm.me> | 2022-04-16 11:55:09 -0500 |
commit | db81b925d776103326128bf629cbdda576a223e7 (patch) | |
tree | 58bea8155c686733310009f6bed7363f91fbeb9d /libs/assimp/code/AssetLib/XGL/XGLLoader.cpp | |
parent | 55860037b14fb3893ba21cf2654c83d349cc1082 (diff) |
move 3rd-party librarys into libs/ and add built-in honeysuckle
Diffstat (limited to 'libs/assimp/code/AssetLib/XGL/XGLLoader.cpp')
-rw-r--r-- | libs/assimp/code/AssetLib/XGL/XGLLoader.cpp | 787 |
1 files changed, 787 insertions, 0 deletions
diff --git a/libs/assimp/code/AssetLib/XGL/XGLLoader.cpp b/libs/assimp/code/AssetLib/XGL/XGLLoader.cpp new file mode 100644 index 0000000..154af98 --- /dev/null +++ b/libs/assimp/code/AssetLib/XGL/XGLLoader.cpp @@ -0,0 +1,787 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2022, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the XGL/ZGL importer class */ + +#ifndef ASSIMP_BUILD_NO_XGL_IMPORTER + +#include "XGLLoader.h" +#include "Common/Compression.h" + +#include <assimp/ParsingUtils.h> +#include <assimp/fast_atof.h> +#include <assimp/MemoryIOWrapper.h> +#include <assimp/StreamReader.h> +#include <assimp/importerdesc.h> +#include <assimp/mesh.h> +#include <assimp/scene.h> +//#include <cctype> +//#include <memory> + +using namespace Assimp; + +namespace Assimp { // this has to be in here because LogFunctions is in ::Assimp + +template <> +const char *LogFunctions<XGLImporter>::Prefix() { + static auto prefix = "XGL: "; + return prefix; +} + +} // namespace Assimp + +static const aiImporterDesc desc = { + "XGL Importer", + "", + "", + "", + aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportCompressedFlavour, + 0, + 0, + 0, + 0, + "xgl zgl" +}; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +XGLImporter::XGLImporter() : + mXmlParser(nullptr), + m_scene(nullptr) { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +XGLImporter::~XGLImporter() { + delete mXmlParser; +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the class can handle the format of the given file. +bool XGLImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { + static const char *tokens[] = { "<world>", "<World>", "<WORLD>" }; + return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); +} + +// ------------------------------------------------------------------------------------------------ +// Get a list of all file extensions which are handled by this class +const aiImporterDesc *XGLImporter::GetInfo() const { + return &desc; +} + +// ------------------------------------------------------------------------------------------------ +// Imports the given file into the given scene structure. +void XGLImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { + #ifndef ASSIMP_BUILD_NO_COMPRESSED_XGL + std::vector<char> uncompressed; +#endif + + m_scene = pScene; + std::shared_ptr<IOStream> stream(pIOHandler->Open(pFile, "rb")); + + // check whether we can read from the file + if (stream.get() == NULL) { + throw DeadlyImportError("Failed to open XGL/ZGL file " + pFile); + } + + // see if its compressed, if so uncompress it + if (GetExtension(pFile) == "zgl") { +#ifdef ASSIMP_BUILD_NO_COMPRESSED_XGL + ThrowException("Cannot read ZGL file since Assimp was built without compression support"); +#else + std::unique_ptr<StreamReaderLE> raw_reader(new StreamReaderLE(stream)); + + Compression compression; + size_t total = 0l; + if (compression.open(Compression::Format::Binary, Compression::FlushMode::NoFlush, -Compression::MaxWBits)) { + // skip two extra bytes, zgl files do carry a crc16 upfront (I think) + raw_reader->IncPtr(2); + total = compression.decompress((unsigned char *)raw_reader->GetPtr(), raw_reader->GetRemainingSize(), uncompressed); + compression.close(); + } + // replace the input stream with a memory stream + stream.reset(new MemoryIOStream(reinterpret_cast<uint8_t*>(uncompressed.data()), total)); +#endif + } + + // parse the XML file + mXmlParser = new XmlParser; + if (!mXmlParser->parse(stream.get())) { + throw DeadlyImportError("XML parse error while loading XGL file ", pFile); + } + + TempScope scope; + XmlNode *worldNode = mXmlParser->findNode("WORLD"); + if (nullptr != worldNode) { + ReadWorld(*worldNode, scope); + } + + std::vector<aiMesh *> &meshes = scope.meshes_linear; + std::vector<aiMaterial *> &materials = scope.materials_linear; + if (!meshes.size() || !materials.size()) { + ThrowException("failed to extract data from XGL file, no meshes loaded"); + } + + // copy meshes + m_scene->mNumMeshes = static_cast<unsigned int>(meshes.size()); + m_scene->mMeshes = new aiMesh *[m_scene->mNumMeshes](); + std::copy(meshes.begin(), meshes.end(), m_scene->mMeshes); + + // copy materials + m_scene->mNumMaterials = static_cast<unsigned int>(materials.size()); + m_scene->mMaterials = new aiMaterial *[m_scene->mNumMaterials](); + std::copy(materials.begin(), materials.end(), m_scene->mMaterials); + + if (scope.light) { + m_scene->mNumLights = 1; + m_scene->mLights = new aiLight *[1]; + m_scene->mLights[0] = scope.light; + + scope.light->mName = m_scene->mRootNode->mName; + } + + scope.dismiss(); +} + +// ------------------------------------------------------------------------------------------------ +void XGLImporter::ReadWorld(XmlNode &node, TempScope &scope) { + for (XmlNode ¤tNode : node.children()) { + const std::string &s = ai_stdStrToLower(currentNode.name()); + + // XXX right now we'd skip <lighting> if it comes after + // <object> or <mesh> + if (s == "lighting") { + ReadLighting(currentNode, scope); + } else if (s == "object" || s == "mesh" || s == "mat") { + break; + } + } + + aiNode *const nd = ReadObject(node, scope); + if (!nd) { + ThrowException("failure reading <world>"); + } + if (nd->mName.length == 0) { + nd->mName.Set("WORLD"); + } + + m_scene->mRootNode = nd; +} + +// ------------------------------------------------------------------------------------------------ +void XGLImporter::ReadLighting(XmlNode &node, TempScope &scope) { + const std::string &s = ai_stdStrToLower(node.name()); + if (s == "directionallight") { + scope.light = ReadDirectionalLight(node); + } else if (s == "ambient") { + LogWarn("ignoring <ambient> tag"); + } else if (s == "spheremap") { + LogWarn("ignoring <spheremap> tag"); + } +} + +// ------------------------------------------------------------------------------------------------ +aiLight *XGLImporter::ReadDirectionalLight(XmlNode &node) { + std::unique_ptr<aiLight> l(new aiLight()); + l->mType = aiLightSource_DIRECTIONAL; + find_node_by_name_predicate predicate("directionallight"); + XmlNode child = node.find_child(predicate); + if (child.empty()) { + return nullptr; + } + + const std::string &s = ai_stdStrToLower(child.name()); + if (s == "direction") { + l->mDirection = ReadVec3(child); + } else if (s == "diffuse") { + l->mColorDiffuse = ReadCol3(child); + } else if (s == "specular") { + l->mColorSpecular = ReadCol3(child); + } + + return l.release(); +} + +// ------------------------------------------------------------------------------------------------ +aiNode *XGLImporter::ReadObject(XmlNode &node, TempScope &scope) { + aiNode *nd = new aiNode; + std::vector<aiNode *> children; + std::vector<unsigned int> meshes; + + try { + for (XmlNode &child : node.children()) { + const std::string &s = ai_stdStrToLower(child.name()); + if (s == "mesh") { + const size_t prev = scope.meshes_linear.size(); + bool empty; + if (ReadMesh(child, scope, empty)) { + const size_t newc = scope.meshes_linear.size(); + for (size_t i = 0; i < newc - prev; ++i) { + meshes.push_back(static_cast<unsigned int>(i + prev)); + } + } + } else if (s == "mat") { + ReadMaterial(child, scope); + } else if (s == "object") { + children.push_back(ReadObject(child, scope)); + } else if (s == "objectref") { + // XXX + } else if (s == "meshref") { + const unsigned int id = static_cast<unsigned int>(ReadIndexFromText(child)); + + std::multimap<unsigned int, aiMesh *>::iterator it = scope.meshes.find(id), end = scope.meshes.end(); + if (it == end) { + ThrowException("<meshref> index out of range"); + } + + for (; it != end && (*it).first == id; ++it) { + // ok, this is n^2 and should get optimized one day + aiMesh *const m = it->second; + unsigned int i = 0, mcount = static_cast<unsigned int>(scope.meshes_linear.size()); + for (; i < mcount; ++i) { + if (scope.meshes_linear[i] == m) { + meshes.push_back(i); + break; + } + } + + ai_assert(i < mcount); + } + } else if (s == "transform") { + nd->mTransformation = ReadTrafo(child); + } + } + } catch (...) { + for (aiNode *ch : children) { + delete ch; + } + throw; + } + + // FIX: since we used std::multimap<> to keep meshes by id, mesh order now depends on the behaviour + // of the multimap implementation with respect to the ordering of entries with same values. + // C++11 gives the guarantee that it uses insertion order, before it is implementation-specific. + // Sort by material id to always guarantee a deterministic result. + std::sort(meshes.begin(), meshes.end(), SortMeshByMaterialId(scope)); + + // link meshes to node + nd->mNumMeshes = static_cast<unsigned int>(meshes.size()); + if (0 != nd->mNumMeshes) { + nd->mMeshes = new unsigned int[nd->mNumMeshes](); + for (unsigned int i = 0; i < nd->mNumMeshes; ++i) { + nd->mMeshes[i] = meshes[i]; + } + } + + // link children to parent + nd->mNumChildren = static_cast<unsigned int>(children.size()); + if (nd->mNumChildren) { + nd->mChildren = new aiNode *[nd->mNumChildren](); + for (unsigned int i = 0; i < nd->mNumChildren; ++i) { + nd->mChildren[i] = children[i]; + children[i]->mParent = nd; + } + } + + return nd; +} + +// ------------------------------------------------------------------------------------------------ +aiMatrix4x4 XGLImporter::ReadTrafo(XmlNode &node) { + aiVector3D forward, up, right, position; + float scale = 1.0f; + + aiMatrix4x4 m; + XmlNode child = node.child("TRANSFORM"); + if (child.empty()) { + return m; + } + + for (XmlNode &sub_child : child.children()) { + const std::string &s = ai_stdStrToLower(sub_child.name()); + if (s == "forward") { + forward = ReadVec3(sub_child); + } else if (s == "up") { + up = ReadVec3(sub_child); + } else if (s == "position") { + position = ReadVec3(sub_child); + } + if (s == "scale") { + scale = ReadFloat(sub_child); + if (scale < 0.f) { + // this is wrong, but we can leave the value and pass it to the caller + LogError("found negative scaling in <transform>, ignoring"); + } + } + } + + if (forward.SquareLength() < 1e-4 || up.SquareLength() < 1e-4) { + LogError("A direction vector in <transform> is zero, ignoring trafo"); + return m; + } + + forward.Normalize(); + up.Normalize(); + + right = forward ^ up; + if (std::fabs(up * forward) > 1e-4) { + // this is definitely wrong - a degenerate coordinate space ruins everything + // so substitute identity transform. + LogError("<forward> and <up> vectors in <transform> are skewing, ignoring trafo"); + return m; + } + + right *= scale; + up *= scale; + forward *= scale; + + m.a1 = right.x; + m.b1 = right.y; + m.c1 = right.z; + + m.a2 = up.x; + m.b2 = up.y; + m.c2 = up.z; + + m.a3 = forward.x; + m.b3 = forward.y; + m.c3 = forward.z; + + m.a4 = position.x; + m.b4 = position.y; + m.c4 = position.z; + + return m; +} + +// ------------------------------------------------------------------------------------------------ +aiMesh *XGLImporter::ToOutputMesh(const TempMaterialMesh &m) { + std::unique_ptr<aiMesh> mesh(new aiMesh()); + + mesh->mNumVertices = static_cast<unsigned int>(m.positions.size()); + mesh->mVertices = new aiVector3D[mesh->mNumVertices]; + std::copy(m.positions.begin(), m.positions.end(), mesh->mVertices); + + if (!m.normals.empty()) { + mesh->mNormals = new aiVector3D[mesh->mNumVertices]; + std::copy(m.normals.begin(), m.normals.end(), mesh->mNormals); + } + + if (!m.uvs.empty()) { + mesh->mNumUVComponents[0] = 2; + mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices]; + + for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { + mesh->mTextureCoords[0][i] = aiVector3D(m.uvs[i].x, m.uvs[i].y, 0.f); + } + } + + mesh->mNumFaces = static_cast<unsigned int>(m.vcounts.size()); + mesh->mFaces = new aiFace[m.vcounts.size()]; + + unsigned int idx = 0; + for (unsigned int i = 0; i < mesh->mNumFaces; ++i) { + aiFace &f = mesh->mFaces[i]; + f.mNumIndices = m.vcounts[i]; + f.mIndices = new unsigned int[f.mNumIndices]; + for (unsigned int c = 0; c < f.mNumIndices; ++c) { + f.mIndices[c] = idx++; + } + } + + ai_assert(idx == mesh->mNumVertices); + + mesh->mPrimitiveTypes = m.pflags; + mesh->mMaterialIndex = m.matid; + + return mesh.release(); +} + +// ------------------------------------------------------------------------------------------------ +bool XGLImporter::ReadMesh(XmlNode &node, TempScope &scope, bool &empty) { + TempMesh t; + + std::map<unsigned int, TempMaterialMesh> bymat; + const unsigned int mesh_id = ReadIDAttr(node); + bool empty_mesh = true; + for (XmlNode &child : node.children()) { + const std::string &s = ai_stdStrToLower(child.name()); + + if (s == "mat") { + ReadMaterial(child, scope); + } else if (s == "p") { + pugi::xml_attribute attr = child.attribute("ID"); + if (attr.empty()) { + LogWarn("no ID attribute on <p>, ignoring"); + } else { + int id = attr.as_int(); + t.points[id] = ReadVec3(child); + } + } else if (s == "n") { + pugi::xml_attribute attr = child.attribute("ID"); + if (attr.empty()) { + LogWarn("no ID attribute on <n>, ignoring"); + } else { + int id = attr.as_int(); + t.normals[id] = ReadVec3(child); + } + } else if (s == "tc") { + pugi::xml_attribute attr = child.attribute("ID"); + if (attr.empty()) { + LogWarn("no ID attribute on <tc>, ignoring"); + } else { + int id = attr.as_int(); + t.uvs[id] = ReadVec2(child); + } + } else if (s == "f" || s == "l" || s == "p") { + const unsigned int vcount = s == "f" ? 3 : (s == "l" ? 2 : 1); + + unsigned int mid = ~0u; + TempFace tf[3]; + bool has[3] = { false }; + for (XmlNode &sub_child : child.children()) { + const std::string &scn = ai_stdStrToLower(sub_child.name()); + if (scn == "fv1" || scn == "lv1" || scn == "pv1") { + ReadFaceVertex(sub_child, t, tf[0]); + has[0] = true; + } else if (scn == "fv2" || scn == "lv2") { + ReadFaceVertex(sub_child, t, tf[1]); + has[1] = true; + } else if (scn == "fv3") { + ReadFaceVertex(sub_child, t, tf[2]); + has[2] = true; + } else if (scn == "mat") { + if (mid != ~0u) { + LogWarn("only one material tag allowed per <f>"); + } + mid = ResolveMaterialRef(sub_child, scope); + } else if (scn == "matref") { + if (mid != ~0u) { + LogWarn("only one material tag allowed per <f>"); + } + mid = ResolveMaterialRef(sub_child, scope); + } + } + if (has[0] || has[1] || has[2]) { + empty_mesh = false; + } + + if (mid == ~0u) { + ThrowException("missing material index"); + } + + bool nor = false; + bool uv = false; + for (unsigned int i = 0; i < vcount; ++i) { + if (!has[i]) { + ThrowException("missing face vertex data"); + } + + nor = nor || tf[i].has_normal; + uv = uv || tf[i].has_uv; + } + + if (mid >= (1 << 30)) { + LogWarn("material indices exhausted, this may cause errors in the output"); + } + unsigned int meshId = mid | ((nor ? 1 : 0) << 31) | ((uv ? 1 : 0) << 30); + + TempMaterialMesh &mesh = bymat[meshId]; + mesh.matid = mid; + + for (unsigned int i = 0; i < vcount; ++i) { + mesh.positions.push_back(tf[i].pos); + if (nor) { + mesh.normals.push_back(tf[i].normal); + } + if (uv) { + mesh.uvs.push_back(tf[i].uv); + } + + mesh.pflags |= 1 << (vcount - 1); + } + + mesh.vcounts.push_back(vcount); + } + } + + // finally extract output meshes and add them to the scope + using pairt = std::pair<const unsigned int, TempMaterialMesh>; + for (const pairt &p : bymat) { + aiMesh *const m = ToOutputMesh(p.second); + scope.meshes_linear.push_back(m); + + // if this is a definition, keep it on the stack + if (mesh_id != ~0u) { + scope.meshes.insert(std::pair<unsigned int, aiMesh *>(mesh_id, m)); + } + } + if (empty_mesh) { + LogWarn("Mesh is empty, skipping."); + empty = empty_mesh; + return false; + } + + // no id == not a reference, insert this mesh right *here* + return mesh_id == ~0u; +} + +// ---------------------------------------------------------------------------------------------- +unsigned int XGLImporter::ResolveMaterialRef(XmlNode &node, TempScope &scope) { + const std::string &s = node.name(); + if (s == "mat") { + ReadMaterial(node, scope); + return static_cast<unsigned int>(scope.materials_linear.size() - 1); + } + + const int id = ReadIndexFromText(node); + + auto it = scope.materials.find(id), end = scope.materials.end(); + if (it == end) { + ThrowException("<matref> index out of range"); + } + + // ok, this is n^2 and should get optimized one day + aiMaterial *const m = it->second; + + unsigned int i = 0, mcount = static_cast<unsigned int>(scope.materials_linear.size()); + for (; i < mcount; ++i) { + if (scope.materials_linear[i] == m) { + return i; + } + } + + ai_assert(false); + + return 0; +} + +// ------------------------------------------------------------------------------------------------ +void XGLImporter::ReadMaterial(XmlNode &node, TempScope &scope) { + const unsigned int mat_id = ReadIDAttr(node); + + auto *mat(new aiMaterial); + for (XmlNode &child : node.children()) { + const std::string &s = ai_stdStrToLower(child.name()); + if (s == "amb") { + const aiColor3D c = ReadCol3(child); + mat->AddProperty(&c, 1, AI_MATKEY_COLOR_AMBIENT); + } else if (s == "diff") { + const aiColor3D c = ReadCol3(child); + mat->AddProperty(&c, 1, AI_MATKEY_COLOR_DIFFUSE); + } else if (s == "spec") { + const aiColor3D c = ReadCol3(child); + mat->AddProperty(&c, 1, AI_MATKEY_COLOR_SPECULAR); + } else if (s == "emiss") { + const aiColor3D c = ReadCol3(child); + mat->AddProperty(&c, 1, AI_MATKEY_COLOR_EMISSIVE); + } else if (s == "alpha") { + const float f = ReadFloat(child); + mat->AddProperty(&f, 1, AI_MATKEY_OPACITY); + } else if (s == "shine") { + const float f = ReadFloat(child); + mat->AddProperty(&f, 1, AI_MATKEY_SHININESS); + } + } + + scope.materials[mat_id] = mat; + scope.materials_linear.push_back(mat); +} + +// ---------------------------------------------------------------------------------------------- +void XGLImporter::ReadFaceVertex(XmlNode &node, const TempMesh &t, TempFace &out) { + bool havep = false; + for (XmlNode &child : node.children()) { + const std::string &s = ai_stdStrToLower(child.name()); + if (s == "pref") { + const unsigned int id = ReadIndexFromText(child); + std::map<unsigned int, aiVector3D>::const_iterator it = t.points.find(id); + if (it == t.points.end()) { + ThrowException("point index out of range"); + } + + out.pos = (*it).second; + havep = true; + } else if (s == "nref") { + const unsigned int id = ReadIndexFromText(child); + std::map<unsigned int, aiVector3D>::const_iterator it = t.normals.find(id); + if (it == t.normals.end()) { + ThrowException("normal index out of range"); + } + + out.normal = (*it).second; + out.has_normal = true; + } else if (s == "tcref") { + const unsigned int id = ReadIndexFromText(child); + std::map<unsigned int, aiVector2D>::const_iterator it = t.uvs.find(id); + if (it == t.uvs.end()) { + ThrowException("uv index out of range"); + } + + out.uv = (*it).second; + out.has_uv = true; + } else if (s == "p") { + out.pos = ReadVec3(child); + } else if (s == "n") { + out.normal = ReadVec3(child); + } else if (s == "tc") { + out.uv = ReadVec2(child); + } + } + + if (!havep) { + ThrowException("missing <pref> in <fvN> element"); + } +} + +// ------------------------------------------------------------------------------------------------ +unsigned int XGLImporter::ReadIDAttr(XmlNode &node) { + for (pugi::xml_attribute attr : node.attributes()) { + if (!ASSIMP_stricmp(attr.name(), "id")) { + return attr.as_int(); + } + } + + return ~0u; +} + +// ------------------------------------------------------------------------------------------------ +float XGLImporter::ReadFloat(XmlNode &node) { + std::string v; + XmlParser::getValueAsString(node, v); + const char *s = v.c_str(), *se; + if (!SkipSpaces(&s)) { + LogError("unexpected EOL, failed to parse index element"); + return 0.f; + } + float t; + se = fast_atoreal_move(s, t); + if (se == s) { + LogError("failed to read float text"); + return 0.f; + } + + return t; +} + +// ------------------------------------------------------------------------------------------------ +unsigned int XGLImporter::ReadIndexFromText(XmlNode &node) { + std::string v; + XmlParser::getValueAsString(node, v); + const char *s = v.c_str(); + if (!SkipSpaces(&s)) { + LogError("unexpected EOL, failed to parse index element"); + return ~0u; + } + const char *se; + const unsigned int t = strtoul10(s, &se); + + if (se == s) { + LogError("failed to read index"); + return ~0u; + } + + return t; +} + +// ------------------------------------------------------------------------------------------------ +aiVector2D XGLImporter::ReadVec2(XmlNode &node) { + aiVector2D vec; + std::string val; + XmlParser::getValueAsString(node, val); + const char *s = val.c_str(); + ai_real v[2] = {}; + for (int i = 0; i < 2; ++i) { + if (!SkipSpaces(&s)) { + LogError("unexpected EOL, failed to parse vec2"); + return vec; + } + + v[i] = fast_atof(&s); + + SkipSpaces(&s); + if (i != 1 && *s != ',') { + LogError("expected comma, failed to parse vec2"); + return vec; + } + ++s; + } + vec.x = v[0]; + vec.y = v[1]; + + return vec; +} + +// ------------------------------------------------------------------------------------------------ +aiVector3D XGLImporter::ReadVec3(XmlNode &node) { + aiVector3D vec; + std::string v; + XmlParser::getValueAsString(node, v); + const char *s = v.c_str(); + for (int i = 0; i < 3; ++i) { + if (!SkipSpaces(&s)) { + LogError("unexpected EOL, failed to parse vec3"); + return vec; + } + vec[i] = fast_atof(&s); + + SkipSpaces(&s); + if (i != 2 && *s != ',') { + LogError("expected comma, failed to parse vec3"); + return vec; + } + ++s; + } + + return vec; +} + +// ------------------------------------------------------------------------------------------------ +aiColor3D XGLImporter::ReadCol3(XmlNode &node) { + const aiVector3D &v = ReadVec3(node); + 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) { + LogWarn("color values out of range, ignoring"); + } + return aiColor3D(v.x, v.y, v.z); +} + +#endif // ASSIMP_BUILD_NO_XGL_IMPORTER |