162 lines
5.5 KiB
C++
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;
|
|
}
|
|
|
|
}
|