Support loading binary shader source data for binary glTF.

This commit is contained in:
Syoyo Fujita 2016-06-15 20:58:42 +09:00
parent 4904852e4e
commit 3cdfb3bd07

View File

@ -1,7 +1,7 @@
// //
// Tiny glTF loader. // Tiny glTF loader.
// //
// Copyright (c) 2015-2016, Syoyo Fujita. // Copyright (c) 2015-2016, Syoyo Fujita and many contributors.
// All rights reserved. // All rights reserved.
// (Licensed under 2-clause BSD liecense) // (Licensed under 2-clause BSD liecense)
// //
@ -31,6 +31,8 @@
// //
// //
// Version: // Version:
// - v0.9.4 Support parsing `shader`, `program` and `tecnique` thanks to @lukesanantonio
// - v0.9.3 Support binary glTF
// - v0.9.2 Support parsing `texture` // - v0.9.2 Support parsing `texture`
// - v0.9.1 Support loading glTF asset from memory // - v0.9.1 Support loading glTF asset from memory
// - v0.9.0 Initial // - v0.9.0 Initial
@ -245,8 +247,7 @@ typedef struct {
std::vector<std::string> attributes; std::vector<std::string> attributes;
} Program; } Program;
typedef struct typedef struct {
{
int count; int count;
std::string node; std::string node;
std::string semantic; std::string semantic;
@ -939,10 +940,8 @@ static bool ParseStringArrayProperty(std::vector<std::string> *ret,
} }
static bool ParseStringMapProperty(std::map<std::string, std::string> *ret, static bool ParseStringMapProperty(std::map<std::string, std::string> *ret,
std::string *err, std::string *err, const picojson::object &o,
const picojson::object &o, const std::string &property, bool required) {
const std::string &property,
bool required) {
picojson::object::const_iterator it = o.find(property); picojson::object::const_iterator it = o.find(property);
if (it == o.end()) { if (it == o.end()) {
if (required) { if (required) {
@ -1140,7 +1139,7 @@ static bool ParseImage(Image *image, std::string *err,
if (IsDataURI(uri)) { if (IsDataURI(uri)) {
if (!DecodeDataURI(&img, uri, 0, false)) { if (!DecodeDataURI(&img, uri, 0, false)) {
if (err) { if (err) {
(*err) += "Failed to decode 'uri'.\n"; (*err) += "Failed to decode 'uri' for image parameter.\n";
} }
return false; return false;
} }
@ -1148,13 +1147,13 @@ static bool ParseImage(Image *image, std::string *err,
// Assume external file // Assume external file
if (!LoadExternalFile(&img, err, uri, basedir, 0, false)) { if (!LoadExternalFile(&img, err, uri, basedir, 0, false)) {
if (err) { if (err) {
(*err) += "Failed to load external 'uri'.\n"; (*err) += "Failed to load external 'uri'. for image parameter\n";
} }
return false; return false;
} }
if (img.empty()) { if (img.empty()) {
if (err) { if (err) {
(*err) += "File is empty.\n"; (*err) += "Image is empty.\n";
} }
return false; return false;
} }
@ -1250,8 +1249,8 @@ static bool ParseBuffer(Buffer *buffer, std::string *err,
if (err) { if (err) {
std::stringstream ss; std::stringstream ss;
ss << "Invalid `byteLength'. Must be equal or less than binary size: " ss << "Invalid `byteLength'. Must be equal or less than binary size: "
"`byteLength' = " "`byteLength' = " << byteLength
<< byteLength << ", binary size = " << bin_size << std::endl; << ", binary size = " << bin_size << std::endl;
(*err) += ss.str(); (*err) += ss.str();
} }
return false; return false;
@ -1434,8 +1433,8 @@ static bool ParsePrimitive(Primitive *primitive, std::string *err,
primitive->indices = ""; primitive->indices = "";
ParseStringProperty(&primitive->indices, err, o, "indices", false); ParseStringProperty(&primitive->indices, err, o, "indices", false);
if(!ParseStringMapProperty(&primitive->attributes, err, o, if (!ParseStringMapProperty(&primitive->attributes, err, o, "attributes",
"attributes", true)) { true)) {
return false; return false;
} }
@ -1452,7 +1451,8 @@ static bool ParseMesh(Mesh *mesh, std::string *err, const picojson::object &o) {
(primObject->second).get<picojson::array>(); (primObject->second).get<picojson::array>();
for (size_t i = 0; i < primArray.size(); i++) { for (size_t i = 0; i < primArray.size(); i++) {
Primitive primitive; Primitive primitive;
if(ParsePrimitive(&primitive, err, primArray[i].get<picojson::object>())){ if (ParsePrimitive(&primitive, err,
primArray[i].get<picojson::object>())) {
// Only add the primitive if the parsing succeeds. // Only add the primitive if the parsing succeeds.
mesh->primitives.push_back(primitive); mesh->primitives.push_back(primitive);
} }
@ -1494,8 +1494,7 @@ static bool ParseNode(Node *node, std::string *err, const picojson::object &o) {
static bool ParseParameterProperty(Parameter *param, std::string *err, static bool ParseParameterProperty(Parameter *param, std::string *err,
const picojson::object &o, const picojson::object &o,
const std::string &prop, bool required) const std::string &prop, bool required) {
{
double num_val; double num_val;
// A parameter value can either be a string or an array of either a boolean or // A parameter value can either be a string or an array of either a boolean or
@ -1506,7 +1505,8 @@ static bool ParseParameterProperty(Parameter *param, std::string *err,
if (ParseStringProperty(&param->string_value, err, o, prop, false)) { if (ParseStringProperty(&param->string_value, err, o, prop, false)) {
// Found string property. // Found string property.
return true; return true;
} else if (ParseNumberArrayProperty(&param->number_array, err,o,prop,false)) { } else if (ParseNumberArrayProperty(&param->number_array, err, o, prop,
false)) {
// Found a number array. // Found a number array.
return true; return true;
} else if (ParseNumberProperty(&num_val, err, o, prop, false)) { } else if (ParseNumberProperty(&num_val, err, o, prop, false)) {
@ -1531,7 +1531,6 @@ static bool ParseMaterial(Material *material, std::string *err,
picojson::object::const_iterator valuesIt = o.find("values"); picojson::object::const_iterator valuesIt = o.find("values");
if ((valuesIt != o.end()) && (valuesIt->second).is<picojson::object>()) { if ((valuesIt != o.end()) && (valuesIt->second).is<picojson::object>()) {
const picojson::object &values_object = const picojson::object &values_object =
(valuesIt->second).get<picojson::object>(); (valuesIt->second).get<picojson::object>();
@ -1540,7 +1539,8 @@ static bool ParseMaterial(Material *material, std::string *err,
for (; it != itEnd; it++) { for (; it != itEnd; it++) {
Parameter param; Parameter param;
if(ParseParameterProperty(&param, err, values_object, it->first, false)) { if (ParseParameterProperty(&param, err, values_object, it->first,
false)) {
material->values[it->first] = param; material->values[it->first] = param;
} }
} }
@ -1550,18 +1550,64 @@ static bool ParseMaterial(Material *material, std::string *err,
} }
static bool ParseShader(Shader *shader, std::string *err, static bool ParseShader(Shader *shader, std::string *err,
const picojson::object &o, const std::string &basedir) { const picojson::object &o, const std::string &basedir,
bool is_binary = false,
const unsigned char *bin_data = NULL,
size_t bin_size = 0) {
std::string uri; std::string uri;
if (!ParseStringProperty(&uri, err, o, "uri", true)) { if (!ParseStringProperty(&uri, err, o, "uri", true)) {
return false; return false;
} }
if (is_binary) {
// Still binary glTF accepts external dataURI. First try external resources.
bool loaded = false;
if (IsDataURI(uri)) {
loaded = DecodeDataURI(&shader->source, uri, 0, false);
} else {
// Assume external .bin file.
loaded = LoadExternalFile(&shader->source, err, uri, basedir, 0, false);
}
if (!loaded) {
// load data from (embedded) binary data
if ((bin_size == 0) || (bin_data == NULL)) {
if (err) {
(*err) += "Invalid binary data.\n";
}
return false;
}
// There should be "extensions" property.
// "extensions":{"KHR_binary_glTF":{"bufferView": "id", ...
std::string buffer_view;
std::string mime_type;
int image_width;
int image_height;
bool ret = ParseKHRBinaryExtension(o, err, &buffer_view, &mime_type,
&image_width, &image_height);
if (!ret) {
return false;
}
if (uri.compare("data:,") == 0) {
// ok
} else {
if (err) {
(*err) += "Invalid URI for binary data.\n";
}
return false;
}
}
} else {
// Load shader source from data uri // Load shader source from data uri
// TODO: Support ascii or utf-8 data uris. // TODO: Support ascii or utf-8 data uris.
if (IsDataURI(uri)) { if (IsDataURI(uri)) {
if (!DecodeDataURI(&shader->source, uri, 0, false)) { if (!DecodeDataURI(&shader->source, uri, 0, false)) {
if (err) { if (err) {
(*err) += "Failed to decode 'uri'.\n"; (*err) += "Failed to decode 'uri' for shader parameter.\n";
} }
return false; return false;
} }
@ -1569,17 +1615,18 @@ static bool ParseShader(Shader *shader, std::string *err,
// Assume external file // Assume external file
if (!LoadExternalFile(&shader->source, err, uri, basedir, 0, false)) { if (!LoadExternalFile(&shader->source, err, uri, basedir, 0, false)) {
if (err) { if (err) {
(*err) += "Failed to load external 'uri'.\n"; (*err) += "Failed to load external 'uri' for shader parameter.\n";
} }
return false; return false;
} }
if (shader->source.empty()) { if (shader->source.empty()) {
if (err) { if (err) {
(*err) += "File is empty.\n"; (*err) += "shader is empty.\n"; // This may be OK?
} }
return false; return false;
} }
} }
}
double type; double type;
if (!ParseNumberProperty(&type, err, o, "type", true)) { if (!ParseNumberProperty(&type, err, o, "type", true)) {
@ -1595,11 +1642,12 @@ static bool ParseProgram(Program *program, std::string *err,
const picojson::object &o) { const picojson::object &o) {
ParseStringProperty(&program->name, err, o, "name", false); ParseStringProperty(&program->name, err, o, "name", false);
if(!ParseStringProperty(&program->vertexShader, err,o, "vertexShader",true)) { if (!ParseStringProperty(&program->vertexShader, err, o, "vertexShader",
true)) {
return false; return false;
} }
if(!ParseStringProperty(&program->fragmentShader, err, o, if (!ParseStringProperty(&program->fragmentShader, err, o, "fragmentShader",
"fragmentShader", true)) { true)) {
return false; return false;
} }
@ -1612,7 +1660,6 @@ static bool ParseProgram(Program *program, std::string *err,
static bool ParseTechniqueParameter(TechniqueParameter *param, std::string *err, static bool ParseTechniqueParameter(TechniqueParameter *param, std::string *err,
const picojson::object &o) { const picojson::object &o) {
double count = 1; double count = 1;
ParseNumberProperty(&count, err, o, "count", false); ParseNumberProperty(&count, err, o, "count", false);
@ -1648,7 +1695,6 @@ static bool ParseTechnique(Technique *technique, std::string *err,
// Verify parameters is an object // Verify parameters is an object
if ((paramsIt != o.end()) && (paramsIt->second).is<picojson::object>()) { if ((paramsIt != o.end()) && (paramsIt->second).is<picojson::object>()) {
// For each parameter in params_object. // For each parameter in params_object.
const picojson::object &params_object = const picojson::object &params_object =
(paramsIt->second).get<picojson::object>(); (paramsIt->second).get<picojson::object>();
@ -1928,7 +1974,7 @@ bool TinyGLTFLoader::LoadFromString(Scene *scene, std::string *err,
} }
} }
// 9. Parse Texture // 10. Parse Texture
if (v.contains("textures") && v.get("textures").is<picojson::object>()) { if (v.contains("textures") && v.get("textures").is<picojson::object>()) {
const picojson::object &root = v.get("textures").get<picojson::object>(); const picojson::object &root = v.get("textures").get<picojson::object>();
@ -1945,17 +1991,16 @@ bool TinyGLTFLoader::LoadFromString(Scene *scene, std::string *err,
} }
} }
// 10. Parse Shader // 11. Parse Shader
if (v.contains("shaders") && v.get("shaders").is<picojson::object>()) { if (v.contains("shaders") && v.get("shaders").is<picojson::object>()) {
const picojson::object &root = v.get("shaders").get<picojson::object>(); const picojson::object &root = v.get("shaders").get<picojson::object>();
picojson::object::const_iterator it(root.begin()); picojson::object::const_iterator it(root.begin());
picojson::object::const_iterator itEnd(root.end()); picojson::object::const_iterator itEnd(root.end());
for(; it != itEnd; ++it) for (; it != itEnd; ++it) {
{
Shader shader; Shader shader;
if (!ParseShader(&shader, err, (it->second).get<picojson::object>(), if (!ParseShader(&shader, err, (it->second).get<picojson::object>(),
base_dir)) { base_dir, is_binary_, bin_data_, bin_size_)) {
return false; return false;
} }
@ -1963,14 +2008,13 @@ bool TinyGLTFLoader::LoadFromString(Scene *scene, std::string *err,
} }
} }
// 11. Parse Program // 12. Parse Program
if (v.contains("programs") && v.get("programs").is<picojson::object>()) { if (v.contains("programs") && v.get("programs").is<picojson::object>()) {
const picojson::object &root = v.get("programs").get<picojson::object>(); const picojson::object &root = v.get("programs").get<picojson::object>();
picojson::object::const_iterator it(root.begin()); picojson::object::const_iterator it(root.begin());
picojson::object::const_iterator itEnd(root.end()); picojson::object::const_iterator itEnd(root.end());
for(; it != itEnd; ++it) for (; it != itEnd; ++it) {
{
Program program; Program program;
if (!ParseProgram(&program, err, (it->second).get<picojson::object>())) { if (!ParseProgram(&program, err, (it->second).get<picojson::object>())) {
return false; return false;
@ -1980,14 +2024,13 @@ bool TinyGLTFLoader::LoadFromString(Scene *scene, std::string *err,
} }
} }
// 12. Parse Technique // 13. Parse Technique
if (v.contains("techniques") && v.get("techniques").is<picojson::object>()) { if (v.contains("techniques") && v.get("techniques").is<picojson::object>()) {
const picojson::object &root = v.get("techniques").get<picojson::object>(); const picojson::object &root = v.get("techniques").get<picojson::object>();
picojson::object::const_iterator it(root.begin()); picojson::object::const_iterator it(root.begin());
picojson::object::const_iterator itEnd(root.end()); picojson::object::const_iterator itEnd(root.end());
for(; it != itEnd; ++it) for (; it != itEnd; ++it) {
{
Technique technique; Technique technique;
if (!ParseTechnique(&technique, err, if (!ParseTechnique(&technique, err,
(it->second).get<picojson::object>())) { (it->second).get<picojson::object>())) {