Skip to content

Commit

Permalink
Animation pointer
Browse files Browse the repository at this point in the history
pt
  • Loading branch information
shrinktofit committed Aug 24, 2023
1 parent 331edd2 commit 889feb8
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 7 deletions.
36 changes: 33 additions & 3 deletions Cli/ReadCliArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,13 @@ getCommandLineArgsU8(int argc_, const char *argv_[]) {

template <auto MemberPtr> struct ConvertOptionBindingTrait {};

template <>
struct ConvertOptionBindingTrait<&bee::ConvertOptions::export_node_visibility> {
constexpr static auto name = "export-node-visibility";
constexpr static auto description = "Export node visibility.";
constexpr static auto default_value = "false";
};

template <>
struct ConvertOptionBindingTrait<&bee::ConvertOptions::no_mesh_instancing> {
constexpr static auto name = "no-mesh-instancing";
Expand All @@ -95,6 +102,15 @@ struct ConvertOptionBindingTrait<
constexpr static auto default_value = "1e-5";
};

template <>
struct ConvertOptionBindingTrait<
&bee::ConvertOptions::use_extension_khr_animation_pointer> {
constexpr static auto name = "extension-khr-animation-pointer";
constexpr static auto description =
"Exports animation using extension KHR-Animation-Pointer";
constexpr static auto default_value = "false";
};

template <auto memberPtr> struct convert_option_binding_helper {};

template <typename OptionType, OptionType bee::ConvertOptions::*optionMemberPtr>
Expand Down Expand Up @@ -202,6 +218,9 @@ std::optional<CliArgs> readCliArgs(std::span<std::string_view> args_) {
cxxopts::value<decltype(cliArgs.convertOptions.animationBakeRate)>()
->default_value("30"));

add_cxx_option
.template operator()<&bee::ConvertOptions::export_node_visibility>();

add_cxx_option.template
operator()<&bee::ConvertOptions::animation_position_error_multiplier>();

Expand All @@ -223,6 +242,11 @@ std::optional<CliArgs> readCliArgs(std::span<std::string_view> args_) {

options.add_options()("verbose", "Verbose output.",
cxxopts::value<bool>()->default_value("false"));

// --enable_extension_khr_animation_pointer
add_cxx_option.template
operator()<&bee::ConvertOptions::use_extension_khr_animation_pointer>();

options.add_options()(
"log-file",
"Specify the log file(logs are outputed as JSON). If not "
Expand Down Expand Up @@ -311,15 +335,18 @@ std::optional<CliArgs> readCliArgs(std::span<std::string_view> args_) {
.template operator()<&bee::ConvertOptions::no_mesh_instancing>();

if (cliParseResult.count("match-mesh-names")) {
cliArgs.convertOptions.match_mesh_names =
cliParseResult["match-mesh-names"].as<bool>();
}
cliArgs.convertOptions.match_mesh_names =
cliParseResult["match-mesh-names"].as<bool>();
}
if (cliParseResult.count("animation-bake-rate")) {
cliArgs.convertOptions.animationBakeRate =
cliParseResult["animation-bake-rate"]
.as<decltype(cliArgs.convertOptions.animationBakeRate)>();
}

fetch_convert_option
.template operator()<&bee::ConvertOptions::export_node_visibility>();

fetch_convert_option.template
operator()<&bee::ConvertOptions::animation_position_error_multiplier>();

Expand Down Expand Up @@ -363,6 +390,9 @@ std::optional<CliArgs> readCliArgs(std::span<std::string_view> args_) {
}
}

fetch_convert_option.template
operator()<&bee::ConvertOptions::use_extension_khr_animation_pointer>();

if (cliParseResult.count("verbose")) {
cliArgs.convertOptions.verbose = cliParseResult["verbose"].as<bool>();
}
Expand Down
13 changes: 13 additions & 0 deletions Cli/Test/ReadCliArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ TEST_CASE("Read CLI arguments") {
bee::ConvertOptions::UnitConversion::geometryLevel);
CHECK_EQ(convertOptions->convertOptions.export_fbx_file_header_info, false);
CHECK_EQ(convertOptions->convertOptions.export_raw_materials, false);
CHECK_EQ(convertOptions->convertOptions.use_extension_khr_animation_pointer,
false);
CHECK_EQ(convertOptions->convertOptions.export_node_visibility, false);
}

{// Input file
Expand Down Expand Up @@ -204,6 +207,16 @@ CHECK_EQ(u8toexe(args->convertOptions.textureResolution.locations[0]), "/a"s);
"export-raw-materials");
}

{ // --extension-khr-animation-pointer
test_boolean_arg<&bee::ConvertOptions::use_extension_khr_animation_pointer>(
"extension-khr-animation-pointer");
}

