summaryrefslogtreecommitdiff
path: root/scene/resources
diff options
context:
space:
mode:
Diffstat (limited to 'scene/resources')
-rw-r--r--scene/resources/canvas_item_material.cpp7
-rw-r--r--scene/resources/default_theme/icon_grid_layout.pngbin640 -> 2170 bytes
-rw-r--r--scene/resources/default_theme/theme_data.h6
-rw-r--r--scene/resources/material.cpp12
-rw-r--r--scene/resources/packed_scene.cpp8
-rw-r--r--scene/resources/particles_material.cpp7
-rw-r--r--scene/resources/skeleton_modification_2d.cpp8
-rw-r--r--scene/resources/skeleton_modification_2d_ccdik.cpp8
-rw-r--r--scene/resources/skeleton_modification_2d_fabrik.cpp10
-rw-r--r--scene/resources/skeleton_modification_2d_jiggle.cpp6
-rw-r--r--scene/resources/skeleton_modification_2d_lookat.cpp2
-rw-r--r--scene/resources/skeleton_modification_2d_twoboneik.cpp4
-rw-r--r--scene/resources/skeleton_modification_3d.cpp170
-rw-r--r--scene/resources/skeleton_modification_3d.h76
-rw-r--r--scene/resources/skeleton_modification_3d_ccdik.cpp474
-rw-r--r--scene/resources/skeleton_modification_3d_ccdik.h114
-rw-r--r--scene/resources/skeleton_modification_3d_fabrik.cpp628
-rw-r--r--scene/resources/skeleton_modification_3d_fabrik.h122
-rw-r--r--scene/resources/skeleton_modification_3d_jiggle.cpp573
-rw-r--r--scene/resources/skeleton_modification_3d_jiggle.h138
-rw-r--r--scene/resources/skeleton_modification_3d_lookat.cpp265
-rw-r--r--scene/resources/skeleton_modification_3d_lookat.h89
-rw-r--r--scene/resources/skeleton_modification_3d_stackholder.cpp104
-rw-r--r--scene/resources/skeleton_modification_3d_stackholder.h59
-rw-r--r--scene/resources/skeleton_modification_3d_twoboneik.cpp599
-rw-r--r--scene/resources/skeleton_modification_3d_twoboneik.h118
-rw-r--r--scene/resources/skeleton_modification_stack_3d.cpp222
-rw-r--r--scene/resources/skeleton_modification_stack_3d.h91
-rw-r--r--scene/resources/sky_material.cpp15
-rw-r--r--scene/resources/visual_shader.cpp21
-rw-r--r--scene/resources/visual_shader.h1
-rw-r--r--scene/resources/visual_shader_nodes.cpp433
-rw-r--r--scene/resources/visual_shader_nodes.h97
-rw-r--r--scene/resources/visual_shader_particle_nodes.cpp25
-rw-r--r--scene/resources/visual_shader_particle_nodes.h4
-rw-r--r--scene/resources/world_margin_shape_2d.cpp (renamed from scene/resources/line_shape_2d.cpp)36
-rw-r--r--scene/resources/world_margin_shape_2d.h (renamed from scene/resources/line_shape_2d.h)16
37 files changed, 4352 insertions, 216 deletions
diff --git a/scene/resources/canvas_item_material.cpp b/scene/resources/canvas_item_material.cpp
index 7ba57a6532..7501efea9e 100644
--- a/scene/resources/canvas_item_material.cpp
+++ b/scene/resources/canvas_item_material.cpp
@@ -30,6 +30,8 @@
#include "canvas_item_material.h"
+#include "core/version.h"
+
Mutex CanvasItemMaterial::material_mutex;
SelfList<CanvasItemMaterial>::List *CanvasItemMaterial::dirty_materials = nullptr;
Map<CanvasItemMaterial::MaterialKey, CanvasItemMaterial::ShaderData> CanvasItemMaterial::shader_map;
@@ -78,7 +80,10 @@ void CanvasItemMaterial::_update_shader() {
//must create a shader!
- String code = "shader_type canvas_item;\nrender_mode ";
+ // Add a comment to describe the shader origin (useful when converting to ShaderMaterial).
+ String code = "// NOTE: Shader automatically converted from " VERSION_NAME " " VERSION_FULL_CONFIG "'s CanvasItemMaterial.\n\n";
+
+ code += "shader_type canvas_item;\nrender_mode ";
switch (blend_mode) {
case BLEND_MODE_MIX:
code += "blend_mix";
diff --git a/scene/resources/default_theme/icon_grid_layout.png b/scene/resources/default_theme/icon_grid_layout.png
index 00a6179d5e..a249252a79 100644
--- a/scene/resources/default_theme/icon_grid_layout.png
+++ b/scene/resources/default_theme/icon_grid_layout.png
Binary files differ
diff --git a/scene/resources/default_theme/theme_data.h b/scene/resources/default_theme/theme_data.h
index 865ee86c76..4a53926066 100644
--- a/scene/resources/default_theme/theme_data.h
+++ b/scene/resources/default_theme/theme_data.h
@@ -178,11 +178,11 @@ static const unsigned char icon_folder_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x2e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x6, 0x78, 0x70, 0xf4, 0xc1, 0x7f, 0x24, 0x78, 0x18, 0x53, 0xc1, 0x7f, 0x54, 0x48, 0x50, 0xc1, 0x43, 0x1b, 0xbc, 0xa, 0x50, 0xad, 0x23, 0xa4, 0xe0, 0xff, 0x70, 0x52, 0x70, 0x18, 0x97, 0xf4, 0xfd, 0x43, 0xd4, 0x88, 0x4a, 0x0, 0x5a, 0xcb, 0x18, 0xab, 0x5e, 0xd9, 0x1a, 0x53, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
-static const unsigned char icon_grid_minimap_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc3, 0x0, 0x0, 0xe, 0xc3, 0x1, 0xc7, 0x6f, 0xa8, 0x64, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x0, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x6e, 0x6b, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x9b, 0xee, 0x3c, 0x1a, 0x0, 0x0, 0x2, 0xd, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0x75, 0x93, 0x31, 0x68, 0x14, 0x51, 0x10, 0x86, 0xbf, 0xd9, 0xd, 0xbb, 0xde, 0x76, 0x82, 0x21, 0xf8, 0xe0, 0xbc, 0x5d, 0x8b, 0x80, 0x69, 0x6c, 0xd2, 0x5a, 0x6a, 0x91, 0xc3, 0xd2, 0x46, 0x22, 0x8, 0x9, 0x89, 0x70, 0x85, 0x10, 0x41, 0xd, 0x24, 0x45, 0xb0, 0xb, 0x68, 0x11, 0x14, 0x24, 0x10, 0x22, 0x62, 0x21, 0x41, 0xe, 0x4b, 0x21, 0xa4, 0xb7, 0x49, 0x17, 0xb1, 0x8, 0xb9, 0xdd, 0xc7, 0x86, 0x33, 0x21, 0xe1, 0x3a, 0x8f, 0x64, 0x61, 0x6f, 0x2c, 0xbc, 0x3b, 0x36, 0xb9, 0xdc, 0xc0, 0x2b, 0xde, 0xcc, 0xfc, 0xf3, 0xff, 0xfc, 0xcc, 0x48, 0xa3, 0xd1, 0x78, 0x20, 0x22, 0x13, 0xbe, 0xef, 0xaf, 0xdf, 0xac, 0xd7, 0x1f, 0xe1, 0x38, 0xd3, 0xa8, 0x2a, 0xf0, 0x45, 0x6a, 0xb5, 0xcf, 0x5c, 0x11, 0xcd, 0x66, 0x33, 0x38, 0x3f, 0x3f, 0x9f, 0x13, 0x91, 0x7d, 0xb1, 0xd6, 0x6e, 0xaa, 0xea, 0xd3, 0xe0, 0xe8, 0xe8, 0xde, 0xe8, 0xee, 0xee, 0x37, 0xc0, 0xe9, 0xf6, 0x75, 0xf0, 0xfd, 0x9, 0x99, 0x9d, 0x6d, 0x15, 0x81, 0x59, 0x96, 0x3d, 0x3, 0x5e, 0x2, 0x63, 0x22, 0xf2, 0x69, 0xa4, 0x57, 0x1c, 0xdd, 0xdb, 0xfb, 0x5b, 0x0, 0x3, 0x38, 0x67, 0x41, 0x30, 0x11, 0xc7, 0xf1, 0x13, 0x0, 0x11, 0x71, 0xb2, 0x2c, 0x7b, 0xd8, 0xad, 0xad, 0x2, 0x6f, 0xb9, 0x0, 0x38, 0x3c, 0xfc, 0x5, 0x9c, 0xf6, 0xff, 0x22, 0x27, 0x27, 0xe3, 0xe3, 0x7f, 0xa, 0x3, 0x67, 0x45, 0xe4, 0xbb, 0xe7, 0x79, 0xb7, 0xc3, 0x30, 0x7c, 0xd7, 0x67, 0xe9, 0xe3, 0x67, 0x66, 0x5c, 0x60, 0x1, 0x50, 0x40, 0x51, 0x7d, 0x71, 0x6b, 0x72, 0xf2, 0x20, 0x8a, 0xa2, 0xf9, 0x28, 0x8a, 0xe6, 0x1, 0x3a, 0x9d, 0xce, 0x4f, 0x63, 0x4c, 0x3b, 0x4d, 0xd3, 0xd2, 0xc0, 0x80, 0x3c, 0xcf, 0xf, 0x92, 0xa9, 0xa9, 0x31, 0x60, 0x5, 0x58, 0x91, 0x5a, 0xed, 0xc7, 0x15, 0xfe, 0x95, 0xac, 0xb5, 0xcf, 0xf3, 0x3c, 0x3f, 0xe8, 0x25, 0x46, 0xa, 0xc5, 0xd, 0x11, 0x59, 0xb3, 0xd5, 0xea, 0x1b, 0xa0, 0x95, 0x54, 0xab, 0x5b, 0x97, 0xd1, 0x22, 0xb2, 0xa6, 0xaa, 0x6d, 0x60, 0xd, 0x58, 0xba, 0xa0, 0x20, 0xc, 0xc3, 0x65, 0xd7, 0x75, 0x23, 0xe0, 0x2e, 0xb0, 0x1, 0x5c, 0xbf, 0xf4, 0x0, 0xbe, 0xba, 0xae, 0x1b, 0x85, 0x61, 0xb8, 0x3c, 0xa0, 0x20, 0x4d, 0xd3, 0x52, 0xb9, 0x5c, 0x6e, 0xc5, 0x71, 0xbc, 0x23, 0x22, 0xd3, 0x61, 0x18, 0xde, 0x2f, 0xb2, 0x27, 0x49, 0xa2, 0xaa, 0xba, 0x53, 0x2e, 0x97, 0x5b, 0x69, 0x9a, 0x96, 0xf2, 0x3c, 0x1f, 0xf0, 0xc0, 0x5a, 0x6b, 0x5f, 0x1, 0x25, 0x86, 0x84, 0xe3, 0x38, 0x9e, 0xb5, 0x76, 0x2e, 0xcf, 0xf3, 0xfd, 0x1, 0x5, 0x22, 0xb2, 0xa1, 0xaa, 0x4b, 0x22, 0x72, 0xad, 0xcb, 0x38, 0xe0, 0x81, 0xaa, 0x7e, 0x0, 0xce, 0x44, 0xe4, 0xbd, 0xaa, 0xbe, 0xbe, 0xa0, 0xa0, 0x52, 0xa9, 0x2c, 0x7a, 0x9e, 0x17, 0x1, 0x3d, 0xe0, 0x55, 0x1e, 0x6c, 0x79, 0x9e, 0x17, 0x55, 0x2a, 0x95, 0xc5, 0x1, 0x5, 0xcd, 0x66, 0x33, 0x30, 0xc6, 0x9c, 0xc6, 0x71, 0xbc, 0x2d, 0x22, 0x8f, 0x87, 0x78, 0xb0, 0x6d, 0x8c, 0x39, 0xed, 0xae, 0x74, 0xdf, 0x83, 0x3a, 0x70, 0x9c, 0x65, 0x59, 0x23, 0x49, 0x92, 0x5, 0x11, 0x9, 0x86, 0x79, 0x20, 0x22, 0x41, 0x92, 0x24, 0xb, 0x59, 0x96, 0x35, 0x80, 0x63, 0xa0, 0x2e, 0x3d, 0xf6, 0xc2, 0x91, 0xdc, 0x0, 0x5c, 0x55, 0x5d, 0xbf, 0x4, 0x9e, 0x3, 0x72, 0xfe, 0xaf, 0xfb, 0xaa, 0xe7, 0x79, 0x1f, 0x8d, 0x31, 0x6d, 0x29, 0x36, 0xf5, 0xce, 0x14, 0xb8, 0x33, 0x44, 0xc4, 0x6f, 0xdf, 0xf7, 0xd7, 0x8d, 0x31, 0xed, 0x5e, 0xe2, 0x1f, 0xb, 0x5c, 0xe2, 0xcb, 0xd, 0x9b, 0x69, 0xcb, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+static const unsigned char icon_grid_layout_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x5, 0x52, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x3d, 0x22, 0xef, 0xbb, 0xbf, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x57, 0x35, 0x4d, 0x30, 0x4d, 0x70, 0x43, 0x65, 0x68, 0x69, 0x48, 0x7a, 0x72, 0x65, 0x53, 0x7a, 0x4e, 0x54, 0x63, 0x7a, 0x6b, 0x63, 0x39, 0x64, 0x22, 0x3f, 0x3e, 0xa, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x35, 0x2e, 0x30, 0x22, 0x3e, 0xa, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0xa, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x64, 0x63, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x70, 0x75, 0x72, 0x6c, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x64, 0x63, 0x2f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x31, 0x2e, 0x31, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x65, 0x78, 0x69, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x74, 0x69, 0x66, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x6d, 0x6d, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x73, 0x74, 0x45, 0x76, 0x74, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x73, 0x54, 0x79, 0x70, 0x65, 0x2f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x23, 0x22, 0xa, 0x20, 0x20, 0x20, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x58, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x31, 0x36, 0x22, 0xa, 0x20, 0x20, 0x20, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x59, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x31, 0x36, 0x22, 0xa, 0x20, 0x20, 0x20, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x70, 0x61, 0x63, 0x65, 0x3d, 0x22, 0x31, 0x22, 0xa, 0x20, 0x20, 0x20, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x3d, 0x22, 0x31, 0x36, 0x22, 0xa, 0x20, 0x20, 0x20, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3d, 0x22, 0x31, 0x36, 0x22, 0xa, 0x20, 0x20, 0x20, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x3d, 0x22, 0x32, 0x22, 0xa, 0x20, 0x20, 0x20, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x58, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x37, 0x32, 0x2e, 0x30, 0x22, 0xa, 0x20, 0x20, 0x20, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x59, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x37, 0x32, 0x2e, 0x30, 0x22, 0xa, 0x20, 0x20, 0x20, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x3d, 0x22, 0x33, 0x22, 0xa, 0x20, 0x20, 0x20, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x3a, 0x49, 0x43, 0x43, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x3d, 0x22, 0x73, 0x52, 0x47, 0x42, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x22, 0xa, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, 0x3d, 0x22, 0x32, 0x30, 0x32, 0x31, 0x2d, 0x30, 0x38, 0x2d, 0x31, 0x35, 0x54, 0x30, 0x39, 0x3a, 0x34, 0x31, 0x3a, 0x34, 0x30, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0x22, 0xa, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x44, 0x61, 0x74, 0x65, 0x3d, 0x22, 0x32, 0x30, 0x32, 0x31, 0x2d, 0x30, 0x38, 0x2d, 0x31, 0x35, 0x54, 0x30, 0x39, 0x3a, 0x34, 0x31, 0x3a, 0x34, 0x30, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0x22, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x3c, 0x64, 0x63, 0x3a, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x41, 0x6c, 0x74, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x6c, 0x69, 0x20, 0x78, 0x6d, 0x6c, 0x3a, 0x6c, 0x61, 0x6e, 0x67, 0x3d, 0x22, 0x78, 0x2d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x22, 0x3e, 0x47, 0x72, 0x69, 0x64, 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x6c, 0x69, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x41, 0x6c, 0x74, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x64, 0x63, 0x3a, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x53, 0x65, 0x71, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x6c, 0x69, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x45, 0x76, 0x74, 0x3a, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x64, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x45, 0x76, 0x74, 0x3a, 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x41, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x20, 0x44, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x20, 0x31, 0x2e, 0x39, 0x2e, 0x31, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x45, 0x76, 0x74, 0x3a, 0x77, 0x68, 0x65, 0x6e, 0x3d, 0x22, 0x32, 0x30, 0x32, 0x31, 0x2d, 0x30, 0x38, 0x2d, 0x31, 0x35, 0x54, 0x30, 0x39, 0x3a, 0x34, 0x31, 0x3a, 0x34, 0x30, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0x22, 0x2f, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x53, 0x65, 0x71, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x3e, 0xa, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0xa, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0xa, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0xa, 0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x65, 0x6e, 0x64, 0x3d, 0x22, 0x72, 0x22, 0x3f, 0x3e, 0x10, 0xfa, 0x51, 0xae, 0x0, 0x0, 0x1, 0x82, 0x69, 0x43, 0x43, 0x50, 0x73, 0x52, 0x47, 0x42, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x0, 0x0, 0x28, 0x91, 0x75, 0x91, 0xbf, 0x4b, 0x42, 0x51, 0x14, 0xc7, 0x3f, 0x5a, 0xa1, 0x94, 0x61, 0x50, 0x43, 0x43, 0x83, 0x84, 0x45, 0x83, 0x85, 0x15, 0x84, 0x2d, 0xd, 0x4a, 0xbf, 0xa0, 0x1a, 0xd4, 0x20, 0xab, 0x45, 0x9f, 0xbf, 0x2, 0xb5, 0xc7, 0x7b, 0x4a, 0x48, 0x6b, 0xd0, 0x2a, 0x14, 0x44, 0x2d, 0xfd, 0x1a, 0xea, 0x2f, 0xa8, 0x35, 0x68, 0xe, 0x82, 0xa2, 0x8, 0xa2, 0x2d, 0x68, 0x2e, 0x6a, 0xa9, 0x78, 0x9d, 0xa7, 0x82, 0x11, 0x79, 0x2e, 0xe7, 0x9e, 0xcf, 0xfd, 0xde, 0x7b, 0xe, 0xf7, 0x9e, 0xb, 0xd6, 0x70, 0x46, 0xc9, 0xea, 0x8d, 0x5e, 0xc8, 0xe6, 0xf2, 0x5a, 0x70, 0xd2, 0xef, 0x5a, 0x88, 0x2c, 0xba, 0x6c, 0xcf, 0xd8, 0x71, 0x62, 0xa3, 0xf, 0x5f, 0x54, 0xd1, 0xd5, 0xd9, 0xd0, 0x44, 0x98, 0xba, 0xf6, 0x71, 0x87, 0xc5, 0x8c, 0x37, 0xfd, 0x66, 0xad, 0xfa, 0xe7, 0xfe, 0xb5, 0x96, 0x78, 0x42, 0x57, 0xc0, 0x62, 0x17, 0x1e, 0x53, 0x54, 0x2d, 0x2f, 0x3c, 0x25, 0x3c, 0xb3, 0x96, 0x57, 0x4d, 0xde, 0x16, 0xee, 0x50, 0xd2, 0xd1, 0xb8, 0xf0, 0xa9, 0xb0, 0x47, 0x93, 0xb, 0xa, 0xdf, 0x9a, 0x7a, 0xac, 0xc2, 0x2f, 0x26, 0xa7, 0x2a, 0xfc, 0x65, 0xb2, 0x16, 0xe, 0x6, 0xc0, 0xda, 0x26, 0xec, 0x4a, 0xfd, 0xe2, 0xd8, 0x2f, 0x56, 0xd2, 0x5a, 0x56, 0x58, 0x5e, 0x8e, 0x3b, 0x9b, 0x29, 0x28, 0xd5, 0xfb, 0x98, 0x2f, 0x71, 0x24, 0x72, 0xf3, 0x21, 0x89, 0xdd, 0xe2, 0x5d, 0xe8, 0x4, 0x99, 0xc4, 0x8f, 0x8b, 0x69, 0xc6, 0x9, 0x30, 0xc2, 0x20, 0xa3, 0x32, 0x8f, 0xd0, 0xcf, 0x10, 0x3, 0xb2, 0xa2, 0x4e, 0xbe, 0xb7, 0x9c, 0x3f, 0xc7, 0xaa, 0xe4, 0x2a, 0x32, 0xab, 0x14, 0xd1, 0x58, 0x21, 0x45, 0x9a, 0x3c, 0x1e, 0x51, 0xb, 0x52, 0x3d, 0x21, 0x31, 0x29, 0x7a, 0x42, 0x46, 0x86, 0xa2, 0xd9, 0xff, 0xbf, 0x7d, 0xd5, 0x93, 0xc3, 0x43, 0x95, 0xea, 0xe, 0x3f, 0x34, 0x3d, 0x19, 0xc6, 0x5b, 0xf, 0xd8, 0xb6, 0xe0, 0xbb, 0x64, 0x18, 0x9f, 0x87, 0x86, 0xf1, 0x7d, 0x4, 0xd, 0x8f, 0x70, 0x91, 0xab, 0xe5, 0xaf, 0x1e, 0x80, 0xef, 0x5d, 0xf4, 0x52, 0x4d, 0x73, 0xef, 0x83, 0x73, 0x3, 0xce, 0x2e, 0x6b, 0x5a, 0x6c, 0x7, 0xce, 0x37, 0xa1, 0xf3, 0x41, 0x8d, 0x6a, 0xd1, 0xb2, 0xd4, 0x20, 0x6e, 0x4d, 0x26, 0xe1, 0xf5, 0x4, 0x5a, 0x23, 0xd0, 0x7e, 0xd, 0xcd, 0x4b, 0x95, 0x9e, 0x55, 0xf7, 0x39, 0xbe, 0x87, 0xf0, 0xba, 0x7c, 0xd5, 0x15, 0xec, 0xee, 0x41, 0xaf, 0x9c, 0x77, 0x2e, 0xff, 0x0, 0xa6, 0xc4, 0x68, 0x3, 0x1f, 0xd7, 0x32, 0xd8, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x1, 0x40, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0x9d, 0xd2, 0xbd, 0x4a, 0x5d, 0x41, 0x14, 0x86, 0xe1, 0xe7, 0x84, 0xa3, 0x51, 0x22, 0x82, 0x41, 0x8c, 0x12, 0x2, 0xeb, 0x1a, 0x2c, 0x84, 0x4, 0x52, 0x88, 0xb1, 0xd2, 0x26, 0x76, 0x62, 0x67, 0xeb, 0xd, 0x78, 0x19, 0x56, 0x69, 0x82, 0x62, 0x67, 0x25, 0x82, 0x60, 0x97, 0xc2, 0x52, 0x8, 0x24, 0x95, 0x16, 0xa9, 0x6, 0x8c, 0x18, 0xb5, 0x30, 0x88, 0xa7, 0x90, 0x88, 0x5a, 0xec, 0x39, 0x66, 0xd8, 0x7a, 0x54, 0x5c, 0xb0, 0xd8, 0xcc, 0xbb, 0xe7, 0xfb, 0x58, 0x3f, 0xd3, 0x50, 0x8b, 0x94, 0xd2, 0x6b, 0xcc, 0x60, 0x25, 0x22, 0xae, 0xea, 0xff, 0xeb, 0xd1, 0xbc, 0x47, 0xfc, 0xd, 0xbd, 0x58, 0x7e, 0x4c, 0xc, 0x8d, 0x42, 0x3c, 0x82, 0x9f, 0xe8, 0xc1, 0x26, 0xce, 0x3b, 0x68, 0xae, 0xf1, 0x25, 0x22, 0x76, 0xeb, 0x15, 0xf4, 0xa3, 0xf, 0x97, 0x18, 0xce, 0xdf, 0x4e, 0x6, 0xfd, 0x77, 0x2a, 0xc8, 0x55, 0x4, 0xb6, 0xf1, 0x1b, 0x1f, 0x23, 0xe2, 0xfa, 0x49, 0x2d, 0xa4, 0x94, 0x7a, 0x30, 0x98, 0xd9, 0x5b, 0x4c, 0x63, 0x49, 0x35, 0xb, 0x68, 0xe1, 0x2f, 0x46, 0xf0, 0x22, 0xb3, 0x93, 0x88, 0xb8, 0x68, 0x1f, 0x36, 0xb0, 0x9f, 0x73, 0x7, 0x87, 0xf8, 0x53, 0xb0, 0x23, 0x2c, 0xe0, 0xa0, 0x60, 0xeb, 0xa, 0xb7, 0x1, 0xac, 0x60, 0x34, 0x5f, 0x1a, 0x52, 0xcd, 0x67, 0x16, 0x73, 0xe8, 0xca, 0x6c, 0x3f, 0xdf, 0x59, 0xcd, 0x9a, 0x5b, 0x83, 0x67, 0x47, 0x7b, 0xb, 0xa7, 0x98, 0xcf, 0x9, 0xc7, 0xaa, 0x2d, 0xac, 0xe5, 0xf3, 0xbf, 0xcc, 0xde, 0xe1, 0x47, 0x66, 0x5b, 0xa5, 0xc1, 0x67, 0xff, 0x87, 0x78, 0xa5, 0x9a, 0xc1, 0x1a, 0x5e, 0x65, 0xd6, 0xc2, 0x24, 0x3e, 0xe4, 0x36, 0xe0, 0x84, 0xda, 0x1a, 0x1f, 0x8a, 0x94, 0xd2, 0x6, 0xc6, 0x30, 0x1e, 0x11, 0xbf, 0xda, 0xbc, 0x7c, 0x89, 0xc3, 0x58, 0x44, 0x77, 0x7, 0x8f, 0x2e, 0x4c, 0xa9, 0x1e, 0xd1, 0xa7, 0x88, 0xd8, 0x29, 0x5b, 0xa0, 0x7a, 0xc2, 0xf1, 0x80, 0x41, 0x23, 0xe7, 0x4b, 0xbc, 0x79, 0x6a, 0xe5, 0x65, 0xb, 0x5f, 0x53, 0x4a, 0x67, 0x29, 0xa5, 0xf7, 0x25, 0x6f, 0x76, 0x12, 0xdc, 0x13, 0x7b, 0x98, 0x88, 0x88, 0xef, 0x25, 0xbc, 0x1, 0x6c, 0x4d, 0x56, 0x9e, 0x2a, 0x4e, 0x48, 0xae, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
-static const unsigned char icon_grid_layout_png[] = {
+static const unsigned char icon_grid_minimap_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc3, 0x0, 0x0, 0xe, 0xc3, 0x1, 0xc7, 0x6f, 0xa8, 0x64, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x0, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x6e, 0x6b, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x9b, 0xee, 0x3c, 0x1a, 0x0, 0x0, 0x2, 0xd, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0x75, 0x93, 0x31, 0x68, 0x14, 0x51, 0x10, 0x86, 0xbf, 0xd9, 0xd, 0xbb, 0xde, 0x76, 0x82, 0x21, 0xf8, 0xe0, 0xbc, 0x5d, 0x8b, 0x80, 0x69, 0x6c, 0xd2, 0x5a, 0x6a, 0x91, 0xc3, 0xd2, 0x46, 0x22, 0x8, 0x9, 0x89, 0x70, 0x85, 0x10, 0x41, 0xd, 0x24, 0x45, 0xb0, 0xb, 0x68, 0x11, 0x14, 0x24, 0x10, 0x22, 0x62, 0x21, 0x41, 0xe, 0x4b, 0x21, 0xa4, 0xb7, 0x49, 0x17, 0xb1, 0x8, 0xb9, 0xdd, 0xc7, 0x86, 0x33, 0x21, 0xe1, 0x3a, 0x8f, 0x64, 0x61, 0x6f, 0x2c, 0xbc, 0x3b, 0x36, 0xb9, 0xdc, 0xc0, 0x2b, 0xde, 0xcc, 0xfc, 0xf3, 0xff, 0xfc, 0xcc, 0x48, 0xa3, 0xd1, 0x78, 0x20, 0x22, 0x13, 0xbe, 0xef, 0xaf, 0xdf, 0xac, 0xd7, 0x1f, 0xe1, 0x38, 0xd3, 0xa8, 0x2a, 0xf0, 0x45, 0x6a, 0xb5, 0xcf, 0x5c, 0x11, 0xcd, 0x66, 0x33, 0x38, 0x3f, 0x3f, 0x9f, 0x13, 0x91, 0x7d, 0xb1, 0xd6, 0x6e, 0xaa, 0xea, 0xd3, 0xe0, 0xe8, 0xe8, 0xde, 0xe8, 0xee, 0xee, 0x37, 0xc0, 0xe9, 0xf6, 0x75, 0xf0, 0xfd, 0x9, 0x99, 0x9d, 0x6d, 0x15, 0x81, 0x59, 0x96, 0x3d, 0x3, 0x5e, 0x2, 0x63, 0x22, 0xf2, 0x69, 0xa4, 0x57, 0x1c, 0xdd, 0xdb, 0xfb, 0x5b, 0x0, 0x3, 0x38, 0x67, 0x41, 0x30, 0x11, 0xc7, 0xf1, 0x13, 0x0, 0x11, 0x71, 0xb2, 0x2c, 0x7b, 0xd8, 0xad, 0xad, 0x2, 0x6f, 0xb9, 0x0, 0x38, 0x3c, 0xfc, 0x5, 0x9c, 0xf6, 0xff, 0x22, 0x27, 0x27, 0xe3, 0xe3, 0x7f, 0xa, 0x3, 0x67, 0x45, 0xe4, 0xbb, 0xe7, 0x79, 0xb7, 0xc3, 0x30, 0x7c, 0xd7, 0x67, 0xe9, 0xe3, 0x67, 0x66, 0x5c, 0x60, 0x1, 0x50, 0x40, 0x51, 0x7d, 0x71, 0x6b, 0x72, 0xf2, 0x20, 0x8a, 0xa2, 0xf9, 0x28, 0x8a, 0xe6, 0x1, 0x3a, 0x9d, 0xce, 0x4f, 0x63, 0x4c, 0x3b, 0x4d, 0xd3, 0xd2, 0xc0, 0x80, 0x3c, 0xcf, 0xf, 0x92, 0xa9, 0xa9, 0x31, 0x60, 0x5, 0x58, 0x91, 0x5a, 0xed, 0xc7, 0x15, 0xfe, 0x95, 0xac, 0xb5, 0xcf, 0xf3, 0x3c, 0x3f, 0xe8, 0x25, 0x46, 0xa, 0xc5, 0xd, 0x11, 0x59, 0xb3, 0xd5, 0xea, 0x1b, 0xa0, 0x95, 0x54, 0xab, 0x5b, 0x97, 0xd1, 0x22, 0xb2, 0xa6, 0xaa, 0x6d, 0x60, 0xd, 0x58, 0xba, 0xa0, 0x20, 0xc, 0xc3, 0x65, 0xd7, 0x75, 0x23, 0xe0, 0x2e, 0xb0, 0x1, 0x5c, 0xbf, 0xf4, 0x0, 0xbe, 0xba, 0xae, 0x1b, 0x85, 0x61, 0xb8, 0x3c, 0xa0, 0x20, 0x4d, 0xd3, 0x52, 0xb9, 0x5c, 0x6e, 0xc5, 0x71, 0xbc, 0x23, 0x22, 0xd3, 0x61, 0x18, 0xde, 0x2f, 0xb2, 0x27, 0x49, 0xa2, 0xaa, 0xba, 0x53, 0x2e, 0x97, 0x5b, 0x69, 0x9a, 0x96, 0xf2, 0x3c, 0x1f, 0xf0, 0xc0, 0x5a, 0x6b, 0x5f, 0x1, 0x25, 0x86, 0x84, 0xe3, 0x38, 0x9e, 0xb5, 0x76, 0x2e, 0xcf, 0xf3, 0xfd, 0x1, 0x5, 0x22, 0xb2, 0xa1, 0xaa, 0x4b, 0x22, 0x72, 0xad, 0xcb, 0x38, 0xe0, 0x81, 0xaa, 0x7e, 0x0, 0xce, 0x44, 0xe4, 0xbd, 0xaa, 0xbe, 0xbe, 0xa0, 0xa0, 0x52, 0xa9, 0x2c, 0x7a, 0x9e, 0x17, 0x1, 0x3d, 0xe0, 0x55, 0x1e, 0x6c, 0x79, 0x9e, 0x17, 0x55, 0x2a, 0x95, 0xc5, 0x1, 0x5, 0xcd, 0x66, 0x33, 0x30, 0xc6, 0x9c, 0xc6, 0x71, 0xbc, 0x2d, 0x22, 0x8f, 0x87, 0x78, 0xb0, 0x6d, 0x8c, 0x39, 0xed, 0xae, 0x74, 0xdf, 0x83, 0x3a, 0x70, 0x9c, 0x65, 0x59, 0x23, 0x49, 0x92, 0x5, 0x11, 0x9, 0x86, 0x79, 0x20, 0x22, 0x41, 0x92, 0x24, 0xb, 0x59, 0x96, 0x35, 0x80, 0x63, 0xa0, 0x2e, 0x3d, 0xf6, 0xc2, 0x91, 0xdc, 0x0, 0x5c, 0x55, 0x5d, 0xbf, 0x4, 0x9e, 0x3, 0x72, 0xfe, 0xaf, 0xfb, 0xaa, 0xe7, 0x79, 0x1f, 0x8d, 0x31, 0x6d, 0x29, 0x36, 0xf5, 0xce, 0x14, 0xb8, 0x33, 0x44, 0xc4, 0x6f, 0xdf, 0xf7, 0xd7, 0x8d, 0x31, 0xed, 0x5e, 0xe2, 0x1f, 0xb, 0x5c, 0xe2, 0xcb, 0xd, 0x9b, 0x69, 0xcb, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 08f7274ff6..1d2a2ef26c 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -31,6 +31,7 @@
#include "material.h"
#include "core/config/engine.h"
+#include "core/version.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_settings.h"
@@ -268,7 +269,7 @@ void ShaderMaterial::_bind_methods() {
void ShaderMaterial::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
#ifdef TOOLS_ENABLED
- const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", 0) ? "'" : "\"";
+ const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\"";
#else
const String quote_style = "\"";
#endif
@@ -279,7 +280,7 @@ void ShaderMaterial::get_argument_options(const StringName &p_function, int p_id
List<PropertyInfo> pl;
shader->get_param_list(&pl);
for (const PropertyInfo &E : pl) {
- r_options->push_back(quote_style + E.name.replace_first("shader_param/", "") + quote_style);
+ r_options->push_back(E.name.replace_first("shader_param/", "").quote(quote_style));
}
}
}
@@ -469,7 +470,12 @@ void BaseMaterial3D::_update_shader() {
//must create a shader!
- String code = "shader_type spatial;\nrender_mode ";
+ // Add a comment to describe the shader origin (useful when converting to ShaderMaterial).
+ String code = vformat(
+ "// NOTE: Shader automatically converted from " VERSION_NAME " " VERSION_FULL_CONFIG "'s %s.\n\n",
+ orm ? "ORMMaterial3D" : "StandardMaterial3D");
+
+ code += "shader_type spatial;\nrender_mode ";
switch (blend_mode) {
case BLEND_MODE_MIX:
code += "blend_mix";
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index eddbb9a842..54bfc427c4 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -162,12 +162,14 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
}
WARN_PRINT(vformat("Node %s of type %s cannot be created. A placeholder will be created instead.", snames[n.name], snames[n.type]).ascii().get_data());
if (n.parent >= 0 && n.parent < nc && ret_nodes[n.parent]) {
- if (Object::cast_to<Node3D>(ret_nodes[n.parent])) {
- obj = memnew(Node3D);
- } else if (Object::cast_to<Control>(ret_nodes[n.parent])) {
+ if (Object::cast_to<Control>(ret_nodes[n.parent])) {
obj = memnew(Control);
} else if (Object::cast_to<Node2D>(ret_nodes[n.parent])) {
obj = memnew(Node2D);
+#ifndef _3D_DISABLED
+ } else if (Object::cast_to<Node3D>(ret_nodes[n.parent])) {
+ obj = memnew(Node3D);
+#endif // _3D_DISABLED
}
}
diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp
index 91569e65d6..34f4142a14 100644
--- a/scene/resources/particles_material.cpp
+++ b/scene/resources/particles_material.cpp
@@ -30,6 +30,8 @@
#include "particles_material.h"
+#include "core/version.h"
+
Mutex ParticlesMaterial::material_mutex;
SelfList<ParticlesMaterial>::List *ParticlesMaterial::dirty_materials = nullptr;
Map<ParticlesMaterial::MaterialKey, ParticlesMaterial::ShaderData> ParticlesMaterial::shader_map;
@@ -141,7 +143,10 @@ void ParticlesMaterial::_update_shader() {
//must create a shader!
- String code = "shader_type particles;\n";
+ // Add a comment to describe the shader origin (useful when converting to ShaderMaterial).
+ String code = "// NOTE: Shader automatically converted from " VERSION_NAME " " VERSION_FULL_CONFIG "'s ParticlesMaterial.\n\n";
+
+ code += "shader_type particles;\n";
if (collision_scale) {
code += "render_mode collision_use_scale;\n";
diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp
index b52a60006a..2d5b42ddf4 100644
--- a/scene/resources/skeleton_modification_2d.cpp
+++ b/scene/resources/skeleton_modification_2d.cpp
@@ -166,13 +166,13 @@ void SkeletonModification2D::editor_draw_angle_constraints(Bone2D *p_operation_b
if (operation_bone_parent_bone) {
stack->skeleton->draw_set_transform(
- stack->skeleton->get_global_transform().affine_inverse().xform(p_operation_bone->get_global_position()),
+ stack->skeleton->to_local(p_operation_bone->get_global_position()),
operation_bone_parent_bone->get_global_rotation() - stack->skeleton->get_global_rotation());
} else {
- stack->skeleton->draw_set_transform(stack->skeleton->get_global_transform().affine_inverse().xform(p_operation_bone->get_global_position()));
+ stack->skeleton->draw_set_transform(stack->skeleton->to_local(p_operation_bone->get_global_position()));
}
} else {
- stack->skeleton->draw_set_transform(stack->skeleton->get_global_transform().affine_inverse().xform(p_operation_bone->get_global_position()));
+ stack->skeleton->draw_set_transform(stack->skeleton->to_local(p_operation_bone->get_global_position()));
}
if (p_constraint_inverted) {
@@ -186,7 +186,7 @@ void SkeletonModification2D::editor_draw_angle_constraints(Bone2D *p_operation_b
stack->skeleton->draw_line(Vector2(0, 0), Vector2(Math::cos(arc_angle_max), Math::sin(arc_angle_max)) * p_operation_bone->get_length(), bone_ik_color, 1.0);
} else {
- stack->skeleton->draw_set_transform(stack->skeleton->get_global_transform().affine_inverse().xform(p_operation_bone->get_global_position()));
+ stack->skeleton->draw_set_transform(stack->skeleton->to_local(p_operation_bone->get_global_position()));
stack->skeleton->draw_arc(Vector2(0, 0), p_operation_bone->get_length(), 0, Math_PI * 2, 32, bone_ik_color, 1.0);
stack->skeleton->draw_line(Vector2(0, 0), Vector2(1, 0) * p_operation_bone->get_length(), bone_ik_color, 1.0);
}
diff --git a/scene/resources/skeleton_modification_2d_ccdik.cpp b/scene/resources/skeleton_modification_2d_ccdik.cpp
index 7ea60e584e..6eab8d4afe 100644
--- a/scene/resources/skeleton_modification_2d_ccdik.cpp
+++ b/scene/resources/skeleton_modification_2d_ccdik.cpp
@@ -201,18 +201,18 @@ void SkeletonModification2DCCDIK::_execute_ccdik_joint(int p_joint_idx, Node2D *
if (ccdik_data.rotate_from_joint) {
// To rotate from the joint, simply look at the target!
operation_transform.set_rotation(
- operation_transform.looking_at(p_target->get_global_transform().get_origin()).get_rotation() - operation_bone->get_bone_angle());
+ operation_transform.looking_at(p_target->get_global_position()).get_rotation() - operation_bone->get_bone_angle());
} else {
// How to rotate from the tip: get the difference of rotation needed from the tip to the target, from the perspective of the joint.
// Because we are only using the offset, we do not need to account for the bone angle of the Bone2D node.
- float joint_to_tip = operation_transform.get_origin().angle_to_point(p_tip->get_global_transform().get_origin());
- float joint_to_target = operation_transform.get_origin().angle_to_point(p_target->get_global_transform().get_origin());
+ float joint_to_tip = operation_transform.get_origin().angle_to_point(p_tip->get_global_position());
+ float joint_to_target = operation_transform.get_origin().angle_to_point(p_target->get_global_position());
operation_transform.set_rotation(
operation_transform.get_rotation() + (joint_to_target - joint_to_tip));
}
// Reset scale
- operation_transform.set_scale(operation_bone->get_global_transform().get_scale());
+ operation_transform.set_scale(operation_bone->get_global_scale());
// Apply constraints in globalspace:
if (ccdik_data.enable_constraint && !ccdik_data.constraint_in_localspace) {
diff --git a/scene/resources/skeleton_modification_2d_fabrik.cpp b/scene/resources/skeleton_modification_2d_fabrik.cpp
index aef852f7e4..c03a92b503 100644
--- a/scene/resources/skeleton_modification_2d_fabrik.cpp
+++ b/scene/resources/skeleton_modification_2d_fabrik.cpp
@@ -161,25 +161,25 @@ void SkeletonModification2DFABRIK::_execute(float p_delta) {
}
Bone2D *final_bone2d_node = Object::cast_to<Bone2D>(ObjectDB::get_instance(fabrik_data_chain[fabrik_data_chain.size() - 1].bone2d_node_cache));
- float final_bone2d_angle = final_bone2d_node->get_global_transform().get_rotation();
+ float final_bone2d_angle = final_bone2d_node->get_global_rotation();
if (fabrik_data_chain[fabrik_data_chain.size() - 1].use_target_rotation) {
final_bone2d_angle = target_global_pose.get_rotation();
}
Vector2 final_bone2d_direction = Vector2(Math::cos(final_bone2d_angle), Math::sin(final_bone2d_angle));
float final_bone2d_length = final_bone2d_node->get_length() * MIN(final_bone2d_node->get_global_scale().x, final_bone2d_node->get_global_scale().y);
- float target_distance = (final_bone2d_node->get_global_transform().get_origin() + (final_bone2d_direction * final_bone2d_length)).distance_to(target->get_global_transform().get_origin());
+ float target_distance = (final_bone2d_node->get_global_position() + (final_bone2d_direction * final_bone2d_length)).distance_to(target->get_global_position());
chain_iterations = 0;
while (target_distance > chain_tolarance) {
chain_backwards();
chain_forwards();
- final_bone2d_angle = final_bone2d_node->get_global_transform().get_rotation();
+ final_bone2d_angle = final_bone2d_node->get_global_rotation();
if (fabrik_data_chain[fabrik_data_chain.size() - 1].use_target_rotation) {
final_bone2d_angle = target_global_pose.get_rotation();
}
final_bone2d_direction = Vector2(Math::cos(final_bone2d_angle), Math::sin(final_bone2d_angle));
- target_distance = (final_bone2d_node->get_global_transform().get_origin() + (final_bone2d_direction * final_bone2d_length)).distance_to(target->get_global_transform().get_origin());
+ target_distance = (final_bone2d_node->get_global_position() + (final_bone2d_direction * final_bone2d_length)).distance_to(target->get_global_position());
chain_iterations += 1;
if (chain_iterations >= chain_max_iterations) {
@@ -210,7 +210,7 @@ void SkeletonModification2DFABRIK::_execute(float p_delta) {
chain_trans.set_rotation(chain_trans.get_rotation() - joint_bone2d_node->get_bone_angle());
// Reset scale
- chain_trans.set_scale(joint_bone2d_node->get_global_transform().get_scale());
+ chain_trans.set_scale(joint_bone2d_node->get_global_scale());
// Apply to the bone, and to the override
joint_bone2d_node->set_global_transform(chain_trans);
diff --git a/scene/resources/skeleton_modification_2d_jiggle.cpp b/scene/resources/skeleton_modification_2d_jiggle.cpp
index 2547083336..84abc9d020 100644
--- a/scene/resources/skeleton_modification_2d_jiggle.cpp
+++ b/scene/resources/skeleton_modification_2d_jiggle.cpp
@@ -171,7 +171,7 @@ void SkeletonModification2DJiggle::_execute_jiggle_joint(int p_joint_idx, Node2D
}
Transform2D operation_bone_trans = operation_bone->get_global_transform();
- Vector2 target_position = p_target->get_global_transform().get_origin();
+ Vector2 target_position = p_target->get_global_position();
jiggle_data_chain.write[p_joint_idx].force = (target_position - jiggle_data_chain[p_joint_idx].dynamic_position) * jiggle_data_chain[p_joint_idx].stiffness * p_delta;
@@ -215,7 +215,7 @@ void SkeletonModification2DJiggle::_execute_jiggle_joint(int p_joint_idx, Node2D
operation_bone_trans.set_rotation(operation_bone_trans.get_rotation() - operation_bone->get_bone_angle());
// Reset scale
- operation_bone_trans.set_scale(operation_bone->get_global_transform().get_scale());
+ operation_bone_trans.set_scale(operation_bone->get_global_scale());
operation_bone->set_global_transform(operation_bone_trans);
stack->skeleton->set_bone_local_pose_override(jiggle_data_chain[p_joint_idx].bone_idx, operation_bone->get_transform(), stack->strength, true);
@@ -244,7 +244,7 @@ void SkeletonModification2DJiggle::_setup_modification(SkeletonModificationStack
int bone_idx = jiggle_data_chain[i].bone_idx;
if (bone_idx > 0 && bone_idx < stack->skeleton->get_bone_count()) {
Bone2D *bone2d_node = stack->skeleton->get_bone(bone_idx);
- jiggle_data_chain.write[i].dynamic_position = bone2d_node->get_global_transform().get_origin();
+ jiggle_data_chain.write[i].dynamic_position = bone2d_node->get_global_position();
}
}
}
diff --git a/scene/resources/skeleton_modification_2d_lookat.cpp b/scene/resources/skeleton_modification_2d_lookat.cpp
index fd5c8c7cc2..2da770f012 100644
--- a/scene/resources/skeleton_modification_2d_lookat.cpp
+++ b/scene/resources/skeleton_modification_2d_lookat.cpp
@@ -147,7 +147,7 @@ void SkeletonModification2DLookAt::_execute(float p_delta) {
// Look at the target!
operation_transform = operation_transform.looking_at(target_trans.get_origin());
// Apply whatever scale it had prior to looking_at
- operation_transform.set_scale(operation_bone->get_global_transform().get_scale());
+ operation_transform.set_scale(operation_bone->get_global_scale());
// Account for the direction the bone faces in:
operation_transform.set_rotation(operation_transform.get_rotation() - operation_bone->get_bone_angle());
diff --git a/scene/resources/skeleton_modification_2d_twoboneik.cpp b/scene/resources/skeleton_modification_2d_twoboneik.cpp
index 0a91290015..88d80a501f 100644
--- a/scene/resources/skeleton_modification_2d_twoboneik.cpp
+++ b/scene/resources/skeleton_modification_2d_twoboneik.cpp
@@ -142,7 +142,7 @@ void SkeletonModification2DTwoBoneIK::_execute(float p_delta) {
// http://theorangeduck.com/page/simple-two-joint
// https://www.alanzucconi.com/2018/05/02/ik-2d-2/
// With modifications by TwistedTwigleg
- Vector2 target_difference = target->get_global_transform().get_origin() - joint_one_bone->get_global_transform().get_origin();
+ Vector2 target_difference = target->get_global_position() - joint_one_bone->get_global_position();
float joint_one_to_target = target_difference.length();
float angle_atan = Math::atan2(target_difference.y, target_difference.x);
@@ -206,7 +206,7 @@ void SkeletonModification2DTwoBoneIK::_draw_editor_gizmo() {
return;
}
stack->skeleton->draw_set_transform(
- stack->skeleton->get_global_transform().affine_inverse().xform(operation_bone_one->get_global_position()),
+ stack->skeleton->to_local(operation_bone_one->get_global_position()),
operation_bone_one->get_global_rotation() - stack->skeleton->get_global_rotation());
Color bone_ik_color = Color(1.0, 0.65, 0.0, 0.4);
diff --git a/scene/resources/skeleton_modification_3d.cpp b/scene/resources/skeleton_modification_3d.cpp
new file mode 100644
index 0000000000..9306ee14cd
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d.cpp
@@ -0,0 +1,170 @@
+/*************************************************************************/
+/* skeleton_modification_3d.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 "skeleton_modification_3d.h"
+#include "scene/3d/skeleton_3d.h"
+
+void SkeletonModification3D::_execute(real_t p_delta) {
+ if (get_script_instance()) {
+ if (get_script_instance()->has_method("execute")) {
+ get_script_instance()->call("execute", p_delta);
+ }
+ }
+
+ if (!enabled)
+ return;
+}
+
+void SkeletonModification3D::_setup_modification(SkeletonModificationStack3D *p_stack) {
+ stack = p_stack;
+ if (stack) {
+ is_setup = true;
+ } else {
+ WARN_PRINT("Could not setup modification with name " + this->get_name());
+ }
+
+ if (get_script_instance()) {
+ if (get_script_instance()->has_method("setup_modification")) {
+ get_script_instance()->call("setup_modification", p_stack);
+ }
+ }
+}
+
+void SkeletonModification3D::set_enabled(bool p_enabled) {
+ enabled = p_enabled;
+}
+
+bool SkeletonModification3D::get_enabled() {
+ return enabled;
+}
+
+// Helper function. Needed for CCDIK.
+real_t SkeletonModification3D::clamp_angle(real_t p_angle, real_t p_min_bound, real_t p_max_bound, bool p_invert) {
+ // Map to the 0 to 360 range (in radians though) instead of the -180 to 180 range.
+ if (p_angle < 0) {
+ p_angle = Math_TAU + p_angle;
+ }
+
+ // Make min and max in the range of 0 to 360 (in radians), and make sure they are in the right order
+ if (p_min_bound < 0) {
+ p_min_bound = Math_TAU + p_min_bound;
+ }
+ if (p_max_bound < 0) {
+ p_max_bound = Math_TAU + p_max_bound;
+ }
+ if (p_min_bound > p_max_bound) {
+ real_t tmp = p_min_bound;
+ p_min_bound = p_max_bound;
+ p_max_bound = tmp;
+ }
+
+ // Note: May not be the most optimal way to clamp, but it always constraints to the nearest angle.
+ if (p_invert == false) {
+ if (p_angle < p_min_bound || p_angle > p_max_bound) {
+ Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound));
+ Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound));
+ Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle));
+
+ if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) {
+ p_angle = p_min_bound;
+ } else {
+ p_angle = p_max_bound;
+ }
+ }
+ } else {
+ if (p_angle > p_min_bound && p_angle < p_max_bound) {
+ Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound));
+ Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound));
+ Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle));
+
+ if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) {
+ p_angle = p_min_bound;
+ } else {
+ p_angle = p_max_bound;
+ }
+ }
+ }
+ return p_angle;
+}
+
+bool SkeletonModification3D::_print_execution_error(bool p_condition, String p_message) {
+ // If the modification is not setup, don't bother printing the error
+ if (!is_setup) {
+ return p_condition;
+ }
+
+ if (p_condition && !execution_error_found) {
+ ERR_PRINT(p_message);
+ execution_error_found = true;
+ }
+ return p_condition;
+}
+
+Ref<SkeletonModificationStack3D> SkeletonModification3D::get_modification_stack() {
+ return stack;
+}
+
+void SkeletonModification3D::set_is_setup(bool p_is_setup) {
+ is_setup = p_is_setup;
+}
+
+bool SkeletonModification3D::get_is_setup() const {
+ return is_setup;
+}
+
+void SkeletonModification3D::set_execution_mode(int p_mode) {
+ execution_mode = p_mode;
+}
+
+int SkeletonModification3D::get_execution_mode() const {
+ return execution_mode;
+}
+
+void SkeletonModification3D::_bind_methods() {
+ BIND_VMETHOD(MethodInfo("_execute", PropertyInfo(Variant::FLOAT, "delta")));
+ BIND_VMETHOD(MethodInfo("_setup_modification", PropertyInfo(Variant::OBJECT, "modification_stack", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonModificationStack3D")));
+
+ ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &SkeletonModification3D::set_enabled);
+ ClassDB::bind_method(D_METHOD("get_enabled"), &SkeletonModification3D::get_enabled);
+ ClassDB::bind_method(D_METHOD("get_modification_stack"), &SkeletonModification3D::get_modification_stack);
+ ClassDB::bind_method(D_METHOD("set_is_setup", "is_setup"), &SkeletonModification3D::set_is_setup);
+ ClassDB::bind_method(D_METHOD("get_is_setup"), &SkeletonModification3D::get_is_setup);
+ ClassDB::bind_method(D_METHOD("set_execution_mode", "execution_mode"), &SkeletonModification3D::set_execution_mode);
+ ClassDB::bind_method(D_METHOD("get_execution_mode"), &SkeletonModification3D::get_execution_mode);
+ ClassDB::bind_method(D_METHOD("clamp_angle", "angle", "min", "max", "invert"), &SkeletonModification3D::clamp_angle);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "get_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "execution_mode", PROPERTY_HINT_ENUM, "process, physics_process"), "set_execution_mode", "get_execution_mode");
+}
+
+SkeletonModification3D::SkeletonModification3D() {
+ stack = nullptr;
+ is_setup = false;
+}
diff --git a/scene/resources/skeleton_modification_3d.h b/scene/resources/skeleton_modification_3d.h
new file mode 100644
index 0000000000..94ab0bf32c
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d.h
@@ -0,0 +1,76 @@
+/*************************************************************************/
+/* skeleton_modification_3d.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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. */
+/*************************************************************************/
+
+#ifndef SKELETONMODIFICATION3D_H
+#define SKELETONMODIFICATION3D_H
+
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_stack_3d.h"
+
+class SkeletonModificationStack3D;
+
+class SkeletonModification3D : public Resource {
+ GDCLASS(SkeletonModification3D, Resource);
+ friend class Skeleton3D;
+ friend class SkeletonModificationStack3D;
+
+protected:
+ static void _bind_methods();
+
+ SkeletonModificationStack3D *stack;
+ int execution_mode = 0; // 0 = process
+
+ bool enabled = true;
+ bool is_setup = false;
+ bool execution_error_found = false;
+
+ bool _print_execution_error(bool p_condition, String p_message);
+
+public:
+ virtual void _execute(real_t p_delta);
+ virtual void _setup_modification(SkeletonModificationStack3D *p_stack);
+
+ real_t clamp_angle(real_t p_angle, real_t p_min_bound, real_t p_max_bound, bool p_invert);
+
+ void set_enabled(bool p_enabled);
+ bool get_enabled();
+
+ void set_execution_mode(int p_mode);
+ int get_execution_mode() const;
+
+ Ref<SkeletonModificationStack3D> get_modification_stack();
+
+ void set_is_setup(bool p_setup);
+ bool get_is_setup() const;
+
+ SkeletonModification3D();
+};
+
+#endif // SKELETONMODIFICATION3D_H
diff --git a/scene/resources/skeleton_modification_3d_ccdik.cpp b/scene/resources/skeleton_modification_3d_ccdik.cpp
new file mode 100644
index 0000000000..6409022563
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_ccdik.cpp
@@ -0,0 +1,474 @@
+/*************************************************************************/
+/* skeleton_modification_3d_ccdik.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 "scene/resources/skeleton_modification_3d_ccdik.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+bool SkeletonModification3DCCDIK::_set(const StringName &p_path, const Variant &p_value) {
+ String path = p_path;
+
+ if (path.begins_with("joint_data/")) {
+ int ccdik_data_size = ccdik_data_chain.size();
+ int which = path.get_slicec('/', 1).to_int();
+ String what = path.get_slicec('/', 2);
+ ERR_FAIL_INDEX_V(which, ccdik_data_size, false);
+
+ if (what == "bone_name") {
+ set_ccdik_joint_bone_name(which, p_value);
+ } else if (what == "bone_index") {
+ set_ccdik_joint_bone_index(which, p_value);
+ } else if (what == "ccdik_axis") {
+ set_ccdik_joint_ccdik_axis(which, p_value);
+ } else if (what == "enable_joint_constraint") {
+ set_ccdik_joint_enable_constraint(which, p_value);
+ } else if (what == "joint_constraint_angle_min") {
+ set_ccdik_joint_constraint_angle_min(which, Math::deg2rad(real_t(p_value)));
+ } else if (what == "joint_constraint_angle_max") {
+ set_ccdik_joint_constraint_angle_max(which, Math::deg2rad(real_t(p_value)));
+ } else if (what == "joint_constraint_angles_invert") {
+ set_ccdik_joint_constraint_invert(which, p_value);
+ }
+ return true;
+ }
+ return true;
+}
+
+bool SkeletonModification3DCCDIK::_get(const StringName &p_path, Variant &r_ret) const {
+ String path = p_path;
+
+ if (path.begins_with("joint_data/")) {
+ const int ccdik_data_size = ccdik_data_chain.size();
+ int which = path.get_slicec('/', 1).to_int();
+ String what = path.get_slicec('/', 2);
+ ERR_FAIL_INDEX_V(which, ccdik_data_size, false);
+
+ if (what == "bone_name") {
+ r_ret = get_ccdik_joint_bone_name(which);
+ } else if (what == "bone_index") {
+ r_ret = get_ccdik_joint_bone_index(which);
+ } else if (what == "ccdik_axis") {
+ r_ret = get_ccdik_joint_ccdik_axis(which);
+ } else if (what == "enable_joint_constraint") {
+ r_ret = get_ccdik_joint_enable_constraint(which);
+ } else if (what == "joint_constraint_angle_min") {
+ r_ret = Math::rad2deg(get_ccdik_joint_constraint_angle_min(which));
+ } else if (what == "joint_constraint_angle_max") {
+ r_ret = Math::rad2deg(get_ccdik_joint_constraint_angle_max(which));
+ } else if (what == "joint_constraint_angles_invert") {
+ r_ret = get_ccdik_joint_constraint_invert(which);
+ }
+ return true;
+ }
+ return true;
+}
+
+void SkeletonModification3DCCDIK::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (uint32_t i = 0; i < ccdik_data_chain.size(); i++) {
+ String base_string = "joint_data/" + itos(i) + "/";
+
+ p_list->push_back(PropertyInfo(Variant::STRING_NAME, base_string + "bone_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::INT, base_string + "bone_index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+
+ p_list->push_back(PropertyInfo(Variant::INT, base_string + "ccdik_axis",
+ PROPERTY_HINT_ENUM, "X Axis, Y Axis, Z Axis", PROPERTY_USAGE_DEFAULT));
+
+ p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "enable_joint_constraint", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ if (ccdik_data_chain[i].enable_constraint) {
+ p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "joint_constraint_angle_min", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "joint_constraint_angle_max", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "joint_constraint_angles_invert", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ }
+ }
+}
+
+void SkeletonModification3DCCDIK::_execute(real_t p_delta) {
+ ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr,
+ "Modification is not setup and therefore cannot execute!");
+ if (!enabled) {
+ return;
+ }
+
+ if (target_node_cache.is_null()) {
+ _print_execution_error(true, "Target cache is out of date. Attempting to update");
+ update_target_cache();
+ return;
+ }
+ if (tip_node_cache.is_null()) {
+ _print_execution_error(true, "Tip cache is out of date. Attempting to update");
+ update_tip_cache();
+ return;
+ }
+
+ // Reset the local bone overrides for CCDIK affected nodes
+ for (uint32_t i = 0; i < ccdik_data_chain.size(); i++) {
+ stack->skeleton->set_bone_local_pose_override(ccdik_data_chain[i].bone_idx,
+ stack->skeleton->get_bone_local_pose_override(ccdik_data_chain[i].bone_idx),
+ 0.0, false);
+ }
+
+ Node3D *node_target = Object::cast_to<Node3D>(ObjectDB::get_instance(target_node_cache));
+ Node3D *node_tip = Object::cast_to<Node3D>(ObjectDB::get_instance(tip_node_cache));
+
+ if (_print_execution_error(!node_target || !node_target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) {
+ return;
+ }
+ if (_print_execution_error(!node_tip || !node_tip->is_inside_tree(), "Tip node is not in the scene tree. Cannot execute modification!")) {
+ return;
+ }
+
+ if (use_high_quality_solve) {
+ for (uint32_t i = 0; i < ccdik_data_chain.size(); i++) {
+ for (uint32_t j = i; j < ccdik_data_chain.size(); j++) {
+ _execute_ccdik_joint(j, node_target, node_tip);
+ }
+ }
+ } else {
+ for (uint32_t i = 0; i < ccdik_data_chain.size(); i++) {
+ _execute_ccdik_joint(i, node_target, node_tip);
+ }
+ }
+
+ execution_error_found = false;
+}
+
+void SkeletonModification3DCCDIK::_execute_ccdik_joint(int p_joint_idx, Node3D *p_target, Node3D *p_tip) {
+ CCDIK_Joint_Data ccdik_data = ccdik_data_chain[p_joint_idx];
+
+ if (_print_execution_error(ccdik_data.bone_idx < 0 || ccdik_data.bone_idx > stack->skeleton->get_bone_count(),
+ "CCDIK joint: bone index for joint" + itos(p_joint_idx) + " not found. Cannot execute modification!")) {
+ return;
+ }
+
+ Transform3D bone_trans = stack->skeleton->global_pose_to_local_pose(ccdik_data.bone_idx, stack->skeleton->get_bone_global_pose(ccdik_data.bone_idx));
+ Transform3D tip_trans = stack->skeleton->global_pose_to_local_pose(ccdik_data.bone_idx, stack->skeleton->world_transform_to_global_pose(p_tip->get_global_transform()));
+ Transform3D target_trans = stack->skeleton->global_pose_to_local_pose(ccdik_data.bone_idx, stack->skeleton->world_transform_to_global_pose(p_target->get_global_transform()));
+
+ if (tip_trans.origin.distance_to(target_trans.origin) <= 0.01) {
+ return;
+ }
+
+ // Inspired (and very loosely based on) by the CCDIK algorithm made by Zalo on GitHub (https://github.com/zalo/MathUtilities)
+ // Convert the 3D position to a 2D position so we can use Atan2 (via the angle function)
+ // to know how much rotation we need on the given axis to place the tip at the target.
+ Vector2 tip_pos_2d;
+ Vector2 target_pos_2d;
+ if (ccdik_data.ccdik_axis == CCDIK_Axes::AXIS_X) {
+ tip_pos_2d = Vector2(tip_trans.origin.y, tip_trans.origin.z);
+ target_pos_2d = Vector2(target_trans.origin.y, target_trans.origin.z);
+ bone_trans.basis.rotate_local(Vector3(1, 0, 0), target_pos_2d.angle() - tip_pos_2d.angle());
+ } else if (ccdik_data.ccdik_axis == CCDIK_Axes::AXIS_Y) {
+ tip_pos_2d = Vector2(tip_trans.origin.z, tip_trans.origin.x);
+ target_pos_2d = Vector2(target_trans.origin.z, target_trans.origin.x);
+ bone_trans.basis.rotate_local(Vector3(0, 1, 0), target_pos_2d.angle() - tip_pos_2d.angle());
+ } else if (ccdik_data.ccdik_axis == CCDIK_Axes::AXIS_Z) {
+ tip_pos_2d = Vector2(tip_trans.origin.x, tip_trans.origin.y);
+ target_pos_2d = Vector2(target_trans.origin.x, target_trans.origin.y);
+ bone_trans.basis.rotate_local(Vector3(0, 0, 1), target_pos_2d.angle() - tip_pos_2d.angle());
+ } else {
+ // Should never happen, but...
+ ERR_FAIL_MSG("CCDIK joint: Unknown axis vector passed for joint" + itos(p_joint_idx) + ". Cannot execute modification!");
+ }
+
+ if (ccdik_data.enable_constraint) {
+ Vector3 rotation_axis;
+ real_t rotation_angle;
+ bone_trans.basis.get_axis_angle(rotation_axis, rotation_angle);
+
+ // Note: When the axis has a negative direction, the angle is OVER 180 degrees and therefore we need to account for this
+ // when constraining.
+ if (ccdik_data.ccdik_axis == CCDIK_Axes::AXIS_X) {
+ if (rotation_axis.x < 0) {
+ rotation_angle += Math_PI;
+ rotation_axis = Vector3(1, 0, 0);
+ }
+ } else if (ccdik_data.ccdik_axis == CCDIK_Axes::AXIS_Y) {
+ if (rotation_axis.y < 0) {
+ rotation_angle += Math_PI;
+ rotation_axis = Vector3(0, 1, 0);
+ }
+ } else if (ccdik_data.ccdik_axis == CCDIK_Axes::AXIS_Z) {
+ if (rotation_axis.z < 0) {
+ rotation_angle += Math_PI;
+ rotation_axis = Vector3(0, 0, 1);
+ }
+ } else {
+ // Should never happen, but...
+ ERR_FAIL_MSG("CCDIK joint: Unknown axis vector passed for joint" + itos(p_joint_idx) + ". Cannot execute modification!");
+ }
+ rotation_angle = clamp_angle(rotation_angle, ccdik_data.constraint_angle_min, ccdik_data.constraint_angle_max, ccdik_data.constraint_angles_invert);
+
+ bone_trans.basis.set_axis_angle(rotation_axis, rotation_angle);
+ }
+
+ stack->skeleton->set_bone_local_pose_override(ccdik_data.bone_idx, bone_trans, stack->strength, true);
+ stack->skeleton->force_update_bone_children_transforms(ccdik_data.bone_idx);
+}
+
+void SkeletonModification3DCCDIK::_setup_modification(SkeletonModificationStack3D *p_stack) {
+ stack = p_stack;
+ if (stack != nullptr) {
+ is_setup = true;
+ execution_error_found = false;
+ update_target_cache();
+ update_tip_cache();
+ }
+}
+
+void SkeletonModification3DCCDIK::update_target_cache() {
+ if (!is_setup || !stack) {
+ _print_execution_error(true, "Cannot update target cache: modification is not properly setup!");
+ return;
+ }
+
+ target_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree()) {
+ if (stack->skeleton->has_node(target_node)) {
+ Node *node = stack->skeleton->get_node(target_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update target cache: node is this modification's skeleton or cannot be found!");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update target cache: node is not in scene tree!");
+ target_node_cache = node->get_instance_id();
+
+ execution_error_found = false;
+ }
+ }
+ }
+}
+
+void SkeletonModification3DCCDIK::update_tip_cache() {
+ if (!is_setup || !stack) {
+ _print_execution_error(true, "Cannot update tip cache: modification is not properly setup!");
+ return;
+ }
+
+ tip_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree()) {
+ if (stack->skeleton->has_node(tip_node)) {
+ Node *node = stack->skeleton->get_node(tip_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update tip cache: node is this modification's skeleton or cannot be found!");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update tip cache: node is not in scene tree!");
+ tip_node_cache = node->get_instance_id();
+
+ execution_error_found = false;
+ }
+ }
+ }
+}
+
+void SkeletonModification3DCCDIK::set_target_node(const NodePath &p_target_node) {
+ target_node = p_target_node;
+ update_target_cache();
+}
+
+NodePath SkeletonModification3DCCDIK::get_target_node() const {
+ return target_node;
+}
+
+void SkeletonModification3DCCDIK::set_tip_node(const NodePath &p_tip_node) {
+ tip_node = p_tip_node;
+ update_tip_cache();
+}
+
+NodePath SkeletonModification3DCCDIK::get_tip_node() const {
+ return tip_node;
+}
+
+void SkeletonModification3DCCDIK::set_use_high_quality_solve(bool p_high_quality) {
+ use_high_quality_solve = p_high_quality;
+}
+
+bool SkeletonModification3DCCDIK::get_use_high_quality_solve() const {
+ return use_high_quality_solve;
+}
+
+// CCDIK joint data functions
+String SkeletonModification3DCCDIK::get_ccdik_joint_bone_name(int p_joint_idx) const {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, String());
+ return ccdik_data_chain[p_joint_idx].bone_name;
+}
+
+void SkeletonModification3DCCDIK::set_ccdik_joint_bone_name(int p_joint_idx, String p_bone_name) {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ ccdik_data_chain[p_joint_idx].bone_name = p_bone_name;
+
+ if (stack) {
+ if (stack->skeleton) {
+ ccdik_data_chain[p_joint_idx].bone_idx = stack->skeleton->find_bone(p_bone_name);
+ }
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+int SkeletonModification3DCCDIK::get_ccdik_joint_bone_index(int p_joint_idx) const {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
+ return ccdik_data_chain[p_joint_idx].bone_idx;
+}
+
+void SkeletonModification3DCCDIK::set_ccdik_joint_bone_index(int p_joint_idx, int p_bone_idx) {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!");
+ ccdik_data_chain[p_joint_idx].bone_idx = p_bone_idx;
+
+ if (stack) {
+ if (stack->skeleton) {
+ ccdik_data_chain[p_joint_idx].bone_name = stack->skeleton->get_bone_name(p_bone_idx);
+ }
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+int SkeletonModification3DCCDIK::get_ccdik_joint_ccdik_axis(int p_joint_idx) const {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
+ return ccdik_data_chain[p_joint_idx].ccdik_axis;
+}
+
+void SkeletonModification3DCCDIK::set_ccdik_joint_ccdik_axis(int p_joint_idx, int p_axis) {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ ERR_FAIL_COND_MSG(p_axis < 0, "CCDIK axis is out of range: The axis mode is too low!");
+ ccdik_data_chain[p_joint_idx].ccdik_axis = p_axis;
+ notify_property_list_changed();
+}
+
+bool SkeletonModification3DCCDIK::get_ccdik_joint_enable_constraint(int p_joint_idx) const {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
+ return ccdik_data_chain[p_joint_idx].enable_constraint;
+}
+
+void SkeletonModification3DCCDIK::set_ccdik_joint_enable_constraint(int p_joint_idx, bool p_enable) {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ ccdik_data_chain[p_joint_idx].enable_constraint = p_enable;
+ notify_property_list_changed();
+}
+
+real_t SkeletonModification3DCCDIK::get_ccdik_joint_constraint_angle_min(int p_joint_idx) const {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
+ return ccdik_data_chain[p_joint_idx].constraint_angle_min;
+}
+
+void SkeletonModification3DCCDIK::set_ccdik_joint_constraint_angle_min(int p_joint_idx, real_t p_angle_min) {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ ccdik_data_chain[p_joint_idx].constraint_angle_min = p_angle_min;
+}
+
+real_t SkeletonModification3DCCDIK::get_ccdik_joint_constraint_angle_max(int p_joint_idx) const {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
+ return ccdik_data_chain[p_joint_idx].constraint_angle_max;
+}
+
+void SkeletonModification3DCCDIK::set_ccdik_joint_constraint_angle_max(int p_joint_idx, real_t p_angle_max) {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ ccdik_data_chain[p_joint_idx].constraint_angle_max = p_angle_max;
+}
+
+bool SkeletonModification3DCCDIK::get_ccdik_joint_constraint_invert(int p_joint_idx) const {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
+ return ccdik_data_chain[p_joint_idx].constraint_angles_invert;
+}
+
+void SkeletonModification3DCCDIK::set_ccdik_joint_constraint_invert(int p_joint_idx, bool p_invert) {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ ccdik_data_chain[p_joint_idx].constraint_angles_invert = p_invert;
+}
+
+int SkeletonModification3DCCDIK::get_ccdik_data_chain_length() {
+ return ccdik_data_chain.size();
+}
+void SkeletonModification3DCCDIK::set_ccdik_data_chain_length(int p_length) {
+ ERR_FAIL_COND(p_length < 0);
+ ccdik_data_chain.resize(p_length);
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+void SkeletonModification3DCCDIK::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification3DCCDIK::set_target_node);
+ ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification3DCCDIK::get_target_node);
+
+ ClassDB::bind_method(D_METHOD("set_tip_node", "tip_nodepath"), &SkeletonModification3DCCDIK::set_tip_node);
+ ClassDB::bind_method(D_METHOD("get_tip_node"), &SkeletonModification3DCCDIK::get_tip_node);
+
+ ClassDB::bind_method(D_METHOD("set_use_high_quality_solve", "high_quality_solve"), &SkeletonModification3DCCDIK::set_use_high_quality_solve);
+ ClassDB::bind_method(D_METHOD("get_use_high_quality_solve"), &SkeletonModification3DCCDIK::get_use_high_quality_solve);
+
+ // CCDIK joint data functions
+ ClassDB::bind_method(D_METHOD("get_ccdik_joint_bone_name", "joint_idx"), &SkeletonModification3DCCDIK::get_ccdik_joint_bone_name);
+ ClassDB::bind_method(D_METHOD("set_ccdik_joint_bone_name", "joint_idx", "bone_name"), &SkeletonModification3DCCDIK::set_ccdik_joint_bone_name);
+ ClassDB::bind_method(D_METHOD("get_ccdik_joint_bone_index", "joint_idx"), &SkeletonModification3DCCDIK::get_ccdik_joint_bone_index);
+ ClassDB::bind_method(D_METHOD("set_ccdik_joint_bone_index", "joint_idx", "bone_index"), &SkeletonModification3DCCDIK::set_ccdik_joint_bone_index);
+ ClassDB::bind_method(D_METHOD("get_ccdik_joint_ccdik_axis", "joint_idx"), &SkeletonModification3DCCDIK::get_ccdik_joint_ccdik_axis);
+ ClassDB::bind_method(D_METHOD("set_ccdik_joint_ccdik_axis", "joint_idx", "axis"), &SkeletonModification3DCCDIK::set_ccdik_joint_ccdik_axis);
+ ClassDB::bind_method(D_METHOD("get_ccdik_joint_enable_joint_constraint", "joint_idx"), &SkeletonModification3DCCDIK::get_ccdik_joint_enable_constraint);
+ ClassDB::bind_method(D_METHOD("set_ccdik_joint_enable_joint_constraint", "joint_idx", "enable"), &SkeletonModification3DCCDIK::set_ccdik_joint_enable_constraint);
+ ClassDB::bind_method(D_METHOD("get_ccdik_joint_constraint_angle_min", "joint_idx"), &SkeletonModification3DCCDIK::get_ccdik_joint_constraint_angle_min);
+ ClassDB::bind_method(D_METHOD("set_ccdik_joint_constraint_angle_min", "joint_idx", "min_angle"), &SkeletonModification3DCCDIK::set_ccdik_joint_constraint_angle_min);
+ ClassDB::bind_method(D_METHOD("get_ccdik_joint_constraint_angle_max", "joint_idx"), &SkeletonModification3DCCDIK::get_ccdik_joint_constraint_angle_max);
+ ClassDB::bind_method(D_METHOD("set_ccdik_joint_constraint_angle_max", "joint_idx", "max_angle"), &SkeletonModification3DCCDIK::set_ccdik_joint_constraint_angle_max);
+ ClassDB::bind_method(D_METHOD("get_ccdik_joint_constraint_invert", "joint_idx"), &SkeletonModification3DCCDIK::get_ccdik_joint_constraint_invert);
+ ClassDB::bind_method(D_METHOD("set_ccdik_joint_constraint_invert", "joint_idx", "invert"), &SkeletonModification3DCCDIK::set_ccdik_joint_constraint_invert);
+
+ ClassDB::bind_method(D_METHOD("set_ccdik_data_chain_length", "length"), &SkeletonModification3DCCDIK::set_ccdik_data_chain_length);
+ ClassDB::bind_method(D_METHOD("get_ccdik_data_chain_length"), &SkeletonModification3DCCDIK::get_ccdik_data_chain_length);
+
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D"), "set_target_node", "get_target_node");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "tip_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D"), "set_tip_node", "get_tip_node");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "high_quality_solve", PROPERTY_HINT_NONE, ""), "set_use_high_quality_solve", "get_use_high_quality_solve");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "ccdik_data_chain_length", PROPERTY_HINT_RANGE, "0,100,1"), "set_ccdik_data_chain_length", "get_ccdik_data_chain_length");
+}
+
+SkeletonModification3DCCDIK::SkeletonModification3DCCDIK() {
+ stack = nullptr;
+ is_setup = false;
+ enabled = true;
+}
+
+SkeletonModification3DCCDIK::~SkeletonModification3DCCDIK() {
+}
diff --git a/scene/resources/skeleton_modification_3d_ccdik.h b/scene/resources/skeleton_modification_3d_ccdik.h
new file mode 100644
index 0000000000..e7537cc5b0
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_ccdik.h
@@ -0,0 +1,114 @@
+/*************************************************************************/
+/* skeleton_modification_3d_ccdik.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 "core/templates/local_vector.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+#ifndef SKELETONMODIFICATION3DCCDIK_H
+#define SKELETONMODIFICATION3DCCDIK_H
+
+class SkeletonModification3DCCDIK : public SkeletonModification3D {
+ GDCLASS(SkeletonModification3DCCDIK, SkeletonModification3D);
+
+private:
+ enum CCDIK_Axes {
+ AXIS_X,
+ AXIS_Y,
+ AXIS_Z
+ };
+
+ struct CCDIK_Joint_Data {
+ String bone_name = "";
+ int bone_idx = -1;
+ int ccdik_axis = 0;
+
+ bool enable_constraint = false;
+ real_t constraint_angle_min = 0;
+ real_t constraint_angle_max = (2.0 * Math_PI);
+ bool constraint_angles_invert = false;
+ };
+
+ LocalVector<CCDIK_Joint_Data> ccdik_data_chain;
+ NodePath target_node;
+ ObjectID target_node_cache;
+
+ NodePath tip_node;
+ ObjectID tip_node_cache;
+
+ bool use_high_quality_solve = true;
+
+ void update_target_cache();
+ void update_tip_cache();
+
+ void _execute_ccdik_joint(int p_joint_idx, Node3D *p_target, Node3D *p_tip);
+
+protected:
+ static void _bind_methods();
+ bool _get(const StringName &p_path, Variant &r_ret) const;
+ bool _set(const StringName &p_path, const Variant &p_value);
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+public:
+ virtual void _execute(real_t p_delta) override;
+ virtual void _setup_modification(SkeletonModificationStack3D *p_stack) override;
+
+ void set_target_node(const NodePath &p_target_node);
+ NodePath get_target_node() const;
+
+ void set_tip_node(const NodePath &p_tip_node);
+ NodePath get_tip_node() const;
+
+ void set_use_high_quality_solve(bool p_solve);
+ bool get_use_high_quality_solve() const;
+
+ String get_ccdik_joint_bone_name(int p_joint_idx) const;
+ void set_ccdik_joint_bone_name(int p_joint_idx, String p_bone_name);
+ int get_ccdik_joint_bone_index(int p_joint_idx) const;
+ void set_ccdik_joint_bone_index(int p_joint_idx, int p_bone_idx);
+ int get_ccdik_joint_ccdik_axis(int p_joint_idx) const;
+ void set_ccdik_joint_ccdik_axis(int p_joint_idx, int p_axis);
+ bool get_ccdik_joint_enable_constraint(int p_joint_idx) const;
+ void set_ccdik_joint_enable_constraint(int p_joint_idx, bool p_enable);
+ real_t get_ccdik_joint_constraint_angle_min(int p_joint_idx) const;
+ void set_ccdik_joint_constraint_angle_min(int p_joint_idx, real_t p_angle_min);
+ real_t get_ccdik_joint_constraint_angle_max(int p_joint_idx) const;
+ void set_ccdik_joint_constraint_angle_max(int p_joint_idx, real_t p_angle_max);
+ bool get_ccdik_joint_constraint_invert(int p_joint_idx) const;
+ void set_ccdik_joint_constraint_invert(int p_joint_idx, bool p_invert);
+
+ int get_ccdik_data_chain_length();
+ void set_ccdik_data_chain_length(int p_new_length);
+
+ SkeletonModification3DCCDIK();
+ ~SkeletonModification3DCCDIK();
+};
+
+#endif //SKELETONMODIFICATION3DCCDIK_H
diff --git a/scene/resources/skeleton_modification_3d_fabrik.cpp b/scene/resources/skeleton_modification_3d_fabrik.cpp
new file mode 100644
index 0000000000..69f75eb7b5
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_fabrik.cpp
@@ -0,0 +1,628 @@
+/*************************************************************************/
+/* skeleton_modification_3d_fabrik.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 "scene/resources/skeleton_modification_3d_fabrik.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+bool SkeletonModification3DFABRIK::_set(const StringName &p_path, const Variant &p_value) {
+ String path = p_path;
+
+ if (path.begins_with("joint_data/")) {
+ int fabrik_data_size = fabrik_data_chain.size();
+ int which = path.get_slicec('/', 1).to_int();
+ String what = path.get_slicec('/', 2);
+ ERR_FAIL_INDEX_V(which, fabrik_data_size, false);
+
+ if (what == "bone_name") {
+ set_fabrik_joint_bone_name(which, p_value);
+ } else if (what == "bone_index") {
+ set_fabrik_joint_bone_index(which, p_value);
+ } else if (what == "length") {
+ set_fabrik_joint_length(which, p_value);
+ } else if (what == "magnet_position") {
+ set_fabrik_joint_magnet(which, p_value);
+ } else if (what == "auto_calculate_length") {
+ set_fabrik_joint_auto_calculate_length(which, p_value);
+ } else if (what == "use_tip_node") {
+ set_fabrik_joint_use_tip_node(which, p_value);
+ } else if (what == "tip_node") {
+ set_fabrik_joint_tip_node(which, p_value);
+ } else if (what == "use_target_basis") {
+ set_fabrik_joint_use_target_basis(which, p_value);
+ } else if (what == "roll") {
+ set_fabrik_joint_roll(which, Math::deg2rad(real_t(p_value)));
+ }
+ return true;
+ }
+ return true;
+}
+
+bool SkeletonModification3DFABRIK::_get(const StringName &p_path, Variant &r_ret) const {
+ String path = p_path;
+
+ if (path.begins_with("joint_data/")) {
+ const int fabrik_data_size = fabrik_data_chain.size();
+ int which = path.get_slicec('/', 1).to_int();
+ String what = path.get_slicec('/', 2);
+ ERR_FAIL_INDEX_V(which, fabrik_data_size, false);
+
+ if (what == "bone_name") {
+ r_ret = get_fabrik_joint_bone_name(which);
+ } else if (what == "bone_index") {
+ r_ret = get_fabrik_joint_bone_index(which);
+ } else if (what == "length") {
+ r_ret = get_fabrik_joint_length(which);
+ } else if (what == "magnet_position") {
+ r_ret = get_fabrik_joint_magnet(which);
+ } else if (what == "auto_calculate_length") {
+ r_ret = get_fabrik_joint_auto_calculate_length(which);
+ } else if (what == "use_tip_node") {
+ r_ret = get_fabrik_joint_use_tip_node(which);
+ } else if (what == "tip_node") {
+ r_ret = get_fabrik_joint_tip_node(which);
+ } else if (what == "use_target_basis") {
+ r_ret = get_fabrik_joint_use_target_basis(which);
+ } else if (what == "roll") {
+ r_ret = Math::rad2deg(get_fabrik_joint_roll(which));
+ }
+ return true;
+ }
+ return true;
+}
+
+void SkeletonModification3DFABRIK::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (uint32_t i = 0; i < fabrik_data_chain.size(); i++) {
+ String base_string = "joint_data/" + itos(i) + "/";
+
+ p_list->push_back(PropertyInfo(Variant::STRING_NAME, base_string + "bone_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::INT, base_string + "bone_index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "roll", PROPERTY_HINT_RANGE, "-360,360,0.01", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "auto_calculate_length", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+
+ if (!fabrik_data_chain[i].auto_calculate_length) {
+ p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "length", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ } else {
+ p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "use_tip_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ if (fabrik_data_chain[i].use_tip_node) {
+ p_list->push_back(PropertyInfo(Variant::NODE_PATH, base_string + "tip_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D", PROPERTY_USAGE_DEFAULT));
+ }
+ }
+
+ // Cannot apply magnet to the origin of the chain, as it will not do anything.
+ if (i > 0) {
+ p_list->push_back(PropertyInfo(Variant::VECTOR3, base_string + "magnet_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ }
+ // Only give the override basis option on the last bone in the chain, so only include it for the last bone.
+ if (i == fabrik_data_chain.size() - 1) {
+ p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "use_target_basis", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ }
+ }
+}
+
+void SkeletonModification3DFABRIK::_execute(real_t p_delta) {
+ ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr,
+ "Modification is not setup and therefore cannot execute!");
+ if (!enabled) {
+ return;
+ }
+
+ if (target_node_cache.is_null()) {
+ _print_execution_error(true, "Target cache is out of date. Attempting to update...");
+ update_target_cache();
+ return;
+ }
+
+ if (_print_execution_error(fabrik_data_chain.size() <= 1, "FABRIK requires at least two joints to operate. Cannot execute modification!")) {
+ return;
+ }
+
+ Node3D *node_target = Object::cast_to<Node3D>(ObjectDB::get_instance(target_node_cache));
+ if (_print_execution_error(!node_target || !node_target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) {
+ return;
+ }
+
+ // Verify that all joints have a valid bone ID, and that all bone lengths are zero or more
+ // Also, while we are here, apply magnet positions.
+ for (uint32_t i = 0; i < fabrik_data_chain.size(); i++) {
+ if (_print_execution_error(fabrik_data_chain[i].bone_idx < 0, "FABRIK Joint " + itos(i) + " has an invalid bone ID. Cannot execute!")) {
+ return;
+ }
+
+ if (fabrik_data_chain[i].length < 0 && fabrik_data_chain[i].auto_calculate_length) {
+ fabrik_joint_auto_calculate_length(i);
+ }
+ if (_print_execution_error(fabrik_data_chain[i].length < 0, "FABRIK Joint " + itos(i) + " has an invalid joint length. Cannot execute!")) {
+ return;
+ }
+
+ Transform3D local_pose_override = stack->skeleton->get_bone_local_pose_override(fabrik_data_chain[i].bone_idx);
+
+ // Apply magnet positions:
+ if (stack->skeleton->get_bone_parent(fabrik_data_chain[i].bone_idx) >= 0) {
+ int parent_bone_idx = stack->skeleton->get_bone_parent(fabrik_data_chain[i].bone_idx);
+ Transform3D conversion_transform = (stack->skeleton->get_bone_global_pose(parent_bone_idx) * stack->skeleton->get_bone_rest(parent_bone_idx));
+ local_pose_override.origin += conversion_transform.basis.xform_inv(fabrik_data_chain[i].magnet_position);
+ } else {
+ local_pose_override.origin += fabrik_data_chain[i].magnet_position;
+ }
+
+ stack->skeleton->set_bone_local_pose_override(fabrik_data_chain[i].bone_idx, local_pose_override, stack->strength, true);
+ }
+
+ target_global_pose = stack->skeleton->world_transform_to_global_pose(node_target->get_global_transform());
+ origin_global_pose = stack->skeleton->local_pose_to_global_pose(
+ fabrik_data_chain[0].bone_idx, stack->skeleton->get_bone_local_pose_override(fabrik_data_chain[0].bone_idx));
+
+ final_joint_idx = fabrik_data_chain.size() - 1;
+ real_t target_distance = stack->skeleton->global_pose_to_local_pose(fabrik_data_chain[final_joint_idx].bone_idx, target_global_pose).origin.length();
+ chain_iterations = 0;
+
+ while (target_distance > chain_tolerance) {
+ chain_backwards();
+ chain_forwards();
+
+ // update the target distance
+ target_distance = stack->skeleton->global_pose_to_local_pose(fabrik_data_chain[final_joint_idx].bone_idx, target_global_pose).origin.length();
+
+ // update chain iterations
+ chain_iterations += 1;
+ if (chain_iterations >= chain_max_iterations) {
+ break;
+ }
+ }
+ chain_apply();
+
+ execution_error_found = false;
+}
+
+void SkeletonModification3DFABRIK::chain_backwards() {
+ int final_bone_idx = fabrik_data_chain[final_joint_idx].bone_idx;
+ Transform3D final_joint_trans = stack->skeleton->local_pose_to_global_pose(final_bone_idx, stack->skeleton->get_bone_local_pose_override(final_bone_idx));
+
+ // Get the direction the final bone is facing in.
+ stack->skeleton->update_bone_rest_forward_vector(final_bone_idx);
+ Transform3D final_bone_direction_trans = final_joint_trans.looking_at(target_global_pose.origin, Vector3(0, 1, 0));
+ final_bone_direction_trans.basis = stack->skeleton->global_pose_z_forward_to_bone_forward(final_bone_idx, final_bone_direction_trans.basis);
+ Vector3 direction = final_bone_direction_trans.basis.xform(stack->skeleton->get_bone_axis_forward_vector(final_bone_idx)).normalized();
+
+ // If set to override, then use the target's Basis rather than the bone's
+ if (fabrik_data_chain[final_joint_idx].use_target_basis) {
+ direction = target_global_pose.basis.xform(stack->skeleton->get_bone_axis_forward_vector(final_bone_idx)).normalized();
+ }
+
+ // set the position of the final joint to the target position
+ final_joint_trans.origin = target_global_pose.origin - (direction * fabrik_data_chain[final_joint_idx].length);
+ final_joint_trans = stack->skeleton->global_pose_to_local_pose(final_bone_idx, final_joint_trans);
+ stack->skeleton->set_bone_local_pose_override(final_bone_idx, final_joint_trans, stack->strength, true);
+
+ // for all other joints, move them towards the target
+ int i = final_joint_idx;
+ while (i >= 1) {
+ int next_bone_idx = fabrik_data_chain[i].bone_idx;
+ Transform3D next_bone_trans = stack->skeleton->local_pose_to_global_pose(next_bone_idx, stack->skeleton->get_bone_local_pose_override(next_bone_idx));
+ i -= 1;
+ int current_bone_idx = fabrik_data_chain[i].bone_idx;
+ Transform3D current_trans = stack->skeleton->local_pose_to_global_pose(current_bone_idx, stack->skeleton->get_bone_local_pose_override(current_bone_idx));
+
+ real_t length = fabrik_data_chain[i].length / (next_bone_trans.origin - current_trans.origin).length();
+ current_trans.origin = next_bone_trans.origin.lerp(current_trans.origin, length);
+
+ // Apply it back to the skeleton
+ stack->skeleton->set_bone_local_pose_override(current_bone_idx, stack->skeleton->global_pose_to_local_pose(current_bone_idx, current_trans), stack->strength, true);
+ }
+}
+
+void SkeletonModification3DFABRIK::chain_forwards() {
+ // Set root at the initial position.
+ int origin_bone_idx = fabrik_data_chain[0].bone_idx;
+ Transform3D root_transform = stack->skeleton->local_pose_to_global_pose(origin_bone_idx, stack->skeleton->get_bone_local_pose_override(origin_bone_idx));
+ root_transform.origin = origin_global_pose.origin;
+ stack->skeleton->set_bone_local_pose_override(origin_bone_idx, stack->skeleton->global_pose_to_local_pose(origin_bone_idx, root_transform), stack->strength, true);
+
+ for (uint32_t i = 0; i < fabrik_data_chain.size() - 1; i++) {
+ int current_bone_idx = fabrik_data_chain[i].bone_idx;
+ Transform3D current_trans = stack->skeleton->local_pose_to_global_pose(current_bone_idx, stack->skeleton->get_bone_local_pose_override(current_bone_idx));
+ int next_bone_idx = fabrik_data_chain[i + 1].bone_idx;
+ Transform3D next_bone_trans = stack->skeleton->local_pose_to_global_pose(next_bone_idx, stack->skeleton->get_bone_local_pose_override(next_bone_idx));
+
+ real_t length = fabrik_data_chain[i].length / (current_trans.origin - next_bone_trans.origin).length();
+ next_bone_trans.origin = current_trans.origin.lerp(next_bone_trans.origin, length);
+
+ // Apply it back to the skeleton
+ stack->skeleton->set_bone_local_pose_override(next_bone_idx, stack->skeleton->global_pose_to_local_pose(next_bone_idx, next_bone_trans), stack->strength, true);
+ }
+}
+
+void SkeletonModification3DFABRIK::chain_apply() {
+ for (uint32_t i = 0; i < fabrik_data_chain.size(); i++) {
+ int current_bone_idx = fabrik_data_chain[i].bone_idx;
+ Transform3D current_trans = stack->skeleton->get_bone_local_pose_override(current_bone_idx);
+ current_trans = stack->skeleton->local_pose_to_global_pose(current_bone_idx, current_trans);
+
+ // If this is the last bone in the chain...
+ if (i == fabrik_data_chain.size() - 1) {
+ if (fabrik_data_chain[i].use_target_basis == false) { // Point to target...
+ // Get the forward direction that the basis is facing in right now.
+ stack->skeleton->update_bone_rest_forward_vector(current_bone_idx);
+ Vector3 forward_vector = stack->skeleton->get_bone_axis_forward_vector(current_bone_idx);
+ // Rotate the bone towards the target:
+ current_trans.basis.rotate_to_align(forward_vector, current_trans.origin.direction_to(target_global_pose.origin));
+ current_trans.basis.rotate_local(forward_vector, fabrik_data_chain[i].roll);
+ } else { // Use the target's Basis...
+ current_trans.basis = target_global_pose.basis.orthonormalized().scaled(current_trans.basis.get_scale());
+ }
+ } else { // every other bone in the chain...
+ int next_bone_idx = fabrik_data_chain[i + 1].bone_idx;
+ Transform3D next_trans = stack->skeleton->local_pose_to_global_pose(next_bone_idx, stack->skeleton->get_bone_local_pose_override(next_bone_idx));
+
+ // Get the forward direction that the basis is facing in right now.
+ stack->skeleton->update_bone_rest_forward_vector(current_bone_idx);
+ Vector3 forward_vector = stack->skeleton->get_bone_axis_forward_vector(current_bone_idx);
+ // Rotate the bone towards the next bone in the chain:
+ current_trans.basis.rotate_to_align(forward_vector, current_trans.origin.direction_to(next_trans.origin));
+ current_trans.basis.rotate_local(forward_vector, fabrik_data_chain[i].roll);
+ }
+ current_trans = stack->skeleton->global_pose_to_local_pose(current_bone_idx, current_trans);
+ current_trans.origin = Vector3(0, 0, 0);
+ stack->skeleton->set_bone_local_pose_override(current_bone_idx, current_trans, stack->strength, true);
+ }
+
+ // Update all the bones so the next modification has up-to-date data.
+ stack->skeleton->force_update_all_bone_transforms();
+}
+
+void SkeletonModification3DFABRIK::_setup_modification(SkeletonModificationStack3D *p_stack) {
+ stack = p_stack;
+ if (stack != nullptr) {
+ is_setup = true;
+ execution_error_found = false;
+ update_target_cache();
+
+ for (uint32_t i = 0; i < fabrik_data_chain.size(); i++) {
+ update_joint_tip_cache(i);
+ }
+ }
+}
+
+void SkeletonModification3DFABRIK::update_target_cache() {
+ if (!is_setup || !stack) {
+ _print_execution_error(true, "Cannot update target cache: modification is not properly setup!");
+ return;
+ }
+ target_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree() && target_node.is_empty() == false) {
+ if (stack->skeleton->has_node(target_node)) {
+ Node *node = stack->skeleton->get_node(target_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update target cache: node is this modification's skeleton or cannot be found!");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update target cache: node is not in the scene tree!");
+ target_node_cache = node->get_instance_id();
+
+ execution_error_found = false;
+ }
+ }
+ }
+}
+
+void SkeletonModification3DFABRIK::update_joint_tip_cache(int p_joint_idx) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX_MSG(p_joint_idx, bone_chain_size, "FABRIK joint not found");
+ if (!is_setup || !stack) {
+ _print_execution_error(true, "Cannot update tip cache: modification is not properly setup!");
+ return;
+ }
+ fabrik_data_chain[p_joint_idx].tip_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree() && fabrik_data_chain[p_joint_idx].tip_node.is_empty() == false) {
+ if (stack->skeleton->has_node(fabrik_data_chain[p_joint_idx].tip_node)) {
+ Node *node = stack->skeleton->get_node(fabrik_data_chain[p_joint_idx].tip_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update tip cache for joint " + itos(p_joint_idx) + ": node is this modification's skeleton or cannot be found!");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update tip cache for joint " + itos(p_joint_idx) + ": node is not in scene tree!");
+ fabrik_data_chain[p_joint_idx].tip_node_cache = node->get_instance_id();
+
+ execution_error_found = false;
+ }
+ }
+ }
+}
+
+void SkeletonModification3DFABRIK::set_target_node(const NodePath &p_target_node) {
+ target_node = p_target_node;
+ update_target_cache();
+}
+
+NodePath SkeletonModification3DFABRIK::get_target_node() const {
+ return target_node;
+}
+
+int SkeletonModification3DFABRIK::get_fabrik_data_chain_length() {
+ return fabrik_data_chain.size();
+}
+
+void SkeletonModification3DFABRIK::set_fabrik_data_chain_length(int p_length) {
+ ERR_FAIL_COND(p_length < 0);
+ fabrik_data_chain.resize(p_length);
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+real_t SkeletonModification3DFABRIK::get_chain_tolerance() {
+ return chain_tolerance;
+}
+
+void SkeletonModification3DFABRIK::set_chain_tolerance(real_t p_tolerance) {
+ ERR_FAIL_COND_MSG(p_tolerance <= 0, "FABRIK chain tolerance must be more than zero!");
+ chain_tolerance = p_tolerance;
+}
+
+int SkeletonModification3DFABRIK::get_chain_max_iterations() {
+ return chain_max_iterations;
+}
+void SkeletonModification3DFABRIK::set_chain_max_iterations(int p_iterations) {
+ ERR_FAIL_COND_MSG(p_iterations <= 0, "FABRIK chain iterations must be at least one. Set enabled to false to disable the FABRIK chain.");
+ chain_max_iterations = p_iterations;
+}
+
+// FABRIK joint data functions
+String SkeletonModification3DFABRIK::get_fabrik_joint_bone_name(int p_joint_idx) const {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, String());
+ return fabrik_data_chain[p_joint_idx].bone_name;
+}
+
+void SkeletonModification3DFABRIK::set_fabrik_joint_bone_name(int p_joint_idx, String p_bone_name) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ fabrik_data_chain[p_joint_idx].bone_name = p_bone_name;
+
+ if (stack) {
+ if (stack->skeleton) {
+ fabrik_data_chain[p_joint_idx].bone_idx = stack->skeleton->find_bone(p_bone_name);
+ }
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+int SkeletonModification3DFABRIK::get_fabrik_joint_bone_index(int p_joint_idx) const {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
+ return fabrik_data_chain[p_joint_idx].bone_idx;
+}
+
+void SkeletonModification3DFABRIK::set_fabrik_joint_bone_index(int p_joint_idx, int p_bone_idx) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!");
+ fabrik_data_chain[p_joint_idx].bone_idx = p_bone_idx;
+
+ if (stack) {
+ if (stack->skeleton) {
+ fabrik_data_chain[p_joint_idx].bone_name = stack->skeleton->get_bone_name(p_bone_idx);
+ }
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+real_t SkeletonModification3DFABRIK::get_fabrik_joint_length(int p_joint_idx) const {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
+ return fabrik_data_chain[p_joint_idx].length;
+}
+
+void SkeletonModification3DFABRIK::set_fabrik_joint_length(int p_joint_idx, real_t p_bone_length) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ ERR_FAIL_COND_MSG(p_bone_length < 0, "FABRIK joint length cannot be less than zero!");
+
+ if (!is_setup) {
+ fabrik_data_chain[p_joint_idx].length = p_bone_length;
+ return;
+ }
+
+ if (fabrik_data_chain[p_joint_idx].auto_calculate_length) {
+ WARN_PRINT("FABRIK Length not set: auto calculate length is enabled for this joint!");
+ fabrik_joint_auto_calculate_length(p_joint_idx);
+ } else {
+ fabrik_data_chain[p_joint_idx].length = p_bone_length;
+ }
+
+ execution_error_found = false;
+}
+
+Vector3 SkeletonModification3DFABRIK::get_fabrik_joint_magnet(int p_joint_idx) const {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, Vector3());
+ return fabrik_data_chain[p_joint_idx].magnet_position;
+}
+
+void SkeletonModification3DFABRIK::set_fabrik_joint_magnet(int p_joint_idx, Vector3 p_magnet) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ fabrik_data_chain[p_joint_idx].magnet_position = p_magnet;
+}
+
+bool SkeletonModification3DFABRIK::get_fabrik_joint_auto_calculate_length(int p_joint_idx) const {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
+ return fabrik_data_chain[p_joint_idx].auto_calculate_length;
+}
+
+void SkeletonModification3DFABRIK::set_fabrik_joint_auto_calculate_length(int p_joint_idx, bool p_auto_calculate) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ fabrik_data_chain[p_joint_idx].auto_calculate_length = p_auto_calculate;
+ fabrik_joint_auto_calculate_length(p_joint_idx);
+ notify_property_list_changed();
+}
+
+void SkeletonModification3DFABRIK::fabrik_joint_auto_calculate_length(int p_joint_idx) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ if (!fabrik_data_chain[p_joint_idx].auto_calculate_length) {
+ return;
+ }
+
+ if (!stack || !stack->skeleton || !is_setup) {
+ _print_execution_error(true, "Cannot auto calculate joint length: modification is not properly setup!");
+ return;
+ }
+ ERR_FAIL_INDEX_MSG(fabrik_data_chain[p_joint_idx].bone_idx, stack->skeleton->get_bone_count(),
+ "Bone for joint " + itos(p_joint_idx) + " is not set or points to an unknown bone!");
+
+ if (fabrik_data_chain[p_joint_idx].use_tip_node) { // Use the tip node to update joint length.
+
+ update_joint_tip_cache(p_joint_idx);
+
+ Node3D *tip_node = Object::cast_to<Node3D>(ObjectDB::get_instance(fabrik_data_chain[p_joint_idx].tip_node_cache));
+ ERR_FAIL_COND_MSG(!tip_node, "Tip node for joint " + itos(p_joint_idx) + "is not a Node3D-based node. Cannot calculate length...");
+ ERR_FAIL_COND_MSG(!tip_node->is_inside_tree(), "Tip node for joint " + itos(p_joint_idx) + "is not in the scene tree. Cannot calculate length...");
+
+ Transform3D node_trans = tip_node->get_global_transform();
+ node_trans = stack->skeleton->world_transform_to_global_pose(node_trans);
+ node_trans = stack->skeleton->global_pose_to_local_pose(fabrik_data_chain[p_joint_idx].bone_idx, node_trans);
+ fabrik_data_chain[p_joint_idx].length = node_trans.origin.length();
+ } else { // Use child bone(s) to update joint length, if possible
+ Vector<int> bone_children = stack->skeleton->get_bone_children(fabrik_data_chain[p_joint_idx].bone_idx);
+ if (bone_children.size() <= 0) {
+ ERR_FAIL_MSG("Cannot calculate length for joint " + itos(p_joint_idx) + "joint uses leaf bone. \nPlease manually set the bone length or use a tip node!");
+ return;
+ }
+
+ real_t final_length = 0;
+ for (int i = 0; i < bone_children.size(); i++) {
+ Transform3D child_transform = stack->skeleton->get_bone_global_pose(bone_children[i]);
+ final_length += stack->skeleton->global_pose_to_local_pose(fabrik_data_chain[p_joint_idx].bone_idx, child_transform).origin.length();
+ }
+ fabrik_data_chain[p_joint_idx].length = final_length / bone_children.size();
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+bool SkeletonModification3DFABRIK::get_fabrik_joint_use_tip_node(int p_joint_idx) const {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
+ return fabrik_data_chain[p_joint_idx].use_tip_node;
+}
+
+void SkeletonModification3DFABRIK::set_fabrik_joint_use_tip_node(int p_joint_idx, bool p_use_tip_node) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ fabrik_data_chain[p_joint_idx].use_tip_node = p_use_tip_node;
+ notify_property_list_changed();
+}
+
+NodePath SkeletonModification3DFABRIK::get_fabrik_joint_tip_node(int p_joint_idx) const {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, NodePath());
+ return fabrik_data_chain[p_joint_idx].tip_node;
+}
+
+void SkeletonModification3DFABRIK::set_fabrik_joint_tip_node(int p_joint_idx, NodePath p_tip_node) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ fabrik_data_chain[p_joint_idx].tip_node = p_tip_node;
+ update_joint_tip_cache(p_joint_idx);
+}
+
+bool SkeletonModification3DFABRIK::get_fabrik_joint_use_target_basis(int p_joint_idx) const {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
+ return fabrik_data_chain[p_joint_idx].use_target_basis;
+}
+
+void SkeletonModification3DFABRIK::set_fabrik_joint_use_target_basis(int p_joint_idx, bool p_use_target_basis) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ fabrik_data_chain[p_joint_idx].use_target_basis = p_use_target_basis;
+}
+
+real_t SkeletonModification3DFABRIK::get_fabrik_joint_roll(int p_joint_idx) const {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, 0.0);
+ return fabrik_data_chain[p_joint_idx].roll;
+}
+
+void SkeletonModification3DFABRIK::set_fabrik_joint_roll(int p_joint_idx, real_t p_roll) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ fabrik_data_chain[p_joint_idx].roll = p_roll;
+}
+
+void SkeletonModification3DFABRIK::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification3DFABRIK::set_target_node);
+ ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification3DFABRIK::get_target_node);
+ ClassDB::bind_method(D_METHOD("set_fabrik_data_chain_length", "length"), &SkeletonModification3DFABRIK::set_fabrik_data_chain_length);
+ ClassDB::bind_method(D_METHOD("get_fabrik_data_chain_length"), &SkeletonModification3DFABRIK::get_fabrik_data_chain_length);
+ ClassDB::bind_method(D_METHOD("set_chain_tolerance", "tolerance"), &SkeletonModification3DFABRIK::set_chain_tolerance);
+ ClassDB::bind_method(D_METHOD("get_chain_tolerance"), &SkeletonModification3DFABRIK::get_chain_tolerance);
+ ClassDB::bind_method(D_METHOD("set_chain_max_iterations", "max_iterations"), &SkeletonModification3DFABRIK::set_chain_max_iterations);
+ ClassDB::bind_method(D_METHOD("get_chain_max_iterations"), &SkeletonModification3DFABRIK::get_chain_max_iterations);
+
+ // FABRIK joint data functions
+ ClassDB::bind_method(D_METHOD("get_fabrik_joint_bone_name", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_bone_name);
+ ClassDB::bind_method(D_METHOD("set_fabrik_joint_bone_name", "joint_idx", "bone_name"), &SkeletonModification3DFABRIK::set_fabrik_joint_bone_name);
+ ClassDB::bind_method(D_METHOD("get_fabrik_joint_bone_index", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_bone_index);
+ ClassDB::bind_method(D_METHOD("set_fabrik_joint_bone_index", "joint_idx", "bone_index"), &SkeletonModification3DFABRIK::set_fabrik_joint_bone_index);
+ ClassDB::bind_method(D_METHOD("get_fabrik_joint_length", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_length);
+ ClassDB::bind_method(D_METHOD("set_fabrik_joint_length", "joint_idx", "length"), &SkeletonModification3DFABRIK::set_fabrik_joint_length);
+ ClassDB::bind_method(D_METHOD("get_fabrik_joint_magnet", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_magnet);
+ ClassDB::bind_method(D_METHOD("set_fabrik_joint_magnet", "joint_idx", "magnet_position"), &SkeletonModification3DFABRIK::set_fabrik_joint_magnet);
+ ClassDB::bind_method(D_METHOD("get_fabrik_joint_auto_calculate_length", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_auto_calculate_length);
+ ClassDB::bind_method(D_METHOD("set_fabrik_joint_auto_calculate_length", "joint_idx", "auto_calculate_length"), &SkeletonModification3DFABRIK::set_fabrik_joint_auto_calculate_length);
+ ClassDB::bind_method(D_METHOD("fabrik_joint_auto_calculate_length", "joint_idx"), &SkeletonModification3DFABRIK::fabrik_joint_auto_calculate_length);
+ ClassDB::bind_method(D_METHOD("get_fabrik_joint_use_tip_node", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_use_tip_node);
+ ClassDB::bind_method(D_METHOD("set_fabrik_joint_use_tip_node", "joint_idx", "use_tip_node"), &SkeletonModification3DFABRIK::set_fabrik_joint_use_tip_node);
+ ClassDB::bind_method(D_METHOD("get_fabrik_joint_tip_node", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_tip_node);
+ ClassDB::bind_method(D_METHOD("set_fabrik_joint_tip_node", "joint_idx", "tip_node"), &SkeletonModification3DFABRIK::set_fabrik_joint_tip_node);
+ ClassDB::bind_method(D_METHOD("get_fabrik_joint_use_target_basis", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_use_target_basis);
+ ClassDB::bind_method(D_METHOD("set_fabrik_joint_use_target_basis", "joint_idx", "use_target_basis"), &SkeletonModification3DFABRIK::set_fabrik_joint_use_target_basis);
+
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D"), "set_target_node", "get_target_node");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "fabrik_data_chain_length", PROPERTY_HINT_RANGE, "0,100,1"), "set_fabrik_data_chain_length", "get_fabrik_data_chain_length");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "chain_tolerance", PROPERTY_HINT_RANGE, "0,100,0.001"), "set_chain_tolerance", "get_chain_tolerance");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "chain_max_iterations", PROPERTY_HINT_RANGE, "1,50,1"), "set_chain_max_iterations", "get_chain_max_iterations");
+}
+
+SkeletonModification3DFABRIK::SkeletonModification3DFABRIK() {
+ stack = nullptr;
+ is_setup = false;
+ enabled = true;
+}
+
+SkeletonModification3DFABRIK::~SkeletonModification3DFABRIK() {
+}
diff --git a/scene/resources/skeleton_modification_3d_fabrik.h b/scene/resources/skeleton_modification_3d_fabrik.h
new file mode 100644
index 0000000000..9b5da883d4
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_fabrik.h
@@ -0,0 +1,122 @@
+/*************************************************************************/
+/* skeleton_modification_3d_fabrik.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 "core/templates/local_vector.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+#ifndef SKELETONMODIFICATION3DFABRIK_H
+#define SKELETONMODIFICATION3DFABRIK_H
+
+class SkeletonModification3DFABRIK : public SkeletonModification3D {
+ GDCLASS(SkeletonModification3DFABRIK, SkeletonModification3D);
+
+private:
+ struct FABRIK_Joint_Data {
+ String bone_name = "";
+ int bone_idx = -1;
+ real_t length = -1;
+ Vector3 magnet_position = Vector3(0, 0, 0);
+
+ bool auto_calculate_length = true;
+ bool use_tip_node = false;
+ NodePath tip_node = NodePath();
+ ObjectID tip_node_cache;
+
+ bool use_target_basis = false;
+ real_t roll = 0;
+ };
+
+ LocalVector<FABRIK_Joint_Data> fabrik_data_chain;
+ NodePath target_node;
+ ObjectID target_node_cache;
+
+ real_t chain_tolerance = 0.01;
+ int chain_max_iterations = 10;
+ int chain_iterations = 0;
+
+ void update_target_cache();
+ void update_joint_tip_cache(int p_joint_idx);
+
+ int final_joint_idx = 0;
+ Transform3D target_global_pose = Transform3D();
+ Transform3D origin_global_pose = Transform3D();
+
+ void chain_backwards();
+ void chain_forwards();
+ void chain_apply();
+
+protected:
+ static void _bind_methods();
+ bool _get(const StringName &p_path, Variant &r_ret) const;
+ bool _set(const StringName &p_path, const Variant &p_value);
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+public:
+ virtual void _execute(real_t p_delta) override;
+ virtual void _setup_modification(SkeletonModificationStack3D *p_stack) override;
+
+ void set_target_node(const NodePath &p_target_node);
+ NodePath get_target_node() const;
+
+ int get_fabrik_data_chain_length();
+ void set_fabrik_data_chain_length(int p_new_length);
+
+ real_t get_chain_tolerance();
+ void set_chain_tolerance(real_t p_tolerance);
+
+ int get_chain_max_iterations();
+ void set_chain_max_iterations(int p_iterations);
+
+ String get_fabrik_joint_bone_name(int p_joint_idx) const;
+ void set_fabrik_joint_bone_name(int p_joint_idx, String p_bone_name);
+ int get_fabrik_joint_bone_index(int p_joint_idx) const;
+ void set_fabrik_joint_bone_index(int p_joint_idx, int p_bone_idx);
+ real_t get_fabrik_joint_length(int p_joint_idx) const;
+ void set_fabrik_joint_length(int p_joint_idx, real_t p_bone_length);
+ Vector3 get_fabrik_joint_magnet(int p_joint_idx) const;
+ void set_fabrik_joint_magnet(int p_joint_idx, Vector3 p_magnet);
+ bool get_fabrik_joint_auto_calculate_length(int p_joint_idx) const;
+ void set_fabrik_joint_auto_calculate_length(int p_joint_idx, bool p_auto_calculate);
+ void fabrik_joint_auto_calculate_length(int p_joint_idx);
+ bool get_fabrik_joint_use_tip_node(int p_joint_idx) const;
+ void set_fabrik_joint_use_tip_node(int p_joint_idx, bool p_use_tip_node);
+ NodePath get_fabrik_joint_tip_node(int p_joint_idx) const;
+ void set_fabrik_joint_tip_node(int p_joint_idx, NodePath p_tip_node);
+ bool get_fabrik_joint_use_target_basis(int p_joint_idx) const;
+ void set_fabrik_joint_use_target_basis(int p_joint_idx, bool p_use_basis);
+ real_t get_fabrik_joint_roll(int p_joint_idx) const;
+ void set_fabrik_joint_roll(int p_joint_idx, real_t p_roll);
+
+ SkeletonModification3DFABRIK();
+ ~SkeletonModification3DFABRIK();
+};
+
+#endif //SKELETONMODIFICATION3DFABRIK_H
diff --git a/scene/resources/skeleton_modification_3d_jiggle.cpp b/scene/resources/skeleton_modification_3d_jiggle.cpp
new file mode 100644
index 0000000000..1fb7dad2ad
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_jiggle.cpp
@@ -0,0 +1,573 @@
+/*************************************************************************/
+/* skeleton_modification_3d_jiggle.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 "scene/resources/skeleton_modification_3d_jiggle.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+bool SkeletonModification3DJiggle::_set(const StringName &p_path, const Variant &p_value) {
+ String path = p_path;
+
+ if (path.begins_with("joint_data/")) {
+ const int jiggle_size = jiggle_data_chain.size();
+ int which = path.get_slicec('/', 1).to_int();
+ String what = path.get_slicec('/', 2);
+ ERR_FAIL_INDEX_V(which, jiggle_size, false);
+
+ if (what == "bone_name") {
+ set_jiggle_joint_bone_name(which, p_value);
+ } else if (what == "bone_index") {
+ set_jiggle_joint_bone_index(which, p_value);
+ } else if (what == "override_defaults") {
+ set_jiggle_joint_override(which, p_value);
+ } else if (what == "stiffness") {
+ set_jiggle_joint_stiffness(which, p_value);
+ } else if (what == "mass") {
+ set_jiggle_joint_mass(which, p_value);
+ } else if (what == "damping") {
+ set_jiggle_joint_damping(which, p_value);
+ } else if (what == "use_gravity") {
+ set_jiggle_joint_use_gravity(which, p_value);
+ } else if (what == "gravity") {
+ set_jiggle_joint_gravity(which, p_value);
+ } else if (what == "roll") {
+ set_jiggle_joint_roll(which, Math::deg2rad(real_t(p_value)));
+ }
+ return true;
+ } else {
+ if (path == "use_colliders") {
+ set_use_colliders(p_value);
+ } else if (path == "collision_mask") {
+ set_collision_mask(p_value);
+ }
+ return true;
+ }
+ return true;
+}
+
+bool SkeletonModification3DJiggle::_get(const StringName &p_path, Variant &r_ret) const {
+ String path = p_path;
+
+ if (path.begins_with("joint_data/")) {
+ const int jiggle_size = jiggle_data_chain.size();
+ int which = path.get_slicec('/', 1).to_int();
+ String what = path.get_slicec('/', 2);
+ ERR_FAIL_INDEX_V(which, jiggle_size, false);
+
+ if (what == "bone_name") {
+ r_ret = get_jiggle_joint_bone_name(which);
+ } else if (what == "bone_index") {
+ r_ret = get_jiggle_joint_bone_index(which);
+ } else if (what == "override_defaults") {
+ r_ret = get_jiggle_joint_override(which);
+ } else if (what == "stiffness") {
+ r_ret = get_jiggle_joint_stiffness(which);
+ } else if (what == "mass") {
+ r_ret = get_jiggle_joint_mass(which);
+ } else if (what == "damping") {
+ r_ret = get_jiggle_joint_damping(which);
+ } else if (what == "use_gravity") {
+ r_ret = get_jiggle_joint_use_gravity(which);
+ } else if (what == "gravity") {
+ r_ret = get_jiggle_joint_gravity(which);
+ } else if (what == "roll") {
+ r_ret = Math::rad2deg(get_jiggle_joint_roll(which));
+ }
+ return true;
+ } else {
+ if (path == "use_colliders") {
+ r_ret = get_use_colliders();
+ } else if (path == "collision_mask") {
+ r_ret = get_collision_mask();
+ }
+ return true;
+ }
+ return true;
+}
+
+void SkeletonModification3DJiggle::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::BOOL, "use_colliders", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ if (use_colliders) {
+ p_list->push_back(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS, "", PROPERTY_USAGE_DEFAULT));
+ }
+
+ for (uint32_t i = 0; i < jiggle_data_chain.size(); i++) {
+ String base_string = "joint_data/" + itos(i) + "/";
+
+ p_list->push_back(PropertyInfo(Variant::STRING_NAME, base_string + "bone_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::INT, base_string + "bone_index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "roll", PROPERTY_HINT_RANGE, "-360,360,0.01", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "override_defaults", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+
+ if (jiggle_data_chain[i].override_defaults) {
+ p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "stiffness", PROPERTY_HINT_RANGE, "0, 1000, 0.01", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "mass", PROPERTY_HINT_RANGE, "0, 1000, 0.01", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "damping", PROPERTY_HINT_RANGE, "0, 1, 0.01", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "use_gravity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ if (jiggle_data_chain[i].use_gravity) {
+ p_list->push_back(PropertyInfo(Variant::VECTOR3, base_string + "gravity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ }
+ }
+ }
+}
+
+void SkeletonModification3DJiggle::_execute(real_t p_delta) {
+ ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr,
+ "Modification is not setup and therefore cannot execute!");
+ if (!enabled) {
+ return;
+ }
+ if (target_node_cache.is_null()) {
+ _print_execution_error(true, "Target cache is out of date. Attempting to update...");
+ update_cache();
+ return;
+ }
+ Node3D *target = Object::cast_to<Node3D>(ObjectDB::get_instance(target_node_cache));
+ _print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!");
+
+ for (uint32_t i = 0; i < jiggle_data_chain.size(); i++) {
+ _execute_jiggle_joint(i, target, p_delta);
+ }
+
+ execution_error_found = false;
+}
+
+void SkeletonModification3DJiggle::_execute_jiggle_joint(int p_joint_idx, Node3D *p_target, real_t p_delta) {
+ // Adopted from: https://wiki.unity3d.com/index.php/JiggleBone
+ // With modifications by TwistedTwigleg.
+
+ if (jiggle_data_chain[p_joint_idx].bone_idx <= -2) {
+ jiggle_data_chain[p_joint_idx].bone_idx = stack->skeleton->find_bone(jiggle_data_chain[p_joint_idx].bone_name);
+ }
+ if (_print_execution_error(
+ jiggle_data_chain[p_joint_idx].bone_idx < 0 || jiggle_data_chain[p_joint_idx].bone_idx > stack->skeleton->get_bone_count(),
+ "Jiggle joint " + itos(p_joint_idx) + " bone index is invald. Cannot execute modification!")) {
+ return;
+ }
+
+ Transform3D new_bone_trans = stack->skeleton->local_pose_to_global_pose(jiggle_data_chain[p_joint_idx].bone_idx, stack->skeleton->get_bone_local_pose_override(jiggle_data_chain[p_joint_idx].bone_idx));
+ Vector3 target_position = stack->skeleton->world_transform_to_global_pose(p_target->get_global_transform()).origin;
+
+ jiggle_data_chain[p_joint_idx].force = (target_position - jiggle_data_chain[p_joint_idx].dynamic_position) * jiggle_data_chain[p_joint_idx].stiffness * p_delta;
+
+ if (jiggle_data_chain[p_joint_idx].use_gravity) {
+ Vector3 gravity_to_apply = new_bone_trans.basis.inverse().xform(jiggle_data_chain[p_joint_idx].gravity);
+ jiggle_data_chain[p_joint_idx].force += gravity_to_apply * p_delta;
+ }
+
+ jiggle_data_chain[p_joint_idx].acceleration = jiggle_data_chain[p_joint_idx].force / jiggle_data_chain[p_joint_idx].mass;
+ jiggle_data_chain[p_joint_idx].velocity += jiggle_data_chain[p_joint_idx].acceleration * (1 - jiggle_data_chain[p_joint_idx].damping);
+
+ jiggle_data_chain[p_joint_idx].dynamic_position += jiggle_data_chain[p_joint_idx].velocity + jiggle_data_chain[p_joint_idx].force;
+ jiggle_data_chain[p_joint_idx].dynamic_position += new_bone_trans.origin - jiggle_data_chain[p_joint_idx].last_position;
+ jiggle_data_chain[p_joint_idx].last_position = new_bone_trans.origin;
+
+ // Collision detection/response
+ if (use_colliders) {
+ if (execution_mode == SkeletonModificationStack3D::EXECUTION_MODE::execution_mode_physics_process) {
+ Ref<World3D> world_3d = stack->skeleton->get_world_3d();
+ ERR_FAIL_COND(world_3d.is_null());
+ PhysicsDirectSpaceState3D *space_state = PhysicsServer3D::get_singleton()->space_get_direct_state(world_3d->get_space());
+ PhysicsDirectSpaceState3D::RayResult ray_result;
+
+ // Convert to world transforms, which is what the physics server needs
+ Transform3D new_bone_trans_world = stack->skeleton->global_pose_to_world_transform(new_bone_trans);
+ Transform3D dynamic_position_world = stack->skeleton->global_pose_to_world_transform(Transform3D(Basis(), jiggle_data_chain[p_joint_idx].dynamic_position));
+
+ bool ray_hit = space_state->intersect_ray(new_bone_trans_world.origin, dynamic_position_world.get_origin(),
+ ray_result, Set<RID>(), collision_mask);
+
+ if (ray_hit) {
+ jiggle_data_chain[p_joint_idx].dynamic_position = jiggle_data_chain[p_joint_idx].last_noncollision_position;
+ jiggle_data_chain[p_joint_idx].acceleration = Vector3(0, 0, 0);
+ jiggle_data_chain[p_joint_idx].velocity = Vector3(0, 0, 0);
+ } else {
+ jiggle_data_chain[p_joint_idx].last_noncollision_position = jiggle_data_chain[p_joint_idx].dynamic_position;
+ }
+
+ } else {
+ WARN_PRINT_ONCE("Jiggle modifier: You cannot detect colliders without the stack mode being set to _physics_process!");
+ }
+ }
+
+ // Get the forward direction that the basis is facing in right now.
+ stack->skeleton->update_bone_rest_forward_vector(jiggle_data_chain[p_joint_idx].bone_idx);
+ Vector3 forward_vector = stack->skeleton->get_bone_axis_forward_vector(jiggle_data_chain[p_joint_idx].bone_idx);
+
+ // Rotate the bone using the dynamic position!
+ new_bone_trans.basis.rotate_to_align(forward_vector, new_bone_trans.origin.direction_to(jiggle_data_chain[p_joint_idx].dynamic_position));
+
+ // Roll
+ new_bone_trans.basis.rotate_local(forward_vector, jiggle_data_chain[p_joint_idx].roll);
+
+ new_bone_trans = stack->skeleton->global_pose_to_local_pose(jiggle_data_chain[p_joint_idx].bone_idx, new_bone_trans);
+ stack->skeleton->set_bone_local_pose_override(jiggle_data_chain[p_joint_idx].bone_idx, new_bone_trans, stack->strength, true);
+ stack->skeleton->force_update_bone_children_transforms(jiggle_data_chain[p_joint_idx].bone_idx);
+}
+
+void SkeletonModification3DJiggle::_update_jiggle_joint_data() {
+ for (uint32_t i = 0; i < jiggle_data_chain.size(); i++) {
+ if (!jiggle_data_chain[i].override_defaults) {
+ set_jiggle_joint_stiffness(i, stiffness);
+ set_jiggle_joint_mass(i, mass);
+ set_jiggle_joint_damping(i, damping);
+ set_jiggle_joint_use_gravity(i, use_gravity);
+ set_jiggle_joint_gravity(i, gravity);
+ }
+ }
+}
+
+void SkeletonModification3DJiggle::_setup_modification(SkeletonModificationStack3D *p_stack) {
+ stack = p_stack;
+
+ if (stack) {
+ is_setup = true;
+ execution_error_found = false;
+
+ if (stack->skeleton) {
+ for (uint32_t i = 0; i < jiggle_data_chain.size(); i++) {
+ int bone_idx = jiggle_data_chain[i].bone_idx;
+ if (bone_idx > 0 && bone_idx < stack->skeleton->get_bone_count()) {
+ jiggle_data_chain[i].dynamic_position = stack->skeleton->local_pose_to_global_pose(bone_idx, stack->skeleton->get_bone_local_pose_override(bone_idx)).origin;
+ }
+ }
+ }
+
+ update_cache();
+ }
+}
+
+void SkeletonModification3DJiggle::update_cache() {
+ if (!is_setup || !stack) {
+ _print_execution_error(true, "Cannot update target cache: modification is not properly setup!");
+ return;
+ }
+
+ target_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree()) {
+ if (stack->skeleton->has_node(target_node)) {
+ Node *node = stack->skeleton->get_node(target_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update target cache: node is this modification's skeleton or cannot be found!");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update target cache: node is not in the scene tree!");
+ target_node_cache = node->get_instance_id();
+
+ execution_error_found = false;
+ }
+ }
+ }
+}
+
+void SkeletonModification3DJiggle::set_target_node(const NodePath &p_target_node) {
+ target_node = p_target_node;
+ update_cache();
+}
+
+NodePath SkeletonModification3DJiggle::get_target_node() const {
+ return target_node;
+}
+
+void SkeletonModification3DJiggle::set_stiffness(real_t p_stiffness) {
+ ERR_FAIL_COND_MSG(p_stiffness < 0, "Stiffness cannot be set to a negative value!");
+ stiffness = p_stiffness;
+ _update_jiggle_joint_data();
+}
+
+real_t SkeletonModification3DJiggle::get_stiffness() const {
+ return stiffness;
+}
+
+void SkeletonModification3DJiggle::set_mass(real_t p_mass) {
+ ERR_FAIL_COND_MSG(p_mass < 0, "Mass cannot be set to a negative value!");
+ mass = p_mass;
+ _update_jiggle_joint_data();
+}
+
+real_t SkeletonModification3DJiggle::get_mass() const {
+ return mass;
+}
+
+void SkeletonModification3DJiggle::set_damping(real_t p_damping) {
+ ERR_FAIL_COND_MSG(p_damping < 0, "Damping cannot be set to a negative value!");
+ ERR_FAIL_COND_MSG(p_damping > 1, "Damping cannot be more than one!");
+ damping = p_damping;
+ _update_jiggle_joint_data();
+}
+
+real_t SkeletonModification3DJiggle::get_damping() const {
+ return damping;
+}
+
+void SkeletonModification3DJiggle::set_use_gravity(bool p_use_gravity) {
+ use_gravity = p_use_gravity;
+ _update_jiggle_joint_data();
+}
+
+bool SkeletonModification3DJiggle::get_use_gravity() const {
+ return use_gravity;
+}
+
+void SkeletonModification3DJiggle::set_gravity(Vector3 p_gravity) {
+ gravity = p_gravity;
+ _update_jiggle_joint_data();
+}
+
+Vector3 SkeletonModification3DJiggle::get_gravity() const {
+ return gravity;
+}
+
+void SkeletonModification3DJiggle::set_use_colliders(bool p_use_collider) {
+ use_colliders = p_use_collider;
+ notify_property_list_changed();
+}
+
+bool SkeletonModification3DJiggle::get_use_colliders() const {
+ return use_colliders;
+}
+
+void SkeletonModification3DJiggle::set_collision_mask(int p_mask) {
+ collision_mask = p_mask;
+}
+
+int SkeletonModification3DJiggle::get_collision_mask() const {
+ return collision_mask;
+}
+
+// Jiggle joint data functions
+int SkeletonModification3DJiggle::get_jiggle_data_chain_length() {
+ return jiggle_data_chain.size();
+}
+
+void SkeletonModification3DJiggle::set_jiggle_data_chain_length(int p_length) {
+ ERR_FAIL_COND(p_length < 0);
+ jiggle_data_chain.resize(p_length);
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+void SkeletonModification3DJiggle::set_jiggle_joint_bone_name(int p_joint_idx, String p_name) {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+
+ jiggle_data_chain[p_joint_idx].bone_name = p_name;
+ if (stack && stack->skeleton) {
+ jiggle_data_chain[p_joint_idx].bone_idx = stack->skeleton->find_bone(p_name);
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+String SkeletonModification3DJiggle::get_jiggle_joint_bone_name(int p_joint_idx) const {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, "");
+ return jiggle_data_chain[p_joint_idx].bone_name;
+}
+
+int SkeletonModification3DJiggle::get_jiggle_joint_bone_index(int p_joint_idx) const {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
+ return jiggle_data_chain[p_joint_idx].bone_idx;
+}
+
+void SkeletonModification3DJiggle::set_jiggle_joint_bone_index(int p_joint_idx, int p_bone_idx) {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!");
+ jiggle_data_chain[p_joint_idx].bone_idx = p_bone_idx;
+
+ if (stack) {
+ if (stack->skeleton) {
+ jiggle_data_chain[p_joint_idx].bone_name = stack->skeleton->get_bone_name(p_bone_idx);
+ }
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+void SkeletonModification3DJiggle::set_jiggle_joint_override(int p_joint_idx, bool p_override) {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ jiggle_data_chain[p_joint_idx].override_defaults = p_override;
+ _update_jiggle_joint_data();
+ notify_property_list_changed();
+}
+
+bool SkeletonModification3DJiggle::get_jiggle_joint_override(int p_joint_idx) const {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
+ return jiggle_data_chain[p_joint_idx].override_defaults;
+}
+
+void SkeletonModification3DJiggle::set_jiggle_joint_stiffness(int p_joint_idx, real_t p_stiffness) {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_COND_MSG(p_stiffness < 0, "Stiffness cannot be set to a negative value!");
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ jiggle_data_chain[p_joint_idx].stiffness = p_stiffness;
+}
+
+real_t SkeletonModification3DJiggle::get_jiggle_joint_stiffness(int p_joint_idx) const {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
+ return jiggle_data_chain[p_joint_idx].stiffness;
+}
+
+void SkeletonModification3DJiggle::set_jiggle_joint_mass(int p_joint_idx, real_t p_mass) {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_COND_MSG(p_mass < 0, "Mass cannot be set to a negative value!");
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ jiggle_data_chain[p_joint_idx].mass = p_mass;
+}
+
+real_t SkeletonModification3DJiggle::get_jiggle_joint_mass(int p_joint_idx) const {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
+ return jiggle_data_chain[p_joint_idx].mass;
+}
+
+void SkeletonModification3DJiggle::set_jiggle_joint_damping(int p_joint_idx, real_t p_damping) {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_COND_MSG(p_damping < 0, "Damping cannot be set to a negative value!");
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ jiggle_data_chain[p_joint_idx].damping = p_damping;
+}
+
+real_t SkeletonModification3DJiggle::get_jiggle_joint_damping(int p_joint_idx) const {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
+ return jiggle_data_chain[p_joint_idx].damping;
+}
+
+void SkeletonModification3DJiggle::set_jiggle_joint_use_gravity(int p_joint_idx, bool p_use_gravity) {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ jiggle_data_chain[p_joint_idx].use_gravity = p_use_gravity;
+ notify_property_list_changed();
+}
+
+bool SkeletonModification3DJiggle::get_jiggle_joint_use_gravity(int p_joint_idx) const {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
+ return jiggle_data_chain[p_joint_idx].use_gravity;
+}
+
+void SkeletonModification3DJiggle::set_jiggle_joint_gravity(int p_joint_idx, Vector3 p_gravity) {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ jiggle_data_chain[p_joint_idx].gravity = p_gravity;
+}
+
+Vector3 SkeletonModification3DJiggle::get_jiggle_joint_gravity(int p_joint_idx) const {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, Vector3(0, 0, 0));
+ return jiggle_data_chain[p_joint_idx].gravity;
+}
+
+void SkeletonModification3DJiggle::set_jiggle_joint_roll(int p_joint_idx, real_t p_roll) {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ jiggle_data_chain[p_joint_idx].roll = p_roll;
+}
+
+real_t SkeletonModification3DJiggle::get_jiggle_joint_roll(int p_joint_idx) const {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, 0.0);
+ return jiggle_data_chain[p_joint_idx].roll;
+}
+
+void SkeletonModification3DJiggle::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification3DJiggle::set_target_node);
+ ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification3DJiggle::get_target_node);
+
+ ClassDB::bind_method(D_METHOD("set_jiggle_data_chain_length", "length"), &SkeletonModification3DJiggle::set_jiggle_data_chain_length);
+ ClassDB::bind_method(D_METHOD("get_jiggle_data_chain_length"), &SkeletonModification3DJiggle::get_jiggle_data_chain_length);
+
+ ClassDB::bind_method(D_METHOD("set_stiffness", "stiffness"), &SkeletonModification3DJiggle::set_stiffness);
+ ClassDB::bind_method(D_METHOD("get_stiffness"), &SkeletonModification3DJiggle::get_stiffness);
+ ClassDB::bind_method(D_METHOD("set_mass", "mass"), &SkeletonModification3DJiggle::set_mass);
+ ClassDB::bind_method(D_METHOD("get_mass"), &SkeletonModification3DJiggle::get_mass);
+ ClassDB::bind_method(D_METHOD("set_damping", "damping"), &SkeletonModification3DJiggle::set_damping);
+ ClassDB::bind_method(D_METHOD("get_damping"), &SkeletonModification3DJiggle::get_damping);
+ ClassDB::bind_method(D_METHOD("set_use_gravity", "use_gravity"), &SkeletonModification3DJiggle::set_use_gravity);
+ ClassDB::bind_method(D_METHOD("get_use_gravity"), &SkeletonModification3DJiggle::get_use_gravity);
+ ClassDB::bind_method(D_METHOD("set_gravity", "gravity"), &SkeletonModification3DJiggle::set_gravity);
+ ClassDB::bind_method(D_METHOD("get_gravity"), &SkeletonModification3DJiggle::get_gravity);
+
+ ClassDB::bind_method(D_METHOD("set_use_colliders", "use_colliders"), &SkeletonModification3DJiggle::set_use_colliders);
+ ClassDB::bind_method(D_METHOD("get_use_colliders"), &SkeletonModification3DJiggle::get_use_colliders);
+ ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &SkeletonModification3DJiggle::set_collision_mask);
+ ClassDB::bind_method(D_METHOD("get_collision_mask"), &SkeletonModification3DJiggle::get_collision_mask);
+
+ // Jiggle joint data functions
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_bone_name", "joint_idx", "name"), &SkeletonModification3DJiggle::set_jiggle_joint_bone_name);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_bone_name", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_bone_name);
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_bone_index", "joint_idx", "bone_idx"), &SkeletonModification3DJiggle::set_jiggle_joint_bone_index);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_bone_index", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_bone_index);
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_override", "joint_idx", "override"), &SkeletonModification3DJiggle::set_jiggle_joint_override);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_override", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_override);
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_stiffness", "joint_idx", "stiffness"), &SkeletonModification3DJiggle::set_jiggle_joint_stiffness);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_stiffness", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_stiffness);
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_mass", "joint_idx", "mass"), &SkeletonModification3DJiggle::set_jiggle_joint_mass);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_mass", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_mass);
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_damping", "joint_idx", "damping"), &SkeletonModification3DJiggle::set_jiggle_joint_damping);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_damping", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_damping);
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_use_gravity", "joint_idx", "use_gravity"), &SkeletonModification3DJiggle::set_jiggle_joint_use_gravity);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_use_gravity", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_use_gravity);
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_gravity", "joint_idx", "gravity"), &SkeletonModification3DJiggle::set_jiggle_joint_gravity);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_gravity", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_gravity);
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_roll", "joint_idx", "roll"), &SkeletonModification3DJiggle::set_jiggle_joint_roll);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_roll", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_roll);
+
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D"), "set_target_node", "get_target_node");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "jiggle_data_chain_length", PROPERTY_HINT_RANGE, "0,100,1"), "set_jiggle_data_chain_length", "get_jiggle_data_chain_length");
+ ADD_GROUP("Default Joint Settings", "");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "stiffness"), "set_stiffness", "get_stiffness");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass"), "set_mass", "get_mass");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping", PROPERTY_HINT_RANGE, "0, 1, 0.01"), "set_damping", "get_damping");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_gravity"), "set_use_gravity", "get_use_gravity");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity"), "set_gravity", "get_gravity");
+ ADD_GROUP("", "");
+}
+
+SkeletonModification3DJiggle::SkeletonModification3DJiggle() {
+ stack = nullptr;
+ is_setup = false;
+ jiggle_data_chain = Vector<Jiggle_Joint_Data>();
+ stiffness = 3;
+ mass = 0.75;
+ damping = 0.75;
+ use_gravity = false;
+ gravity = Vector3(0, -6.0, 0);
+ enabled = true;
+}
+
+SkeletonModification3DJiggle::~SkeletonModification3DJiggle() {
+}
diff --git a/scene/resources/skeleton_modification_3d_jiggle.h b/scene/resources/skeleton_modification_3d_jiggle.h
new file mode 100644
index 0000000000..c210c8fa73
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_jiggle.h
@@ -0,0 +1,138 @@
+/*************************************************************************/
+/* skeleton_modification_3d_jiggle.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 "core/templates/local_vector.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+#ifndef SKELETONMODIFICATION3DJIGGLE_H
+#define SKELETONMODIFICATION3DJIGGLE_H
+
+class SkeletonModification3DJiggle : public SkeletonModification3D {
+ GDCLASS(SkeletonModification3DJiggle, SkeletonModification3D);
+
+private:
+ struct Jiggle_Joint_Data {
+ String bone_name = "";
+ int bone_idx = -1;
+
+ bool override_defaults = false;
+ real_t stiffness = 3;
+ real_t mass = 0.75;
+ real_t damping = 0.75;
+ bool use_gravity = false;
+ Vector3 gravity = Vector3(0, -6.0, 0);
+ real_t roll = 0;
+
+ Vector3 cached_rotation = Vector3(0, 0, 0);
+ Vector3 force = Vector3(0, 0, 0);
+ Vector3 acceleration = Vector3(0, 0, 0);
+ Vector3 velocity = Vector3(0, 0, 0);
+ Vector3 last_position = Vector3(0, 0, 0);
+ Vector3 dynamic_position = Vector3(0, 0, 0);
+
+ Vector3 last_noncollision_position = Vector3(0, 0, 0);
+ };
+
+ NodePath target_node;
+ ObjectID target_node_cache;
+ LocalVector<Jiggle_Joint_Data> jiggle_data_chain;
+
+ real_t stiffness = 3;
+ real_t mass = 0.75;
+ real_t damping = 0.75;
+ bool use_gravity = false;
+ Vector3 gravity = Vector3(0, -6.0, 0);
+
+ bool use_colliders = false;
+ uint32_t collision_mask = 1;
+
+ void update_cache();
+ void _execute_jiggle_joint(int p_joint_idx, Node3D *p_target, real_t p_delta);
+ void _update_jiggle_joint_data();
+
+protected:
+ static void _bind_methods();
+ bool _get(const StringName &p_path, Variant &r_ret) const;
+ bool _set(const StringName &p_path, const Variant &p_value);
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+public:
+ virtual void _execute(real_t p_delta) override;
+ virtual void _setup_modification(SkeletonModificationStack3D *p_stack) override;
+
+ void set_target_node(const NodePath &p_target_node);
+ NodePath get_target_node() const;
+
+ void set_stiffness(real_t p_stiffness);
+ real_t get_stiffness() const;
+ void set_mass(real_t p_mass);
+ real_t get_mass() const;
+ void set_damping(real_t p_damping);
+ real_t get_damping() const;
+
+ void set_use_gravity(bool p_use_gravity);
+ bool get_use_gravity() const;
+ void set_gravity(Vector3 p_gravity);
+ Vector3 get_gravity() const;
+
+ void set_use_colliders(bool p_use_colliders);
+ bool get_use_colliders() const;
+ void set_collision_mask(int p_mask);
+ int get_collision_mask() const;
+
+ int get_jiggle_data_chain_length();
+ void set_jiggle_data_chain_length(int p_new_length);
+
+ void set_jiggle_joint_bone_name(int p_joint_idx, String p_name);
+ String get_jiggle_joint_bone_name(int p_joint_idx) const;
+ void set_jiggle_joint_bone_index(int p_joint_idx, int p_idx);
+ int get_jiggle_joint_bone_index(int p_joint_idx) const;
+
+ void set_jiggle_joint_override(int p_joint_idx, bool p_override);
+ bool get_jiggle_joint_override(int p_joint_idx) const;
+ void set_jiggle_joint_stiffness(int p_joint_idx, real_t p_stiffness);
+ real_t get_jiggle_joint_stiffness(int p_joint_idx) const;
+ void set_jiggle_joint_mass(int p_joint_idx, real_t p_mass);
+ real_t get_jiggle_joint_mass(int p_joint_idx) const;
+ void set_jiggle_joint_damping(int p_joint_idx, real_t p_damping);
+ real_t get_jiggle_joint_damping(int p_joint_idx) const;
+ void set_jiggle_joint_use_gravity(int p_joint_idx, bool p_use_gravity);
+ bool get_jiggle_joint_use_gravity(int p_joint_idx) const;
+ void set_jiggle_joint_gravity(int p_joint_idx, Vector3 p_gravity);
+ Vector3 get_jiggle_joint_gravity(int p_joint_idx) const;
+ void set_jiggle_joint_roll(int p_joint_idx, real_t p_roll);
+ real_t get_jiggle_joint_roll(int p_joint_idx) const;
+
+ SkeletonModification3DJiggle();
+ ~SkeletonModification3DJiggle();
+};
+
+#endif //SKELETONMODIFICATION3DJIGGLE_H
diff --git a/scene/resources/skeleton_modification_3d_lookat.cpp b/scene/resources/skeleton_modification_3d_lookat.cpp
new file mode 100644
index 0000000000..afdb077e71
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_lookat.cpp
@@ -0,0 +1,265 @@
+/*************************************************************************/
+/* skeleton_modification_3d_lookat.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 "scene/resources/skeleton_modification_3d_lookat.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+bool SkeletonModification3DLookAt::_set(const StringName &p_path, const Variant &p_value) {
+ if (p_path == "lock_rotation_to_plane") {
+ set_lock_rotation_to_plane(p_value);
+ } else if (p_path == "lock_rotation_plane") {
+ set_lock_rotation_plane(p_value);
+ } else if (p_path == "additional_rotation") {
+ Vector3 tmp = p_value;
+ tmp.x = Math::deg2rad(tmp.x);
+ tmp.y = Math::deg2rad(tmp.y);
+ tmp.z = Math::deg2rad(tmp.z);
+ set_additional_rotation(tmp);
+ }
+
+ return true;
+}
+
+bool SkeletonModification3DLookAt::_get(const StringName &p_path, Variant &r_ret) const {
+ if (p_path == "lock_rotation_to_plane") {
+ r_ret = get_lock_rotation_to_plane();
+ } else if (p_path == "lock_rotation_plane") {
+ r_ret = get_lock_rotation_plane();
+ } else if (p_path == "additional_rotation") {
+ Vector3 tmp = get_additional_rotation();
+ tmp.x = Math::rad2deg(tmp.x);
+ tmp.y = Math::rad2deg(tmp.y);
+ tmp.z = Math::rad2deg(tmp.z);
+ r_ret = tmp;
+ }
+
+ return true;
+}
+
+void SkeletonModification3DLookAt::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::BOOL, "lock_rotation_to_plane", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ if (lock_rotation_to_plane) {
+ p_list->push_back(PropertyInfo(Variant::INT, "lock_rotation_plane", PROPERTY_HINT_ENUM, "X plane, Y plane, Z plane", PROPERTY_USAGE_DEFAULT));
+ }
+ p_list->push_back(PropertyInfo(Variant::VECTOR3, "additional_rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+}
+
+void SkeletonModification3DLookAt::_execute(real_t p_delta) {
+ ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr,
+ "Modification is not setup and therefore cannot execute!");
+ if (!enabled) {
+ return;
+ }
+
+ if (target_node_cache.is_null()) {
+ _print_execution_error(true, "Target cache is out of date. Attempting to update...");
+ update_cache();
+ return;
+ }
+
+ if (bone_idx <= -2) {
+ bone_idx = stack->skeleton->find_bone(bone_name);
+ }
+
+ Node3D *target = Object::cast_to<Node3D>(ObjectDB::get_instance(target_node_cache));
+ if (_print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) {
+ return;
+ }
+ if (_print_execution_error(bone_idx <= -1, "Bone index is invalid. Cannot execute modification!")) {
+ return;
+ }
+
+ Transform3D new_bone_trans = stack->skeleton->get_bone_local_pose_override(bone_idx);
+ Vector3 target_pos = stack->skeleton->global_pose_to_local_pose(bone_idx, stack->skeleton->world_transform_to_global_pose(target->get_global_transform())).origin;
+
+ // Lock the rotation to a plane relative to the bone by changing the target position
+ if (lock_rotation_to_plane) {
+ if (lock_rotation_plane == ROTATION_PLANE::ROTATION_PLANE_X) {
+ target_pos.x = new_bone_trans.origin.x;
+ } else if (lock_rotation_plane == ROTATION_PLANE::ROTATION_PLANE_Y) {
+ target_pos.y = new_bone_trans.origin.y;
+ } else if (lock_rotation_plane == ROTATION_PLANE::ROTATION_PLANE_Z) {
+ target_pos.z = new_bone_trans.origin.z;
+ }
+ }
+
+ // Look at the target!
+ new_bone_trans = new_bone_trans.looking_at(target_pos, Vector3(0, 1, 0));
+ // Convert from Z-forward to whatever direction the bone faces.
+ stack->skeleton->update_bone_rest_forward_vector(bone_idx);
+ new_bone_trans.basis = stack->skeleton->global_pose_z_forward_to_bone_forward(bone_idx, new_bone_trans.basis);
+
+ // Apply additional rotation
+ new_bone_trans.basis.rotate_local(Vector3(1, 0, 0), additional_rotation.x);
+ new_bone_trans.basis.rotate_local(Vector3(0, 1, 0), additional_rotation.y);
+ new_bone_trans.basis.rotate_local(Vector3(0, 0, 1), additional_rotation.z);
+
+ stack->skeleton->set_bone_local_pose_override(bone_idx, new_bone_trans, stack->strength, true);
+ stack->skeleton->force_update_bone_children_transforms(bone_idx);
+
+ // If we completed it successfully, then we can set execution_error_found to false
+ execution_error_found = false;
+}
+
+void SkeletonModification3DLookAt::_setup_modification(SkeletonModificationStack3D *p_stack) {
+ stack = p_stack;
+
+ if (stack != nullptr) {
+ is_setup = true;
+ execution_error_found = false;
+ update_cache();
+ }
+}
+
+void SkeletonModification3DLookAt::set_bone_name(String p_name) {
+ bone_name = p_name;
+ if (stack) {
+ if (stack->skeleton) {
+ bone_idx = stack->skeleton->find_bone(bone_name);
+ }
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+String SkeletonModification3DLookAt::get_bone_name() const {
+ return bone_name;
+}
+
+int SkeletonModification3DLookAt::get_bone_index() const {
+ return bone_idx;
+}
+
+void SkeletonModification3DLookAt::set_bone_index(int p_bone_idx) {
+ ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!");
+ bone_idx = p_bone_idx;
+
+ if (stack) {
+ if (stack->skeleton) {
+ bone_name = stack->skeleton->get_bone_name(p_bone_idx);
+ }
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+void SkeletonModification3DLookAt::update_cache() {
+ if (!is_setup || !stack) {
+ _print_execution_error(true, "Cannot update target cache: modification is not properly setup!");
+ return;
+ }
+
+ target_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree()) {
+ if (stack->skeleton->has_node(target_node)) {
+ Node *node = stack->skeleton->get_node(target_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update target cache: Node is this modification's skeleton or cannot be found!");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update target cache: Node is not in the scene tree!");
+ target_node_cache = node->get_instance_id();
+
+ execution_error_found = false;
+ }
+ }
+ }
+}
+
+void SkeletonModification3DLookAt::set_target_node(const NodePath &p_target_node) {
+ target_node = p_target_node;
+ update_cache();
+}
+
+NodePath SkeletonModification3DLookAt::get_target_node() const {
+ return target_node;
+}
+
+Vector3 SkeletonModification3DLookAt::get_additional_rotation() const {
+ return additional_rotation;
+}
+
+void SkeletonModification3DLookAt::set_additional_rotation(Vector3 p_offset) {
+ additional_rotation = p_offset;
+}
+
+bool SkeletonModification3DLookAt::get_lock_rotation_to_plane() const {
+ return lock_rotation_plane;
+}
+
+void SkeletonModification3DLookAt::set_lock_rotation_to_plane(bool p_lock_rotation) {
+ lock_rotation_to_plane = p_lock_rotation;
+ notify_property_list_changed();
+}
+
+int SkeletonModification3DLookAt::get_lock_rotation_plane() const {
+ return lock_rotation_plane;
+}
+
+void SkeletonModification3DLookAt::set_lock_rotation_plane(int p_plane) {
+ lock_rotation_plane = p_plane;
+}
+
+void SkeletonModification3DLookAt::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_bone_name", "name"), &SkeletonModification3DLookAt::set_bone_name);
+ ClassDB::bind_method(D_METHOD("get_bone_name"), &SkeletonModification3DLookAt::get_bone_name);
+
+ ClassDB::bind_method(D_METHOD("set_bone_index", "bone_idx"), &SkeletonModification3DLookAt::set_bone_index);
+ ClassDB::bind_method(D_METHOD("get_bone_index"), &SkeletonModification3DLookAt::get_bone_index);
+
+ ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification3DLookAt::set_target_node);
+ ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification3DLookAt::get_target_node);
+
+ ClassDB::bind_method(D_METHOD("set_additional_rotation", "additional_rotation"), &SkeletonModification3DLookAt::set_additional_rotation);
+ ClassDB::bind_method(D_METHOD("get_additional_rotation"), &SkeletonModification3DLookAt::get_additional_rotation);
+
+ ClassDB::bind_method(D_METHOD("set_lock_rotation_to_plane", "lock_to_plane"), &SkeletonModification3DLookAt::set_lock_rotation_to_plane);
+ ClassDB::bind_method(D_METHOD("get_lock_rotation_to_plane"), &SkeletonModification3DLookAt::get_lock_rotation_to_plane);
+ ClassDB::bind_method(D_METHOD("set_lock_rotation_plane", "plane"), &SkeletonModification3DLookAt::set_lock_rotation_plane);
+ ClassDB::bind_method(D_METHOD("get_lock_rotation_plane"), &SkeletonModification3DLookAt::get_lock_rotation_plane);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bone_name"), "set_bone_name", "get_bone_name");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_index"), "set_bone_index", "get_bone_index");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D"), "set_target_node", "get_target_node");
+}
+
+SkeletonModification3DLookAt::SkeletonModification3DLookAt() {
+ stack = nullptr;
+ is_setup = false;
+ bone_name = "";
+ bone_idx = -2;
+ additional_rotation = Vector3();
+ lock_rotation_to_plane = false;
+ enabled = true;
+}
+
+SkeletonModification3DLookAt::~SkeletonModification3DLookAt() {
+}
diff --git a/scene/resources/skeleton_modification_3d_lookat.h b/scene/resources/skeleton_modification_3d_lookat.h
new file mode 100644
index 0000000000..5971e3f647
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_lookat.h
@@ -0,0 +1,89 @@
+/*************************************************************************/
+/* skeleton_modification_3d_lookat.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+#ifndef SKELETONMODIFICATION3DLOOKAT_H
+#define SKELETONMODIFICATION3DLOOKAT_H
+
+class SkeletonModification3DLookAt : public SkeletonModification3D {
+ GDCLASS(SkeletonModification3DLookAt, SkeletonModification3D);
+
+private:
+ String bone_name = "";
+ int bone_idx = -1;
+ NodePath target_node;
+ ObjectID target_node_cache;
+
+ Vector3 additional_rotation = Vector3(1, 0, 0);
+ bool lock_rotation_to_plane = false;
+ int lock_rotation_plane = ROTATION_PLANE_X;
+
+ void update_cache();
+
+protected:
+ static void _bind_methods();
+ bool _get(const StringName &p_path, Variant &r_ret) const;
+ bool _set(const StringName &p_path, const Variant &p_value);
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+public:
+ enum ROTATION_PLANE {
+ ROTATION_PLANE_X,
+ ROTATION_PLANE_Y,
+ ROTATION_PLANE_Z
+ };
+
+ virtual void _execute(real_t p_delta) override;
+ virtual void _setup_modification(SkeletonModificationStack3D *p_stack) override;
+
+ void set_bone_name(String p_name);
+ String get_bone_name() const;
+
+ void set_bone_index(int p_idx);
+ int get_bone_index() const;
+
+ void set_target_node(const NodePath &p_target_node);
+ NodePath get_target_node() const;
+
+ void set_additional_rotation(Vector3 p_offset);
+ Vector3 get_additional_rotation() const;
+
+ void set_lock_rotation_to_plane(bool p_lock_to_plane);
+ bool get_lock_rotation_to_plane() const;
+ void set_lock_rotation_plane(int p_plane);
+ int get_lock_rotation_plane() const;
+
+ SkeletonModification3DLookAt();
+ ~SkeletonModification3DLookAt();
+};
+
+#endif //SKELETONMODIFICATION3DLOOKAT_H
diff --git a/scene/resources/skeleton_modification_3d_stackholder.cpp b/scene/resources/skeleton_modification_3d_stackholder.cpp
new file mode 100644
index 0000000000..56035a4def
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_stackholder.cpp
@@ -0,0 +1,104 @@
+/*************************************************************************/
+/* skeleton_modification_3d_stackholder.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 "scene/resources/skeleton_modification_3d_stackholder.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+bool SkeletonModification3DStackHolder::_set(const StringName &p_path, const Variant &p_value) {
+ String path = p_path;
+
+ if (path == "held_modification_stack") {
+ set_held_modification_stack(p_value);
+ }
+ return true;
+}
+
+bool SkeletonModification3DStackHolder::_get(const StringName &p_path, Variant &r_ret) const {
+ String path = p_path;
+
+ if (path == "held_modification_stack") {
+ r_ret = get_held_modification_stack();
+ }
+ return true;
+}
+
+void SkeletonModification3DStackHolder::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::OBJECT, "held_modification_stack", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonModificationStack3D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
+}
+
+void SkeletonModification3DStackHolder::_execute(real_t p_delta) {
+ ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr,
+ "Modification is not setup and therefore cannot execute!");
+
+ if (held_modification_stack.is_valid()) {
+ held_modification_stack->execute(p_delta, execution_mode);
+ }
+}
+
+void SkeletonModification3DStackHolder::_setup_modification(SkeletonModificationStack3D *p_stack) {
+ stack = p_stack;
+
+ if (stack != nullptr) {
+ is_setup = true;
+
+ if (held_modification_stack.is_valid()) {
+ held_modification_stack->set_skeleton(stack->get_skeleton());
+ held_modification_stack->setup();
+ }
+ }
+}
+
+void SkeletonModification3DStackHolder::set_held_modification_stack(Ref<SkeletonModificationStack3D> p_held_stack) {
+ held_modification_stack = p_held_stack;
+
+ if (is_setup && held_modification_stack.is_valid()) {
+ held_modification_stack->set_skeleton(stack->get_skeleton());
+ held_modification_stack->setup();
+ }
+}
+
+Ref<SkeletonModificationStack3D> SkeletonModification3DStackHolder::get_held_modification_stack() const {
+ return held_modification_stack;
+}
+
+void SkeletonModification3DStackHolder::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_held_modification_stack", "held_modification_stack"), &SkeletonModification3DStackHolder::set_held_modification_stack);
+ ClassDB::bind_method(D_METHOD("get_held_modification_stack"), &SkeletonModification3DStackHolder::get_held_modification_stack);
+}
+
+SkeletonModification3DStackHolder::SkeletonModification3DStackHolder() {
+ stack = nullptr;
+ is_setup = false;
+ enabled = true;
+}
+
+SkeletonModification3DStackHolder::~SkeletonModification3DStackHolder() {
+}
diff --git a/scene/resources/skeleton_modification_3d_stackholder.h b/scene/resources/skeleton_modification_3d_stackholder.h
new file mode 100644
index 0000000000..c765cd8de3
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_stackholder.h
@@ -0,0 +1,59 @@
+/*************************************************************************/
+/* skeleton_modification_3d_stackholder.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+#ifndef SKELETONMODIFICATION3DSTACKHOLDER_H
+#define SKELETONMODIFICATION3DSTACKHOLDER_H
+
+class SkeletonModification3DStackHolder : public SkeletonModification3D {
+ GDCLASS(SkeletonModification3DStackHolder, SkeletonModification3D);
+
+protected:
+ static void _bind_methods();
+ bool _get(const StringName &p_path, Variant &r_ret) const;
+ bool _set(const StringName &p_path, const Variant &p_value);
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+public:
+ Ref<SkeletonModificationStack3D> held_modification_stack;
+
+ virtual void _execute(real_t p_delta) override;
+ virtual void _setup_modification(SkeletonModificationStack3D *p_stack) override;
+
+ void set_held_modification_stack(Ref<SkeletonModificationStack3D> p_held_stack);
+ Ref<SkeletonModificationStack3D> get_held_modification_stack() const;
+
+ SkeletonModification3DStackHolder();
+ ~SkeletonModification3DStackHolder();
+};
+
+#endif //SKELETONMODIFICATION3DSTACKHOLDER_H
diff --git a/scene/resources/skeleton_modification_3d_twoboneik.cpp b/scene/resources/skeleton_modification_3d_twoboneik.cpp
new file mode 100644
index 0000000000..ae7a3bab7e
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_twoboneik.cpp
@@ -0,0 +1,599 @@
+/*************************************************************************/
+/* skeleton_modification_3d_twoboneik.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 "scene/resources/skeleton_modification_3d_twoboneik.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+bool SkeletonModification3DTwoBoneIK::_set(const StringName &p_path, const Variant &p_value) {
+ String path = p_path;
+
+ if (path == "use_tip_node") {
+ set_use_tip_node(p_value);
+ } else if (path == "tip_node") {
+ set_tip_node(p_value);
+ } else if (path == "auto_calculate_joint_length") {
+ set_auto_calculate_joint_length(p_value);
+ } else if (path == "use_pole_node") {
+ set_use_pole_node(p_value);
+ } else if (path == "pole_node") {
+ set_pole_node(p_value);
+ } else if (path == "joint_one_length") {
+ set_joint_one_length(p_value);
+ } else if (path == "joint_two_length") {
+ set_joint_two_length(p_value);
+ } else if (path == "joint_one/bone_name") {
+ set_joint_one_bone_name(p_value);
+ } else if (path == "joint_one/bone_idx") {
+ set_joint_one_bone_idx(p_value);
+ } else if (path == "joint_one/roll") {
+ set_joint_one_roll(Math::deg2rad(real_t(p_value)));
+ } else if (path == "joint_two/bone_name") {
+ set_joint_two_bone_name(p_value);
+ } else if (path == "joint_two/bone_idx") {
+ set_joint_two_bone_idx(p_value);
+ } else if (path == "joint_two/roll") {
+ set_joint_two_roll(Math::deg2rad(real_t(p_value)));
+ }
+
+ return true;
+}
+
+bool SkeletonModification3DTwoBoneIK::_get(const StringName &p_path, Variant &r_ret) const {
+ String path = p_path;
+
+ if (path == "use_tip_node") {
+ r_ret = get_use_tip_node();
+ } else if (path == "tip_node") {
+ r_ret = get_tip_node();
+ } else if (path == "auto_calculate_joint_length") {
+ r_ret = get_auto_calculate_joint_length();
+ } else if (path == "use_pole_node") {
+ r_ret = get_use_pole_node();
+ } else if (path == "pole_node") {
+ r_ret = get_pole_node();
+ } else if (path == "joint_one_length") {
+ r_ret = get_joint_one_length();
+ } else if (path == "joint_two_length") {
+ r_ret = get_joint_two_length();
+ } else if (path == "joint_one/bone_name") {
+ r_ret = get_joint_one_bone_name();
+ } else if (path == "joint_one/bone_idx") {
+ r_ret = get_joint_one_bone_idx();
+ } else if (path == "joint_one/roll") {
+ r_ret = Math::rad2deg(get_joint_one_roll());
+ } else if (path == "joint_two/bone_name") {
+ r_ret = get_joint_two_bone_name();
+ } else if (path == "joint_two/bone_idx") {
+ r_ret = get_joint_two_bone_idx();
+ } else if (path == "joint_two/roll") {
+ r_ret = Math::rad2deg(get_joint_two_roll());
+ }
+
+ return true;
+}
+
+void SkeletonModification3DTwoBoneIK::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::BOOL, "use_tip_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ if (use_tip_node) {
+ p_list->push_back(PropertyInfo(Variant::NODE_PATH, "tip_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D", PROPERTY_USAGE_DEFAULT));
+ }
+
+ p_list->push_back(PropertyInfo(Variant::BOOL, "auto_calculate_joint_length", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ if (!auto_calculate_joint_length) {
+ p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_one_length", PROPERTY_HINT_RANGE, "-1, 10000, 0.001", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_two_length", PROPERTY_HINT_RANGE, "-1, 10000, 0.001", PROPERTY_USAGE_DEFAULT));
+ }
+
+ p_list->push_back(PropertyInfo(Variant::BOOL, "use_pole_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ if (use_pole_node) {
+ p_list->push_back(PropertyInfo(Variant::NODE_PATH, "pole_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D", PROPERTY_USAGE_DEFAULT));
+ }
+
+ p_list->push_back(PropertyInfo(Variant::STRING_NAME, "joint_one/bone_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::INT, "joint_one/bone_idx", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_one/roll", PROPERTY_HINT_RANGE, "-360, 360, 0.01", PROPERTY_USAGE_DEFAULT));
+
+ p_list->push_back(PropertyInfo(Variant::STRING_NAME, "joint_two/bone_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::INT, "joint_two/bone_idx", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_two/roll", PROPERTY_HINT_RANGE, "-360, 360, 0.01", PROPERTY_USAGE_DEFAULT));
+}
+
+void SkeletonModification3DTwoBoneIK::_execute(real_t p_delta) {
+ ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr,
+ "Modification is not setup and therefore cannot execute!");
+
+ if (!enabled) {
+ return;
+ }
+
+ if (_print_execution_error(joint_one_bone_idx < 0 || joint_two_bone_idx < 0,
+ "One (or more) of the bones in the modification have invalid bone indexes. Cannot execute modification!")) {
+ return;
+ }
+
+ if (target_node_cache.is_null()) {
+ _print_execution_error(true, "Target cache is out of date. Attempting to update...");
+ update_cache_target();
+ return;
+ }
+
+ // Update joint lengths (if needed)
+ if (auto_calculate_joint_length && (joint_one_length < 0 || joint_two_length < 0)) {
+ calculate_joint_lengths();
+ }
+
+ // Adopted from the links below:
+ // http://theorangeduck.com/page/simple-two-joint
+ // https://www.alanzucconi.com/2018/05/02/ik-2d-2/
+ // With modifications by TwistedTwigleg
+ Node3D *target = Object::cast_to<Node3D>(ObjectDB::get_instance(target_node_cache));
+ if (_print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) {
+ return;
+ }
+ Transform3D target_trans = stack->skeleton->world_transform_to_global_pose(target->get_global_transform());
+
+ Transform3D bone_one_trans;
+ Transform3D bone_two_trans;
+
+ // Make the first joint look at the pole, and the second look at the target. That way, the
+ // TwoBoneIK solver has to really only handle extension/contraction, which should make it align with the pole.
+ if (use_pole_node) {
+ if (pole_node_cache.is_null()) {
+ _print_execution_error(true, "Pole cache is out of date. Attempting to update...");
+ update_cache_pole();
+ return;
+ }
+
+ Node3D *pole = Object::cast_to<Node3D>(ObjectDB::get_instance(pole_node_cache));
+ if (_print_execution_error(!pole || !pole->is_inside_tree(), "Pole node is not in the scene tree. Cannot execute modification!")) {
+ return;
+ }
+ Transform3D pole_trans = stack->skeleton->world_transform_to_global_pose(pole->get_global_transform());
+
+ bone_one_trans = stack->skeleton->local_pose_to_global_pose(joint_one_bone_idx, stack->skeleton->get_bone_local_pose_override(joint_one_bone_idx));
+ bone_one_trans = bone_one_trans.looking_at(pole_trans.origin, Vector3(0, 1, 0));
+ bone_one_trans.basis = stack->skeleton->global_pose_z_forward_to_bone_forward(joint_one_bone_idx, bone_one_trans.basis);
+ stack->skeleton->update_bone_rest_forward_vector(joint_one_bone_idx);
+ bone_one_trans.basis.rotate_local(stack->skeleton->get_bone_axis_forward_vector(joint_one_bone_idx), joint_one_roll);
+ stack->skeleton->set_bone_local_pose_override(joint_one_bone_idx, stack->skeleton->global_pose_to_local_pose(joint_one_bone_idx, bone_one_trans), stack->strength, true);
+ stack->skeleton->force_update_bone_children_transforms(joint_one_bone_idx);
+
+ bone_two_trans = stack->skeleton->local_pose_to_global_pose(joint_two_bone_idx, stack->skeleton->get_bone_local_pose_override(joint_two_bone_idx));
+ bone_two_trans = bone_two_trans.looking_at(target_trans.origin, Vector3(0, 1, 0));
+ bone_two_trans.basis = stack->skeleton->global_pose_z_forward_to_bone_forward(joint_two_bone_idx, bone_two_trans.basis);
+ stack->skeleton->update_bone_rest_forward_vector(joint_two_bone_idx);
+ bone_two_trans.basis.rotate_local(stack->skeleton->get_bone_axis_forward_vector(joint_two_bone_idx), joint_two_roll);
+ stack->skeleton->set_bone_local_pose_override(joint_two_bone_idx, stack->skeleton->global_pose_to_local_pose(joint_two_bone_idx, bone_two_trans), stack->strength, true);
+ stack->skeleton->force_update_bone_children_transforms(joint_two_bone_idx);
+ } else {
+ bone_one_trans = stack->skeleton->local_pose_to_global_pose(joint_one_bone_idx, stack->skeleton->get_bone_local_pose_override(joint_one_bone_idx));
+ bone_two_trans = stack->skeleton->local_pose_to_global_pose(joint_two_bone_idx, stack->skeleton->get_bone_local_pose_override(joint_two_bone_idx));
+ }
+
+ Transform3D bone_two_tip_trans;
+ if (use_tip_node) {
+ if (tip_node_cache.is_null()) {
+ _print_execution_error(true, "Tip cache is out of date. Attempting to update...");
+ update_cache_tip();
+ return;
+ }
+ Node3D *tip = Object::cast_to<Node3D>(ObjectDB::get_instance(tip_node_cache));
+ if (_print_execution_error(!tip || !tip->is_inside_tree(), "Tip node is not in the scene tree. Cannot execute modification!")) {
+ return;
+ }
+ bone_two_tip_trans = stack->skeleton->world_transform_to_global_pose(tip->get_global_transform());
+ } else {
+ stack->skeleton->update_bone_rest_forward_vector(joint_two_bone_idx);
+ bone_two_tip_trans = bone_two_trans;
+ bone_two_tip_trans.origin += bone_two_trans.basis.xform(stack->skeleton->get_bone_axis_forward_vector(joint_two_bone_idx)).normalized() * joint_two_length;
+ }
+
+ real_t joint_one_to_target_length = bone_one_trans.origin.distance_to(target_trans.origin);
+ if (joint_one_length + joint_two_length < joint_one_to_target_length) {
+ // Set the target *just* out of reach to straighten the bones
+ joint_one_to_target_length = joint_one_length + joint_two_length + 0.01;
+ } else if (joint_one_to_target_length < joint_one_length) {
+ // Place the target in reach so the solver doesn't do crazy things
+ joint_one_to_target_length = joint_one_length;
+ }
+
+ // Get the square lengths for all three sides of the triangle we'll use to calculate the angles
+ real_t sqr_one_length = joint_one_length * joint_one_length;
+ real_t sqr_two_length = joint_two_length * joint_two_length;
+ real_t sqr_three_length = joint_one_to_target_length * joint_one_to_target_length;
+
+ // Calculate the angles for the first joint using the law of cosigns
+ real_t ac_ab_0 = Math::acos(CLAMP(bone_two_tip_trans.origin.direction_to(bone_one_trans.origin).dot(bone_two_trans.origin.direction_to(bone_one_trans.origin)), -1, 1));
+ real_t ac_at_0 = Math::acos(CLAMP(bone_one_trans.origin.direction_to(bone_two_tip_trans.origin).dot(bone_one_trans.origin.direction_to(target_trans.origin)), -1, 1));
+ real_t ac_ab_1 = Math::acos(CLAMP((sqr_two_length - sqr_one_length - sqr_three_length) / (-2.0 * joint_one_length * joint_one_to_target_length), -1, 1));
+
+ // Calculate the angles of rotation. Angle 0 is the extension/contraction axis, while angle 1 is the rotation axis to align the triangle to the target
+ Vector3 axis_0 = bone_one_trans.origin.direction_to(bone_two_tip_trans.origin).cross(bone_one_trans.origin.direction_to(bone_two_trans.origin));
+ Vector3 axis_1 = bone_one_trans.origin.direction_to(bone_two_tip_trans.origin).cross(bone_one_trans.origin.direction_to(target_trans.origin));
+
+ // Make a quaternion with the delta rotation needed to rotate the first joint into alignment and apply it to the transform.
+ Quaternion bone_one_quat = bone_one_trans.basis.get_rotation_quaternion();
+ Quaternion rot_0 = Quaternion(bone_one_quat.inverse().xform(axis_0).normalized(), (ac_ab_1 - ac_ab_0));
+ Quaternion rot_2 = Quaternion(bone_one_quat.inverse().xform(axis_1).normalized(), ac_at_0);
+ bone_one_trans.basis.set_quaternion(bone_one_quat * (rot_0 * rot_2));
+
+ stack->skeleton->update_bone_rest_forward_vector(joint_one_bone_idx);
+ bone_one_trans.basis.rotate_local(stack->skeleton->get_bone_axis_forward_vector(joint_one_bone_idx), joint_one_roll);
+
+ // Apply the rotation to the first joint
+ bone_one_trans = stack->skeleton->global_pose_to_local_pose(joint_one_bone_idx, bone_one_trans);
+ bone_one_trans.origin = Vector3(0, 0, 0);
+ stack->skeleton->set_bone_local_pose_override(joint_one_bone_idx, bone_one_trans, stack->strength, true);
+ stack->skeleton->force_update_bone_children_transforms(joint_one_bone_idx);
+
+ if (use_pole_node) {
+ // Update bone_two_trans so its at the latest position, with the rotation of bone_one_trans taken into account, then look at the target.
+ bone_two_trans = stack->skeleton->local_pose_to_global_pose(joint_two_bone_idx, stack->skeleton->get_bone_local_pose_override(joint_two_bone_idx));
+ stack->skeleton->update_bone_rest_forward_vector(joint_two_bone_idx);
+ Vector3 forward_vector = stack->skeleton->get_bone_axis_forward_vector(joint_two_bone_idx);
+ bone_two_trans.basis.rotate_to_align(forward_vector, bone_two_trans.origin.direction_to(target_trans.origin));
+
+ stack->skeleton->update_bone_rest_forward_vector(joint_two_bone_idx);
+ bone_two_trans.basis.rotate_local(stack->skeleton->get_bone_axis_forward_vector(joint_two_bone_idx), joint_two_roll);
+
+ bone_two_trans = stack->skeleton->global_pose_to_local_pose(joint_two_bone_idx, bone_two_trans);
+ stack->skeleton->set_bone_local_pose_override(joint_two_bone_idx, bone_two_trans, stack->strength, true);
+ stack->skeleton->force_update_bone_children_transforms(joint_two_bone_idx);
+ } else {
+ // Calculate the angles for the second joint using the law of cosigns, make a quaternion with the delta rotation needed to rotate the joint into
+ // alignment, and then apply it to the second joint.
+ real_t ba_bc_0 = Math::acos(CLAMP(bone_two_trans.origin.direction_to(bone_one_trans.origin).dot(bone_two_trans.origin.direction_to(bone_two_tip_trans.origin)), -1, 1));
+ real_t ba_bc_1 = Math::acos(CLAMP((sqr_three_length - sqr_one_length - sqr_two_length) / (-2.0 * joint_one_length * joint_two_length), -1, 1));
+ Quaternion bone_two_quat = bone_two_trans.basis.get_rotation_quaternion();
+ Quaternion rot_1 = Quaternion(bone_two_quat.inverse().xform(axis_0).normalized(), (ba_bc_1 - ba_bc_0));
+ bone_two_trans.basis.set_quaternion(bone_two_quat * rot_1);
+
+ stack->skeleton->update_bone_rest_forward_vector(joint_two_bone_idx);
+ bone_two_trans.basis.rotate_local(stack->skeleton->get_bone_axis_forward_vector(joint_two_bone_idx), joint_two_roll);
+
+ bone_two_trans = stack->skeleton->global_pose_to_local_pose(joint_two_bone_idx, bone_two_trans);
+ bone_two_trans.origin = Vector3(0, 0, 0);
+ stack->skeleton->set_bone_local_pose_override(joint_two_bone_idx, bone_two_trans, stack->strength, true);
+ stack->skeleton->force_update_bone_children_transforms(joint_two_bone_idx);
+ }
+}
+
+void SkeletonModification3DTwoBoneIK::_setup_modification(SkeletonModificationStack3D *p_stack) {
+ stack = p_stack;
+
+ if (stack != nullptr) {
+ is_setup = true;
+ execution_error_found = false;
+ update_cache_target();
+ update_cache_tip();
+ }
+}
+
+void SkeletonModification3DTwoBoneIK::update_cache_target() {
+ if (!is_setup || !stack) {
+ _print_execution_error(true, "Cannot update target cache: modification is not properly setup!");
+ return;
+ }
+
+ target_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree() && target_node.is_empty() == false) {
+ if (stack->skeleton->has_node(target_node)) {
+ Node *node = stack->skeleton->get_node(target_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update target cache: Target node is this modification's skeleton or cannot be found. Cannot execute modification");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update target cache: Target node is not in the scene tree. Cannot execute modification!");
+ target_node_cache = node->get_instance_id();
+
+ execution_error_found = false;
+ }
+ }
+ }
+}
+
+void SkeletonModification3DTwoBoneIK::update_cache_tip() {
+ if (!is_setup || !stack) {
+ _print_execution_error(true, "Cannot update tip cache: modification is not properly setup!");
+ return;
+ }
+
+ tip_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree()) {
+ if (stack->skeleton->has_node(tip_node)) {
+ Node *node = stack->skeleton->get_node(tip_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update tip cache: Tip node is this modification's skeleton or cannot be found!");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update tip cache: Tip node is not in the scene tree. Cannot execute modification!");
+ tip_node_cache = node->get_instance_id();
+
+ execution_error_found = false;
+ }
+ }
+ }
+}
+
+void SkeletonModification3DTwoBoneIK::update_cache_pole() {
+ if (!is_setup || !stack) {
+ _print_execution_error(true, "Cannot update pole cache: modification is not properly setup!");
+ return;
+ }
+
+ pole_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree()) {
+ if (stack->skeleton->has_node(pole_node)) {
+ Node *node = stack->skeleton->get_node(pole_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update pole cache: Pole node is this modification's skeleton or cannot be found!");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update pole cache: Pole node is not in the scene tree. Cannot execute modification!");
+ pole_node_cache = node->get_instance_id();
+
+ execution_error_found = false;
+ }
+ }
+ }
+}
+
+void SkeletonModification3DTwoBoneIK::set_target_node(const NodePath &p_target_node) {
+ target_node = p_target_node;
+ update_cache_target();
+}
+
+NodePath SkeletonModification3DTwoBoneIK::get_target_node() const {
+ return target_node;
+}
+
+void SkeletonModification3DTwoBoneIK::set_use_tip_node(const bool p_use_tip_node) {
+ use_tip_node = p_use_tip_node;
+ notify_property_list_changed();
+}
+
+bool SkeletonModification3DTwoBoneIK::get_use_tip_node() const {
+ return use_tip_node;
+}
+
+void SkeletonModification3DTwoBoneIK::set_tip_node(const NodePath &p_tip_node) {
+ tip_node = p_tip_node;
+ update_cache_tip();
+}
+
+NodePath SkeletonModification3DTwoBoneIK::get_tip_node() const {
+ return tip_node;
+}
+
+void SkeletonModification3DTwoBoneIK::set_use_pole_node(const bool p_use_pole_node) {
+ use_pole_node = p_use_pole_node;
+ notify_property_list_changed();
+}
+
+bool SkeletonModification3DTwoBoneIK::get_use_pole_node() const {
+ return use_pole_node;
+}
+
+void SkeletonModification3DTwoBoneIK::set_pole_node(const NodePath &p_pole_node) {
+ pole_node = p_pole_node;
+ update_cache_pole();
+}
+
+NodePath SkeletonModification3DTwoBoneIK::get_pole_node() const {
+ return pole_node;
+}
+
+void SkeletonModification3DTwoBoneIK::set_auto_calculate_joint_length(bool p_calculate) {
+ auto_calculate_joint_length = p_calculate;
+ if (p_calculate) {
+ calculate_joint_lengths();
+ }
+ notify_property_list_changed();
+}
+
+bool SkeletonModification3DTwoBoneIK::get_auto_calculate_joint_length() const {
+ return auto_calculate_joint_length;
+}
+
+void SkeletonModification3DTwoBoneIK::calculate_joint_lengths() {
+ if (!is_setup) {
+ return; // fail silently, as we likely just loaded the scene.
+ }
+ ERR_FAIL_COND_MSG(!stack || stack->skeleton == nullptr,
+ "Modification is not setup and therefore cannot calculate joint lengths!");
+ ERR_FAIL_COND_MSG(joint_one_bone_idx <= -1 || joint_two_bone_idx <= -1,
+ "One of the bones in the TwoBoneIK modification are not set! Cannot calculate joint lengths!");
+
+ Transform3D bone_one_rest_trans = stack->skeleton->get_bone_global_pose(joint_one_bone_idx);
+ Transform3D bone_two_rest_trans = stack->skeleton->get_bone_global_pose(joint_two_bone_idx);
+
+ joint_one_length = bone_one_rest_trans.origin.distance_to(bone_two_rest_trans.origin);
+
+ if (use_tip_node) {
+ if (tip_node_cache.is_null()) {
+ update_cache_tip();
+ WARN_PRINT("Tip cache is out of date. Updating...");
+ }
+
+ Node3D *tip = Object::cast_to<Node3D>(ObjectDB::get_instance(tip_node_cache));
+ if (tip) {
+ Transform3D bone_tip_trans = stack->skeleton->world_transform_to_global_pose(tip->get_global_transform());
+ joint_two_length = bone_two_rest_trans.origin.distance_to(bone_tip_trans.origin);
+ }
+ } else {
+ // Attempt to use children bones to get the length
+ Vector<int> bone_two_children = stack->skeleton->get_bone_children(joint_two_bone_idx);
+ if (bone_two_children.size() > 0) {
+ joint_two_length = 0;
+ for (int i = 0; i < bone_two_children.size(); i++) {
+ joint_two_length += bone_two_rest_trans.origin.distance_to(
+ stack->skeleton->local_pose_to_global_pose(bone_two_children[i], stack->skeleton->get_bone_rest(bone_two_children[i])).origin);
+ }
+ joint_two_length = joint_two_length / bone_two_children.size();
+ } else {
+ WARN_PRINT("TwoBoneIK modification: Cannot auto calculate length for joint 2! Auto setting the length to 1...");
+ joint_two_length = 1.0;
+ }
+ }
+ execution_error_found = false;
+}
+
+void SkeletonModification3DTwoBoneIK::set_joint_one_bone_name(String p_bone_name) {
+ joint_one_bone_name = p_bone_name;
+ if (stack && stack->skeleton) {
+ joint_one_bone_idx = stack->skeleton->find_bone(p_bone_name);
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+String SkeletonModification3DTwoBoneIK::get_joint_one_bone_name() const {
+ return joint_one_bone_name;
+}
+
+void SkeletonModification3DTwoBoneIK::set_joint_one_bone_idx(int p_bone_idx) {
+ joint_one_bone_idx = p_bone_idx;
+ if (stack && stack->skeleton) {
+ joint_one_bone_name = stack->skeleton->get_bone_name(p_bone_idx);
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+int SkeletonModification3DTwoBoneIK::get_joint_one_bone_idx() const {
+ return joint_one_bone_idx;
+}
+
+void SkeletonModification3DTwoBoneIK::set_joint_one_length(real_t p_length) {
+ joint_one_length = p_length;
+}
+
+real_t SkeletonModification3DTwoBoneIK::get_joint_one_length() const {
+ return joint_one_length;
+}
+
+void SkeletonModification3DTwoBoneIK::set_joint_two_bone_name(String p_bone_name) {
+ joint_two_bone_name = p_bone_name;
+ if (stack && stack->skeleton) {
+ joint_two_bone_idx = stack->skeleton->find_bone(p_bone_name);
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+String SkeletonModification3DTwoBoneIK::get_joint_two_bone_name() const {
+ return joint_two_bone_name;
+}
+
+void SkeletonModification3DTwoBoneIK::set_joint_two_bone_idx(int p_bone_idx) {
+ joint_two_bone_idx = p_bone_idx;
+ if (stack && stack->skeleton) {
+ joint_two_bone_name = stack->skeleton->get_bone_name(p_bone_idx);
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+int SkeletonModification3DTwoBoneIK::get_joint_two_bone_idx() const {
+ return joint_two_bone_idx;
+}
+
+void SkeletonModification3DTwoBoneIK::set_joint_two_length(real_t p_length) {
+ joint_two_length = p_length;
+}
+
+real_t SkeletonModification3DTwoBoneIK::get_joint_two_length() const {
+ return joint_two_length;
+}
+
+void SkeletonModification3DTwoBoneIK::set_joint_one_roll(real_t p_roll) {
+ joint_one_roll = p_roll;
+}
+
+real_t SkeletonModification3DTwoBoneIK::get_joint_one_roll() const {
+ return joint_one_roll;
+}
+
+void SkeletonModification3DTwoBoneIK::set_joint_two_roll(real_t p_roll) {
+ joint_two_roll = p_roll;
+}
+
+real_t SkeletonModification3DTwoBoneIK::get_joint_two_roll() const {
+ return joint_two_roll;
+}
+
+void SkeletonModification3DTwoBoneIK::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification3DTwoBoneIK::set_target_node);
+ ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification3DTwoBoneIK::get_target_node);
+
+ ClassDB::bind_method(D_METHOD("set_use_pole_node", "use_pole_node"), &SkeletonModification3DTwoBoneIK::set_use_pole_node);
+ ClassDB::bind_method(D_METHOD("get_use_pole_node"), &SkeletonModification3DTwoBoneIK::get_use_pole_node);
+ ClassDB::bind_method(D_METHOD("set_pole_node", "pole_nodepath"), &SkeletonModification3DTwoBoneIK::set_pole_node);
+ ClassDB::bind_method(D_METHOD("get_pole_node"), &SkeletonModification3DTwoBoneIK::get_pole_node);
+
+ ClassDB::bind_method(D_METHOD("set_use_tip_node", "use_tip_node"), &SkeletonModification3DTwoBoneIK::set_use_tip_node);
+ ClassDB::bind_method(D_METHOD("get_use_tip_node"), &SkeletonModification3DTwoBoneIK::get_use_tip_node);
+ ClassDB::bind_method(D_METHOD("set_tip_node", "tip_nodepath"), &SkeletonModification3DTwoBoneIK::set_tip_node);
+ ClassDB::bind_method(D_METHOD("get_tip_node"), &SkeletonModification3DTwoBoneIK::get_tip_node);
+
+ ClassDB::bind_method(D_METHOD("set_auto_calculate_joint_length", "auto_calculate_joint_length"), &SkeletonModification3DTwoBoneIK::set_auto_calculate_joint_length);
+ ClassDB::bind_method(D_METHOD("get_auto_calculate_joint_length"), &SkeletonModification3DTwoBoneIK::get_auto_calculate_joint_length);
+
+ ClassDB::bind_method(D_METHOD("set_joint_one_bone_name", "bone_name"), &SkeletonModification3DTwoBoneIK::set_joint_one_bone_name);
+ ClassDB::bind_method(D_METHOD("get_joint_one_bone_name"), &SkeletonModification3DTwoBoneIK::get_joint_one_bone_name);
+ ClassDB::bind_method(D_METHOD("set_joint_one_bone_idx", "bone_idx"), &SkeletonModification3DTwoBoneIK::set_joint_one_bone_idx);
+ ClassDB::bind_method(D_METHOD("get_joint_one_bone_idx"), &SkeletonModification3DTwoBoneIK::get_joint_one_bone_idx);
+ ClassDB::bind_method(D_METHOD("set_joint_one_length", "bone_length"), &SkeletonModification3DTwoBoneIK::set_joint_one_length);
+ ClassDB::bind_method(D_METHOD("get_joint_one_length"), &SkeletonModification3DTwoBoneIK::get_joint_one_length);
+
+ ClassDB::bind_method(D_METHOD("set_joint_two_bone_name", "bone_name"), &SkeletonModification3DTwoBoneIK::set_joint_two_bone_name);
+ ClassDB::bind_method(D_METHOD("get_joint_two_bone_name"), &SkeletonModification3DTwoBoneIK::get_joint_two_bone_name);
+ ClassDB::bind_method(D_METHOD("set_joint_two_bone_idx", "bone_idx"), &SkeletonModification3DTwoBoneIK::set_joint_two_bone_idx);
+ ClassDB::bind_method(D_METHOD("get_joint_two_bone_idx"), &SkeletonModification3DTwoBoneIK::get_joint_two_bone_idx);
+ ClassDB::bind_method(D_METHOD("set_joint_two_length", "bone_length"), &SkeletonModification3DTwoBoneIK::set_joint_two_length);
+ ClassDB::bind_method(D_METHOD("get_joint_two_length"), &SkeletonModification3DTwoBoneIK::get_joint_two_length);
+
+ ClassDB::bind_method(D_METHOD("set_joint_one_roll", "roll"), &SkeletonModification3DTwoBoneIK::set_joint_one_roll);
+ ClassDB::bind_method(D_METHOD("get_joint_one_roll"), &SkeletonModification3DTwoBoneIK::get_joint_one_roll);
+ ClassDB::bind_method(D_METHOD("set_joint_two_roll", "roll"), &SkeletonModification3DTwoBoneIK::set_joint_two_roll);
+ ClassDB::bind_method(D_METHOD("get_joint_two_roll"), &SkeletonModification3DTwoBoneIK::get_joint_two_roll);
+
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D"), "set_target_node", "get_target_node");
+ ADD_GROUP("", "");
+}
+
+SkeletonModification3DTwoBoneIK::SkeletonModification3DTwoBoneIK() {
+ stack = nullptr;
+ is_setup = false;
+}
+
+SkeletonModification3DTwoBoneIK::~SkeletonModification3DTwoBoneIK() {
+}
diff --git a/scene/resources/skeleton_modification_3d_twoboneik.h b/scene/resources/skeleton_modification_3d_twoboneik.h
new file mode 100644
index 0000000000..e62d6cc497
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_twoboneik.h
@@ -0,0 +1,118 @@
+/*************************************************************************/
+/* skeleton_modification_3d_twoboneik.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+#ifndef SKELETONMODIFICATION3DTWOBONEIK_H
+#define SKELETONMODIFICATION3DTWOBONEIK_H
+
+class SkeletonModification3DTwoBoneIK : public SkeletonModification3D {
+ GDCLASS(SkeletonModification3DTwoBoneIK, SkeletonModification3D);
+
+private:
+ NodePath target_node;
+ ObjectID target_node_cache;
+
+ bool use_tip_node = false;
+ NodePath tip_node;
+ ObjectID tip_node_cache;
+
+ bool use_pole_node = false;
+ NodePath pole_node;
+ ObjectID pole_node_cache;
+
+ String joint_one_bone_name = "";
+ int joint_one_bone_idx = -1;
+ String joint_two_bone_name = "";
+ int joint_two_bone_idx = -1;
+
+ bool auto_calculate_joint_length = false;
+ real_t joint_one_length = -1;
+ real_t joint_two_length = -1;
+
+ real_t joint_one_roll = 0;
+ real_t joint_two_roll = 0;
+
+ void update_cache_target();
+ void update_cache_tip();
+ void update_cache_pole();
+
+protected:
+ static void _bind_methods();
+ bool _get(const StringName &p_path, Variant &r_ret) const;
+ bool _set(const StringName &p_path, const Variant &p_value);
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+public:
+ virtual void _execute(real_t p_delta) override;
+ virtual void _setup_modification(SkeletonModificationStack3D *p_stack) override;
+
+ void set_target_node(const NodePath &p_target_node);
+ NodePath get_target_node() const;
+
+ void set_use_tip_node(const bool p_use_tip_node);
+ bool get_use_tip_node() const;
+ void set_tip_node(const NodePath &p_tip_node);
+ NodePath get_tip_node() const;
+
+ void set_use_pole_node(const bool p_use_pole_node);
+ bool get_use_pole_node() const;
+ void set_pole_node(const NodePath &p_pole_node);
+ NodePath get_pole_node() const;
+
+ void set_auto_calculate_joint_length(bool p_calculate);
+ bool get_auto_calculate_joint_length() const;
+ void calculate_joint_lengths();
+
+ void set_joint_one_bone_name(String p_bone_name);
+ String get_joint_one_bone_name() const;
+ void set_joint_one_bone_idx(int p_bone_idx);
+ int get_joint_one_bone_idx() const;
+ void set_joint_one_length(real_t p_length);
+ real_t get_joint_one_length() const;
+
+ void set_joint_two_bone_name(String p_bone_name);
+ String get_joint_two_bone_name() const;
+ void set_joint_two_bone_idx(int p_bone_idx);
+ int get_joint_two_bone_idx() const;
+ void set_joint_two_length(real_t p_length);
+ real_t get_joint_two_length() const;
+
+ void set_joint_one_roll(real_t p_roll);
+ real_t get_joint_one_roll() const;
+ void set_joint_two_roll(real_t p_roll);
+ real_t get_joint_two_roll() const;
+
+ SkeletonModification3DTwoBoneIK();
+ ~SkeletonModification3DTwoBoneIK();
+};
+
+#endif //SKELETONMODIFICATION3DTWOBONEIK_H
diff --git a/scene/resources/skeleton_modification_stack_3d.cpp b/scene/resources/skeleton_modification_stack_3d.cpp
new file mode 100644
index 0000000000..3fce0e5dbd
--- /dev/null
+++ b/scene/resources/skeleton_modification_stack_3d.cpp
@@ -0,0 +1,222 @@
+/*************************************************************************/
+/* skeleton_modification_stack_3d.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 "skeleton_modification_stack_3d.h"
+#include "scene/3d/skeleton_3d.h"
+
+///////////////////////////////////////
+// ModificationStack3D
+///////////////////////////////////////
+
+void SkeletonModificationStack3D::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (uint32_t i = 0; i < modifications.size(); i++) {
+ p_list->push_back(
+ PropertyInfo(Variant::OBJECT, "modifications/" + itos(i),
+ PROPERTY_HINT_RESOURCE_TYPE,
+ "SkeletonModification3D",
+ PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
+ }
+}
+
+bool SkeletonModificationStack3D::_set(const StringName &p_path, const Variant &p_value) {
+ String path = p_path;
+
+ if (path.begins_with("modifications/")) {
+ int mod_idx = path.get_slicec('/', 1).to_int();
+ set_modification(mod_idx, p_value);
+ return true;
+ }
+ return true;
+}
+
+bool SkeletonModificationStack3D::_get(const StringName &p_path, Variant &r_ret) const {
+ String path = p_path;
+
+ if (path.begins_with("modifications/")) {
+ int mod_idx = path.get_slicec('/', 1).to_int();
+ r_ret = get_modification(mod_idx);
+ return true;
+ }
+ return true;
+}
+
+void SkeletonModificationStack3D::setup() {
+ if (is_setup) {
+ return;
+ }
+
+ if (skeleton != nullptr) {
+ is_setup = true;
+ for (uint32_t i = 0; i < modifications.size(); i++) {
+ if (!modifications[i].is_valid()) {
+ continue;
+ }
+ modifications[i]->_setup_modification(this);
+ }
+ } else {
+ WARN_PRINT("Cannot setup SkeletonModificationStack3D: no skeleton set!");
+ }
+}
+
+void SkeletonModificationStack3D::execute(real_t p_delta, int p_execution_mode) {
+ ERR_FAIL_COND_MSG(!is_setup || skeleton == nullptr || is_queued_for_deletion(),
+ "Modification stack is not properly setup and therefore cannot execute!");
+
+ if (!skeleton->is_inside_tree()) {
+ ERR_PRINT_ONCE("Skeleton is not inside SceneTree! Cannot execute modification!");
+ return;
+ }
+
+ if (!enabled) {
+ return;
+ }
+
+ for (uint32_t i = 0; i < modifications.size(); i++) {
+ if (!modifications[i].is_valid()) {
+ continue;
+ }
+
+ if (modifications[i]->get_execution_mode() == p_execution_mode) {
+ modifications[i]->_execute(p_delta);
+ }
+ }
+}
+
+void SkeletonModificationStack3D::enable_all_modifications(bool p_enabled) {
+ for (uint32_t i = 0; i < modifications.size(); i++) {
+ if (!modifications[i].is_valid()) {
+ continue;
+ }
+ modifications[i]->set_enabled(p_enabled);
+ }
+}
+
+Ref<SkeletonModification3D> SkeletonModificationStack3D::get_modification(int p_mod_idx) const {
+ const int modifications_size = modifications.size();
+ ERR_FAIL_INDEX_V(p_mod_idx, modifications_size, nullptr);
+ return modifications[p_mod_idx];
+}
+
+void SkeletonModificationStack3D::add_modification(Ref<SkeletonModification3D> p_mod) {
+ p_mod->_setup_modification(this);
+ modifications.push_back(p_mod);
+}
+
+void SkeletonModificationStack3D::delete_modification(int p_mod_idx) {
+ const int modifications_size = modifications.size();
+ ERR_FAIL_INDEX(p_mod_idx, modifications_size);
+ modifications.remove(p_mod_idx);
+}
+
+void SkeletonModificationStack3D::set_modification(int p_mod_idx, Ref<SkeletonModification3D> p_mod) {
+ const int modifications_size = modifications.size();
+ ERR_FAIL_INDEX(p_mod_idx, modifications_size);
+
+ if (p_mod == nullptr) {
+ modifications.remove(p_mod_idx);
+ } else {
+ p_mod->_setup_modification(this);
+ modifications[p_mod_idx] = p_mod;
+ }
+}
+
+void SkeletonModificationStack3D::set_modification_count(int p_count) {
+ modifications.resize(p_count);
+ notify_property_list_changed();
+}
+
+int SkeletonModificationStack3D::get_modification_count() const {
+ return modifications.size();
+}
+
+void SkeletonModificationStack3D::set_skeleton(Skeleton3D *p_skeleton) {
+ skeleton = p_skeleton;
+}
+
+Skeleton3D *SkeletonModificationStack3D::get_skeleton() const {
+ return skeleton;
+}
+
+bool SkeletonModificationStack3D::get_is_setup() const {
+ return is_setup;
+}
+
+void SkeletonModificationStack3D::set_enabled(bool p_enabled) {
+ enabled = p_enabled;
+
+ if (!enabled && is_setup && skeleton != nullptr) {
+ skeleton->clear_bones_local_pose_override();
+ }
+}
+
+bool SkeletonModificationStack3D::get_enabled() const {
+ return enabled;
+}
+
+void SkeletonModificationStack3D::set_strength(real_t p_strength) {
+ ERR_FAIL_COND_MSG(p_strength < 0, "Strength cannot be less than zero!");
+ ERR_FAIL_COND_MSG(p_strength > 1, "Strength cannot be more than one!");
+ strength = p_strength;
+}
+
+real_t SkeletonModificationStack3D::get_strength() const {
+ return strength;
+}
+
+void SkeletonModificationStack3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("setup"), &SkeletonModificationStack3D::setup);
+ ClassDB::bind_method(D_METHOD("execute", "delta", "execution_mode"), &SkeletonModificationStack3D::execute);
+
+ ClassDB::bind_method(D_METHOD("enable_all_modifications", "enabled"), &SkeletonModificationStack3D::enable_all_modifications);
+ ClassDB::bind_method(D_METHOD("get_modification", "mod_idx"), &SkeletonModificationStack3D::get_modification);
+ ClassDB::bind_method(D_METHOD("add_modification", "modification"), &SkeletonModificationStack3D::add_modification);
+ ClassDB::bind_method(D_METHOD("delete_modification", "mod_idx"), &SkeletonModificationStack3D::delete_modification);
+ ClassDB::bind_method(D_METHOD("set_modification", "mod_idx", "modification"), &SkeletonModificationStack3D::set_modification);
+
+ ClassDB::bind_method(D_METHOD("set_modification_count"), &SkeletonModificationStack3D::set_modification_count);
+ ClassDB::bind_method(D_METHOD("get_modification_count"), &SkeletonModificationStack3D::get_modification_count);
+
+ ClassDB::bind_method(D_METHOD("get_is_setup"), &SkeletonModificationStack3D::get_is_setup);
+
+ ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &SkeletonModificationStack3D::set_enabled);
+ ClassDB::bind_method(D_METHOD("get_enabled"), &SkeletonModificationStack3D::get_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_strength", "strength"), &SkeletonModificationStack3D::set_strength);
+ ClassDB::bind_method(D_METHOD("get_strength"), &SkeletonModificationStack3D::get_strength);
+
+ ClassDB::bind_method(D_METHOD("get_skeleton"), &SkeletonModificationStack3D::get_skeleton);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "get_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "strength", PROPERTY_HINT_RANGE, "0, 1, 0.001"), "set_strength", "get_strength");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "modification_count", PROPERTY_HINT_RANGE, "0, 100, 1"), "set_modification_count", "get_modification_count");
+}
+
+SkeletonModificationStack3D::SkeletonModificationStack3D() {
+}
diff --git a/scene/resources/skeleton_modification_stack_3d.h b/scene/resources/skeleton_modification_stack_3d.h
new file mode 100644
index 0000000000..cbc8d4e0b9
--- /dev/null
+++ b/scene/resources/skeleton_modification_stack_3d.h
@@ -0,0 +1,91 @@
+/*************************************************************************/
+/* skeleton_modification_stack_3d.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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. */
+/*************************************************************************/
+
+#ifndef SKELETONMODIFICATIONSTACK3D_H
+#define SKELETONMODIFICATIONSTACK3D_H
+
+#include "core/templates/local_vector.h"
+#include "scene/3d/skeleton_3d.h"
+
+class Skeleton3D;
+class SkeletonModification3D;
+
+class SkeletonModificationStack3D : public Resource {
+ GDCLASS(SkeletonModificationStack3D, Resource);
+ friend class Skeleton3D;
+ friend class SkeletonModification3D;
+
+protected:
+ static void _bind_methods();
+ virtual void _get_property_list(List<PropertyInfo> *p_list) const;
+ virtual bool _set(const StringName &p_path, const Variant &p_value);
+ virtual bool _get(const StringName &p_path, Variant &r_ret) const;
+
+public:
+ Skeleton3D *skeleton = nullptr;
+ bool is_setup = false;
+ bool enabled = false;
+ real_t strength = 1.0;
+
+ enum EXECUTION_MODE {
+ execution_mode_process,
+ execution_mode_physics_process,
+ };
+
+ LocalVector<Ref<SkeletonModification3D>> modifications = LocalVector<Ref<SkeletonModification3D>>();
+ int modifications_count = 0;
+
+ virtual void setup();
+ virtual void execute(real_t p_delta, int p_execution_mode);
+
+ void enable_all_modifications(bool p_enable);
+ Ref<SkeletonModification3D> get_modification(int p_mod_idx) const;
+ void add_modification(Ref<SkeletonModification3D> p_mod);
+ void delete_modification(int p_mod_idx);
+ void set_modification(int p_mod_idx, Ref<SkeletonModification3D> p_mod);
+
+ void set_modification_count(int p_count);
+ int get_modification_count() const;
+
+ void set_skeleton(Skeleton3D *p_skeleton);
+ Skeleton3D *get_skeleton() const;
+
+ bool get_is_setup() const;
+
+ void set_enabled(bool p_enabled);
+ bool get_enabled() const;
+
+ void set_strength(real_t p_strength);
+ real_t get_strength() const;
+
+ SkeletonModificationStack3D();
+};
+
+#endif // SKELETONMODIFICATIONSTACK3D_H
diff --git a/scene/resources/sky_material.cpp b/scene/resources/sky_material.cpp
index ec00f9d7b7..39082b6f7a 100644
--- a/scene/resources/sky_material.cpp
+++ b/scene/resources/sky_material.cpp
@@ -30,6 +30,8 @@
#include "sky_material.h"
+#include "core/version.h"
+
Mutex ProceduralSkyMaterial::shader_mutex;
RID ProceduralSkyMaterial::shader;
@@ -204,7 +206,10 @@ void ProceduralSkyMaterial::_update_shader() {
if (shader.is_null()) {
shader = RS::get_singleton()->shader_create();
+ // Add a comment to describe the shader origin (useful when converting to ShaderMaterial).
RS::get_singleton()->shader_set_code(shader, R"(
+// NOTE: Shader automatically converted from )" VERSION_NAME " " VERSION_FULL_CONFIG R"('s ProceduralSkyMaterial.
+
shader_type sky;
uniform vec4 sky_top_color : hint_color = vec4(0.35, 0.46, 0.71, 1.0);
@@ -350,10 +355,13 @@ void PanoramaSkyMaterial::_update_shader() {
if (shader.is_null()) {
shader = RS::get_singleton()->shader_create();
+ // Add a comment to describe the shader origin (useful when converting to ShaderMaterial).
RS::get_singleton()->shader_set_code(shader, R"(
+// NOTE: Shader automatically converted from )" VERSION_NAME " " VERSION_FULL_CONFIG R"('s PanoramaSkyMaterial.
+
shader_type sky;
-uniform sampler2D source_panorama : filter_linear;
+uniform sampler2D source_panorama : filter_linear, hint_albedo;
void sky() {
COLOR = texture(source_panorama, SKY_COORDS).rgb;
@@ -561,7 +569,10 @@ void PhysicalSkyMaterial::_update_shader() {
if (shader.is_null()) {
shader = RS::get_singleton()->shader_create();
+ // Add a comment to describe the shader origin (useful when converting to ShaderMaterial).
RS::get_singleton()->shader_set_code(shader, R"(
+// NOTE: Shader automatically converted from )" VERSION_NAME " " VERSION_FULL_CONFIG R"('s PhysicalSkyMaterial.
+
shader_type sky;
uniform float rayleigh : hint_range(0, 64) = 2.0;
@@ -576,7 +587,7 @@ uniform vec4 ground_color : hint_color = vec4(1.0);
uniform float exposure : hint_range(0, 128) = 0.1;
uniform float dither_strength : hint_range(0, 10) = 1.0;
-uniform sampler2D night_sky : hint_black;
+uniform sampler2D night_sky : hint_black_albedo;
const vec3 UP = vec3( 0.0, 1.0, 0.0 );
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index 75dd7448e7..b00dcca004 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -3032,6 +3032,10 @@ String VisualShaderNodeUniform::get_uniform_name() const {
}
void VisualShaderNodeUniform::set_qualifier(VisualShaderNodeUniform::Qualifier p_qual) {
+ ERR_FAIL_INDEX(int(p_qual), int(QUAL_MAX));
+ if (qualifier == p_qual) {
+ return;
+ }
qualifier = p_qual;
emit_changed();
}
@@ -3061,6 +3065,7 @@ void VisualShaderNodeUniform::_bind_methods() {
BIND_ENUM_CONSTANT(QUAL_NONE);
BIND_ENUM_CONSTANT(QUAL_GLOBAL);
BIND_ENUM_CONSTANT(QUAL_INSTANCE);
+ BIND_ENUM_CONSTANT(QUAL_MAX);
}
String VisualShaderNodeUniform::_get_qual_str() const {
@@ -3072,6 +3077,8 @@ String VisualShaderNodeUniform::_get_qual_str() const {
return "global ";
case QUAL_INSTANCE:
return "instance ";
+ default:
+ break;
}
}
return String();
@@ -3094,6 +3101,8 @@ String VisualShaderNodeUniform::get_warning(Shader::Mode p_mode, VisualShader::T
case QUAL_INSTANCE:
qualifier_str = "instance";
break;
+ default:
+ break;
}
return vformat(TTR("This uniform type does not support the '%s' qualifier."), qualifier_str);
} else if (qualifier == Qualifier::QUAL_GLOBAL) {
@@ -3350,6 +3359,10 @@ bool VisualShaderNodeGroupBase::is_valid_port_name(const String &p_name) const {
}
void VisualShaderNodeGroupBase::add_input_port(int p_id, int p_type, const String &p_name) {
+ ERR_FAIL_COND(has_input_port(p_id));
+ ERR_FAIL_INDEX(p_type, int(PORT_TYPE_MAX));
+ ERR_FAIL_COND(!is_valid_port_name(p_name));
+
String str = itos(p_id) + "," + itos(p_type) + "," + p_name + ";";
Vector<String> inputs_strings = inputs.split(";", false);
int index = 0;
@@ -3422,6 +3435,10 @@ bool VisualShaderNodeGroupBase::has_input_port(int p_id) const {
}
void VisualShaderNodeGroupBase::add_output_port(int p_id, int p_type, const String &p_name) {
+ ERR_FAIL_COND(has_output_port(p_id));
+ ERR_FAIL_INDEX(p_type, int(PORT_TYPE_MAX));
+ ERR_FAIL_COND(!is_valid_port_name(p_name));
+
String str = itos(p_id) + "," + itos(p_type) + "," + p_name + ";";
Vector<String> outputs_strings = outputs.split(";", false);
int index = 0;
@@ -3503,7 +3520,7 @@ void VisualShaderNodeGroupBase::clear_output_ports() {
void VisualShaderNodeGroupBase::set_input_port_type(int p_id, int p_type) {
ERR_FAIL_COND(!has_input_port(p_id));
- ERR_FAIL_COND(p_type < 0 || p_type >= PORT_TYPE_MAX);
+ ERR_FAIL_INDEX(p_type, int(PORT_TYPE_MAX));
if (input_ports[p_id].type == p_type) {
return;
@@ -3575,7 +3592,7 @@ String VisualShaderNodeGroupBase::get_input_port_name(int p_id) const {
void VisualShaderNodeGroupBase::set_output_port_type(int p_id, int p_type) {
ERR_FAIL_COND(!has_output_port(p_id));
- ERR_FAIL_COND(p_type < 0 || p_type >= PORT_TYPE_MAX);
+ ERR_FAIL_INDEX(p_type, int(PORT_TYPE_MAX));
if (output_ports[p_id].type == p_type) {
return;
diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h
index abf55185ab..31651318ae 100644
--- a/scene/resources/visual_shader.h
+++ b/scene/resources/visual_shader.h
@@ -435,6 +435,7 @@ public:
QUAL_NONE,
QUAL_GLOBAL,
QUAL_INSTANCE,
+ QUAL_MAX,
};
private:
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
index a8ec78f63e..e45dfdcb1b 100644
--- a/scene/resources/visual_shader_nodes.cpp
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -69,8 +69,11 @@ String VisualShaderNodeFloatConstant::generate_code(Shader::Mode p_mode, VisualS
return " " + p_output_vars[0] + " = " + vformat("%.6f", constant) + ";\n";
}
-void VisualShaderNodeFloatConstant::set_constant(float p_value) {
- constant = p_value;
+void VisualShaderNodeFloatConstant::set_constant(float p_constant) {
+ if (Math::is_equal_approx(constant, p_constant)) {
+ return;
+ }
+ constant = p_constant;
emit_changed();
}
@@ -85,7 +88,7 @@ Vector<StringName> VisualShaderNodeFloatConstant::get_editable_properties() cons
}
void VisualShaderNodeFloatConstant::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeFloatConstant::set_constant);
+ ClassDB::bind_method(D_METHOD("set_constant", "constant"), &VisualShaderNodeFloatConstant::set_constant);
ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeFloatConstant::get_constant);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "constant"), "set_constant", "get_constant");
@@ -128,8 +131,11 @@ String VisualShaderNodeIntConstant::generate_code(Shader::Mode p_mode, VisualSha
return " " + p_output_vars[0] + " = " + itos(constant) + ";\n";
}
-void VisualShaderNodeIntConstant::set_constant(int p_value) {
- constant = p_value;
+void VisualShaderNodeIntConstant::set_constant(int p_constant) {
+ if (constant == p_constant) {
+ return;
+ }
+ constant = p_constant;
emit_changed();
}
@@ -144,7 +150,7 @@ Vector<StringName> VisualShaderNodeIntConstant::get_editable_properties() const
}
void VisualShaderNodeIntConstant::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeIntConstant::set_constant);
+ ClassDB::bind_method(D_METHOD("set_constant", "constant"), &VisualShaderNodeIntConstant::set_constant);
ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeIntConstant::get_constant);
ADD_PROPERTY(PropertyInfo(Variant::INT, "constant"), "set_constant", "get_constant");
@@ -187,8 +193,11 @@ String VisualShaderNodeBooleanConstant::generate_code(Shader::Mode p_mode, Visua
return " " + p_output_vars[0] + " = " + (constant ? "true" : "false") + ";\n";
}
-void VisualShaderNodeBooleanConstant::set_constant(bool p_value) {
- constant = p_value;
+void VisualShaderNodeBooleanConstant::set_constant(bool p_constant) {
+ if (constant == p_constant) {
+ return;
+ }
+ constant = p_constant;
emit_changed();
}
@@ -203,7 +212,7 @@ Vector<StringName> VisualShaderNodeBooleanConstant::get_editable_properties() co
}
void VisualShaderNodeBooleanConstant::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeBooleanConstant::set_constant);
+ ClassDB::bind_method(D_METHOD("set_constant", "constant"), &VisualShaderNodeBooleanConstant::set_constant);
ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeBooleanConstant::get_constant);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "constant"), "set_constant", "get_constant");
@@ -257,8 +266,11 @@ String VisualShaderNodeColorConstant::generate_code(Shader::Mode p_mode, VisualS
return code;
}
-void VisualShaderNodeColorConstant::set_constant(Color p_value) {
- constant = p_value;
+void VisualShaderNodeColorConstant::set_constant(const Color &p_constant) {
+ if (constant.is_equal_approx(p_constant)) {
+ return;
+ }
+ constant = p_constant;
emit_changed();
}
@@ -273,7 +285,7 @@ Vector<StringName> VisualShaderNodeColorConstant::get_editable_properties() cons
}
void VisualShaderNodeColorConstant::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeColorConstant::set_constant);
+ ClassDB::bind_method(D_METHOD("set_constant", "constant"), &VisualShaderNodeColorConstant::set_constant);
ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeColorConstant::get_constant);
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "constant"), "set_constant", "get_constant");
@@ -316,8 +328,11 @@ String VisualShaderNodeVec3Constant::generate_code(Shader::Mode p_mode, VisualSh
return " " + p_output_vars[0] + " = " + vformat("vec3(%.6f, %.6f, %.6f)", constant.x, constant.y, constant.z) + ";\n";
}
-void VisualShaderNodeVec3Constant::set_constant(Vector3 p_value) {
- constant = p_value;
+void VisualShaderNodeVec3Constant::set_constant(const Vector3 &p_constant) {
+ if (constant.is_equal_approx(p_constant)) {
+ return;
+ }
+ constant = p_constant;
emit_changed();
}
@@ -332,7 +347,7 @@ Vector<StringName> VisualShaderNodeVec3Constant::get_editable_properties() const
}
void VisualShaderNodeVec3Constant::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeVec3Constant::set_constant);
+ ClassDB::bind_method(D_METHOD("set_constant", "constant"), &VisualShaderNodeVec3Constant::set_constant);
ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeVec3Constant::get_constant);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant"), "set_constant", "get_constant");
@@ -383,8 +398,11 @@ String VisualShaderNodeTransformConstant::generate_code(Shader::Mode p_mode, Vis
return code;
}
-void VisualShaderNodeTransformConstant::set_constant(Transform3D p_value) {
- constant = p_value;
+void VisualShaderNodeTransformConstant::set_constant(const Transform3D &p_constant) {
+ if (constant.is_equal_approx(p_constant)) {
+ return;
+ }
+ constant = p_constant;
emit_changed();
}
@@ -399,7 +417,7 @@ Vector<StringName> VisualShaderNodeTransformConstant::get_editable_properties()
}
void VisualShaderNodeTransformConstant::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeTransformConstant::set_constant);
+ ClassDB::bind_method(D_METHOD("set_constant", "constant"), &VisualShaderNodeTransformConstant::set_constant);
ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeTransformConstant::get_constant);
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "constant"), "set_constant", "get_constant");
@@ -502,6 +520,8 @@ String VisualShaderNodeTexture::generate_global(Shader::Mode p_mode, VisualShade
case TYPE_NORMAL_MAP:
u += " : hint_normal";
break;
+ default:
+ break;
}
return u + ";\n";
}
@@ -685,8 +705,11 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader:
}
void VisualShaderNodeTexture::set_source(Source p_source) {
- source = p_source;
- switch (source) {
+ ERR_FAIL_INDEX(int(p_source), int(SOURCE_MAX));
+ if (source == p_source) {
+ return;
+ }
+ switch (p_source) {
case SOURCE_TEXTURE:
simple_decl = true;
break;
@@ -705,7 +728,10 @@ void VisualShaderNodeTexture::set_source(Source p_source) {
case SOURCE_PORT:
simple_decl = false;
break;
+ default:
+ break;
}
+ source = p_source;
emit_changed();
emit_signal(SNAME("editor_refresh_request"));
}
@@ -714,8 +740,8 @@ VisualShaderNodeTexture::Source VisualShaderNodeTexture::get_source() const {
return source;
}
-void VisualShaderNodeTexture::set_texture(Ref<Texture2D> p_value) {
- texture = p_value;
+void VisualShaderNodeTexture::set_texture(Ref<Texture2D> p_texture) {
+ texture = p_texture;
emit_changed();
}
@@ -723,8 +749,12 @@ Ref<Texture2D> VisualShaderNodeTexture::get_texture() const {
return texture;
}
-void VisualShaderNodeTexture::set_texture_type(TextureType p_type) {
- texture_type = p_type;
+void VisualShaderNodeTexture::set_texture_type(TextureType p_texture_type) {
+ ERR_FAIL_INDEX(int(p_texture_type), int(TYPE_MAX));
+ if (texture_type == p_texture_type) {
+ return;
+ }
+ texture_type = p_texture_type;
emit_changed();
}
@@ -797,9 +827,12 @@ void VisualShaderNodeTexture::_bind_methods() {
BIND_ENUM_CONSTANT(SOURCE_2D_NORMAL);
BIND_ENUM_CONSTANT(SOURCE_DEPTH);
BIND_ENUM_CONSTANT(SOURCE_PORT);
+ BIND_ENUM_CONSTANT(SOURCE_MAX);
+
BIND_ENUM_CONSTANT(TYPE_DATA);
BIND_ENUM_CONSTANT(TYPE_COLOR);
BIND_ENUM_CONSTANT(TYPE_NORMAL_MAP);
+ BIND_ENUM_CONSTANT(TYPE_MAX);
}
VisualShaderNodeTexture::VisualShaderNodeTexture() {
@@ -1074,6 +1107,10 @@ String VisualShaderNodeSample3D::generate_code(Shader::Mode p_mode, VisualShader
}
void VisualShaderNodeSample3D::set_source(Source p_source) {
+ ERR_FAIL_INDEX(int(p_source), int(SOURCE_MAX));
+ if (source == p_source) {
+ return;
+ }
source = p_source;
emit_changed();
emit_signal(SNAME("editor_refresh_request"));
@@ -1091,6 +1128,7 @@ void VisualShaderNodeSample3D::_bind_methods() {
BIND_ENUM_CONSTANT(SOURCE_TEXTURE);
BIND_ENUM_CONSTANT(SOURCE_PORT);
+ BIND_ENUM_CONSTANT(SOURCE_MAX);
}
String VisualShaderNodeSample3D::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const {
@@ -1127,7 +1165,7 @@ String VisualShaderNodeTexture2DArray::get_input_port_name(int p_port) const {
Vector<VisualShader::DefaultTextureParam> VisualShaderNodeTexture2DArray::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
VisualShader::DefaultTextureParam dtp;
dtp.name = make_unique_id(p_type, p_id, "tex3d");
- dtp.param = texture;
+ dtp.param = texture_array;
Vector<VisualShader::DefaultTextureParam> ret;
ret.push_back(dtp);
return ret;
@@ -1140,13 +1178,13 @@ String VisualShaderNodeTexture2DArray::generate_global(Shader::Mode p_mode, Visu
return String();
}
-void VisualShaderNodeTexture2DArray::set_texture_array(Ref<Texture2DArray> p_value) {
- texture = p_value;
+void VisualShaderNodeTexture2DArray::set_texture_array(Ref<Texture2DArray> p_texture_array) {
+ texture_array = p_texture_array;
emit_changed();
}
Ref<Texture2DArray> VisualShaderNodeTexture2DArray::get_texture_array() const {
- return texture;
+ return texture_array;
}
Vector<StringName> VisualShaderNodeTexture2DArray::get_editable_properties() const {
@@ -1197,8 +1235,8 @@ String VisualShaderNodeTexture3D::generate_global(Shader::Mode p_mode, VisualSha
return String();
}
-void VisualShaderNodeTexture3D::set_texture(Ref<Texture3D> p_value) {
- texture = p_value;
+void VisualShaderNodeTexture3D::set_texture(Ref<Texture3D> p_texture) {
+ texture = p_texture;
emit_changed();
}
@@ -1301,6 +1339,8 @@ String VisualShaderNodeCubemap::generate_global(Shader::Mode p_mode, VisualShade
case TYPE_NORMAL_MAP:
u += " : hint_normal";
break;
+ default:
+ break;
}
return u + ";\n";
}
@@ -1364,6 +1404,10 @@ String VisualShaderNodeCubemap::get_input_port_default_hint(int p_port) const {
}
void VisualShaderNodeCubemap::set_source(Source p_source) {
+ ERR_FAIL_INDEX(int(p_source), int(SOURCE_MAX));
+ if (source == p_source) {
+ return;
+ }
source = p_source;
emit_changed();
emit_signal(SNAME("editor_refresh_request"));
@@ -1373,8 +1417,8 @@ VisualShaderNodeCubemap::Source VisualShaderNodeCubemap::get_source() const {
return source;
}
-void VisualShaderNodeCubemap::set_cube_map(Ref<Cubemap> p_value) {
- cube_map = p_value;
+void VisualShaderNodeCubemap::set_cube_map(Ref<Cubemap> p_cube_map) {
+ cube_map = p_cube_map;
emit_changed();
}
@@ -1382,8 +1426,12 @@ Ref<Cubemap> VisualShaderNodeCubemap::get_cube_map() const {
return cube_map;
}
-void VisualShaderNodeCubemap::set_texture_type(TextureType p_type) {
- texture_type = p_type;
+void VisualShaderNodeCubemap::set_texture_type(TextureType p_texture_type) {
+ ERR_FAIL_INDEX(int(p_texture_type), int(TYPE_MAX));
+ if (texture_type == p_texture_type) {
+ return;
+ }
+ texture_type = p_texture_type;
emit_changed();
}
@@ -1424,10 +1472,12 @@ void VisualShaderNodeCubemap::_bind_methods() {
BIND_ENUM_CONSTANT(SOURCE_TEXTURE);
BIND_ENUM_CONSTANT(SOURCE_PORT);
+ BIND_ENUM_CONSTANT(SOURCE_MAX);
BIND_ENUM_CONSTANT(TYPE_DATA);
BIND_ENUM_CONSTANT(TYPE_COLOR);
BIND_ENUM_CONSTANT(TYPE_NORMAL_MAP);
+ BIND_ENUM_CONSTANT(TYPE_MAX);
}
VisualShaderNodeCubemap::VisualShaderNodeCubemap() {
@@ -1497,12 +1547,17 @@ String VisualShaderNodeFloatOp::generate_code(Shader::Mode p_mode, VisualShader:
case OP_STEP:
code += "step(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
break;
+ default:
+ break;
}
-
return code;
}
void VisualShaderNodeFloatOp::set_operator(Operator p_op) {
+ ERR_FAIL_INDEX(int(p_op), int(OP_ENUM_SIZE));
+ if (op == p_op) {
+ return;
+ }
op = p_op;
emit_changed();
}
@@ -1533,6 +1588,7 @@ void VisualShaderNodeFloatOp::_bind_methods() {
BIND_ENUM_CONSTANT(OP_MIN);
BIND_ENUM_CONSTANT(OP_ATAN2);
BIND_ENUM_CONSTANT(OP_STEP);
+ BIND_ENUM_CONSTANT(OP_ENUM_SIZE);
}
VisualShaderNodeFloatOp::VisualShaderNodeFloatOp() {
@@ -1594,12 +1650,18 @@ String VisualShaderNodeIntOp::generate_code(Shader::Mode p_mode, VisualShader::T
case OP_MIN:
code += "min(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
break;
+ default:
+ break;
}
return code;
}
void VisualShaderNodeIntOp::set_operator(Operator p_op) {
+ ERR_FAIL_INDEX(int(p_op), OP_ENUM_SIZE);
+ if (op == p_op) {
+ return;
+ }
op = p_op;
emit_changed();
}
@@ -1627,6 +1689,7 @@ void VisualShaderNodeIntOp::_bind_methods() {
BIND_ENUM_CONSTANT(OP_MOD);
BIND_ENUM_CONSTANT(OP_MAX);
BIND_ENUM_CONSTANT(OP_MIN);
+ BIND_ENUM_CONSTANT(OP_ENUM_SIZE);
}
VisualShaderNodeIntOp::VisualShaderNodeIntOp() {
@@ -1703,12 +1766,18 @@ String VisualShaderNodeVectorOp::generate_code(Shader::Mode p_mode, VisualShader
case OP_STEP:
code += "step(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
break;
+ default:
+ break;
}
return code;
}
void VisualShaderNodeVectorOp::set_operator(Operator p_op) {
+ ERR_FAIL_INDEX(int(p_op), int(OP_ENUM_SIZE));
+ if (op == p_op) {
+ return;
+ }
op = p_op;
emit_changed();
}
@@ -1741,6 +1810,7 @@ void VisualShaderNodeVectorOp::_bind_methods() {
BIND_ENUM_CONSTANT(OP_ATAN2);
BIND_ENUM_CONSTANT(OP_REFLECT);
BIND_ENUM_CONSTANT(OP_STEP);
+ BIND_ENUM_CONSTANT(OP_ENUM_SIZE);
}
VisualShaderNodeVectorOp::VisualShaderNodeVectorOp() {
@@ -1844,14 +1914,19 @@ String VisualShaderNodeColorOp::generate_code(Shader::Mode p_mode, VisualShader:
}
} break;
+ default:
+ break;
}
return code;
}
void VisualShaderNodeColorOp::set_operator(Operator p_op) {
- op = p_op;
- switch (op) {
+ ERR_FAIL_INDEX(int(p_op), int(OP_MAX));
+ if (op == p_op) {
+ return;
+ }
+ switch (p_op) {
case OP_SCREEN:
simple_decl = true;
break;
@@ -1879,7 +1954,10 @@ void VisualShaderNodeColorOp::set_operator(Operator p_op) {
case OP_HARD_LIGHT:
simple_decl = false;
break;
+ default:
+ break;
}
+ op = p_op;
emit_changed();
}
@@ -1908,6 +1986,7 @@ void VisualShaderNodeColorOp::_bind_methods() {
BIND_ENUM_CONSTANT(OP_BURN);
BIND_ENUM_CONSTANT(OP_SOFT_LIGHT);
BIND_ENUM_CONSTANT(OP_HARD_LIGHT);
+ BIND_ENUM_CONSTANT(OP_MAX);
}
VisualShaderNodeColorOp::VisualShaderNodeColorOp() {
@@ -1971,7 +2050,10 @@ String VisualShaderNodeTransformOp::generate_code(Shader::Mode p_mode, VisualSha
}
void VisualShaderNodeTransformOp::set_operator(Operator p_op) {
- ERR_FAIL_INDEX(int(p_op), int(OP_LIMITER));
+ ERR_FAIL_INDEX(int(p_op), int(OP_MAX));
+ if (op == p_op) {
+ return;
+ }
op = p_op;
emit_changed();
}
@@ -2001,7 +2083,7 @@ void VisualShaderNodeTransformOp::_bind_methods() {
BIND_ENUM_CONSTANT(OP_B_MINUS_A);
BIND_ENUM_CONSTANT(OP_A_DIV_B);
BIND_ENUM_CONSTANT(OP_B_DIV_A);
- BIND_ENUM_CONSTANT(OP_LIMITER);
+ BIND_ENUM_CONSTANT(OP_MAX);
}
VisualShaderNodeTransformOp::VisualShaderNodeTransformOp() {
@@ -2052,6 +2134,10 @@ String VisualShaderNodeTransformVecMult::generate_code(Shader::Mode p_mode, Visu
}
void VisualShaderNodeTransformVecMult::set_operator(Operator p_op) {
+ ERR_FAIL_INDEX(int(p_op), int(OP_MAX));
+ if (op == p_op) {
+ return;
+ }
op = p_op;
emit_changed();
}
@@ -2076,6 +2162,7 @@ void VisualShaderNodeTransformVecMult::_bind_methods() {
BIND_ENUM_CONSTANT(OP_BxA);
BIND_ENUM_CONSTANT(OP_3x3_AxB);
BIND_ENUM_CONSTANT(OP_3x3_BxA);
+ BIND_ENUM_CONSTANT(OP_MAX);
}
VisualShaderNodeTransformVecMult::VisualShaderNodeTransformVecMult() {
@@ -2114,7 +2201,7 @@ String VisualShaderNodeFloatFunc::get_output_port_name(int p_port) const {
}
String VisualShaderNodeFloatFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- static const char *scalar_func_id[FUNC_ONEMINUS + 1] = {
+ static const char *functions[FUNC_MAX] = {
"sin($)",
"cos($)",
"tan($)",
@@ -2148,11 +2235,14 @@ String VisualShaderNodeFloatFunc::generate_code(Shader::Mode p_mode, VisualShade
"trunc($)",
"1.0 - $"
};
-
- return " " + p_output_vars[0] + " = " + String(scalar_func_id[func]).replace("$", p_input_vars[0]) + ";\n";
+ return " " + p_output_vars[0] + " = " + String(functions[func]).replace("$", p_input_vars[0]) + ";\n";
}
void VisualShaderNodeFloatFunc::set_function(Function p_func) {
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
+ if (func == p_func) {
+ return;
+ }
func = p_func;
emit_changed();
}
@@ -2205,6 +2295,7 @@ void VisualShaderNodeFloatFunc::_bind_methods() {
BIND_ENUM_CONSTANT(FUNC_ROUNDEVEN);
BIND_ENUM_CONSTANT(FUNC_TRUNC);
BIND_ENUM_CONSTANT(FUNC_ONEMINUS);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
}
VisualShaderNodeFloatFunc::VisualShaderNodeFloatFunc() {
@@ -2242,16 +2333,20 @@ String VisualShaderNodeIntFunc::get_output_port_name(int p_port) const {
}
String VisualShaderNodeIntFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- static const char *int_func_id[FUNC_SIGN + 1] = {
+ static const char *functions[FUNC_MAX] = {
"abs($)",
"-($)",
"sign($)"
};
- return " " + p_output_vars[0] + " = " + String(int_func_id[func]).replace("$", p_input_vars[0]) + ";\n";
+ return " " + p_output_vars[0] + " = " + String(functions[func]).replace("$", p_input_vars[0]) + ";\n";
}
void VisualShaderNodeIntFunc::set_function(Function p_func) {
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
+ if (func == p_func) {
+ return;
+ }
func = p_func;
emit_changed();
}
@@ -2275,6 +2370,7 @@ void VisualShaderNodeIntFunc::_bind_methods() {
BIND_ENUM_CONSTANT(FUNC_ABS);
BIND_ENUM_CONSTANT(FUNC_NEGATE);
BIND_ENUM_CONSTANT(FUNC_SIGN);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
}
VisualShaderNodeIntFunc::VisualShaderNodeIntFunc() {
@@ -2312,7 +2408,7 @@ String VisualShaderNodeVectorFunc::get_output_port_name(int p_port) const {
}
String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- static const char *vec_func_id[FUNC_ONEMINUS + 1] = {
+ static const char *vec_func_id[FUNC_MAX] = {
"normalize($)",
"max(min($, vec3(1.0)), vec3(0.0))",
"-($)",
@@ -2378,14 +2474,18 @@ String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShad
}
void VisualShaderNodeVectorFunc::set_function(Function p_func) {
- func = p_func;
- if (func == FUNC_RGB2HSV) {
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
+ if (func == p_func) {
+ return;
+ }
+ if (p_func == FUNC_RGB2HSV) {
simple_decl = false;
- } else if (func == FUNC_HSV2RGB) {
+ } else if (p_func == FUNC_HSV2RGB) {
simple_decl = false;
} else {
simple_decl = true;
}
+ func = p_func;
emit_changed();
}
@@ -2440,6 +2540,7 @@ void VisualShaderNodeVectorFunc::_bind_methods() {
BIND_ENUM_CONSTANT(FUNC_TANH);
BIND_ENUM_CONSTANT(FUNC_TRUNC);
BIND_ENUM_CONSTANT(FUNC_ONEMINUS);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
}
VisualShaderNodeVectorFunc::VisualShaderNodeVectorFunc() {
@@ -2498,12 +2599,18 @@ String VisualShaderNodeColorFunc::generate_code(Shader::Mode p_mode, VisualShade
code += " " + p_output_vars[0] + " = vec3(r, g, b);\n";
code += " }\n";
break;
+ default:
+ break;
}
return code;
}
void VisualShaderNodeColorFunc::set_function(Function p_func) {
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
+ if (func == p_func) {
+ return;
+ }
func = p_func;
emit_changed();
}
@@ -2526,6 +2633,7 @@ void VisualShaderNodeColorFunc::_bind_methods() {
BIND_ENUM_CONSTANT(FUNC_GRAYSCALE);
BIND_ENUM_CONSTANT(FUNC_SEPIA);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
}
VisualShaderNodeColorFunc::VisualShaderNodeColorFunc() {
@@ -2564,17 +2672,21 @@ String VisualShaderNodeTransformFunc::get_output_port_name(int p_port) const {
}
String VisualShaderNodeTransformFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- static const char *funcs[FUNC_TRANSPOSE + 1] = {
+ static const char *functions[FUNC_MAX] = {
"inverse($)",
"transpose($)"
};
String code;
- code += " " + p_output_vars[0] + " = " + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + String(functions[func]).replace("$", p_input_vars[0]) + ";\n";
return code;
}
void VisualShaderNodeTransformFunc::set_function(Function p_func) {
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
+ if (func == p_func) {
+ return;
+ }
func = p_func;
emit_changed();
}
@@ -2597,6 +2709,7 @@ void VisualShaderNodeTransformFunc::_bind_methods() {
BIND_ENUM_CONSTANT(FUNC_INVERSE);
BIND_ENUM_CONSTANT(FUNC_TRANSPOSE);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
}
VisualShaderNodeTransformFunc::VisualShaderNodeTransformFunc() {
@@ -2639,8 +2752,6 @@ String VisualShaderNodeUVFunc::get_input_port_name(int p_port) const {
return "offset";
case FUNC_SCALING:
return "pivot";
- case FUNC_MAX:
- break;
default:
break;
}
@@ -2693,24 +2804,23 @@ String VisualShaderNodeUVFunc::generate_code(Shader::Mode p_mode, VisualShader::
case FUNC_SCALING: {
code += vformat(" %s = fma((%s - %s), %s, %s);\n", p_output_vars[0], uv, offset_pivot, scale, offset_pivot);
} break;
- case FUNC_MAX:
+ default:
break;
}
return code;
}
void VisualShaderNodeUVFunc::set_function(VisualShaderNodeUVFunc::Function p_func) {
- ERR_FAIL_INDEX(int(p_func), FUNC_MAX);
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
if (func == p_func) {
return;
}
- func = p_func;
-
if (p_func == FUNC_PANNING) {
set_input_port_default_value(2, Vector3()); // offset
} else { // FUNC_SCALING
set_input_port_default_value(2, Vector3(0.5, 0.5, 0.0)); // pivot
}
+ func = p_func;
emit_changed();
}
@@ -2886,18 +2996,22 @@ String VisualShaderNodeScalarDerivativeFunc::get_output_port_name(int p_port) co
}
String VisualShaderNodeScalarDerivativeFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- static const char *funcs[FUNC_Y + 1] = {
+ static const char *functions[FUNC_MAX] = {
"fwidth($)",
"dFdx($)",
"dFdy($)"
};
String code;
- code += " " + p_output_vars[0] + " = " + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + String(functions[func]).replace("$", p_input_vars[0]) + ";\n";
return code;
}
void VisualShaderNodeScalarDerivativeFunc::set_function(Function p_func) {
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
+ if (func == p_func) {
+ return;
+ }
func = p_func;
emit_changed();
}
@@ -2921,6 +3035,7 @@ void VisualShaderNodeScalarDerivativeFunc::_bind_methods() {
BIND_ENUM_CONSTANT(FUNC_SUM);
BIND_ENUM_CONSTANT(FUNC_X);
BIND_ENUM_CONSTANT(FUNC_Y);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
}
VisualShaderNodeScalarDerivativeFunc::VisualShaderNodeScalarDerivativeFunc() {
@@ -2958,18 +3073,22 @@ String VisualShaderNodeVectorDerivativeFunc::get_output_port_name(int p_port) co
}
String VisualShaderNodeVectorDerivativeFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- static const char *funcs[FUNC_Y + 1] = {
+ static const char *functions[FUNC_MAX] = {
"fwidth($)",
"dFdx($)",
"dFdy($)"
};
String code;
- code += " " + p_output_vars[0] + " = " + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + String(functions[func]).replace("$", p_input_vars[0]) + ";\n";
return code;
}
void VisualShaderNodeVectorDerivativeFunc::set_function(Function p_func) {
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
+ if (func == p_func) {
+ return;
+ }
func = p_func;
emit_changed();
}
@@ -2993,6 +3112,7 @@ void VisualShaderNodeVectorDerivativeFunc::_bind_methods() {
BIND_ENUM_CONSTANT(FUNC_SUM);
BIND_ENUM_CONSTANT(FUNC_X);
BIND_ENUM_CONSTANT(FUNC_Y);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
}
VisualShaderNodeVectorDerivativeFunc::VisualShaderNodeVectorDerivativeFunc() {
@@ -3057,7 +3177,7 @@ String VisualShaderNodeClamp::generate_code(Shader::Mode p_mode, VisualShader::T
}
void VisualShaderNodeClamp::set_op_type(OpType p_op_type) {
- ERR_FAIL_INDEX((int)p_op_type, OP_TYPE_MAX);
+ ERR_FAIL_INDEX((int)p_op_type, int(OP_TYPE_MAX));
if (op_type == p_op_type) {
return;
}
@@ -3095,7 +3215,7 @@ Vector<StringName> VisualShaderNodeClamp::get_editable_properties() const {
}
void VisualShaderNodeClamp::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeClamp::set_op_type);
+ ClassDB::bind_method(D_METHOD("set_op_type", "op_type"), &VisualShaderNodeClamp::set_op_type);
ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeClamp::get_op_type);
ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Float,Int,Vector"), "set_op_type", "get_op_type");
@@ -3262,7 +3382,7 @@ String VisualShaderNodeStep::get_output_port_name(int p_port) const {
}
void VisualShaderNodeStep::set_op_type(OpType p_op_type) {
- ERR_FAIL_INDEX((int)p_op_type, OP_TYPE_MAX);
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
if (op_type == p_op_type) {
return;
}
@@ -3313,7 +3433,7 @@ Vector<StringName> VisualShaderNodeStep::get_editable_properties() const {
}
void VisualShaderNodeStep::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeStep::set_op_type);
+ ClassDB::bind_method(D_METHOD("set_op_type", "op_type"), &VisualShaderNodeStep::set_op_type);
ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeStep::get_op_type);
ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector,VectorScalar"), "set_op_type", "get_op_type");
@@ -3386,7 +3506,7 @@ String VisualShaderNodeSmoothStep::get_output_port_name(int p_port) const {
}
void VisualShaderNodeSmoothStep::set_op_type(OpType p_op_type) {
- ERR_FAIL_INDEX((int)p_op_type, OP_TYPE_MAX);
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
if (op_type == p_op_type) {
return;
}
@@ -3440,7 +3560,7 @@ Vector<StringName> VisualShaderNodeSmoothStep::get_editable_properties() const {
}
void VisualShaderNodeSmoothStep::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeSmoothStep::set_op_type);
+ ClassDB::bind_method(D_METHOD("set_op_type", "op_type"), &VisualShaderNodeSmoothStep::set_op_type);
ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeSmoothStep::get_op_type);
ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector,VectorScalar"), "set_op_type", "get_op_type");
@@ -3608,7 +3728,7 @@ String VisualShaderNodeMix::get_output_port_name(int p_port) const {
}
void VisualShaderNodeMix::set_op_type(OpType p_op_type) {
- ERR_FAIL_INDEX((int)p_op_type, OP_TYPE_MAX);
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
if (op_type == p_op_type) {
return;
}
@@ -3656,7 +3776,7 @@ Vector<StringName> VisualShaderNodeMix::get_editable_properties() const {
}
void VisualShaderNodeMix::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeMix::set_op_type);
+ ClassDB::bind_method(D_METHOD("set_op_type", "op_type"), &VisualShaderNodeMix::set_op_type);
ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeMix::get_op_type);
ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector,VectorScalar"), "set_op_type", "get_op_type");
@@ -3925,6 +4045,10 @@ bool VisualShaderNodeFloatUniform::is_use_prop_slots() const {
}
void VisualShaderNodeFloatUniform::set_hint(Hint p_hint) {
+ ERR_FAIL_INDEX(int(p_hint), int(HINT_MAX));
+ if (hint == p_hint) {
+ return;
+ }
hint = p_hint;
emit_changed();
}
@@ -3934,6 +4058,9 @@ VisualShaderNodeFloatUniform::Hint VisualShaderNodeFloatUniform::get_hint() cons
}
void VisualShaderNodeFloatUniform::set_min(float p_value) {
+ if (Math::is_equal_approx(hint_range_min, p_value)) {
+ return;
+ }
hint_range_min = p_value;
emit_changed();
}
@@ -3943,6 +4070,9 @@ float VisualShaderNodeFloatUniform::get_min() const {
}
void VisualShaderNodeFloatUniform::set_max(float p_value) {
+ if (Math::is_equal_approx(hint_range_max, p_value)) {
+ return;
+ }
hint_range_max = p_value;
emit_changed();
}
@@ -3952,6 +4082,9 @@ float VisualShaderNodeFloatUniform::get_max() const {
}
void VisualShaderNodeFloatUniform::set_step(float p_value) {
+ if (Math::is_equal_approx(hint_range_step, p_value)) {
+ return;
+ }
hint_range_step = p_value;
emit_changed();
}
@@ -3961,6 +4094,9 @@ float VisualShaderNodeFloatUniform::get_step() const {
}
void VisualShaderNodeFloatUniform::set_default_value_enabled(bool p_enabled) {
+ if (default_value_enabled == p_enabled) {
+ return;
+ }
default_value_enabled = p_enabled;
emit_changed();
}
@@ -3970,6 +4106,9 @@ bool VisualShaderNodeFloatUniform::is_default_value_enabled() const {
}
void VisualShaderNodeFloatUniform::set_default_value(float p_value) {
+ if (Math::is_equal_approx(default_value, p_value)) {
+ return;
+ }
default_value = p_value;
emit_changed();
}
@@ -4007,6 +4146,7 @@ void VisualShaderNodeFloatUniform::_bind_methods() {
BIND_ENUM_CONSTANT(HINT_NONE);
BIND_ENUM_CONSTANT(HINT_RANGE);
BIND_ENUM_CONSTANT(HINT_RANGE_STEP);
+ BIND_ENUM_CONSTANT(HINT_MAX);
}
bool VisualShaderNodeFloatUniform::is_qualifier_supported(Qualifier p_qual) const {
@@ -4096,6 +4236,10 @@ bool VisualShaderNodeIntUniform::is_use_prop_slots() const {
}
void VisualShaderNodeIntUniform::set_hint(Hint p_hint) {
+ ERR_FAIL_INDEX(int(p_hint), int(HINT_MAX));
+ if (hint == p_hint) {
+ return;
+ }
hint = p_hint;
emit_changed();
}
@@ -4105,6 +4249,9 @@ VisualShaderNodeIntUniform::Hint VisualShaderNodeIntUniform::get_hint() const {
}
void VisualShaderNodeIntUniform::set_min(int p_value) {
+ if (hint_range_min == p_value) {
+ return;
+ }
hint_range_min = p_value;
emit_changed();
}
@@ -4114,6 +4261,9 @@ int VisualShaderNodeIntUniform::get_min() const {
}
void VisualShaderNodeIntUniform::set_max(int p_value) {
+ if (hint_range_max == p_value) {
+ return;
+ }
hint_range_max = p_value;
emit_changed();
}
@@ -4123,6 +4273,9 @@ int VisualShaderNodeIntUniform::get_max() const {
}
void VisualShaderNodeIntUniform::set_step(int p_value) {
+ if (hint_range_step == p_value) {
+ return;
+ }
hint_range_step = p_value;
emit_changed();
}
@@ -4131,8 +4284,11 @@ int VisualShaderNodeIntUniform::get_step() const {
return hint_range_step;
}
-void VisualShaderNodeIntUniform::set_default_value_enabled(bool p_enabled) {
- default_value_enabled = p_enabled;
+void VisualShaderNodeIntUniform::set_default_value_enabled(bool p_default_value_enabled) {
+ if (default_value_enabled == p_default_value_enabled) {
+ return;
+ }
+ default_value_enabled = p_default_value_enabled;
emit_changed();
}
@@ -4140,8 +4296,11 @@ bool VisualShaderNodeIntUniform::is_default_value_enabled() const {
return default_value_enabled;
}
-void VisualShaderNodeIntUniform::set_default_value(int p_value) {
- default_value = p_value;
+void VisualShaderNodeIntUniform::set_default_value(int p_default_value) {
+ if (default_value == p_default_value) {
+ return;
+ }
+ default_value = p_default_value;
emit_changed();
}
@@ -4178,6 +4337,7 @@ void VisualShaderNodeIntUniform::_bind_methods() {
BIND_ENUM_CONSTANT(HINT_NONE);
BIND_ENUM_CONSTANT(HINT_RANGE);
BIND_ENUM_CONSTANT(HINT_RANGE_STEP);
+ BIND_ENUM_CONSTANT(HINT_MAX);
}
bool VisualShaderNodeIntUniform::is_qualifier_supported(Qualifier p_qual) const {
@@ -4238,8 +4398,11 @@ String VisualShaderNodeBooleanUniform::get_output_port_name(int p_port) const {
return ""; //no output port means the editor will be used as port
}
-void VisualShaderNodeBooleanUniform::set_default_value_enabled(bool p_enabled) {
- default_value_enabled = p_enabled;
+void VisualShaderNodeBooleanUniform::set_default_value_enabled(bool p_default_value_enabled) {
+ if (default_value_enabled == p_default_value_enabled) {
+ return;
+ }
+ default_value_enabled = p_default_value_enabled;
emit_changed();
}
@@ -4247,8 +4410,11 @@ bool VisualShaderNodeBooleanUniform::is_default_value_enabled() const {
return default_value_enabled;
}
-void VisualShaderNodeBooleanUniform::set_default_value(bool p_value) {
- default_value = p_value;
+void VisualShaderNodeBooleanUniform::set_default_value(bool p_default_value) {
+ if (default_value == p_default_value) {
+ return;
+ }
+ default_value = p_default_value;
emit_changed();
}
@@ -4343,6 +4509,9 @@ String VisualShaderNodeColorUniform::get_output_port_name(int p_port) const {
}
void VisualShaderNodeColorUniform::set_default_value_enabled(bool p_enabled) {
+ if (default_value_enabled == p_enabled) {
+ return;
+ }
default_value_enabled = p_enabled;
emit_changed();
}
@@ -4352,6 +4521,9 @@ bool VisualShaderNodeColorUniform::is_default_value_enabled() const {
}
void VisualShaderNodeColorUniform::set_default_value(const Color &p_value) {
+ if (default_value.is_equal_approx(p_value)) {
+ return;
+ }
default_value = p_value;
emit_changed();
}
@@ -4689,6 +4861,9 @@ String VisualShaderNodeTextureUniform::generate_global(Shader::Mode p_mode, Visu
case TYPE_ANISO:
code += " : hint_aniso;\n";
break;
+ default:
+ code += ";\n";
+ break;
}
return code;
@@ -4727,8 +4902,12 @@ String VisualShaderNodeTextureUniform::generate_code(Shader::Mode p_mode, Visual
return code;
}
-void VisualShaderNodeTextureUniform::set_texture_type(TextureType p_type) {
- texture_type = p_type;
+void VisualShaderNodeTextureUniform::set_texture_type(TextureType p_texture_type) {
+ ERR_FAIL_INDEX(int(p_texture_type), int(TYPE_MAX));
+ if (texture_type == p_texture_type) {
+ return;
+ }
+ texture_type = p_texture_type;
emit_changed();
}
@@ -4736,8 +4915,12 @@ VisualShaderNodeTextureUniform::TextureType VisualShaderNodeTextureUniform::get_
return texture_type;
}
-void VisualShaderNodeTextureUniform::set_color_default(ColorDefault p_default) {
- color_default = p_default;
+void VisualShaderNodeTextureUniform::set_color_default(ColorDefault p_color_default) {
+ ERR_FAIL_INDEX(int(p_color_default), int(COLOR_DEFAULT_MAX));
+ if (color_default == p_color_default) {
+ return;
+ }
+ color_default = p_color_default;
emit_changed();
}
@@ -4766,9 +4949,11 @@ void VisualShaderNodeTextureUniform::_bind_methods() {
BIND_ENUM_CONSTANT(TYPE_COLOR);
BIND_ENUM_CONSTANT(TYPE_NORMAL_MAP);
BIND_ENUM_CONSTANT(TYPE_ANISO);
+ BIND_ENUM_CONSTANT(TYPE_MAX);
BIND_ENUM_CONSTANT(COLOR_DEFAULT_WHITE);
BIND_ENUM_CONSTANT(COLOR_DEFAULT_BLACK);
+ BIND_ENUM_CONSTANT(COLOR_DEFAULT_MAX);
}
String VisualShaderNodeTextureUniform::get_input_port_default_hint(int p_port) const {
@@ -4786,6 +4971,8 @@ bool VisualShaderNodeTextureUniform::is_qualifier_supported(Qualifier p_qual) co
return true;
case Qualifier::QUAL_INSTANCE:
return false;
+ default:
+ break;
}
return false;
}
@@ -4951,6 +5138,9 @@ String VisualShaderNodeTexture2DArrayUniform::generate_global(Shader::Mode p_mod
case TYPE_ANISO:
code += " : hint_aniso;\n";
break;
+ default:
+ code += ";\n";
+ break;
}
return code;
@@ -5021,6 +5211,9 @@ String VisualShaderNodeTexture3DUniform::generate_global(Shader::Mode p_mode, Vi
case TYPE_ANISO:
code += " : hint_aniso;\n";
break;
+ default:
+ code += ";\n";
+ break;
}
return code;
@@ -5091,6 +5284,9 @@ String VisualShaderNodeCubemapUniform::generate_global(Shader::Mode p_mode, Visu
case TYPE_ANISO:
code += " : hint_aniso;\n";
break;
+ default:
+ code += ";\n";
+ break;
}
return code;
@@ -5247,7 +5443,7 @@ String VisualShaderNodeSwitch::get_output_port_name(int p_port) const {
}
void VisualShaderNodeSwitch::set_op_type(OpType p_op_type) {
- ERR_FAIL_INDEX((int)p_op_type, OP_TYPE_MAX);
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
if (op_type == p_op_type) {
return;
}
@@ -5452,17 +5648,21 @@ String VisualShaderNodeIs::get_output_port_name(int p_port) const {
}
String VisualShaderNodeIs::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- static const char *funcs[FUNC_IS_NAN + 1] = {
+ static const char *functions[FUNC_MAX] = {
"isinf($)",
"isnan($)"
};
String code;
- code += " " + p_output_vars[0] + " = " + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + String(functions[func]).replace("$", p_input_vars[0]) + ";\n";
return code;
}
void VisualShaderNodeIs::set_function(Function p_func) {
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
+ if (func == p_func) {
+ return;
+ }
func = p_func;
emit_changed();
}
@@ -5485,6 +5685,7 @@ void VisualShaderNodeIs::_bind_methods() {
BIND_ENUM_CONSTANT(FUNC_IS_INF);
BIND_ENUM_CONSTANT(FUNC_IS_NAN);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
}
VisualShaderNodeIs::VisualShaderNodeIs() {
@@ -5498,14 +5699,14 @@ String VisualShaderNodeCompare::get_caption() const {
}
int VisualShaderNodeCompare::get_input_port_count() const {
- if (ctype == CTYPE_SCALAR && (func == FUNC_EQUAL || func == FUNC_NOT_EQUAL)) {
+ if (comparison_type == CTYPE_SCALAR && (func == FUNC_EQUAL || func == FUNC_NOT_EQUAL)) {
return 3;
}
return 2;
}
VisualShaderNodeCompare::PortType VisualShaderNodeCompare::get_input_port_type(int p_port) const {
- switch (ctype) {
+ switch (comparison_type) {
case CTYPE_SCALAR:
return PORT_TYPE_SCALAR;
case CTYPE_SCALAR_INT:
@@ -5548,17 +5749,16 @@ String VisualShaderNodeCompare::get_output_port_name(int p_port) const {
}
String VisualShaderNodeCompare::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const {
- if (ctype == CTYPE_BOOLEAN || ctype == CTYPE_TRANSFORM) {
+ if (comparison_type == CTYPE_BOOLEAN || comparison_type == CTYPE_TRANSFORM) {
if (func > FUNC_NOT_EQUAL) {
return TTR("Invalid comparison function for that type.");
}
}
-
return "";
}
String VisualShaderNodeCompare::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- static const char *ops[FUNC_LESS_THAN_EQUAL + 1] = {
+ static const char *operators[FUNC_MAX] = {
"==",
"!=",
">",
@@ -5567,7 +5767,7 @@ String VisualShaderNodeCompare::generate_code(Shader::Mode p_mode, VisualShader:
"<=",
};
- static const char *funcs[FUNC_LESS_THAN_EQUAL + 1] = {
+ static const char *functions[FUNC_MAX] = {
"equal($)",
"notEqual($)",
"greaterThan($)",
@@ -5576,31 +5776,31 @@ String VisualShaderNodeCompare::generate_code(Shader::Mode p_mode, VisualShader:
"lessThanEqual($)",
};
- static const char *conds[COND_ANY + 1] = {
+ static const char *conditions[COND_MAX] = {
"all($)",
"any($)",
};
String code;
- switch (ctype) {
+ switch (comparison_type) {
case CTYPE_SCALAR:
if (func == FUNC_EQUAL) {
code += " " + p_output_vars[0] + " = (abs(" + p_input_vars[0] + " - " + p_input_vars[1] + ") < " + p_input_vars[2] + ");";
} else if (func == FUNC_NOT_EQUAL) {
code += " " + p_output_vars[0] + " = !(abs(" + p_input_vars[0] + " - " + p_input_vars[1] + ") < " + p_input_vars[2] + ");";
} else {
- code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", ops[func]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", operators[func]) + ";\n";
}
break;
case CTYPE_SCALAR_INT:
- code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", ops[func]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", operators[func]) + ";\n";
break;
case CTYPE_VECTOR:
code += " {\n";
- code += " bvec3 _bv = " + String(funcs[func]).replace("$", p_input_vars[0] + ", " + p_input_vars[1]) + ";\n";
- code += " " + p_output_vars[0] + " = " + String(conds[condition]).replace("$", "_bv") + ";\n";
+ code += " bvec3 _bv = " + String(functions[func]).replace("$", p_input_vars[0] + ", " + p_input_vars[1]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + String(conditions[condition]).replace("$", "_bv") + ";\n";
code += " }\n";
break;
@@ -5608,14 +5808,14 @@ String VisualShaderNodeCompare::generate_code(Shader::Mode p_mode, VisualShader:
if (func > FUNC_NOT_EQUAL) {
return " " + p_output_vars[0] + " = false;\n";
}
- code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", ops[func]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", operators[func]) + ";\n";
break;
case CTYPE_TRANSFORM:
if (func > FUNC_NOT_EQUAL) {
return " " + p_output_vars[0] + " = false;\n";
}
- code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", ops[func]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", operators[func]) + ";\n";
break;
default:
@@ -5624,10 +5824,12 @@ String VisualShaderNodeCompare::generate_code(Shader::Mode p_mode, VisualShader:
return code;
}
-void VisualShaderNodeCompare::set_comparison_type(ComparisonType p_type) {
- ctype = p_type;
-
- switch (ctype) {
+void VisualShaderNodeCompare::set_comparison_type(ComparisonType p_comparison_type) {
+ ERR_FAIL_INDEX(int(p_comparison_type), int(CTYPE_MAX));
+ if (comparison_type == p_comparison_type) {
+ return;
+ }
+ switch (p_comparison_type) {
case CTYPE_SCALAR:
set_input_port_default_value(0, 0.0);
set_input_port_default_value(1, 0.0);
@@ -5653,15 +5855,22 @@ void VisualShaderNodeCompare::set_comparison_type(ComparisonType p_type) {
set_input_port_default_value(1, Transform3D());
simple_decl = true;
break;
+ default:
+ break;
}
+ comparison_type = p_comparison_type;
emit_changed();
}
VisualShaderNodeCompare::ComparisonType VisualShaderNodeCompare::get_comparison_type() const {
- return ctype;
+ return comparison_type;
}
void VisualShaderNodeCompare::set_function(Function p_func) {
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
+ if (func == p_func) {
+ return;
+ }
func = p_func;
emit_changed();
}
@@ -5670,8 +5879,12 @@ VisualShaderNodeCompare::Function VisualShaderNodeCompare::get_function() const
return func;
}
-void VisualShaderNodeCompare::set_condition(Condition p_cond) {
- condition = p_cond;
+void VisualShaderNodeCompare::set_condition(Condition p_condition) {
+ ERR_FAIL_INDEX(int(p_condition), int(COND_MAX));
+ if (condition == p_condition) {
+ return;
+ }
+ condition = p_condition;
emit_changed();
}
@@ -5683,7 +5896,7 @@ Vector<StringName> VisualShaderNodeCompare::get_editable_properties() const {
Vector<StringName> props;
props.push_back("type");
props.push_back("function");
- if (ctype == CTYPE_VECTOR) {
+ if (comparison_type == CTYPE_VECTOR) {
props.push_back("condition");
}
return props;
@@ -5708,6 +5921,7 @@ void VisualShaderNodeCompare::_bind_methods() {
BIND_ENUM_CONSTANT(CTYPE_VECTOR);
BIND_ENUM_CONSTANT(CTYPE_BOOLEAN);
BIND_ENUM_CONSTANT(CTYPE_TRANSFORM);
+ BIND_ENUM_CONSTANT(CTYPE_MAX);
BIND_ENUM_CONSTANT(FUNC_EQUAL);
BIND_ENUM_CONSTANT(FUNC_NOT_EQUAL);
@@ -5715,9 +5929,11 @@ void VisualShaderNodeCompare::_bind_methods() {
BIND_ENUM_CONSTANT(FUNC_GREATER_THAN_EQUAL);
BIND_ENUM_CONSTANT(FUNC_LESS_THAN);
BIND_ENUM_CONSTANT(FUNC_LESS_THAN_EQUAL);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
BIND_ENUM_CONSTANT(COND_ALL);
BIND_ENUM_CONSTANT(COND_ANY);
+ BIND_ENUM_CONSTANT(COND_MAX);
}
VisualShaderNodeCompare::VisualShaderNodeCompare() {
@@ -5775,7 +5991,7 @@ String VisualShaderNodeMultiplyAdd::generate_code(Shader::Mode p_mode, VisualSha
}
void VisualShaderNodeMultiplyAdd::set_op_type(OpType p_op_type) {
- ERR_FAIL_INDEX((int)p_op_type, OP_TYPE_MAX);
+ ERR_FAIL_INDEX((int)p_op_type, int(OP_TYPE_MAX));
if (op_type == p_op_type) {
return;
}
@@ -5898,7 +6114,10 @@ bool VisualShaderNodeBillboard::is_show_prop_names() const {
}
void VisualShaderNodeBillboard::set_billboard_type(BillboardType p_billboard_type) {
- ERR_FAIL_INDEX((int)p_billboard_type, BILLBOARD_TYPE_MAX);
+ ERR_FAIL_INDEX(int(p_billboard_type), int(BILLBOARD_TYPE_MAX));
+ if (billboard_type == p_billboard_type) {
+ return;
+ }
billboard_type = p_billboard_type;
simple_decl = bool(billboard_type == BILLBOARD_TYPE_DISABLED);
set_disabled(simple_decl);
diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h
index 99617b46a3..2c952300fe 100644
--- a/scene/resources/visual_shader_nodes.h
+++ b/scene/resources/visual_shader_nodes.h
@@ -76,7 +76,7 @@ public:
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- void set_constant(float p_value);
+ void set_constant(float p_constant);
float get_constant() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -106,7 +106,7 @@ public:
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- void set_constant(int p_value);
+ void set_constant(int p_constant);
int get_constant() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -136,7 +136,7 @@ public:
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- void set_constant(bool p_value);
+ void set_constant(bool p_constant);
bool get_constant() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -167,7 +167,7 @@ public:
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- void set_constant(Color p_value);
+ void set_constant(const Color &p_constant);
Color get_constant() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -197,7 +197,7 @@ public:
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- void set_constant(Vector3 p_value);
+ void set_constant(const Vector3 &p_constant);
Vector3 get_constant() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -227,7 +227,7 @@ public:
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- void set_constant(Transform3D p_value);
+ void set_constant(const Transform3D &p_constant);
Transform3D get_constant() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -251,12 +251,14 @@ public:
SOURCE_2D_NORMAL,
SOURCE_DEPTH,
SOURCE_PORT,
+ SOURCE_MAX,
};
enum TextureType {
TYPE_DATA,
TYPE_COLOR,
TYPE_NORMAL_MAP,
+ TYPE_MAX,
};
private:
@@ -287,10 +289,10 @@ public:
void set_source(Source p_source);
Source get_source() const;
- void set_texture(Ref<Texture2D> p_value);
+ void set_texture(Ref<Texture2D> p_texture);
Ref<Texture2D> get_texture() const;
- void set_texture_type(TextureType p_type);
+ void set_texture_type(TextureType p_texture_type);
TextureType get_texture_type() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -327,7 +329,7 @@ public:
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- void set_texture(Ref<CurveTexture> p_value);
+ void set_texture(Ref<CurveTexture> p_texture);
Ref<CurveTexture> get_texture() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -360,7 +362,7 @@ public:
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- void set_texture(Ref<CurveXYZTexture> p_value);
+ void set_texture(Ref<CurveXYZTexture> p_texture);
Ref<CurveXYZTexture> get_texture() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -378,6 +380,7 @@ public:
enum Source {
SOURCE_TEXTURE,
SOURCE_PORT,
+ SOURCE_MAX,
};
protected:
@@ -410,7 +413,7 @@ VARIANT_ENUM_CAST(VisualShaderNodeSample3D::Source)
class VisualShaderNodeTexture2DArray : public VisualShaderNodeSample3D {
GDCLASS(VisualShaderNodeTexture2DArray, VisualShaderNodeSample3D);
- Ref<Texture2DArray> texture;
+ Ref<Texture2DArray> texture_array;
protected:
static void _bind_methods();
@@ -423,7 +426,7 @@ public:
virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const override;
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
- void set_texture_array(Ref<Texture2DArray> p_value);
+ void set_texture_array(Ref<Texture2DArray> p_texture_array);
Ref<Texture2DArray> get_texture_array() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -446,7 +449,7 @@ public:
virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const override;
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
- void set_texture(Ref<Texture3D> p_value);
+ void set_texture(Ref<Texture3D> p_texture);
Ref<Texture3D> get_texture() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -461,13 +464,15 @@ class VisualShaderNodeCubemap : public VisualShaderNode {
public:
enum Source {
SOURCE_TEXTURE,
- SOURCE_PORT
+ SOURCE_PORT,
+ SOURCE_MAX,
};
enum TextureType {
TYPE_DATA,
TYPE_COLOR,
- TYPE_NORMAL_MAP
+ TYPE_NORMAL_MAP,
+ TYPE_MAX,
};
private:
@@ -497,10 +502,10 @@ public:
void set_source(Source p_source);
Source get_source() const;
- void set_cube_map(Ref<Cubemap> p_value);
+ void set_cube_map(Ref<Cubemap> p_cube_map);
Ref<Cubemap> get_cube_map() const;
- void set_texture_type(TextureType p_type);
+ void set_texture_type(TextureType p_texture_type);
TextureType get_texture_type() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -530,7 +535,8 @@ public:
OP_MAX,
OP_MIN,
OP_ATAN2,
- OP_STEP
+ OP_STEP,
+ OP_ENUM_SIZE,
};
protected:
@@ -573,6 +579,7 @@ public:
OP_MOD,
OP_MAX,
OP_MIN,
+ OP_ENUM_SIZE,
};
protected:
@@ -619,7 +626,8 @@ public:
OP_CROSS,
OP_ATAN2,
OP_REFLECT,
- OP_STEP
+ OP_STEP,
+ OP_ENUM_SIZE,
};
protected:
@@ -665,7 +673,8 @@ public:
OP_DODGE,
OP_BURN,
OP_SOFT_LIGHT,
- OP_HARD_LIGHT
+ OP_HARD_LIGHT,
+ OP_MAX,
};
protected:
@@ -714,7 +723,7 @@ public:
OP_B_MINUS_A,
OP_A_DIV_B,
OP_B_DIV_A,
- OP_LIMITER,
+ OP_MAX,
};
protected:
@@ -758,6 +767,7 @@ public:
OP_BxA,
OP_3x3_AxB,
OP_3x3_BxA,
+ OP_MAX,
};
protected:
@@ -828,7 +838,8 @@ public:
FUNC_RECIPROCAL,
FUNC_ROUNDEVEN,
FUNC_TRUNC,
- FUNC_ONEMINUS
+ FUNC_ONEMINUS,
+ FUNC_MAX,
};
protected:
@@ -871,6 +882,7 @@ public:
FUNC_ABS,
FUNC_NEGATE,
FUNC_SIGN,
+ FUNC_MAX,
};
protected:
@@ -944,7 +956,8 @@ public:
FUNC_TAN,
FUNC_TANH,
FUNC_TRUNC,
- FUNC_ONEMINUS
+ FUNC_ONEMINUS,
+ FUNC_MAX,
};
protected:
@@ -985,7 +998,8 @@ class VisualShaderNodeColorFunc : public VisualShaderNode {
public:
enum Function {
FUNC_GRAYSCALE,
- FUNC_SEPIA
+ FUNC_SEPIA,
+ FUNC_MAX,
};
protected:
@@ -1026,7 +1040,8 @@ class VisualShaderNodeTransformFunc : public VisualShaderNode {
public:
enum Function {
FUNC_INVERSE,
- FUNC_TRANSPOSE
+ FUNC_TRANSPOSE,
+ FUNC_MAX,
};
protected:
@@ -1092,7 +1107,7 @@ public:
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- void set_function(Function p_op);
+ void set_function(Function p_func);
Function get_function() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -1201,7 +1216,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
- void set_op_type(OpType p_type);
+ void set_op_type(OpType p_op_type);
OpType get_op_type() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -1224,7 +1239,8 @@ public:
enum Function {
FUNC_SUM,
FUNC_X,
- FUNC_Y
+ FUNC_Y,
+ FUNC_MAX,
};
protected:
@@ -1264,7 +1280,8 @@ public:
enum Function {
FUNC_SUM,
FUNC_X,
- FUNC_Y
+ FUNC_Y,
+ FUNC_MAX,
};
protected:
@@ -1371,7 +1388,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
- void set_op_type(OpType p_type);
+ void set_op_type(OpType p_op_type);
OpType get_op_type() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -1413,7 +1430,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
- void set_op_type(OpType p_type);
+ void set_op_type(OpType p_op_type);
OpType get_op_type() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -1501,7 +1518,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
- void set_op_type(OpType p_type);
+ void set_op_type(OpType p_op_type);
OpType get_op_type() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -1613,6 +1630,7 @@ public:
HINT_NONE,
HINT_RANGE,
HINT_RANGE_STEP,
+ HINT_MAX,
};
private:
@@ -1679,6 +1697,7 @@ public:
HINT_NONE,
HINT_RANGE,
HINT_RANGE_STEP,
+ HINT_MAX,
};
private:
@@ -1919,11 +1938,13 @@ public:
TYPE_COLOR,
TYPE_NORMAL_MAP,
TYPE_ANISO,
+ TYPE_MAX,
};
enum ColorDefault {
COLOR_DEFAULT_WHITE,
- COLOR_DEFAULT_BLACK
+ COLOR_DEFAULT_BLACK,
+ COLOR_DEFAULT_MAX,
};
protected:
@@ -2113,7 +2134,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
- void set_op_type(OpType p_type);
+ void set_op_type(OpType p_op_type);
OpType get_op_type() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -2161,6 +2182,7 @@ public:
enum Function {
FUNC_IS_INF,
FUNC_IS_NAN,
+ FUNC_MAX,
};
protected:
@@ -2206,6 +2228,7 @@ public:
CTYPE_VECTOR,
CTYPE_BOOLEAN,
CTYPE_TRANSFORM,
+ CTYPE_MAX,
};
enum Function {
@@ -2215,15 +2238,17 @@ public:
FUNC_GREATER_THAN_EQUAL,
FUNC_LESS_THAN,
FUNC_LESS_THAN_EQUAL,
+ FUNC_MAX,
};
enum Condition {
COND_ALL,
COND_ANY,
+ COND_MAX,
};
protected:
- ComparisonType ctype = CTYPE_SCALAR;
+ ComparisonType comparison_type = CTYPE_SCALAR;
Function func = FUNC_EQUAL;
Condition condition = COND_ALL;
@@ -2291,7 +2316,7 @@ public:
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- void set_op_type(OpType p_type);
+ void set_op_type(OpType p_op_type);
OpType get_op_type() const;
virtual Vector<StringName> get_editable_properties() const override;
diff --git a/scene/resources/visual_shader_particle_nodes.cpp b/scene/resources/visual_shader_particle_nodes.cpp
index 2250cf8d95..5fe801e037 100644
--- a/scene/resources/visual_shader_particle_nodes.cpp
+++ b/scene/resources/visual_shader_particle_nodes.cpp
@@ -402,15 +402,16 @@ String VisualShaderNodeParticleRandomness::generate_code(Shader::Mode p_mode, Vi
}
void VisualShaderNodeParticleRandomness::set_op_type(OpType p_op_type) {
- ERR_FAIL_INDEX((int)p_op_type, OP_TYPE_MAX);
- if (p_op_type != op_type) {
- if (p_op_type == OP_TYPE_SCALAR) {
- set_input_port_default_value(0, 0.0);
- set_input_port_default_value(1, 1.0);
- } else {
- set_input_port_default_value(0, Vector3(-1.0, -1.0, -1.0));
- set_input_port_default_value(1, Vector3(1.0, 1.0, 1.0));
- }
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
+ if (op_type == p_op_type) {
+ return;
+ }
+ if (p_op_type == OP_TYPE_SCALAR) {
+ set_input_port_default_value(0, 0.0);
+ set_input_port_default_value(1, 1.0);
+ } else {
+ set_input_port_default_value(0, Vector3(-1.0, -1.0, -1.0));
+ set_input_port_default_value(1, Vector3(1.0, 1.0, 1.0));
}
op_type = p_op_type;
emit_changed();
@@ -500,8 +501,6 @@ String VisualShaderNodeParticleAccelerator::generate_code(Shader::Mode p_mode, V
code += " __vec3_buff1 = cross(__ndiff, normalize(" + (p_input_vars[2].is_empty() ? "vec3" + (String)get_input_port_default_value(2) : p_input_vars[2]) + "));\n";
code += " " + p_output_vars[0] + " = length(__vec3_buff1) > 0.0 ? normalize(__vec3_buff1) * (" + (p_input_vars[0].is_empty() ? "vec3" + (String)get_input_port_default_value(0) : p_input_vars[0]) + " * mix(1.0, __rand_from_seed(__seed), " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ")) : vec3(0.0);\n";
break;
- case MODE_MAX:
- break;
default:
break;
}
@@ -510,6 +509,10 @@ String VisualShaderNodeParticleAccelerator::generate_code(Shader::Mode p_mode, V
}
void VisualShaderNodeParticleAccelerator::set_mode(Mode p_mode) {
+ ERR_FAIL_INDEX(int(p_mode), int(MODE_MAX));
+ if (mode == p_mode) {
+ return;
+ }
mode = p_mode;
emit_changed();
}
diff --git a/scene/resources/visual_shader_particle_nodes.h b/scene/resources/visual_shader_particle_nodes.h
index ecd187a885..f5435c3511 100644
--- a/scene/resources/visual_shader_particle_nodes.h
+++ b/scene/resources/visual_shader_particle_nodes.h
@@ -181,8 +181,8 @@ VARIANT_ENUM_CAST(VisualShaderNodeParticleRandomness::OpType)
// Process nodes
-class VisualShaderNodeParticleAccelerator : public VisualShaderNodeOutput {
- GDCLASS(VisualShaderNodeParticleAccelerator, VisualShaderNodeOutput);
+class VisualShaderNodeParticleAccelerator : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeParticleAccelerator, VisualShaderNode);
public:
enum Mode {
diff --git a/scene/resources/line_shape_2d.cpp b/scene/resources/world_margin_shape_2d.cpp
index d206f12287..3b43681528 100644
--- a/scene/resources/line_shape_2d.cpp
+++ b/scene/resources/world_margin_shape_2d.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* line_shape_2d.cpp */
+/* world_margin_shape_2d.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,13 +28,13 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "line_shape_2d.h"
+#include "world_margin_shape_2d.h"
#include "core/math/geometry_2d.h"
#include "servers/physics_server_2d.h"
#include "servers/rendering_server.h"
-bool LineShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
+bool WorldMarginShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
Vector2 point = get_distance() * get_normal();
Vector2 l[2][2] = { { point - get_normal().orthogonal() * 100, point + get_normal().orthogonal() * 100 }, { point, point + get_normal() * 30 } };
@@ -48,7 +48,7 @@ bool LineShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tol
return false;
}
-void LineShape2D::_update_shape() {
+void WorldMarginShape2D::_update_shape() {
Array arr;
arr.push_back(normal);
arr.push_back(distance);
@@ -56,25 +56,25 @@ void LineShape2D::_update_shape() {
emit_changed();
}
-void LineShape2D::set_normal(const Vector2 &p_normal) {
+void WorldMarginShape2D::set_normal(const Vector2 &p_normal) {
normal = p_normal;
_update_shape();
}
-void LineShape2D::set_distance(real_t p_distance) {
+void WorldMarginShape2D::set_distance(real_t p_distance) {
distance = p_distance;
_update_shape();
}
-Vector2 LineShape2D::get_normal() const {
+Vector2 WorldMarginShape2D::get_normal() const {
return normal;
}
-real_t LineShape2D::get_distance() const {
+real_t WorldMarginShape2D::get_distance() const {
return distance;
}
-void LineShape2D::draw(const RID &p_to_rid, const Color &p_color) {
+void WorldMarginShape2D::draw(const RID &p_to_rid, const Color &p_color) {
Vector2 point = get_distance() * get_normal();
Vector2 l1[2] = { point - get_normal().orthogonal() * 100, point + get_normal().orthogonal() * 100 };
@@ -83,7 +83,7 @@ void LineShape2D::draw(const RID &p_to_rid, const Color &p_color) {
RS::get_singleton()->canvas_item_add_line(p_to_rid, l2[0], l2[1], p_color, 3);
}
-Rect2 LineShape2D::get_rect() const {
+Rect2 WorldMarginShape2D::get_rect() const {
Vector2 point = get_distance() * get_normal();
Vector2 l1[2] = { point - get_normal().orthogonal() * 100, point + get_normal().orthogonal() * 100 };
@@ -96,22 +96,22 @@ Rect2 LineShape2D::get_rect() const {
return rect;
}
-real_t LineShape2D::get_enclosing_radius() const {
+real_t WorldMarginShape2D::get_enclosing_radius() const {
return distance;
}
-void LineShape2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_normal", "normal"), &LineShape2D::set_normal);
- ClassDB::bind_method(D_METHOD("get_normal"), &LineShape2D::get_normal);
+void WorldMarginShape2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_normal", "normal"), &WorldMarginShape2D::set_normal);
+ ClassDB::bind_method(D_METHOD("get_normal"), &WorldMarginShape2D::get_normal);
- ClassDB::bind_method(D_METHOD("set_distance", "distance"), &LineShape2D::set_distance);
- ClassDB::bind_method(D_METHOD("get_distance"), &LineShape2D::get_distance);
+ ClassDB::bind_method(D_METHOD("set_distance", "distance"), &WorldMarginShape2D::set_distance);
+ ClassDB::bind_method(D_METHOD("get_distance"), &WorldMarginShape2D::get_distance);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "normal"), "set_normal", "get_normal");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance"), "set_distance", "get_distance");
}
-LineShape2D::LineShape2D() :
- Shape2D(PhysicsServer2D::get_singleton()->line_shape_create()) {
+WorldMarginShape2D::WorldMarginShape2D() :
+ Shape2D(PhysicsServer2D::get_singleton()->world_margin_shape_create()) {
_update_shape();
}
diff --git a/scene/resources/line_shape_2d.h b/scene/resources/world_margin_shape_2d.h
index 210a1aa9e6..3c1d593ffe 100644
--- a/scene/resources/line_shape_2d.h
+++ b/scene/resources/world_margin_shape_2d.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* line_shape_2d.h */
+/* world_margin_shape_2d.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,15 +28,15 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef LINE_SHAPE_2D_H
-#define LINE_SHAPE_2D_H
+#ifndef WORLD_MARGIN_SHAPE_2D_H
+#define WORLD_MARGIN_SHAPE_2D_H
#include "scene/resources/shape_2d.h"
-class LineShape2D : public Shape2D {
- GDCLASS(LineShape2D, Shape2D);
+class WorldMarginShape2D : public Shape2D {
+ GDCLASS(WorldMarginShape2D, Shape2D);
- // LineShape2D is often used for one-way platforms, where the normal pointing up makes sense.
+ // WorldMarginShape2D is often used for one-way platforms, where the normal pointing up makes sense.
Vector2 normal = Vector2(0, -1);
real_t distance = 0.0;
@@ -58,7 +58,7 @@ public:
virtual Rect2 get_rect() const override;
virtual real_t get_enclosing_radius() const override;
- LineShape2D();
+ WorldMarginShape2D();
};
-#endif // LINE_SHAPE_2D_H
+#endif // WORLD_MARGIN_SHAPE_2D_H