Updated ozz-animation to version 0.14.1 @35b2efd4

This commit is contained in:
Martin Felis
2023-03-26 13:28:12 +02:00
parent bf3189ff49
commit 15871f349c
194 changed files with 3495 additions and 1957 deletions
+13 -7
View File
@@ -5,16 +5,19 @@ add_custom_command(
"${CMAKE_CURRENT_LIST_DIR}/README.md"
"${ozz_media_directory}/bin/pab_skeleton.ozz"
"${ozz_media_directory}/bin/pab_walk.ozz"
"${ozz_media_directory}/bin/pab_crackhead_additive.ozz"
"${ozz_media_directory}/bin/pab_curl_additive.ozz"
"${ozz_media_directory}/bin/pab_splay_additive.ozz"
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/README.md"
"${CMAKE_CURRENT_BINARY_DIR}/media/skeleton.ozz"
"${CMAKE_CURRENT_BINARY_DIR}/media/animation_base.ozz"
"${CMAKE_CURRENT_BINARY_DIR}/media/animation_additive.ozz"
"${CMAKE_CURRENT_BINARY_DIR}/media/animation_curl_additive.ozz"
"${CMAKE_CURRENT_BINARY_DIR}/media/animation_splay_additive.ozz"
COMMAND ${CMAKE_COMMAND} -E make_directory media
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_LIST_DIR}/README.md" .
COMMAND ${CMAKE_COMMAND} -E copy "${ozz_media_directory}/bin/pab_skeleton.ozz" "./media/skeleton.ozz"
COMMAND ${CMAKE_COMMAND} -E copy "${ozz_media_directory}/bin/pab_walk.ozz" "./media/animation_base.ozz"
COMMAND ${CMAKE_COMMAND} -E copy "${ozz_media_directory}/bin/pab_crackhead_additive.ozz" "./media/animation_additive.ozz"
COMMAND ${CMAKE_COMMAND} -E copy "${ozz_media_directory}/bin/pab_curl_additive.ozz" "./media/animation_curl_additive.ozz"
COMMAND ${CMAKE_COMMAND} -E copy "${ozz_media_directory}/bin/pab_splay_additive.ozz" "./media/animation_splay_additive.ozz"
VERBATIM)
add_executable(sample_additive
@@ -22,10 +25,11 @@ add_executable(sample_additive
"${CMAKE_CURRENT_BINARY_DIR}/README.md"
"${CMAKE_CURRENT_BINARY_DIR}/media/skeleton.ozz"
"${CMAKE_CURRENT_BINARY_DIR}/media/animation_base.ozz"
"${CMAKE_CURRENT_BINARY_DIR}/media/animation_additive.ozz")
"${CMAKE_CURRENT_BINARY_DIR}/media/animation_curl_additive.ozz"
"${CMAKE_CURRENT_BINARY_DIR}/media/animation_splay_additive.ozz")
target_link_libraries(sample_additive
sample_framework)
target_copy_shared_libraries(sample_additive)
set_target_properties(sample_additive
PROPERTIES FOLDER "samples")
@@ -46,10 +50,12 @@ else()
endif(EMSCRIPTEN)
add_test(NAME sample_additive COMMAND sample_additive "--max_idle_loops=${ozz_sample_testing_loops}" $<$<BOOL:${ozz_run_tests_headless}>:--norender>)
add_test(NAME sample_additive_path COMMAND sample_additive "--skeleton=media/skeleton.ozz" "--animation=media/animation_base.ozz" "--additive_animation=media/animation_additive.ozz" "--max_idle_loops=${ozz_sample_testing_loops}" $<$<BOOL:${ozz_run_tests_headless}>:--norender>)
add_test(NAME sample_additive_path COMMAND sample_additive "--skeleton=media/skeleton.ozz" "--animation=media/animation_base.ozz" "--splay_animation=media/animation_splay_additive.ozz" "--curl_animation=media/animation_curl_additive.ozz" "--max_idle_loops=${ozz_sample_testing_loops}" $<$<BOOL:${ozz_run_tests_headless}>:--norender>)
add_test(NAME sample_additive_invalid_skeleton_path COMMAND sample_additive "--skeleton=media/bad_skeleton.ozz" $<$<BOOL:${ozz_run_tests_headless}>:--norender>)
set_tests_properties(sample_additive_invalid_skeleton_path PROPERTIES WILL_FAIL true)
add_test(NAME sample_additive_invalid_animation_path1 COMMAND sample_additive "--animation=media/bad_animation.ozz" $<$<BOOL:${ozz_run_tests_headless}>:--norender>)
set_tests_properties(sample_additive_invalid_animation_path1 PROPERTIES WILL_FAIL true)
add_test(NAME sample_additive_invalid_animation_path2 COMMAND sample_additive "--additive_animation=media/bad_animation.ozz" $<$<BOOL:${ozz_run_tests_headless}>:--norender>)
add_test(NAME sample_additive_invalid_animation_path2 COMMAND sample_additive "--curl_animation=media/bad_animation.ozz" $<$<BOOL:${ozz_run_tests_headless}>:--norender>)
set_tests_properties(sample_additive_invalid_animation_path2 PROPERTIES WILL_FAIL true)
add_test(NAME sample_additive_invalid_animation_path3 COMMAND sample_additive "--splay_animation=media/bad_animation.ozz" $<$<BOOL:${ozz_run_tests_headless}>:--norender>)
set_tests_properties(sample_additive_invalid_animation_path3 PROPERTIES WILL_FAIL true)
+10 -17
View File
@@ -2,32 +2,25 @@
## Description
Additive blending is a key concept in run-time animation. By superimposing a movement on top of a playing animation, it allows to add variety while lessening animation count and complexity.
In this sample, a "cracking head" animation is added to a "walk" cycle. Note that no synchronization is required between the two animations.
Additive blending is a key concept in run-time animation. Superimposing a movement on top of a playing animation allows to add variety while lessening animation count and management complexity. In this sample, 2 *splay* and *curl* hand animations are a added to a *walk* cycle. A weight is associated to each additive layers independently, allowing to control fingers.
## Concept
Additive blending is different to normal blending because it does not interpolate from a pose to an other. Instead it combines poses, meaning you can see the two animations at the same time. In this example the walk cycle is never altered. The cracking head and shoulder movements from the additive animation are simply (visually speaking) added.
Additive blending is different from normal blending because it does not interpolate from a pose to an other. Instead it combines poses, meaning the two animations can be seen at the same time. In this example the walk cycle is never altered. Fingers curl and splay poses coming from the additive animation are simply added (visually speaking) to the base animation. For the purpose of this sample, only the first frames of the 2 additive animations are used, aka a fixed pose for each (curl and splay poses).
Additive blending is performed by ozz::animation::BlendingJob. ozz::animation::BlendingJob exposes additive layers with the same input as normal blending layers: per joint local transforms, a global layer weight and optional per-joint weights. The main differences are that additive blending is done at the end of the normal blending pass, with a different equation.
Additive blending is performed by ozz::animation::BlendingJob. BlendingJob exposes additive layers with the same input as normal blending layers: per joint local transforms, a global layer weight and optional per-joint weights. The main differences are that additive blending is done at the end of the normal blending pass, with a different equation.
The additive (or delta) animation is created by subtracting a reference pose from a source animation. ozz proposes a ozz::animation::offline::AdditiveAnimationBuilder utility to build additive animations. It uses the first frame of the animation as the reference pose. fbx2ozz supports additive configuration option to export delta animations from a source file.
Additionally, the sample uses an optional mask to blend the additive animation on the character upper body only. See partial blending sample for more details.
The additive (or delta) animation is created by subtracting a reference pose from a source animation. ozz proposes a ozz::animation::offline::AdditiveAnimationBuilder utility to build additive animations. It uses the first frame of the animation as the reference pose. fbx2ozz and gltf2ozz support additive configuration option to export delta animations from a source file.
## Sample usage
The sample proposes to modify main and additive layers weights:
- Normal blending layer weight.
- Additive blending layer weight.
- Upper body masking activation and base joint selection.
Both main and additive animation playback parameters are exposed.
The sample proposes to tune base and additive (curl & splay) layers weights. Additive layers weight are exposed via 2d slider to make it easier to control together.
## Implementation
1. Loads main, additive animations, and their skeleton. See "playback" sample for more details.
2. Samples each animation to get local-space transformations. Sampling an additive animation is not different from a standard one.
3. Setups ozz::animation::BlendingJob object. BlendingJob object takes as input two arrays of BlendingJob::Layer, one for standard blending, the other for additive blending. Each layer is setup with its weights and the local-space transforms outputted from the sampling stage.
1. Loads base, additive (curl and splay) animations, and their skeleton. See "playback" sample for more details.
2. At initialization time, the first frame of curl and splay animations are sampled in order to extract their respective local-space transformations. Sampling an additive animation is not different from a standard one. The 2 animations are deleted as only the extracted pose will be used further.
3. At runtime, base animation is sampled to get skeleton base local-space transformations.
3. Setups ozz::animation::BlendingJob object. BlendingJob takes as input two arrays of BlendingJob::Layer, one for standard blending (base animation sampling output), the other for additive blending (the 2 curl and splay poses). Each layer is setup with its weights and the local-space transforms.
4. Convert local-space transformations to model-space matrices using ozz::animation::LocalToModelJob. It takes as input the skeleton (to know about joint's hierarchy) and local-space transforms outputted from the blending pass. Output is model-space matrices array.
5. Model-space matrices array is then used for rendering skeleton posture.
5. Model-space matrices array is then used for rendering the animated skeleton pose (or the skinned mesh in a real world example).
+167 -200
View File
@@ -39,6 +39,7 @@
#include "ozz/animation/runtime/skeleton_utils.h"
#include "ozz/base/containers/vector.h"
#include "ozz/base/log.h"
#include "ozz/base/maths/box.h"
#include "ozz/base/maths/simd_math.h"
#include "ozz/base/maths/soa_transform.h"
#include "ozz/base/maths/vec_float.h"
@@ -56,68 +57,65 @@ OZZ_OPTIONS_DECLARE_STRING(animation,
// Additive animation archive can be specified as an option.
OZZ_OPTIONS_DECLARE_STRING(
additive_animation, "Path to the additive animation (ozz archive format).",
"media/animation_additive.ozz", false)
splay_animation,
"Path to the additive splay animation (ozz archive format).",
"media/animation_splay_additive.ozz", false)
OZZ_OPTIONS_DECLARE_STRING(
curl_animation, "Path to the additive curl animation (ozz archive format).",
"media/animation_curl_additive.ozz", false)
class AdditiveBlendSampleApplication : public ozz::sample::Application {
public:
AdditiveBlendSampleApplication()
: upper_body_root_(0),
upper_body_mask_enable_(true),
upper_body_joint_weight_setting_(1.f),
threshold_(ozz::animation::BlendingJob().threshold) {}
: base_weight_(0.f),
additive_weigths_{.3f, .9f},
auto_animate_weights_(true) {}
protected:
// Updates current animation time and skeleton pose.
virtual bool OnUpdate(float _dt, float) {
// Updates and samples both animations to their respective local space
// transform buffers.
for (int i = 0; i < kNumLayers; ++i) {
Sampler& sampler = samplers_[i];
// Updates animations time.
sampler.controller.Update(sampler.animation, _dt);
// Setup sampling job.
ozz::animation::SamplingJob sampling_job;
sampling_job.animation = &sampler.animation;
sampling_job.cache = &sampler.cache;
sampling_job.ratio = sampler.controller.time_ratio();
sampling_job.output = make_span(sampler.locals);
// Samples animation.
if (!sampling_job.Run()) {
return false;
}
// For the sample purpose, animates additive weights automatically so the
// hand moves.
if (auto_animate_weights_) {
AnimateWeights(_dt);
}
// Blends animations.
// Blends the local spaces transforms computed by sampling all animations
// (1st stage just above), and outputs the result to the local space
// transform buffer blended_locals_
// Updates base animation time for main animation.
controller_.Update(base_animation_, _dt);
// Prepares standard blending layers.
// Setup sampling job.
ozz::animation::SamplingJob sampling_job;
sampling_job.animation = &base_animation_;
sampling_job.context = &context_;
sampling_job.ratio = controller_.time_ratio();
sampling_job.output = make_span(locals_);
// Samples animation.
if (!sampling_job.Run()) {
return false;
}
// Setups blending job layers.
// Main animation is used as-is.
ozz::animation::BlendingJob::Layer layers[1];
layers[0].transform = make_span(samplers_[kMainAnimation].locals);
layers[0].weight = samplers_[kMainAnimation].weight_setting;
layers[0].transform = make_span(locals_);
layers[0].weight = base_weight_;
layers[0].joint_weights = make_span(base_joint_weights_);
// Prepares additive blending layers.
ozz::animation::BlendingJob::Layer additive_layers[1];
additive_layers[0].transform =
make_span(samplers_[kAdditiveAnimation].locals);
additive_layers[0].weight = samplers_[kAdditiveAnimation].weight_setting;
// Set per-joint weights for the additive blended layer.
if (upper_body_mask_enable_) {
additive_layers[0].joint_weights = make_span(upper_body_joint_weights_);
// The two additive layers (curl and splay) are blended on top of the main
// layer.
ozz::animation::BlendingJob::Layer additive_layers[kNumLayers];
for (size_t i = 0; i < kNumLayers; ++i) {
additive_layers[i].transform = make_span(additive_locals_[i]);
additive_layers[i].weight = additive_weigths_[i];
}
// Setups blending job.
ozz::animation::BlendingJob blend_job;
blend_job.threshold = threshold_;
blend_job.layers = layers;
blend_job.additive_layers = additive_layers;
blend_job.bind_pose = skeleton_.joint_bind_poses();
blend_job.rest_pose = skeleton_.joint_rest_poses();
blend_job.output = make_span(blended_locals_);
// Blends.
@@ -141,12 +139,34 @@ class AdditiveBlendSampleApplication : public ozz::sample::Application {
return true;
}
void AnimateWeights(float _dt) {
static float t = 0.f;
t += _dt;
additive_weigths_[0] = .5f + std::cos(t * 1.7f) * .5f;
additive_weigths_[1] = .5f + std::cos(t * 2.5f) * .5f;
}
// Samples animation, transforms to model space and renders.
virtual bool OnDisplay(ozz::sample::Renderer* _renderer) {
return _renderer->DrawPosture(skeleton_, make_span(models_),
ozz::math::Float4x4::identity());
}
bool SetJointWeights(const char* _name, float _weight) {
const auto set_joint = [this, _weight](int _joint, int) {
ozz::math::SimdFloat4& soa_weight = base_joint_weights_[_joint / 4];
soa_weight = ozz::math::SetI(
soa_weight, ozz::math::simd_float4::Load1(_weight), _joint % 4);
};
const int joint = FindJoint(skeleton_, _name);
if (joint >= 0) {
ozz::animation::IterateJointsDF(skeleton_, set_joint, joint);
return true;
}
return false;
}
virtual bool OnInitialize() {
// Reading skeleton.
if (!ozz::sample::LoadSkeleton(OPTIONS_skeleton, &skeleton_)) {
@@ -155,81 +175,70 @@ class AdditiveBlendSampleApplication : public ozz::sample::Application {
const int num_soa_joints = skeleton_.num_soa_joints();
const int num_joints = skeleton_.num_joints();
// Reading animations.
const char* filenames[] = {OPTIONS_animation, OPTIONS_additive_animation};
for (int i = 0; i < kNumLayers; ++i) {
Sampler& sampler = samplers_[i];
if (!ozz::sample::LoadAnimation(filenames[i], &sampler.animation)) {
return false;
}
// Allocates sampler runtime buffers.
sampler.locals.resize(num_soa_joints);
// Allocates a cache that matches animation requirements.
sampler.cache.Resize(num_joints);
// Reads base animation.
if (!ozz::sample::LoadAnimation(OPTIONS_animation, &base_animation_)) {
return false;
}
// Default weight settings.
samplers_[kMainAnimation].weight_setting = 1.f;
if (num_joints != base_animation_.num_tracks()) {
return false;
}
upper_body_joint_weight_setting_ = 1.f;
samplers_[kAdditiveAnimation].weight_setting = 1.f;
// Allocates sampling context.
context_.Resize(num_joints);
// Allocates local space runtime buffers of blended data.
blended_locals_.resize(num_soa_joints);
// Allocates local space runtime buffers for base animation.
locals_.resize(num_soa_joints);
// Allocates model space runtime buffers of blended data.
models_.resize(num_joints);
// Allocates per-joint weights used for the partial additive animation.
// Note that this is a Soa structure.
upper_body_joint_weights_.resize(num_soa_joints);
// Storage for blending stage output.
blended_locals_.resize(num_soa_joints);
// Finds the "Spine1" joint in the joint hierarchy.
for (int i = 0; i < num_joints; ++i) {
if (std::strstr(skeleton_.joint_names()[i], "Spine1")) {
upper_body_root_ = i;
break;
// Allocates and sets base animation mask weights to one.
base_joint_weights_.resize(num_soa_joints, ozz::math::simd_float4::one());
SetJointWeights("Lefthand", 0.f);
SetJointWeights("RightHand", 0.f);
// Reads and extract additive animations pose.
const char* filenames[] = {OPTIONS_splay_animation, OPTIONS_curl_animation};
for (int i = 0; i < kNumLayers; ++i) {
// Reads animation on the stack as it won't need to be maintained in
// memory. Only the pose is needed.
ozz::animation::Animation animation;
if (!ozz::sample::LoadAnimation(filenames[i], &animation)) {
return false;
}
if (num_joints != animation.num_tracks()) {
return false;
}
// Allocates additive poses, aka buffers of Soa tranforms.
additive_locals_[i].resize(num_soa_joints);
// Samples the first frame pose.
ozz::animation::SamplingJob sampling_job;
sampling_job.animation = &animation;
sampling_job.context = &context_;
sampling_job.ratio = 0.f; // Only needs the first frame pose
sampling_job.output = make_span(additive_locals_[i]);
// Samples animation.
if (!sampling_job.Run()) {
return false;
}
// Invalidates context which will be re-used for another animation.
// This is usually not needed, animation address on the stack is the same
// each loop, hence creating an issue as animation content is changing.
context_.Invalidate();
}
SetupPerJointWeights();
return true;
}
// Helper functor used to set weights while traversing joints hierarchy.
struct WeightSetupIterator {
WeightSetupIterator(ozz::vector<ozz::math::SimdFloat4>* _weights,
float _weight_setting)
: weights(_weights), weight_setting(_weight_setting) {}
void operator()(int _joint, int) {
ozz::math::SimdFloat4& soa_weight = weights->at(_joint / 4);
soa_weight = ozz::math::SetI(
soa_weight, ozz::math::simd_float4::Load1(weight_setting),
_joint % 4);
}
ozz::vector<ozz::math::SimdFloat4>* weights;
float weight_setting;
};
void SetupPerJointWeights() {
// Setup partial animation mask. This mask is defined by a weight_setting
// assigned to each joint of the hierarchy. Joint to disable are set to a
// weight_setting of 0.f, and enabled joints are set to 1.f.
// Disables all joints: set all weights to 0.
for (int i = 0; i < skeleton_.num_soa_joints(); ++i) {
upper_body_joint_weights_[i] = ozz::math::simd_float4::zero();
}
// Extracts the list of children of the shoulder
WeightSetupIterator it(&upper_body_joint_weights_,
upper_body_joint_weight_setting_);
IterateJointsDF(skeleton_, it, upper_body_root_);
}
virtual void OnDestroy() {}
virtual bool OnGui(ozz::sample::ImGui* _im_gui) {
@@ -241,69 +250,31 @@ class AdditiveBlendSampleApplication : public ozz::sample::Application {
ozz::sample::ImGui::OpenClose oc(_im_gui, "Blending parameters", &open);
if (open) {
_im_gui->DoLabel("Main layer:");
std::sprintf(label, "Layer weight: %.2f",
samplers_[kMainAnimation].weight_setting);
_im_gui->DoSlider(label, 0.f, 1.f,
&samplers_[kMainAnimation].weight_setting, 1.f);
std::sprintf(label, "Layer weight: %.2f", base_weight_);
_im_gui->DoSlider(label, 0.f, 1.f, &base_weight_, 1.f);
_im_gui->DoLabel("Additive layer:");
std::sprintf(label, "Layer weight: %.2f",
samplers_[kAdditiveAnimation].weight_setting);
_im_gui->DoSlider(label, -1.f, 1.f,
&samplers_[kAdditiveAnimation].weight_setting, 1.f);
_im_gui->DoLabel("Global settings:");
std::sprintf(label, "Threshold: %.2f", threshold_);
_im_gui->DoSlider(label, .01f, 1.f, &threshold_);
}
}
// Exposes selection of the root of the partial blending hierarchy.
{
static bool open = true;
ozz::sample::ImGui::OpenClose oc(_im_gui, "Upper body masking", &open);
_im_gui->DoCheckBox("Animates weights", &auto_animate_weights_);
ozz::array<float, OZZ_ARRAY_SIZE(additive_weigths_)> weights;
std::memcpy(weights.data(), additive_weigths_,
sizeof(additive_weigths_));
if (open) {
bool rebuild_joint_weights = false;
rebuild_joint_weights |=
_im_gui->DoCheckBox("Enable mask", &upper_body_mask_enable_);
std::sprintf(label, "Joints weight: %.2f",
upper_body_joint_weight_setting_);
rebuild_joint_weights |= _im_gui->DoSlider(
label, 0.f, 1.f, &upper_body_joint_weight_setting_, 1.f,
upper_body_mask_enable_);
if (skeleton_.num_joints() != 0) {
_im_gui->DoLabel("Root of the upper body hierarchy:",
ozz::sample::ImGui::kLeft, false);
std::sprintf(label, "%s (%d)",
skeleton_.joint_names()[upper_body_root_],
upper_body_root_);
rebuild_joint_weights |= _im_gui->DoSlider(
label, 0, skeleton_.num_joints() - 1, &upper_body_root_, 1.f,
upper_body_mask_enable_);
}
// Rebuilds per-joint weights if something has changed.
if (rebuild_joint_weights) {
SetupPerJointWeights();
std::sprintf(label, "Weights\nCurl: %.2f\nSplay: %.2f",
additive_weigths_[kCurl], additive_weigths_[kSplay]);
if (_im_gui->DoSlider2D(label, {{0.f, 0.f}}, {{1.f, 1.f}}, &weights)) {
auto_animate_weights_ = false; // User interacted.
std::memcpy(additive_weigths_, weights.data(),
sizeof(additive_weigths_));
}
}
}
// Exposes animations runtime playback controls.
// Exposes base animation runtime playback controls.
{
static bool oc_open = true;
ozz::sample::ImGui::OpenClose oc(_im_gui, "Animation control", &oc_open);
if (oc_open) {
static bool open[kNumLayers] = {true, true};
const char* oc_names[kNumLayers] = {"Main animation",
"Additive animation"};
for (int i = 0; i < kNumLayers; ++i) {
Sampler& sampler = samplers_[i];
ozz::sample::ImGui::OpenClose loc(_im_gui, oc_names[i], nullptr);
if (open[i]) {
sampler.controller.OnGui(sampler.animation, _im_gui);
}
}
controller_.OnGui(base_animation_, _im_gui);
}
}
@@ -311,62 +282,55 @@ class AdditiveBlendSampleApplication : public ozz::sample::Application {
}
virtual void GetSceneBounds(ozz::math::Box* _bound) const {
ozz::sample::ComputePostureBounds(make_span(models_), _bound);
// Finds the "hand" joint in the joint hierarchy.
const int hand = FindJoint(skeleton_, "Lefthand");
// Creates a bounding volume around the hand.
if (hand != -1) {
const ozz::math::Float4x4& hand_matrix = models_[hand];
ozz::math::Float3 hand_position;
ozz::math::Store3PtrU(hand_matrix.cols[3], &hand_position.x);
const ozz::math::Float3 extent(.15f);
_bound->min = hand_position - extent;
_bound->max = hand_position + extent;
} else {
ozz::sample::ComputePostureBounds(make_span(models_), _bound);
}
}
private:
// Runtime skeleton.
ozz::animation::Skeleton skeleton_;
// The number of layers to blend.
enum {
kMainAnimation = 0,
kAdditiveAnimation = 1,
kNumLayers = 2,
};
// The number of additive layers to blend.
enum { kSplay, kCurl, kNumLayers };
// Sampler structure contains all the data required to sample a single
// animation.
struct Sampler {
// Constructor, default initialization.
Sampler() : weight_setting(1.f) {}
// Runtime animation.
ozz::animation::Animation base_animation_;
// Playback animation controller. This is a utility class that helps with
// controlling animation playback time.
ozz::sample::PlaybackController controller;
// Per-joint weights used to define the base animation mask. Allows to remove
// hands from base animations.
ozz::vector<ozz::math::SimdFloat4> base_joint_weights_;
// Blending weight_setting for the layer.
float weight_setting;
// Main animation controller. This is a utility class that helps with
// controlling animation playback time.
ozz::sample::PlaybackController controller_;
// Runtime animation.
ozz::animation::Animation animation;
// Sampling context.
ozz::animation::SamplingJob::Context context_;
// Sampling cache.
ozz::animation::SamplingCache cache;
// Buffer of local transforms as sampled from main animation_.
ozz::vector<ozz::math::SoaTransform> locals_;
// Buffer of local transforms as sampled from animation_.
ozz::vector<ozz::math::SoaTransform> locals;
// Blending weight of the base animation layer.
float base_weight_;
} samplers_[kNumLayers]; // kNumLayers animations to blend.
// Poses of local transforms as sampled from curl and splay animations.
// They are sampled during initialization, as a single pose is used.
ozz::vector<ozz::math::SoaTransform> additive_locals_[kNumLayers];
// Index of the joint at the base of the upper body hierarchy.
int upper_body_root_;
// Enables upper boddy per-joint weights.
bool upper_body_mask_enable_;
// Blending weight_setting setting of the joints of this layer that are
// affected
// by the masking.
float upper_body_joint_weight_setting_;
// Per-joint weights used to define the partial animation mask. Allows to
// select which joints are considered during blending, and their individual
// weight_setting.
ozz::vector<ozz::math::SimdFloat4> upper_body_joint_weights_;
// Blending job bind pose threshold.
float threshold_;
// Blending weight of the additive animation layer.
float additive_weigths_[kNumLayers];
// Buffer of local transforms which stores the blending result.
ozz::vector<ozz::math::SoaTransform> blended_locals_;
@@ -374,7 +338,10 @@ class AdditiveBlendSampleApplication : public ozz::sample::Application {
// Buffer of model space matrices. These are computed by the local-to-model
// job after the blending stage.
ozz::vector<ozz::math::Float4x4> models_;
};
// Automatically animates additive weights.
bool auto_animate_weights_;
};
int main(int _argc, const char** _argv) {
const char* title = "Ozz-animation sample: Additive animations blending";