{ // --export-node-visibility
test_boolean_arg<&bee::ConvertOptions::export_node_visibility>(
"export-node-visibility");
}

{ // Log file
const auto fbmDir = "666�� ��"s;
CHECK_EQ(u8toexe(read_cli_args_with_dummy_and("--fbm-dir=" + fbmDir)->fbmDir),
Expand Down
9 changes: 9 additions & 0 deletions Core/Source/bee/Convert/MyExtras.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#pragma once

#include <string_view>

namespace bee::detail::my_extras {
inline const static std::string_view extras_name = "FBX-glTF-conv";

inline const static std::string_view node_extra_key_visibility = "visibility";
} // namespace bee::detail::my_extras
118 changes: 114 additions & 4 deletions Core/Source/bee/Convert/SceneConverter.Animation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
#include <bee/Convert/AnimationUtility.h>
#include <bee/Convert/ConvertError.h>
#include <bee/Convert/DirectSpreader.h>
#include <bee/Convert/MyExtras.h>
#include <bee/Convert/SceneConverter.h>
#include <bee/Convert/fbxsdk/Spreader.h>
#include <bee/Convert/glTF-extensions/KHR_animation_pointer.h>
#include <fmt/format.h>

namespace bee {
Expand Down Expand Up @@ -198,6 +200,11 @@ void SceneConverter::_convertAnimationLayer(
_extractWeightsAnimation(glTF_animation_, fbx_anim_layer_, fbx_node_,
anim_range_);
}

if (_options.use_extension_khr_animation_pointer) {
_extractNonStandardAnimation(glTF_animation_, fbx_anim_layer_, fbx_node_,
anim_range_);
}
}

void SceneConverter::_extractWeightsAnimation(
Expand Down Expand Up @@ -284,8 +291,13 @@ void SceneConverter::_writeMorphAnimtion(fx::gltf::Animation &glTF_animation_,
auto samplerIndex = glTF_animation_.samplers.size();
glTF_animation_.samplers.emplace_back(std::move(sampler));
fx::gltf::Animation::Channel channel;
channel.target.node = glTF_node_index_;
channel.target.path = "weights";
if (_options.use_extension_khr_animation_pointer) {
channel.target = detail::glTF_extension::khr_animation_pointer::make_target(
_glTFBuilder, fmt::format("/nodes/{}/weights", glTF_node_index_));
} else {
channel.target.node = glTF_node_index_;
channel.target.path = "weights";
}
channel.sampler = static_cast<std::int32_t>(samplerIndex);
glTF_animation_.channels.push_back(channel);
}
Expand Down Expand Up @@ -473,8 +485,14 @@ void SceneConverter::_extractTrsAnimation(fx::gltf::Animation &glTF_animation_,
auto samplerIndex = glTF_animation_.samplers.size();
glTF_animation_.samplers.emplace_back(std::move(sampler));
fx::gltf::Animation::Channel channel;
channel.target.node = *glTFNodeIndex;
channel.target.path = path_;
if (_options.use_extension_khr_animation_pointer) {
channel.target =
detail::glTF_extension::khr_animation_pointer::make_target(
_glTFBuilder, fmt::format("/nodes/{}/{}", *glTFNodeIndex, path_));
} else {
channel.target.node = *glTFNodeIndex;
channel.target.path = path_;
}
channel.sampler = static_cast<std::int32_t>(samplerIndex);
glTF_animation_.channels.push_back(channel);
};
Expand Down Expand Up @@ -503,4 +521,96 @@ void SceneConverter::_extractTrsAnimation(fx::gltf::Animation &glTF_animation_,
addChannel(scales, "scale", valueAccessorIndex);
}
}

void SceneConverter::_extractNonStandardAnimation(
fx::gltf::Animation &glTF_animation_,
fbxsdk::FbxAnimLayer &fbx_anim_layer_,
fbxsdk::FbxNode &fbx_node_,
const AnimRange &anim_range_) {

const auto glTFNodeIndex = _getNodeMap(fbx_node_);
if (!glTFNodeIndex) {
_log(Logger::Level::verbose,
fmt::format("{} is not being exported, skip animation on that.",
fbx_node_.GetNameWithNameSpacePrefix()));
return;
}

const auto addFloatPropertyAnimation =
[&glTF_animation_, &fbx_anim_layer_, &anim_range_,
this](fbxsdk::FbxPropertyT<fbxsdk::FbxDouble> &fbx_property_,
std::string_view pointer_) -> bool {
if (!fbx_property_.IsAnimated(&fbx_anim_layer_)) {
return false;
}

Track<fbxsdk::FbxDouble> track;
const auto firstTimeDouble = anim_range_.first_frame_seconds();
const auto nFrames = anim_range_.frames_count();
for (std::remove_const_t<decltype(nFrames)> iFrame = 0; iFrame < nFrames;
++iFrame) {
const auto fbxTime = anim_range_.at(iFrame);
const auto value = fbx_property_.EvaluateValue(fbxTime);
track.add(fbxTime.GetSecondDouble() - firstTimeDouble, value);
}

track.reduceLinearKeys(1e-5);

{
const auto timeAccessorIndex = _glTFBuilder.createAccessor<
fx::gltf::Accessor::Type::Scalar,
fx::gltf::Accessor::ComponentType::Float,
DirectSpreader<typename decltype(track.times)::value_type>>(
track.times, 0, 0, true);
_glTFBuilder.get(&fx::gltf::Document::accessors)[timeAccessorIndex].name =
fmt::format("{}/Input", pointer_);

const auto valueAccessorIndex =
_glTFBuilder.createAccessor<fx::gltf::Accessor::Type::Scalar,
fx::gltf::Accessor::ComponentType::Float,
DirectSpreader<fbxsdk::FbxDouble>>(
track.values, 0, 0);
_glTFBuilder.get(&fx::gltf::Document::accessors)[valueAccessorIndex]
.name = fmt::format("{}/Output", pointer_);

fx::gltf::Animation::Sampler sampler;
sampler.input = timeAccessorIndex;
sampler.output = valueAccessorIndex;
auto samplerIndex = glTF_animation_.samplers.size();
glTF_animation_.samplers.emplace_back(std::move(sampler));

fx::gltf::Animation::Channel channel;
channel.target =
detail::glTF_extension::khr_animation_pointer::make_target(
_glTFBuilder, pointer_);
channel.sampler = static_cast<std::int32_t>(samplerIndex);
glTF_animation_.channels.push_back(channel);
}

return true;
};

if (_options.export_node_visibility) {
const auto exported = addFloatPropertyAnimation(
fbx_node_.Visibility,
fmt::format("/nodes/{}/extras/{}/{}", *glTFNodeIndex,
detail::my_extras::extras_name,
detail::my_extras::node_extra_key_visibility));
if (exported) {
// According to
// https://github.com/KhronosGroup/glTF/blob/95e28d582382d9564d4ccc5706ae4640d2a14377/extensions/2.0/Khronos/KHR_animation_pointer/README.md
// > It is valid, to set the pointer to a property, which is not stored in
// > the glTF. This is the case, when the default value is omitted.
// > However, the parent object and/or extension must exist.
auto &nodes = _glTFBuilder.get(&fx::gltf::Document::nodes);
assert(*glTFNodeIndex > 0 && *glTFNodeIndex < nodes.size());
auto &node = nodes[*glTFNodeIndex];
auto &extra =
node.extensionsAndExtras["extras"][detail::my_extras::extras_name];
if (extra.is_null()) {
extra = Json::object();
}
}
}
}
} // namespace bee
5 changes: 5 additions & 0 deletions Core/Source/bee/Convert/SceneConverter.h
Original file line number Diff line number Diff line change
Expand Up @@ -496,5 +496,10 @@ class SceneConverter {
fbxsdk::FbxAnimLayer &fbx_anim_layer_,
fbxsdk::FbxNode &fbx_node_,
const AnimRange &anim_range_);

void _extractNonStandardAnimation(fx::gltf::Animation &glTF_animation_,
fbxsdk::FbxAnimLayer &fbx_anim_layer_,
fbxsdk::FbxNode &fbx_node_,
const AnimRange &anim_range_);
};
} // namespace bee
23 changes: 23 additions & 0 deletions Core/Source/bee/Convert/glTF-extensions/KHR_animation_pointer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma once

#include <bee/GLTFBuilder.h>
#include <string_view>

namespace bee::detail::glTF_extension::khr_animation_pointer {
inline const static std::string_view extension_name = "KHR_animation_pointer";

inline fx::gltf::Animation::Channel::Target
make_target(GLTFBuilder &builder_, std::string_view pointer_) {
fx::gltf::Animation::Channel::Target target;

target.path = "pointer";
target.extensionsAndExtras["extensions"]
[detail::glTF_extension::khr_animation_pointer::
extension_name]["pointer"] = pointer_;

builder_.requireExtension(
detail::glTF_extension::khr_animation_pointer::extension_name);

return target;
}
} // namespace bee::detail::glTF_extension::khr_animation_pointer
4 changes: 4 additions & 0 deletions Core/Source/bee/Converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@ struct ConvertOptions {

bool export_blend_shape_animation = true;

bool use_extension_khr_animation_pointer = false;

bool export_node_visibility = false;

Logger *logger = nullptr;

bool verbose = false;
Expand Down

0 comments on commit 889feb8

Please sign in to comment.