diff --git a/src/FBX2glTF.cpp b/src/FBX2glTF.cpp index 12c8dccc..0ff11f85 100644 --- a/src/FBX2glTF.cpp +++ b/src/FBX2glTF.cpp @@ -185,12 +185,27 @@ int main(int argc, char* argv[]) { ->type_size(-1) ->type_name("(position|normal|tangent|binormial|color|uv0|uv1|auto)"); - app.add_flag( - "-d,--draco", gltfOptions.draco.enabled, "Apply Draco mesh compression to geometries.") - ->group("Draco"); + app.add_option( + "-d,--draco", + [&](std::vector choices) -> bool { + for (const std::string choice : choices) { + if (choice == "mesh") { + gltfOptions.draco.enabledMesh = true; + } else if (choice == "animation") { + gltfOptions.draco.enabledAnimation = true; + } else { + fmt::printf("Unknown -d,--draco: %s\n", choice); + throw CLI::RuntimeError(1); + } + } + return true; + }, + "Apply Draco mesh|animation compression to geometries|animation data.") + ->type_size(-1) + ->type_name("(mesh|animation)"); app.add_option( - "--draco-compression-level", + "--draco-mesh-compression-level", gltfOptions.draco.compressionLevel, "The compression level to tune Draco to.", true) @@ -202,7 +217,7 @@ int main(int argc, char* argv[]) { gltfOptions.draco.quantBitsPosition, "How many bits to quantize position to.", true) - ->check(CLI::Range(1, 32)) + ->check(CLI::Range(1, 30)) ->group("Draco"); app.add_option( @@ -210,7 +225,7 @@ int main(int argc, char* argv[]) { gltfOptions.draco.quantBitsTexCoord, "How many bits to quantize UV coordinates to.", true) - ->check(CLI::Range(1, 32)) + ->check(CLI::Range(1, 30)) ->group("Draco"); app.add_option( @@ -218,7 +233,7 @@ int main(int argc, char* argv[]) { gltfOptions.draco.quantBitsNormal, "How many bits to quantize nornals to.", true) - ->check(CLI::Range(1, 32)) + ->check(CLI::Range(1, 30)) ->group("Draco"); app.add_option( @@ -226,7 +241,7 @@ int main(int argc, char* argv[]) { gltfOptions.draco.quantBitsColor, "How many bits to quantize colors to.", true) - ->check(CLI::Range(1, 32)) + ->check(CLI::Range(1, 30)) ->group("Draco"); app.add_option( @@ -234,9 +249,33 @@ int main(int argc, char* argv[]) { gltfOptions.draco.quantBitsGeneric, "How many bits to quantize all other vertex attributes to.", true) - ->check(CLI::Range(1, 32)) + ->check(CLI::Range(1, 30)) + ->group("Draco"); + + app.add_option( + "--draco-animation-compression-level", + gltfOptions.draco.animationCompressionLevel, + "The animation compression level to tune Draco to.", + true) + ->check(CLI::Range(0, 10)) ->group("Draco"); + app.add_option( + "--draco-bits-for-timestamp", + gltfOptions.draco.quantBitsTimestamp, + "How many bits to quantize timestamp to.", + true) + ->check(CLI::Range(1, 30)) + ->group("Draco"); + + app.add_option( + "--draco-bits-for-keyframe", + gltfOptions.draco.quantBitsKeyframe, + "How many bits to quantize keyframe to.", + true) + ->check(CLI::Range(1, 30)) + ->group("Draco"); + CLI11_PARSE(app, argc, argv); bool do_flip_u = false; @@ -249,11 +288,11 @@ int main(int argc, char* argv[]) { std::vector> texturesTransforms; if (do_flip_u || do_flip_v) { if (do_flip_u && do_flip_v) { - texturesTransforms.emplace_back([](Vec2f uv) { return Vec2f(1.0 - uv[0], 1.0 - uv[1]); }); + texturesTransforms.emplace_back([](Vec2f uv) { return Vec2f(1.0f - uv[0], 1.0f - uv[1]); }); } else if (do_flip_u) { - texturesTransforms.emplace_back([](Vec2f uv) { return Vec2f(1.0 - uv[0], uv[1]); }); + texturesTransforms.emplace_back([](Vec2f uv) { return Vec2f(1.0f - uv[0], uv[1]); }); } else { - texturesTransforms.emplace_back([](Vec2f uv) { return Vec2f(uv[0], 1.0 - uv[1]); }); + texturesTransforms.emplace_back([](Vec2f uv) { return Vec2f(uv[0], 1.0f - uv[1]); }); } } if (verboseOutput) { @@ -366,7 +405,7 @@ int main(int argc, char* argv[]) { if (data_render_model->binary->empty() == false) { const unsigned char* binaryData = &(*data_render_model->binary)[0]; - unsigned long binarySize = data_render_model->binary->size(); + size_t binarySize = data_render_model->binary->size(); if (fwrite(binaryData, binarySize, 1, fp) != 1) { fmt::fprintf( stderr, "ERROR: Failed to write %lu bytes to file '%s'.\n", binarySize, binaryPath); diff --git a/src/FBX2glTF.h b/src/FBX2glTF.h index e075bbde..45c89259 100644 --- a/src/FBX2glTF.h +++ b/src/FBX2glTF.h @@ -90,15 +90,26 @@ struct GltfOptions { /** If non-binary, whether to inline all resources, for a single (large) .glTF file. */ bool embedResources{false}; - /** Whether and how to use KHR_draco_mesh_compression to minimize static geometry size. */ + /** Whether and how to use KHR_draco_mesh_compression & Draco_animation_compression to minimize static geometry size. */ struct { - bool enabled = false; + bool enabledMesh = false; int compressionLevel = 7; int quantBitsPosition = 14; int quantBitsTexCoord = 10; int quantBitsNormal = 10; int quantBitsColor = 8; int quantBitsGeneric = 8; + // int compressionLevel = -1; // 7 + // int quantBitsPosition = -1; // 14 + // int quantBitsTexCoord = -1; // 10 + // int quantBitsNormal = -1; // 10 + // int quantBitsColor = -1; // 8 + // int quantBitsGeneric = -1; // 8 + + bool enabledAnimation = false; + int animationCompressionLevel = -1; // 5 + int quantBitsTimestamp = -1; + int quantBitsKeyframe = -1; } draco; /** Whether to include FBX User Properties as 'extras' metadata in glTF nodes. */ diff --git a/src/gltf/GltfModel.hpp b/src/gltf/GltfModel.hpp index 9a154374..6f094ad2 100644 --- a/src/gltf/GltfModel.hpp +++ b/src/gltf/GltfModel.hpp @@ -122,6 +122,47 @@ class GltfModel { return accessor; }; + template + std::shared_ptr AddTimestampsToAnimation( + BufferData& buffer, + AnimationData& animationData, + const std::vector& timestamps, + const GLType& glType, + const draco::DataType dracoComponentType) { + std::shared_ptr accessor; + if (dracoComponentType != draco::DT_INVALID && animationData.dracoKeyframeAnimation != nullptr) { + accessor = accessors.hold(new AccessorData(glType)); + accessor->count = to_uint32(timestamps.size()); + animationData.AddDracoTimestamps(*accessor, timestamps); + } else { + accessor = AddAccessorAndView(buffer, glType, timestamps); + animationData.AddTimestamps(*accessor); + } + return accessor; + }; + + template + std::shared_ptr AddChannelToAnimation( + BufferData& buffer, + AnimationData& animationData, + const NodeData& nDat, + const ChannelDefinition& channelDef) { + std::shared_ptr accessor; + if (channelDef.dracoComponentType != draco::DT_INVALID && animationData.dracoKeyframeAnimation != nullptr) { + accessor = accessors.hold(new AccessorData(channelDef.glType)); + accessor->count = to_uint32(channelDef.channelData.size()); + animationData.AddDracoNodeChannel( + nDat, + *accessor, + channelDef.path, + channelDef); + } else { + accessor = AddAccessorAndView(buffer, channelDef.glType, channelDef.channelData); + animationData.AddNodeChannel(nDat, *accessor, channelDef.path); + } + return accessor; + }; + template void serializeHolder(json& glTFJson, std::string key, const Holder holder) { if (!holder.ptrs.empty()) { diff --git a/src/gltf/Raw2Gltf.cpp b/src/gltf/Raw2Gltf.cpp index 0e2c5926..4bbed129 100644 --- a/src/gltf/Raw2Gltf.cpp +++ b/src/gltf/Raw2Gltf.cpp @@ -149,6 +149,8 @@ ModelData* Raw2Gltf( nodesById.insert(std::make_pair(node.id, nodeData)); } + std::vector> accessors; + // // animations // @@ -162,11 +164,22 @@ ModelData* Raw2Gltf( continue; } - auto accessor = gltf->AddAccessorAndView(buffer, GLT_FLOAT, animation.times); - accessor->min = {*std::min_element(std::begin(animation.times), std::end(animation.times))}; - accessor->max = {*std::max_element(std::begin(animation.times), std::end(animation.times))}; + AnimationData* animationData = nullptr; + if (options.draco.enabledAnimation) { + // create Draco KeyframeAnimation + auto dracoKeyframeAnimation(std::make_shared()); + animationData = new AnimationData(animation.name, dracoKeyframeAnimation); + } else { + animationData = new AnimationData(animation.name); + } + AnimationData& aDat = *gltf->animations.hold(animationData); + + const auto timestampsAccessor = + gltf->AddTimestampsToAnimation(buffer, aDat, animation.times, GLT_FLOAT, draco::DT_FLOAT32); + timestampsAccessor->min = { *std::min_element(std::begin(animation.times), std::end(animation.times)) }; + timestampsAccessor->max = { *std::max_element(std::begin(animation.times), std::end(animation.times)) }; + accessors.emplace_back(timestampsAccessor); - AnimationData& aDat = *gltf->animations.hold(new AnimationData(animation.name, *accessor)); if (verboseOutput) { fmt::printf( "Animation '%s' has %lu channels:\n", @@ -191,26 +204,82 @@ ModelData* Raw2Gltf( NodeData& nDat = require(nodesById, node.id); if (!channel.translations.empty()) { - aDat.AddNodeChannel( - nDat, - *gltf->AddAccessorAndView(buffer, GLT_VEC3F, channel.translations), - "translation"); + const ChannelDefinition CHANNEL_TRANSLATIONS( + "translation", + channel.translations, + GLT_VEC3F, + draco::DT_FLOAT32); + const auto _ = + gltf->AddChannelToAnimation(buffer, aDat, nDat, CHANNEL_TRANSLATIONS); + accessors.emplace_back(_); } if (!channel.rotations.empty()) { - aDat.AddNodeChannel( - nDat, *gltf->AddAccessorAndView(buffer, GLT_QUATF, channel.rotations), "rotation"); + const ChannelDefinition CHANNEL_ROTATIONS( + "rotation", + channel.rotations, + GLT_QUATF, + draco::DT_FLOAT32); + const auto _ = + gltf->AddChannelToAnimation(buffer, aDat, nDat, CHANNEL_ROTATIONS); + accessors.emplace_back(_); } if (!channel.scales.empty()) { - aDat.AddNodeChannel( - nDat, *gltf->AddAccessorAndView(buffer, GLT_VEC3F, channel.scales), "scale"); + const ChannelDefinition CHANNEL_SCALES( + "scale", + channel.scales, + GLT_VEC3F, + draco::DT_FLOAT32); + const auto _ = + gltf->AddChannelToAnimation(buffer, aDat, nDat, CHANNEL_SCALES); + accessors.emplace_back(_); } if (!channel.weights.empty()) { - aDat.AddNodeChannel( - nDat, - *gltf->AddAccessorAndView(buffer, {CT_FLOAT, 1, "SCALAR"}, channel.weights), - "weights"); + const ChannelDefinition CHANNEL_WEIGHTS( + "weights", + channel.weights, + { CT_FLOAT, 1, "SCALAR" }, + draco::DT_FLOAT32); + const auto _ = + gltf->AddChannelToAnimation(buffer, aDat, nDat, CHANNEL_WEIGHTS); + accessors.emplace_back(_); } } + + if (options.draco.enabledAnimation) { + draco::EncoderOptions encodeOptions = draco::EncoderOptions::CreateDefaultOptions(); + if (options.draco.animationCompressionLevel != -1) { + int dracoSpeed = 10 - options.draco.animationCompressionLevel; + int en = dracoSpeed; + int de = dracoSpeed; + encodeOptions.SetSpeed(en, de); + } + + if (-1 != options.draco.quantBitsTimestamp) { + // set quantization for timestamps. + encodeOptions.SetAttributeInt(0, "quantization_bits", options.draco.quantBitsTimestamp); + } + + if (-1 != options.draco.quantBitsKeyframe) { + // set quantization for keyframes. + for (int i = 1; i <= aDat.dracoKeyframeAnimation->num_animations(); ++i) { + encodeOptions.SetAttributeInt(i, "quantization_bits", options.draco.quantBitsKeyframe); + } + } + + draco::EncoderBuffer dracoBuffer; + draco::KeyframeAnimationEncoder encoder; + draco::Status status = encoder.EncodeKeyframeAnimation(*(aDat.dracoKeyframeAnimation), encodeOptions, &dracoBuffer); + assert(status.code() == draco::Status::OK); + auto view = gltf->AddRawBufferView(buffer, dracoBuffer.data(), to_uint32(dracoBuffer.size())); + dracoBuffer.Clear(); + + for (auto accessor : accessors) + { + accessor->bufferView = view->ix; + accessor->byteOffset = -1; + } + } + accessors.clear(); } // @@ -438,7 +507,7 @@ ModelData* Raw2Gltf( surfaceModel.GetVertexCount() > 65535); std::shared_ptr primitive; - if (options.draco.enabled) { + if (options.draco.enabledMesh) { size_t triangleCount = surfaceModel.GetTriangleCount(); // initialize Draco mesh with vertex index information @@ -454,10 +523,12 @@ ModelData* Raw2Gltf( dracoMesh->SetFace(draco::FaceIndex(ii), face); } - AccessorData& indexes = - *gltf->accessors.hold(new AccessorData(useLongIndices ? GLT_UINT : GLT_USHORT)); - indexes.count = to_uint32(3 * triangleCount); - primitive.reset(new PrimitiveData(indexes, mData, dracoMesh)); + std::shared_ptr indexes = + gltf->accessors.hold(new AccessorData(useLongIndices ? GLT_UINT : GLT_USHORT)); + indexes->count = to_uint32(3 * triangleCount); + primitive.reset(new PrimitiveData(*indexes, mData, dracoMesh)); + accessors.emplace_back(indexes); + } else { const AccessorData& indexes = *gltf->AddAccessorWithView( *gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ELEMENT_ARRAY_BUFFER), @@ -483,6 +554,7 @@ ModelData* Raw2Gltf( accessor->min = toStdVec(rawSurface.bounds.min); accessor->max = toStdVec(rawSurface.bounds.max); + accessors.emplace_back(accessor); } if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_NORMAL) != 0) { const AttributeDefinition ATTR_NORMAL( @@ -492,12 +564,13 @@ ModelData* Raw2Gltf( draco::GeometryAttribute::NORMAL, draco::DT_FLOAT32); const auto _ = - gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_NORMAL); - } + gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_NORMAL); + accessors.emplace_back(_); + } if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_TANGENT) != 0) { const AttributeDefinition ATTR_TANGENT("TANGENT", &RawVertex::tangent, GLT_VEC4F); - const auto _ = gltf->AddAttributeToPrimitive( - buffer, surfaceModel, *primitive, ATTR_TANGENT); + const auto _ = gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_TANGENT); + accessors.emplace_back(_); } if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_COLOR) != 0) { const AttributeDefinition ATTR_COLOR( @@ -508,6 +581,7 @@ ModelData* Raw2Gltf( draco::DT_FLOAT32); const auto _ = gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_COLOR); + accessors.emplace_back(_); } if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_UV0) != 0) { const AttributeDefinition ATTR_TEXCOORD_0( @@ -518,6 +592,7 @@ ModelData* Raw2Gltf( draco::DT_FLOAT32); const auto _ = gltf->AddAttributeToPrimitive( buffer, surfaceModel, *primitive, ATTR_TEXCOORD_0); + accessors.emplace_back(_); } if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_UV1) != 0) { const AttributeDefinition ATTR_TEXCOORD_1( @@ -528,6 +603,7 @@ ModelData* Raw2Gltf( draco::DT_FLOAT32); const auto _ = gltf->AddAttributeToPrimitive( buffer, surfaceModel, *primitive, ATTR_TEXCOORD_1); + accessors.emplace_back(_); } if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES) != 0) { const AttributeDefinition ATTR_JOINTS( @@ -538,6 +614,7 @@ ModelData* Raw2Gltf( draco::DT_UINT16); const auto _ = gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_JOINTS); + accessors.emplace_back(_); } if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS) != 0) { const AttributeDefinition ATTR_WEIGHTS( @@ -548,6 +625,7 @@ ModelData* Raw2Gltf( draco::DT_FLOAT32); const auto _ = gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_WEIGHTS); + accessors.emplace_back(_); } // each channel present in the mesh always ends up a target in the primitive @@ -599,7 +677,7 @@ ModelData* Raw2Gltf( primitive->AddTarget(pAcc.get(), nAcc.get(), tAcc.get()); } } - if (options.draco.enabled) { + if (options.draco.enabledMesh) { // Set up the encoder. draco::Encoder encoder; @@ -634,7 +712,15 @@ ModelData* Raw2Gltf( auto view = gltf->AddRawBufferView(buffer, dracoBuffer.data(), to_uint32(dracoBuffer.size())); primitive->NoteDracoBuffer(*view); + dracoBuffer.Clear(); + + for (auto accessor : accessors) + { + accessor->bufferView = view->ix; + accessor->byteOffset = -1; + } } + accessors.clear(); mesh->AddPrimitive(primitive); } @@ -800,10 +886,14 @@ ModelData* Raw2Gltf( if (!gltf->lights.ptrs.empty()) { extensionsUsed.push_back(KHR_LIGHTS_PUNCTUAL); } - if (options.draco.enabled) { + if (options.draco.enabledMesh) { extensionsUsed.push_back(KHR_DRACO_MESH_COMPRESSION); extensionsRequired.push_back(KHR_DRACO_MESH_COMPRESSION); } + if (options.draco.enabledAnimation) { + extensionsUsed.push_back(DRACO_ANIMATION_COMPRESSION); + extensionsRequired.push_back(DRACO_ANIMATION_COMPRESSION); + } json glTFJson{{"asset", {{"generator", "FBX2glTF v" + FBX2GLTF_VERSION}, {"version", "2.0"}}}, {"scene", rootScene.ix}}; diff --git a/src/gltf/Raw2Gltf.hpp b/src/gltf/Raw2Gltf.hpp index f30b5458..cbc05c19 100644 --- a/src/gltf/Raw2Gltf.hpp +++ b/src/gltf/Raw2Gltf.hpp @@ -14,6 +14,8 @@ // This can be a macro under Windows, confusing Draco #undef ERROR #include +#include +#include #include "FBX2glTF.h" #include "raw/RawModel.hpp" @@ -21,6 +23,7 @@ const std::string KHR_DRACO_MESH_COMPRESSION = "KHR_draco_mesh_compression"; const std::string KHR_MATERIALS_CMN_UNLIT = "KHR_materials_unlit"; const std::string KHR_LIGHTS_PUNCTUAL = "KHR_lights_punctual"; +const std::string DRACO_ANIMATION_COMPRESSION = "Draco_animation_compression"; const std::string extBufferFilename = "buffer.bin"; @@ -98,6 +101,41 @@ struct GLType { ((T*)buf)[3] = quaternion.scalar(); } + template + const std::vector& toStdVec(const std::vector& scalars) const { + return scalars; + } + + template + std::vector toStdVec(const std::vector>& vectors) const { + std::vector vec(vectors.size() * d); + std::vector component(sizeof(T) * d); + for (uint32_t ii = 0; ii < vectors.size(); ii++) { + uint8_t* ptr = &component[0]; + this->write(ptr, vectors[ii]); + const T* typePtr = (const T*)ptr; + for (uint32_t jj = 0; jj < d; ++jj) { + vec[ii * d + jj] = *(typePtr + jj); + } + } + return vec; + } + + template + std::vector toStdVec(const std::vector>& quaternions) const { + std::vector vec(quaternions.size() * 4); + std::vector component(sizeof(T) * 4); + for (uint32_t ii = 0; ii < quaternions.size(); ii++) { + uint8_t* ptr = &component[0]; + this->write(ptr, quaternions[ii]); + const T* typePtr = (const T*)ptr; + for (uint32_t jj = 0; jj < 4; ++jj) { + vec[ii * 4 + jj] = *(typePtr + jj); + } + } + return vec; + } + const ComponentType componentType; const uint8_t count; const std::string dataType; @@ -138,7 +176,7 @@ struct AttributeDefinition { const GLType& _glType, const draco::GeometryAttribute::Type dracoAttribute, const draco::DataType dracoComponentType) - : gltfName(gltfName), + : gltfName(std::move(gltfName)), rawAttributeIx(rawAttributeIx), glType(_glType), dracoAttribute(dracoAttribute), @@ -148,13 +186,40 @@ struct AttributeDefinition { const std::string gltfName, const T RawVertex::*rawAttributeIx, const GLType& _glType) - : gltfName(gltfName), + : gltfName(std::move(gltfName)), rawAttributeIx(rawAttributeIx), glType(_glType), dracoAttribute(draco::GeometryAttribute::INVALID), dracoComponentType(draco::DataType::DT_INVALID) {} }; +template +struct ChannelDefinition { + const std::string path; + const std::vector& channelData; + const GLType glType; + const draco::DataType dracoComponentType; + + ChannelDefinition( + const std::string path, + const std::vector& channelData, + const GLType& glType, + const draco::DataType dracoComponentType) + : path(std::move(path)), + channelData(channelData), + glType(glType), + dracoComponentType(dracoComponentType) {} + + ChannelDefinition( + const std::string path, + const std::vector& channelData, + const GLType& glType) + : path(std::move(path)), + channelData(channelData), + glType(glType), + dracoComponentType(draco::DataType::DT_INVALID) {} +}; + struct AccessorData; struct AnimationData; struct BufferData; diff --git a/src/gltf/properties/AccessorData.cpp b/src/gltf/properties/AccessorData.cpp index 3626c4e3..4ecb39e3 100644 --- a/src/gltf/properties/AccessorData.cpp +++ b/src/gltf/properties/AccessorData.cpp @@ -25,6 +25,8 @@ json AccessorData::serialize() const { {"componentType", type.componentType.glType}, {"type", type.dataType}, {"count", count}}; if (bufferView >= 0) { result["bufferView"] = bufferView; + } + if (byteOffset >= 0) { result["byteOffset"] = byteOffset; } if (!min.empty()) { diff --git a/src/gltf/properties/AccessorData.hpp b/src/gltf/properties/AccessorData.hpp index 5a72e70a..2780a9bb 100644 --- a/src/gltf/properties/AccessorData.hpp +++ b/src/gltf/properties/AccessorData.hpp @@ -34,10 +34,10 @@ struct AccessorData : Holdable { return type.byteStride() * count; } - const int bufferView; + /*const*/ int bufferView; const GLType type; - unsigned int byteOffset; + /*unsigned*/ int byteOffset; unsigned int count; std::vector min; std::vector max; diff --git a/src/gltf/properties/AnimationData.cpp b/src/gltf/properties/AnimationData.cpp index 9b26d438..15781187 100644 --- a/src/gltf/properties/AnimationData.cpp +++ b/src/gltf/properties/AnimationData.cpp @@ -10,11 +10,20 @@ #include -#include "AccessorData.hpp" #include "NodeData.hpp" -AnimationData::AnimationData(std::string name, const AccessorData& timeAccessor) - : Holdable(), name(std::move(name)), timeAccessor(timeAccessor.ix) {} +AnimationData::AnimationData(std::string name) + : Holdable(), + name(std::move(name)) {} + +AnimationData::AnimationData(std::string name, std::shared_ptr dracoKeyframeAnimation) + : Holdable(), + name(std::move(name)), + dracoKeyframeAnimation(dracoKeyframeAnimation) {} + +void AnimationData::AddTimestamps(const AccessorData& timeAccessor) { + this->timeAccessor = timeAccessor.ix; +} // assumption: 1-to-1 relationship between channels and samplers; this is a simplification on what // glTF can express, but it means we can rely on samplerIx == channelIx throughout an animation diff --git a/src/gltf/properties/AnimationData.hpp b/src/gltf/properties/AnimationData.hpp index 31a1be64..ca6c4057 100644 --- a/src/gltf/properties/AnimationData.hpp +++ b/src/gltf/properties/AnimationData.hpp @@ -9,14 +9,44 @@ #pragma once #include "gltf/Raw2Gltf.hpp" +#include "AccessorData.hpp" struct AnimationData : Holdable { - AnimationData(std::string name, const AccessorData& timeAccessor); + AnimationData(std::string name); + + AnimationData(std::string name, std::shared_ptr dracoKeyframeAnimation); + + void AddTimestamps(const AccessorData& timeAccessor); + + template + void AddDracoTimestamps(const AccessorData& timeAccessor, const std::vector& timestamps) { + this->timeAccessor = timeAccessor.ix; + + std::vector dracoTimestamps(timestamps.begin(), timestamps.end()); + dracoKeyframeAnimation->SetTimestamps(dracoTimestamps); + } // assumption: 1-to-1 relationship between channels and samplers; this is a simplification on what // glTF can express, but it means we can rely on samplerIx == channelIx throughout an animation void AddNodeChannel(const NodeData& node, const AccessorData& accessor, std::string path); + template + void AddDracoNodeChannel( + const NodeData& node, + const AccessorData& accessor, + const std::string& path, + const ChannelDefinition& keyframe) { + assert(channels.size() == samplers.size()); + uint32_t ix = to_uint32(channels.size()); + channels.emplace_back(channel_t(ix, node, std::move(path))); + samplers.emplace_back(sampler_t(timeAccessor, accessor.ix)); + + dracoKeyframeAnimation->AddKeyframes( + keyframe.dracoComponentType, + keyframe.glType.count, + keyframe.glType.toStdVec(keyframe.channelData)); + } + json serialize() const override; struct channel_t { @@ -35,9 +65,10 @@ struct AnimationData : Holdable { }; const std::string name; - const uint32_t timeAccessor; + uint32_t timeAccessor; std::vector channels; std::vector samplers; + std::shared_ptr dracoKeyframeAnimation; }; void to_json(json& j, const AnimationData::channel_t& data); diff --git a/src/gltf/properties/PrimitiveData.hpp b/src/gltf/properties/PrimitiveData.hpp index 6e87e533..44cd6ac2 100644 --- a/src/gltf/properties/PrimitiveData.hpp +++ b/src/gltf/properties/PrimitiveData.hpp @@ -36,7 +36,7 @@ struct PrimitiveData { const AccessorData* tangents); template - void AddDracoAttrib(const AttributeDefinition attribute, const std::vector& attribArr) { + void AddDracoAttrib(const AttributeDefinition& attribute, const std::vector& attribArr) { draco::PointAttribute att; int8_t componentCount = attribute.glType.count; att.Init(