Path: blob/master/tests/scene/test_copy_transform_modifier_3d.h
10277 views
/**************************************************************************/1/* test_copy_transform_modifier_3d.h */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#pragma once3132#include "tests/test_macros.h"3334#include "scene/3d/bone_attachment_3d.h"35#include "scene/3d/copy_transform_modifier_3d.h"3637namespace TestCopyTransformModifier3D {3839Transform3D make_random_transform_3d(int p_seed) {40RandomNumberGenerator rng;41rng.set_seed(p_seed);4243Vector3 pos;44pos.x = rng.randf_range(-10.0, 10.0);45rng.set_seed(++p_seed);46pos.y = rng.randf_range(-10.0, 10.0);47rng.set_seed(++p_seed);48pos.z = rng.randf_range(-10.0, 10.0);49rng.set_seed(++p_seed);5051Quaternion rot;52rot.x = rng.randf_range(-1.0, 1.0);53rng.set_seed(++p_seed);54rot.y = rng.randf_range(-1.0, 1.0);55rng.set_seed(++p_seed);56rot.z = rng.randf_range(-1.0, 1.0);57rng.set_seed(++p_seed);58rot.w = rng.randf_range(-1.0, 1.0);59rng.set_seed(++p_seed);60rot = rot.normalized();6162Vector3 scl;63scl.x = rng.randf_range(0.5, 2.0);64rng.set_seed(++p_seed);65scl.y = rng.randf_range(0.5, 2.0);66rng.set_seed(++p_seed);67scl.z = rng.randf_range(0.5, 2.0);68rng.set_seed(++p_seed);6970return Transform3D(Basis(rot).scaled(scl), pos);71}7273Vector3 flip_x(Vector3 p_pos) {74return Vector3(-p_pos.x, p_pos.y, p_pos.z);75}7677Vector3 flip_xy(Vector3 p_pos) {78return Vector3(-p_pos.x, -p_pos.y, p_pos.z);79}8081Vector3 flip_all(Vector3 p_pos) {82return -p_pos;83}8485// Quaternion's phase can be confused by inversion, it is aligned via casting to Basis.8687Quaternion flip_x(Quaternion p_rot) {88return Basis(Quaternion(-p_rot.x, p_rot.y, p_rot.z, p_rot.w).normalized()).get_rotation_quaternion();89}9091Quaternion flip_xy(Quaternion p_rot) {92return Basis(Quaternion(-p_rot.x, -p_rot.y, p_rot.z, p_rot.w).normalized()).get_rotation_quaternion();93}9495Quaternion flip_all(Quaternion p_rot) {96return Basis(p_rot.inverse()).get_rotation_quaternion();97}9899Vector3 inv_x(Vector3 p_scl) {100return Vector3(1.0 / p_scl.x, p_scl.y, p_scl.z);101}102103Vector3 inv_xy(Vector3 p_scl) {104return Vector3(1.0 / p_scl.x, 1.0 / p_scl.y, p_scl.z);105}106107Vector3 inv_all(Vector3 p_scl) {108return Vector3(1.0, 1.0, 1.0) / p_scl;109}110111TEST_CASE("[SceneTree][CopyTransformModifier3D]") {112SceneTree *tree = SceneTree::get_singleton();113int seed = 12345;114Skeleton3D *skeleton = memnew(Skeleton3D);115CopyTransformModifier3D *mod = memnew(CopyTransformModifier3D);116117// Instead of awaiting the process to wait to finish deferred process and watch "skeleton_updated" signal,118// force notify NOTIFICATION_UPDATE_SKELETON and get the modified pose from the BoneAttachment's transform.119BoneAttachment3D *modified = memnew(BoneAttachment3D);120121tree->get_root()->add_child(skeleton);122123int root = skeleton->add_bone("root");124skeleton->set_bone_rest(root, make_random_transform_3d(++seed));125skeleton->set_bone_pose(root, make_random_transform_3d(++seed));126127int apl_root = skeleton->add_bone("apl_root");128skeleton->set_bone_parent(apl_root, root);129skeleton->set_bone_rest(apl_root, make_random_transform_3d(++seed));130skeleton->set_bone_pose(apl_root, make_random_transform_3d(++seed));131132int apl_bone = skeleton->add_bone("apl_bone");133skeleton->set_bone_parent(apl_bone, apl_root);134skeleton->set_bone_rest(apl_bone, make_random_transform_3d(++seed));135skeleton->set_bone_pose(apl_bone, make_random_transform_3d(++seed));136137int tgt_root = skeleton->add_bone("tgt_root");138skeleton->set_bone_parent(tgt_root, root);139skeleton->set_bone_rest(tgt_root, make_random_transform_3d(++seed));140skeleton->set_bone_pose(tgt_root, make_random_transform_3d(++seed));141142int tgt_bone = skeleton->add_bone("tgt_bone");143skeleton->set_bone_parent(tgt_bone, tgt_root);144skeleton->set_bone_rest(tgt_bone, make_random_transform_3d(++seed));145skeleton->set_bone_pose(tgt_bone, make_random_transform_3d(++seed));146147skeleton->add_child(mod);148skeleton->add_child(modified);149modified->set_rotation_edit_mode(Node3D::ROTATION_EDIT_MODE_QUATERNION);150modified->set_bone_idx(apl_bone);151152mod->set_setting_count(1);153mod->set_reference_bone(0, tgt_bone);154mod->set_apply_bone(0, apl_bone);155156mod->set_copy_position(0, true);157mod->set_copy_rotation(0, true);158mod->set_copy_scale(0, true);159160// ===== [CopyTransformModifier3D] Enable 1 axis =====161mod->set_axis_x_enabled(0, true);162mod->set_axis_y_enabled(0, false);163mod->set_axis_z_enabled(0, false);164mod->set_axis_x_inverted(0, false);165mod->set_axis_y_inverted(0, false);166mod->set_axis_z_inverted(0, false);167168SUBCASE("[CopyTransformModifier3D] Enable 1 axis, additive=false, relative=false") {169mod->set_additive(0, false);170mod->set_relative(0, false);171skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);172CHECK_MESSAGE(Math::is_equal_approx(173skeleton->get_bone_pose_position(tgt_bone).x,174(skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin.x),175"Position x is copied correctly.");176CHECK_MESSAGE(Math::is_equal_approx(177BoneConstraint3D::get_roll_angle(skeleton->get_bone_pose_rotation(tgt_bone), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_X)),178BoneConstraint3D::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))),179"Rotation x (roll x) is copied correctly.");180CHECK_MESSAGE(Math::is_equal_approx(181skeleton->get_bone_pose_scale(tgt_bone).x,182(skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale().x),183"Scale x is copied correctly.");184}185186SUBCASE("[CopyTransformModifier3D] Enable 1 axis, additive=true, relative=false") {187mod->set_additive(0, true);188mod->set_relative(0, false);189skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);190CHECK_MESSAGE(Math::is_equal_approx(191skeleton->get_bone_pose_position(tgt_bone).x,192((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)).x),193"Position x is copied correctly.");194CHECK_MESSAGE(Math::is_equal_approx(195BoneConstraint3D::get_roll_angle(skeleton->get_bone_pose_rotation(tgt_bone), BoneConstraint3D::get_vector_from_axis(Vector3::AXIS_X)),196BoneConstraint3D::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))),197"Rotation x (roll x) is copied correctly.");198CHECK_MESSAGE(Math::is_equal_approx(199skeleton->get_bone_pose_scale(tgt_bone).x,200((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)).x),201"Scale x is copied correctly.");202}203204SUBCASE("[CopyTransformModifier3D] Enable 1 axis, additive=false, relative=true") {205mod->set_additive(0, false);206mod->set_relative(0, true);207skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);208CHECK_MESSAGE(Math::is_equal_approx(209(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).x,210((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_rest(apl_bone).origin).x),211"Position x is copied correctly.");212CHECK_MESSAGE(Math::is_equal_approx(213BoneConstraint3D::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)),214BoneConstraint3D::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))),215"Rotation x (roll x) is copied correctly.");216CHECK_MESSAGE(Math::is_equal_approx(217(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).x,218((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),219"Scale x is copied correctly.");220}221222SUBCASE("[CopyTransformModifier3D] Enable 1 axis, additive=true, relative=true") {223mod->set_additive(0, true);224mod->set_relative(0, true);225skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);226CHECK_MESSAGE(Math::is_equal_approx(227(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).x,228((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)).x),229"Position x is copied correctly.");230CHECK_MESSAGE(Math::is_equal_approx(231BoneConstraint3D::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)),232BoneConstraint3D::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))),233"Rotation x (roll x) is copied correctly.");234CHECK_MESSAGE(Math::is_equal_approx(235(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).x,236((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)).x),237"Scale x is copied correctly.");238}239240// ===== [CopyTransformModifier3D] Enable 2 axes =====241mod->set_axis_x_enabled(0, true);242mod->set_axis_y_enabled(0, true);243mod->set_axis_z_enabled(0, false);244mod->set_axis_x_inverted(0, false);245mod->set_axis_y_inverted(0, false);246mod->set_axis_z_inverted(0, false);247248SUBCASE("[CopyTransformModifier3D] Enable 2 axes, additive=false, relative=false") {249mod->set_additive(0, false);250mod->set_relative(0, false);251skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);252CHECK_MESSAGE(Math::is_equal_approx(253skeleton->get_bone_pose_position(tgt_bone).x,254(skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin.x),255"Position x is copied correctly.");256CHECK_MESSAGE(Math::is_equal_approx(257skeleton->get_bone_pose_position(tgt_bone).y,258(skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin.y),259"Position y is copied correctly.");260CHECK_MESSAGE(Math::is_zero_approx(261BoneConstraint3D::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))),262"Rotation z (roll z) is zero correctly.");263CHECK_MESSAGE(Math::is_equal_approx(264skeleton->get_bone_pose_scale(tgt_bone).x,265(skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale().x),266"Scale x is copied correctly.");267CHECK_MESSAGE(Math::is_equal_approx(268skeleton->get_bone_pose_scale(tgt_bone).y,269(skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale().y),270"Scale y is copied correctly.");271}272273SUBCASE("[CopyTransformModifier3D] Enable 2 axes, additive=true, relative=false") {274mod->set_additive(0, true);275mod->set_relative(0, false);276skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);277CHECK_MESSAGE(Math::is_equal_approx(278skeleton->get_bone_pose_position(tgt_bone).x,279((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)).x),280"Position x is copied correctly.");281CHECK_MESSAGE(Math::is_equal_approx(282skeleton->get_bone_pose_position(tgt_bone).y,283((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)).y),284"Position y is copied correctly.");285CHECK_MESSAGE(Math::is_zero_approx(286BoneConstraint3D::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))),287"Rotation z (roll z) is zero correctly.");288CHECK_MESSAGE(Math::is_equal_approx(289skeleton->get_bone_pose_scale(tgt_bone).x,290((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)).x),291"Scale x is copied correctly.");292CHECK_MESSAGE(Math::is_equal_approx(293skeleton->get_bone_pose_scale(tgt_bone).y,294((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)).y),295"Scale y is copied correctly.");296}297298SUBCASE("[CopyTransformModifier3D] Enable 2 axes, additive=false, relative=true") {299mod->set_additive(0, false);300mod->set_relative(0, true);301skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);302CHECK_MESSAGE(Math::is_equal_approx(303(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).x,304((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_rest(apl_bone).origin).x),305"Position x is copied correctly.");306CHECK_MESSAGE(Math::is_equal_approx(307(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).y,308((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_rest(apl_bone).origin).y),309"Position y is copied correctly.");310CHECK_MESSAGE(Math::is_zero_approx(311BoneConstraint3D::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))),312"Rotation z (roll z) is zero correctly.");313CHECK_MESSAGE(Math::is_equal_approx(314(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).x,315((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),316"Scale x is copied correctly.");317CHECK_MESSAGE(Math::is_equal_approx(318(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).y,319((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),320"Scale y is copied correctly.");321}322323SUBCASE("[CopyTransformModifier3D] Enable 2 axes, additive=true, relative=true") {324mod->set_additive(0, true);325mod->set_relative(0, true);326skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);327CHECK_MESSAGE(Math::is_equal_approx(328(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).x,329((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)).x),330"Position x is copied correctly.");331CHECK_MESSAGE(Math::is_equal_approx(332(skeleton->get_bone_pose_position(tgt_bone) - skeleton->get_bone_rest(tgt_bone).origin).y,333((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).origin - skeleton->get_bone_pose_position(apl_bone)).y),334"Position y is copied correctly.");335CHECK_MESSAGE(Math::is_zero_approx(336BoneConstraint3D::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))),337"Rotation z (roll z) is zero correctly.");338CHECK_MESSAGE(Math::is_equal_approx(339(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).x,340((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)).x),341"Scale x is copied correctly.");342CHECK_MESSAGE(Math::is_equal_approx(343(skeleton->get_bone_pose_scale(tgt_bone) / skeleton->get_bone_rest(tgt_bone).basis.get_scale()).y,344((skeleton->get_bone_global_pose(apl_root).affine_inverse() * modified->get_transform()).basis.get_scale() / skeleton->get_bone_pose_scale(apl_bone)).y),345"Scale y is copied correctly.");346}347348// ===== [CopyTransformModifier3D] Enable all axes =====349mod->set_axis_x_enabled(0, true);350mod->set_axis_y_enabled(0, true);351mod->set_axis_z_enabled(0, true);352mod->set_axis_x_inverted(0, false);353mod->set_axis_y_inverted(0, false);354mod->set_axis_z_inverted(0, false);355356SUBCASE("[CopyTransformModifier3D] Enable all axes, additive=false, relative=false") {357mod->set_additive(0, false);358mod->set_relative(0, false);359skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);360CHECK_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.");361CHECK_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.");362CHECK_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.");363}364365SUBCASE("[CopyTransformModifier3D] Enable all axes, additive=true, relative=false") {366mod->set_additive(0, true);367mod->set_relative(0, false);368skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);369CHECK_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.");370CHECK_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.");371CHECK_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.");372}373374SUBCASE("[CopyTransformModifier3D] Enable all axes, additive=false, relative=true") {375mod->set_additive(0, false);376mod->set_relative(0, true);377skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);378CHECK_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.");379CHECK_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.");380CHECK_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.");381}382383SUBCASE("[CopyTransformModifier3D] Enable all axes, additive=true, relative=true") {384mod->set_additive(0, true);385mod->set_relative(0, true);386skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);387CHECK_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.");388CHECK_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.");389CHECK_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.");390}391392// ===== [CopyTransformModifier3D] Enable all axes, invert 1 axis =====393mod->set_axis_x_enabled(0, true);394mod->set_axis_y_enabled(0, true);395mod->set_axis_z_enabled(0, true);396mod->set_axis_x_inverted(0, true);397mod->set_axis_y_inverted(0, false);398mod->set_axis_z_inverted(0, false);399400SUBCASE("[CopyTransformModifier3D] Enable all axes, invert 1 axis, additive=false, relative=false") {401mod->set_additive(0, false);402mod->set_relative(0, false);403skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);404CHECK_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.");405CHECK_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.");406CHECK_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.");407}408409SUBCASE("[CopyTransformModifier3D] Enable all axes, invert 1 axis, additive=true, relative=false") {410mod->set_additive(0, true);411mod->set_relative(0, false);412skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);413CHECK_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.");414CHECK_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.");415CHECK_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.");416}417418SUBCASE("[CopyTransformModifier3D] Enable all axes, invert 1 axis, additive=false, relative=true") {419mod->set_additive(0, false);420mod->set_relative(0, true);421skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);422CHECK_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.");423CHECK_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.");424CHECK_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.");425}426427SUBCASE("[CopyTransformModifier3D] Enable all axes, invert 1 axis, additive=true, relative=true") {428mod->set_additive(0, true);429mod->set_relative(0, true);430skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);431CHECK_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.");432CHECK_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.");433CHECK_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.");434}435436// ===== [CopyTransformModifier3D] Enable all axes, invert 2 axes =====437mod->set_axis_x_enabled(0, true);438mod->set_axis_y_enabled(0, true);439mod->set_axis_z_enabled(0, true);440mod->set_axis_x_inverted(0, true);441mod->set_axis_y_inverted(0, true);442mod->set_axis_z_inverted(0, false);443444SUBCASE("[CopyTransformModifier3D] Enable all axes, invert 2 axes, additive=false, relative=false") {445mod->set_additive(0, false);446mod->set_relative(0, false);447skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);448CHECK_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.");449CHECK_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.");450CHECK_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.");451}452453SUBCASE("[CopyTransformModifier3D] Enable all axes, invert 2 axes, additive=true, relative=false") {454mod->set_additive(0, true);455mod->set_relative(0, false);456skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);457CHECK_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.");458CHECK_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.");459CHECK_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.");460}461462SUBCASE("[CopyTransformModifier3D] Enable all axes, invert 2 axes, additive=false, relative=true") {463mod->set_additive(0, false);464mod->set_relative(0, true);465skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);466CHECK_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.");467CHECK_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.");468CHECK_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.");469}470471SUBCASE("[CopyTransformModifier3D] Enable all axes, invert 2 axes, additive=true, relative=true") {472mod->set_additive(0, true);473mod->set_relative(0, true);474skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);475CHECK_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.");476CHECK_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.");477CHECK_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.");478}479480// ===== [CopyTransformModifier3D] Enable all axes, invert all axes =====481mod->set_axis_x_enabled(0, true);482mod->set_axis_y_enabled(0, true);483mod->set_axis_z_enabled(0, true);484mod->set_axis_x_inverted(0, true);485mod->set_axis_y_inverted(0, true);486mod->set_axis_z_inverted(0, true);487488SUBCASE("[CopyTransformModifier3D] Enable all axes, invert all axes, additive=false, relative=false") {489mod->set_additive(0, false);490mod->set_relative(0, false);491skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);492CHECK_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.");493CHECK_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.");494CHECK_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.");495}496497SUBCASE("[CopyTransformModifier3D] Enable all axes, invert all axes, additive=true, relative=false") {498mod->set_additive(0, true);499mod->set_relative(0, false);500skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);501CHECK_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.");502CHECK_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.");503CHECK_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.");504}505506SUBCASE("[CopyTransformModifier3D] Enable all axes, invert all axes, additive=false, relative=true") {507mod->set_additive(0, false);508mod->set_relative(0, true);509skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);510CHECK_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.");511CHECK_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.");512CHECK_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.");513}514515SUBCASE("[CopyTransformModifier3D] Enable all axes, invert all axes, additive=true, relative=true") {516mod->set_additive(0, true);517mod->set_relative(0, true);518skeleton->notification(Skeleton3D::NOTIFICATION_UPDATE_SKELETON);519CHECK_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.");520CHECK_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.");521CHECK_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.");522}523524memdelete(modified);525memdelete(mod);526memdelete(skeleton);527}528} // namespace TestCopyTransformModifier3D529530531