psemek/libs/gfx/source/gltf_parser.cpp

162 lines
5.5 KiB
C++

#include <psemek/gfx/gltf_parser.hpp>
#include <psemek/util/to_string.hpp>
#include <boost/preprocessor/stringize.hpp>
#define RAPIDJSON_ASSERT(x) if (!(x)) throw ::std::runtime_error("Error parsing glTF: " BOOST_PP_STRINGIZE(x));
#define RAPIDJSON_NOEXCEPT_ASSERT(x)
#include <rapidjson/document.h>
#include <rapidjson/istreamwrapper.h>
namespace psemek::gfx
{
namespace
{
std::string_view to_string(rapidjson::ParseErrorCode const & code)
{
using namespace rapidjson;
switch (code)
{
case kParseErrorNone: return "no error";
case kParseErrorDocumentEmpty: return "document is empty";
case kParseErrorDocumentRootNotSingular: return "document root must not be followed by other values";
case kParseErrorValueInvalid: return "invalid value";
case kParseErrorObjectMissName: return "missing a name for object member";
case kParseErrorObjectMissColon: return "missing a colon after a name of object member";
case kParseErrorObjectMissCommaOrCurlyBracket: return "missing a comma or '}' after an object member";
case kParseErrorArrayMissCommaOrSquareBracket: return "missing a comma or ']' after an array element";
case kParseErrorStringUnicodeEscapeInvalidHex: return R"(Incorrect hex digit after \\u escape in string)";
case kParseErrorStringUnicodeSurrogateInvalid: return "surrogate pair in string is invalid";
case kParseErrorStringEscapeInvalid: return "invalid escape character in string";
case kParseErrorStringMissQuotationMark: return "missing a closing quotation mark in string";
case kParseErrorStringInvalidEncoding: return "invalid encoding in string";
case kParseErrorNumberTooBig: return "number too big to be stored in double";
case kParseErrorNumberMissFraction: return "missing fraction part in number";
case kParseErrorNumberMissExponent: return "missing exponent in number";
case kParseErrorTermination: return "parsing was terminated";
case kParseErrorUnspecificSyntaxError: return "unspecific syntax error";
default: return "(unknown error)";
}
}
std::size_t parse_accessor_type(std::string const & type)
{
if (type == "SCALAR")
return 1;
if (type == "VEC2")
return 2;
if (type == "VEC3")
return 3;
if (type == "VEC4")
return 4;
throw std::runtime_error(util::to_string("Unknown accessor component type: ", type));
}
}
gltf_asset parse_gltf(io::istream && stream)
{
auto description_str = io::read_full(std::move(stream));
description_str.push_back(0);
rapidjson::Document document;
document.ParseInsitu(description_str.data());
if (document.HasParseError())
throw std::runtime_error(util::to_string("Error parsing glTF: ", to_string(document.GetParseError()), " at ", document.GetErrorOffset()));
gltf_asset result;
for (auto const & node : document["nodes"].GetArray())
{
auto & target = result.nodes.emplace_back();
target.mesh = node["mesh"].GetUint64();
target.name = node["name"].GetString();
}
for (auto const & mesh : document["meshes"].GetArray())
{
auto & target = result.meshes.emplace_back();
for (auto const & primitive : mesh["primitives"].GetArray())
{
auto & primitive_target = target.primitives.emplace_back();
primitive_target.indices = primitive["indices"].GetUint64();
if (primitive.HasMember("material"))
primitive_target.material = primitive["material"].GetUint64();
auto const & attributes = primitive["attributes"];
if (attributes.HasMember("POSITION"))
primitive_target.position = attributes["POSITION"].GetUint64();
if (attributes.HasMember("NORMAL"))
primitive_target.normal = attributes["NORMAL"].GetUint64();
if (attributes.HasMember("TEXCOORD_0"))
primitive_target.texcoord = attributes["TEXCOORD_0"].GetUint64();
}
}
for (auto const & material : document["materials"].GetArray())
{
auto & target = result.materials.emplace_back();
auto const & pbr = material["pbrMetallicRoughness"];
if (pbr.HasMember("baseColorFactor"))
{
auto const & color = pbr["baseColorFactor"].GetArray();
gfx::color_4f & target_color = target.albedo.emplace();
for (std::size_t i = 0; i < 4; ++i)
target_color[i] = color[i].GetFloat();
}
if (pbr.HasMember("baseColorTexture"))
{
target.texture = document["textures"].GetArray()[pbr["baseColorTexture"]["index"].GetUint64()]["source"].GetInt64();
}
}
for (auto const & image : document["images"].GetArray())
{
auto & target = result.textures.emplace_back();
target.uri = image["uri"].GetString();
}
for (auto const & accessor : document["accessors"].GetArray())
{
auto & target = result.accessors.emplace_back();
target.buffer_view = accessor["bufferView"].GetUint64();
target.component_type = accessor["componentType"].GetUint64();
target.count = accessor["count"].GetUint64();
target.type = parse_accessor_type(accessor["type"].GetString());
if (accessor.HasMember("normalized"))
target.normalized = accessor["normalized"].GetBool();
else
target.normalized = false;
}
for (auto const & buffer_view : document["bufferViews"].GetArray())
{
auto & target = result.buffer_views.emplace_back();
target.buffer = buffer_view["buffer"].GetUint64();
target.offset = buffer_view["byteOffset"].GetUint64();
target.length = buffer_view["byteLength"].GetUint64();
}
for (auto const & buffer : document["buffers"].GetArray())
{
auto & target = result.buffers.emplace_back();
target.length = buffer["byteLength"].GetUint64();
target.uri = buffer["uri"].GetString();
}
return result;
}
}