Add animation compression:draco-animation.
This commit is contained in:
parent
9853625ba1
commit
6c3d075193
|
@ -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<std::string> 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,7 +249,31 @@ 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);
|
||||
|
@ -249,11 +288,11 @@ int main(int argc, char* argv[]) {
|
|||
std::vector<std::function<Vec2f(Vec2f)>> 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);
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -122,6 +122,47 @@ class GltfModel {
|
|||
return accessor;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
std::shared_ptr<AccessorData> AddTimestampsToAnimation(
|
||||
BufferData& buffer,
|
||||
AnimationData& animationData,
|
||||
const std::vector<T>& timestamps,
|
||||
const GLType& glType,
|
||||
const draco::DataType dracoComponentType) {
|
||||
std::shared_ptr<AccessorData> 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 <class T>
|
||||
std::shared_ptr<AccessorData> AddChannelToAnimation(
|
||||
BufferData& buffer,
|
||||
AnimationData& animationData,
|
||||
const NodeData& nDat,
|
||||
const ChannelDefinition<T>& channelDef) {
|
||||
std::shared_ptr<AccessorData> 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 <class T>
|
||||
void serializeHolder(json& glTFJson, std::string key, const Holder<T> holder) {
|
||||
if (!holder.ptrs.empty()) {
|
||||
|
|
|
@ -149,6 +149,8 @@ ModelData* Raw2Gltf(
|
|||
nodesById.insert(std::make_pair(node.id, nodeData));
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<AccessorData>> 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<draco::KeyframeAnimation>());
|
||||
animationData = new AnimationData(animation.name, dracoKeyframeAnimation);
|
||||
} else {
|
||||
animationData = new AnimationData(animation.name);
|
||||
}
|
||||
AnimationData& aDat = *gltf->animations.hold(animationData);
|
||||
|
||||
const auto timestampsAccessor =
|
||||
gltf->AddTimestampsToAnimation<float>(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<Vec3f> CHANNEL_TRANSLATIONS(
|
||||
"translation",
|
||||
channel.translations,
|
||||
GLT_VEC3F,
|
||||
draco::DT_FLOAT32);
|
||||
const auto _ =
|
||||
gltf->AddChannelToAnimation<Vec3f>(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<Quatf> CHANNEL_ROTATIONS(
|
||||
"rotation",
|
||||
channel.rotations,
|
||||
GLT_QUATF,
|
||||
draco::DT_FLOAT32);
|
||||
const auto _ =
|
||||
gltf->AddChannelToAnimation<Quatf>(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<Vec3f> CHANNEL_SCALES(
|
||||
"scale",
|
||||
channel.scales,
|
||||
GLT_VEC3F,
|
||||
draco::DT_FLOAT32);
|
||||
const auto _ =
|
||||
gltf->AddChannelToAnimation<Vec3f>(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<float> CHANNEL_WEIGHTS(
|
||||
"weights",
|
||||
channel.weights,
|
||||
{ CT_FLOAT, 1, "SCALAR" },
|
||||
draco::DT_FLOAT32);
|
||||
const auto _ =
|
||||
gltf->AddChannelToAnimation<float>(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<PrimitiveData> 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<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));
|
||||
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<Vec3f> ATTR_NORMAL(
|
||||
|
@ -493,11 +565,12 @@ ModelData* Raw2Gltf(
|
|||
draco::DT_FLOAT32);
|
||||
const auto _ =
|
||||
gltf->AddAttributeToPrimitive<Vec3f>(buffer, surfaceModel, *primitive, ATTR_NORMAL);
|
||||
accessors.emplace_back(_);
|
||||
}
|
||||
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_TANGENT) != 0) {
|
||||
const AttributeDefinition<Vec4f> ATTR_TANGENT("TANGENT", &RawVertex::tangent, GLT_VEC4F);
|
||||
const auto _ = gltf->AddAttributeToPrimitive<Vec4f>(
|
||||
buffer, surfaceModel, *primitive, ATTR_TANGENT);
|
||||
const auto _ = gltf->AddAttributeToPrimitive<Vec4f>(buffer, surfaceModel, *primitive, ATTR_TANGENT);
|
||||
accessors.emplace_back(_);
|
||||
}
|
||||
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_COLOR) != 0) {
|
||||
const AttributeDefinition<Vec4f> ATTR_COLOR(
|
||||
|
@ -508,6 +581,7 @@ ModelData* Raw2Gltf(
|
|||
draco::DT_FLOAT32);
|
||||
const auto _ =
|
||||
gltf->AddAttributeToPrimitive<Vec4f>(buffer, surfaceModel, *primitive, ATTR_COLOR);
|
||||
accessors.emplace_back(_);
|
||||
}
|
||||
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_UV0) != 0) {
|
||||
const AttributeDefinition<Vec2f> ATTR_TEXCOORD_0(
|
||||
|
@ -518,6 +592,7 @@ ModelData* Raw2Gltf(
|
|||
draco::DT_FLOAT32);
|
||||
const auto _ = gltf->AddAttributeToPrimitive<Vec2f>(
|
||||
buffer, surfaceModel, *primitive, ATTR_TEXCOORD_0);
|
||||
accessors.emplace_back(_);
|
||||
}
|
||||
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_UV1) != 0) {
|
||||
const AttributeDefinition<Vec2f> ATTR_TEXCOORD_1(
|
||||
|
@ -528,6 +603,7 @@ ModelData* Raw2Gltf(
|
|||
draco::DT_FLOAT32);
|
||||
const auto _ = gltf->AddAttributeToPrimitive<Vec2f>(
|
||||
buffer, surfaceModel, *primitive, ATTR_TEXCOORD_1);
|
||||
accessors.emplace_back(_);
|
||||
}
|
||||
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES) != 0) {
|
||||
const AttributeDefinition<Vec4i> ATTR_JOINTS(
|
||||
|
@ -538,6 +614,7 @@ ModelData* Raw2Gltf(
|
|||
draco::DT_UINT16);
|
||||
const auto _ =
|
||||
gltf->AddAttributeToPrimitive<Vec4i>(buffer, surfaceModel, *primitive, ATTR_JOINTS);
|
||||
accessors.emplace_back(_);
|
||||
}
|
||||
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS) != 0) {
|
||||
const AttributeDefinition<Vec4f> ATTR_WEIGHTS(
|
||||
|
@ -548,6 +625,7 @@ ModelData* Raw2Gltf(
|
|||
draco::DT_FLOAT32);
|
||||
const auto _ =
|
||||
gltf->AddAttributeToPrimitive<Vec4f>(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}};
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
// This can be a macro under Windows, confusing Draco
|
||||
#undef ERROR
|
||||
#include <draco/compression/encode.h>
|
||||
#include <draco/animation/keyframe_animation.h>
|
||||
#include <draco/animation/keyframe_animation_encoder.h>
|
||||
|
||||
#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 <class T>
|
||||
const std::vector<T>& toStdVec(const std::vector<T>& scalars) const {
|
||||
return scalars;
|
||||
}
|
||||
|
||||
template <class T, int d>
|
||||
std::vector<T> toStdVec(const std::vector<mathfu::Vector<T, d>>& vectors) const {
|
||||
std::vector<T> vec(vectors.size() * d);
|
||||
std::vector<uint8_t> 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 <class T>
|
||||
std::vector<T> toStdVec(const std::vector<mathfu::Quaternion<T>>& quaternions) const {
|
||||
std::vector<T> vec(quaternions.size() * 4);
|
||||
std::vector<uint8_t> 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 <class T>
|
||||
struct ChannelDefinition {
|
||||
const std::string path;
|
||||
const std::vector<T>& channelData;
|
||||
const GLType glType;
|
||||
const draco::DataType dracoComponentType;
|
||||
|
||||
ChannelDefinition(
|
||||
const std::string path,
|
||||
const std::vector<T>& 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<T>& channelData,
|
||||
const GLType& glType)
|
||||
: path(std::move(path)),
|
||||
channelData(channelData),
|
||||
glType(glType),
|
||||
dracoComponentType(draco::DataType::DT_INVALID) {}
|
||||
};
|
||||
|
||||
struct AccessorData;
|
||||
struct AnimationData;
|
||||
struct BufferData;
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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<float> min;
|
||||
std::vector<float> max;
|
||||
|
|
|
@ -13,8 +13,18 @@
|
|||
#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<draco::KeyframeAnimation> 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
|
||||
|
|
|
@ -11,12 +11,41 @@
|
|||
#include "gltf/Raw2Gltf.hpp"
|
||||
|
||||
struct AnimationData : Holdable {
|
||||
AnimationData(std::string name, const AccessorData& timeAccessor);
|
||||
AnimationData(std::string name);
|
||||
|
||||
AnimationData(std::string name, std::shared_ptr<draco::KeyframeAnimation> dracoKeyframeAnimation);
|
||||
|
||||
void AddTimestamps(const AccessorData& timeAccessor);
|
||||
|
||||
template <class T>
|
||||
void AddDracoTimestamps(const AccessorData& timeAccessor, const std::vector<T>& timestamps) {
|
||||
this->timeAccessor = timeAccessor.ix;
|
||||
|
||||
std::vector<draco::KeyframeAnimation::TimestampType> 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 <class T>
|
||||
void AddDracoNodeChannel(
|
||||
const NodeData& node,
|
||||
const AccessorData& accessor,
|
||||
const std::string& path,
|
||||
const ChannelDefinition<T>& 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 +64,10 @@ struct AnimationData : Holdable {
|
|||
};
|
||||
|
||||
const std::string name;
|
||||
const uint32_t timeAccessor;
|
||||
uint32_t timeAccessor;
|
||||
std::vector<channel_t> channels;
|
||||
std::vector<sampler_t> samplers;
|
||||
std::shared_ptr<draco::KeyframeAnimation> dracoKeyframeAnimation;
|
||||
};
|
||||
|
||||
void to_json(json& j, const AnimationData::channel_t& data);
|
||||
|
|
|
@ -36,7 +36,7 @@ struct PrimitiveData {
|
|||
const AccessorData* tangents);
|
||||
|
||||
template <class T>
|
||||
void AddDracoAttrib(const AttributeDefinition<T> attribute, const std::vector<T>& attribArr) {
|
||||
void AddDracoAttrib(const AttributeDefinition<T>& attribute, const std::vector<T>& attribArr) {
|
||||
draco::PointAttribute att;
|
||||
int8_t componentCount = attribute.glType.count;
|
||||
att.Init(
|
||||
|
|
Loading…
Reference in New Issue