mirror of
https://github.com/syoyo/tinygltf.git
synced 2026-06-08 19:23:50 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a570c88d9 | ||
|
|
e12e0a9392 | ||
|
|
879cb473a3 | ||
|
|
c9657be1de | ||
|
|
95bbf15ce2 | ||
|
|
78864c8d3a | ||
|
|
14c86324d7 | ||
|
|
8d5d0b34be | ||
|
|
a1a34cb54d | ||
|
|
cfe64fb6c8 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -24,6 +24,7 @@ premake5.tar.gz
|
||||
#binary directories
|
||||
bin/
|
||||
obj/
|
||||
out/
|
||||
|
||||
#runtime gui config
|
||||
imgui.ini
|
||||
|
||||
548
tiny_gltf.h
548
tiny_gltf.h
@@ -53,10 +53,6 @@
|
||||
#define TINYGLTF_USE_CPP14
|
||||
#endif
|
||||
|
||||
#ifndef TINYGLTF_USE_CPP14
|
||||
#include <functional>
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
|
||||
#include <android/asset_manager.h>
|
||||
@@ -1043,18 +1039,19 @@ struct Mesh {
|
||||
|
||||
class Node {
|
||||
public:
|
||||
Node() : camera(-1), skin(-1), mesh(-1) {}
|
||||
Node() : camera(-1), skin(-1), mesh(-1), light(-1), emitter(-1) {}
|
||||
|
||||
DEFAULT_METHODS(Node)
|
||||
|
||||
bool operator==(const Node &) const;
|
||||
|
||||
int camera; // the index of the camera referenced by this node
|
||||
int camera{-1}; // the index of the camera referenced by this node
|
||||
|
||||
std::string name;
|
||||
int skin;
|
||||
int mesh;
|
||||
int light; // light source index (KHR_lights_punctual)
|
||||
int skin{-1};
|
||||
int mesh{-1};
|
||||
int light{-1}; // light source index (KHR_lights_punctual)
|
||||
int emitter{-1}; // audio emitter index (KHR_audio)
|
||||
std::vector<int> children;
|
||||
std::vector<double> rotation; // length must be 0 or 4
|
||||
std::vector<double> scale; // length must be 0 or 3
|
||||
@@ -1108,6 +1105,7 @@ struct Asset {
|
||||
struct Scene {
|
||||
std::string name;
|
||||
std::vector<int> nodes;
|
||||
std::vector<int> audioEmitters; // KHR_audio global emitters
|
||||
|
||||
ExtensionMap extensions;
|
||||
Value extras;
|
||||
@@ -1158,6 +1156,93 @@ struct Light {
|
||||
std::string extensions_json_string;
|
||||
};
|
||||
|
||||
struct PositionalEmitter {
|
||||
double coneInnerAngle{6.283185307179586};
|
||||
double coneOuterAngle{6.283185307179586};
|
||||
double coneOuterGain{0.0};
|
||||
double maxDistance{100.0};
|
||||
double refDistance{1.0};
|
||||
double rolloffFactor{1.0};
|
||||
|
||||
PositionalEmitter() : coneInnerAngle(6.283185307179586), coneOuterAngle(6.283185307179586) {}
|
||||
DEFAULT_METHODS(PositionalEmitter)
|
||||
bool operator==(const PositionalEmitter &) const;
|
||||
|
||||
ExtensionMap extensions;
|
||||
Value extras;
|
||||
|
||||
// Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
|
||||
std::string extras_json_string;
|
||||
std::string extensions_json_string;
|
||||
};
|
||||
|
||||
struct AudioEmitter {
|
||||
std::string name;
|
||||
double gain{1.0};
|
||||
bool loop{false};
|
||||
bool playing{false};
|
||||
std::string
|
||||
type; // positional - Positional audio emitters. Using sound cones, the
|
||||
// orientation is +Z having the same front side for a glTF asset.
|
||||
// global - Global audio emitters are not affected by the position
|
||||
// of audio listeners. coneInnerAngle, coneOuterAngle,
|
||||
// coneOuterGain, distanceModel, maxDistance, refDistance, and
|
||||
// rolloffFactor should all be ignored when set.
|
||||
std::string
|
||||
distanceModel; // linear - A linear distance model calculating the
|
||||
// gain induced by the distance according to: 1.0
|
||||
// - rolloffFactor * (distance - refDistance) /
|
||||
// (maxDistance - refDistance)
|
||||
// inverse - (default) An inverse distance model
|
||||
// calculating the gain induced by the distance according
|
||||
// to: refDistance / (refDistance + rolloffFactor *
|
||||
// (Math.max(distance, refDistance) - refDistance))
|
||||
// exponential - An exponential distance model calculating
|
||||
// the gain induced by the distance according to:
|
||||
// pow((Math.max(distance, refDistance) / refDistance,
|
||||
// -rolloffFactor))
|
||||
PositionalEmitter positional;
|
||||
int source{-1};
|
||||
|
||||
AudioEmitter()
|
||||
: gain(1.0),
|
||||
loop(false),
|
||||
playing(false),
|
||||
type("global"),
|
||||
distanceModel("inverse") {}
|
||||
DEFAULT_METHODS(AudioEmitter)
|
||||
|
||||
bool operator==(const AudioEmitter &) const;
|
||||
|
||||
ExtensionMap extensions;
|
||||
Value extras;
|
||||
|
||||
// Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
|
||||
std::string extras_json_string;
|
||||
std::string extensions_json_string;
|
||||
};
|
||||
|
||||
struct AudioSource {
|
||||
std::string name;
|
||||
std::string uri;
|
||||
int bufferView{-1}; // (required if no uri)
|
||||
std::string mimeType; // (required if no uri) The audio's MIME type. Required if
|
||||
// bufferView is defined. Unless specified by another
|
||||
// extension, the only supported mimeType is audio/mpeg.
|
||||
|
||||
AudioSource() {}
|
||||
DEFAULT_METHODS(AudioSource)
|
||||
|
||||
bool operator==(const AudioSource &) const;
|
||||
|
||||
Value extras;
|
||||
ExtensionMap extensions;
|
||||
|
||||
// Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
|
||||
std::string extras_json_string;
|
||||
std::string extensions_json_string;
|
||||
};
|
||||
|
||||
class Model {
|
||||
public:
|
||||
Model() = default;
|
||||
@@ -1179,6 +1264,8 @@ class Model {
|
||||
std::vector<Camera> cameras;
|
||||
std::vector<Scene> scenes;
|
||||
std::vector<Light> lights;
|
||||
std::vector<AudioEmitter> audioEmitters;
|
||||
std::vector<AudioSource> audioSources;
|
||||
|
||||
int defaultScene = -1;
|
||||
std::vector<std::string> extensionsUsed;
|
||||
@@ -1736,6 +1823,7 @@ namespace detail {
|
||||
// documents may be active at once.
|
||||
using json =
|
||||
rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
|
||||
using json_iterator = json::MemberIterator;
|
||||
using json_const_iterator = json::ConstMemberIterator;
|
||||
using json_const_array_iterator = json const *;
|
||||
using JsonDocument =
|
||||
@@ -1747,6 +1835,7 @@ rapidjson::CrtAllocator &GetAllocator() { return s_CrtAllocator; }
|
||||
// not thread safe. Only a single JsonDocument may be active at any one time,
|
||||
// meaning only a single gltf load/save can be active any one time.
|
||||
using json = rapidjson::Value;
|
||||
using json_iterator = json::MemberIterator;
|
||||
using json_const_iterator = json::ConstMemberIterator;
|
||||
using json_const_array_iterator = json const *;
|
||||
rapidjson::Document *s_pActiveDocument = nullptr;
|
||||
@@ -1793,6 +1882,7 @@ struct JsonDocument : public rapidjson::Document {
|
||||
|
||||
#else
|
||||
using nlohmann::json;
|
||||
using json_iterator = json::iterator;
|
||||
using json_const_iterator = json::const_iterator;
|
||||
using json_const_array_iterator = json_const_iterator;
|
||||
using JsonDocument = json;
|
||||
@@ -1951,6 +2041,15 @@ bool Light::operator==(const Light &other) const {
|
||||
return Equals(this->color, other.color) && this->name == other.name &&
|
||||
this->type == other.type;
|
||||
}
|
||||
bool AudioEmitter::operator==(const AudioEmitter &other) const {
|
||||
return this->name == other.name && TINYGLTF_DOUBLE_EQUAL(this->gain, other.gain) &&
|
||||
this->loop == other.loop && this->playing == other.playing &&
|
||||
this->type == other.type && this->distanceModel == other.distanceModel &&
|
||||
this->source == other.source;
|
||||
}
|
||||
bool AudioSource::operator == (const AudioSource &other) const {
|
||||
return this->name == other.name && this->uri == other.uri;
|
||||
}
|
||||
bool Material::operator==(const Material &other) const {
|
||||
return (this->pbrMetallicRoughness == other.pbrMetallicRoughness) &&
|
||||
(this->normalTexture == other.normalTexture) &&
|
||||
@@ -1990,6 +2089,8 @@ bool Node::operator==(const Node &other) const {
|
||||
return this->camera == other.camera && this->children == other.children &&
|
||||
this->extensions == other.extensions && this->extras == other.extras &&
|
||||
Equals(this->matrix, other.matrix) && this->mesh == other.mesh &&
|
||||
(this->light == other.light) &&
|
||||
(this->emitter == other.emitter) &&
|
||||
this->name == other.name && Equals(this->rotation, other.rotation) &&
|
||||
Equals(this->scale, other.scale) && this->skin == other.skin &&
|
||||
Equals(this->translation, other.translation) &&
|
||||
@@ -2000,6 +2101,15 @@ bool SpotLight::operator==(const SpotLight &other) const {
|
||||
TINYGLTF_DOUBLE_EQUAL(this->innerConeAngle, other.innerConeAngle) &&
|
||||
TINYGLTF_DOUBLE_EQUAL(this->outerConeAngle, other.outerConeAngle);
|
||||
}
|
||||
bool PositionalEmitter::operator == (const PositionalEmitter& other) const {
|
||||
return this->extensions == other.extensions && this->extras == other.extras &&
|
||||
TINYGLTF_DOUBLE_EQUAL(this->coneInnerAngle, other.coneInnerAngle) &&
|
||||
TINYGLTF_DOUBLE_EQUAL(this->coneOuterAngle, other.coneOuterAngle) &&
|
||||
TINYGLTF_DOUBLE_EQUAL(this->coneOuterGain, other.coneOuterGain) &&
|
||||
TINYGLTF_DOUBLE_EQUAL(this->maxDistance, other.maxDistance) &&
|
||||
TINYGLTF_DOUBLE_EQUAL(this->refDistance, other.refDistance) &&
|
||||
TINYGLTF_DOUBLE_EQUAL(this->rolloffFactor, other.rolloffFactor);
|
||||
}
|
||||
bool OrthographicCamera::operator==(const OrthographicCamera &other) const {
|
||||
return this->extensions == other.extensions && this->extras == other.extras &&
|
||||
TINYGLTF_DOUBLE_EQUAL(this->xmag, other.xmag) &&
|
||||
@@ -3404,6 +3514,35 @@ bool FindMember(const detail::json &o, const char *member, detail::json_const_it
|
||||
#endif
|
||||
}
|
||||
|
||||
bool FindMember(detail::json &o, const char *member, detail::json_iterator &it) {
|
||||
#ifdef TINYGLTF_USE_RAPIDJSON
|
||||
if (!o.IsObject()) {
|
||||
return false;
|
||||
}
|
||||
it = o.FindMember(member);
|
||||
return it != o.MemberEnd();
|
||||
#else
|
||||
it = o.find(member);
|
||||
return it != o.end();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Erase(detail::json & o, detail::json_iterator &it) {
|
||||
#ifdef TINYGLTF_USE_RAPIDJSON
|
||||
o.EraseMember(it);
|
||||
#else
|
||||
o.erase(it);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool IsEmpty(const detail::json & o) {
|
||||
#ifdef TINYGLTF_USE_RAPIDJSON
|
||||
return o.ObjectEmpty();
|
||||
#else
|
||||
return o.empty();
|
||||
#endif
|
||||
}
|
||||
|
||||
const detail::json &GetValue(detail::json_const_iterator &it) {
|
||||
#ifdef TINYGLTF_USE_RAPIDJSON
|
||||
return it->value;
|
||||
@@ -3412,6 +3551,14 @@ const detail::json &GetValue(detail::json_const_iterator &it) {
|
||||
#endif
|
||||
}
|
||||
|
||||
detail::json &GetValue(detail::json_iterator &it) {
|
||||
#ifdef TINYGLTF_USE_RAPIDJSON
|
||||
return it->value;
|
||||
#else
|
||||
return it.value();
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string JsonToString(const detail::json &o, int spacing = -1) {
|
||||
#ifdef TINYGLTF_USE_RAPIDJSON
|
||||
using namespace rapidjson;
|
||||
@@ -4027,7 +4174,7 @@ static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err,
|
||||
template <typename GltfType>
|
||||
static bool ParseExtrasAndExtensions(GltfType * target, std::string *err,
|
||||
const detail::json & o, bool store_json_strings) {
|
||||
|
||||
|
||||
ParseExtensionsProperty(&target->extensions, err, o);
|
||||
ParseExtrasProperty(&target->extras, o);
|
||||
|
||||
@@ -4936,7 +5083,53 @@ static bool ParseNode(Node *node, std::string *err, const detail::json &o,
|
||||
}
|
||||
}
|
||||
node->light = light;
|
||||
|
||||
|
||||
// KHR_audio: parse audio source reference
|
||||
int emitter = -1;
|
||||
if (node->extensions.count("KHR_audio") != 0) {
|
||||
auto const &audio_ext = node->extensions["KHR_audio"];
|
||||
if (audio_ext.Has("emitter")) {
|
||||
emitter = audio_ext.Get("emitter").GetNumberAsInt();
|
||||
} else {
|
||||
if (err) {
|
||||
*err +=
|
||||
"Node has extension KHR_audio, but does not reference "
|
||||
"a audio emitter.\n";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
node->emitter = emitter;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ParseScene(Scene *scene, std::string *err, const detail::json &o,
|
||||
bool store_original_json_for_extras_and_extensions) {
|
||||
ParseStringProperty(&scene->name, err, o, "name", false);
|
||||
ParseIntegerArrayProperty(&scene->nodes, err, o, "nodes", false);
|
||||
|
||||
ParseExtrasAndExtensions(scene, err, o,
|
||||
store_original_json_for_extras_and_extensions);
|
||||
|
||||
// Parse KHR_audio global emitters
|
||||
if (scene->extensions.count("KHR_audio") != 0) {
|
||||
auto const &audio_ext = scene->extensions["KHR_audio"];
|
||||
if (audio_ext.Has("emitters")) {
|
||||
auto emittersArr = audio_ext.Get("emitters");
|
||||
for (size_t i = 0; i < emittersArr.ArrayLen(); ++i) {
|
||||
scene->audioEmitters.emplace_back(emittersArr.Get(i).GetNumberAsInt());
|
||||
}
|
||||
} else {
|
||||
if (err) {
|
||||
*err +=
|
||||
"Node has extension KHR_audio, but does not reference "
|
||||
"a audio emitter.\n";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -5465,6 +5658,85 @@ static bool ParseLight(Light *light, std::string *err, const detail::json &o,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ParsePositionalEmitter(
|
||||
PositionalEmitter *positional, std::string *err, const detail::json &o,
|
||||
bool store_original_json_for_extras_and_extensions) {
|
||||
ParseNumberProperty(&positional->coneInnerAngle, err, o, "coneInnerAngle", false);
|
||||
ParseNumberProperty(&positional->coneOuterAngle, err, o, "coneOuterAngle", false);
|
||||
ParseNumberProperty(&positional->coneOuterGain, err, o, "coneOuterGain", false);
|
||||
ParseNumberProperty(&positional->maxDistance, err, o, "maxDistance", false);
|
||||
ParseNumberProperty(&positional->refDistance, err, o, "refDistance", false);
|
||||
ParseNumberProperty(&positional->rolloffFactor, err, o, "rolloffFactor", false);
|
||||
|
||||
ParseExtrasAndExtensions(positional, err, o, store_original_json_for_extras_and_extensions);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ParseAudioEmitter(
|
||||
AudioEmitter *emitter, std::string *err, const detail::json &o,
|
||||
bool store_original_json_for_extras_and_extensions) {
|
||||
if (!ParseStringProperty(&emitter->type, err, o, "type", true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (emitter->type == "positional") {
|
||||
detail::json_const_iterator positionalIt;
|
||||
if (!detail::FindMember(o, "positional", positionalIt)) {
|
||||
if (err) {
|
||||
std::stringstream ss;
|
||||
ss << "Positional emitter description not found." << std::endl;
|
||||
(*err) += ss.str();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const detail::json &v = detail::GetValue(positionalIt);
|
||||
if (!detail::IsObject(v)) {
|
||||
if (err) {
|
||||
std::stringstream ss;
|
||||
ss << "\"positional\" is not a JSON object." << std::endl;
|
||||
(*err) += ss.str();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ParsePositionalEmitter(&emitter->positional, err, v,
|
||||
store_original_json_for_extras_and_extensions)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ParseStringProperty(&emitter->name, err, o, "name", false);
|
||||
ParseNumberProperty(&emitter->gain, err, o, "gain", false);
|
||||
ParseBooleanProperty(&emitter->loop, err, o, "loop", false);
|
||||
ParseBooleanProperty(&emitter->playing, err, o, "playing", false);
|
||||
ParseStringProperty(&emitter->distanceModel, err, o, "distanceModel", false);
|
||||
ParseIntegerProperty(&emitter->source, err, o, "source", true);
|
||||
|
||||
ParseExtrasAndExtensions(emitter, err, o,
|
||||
store_original_json_for_extras_and_extensions);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ParseAudioSource(
|
||||
AudioSource *source, std::string *err, const detail::json &o,
|
||||
bool store_original_json_for_extras_and_extensions) {
|
||||
ParseStringProperty(&source->name, err, o, "name", false);
|
||||
ParseStringProperty(&source->uri, err, o, "uri", false);
|
||||
|
||||
if (source->uri.empty()) {
|
||||
ParseIntegerProperty(&source->bufferView, err, o, "bufferView", true);
|
||||
ParseStringProperty(&source->mimeType, err, o, "mimeType", true);
|
||||
}
|
||||
|
||||
ParseExtrasAndExtensions(source, err, o,
|
||||
store_original_json_for_extras_and_extensions);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename Callback>
|
||||
@@ -5841,15 +6113,13 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
|
||||
}
|
||||
return false;
|
||||
}
|
||||
std::vector<int> nodes;
|
||||
ParseIntegerArrayProperty(&nodes, err, o, "nodes", false);
|
||||
|
||||
Scene scene;
|
||||
scene.nodes = std::move(nodes);
|
||||
if (!ParseScene(&scene, err, o,
|
||||
store_original_json_for_extras_and_extensions_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ParseStringProperty(&scene.name, err, o, "name", false);
|
||||
|
||||
ParseExtrasAndExtensions(&scene, err, o, store_original_json_for_extras_and_extensions_);
|
||||
model->scenes.emplace_back(std::move(scene));
|
||||
return true;
|
||||
});
|
||||
@@ -6130,6 +6400,48 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
|
||||
}
|
||||
}
|
||||
}
|
||||
// parse KHR_audio extension
|
||||
if ((key == "KHR_audio") &&
|
||||
detail::IsObject(detail::GetValue(it))) {
|
||||
const detail::json &object = detail::GetValue(it);
|
||||
detail::json_const_iterator itKhrAudio;
|
||||
if (detail::FindMember(object, "emitters", itKhrAudio)) {
|
||||
const detail::json &emitters = detail::GetValue(itKhrAudio);
|
||||
if (!detail::IsArray(emitters)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto arrayIt(detail::ArrayBegin(emitters));
|
||||
auto arrayItEnd(detail::ArrayEnd(emitters));
|
||||
for (; arrayIt != arrayItEnd; ++arrayIt) {
|
||||
AudioEmitter emitter;
|
||||
if (!ParseAudioEmitter(&emitter, err, *arrayIt,
|
||||
store_original_json_for_extras_and_extensions_)) {
|
||||
return false;
|
||||
}
|
||||
model->audioEmitters.emplace_back(std::move(emitter));
|
||||
}
|
||||
}
|
||||
|
||||
if (detail::FindMember(object, "sources", itKhrAudio)) {
|
||||
const detail::json &sources = detail::GetValue(itKhrAudio);
|
||||
if (!detail::IsArray(sources)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto arrayIt(detail::ArrayBegin(sources));
|
||||
auto arrayItEnd(detail::ArrayEnd(sources));
|
||||
for (; arrayIt != arrayItEnd; ++arrayIt) {
|
||||
AudioSource source;
|
||||
if (!ParseAudioSource(
|
||||
&source, err, *arrayIt,
|
||||
store_original_json_for_extras_and_extensions_)) {
|
||||
return false;
|
||||
}
|
||||
model->audioSources.emplace_back(std::move(source));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6708,7 +7020,7 @@ static void SerializeExtensionMap(const ExtensionMap &extensions, detail::json &
|
||||
}
|
||||
|
||||
static void SerializeExtras(const Value & extras, detail::json & o) {
|
||||
if (extras.Type() != NULL_TYPE)
|
||||
if (extras.Type() != NULL_TYPE)
|
||||
SerializeValue("extras", extras, o);
|
||||
}
|
||||
|
||||
@@ -7196,6 +7508,61 @@ static void SerializeGltfLight(const Light &light, detail::json &o) {
|
||||
SerializeExtrasAndExtensions(light, o);
|
||||
}
|
||||
|
||||
static void SerializeGltfPositionalEmitter(const PositionalEmitter &positional,
|
||||
detail::json &o) {
|
||||
if (!TINYGLTF_DOUBLE_EQUAL(positional.coneInnerAngle, 6.283185307179586))
|
||||
SerializeNumberProperty("coneInnerAngle", positional.coneInnerAngle, o);
|
||||
if (!TINYGLTF_DOUBLE_EQUAL(positional.coneOuterAngle, 6.283185307179586))
|
||||
SerializeNumberProperty("coneOuterAngle", positional.coneOuterAngle, o);
|
||||
if (positional.coneOuterGain > 0.0)
|
||||
SerializeNumberProperty("coneOuterGain", positional.coneOuterGain, o);
|
||||
if (!TINYGLTF_DOUBLE_EQUAL(positional.maxDistance, 100.0))
|
||||
SerializeNumberProperty("maxDistance", positional.maxDistance, o);
|
||||
if (!TINYGLTF_DOUBLE_EQUAL(positional.refDistance, 1.0))
|
||||
SerializeNumberProperty("refDistance", positional.refDistance, o);
|
||||
if (!TINYGLTF_DOUBLE_EQUAL(positional.rolloffFactor, 1.0))
|
||||
SerializeNumberProperty("rolloffFactor", positional.rolloffFactor, o);
|
||||
|
||||
SerializeExtrasAndExtensions(positional, o);
|
||||
}
|
||||
|
||||
static void SerializeGltfAudioEmitter(const AudioEmitter &emitter,
|
||||
detail::json &o) {
|
||||
if (!emitter.name.empty()) SerializeStringProperty("name", emitter.name, o);
|
||||
if (!TINYGLTF_DOUBLE_EQUAL(emitter.gain, 1.0))
|
||||
SerializeNumberProperty("gain", emitter.gain, o);
|
||||
if (emitter.loop) SerializeNumberProperty("loop", emitter.loop, o);
|
||||
if (emitter.playing) SerializeNumberProperty("playing", emitter.playing, o);
|
||||
if (!emitter.type.empty()) SerializeStringProperty("type", emitter.type, o);
|
||||
if (!emitter.distanceModel.empty())
|
||||
SerializeStringProperty("distanceModel", emitter.distanceModel, o);
|
||||
if (emitter.type == "positional") {
|
||||
detail::json positional;
|
||||
SerializeGltfPositionalEmitter(emitter.positional, positional);
|
||||
detail::JsonAddMember(o, "positional", std::move(positional));
|
||||
}
|
||||
SerializeNumberProperty("source", emitter.source, o);
|
||||
SerializeExtrasAndExtensions(emitter, o);
|
||||
}
|
||||
|
||||
static void SerializeGltfAudioSource(const AudioSource& source, detail::json& o) {
|
||||
std::string name;
|
||||
std::string uri;
|
||||
int bufferView; // (required if no uri)
|
||||
std::string mimeType; // (required if no uri) ["audio/mp3", "audio/ogg",
|
||||
// "audio/wav", "audio/m4a"]
|
||||
|
||||
if (!source.name.empty()) SerializeStringProperty("name", source.name, o);
|
||||
if (source.uri.empty()) {
|
||||
SerializeStringProperty("mimeType", source.mimeType, o);
|
||||
SerializeNumberProperty<int>("bufferView", source.bufferView, o);
|
||||
}
|
||||
else {
|
||||
SerializeStringProperty("uri", source.uri, o);
|
||||
}
|
||||
SerializeExtrasAndExtensions(source, o);
|
||||
}
|
||||
|
||||
static void SerializeGltfNode(const Node &node, detail::json &o) {
|
||||
if (node.translation.size() > 0) {
|
||||
SerializeNumberArrayProperty<double>("translation", node.translation, o);
|
||||
@@ -7227,6 +7594,73 @@ static void SerializeGltfNode(const Node &node, detail::json &o) {
|
||||
|
||||
SerializeExtrasAndExtensions(node, o);
|
||||
|
||||
// Note(agnat): If the asset was loaded from disk, the node may already
|
||||
// contain the KHR_lights_punctual extension. If it was constructed in
|
||||
// memory it does not. In any case we update the JSON property using
|
||||
// the value from the struct. Last, if the node does not have a light
|
||||
// reference but the extension is still present, we remove it.
|
||||
if (node.light != -1) {
|
||||
detail::json_iterator it;
|
||||
if (!detail::FindMember(o, "extensions", it)) {
|
||||
detail::json extensions;
|
||||
detail::JsonSetObject(extensions);
|
||||
detail::JsonAddMember(o, "extensions", std::move(extensions));
|
||||
detail::FindMember(o, "extensions", it);
|
||||
}
|
||||
auto & extensions = detail::GetValue(it);
|
||||
if ( ! detail::FindMember(extensions, "KHR_lights_punctual", it)) {
|
||||
detail::json lights_punctual;
|
||||
detail::JsonSetObject(lights_punctual);
|
||||
detail::JsonAddMember(extensions, "KHR_lights_punctual", std::move(lights_punctual));
|
||||
detail::FindMember(o, "KHR_lights_punctual", it);
|
||||
}
|
||||
SerializeNumberProperty("light", node.light, detail::GetValue(it));
|
||||
} else {
|
||||
// node has no light ref (any longer)... so we clean up
|
||||
detail::json_iterator ext_it;
|
||||
if (detail::FindMember(o, "extensions", ext_it)) {
|
||||
auto & extensions = detail::GetValue(ext_it);
|
||||
detail::json_iterator lp_it;
|
||||
if (detail::FindMember(extensions, "KHR_lights_punctual", lp_it)) {
|
||||
detail::Erase(extensions, lp_it);
|
||||
}
|
||||
if (detail::IsEmpty(extensions)) {
|
||||
detail::Erase(o, ext_it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// KHR_audio
|
||||
if (node.emitter != -1) {
|
||||
detail::json_iterator it;
|
||||
if (!detail::FindMember(o, "extensions", it)) {
|
||||
detail::json extensions;
|
||||
detail::JsonSetObject(extensions);
|
||||
detail::JsonAddMember(o, "extensions", std::move(extensions));
|
||||
detail::FindMember(o, "extensions", it);
|
||||
}
|
||||
auto &extensions = detail::GetValue(it);
|
||||
if (!detail::FindMember(extensions, "KHR_audio", it)) {
|
||||
detail::json audio;
|
||||
detail::JsonSetObject(audio);
|
||||
detail::JsonAddMember(extensions, "KHR_audio", std::move(audio));
|
||||
detail::FindMember(o, "KHR_audio", it);
|
||||
}
|
||||
SerializeNumberProperty("emitter", node.emitter, detail::GetValue(it));
|
||||
} else {
|
||||
detail::json_iterator ext_it;
|
||||
if (detail::FindMember(o, "extensions", ext_it)) {
|
||||
auto &extensions = detail::GetValue(ext_it);
|
||||
detail::json_iterator lp_it;
|
||||
if (detail::FindMember(extensions, "KHR_audio", lp_it)) {
|
||||
detail::Erase(extensions, lp_it);
|
||||
}
|
||||
if (detail::IsEmpty(extensions)) {
|
||||
detail::Erase(o, ext_it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!node.name.empty()) SerializeStringProperty("name", node.name, o);
|
||||
SerializeNumberArrayProperty<int>("children", node.children, o);
|
||||
}
|
||||
@@ -7301,6 +7735,37 @@ static void SerializeGltfScene(const Scene &scene, detail::json &o) {
|
||||
SerializeStringProperty("name", scene.name, o);
|
||||
}
|
||||
SerializeExtrasAndExtensions(scene, o);
|
||||
|
||||
// KHR_audio
|
||||
if (!scene.audioEmitters.empty()) {
|
||||
detail::json_iterator it;
|
||||
if (!detail::FindMember(o, "extensions", it)) {
|
||||
detail::json extensions;
|
||||
detail::JsonSetObject(extensions);
|
||||
detail::JsonAddMember(o, "extensions", std::move(extensions));
|
||||
detail::FindMember(o, "extensions", it);
|
||||
}
|
||||
auto &extensions = detail::GetValue(it);
|
||||
if (!detail::FindMember(extensions, "KHR_audio", it)) {
|
||||
detail::json audio;
|
||||
detail::JsonSetObject(audio);
|
||||
detail::JsonAddMember(extensions, "KHR_audio", std::move(audio));
|
||||
detail::FindMember(o, "KHR_audio", it);
|
||||
}
|
||||
SerializeNumberArrayProperty("emitters", scene.audioEmitters, detail::GetValue(it));
|
||||
} else {
|
||||
detail::json_iterator ext_it;
|
||||
if (detail::FindMember(o, "extensions", ext_it)) {
|
||||
auto &extensions = detail::GetValue(ext_it);
|
||||
detail::json_iterator lp_it;
|
||||
if (detail::FindMember(extensions, "KHR_audio", lp_it)) {
|
||||
detail::Erase(extensions, lp_it);
|
||||
}
|
||||
if (detail::IsEmpty(extensions)) {
|
||||
detail::Erase(o, ext_it);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void SerializeGltfSkin(const Skin &skin, detail::json &o) {
|
||||
@@ -7543,6 +8008,53 @@ static void SerializeGltfModel(const Model *model, detail::json &o) {
|
||||
}
|
||||
}
|
||||
|
||||
// KHR_audio
|
||||
if (!model->audioEmitters.empty() || !model->audioSources.empty()) {
|
||||
detail::json emitters;
|
||||
detail::JsonReserveArray(emitters, model->audioEmitters.size());
|
||||
for (unsigned int i = 0; i < model->audioEmitters.size(); ++i) {
|
||||
detail::json emitter;
|
||||
SerializeGltfAudioEmitter(model->audioEmitters[i], emitter);
|
||||
detail::JsonPushBack(emitters, std::move(emitter));
|
||||
}
|
||||
detail::json khr_audio_cmn;
|
||||
detail::JsonAddMember(khr_audio_cmn, "emitters", std::move(emitters));
|
||||
|
||||
detail::json sources;
|
||||
detail::JsonReserveArray(sources, model->audioSources.size());
|
||||
for (unsigned int i = 0; i < model->audioSources.size(); ++i) {
|
||||
detail::json source;
|
||||
SerializeGltfAudioSource(model->audioSources[i], source);
|
||||
detail::JsonPushBack(sources, std::move(source));
|
||||
}
|
||||
detail::JsonAddMember(khr_audio_cmn, "sources", std::move(sources));
|
||||
|
||||
detail::json ext_j;
|
||||
{
|
||||
detail::json_const_iterator it;
|
||||
if (detail::FindMember(o, "extensions", it)) {
|
||||
detail::JsonAssign(ext_j, detail::GetValue(it));
|
||||
}
|
||||
}
|
||||
|
||||
detail::JsonAddMember(ext_j, "KHR_audio", std::move(khr_audio_cmn));
|
||||
|
||||
detail::JsonAddMember(o, "extensions", std::move(ext_j));
|
||||
|
||||
// Also add "KHR_audio" to `extensionsUsed`
|
||||
{
|
||||
auto has_khr_audio =
|
||||
std::find_if(extensionsUsed.begin(), extensionsUsed.end(),
|
||||
[](const std::string &s) {
|
||||
return (s.compare("KHR_audio") == 0);
|
||||
});
|
||||
|
||||
if (has_khr_audio == extensionsUsed.end()) {
|
||||
extensionsUsed.push_back("KHR_audio");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extensions used
|
||||
if (extensionsUsed.size()) {
|
||||
SerializeStringArrayProperty("extensionsUsed", extensionsUsed, o);
|
||||
|
||||
Reference in New Issue
Block a user