Export glTF 2

pull/1363/head
jamesgk 2017-07-25 17:26:18 -07:00
parent d7cbbaf23e
commit bb55246c18
4 changed files with 93 additions and 81 deletions

View File

@ -177,7 +177,7 @@ namespace glTF2
struct GLB_Header struct GLB_Header
{ {
uint8_t magic[4]; //!< Magic number: "glTF" uint8_t magic[4]; //!< Magic number: "glTF"
uint32_t version; //!< Version number (always 1 as of the last update) uint32_t version; //!< Version number
uint32_t length; //!< Total length of the Binary glTF, including header, scene, and body, in bytes uint32_t length; //!< Total length of the Binary glTF, including header, scene, and body, in bytes
uint32_t sceneLength; //!< Length, in bytes, of the glTF scene uint32_t sceneLength; //!< Length, in bytes, of the glTF scene
uint32_t sceneFormat; //!< Specifies the format of the glTF scene (see the SceneFormat enum) uint32_t sceneFormat; //!< Specifies the format of the glTF scene (see the SceneFormat enum)
@ -381,6 +381,7 @@ namespace glTF2
//! Base classe for all glTF top-level objects //! Base classe for all glTF top-level objects
struct Object struct Object
{ {
int index; //!< The index of this object within its property container
std::string id; //!< The globally unique ID used to reference this object std::string id; //!< The globally unique ID used to reference this object
std::string name; //!< The user-defined name of this object std::string name; //!< The user-defined name of this object
@ -952,10 +953,10 @@ namespace glTF2
}; };
struct AnimChannel { struct AnimChannel {
std::string sampler; //!< The ID of one sampler present in the containing animation's samplers property. int sampler; //!< The index of a sampler in the containing animation's samplers property.
struct AnimTarget { struct AnimTarget {
Ref<Node> id; //!< The ID of the node to animate. Ref<Node> node; //!< The node to animate.
std::string path; //!< The name of property of the node to animate ("translation", "rotation", or "scale"). std::string path; //!< The name of property of the node to animate ("translation", "rotation", or "scale").
} target; } target;
}; };
@ -977,6 +978,20 @@ namespace glTF2
Animation() {} Animation() {}
void Read(Value& obj, Asset& r); void Read(Value& obj, Asset& r);
//! Get accessor given an animation parameter name.
Ref<Accessor> GetAccessor(std::string name) {
if (name == "TIME") {
return Parameters.TIME;
} else if (name == "rotation") {
return Parameters.rotation;
} else if (name == "scale") {
return Parameters.scale;
} else if (name == "translation") {
return Parameters.translation;
}
return Ref<Accessor>();
}
}; };
@ -1058,7 +1073,7 @@ namespace glTF2
std::string version; //!< Specifies the target rendering API (default: "1.0.3") std::string version; //!< Specifies the target rendering API (default: "1.0.3")
} profile; //!< Specifies the target rendering API and version, e.g., WebGL 1.0.3. (default: {}) } profile; //!< Specifies the target rendering API and version, e.g., WebGL 1.0.3. (default: {})
int version; //!< The glTF format version (should be 1) int version; //!< The glTF format version
void Read(Document& doc); void Read(Document& doc);

View File

@ -242,6 +242,7 @@ Ref<T> LazyDict<T>::Create(const char* id)
} }
T* inst = new T(); T* inst = new T();
inst->id = id; inst->id = id;
inst->index = mObjs.size();
return Add(inst); return Add(inst);
} }

View File

