Initial commit
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
# sampling_job_tests
|
||||
add_executable(test_sampling_job
|
||||
sampling_job_tests.cc)
|
||||
target_link_libraries(test_sampling_job
|
||||
ozz_animation_offline
|
||||
gtest)
|
||||
set_target_properties(test_sampling_job PROPERTIES FOLDER "ozz/tests/animation")
|
||||
add_test(NAME test_sampling_job COMMAND test_sampling_job)
|
||||
|
||||
# blending_job_tests
|
||||
add_executable(test_blending_job
|
||||
blending_job_tests.cc)
|
||||
target_link_libraries(test_blending_job
|
||||
ozz_animation_offline
|
||||
gtest)
|
||||
set_target_properties(test_blending_job PROPERTIES FOLDER "ozz/tests/animation")
|
||||
add_test(NAME test_blending_job COMMAND test_blending_job)
|
||||
|
||||
# local_to_model_job_tests
|
||||
add_executable(test_local_to_model_job
|
||||
local_to_model_job_tests.cc)
|
||||
target_link_libraries(test_local_to_model_job
|
||||
ozz_animation_offline
|
||||
gtest)
|
||||
set_target_properties(test_local_to_model_job PROPERTIES FOLDER "ozz/tests/animation")
|
||||
add_test(NAME test_local_to_model_job COMMAND test_local_to_model_job)
|
||||
|
||||
add_executable(test_animation_archive
|
||||
animation_archive_tests.cc)
|
||||
target_link_libraries(test_animation_archive
|
||||
ozz_animation_offline
|
||||
gtest)
|
||||
set_target_properties(test_animation_archive PROPERTIES FOLDER "ozz/tests/animation")
|
||||
add_test(NAME test_animation_archive COMMAND test_animation_archive)
|
||||
|
||||
add_executable(test_animation_archive_versioning
|
||||
animation_archive_versioning_tests.cc)
|
||||
target_link_libraries(test_animation_archive_versioning
|
||||
ozz_animation
|
||||
ozz_options
|
||||
gtest)
|
||||
set_target_properties(test_animation_archive_versioning PROPERTIES FOLDER "ozz/tests/animation")
|
||||
add_test(NAME test_animation_archive_versioning_le COMMAND test_animation_archive_versioning "--file=${ozz_media_directory}/bin/versioning/animation_v6_le.ozz" "--tracks=67" "--duration=.66666667" "--name=run")
|
||||
add_test(NAME test_animation_archive_versioning_be COMMAND test_animation_archive_versioning "--file=${ozz_media_directory}/bin/versioning/animation_v6_be.ozz" "--tracks=67" "--duration=.66666667" "--name=run")
|
||||
|
||||
# Previous versions.
|
||||
add_test(NAME test_animation_archive_versioning_le_older5 COMMAND test_animation_archive_versioning "--file=${ozz_media_directory}/bin/versioning/animation_v5_le.ozz" "--tracks=67" "--duration=.66666667" "--name=")
|
||||
set_tests_properties(test_animation_archive_versioning_le_older5 PROPERTIES WILL_FAIL true)
|
||||
add_test(NAME test_animation_archive_versioning_le_older4 COMMAND test_animation_archive_versioning "--file=${ozz_media_directory}/bin/versioning/animation_v4_le.ozz" "--tracks=67" "--duration=.66666667" "--name=")
|
||||
set_tests_properties(test_animation_archive_versioning_le_older4 PROPERTIES WILL_FAIL true)
|
||||
add_test(NAME test_animation_archive_versioning_le_older3 COMMAND test_animation_archive_versioning "--file=${ozz_media_directory}/bin/versioning/animation_v3_le.ozz" "--tracks=67" "--duration=.66666667" "--name=")
|
||||
set_tests_properties(test_animation_archive_versioning_le_older3 PROPERTIES WILL_FAIL true)
|
||||
add_test(NAME test_animation_archive_versioning_le_older2 COMMAND test_animation_archive_versioning "--file=${ozz_media_directory}/bin/versioning/animation_v2_le.ozz" "--tracks=67" "--duration=1.33333302" "--name=")
|
||||
set_tests_properties(test_animation_archive_versioning_le_older2 PROPERTIES WILL_FAIL true)
|
||||
add_test(NAME test_animation_archive_versioning_le_older1 COMMAND test_animation_archive_versioning "--file=${ozz_media_directory}/bin/versioning/animation_v1_le.ozz" "--tracks=67" "--duration=.66666698" "--name=")
|
||||
set_tests_properties(test_animation_archive_versioning_le_older1 PROPERTIES WILL_FAIL true)
|
||||
|
||||
add_executable(test_skeleton_archive
|
||||
skeleton_archive_tests.cc)
|
||||
target_link_libraries(test_skeleton_archive
|
||||
ozz_animation_offline
|
||||
gtest)
|
||||
set_target_properties(test_skeleton_archive PROPERTIES FOLDER "ozz/tests/animation")
|
||||
add_test(NAME test_skeleton_archive COMMAND test_skeleton_archive)
|
||||
|
||||
add_executable(test_skeleton_archive_versioning
|
||||
skeleton_archive_versioning_tests.cc)
|
||||
target_link_libraries(test_skeleton_archive_versioning
|
||||
ozz_animation
|
||||
ozz_options
|
||||
gtest)
|
||||
set_target_properties(test_skeleton_archive_versioning PROPERTIES FOLDER "ozz/tests/animation")
|
||||
|
||||
# Previous skeleton versions.
|
||||
add_test(NAME test_skeleton_archive_versioning_le_older1 COMMAND test_skeleton_archive_versioning "--file=${ozz_media_directory}/bin/versioning/skeleton_v1_le.ozz" "--joints=67" "--root_name=Hips")
|
||||
set_tests_properties(test_skeleton_archive_versioning_le_older1 PROPERTIES WILL_FAIL true)
|
||||
|
||||
# Current skeleton version.
|
||||
add_test(NAME test_skeleton_archive_versioning_le COMMAND test_skeleton_archive_versioning "--file=${ozz_media_directory}/bin/versioning/skeleton_v2_le.ozz" "--joints=67" "--root_name=Hips")
|
||||
add_test(NAME test_skeleton_archive_versioning_be COMMAND test_skeleton_archive_versioning "--file=${ozz_media_directory}/bin/versioning/skeleton_v2_be.ozz" "--joints=67" "--root_name=Hips")
|
||||
|
||||
add_executable(test_skeleton_utils
|
||||
skeleton_utils_tests.cc)
|
||||
target_link_libraries(test_skeleton_utils
|
||||
ozz_animation_offline
|
||||
gtest)
|
||||
set_target_properties(test_skeleton_utils PROPERTIES FOLDER "ozz/tests/animation")
|
||||
add_test(NAME test_skeleton_utils COMMAND test_skeleton_utils)
|
||||
|
||||
add_executable(test_animation_utils
|
||||
animation_utils_tests.cc)
|
||||
target_link_libraries(test_animation_utils
|
||||
ozz_animation_offline
|
||||
gtest)
|
||||
set_target_properties(test_animation_utils PROPERTIES FOLDER "ozz/tests/animation")
|
||||
add_test(NAME test_animation_utils COMMAND test_skeleton_utils)
|
||||
|
||||
# track_sampling_job_tests
|
||||
add_executable(test_track_sampling_job
|
||||
track_sampling_job_tests.cc)
|
||||
target_link_libraries(test_track_sampling_job
|
||||
ozz_animation_offline
|
||||
ozz_animation
|
||||
ozz_base
|
||||
gtest)
|
||||
set_target_properties(test_track_sampling_job PROPERTIES FOLDER "ozz/tests/animation")
|
||||
add_test(NAME test_track_sampling_job COMMAND test_track_sampling_job)
|
||||
|
||||
# test_track_triggering_job
|
||||
add_executable(test_track_triggering_job
|
||||
track_triggering_job_tests.cc
|
||||
track_triggering_job_trait_tests.cc)
|
||||
target_link_libraries(test_track_triggering_job
|
||||
ozz_animation_offline
|
||||
ozz_animation
|
||||
ozz_base
|
||||
gtest)
|
||||
set_target_properties(test_track_triggering_job PROPERTIES FOLDER "ozz/tests/animation")
|
||||
add_test(NAME test_track_triggering_job COMMAND test_track_triggering_job)
|
||||
|
||||
add_executable(test_track_archive
|
||||
track_archive_tests.cc)
|
||||
target_link_libraries(test_track_archive
|
||||
ozz_animation_offline
|
||||
gtest)
|
||||
set_target_properties(test_track_archive PROPERTIES FOLDER "ozz/tests/animation")
|
||||
add_test(NAME test_track_archive COMMAND test_track_archive)
|
||||
|
||||
add_executable(test_ik_aim_job
|
||||
ik_aim_job_tests.cc)
|
||||
target_link_libraries(test_ik_aim_job
|
||||
ozz_animation
|
||||
gtest)
|
||||
set_target_properties(test_ik_aim_job PROPERTIES FOLDER "ozz/tests/animation")
|
||||
add_test(NAME test_ik_aim_job COMMAND test_ik_aim_job)
|
||||
|
||||
add_executable(test_ik_two_bone_job
|
||||
ik_two_bone_job_tests.cc)
|
||||
target_link_libraries(test_ik_two_bone_job
|
||||
ozz_animation
|
||||
gtest)
|
||||
set_target_properties(test_ik_two_bone_job PROPERTIES FOLDER "ozz/tests/animation")
|
||||
add_test(NAME test_ik_two_bone_job COMMAND test_ik_two_bone_job)
|
||||
|
||||
# ozz_animation fuse tests
|
||||
set_source_files_properties(${PROJECT_BINARY_DIR}/src_fused/ozz_animation.cc PROPERTIES GENERATED 1)
|
||||
add_executable(test_fuse_animation
|
||||
sampling_job_tests.cc
|
||||
${PROJECT_BINARY_DIR}/src_fused/ozz_animation.cc)
|
||||
add_dependencies(test_fuse_animation BUILD_FUSE_ozz_animation)
|
||||
target_link_libraries(test_fuse_animation
|
||||
ozz_animation_offline
|
||||
gtest)
|
||||
add_test(NAME test_fuse_animation COMMAND test_fuse_animation)
|
||||
set_target_properties(test_fuse_animation PROPERTIES FOLDER "ozz/tests/animation")
|
||||
@@ -0,0 +1,185 @@
|
||||
//----------------------------------------------------------------------------//
|
||||
// //
|
||||
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
|
||||
// and distributed under the MIT License (MIT). //
|
||||
// //
|
||||
// Copyright (c) Guillaume Blanc //
|
||||
// //
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a //
|
||||
// copy of this software and associated documentation files (the "Software"), //
|
||||
// to deal in the Software without restriction, including without limitation //
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
|
||||
// and/or sell copies of the Software, and to permit persons to whom the //
|
||||
// Software is furnished to do so, subject to the following conditions: //
|
||||
// //
|
||||
// The above copyright notice and this permission notice shall be included in //
|
||||
// all copies or substantial portions of the Software. //
|
||||
// //
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
|
||||
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
|
||||
// DEALINGS IN THE SOFTWARE. //
|
||||
// //
|
||||
//----------------------------------------------------------------------------//
|
||||
|
||||
#include "ozz/animation/runtime/animation.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "ozz/base/maths/gtest_math_helper.h"
|
||||
|
||||
#include "ozz/base/io/archive.h"
|
||||
#include "ozz/base/io/stream.h"
|
||||
#include "ozz/base/memory/unique_ptr.h"
|
||||
|
||||
#include "ozz/base/maths/soa_transform.h"
|
||||
|
||||
#include "ozz/animation/runtime/sampling_job.h"
|
||||
|
||||
#include "ozz/animation/offline/animation_builder.h"
|
||||
#include "ozz/animation/offline/raw_animation.h"
|
||||
|
||||
using ozz::animation::Animation;
|
||||
using ozz::animation::offline::RawAnimation;
|
||||
using ozz::animation::offline::AnimationBuilder;
|
||||
|
||||
TEST(Empty, AnimationSerialize) {
|
||||
ozz::io::MemoryStream stream;
|
||||
|
||||
// Streams out.
|
||||
ozz::io::OArchive o(&stream, ozz::GetNativeEndianness());
|
||||
|
||||
Animation o_animation;
|
||||
o << o_animation;
|
||||
|
||||
// Streams in.
|
||||
stream.Seek(0, ozz::io::Stream::kSet);
|
||||
ozz::io::IArchive i(&stream);
|
||||
|
||||
Animation i_animation;
|
||||
i >> i_animation;
|
||||
|
||||
EXPECT_EQ(o_animation.num_tracks(), i_animation.num_tracks());
|
||||
}
|
||||
|
||||
TEST(Filled, AnimationSerialize) {
|
||||
// Builds a valid animation.
|
||||
ozz::unique_ptr<Animation> o_animation;
|
||||
{
|
||||
RawAnimation raw_animation;
|
||||
raw_animation.duration = 1.f;
|
||||
raw_animation.tracks.resize(1);
|
||||
|
||||
RawAnimation::TranslationKey t_key0 = {0.f,
|
||||
ozz::math::Float3(93.f, 58.f, 46.f)};
|
||||
raw_animation.tracks[0].translations.push_back(t_key0);
|
||||
RawAnimation::TranslationKey t_key1 = {.9f,
|
||||
ozz::math::Float3(46.f, 58.f, 93.f)};
|
||||
raw_animation.tracks[0].translations.push_back(t_key1);
|
||||
|
||||
RawAnimation::RotationKey r_key = {
|
||||
0.7f, ozz::math::Quaternion(0.f, 1.f, 0.f, 0.f)};
|
||||
raw_animation.tracks[0].rotations.push_back(r_key);
|
||||
|
||||
RawAnimation::ScaleKey s_key = {0.1f, ozz::math::Float3(99.f, 26.f, 14.f)};
|
||||
raw_animation.tracks[0].scales.push_back(s_key);
|
||||
|
||||
AnimationBuilder builder;
|
||||
o_animation = builder(raw_animation);
|
||||
ASSERT_TRUE(o_animation);
|
||||
}
|
||||
|
||||
for (int e = 0; e < 2; ++e) {
|
||||
ozz::Endianness endianess = e == 0 ? ozz::kBigEndian : ozz::kLittleEndian;
|
||||
ozz::io::MemoryStream stream;
|
||||
|
||||
// Streams out.
|
||||
ozz::io::OArchive o(&stream, endianess);
|
||||
o << *o_animation;
|
||||
|
||||
// Streams in.
|
||||
stream.Seek(0, ozz::io::Stream::kSet);
|
||||
ozz::io::IArchive i(&stream);
|
||||
|
||||
Animation i_animation;
|
||||
i >> i_animation;
|
||||
|
||||
ASSERT_FLOAT_EQ(o_animation->duration(), i_animation.duration());
|
||||
ASSERT_EQ(o_animation->num_tracks(), i_animation.num_tracks());
|
||||
EXPECT_EQ(o_animation->size(), i_animation.size());
|
||||
|
||||
// Needs to sample to test the animation.
|
||||
ozz::animation::SamplingJob job;
|
||||
ozz::animation::SamplingCache cache(1);
|
||||
ozz::math::SoaTransform output[1];
|
||||
job.animation = o_animation.get();
|
||||
job.cache = &cache;
|
||||
job.output = output;
|
||||
|
||||
// Samples and compares the two animations
|
||||
{ // Samples at t = 0
|
||||
job.ratio = 0.f;
|
||||
job.Run();
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].translation, 93.f, 0.f, 0.f, 0.f, 58.f,
|
||||
0.f, 0.f, 0.f, 46.f, 0.f, 0.f, 0.f);
|
||||
EXPECT_SOAQUATERNION_EQ_EST(output[0].rotation, 0.f, 0.f, 0.f, 0.f, 1.f,
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f,
|
||||
1.f, 1.f);
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].scale, 99.f, 1.f, 1.f, 1.f, 26.f, 1.f,
|
||||
1.f, 1.f, 14.f, 1.f, 1.f, 1.f);
|
||||
}
|
||||
{ // Samples at t = 1
|
||||
job.ratio = 1.f;
|
||||
job.Run();
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].translation, 46.f, 0.f, 0.f, 0.f, 58.f,
|
||||
0.f, 0.f, 0.f, 93.f, 0.f, 0.f, 0.f);
|
||||
EXPECT_SOAQUATERNION_EQ_EST(output[0].rotation, 0.f, 0.f, 0.f, 0.f, 1.f,
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f,
|
||||
1.f, 1.f);
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].scale, 99.f, 1.f, 1.f, 1.f, 26.f, 1.f,
|
||||
1.f, 1.f, 14.f, 1.f, 1.f, 1.f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(AlreadyInitialized, AnimationSerialize) {
|
||||
ozz::io::MemoryStream stream;
|
||||
|
||||
{
|
||||
ozz::io::OArchive o(&stream);
|
||||
|
||||
RawAnimation raw_animation;
|
||||
raw_animation.duration = 1.f;
|
||||
raw_animation.tracks.resize(1);
|
||||
|
||||
AnimationBuilder builder;
|
||||
ozz::unique_ptr<Animation> o_animation(builder(raw_animation));
|
||||
ASSERT_TRUE(o_animation);
|
||||
o << *o_animation;
|
||||
|
||||
raw_animation.duration = 2.f;
|
||||
raw_animation.tracks.resize(2);
|
||||
o_animation = builder(raw_animation);
|
||||
ASSERT_TRUE(o_animation);
|
||||
o << *o_animation;
|
||||
}
|
||||
|
||||
{
|
||||
// Streams in.
|
||||
stream.Seek(0, ozz::io::Stream::kSet);
|
||||
ozz::io::IArchive i(&stream);
|
||||
|
||||
// Reads and check the first animation.
|
||||
Animation i_animation;
|
||||
i >> i_animation;
|
||||
EXPECT_FLOAT_EQ(i_animation.duration(), 1.f);
|
||||
EXPECT_EQ(i_animation.num_tracks(), 1);
|
||||
|
||||
// Reuse the animation a second time.
|
||||
i >> i_animation;
|
||||
EXPECT_FLOAT_EQ(i_animation.duration(), 2.f);
|
||||
ASSERT_EQ(i_animation.num_tracks(), 2);
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
//----------------------------------------------------------------------------//
|
||||
// //
|
||||
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
|
||||
// and distributed under the MIT License (MIT). //
|
||||
// //
|
||||
// Copyright (c) Guillaume Blanc //
|
||||
// //
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a //
|
||||
// copy of this software and associated documentation files (the "Software"), //
|
||||
// to deal in the Software without restriction, including without limitation //
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
|
||||
// and/or sell copies of the Software, and to permit persons to whom the //
|
||||
// Software is furnished to do so, subject to the following conditions: //
|
||||
// //
|
||||
// The above copyright notice and this permission notice shall be included in //
|
||||
// all copies or substantial portions of the Software. //
|
||||
// //
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
|
||||
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
|
||||
// DEALINGS IN THE SOFTWARE. //
|
||||
// //
|
||||
//----------------------------------------------------------------------------//
|
||||
|
||||
#include "ozz/animation/runtime/animation.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "ozz/base/io/archive.h"
|
||||
#include "ozz/base/io/stream.h"
|
||||
|
||||
#include "ozz/base/log.h"
|
||||
#include "ozz/options/options.h"
|
||||
|
||||
OZZ_OPTIONS_DECLARE_STRING(file, "Specifies input file", "", true)
|
||||
OZZ_OPTIONS_DECLARE_INT(tracks, "Number of tracks", 0, true)
|
||||
OZZ_OPTIONS_DECLARE_FLOAT(duration, "Duration", 0.f, true)
|
||||
OZZ_OPTIONS_DECLARE_STRING(name, "Name", "", true)
|
||||
|
||||
int main(int _argc, char** _argv) {
|
||||
// Parses arguments.
|
||||
testing::InitGoogleTest(&_argc, _argv);
|
||||
ozz::options::ParseResult parse_result = ozz::options::ParseCommandLine(
|
||||
_argc, _argv, "1.0",
|
||||
"Test Animation archive versioning retrocompatibility");
|
||||
if (parse_result != ozz::options::kSuccess) {
|
||||
return parse_result == ozz::options::kExitSuccess ? EXIT_SUCCESS
|
||||
: EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
TEST(Versioning, AnimationSerialize) {
|
||||
// Open the file.
|
||||
const char* filename = OPTIONS_file;
|
||||
ozz::io::File file(filename, "rb");
|
||||
ASSERT_TRUE(file.opened());
|
||||
|
||||
// Open archive and test object tag.
|
||||
ozz::io::IArchive archive(&file);
|
||||
ASSERT_TRUE(archive.TestTag<ozz::animation::Animation>());
|
||||
|
||||
// Read the object.
|
||||
ozz::animation::Animation animation;
|
||||
archive >> animation;
|
||||
|
||||
// More testing
|
||||
EXPECT_EQ(animation.num_tracks(), OPTIONS_tracks);
|
||||
EXPECT_FLOAT_EQ(animation.duration(), OPTIONS_duration);
|
||||
EXPECT_STREQ(animation.name(), OPTIONS_name);
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
//----------------------------------------------------------------------------//
|
||||
// //
|
||||
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
|
||||
// and distributed under the MIT License (MIT). //
|
||||
// //
|
||||
// Copyright (c) Guillaume Blanc //
|
||||
// //
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a //
|
||||
// copy of this software and associated documentation files (the "Software"), //
|
||||
// to deal in the Software without restriction, including without limitation //
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
|
||||
// and/or sell copies of the Software, and to permit persons to whom the //
|
||||
// Software is furnished to do so, subject to the following conditions: //
|
||||
// //
|
||||
// The above copyright notice and this permission notice shall be included in //
|
||||
// all copies or substantial portions of the Software. //
|
||||
// //
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
|
||||
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
|
||||
// DEALINGS IN THE SOFTWARE. //
|
||||
// //
|
||||
//----------------------------------------------------------------------------//
|
||||
|
||||
#include "ozz/animation/runtime/animation_utils.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "ozz/base/maths/gtest_math_helper.h"
|
||||
|
||||
#include "ozz/base/memory/unique_ptr.h"
|
||||
|
||||
#include "ozz/animation/offline/animation_builder.h"
|
||||
#include "ozz/animation/offline/raw_animation.h"
|
||||
|
||||
using ozz::animation::Animation;
|
||||
using ozz::animation::offline::RawAnimation;
|
||||
using ozz::animation::offline::AnimationBuilder;
|
||||
|
||||
TEST(CountKeyframes, AnimationUtils) {
|
||||
// Builds a valid animation.
|
||||
ozz::unique_ptr<Animation> animation;
|
||||
|
||||
{
|
||||
RawAnimation raw_animation;
|
||||
raw_animation.duration = 1.f;
|
||||
raw_animation.tracks.resize(2);
|
||||
|
||||
RawAnimation::TranslationKey t_key0 = {0.f,
|
||||
ozz::math::Float3(93.f, 58.f, 46.f)};
|
||||
raw_animation.tracks[0].translations.push_back(t_key0);
|
||||
RawAnimation::TranslationKey t_key1 = {.9f,
|
||||
ozz::math::Float3(46.f, 58.f, 93.f)};
|
||||
raw_animation.tracks[0].translations.push_back(t_key1);
|
||||
RawAnimation::TranslationKey t_key2 = {1.f,
|
||||
ozz::math::Float3(46.f, 58.f, 99.f)};
|
||||
raw_animation.tracks[0].translations.push_back(t_key2);
|
||||
|
||||
RawAnimation::RotationKey r_key = {
|
||||
0.7f, ozz::math::Quaternion(0.f, 1.f, 0.f, 0.f)};
|
||||
raw_animation.tracks[0].rotations.push_back(r_key);
|
||||
|
||||
RawAnimation::ScaleKey s_key = {0.1f, ozz::math::Float3(99.f, 26.f, 14.f)};
|
||||
raw_animation.tracks[1].scales.push_back(s_key);
|
||||
|
||||
AnimationBuilder builder;
|
||||
animation = builder(raw_animation);
|
||||
ASSERT_TRUE(animation);
|
||||
}
|
||||
|
||||
// 4 more tracks than expected due to SoA
|
||||
EXPECT_EQ(ozz::animation::CountTranslationKeyframes(*animation, -1), 9);
|
||||
EXPECT_EQ(ozz::animation::CountTranslationKeyframes(*animation, 0), 3);
|
||||
EXPECT_EQ(ozz::animation::CountTranslationKeyframes(*animation, 1), 2);
|
||||
|
||||
EXPECT_EQ(ozz::animation::CountRotationKeyframes(*animation, -1), 8);
|
||||
EXPECT_EQ(ozz::animation::CountRotationKeyframes(*animation, 0), 2);
|
||||
EXPECT_EQ(ozz::animation::CountRotationKeyframes(*animation, 1), 2);
|
||||
|
||||
EXPECT_EQ(ozz::animation::CountScaleKeyframes(*animation, -1), 8);
|
||||
EXPECT_EQ(ozz::animation::CountScaleKeyframes(*animation, 0), 2);
|
||||
EXPECT_EQ(ozz::animation::CountScaleKeyframes(*animation, 1), 2);
|
||||
}
|
||||
@@ -0,0 +1,871 @@
|
||||
//----------------------------------------------------------------------------//
|
||||
// //
|
||||
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
|
||||
// and distributed under the MIT License (MIT). //
|
||||
// //
|
||||
// Copyright (c) Guillaume Blanc //
|
||||
// //
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a //
|
||||
// copy of this software and associated documentation files (the "Software"), //
|
||||
// to deal in the Software without restriction, including without limitation //
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
|
||||
// and/or sell copies of the Software, and to permit persons to whom the //
|
||||
// Software is furnished to do so, subject to the following conditions: //
|
||||
// //
|
||||
// The above copyright notice and this permission notice shall be included in //
|
||||
// all copies or substantial portions of the Software. //
|
||||
// //
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
|
||||
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
|
||||
// DEALINGS IN THE SOFTWARE. //
|
||||
// //
|
||||
//----------------------------------------------------------------------------//
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "ozz/animation/runtime/blending_job.h"
|
||||
#include "ozz/base/maths/gtest_math_helper.h"
|
||||
#include "ozz/base/maths/soa_transform.h"
|
||||
|
||||
using ozz::animation::BlendingJob;
|
||||
|
||||
TEST(JobValidity, BlendingJob) {
|
||||
const ozz::math::SoaTransform identity = ozz::math::SoaTransform::identity();
|
||||
const ozz::math::SimdFloat4 zero = ozz::math::simd_float4::zero();
|
||||
BlendingJob::Layer layers[2];
|
||||
const ozz::math::SoaTransform bind_poses[3] = {identity, identity, identity};
|
||||
const ozz::math::SoaTransform input_transforms[3] = {identity, identity,
|
||||
identity};
|
||||
ozz::math::SoaTransform output_transforms[3] = {identity, identity, identity};
|
||||
ozz::math::SimdFloat4 joint_weights[3] = {zero, zero, zero};
|
||||
|
||||
layers[0].transform = input_transforms;
|
||||
layers[1].transform = {input_transforms, input_transforms + 2};
|
||||
|
||||
{ // Empty/default job.
|
||||
BlendingJob job;
|
||||
EXPECT_FALSE(job.Validate());
|
||||
EXPECT_FALSE(job.Run());
|
||||
}
|
||||
|
||||
{ // Invalid output.
|
||||
BlendingJob job;
|
||||
job.layers = {layers, layers + 2};
|
||||
job.bind_pose = {bind_poses, bind_poses + 2};
|
||||
EXPECT_FALSE(job.Validate());
|
||||
EXPECT_FALSE(job.Run());
|
||||
}
|
||||
{ // Layers are optional.
|
||||
BlendingJob job;
|
||||
job.bind_pose = {bind_poses, bind_poses + 2};
|
||||
job.output = {output_transforms, output_transforms + 2};
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
}
|
||||
{ // Invalid bind pose.
|
||||
BlendingJob job;
|
||||
job.layers = {layers, layers + 2};
|
||||
job.output = {output_transforms, output_transforms + 2};
|
||||
EXPECT_FALSE(job.Validate());
|
||||
EXPECT_FALSE(job.Run());
|
||||
}
|
||||
{ // Invalid layer input range, too small.
|
||||
BlendingJob::Layer invalid_layers[2];
|
||||
invalid_layers[0].transform = {input_transforms, input_transforms + 1};
|
||||
invalid_layers[1].transform = {input_transforms, input_transforms + 2};
|
||||
|
||||
BlendingJob job;
|
||||
job.layers = invalid_layers;
|
||||
job.bind_pose = {bind_poses, bind_poses + 2};
|
||||
job.output = {output_transforms, output_transforms + 2};
|
||||
EXPECT_FALSE(job.Validate());
|
||||
EXPECT_FALSE(job.Run());
|
||||
}
|
||||
{ // Invalid output range, smaller output.
|
||||
BlendingJob job;
|
||||
job.layers = {layers, layers + 2};
|
||||
job.bind_pose = {bind_poses, bind_poses + 2};
|
||||
job.output = {output_transforms, output_transforms + 1};
|
||||
EXPECT_FALSE(job.Validate());
|
||||
EXPECT_FALSE(job.Run());
|
||||
}
|
||||
|
||||
{ // Invalid smaller input.
|
||||
BlendingJob job;
|
||||
job.layers = {layers, layers + 2};
|
||||
job.bind_pose = {bind_poses, bind_poses + 3};
|
||||
job.output = {output_transforms, output_transforms + 3};
|
||||
EXPECT_FALSE(job.Validate());
|
||||
EXPECT_FALSE(job.Run());
|
||||
}
|
||||
|
||||
{ // Invalid threshold.
|
||||
BlendingJob job;
|
||||
job.threshold = 0.f;
|
||||
job.layers = {layers, layers + 2};
|
||||
job.bind_pose = {bind_poses, bind_poses + 2};
|
||||
job.output = {output_transforms, output_transforms + 2};
|
||||
EXPECT_FALSE(job.Validate());
|
||||
EXPECT_FALSE(job.Run());
|
||||
}
|
||||
|
||||
{ // Invalid joint weights range.
|
||||
layers[0].joint_weights = {joint_weights, joint_weights + 1};
|
||||
|
||||
BlendingJob job;
|
||||
job.layers = {layers, layers + 2};
|
||||
job.bind_pose = {bind_poses, bind_poses + 2};
|
||||
job.output = {output_transforms, output_transforms + 2};
|
||||
EXPECT_FALSE(job.Validate());
|
||||
EXPECT_FALSE(job.Run());
|
||||
}
|
||||
|
||||
{ // Valid job.
|
||||
layers[0].joint_weights = {nullptr, nullptr};
|
||||
|
||||
BlendingJob job;
|
||||
job.layers = {layers, layers + 2};
|
||||
job.bind_pose = {bind_poses, bind_poses + 2};
|
||||
job.output = {output_transforms, output_transforms + 2};
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
}
|
||||
|
||||
{ // Valid joint weights range.
|
||||
layers[0].joint_weights = {joint_weights, joint_weights + 2};
|
||||
|
||||
BlendingJob job;
|
||||
job.layers = {layers, layers + 2};
|
||||
job.bind_pose = {bind_poses, bind_poses + 2};
|
||||
job.output = {output_transforms, output_transforms + 2};
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
}
|
||||
|
||||
{ // Valid job, bigger output.
|
||||
layers[0].joint_weights = {joint_weights, joint_weights + 2};
|
||||
|
||||
BlendingJob job;
|
||||
job.layers = {layers, layers + 2};
|
||||
job.bind_pose = {bind_poses, bind_poses + 2};
|
||||
job.output = {output_transforms, output_transforms + 3};
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
}
|
||||
|
||||
{ // Valid no layers.
|
||||
BlendingJob job;
|
||||
job.bind_pose = {bind_poses, bind_poses + 2};
|
||||
job.output = {output_transforms, output_transforms + 2};
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(JobValidityAdditive, BlendingJob) {
|
||||
const ozz::math::SoaTransform identity = ozz::math::SoaTransform::identity();
|
||||
const ozz::math::SimdFloat4 zero = ozz::math::simd_float4::zero();
|
||||
BlendingJob::Layer layers[2];
|
||||
BlendingJob::Layer additive_layers[2];
|
||||
|
||||
const ozz::math::SoaTransform bind_poses[3] = {identity, identity, identity};
|
||||
const ozz::math::SoaTransform input_transforms[3] = {identity, identity,
|
||||
identity};
|
||||
ozz::math::SoaTransform output_transforms[3] = {identity, identity, identity};
|
||||
ozz::math::SimdFloat4 joint_weights[3] = {zero, zero, zero};
|
||||
|
||||
layers[0].transform = input_transforms;
|
||||
layers[1].transform = input_transforms;
|
||||
|
||||
additive_layers[0].transform = input_transforms;
|
||||
additive_layers[1].transform = input_transforms;
|
||||
|
||||
{ // Valid additive job, no normal blending.
|
||||
BlendingJob job;
|
||||
job.additive_layers = additive_layers;
|
||||
job.bind_pose = bind_poses;
|
||||
job.output = output_transforms;
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
}
|
||||
|
||||
{ // Valid additive job, with normal blending also.
|
||||
|
||||
BlendingJob job;
|
||||
job.layers = layers;
|
||||
job.additive_layers = additive_layers;
|
||||
job.bind_pose = bind_poses;
|
||||
job.output = output_transforms;
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
}
|
||||
|
||||
{ // Invalid layer input range, too small.
|
||||
BlendingJob::Layer invalid_layers[2];
|
||||
invalid_layers[0].transform = {input_transforms, input_transforms + 1};
|
||||
invalid_layers[1].transform = {input_transforms, input_transforms + 2};
|
||||
|
||||
BlendingJob job;
|
||||
job.layers = layers;
|
||||
job.additive_layers = invalid_layers;
|
||||
job.bind_pose = {bind_poses, bind_poses + 2};
|
||||
job.output = {output_transforms, output_transforms + 2};
|
||||
EXPECT_FALSE(job.Validate());
|
||||
EXPECT_FALSE(job.Run());
|
||||
}
|
||||
|
||||
{ // Valid additive job, with per-joint weights.
|
||||
layers[0].joint_weights = {joint_weights, joint_weights + 2};
|
||||
|
||||
BlendingJob job;
|
||||
job.additive_layers = additive_layers;
|
||||
job.bind_pose = {bind_poses, bind_poses + 2};
|
||||
job.output = {output_transforms, output_transforms + 2};
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Empty, BlendingJob) {
|
||||
const ozz::math::SoaTransform identity = ozz::math::SoaTransform::identity();
|
||||
|
||||
// Initialize bind pose.
|
||||
ozz::math::SoaTransform bind_poses[2] = {identity, identity};
|
||||
bind_poses[0].translation = ozz::math::SoaFloat3::Load(
|
||||
ozz::math::simd_float4::Load(0.f, 1.f, 2.f, 3.f),
|
||||
ozz::math::simd_float4::Load(4.f, 5.f, 6.f, 7.f),
|
||||
ozz::math::simd_float4::Load(8.f, 9.f, 10.f, 11.f));
|
||||
bind_poses[0].scale = ozz::math::SoaFloat3::Load(
|
||||
ozz::math::simd_float4::Load(0.f, 10.f, 20.f, 30.f),
|
||||
ozz::math::simd_float4::Load(40.f, 50.f, 60.f, 70.f),
|
||||
ozz::math::simd_float4::Load(80.f, 90.f, 100.f, 110.f));
|
||||
bind_poses[1].translation = bind_poses[0].translation *
|
||||
ozz::math::simd_float4::Load(2.f, 2.f, 2.f, 2.f);
|
||||
bind_poses[1].scale =
|
||||
bind_poses[0].scale * ozz::math::simd_float4::Load(2.f, 2.f, 2.f, 2.f);
|
||||
|
||||
BlendingJob job;
|
||||
job.bind_pose = bind_poses;
|
||||
ozz::math::SoaTransform output_transforms[2];
|
||||
job.output = output_transforms;
|
||||
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].translation, 0.f, 1.f, 2.f, 3.f, 4.f,
|
||||
5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 11.f);
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].scale, 0.f, 10.f, 20.f, 30.f, 40.f,
|
||||
50.f, 60.f, 70.f, 80.f, 90.f, 100.f, 110.f);
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[1].translation, 0.f, 2.f, 4.f, 6.f, 8.f,
|
||||
10.f, 12.f, 14.f, 16.f, 18.f, 20.f, 22.f);
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[1].scale, 0.f, 20.f, 40.f, 60.f, 80.f,
|
||||
100.f, 120.f, 140.f, 160.f, 180.f, 200.f, 220.f);
|
||||
}
|
||||
|
||||
TEST(Weight, BlendingJob) {
|
||||
const ozz::math::SoaTransform identity = ozz::math::SoaTransform::identity();
|
||||
|
||||
// Initialize inputs.
|
||||
ozz::math::SoaTransform input_transforms[2][2] = {{identity, identity},
|
||||
{identity, identity}};
|
||||
input_transforms[0][0].translation = ozz::math::SoaFloat3::Load(
|
||||
ozz::math::simd_float4::Load(0.f, 1.f, 2.f, 3.f),
|
||||
ozz::math::simd_float4::Load(4.f, 5.f, 6.f, 7.f),
|
||||
ozz::math::simd_float4::Load(8.f, 9.f, 10.f, 11.f));
|
||||
input_transforms[0][1].translation = ozz::math::SoaFloat3::Load(
|
||||
ozz::math::simd_float4::Load(12.f, 13.f, 14.f, 15.f),
|
||||
ozz::math::simd_float4::Load(16.f, 17.f, 18.f, 19.f),
|
||||
ozz::math::simd_float4::Load(20.f, 21.f, 22.f, 23.f));
|
||||
input_transforms[1][0].translation = -input_transforms[0][0].translation;
|
||||
input_transforms[1][1].translation = -input_transforms[0][1].translation;
|
||||
|
||||
// Initialize bind pose.
|
||||
ozz::math::SoaTransform bind_poses[2] = {identity, identity};
|
||||
bind_poses[0].scale = ozz::math::SoaFloat3::Load(
|
||||
ozz::math::simd_float4::Load(0.f, 1.f, 2.f, 3.f),
|
||||
ozz::math::simd_float4::Load(4.f, 5.f, 6.f, 7.f),
|
||||
ozz::math::simd_float4::Load(8.f, 9.f, 10.f, 11.f));
|
||||
bind_poses[1].scale =
|
||||
bind_poses[0].scale * ozz::math::simd_float4::Load(2.f, 2.f, 2.f, 2.f);
|
||||
|
||||
{
|
||||
BlendingJob::Layer layers[2];
|
||||
layers[0].transform = input_transforms[0];
|
||||
layers[1].transform = input_transforms[1];
|
||||
|
||||
ozz::math::SoaTransform output_transforms[2];
|
||||
|
||||
BlendingJob job;
|
||||
job.layers = layers;
|
||||
job.bind_pose = bind_poses;
|
||||
job.output = output_transforms;
|
||||
|
||||
// Weight 0 (a bit less must give the same result) for the first layer,
|
||||
// 1 for the second.
|
||||
layers[0].weight = -.07f;
|
||||
layers[1].weight = 1.f;
|
||||
|
||||
EXPECT_TRUE(job.Run());
|
||||
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].translation, -0.f, -1.f, -2.f,
|
||||
-3.f, -4.f, -5.f, -6.f, -7.f, -8.f, -9.f, -10.f, -11.f);
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].scale, 1.f, 1.f, 1.f, 1.f, 1.f,
|
||||
1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f);
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[1].translation, -12.f, -13.f, -14.f,
|
||||
-15.f, -16.f, -17.f, -18.f, -19.f, -20.f, -21.f, -22.f,
|
||||
-23.f);
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[1].scale, 1.f, 1.f, 1.f, 1.f, 1.f,
|
||||
1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f);
|
||||
|
||||
// Weight 1 for the first layer, 0 for the second.
|
||||
layers[0].weight = 1.f;
|
||||
layers[1].weight = 1e-27f; // Very low weight value.
|
||||
|
||||
EXPECT_TRUE(job.Run());
|
||||
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].translation, 0.f, 1.f, 2.f, 3.f,
|
||||
4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 11.f);
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].scale, 1.f, 1.f, 1.f, 1.f, 1.f,
|
||||
1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f);
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[1].translation, 12.f, 13.f, 14.f,
|
||||
15.f, 16.f, 17.f, 18.f, 19.f, 20.f, 21.f, 22.f, 23.f);
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[1].scale, 1.f, 1.f, 1.f, 1.f, 1.f,
|
||||
1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f);
|
||||
|
||||
// Weight .5 for both layers.
|
||||
layers[0].weight = .5f;
|
||||
layers[1].weight = .5f;
|
||||
|
||||
EXPECT_TRUE(job.Run());
|
||||
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].translation, 0.f, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f);
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].scale, 1.f, 1.f, 1.f, 1.f, 1.f,
|
||||
1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f);
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[1].translation, 0.f, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f);
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[1].scale, 1.f, 1.f, 1.f, 1.f, 1.f,
|
||||
1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(JointWeights, BlendingJob) {
|
||||
const ozz::math::SoaTransform identity = ozz::math::SoaTransform::identity();
|
||||
|
||||
// Initialize inputs.
|
||||
ozz::math::SoaTransform input_transforms[2][2] = {{identity, identity},
|
||||
{identity, identity}};
|
||||
input_transforms[0][0].translation = ozz::math::SoaFloat3::Load(
|
||||
ozz::math::simd_float4::Load(0.f, 1.f, 2.f, 3.f),
|
||||
ozz::math::simd_float4::Load(4.f, 5.f, 6.f, 7.f),
|
||||
ozz::math::simd_float4::Load(8.f, 9.f, 10.f, 11.f));
|
||||
input_transforms[0][1].translation = ozz::math::SoaFloat3::Load(
|
||||
ozz::math::simd_float4::Load(12.f, 13.f, 14.f, 15.f),
|
||||
ozz::math::simd_float4::Load(16.f, 17.f, 18.f, 19.f),
|
||||
ozz::math::simd_float4::Load(20.f, 21.f, 22.f, 23.f));
|
||||
input_transforms[1][0].translation = -input_transforms[0][0].translation;
|
||||
input_transforms[1][1].translation = -input_transforms[0][1].translation;
|
||||
ozz::math::SimdFloat4 joint_weights[2][2] = {
|
||||
{ozz::math::simd_float4::Load(1.f, 1.f, 0.f, 0.f),
|
||||
ozz::math::simd_float4::Load(1.f, 0.f, 1.f, 1.f)},
|
||||
{ozz::math::simd_float4::Load(1.f, 1.f, 1.f, 0.f),
|
||||
ozz::math::simd_float4::Load(0.f, 1.f, 1.f, 1.f)}};
|
||||
// Initialize bind pose.
|
||||
ozz::math::SoaTransform bind_poses[2] = {identity, identity};
|
||||
bind_poses[0].translation = ozz::math::SoaFloat3::Load(
|
||||
ozz::math::simd_float4::Load(10.f, 11.f, 12.f, 13.f),
|
||||
ozz::math::simd_float4::Load(14.f, 15.f, 16.f, 17.f),
|
||||
ozz::math::simd_float4::Load(18.f, 19.f, 20.f, 21.f));
|
||||
bind_poses[0].scale = ozz::math::SoaFloat3::Load(
|
||||
ozz::math::simd_float4::Load(0.f, 1.f, 2.f, 3.f),
|
||||
ozz::math::simd_float4::Load(4.f, 5.f, 6.f, 7.f),
|
||||
ozz::math::simd_float4::Load(8.f, 9.f, 10.f, 11.f));
|
||||
bind_poses[1].scale =
|
||||
bind_poses[0].scale * ozz::math::simd_float4::Load(2.f, 2.f, 2.f, 2.f);
|
||||
|
||||
BlendingJob::Layer layers[2];
|
||||
layers[0].transform = input_transforms[0];
|
||||
layers[0].joint_weights = joint_weights[0];
|
||||
layers[1].transform = input_transforms[1];
|
||||
layers[1].joint_weights = joint_weights[1];
|
||||
|
||||
{ // Weight .5 for both layers.
|
||||
ozz::math::SoaTransform output_transforms[3];
|
||||
|
||||
BlendingJob job;
|
||||
job.layers = layers;
|
||||
job.bind_pose = bind_poses;
|
||||
job.output = output_transforms;
|
||||
|
||||
layers[0].weight = .5f;
|
||||
layers[1].weight = .5f;
|
||||
|
||||
EXPECT_TRUE(job.Run());
|
||||
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].translation, 0.f, 0.f, -2.f, 13.f,
|
||||
0.f, 0.f, -6.f, 17.f, 0.f, 0.f, -10.f, 21.f);
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].scale, 1.f, 1.f, 1.f, 3.f, 1.f,
|
||||
1.f, 1.f, 7.f, 1.f, 1.f, 1.f, 11.f);
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[1].translation, 12.f, -13.f, 0.f, 0.f,
|
||||
16.f, -17.f, 0.f, 0.f, 20.f, -21.f, 0.f, 0.f);
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[1].scale, 1.f, 1.f, 1.f, 1.f, 1.f,
|
||||
1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f);
|
||||
}
|
||||
{ // Null weight for the first layer.
|
||||
ozz::math::SoaTransform output_transforms[2];
|
||||
|
||||
BlendingJob job;
|
||||
job.layers = layers;
|
||||
job.bind_pose = bind_poses;
|
||||
job.output = output_transforms;
|
||||
|
||||
layers[0].weight = 0.f;
|
||||
layers[1].weight = 1.f;
|
||||
|
||||
EXPECT_TRUE(job.Run());
|
||||
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].translation, -0.f, -1.f, -2.f,
|
||||
13.f, -4.f, -5.f, -6.f, 17.f, -8.f, -9.f, -10.f, 21.f);
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].scale, 1.f, 1.f, 1.f, 3.f, 1.f,
|
||||
1.f, 1.f, 7.f, 1.f, 1.f, 1.f, 11.f);
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[1].translation, 0.f, -13.f, -14.f,
|
||||
-15.f, 0.f, -17.f, -18.f, -19.f, 0.f, -21.f, -22.f,
|
||||
-23.f);
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[1].scale, 0.f, 1.f, 1.f, 1.f, 8.f,
|
||||
1.f, 1.f, 1.f, 16.f, 1.f, 1.f, 1.f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Normalize, BlendingJob) {
|
||||
const ozz::math::SoaTransform identity = ozz::math::SoaTransform::identity();
|
||||
|
||||
// Initialize inputs.
|
||||
ozz::math::SoaTransform input_transforms[2][1] = {{identity}, {identity}};
|
||||
|
||||
// Initialize bind pose.
|
||||
ozz::math::SoaTransform bind_poses[1] = {identity};
|
||||
bind_poses[0].scale = ozz::math::SoaFloat3::Load(
|
||||
ozz::math::simd_float4::Load(0.f, 1.f, 2.f, 3.f),
|
||||
ozz::math::simd_float4::Load(4.f, 5.f, 6.f, 7.f),
|
||||
ozz::math::simd_float4::Load(8.f, 9.f, 10.f, 11.f));
|
||||
|
||||
input_transforms[0][0].rotation = ozz::math::SoaQuaternion::Load(
|
||||
ozz::math::simd_float4::Load(.70710677f, 0.f, 0.f, .382683432f),
|
||||
ozz::math::simd_float4::Load(0.f, 0.f, .70710677f, 0.f),
|
||||
ozz::math::simd_float4::Load(0.f, 0.f, 0.f, 0.f),
|
||||
ozz::math::simd_float4::Load(.70710677f, 1.f, .70710677f, .9238795f));
|
||||
input_transforms[1][0].rotation = ozz::math::SoaQuaternion::Load(
|
||||
ozz::math::simd_float4::Load(0.f, .70710677f, -.70710677f, -.382683432f),
|
||||
ozz::math::simd_float4::Load(0.f, 0.f, 0.f, 0.f),
|
||||
ozz::math::simd_float4::Load(0.f, 0.f, -.70710677f, 0.f),
|
||||
ozz::math::simd_float4::Load(1.f, .70710677f, 0.f, -.9238795f));
|
||||
|
||||
{ // Un-normalized weights < 1.f.
|
||||
input_transforms[0][0].translation = ozz::math::SoaFloat3::Load(
|
||||
ozz::math::simd_float4::Load(2.f, 3.f, 4.f, 5.f),
|
||||
ozz::math::simd_float4::Load(6.f, 7.f, 8.f, 9.f),
|
||||
ozz::math::simd_float4::Load(10.f, 11.f, 12.f, 13.f));
|
||||
input_transforms[1][0].translation = ozz::math::SoaFloat3::Load(
|
||||
ozz::math::simd_float4::Load(3.f, 4.f, 5.f, 6.f),
|
||||
ozz::math::simd_float4::Load(7.f, 8.f, 9.f, 10.f),
|
||||
ozz::math::simd_float4::Load(11.f, 12.f, 13.f, 14.f));
|
||||
|
||||
BlendingJob::Layer layers[2];
|
||||
layers[0].weight = 0.2f;
|
||||
layers[0].transform = input_transforms[0];
|
||||
layers[1].weight = 0.3f;
|
||||
layers[1].transform = input_transforms[1];
|
||||
|
||||
ozz::math::SoaTransform output_transforms[1];
|
||||
|
||||
BlendingJob job;
|
||||
job.layers = layers;
|
||||
job.bind_pose = bind_poses;
|
||||
job.output = output_transforms;
|
||||
|
||||
EXPECT_TRUE(job.Run());
|
||||
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].translation, 2.6f, 3.6f, 4.6f,
|
||||
5.6f, 6.6f, 7.6f, 8.6f, 9.6f, 10.6f, 11.6f, 12.6f,
|
||||
13.6f);
|
||||
EXPECT_SOAQUATERNION_EQ_EST(output_transforms[0].rotation, .30507791f,
|
||||
.45761687f, -.58843851f, .38268352f, 0.f, 0.f,
|
||||
.39229235f, 0.f, 0.f, 0.f, -.58843851f, 0.f,
|
||||
.95224595f, .88906217f, .39229235f, .92387962f);
|
||||
EXPECT_TRUE(
|
||||
ozz::math::AreAllTrue(IsNormalizedEst(output_transforms[0].rotation)));
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].scale, 1.f, 1.f, 1.f, 1.f, 1.f,
|
||||
1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f);
|
||||
}
|
||||
{ // Un-normalized weights > 1.f.
|
||||
input_transforms[0][0].translation = ozz::math::SoaFloat3::Load(
|
||||
ozz::math::simd_float4::Load(5.f, 10.f, 15.f, 20.f),
|
||||
ozz::math::simd_float4::Load(25.f, 30.f, 35.f, 40.f),
|
||||
ozz::math::simd_float4::Load(45.f, 50.f, 55.f, 60.f));
|
||||
input_transforms[1][0].translation = ozz::math::SoaFloat3::Load(
|
||||
ozz::math::simd_float4::Load(10.f, 15.f, 20.f, 25.f),
|
||||
ozz::math::simd_float4::Load(30.f, 35.f, 40.f, 45.f),
|
||||
ozz::math::simd_float4::Load(50.f, 55.f, 60.f, 65.f));
|
||||
|
||||
BlendingJob::Layer layers[2];
|
||||
layers[0].weight = 2.f;
|
||||
layers[0].transform = input_transforms[0];
|
||||
layers[1].weight = 3.f;
|
||||
layers[1].transform = input_transforms[1];
|
||||
|
||||
ozz::math::SoaTransform output_transforms[1];
|
||||
|
||||
BlendingJob job;
|
||||
job.layers = layers;
|
||||
job.bind_pose = bind_poses;
|
||||
job.output = output_transforms;
|
||||
|
||||
EXPECT_TRUE(job.Run());
|
||||
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].translation, 8.f, 13.f, 18.f, 23.f,
|
||||
28.f, 33.f, 38.f, 43.f, 48.f, 53.f, 58.f, 63.f);
|
||||
EXPECT_SOAQUATERNION_EQ_EST(output_transforms[0].rotation, .30507791f,
|
||||
.45761687f, -.58843851f, .38268352f, 0.f, 0.f,
|
||||
.39229235f, 0.f, 0.f, 0.f, -.58843851f, 0.f,
|
||||
.95224595f, .88906217f, .39229235f, .92387962f);
|
||||
EXPECT_TRUE(
|
||||
ozz::math::AreAllTrue(IsNormalizedEst(output_transforms[0].rotation)));
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].scale, 1.f, 1.f, 1.f, 1.f, 1.f,
|
||||
1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f);
|
||||
}
|
||||
{ // Un-normalized weights > 1.f, with per-joint weights.
|
||||
input_transforms[0][0].translation = ozz::math::SoaFloat3::Load(
|
||||
ozz::math::simd_float4::Load(5.f, 10.f, 15.f, 20.f),
|
||||
ozz::math::simd_float4::Load(25.f, 30.f, 35.f, 40.f),
|
||||
ozz::math::simd_float4::Load(45.f, 50.f, 55.f, 60.f));
|
||||
input_transforms[1][0].translation = ozz::math::SoaFloat3::Load(
|
||||
ozz::math::simd_float4::Load(10.f, 15.f, 20.f, 25.f),
|
||||
ozz::math::simd_float4::Load(30.f, 35.f, 40.f, 45.f),
|
||||
ozz::math::simd_float4::Load(50.f, 55.f, 60.f, 65.f));
|
||||
ozz::math::SimdFloat4 joint_weights[1] = {
|
||||
ozz::math::simd_float4::Load(1.f, -1.f, 2.f, .1f)};
|
||||
|
||||
BlendingJob::Layer layers[2];
|
||||
layers[0].weight = 2.f;
|
||||
layers[0].transform = input_transforms[0];
|
||||
layers[1].weight = 3.f;
|
||||
layers[1].transform = input_transforms[1];
|
||||
layers[1].joint_weights = joint_weights;
|
||||
|
||||
ozz::math::SoaTransform output_transforms[1];
|
||||
|
||||
BlendingJob job;
|
||||
job.layers = layers;
|
||||
job.bind_pose = bind_poses;
|
||||
job.output = output_transforms;
|
||||
|
||||
EXPECT_TRUE(job.Run());
|
||||
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].translation, 8.f, 10.f,
|
||||
150.f / 8.f, 47.5f / 2.3f, 28.f, 30.f, 310.f / 8.f,
|
||||
93.5f / 2.3f, 48.f, 50.f, 470.f / 8.f, 139.5f / 2.3f);
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].scale, 1.f, 1.f, 1.f, 1.f, 1.f,
|
||||
1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Threshold, BlendingJob) {
|
||||
const ozz::math::SoaTransform identity = ozz::math::SoaTransform::identity();
|
||||
|
||||
// Initialize inputs.
|
||||
ozz::math::SoaTransform input_transforms[2][1] = {{identity}, {identity}};
|
||||
|
||||
// Initialize bind pose.
|
||||
ozz::math::SoaTransform bind_poses[1] = {identity};
|
||||
bind_poses[0].scale = ozz::math::SoaFloat3::Load(
|
||||
ozz::math::simd_float4::Load(0.f, 1.f, 2.f, 3.f),
|
||||
ozz::math::simd_float4::Load(4.f, 5.f, 6.f, 7.f),
|
||||
ozz::math::simd_float4::Load(8.f, 9.f, 10.f, 11.f));
|
||||
|
||||
input_transforms[0][0].translation = ozz::math::SoaFloat3::Load(
|
||||
ozz::math::simd_float4::Load(2.f, 3.f, 4.f, 5.f),
|
||||
ozz::math::simd_float4::Load(6.f, 7.f, 8.f, 9.f),
|
||||
ozz::math::simd_float4::Load(10.f, 11.f, 12.f, 13.f));
|
||||
input_transforms[1][0].translation = ozz::math::SoaFloat3::Load(
|
||||
ozz::math::simd_float4::Load(3.f, 4.f, 5.f, 6.f),
|
||||
ozz::math::simd_float4::Load(7.f, 8.f, 9.f, 10.f),
|
||||
ozz::math::simd_float4::Load(11.f, 12.f, 13.f, 14.f));
|
||||
|
||||
{ // Threshold is not reached.
|
||||
BlendingJob::Layer layers[2];
|
||||
layers[0].weight = 0.04f;
|
||||
layers[0].transform = input_transforms[0];
|
||||
layers[1].weight = 0.06f;
|
||||
layers[1].transform = input_transforms[1];
|
||||
|
||||
ozz::math::SoaTransform output_transforms[1];
|
||||
|
||||
BlendingJob job;
|
||||
job.threshold = .1f;
|
||||
job.layers = layers;
|
||||
job.bind_pose = bind_poses;
|
||||
job.output = output_transforms;
|
||||
|
||||
EXPECT_TRUE(job.Run());
|
||||
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].translation, 2.6f, 3.6f, 4.6f,
|
||||
5.6f, 6.6f, 7.6f, 8.6f, 9.6f, 10.6f, 11.6f, 12.6f,
|
||||
13.6f);
|
||||
EXPECT_SOAQUATERNION_EQ_EST(output_transforms[0].rotation, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f,
|
||||
1.f, 1.f, 1.f, 1.f);
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].scale, 1.f, 1.f, 1.f, 1.f, 1.f,
|
||||
1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f);
|
||||
}
|
||||
{ // Threshold is reached at 100%.
|
||||
BlendingJob::Layer layers[2];
|
||||
layers[0].weight = 1e-27f;
|
||||
layers[0].transform = input_transforms[0];
|
||||
layers[1].weight = 0.f;
|
||||
layers[1].transform = input_transforms[1];
|
||||
|
||||
ozz::math::SoaTransform output_transforms[1];
|
||||
|
||||
BlendingJob job;
|
||||
job.threshold = .1f;
|
||||
job.layers = layers;
|
||||
job.bind_pose = bind_poses;
|
||||
job.output = output_transforms;
|
||||
|
||||
EXPECT_TRUE(job.Run());
|
||||
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].translation, 0.f, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f);
|
||||
EXPECT_SOAQUATERNION_EQ_EST(output_transforms[0].rotation, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f,
|
||||
1.f, 1.f, 1.f, 1.f);
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].scale, 0.f, 1.f, 2.f, 3.f, 4.f,
|
||||
5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 11.f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(AdditiveWeight, BlendingJob) {
|
||||
const ozz::math::SoaTransform identity = ozz::math::SoaTransform::identity();
|
||||
|
||||
// Initialize inputs.
|
||||
ozz::math::SoaTransform input_transforms[2][1] = {{identity}, {identity}};
|
||||
input_transforms[0][0].translation = ozz::math::SoaFloat3::Load(
|
||||
ozz::math::simd_float4::Load(0.f, 1.f, 2.f, 3.f),
|
||||
ozz::math::simd_float4::Load(4.f, 5.f, 6.f, 7.f),
|
||||
ozz::math::simd_float4::Load(8.f, 9.f, 10.f, 11.f));
|
||||
input_transforms[0][0].rotation = ozz::math::SoaQuaternion::Load(
|
||||
ozz::math::simd_float4::Load(.70710677f, 0.f, 0.f, .382683432f),
|
||||
ozz::math::simd_float4::Load(0.f, 0.f, .70710677f, 0.f),
|
||||
ozz::math::simd_float4::Load(0.f, 0.f, 0.f, 0.f),
|
||||
ozz::math::simd_float4::Load(.70710677f, 1.f, -.70710677f, .9238795f));
|
||||
input_transforms[0][0].scale = ozz::math::SoaFloat3::Load(
|
||||
ozz::math::simd_float4::Load(12.f, 13.f, 14.f, 15.f),
|
||||
ozz::math::simd_float4::Load(16.f, 17.f, 18.f, 19.f),
|
||||
ozz::math::simd_float4::Load(20.f, 21.f, 22.f, 23.f));
|
||||
input_transforms[1][0].translation = -input_transforms[0][0].translation;
|
||||
input_transforms[1][0].rotation = Conjugate(input_transforms[0][0].rotation);
|
||||
input_transforms[1][0].scale = -input_transforms[0][0].scale;
|
||||
|
||||
// Initialize bind pose.
|
||||
ozz::math::SoaTransform bind_poses[1] = {identity};
|
||||
|
||||
{
|
||||
BlendingJob::Layer layers[1];
|
||||
layers[0].transform = input_transforms[0];
|
||||
|
||||
ozz::math::SoaTransform output_transforms[1];
|
||||
|
||||
BlendingJob job;
|
||||
job.additive_layers = layers;
|
||||
job.bind_pose = bind_poses;
|
||||
job.output = output_transforms;
|
||||
|
||||
// No weight for the 1st layer.
|
||||
layers[0].weight = 0.f;
|
||||
EXPECT_TRUE(job.Run());
|
||||
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].translation, 0.f, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f);
|
||||
EXPECT_SOAQUATERNION_EQ_EST(output_transforms[0].rotation, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f,
|
||||
1.f, 1.f, 1.f, 1.f);
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].scale, 1.f, 1.f, 1.f, 1.f, 1.f,
|
||||
1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f);
|
||||
|
||||
// .5 weight for the 1st layer.
|
||||
layers[0].weight = .5f;
|
||||
EXPECT_TRUE(job.Run());
|
||||
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].translation, 0.f, .5f, 1.f, 1.5f,
|
||||
2.f, 2.5f, 3.f, 3.5f, 4.f, 4.5f, 5.f, 5.5f);
|
||||
EXPECT_SOAQUATERNION_EQ_EST(output_transforms[0].rotation, .3826834f, 0.f,
|
||||
0.f, .19509032f, 0.f, 0.f, -.3826834f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f, .9238795f, 1.f, .9238795f,
|
||||
.98078528f);
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].scale, 6.5f, 7.f, 7.5f, 8.f, 8.5f,
|
||||
9.f, 9.5f, 10.f, 10.5f, 11.f, 11.5f, 12.f);
|
||||
|
||||
// Full weight for the 1st layer.
|
||||
layers[0].weight = 1.f;
|
||||
EXPECT_TRUE(job.Run());
|
||||
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].translation, 0.f, 1.f, 2.f, 3.f,
|
||||
4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 11.f);
|
||||
EXPECT_SOAQUATERNION_EQ_EST(output_transforms[0].rotation, .70710677f, 0.f,
|
||||
0.f, .382683432f, 0.f, 0.f, -.70710677f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f, .70710677f, 1.f, .70710677f,
|
||||
.9238795f);
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].scale, 12.f, 13.f, 14.f, 15.f,
|
||||
16.f, 17.f, 18.f, 19.f, 20.f, 21.f, 22.f, 23.f);
|
||||
}
|
||||
|
||||
{
|
||||
BlendingJob::Layer layers[2];
|
||||
layers[0].transform = input_transforms[0];
|
||||
layers[1].transform = input_transforms[1];
|
||||
|
||||
ozz::math::SoaTransform output_transforms[1];
|
||||
|
||||
BlendingJob job;
|
||||
job.additive_layers = layers;
|
||||
job.bind_pose = bind_poses;
|
||||
job.output = output_transforms;
|
||||
|
||||
// No weight for the 1st layer.
|
||||
layers[0].weight = 0.f;
|
||||
layers[1].weight = 1.f;
|
||||
|
||||
EXPECT_TRUE(job.Run());
|
||||
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].translation, -0.f, -1.f, -2.f,
|
||||
-3.f, -4.f, -5.f, -6.f, -7.f, -8.f, -9.f, -10.f, -11.f);
|
||||
EXPECT_SOAQUATERNION_EQ_EST(output_transforms[0].rotation, -.70710677f,
|
||||
-0.f, -0.f, -.382683432f, -0.f, -0.f,
|
||||
.70710677f, -0.f, -0.f, -0.f, -0.f, -0.f,
|
||||
.70710677f, 1.f, .70710677f, .9238795f);
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].scale, -12.f, -13.f, -14.f, -15.f,
|
||||
-16.f, -17.f, -18.f, -19.f, -20.f, -21.f, -22.f, -23.f);
|
||||
|
||||
// Full weight for the both layer.
|
||||
layers[0].weight = 1.f;
|
||||
layers[1].weight = 1.f;
|
||||
|
||||
EXPECT_TRUE(job.Run());
|
||||
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].translation, 0.f, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f);
|
||||
EXPECT_SOAQUATERNION_EQ_EST(output_transforms[0].rotation, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f,
|
||||
1.f, 1.f, 1.f, 1.f);
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].scale, -144.f, -169.f, -196.f,
|
||||
-225.f, -256.f, -289.f, -324.f, -361.f, -400.f, -441.f,
|
||||
-484.f, -529.f);
|
||||
|
||||
// Subtract second layer.
|
||||
layers[0].weight = .5f;
|
||||
layers[1].transform = input_transforms[0];
|
||||
layers[1].weight = -.5f;
|
||||
|
||||
EXPECT_TRUE(job.Run());
|
||||
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].translation, 0.f, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f);
|
||||
EXPECT_SOAQUATERNION_EQ_EST(output_transforms[0].rotation, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f,
|
||||
1.f, 1.f, 1.f, 1.f);
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output_transforms[0].scale, 1.f, 1.f, 1.f, 1.f, 1.f,
|
||||
1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(AdditiveJointWeight, BlendingJob) {
|
||||
const ozz::math::SoaTransform identity = ozz::math::SoaTransform::identity();
|
||||
|
||||
// Initialize inputs.
|
||||
ozz::math::SoaTransform input_transforms[1] = {identity};
|
||||
input_transforms[0].translation = ozz::math::SoaFloat3::Load(
|
||||
ozz::math::simd_float4::Load(0.f, 1.f, 2.f, 3.f),
|
||||
ozz::math::simd_float4::Load(4.f, 5.f, 6.f, 7.f),
|
||||
ozz::math::simd_float4::Load(8.f, 9.f, 10.f, 11.f));
|
||||
input_transforms[0].rotation = ozz::math::SoaQuaternion::Load(
|
||||
ozz::math::simd_float4::Load(.70710677f, 0.f, 0.f, .382683432f),
|
||||
ozz::math::simd_float4::Load(0.f, 0.f, .70710677f, 0.f),
|
||||
ozz::math::simd_float4::Load(0.f, 0.f, 0.f, 0.f),
|
||||
ozz::math::simd_float4::Load(.70710677f, 1.f, -.70710677f, .9238795f));
|
||||
input_transforms[0].scale = ozz::math::SoaFloat3::Load(
|
||||
ozz::math::simd_float4::Load(12.f, 13.f, 14.f, 15.f),
|
||||
ozz::math::simd_float4::Load(16.f, 17.f, 18.f, 19.f),
|
||||
ozz::math::simd_float4::Load(20.f, 21.f, 22.f, 23.f));
|
||||
|
||||
ozz::math::SimdFloat4 joint_weights[1] = {
|
||||
ozz::math::simd_float4::Load(1.f, .5f, 0.f, -1.f)};
|
||||
|
||||
// Initialize bind pose.
|
||||
ozz::math::SoaTransform bind_poses[1] = {identity};
|
||||
|
||||
{
|
||||
BlendingJob::Layer layers[1];
|
||||
layers[0].transform = input_transforms;
|
||||
layers[0].joint_weights = joint_weights;
|
||||
|
||||
ozz::math::SoaTransform output_transforms[1];
|
||||
|
||||
BlendingJob job;
|
||||
job.additive_layers = layers;
|
||||
job.bind_pose = bind_poses;
|
||||
job.output = output_transforms;
|
||||
|
||||
// No weight for the 1st layer.
|
||||
layers[0].weight = 0.f;
|
||||
EXPECT_TRUE(job.Run());
|
||||
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].translation, 0.f, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f);
|
||||
EXPECT_SOAQUATERNION_EQ_EST(output_transforms[0].rotation, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f,
|
||||
1.f, 1.f, 1.f, 1.f);
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].scale, 1.f, 1.f, 1.f, 1.f, 1.f,
|
||||
1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f);
|
||||
|
||||
// .5 weight for the 1st layer.
|
||||
layers[0].weight = .5f;
|
||||
EXPECT_TRUE(job.Run());
|
||||
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].translation, 0.f, .25f, 0.f, 0.f,
|
||||
2.f, 1.25f, 0.f, 0.f, 4.f, 2.25f, 0.f, 0.f);
|
||||
EXPECT_SOAQUATERNION_EQ_EST(output_transforms[0].rotation, .3826834f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f,
|
||||
0.f, .9238795f, 1.f, 1.f, 1.f);
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].scale, 6.5f, 4.f, 1.f, 1.f, 8.5f,
|
||||
5.f, 1.f, 1.f, 10.5f, 6.f, 1.f, 1.f);
|
||||
|
||||
// Full weight for the 1st layer.
|
||||
layers[0].weight = 1.f;
|
||||
EXPECT_TRUE(job.Run());
|
||||
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].translation, 0.f, .5f, 0.f, 0.f,
|
||||
4.f, 2.5f, 0.f, 0.f, 8.f, 4.5f, 0.f, 0.f);
|
||||
EXPECT_SOAQUATERNION_EQ_EST(output_transforms[0].rotation, .70710677f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f,
|
||||
0.f, .70710677f, 1.f, 1.f, 1.f);
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].scale, 12.f, 7.f, 1.f, 1.f, 16.f,
|
||||
9.f, 1.f, 1.f, 20.f, 11.f, 1.f, 1.f);
|
||||
|
||||
// Subtract layer.
|
||||
layers[0].weight = -1.f;
|
||||
|
||||
EXPECT_TRUE(job.Run());
|
||||
|
||||
EXPECT_SOAFLOAT3_EQ(output_transforms[0].translation, -0.f, -.5f, 0.f, 0.f,
|
||||
-4.f, -2.5f, 0.f, 0.f, -8.f, -4.5f, 0.f, 0.f);
|
||||
EXPECT_SOAQUATERNION_EQ_EST(output_transforms[0].rotation, -.70710677f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f,
|
||||
0.f, .70710677f, 1.f, 1.f, 1.f);
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output_transforms[0].scale, 1.f / 12.f, 1.f / 7.f,
|
||||
1.f, 1.f, 1.f / 16.f, 1.f / 9.f, 1.f, 1.f,
|
||||
1.f / 20.f, 1.f / 11.f, 1.f, 1.f);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,626 @@
|
||||
//----------------------------------------------------------------------------//
|
||||
// //
|
||||
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
|
||||
// and distributed under the MIT License (MIT). //
|
||||
// //
|
||||
// Copyright (c) Guillaume Blanc //
|
||||
// //
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a //
|
||||
// copy of this software and associated documentation files (the "Software"), //
|
||||
// to deal in the Software without restriction, including without limitation //
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
|
||||
// and/or sell copies of the Software, and to permit persons to whom the //
|
||||
// Software is furnished to do so, subject to the following conditions: //
|
||||
// //
|
||||
// The above copyright notice and this permission notice shall be included in //
|
||||
// all copies or substantial portions of the Software. //
|
||||
// //
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
|
||||
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
|
||||
// DEALINGS IN THE SOFTWARE. //
|
||||
// //
|
||||
//----------------------------------------------------------------------------//
|
||||
|
||||
#include "ozz/animation/runtime/ik_aim_job.h"
|
||||
|
||||
#include "ozz/base/maths/quaternion.h"
|
||||
#include "ozz/base/maths/simd_math.h"
|
||||
#include "ozz/base/maths/simd_quaternion.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "ozz/base/maths/gtest_math_helper.h"
|
||||
|
||||
TEST(JobValidity, IKAimJob) {
|
||||
const ozz::math::Float4x4 joint = ozz::math::Float4x4::identity();
|
||||
ozz::math::SimdQuaternion quat;
|
||||
|
||||
{ // Default is invalid
|
||||
ozz::animation::IKAimJob job;
|
||||
EXPECT_FALSE(job.Validate());
|
||||
}
|
||||
|
||||
{ // Invalid joint matrix
|
||||
ozz::animation::IKAimJob job;
|
||||
job.joint = &joint;
|
||||
EXPECT_FALSE(job.Validate());
|
||||
}
|
||||
|
||||
{ // Invalid output
|
||||
ozz::animation::IKAimJob job;
|
||||
job.joint_correction = &quat;
|
||||
EXPECT_FALSE(job.Validate());
|
||||
}
|
||||
|
||||
{ // Invalid non normalized forward vector.
|
||||
ozz::animation::IKAimJob job;
|
||||
job.forward = ozz::math::simd_float4::Load(.5f, 0.f, 0.f, 0.f);
|
||||
EXPECT_FALSE(job.Validate());
|
||||
}
|
||||
|
||||
{ // Valid
|
||||
ozz::animation::IKAimJob job;
|
||||
job.joint = &joint;
|
||||
job.joint_correction = &quat;
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Correction, IKAimJob) {
|
||||
ozz::math::SimdQuaternion quat;
|
||||
|
||||
ozz::animation::IKAimJob job;
|
||||
job.joint_correction = &quat;
|
||||
|
||||
// Test will be executed with different root transformations
|
||||
const ozz::math::Float4x4 parents[] = {
|
||||
ozz::math::Float4x4::identity(), // No root transformation
|
||||
ozz::math::Float4x4::Translation(ozz::math::simd_float4::y_axis()), // Up
|
||||
ozz::math::Float4x4::FromEuler(ozz::math::simd_float4::Load(
|
||||
ozz::math::kPi / 3.f, 0.f, 0.f, 0.f)), // Rotated
|
||||
ozz::math::Float4x4::Scaling(ozz::math::simd_float4::Load(
|
||||
2.f, 2.f, 2.f, 0.f)), // Uniformly scaled
|
||||
ozz::math::Float4x4::Scaling(ozz::math::simd_float4::Load(
|
||||
1.f, 2.f, 1.f, 0.f)), // Non-uniformly scaled
|
||||
ozz::math::Float4x4::Scaling(
|
||||
ozz::math::simd_float4::Load(-3.f, -3.f, -3.f, 0.f)) // Mirrored
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < OZZ_ARRAY_SIZE(parents); ++i) {
|
||||
const ozz::math::Float4x4& parent = parents[i];
|
||||
job.joint = &parent;
|
||||
|
||||
// These are in joint local-space
|
||||
job.forward = ozz::math::simd_float4::x_axis();
|
||||
job.up = ozz::math::simd_float4::y_axis();
|
||||
|
||||
// Pole vector is in model space
|
||||
job.pole_vector = TransformVector(parent, ozz::math::simd_float4::y_axis());
|
||||
|
||||
{ // x
|
||||
job.target = TransformPoint(parent, ozz::math::simd_float4::x_axis());
|
||||
EXPECT_TRUE(job.Run());
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
|
||||
{ // -x
|
||||
job.target = TransformPoint(parent, -ozz::math::simd_float4::x_axis());
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion y_Pi = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::y_axis(), ozz::math::kPi);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, y_Pi.x, y_Pi.y, y_Pi.z, y_Pi.w, 2e-3f);
|
||||
}
|
||||
|
||||
{ // z
|
||||
job.target = TransformPoint(parent, ozz::math::simd_float4::z_axis());
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion y_mPi_2 =
|
||||
ozz::math::Quaternion::FromAxisAngle(ozz::math::Float3::y_axis(),
|
||||
-ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, y_mPi_2.x, y_mPi_2.y, y_mPi_2.z,
|
||||
y_mPi_2.w, 2e-3f);
|
||||
}
|
||||
|
||||
{ // -z
|
||||
job.target = TransformPoint(parent, -ozz::math::simd_float4::z_axis());
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion y_Pi_2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::y_axis(), ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, y_Pi_2.x, y_Pi_2.y, y_Pi_2.z, y_Pi_2.w,
|
||||
2e-3f);
|
||||
}
|
||||
|
||||
{ // 45 up y
|
||||
job.target = TransformPoint(
|
||||
parent, ozz::math::simd_float4::Load(1.f, 1.f, 0.f, 0.f));
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion z_Pi_4 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::z_axis(), ozz::math::kPi_4);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, z_Pi_4.x, z_Pi_4.y, z_Pi_4.z, z_Pi_4.w,
|
||||
2e-3f);
|
||||
}
|
||||
|
||||
{ // 45 up y, further
|
||||
job.target = TransformPoint(
|
||||
parent, ozz::math::simd_float4::Load(2.f, 2.f, 0.f, 0.f));
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion z_Pi_4 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::z_axis(), ozz::math::kPi_4);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, z_Pi_4.x, z_Pi_4.y, z_Pi_4.z, z_Pi_4.w,
|
||||
2e-3f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Forward, IKAimJob) {
|
||||
ozz::animation::IKAimJob job;
|
||||
ozz::math::SimdQuaternion quat;
|
||||
job.joint_correction = &quat;
|
||||
const ozz::math::Float4x4 joint = ozz::math::Float4x4::identity();
|
||||
job.joint = &joint;
|
||||
|
||||
job.target = ozz::math::simd_float4::x_axis();
|
||||
job.up = ozz::math::simd_float4::y_axis();
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
|
||||
{ // forward x
|
||||
job.forward = ozz::math::simd_float4::x_axis();
|
||||
EXPECT_TRUE(job.Run());
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
|
||||
{ // forward -x
|
||||
job.forward = -ozz::math::simd_float4::x_axis();
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion y_Pi = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::y_axis(), -ozz::math::kPi);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, y_Pi.x, y_Pi.y, y_Pi.z, y_Pi.w, 2e-3f);
|
||||
}
|
||||
|
||||
{ // forward z
|
||||
job.forward = ozz::math::simd_float4::z_axis();
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion y_Pi_2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::y_axis(), ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, y_Pi_2.x, y_Pi_2.y, y_Pi_2.z, y_Pi_2.w,
|
||||
2e-3f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Up, IKAimJob) {
|
||||
ozz::animation::IKAimJob job;
|
||||
ozz::math::SimdQuaternion quat;
|
||||
job.joint_correction = &quat;
|
||||
const ozz::math::Float4x4 joint = ozz::math::Float4x4::identity();
|
||||
job.joint = &joint;
|
||||
|
||||
job.target = ozz::math::simd_float4::x_axis();
|
||||
job.forward = ozz::math::simd_float4::x_axis();
|
||||
job.up = ozz::math::simd_float4::y_axis();
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
|
||||
{ // up y
|
||||
job.up = ozz::math::simd_float4::y_axis();
|
||||
EXPECT_TRUE(job.Run());
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
|
||||
{ // up -y
|
||||
job.up = -ozz::math::simd_float4::y_axis();
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion x_Pi = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::x_axis(), ozz::math::kPi);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, x_Pi.x, x_Pi.y, x_Pi.z, x_Pi.w, 2e-3f);
|
||||
}
|
||||
|
||||
{ // up z
|
||||
job.up = ozz::math::simd_float4::z_axis();
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion x_mPi_2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::x_axis(), -ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, x_mPi_2.x, x_mPi_2.y, x_mPi_2.z,
|
||||
x_mPi_2.w, 2e-3f);
|
||||
}
|
||||
|
||||
{ // up 2*z
|
||||
job.up =
|
||||
ozz::math::simd_float4::z_axis() * ozz::math::simd_float4::Load1(2.f);
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion x_mPi_2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::x_axis(), -ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, x_mPi_2.x, x_mPi_2.y, x_mPi_2.z,
|
||||
x_mPi_2.w, 2e-3f);
|
||||
}
|
||||
|
||||
{ // up very small z
|
||||
job.up =
|
||||
ozz::math::simd_float4::z_axis() * ozz::math::simd_float4::Load1(1e-9f);
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion x_mPi_2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::x_axis(), -ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, x_mPi_2.x, x_mPi_2.y, x_mPi_2.z,
|
||||
x_mPi_2.w, 2e-3f);
|
||||
}
|
||||
|
||||
{ // up is zero
|
||||
job.up = ozz::math::simd_float4::zero();
|
||||
EXPECT_TRUE(job.Run());
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Pole, IKAimJob) {
|
||||
ozz::animation::IKAimJob job;
|
||||
ozz::math::SimdQuaternion quat;
|
||||
job.joint_correction = &quat;
|
||||
const ozz::math::Float4x4 joint = ozz::math::Float4x4::identity();
|
||||
job.joint = &joint;
|
||||
|
||||
job.target = ozz::math::simd_float4::x_axis();
|
||||
job.forward = ozz::math::simd_float4::x_axis();
|
||||
job.up = ozz::math::simd_float4::y_axis();
|
||||
|
||||
{ // Pole y
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
EXPECT_TRUE(job.Run());
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
|
||||
{ // Pole -y
|
||||
job.pole_vector = -ozz::math::simd_float4::y_axis();
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion x_Pi = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::x_axis(), ozz::math::kPi);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, x_Pi.x, x_Pi.y, x_Pi.z, x_Pi.w, 2e-3f);
|
||||
}
|
||||
|
||||
{ // Pole z
|
||||
job.pole_vector = ozz::math::simd_float4::z_axis();
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion x_Pi_2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::x_axis(), ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, x_Pi_2.x, x_Pi_2.y, x_Pi_2.z, x_Pi_2.w,
|
||||
2e-3f);
|
||||
}
|
||||
|
||||
{ // Pole 2*z
|
||||
job.pole_vector =
|
||||
ozz::math::simd_float4::z_axis() * ozz::math::simd_float4::Load1(2.f);
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion x_Pi_2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::x_axis(), ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, x_Pi_2.x, x_Pi_2.y, x_Pi_2.z, x_Pi_2.w,
|
||||
2e-3f);
|
||||
}
|
||||
|
||||
{ // Pole very small z
|
||||
job.pole_vector =
|
||||
ozz::math::simd_float4::z_axis() * ozz::math::simd_float4::Load1(1e-9f);
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion x_Pi_2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::x_axis(), ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, x_Pi_2.x, x_Pi_2.y, x_Pi_2.z, x_Pi_2.w,
|
||||
2e-3f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Offset, IKAimJob) {
|
||||
ozz::animation::IKAimJob job;
|
||||
ozz::math::SimdQuaternion quat;
|
||||
job.joint_correction = &quat;
|
||||
const ozz::math::Float4x4 joint = ozz::math::Float4x4::identity();
|
||||
job.joint = &joint;
|
||||
bool reached;
|
||||
job.reached = &reached;
|
||||
|
||||
job.target = ozz::math::simd_float4::x_axis();
|
||||
job.forward = ozz::math::simd_float4::x_axis();
|
||||
job.up = ozz::math::simd_float4::y_axis();
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
|
||||
{ // No offset
|
||||
reached = false;
|
||||
job.offset = ozz::math::simd_float4::zero();
|
||||
EXPECT_TRUE(job.Run());
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
EXPECT_TRUE(reached);
|
||||
}
|
||||
|
||||
{ // Offset inside target sphere
|
||||
reached = false;
|
||||
job.offset =
|
||||
ozz::math::simd_float4::Load(0.f, ozz::math::kSqrt2_2, 0.f, 0.f);
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion z_Pi_4 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::z_axis(), -ozz::math::kPi_4);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, z_Pi_4.x, z_Pi_4.y, z_Pi_4.z, z_Pi_4.w,
|
||||
2e-3f);
|
||||
EXPECT_TRUE(reached);
|
||||
}
|
||||
|
||||
{ // Offset inside target sphere
|
||||
reached = false;
|
||||
job.offset = ozz::math::simd_float4::Load(.5f, .5f, 0.f, 0.f);
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion z_Pi_6 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::z_axis(), -ozz::math::kPi / 6.f);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, z_Pi_6.x, z_Pi_6.y, z_Pi_6.z, z_Pi_6.w,
|
||||
2e-3f);
|
||||
EXPECT_TRUE(reached);
|
||||
}
|
||||
|
||||
{ // Offset inside target sphere
|
||||
reached = false;
|
||||
job.offset = ozz::math::simd_float4::Load(-.5f, .5f, 0.f, 0.f);
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion z_Pi_6 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::z_axis(), -ozz::math::kPi / 6.f);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, z_Pi_6.x, z_Pi_6.y, z_Pi_6.z, z_Pi_6.w,
|
||||
2e-3f);
|
||||
EXPECT_TRUE(reached);
|
||||
}
|
||||
|
||||
{ // Offset inside target sphere
|
||||
reached = false;
|
||||
job.offset = ozz::math::simd_float4::Load(.5f, 0.f, .5f, 0.f);
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion y_Pi_6 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::y_axis(), ozz::math::kPi / 6.f);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, y_Pi_6.x, y_Pi_6.y, y_Pi_6.z, y_Pi_6.w,
|
||||
2e-3f);
|
||||
EXPECT_TRUE(reached);
|
||||
}
|
||||
|
||||
{ // Offset on target sphere
|
||||
reached = false;
|
||||
job.offset = ozz::math::simd_float4::Load(0.f, 1.f, 0.f, 0.f);
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion z_Pi_2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::z_axis(), -ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, z_Pi_2.x, z_Pi_2.y, z_Pi_2.z, z_Pi_2.w,
|
||||
2e-3f);
|
||||
EXPECT_TRUE(reached);
|
||||
}
|
||||
|
||||
{ // Offset outside of target sphere, unreachable
|
||||
reached = true;
|
||||
job.offset = ozz::math::simd_float4::Load(0.f, 2.f, 0.f, 0.f);
|
||||
EXPECT_TRUE(job.Run());
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
EXPECT_FALSE(reached);
|
||||
}
|
||||
|
||||
const ozz::math::Float4x4 translated_joint =
|
||||
ozz::math::Float4x4::Translation(ozz::math::simd_float4::y_axis());
|
||||
job.joint = &translated_joint;
|
||||
|
||||
{ // Offset inside of target sphere, unreachable
|
||||
reached = false;
|
||||
job.offset = ozz::math::simd_float4::y_axis();
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion z_Pi_2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::z_axis(), -ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, z_Pi_2.x, z_Pi_2.y, z_Pi_2.z, z_Pi_2.w,
|
||||
2e-3f);
|
||||
EXPECT_TRUE(reached);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Twist, IKAimJob) {
|
||||
ozz::animation::IKAimJob job;
|
||||
ozz::math::SimdQuaternion quat;
|
||||
job.joint_correction = &quat;
|
||||
const ozz::math::Float4x4 joint = ozz::math::Float4x4::identity();
|
||||
job.joint = &joint;
|
||||
|
||||
job.target = ozz::math::simd_float4::x_axis();
|
||||
job.forward = ozz::math::simd_float4::x_axis();
|
||||
job.up = ozz::math::simd_float4::y_axis();
|
||||
|
||||
{ // Pole y, twist 0
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
job.twist_angle = 0.f;
|
||||
EXPECT_TRUE(job.Run());
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
|
||||
{ // Pole y, twist pi
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
job.twist_angle = ozz::math::kPi;
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion x_Pi = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::x_axis(), -ozz::math::kPi);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, x_Pi.x, x_Pi.y, x_Pi.z, x_Pi.w, 2e-3f);
|
||||
}
|
||||
|
||||
{ // Pole y, twist -pi
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
job.twist_angle = -ozz::math::kPi;
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion x_mPi = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::x_axis(), -ozz::math::kPi);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, x_mPi.x, x_mPi.y, x_mPi.z, x_mPi.w,
|
||||
2e-3f);
|
||||
}
|
||||
|
||||
{ // Pole y, twist pi/2
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
job.twist_angle = ozz::math::kPi_2;
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion x_Pi_2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::x_axis(), ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, x_Pi_2.x, x_Pi_2.y, x_Pi_2.z, x_Pi_2.w,
|
||||
2e-3f);
|
||||
}
|
||||
|
||||
{ // Pole z, twist pi/2
|
||||
job.pole_vector = ozz::math::simd_float4::z_axis();
|
||||
job.twist_angle = ozz::math::kPi_2;
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion x_Pi = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::x_axis(), ozz::math::kPi);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, x_Pi.x, x_Pi.y, x_Pi.z, x_Pi.w, 2e-3f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(AlignedTargetUp, IKAimJob) {
|
||||
ozz::animation::IKAimJob job;
|
||||
ozz::math::SimdQuaternion quat;
|
||||
job.joint_correction = &quat;
|
||||
const ozz::math::Float4x4 joint = ozz::math::Float4x4::identity();
|
||||
job.joint = &joint;
|
||||
|
||||
job.forward = ozz::math::simd_float4::x_axis();
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
|
||||
{ // Not aligned
|
||||
job.target = ozz::math::simd_float4::x_axis();
|
||||
job.up = ozz::math::simd_float4::y_axis();
|
||||
EXPECT_TRUE(job.Run());
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
|
||||
{ // Aligned y
|
||||
job.target = ozz::math::simd_float4::y_axis();
|
||||
job.up = ozz::math::simd_float4::y_axis();
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion z_Pi_2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::z_axis(), ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, z_Pi_2.x, z_Pi_2.y, z_Pi_2.z, z_Pi_2.w,
|
||||
2e-3f);
|
||||
}
|
||||
|
||||
{ // Aligned 2*y
|
||||
job.target =
|
||||
ozz::math::simd_float4::y_axis() * ozz::math::simd_float4::Load1(2.f);
|
||||
job.up = ozz::math::simd_float4::y_axis();
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion z_Pi_2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::z_axis(), ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, z_Pi_2.x, z_Pi_2.y, z_Pi_2.z, z_Pi_2.w,
|
||||
2e-3f);
|
||||
}
|
||||
|
||||
{ // Aligned -2*y
|
||||
job.target =
|
||||
ozz::math::simd_float4::y_axis() * ozz::math::simd_float4::Load1(-2.f);
|
||||
job.up = ozz::math::simd_float4::y_axis();
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion z_mPi_2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::z_axis(), -ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, z_mPi_2.x, z_mPi_2.y, z_mPi_2.z,
|
||||
z_mPi_2.w, 2e-3f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(AlignedTargetPole, IKAimJob) {
|
||||
ozz::animation::IKAimJob job;
|
||||
ozz::math::SimdQuaternion quat;
|
||||
job.joint_correction = &quat;
|
||||
const ozz::math::Float4x4 joint = ozz::math::Float4x4::identity();
|
||||
job.joint = &joint;
|
||||
|
||||
job.forward = ozz::math::simd_float4::x_axis();
|
||||
job.up = ozz::math::simd_float4::y_axis();
|
||||
|
||||
{ // Not aligned
|
||||
job.target = ozz::math::simd_float4::x_axis();
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
EXPECT_TRUE(job.Run());
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
|
||||
{ // Aligned y
|
||||
job.target = ozz::math::simd_float4::y_axis();
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion z_Pi_2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::z_axis(), ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, z_Pi_2.x, z_Pi_2.y, z_Pi_2.z, z_Pi_2.w,
|
||||
2e-3f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TargetTooClose, IKAimJob) {
|
||||
ozz::animation::IKAimJob job;
|
||||
ozz::math::SimdQuaternion quat;
|
||||
job.joint_correction = &quat;
|
||||
const ozz::math::Float4x4 joint = ozz::math::Float4x4::identity();
|
||||
job.joint = &joint;
|
||||
|
||||
job.target = ozz::math::simd_float4::zero();
|
||||
job.forward = ozz::math::simd_float4::x_axis();
|
||||
job.up = ozz::math::simd_float4::y_axis();
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
|
||||
EXPECT_TRUE(job.Run());
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
|
||||
TEST(Weight, IKAimJob) {
|
||||
ozz::animation::IKAimJob job;
|
||||
ozz::math::SimdQuaternion quat;
|
||||
job.joint_correction = &quat;
|
||||
const ozz::math::Float4x4 joint = ozz::math::Float4x4::identity();
|
||||
job.joint = &joint;
|
||||
|
||||
job.target = ozz::math::simd_float4::z_axis();
|
||||
job.forward = ozz::math::simd_float4::x_axis();
|
||||
job.up = ozz::math::simd_float4::y_axis();
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
|
||||
{ // Full weight
|
||||
job.weight = 1.f;
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion y_mPi2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::y_axis(), -ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, y_mPi2.x, y_mPi2.y, y_mPi2.z, y_mPi2.w,
|
||||
2e-3f);
|
||||
}
|
||||
|
||||
{ // > 1
|
||||
job.weight = 2.f;
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion y_mPi2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::y_axis(), -ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, y_mPi2.x, y_mPi2.y, y_mPi2.z, y_mPi2.w,
|
||||
2e-3f);
|
||||
}
|
||||
|
||||
{ // Half weight
|
||||
job.weight = .5f;
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion y_mPi4 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::y_axis(), -ozz::math::kPi_4);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, y_mPi4.x, y_mPi4.y, y_mPi4.z, y_mPi4.w,
|
||||
2e-3f);
|
||||
}
|
||||
|
||||
{ // Zero weight
|
||||
job.weight = 0.f;
|
||||
EXPECT_TRUE(job.Run());
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
|
||||
{ // < 0
|
||||
job.weight = -.5f;
|
||||
EXPECT_TRUE(job.Run());
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ZeroScale, IKAimJob) {
|
||||
ozz::animation::IKAimJob job;
|
||||
ozz::math::SimdQuaternion quat;
|
||||
job.joint_correction = &quat;
|
||||
const ozz::math::Float4x4 joint =
|
||||
ozz::math::Float4x4::Scaling(ozz::math::simd_float4::zero());
|
||||
job.joint = &joint;
|
||||
|
||||
EXPECT_TRUE(job.Run());
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
@@ -0,0 +1,976 @@
|
||||
//----------------------------------------------------------------------------//
|
||||
// //
|
||||
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
|
||||
// and distributed under the MIT License (MIT). //
|
||||
// //
|
||||
// Copyright (c) Guillaume Blanc //
|
||||
// //
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a //
|
||||
// copy of this software and associated documentation files (the "Software"), //
|
||||
// to deal in the Software without restriction, including without limitation //
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
|
||||
// and/or sell copies of the Software, and to permit persons to whom the //
|
||||
// Software is furnished to do so, subject to the following conditions: //
|
||||
// //
|
||||
// The above copyright notice and this permission notice shall be included in //
|
||||
// all copies or substantial portions of the Software. //
|
||||
// //
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
|
||||
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
|
||||
// DEALINGS IN THE SOFTWARE. //
|
||||
// //
|
||||
//----------------------------------------------------------------------------//
|
||||
|
||||
#include "ozz/animation/runtime/ik_two_bone_job.h"
|
||||
|
||||
#include "ozz/base/maths/quaternion.h"
|
||||
#include "ozz/base/maths/simd_math.h"
|
||||
#include "ozz/base/maths/simd_quaternion.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "ozz/base/maths/gtest_math_helper.h"
|
||||
|
||||
// Implement helper macro that verify that target was reached once ik job is
|
||||
// executed.
|
||||
#define EXPECT_REACHED(_job) \
|
||||
\
|
||||
do { \
|
||||
SCOPED_TRACE(""); \
|
||||
_ExpectReached(_job, true); \
|
||||
} while (void(0), 0)
|
||||
|
||||
#define EXPECT_NOT_REACHED(_job) \
|
||||
\
|
||||
do { \
|
||||
SCOPED_TRACE(""); \
|
||||
_ExpectReached(_job, false); \
|
||||
} while (void(0), 0)
|
||||
|
||||
void _ExpectReached(const ozz::animation::IKTwoBoneJob& _job, bool _reachable) {
|
||||
// Computes local transforms
|
||||
const ozz::math::Float4x4 mid_local =
|
||||
Invert(*_job.start_joint) * *_job.mid_joint;
|
||||
const ozz::math::Float4x4 end_local =
|
||||
Invert(*_job.mid_joint) * *_job.end_joint;
|
||||
|
||||
// Rebuild corrected model transforms
|
||||
const ozz::math::Float4x4 start_correction =
|
||||
ozz::math::Float4x4::FromQuaternion(_job.start_joint_correction->xyzw);
|
||||
const ozz::math::Float4x4 start_corrected =
|
||||
*_job.start_joint * start_correction;
|
||||
const ozz::math::Float4x4 mid_correction =
|
||||
ozz::math::Float4x4::FromQuaternion(_job.mid_joint_correction->xyzw);
|
||||
const ozz::math::Float4x4 mid_corrected =
|
||||
start_corrected * mid_local * mid_correction;
|
||||
const ozz::math::Float4x4 end_corrected = mid_corrected * end_local;
|
||||
|
||||
const ozz::math::SimdFloat4 diff =
|
||||
ozz::math::Length3(end_corrected.cols[3] - _job.target);
|
||||
EXPECT_EQ(ozz::math::GetX(diff) < 1e-2f, _reachable);
|
||||
|
||||
EXPECT_TRUE(_job.reached == nullptr || *_job.reached == _reachable);
|
||||
}
|
||||
|
||||
TEST(JobValidity, IKTwoBoneJob) {
|
||||
// Setup initial pose
|
||||
const ozz::math::Float4x4 start = ozz::math::Float4x4::identity();
|
||||
const ozz::math::Float4x4 mid = ozz::math::Float4x4::FromAffine(
|
||||
ozz::math::simd_float4::y_axis(),
|
||||
ozz::math::SimdQuaternion::FromAxisAngle(
|
||||
ozz::math::simd_float4::z_axis(),
|
||||
ozz::math::simd_float4::Load1(ozz::math::kPi_2))
|
||||
.xyzw,
|
||||
ozz::math::simd_float4::one());
|
||||
const ozz::math::Float4x4 end = ozz::math::Float4x4::Translation(
|
||||
ozz::math::simd_float4::x_axis() + ozz::math::simd_float4::y_axis());
|
||||
|
||||
{ // Default is invalid
|
||||
ozz::animation::IKTwoBoneJob job;
|
||||
EXPECT_FALSE(job.Validate());
|
||||
}
|
||||
|
||||
{ // Missing start joint matrix
|
||||
ozz::animation::IKTwoBoneJob job;
|
||||
job.mid_joint = ∣
|
||||
job.end_joint = &end;
|
||||
ozz::math::SimdQuaternion quat;
|
||||
job.start_joint_correction = &quat;
|
||||
job.mid_joint_correction = &quat;
|
||||
EXPECT_FALSE(job.Validate());
|
||||
}
|
||||
|
||||
{ // Missing mid joint matrix
|
||||
ozz::animation::IKTwoBoneJob job;
|
||||
job.start_joint = &start;
|
||||
job.end_joint = &end;
|
||||
ozz::math::SimdQuaternion quat;
|
||||
job.start_joint_correction = &quat;
|
||||
job.mid_joint_correction = &quat;
|
||||
EXPECT_FALSE(job.Validate());
|
||||
}
|
||||
|
||||
{ // Missing end joint matrix
|
||||
ozz::animation::IKTwoBoneJob job;
|
||||
job.start_joint = &start;
|
||||
job.mid_joint = ∣
|
||||
ozz::math::SimdQuaternion quat;
|
||||
job.start_joint_correction = &quat;
|
||||
job.mid_joint_correction = &quat;
|
||||
EXPECT_FALSE(job.Validate());
|
||||
}
|
||||
|
||||
{ // Missing start joint output quaternion
|
||||
ozz::animation::IKTwoBoneJob job;
|
||||
job.start_joint = &start;
|
||||
job.mid_joint = ∣
|
||||
job.end_joint = &end;
|
||||
ozz::math::SimdQuaternion quat;
|
||||
job.mid_joint_correction = &quat;
|
||||
EXPECT_FALSE(job.Validate());
|
||||
}
|
||||
|
||||
{ // Missing mid joint output quaternion
|
||||
ozz::animation::IKTwoBoneJob job;
|
||||
job.start_joint = &start;
|
||||
job.mid_joint = ∣
|
||||
job.end_joint = &end;
|
||||
ozz::math::SimdQuaternion quat;
|
||||
job.start_joint_correction = &quat;
|
||||
EXPECT_FALSE(job.Validate());
|
||||
}
|
||||
|
||||
{ // Unnormalized mid axis
|
||||
ozz::animation::IKTwoBoneJob job;
|
||||
job.mid_axis =
|
||||
ozz::math::simd_float4::Load(0.f, .70710678f, 0.f, .70710678f);
|
||||
job.start_joint = &start;
|
||||
job.mid_joint = ∣
|
||||
job.end_joint = &end;
|
||||
ozz::math::SimdQuaternion quat;
|
||||
job.start_joint_correction = &quat;
|
||||
job.mid_joint_correction = &quat;
|
||||
EXPECT_FALSE(job.Validate());
|
||||
}
|
||||
|
||||
{ // Valid
|
||||
ozz::animation::IKTwoBoneJob job;
|
||||
job.start_joint = &start;
|
||||
job.mid_joint = ∣
|
||||
job.end_joint = &end;
|
||||
ozz::math::SimdQuaternion quat;
|
||||
job.start_joint_correction = &quat;
|
||||
job.mid_joint_correction = &quat;
|
||||
EXPECT_TRUE(job.Validate());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(StartJointCorrection, IKTwoBoneJob) {
|
||||
// Setup initial pose
|
||||
const ozz::math::Float4x4 base_start = ozz::math::Float4x4::identity();
|
||||
const ozz::math::Float4x4 base_mid = ozz::math::Float4x4::FromAffine(
|
||||
ozz::math::simd_float4::y_axis(),
|
||||
ozz::math::SimdQuaternion::FromAxisAngle(
|
||||
ozz::math::simd_float4::z_axis(),
|
||||
ozz::math::simd_float4::Load1(ozz::math::kPi_2))
|
||||
.xyzw,
|
||||
ozz::math::simd_float4::one());
|
||||
const ozz::math::Float4x4 base_end = ozz::math::Float4x4::Translation(
|
||||
ozz::math::simd_float4::x_axis() + ozz::math::simd_float4::y_axis());
|
||||
|
||||
const ozz::math::SimdFloat4 mid_axis =
|
||||
ozz::math::Cross3(base_start.cols[3] - base_mid.cols[3],
|
||||
base_end.cols[3] - base_mid.cols[3]);
|
||||
|
||||
// Test will be executed with different root transformations
|
||||
const ozz::math::Float4x4 parents[] = {
|
||||
ozz::math::Float4x4::identity(), // No root transformation
|
||||
ozz::math::Float4x4::Translation(ozz::math::simd_float4::y_axis()), // Up
|
||||
ozz::math::Float4x4::FromEuler(ozz::math::simd_float4::Load(
|
||||
ozz::math::kPi / 3.f, 0.f, 0.f, 0.f)), // Rotated
|
||||
ozz::math::Float4x4::Scaling(ozz::math::simd_float4::Load(
|
||||
2.f, 2.f, 2.f, 0.f)), // Uniformly scaled
|
||||
ozz::math::Float4x4::Scaling(ozz::math::simd_float4::Load(
|
||||
1.f, 2.f, 1.f, 0.f)), // Non-uniformly scaled
|
||||
ozz::math::Float4x4::Scaling(
|
||||
ozz::math::simd_float4::Load(-3.f, -3.f, -3.f, 0.f)) // Mirrored
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < OZZ_ARRAY_SIZE(parents); ++i) {
|
||||
const ozz::math::Float4x4& parent = parents[i];
|
||||
const ozz::math::Float4x4 start = parent * base_start;
|
||||
const ozz::math::Float4x4 mid = parent * base_mid;
|
||||
const ozz::math::Float4x4 end = parent * base_end;
|
||||
|
||||
// Prepares job.
|
||||
ozz::animation::IKTwoBoneJob job;
|
||||
job.pole_vector = TransformVector(parent, ozz::math::simd_float4::y_axis());
|
||||
job.mid_axis = mid_axis;
|
||||
job.start_joint = &start;
|
||||
job.mid_joint = ∣
|
||||
job.end_joint = &end;
|
||||
ozz::math::SimdQuaternion qstart;
|
||||
job.start_joint_correction = &qstart;
|
||||
ozz::math::SimdQuaternion qmid;
|
||||
job.mid_joint_correction = &qmid;
|
||||
bool reached;
|
||||
job.reached = &reached;
|
||||
ASSERT_TRUE(job.Validate());
|
||||
|
||||
{ // No correction expected
|
||||
job.target = TransformPoint(
|
||||
parent, ozz::math::simd_float4::Load(1.f, 1.f, 0.f, 0.f));
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_REACHED(job);
|
||||
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qstart, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qmid, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
|
||||
{ // 90
|
||||
job.target = TransformPoint(
|
||||
parent, ozz::math::simd_float4::Load(0.f, 1.f, 1.f, 0.f));
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_REACHED(job);
|
||||
|
||||
const ozz::math::Quaternion y_mPi_2 =
|
||||
ozz::math::Quaternion::FromAxisAngle(ozz::math::Float3::y_axis(),
|
||||
-ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qstart, y_mPi_2.x, y_mPi_2.y, y_mPi_2.z,
|
||||
y_mPi_2.w, 2e-3f);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qmid, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
|
||||
{ // 180 behind
|
||||
job.target = TransformPoint(
|
||||
parent, ozz::math::simd_float4::Load(-1.f, 1.f, 0.f, 0.f));
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_REACHED(job);
|
||||
|
||||
const ozz::math::Quaternion y_kPi = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::y_axis(), ozz::math::kPi);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qstart, y_kPi.x, y_kPi.y, y_kPi.z, y_kPi.w,
|
||||
2e-3f);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qmid, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
|
||||
{ // 270
|
||||
job.target = TransformPoint(
|
||||
parent, ozz::math::simd_float4::Load(0.f, 1.f, -1.f, 0.f));
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_REACHED(job);
|
||||
|
||||
const ozz::math::Quaternion y_kPi_2 =
|
||||
ozz::math::Quaternion::FromAxisAngle(ozz::math::Float3::y_axis(),
|
||||
ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qstart, y_kPi_2.x, y_kPi_2.y, y_kPi_2.z,
|
||||
y_kPi_2.w, 2e-3f);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qmid, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Pole, IKTwoBoneJob) {
|
||||
// Setup initial pose
|
||||
const ozz::math::Float4x4 start = ozz::math::Float4x4::identity();
|
||||
const ozz::math::Float4x4 mid = ozz::math::Float4x4::FromAffine(
|
||||
ozz::math::simd_float4::y_axis(),
|
||||
ozz::math::SimdQuaternion::FromAxisAngle(
|
||||
ozz::math::simd_float4::z_axis(),
|
||||
ozz::math::simd_float4::Load1(ozz::math::kPi_2))
|
||||
.xyzw,
|
||||
ozz::math::simd_float4::one());
|
||||
const ozz::math::Float4x4 end = ozz::math::Float4x4::Translation(
|
||||
ozz::math::simd_float4::x_axis() + ozz::math::simd_float4::y_axis());
|
||||
|
||||
const ozz::math::SimdFloat4 mid_axis =
|
||||
ozz::math::Cross3(start.cols[3] - mid.cols[3], end.cols[3] - mid.cols[3]);
|
||||
|
||||
// Prepares job.
|
||||
ozz::animation::IKTwoBoneJob job;
|
||||
job.start_joint = &start;
|
||||
job.mid_joint = ∣
|
||||
job.end_joint = &end;
|
||||
job.mid_axis = mid_axis;
|
||||
ozz::math::SimdQuaternion qstart;
|
||||
job.start_joint_correction = &qstart;
|
||||
ozz::math::SimdQuaternion qmid;
|
||||
job.mid_joint_correction = &qmid;
|
||||
bool reached;
|
||||
job.reached = &reached;
|
||||
ASSERT_TRUE(job.Validate());
|
||||
|
||||
// Pole Y
|
||||
{
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
job.target = ozz::math::simd_float4::Load(1.f, 1.f, 0.f, 0.f);
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_REACHED(job);
|
||||
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qstart, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qmid, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
|
||||
// Pole Z
|
||||
{
|
||||
job.pole_vector = ozz::math::simd_float4::z_axis();
|
||||
job.target = ozz::math::simd_float4::Load(1.f, 0.f, 1.f, 0.f);
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_REACHED(job);
|
||||
|
||||
const ozz::math::Quaternion x_kPi_2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::x_axis(), ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qstart, x_kPi_2.x, x_kPi_2.y, x_kPi_2.z,
|
||||
x_kPi_2.w, 2e-3f);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qmid, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
|
||||
// Pole -Z
|
||||
{
|
||||
job.pole_vector = -ozz::math::simd_float4::z_axis();
|
||||
job.target = ozz::math::simd_float4::Load(1.f, 0.f, -1.f, 0.f);
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_REACHED(job);
|
||||
|
||||
const ozz::math::Quaternion x_mPi_2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::x_axis(), -ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qstart, x_mPi_2.x, x_mPi_2.y, x_mPi_2.z,
|
||||
x_mPi_2.w, 2e-3f);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qmid, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
|
||||
// Pole X
|
||||
{
|
||||
job.pole_vector = ozz::math::simd_float4::x_axis();
|
||||
job.target = ozz::math::simd_float4::Load(1.f, -1.f, 0.f, 0.f);
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_REACHED(job);
|
||||
|
||||
const ozz::math::Quaternion z_mPi_2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::z_axis(), -ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qstart, z_mPi_2.x, z_mPi_2.y, z_mPi_2.z,
|
||||
z_mPi_2.w, 2e-3f);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qmid, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
|
||||
// Pole -X
|
||||
{
|
||||
job.pole_vector = -ozz::math::simd_float4::x_axis();
|
||||
job.target = ozz::math::simd_float4::Load(-1.f, 1.f, 0.f, 0.f);
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_REACHED(job);
|
||||
|
||||
const ozz::math::Quaternion z_Pi_2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::z_axis(), ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qstart, z_Pi_2.x, z_Pi_2.y, z_Pi_2.z, z_Pi_2.w,
|
||||
2e-3f);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qmid, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ZeroScale, IKTwoBoneJob) {
|
||||
// Setup initial pose
|
||||
const ozz::math::Float4x4 start =
|
||||
ozz::math::Float4x4::Scaling(ozz::math::simd_float4::zero());
|
||||
const ozz::math::Float4x4 mid = start;
|
||||
const ozz::math::Float4x4 end = start;
|
||||
|
||||
// Prepares job.
|
||||
ozz::animation::IKTwoBoneJob job;
|
||||
job.start_joint = &start;
|
||||
job.mid_joint = ∣
|
||||
job.end_joint = &end;
|
||||
ozz::math::SimdQuaternion qstart;
|
||||
job.start_joint_correction = &qstart;
|
||||
ozz::math::SimdQuaternion qmid;
|
||||
job.mid_joint_correction = &qmid;
|
||||
ASSERT_TRUE(job.Validate());
|
||||
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qstart, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qmid, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
|
||||
TEST(Soften, IKTwoBoneJob) {
|
||||
// Setup initial pose
|
||||
const ozz::math::Float4x4 start = ozz::math::Float4x4::identity();
|
||||
const ozz::math::Float4x4 mid = ozz::math::Float4x4::FromAffine(
|
||||
ozz::math::simd_float4::y_axis(),
|
||||
ozz::math::SimdQuaternion::FromAxisAngle(
|
||||
ozz::math::simd_float4::z_axis(),
|
||||
ozz::math::simd_float4::Load1(ozz::math::kPi_2))
|
||||
.xyzw,
|
||||
ozz::math::simd_float4::one());
|
||||
const ozz::math::Float4x4 end = ozz::math::Float4x4::Translation(
|
||||
ozz::math::simd_float4::x_axis() + ozz::math::simd_float4::y_axis());
|
||||
const ozz::math::SimdFloat4 mid_axis =
|
||||
ozz::math::Cross3(start.cols[3] - mid.cols[3], end.cols[3] - mid.cols[3]);
|
||||
|
||||
// Prepares job.
|
||||
ozz::animation::IKTwoBoneJob job;
|
||||
job.start_joint = &start;
|
||||
job.mid_joint = ∣
|
||||
job.end_joint = &end;
|
||||
job.mid_axis = mid_axis;
|
||||
ozz::math::SimdQuaternion qstart;
|
||||
job.start_joint_correction = &qstart;
|
||||
ozz::math::SimdQuaternion qmid;
|
||||
job.mid_joint_correction = &qmid;
|
||||
bool reached;
|
||||
job.reached = &reached;
|
||||
ASSERT_TRUE(job.Validate());
|
||||
|
||||
// Reachable
|
||||
{
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
job.target = ozz::math::simd_float4::Load(2.f, 0.f, 0.f, 0.f);
|
||||
job.soften = 1.f;
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_REACHED(job);
|
||||
|
||||
const ozz::math::Quaternion z_mPi_2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::z_axis(), -ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qstart, z_mPi_2.x, z_mPi_2.y, z_mPi_2.z,
|
||||
z_mPi_2.w, 2e-3f);
|
||||
const ozz::math::Quaternion z_Pi_2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::z_axis(), ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qmid, z_Pi_2.x, z_Pi_2.y, z_Pi_2.z, z_Pi_2.w,
|
||||
2e-3f);
|
||||
}
|
||||
|
||||
// Reachable, softened
|
||||
{
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
job.target = ozz::math::simd_float4::Load(2.f * .5f, 0.f, 0.f, 0.f);
|
||||
job.soften = .5f;
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_REACHED(job);
|
||||
}
|
||||
|
||||
// Reachable, softened
|
||||
{
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
job.target = ozz::math::simd_float4::Load(2.f * .4f, 0.f, 0.f, 0.f);
|
||||
job.soften = .5f;
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_REACHED(job);
|
||||
}
|
||||
|
||||
// Not reachable, softened
|
||||
{
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
job.target = ozz::math::simd_float4::Load(2.f * .6f, 0.f, 0.f, 0.f);
|
||||
job.soften = .5f;
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_NOT_REACHED(job);
|
||||
}
|
||||
|
||||
// Not reachable, softened at max
|
||||
{
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
job.target = ozz::math::simd_float4::Load(2.f * .6f, 0.f, 0.f, 0.f);
|
||||
job.soften = 0.f;
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_NOT_REACHED(job);
|
||||
}
|
||||
|
||||
// Not reachable, softened
|
||||
{
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
job.target = ozz::math::simd_float4::Load(2.f, 0.f, 0.f, 0.f);
|
||||
job.soften = .5f;
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_NOT_REACHED(job);
|
||||
}
|
||||
|
||||
// Not reachable, a bit too far
|
||||
{
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
job.target = ozz::math::simd_float4::Load(3.f, 0.f, 0.f, 0.f);
|
||||
job.soften = 1.f;
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_NOT_REACHED(job);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Twist, IKTwoBoneJob) {
|
||||
// Setup initial pose
|
||||
const ozz::math::Float4x4 start = ozz::math::Float4x4::identity();
|
||||
const ozz::math::Float4x4 mid = ozz::math::Float4x4::FromAffine(
|
||||
ozz::math::simd_float4::y_axis(),
|
||||
ozz::math::SimdQuaternion::FromAxisAngle(
|
||||
ozz::math::simd_float4::z_axis(),
|
||||
ozz::math::simd_float4::Load1(ozz::math::kPi_2))
|
||||
.xyzw,
|
||||
ozz::math::simd_float4::one());
|
||||
const ozz::math::Float4x4 end = ozz::math::Float4x4::Translation(
|
||||
ozz::math::simd_float4::x_axis() + ozz::math::simd_float4::y_axis());
|
||||
const ozz::math::SimdFloat4 mid_axis =
|
||||
ozz::math::Cross3(start.cols[3] - mid.cols[3], end.cols[3] - mid.cols[3]);
|
||||
|
||||
// Prepares job.
|
||||
ozz::animation::IKTwoBoneJob job;
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
job.target = ozz::math::simd_float4::Load(1.f, 1.f, 0.f, 0.f);
|
||||
job.start_joint = &start;
|
||||
job.mid_joint = ∣
|
||||
job.end_joint = &end;
|
||||
job.mid_axis = mid_axis;
|
||||
ozz::math::SimdQuaternion qstart;
|
||||
job.start_joint_correction = &qstart;
|
||||
ozz::math::SimdQuaternion qmid;
|
||||
job.mid_joint_correction = &qmid;
|
||||
bool reached;
|
||||
job.reached = &reached;
|
||||
ASSERT_TRUE(job.Validate());
|
||||
|
||||
// Twist angle 0
|
||||
{
|
||||
job.twist_angle = 0.f;
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_REACHED(job);
|
||||
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qstart, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qmid, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
|
||||
// Twist angle pi / 2
|
||||
{
|
||||
job.twist_angle = ozz::math::kPi_2;
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_REACHED(job);
|
||||
|
||||
const ozz::math::Quaternion h_Pi_2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3(.70710678f, .70710678f, 0.f), ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qstart, h_Pi_2.x, h_Pi_2.y, h_Pi_2.z, h_Pi_2.w,
|
||||
2e-3f);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qmid, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
|
||||
// Twist angle pi
|
||||
{
|
||||
job.twist_angle = ozz::math::kPi;
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_REACHED(job);
|
||||
|
||||
const ozz::math::Quaternion h_Pi = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3(.70710678f, .70710678f, 0.f), -ozz::math::kPi);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qstart, h_Pi.x, h_Pi.y, h_Pi.z, h_Pi.w, 2e-3f);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qmid, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
|
||||
// Twist angle 2pi
|
||||
{
|
||||
job.twist_angle = ozz::math::kPi * 2.f;
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_REACHED(job);
|
||||
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qstart, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qmid, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Weight, IKTwoBoneJob) {
|
||||
// Setup initial pose
|
||||
const ozz::math::Float4x4 start = ozz::math::Float4x4::identity();
|
||||
const ozz::math::Float4x4 mid = ozz::math::Float4x4::FromAffine(
|
||||
ozz::math::simd_float4::y_axis(),
|
||||
ozz::math::SimdQuaternion::FromAxisAngle(
|
||||
ozz::math::simd_float4::z_axis(),
|
||||
ozz::math::simd_float4::Load1(ozz::math::kPi_2))
|
||||
.xyzw,
|
||||
ozz::math::simd_float4::one());
|
||||
const ozz::math::Float4x4 end = ozz::math::Float4x4::Translation(
|
||||
ozz::math::simd_float4::x_axis() + ozz::math::simd_float4::y_axis());
|
||||
const ozz::math::SimdFloat4 mid_axis =
|
||||
ozz::math::Cross3(start.cols[3] - mid.cols[3], end.cols[3] - mid.cols[3]);
|
||||
|
||||
// Prepares job.
|
||||
ozz::animation::IKTwoBoneJob job;
|
||||
job.start_joint = &start;
|
||||
job.mid_joint = ∣
|
||||
job.end_joint = &end;
|
||||
job.mid_axis = mid_axis;
|
||||
ozz::math::SimdQuaternion qstart;
|
||||
job.start_joint_correction = &qstart;
|
||||
ozz::math::SimdQuaternion qmid;
|
||||
job.mid_joint_correction = &qmid;
|
||||
bool reached;
|
||||
job.reached = &reached;
|
||||
ASSERT_TRUE(job.Validate());
|
||||
|
||||
// Maximum weight
|
||||
{
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
job.target = ozz::math::simd_float4::Load(2.f, 0.f, 0.f, 0.f);
|
||||
job.weight = 1.f;
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_REACHED(job);
|
||||
|
||||
const ozz::math::Quaternion z_mPi_2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::z_axis(), -ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qstart, z_mPi_2.x, z_mPi_2.y, z_mPi_2.z,
|
||||
z_mPi_2.w, 2e-3f);
|
||||
const ozz::math::Quaternion z_Pi_2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::z_axis(), ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qmid, z_Pi_2.x, z_Pi_2.y, z_Pi_2.z, z_Pi_2.w,
|
||||
2e-3f);
|
||||
}
|
||||
|
||||
// Weight > 1 is clamped
|
||||
{
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
job.target = ozz::math::simd_float4::Load(2.f, 0.f, 0.f, 0.f);
|
||||
job.weight = 1.1f;
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_REACHED(job);
|
||||
|
||||
const ozz::math::Quaternion z_mPi_2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::z_axis(), -ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qstart, z_mPi_2.x, z_mPi_2.y, z_mPi_2.z,
|
||||
z_mPi_2.w, 2e-3f);
|
||||
const ozz::math::Quaternion z_Pi_2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::z_axis(), ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qmid, z_Pi_2.x, z_Pi_2.y, z_Pi_2.z, z_Pi_2.w,
|
||||
2e-3f);
|
||||
}
|
||||
|
||||
// 0 weight
|
||||
{
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
job.target = ozz::math::simd_float4::Load(2.f, 0.f, 0.f, 0.f);
|
||||
job.weight = 0.f;
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_NOT_REACHED(job);
|
||||
|
||||
EXPECT_SIMDQUATERNION_EQ_EST(qstart, 0.f, 0.f, 0.f, 1.f);
|
||||
EXPECT_SIMDQUATERNION_EQ_EST(qmid, 0.f, 0.f, 0.f, 1.f);
|
||||
}
|
||||
|
||||
// Weight < 0 is clamped
|
||||
{
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
job.target = ozz::math::simd_float4::Load(2.f, 0.f, 0.f, 0.f);
|
||||
job.weight = -.1f;
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_NOT_REACHED(job);
|
||||
|
||||
EXPECT_SIMDQUATERNION_EQ_EST(qstart, 0.f, 0.f, 0.f, 1.f);
|
||||
EXPECT_SIMDQUATERNION_EQ_EST(qmid, 0.f, 0.f, 0.f, 1.f);
|
||||
}
|
||||
|
||||
// .5 weight
|
||||
{
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
job.target = ozz::math::simd_float4::Load(2.f, 0.f, 0.f, 0.f);
|
||||
job.weight = .5f;
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_NOT_REACHED(job);
|
||||
|
||||
const ozz::math::Quaternion z_mPi_2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::z_axis(), -ozz::math::kPi_2 * job.weight);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qstart, z_mPi_2.x, z_mPi_2.y, z_mPi_2.z,
|
||||
z_mPi_2.w, 2e-3f);
|
||||
const ozz::math::Quaternion z_Pi_2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::z_axis(), ozz::math::kPi_2 * job.weight);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qmid, z_Pi_2.x, z_Pi_2.y, z_Pi_2.z, z_Pi_2.w,
|
||||
2e-3f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PoleTargetAlignment, IKTwoBoneJob) {
|
||||
// Setup initial pose
|
||||
const ozz::math::Float4x4 start = ozz::math::Float4x4::identity();
|
||||
const ozz::math::Float4x4 mid = ozz::math::Float4x4::FromAffine(
|
||||
ozz::math::simd_float4::y_axis(),
|
||||
ozz::math::SimdQuaternion::FromAxisAngle(
|
||||
ozz::math::simd_float4::z_axis(),
|
||||
ozz::math::simd_float4::Load1(ozz::math::kPi_2))
|
||||
.xyzw,
|
||||
ozz::math::simd_float4::one());
|
||||
const ozz::math::Float4x4 end = ozz::math::Float4x4::Translation(
|
||||
ozz::math::simd_float4::x_axis() + ozz::math::simd_float4::y_axis());
|
||||
const ozz::math::SimdFloat4 mid_axis =
|
||||
ozz::math::Cross3(start.cols[3] - mid.cols[3], end.cols[3] - mid.cols[3]);
|
||||
|
||||
// Prepares job.
|
||||
ozz::animation::IKTwoBoneJob job;
|
||||
job.start_joint = &start;
|
||||
job.mid_joint = ∣
|
||||
job.end_joint = &end;
|
||||
job.mid_axis = mid_axis;
|
||||
ozz::math::SimdQuaternion qstart;
|
||||
job.start_joint_correction = &qstart;
|
||||
ozz::math::SimdQuaternion qmid;
|
||||
job.mid_joint_correction = &qmid;
|
||||
bool reached;
|
||||
job.reached = &reached;
|
||||
ASSERT_TRUE(job.Validate());
|
||||
|
||||
{ // Reachable, undefined qstart
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
job.target = ozz::math::simd_float4::Load(0.f, ozz::math::kSqrt2, 0.f, 0.f);
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_REACHED(job);
|
||||
|
||||
// qstart is undefined, many solutions in this case
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qmid, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
|
||||
{ // Reachable, defined qstart
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
job.target =
|
||||
ozz::math::simd_float4::Load(.001f, ozz::math::kSqrt2, 0.f, 0.f);
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_REACHED(job);
|
||||
|
||||
const ozz::math::Quaternion z_Pi_4 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::z_axis(), ozz::math::kPi_4);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qstart, z_Pi_4.x, z_Pi_4.y, z_Pi_4.z, z_Pi_4.w,
|
||||
2e-3f);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qmid, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
|
||||
{ // Full extent, undefined qstart, end not reached
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
job.target = ozz::math::simd_float4::Load(0.f, 3.f, 0.f, 0.f);
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
// qstart is undefined, many solutions in this case
|
||||
const ozz::math::Quaternion z_Pi_2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::z_axis(), ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qmid, z_Pi_2.x, z_Pi_2.y, z_Pi_2.z, z_Pi_2.w,
|
||||
2e-3f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MidAxis, IKTwoBoneJob) {
|
||||
// Setup initial pose
|
||||
const ozz::math::Float4x4 start = ozz::math::Float4x4::identity();
|
||||
const ozz::math::Float4x4 mid = ozz::math::Float4x4::FromAffine(
|
||||
ozz::math::simd_float4::y_axis(),
|
||||
ozz::math::SimdQuaternion::FromAxisAngle(
|
||||
ozz::math::simd_float4::z_axis(),
|
||||
ozz::math::simd_float4::Load1(ozz::math::kPi_2))
|
||||
.xyzw,
|
||||
ozz::math::simd_float4::one());
|
||||
const ozz::math::Float4x4 end = ozz::math::Float4x4::Translation(
|
||||
ozz::math::simd_float4::x_axis() + ozz::math::simd_float4::y_axis());
|
||||
const ozz::math::SimdFloat4 mid_axis =
|
||||
ozz::math::Cross3(start.cols[3] - mid.cols[3], end.cols[3] - mid.cols[3]);
|
||||
|
||||
// Prepares job.
|
||||
ozz::animation::IKTwoBoneJob job;
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
job.target = ozz::math::simd_float4::Load(1.f, 1.f, 0.f, 0.f);
|
||||
job.start_joint = &start;
|
||||
job.mid_joint = ∣
|
||||
job.end_joint = &end;
|
||||
ozz::math::SimdQuaternion qstart;
|
||||
job.start_joint_correction = &qstart;
|
||||
ozz::math::SimdQuaternion qmid;
|
||||
job.mid_joint_correction = &qmid;
|
||||
bool reached;
|
||||
job.reached = &reached;
|
||||
ASSERT_TRUE(job.Validate());
|
||||
|
||||
// Positive mid_axis
|
||||
{
|
||||
job.mid_axis = mid_axis;
|
||||
job.target = ozz::math::simd_float4::Load(1.f, 1.f, 0.f, 0.f);
|
||||
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_REACHED(job);
|
||||
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qstart, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qmid, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
|
||||
// Negative mid_axis
|
||||
{
|
||||
job.mid_axis = -mid_axis;
|
||||
job.target = ozz::math::simd_float4::Load(1.f, 1.f, 0.f, 0.f);
|
||||
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_REACHED(job);
|
||||
|
||||
const ozz::math::Quaternion y_Pi = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::y_axis(), ozz::math::kPi);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qstart, y_Pi.x, y_Pi.y, y_Pi.z, y_Pi.w, 2e-3f);
|
||||
const ozz::math::Quaternion z_Pi = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::z_axis(), ozz::math::kPi);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qmid, z_Pi.x, z_Pi.y, z_Pi.z, z_Pi.w, 2e-3f);
|
||||
}
|
||||
|
||||
// Aligned joints
|
||||
{
|
||||
// Replaces "end" joint matrix to align the 3 joints.
|
||||
const ozz::math::Float4x4 aligned_end = ozz::math::Float4x4::Translation(
|
||||
ozz::math::simd_float4::Load(0.f, 2.f, 0.f, 0.f));
|
||||
job.end_joint = &aligned_end;
|
||||
|
||||
job.mid_axis = mid_axis;
|
||||
job.target = ozz::math::simd_float4::Load(1.f, 1.f, 0.f, 0.f);
|
||||
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_REACHED(job);
|
||||
|
||||
// Restore end joint matrix
|
||||
job.end_joint = &end;
|
||||
|
||||
// Start rotates 180 on y, to allow Mid to turn positively on z axis.
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qstart, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
const ozz::math::Quaternion z_mPi_2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::z_axis(), -ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qmid, z_mPi_2.x, z_mPi_2.y, z_mPi_2.z,
|
||||
z_mPi_2.w, 2e-3f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(AlignedJointsAndTarget, IKTwoBoneJob) {
|
||||
// Setup initial pose
|
||||
const ozz::math::Float4x4 start = ozz::math::Float4x4::identity();
|
||||
const ozz::math::Float4x4 mid =
|
||||
ozz::math::Float4x4::Translation(ozz::math::simd_float4::x_axis());
|
||||
const ozz::math::Float4x4 end = ozz::math::Float4x4::Translation(
|
||||
ozz::math::simd_float4::x_axis() + ozz::math::simd_float4::x_axis());
|
||||
const ozz::math::SimdFloat4 mid_axis = ozz::math::simd_float4::z_axis();
|
||||
|
||||
// Prepares job.
|
||||
ozz::animation::IKTwoBoneJob job;
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
job.mid_axis = mid_axis;
|
||||
job.start_joint = &start;
|
||||
job.mid_joint = ∣
|
||||
job.end_joint = &end;
|
||||
ozz::math::SimdQuaternion qstart;
|
||||
job.start_joint_correction = &qstart;
|
||||
ozz::math::SimdQuaternion qmid;
|
||||
job.mid_joint_correction = &qmid;
|
||||
bool reached;
|
||||
job.reached = &reached;
|
||||
ASSERT_TRUE(job.Validate());
|
||||
|
||||
// Aligned and reachable
|
||||
{
|
||||
job.target = ozz::math::simd_float4::Load(2.f, 0.f, 0.f, 0.f);
|
||||
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_REACHED(job);
|
||||
|
||||
// Start rotates 180 on y, to allow Mid to turn positively on z axis.
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qstart, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qmid, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
|
||||
// Aligned and unreachable
|
||||
{
|
||||
job.target = ozz::math::simd_float4::Load(3.f, 0.f, 0.f, 0.f);
|
||||
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_NOT_REACHED(job);
|
||||
|
||||
// Start rotates 180 on y, to allow Mid to turn positively on z axis.
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qstart, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qmid, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ZeroLengthStartTarget, IKTwoBoneJob) {
|
||||
// Setup initial pose
|
||||
const ozz::math::Float4x4 start = ozz::math::Float4x4::identity();
|
||||
const ozz::math::Float4x4 mid = ozz::math::Float4x4::FromAffine(
|
||||
ozz::math::simd_float4::y_axis(),
|
||||
ozz::math::SimdQuaternion::FromAxisAngle(
|
||||
ozz::math::simd_float4::z_axis(),
|
||||
ozz::math::simd_float4::Load1(ozz::math::kPi_2))
|
||||
.xyzw,
|
||||
ozz::math::simd_float4::one());
|
||||
const ozz::math::Float4x4 end = ozz::math::Float4x4::Translation(
|
||||
ozz::math::simd_float4::x_axis() + ozz::math::simd_float4::y_axis());
|
||||
|
||||
// Prepares job.
|
||||
ozz::animation::IKTwoBoneJob job;
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
job.target = start.cols[3]; // 0 length from start to target
|
||||
job.start_joint = &start;
|
||||
job.mid_joint = ∣
|
||||
job.end_joint = &end;
|
||||
ozz::math::SimdQuaternion qstart;
|
||||
job.start_joint_correction = &qstart;
|
||||
ozz::math::SimdQuaternion qmid;
|
||||
job.mid_joint_correction = &qmid;
|
||||
bool reached;
|
||||
job.reached = &reached;
|
||||
ASSERT_TRUE(job.Validate());
|
||||
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qstart, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
// Mid joint is bent -90 degrees to reach start.
|
||||
const ozz::math::Quaternion z_mPi_2 = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::z_axis(), -ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(qmid, z_mPi_2.x, z_mPi_2.y, z_mPi_2.z, z_mPi_2.w,
|
||||
2e-3f);
|
||||
}
|
||||
|
||||
TEST(ZeroLengthBoneChain, IKTwoBoneJob) {
|
||||
// Setup initial pose
|
||||
const ozz::math::Float4x4 start = ozz::math::Float4x4::identity();
|
||||
const ozz::math::Float4x4 mid = ozz::math::Float4x4::identity();
|
||||
const ozz::math::Float4x4 end = ozz::math::Float4x4::identity();
|
||||
|
||||
// Prepares job.
|
||||
ozz::animation::IKTwoBoneJob job;
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
job.target = ozz::math::simd_float4::x_axis();
|
||||
job.start_joint = &start;
|
||||
job.mid_joint = ∣
|
||||
job.end_joint = &end;
|
||||
ozz::math::SimdQuaternion qstart;
|
||||
job.start_joint_correction = &qstart;
|
||||
ozz::math::SimdQuaternion qmid;
|
||||
job.mid_joint_correction = &qmid;
|
||||
ASSERT_TRUE(job.Validate());
|
||||
|
||||
// Just expecting it's not crashing
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
EXPECT_NOT_REACHED(job);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,640 @@
|
||||
//----------------------------------------------------------------------------//
|
||||
// //
|
||||
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
|
||||
// and distributed under the MIT License (MIT). //
|
||||
// //
|
||||
// Copyright (c) Guillaume Blanc //
|
||||
// //
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a //
|
||||
// copy of this software and associated documentation files (the "Software"), //
|
||||
// to deal in the Software without restriction, including without limitation //
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
|
||||
// and/or sell copies of the Software, and to permit persons to whom the //
|
||||
// Software is furnished to do so, subject to the following conditions: //
|
||||
// //
|
||||
// The above copyright notice and this permission notice shall be included in //
|
||||
// all copies or substantial portions of the Software. //
|
||||
// //
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
|
||||
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
|
||||
// DEALINGS IN THE SOFTWARE. //
|
||||
// //
|
||||
//----------------------------------------------------------------------------//
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "ozz/animation/offline/animation_builder.h"
|
||||
#include "ozz/animation/offline/raw_animation.h"
|
||||
#include "ozz/animation/runtime/animation.h"
|
||||
#include "ozz/animation/runtime/sampling_job.h"
|
||||
#include "ozz/base/maths/gtest_math_helper.h"
|
||||
#include "ozz/base/maths/soa_transform.h"
|
||||
#include "ozz/base/memory/unique_ptr.h"
|
||||
|
||||
using ozz::animation::Animation;
|
||||
using ozz::animation::SamplingCache;
|
||||
using ozz::animation::SamplingJob;
|
||||
using ozz::animation::offline::AnimationBuilder;
|
||||
using ozz::animation::offline::RawAnimation;
|
||||
|
||||
TEST(JobValidity, SamplingJob) {
|
||||
RawAnimation raw_animation;
|
||||
raw_animation.duration = 1.f;
|
||||
raw_animation.tracks.resize(1);
|
||||
|
||||
AnimationBuilder builder;
|
||||
ozz::unique_ptr<Animation> animation(builder(raw_animation));
|
||||
ASSERT_TRUE(animation);
|
||||
|
||||
// Allocates cache.
|
||||
SamplingCache cache(1);
|
||||
|
||||
{ // Empty/default job
|
||||
SamplingJob job;
|
||||
EXPECT_FALSE(job.Validate());
|
||||
EXPECT_FALSE(job.Run());
|
||||
}
|
||||
|
||||
{ // Invalid output
|
||||
SamplingJob job;
|
||||
job.animation = animation.get();
|
||||
job.cache = &cache;
|
||||
EXPECT_FALSE(job.Validate());
|
||||
EXPECT_FALSE(job.Run());
|
||||
}
|
||||
|
||||
{ // Invalid animation.
|
||||
ozz::math::SoaTransform output[1];
|
||||
|
||||
SamplingJob job;
|
||||
job.cache = &cache;
|
||||
job.output = output;
|
||||
EXPECT_FALSE(job.Validate());
|
||||
EXPECT_FALSE(job.Run());
|
||||
}
|
||||
|
||||
{ // Invalid cache.
|
||||
ozz::math::SoaTransform output[1];
|
||||
|
||||
SamplingJob job;
|
||||
job.animation = animation.get();
|
||||
job.output = output;
|
||||
EXPECT_FALSE(job.Validate());
|
||||
EXPECT_FALSE(job.Run());
|
||||
}
|
||||
|
||||
{ // Invalid cache size.
|
||||
SamplingCache zero_cache(0);
|
||||
ozz::math::SoaTransform output[1];
|
||||
|
||||
SamplingJob job;
|
||||
job.animation = animation.get();
|
||||
job.cache = &zero_cache;
|
||||
job.output = output;
|
||||
EXPECT_FALSE(job.Validate());
|
||||
EXPECT_FALSE(job.Run());
|
||||
}
|
||||
|
||||
{ // Invalid job with smaller output.
|
||||
ozz::math::SoaTransform* output = nullptr;
|
||||
SamplingJob job;
|
||||
job.ratio =
|
||||
2155.f; // Any time ratio can be set, it's clamped in unit interval.
|
||||
job.animation = animation.get();
|
||||
job.cache = &cache;
|
||||
job.output = ozz::span<ozz::math::SoaTransform>(output, size_t(0));
|
||||
EXPECT_FALSE(job.Validate());
|
||||
EXPECT_FALSE(job.Run());
|
||||
}
|
||||
|
||||
{ // Valid job.
|
||||
ozz::math::SoaTransform output[1];
|
||||
SamplingJob job;
|
||||
job.ratio = 2155.f; // Any time can be set.
|
||||
job.animation = animation.get();
|
||||
job.cache = &cache;
|
||||
job.output = output;
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
}
|
||||
|
||||
{ // Valid job with bigger cache.
|
||||
SamplingCache big_cache(2);
|
||||
ozz::math::SoaTransform output[1];
|
||||
SamplingJob job;
|
||||
job.ratio = 2155.f; // Any time can be set.
|
||||
job.animation = animation.get();
|
||||
job.cache = &big_cache;
|
||||
job.output = output;
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
}
|
||||
|
||||
{ // Valid job with bigger output.
|
||||
ozz::math::SoaTransform output[2];
|
||||
SamplingJob job;
|
||||
job.ratio = 2155.f; // Any time can be set.
|
||||
job.animation = animation.get();
|
||||
job.cache = &cache;
|
||||
job.output = output;
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
}
|
||||
|
||||
{ // Default animation.
|
||||
ozz::math::SoaTransform output[1];
|
||||
Animation default_animation;
|
||||
SamplingJob job;
|
||||
job.animation = &default_animation;
|
||||
job.cache = &cache;
|
||||
job.output = output;
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Sampling, SamplingJob) {
|
||||
// Instantiates a builder objects with default parameters.
|
||||
AnimationBuilder builder;
|
||||
|
||||
// Building an Animation with unsorted keys fails.
|
||||
RawAnimation raw_animation;
|
||||
raw_animation.duration = 1.f;
|
||||
raw_animation.tracks.resize(4);
|
||||
|
||||
SamplingCache cache(4);
|
||||
|
||||
// Raw animation inputs.
|
||||
// 0 1
|
||||
// -----------------------
|
||||
// 0 - | A |
|
||||
// 1 - | |
|
||||
// 2 - B C D E F
|
||||
// 3 - | G H |
|
||||
|
||||
// Final animation.
|
||||
// 0 1
|
||||
// -----------------------
|
||||
// 0 - A-1 4
|
||||
// 1 - 1 5
|
||||
// 2 - B2 C6 D8 E10 F11
|
||||
// 3 - 3 G7 H9 12
|
||||
|
||||
struct {
|
||||
float sample_time;
|
||||
float trans[12];
|
||||
} result[] = {
|
||||
{-.2f, {-1.f, 0.f, 2.f, 7.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}},
|
||||
{.0f, {-1.f, 0.f, 2.f, 7.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}},
|
||||
{.0000001f,
|
||||
{-1.f, 0.f, 2.f, 7.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}},
|
||||
{.1f, {-1.f, 0.f, 4.f, 7.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}},
|
||||
{.2f, {-1.f, 0.f, 6.f, 7.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}},
|
||||
{.3f, {-1.f, 0.f, 7.f, 7.5, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}},
|
||||
{.4f, {-1.f, 0.f, 8.f, 8.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}},
|
||||
{.3999999f,
|
||||
{-1.f, 0.f, 8.f, 8.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}},
|
||||
{.4000001f,
|
||||
{-1.f, 0.f, 8.f, 8.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}},
|
||||
{.5f, {-1.f, 0.f, 9.f, 8.5f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}},
|
||||
{.6f, {-1.f, 0.f, 10.f, 9.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}},
|
||||
{.9999999f,
|
||||
{-1.f, 0.f, 11.f, 9.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}},
|
||||
{1.f, {-1.f, 0.f, 11.f, 9.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}},
|
||||
{1.000001f,
|
||||
{-1.f, 0.f, 11.f, 9.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}},
|
||||
{.5f, {-1.f, 0.f, 9.f, 8.5f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}},
|
||||
{.9999999f,
|
||||
{-1.f, 0.f, 11.f, 9.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}},
|
||||
{.0000001f,
|
||||
{-1.f, 0.f, 2.f, 7.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}}};
|
||||
|
||||
RawAnimation::TranslationKey a = {.2f, ozz::math::Float3(-1.f, 0.f, 0.f)};
|
||||
raw_animation.tracks[0].translations.push_back(a);
|
||||
|
||||
RawAnimation::TranslationKey b = {0.f, ozz::math::Float3(2.f, 0.f, 0.f)};
|
||||
raw_animation.tracks[2].translations.push_back(b);
|
||||
RawAnimation::TranslationKey c = {0.2f, ozz::math::Float3(6.f, 0.f, 0.f)};
|
||||
raw_animation.tracks[2].translations.push_back(c);
|
||||
RawAnimation::TranslationKey d = {0.4f, ozz::math::Float3(8.f, 0.f, 0.f)};
|
||||
raw_animation.tracks[2].translations.push_back(d);
|
||||
RawAnimation::TranslationKey e = {0.6f, ozz::math::Float3(10.f, 0.f, 0.f)};
|
||||
raw_animation.tracks[2].translations.push_back(e);
|
||||
RawAnimation::TranslationKey f = {1.f, ozz::math::Float3(11.f, 0.f, 0.f)};
|
||||
raw_animation.tracks[2].translations.push_back(f);
|
||||
|
||||
RawAnimation::TranslationKey g = {0.2f, ozz::math::Float3(7.f, 0.f, 0.f)};
|
||||
raw_animation.tracks[3].translations.push_back(g);
|
||||
RawAnimation::TranslationKey h = {0.6f, ozz::math::Float3(9.f, 0.f, 0.f)};
|
||||
raw_animation.tracks[3].translations.push_back(h);
|
||||
|
||||
// Builds animation
|
||||
ozz::unique_ptr<Animation> animation(builder(raw_animation));
|
||||
ASSERT_TRUE(animation);
|
||||
|
||||
ozz::math::SoaTransform output[1];
|
||||
|
||||
SamplingJob job;
|
||||
job.animation = animation.get();
|
||||
job.cache = &cache;
|
||||
job.output = output;
|
||||
|
||||
for (size_t i = 0; i < OZZ_ARRAY_SIZE(result); ++i) {
|
||||
memset(output, 0xde, sizeof(output));
|
||||
job.ratio = result[i].sample_time / animation->duration();
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
|
||||
EXPECT_SOAFLOAT3_EQ_EST(
|
||||
output[0].translation, result[i].trans[0], result[i].trans[1],
|
||||
result[i].trans[2], result[i].trans[3], result[i].trans[4],
|
||||
result[i].trans[5], result[i].trans[6], result[i].trans[7],
|
||||
result[i].trans[8], result[i].trans[9], result[i].trans[10],
|
||||
result[i].trans[11]);
|
||||
EXPECT_SOAQUATERNION_EQ_EST(output[0].rotation, 0.f, 0.f, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f, 1.f,
|
||||
1.f, 1.f);
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].scale, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f,
|
||||
1.f, 1.f, 1.f, 1.f, 1.f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SamplingNoTrack, SamplingJob) {
|
||||
RawAnimation raw_animation;
|
||||
raw_animation.duration = 46.f;
|
||||
|
||||
SamplingCache cache(1);
|
||||
|
||||
AnimationBuilder builder;
|
||||
ozz::unique_ptr<Animation> animation(builder(raw_animation));
|
||||
ASSERT_TRUE(animation);
|
||||
|
||||
ozz::math::SoaTransform test_output[1];
|
||||
ozz::math::SoaTransform output[1];
|
||||
memset(test_output, 0xde, sizeof(test_output));
|
||||
memset(output, 0xde, sizeof(output));
|
||||
|
||||
SamplingJob job;
|
||||
job.ratio = 0.f;
|
||||
job.animation = animation.get();
|
||||
job.cache = &cache;
|
||||
job.output = output;
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
|
||||
// Tests output.
|
||||
EXPECT_EQ(memcmp(test_output, output, sizeof(output)), 0);
|
||||
}
|
||||
|
||||
TEST(Sampling1Track0Key, SamplingJob) {
|
||||
RawAnimation raw_animation;
|
||||
raw_animation.duration = 46.f;
|
||||
raw_animation.tracks.resize(1); // Adds a joint.
|
||||
|
||||
SamplingCache cache(1);
|
||||
|
||||
AnimationBuilder builder;
|
||||
ozz::unique_ptr<Animation> animation(builder(raw_animation));
|
||||
ASSERT_TRUE(animation);
|
||||
|
||||
ozz::math::SoaTransform output[1];
|
||||
|
||||
SamplingJob job;
|
||||
job.animation = animation.get();
|
||||
job.cache = &cache;
|
||||
job.output = output;
|
||||
|
||||
for (float t = -.2f; t < 1.2f; t += .1f) {
|
||||
memset(output, 0xde, sizeof(output));
|
||||
job.ratio = t;
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].translation, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.f);
|
||||
EXPECT_SOAQUATERNION_EQ_EST(output[0].rotation, 0.f, 0.f, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f, 1.f,
|
||||
1.f, 1.f);
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].scale, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f,
|
||||
1.f, 1.f, 1.f, 1.f, 1.f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Sampling1Track1Key, SamplingJob) {
|
||||
RawAnimation raw_animation;
|
||||
raw_animation.duration = 46.f;
|
||||
raw_animation.tracks.resize(1); // Adds a joint.
|
||||
|
||||
SamplingCache cache(1);
|
||||
|
||||
const RawAnimation::TranslationKey tkey = {.3f,
|
||||
ozz::math::Float3(1.f, -1.f, 5.f)};
|
||||
raw_animation.tracks[0].translations.push_back(tkey); // Adds a key.
|
||||
|
||||
AnimationBuilder builder;
|
||||
ozz::unique_ptr<Animation> animation(builder(raw_animation));
|
||||
ASSERT_TRUE(animation);
|
||||
|
||||
ozz::math::SoaTransform output[1];
|
||||
|
||||
SamplingJob job;
|
||||
job.animation = animation.get();
|
||||
job.cache = &cache;
|
||||
job.output = output;
|
||||
|
||||
for (float t = -.2f; t < 1.2f; t += .1f) {
|
||||
memset(output, 0xde, sizeof(output));
|
||||
job.ratio = t;
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].translation, 1.f, 0.f, 0.f, 0.f, -1.f,
|
||||
0.f, 0.f, 0.f, 5.f, 0.f, 0.f, 0.f);
|
||||
EXPECT_SOAQUATERNION_EQ_EST(output[0].rotation, 0.f, 0.f, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f, 1.f,
|
||||
1.f, 1.f);
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].scale, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f,
|
||||
1.f, 1.f, 1.f, 1.f, 1.f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Sampling1Track2Keys, SamplingJob) {
|
||||
RawAnimation raw_animation;
|
||||
raw_animation.duration = 46.f;
|
||||
raw_animation.tracks.resize(1); // Adds a joint.
|
||||
|
||||
SamplingCache cache(1);
|
||||
|
||||
const RawAnimation::TranslationKey tkey0 = {.5f,
|
||||
ozz::math::Float3(1.f, 2.f, 4.f)};
|
||||
raw_animation.tracks[0].translations.push_back(tkey0); // Adds a key.
|
||||
const RawAnimation::TranslationKey tkey1 = {.8f,
|
||||
ozz::math::Float3(2.f, 4.f, 8.f)};
|
||||
raw_animation.tracks[0].translations.push_back(tkey1); // Adds a key.
|
||||
|
||||
AnimationBuilder builder;
|
||||
ozz::unique_ptr<Animation> animation(builder(raw_animation));
|
||||
ASSERT_TRUE(animation);
|
||||
|
||||
ozz::math::SoaTransform output[1];
|
||||
memset(output, 0xde, sizeof(output));
|
||||
|
||||
SamplingJob job;
|
||||
job.animation = animation.get();
|
||||
job.cache = &cache;
|
||||
job.output = output;
|
||||
|
||||
// Samples at t = 0.
|
||||
job.ratio = 0.f;
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].translation, 1.f, 0.f, 0.f, 0.f, 2.f, 0.f,
|
||||
0.f, 0.f, 4.f, 0.f, 0.f, 0.f);
|
||||
EXPECT_SOAQUATERNION_EQ_EST(output[0].rotation, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f, 1.f, 1.f, 1.f);
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].scale, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f,
|
||||
1.f, 1.f, 1.f, 1.f, 1.f);
|
||||
|
||||
// Samples at t = tkey0.
|
||||
job.ratio = tkey0.time / animation->duration();
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].translation, 1.f, 0.f, 0.f, 0.f, 2.f, 0.f,
|
||||
0.f, 0.f, 4.f, 0.f, 0.f, 0.f);
|
||||
EXPECT_SOAQUATERNION_EQ_EST(output[0].rotation, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f, 1.f, 1.f, 1.f);
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].scale, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f,
|
||||
1.f, 1.f, 1.f, 1.f, 1.f);
|
||||
|
||||
// Samples at t = tkey1.
|
||||
job.ratio = tkey1.time / animation->duration();
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].translation, 2.f, 0.f, 0.f, 0.f, 4.f, 0.f,
|
||||
0.f, 0.f, 8.f, 0.f, 0.f, 0.f);
|
||||
EXPECT_SOAQUATERNION_EQ_EST(output[0].rotation, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f, 1.f, 1.f, 1.f);
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].scale, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f,
|
||||
1.f, 1.f, 1.f, 1.f, 1.f);
|
||||
|
||||
// Samples at t = end.
|
||||
job.ratio = 1.f;
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].translation, 2.f, 0.f, 0.f, 0.f, 4.f, 0.f,
|
||||
0.f, 0.f, 8.f, 0.f, 0.f, 0.f);
|
||||
EXPECT_SOAQUATERNION_EQ_EST(output[0].rotation, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f, 1.f, 1.f, 1.f);
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].scale, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f,
|
||||
1.f, 1.f, 1.f, 1.f, 1.f);
|
||||
|
||||
// Samples at tkey0.time < t < tkey1.time.
|
||||
job.ratio = (tkey0.time / animation->duration() +
|
||||
tkey1.time / animation->duration()) /
|
||||
2.f;
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].translation, 1.5f, 0.f, 0.f, 0.f, 3.f, 0.f,
|
||||
0.f, 0.f, 6.f, 0.f, 0.f, 0.f);
|
||||
EXPECT_SOAQUATERNION_EQ_EST(output[0].rotation, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f, 1.f, 1.f, 1.f);
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].scale, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f,
|
||||
1.f, 1.f, 1.f, 1.f, 1.f);
|
||||
}
|
||||
|
||||
TEST(Sampling4Track2Keys, SamplingJob) {
|
||||
RawAnimation raw_animation;
|
||||
raw_animation.duration = 1.f;
|
||||
raw_animation.tracks.resize(4); // Adds a joint.
|
||||
|
||||
SamplingCache cache(1);
|
||||
|
||||
const RawAnimation::TranslationKey tkey00 = {
|
||||
.5f, ozz::math::Float3(1.f, 2.f, 4.f)};
|
||||
raw_animation.tracks[0].translations.push_back(tkey00); // Adds a key.
|
||||
const RawAnimation::TranslationKey tkey01 = {
|
||||
.8f, ozz::math::Float3(2.f, 4.f, 8.f)};
|
||||
raw_animation.tracks[0].translations.push_back(tkey01); // Adds a key.
|
||||
|
||||
// This quaternion will be negated as the builder ensures that the first key
|
||||
// is in identity quaternion hemisphere.
|
||||
const RawAnimation::RotationKey rkey10 = {
|
||||
0.f, ozz::math::Quaternion(0.f, 0.f, 0.f, -1.f)};
|
||||
raw_animation.tracks[1].rotations.push_back(rkey10); // Adds a key.
|
||||
const RawAnimation::RotationKey rkey11 = {
|
||||
1.f, ozz::math::Quaternion(0.f, 1.f, 0.f, 0.f)};
|
||||
raw_animation.tracks[1].rotations.push_back(rkey11); // Adds a key.
|
||||
|
||||
const RawAnimation::ScaleKey skey20 = {.5f, ozz::math::Float3(0.f, 0.f, 0.f)};
|
||||
raw_animation.tracks[2].scales.push_back(skey20); // Adds a key.
|
||||
const RawAnimation::ScaleKey skey21 = {.8f,
|
||||
ozz::math::Float3(-1.f, -1.f, -1.f)};
|
||||
raw_animation.tracks[2].scales.push_back(skey21); // Adds a key.
|
||||
|
||||
const RawAnimation::TranslationKey tkey30 = {
|
||||
0.f, ozz::math::Float3(-1.f, -2.f, -4.f)};
|
||||
raw_animation.tracks[3].translations.push_back(tkey30); // Adds a key.
|
||||
const RawAnimation::TranslationKey tkey31 = {
|
||||
1.f, ozz::math::Float3(-2.f, -4.f, -8.f)};
|
||||
raw_animation.tracks[3].translations.push_back(tkey31); // Adds a key.
|
||||
|
||||
AnimationBuilder builder;
|
||||
ozz::unique_ptr<Animation> animation(builder(raw_animation));
|
||||
ASSERT_TRUE(animation);
|
||||
|
||||
ozz::math::SoaTransform output[1];
|
||||
memset(output, 0xde, sizeof(output));
|
||||
|
||||
SamplingJob job;
|
||||
job.animation = animation.get();
|
||||
job.cache = &cache;
|
||||
job.output = output;
|
||||
|
||||
// Samples at t = 0.
|
||||
job.ratio = 0.f;
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].translation, 1.f, 0.f, 0.f, -1.f, 2.f, 0.f,
|
||||
0.f, -2.f, 4.f, 0.f, 0.f, -4.f);
|
||||
EXPECT_SOAQUATERNION_EQ_EST(output[0].rotation, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f, 1.f, 1.f, 1.f);
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].scale, 1.f, 1.f, 0.f, 1.f, 1.f, 1.f, 0.f,
|
||||
1.f, 1.f, 1.f, 0.f, 1.f);
|
||||
|
||||
// Samples at t = tkey00.
|
||||
job.ratio = tkey00.time / animation->duration();
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].translation, 1.f, 0.f, 0.f, -1.5f, 2.f, 0.f,
|
||||
0.f, -3.f, 4.f, 0.f, 0.f, -6.f);
|
||||
EXPECT_SOAQUATERNION_EQ_EST(output[0].rotation, 0.f, 0.f, 0.f, 0.f, 0.f,
|
||||
.7071067f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f,
|
||||
.7071067f, 1.f, 1.f);
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].scale, 1.f, 1.f, 0.f, 1.f, 1.f, 1.f, 0.f,
|
||||
1.f, 1.f, 1.f, 0.f, 1.f);
|
||||
|
||||
// Samples at t = end.
|
||||
job.ratio = 1.f;
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].translation, 2.f, 0.f, 0.f, -2.f, 4.f, 0.f,
|
||||
0.f, -4.f, 8.f, 0.f, 0.f, -8.f);
|
||||
EXPECT_SOAQUATERNION_EQ_EST(output[0].rotation, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f,
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 1.f);
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].scale, 1.f, 1.f, -1.f, 1.f, 1.f, 1.f, -1.f,
|
||||
1.f, 1.f, 1.f, -1.f, 1.f);
|
||||
}
|
||||
|
||||
TEST(Cache, SamplingJob) {
|
||||
RawAnimation raw_animation;
|
||||
raw_animation.duration = 46.f;
|
||||
raw_animation.tracks.resize(1); // Adds a joint.
|
||||
const RawAnimation::TranslationKey empty_key = {
|
||||
0.f, RawAnimation::TranslationKey::identity()};
|
||||
raw_animation.tracks[0].translations.push_back(empty_key);
|
||||
|
||||
SamplingCache cache(1);
|
||||
ozz::unique_ptr<Animation> animations[2];
|
||||
|
||||
{
|
||||
const RawAnimation::TranslationKey tkey = {
|
||||
.3f, ozz::math::Float3(1.f, -1.f, 5.f)};
|
||||
raw_animation.tracks[0].translations[0] = tkey;
|
||||
|
||||
AnimationBuilder builder;
|
||||
animations[0] = builder(raw_animation);
|
||||
ASSERT_TRUE(animations[0]);
|
||||
}
|
||||
{
|
||||
const RawAnimation::TranslationKey tkey = {
|
||||
.3f, ozz::math::Float3(-1.f, 1.f, -5.f)};
|
||||
raw_animation.tracks[0].translations[0] = tkey;
|
||||
|
||||
AnimationBuilder builder;
|
||||
animations[1] = builder(raw_animation);
|
||||
ASSERT_TRUE(animations[1]);
|
||||
}
|
||||
|
||||
ozz::math::SoaTransform output[1];
|
||||
|
||||
SamplingJob job;
|
||||
job.animation = animations[0].get();
|
||||
job.cache = &cache;
|
||||
job.ratio = 0.f;
|
||||
job.output = output;
|
||||
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].translation, 1.f, 0.f, 0.f, 0.f, -1.f, 0.f,
|
||||
0.f, 0.f, 5.f, 0.f, 0.f, 0.f);
|
||||
EXPECT_SOAQUATERNION_EQ_EST(output[0].rotation, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f, 1.f, 1.f, 1.f);
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].scale, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f,
|
||||
1.f, 1.f, 1.f, 1.f, 1.f);
|
||||
|
||||
// Re-uses cache.
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].translation, 1.f, 0.f, 0.f, 0.f, -1.f, 0.f,
|
||||
0.f, 0.f, 5.f, 0.f, 0.f, 0.f);
|
||||
|
||||
// Invalidates cache.
|
||||
cache.Invalidate();
|
||||
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].translation, 1.f, 0.f, 0.f, 0.f, -1.f, 0.f,
|
||||
0.f, 0.f, 5.f, 0.f, 0.f, 0.f);
|
||||
|
||||
// Changes animation.
|
||||
job.animation = animations[1].get();
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].translation, -1.f, 0.f, 0.f, 0.f, 1.f, 0.f,
|
||||
0.f, 0.f, -5.f, 0.f, 0.f, 0.f);
|
||||
EXPECT_SOAQUATERNION_EQ_EST(output[0].rotation, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f, 1.f, 1.f, 1.f);
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].scale, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f,
|
||||
1.f, 1.f, 1.f, 1.f, 1.f);
|
||||
|
||||
// Invalidates and changes animation.
|
||||
job.animation = animations[1].get();
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
EXPECT_SOAFLOAT3_EQ_EST(output[0].translation, -1.f, 0.f, 0.f, 0.f, 1.f, 0.f,
|
||||
0.f, 0.f, -5.f, 0.f, 0.f, 0.f);
|
||||
}
|
||||
|
||||
TEST(CacheResize, SamplingJob) {
|
||||
RawAnimation raw_animation;
|
||||
raw_animation.duration = 46.f;
|
||||
raw_animation.tracks.resize(7);
|
||||
|
||||
AnimationBuilder builder;
|
||||
ozz::unique_ptr<Animation> animation(builder(raw_animation));
|
||||
ASSERT_TRUE(animation);
|
||||
|
||||
// Empty cache by default
|
||||
SamplingCache cache;
|
||||
|
||||
ozz::math::SoaTransform output[7];
|
||||
|
||||
SamplingJob job;
|
||||
job.animation = animation.get();
|
||||
job.cache = &cache;
|
||||
job.ratio = 0.f;
|
||||
job.output = output;
|
||||
|
||||
// Cache is too small
|
||||
EXPECT_FALSE(job.Validate());
|
||||
|
||||
// Cache is ok.
|
||||
cache.Resize(7);
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
|
||||
// Cache is too small
|
||||
cache.Resize(1);
|
||||
EXPECT_FALSE(job.Validate());
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
//----------------------------------------------------------------------------//
|
||||
// //
|
||||
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
|
||||
// and distributed under the MIT License (MIT). //
|
||||
// //
|
||||
// Copyright (c) Guillaume Blanc //
|
||||
// //
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a //
|
||||
// copy of this software and associated documentation files (the "Software"), //
|
||||
// to deal in the Software without restriction, including without limitation //
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
|
||||
// and/or sell copies of the Software, and to permit persons to whom the //
|
||||
// Software is furnished to do so, subject to the following conditions: //
|
||||
// //
|
||||
// The above copyright notice and this permission notice shall be included in //
|
||||
// all copies or substantial portions of the Software. //
|
||||
// //
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
|
||||
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
|
||||
// DEALINGS IN THE SOFTWARE. //
|
||||
// //
|
||||
//----------------------------------------------------------------------------//
|
||||
|
||||
#include "ozz/animation/runtime/skeleton.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "ozz/animation/offline/raw_skeleton.h"
|
||||
#include "ozz/animation/offline/skeleton_builder.h"
|
||||
|
||||
#include "ozz/base/io/archive.h"
|
||||
#include "ozz/base/io/stream.h"
|
||||
|
||||
#include "ozz/base/maths/soa_transform.h"
|
||||
|
||||
#include "ozz/base/memory/unique_ptr.h"
|
||||
|
||||
using ozz::animation::Skeleton;
|
||||
using ozz::animation::offline::RawSkeleton;
|
||||
using ozz::animation::offline::SkeletonBuilder;
|
||||
|
||||
TEST(Empty, SkeletonSerialize) {
|
||||
ozz::io::MemoryStream stream;
|
||||
|
||||
// Streams out.
|
||||
ozz::io::OArchive o(&stream, ozz::GetNativeEndianness());
|
||||
|
||||
Skeleton o_skeleton;
|
||||
o << o_skeleton;
|
||||
|
||||
// Streams in.
|
||||
stream.Seek(0, ozz::io::Stream::kSet);
|
||||
ozz::io::IArchive i(&stream);
|
||||
|
||||
Skeleton i_skeleton;
|
||||
i >> i_skeleton;
|
||||
|
||||
EXPECT_EQ(o_skeleton.num_joints(), i_skeleton.num_joints());
|
||||
}
|
||||
|
||||
TEST(Filled, SkeletonSerialize) {
|
||||
ozz::unique_ptr<Skeleton> o_skeleton;
|
||||
/* Builds output skeleton.
|
||||
3 joints
|
||||
|
||||
*
|
||||
|
|
||||
root
|
||||
/ \
|
||||
j0 j1
|
||||
*/
|
||||
{
|
||||
RawSkeleton raw_skeleton;
|
||||
raw_skeleton.roots.resize(1);
|
||||
RawSkeleton::Joint& root = raw_skeleton.roots[0];
|
||||
root.name = "root";
|
||||
|
||||
root.children.resize(2);
|
||||
root.children[0].name = "j0";
|
||||
root.children[1].name = "j1";
|
||||
|
||||
EXPECT_TRUE(raw_skeleton.Validate());
|
||||
EXPECT_EQ(raw_skeleton.num_joints(), 3);
|
||||
|
||||
SkeletonBuilder builder;
|
||||
o_skeleton = builder(raw_skeleton);
|
||||
ASSERT_TRUE(o_skeleton);
|
||||
}
|
||||
|
||||
for (int e = 0; e < 2; ++e) {
|
||||
ozz::Endianness endianess = e == 0 ? ozz::kBigEndian : ozz::kLittleEndian;
|
||||
ozz::io::MemoryStream stream;
|
||||
|
||||
// Streams out.
|
||||
ozz::io::OArchive o(&stream, endianess);
|
||||
o << *o_skeleton;
|
||||
|
||||
// Streams in.
|
||||
stream.Seek(0, ozz::io::Stream::kSet);
|
||||
ozz::io::IArchive ia(&stream);
|
||||
|
||||
Skeleton i_skeleton;
|
||||
ia >> i_skeleton;
|
||||
|
||||
// Compares skeletons.
|
||||
EXPECT_EQ(o_skeleton->num_joints(), i_skeleton.num_joints());
|
||||
for (int i = 0; i < i_skeleton.num_joints(); ++i) {
|
||||
EXPECT_EQ(i_skeleton.joint_parents()[i],
|
||||
o_skeleton->joint_parents()[i]);
|
||||
EXPECT_STREQ(i_skeleton.joint_names()[i], o_skeleton->joint_names()[i]);
|
||||
}
|
||||
for (int i = 0; i < (i_skeleton.num_joints() + 3) / 4; ++i) {
|
||||
EXPECT_TRUE(ozz::math::AreAllTrue(
|
||||
i_skeleton.joint_bind_poses()[i].translation ==
|
||||
o_skeleton->joint_bind_poses()[i].translation));
|
||||
EXPECT_TRUE(ozz::math::AreAllTrue(
|
||||
i_skeleton.joint_bind_poses()[i].rotation ==
|
||||
o_skeleton->joint_bind_poses()[i].rotation));
|
||||
EXPECT_TRUE(
|
||||
ozz::math::AreAllTrue(i_skeleton.joint_bind_poses()[i].scale ==
|
||||
o_skeleton->joint_bind_poses()[i].scale));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(AlreadyInitialized, SkeletonSerialize) {
|
||||
ozz::unique_ptr<Skeleton> o_skeleton[2];
|
||||
/* Builds output skeleton.
|
||||
3 joints
|
||||
|
||||
*
|
||||
|
|
||||
root
|
||||
/ \
|
||||
j0 j1
|
||||
*/
|
||||
{
|
||||
RawSkeleton raw_skeleton;
|
||||
raw_skeleton.roots.resize(1);
|
||||
raw_skeleton.roots[0].name = "root0";
|
||||
|
||||
SkeletonBuilder builder;
|
||||
o_skeleton[0] = builder(raw_skeleton);
|
||||
ASSERT_TRUE(o_skeleton[0]);
|
||||
|
||||
raw_skeleton.roots.resize(2);
|
||||
raw_skeleton.roots[1].name = "root1";
|
||||
o_skeleton[1] = builder(raw_skeleton);
|
||||
ASSERT_TRUE(o_skeleton[1]);
|
||||
}
|
||||
|
||||
{
|
||||
// Streams out.
|
||||
ozz::io::MemoryStream stream;
|
||||
ozz::io::OArchive o(&stream);
|
||||
o << *o_skeleton[0];
|
||||
o << *o_skeleton[1];
|
||||
|
||||
// Streams in.
|
||||
stream.Seek(0, ozz::io::Stream::kSet);
|
||||
ozz::io::IArchive i(&stream);
|
||||
|
||||
// Reads and check the first skeleton.
|
||||
Skeleton i_skeleton;
|
||||
i >> i_skeleton;
|
||||
ASSERT_EQ(o_skeleton[0]->num_joints(), 1);
|
||||
EXPECT_STREQ(i_skeleton.joint_names()[0], o_skeleton[0]->joint_names()[0]);
|
||||
|
||||
// Reuse the skeleton a second time.
|
||||
i >> i_skeleton;
|
||||
ASSERT_EQ(o_skeleton[1]->num_joints(), 2);
|
||||
EXPECT_STREQ(i_skeleton.joint_names()[0], o_skeleton[1]->joint_names()[0]);
|
||||
EXPECT_STREQ(i_skeleton.joint_names()[1], o_skeleton[1]->joint_names()[1]);
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
//----------------------------------------------------------------------------//
|
||||
// //
|
||||
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
|
||||
// and distributed under the MIT License (MIT). //
|
||||
// //
|
||||
// Copyright (c) Guillaume Blanc //
|
||||
// //
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a //
|
||||
// copy of this software and associated documentation files (the "Software"), //
|
||||
// to deal in the Software without restriction, including without limitation //
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
|
||||
// and/or sell copies of the Software, and to permit persons to whom the //
|
||||
// Software is furnished to do so, subject to the following conditions: //
|
||||
// //
|
||||
// The above copyright notice and this permission notice shall be included in //
|
||||
// all copies or substantial portions of the Software. //
|
||||
// //
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
|
||||
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
|
||||
// DEALINGS IN THE SOFTWARE. //
|
||||
// //
|
||||
//----------------------------------------------------------------------------//
|
||||
|
||||
#include "ozz/animation/runtime/skeleton.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "ozz/base/io/archive.h"
|
||||
#include "ozz/base/io/stream.h"
|
||||
|
||||
#include "ozz/base/log.h"
|
||||
#include "ozz/options/options.h"
|
||||
|
||||
OZZ_OPTIONS_DECLARE_STRING(file, "Specifies input file", "", true)
|
||||
OZZ_OPTIONS_DECLARE_INT(joints, "Number of joints", 0, true)
|
||||
OZZ_OPTIONS_DECLARE_STRING(root_name, "Name of the root joint", "", true)
|
||||
|
||||
int main(int _argc, char** _argv) {
|
||||
// Parses arguments.
|
||||
testing::InitGoogleTest(&_argc, _argv);
|
||||
ozz::options::ParseResult parse_result = ozz::options::ParseCommandLine(
|
||||
_argc, _argv, "1.0",
|
||||
"Test Skeleton archive versioning retrocompatibility");
|
||||
if (parse_result != ozz::options::kSuccess) {
|
||||
return parse_result == ozz::options::kExitSuccess ? EXIT_SUCCESS
|
||||
: EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
TEST(Versioning, SkeletonSerialize) {
|
||||
// Open the file.
|
||||
const char* filename = OPTIONS_file;
|
||||
ozz::io::File file(filename, "rb");
|
||||
ASSERT_TRUE(file.opened());
|
||||
|
||||
// Open archive and test object tag.
|
||||
ozz::io::IArchive archive(&file);
|
||||
ASSERT_TRUE(archive.TestTag<ozz::animation::Skeleton>());
|
||||
|
||||
// Read the object.
|
||||
ozz::animation::Skeleton skeleton;
|
||||
archive >> skeleton;
|
||||
|
||||
// More testing
|
||||
EXPECT_EQ(skeleton.num_joints(), OPTIONS_joints);
|
||||
if (skeleton.num_joints()) {
|
||||
EXPECT_STREQ(skeleton.joint_names()[0], OPTIONS_root_name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,417 @@
|
||||
//----------------------------------------------------------------------------//
|
||||
// //
|
||||
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
|
||||
// and distributed under the MIT License (MIT). //
|
||||
// //
|
||||
// Copyright (c) Guillaume Blanc //
|
||||
// //
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a //
|
||||
// copy of this software and associated documentation files (the "Software"), //
|
||||
// to deal in the Software without restriction, including without limitation //
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
|
||||
// and/or sell copies of the Software, and to permit persons to whom the //
|
||||
// Software is furnished to do so, subject to the following conditions: //
|
||||
// //
|
||||
// The above copyright notice and this permission notice shall be included in //
|
||||
// all copies or substantial portions of the Software. //
|
||||
// //
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
|
||||
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
|
||||
// DEALINGS IN THE SOFTWARE. //
|
||||
// //
|
||||
//----------------------------------------------------------------------------//
|
||||
|
||||
#include "ozz/animation/offline/raw_skeleton.h"
|
||||
#include "ozz/animation/offline/skeleton_builder.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "ozz/base/gtest_helper.h"
|
||||
#include "ozz/base/maths/gtest_math_helper.h"
|
||||
|
||||
#include "ozz/animation/runtime/skeleton.h"
|
||||
#include "ozz/animation/runtime/skeleton_utils.h"
|
||||
#include "ozz/base/memory/unique_ptr.h"
|
||||
|
||||
using ozz::animation::Skeleton;
|
||||
using ozz::animation::offline::RawSkeleton;
|
||||
using ozz::animation::offline::SkeletonBuilder;
|
||||
|
||||
TEST(JointBindPose, SkeletonUtils) {
|
||||
// Instantiates a builder objects with default parameters.
|
||||
SkeletonBuilder builder;
|
||||
|
||||
RawSkeleton raw_skeleton;
|
||||
raw_skeleton.roots.resize(1);
|
||||
RawSkeleton::Joint& r = raw_skeleton.roots[0];
|
||||
r.name = "r0";
|
||||
r.transform.translation = ozz::math::Float3::x_axis();
|
||||
r.transform.rotation = ozz::math::Quaternion::identity();
|
||||
r.transform.scale = ozz::math::Float3::zero();
|
||||
|
||||
r.children.resize(2);
|
||||
RawSkeleton::Joint& c0 = r.children[0];
|
||||
c0.name = "j0";
|
||||
c0.transform.translation = ozz::math::Float3::y_axis();
|
||||
c0.transform.rotation = -ozz::math::Quaternion::identity();
|
||||
c0.transform.scale = -ozz::math::Float3::one();
|
||||
|
||||
RawSkeleton::Joint& c1 = r.children[1];
|
||||
c1.name = "j1";
|
||||
c1.transform.translation = ozz::math::Float3::z_axis();
|
||||
c1.transform.rotation = Conjugate(ozz::math::Quaternion::identity());
|
||||
c1.transform.scale = ozz::math::Float3::one();
|
||||
|
||||
EXPECT_TRUE(raw_skeleton.Validate());
|
||||
EXPECT_EQ(raw_skeleton.num_joints(), 3);
|
||||
|
||||
ozz::unique_ptr<Skeleton> skeleton(builder(raw_skeleton));
|
||||
ASSERT_TRUE(skeleton);
|
||||
EXPECT_EQ(skeleton->num_joints(), 3);
|
||||
|
||||
// Out of range.
|
||||
EXPECT_ASSERTION(GetJointLocalBindPose(*skeleton, 3),
|
||||
"Joint index out of range.");
|
||||
|
||||
const ozz::math::Transform bind_pose0 = GetJointLocalBindPose(*skeleton, 0);
|
||||
EXPECT_FLOAT3_EQ(bind_pose0.translation, 1.f, 0.f, 0.f);
|
||||
EXPECT_QUATERNION_EQ(bind_pose0.rotation, 0.f, 0.f, 0.f, 1.f);
|
||||
EXPECT_FLOAT3_EQ(bind_pose0.scale, 0.f, 0.f, 0.f);
|
||||
|
||||
const ozz::math::Transform bind_pose1 = GetJointLocalBindPose(*skeleton, 1);
|
||||
EXPECT_FLOAT3_EQ(bind_pose1.translation, 0.f, 1.f, 0.f);
|
||||
EXPECT_QUATERNION_EQ(bind_pose1.rotation, 0.f, 0.f, 0.f, -1.f);
|
||||
EXPECT_FLOAT3_EQ(bind_pose1.scale, -1.f, -1.f, -1.f);
|
||||
|
||||
const ozz::math::Transform bind_pose2 = GetJointLocalBindPose(*skeleton, 2);
|
||||
EXPECT_FLOAT3_EQ(bind_pose2.translation, 0.f, 0.f, 1.f);
|
||||
EXPECT_QUATERNION_EQ(bind_pose2.rotation, -0.f, -0.f, -0.f, 1.f);
|
||||
EXPECT_FLOAT3_EQ(bind_pose2.scale, 1.f, 1.f, 1.f);
|
||||
}
|
||||
|
||||
/* Definition of the skeleton used by the tests.
|
||||
10 joints, 2 roots
|
||||
|
||||
*
|
||||
/ \
|
||||
j0 j8
|
||||
/ \ \
|
||||
j1 j4 j9
|
||||
| / \
|
||||
j2 j5 j6
|
||||
| |
|
||||
j3 j7
|
||||
*/
|
||||
|
||||
namespace {
|
||||
|
||||
class IterateDFFailTester {
|
||||
public:
|
||||
void operator()(int, int) { ASSERT_TRUE(false); }
|
||||
};
|
||||
|
||||
class IterateDFTester {
|
||||
public:
|
||||
IterateDFTester(const ozz::animation::Skeleton* _skeleton, int _start)
|
||||
: skeleton_(_skeleton), start_(_start), num_iterations_(0) {}
|
||||
|
||||
void operator()(int _current, int _parent) {
|
||||
const int joint = start_ + num_iterations_;
|
||||
EXPECT_EQ(joint, _current);
|
||||
EXPECT_EQ(skeleton_->joint_parents()[joint], _parent);
|
||||
++num_iterations_;
|
||||
}
|
||||
|
||||
int num_iterations() const { return num_iterations_; }
|
||||
|
||||
private:
|
||||
// Iterated skeleton.
|
||||
const ozz::animation::Skeleton* skeleton_;
|
||||
|
||||
// First joint to explore.
|
||||
int start_;
|
||||
|
||||
// Number of iterations completed.
|
||||
int num_iterations_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
TEST(InterateDF, SkeletonUtils) {
|
||||
// Instantiates a builder objects with default parameters.
|
||||
SkeletonBuilder builder;
|
||||
|
||||
RawSkeleton raw_skeleton;
|
||||
raw_skeleton.roots.resize(2);
|
||||
RawSkeleton::Joint& j0 = raw_skeleton.roots[0];
|
||||
j0.name = "j0";
|
||||
RawSkeleton::Joint& j8 = raw_skeleton.roots[1];
|
||||
j8.name = "j8";
|
||||
|
||||
j0.children.resize(2);
|
||||
j0.children[0].name = "j1";
|
||||
j0.children[1].name = "j4";
|
||||
|
||||
j0.children[0].children.resize(1);
|
||||
j0.children[0].children[0].name = "j2";
|
||||
|
||||
j0.children[0].children[0].children.resize(1);
|
||||
j0.children[0].children[0].children[0].name = "j3";
|
||||
|
||||
j0.children[1].children.resize(2);
|
||||
j0.children[1].children[0].name = "j5";
|
||||
j0.children[1].children[1].name = "j6";
|
||||
|
||||
j0.children[1].children[1].children.resize(1);
|
||||
j0.children[1].children[1].children[0].name = "j7";
|
||||
|
||||
j8.children.resize(1);
|
||||
j8.children[0].name = "j9";
|
||||
|
||||
EXPECT_TRUE(raw_skeleton.Validate());
|
||||
EXPECT_EQ(raw_skeleton.num_joints(), 10);
|
||||
|
||||
ozz::unique_ptr<Skeleton> skeleton(builder(raw_skeleton));
|
||||
ASSERT_TRUE(skeleton);
|
||||
EXPECT_EQ(skeleton->num_joints(), 10);
|
||||
|
||||
{
|
||||
IterateDFTester fct =
|
||||
IterateJointsDF(*skeleton, IterateDFTester(skeleton.get(), 0), -12);
|
||||
EXPECT_EQ(fct.num_iterations(), 10);
|
||||
}
|
||||
{
|
||||
IterateDFTester fct =
|
||||
IterateJointsDF(*skeleton, IterateDFTester(skeleton.get(), 0));
|
||||
EXPECT_EQ(fct.num_iterations(), 10);
|
||||
}
|
||||
{
|
||||
IterateDFTester fct = IterateJointsDF(
|
||||
*skeleton, IterateDFTester(skeleton.get(), 0), Skeleton::kNoParent);
|
||||
EXPECT_EQ(fct.num_iterations(), 10);
|
||||
}
|
||||
{
|
||||
IterateDFTester fct =
|
||||
IterateJointsDF(*skeleton, IterateDFTester(skeleton.get(), 0), 0);
|
||||
EXPECT_EQ(fct.num_iterations(), 8);
|
||||
}
|
||||
{
|
||||
IterateDFTester fct =
|
||||
IterateJointsDF(*skeleton, IterateDFTester(skeleton.get(), 1), 1);
|
||||
EXPECT_EQ(fct.num_iterations(), 3);
|
||||
}
|
||||
{
|
||||
IterateDFTester fct =
|
||||
IterateJointsDF(*skeleton, IterateDFTester(skeleton.get(), 2), 2);
|
||||
EXPECT_EQ(fct.num_iterations(), 2);
|
||||
}
|
||||
{
|
||||
IterateDFTester fct =
|
||||
IterateJointsDF(*skeleton, IterateDFTester(skeleton.get(), 3), 3);
|
||||
EXPECT_EQ(fct.num_iterations(), 1);
|
||||
}
|
||||
{
|
||||
IterateDFTester fct =
|
||||
IterateJointsDF(*skeleton, IterateDFTester(skeleton.get(), 4), 4);
|
||||
EXPECT_EQ(fct.num_iterations(), 4);
|
||||
}
|
||||
{
|
||||
IterateDFTester fct =
|
||||
IterateJointsDF(*skeleton, IterateDFTester(skeleton.get(), 5), 5);
|
||||
EXPECT_EQ(fct.num_iterations(), 1);
|
||||
}
|
||||
{
|
||||
IterateDFTester fct =
|
||||
IterateJointsDF(*skeleton, IterateDFTester(skeleton.get(), 6), 6);
|
||||
EXPECT_EQ(fct.num_iterations(), 2);
|
||||
}
|
||||
{
|
||||
IterateDFTester fct =
|
||||
IterateJointsDF(*skeleton, IterateDFTester(skeleton.get(), 7), 7);
|
||||
EXPECT_EQ(fct.num_iterations(), 1);
|
||||
}
|
||||
{
|
||||
IterateDFTester fct =
|
||||
IterateJointsDF(*skeleton, IterateDFTester(skeleton.get(), 8), 8);
|
||||
EXPECT_EQ(fct.num_iterations(), 2);
|
||||
}
|
||||
{
|
||||
IterateDFTester fct =
|
||||
IterateJointsDF(*skeleton, IterateDFTester(skeleton.get(), 9), 9);
|
||||
EXPECT_EQ(fct.num_iterations(), 1);
|
||||
}
|
||||
IterateJointsDF(*skeleton, IterateDFFailTester(), 10);
|
||||
IterateJointsDF(*skeleton, IterateDFFailTester(), 99);
|
||||
}
|
||||
|
||||
TEST(InterateDFEmpty, SkeletonUtils) {
|
||||
ozz::animation::Skeleton empty;
|
||||
IterateJointsDF(empty, IterateDFFailTester(),
|
||||
ozz::animation::Skeleton::kNoParent);
|
||||
IterateJointsDF(empty, IterateDFFailTester(), 0);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class IterateDFReverseTester {
|
||||
public:
|
||||
IterateDFReverseTester(const ozz::animation::Skeleton* _skeleton)
|
||||
: skeleton_(_skeleton), num_iterations_(0) {}
|
||||
|
||||
void operator()(int _current, int _parent) {
|
||||
if (num_iterations_ == 0) {
|
||||
EXPECT_TRUE(ozz::animation::IsLeaf(*skeleton_, _current));
|
||||
}
|
||||
|
||||
// A joint is traversed once.
|
||||
ozz::vector<int>::const_iterator itc =
|
||||
std::find(processed_joints_.begin(), processed_joints_.end(), _current);
|
||||
EXPECT_TRUE(itc == processed_joints_.end());
|
||||
|
||||
// A parent can't be traversed before a child.
|
||||
ozz::vector<int>::const_iterator itp =
|
||||
std::find(processed_joints_.begin(), processed_joints_.end(), _parent);
|
||||
EXPECT_TRUE(itp == processed_joints_.end());
|
||||
|
||||
// joint processed
|
||||
processed_joints_.push_back(_current);
|
||||
|
||||
// Validates parent id.
|
||||
EXPECT_EQ(skeleton_->joint_parents()[_current], _parent);
|
||||
|
||||
++num_iterations_;
|
||||
}
|
||||
|
||||
int num_iterations() const { return num_iterations_; }
|
||||
|
||||
private:
|
||||
// Iterated skeleton.
|
||||
const ozz::animation::Skeleton* skeleton_;
|
||||
|
||||
// Number of iterations completed.
|
||||
int num_iterations_;
|
||||
|
||||
// Already processed joints
|
||||
ozz::vector<int> processed_joints_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
TEST(InterateDFReverse, SkeletonUtils) {
|
||||
// Instantiates a builder objects with default parameters.
|
||||
SkeletonBuilder builder;
|
||||
|
||||
RawSkeleton raw_skeleton;
|
||||
raw_skeleton.roots.resize(2);
|
||||
RawSkeleton::Joint& j0 = raw_skeleton.roots[0];
|
||||
j0.name = "j0";
|
||||
RawSkeleton::Joint& j8 = raw_skeleton.roots[1];
|
||||
j8.name = "j8";
|
||||
|
||||
j0.children.resize(2);
|
||||
j0.children[0].name = "j1";
|
||||
j0.children[1].name = "j4";
|
||||
|
||||
j0.children[0].children.resize(1);
|
||||
j0.children[0].children[0].name = "j2";
|
||||
|
||||
j0.children[0].children[0].children.resize(1);
|
||||
j0.children[0].children[0].children[0].name = "j3";
|
||||
|
||||
j0.children[1].children.resize(2);
|
||||
j0.children[1].children[0].name = "j5";
|
||||
j0.children[1].children[1].name = "j6";
|
||||
|
||||
j0.children[1].children[1].children.resize(1);
|
||||
j0.children[1].children[1].children[0].name = "j7";
|
||||
|
||||
j8.children.resize(1);
|
||||
j8.children[0].name = "j9";
|
||||
|
||||
EXPECT_TRUE(raw_skeleton.Validate());
|
||||
EXPECT_EQ(raw_skeleton.num_joints(), 10);
|
||||
|
||||
ozz::unique_ptr<Skeleton> skeleton(builder(raw_skeleton));
|
||||
ASSERT_TRUE(skeleton);
|
||||
EXPECT_EQ(skeleton->num_joints(), 10);
|
||||
|
||||
{
|
||||
IterateDFReverseTester fct = IterateJointsDFReverse(
|
||||
*skeleton, IterateDFReverseTester(skeleton.get()));
|
||||
EXPECT_EQ(fct.num_iterations(), 10);
|
||||
}
|
||||
}
|
||||
|
||||
/* Definition of the skeleton used by the tests.
|
||||
10 joints, 2 roots
|
||||
|
||||
*
|
||||
/ \
|
||||
j0 j8
|
||||
/ \ \
|
||||
j1 j4 j9
|
||||
| / \
|
||||
j2 j5 j6
|
||||
| |
|
||||
j3 j7
|
||||
*/
|
||||
|
||||
TEST(IsLeaf, SkeletonUtils) {
|
||||
// Instantiates a builder objects with default parameters.
|
||||
SkeletonBuilder builder;
|
||||
|
||||
RawSkeleton raw_skeleton;
|
||||
raw_skeleton.roots.resize(2);
|
||||
RawSkeleton::Joint& j0 = raw_skeleton.roots[0];
|
||||
j0.name = "j0";
|
||||
RawSkeleton::Joint& j8 = raw_skeleton.roots[1];
|
||||
j8.name = "j8";
|
||||
|
||||
j0.children.resize(2);
|
||||
j0.children[0].name = "j1";
|
||||
j0.children[1].name = "j4";
|
||||
|
||||
j0.children[0].children.resize(1);
|
||||
j0.children[0].children[0].name = "j2";
|
||||
|
||||
j0.children[0].children[0].children.resize(1);
|
||||
j0.children[0].children[0].children[0].name = "j3";
|
||||
|
||||
j0.children[1].children.resize(2);
|
||||
j0.children[1].children[0].name = "j5";
|
||||
j0.children[1].children[1].name = "j6";
|
||||
|
||||
j0.children[1].children[1].children.resize(1);
|
||||
j0.children[1].children[1].children[0].name = "j7";
|
||||
|
||||
j8.children.resize(1);
|
||||
j8.children[0].name = "j9";
|
||||
|
||||
EXPECT_TRUE(raw_skeleton.Validate());
|
||||
EXPECT_EQ(raw_skeleton.num_joints(), 10);
|
||||
|
||||
ozz::unique_ptr<Skeleton> skeleton(builder(raw_skeleton));
|
||||
ASSERT_TRUE(skeleton);
|
||||
EXPECT_EQ(skeleton->num_joints(), 10);
|
||||
|
||||
// Out of bound
|
||||
EXPECT_ASSERTION(IsLeaf(*skeleton, 10), "_joint index out of range");
|
||||
EXPECT_ASSERTION(IsLeaf(*skeleton, 93), "_joint index out of range");
|
||||
EXPECT_ASSERTION(IsLeaf(*skeleton, ozz::animation::Skeleton::kNoParent),
|
||||
"_joint index out of range");
|
||||
|
||||
EXPECT_FALSE(IsLeaf(*skeleton, 0));
|
||||
EXPECT_FALSE(IsLeaf(*skeleton, 1));
|
||||
EXPECT_FALSE(IsLeaf(*skeleton, 2));
|
||||
EXPECT_TRUE(IsLeaf(*skeleton, 3));
|
||||
EXPECT_FALSE(IsLeaf(*skeleton, 4));
|
||||
EXPECT_TRUE(IsLeaf(*skeleton, 5));
|
||||
EXPECT_FALSE(IsLeaf(*skeleton, 6));
|
||||
EXPECT_TRUE(IsLeaf(*skeleton, 7));
|
||||
EXPECT_FALSE(IsLeaf(*skeleton, 8));
|
||||
EXPECT_TRUE(IsLeaf(*skeleton, 9));
|
||||
}
|
||||
@@ -0,0 +1,454 @@
|
||||
//----------------------------------------------------------------------------//
|
||||
// //
|
||||
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
|
||||
// and distributed under the MIT License (MIT). //
|
||||
// //
|
||||
// Copyright (c) Guillaume Blanc //
|
||||
// //
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a //
|
||||
// copy of this software and associated documentation files (the "Software"), //
|
||||
// to deal in the Software without restriction, including without limitation //
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
|
||||
// and/or sell copies of the Software, and to permit persons to whom the //
|
||||
// Software is furnished to do so, subject to the following conditions: //
|
||||
// //
|
||||
// The above copyright notice and this permission notice shall be included in //
|
||||
// all copies or substantial portions of the Software. //
|
||||
// //
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
|
||||
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
|
||||
// DEALINGS IN THE SOFTWARE. //
|
||||
// //
|
||||
//----------------------------------------------------------------------------//
|
||||
|
||||
#include "ozz/animation/runtime/track.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "ozz/base/maths/gtest_math_helper.h"
|
||||
|
||||
#include "ozz/base/io/archive.h"
|
||||
#include "ozz/base/io/stream.h"
|
||||
#include "ozz/base/memory/unique_ptr.h"
|
||||
|
||||
#include "ozz/animation/runtime/track_sampling_job.h"
|
||||
|
||||
#include "ozz/animation/offline/raw_track.h"
|
||||
#include "ozz/animation/offline/track_builder.h"
|
||||
|
||||
using ozz::animation::FloatTrack;
|
||||
using ozz::animation::FloatTrackSamplingJob;
|
||||
using ozz::animation::Float2Track;
|
||||
using ozz::animation::Float2TrackSamplingJob;
|
||||
using ozz::animation::Float3Track;
|
||||
using ozz::animation::Float3TrackSamplingJob;
|
||||
using ozz::animation::Float4Track;
|
||||
using ozz::animation::Float4TrackSamplingJob;
|
||||
using ozz::animation::QuaternionTrack;
|
||||
using ozz::animation::QuaternionTrackSamplingJob;
|
||||
using ozz::animation::offline::RawFloatTrack;
|
||||
using ozz::animation::offline::RawFloat2Track;
|
||||
using ozz::animation::offline::RawFloat3Track;
|
||||
using ozz::animation::offline::RawFloat4Track;
|
||||
using ozz::animation::offline::RawQuaternionTrack;
|
||||
using ozz::animation::offline::RawTrackInterpolation;
|
||||
using ozz::animation::offline::TrackBuilder;
|
||||
|
||||
TEST(Empty, TrackSerialize) {
|
||||
ozz::io::MemoryStream stream;
|
||||
|
||||
// Streams out.
|
||||
ozz::io::OArchive o(&stream, ozz::GetNativeEndianness());
|
||||
|
||||
FloatTrack o_track;
|
||||
o << o_track;
|
||||
|
||||
// Streams in.
|
||||
stream.Seek(0, ozz::io::Stream::kSet);
|
||||
ozz::io::IArchive i(&stream);
|
||||
|
||||
FloatTrack i_track;
|
||||
i >> i_track;
|
||||
|
||||
EXPECT_EQ(o_track.size(), i_track.size());
|
||||
}
|
||||
|
||||
TEST(Name, TrackSerialize) {
|
||||
ozz::io::MemoryStream stream;
|
||||
|
||||
// Instantiates a builder objects with default parameters.
|
||||
TrackBuilder builder;
|
||||
|
||||
{ // No name
|
||||
RawFloatTrack raw_float_track;
|
||||
|
||||
ozz::unique_ptr<FloatTrack> o_track(builder(raw_float_track));
|
||||
ASSERT_TRUE(o_track);
|
||||
|
||||
// Streams out.
|
||||
{
|
||||
stream.Seek(0, ozz::io::Stream::kSet);
|
||||
ozz::io::OArchive o(&stream, ozz::GetNativeEndianness());
|
||||
o << *o_track;
|
||||
}
|
||||
|
||||
// Streams in.
|
||||
stream.Seek(0, ozz::io::Stream::kSet);
|
||||
ozz::io::IArchive i(&stream);
|
||||
|
||||
FloatTrack i_track;
|
||||
i >> i_track;
|
||||
|
||||
EXPECT_STREQ(o_track->name(), i_track.name());
|
||||
}
|
||||
|
||||
{ // A name
|
||||
RawFloatTrack raw_float_track;
|
||||
raw_float_track.name = "test name";
|
||||
|
||||
ozz::unique_ptr<FloatTrack> o_track(builder(raw_float_track));
|
||||
ASSERT_TRUE(o_track);
|
||||
|
||||
// Streams out.
|
||||
{
|
||||
stream.Seek(0, ozz::io::Stream::kSet);
|
||||
ozz::io::OArchive o(&stream, ozz::GetNativeEndianness());
|
||||
o << *o_track;
|
||||
}
|
||||
|
||||
// Streams in.
|
||||
stream.Seek(0, ozz::io::Stream::kSet);
|
||||
ozz::io::IArchive i(&stream);
|
||||
|
||||
FloatTrack i_track;
|
||||
i >> i_track;
|
||||
|
||||
EXPECT_STREQ(o_track->name(), i_track.name());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(FilledFloat, TrackSerialize) {
|
||||
// Builds a valid animation.
|
||||
ozz::unique_ptr<FloatTrack> o_track;
|
||||
{
|
||||
TrackBuilder builder;
|
||||
RawFloatTrack raw_float_track;
|
||||
|
||||
const RawFloatTrack::Keyframe key0 = {RawTrackInterpolation::kLinear, 0.f,
|
||||
0.f};
|
||||
raw_float_track.keyframes.push_back(key0);
|
||||
const RawFloatTrack::Keyframe key1 = {RawTrackInterpolation::kStep, .5f,
|
||||
46.f};
|
||||
raw_float_track.keyframes.push_back(key1);
|
||||
const RawFloatTrack::Keyframe key2 = {RawTrackInterpolation::kLinear, .7f,
|
||||
0.f};
|
||||
raw_float_track.keyframes.push_back(key2);
|
||||
|
||||
// Builds track
|
||||
o_track = builder(raw_float_track);
|
||||
ASSERT_TRUE(o_track);
|
||||
}
|
||||
|
||||
for (int e = 0; e < 2; ++e) {
|
||||
ozz::Endianness endianess = e == 0 ? ozz::kBigEndian : ozz::kLittleEndian;
|
||||
ozz::io::MemoryStream stream;
|
||||
|
||||
// Streams out.
|
||||
ozz::io::OArchive o(&stream, endianess);
|
||||
o << *o_track;
|
||||
|
||||
// Streams in.
|
||||
stream.Seek(0, ozz::io::Stream::kSet);
|
||||
ozz::io::IArchive i(&stream);
|
||||
|
||||
FloatTrack i_track;
|
||||
i >> i_track;
|
||||
|
||||
EXPECT_EQ(o_track->size(), i_track.size());
|
||||
|
||||
// Samples and compares the two animations
|
||||
FloatTrackSamplingJob sampling;
|
||||
sampling.track = &i_track;
|
||||
float result;
|
||||
sampling.result = &result;
|
||||
|
||||
sampling.ratio = 0.f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT_EQ(result, 0.f);
|
||||
|
||||
sampling.ratio = .5f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT_EQ(result, 46.f);
|
||||
|
||||
sampling.ratio = 1.f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT_EQ(result, 0.f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(FilledFloat2, TrackSerialize) {
|
||||
TrackBuilder builder;
|
||||
RawFloat2Track raw_float2_track;
|
||||
|
||||
const RawFloat2Track::Keyframe key0 = {RawTrackInterpolation::kLinear, 0.f,
|
||||
ozz::math::Float2(0.f, 26.f)};
|
||||
raw_float2_track.keyframes.push_back(key0);
|
||||
const RawFloat2Track::Keyframe key1 = {RawTrackInterpolation::kStep, .5f,
|
||||
ozz::math::Float2(46.f, 0.f)};
|
||||
raw_float2_track.keyframes.push_back(key1);
|
||||
const RawFloat2Track::Keyframe key2 = {RawTrackInterpolation::kLinear, .7f,
|
||||
ozz::math::Float2(0.f, 5.f)};
|
||||
raw_float2_track.keyframes.push_back(key2);
|
||||
|
||||
// Builds track
|
||||
ozz::unique_ptr<Float2Track> o_track(builder(raw_float2_track));
|
||||
ASSERT_TRUE(o_track);
|
||||
|
||||
ozz::io::MemoryStream stream;
|
||||
|
||||
// Streams out.
|
||||
ozz::io::OArchive o(&stream);
|
||||
o << *o_track;
|
||||
|
||||
// Streams in.
|
||||
stream.Seek(0, ozz::io::Stream::kSet);
|
||||
ozz::io::IArchive i(&stream);
|
||||
|
||||
Float2Track i_track;
|
||||
i >> i_track;
|
||||
|
||||
EXPECT_EQ(o_track->size(), i_track.size());
|
||||
|
||||
// Samples and compares the two animations
|
||||
Float2TrackSamplingJob sampling;
|
||||
sampling.track = &i_track;
|
||||
ozz::math::Float2 result;
|
||||
sampling.result = &result;
|
||||
|
||||
sampling.ratio = 0.f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT2_EQ(result, 0.f, 26.f);
|
||||
|
||||
sampling.ratio = .5f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT2_EQ(result, 46.f, 0.f);
|
||||
|
||||
sampling.ratio = 1.f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT2_EQ(result, 0.f, 5.f);
|
||||
}
|
||||
|
||||
TEST(FilledFloat3, TrackSerialize) {
|
||||
TrackBuilder builder;
|
||||
RawFloat3Track raw_float3_track;
|
||||
|
||||
const RawFloat3Track::Keyframe key0 = {RawTrackInterpolation::kLinear, 0.f,
|
||||
ozz::math::Float3(0.f, 26.f, 93.f)};
|
||||
raw_float3_track.keyframes.push_back(key0);
|
||||
const RawFloat3Track::Keyframe key1 = {RawTrackInterpolation::kStep, .5f,
|
||||
ozz::math::Float3(46.f, 0.f, 25.f)};
|
||||
raw_float3_track.keyframes.push_back(key1);
|
||||
const RawFloat3Track::Keyframe key2 = {RawTrackInterpolation::kLinear, .7f,
|
||||
ozz::math::Float3(0.f, 5.f, 0.f)};
|
||||
raw_float3_track.keyframes.push_back(key2);
|
||||
|
||||
// Builds track
|
||||
ozz::unique_ptr<Float3Track> o_track(builder(raw_float3_track));
|
||||
ASSERT_TRUE(o_track);
|
||||
|
||||
ozz::io::MemoryStream stream;
|
||||
|
||||
// Streams out.
|
||||
ozz::io::OArchive o(&stream);
|
||||
o << *o_track;
|
||||
|
||||
// Streams in.
|
||||
stream.Seek(0, ozz::io::Stream::kSet);
|
||||
ozz::io::IArchive i(&stream);
|
||||
|
||||
Float3Track i_track;
|
||||
i >> i_track;
|
||||
|
||||
EXPECT_EQ(o_track->size(), i_track.size());
|
||||
|
||||
// Samples and compares the two animations
|
||||
Float3TrackSamplingJob sampling;
|
||||
sampling.track = &i_track;
|
||||
ozz::math::Float3 result;
|
||||
sampling.result = &result;
|
||||
|
||||
sampling.ratio = 0.f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT3_EQ(result, 0.f, 26.f, 93.f);
|
||||
|
||||
sampling.ratio = .5f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT3_EQ(result, 46.f, 0.f, 25.f);
|
||||
|
||||
sampling.ratio = 1.f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT3_EQ(result, 0.f, 5.f, 0.f);
|
||||
}
|
||||
|
||||
TEST(FilledFloat4, TrackSerialize) {
|
||||
TrackBuilder builder;
|
||||
RawFloat4Track raw_float4_track;
|
||||
|
||||
const RawFloat4Track::Keyframe key0 = {
|
||||
RawTrackInterpolation::kLinear, 0.f,
|
||||
ozz::math::Float4(0.f, 26.f, 93.f, 5.f)};
|
||||
raw_float4_track.keyframes.push_back(key0);
|
||||
const RawFloat4Track::Keyframe key1 = {
|
||||
RawTrackInterpolation::kStep, .5f,
|
||||
ozz::math::Float4(46.f, 0.f, 25.f, 25.f)};
|
||||
raw_float4_track.keyframes.push_back(key1);
|
||||
const RawFloat4Track::Keyframe key2 = {RawTrackInterpolation::kLinear, .7f,
|
||||
ozz::math::Float4(0.f, 5.f, 0.f, 0.f)};
|
||||
raw_float4_track.keyframes.push_back(key2);
|
||||
|
||||
// Builds track
|
||||
ozz::unique_ptr<Float4Track> o_track(builder(raw_float4_track));
|
||||
ASSERT_TRUE(o_track);
|
||||
|
||||
ozz::io::MemoryStream stream;
|
||||
|
||||
// Streams out.
|
||||
ozz::io::OArchive o(&stream);
|
||||
o << *o_track;
|
||||
|
||||
// Streams in.
|
||||
stream.Seek(0, ozz::io::Stream::kSet);
|
||||
ozz::io::IArchive i(&stream);
|
||||
|
||||
Float4Track i_track;
|
||||
i >> i_track;
|
||||
|
||||
EXPECT_EQ(o_track->size(), i_track.size());
|
||||
|
||||
// Samples and compares the two animations
|
||||
Float4TrackSamplingJob sampling;
|
||||
sampling.track = &i_track;
|
||||
ozz::math::Float4 result;
|
||||
sampling.result = &result;
|
||||
|
||||
sampling.ratio = 0.f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT4_EQ(result, 0.f, 26.f, 93.f, 5.f);
|
||||
|
||||
sampling.ratio = .5f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT4_EQ(result, 46.f, 0.f, 25.f, 25.f);
|
||||
|
||||
sampling.ratio = 1.f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT4_EQ(result, 0.f, 5.f, 0.f, 0.f);
|
||||
}
|
||||
|
||||
TEST(FilledQuaternion, TrackSerialize) {
|
||||
TrackBuilder builder;
|
||||
RawQuaternionTrack raw_quat_track;
|
||||
|
||||
const RawQuaternionTrack::Keyframe key0 = {
|
||||
RawTrackInterpolation::kLinear, 0.f,
|
||||
ozz::math::Quaternion(0.f, .70710677f, 0.f, .70710677f)};
|
||||
raw_quat_track.keyframes.push_back(key0);
|
||||
const RawQuaternionTrack::Keyframe key1 = {
|
||||
RawTrackInterpolation::kStep, .5f,
|
||||
ozz::math::Quaternion(.61721331f, .15430345f, 0.f, .77151674f)};
|
||||
raw_quat_track.keyframes.push_back(key1);
|
||||
const RawQuaternionTrack::Keyframe key2 = {
|
||||
RawTrackInterpolation::kLinear, .7f,
|
||||
ozz::math::Quaternion(1.f, 0.f, 0.f, 0.f)};
|
||||
raw_quat_track.keyframes.push_back(key2);
|
||||
|
||||
// Builds track
|
||||
ozz::unique_ptr<QuaternionTrack> o_track(builder(raw_quat_track));
|
||||
ASSERT_TRUE(o_track);
|
||||
|
||||
ozz::io::MemoryStream stream;
|
||||
|
||||
// Streams out.
|
||||
ozz::io::OArchive o(&stream);
|
||||
o << *o_track;
|
||||
|
||||
// Streams in.
|
||||
stream.Seek(0, ozz::io::Stream::kSet);
|
||||
ozz::io::IArchive i(&stream);
|
||||
|
||||
QuaternionTrack i_track;
|
||||
i >> i_track;
|
||||
|
||||
EXPECT_EQ(o_track->size(), i_track.size());
|
||||
|
||||
// Samples and compares the two animations
|
||||
QuaternionTrackSamplingJob sampling;
|
||||
sampling.track = &i_track;
|
||||
ozz::math::Quaternion result;
|
||||
sampling.result = &result;
|
||||
|
||||
sampling.ratio = 0.f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_QUATERNION_EQ(result, 0.f, .70710677f, 0.f, .70710677f);
|
||||
|
||||
sampling.ratio = .5f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_QUATERNION_EQ(result, .61721331f, .15430345f, 0.f, .77151674f);
|
||||
|
||||
sampling.ratio = 1.f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_QUATERNION_EQ(result, 1.f, 0.f, 0.f, 0.f);
|
||||
}
|
||||
|
||||
TEST(AlreadyInitialized, TrackSerialize) {
|
||||
ozz::io::MemoryStream stream;
|
||||
|
||||
{
|
||||
ozz::io::OArchive o(&stream);
|
||||
|
||||
TrackBuilder builder;
|
||||
RawFloatTrack raw_float_track;
|
||||
|
||||
const RawFloatTrack::Keyframe key0 = {RawTrackInterpolation::kLinear, 0.f,
|
||||
0.f};
|
||||
raw_float_track.keyframes.push_back(key0);
|
||||
const RawFloatTrack::Keyframe key1 = {RawTrackInterpolation::kStep, .5f,
|
||||
46.f};
|
||||
raw_float_track.keyframes.push_back(key1);
|
||||
const RawFloatTrack::Keyframe key2 = {RawTrackInterpolation::kLinear, .7f,
|
||||
0.f};
|
||||
raw_float_track.keyframes.push_back(key2);
|
||||
|
||||
// Builds track
|
||||
ozz::unique_ptr<FloatTrack> o_track(builder(raw_float_track));
|
||||
ASSERT_TRUE(o_track);
|
||||
|
||||
o << *o_track;
|
||||
|
||||
// Builds 2nd track
|
||||
const RawFloatTrack::Keyframe key3 = {RawTrackInterpolation::kStep, .9f,
|
||||
46.f};
|
||||
raw_float_track.keyframes.push_back(key3);
|
||||
|
||||
o_track = builder(raw_float_track);
|
||||
ASSERT_TRUE(o_track);
|
||||
o << *o_track;
|
||||
}
|
||||
|
||||
{
|
||||
// Streams in.
|
||||
stream.Seek(0, ozz::io::Stream::kSet);
|
||||
ozz::io::IArchive i(&stream);
|
||||
|
||||
// Reads and check the first animation.
|
||||
FloatTrack i_track;
|
||||
i >> i_track;
|
||||
const size_t size = i_track.size();
|
||||
|
||||
// Reuse the animation a second ratio.
|
||||
i >> i_track;
|
||||
ASSERT_TRUE(i_track.size() > size);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,467 @@
|
||||
//----------------------------------------------------------------------------//
|
||||
// //
|
||||
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
|
||||
// and distributed under the MIT License (MIT). //
|
||||
// //
|
||||
// Copyright (c) Guillaume Blanc //
|
||||
// //
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a //
|
||||
// copy of this software and associated documentation files (the "Software"), //
|
||||
// to deal in the Software without restriction, including without limitation //
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
|
||||
// and/or sell copies of the Software, and to permit persons to whom the //
|
||||
// Software is furnished to do so, subject to the following conditions: //
|
||||
// //
|
||||
// The above copyright notice and this permission notice shall be included in //
|
||||
// all copies or substantial portions of the Software. //
|
||||
// //
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
|
||||
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
|
||||
// DEALINGS IN THE SOFTWARE. //
|
||||
// //
|
||||
//----------------------------------------------------------------------------//
|
||||
|
||||
#include "ozz/animation/runtime/track_sampling_job.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "ozz/base/maths/gtest_math_helper.h"
|
||||
#include "ozz/base/memory/unique_ptr.h"
|
||||
|
||||
#include "ozz/animation/offline/raw_track.h"
|
||||
#include "ozz/animation/offline/track_builder.h"
|
||||
|
||||
#include "ozz/animation/runtime/track.h"
|
||||
|
||||
using ozz::animation::FloatTrack;
|
||||
using ozz::animation::Float2Track;
|
||||
using ozz::animation::Float3Track;
|
||||
using ozz::animation::Float4Track;
|
||||
using ozz::animation::QuaternionTrack;
|
||||
using ozz::animation::FloatTrackSamplingJob;
|
||||
using ozz::animation::offline::RawFloatTrack;
|
||||
using ozz::animation::offline::TrackBuilder;
|
||||
using ozz::animation::offline::RawTrackInterpolation;
|
||||
|
||||
TEST(JobValidity, TrackSamplingJob) {
|
||||
// Instantiates a builder objects with default parameters.
|
||||
TrackBuilder builder;
|
||||
|
||||
// Building default RawFloatTrack succeeds.
|
||||
RawFloatTrack raw_float_track;
|
||||
EXPECT_TRUE(raw_float_track.Validate());
|
||||
|
||||
// Builds track
|
||||
ozz::unique_ptr<FloatTrack> track(builder(raw_float_track));
|
||||
ASSERT_TRUE(track);
|
||||
|
||||
{ // Empty/default job
|
||||
FloatTrackSamplingJob job;
|
||||
EXPECT_FALSE(job.Validate());
|
||||
EXPECT_FALSE(job.Run());
|
||||
}
|
||||
|
||||
{ // Invalid output
|
||||
FloatTrackSamplingJob job;
|
||||
job.track = track.get();
|
||||
EXPECT_FALSE(job.Validate());
|
||||
EXPECT_FALSE(job.Run());
|
||||
}
|
||||
|
||||
{ // Invalid track.
|
||||
FloatTrackSamplingJob job;
|
||||
float result;
|
||||
job.result = &result;
|
||||
EXPECT_FALSE(job.Validate());
|
||||
EXPECT_FALSE(job.Run());
|
||||
}
|
||||
|
||||
{ // Valid
|
||||
FloatTrackSamplingJob job;
|
||||
job.track = track.get();
|
||||
float result;
|
||||
job.result = &result;
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Default, TrackSamplingJob) {
|
||||
FloatTrack default_track;
|
||||
FloatTrackSamplingJob job;
|
||||
job.track = &default_track;
|
||||
float result = 1.f;
|
||||
job.result = &result;
|
||||
EXPECT_TRUE(job.Validate());
|
||||
EXPECT_TRUE(job.Run());
|
||||
EXPECT_FLOAT_EQ(result, 0.f);
|
||||
}
|
||||
|
||||
TEST(Bounds, TrackSamplingJob) {
|
||||
TrackBuilder builder;
|
||||
float result;
|
||||
|
||||
RawFloatTrack raw_float_track;
|
||||
|
||||
const RawFloatTrack::Keyframe key0 = {RawTrackInterpolation::kLinear, 0.f,
|
||||
0.f};
|
||||
raw_float_track.keyframes.push_back(key0);
|
||||
const RawFloatTrack::Keyframe key1 = {RawTrackInterpolation::kStep, .5f,
|
||||
46.f};
|
||||
raw_float_track.keyframes.push_back(key1);
|
||||
const RawFloatTrack::Keyframe key2 = {RawTrackInterpolation::kLinear, .7f,
|
||||
0.f};
|
||||
raw_float_track.keyframes.push_back(key2);
|
||||
|
||||
// Builds track
|
||||
ozz::unique_ptr<FloatTrack> track(builder(raw_float_track));
|
||||
ASSERT_TRUE(track);
|
||||
|
||||
// Samples to verify build output.
|
||||
FloatTrackSamplingJob sampling;
|
||||
sampling.track = track.get();
|
||||
sampling.result = &result;
|
||||
|
||||
sampling.ratio = 0.f - 1e-7f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT_EQ(result, 0.f);
|
||||
|
||||
sampling.ratio = 0.f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT_EQ(result, 0.f);
|
||||
|
||||
sampling.ratio = .5f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT_EQ(result, 46.f);
|
||||
|
||||
sampling.ratio = 1.f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT_EQ(result, 0.f);
|
||||
|
||||
sampling.ratio = 1.f + 1e-7f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT_EQ(result, 0.f);
|
||||
|
||||
sampling.ratio = 1.5f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT_EQ(result, 0.f);
|
||||
}
|
||||
|
||||
TEST(Float, TrackSamplingJob) {
|
||||
TrackBuilder builder;
|
||||
float result;
|
||||
|
||||
ozz::animation::offline::RawFloatTrack raw_track;
|
||||
|
||||
const ozz::animation::offline::RawFloatTrack::Keyframe key0 = {
|
||||
RawTrackInterpolation::kLinear, 0.f, 0.f};
|
||||
raw_track.keyframes.push_back(key0);
|
||||
const ozz::animation::offline::RawFloatTrack::Keyframe key1 = {
|
||||
RawTrackInterpolation::kStep, .5f, 4.6f};
|
||||
raw_track.keyframes.push_back(key1);
|
||||
const ozz::animation::offline::RawFloatTrack::Keyframe key2 = {
|
||||
RawTrackInterpolation::kLinear, .7f, 9.2f};
|
||||
raw_track.keyframes.push_back(key2);
|
||||
const ozz::animation::offline::RawFloatTrack::Keyframe key3 = {
|
||||
RawTrackInterpolation::kLinear, .9f, 0.f};
|
||||
raw_track.keyframes.push_back(key3);
|
||||
|
||||
// Builds track
|
||||
ozz::unique_ptr<FloatTrack> track(builder(raw_track));
|
||||
ASSERT_TRUE(track);
|
||||
|
||||
// Samples to verify build output.
|
||||
ozz::animation::FloatTrackSamplingJob sampling;
|
||||
sampling.track = track.get();
|
||||
sampling.result = &result;
|
||||
|
||||
sampling.ratio = 0.f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT_EQ(result, 0.f);
|
||||
|
||||
sampling.ratio = .25f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT_EQ(result, 2.3f);
|
||||
|
||||
sampling.ratio = .5f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT_EQ(result, 4.6f);
|
||||
|
||||
sampling.ratio = .6f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT_EQ(result, 4.6f);
|
||||
|
||||
sampling.ratio = .7f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT_EQ(result, 9.2f);
|
||||
|
||||
sampling.ratio = .8f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT_EQ(result, 4.6f);
|
||||
|
||||
sampling.ratio = .9f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT_EQ(result, 0.f);
|
||||
|
||||
sampling.ratio = 1.f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT_EQ(result, 0.f);
|
||||
}
|
||||
|
||||
TEST(Float2, TrackSamplingJob) {
|
||||
TrackBuilder builder;
|
||||
ozz::math::Float2 result;
|
||||
|
||||
ozz::animation::offline::RawFloat2Track raw_track;
|
||||
|
||||
const ozz::animation::offline::RawFloat2Track::Keyframe key0 = {
|
||||
RawTrackInterpolation::kLinear, 0.f, ozz::math::Float2(0.f, 0.f)};
|
||||
raw_track.keyframes.push_back(key0);
|
||||
const ozz::animation::offline::RawFloat2Track::Keyframe key1 = {
|
||||
RawTrackInterpolation::kStep, .5f, ozz::math::Float2(2.3f, 4.6f)};
|
||||
raw_track.keyframes.push_back(key1);
|
||||
const ozz::animation::offline::RawFloat2Track::Keyframe key2 = {
|
||||
RawTrackInterpolation::kLinear, .7f, ozz::math::Float2(4.6f, 9.2f)};
|
||||
raw_track.keyframes.push_back(key2);
|
||||
const ozz::animation::offline::RawFloat2Track::Keyframe key3 = {
|
||||
RawTrackInterpolation::kLinear, .9f, ozz::math::Float2(0.f, 0.f)};
|
||||
raw_track.keyframes.push_back(key3);
|
||||
|
||||
// Builds track
|
||||
ozz::unique_ptr<Float2Track> track(builder(raw_track));
|
||||
ASSERT_TRUE(track);
|
||||
|
||||
// Samples to verify build output.
|
||||
ozz::animation::Float2TrackSamplingJob sampling;
|
||||
sampling.track = track.get();
|
||||
sampling.result = &result;
|
||||
|
||||
sampling.ratio = 0.f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT2_EQ(result, 0.f, 0.f);
|
||||
|
||||
sampling.ratio = .25f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT2_EQ(result, 1.15f, 2.3f);
|
||||
|
||||
sampling.ratio = .5f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT2_EQ(result, 2.3f, 4.6f);
|
||||
|
||||
sampling.ratio = .6f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT2_EQ(result, 2.3f, 4.6f);
|
||||
|
||||
sampling.ratio = .7f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT2_EQ(result, 4.6f, 9.2f);
|
||||
|
||||
sampling.ratio = .8f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT2_EQ(result, 2.3f, 4.6f);
|
||||
|
||||
sampling.ratio = .9f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT2_EQ(result, 0.f, 0.f);
|
||||
|
||||
sampling.ratio = 1.f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT2_EQ(result, 0.f, 0.f);
|
||||
}
|
||||
|
||||
TEST(Float3, TrackSamplingJob) {
|
||||
TrackBuilder builder;
|
||||
ozz::math::Float3 result;
|
||||
|
||||
ozz::animation::offline::RawFloat3Track raw_track;
|
||||
|
||||
const ozz::animation::offline::RawFloat3Track::Keyframe key0 = {
|
||||
RawTrackInterpolation::kLinear, 0.f, ozz::math::Float3(0.f, 0.f, 0.f)};
|
||||
raw_track.keyframes.push_back(key0);
|
||||
const ozz::animation::offline::RawFloat3Track::Keyframe key1 = {
|
||||
RawTrackInterpolation::kStep, .5f, ozz::math::Float3(0.f, 2.3f, 4.6f)};
|
||||
raw_track.keyframes.push_back(key1);
|
||||
const ozz::animation::offline::RawFloat3Track::Keyframe key2 = {
|
||||
RawTrackInterpolation::kLinear, .7f, ozz::math::Float3(0.f, 4.6f, 9.2f)};
|
||||
raw_track.keyframes.push_back(key2);
|
||||
const ozz::animation::offline::RawFloat3Track::Keyframe key3 = {
|
||||
RawTrackInterpolation::kLinear, .9f, ozz::math::Float3(0.f, 0.f, 0.f)};
|
||||
raw_track.keyframes.push_back(key3);
|
||||
|
||||
// Builds track
|
||||
ozz::unique_ptr<Float3Track> track(builder(raw_track));
|
||||
ASSERT_TRUE(track);
|
||||
|
||||
// Samples to verify build output.
|
||||
ozz::animation::Float3TrackSamplingJob sampling;
|
||||
sampling.track = track.get();
|
||||
sampling.result = &result;
|
||||
|
||||
sampling.ratio = 0.f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT3_EQ(result, 0.f, 0.f, 0.f);
|
||||
|
||||
sampling.ratio = .25f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT3_EQ(result, 0.f, 1.15f, 2.3f);
|
||||
|
||||
sampling.ratio = .5f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT3_EQ(result, 0.f, 2.3f, 4.6f);
|
||||
|
||||
sampling.ratio = .6f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT3_EQ(result, 0.f, 2.3f, 4.6f);
|
||||
|
||||
sampling.ratio = .7f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT3_EQ(result, 0.f, 4.6f, 9.2f);
|
||||
|
||||
sampling.ratio = .8f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT3_EQ(result, 0.f, 2.3f, 4.6f);
|
||||
|
||||
sampling.ratio = .9f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT3_EQ(result, 0.f, 0.f, 0.f);
|
||||
|
||||
sampling.ratio = 1.f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT3_EQ(result, 0.f, 0.f, 0.f);
|
||||
}
|
||||
|
||||
TEST(Float4, TrackSamplingJob) {
|
||||
TrackBuilder builder;
|
||||
ozz::math::Float4 result;
|
||||
|
||||
ozz::animation::offline::RawFloat4Track raw_track;
|
||||
|
||||
const ozz::animation::offline::RawFloat4Track::Keyframe key0 = {
|
||||
RawTrackInterpolation::kLinear, 0.f,
|
||||
ozz::math::Float4(0.f, 0.f, 0.f, 0.f)};
|
||||
raw_track.keyframes.push_back(key0);
|
||||
const ozz::animation::offline::RawFloat4Track::Keyframe key1 = {
|
||||
RawTrackInterpolation::kStep, .5f,
|
||||
ozz::math::Float4(0.f, 2.3f, 0.f, 4.6f)};
|
||||
raw_track.keyframes.push_back(key1);
|
||||
const ozz::animation::offline::RawFloat4Track::Keyframe key2 = {
|
||||
RawTrackInterpolation::kLinear, .7f,
|
||||
ozz::math::Float4(0.f, 4.6f, 0.f, 9.2f)};
|
||||
raw_track.keyframes.push_back(key2);
|
||||
const ozz::animation::offline::RawFloat4Track::Keyframe key3 = {
|
||||
RawTrackInterpolation::kLinear, .9f,
|
||||
ozz::math::Float4(0.f, 0.f, 0.f, 0.f)};
|
||||
raw_track.keyframes.push_back(key3);
|
||||
|
||||
// Builds track
|
||||
ozz::unique_ptr<Float4Track> track(builder(raw_track));
|
||||
ASSERT_TRUE(track);
|
||||
|
||||
// Samples to verify build output.
|
||||
ozz::animation::Float4TrackSamplingJob sampling;
|
||||
sampling.track = track.get();
|
||||
sampling.result = &result;
|
||||
|
||||
sampling.ratio = 0.f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT4_EQ(result, 0.f, 0.f, 0.f, 0.f);
|
||||
|
||||
sampling.ratio = .25f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT4_EQ(result, 0.f, 1.15f, 0.f, 2.3f);
|
||||
|
||||
sampling.ratio = .5f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT4_EQ(result, 0.f, 2.3f, 0.f, 4.6f);
|
||||
|
||||
sampling.ratio = .6f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT4_EQ(result, 0.f, 2.3f, 0.f, 4.6f);
|
||||
|
||||
sampling.ratio = .7f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT4_EQ(result, 0.f, 4.6f, 0.f, 9.2f);
|
||||
|
||||
sampling.ratio = .8f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT4_EQ(result, 0.f, 2.3f, 0.f, 4.6f);
|
||||
|
||||
sampling.ratio = .9f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT4_EQ(result, 0.f, 0.f, 0.f, 0.f);
|
||||
|
||||
sampling.ratio = 1.f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_FLOAT4_EQ(result, 0.f, 0.f, 0.f, 0.f);
|
||||
}
|
||||
|
||||
TEST(Quaternion, TrackSamplingJob) {
|
||||
TrackBuilder builder;
|
||||
ozz::math::Quaternion result;
|
||||
|
||||
ozz::animation::offline::RawQuaternionTrack raw_track;
|
||||
|
||||
const ozz::animation::offline::RawQuaternionTrack::Keyframe key0 = {
|
||||
RawTrackInterpolation::kLinear, 0.f,
|
||||
ozz::math::Quaternion(.70710677f, 0.f, 0.f, .70710677f)};
|
||||
raw_track.keyframes.push_back(key0);
|
||||
const ozz::animation::offline::RawQuaternionTrack::Keyframe key1 = {
|
||||
RawTrackInterpolation::kStep, .5f,
|
||||
ozz::math::Quaternion(0.f, .70710677f, 0.f, .70710677f)};
|
||||
raw_track.keyframes.push_back(key1);
|
||||
const ozz::animation::offline::RawQuaternionTrack::Keyframe key2 = {
|
||||
RawTrackInterpolation::kLinear, .7f,
|
||||
ozz::math::Quaternion(.70710677f, 0.f, 0.f, .70710677f)};
|
||||
raw_track.keyframes.push_back(key2);
|
||||
const ozz::animation::offline::RawQuaternionTrack::Keyframe key3 = {
|
||||
RawTrackInterpolation::kLinear, .9f, ozz::math::Quaternion::identity()};
|
||||
raw_track.keyframes.push_back(key3);
|
||||
|
||||
// Builds track
|
||||
ozz::unique_ptr<QuaternionTrack> track(builder(raw_track));
|
||||
ASSERT_TRUE(track);
|
||||
// Samples to verify build output.
|
||||
ozz::animation::QuaternionTrackSamplingJob sampling;
|
||||
sampling.track = track.get();
|
||||
sampling.result = &result;
|
||||
|
||||
sampling.ratio = 0.f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_QUATERNION_EQ(result, .70710677f, 0.f, 0.f, .70710677f);
|
||||
|
||||
sampling.ratio = .1f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_QUATERNION_EQ(result, .61721331f, .15430345f, 0.f, .77151674f);
|
||||
|
||||
sampling.ratio = .4999999f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_QUATERNION_EQ(result, 0.f, .70710677f, 0.f, .70710677f);
|
||||
|
||||
sampling.ratio = .5f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_QUATERNION_EQ(result, 0.f, .70710677f, 0.f, .70710677f);
|
||||
|
||||
sampling.ratio = .6f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_QUATERNION_EQ(result, 0.f, .70710677f, 0.f, .70710677f);
|
||||
|
||||
sampling.ratio = .7f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_QUATERNION_EQ(result, .70710677f, 0.f, 0.f, .70710677f);
|
||||
|
||||
sampling.ratio = .8f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_QUATERNION_EQ(result, .38268333f, 0.f, 0.f, .92387962f);
|
||||
|
||||
sampling.ratio = .9f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_QUATERNION_EQ(result, 0.f, 0.f, 0.f, 1.f);
|
||||
|
||||
sampling.ratio = 1.f;
|
||||
ASSERT_TRUE(sampling.Run());
|
||||
EXPECT_QUATERNION_EQ(result, 0.f, 0.f, 0.f, 1.f);
|
||||
}
|
||||
+1403
File diff suppressed because it is too large
Load Diff
+110
@@ -0,0 +1,110 @@
|
||||
//----------------------------------------------------------------------------//
|
||||
// //
|
||||
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
|
||||
// and distributed under the MIT License (MIT). //
|
||||
// //
|
||||
// Copyright (c) Guillaume Blanc //
|
||||
// //
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a //
|
||||
// copy of this software and associated documentation files (the "Software"), //
|
||||
// to deal in the Software without restriction, including without limitation //
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
|
||||
// and/or sell copies of the Software, and to permit persons to whom the //
|
||||
// Software is furnished to do so, subject to the following conditions: //
|
||||
// //
|
||||
// The above copyright notice and this permission notice shall be included in //
|
||||
// all copies or substantial portions of the Software. //
|
||||
// //
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
|
||||
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
|
||||
// DEALINGS IN THE SOFTWARE. //
|
||||
// //
|
||||
//----------------------------------------------------------------------------//
|
||||
|
||||
#include "ozz/animation/runtime/track_triggering_job_trait.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "ozz/base/gtest_helper.h"
|
||||
#include "ozz/base/maths/gtest_math_helper.h"
|
||||
#include "ozz/base/memory/unique_ptr.h"
|
||||
|
||||
#include "ozz/animation/offline/raw_track.h"
|
||||
#include "ozz/animation/offline/track_builder.h"
|
||||
|
||||
#include "ozz/animation/runtime/track.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using ozz::animation::FloatTrack;
|
||||
using ozz::animation::TrackTriggeringJob;
|
||||
using ozz::animation::offline::RawFloatTrack;
|
||||
using ozz::animation::offline::RawTrackInterpolation;
|
||||
using ozz::animation::offline::TrackBuilder;
|
||||
|
||||
namespace {
|
||||
bool IsRising(const TrackTriggeringJob::Edge& _edge) { return _edge.rising; }
|
||||
} // namespace
|
||||
|
||||
TEST(Algorithm, TrackEdgeTriggerJob) {
|
||||
TrackBuilder builder;
|
||||
|
||||
ozz::animation::offline::RawFloatTrack raw_track;
|
||||
|
||||
// Keyframe values oscillate in range [0,2].
|
||||
const ozz::animation::offline::RawFloatTrack::Keyframe key0 = {
|
||||
RawTrackInterpolation::kStep, 0.f, 0.f};
|
||||
raw_track.keyframes.push_back(key0);
|
||||
const ozz::animation::offline::RawFloatTrack::Keyframe key1 = {
|
||||
RawTrackInterpolation::kStep, .5f, 2.f};
|
||||
raw_track.keyframes.push_back(key1);
|
||||
const ozz::animation::offline::RawFloatTrack::Keyframe key2 = {
|
||||
RawTrackInterpolation::kStep, 1.f, 0.f};
|
||||
raw_track.keyframes.push_back(key2);
|
||||
|
||||
// Builds track
|
||||
ozz::unique_ptr<FloatTrack> track(builder(raw_track));
|
||||
ASSERT_TRUE(track);
|
||||
|
||||
TrackTriggeringJob job;
|
||||
job.track = track.get();
|
||||
job.threshold = 1.f;
|
||||
|
||||
job.from = 0.f;
|
||||
job.to = 2.f;
|
||||
TrackTriggeringJob::Iterator iterator;
|
||||
job.iterator = &iterator;
|
||||
ASSERT_TRUE(job.Run());
|
||||
|
||||
{ // copy
|
||||
ozz::vector<TrackTriggeringJob::Edge> edges;
|
||||
std::copy(iterator, job.end(), std::back_inserter(edges));
|
||||
EXPECT_EQ(edges.size(), 4u);
|
||||
}
|
||||
|
||||
{ // count
|
||||
ozz::vector<TrackTriggeringJob::Edge> edges;
|
||||
std::iterator_traits<TrackTriggeringJob::Iterator>::difference_type count =
|
||||
std::count_if(iterator, job.end(), IsRising);
|
||||
EXPECT_EQ(count, 2);
|
||||
|
||||
std::iterator_traits<TrackTriggeringJob::Iterator>::difference_type
|
||||
count_end = std::count_if(job.end(), job.end(), IsRising);
|
||||
EXPECT_EQ(count_end, 0);
|
||||
}
|
||||
|
||||
{ // for_each
|
||||
ozz::vector<TrackTriggeringJob::Edge> edges;
|
||||
std::for_each(iterator, job.end(), IsRising);
|
||||
}
|
||||
|
||||
{ // find_if
|
||||
ozz::vector<TrackTriggeringJob::Edge> edges;
|
||||
TrackTriggeringJob::Iterator it_if =
|
||||
std::find_if(iterator, job.end(), IsRising);
|
||||
EXPECT_TRUE(it_if->rising);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user