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
{
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<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").
} target;
};
@ -977,6 +978,20 @@ namespace glTF2
Animation() {}
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")
} 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);

View File

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

View File

@ -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<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("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<uint64_t>(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<uint64_t>(bv.byteOffset), w.mAl);
obj.AddMember("byteLength", static_cast<uint64_t>(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);
}
}

View File

@ -434,7 +434,7 @@ void ExportSkin(Asset& mAsset, const aiMesh* aimesh, Ref<Mesh>& meshRef, Ref<Buf
// aib->mName =====> skinRef->jointNames
// Find the node with id = mName.
Ref<Node> 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<Node> rootNode = mAsset->nodes.Get(unsigned(0));
Ref<Node> meshNode;
std::string meshID = mAsset->meshes.Get(unsigned(0))->id;
FindMeshNode(rootNode, meshNode, meshID);
Ref<Node> rootJoint = FindSkeletonRootJoint(skinRef);
meshNode->skeletons.push_back(rootJoint);
meshNode->skin = skinRef;
for (unsigned int meshIndex = 0; meshIndex < mAsset->meshes.Size(); ++meshIndex) {
Ref<Mesh> 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<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)
{
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->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";