From bb55246c180f4722d2dda5913199e66cd14a8f03 Mon Sep 17 00:00:00 2001 From: jamesgk Date: Tue, 25 Jul 2017 17:26:18 -0700 Subject: [PATCH] Export glTF 2 --- code/glTF2Asset.h | 23 ++++++++-- code/glTF2Asset.inl | 1 + code/glTF2AssetWriter.inl | 112 +++++++++++++++++++--------------------------- code/glTF2Exporter.cpp | 38 +++++++++++----- 4 files changed, 93 insertions(+), 81 deletions(-) diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index 3331c11..36caba2 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -177,7 +177,7 @@ namespace glTF2 struct GLB_Header { 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 sceneLength; //!< Length, in bytes, of the glTF scene 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 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 name; //!< The user-defined name of this object @@ -952,10 +953,10 @@ namespace glTF2 }; 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 { - Ref id; //!< The ID of the node to animate. + Ref node; //!< The node to animate. std::string path; //!< The name of property of the node to animate ("translation", "rotation", or "scale"). } target; }; @@ -977,6 +978,20 @@ namespace glTF2 Animation() {} void Read(Value& obj, Asset& r); + + //! Get accessor given an animation parameter name. + Ref 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(); + } }; @@ -1058,7 +1073,7 @@ namespace glTF2 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: {}) - int version; //!< The glTF format version (should be 1) + int version; //!< The glTF format version void Read(Document& doc); diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 9065ced..33598e2 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -242,6 +242,7 @@ Ref LazyDict::Create(const char* id) } T* inst = new T(); inst->id = id; + inst->index = mObjs.size(); return Add(inst); } diff --git a/code/glTF2AssetWriter.inl b/code/glTF2AssetWriter.inl index 9bf1c56..2c3f2ff 100644 --- a/code/glTF2AssetWriter.inl +++ b/code/glTF2AssetWriter.inl @@ -79,7 +79,7 @@ namespace glTF2 { lst.SetArray(); lst.Reserve(unsigned(v.size()), al); 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); } @@ -89,7 +89,7 @@ namespace glTF2 { 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("byteStride", a.byteStride, w.mAl); obj.AddMember("componentType", int(a.componentType), w.mAl); @@ -113,12 +113,13 @@ namespace glTF2 { Value valChannel; 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; 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); } valChannel.AddMember("target", valTarget, w.mAl); @@ -127,61 +128,35 @@ namespace glTF2 { } 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 *******************/ Value valSamplers; - valSamplers.SetObject(); + valSamplers.SetArray(); for (size_t i = 0; i < unsigned(a.Samplers.size()); ++i) { Animation::AnimSampler& s = a.Samplers[i]; Value valSampler; valSampler.SetObject(); { - valSampler.AddMember("input", s.input, w.mAl); + Ref inputAccessor = a.GetAccessor(s.input); + Ref outputAccessor = a.GetAccessor(s.output); + valSampler.AddMember("input", inputAccessor->index, 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); } 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(b.byteLength), w.mAl); - obj.AddMember("type", StringRef(type), w.mAl); obj.AddMember("uri", Value(b.GetURI(), w.mAl).Move(), w.mAl); } 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(bv.byteOffset), w.mAl); obj.AddMember("byteLength", static_cast(bv.byteLength), w.mAl); obj.AddMember("target", int(bv.target), w.mAl); @@ -200,7 +175,7 @@ namespace glTF2 { exts.SetObject(); ext.SetObject(); - ext.AddMember("bufferView", StringRef(img.bufferView->id), w.mAl); + ext.AddMember("bufferView", img.bufferView->index, w.mAl); if (!img.mimeType.empty()) ext.AddMember("mimeType", StringRef(img.mimeType), w.mAl); @@ -224,9 +199,12 @@ namespace glTF2 { namespace { inline void WriteColorOrTex(Value& obj, TexProperty& prop, const char* propName, MemoryPoolAllocator<>& al) { - if (prop.texture) - obj.AddMember(StringRef(propName), Value(prop.texture->id, al).Move(), al); - else { + if (prop.texture) { + Value tex; + tex.SetObject(); + tex.AddMember("index", prop.texture->index, al); + obj.AddMember(StringRef(propName), tex, al); + } else { Value col; obj.AddMember(StringRef(propName), MakeValue(col, prop.color, al), al); } @@ -238,17 +216,21 @@ namespace glTF2 { Value v; v.SetObject(); { - WriteColorOrTex(v, m.ambient, "ambient", w.mAl); - WriteColorOrTex(v, m.diffuse, "diffuse", w.mAl); - WriteColorOrTex(v, m.specular, "specular", w.mAl); - WriteColorOrTex(v, m.emission, "emission", w.mAl); + WriteColorOrTex(v, m.ambient, m.ambient.texture ? "ambientTexture" : "ambientFactor", w.mAl); + WriteColorOrTex(v, m.diffuse, m.diffuse.texture ? "diffuseTexture" : "diffuseFactor", w.mAl); + WriteColorOrTex(v, m.specular, m.specular.texture ? "specularTexture" : "specularFactor", w.mAl); + WriteColorOrTex(v, m.emission, m.emission.texture ? "emissionTexture" : "emissionFactor", w.mAl); if (m.transparent) 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 { @@ -257,13 +239,13 @@ namespace glTF2 { { if (lst.empty()) return; 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 { for (size_t i = 0; i < lst.size(); ++i) { char buffer[32]; 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); if (p.material) - prim.AddMember("material", p.material->id, w.mAl); + prim.AddMember("material", p.material->index, w.mAl); 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; attrs.SetObject(); @@ -390,7 +372,7 @@ namespace glTF2 { AddRefsVector(obj, "skeletons", n.skeletons, w.mAl); 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()) { @@ -437,9 +419,9 @@ namespace glTF2 { vJointNames.Reserve(unsigned(b.jointNames.size()), w.mAl); 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) { Value val; @@ -447,7 +429,7 @@ namespace glTF2 { } 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) { 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) { - 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 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; memcpy(header.magic, AI_GLB_MAGIC_NUMBER, sizeof(header.magic)); - header.version = 1; + header.version = 2; AI_SWAP4(header.version); header.length = uint32_t(sizeof(header) + sceneLength + bodyLength); @@ -624,8 +606,8 @@ namespace glTF2 { if (false) exts.PushBack(StringRef("KHR_binary_glTF"), mAl); - if (false) - exts.PushBack(StringRef("KHR_materials_common"), mAl); + // This is used to export common materials with GLTF 2. + exts.PushBack(StringRef("KHR_materials_common"), mAl); } if (!exts.Empty()) @@ -653,9 +635,9 @@ namespace glTF2 { } Value* dict; - if (!(dict = FindObject(*container, d.mDictId))) { - container->AddMember(StringRef(d.mDictId), Value().SetObject().Move(), mDoc.GetAllocator()); - dict = FindObject(*container, d.mDictId); + if (!(dict = FindArray(*container, d.mDictId))) { + container->AddMember(StringRef(d.mDictId), Value().SetArray().Move(), mDoc.GetAllocator()); + dict = FindArray(*container, d.mDictId); } for (size_t i = 0; i < d.mObjs.size(); ++i) { @@ -670,7 +652,7 @@ namespace glTF2 { Write(obj, *d.mObjs[i], *this); - dict->AddMember(StringRef(d.mObjs[i]->id), obj, mAl); + dict->PushBack(obj, mAl); } } diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index e6dc2c4..ac72790 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -434,7 +434,7 @@ void ExportSkin(Asset& mAsset, const aiMesh* aimesh, Ref& meshRef, RefmName =====> skinRef->jointNames // Find the node with id = mName. Ref nodeRef = mAsset.nodes.Get(aib->mName.C_Str()); - nodeRef->jointName = nodeRef->id; + nodeRef->jointName = nodeRef->name; unsigned int jointNamesIndex; bool addJointToJointNames = true; @@ -744,15 +744,27 @@ void glTF2Exporter::ExportMeshes() skinRef->bindShapeMatrix.isPresent = true; 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 rootNode = mAsset->nodes.Get(unsigned(0)); Ref meshNode; - std::string meshID = mAsset->meshes.Get(unsigned(0))->id; - FindMeshNode(rootNode, meshNode, meshID); - - Ref rootJoint = FindSkeletonRootJoint(skinRef); - meshNode->skeletons.push_back(rootJoint); - meshNode->skin = skinRef; + for (unsigned int meshIndex = 0; meshIndex < mAsset->meshes.Size(); ++meshIndex) { + Ref mesh = mAsset->meshes.Get(meshIndex); + bool hasBones = false; + for (unsigned int i = 0; i < mesh->primitives.size(); ++i) { + if (!mesh->primitives[i].attributes.weight.empty()) { + hasBones = true; + break; + } + } + if (!hasBones) { + continue; + } + std::string meshID = mesh->id; + FindMeshNode(rootNode, meshNode, meshID); + Ref 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& parent) { - Ref node = mAsset->nodes.Create(mAsset->FindUniqueID(n->mName.C_Str(), "node")); + std::string name = mAsset->FindUniqueID(n->mName.C_Str(), "node"); + Ref node = mAsset->nodes.Create(name); node->parent = parent; + node->name = name; if (!n->mTransformation.IsIdentity()) { node->matrix.isPresent = true; @@ -826,7 +840,7 @@ void glTF2Exporter::ExportScene() void glTF2Exporter::ExportMetadata() { AssetMetadata& asset = mAsset->asset; - asset.version = 1; + asset.version = 2; char buffer[256]; ai_snprintf(buffer, 256, "Open Asset Import Library (assimp v%d.%d.%d)", @@ -970,12 +984,12 @@ void glTF2Exporter::ExportAnimations() Animation::AnimChannel tmpAnimChannel; Animation::AnimSampler tmpAnimSampler; - tmpAnimChannel.sampler = name + "_" + channelType; + tmpAnimChannel.sampler = animRef->Samplers.size(); tmpAnimChannel.target.path = channelType; tmpAnimSampler.output = 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.interpolation = "LINEAR"; -- 2.7.4