#include #include #include #define RAPIDJSON_ASSERT(x) if (!(x)) throw ::std::runtime_error("Error parsing glTF: " BOOST_PP_STRINGIZE(x)); #define RAPIDJSON_NOEXCEPT_ASSERT(x) #include #include 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(); target.name = material["name"].GetString(); 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(); } if (material.HasMember("emissiveFactor")) { auto const & emission = material["emissiveFactor"].GetArray(); gfx::color_3f & target_emission = target.emission.emplace(); for (std::size_t i = 0; i < 3; ++i) target_emission[i] = emission[i].GetFloat(); } } 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; } }