@ -79,7 +79,7 @@ namespace glTF2 {
lst.SetArray(); lst.SetArray();
lst.Reserve(unsigned(v.size()), al); lst.Reserve(unsigned(v.size()), al);
for (size_t i = 0; i < v.size(); ++i) { for (size_t i = 0; i < v.size(); ++i) {
lst.PushBack(StringRef(v[i]->id), al); lst.PushBack(v[i]->index, al);
} }
obj.AddMember(StringRef(fieldId), lst, al); obj.AddMember(StringRef(fieldId), lst, al);
} }
@ -89,7 +89,7 @@ namespace glTF2 {
inline void Write(Value& obj, Accessor& a, AssetWriter& w) inline void Write(Value& obj, Accessor& a, AssetWriter& w)
{ {
obj.AddMember("bufferView", Value(a.bufferView->id, w.mAl).Move(), w.mAl); obj.AddMember("bufferView", a.bufferView->index, w.mAl);
obj.AddMember("byteOffset", a.byteOffset, w.mAl); obj.AddMember("byteOffset", a.byteOffset, w.mAl);
obj.AddMember("byteStride", a.byteStride, w.mAl); obj.AddMember("byteStride", a.byteStride, w.mAl);
obj.AddMember("componentType", int(a.componentType), w.mAl); obj.AddMember("componentType", int(a.componentType), w.mAl);
@ -113,12 +113,13 @@ namespace glTF2 {
Value valChannel; Value valChannel;
valChannel.SetObject(); valChannel.SetObject();
{ {
valChannel.AddMember("sampler", c.sampler, w.mAl); Animation::AnimSampler& s = a.Samplers[c.sampler];
valChannel.AddMember("sampler", s.id, w.mAl);
Value valTarget; Value valTarget;
valTarget.SetObject(); valTarget.SetObject();
{ {
valTarget.AddMember("id", StringRef(c.target.id->id), w.mAl); valTarget.AddMember("id", StringRef(c.target.node->id), w.mAl);
valTarget.AddMember("path", c.target.path, w.mAl); valTarget.AddMember("path", c.target.path, w.mAl);
} }
valChannel.AddMember("target", valTarget, w.mAl); valChannel.AddMember("target", valTarget, w.mAl);
@ -127,61 +128,35 @@ namespace glTF2 {
} }
obj.AddMember("channels", channels, w.mAl); obj.AddMember("channels", channels, w.mAl);
/****************** Parameters *******************/
Value valParameters;
valParameters.SetObject();
{
if (a.Parameters.TIME) {
valParameters.AddMember("TIME", StringRef(a.Parameters.TIME->id), w.mAl);
}
if (a.Parameters.rotation) {
valParameters.AddMember("rotation", StringRef(a.Parameters.rotation->id), w.mAl);
}
if (a.Parameters.scale) {
valParameters.AddMember("scale", StringRef(a.Parameters.scale->id), w.mAl);
}
if (a.Parameters.translation) {
valParameters.AddMember("translation", StringRef(a.Parameters.translation->id), w.mAl);
}
}
obj.AddMember("parameters", valParameters, w.mAl);
/****************** Samplers *******************/ /****************** Samplers *******************/
Value valSamplers; Value valSamplers;
valSamplers.SetObject(); valSamplers.SetArray();
for (size_t i = 0; i < unsigned(a.Samplers.size()); ++i) { for (size_t i = 0; i < unsigned(a.Samplers.size()); ++i) {
Animation::AnimSampler& s = a.Samplers[i]; Animation::AnimSampler& s = a.Samplers[i];
Value valSampler; Value valSampler;
valSampler.SetObject(); valSampler.SetObject();
{ {
valSampler.AddMember("input", s.input, w.mAl); Ref<Accessor> inputAccessor = a.GetAccessor(s.input);
Ref<Accessor> outputAccessor = a.GetAccessor(s.output);
valSampler.AddMember("input", inputAccessor->index, w.mAl);
valSampler.AddMember("interpolation", s.interpolation, w.mAl); valSampler.AddMember("interpolation", s.interpolation, w.mAl);
valSampler.AddMember("output", s.output, w.mAl); valSampler.AddMember("output", outputAccessor->index, w.mAl);
} }
valSamplers.AddMember(StringRef(s.id), valSampler, w.mAl); valSamplers.PushBack(valSampler, w.mAl);
} }
obj.AddMember("samplers", valSamplers, w.mAl); obj.AddMember("samplers", valSamplers, w.mAl);
} }
inline void Write(Value& obj, Buffer& b, AssetWriter& w) inline void Write(Value& obj, Buffer& b, AssetWriter& w)
{ {
const char* type;
switch (b.type) {
case Buffer::Type_text:
type = "text"; break;
default:
type = "arraybuffer";
}
obj.AddMember("byteLength", static_cast<uint64_t>(b.byteLength), w.mAl); obj.AddMember("byteLength", static_cast<uint64_t>(b.byteLength), w.mAl);
obj.AddMember("type", StringRef(type), w.mAl);
obj.AddMember("uri", Value(b.GetURI(), w.mAl).Move(), w.mAl); obj.AddMember("uri", Value(b.GetURI(), w.mAl).Move(), w.mAl);
} }
inline void Write(Value& obj, BufferView& bv, AssetWriter& w) inline void Write(Value& obj, BufferView& bv, AssetWriter& w)
{ {
obj.AddMember("buffer", Value(bv.buffer->id, w.mAl).Move(), w.mAl); obj.AddMember("buffer", bv.buffer->index, w.mAl);
obj.AddMember("byteOffset", static_cast<uint64_t>(bv.byteOffset), w.mAl); obj.AddMember("byteOffset", static_cast<uint64_t>(bv.byteOffset), w.mAl);
obj.AddMember("byteLength", static_cast<uint64_t>(bv.byteLength), w.mAl); obj.AddMember("byteLength", static_cast<uint64_t>(bv.byteLength), w.mAl);
obj.AddMember("target", int(bv.target), w.mAl); obj.AddMember("target", int(bv.target), w.mAl);
@ -200,7 +175,7 @@ namespace glTF2 {
exts.SetObject(); exts.SetObject();
ext.SetObject(); ext.SetObject();
ext.AddMember("bufferView", StringRef(img.bufferView->id), w.mAl); ext.AddMember("bufferView", img.bufferView->index, w.mAl);
if (!img.mimeType.empty()) if (!img.mimeType.empty())
ext.AddMember("mimeType", StringRef(img.mimeType), w.mAl); ext.AddMember("mimeType", StringRef(img.mimeType), w.mAl);
@ -224,9 +199,12 @@ namespace glTF2 {
namespace { namespace {
inline void WriteColorOrTex(Value& obj, TexProperty& prop, const char* propName, MemoryPoolAllocator<>& al) inline void WriteColorOrTex(Value& obj, TexProperty& prop, const char* propName, MemoryPoolAllocator<>& al)
{ {
if (prop.texture) if (prop.texture) {
obj.AddMember(StringRef(propName), Value(prop.texture->id, al).Move(), al); Value tex;
else { tex.SetObject();
tex.AddMember("index", prop.texture->index, al);
obj.AddMember(StringRef(propName), tex, al);
} else {
Value col; Value col;
obj.AddMember(StringRef(propName), MakeValue(col, prop.color, al), al); obj.AddMember(StringRef(propName), MakeValue(col, prop.color, al), al);
} }
@ -238,17 +216,21 @@ namespace glTF2 {
Value v; Value v;
v.SetObject(); v.SetObject();
{ {
WriteColorOrTex(v, m.ambient, "ambient", w.mAl); WriteColorOrTex(v, m.ambient, m.ambient.texture ? "ambientTexture" : "ambientFactor", w.mAl);
WriteColorOrTex(v, m.diffuse, "diffuse", w.mAl); WriteColorOrTex(v, m.diffuse, m.diffuse.texture ? "diffuseTexture" : "diffuseFactor", w.mAl);
WriteColorOrTex(v, m.specular, "specular", w.mAl); WriteColorOrTex(v, m.specular, m.specular.texture ? "specularTexture" : "specularFactor", w.mAl);
WriteColorOrTex(v, m.emission, "emission", w.mAl); WriteColorOrTex(v, m.emission, m.emission.texture ? "emissionTexture" : "emissionFactor", w.mAl);
if (m.transparent) if (m.transparent)
v.AddMember("transparency", m.transparency, w.mAl); v.AddMember("transparency", m.transparency, w.mAl);
v.AddMember("shininess", m.shininess, w.mAl); v.AddMember("shininessFactor", m.shininess, w.mAl);
} }
obj.AddMember("values", v, w.mAl); v.AddMember("type", "commonPhong", w.mAl);
Value ext;
ext.SetObject();
ext.AddMember("KHR_materials_common", v, w.mAl);
obj.AddMember("extensions", ext, w.mAl);
} }
namespace { namespace {
@ -257,13 +239,13 @@ namespace glTF2 {
{ {
if (lst.empty()) return; if (lst.empty()) return;
if (lst.size() == 1 && !forceNumber) { if (lst.size() == 1 && !forceNumber) {
attrs.AddMember(StringRef(semantic), Value(lst[0]->id, w.mAl).Move(), w.mAl); attrs.AddMember(StringRef(semantic), lst[0]->index, w.mAl);
} }
else { else {
for (size_t i = 0; i < lst.size(); ++i) { for (size_t i = 0; i < lst.size(); ++i) {
char buffer[32]; char buffer[32];
ai_snprintf(buffer, 32, "%s_%d", semantic, int(i)); ai_snprintf(buffer, 32, "%s_%d", semantic, int(i));
attrs.AddMember(Value(buffer, w.mAl).Move(), Value(lst[i]->id, w.mAl).Move(), w.mAl); attrs.AddMember(Value(buffer, w.mAl).Move(), lst[i]->index, w.mAl);
} }
} }
} }
@ -337,10 +319,10 @@ namespace glTF2 {
prim.AddMember("mode", Value(int(p.mode)).Move(), w.mAl); prim.AddMember("mode", Value(int(p.mode)).Move(), w.mAl);
if (p.material) if (p.material)
prim.AddMember("material", p.material->id, w.mAl); prim.AddMember("material", p.material->index, w.mAl);
if (p.indices) if (p.indices)
prim.AddMember("indices", Value(p.indices->id, w.mAl).Move(), w.mAl); prim.AddMember("indices", p.indices->index, w.mAl);
Value attrs; Value attrs;
attrs.SetObject(); attrs.SetObject();
@ -390,7 +372,7 @@ namespace glTF2 {
AddRefsVector(obj, "skeletons", n.skeletons, w.mAl); AddRefsVector(obj, "skeletons", n.skeletons, w.mAl);
if (n.skin) { if (n.skin) {
obj.AddMember("skin", Value(n.skin->id, w.mAl).Move(), w.mAl); obj.AddMember("skin", n.skin->index, w.mAl);
} }
if (!n.jointName.empty()) { if (!n.jointName.empty()) {
@ -437,9 +419,9 @@ namespace glTF2 {
vJointNames.Reserve(unsigned(b.jointNames.size()), w.mAl); vJointNames.Reserve(unsigned(b.jointNames.size()), w.mAl);
for (size_t i = 0; i < unsigned(b.jointNames.size()); ++i) { for (size_t i = 0; i < unsigned(b.jointNames.size()); ++i) {
vJointNames.PushBack(StringRef(b.jointNames[i]->jointName), w.mAl); vJointNames.PushBack(b.jointNames[i]->index, w.mAl);
} }
obj.AddMember("jointNames", vJointNames, w.mAl); obj.AddMember("joints", vJointNames, w.mAl);
if (b.bindShapeMatrix.isPresent) { if (b.bindShapeMatrix.isPresent) {
Value val; Value val;
@ -447,7 +429,7 @@ namespace glTF2 {
} }
if (b.inverseBindMatrices) { if (b.inverseBindMatrices) {
obj.AddMember("inverseBindMatrices", Value(b.inverseBindMatrices->id, w.mAl).Move(), w.mAl); obj.AddMember("inverseBindMatrices", b.inverseBindMatrices->index, w.mAl);
} }
} }
@ -460,10 +442,10 @@ namespace glTF2 {
inline void Write(Value& obj, Texture& tex, AssetWriter& w) inline void Write(Value& obj, Texture& tex, AssetWriter& w)
{ {
if (tex.source) { if (tex.source) {
obj.AddMember("source", Value(tex.source->id, w.mAl).Move(), w.mAl); obj.AddMember("source", tex.source->index, w.mAl);
} }
if (tex.sampler) { if (tex.sampler) {
obj.AddMember("sampler", Value(tex.sampler->id, w.mAl).Move(), w.mAl); obj.AddMember("sampler", tex.sampler->index, w.mAl);
} }
} }
@ -490,7 +472,7 @@ namespace glTF2 {
// Add the target scene field // Add the target scene field
if (mAsset.scene) { if (mAsset.scene) {
mDoc.AddMember("scene", StringRef(mAsset.scene->id), mAl); mDoc.AddMember("scene", mAsset.scene->index, mAl);
} }
} }
@ -582,7 +564,7 @@ namespace glTF2 {
GLB_Header header; GLB_Header header;
memcpy(header.magic, AI_GLB_MAGIC_NUMBER, sizeof(header.magic)); memcpy(header.magic, AI_GLB_MAGIC_NUMBER, sizeof(header.magic));
header.version = 1; header.version = 2;
AI_SWAP4(header.version); AI_SWAP4(header.version);
header.length = uint32_t(sizeof(header) + sceneLength + bodyLength); header.length = uint32_t(sizeof(header) + sceneLength + bodyLength);
@ -624,8 +606,8 @@ namespace glTF2 {
if (false) if (false)
exts.PushBack(StringRef("KHR_binary_glTF"), mAl); exts.PushBack(StringRef("KHR_binary_glTF"), mAl);
if (false) // This is used to export common materials with GLTF 2.
exts.PushBack(StringRef("KHR_materials_common"), mAl); exts.PushBack(StringRef("KHR_materials_common"), mAl);
} }
if (!exts.Empty()) if (!exts.Empty())
@ -653,9 +635,9 @@ namespace glTF2 {
} }
Value* dict; Value* dict;
if (!(dict = FindObject(*container, d.mDictId))) { if (!(dict = FindArray(*container, d.mDictId))) {
container->AddMember(StringRef(d.mDictId), Value().SetObject().Move(), mDoc.GetAllocator()); container->AddMember(StringRef(d.mDictId), Value().SetArray().Move(), mDoc.GetAllocator());
dict = FindObject(*container, d.mDictId); dict = FindArray(*container, d.mDictId);
} }
for (size_t i = 0; i < d.mObjs.size(); ++i) { for (size_t i = 0; i < d.mObjs.size(); ++i) {
@ -670,7 +652,7 @@ namespace glTF2 {
Write(obj, *d.mObjs[i], *this); Write(obj, *d.mObjs[i], *this);
dict->AddMember(StringRef(d.mObjs[i]->id), obj, mAl); dict->PushBack(obj, mAl);
} }
} }

View File

@ -434,7 +434,7 @@ void ExportSkin(Asset& mAsset, const aiMesh* aimesh, Ref<Mesh>& meshRef, Ref<Buf
// aib->mName =====> skinRef->jointNames // aib->mName =====> skinRef->jointNames
// Find the node with id = mName. // Find the node with id = mName.
Ref<Node> nodeRef = mAsset.nodes.Get(aib->mName.C_Str()); Ref<Node> nodeRef = mAsset.nodes.Get(aib->mName.C_Str());
nodeRef->jointName = nodeRef->id; nodeRef->jointName = nodeRef->name;
unsigned int jointNamesIndex; unsigned int jointNamesIndex;
bool addJointToJointNames = true; bool addJointToJointNames = true;
@ -744,15 +744,27 @@ void glTF2Exporter::ExportMeshes()
skinRef->bindShapeMatrix.isPresent = true; skinRef->bindShapeMatrix.isPresent = true;
IdentityMatrix4(skinRef->bindShapeMatrix.value); IdentityMatrix4(skinRef->bindShapeMatrix.value);
// Find node that contains this mesh and add "skeletons" and "skin" attributes to that node. // Find nodes that contain a mesh with bones and add "skeletons" and "skin" attributes to those nodes.
Ref<Node> rootNode = mAsset->nodes.Get(unsigned(0)); Ref<Node> rootNode = mAsset->nodes.Get(unsigned(0));
Ref<Node> meshNode; Ref<Node> meshNode;
std::string meshID = mAsset->meshes.Get(unsigned(0))->id; for (unsigned int meshIndex = 0; meshIndex < mAsset->meshes.Size(); ++meshIndex) {
FindMeshNode(rootNode, meshNode, meshID); Ref<Mesh> mesh = mAsset->meshes.Get(meshIndex);
bool hasBones = false;
Ref<Node> rootJoint = FindSkeletonRootJoint(skinRef); for (unsigned int i = 0; i < mesh->primitives.size(); ++i) {
meshNode->skeletons.push_back(rootJoint); if (!mesh->primitives[i].attributes.weight.empty()) {
meshNode->skin = skinRef; hasBones = true;
break;
}
}
if (!hasBones) {
continue;
}
std::string meshID = mesh->id;
FindMeshNode(rootNode, meshNode, meshID);
Ref<Node> rootJoint = FindSkeletonRootJoint(skinRef);
meshNode->skeletons.push_back(rootJoint);
meshNode->skin = skinRef;
}
} }
} }
@ -787,9 +799,11 @@ unsigned int glTF2Exporter::ExportNodeHierarchy(const aiNode* n)
*/ */
unsigned int glTF2Exporter::ExportNode(const aiNode* n, Ref<Node>& parent) unsigned int glTF2Exporter::ExportNode(const aiNode* n, Ref<Node>& parent)
{ {
Ref<Node> node = mAsset->nodes.Create(mAsset->FindUniqueID(n->mName.C_Str(), "node")); std::string name = mAsset->FindUniqueID(n->mName.C_Str(), "node");
Ref<Node> node = mAsset->nodes.Create(name);
node->parent = parent; node->parent = parent;
node->name = name;
if (!n->mTransformation.IsIdentity()) { if (!n->mTransformation.IsIdentity()) {
node->matrix.isPresent = true; node->matrix.isPresent = true;
@ -826,7 +840,7 @@ void glTF2Exporter::ExportScene()
void glTF2Exporter::ExportMetadata() void glTF2Exporter::ExportMetadata()
{ {
AssetMetadata& asset = mAsset->asset; AssetMetadata& asset = mAsset->asset;
asset.version = 1; asset.version = 2;
char buffer[256]; char buffer[256];
ai_snprintf(buffer, 256, "Open Asset Import Library (assimp v%d.%d.%d)", ai_snprintf(buffer, 256, "Open Asset Import Library (assimp v%d.%d.%d)",
@ -970,12 +984,12 @@ void glTF2Exporter::ExportAnimations()
Animation::AnimChannel tmpAnimChannel; Animation::AnimChannel tmpAnimChannel;
Animation::AnimSampler tmpAnimSampler; Animation::AnimSampler tmpAnimSampler;
tmpAnimChannel.sampler = name + "_" + channelType; tmpAnimChannel.sampler = animRef->Samplers.size();
tmpAnimChannel.target.path = channelType; tmpAnimChannel.target.path = channelType;
tmpAnimSampler.output = channelType; tmpAnimSampler.output = channelType;
tmpAnimSampler.id = name + "_" + channelType; tmpAnimSampler.id = name + "_" + channelType;
tmpAnimChannel.target.id = mAsset->nodes.Get(nodeChannel->mNodeName.C_Str()); tmpAnimChannel.target.node = mAsset->nodes.Get(nodeChannel->mNodeName.C_Str());
tmpAnimSampler.input = "TIME"; tmpAnimSampler.input = "TIME";
tmpAnimSampler.interpolation = "LINEAR"; tmpAnimSampler.interpolation = "LINEAR";