Path: blob/master/tests/scene/test_copy_transform_modifier_3d.cpp
45991 views
/**************************************************************************/1/* test_copy_transform_modifier_3d.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "tests/test_macros.h"3132TEST_FORCE_LINK(test_copy_transform_modifier_3d)3334#ifndef _3D_DISABLED3536#include "core/math/random_number_generator.h"37#include "scene/3d/bone_attachment_3d.h"38#include "scene/3d/copy_transform_modifier_3d.h"39#include "scene/main/scene_tree.h"40#include "scene/main/window.h"4142namespace TestCopyTransformModifier3D {4344Transform3D make_random_transform_3d(int p_seed) {45RandomNumberGenerator rng;46rng.set_seed(p_seed);4748Vector3 pos;49pos.x = rng.randf_range(-10.0, 10.0);50rng.set_seed(++p_seed);51pos.y = rng.randf_range(-10.0, 10.0);52rng.set_seed(++p_seed);53pos.z = rng.randf_range(-10.0, 10.0);54rng.set_seed(++p_seed);5556Quaternion rot;57rot.x = rng.randf_range(-1.0, 1.0);58rng.set_seed(++p_seed);59rot.y = rng.randf_range(-1.0, 1.0);60rng.set_seed(++p_seed);61rot.z = rng.randf_range(-1.0, 1.0);62rng.set_seed(++p_seed);63rot.w = rng.randf_range(-1.0, 1.0);64rng.set_seed(++p_seed);65rot = rot.normalized();6667Vector3 scl;68scl.x = rng.randf_range(0.5, 2.0);69rng.set_seed(++p_seed);70scl.y = rng.randf_range(0.5, 2.0);71rng.set_seed(++p_seed);72scl.z = rng.randf_range(0.5, 2.0);73rng.set_seed(++p_seed);7475return Transform3D(Basis(rot).scaled(scl), pos);76}7778Vector3 flip_x(Vector3 p_pos) {79return Vector3(-p_pos.x, p_pos.y, p_pos.z);80}8182Vector3 flip_xy(Vector3 p_pos) {83return Vector3(-p_pos.x, -p_pos.y, p_pos.z);84}8586Vector3 flip_all(Vector3 p_pos) {87return -p_pos;88}8990// Quaternion's phase can be confused by inversion, it is aligned via casting to Basis.9192Quaternion flip_x(Quaternion p_rot) {93return Basis(Quaternion(-p_rot.x, p_rot.y, p_rot.z, p_rot.w).normalized()).get_rotation_quaternion();94}9596Quaternion flip_xy(Quaternion p_rot) {97return Basis(Quaternion(-p_rot.x, -p_rot.y, p_rot.z, p_rot.w).normalized()).get_rotation_quaternion();98}99100Quaternion flip_all(Quaternion p_rot) {101return Basis(p_rot.inverse()).get_rotation_quaternion();102}103104Vector3 inv_x(Vector3 p_scl) {105return Vector3(1.0 / p_scl.x, p_scl.y, p_scl.z);106}107108Vector3 inv_xy(Vector3 p_scl) {109return Vector3(1.0 / p_scl.x, 1.0 / p_scl.y, p_scl.z);110}111112Vector3 inv_all(Vector3 p_scl) {113return Vector3(1.0, 1.0, 1.0) / p_scl;114}115116TEST_CASE("[SceneTree][CopyTransformModifier3D]") {117SceneTree *tree = SceneTree::get_singleton();118int seed = 12345;119Skeleton3D *skeleton = memnew(Skeleton3D);120CopyTransformModifier3D *mod = memnew(CopyTransformModifier3D);121122// Instead of awaiting the process to wait to finish deferred process and watch "skeleton_updated" signal,123// force notify NOTIFICATION_UPDATE_SKELETON and get the modified pose from the BoneAttachment's transform.124BoneAttachment3D *modified = memnew(BoneAttachment3D);125126tree->get_root()->add_child(skeleton);127128int root = skeleton->add_bone("root");129skeleton->set_bone_rest(root, make_random_transform_3d(++seed));130skeleton->set_bone_pose(root, make_random_transform_3d(++seed));131132int apl_root = skeleton->add_bone("apl_root");133skeleton->set_bone_parent(apl_root, root);134skeleton->set_bone_rest(apl_root, make_random_transform_3d(++seed));135skeleton->set_bone_pose(apl_root, make_random_transform_3d(++seed));136137int apl_bone = skeleton->add_bone("apl_bone");138skeleton->set_bone_parent(apl_bone, apl_root);139skeleton->set_bone_rest(apl_bone, make_random_transform_3d(++seed));140skeleton->set_bone_pose(apl_bone, make_random_transform_3d(++seed));141142int tgt_root = skeleton->add_bone("tgt_root");143skeleton->set_bone_parent(tgt_root, root);144skeleton->set_bone_rest(tgt_root, make_random_transform_3d(++seed));145skeleton->set_bone_pose(tgt_root, make_random_transform_3d(++seed));146147int tgt_bone = skeleton->add_bone("tgt_bone");148skeleton->set_bone_parent(tgt_bone, tgt_root);149skeleton->set_bone_rest(tgt_bone, make_random_transform_3d(++seed));150skeleton->set_bone_pose(tgt_bone, make_random_transform_3d(++seed));151152skeleton->add_child(mod);153skeleton->add_child(modified);154modified->set_rotation_edit_mode(Node3D::ROTATION_EDIT_MODE_QUATERNION);155modified->set_bone_idx(apl_bone);156157mod->set_setting_count(1);158mod->set_reference_bone(0, tgt_bone);159mod->set_apply_bone(0, apl_bone);160161mod->set_copy_position(0, true);162mod->set_copy_rotation(0, true);163mod->set_copy_scale(0, true);164165// ===== [CopyTransformModifier3D] Enable 1 axis =====166mod->set_axis_x_enabled(0, true);167mod->set_axis_y_enabled(0, false);168mod->set_axis_z_enabled(0, false);169mod->set_axis_x_inverted(0, false);170mod->set_axis_y_inverted(0, false);171mod->set_axis_z_inverted(0, false);172173SUBCASE("[CopyTransformModifier3D] Enable 1 axis, additive=false, relative=false") {174mod->set_additive(0, false);175mod->set_relative(0, false);176skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);177CHECK_MESSAGE(Math::is_equal_approx(178skeleton->get_bone_pose_position(tgt_bone).x,179(skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin.x),180"Position x is copied correctly.");181CHECK_MESSAGE(Math::is_equal_approx(182BoneConstraint3D::get_roll_angle(skeleton->get_bone_pose_rotation(tgt_bone), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_X)),183BoneConstraint3D::get_roll_angle((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion(), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_X))),184"Rotation x (roll x) is copied correctly.");185CHECK_MESSAGE(Math::is_equal_approx(186skeleton->get_bone_pose_scale(tgt_bone).x,187(skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale().x),188"Scale x is copied correctly.");189}190191SUBCASE("[CopyTransformModifier3D] Enable 1 axis, additive=true, relative=false") {192mod->set_additive(0, true);193mod->set_relative(0, false);194skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);195CHECK_MESSAGE(Math::is_equal_approx(196skeleton->get_bone_pose_position(tgt_bone).x,197((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)).x),198"Position x is copied correctly.");199CHECK_MESSAGE(Math::is_equal_approx(200BoneConstraint3D::get_roll_angle(skeleton->get_bone_pose_rotation(tgt_bone), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_X)),201BoneConstraint3D::get_roll_angle(skeleton->get_bone_pose_rotation(apl_bone).inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion(), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_X))),202"Rotation x (roll x) is copied correctly.");203CHECK_MESSAGE(Math::is_equal_approx(204skeleton->get_bone_pose_scale(tgt_bone).x,205((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)).x),206"Scale x is copied correctly.");207}208209SUBCASE("[CopyTransformModifier3D] Enable 1 axis, additive=false, relative=true") {210mod->set_additive(0, false);211mod->set_relative(0, true);212skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);213CHECK_MESSAGE(Math::is_equal_approx(214(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).x,215((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_rest(apl_bone).origin).x),216"Position x is copied correctly.");217CHECK_MESSAGE(Math::is_equal_approx(218BoneConstraint3D::get_roll_angle(skeleton->get_bone_rest(tgt_bone).basis.get_rotation_quaternion().inverse() * skeleton->get_bone_pose_rotation(tgt_bone), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_X)),219BoneConstraint3D::get_roll_angle(skeleton->get_bone_rest(apl_bone).basis.get_rotation_quaternion().inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion(), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_X))),220"Rotation x (roll x) is copied correctly.");221CHECK_MESSAGE(Math::is_equal_approx(222(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).x,223((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_rest(apl_bone).basis.get_scale()).x),224"Scale x is copied correctly.");225}226227SUBCASE("[CopyTransformModifier3D] Enable 1 axis, additive=true, relative=true") {228mod->set_additive(0, true);229mod->set_relative(0, true);230skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);231CHECK_MESSAGE(Math::is_equal_approx(232(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).x,233((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)).x),234"Position x is copied correctly.");235CHECK_MESSAGE(Math::is_equal_approx(236BoneConstraint3D::get_roll_angle(skeleton->get_bone_rest(tgt_bone).basis.get_rotation_quaternion().inverse() * skeleton->get_bone_pose_rotation(tgt_bone), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_X)),237BoneConstraint3D::get_roll_angle(skeleton->get_bone_pose_rotation(apl_bone).inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion(), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_X))),238"Rotation x (roll x) is copied correctly.");239CHECK_MESSAGE(Math::is_equal_approx(240(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).x,241((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)).x),242"Scale x is copied correctly.");243}244245// ===== [CopyTransformModifier3D] Enable 2 axes =====246mod->set_axis_x_enabled(0, true);247mod->set_axis_y_enabled(0, true);248mod->set_axis_z_enabled(0, false);249mod->set_axis_x_inverted(0, false);250mod->set_axis_y_inverted(0, false);251mod->set_axis_z_inverted(0, false);252253SUBCASE("[CopyTransformModifier3D] Enable 2 axes, additive=false, relative=false") {254mod->set_additive(0, false);255mod->set_relative(0, false);256skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);257CHECK_MESSAGE(Math::is_equal_approx(258skeleton->get_bone_pose_position(tgt_bone).x,259(skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin.x),260"Position x is copied correctly.");261CHECK_MESSAGE(Math::is_equal_approx(262skeleton->get_bone_pose_position(tgt_bone).y,263(skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin.y),264"Position y is copied correctly.");265CHECK_MESSAGE(Math::is_zero_approx(266BoneConstraint3D::get_roll_angle((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion(), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_Z))),267"Rotation z (roll z) is zero correctly.");268CHECK_MESSAGE(Math::is_equal_approx(269skeleton->get_bone_pose_scale(tgt_bone).x,270(skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale().x),271"Scale x is copied correctly.");272CHECK_MESSAGE(Math::is_equal_approx(273skeleton->get_bone_pose_scale(tgt_bone).y,274(skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale().y),275"Scale y is copied correctly.");276}277278SUBCASE("[CopyTransformModifier3D] Enable 2 axes, additive=true, relative=false") {279mod->set_additive(0, true);280mod->set_relative(0, false);281skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);282CHECK_MESSAGE(Math::is_equal_approx(283skeleton->get_bone_pose_position(tgt_bone).x,284((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)).x),285"Position x is copied correctly.");286CHECK_MESSAGE(Math::is_equal_approx(287skeleton->get_bone_pose_position(tgt_bone).y,288((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)).y),289"Position y is copied correctly.");290CHECK_MESSAGE(Math::is_zero_approx(291BoneConstraint3D::get_roll_angle(skeleton->get_bone_pose_rotation(apl_bone).inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion(), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_Z))),292"Rotation z (roll z) is zero correctly.");293CHECK_MESSAGE(Math::is_equal_approx(294skeleton->get_bone_pose_scale(tgt_bone).x,295((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)).x),296"Scale x is copied correctly.");297CHECK_MESSAGE(Math::is_equal_approx(298skeleton->get_bone_pose_scale(tgt_bone).y,299((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)).y),300"Scale y is copied correctly.");301}302303SUBCASE("[CopyTransformModifier3D] Enable 2 axes, additive=false, relative=true") {304mod->set_additive(0, false);305mod->set_relative(0, true);306skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);307CHECK_MESSAGE(Math::is_equal_approx(308(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).x,309((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_rest(apl_bone).origin).x),310"Position x is copied correctly.");311CHECK_MESSAGE(Math::is_equal_approx(312(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).y,313((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_rest(apl_bone).origin).y),314"Position y is copied correctly.");315CHECK_MESSAGE(Math::is_zero_approx(316BoneConstraint3D::get_roll_angle(skeleton->get_bone_rest(apl_bone).basis.get_rotation_quaternion().inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion(), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_Z))),317"Rotation z (roll z) is zero correctly.");318CHECK_MESSAGE(Math::is_equal_approx(319(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).x,320((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_rest(apl_bone).basis.get_scale()).x),321"Scale x is copied correctly.");322CHECK_MESSAGE(Math::is_equal_approx(323(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).y,324((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_rest(apl_bone).basis.get_scale()).y),325"Scale y is copied correctly.");326}327328SUBCASE("[CopyTransformModifier3D] Enable 2 axes, additive=true, relative=true") {329mod->set_additive(0, true);330mod->set_relative(0, true);331skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);332CHECK_MESSAGE(Math::is_equal_approx(333(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).x,334((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)).x),335"Position x is copied correctly.");336CHECK_MESSAGE(Math::is_equal_approx(337(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).y,338((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)).y),339"Position y is copied correctly.");340CHECK_MESSAGE(Math::is_zero_approx(341BoneConstraint3D::get_roll_angle(skeleton->get_bone_pose_rotation(apl_bone).inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion(), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_Z))),342"Rotation z (roll z) is zero correctly.");343CHECK_MESSAGE(Math::is_equal_approx(344(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).x,345((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)).x),346"Scale x is copied correctly.");347CHECK_MESSAGE(Math::is_equal_approx(348(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).y,349((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)).y),350"Scale y is copied correctly.");351}352353// ===== [CopyTransformModifier3D] Enable all axes =====354mod->set_axis_x_enabled(0, true);355mod->set_axis_y_enabled(0, true);356mod->set_axis_z_enabled(0, true);357mod->set_axis_x_inverted(0, false);358mod->set_axis_y_inverted(0, false);359mod->set_axis_z_inverted(0, false);360361SUBCASE("[CopyTransformModifier3D] Enable all axes, additive=false, relative=false") {362mod->set_additive(0, false);363mod->set_relative(0, false);364skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);365CHECK_MESSAGE(skeleton->get_bone_pose_position(tgt_bone).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin), "Position is copied correctly.");366CHECK_MESSAGE(Basis(skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx(Basis((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion())), "Rotation is copied correctly.");367CHECK_MESSAGE(skeleton->get_bone_pose_scale(tgt_bone).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale()), "Scale is copied correctly.");368}369370SUBCASE("[CopyTransformModifier3D] Enable all axes, additive=true, relative=false") {371mod->set_additive(0, true);372mod->set_relative(0, false);373skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);374CHECK_MESSAGE(skeleton->get_bone_pose_position(tgt_bone).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)), "Position is copied correctly.");375CHECK_MESSAGE(Basis(skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx(skeleton->get_bone_pose_rotation(apl_bone).inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion()), "Rotation is copied correctly.");376CHECK_MESSAGE(skeleton->get_bone_pose_scale(tgt_bone).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)), "Scale is copied correctly.");377}378379SUBCASE("[CopyTransformModifier3D] Enable all axes, additive=false, relative=true") {380mod->set_additive(0, false);381mod->set_relative(0, true);382skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);383CHECK_MESSAGE((skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_rest(apl_bone).origin), "Position is copied correctly.");384CHECK_MESSAGE(Basis(skeleton->get_bone_rest(tgt_bone).basis.get_rotation_quaternion().inverse() * skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx(Basis(skeleton->get_bone_rest(apl_bone).basis.get_rotation_quaternion().inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion())), "Rotation is copied correctly.");385CHECK_MESSAGE((skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_rest(apl_bone).basis.get_scale()), "Scale is copied correctly.");386}387388SUBCASE("[CopyTransformModifier3D] Enable all axes, additive=true, relative=true") {389mod->set_additive(0, true);390mod->set_relative(0, true);391skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);392CHECK_MESSAGE((skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)), "Position is copied correctly.");393CHECK_MESSAGE(Basis(skeleton->get_bone_rest(tgt_bone).basis.get_rotation_quaternion().inverse() * skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx(skeleton->get_bone_pose_rotation(apl_bone).inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion()), "Rotation is copied correctly.");394CHECK_MESSAGE((skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)), "Scale is copied correctly.");395}396397// ===== [CopyTransformModifier3D] Enable all axes, invert 1 axis =====398mod->set_axis_x_enabled(0, true);399mod->set_axis_y_enabled(0, true);400mod->set_axis_z_enabled(0, true);401mod->set_axis_x_inverted(0, true);402mod->set_axis_y_inverted(0, false);403mod->set_axis_z_inverted(0, false);404405SUBCASE("[CopyTransformModifier3D] Enable all axes, invert 1 axis, additive=false, relative=false") {406mod->set_additive(0, false);407mod->set_relative(0, false);408skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);409CHECK_MESSAGE(flip_x(skeleton->get_bone_pose_position(tgt_bone)).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin), "Position is copied/inverted correctly.");410CHECK_MESSAGE(flip_x(skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion()), "Rotation is copied/inverted correctly.");411CHECK_MESSAGE(inv_x(skeleton->get_bone_pose_scale(tgt_bone)).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale()), "Scale is copied/inverted correctly.");412}413414SUBCASE("[CopyTransformModifier3D] Enable all axes, invert 1 axis, additive=true, relative=false") {415mod->set_additive(0, true);416mod->set_relative(0, false);417skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);418CHECK_MESSAGE(flip_x(skeleton->get_bone_pose_position(tgt_bone)).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)), "Position is copied/inverted correctly.");419CHECK_MESSAGE(flip_x(skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx(Basis(skeleton->get_bone_pose_rotation(apl_bone).inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion()).get_rotation_quaternion()), "Rotation is copied/inverted correctly.");420CHECK_MESSAGE(inv_x(skeleton->get_bone_pose_scale(tgt_bone)).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)), "Scale is copied/inverted correctly.");421}422423SUBCASE("[CopyTransformModifier3D] Enable all axes, invert 1 axis, additive=false, relative=true") {424mod->set_additive(0, false);425mod->set_relative(0, true);426skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);427CHECK_MESSAGE(flip_x(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_rest(apl_bone).origin), "Position is copied/inverted correctly.");428CHECK_MESSAGE(flip_x(skeleton->get_bone_rest(tgt_bone).basis.get_rotation_quaternion().inverse() * skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx(Basis(skeleton->get_bone_rest(apl_bone).basis.get_rotation_quaternion().inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion()).get_rotation_quaternion()), "Rotation is copied/inverted correctly.");429CHECK_MESSAGE(inv_x(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_rest(apl_bone).basis.get_scale()), "Scale is copied/inverted correctly.");430}431432SUBCASE("[CopyTransformModifier3D] Enable all axes, invert 1 axis, additive=true, relative=true") {433mod->set_additive(0, true);434mod->set_relative(0, true);435skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);436CHECK_MESSAGE(flip_x(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)), "Position is copied/inverted correctly.");437CHECK_MESSAGE(flip_x(skeleton->get_bone_rest(tgt_bone).basis.get_rotation_quaternion().inverse() * skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx(Basis(skeleton->get_bone_pose_rotation(apl_bone).inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion()).get_rotation_quaternion()), "Rotation is copied/inverted correctly.");438CHECK_MESSAGE(inv_x(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)), "Scale is copied/inverted correctly.");439}440441// ===== [CopyTransformModifier3D] Enable all axes, invert 2 axes =====442mod->set_axis_x_enabled(0, true);443mod->set_axis_y_enabled(0, true);444mod->set_axis_z_enabled(0, true);445mod->set_axis_x_inverted(0, true);446mod->set_axis_y_inverted(0, true);447mod->set_axis_z_inverted(0, false);448449SUBCASE("[CopyTransformModifier3D] Enable all axes, invert 2 axes, additive=false, relative=false") {450mod->set_additive(0, false);451mod->set_relative(0, false);452skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);453CHECK_MESSAGE(flip_xy(skeleton->get_bone_pose_position(tgt_bone)).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin), "Position is copied/inverted correctly.");454CHECK_MESSAGE(flip_xy(skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion()), "Rotation is copied/inverted correctly.");455CHECK_MESSAGE(inv_xy(skeleton->get_bone_pose_scale(tgt_bone)).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale()), "Scale is copied/inverted correctly.");456}457458SUBCASE("[CopyTransformModifier3D] Enable all axes, invert 2 axes, additive=true, relative=false") {459mod->set_additive(0, true);460mod->set_relative(0, false);461skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);462CHECK_MESSAGE(flip_xy(skeleton->get_bone_pose_position(tgt_bone)).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)), "Position is copied/inverted correctly.");463CHECK_MESSAGE(flip_xy(skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx(Basis(skeleton->get_bone_pose_rotation(apl_bone).inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion()).get_rotation_quaternion()), "Rotation is copied/inverted correctly.");464CHECK_MESSAGE(inv_xy(skeleton->get_bone_pose_scale(tgt_bone)).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)), "Scale is copied/inverted correctly.");465}466467SUBCASE("[CopyTransformModifier3D] Enable all axes, invert 2 axes, additive=false, relative=true") {468mod->set_additive(0, false);469mod->set_relative(0, true);470skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);471CHECK_MESSAGE(flip_xy(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_rest(apl_bone).origin), "Position is copied/inverted correctly.");472CHECK_MESSAGE(flip_xy(skeleton->get_bone_rest(tgt_bone).basis.get_rotation_quaternion().inverse() * skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx(Basis(skeleton->get_bone_rest(apl_bone).basis.get_rotation_quaternion().inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion()).get_rotation_quaternion()), "Rotation is copied/inverted correctly.");473CHECK_MESSAGE(inv_xy(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_rest(apl_bone).basis.get_scale()), "Scale is copied/inverted correctly.");474}475476SUBCASE("[CopyTransformModifier3D] Enable all axes, invert 2 axes, additive=true, relative=true") {477mod->set_additive(0, true);478mod->set_relative(0, true);479skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);480CHECK_MESSAGE(flip_xy(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)), "Position is copied/inverted correctly.");481CHECK_MESSAGE(flip_xy(skeleton->get_bone_rest(tgt_bone).basis.get_rotation_quaternion().inverse() * skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx(Basis(skeleton->get_bone_pose_rotation(apl_bone).inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion()).get_rotation_quaternion()), "Rotation is copied/inverted correctly.");482CHECK_MESSAGE(inv_xy(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)), "Scale is copied/inverted correctly.");483}484485// ===== [CopyTransformModifier3D] Enable all axes, invert all axes =====486mod->set_axis_x_enabled(0, true);487mod->set_axis_y_enabled(0, true);488mod->set_axis_z_enabled(0, true);489mod->set_axis_x_inverted(0, true);490mod->set_axis_y_inverted(0, true);491mod->set_axis_z_inverted(0, true);492493SUBCASE("[CopyTransformModifier3D] Enable all axes, invert all axes, additive=false, relative=false") {494mod->set_additive(0, false);495mod->set_relative(0, false);496skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);497CHECK_MESSAGE(flip_all(skeleton->get_bone_pose_position(tgt_bone)).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin), "Position is copied/inverted correctly.");498CHECK_MESSAGE(flip_all(skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion()), "Rotation is copied/inverted correctly.");499CHECK_MESSAGE(inv_all(skeleton->get_bone_pose_scale(tgt_bone)).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale()), "Scale is copied/inverted correctly.");500}501502SUBCASE("[CopyTransformModifier3D] Enable all axes, invert all axes, additive=true, relative=false") {503mod->set_additive(0, true);504mod->set_relative(0, false);505skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);506CHECK_MESSAGE(flip_all(skeleton->get_bone_pose_position(tgt_bone)).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)), "Position is copied/inverted correctly.");507CHECK_MESSAGE(flip_all(skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx(Basis(skeleton->get_bone_pose_rotation(apl_bone).inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion()).get_rotation_quaternion()), "Rotation is copied/inverted correctly.");508CHECK_MESSAGE(inv_all(skeleton->get_bone_pose_scale(tgt_bone)).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)), "Scale is copied/inverted correctly.");509}510511SUBCASE("[CopyTransformModifier3D] Enable all axes, invert all axes, additive=false, relative=true") {512mod->set_additive(0, false);513mod->set_relative(0, true);514skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);515CHECK_MESSAGE(flip_all(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_rest(apl_bone).origin), "Position is copied/inverted correctly.");516CHECK_MESSAGE(flip_all(skeleton->get_bone_rest(tgt_bone).basis.get_rotation_quaternion().inverse() * skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx(Basis(skeleton->get_bone_rest(apl_bone).basis.get_rotation_quaternion().inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion()).get_rotation_quaternion()), "Rotation is copied/inverted correctly.");517CHECK_MESSAGE(inv_all(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_rest(apl_bone).basis.get_scale()), "Scale is copied/inverted correctly.");518}519520SUBCASE("[CopyTransformModifier3D] Enable all axes, invert all axes, additive=true, relative=true") {521mod->set_additive(0, true);522mod->set_relative(0, true);523skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);524CHECK_MESSAGE(flip_all(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)), "Position is copied/inverted correctly.");525CHECK_MESSAGE(flip_all(skeleton->get_bone_rest(tgt_bone).basis.get_rotation_quaternion().inverse() * skeleton->get_bone_pose_rotation(tgt_bone)).is_equal_approx(Basis(skeleton->get_bone_pose_rotation(apl_bone).inverse() * (skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_rotation_quaternion()).get_rotation_quaternion()), "Rotation is copied/inverted correctly.");526CHECK_MESSAGE(inv_all(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).is_equal_approx((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)), "Scale is copied/inverted correctly.");527}528529memdelete(modified);530memdelete(mod);531memdelete(skeleton);532}533534} // namespace TestCopyTransformModifier3D535536#endif // _3D_DISABLED537538539