summaryrefslogtreecommitdiff
path: root/scene/resources
diff options
context:
space:
mode:
Diffstat (limited to 'scene/resources')
-rw-r--r--scene/resources/SCsub21
-rw-r--r--scene/resources/animation.cpp352
-rw-r--r--scene/resources/animation.h175
-rw-r--r--scene/resources/audio_stream_sample.cpp41
-rw-r--r--scene/resources/audio_stream_sample.h42
-rw-r--r--scene/resources/bit_map.cpp37
-rw-r--r--scene/resources/bit_map.h12
-rw-r--r--scene/resources/box_shape_3d.cpp49
-rw-r--r--scene/resources/box_shape_3d.h14
-rw-r--r--scene/resources/camera_effects.cpp25
-rw-r--r--scene/resources/camera_effects.h17
-rw-r--r--scene/resources/canvas_item_material.cpp306
-rw-r--r--scene/resources/canvas_item_material.h151
-rw-r--r--scene/resources/capsule_shape_2d.cpp36
-rw-r--r--scene/resources/capsule_shape_2d.h8
-rw-r--r--scene/resources/capsule_shape_3d.cpp24
-rw-r--r--scene/resources/capsule_shape_3d.h8
-rw-r--r--scene/resources/circle_shape_2d.cpp13
-rw-r--r--scene/resources/circle_shape_2d.h6
-rw-r--r--scene/resources/concave_polygon_shape_2d.cpp6
-rw-r--r--scene/resources/concave_polygon_shape_2d.h4
-rw-r--r--scene/resources/concave_polygon_shape_3d.cpp43
-rw-r--r--scene/resources/concave_polygon_shape_3d.h10
-rw-r--r--scene/resources/convex_polygon_shape_2d.cpp17
-rw-r--r--scene/resources/convex_polygon_shape_2d.h4
-rw-r--r--scene/resources/convex_polygon_shape_3d.cpp10
-rw-r--r--scene/resources/convex_polygon_shape_3d.h4
-rw-r--r--scene/resources/curve.cpp143
-rw-r--r--scene/resources/curve.h62
-rw-r--r--scene/resources/cylinder_shape_3d.cpp12
-rw-r--r--scene/resources/cylinder_shape_3d.h8
-rw-r--r--scene/resources/default_theme/SCsub12
-rw-r--r--scene/resources/default_theme/arrow_left.pngbin0 -> 159 bytes
-rw-r--r--scene/resources/default_theme/bar_arrow.pngbin0 -> 208 bytes
-rw-r--r--scene/resources/default_theme/checked_disabled.pngbin0 -> 421 bytes
-rw-r--r--scene/resources/default_theme/default_theme.cpp528
-rw-r--r--scene/resources/default_theme/default_theme.h6
-rw-r--r--scene/resources/default_theme/default_theme_builders.py40
-rw-r--r--scene/resources/default_theme/dialog_bg.pngbin0 -> 1314 bytes
-rw-r--r--scene/resources/default_theme/ellipsis.pngbin0 -> 193 bytes
-rw-r--r--scene/resources/default_theme/font_hidpi.inc25463
-rw-r--r--scene/resources/default_theme/font_lodpi.inc13117
-rw-r--r--scene/resources/default_theme/icon_grid_layout.pngbin0 -> 2170 bytes
-rw-r--r--scene/resources/default_theme/icon_grid_minimap.pngbin0 -> 640 bytes
-rw-r--r--scene/resources/default_theme/indeterminate.pngbin0 -> 242 bytes
-rw-r--r--scene/resources/default_theme/option_button_disabled_mirrored.pngbin0 -> 704 bytes
-rw-r--r--scene/resources/default_theme/option_button_hover_mirrored.pngbin0 -> 728 bytes
-rw-r--r--scene/resources/default_theme/option_button_normal_mirrored.pngbin0 -> 726 bytes
-rw-r--r--scene/resources/default_theme/option_button_pressed_mirrored.pngbin0 -> 736 bytes
-rw-r--r--scene/resources/default_theme/picker_cursor.pngbin0 -> 300 bytes
-rw-r--r--scene/resources/default_theme/popup_window.pngbin903 -> 921 bytes
-rw-r--r--scene/resources/default_theme/radio_checked_disabled.pngbin0 -> 235 bytes
-rw-r--r--scene/resources/default_theme/radio_unchecked_disabled.pngbin0 -> 183 bytes
-rw-r--r--scene/resources/default_theme/submenu_mirrored.pngbin0 -> 157 bytes
-rw-r--r--scene/resources/default_theme/theme_data.h94
-rw-r--r--scene/resources/default_theme/toggle_off_disabled_mirrored.pngbin0 -> 535 bytes
-rw-r--r--scene/resources/default_theme/toggle_off_mirrored.pngbin0 -> 1169 bytes
-rw-r--r--scene/resources/default_theme/toggle_on_disabled_mirrored.pngbin0 -> 496 bytes
-rw-r--r--scene/resources/default_theme/toggle_on_mirrored.pngbin0 -> 1133 bytes
-rw-r--r--scene/resources/default_theme/unchecked_disabled.pngbin0 -> 246 bytes
-rw-r--r--scene/resources/default_theme/window_resizer_mirrored.pngbin0 -> 109 bytes
-rwxr-xr-xscene/resources/default_theme/xpmfix.sh2
-rw-r--r--scene/resources/dynamic_font.cpp1143
-rw-r--r--scene/resources/dynamic_font.h316
-rw-r--r--scene/resources/environment.cpp294
-rw-r--r--scene/resources/environment.h75
-rw-r--r--scene/resources/font.cpp1813
-rw-r--r--scene/resources/font.h358
-rw-r--r--scene/resources/gradient.cpp27
-rw-r--r--scene/resources/gradient.h12
-rw-r--r--scene/resources/height_map_shape_3d.cpp26
-rw-r--r--scene/resources/height_map_shape_3d.h18
-rw-r--r--scene/resources/immediate_mesh.cpp416
-rw-r--r--scene/resources/immediate_mesh.h117
-rw-r--r--scene/resources/material.cpp855
-rw-r--r--scene/resources/material.h203
-rw-r--r--scene/resources/mesh.cpp940
-rw-r--r--scene/resources/mesh.h133
-rw-r--r--scene/resources/mesh_data_tool.cpp48
-rw-r--r--scene/resources/mesh_data_tool.h16
-rw-r--r--scene/resources/mesh_library.cpp39
-rw-r--r--scene/resources/mesh_library.h17
-rw-r--r--scene/resources/multimesh.cpp27
-rw-r--r--scene/resources/multimesh.h18
-rw-r--r--scene/resources/navigation_mesh.cpp131
-rw-r--r--scene/resources/navigation_mesh.h72
-rw-r--r--scene/resources/packed_scene.cpp174
-rw-r--r--scene/resources/packed_scene.h53
-rw-r--r--scene/resources/particles_material.cpp871
-rw-r--r--scene/resources/particles_material.h176
-rw-r--r--scene/resources/physics_material.cpp8
-rw-r--r--scene/resources/physics_material.h10
-rw-r--r--scene/resources/polygon_path_finder.cpp18
-rw-r--r--scene/resources/polygon_path_finder.h14
-rw-r--r--scene/resources/primitive_meshes.cpp768
-rw-r--r--scene/resources/primitive_meshes.h185
-rw-r--r--scene/resources/rectangle_shape_2d.cpp66
-rw-r--r--scene/resources/rectangle_shape_2d.h14
-rw-r--r--scene/resources/resource_format_text.cpp503
-rw-r--r--scene/resources/resource_format_text.h67
-rw-r--r--scene/resources/segment_shape_2d.cpp4
-rw-r--r--scene/resources/segment_shape_2d.h4
-rw-r--r--scene/resources/separation_ray_shape_2d.cpp (renamed from scene/resources/ray_shape_2d.cpp)76
-rw-r--r--scene/resources/separation_ray_shape_2d.h (renamed from scene/resources/ray_shape_2d.h)26
-rw-r--r--scene/resources/separation_ray_shape_3d.cpp (renamed from scene/resources/ray_shape_3d.cpp)53
-rw-r--r--scene/resources/separation_ray_shape_3d.h (renamed from scene/resources/ray_shape_3d.h)26
-rw-r--r--scene/resources/shader.cpp27
-rw-r--r--scene/resources/shader.h13
-rw-r--r--scene/resources/shape_2d.cpp18
-rw-r--r--scene/resources/shape_2d.h11
-rw-r--r--scene/resources/shape_3d.cpp10
-rw-r--r--scene/resources/shape_3d.h8
-rw-r--r--scene/resources/skeleton_modification_2d.cpp253
-rw-r--r--scene/resources/skeleton_modification_2d.h89
-rw-r--r--scene/resources/skeleton_modification_2d_ccdik.cpp545
-rw-r--r--scene/resources/skeleton_modification_2d_ccdik.h116
-rw-r--r--scene/resources/skeleton_modification_2d_fabrik.cpp445
-rw-r--r--scene/resources/skeleton_modification_2d_fabrik.h108
-rw-r--r--scene/resources/skeleton_modification_2d_jiggle.cpp564
-rw-r--r--scene/resources/skeleton_modification_2d_jiggle.h139
-rw-r--r--scene/resources/skeleton_modification_2d_lookat.cpp407
-rw-r--r--scene/resources/skeleton_modification_2d_lookat.h100
-rw-r--r--scene/resources/skeleton_modification_2d_physicalbones.cpp297
-rw-r--r--scene/resources/skeleton_modification_2d_physicalbones.h82
-rw-r--r--scene/resources/skeleton_modification_2d_stackholder.cpp131
-rw-r--r--scene/resources/skeleton_modification_2d_stackholder.h64
-rw-r--r--scene/resources/skeleton_modification_2d_twoboneik.cpp481
-rw-r--r--scene/resources/skeleton_modification_2d_twoboneik.h107
-rw-r--r--scene/resources/skeleton_modification_3d.cpp162
-rw-r--r--scene/resources/skeleton_modification_3d.h79
-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_2d.cpp269
-rw-r--r--scene/resources/skeleton_modification_stack_2d.h99
-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/skin.cpp20
-rw-r--r--scene/resources/skin.h23
-rw-r--r--scene/resources/sky.cpp6
-rw-r--r--scene/resources/sky.h8
-rw-r--r--scene/resources/sky_material.cpp403
-rw-r--r--scene/resources/sky_material.h37
-rw-r--r--scene/resources/sphere_shape_3d.cpp7
-rw-r--r--scene/resources/sphere_shape_3d.h4
-rw-r--r--scene/resources/sprite_frames.cpp241
-rw-r--r--scene/resources/sprite_frames.h102
-rw-r--r--scene/resources/style_box.cpp454
-rw-r--r--scene/resources/style_box.h107
-rw-r--r--scene/resources/surface_tool.cpp778
-rw-r--r--scene/resources/surface_tool.h110
-rw-r--r--scene/resources/syntax_highlighter.cpp58
-rw-r--r--scene/resources/syntax_highlighter.h17
-rw-r--r--scene/resources/text_file.cpp10
-rw-r--r--scene/resources/text_file.h4
-rw-r--r--scene/resources/text_line.cpp423
-rw-r--r--scene/resources/text_line.h132
-rw-r--r--scene/resources/text_paragraph.cpp849
-rw-r--r--scene/resources/text_paragraph.h165
-rw-r--r--scene/resources/texture.cpp813
-rw-r--r--scene/resources/texture.h274
-rw-r--r--scene/resources/theme.cpp1258
-rw-r--r--scene/resources/theme.h186
-rw-r--r--scene/resources/tile_set.cpp4777
-rw-r--r--scene/resources/tile_set.h723
-rw-r--r--scene/resources/video_stream.h4
-rw-r--r--scene/resources/visual_shader.cpp1407
-rw-r--r--scene/resources/visual_shader.h183
-rw-r--r--scene/resources/visual_shader_nodes.cpp2337
-rw-r--r--scene/resources/visual_shader_nodes.h644
-rw-r--r--scene/resources/visual_shader_particle_nodes.cpp1028
-rw-r--r--scene/resources/visual_shader_particle_nodes.h285
-rw-r--r--scene/resources/visual_shader_sdf_nodes.cpp283
-rw-r--r--scene/resources/visual_shader_sdf_nodes.h132
-rw-r--r--scene/resources/world_2d.cpp326
-rw-r--r--scene/resources/world_2d.h30
-rw-r--r--scene/resources/world_3d.cpp236
-rw-r--r--scene/resources/world_3d.h24
-rw-r--r--scene/resources/world_margin_shape_2d.cpp (renamed from scene/resources/line_shape_2d.cpp)48
-rw-r--r--scene/resources/world_margin_shape_2d.h (renamed from scene/resources/line_shape_2d.h)23
-rw-r--r--scene/resources/world_margin_shape_3d.cpp5
-rw-r--r--scene/resources/world_margin_shape_3d.h4
191 files changed, 30124 insertions, 48236 deletions
diff --git a/scene/resources/SCsub b/scene/resources/SCsub
index 3a86b22835..f4dc7a46fb 100644
--- a/scene/resources/SCsub
+++ b/scene/resources/SCsub
@@ -2,6 +2,25 @@
Import("env")
-env.add_source_files(env.scene_sources, "*.cpp")
+# Thirdparty code
+
+thirdparty_obj = []
+
+thirdparty_sources = "#thirdparty/misc/mikktspace.c"
+
+env_thirdparty = env.Clone()
+env_thirdparty.disable_warnings()
+env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+env.scene_sources += thirdparty_obj
+
+# Godot source files
+
+scene_obj = []
+
+env.add_source_files(scene_obj, "*.cpp")
+env.scene_sources += scene_obj
+
+# Needed to force rebuilding the scene files when the thirdparty code is updated.
+env.Depends(scene_obj, thirdparty_obj)
SConscript("default_theme/SCsub")
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index 92103f3b1d..b4eec2530b 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,11 +29,9 @@
/*************************************************************************/
#include "animation.h"
-#include "scene/scene_string_names.h"
#include "core/math/geometry_3d.h"
-
-#define ANIM_MIN_LENGTH 0.001
+#include "scene/scene_string_names.h"
bool Animation::_set(const StringName &p_name, const Variant &p_value) {
String name = p_name;
@@ -45,8 +43,8 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
if (tracks.size() == track && what == "type") {
String type = p_value;
- if (type == "transform") {
- add_track(TYPE_TRANSFORM);
+ if (type == "transform" || type == "transform3d") {
+ add_track(TYPE_TRANSFORM3D);
} else if (type == "value") {
add_track(TYPE_VALUE);
} else if (type == "method") {
@@ -77,19 +75,20 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
} else if (what == "enabled") {
track_set_enabled(track, p_value);
} else if (what == "keys" || what == "key_values") {
- if (track_get_type(track) == TYPE_TRANSFORM) {
+ if (track_get_type(track) == TYPE_TRANSFORM3D) {
TransformTrack *tt = static_cast<TransformTrack *>(tracks[track]);
- Vector<float> values = p_value;
+ Vector<real_t> values = p_value;
int vcount = values.size();
- ERR_FAIL_COND_V(vcount % 12, false); // should be multiple of 11
+ ERR_FAIL_COND_V(vcount % TRANSFORM_TRACK_SIZE, false);
- const float *r = values.ptr();
+ const real_t *r = values.ptr();
- tt->transforms.resize(vcount / 12);
+ int64_t count = vcount / TRANSFORM_TRACK_SIZE;
+ tt->transforms.resize(count);
- for (int i = 0; i < (vcount / 12); i++) {
+ for (int i = 0; i < count; i++) {
TKey<TransformKey> &tk = tt->transforms.write[i];
- const float *ofs = &r[i * 12];
+ const real_t *ofs = &r[i * TRANSFORM_TRACK_SIZE];
tk.time = ofs[0];
tk.transition = ofs[1];
@@ -127,7 +126,7 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
vt->update_mode = UpdateMode(um);
}
- Vector<float> times = d["times"];
+ Vector<real_t> times = d["times"];
Array values = d["values"];
ERR_FAIL_COND_V(times.size() != values.size(), false);
@@ -135,7 +134,7 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
if (times.size()) {
int valcount = times.size();
- const float *rt = times.ptr();
+ const real_t *rt = times.ptr();
vt->values.resize(valcount);
@@ -145,10 +144,10 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
}
if (d.has("transitions")) {
- Vector<float> transitions = d["transitions"];
+ Vector<real_t> transitions = d["transitions"];
ERR_FAIL_COND_V(transitions.size() != valcount, false);
- const float *rtr = transitions.ptr();
+ const real_t *rtr = transitions.ptr();
for (int i = 0; i < valcount; i++) {
vt->values.write[i].transition = rtr[i];
@@ -167,7 +166,7 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
ERR_FAIL_COND_V(!d.has("times"), false);
ERR_FAIL_COND_V(!d.has("values"), false);
- Vector<float> times = d["times"];
+ Vector<real_t> times = d["times"];
Array values = d["values"];
ERR_FAIL_COND_V(times.size() != values.size(), false);
@@ -175,17 +174,17 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
if (times.size()) {
int valcount = times.size();
- const float *rt = times.ptr();
+ const real_t *rt = times.ptr();
for (int i = 0; i < valcount; i++) {
track_insert_key(track, rt[i], values[i]);
}
if (d.has("transitions")) {
- Vector<float> transitions = d["transitions"];
+ Vector<real_t> transitions = d["transitions"];
ERR_FAIL_COND_V(transitions.size() != valcount, false);
- const float *rtr = transitions.ptr();
+ const real_t *rtr = transitions.ptr();
for (int i = 0; i < valcount; i++) {
track_set_key_transition(track, i, rtr[i]);
@@ -198,16 +197,16 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
ERR_FAIL_COND_V(!d.has("times"), false);
ERR_FAIL_COND_V(!d.has("points"), false);
- Vector<float> times = d["times"];
- PackedFloat32Array values = d["points"];
+ Vector<real_t> times = d["times"];
+ Vector<real_t> values = d["points"];
ERR_FAIL_COND_V(times.size() * 5 != values.size(), false);
if (times.size()) {
int valcount = times.size();
- const float *rt = times.ptr();
- const float *rv = values.ptr();
+ const real_t *rt = times.ptr();
+ const real_t *rv = values.ptr();
bt->values.resize(valcount);
@@ -229,7 +228,7 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
ERR_FAIL_COND_V(!d.has("times"), false);
ERR_FAIL_COND_V(!d.has("clips"), false);
- Vector<float> times = d["times"];
+ Vector<real_t> times = d["times"];
Array clips = d["clips"];
ERR_FAIL_COND_V(clips.size() != times.size(), false);
@@ -237,7 +236,7 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
if (times.size()) {
int valcount = times.size();
- const float *rt = times.ptr();
+ const real_t *rt = times.ptr();
ad->values.clear();
@@ -270,7 +269,7 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
ERR_FAIL_COND_V(!d.has("times"), false);
ERR_FAIL_COND_V(!d.has("clips"), false);
- Vector<float> times = d["times"];
+ Vector<real_t> times = d["times"];
Vector<String> clips = d["clips"];
ERR_FAIL_COND_V(clips.size() != times.size(), false);
@@ -278,7 +277,7 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
if (times.size()) {
int valcount = times.size();
- const float *rt = times.ptr();
+ const real_t *rt = times.ptr();
const String *rc = clips.ptr();
an->values.resize(valcount);
@@ -320,7 +319,7 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
ERR_FAIL_INDEX_V(track, tracks.size(), false);
if (what == "type") {
switch (track_get_type(track)) {
- case TYPE_TRANSFORM:
+ case TYPE_TRANSFORM3D:
r_ret = "transform";
break;
case TYPE_VALUE:
@@ -353,17 +352,17 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
} else if (what == "enabled") {
r_ret = track_is_enabled(track);
} else if (what == "keys") {
- if (track_get_type(track) == TYPE_TRANSFORM) {
- Vector<float> keys;
+ if (track_get_type(track) == TYPE_TRANSFORM3D) {
+ Vector<real_t> keys;
int kk = track_get_key_count(track);
- keys.resize(kk * 12);
+ keys.resize(kk * TRANSFORM_TRACK_SIZE);
real_t *w = keys.ptrw();
int idx = 0;
for (int i = 0; i < track_get_key_count(track); i++) {
Vector3 loc;
- Quat rot;
+ Quaternion rot;
Vector3 scale;
transform_track_get_key(track, i, &loc, &rot, &scale);
@@ -391,8 +390,8 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
Dictionary d;
- Vector<float> key_times;
- Vector<float> key_transitions;
+ Vector<real_t> key_times;
+ Vector<real_t> key_transitions;
Array key_values;
int kk = vt->values.size();
@@ -401,8 +400,8 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
key_transitions.resize(kk);
key_values.resize(kk);
- float *wti = key_times.ptrw();
- float *wtr = key_transitions.ptrw();
+ real_t *wti = key_times.ptrw();
+ real_t *wtr = key_transitions.ptrw();
int idx = 0;
@@ -429,8 +428,8 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
} else if (track_get_type(track) == TYPE_METHOD) {
Dictionary d;
- Vector<float> key_times;
- Vector<float> key_transitions;
+ Vector<real_t> key_times;
+ Vector<real_t> key_transitions;
Array key_values;
int kk = track_get_key_count(track);
@@ -439,8 +438,8 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
key_transitions.resize(kk);
key_values.resize(kk);
- float *wti = key_times.ptrw();
- float *wtr = key_transitions.ptrw();
+ real_t *wti = key_times.ptrw();
+ real_t *wtr = key_transitions.ptrw();
int idx = 0;
for (int i = 0; i < track_get_key_count(track); i++) {
@@ -465,16 +464,16 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
Dictionary d;
- Vector<float> key_times;
- Vector<float> key_points;
+ Vector<real_t> key_times;
+ Vector<real_t> key_points;
int kk = bt->values.size();
key_times.resize(kk);
key_points.resize(kk * 5);
- float *wti = key_times.ptrw();
- float *wpo = key_points.ptrw();
+ real_t *wti = key_times.ptrw();
+ real_t *wpo = key_points.ptrw();
int idx = 0;
@@ -501,14 +500,14 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
Dictionary d;
- Vector<float> key_times;
+ Vector<real_t> key_times;
Array clips;
int kk = ad->values.size();
key_times.resize(kk);
- float *wti = key_times.ptrw();
+ real_t *wti = key_times.ptrw();
int idx = 0;
@@ -535,7 +534,7 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
Dictionary d;
- Vector<float> key_times;
+ Vector<real_t> key_times;
Vector<String> clips;
int kk = an->values.size();
@@ -543,7 +542,7 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
key_times.resize(kk);
clips.resize(kk);
- float *wti = key_times.ptrw();
+ real_t *wti = key_times.ptrw();
String *wcl = clips.ptrw();
const TKey<StringName> *vls = an->values.ptr();
@@ -582,13 +581,17 @@ void Animation::_get_property_list(List<PropertyInfo> *p_list) const {
}
}
+void Animation::reset_state() {
+ clear();
+}
+
int Animation::add_track(TrackType p_type, int p_at_pos) {
if (p_at_pos < 0 || p_at_pos >= tracks.size()) {
p_at_pos = tracks.size();
}
switch (p_type) {
- case TYPE_TRANSFORM: {
+ case TYPE_TRANSFORM3D: {
TransformTrack *tt = memnew(TransformTrack);
tracks.insert(p_at_pos, tt);
} break;
@@ -626,7 +629,7 @@ void Animation::remove_track(int p_track) {
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM: {
+ case TYPE_TRANSFORM3D: {
TransformTrack *tt = static_cast<TransformTrack *>(t);
_clear(tt->transforms);
@@ -669,7 +672,7 @@ int Animation::get_track_count() const {
}
Animation::TrackType Animation::track_get_type(int p_track) const {
- ERR_FAIL_INDEX_V(p_track, tracks.size(), TYPE_TRANSFORM);
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), TYPE_TRANSFORM3D);
return tracks[p_track]->type;
}
@@ -720,8 +723,7 @@ bool Animation::track_get_interpolation_loop_wrap(int p_track) const {
// transform
/*
template<class T>
-int Animation::_insert_pos(float p_time, T& p_keys) {
-
+int Animation::_insert_pos(double p_time, T& p_keys) {
// simple, linear time inset that should be fast enough in reality.
int idx=p_keys.size();
@@ -734,24 +736,22 @@ int Animation::_insert_pos(float p_time, T& p_keys) {
p_keys.insert(idx,T());
return idx;
} else if (p_keys[idx-1].time == p_time) {
-
// condition for replacing.
return idx-1;
}
idx--;
}
-
}
*/
template <class T, class V>
-int Animation::_insert(float p_time, T &p_keys, const V &p_value) {
+int Animation::_insert(double p_time, T &p_keys, const V &p_value) {
int idx = p_keys.size();
while (true) {
// Condition for replacement.
- if (idx > 0 && Math::is_equal_approx(p_keys[idx - 1].time, p_time)) {
+ if (idx > 0 && Math::is_equal_approx((double)p_keys[idx - 1].time, p_time)) {
float transition = p_keys[idx - 1].transition;
p_keys.write[idx - 1] = p_value;
p_keys.write[idx - 1].transition = transition;
@@ -774,12 +774,12 @@ void Animation::_clear(T &p_keys) {
p_keys.clear();
}
-Error Animation::transform_track_get_key(int p_track, int p_key, Vector3 *r_loc, Quat *r_rot, Vector3 *r_scale) const {
+Error Animation::transform_track_get_key(int p_track, int p_key, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
Track *t = tracks[p_track];
TransformTrack *tt = static_cast<TransformTrack *>(t);
- ERR_FAIL_COND_V(t->type != TYPE_TRANSFORM, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(t->type != TYPE_TRANSFORM3D, ERR_INVALID_PARAMETER);
ERR_FAIL_INDEX_V(p_key, tt->transforms.size(), ERR_INVALID_PARAMETER);
if (r_loc) {
@@ -795,10 +795,10 @@ Error Animation::transform_track_get_key(int p_track, int p_key, Vector3 *r_loc,
return OK;
}
-int Animation::transform_track_insert_key(int p_track, float p_time, const Vector3 &p_loc, const Quat &p_rot, const Vector3 &p_scale) {
+int Animation::transform_track_insert_key(int p_track, double p_time, const Vector3 &p_loc, const Quaternion &p_rot, const Vector3 &p_scale) {
ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
Track *t = tracks[p_track];
- ERR_FAIL_COND_V(t->type != TYPE_TRANSFORM, -1);
+ ERR_FAIL_COND_V(t->type != TYPE_TRANSFORM3D, -1);
TransformTrack *tt = static_cast<TransformTrack *>(t);
@@ -813,8 +813,8 @@ int Animation::transform_track_insert_key(int p_track, float p_time, const Vecto
return ret;
}
-void Animation::track_remove_key_at_position(int p_track, float p_pos) {
- int idx = track_find_key(p_track, p_pos, true);
+void Animation::track_remove_key_at_time(int p_track, double p_time) {
+ int idx = track_find_key(p_track, p_time, true);
ERR_FAIL_COND(idx < 0);
track_remove_key(p_track, idx);
}
@@ -824,7 +824,7 @@ void Animation::track_remove_key(int p_track, int p_idx) {
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM: {
+ case TYPE_TRANSFORM3D: {
TransformTrack *tt = static_cast<TransformTrack *>(t);
ERR_FAIL_INDEX(p_idx, tt->transforms.size());
tt->transforms.remove(p_idx);
@@ -865,12 +865,12 @@ void Animation::track_remove_key(int p_track, int p_idx) {
emit_changed();
}
-int Animation::track_find_key(int p_track, float p_time, bool p_exact) const {
+int Animation::track_find_key(int p_track, double p_time, bool p_exact) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM: {
+ case TYPE_TRANSFORM3D: {
TransformTrack *tt = static_cast<TransformTrack *>(t);
int k = _find(tt->transforms, p_time);
if (k < 0 || k >= tt->transforms.size()) {
@@ -947,19 +947,19 @@ int Animation::track_find_key(int p_track, float p_time, bool p_exact) const {
return -1;
}
-void Animation::track_insert_key(int p_track, float p_time, const Variant &p_key, float p_transition) {
+void Animation::track_insert_key(int p_track, double p_time, const Variant &p_key, real_t p_transition) {
ERR_FAIL_INDEX(p_track, tracks.size());
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM: {
+ case TYPE_TRANSFORM3D: {
Dictionary d = p_key;
Vector3 loc;
if (d.has("location")) {
loc = d["location"];
}
- Quat rot;
+ Quaternion rot;
if (d.has("rotation")) {
rot = d["rotation"];
}
@@ -1054,7 +1054,7 @@ int Animation::track_get_key_count(int p_track) const {
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM: {
+ case TYPE_TRANSFORM3D: {
TransformTrack *tt = static_cast<TransformTrack *>(t);
return tt->transforms.size();
} break;
@@ -1089,7 +1089,7 @@ Variant Animation::track_get_key_value(int p_track, int p_key_idx) const {
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM: {
+ case TYPE_TRANSFORM3D: {
TransformTrack *tt = static_cast<TransformTrack *>(t);
ERR_FAIL_INDEX_V(p_key_idx, tt->transforms.size(), Variant());
@@ -1152,12 +1152,12 @@ Variant Animation::track_get_key_value(int p_track, int p_key_idx) const {
ERR_FAIL_V(Variant());
}
-float Animation::track_get_key_time(int p_track, int p_key_idx) const {
+double Animation::track_get_key_time(int p_track, int p_key_idx) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM: {
+ case TYPE_TRANSFORM3D: {
TransformTrack *tt = static_cast<TransformTrack *>(t);
ERR_FAIL_INDEX_V(p_key_idx, tt->transforms.size(), -1);
return tt->transforms[p_key_idx].time;
@@ -1197,12 +1197,12 @@ float Animation::track_get_key_time(int p_track, int p_key_idx) const {
ERR_FAIL_V(-1);
}
-void Animation::track_set_key_time(int p_track, int p_key_idx, float p_time) {
+void Animation::track_set_key_time(int p_track, int p_key_idx, double p_time) {
ERR_FAIL_INDEX(p_track, tracks.size());
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM: {
+ case TYPE_TRANSFORM3D: {
TransformTrack *tt = static_cast<TransformTrack *>(t);
ERR_FAIL_INDEX(p_key_idx, tt->transforms.size());
TKey<TransformKey> key = tt->transforms[p_key_idx];
@@ -1261,12 +1261,12 @@ void Animation::track_set_key_time(int p_track, int p_key_idx, float p_time) {
ERR_FAIL();
}
-float Animation::track_get_key_transition(int p_track, int p_key_idx) const {
+real_t Animation::track_get_key_transition(int p_track, int p_key_idx) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM: {
+ case TYPE_TRANSFORM3D: {
TransformTrack *tt = static_cast<TransformTrack *>(t);
ERR_FAIL_INDEX_V(p_key_idx, tt->transforms.size(), -1);
return tt->transforms[p_key_idx].transition;
@@ -1302,7 +1302,7 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM: {
+ case TYPE_TRANSFORM3D: {
TransformTrack *tt = static_cast<TransformTrack *>(t);
ERR_FAIL_INDEX(p_key_idx, tt->transforms.size());
@@ -1380,12 +1380,12 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p
emit_changed();
}
-void Animation::track_set_key_transition(int p_track, int p_key_idx, float p_transition) {
+void Animation::track_set_key_transition(int p_track, int p_key_idx, real_t p_transition) {
ERR_FAIL_INDEX(p_track, tracks.size());
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM: {
+ case TYPE_TRANSFORM3D: {
TransformTrack *tt = static_cast<TransformTrack *>(t);
ERR_FAIL_INDEX(p_key_idx, tt->transforms.size());
tt->transforms.write[p_key_idx].transition = p_transition;
@@ -1413,7 +1413,7 @@ void Animation::track_set_key_transition(int p_track, int p_key_idx, float p_tra
}
template <class K>
-int Animation::_find(const Vector<K> &p_keys, float p_time) const {
+int Animation::_find(const Vector<K> &p_keys, double p_time) const {
int len = p_keys.size();
if (len == 0) {
return -2;
@@ -1434,7 +1434,7 @@ int Animation::_find(const Vector<K> &p_keys, float p_time) const {
while (low <= high) {
middle = (low + high) / 2;
- if (Math::is_equal_approx(p_time, keys[middle].time)) { //match
+ if (Math::is_equal_approx(p_time, (double)keys[middle].time)) { //match
return middle;
} else if (p_time < keys[middle].time) {
high = middle - 1; //search low end of array
@@ -1450,7 +1450,7 @@ int Animation::_find(const Vector<K> &p_keys, float p_time) const {
return middle;
}
-Animation::TransformKey Animation::_interpolate(const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, float p_c) const {
+Animation::TransformKey Animation::_interpolate(const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, real_t p_c) const {
TransformKey ret;
ret.loc = _interpolate(p_a.loc, p_b.loc, p_c);
ret.rot = _interpolate(p_a.rot, p_b.rot, p_c);
@@ -1459,25 +1459,25 @@ Animation::TransformKey Animation::_interpolate(const Animation::TransformKey &p
return ret;
}
-Vector3 Animation::_interpolate(const Vector3 &p_a, const Vector3 &p_b, float p_c) const {
+Vector3 Animation::_interpolate(const Vector3 &p_a, const Vector3 &p_b, real_t p_c) const {
return p_a.lerp(p_b, p_c);
}
-Quat Animation::_interpolate(const Quat &p_a, const Quat &p_b, float p_c) const {
+Quaternion Animation::_interpolate(const Quaternion &p_a, const Quaternion &p_b, real_t p_c) const {
return p_a.slerp(p_b, p_c);
}
-Variant Animation::_interpolate(const Variant &p_a, const Variant &p_b, float p_c) const {
+Variant Animation::_interpolate(const Variant &p_a, const Variant &p_b, real_t p_c) const {
Variant dst;
Variant::interpolate(p_a, p_b, p_c, dst);
return dst;
}
-float Animation::_interpolate(const float &p_a, const float &p_b, float p_c) const {
+real_t Animation::_interpolate(const real_t &p_a, const real_t &p_b, real_t p_c) const {
return p_a * (1.0 - p_c) + p_b * p_c;
}
-Animation::TransformKey Animation::_cubic_interpolate(const Animation::TransformKey &p_pre_a, const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, const Animation::TransformKey &p_post_b, float p_c) const {
+Animation::TransformKey Animation::_cubic_interpolate(const Animation::TransformKey &p_pre_a, const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, const Animation::TransformKey &p_post_b, real_t p_c) const {
Animation::TransformKey tk;
tk.loc = p_a.loc.cubic_interpolate(p_b.loc, p_pre_a.loc, p_post_b.loc, p_c);
@@ -1487,15 +1487,15 @@ Animation::TransformKey Animation::_cubic_interpolate(const Animation::Transform
return tk;
}
-Vector3 Animation::_cubic_interpolate(const Vector3 &p_pre_a, const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_post_b, float p_c) const {
+Vector3 Animation::_cubic_interpolate(const Vector3 &p_pre_a, const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_post_b, real_t p_c) const {
return p_a.cubic_interpolate(p_b, p_pre_a, p_post_b, p_c);
}
-Quat Animation::_cubic_interpolate(const Quat &p_pre_a, const Quat &p_a, const Quat &p_b, const Quat &p_post_b, float p_c) const {
+Quaternion Animation::_cubic_interpolate(const Quaternion &p_pre_a, const Quaternion &p_a, const Quaternion &p_b, const Quaternion &p_post_b, real_t p_c) const {
return p_a.cubic_slerp(p_b, p_pre_a, p_post_b, p_c);
}
-Variant Animation::_cubic_interpolate(const Variant &p_pre_a, const Variant &p_a, const Variant &p_b, const Variant &p_post_b, float p_c) const {
+Variant Animation::_cubic_interpolate(const Variant &p_pre_a, const Variant &p_a, const Variant &p_b, const Variant &p_post_b, real_t p_c) const {
Variant::Type type_a = p_a.get_type();
Variant::Type type_b = p_b.get_type();
Variant::Type type_pa = p_pre_a.get_type();
@@ -1516,9 +1516,9 @@ Variant Animation::_cubic_interpolate(const Variant &p_pre_a, const Variant &p_a
real_t p2 = p_b;
real_t p3 = p_post_b;
- float t = p_c;
- float t2 = t * t;
- float t3 = t2 * t;
+ real_t t = p_c;
+ real_t t2 = t * t;
+ real_t t3 = t2 * t;
return 0.5f * ((p1 * 2.0f) +
(-p0 + p2) * t +
@@ -1556,11 +1556,11 @@ Variant Animation::_cubic_interpolate(const Variant &p_pre_a, const Variant &p_a
return a.cubic_interpolate(b, pa, pb, p_c);
}
- case Variant::QUAT: {
- Quat a = p_a;
- Quat b = p_b;
- Quat pa = p_pre_a;
- Quat pb = p_post_b;
+ case Variant::QUATERNION: {
+ Quaternion a = p_a;
+ Quaternion b = p_b;
+ Quaternion pa = p_pre_a;
+ Quaternion pb = p_post_b;
return a.cubic_slerp(b, pa, pb, p_c);
}
@@ -1580,12 +1580,12 @@ Variant Animation::_cubic_interpolate(const Variant &p_pre_a, const Variant &p_a
}
}
-float Animation::_cubic_interpolate(const float &p_pre_a, const float &p_a, const float &p_b, const float &p_post_b, float p_c) const {
+real_t Animation::_cubic_interpolate(const real_t &p_pre_a, const real_t &p_a, const real_t &p_b, const real_t &p_post_b, real_t p_c) const {
return _interpolate(p_a, p_b, p_c);
}
template <class T>
-T Animation::_interpolate(const Vector<TKey<T>> &p_keys, float p_time, InterpolationType p_interp, bool p_loop_wrap, bool *p_ok) const {
+T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, InterpolationType p_interp, bool p_loop_wrap, bool *p_ok) const {
int len = _find(p_keys, length) + 1; // try to find last key (there may be more past the end)
if (len <= 0) {
@@ -1609,7 +1609,7 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, float p_time, Interpola
bool result = true;
int next = 0;
- float c = 0;
+ real_t c = 0.0;
// prepare for all cases of interpolation
if (loop && p_loop_wrap) {
@@ -1617,8 +1617,8 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, float p_time, Interpola
if (idx >= 0) {
if ((idx + 1) < len) {
next = idx + 1;
- float delta = p_keys[next].time - p_keys[idx].time;
- float from = p_time - p_keys[idx].time;
+ real_t delta = p_keys[next].time - p_keys[idx].time;
+ real_t from = p_time - p_keys[idx].time;
if (Math::is_zero_approx(delta)) {
c = 0;
@@ -1628,8 +1628,8 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, float p_time, Interpola
} else {
next = 0;
- float delta = (length - p_keys[idx].time) + p_keys[next].time;
- float from = p_time - p_keys[idx].time;
+ real_t delta = (length - p_keys[idx].time) + p_keys[next].time;
+ real_t from = p_time - p_keys[idx].time;
if (Math::is_zero_approx(delta)) {
c = 0;
@@ -1642,12 +1642,12 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, float p_time, Interpola
// on loop, behind first key
idx = len - 1;
next = 0;
- float endtime = (length - p_keys[idx].time);
+ real_t endtime = (length - p_keys[idx].time);
if (endtime < 0) { // may be keys past the end
endtime = 0;
}
- float delta = endtime + p_keys[next].time;
- float from = endtime + p_time;
+ real_t delta = endtime + p_keys[next].time;
+ real_t from = endtime + p_time;
if (Math::is_zero_approx(delta)) {
c = 0;
@@ -1661,8 +1661,8 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, float p_time, Interpola
if (idx >= 0) {
if ((idx + 1) < len) {
next = idx + 1;
- float delta = p_keys[next].time - p_keys[idx].time;
- float from = p_time - p_keys[idx].time;
+ real_t delta = p_keys[next].time - p_keys[idx].time;
+ real_t from = p_time - p_keys[idx].time;
if (Math::is_zero_approx(delta)) {
c = 0;
@@ -1691,7 +1691,7 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, float p_time, Interpola
return T();
}
- float tr = p_keys[idx].transition;
+ real_t tr = p_keys[idx].transition;
if (tr == 0 || idx == next) {
// don't interpolate if not needed
@@ -1729,10 +1729,10 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, float p_time, Interpola
// do a barrel roll
}
-Error Animation::transform_track_interpolate(int p_track, float p_time, Vector3 *r_loc, Quat *r_rot, Vector3 *r_scale) const {
+Error Animation::transform_track_interpolate(int p_track, double p_time, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
Track *t = tracks[p_track];
- ERR_FAIL_COND_V(t->type != TYPE_TRANSFORM, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(t->type != TYPE_TRANSFORM3D, ERR_INVALID_PARAMETER);
TransformTrack *tt = static_cast<TransformTrack *>(t);
@@ -1759,7 +1759,7 @@ Error Animation::transform_track_interpolate(int p_track, float p_time, Vector3
return OK;
}
-Variant Animation::value_track_interpolate(int p_track, float p_time) const {
+Variant Animation::value_track_interpolate(int p_track, double p_time) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), 0);
Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_VALUE, Variant());
@@ -1776,7 +1776,7 @@ Variant Animation::value_track_interpolate(int p_track, float p_time) const {
return Variant();
}
-void Animation::_value_track_get_key_indices_in_range(const ValueTrack *vt, float from_time, float to_time, List<int> *p_indices) const {
+void Animation::_value_track_get_key_indices_in_range(const ValueTrack *vt, double from_time, double to_time, List<int> *p_indices) const {
if (from_time != length && to_time == length) {
to_time = length * 1.001; //include a little more if at the end
}
@@ -1813,15 +1813,15 @@ void Animation::_value_track_get_key_indices_in_range(const ValueTrack *vt, floa
}
}
-void Animation::value_track_get_key_indices(int p_track, float p_time, float p_delta, List<int> *p_indices) const {
+void Animation::value_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices) const {
ERR_FAIL_INDEX(p_track, tracks.size());
Track *t = tracks[p_track];
ERR_FAIL_COND(t->type != TYPE_VALUE);
ValueTrack *vt = static_cast<ValueTrack *>(t);
- float from_time = p_time - p_delta;
- float to_time = p_time;
+ double from_time = p_time - p_delta;
+ double to_time = p_time;
if (from_time > to_time) {
SWAP(from_time, to_time);
@@ -1876,7 +1876,7 @@ Animation::UpdateMode Animation::value_track_get_update_mode(int p_track) const
}
template <class T>
-void Animation::_track_get_key_indices_in_range(const Vector<T> &p_array, float from_time, float to_time, List<int> *p_indices) const {
+void Animation::_track_get_key_indices_in_range(const Vector<T> &p_array, double from_time, double to_time, List<int> *p_indices) const {
if (from_time != length && to_time == length) {
to_time = length * 1.01; //include a little more if at the end
}
@@ -1909,12 +1909,12 @@ void Animation::_track_get_key_indices_in_range(const Vector<T> &p_array, float
}
}
-void Animation::track_get_key_indices_in_range(int p_track, float p_time, float p_delta, List<int> *p_indices) const {
+void Animation::track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices) const {
ERR_FAIL_INDEX(p_track, tracks.size());
const Track *t = tracks[p_track];
- float from_time = p_time - p_delta;
- float to_time = p_time;
+ double from_time = p_time - p_delta;
+ double to_time = p_time;
if (from_time > to_time) {
SWAP(from_time, to_time);
@@ -1933,7 +1933,7 @@ void Animation::track_get_key_indices_in_range(int p_track, float p_time, float
// handle loop by splitting
switch (t->type) {
- case TYPE_TRANSFORM: {
+ case TYPE_TRANSFORM3D: {
const TransformTrack *tt = static_cast<const TransformTrack *>(t);
_track_get_key_indices_in_range(tt->transforms, from_time, length, p_indices);
_track_get_key_indices_in_range(tt->transforms, 0, to_time, p_indices);
@@ -1989,7 +1989,7 @@ void Animation::track_get_key_indices_in_range(int p_track, float p_time, float
}
switch (t->type) {
- case TYPE_TRANSFORM: {
+ case TYPE_TRANSFORM3D: {
const TransformTrack *tt = static_cast<const TransformTrack *>(t);
_track_get_key_indices_in_range(tt->transforms, from_time, to_time, p_indices);
@@ -2022,7 +2022,7 @@ void Animation::track_get_key_indices_in_range(int p_track, float p_time, float
}
}
-void Animation::_method_track_get_key_indices_in_range(const MethodTrack *mt, float from_time, float to_time, List<int> *p_indices) const {
+void Animation::_method_track_get_key_indices_in_range(const MethodTrack *mt, double from_time, double to_time, List<int> *p_indices) const {
if (from_time != length && to_time == length) {
to_time = length * 1.01; //include a little more if at the end
}
@@ -2055,15 +2055,15 @@ void Animation::_method_track_get_key_indices_in_range(const MethodTrack *mt, fl
}
}
-void Animation::method_track_get_key_indices(int p_track, float p_time, float p_delta, List<int> *p_indices) const {
+void Animation::method_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices) const {
ERR_FAIL_INDEX(p_track, tracks.size());
Track *t = tracks[p_track];
ERR_FAIL_COND(t->type != TYPE_METHOD);
MethodTrack *mt = static_cast<MethodTrack *>(t);
- float from_time = p_time - p_delta;
- float to_time = p_time;
+ double from_time = p_time - p_delta;
+ double to_time = p_time;
if (from_time > to_time) {
SWAP(from_time, to_time);
@@ -2129,7 +2129,7 @@ StringName Animation::method_track_get_name(int p_track, int p_key_idx) const {
return pm->methods[p_key_idx].method;
}
-int Animation::bezier_track_insert_key(int p_track, float p_time, float p_value, const Vector2 &p_in_handle, const Vector2 &p_out_handle) {
+int Animation::bezier_track_insert_key(int p_track, double p_time, real_t p_value, const Vector2 &p_in_handle, const Vector2 &p_out_handle) {
ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_BEZIER, -1);
@@ -2155,7 +2155,7 @@ int Animation::bezier_track_insert_key(int p_track, float p_time, float p_value,
return key;
}
-void Animation::bezier_track_set_key_value(int p_track, int p_index, float p_value) {
+void Animation::bezier_track_set_key_value(int p_track, int p_index, real_t p_value) {
ERR_FAIL_INDEX(p_track, tracks.size());
Track *t = tracks[p_track];
ERR_FAIL_COND(t->type != TYPE_BEZIER);
@@ -2200,7 +2200,7 @@ void Animation::bezier_track_set_key_out_handle(int p_track, int p_index, const
emit_changed();
}
-float Animation::bezier_track_get_key_value(int p_track, int p_index) const {
+real_t Animation::bezier_track_get_key_value(int p_track, int p_index) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), 0);
Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_BEZIER, 0);
@@ -2247,7 +2247,7 @@ static _FORCE_INLINE_ Vector2 _bezier_interp(real_t t, const Vector2 &start, con
return start * omt3 + control_1 * omt2 * t * 3.0 + control_2 * omt * t2 * 3.0 + end * t3;
}
-float Animation::bezier_track_interpolate(int p_track, float p_time) const {
+real_t Animation::bezier_track_interpolate(int p_track, double p_time) const {
//this uses a different interpolation scheme
ERR_FAIL_INDEX_V(p_track, tracks.size(), 0);
Track *track = tracks[p_track];
@@ -2278,14 +2278,14 @@ float Animation::bezier_track_interpolate(int p_track, float p_time) const {
return bt->values[bt->values.size() - 1].value.value;
}
- float t = p_time - bt->values[idx].time;
+ double t = p_time - bt->values[idx].time;
int iterations = 10;
- float duration = bt->values[idx + 1].time - bt->values[idx].time; // time duration between our two keyframes
- float low = 0; // 0% of the current animation segment
- float high = 1; // 100% of the current animation segment
- float middle;
+ real_t duration = bt->values[idx + 1].time - bt->values[idx].time; // time duration between our two keyframes
+ real_t low = 0.0; // 0% of the current animation segment
+ real_t high = 1.0; // 100% of the current animation segment
+ real_t middle;
Vector2 start(0, bt->values[idx].value.value);
Vector2 start_out = start + bt->values[idx].value.out_handle;
@@ -2308,12 +2308,12 @@ float Animation::bezier_track_interpolate(int p_track, float p_time) const {
//interpolate the result:
Vector2 low_pos = _bezier_interp(low, start, start_out, end_in, end);
Vector2 high_pos = _bezier_interp(high, start, start_out, end_in, end);
- float c = (t - low_pos.x) / (high_pos.x - low_pos.x);
+ real_t c = (t - low_pos.x) / (high_pos.x - low_pos.x);
return low_pos.lerp(high_pos, c).y;
}
-int Animation::audio_track_insert_key(int p_track, float p_time, const RES &p_stream, float p_start_offset, float p_end_offset) {
+int Animation::audio_track_insert_key(int p_track, double p_time, const RES &p_stream, real_t p_start_offset, real_t p_end_offset) {
ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_AUDIO, -1);
@@ -2353,7 +2353,7 @@ void Animation::audio_track_set_key_stream(int p_track, int p_key, const RES &p_
emit_changed();
}
-void Animation::audio_track_set_key_start_offset(int p_track, int p_key, float p_offset) {
+void Animation::audio_track_set_key_start_offset(int p_track, int p_key, real_t p_offset) {
ERR_FAIL_INDEX(p_track, tracks.size());
Track *t = tracks[p_track];
ERR_FAIL_COND(t->type != TYPE_AUDIO);
@@ -2371,7 +2371,7 @@ void Animation::audio_track_set_key_start_offset(int p_track, int p_key, float p
emit_changed();
}
-void Animation::audio_track_set_key_end_offset(int p_track, int p_key, float p_offset) {
+void Animation::audio_track_set_key_end_offset(int p_track, int p_key, real_t p_offset) {
ERR_FAIL_INDEX(p_track, tracks.size());
Track *t = tracks[p_track];
ERR_FAIL_COND(t->type != TYPE_AUDIO);
@@ -2401,7 +2401,7 @@ RES Animation::audio_track_get_key_stream(int p_track, int p_key) const {
return at->values[p_key].value.stream;
}
-float Animation::audio_track_get_key_start_offset(int p_track, int p_key) const {
+real_t Animation::audio_track_get_key_start_offset(int p_track, int p_key) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), 0);
const Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_AUDIO, 0);
@@ -2413,7 +2413,7 @@ float Animation::audio_track_get_key_start_offset(int p_track, int p_key) const
return at->values[p_key].value.start_offset;
}
-float Animation::audio_track_get_key_end_offset(int p_track, int p_key) const {
+real_t Animation::audio_track_get_key_end_offset(int p_track, int p_key) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), 0);
const Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_AUDIO, 0);
@@ -2427,7 +2427,7 @@ float Animation::audio_track_get_key_end_offset(int p_track, int p_key) const {
//
-int Animation::animation_track_insert_key(int p_track, float p_time, const StringName &p_animation) {
+int Animation::animation_track_insert_key(int p_track, double p_time, const StringName &p_animation) {
ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_ANIMATION, -1);
@@ -2471,7 +2471,7 @@ StringName Animation::animation_track_get_key_animation(int p_track, int p_key)
return at->values[p_key].value;
}
-void Animation::set_length(float p_length) {
+void Animation::set_length(real_t p_length) {
if (p_length < ANIM_MIN_LENGTH) {
p_length = ANIM_MIN_LENGTH;
}
@@ -2479,7 +2479,7 @@ void Animation::set_length(float p_length) {
emit_changed();
}
-float Animation::get_length() const {
+real_t Animation::get_length() const {
return length;
}
@@ -2559,12 +2559,12 @@ void Animation::track_swap(int p_track, int p_with_track) {
emit_signal(SceneStringNames::get_singleton()->tracks_changed);
}
-void Animation::set_step(float p_step) {
+void Animation::set_step(real_t p_step) {
step = p_step;
emit_changed();
}
-float Animation::get_step() const {
+real_t Animation::get_step() const {
return step;
}
@@ -2611,7 +2611,7 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("transform_track_insert_key", "track_idx", "time", "location", "rotation", "scale"), &Animation::transform_track_insert_key);
ClassDB::bind_method(D_METHOD("track_insert_key", "track_idx", "time", "key", "transition"), &Animation::track_insert_key, DEFVAL(1));
ClassDB::bind_method(D_METHOD("track_remove_key", "track_idx", "key_idx"), &Animation::track_remove_key);
- ClassDB::bind_method(D_METHOD("track_remove_key_at_position", "track_idx", "position"), &Animation::track_remove_key_at_position);
+ ClassDB::bind_method(D_METHOD("track_remove_key_at_time", "track_idx", "time"), &Animation::track_remove_key_at_time);
ClassDB::bind_method(D_METHOD("track_set_key_value", "track_idx", "key", "value"), &Animation::track_set_key_value);
ClassDB::bind_method(D_METHOD("track_set_key_transition", "track_idx", "key_idx", "transition"), &Animation::track_set_key_transition);
ClassDB::bind_method(D_METHOD("track_set_key_time", "track_idx", "key_idx", "time"), &Animation::track_set_key_time);
@@ -2682,7 +2682,7 @@ void Animation::_bind_methods() {
ADD_SIGNAL(MethodInfo("tracks_changed"));
BIND_ENUM_CONSTANT(TYPE_VALUE);
- BIND_ENUM_CONSTANT(TYPE_TRANSFORM);
+ BIND_ENUM_CONSTANT(TYPE_TRANSFORM3D);
BIND_ENUM_CONSTANT(TYPE_METHOD);
BIND_ENUM_CONSTANT(TYPE_BEZIER);
BIND_ENUM_CONSTANT(TYPE_AUDIO);
@@ -2709,7 +2709,7 @@ void Animation::clear() {
emit_signal(SceneStringNames::get_singleton()->tracks_changed);
}
-bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, const TKey<TransformKey> &t1, const TKey<TransformKey> &t2, float p_alowed_linear_err, float p_alowed_angular_err, float p_max_optimizable_angle, const Vector3 &p_norm) {
+bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, const TKey<TransformKey> &t1, const TKey<TransformKey> &t2, real_t p_alowed_linear_err, real_t p_alowed_angular_err, real_t p_max_optimizable_angle, const Vector3 &p_norm) {
real_t c = (t1.time - t0.time) / (t2.time - t0.time);
real_t t[3] = { -1, -1, -1 };
@@ -2728,9 +2728,9 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons
} else {
Vector3 pd = (v2 - v0);
- float d0 = pd.dot(v0);
- float d1 = pd.dot(v1);
- float d2 = pd.dot(v2);
+ real_t d0 = pd.dot(v0);
+ real_t d1 = pd.dot(v1);
+ real_t d2 = pd.dot(v2);
if (d1 < d0 || d1 > d2) {
return false;
}
@@ -2739,7 +2739,7 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons
real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1);
if (d > pd.length() * p_alowed_linear_err) {
- return false; //beyond allowed error for colinearity
+ return false; //beyond allowed error for collinearity
}
if (p_norm != Vector3() && Math::acos(pd.normalized().dot(p_norm)) > p_alowed_angular_err) {
@@ -2752,9 +2752,9 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons
{ //rotation
- const Quat &q0 = t0.value.rot;
- const Quat &q1 = t1.value.rot;
- const Quat &q2 = t2.value.rot;
+ const Quaternion &q0 = t0.value.rot;
+ const Quaternion &q1 = t1.value.rot;
+ const Quaternion &q2 = t2.value.rot;
//localize both to rotation from q0
@@ -2764,8 +2764,8 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons
}
} else {
- Quat r02 = (q0.inverse() * q2).normalized();
- Quat r01 = (q0.inverse() * q1).normalized();
+ Quaternion r02 = (q0.inverse() * q2).normalized();
+ Quaternion r01 = (q0.inverse() * q1).normalized();
Vector3 v02, v01;
real_t a02, a01;
@@ -2818,9 +2818,9 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons
} else {
Vector3 pd = (v2 - v0);
- float d0 = pd.dot(v0);
- float d1 = pd.dot(v1);
- float d2 = pd.dot(v2);
+ real_t d0 = pd.dot(v0);
+ real_t d1 = pd.dot(v1);
+ real_t d2 = pd.dot(v2);
if (d1 < d0 || d1 > d2) {
return false; //beyond segment range
}
@@ -2829,7 +2829,7 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons
real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1);
if (d > pd.length() * p_alowed_linear_err) {
- return false; //beyond allowed error for colinearity
+ return false; //beyond allowed error for collinearity
}
t[2] = (d1 - d0) / (d2 - d0);
@@ -2841,7 +2841,7 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons
erase = true;
} else {
erase = true;
- real_t lt = -1;
+ real_t lt = -1.0;
for (int j = 0; j < 3; j++) {
//search for t on first, one must be it
if (t[j] != -1) {
@@ -2876,9 +2876,9 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons
return erase;
}
-void Animation::_transform_track_optimize(int p_idx, float p_allowed_linear_err, float p_allowed_angular_err, float p_max_optimizable_angle) {
+void Animation::_transform_track_optimize(int p_idx, real_t p_allowed_linear_err, real_t p_allowed_angular_err, real_t p_max_optimizable_angle) {
ERR_FAIL_INDEX(p_idx, tracks.size());
- ERR_FAIL_COND(tracks[p_idx]->type != TYPE_TRANSFORM);
+ ERR_FAIL_COND(tracks[p_idx]->type != TYPE_TRANSFORM3D);
TransformTrack *tt = static_cast<TransformTrack *>(tracks[p_idx]);
bool prev_erased = false;
TKey<TransformKey> first_erased;
@@ -2916,19 +2916,15 @@ void Animation::_transform_track_optimize(int p_idx, float p_allowed_linear_err,
}
}
-void Animation::optimize(float p_allowed_linear_err, float p_allowed_angular_err, float p_max_optimizable_angle) {
+void Animation::optimize(real_t p_allowed_linear_err, real_t p_allowed_angular_err, real_t p_max_optimizable_angle) {
for (int i = 0; i < tracks.size(); i++) {
- if (tracks[i]->type == TYPE_TRANSFORM) {
+ if (tracks[i]->type == TYPE_TRANSFORM3D) {
_transform_track_optimize(i, p_allowed_linear_err, p_allowed_angular_err, p_max_optimizable_angle);
}
}
}
-Animation::Animation() {
- step = 0.1;
- loop = false;
- length = 1;
-}
+Animation::Animation() {}
Animation::~Animation() {
for (int i = 0; i < tracks.size(); i++) {
diff --git a/scene/resources/animation.h b/scene/resources/animation.h
index 722a400fd6..9a410bd566 100644
--- a/scene/resources/animation.h
+++ b/scene/resources/animation.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,7 +31,9 @@
#ifndef ANIMATION_H
#define ANIMATION_H
-#include "core/resource.h"
+#include "core/io/resource.h"
+
+#define ANIM_MIN_LENGTH 0.001
class Animation : public Resource {
GDCLASS(Animation, Resource);
@@ -40,7 +42,7 @@ class Animation : public Resource {
public:
enum TrackType {
TYPE_VALUE, ///< Set a value in a property, can be interpolated.
- TYPE_TRANSFORM, ///< Transform a node or a bone.
+ TYPE_TRANSFORM3D, ///< Transform a node or a bone.
TYPE_METHOD, ///< Call any method on a specific node.
TYPE_BEZIER, ///< Bezier curve
TYPE_AUDIO,
@@ -63,28 +65,19 @@ public:
private:
struct Track {
- TrackType type;
- InterpolationType interpolation;
- bool loop_wrap;
+ TrackType type = TrackType::TYPE_ANIMATION;
+ InterpolationType interpolation = INTERPOLATION_LINEAR;
+ bool loop_wrap = true;
NodePath path; // path to something
- bool imported;
- bool enabled;
- Track() {
- interpolation = INTERPOLATION_LINEAR;
- imported = false;
- loop_wrap = true;
- enabled = true;
- }
+ bool imported = false;
+ bool enabled = true;
+ Track() {}
virtual ~Track() {}
};
struct Key {
- float transition;
- float time; // time in secs
- Key() {
- transition = 1;
- time = 0;
- }
+ real_t transition = 1.0;
+ double time = 0.0; // time in secs
};
// transform key holds either Vector3 or Quaternion
@@ -95,28 +88,30 @@ private:
struct TransformKey {
Vector3 loc;
- Quat rot;
+ Quaternion rot;
Vector3 scale;
};
+ // Not necessarily the same size as Transform3D. The amount of numbers in Animation::Key and TransformKey.
+ const int32_t TRANSFORM_TRACK_SIZE = 12;
+
/* TRANSFORM TRACK */
struct TransformTrack : public Track {
Vector<TKey<TransformKey>> transforms;
- TransformTrack() { type = TYPE_TRANSFORM; }
+ TransformTrack() { type = TYPE_TRANSFORM3D; }
};
/* PROPERTY VALUE TRACK */
struct ValueTrack : public Track {
- UpdateMode update_mode;
- bool update_on_seek;
+ UpdateMode update_mode = UPDATE_CONTINUOUS;
+ bool update_on_seek = false;
Vector<TKey<Variant>> values;
ValueTrack() {
type = TYPE_VALUE;
- update_mode = UPDATE_CONTINUOUS;
}
};
@@ -137,7 +132,7 @@ private:
struct BezierKey {
Vector2 in_handle; //relative (x always <0)
Vector2 out_handle; //relative (x always >0)
- float value;
+ real_t value = 0.0;
};
struct BezierTrack : public Track {
@@ -152,11 +147,9 @@ private:
struct AudioKey {
RES stream;
- float start_offset; //offset from start
- float end_offset; //offset from end, if 0 then full length or infinite
+ real_t start_offset = 0.0; //offset from start
+ real_t end_offset = 0.0; //offset from end, if 0 then full length or infinite
AudioKey() {
- start_offset = 0;
- end_offset = 0;
}
};
@@ -182,48 +175,48 @@ private:
/*
template<class T>
- int _insert_pos(float p_time, T& p_keys);*/
+ int _insert_pos(double p_time, T& p_keys);*/
template <class T>
void _clear(T &p_keys);
template <class T, class V>
- int _insert(float p_time, T &p_keys, const V &p_value);
+ int _insert(double p_time, T &p_keys, const V &p_value);
template <class K>
- inline int _find(const Vector<K> &p_keys, float p_time) const;
+ inline int _find(const Vector<K> &p_keys, double p_time) const;
- _FORCE_INLINE_ Animation::TransformKey _interpolate(const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, float p_c) const;
+ _FORCE_INLINE_ Animation::TransformKey _interpolate(const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, real_t p_c) const;
- _FORCE_INLINE_ Vector3 _interpolate(const Vector3 &p_a, const Vector3 &p_b, float p_c) const;
- _FORCE_INLINE_ Quat _interpolate(const Quat &p_a, const Quat &p_b, float p_c) const;
- _FORCE_INLINE_ Variant _interpolate(const Variant &p_a, const Variant &p_b, float p_c) const;
- _FORCE_INLINE_ float _interpolate(const float &p_a, const float &p_b, float p_c) const;
+ _FORCE_INLINE_ Vector3 _interpolate(const Vector3 &p_a, const Vector3 &p_b, real_t p_c) const;
+ _FORCE_INLINE_ Quaternion _interpolate(const Quaternion &p_a, const Quaternion &p_b, real_t p_c) const;
+ _FORCE_INLINE_ Variant _interpolate(const Variant &p_a, const Variant &p_b, real_t p_c) const;
+ _FORCE_INLINE_ real_t _interpolate(const real_t &p_a, const real_t &p_b, real_t p_c) const;
- _FORCE_INLINE_ Animation::TransformKey _cubic_interpolate(const Animation::TransformKey &p_pre_a, const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, const Animation::TransformKey &p_post_b, float p_c) const;
- _FORCE_INLINE_ Vector3 _cubic_interpolate(const Vector3 &p_pre_a, const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_post_b, float p_c) const;
- _FORCE_INLINE_ Quat _cubic_interpolate(const Quat &p_pre_a, const Quat &p_a, const Quat &p_b, const Quat &p_post_b, float p_c) const;
- _FORCE_INLINE_ Variant _cubic_interpolate(const Variant &p_pre_a, const Variant &p_a, const Variant &p_b, const Variant &p_post_b, float p_c) const;
- _FORCE_INLINE_ float _cubic_interpolate(const float &p_pre_a, const float &p_a, const float &p_b, const float &p_post_b, float p_c) const;
+ _FORCE_INLINE_ Animation::TransformKey _cubic_interpolate(const Animation::TransformKey &p_pre_a, const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, const Animation::TransformKey &p_post_b, real_t p_c) const;
+ _FORCE_INLINE_ Vector3 _cubic_interpolate(const Vector3 &p_pre_a, const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_post_b, real_t p_c) const;
+ _FORCE_INLINE_ Quaternion _cubic_interpolate(const Quaternion &p_pre_a, const Quaternion &p_a, const Quaternion &p_b, const Quaternion &p_post_b, real_t p_c) const;
+ _FORCE_INLINE_ Variant _cubic_interpolate(const Variant &p_pre_a, const Variant &p_a, const Variant &p_b, const Variant &p_post_b, real_t p_c) const;
+ _FORCE_INLINE_ real_t _cubic_interpolate(const real_t &p_pre_a, const real_t &p_a, const real_t &p_b, const real_t &p_post_b, real_t p_c) const;
template <class T>
- _FORCE_INLINE_ T _interpolate(const Vector<TKey<T>> &p_keys, float p_time, InterpolationType p_interp, bool p_loop_wrap, bool *p_ok) const;
+ _FORCE_INLINE_ T _interpolate(const Vector<TKey<T>> &p_keys, double p_time, InterpolationType p_interp, bool p_loop_wrap, bool *p_ok) const;
template <class T>
- _FORCE_INLINE_ void _track_get_key_indices_in_range(const Vector<T> &p_array, float from_time, float to_time, List<int> *p_indices) const;
+ _FORCE_INLINE_ void _track_get_key_indices_in_range(const Vector<T> &p_array, double from_time, double to_time, List<int> *p_indices) const;
- _FORCE_INLINE_ void _value_track_get_key_indices_in_range(const ValueTrack *vt, float from_time, float to_time, List<int> *p_indices) const;
- _FORCE_INLINE_ void _method_track_get_key_indices_in_range(const MethodTrack *mt, float from_time, float to_time, List<int> *p_indices) const;
+ _FORCE_INLINE_ void _value_track_get_key_indices_in_range(const ValueTrack *vt, double from_time, double to_time, List<int> *p_indices) const;
+ _FORCE_INLINE_ void _method_track_get_key_indices_in_range(const MethodTrack *mt, double from_time, double to_time, List<int> *p_indices) const;
- float length;
- float step;
- bool loop;
+ double length = 1.0;
+ real_t step = 0.1;
+ bool loop = false;
// bind helpers
private:
- Array _transform_track_interpolate(int p_track, float p_time) const {
+ Array _transform_track_interpolate(int p_track, double p_time) const {
Vector3 loc;
- Quat rot;
+ Quaternion rot;
Vector3 scale;
transform_track_interpolate(p_track, p_time, &loc, &rot, &scale);
Array ret;
@@ -233,35 +226,37 @@ private:
return ret;
}
- Vector<int> _value_track_get_key_indices(int p_track, float p_time, float p_delta) const {
+ Vector<int> _value_track_get_key_indices(int p_track, double p_time, double p_delta) const {
List<int> idxs;
value_track_get_key_indices(p_track, p_time, p_delta, &idxs);
Vector<int> idxr;
- for (List<int>::Element *E = idxs.front(); E; E = E->next()) {
- idxr.push_back(E->get());
+ for (int &E : idxs) {
+ idxr.push_back(E);
}
return idxr;
}
- Vector<int> _method_track_get_key_indices(int p_track, float p_time, float p_delta) const {
+ Vector<int> _method_track_get_key_indices(int p_track, double p_time, double p_delta) const {
List<int> idxs;
method_track_get_key_indices(p_track, p_time, p_delta, &idxs);
Vector<int> idxr;
- for (List<int>::Element *E = idxs.front(); E; E = E->next()) {
- idxr.push_back(E->get());
+ for (int &E : idxs) {
+ idxr.push_back(E);
}
return idxr;
}
- bool _transform_track_optimize_key(const TKey<TransformKey> &t0, const TKey<TransformKey> &t1, const TKey<TransformKey> &t2, float p_alowed_linear_err, float p_alowed_angular_err, float p_max_optimizable_angle, const Vector3 &p_norm);
- void _transform_track_optimize(int p_idx, float p_allowed_linear_err = 0.05, float p_allowed_angular_err = 0.01, float p_max_optimizable_angle = Math_PI * 0.125);
+ bool _transform_track_optimize_key(const TKey<TransformKey> &t0, const TKey<TransformKey> &t1, const TKey<TransformKey> &t2, real_t p_alowed_linear_err, real_t p_alowed_angular_err, real_t p_max_optimizable_angle, const Vector3 &p_norm);
+ void _transform_track_optimize(int p_idx, real_t p_allowed_linear_err = 0.05, real_t p_allowed_angular_err = 0.01, real_t p_max_optimizable_angle = Math_PI * 0.125);
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
+ virtual void reset_state() override;
+
static void _bind_methods();
public:
@@ -287,75 +282,75 @@ public:
void track_set_enabled(int p_track, bool p_enabled);
bool track_is_enabled(int p_track) const;
- void track_insert_key(int p_track, float p_time, const Variant &p_key, float p_transition = 1);
- void track_set_key_transition(int p_track, int p_key_idx, float p_transition);
+ void track_insert_key(int p_track, double p_time, const Variant &p_key, real_t p_transition = 1);
+ void track_set_key_transition(int p_track, int p_key_idx, real_t p_transition);
void track_set_key_value(int p_track, int p_key_idx, const Variant &p_value);
- void track_set_key_time(int p_track, int p_key_idx, float p_time);
- int track_find_key(int p_track, float p_time, bool p_exact = false) const;
+ void track_set_key_time(int p_track, int p_key_idx, double p_time);
+ int track_find_key(int p_track, double p_time, bool p_exact = false) const;
void track_remove_key(int p_track, int p_idx);
- void track_remove_key_at_position(int p_track, float p_pos);
+ void track_remove_key_at_time(int p_track, double p_time);
int track_get_key_count(int p_track) const;
Variant track_get_key_value(int p_track, int p_key_idx) const;
- float track_get_key_time(int p_track, int p_key_idx) const;
- float track_get_key_transition(int p_track, int p_key_idx) const;
+ double track_get_key_time(int p_track, int p_key_idx) const;
+ real_t track_get_key_transition(int p_track, int p_key_idx) const;
- int transform_track_insert_key(int p_track, float p_time, const Vector3 &p_loc, const Quat &p_rot = Quat(), const Vector3 &p_scale = Vector3());
- Error transform_track_get_key(int p_track, int p_key, Vector3 *r_loc, Quat *r_rot, Vector3 *r_scale) const;
+ int transform_track_insert_key(int p_track, double p_time, const Vector3 &p_loc, const Quaternion &p_rot = Quaternion(), const Vector3 &p_scale = Vector3());
+ Error transform_track_get_key(int p_track, int p_key, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const;
void track_set_interpolation_type(int p_track, InterpolationType p_interp);
InterpolationType track_get_interpolation_type(int p_track) const;
- int bezier_track_insert_key(int p_track, float p_time, float p_value, const Vector2 &p_in_handle, const Vector2 &p_out_handle);
- void bezier_track_set_key_value(int p_track, int p_index, float p_value);
+ int bezier_track_insert_key(int p_track, double p_time, real_t p_value, const Vector2 &p_in_handle, const Vector2 &p_out_handle);
+ void bezier_track_set_key_value(int p_track, int p_index, real_t p_value);
void bezier_track_set_key_in_handle(int p_track, int p_index, const Vector2 &p_handle);
void bezier_track_set_key_out_handle(int p_track, int p_index, const Vector2 &p_handle);
- float bezier_track_get_key_value(int p_track, int p_index) const;
+ real_t bezier_track_get_key_value(int p_track, int p_index) const;
Vector2 bezier_track_get_key_in_handle(int p_track, int p_index) const;
Vector2 bezier_track_get_key_out_handle(int p_track, int p_index) const;
- float bezier_track_interpolate(int p_track, float p_time) const;
+ real_t bezier_track_interpolate(int p_track, double p_time) const;
- int audio_track_insert_key(int p_track, float p_time, const RES &p_stream, float p_start_offset = 0, float p_end_offset = 0);
+ int audio_track_insert_key(int p_track, double p_time, const RES &p_stream, real_t p_start_offset = 0, real_t p_end_offset = 0);
void audio_track_set_key_stream(int p_track, int p_key, const RES &p_stream);
- void audio_track_set_key_start_offset(int p_track, int p_key, float p_offset);
- void audio_track_set_key_end_offset(int p_track, int p_key, float p_offset);
+ void audio_track_set_key_start_offset(int p_track, int p_key, real_t p_offset);
+ void audio_track_set_key_end_offset(int p_track, int p_key, real_t p_offset);
RES audio_track_get_key_stream(int p_track, int p_key) const;
- float audio_track_get_key_start_offset(int p_track, int p_key) const;
- float audio_track_get_key_end_offset(int p_track, int p_key) const;
+ real_t audio_track_get_key_start_offset(int p_track, int p_key) const;
+ real_t audio_track_get_key_end_offset(int p_track, int p_key) const;
- int animation_track_insert_key(int p_track, float p_time, const StringName &p_animation);
+ int animation_track_insert_key(int p_track, double p_time, const StringName &p_animation);
void animation_track_set_key_animation(int p_track, int p_key, const StringName &p_animation);
StringName animation_track_get_key_animation(int p_track, int p_key) const;
void track_set_interpolation_loop_wrap(int p_track, bool p_enable);
bool track_get_interpolation_loop_wrap(int p_track) const;
- Error transform_track_interpolate(int p_track, float p_time, Vector3 *r_loc, Quat *r_rot, Vector3 *r_scale) const;
+ Error transform_track_interpolate(int p_track, double p_time, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const;
- Variant value_track_interpolate(int p_track, float p_time) const;
- void value_track_get_key_indices(int p_track, float p_time, float p_delta, List<int> *p_indices) const;
+ Variant value_track_interpolate(int p_track, double p_time) const;
+ void value_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices) const;
void value_track_set_update_mode(int p_track, UpdateMode p_mode);
UpdateMode value_track_get_update_mode(int p_track) const;
- void method_track_get_key_indices(int p_track, float p_time, float p_delta, List<int> *p_indices) const;
+ void method_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices) const;
Vector<Variant> method_track_get_params(int p_track, int p_key_idx) const;
StringName method_track_get_name(int p_track, int p_key_idx) const;
void copy_track(int p_track, Ref<Animation> p_to_animation);
- void track_get_key_indices_in_range(int p_track, float p_time, float p_delta, List<int> *p_indices) const;
+ void track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices) const;
- void set_length(float p_length);
- float get_length() const;
+ void set_length(real_t p_length);
+ real_t get_length() const;
void set_loop(bool p_enabled);
bool has_loop() const;
- void set_step(float p_step);
- float get_step() const;
+ void set_step(real_t p_step);
+ real_t get_step() const;
void clear();
- void optimize(float p_allowed_linear_err = 0.05, float p_allowed_angular_err = 0.01, float p_max_optimizable_angle = Math_PI * 0.125);
+ void optimize(real_t p_allowed_linear_err = 0.05, real_t p_allowed_angular_err = 0.01, real_t p_max_optimizable_angle = Math_PI * 0.125);
Animation();
~Animation();
diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_sample.cpp
index f02e7987a9..2ab9b7b5a4 100644
--- a/scene/resources/audio_stream_sample.cpp
+++ b/scene/resources/audio_stream_sample.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,8 +30,8 @@
#include "audio_stream_sample.h"
+#include "core/io/file_access.h"
#include "core/io/marshalls.h"
-#include "core/os/file_access.h"
void AudioStreamPlaybackSample::start(float p_from_pos) {
if (base->format == AudioStreamSample::FORMAT_IMA_ADPCM) {
@@ -221,12 +221,12 @@ void AudioStreamPlaybackSample::do_resample(const Depth *p_src, AudioFrame *p_ds
}
}
-void AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) {
+int AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) {
if (!base->data || !active) {
for (int i = 0; i < p_frames; i++) {
p_buffer[i] = AudioFrame(0, 0);
}
- return;
+ return 0;
}
int len = base->data_bytes;
@@ -264,7 +264,8 @@ void AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, in
float base_rate = AudioServer::get_singleton()->get_mix_rate();
float srate = base->mix_rate;
srate *= p_rate_scale;
- float fincrement = srate / base_rate;
+ float playback_speed_scale = AudioServer::get_singleton()->get_playback_speed_scale();
+ float fincrement = (srate * playback_speed_scale) / base_rate;
int32_t increment = int32_t(MAX(fincrement * MIX_FRAC_LEN, 1));
increment *= sign;
@@ -394,19 +395,18 @@ void AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, in
}
if (todo) {
+ int mixed_frames = p_frames - todo;
//bit was missing from mix
int todo_ofs = p_frames - todo;
for (int i = todo_ofs; i < p_frames; i++) {
p_buffer[i] = AudioFrame(0, 0);
}
+ return mixed_frames;
}
+ return p_frames;
}
-AudioStreamPlaybackSample::AudioStreamPlaybackSample() {
- active = false;
- offset = 0;
- sign = 1;
-}
+AudioStreamPlaybackSample::AudioStreamPlaybackSample() {}
/////////////////////
@@ -493,9 +493,9 @@ void AudioStreamSample::set_data(const Vector<uint8_t> &p_data) {
const uint8_t *r = p_data.ptr();
int alloc_len = datalen + DATA_PAD * 2;
data = memalloc(alloc_len); //alloc with some padding for interpolation
- zeromem(data, alloc_len);
+ memset(data, 0, alloc_len);
uint8_t *dataptr = (uint8_t *)data;
- copymem(dataptr + DATA_PAD, r, datalen);
+ memcpy(dataptr + DATA_PAD, r, datalen);
data_bytes = datalen;
}
@@ -510,7 +510,7 @@ Vector<uint8_t> AudioStreamSample::get_data() const {
{
uint8_t *w = pv.ptrw();
uint8_t *dataptr = (uint8_t *)data;
- copymem(w, dataptr + DATA_PAD, data_bytes);
+ memcpy(w, dataptr + DATA_PAD, data_bytes);
}
}
@@ -599,7 +599,7 @@ Error AudioStreamSample::save_to_wav(const String &p_path) {
Ref<AudioStreamPlayback> AudioStreamSample::instance_playback() {
Ref<AudioStreamPlaybackSample> sample;
- sample.instance();
+ sample.instantiate();
sample->base = Ref<AudioStreamSample>(this);
return sample;
}
@@ -650,16 +650,7 @@ void AudioStreamSample::_bind_methods() {
BIND_ENUM_CONSTANT(LOOP_BACKWARD);
}
-AudioStreamSample::AudioStreamSample() {
- format = FORMAT_8_BITS;
- loop_mode = LOOP_DISABLED;
- stereo = false;
- loop_begin = 0;
- loop_end = 0;
- mix_rate = 44100;
- data = nullptr;
- data_bytes = 0;
-}
+AudioStreamSample::AudioStreamSample() {}
AudioStreamSample::~AudioStreamSample() {
if (data) {
diff --git a/scene/resources/audio_stream_sample.h b/scene/resources/audio_stream_sample.h
index d91cdef57d..8bf3d29123 100644
--- a/scene/resources/audio_stream_sample.h
+++ b/scene/resources/audio_stream_sample.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -44,19 +44,19 @@ class AudioStreamPlaybackSample : public AudioStreamPlayback {
};
struct IMA_ADPCM_State {
- int16_t step_index;
- int32_t predictor;
+ int16_t step_index = 0;
+ int32_t predictor = 0;
/* values at loop point */
- int16_t loop_step_index;
- int32_t loop_predictor;
- int32_t last_nibble;
- int32_t loop_pos;
- int32_t window_ofs;
+ int16_t loop_step_index = 0;
+ int32_t loop_predictor = 0;
+ int32_t last_nibble = 0;
+ int32_t loop_pos = 0;
+ int32_t window_ofs = 0;
} ima_adpcm[2];
- int64_t offset;
- int sign;
- bool active;
+ int64_t offset = 0;
+ int sign = 1;
+ bool active = false;
friend class AudioStreamSample;
Ref<AudioStreamSample> base;
@@ -73,7 +73,7 @@ public:
virtual float get_playback_position() const override;
virtual void seek(float p_time) override;
- virtual void mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override;
+ virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override;
AudioStreamPlaybackSample();
};
@@ -103,14 +103,14 @@ private:
DATA_PAD = 16 //padding for interpolation
};
- Format format;
- LoopMode loop_mode;
- bool stereo;
- int loop_begin;
- int loop_end;
- int mix_rate;
- void *data;
- uint32_t data_bytes;
+ Format format = FORMAT_8_BITS;
+ LoopMode loop_mode = LOOP_DISABLED;
+ bool stereo = false;
+ int loop_begin = 0;
+ int loop_end = 0;
+ int mix_rate = 44100;
+ void *data = nullptr;
+ uint32_t data_bytes = 0;
protected:
static void _bind_methods();
diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp
index 10f0de8ff8..de557494c3 100644
--- a/scene/resources/bit_map.cpp
+++ b/scene/resources/bit_map.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -38,12 +38,12 @@ void BitMap::create(const Size2 &p_size) {
width = p_size.width;
height = p_size.height;
- bitmask.resize(((width * height) / 8) + 1);
- zeromem(bitmask.ptrw(), bitmask.size());
+ bitmask.resize((((width * height) - 1) / 8) + 1);
+ memset(bitmask.ptrw(), 0, bitmask.size());
}
void BitMap::create_from_image_alpha(const Ref<Image> &p_image, float p_threshold) {
- ERR_FAIL_COND(p_image.is_null() || p_image->empty());
+ ERR_FAIL_COND(p_image.is_null() || p_image->is_empty());
Ref<Image> img = p_image->duplicate();
img->convert(Image::FORMAT_LA8);
ERR_FAIL_COND(img->get_format() != Image::FORMAT_LA8);
@@ -63,7 +63,7 @@ void BitMap::create_from_image_alpha(const Ref<Image> &p_image, float p_threshol
}
void BitMap::set_bit_rect(const Rect2 &p_rect, bool p_value) {
- Rect2i current = Rect2i(0, 0, width, height).clip(p_rect);
+ Rect2i current = Rect2i(0, 0, width, height).intersection(p_rect);
uint8_t *data = bitmask.ptrw();
for (int i = current.position.x; i < current.position.x + current.size.x; i++) {
@@ -285,7 +285,7 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &rect, const Point2i &start)
+---+---+
| 4 | |
+---+---+
- this normally go RIGHT, but if its coming from RIGHT, it should go LEFT
+ this normally go RIGHT, but if it's coming from RIGHT, it should go LEFT
*/
if (case6s.has(Point2i(curx, cury))) {
//found, so we go left, and delete from case6s;
@@ -346,7 +346,7 @@ static Vector<Vector2> rdp(const Vector<Vector2> &v, float optimization) {
}
int index = -1;
- float dist = 0;
+ float dist = 0.0;
//not looping first and last point
for (size_t i = 1, size = v.size(); i < size - 1; ++i) {
float cdist = perpendicular_distance(v[i], v[0], v[v.size() - 1]);
@@ -406,8 +406,8 @@ static Vector<Vector2> reduce(const Vector<Vector2> &points, const Rect2i &rect,
struct FillBitsStackEntry {
Point2i pos;
- int i;
- int j;
+ int i = 0;
+ int j = 0;
};
static void fill_bits(const BitMap *p_src, Ref<BitMap> &p_map, const Point2i &p_pos, const Rect2i &rect) {
@@ -482,12 +482,12 @@ static void fill_bits(const BitMap *p_src, Ref<BitMap> &p_map, const Point2i &p_
}
Vector<Vector<Vector2>> BitMap::clip_opaque_to_polygons(const Rect2 &p_rect, float p_epsilon) const {
- Rect2i r = Rect2i(0, 0, width, height).clip(p_rect);
+ Rect2i r = Rect2i(0, 0, width, height).intersection(p_rect);
print_verbose("BitMap: Rect: " + r);
Point2i from;
Ref<BitMap> fill;
- fill.instance();
+ fill.instantiate();
fill->create(get_size());
Vector<Vector<Vector2>> polygons;
@@ -522,10 +522,10 @@ void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) {
bool bit_value = p_pixels > 0;
p_pixels = Math::abs(p_pixels);
- Rect2i r = Rect2i(0, 0, width, height).clip(p_rect);
+ Rect2i r = Rect2i(0, 0, width, height).intersection(p_rect);
Ref<BitMap> copy;
- copy.instance();
+ copy.instantiate();
copy->create(get_size());
copy->bitmask = bitmask;
@@ -604,7 +604,7 @@ Array BitMap::_opaque_to_polygons_bind(const Rect2 &p_rect, float p_epsilon) con
void BitMap::resize(const Size2 &p_new_size) {
Ref<BitMap> new_bitmap;
- new_bitmap.instance();
+ new_bitmap.instantiate();
new_bitmap->create(p_new_size);
int lw = MIN(width, p_new_size.width);
int lh = MIN(height, p_new_size.height);
@@ -621,7 +621,7 @@ void BitMap::resize(const Size2 &p_new_size) {
Ref<Image> BitMap::convert_to_image() const {
Ref<Image> image;
- image.instance();
+ image.instantiate();
image->create(width, height, false, Image::FORMAT_L8);
for (int i = 0; i < width; i++) {
@@ -677,9 +677,6 @@ void BitMap::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
}
-BitMap::BitMap() {
- width = 0;
- height = 0;
-}
+BitMap::BitMap() {}
//////////////////////////////////////
diff --git a/scene/resources/bit_map.h b/scene/resources/bit_map.h
index 59f3b4dc3c..68fd0b999a 100644
--- a/scene/resources/bit_map.h
+++ b/scene/resources/bit_map.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,17 +31,17 @@
#ifndef BIT_MAP_H
#define BIT_MAP_H
-#include "core/image.h"
+#include "core/io/image.h"
+#include "core/io/resource.h"
#include "core/io/resource_loader.h"
-#include "core/resource.h"
class BitMap : public Resource {
GDCLASS(BitMap, Resource);
OBJ_SAVE_TYPE(BitMap);
Vector<uint8_t> bitmask;
- int width;
- int height;
+ int width = 0;
+ int height = 0;
Vector<Vector2> _march_square(const Rect2i &rect, const Point2i &start) const;
diff --git a/scene/resources/box_shape_3d.cpp b/scene/resources/box_shape_3d.cpp
index e1c8a377c0..008914c5ee 100644
--- a/scene/resources/box_shape_3d.cpp
+++ b/scene/resources/box_shape_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,8 +34,8 @@
Vector<Vector3> BoxShape3D::get_debug_mesh_lines() const {
Vector<Vector3> lines;
AABB aabb;
- aabb.position = -get_extents();
- aabb.size = aabb.position * -2;
+ aabb.position = -size / 2;
+ aabb.size = size;
for (int i = 0; i < 12; i++) {
Vector3 a, b;
@@ -48,33 +48,52 @@ Vector<Vector3> BoxShape3D::get_debug_mesh_lines() const {
}
real_t BoxShape3D::get_enclosing_radius() const {
- return extents.length();
+ return size.length() / 2;
}
void BoxShape3D::_update_shape() {
- PhysicsServer3D::get_singleton()->shape_set_data(get_shape(), extents);
+ PhysicsServer3D::get_singleton()->shape_set_data(get_shape(), size / 2);
Shape3D::_update_shape();
}
-void BoxShape3D::set_extents(const Vector3 &p_extents) {
- extents = p_extents;
+#ifndef DISABLE_DEPRECATED
+bool BoxShape3D::_set(const StringName &p_name, const Variant &p_value) {
+ if (p_name == "extents") { // Compatibility with Godot 3.x.
+ // Convert to `size`, twice as big.
+ set_size((Vector3)p_value * 2);
+ return true;
+ }
+ return false;
+}
+
+bool BoxShape3D::_get(const StringName &p_name, Variant &r_property) const {
+ if (p_name == "extents") { // Compatibility with Godot 3.x.
+ // Convert to `extents`, half as big.
+ r_property = size / 2;
+ return true;
+ }
+ return false;
+}
+#endif // DISABLE_DEPRECATED
+
+void BoxShape3D::set_size(const Vector3 &p_size) {
+ size = p_size;
_update_shape();
notify_change_to_owners();
- _change_notify("extents");
}
-Vector3 BoxShape3D::get_extents() const {
- return extents;
+Vector3 BoxShape3D::get_size() const {
+ return size;
}
void BoxShape3D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_extents", "extents"), &BoxShape3D::set_extents);
- ClassDB::bind_method(D_METHOD("get_extents"), &BoxShape3D::get_extents);
+ ClassDB::bind_method(D_METHOD("set_size", "size"), &BoxShape3D::set_size);
+ ClassDB::bind_method(D_METHOD("get_size"), &BoxShape3D::get_size);
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents"), "set_extents", "get_extents");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size"), "set_size", "get_size");
}
BoxShape3D::BoxShape3D() :
Shape3D(PhysicsServer3D::get_singleton()->shape_create(PhysicsServer3D::SHAPE_BOX)) {
- set_extents(Vector3(1, 1, 1));
+ set_size(Vector3(2, 2, 2));
}
diff --git a/scene/resources/box_shape_3d.h b/scene/resources/box_shape_3d.h
index fe634ce568..91978a0e6a 100644
--- a/scene/resources/box_shape_3d.h
+++ b/scene/resources/box_shape_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,16 +35,20 @@
class BoxShape3D : public Shape3D {
GDCLASS(BoxShape3D, Shape3D);
- Vector3 extents;
+ Vector3 size;
protected:
static void _bind_methods();
+#ifndef DISABLE_DEPRECATED
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_property) const;
+#endif // DISABLE_DEPRECATED
virtual void _update_shape() override;
public:
- void set_extents(const Vector3 &p_extents);
- Vector3 get_extents() const;
+ void set_size(const Vector3 &p_size);
+ Vector3 get_size() const;
virtual Vector<Vector3> get_debug_mesh_lines() const override;
virtual real_t get_enclosing_radius() const override;
diff --git a/scene/resources/camera_effects.cpp b/scene/resources/camera_effects.cpp
index 6b6ed51ed0..b633196424 100644
--- a/scene/resources/camera_effects.cpp
+++ b/scene/resources/camera_effects.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -41,7 +41,7 @@ RID CameraEffects::get_rid() const {
void CameraEffects::set_dof_blur_far_enabled(bool p_enabled) {
dof_blur_far_enabled = p_enabled;
_update_dof_blur();
- _change_notify();
+ notify_property_list_changed();
}
bool CameraEffects::is_dof_blur_far_enabled() const {
@@ -69,7 +69,7 @@ float CameraEffects::get_dof_blur_far_transition() const {
void CameraEffects::set_dof_blur_near_enabled(bool p_enabled) {
dof_blur_near_enabled = p_enabled;
_update_dof_blur();
- _change_notify();
+ notify_property_list_changed();
}
bool CameraEffects::is_dof_blur_near_enabled() const {
@@ -120,6 +120,7 @@ void CameraEffects::_update_dof_blur() {
void CameraEffects::set_override_exposure_enabled(bool p_enabled) {
override_exposure_enabled = p_enabled;
_update_override_exposure();
+ notify_property_list_changed();
}
bool CameraEffects::is_override_exposure_enabled() const {
@@ -144,6 +145,14 @@ void CameraEffects::_update_override_exposure() {
// Private methods, constructor and destructor
+void CameraEffects::_validate_property(PropertyInfo &property) const {
+ if ((!dof_blur_far_enabled && (property.name == "dof_blur_far_distance" || property.name == "dof_blur_far_transition")) ||
+ (!dof_blur_near_enabled && (property.name == "dof_blur_near_distance" || property.name == "dof_blur_near_transition")) ||
+ (!override_exposure_enabled && property.name == "override_exposure")) {
+ property.usage = PROPERTY_USAGE_NOEDITOR;
+ }
+}
+
void CameraEffects::_bind_methods() {
// DOF blur
@@ -166,11 +175,11 @@ void CameraEffects::_bind_methods() {
ADD_GROUP("DOF Blur", "dof_blur_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dof_blur_far_enabled"), "set_dof_blur_far_enabled", "is_dof_blur_far_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_far_distance", PROPERTY_HINT_EXP_RANGE, "0.01,8192,0.01"), "set_dof_blur_far_distance", "get_dof_blur_far_distance");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_far_transition", PROPERTY_HINT_EXP_RANGE, "0.01,8192,0.01"), "set_dof_blur_far_transition", "get_dof_blur_far_transition");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_far_distance", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp"), "set_dof_blur_far_distance", "get_dof_blur_far_distance");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_far_transition", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp"), "set_dof_blur_far_transition", "get_dof_blur_far_transition");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dof_blur_near_enabled"), "set_dof_blur_near_enabled", "is_dof_blur_near_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_near_distance", PROPERTY_HINT_EXP_RANGE, "0.01,8192,0.01"), "set_dof_blur_near_distance", "get_dof_blur_near_distance");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_near_transition", PROPERTY_HINT_EXP_RANGE, "0.01,8192,0.01"), "set_dof_blur_near_transition", "get_dof_blur_near_transition");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_near_distance", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp"), "set_dof_blur_near_distance", "get_dof_blur_near_distance");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_near_transition", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp"), "set_dof_blur_near_transition", "get_dof_blur_near_transition");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_amount", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_dof_blur_amount", "get_dof_blur_amount");
// Override exposure
diff --git a/scene/resources/camera_effects.h b/scene/resources/camera_effects.h
index 99fd4eb37c..b9338f4806 100644
--- a/scene/resources/camera_effects.h
+++ b/scene/resources/camera_effects.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,8 +31,8 @@
#ifndef CAMERA_EFFECTS_H
#define CAMERA_EFFECTS_H
-#include "core/resource.h"
-#include "core/rid.h"
+#include "core/io/resource.h"
+#include "core/templates/rid.h"
class CameraEffects : public Resource {
GDCLASS(CameraEffects, Resource);
@@ -42,12 +42,12 @@ private:
// DOF blur
bool dof_blur_far_enabled = false;
- float dof_blur_far_distance = 10;
- float dof_blur_far_transition = 5;
+ float dof_blur_far_distance = 10.0;
+ float dof_blur_far_transition = 5.0;
bool dof_blur_near_enabled = false;
- float dof_blur_near_distance = 2;
- float dof_blur_near_transition = 1;
+ float dof_blur_near_distance = 2.0;
+ float dof_blur_near_transition = 1.0;
float dof_blur_amount = 0.1;
void _update_dof_blur();
@@ -59,6 +59,7 @@ private:
protected:
static void _bind_methods();
+ void _validate_property(PropertyInfo &property) const override;
public:
virtual RID get_rid() const override;
diff --git a/scene/resources/canvas_item_material.cpp b/scene/resources/canvas_item_material.cpp
new file mode 100644
index 0000000000..7501efea9e
--- /dev/null
+++ b/scene/resources/canvas_item_material.cpp
@@ -0,0 +1,306 @@
+/*************************************************************************/
+/* canvas_item_material.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 "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;
+CanvasItemMaterial::ShaderNames *CanvasItemMaterial::shader_names = nullptr;
+
+void CanvasItemMaterial::init_shaders() {
+ dirty_materials = memnew(SelfList<CanvasItemMaterial>::List);
+
+ shader_names = memnew(ShaderNames);
+
+ shader_names->particles_anim_h_frames = "particles_anim_h_frames";
+ shader_names->particles_anim_v_frames = "particles_anim_v_frames";
+ shader_names->particles_anim_loop = "particles_anim_loop";
+}
+
+void CanvasItemMaterial::finish_shaders() {
+ memdelete(dirty_materials);
+ memdelete(shader_names);
+ dirty_materials = nullptr;
+}
+
+void CanvasItemMaterial::_update_shader() {
+ dirty_materials->remove(&element);
+
+ MaterialKey mk = _compute_key();
+ if (mk.key == current_key.key) {
+ return; //no update required in the end
+ }
+
+ if (shader_map.has(current_key)) {
+ shader_map[current_key].users--;
+ if (shader_map[current_key].users == 0) {
+ //deallocate shader, as it's no longer in use
+ RS::get_singleton()->free(shader_map[current_key].shader);
+ shader_map.erase(current_key);
+ }
+ }
+
+ current_key = mk;
+
+ if (shader_map.has(mk)) {
+ RS::get_singleton()->material_set_shader(_get_material(), shader_map[mk].shader);
+ shader_map[mk].users++;
+ return;
+ }
+
+ //must create a shader!
+
+ // 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";
+ break;
+ case BLEND_MODE_ADD:
+ code += "blend_add";
+ break;
+ case BLEND_MODE_SUB:
+ code += "blend_sub";
+ break;
+ case BLEND_MODE_MUL:
+ code += "blend_mul";
+ break;
+ case BLEND_MODE_PREMULT_ALPHA:
+ code += "blend_premul_alpha";
+ break;
+ case BLEND_MODE_DISABLED:
+ code += "blend_disabled";
+ break;
+ }
+
+ switch (light_mode) {
+ case LIGHT_MODE_NORMAL:
+ break;
+ case LIGHT_MODE_UNSHADED:
+ code += ",unshaded";
+ break;
+ case LIGHT_MODE_LIGHT_ONLY:
+ code += ",light_only";
+ break;
+ }
+
+ code += ";\n";
+
+ if (particles_animation) {
+ code += "uniform int particles_anim_h_frames;\n";
+ code += "uniform int particles_anim_v_frames;\n";
+ code += "uniform bool particles_anim_loop;\n\n";
+
+ code += "void vertex() {\n";
+ code += " float h_frames = float(particles_anim_h_frames);\n";
+ code += " float v_frames = float(particles_anim_v_frames);\n";
+ code += " VERTEX.xy /= vec2(h_frames, v_frames);\n";
+ code += " float particle_total_frames = float(particles_anim_h_frames * particles_anim_v_frames);\n";
+ code += " float particle_frame = floor(INSTANCE_CUSTOM.z * float(particle_total_frames));\n";
+ code += " if (!particles_anim_loop) {\n";
+ code += " particle_frame = clamp(particle_frame, 0.0, particle_total_frames - 1.0);\n";
+ code += " } else {\n";
+ code += " particle_frame = mod(particle_frame, particle_total_frames);\n";
+ code += " }";
+ code += " UV /= vec2(h_frames, v_frames);\n";
+ code += " UV += vec2(mod(particle_frame, h_frames) / h_frames, floor(particle_frame / h_frames) / v_frames);\n";
+ code += "}\n";
+ }
+
+ ShaderData shader_data;
+ shader_data.shader = RS::get_singleton()->shader_create();
+ shader_data.users = 1;
+
+ RS::get_singleton()->shader_set_code(shader_data.shader, code);
+
+ shader_map[mk] = shader_data;
+
+ RS::get_singleton()->material_set_shader(_get_material(), shader_data.shader);
+}
+
+void CanvasItemMaterial::flush_changes() {
+ MutexLock lock(material_mutex);
+
+ while (dirty_materials->first()) {
+ dirty_materials->first()->self()->_update_shader();
+ }
+}
+
+void CanvasItemMaterial::_queue_shader_change() {
+ MutexLock lock(material_mutex);
+
+ if (!element.in_list()) {
+ dirty_materials->add(&element);
+ }
+}
+
+bool CanvasItemMaterial::_is_shader_dirty() const {
+ MutexLock lock(material_mutex);
+
+ return element.in_list();
+}
+
+void CanvasItemMaterial::set_blend_mode(BlendMode p_blend_mode) {
+ blend_mode = p_blend_mode;
+ _queue_shader_change();
+}
+
+CanvasItemMaterial::BlendMode CanvasItemMaterial::get_blend_mode() const {
+ return blend_mode;
+}
+
+void CanvasItemMaterial::set_light_mode(LightMode p_light_mode) {
+ light_mode = p_light_mode;
+ _queue_shader_change();
+}
+
+CanvasItemMaterial::LightMode CanvasItemMaterial::get_light_mode() const {
+ return light_mode;
+}
+
+void CanvasItemMaterial::set_particles_animation(bool p_particles_anim) {
+ particles_animation = p_particles_anim;
+ _queue_shader_change();
+ notify_property_list_changed();
+}
+
+bool CanvasItemMaterial::get_particles_animation() const {
+ return particles_animation;
+}
+
+void CanvasItemMaterial::set_particles_anim_h_frames(int p_frames) {
+ particles_anim_h_frames = p_frames;
+ RS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_h_frames, p_frames);
+}
+
+int CanvasItemMaterial::get_particles_anim_h_frames() const {
+ return particles_anim_h_frames;
+}
+
+void CanvasItemMaterial::set_particles_anim_v_frames(int p_frames) {
+ particles_anim_v_frames = p_frames;
+ RS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_v_frames, p_frames);
+}
+
+int CanvasItemMaterial::get_particles_anim_v_frames() const {
+ return particles_anim_v_frames;
+}
+
+void CanvasItemMaterial::set_particles_anim_loop(bool p_loop) {
+ particles_anim_loop = p_loop;
+ RS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_loop, particles_anim_loop);
+}
+
+bool CanvasItemMaterial::get_particles_anim_loop() const {
+ return particles_anim_loop;
+}
+
+void CanvasItemMaterial::_validate_property(PropertyInfo &property) const {
+ if (property.name.begins_with("particles_anim_") && !particles_animation) {
+ property.usage = PROPERTY_USAGE_NONE;
+ }
+}
+
+RID CanvasItemMaterial::get_shader_rid() const {
+ ERR_FAIL_COND_V(!shader_map.has(current_key), RID());
+ return shader_map[current_key].shader;
+}
+
+Shader::Mode CanvasItemMaterial::get_shader_mode() const {
+ return Shader::MODE_CANVAS_ITEM;
+}
+
+void CanvasItemMaterial::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_blend_mode", "blend_mode"), &CanvasItemMaterial::set_blend_mode);
+ ClassDB::bind_method(D_METHOD("get_blend_mode"), &CanvasItemMaterial::get_blend_mode);
+
+ ClassDB::bind_method(D_METHOD("set_light_mode", "light_mode"), &CanvasItemMaterial::set_light_mode);
+ ClassDB::bind_method(D_METHOD("get_light_mode"), &CanvasItemMaterial::get_light_mode);
+
+ ClassDB::bind_method(D_METHOD("set_particles_animation", "particles_anim"), &CanvasItemMaterial::set_particles_animation);
+ ClassDB::bind_method(D_METHOD("get_particles_animation"), &CanvasItemMaterial::get_particles_animation);
+
+ ClassDB::bind_method(D_METHOD("set_particles_anim_h_frames", "frames"), &CanvasItemMaterial::set_particles_anim_h_frames);
+ ClassDB::bind_method(D_METHOD("get_particles_anim_h_frames"), &CanvasItemMaterial::get_particles_anim_h_frames);
+
+ ClassDB::bind_method(D_METHOD("set_particles_anim_v_frames", "frames"), &CanvasItemMaterial::set_particles_anim_v_frames);
+ ClassDB::bind_method(D_METHOD("get_particles_anim_v_frames"), &CanvasItemMaterial::get_particles_anim_v_frames);
+
+ ClassDB::bind_method(D_METHOD("set_particles_anim_loop", "loop"), &CanvasItemMaterial::set_particles_anim_loop);
+ ClassDB::bind_method(D_METHOD("get_particles_anim_loop"), &CanvasItemMaterial::get_particles_anim_loop);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Mix,Add,Subtract,Multiply,Premultiplied Alpha"), "set_blend_mode", "get_blend_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mode", PROPERTY_HINT_ENUM, "Normal,Unshaded,Light Only"), "set_light_mode", "get_light_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "particles_animation"), "set_particles_animation", "get_particles_animation");
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "particles_anim_h_frames", PROPERTY_HINT_RANGE, "1,128,1"), "set_particles_anim_h_frames", "get_particles_anim_h_frames");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "particles_anim_v_frames", PROPERTY_HINT_RANGE, "1,128,1"), "set_particles_anim_v_frames", "get_particles_anim_v_frames");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "particles_anim_loop"), "set_particles_anim_loop", "get_particles_anim_loop");
+
+ BIND_ENUM_CONSTANT(BLEND_MODE_MIX);
+ BIND_ENUM_CONSTANT(BLEND_MODE_ADD);
+ BIND_ENUM_CONSTANT(BLEND_MODE_SUB);
+ BIND_ENUM_CONSTANT(BLEND_MODE_MUL);
+ BIND_ENUM_CONSTANT(BLEND_MODE_PREMULT_ALPHA);
+
+ BIND_ENUM_CONSTANT(LIGHT_MODE_NORMAL);
+ BIND_ENUM_CONSTANT(LIGHT_MODE_UNSHADED);
+ BIND_ENUM_CONSTANT(LIGHT_MODE_LIGHT_ONLY);
+}
+
+CanvasItemMaterial::CanvasItemMaterial() :
+ element(this) {
+ set_particles_anim_h_frames(1);
+ set_particles_anim_v_frames(1);
+ set_particles_anim_loop(false);
+
+ current_key.invalid_key = 1;
+ _queue_shader_change();
+}
+
+CanvasItemMaterial::~CanvasItemMaterial() {
+ MutexLock lock(material_mutex);
+
+ if (shader_map.has(current_key)) {
+ shader_map[current_key].users--;
+ if (shader_map[current_key].users == 0) {
+ //deallocate shader, as it's no longer in use
+ RS::get_singleton()->free(shader_map[current_key].shader);
+ shader_map.erase(current_key);
+ }
+
+ RS::get_singleton()->material_set_shader(_get_material(), RID());
+ }
+}
diff --git a/scene/resources/canvas_item_material.h b/scene/resources/canvas_item_material.h
new file mode 100644
index 0000000000..0a813e0ae5
--- /dev/null
+++ b/scene/resources/canvas_item_material.h
@@ -0,0 +1,151 @@
+/*************************************************************************/
+/* canvas_item_material.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 CANVAS_ITEM_MATERIAL_H
+#define CANVAS_ITEM_MATERIAL_H
+
+#include "scene/resources/material.h"
+
+class CanvasItemMaterial : public Material {
+ GDCLASS(CanvasItemMaterial, Material);
+
+public:
+ enum BlendMode {
+ BLEND_MODE_MIX,
+ BLEND_MODE_ADD,
+ BLEND_MODE_SUB,
+ BLEND_MODE_MUL,
+ BLEND_MODE_PREMULT_ALPHA,
+ BLEND_MODE_DISABLED
+ };
+
+ enum LightMode {
+ LIGHT_MODE_NORMAL,
+ LIGHT_MODE_UNSHADED,
+ LIGHT_MODE_LIGHT_ONLY
+ };
+
+private:
+ union MaterialKey {
+ struct {
+ uint32_t blend_mode : 4;
+ uint32_t light_mode : 4;
+ uint32_t particles_animation : 1;
+ uint32_t invalid_key : 1;
+ };
+
+ uint32_t key = 0;
+
+ bool operator<(const MaterialKey &p_key) const {
+ return key < p_key.key;
+ }
+ };
+
+ struct ShaderNames {
+ StringName particles_anim_h_frames;
+ StringName particles_anim_v_frames;
+ StringName particles_anim_loop;
+ };
+
+ static ShaderNames *shader_names;
+
+ struct ShaderData {
+ RID shader;
+ int users = 0;
+ };
+
+ static Map<MaterialKey, ShaderData> shader_map;
+
+ MaterialKey current_key;
+
+ _FORCE_INLINE_ MaterialKey _compute_key() const {
+ MaterialKey mk;
+ mk.key = 0;
+ mk.blend_mode = blend_mode;
+ mk.light_mode = light_mode;
+ mk.particles_animation = particles_animation;
+ return mk;
+ }
+
+ static Mutex material_mutex;
+ static SelfList<CanvasItemMaterial>::List *dirty_materials;
+ SelfList<CanvasItemMaterial> element;
+
+ void _update_shader();
+ _FORCE_INLINE_ void _queue_shader_change();
+ _FORCE_INLINE_ bool _is_shader_dirty() const;
+
+ BlendMode blend_mode = BLEND_MODE_MIX;
+ LightMode light_mode = LIGHT_MODE_NORMAL;
+ bool particles_animation = false;
+
+ // Initialized in the constructor.
+ int particles_anim_h_frames;
+ int particles_anim_v_frames;
+ bool particles_anim_loop;
+
+protected:
+ static void _bind_methods();
+ void _validate_property(PropertyInfo &property) const override;
+
+public:
+ void set_blend_mode(BlendMode p_blend_mode);
+ BlendMode get_blend_mode() const;
+
+ void set_light_mode(LightMode p_light_mode);
+ LightMode get_light_mode() const;
+
+ void set_particles_animation(bool p_particles_anim);
+ bool get_particles_animation() const;
+
+ void set_particles_anim_h_frames(int p_frames);
+ int get_particles_anim_h_frames() const;
+ void set_particles_anim_v_frames(int p_frames);
+ int get_particles_anim_v_frames() const;
+
+ void set_particles_anim_loop(bool p_loop);
+ bool get_particles_anim_loop() const;
+
+ static void init_shaders();
+ static void finish_shaders();
+ static void flush_changes();
+
+ virtual RID get_shader_rid() const override;
+
+ virtual Shader::Mode get_shader_mode() const override;
+
+ CanvasItemMaterial();
+ virtual ~CanvasItemMaterial();
+};
+
+VARIANT_ENUM_CAST(CanvasItemMaterial::BlendMode)
+VARIANT_ENUM_CAST(CanvasItemMaterial::LightMode)
+
+#endif // CANVAS_ITEM_MATERIAL_H
diff --git a/scene/resources/capsule_shape_2d.cpp b/scene/resources/capsule_shape_2d.cpp
index e519970f38..0818e4fd99 100644
--- a/scene/resources/capsule_shape_2d.cpp
+++ b/scene/resources/capsule_shape_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -36,12 +36,13 @@
Vector<Vector2> CapsuleShape2D::_get_points() const {
Vector<Vector2> points;
+ const real_t turn_step = Math_TAU / 24.0;
for (int i = 0; i < 24; i++) {
- Vector2 ofs = Vector2(0, (i > 6 && i <= 18) ? -get_height() * 0.5 : get_height() * 0.5);
+ Vector2 ofs = Vector2(0, (i > 6 && i <= 18) ? -height * 0.5 + radius : height * 0.5 - radius);
- points.push_back(Vector2(Math::sin(i * Math_PI * 2 / 24.0), Math::cos(i * Math_PI * 2 / 24.0)) * get_radius() + ofs);
+ points.push_back(Vector2(Math::sin(i * turn_step), Math::cos(i * turn_step)) * radius + ofs);
if (i == 6 || i == 18) {
- points.push_back(Vector2(Math::sin(i * Math_PI * 2 / 24.0), Math::cos(i * Math_PI * 2 / 24.0)) * get_radius() - ofs);
+ points.push_back(Vector2(Math::sin(i * turn_step), Math::cos(i * turn_step)) * radius - ofs);
}
}
@@ -59,6 +60,9 @@ void CapsuleShape2D::_update_shape() {
void CapsuleShape2D::set_radius(real_t p_radius) {
radius = p_radius;
+ if (radius > height * 0.5) {
+ height = radius * 2.0;
+ }
_update_shape();
}
@@ -68,10 +72,9 @@ real_t CapsuleShape2D::get_radius() const {
void CapsuleShape2D::set_height(real_t p_height) {
height = p_height;
- if (height < 0) {
- height = 0;
+ if (radius > height * 0.5) {
+ radius = height * 0.5;
}
-
_update_shape();
}
@@ -84,18 +87,19 @@ void CapsuleShape2D::draw(const RID &p_to_rid, const Color &p_color) {
Vector<Color> col;
col.push_back(p_color);
RenderingServer::get_singleton()->canvas_item_add_polygon(p_to_rid, points, col);
+ if (is_collision_outline_enabled()) {
+ RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, points, col);
+ // Draw the last segment as it's not drawn by `canvas_item_add_polyline()`.
+ RenderingServer::get_singleton()->canvas_item_add_line(p_to_rid, points[points.size() - 1], points[0], p_color);
+ }
}
Rect2 CapsuleShape2D::get_rect() const {
- Vector2 he = Point2(get_radius(), get_radius() + get_height() * 0.5);
- Rect2 rect;
- rect.position = -he;
- rect.size = he * 2.0;
- return rect;
+ return Rect2(0, 0, radius, height);
}
real_t CapsuleShape2D::get_enclosing_radius() const {
- return radius + height * 0.5;
+ return height * 0.5;
}
void CapsuleShape2D::_bind_methods() {
@@ -107,11 +111,11 @@ void CapsuleShape2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius"), "set_radius", "get_radius");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height"), "set_height", "get_height");
+ ADD_LINKED_PROPERTY("radius", "height");
+ ADD_LINKED_PROPERTY("height", "radius");
}
CapsuleShape2D::CapsuleShape2D() :
Shape2D(PhysicsServer2D::get_singleton()->capsule_shape_create()) {
- radius = 10;
- height = 20;
_update_shape();
}
diff --git a/scene/resources/capsule_shape_2d.h b/scene/resources/capsule_shape_2d.h
index 1caa6c68b8..37b6c52c0e 100644
--- a/scene/resources/capsule_shape_2d.h
+++ b/scene/resources/capsule_shape_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -36,8 +36,8 @@
class CapsuleShape2D : public Shape2D {
GDCLASS(CapsuleShape2D, Shape2D);
- real_t height;
- real_t radius;
+ real_t height = 30.0;
+ real_t radius = 10.0;
void _update_shape();
Vector<Vector2> _get_points() const;
diff --git a/scene/resources/capsule_shape_3d.cpp b/scene/resources/capsule_shape_3d.cpp
index 9d1355eec6..afec7b1877 100644
--- a/scene/resources/capsule_shape_3d.cpp
+++ b/scene/resources/capsule_shape_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -37,7 +37,7 @@ Vector<Vector3> CapsuleShape3D::get_debug_mesh_lines() const {
Vector<Vector3> points;
- Vector3 d(0, height * 0.5, 0);
+ Vector3 d(0, height * 0.5 - radius, 0);
for (int i = 0; i < 360; i++) {
float ra = Math::deg2rad((float)i);
float rb = Math::deg2rad((float)i + 1);
@@ -67,7 +67,7 @@ Vector<Vector3> CapsuleShape3D::get_debug_mesh_lines() const {
}
real_t CapsuleShape3D::get_enclosing_radius() const {
- return radius + height * 0.5;
+ return height * 0.5;
}
void CapsuleShape3D::_update_shape() {
@@ -80,9 +80,11 @@ void CapsuleShape3D::_update_shape() {
void CapsuleShape3D::set_radius(float p_radius) {
radius = p_radius;
+ if (radius > height * 0.5) {
+ height = radius * 2.0;
+ }
_update_shape();
notify_change_to_owners();
- _change_notify("radius");
}
float CapsuleShape3D::get_radius() const {
@@ -91,9 +93,11 @@ float CapsuleShape3D::get_radius() const {
void CapsuleShape3D::set_height(float p_height) {
height = p_height;
+ if (radius > height * 0.5) {
+ radius = height * 0.5;
+ }
_update_shape();
notify_change_to_owners();
- _change_notify("height");
}
float CapsuleShape3D::get_height() const {
@@ -106,13 +110,13 @@ void CapsuleShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_height", "height"), &CapsuleShape3D::set_height);
ClassDB::bind_method(D_METHOD("get_height"), &CapsuleShape3D::get_height);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,4096,0.01"), "set_radius", "get_radius");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.01,4096,0.01"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,4096,0.001"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,4096,0.001"), "set_height", "get_height");
+ ADD_LINKED_PROPERTY("radius", "height");
+ ADD_LINKED_PROPERTY("height", "radius");
}
CapsuleShape3D::CapsuleShape3D() :
Shape3D(PhysicsServer3D::get_singleton()->shape_create(PhysicsServer3D::SHAPE_CAPSULE)) {
- radius = 1.0;
- height = 1.0;
_update_shape();
}
diff --git a/scene/resources/capsule_shape_3d.h b/scene/resources/capsule_shape_3d.h
index 432ca5654e..f09b4fb77d 100644
--- a/scene/resources/capsule_shape_3d.h
+++ b/scene/resources/capsule_shape_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,8 +35,8 @@
class CapsuleShape3D : public Shape3D {
GDCLASS(CapsuleShape3D, Shape3D);
- float radius;
- float height;
+ float radius = 1.0;
+ float height = 3.0;
protected:
static void _bind_methods();
diff --git a/scene/resources/circle_shape_2d.cpp b/scene/resources/circle_shape_2d.cpp
index dc1bf3b185..f06bc4248d 100644
--- a/scene/resources/circle_shape_2d.cpp
+++ b/scene/resources/circle_shape_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -71,17 +71,22 @@ real_t CircleShape2D::get_enclosing_radius() const {
void CircleShape2D::draw(const RID &p_to_rid, const Color &p_color) {
Vector<Vector2> points;
+ const real_t turn_step = Math_TAU / 24.0;
for (int i = 0; i < 24; i++) {
- points.push_back(Vector2(Math::cos(i * Math_PI * 2 / 24.0), Math::sin(i * Math_PI * 2 / 24.0)) * get_radius());
+ points.push_back(Vector2(Math::cos(i * turn_step), Math::sin(i * turn_step)) * get_radius());
}
Vector<Color> col;
col.push_back(p_color);
RenderingServer::get_singleton()->canvas_item_add_polygon(p_to_rid, points, col);
+ if (is_collision_outline_enabled()) {
+ RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, points, col);
+ // Draw the last segment as it's not drawn by `canvas_item_add_polyline()`.
+ RenderingServer::get_singleton()->canvas_item_add_line(p_to_rid, points[points.size() - 1], points[0], p_color);
+ }
}
CircleShape2D::CircleShape2D() :
Shape2D(PhysicsServer2D::get_singleton()->circle_shape_create()) {
- radius = 10;
_update_shape();
}
diff --git a/scene/resources/circle_shape_2d.h b/scene/resources/circle_shape_2d.h
index ac8757e781..333f299236 100644
--- a/scene/resources/circle_shape_2d.h
+++ b/scene/resources/circle_shape_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -36,7 +36,7 @@
class CircleShape2D : public Shape2D {
GDCLASS(CircleShape2D, Shape2D);
- real_t radius;
+ real_t radius = 10.0;
void _update_shape();
protected:
diff --git a/scene/resources/concave_polygon_shape_2d.cpp b/scene/resources/concave_polygon_shape_2d.cpp
index eecf8afa8f..0c767c8a52 100644
--- a/scene/resources/concave_polygon_shape_2d.cpp
+++ b/scene/resources/concave_polygon_shape_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -98,7 +98,7 @@ Rect2 ConcavePolygonShape2D::get_rect() const {
real_t ConcavePolygonShape2D::get_enclosing_radius() const {
Vector<Vector2> data = get_segments();
const Vector2 *read = data.ptr();
- real_t r = 0;
+ real_t r = 0.0;
for (int i(0); i < data.size(); i++) {
r = MAX(read[i].length_squared(), r);
}
diff --git a/scene/resources/concave_polygon_shape_2d.h b/scene/resources/concave_polygon_shape_2d.h
index df8cc9920f..98ae341e97 100644
--- a/scene/resources/concave_polygon_shape_2d.h
+++ b/scene/resources/concave_polygon_shape_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/concave_polygon_shape_3d.cpp b/scene/resources/concave_polygon_shape_3d.cpp
index 7cbafcbc4d..3fed700383 100644
--- a/scene/resources/concave_polygon_shape_3d.cpp
+++ b/scene/resources/concave_polygon_shape_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,13 +35,12 @@
Vector<Vector3> ConcavePolygonShape3D::get_debug_mesh_lines() const {
Set<DrawEdge> edges;
- Vector<Vector3> data = get_faces();
- int datalen = data.size();
- ERR_FAIL_COND_V((datalen % 3) != 0, Vector<Vector3>());
+ int index_count = faces.size();
+ ERR_FAIL_COND_V((index_count % 3) != 0, Vector<Vector3>());
- const Vector3 *r = data.ptr();
+ const Vector3 *r = faces.ptr();
- for (int i = 0; i < datalen; i += 3) {
+ for (int i = 0; i < index_count; i += 3) {
for (int j = 0; j < 3; j++) {
DrawEdge de(r[i + j], r[i + ((j + 1) % 3)]);
edges.insert(de);
@@ -63,7 +62,7 @@ Vector<Vector3> ConcavePolygonShape3D::get_debug_mesh_lines() const {
real_t ConcavePolygonShape3D::get_enclosing_radius() const {
Vector<Vector3> data = get_faces();
const Vector3 *read = data.ptr();
- real_t r = 0;
+ real_t r = 0.0;
for (int i(0); i < data.size(); i++) {
r = MAX(read[i].length_squared(), r);
}
@@ -71,22 +70,46 @@ real_t ConcavePolygonShape3D::get_enclosing_radius() const {
}
void ConcavePolygonShape3D::_update_shape() {
+ Dictionary d;
+ d["faces"] = faces;
+ d["backface_collision"] = backface_collision;
+ PhysicsServer3D::get_singleton()->shape_set_data(get_shape(), d);
+
Shape3D::_update_shape();
}
void ConcavePolygonShape3D::set_faces(const Vector<Vector3> &p_faces) {
- PhysicsServer3D::get_singleton()->shape_set_data(get_shape(), p_faces);
+ faces = p_faces;
+ _update_shape();
notify_change_to_owners();
}
Vector<Vector3> ConcavePolygonShape3D::get_faces() const {
- return PhysicsServer3D::get_singleton()->shape_get_data(get_shape());
+ return faces;
+}
+
+void ConcavePolygonShape3D::set_backface_collision_enabled(bool p_enabled) {
+ backface_collision = p_enabled;
+
+ if (!faces.is_empty()) {
+ _update_shape();
+ notify_change_to_owners();
+ }
+}
+
+bool ConcavePolygonShape3D::is_backface_collision_enabled() const {
+ return backface_collision;
}
void ConcavePolygonShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_faces", "faces"), &ConcavePolygonShape3D::set_faces);
ClassDB::bind_method(D_METHOD("get_faces"), &ConcavePolygonShape3D::get_faces);
+
+ ClassDB::bind_method(D_METHOD("set_backface_collision_enabled", "enabled"), &ConcavePolygonShape3D::set_backface_collision_enabled);
+ ClassDB::bind_method(D_METHOD("is_backface_collision_enabled"), &ConcavePolygonShape3D::is_backface_collision_enabled);
+
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_faces", "get_faces");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "backface_collision"), "set_backface_collision_enabled", "is_backface_collision_enabled");
}
ConcavePolygonShape3D::ConcavePolygonShape3D() :
diff --git a/scene/resources/concave_polygon_shape_3d.h b/scene/resources/concave_polygon_shape_3d.h
index c17765b9ef..bb28359dcc 100644
--- a/scene/resources/concave_polygon_shape_3d.h
+++ b/scene/resources/concave_polygon_shape_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -36,6 +36,9 @@
class ConcavePolygonShape3D : public Shape3D {
GDCLASS(ConcavePolygonShape3D, Shape3D);
+ Vector<Vector3> faces;
+ bool backface_collision = false;
+
struct DrawEdge {
Vector3 a;
Vector3 b;
@@ -65,6 +68,9 @@ public:
void set_faces(const Vector<Vector3> &p_faces);
Vector<Vector3> get_faces() const;
+ void set_backface_collision_enabled(bool p_enabled);
+ bool is_backface_collision_enabled() const;
+
virtual Vector<Vector3> get_debug_mesh_lines() const override;
virtual real_t get_enclosing_radius() const override;
diff --git a/scene/resources/convex_polygon_shape_2d.cpp b/scene/resources/convex_polygon_shape_2d.cpp
index 2b7531c630..ac31315858 100644
--- a/scene/resources/convex_polygon_shape_2d.cpp
+++ b/scene/resources/convex_polygon_shape_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -41,7 +41,7 @@ bool ConvexPolygonShape2D::_edit_is_selected_on_click(const Point2 &p_point, dou
void ConvexPolygonShape2D::_update_shape() {
Vector<Vector2> final_points = points;
if (Geometry2D::is_polygon_clockwise(final_points)) { //needs to be counter clockwise
- final_points.invert();
+ final_points.reverse();
}
PhysicsServer2D::get_singleton()->shape_set_data(get_rid(), final_points);
emit_changed();
@@ -72,9 +72,18 @@ void ConvexPolygonShape2D::_bind_methods() {
}
void ConvexPolygonShape2D::draw(const RID &p_to_rid, const Color &p_color) {
+ if (points.size() < 3) {
+ return;
+ }
+
Vector<Color> col;
col.push_back(p_color);
RenderingServer::get_singleton()->canvas_item_add_polygon(p_to_rid, points, col);
+ if (is_collision_outline_enabled()) {
+ RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, points, col);
+ // Draw the last segment as it's not drawn by `canvas_item_add_polyline()`.
+ RenderingServer::get_singleton()->canvas_item_add_line(p_to_rid, points[points.size() - 1], points[0], p_color);
+ }
}
Rect2 ConvexPolygonShape2D::get_rect() const {
@@ -91,7 +100,7 @@ Rect2 ConvexPolygonShape2D::get_rect() const {
}
real_t ConvexPolygonShape2D::get_enclosing_radius() const {
- real_t r = 0;
+ real_t r = 0.0;
for (int i(0); i < get_points().size(); i++) {
r = MAX(get_points()[i].length_squared(), r);
}
diff --git a/scene/resources/convex_polygon_shape_2d.h b/scene/resources/convex_polygon_shape_2d.h
index 294157bec5..1813b608bd 100644
--- a/scene/resources/convex_polygon_shape_2d.h
+++ b/scene/resources/convex_polygon_shape_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/convex_polygon_shape_3d.cpp b/scene/resources/convex_polygon_shape_3d.cpp
index 29549e1114..6b895da606 100644
--- a/scene/resources/convex_polygon_shape_3d.cpp
+++ b/scene/resources/convex_polygon_shape_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,7 +29,7 @@
/*************************************************************************/
#include "convex_polygon_shape_3d.h"
-#include "core/math/quick_hull.h"
+#include "core/math/convex_hull.h"
#include "servers/physics_server_3d.h"
Vector<Vector3> ConvexPolygonShape3D::get_debug_mesh_lines() const {
@@ -38,7 +38,7 @@ Vector<Vector3> ConvexPolygonShape3D::get_debug_mesh_lines() const {
if (points.size() > 3) {
Vector<Vector3> varr = Variant(points);
Geometry3D::MeshData md;
- Error err = QuickHull::build(varr, md);
+ Error err = ConvexHullComputer::convex_hull(varr, md);
if (err == OK) {
Vector<Vector3> lines;
lines.resize(md.edges.size() * 2);
@@ -56,7 +56,7 @@ Vector<Vector3> ConvexPolygonShape3D::get_debug_mesh_lines() const {
real_t ConvexPolygonShape3D::get_enclosing_radius() const {
Vector<Vector3> data = get_points();
const Vector3 *read = data.ptr();
- real_t r = 0;
+ real_t r = 0.0;
for (int i(0); i < data.size(); i++) {
r = MAX(read[i].length_squared(), r);
}
diff --git a/scene/resources/convex_polygon_shape_3d.h b/scene/resources/convex_polygon_shape_3d.h
index f436d2f5d4..edd127c8f4 100644
--- a/scene/resources/convex_polygon_shape_3d.h
+++ b/scene/resources/convex_polygon_shape_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp
index de076670cf..a364a27e80 100644
--- a/scene/resources/curve.cpp
+++ b/scene/resources/curve.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -47,11 +47,6 @@ static _FORCE_INLINE_ T _bezier_interp(real_t t, T start, T control_1, T control
const char *Curve::SIGNAL_RANGE_CHANGED = "range_changed";
Curve::Curve() {
- _bake_resolution = 100;
- _baked_cache_dirty = false;
- _min_value = 0;
- _max_value = 1;
- _minmax_set_once = 0b00;
}
int Curve::add_point(Vector2 p_pos, real_t left_tangent, real_t right_tangent, TangentMode left_mode, TangentMode right_mode) {
@@ -291,7 +286,7 @@ void Curve::set_min_value(float p_min) {
}
// Note: min and max are indicative values,
// it's still possible that existing points are out of range at this point.
- emit_signal(SIGNAL_RANGE_CHANGED);
+ emit_signal(SNAME(SIGNAL_RANGE_CHANGED));
}
void Curve::set_max_value(float p_max) {
@@ -301,7 +296,7 @@ void Curve::set_max_value(float p_max) {
_minmax_set_once |= 0b01; // second bit is "max set"
_max_value = p_max;
}
- emit_signal(SIGNAL_RANGE_CHANGED);
+ emit_signal(SNAME(SIGNAL_RANGE_CHANGED));
}
real_t Curve::interpolate(real_t offset) const {
@@ -450,10 +445,10 @@ void Curve::set_bake_resolution(int p_resolution) {
_baked_cache_dirty = true;
}
-real_t Curve::interpolate_baked(real_t offset) {
+real_t Curve::interpolate_baked(real_t offset) const {
if (_baked_cache_dirty) {
// Last-second bake if not done already
- bake();
+ const_cast<Curve *>(this)->bake();
}
// Special cases if the cache is too small
@@ -602,7 +597,7 @@ void Curve2D::remove_point(int p_index) {
}
void Curve2D::clear_points() {
- if (!points.empty()) {
+ if (!points.is_empty()) {
points.clear();
baked_cache_dirty = true;
emit_signal(CoreStringNames::get_singleton()->changed);
@@ -667,23 +662,31 @@ void Curve2D::_bake() const {
if (points.size() == 0) {
baked_point_cache.resize(0);
+ baked_dist_cache.resize(0);
return;
}
if (points.size() == 1) {
baked_point_cache.resize(1);
baked_point_cache.set(0, points[0].pos);
+
+ baked_dist_cache.resize(1);
+ baked_dist_cache.set(0, 0.0);
return;
}
Vector2 pos = points[0].pos;
+ float dist = 0.0;
+
List<Vector2> pointlist;
+ List<float> distlist;
pointlist.push_back(pos); //start always from origin
+ distlist.push_back(0.0);
for (int i = 0; i < points.size() - 1; i++) {
float step = 0.1; // at least 10 substeps ought to be enough?
- float p = 0;
+ float p = 0.0;
while (p < 1.0) {
float np = p + step;
@@ -717,7 +720,10 @@ void Curve2D::_bake() const {
pos = npp;
p = mid;
+ dist += d;
+
pointlist.push_back(pos);
+ distlist.push_back(dist);
} else {
p = np;
}
@@ -727,16 +733,20 @@ void Curve2D::_bake() const {
Vector2 lastpos = points[points.size() - 1].pos;
float rem = pos.distance_to(lastpos);
- baked_max_ofs = (pointlist.size() - 1) * bake_interval + rem;
+ dist += rem;
+ baked_max_ofs = dist;
pointlist.push_back(lastpos);
+ distlist.push_back(dist);
baked_point_cache.resize(pointlist.size());
+ baked_dist_cache.resize(distlist.size());
+
Vector2 *w = baked_point_cache.ptrw();
- int idx = 0;
+ float *wd = baked_dist_cache.ptrw();
- for (List<Vector2>::Element *E = pointlist.front(); E; E = E->next()) {
- w[idx] = E->get();
- idx++;
+ for (int i = 0; i < pointlist.size(); i++) {
+ w[i] = pointlist[i];
+ wd[i] = distlist[i];
}
}
@@ -771,19 +781,26 @@ Vector2 Curve2D::interpolate_baked(float p_offset, bool p_cubic) const {
return r[bpc - 1];
}
- int idx = Math::floor((double)p_offset / (double)bake_interval);
- float frac = Math::fmod(p_offset, (float)bake_interval);
-
- if (idx >= bpc - 1) {
- return r[bpc - 1];
- } else if (idx == bpc - 2) {
- if (frac > 0) {
- frac /= Math::fmod(baked_max_ofs, bake_interval);
+ int start = 0, end = bpc, idx = (end + start) / 2;
+ // binary search to find baked points
+ while (start < idx) {
+ float offset = baked_dist_cache[idx];
+ if (p_offset <= offset) {
+ end = idx;
+ } else {
+ start = idx;
}
- } else {
- frac /= bake_interval;
+ idx = (end + start) / 2;
}
+ float offset_begin = baked_dist_cache[idx];
+ float offset_end = baked_dist_cache[idx + 1];
+
+ float idx_interval = offset_end - offset_begin;
+ ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector2(), "failed to find baked segment");
+
+ float frac = (p_offset - offset_begin) / idx_interval;
+
if (p_cubic) {
Vector2 pre = idx > 0 ? r[idx - 1] : r[idx];
Vector2 post = (idx < (bpc - 2)) ? r[idx + 2] : r[idx + 1];
@@ -993,12 +1010,9 @@ void Curve2D::_bind_methods() {
}
Curve2D::Curve2D() {
- baked_cache_dirty = false;
- baked_max_ofs = 0;
/* add_point(Vector2(-1,0,0));
add_point(Vector2(0,2,0));
add_point(Vector2(0,3,5));*/
- bake_interval = 5;
}
/***********************************************************************************/
@@ -1087,7 +1101,7 @@ void Curve3D::remove_point(int p_index) {
}
void Curve3D::clear_points() {
- if (!points.empty()) {
+ if (!points.is_empty()) {
points.clear();
baked_cache_dirty = true;
emit_signal(CoreStringNames::get_singleton()->changed);
@@ -1153,6 +1167,7 @@ void Curve3D::_bake() const {
baked_point_cache.resize(0);
baked_tilt_cache.resize(0);
baked_up_vector_cache.resize(0);
+ baked_dist_cache.resize(0);
return;
}
@@ -1161,6 +1176,8 @@ void Curve3D::_bake() const {
baked_point_cache.set(0, points[0].pos);
baked_tilt_cache.resize(1);
baked_tilt_cache.set(0, points[0].tilt);
+ baked_dist_cache.resize(1);
+ baked_dist_cache.set(0, 0.0);
if (up_vector_enabled) {
baked_up_vector_cache.resize(1);
@@ -1173,12 +1190,16 @@ void Curve3D::_bake() const {
}
Vector3 pos = points[0].pos;
+ float dist = 0.0;
List<Plane> pointlist;
+ List<float> distlist;
+
pointlist.push_back(Plane(pos, points[0].tilt));
+ distlist.push_back(0.0);
for (int i = 0; i < points.size() - 1; i++) {
float step = 0.1; // at least 10 substeps ought to be enough?
- float p = 0;
+ float p = 0.0;
while (p < 1.0) {
float np = p + step;
@@ -1215,7 +1236,10 @@ void Curve3D::_bake() const {
Plane post;
post.normal = pos;
post.d = Math::lerp(points[i].tilt, points[i + 1].tilt, mid);
+ dist += d;
+
pointlist.push_back(post);
+ distlist.push_back(dist);
} else {
p = np;
}
@@ -1226,8 +1250,10 @@ void Curve3D::_bake() const {
float lastilt = points[points.size() - 1].tilt;
float rem = pos.distance_to(lastpos);
- baked_max_ofs = (pointlist.size() - 1) * bake_interval + rem;
+ dist += rem;
+ baked_max_ofs = dist;
pointlist.push_back(Plane(lastpos, lastilt));
+ distlist.push_back(dist);
baked_point_cache.resize(pointlist.size());
Vector3 *w = baked_point_cache.ptrw();
@@ -1239,6 +1265,9 @@ void Curve3D::_bake() const {
baked_up_vector_cache.resize(up_vector_enabled ? pointlist.size() : 0);
Vector3 *up_write = baked_up_vector_cache.ptrw();
+ baked_dist_cache.resize(pointlist.size());
+ float *wd = baked_dist_cache.ptrw();
+
Vector3 sideways;
Vector3 up;
Vector3 forward;
@@ -1247,9 +1276,10 @@ void Curve3D::_bake() const {
Vector3 prev_up = Vector3(0, 1, 0);
Vector3 prev_forward = Vector3(0, 0, 1);
- for (List<Plane>::Element *E = pointlist.front(); E; E = E->next()) {
- w[idx] = E->get().normal;
- wt[idx] = E->get().d;
+ for (const Plane &E : pointlist) {
+ w[idx] = E.normal;
+ wt[idx] = E.d;
+ wd[idx] = distlist[idx];
if (!up_vector_enabled) {
idx++;
@@ -1316,19 +1346,26 @@ Vector3 Curve3D::interpolate_baked(float p_offset, bool p_cubic) const {
return r[bpc - 1];
}
- int idx = Math::floor((double)p_offset / (double)bake_interval);
- float frac = Math::fmod(p_offset, bake_interval);
-
- if (idx >= bpc - 1) {
- return r[bpc - 1];
- } else if (idx == bpc - 2) {
- if (frac > 0) {
- frac /= Math::fmod(baked_max_ofs, bake_interval);
+ int start = 0, end = bpc, idx = (end + start) / 2;
+ // binary search to find baked points
+ while (start < idx) {
+ float offset = baked_dist_cache[idx];
+ if (p_offset <= offset) {
+ end = idx;
+ } else {
+ start = idx;
}
- } else {
- frac /= bake_interval;
+ idx = (end + start) / 2;
}
+ float offset_begin = baked_dist_cache[idx];
+ float offset_end = baked_dist_cache[idx + 1];
+
+ float idx_interval = offset_end - offset_begin;
+ ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector3(), "failed to find baked segment");
+
+ float frac = (p_offset - offset_begin) / idx_interval;
+
if (p_cubic) {
Vector3 pre = idx > 0 ? r[idx - 1] : r[idx];
Vector3 post = (idx < (bpc - 2)) ? r[idx + 2] : r[idx + 1];
@@ -1374,7 +1411,7 @@ float Curve3D::interpolate_baked_tilt(float p_offset) const {
frac /= bake_interval;
}
- return Math::lerp(r[idx], r[idx + 1], frac);
+ return Math::lerp(r[idx], r[idx + 1], (real_t)frac);
}
Vector3 Curve3D::interpolate_baked_up_vector(float p_offset, bool p_apply_tilt) const {
@@ -1432,7 +1469,7 @@ PackedVector3Array Curve3D::get_baked_points() const {
return baked_point_cache;
}
-PackedFloat32Array Curve3D::get_baked_tilts() const {
+Vector<real_t> Curve3D::get_baked_tilts() const {
if (baked_cache_dirty) {
_bake();
}
@@ -1553,7 +1590,7 @@ Dictionary Curve3D::_get_data() const {
PackedVector3Array d;
d.resize(points.size() * 3);
Vector3 *w = d.ptrw();
- PackedFloat32Array t;
+ Vector<real_t> t;
t.resize(points.size());
real_t *wt = t.ptrw();
@@ -1579,7 +1616,7 @@ void Curve3D::_set_data(const Dictionary &p_data) {
ERR_FAIL_COND(pc % 3 != 0);
points.resize(pc / 3);
const Vector3 *r = rp.ptr();
- PackedFloat32Array rtl = p_data["tilts"];
+ Vector<real_t> rtl = p_data["tilts"];
const real_t *rt = rtl.ptr();
for (int i = 0; i < points.size(); i++) {
@@ -1669,11 +1706,7 @@ void Curve3D::_bind_methods() {
}
Curve3D::Curve3D() {
- baked_cache_dirty = false;
- baked_max_ofs = 0;
/* add_point(Vector3(-1,0,0));
add_point(Vector3(0,2,0));
add_point(Vector3(0,3,5));*/
- bake_interval = 0.2;
- up_vector_enabled = true;
}
diff --git a/scene/resources/curve.h b/scene/resources/curve.h
index 57ddaf897d..5808fd6508 100644
--- a/scene/resources/curve.h
+++ b/scene/resources/curve.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,7 +31,7 @@
#ifndef CURVE_H
#define CURVE_H
-#include "core/resource.h"
+#include "core/io/resource.h"
// y(x) curve
class Curve : public Resource {
@@ -51,21 +51,17 @@ public:
struct Point {
Vector2 pos;
- real_t left_tangent;
- real_t right_tangent;
- TangentMode left_mode;
- TangentMode right_mode;
+ real_t left_tangent = 0.0;
+ real_t right_tangent = 0.0;
+ TangentMode left_mode = TANGENT_FREE;
+ TangentMode right_mode = TANGENT_FREE;
Point() {
- left_tangent = 0;
- right_tangent = 0;
- left_mode = TANGENT_FREE;
- right_mode = TANGENT_FREE;
}
Point(Vector2 p_pos,
- real_t p_left = 0,
- real_t p_right = 0,
+ real_t p_left = 0.0,
+ real_t p_right = 0.0,
TangentMode p_left_mode = TANGENT_FREE,
TangentMode p_right_mode = TANGENT_FREE) {
pos = p_pos;
@@ -126,7 +122,7 @@ public:
void bake();
int get_bake_resolution() const { return _bake_resolution; }
void set_bake_resolution(int p_resolution);
- real_t interpolate_baked(real_t offset);
+ real_t interpolate_baked(real_t offset) const;
void ensure_default_setup(float p_min, float p_max);
@@ -137,12 +133,12 @@ private:
void mark_dirty();
Vector<Point> _points;
- bool _baked_cache_dirty;
+ bool _baked_cache_dirty = false;
Vector<real_t> _baked_cache;
- int _bake_resolution;
- float _min_value;
- float _max_value;
- int _minmax_set_once; // Encodes whether min and max have been set a first time, first bit for min and second for max.
+ int _bake_resolution = 100;
+ float _min_value = 0.0;
+ float _max_value = 1.0;
+ int _minmax_set_once = 0b00; // Encodes whether min and max have been set a first time, first bit for min and second for max.
};
VARIANT_ENUM_CAST(Curve::TangentMode)
@@ -159,17 +155,18 @@ class Curve2D : public Resource {
Vector<Point> points;
struct BakedPoint {
- float ofs;
+ float ofs = 0.0;
Vector2 point;
};
- mutable bool baked_cache_dirty;
+ mutable bool baked_cache_dirty = false;
mutable PackedVector2Array baked_point_cache;
- mutable float baked_max_ofs;
+ mutable PackedFloat32Array baked_dist_cache;
+ mutable float baked_max_ofs = 0.0;
void _bake() const;
- float bake_interval;
+ float bake_interval = 5.0;
void _bake_segment2d(Map<float, Vector2> &r_bake, float p_begin, float p_end, const Vector2 &p_a, const Vector2 &p_out, const Vector2 &p_b, const Vector2 &p_in, int p_depth, int p_max_depth, float p_tol) const;
Dictionary _get_data() const;
@@ -214,28 +211,27 @@ class Curve3D : public Resource {
Vector3 in;
Vector3 out;
Vector3 pos;
- float tilt;
-
- Point() { tilt = 0; }
+ float tilt = 0.0;
};
Vector<Point> points;
struct BakedPoint {
- float ofs;
+ float ofs = 0.0;
Vector3 point;
};
- mutable bool baked_cache_dirty;
+ mutable bool baked_cache_dirty = false;
mutable PackedVector3Array baked_point_cache;
- mutable PackedFloat32Array baked_tilt_cache;
+ mutable Vector<real_t> baked_tilt_cache;
mutable PackedVector3Array baked_up_vector_cache;
- mutable float baked_max_ofs;
+ mutable PackedFloat32Array baked_dist_cache;
+ mutable float baked_max_ofs = 0.0;
void _bake() const;
- float bake_interval;
- bool up_vector_enabled;
+ float bake_interval = 0.2;
+ bool up_vector_enabled = true;
void _bake_segment3d(Map<float, Vector3> &r_bake, float p_begin, float p_end, const Vector3 &p_a, const Vector3 &p_out, const Vector3 &p_b, const Vector3 &p_in, int p_depth, int p_max_depth, float p_tol) const;
Dictionary _get_data() const;
@@ -271,7 +267,7 @@ public:
float interpolate_baked_tilt(float p_offset) const;
Vector3 interpolate_baked_up_vector(float p_offset, bool p_apply_tilt = false) const;
PackedVector3Array get_baked_points() const; //useful for going through
- PackedFloat32Array get_baked_tilts() const; //useful for going through
+ Vector<real_t> get_baked_tilts() const; //useful for going through
PackedVector3Array get_baked_up_vectors() const;
Vector3 get_closest_point(const Vector3 &p_to_point) const;
float get_closest_offset(const Vector3 &p_to_point) const;
diff --git a/scene/resources/cylinder_shape_3d.cpp b/scene/resources/cylinder_shape_3d.cpp
index ad64541247..63bdc8d26d 100644
--- a/scene/resources/cylinder_shape_3d.cpp
+++ b/scene/resources/cylinder_shape_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -75,7 +75,6 @@ void CylinderShape3D::set_radius(float p_radius) {
radius = p_radius;
_update_shape();
notify_change_to_owners();
- _change_notify("radius");
}
float CylinderShape3D::get_radius() const {
@@ -86,7 +85,6 @@ void CylinderShape3D::set_height(float p_height) {
height = p_height;
_update_shape();
notify_change_to_owners();
- _change_notify("height");
}
float CylinderShape3D::get_height() const {
@@ -99,13 +97,11 @@ void CylinderShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_height", "height"), &CylinderShape3D::set_height);
ClassDB::bind_method(D_METHOD("get_height"), &CylinderShape3D::get_height);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,4096,0.01"), "set_radius", "get_radius");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.01,4096,0.01"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,4096,0.001"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,4096,0.001"), "set_height", "get_height");
}
CylinderShape3D::CylinderShape3D() :
Shape3D(PhysicsServer3D::get_singleton()->shape_create(PhysicsServer3D::SHAPE_CYLINDER)) {
- radius = 1.0;
- height = 2.0;
_update_shape();
}
diff --git a/scene/resources/cylinder_shape_3d.h b/scene/resources/cylinder_shape_3d.h
index e579e1f7cf..d1b8364672 100644
--- a/scene/resources/cylinder_shape_3d.h
+++ b/scene/resources/cylinder_shape_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,8 +35,8 @@
class CylinderShape3D : public Shape3D {
GDCLASS(CylinderShape3D, Shape3D);
- float radius;
- float height;
+ float radius = 1.0;
+ float height = 2.0;
protected:
static void _bind_methods();
diff --git a/scene/resources/default_theme/SCsub b/scene/resources/default_theme/SCsub
index fc61250247..0fb6bb2c62 100644
--- a/scene/resources/default_theme/SCsub
+++ b/scene/resources/default_theme/SCsub
@@ -2,4 +2,16 @@
Import("env")
+import os
+import os.path
+from platform_methods import run_in_subprocess
+import default_theme_builders
+
env.add_source_files(env.scene_sources, "*.cpp")
+
+env.Depends("#scene/resources/default_theme/default_font.gen.h", "#thirdparty/fonts/OpenSans_SemiBold.ttf")
+env.CommandNoCache(
+ "#scene/resources/default_theme/default_font.gen.h",
+ "#thirdparty/fonts/OpenSans_SemiBold.ttf",
+ run_in_subprocess(default_theme_builders.make_fonts_header),
+)
diff --git a/scene/resources/default_theme/arrow_left.png b/scene/resources/default_theme/arrow_left.png
new file mode 100644
index 0000000000..4163059dd3
--- /dev/null
+++ b/scene/resources/default_theme/arrow_left.png
Binary files differ
diff --git a/scene/resources/default_theme/bar_arrow.png b/scene/resources/default_theme/bar_arrow.png
new file mode 100644
index 0000000000..7cf146b8da
--- /dev/null
+++ b/scene/resources/default_theme/bar_arrow.png
Binary files differ
diff --git a/scene/resources/default_theme/checked_disabled.png b/scene/resources/default_theme/checked_disabled.png
new file mode 100644
index 0000000000..70549e2edc
--- /dev/null
+++ b/scene/resources/default_theme/checked_disabled.png
Binary files differ
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index f65f78b332..4845c556c6 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,21 +30,20 @@
#include "default_theme.h"
-#include "scene/resources/theme.h"
-
#include "core/os/os.h"
+#include "default_font.gen.h"
+#include "scene/resources/font.h"
+#include "scene/resources/theme.h"
+#include "servers/text_server.h"
#include "theme_data.h"
-#include "font_hidpi.inc"
-#include "font_lodpi.inc"
-
typedef Map<const void *, Ref<ImageTexture>> TexCacheMap;
static TexCacheMap *tex_cache;
-static float scale = 1;
+static float scale = 1.0;
template <class T>
-static Ref<StyleBoxTexture> make_stylebox(T p_src, float p_left, float p_top, float p_right, float p_botton, float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_botton = -1, bool p_draw_center = true) {
+static Ref<StyleBoxTexture> make_stylebox(T p_src, float p_left, float p_top, float p_right, float p_bottom, float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_bottom = -1, bool p_draw_center = true) {
Ref<ImageTexture> texture;
if (tex_cache->has(p_src)) {
@@ -62,24 +61,35 @@ static Ref<StyleBoxTexture> make_stylebox(T p_src, float p_left, float p_top, fl
Ref<StyleBoxTexture> style(memnew(StyleBoxTexture));
style->set_texture(texture);
- style->set_margin_size(MARGIN_LEFT, p_left * scale);
- style->set_margin_size(MARGIN_RIGHT, p_right * scale);
- style->set_margin_size(MARGIN_BOTTOM, p_botton * scale);
- style->set_margin_size(MARGIN_TOP, p_top * scale);
- style->set_default_margin(MARGIN_LEFT, p_margin_left * scale);
- style->set_default_margin(MARGIN_RIGHT, p_margin_right * scale);
- style->set_default_margin(MARGIN_BOTTOM, p_margin_botton * scale);
- style->set_default_margin(MARGIN_TOP, p_margin_top * scale);
+ style->set_margin_size(SIDE_LEFT, p_left * scale);
+ style->set_margin_size(SIDE_RIGHT, p_right * scale);
+ style->set_margin_size(SIDE_BOTTOM, p_bottom * scale);
+ style->set_margin_size(SIDE_TOP, p_top * scale);
+ style->set_default_margin(SIDE_LEFT, p_margin_left * scale);
+ style->set_default_margin(SIDE_RIGHT, p_margin_right * scale);
+ style->set_default_margin(SIDE_BOTTOM, p_margin_bottom * scale);
+ style->set_default_margin(SIDE_TOP, p_margin_top * scale);
style->set_draw_center(p_draw_center);
return style;
}
-static Ref<StyleBoxTexture> sb_expand(Ref<StyleBoxTexture> p_sbox, float p_left, float p_top, float p_right, float p_botton) {
- p_sbox->set_expand_margin_size(MARGIN_LEFT, p_left * scale);
- p_sbox->set_expand_margin_size(MARGIN_TOP, p_top * scale);
- p_sbox->set_expand_margin_size(MARGIN_RIGHT, p_right * scale);
- p_sbox->set_expand_margin_size(MARGIN_BOTTOM, p_botton * scale);
+static Ref<StyleBoxFlat> make_flat_stylebox(Color p_color, float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_bottom = -1) {
+ Ref<StyleBoxFlat> style(memnew(StyleBoxFlat));
+ style->set_bg_color(p_color);
+ style->set_default_margin(SIDE_LEFT, p_margin_left * scale);
+ style->set_default_margin(SIDE_RIGHT, p_margin_right * scale);
+ style->set_default_margin(SIDE_BOTTOM, p_margin_bottom * scale);
+ style->set_default_margin(SIDE_TOP, p_margin_top * scale);
+
+ return style;
+}
+
+static Ref<StyleBoxTexture> sb_expand(Ref<StyleBoxTexture> p_sbox, float p_left, float p_top, float p_right, float p_bottom) {
+ p_sbox->set_expand_margin_size(SIDE_LEFT, p_left * scale);
+ p_sbox->set_expand_margin_size(SIDE_TOP, p_top * scale);
+ p_sbox->set_expand_margin_size(SIDE_RIGHT, p_right * scale);
+ p_sbox->set_expand_margin_size(SIDE_BOTTOM, p_bottom * scale);
return p_sbox;
}
@@ -95,47 +105,33 @@ static Ref<Texture2D> make_icon(T p_src) {
return texture;
}
-static Ref<BitmapFont> make_font(int p_height, int p_ascent, int p_charcount, const int *p_char_rects, int p_kerning_count, const int *p_kernings, int p_w, int p_h, const unsigned char *p_img) {
- Ref<BitmapFont> font(memnew(BitmapFont));
-
- Ref<Image> image = memnew(Image(p_img));
- Ref<ImageTexture> tex = memnew(ImageTexture);
- tex->create_from_image(image);
-
- font->add_texture(tex);
-
- for (int i = 0; i < p_charcount; i++) {
- const int *c = &p_char_rects[i * 8];
+static Ref<Texture2D> flip_icon(Ref<Texture2D> p_texture, bool p_flip_y = false, bool p_flip_x = false) {
+ if (!p_flip_y && !p_flip_x) {
+ return p_texture;
+ }
- int chr = c[0];
- Rect2 frect;
- frect.position.x = c[1];
- frect.position.y = c[2];
- frect.size.x = c[3];
- frect.size.y = c[4];
- Point2 align(c[6], c[5]);
- int advance = c[7];
+ Ref<ImageTexture> texture(memnew(ImageTexture));
+ Ref<Image> img = p_texture->get_image();
+ img = img->duplicate();
- font->add_char(chr, 0, frect, align, advance);
+ if (p_flip_y) {
+ img->flip_y();
}
-
- for (int i = 0; i < p_kerning_count; i++) {
- font->add_kerning_pair(p_kernings[i * 3 + 0], p_kernings[i * 3 + 1], p_kernings[i * 3 + 2]);
+ if (p_flip_x) {
+ img->flip_x();
}
- font->set_height(p_height);
- font->set_ascent(p_ascent);
-
- return font;
+ texture->create_from_image(img);
+ return texture;
}
-static Ref<StyleBox> make_empty_stylebox(float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_botton = -1) {
+static Ref<StyleBox> make_empty_stylebox(float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_bottom = -1) {
Ref<StyleBox> style(memnew(StyleBoxEmpty));
- style->set_default_margin(MARGIN_LEFT, p_margin_left * scale);
- style->set_default_margin(MARGIN_RIGHT, p_margin_right * scale);
- style->set_default_margin(MARGIN_BOTTOM, p_margin_botton * scale);
- style->set_default_margin(MARGIN_TOP, p_margin_top * scale);
+ style->set_default_margin(SIDE_LEFT, p_margin_left * scale);
+ style->set_default_margin(SIDE_RIGHT, p_margin_right * scale);
+ style->set_default_margin(SIDE_BOTTOM, p_margin_bottom * scale);
+ style->set_default_margin(SIDE_TOP, p_margin_top * scale);
return style;
}
@@ -148,12 +144,13 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// Font Colors
Color control_font_color = Color(0.88, 0.88, 0.88);
- Color control_font_color_lower = Color(0.63, 0.63, 0.63);
- Color control_font_color_low = Color(0.69, 0.69, 0.69);
- Color control_font_color_hover = Color(0.94, 0.94, 0.94);
- Color control_font_color_disabled = Color(0.9, 0.9, 0.9, 0.2);
- Color control_font_color_pressed = Color(1, 1, 1);
- Color font_color_selection = Color(0.49, 0.49, 0.49);
+ Color control_font_lower_color = Color(0.63, 0.63, 0.63);
+ Color control_font_low_color = Color(0.69, 0.69, 0.69);
+ Color control_font_hover_color = Color(0.94, 0.94, 0.94);
+ Color control_font_disabled_color = Color(0.9, 0.9, 0.9, 0.2);
+ Color control_font_pressed_color = Color(1, 1, 1);
+
+ Color control_selection_color = Color(0.49, 0.49, 0.49);
// Panel
@@ -164,7 +161,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
Ref<StyleBoxTexture> focus = make_stylebox(focus_png, 5, 5, 5, 5);
for (int i = 0; i < 4; i++) {
- focus->set_expand_margin_size(Margin(i), 1 * scale);
+ focus->set_expand_margin_size(Side(i), 1 * scale);
}
// Button
@@ -182,11 +179,21 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("focus", "Button", sb_button_focus);
theme->set_font("font", "Button", Ref<Font>());
+ theme->set_font_size("font_size", "Button", -1);
+ theme->set_constant("outline_size", "Button", 0 * scale);
theme->set_color("font_color", "Button", control_font_color);
- theme->set_color("font_color_pressed", "Button", control_font_color_pressed);
- theme->set_color("font_color_hover", "Button", control_font_color_hover);
- theme->set_color("font_color_disabled", "Button", control_font_color_disabled);
+ theme->set_color("font_pressed_color", "Button", control_font_pressed_color);
+ theme->set_color("font_hover_color", "Button", control_font_hover_color);
+ theme->set_color("font_hover_pressed_color", "Button", control_font_pressed_color);
+ theme->set_color("font_disabled_color", "Button", control_font_disabled_color);
+ theme->set_color("font_outline_color", "Button", Color(1, 1, 1));
+
+ theme->set_color("icon_normal_color", "Button", Color(1, 1, 1, 1));
+ theme->set_color("icon_pressed_color", "Button", Color(1, 1, 1, 1));
+ theme->set_color("icon_hover_color", "Button", Color(1, 1, 1, 1));
+ theme->set_color("icon_hover_pressed_color", "Button", Color(1, 1, 1, 1));
+ theme->set_color("icon_disabled_color", "Button", Color(1, 1, 1, 1));
theme->set_constant("hseparation", "Button", 2 * scale);
@@ -195,55 +202,55 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("focus", "LinkButton", focus);
theme->set_font("font", "LinkButton", Ref<Font>());
+ theme->set_font_size("font_size", "LinkButton", -1);
theme->set_color("font_color", "LinkButton", control_font_color);
- theme->set_color("font_color_pressed", "LinkButton", control_font_color_pressed);
- theme->set_color("font_color_hover", "LinkButton", control_font_color_hover);
+ theme->set_color("font_pressed_color", "LinkButton", control_font_pressed_color);
+ theme->set_color("font_hover_color", "LinkButton", control_font_hover_color);
+ theme->set_color("font_outline_color", "LinkButton", Color(1, 1, 1));
+ theme->set_constant("outline_size", "LinkButton", 0);
theme->set_constant("underline_spacing", "LinkButton", 2 * scale);
- // ColorPickerButton
-
- theme->set_stylebox("normal", "ColorPickerButton", sb_button_normal);
- theme->set_stylebox("pressed", "ColorPickerButton", sb_button_pressed);
- theme->set_stylebox("hover", "ColorPickerButton", sb_button_hover);
- theme->set_stylebox("disabled", "ColorPickerButton", sb_button_disabled);
- theme->set_stylebox("focus", "ColorPickerButton", sb_button_focus);
-
- theme->set_font("font", "ColorPickerButton", Ref<Font>());
-
- theme->set_color("font_color", "ColorPickerButton", Color(1, 1, 1, 1));
- theme->set_color("font_color_pressed", "ColorPickerButton", Color(0.8, 0.8, 0.8, 1));
- theme->set_color("font_color_hover", "ColorPickerButton", Color(1, 1, 1, 1));
- theme->set_color("font_color_disabled", "ColorPickerButton", Color(0.9, 0.9, 0.9, 0.3));
-
- theme->set_constant("hseparation", "ColorPickerButton", 2 * scale);
-
// OptionButton
+ Ref<StyleBox> sb_optbutton_focus = sb_expand(make_stylebox(button_focus_png, 4, 4, 4, 4, 6, 2, 6, 2), 2, 2, 2, 2);
+ theme->set_stylebox("focus", "OptionButton", sb_optbutton_focus);
+
Ref<StyleBox> sb_optbutton_normal = sb_expand(make_stylebox(option_button_normal_png, 4, 4, 21, 4, 6, 3, 9, 3), 2, 2, 2, 2);
Ref<StyleBox> sb_optbutton_pressed = sb_expand(make_stylebox(option_button_pressed_png, 4, 4, 21, 4, 6, 3, 9, 3), 2, 2, 2, 2);
Ref<StyleBox> sb_optbutton_hover = sb_expand(make_stylebox(option_button_hover_png, 4, 4, 21, 4, 6, 2, 9, 2), 2, 2, 2, 2);
Ref<StyleBox> sb_optbutton_disabled = sb_expand(make_stylebox(option_button_disabled_png, 4, 4, 21, 4, 6, 2, 9, 2), 2, 2, 2, 2);
- Ref<StyleBox> sb_optbutton_focus = sb_expand(make_stylebox(button_focus_png, 4, 4, 4, 4, 6, 2, 6, 2), 2, 2, 2, 2);
theme->set_stylebox("normal", "OptionButton", sb_optbutton_normal);
theme->set_stylebox("pressed", "OptionButton", sb_optbutton_pressed);
theme->set_stylebox("hover", "OptionButton", sb_optbutton_hover);
theme->set_stylebox("disabled", "OptionButton", sb_optbutton_disabled);
- theme->set_stylebox("focus", "OptionButton", sb_optbutton_focus);
+
+ Ref<StyleBox> sb_optbutton_normal_mirrored = sb_expand(make_stylebox(option_button_normal_mirrored_png, 21, 4, 4, 4, 9, 3, 6, 3), 2, 2, 2, 2);
+ Ref<StyleBox> sb_optbutton_pressed_mirrored = sb_expand(make_stylebox(option_button_pressed_mirrored_png, 21, 4, 4, 4, 9, 3, 6, 3), 2, 2, 2, 2);
+ Ref<StyleBox> sb_optbutton_hover_mirrored = sb_expand(make_stylebox(option_button_hover_mirrored_png, 21, 4, 4, 4, 9, 2, 6, 2), 2, 2, 2, 2);
+ Ref<StyleBox> sb_optbutton_disabled_mirrored = sb_expand(make_stylebox(option_button_disabled_mirrored_png, 21, 4, 4, 4, 9, 2, 6, 2), 2, 2, 2, 2);
+
+ theme->set_stylebox("normal_mirrored", "OptionButton", sb_optbutton_normal_mirrored);
+ theme->set_stylebox("pressed_mirrored", "OptionButton", sb_optbutton_pressed_mirrored);
+ theme->set_stylebox("hover_mirrored", "OptionButton", sb_optbutton_hover_mirrored);
+ theme->set_stylebox("disabled_mirrored", "OptionButton", sb_optbutton_disabled_mirrored);
theme->set_icon("arrow", "OptionButton", make_icon(option_arrow_png));
theme->set_font("font", "OptionButton", Ref<Font>());
+ theme->set_font_size("font_size", "OptionButton", -1);
theme->set_color("font_color", "OptionButton", control_font_color);
- theme->set_color("font_color_pressed", "OptionButton", control_font_color_pressed);
- theme->set_color("font_color_hover", "OptionButton", control_font_color_hover);
- theme->set_color("font_color_disabled", "OptionButton", control_font_color_disabled);
+ theme->set_color("font_pressed_color", "OptionButton", control_font_pressed_color);
+ theme->set_color("font_hover_color", "OptionButton", control_font_hover_color);
+ theme->set_color("font_disabled_color", "OptionButton", control_font_disabled_color);
+ theme->set_color("font_outline_color", "OptionButton", Color(1, 1, 1));
theme->set_constant("hseparation", "OptionButton", 2 * scale);
theme->set_constant("arrow_margin", "OptionButton", 2 * scale);
+ theme->set_constant("outline_size", "OptionButton", 0);
// MenuButton
@@ -254,26 +261,29 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("focus", "MenuButton", sb_button_focus);
theme->set_font("font", "MenuButton", Ref<Font>());
+ theme->set_font_size("font_size", "MenuButton", -1);
theme->set_color("font_color", "MenuButton", control_font_color);
- theme->set_color("font_color_pressed", "MenuButton", control_font_color_pressed);
- theme->set_color("font_color_hover", "MenuButton", control_font_color_hover);
- theme->set_color("font_color_disabled", "MenuButton", Color(1, 1, 1, 0.3));
+ theme->set_color("font_pressed_color", "MenuButton", control_font_pressed_color);
+ theme->set_color("font_hover_color", "MenuButton", control_font_hover_color);
+ theme->set_color("font_disabled_color", "MenuButton", Color(1, 1, 1, 0.3));
+ theme->set_color("font_outline_color", "MenuButton", Color(1, 1, 1));
theme->set_constant("hseparation", "MenuButton", 3 * scale);
+ theme->set_constant("outline_size", "MenuButton", 0);
// CheckBox
Ref<StyleBox> cbx_empty = memnew(StyleBoxEmpty);
- cbx_empty->set_default_margin(MARGIN_LEFT, 4 * scale);
- cbx_empty->set_default_margin(MARGIN_RIGHT, 4 * scale);
- cbx_empty->set_default_margin(MARGIN_TOP, 4 * scale);
- cbx_empty->set_default_margin(MARGIN_BOTTOM, 4 * scale);
+ cbx_empty->set_default_margin(SIDE_LEFT, 4 * scale);
+ cbx_empty->set_default_margin(SIDE_RIGHT, 4 * scale);
+ cbx_empty->set_default_margin(SIDE_TOP, 4 * scale);
+ cbx_empty->set_default_margin(SIDE_BOTTOM, 4 * scale);
Ref<StyleBox> cbx_focus = focus;
- cbx_focus->set_default_margin(MARGIN_LEFT, 4 * scale);
- cbx_focus->set_default_margin(MARGIN_RIGHT, 4 * scale);
- cbx_focus->set_default_margin(MARGIN_TOP, 4 * scale);
- cbx_focus->set_default_margin(MARGIN_BOTTOM, 4 * scale);
+ cbx_focus->set_default_margin(SIDE_LEFT, 4 * scale);
+ cbx_focus->set_default_margin(SIDE_RIGHT, 4 * scale);
+ cbx_focus->set_default_margin(SIDE_TOP, 4 * scale);
+ cbx_focus->set_default_margin(SIDE_BOTTOM, 4 * scale);
theme->set_stylebox("normal", "CheckBox", cbx_empty);
theme->set_stylebox("pressed", "CheckBox", cbx_empty);
@@ -283,28 +293,35 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("focus", "CheckBox", cbx_focus);
theme->set_icon("checked", "CheckBox", make_icon(checked_png));
+ theme->set_icon("checked_disabled", "CheckBox", make_icon(checked_disabled_png));
theme->set_icon("unchecked", "CheckBox", make_icon(unchecked_png));
+ theme->set_icon("unchecked_disabled", "CheckBox", make_icon(unchecked_disabled_png));
theme->set_icon("radio_checked", "CheckBox", make_icon(radio_checked_png));
+ theme->set_icon("radio_checked_disabled", "CheckBox", make_icon(radio_checked_disabled_png));
theme->set_icon("radio_unchecked", "CheckBox", make_icon(radio_unchecked_png));
+ theme->set_icon("radio_unchecked_disabled", "CheckBox", make_icon(radio_unchecked_disabled_png));
theme->set_font("font", "CheckBox", Ref<Font>());
+ theme->set_font_size("font_size", "CheckBox", -1);
theme->set_color("font_color", "CheckBox", control_font_color);
- theme->set_color("font_color_pressed", "CheckBox", control_font_color_pressed);
- theme->set_color("font_color_hover", "CheckBox", control_font_color_hover);
- theme->set_color("font_color_hover_pressed", "CheckBox", control_font_color_pressed);
- theme->set_color("font_color_disabled", "CheckBox", control_font_color_disabled);
+ theme->set_color("font_pressed_color", "CheckBox", control_font_pressed_color);
+ theme->set_color("font_hover_color", "CheckBox", control_font_hover_color);
+ theme->set_color("font_hover_pressed_color", "CheckBox", control_font_pressed_color);
+ theme->set_color("font_disabled_color", "CheckBox", control_font_disabled_color);
+ theme->set_color("font_outline_color", "CheckBox", Color(1, 1, 1));
theme->set_constant("hseparation", "CheckBox", 4 * scale);
theme->set_constant("check_vadjust", "CheckBox", 0 * scale);
+ theme->set_constant("outline_size", "CheckBox", 0);
// CheckButton
Ref<StyleBox> cb_empty = memnew(StyleBoxEmpty);
- cb_empty->set_default_margin(MARGIN_LEFT, 6 * scale);
- cb_empty->set_default_margin(MARGIN_RIGHT, 6 * scale);
- cb_empty->set_default_margin(MARGIN_TOP, 4 * scale);
- cb_empty->set_default_margin(MARGIN_BOTTOM, 4 * scale);
+ cb_empty->set_default_margin(SIDE_LEFT, 6 * scale);
+ cb_empty->set_default_margin(SIDE_RIGHT, 6 * scale);
+ cb_empty->set_default_margin(SIDE_TOP, 4 * scale);
+ cb_empty->set_default_margin(SIDE_BOTTOM, 4 * scale);
theme->set_stylebox("normal", "CheckButton", cb_empty);
theme->set_stylebox("pressed", "CheckButton", cb_empty);
@@ -318,31 +335,50 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("off", "CheckButton", make_icon(toggle_off_png));
theme->set_icon("off_disabled", "CheckButton", make_icon(toggle_off_disabled_png));
+ theme->set_icon("on_mirrored", "CheckButton", make_icon(toggle_on_mirrored_png));
+ theme->set_icon("on_disabled_mirrored", "CheckButton", make_icon(toggle_on_disabled_mirrored_png));
+ theme->set_icon("off_mirrored", "CheckButton", make_icon(toggle_off_mirrored_png));
+ theme->set_icon("off_disabled_mirrored", "CheckButton", make_icon(toggle_off_disabled_mirrored_png));
+
theme->set_font("font", "CheckButton", Ref<Font>());
+ theme->set_font_size("font_size", "CheckButton", -1);
theme->set_color("font_color", "CheckButton", control_font_color);
- theme->set_color("font_color_pressed", "CheckButton", control_font_color_pressed);
- theme->set_color("font_color_hover", "CheckButton", control_font_color_hover);
- theme->set_color("font_color_hover_pressed", "CheckButton", control_font_color_pressed);
- theme->set_color("font_color_disabled", "CheckButton", control_font_color_disabled);
+ theme->set_color("font_pressed_color", "CheckButton", control_font_pressed_color);
+ theme->set_color("font_hover_color", "CheckButton", control_font_hover_color);
+ theme->set_color("font_hover_pressed_color", "CheckButton", control_font_pressed_color);
+ theme->set_color("font_disabled_color", "CheckButton", control_font_disabled_color);
+ theme->set_color("font_outline_color", "CheckButton", Color(1, 1, 1));
theme->set_constant("hseparation", "CheckButton", 4 * scale);
theme->set_constant("check_vadjust", "CheckButton", 0 * scale);
+ theme->set_constant("outline_size", "CheckButton", 0);
// Label
theme->set_stylebox("normal", "Label", memnew(StyleBoxEmpty));
theme->set_font("font", "Label", Ref<Font>());
+ theme->set_font_size("font_size", "Label", -1);
theme->set_color("font_color", "Label", Color(1, 1, 1));
- theme->set_color("font_color_shadow", "Label", Color(0, 0, 0, 0));
- theme->set_color("font_outline_modulate", "Label", Color(1, 1, 1));
+ theme->set_color("font_shadow_color", "Label", Color(0, 0, 0, 0));
+ theme->set_color("font_outline_color", "Label", Color(1, 1, 1));
theme->set_constant("shadow_offset_x", "Label", 1 * scale);
theme->set_constant("shadow_offset_y", "Label", 1 * scale);
- theme->set_constant("shadow_as_outline", "Label", 0 * scale);
+ theme->set_constant("outline_size", "Label", 0);
+ theme->set_constant("shadow_outline_size", "Label", 1 * scale);
theme->set_constant("line_spacing", "Label", 3 * scale);
+ theme->set_type_variation("HeaderSmall", "Label");
+ theme->set_font_size("font_size", "HeaderSmall", default_font_size + 4);
+
+ theme->set_type_variation("HeaderMedium", "Label");
+ theme->set_font_size("font_size", "HeaderMedium", default_font_size + 8);
+
+ theme->set_type_variation("HeaderLarge", "Label");
+ theme->set_font_size("font_size", "HeaderLarge", default_font_size + 12);
+
// LineEdit
theme->set_stylebox("normal", "LineEdit", make_stylebox(line_edit_png, 5, 5, 5, 5));
@@ -350,16 +386,19 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("read_only", "LineEdit", make_stylebox(line_edit_disabled_png, 6, 6, 6, 6));
theme->set_font("font", "LineEdit", Ref<Font>());
+ theme->set_font_size("font_size", "LineEdit", -1);
theme->set_color("font_color", "LineEdit", control_font_color);
- theme->set_color("font_color_selected", "LineEdit", Color(0, 0, 0));
- theme->set_color("font_color_uneditable", "LineEdit", Color(control_font_color.r, control_font_color.g, control_font_color.b, 0.5f));
- theme->set_color("cursor_color", "LineEdit", control_font_color_hover);
- theme->set_color("selection_color", "LineEdit", font_color_selection);
+ theme->set_color("font_selected_color", "LineEdit", Color(0, 0, 0));
+ theme->set_color("font_uneditable_color", "LineEdit", Color(control_font_color.r, control_font_color.g, control_font_color.b, 0.5f));
+ theme->set_color("font_outline_color", "LineEdit", Color(1, 1, 1));
+ theme->set_color("caret_color", "LineEdit", control_font_hover_color);
+ theme->set_color("selection_color", "LineEdit", control_selection_color);
theme->set_color("clear_button_color", "LineEdit", control_font_color);
- theme->set_color("clear_button_color_pressed", "LineEdit", control_font_color_pressed);
+ theme->set_color("clear_button_color_pressed", "LineEdit", control_font_pressed_color);
- theme->set_constant("minimum_spaces", "LineEdit", 12 * scale);
+ theme->set_constant("minimum_character_width", "LineEdit", 4);
+ theme->set_constant("outline_size", "LineEdit", 0);
theme->set_icon("clear", "LineEdit", make_icon(line_edit_clear_png));
@@ -369,46 +408,42 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("fg", "ProgressBar", make_stylebox(progress_fill_png, 6, 6, 6, 6, 2, 1, 2, 1));
theme->set_font("font", "ProgressBar", Ref<Font>());
+ theme->set_font_size("font_size", "ProgressBar", -1);
+
+ theme->set_color("font_color", "ProgressBar", control_font_hover_color);
+ theme->set_color("font_shadow_color", "ProgressBar", Color(0, 0, 0));
+ theme->set_color("font_outline_color", "ProgressBar", Color(1, 1, 1));
- theme->set_color("font_color", "ProgressBar", control_font_color_hover);
- theme->set_color("font_color_shadow", "ProgressBar", Color(0, 0, 0));
+ theme->set_constant("outline_size", "ProgressBar", 0);
// TextEdit
theme->set_stylebox("normal", "TextEdit", make_stylebox(tree_bg_png, 3, 3, 3, 3, 0, 0, 0, 0));
theme->set_stylebox("focus", "TextEdit", focus);
theme->set_stylebox("read_only", "TextEdit", make_stylebox(tree_bg_disabled_png, 4, 4, 4, 4, 0, 0, 0, 0));
- theme->set_stylebox("completion", "TextEdit", make_stylebox(tree_bg_png, 3, 3, 3, 3, 0, 0, 0, 0));
theme->set_icon("tab", "TextEdit", make_icon(tab_png));
theme->set_icon("space", "TextEdit", make_icon(space_png));
theme->set_font("font", "TextEdit", Ref<Font>());
+ theme->set_font_size("font_size", "TextEdit", -1);
theme->set_color("background_color", "TextEdit", Color(0, 0, 0, 0));
- theme->set_color("completion_background_color", "TextEdit", Color(0.17, 0.16, 0.2));
- theme->set_color("completion_selected_color", "TextEdit", Color(0.26, 0.26, 0.27));
- theme->set_color("completion_existing_color", "TextEdit", Color(0.87, 0.87, 0.87, 0.13));
- theme->set_color("completion_scroll_color", "TextEdit", control_font_color_pressed);
- theme->set_color("completion_font_color", "TextEdit", Color(0.67, 0.67, 0.67));
theme->set_color("font_color", "TextEdit", control_font_color);
- theme->set_color("font_color_selected", "TextEdit", Color(0, 0, 0));
- theme->set_color("font_color_readonly", "TextEdit", Color(control_font_color.r, control_font_color.g, control_font_color.b, 0.5f));
- theme->set_color("selection_color", "TextEdit", font_color_selection);
- theme->set_color("mark_color", "TextEdit", Color(1.0, 0.4, 0.4, 0.4));
- theme->set_color("code_folding_color", "TextEdit", Color(0.8, 0.8, 0.8, 0.8));
+ theme->set_color("font_selected_color", "TextEdit", Color(0, 0, 0));
+ theme->set_color("font_readonly_color", "TextEdit", Color(control_font_color.r, control_font_color.g, control_font_color.b, 0.5f));
+ theme->set_color("font_outline_color", "TextEdit", Color(1, 1, 1));
+ theme->set_color("selection_color", "TextEdit", control_selection_color);
theme->set_color("current_line_color", "TextEdit", Color(0.25, 0.25, 0.26, 0.8));
theme->set_color("caret_color", "TextEdit", control_font_color);
theme->set_color("caret_background_color", "TextEdit", Color(0, 0, 0));
- theme->set_color("brace_mismatch_color", "TextEdit", Color(1, 0.2, 0.2));
theme->set_color("word_highlighted_color", "TextEdit", Color(0.8, 0.9, 0.9, 0.15));
- theme->set_constant("completion_lines", "TextEdit", 7);
- theme->set_constant("completion_max_width", "TextEdit", 50);
- theme->set_constant("completion_scroll_width", "TextEdit", 3);
theme->set_constant("line_spacing", "TextEdit", 4 * scale);
+ theme->set_constant("outline_size", "TextEdit", 0);
// CodeEdit
+
theme->set_stylebox("normal", "CodeEdit", make_stylebox(tree_bg_png, 3, 3, 3, 3, 0, 0, 0, 0));
theme->set_stylebox("focus", "CodeEdit", focus);
theme->set_stylebox("read_only", "CodeEdit", make_stylebox(tree_bg_disabled_png, 4, 4, 4, 4, 0, 0, 0, 0));
@@ -421,36 +456,39 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("executing_line", "CodeEdit", make_icon(arrow_right_png));
theme->set_icon("can_fold", "CodeEdit", make_icon(arrow_down_png));
theme->set_icon("folded", "CodeEdit", make_icon(arrow_right_png));
+ theme->set_icon("folded_eol_icon", "CodeEdit", make_icon(ellipsis_png));
theme->set_font("font", "CodeEdit", Ref<Font>());
+ theme->set_font_size("font_size", "CodeEdit", -1);
theme->set_color("background_color", "CodeEdit", Color(0, 0, 0, 0));
theme->set_color("completion_background_color", "CodeEdit", Color(0.17, 0.16, 0.2));
theme->set_color("completion_selected_color", "CodeEdit", Color(0.26, 0.26, 0.27));
theme->set_color("completion_existing_color", "CodeEdit", Color(0.87, 0.87, 0.87, 0.13));
- theme->set_color("completion_scroll_color", "CodeEdit", control_font_color_pressed);
+ theme->set_color("completion_scroll_color", "CodeEdit", control_font_pressed_color);
theme->set_color("completion_font_color", "CodeEdit", Color(0.67, 0.67, 0.67));
theme->set_color("font_color", "CodeEdit", control_font_color);
- theme->set_color("font_color_selected", "CodeEdit", Color(0, 0, 0));
- theme->set_color("font_color_readonly", "CodeEdit", Color(control_font_color.r, control_font_color.g, control_font_color.b, 0.5f));
- theme->set_color("selection_color", "CodeEdit", font_color_selection);
- theme->set_color("mark_color", "CodeEdit", Color(1.0, 0.4, 0.4, 0.4));
+ theme->set_color("font_selected_color", "CodeEdit", Color(0, 0, 0));
+ theme->set_color("font_readonly_color", "CodeEdit", Color(control_font_color.r, control_font_color.g, control_font_color.b, 0.5f));
+ theme->set_color("font_outline_color", "CodeEdit", Color(1, 1, 1));
+ theme->set_color("selection_color", "CodeEdit", control_selection_color);
theme->set_color("bookmark_color", "CodeEdit", Color(0.5, 0.64, 1, 0.8));
theme->set_color("breakpoint_color", "CodeEdit", Color(0.9, 0.29, 0.3));
theme->set_color("executing_line_color", "CodeEdit", Color(0.98, 0.89, 0.27));
- theme->set_color("code_folding_color", "CodeEdit", Color(0.8, 0.8, 0.8, 0.8));
theme->set_color("current_line_color", "CodeEdit", Color(0.25, 0.25, 0.26, 0.8));
+ theme->set_color("code_folding_color", "CodeEdit", Color(0.8, 0.8, 0.8, 0.8));
theme->set_color("caret_color", "CodeEdit", control_font_color);
theme->set_color("caret_background_color", "CodeEdit", Color(0, 0, 0));
theme->set_color("brace_mismatch_color", "CodeEdit", Color(1, 0.2, 0.2));
theme->set_color("line_number_color", "CodeEdit", Color(0.67, 0.67, 0.67, 0.4));
- theme->set_color("safe_line_number_color", "CodeEdit", Color(0.67, 0.78, 0.67, 0.6));
theme->set_color("word_highlighted_color", "CodeEdit", Color(0.8, 0.9, 0.9, 0.15));
+ theme->set_color("line_length_guideline_color", "CodeEdit", Color(0.3, 0.5, 0.8, 0.1));
theme->set_constant("completion_lines", "CodeEdit", 7);
theme->set_constant("completion_max_width", "CodeEdit", 50);
theme->set_constant("completion_scroll_width", "CodeEdit", 3);
theme->set_constant("line_spacing", "CodeEdit", 4 * scale);
+ theme->set_constant("outline_size", "CodeEdit", 0);
Ref<Texture2D> empty_icon = memnew(ImageTexture);
@@ -506,30 +544,41 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("updown", "SpinBox", make_icon(spinbox_updown_png));
- //scroll container
+ // ScrollContainer
+
Ref<StyleBoxEmpty> empty;
- empty.instance();
+ empty.instantiate();
theme->set_stylebox("bg", "ScrollContainer", empty);
- // WindowDialog
+ // Window
- theme->set_stylebox("panel", "Window", default_style);
- theme->set_stylebox("window_panel", "Window", sb_expand(make_stylebox(popup_window_png, 10, 26, 10, 8), 8, 24, 8, 6));
+ theme->set_stylebox("embedded_border", "Window", sb_expand(make_stylebox(popup_window_png, 10, 26, 10, 8), 8, 24, 8, 6));
theme->set_constant("scaleborder_size", "Window", 4 * scale);
theme->set_font("title_font", "Window", large_font);
+ theme->set_font_size("title_font_size", "Window", -1);
+
theme->set_color("title_color", "Window", Color(0, 0, 0));
+ theme->set_color("title_outline_modulate", "Window", Color(1, 1, 1));
+
+ theme->set_constant("title_outline_size", "Window", 0);
theme->set_constant("title_height", "Window", 20 * scale);
theme->set_constant("resize_margin", "Window", 4 * scale);
theme->set_icon("close", "Window", make_icon(close_png));
- theme->set_icon("close_highlight", "Window", make_icon(close_hl_png));
+ theme->set_icon("close_pressed", "Window", make_icon(close_hl_png));
theme->set_constant("close_h_ofs", "Window", 18 * scale);
theme->set_constant("close_v_ofs", "Window", 18 * scale);
+ // AcceptDialog
+
+ theme->set_stylebox("panel", "AcceptDialog", make_stylebox(dialog_bg_png, 0, 0, 0, 0));
+
// File Dialog
theme->set_icon("parent_folder", "FileDialog", make_icon(icon_parent_folder_png));
+ theme->set_icon("back_folder", "FileDialog", make_icon(arrow_left_png));
+ theme->set_icon("forward_folder", "FileDialog", make_icon(arrow_right_png));
theme->set_icon("reload", "FileDialog", make_icon(icon_reload_png));
theme->set_icon("toggle_hidden", "FileDialog", make_icon(icon_visibility_png));
@@ -539,7 +588,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
Ref<StyleBoxTexture> selected = make_stylebox(selection_png, 6, 6, 6, 6);
for (int i = 0; i < 4; i++) {
- selected->set_expand_margin_size(Margin(i), 2 * scale);
+ selected->set_expand_margin_size(Side(i), 2 * scale);
}
theme->set_stylebox("panel", "PopupPanel", style_pp);
@@ -563,16 +612,23 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("radio_checked", "PopupMenu", make_icon(radio_checked_png));
theme->set_icon("radio_unchecked", "PopupMenu", make_icon(radio_unchecked_png));
theme->set_icon("submenu", "PopupMenu", make_icon(submenu_png));
+ theme->set_icon("submenu_mirrored", "PopupMenu", make_icon(submenu_mirrored_png));
theme->set_font("font", "PopupMenu", Ref<Font>());
+ theme->set_font_size("font_size", "PopupMenu", -1);
theme->set_color("font_color", "PopupMenu", control_font_color);
- theme->set_color("font_color_accel", "PopupMenu", Color(0.7, 0.7, 0.7, 0.8));
- theme->set_color("font_color_disabled", "PopupMenu", Color(0.4, 0.4, 0.4, 0.8));
- theme->set_color("font_color_hover", "PopupMenu", control_font_color);
+ theme->set_color("font_accelerator_color", "PopupMenu", Color(0.7, 0.7, 0.7, 0.8));
+ theme->set_color("font_disabled_color", "PopupMenu", Color(0.4, 0.4, 0.4, 0.8));
+ theme->set_color("font_hover_color", "PopupMenu", control_font_color);
+ theme->set_color("font_separator_color", "PopupMenu", control_font_color);
+ theme->set_color("font_outline_color", "PopupMenu", Color(1, 1, 1));
theme->set_constant("hseparation", "PopupMenu", 4 * scale);
theme->set_constant("vseparation", "PopupMenu", 4 * scale);
+ theme->set_constant("outline_size", "PopupMenu", 0);
+ theme->set_constant("item_start_padding", "PopupMenu", 2 * scale);
+ theme->set_constant("item_end_padding", "PopupMenu", 2 * scale);
// GraphNode
@@ -585,8 +641,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
Ref<StyleBoxTexture> graph_bpoint = make_stylebox(graph_node_breakpoint_png, 6, 24, 6, 5, 16, 24, 16, 6);
Ref<StyleBoxTexture> graph_position = make_stylebox(graph_node_position_png, 6, 24, 6, 5, 16, 24, 16, 6);
- //graphsb->set_expand_margin_size(MARGIN_LEFT,10);
- //graphsb->set_expand_margin_size(MARGIN_RIGHT,10);
+ //graphsb->set_expand_margin_size(SIDE_LEFT,10);
+ //graphsb->set_expand_margin_size(SIDE_RIGHT,10);
theme->set_stylebox("frame", "GraphNode", graphsb);
theme->set_stylebox("selectedframe", "GraphNode", graphsbselected);
theme->set_stylebox("defaultframe", "GraphNode", graphsbdefault);
@@ -628,32 +684,44 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("checked", "Tree", make_icon(checked_png));
theme->set_icon("unchecked", "Tree", make_icon(unchecked_png));
+ theme->set_icon("indeterminate", "Tree", make_icon(indeterminate_png));
theme->set_icon("updown", "Tree", make_icon(updown_png));
theme->set_icon("select_arrow", "Tree", make_icon(dropdown_png));
theme->set_icon("arrow", "Tree", make_icon(arrow_down_png));
theme->set_icon("arrow_collapsed", "Tree", make_icon(arrow_right_png));
+ theme->set_icon("arrow_collapsed_mirrored", "Tree", make_icon(arrow_left_png));
theme->set_font("title_button_font", "Tree", Ref<Font>());
theme->set_font("font", "Tree", Ref<Font>());
+ theme->set_font_size("font_size", "Tree", -1);
theme->set_color("title_button_color", "Tree", control_font_color);
- theme->set_color("font_color", "Tree", control_font_color_low);
- theme->set_color("font_color_selected", "Tree", control_font_color_pressed);
+ theme->set_color("font_color", "Tree", control_font_low_color);
+ theme->set_color("font_selected_color", "Tree", control_font_pressed_color);
+ theme->set_color("font_outline_color", "Tree", Color(1, 1, 1));
theme->set_color("guide_color", "Tree", Color(0, 0, 0, 0.1));
theme->set_color("drop_position_color", "Tree", Color(1, 0.3, 0.2));
theme->set_color("relationship_line_color", "Tree", Color(0.27, 0.27, 0.27));
- theme->set_color("custom_button_font_highlight", "Tree", control_font_color_hover);
+ theme->set_color("parent_hl_line_color", "Tree", Color(0.27, 0.27, 0.27));
+ theme->set_color("children_hl_line_color", "Tree", Color(0.27, 0.27, 0.27));
+ theme->set_color("custom_button_font_highlight", "Tree", control_font_hover_color);
theme->set_constant("hseparation", "Tree", 4 * scale);
theme->set_constant("vseparation", "Tree", 4 * scale);
theme->set_constant("item_margin", "Tree", 12 * scale);
theme->set_constant("button_margin", "Tree", 4 * scale);
theme->set_constant("draw_relationship_lines", "Tree", 0);
+ theme->set_constant("relationship_line_width", "Tree", 1);
+ theme->set_constant("parent_hl_line_width", "Tree", 1);
+ theme->set_constant("children_hl_line_width", "Tree", 1);
+ theme->set_constant("parent_hl_line_margin", "Tree", 0);
theme->set_constant("draw_guides", "Tree", 1);
theme->set_constant("scroll_border", "Tree", 4);
theme->set_constant("scroll_speed", "Tree", 12);
+ theme->set_constant("outline_size", "Tree", 0);
// ItemList
+
Ref<StyleBoxTexture> item_selected = make_stylebox(selection_png, 4, 4, 4, 4, 8, 2, 8, 2);
Ref<StyleBoxTexture> item_selected_oof = make_stylebox(selection_oof_png, 4, 4, 4, 4, 8, 2, 8, 2);
@@ -663,24 +731,30 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("vseparation", "ItemList", 2);
theme->set_constant("icon_margin", "ItemList", 4);
theme->set_constant("line_separation", "ItemList", 2 * scale);
+
theme->set_font("font", "ItemList", Ref<Font>());
- theme->set_color("font_color", "ItemList", control_font_color_lower);
- theme->set_color("font_color_selected", "ItemList", control_font_color_pressed);
+ theme->set_font_size("font_size", "ItemList", -1);
+
+ theme->set_color("font_color", "ItemList", control_font_lower_color);
+ theme->set_color("font_selected_color", "ItemList", control_font_pressed_color);
+ theme->set_color("font_outline_color", "ItemList", Color(1, 1, 1));
theme->set_color("guide_color", "ItemList", Color(0, 0, 0, 0.1));
theme->set_stylebox("selected", "ItemList", item_selected_oof);
theme->set_stylebox("selected_focus", "ItemList", item_selected);
theme->set_stylebox("cursor", "ItemList", focus);
theme->set_stylebox("cursor_unfocused", "ItemList", focus);
+ theme->set_constant("outline_size", "ItemList", 0);
+
// TabContainer
Ref<StyleBoxTexture> tc_sb = sb_expand(make_stylebox(tab_container_bg_png, 4, 4, 4, 4, 4, 4, 4, 4), 3, 3, 3, 3);
- tc_sb->set_expand_margin_size(MARGIN_TOP, 2 * scale);
- tc_sb->set_default_margin(MARGIN_TOP, 8 * scale);
+ tc_sb->set_expand_margin_size(SIDE_TOP, 2 * scale);
+ tc_sb->set_default_margin(SIDE_TOP, 8 * scale);
- theme->set_stylebox("tab_fg", "TabContainer", sb_expand(make_stylebox(tab_current_png, 4, 4, 4, 1, 16, 4, 16, 4), 2, 2, 2, 2));
- theme->set_stylebox("tab_bg", "TabContainer", sb_expand(make_stylebox(tab_behind_png, 5, 5, 5, 1, 16, 6, 16, 4), 3, 0, 3, 3));
+ theme->set_stylebox("tab_selected", "TabContainer", sb_expand(make_stylebox(tab_current_png, 4, 4, 4, 1, 16, 4, 16, 4), 2, 2, 2, 2));
+ theme->set_stylebox("tab_unselected", "TabContainer", sb_expand(make_stylebox(tab_behind_png, 5, 5, 5, 1, 16, 6, 16, 4), 3, 0, 3, 3));
theme->set_stylebox("tab_disabled", "TabContainer", sb_expand(make_stylebox(tab_disabled_png, 5, 5, 5, 1, 16, 6, 16, 4), 3, 0, 3, 3));
theme->set_stylebox("panel", "TabContainer", tc_sb);
@@ -692,20 +766,22 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("menu_highlight", "TabContainer", make_icon(tab_menu_hl_png));
theme->set_font("font", "TabContainer", Ref<Font>());
+ theme->set_font_size("font_size", "TabContainer", -1);
- theme->set_color("font_color_fg", "TabContainer", control_font_color_hover);
- theme->set_color("font_color_bg", "TabContainer", control_font_color_low);
- theme->set_color("font_color_disabled", "TabContainer", control_font_color_disabled);
+ theme->set_color("font_selected_color", "TabContainer", control_font_hover_color);
+ theme->set_color("font_unselected_color", "TabContainer", control_font_low_color);
+ theme->set_color("font_disabled_color", "TabContainer", control_font_disabled_color);
+ theme->set_color("font_outline_color", "TabContainer", Color(1, 1, 1));
theme->set_constant("side_margin", "TabContainer", 8 * scale);
theme->set_constant("icon_separation", "TabContainer", 4 * scale);
+ theme->set_constant("outline_size", "TabContainer", 0);
// Tabs
- theme->set_stylebox("tab_fg", "Tabs", sb_expand(make_stylebox(tab_current_png, 4, 3, 4, 1, 16, 3, 16, 2), 2, 2, 2, 2));
- theme->set_stylebox("tab_bg", "Tabs", sb_expand(make_stylebox(tab_behind_png, 5, 4, 5, 1, 16, 5, 16, 2), 3, 3, 3, 3));
+ theme->set_stylebox("tab_selected", "Tabs", sb_expand(make_stylebox(tab_current_png, 4, 3, 4, 1, 16, 3, 16, 2), 2, 2, 2, 2));
+ theme->set_stylebox("tab_unselected", "Tabs", sb_expand(make_stylebox(tab_behind_png, 5, 4, 5, 1, 16, 5, 16, 2), 3, 3, 3, 3));
theme->set_stylebox("tab_disabled", "Tabs", sb_expand(make_stylebox(tab_disabled_png, 5, 5, 5, 1, 16, 6, 16, 4), 3, 0, 3, 3));
- theme->set_stylebox("panel", "Tabs", tc_sb);
theme->set_stylebox("button_pressed", "Tabs", make_stylebox(button_pressed_png, 4, 4, 4, 4));
theme->set_stylebox("button", "Tabs", make_stylebox(button_normal_png, 4, 4, 4, 4));
@@ -716,12 +792,15 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("close", "Tabs", make_icon(tab_close_png));
theme->set_font("font", "Tabs", Ref<Font>());
+ theme->set_font_size("font_size", "Tabs", -1);
- theme->set_color("font_color_fg", "Tabs", control_font_color_hover);
- theme->set_color("font_color_bg", "Tabs", control_font_color_low);
- theme->set_color("font_color_disabled", "Tabs", control_font_color_disabled);
+ theme->set_color("font_selected_color", "Tabs", control_font_hover_color);
+ theme->set_color("font_unselected_color", "Tabs", control_font_low_color);
+ theme->set_color("font_disabled_color", "Tabs", control_font_disabled_color);
+ theme->set_color("font_outline_color", "Tabs", Color(1, 1, 1));
theme->set_constant("hseparation", "Tabs", 4 * scale);
+ theme->set_constant("outline_size", "Tabs", 0);
// Separators
@@ -760,27 +839,62 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("add_preset", "ColorPicker", make_icon(icon_add_png));
theme->set_icon("color_hue", "ColorPicker", make_icon(color_picker_hue_png));
theme->set_icon("color_sample", "ColorPicker", make_icon(color_picker_sample_png));
- theme->set_icon("preset_bg", "ColorPicker", make_icon(mini_checkerboard_png));
+ theme->set_icon("sample_bg", "ColorPicker", make_icon(mini_checkerboard_png));
theme->set_icon("overbright_indicator", "ColorPicker", make_icon(overbright_indicator_png));
+ theme->set_icon("bar_arrow", "ColorPicker", make_icon(bar_arrow_png));
+ theme->set_icon("picker_cursor", "ColorPicker", make_icon(picker_cursor_png));
+
+ // ColorPickerButton
theme->set_icon("bg", "ColorPickerButton", make_icon(mini_checkerboard_png));
+ theme->set_stylebox("normal", "ColorPickerButton", sb_button_normal);
+ theme->set_stylebox("pressed", "ColorPickerButton", sb_button_pressed);
+ theme->set_stylebox("hover", "ColorPickerButton", sb_button_hover);
+ theme->set_stylebox("disabled", "ColorPickerButton", sb_button_disabled);
+ theme->set_stylebox("focus", "ColorPickerButton", sb_button_focus);
+
+ theme->set_font("font", "ColorPickerButton", Ref<Font>());
+ theme->set_font_size("font_size", "ColorPickerButton", -1);
+
+ theme->set_color("font_color", "ColorPickerButton", Color(1, 1, 1, 1));
+ theme->set_color("font_pressed_color", "ColorPickerButton", Color(0.8, 0.8, 0.8, 1));
+ theme->set_color("font_hover_color", "ColorPickerButton", Color(1, 1, 1, 1));
+ theme->set_color("font_disabled_color", "ColorPickerButton", Color(0.9, 0.9, 0.9, 0.3));
+ theme->set_color("font_outline_color", "ColorPickerButton", Color(1, 1, 1));
+
+ theme->set_constant("hseparation", "ColorPickerButton", 2 * scale);
+ theme->set_constant("outline_size", "ColorPickerButton", 0);
+
+ // ColorPresetButton
+
+ Ref<StyleBoxFlat> preset_sb = make_flat_stylebox(Color(1, 1, 1), 2, 2, 2, 2);
+ preset_sb->set_corner_radius_all(2);
+ preset_sb->set_corner_detail(2);
+ preset_sb->set_anti_aliased(false);
+
+ theme->set_stylebox("preset_fg", "ColorPresetButton", preset_sb);
+ theme->set_icon("preset_bg", "ColorPresetButton", make_icon(mini_checkerboard_png));
+ theme->set_icon("overbright_indicator", "ColorPresetButton", make_icon(overbright_indicator_png));
// TooltipPanel
Ref<StyleBoxTexture> style_tt = make_stylebox(tooltip_bg_png, 4, 4, 4, 4);
for (int i = 0; i < 4; i++) {
- style_tt->set_expand_margin_size((Margin)i, 4 * scale);
+ style_tt->set_expand_margin_size((Side)i, 4 * scale);
}
theme->set_stylebox("panel", "TooltipPanel", style_tt);
theme->set_font("font", "TooltipLabel", Ref<Font>());
+ theme->set_font_size("font_size", "TooltipLabel", -1);
theme->set_color("font_color", "TooltipLabel", Color(0, 0, 0));
- theme->set_color("font_color_shadow", "TooltipLabel", Color(0, 0, 0, 0.1));
+ theme->set_color("font_shadow_color", "TooltipLabel", Color(0, 0, 0, 0.1));
+ theme->set_color("font_outline_color", "TooltipLabel", Color(1, 1, 1));
theme->set_constant("shadow_offset_x", "TooltipLabel", 1);
theme->set_constant("shadow_offset_y", "TooltipLabel", 1);
+ theme->set_constant("outline_size", "TooltipLabel", 0);
// RichTextLabel
@@ -793,20 +907,34 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_font("bold_italics_font", "RichTextLabel", Ref<Font>());
theme->set_font("mono_font", "RichTextLabel", Ref<Font>());
+ theme->set_font_size("normal_font_size", "RichTextLabel", -1);
+ theme->set_font_size("bold_font_size", "RichTextLabel", -1);
+ theme->set_font_size("italics_font_size", "RichTextLabel", -1);
+ theme->set_font_size("bold_italics_font_size", "RichTextLabel", -1);
+ theme->set_font_size("mono_font_size", "RichTextLabel", -1);
+
theme->set_color("default_color", "RichTextLabel", Color(1, 1, 1));
- theme->set_color("font_color_selected", "RichTextLabel", font_color_selection);
+ theme->set_color("font_selected_color", "RichTextLabel", Color(0, 0, 0));
theme->set_color("selection_color", "RichTextLabel", Color(0.1, 0.1, 1, 0.8));
- theme->set_color("font_color_shadow", "RichTextLabel", Color(0, 0, 0, 0));
+ theme->set_color("font_shadow_color", "RichTextLabel", Color(0, 0, 0, 0));
+
+ theme->set_color("font_outline_color", "RichTextLabel", Color(1, 1, 1));
theme->set_constant("shadow_offset_x", "RichTextLabel", 1 * scale);
theme->set_constant("shadow_offset_y", "RichTextLabel", 1 * scale);
theme->set_constant("shadow_as_outline", "RichTextLabel", 0 * scale);
- theme->set_constant("line_separation", "RichTextLabel", 1 * scale);
+ theme->set_constant("line_separation", "RichTextLabel", 0 * scale);
theme->set_constant("table_hseparation", "RichTextLabel", 3 * scale);
theme->set_constant("table_vseparation", "RichTextLabel", 3 * scale);
+ theme->set_constant("outline_size", "RichTextLabel", 0);
+
+ theme->set_color("table_odd_row_bg", "RichTextLabel", Color(0, 0, 0, 0));
+ theme->set_color("table_even_row_bg", "RichTextLabel", Color(0, 0, 0, 0));
+ theme->set_color("table_border", "RichTextLabel", Color(0, 0, 0, 0));
+
// Containers
theme->set_stylebox("bg", "VSplitContainer", make_stylebox(vsplit_bg_png, 1, 1, 1, 1));
@@ -835,6 +963,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("reset", "GraphEdit", make_icon(icon_zoom_reset_png));
theme->set_icon("more", "GraphEdit", make_icon(icon_zoom_more_png));
theme->set_icon("snap", "GraphEdit", make_icon(icon_snap_grid_png));
+ theme->set_icon("minimap", "GraphEdit", make_icon(icon_grid_minimap_png));
+ theme->set_icon("layout", "GraphEdit", make_icon(icon_grid_layout_png));
theme->set_stylebox("bg", "GraphEdit", make_stylebox(tree_bg_png, 4, 4, 4, 5));
theme->set_color("grid_minor", "GraphEdit", Color(1, 1, 1, 0.05));
theme->set_color("grid_major", "GraphEdit", Color(1, 1, 1, 0.2));
@@ -845,9 +975,23 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("bezier_len_neg", "GraphEdit", 160 * scale);
// Visual Node Ports
- theme->set_constant("port_grab_distance_horizontal", "GraphEdit", 48 * scale);
+
+ theme->set_constant("port_grab_distance_horizontal", "GraphEdit", 24 * scale);
theme->set_constant("port_grab_distance_vertical", "GraphEdit", 6 * scale);
+ theme->set_stylebox("bg", "GraphEditMinimap", make_flat_stylebox(Color(0.24, 0.24, 0.24), 0, 0, 0, 0));
+ Ref<StyleBoxFlat> style_minimap_camera = make_flat_stylebox(Color(0.65, 0.65, 0.65, 0.2), 0, 0, 0, 0);
+ style_minimap_camera->set_border_color(Color(0.65, 0.65, 0.65, 0.45));
+ style_minimap_camera->set_border_width_all(1);
+ theme->set_stylebox("camera", "GraphEditMinimap", style_minimap_camera);
+ Ref<StyleBoxFlat> style_minimap_node = make_flat_stylebox(Color(1, 1, 1), 0, 0, 0, 0);
+ style_minimap_node->set_corner_radius_all(2);
+ theme->set_stylebox("node", "GraphEditMinimap", style_minimap_node);
+
+ Ref<Texture2D> resizer_icon = make_icon(window_resizer_png);
+ theme->set_icon("resizer", "GraphEditMinimap", flip_icon(resizer_icon, true, true));
+ theme->set_color("resizer_color", "GraphEditMinimap", Color(1, 1, 1, 0.85));
+
// Theme
default_icon = make_icon(error_icon_png);
@@ -858,18 +1002,31 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
void make_default_theme(bool p_hidpi, Ref<Font> p_font) {
Ref<Theme> t;
- t.instance();
+ t.instantiate();
Ref<StyleBox> default_style;
Ref<Texture2D> default_icon;
Ref<Font> default_font;
+
if (p_font.is_valid()) {
+ // Use the custom font defined in the Project Settings.
default_font = p_font;
- } else if (p_hidpi) {
- default_font = make_font(_hidpi_font_height, _hidpi_font_ascent, _hidpi_font_charcount, &_hidpi_font_charrects[0][0], _hidpi_font_kerning_pair_count, &_hidpi_font_kerning_pairs[0][0], _hidpi_font_img_width, _hidpi_font_img_height, _hidpi_font_img_data);
} else {
- default_font = make_font(_lodpi_font_height, _lodpi_font_ascent, _lodpi_font_charcount, &_lodpi_font_charrects[0][0], _lodpi_font_kerning_pair_count, &_lodpi_font_kerning_pairs[0][0], _lodpi_font_img_width, _lodpi_font_img_height, _lodpi_font_img_data);
+ // Use the default DynamicFont (separate from the editor font).
+ // The default DynamicFont is chosen to have a small file size since it's
+ // embedded in both editor and export template binaries.
+ Ref<Font> dynamic_font;
+ dynamic_font.instantiate();
+
+ Ref<FontData> dynamic_font_data;
+ dynamic_font_data.instantiate();
+ dynamic_font_data->set_data_ptr(_font_OpenSans_SemiBold, _font_OpenSans_SemiBold_size);
+ dynamic_font->add_data(dynamic_font_data);
+ dynamic_font->set_base_size(default_font_size);
+
+ default_font = dynamic_font;
}
+
Ref<Font> large_font = default_font;
fill_default_theme(t, default_font, large_font, default_icon, default_style, p_hidpi ? 2.0 : 1.0);
@@ -877,6 +1034,7 @@ void make_default_theme(bool p_hidpi, Ref<Font> p_font) {
Theme::set_default_icon(default_icon);
Theme::set_default_style(default_style);
Theme::set_default_font(default_font);
+ Theme::set_default_font_size(default_font_size);
}
void clear_default_theme() {
diff --git a/scene/resources/default_theme/default_theme.h b/scene/resources/default_theme/default_theme.h
index 46f89a9b50..4cd781e814 100644
--- a/scene/resources/default_theme/default_theme.h
+++ b/scene/resources/default_theme/default_theme.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,6 +33,8 @@
#include "scene/resources/theme.h"
+const int default_font_size = 16;
+
void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const Ref<Font> &large_font, Ref<Texture2D> &default_icon, Ref<StyleBox> &default_style, float p_scale);
void make_default_theme(bool p_hidpi, Ref<Font> p_font);
void clear_default_theme();
diff --git a/scene/resources/default_theme/default_theme_builders.py b/scene/resources/default_theme/default_theme_builders.py
new file mode 100644
index 0000000000..0455d6d246
--- /dev/null
+++ b/scene/resources/default_theme/default_theme_builders.py
@@ -0,0 +1,40 @@
+"""Functions used to generate source files during build time
+
+All such functions are invoked in a subprocess on Windows to prevent build flakiness.
+
+"""
+import os
+import os.path
+from platform_methods import subprocess_main
+
+
+def make_fonts_header(target, source, env):
+ dst = target[0]
+
+ g = open(dst, "w", encoding="utf-8")
+
+ g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
+ g.write("#ifndef _DEFAULT_FONTS_H\n")
+ g.write("#define _DEFAULT_FONTS_H\n")
+
+ # Saving uncompressed, since FreeType will reference from memory pointer.
+ for i in range(len(source)):
+ with open(source[i], "rb") as f:
+ buf = f.read()
+
+ name = os.path.splitext(os.path.basename(source[i]))[0]
+
+ g.write("static const int _font_" + name + "_size = " + str(len(buf)) + ";\n")
+ g.write("static const unsigned char _font_" + name + "[] = {\n")
+ for j in range(len(buf)):
+ g.write("\t" + str(buf[j]) + ",\n")
+
+ g.write("};\n")
+
+ g.write("#endif")
+
+ g.close()
+
+
+if __name__ == "__main__":
+ subprocess_main(globals())
diff --git a/scene/resources/default_theme/dialog_bg.png b/scene/resources/default_theme/dialog_bg.png
new file mode 100644
index 0000000000..a23a10b48a
--- /dev/null
+++ b/scene/resources/default_theme/dialog_bg.png
Binary files differ
diff --git a/scene/resources/default_theme/ellipsis.png b/scene/resources/default_theme/ellipsis.png
new file mode 100644
index 0000000000..c949e2c95b
--- /dev/null
+++ b/scene/resources/default_theme/ellipsis.png
Binary files differ
diff --git a/scene/resources/default_theme/font_hidpi.inc b/scene/resources/default_theme/font_hidpi.inc
deleted file mode 100644
index 4860149e6b..0000000000
--- a/scene/resources/default_theme/font_hidpi.inc
+++ /dev/null
@@ -1,25463 +0,0 @@
-/* clang-format off */
-static const int _hidpi_font_height=25;
-static const int _hidpi_font_ascent=19;
-static const int _hidpi_font_charcount=191;
-static const int _hidpi_font_charrects[191][8]={
-/* charidx , ofs_x, ofs_y, size_x, size_y, valign, halign, advance */
-{192,63,23,16,23,-4,-1,15},
-{224,184,182,10,19,0,1,13},
-{64,98,2,18,19,2,1,21},
-{96,159,234,5,4,0,5,14},
-{160,0,0,0,0,19,0,6},
-{32,0,0,0,0,19,0,6},
-{33,2,249,3,17,2,2,6},
-{193,83,25,16,23,-4,-1,15},
-{225,92,160,10,19,0,1,13},
-{65,163,23,16,17,2,-1,15},
-{161,246,236,3,17,6,2,6},
-{97,142,190,10,13,6,1,13},
-{162,67,214,9,17,2,2,13},
-{98,242,136,11,18,1,2,14},
-{194,103,25,16,23,-4,-1,15},
-{226,106,160,10,19,0,1,13},
-{66,122,146,11,17,2,2,15},
-{34,226,203,8,6,2,1,10},
-{35,189,44,14,17,2,1,16},
-{163,62,166,11,17,2,1,13},
-{195,123,23,16,23,-4,-1,15},
-{227,134,167,10,19,0,1,13},
-{67,209,65,12,17,2,1,14},
-{99,225,186,9,13,6,1,11},
-{228,170,182,10,18,1,1,13},
-{100,227,143,11,18,1,1,14},
-{196,23,23,16,22,-3,-1,15},
-{36,2,179,10,20,1,2,13},
-{68,193,65,12,17,2,2,16},
-{164,176,65,13,12,5,0,13},
-{37,120,2,18,17,2,1,20},
-{69,41,222,9,17,2,2,13},
-{165,98,99,12,17,2,1,13},
-{197,43,23,16,21,-2,-1,15},
-{229,16,179,10,20,-1,1,13},
-{101,189,109,11,13,6,1,13},
-{38,183,23,16,17,2,1,17},
-{70,54,222,9,17,2,2,12},
-{198,75,2,19,17,2,-1,20},
-{102,202,212,8,18,1,1,8},
-{166,44,268,2,24,1,6,13},
-{230,142,2,18,13,6,1,20},
-{71,2,71,14,17,2,1,17},
-{167,176,160,10,18,1,0,12},
-{199,146,94,12,23,2,1,14},
-{103,159,67,13,19,6,0,13},
-{231,212,189,9,19,6,1,11},
-{39,30,271,3,6,2,1,5},
-{72,130,99,12,17,2,2,17},
-{104,162,160,10,18,1,2,14},
-{200,238,186,9,23,-4,2,13},
-{40,46,243,7,21,2,1,7},
-{232,2,156,11,19,0,1,13},
-{168,68,235,7,3,1,4,14},
-{73,106,209,8,17,2,0,9},
-{169,230,2,17,17,2,1,20},
-{105,222,232,4,18,1,1,6},
-{201,28,222,9,23,-4,2,13},
-{41,130,238,6,21,2,0,7},
-{233,197,126,11,19,0,1,13},
-{202,93,209,9,23,-4,2,13},
-{74,13,225,7,22,2,-2,7},
-{234,167,137,11,19,0,1,13},
-{42,82,120,12,11,1,0,13},
-{170,100,236,6,8,2,1,8},
-{106,110,237,6,24,1,-1,6},
-{171,144,121,11,10,8,0,12},
-{43,2,119,12,12,5,1,13},
-{203,80,209,9,22,-3,2,13},
-{107,32,177,11,18,1,2,12},
-{235,77,166,11,18,1,1,13},
-{75,74,77,13,17,2,2,14},
-{44,230,213,4,6,16,1,6},
-{172,204,109,11,7,10,1,13},
-{236,204,234,5,19,0,0,6},
-{204,130,211,8,23,-4,0,9},
-{108,23,271,3,18,1,2,6},
-{76,86,188,10,17,2,2,12},
-{173,140,238,6,2,12,1,8},
-{45,120,237,6,2,12,1,8},
-{109,208,2,18,13,6,2,22},
-{205,178,205,8,23,-4,0,9},
-{237,213,234,5,19,0,2,6},
-{77,143,19,16,17,2,2,21},
-{46,9,251,3,3,16,2,6},
-{110,100,183,10,13,6,2,14},
-{206,154,207,8,23,-4,0,9},
-{238,238,213,8,19,0,-1,6},
-{174,2,23,17,17,2,1,20},
-{78,91,78,13,17,2,2,18},
-{175,18,125,12,2,-1,0,12},
-{111,162,90,12,13,6,1,14},
-{207,118,211,8,22,-3,0,9},
-{239,24,249,7,18,1,0,6},
-{79,203,23,15,17,2,1,18},
-{47,120,167,10,17,2,-1,9},
-{176,166,207,8,8,2,1,10},
-{112,212,143,11,19,6,2,14},
-{240,18,103,12,18,1,1,14},
-{208,171,44,14,17,2,0,16},
-{80,128,190,10,17,2,2,14},
-{48,234,115,11,17,2,1,13},
-{177,66,127,12,14,5,1,13},
-{113,182,126,11,19,6,1,14},
-{241,30,199,10,19,0,2,14},
-{81,97,52,15,22,2,1,18},
-{209,142,67,13,23,-4,2,18},
-{49,57,243,7,17,2,2,13},
-{178,190,206,8,10,2,0,8},
-{114,142,207,8,13,6,2,10},
-{242,242,87,12,19,0,1,14},
-{210,59,50,15,23,-4,1,18},
-{82,82,99,12,17,2,2,14},
-{50,2,135,11,17,2,1,13},
-{179,35,249,7,10,2,0,8},
-{115,72,188,10,13,6,0,11},
-{243,226,92,12,19,0,1,14},
-{211,40,49,15,23,-4,1,18},
-{83,129,125,11,17,2,0,13},
-{51,17,135,11,17,2,1,13},
-{180,168,219,5,4,0,5,14},
-{116,214,212,8,16,3,0,8},
-{244,194,86,12,19,0,1,14},
-{212,21,49,15,23,-4,1,18},
-{84,66,106,12,17,2,0,13},
-{52,125,78,13,17,2,0,13},
-{53,32,156,11,17,2,1,13},
-{85,50,106,12,17,2,2,17},
-{181,204,166,10,19,6,2,14},
-{117,198,189,10,13,6,2,14},
-{245,178,86,12,19,0,1,14},
-{213,2,44,15,23,-4,1,18},
-{54,47,156,11,17,2,1,13},
-{86,222,23,15,17,2,-1,14},
-{246,241,65,12,18,1,1,14},
-{214,116,52,15,22,-3,1,18},
-{182,34,131,12,21,1,1,16},
-{118,135,50,14,13,6,-1,12},
-{55,62,145,11,17,2,1,13},
-{87,2,2,22,17,2,-1,21},
-{119,28,2,20,13,6,-1,18},
-{215,114,125,11,11,5,1,13},
-{247,210,92,12,10,6,0,13},
-{183,16,251,3,3,9,2,6},
-{56,77,145,11,17,2,1,13},
-{88,225,44,14,17,2,-1,13},
-{216,78,52,15,19,1,1,18},
-{248,98,120,12,15,5,1,14},
-{120,50,127,12,13,6,0,12},
-{184,150,234,5,6,19,0,5},
-{89,207,44,14,17,2,-1,13},
-{121,153,44,14,19,6,-1,12},
-{217,225,65,12,23,-4,2,17},
-{249,44,199,10,19,0,2,14},
-{57,92,139,11,17,2,1,13},
-{185,177,232,5,10,2,0,8},
-{218,2,92,12,23,-4,2,17},
-{250,156,184,10,19,0,2,14},
-{90,232,165,10,17,2,1,13},
-{122,148,167,10,13,6,1,11},
-{58,37,263,3,13,6,2,6},
-{186,90,236,6,8,2,1,8},
-{219,34,104,12,23,-4,2,17},
-{123,2,224,7,21,2,1,9},
-{91,186,232,5,21,2,2,7},
-{251,58,191,10,19,0,2,14},
-{59,238,236,4,16,6,1,6},
-{187,47,177,11,10,8,1,12},
-{188,164,2,18,17,2,0,18},
-{252,190,149,10,18,1,2,14},
-{124,50,268,2,24,1,6,13},
-{220,114,99,12,22,-3,2,17},
-{92,137,146,11,17,2,-1,9},
-{60,159,121,11,12,5,1,13},
-{189,186,2,18,17,2,0,18},
-{253,56,77,14,25,0,-1,12},
-{221,20,76,14,23,-4,-1,13},
-{125,79,235,7,21,2,1,9},
-{93,195,234,5,21,2,0,7},
-{61,152,137,11,6,8,1,13},
-{190,52,2,19,17,2,0,18},
-{222,114,188,10,17,2,2,14},
-{254,219,115,11,24,1,2,14},
-{62,174,109,11,12,5,1,13},
-{94,108,78,13,11,2,0,13},
-{126,107,140,11,5,8,1,13},
-{223,17,156,11,18,1,2,14},
-{191,15,203,9,18,6,0,10},
-{255,38,76,14,24,1,-1,12},
-{63,2,203,9,17,2,0,10},
-{95,218,166,10,2,21,0,10},
-};
-static const int _hidpi_font_kerning_pair_count=0;
-static const int _hidpi_font_kerning_pairs[1][3]={
-{0,0,0}
-};
-static const int _hidpi_font_img_width=256;
-static const int _hidpi_font_img_height=512;
-static const int _hidpi_font_img_data_size=25255;
-static const unsigned char _hidpi_font_img_data[25255]={
-137,
-80,
-78,
-71,
-13,
-10,
-26,
-10,
-0,
-0,
-0,
-13,
-73,
-72,
-68,
-82,
-0,
-0,
-1,
-0,
-0,
-0,
-2,
-0,
-8,
-6,
-0,
-0,
-0,
-109,
-154,
-178,
-251,
-0,
-0,
-32,
-0,
-73,
-68,
-65,
-84,
-120,
-156,
-236,
-157,
-119,
-184,
-38,
-53,
-245,
-199,
-51,
-84,
-165,
-55,
-1,
-21,
-16,
-41,
-34,
-42,
-130,
-98,
-23,
-1,
-21,
-20,
-84,
-108,
-40,
-138,
-138,
-10,
-34,
-40,
-86,
-148,
-159,
-216,
-11,
-34,
-138,
-130,
-162,
-40,
-32,
-160,
-128,
-160,
-2,
-162,
-32,
-162,
-34,
-34,
-29,
-145,
-142,
-235,
-210,
-219,
-210,
-151,
-14,
-219,
-251,
-231,
-247,
-199,
-57,
-97,
-114,
-231,
-205,
-204,
-100,
-102,
-50,
-239,
-189,
-119,
-111,
-190,
-207,
-179,
-207,
-221,
-55,
-57,
-57,
-147,
-153,
-73,
-206,
-36,
-39,
-167,
-24,
-147,
-144,
-144,
-144,
-144,
-144,
-144,
-48,
-129,
-1,
-172,
-75,
-142,
-45,
-74,
-104,
-158,
-231,
-208,
-188,
-176,
-132,
-230,
-85,
-14,
-205,
-58,
-158,
-250,
-245,
-156,
-250,
-103,
-121,
-234,
-95,
-237,
-212,
-239,
-225,
-169,
-95,
-25,
-88,
-160,
-245,
-235,
-122,
-234,
-55,
-5,
-78,
-0,
-238,
-5,
-230,
-2,
-83,
-128,
-95,
-2,
-107,
-149,
-244,
-119,
-105,
-224,
-22,
-224,
-65,
-96,
-37,
-31,
-77,
-8,
-128,
-143,
-211,
-0,
-129,
-60,
-215,
-1,
-190,
-8,
-156,
-15,
-220,
-13,
-204,
-1,
-30,
-7,
-38,
-1,
-63,
-3,
-182,
-12,
-228,
-243,
-33,
-224,
-102,
-109,
-255,
-63,
-224,
-109,
-21,
-180,
-235,
-3,
-179,
-244,
-249,
-173,
-88,
-195,
-119,
-109,
-224,
-43,
-192,
-101,
-192,
-19,
-250,
-188,
-239,
-5,
-126,
-3,
-108,
-160,
-52,
-27,
-0,
-191,
-7,
-30,
-86,
-190,
-215,
-0,
-159,
-0,
-150,
-168,
-224,
-187,
-42,
-112,
-172,
-62,
-170,
-211,
-42,
-232,
-26,
-189,
-187,
-62,
-248,
-42,
-175,
-235,
-244,
-255,
-239,
-211,
-103,
-49,
-67,
-223,
-211,
-133,
-246,
-89,
-3,
-75,
-0,
-159,
-5,
-174,
-211,
-231,
-240,
-36,
-240,
-79,
-224,
-117,
-117,
-253,
-214,
-246,
-43,
-105,
-251,
-127,
-1,
-143,
-0,
-243,
-245,
-239,
-185,
-192,
-103,
-234,
-222,
-85,
-16,
-128,
-27,
-244,
-134,
-246,
-45,
-169,
-255,
-164,
-51,
-134,
-247,
-41,
-161,
-249,
-138,
-214,
-79,
-174,
-184,
-206,
-205,
-74,
-243,
-126,
-79,
-221,
-33,
-206,
-53,
-78,
-247,
-212,
-239,
-164,
-117,
-55,
-150,
-240,
-254,
-43,
-48,
-93,
-7,
-225,
-87,
-129,
-95,
-1,
-139,
-128,
-127,
-149,
-208,
-127,
-65,
-249,
-125,
-188,
-172,
-191,
-33,
-0,
-174,
-5,
-22,
-2,
-151,
-56,
-253,
-191,
-86,
-127,
-15,
-252,
-171,
-225,
-245,
-52,
-224,
-251,
-192,
-108,
-229,
-115,
-43,
-112,
-6,
-112,
-52,
-112,
-18,
-112,
-149,
-94,
-11,
-224,
-52,
-96,
-149,
-10,
-94,
-175,
-215,
-251,
-183,
-130,
-240,
-126,
-29,
-60,
-94,
-225,
-1,
-156,
-165,
-124,
-119,
-14,
-184,
-231,
-43,
-149,
-118,
-1,
-112,
-35,
-112,
-5,
-50,
-209,
-1,
-30,
-211,
-107,
-63,
-166,
-191,
-239,
-214,
-241,
-101,
-251,
-125,
-98,
-9,
-207,
-247,
-2,
-83,
-129,
-187,
-128,
-153,
-84,
-79,
-212,
-224,
-119,
-215,
-35,
-95,
-144,
-9,
-253,
-85,
-253,
-255,
-125,
-250,
-126,
-158,
-212,
-223,
-139,
-128,
-157,
-245,
-61,
-161,
-215,
-191,
-70,
-251,
-128,
-190,
-139,
-87,
-215,
-92,
-99,
-39,
-224,
-81,
-100,
-194,
-31,
-13,
-236,
-14,
-188,
-93,
-255,
-30,
-173,
-229,
-143,
-80,
-33,
-216,
-131,
-0,
-28,
-166,
-157,
-250,
-75,
-73,
-253,
-105,
-136,
-148,
-159,
-11,
-156,
-82,
-66,
-115,
-142,
-242,
-56,
-164,
-226,
-58,
-63,
-87,
-154,
-35,
-60,
-117,
-183,
-235,
-96,
-189,
-18,
-145,
-164,
-79,
-43,
-212,
-31,
-170,
-109,
-127,
-90,
-194,
-123,
-85,
-96,
-181,
-66,
-217,
-121,
-192,
-116,
-15,
-237,
-26,
-136,
-164,
-254,
-47,
-21,
-95,
-164,
-58,
-0,
-175,
-209,
-62,
-253,
-85,
-127,
-91,
-120,
-87,
-82,
-53,
-188,
-214,
-0,
-254,
-173,
-237,
-79,
-1,
-94,
-80,
-66,
-183,
-46,
-240,
-107,
-165,
-187,
-17,
-88,
-181,
-132,
-238,
-79,
-74,
-179,
-137,
-254,
-126,
-169,
-254,
-62,
-201,
-67,
-251,
-110,
-247,
-62,
-2,
-250,
-250,
-38,
-224,
-115,
-238,
-243,
-70,
-190,
-158,
-71,
-58,
-131,
-127,
-33,
-176,
-139,
-83,
-255,
-34,
-100,
-34,
-2,
-188,
-215,
-195,
-243,
-64,
-224,
-183,
-192,
-234,
-84,
-76,
-212,
-166,
-239,
-174,
-71,
-190,
-22,
-243,
-129,
-15,
-58,
-229,
-203,
-33,
-31,
-35,
-128,
-121,
-200,
-234,
-235,
-237,
-133,
-235,
-92,
-163,
-245,
-103,
-84,
-240,
-255,
-178,
-62,
-195,
-31,
-0,
-43,
-148,
-208,
-172,
-0,
-28,
-172,
-116,
-95,
-174,
-235,
-115,
-213,
-205,
-236,
-168,
-29,
-122,
-18,
-88,
-178,
-80,
-183,
-4,
-34,
-133,
-174,
-212,
-127,
-15,
-120,
-218,
-47,
-67,
-46,
-217,
-182,
-171,
-184,
-206,
-59,
-148,
-102,
-114,
-161,
-124,
-11,
-45,
-63,
-94,
-111,
-24,
-224,
-45,
-5,
-154,
-171,
-181,
-188,
-86,
-218,
-33,
-95,
-210,
-215,
-35,
-203,
-211,
-223,
-122,
-234,
-127,
-161,
-188,
-222,
-80,
-199,
-171,
-230,
-58,
-191,
-85,
-62,
-239,
-208,
-223,
-22,
-141,
-4,
-0,
-176,
-20,
-178,
-66,
-152,
-15,
-236,
-22,
-216,
-102,
-95,
-189,
-214,
-159,
-75,
-234,
-239,
-5,
-110,
-45,
-148,
-221,
-9,
-220,
-89,
-40,
-91,
-1,
-184,
-7,
-249,
-154,
-61,
-183,
-73,
-191,
-61,
-215,
-92,
-203,
-121,
-6,
-199,
-121,
-234,
-247,
-215,
-186,
-127,
-212,
-240,
-129,
-242,
-137,
-218,
-250,
-221,
-197,
-228,
-235,
-220,
-231,
-209,
-158,
-186,
-45,
-156,
-250,
-67,
-61,
-245,
-239,
-209,
-186,
-7,
-75,
-120,
-239,
-132,
-76,
-234,
-93,
-245,
-247,
-18,
-192,
-222,
-200,
-252,
-155,
-137,
-204,
-199,
-75,
-128,
-79,
-107,
-253,
-110,
-74,
-223,
-110,
-37,
-128,
-72,
-173,
-57,
-218,
-169,
-87,
-20,
-234,
-236,
-151,
-227,
-112,
-231,
-33,
-109,
-82,
-160,
-217,
-74,
-203,
-103,
-0,
-203,
-86,
-92,
-103,
-37,
-29,
-228,
-139,
-128,
-213,
-157,
-242,
-3,
-180,
-253,
-135,
-145,
-175,
-11,
-192,
-145,
-78,
-253,
-202,
-122,
-131,
-243,
-40,
-145,
-134,
-14,
-173,
-139,
-127,
-22,
-251,
-3,
-188,
-0,
-89,
-186,
-122,
-39,
-78,
-40,
-128,
-103,
-32,
-43,
-162,
-251,
-80,
-161,
-233,
-92,
-183,
-169,
-0,
-248,
-134,
-182,
-251,
-124,
-195,
-118,
-118,
-121,
-249,
-90,
-79,
-221,
-60,
-224,
-226,
-66,
-217,
-165,
-192,
-220,
-66,
-217,
-143,
-149,
-199,
-87,
-155,
-92,
-187,
-162,
-79,
-115,
-149,
-223,
-174,
-158,
-186,
-55,
-106,
-221,
-35,
-53,
-60,
-192,
-51,
-81,
-187,
-190,
-187,
-152,
-124,
-157,
-119,
-61,
-48,
-233,
-128,
-167,
-59,
-245,
-59,
-120,
-234,
-55,
-211,
-186,
-5,
-37,
-109,
-31,
-6,
-14,
-114,
-202,
-78,
-82,
-250,
-169,
-250,
-255,
-211,
-149,
-230,
-215,
-14,
-205,
-193,
-200,
-118,
-160,
-157,
-78,
-0,
-81,
-42,
-0,
-236,
-95,
-40,
-255,
-63,
-251,
-66,
-129,
-15,
-234,
-255,
-247,
-46,
-208,
-216,
-1,
-124,
-86,
-192,
-117,
-46,
-85,
-218,
-119,
-56,
-101,
-215,
-35,
-66,
-97,
-45,
-125,
-0,
-179,
-129,
-123,
-157,
-122,
-187,
-255,
-191,
-48,
-128,
-255,
-14,
-200,
-222,
-235,
-8,
-109,
-115,
-80,
-161,
-254,
-108,
-100,
-114,
-108,
-92,
-199,
-171,
-230,
-58,
-86,
-231,
-241,
-93,
-167,
-204,
-34,
-88,
-0,
-32,
-66,
-241,
-9,
-100,
-133,
-147,
-57,
-229,
-203,
-34,
-203,
-192,
-201,
-136,
-112,
-158,
-141,
-124,
-193,
-207,
-36,
-95,
-214,
-219,
-47,
-141,
-111,
-75,
-53,
-157,
-130,
-206,
-1,
-217,
-98,
-76,
-115,
-126,
-111,
-142,
-12,
-252,
-27,
-128,
-165,
-27,
-244,
-121,
-73,
-68,
-241,
-117,
-18,
-162,
-220,
-186,
-31,
-152,
-166,
-207,
-53,
-4,
-11,
-107,
-248,
-131,
-127,
-162,
-118,
-122,
-119,
-49,
-249,
-58,
-247,
-178,
-89,
-77,
-253,
-139,
-61,
-117,
-27,
-217,
-74,
-79,
-221,
-199,
-16,
-37,
-228,
-242,
-250,
-251,
-189,
-74,
-122,
-5,
-35,
-63,
-154,
-203,
-0,
-43,
-59,
-191,
-87,
-64,
-4,
-192,
-103,
-66,
-239,
-161,
-120,
-97,
-59,
-209,
-255,
-81,
-40,
-63,
-91,
-203,
-159,
-173,
-255,
-0,
-126,
-87,
-160,
-57,
-79,
-203,
-63,
-29,
-112,
-157,
-111,
-43,
-237,
-161,
-250,
-219,
-158,
-48,
-92,
-234,
-208,
-252,
-93,
-203,
-94,
-170,
-191,
-237,
-254,
-255,
-107,
-13,
-239,
-233,
-66,
-224,
-9,
-231,
-183,
-221,
-234,
-252,
-184,
-9,
-31,
-15,
-223,
-37,
-16,
-125,
-197,
-66,
-224,
-57,
-78,
-185,
-69,
-19,
-1,
-176,
-151,
-182,
-249,
-136,
-83,
-182,
-52,
-114,
-2,
-0,
-162,
-80,
-60,
-4,
-89,
-129,
-221,
-173,
-101,
-175,
-117,
-104,
-239,
-3,
-174,
-247,
-240,
-189,
-137,
-193,
-45,
-192,
-20,
-84,
-137,
-10,
-100,
-228,
-58,
-135,
-109,
-27,
-244,
-119,
-13,
-70,
-42,
-1,
-255,
-131,
-40,
-94,
-127,
-10,
-28,
-228,
-60,
-131,
-235,
-40,
-81,
-132,
-82,
-175,
-12,
-133,
-194,
-68,
-141,
-241,
-238,
-98,
-242,
-117,
-238,
-115,
-163,
-166,
-245,
-84,
-11,
-128,
-243,
-25,
-185,
-250,
-253,
-135,
-146,
-110,
-19,
-208,
-167,
-163,
-129,
-115,
-155,
-220,
-135,
-219,
-120,
-115,
-189,
-208,
-12,
-244,
-107,
-64,
-190,
-183,
-191,
-201,
-161,
-187,
-13,
-184,
-207,
-249,
-189,
-44,
-185,
-214,
-218,
-251,
-48,
-10,
-215,
-121,
-173,
-210,
-94,
-166,
-191,
-191,
-172,
-191,
-247,
-115,
-104,
-62,
-165,
-101,
-223,
-208,
-223,
-118,
-192,
-189,
-162,
-130,
-239,
-42,
-133,
-223,
-75,
-34,
-95,
-182,
-71,
-244,
-247,
-82,
-246,
-183,
-75,
-11,
-124,
-196,
-55,
-48,
-106,
-238,
-225,
-237,
-218,
-230,
-239,
-133,
-242,
-90,
-120,
-120,
-157,
-170,
-85,
-207,
-112,
-202,
-172,
-48,
-62,
-13,
-88,
-202,
-41,
-63,
-89,
-203,
-95,
-228,
-148,
-93,
-12,
-60,
-238,
-225,
-251,
-59,
-165,
-45,
-42,
-1,
-79,
-214,
-223,
-123,
-235,
-239,
-19,
-244,
-119,
-208,
-145,
-33,
-162,
-167,
-1,
-81,
-150,
-61,
-167,
-80,
-151,
-57,
-183,
-218,
-90,
-191,
-82,
-124,
-31,
-177,
-222,
-93,
-13,
-223,
-117,
-17,
-1,
-118,
-135,
-62,
-131,
-59,
-145,
-15,
-79,
-153,
-2,
-206,
-34,
-182,
-0,
-120,
-20,
-248,
-168,
-243,
-219,
-30,
-253,
-45,
-89,
-164,
-245,
-180,
-221,
-157,
-154,
-237,
-85,
-85,
-227,
-12,
-120,
-64,
-251,
-181,
-149,
-150,
-109,
-173,
-191,
-143,
-112,
-232,
-236,
-121,
-234,
-70,
-250,
-123,
-91,
-253,
-125,
-91,
-224,
-117,
-150,
-66,
-148,
-141,
-115,
-144,
-47,
-157,
-61,
-62,
-219,
-208,
-161,
-89,
-95,
-203,
-46,
-66,
-20,
-122,
-243,
-245,
-193,
-84,
-157,
-33,
-207,
-65,
-142,
-204,
-190,
-11,
-124,
-29,
-153,
-24,
-0,
-7,
-107,
-253,
-167,
-245,
-247,
-167,
-156,
-54,
-175,
-64,
-246,
-85,
-115,
-66,
-7,
-145,
-182,
-179,
-171,
-162,
-119,
-21,
-202,
-45,
-130,
-143,
-1,
-145,
-163,
-190,
-187,
-10,
-101,
-147,
-148,
-207,
-250,
-37,
-215,
-93,
-199,
-41,
-59,
-135,
-194,
-190,
-94,
-203,
-95,
-137,
-124,
-161,
-167,
-144,
-31,
-3,
-46,
-68,
-236,
-45,
-158,
-129,
-28,
-211,
-61,
-166,
-255,
-15,
-62,
-50,
-212,
-247,
-0,
-126,
-77,
-254,
-218,
-206,
-51,
-104,
-183,
-20,
-53,
-222,
-137,
-26,
-229,
-221,
-85,
-241,
-5,
-254,
-172,
-207,
-224,
-12,
-228,
-163,
-100,
-79,
-90,
-6,
-148,
-200,
-14,
-47,
-136,
-47,
-0,
-22,
-48,
-242,
-212,
-96,
-62,
-30,
-197,
-123,
-201,
-53,
-223,
-14,
-204,
-15,
-161,
-45,
-99,
-240,
-27,
-237,
-151,
-253,
-242,
-126,
-71,
-127,
-191,
-199,
-161,
-249,
-144,
-150,
-125,
-76,
-127,
-219,
-37,
-253,
-207,
-27,
-92,
-231,
-207,
-218,
-230,
-213,
-200,
-222,
-107,
-146,
-135,
-102,
-178,
-214,
-109,
-163,
-180,
-127,
-168,
-225,
-121,
-16,
-114,
-44,
-54,
-83,
-7,
-197,
-100,
-196,
-152,
-102,
-73,
-228,
-136,
-240,
-17,
-68,
-215,
-176,
-148,
-210,
-175,
-141,
-104,
-202,
-247,
-68,
-20,
-87,
-161,
-131,
-104,
-35,
-29,
-40,
-247,
-227,
-124,
-157,
-181,
-206,
-162,
-201,
-22,
-224,
-73,
-224,
-170,
-66,
-217,
-108,
-224,
-81,
-15,
-237,
-85,
-202,
-127,
-69,
-167,
-236,
-106,
-28,
-125,
-73,
-129,
-254,
-29,
-200,
-23,
-110,
-174,
-254,
-221,
-89,
-203,
-237,
-123,
-222,
-75,
-127,
-255,
-81,
-127,
-23,
-87,
-11,
-3,
-103,
-246,
-228,
-202,
-226,
-55,
-122,
-234,
-236,
-106,
-14,
-224,
-114,
-28,
-157,
-70,
-19,
-104,
-251,
-211,
-244,
-255,
-49,
-223,
-93,
-41,
-95,
-96,
-53,
-224,
-85,
-5,
-250,
-243,
-128,
-89,
-21,
-188,
-160,
-255,
-21,
-192,
-195,
-200,
-60,
-8,
-57,
-154,
-108,
-191,
-2,
-80,
-6,
-86,
-201,
-247,
-47,
-253,
-125,
-1,
-131,
-26,
-251,
-117,
-148,
-230,
-4,
-135,
-6,
-26,
-28,
-65,
-144,
-75,
-222,
-163,
-245,
-239,
-119,
-60,
-52,
-7,
-219,
-235,
-232,
-223,
-214,
-6,
-59,
-192,
-79,
-148,
-199,
-14,
-250,
-123,
-25,
-68,
-25,
-249,
-91,
-253,
-253,
-212,
-192,
-8,
-224,
-101,
-245,
-17,
-85,
-104,
-34,
-0,
-230,
-1,
-255,
-46,
-148,
-61,
-12,
-60,
-89,
-40,
-179,
-43,
-39,
-236,
-96,
-32,
-63,
-189,
-105,
-162,
-189,
-182,
-43,
-182,
-127,
-163,
-19,
-20,
-255,
-145,
-225,
-20,
-10,
-71,
-134,
-90,
-110,
-183,
-99,
-167,
-0,
-203,
-104,
-89,
-134,
-28,
-69,
-205,
-70,
-116,
-2,
-183,
-43,
-205,
-177,
-192,
-154,
-78,
-219,
-117,
-16,
-163,
-178,
-183,
-54,
-232,
-111,
-180,
-119,
-87,
-197,
-183,
-132,
-230,
-122,
-60,
-31,
-39,
-231,
-186,
-16,
-95,
-0,
-92,
-200,
-72,
-29,
-128,
-213,
-135,
-189,
-38,
-224,
-158,
-142,
-166,
-196,
-240,
-45,
-8,
-192,
-154,
-200,
-132,
-159,
-142,
-44,
-207,
-103,
-162,
-230,
-142,
-5,
-186,
-219,
-144,
-189,
-210,
-82,
-250,
-210,
-231,
-160,
-90,
-203,
-192,
-235,
-108,
-162,
-55,
-53,
-189,
-108,
-194,
-0,
-175,
-211,
-186,
-25,
-250,
-119,
-253,
-150,
-247,
-180,
-49,
-50,
-201,
-254,
-238,
-148,
-253,
-18,
-217,
-195,
-46,
-167,
-191,
-131,
-6,
-17,
-114,
-66,
-241,
-168,
-62,
-35,
-223,
-18,
-223,
-162,
-137,
-0,
-152,
-138,
-163,
-99,
-209,
-178,
-127,
-41,
-159,
-173,
-156,
-178,
-143,
-146,
-91,
-211,
-109,
-168,
-101,
-86,
-87,
-50,
-112,
-228,
-86,
-114,
-173,
-101,
-144,
-85,
-210,
-124,
-28,
-237,
-180,
-62,
-31,
-223,
-137,
-129,
-111,
-107,
-177,
-141,
-211,
-143,
-135,
-145,
-21,
-136,
-181,
-250,
-187,
-9,
-153,
-228,
-47,
-39,
-223,
-42,
-44,
-210,
-255,
-91,
-59,
-17,
-8,
-80,
-22,
-235,
-181,
-162,
-189,
-187,
-58,
-190,
-30,
-154,
-207,
-107,
-223,
-119,
-42,
-169,
-183,
-136,
-45,
-0,
-246,
-98,
-228,
-41,
-128,
-181,
-25,
-248,
-15,
-176,
-70,
-129,
-118,
-25,
-231,
-255,
-246,
-20,
-224,
-115,
-229,
-119,
-30,
-0,
-114,
-43,
-165,
-119,
-234,
-95,
-159,
-33,
-131,
-213,
-3,
-216,
-115,
-221,
-198,
-154,
-71,
-114,
-141,
-246,
-192,
-87,
-70,
-235,
-151,
-116,
-6,
-214,
-45,
-109,
-238,
-69,
-249,
-156,
-137,
-12,
-248,
-23,
-232,
-239,
-79,
-40,
-223,
-13,
-28,
-154,
-160,
-65,
-4,
-236,
-81,
-117,
-191,
-206,
-75,
-111,
-34,
-0,
-46,
-213,
-254,
-173,
-224,
-148,
-189,
-81,
-7,
-223,
-147,
-136,
-117,
-221,
-73,
-58,
-96,
-63,
-141,
-28,
-25,
-94,
-139,
-216,
-100,
-204,
-210,
-246,
-75,
-85,
-93,
-195,
-225,
-251,
-53,
-223,
-59,
-37,
-224,
-200,
-176,
-80,
-183,
-53,
-114,
-108,
-252,
-48,
-50,
-177,
-111,
-68,
-108,
-57,
-220,
-99,
-169,
-231,
-32,
-71,
-177,
-119,
-34,
-203,
-244,
-25,
-218,
-239,
-131,
-128,
-181,
-3,
-251,
-27,
-237,
-221,
-85,
-241,
-245,
-212,
-127,
-82,
-235,
-247,
-172,
-224,
-97,
-17,
-91,
-0,
-44,
-135,
-8,
-204,
-239,
-57,
-101,
-118,
-203,
-246,
-16,
-162,
-8,
-62,
-22,
-177,
-113,
-113,
-143,
-116,
-127,
-160,
-207,
-102,
-229,
-34,
-207,
-70,
-64,
-108,
-209,
-33,
-215,
-56,
-15,
-44,
-215,
-200,
-245,
-0,
-182,
-99,
-251,
-249,
-120,
-141,
-54,
-200,
-5,
-212,
-207,
-157,
-178,
-186,
-179,
-234,
-245,
-43,
-248,
-217,
-229,
-239,
-46,
-37,
-245,
-22,
-77,
-4,
-192,
-129,
-218,
-230,
-125,
-133,
-242,
-183,
-34,
-75,
-208,
-185,
-136,
-118,
-126,
-119,
-45,
-223,
-17,
-89,
-98,
-63,
-170,
-207,
-127,
-117,
-63,
-231,
-129,
-235,
-108,
-128,
-8,
-140,
-187,
-41,
-104,
-182,
-41,
-63,
-50,
-108,
-45,
-120,
-187,
-34,
-246,
-187,
-171,
-226,
-91,
-168,
-255,
-54,
-34,
-120,
-223,
-226,
-171,
-31,
-6,
-128,
-119,
-225,
-152,
-82,
-35,
-91,
-172,
-61,
-80,
-161,
-140,
-163,
-220,
-213,
-250,
-15,
-40,
-253,
-59,
-99,
-92,
-252,
-245,
-250,
-128,
-158,
-64,
-164,
-224,
-128,
-101,
-17,
-185,
-30,
-224,
-113,
-253,
-251,
-34,
-31,
-175,
-209,
-4,
-178,
-130,
-248,
-175,
-246,
-209,
-213,
-97,
-124,
-222,
-243,
-15,
-228,
-220,
-250,
-243,
-148,
-120,
-129,
-33,
-90,
-103,
-144,
-229,
-153,
-215,
-104,
-198,
-25,
-136,
-77,
-4,
-192,
-198,
-250,
-66,
-175,
-43,
-227,
-27,
-3,
-192,
-223,
-180,
-111,
-239,
-242,
-212,
-149,
-29,
-25,
-54,
-222,
-91,
-199,
-64,
-236,
-119,
-87,
-199,
-215,
-169,
-251,
-37,
-34,
-12,
-159,
-223,
-215,
-189,
-133,
-2,
-57,
-201,
-90,
-8,
-124,
-143,
-146,
-237,
-53,
-176,
-60,
-242,
-193,
-94,
-8,
-124,
-61,
-214,
-133,
-151,
-33,
-223,
-119,
-95,
-86,
-65,
-119,
-155,
-210,
-220,
-19,
-229,
-194,
-145,
-65,
-110,
-96,
-227,
-245,
-112,
-44,
-208,
-214,
-14,
-118,
-114,
-101,
-228,
-193,
-53,
-124,
-160,
-161,
-55,
-32,
-185,
-213,
-226,
-81,
-116,
-112,
-78,
-170,
-232,
-151,
-221,
-71,
-122,
-45,
-53,
-41,
-63,
-50,
-12,
-114,
-89,
-141,
-141,
-216,
-239,
-46,
-132,
-47,
-114,
-252,
-55,
-19,
-248,
-166,
-71,
-200,
-120,
-173,
-253,
-250,
-6,
-178,
-18,
-120,
-28,
-249,
-232,
-28,
-137,
-232,
-129,
-222,
-174,
-127,
-143,
-212,
-242,
-199,
-136,
-241,
-229,
-47,
-92,
-248,
-47,
-250,
-160,
-190,
-87,
-65,
-99,
-245,
-0,
-199,
-70,
-189,
-120,
-4,
-32,
-230,
-181,
-15,
-34,
-190,
-221,
-181,
-95,
-213,
-186,
-65,
-132,
-120,
-146,
-205,
-70,
-246,
-229,
-165,
-198,
-78,
-4,
-160,
-164,
-221,
-178,
-228,
-74,
-196,
-243,
-241,
-216,
-246,
-59,
-180,
-27,
-0,
-95,
-168,
-187,
-39,
-135,
-126,
-69,
-68,
-203,
-63,
-139,
-234,
-237,
-205,
-187,
-144,
-189,
-252,
-92,
-253,
-235,
-221,
-230,
-244,
-141,
-216,
-239,
-46,
-148,
-111,
-205,
-107,
-251,
-68,
-219,
-251,
-233,
-10,
-96,
-21,
-228,
-56,
-251,
-2,
-100,
-219,
-183,
-64,
-255,
-94,
-128,
-184,
-47,
-119,
-219,
-243,
-39,
-140,
-13,
-168,
-16,
-248,
-57,
-178,
-237,
-2,
-57,
-29,
-248,
-27,
-112,
-34,
-162,
-4,
-252,
-7,
-98,
-246,
-91,
-42,
-72,
-134,
-216,
-87,
-23,
-63,
-11,
-160,
-255,
-180,
-219,
-96,
-24,
-125,
-76,
-72,
-24,
-151,
-0,
-158,
-139,
-24,
-96,
-93,
-128,
-44,
-197,
-231,
-32,
-202,
-175,
-135,
-16,
-37,
-208,
-143,
-40,
-24,
-172,
-140,
-66,
-31,
-93,
-220,
-75,
-141,
-193,
-15,
-35,
-143,
-72,
-147,
-0,
-72,
-72,
-24,
-207,
-208,
-121,
-188,
-8,
-177,
-182,
-132,
-10,
-35,
-21,
-68,
-97,
-188,
-8,
-81,
-212,
-37,
-1,
-48,
-70,
-16,
-93,
-209,
-148,
-48,
-225,
-144,
-25,
-99,
-206,
-214,
-255,
-191,
-167,
-130,
-238,
-125,
-74,
-219,
-41,
-254,
-66,
-194,
-144,
-64,
-79,
-65,
-8,
-17,
-207,
-182,
-219,
-172,
-210,
-2,
-177,
-197,
-190,
-3,
-245,
-78,
-107,
-201,
-115,
-51,
-196,
-230,
-124,
-134,
-46,
-153,
-91,
-251,
-248,
-147,
-219,
-217,
-79,
-199,
-241,
-204,
-155,
-104,
-32,
-204,
-235,
-204,
-194,
-6,
-111,
-185,
-171,
-130,
-246,
-74,
-29,
-67,
-79,
-5,
-141,
-109,
-208,
-151,
-23,
-58,
-215,
-218,
-60,
-180,
-93,
-129,
-199,
-43,
-16,
-95,
-135,
-169,
-228,
-78,
-101,
-127,
-35,
-192,
-189,
-182,
-132,
-223,
-106,
-72,
-28,
-192,
-75,
-17,
-165,
-226,
-60,
-253,
-123,
-9,
-240,
-37,
-234,
-143,
-34,
-215,
-38,
-183,
-164,
-172,
-220,
-202,
-57,
-207,
-108,
-33,
-129,
-198,
-83,
-157,
-64,
-143,
-65,
-8,
-201,
-157,
-72,
-94,
-171,
-191,
-109,
-20,
-161,
-217,
-29,
-250,
-123,
-57,
-162,
-28,
-219,
-6,
-241,
-138,
-187,
-168,
-3,
-175,
-231,
-234,
-139,
-4,
-79,
-128,
-141,
-197,
-9,
-58,
-8,
-207,
-67,
-142,
-152,
-78,
-67,
-237,
-22,
-16,
-123,
-254,
-251,
-2,
-218,
-91,
-108,
-130,
-24,
-43,
-129,
-199,
-85,
-27,
-57,
-177,
-0,
-241,
-98,
-124,
-81,
-11,
-1,
-240,
-67,
-231,
-90,
-3,
-22,
-169,
-1,
-237,
-183,
-39,
-87,
-170,
-62,
-138,
-152,
-45,
-91,
-19,
-229,
-133,
-148,
-152,
-249,
-86,
-240,
-123,
-55,
-185,
-237,
-11,
-136,
-149,
-227,
-21,
-228,
-71,
-226,
-232,
-24,
-26,
-112,
-148,
-42,
-240,
-185,
-64,
-105,
-75,
-143,
-148,
-149,
-206,
-250,
-195,
-92,
-208,
-164,
-159,
-173,
-64,
-207,
-65,
-8,
-25,
-92,
-1,
-172,
-130,
-172,
-0,
-188,
-65,
-70,
-3,
-121,
-206,
-68,
-221,
-82,
-129,
-93,
-240,
-4,
-0,
-109,
-200,
-111,
-75,
-196,
-0,
-106,
-33,
-240,
-242,
-46,
-188,
-10,
-124,
-215,
-36,
-183,
-173,
-184,
-162,
-3,
-31,
-87,
-155,
-30,
-100,
-79,
-95,
-194,
-231,
-56,
-228,
-88,
-112,
-50,
-178,
-63,
-183,
-251,
-249,
-89,
-192,
-49,
-1,
-237,
-45,
-54,
-35,
-247,
-24,
-253,
-145,
-135,
-206,
-70,
-204,
-253,
-8,
-121,
-188,
-137,
-208,
-208,
-232,
-75,
-34,
-167,
-30,
-211,
-244,
-223,
-3,
-4,
-172,
-78,
-10,
-60,
-236,
-170,
-238,
-120,
-52,
-52,
-28,
-114,
-218,
-98,
-45,
-88,
-7,
-252,
-92,
-42,
-120,
-237,
-72,
-30,
-150,
-254,
-116,
-10,
-199,
-193,
-136,
-233,
-243,
-239,
-181,
-126,
-14,
-21,
-95,
-119,
-114,
-31,
-142,
-74,
-75,
-75,
-228,
-200,
-18,
-28,
-55,
-232,
-94,
-192,
-96,
-16,
-66,
-23,
-179,
-16,
-5,
-206,
-103,
-28,
-250,
-110,
-65,
-8,
-35,
-1,
-89,
-94,
-158,
-133,
-216,
-166,
-159,
-67,
-64,
-200,
-176,
-0,
-158,
-91,
-234,
-96,
-187,
-33,
-70,
-31,
-149,
-231,
-81,
-250,
-44,
-31,
-160,
-16,
-68,
-163,
-1,
-143,
-23,
-33,
-66,
-196,
-78,
-136,
-25,
-148,
-216,
-178,
-7,
-240,
-154,
-10,
-124,
-64,
-255,
-255,
-98,
-68,
-56,
-63,
-137,
-172,
-10,
-188,
-121,
-20,
-10,
-237,
-45,
-182,
-32,
-143,
-111,
-119,
-135,
-135,
-110,
-146,
-78,
-134,
-149,
-112,
-2,
-101,
-6,
-246,
-241,
-205,
-74,
-126,
-166,
-254,
-3,
-216,
-177,
-225,
-125,
-218,
-85,
-231,
-75,
-10,
-229,
-91,
-218,
-137,
-26,
-200,
-231,
-105,
-200,
-105,
-7,
-72,
-0,
-151,
-210,
-83,
-15,
-228,
-216,
-22,
-196,
-124,
-219,
-43,
-176,
-24,
-185,
-13,
-240,
-90,
-209,
-146,
-111,
-127,
-250,
-93,
-254,
-227,
-15,
-66,
-8,
-178,
-23,
-62,
-10,
-248,
-3,
-185,
-27,
-234,
-199,
-28,
-154,
-110,
-65,
-8,
-35,
-0,
-177,
-129,
-182,
-152,
-6,
-60,
-111,
-180,
-250,
-82,
-6,
-196,
-51,
-110,
-33,
-178,
-116,
-108,
-28,
-46,
-92,
-121,
-172,
-133,
-44,
-55,
-167,
-33,
-49,
-20,
-94,
-167,
-2,
-224,
-142,
-144,
-9,
-235,
-225,
-23,
-228,
-60,
-84,
-209,
-254,
-41,
-1,
-160,
-191,
-109,
-190,
-135,
-45,
-29,
-154,
-77,
-181,
-236,
-116,
-253,
-221,
-84,
-0,
-216,
-136,
-203,
-123,
-34,
-113,
-242,
-0,
-126,
-223,
-176,
-159,
-214,
-225,
-236,
-205,
-133,
-114,
-203,
-47,
-200,
-215,
-129,
-220,
-77,
-126,
-54,
-142,
-123,
-115,
-9,
-173,
-13,
-63,
-14,
-78,
-220,
-75,
-15,
-221,
-133,
-74,
-243,
-141,
-146,
-250,
-175,
-107,
-125,
-231,
-143,
-90,
-37,
-40,
-4,
-33,
-212,
-50,
-128,
-41,
-206,
-239,
-151,
-105,
-217,
-121,
-78,
-89,
-227,
-32,
-132,
-68,
-80,
-234,
-20,
-248,
-117,
-218,
-35,
-14,
-3,
-136,
-85,
-221,
-84,
-2,
-179,
-249,
-148,
-240,
-184,
-10,
-49,
-211,
-117,
-93,
-120,
-95,
-138,
-36,
-155,
-184,
-170,
-170,
-109,
-31,
-240,
-8,
-0,
-235,
-208,
-244,
-125,
-135,
-198,
-110,
-13,
-172,
-67,
-75,
-176,
-0,
-64,
-172,
-23,
-103,
-34,
-75,
-238,
-103,
-32,
-138,
-183,
-121,
-200,
-106,
-52,
-56,
-139,
-19,
-121,
-20,
-235,
-243,
-200,
-99,
-31,
-108,
-71,
-190,
-143,
-31,
-240,
-139,
-40,
-225,
-99,
-151,
-246,
-65,
-39,
-25,
-228,
-1,
-86,
-142,
-175,
-160,
-177,
-219,
-185,
-107,
-75,
-234,
-109,
-24,
-252,
-214,
-91,
-189,
-32,
-80,
-8,
-66,
-168,
-101,
-69,
-1,
-176,
-162,
-150,
-253,
-175,
-64,
-215,
-40,
-8,
-97,
-204,
-9,
-75,
-132,
-61,
-162,
-135,
-103,
-84,
-1,
-53,
-214,
-209,
-246,
-126,
-61,
-2,
-192,
-78,
-238,
-91,
-28,
-154,
-155,
-144,
-85,
-202,
-114,
-5,
-154,
-16,
-1,
-96,
-221,
-174,
-255,
-233,
-148,
-89,
-103,
-166,
-143,
-85,
-181,
-45,
-240,
-121,
-22,
-185,
-75,
-249,
-49,
-200,
-170,
-98,
-17,
-34,
-76,
-62,
-217,
-128,
-143,
-93,
-225,
-4,
-133,
-78,
-39,
-143,
-140,
-52,
-16,
-172,
-213,
-161,
-113,
-183,
-1,
-235,
-23,
-234,
-108,
-26,
-189,
-133,
-192,
-51,
-67,
-251,
-217,
-10,
-20,
-66,
-16,
-105,
-217,
-83,
-2,
-0,
-241,
-79,
-182,
-46,
-194,
-71,
-20,
-232,
-130,
-67,
-16,
-197,
-158,
-176,
-68,
-216,
-35,
-122,
-120,
-142,
-249,
-21,
-69,
-76,
-180,
-189,
-223,
-162,
-0,
-208,
-178,
-91,
-181,
-108,
-115,
-103,
-178,
-255,
-214,
-169,
-111,
-34,
-0,
-172,
-150,
-124,
-79,
-167,
-236,
-35,
-90,
-214,
-104,
-73,
-76,
-158,
-245,
-200,
-226,
-82,
-52,
-218,
-116,
-3,
-30,
-118,
-11,
-252,
-161,
-64,
-250,
-221,
-148,
-254,
-177,
-26,
-58,
-187,
-13,
-216,
-183,
-80,
-254,
-89,
-45,
-111,
-125,
-170,
-21,
-12,
-10,
-65,
-8,
-181,
-12,
-68,
-82,
-90,
-205,
-181,
-125,
-112,
-69,
-23,
-202,
-224,
-32,
-132,
-68,
-158,
-176,
-68,
-216,
-35,
-22,
-248,
-141,
-249,
-21,
-69,
-76,
-126,
-93,
-238,
-215,
-233,
-131,
-43,
-0,
-108,
-70,
-167,
-3,
-157,
-255,
-239,
-228,
-212,
-7,
-9,
-0,
-68,
-155,
-110,
-191,
-210,
-110,
-234,
-177,
-149,
-17,
-165,
-222,
-34,
-2,
-163,
-67,
-33,
-43,
-9,
-155,
-142,
-204,
-226,
-7,
-33,
-109,
-11,
-124,
-172,
-246,
-191,
-54,
-111,
-162,
-210,
-239,
-172,
-244,
-149,
-115,
-131,
-124,
-27,
-80,
-76,
-224,
-114,
-190,
-150,
-183,
-14,
-172,
-26,
-12,
-202,
-87,
-0,
-51,
-16,
-39,
-148,
-187,
-245,
-1,
-108,
-224,
-105,
-219,
-100,
-5,
-16,
-109,
-194,
-18,
-105,
-143,
-88,
-224,
-57,
-230,
-87,
-20,
-49,
-249,
-117,
-185,
-95,
-167,
-15,
-174,
-0,
-176,
-154,
-245,
-155,
-17,
-229,
-228,
-99,
-140,
-12,
-89,
-21,
-42,
-0,
-108,
-146,
-153,
-129,
-112,
-93,
-136,
-203,
-46,
-192,
-55,
-107,
-120,
-44,
-137,
-28,
-117,
-218,
-113,
-252,
-85,
-36,
-145,
-137,
-205,
-90,
-244,
-253,
-2,
-189,
-53,
-104,
-242,
-70,
-182,
-70,
-142,
-134,
-33,
-60,
-109,
-91,
-232,
-10,
-224,
-153,
-200,
-50,
-127,
-33,
-170,
-92,
-68,
-242,
-46,
-44,
-64,
-4,
-221,
-64,
-22,
-237,
-232,
-160,
-16,
-132,
-80,
-203,
-32,
-223,
-2,
-216,
-24,
-248,
-190,
-140,
-42,
-65,
-65,
-8,
-137,
-60,
-97,
-137,
-180,
-71,
-44,
-240,
-28,
-211,
-43,
-138,
-30,
-248,
-181,
-190,
-95,
-114,
-108,
-81,
-40,
-191,
-195,
-169,
-59,
-182,
-80,
-23,
-42,
-0,
-110,
-161,
-30,
-183,
-214,
-240,
-176,
-123,
-240,
-135,
-113,
-210,
-217,
-35,
-81,
-150,
-236,
-209,
-160,
-171,
-176,
-252,
-165,
-150,
-121,
-147,
-131,
-32,
-138,
-92,
-8,
-215,
-1,
-212,
-102,
-202,
-118,
-104,
-47,
-82,
-218,
-143,
-235,
-239,
-143,
-234,
-239,
-139,
-235,
-218,
-70,
-1,
-133,
-32,
-132,
-90,
-6,
-35,
-149,
-128,
-231,
-107,
-217,
-235,
-157,
-178,
-224,
-32,
-132,
-68,
-158,
-176,
-68,
-220,
-35,
-106,
-219,
-49,
-191,
-162,
-136,
-201,
-175,
-235,
-253,
-146,
-163,
-40,
-0,
-126,
-228,
-212,
-109,
-87,
-168,
-171,
-21,
-0,
-200,
-17,
-39,
-200,
-87,
-187,
-44,
-171,
-144,
-13,
-36,
-91,
-229,
-128,
-100,
-163,
-18,
-15,
-164,
-178,
-71,
-12,
-122,
-158,
-18,
-2,
-72,
-102,
-170,
-57,
-136,
-197,
-160,
-215,
-148,
-156,
-60,
-55,
-95,
-232,
-41,
-128,
-205,
-204,
-60,
-144,
-32,
-213,
-67,
-251,
-25,
-165,
-61,
-189,
-208,
-246,
-179,
-33,
-215,
-234,
-12,
-252,
-65,
-8,
-97,
-164,
-0,
-216,
-18,
-89,
-146,
-92,
-75,
-30,
-146,
-58,
-56,
-8,
-33,
-113,
-149,
-58,
-207,
-33,
-210,
-30,
-209,
-105,
-59,
-30,
-86,
-20,
-49,
-183,
-80,
-157,
-238,
-151,
-28,
-69,
-1,
-96,
-195,
-166,
-77,
-101,
-48,
-203,
-116,
-136,
-0,
-176,
-6,
-83,
-135,
-87,
-208,
-252,
-76,
-105,
-142,
-170,
-160,
-177,
-177,
-3,
-183,
-45,
-169,
-127,
-51,
-121,
-54,
-43,
-123,
-74,
-240,
-195,
-10,
-126,
-239,
-83,
-154,
-57,
-52,
-179,
-3,
-120,
-123,
-21,
-173,
-210,
-219,
-109,
-192,
-147,
-200,
-92,
-156,
-174,
-99,
-248,
-217,
-117,
-109,
-163,
-129,
-193,
-32,
-132,
-224,
-8,
-0,
-45,
-179,
-22,
-78,
-31,
-163,
-65,
-16,
-66,
-34,
-79,
-88,
-34,
-236,
-17,
-61,
-237,
-198,
-244,
-138,
-162,
-7,
-126,
-81,
-239,
-55,
-6,
-16,
-19,
-93,
-59,
-25,
-95,
-89,
-65,
-103,
-29,
-100,
-30,
-163,
-36,
-19,
-53,
-146,
-238,
-28,
-224,
-43,
-21,
-124,
-118,
-37,
-199,
-29,
-84,
-68,
-31,
-210,
-190,
-221,
-165,
-180,
-191,
-43,
-163,
-83,
-90,
-107,
-102,
-124,
-19,
-225,
-74,
-85,
-187,
-13,
-248,
-162,
-254,
-173,
-204,
-157,
-216,
-11,
-232,
-41,
-8,
-97,
-236,
-9,
-75,
-132,
-61,
-98,
-129,
-223,
-115,
-24,
-227,
-43,
-138,
-152,
-252,
-250,
-184,
-223,
-24,
-32,
-143,
-91,
-88,
-107,
-157,
-71,
-190,
-196,
-247,
-186,
-33,
-147,
-39,
-253,
-152,
-173,
-207,
-206,
-205,
-175,
-184,
-30,
-146,
-123,
-241,
-225,
-194,
-152,
-57,
-160,
-230,
-154,
-111,
-34,
-63,
-13,
-56,
-133,
-193,
-188,
-136,
-235,
-145,
-7,
-87,
-157,
-67,
-133,
-16,
-243,
-240,
-182,
-219,
-128,
-251,
-245,
-111,
-183,
-216,
-254,
-109,
-65,
-15,
-65,
-8,
-137,
-56,
-97,
-137,
-180,
-71,
-44,
-240,
-28,
-15,
-43,
-138,
-152,
-91,
-168,
-232,
-247,
-27,
-3,
-228,
-113,
-40,
-107,
-175,
-77,
-110,
-121,
-120,
-102,
-73,
-253,
-74,
-136,
-167,
-168,
-197,
-124,
-228,
-52,
-203,
-245,
-228,
-91,
-136,
-108,
-57,
-118,
-116,
-198,
-204,
-64,
-134,
-170,
-2,
-223,
-119,
-21,
-120,
-220,
-166,
-215,
-113,
-189,
-1,
-167,
-226,
-232,
-202,
-2,
-239,
-253,
-89,
-136,
-224,
-69,
-255,
-174,
-83,
-223,
-170,
-39,
-16,
-49,
-8,
-33,
-145,
-39,
-44,
-145,
-246,
-136,
-5,
-250,
-49,
-189,
-162,
-232,
-129,
-95,
-212,
-251,
-141,
-1,
-100,
-91,
-99,
-221,
-118,
-7,
-142,
-155,
-61,
-244,
-214,
-207,
-96,
-30,
-37,
-177,
-27,
-144,
-204,
-86,
-159,
-68,
-20,
-216,
-54,
-174,
-197,
-99,
-136,
-99,
-219,
-161,
-56,
-161,
-191,
-145,
-109,
-133,
-61,
-234,
-251,
-118,
-205,
-181,
-215,
-64,
-132,
-232,
-101,
-72,
-152,
-54,
-27,
-174,
-237,
-98,
-100,
-101,
-209,
-54,
-94,
-134,
-77,
-102,
-251,
-239,
-122,
-234,
-113,
-2,
-34,
-78,
-88,
-34,
-238,
-17,
-29,
-218,
-49,
-191,
-162,
-136,
-201,
-175,
-143,
-251,
-77,
-72,
-240,
-34,
-246,
-132,
-37,
-226,
-30,
-209,
-161,
-27,
-15,
-43,
-138,
-152,
-91,
-168,
-232,
-247,
-155,
-144,
-224,
-69,
-236,
-9,
-75,
-196,
-61,
-162,
-210,
-140,
-249,
-21,
-69,
-76,
-126,
-125,
-220,
-111,
-66,
-66,
-41,
-98,
-78,
-88,
-250,
-217,
-35,
-142,
-249,
-21,
-69,
-76,
-126,
-125,
-220,
-111,
-194,
-4,
-3,
-225,
-231,
-154,
-209,
-39,
-108,
-108,
-196,
-20,
-80,
-74,
-19,
-123,
-203,
-19,
-155,
-95,
-212,
-251,
-13,
-1,
-162,
-48,
-179,
-86,
-119,
-165,
-249,
-246,
-144,
-252,
-136,
-32,
-182,
-250,
-107,
-84,
-208,
-21,
-177,
-16,
-57,
-206,
-187,
-4,
-177,
-247,
-95,
-181,
-97,
-255,
-214,
-82,
-62,
-39,
-20,
-202,
-47,
-3,
-30,
-172,
-104,
-183,
-10,
-185,
-210,
-240,
-131,
-21,
-116,
-239,
-82,
-154,
-39,
-129,
-85,
-60,
-245,
-43,
-146,
-219,
-44,
-236,
-81,
-193,
-103,
-71,
-165,
-185,
-135,
-190,
-3,
-240,
-208,
-49,
-96,
-228,
-120,
-0,
-227,
-96,
-69,
-17,
-147,
-95,
-31,
-247,
-27,
-10,
-242,
-179,
-241,
-42,
-75,
-187,
-131,
-148,
-230,
-212,
-26,
-94,
-22,
-54,
-247,
-226,
-127,
-200,
-163,
-254,
-160,
-99,
-118,
-32,
-155,
-117,
-5,
-191,
-237,
-181,
-221,
-23,
-157,
-178,
-12,
-217,
-86,
-253,
-179,
-166,
-237,
-183,
-181,
-109,
-105,
-92,
-65,
-114,
-205,
-254,
-129,
-21,
-52,
-86,
-72,
-60,
-130,
-39,
-211,
-51,
-18,
-142,
-204,
-190,
-223,
-119,
-135,
-222,
-91,
-107,
-208,
-49,
-96,
-228,
-68,
-5,
-241,
-87,
-20,
-67,
-255,
-98,
-247,
-1,
-36,
-98,
-51,
-136,
-243,
-210,
-64,
-40,
-50,
-96,
-9,
-242,
-120,
-123,
-111,
-170,
-225,
-101,
-81,
-52,
-65,
-222,
-20,
-248,
-179,
-214,
-45,
-160,
-196,
-4,
-216,
-195,
-239,
-11,
-218,
-102,
-123,
-167,
-204,
-70,
-50,
-174,
-244,
-182,
-100,
-228,
-42,
-224,
-205,
-158,
-122,
-27,
-69,
-107,
-186,
-111,
-98,
-23,
-104,
-109,
-223,
-127,
-229,
-169,
-179,
-130,
-198,
-155,
-216,
-53,
-58,
-232,
-24,
-48,
-114,
-34,
-130,
-200,
-95,
-216,
-216,
-252,
-70,
-27,
-136,
-89,
-44,
-120,
-108,
-227,
-129,
-29,
-180,
-110,
-10,
-53,
-153,
-145,
-203,
-4,
-128,
-214,
-101,
-192,
-201,
-90,
-127,
-23,
-97,
-137,
-69,
-143,
-87,
-250,
-181,
-157,
-178,
-119,
-106,
-217,
-71,
-3,
-218,
-219,
-201,
-57,
-224,
-17,
-75,
-190,
-242,
-25,
-136,
-150,
-236,
-161,
-93,
-151,
-220,
-7,
-96,
-43,
-167,
-124,
-67,
-196,
-154,
-113,
-38,
-195,
-178,
-206,
-164,
-99,
-192,
-200,
-132,
-132,
-34,
-200,
-191,
-180,
-103,
-120,
-234,
-78,
-209,
-186,
-111,
-5,
-240,
-41,
-21,
-0,
-90,
-191,
-6,
-185,
-147,
-207,
-7,
-2,
-248,
-93,
-13,
-60,
-84,
-40,
-179,
-118,
-23,
-181,
-81,
-131,
-24,
-185,
-10,
-112,
-3,
-162,
-174,
-163,
-2,
-124,
-22,
-129,
-31,
-77,
-96,
-95,
-229,
-243,
-63,
-59,
-7,
-201,
-205,
-189,
-27,
-133,
-224,
-31,
-115,
-32,
-82,
-60,
-123,
-135,
-95,
-148,
-56,
-251,
-9,
-195,
-1,
-226,
-33,
-103,
-93,
-110,
-215,
-116,
-202,
-87,
-213,
-242,
-133,
-192,
-122,
-1,
-124,
-42,
-5,
-128,
-210,
-216,
-128,
-156,
-94,
-167,
-29,
-90,
-160,
-166,
-79,
-118,
-21,
-112,
-170,
-83,
-102,
-19,
-122,
-252,
-180,
-238,
-158,
-156,
-54,
-75,
-2,
-215,
-104,
-187,
-47,
-145,
-235,
-6,
-38,
-19,
-176,
-154,
-137,
-2,
-58,
-166,
-61,
-42,
-225,
-25,
-45,
-158,
-189,
-195,
-179,
-83,
-156,
-125,
-82,
-26,
-176,
-74,
-32,
-91,
-16,
-235,
-82,
-91,
-186,
-5,
-1,
-54,
-82,
-154,
-218,
-173,
-7,
-185,
-59,
-243,
-126,
-78,
-153,
-77,
-144,
-113,
-118,
-85,
-91,
-135,
-222,
-162,
-74,
-0,
-236,
-175,
-52,
-94,
-227,
-40,
-224,
-88,
-253,
-119,
-186,
-210,
-93,
-227,
-148,
-29,
-139,
-172,
-32,
-30,
-118,
-203,
-106,
-250,
-100,
-87,
-1,
-11,
-144,
-37,
-251,
-242,
-200,
-137,
-204,
-28,
-26,
-186,
-244,
-34,
-122,
-131,
-133,
-58,
-71,
-238,
-69,
-182,
-4,
-175,
-107,
-194,
-163,
-53,
-136,
-148,
-246,
-168,
-192,
-51,
-106,
-60,
-123,
-229,
-25,
-35,
-206,
-254,
-132,
-73,
-3,
-214,
-22,
-228,
-138,
-169,
-82,
-37,
-36,
-240,
-93,
-165,
-249,
-99,
-0,
-191,
-173,
-149,
-246,
-122,
-167,
-204,
-10,
-226,
-247,
-214,
-180,
-173,
-68,
-129,
-214,
-134,
-227,
-122,
-162,
-134,
-167,
-141,
-219,
-183,
-135,
-83,
-182,
-188,
-142,
-173,
-63,
-212,
-221,
-79,
-129,
-151,
-93,
-5,
-28,
-65,
-190,
-218,
-109,
-101,
-73,
-73,
-110,
-215,
-1,
-240,
-235,
-54,
-60,
-218,
-92,
-52,
-90,
-218,
-163,
-66,
-187,
-232,
-241,
-236,
-137,
-16,
-103,
-95,
-249,
-244,
-146,
-6,
-172,
-112,
-141,
-12,
-9,
-38,
-81,
-171,
-87,
-33,
-242,
-234,
-139,
-142,
-201,
-88,
-201,
-163,
-233,
-122,
-143,
-33,
-245,
-222,
-166,
-40,
-77,
-80,
-134,
-40,
-242,
-208,
-90,
-47,
-37,
-87,
-92,
-62,
-140,
-19,
-59,
-176,
-164,
-157,
-181,
-122,
-180,
-184,
-214,
-41,
-187,
-164,
-64,
-107,
-39,
-246,
-188,
-26,
-158,
-7,
-40,
-157,
-187,
-119,
-183,
-1,
-77,
-188,
-201,
-58,
-42,
-120,
-217,
-85,
-192,
-44,
-125,
-38,
-243,
-105,
-169,
-180,
-3,
-246,
-113,
-238,
-179,
-113,
-0,
-211,
-54,
-23,
-140,
-154,
-246,
-104,
-60,
-129,
-142,
-105,
-192,
-60,
-147,
-118,
-46,
-98,
-172,
-113,
-1,
-226,
-137,
-182,
-159,
-62,
-175,
-223,
-214,
-240,
-9,
-89,
-125,
-61,
-68,
-33,
-204,
-86,
-13,
-207,
-78,
-201,
-88,
-129,
-101,
-144,
-179,
-105,
-240,
-8,
-124,
-224,
-245,
-90,
-231,
-61,
-222,
-43,
-225,
-249,
-121,
-109,
-115,
-32,
-185,
-178,
-205,
-27,
-135,
-175,
-164,
-189,
-69,
-213,
-22,
-224,
-163,
-74,
-83,
-25,
-172,
-22,
-9,
-171,
-182,
-0,
-120,
-154,
-83,
-182,
-167,
-182,
-13,
-118,
-119,
-119,
-218,
-126,
-219,
-233,
-95,
-171,
-47,
-55,
-226,
-225,
-249,
-48,
-162,
-245,
-159,
-133,
-108,
-71,
-106,
-117,
-35,
-157,
-64,
-15,
-105,
-143,
-38,
-2,
-60,
-147,
-246,
-14,
-157,
-180,
-247,
-144,
-251,
-118,
-131,
-124,
-201,
-189,
-249,
-223,
-148,
-79,
-211,
-213,
-87,
-168,
-87,
-98,
-231,
-100,
-172,
-192,
-225,
-122,
-221,
-95,
-120,
-234,
-236,
-17,
-90,
-169,
-129,
-143,
-167,
-205,
-106,
-58,
-206,
-46,
-35,
-95,
-254,
-7,
-235,
-131,
-2,
-5,
-128,
-205,
-72,
-52,
-201,
-83,
-231,
-238,
-245,
-31,
-211,
-177,
-236,
-150,
-93,
-171,
-109,
-255,
-68,
-192,
-254,
-191,
-192,
-123,
-21,
-167,
-127,
-175,
-14,
-109,
-87,
-224,
-113,
-168,
-182,
-255,
-46,
-121,
-124,
-197,
-147,
-218,
-240,
-106,
-114,
-209,
-232,
-105,
-143,
-148,
-46,
-218,
-146,
-54,
-38,
-175,
-24,
-40,
-76,
-218,
-211,
-128,
-13,
-11,
-245,
-27,
-147,
-235,
-24,
-230,
-80,
-72,
-78,
-233,
-208,
-181,
-89,
-125,
-221,
-194,
-144,
-142,
-106,
-201,
-141,
-89,
-30,
-193,
-209,
-68,
-35,
-123,
-101,
-235,
-132,
-84,
-106,
-226,
-91,
-194,
-243,
-68,
-125,
-127,
-139,
-128,
-203,
-26,
-182,
-181,
-168,
-18,
-0,
-231,
-42,
-205,
-128,
-209,
-26,
-45,
-16,
-187,
-127,
-21,
-109,
-55,
-66,
-86,
-144,
-15,
-34,
-38,
-194,
-107,
-144,
-219,
-6,
-116,
-218,
-238,
-214,
-93,
-184,
-143,
-180,
-71,
-209,
-20,
-138,
-49,
-121,
-41,
-191,
-78,
-194,
-132,
-145,
-147,
-246,
-119,
-120,
-38,
-45,
-240,
-33,
-173,
-183,
-198,
-60,
-215,
-151,
-208,
-181,
-93,
-125,
-5,
-229,
-180,
-139,
-1,
-228,
-24,
-10,
-156,
-21,
-31,
-240,
-97,
-45,
-107,
-52,
-129,
-181,
-173,
-221,
-138,
-64,
-243,
-112,
-102,
-22,
-101,
-118,
-0,
-27,
-147,
-167,
-219,
-42,
-53,
-9,
-38,
-87,
-72,
-126,
-163,
-80,
-254,
-16,
-112,
-121,
-147,
-62,
-53,
-233,
-95,
-77,
-91,
-27,
-219,
-97,
-31,
-167,
-236,
-123,
-90,
-118,
-126,
-219,
-62,
-133,
-92,
-56,
-106,
-218,
-35,
-226,
-230,
-81,
-143,
-170,
-156,
-36,
-130,
-48,
-33,
-159,
-180,
-179,
-240,
-56,
-174,
-32,
-146,
-251,
-97,
-29,
-136,
-111,
-35,
-159,
-180,
-3,
-138,
-50,
-198,
-199,
-234,
-235,
-255,
-244,
-154,
-238,
-57,
-183,
-253,
-202,
-238,
-21,
-202,
-199,
-105,
-187,
-190,
-182,
-157,
-14,
-172,
-208,
-176,
-173,
-133,
-207,
-18,
-240,
-233,
-228,
-138,
-194,
-201,
-84,
-175,
-166,
-62,
-169,
-116,
-174,
-80,
-123,
-166,
-150,
-5,
-47,
-251,
-155,
-244,
-175,
-166,
-157,
-213,
-167,
-220,
-204,
-200,
-248,
-133,
-171,
-146,
-27,
-26,
-213,
-70,
-24,
-110,
-5,
-34,
-166,
-61,
-34,
-162,
-66,
-49,
-38,
-47,
-165,
-137,
-34,
-76,
-28,
-26,
-239,
-209,
-23,
-121,
-84,
-216,
-159,
-233,
-111,
-27,
-231,
-221,
-183,
-36,
-109,
-187,
-250,
-42,
-85,
-90,
-18,
-127,
-197,
-244,
-76,
-125,
-110,
-51,
-145,
-176,
-213,
-107,
-33,
-194,
-173,
-109,
-68,
-98,
-155,
-246,
-186,
-241,
-68,
-115,
-250,
-239,
-102,
-36,
-90,
-2,
-216,
-22,
-184,
-82,
-235,
-102,
-81,
-51,
-1,
-129,
-159,
-43,
-237,
-115,
-157,
-50,
-155,
-29,
-168,
-117,
-64,
-78,
-95,
-255,
-2,
-218,
-44,
-129,
-132,
-41,
-3,
-143,
-179,
-15,
-240,
-45,
-173,
-187,
-145,
-62,
-182,
-126,
-68,
-76,
-123,
-68,
-68,
-133,
-98,
-100,
-94,
-49,
-5,
-147,
-157,
-180,
-251,
-123,
-234,
-172,
-119,
-217,
-221,
-232,
-215,
-141,
-138,
-44,
-49,
-180,
-95,
-125,
-61,
-94,
-82,
-223,
-215,
-113,
-174,
-53,
-73,
-221,
-25,
-216,
-91,
-255,
-127,
-98,
-72,
-219,
-2,
-159,
-165,
-16,
-129,
-4,
-240,
-178,
-22,
-237,
-45,
-236,
-49,
-224,
-181,
-228,
-46,
-211,
-32,
-199,
-203,
-175,
-8,
-224,
-115,
-62,
-98,
-155,
-146,
-57,
-101,
-246,
-196,
-230,
-13,
-77,
-251,
-229,
-233,
-95,
-19,
-1,
-176,
-151,
-182,
-185,
-180,
-164,
-126,
-37,
-36,
-38,
-39,
-120,
-18,
-157,
-116,
-6,
-17,
-211,
-30,
-17,
-113,
-73,
-27,
-153,
-87,
-76,
-97,
-226,
-157,
-180,
-200,
-215,
-209,
-166,
-198,
-122,
-171,
-83,
-110,
-245,
-1,
-143,
-122,
-120,
-181,
-93,
-125,
-45,
-240,
-212,
-245,
-118,
-156,
-75,
-158,
-24,
-227,
-120,
-224,
-239,
-250,
-255,
-70,
-19,
-5,
-57,
-86,
-180,
-167,
-10,
-23,
-52,
-105,
-235,
-240,
-40,
-98,
-54,
-114,
-234,
-114,
-38,
-240,
-113,
-2,
-35,
-24,
-33,
-123,
-253,
-127,
-23,
-202,
-78,
-80,
-158,
-149,
-227,
-35,
-176,
-127,
-65,
-2,
-0,
-153,
-220,
-86,
-89,
-92,
-149,
-233,
-200,
-206,
-185,
-135,
-136,
-173,
-240,
-38,
-98,
-218,
-35,
-34,
-42,
-20,
-35,
-243,
-138,
-41,
-76,
-188,
-147,
-150,
-252,
-216,
-230,
-228,
-66,
-121,
-213,
-150,
-169,
-237,
-234,
-107,
-154,
-167,
-174,
-183,
-227,
-92,
-68,
-184,
-216,
-112,
-241,
-179,
-144,
-175,
-120,
-169,
-128,
-113,
-218,
-173,
-132,
-40,
-64,
-175,
-32,
-183,
-41,
-120,
-130,
-0,
-15,
-199,
-132,
-126,
-225,
-186,
-93,
-254,
-69,
-255,
-190,
-57,
-100,
-224,
-24,
-99,
-172,
-239,
-243,
-233,
-30,
-18,
-235,
-86,
-121,
-119,
-96,
-63,
-108,
-128,
-145,
-103,
-246,
-204,
-203,
-122,
-118,
-133,
-106,
-120,
-175,
-212,
-191,
-62,
-11,
-193,
-233,
-250,
-247,
-233,
-182,
-0,
-241,
-28,
-219,
-215,
-24,
-243,
-152,
-49,
-166,
-184,
-135,
-92,
-174,
-208,
-206,
-197,
-3,
-250,
-119,
-221,
-192,
-126,
-217,
-120,
-241,
-119,
-121,
-234,
-172,
-146,
-241,
-156,
-44,
-203,
-30,
-242,
-212,
-63,
-133,
-44,
-203,
-30,
-53,
-198,
-88,
-251,
-251,
-218,
-19,
-133,
-44,
-203,
-230,
-24,
-99,
-78,
-49,
-198,
-172,
-105,
-228,
-190,
-79,
-200,
-178,
-44,
-228,
-136,
-108,
-145,
-49,
-102,
-101,
-99,
-204,
-22,
-198,
-152,
-101,
-140,
-140,
-181,
-151,
-100,
-89,
-118,
-71,
-64,
-219,
-132,
-30,
-225,
-10,
-128,
-51,
-140,
-76,
-178,
-101,
-141,
-49,
-135,
-213,
-180,
-251,
-137,
-145,
-1,
-125,
-179,
-49,
-230,
-175,
-158,
-122,
-155,
-81,
-40,
-200,
-218,
-204,
-24,
-51,
-75,
-255,
-250,
-194,
-28,
-197,
-228,
-21,
-83,
-152,
-216,
-201,
-181,
-142,
-49,
-198,
-32,
-75,
-232,
-99,
-140,
-49,
-75,
-26,
-99,
-246,
-203,
-178,
-172,
-24,
-74,
-202,
-78,
-218,
-251,
-61,
-188,
-174,
-214,
-191,
-161,
-89,
-100,
-172,
-64,
-242,
-153,
-81,
-199,
-20,
-114,
-3,
-200,
-178,
-236,
-19,
-89,
-142,
-111,
-7,
-182,
-153,
-145,
-101,
-217,
-58,
-89,
-150,
-45,
-147,
-101,
-217,
-74,
-89,
-150,
-189,
-61,
-203,
-178,
-59,
-3,
-251,
-151,
-208,
-35,
-158,
-18,
-0,
-89,
-150,
-205,
-53,
-198,
-124,
-220,
-24,
-179,
-208,
-24,
-179,
-43,
-21,
-105,
-143,
-140,
-49,
-187,
-25,
-99,
-230,
-26,
-99,
-62,
-146,
-101,
-217,
-66,
-15,
-223,
-25,
-250,
-119,
-57,
-79,
-157,
-15,
-85,
-95,
-199,
-152,
-188,
-98,
-10,
-19,
-59,
-249,
-236,
-196,
-217,
-215,
-200,
-228,
-59,
-47,
-203,
-50,
-95,
-54,
-88,
-75,
-119,
-165,
-167,
-174,
-237,
-234,
-107,
-192,
-175,
-222,
-196,
-21,
-114,
-9,
-139,
-57,
-70,
-68,
-94,
-201,
-178,
-236,
-28,
-99,
-204,
-123,
-141,
-49,
-79,
-24,
-99,
-118,
-49,
-198,
-76,
-193,
-73,
-123,
-100,
-100,
-201,
-185,
-171,
-49,
-230,
-65,
-99,
-204,
-142,
-89,
-150,
-149,
-125,
-101,
-218,
-46,
-105,
-125,
-95,
-199,
-152,
-188,
-98,
-10,
-147,
-127,
-232,
-223,
-29,
-16,
-141,
-243,
-119,
-140,
-8,
-150,
-189,
-139,
-132,
-1,
-91,
-166,
-54,
-171,
-175,
-27,
-77,
-46,
-56,
-92,
-196,
-20,
-114,
-227,
-2,
-136,
-157,
-197,
-41,
-148,
-28,
-143,
-33,
-217,
-129,
-254,
-136,
-39,
-100,
-151,
-214,
-23,
-209,
-58,
-200,
-40,
-18,
-196,
-117,
-111,
-224,
-31,
-192,
-125,
-136,
-237,
-197,
-52,
-228,
-136,
-239,
-135,
-56,
-14,
-113,
-37,
-237,
-59,
-185,
-168,
-211,
-209,
-249,
-203,
-50,
-233,
-148,
-246,
-136,
-184,
-10,
-197,
-152,
-188,
-98,
-158,
-116,
-44,
-67,
-158,
-196,
-209,
-42,
-210,
-6,
-142,
-4,
-149,
-182,
-54,
-83,
-44,
-205,
-146,
-78,
-206,
-162,
-228,
-152,
-139,
-136,
-199,
-185,
-227,
-5,
-58,
-232,
-209,
-231,
-252,
-101,
-224,
-6,
-125,
-39,
-147,
-145,
-99,
-189,
-83,
-181,
-222,
-39,
-48,
-125,
-199,
-138,
-173,
-130,
-140,
-34,
-169,
-207,
-111,
-119,
-218,
-77,
-69,
-20,
-159,
-55,
-146,
-91,
-38,
-86,
-230,
-112,
-164,
-163,
-139,
-58,
-29,
-157,
-191,
-162,
-128,
-136,
-121,
-212,
-35,
-243,
-138,
-38,
-76,
-180,
-126,
-123,
-231,
-197,
-78,
-7,
-54,
-45,
-212,
-55,
-202,
-20,
-75,
-120,
-210,
-201,
-109,
-43,
-120,
-68,
-19,
-114,
-49,
-64,
-216,
-241,
-226,
-234,
-192,
-33,
-136,
-127,
-195,
-92,
-202,
-225,
-245,
-55,
-64,
-162,
-232,
-252,
-202,
-161,
-187,
-29,
-17,
-162,
-147,
-157,
-178,
-83,
-40,
-113,
-55,
-118,
-104,
-90,
-7,
-25,
-5,
-94,
-64,
-46,
-124,
-255,
-11,
-108,
-83,
-168,
-95,
-11,
-137,
-16,
-84,
-155,
-48,
-148,
-14,
-46,
-234,
-68,
-112,
-254,
-234,
-12,
-34,
-230,
-81,
-143,
-204,
-43,
-154,
-48,
-81,
-154,
-103,
-146,
-219,
-3,
-128,
-56,
-108,
-220,
-197,
-160,
-197,
-93,
-112,
-166,
-88,
-6,
-87,
-95,
-11,
-201,
-133,
-204,
-69,
-212,
-251,
-40,
-68,
-21,
-114,
-129,
-60,
-90,
-135,
-144,
-215,
-251,
-181,
-95,
-205,
-219,
-129,
-99,
-128,
-163,
-129,
-91,
-157,
-231,
-119,
-55,
-226,
-33,
-87,
-122,
-239,
-228,
-246,
-242,
-199,
-49,
-210,
-97,
-201,
-90,
-28,
-126,
-161,
-162,
-173,
-69,
-171,
-32,
-163,
-74,
-99,
-45,
-16,
-175,
-37,
-66,
-204,
-126,
-58,
-186,
-168,
-143,
-58,
-136,
-152,
-71,
-61,
-22,
-47,
-34,
-10,
-19,
-165,
-57,
-70,
-105,
-14,
-71,
-246,
-138,
-255,
-209,
-151,
-22,
-37,
-83,
-172,
-115,
-29,
-43,
-184,
-102,
-2,
-149,
-186,
-16,
-34,
-11,
-185,
-192,
-254,
-29,
-71,
-203,
-16,
-242,
-192,
-79,
-245,
-250,
-255,
-194,
-49,
-224,
-65,
-246,
-237,
-103,
-107,
-93,
-109,
-48,
-76,
-100,
-245,
-48,
-3,
-88,
-190,
-80,
-158,
-33,
-1,
-54,
-7,
-220,
-130,
-29,
-26,
-139,
-86,
-65,
-70,
-201,
-45,
-63,
-33,
-32,
-136,
-232,
-132,
-1,
-17,
-243,
-168,
-199,
-226,
-69,
-92,
-193,
-180,
-2,
-146,
-66,
-189,
-215,
-220,
-121,
-200,
-18,
-215,
-46,
-103,
-47,
-5,
-158,
-94,
-65,
-27,
-85,
-200,
-5,
-246,
-175,
-117,
-8,
-121,
-114,
-147,
-224,
-29,
-61,
-117,
-111,
-214,
-186,
-255,
-5,
-244,
-97,
-54,
-37,
-201,
-57,
-144,
-85,
-209,
-204,
-138,
-182,
-22,
-173,
-130,
-140,
-34,
-43,
-22,
-40,
-49,
-227,
-29,
-22,
-144,
-109,
-198,
-65,
-136,
-192,
-155,
-129,
-8,
-247,
-255,
-34,
-65,
-74,
-26,
-101,
-75,
-50,
-192,
-243,
-145,
-101,
-213,
-165,
-200,
-87,
-109,
-46,
-98,
-111,
-125,
-35,
-98,
-6,
-250,
-57,
-28,
-39,
-138,
-10,
-62,
-209,
-242,
-168,
-199,
-226,
-69,
-68,
-193,
-52,
-44,
-32,
-49,
-16,
-173,
-91,
-241,
-5,
-84,
-36,
-153,
-32,
-162,
-144,
-11,
-236,
-91,
-107,
-231,
-20,
-242,
-61,
-255,
-102,
-158,
-186,
-205,
-180,
-110,
-150,
-175,
-109,
-129,
-246,
-118,
-68,
-219,
-62,
-32,
-28,
-17,
-13,
-252,
-109,
-21,
-109,
-67,
-4,
-64,
-105,
-144,
-81,
-157,
-112,
-0,
-223,
-173,
-235,
-103,
-9,
-239,
-5,
-250,
-111,
-243,
-10,
-154,
-45,
-108,
-39,
-75,
-234,
-95,
-69,
-110,
-101,
-57,
-11,
-9,
-117,
-126,
-13,
-249,
-202,
-229,
-30,
-42,
-2,
-210,
-184,
-140,
-150,
-71,
-150,
-116,
-118,
-223,
-185,
-0,
-89,
-94,
-93,
-142,
-104,
-87,
-109,
-148,
-88,
-232,
-224,
-49,
-53,
-218,
-32,
-162,
-96,
-26,
-22,
-16,
-51,
-95,
-27,
-101,
-168,
-50,
-172,
-58,
-227,
-68,
-200,
-145,
-231,
-196,
-27,
-176,
-68,
-36,
-79,
-206,
-225,
-59,
-210,
-45,
-210,
-218,
-40,
-58,
-199,
-48,
-210,
-149,
-214,
-78,
-220,
-210,
-12,
-63,
-206,
-243,
-168,
-18,
-0,
-165,
-65,
-70,
-157,
-231,
-252,
-209,
-186,
-126,
-118,
-184,
-126,
-169,
-0,
-64,
-20,
-125,
-246,
-68,
-234,
-199,
-56,
-66,
-16,
-153,
-207,
-214,
-235,
-241,
-14,
-10,
-91,
-164,
-34,
-163,
-21,
-201,
-195,
-33,
-61,
-12,
-124,
-134,
-194,
-210,
-1,
-89,
-98,
-110,
-143,
-68,
-62,
-45,
-93,
-138,
-38,
-244,
-3,
-96,
-23,
-2,
-61,
-240,
-232,
-81,
-200,
-21,
-132,
-72,
-41,
-159,
-170,
-129,
-171,
-245,
-214,
-49,
-104,
-50,
-78,
-0,
-77,
-36,
-169,
-198,
-127,
-237,
-164,
-174,
-233,
-75,
-230,
-240,
-1,
-249,
-96,
-157,
-236,
-180,
-135,
-138,
-175,
-115,
-224,
-4,
-44,
-13,
-50,
-74,
-67,
-103,
-174,
-150,
-215,
-175,
-18,
-0,
-214,
-255,
-165,
-204,
-147,
-48,
-67,
-20,
-211,
-0,
-159,
-173,
-234,
-136,
-117,
-150,
-185,
-135,
-228,
-168,
-145,
-80,
-1,
-70,
-162,
-42,
-225,
-103,
-157,
-0,
-88,
-93,
-39,
-44,
-136,
-128,
-186,
-65,
-133,
-129,
-221,
-26,
-76,
-193,
-73,
-217,
-85,
-194,
-227,
-56,
-165,
-253,
-13,
-162,
-140,
-189,
-17,
-89,
-6,
-223,
-164,
-191,
-109,
-236,
-127,
-239,
-185,
-122,
-224,
-4,
-252,
-168,
-210,
-12,
-4,
-25,
-37,
-63,
-254,
-251,
-112,
-85,
-63,
-43,
-120,
-119,
-21,
-0,
-151,
-106,
-213,
-167,
-42,
-218,
-127,
-86,
-105,
-46,
-40,
-35,
-120,
-137,
-211,
-145,
-237,
-189,
-68,
-9,
-9,
-10,
-29,
-39,
-139,
-16,
-101,
-211,
-92,
-10,
-49,
-7,
-28,
-186,
-74,
-1,
-160,
-52,
-171,
-146,
-123,
-125,
-46,
-64,
-244,
-18,
-55,
-35,
-231,
-230,
-171,
-5,
-244,
-229,
-173,
-192,
-31,
-40,
-183,
-4,
-92,
-22,
-73,
-178,
-234,
-141,
-242,
-27,
-56,
-1,
-171,
-130,
-140,
-90,
-219,
-139,
-218,
-4,
-174,
-29,
-174,
-95,
-37,
-0,
-108,
-44,
-132,
-82,
-215,
-108,
-224,
-141,
-101,
-2,
-204,
-18,
-28,
-166,
-4,
-215,
-182,
-185,
-137,
-16,
-208,
-33,
-149,
-23,
-49,
-76,
-28,
-19,
-162,
-193,
-25,
-180,
-223,
-210,
-191,
-139,
-86,
-90,
-155,
-0,
-0,
-32,
-0,
-73,
-68,
-65,
-84,
-222,
-140,
-196,
-129,
-2,
-224,
-215,
-136,
-206,
-105,
-167,
-254,
-122,
-92,
-142,
-192,
-9,
-88,
-21,
-100,
-212,
-218,
-94,
-84,
-166,
-18,
-239,
-120,
-253,
-42,
-1,
-96,
-149,
-195,
-85,
-39,
-86,
-175,
-84,
-154,
-1,
-119,
-116,
-75,
-96,
-247,
-254,
-223,
-111,
-115,
-19,
-33,
-160,
-67,
-42,
-47,
-122,
-48,
-113,
-108,
-34,
-144,
-234,
-94,
-18,
-178,
-207,
-178,
-3,
-225,
-34,
-156,
-56,
-243,
-139,
-35,
-156,
-231,
-241,
-108,
-242,
-0,
-40,
-3,
-105,
-189,
-3,
-5,
-192,
-44,
-228,
-200,
-106,
-83,
-106,
-50,
-3,
-247,
-129,
-128,
-119,
-91,
-25,
-100,
-20,
-120,
-175,
-214,
-45,
-4,
-158,
-23,
-251,
-250,
-74,
-83,
-37,
-0,
-108,
-164,
-160,
-170,
-24,
-150,
-219,
-41,
-205,
-64,
-64,
-154,
-34,
-147,
-221,
-155,
-222,
-64,
-8,
-232,
-152,
-202,
-139,
-30,
-76,
-28,
-105,
-32,
-144,
-2,
-6,
-137,
-53,
-104,
-249,
-175,
-237,
-99,
-5,
-143,
-78,
-138,
-51,
-15,
-253,
-174,
-74,
-94,
-155,
-89,
-55,
-128,
-215,
-55,
-156,
-126,
-174,
-82,
-65,
-103,
-177,
-62,
-185,
-130,
-236,
-6,
-10,
-203,
-240,
-144,
-123,
-97,
-164,
-25,
-175,
-139,
-25,
-136,
-113,
-85,
-109,
-134,
-223,
-46,
-168,
-122,
-183,
-4,
-4,
-25,
-69,
-140,
-150,
-172,
-53,
-227,
-165,
-64,
-168,
-179,
-89,
-237,
-245,
-29,
-154,
-42,
-1,
-112,
-145,
-86,
-149,
-42,
-248,
-144,
-99,
-123,
-128,
-139,
-203,
-8,
-236,
-50,
-194,
-171,
-201,
-44,
-121,
-65,
-193,
-241,
-210,
-137,
-148,
-202,
-43,
-22,
-104,
-40,
-144,
-106,
-6,
-137,
-53,
-55,
-189,
-3,
-40,
-117,
-171,
-45,
-60,
-186,
-214,
-138,
-179,
-2,
-173,
-59,
-248,
-158,
-164,
-194,
-62,
-32,
-128,
-151,
-107,
-107,
-0,
-97,
-2,
-96,
-83,
-253,
-125,
-158,
-254,
-254,
-92,
-129,
-174,
-78,
-9,
-248,
-2,
-68,
-121,
-103,
-147,
-97,
-62,
-162,
-255,
-30,
-103,
-100,
-98,
-149,
-248,
-113,
-240,
-6,
-239,
-165,
-117,
-144,
-81,
-36,
-196,
-184,
-85,
-92,
-94,
-173,
-109,
-151,
-40,
-208,
-108,
-4,
-236,
-27,
-114,
-125,
-15,
-77,
-149,
-0,
-176,
-153,
-150,
-188,
-222,
-185,
-140,
-60,
-5,
-248,
-82,
-217,
-5,
-172,
-34,
-225,
-35,
-37,
-245,
-151,
-56,
-255,
-174,
-45,
-235,
-204,
-120,
-1,
-13,
-5,
-82,
-217,
-75,
-34,
-15,
-142,
-249,
-32,
-37,
-138,
-176,
-2,
-143,
-40,
-138,
-51,
-135,
-246,
-51,
-74,
-106,
-99,
-244,
-253,
-40,
-228,
-126,
-60,
-124,
-150,
-67,
-180,
-230,
-247,
-146,
-159,
-41,
-135,
-8,
-128,
-23,
-235,
-239,
-23,
-35,
-10,
-188,
-199,
-113,
-194,
-164,
-215,
-12,
-220,
-237,
-17,
-205,
-255,
-21,
-192,
-11,
-61,
-245,
-43,
-146,
-39,
-30,
-237,
-197,
-89,
-169,
-112,
-47,
-93,
-131,
-140,
-190,
-9,
-57,
-102,
-181,
-120,
-20,
-49,
-198,
-185,
-66,
-199,
-90,
-217,
-115,
-240,
-142,
-173,
-2,
-77,
-213,
-115,
-92,
-158,
-252,
-35,
-240,
-115,
-156,
-179,
-126,
-125,
-175,
-246,
-136,
-244,
-94,
-74,
-86,
-167,
-6,
-152,
-164,
-68,
-7,
-4,
-220,
-104,
-163,
-37,
-106,
-27,
-32,
-103,
-216,
-118,
-223,
-95,
-154,
-117,
-6,
-217,
-159,
-129,
-76,
-168,
-129,
-248,
-252,
-17,
-251,
-51,
-240,
-146,
-144,
-189,
-223,
-66,
-228,
-203,
-235,
-205,
-250,
-83,
-194,
-227,
-91,
-250,
-183,
-181,
-226,
-76,
-233,
-86,
-68,
-4,
-207,
-253,
-200,
-82,
-245,
-106,
-228,
-75,
-245,
-172,
-22,
-247,
-247,
-11,
-189,
-228,
-62,
-206,
-96,
-13,
-17,
-0,
-238,
-243,
-56,
-82,
-203,
-142,
-114,
-202,
-170,
-6,
-174,
-61,
-254,
-219,
-164,
-226,
-58,
-171,
-42,
-77,
-111,
-238,
-172,
-12,
-162,
-85,
-144,
-81,
-229,
-181,
-18,
-226,
-130,
-124,
-62,
-178,
-181,
-156,
-175,
-255,
-30,
-68,
-44,
-56,
-7,
-188,
-52,
-125,
-207,
-210,
-67,
-83,
-183,
-146,
-122,
-33,
-185,
-11,
-243,
-108,
-68,
-240,
-216,
-241,
-128,
-94,
-191,
-60,
-2,
-51,
-249,
-126,
-216,
-191,
-71,
-8,
-236,
-12,
-121,
-134,
-84,
-128,
-15,
-86,
-240,
-120,
-151,
-210,
-60,
-89,
-54,
-208,
-200,
-205,
-85,
-171,
-150,
-203,
-7,
-41,
-205,
-169,
-101,
-52,
-49,
-80,
-124,
-73,
-136,
-82,
-101,
-46,
-34,
-164,
-182,
-109,
-200,
-163,
-179,
-226,
-76,
-233,
-236,
-209,
-212,
-167,
-244,
-247,
-219,
-245,
-247,
-145,
-13,
-239,
-109,
-7,
-109,
-247,
-111,
-196,
-239,
-192,
-42,
-70,
-155,
-10,
-128,
-53,
-144,
-21,
-192,
-83,
-102,
-173,
-53,
-99,
-197,
-46,
-153,
-253,
-95,
-37,
-243,
-212,
-22,
-1,
-96,
-177,
-141,
-31,
-232,
-60,
-239,
-82,
-171,
-76,
-224,
-13,
-74,
-227,
-11,
-78,
-99,
-105,
-86,
-65,
-236,
-254,
-255,
-139,
-40,
-85,
-103,
-34,
-102,
-202,
-223,
-163,
-238,
-227,
-232,
-92,
-0,
-106,
-150,
-197,
-117,
-3,
-148,
-60,
-67,
-170,
-215,
-57,
-67,
-105,
-46,
-86,
-154,
-3,
-43,
-104,
-182,
-81,
-26,
-111,
-230,
-89,
-100,
-159,
-102,
-67,
-96,
-15,
-76,
-36,
-165,
-137,
-178,
-146,
-112,
-7,
-60,
-226,
-170,
-57,
-29,
-249,
-250,
-15,
-36,
-113,
-168,
-184,
-134,
-69,
-12,
-197,
-217,
-154,
-218,
-135,
-187,
-112,
-252,
-220,
-17,
-201,
-63,
-143,
-64,
-67,
-46,
-196,
-16,
-231,
-126,
-229,
-181,
-161,
-150,
-89,
-115,
-239,
-70,
-2,
-64,
-203,
-173,
-178,
-233,
-124,
-253,
-253,
-162,
-178,
-123,
-33,
-223,
-95,
-123,
-93,
-117,
-145,
-252,
-5,
-151,
-43,
-205,
-183,
-67,
-238,
-103,
-60,
-130,
-124,
-245,
-253,
-241,
-10,
-26,
-155,
-197,
-168,
-116,
-78,
-197,
-232,
-200,
-191,
-157,
-65,
-89,
-234,
-74,
-90,
-55,
-64,
-25,
-185,
-10,
-24,
-8,
-195,
-68,
-158,
-108,
-114,
-58,
-53,
-74,
-43,
-100,
-95,
-10,
-254,
-0,
-31,
-246,
-203,
-53,
-133,
-138,
-227,
-35,
-34,
-172,
-36,
-156,
-1,
-191,
-3,
-185,
-208,
-241,
-133,
-247,
-170,
-186,
-23,
-139,
-78,
-138,
-51,
-165,
-177,
-246,
-221,
-123,
-22,
-202,
-173,
-253,
-124,
-80,
-54,
-89,
-114,
-47,
-183,
-61,
-156,
-50,
-171,
-124,
-107,
-35,
-0,
-150,
-210,
-241,
-3,
-240,
-30,
-36,
-194,
-141,
-247,
-94,
-16,
-197,
-153,
-93,
-162,
-254,
-27,
-217,
-171,
-254,
-16,
-248,
-37,
-162,
-217,
-182,
-10,
-201,
-83,
-41,
-9,
-230,
-177,
-56,
-128,
-60,
-148,
-252,
-85,
-248,
-79,
-25,
-50,
-242,
-172,
-65,
-223,
-235,
-179,
-35,
-27,
-146,
-31,
-7,
-222,
-141,
-56,
-64,
-12,
-228,
-109,
-67,
-60,
-143,
-234,
-6,
-168,
-93,
-5,
-252,
-203,
-83,
-103,
-39,
-100,
-173,
-194,
-10,
-248,
-130,
-210,
-14,
-4,
-192,
-68,
-60,
-221,
-160,
-230,
-248,
-139,
-56,
-43,
-9,
-139,
-251,
-116,
-130,
-44,
-212,
-191,
-193,
-137,
-49,
-28,
-30,
-173,
-21,
-103,
-90,
-191,
-33,
-242,
-149,
-190,
-181,
-120,
-63,
-206,
-96,
-89,
-72,
-141,
-247,
-23,
-185,
-121,
-235,
-31,
-11,
-229,
-22,
-141,
-5,
-128,
-214,
-89,
-55,
-222,
-41,
-228,
-57,
-0,
-203,
-238,
-101,
-19,
-100,
-251,
-121,
-51,
-34,
-12,
-22,
-32,
-219,
-194,
-73,
-136,
-99,
-207,
-152,
-241,
-200,
-236,
-11,
-136,
-223,
-131,
-13,
-44,
-115,
-56,
-206,
-156,
-67,
-244,
-60,
-86,
-183,
-242,
-8,
-53,
-102,
-209,
-49,
-58,
-243,
-60,
-242,
-37,
-9,
-58,
-208,
-110,
-38,
-207,
-229,
-126,
-47,
-206,
-241,
-76,
-5,
-31,
-119,
-21,
-176,
-165,
-83,
-190,
-14,
-34,
-217,
-103,
-81,
-227,
-43,
-174,
-244,
-171,
-35,
-75,
-248,
-249,
-56,
-171,
-18,
-68,
-57,
-52,
-71,
-7,
-250,
-122,
-1,
-124,
-58,
-173,
-36,
-156,
-231,
-49,
-21,
-73,
-189,
-101,
-87,
-12,
-247,
-16,
-232,
-107,
-237,
-155,
-52,
-52,
-84,
-156,
-105,
-189,
-141,
-80,
-227,
-77,
-37,
-134,
-228,
-5,
-4,
-143,
-208,
-116,
-104,
-214,
-215,
-65,
-119,
-63,
-133,
-85,
-152,
-211,
-207,
-86,
-2,
-64,
-235,
-255,
-162,
-245,
-7,
-215,
-141,
-149,
-97,
-129,
-6,
-49,
-15,
-128,
-79,
-59,
-247,
-88,
-233,
-121,
-25,
-3,
-58,
-166,
-108,
-112,
-22,
-87,
-137,
-103,
-221,
-121,
-31,
-167,
-16,
-102,
-172,
-207,
-206,
-44,
-1,
-188,
-31,
-89,
-122,
-77,
-33,
-151,
-204,
-51,
-144,
-227,
-134,
-115,
-244,
-197,
-122,
-163,
-172,
-58,
-124,
-236,
-42,
-192,
-205,
-40,
-107,
-7,
-196,
-79,
-27,
-244,
-231,
-183,
-218,
-102,
-63,
-167,
-236,
-83,
-90,
-118,
-118,
-85,
-91,
-135,
-190,
-211,
-74,
-194,
-25,
-12,
-175,
-210,
-223,
-75,
-147,
-71,
-112,
-13,
-50,
-70,
-242,
-77,
-26,
-154,
-43,
-206,
-182,
-68,
-4,
-240,
-13,
-148,
-11,
-171,
-140,
-92,
-136,
-15,
-28,
-95,
-233,
-251,
-189,
-72,
-249,
-248,
-148,
-144,
-22,
-93,
-4,
-192,
-198,
-228,
-74,
-82,
-239,
-189,
-244,
-1,
-58,
-132,
-39,
-115,
-120,
-188,
-72,
-199,
-250,
-52,
-253,
-55,
-3,
-120,
-65,
-191,
-61,
-55,
-6,
-217,
-46,
-253,
-28,
-57,
-29,
-153,
-173,
-215,
-189,
-30,
-217,
-34,
-4,
-127,
-249,
-233,
-24,
-89,
-56,
-26,
-200,
-87,
-1,
-11,
-144,
-101,
-235,
-242,
-200,
-249,
-234,
-28,
-224,
-217,
-13,
-248,
-216,
-60,
-238,
-215,
-59,
-101,
-246,
-38,
-223,
-27,
-200,
-163,
-211,
-74,
-162,
-100,
-242,
-110,
-66,
-46,
-181,
-107,
-61,
-193,
-202,
-38,
-13,
-205,
-20,
-103,
-255,
-12,
-185,
-111,
-100,
-255,
-13,
-112,
-174,
-167,
-206,
-250,
-200,
-123,
-67,
-144,
-59,
-253,
-108,
-45,
-0,
-148,
-230,
-71,
-14,
-221,
-176,
-4,
-192,
-113,
-180,
-12,
-79,
-166,
-237,
-215,
-66,
-162,
-20,
-77,
-3,
-94,
-13,
-188,
-14,
-153,
-136,
-119,
-16,
-176,
-98,
-29,
-11,
-160,
-99,
-100,
-225,
-216,
-157,
-177,
-171,
-128,
-35,
-200,
-151,
-85,
-71,
-213,
-183,
-28,
-224,
-99,
-61,
-174,
-94,
-138,
-216,
-140,
-131,
-196,
-44,
-8,
-86,
-14,
-209,
-97,
-37,
-81,
-54,
-224,
-201,
-181,
-179,
-79,
-226,
-248,
-179,
-55,
-228,
-17,
-172,
-56,
-139,
-1,
-242,
-227,
-183,
-91,
-116,
-114,
-20,
-255,
-89,
-88,
-215,
-92,
-95,
-88,
-116,
-239,
-189,
-20,
-104,
-86,
-162,
-194,
-0,
-166,
-15,
-208,
-33,
-60,
-153,
-182,
-185,
-10,
-89,
-245,
-190,
-216,
-41,
-123,
-41,
-114,
-218,
-226,
-203,
-196,
-52,
-38,
-65,
-135,
-200,
-194,
-177,
-59,
-98,
-87,
-1,
-179,
-244,
-193,
-206,
-175,
-155,
-40,
-37,
-124,
-172,
-185,
-227,
-129,
-228,
-246,
-234,
-63,
-110,
-200,
-163,
-245,
-74,
-162,
-106,
-192,
-3,
-103,
-105,
-221,
-37,
-84,
-236,
-51,
-107,
-120,
-4,
-43,
-206,
-186,
-130,
-22,
-232,
-163,
-31,
-37,
-125,
-235,
-20,
-215,
-142,
-14,
-225,
-201,
-22,
-55,
-48,
-86,
-34,
-11,
-147,
-175,
-2,
-0,
-126,
-221,
-146,
-199,
-106,
-200,
-190,
-200,
-42,
-35,
-161,
-197,
-190,
-140,
-150,
-43,
-137,
-154,
-201,
-187,
-38,
-249,
-146,
-235,
-235,
-109,
-120,
-104,
-253,
-152,
-80,
-156,
-57,
-253,
-44,
-221,
-2,
-244,
-116,
-221,
-56,
-113,
-237,
-18,
-226,
-129,
-14,
-62,
-252,
-14,
-143,
-85,
-156,
-65,
-245,
-234,
-14,
-125,
-57,
-17,
-57,
-149,
-88,
-4,
-92,
-214,
-146,
-71,
-171,
-149,
-68,
-192,
-228,
-125,
-155,
-214,
-207,
-167,
-100,
-201,
-21,
-192,
-99,
-84,
-20,
-103,
-158,
-126,
-12,
-93,
-0,
-16,
-41,
-174,
-29,
-221,
-87,
-16,
-209,
-34,
-235,
-34,
-167,
-103,
-181,
-122,
-135,
-26,
-30,
-157,
-231,
-95,
-39,
-208,
-193,
-135,
-191,
-192,
-167,
-114,
-240,
-7,
-242,
-216,
-202,
-225,
-243,
-177,
-150,
-60,
-90,
-173,
-36,
-66,
-250,
-143,
-232,
-57,
-64,
-246,
-214,
-3,
-131,
-52,
-144,
-199,
-208,
-21,
-103,
-158,
-62,
-140,
-134,
-0,
-232,
-28,
-215,
-142,
-142,
-43,
-136,
-174,
-237,
-61,
-252,
-126,
-5,
-124,
-38,
-148,
-190,
-132,
-71,
-163,
-249,
-71,
-228,
-208,
-224,
-157,
-124,
-248,
-11,
-188,
-106,
-7,
-127,
-0,
-15,
-187,
-55,
-158,
-142,
-199,
-64,
-169,
-1,
-159,
-206,
-43,
-137,
-150,
-215,
-13,
-17,
-0,
-67,
-87,
-156,
-121,
-250,
-208,
-73,
-0,
-208,
-34,
-65,
-38,
-29,
-227,
-218,
-209,
-113,
-5,
-209,
-181,
-125,
-31,
-104,
-58,
-255,
-136,
-189,
-133,
-34,
-162,
-15,
-127,
-36,
-1,
-96,
-253,
-238,
-143,
-237,
-216,
-151,
-206,
-43,
-137,
-4,
-63,
-104,
-153,
-32,
-147,
-142,
-113,
-237,
-232,
-184,
-130,
-232,
-218,
-190,
-15,
-52,
-153,
-127,
-99,
-81,
-128,
-21,
-59,
-216,
-73,
-0,
-32,
-71,
-101,
-119,
-42,
-143,
-114,
-183,
-198,
-48,
-94,
-81,
-86,
-18,
-9,
-35,
-65,
-135,
-4,
-153,
-116,
-140,
-107,
-71,
-247,
-21,
-68,
-140,
-21,
-72,
-40,
-46,
-41,
-187,
-70,
-91,
-208,
-82,
-128,
-141,
-139,
-35,
-19,
-68,
-67,
-127,
-168,
-49,
-102,
-125,
-99,
-204,
-133,
-89,
-150,
-117,
-61,
-143,
-181,
-38,
-180,
-167,
-100,
-89,
-54,
-163,
-35,
-175,
-4,
-35,
-3,
-204,
-24,
-115,
-130,
-49,
-102,
-101,
-99,
-204,
-117,
-198,
-152,
-173,
-179,
-44,
-27,
-225,
-186,
-154,
-101,
-217,
-131,
-198,
-24,
-111,
-10,
-117,
-99,
-204,
-52,
-99,
-204,
-106,
-198,
-152,
-42,
-129,
-188,
-162,
-67,
-91,
-132,
-205,
-204,
-124,
-99,
-69,
-123,
-123,
-252,
-235,
-91,
-6,
-119,
-109,
-191,
-192,
-24,
-19,
-154,
-26,
-172,
-52,
-79,
-97,
-7,
-216,
-128,
-170,
-222,
-116,
-112,
-89,
-150,
-129,
-56,
-136,
-189,
-220,
-24,
-243,
-110,
-99,
-204,
-207,
-70,
-16,
-16,
-33,
-61,
-81,
-21,
-28,
-233,
-23,
-180,
-2,
-64,
-246,
-194,
-215,
-171,
-212,
-178,
-251,
-154,
-39,
-232,
-152,
-175,
-128,
-136,
-43,
-137,
-132,
-28,
-116,
-76,
-144,
-73,
-199,
-184,
-118,
-116,
-95,
-65,
-116,
-143,
-172,
-27,
-1,
-180,
-84,
-226,
-209,
-53,
-52,
-120,
-200,
-4,
-237,
-34,
-0,
-154,
-2,
-73,
-188,
-121,
-47,
-185,
-2,
-233,
-76,
-2,
-114,
-17,
-214,
-240,
-92,
-134,
-60,
-60,
-210,
-5,
-145,
-186,
-106,
-121,
-23,
-49,
-23,
-217,
-147,
-253,
-3,
-248,
-24,
-53,
-6,
-42,
-228,
-129,
-39,
-75,
-51,
-225,
-146,
-251,
-51,
-84,
-126,
-105,
-104,
-17,
-66,
-189,
-235,
-251,
-167,
-99,
-130,
-76,
-58,
-198,
-181,
-163,
-99,
-100,
-220,
-174,
-237,
-99,
-128,
-14,
-74,
-60,
-186,
-10,
-176,
-174,
-3,
-96,
-172,
-130,
-158,
-86,
-18,
-158,
-235,
-88,
-216,
-184,
-114,
-87,
-58,
-215,
-3,
-9,
-128,
-82,
-26,
-45,
-150,
-220,
-142,
-224,
-94,
-252,
-57,
-232,
-151,
-32,
-87,
-174,
-13,
-228,
-209,
-43,
-208,
-54,
-14,
-161,
-222,
-245,
-253,
-211,
-61,
-65,
-102,
-167,
-184,
-118,
-116,
-95,
-65,
-116,
-143,
-172,
-59,
-72,
-255,
-107,
-2,
-143,
-1,
-233,
-126,
-138,
-209,
-77,
-128,
-117,
-29,
-0,
-99,
-21,
-244,
-176,
-146,
-40,
-185,
-206,
-192,
-243,
-67,
-38,
-237,
-174,
-228,
-19,
-242,
-59,
-21,
-237,
-109,
-30,
-123,
-240,
-231,
-160,
-183,
-2,
-226,
-22,
-106,
-98,
-231,
-211,
-34,
-132,
-122,
-215,
-247,
-79,
-199,
-4,
-153,
-202,
-163,
-117,
-92,
-59,
-186,
-175,
-32,
-186,
-71,
-214,
-29,
-108,
-115,
-51,
-112,
-116,
-32,
-109,
-215,
-83,
-140,
-110,
-2,
-172,
-235,
-0,
-152,
-232,
-168,
-122,
-126,
-192,
-207,
-180,
-174,
-74,
-193,
-100,
-128,
-15,
-41,
-221,
-149,
-158,
-186,
-127,
-104,
-221,
-39,
-98,
-246,
-219,
-225,
-223,
-233,
-253,
-211,
-49,
-65,
-166,
-195,
-167,
-85,
-92,
-59,
-186,
-175,
-32,
-186,
-71,
-214,
-237,
-118,
-223,
-93,
-79,
-33,
-186,
-9,
-176,
-174,
-3,
-192,
-211,
-153,
-115,
-129,
-29,
-156,
-178,
-231,
-33,
-174,
-154,
-222,
-32,
-22,
-227,
-29,
-85,
-207,
-15,
-137,
-173,
-0,
-48,
-167,
-134,
-135,
-171,
-160,
-220,
-202,
-41,
-223,
-4,
-49,
-90,
-122,
-136,
-158,
-50,
-50,
-119,
-125,
-255,
-116,
-76,
-144,
-25,
-3,
-116,
-140,
-140,
-219,
-181,
-125,
-199,
-190,
-119,
-181,
-131,
-232,
-38,
-192,
-186,
-14,
-128,
-2,
-221,
-1,
-228,
-246,
-203,
-23,
-35,
-89,
-135,
-23,
-34,
-231,
-238,
-223,
-174,
-106,
-235,
-240,
-24,
-106,
-68,
-150,
-174,
-168,
-122,
-126,
-72,
-104,
-105,
-0,
-223,
-241,
-85,
-145,
-214,
-222,
-247,
-31,
-157,
-50,
-251,
-242,
-170,
-2,
-150,
-116,
-58,
-197,
-233,
-250,
-254,
-233,
-152,
-32,
-51,
-22,
-232,
-18,
-25,
-55,
-66,
-251,
-14,
-253,
-238,
-124,
-10,
-65,
-23,
-1,
-214,
-117,
-0,
-120,
-104,
-119,
-33,
-247,
-59,
-7,
-177,
-189,
-175,
-205,
-244,
-170,
-109,
-71,
-37,
-34,
-75,
-23,
-84,
-61,
-63,
-36,
-200,
-37,
-4,
-68,
-47,
-66,
-226,
-251,
-63,
-132,
-8,
-204,
-231,
-34,
-49,
-225,
-166,
-233,
-75,
-172,
-90,
-2,
-119,
-157,
-192,
-93,
-219,
-119,
-74,
-144,
-57,
-209,
-65,
-164,
-83,
-8,
-218,
-10,
-176,
-88,
-2,
-0,
-216,
-131,
-60,
-28,
-213,
-125,
-136,
-115,
-203,
-44,
-100,
-9,
-91,
-27,
-189,
-135,
-150,
-17,
-89,
-136,
-247,
-5,
-12,
-66,
-69,
-251,
-162,
-18,
-112,
-119,
-68,
-186,
-47,
-162,
-66,
-186,
-23,
-120,
-89,
-147,
-231,
-159,
-144,
-103,
-254,
-169,
-140,
-236,
-210,
-245,
-253,
-69,
-184,
-255,
-78,
-9,
-50,
-39,
-58,
-232,
-225,
-20,
-34,
-4,
-125,
-88,
-2,
-46,
-105,
-140,
-153,
-103,
-140,
-217,
-205,
-136,
-165,
-221,
-124,
-36,
-212,
-213,
-89,
-198,
-152,
-223,
-0,
-87,
-102,
-89,
-54,
-165,
-162,
-253,
-95,
-141,
-49,
-153,
-49,
-102,
-171,
-44,
-203,
-38,
-25,
-99,
-12,
-176,
-181,
-49,
-230,
-116,
-173,
-43,
-219,
-131,
-217,
-64,
-28,
-3,
-97,
-149,
-3,
-225,
-211,
-190,
-190,
-86,
-255,
-94,
-103,
-140,
-153,
-25,
-200,
-231,
-56,
-96,
-166,
-49,
-102,
-89,
-99,
-204,
-6,
-70,
-172,
-219,
-102,
-24,
-99,
-246,
-200,
-178,
-204,
-171,
-160,
-241,
-224,
-23,
-198,
-152,
-47,
-25,
-99,
-62,
-102,
-140,
-121,
-208,
-24,
-179,
-200,
-24,
-211,
-40,
-248,
-73,
-7,
-84,
-221,
-235,
-242,
-198,
-152,
-50,
-1,
-115,
-134,
-49,
-230,
-14,
-35,
-247,
-124,
-28,
-176,
-125,
-150,
-101,
-179,
-122,
-232,
-223,
-226,
-138,
-63,
-25,
-99,
-94,
-103,
-140,
-249,
-160,
-41,
-90,
-233,
-25,
-81,
-226,
-105,
-157,
-49,
-198,
-252,
-37,
-250,
-213,
-187,
-126,
-65,
-2,
-248,
-31,
-170,
-77,
-91,
-103,
-243,
-173,
-225,
-31,
-189,
-255,
-33,
-60,
-61,
-180,
-69,
-60,
-66,
-96,
-96,
-70,
-242,
-48,
-221,
-85,
-240,
-234,
-67,
-186,
-222,
-127,
-140,
-231,
-71,
-135,
-4,
-153,
-19,
-29,
-140,
-242,
-41,
-132,
-33,
-82,
-122,
-162,
-138,
-182,
-171,
-144,
-159,
-21,
-15,
-229,
-28,
-222,
-67,
-51,
-12,
-1,
-224,
-110,
-1,
-206,
-215,
-178,
-61,
-170,
-218,
-58,
-244,
-59,
-50,
-50,
-9,
-171,
-197,
-181,
-78,
-153,
-55,
-19,
-81,
-215,
-251,
-143,
-245,
-252,
-104,
-153,
-32,
-179,
-41,
-144,
-60,
-123,
-219,
-118,
-228,
-241,
-44,
-228,
-136,
-246,
-118,
-196,
-86,
-227,
-17,
-36,
-188,
-155,
-55,
-55,
-132,
-182,
-9,
-70,
-139,
-254,
-140,
-218,
-41,
-132,
-33,
-82,
-122,
-34,
-224,
-53,
-136,
-214,
-255,
-110,
-228,
-107,
-48,
-93,
-95,
-254,
-231,
-145,
-68,
-15,
-0,
-159,
-247,
-180,
-27,
-85,
-45,
-118,
-9,
-125,
-45,
-207,
-42,
-90,
-224,
-37,
-200,
-158,
-248,
-65,
-90,
-72,
-237,
-174,
-215,
-247,
-208,
-12,
-229,
-249,
-209,
-34,
-65,
-102,
-83,
-144,
-107,
-205,
-91,
-9,
-2,
-196,
-215,
-222,
-42,
-222,
-166,
-33,
-147,
-237,
-86,
-231,
-57,
-120,
-141,
-182,
-24,
-41,
-160,
-203,
-254,
-61,
-25,
-58,
-198,
-60,
-252,
-71,
-229,
-20,
-194,
-16,
-33,
-61,
-17,
-240,
-85,
-242,
-164,
-33,
-211,
-148,
-151,
-43,
-193,
-30,
-208,
-191,
-255,
-240,
-180,
-29,
-51,
-3,
-184,
-9,
-207,
-58,
-90,
-36,
-42,
-12,
-192,
-192,
-190,
-206,
-195,
-99,
-92,
-111,
-1,
-74,
-218,
-188,
-4,
-249,
-154,
-109,
-90,
-79,
-29,
-14,
-196,
-165,
-251,
-23,
-228,
-99,
-43,
-88,
-16,
-32,
-2,
-234,
-30,
-109,
-247,
-45,
-70,
-230,
-86,
-220,
-1,
-249,
-104,
-65,
-131,
-172,
-79,
-5,
-254,
-151,
-52,
-121,
-70,
-99,
-2,
-116,
-76,
-79,
-68,
-126,
-68,
-177,
-200,
-243,
-80,
-159,
-142,
-8,
-7,
-139,
-123,
-60,
-237,
-199,
-220,
-0,
-14,
-225,
-89,
-71,
-11,
-60,
-83,
-7,
-212,
-2,
-60,
-145,
-112,
-10,
-180,
-227,
-126,
-11,
-224,
-105,
-243,
-7,
-29,
-19,
-231,
-19,
-89,
-8,
-40,
-255,
-53,
-17,
-239,
-57,
-107,
-136,
-84,
-43,
-8,
-128,
-47,
-42,
-173,
-55,
-115,
-18,
-240,
-93,
-173,
-255,
-83,
-203,
-62,
-141,
-63,
-1,
-96,
-204,
-83,
-3,
-176,
-85,
-122,
-34,
-242,
-208,
-216,
-165,
-81,
-127,
-129,
-19,
-44,
-111,
-79,
-221,
-152,
-27,
-192,
-33,
-60,
-67,
-104,
-129,
-175,
-105,
-221,
-69,
-33,
-215,
-141,
-125,
-125,
-135,
-102,
-52,
-4,
-192,
-187,
-17,
-223,
-4,
-128,
-107,
-67,
-219,
-53,
-5,
-242,
-85,
-223,
-159,
-124,
-149,
-89,
-42,
-8,
-128,
-11,
-149,
-198,
-107,
-181,
-136,
-232,
-49,
-0,
-166,
-182,
-236,
-203,
-248,
-20,
-0,
-198,
-24,
-67,
-203,
-244,
-68,
-72,
-104,
-109,
-128,
-247,
-87,
-208,
-188,
-85,
-105,
-230,
-122,
-234,
-198,
-220,
-0,
-142,
-53,
-1,
-145,
-21,
-208,
-93,
-90,
-255,
-65,
-95,
-251,
-62,
-175,
-239,
-208,
-12,
-93,
-0,
-56,
-109,
-47,
-5,
-158,
-104,
-218,
-174,
-197,
-117,
-150,
-5,
-62,
-129,
-56,
-67,
-129,
-223,
-110,
-222,
-245,
-210,
-172,
-194,
-194,
-150,
-125,
-24,
-55,
-2,
-96,
-192,
-14,
-32,
-203,
-178,
-59,
-141,
-49,
-109,
-76,
-111,
-173,
-146,
-235,
-222,
-10,
-154,
-39,
-245,
-239,
-99,
-45,
-248,
-143,
-91,
-100,
-89,
-54,
-27,
-241,
-243,
-255,
-157,
-49,
-230,
-71,
-192,
-153,
-197,
-104,
-57,
-17,
-48,
-211,
-200,
-57,
-125,
-85,
-228,
-87,
-107,
-137,
-57,
-148,
-40,
-72,
-136,
-99,
-16,
-70,
-108,
-7,
-182,
-52,
-198,
-252,
-97,
-24,
-215,
-213,
-107,
-90,
-248,
-140,
-146,
-236,
-88,
-253,
-175,
-233,
-231,
-89,
-124,
-222,
-24,
-51,
-16,
-80,
-213,
-17,
-10,
-27,
-103,
-89,
-118,
-91,
-27,
-198,
-192,
-134,
-198,
-152,
-111,
-24,
-99,
-182,
-55,
-198,
-172,
-110,
-196,
-78,
-228,
-76,
-99,
-204,
-1,
-89,
-150,
-61,
-220,
-174,
-187,
-17,
-64,
-190,
-244,
-218,
-165,
-130,
-230,
-195,
-74,
-243,
-119,
-79,
-221,
-152,
-251,
-130,
-133,
-240,
-28,
-43,
-32,
-210,
-41,
-78,
-228,
-62,
-205,
-64,
-180,
-245,
-83,
-144,
-227,
-182,
-222,
-130,
-81,
-34,
-91,
-128,
-47,
-57,
-227,
-112,
-10,
-176,
-15,
-176,
-172,
-135,
-214,
-58,
-222,
-148,
-154,
-221,
-246,
-212,
-71,
-139,
-141,
-90,
-182,
-127,
-41,
-185,
-158,
-110,
-6,
-146,
-241,
-218,
-42,
-65,
-239,
-35,
-32,
-75,
-118,
-111,
-32,
-79,
-89,
-253,
-199,
-10,
-154,
-191,
-41,
-205,
-94,
-158,
-186,
-174,
-2,
-32,
-186,
-29,
-195,
-56,
-19,
-0,
-157,
-79,
-113,
-198,
-35,
-16,
-37,
-224,
-247,
-200,
-109,
-76,
-110,
-70,
-204,
-175,
-75,
-173,
-92,
-201,
-93,
-111,
-191,
-48,
-228,
-190,
-182,
-22,
-0,
-136,
-89,
-185,
-77,
-113,
-127,
-2,
-42,
-76,
-129,
-149,
-17,
-69,
-43,
-104,
-114,
-217,
-81,
-1,
-114,
-220,
-99,
-207,
-102,
-191,
-138,
-35,
-121,
-145,
-125,
-217,
-119,
-156,
-23,
-52,
-224,
-210,
-26,
-65,
-0,
-68,
-255,
-2,
-142,
-51,
-1,
-208,
-233,
-20,
-103,
-188,
-1,
-57,
-6,
-252,
-57,
-249,
-23,
-112,
-18,
-226,
-118,
-93,
-25,
-44,
-69,
-219,
-238,
-167,
-109,
-174,
-199,
-19,
-125,
-169,
-47,
-116,
-20,
-0,
-111,
-215,
-182,
-247,
-2,
-79,
-43,
-212,
-45,
-79,
-158,
-154,
-238,
-85,
-49,
-59,
-124,
-24,
-13,
-98,
-160,
-3,
-31,
-32,
-63,
-49,
-152,
-129,
-156,
-32,
-92,
-77,
-254,
-117,
-190,
-7,
-120,
-97,
-73,
-219,
-174,
-2,
-32,
-250,
-23,
-112,
-60,
-9,
-0,
-99,
-186,
-157,
-226,
-140,
-55,
-144,
-127,
-108,
-174,
-4,
-222,
-233,
-123,
-231,
-21,
-109,
-87,
-64,
-182,
-8,
-32,
-17,
-162,
-54,
-44,
-212,
-63,
-7,
-81,
-36,
-238,
-80,
-198,
-163,
-101,
-159,
-187,
-8,
-0,
-43,
-192,
-189,
-246,
-36,
-228,
-89,
-169,
-126,
-208,
-189,
-167,
-57,
-83,
-104,
-24,
-195,
-28,
-9,
-252,
-113,
-4,
-249,
-41,
-130,
-141,
-106,
-250,
-29,
-186,
-231,
-155,
-175,
-18,
-0,
-209,
-191,
-128,
-93,
-5,
-0,
-34,
-248,
-162,
-41,
-153,
-144,
-124,
-134,
-223,
-167,
-58,
-55,
-94,
-171,
-83,
-156,
-241,
-6,
-196,
-123,
-238,
-205,
-29,
-218,
-111,
-70,
-126,
-50,
-3,
-242,
-5,
-157,
-66,
-110,
-4,
-4,
-145,
-227,
-80,
-56,
-124,
-159,
-131,
-28,
-143,
-90,
-171,
-65,
-155,
-190,
-252,
-45,
-21,
-109,
-255,
-163,
-109,
-119,
-43,
-169,
-223,
-93,
-235,
-207,
-139,
-221,
-225,
-232,
-73,
-12,
-74,
-174,
-213,
-121,
-15,
-79,
-228,
-47,
-96,
-4,
-1,
-112,
-59,
-208,
-72,
-219,
-203,
-96,
-88,
-232,
-39,
-245,
-30,
-190,
-133,
-44,
-119,
-103,
-0,
-207,
-106,
-211,
-159,
-190,
-129,
-68,
-93,
-62,
-7,
-103,
-27,
-230,
-43,
-107,
-200,
-115,
-91,
-34,
-71,
-112,
-118,
-120,
-175,
-12,
-124,
-5,
-89,
-69,
-76,
-67,
-140,
-181,
-30,
-211,
-201,
-246,
-157,
-216,
-2,
-211,
-25,
-79,
-63,
-212,
-191,
-247,
-1,
-151,
-147,
-7,
-3,
-5,
-143,
-153,
-188,
-182,
-181,
-202,
-77,
-239,
-252,
-32,
-143,
-22,
-52,
-96,
-100,
-215,
-181,
-195,
-195,
-18,
-0,
-177,
-124,
-17,
-162,
-125,
-1,
-35,
-8,
-128,
-127,
-211,
-32,
-76,
-54,
-254,
-176,
-208,
-147,
-28,
-1,
-6,
-112,
-112,
-155,
-190,
-12,
-3,
-120,
-150,
-169,
-190,
-178,
-64,
-94,
-219,
-34,
-198,
-60,
-208,
-99,
-28,
-254,
-97,
-194,
-121,
-135,
-115,
-113,
-98,
-99,
-32,
-219,
-83,
-27,
-247,
-97,
-30,
-158,
-120,
-10,
-228,
-186,
-14,
-111,
-206,
-5,
-224,
-101,
-90,
-31,
-239,
-88,
-115,
-200,
-2,
-96,
-92,
-107,
-177,
-129,
-53,
-144,
-200,
-174,
-155,
-57,
-101,
-167,
-227,
-152,
-147,
-34,
-169,
-179,
-190,
-136,
-39,
-203,
-43,
-213,
-97,
-161,
-63,
-224,
-12,
-158,
-41,
-140,
-193,
-116,
-102,
-228,
-71,
-188,
-215,
-160,
-10,
-96,
-95,
-89,
-0,
-31,
-119,
-226,
-207,
-70,
-236,
-253,
-215,
-239,
-181,
-243,
-67,
-130,
-243,
-14,
-189,
-193,
-93,
-200,
-79,
-201,
-14,
-241,
-212,
-217,
-188,
-138,
-155,
-149,
-180,
-125,
-177,
-214,
-183,
-50,
-94,
-50,
-192,
-15,
-8,
-199,
-59,
-91,
-93,
-164,
-250,
-250,
-227,
-90,
-139,
-141,
-248,
-194,
-91,
-220,
-141,
-164,
-117,
-190,
-88,
-255,
-29,
-73,
-174,
-116,
-2,
-216,
-201,
-211,
-222,
-27,
-22,
-26,
-241,
-37,
-176,
-86,
-150,
-118,
-25,
-232,
-93,
-38,
-142,
-22,
-128,
-205,
-145,
-47,
-212,
-116,
-244,
-235,
-229,
-43,
-171,
-225,
-225,
-78,
-252,
-39,
-16,
-93,
-199,
-154,
-253,
-247,
-126,
-120,
-112,
-222,
-255,
-219,
-74,
-234,
-109,
-236,
-200,
-171,
-61,
-117,
-118,
-107,
-91,
-183,
-2,
-8,
-13,
-92,
-51,
-192,
-224,
-19,
-12,
-186,
-53,
-162,
-147,
-178,
-88,
-254,
-186,
-138,
-155,
-11,
-66,
-73,
-31,
-198,
-173,
-22,
-27,
-17,
-0,
-255,
-208,
-254,
-22,
-149,
-73,
-211,
-181,
-236,
-106,
-100,
-63,
-60,
-224,
-111,
-142,
-39,
-44,
-52,
-178,
-234,
-177,
-225,
-192,
-143,
-5,
-246,
-213,
-255,
-15,
-100,
-215,
-85,
-250,
-205,
-41,
-28,
-17,
-57,
-117,
-75,
-81,
-227,
-75,
-142,
-104,
-199,
-191,
-174,
-207,
-221,
-26,
-241,
-60,
-140,
-8,
-177,
-239,
-80,
-208,
-150,
-107,
-155,
-149,
-200,
-205,
-110,
-63,
-92,
-86,
-86,
-113,
-77,
-119,
-226,
-79,
-69,
-4,
-225,
-74,
-85,
-109,
-198,
-43,
-156,
-241,
-80,
-246,
-21,
-127,
-189,
-214,
-15,
-152,
-76,
-35,
-250,
-2,
-168,
-215,
-1,
-60,
-16,
-187,
-195,
-65,
-91,
-0,
-36,
-40,
-100,
-240,
-191,
-10,
-62,
-139,
-133,
-22,
-27,
-145,
-230,
-243,
-16,
-79,
-184,
-218,
-88,
-254,
-228,
-214,
-105,
-175,
-119,
-202,
-108,
-172,
-247,
-91,
-144,
-179,
-222,
-29,
-244,
-183,
-47,
-44,
-244,
-11,
-17,
-69,
-214,
-63,
-138,
-66,
-0,
-88,
-26,
-217,
-142,
-204,
-161,
-228,
-156,
-24,
-81,
-62,
-222,
-172,
-252,
-23,
-234,
-4,
-190,
-18,
-39,
-136,
-7,
-254,
-132,
-26,
-43,
-147,
-135,
-50,
-255,
-112,
-89,
-89,
-201,
-53,
-237,
-196,
-191,
-3,
-209,
-239,
-120,
-133,
-87,
-23,
-32,
-31,
-149,
-243,
-200,
-181,
-237,
-23,
-163,
-43,
-88,
-116,
-75,
-217,
-128,
-215,
-154,
-228,
-202,
-234,
-43,
-90,
-244,
-197,
-98,
-227,
-146,
-250,
-170,
-220,
-133,
-54,
-102,
-96,
-153,
-3,
-147,
-61,
-5,
-184,
-172,
-105,
-191,
-234,
-58,
-60,
-20,
-29,
-64,
-87,
-32,
-57,
-7,
-64,
-190,
-164,
-3,
-86,
-96,
-200,
-23,
-208,
-126,
-101,
-75,
-61,
-22,
-11,
-109,
-26,
-217,
-65,
-56,
-237,
-94,
-128,
-172,
-100,
-78,
-1,
-206,
-70,
-4,
-153,
-87,
-234,
-59,
-109,
-70,
-132,
-133,
-70,
-142,
-169,
-230,
-32,
-66,
-228,
-101,
-90,
-246,
-170,
-138,
-1,
-178,
-52,
-112,
-134,
-214,
-63,
-37,
-4,
-16,
-45,
-252,
-153,
-90,
-126,
-22,
-37,
-123,
-113,
-224,
-120,
-165,
-185,
-141,
-66,
-4,
-102,
-196,
-200,
-235,
-8,
-74,
-162,
-58,
-35,
-38,
-170,
-179,
-25,
-185,
-5,
-24,
-40,
-243,
-180,
-179,
-58,
-143,
-219,
-145,
-21,
-104,
-84,
-1,
-64,
-158,
-72,
-3,
-100,
-251,
-228,
-70,
-37,
-250,
-50,
-170,
-88,
-107,
-192,
-239,
-40,
-135,
-215,
-128,
-30,
-39,
-160,
-189,
-133,
-55,
-232,
-13,
-249,
-87,
-252,
-113,
-79,
-221,
-33,
-90,
-119,
-120,
-73,
-219,
-95,
-104,
-125,
-101,
-240,
-216,
-54,
-29,
-30,
-47,
-2,
-96,
-57,
-96,
-178,
-246,
-249,
-32,
-79,
-253,
-247,
-181,
-110,
-18,
-129,
-201,
-53,
-218,
-220,
-63,
-226,
-249,
-103,
-83,
-124,
-109,
-79,
-254,
-213,
-190,
-145,
-234,
-243,
-123,
-27,
-157,
-230,
-77,
-192,
-211,
-28,
-30,
-95,
-118,
-104,
-42,
-195,
-66,
-35,
-66,
-224,
-207,
-74,
-115,
-54,
-242,
-37,
-254,
-171,
-254,
-46,
-157,
-252,
-218,
-214,
-158,
-62,
-148,
-250,
-114,
-212,
-220,
-183,
-253,
-2,
-185,
-74,
-192,
-129,
-178,
-66,
-155,
-101,
-17,
-123,
-125,
-171,
-31,
-153,
-138,
-184,
-244,
-118,
-222,
-2,
-0,
-27,
-147,
-199,
-39,
-252,
-63,
-84,
-177,
-140,
-108,
-171,
-62,
-70,
-174,
-84,
-11,
-245,
-11,
-121,
-185,
-182,
-121,
-156,
-246,
-167,
-66,
-22,
-239,
-40,
-169,
-255,
-148,
-214,
-95,
-229,
-169,
-179,
-249,
-29,
-239,
-163,
-218,
-18,
-208,
-171,
-95,
-104,
-5,
-101,
-56,
-46,
-4,
-128,
-49,
-198,
-0,
-155,
-34,
-75,
-180,
-69,
-192,
-118,
-78,
-249,
-246,
-90,
-54,
-29,
-216,
-164,
-1,
-191,
-198,
-247,
-79,
-158,
-6,
-236,
-78,
-29,
-108,
-25,
-112,
-131,
-150,
-253,
-178,
-162,
-221,
-83,
-97,
-161,
-129,
-159,
-234,
-255,
-207,
-199,
-49,
-109,
-37,
-32,
-44,
-52,
-242,
-197,
-183,
-66,
-192,
-218,
-199,
-87,
-78,
-126,
-109,
-103,
-151,
-182,
-91,
-85,
-209,
-213,
-240,
-176,
-249,
-15,
-126,
-86,
-85,
-230,
-105,
-183,
-52,
-34,
-44,
-236,
-22,
-228,
-9,
-196,
-22,
-162,
-181,
-18,
-144,
-60,
-8,
-173,
-215,
-48,
-134,
-60,
-54,
-69,
-168,
-0,
-184,
-17,
-17,
-80,
-91,
-118,
-232,
-147,
-197,
-175,
-74,
-234,
-207,
-211,
-250,
-31,
-150,
-212,
-91,
-189,
-220,
-111,
-200,
-125,
-1,
-86,
-4,
-78,
-213,
-242,
-235,
-9,
-48,
-133,
-110,
-210,
-225,
-237,
-232,
-43,
-8,
-97,
-79,
-0,
-62,
-168,
-15,
-99,
-42,
-178,
-175,
-93,
-139,
-124,
-217,
-87,
-26,
-171,
-160,
-132,
-23,
-52,
-23,
-0,
-107,
-35,
-58,
-140,
-253,
-157,
-178,
-143,
-35,
-91,
-148,
-117,
-42,
-218,
-89,
-5,
-223,
-221,
-136,
-176,
-122,
-12,
-88,
-215,
-169,
-15,
-78,
-78,
-137,
-40,
-243,
-236,
-201,
-193,
-163,
-192,
-138,
-1,
-253,
-182,
-118,
-24,
-165,
-9,
-76,
-3,
-120,
-44,
-139,
-40,
-57,
-247,
-170,
-42,
-171,
-104,
-111,
-147,
-169,
-218,
-213,
-207,
-44,
-125,
-150,
-235,
-183,
-232,
-203,
-53,
-202,
-99,
-159,
-146,
-250,
-157,
-155,
-8,
-128,
-24,
-112,
-4,
-192,
-2,
-36,
-252,
-155,
-187,
-42,
-249,
-63,
-173,
-155,
-75,
-137,
-169,
-48,
-162,
-31,
-179,
-39,
-65,
-51,
-16,
-161,
-100,
-149,
-230,
-143,
-83,
-19,
-113,
-106,
-204,
-67,
-7,
-192,
-62,
-136,
-1,
-141,
-61,
-18,
-244,
-162,
-134,
-143,
-205,
-85,
-127,
-142,
-254,
-131,
-22,
-123,
-35,
-109,
-55,
-44,
-59,
-136,
-21,
-200,
-21,
-103,
-224,
-40,
-123,
-104,
-16,
-22,
-154,
-145,
-219,
-0,
-43,
-4,
-254,
-130,
-19,
-162,
-173,
-164,
-157,
-141,
-90,
-52,
-15,
-89,
-138,
-46,
-89,
-69,
-223,
-39,
-116,
-66,
-188,
-19,
-81,
-66,
-66,
-11,
-67,
-32,
-242,
-16,
-97,
-222,
-184,
-126,
-136,
-94,
-163,
-118,
-44,
-197,
-132,
-243,
-110,
-109,
-170,
-239,
-7,
-24,
-180,
-4,
-172,
-212,
-57,
-33,
-31,
-152,
-35,
-17,
-191,
-154,
-185,
-250,
-247,
-215,
-140,
-119,
-91,
-9,
-125,
-233,
-127,
-28,
-152,
-233,
-57,
-30,
-66,
-20,
-84,
-15,
-213,
-189,
-52,
-100,
-15,
-125,
-157,
-211,
-246,
-42,
-234,
-151,
-192,
-163,
-106,
-7,
-161,
-125,
-248,
-142,
-115,
-141,
-89,
-72,
-44,
-192,
-38,
-201,
-45,
-93,
-69,
-224,
-25,
-200,
-222,
-240,
-116,
-253,
-93,
-41,
-4,
-180,
-173,
-213,
-23,
-128,
-8,
-163,
-175,
-50,
-202,
-103,
-241,
-192,
-155,
-105,
-24,
-74,
-77,
-219,
-217,
-108,
-197,
-175,
-40,
-169,
-223,
-216,
-222,
-104,
-5,
-143,
-198,
-97,
-195,
-107,
-250,
-100,
-241,
-44,
-100,
-165,
-243,
-31,
-100,
-91,
-58,
-29,
-9,
-83,
-22,
-111,
-255,
-62,
-222,
-64,
-158,
-65,
-183,
-12,
-159,
-80,
-186,
-79,
-84,
-189,
-52,
-135,
-159,
-155,
-92,
-116,
-239,
-0,
-250,
-78,
-118,
-16,
-93,
-1,
-60,
-3,
-249,
-106,
-205,
-5,
-14,
-70,
-150,
-228,
-51,
-144,
-37,
-222,
-36,
-234,
-211,
-99,
-219,
-163,
-62,
-244,
-239,
-210,
-78,
-185,
-141,
-201,
-119,
-38,
-213,
-66,
-96,
-9,
-125,
-110,
-246,
-204,
-25,
-29,
-248,
-63,
-165,
-143,
-100,
-20,
-61,
-130,
-124,
-5,
-89,
-22,
-195,
-114,
-51,
-123,
-131,
-37,
-245,
-173,
-194,
-134,
-215,
-244,
-201,
-98,
-192,
-158,
-98,
-194,
-131,
-220,
-12,
-178,
-12,
-193,
-2,
-0,
-241,
-182,
-122,
-2,
-209,
-218,
-46,
-68,
-246,
-211,
-165,
-251,
-239,
-10,
-62,
-48,
-188,
-45,
-192,
-114,
-58,
-201,
-127,
-212,
-178,
-253,
-243,
-145,
-125,
-224,
-83,
-147,
-223,
-169,
-91,
-10,
-9,
-216,
-50,
-147,
-18,
-75,
-50,
-15,
-253,
-206,
-228,
-138,
-73,
-16,
-5,
-221,
-184,
-177,
-204,
-35,
-95,
-1,
-238,
-89,
-82,
-255,
-182,
-50,
-1,
-64,
-79,
-97,
-195,
-157,
-103,
-217,
-42,
-34,
-80,
-239,
-64,
-236,
-217,
-191,
-133,
-72,
-187,
-39,
-145,
-165,
-231,
-36,
-36,
-81,
-65,
-165,
-253,
-57,
-34,
-81,
-15,
-71,
-52,
-222,
-51,
-144,
-51,
-224,
-155,
-144,
-4,
-151,
-181,
-222,
-107,
-228,
-202,
-141,
-78,
-2,
-0,
-88,
-18,
-49,
-246,
-0,
-49,
-30,
-178,
-231,
-167,
-23,
-208,
-80,
-67,
-170,
-237,
-198,
-211,
-41,
-200,
-243,
-139,
-147,
-223,
-169,
-91,
-146,
-22,
-74,
-34,
-36,
-144,
-171,
-221,
-79,
-7,
-217,
-79,
-140,
-5,
-144,
-159,
-164,
-120,
-243,
-232,
-145,
-155,
-150,
-251,
-4,
-64,
-47,
-97,
-195,
-199,
-180,
-0,
-0,
-62,
-68,
-190,
-108,
-90,
-128,
-236,
-123,
-220,
-165,
-224,
-245,
-128,
-55,
-224,
-164,
-78,
-124,
-155,
-16,
-100,
-14,
-98,
-185,
-118,
-143,
-83,
-246,
-48,
-53,
-199,
-111,
-140,
-76,
-37,
-238,
-67,
-168,
-0,
-248,
-166,
-210,
-79,
-66,
-52,
-208,
-203,
-146,
-107,
-184,
-191,
-214,
-240,
-153,
-192,
-56,
-18,
-0,
-125,
-193,
-62,
-115,
-224,
-161,
-209,
-238,
-75,
-40,
-16,
-97,
-104,
-141,
-171,
-246,
-41,
-212,
-189,
-7,
-81,
-118,
-226,
-27,
-75,
-244,
-20,
-54,
-220,
-25,
-203,
-209,
-5,
-64,
-103,
-222,
-58,
-137,
-231,
-81,
-56,
-127,
-5,
-94,
-65,
-254,
-117,
-246,
-102,
-168,
-37,
-143,
-199,
-254,
-49,
-70,
-122,
-177,
-61,
-31,
-57,
-170,
-128,
-18,
-73,
-236,
-208,
-186,
-46,
-175,
-173,
-4,
-0,
-146,
-78,
-124,
-1,
-34,
-76,
-94,
-236,
-148,
-111,
-174,
-101,
-243,
-9,
-76,
-209,
-173,
-237,
-146,
-0,
-48,
-198,
-0,
-175,
-213,
-103,
-49,
-16,
-206,
-61,
-160,
-109,
-48,
-2,
-120,
-185,
-122,
-157,
-218,
-96,
-29,
-228,
-71,
-171,
-144,
-251,
-222,
-219,
-165,
-189,
-253,
-138,
-251,
-4,
-64,
-47,
-97,
-195,
-157,
-118,
-99,
-82,
-0,
-44,
-65,
-121,
-184,
-174,
-189,
-148,
-249,
-93,
-21,
-109,
-189,
-91,
-4,
-224,
-29,
-218,
-182,
-210,
-79,
-153,
-145,
-222,
-114,
-62,
-84,
-10,
-0,
-100,
-223,
-118,
-135,
-210,
-250,
-108,
-214,
-247,
-215,
-186,
-219,
-9,
-56,
-23,
-215,
-54,
-227,
-206,
-14,
-162,
-15,
-56,
-239,
-255,
-198,
-22,
-109,
-67,
-114,
-233,
-93,
-66,
-141,
-160,
-5,
-94,
-132,
-108,
-45,
-167,
-233,
-191,
-25,
-20,
-76,
-150,
-75,
-218,
-189,
-13,
-249,
-162,
-91,
-109,
-251,
-127,
-128,
-247,
-2,
-235,
-218,
-129,
-229,
-105,
-99,
-87,
-14,
-215,
-117,
-233,
-179,
-135,
-111,
-183,
-73,
-58,
-74,
-188,
-173,
-70,
-20,
-96,
-94,
-139,
-182,
-219,
-106,
-219,
-1,
-251,
-230,
-2,
-221,
-239,
-171,
-231,
-127,
-173,
-0,
-56,
-73,
-233,
-46,
-194,
-179,
-215,
-71,
-132,
-148,
-85,
-106,
-157,
-232,
-169,
-247,
-157,
-2,
-148,
-253,
-235,
-124,
-10,
-128,
-8,
-151,
-179,
-144,
-175,
-205,
-28,
-68,
-195,
-124,
-8,
-125,
-38,
-128,
-44,
-239,
-203,
-230,
-200,
-113,
-215,
-43,
-139,
-207,
-14,
-177,
-162,
-180,
-95,
-196,
-74,
-3,
-164,
-30,
-251,
-183,
-22,
-114,
-44,
-57,
-13,
-89,
-229,
-189,
-14,
-17,
-0,
-119,
-0,
-107,
-181,
-228,
-185,
-161,
-29,
-88,
-158,
-186,
-81,
-9,
-27,
-222,
-5,
-125,
-11,
-0,
-187,
-231,
-169,
-74,
-250,
-81,
-214,
-214,
-58,
-41,
-84,
-42,
-144,
-24,
-233,
-79,
-223,
-88,
-0,
-116,
-5,
-67,
-180,
-3,
-32,
-95,
-141,
-128,
-124,
-149,
-110,
-38,
-215,
-129,
-220,
-71,
-3,
-115,
-229,
-24,
-32,
-119,
-50,
-2,
-153,
-100,
-215,
-33,
-138,
-96,
-55,
-205,
-247,
-31,
-168,
-8,
-181,
-221,
-115,
-255,
-174,
-66,
-86,
-136,
-238,
-182,
-238,
-165,
-72,
-76,
-191,
-1,
-187,
-249,
-64,
-158,
-27,
-217,
-27,
-243,
-212,
-141,
-74,
-216,
-240,
-46,
-112,
-222,
-211,
-6,
-125,
-48,
-183,
-17,
-71,
-143,
-9,
-164,
-95,
-6,
-217,
-255,
-255,
-4,
-81,
-4,
-94,
-67,
-137,
-39,
-89,
-161,
-221,
-215,
-201,
-21,
-135,
-69,
-244,
-42,
-0,
-134,
-5,
-228,
-43,
-187,
-72,
-39,
-252,
-7,
-200,
-205,
-65,
-159,
-129,
-216,
-254,
-163,
-19,
-208,
-231,
-209,
-248,
-74,
-224,
-159,
-200,
-215,
-111,
-22,
-98,
-26,
-252,
-85,
-224,
-185,
-14,
-205,
-218,
-192,
-111,
-27,
-246,
-105,
-69,
-36,
-54,
-222,
-121,
-200,
-22,
-105,
-58,
-162,
-15,
-186,
-15,
-49,
-40,
-122,
-87,
-135,
-251,
-13,
-70,
-219,
-107,
-180,
-236,
-87,
-149,
-0,
-24,
-149,
-176,
-225,
-93,
-224,
-60,
-198,
-117,
-128,
-93,
-16,
-33,
-22,
-20,
-112,
-180,
-142,
-241,
-115,
-144,
-37,
-234,
-2,
-2,
-150,
-23,
-133,
-119,
-58,
-9,
-177,
-121,
-14,
-126,
-136,
-136,
-100,
-63,
-88,
-7,
-222,
-223,
-201,
-221,
-124,
-23,
-23,
-1,
-96,
-183,
-42,
-62,
-143,
-197,
-101,
-201,
-245,
-24,
-239,
-241,
-212,
-91,
-69,
-230,
-181,
-140,
-52,
-74,
-1,
-81,
-182,
-94,
-133,
-76,
-220,
-57,
-195,
-185,
-155,
-122,
-16,
-73,
-7,
-208,
-67,
-191,
-170,
-4,
-192,
-168,
-132,
-13,
-239,
-2,
-103,
-28,
-88,
-147,
-241,
-224,
-128,
-163,
-85,
-76,
-151,
-6,
-46,
-211,
-198,
-65,
-241,
-198,
-145,
-175,
-215,
-36,
-196,
-1,
-103,
-33,
-114,
-58,
-112,
-8,
-29,
-44,
-201,
-22,
-51,
-1,
-112,
-183,
-222,
-207,
-75,
-74,
-234,
-109,
-16,
-144,
-147,
-61,
-117,
-39,
-1,
-207,
-116,
-126,
-111,
-4,
-124,
-67,
-5,
-194,
-60,
-196,
-208,
-231,
-92,
-26,
-156,
-116,
-140,
-7,
-16,
-217,
-36,
-87,
-121,
-150,
-10,
-0,
-173,
-111,
-21,
-54,
-92,
-203,
-175,
-211,
-255,
-55,
-10,
-253,
-221,
-165,
-207,
-78,
-159,
-230,
-225,
-56,
-189,
-17,
-16,
-112,
-180,
-234,
-130,
-191,
-210,
-134,
-151,
-208,
-98,
-255,
-135,
-44,
-107,
-191,
-174,
-23,
-190,
-159,
-246,
-57,
-209,
-22,
-39,
-1,
-96,
-207,
-159,
-159,
-81,
-82,
-111,
-143,
-220,
-90,
-37,
-143,
-92,
-220,
-64,
-15,
-38,
-185,
-202,
-183,
-82,
-0,
-40,
-77,
-227,
-176,
-225,
-202,
-114,
-22,
-178,
-53,
-131,
-24,
-95,
-226,
-128,
-62,
-59,
-188,
-27,
-7,
-28,
-45,
-187,
-216,
-193,
-218,
-224,
-150,
-178,
-193,
-218,
-160,
-227,
-214,
-56,
-231,
-172,
-150,
-237,
-163,
-11,
-0,
-26,
-160,
-130,
-199,
-70,
-136,
-95,
-249,
-125,
-200,
-210,
-252,
-126,
-196,
-87,
-187,
-84,
-202,
-234,
-64,
-2,
-120,
-126,
-73,
-253,
-139,
-180,
-62,
-118,
-246,
-224,
-113,
-7,
-122,
-50,
-201,
-237,
-19,
-206,
-176,
-105,
-28,
-250,
-59,
-128,
-119,
-136,
-0,
-104,
-28,
-112,
-212,
-71,
-252,
-37,
-37,
-190,
-31,
-71,
-193,
-212,
-22,
-228,
-199,
-136,
-173,
-34,
-150,
-106,
-219,
-216,
-2,
-96,
-114,
-205,
-191,
-59,
-170,
-250,
-140,
-28,
-157,
-89,
-203,
-201,
-153,
-136,
-38,
-223,
-250,
-102,
-79,
-163,
-124,
-137,
-111,
-51,
-188,
-120,
-29,
-148,
-200,
-5,
-111,
-212,
-88,
-248,
-248,
-49,
-27,
-89,
-214,
-158,
-137,
-196,
-81,
-240,
-154,
-74,
-3,
-171,
-43,
-237,
-28,
-58,
-126,
-12,
-26,
-246,
-185,
-23,
-147,
-220,
-62,
-225,
-60,
-219,
-56,
-95,
-226,
-145,
-109,
-67,
-4,
-128,
-215,
-220,
-155,
-138,
-128,
-163,
-69,
-66,
-43,
-41,
-30,
-2,
-54,
-109,
-218,
-201,
-18,
-158,
-219,
-40,
-207,
-39,
-91,
-182,
-135,
-33,
-111,
-1,
-16,
-255,
-7,
-128,
-95,
-148,
-212,
-159,
-171,
-245,
-167,
-161,
-70,
-80,
-136,
-242,
-200,
-122,
-222,
-157,
-91,
-210,
-110,
-31,
-173,
-191,
-31,
-71,
-72,
-32,
-118,
-10,
-159,
-33,
-15,
-85,
-85,
-105,
-55,
-209,
-226,
-126,
-44,
-174,
-37,
-87,
-188,
-93,
-67,
-110,
-231,
-15,
-162,
-120,
-245,
-230,
-216,
-67,
-124,
-206,
-1,
-190,
-26,
-179,
-95,
-53,
-125,
-238,
-197,
-36,
-183,
-79,
-56,
-207,
-178,
-251,
-151,
-120,
-176,
-109,
-136,
-0,
-40,
-139,
-191,
-88,
-26,
-112,
-212,
-37,
-218,
-133,
-220,
-123,
-46,
-90,
-66,
-76,
-242,
-0,
-8,
-173,
-242,
-150,
-105,
-219,
-161,
-9,
-0,
-196,
-51,
-239,
-17,
-125,
-22,
-94,
-215,
-77,
-114,
-31,
-253,
-77,
-10,
-229,
-207,
-215,
-242,
-178,
-149,
-195,
-146,
-72,
-188,
-62,
-144,
-109,
-195,
-69,
-200,
-23,
-248,
-78,
-189,
-158,
-77,
-144,
-50,
-37,
-242,
-61,
-89,
-108,
-81,
-40,
-95,
-18,
-216,
-155,
-252,
-248,
-213,
-123,
-228,
-135,
-156,
-206,
-128,
-40,
-49,
-135,
-18,
-44,
-132,
-158,
-76,
-114,
-251,
-132,
-211,
-167,
-198,
-161,
-191,
-29,
-154,
-98,
-36,
-227,
-75,
-16,
-235,
-197,
-245,
-44,
-243,
-138,
-235,
-54,
-10,
-56,
-234,
-198,
-155,
-219,
-193,
-24,
-115,
-146,
-49,
-102,
-166,
-49,
-102,
-199,
-44,
-203,
-130,
-82,
-104,
-107,
-219,
-115,
-145,
-44,
-48,
-43,
-23,
-202,
-215,
-0,
-14,
-52,
-198,
-88,
-103,
-12,
-175,
-47,
-193,
-24,
-196,
-158,
-198,
-152,
-213,
-141,
-49,
-103,
-100,
-89,
-118,
-123,
-9,
-205,
-52,
-253,
-187,
-160,
-80,
-190,
-72,
-255,
-122,
-95,
-112,
-150,
-101,
-11,
-141,
-49,
-59,
-25,
-99,
-190,
-96,
-140,
-153,
-108,
-140,
-121,
-153,
-49,
-102,
-107,
-99,
-204,
-29,
-198,
-152,
-237,
-140,
-49,
-147,
-148,
-244,
-250,
-86,
-61,
-111,
-136,
-44,
-203,
-22,
-102,
-89,
-246,
-75,
-99,
-204,
-63,
-181,
-104,
-235,
-18,
-186,
-107,
-140,
-49,
-151,
-25,
-99,
-214,
-53,
-198,
-120,
-131,
-90,
-246,
-0,
-59,
-158,
-254,
-107,
-140,
-185,
-180,
-226,
-95,
-235,
-80,
-216,
-200,
-138,
-237,
-54,
-59,
-118,
-129,
-85,
-145,
-237,
-223,
-192,
-41,
-76,
-67,
-148,
-29,
-197,
-206,
-210,
-191,
-222,
-0,
-177,
-72,
-68,
-160,
-191,
-25,
-99,
-94,
-175,
-180,
-55,
-27,
-99,
-54,
-50,
-198,
-156,
-106,
-140,
-217,
-55,
-224,
-186,
-235,
-151,
-148,
-91,
-157,
-147,
-127,
-60,
-147,
-239,
-95,
-167,
-82,
-189,
-55,
-30,
-152,
-196,
-142,
-244,
-89,
-132,
-40,
-109,
-254,
-135,
-124,
-209,
-236,
-87,
-101,
-1,
-78,
-116,
-219,
-166,
-80,
-30,
-67,
-89,
-1,
-32,
-190,
-240,
-246,
-248,
-231,
-213,
-21,
-116,
-39,
-42,
-205,
-247,
-10,
-229,
-54,
-241,
-99,
-144,
-209,
-148,
-135,
-175,
-221,
-215,
-182,
-142,
-205,
-87,
-194,
-215,
-194,
-187,
-178,
-67,
-18,
-143,
-0,
-28,
-86,
-193,
-99,
-87,
-165,
-137,
-151,
-129,
-182,
-2,
-12,
-193,
-36,
-23,
-209,
-107,
-0,
-188,
-86,
-127,
-219,
-232,
-187,
-179,
-91,
-242,
-179,
-104,
-19,
-250,
-187,
-117,
-36,
-99,
-231,
-186,
-173,
-2,
-142,
-54,
-209,
-138,
-159,
-230,
-105,
-187,
-35,
-162,
-13,
-159,
-132,
-156,
-151,
-206,
-71,
-20,
-97,
-147,
-17,
-43,
-194,
-78,
-193,
-10,
-245,
-186,
-195,
-18,
-0,
-187,
-233,
-245,
-42,
-147,
-122,
-34,
-203,
-49,
-235,
-37,
-249,
-83,
-96,
-3,
-242,
-173,
-206,
-245,
-180,
-8,
-158,
-161,
-47,
-218,
-186,
-46,
-87,
-230,
-17,
-104,
-193,
-219,
-98,
-64,
-0,
-32,
-250,
-7,
-27,
-64,
-99,
-192,
-0,
-201,
-161,
-91,
-218,
-185,
-103,
-175,
-227,
-88,
-76,
-48,
-4,
-147,
-92,
-6,
-87,
-0,
-171,
-32,
-43,
-128,
-83,
-90,
-242,
-179,
-104,
-19,
-250,
-187,
-117,
-36,
-99,
-231,
-186,
-173,
-2,
-142,
-38,
-152,
-167,
-30,
-150,
-141,
-72,
-91,
-107,
-254,
-138,
-72,
-236,
-171,
-25,
-137,
-95,
-2,
-171,
-180,
-188,
-190,
-245,
-186,
-59,
-191,
-77,
-251,
-26,
-222,
-3,
-2,
-0,
-49,
-217,
-222,
-20,
-56,
-70,
-235,
-254,
-76,
-137,
-18,
-208,
-105,
-99,
-227,
-22,
-30,
-25,
-187,
-143,
-158,
-107,
-141,
-103,
-147,
-220,
-198,
-95,
-98,
-58,
-68,
-50,
-214,
-226,
-7,
-201,
-117,
-72,
-141,
-3,
-142,
-78,
-120,
-32,
-209,
-111,
-64,
-108,
-31,
-106,
-163,
-7,
-233,
-132,
-189,
-23,
-145,
-186,
-214,
-113,
-230,
-6,
-26,
-154,
-136,
-34,
-193,
-59,
-191,
-170,
-124,
-102,
-81,
-178,
-124,
-236,
-2,
-170,
-113,
-17,
-162,
-195,
-169,
-156,
-252,
-202,
-231,
-89,
-200,
-57,
-246,
-116,
-122,
-142,
-19,
-200,
-248,
-54,
-201,
-109,
-252,
-37,
-166,
-67,
-36,
-99,
-45,
-254,
-147,
-254,
-63,
-5,
-28,
-109,
-3,
-114,
-119,
-225,
-79,
-214,
-208,
-45,
-1,
-252,
-86,
-105,
-47,
-70,
-52,
-255,
-203,
-34,
-73,
-61,
-158,
-64,
-116,
-31,
-7,
-86,
-180,
-255,
-53,
-146,
-190,
-235,
-207,
-72,
-8,
-116,
-27,
-12,
-229,
-1,
-122,
-74,
-126,
-234,
-12,
-76,
-247,
-24,
-240,
-74,
-36,
-142,
-224,
-2,
-189,
-247,
-215,
-4,
-242,
-58,
-89,
-121,
-125,
-174,
-143,
-190,
-22,
-174,
-213,
-202,
-36,
-55,
-128,
-175,
-133,
-111,
-34,
-214,
-90,
-9,
-6,
-240,
-109,
-28,
-250,
-155,
-8,
-145,
-140,
-19,
-90,
-2,
-241,
-51,
-7,
-9,
-95,
-86,
-153,
-58,
-140,
-220,
-162,
-235,
-66,
-10,
-81,
-118,
-145,
-175,
-210,
-157,
-90,
-255,
-145,
-146,
-246,
-199,
-32,
-147,
-126,
-1,
-114,
-212,
-117,
-33,
-98,
-244,
-210,
-219,
-23,
-213,
-25,
-124,
-190,
-99,
-192,
-143,
-146,
-7,
-80,
-173,
-253,
-154,
-146,
-43,
-202,
-110,
-33,
-96,
-213,
-208,
-21,
-180,
-48,
-201,
-13,
-224,
-217,
-183,
-0,
-104,
-28,
-250,
-155,
-142,
-145,
-140,
-163,
-0,
-241,
-40,
-67,
-59,
-28,
-108,
-245,
-165,
-15,
-237,
-24,
-68,
-241,
-247,
-184,
-182,
-191,
-9,
-113,
-96,
-241,
-90,
-197,
-105,
-187,
-189,
-169,
-135,
-215,
-168,
-166,
-192,
-103,
-101,
-100,
-207,
-120,
-33,
-50,
-169,
-230,
-107,
-31,
-130,
-246,
-170,
-228,
-201,
-52,
-106,
-181,
-239,
-228,
-169,
-175,
-203,
-94,
-148,
-205,
-225,
-55,
-102,
-236,
-249,
-157,
-103,
-89,
-119,
-10,
-16,
-116,
-164,
-134,
-172,
-36,
-96,
-12,
-45,
-191,
-155,
-192,
-121,
-30,
-125,
-9,
-128,
-198,
-161,
-191,
-233,
-16,
-201,
-56,
-26,
-144,
-244,
-67,
-54,
-209,
-96,
-80,
-54,
-29,
-196,
-44,
-214,
-218,
-184,
-207,
-69,
-20,
-105,
-215,
-144,
-47,
-211,
-230,
-1,
-59,
-149,
-180,
-253,
-134,
-210,
-84,
-29,
-63,
-86,
-30,
-169,
-33,
-150,
-134,
-110,
-84,
-225,
-7,
-181,
-221,
-20,
-224,
-204,
-128,
-254,
-111,
-138,
-44,
-219,
-103,
-19,
-32,
-244,
-200,
-29,
-122,
-188,
-209,
-142,
-129,
-103,
-107,
-253,
-88,
-114,
-203,
-173,
-19,
-0,
-31,
-209,
-250,
-89,
-190,
-122,
-15,
-253,
-158,
-74,
-223,
-202,
-191,
-99,
-180,
-49,
-4,
-1,
-208,
-88,
-219,
-78,
-135,
-72,
-198,
-81,
-1,
-108,
-73,
-30,
-87,
-255,
-229,
-1,
-244,
-103,
-105,
-191,
-46,
-99,
-100,
-64,
-209,
-229,
-200,
-143,
-46,
-38,
-151,
-180,
-181,
-254,
-203,
-94,
-205,
-103,
-192,
-181,
-55,
-35,
-183,
-202,
-251,
-13,
-133,
-227,
-41,
-2,
-180,
-199,
-228,
-49,
-7,
-74,
-147,
-119,
-22,
-232,
-173,
-75,
-175,
-247,
-235,
-135,
-88,
-84,
-2,
-220,
-20,
-118,
-23,
-253,
-35,
-64,
-0,
-216,
-19,
-136,
-202,
-216,
-141,
-14,
-253,
-211,
-17,
-47,
-189,
-133,
-244,
-17,
-133,
-166,
-103,
-140,
-81,
-1,
-208,
-58,
-146,
-113,
-116,
-168,
-16,
-120,
-0,
-184,
-33,
-128,
-214,
-106,
-47,
-7,
-98,
-229,
-145,
-155,
-144,
-122,
-191,
-134,
-192,
-41,
-90,
-223,
-202,
-186,
-140,
-252,
-88,
-165,
-84,
-233,
-86,
-211,
-126,
-29,
-125,
-176,
-139,
-8,
-244,
-208,
-34,
-63,
-175,
-189,
-157,
-130,
-177,
-16,
-98,
-19,
-97,
-87,
-80,
-251,
-151,
-241,
-24,
-54,
-2,
-4,
-128,
-117,
-253,
-190,
-188,
-1,
-79,
-123,
-228,
-116,
-104,
-188,
-158,
-14,
-7,
-99,
-81,
-0,
-104,
-251,
-170,
-72,
-198,
-95,
-71,
-21,
-198,
-109,
-120,
-247,
-134,
-154,
-135,
-121,
-144,
-214,
-253,
-179,
-164,
-237,
-249,
-90,
-95,
-187,
-210,
-240,
-180,
-125,
-177,
-182,
-157,
-66,
-77,
-242,
-203,
-10,
-30,
-118,
-50,
-255,
-185,
-65,
-155,
-21,
-201,
-189,
-250,
-64,
-190,
-132,
-55,
-50,
-50,
-161,
-233,
-25,
-4,
-158,
-93,
-3,
-107,
-146,
-167,
-231,
-190,
-34,
-176,
-77,
-211,
-16,
-217,
-22,
-69,
-37,
-96,
-6,
-236,
-65,
-174,
-129,
-174,
-205,
-226,
-235,
-180,
-125,
-46,
-185,
-239,
-200,
-114,
-161,
-237,
-106,
-250,
-215,
-118,
-226,
-108,
-139,
-124,
-76,
-110,
-211,
-73,
-50,
-3,
-49,
-170,
-58,
-20,
-143,
-14,
-170,
-102,
-204,
-142,
-154,
-0,
-80,
-30,
-222,
-72,
-198,
-90,
-119,
-93,
-155,
-228,
-11,
-222,
-105,
-0,
-0,
-32,
-0,
-73,
-68,
-65,
-84,
-126,
-245,
-10,
-223,
-77,
-35,
-103,
-184,
-54,
-55,
-252,
-109,
-192,
-179,
-75,
-218,
-94,
-175,
-52,
-222,
-250,
-154,
-235,
-30,
-168,
-109,
-127,
-210,
-178,
-223,
-171,
-146,
-235,
-41,
-26,
-69,
-250,
-69,
-172,
-226,
-246,
-65,
-142,
-207,
-30,
-211,
-9,
-244,
-40,
-18,
-179,
-239,
-169,
-88,
-127,
-129,
-188,
-142,
-210,
-62,
-60,
-0,
-60,
-39,
-128,
-190,
-113,
-136,
-108,
-231,
-29,
-185,
-199,
-128,
-151,
-147,
-103,
-16,
-6,
-57,
-222,
-107,
-154,
-61,
-233,
-47,
-218,
-246,
-227,
-77,
-218,
-85,
-244,
-175,
-205,
-210,
-217,
-21,
-134,
-183,
-35,
-177,
-18,
-239,
-33,
-55,
-157,
-29,
-88,
-29,
-86,
-93,
-143,
-252,
-195,
-50,
-42,
-2,
-96,
-84,
-65,
-139,
-240,
-75,
-197,
-155,
-70,
-190,
-12,
-54,
-106,
-203,
-239,
-168,
-176,
-140,
-115,
-6,
-224,
-13,
-228,
-122,
-135,
-25,
-136,
-34,
-241,
-48,
-96,
-227,
-138,
-182,
-214,
-183,
-250,
-3,
-136,
-34,
-240,
-92,
-228,
-4,
-194,
-102,
-214,
-221,
-151,
-81,
-138,
-100,
-27,
-10,
-36,
-86,
-194,
-66,
-237,
-119,
-173,
-7,
-38,
-45,
-67,
-100,
-227,
-199,
-34,
-114,
-161,
-181,
-107,
-203,
-254,
-91,
-151,
-220,
-255,
-182,
-105,
-239,
-233,
-95,
-27,
-1,
-96,
-141,
-176,
-182,
-43,
-148,
-175,
-130,
-152,
-118,
-15,
-40,
-107,
-201,
-133,
-195,
-139,
-60,
-117,
-54,
-151,
-197,
-216,
-250,
-210,
-246,
-13,
-90,
-134,
-95,
-42,
-190,
-60,
-242,
-188,
-238,
-181,
-54,
-220,
-192,
-57,
-74,
-251,
-24,
-18,
-84,
-99,
-50,
-249,
-30,
-26,
-68,
-8,
-121,
-191,
-46,
-228,
-86,
-98,
-135,
-32,
-95,
-224,
-123,
-144,
-47,
-210,
-69,
-228,
-10,
-147,
-191,
-50,
-138,
-185,
-238,
-235,
-128,
-108,
-29,
-166,
-2,
-91,
-6,
-210,
-71,
-15,
-145,
-61,
-22,
-208,
-81,
-0,
-216,
-49,
-251,
-170,
-6,
-109,
-236,
-24,
-123,
-183,
-167,
-238,
-232,
-9,
-39,
-0,
-232,
-16,
-126,
-201,
-35,
-0,
-172,
-86,
-62,
-40,
-35,
-47,
-158,
-76,
-61,
-192,
-243,
-200,
-45,
-206,
-22,
-1,
-219,
-122,
-104,
-158,
-112,
-234,
-191,
-228,
-78,
-116,
-196,
-108,
-210,
-30,
-13,
-214,
-166,
-7,
-79,
-24,
-93,
-116,
-20,
-0,
-214,
-131,
-114,
-26,
-18,
-200,
-165,
-54,
-89,
-8,
-185,
-221,
-199,
-21,
-56,
-33,
-235,
-17,
-109,
-187,
-213,
-196,
-79,
-40,
-1,
-208,
-58,
-252,
-146,
-71,
-0,
-156,
-161,
-191,
-255,
-71,
-71,
-175,
-54,
-135,
-215,
-217,
-158,
-58,
-171,
-184,
-250,
-105,
-73,
-91,
-107,
-100,
-84,
-233,
-217,
-151,
-48,
-250,
-112,
-198,
-208,
-250,
-72,
-120,
-178,
-43,
-244,
-67,
-242,
-36,
-178,
-69,
-169,
-212,
-209,
-0,
-7,
-56,
-60,
-230,
-35,
-137,
-76,
-94,
-91,
-65,
-255,
-26,
-103,
-252,
-204,
-64,
-156,
-186,
-172,
-201,
-241,
-183,
-24,
-35,
-218,
-118,
-2,
-17,
-227,
-66,
-173,
-195,
-47,
-57,
-253,
-176,
-2,
-96,
-53,
-196,
-198,
-29,
-100,
-41,
-254,
-77,
-90,
-46,
-195,
-145,
-99,
-53,
-240,
-251,
-79,
-91,
-205,
-185,
-55,
-12,
-54,
-98,
-224,
-3,
-53,
-161,
-200,
-144,
-213,
-198,
-137,
-200,
-82,
-124,
-62,
-178,
-31,
-191,
-4,
-201,
-224,
-227,
-205,
-136,
-172,
-237,
-90,
-5,
-147,
-64,
-146,
-119,
-124,
-13,
-81,
-194,
-77,
-211,
-107,
-78,
-69,
-132,
-221,
-86,
-177,
-219,
-141,
-7,
-56,
-99,
-200,
-166,
-115,
-191,
-27,
-217,
-130,
-218,
-119,
-60,
-159,
-146,
-248,
-12,
-192,
-203,
-144,
-45,
-228,
-99,
-200,
-135,
-234,
-50,
-135,
-223,
-241,
-192,
-178,
-37,
-237,
-222,
-128,
-156,
-66,
-77,
-67,
-38,
-252,
-53,
-168,
-249,
-54,
-37,
-218,
-246,
-226,
-88,
-47,
-212,
-133,
-132,
-236,
-170,
-132,
-167,
-93,
-85,
-46,
-5,
-107,
-143,
-210,
-42,
-212,
-94,
-241,
-66,
-173,
-195,
-47,
-249,
-30,
-10,
-162,
-37,
-255,
-6,
-121,
-128,
-131,
-179,
-129,
-167,
-181,
-232,
-151,
-245,
-128,
-26,
-8,
-208,
-128,
-40,
-42,
-161,
-160,
-252,
-113,
-234,
-159,
-161,
-245,
-165,
-89,
-109,
-17,
-187,
-118,
-235,
-140,
-51,
-23,
-209,
-67,
-184,
-105,
-209,
-75,
-99,
-236,
-211,
-50,
-152,
-4,
-185,
-185,
-245,
-124,
-228,
-20,
-228,
-42,
-242,
-237,
-204,
-2,
-224,
-245,
-49,
-219,
-85,
-244,
-227,
-45,
-218,
-54,
-232,
-232,
-209,
-211,
-126,
-64,
-168,
-35,
-19,
-174,
-18,
-37,
-188,
-44,
-230,
-3,
-187,
-56,
-229,
-171,
-59,
-247,
-93,
-182,
-58,
-181,
-31,
-155,
-55,
-59,
-101,
-219,
-144,
-7,
-118,
-245,
-174,
-16,
-219,
-192,
-233,
-103,
-83,
-1,
-112,
-82,
-200,
-191,
-6,
-253,
-88,
-146,
-252,
-4,
-173,
-117,
-176,
-29,
-151,
-97,
-235,
-140,
-168,
-53,
-15,
-101,
-11,
-114,
-79,
-168,
-160,
-228,
-34,
-133,
-246,
-239,
-212,
-182,
-183,
-122,
-234,
-172,
-17,
-145,
-247,
-1,
-144,
-11,
-143,
-187,
-43,
-248,
-219,
-243,
-252,
-75,
-113,
-204,
-128,
-17,
-171,
-172,
-255,
-171,
-233,
-91,
-171,
-96,
-18,
-136,
-78,
-229,
-211,
-56,
-206,
-63,
-136,
-213,
-164,
-221,
-151,
-150,
-5,
-20,
-109,
-213,
-174,
-162,
-31,
-246,
-20,
-197,
-187,
-234,
-83,
-154,
-181,
-17,
-99,
-171,
-199,
-245,
-126,
-183,
-208,
-242,
-221,
-128,
-251,
-60,
-244,
-239,
-69,
-242,
-44,
-22,
-255,
-253,
-81,
-175,
-229,
-181,
-52,
-116,
-198,
-208,
-81,
-158,
-58,
-235,
-7,
-255,
-96,
-73,
-91,
-43,
-192,
-87,
-45,
-148,
-111,
-169,
-229,
-211,
-124,
-237,
-218,
-160,
-102,
-172,
-183,
-182,
-31,
-104,
-209,
-15,
-107,
-185,
-121,
-55,
-45,
-62,
-172,
-62,
-134,
-173,
-195,
-47,
-85,
-61,
-20,
-173,
-183,
-166,
-177,
-222,
-244,
-226,
-53,
-188,
-173,
-153,
-241,
-64,
-116,
-94,
-29,
-108,
-32,
-202,
-203,
-1,
-15,
-62,
-224,
-251,
-90,
-95,
-154,
-43,
-143,
-124,
-133,
-210,
-58,
-99,
-75,
-44,
-144,
-123,
-36,
-54,
-90,
-210,
-181,
-105,
-135,
-100,
-199,
-93,
-132,
-28,
-161,
-121,
-151,
-200,
-74,
-119,
-28,
-178,
-23,
-159,
-172,
-244,
-139,
-244,
-255,
-179,
-8,
-207,
-21,
-185,
-20,
-249,
-87,
-220,
-235,
-106,
-237,
-140,
-161,
-1,
-111,
-57,
-242,
-92,
-9,
-197,
-248,
-139,
-182,
-222,
-58,
-103,
-125,
-177,
-80,
-190,
-154,
-150,
-63,
-28,
-210,
-207,
-16,
-140,
-5,
-1,
-128,
-196,
-143,
-176,
-10,
-238,
-82,
-225,
-221,
-148,
-105,
-235,
-240,
-75,
-1,
-2,
-96,
-11,
-173,
-247,
-45,
-227,
-255,
-12,
-236,
-4,
-172,
-84,
-40,
-127,
-22,
-185,
-121,
-234,
-116,
-96,
-125,
-79,
-219,
-165,
-200,
-61,
-168,
-46,
-70,
-181,
-191,
-136,
-117,
-219,
-222,
-200,
-228,
-94,
-68,
-197,
-241,
-144,
-211,
-247,
-129,
-243,
-224,
-97,
-131,
-138,
-156,
-245,
-177,
-219,
-1,
-63,
-214,
-38,
-3,
-57,
-10,
-11,
-116,
-83,
-129,
-15,
-232,
-255,
-95,
-140,
-172,
-2,
-108,
-154,
-171,
-160,
-212,
-220,
-136,
-82,
-13,
-224,
-156,
-10,
-26,
-139,
-1,
-165,
-113,
-221,
-196,
-66,
-44,
-231,
-108,
-252,
-201,
-19,
-201,
-117,
-81,
-54,
-191,
-197,
-9,
-33,
-253,
-12,
-188,
-151,
-210,
-177,
-94,
-215,
-207,
-136,
-125,
-248,
-182,
-94,
-230,
-26,
-98,
-185,
-99,
-211,
-33,
-252,
-146,
-251,
-80,
-244,
-101,
-63,
-203,
-169,
-91,
-154,
-220,
-34,
-112,
-96,
-175,
-233,
-180,
-93,
-132,
-68,
-215,
-185,
-158,
-145,
-250,
-136,
-71,
-41,
-217,
-227,
-107,
-251,
-231,
-147,
-239,
-217,
-231,
-33,
-123,
-120,
-55,
-88,
-68,
-229,
-254,
-200,
-161,
-107,
-44,
-0,
-104,
-153,
-179,
-14,
-17,
-92,
-31,
-68,
-142,
-57,
-39,
-33,
-210,
-124,
-58,
-185,
-86,
-186,
-108,
-160,
-183,
-106,
-231,
-225,
-179,
-28,
-121,
-32,
-144,
-245,
-234,
-250,
-26,
-194,
-179,
-162,
-253,
-75,
-245,
-189,
-60,
-65,
-197,
-177,
-176,
-59,
-134,
-60,
-117,
-33,
-105,
-188,
-94,
-67,
-190,
-202,
-88,
-132,
-40,
-74,
-23,
-33,
-218,
-253,
-213,
-187,
-220,
-67,
-204,
-126,
-70,
-184,
-254,
-51,
-201,
-21,
-163,
-241,
-130,
-165,
-210,
-33,
-252,
-146,
-251,
-80,
-156,
-23,
-112,
-51,
-98,
-16,
-100,
-39,
-243,
-130,
-146,
-182,
-111,
-67,
-162,
-235,
-220,
-138,
-44,
-43,
-109,
-176,
-135,
-203,
-144,
-211,
-131,
-218,
-224,
-154,
-136,
-178,
-239,
-80,
-229,
-49,
-87,
-219,
-159,
-77,
-64,
-242,
-72,
-167,
-239,
-141,
-4,
-0,
-237,
-141,
-166,
-214,
-36,
-247,
-165,
-183,
-207,
-197,
-70,
-82,
-182,
-171,
-25,
-159,
-18,
-169,
-85,
-187,
-146,
-62,
-216,
-253,
-227,
-233,
-77,
-238,
-185,
-41,
-144,
-152,
-131,
-54,
-198,
-226,
-110,
-53,
-180,
-79,
-141,
-33,
-79,
-93,
-240,
-196,
-2,
-94,
-133,
-172,
-28,
-109,
-148,
-235,
-203,
-112,
-18,
-170,
-118,
-69,
-172,
-126,
-118,
-184,
-190,
-141,
-223,
-248,
-215,
-62,
-152,
-119,
-201,
-136,
-138,
-62,
-128,
-189,
-244,
-161,
-79,
-67,
-36,
-255,
-20,
-100,
-153,
-223,
-200,
-214,
-190,
-111,
-144,
-107,
-240,
-189,
-168,
-105,
-219,
-197,
-104,
-234,
-55,
-90,
-55,
-21,
-49,
-58,
-121,
-186,
-83,
-87,
-165,
-69,
-110,
-213,
-174,
-164,
-255,
-255,
-85,
-242,
-202,
-47,
-8,
-29,
-143,
-29,
-17,
-229,
-31,
-4,
-164,
-238,
-114,
-199,
-144,
-167,
-174,
-241,
-196,
-66,
-78,
-15,
-78,
-213,
-102,
-165,
-91,
-143,
-166,
-168,
-233,
-103,
-175,
-17,
-123,
-128,
-23,
-34,
-130,
-127,
-1,
-125,
-69,
-101,
-166,
-135,
-240,
-75,
-99,
-17,
-136,
-143,
-194,
-105,
-206,
-11,
-61,
-71,
-127,
-159,
-134,
-39,
-244,
-121,
-161,
-109,
-23,
-163,
-41,
-187,
-106,
-24,
-8,
-23,
-70,
-158,
-67,
-209,
-39,
-0,
-90,
-181,
-243,
-208,
-110,
-173,
-164,
-33,
-110,
-222,
-173,
-143,
-29,
-17,
-197,
-164,
-13,
-150,
-26,
-178,
-138,
-171,
-154,
-88,
-173,
-190,
-172,
-136,
-16,
-128,
-192,
-32,
-39,
-129,
-60,
-45,
-124,
-62,
-4,
-189,
-70,
-236,
-33,
-63,
-181,
-57,
-186,
-15,
-254,
-19,
-18,
-85,
-47,
-180,
-162,
-77,
-23,
-163,
-41,
-123,
-234,
-176,
-173,
-167,
-206,
-38,
-21,
-241,
-9,
-128,
-86,
-237,
-60,
-180,
-246,
-171,
-248,
-169,
-0,
-218,
-182,
-199,
-149,
-79,
-71,
-182,
-128,
-224,
-177,
-181,
-47,
-105,
-211,
-74,
-0,
-32,
-142,
-103,
-251,
-81,
-80,
-18,
-35,
-129,
-91,
-191,
-172,
-205,
-188,
-193,
-104,
-218,
-192,
-233,
-167,
-207,
-135,
-224,
-136,
-190,
-4,
-0,
-121,
-66,
-145,
-25,
-44,
-38,
-31,
-225,
-49,
-129,
-150,
-2,
-160,
-139,
-209,
-148,
-221,
-175,
-31,
-135,
-42,
-216,
-116,
-176,
-238,
-69,
-133,
-50,
-175,
-109,
-187,
-2,
-143,
-103,
-145,
-39,
-109,
-25,
-240,
-191,
-104,
-112,
-255,
-149,
-199,
-142,
-136,
-98,
-20,
-224,
-196,
-6,
-60,
-45,
-154,
-10,
-128,
-45,
-156,
-182,
-83,
-145,
-149,
-235,
-21,
-228,
-43,
-166,
-57,
-56,
-6,
-66,
-93,
-225,
-92,
-171,
-232,
-67,
-240,
-46,
-122,
-138,
-216,
-163,
-239,
-217,
-234,
-127,
-190,
-21,
-147,
-247,
-168,
-163,
-234,
-197,
-7,
-180,
-253,
-56,
-18,
-94,
-123,
-153,
-170,
-178,
-138,
-246,
-75,
-59,
-215,
-127,
-126,
-29,
-189,
-211,
-174,
-139,
-209,
-212,
-78,
-133,
-1,
-123,
-53,
-185,
-13,
-198,
-126,
-168,
-34,
-54,
-86,
-187,
-2,
-15,
-107,
-47,
-255,
-243,
-208,
-123,
-45,
-225,
-83,
-122,
-236,
-136,
-36,
-191,
-180,
-39,
-58,
-193,
-9,
-82,
-170,
-198,
-1,
-213,
-2,
-96,
-57,
-224,
-115,
-250,
-188,
-109,
-92,
-134,
-199,
-16,
-229,
-227,
-49,
-68,
-202,
-112,
-237,
-233,
-39,
-228,
-62,
-4,
-174,
-242,
-124,
-126,
-221,
-123,
-104,
-113,
-205,
-143,
-40,
-255,
-7,
-0,
-111,
-78,
-193,
-113,
-139,
-170,
-23,
-31,
-208,
-214,
-198,
-19,
-60,
-162,
-170,
-172,
-162,
-253,
-122,
-206,
-245,
-87,
-171,
-163,
-119,
-218,
-117,
-202,
-89,
-135,
-248,
-155,
-95,
-142,
-40,
-11,
-167,
-33,
-166,
-172,
-239,
-214,
-186,
-147,
-203,
-6,
-80,
-219,
-118,
-90,
-239,
-166,
-245,
-10,
-18,
-118,
-180,
-56,
-118,
-116,
-38,
-195,
-121,
-248,
-45,
-2,
-127,
-0,
-172,
-27,
-246,
-164,
-198,
-30,
-156,
-241,
-178,
-23,
-114,
-234,
-51,
-15,
-17,
-118,
-135,
-33,
-6,
-58,
-255,
-139,
-41,
-0,
-144,
-237,
-148,
-85,
-56,
-119,
-10,
-186,
-210,
-244,
-194,
-0,
-199,
-55,
-108,
-179,
-153,
-14,
-208,
-25,
-192,
-5,
-84,
-4,
-243,
-40,
-92,
-167,
-173,
-0,
-88,
-150,
-124,
-105,
-180,
-91,
-89,
-89,
-69,
-123,
-155,
-171,
-189,
-209,
-30,
-145,
-33,
-228,
-172,
-139,
-13,
-36,
-104,
-10,
-4,
-154,
-11,
-211,
-254,
-184,
-50,
-4,
-193,
-126,
-251,
-99,
-13,
-206,
-61,
-84,
-218,
-79,
-68,
-188,
-222,
-87,
-236,
-24,
-101,
-152,
-113,
-45,
-90,
-10,
-128,
-203,
-145,
-229,
-247,
-54,
-136,
-86,
-253,
-162,
-192,
-235,
-180,
-18,
-0,
-218,
-254,
-121,
-136,
-192,
-153,
-137,
-6,
-201,
-240,
-149,
-121,
-218,
-109,
-133,
-236,
-229,
-231,
-18,
-96,
-47,
-80,
-104,
-59,
-30,
-115,
-214,
-89,
-135,
-153,
-218,
-156,
-135,
-74,
-31,
-237,
-216,
-113,
-113,
-130,
-51,
-94,
-135,
-18,
-9,
-153,
-60,
-206,
-228,
-221,
-52,
-216,
-106,
-182,
-189,
-216,
-26,
-136,
-59,
-166,
-53,
-222,
-88,
-136,
-44,
-233,
-206,
-69,
-114,
-215,
-85,
-38,
-59,
-208,
-9,
-103,
-131,
-23,
-238,
-2,
-76,
-15,
-184,
-102,
-39,
-1,
-160,
-60,
-62,
-172,
-60,
-110,
-69,
-77,
-138,
-125,
-101,
-37,
-215,
-125,
-130,
-234,
-116,
-232,
-3,
-171,
-3,
-198,
-89,
-206,
-58,
-114,
-167,
-168,
-187,
-8,
-252,
-138,
-16,
-233,
-216,
-113,
-113,
-67,
-140,
-241,
-218,
-242,
-122,
-149,
-136,
-113,
-161,
-13,
-200,
-227,
-243,
-89,
-239,
-170,
-39,
-201,
-205,
-14,
-161,
-102,
-201,
-139,
-104,
-96,
-207,
-66,
-206,
-154,
-207,
-1,
-46,
-108,
-112,
-131,
-27,
-21,
-202,
-215,
-118,
-38,
-217,
-55,
-107,
-120,
-216,
-201,
-126,
-39,
-185,
-103,
-222,
-64,
-89,
-201,
-117,
-91,
-61,
-92,
-122,
-202,
-89,
-215,
-7,
-144,
-92,
-132,
-0,
-95,
-105,
-208,
-38,
-202,
-177,
-99,
-108,
-208,
-62,
-6,
-67,
-171,
-140,
-87,
-30,
-62,
-222,
-241,
-58,
-238,
-1,
-252,
-93,
-111,
-236,
-100,
-228,
-11,
-7,
-226,
-219,
-157,
-33,
-129,
-53,
-190,
-10,
-172,
-80,
-195,
-99,
-115,
-228,
-120,
-100,
-38,
-226,
-156,
-83,
-27,
-103,
-223,
-247,
-64,
-245,
-250,
-54,
-237,
-118,
-93,
-86,
-160,
-231,
-233,
-75,
-157,
-141,
-134,
-127,
-246,
-149,
-245,
-1,
-250,
-203,
-89,
-119,
-157,
-254,
-191,
-113,
-100,
-28,
-15,
-191,
-213,
-180,
-253,
-156,
-38,
-3,
-159,
-8,
-199,
-142,
-125,
-128,
-246,
-49,
-24,
-26,
-103,
-188,
-42,
-225,
-179,
-216,
-10,
-0,
-251,
-213,
-127,
-174,
-254,
-134,
-134,
-58,
-128,
-150,
-215,
-29,
-241,
-64,
-17,
-205,
-179,
-21,
-70,
-103,
-81,
-177,
-100,
-101,
-164,
-194,
-239,
-163,
-101,
-101,
-227,
-9,
-218,
-239,
-89,
-136,
-9,
-46,
-52,
-136,
-140,
-19,
-185,
-31,
-157,
-143,
-29,
-27,
-92,
-43,
-56,
-47,
-2,
-45,
-99,
-48,
-40,
-109,
-163,
-140,
-87,
-37,
-60,
-22,
-91,
-1,
-96,
-131,
-118,
-188,
-64,
-127,
-15,
-91,
-0,
-172,
-171,
-191,
-173,
-179,
-195,
-21,
-212,
-156,
-121,
-146,
-31,
-249,
-29,
-85,
-85,
-54,
-158,
-224,
-60,
-143,
-198,
-145,
-113,
-122,
-232,
-75,
-235,
-99,
-199,
-134,
-215,
-105,
-148,
-23,
-161,
-227,
-181,
-130,
-51,
-94,
-77,
-40,
-144,
-107,
-125,
-207,
-71,
-210,
-101,
-5,
-11,
-0,
-58,
-56,
-141,
-56,
-3,
-126,
-37,
-242,
-175,
-222,
-109,
-132,
-217,
-143,
-239,
-133,
-232,
-26,
-150,
-169,
-42,
-27,
-79,
-112,
-158,
-71,
-227,
-200,
-56,
-227,
-17,
-52,
-204,
-139,
-176,
-56,
-3,
-120,
-1,
-146,
-165,
-249,
-78,
-68,
-7,
-243,
-32,
-178,
-157,
-252,
-70,
-9,
-125,
-20,
-189,
-134,
-101,
-182,
-22,
-185,
-59,
-171,
-53,
-105,
-60,
-139,
-138,
-104,
-49,
-158,
-142,
-180,
-113,
-26,
-65,
-7,
-192,
-251,
-201,
-131,
-58,
-116,
-138,
-36,
-60,
-158,
-225,
-8,
-128,
-198,
-145,
-113,
-198,
-35,
-104,
-144,
-23,
-161,
-203,
-135,
-102,
-172,
-3,
-9,
-125,
-103,
-183,
-225,
-243,
-17,
-155,
-11,
-155,
-113,
-219,
-235,
-250,
-75,
-36,
-189,
-134,
-203,
-112,
-5,
-196,
-189,
-213,
-77,
-181,
-253,
-40,
-146,
-130,
-107,
-224,
-40,
-205,
-105,
-215,
-58,
-86,
-157,
-35,
-0,
-102,
-59,
-215,
-236,
-100,
-170,
-218,
-7,
-144,
-147,
-141,
-63,
-170,
-144,
-156,
-141,
-40,
-58,
-39,
-35,
-22,
-96,
-3,
-251,
-73,
-231,
-94,
-26,
-125,
-213,
-156,
-118,
-141,
-34,
-227,
-180,
-189,
-222,
-120,
-2,
-237,
-131,
-169,
-142,
-105,
-193,
-129,
-164,
-147,
-183,
-167,
-71,
-191,
-64,
-77,
-168,
-17,
-5,
-252,
-22,
-84,
-40,
-178,
-137,
-160,
-215,
-240,
-49,
-93,
-74,
-59,
-243,
-8,
-249,
-106,
-224,
-54,
-26,
-90,
-63,
-17,
-16,
-171,
-206,
-25,
-184,
-23,
-0,
-219,
-59,
-15,
-98,
-224,
-11,
-56,
-90,
-0,
-118,
-37,
-95,
-157,
-216,
-140,
-173,
-55,
-145,
-167,
-151,
-58,
-204,
-211,
-166,
-171,
-0,
-104,
-27,
-118,
-122,
-113,
-22,
-0,
-109,
-189,
-19,
-27,
-11,
-142,
-182,
-239,
-193,
-161,
-9,
-94,
-206,
-147,
-39,
-208,
-253,
-119,
-248,
-211,
-24,
-209,
-62,
-190,
-94,
-67,
-59,
-116,
-60,
-34,
-157,
-172,
-251,
-107,
-35,
-229,
-19,
-1,
-177,
-234,
-138,
-15,
-26,
-201,
-84,
-11,
-226,
-71,
-62,
-38,
-220,
-30,
-201,
-131,
-78,
-126,
-169,
-80,
-190,
-58,
-240,
-73,
-60,
-130,
-177,
-237,
-132,
-108,
-59,
-240,
-34,
-92,
-207,
-98,
-158,
-14,
-166,
-179,
-245,
-93,
-84,
-157,
-194,
-52,
-254,
-178,
-182,
-189,
-191,
-154,
-123,
-168,
-243,
-78,
-108,
-44,
-56,
-186,
-244,
-147,
-134,
-203,
-121,
-68,
-225,
-13,
-99,
-196,
-110,
-196,
-24,
-51,
-242,
-20,
-0,
-216,
-68,
-127,
-151,
-133,
-116,
-110,
-29,
-171,
-206,
-247,
-160,
-201,
-67,
-72,
-159,
-67,
-172,
-160,
-135,
-35,
-175,
-217,
-52,
-181,
-182,
-53,
-138,
-121,
-69,
-131,
-107,
-88,
-12,
-91,
-0,
-88,
-204,
-67,
-38,
-227,
-57,
-192,
-158,
-148,
-196,
-246,
-115,
-232,
-109,
-214,
-224,
-171,
-16,
-133,
-156,
-197,
-69,
-148,
-164,
-254,
-102,
-20,
-190,
-172,
-37,
-253,
-104,
-27,
-76,
-181,
-84,
-112,
-116,
-120,
-15,
-141,
-151,
-243,
-228,
-199,
-171,
-3,
-17,
-164,
-70,
-13,
-218,
-161,
-227,
-245,
-255,
-214,
-231,
-218,
-151,
-157,
-167,
-83,
-172,
-58,
-223,
-131,
-70,
-190,
-172,
-54,
-208,
-231,
-126,
-145,
-239,
-171,
-77,
-106,
-109,
-187,
-2,
-186,
-140,
-26,
-115,
-104,
-167,
-141,
-197,
-176,
-5,
-64,
-217,
-68,
-190,
-20,
-207,
-177,
-170,
-175,
-159,
-72,
-210,
-137,
-221,
-201,
-183,
-56,
-7,
-148,
-244,
-117,
-216,
-95,
-214,
-40,
-65,
-81,
-29,
-126,
-85,
-110,
-205,
-109,
-223,
-67,
-227,
-229,
-60,
-185,
-123,
-121,
-155,
-15,
-76,
-41,
-66,
-121,
-89,
-134,
-199,
-2,
-219,
-162,
-199,
-103,
-202,
-227,
-120,
-36,
-20,
-180,
-205,
-246,
-251,
-27,
-79,
-187,
-78,
-78,
-35,
-101,
-15,
-26,
-209,
-7,
-44,
-66,
-190,
-190,
-47,
-109,
-116,
-51,
-229,
-215,
-106,
-155,
-90,
-251,
-185,
-228,
-89,
-102,
-108,
-222,
-185,
-186,
-120,
-122,
-22,
-163,
-182,
-5,
-96,
-112,
-34,
-127,
-183,
-73,
-63,
-117,
-76,
-0,
-220,
-212,
-240,
-30,
-250,
-248,
-178,
-182,
-254,
-208,
-208,
-206,
-173,
-185,
-109,
-63,
-27,
-47,
-231,
-201,
-87,
-77,
-219,
-54,
-104,
-99,
-97,
-5,
-126,
-55,
-7,
-33,
-135,
-225,
-28,
-242,
-227,
-192,
-69,
-78,
-249,
-36,
-60,
-103,
-243,
-116,
-116,
-26,
-169,
-121,
-208,
-54,
-118,
-253,
-77,
-68,
-8,
-132,
-64,
-135,
-212,
-218,
-200,
-151,
-237,
-235,
-140,
-60,
-33,
-249,
-95,
-217,
-75,
-115,
-104,
-70,
-93,
-7,
-64,
-197,
-68,
-174,
-105,
-183,
-187,
-29,
-19,
-13,
-239,
-161,
-143,
-47,
-107,
-219,
-96,
-170,
-93,
-221,
-154,
-155,
-246,
-179,
-241,
-114,
-30,
-177,
-244,
-132,
-6,
-89,
-172,
-171,
-222,
-91,
-43,
-32,
-190,
-241,
-127,
-67,
-150,
-222,
-118,
-73,
-50,
-13,
-56,
-29,
-217,
-67,
-150,
-37,
-88,
-28,
-147,
-78,
-35,
-125,
-1,
-9,
-172,
-241,
-254,
-194,
-160,
-218,
-217,
-67,
-215,
-234,
-5,
-117,
-24,
-120,
-165,
-215,
-163,
-98,
-34,
-215,
-180,
-219,
-77,
-235,
-158,
-40,
-233,
-235,
-48,
-191,
-172,
-109,
-131,
-169,
-182,
-21,
-28,
-109,
-251,
-217,
-102,
-57,
-127,
-176,
-182,
-57,
-191,
-65,
-155,
-210,
-247,
-22,
-5,
-202,
-252,
-248,
-0,
-186,
-49,
-233,
-52,
-210,
-55,
-244,
-30,
-109,
-16,
-200,
-41,
-158,
-250,
-126,
-95,
-80,
-131,
-235,
-81,
-49,
-145,
-107,
-218,
-157,
-160,
-117,
-255,
-242,
-212,
-13,
-251,
-203,
-218,
-54,
-152,
-106,
-91,
-193,
-81,
-213,
-207,
-23,
-87,
-180,
-107,
-179,
-156,
-95,
-151,
-60,
-143,
-193,
-33,
-56,
-241,
-26,
-145,
-20,
-110,
-187,
-86,
-244,
-111,
-212,
-5,
-192,
-208,
-156,
-70,
-186,
-160,
-237,
-192,
-171,
-225,
-249,
-82,
-109,
-214,
-232,
-203,
-218,
-7,
-170,
-174,
-71,
-245,
-68,
-30,
-104,
-135,
-56,
-84,
-125,
-158,
-92,
-119,
-240,
-78,
-79,
-187,
-62,
-190,
-172,
-85,
-19,
-171,
-109,
-48,
-213,
-182,
-130,
-195,
-222,
-187,
-47,
-252,
-247,
-59,
-42,
-218,
-53,
-94,
-206,
-107,
-187,
-247,
-145,
-219,
-220,
-204,
-67,
-156,
-192,
-102,
-233,
-239,
-255,
-120,
-232,
-75,
-223,
-119,
-20,
-40,
-243,
-227,
-3,
-105,
-135,
-226,
-52,
-210,
-5,
-53,
-3,
-175,
-106,
-192,
-110,
-128,
-132,
-151,
-222,
-160,
-80,
-254,
-52,
-114,
-39,
-150,
-43,
-43,
-174,
-55,
-106,
-2,
-128,
-176,
-137,
-108,
-97,
-149,
-73,
-215,
-49,
-50,
-6,
-196,
-129,
-37,
-215,
-107,
-251,
-101,
-109,
-59,
-177,
-218,
-6,
-83,
-109,
-43,
-56,
-172,
-137,
-173,
-47,
-252,
-247,
-209,
-21,
-237,
-26,
-47,
-231,
-157,
-182,
-155,
-35,
-185,
-13,
-239,
-65,
-182,
-18,
-51,
-144,
-147,
-167,
-15,
-121,
-104,
-7,
-222,
-247,
-98,
-5,
-106,
-34,
-215,
-208,
-208,
-17,
-194,
-121,
-96,
-93,
-194,
-78,
-63,
-128,
-104,
-121,
-175,
-36,
-15,
-213,
-52,
-11,
-216,
-186,
-226,
-122,
-195,
-22,
-0,
-77,
-39,
-114,
-17,
-179,
-144,
-19,
-143,
-147,
-80,
-159,
-251,
-146,
-118,
-109,
-191,
-172,
-173,
-38,
-150,
-214,
-55,
-254,
-208,
-208,
-94,
-112,
-216,
-163,
-204,
-98,
-248,
-239,
-247,
-144,
-239,
-243,
-125,
-237,
-26,
-47,
-231,
-219,
-192,
-243,
-222,
-6,
-16,
-227,
-58,
-189,
-130,
-134,
-185,
-231,
-11,
-109,
-27,
-57,
-66,
-56,
-207,
-165,
-75,
-216,
-233,
-71,
-145,
-175,
-198,
-227,
-136,
-31,
-192,
-49,
-192,
-38,
-53,
-215,
-27,
-182,
-0,
-176,
-8,
-157,
-200,
-173,
-250,
-73,
-251,
-47,
-107,
-171,
-137,
-213,
-5,
-180,
-19,
-28,
-175,
-113,
-238,
-195,
-134,
-255,
-182,
-17,
-160,
-190,
-133,
-90,
-250,
-149,
-92,
-175,
-209,
-114,
-190,
-229,
-61,
-89,
-196,
-57,
-6,
-28,
-13,
-208,
-49,
-247,
-60,
-13,
-28,
-33,
-156,
-7,
-54,
-148,
-164,
-142,
-109,
-39,
-214,
-176,
-175,
-215,
-161,
-93,
-219,
-47,
-107,
-235,
-137,
-53,
-108,
-0,
-111,
-64,
-220,
-227,
-167,
-105,
-191,
-174,
-65,
-183,
-60,
-168,
-0,
-172,
-104,
-27,
-188,
-156,
-111,
-217,
-183,
-161,
-142,
-175,
-94,
-64,
-156,
-220,
-243,
-65,
-142,
-16,
-73,
-0,
-196,
-109,
-167,
-109,
-219,
-230,
-55,
-104,
-61,
-177,
-198,
-27,
-144,
-128,
-172,
-179,
-233,
-47,
-65,
-73,
-52,
-59,
-128,
-149,
-129,
-207,
-34,
-182,
-223,
-143,
-35,
-75,
-151,
-59,
-145,
-163,
-174,
-210,
-188,
-238,
-5,
-30,
-63,
-69,
-246,
-63,
-191,
-8,
-164,
-239,
-148,
-123,
-190,
-9,
-70,
-81,
-0,
-148,
-46,
-209,
-208,
-8,
-202,
-145,
-175,
-55,
-52,
-1,
-144,
-80,
-15,
-196,
-106,
-116,
-145,
-10,
-188,
-82,
-33,
-64,
-190,
-69,
-216,
-190,
-170,
-204,
-169,
-139,
-46,
-0,
-174,
-115,
-152,
-62,
-142,
-132,
-8,
-179,
-150,
-128,
-15,
-17,
-96,
-3,
-79,
-238,
-241,
-52,
-51,
-74,
-167,
-34,
-162,
-70,
-0,
-148,
-30,
-63,
-69,
-184,
-94,
-21,
-62,
-223,
-195,
-245,
-146,
-0,
-24,
-67,
-0,
-222,
-77,
-158,
-133,
-250,
-218,
-10,
-186,
-187,
-138,
-239,
-193,
-87,
-230,
-212,
-69,
-23,
-0,
-123,
-0,
-63,
-193,
-201,
-59,
-142,
-36,
-147,
-188,
-64,
-47,
-116,
-106,
-0,
-143,
-35,
-145,
-229,
-78,
-200,
-254,
-29,
-90,
-68,
-191,
-173,
-153,
-200,
-85,
-202,
-188,
-86,
-199,
-79,
-139,
-59,
-22,
-87,
-1,
-64,
-131,
-64,
-163,
-67,
-234,
-207,
-165,
-148,
-88,
-84,
-106,
-189,
-61,
-213,
-90,
-167,
-170,
-204,
-169,
-27,
-206,
-123,
-3,
-94,
-168,
-23,
-122,
-44,
-50,
-95,
-104,
-17,
-253,
-182,
-131,
-0,
-104,
-125,
-252,
-180,
-56,
-99,
-49,
-22,
-0,
-149,
-129,
-70,
-145,
-232,
-87,
-7,
-33,
-39,
-37,
-115,
-144,
-237,
-238,
-161,
-212,
-132,
-190,
-111,
-216,
-135,
-157,
-145,
-21,
-192,
-1,
-122,
-141,
-210,
-108,
-201,
-72,
-252,
-5,
-112,
-76,
-238,
-125,
-101,
-78,
-221,
-208,
-4,
-192,
-202,
-118,
-178,
-70,
-230,
-107,
-209,
-40,
-250,
-109,
-7,
-1,
-48,
-244,
-227,
-167,
-197,
-25,
-250,
-184,
-46,
-215,
-255,
-175,
-173,
-19,
-238,
-110,
-68,
-195,
-127,
-123,
-69,
-187,
-224,
-88,
-12,
-200,
-10,
-244,
-100,
-100,
-85,
-248,
-36,
-240,
-59,
-2,
-130,
-196,
-16,
-16,
-104,
-84,
-199,
-195,
-34,
-36,
-120,
-201,
-151,
-201,
-147,
-167,
-252,
-182,
-142,
-127,
-40,
-144,
-143,
-217,
-124,
-228,
-68,
-228,
-103,
-84,
-56,
-180,
-33,
-39,
-6,
-211,
-234,
-202,
-156,
-58,
-139,
-126,
-143,
-1,
-17,
-141,
-45,
-212,
-36,
-208,
-68,
-206,
-203,
-33,
-48,
-96,
-165,
-115,
-3,
-141,
-162,
-223,
-118,
-16,
-0,
-227,
-230,
-248,
-105,
-60,
-64,
-159,
-219,
-61,
-72,
-26,
-180,
-41,
-250,
-123,
-33,
-162,
-60,
-190,
-180,
-164,
-77,
-112,
-44,
-6,
-157,
-252,
-54,
-83,
-213,
-249,
-72,
-128,
-218,
-121,
-200,
-74,
-174,
-82,
-8,
-16,
-16,
-104,
-20,
-73,
-152,
-242,
-170,
-66,
-217,
-121,
-68,
-254,
-208,
-133,
-2,
-241,
-124,
-189,
-163,
-174,
-204,
-169,
-171,
-69,
-140,
-78,
-173,
-64,
-238,
-240,
-241,
-165,
-26,
-218,
-87,
-148,
-77,
-218,
-154,
-27,
-104,
-20,
-253,
-214,
-105,
-215,
-38,
-160,
-68,
-47,
-199,
-79,
-180,
-76,
-87,
-165,
-180,
-65,
-95,
-68,
-242,
-56,
-141,
-62,
-188,
-172,
-77,
-191,
-187,
-64,
-175,
-59,
-31,
-17,
-164,
-255,
-3,
-222,
-132,
-38,
-74,
-197,
-241,
-11,
-112,
-232,
-27,
-197,
-98,
-64,
-143,
-17,
-113,
-50,
-60,
-35,
-66,
-124,
-62,
-112,
-82,
-79,
-247,
-116,
-61,
-48,
-169,
-15,
-222,
-227,
-14,
-72,
-162,
-208,
-203,
-245,
-37,
-92,
-74,
-69,
-22,
-92,
-196,
-2,
-236,
-159,
-74,
-91,
-155,
-161,
-69,
-219,
-88,
-180,
-141,
-126,
-59,
-148,
-227,
-188,
-16,
-208,
-62,
-93,
-85,
-163,
-232,
-68,
-72,
-172,
-185,
-67,
-157,
-103,
-240,
-123,
-224,
-67,
-68,
-136,
-149,
-208,
-20,
-78,
-31,
-30,
-194,
-217,
-82,
-85,
-208,
-55,
-138,
-197,
-160,
-207,
-99,
-96,
-43,
-1,
-252,
-133,
-10,
-101,
-90,
-91,
-32,
-62,
-19,
-139,
-128,
-157,
-98,
-243,
-30,
-119,
-64,
-210,
-45,
-93,
-175,
-47,
-248,
-106,
-52,
-174,
-89,
-5,
-189,
-85,
-162,
-205,
-2,
-158,
-31,
-120,
-141,
-86,
-19,
-121,
-140,
-10,
-128,
-198,
-233,
-170,
-104,
-25,
-157,
-72,
-219,
-90,
-84,
-42,
-128,
-60,
-253,
-170,
-92,
-153,
-208,
-192,
-191,
-194,
-233,
-195,
-103,
-171,
-232,
-218,
-66,
-251,
-112,
-139,
-167,
-252,
-12,
-60,
-251,
-98,
-58,
-172,
-144,
-144,
-192,
-174,
-243,
-129,
-61,
-99,
-222,
-195,
-184,
-5,
-18,
-204,
-16,
-224,
-6,
-106,
-164,
-59,
-146,
-4,
-19,
-100,
-73,
-61,
-96,
-176,
-80,
-209,
-174,
-15,
-1,
-16,
-253,
-60,
-191,
-47,
-208,
-45,
-58,
-81,
-168,
-0,
-104,
-180,
-50,
-161,
-129,
-127,
-133,
-211,
-135,
-94,
-146,
-175,
-146,
-7,
-134,
-117,
-21,
-196,
-47,
-71,
-156,
-144,
-188,
-71,
-210,
-180,
-88,
-33,
-1,
-223,
-70,
-20,
-140,
-111,
-9,
-236,
-215,
-146,
-218,
-230,
-38,
-196,
-232,
-237,
-33,
-42,
-182,
-122,
-228,
-130,
-125,
-131,
-50,
-154,
-49,
-7,
-242,
-176,
-87,
-219,
-212,
-208,
-109,
-133,
-44,
-155,
-230,
-1,
-219,
-53,
-188,
-70,
-91,
-1,
-48,
-225,
-207,
-243,
-27,
-8,
-128,
-54,
-43,
-147,
-32,
-255,
-138,
-170,
-247,
-87,
-66,
-223,
-232,
-11,
-13,
-172,
-135,
-248,
-22,
-44,
-2,
-206,
-5,
-206,
-68,
-4,
-218,
-163,
-192,
-179,
-107,
-174,
-85,
-251,
-124,
-116,
-34,
-255,
-82,
-39,
-114,
-208,
-170,
-85,
-219,
-125,
-67,
-121,
-47,
-66,
-244,
-70,
-183,
-0,
-191,
-175,
-160,
-183,
-31,
-211,
-198,
-201,
-68,
-201,
-243,
-100,
-222,
-83,
-65,
-211,
-40,
-194,
-117,
-232,
-133,
-237,
-36,
-27,
-80,
-230,
-20,
-232,
-78,
-87,
-186,
-111,
-182,
-184,
-70,
-233,
-0,
-98,
-140,
-156,
-231,
-59,
-125,
-44,
-53,
-89,
-174,
-24,
-212,
-79,
-33,
-70,
-95,
-74,
-174,
-217,
-203,
-25,
-48,
-1,
-254,
-21,
-85,
-239,
-175,
-162,
-77,
-163,
-47,
-52,
-34,
-4,
-78,
-35,
-215,
-145,
-156,
-74,
-128,
-89,
-122,
-200,
-243,
-65,
-182,
-18,
-51,
-129,
-111,
-34,
-251,
-127,
-247,
-95,
-105,
-106,
-58,
-242,
-120,
-153,
-95,
-112,
-202,
-202,
-66,
-174,
-175,
-134,
-156,
-70,
-52,
-122,
-78,
-78,
-251,
-255,
-105,
-91,
-111,
-162,
-91,
-90,
-68,
-184,
-142,
-10,
-231,
-230,
-26,
-101,
-12,
-210,
-182,
-165,
-3,
-136,
-49,
-114,
-158,
-239,
-244,
-177,
-106,
-9,
-105,
-17,
-124,
-30,
-75,
-0,
-2,
-251,
-53,
-106,
-198,
-59,
-85,
-239,
-175,
-65,
-219,
-190,
-4,
-88,
-45,
-255,
-154,
-199,
-255,
-137,
-138,
-118,
-214,
-205,
-183,
-46,
-148,
-252,
-81,
-228,
-113,
-1,
-138,
-152,
-26,
-112,
-15,
-171,
-146,
-155,
-226,
-15,
-196,
-23,
-164,
-131,
-14,
-201,
-69,
-87,
-103,
-28,
-59,
-1,
-163,
-90,
-9,
-214,
-224,
-96,
-99,
-204,
-91,
-141,
-49,
-47,
-55,
-198,
-220,
-13,
-220,
-108,
-140,
-89,
-195,
-24,
-179,
-158,
-49,
-230,
-219,
-198,
-152,
-47,
-27,
-99,
-158,
-214,
-150,
-57,
-34,
-205,
-247,
-51,
-198,
-236,
-229,
-20,
-79,
-7,
-238,
-50,
-198,
-252,
-57,
-203,
-178,
-50,
-251,
-253,
-221,
-179,
-44,
-187,
-174,
-225,
-229,
-174,
-51,
-198,
-244,
-226,
-55,
-161,
-247,
-49,
-191,
-164,
-250,
-229,
-89,
-150,
-85,
-234,
-25,
-22,
-119,
-100,
-89,
-214,
-56,
-225,
-12,
-146,
-164,
-198,
-158,
-134,
-205,
-171,
-33,
-127,
-194,
-24,
-115,
-184,
-49,
-102,
-127,
-253,
-253,
-75,
-45,
-51,
-198,
-152,
-233,
-1,
-151,
-219,
-198,
-24,
-147,
-25,
-99,
-174,
-206,
-178,
-204,
-103,
-202,
-252,
-87,
-173,
-223,
-42,
-203,
-178,
-73,
-218,
-191,
-173,
-141,
-49,
-167,
-107,
-93,
-187,
-163,
-97,
-100,
-95,
-52,
-27,
-56,
-60,
-128,
-246,
-30,
-149,
-80,
-3,
-38,
-187,
-1,
-109,
-75,
-191,
-32,
-140,
-210,
-121,
-190,
-182,
-63,
-217,
-233,
-219,
-44,
-196,
-168,
-228,
-94,
-253,
-253,
-231,
-138,
-251,
-8,
-254,
-154,
-181,
-105,
-211,
-166,
-61,
-13,
-150,
-220,
-52,
-223,
-163,
-151,
-190,
-191,
-88,
-253,
-111,
-139,
-62,
-248,
-35,
-123,
-255,
-219,
-125,
-15,
-39,
-176,
-47,
-141,
-158,
-19,
-98,
-57,
-8,
-145,
-34,
-8,
-53,
-185,
-176,
-117,
-65,
-172,
-61,
-107,
-69,
-156,
-127,
-64,
-38,
-224,
-250,
-78,
-249,
-166,
-136,
-107,
-240,
-238,
-189,
-118,
-54,
-50,
-144,
-100,
-36,
-22,
-135,
-224,
-164,
-196,
-2,
-214,
-199,
-227,
-206,
-217,
-102,
-176,
-117,
-29,
-160,
-77,
-219,
-135,
-210,
-211,
-76,
-96,
-180,
-26,
-216,
-109,
-250,
-63,
-22,
-248,
-3,
-63,
-208,
-127,
-22,
-71,
-217,
-178,
-192,
-190,
-52,
-21,
-0,
-183,
-234,
-191,
-202,
-208,
-120,
-209,
-65,
-179,
-21,
-192,
-218,
-136,
-253,
-183,
-197,
-253,
-228,
-81,
-97,
-64,
-189,
-253,
-198,
-11,
-200,
-163,
-231,
-94,
-220,
-160,
-77,
-227,
-193,
-214,
-117,
-128,
-54,
-109,
-223,
-7,
-253,
-48,
-5,
-0,
-13,
-61,
-251,
-186,
-62,
-223,
-64,
-222,
-161,
-167,
-31,
-173,
-159,
-83,
-95,
-125,
-114,
-177,
-68,
-177,
-32,
-203,
-178,
-189,
-179,
-44,
-123,
-122,
-150,
-101,
-159,
-169,
-107,
-156,
-101,
-217,
-84,
-35,
-123,
-141,
-195,
-141,
-49,
-83,
-140,
-236,
-197,
-151,
-48,
-198,
-92,
-108,
-140,
-249,
-156,
-49,
-102,
-219,
-166,
-29,
-26,
-101,
-88,
-101,
-75,
-173,
-9,
-239,
-68,
-71,
-150,
-227,
-182,
-33,
-92,
-238,
-0,
-99,
-204,
-242,
-198,
-152,
-169,
-198,
-152,
-104,
-65,
-84,
-18,
-186,
-43,
-1,
-77,
-150,
-101,
-15,
-25,
-99,
-62,
-171,
-255,
-198,
-59,
-236,
-17,
-211,
-141,
-45,
-218,
-94,
-75,
-201,
-118,
-176,
-141,
-194,
-41,
-65,
-128,
-216,
-34,
-124,
-220,
-136,
-2,
-109,
-199,
-44,
-203,
-238,
-26,
-229,
-46,
-45,
-86,
-24,
-90,
-72,
-174,
-50,
-32,
-249,
-6,
-239,
-48,
-34,
-225,
-175,
-204,
-178,
-204,
-119,
-228,
-81,
-171,
-212,
-43,
-78,
-178,
-54,
-109,
-140,
-49,
-118,
-207,
-239,
-77,
-131,
-94,
-131,
-54,
-26,
-253,
-36,
-52,
-234,
-241,
-27,
-99,
-204,
-195,
-198,
-152,
-183,
-182,
-56,
-101,
-73,
-168,
-193,
-168,
-11,
-0,
-211,
-108,
-121,
-215,
-102,
-146,
-53,
-105,
-51,
-199,
-136,
-16,
-88,
-174,
-142,
-208,
-131,
-49,
-117,
-12,
-184,
-184,
-32,
-203,
-178,
-168,
-193,
-52,
-19,
-2,
-129,
-132,
-8,
-251,
-15,
-240,
-120,
-143,
-215,
-168,
-13,
-220,
-160,
-116,
-67,
-81,
-180,
-33,
-102,
-179,
-0,
-123,
-213,
-83,
-15,
-183,
-111,
-218,
-238,
-29,
-12,
-106,
-233,
-63,
-136,
-115,
-90,
-17,
-227,
-122,
-125,
-42,
-209,
-198,
-59,
-255,
-166,
-10,
-55,
-52,
-206,
-4,
-145,
-82,
-220,
-199,
-232,
-147,
-139,
-1,
-37,
-160,
-50,
-220,
-223,
-24,
-243,
-43,
-99,
-204,
-43,
-141,
-49,
-247,
-55,
-232,
-64,
-211,
-7,
-110,
-151,
-119,
-219,
-141,
-145,
-229,
-221,
-213,
-250,
-119,
-151,
-74,
-170,
-81,
-0,
-98,
-216,
-115,
-134,
-49,
-230,
-11,
-78,
-241,
-251,
-141,
-49,
-39,
-25,
-99,
-134,
-103,
-254,
-153,
-208,
-20,
-183,
-234,
-223,
-49,
-55,
-166,
-140,
-41,
-17,
-0,
-198,
-152,
-125,
-245,
-239,
-39,
-178,
-44,
-123,
-97,
-9,
-77,
-103,
-100,
-89,
-182,
-105,
-150,
-101,
-107,
-103,
-89,
-118,
-117,
-61,
-245,
-80,
-96,
-195,
-65,
-189,
-17,
-248,
-46,
-35,
-227,
-179,
-173,
-5,
-188,
-125,
-148,
-250,
-101,
-178,
-44,
-91,
-144,
-149,
-99,
-66,
-91,
-245,
-89,
-216,
-21,
-146,
-83,
-180,
-127,
-200,
-10,
-169,
-103,
-28,
-235,
-244,
-229,
-102,
-196,
-132,
-189,
-214,
-20,
-120,
-84,
-225,
-124,
-209,
-155,
-250,
-246,
-143,
-153,
-37,
-93,
-219,
-62,
-145,
-59,
-56,
-129,
-156,
-61,
-223,
-64,
-30,
-154,
-42,
-74,
-150,
-214,
-190,
-159,
-87,
-215,
-235,
-141,
-197,
-247,
-25,
-192,
-115,
-40,
-17,
-147,
-28,
-158,
-161,
-91,
-128,
-12,
-216,
-95,
-199,
-209,
-108,
-196,
-122,
-213,
-27,
-50,
-109,
-88,
-125,
-114,
-81,
-167,
-4,
-12,
-138,
-239,
-215,
-4,
-208,
-74,
-59,
-63,
-76,
-236,
-98,
-140,
-249,
-146,
-49,
-102,
-79,
-99,
-204,
-250,
-198,
-24,
-171,
-132,
-186,
-195,
-24,
-83,
-26,
-213,
-53,
-161,
-30,
-192,
-59,
-140,
-49,
-110,
-34,
-213,
-253,
-129,
-179,
-140,
-49,
-167,
-103,
-89,
-214,
-41,
-22,
-95,
-150,
-101,
-11,
-140,
-216,
-198,
-143,
-41,
-100,
-89,
-134,
-17,
-255,
-149,
-131,
-71,
-187,
-47,
-62,
-140,
-230,
-41,
-192,
-176,
-142,
-205,
-26,
-181,
-201,
-178,
-108,
-190,
-49,
-230,
-123,
-198,
-152,
-239,
-57,
-194,
-106,
-165,
-44,
-203,
-234,
-28,
-56,
-142,
-163,
-36,
-33,
-74,
-150,
-101,
-91,
-213,
-180,
-93,
-236,
-65,
-174,
-195,
-112,
-241,
-126,
-253,
-247,
-114,
-99,
-76,
-218,
-198,
-140,
-2,
-70,
-83,
-0,
-12,
-235,
-216,
-44,
-198,
-81,
-91,
-101,
-108,
-63,
-69,
-155,
-229,
-236,
-132,
-17,
-26,
-99,
-245,
-11,
-221,
-20,
-139,
-155,
-125,
-198,
-128,
-0,
-96,
-100,
-18,
-133,
-97,
-186,
-249,
-134,
-160,
-141,
-208,
-104,
-211,
-198,
-24,
-19,
-246,
-178,
-59,
-14,
-136,
-94,
-117,
-0,
-125,
-46,
-185,
-19,
-22,
-51,
-0,
-203,
-3,
-47,
-3,
-254,
-165,
-10,
-133,
-191,
-53,
-104,
-27,
-172,
-212,
-25,
-150,
-210,
-172,
-111,
-69,
-214,
-88,
-71,
-27,
-165,
-24,
-45,
-237,
-12,
-198,
-2,
-144,
-0,
-26,
-199,
-106,
-191,
-79,
-243,
-212,
-15,
-96,
-52,
-250,
-233,
-244,
-103,
-105,
-228,
-68,
-224,
-78,
-26,
-4,
-240,
-232,
-179,
-67,
-22,
-55,
-2,
-95,
-1,
-130,
-131,
-106,
-36,
-1,
-48,
-254,
-209,
-70,
-96,
-140,
-21,
-0,
-239,
-69,
-162,
-83,
-221,
-133,
-68,
-225,
-169,
-18,
-0,
-87,
-17,
-51,
-115,
-78,
-75,
-0,
-27,
-58,
-125,
-26,
-181,
-227,
-101,
-215,
-14,
-224,
-127,
-70,
-246,
-202,
-235,
-25,
-99,
-94,
-104,
-196,
-60,
-55,
-97,
-130,
-160,
-15,
-59,
-3,
-224,
-37,
-200,
-209,
-151,
-215,
-156,
-151,
-6,
-97,
-200,
-107,
-176,
-185,
-49,
-230,
-95,
-198,
-152,
-16,
-107,
-187,
-247,
-103,
-89,
-182,
-149,
-79,
-199,
-226,
-147,
-124,
-109,
-251,
-76,
-189,
-11,
-243,
-221,
-198,
-152,
-43,
-141,
-49,
-119,
-26,
-99,
-46,
-15,
-232,
-119,
-255,
-0,
-86,
-68,
-242,
-145,
-1,
-252,
-179,
-65,
-187,
-224,
-175,
-109,
-197,
-87,
-166,
-234,
-161,
-7,
-243,
-239,
-210,
-70,
-219,
-5,
-199,
-210,
-71,
-76,
-152,
-255,
-3,
-236,
-209,
-228,
-26,
-19,
-5,
-192,
-31,
-144,
-184,
-118,
-231,
-251,
-132,
-0,
-13,
-194,
-144,
-55,
-184,
-38,
-84,
-175,
-0,
-74,
-207,
-202,
-201,
-227,
-56,
-218,
-73,
-238,
-27,
-139,
-65,
-125,
-166,
-38,
-57,
-233,
-152,
-5,
-176,
-142,
-243,
-176,
-86,
-10,
-108,
-19,
-60,
-217,
-28,
-218,
-54,
-65,
-52,
-135,
-33,
-0,
-130,
-99,
-233,
-147,
-39,
-79,
-1,
-248,
-98,
-13,
-223,
-37,
-144,
-40,
-67,
-143,
-35,
-65,
-83,
-14,
-68,
-98,
-204,
-21,
-233,
-62,
-15,
-92,
-134,
-132,
-230,
-126,
-20,
-248,
-59,
-240,
-188,
-38,
-247,
-48,
-86,
-128,
-100,
-198,
-61,
-205,
-190,
-239,
-18,
-154,
-160,
-48,
-228,
-13,
-174,
-9,
-45,
-5,
-128,
-67,
-91,
-23,
-146,
-174,
-178,
-207,
-4,
-250,
-184,
-116,
-5,
-18,
-54,
-124,
-54,
-240,
-75,
-79,
-221,
-47,
-144,
-237,
-208,
-79,
-219,
-48,
-182,
-216,
-176,
-33,
-125,
-223,
-58,
-128,
-94,
-133,
-134,
-182,
-107,
-20,
-75,
-31,
-201,
-42,
-3,
-240,
-64,
-13,
-223,
-47,
-34,
-81,
-139,
-191,
-160,
-255,
-230,
-227,
-137,
-62,
-139,
-164,
-146,
-62,
-20,
-120,
-63,
-176,
-59,
-146,
-120,
-226,
-162,
-10,
-190,
-149,
-202,
-36,
-74,
-80,
-255,
-36,
-226,
-1,
-73,
-45,
-87,
-26,
-98,
-142,
-128,
-48,
-228,
-13,
-174,
-5,
-61,
-11,
-128,
-186,
-62,
-19,
-144,
-156,
-180,
-105,
-159,
-74,
-218,
-219,
-240,
-125,
-3,
-238,
-235,
-78,
-221,
-147,
-109,
-24,
-55,
-234,
-88,
-147,
-201,
-214,
-132,
-214,
-211,
-166,
-20,
-49,
-174,
-211,
-6,
-192,
-243,
-235,
-6,
-139,
-210,
-93,
-11,
-252,
-213,
-249,
-253,
-87,
-60,
-166,
-197,
-158,
-118,
-135,
-81,
-145,
-177,
-150,
-26,
-101,
-18,
-178,
-162,
-25,
-248,
-231,
-161,
-179,
-193,
-80,
-127,
-94,
-211,
-159,
-208,
-231,
-191,
-51,
-178,
-2,
-56,
-64,
-175,
-57,
-20,
-43,
-74,
-237,
-78,
-239,
-2,
-32,
-6,
-154,
-244,
-169,
-164,
-253,
-145,
-200,
-10,
-224,
-23,
-158,
-58,
-187,
-58,
-56,
-178,
-247,
-142,
-53,
-153,
-108,
-195,
-154,
-152,
-195,
-66,
-232,
-96,
-65,
-210,
-79,
-29,
-230,
-252,
-62,
-12,
-24,
-176,
-179,
-64,
-146,
-178,
-254,
-12,
-177,
-29,
-159,
-129,
-198,
-134,
-175,
-224,
-107,
-87,
-0,
-141,
-226,
-193,
-123,
-248,
-172,
-6,
-220,
-167,
-183,
-242,
-158,
-10,
-186,
-80,
-129,
-50,
-3,
-89,
-229,
-76,
-209,
-251,
-25,
-138,
-82,
-121,
-34,
-9,
-128,
-24,
-24,
-11,
-1,
-65,
-38,
-10,
-22,
-153,
-145,
-150,
-112,
-101,
-158,
-152,
-191,
-49,
-18,
-19,
-254,
-48,
-99,
-204,
-100,
-99,
-204,
-206,
-250,
-207,
-11,
-53,
-93,
-30,
-136,
-162,
-212,
-20,
-89,
-150,
-61,
-134,
-164,
-150,
-250,
-147,
-49,
-230,
-112,
-224,
-239,
-89,
-150,
-13,
-88,
-41,
-102,
-89,
-22,
-116,
-60,
-156,
-101,
-217,
-10,
-93,
-251,
-52,
-150,
-225,
-8,
-135,
-141,
-139,
-113,
-17,
-171,
-234,
-198,
-26,
-202,
-6,
-161,
-117,
-2,
-170,
-76,
-13,
-150,
-208,
-8,
-183,
-24,
-99,
-54,
-113,
-126,
-63,
-207,
-228,
-190,
-226,
-46,
-182,
-54,
-198,
-28,
-159,
-101,
-217,
-215,
-178,
-44,
-251,
-189,
-49,
-102,
-152,
-97,
-161,
-207,
-48,
-198,
-220,
-100,
-140,
-89,
-219,
-24,
-19,
-39,
-207,
-92,
-194,
-168,
-0,
-248,
-152,
-174,
-46,
-94,
-92,
-69,
-87,
-38,
-0,
-172,
-212,
-122,
-83,
-192,
-133,
-92,
-30,
-115,
-3,
-251,
-183,
-56,
-225,
-169,
-136,
-73,
-192,
-186,
-21,
-116,
-71,
-27,
-99,
-182,
-3,
-246,
-3,
-246,
-51,
-198,
-108,
-103,
-114,
-95,
-113,
-23,
-119,
-24,
-99,
-182,
-69,
-148,
-128,
-223,
-51,
-198,
-188,
-173,
-174,
-3,
-177,
-150,
-146,
-234,
-185,
-102,
-253,
-233,
-247,
-41,
-188,
-219,
-113,
-1,
-181,
-91,
-40,
-221,
-194,
-76,
-32,
-172,
-84,
-248,
-27,
-14,
-196,
-178,
-202,
-226,
-250,
-26,
-218,
-245,
-148,
-110,
-33,
-1,
-199,
-134,
-14,
-223,
-96,
-141,
-126,
-0,
-207,
-159,
-43,
-207,
-63,
-4,
-208,
-238,
-66,
-158,
-179,
-109,
-149,
-166,
-215,
-42,
-225,
-121,
-182,
-242,
-59,
-23,
-209,
-14,
-15,
-152,
-206,
-34,
-126,
-225,
-223,
-36,
-63,
-6,
-252,
-14,
-254,
-99,
-192,
-215,
-144,
-251,
-142,
-159,
-13,
-236,
-90,
-183,
-23,
-141,
-185,
-151,
-68,
-108,
-65,
-108,
-142,
-197,
-215,
-117,
-229,
-55,
-86,
-208,
-228,
-25,
-17,
-118,
-10,
-80,
-202,
-47,
-244,
-90,
-49,
-223,
-155,
-135,
-247,
-55,
-149,
-119,
-80,
-218,
-115,
-31,
-131,
-119,
-2,
-23,
-227,
-81,
-84,
-57,
-52,
-75,
-3,
-39,
-233,
-133,
-74,
-143,
-170,
-10,
-109,
-106,
-209,
-176,
-159,
-59,
-107,
-179,
-251,
-129,
-213,
-43,
-232,
-50,
-68,
-35,
-237,
-34,
-150,
-0,
-120,
-58,
-98,
-62,
-125,
-99,
-155,
-123,
-136,
-112,
-253,
-168,
-3,
-9,
-248,
-183,
-242,
-59,
-48,
-6,
-191,
-177,
-128,
-38,
-207,
-136,
-197,
-67,
-0,
-28,
-162,
-188,
-223,
-87,
-69,
-87,
-170,
-4,
-204,
-178,
-236,
-12,
-51,
-232,
-191,
-109,
-153,
-223,
-103,
-140,
-185,
-215,
-136,
-217,
-240,
-218,
-70,
-162,
-233,
-238,
-23,
-210,
-177,
-152,
-238,
-148,
-72,
-250,
-114,
-155,
-193,
-232,
-179,
-89,
-150,
-61,
-90,
-66,
-183,
-188,
-145,
-96,
-30,
-239,
-50,
-18,
-247,
-111,
-99,
-211,
-102,
-105,
-84,
-142,
-21,
-140,
-49,
-47,
-50,
-242,
-60,
-102,
-26,
-89,
-198,
-143,
-103,
-156,
-103,
-140,
-121,
-181,
-49,
-166,
-242,
-28,
-59,
-97,
-76,
-35,
-104,
-11,
-208,
-118,
-143,
-55,
-199,
-136,
-221,
-245,
-211,
-140,
-49,
-103,
-25,
-99,
-94,
-155,
-249,
-51,
-152,
-246,
-141,
-221,
-141,
-49,
-207,
-52,
-198,
-220,
-108,
-140,
-249,
-99,
-5,
-221,
-95,
-140,
-76,
-254,
-11,
-141,
-236,
-189,
-99,
-251,
-116,
-159,
-98,
-140,
-249,
-128,
-17,
-13,
-250,
-218,
-89,
-150,
-85,
-42,
-94,
-198,
-1,
-172,
-0,
-171,
-210,
-105,
-36,
-140,
-50,
-144,
-179,
-126,
-128,
-123,
-60,
-213,
-43,
-234,
-223,
-74,
-1,
-208,
-234,
-24,
-48,
-203,
-178,
-32,
-11,
-193,
-62,
-129,
-36,
-77,
-180,
-230,
-183,
-135,
-168,
-2,
-171,
-12,
-63,
-54,
-198,
-220,
-99,
-140,
-249,
-120,
-150,
-101,
-243,
-104,
-224,
-233,
-24,
-208,
-143,
-149,
-141,
-49,
-175,
-215,
-159,
-95,
-201,
-178,
-172,
-77,
-82,
-145,
-177,
-134,
-135,
-244,
-239,
-26,
-163,
-218,
-139,
-132,
-58,
-188,
-74,
-255,
-254,
-213,
-83,
-103,
-39,
-254,
-138,
-158,
-186,
-167,
-48,
-158,
-237,
-0,
-94,
-105,
-140,
-217,
-192,
-200,
-145,
-165,
-215,
-76,
-215,
-34,
-203,
-178,
-179,
-140,
-172,
-84,
-44,
-150,
-46,
-163,
-109,
-129,
-167,
-38,
-73,
-150,
-101,
-247,
-70,
-228,
-59,
-154,
-120,
-68,
-255,
-174,
-54,
-170,
-189,
-72,
-40,
-5,
-176,
-170,
-17,
-175,
-93,
-99,
-140,
-249,
-181,
-135,
-164,
-217,
-22,
-0,
-152,
-171,
-203,
-137,
-168,
-174,
-137,
-52,
-243,
-174,
-219,
-20,
-201,
-208,
-123,
-175,
-246,
-103,
-10,
-146,
-173,
-216,
-103,
-225,
-182,
-173,
-254,
-189,
-34,
-32,
-94,
-95,
-159,
-88,
-172,
-66,
-68,
-41,
-150,
-209,
-191,
-211,
-70,
-181,
-23,
-9,
-85,
-216,
-198,
-200,
-216,
-187,
-186,
-100,
-251,
-221,
-120,
-11,
-48,
-221,
-24,
-179,
-186,
-137,
-255,
-210,
-223,
-102,
-140,
-89,
-214,
-136,
-146,
-236,
-82,
-35,
-82,
-235,
-185,
-70,
-246,
-238,
-69,
-28,
-98,
-196,
-16,
-230,
-116,
-35,
-6,
-41,
-27,
-26,
-73,
-12,
-185,
-145,
-49,
-230,
-141,
-5,
-218,
-109,
-244,
-239,
-121,
-145,
-251,
-155,
-144,
-191,
-155,
-135,
-71,
-181,
-23,
-139,
-57,
-58,
-42,
-196,
-223,
-160,
-127,
-15,
-45,
-169,
-111,
-188,
-5,
-152,
-102,
-74,
-4,
-0,
-226,
-110,
-248,
-97,
-99,
-204,
-113,
-89,
-150,
-237,
-19,
-90,
-167,
-56,
-203,
-72,
-236,
-187,
-201,
-250,
-123,
-178,
-145,
-32,
-8,
-87,
-122,
-104,
-63,
-100,
-228,
-185,
-60,
-117,
-244,
-8,
-60,
-215,
-248,
-77,
-93,
-173,
-143,
-245,
-120,
-215,
-184,
-143,
-69,
-88,
-247,
-227,
-74,
-15,
-199,
-113,
-138,
-147,
-81,
-223,
-133,
-113,
-30,
-120,
-117,
-71,
-35,
-6,
-123,
-167,
-150,
-212,
-55,
-51,
-4,
-2,
-174,
-211,
-45,
-192,
-241,
-158,
-186,
-16,
-183,
-195,
-168,
-202,
-47,
-224,
-105,
-192,
-235,
-17,
-191,
-235,
-223,
-122,
-234,
-109,
-178,
-142,
-183,
-182,
-224,
-109,
-209,
-217,
-14,
-0,
-216,
-76,
-121,
-205,
-235,
-202,
-171,
-67,
-31,
-162,
-158,
-39,
-3,
-103,
-41,
-191,
-178,
-175,
-203,
-184,
-3,
-30,
-84,
-208,
-14,
-197,
-14,
-160,
-79,
-56,
-91,
-250,
-202,
-36,
-36,
-197,
-45,
-128,
-49,
-254,
-45,
-192,
-137,
-70,
-190,
-242,
-191,
-241,
-212,
-157,
-96,
-140,
-249,
-168,
-254,
-141,
-130,
-194,
-131,
-63,
-215,
-24,
-227,
-139,
-184,
-99,
-39,
-175,
-247,
-236,
-127,
-136,
-216,
-65,
-255,
-250,
-236,
-250,
-199,
-29,
-144,
-19,
-146,
-109,
-245,
-231,
-57,
-163,
-216,
-149,
-168,
-8,
-89,
-110,
-147,
-91,
-161,
-214,
-158,
-18,
-85,
-241,
-27,
-11,
-161,
-195,
-179,
-44,
-91,
-182,
-158,
-202,
-1,
-226,
-159,
-14,
-61,
-89,
-127,
-53,
-145,
-138,
-192,
-14,
-136,
-133,
-223,
-17,
-218,
-230,
-32,
-15,
-205,
-227,
-90,
-183,
-181,
-143,
-71,
-96,
-95,
-58,
-173,
-0,
-16,
-147,
-93,
-139,
-82,
-143,
-189,
-190,
-17,
-243,
-139,
-131,
-248,
-32,
-128,
-132,
-189,
-90,
-166,
-190,
-197,
-226,
-131,
-38,
-171,
-132,
-197,
-14,
-72,
-8,
-104,
-144,
-204,
-192,
-125,
-240,
-111,
-53,
-72,
-129,
-11,
-241,
-68,
-147,
-1,
-110,
-85,
-126,
-141,
-179,
-174,
-70,
-20,
-0,
-143,
-33,
-230,
-210,
-239,
-236,
-194,
-167,
-43,
-34,
-11,
-128,
-75,
-149,
-215,
-119,
-99,
-244,
-45,
-97,
-108,
-35,
-116,
-11,
-48,
-20,
-0,
-171,
-100,
-89,
-246,
-132,
-243,
-123,
-73,
-99,
-204,
-51,
-140,
-63,
-71,
-225,
-253,
-70,
-78,
-7,
-188,
-17,
-103,
-135,
-129,
-44,
-203,
-22,
-171,
-115,
-114,
-196,
-113,
-228,
-53,
-70,
-18,
-194,
-44,
-54,
-251,
-255,
-132,
-114,
-184,
-166,
-192,
-211,
-10,
-127,
-71,
-3,
-83,
-129,
-51,
-144,
-212,
-220,
-95,
-55,
-198,
-92,
-96,
-100,
-130,
-255,
-202,
-67,
-107,
-247,
-107,
-111,
-240,
-212,
-77,
-24,
-48,
-210,
-243,
-176,
-52,
-238,
-94,
-0,
-159,
-149,
-141,
-49,
-71,
-233,
-207,
-207,
-186,
-130,
-56,
-97,
-2,
-128,
-220,
-125,
-176,
-151,
-36,
-5,
-33,
-203,
-84,
-224,
-32,
-196,
-163,
-110,
-38,
-18,
-106,
-106,
-50,
-18,
-76,
-115,
-32,
-40,
-6,
-240,
-70,
-229,
-55,
-151,
-134,
-153,
-107,
-98,
-109,
-1,
-198,
-2,
-128,
-183,
-235,
-189,
-60,
-94,
-79,
-93,
-201,
-199,
-134,
-131,
-111,
-30,
-67,
-46,
-97,
-252,
-3,
-216,
-87,
-7,
-192,
-182,
-61,
-241,
-143,
-182,
-79,
-85,
-126,
-203,
-34,
-145,
-87,
-1,
-118,
-107,
-217,
-151,
-113,
-43,
-0,
-128,
-187,
-145,
-248,
-245,
-243,
-244,
-94,
-14,
-24,
-237,
-62,
-37,
-140,
-99,
-144,
-135,
-16,
-10,
-201,
-174,
-210,
-134,
-127,
-84,
-1,
-160,
-60,
-247,
-87,
-158,
-147,
-98,
-241,
-28,
-47,
-64,
-148,
-160,
-243,
-144,
-64,
-158,
-95,
-247,
-173,
-146,
-18,
-18,
-198,
-12,
-122,
-18,
-0,
-43,
-34,
-201,
-51,
-0,
-106,
-67,
-103,
-37,
-36,
-36,
-140,
-18,
-250,
-16,
-0,
-202,
-247,
-67,
-202,
-247,
-238,
-241,
-188,
-164,
-79,
-72,
-24,
-85,
-32,
-94,
-119,
-179,
-129,
-195,
-235,
-169,
-91,
-241,
-15,
-22,
-0,
-84,
-164,
-60,
-42,
-161,
-63,
-82,
-121,
-255,
-174,
-123,
-79,
-19,
-18,
-38,
-32,
-200,
-109,
-250,
-123,
-57,
-254,
-105,
-40,
-0,
-122,
-241,
-47,
-72,
-72,
-72,
-24,
-9,
-215,
-14,
-224,
-68,
-35,
-161,
-190,
-134,
-146,
-194,
-169,
-6,
-39,
-24,
-233,
-75,
-52,
-255,
-130,
-132,
-132,
-132,
-81,
-68,
-95,
-58,
-128,
-132,
-132,
-132,
-132,
-132,
-132,
-132,
-137,
-0,
-93,
-69,
-148,
-165,
-3,
-111,
-156,
-84,
-36,
-33,
-97,
-34,
-99,
-220,
-165,
-126,
-74,
-72,
-72,
-136,
-135,
-36,
-0,
-18,
-18,
-38,
-48,
-122,
-21,
-0,
-85,
-138,
-63,
-231,
-168,
-111,
-251,
-62,
-251,
-144,
-144,
-144,
-80,
-142,
-209,
-92,
-1,
-60,
-92,
-248,
-155,
-144,
-144,
-48,
-100,
-140,
-5,
-1,
-240,
-72,
-177,
-194,
-177,
-74,
-60,
-98,
-200,
-125,
-74,
-72,
-152,
-80,
-24,
-77,
-1,
-96,
-39,
-190,
-111,
-5,
-176,
-155,
-145,
-192,
-140,
-31,
-30,
-94,
-119,
-18,
-18,
-38,
-30,
-70,
-123,
-5,
-48,
-61,
-203,
-178,
-185,
-158,
-58,
-107,
-149,
-232,
-139,
-66,
-156,
-144,
-144,
-16,
-9,
-163,
-153,
-27,
-240,
-97,
-227,
-89,
-254,
-27,
-99,
-76,
-150,
-101,
-123,
-27,
-99,
-246,
-30,
-110,
-119,
-18,
-18,
-18,
-162,
-98,
-152,
-230,
-191,
-201,
-16,
-40,
-33,
-161,
-57,
-146,
-29,
-64,
-66,
-194,
-4,
-70,
-18,
-0,
-9,
-9,
-19,
-24,
-73,
-0,
-36,
-36,
-76,
-96,
-36,
-1,
-144,
-144,
-48,
-129,
-145,
-4,
-64,
-66,
-194,
-4,
-198,
-184,
-19,
-0,
-73,
-219,
-159,
-144,
-16,
-15,
-227,
-78,
-0,
-36,
-36,
-36,
-196,
-67,
-18,
-0,
-9,
-9,
-19,
-24,
-73,
-0,
-36,
-36,
-76,
-96,
-36,
-1,
-144,
-144,
-48,
-117,
-14,
-169,
-49,
-0,
-0,
-2,
-74,
-73,
-68,
-65,
-84,
-129,
-145,
-4,
-64,
-66,
-194,
-4,
-70,
-175,
-206,
-64,
-89,
-150,
-101,
-125,
-242,
-79,
-72,
-72,
-232,
-134,
-180,
-2,
-72,
-72,
-152,
-192,
-72,
-2,
-32,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-33,
-225,
-255,
-219,
-131,
-3,
-2,
-0,
-0,
-0,
-0,
-33,
-253,
-95,
-221,
-17,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-208,
-17,
-112,
-180,
-144,
-16,
-164,
-2,
-78,
-0,
-0,
-0,
-0,
-73,
-69,
-78,
-68,
-174,
-66,
-96,
-130,
-};
-/* clang-format on */
diff --git a/scene/resources/default_theme/font_lodpi.inc b/scene/resources/default_theme/font_lodpi.inc
deleted file mode 100644
index 959e2c1d7b..0000000000
--- a/scene/resources/default_theme/font_lodpi.inc
+++ /dev/null
@@ -1,13117 +0,0 @@
-/* clang-format off */
-static const int _lodpi_font_height=14;
-static const int _lodpi_font_ascent=11;
-static const int _lodpi_font_charcount=191;
-static const int _lodpi_font_charrects[191][8]={
-/* charidx , ofs_x, ofs_y, size_x, size_y, valign, halign, advance */
-{64,72,34,10,11,1,1,12},
-{224,85,180,5,11,0,1,7},
-{192,32,16,11,13,-2,-1,9},
-{96,2,216,3,2,0,3,8},
-{160,1734439808,0,0,0,11,0,4},
-{32,0,0,0,0,11,0,4},
-{33,65,234,2,10,1,1,4},
-{225,112,169,5,11,0,1,7},
-{193,17,16,11,13,-2,-1,9},
-{161,2,222,2,11,3,1,4},
-{65,2,16,11,10,1,-1,9},
-{97,76,188,5,8,3,1,7},
-{98,102,165,6,11,0,1,8},
-{226,72,143,6,11,0,1,7},
-{194,113,2,11,13,-2,-1,9},
-{66,46,109,7,10,1,1,9},
-{162,12,136,6,10,1,1,8},
-{34,49,187,5,4,1,1,6},
-{35,78,66,8,10,1,0,9},
-{163,22,167,6,10,1,1,8},
-{195,53,2,11,14,-3,-1,9},
-{227,2,155,6,12,-1,1,7},
-{67,68,115,7,10,1,1,8},
-{99,40,179,5,8,3,1,7},
-{228,121,169,5,11,0,1,7},
-{196,98,2,11,13,-2,-1,9},
-{36,102,137,6,12,0,1,8},
-{100,82,150,6,11,0,1,8},
-{68,90,66,8,10,1,1,10},
-{164,14,79,8,7,3,0,8},
-{37,2,30,10,10,1,1,12},
-{69,29,191,5,10,1,1,7},
-{165,79,98,7,10,1,0,8},
-{229,20,196,5,12,-1,1,7},
-{197,83,2,11,12,-1,-1,9},
-{101,32,124,6,8,3,1,8},
-{38,67,49,9,10,1,1,10},
-{70,101,105,6,10,1,1,7},
-{198,21,2,12,10,1,-1,12},
-{166,95,228,2,14,0,3,7},
-{102,2,201,5,11,0,0,4},
-{230,58,34,10,8,3,1,12},
-{71,66,65,8,10,1,1,10},
-{231,2,186,5,11,3,1,7},
-{199,57,97,7,13,1,1,8},
-{103,13,107,7,11,3,1,7},
-{167,112,131,6,11,0,0,7},
-{39,119,219,2,4,1,1,3},
-{72,54,65,8,10,1,1,10},
-{232,62,143,6,11,0,1,8},
-{200,47,195,5,13,-2,1,7},
-{40,93,212,4,12,1,1,4},
-{104,72,158,6,11,0,1,8},
-{168,77,217,4,2,0,2,8},
-{73,38,191,5,10,1,0,5},
-{169,44,34,10,10,1,1,12},
-{233,52,142,6,11,0,1,8},
-{201,56,197,5,13,-2,1,7},
-{41,51,226,3,12,1,0,4},
-{105,109,213,3,11,0,0,4},
-{106,101,213,4,14,0,-1,4},
-{74,92,195,5,13,1,-2,3},
-{202,65,202,5,13,-2,1,7},
-{42,108,80,7,6,0,0,8},
-{170,29,205,4,5,1,0,5},
-{234,12,181,6,11,0,1,8},
-{171,22,181,5,6,4,1,7},
-{43,101,94,7,7,3,0,8},
-{107,112,92,7,11,0,1,7},
-{203,83,200,5,13,-2,1,7},
-{235,2,171,6,11,0,1,8},
-{75,102,66,8,10,1,1,8},
-{44,107,231,2,3,9,1,4},
-{172,2,104,7,4,6,0,8},
-{108,113,228,2,11,0,1,4},
-{204,101,196,5,13,-2,0,5},
-{236,30,214,3,11,0,0,4},
-{76,22,124,6,10,1,1,7},
-{173,16,229,3,2,7,1,5},
-{45,123,201,3,2,7,1,5},
-{109,68,2,11,8,3,1,13},
-{205,11,211,5,13,-2,0,5},
-{237,37,214,3,11,0,1,4},
-{77,62,20,10,10,1,1,12},
-{46,101,231,2,2,9,1,4},
-{110,111,107,6,8,3,1,8},
-{206,20,212,5,13,-2,0,5},
-{238,11,196,5,11,0,-1,4},
-{174,30,33,10,10,1,1,12},
-{78,2,79,8,10,1,1,10},
-{175,35,111,7,1,-1,0,7},
-{111,102,153,6,8,3,1,8},
-{207,119,184,5,13,-2,0,5},
-{239,69,219,4,11,0,0,4},
-{79,41,66,9,10,1,1,11},
-{47,90,105,7,10,1,-1,5},
-{176,61,219,4,4,1,1,6},
-{112,32,150,6,11,3,1,8},
-{240,82,165,6,11,0,1,8},
-{208,86,33,9,10,1,0,10},
-{80,52,128,6,10,1,1,8},
-{48,42,135,6,10,1,1,8},
-{177,46,97,7,8,3,0,8},
-{113,22,152,6,11,3,1,8},
-{241,2,112,6,12,-1,1,8},
-{81,15,59,9,13,1,1,11},
-{209,74,80,8,14,-3,1,10},
-{49,45,212,4,10,1,2,8},
-{178,58,187,5,6,1,0,5},
-{114,85,217,4,8,3,1,5},
-{210,2,62,9,13,-2,1,11},
-{242,62,165,6,11,0,1,8},
-{82,35,97,7,10,1,1,8},
-{50,57,114,7,10,1,1,8},
-{179,53,214,4,6,1,0,5},
-{115,112,146,6,8,3,0,7},
-{211,106,49,9,13,-2,1,11},
-{243,52,172,6,11,0,1,8},
-{83,24,96,7,10,1,0,7},
-{51,22,138,6,10,1,1,8},
-{180,9,228,3,2,0,3,8},
-{116,67,188,5,10,1,0,5},
-{212,93,49,9,13,-2,1,11},
-{244,42,164,6,11,0,1,8},
-{84,13,93,7,10,1,0,7},
-{52,24,110,7,10,1,1,8},
-{53,92,119,6,10,1,1,8},
-{85,114,66,8,10,1,1,10},
-{213,2,44,9,14,-3,1,11},
-{117,42,123,6,8,3,1,8},
-{181,2,140,6,11,3,1,8},
-{245,12,165,6,12,-1,1,8},
-{54,82,121,6,10,1,1,8},
-{86,76,18,10,10,1,-1,8},
-{246,72,173,6,11,0,1,8},
-{214,80,49,9,13,-2,1,11},
-{182,68,98,7,13,0,1,9},
-{118,15,47,9,8,3,-1,7},
-{55,72,129,6,10,1,1,8},
-{87,2,2,15,10,1,-1,13},
-{119,37,2,12,8,3,-1,10},
-{247,90,94,7,7,3,0,8},
-{215,2,93,7,7,3,0,8},
-{183,77,223,2,2,5,1,4},
-{56,62,129,6,10,1,1,8},
-{88,90,19,10,10,1,-1,8},
-{216,99,33,9,12,0,1,11},
-{248,2,128,6,8,3,1,8},
-{120,119,80,7,8,3,0,7},
-{184,116,212,3,3,11,0,3},
-{89,28,65,9,10,1,-1,7},
-{217,38,80,8,13,-2,1,10},
-{249,52,157,6,11,0,1,8},
-{121,112,33,9,11,3,-1,7},
-{57,12,122,6,10,1,1,8},
-{185,23,229,3,6,1,0,5},
-{218,26,79,8,13,-2,1,10},
-{250,42,149,6,11,0,1,8},
-{90,32,136,6,10,1,1,8},
-{122,112,119,6,8,3,1,7},
-{58,89,229,2,8,3,1,4},
-{186,37,205,4,5,1,0,5},
-{219,50,80,8,13,-2,1,10},
-{91,58,227,3,12,1,1,4},
-{123,103,180,5,12,1,0,5},
-{251,12,150,6,11,0,1,8},
-{59,71,234,2,9,3,1,4},
-{187,31,181,5,6,4,1,7},
-{188,16,33,10,10,1,0,10},
-{124,83,229,2,14,0,3,7},
-{220,62,79,8,13,-2,1,10},
-{252,92,133,6,11,0,1,8},
-{92,97,80,7,10,1,-1,5},
-{60,92,153,6,7,3,1,8},
-{189,47,20,11,10,1,0,10},
-{253,28,47,9,14,0,-1,7},
-{221,54,48,9,13,-2,-1,7},
-{93,44,226,3,12,1,0,4},
-{125,110,196,5,12,1,0,5},
-{61,79,112,7,5,4,0,8},
-{190,104,19,10,10,1,0,10},
-{222,32,165,6,10,1,1,8},
-{254,102,119,6,14,0,1,8},
-{62,112,158,6,7,3,1,8},
-{94,86,80,7,6,1,0,7},
-{126,62,158,6,3,5,1,8},
-{223,82,135,6,11,0,1,8},
-{255,41,48,9,14,0,-1,7},
-{191,94,180,5,11,3,0,6},
-{63,74,202,5,10,1,0,6},
-{95,92,148,6,1,12,0,6},
-};
-static const int _lodpi_font_kerning_pair_count=0;
-static const int _lodpi_font_kerning_pairs[1][3]={
-{0,0,0},
-};
-static const int _lodpi_font_img_width=128;
-static const int _lodpi_font_img_height=256;
-static const int _lodpi_font_img_data_size=12909;
-static const unsigned char _lodpi_font_img_data[12909]={
-137,
-80,
-78,
-71,
-13,
-10,
-26,
-10,
-0,
-0,
-0,
-13,
-73,
-72,
-68,
-82,
-0,
-0,
-0,
-128,
-0,
-0,
-1,
-0,
-8,
-6,
-0,
-0,
-0,
-123,
-249,
-126,
-167,
-0,
-0,
-32,
-0,
-73,
-68,
-65,
-84,
-120,
-156,
-237,
-157,
-119,
-184,
-30,
-85,
-181,
-255,
-191,
-59,
-128,
-8,
-210,
-155,
-82,
-77,
-232,
-221,
-2,
-210,
-164,
-132,
-26,
-154,
-5,
-164,
-131,
-63,
-224,
-98,
-65,
-4,
-68,
-46,
-8,
-42,
-23,
-84,
-80,
-41,
-94,
-16,
-21,
-17,
-197,
-10,
-22,
-80,
-17,
-21,
-4,
-5,
-65,
-16,
-41,
-42,
-185,
-116,
-19,
-106,
-128,
-80,
-46,
-32,
-33,
-148,
-208,
-18,
-62,
-191,
-63,
-214,
-122,
-115,
-230,
-204,
-217,
-51,
-179,
-103,
-222,
-121,
-223,
-115,
-146,
-123,
-190,
-207,
-115,
-158,
-51,
-101,
-237,
-50,
-243,
-174,
-217,
-123,
-237,
-213,
-182,
-52,
-138,
-81,
-140,
-98,
-20,
-2,
-150,
-3,
-222,
-0,
-54,
-207,
-93,
-255,
-32,
-134,
-247,
-231,
-174,
-111,
-237,
-244,
-203,
-38,
-212,
-61,
-31,
-48,
-149,
-2,
-68,
-232,
-87,
-242,
-91,
-107,
-102,
-174,
-253,
-28,
-184,
-58,
-115,
-190,
-178,
-211,
-172,
-88,
-243,
-57,
-199,
-84,
-220,
-7,
-216,
-17,
-184,
-21,
-120,
-21,
-184,
-23,
-216,
-9,
-56,
-192,
-143,
-103,
-0,
-55,
-1,
-235,
-36,
-180,
-245,
-117,
-224,
-122,
-224,
-244,
-58,
-125,
-244,
-178,
-247,
-0,
-239,
-4,
-222,
-13,
-220,
-85,
-179,
-236,
-55,
-253,
-57,
-190,
-89,
-69,
-59,
-251,
-101,
-132,
-16,
-30,
-151,
-116,
-171,
-164,
-157,
-114,
-52,
-59,
-74,
-186,
-94,
-210,
-118,
-185,
-235,
-59,
-73,
-250,
-123,
-8,
-225,
-137,
-132,
-62,
-237,
-39,
-233,
-113,
-73,
-11,
-248,
-249,
-206,
-126,
-188,
-64,
-230,
-218,
-108,
-132,
-16,
-30,
-145,
-116,
-175,
-164,
-205,
-252,
-129,
-230,
-149,
-180,
-161,
-164,
-21,
-129,
-133,
-156,
-108,
-115,
-73,
-147,
-67,
-8,
-143,
-150,
-53,
-12,
-28,
-9,
-76,
-3,
-206,
-4,
-198,
-74,
-186,
-49,
-161,
-191,
-103,
-75,
-58,
-76,
-210,
-242,
-178,
-103,
-191,
-88,
-210,
-17,
-146,
-118,
-151,
-52,
-78,
-210,
-51,
-146,
-190,
-157,
-80,
-207,
-102,
-33,
-132,
-45,
-37,
-109,
-157,
-64,
-155,
-199,
-204,
-204,
-241,
-172,
-212,
-66,
-192,
-119,
-37,
-45,
-41,
-105,
-13,
-73,
-75,
-251,
-121,
-114,
-225,
-19,
-128,
-137,
-185,
-107,
-15,
-1,
-59,
-3,
-255,
-202,
-93,
-191,
-11,
-248,
-108,
-98,
-189,
-119,
-2,
-251,
-250,
-49,
-64,
-158,
-153,
-98,
-101,
-206,
-1,
-206,
-247,
-227,
-109,
-128,
-75,
-129,
-31,
-117,
-70,
-34,
-224,
-188,
-20,
-14,
-7,
-110,
-241,
-209,
-226,
-76,
-224,
-57,
-224,
-136,
-10,
-122,
-128,
-79,
-100,
-206,
-215,
-205,
-247,
-217,
-71,
-136,
-23,
-18,
-218,
-190,
-223,
-203,
-222,
-93,
-69,
-59,
-34,
-0,
-172,
-159,
-29,
-214,
-129,
-53,
-128,
-71,
-252,
-120,
-42,
-176,
-188,
-31,
-175,
-232,
-15,
-182,
-118,
-66,
-157,
-59,
-1,
-143,
-250,
-87,
-92,
-135,
-1,
-62,
-216,
-121,
-113,
-192,
-89,
-192,
-97,
-192,
-65,
-192,
-185,
-126,
-237,
-110,
-114,
-211,
-82,
-65,
-61,
-165,
-67,
-126,
-132,
-30,
-96,
-219,
-204,
-249,
-88,
-191,
-54,
-54,
-115,
-109,
-115,
-24,
-58,
-117,
-69,
-234,
-186,
-12,
-248,
-53,
-240,
-251,
-58,
-125,
-240,
-178,
-59,
-121,
-187,
-249,
-17,
-185,
-167,
-101,
-59,
-95,
-252,
-193,
-126,
-124,
-100,
-230,
-43,
-252,
-17,
-112,
-144,
-31,
-127,
-28,
-184,
-47,
-177,
-190,
-107,
-128,
-207,
-100,
-206,
-135,
-160,
-160,
-220,
-162,
-192,
-107,
-254,
-255,
-62,
-76,
-46,
-88,
-14,
-120,
-16,
-88,
-204,
-239,
-45,
-146,
-216,
-135,
-228,
-23,
-226,
-116,
-155,
-103,
-206,
-59,
-12,
-176,
-66,
-230,
-90,
-37,
-3,
-0,
-227,
-128,
-151,
-252,
-99,
-121,
-41,
-203,
-64,
-137,
-125,
-238,
-134,
-121,
-26,
-151,
-21,
-112,
-54,
-112,
-177,
-31,
-255,
-1,
-216,
-211,
-143,
-247,
-7,
-46,
-244,
-227,
-75,
-129,
-175,
-37,
-212,
-245,
-110,
-224,
-5,
-96,
-177,
-204,
-53,
-252,
-7,
-121,
-115,
-231,
-175,
-164,
-252,
-77,
-192,
-129,
-192,
-29,
-153,
-107,
-119,
-0,
-7,
-3,
-55,
-212,
-120,
-166,
-228,
-23,
-210,
-34,
-3,
-156,
-14,
-252,
-218,
-143,
-47,
-1,
-78,
-171,
-209,
-223,
-198,
-204,
-211,
-45,
-227,
-9,
-216,
-22,
-19,
-154,
-22,
-0,
-158,
-7,
-22,
-247,
-235,
-111,
-5,
-30,
-199,
-36,
-250,
-231,
-201,
-173,
-22,
-10,
-234,
-250,
-121,
-236,
-139,
-39,
-97,
-10,
-240,
-242,
-39,
-3,
-55,
-0,
-95,
-201,
-92,
-59,
-195,
-25,
-227,
-164,
-196,
-58,
-106,
-189,
-144,
-54,
-24,
-192,
-25,
-251,
-25,
-96,
-15,
-63,
-223,
-195,
-207,
-11,
-153,
-61,
-87,
-190,
-27,
-230,
-105,
-92,
-182,
-83,
-193,
-188,
-206,
-0,
-31,
-7,
-110,
-206,
-221,
-187,
-29,
-27,
-9,
-158,
-162,
-122,
-57,
-53,
-22,
-27,
-166,
-215,
-206,
-126,
-237,
-53,
-25,
-96,
-75,
-167,
-223,
-44,
-115,
-109,
-187,
-252,
-181,
-138,
-58,
-106,
-189,
-144,
-150,
-24,
-224,
-64,
-255,
-72,
-22,
-240,
-243,
-55,
-3,
-211,
-129,
-3,
-19,
-250,
-219,
-152,
-121,
-186,
-101,
-188,
-108,
-69,
-63,
-195,
-164,
-231,
-47,
-230,
-174,
-127,
-205,
-191,
-190,
-31,
-36,
-212,
-113,
-54,
-240,
-219,
-200,
-117,
-72,
-156,
-2,
-186,
-69,
-107,
-47,
-164,
-126,
-187,
-127,
-39,
-142,
-91,
-18,
-202,
-118,
-195,
-60,
-141,
-203,
-230,
-43,
-218,
-219,
-59,
-252,
-222,
-220,
-245,
-9,
-126,
-253,
-3,
-21,
-229,
-23,
-7,
-94,
-4,
-198,
-71,
-238,
-13,
-65,
-173,
-206,
-213,
-64,
-107,
-47,
-164,
-94,
-155,
-239,
-1,
-102,
-97,
-43,
-168,
-236,
-200,
-183,
-154,
-95,
-223,
-176,
-162,
-124,
-55,
-204,
-211,
-184,
-236,
-92,
-137,
-225,
-120,
-33,
-216,
-106,
-233,
-55,
-5,
-247,
-46,
-5,
-126,
-88,
-82,
-182,
-49,
-243,
-116,
-203,
-120,
-115,
-29,
-134,
-227,
-133,
-0,
-75,
-2,
-47,
-3,
-91,
-21,
-220,
-223,
-214,
-239,
-47,
-89,
-112,
-191,
-27,
-230,
-105,
-92,
-118,
-174,
-196,
-156,
-246,
-66,
-186,
-97,
-158,
-110,
-25,
-111,
-174,
-195,
-232,
-11,
-105,
-1,
-116,
-163,
-78,
-180,
-242,
-75,
-1,
-255,
-11,
-252,
-180,
-102,
-185,
-159,
-123,
-185,
-165,
-154,
-180,
-59,
-138,
-150,
-64,
-55,
-234,
-68,
-43,
-127,
-5,
-166,
-195,
-175,
-171,
-139,
-159,
-7,
-51,
-163,
-254,
-161,
-73,
-187,
-163,
-104,
-1,
-116,
-171,
-78,
-156,
-139,
-1,
-44,
-3,
-188,
-14,
-108,
-19,
-185,
-183,
-131,
-223,
-91,
-102,
-56,
-250,
-214,
-4,
-69,
-95,
-231,
-39,
-36,
-93,
-233,
-182,
-246,
-63,
-250,
-121,
-50,
-186,
-153,
-62,
-186,
-157,
-122,
-122,
-141,
-16,
-194,
-83,
-146,
-174,
-146,
-180,
-103,
-228,
-246,
-158,
-146,
-254,
-228,
-52,
-131,
-0,
-44,
-136,
-57,
-121,
-124,
-46,
-114,
-239,
-72,
-191,
-183,
-96,
-228,
-222,
-217,
-152,
-129,
-238,
-62,
-224,
-204,
-204,
-245,
-177,
-152,
-133,
-54,
-201,
-32,
-150,
-12,
-90,
-208,
-158,
-117,
-51,
-125,
-212,
-41,
-235,
-194,
-222,
-143,
-129,
-215,
-114,
-215,
-43,
-95,
-142,
-51,
-217,
-171,
-184,
-173,
-35,
-119,
-239,
-77,
-192,
-211,
-80,
-104,
-169,
-220,
-215,
-229,
-148,
-121,
-50,
-215,
-230,
-245,
-50,
-251,
-148,
-180,
-185,
-30,
-102,
-28,
-219,
-58,
-115,
-109,
-99,
-204,
-79,
-97,
-221,
-130,
-50,
-107,
-250,
-180,
-184,
-46,
-240,
-104,
-230,
-250,
-133,
-192,
-167,
-138,
-218,
-106,
-12,
-186,
-212,
-158,
-117,
-51,
-125,
-212,
-45,
-11,
-252,
-2,
-179,
-77,
-204,
-200,
-93,
-175,
-124,
-57,
-206,
-0,
-55,
-147,
-113,
-254,
-200,
-220,
-219,
-3,
-184,
-177,
-132,
-1,
-22,
-244,
-119,
-180,
-77,
-230,
-218,
-118,
-217,
-247,
-86,
-210,
-238,
-199,
-128,
-39,
-129,
-101,
-157,
-129,
-31,
-6,
-62,
-86,
-81,
-230,
-58,
-103,
-174,
-143,
-248,
-249,
-187,
-48,
-103,
-147,
-55,
-149,
-149,
-107,
-4,
-186,
-212,
-158,
-49,
-12,
-150,
-44,
-96,
-102,
-230,
-56,
-233,
-229,
-248,
-51,
-29,
-79,
-206,
-224,
-229,
-247,
-254,
-0,
-28,
-81,
-196,
-0,
-78,
-243,
-35,
-220,
-57,
-197,
-207,
-207,
-3,
-126,
-148,
-216,
-223,
-95,
-96,
-190,
-130,
-127,
-0,
-126,
-145,
-88,
-102,
-89,
-224,
-223,
-126,
-252,
-39,
-220,
-195,
-170,
-85,
-208,
-189,
-30,
-123,
-88,
-44,
-89,
-57,
-6,
-72,
-122,
-57,
-206,
-0,
-219,
-98,
-243,
-235,
-26,
-153,
-235,
-203,
-3,
-255,
-196,
-220,
-208,
-202,
-24,
-96,
-59,
-124,
-26,
-240,
-191,
-167,
-200,
-120,
-18,
-85,
-180,
-189,
-48,
-54,
-69,
-61,
-69,
-197,
-28,
-142,
-59,
-189,
-2,
-107,
-97,
-158,
-85,
-219,
-99,
-14,
-171,
-1,
-27,
-253,
-94,
-43,
-43,
-95,
-11,
-116,
-169,
-61,
-99,
-152,
-44,
-89,
-29,
-6,
-200,
-188,
-156,
-147,
-128,
-73,
-216,
-240,
-26,
-157,
-10,
-156,
-1,
-182,
-195,
-124,
-14,
-190,
-156,
-185,
-254,
-121,
-224,
-112,
-191,
-87,
-198,
-0,
-99,
-128,
-199,
-156,
-81,
-182,
-246,
-31,
-52,
-105,
-201,
-235,
-31,
-218,
-116,
-108,
-154,
-27,
-178,
-154,
-200,
-209,
-222,
-130,
-9,
-128,
-119,
-2,
-239,
-243,
-231,
-219,
-14,
-88,
-221,
-127,
-175,
-25,
-101,
-229,
-147,
-65,
-11,
-218,
-51,
-134,
-201,
-146,
-5,
-204,
-244,
-47,
-162,
-243,
-114,
-54,
-197,
-28,
-90,
-22,
-3,
-166,
-23,
-148,
-233,
-48,
-192,
-106,
-206,
-40,
-99,
-188,
-142,
-187,
-49,
-107,
-102,
-41,
-3,
-120,
-29,
-103,
-96,
-186,
-142,
-179,
-73,
-116,
-253,
-118,
-198,
-254,
-23,
-240,
-73,
-224,
-88,
-108,
-4,
-90,
-168,
-186,
-164,
-4,
-236,
-7,
-252,
-17,
-152,
-31,
-184,
-26,
-120,
-27,
-240,
-122,
-74,
-217,
-158,
-131,
-97,
-182,
-100,
-117,
-94,
-78,
-238,
-218,
-4,
-224,
-138,
-2,
-122,
-112,
-199,
-20,
-76,
-224,
-219,
-22,
-24,
-143,
-207,
-201,
-137,
-12,
-240,
-14,
-108,
-249,
-54,
-25,
-88,
-191,
-170,
-143,
-94,
-230,
-76,
-224,
-42,
-103,
-182,
-121,
-128,
-127,
-0,
-149,
-46,
-230,
-216,
-202,
-228,
-126,
-44,
-86,
-224,
-28,
-6,
-166,
-202,
-153,
-85,
-101,
-251,
-2,
-134,
-209,
-146,
-149,
-125,
-57,
-153,
-107,
-155,
-2,
-215,
-82,
-108,
-121,
-203,
-50,
-192,
-161,
-216,
-114,
-242,
-2,
-96,
-123,
-191,
-86,
-201,
-0,
-78,
-119,
-59,
-112,
-123,
-21,
-157,
-211,
-110,
-9,
-60,
-75,
-38,
-152,
-5,
-243,
-196,
-126,
-149,
-204,
-210,
-176,
-160,
-236,
-81,
-192,
-5,
-126,
-252,
-47,
-108,
-138,
-155,
-228,
-207,
-49,
-188,
-90,
-83,
-134,
-217,
-146,
-149,
-125,
-57,
-126,
-190,
-13,
-112,
-17,
-176,
-112,
-73,
-153,
-44,
-3,
-44,
-14,
-252,
-219,
-95,
-236,
-24,
-191,
-150,
-196,
-0,
-169,
-0,
-222,
-2,
-60,
-64,
-68,
-166,
-193,
-228,
-144,
-7,
-129,
-183,
-20,
-148,
-93,
-4,
-147,
-55,
-198,
-70,
-238,
-141,
-140,
-17,
-96,
-184,
-16,
-123,
-57,
-206,
-48,
-247,
-101,
-190,
-146,
-141,
-34,
-229,
-102,
-51,
-128,
-159,
-255,
-146,
-140,
-11,
-92,
-219,
-12,
-48,
-226,
-1,
-108,
-229,
-28,
-122,
-47,
-176,
-113,
-230,
-250,
-242,
-152,
-144,
-212,
-174,
-202,
-113,
-20,
-35,
-11,
-152,
-20,
-190,
-13,
-166,
-139,
-191,
-46,
-115,
-253,
-7,
-192,
-145,
-195,
-217,
-183,
-81,
-244,
-1,
-216,
-154,
-244,
-45,
-192,
-66,
-248,
-210,
-9,
-211,
-63,
-223,
-75,
-47,
-84,
-142,
-163,
-24,
-89,
-192,
-140,
-20,
-29,
-6,
-120,
-222,
-175,
-93,
-78,
-137,
-113,
-195,
-105,
-26,
-27,
-100,
-156,
-110,
-39,
-204,
-0,
-116,
-149,
-255,
-93,
-6,
-76,
-232,
-254,
-137,
-134,
-31,
-12,
-198,
-12,
-44,
-170,
-233,
-68,
-42,
-236,
-5,
-5,
-117,
-189,
-189,
-224,
-250,
-82,
-190,
-138,
-200,
-135,
-245,
-111,
-130,
-197,
-101,
-44,
-157,
-218,
-192,
-13,
-153,
-41,
-224,
-111,
-216,
-154,
-248,
-159,
-152,
-150,
-107,
-136,
-108,
-144,
-41,
-215,
-141,
-65,
-230,
-100,
-224,
-123,
-89,
-9,
-31,
-88,
-218,
-175,
-149,
-70,
-254,
-212,
-101,
-28,
-111,
-107,
-50,
-240,
-138,
-255,
-159,
-0,
-124,
-26,
-51,
-204,
-60,
-5,
-156,
-75,
-196,
-28,
-155,
-41,
-191,
-14,
-240,
-83,
-23,
-56,
-159,
-198,
-150,
-157,
-167,
-149,
-49,
-184,
-255,
-240,
-157,
-24,
-136,
-177,
-192,
-7,
-48,
-101,
-213,
-223,
-139,
-218,
-194,
-116,
-254,
-223,
-243,
-118,
-94,
-5,
-166,
-248,
-52,
-252,
-34,
-176,
-92,
-65,
-153,
-139,
-240,
-24,
-206,
-204,
-181,
-115,
-113,
-187,
-74,
-18,
-128,
-205,
-48,
-201,
-249,
-126,
-96,
-139,
-140,
-76,
-16,
-149,
-13,
-34,
-229,
-107,
-25,
-100,
-128,
-93,
-24,
-80,
-186,
-128,
-169,
-70,
-31,
-7,
-142,
-245,
-107,
-223,
-43,
-250,
-65,
-155,
-48,
-14,
-102,
-194,
-125,
-15,
-182,
-228,
-251,
-152,
-127,
-145,
-147,
-176,
-105,
-110,
-37,
-255,
-0,
-162,
-241,
-142,
-192,
-110,
-254,
-110,
-246,
-194,
-109,
-19,
-152,
-99,
-200,
-119,
-252,
-171,
-142,
-46,
-55,
-253,
-185,
-182,
-203,
-93,
-91,
-8,
-91,
-242,
-157,
-16,
-161,
-95,
-10,
-19,
-184,
-127,
-131,
-41,
-153,
-22,
-1,
-54,
-192,
-84,
-228,
-143,
-20,
-49,
-27,
-3,
-150,
-200,
-5,
-253,
-124,
-126,
-44,
-186,
-107,
-151,
-24,
-125,
-37,
-128,
-125,
-112,
-229,
-2,
-17,
-217,
-160,
-160,
-76,
-45,
-131,
-12,
-230,
-50,
-182,
-148,
-31,
-131,
-169,
-53,
-87,
-195,
-194,
-211,
-23,
-244,
-31,
-244,
-178,
-72,
-185,
-44,
-227,
-172,
-4,
-252,
-197,
-31,
-246,
-119,
-126,
-173,
-144,
-113,
-50,
-117,
-204,
-139,
-105,
-24,
-63,
-150,
-185,
-182,
-7,
-240,
-112,
-132,
-118,
-172,
-191,
-252,
-149,
-10,
-234,
-186,
-144,
-140,
-45,
-33,
-119,
-111,
-8,
-3,
-248,
-245,
-207,
-17,
-201,
-250,
-1,
-252,
-183,
-51,
-229,
-152,
-28,
-237,
-3,
-152,
-138,
-250,
-11,
-5,
-237,
-4,
-103,
-170,
-255,
-231,
-231,
-123,
-249,
-199,
-52,
-79,
-140,
-190,
-20,
-152,
-86,
-237,
-94,
-92,
-181,
-73,
-68,
-54,
-40,
-40,
-151,
-55,
-200,
-148,
-90,
-171,
-24,
-156,
-238,
-5,
-224,
-67,
-192,
-113,
-152,
-110,
-188,
-163,
-140,
-185,
-42,
-82,
-46,
-203,
-56,
-191,
-195,
-244,
-241,
-99,
-112,
-61,
-64,
-9,
-227,
-172,
-128,
-133,
-188,
-61,
-236,
-127,
-49,
-188,
-17,
-41,
-247,
-117,
-224,
-56,
-63,
-94,
-13,
-83,
-27,
-79,
-197,
-134,
-230,
-21,
-48,
-245,
-245,
-189,
-145,
-114,
-67,
-144,
-185,
-247,
-65,
-224,
-229,
-72,
-153,
-201,
-157,
-182,
-252,
-124,
-93,
-76,
-167,
-177,
-165,
-191,
-155,
-59,
-242,
-101,
-50,
-180,
-159,
-7,
-174,
-241,
-227,
-203,
-129,
-83,
-139,
-104,
-75,
-1,
-124,
-138,
-140,
-93,
-27,
-147,
-7,
-182,
-198,
-101,
-131,
-146,
-114,
-121,
-131,
-76,
-169,
-181,
-42,
-194,
-0,
-87,
-96,
-115,
-241,
-58,
-153,
-235,
-49,
-6,
-200,
-150,
-123,
-14,
-88,
-61,
-66,
-19,
-43,
-247,
-103,
-204,
-211,
-104,
-17,
-103,
-24,
-176,
-16,
-184,
-172,
-237,
-97,
-136,
-233,
-25,
-251,
-34,
-215,
-246,
-227,
-107,
-128,
-195,
-252,
-248,
-85,
-44,
-98,
-122,
-12,
-240,
-74,
-164,
-92,
-39,
-16,
-118,
-167,
-124,
-221,
-192,
-238,
-192,
-139,
-145,
-50,
-175,
-0,
-123,
-249,
-241,
-188,
-152,
-12,
-118,
-150,
-159,
-239,
-89,
-244,
-46,
-253,
-254,
-114,
-152,
-208,
-183,
-14,
-230,
-151,
-184,
-70,
-17,
-109,
-33,
-252,
-229,
-60,
-202,
-96,
-93,
-245,
-32,
-217,
-160,
-162,
-124,
-178,
-181,
-202,
-127,
-240,
-37,
-253,
-24,
-167,
-61,
-16,
-56,
-207,
-175,
-45,
-69,
-252,
-75,
-206,
-51,
-192,
-106,
-17,
-154,
-24,
-3,
-188,
-4,
-236,
-234,
-199,
-227,
-188,
-205,
-83,
-202,
-158,
-199,
-105,
-95,
-102,
-96,
-110,
-125,
-9,
-120,
-59,
-54,
-26,
-130,
-141,
-150,
-139,
-3,
-79,
-23,
-148,
-133,
-248,
-20,
-240,
-37,
-224,
-214,
-200,
-245,
-87,
-24,
-200,
-199,
-112,
-2,
-54,
-34,
-116,
-204,
-227,
-123,
-17,
-25,
-53,
-114,
-229,
-127,
-135,
-77,
-191,
-133,
-31,
-106,
-207,
-64,
-77,
-107,
-21,
-102,
-219,
-254,
-185,
-31,
-119,
-24,
-32,
-96,
-158,
-50,
-155,
-96,
-115,
-249,
-16,
-199,
-208,
-28,
-227,
-252,
-22,
-243,
-34,
-26,
-195,
-192,
-180,
-80,
-196,
-56,
-147,
-176,
-233,
-226,
-173,
-216,
-84,
-112,
-163,
-51,
-208,
-222,
-206,
-16,
-239,
-5,
-118,
-143,
-148,
-123,
-9,
-88,
-212,
-143,
-31,
-194,
-134,
-227,
-29,
-176,
-17,
-96,
-25,
-44,
-140,
-254,
-226,
-130,
-103,
-28,
-194,
-0,
-94,
-230,
-73,
-224,
-63,
-35,
-244,
-147,
-49,
-47,
-165,
-245,
-48,
-33,
-117,
-211,
-204,
-189,
-227,
-129,
-59,
-99,
-237,
-228,
-222,
-41,
-184,
-235,
-88,
-95,
-65,
-3,
-107,
-21,
-240,
-85,
-44,
-16,
-36,
-43,
-205,
-47,
-133,
-45,
-45,
-79,
-46,
-40,
-147,
-101,
-156,
-149,
-176,
-37,
-224,
-52,
-60,
-137,
-85,
-9,
-227,
-108,
-143,
-205,
-253,
-211,
-129,
-111,
-97,
-67,
-236,
-209,
-126,
-237,
-117,
-108,
-78,
-143,
-249,
-7,
-78,
-196,
-163,
-156,
-177,
-161,
-123,
-138,
-63,
-215,
-4,
-224,
-127,
-48,
-225,
-108,
-229,
-130,
-190,
-130,
-77,
-1,
-11,
-96,
-242,
-194,
-1,
-152,
-176,
-118,
-13,
-145,
-21,
-18,
-230,
-91,
-112,
-15,
-54,
-141,
-158,
-150,
-185,
-62,
-143,
-51,
-71,
-244,
-157,
-100,
-232,
-118,
-114,
-134,
-45,
-52,
-130,
-245,
-4,
-116,
-97,
-173,
-242,
-31,
-244,
-10,
-108,
-202,
-184,
-218,
-143,
-75,
-151,
-47,
-77,
-24,
-167,
-41,
-128,
-255,
-44,
-98,
-224,
-132,
-178,
-89,
-60,
-143,
-45,
-169,
-143,
-2,
-230,
-43,
-160,
-95,
-6,
-147,
-222,
-95,
-198,
-150,
-172,
-11,
-99,
-115,
-250,
-149,
-206,
-116,
-67,
-150,
-129,
-206,
-28,
-11,
-97,
-194,
-232,
-93,
-192,
-25,
-77,
-250,
-58,
-199,
-161,
-9,
-227,
-52,
-108,
-103,
-62,
-255,
-98,
-127,
-69,
-198,
-117,
-219,
-127,
-172,
-86,
-29,
-51,
-177,
-21,
-204,
-115,
-152,
-101,
-242,
-49,
-76,
-168,
-155,
-234,
-35,
-86,
-52,
-84,
-206,
-71,
-150,
-87,
-188,
-220,
-15,
-128,
-249,
-219,
-236,
-147,
-168,
-25,
-218,
-53,
-55,
-194,
-153,
-224,
-211,
-152,
-84,
-62,
-13,
-243,
-29,
-120,
-0,
-23,
-88,
-91,
-110,
-43,
-26,
-31,
-208,
-55,
-144,
-203,
-172,
-73,
-196,
-109,
-122,
-20,
-115,
-15,
-98,
-95,
-247,
-254,
-146,
-54,
-240,
-227,
-219,
-36,
-213,
-138,
-238,
-29,
-197,
-28,
-142,
-58,
-67,
-190,
-207,
-89,
-175,
-145,
-243,
-184,
-193,
-194,
-157,
-94,
-45,
-154,
-183,
-114,
-180,
-141,
-194,
-200,
-71,
-209,
-3,
-48,
-216,
-18,
-213,
-17,
-64,
-190,
-77,
-137,
-73,
-17,
-211,
-174,
-125,
-35,
-119,
-237,
-219,
-20,
-172,
-141,
-35,
-229,
-107,
-133,
-145,
-211,
-48,
-139,
-246,
-40,
-163,
-85,
-128,
-1,
-75,
-212,
-165,
-12,
-88,
-162,
-214,
-243,
-31,
-248,
-62,
-92,
-25,
-18,
-41,
-183,
-11,
-166,
-194,
-237,
-228,
-2,
-126,
-147,
-11,
-73,
-61,
-177,
-233,
-99,
-214,
-183,
-14,
-3,
-36,
-121,
-227,
-122,
-185,
-36,
-70,
-195,
-20,
-66,
-19,
-49,
-69,
-204,
-237,
-84,
-104,
-63,
-115,
-101,
-107,
-51,
-39,
-53,
-82,
-187,
-231,
-202,
-181,
-203,
-208,
-152,
-37,
-234,
-94,
-114,
-22,
-36,
-76,
-203,
-54,
-153,
-98,
-139,
-215,
-60,
-62,
-98,
-236,
-226,
-231,
-187,
-59,
-35,
-69,
-95,
-52,
-109,
-57,
-48,
-244,
-8,
-152,
-54,
-243,
-84,
-96,
-9,
-76,
-115,
-152,
-148,
-19,
-217,
-203,
-214,
-98,
-78,
-224,
-187,
-152,
-86,
-114,
-117,
-76,
-135,
-145,
-156,
-218,
-189,
-238,
-200,
-153,
-82,
-225,
-32,
-75,
-84,
-238,
-222,
-241,
-101,
-28,
-141,
-41,
-102,
-126,
-230,
-199,
-191,
-161,
-192,
-108,
-153,
-161,
-175,
-229,
-192,
-208,
-132,
-105,
-176,
-168,
-160,
-105,
-228,
-220,
-176,
-49,
-83,
-247,
-179,
-37,
-35,
-218,
-11,
-25,
-102,
-222,
-139,
-2,
-61,
-255,
-92,
-7,
-50,
-150,
-168,
-200,
-189,
-82,
-67,
-4,
-102,
-38,
-157,
-142,
-185,
-135,
-205,
-160,
-192,
-118,
-158,
-161,
-175,
-237,
-192,
-80,
-151,
-105,
-252,
-254,
-137,
-152,
-118,
-44,
-100,
-174,
-221,
-70,
-196,
-25,
-35,
-115,
-255,
-39,
-152,
-61,
-98,
-2,
-166,
-149,
-59,
-172,
-236,
-89,
-188,
-76,
-19,
-217,
-169,
-47,
-101,
-146,
-209,
-13,
-3,
-56,
-205,
-95,
-49,
-15,
-153,
-63,
-150,
-209,
-57,
-109,
-109,
-7,
-134,
-134,
-76,
-179,
-136,
-127,
-237,
-239,
-243,
-243,
-157,
-177,
-136,
-227,
-178,
-128,
-145,
-245,
-48,
-211,
-246,
-27,
-192,
-222,
-9,
-207,
-82,
-91,
-118,
-234,
-87,
-153,
-90,
-192,
-116,
-205,
-199,
-23,
-220,
-75,
-177,
-68,
-29,
-236,
-47,
-46,
-150,
-58,
-37,
-70,
-95,
-203,
-129,
-161,
-9,
-211,
-100,
-218,
-185,
-193,
-143,
-255,
-74,
-193,
-52,
-231,
-247,
-223,
-235,
-12,
-115,
-54,
-166,
-233,
-251,
-25,
-3,
-206,
-41,
-135,
-19,
-113,
-200,
-160,
-129,
-236,
-212,
-175,
-50,
-181,
-128,
-37,
-130,
-190,
-47,
-210,
-64,
-146,
-37,
-170,
-65,
-123,
-181,
-29,
-24,
-234,
-50,
-141,
-211,
-45,
-236,
-95,
-253,
-103,
-49,
-169,
-57,
-26,
-126,
-229,
-180,
-119,
-0,
-95,
-242,
-227,
-149,
-24,
-112,
-200,
-92,
-206,
-153,
-237,
-195,
-145,
-50,
-181,
-101,
-167,
-126,
-149,
-169,
-133,
-204,
-16,
-115,
-21,
-230,
-138,
-148,
-181,
-68,
-117,
-63,
-196,
-196,
-219,
-172,
-229,
-192,
-208,
-132,
-105,
-188,
-220,
-113,
-24,
-134,
-216,
-224,
-115,
-116,
-47,
-2,
-187,
-101,
-206,
-87,
-198,
-28,
-100,
-94,
-0,
-190,
-95,
-80,
-166,
-246,
-212,
-217,
-175,
-50,
-41,
-200,
-238,
-26,
-246,
-140,
-164,
-77,
-36,
-77,
-145,
-244,
-39,
-73,
-255,
-150,
-101,
-8,
-123,
-80,
-182,
-251,
-85,
-161,
-67,
-104,
-23,
-248,
-158,
-164,
-237,
-37,
-37,
-165,
-109,
-245,
-157,
-205,
-174,
-148,
-116,
-150,
-108,
-199,
-178,
-201,
-137,
-237,
-116,
-148,
-82,
-67,
-210,
-215,
-231,
-112,
-163,
-164,
-227,
-176,
-101,
-220,
-56,
-73,
-155,
-202,
-118,
-239,
-154,
-79,
-210,
-180,
-196,
-182,
-230,
-40,
-204,
-155,
-61,
-241,
-45,
-224,
-62,
-218,
-199,
-246,
-103,
-74,
-154,
-33,
-233,
-162,
-26,
-101,
-190,
-39,
-233,
-119,
-170,
-215,
-207,
-212,
-32,
-207,
-131,
-36,
-125,
-93,
-198,
-248,
-11,
-73,
-154,
-36,
-233,
-11,
-146,
-110,
-146,
-116,
-45,
-240,
-74,
-8,
-33,
-191,
-130,
-152,
-34,
-41,
-234,
-16,
-226,
-215,
-239,
-143,
-92,
-239,
-87,
-153,
-222,
-131,
-226,
-165,
-73,
-52,
-89,
-34,
-93,
-58,
-48,
-208,
-192,
-235,
-133,
-129,
-93,
-63,
-86,
-173,
-211,
-86,
-98,
-221,
-181,
-101,
-167,
-126,
-149,
-233,
-57,
-40,
-95,
-154,
-60,
-64,
-102,
-179,
-168,
-76,
-153,
-218,
-14,
-12,
-45,
-48,
-77,
-47,
-25,
-160,
-182,
-236,
-212,
-175,
-50,
-61,
-7,
-189,
-94,
-154,
-12,
-212,
-215,
-91,
-175,
-151,
-46,
-145,
-25,
-5,
-31,
-167,
-190,
-82,
-167,
-167,
-101,
-154,
-62,
-80,
-82,
-198,
-110,
-122,
-189,
-52,
-25,
-197,
-240,
-128,
-196,
-140,
-221,
-244,
-104,
-105,
-50,
-138,
-254,
-97,
-222,
-216,
-197,
-16,
-194,
-44,
-73,
-71,
-181,
-80,
-127,
-123,
-73,
-12,
-71,
-209,
-19,
-12,
-50,
-37,
-82,
-223,
-216,
-48,
-69,
-229,
-75,
-147,
-41,
-249,
-139,
-52,
-180,
-210,
-213,
-133,
-11,
-125,
-177,
-168,
-156,
-194,
-220,
-63,
-152,
-47,
-195,
-49,
-152,
-63,
-192,
-75,
-152,
-98,
-232,
-110,
-160,
-141,
-143,
-97,
-100,
-163,
-66,
-162,
-47,
-146,
-76,
-171,
-150,
-38,
-255,
-85,
-208,
-86,
-19,
-43,
-93,
-45,
-230,
-172,
-203,
-0,
-254,
-227,
-95,
-135,
-5,
-101,
-236,
-224,
-140,
-250,
-86,
-224,
-253,
-177,
-122,
-114,
-253,
-122,
-202,
-143,
-151,
-6,
-30,
-47,
-233,
-79,
-7,
-149,
-201,
-34,
-98,
-253,
-199,
-98,
-45,
-175,
-35,
-30,
-195,
-56,
-147,
-248,
-30,
-6,
-105,
-201,
-174,
-104,
-102,
-160,
-40,
-91,
-154,
-220,
-73,
-65,
-6,
-76,
-106,
-90,
-233,
-26,
-50,
-103,
-93,
-6,
-56,
-14,
-75,
-252,
-80,
-107,
-4,
-2,
-118,
-197,
-147,
-81,
-98,
-219,
-202,
-71,
-211,
-220,
-123,
-127,
-146,
-147,
-69,
-228,
-251,
-15,
-124,
-25,
-27,
-153,
-138,
-114,
-4,
-212,
-122,
-222,
-14,
-178,
-83,
-192,
-174,
-146,
-190,
-239,
-243,
-255,
-108,
-132,
-16,
-222,
-144,
-169,
-106,
-135,
-108,
-22,
-25,
-81,
-31,
-79,
-147,
-116,
-151,
-164,
-151,
-36,
-109,
-17,
-66,
-24,
-18,
-1,
-235,
-229,
-158,
-151,
-244,
-223,
-146,
-58,
-43,
-136,
-207,
-74,
-58,
-35,
-132,
-240,
-66,
-65,
-63,
-63,
-43,
-233,
-85,
-73,
-31,
-10,
-33,
-220,
-30,
-66,
-120,
-62,
-132,
-112,
-167,
-108,
-131,
-134,
-55,
-36,
-125,
-166,
-160,
-92,
-29,
-236,
-39,
-233,
-188,
-6,
-42,
-239,
-13,
-36,
-77,
-140,
-28,
-199,
-240,
-122,
-8,
-225,
-149,
-16,
-194,
-148,
-16,
-194,
-111,
-37,
-109,
-37,
-105,
-41,
-73,
-71,
-151,
-53,
-0,
-28,
-42,
-105,
-15,
-73,
-59,
-250,
-187,
-107,
-31,
-180,
-36,
-209,
-99,
-86,
-186,
-137,
-249,
-145,
-36,
-66,
-87,
-199,
-74,
-215,
-196,
-122,
-86,
-138,
-8,
-253,
-12,
-96,
-191,
-148,
-103,
-116,
-250,
-231,
-252,
-111,
-38,
-102,
-44,
-26,
-116,
-92,
-208,
-159,
-58,
-201,
-34,
-240,
-175,
-247,
-3,
-62,
-250,
-69,
-115,
-4,
-37,
-212,
-159,
-60,
-2,
-180,
-133,
-79,
-75,
-90,
-69,
-210,
-37,
-148,
-36,
-67,
-242,
-175,
-253,
-12,
-73,
-95,
-145,
-116,
-122,
-8,
-225,
-165,
-146,
-58,
-223,
-46,
-233,
-161,
-130,
-123,
-15,
-122,
-123,
-49,
-236,
-44,
-105,
-129,
-220,
-223,
-206,
-69,
-93,
-146,
-217,
-38,
-102,
-163,
-140,
-97,
-66,
-8,
-139,
-133,
-16,
-22,
-147,
-244,
-164,
-164,
-181,
-252,
-248,
-127,
-37,
-173,
-238,
-199,
-169,
-184,
-167,
-164,
-255,
-235,
-75,
-250,
-153,
-108,
-116,
-28,
-146,
-189,
-164,
-13,
-100,
-25,
-96,
-138,
-90,
-48,
-54,
-132,
-16,
-238,
-149,
-89,
-248,
-222,
-144,
-84,
-149,
-201,
-58,
-213,
-74,
-215,
-20,
-157,
-33,
-119,
-246,
-95,
-73,
-159,
-30,
-148,
-148,
-207,
-53,
-80,
-198,
-48,
-194,
-118,
-20,
-39,
-132,
-48,
-21,
-75,
-224,
-52,
-203,
-13,
-106,
-117,
-48,
-70,
-210,
-172,
-130,
-123,
-235,
-203,
-166,
-166,
-147,
-128,
-229,
-107,
-214,
-155,
-220,
-120,
-7,
-151,
-73,
-58,
-36,
-63,
-116,
-251,
-249,
-193,
-146,
-46,
-77,
-173,
-52,
-132,
-240,
-247,
-16,
-194,
-110,
-33,
-132,
-170,
-60,
-182,
-169,
-86,
-186,
-41,
-234,
-189,
-37,
-236,
-18,
-73,
-135,
-146,
-145,
-176,
-203,
-24,
-198,
-135,
-249,
-127,
-73,
-90,
-214,
-143,
-39,
-75,
-90,
-174,
-51,
-53,
-212,
-104,
-247,
-157,
-94,
-54,
-134,
-11,
-93,
-86,
-184,
-72,
-210,
-79,
-200,
-172,
-154,
-34,
-152,
-37,
-51,
-91,
-231,
-241,
-38,
-229,
-70,
-182,
-44,
-178,
-12,
-112,
-170,
-19,
-95,
-73,
-70,
-162,
-151,
-116,
-185,
-211,
-69,
-51,
-104,
-245,
-9,
-173,
-49,
-103,
-9,
-206,
-144,
-9,
-175,
-127,
-194,
-178,
-164,
-45,
-138,
-173,
-74,
-162,
-95,
-158,
-15,
-243,
-231,
-72,
-58,
-209,
-143,
-207,
-148,
-116,
-82,
-102,
-106,
-168,
-4,
-102,
-49,
-253,
-152,
-108,
-152,
-47,
-195,
-103,
-36,
-173,
-36,
-233,
-216,
-18,
-154,
-123,
-37,
-189,
-59,
-114,
-125,
-35,
-191,
-151,
-212,
-161,
-254,
-24,
-27,
-6,
-218,
-75,
-178,
-210,
-209,
-204,
-122,
-86,
-91,
-40,
-194,
-188,
-154,
-191,
-137,
-101,
-5,
-155,
-233,
-130,
-225,
-189,
-20,
-236,
-235,
-227,
-237,
-79,
-240,
-227,
-203,
-40,
-217,
-234,
-206,
-251,
-83,
-39,
-89,
-196,
-160,
-254,
-99,
-233,
-239,
-95,
-6,
-54,
-200,
-211,
-250,
-253,
-61,
-48,
-167,
-217,
-3,
-253,
-119,
-124,
-27,
-112,
-144,
-11,
-165,
-251,
-23,
-245,
-107,
-142,
-65,
-191,
-153,
-51,
-177,
-79,
-79,
-116,
-218,
-247,
-227,
-183,
-149,
-208,
-102,
-145,
-146,
-44,
-98,
-8,
-3,
-99,
-241,
-23,
-147,
-41,
-78,
-43,
-255,
-62,
-44,
-132,
-253,
-101,
-103,
-222,
-155,
-129,
-15,
-117,
-243,
-140,
-163,
-24,
-197,
-40,
-70,
-49,
-138,
-185,
-31,
-62,
-231,
-220,
-157,
-159,
-147,
-42,
-132,
-166,
-43,
-124,
-94,
-126,
-210,
-231,
-230,
-239,
-2,
-151,
-39,
-180,
-53,
-98,
-66,
-181,
-105,
-24,
-110,
-62,
-183,
-32,
-175,
-9,
-92,
-84,
-210,
-49,
-53,
-202,
-63,
-41,
-105,
-89,
-73,
-107,
-75,
-90,
-75,
-210,
-91,
-252,
-90,
-21,
-46,
-144,
-45,
-125,
-134,
-4,
-90,
-228,
-65,
-162,
-119,
-82,
-174,
-76,
-157,
-144,
-235,
-236,
-26,
-185,
-72,
-33,
-83,
-214,
-86,
-85,
-184,
-249,
-10,
-152,
-89,
-121,
-205,
-18,
-154,
-206,
-190,
-73,
-235,
-213,
-108,
-187,
-52,
-112,
-21,
-203,
-99,
-124,
-117,
-238,
-218,
-159,
-201,
-236,
-175,
-148,
-189,
-1,
-240,
-31,
-46,
-161,
-142,
-203,
-92,
-47,
-27,
-1,
-30,
-3,
-86,
-201,
-156,
-47,
-82,
-213,
-169,
-186,
-32,
-209,
-59,
-41,
-67,
-223,
-56,
-228,
-58,
-177,
-254,
-90,
-57,
-148,
-128,
-243,
-49,
-251,
-72,
-105,
-194,
-12,
-108,
-207,
-133,
-115,
-203,
-104,
-156,
-110,
-33,
-6,
-194,
-213,
-254,
-234,
-255,
-3,
-17,
-203,
-171,
-255,
-30,
-15,
-2,
-255,
-225,
-231,
-135,
-96,
-206,
-186,
-67,
-173,
-174,
-206,
-0,
-91,
-250,
-131,
-93,
-158,
-185,
-94,
-198,
-0,
-79,
-144,
-73,
-146,
-136,
-173,
-207,
-163,
-241,
-244,
-116,
-107,
-175,
-110,
-25,
-77,
-251,
-131,
-237,
-228,
-185,
-178,
-191,
-167,
-231,
-128,
-35,
-74,
-104,
-215,
-240,
-23,
-62,
-63,
-102,
-30,
-127,
-103,
-9,
-237,
-70,
-216,
-154,
-189,
-106,
-131,
-141,
-19,
-48,
-179,
-245,
-79,
-49,
-31,
-138,
-159,
-98,
-9,
-58,
-190,
-88,
-64,
-191,
-137,
-223,
-127,
-167,
-255,
-31,
-178,
-231,
-67,
-135,
-16,
-108,
-139,
-182,
-133,
-176,
-112,
-168,
-78,
-170,
-215,
-50,
-6,
-248,
-1,
-150,
-174,
-117,
-49,
-255,
-241,
-127,
-82,
-244,
-197,
-121,
-253,
-117,
-236,
-243,
-181,
-126,
-160,
-6,
-244,
-181,
-250,
-147,
-185,
-95,
-39,
-135,
-210,
-175,
-128,
-143,
-250,
-241,
-190,
-68,
-82,
-216,
-230,
-232,
-255,
-9,
-28,
-94,
-65,
-179,
-8,
-182,
-165,
-252,
-49,
-216,
-212,
-114,
-180,
-159,
-23,
-250,
-49,
-96,
-142,
-39,
-51,
-136,
-56,
-232,
-228,
-31,
-38,
-184,
-13,
-255,
-40,
-73,
-103,
-71,
-135,
-138,
-193,
-120,
-171,
-164,
-103,
-100,
-170,
-198,
-251,
-100,
-115,
-104,
-105,
-110,
-128,
-26,
-152,
-39,
-210,
-191,
-54,
-233,
-107,
-193,
-25,
-230,
-110,
-111,
-39,
-123,
-189,
-136,
-193,
-54,
-148,
-244,
-46,
-13,
-132,
-189,
-93,
-36,
-105,
-53,
-96,
-147,
-146,
-102,
-206,
-145,
-52,
-36,
-93,
-109,
-22,
-238,
-15,
-48,
-77,
-246,
-27,
-29,
-44,
-179,
-190,
-78,
-171,
-240,
-99,
-24,
-39,
-233,
-89,
-255,
-63,
-8,
-209,
-23,
-22,
-66,
-248,
-181,
-44,
-69,
-220,
-41,
-42,
-49,
-36,
-132,
-16,
-118,
-9,
-33,
-28,
-18,
-66,
-88,
-38,
-132,
-240,
-182,
-16,
-194,
-193,
-33,
-132,
-29,
-203,
-30,
-96,
-14,
-71,
-29,
-33,
-249,
-84,
-73,
-167,
-116,
-12,
-98,
-238,
-88,
-243,
-21,
-73,
-101,
-177,
-18,
-191,
-144,
-244,
-54,
-42,
-118,
-17,
-149,
-180,
-177,
-164,
-137,
-33,
-132,
-95,
-74,
-186,
-69,
-210,
-123,
-139,
-8,
-49,
-175,
-171,
-237,
-36,
-189,
-71,
-210,
-14,
-120,
-198,
-244,
-24,
-225,
-160,
-33,
-17,
-203,
-160,
-253,
-60,
-182,
-189,
-74,
-215,
-115,
-116,
-131,
-41,
-96,
-36,
-210,
-215,
-18,
-146,
-155,
-0,
-115,
-205,
-251,
-101,
-75,
-117,
-45,
-137,
-201,
-105,
-157,
-233,
-124,
-79,
-63,
-95,
-162,
-67,
-83,
-56,
-100,
-134,
-16,
-30,
-146,
-244,
-85,
-73,
-95,
-106,
-163,
-51,
-115,
-9,
-238,
-151,
-116,
-190,
-164,
-111,
-245,
-176,
-141,
-115,
-37,
-189,
-159,
-130,
-13,
-162,
-106,
-226,
-219,
-146,
-238,
-8,
-33,
-252,
-74,
-146,
-124,
-196,
-184,
-211,
-175,
-75,
-170,
-158,
-51,
-191,
-38,
-11,
-19,
-111,
-3,
-141,
-236,
-213,
-61,
-68,
-147,
-254,
-204,
-39,
-233,
-68,
-73,
-235,
-119,
-190,
-170,
-182,
-17,
-66,
-184,
-95,
-210,
-181,
-50,
-51,
-113,
-183,
-117,
-237,
-29,
-66,
-152,
-144,
-187,
-182,
-67,
-8,
-97,
-246,
-86,
-128,
-217,
-252,
-0,
-33,
-132,
-112,
-117,
-142,
-248,
-245,
-16,
-194,
-58,
-33,
-132,
-50,
-71,
-132,
-84,
-212,
-181,
-87,
-215,
-253,
-129,
-234,
-210,
-55,
-177,
-159,
-215,
-21,
-146,
-155,
-226,
-28,
-73,
-31,
-197,
-115,
-47,
-206,
-21,
-160,
-166,
-189,
-26,
-83,
-209,
-126,
-62,
-114,
-253,
-11,
-46,
-141,
-119,
-75,
-95,
-183,
-63,
-121,
-25,
-233,
-114,
-44,
-151,
-208,
-248,
-54,
-101,
-128,
-185,
-26,
-212,
-176,
-87,
-55,
-248,
-129,
-106,
-59,
-68,
-212,
-236,
-79,
-158,
-1,
-90,
-21,
-146,
-71,
-17,
-65,
-157,
-31,
-168,
-9,
-125,
-205,
-190,
-12,
-89,
-53,
-96,
-46,
-237,
-79,
-14,
-39,
-3,
-120,
-191,
-54,
-175,
-166,
-76,
-171,
-108,
-173,
-252,
-57,
-17,
-71,
-68,
-74,
-208,
-74,
-71,
-70,
-32,
-10,
-24,
-96,
-62,
-204,
-130,
-58,
-231,
-51,
-0,
-176,
-10,
-166,
-99,
-222,
-193,
-207,
-183,
-33,
-183,
-143,
-95,
-134,
-182,
-179,
-207,
-222,
-26,
-222,
-129,
-181,
-41,
-216,
-119,
-111,
-20,
-67,
-129,
-109,
-170,
-149,
-36,
-88,
-99,
-6,
-173,
-168,
-219,
-152,
-223,
-111,
-117,
-4,
-216,
-210,
-153,
-224,
-243,
-254,
-127,
-124,
-5,
-253,
-88,
-239,
-64,
-235,
-169,
-87,
-230,
-102,
-164,
-254,
-248,
-78,
-75,
-217,
-135,
-149,
-101,
-0,
-204,
-225,
-244,
-38,
-114,
-41,
-117,
-235,
-118,
-238,
-24,
-175,
-52,
-154,
-53,
-52,
-71,
-155,
-196,
-0,
-88,
-46,
-225,
-171,
-49,
-227,
-197,
-19,
-152,
-141,
-255,
-255,
-252,
-94,
-68,
-41,
-72,
-101,
-0,
-44,
-136,
-247,
-55,
-192,
-239,
-105,
-178,
-103,
-176,
-87,
-182,
-149,
-127,
-249,
-157,
-72,
-217,
-237,
-43,
-232,
-83,
-25,
-224,
-70,
-44,
-217,
-243,
-50,
-152,
-229,
-234,
-160,
-146,
-135,
-129,
-129,
-125,
-252,
-206,
-1,
-22,
-79,
-232,
-119,
-233,
-48,
-233,
-52,
-75,
-97,
-142,
-34,
-83,
-177,
-56,
-200,
-135,
-40,
-216,
-45,
-188,
-23,
-192,
-220,
-192,
-99,
-136,
-109,
-33,
-123,
-97,
-1,
-237,
-175,
-34,
-180,
-96,
-91,
-204,
-125,
-195,
-191,
-254,
-232,
-182,
-244,
-41,
-29,
-28,
-135,
-205,
-249,
-219,
-248,
-249,
-22,
-126,
-190,
-86,
-73,
-153,
-84,
-6,
-120,
-153,
-34,
-35,
-196,
-96,
-58,
-48,
-223,
-249,
-133,
-49,
-251,
-248,
-157,
-120,
-26,
-250,
-132,
-114,
-101,
-95,
-201,
-34,
-152,
-59,
-245,
-213,
-152,
-235,
-215,
-226,
-254,
-124,
-177,
-212,
-175,
-11,
-3,
-23,
-224,
-210,
-125,
-22,
-17,
-218,
-107,
-129,
-255,
-206,
-93,
-59,
-25,
-207,
-77,
-156,
-187,
-62,
-134,
-220,
-62,
-197,
-254,
-55,
-36,
-225,
-21,
-38,
-92,
-118,
-238,
-131,
-5,
-169,
-188,
-153,
-8,
-147,
-251,
-253,
-142,
-7,
-212,
-16,
-121,
-173,
-22,
-200,
-24,
-57,
-252,
-188,
-40,
-104,
-177,
-115,
-63,
-149,
-1,
-238,
-33,
-205,
-219,
-5,
-6,
-175,
-181,
-247,
-7,
-42,
-85,
-209,
-9,
-12,
-112,
-10,
-230,
-189,
-84,
-24,
-172,
-154,
-161,
-61,
-39,
-247,
-187,
-79,
-194,
-116,
-10,
-147,
-34,
-180,
-91,
-96,
-211,
-90,
-103,
-43,
-219,
-249,
-253,
-163,
-121,
-127,
-69,
-27,
-109,
-203,
-0,
-191,
-194,
-188,
-160,
-174,
-168,
-83,
-119,
-215,
-168,
-193,
-0,
-59,
-99,
-27,
-62,
-92,
-71,
-137,
-61,
-60,
-194,
-0,
-31,
-6,
-162,
-193,
-150,
-212,
-27,
-38,
-239,
-166,
-98,
-19,
-139,
-12,
-237,
-99,
-185,
-250,
-238,
-242,
-235,
-81,
-135,
-81,
-44,
-215,
-241,
-201,
-126,
-124,
-176,
-51,
-123,
-233,
-143,
-64,
-15,
-86,
-1,
-216,
-200,
-245,
-16,
-240,
-233,
-148,
-122,
-91,
-65,
-42,
-3,
-56,
-237,
-234,
-152,
-144,
-50,
-139,
-200,
-208,
-235,
-52,
-48,
-48,
-5,
-108,
-140,
-165,
-145,
-249,
-66,
-1,
-109,
-157,
-97,
-242,
-21,
-96,
-159,
-88,
-61,
-17,
-218,
-153,
-53,
-25,
-96,
-19,
-204,
-71,
-112,
-17,
-108,
-47,
-225,
-131,
-83,
-218,
-105,
-11,
-29,
-6,
-240,
-227,
-205,
-49,
-5,
-216,
-187,
-250,
-213,
-120,
-237,
-101,
-32,
-182,
-15,
-79,
-52,
-214,
-63,
-242,
-53,
-31,
-153,
-88,
-39,
-148,
-15,
-147,
-175,
-144,
-176,
-249,
-131,
-211,
-78,
-175,
-195,
-0,
-126,
-239,
-114,
-76,
-250,
-158,
-74,
-36,
-206,
-175,
-151,
-200,
-50,
-128,
-159,
-127,
-5,
-155,
-182,
-10,
-19,
-110,
-12,
-43,
-176,
-164,
-75,
-209,
-52,
-39,
-254,
-48,
-219,
-249,
-241,
-223,
-129,
-66,
-239,
-155,
-220,
-8,
-112,
-169,
-143,
-26,
-69,
-35,
-192,
-36,
-224,
-115,
-137,
-253,
-251,
-83,
-42,
-3,
-48,
-48,
-250,
-228,
-177,
-66,
-74,
-91,
-35,
-10,
-184,
-85,
-43,
-134,
-46,
-235,
-93,
-10,
-51,
-208,
-44,
-141,
-249,
-200,
-95,
-6,
-252,
-174,
-128,
-54,
-203,
-0,
-187,
-97,
-75,
-209,
-162,
-68,
-83,
-117,
-100,
-128,
-51,
-177,
-249,
-177,
-242,
-235,
-196,
-146,
-80,
-61,
-156,
-194,
-0,
-126,
-61,
-59,
-13,
-237,
-196,
-156,
-170,
-13,
-37,
-190,
-76,
-89,
-180,
-5,
-6,
-88,
-28,
-219,
-170,
-165,
-147,
-71,
-231,
-18,
-10,
-162,
-104,
-115,
-12,
-16,
-48,
-225,
-45,
-233,
-203,
-173,
-232,
-195,
-210,
-152,
-167,
-243,
-223,
-176,
-53,
-243,
-34,
-216,
-178,
-183,
-72,
-22,
-121,
-51,
-176,
-1,
-176,
-97,
-25,
-3,
-208,
-197,
-8,
-224,
-12,
-254,
-40,
-17,
-165,
-77,
-65,
-157,
-253,
-183,
-55,
-116,
-30,
-176,
-143,
-237,
-205,
-102,
-0,
-63,
-255,
-48,
-45,
-37,
-143,
-4,
-150,
-7,
-190,
-143,
-133,
-176,
-205,
-194,
-76,
-185,
-215,
-38,
-148,
-235,
-201,
-8,
-128,
-229,
-58,
-40,
-202,
-163,
-56,
-187,
-174,
-236,
-95,
-74,
-189,
-173,
-2,
-155,
-103,
-135,
-36,
-71,
-192,
-180,
-121,
-127,
-241,
-151,
-217,
-193,
-227,
-100,
-156,
-13,
-71,
-50,
-128,
-139,
-129,
-75,
-18,
-105,
-43,
-133,
-64,
-191,
-63,
-136,
-121,
-43,
-104,
-87,
-199,
-150,
-197,
-149,
-35,
-97,
-66,
-155,
-187,
-96,
-59,
-156,
-206,
-192,
-130,
-86,
-214,
-79,
-233,
-67,
-87,
-192,
-134,
-244,
-147,
-252,
-248,
-68,
-224,
-250,
-158,
-55,
-218,
-34,
-176,
-41,
-33,
-41,
-223,
-94,
-42,
-3,
-212,
-108,
-255,
-44,
-160,
-112,
-151,
-148,
-154,
-12,
-48,
-25,
-155,
-170,
-150,
-193,
-132,
-225,
-222,
-255,
-22,
-206,
-109,
-219,
-250,
-241,
-182,
-68,
-244,
-217,
-125,
-232,
-67,
-22,
-47,
-99,
-243,
-233,
-197,
-84,
-107,
-47,
-255,
-138,
-173,
-48,
-62,
-153,
-216,
-78,
-171,
-12,
-128,
-89,
-236,
-166,
-1,
-91,
-149,
-208,
-68,
-81,
-64,
-247,
-137,
-204,
-249,
-142,
-64,
-81,
-178,
-205,
-40,
-154,
-90,
-228,
-238,
-151,
-244,
-65,
-31,
-242,
-63,
-168,
-226,
-44,
-87,
-131,
-128,
-205,
-233,
-80,
-18,
-35,
-87,
-19,
-157,
-60,
-128,
-111,
-147,
-101,
-50,
-93,
-82,
-210,
-144,
-85,
-64,
-22,
-33,
-132,
-45,
-66,
-8,
-27,
-133,
-16,
-206,
-105,
-169,
-15,
-117,
-177,
-159,
-164,
-199,
-66,
-8,
-215,
-85,
-208,
-197,
-114,
-28,
-198,
-144,
-117,
-96,
-125,
-81,
-182,
-215,
-81,
-111,
-225,
-67,
-206,
-52,
-204,
-106,
-55,
-145,
-4,
-205,
-19,
-176,
-32,
-166,
-217,
-123,
-190,
-136,
-1,
-48,
-189,
-122,
-244,
-47,
-66,
-59,
-100,
-152,
-196,
-36,
-235,
-178,
-132,
-147,
-195,
-14,
-204,
-101,
-173,
-116,
-244,
-169,
-57,
-5,
-100,
-149,
-64,
-155,
-199,
-70,
-138,
-50,
-148,
-186,
-29,
-3,
-99,
-60,
-164,
-105,
-16,
-66,
-8,
-255,
-196,
-214,
-212,
-107,
-132,
-16,
-30,
-76,
-108,
-235,
-51,
-178,
-221,
-184,
-10,
-99,
-223,
-66,
-8,
-141,
-184,
-23,
-83,
-254,
-140,
-149,
-237,
-36,
-86,
-103,
-7,
-178,
-174,
-128,
-57,
-204,
-68,
-87,
-18,
-49,
-87,
-122,
-44,
-50,
-119,
-13,
-73,
-63,
-233,
-109,
-207,
-26,
-130,
-26,
-177,
-239,
-88,
-122,
-179,
-14,
-158,
-164,
-68,
-213,
-138,
-45,
-193,
-158,
-192,
-148,
-66,
-111,
-180,
-49,
-5,
-20,
-76,
-147,
-23,
-208,
-212,
-25,
-162,
-89,
-31,
-138,
-76,
-188,
-209,
-37,
-27,
-233,
-57,
-0,
-250,
-54,
-2,
-228,
-101,
-128,
-253,
-101,
-25,
-175,
-37,
-11,
-14,
-141,
-166,
-112,
-1,
-206,
-145,
-101,
-239,
-92,
-213,
-255,
-206,
-85,
-121,
-34,
-201,
-175,
-74,
-58,
-199,
-179,
-139,
-183,
-105,
-174,
-236,
-204,
-147,
-11,
-75,
-26,
-47,
-11,
-234,
-248,
-122,
-164,
-191,
-27,
-98,
-186,
-250,
-233,
-88,
-130,
-234,
-139,
-40,
-73,
-233,
-150,
-138,
-16,
-194,
-27,
-249,
-84,
-180,
-153,
-148,
-180,
-249,
-62,
-44,
-41,
-105,
-47,
-101,
-194,
-178,
-70,
-28,
-72,
-116,
-211,
-242,
-121,
-121,
-163,
-204,
-249,
-38,
-20,
-164,
-71,
-197,
-52,
-111,
-143,
-227,
-198,
-9,
-231,
-218,
-182,
-70,
-128,
-188,
-12,
-176,
-63,
-240,
-108,
-132,
-246,
-66,
-204,
-81,
-99,
-101,
-108,
-13,
-126,
-45,
-5,
-121,
-253,
-157,
-62,
-201,
-123,
-136,
-30,
-169,
-207,
-135,
-5,
-222,
-239,
-36,
-165,
-2,
-102,
-207,
-62,
-210,
-143,
-23,
-244,
-23,
-28,
-141,
-104,
-5,
-110,
-192,
-44,
-84,
-171,
-250,
-31,
-152,
-255,
-126,
-87,
-201,
-143,
-11,
-24,
-224,
-96,
-224,
-177,
-132,
-178,
-239,
-163,
-216,
-32,
-85,
-199,
-123,
-168,
-214,
-20,
-48,
-162,
-225,
-47,
-52,
-73,
-169,
-224,
-95,
-252,
-173,
-152,
-243,
-196,
-67,
-88,
-166,
-176,
-168,
-22,
-176,
-232,
-11,
-1,
-254,
-210,
-66,
-127,
-59,
-234,
-210,
-69,
-49,
-143,
-230,
-251,
-129,
-51,
-19,
-202,
-126,
-130,
-72,
-184,
-152,
-223,
-75,
-246,
-30,
-154,
-171,
-224,
-47,
-180,
-43,
-165,
-66,
-141,
-118,
-218,
-22,
-2,
-103,
-98,
-185,
-120,
-78,
-166,
-98,
-83,
-73,
-44,
-5,
-206,
-125,
-192,
-199,
-11,
-238,
-39,
-123,
-15,
-205,
-85,
-240,
-23,
-185,
-109,
-230,
-188,
-182,
-68,
-89,
-163,
-157,
-182,
-20,
-65,
-117,
-219,
-158,
-199,
-71,
-182,
-223,
-80,
-224,
-146,
-69,
-13,
-239,
-161,
-134,
-125,
-56,
-44,
-50,
-26,
-198,
-18,
-85,
-64,
-66,
-192,
-7,
-240,
-59,
-76,
-86,
-153,
-138,
-9,
-184,
-93,
-233,
-1,
-94,
-173,
-83,
-184,
-9,
-90,
-10,
-53,
-175,
-13,
-76,
-192,
-253,
-129,
-164,
-197,
-101,
-123,
-239,
-148,
-189,
-168,
-94,
-10,
-113,
-75,
-73,
-186,
-66,
-82,
-214,
-219,
-169,
-82,
-110,
-41,
-66,
-8,
-97,
-182,
-3,
-42,
-240,
-99,
-21,
-107,
-12,
-163,
-152,
-123,
-226,
-207,
-75,
-224,
-95,
-251,
-119,
-100,
-75,
-214,
-9,
-33,
-132,
-178,
-253,
-143,
-166,
-168,
-120,
-11,
-151,
-170,
-118,
-86,
-150,
-229,
-1,
-218,
-82,
-166,
-150,
-30,
-31,
-66,
-184,
-41,
-71,
-182,
-148,
-164,
-7,
-61,
-17,
-68,
-107,
-0,
-54,
-147,
-244,
-33,
-89,
-194,
-206,
-100,
-212,
-182,
-5,
-96,
-82,
-242,
-143,
-128,
-127,
-99,
-10,
-160,
-202,
-8,
-162,
-17,
-128,
-111,
-200,
-244,
-4,
-71,
-200,
-146,
-48,
-117,
-86,
-36,
-177,
-156,
-124,
-127,
-144,
-37,
-103,
-168,
-229,
-219,
-135,
-101,
-50,
-189,
-78,
-54,
-122,
-76,
-144,
-237,
-115,
-116,
-103,
-132,
-116,
-41,
-217,
-222,
-66,
-41,
-216,
-4,
-19,
-204,
-103,
-96,
-249,
-0,
-163,
-242,
-141,
-143,
-110,
-223,
-146,
-244,
-213,
-16,
-194,
-163,
-117,
-250,
-157,
-173,
-100,
-208,
-156,
-83,
-36,
-3,
-0,
-223,
-193,
-44,
-106,
-171,
-57,
-205,
-35,
-192,
-1,
-141,
-26,
-237,
-65,
-255,
-74,
-202,
-198,
-240,
-145,
-8,
-109,
-45,
-239,
-161,
-76,
-185,
-255,
-194,
-54,
-131,
-172,
-218,
-45,
-237,
-50,
-204,
-59,
-234,
-53,
-76,
-63,
-242,
-109,
-34,
-153,
-70,
-188,
-127,
-183,
-97,
-190,
-23,
-107,
-98,
-130,
-235,
-23,
-10,
-234,
-60,
-212,
-239,
-247,
-126,
-87,
-117,
-239,
-244,
-142,
-153,
-243,
-143,
-18,
-89,
-46,
-22,
-9,
-55,
-5,
-117,
-158,
-128,
-229,
-3,
-126,
-26,
-56,
-151,
-226,
-141,
-20,
-187,
-82,
-123,
-166,
-130,
-6,
-222,
-67,
-88,
-46,
-130,
-74,
-247,
-53,
-44,
-115,
-215,
-170,
-216,
-82,
-123,
-107,
-204,
-97,
-245,
-194,
-8,
-29,
-120,
-138,
-87,
-63,
-255,
-52,
-241,
-76,
-39,
-75,
-98,
-163,
-241,
-46,
-169,
-207,
-215,
-21,
-48,
-117,
-234,
-123,
-51,
-231,
-59,
-0,
-67,
-146,
-20,
-166,
-50,
-0,
-176,
-31,
-166,
-88,
-90,
-23,
-88,
-9,
-83,
-28,
-197,
-180,
-110,
-125,
-99,
-128,
-38,
-192,
-252,
-29,
-191,
-136,
-133,
-179,
-189,
-128,
-69,
-233,
-84,
-42,
-187,
-176,
-61,
-25,
-103,
-68,
-174,
-67,
-38,
-95,
-32,
-166,
-188,
-26,
-34,
-164,
-99,
-35,
-114,
-105,
-6,
-210,
-50,
-52,
-241,
-7,
-184,
-94,
-210,
-17,
-62,
-52,
-190,
-93,
-182,
-251,
-103,
-179,
-128,
-68,
-195,
-17,
-146,
-190,
-22,
-66,
-184,
-43,
-132,
-240,
-136,
-76,
-151,
-191,
-103,
-23,
-245,
-13,
-23,
-22,
-146,
-180,
-181,
-164,
-221,
-36,
-173,
-40,
-233,
-101,
-153,
-141,
-164,
-10,
-243,
-73,
-122,
-170,
-224,
-222,
-152,
-220,
-113,
-140,
-225,
-63,
-42,
-105,
-51,
-6,
-150,
-130,
-83,
-233,
-165,
-91,
-186,
-127,
-165,
-87,
-98,
-246,
-128,
-59,
-157,
-235,
-159,
-137,
-208,
-165,
-142,
-0,
-207,
-50,
-20,
-67,
-76,
-208,
-145,
-17,
-96,
-251,
-17,
-54,
-2,
-188,
-64,
-38,
-0,
-22,
-83,
-31,
-199,
-70,
-198,
-227,
-125,
-180,
-91,
-26,
-139,
-198,
-190,
-15,
-56,
-37,
-66,
-7,
-158,
-103,
-216,
-207,
-143,
-161,
-7,
-251,
-25,
-204,
-230,
-48,
-76,
-175,
-125,
-134,
-255,
-32,
-79,
-99,
-33,
-221,
-135,
-228,
-11,
-132,
-16,
-30,
-9,
-33,
-236,
-24,
-66,
-88,
-40,
-132,
-176,
-158,
-164,
-87,
-148,
-186,
-45,
-89,
-113,
-31,
-246,
-209,
-96,
-207,
-151,
-216,
-136,
-242,
-146,
-6,
-123,
-187,
-172,
-17,
-171,
-44,
-195,
-68,
-179,
-48,
-19,
-244,
-55,
-72,
-8,
-49,
-111,
-1,
-119,
-201,
-190,
-252,
-14,
-22,
-146,
-121,
-232,
-228,
-241,
-30,
-217,
-40,
-58,
-85,
-210,
-121,
-146,
-126,
-36,
-41,
-154,
-233,
-91,
-210,
-145,
-152,
-0,
-184,
-150,
-164,
-195,
-212,
-75,
-63,
-2,
-224,
-63,
-157,
-27,
-215,
-198,
-50,
-91,
-19,
-99,
-128,
-72,
-185,
-137,
-192,
-137,
-145,
-235,
-169,
-35,
-192,
-45,
-120,
-112,
-101,
-5,
-221,
-13,
-152,
-6,
-111,
-25,
-76,
-58,
-159,
-28,
-27,
-1,
-188,
-221,
-78,
-124,
-225,
-38,
-152,
-106,
-247,
-231,
-17,
-186,
-113,
-88,
-166,
-243,
-206,
-30,
-192,
-151,
-18,
-217,
-159,
-55,
-50,
-58,
-81,
-208,
-238,
-129,
-152,
-13,
-97,
-75,
-108,
-133,
-116,
-3,
-240,
-157,
-170,
-231,
-42,
-121,
-94,
-128,
-207,
-96,
-246,
-141,
-151,
-177,
-125,
-7,
-122,
-23,
-118,
-134,
-5,
-54,
-30,
-229,
-199,
-215,
-96,
-214,
-176,
-91,
-34,
-116,
-243,
-99,
-238,
-226,
-75,
-98,
-142,
-35,
-83,
-137,
-108,
-221,
-86,
-131,
-1,
-246,
-194,
-156,
-80,
-62,
-4,
-172,
-136,
-229,
-5,
-216,
-45,
-66,
-247,
-78,
-103,
-182,
-87,
-48,
-139,
-229,
-174,
-37,
-12,
-144,
-141,
-45,
-56,
-0,
-152,
-22,
-161,
-187,
-25,
-11,
-169,
-30,
-135,
-229,
-71,
-250,
-5,
-112,
-107,
-132,
-110,
-213,
-220,
-223,
-215,
-40,
-112,
-148,
-193,
-150,
-130,
-79,
-97,
-82,
-249,
-15,
-169,
-200,
-253,
-95,
-6,
-127,
-142,
-118,
-114,
-255,
-36,
-54,
-56,
-29,
-147,
-52,
-183,
-193,
-252,
-214,
-118,
-45,
-120,
-113,
-27,
-97,
-107,
-216,
-255,
-197,
-162,
-124,
-242,
-251,
-237,
-118,
-232,
-146,
-24,
-192,
-105,
-63,
-130,
-133,
-99,
-205,
-116,
-134,
-250,
-84,
-23,
-207,
-145,
-103,
-128,
-168,
-137,
-24,
-155,
-179,
-179,
-50,
-197,
-150,
-84,
-24,
-191,
-48,
-91,
-194,
-20,
-122,
-104,
-43,
-200,
-180,
-149,
-204,
-0,
-152,
-60,
-52,
-209,
-71,
-138,
-169,
-36,
-140,
-168,
-177,
-74,
-166,
-97,
-67,
-231,
-141,
-152,
-37,
-240,
-3,
-68,
-156,
-43,
-106,
-212,
-7,
-67,
-163,
-91,
-74,
-211,
-184,
-180,
-129,
-76,
-187,
-139,
-49,
-32,
-100,
-157,
-29,
-161,
-251,
-33,
-240,
-107,
-204,
-58,
-184,
-16,
-230,
-49,
-84,
-234,
-41,
-12,
-236,
-141,
-41,
-137,
-122,
-174,
-66,
-175,
-201,
-0,
-79,
-98,
-250,
-152,
-69,
-177,
-216,
-198,
-241,
-77,
-26,
-252,
-7,
-22,
-230,
-220,
-217,
-135,
-230,
-104,
-224,
-239,
-181,
-43,
-26,
-168,
-47,
-134,
-82,
-151,
-237,
-54,
-144,
-105,
-107,
-38,
-230,
-171,
-112,
-10,
-241,
-173,
-89,
-231,
-199,
-194,
-179,
-158,
-197,
-134,
-236,
-243,
-168,
-222,
-0,
-234,
-22,
-224,
-179,
-45,
-244,
-177,
-213,
-157,
-202,
-188,
-190,
-243,
-233,
-38,
-132,
-14,
-248,
-148,
-191,
-184,
-253,
-49,
-65,
-240,
-1,
-224,
-208,
-110,
-59,
-215,
-111,
-248,
-51,
-164,
-200,
-30,
-187,
-251,
-151,
-179,
-157,
-63,
-251,
-179,
-148,
-7,
-107,
-116,
-146,
-47,
-44,
-217,
-66,
-31,
-239,
-200,
-48,
-192,
-237,
-45,
-212,
-55,
-30,
-115,
-209,
-159,
-142,
-109,
-47,
-91,
-223,
-153,
-5,
-203,
-171,
-251,
-15,
-127,
-17,
-211,
-128,
-47,
-85,
-125,
-17,
-253,
-4,
-22,
-45,
-124,
-154,
-247,
-237,
-25,
-34,
-137,
-161,
-157,
-46,
-149,
-1,
-38,
-147,
-209,
-239,
-99,
-94,
-66,
-49,
-227,
-77,
-231,
-254,
-111,
-40,
-217,
-129,
-12,
-203,
-104,
-50,
-209,
-153,
-228,
-206,
-70,
-195,
-112,
-67,
-120,
-123,
-7,
-249,
-135,
-123,
-11,
-240,
-219,
-126,
-53,
-12,
-53,
-247,
-186,
-43,
-168,
-99,
-8,
-34,
-116,
-7,
-99,
-235,
-250,
-119,
-97,
-110,
-107,
-79,
-1,
-187,
-23,
-212,
-151,
-194,
-0,
-47,
-98,
-38,
-212,
-206,
-249,
-86,
-68,
-20,
-55,
-126,
-111,
-21,
-159,
-82,
-10,
-179,
-112,
-249,
-87,
-253,
-77,
-204,
-135,
-240,
-43,
-64,
-212,
-220,
-235,
-253,
-107,
-85,
-165,
-141,
-9,
-180,
-59,
-249,
-241,
-102,
-244,
-58,
-56,
-6,
-56,
-28,
-19,
-174,
-240,
-161,
-39,
-105,
-79,
-191,
-130,
-186,
-242,
-14,
-149,
-151,
-17,
-9,
-156,
-196,
-150,
-166,
-95,
-204,
-156,
-159,
-76,
-68,
-7,
-94,
-131,
-1,
-174,
-196,
-214,
-254,
-227,
-176,
-117,
-251,
-31,
-129,
-95,
-23,
-208,
-126,
-11,
-184,
-170,
-162,
-190,
-89,
-157,
-175,
-30,
-19,
-66,
-135,
-232,
-247,
-51,
-253,
-171,
-197,
-0,
-20,
-120,
-92,
-103,
-238,
-159,
-238,
-31,
-196,
-193,
-192,
-73,
-192,
-53,
-101,
-244,
-93,
-193,
-27,
-121,
-209,
-135,
-156,
-21,
-176,
-124,
-193,
-155,
-69,
-232,
-222,
-135,
-101,
-5,
-123,
-17,
-27,
-182,
-47,
-7,
-86,
-175,
-168,
-123,
-107,
-108,
-41,
-51,
-54,
-114,
-111,
-42,
-153,
-204,
-223,
-152,
-222,
-160,
-177,
-39,
-141,
-247,
-253,
-82,
-6,
-166,
-148,
-11,
-137,
-236,
-78,
-234,
-95,
-244,
-139,
-84,
-88,
-219,
-178,
-63,
-44,
-230,
-36,
-251,
-199,
-42,
-58,
-63,
-239,
-154,
-1,
-156,
-102,
-87,
-76,
-249,
-4,
-137,
-249,
-144,
-26,
-1,
-83,
-194,
-156,
-154,
-64,
-247,
-117,
-224,
-8,
-76,
-149,
-185,
-10,
-54,
-135,
-22,
-174,
-42,
-48,
-85,
-244,
-109,
-192,
-87,
-10,
-238,
-191,
-204,
-96,
-159,
-197,
-109,
-129,
-215,
-154,
-61,
-69,
-79,
-60,
-184,
-94,
-0,
-0,
-18,
-40,
-73,
-68,
-65,
-84,
-251,
-200,
-49,
-192,
-115,
-192,
-7,
-170,
-232,
-252,
-188,
-200,
-239,
-98,
-69,
-76,
-178,
-159,
-226,
-101,
-30,
-163,
-64,
-238,
-201,
-149,
-251,
-36,
-53,
-118,
-111,
-205,
-218,
-2,
-82,
-57,
-115,
-13,
-73,
-149,
-203,
-195,
-16,
-194,
-81,
-33,
-132,
-111,
-134,
-16,
-38,
-133,
-16,
-30,
-144,
-237,
-23,
-92,
-54,
-2,
-252,
-135,
-108,
-31,
-194,
-175,
-22,
-220,
-207,
-247,
-101,
-94,
-73,
-175,
-87,
-245,
-99,
-152,
-48,
-70,
-233,
-190,
-121,
-69,
-116,
-157,
-60,
-74,
-239,
-147,
-244,
-188,
-164,
-93,
-36,
-253,
-45,
-79,
-132,
-173,
-253,
-247,
-195,
-54,
-201,
-88,
-92,
-182,
-181,
-221,
-16,
-5,
-94,
-17,
-154,
-40,
-52,
-130,
-226,
-102,
-201,
-40,
-48,
-231,
-142,
-119,
-201,
-12,
-30,
-81,
-243,
-40,
-230,
-17,
-115,
-138,
-164,
-207,
-135,
-16,
-138,
-180,
-113,
-79,
-203,
-28,
-58,
-59,
-88,
-212,
-175,
-141,
-68,
-156,
-45,
-233,
-187,
-192,
-191,
-66,
-8,
-249,
-101,
-94,
-146,
-81,
-75,
-182,
-41,
-247,
-255,
-132,
-16,
-238,
-4,
-8,
-33,
-220,
-86,
-210,
-222,
-161,
-50,
-195,
-82,
-144,
-52,
-81,
-210,
-190,
-181,
-123,
-92,
-99,
-104,
-186,
-139,
-136,
-249,
-178,
-164,
-78,
-48,
-1,
-233,
-28,
-10,
-220,
-165,
-176,
-181,
-235,
-109,
-148,
-44,
-59,
-49,
-161,
-237,
-212,
-204,
-249,
-233,
-84,
-11,
-102,
-125,
-219,
-166,
-62,
-55,
-5,
-4,
-151,
-47,
-254,
-26,
-161,
-75,
-53,
-106,
-77,
-192,
-220,
-237,
-30,
-197,
-150,
-150,
-221,
-229,
-1,
-174,
-243,
-0,
-126,
-94,
-196,
-0,
-31,
-193,
-92,
-164,
-14,
-192,
-92,
-167,
-86,
-195,
-118,
-167,
-140,
-213,
-185,
-152,
-207,
-101,
-31,
-196,
-116,
-253,
-223,
-143,
-208,
-140,
-245,
-249,
-189,
-116,
-183,
-76,
-204,
-104,
-244,
-12,
-166,
-60,
-121,
-47,
-166,
-175,
-56,
-168,
-162,
-204,
-21,
-88,
-58,
-150,
-40,
-99,
-81,
-99,
-107,
-122,
-42,
-182,
-164,
-143,
-188,
-191,
-9,
-192,
-16,
-239,
-99,
-18,
-141,
-90,
-78,
-59,
-47,
-102,
-163,
-152,
-137,
-41,
-121,
-26,
-219,
-72,
-42,
-17,
-121,
-128,
-66,
-135,
-11,
-44,
-140,
-60,
-187,
-12,
-188,
-60,
-70,
-151,
-43,
-179,
-47,
-145,
-120,
-60,
-44,
-173,
-203,
-101,
-164,
-133,
-87,
-127,
-145,
-1,
-69,
-213,
-233,
-116,
-153,
-24,
-153,
-196,
-173,
-233,
-73,
-216,
-146,
-62,
-55,
-2,
-108,
-134,
-105,
-23,
-155,
-121,
-232,
-14,
-173,
-251,
-57,
-10,
-140,
-115,
-173,
-193,
-31,
-32,
-235,
-236,
-121,
-120,
-17,
-3,
-228,
-202,
-36,
-41,
-130,
-176,
-244,
-48,
-49,
-171,
-92,
-20,
-245,
-159,
-96,
-80,
-157,
-149,
-78,
-166,
-109,
-35,
-199,
-0,
-19,
-177,
-81,
-114,
-136,
-215,
-113,
-141,
-250,
-254,
-11,
-27,
-233,
-222,
-234,
-117,
-157,
-69,
-36,
-99,
-121,
-107,
-240,
-7,
-200,
-206,
-77,
-15,
-116,
-243,
-67,
-248,
-215,
-250,
-78,
-44,
-213,
-250,
-54,
-36,
-6,
-110,
-118,
-11,
-18,
-157,
-76,
-157,
-54,
-197,
-27,
-121,
-127,
-76,
-203,
-247,
-146,
-255,
-47,
-220,
-168,
-185,
-77,
-96,
-166,
-246,
-71,
-49,
-211,
-251,
-27,
-254,
-28,
-27,
-20,
-208,
-110,
-129,
-5,
-235,
-118,
-204,
-193,
-73,
-50,
-90,
-190,
-18,
-48,
-109,
-219,
-11,
-192,
-189,
-152,
-121,
-177,
-27,
-6,
-184,
-16,
-211,
-78,
-189,
-234,
-204,
-244,
-101,
-250,
-16,
-54,
-141,
-237,
-154,
-113,
-72,
-230,
-124,
-15,
-224,
-225,
-8,
-93,
-170,
-55,
-242,
-54,
-152,
-60,
-180,
-4,
-182,
-81,
-228,
-255,
-244,
-250,
-25,
-34,
-125,
-168,
-210,
-4,
-62,
-132,
-237,
-243,
-180,
-24,
-150,
-225,
-116,
-124,
-147,
-70,
-160,
-124,
-111,
-251,
-57,
-2,
-164,
-59,
-153,
-38,
-49,
-74,
-174,
-204,
-22,
-68,
-92,
-179,
-107,
-246,
-47,
-41,
-9,
-86,
-174,
-76,
-21,
-3,
-60,
-5,
-28,
-219,
-77,
-191,
-134,
-8,
-129,
-35,
-21,
-84,
-44,
-237,
-48,
-129,
-105,
-111,
-42,
-132,
-202,
-26,
-140,
-50,
-214,
-135,
-227,
-39,
-241,
-189,
-4,
-122,
-241,
-92,
-221,
-0,
-19,
-176,
-103,
-0,
-127,
-198,
-183,
-252,
-105,
-82,
-73,
-45,
-6,
-160,
-98,
-89,
-84,
-179,
-221,
-221,
-72,
-136,
-129,
-115,
-250,
-170,
-165,
-93,
-170,
-147,
-105,
-42,
-163,
-252,
-5,
-243,
-143,
-92,
-15,
-91,
-218,
-165,
-134,
-163,
-245,
-53,
-112,
-5,
-147,
-181,
-78,
-195,
-150,
-151,
-69,
-94,
-198,
-173,
-53,
-86,
-185,
-44,
-170,
-81,
-23,
-152,
-67,
-106,
-101,
-12,
-92,
-98,
-125,
-169,
-78,
-166,
-169,
-140,
-50,
-3,
-247,
-247,
-119,
-134,
-25,
-145,
-12,
-144,
-105,
-247,
-195,
-20,
-152,
-181,
-251,
-221,
-145,
-249,
-177,
-180,
-104,
-51,
-48,
-97,
-235,
-253,
-177,
-23,
-226,
-12,
-144,
-18,
-3,
-55,
-104,
-132,
-42,
-123,
-193,
-36,
-56,
-153,
-214,
-96,
-148,
-251,
-49,
-211,
-243,
-242,
-62,
-26,
-140,
-40,
-6,
-192,
-4,
-191,
-15,
-99,
-182,
-128,
-37,
-48,
-79,
-237,
-59,
-122,
-217,
-96,
-210,
-15,
-129,
-217,
-165,
-31,
-194,
-188,
-84,
-214,
-197,
-252,
-214,
-138,
-24,
-32,
-37,
-6,
-46,
-153,
-1,
-106,
-60,
-75,
-10,
-163,
-124,
-8,
-243,
-25,
-124,
-12,
-83,
-198,
-12,
-73,
-1,
-231,
-116,
-89,
-134,
-159,
-236,
-140,
-92,
-217,
-63,
-204,
-185,
-101,
-136,
-74,
-155,
-193,
-129,
-58,
-207,
-98,
-190,
-141,
-177,
-228,
-147,
-139,
-98,
-102,
-247,
-23,
-48,
-129,
-242,
-58,
-224,
-29,
-169,
-239,
-160,
-54,
-106,
-48,
-192,
-36,
-50,
-91,
-190,
-224,
-169,
-81,
-10,
-234,
-203,
-154,
-121,
-63,
-16,
-123,
-201,
-189,
-96,
-128,
-54,
-17,
-97,
-248,
-127,
-149,
-245,
-15,
-155,
-62,
-47,
-246,
-15,
-99,
-136,
-241,
-134,
-193,
-129,
-58,
-107,
-251,
-113,
-235,
-62,
-154,
-85,
-94,
-176,
-221,
-248,
-4,
-142,
-211,
-224,
-144,
-177,
-178,
-140,
-24,
-43,
-103,
-142,
-87,
-171,
-160,
-29,
-169,
-216,
-87,
-150,
-12,
-243,
-158,
-16,
-194,
-93,
-146,
-138,
-108,
-6,
-203,
-97,
-17,
-67,
-151,
-72,
-250,
-173,
-164,
-117,
-67,
-8,
-67,
-34,
-151,
-36,
-29,
-144,
-169,
-239,
-30,
-89,
-130,
-201,
-131,
-218,
-238,
-244,
-160,
-31,
-152,
-92,
-170,
-88,
-73,
-55,
-118,
-81,
-247,
-171,
-210,
-32,
-179,
-241,
-204,
-18,
-218,
-38,
-49,
-112,
-35,
-45,
-141,
-91,
-42,
-195,
-79,
-150,
-180,
-130,
-164,
-119,
-135,
-16,
-126,
-26,
-203,
-197,
-236,
-88,
-89,
-210,
-3,
-185,
-250,
-74,
-61,
-170,
-154,
-160,
-81,
-170,
-216,
-28,
-138,
-126,
-136,
-71,
-52,
-248,
-203,
-46,
-203,
-187,
-115,
-129,
-164,
-203,
-100,
-182,
-236,
-107,
-20,
-73,
-247,
-170,
-116,
-59,
-122,
-45,
-43,
-95,
-139,
-72,
-101,
-248,
-141,
-100,
-161,
-227,
-119,
-96,
-22,
-213,
-162,
-140,
-34,
-111,
-104,
-112,
-90,
-221,
-222,
-123,
-104,
-167,
-12,
-249,
-46,
-104,
-84,
-26,
-141,
-128,
-207,
-97,
-42,
-229,
-53,
-49,
-147,
-241,
-173,
-37,
-50,
-64,
-74,
-58,
-180,
-36,
-59,
-186,
-211,
-86,
-90,
-249,
-48,
-255,
-132,
-231,
-177,
-37,
-232,
-62,
-152,
-4,
-253,
-229,
-46,
-250,
-119,
-87,
-86,
-136,
-164,
-66,
-149,
-142,
-121,
-55,
-95,
-238,
-178,
-210,
-144,
-20,
-59,
-216,
-166,
-22,
-71,
-103,
-206,
-163,
-129,
-58,
-88,
-112,
-75,
-212,
-70,
-80,
-11,
-36,
-26,
-61,
-82,
-127,
-8,
-76,
-177,
-242,
-83,
-204,
-64,
-113,
-63,
-190,
-45,
-125,
-132,
-46,
-245,
-5,
-39,
-219,
-209,
-83,
-0,
-156,
-138,
-5,
-184,
-238,
-131,
-249,
-213,
-191,
-72,
-36,
-147,
-119,
-141,
-254,
-229,
-25,
-126,
-98,
-74,
-255,
-128,
-77,
-137,
-120,
-241,
-2,
-135,
-248,
-123,
-235,
-8,
-129,
-209,
-64,
-29,
-76,
-11,
-120,
-15,
-150,
-0,
-59,
-26,
-167,
-153,
-4,
-18,
-141,
-30,
-77,
-127,
-8,
-138,
-87,
-11,
-73,
-47,
-56,
-21,
-249,
-250,
-138,
-218,
-109,
-90,
-95,
-9,
-93,
-158,
-225,
-119,
-239,
-178,
-221,
-128,
-237,
-199,
-52,
-141,
-138,
-64,
-29,
-108,
-201,
-184,
-191,
-51,
-242,
-119,
-128,
-101,
-155,
-182,
-219,
-169,
-176,
-107,
-163,
-71,
-164,
-206,
-126,
-41,
-70,
-122,
-193,
-0,
-71,
-210,
-239,
-157,
-185,
-26,
-0,
-120,
-19,
-150,
-243,
-32,
-57,
-197,
-111,
-214,
-43,
-120,
-182,
-209,
-67,
-182,
-11,
-70,
-95,
-247,
-192,
-29,
-225,
-56,
-92,
-210,
-33,
-178,
-93,
-73,
-158,
-144,
-229,
-228,
-27,
-49,
-192,
-100,
-158,
-3,
-36,
-221,
-46,
-233,
-53,
-153,
-160,
-89,
-187,
-146,
-70,
-70,
-143,
-145,
-134,
-30,
-141,
-0,
-61,
-79,
-162,
-221,
-20,
-216,
-74,
-98,
-18,
-230,
-86,
-215,
-60,
-7,
-51,
-13,
-141,
-30,
-253,
-6,
-245,
-157,
-51,
-187,
-74,
-38,
-229,
-245,
-245,
-60,
-137,
-118,
-83,
-96,
-78,
-60,
-155,
-54,
-45,
-159,
-21,
-42,
-30,
-151,
-180,
-49,
-150,
-219,
-174,
-112,
-99,
-167,
-130,
-78,
-52,
-94,
-163,
-250,
-11,
-78,
-205,
-36,
-242,
-93,
-89,
-14,
-222,
-53,
-36,
-45,
-77,
-220,
-10,
-217,
-11,
-125,
-65,
-146,
-60,
-132,
-37,
-153,
-56,
-20,
-216,
-147,
-72,
-242,
-199,
-58,
-72,
-21,
-62,
-67,
-8,
-219,
-68,
-242,
-17,
-55,
-106,
-48,
-201,
-232,
-225,
-180,
-201,
-155,
-75,
-37,
-180,
-155,
-204,
-0,
-137,
-245,
-181,
-173,
-47,
-72,
-158,
-82,
-176,
-112,
-181,
-89,
-152,
-175,
-193,
-184,
-46,
-159,
-35,
-137,
-1,
-134,
-5,
-46,
-9,
-175,
-236,
-12,
-240,
-28,
-112,
-68,
-1,
-221,
-170,
-192,
-245,
-152,
-110,
-225,
-62,
-96,
-255,
-8,
-77,
-7,
-165,
-105,
-221,
-72,
-183,
-142,
-181,
-173,
-47,
-168,
-195,
-0,
-151,
-96,
-145,
-193,
-151,
-23,
-140,
-78,
-117,
-219,
-237,
-139,
-3,
-106,
-109,
-164,
-14,
-249,
-152,
-139,
-210,
-165,
-88,
-36,
-238,
-22,
-192,
-187,
-35,
-52,
-144,
-150,
-214,
-173,
-85,
-235,
-88,
-13,
-134,
-74,
-102,
-128,
-12,
-205,
-130,
-152,
-71,
-111,
-227,
-47,
-216,
-219,
-61,
-154,
-68,
-79,
-169,
-190,
-33,
-245,
-197,
-57,
-237,
-12,
-96,
-231,
-138,
-250,
-96,
-112,
-86,
-175,
-253,
-137,
-103,
-39,
-155,
-157,
-198,
-206,
-207,
-63,
-77,
-119,
-83,
-79,
-219,
-12,
-181,
-16,
-230,
-15,
-240,
-44,
-166,
-8,
-186,
-22,
-248,
-65,
-23,
-245,
-65,
-13,
-79,
-41,
-170,
-125,
-37,
-219,
-201,
-75,
-84,
-231,
-197,
-249,
-67,
-140,
-175,
-168,
-47,
-207,
-0,
-69,
-105,
-221,
-166,
-147,
-9,
-65,
-195,
-28,
-71,
-186,
-201,
-98,
-214,
-54,
-67,
-157,
-137,
-169,
-208,
-215,
-240,
-247,
-114,
-15,
-5,
-89,
-66,
-18,
-235,
-131,
-4,
-79,
-169,
-204,
-253,
-42,
-95,
-201,
-234,
-188,
-68,
-41,
-92,
-82,
-231,
-197,
-213,
-96,
-128,
-148,
-180,
-110,
-211,
-128,
-236,
-246,
-40,
-221,
-166,
-177,
-107,
-155,
-161,
-166,
-228,
-126,
-176,
-131,
-40,
-200,
-18,
-146,
-88,
-31,
-12,
-94,
-126,
-70,
-61,
-165,
-186,
-69,
-158,
-91,
-178,
-38,
-204,
-89,
-5,
-101,
-146,
-236,
-212,
-12,
-172,
-34,
-254,
-140,
-185,
-92,
-149,
-249,
-3,
-252,
-65,
-210,
-51,
-178,
-188,
-185,
-23,
-73,
-138,
-249,
-184,
-223,
-39,
-219,
-242,
-165,
-131,
-85,
-84,
-226,
-56,
-82,
-53,
-36,
-170,
-125,
-115,
-235,
-91,
-101,
-90,
-194,
-14,
-158,
-145,
-45,
-73,
-219,
-66,
-52,
-91,
-56,
-9,
-70,
-188,
-50,
-154,
-65,
-15,
-29,
-66,
-88,
-63,
-132,
-112,
-91,
-8,
-97,
-98,
-8,
-161,
-200,
-175,
-44,
-233,
-197,
-133,
-16,
-58,
-46,
-214,
-219,
-134,
-16,
-230,
-13,
-33,
-148,
-229,
-34,
-216,
-222,
-105,
-198,
-133,
-16,
-78,
-8,
-33,
-196,
-50,
-127,
-156,
-39,
-233,
-176,
-206,
-212,
-35,
-233,
-147,
-178,
-77,
-160,
-138,
-112,
-129,
-164,
-159,
-73,
-42,
-218,
-233,
-35,
-153,
-161,
-18,
-231,
-207,
-71,
-37,
-45,
-151,
-57,
-31,
-39,
-115,
-254,
-232,
-6,
-41,
-158,
-82,
-79,
-200,
-156,
-104,
-86,
-148,
-169,
-240,
-99,
-106,
-234,
-20,
-26,
-137,
-180,
-88,
-185,
-84,
-59,
-245,
-252,
-62,
-140,
-149,
-234,
-165,
-243,
-50,
-64,
-9,
-93,
-178,
-117,
-44,
-5,
-36,
-154,
-91,
-157,
-182,
-114,
-254,
-4,
-190,
-128,
-173,
-96,
-58,
-66,
-219,
-100,
-42,
-194,
-215,
-43,
-250,
-135,
-183,
-187,
-38,
-176,
-22,
-240,
-32,
-240,
-153,
-138,
-50,
-149,
-70,
-188,
-66,
-26,
-210,
-99,
-229,
-82,
-237,
-212,
-251,
-49,
-32,
-20,
-205,
-78,
-182,
-156,
-240,
-236,
-93,
-129,
-244,
-229,
-93,
-219,
-12,
-53,
-63,
-38,
-8,
-62,
-225,
-245,
-157,
-22,
-107,
-183,
-70,
-125,
-175,
-99,
-91,
-249,
-150,
-102,
-11,
-39,
-33,
-114,
-41,
-133,
-70,
-164,
-7,
-85,
-38,
-189,
-56,
-10,
-208,
-232,
-109,
-212,
-0,
-195,
-164,
-47,
-24,
-46,
-144,
-96,
-196,
-75,
-161,
-17,
-137,
-177,
-114,
-35,
-29,
-140,
-112,
-125,
-65,
-219,
-32,
-193,
-136,
-87,
-70,
-147,
-223,
-147,
-38,
-101,
-231,
-142,
-145,
-142,
-182,
-189,
-105,
-251,
-226,
-158,
-221,
-5,
-82,
-140,
-120,
-213,
-52,
-36,
-198,
-202,
-245,
-27,
-62,
-4,
-127,
-25,
-91,
-103,
-191,
-72,
-197,
-14,
-89,
-140,
-112,
-125,
-65,
-219,
-32,
-193,
-136,
-151,
-66,
-35,
-18,
-99,
-229,
-250,
-13,
-224,
-40,
-108,
-101,
-50,
-1,
-75,
-56,
-89,
-149,
-76,
-42,
-105,
-149,
-82,
-163,
-253,
-70,
-12,
-69,
-130,
-169,
-25,
-115,
-72,
-189,
-39,
-161,
-174,
-9,
-254,
-129,
-150,
-230,
-17,
-232,
-26,
-212,
-216,
-185,
-131,
-132,
-240,
-112,
-224,
-91,
-153,
-227,
-111,
-84,
-220,
-31,
-162,
-253,
-243,
-235,
-183,
-147,
-176,
-33,
-99,
-134,
-62,
-121,
-121,
-151,
-88,
-95,
-35,
-134,
-34,
-205,
-212,
-188,
-31,
-80,
-169,
-43,
-192,
-18,
-64,
-156,
-76,
-127,
-54,
-191,
-170,
-6,
-105,
-89,
-179,
-182,
-0,
-206,
-247,
-227,
-205,
-128,
-239,
-69,
-104,
-190,
-207,
-64,
-98,
-165,
-243,
-137,
-120,
-182,
-96,
-75,
-160,
-104,
-218,
-213,
-130,
-190,
-13,
-155,
-190,
-160,
-87,
-240,
-15,
-109,
-124,
-63,
-219,
-236,
-26,
-152,
-143,
-218,
-134,
-249,
-227,
-28,
-205,
-134,
-157,
-57,
-221,
-143,
-127,
-31,
-161,
-121,
-53,
-59,
-4,
-247,
-27,
-77,
-24,
-138,
-244,
-24,
-139,
-13,
-128,
-202,
-116,
-183,
-206,
-0,
-133,
-155,
-89,
-116,
-131,
-70,
-95,
-6,
-105,
-105,
-216,
-230,
-41,
-56,
-46,
-163,
-139,
-169,
-139,
-31,
-80,
-205,
-45,
-209,
-219,
-68,
-8,
-129,
-16,
-194,
-151,
-66,
-8,
-139,
-251,
-223,
-137,
-37,
-241,
-124,
-29,
-164,
-169,
-94,
-165,
-93,
-53,
-120,
-197,
-50,
-8,
-152,
-95,
-193,
-129,
-50,
-27,
-77,
-105,
-254,
-162,
-146,
-58,
-90,
-221,
-158,
-166,
-142,
-198,
-112,
-171,
-204,
-20,
-48,
-123,
-58,
-200,
-209,
-124,
-31,
-216,
-210,
-143,
-207,
-39,
-226,
-64,
-129,
-69,
-20,
-61,
-142,
-185,
-91,
-173,
-74,
-65,
-86,
-210,
-145,
-10,
-10,
-84,
-175,
-192,
-58,
-152,
-215,
-210,
-129,
-37,
-101,
-193,
-164,
-247,
-198,
-35,
-32,
-169,
-219,
-211,
-144,
-184,
-220,
-162,
-70,
-118,
-45,
-50,
-187,
-112,
-17,
-17,
-22,
-169,
-16,
-18,
-253,
-250,
-60,
-152,
-38,
-110,
-42,
-166,
-208,
-24,
-178,
-153,
-196,
-72,
-3,
-105,
-234,
-217,
-75,
-171,
-158,
-5,
-179,
-104,
-30,
-139,
-41,
-233,
-150,
-232,
-93,
-143,
-149,
-190,
-220,
-98,
-4,
-106,
-12,
-201,
-8,
-138,
-20,
-8,
-141,
-62,
-143,
-79,
-194,
-86,
-57,
-189,
-203,
-185,
-171,
-100,
-245,
-236,
-244,
-148,
-121,
-29,
-203,
-23,
-12,
-176,
-69,
-131,
-126,
-108,
-130,
-173,
-253,
-241,
-81,
-180,
-216,
-199,
-144,
-196,
-229,
-22,
-137,
-217,
-181,
-250,
-9,
-127,
-200,
-45,
-242,
-199,
-57,
-154,
-77,
-129,
-5,
-48,
-199,
-147,
-194,
-36,
-74,
-88,
-142,
-223,
-117,
-75,
-238,
-167,
-216,
-223,
-83,
-212,
-179,
-47,
-146,
-152,
-151,
-209,
-127,
-192,
-241,
-41,
-180,
-145,
-178,
-99,
-189,
-124,
-212,
-16,
-151,
-21,
-2,
-87,
-151,
-84,
-232,
-114,
-148,
-193,
-100,
-89,
-86,
-139,
-87,
-178,
-127,
-77,
-58,
-215,
-34,
-158,
-245,
-191,
-252,
-241,
-108,
-132,
-16,
-110,
-10,
-33,
-188,
-44,
-105,
-99,
-21,
-36,
-190,
-192,
-172,
-109,
-71,
-75,
-90,
-172,
-164,
-173,
-20,
-1,
-47,
-69,
-61,
-123,
-147,
-164,
-99,
-48,
-185,
-102,
-107,
-224,
-240,
-146,
-54,
-123,
-15,
-18,
-151,
-91,
-140,
-64,
-141,
-33,
-150,
-24,
-105,
-217,
-252,
-113,
-132,
-110,
-83,
-204,
-89,
-51,
-186,
-247,
-31,
-3,
-123,
-238,
-60,
-133,
-201,
-28,
-165,
-31,
-4,
-197,
-2,
-94,
-138,
-122,
-118,
-101,
-44,
-170,
-231,
-37,
-76,
-183,
-80,
-168,
-134,
-247,
-169,
-235,
-44,
-26,
-200,
-1,
-85,
-35,
-64,
-150,
-240,
-30,
-224,
-184,
-196,
-74,
-187,
-218,
-235,
-151,
-8,
-34,
-52,
-233,
-243,
-87,
-90,
-155,
-219,
-96,
-49,
-244,
-11,
-39,
-244,
-173,
-208,
-157,
-155,
-97,
-200,
-28,
-138,
-233,
-11,
-254,
-66,
-131,
-152,
-196,
-58,
-12,
-208,
-183,
-229,
-150,
-119,
-104,
-208,
-190,
-194,
-5,
-116,
-105,
-157,
-79,
-107,
-243,
-101,
-204,
-148,
-59,
-201,
-255,
-162,
-158,
-74,
-9,
-12,
-80,
-41,
-224,
-141,
-36,
-84,
-189,
-195,
-172,
-226,
-229,
-44,
-153,
-99,
-227,
-143,
-37,
-45,
-33,
-233,
-247,
-254,
-215,
-43,
-188,
-222,
-79,
-217,
-33,
-132,
-208,
-86,
-82,
-169,
-141,
-36,
-237,
-229,
-123,
-249,
-172,
-221,
-82,
-157,
-195,
-134,
-217,
-66,
-96,
-8,
-97,
-86,
-8,
-225,
-216,
-16,
-194,
-10,
-178,
-132,
-77,
-61,
-223,
-233,
-123,
-14,
-69,
-227,
-32,
-218,
-57,
-6,
-192,
-210,
-68,
-182,
-119,
-105,
-177,
-254,
-74,
-25,
-192,
-233,
-42,
-167,
-0,
-90,
-206,
-8,
-134,
-109,
-206,
-176,
-101,
-201,
-253,
-228,
-32,
-218,
-132,
-182,
-160,
-197,
-192,
-216,
-130,
-54,
-234,
-77,
-163,
-192,
-95,
-49,
-19,
-232,
-39,
-11,
-238,
-39,
-101,
-9,
-199,
-76,
-161,
-215,
-3,
-167,
-71,
-238,
-181,
-38,
-3,
-144,
-184,
-239,
-79,
-42,
-48,
-111,
-222,
-51,
-129,
-69,
-128,
-229,
-170,
-75,
-116,
-213,
-86,
-33,
-3,
-84,
-49,
-54,
-166,
-71,
-184,
-25,
-216,
-190,
-164,
-254,
-77,
-48,
-33,
-29,
-103,
-216,
-238,
-130,
-77,
-169,
-145,
-37,
-28,
-183,
-153,
-3,
-255,
-136,
-220,
-75,
-226,
-252,
-218,
-220,
-91,
-94,
-87,
-210,
-22,
-114,
-152,
-13,
-99,
-18,
-230,
-149,
-219,
-120,
-107,
-218,
-196,
-62,
-149,
-49,
-64,
-41,
-99,
-99,
-91,
-218,
-126,
-25,
-75,
-233,
-51,
-242,
-224,
-35,
-197,
-117,
-196,
-13,
-69,
-173,
-48,
-0,
-17,
-148,
-212,
-85,
-26,
-59,
-55,
-28,
-72,
-125,
-15,
-37,
-229,
-199,
-151,
-61,
-115,
-207,
-224,
-29,
-239,
-54,
-244,
-185,
-235,
-41,
-32,
-181,
-158,
-145,
-138,
-34,
-6,
-32,
-81,
-7,
-50,
-34,
-25,
-128,
-52,
-93,
-121,
-229,
-151,
-75,
-194,
-252,
-149,
-242,
-5,
-213,
-25,
-37,
-218,
-0,
-53,
-236,
-239,
-101,
-253,
-175,
-98,
-254,
-12,
-205,
-235,
-192,
-129,
-64,
-163,
-101,
-110,
-62,
-89,
-244,
-78,
-88,
-182,
-203,
-87,
-129,
-235,
-155,
-84,
-168,
-4,
-93,
-121,
-136,
-32,
-66,
-115,
-115,
-8,
-97,
-5,
-191,
-189,
-124,
-8,
-97,
-200,
-198,
-201,
-53,
-176,
-179,
-6,
-187,
-187,
-247,
-18,
-41,
-1,
-182,
-173,
-32,
-132,
-48,
-69,
-210,
-30,
-50,
-29,
-78,
-101,
-36,
-114,
-229,
-71,
-128,
-237,
-85,
-123,
-18,
-182,
-9,
-65,
-97,
-182,
-201,
-178,
-17,
-32,
-71,
-215,
-122,
-194,
-201,
-72,
-63,
-170,
-70,
-146,
-202,
-81,
-194,
-233,
-170,
-178,
-143,
-125,
-17,
-91,
-33,
-188,
-132,
-173,
-148,
-186,
-86,
-2,
-181,
-48,
-2,
-44,
-129,
-153,
-231,
-143,
-37,
-109,
-251,
-219,
-78,
-220,
-226,
-154,
-157,
-107,
-121,
-129,
-104,
-5,
-73,
-215,
-132,
-16,
-166,
-135,
-16,
-158,
-80,
-3,
-208,
-255,
-132,
-147,
-93,
-127,
-221,
-164,
-101,
-31,
-123,
-70,
-22,
-145,
-252,
-14,
-89,
-100,
-241,
-144,
-200,
-228,
-126,
-79,
-55,
-146,
-214,
-145,
-237,
-168,
-126,
-86,
-8,
-225,
-153,
-42,
-226,
-16,
-194,
-164,
-206,
-223,
-144,
-155,
-88,
-98,
-35,
-112,
-75,
-88,
-89,
-69,
-101,
-35,
-0,
-125,
-212,
-149,
-167,
-124,
-221,
-189,
-248,
-81,
-48,
-7,
-214,
-162,
-93,
-77,
-146,
-133,
-210,
-22,
-70,
-128,
-174,
-133,
-192,
-172,
-42,
-120,
-23,
-63,
-220,
-221,
-213,
-193,
-77,
-177,
-145,
-164,
-175,
-135,
-16,
-238,
-84,
-129,
-93,
-29,
-248,
-33,
-112,
-35,
-22,
-199,
-119,
-66,
-228,
-254,
-246,
-126,
-255,
-111,
-180,
-227,
-14,
-93,
-58,
-74,
-52,
-96,
-146,
-247,
-72,
-122,
-168,
-224,
-222,
-235,
-41,
-126,
-18,
-12,
-56,
-210,
-118,
-19,
-101,
-212,
-110,
-144,
-106,
-217,
-151,
-157,
-74,
-71,
-194,
-46,
-91,
-192,
-124,
-254,
-127,
-94,
-226,
-249,
-128,
-110,
-195,
-20,
-29,
-75,
-2,
-255,
-172,
-232,
-71,
-233,
-23,
-87,
-99,
-148,
-72,
-250,
-114,
-125,
-100,
-123,
-30,
-216,
-175,
-97,
-91,
-203,
-96,
-94,
-73,
-39,
-97,
-171,
-155,
-104,
-230,
-175,
-170,
-17,
-0,
-155,
-255,
-191,
-65,
-133,
-207,
-66,
-45,
-164,
-50,
-64,
-69,
-29,
-41,
-206,
-16,
-159,
-192,
-66,
-157,
-174,
-35,
-238,
-80,
-49,
-37,
-118,
-92,
-208,
-223,
-210,
-47,
-183,
-6,
-3,
-164,
-102,
-43,
-189,
-25,
-248,
-97,
-23,
-253,
-153,
-142,
-169,
-113,
-175,
-39,
-18,
-43,
-145,
-161,
-171,
-98,
-128,
-23,
-48,
-231,
-150,
-230,
-249,
-129,
-11,
-30,
-160,
-231,
-217,
-41,
-49,
-149,
-236,
-155,
-253,
-75,
-24,
-178,
-124,
-193,
-166,
-134,
-37,
-48,
-245,
-237,
-109,
-93,
-182,
-85,
-249,
-117,
-215,
-100,
-128,
-153,
-148,
-251,
-18,
-116,
-173,
-152,
-162,
-109,
-29,
-126,
-141,
-134,
-251,
-197,
-0,
-231,
-96,
-6,
-167,
-223,
-19,
-223,
-36,
-114,
-107,
-76,
-6,
-184,
-145,
-76,
-166,
-172,
-134,
-109,
-165,
-142,
-18,
-165,
-52,
-53,
-218,
-234,
-169,
-117,
-175,
-167,
-232,
-23,
-3,
-140,
-52,
-212,
-249,
-114,
-49,
-69,
-217,
-222,
-37,
-245,
-204,
-81,
-12,
-208,
-52,
-52,
-44,
-201,
-178,
-54,
-135,
-33,
-73,
-122,
-87,
-181,
-228,
-61,
-95,
-183,
-83,
-192,
-136,
-7,
-213,
-89,
-41,
-91,
-217,
-89,
-188,
-162,
-15,
-251,
-249,
-124,
-220,
-181,
-16,
-212,
-214,
-151,
-91,
-103,
-42,
-1,
-62,
-6,
-252,
-185,
-219,
-54,
-11,
-234,
-190,
-24,
-184,
-164,
-23,
-117,
-167,
-52,
-222,
-218,
-206,
-226,
-37,
-109,
-28,
-136,
-45,
-55,
-95,
-109,
-145,
-1,
-250,
-102,
-85,
-196,
-18,
-99,
-79,
-163,
-100,
-163,
-7,
-239,
-199,
-195,
-192,
-123,
-234,
-220,
-243,
-251,
-189,
-241,
-232,
-162,
-69,
-87,
-109,
-74,
-60,
-94,
-170,
-166,
-24,
-103,
-176,
-85,
-48,
-75,
-216,
-16,
-6,
-192,
-118,
-61,
-123,
-8,
-243,
-2,
-62,
-51,
-161,
-47,
-201,
-95,
-110,
-27,
-240,
-175,
-191,
-52,
-49,
-68,
-83,
-6,
-160,
-194,
-163,
-171,
-107,
-80,
-189,
-62,
-77,
-181,
-101,
-23,
-122,
-188,
-144,
-232,
-188,
-225,
-109,
-196,
-24,
-96,
-77,
-175,
-127,
-93,
-224,
-209,
-212,
-103,
-235,
-23,
-176,
-192,
-208,
-115,
-170,
-41,
-251,
-136,
-178,
-47,
-50,
-71,
-151,
-106,
-167,
-46,
-165,
-105,
-3,
-69,
-12,
-224,
-247,
-174,
-195,
-242,
-23,
-124,
-164,
-151,
-125,
-104,
-2,
-204,
-234,
-250,
-209,
-4,
-58,
-40,
-214,
-184,
-22,
-222,
-171,
-131,
-236,
-23,
-118,
-128,
-164,
-159,
-43,
-109,
-227,
-230,
-86,
-80,
-244,
-16,
-109,
-60,
-92,
-8,
-97,
-43,
-73,
-235,
-75,
-58,
-173,
-155,
-122,
-98,
-160,
-194,
-233,
-195,
-251,
-191,
-62,
-166,
-210,
-126,
-58,
-82,
-197,
-210,
-50,
-235,
-226,
-176,
-99,
-118,
-96,
-72,
-8,
-97,
-150,
-164,
-163,
-74,
-104,
-231,
-24,
-0,
-43,
-134,
-16,
-30,
-149,
-25,
-163,
-26,
-167,
-108,
-47,
-65,
-138,
-211,
-199,
-143,
-37,
-157,
-44,
-41,
-230,
-21,
-20,
-100,
-73,
-183,
-135,
-29,
-35,
-198,
-65,
-178,
-101,
-252,
-10,
-184,
-79,
-210,
-197,
-138,
-4,
-111,
-96,
-18,
-255,
-195,
-46,
-40,
-78,
-136,
-220,
-47,
-253,
-194,
-19,
-179,
-170,
-255,
-44,
-132,
-240,
-235,
-16,
-66,
-76,
-216,
-251,
-183,
-164,
-158,
-239,
-108,
-78,
-66,
-130,
-173,
-178,
-20,
-238,
-115,
-44,
-66,
-8,
-27,
-87,
-144,
-156,
-39,
-105,
-127,
-73,
-79,
-75,
-186,
-66,
-150,
-218,
-61,
-139,
-54,
-220,
-186,
-162,
-123,
-47,
-59,
-110,
-147,
-57,
-150,
-244,
-26,
-209,
-80,
-249,
-44,
-230,
-74,
-6,
-72,
-192,
-24,
-217,
-143,
-60,
-83,
-17,
-205,
-94,
-8,
-33,
-187,
-55,
-112,
-211,
-31,
-170,
-76,
-155,
-120,
-165,
-164,
-126,
-164,
-154,
-171,
-100,
-128,
-236,
-222,
-193,
-147,
-128,
-189,
-122,
-222,
-165,
-22,
-225,
-14,
-163,
-77,
-172,
-133,
-31,
-151,
-244,
-11,
-73,
-87,
-169,
-32,
-190,
-175,
-45,
-41,
-187,
-0,
-23,
-72,
-90,
-145,
-18,
-115,
-112,
-75,
-72,
-103,
-0,
-89,
-186,
-182,
-170,
-117,
-247,
-38,
-146,
-110,
-240,
-211,
-235,
-138,
-214,
-248,
-35,
-29,
-33,
-132,
-203,
-67,
-8,
-111,
-247,
-29,
-74,
-174,
-168,
-91,
-30,
-184,
-149,
-72,
-196,
-83,
-141,
-246,
-159,
-147,
-116,
-156,
-164,
-83,
-154,
-214,
-145,
-216,
-206,
-86,
-77,
-125,
-59,
-27,
-131,
-26,
-182,
-236,
-94,
-46,
-3,
-187,
-69,
-197,
-26,
-124,
-34,
-112,
-107,
-147,
-178,
-13,
-250,
-176,
-89,
-183,
-245,
-164,
-54,
-54,
-29,
-88,
-186,
-47,
-141,
-205,
-1,
-24,
-78,
-38,
-196,
-92,
-225,
-198,
-99,
-187,
-169,
-22,
-186,
-231,
-183,
-129,
-172,
-16,
-56,
-70,
-115,
-192,
-254,
-0,
-48,
-160,
-167,
-207,
-7,
-148,
-0,
-127,
-146,
-116,
-73,
-8,
-225,
-59,
-125,
-239,
-88,
-187,
-120,
-80,
-210,
-235,
-146,
-142,
-239,
-219,
-16,
-142,
-5,
-60,
-12,
-91,
-74,
-214,
-84,
-96,
-134,
-144,
-29,
-178,
-140,
-144,
-185,
-183,
-9,
-102,
-101,
-91,
-40,
-114,
-111,
-54,
-18,
-219,
-25,
-246,
-105,
-168,
-31,
-200,
-239,
-24,
-18,
-29,
-1,
-24,
-65,
-14,
-32,
-238,
-172,
-17,
-219,
-86,
-78,
-33,
-132,
-155,
-37,
-61,
-37,
-105,
-136,
-199,
-174,
-99,
-130,
-122,
-31,
-26,
-150,
-12,
-103,
-178,
-174,
-77,
-207,
-221,
-48,
-107,
-18,
-3,
-168,
-100,
-15,
-190,
-178,
-47,
-203,
-231,
-178,
-31,
-3,
-209,
-31,
-172,
-71,
-248,
-179,
-236,
-135,
-142,
-225,
-181,
-94,
-230,
-37,
-42,
-250,
-65,
-49,
-155,
-192,
-196,
-94,
-181,
-219,
-13,
-146,
-100,
-128,
-16,
-194,
-78,
-37,
-117,
-148,
-77,
-27,
-231,
-200,
-18,
-77,
-237,
-89,
-191,
-107,
-141,
-113,
-155,
-164,
-227,
-187,
-173,
-36,
-22,
-176,
-218,
-5,
-222,
-144,
-134,
-238,
-250,
-57,
-18,
-208,
-181,
-16,
-24,
-141,
-51,
-27,
-184,
-183,
-143,
-36,
-1,
-63,
-174,
-223,
-181,
-198,
-120,
-70,
-150,
-237,
-108,
-196,
-32,
-132,
-240,
-238,
-58,
-244,
-190,
-196,
-36,
-132,
-176,
-97,
-238,
-250,
-169,
-146,
-246,
-13,
-33,
-188,
-189,
-173,
-190,
-205,
-113,
-171,
-128,
-4,
-228,
-183,
-182,
-157,
-19,
-81,
-212,
-255,
-117,
-36,
-253,
-178,
-205,
-134,
-178,
-230,
-224,
-57,
-253,
-165,
-117,
-176,
-148,
-70,
-136,
-173,
-189,
-10,
-69,
-239,
-188,
-100,
-196,
-120,
-151,
-164,
-99,
-218,
-236,
-195,
-220,
-104,
-14,
-126,
-135,
-76,
-14,
-72,
-134,
-11,
-111,
-255,
-47,
-114,
-125,
-68,
-9,
-111,
-158,
-48,
-163,
-219,
-13,
-169,
-7,
-161,
-47,
-12,
-80,
-177,
-115,
-120,
-219,
-216,
-70,
-102,
-228,
-169,
-131,
-55,
-36,
-197,
-182,
-100,
-107,
-69,
-120,
-115,
-219,
-65,
-97,
-144,
-235,
-112,
-98,
-142,
-51,
-7,
-251,
-50,
-43,
-154,
-116,
-194,
-173,
-107,
-43,
-202,
-150,
-173,
-117,
-240,
-134,
-164,
-151,
-242,
-23,
-235,
-10,
-111,
-37,
-24,
-177,
-211,
-235,
-28,
-199,
-0,
-146,
-94,
-46,
-185,
-247,
-37,
-73,
-159,
-117,
-107,
-91,
-12,
-111,
-2,
-222,
-28,
-209,
-5,
-204,
-82,
-124,
-4,
-104,
-5,
-69,
-140,
-228,
-186,
-147,
-5,
-134,
-115,
-191,
-133,
-57,
-142,
-1,
-202,
-132,
-213,
-16,
-194,
-206,
-21,
-197,
-255,
-216,
-33,
-205,
-93,
-143,
-142,
-0,
-255,
-23,
-48,
-199,
-49,
-64,
-83,
-84,
-172,
-114,
-138,
-100,
-128,
-57,
-2,
-221,
-172,
-224,
-230,
-198,
-85,
-64,
-19,
-204,
-82,
-100,
-4,
-0,
-78,
-165,
-96,
-71,
-180,
-185,
-5,
-255,
-103,
-70,
-128,
-50,
-132,
-16,
-22,
-45,
-184,
-213,
-186,
-226,
-101,
-20,
-35,
-16,
-20,
-196,
-252,
-187,
-103,
-211,
-26,
-53,
-234,
-105,
-197,
-186,
-215,
-79,
-140,
-142,
-0,
-134,
-34,
-141,
-92,
-173,
-108,
-105,
-115,
-145,
-54,
-117,
-20,
-163,
-24,
-197,
-40,
-70,
-49,
-138,
-81,
-140,
-98,
-20,
-163,
-24,
-197,
-40,
-70,
-49,
-138,
-81,
-140,
-98,
-20,
-163,
-24,
-197,
-40,
-70,
-49,
-138,
-81,
-204,
-37,
-248,
-255,
-129,
-51,
-157,
-250,
-111,
-139,
-157,
-153,
-0,
-0,
-0,
-0,
-73,
-69,
-78,
-68,
-174,
-66,
-96,
-130,
-};
-/* clang-format on */
diff --git a/scene/resources/default_theme/icon_grid_layout.png b/scene/resources/default_theme/icon_grid_layout.png
new file mode 100644
index 0000000000..a249252a79
--- /dev/null
+++ b/scene/resources/default_theme/icon_grid_layout.png
Binary files differ
diff --git a/scene/resources/default_theme/icon_grid_minimap.png b/scene/resources/default_theme/icon_grid_minimap.png
new file mode 100644
index 0000000000..00a6179d5e
--- /dev/null
+++ b/scene/resources/default_theme/icon_grid_minimap.png
Binary files differ
diff --git a/scene/resources/default_theme/indeterminate.png b/scene/resources/default_theme/indeterminate.png
new file mode 100644
index 0000000000..28a457b251
--- /dev/null
+++ b/scene/resources/default_theme/indeterminate.png
Binary files differ
diff --git a/scene/resources/default_theme/option_button_disabled_mirrored.png b/scene/resources/default_theme/option_button_disabled_mirrored.png
new file mode 100644
index 0000000000..9d149a35ca
--- /dev/null
+++ b/scene/resources/default_theme/option_button_disabled_mirrored.png
Binary files differ
diff --git a/scene/resources/default_theme/option_button_hover_mirrored.png b/scene/resources/default_theme/option_button_hover_mirrored.png
new file mode 100644
index 0000000000..d49c165645
--- /dev/null
+++ b/scene/resources/default_theme/option_button_hover_mirrored.png
Binary files differ
diff --git a/scene/resources/default_theme/option_button_normal_mirrored.png b/scene/resources/default_theme/option_button_normal_mirrored.png
new file mode 100644
index 0000000000..feec848f33
--- /dev/null
+++ b/scene/resources/default_theme/option_button_normal_mirrored.png
Binary files differ
diff --git a/scene/resources/default_theme/option_button_pressed_mirrored.png b/scene/resources/default_theme/option_button_pressed_mirrored.png
new file mode 100644
index 0000000000..94cabb18d6
--- /dev/null
+++ b/scene/resources/default_theme/option_button_pressed_mirrored.png
Binary files differ
diff --git a/scene/resources/default_theme/picker_cursor.png b/scene/resources/default_theme/picker_cursor.png
new file mode 100644
index 0000000000..2f403492d2
--- /dev/null
+++ b/scene/resources/default_theme/picker_cursor.png
Binary files differ
diff --git a/scene/resources/default_theme/popup_window.png b/scene/resources/default_theme/popup_window.png
index 174a29ef45..442084049d 100644
--- a/scene/resources/default_theme/popup_window.png
+++ b/scene/resources/default_theme/popup_window.png
Binary files differ
diff --git a/scene/resources/default_theme/radio_checked_disabled.png b/scene/resources/default_theme/radio_checked_disabled.png
new file mode 100644
index 0000000000..72f08ecb96
--- /dev/null
+++ b/scene/resources/default_theme/radio_checked_disabled.png
Binary files differ
diff --git a/scene/resources/default_theme/radio_unchecked_disabled.png b/scene/resources/default_theme/radio_unchecked_disabled.png
new file mode 100644
index 0000000000..a8f4c1b555
--- /dev/null
+++ b/scene/resources/default_theme/radio_unchecked_disabled.png
Binary files differ
diff --git a/scene/resources/default_theme/submenu_mirrored.png b/scene/resources/default_theme/submenu_mirrored.png
new file mode 100644
index 0000000000..1142b9ba9f
--- /dev/null
+++ b/scene/resources/default_theme/submenu_mirrored.png
Binary files differ
diff --git a/scene/resources/default_theme/theme_data.h b/scene/resources/default_theme/theme_data.h
index a15efb593a..6a556c1112 100644
--- a/scene/resources/default_theme/theme_data.h
+++ b/scene/resources/default_theme/theme_data.h
@@ -6,10 +6,18 @@ static const unsigned char arrow_down_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 0x0, 0x0, 0x0, 0x34, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x32, 0x78, 0xf0, 0x1f, 0x15, 0x52, 0x20, 0xf1, 0x30, 0xee, 0xc1, 0x17, 0xb8, 0xf0, 0xb7, 0x87, 0x69, 0x48, 0xb6, 0xdc, 0xd7, 0xb8, 0x7f, 0x9, 0x2c, 0x7c, 0xfd, 0xb1, 0x2e, 0x9a, 0x3, 0x5e, 0x70, 0x3f, 0x9c, 0xff, 0x70, 0xfe, 0xb, 0x6e, 0x6, 0xea, 0x3, 0x0, 0xfb, 0x81, 0x48, 0xb8, 0x4d, 0xe4, 0x75, 0xd9, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char arrow_left_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x0, 0x59, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0x63, 0x60, 0xa0, 0x0, 0xdc, 0x4f, 0x78, 0xf0, 0xff, 0xc1, 0xff, 0x7, 0xff, 0x21, 0x3c, 0x46, 0x98, 0xf0, 0x43, 0xed, 0xff, 0x27, 0x19, 0xb8, 0x19, 0x18, 0x18, 0x18, 0x14, 0x18, 0x19, 0x18, 0x18, 0x18, 0x98, 0x20, 0xc2, 0x2f, 0xb8, 0xff, 0xaf, 0x82, 0x8, 0xc3, 0x0, 0x54, 0xe2, 0xe7, 0x14, 0x6, 0x2d, 0x54, 0x83, 0x99, 0x70, 0xd9, 0x8, 0x95, 0x60, 0xcf, 0x61, 0xb8, 0x86, 0x55, 0x42, 0xe2, 0x2b, 0x63, 0x18, 0xc3, 0x57, 0xac, 0x46, 0xc9, 0x5f, 0xfd, 0x9f, 0x43, 0x89, 0x67, 0x19, 0x18, 0x0, 0xf4, 0x30, 0x14, 0x49, 0xef, 0xe6, 0x74, 0x60, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char arrow_right_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 0x0, 0x0, 0x0, 0x2e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x17, 0x3c, 0xf8, 0xf, 0x82, 0xf7, 0x13, 0x70, 0x48, 0x3c, 0xf8, 0xf2, 0x50, 0x1b, 0x43, 0x2, 0xa, 0xaf, 0xbe, 0xe0, 0xc6, 0x2e, 0xf1, 0xff, 0xe1, 0x7c, 0x12, 0x24, 0x10, 0x46, 0x11, 0xb6, 0x1c, 0xe1, 0x5c, 0xa, 0x0, 0x0, 0xe0, 0x14, 0x48, 0xb1, 0x3d, 0x1b, 0x7a, 0xf0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char bar_arrow_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x14, 0x8, 0x4, 0x0, 0x0, 0x0, 0x2e, 0x6b, 0x75, 0xfc, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x1, 0x73, 0x52, 0x47, 0x42, 0x0, 0xae, 0xce, 0x1c, 0xe9, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc0, 0x0, 0x0, 0xe, 0xc0, 0x1, 0x6a, 0xd6, 0x89, 0x9, 0x0, 0x0, 0x0, 0x65, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x63, 0xfc, 0xcf, 0x80, 0x1f, 0x30, 0x8e, 0x20, 0x5, 0x8c, 0x38, 0x24, 0xff, 0x53, 0x5f, 0xc1, 0xb, 0xee, 0x9f, 0x53, 0x18, 0x18, 0xd8, 0x73, 0x24, 0xbe, 0x62, 0x55, 0x70, 0x5f, 0x83, 0x61, 0x15, 0xa3, 0x2e, 0x3, 0x3, 0xc3, 0xd, 0xe6, 0x30, 0xd9, 0xcb, 0x18, 0xa, 0x1e, 0xc6, 0xfd, 0x9f, 0xc6, 0xc0, 0xd, 0x35, 0xea, 0x3b, 0x63, 0x81, 0xfc, 0x2c, 0x14, 0x5, 0xf, 0x2a, 0x18, 0xda, 0xd1, 0x1c, 0x50, 0xa9, 0xd0, 0x1, 0x57, 0xf0, 0x10, 0x53, 0x9a, 0x81, 0x81, 0x81, 0xa1, 0x52, 0xbe, 0x83, 0x81, 0x81, 0xf1, 0x3f, 0x2e, 0x69, 0xa8, 0x12, 0x3a, 0x4, 0x14, 0x0, 0x7b, 0xda, 0x34, 0x1, 0xbb, 0xb5, 0x3e, 0x6c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char bookmark_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, 0x4, 0x73, 0x42, 0x49, 0x54, 0x8, 0x8, 0x8, 0x8, 0x7c, 0x8, 0x64, 0x88, 0x0, 0x0, 0x0, 0x57, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0xed, 0x93, 0x31, 0xa, 0xc0, 0x30, 0xc, 0x3, 0xa5, 0xd0, 0xff, 0x7f, 0x59, 0x1d, 0x8a, 0x42, 0x8, 0x9, 0x95, 0xc9, 0xd2, 0xa1, 0x9a, 0x8c, 0xf1, 0xdd, 0x62, 0x1b, 0x38, 0xc, 0x87, 0x5a, 0x5, 0xae, 0x79, 0xde, 0x2, 0x1, 0x80, 0x94, 0x39, 0x48, 0x76, 0x49, 0x17, 0xa4, 0xf0, 0x24, 0x61, 0x2b, 0x51, 0x8b, 0xfc, 0x82, 0xcf, 0xb, 0x48, 0x7a, 0xdf, 0x75, 0x81, 0xf, 0xe5, 0x29, 0xf7, 0x92, 0x6b, 0x3, 0x1a, 0x1e, 0xda, 0x7c, 0x3d, 0x77, 0x21, 0x7b, 0xa8, 0x74, 0x2e, 0xcb, 0xd, 0xc8, 0x75, 0x13, 0x28, 0x9, 0xed, 0xc2, 0xc8, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
@@ -38,8 +46,8 @@ static const unsigned char checked_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, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x8d, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x58, 0x56, 0x63, 0xb0, 0xaf, 0xb5, 0x38, 0x37, 0x40, 0x20, 0x20, 0x24, 0xb6, 0xb6, 0xb9, 0x57, 0x57, 0x5a, 0x20, 0x20, 0x24, 0x38, 0x36, 0x40, 0x20, 0x20, 0x25, 0x1e, 0x1e, 0x22, 0x1f, 0x1f, 0x23, 0x8b, 0x8b, 0x8d, 0xff, 0xff, 0xff, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x23, 0x23, 0x28, 0x42, 0x42, 0x47, 0xf8, 0xf8, 0xf8, 0xfe, 0xfe, 0xfe, 0x25, 0x25, 0x2a, 0x4e, 0x4e, 0x52, 0x26, 0x26, 0x2b, 0xc5, 0xc5, 0xc7, 0xaa, 0xaa, 0xab, 0xb8, 0xb8, 0xba, 0x5f, 0x5f, 0x63, 0x74, 0x74, 0x77, 0xed, 0xed, 0xed, 0x33, 0x33, 0x38, 0x8d, 0x8d, 0x8f, 0xb8, 0xb8, 0xb9, 0x35, 0x35, 0x39, 0x3a, 0x3a, 0x3e, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xb2, 0xb2, 0xb4, 0x45, 0x45, 0x49, 0x61, 0x61, 0x65, 0x8f, 0x8f, 0x92, 0x63, 0x63, 0x66, 0x2a, 0x2a, 0x2f, 0x40, 0x82, 0xb, 0xf6, 0x0, 0x0, 0x0, 0xf, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x50, 0x66, 0x68, 0x6a, 0x81, 0xb4, 0xfa, 0xdd, 0xfb, 0xfb, 0xb4, 0xfa, 0xb8, 0xf0, 0x7f, 0x59, 0x0, 0x0, 0x0, 0x7e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x5d, 0xca, 0x5, 0xb2, 0x2, 0x30, 0x18, 0x3, 0xe1, 0x4d, 0xed, 0xb9, 0x60, 0xf7, 0x3f, 0x20, 0xee, 0x4e, 0x99, 0xe0, 0xb0, 0x63, 0xfd, 0xbf, 0x86, 0xd7, 0x12, 0x72, 0x38, 0x69, 0x5b, 0x6b, 0x42, 0x45, 0xe5, 0xa, 0xab, 0x95, 0x41, 0x9f, 0x32, 0x20, 0x69, 0x2d, 0xbc, 0x50, 0x46, 0x3a, 0x10, 0x17, 0x5f, 0x49, 0x4, 0x7f, 0x90, 0x57, 0x89, 0xb7, 0xc5, 0x5f, 0x96, 0x17, 0x2e, 0x93, 0xcb, 0x8e, 0x3a, 0x83, 0xb, 0xc4, 0x8e, 0xd4, 0xff, 0x5c, 0x73, 0x83, 0x69, 0x9e, 0x95, 0xfc, 0x3b, 0xf4, 0x33, 0xe0, 0xf8, 0x61, 0xd3, 0xf1, 0x7d, 0x5d, 0x30, 0x7a, 0x6f, 0x89, 0xb, 0xd4, 0x5a, 0xe1, 0x40, 0xf, 0xfc, 0x34, 0x6c, 0xd2, 0x56, 0x80, 0xef, 0xfd, 0x9, 0xd2, 0x3a, 0x5e, 0x41, 0x15, 0x21, 0x77, 0x6, 0xc7, 0x6b, 0x47, 0x4e, 0x3a, 0x2f, 0x53, 0xb4, 0x10, 0xc7, 0x8c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
-static const unsigned char checker_bg_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x0, 0x0, 0x0, 0x0, 0xe1, 0x64, 0xe1, 0x57, 0x0, 0x0, 0x0, 0x14, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xfc, 0xcf, 0xc0, 0xc0, 0xd0, 0x0, 0xc4, 0xf8, 0x18, 0xf5, 0x84, 0x19, 0x0, 0x9f, 0x5f, 0xa, 0x1, 0xf8, 0xef, 0x65, 0xf4, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+static const unsigned char checked_disabled_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, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x99, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x73, 0x72, 0x7b, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x82, 0x80, 0x8a, 0x90, 0x90, 0x93, 0x6a, 0x69, 0x70, 0x6a, 0x68, 0x70, 0x93, 0x93, 0x95, 0x58, 0x58, 0x5c, 0x58, 0x58, 0x5b, 0x7d, 0x7d, 0x7f, 0x58, 0x58, 0x5b, 0xa4, 0xa4, 0xa4, 0x9e, 0x9e, 0xa0, 0x9e, 0x9e, 0x9e, 0x9b, 0x9b, 0x9c, 0x9b, 0x9b, 0x9b, 0x9a, 0x9a, 0x9a, 0x99, 0x99, 0x99, 0x98, 0x98, 0x98, 0x97, 0x97, 0x97, 0x96, 0x96, 0x96, 0x95, 0x95, 0x95, 0x93, 0x93, 0x94, 0x8f, 0x8f, 0x8f, 0x86, 0x86, 0x88, 0x85, 0x85, 0x86, 0x82, 0x82, 0x83, 0x81, 0x81, 0x83, 0x7f, 0x7f, 0x81, 0x7c, 0x7c, 0x7e, 0x7a, 0x7a, 0x7d, 0x78, 0x78, 0x7b, 0x71, 0x71, 0x74, 0x68, 0x68, 0x6c, 0x66, 0x66, 0x6a, 0x65, 0x65, 0x68, 0x63, 0x63, 0x66, 0x5f, 0x5f, 0x63, 0x5c, 0x5c, 0x60, 0x5c, 0x5c, 0x5f, 0x5a, 0x5a, 0x5e, 0x59, 0x59, 0x5d, 0x59, 0x59, 0x5c, 0x58, 0x58, 0x5b, 0x57, 0x57, 0x5a, 0x56, 0x56, 0x59, 0x10, 0x13, 0xbb, 0xf, 0x0, 0x0, 0x0, 0x10, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x27, 0x50, 0x66, 0x68, 0x6a, 0x81, 0xb4, 0xb4, 0xdd, 0xfa, 0xfa, 0xfb, 0xfb, 0x5b, 0xd1, 0xf1, 0xe6, 0x0, 0x0, 0x0, 0x96, 0x49, 0x44, 0x41, 0x54, 0x78, 0x5e, 0x5d, 0x8f, 0xc9, 0x12, 0x82, 0x30, 0x14, 0x4, 0x87, 0x18, 0x50, 0x51, 0x44, 0x25, 0x42, 0x4, 0x77, 0xc4, 0x8d, 0x97, 0x0, 0xf9, 0xff, 0x8f, 0xb3, 0x88, 0xa4, 0x4a, 0xed, 0x63, 0x5f, 0xa6, 0x7, 0xf8, 0x7, 0x1e, 0xe3, 0x7e, 0x60, 0x19, 0x4f, 0x46, 0x1e, 0x0, 0x36, 0x8d, 0x4c, 0x67, 0x59, 0x65, 0x33, 0x6, 0x80, 0x47, 0xad, 0x56, 0x3d, 0xb7, 0x3c, 0x5d, 0x70, 0x0, 0xbe, 0xd1, 0x44, 0x65, 0x4d, 0x94, 0xc8, 0xc2, 0xf8, 0x0, 0x82, 0x4e, 0x91, 0x94, 0x15, 0x5d, 0xd2, 0xec, 0xde, 0x5, 0x83, 0x38, 0xc8, 0xe3, 0x63, 0x23, 0xce, 0xca, 0x9, 0x7a, 0x6e, 0xf3, 0x93, 0x48, 0x1a, 0x27, 0x14, 0x35, 0x3b, 0xb9, 0x5e, 0x56, 0xe4, 0x84, 0x22, 0xba, 0xa, 0x51, 0xd0, 0xb7, 0xa8, 0xcb, 0xfd, 0xcb, 0x9, 0x3b, 0xfb, 0x41, 0xdb, 0x59, 0x17, 0xa6, 0x94, 0x6e, 0xe3, 0x3e, 0x8c, 0x85, 0xf1, 0x90, 0x6e, 0xe6, 0x21, 0xfb, 0x39, 0xe7, 0x73, 0xe6, 0xfd, 0x5f, 0x7, 0xde, 0xc3, 0xb5, 0x16, 0x87, 0xb0, 0x9e, 0x42, 0x46, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
static const unsigned char close_png[] = {
@@ -58,10 +66,18 @@ static const unsigned char color_picker_sample_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x14, 0x8, 0x0, 0x0, 0x0, 0x0, 0x47, 0x29, 0xbc, 0x83, 0x0, 0x0, 0x0, 0x3c, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xed, 0xd5, 0x21, 0x11, 0x0, 0x30, 0xc, 0x4, 0xc1, 0xfa, 0x57, 0x53, 0x87, 0xed, 0x4, 0x45, 0xc4, 0xed, 0xa3, 0xc3, 0x4b, 0xfe, 0xbc, 0xd9, 0x9d, 0x35, 0x2b, 0xe, 0x0, 0x0, 0x0, 0x80, 0xed, 0x66, 0xc5, 0x1, 0x0, 0x0, 0x0, 0xe0, 0x6, 0x1, 0x0, 0x0, 0x90, 0x6, 0x70, 0x83, 0x0, 0x0, 0x0, 0x28, 0x3, 0x7c, 0x54, 0x93, 0xd6, 0xf1, 0xd1, 0x16, 0x8a, 0x17, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char dialog_bg_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0x26, 0x8, 0x3, 0x0, 0x0, 0x0, 0xf7, 0x10, 0x9b, 0xa4, 0x0, 0x0, 0x2, 0xf5, 0x7a, 0x54, 0x58, 0x74, 0x52, 0x61, 0x77, 0x20, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x65, 0x78, 0x69, 0x66, 0x0, 0x0, 0x78, 0xda, 0xb5, 0x95, 0x6d, 0x72, 0xe4, 0x28, 0xc, 0x86, 0xff, 0x73, 0x8a, 0x3d, 0x2, 0xfa, 0x42, 0x70, 0x1c, 0xc, 0xa6, 0x6a, 0x6f, 0xb0, 0xc7, 0x9f, 0x17, 0xec, 0x38, 0xd9, 0x4c, 0x57, 0xda, 0x49, 0xcf, 0x40, 0xb5, 0xa5, 0x92, 0x5, 0x12, 0x3c, 0x92, 0x3b, 0xec, 0xff, 0xfd, 0x3b, 0xc2, 0x3f, 0x18, 0xa4, 0x39, 0x6, 0x35, 0xcf, 0xa9, 0xa4, 0x14, 0x31, 0xb4, 0x68, 0xe1, 0xa, 0x25, 0xc7, 0xf7, 0xb1, 0x9f, 0xf2, 0xb0, 0x51, 0xd4, 0xf5, 0x5c, 0xa3, 0xe8, 0xf9, 0x8e, 0x22, 0x85, 0x8f, 0x2f, 0x2e, 0x8d, 0x21, 0x5, 0x52, 0xe, 0xa3, 0xbf, 0x2d, 0x90, 0xd3, 0xfe, 0xe6, 0x9f, 0x2e, 0x89, 0x8d, 0x1e, 0xbc, 0x20, 0xfb, 0xb4, 0x40, 0xae, 0x30, 0xfc, 0x31, 0xb0, 0xd7, 0xd3, 0xce, 0x91, 0xff, 0x97, 0x91, 0x7a, 0xb4, 0xf8, 0x71, 0xe4, 0xf7, 0xdf, 0x18, 0x3d, 0x8f, 0xb1, 0x1f, 0xa7, 0xab, 0x9a, 0x70, 0xd, 0xe9, 0x38, 0xd4, 0x11, 0x22, 0xbc, 0x6d, 0x3, 0xc7, 0xd, 0x5b, 0xc9, 0x5a, 0x96, 0x30, 0x1d, 0x3f, 0x83, 0xee, 0x6b, 0x16, 0xcc, 0x1c, 0x6b, 0x6c, 0xa4, 0xb1, 0xc7, 0x16, 0x37, 0xcc, 0x46, 0x85, 0x98, 0x24, 0xe, 0x52, 0xea, 0x81, 0x2a, 0xd, 0xda, 0xa9, 0x43, 0x36, 0x6a, 0xc8, 0x51, 0x79, 0x67, 0x87, 0x64, 0x6e, 0x2c, 0xcb, 0x96, 0xc5, 0xb9, 0x70, 0x93, 0x28, 0x24, 0x3a, 0x27, 0xd, 0x76, 0x29, 0xd2, 0x25, 0xb, 0x4b, 0xe3, 0x5d, 0x44, 0x34, 0x8, 0x5f, 0xb9, 0xd0, 0x8a, 0x5b, 0x56, 0xbc, 0x46, 0x19, 0x91, 0x3b, 0xc1, 0x95, 0x9, 0x9b, 0x11, 0x96, 0x7c, 0x39, 0xc3, 0x33, 0x87, 0x3b, 0x73, 0x8c, 0x16, 0x71, 0x47, 0x44, 0x38, 0x3d, 0x9d, 0x75, 0x81, 0xbc, 0x98, 0x27, 0x7, 0x9a, 0xd7, 0x28, 0xf3, 0x9, 0x37, 0x0, 0xa1, 0x71, 0x72, 0xb3, 0x75, 0xc1, 0x6f, 0xf3, 0x1a, 0xe1, 0x3, 0x58, 0x1, 0x41, 0x5b, 0xd7, 0x9c, 0x71, 0xc0, 0x1a, 0xb7, 0x63, 0x8b, 0xcd, 0xe8, 0xbd, 0xb6, 0x64, 0x15, 0x80, 0xc0, 0xcf, 0x20, 0x8f, 0xfa, 0x22, 0xef, 0x93, 0x1a, 0xaf, 0x2a, 0x51, 0xc4, 0x36, 0x24, 0x43, 0x2, 0x4, 0x31, 0x91, 0x18, 0x25, 0x8a, 0xce, 0xec, 0x44, 0x2a, 0x9c, 0x1, 0xa8, 0x22, 0x73, 0x16, 0xe5, 0xd, 0x4, 0xc8, 0x8c, 0x3b, 0x92, 0x64, 0x15, 0x49, 0x60, 0x93, 0x51, 0x47, 0x88, 0x8d, 0x35, 0x4e, 0xcb, 0x97, 0x8d, 0xf, 0x3b, 0x5a, 0x5, 0x7c, 0x4c, 0x92, 0x38, 0xd8, 0x14, 0xa9, 0x80, 0xa5, 0x6a, 0xa8, 0x1f, 0xd7, 0x8c, 0x1a, 0xaa, 0x26, 0xa6, 0x66, 0x96, 0xcc, 0x2d, 0x5b, 0xb1, 0x1a, 0x92, 0x24, 0x4d, 0x96, 0x52, 0xf2, 0x34, 0x7b, 0xae, 0xba, 0xb8, 0xba, 0x79, 0x72, 0xf7, 0xec, 0xc5, 0x6b, 0x96, 0xac, 0xd9, 0x72, 0xca, 0x9e, 0x73, 0x2e, 0xb9, 0x16, 0x2e, 0x82, 0x96, 0xb4, 0x92, 0x8a, 0x97, 0x5c, 0x4a, 0xa9, 0x15, 0x31, 0xab, 0x86, 0x6a, 0x15, 0xab, 0x2b, 0x3c, 0x6a, 0xdd, 0x78, 0x93, 0x4d, 0x37, 0xdb, 0xd2, 0xe6, 0x5b, 0xde, 0xca, 0x56, 0x1b, 0xca, 0xa7, 0x69, 0xb3, 0x96, 0x9a, 0xb7, 0xdc, 0x4a, 0xab, 0x9d, 0xbb, 0x74, 0xed, 0xd6, 0x53, 0xf7, 0x9e, 0x7b, 0xe9, 0x75, 0xa7, 0x1d, 0xa5, 0x14, 0x76, 0xdd, 0x6d, 0x4f, 0xbb, 0xef, 0x79, 0x2f, 0x7b, 0x1d, 0xa8, 0xb5, 0x21, 0x43, 0x87, 0x8d, 0x34, 0x7c, 0xe4, 0x51, 0x46, 0xbd, 0xa8, 0xd1, 0xd9, 0xb6, 0x9f, 0xe7, 0x37, 0xa8, 0xd1, 0x49, 0x8d, 0x17, 0xa9, 0xe9, 0xe7, 0x17, 0x35, 0x58, 0xdd, 0xe7, 0x46, 0x6b, 0xb, 0x9a, 0xdf, 0x19, 0x9b, 0xcc, 0x40, 0x8c, 0x95, 0x40, 0xdc, 0x27, 0x1, 0x14, 0x34, 0x4f, 0x66, 0x31, 0x93, 0x2a, 0x4f, 0x72, 0x93, 0x59, 0x2c, 0x8c, 0xae, 0x30, 0x46, 0x92, 0x36, 0xd9, 0x74, 0x8a, 0x35, 0x50, 0x2, 0x42, 0xdd, 0x89, 0x6d, 0xd0, 0xc5, 0xee, 0x9d, 0xdc, 0x6d, 0x6e, 0x1, 0x77, 0xfd, 0x8c, 0x1b, 0xdf, 0x21, 0x17, 0x26, 0xba, 0x3f, 0x40, 0x8e, 0xc3, 0x2e, 0x9f, 0xb8, 0x3d, 0xa0, 0xd6, 0xe7, 0x97, 0xb0, 0x2d, 0x62, 0x47, 0x17, 0xce, 0x3b, 0x8d, 0x82, 0xee, 0x83, 0x6f, 0xe5, 0x5c, 0x79, 0x53, 0x50, 0x5b, 0x5a, 0x7e, 0x55, 0xfe, 0x85, 0x8d, 0x68, 0x9, 0x1c, 0xe2, 0x67, 0x32, 0x3c, 0x7c, 0xa1, 0x6b, 0xe7, 0xf4, 0x1d, 0x25, 0xdc, 0x70, 0x42, 0xce, 0x65, 0x85, 0xb0, 0x2f, 0x94, 0x70, 0xc7, 0xe9, 0x37, 0xe5, 0x41, 0xd2, 0xe1, 0x47, 0xe7, 0x78, 0x90, 0x74, 0x40, 0x91, 0x8a, 0xa6, 0x74, 0x1a, 0x5b, 0xa7, 0x72, 0x86, 0xfe, 0xa6, 0x39, 0xdc, 0x72, 0x47, 0x97, 0x1b, 0xa1, 0xbf, 0xf0, 0x3f, 0x8c, 0xe, 0x7d, 0xa8, 0x84, 0x2f, 0xde, 0x7d, 0xad, 0x90, 0x1a, 0xdb, 0xc, 0xb1, 0x2d, 0x25, 0x5c, 0xda, 0x8b, 0x4a, 0x38, 0xb3, 0x2e, 0x15, 0xa7, 0x39, 0x8c, 0x38, 0x53, 0xeb, 0x67, 0xe8, 0xfb, 0xe6, 0x70, 0xdb, 0xfd, 0x49, 0xa9, 0x86, 0x17, 0xa9, 0x5f, 0x5c, 0xc2, 0x8b, 0xd4, 0x2f, 0x73, 0x78, 0x4e, 0xdd, 0x6e, 0xc9, 0x70, 0xd7, 0xf1, 0x59, 0x3d, 0x84, 0x17, 0xca, 0x88, 0x3e, 0xca, 0xf0, 0xd9, 0xf0, 0x53, 0xf9, 0xe7, 0x36, 0xf2, 0x81, 0x8f, 0x7b, 0x81, 0xf2, 0xb, 0x12, 0xea, 0xac, 0x15, 0x79, 0x70, 0x44, 0x63, 0x0, 0x0, 0x1, 0x68, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xe8, 0xe5, 0xf1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x1d, 0x22, 0x0, 0x0, 0x0, 0x1a, 0x19, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x1e, 0x23, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x1d, 0x21, 0x17, 0x16, 0x19, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x21, 0x1f, 0x24, 0x1b, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x21, 0x1f, 0x24, 0x1e, 0x1c, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x22, 0x20, 0x25, 0x20, 0x1e, 0x23, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x21, 0x1f, 0x24, 0x0, 0x0, 0x0, 0x21, 0x1f, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x22, 0x20, 0x25, 0x0, 0x0, 0x0, 0x20, 0x20, 0x25, 0x20, 0x1d, 0x25, 0x20, 0x1d, 0x22, 0x1d, 0x1d, 0x22, 0x1d, 0x1d, 0x20, 0x1d, 0x1a, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x30, 0x38, 0xe8, 0xe5, 0xf1, 0xe5, 0xe2, 0xeb, 0xe3, 0xe1, 0xe8, 0xe1, 0xdf, 0xe7, 0xe0, 0xde, 0xe6, 0xdf, 0xdd, 0xe5, 0xde, 0xdc, 0xe4, 0xdd, 0xdb, 0xe3, 0xdc, 0xda, 0xe2, 0xda, 0xd8, 0xe0, 0xd9, 0xd7, 0xdf, 0xd7, 0xd6, 0xdf, 0xd6, 0xd4, 0xdd, 0xd5, 0xd3, 0xdc, 0xd4, 0xd1, 0xdb, 0xd3, 0xd0, 0xda, 0xd1, 0xce, 0xd8, 0xd0, 0xcd, 0xd7, 0xcf, 0xcd, 0xd7, 0xe2, 0xdf, 0xeb, 0x48, 0x46, 0x51, 0x42, 0x40, 0x4b, 0x40, 0x3e, 0x48, 0x40, 0x3d, 0x48, 0x48, 0x45, 0x50, 0x42, 0x3f, 0x4a, 0x3f, 0x3d, 0x48, 0x47, 0x44, 0x50, 0x41, 0x3f, 0x4a, 0x3f, 0x3d, 0x47, 0x41, 0x3e, 0x49, 0x3f, 0x3c, 0x47, 0x46, 0x43, 0x4f, 0x3e, 0x3c, 0x46, 0x40, 0x3e, 0x49, 0x3d, 0x3b, 0x46, 0x45, 0x43, 0x4e, 0x3d, 0x3b, 0x45, 0x44, 0x42, 0x4d, 0x3d, 0x3a, 0x45, 0x3e, 0x3c, 0x47, 0x3c, 0x3a, 0x44, 0x43, 0x42, 0x4c, 0x43, 0x40, 0x4c, 0x3e, 0x3b, 0x46, 0x3b, 0x39, 0x43, 0x43, 0x3f, 0x4c, 0x43, 0x3f, 0x4b, 0x3a, 0x38, 0x42, 0x42, 0x3e, 0x4b, 0x42, 0x3e, 0x49, 0x3a, 0x37, 0x41, 0x39, 0x37, 0x41, 0x3f, 0x3e, 0x48, 0x39, 0x37, 0x40, 0x38, 0x36, 0x40, 0x3e, 0x3d, 0x48, 0x38, 0x36, 0x3f, 0x3e, 0x3d, 0x47, 0x3a, 0x38, 0x41, 0x38, 0x35, 0x3f, 0x37, 0x35, 0x3e, 0x39, 0x36, 0x40, 0x37, 0x34, 0x3e, 0x3d, 0x3a, 0x46, 0x36, 0x34, 0x3d, 0x3d, 0x3a, 0x44, 0x37, 0x35, 0x3f, 0x35, 0x33, 0x3c, 0x6b, 0xff, 0x8f, 0xb1, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x0, 0x88, 0x5, 0x1d, 0x48, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe5, 0x7, 0x1b, 0x17, 0x11, 0x18, 0xe6, 0xb9, 0x22, 0xac, 0x0, 0x0, 0x0, 0x3f, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x63, 0xf4, 0x64, 0xc0, 0x6, 0x98, 0x18, 0x68, 0x27, 0xcc, 0xc2, 0x88, 0x5d, 0x98, 0x99, 0x24, 0x61, 0x26, 0x52, 0x84, 0x71, 0xb9, 0x84, 0x89, 0x76, 0xc2, 0xb8, 0xac, 0x64, 0x21, 0x49, 0x35, 0x33, 0x15, 0x54, 0xe3, 0xa, 0x6f, 0x26, 0x6a, 0xa8, 0x26, 0xc9, 0x4a, 0xaa, 0x44, 0x3, 0x89, 0x89, 0x8d, 0x99, 0xee, 0xe9, 0x1b, 0x87, 0x30, 0x0, 0x20, 0x4, 0x0, 0xed, 0x48, 0xa7, 0x26, 0x6c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char dropdown_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x4, 0x0, 0x0, 0x0, 0x6e, 0x6, 0x76, 0x0, 0x0, 0x0, 0x0, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0x60, 0xf8, 0xc0, 0xcc, 0x0, 0x2, 0x60, 0x16, 0x98, 0x78, 0x67, 0x8, 0x81, 0x6f, 0x4d, 0xde, 0x9a, 0x0, 0x5, 0xde, 0x3a, 0x3d, 0xfc, 0x8f, 0x80, 0xaf, 0xba, 0x18, 0xde, 0x29, 0x2, 0x19, 0xbf, 0x61, 0x2, 0x6f, 0x62, 0x18, 0x3e, 0xb0, 0xbd, 0x97, 0x4, 0x32, 0xff, 0x80, 0xb9, 0xb1, 0x20, 0x93, 0xc0, 0x42, 0x8, 0x2e, 0x54, 0xe8, 0x9d, 0xdc, 0x9b, 0x54, 0x10, 0xb, 0x21, 0xc4, 0x4, 0x63, 0x1, 0x0, 0x86, 0x1f, 0x3b, 0x1e, 0x92, 0x22, 0x3f, 0x40, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char ellipsis_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x0, 0x8, 0x8, 0x6, 0x0, 0x0, 0x0, 0xc9, 0x11, 0xce, 0xcc, 0x0, 0x0, 0x0, 0x4, 0x73, 0x42, 0x49, 0x54, 0x8, 0x8, 0x8, 0x8, 0x7c, 0x8, 0x64, 0x88, 0x0, 0x0, 0x0, 0x78, 0x49, 0x44, 0x41, 0x54, 0x18, 0x95, 0x95, 0xd1, 0x31, 0xa, 0xc2, 0x50, 0x10, 0x4, 0xd0, 0xb7, 0x1f, 0xf, 0x60, 0x67, 0xa, 0xf, 0x12, 0x6f, 0x60, 0xe9, 0x51, 0x5, 0x3d, 0x44, 0xee, 0x61, 0xa1, 0xa5, 0xf6, 0x81, 0xb5, 0xc8, 0x47, 0x82, 0x84, 0x4f, 0xb2, 0xdd, 0xb0, 0x33, 0xb3, 0xb3, 0x4c, 0x40, 0x66, 0xee, 0x71, 0xc2, 0x1, 0x3b, 0xcb, 0x33, 0xe2, 0x85, 0x21, 0x22, 0xde, 0x51, 0x45, 0x97, 0x86, 0x60, 0xc9, 0xe0, 0x5a, 0xea, 0xa5, 0xb5, 0x22, 0x95, 0xdb, 0x97, 0x1a, 0xf, 0x6e, 0xb8, 0xcf, 0x8, 0x2d, 0xdc, 0x95, 0xd9, 0x22, 0xfe, 0x9c, 0x9b, 0x38, 0x32, 0xf3, 0x8c, 0xe3, 0x86, 0xa8, 0xf0, 0x28, 0x18, 0x4c, 0xf, 0xaf, 0x9d, 0x11, 0x43, 0xf0, 0xab, 0xa3, 0x47, 0xa7, 0x5d, 0xc7, 0xd3, 0x54, 0xc7, 0xe7, 0xb, 0xb9, 0xce, 0x1f, 0xc6, 0x2d, 0x99, 0x55, 0xc7, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char error_icon_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, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x2, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x87, 0x8f, 0xcc, 0xbf, 0x0, 0x0, 0x0, 0xe, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x63, 0x60, 0x18, 0x5, 0xa3, 0x0, 0x1, 0x0, 0x2, 0x10, 0x0, 0x1, 0x14, 0xc2, 0xc0, 0x92, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
@@ -162,6 +178,14 @@ 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_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_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_parent_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, 0x68, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x33, 0xb8, 0x27, 0xfe, 0xe0, 0xfc, 0x83, 0x73, 0xf7, 0xc4, 0x71, 0x48, 0xdf, 0x11, 0x7b, 0x78, 0xe9, 0xc1, 0x3f, 0x20, 0xbc, 0xfe, 0x40, 0x12, 0x8f, 0x34, 0x4c, 0x9, 0xa6, 0xe1, 0x57, 0x80, 0x12, 0x17, 0x81, 0xf8, 0x2f, 0x58, 0xe1, 0x15, 0x34, 0x8b, 0x1e, 0x9c, 0x5, 0xa, 0x5e, 0xb8, 0x23, 0x6, 0x52, 0x70, 0x5b, 0x14, 0xac, 0xf0, 0xc, 0xaa, 0x82, 0x7d, 0xf, 0x8e, 0xde, 0x14, 0xf9, 0xcf, 0x8, 0x52, 0xc0, 0xc0, 0x70, 0x5b, 0xf4, 0xe1, 0xc9, 0x7, 0x47, 0xb1, 0xb8, 0x3, 0xaa, 0x0, 0xa, 0x48, 0x52, 0x80, 0xb0, 0xea, 0xc8, 0xc3, 0x83, 0xc, 0x83, 0xe, 0x0, 0x0, 0xb8, 0x27, 0x55, 0x4c, 0xbe, 0xc0, 0xd2, 0xac, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
@@ -190,6 +214,14 @@ static const unsigned char icon_zoom_reset_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, 0x33, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0xa, 0x3c, 0xc, 0x7b, 0xf0, 0xff, 0xc1, 0x7f, 0x9c, 0x22, 0xcf, 0x44, 0x1e, 0xbc, 0x84, 0x72, 0xb1, 0x8b, 0x3c, 0x58, 0x5, 0xe4, 0x40, 0xb8, 0x38, 0x45, 0x18, 0x60, 0x5c, 0x84, 0x30, 0x59, 0xa, 0xa0, 0x80, 0x6e, 0xa, 0x86, 0x92, 0x2f, 0x8, 0x3, 0x0, 0x69, 0xc8, 0x86, 0x87, 0x72, 0xca, 0x85, 0x23, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char indeterminate_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, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x36, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x38, 0x37, 0x40, 0x20, 0x20, 0x24, 0x20, 0x20, 0x24, 0x38, 0x36, 0x40, 0x20, 0x20, 0x25, 0x1e, 0x1e, 0x22, 0x1f, 0x1f, 0x23, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2a, 0xfe, 0xfe, 0xfe, 0x98, 0x4d, 0x2d, 0x9a, 0x0, 0x0, 0x0, 0x12, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x50, 0x66, 0x68, 0xb4, 0xfa, 0xfb, 0xb4, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1c, 0x77, 0x5e, 0x7f, 0x0, 0x0, 0x0, 0x59, 0x49, 0x44, 0x41, 0x54, 0x18, 0x95, 0x85, 0xcf, 0xc1, 0x12, 0x80, 0x20, 0x8, 0x45, 0xd1, 0x4, 0x14, 0xd, 0xc5, 0xfa, 0xff, 0x9f, 0x8d, 0x9c, 0x6c, 0x9a, 0xb4, 0xe9, 0x2e, 0xcf, 0x42, 0x9e, 0xcb, 0x32, 0xe4, 0x0, 0xc9, 0xb7, 0x8, 0xc1, 0x19, 0x40, 0x60, 0xc9, 0x2d, 0xe1, 0x0, 0x6, 0xc8, 0x45, 0x6b, 0x4b, 0xb, 0xa3, 0x1, 0x89, 0x6e, 0x57, 0x2a, 0x64, 0xe0, 0x73, 0xed, 0x50, 0xb3, 0x9f, 0xc3, 0x7e, 0xf7, 0x5, 0x7f, 0x6f, 0xc, 0x67, 0x9f, 0xc3, 0xe2, 0x39, 0xc, 0x52, 0xec, 0xd3, 0xd7, 0x4, 0xb3, 0xcf, 0xbd, 0x3a, 0x0, 0xa0, 0xa2, 0x8, 0xbc, 0xf6, 0x84, 0x3a, 0x9d, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
+static const unsigned char indeterminate_disabled_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, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x3c, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x6a, 0x69, 0x70, 0x6a, 0x68, 0x70, 0x58, 0x58, 0x5c, 0x58, 0x58, 0x5b, 0x58, 0x58, 0x5b, 0x5c, 0x5c, 0x5f, 0x5a, 0x5a, 0x5e, 0x59, 0x59, 0x5d, 0x58, 0x58, 0x5b, 0x57, 0x57, 0x5a, 0x56, 0x56, 0x59, 0xff, 0x0, 0x0, 0xff, 0xff, 0xff, 0x9e, 0x9e, 0x9e, 0x8c, 0x93, 0x80, 0x95, 0x0, 0x0, 0x0, 0x14, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x50, 0x66, 0x68, 0xb4, 0xb4, 0xfa, 0xfa, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6c, 0xb9, 0x8d, 0x1e, 0x0, 0x0, 0x0, 0x59, 0x49, 0x44, 0x41, 0x54, 0x18, 0x95, 0x85, 0xcf, 0xd1, 0xe, 0x80, 0x20, 0x8, 0x85, 0xe1, 0x4, 0xd4, 0x30, 0x51, 0xb6, 0xde, 0xff, 0x5d, 0x23, 0x97, 0xad, 0xa5, 0xad, 0xff, 0xf2, 0xbb, 0x90, 0xe3, 0xb2, 0xc, 0x39, 0x40, 0xf2, 0x2d, 0x42, 0x70, 0x6, 0x10, 0x58, 0x6b, 0x4b, 0x39, 0x80, 0x1, 0x72, 0x91, 0xdc, 0x92, 0xc2, 0x68, 0x40, 0x2a, 0xdb, 0x95, 0x28, 0x19, 0xf8, 0x9a, 0x3b, 0xe4, 0xea, 0xe7, 0xb0, 0xdf, 0x7d, 0xc1, 0xdf, 0x1b, 0xc3, 0xd9, 0xe7, 0xb0, 0x74, 0xe, 0x83, 0x98, 0xfa, 0xf4, 0x35, 0xc2, 0xec, 0x73, 0xaf, 0xe, 0x57, 0x20, 0x8, 0x2c, 0x1a, 0x56, 0xe5, 0x32, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char line_edit_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0xa, 0x4, 0x3, 0x0, 0x0, 0x0, 0x7f, 0x1c, 0xd2, 0x8e, 0x0, 0x0, 0x0, 0x2a, 0x50, 0x4c, 0x54, 0x45, 0x17, 0x16, 0x1a, 0x1d, 0x1c, 0x21, 0x20, 0x1e, 0x24, 0x21, 0x1f, 0x25, 0x1d, 0x1c, 0x21, 0x20, 0x1e, 0x24, 0x1d, 0x1c, 0x21, 0x1d, 0x1c, 0x21, 0x24, 0x22, 0x29, 0x28, 0x26, 0x2d, 0x28, 0x26, 0x2e, 0x2b, 0x2a, 0x31, 0x2c, 0x2a, 0x32, 0xff, 0xff, 0xff, 0xb9, 0x11, 0x56, 0x3e, 0x0, 0x0, 0x0, 0x8, 0x74, 0x52, 0x4e, 0x53, 0x6f, 0xef, 0xf7, 0xf7, 0xf0, 0xf9, 0xf1, 0xee, 0xcf, 0x21, 0xd2, 0xdf, 0x0, 0x0, 0x0, 0x2d, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x54, 0x36, 0x36, 0x12, 0x60, 0xf0, 0x98, 0xb5, 0x6a, 0x65, 0xb, 0x43, 0xe4, 0x9e, 0x33, 0xa7, 0xa7, 0x32, 0x58, 0x9d, 0x39, 0x73, 0x66, 0x31, 0x16, 0x12, 0x22, 0xb, 0x52, 0xd9, 0xc6, 0xc0, 0x2, 0xd4, 0x55, 0x0, 0x0, 0xc, 0x14, 0x1a, 0x90, 0x55, 0x1a, 0xec, 0xdb, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
@@ -214,18 +246,34 @@ static const unsigned char option_button_disabled_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x2f, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x3f, 0x3f, 0x5a, 0x5a, 0x5a, 0x2b, 0x2b, 0x31, 0x2e, 0x2e, 0x34, 0x59, 0x59, 0x59, 0x2a, 0x2a, 0x30, 0x4b, 0x4b, 0x4b, 0x22, 0x22, 0x27, 0x35, 0x35, 0x35, 0x4a, 0x4a, 0x4a, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x56, 0x56, 0x56, 0x62, 0x62, 0x62, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x2d, 0x2d, 0x34, 0x2f, 0x2f, 0x36, 0x2e, 0x2e, 0x35, 0x2c, 0x2c, 0x32, 0x48, 0x48, 0x48, 0x44, 0x44, 0x44, 0x43, 0x43, 0x43, 0x54, 0x54, 0x54, 0x26, 0x26, 0x2b, 0x24, 0x24, 0x28, 0x27, 0x27, 0x2d, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x25, 0x25, 0x2b, 0x23, 0x23, 0x28, 0x26, 0x26, 0x2c, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x22, 0x22, 0x26, 0x52, 0x52, 0x52, 0x42, 0x42, 0x42, 0x2d, 0x2d, 0x33, 0x22, 0x22, 0x27, 0x51, 0x51, 0x51, 0x40, 0x40, 0x40, 0x27, 0x27, 0x2b, 0x2e, 0x2e, 0x34, 0x2c, 0x2c, 0x31, 0x29, 0x29, 0x2e, 0x4f, 0x4f, 0x4f, 0x3f, 0x3f, 0x3f, 0x4d, 0x4d, 0x4d, 0x3e, 0x3e, 0x3e, 0x24, 0x24, 0x2a, 0x24, 0x24, 0x29, 0x20, 0x20, 0x25, 0x4c, 0x4c, 0x4c, 0x3d, 0x3d, 0x3d, 0x28, 0x28, 0x2d, 0x2b, 0x2b, 0x30, 0x29, 0x29, 0x2d, 0x20, 0x20, 0x23, 0x4a, 0x4a, 0x4a, 0x3b, 0x3b, 0x3b, 0x22, 0x22, 0x28, 0x27, 0x27, 0x2c, 0x1e, 0x1e, 0x22, 0x49, 0x49, 0x49, 0x3a, 0x3a, 0x3a, 0x21, 0x21, 0x26, 0x21, 0x21, 0x25, 0x23, 0x23, 0x27, 0x20, 0x20, 0x24, 0x1d, 0x1d, 0x21, 0x39, 0x39, 0x39, 0x47, 0x47, 0x47, 0x1f, 0x1f, 0x24, 0x1f, 0x1f, 0x23, 0x1e, 0x1e, 0x21, 0x46, 0x46, 0x46, 0xd3, 0xa7, 0xd4, 0x88, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x1d, 0x16, 0xd, 0x7, 0x2, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x43, 0x3a, 0x2d, 0x1b, 0x77, 0xef, 0xe6, 0x49, 0xef, 0xe6, 0xef, 0xe7, 0x77, 0xef, 0xe4, 0x4a, 0xba, 0xea, 0xc1, 0xeb, 0x0, 0x0, 0x0, 0xec, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6c, 0x8e, 0x5, 0x4e, 0x0, 0x31, 0x10, 0x45, 0xff, 0xef, 0x56, 0xc2, 0xe2, 0xee, 0x4e, 0x3c, 0xc8, 0xd, 0xb8, 0x38, 0xa7, 0xc0, 0xdd, 0xdd, 0x5d, 0xbb, 0x94, 0x4c, 0xd2, 0xc1, 0xdf, 0x4a, 0x47, 0x5e, 0x27, 0xc3, 0xc, 0x80, 0x64, 0x24, 0xb8, 0xc7, 0x4f, 0x2c, 0x6b, 0xd5, 0x90, 0xff, 0xdd, 0x23, 0x7e, 0xc1, 0xa2, 0xb6, 0x64, 0xad, 0x26, 0x82, 0xc6, 0xef, 0x45, 0xbc, 0xe3, 0x1, 0x2c, 0x69, 0x9b, 0xc8, 0x7f, 0x5, 0x36, 0x1e, 0x41, 0x84, 0xd6, 0x4, 0x25, 0x90, 0xb7, 0x39, 0x6c, 0x4c, 0x2f, 0xbe, 0x68, 0xdf, 0x13, 0xa1, 0x9e, 0xc8, 0xa4, 0xb7, 0xe7, 0x47, 0x93, 0x63, 0x1b, 0xf9, 0xdc, 0x8, 0x88, 0x60, 0xbf, 0x4, 0xff, 0xd2, 0x56, 0x93, 0xe3, 0xb7, 0xb2, 0xf6, 0x2c, 0x36, 0x1, 0x16, 0xf4, 0x5f, 0x42, 0xc4, 0x17, 0x8f, 0xb5, 0xc0, 0xa5, 0x8, 0x30, 0x5f, 0xc2, 0x5d, 0xcf, 0xc9, 0xd, 0x74, 0x87, 0x8b, 0x5e, 0x56, 0x22, 0x24, 0xf7, 0x6d, 0xc2, 0xd1, 0x80, 0x26, 0x27, 0x5d, 0x5b, 0x67, 0x7d, 0x2f, 0x80, 0x4d, 0xc9, 0x7f, 0x9, 0x63, 0xa7, 0x61, 0x23, 0xc7, 0x74, 0x3d, 0xf, 0xae, 0x41, 0x84, 0x6f, 0x13, 0xe6, 0x26, 0xfa, 0x36, 0x46, 0x73, 0xbc, 0xba, 0x3b, 0xd6, 0xca, 0x8, 0xd0, 0xd6, 0x36, 0x4e, 0x35, 0xeb, 0xad, 0xd7, 0xb0, 0x60, 0x72, 0xdc, 0xda, 0x9c, 0x2, 0x67, 0x76, 0x64, 0x82, 0x99, 0x9b, 0xb6, 0x80, 0xb0, 0x7e, 0x8d, 0xf6, 0x5a, 0xdd, 0xe1, 0xb5, 0xba, 0xba, 0xb1, 0x0, 0xcd, 0xc7, 0x50, 0x23, 0xeb, 0xfb, 0x7f, 0xb4, 0xc8, 0x22, 0x18, 0xdd, 0x0, 0xd5, 0xec, 0x4e, 0x53, 0xc6, 0x18, 0x44, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char option_button_disabled_mirrored_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x2f, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x2d, 0x34, 0x2b, 0x2b, 0x31, 0x5a, 0x5a, 0x5a, 0x3e, 0x3e, 0x3e, 0x2a, 0x2a, 0x30, 0x59, 0x59, 0x59, 0x22, 0x22, 0x27, 0x4b, 0x4b, 0x4b, 0x22, 0x22, 0x29, 0x24, 0x24, 0x28, 0x4a, 0x4a, 0x4a, 0x36, 0x36, 0x36, 0x2a, 0x2a, 0x30, 0x2c, 0x2c, 0x32, 0x2d, 0x2d, 0x34, 0x2e, 0x2e, 0x35, 0x2f, 0x2f, 0x36, 0x2a, 0x2a, 0x31, 0x62, 0x62, 0x62, 0x56, 0x56, 0x56, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2b, 0x27, 0x27, 0x2d, 0x28, 0x28, 0x2e, 0x29, 0x29, 0x2f, 0x24, 0x24, 0x28, 0x26, 0x26, 0x2b, 0x54, 0x54, 0x54, 0x43, 0x43, 0x43, 0x44, 0x44, 0x44, 0x48, 0x48, 0x48, 0x22, 0x22, 0x26, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x26, 0x26, 0x2c, 0x22, 0x22, 0x27, 0x2d, 0x2d, 0x33, 0x52, 0x52, 0x52, 0x42, 0x42, 0x42, 0x27, 0x27, 0x2b, 0x29, 0x29, 0x2e, 0x2c, 0x2c, 0x31, 0x2e, 0x2e, 0x34, 0x51, 0x51, 0x51, 0x40, 0x40, 0x40, 0x4f, 0x4f, 0x4f, 0x3f, 0x3f, 0x3f, 0x20, 0x20, 0x25, 0x24, 0x24, 0x29, 0x24, 0x24, 0x2a, 0x4d, 0x4d, 0x4d, 0x3e, 0x3e, 0x3e, 0x20, 0x20, 0x23, 0x29, 0x29, 0x2d, 0x2b, 0x2b, 0x30, 0x28, 0x28, 0x2d, 0x4c, 0x4c, 0x4c, 0x3d, 0x3d, 0x3d, 0x1e, 0x1e, 0x22, 0x27, 0x27, 0x2c, 0x22, 0x22, 0x28, 0x4a, 0x4a, 0x4a, 0x3b, 0x3b, 0x3b, 0x1d, 0x1d, 0x21, 0x20, 0x20, 0x24, 0x23, 0x23, 0x27, 0x21, 0x21, 0x25, 0x21, 0x21, 0x26, 0x3a, 0x3a, 0x3a, 0x49, 0x49, 0x49, 0x1e, 0x1e, 0x21, 0x1f, 0x1f, 0x23, 0x1f, 0x1f, 0x24, 0x47, 0x47, 0x47, 0x39, 0x39, 0x39, 0x46, 0x46, 0x46, 0xda, 0x9d, 0x96, 0x5c, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x2, 0x7, 0xd, 0x16, 0x1d, 0x22, 0x24, 0x1f, 0x19, 0x11, 0xa, 0x4, 0x1b, 0x2d, 0x3a, 0x43, 0x48, 0x47, 0x46, 0x3f, 0x34, 0x25, 0x15, 0x49, 0xe6, 0xef, 0x77, 0xe6, 0xef, 0xe7, 0xef, 0x4a, 0xe4, 0xef, 0x77, 0x7e, 0xb9, 0x59, 0x66, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x1, 0xf, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x85, 0xd1, 0xd9, 0x52, 0xc2, 0x30, 0x14, 0x80, 0xe1, 0x2a, 0x5a, 0x56, 0x97, 0xb2, 0xb9, 0xef, 0x50, 0x45, 0x4b, 0x6d, 0xe, 0x95, 0xa6, 0xa5, 0x40, 0xab, 0x15, 0x5, 0xdc, 0xc0, 0x8a, 0xa, 0xb8, 0xa1, 0xef, 0xff, 0xc, 0x86, 0x90, 0x30, 0xc, 0x17, 0xfa, 0x5d, 0x26, 0xff, 0x4c, 0x4e, 0x12, 0x41, 0x98, 0x32, 0x33, 0x1b, 0x98, 0x9b, 0x17, 0x83, 0x84, 0x18, 0xa, 0x47, 0xa2, 0xb1, 0xe9, 0x7d, 0x21, 0x16, 0x58, 0x58, 0x5c, 0x5a, 0x96, 0x24, 0x29, 0x9e, 0x48, 0xa6, 0xd2, 0x2b, 0x51, 0xb2, 0xb4, 0xba, 0x96, 0xc9, 0xca, 0x87, 0x47, 0x8c, 0x9c, 0xc9, 0x1d, 0x9f, 0x30, 0xeb, 0x1b, 0xe9, 0x8, 0x9, 0x36, 0x15, 0x25, 0xaf, 0x9e, 0x6a, 0x8c, 0x8a, 0xa0, 0xa0, 0x8f, 0x9c, 0x15, 0xb7, 0x52, 0x61, 0x12, 0xa8, 0x6, 0x56, 0x4d, 0x2b, 0xcb, 0x98, 0xb8, 0xc4, 0x3, 0x5d, 0x2f, 0x24, 0x43, 0xc3, 0xc0, 0x6, 0xcd, 0x2a, 0xcb, 0x4c, 0xe, 0x4a, 0x95, 0x2a, 0x57, 0x49, 0x88, 0x24, 0x70, 0xc, 0x70, 0xcf, 0xcb, 0x17, 0x8c, 0x5, 0x8e, 0x77, 0xc9, 0x79, 0xf1, 0x20, 0x9, 0x80, 0x4, 0xd6, 0x64, 0x50, 0xbb, 0xe2, 0x6a, 0xd2, 0x30, 0xc0, 0xd7, 0xf5, 0x89, 0x19, 0xb4, 0x46, 0xbe, 0x79, 0xc3, 0x35, 0x69, 0x80, 0x6e, 0x15, 0xb8, 0x33, 0xef, 0x99, 0x7, 0x84, 0x5a, 0x6d, 0xae, 0x45, 0x8f, 0xb0, 0x1f, 0xed, 0x86, 0x3f, 0xbe, 0xa6, 0x6f, 0x3f, 0x75, 0x9e, 0xb9, 0xe, 0x1d, 0xd2, 0x78, 0x79, 0xed, 0x62, 0xf0, 0x19, 0xdc, 0xeb, 0x17, 0xdf, 0xb8, 0x77, 0x7a, 0xcd, 0xed, 0x8f, 0xcf, 0x5e, 0xb7, 0x8e, 0x19, 0xe5, 0xab, 0x3f, 0xf8, 0x66, 0xda, 0x3b, 0xf4, 0xa1, 0x76, 0xf7, 0x90, 0xe3, 0x8e, 0x67, 0x70, 0x1, 0xbc, 0x9f, 0x91, 0xc1, 0xfe, 0x1, 0x7d, 0xea, 0x7f, 0x3f, 0xeb, 0xef, 0xef, 0xfe, 0x5, 0xd6, 0xe3, 0x56, 0x89, 0xd8, 0x62, 0xb6, 0x83, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char option_button_hover_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x41, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x42, 0x40, 0x4b, 0x5f, 0x5a, 0x6c, 0x2b, 0x2b, 0x31, 0x2e, 0x2e, 0x34, 0x5f, 0x5a, 0x6b, 0x2a, 0x2a, 0x30, 0x56, 0x53, 0x64, 0x22, 0x22, 0x27, 0x3e, 0x3b, 0x46, 0x57, 0x53, 0x63, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x5b, 0x57, 0x68, 0x5a, 0x56, 0x67, 0x67, 0x63, 0x76, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x2d, 0x2d, 0x34, 0x2f, 0x2f, 0x36, 0x2e, 0x2e, 0x35, 0x2c, 0x2c, 0x32, 0x4d, 0x4a, 0x57, 0x49, 0x46, 0x52, 0x48, 0x45, 0x51, 0x5a, 0x56, 0x65, 0x26, 0x26, 0x2b, 0x24, 0x24, 0x28, 0x27, 0x27, 0x2d, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x25, 0x25, 0x2b, 0x23, 0x23, 0x28, 0x5b, 0x57, 0x66, 0x26, 0x26, 0x2c, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x22, 0x22, 0x26, 0x59, 0x55, 0x64, 0x47, 0x44, 0x50, 0x2d, 0x2d, 0x33, 0x22, 0x22, 0x27, 0x58, 0x54, 0x64, 0x46, 0x43, 0x50, 0x27, 0x27, 0x2b, 0x2e, 0x2e, 0x34, 0x2c, 0x2c, 0x31, 0x29, 0x29, 0x2e, 0x56, 0x53, 0x63, 0x45, 0x42, 0x4f, 0x56, 0x53, 0x62, 0x45, 0x42, 0x4e, 0x24, 0x24, 0x2a, 0x24, 0x24, 0x29, 0x20, 0x20, 0x25, 0x55, 0x51, 0x62, 0x44, 0x41, 0x4e, 0x28, 0x28, 0x2d, 0x2b, 0x2b, 0x30, 0x29, 0x29, 0x2d, 0x20, 0x20, 0x23, 0x55, 0x51, 0x60, 0x44, 0x41, 0x4d, 0x22, 0x22, 0x28, 0x27, 0x27, 0x2c, 0x1e, 0x1e, 0x22, 0x43, 0x40, 0x4c, 0x54, 0x50, 0x5f, 0x21, 0x21, 0x26, 0x21, 0x21, 0x25, 0x23, 0x23, 0x27, 0x20, 0x20, 0x24, 0x1d, 0x1d, 0x21, 0x47, 0x43, 0x51, 0x43, 0x3f, 0x4d, 0x42, 0x3f, 0x4c, 0x53, 0x4f, 0x5f, 0x1f, 0x1f, 0x24, 0x1f, 0x1f, 0x23, 0x1e, 0x1e, 0x21, 0x53, 0x50, 0x5f, 0x53, 0x4f, 0x5e, 0x5f, 0x5a, 0x6c, 0xd3, 0x26, 0x54, 0x35, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x1d, 0x16, 0xd, 0x7, 0x2, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x43, 0x3a, 0x2d, 0x1b, 0x77, 0xef, 0xe6, 0x49, 0xef, 0xe6, 0xef, 0xe7, 0x77, 0xef, 0xe4, 0x4a, 0xba, 0xea, 0xc1, 0xeb, 0x0, 0x0, 0x0, 0xe5, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x85, 0x91, 0x43, 0x62, 0xc5, 0x60, 0x18, 0x45, 0xef, 0x8d, 0x51, 0xdb, 0xee, 0x46, 0xca, 0x6d, 0x77, 0x58, 0xce, 0x6b, 0xdb, 0x7c, 0x8a, 0xad, 0xea, 0x44, 0x1f, 0x4e, 0x92, 0x1f, 0x4c, 0x0, 0xe0, 0x9, 0x61, 0xf0, 0x89, 0x32, 0x12, 0xcd, 0xd4, 0x8, 0xef, 0x1f, 0x35, 0x54, 0xa0, 0x68, 0x1a, 0x34, 0x89, 0x8, 0x86, 0xa4, 0xd, 0x57, 0xb4, 0xdf, 0x79, 0x5, 0x89, 0x94, 0xba, 0xf9, 0xb3, 0xc0, 0xce, 0xeb, 0xf0, 0x17, 0x1c, 0xab, 0x11, 0x9, 0x1a, 0xf9, 0x96, 0x84, 0x5d, 0x5e, 0x43, 0x15, 0x7, 0x2e, 0x42, 0x41, 0x51, 0xd3, 0xbe, 0x67, 0xd5, 0x6b, 0x42, 0x3a, 0x38, 0x9b, 0xf5, 0x2e, 0x20, 0xfa, 0x5, 0x33, 0x41, 0x69, 0xf4, 0xeb, 0x49, 0x6c, 0x19, 0xe6, 0xbd, 0xdd, 0xd, 0x48, 0xa0, 0x92, 0xb, 0x36, 0x72, 0x6a, 0x26, 0xf0, 0x14, 0xa, 0x10, 0x72, 0xe1, 0x7d, 0xf4, 0xf6, 0x35, 0x1b, 0xc3, 0xe3, 0x18, 0x9d, 0x50, 0xf0, 0xe4, 0xc2, 0x17, 0xae, 0x27, 0xd3, 0xe4, 0x6e, 0xe8, 0xf8, 0x7e, 0xbc, 0x1, 0x48, 0x9e, 0x57, 0xf8, 0xc5, 0xfc, 0xbd, 0x7a, 0x98, 0xc4, 0x94, 0x47, 0xbf, 0xe4, 0xce, 0x48, 0x8, 0x6e, 0x69, 0x91, 0x63, 0x47, 0x73, 0x49, 0xbc, 0x77, 0x3e, 0xd7, 0x4b, 0x3b, 0x12, 0x36, 0x16, 0xb3, 0x85, 0x6a, 0x68, 0xce, 0x39, 0x62, 0xc6, 0xba, 0x3d, 0x8d, 0xe7, 0x91, 0x20, 0xac, 0xad, 0xa6, 0xc2, 0xe, 0x6, 0xcc, 0x74, 0xc, 0x2d, 0xe7, 0xf9, 0x55, 0x2, 0x28, 0x94, 0x37, 0xab, 0xee, 0xa1, 0xcc, 0xbf, 0xdb, 0xed, 0x3, 0x70, 0xe6, 0x4f, 0x4a, 0xc3, 0xed, 0xed, 0xf3, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char option_button_hover_mirrored_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x41, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x2d, 0x34, 0x2b, 0x2b, 0x31, 0x5f, 0x5a, 0x6c, 0x42, 0x40, 0x4b, 0x2a, 0x2a, 0x30, 0x5f, 0x5a, 0x6b, 0x22, 0x22, 0x27, 0x56, 0x53, 0x64, 0x22, 0x22, 0x29, 0x24, 0x24, 0x28, 0x57, 0x53, 0x63, 0x3e, 0x3c, 0x47, 0x2a, 0x2a, 0x30, 0x2c, 0x2c, 0x32, 0x2d, 0x2d, 0x34, 0x2e, 0x2e, 0x35, 0x2f, 0x2f, 0x36, 0x2a, 0x2a, 0x31, 0x67, 0x63, 0x76, 0x5a, 0x56, 0x67, 0x5b, 0x57, 0x68, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2b, 0x27, 0x27, 0x2d, 0x28, 0x28, 0x2e, 0x29, 0x29, 0x2f, 0x24, 0x24, 0x28, 0x26, 0x26, 0x2b, 0x5a, 0x56, 0x65, 0x48, 0x45, 0x51, 0x49, 0x46, 0x52, 0x4d, 0x4a, 0x57, 0x22, 0x22, 0x26, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x26, 0x26, 0x2c, 0x5b, 0x57, 0x66, 0x22, 0x22, 0x27, 0x2d, 0x2d, 0x33, 0x59, 0x55, 0x64, 0x47, 0x44, 0x50, 0x27, 0x27, 0x2b, 0x29, 0x29, 0x2e, 0x2c, 0x2c, 0x31, 0x2e, 0x2e, 0x34, 0x58, 0x54, 0x64, 0x46, 0x43, 0x50, 0x56, 0x53, 0x63, 0x45, 0x42, 0x4f, 0x20, 0x20, 0x25, 0x24, 0x24, 0x29, 0x24, 0x24, 0x2a, 0x56, 0x53, 0x62, 0x45, 0x42, 0x4e, 0x20, 0x20, 0x23, 0x29, 0x29, 0x2d, 0x2b, 0x2b, 0x30, 0x28, 0x28, 0x2d, 0x55, 0x51, 0x62, 0x44, 0x41, 0x4e, 0x1e, 0x1e, 0x22, 0x27, 0x27, 0x2c, 0x22, 0x22, 0x28, 0x55, 0x51, 0x60, 0x44, 0x41, 0x4d, 0x1d, 0x1d, 0x21, 0x20, 0x20, 0x24, 0x23, 0x23, 0x27, 0x21, 0x21, 0x25, 0x21, 0x21, 0x26, 0x54, 0x50, 0x5f, 0x43, 0x40, 0x4c, 0x1e, 0x1e, 0x21, 0x1f, 0x1f, 0x23, 0x1f, 0x1f, 0x24, 0x53, 0x4f, 0x5f, 0x42, 0x3f, 0x4c, 0x43, 0x3f, 0x4d, 0x47, 0x43, 0x51, 0x5f, 0x5a, 0x6c, 0x53, 0x4f, 0x5e, 0x53, 0x50, 0x5f, 0x68, 0x6, 0xa3, 0x65, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x2, 0x7, 0xd, 0x16, 0x1d, 0x22, 0x24, 0x1f, 0x19, 0x11, 0xa, 0x4, 0x1b, 0x2d, 0x3a, 0x43, 0x48, 0x47, 0x46, 0x3f, 0x34, 0x25, 0x15, 0x49, 0xe6, 0xef, 0x77, 0xe6, 0xef, 0xe7, 0xef, 0x4a, 0xe4, 0xef, 0x77, 0x7e, 0xb9, 0x59, 0x66, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x1, 0x15, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x85, 0xd1, 0xd9, 0x52, 0xc2, 0x30, 0x14, 0x80, 0xe1, 0x2a, 0x5a, 0x56, 0x97, 0xb2, 0xb9, 0x8b, 0xb, 0x54, 0x51, 0x4b, 0x6d, 0x8f, 0x60, 0xd3, 0x62, 0xc1, 0x56, 0x11, 0x5, 0xdc, 0xd0, 0x5a, 0x14, 0x70, 0x5f, 0xdf, 0xff, 0x1, 0xc, 0x69, 0xc2, 0x30, 0x5c, 0xe8, 0x77, 0x99, 0xfc, 0x33, 0x39, 0x49, 0x38, 0x6e, 0xc8, 0xc8, 0xa8, 0x6f, 0x6c, 0x9c, 0xf7, 0x63, 0x7c, 0x20, 0x18, 0xa, 0x47, 0x86, 0xf7, 0xb9, 0x88, 0x6f, 0x62, 0x72, 0x6a, 0x5a, 0x10, 0x84, 0x68, 0x2c, 0x9e, 0x48, 0xce, 0x84, 0xf1, 0xd2, 0xec, 0x5c, 0x3a, 0x23, 0x6e, 0x6c, 0x52, 0x62, 0x3a, 0xbb, 0xb5, 0xed, 0xd9, 0x99, 0x5f, 0x48, 0x86, 0x70, 0xb0, 0x28, 0x49, 0x39, 0x79, 0x57, 0xa1, 0x64, 0x15, 0xf6, 0xf2, 0x9e, 0xc2, 0xfe, 0x52, 0x22, 0x88, 0x3, 0x59, 0x43, 0xb2, 0x6e, 0x64, 0x28, 0x1d, 0x15, 0x59, 0x90, 0x2f, 0x1c, 0xc4, 0x3, 0xbd, 0xc0, 0x4, 0xc5, 0x28, 0x89, 0x54, 0x16, 0x8a, 0xe5, 0x43, 0xa6, 0x1c, 0xe3, 0x71, 0x60, 0x69, 0x60, 0x1f, 0x95, 0x8e, 0x29, 0x3, 0xac, 0xca, 0x9, 0x53, 0x89, 0xfa, 0x71, 0x0, 0x38, 0x30, 0x6, 0x83, 0xea, 0x29, 0x53, 0x15, 0x7a, 0x1, 0x3a, 0xab, 0xd, 0xcc, 0xa0, 0xd4, 0x73, 0x8d, 0x73, 0xa6, 0x41, 0x2, 0xf5, 0x42, 0x82, 0x4b, 0xfd, 0x8a, 0xba, 0x56, 0xd5, 0xe6, 0xd, 0xd3, 0x24, 0x47, 0x98, 0xb7, 0x66, 0xdd, 0xe9, 0x5f, 0xd3, 0x31, 0xef, 0xdc, 0x16, 0xe3, 0x92, 0x21, 0xb5, 0xfb, 0x87, 0x36, 0x2, 0x87, 0x42, 0x9d, 0xee, 0xe3, 0x13, 0xd5, 0x72, 0xc9, 0x35, 0x97, 0x9f, 0x5f, 0x3a, 0xed, 0x1a, 0xa2, 0xa4, 0xd7, 0xee, 0xdb, 0xbb, 0xe7, 0xe3, 0x33, 0x45, 0x1e, 0x6a, 0x65, 0x55, 0xb5, 0xec, 0xfe, 0xc, 0x36, 0xc0, 0xd7, 0xb7, 0xe7, 0x67, 0x6d, 0x9d, 0x3c, 0xf5, 0xbf, 0x9f, 0xf5, 0xf7, 0x77, 0xff, 0x2, 0xa7, 0xc5, 0x58, 0xc8, 0x6e, 0x59, 0x8, 0x3, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char option_button_normal_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x41, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x3a, 0x44, 0x56, 0x53, 0x61, 0x2b, 0x2b, 0x31, 0x2e, 0x2e, 0x34, 0x56, 0x52, 0x60, 0x2a, 0x2a, 0x30, 0x47, 0x44, 0x52, 0x22, 0x22, 0x27, 0x33, 0x31, 0x39, 0x47, 0x44, 0x50, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x52, 0x50, 0x5d, 0x51, 0x4f, 0x5d, 0x5d, 0x5a, 0x6a, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x2d, 0x2d, 0x34, 0x2f, 0x2f, 0x36, 0x2e, 0x2e, 0x35, 0x2c, 0x2c, 0x32, 0x46, 0x42, 0x4e, 0x42, 0x3e, 0x4a, 0x41, 0x3e, 0x49, 0x51, 0x4e, 0x5b, 0x26, 0x26, 0x2b, 0x24, 0x24, 0x28, 0x27, 0x27, 0x2d, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x25, 0x25, 0x2b, 0x23, 0x23, 0x28, 0x40, 0x3e, 0x48, 0x50, 0x4e, 0x5a, 0x26, 0x26, 0x2c, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x22, 0x22, 0x26, 0x4f, 0x4c, 0x59, 0x3f, 0x3d, 0x47, 0x2d, 0x2d, 0x33, 0x22, 0x22, 0x27, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x27, 0x27, 0x2b, 0x2e, 0x2e, 0x34, 0x2c, 0x2c, 0x31, 0x29, 0x29, 0x2e, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x24, 0x24, 0x2a, 0x24, 0x24, 0x29, 0x20, 0x20, 0x25, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x28, 0x28, 0x2d, 0x2b, 0x2b, 0x30, 0x29, 0x29, 0x2d, 0x20, 0x20, 0x23, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x22, 0x22, 0x28, 0x27, 0x27, 0x2c, 0x1e, 0x1e, 0x22, 0x47, 0x43, 0x50, 0x38, 0x35, 0x3f, 0x46, 0x42, 0x4f, 0x21, 0x21, 0x26, 0x21, 0x21, 0x25, 0x23, 0x23, 0x27, 0x20, 0x20, 0x24, 0x1d, 0x1d, 0x21, 0x36, 0x34, 0x3e, 0x44, 0x41, 0x4e, 0x1f, 0x1f, 0x24, 0x1f, 0x1f, 0x23, 0x1e, 0x1e, 0x21, 0x44, 0x42, 0x4d, 0x44, 0x41, 0x4c, 0x4e, 0x4b, 0x58, 0x8, 0xd9, 0x10, 0xcb, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x1d, 0x16, 0xd, 0x7, 0x2, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x43, 0x3a, 0x2d, 0x1b, 0x77, 0xef, 0xe6, 0x49, 0xef, 0xe6, 0xef, 0xe7, 0x77, 0xef, 0xe4, 0x4a, 0xba, 0xea, 0xc1, 0xeb, 0x0, 0x0, 0x0, 0xe6, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6c, 0xd1, 0x55, 0x5a, 0xc4, 0x30, 0x14, 0x5, 0xe0, 0x73, 0xea, 0x82, 0xbb, 0x3b, 0x6c, 0x80, 0x45, 0xb0, 0x70, 0x5e, 0xb1, 0x37, 0xdc, 0x75, 0xdc, 0x3d, 0x43, 0x2a, 0x5f, 0x3a, 0x7e, 0x6a, 0xc9, 0xbd, 0x7f, 0x9d, 0x2a, 0x0, 0xa4, 0x16, 0xd, 0xaa, 0x18, 0x8e, 0x41, 0x3f, 0x11, 0xd1, 0xbe, 0x52, 0xc7, 0x48, 0xa8, 0xfb, 0x5e, 0x68, 0xd4, 0x24, 0x96, 0x6a, 0xfc, 0xaf, 0x8b, 0x32, 0xbf, 0x61, 0x90, 0xc6, 0x3c, 0x27, 0x3, 0xce, 0xfe, 0x20, 0x2, 0x4b, 0xd2, 0x45, 0x1f, 0x14, 0xd5, 0x78, 0x5e, 0x36, 0x1c, 0x7d, 0xe5, 0x33, 0x2, 0xb3, 0x84, 0x8a, 0xec, 0xd4, 0xeb, 0x9a, 0x1a, 0x1b, 0x82, 0xf5, 0x79, 0x20, 0x2, 0x46, 0x1f, 0x58, 0x8d, 0x95, 0xe4, 0x6a, 0x1d, 0xcf, 0x4f, 0x89, 0x85, 0x10, 0x80, 0x56, 0x1f, 0x8, 0xf4, 0x53, 0xf7, 0x81, 0x6c, 0x4, 0xa0, 0xf5, 0x41, 0x69, 0xeb, 0xb7, 0xd0, 0x7b, 0x86, 0xcc, 0x36, 0xbb, 0x11, 0x90, 0x66, 0x1f, 0x88, 0xef, 0xbd, 0x64, 0x92, 0x5a, 0x7b, 0x4e, 0xed, 0x34, 0x42, 0x20, 0xa5, 0xd5, 0x7, 0x27, 0x69, 0xfb, 0x51, 0x8d, 0x69, 0x6e, 0xd5, 0xcc, 0xb9, 0x18, 0xf4, 0xaf, 0x40, 0x6e, 0x3f, 0x1d, 0xab, 0xf1, 0xdd, 0xc7, 0xf1, 0x12, 0x45, 0xc, 0xce, 0x4f, 0x17, 0x12, 0xd0, 0xb6, 0xc4, 0x87, 0x1a, 0x6f, 0x2f, 0x48, 0x8b, 0xef, 0x31, 0xd0, 0x6e, 0xce, 0xa8, 0xc0, 0x25, 0x56, 0x7d, 0x5, 0x52, 0xed, 0x6e, 0xae, 0x68, 0x0, 0xd4, 0x86, 0x7f, 0x56, 0x43, 0x62, 0x38, 0xc, 0x46, 0x28, 0xba, 0x1, 0x7a, 0xad, 0x4f, 0x59, 0x90, 0xab, 0xbf, 0xa4, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char option_button_normal_mirrored_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x41, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x2d, 0x34, 0x2b, 0x2b, 0x31, 0x56, 0x53, 0x61, 0x3c, 0x3a, 0x45, 0x2a, 0x2a, 0x30, 0x56, 0x52, 0x60, 0x22, 0x22, 0x27, 0x47, 0x44, 0x52, 0x22, 0x22, 0x29, 0x24, 0x24, 0x28, 0x47, 0x44, 0x50, 0x33, 0x31, 0x3a, 0x2a, 0x2a, 0x30, 0x2c, 0x2c, 0x32, 0x2d, 0x2d, 0x34, 0x2e, 0x2e, 0x35, 0x2f, 0x2f, 0x36, 0x2a, 0x2a, 0x31, 0x5d, 0x5a, 0x6a, 0x51, 0x4f, 0x5d, 0x52, 0x50, 0x5d, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2b, 0x27, 0x27, 0x2d, 0x28, 0x28, 0x2e, 0x29, 0x29, 0x2f, 0x24, 0x24, 0x28, 0x26, 0x26, 0x2b, 0x51, 0x4e, 0x5b, 0x41, 0x3e, 0x49, 0x42, 0x3e, 0x4a, 0x46, 0x42, 0x4e, 0x22, 0x22, 0x26, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x26, 0x26, 0x2c, 0x50, 0x4e, 0x5a, 0x40, 0x3e, 0x48, 0x22, 0x22, 0x27, 0x2d, 0x2d, 0x33, 0x4f, 0x4c, 0x59, 0x3f, 0x3d, 0x47, 0x27, 0x27, 0x2b, 0x29, 0x29, 0x2e, 0x2c, 0x2c, 0x31, 0x2e, 0x2e, 0x34, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x20, 0x20, 0x25, 0x24, 0x24, 0x29, 0x24, 0x24, 0x2a, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x20, 0x20, 0x23, 0x29, 0x29, 0x2d, 0x2b, 0x2b, 0x30, 0x28, 0x28, 0x2d, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x1e, 0x1e, 0x22, 0x27, 0x27, 0x2c, 0x22, 0x22, 0x28, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x1d, 0x1d, 0x21, 0x20, 0x20, 0x24, 0x23, 0x23, 0x27, 0x21, 0x21, 0x25, 0x21, 0x21, 0x26, 0x46, 0x42, 0x4f, 0x38, 0x35, 0x3f, 0x47, 0x43, 0x50, 0x1e, 0x1e, 0x21, 0x1f, 0x1f, 0x23, 0x1f, 0x1f, 0x24, 0x44, 0x41, 0x4e, 0x36, 0x34, 0x3e, 0x4e, 0x4b, 0x58, 0x44, 0x41, 0x4c, 0x44, 0x42, 0x4d, 0x7d, 0x2e, 0xcf, 0xc5, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x2, 0x7, 0xd, 0x16, 0x1d, 0x22, 0x24, 0x1f, 0x19, 0x11, 0xa, 0x4, 0x1b, 0x2d, 0x3a, 0x43, 0x48, 0x47, 0x46, 0x3f, 0x34, 0x25, 0x15, 0x49, 0xe6, 0xef, 0x77, 0xe6, 0xef, 0xe7, 0xef, 0x4a, 0xe4, 0xef, 0x77, 0x7e, 0xb9, 0x59, 0x66, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x1, 0x13, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x85, 0xd1, 0xd9, 0x52, 0xc2, 0x30, 0x14, 0x80, 0xe1, 0x2a, 0x5a, 0x56, 0x97, 0xb2, 0xb9, 0x8b, 0xb, 0x54, 0x51, 0x4b, 0x6d, 0xf, 0x68, 0xd3, 0xa2, 0x5, 0x5b, 0x40, 0x14, 0x70, 0x3, 0x2b, 0x2a, 0xe0, 0xbe, 0xbd, 0xff, 0x3, 0x18, 0xd2, 0x84, 0x61, 0xb8, 0xd0, 0xef, 0x32, 0xf9, 0x67, 0x72, 0x92, 0x70, 0xdc, 0x88, 0xb1, 0x71, 0xcf, 0xc4, 0x24, 0xef, 0xc5, 0x78, 0x9f, 0x3f, 0x10, 0xc, 0x8d, 0xee, 0x73, 0x21, 0xcf, 0xd4, 0xf4, 0xcc, 0xac, 0x20, 0x8, 0xe1, 0x48, 0x34, 0x16, 0x9f, 0xb, 0xe2, 0xa5, 0xf9, 0x85, 0x64, 0x4a, 0xdc, 0xda, 0xa6, 0xc4, 0x64, 0x7a, 0x67, 0xd7, 0xb5, 0xb7, 0xb8, 0x14, 0xf, 0xe0, 0x60, 0x59, 0x92, 0x32, 0xf2, 0xbe, 0x42, 0xc9, 0x2a, 0x64, 0x73, 0xae, 0x83, 0xc3, 0x95, 0x98, 0x1f, 0x7, 0xb2, 0x86, 0x64, 0xdd, 0x48, 0x51, 0x3a, 0xca, 0x1f, 0x1d, 0x53, 0xb9, 0x6c, 0xd4, 0xd7, 0xf, 0x4c, 0x50, 0x8c, 0x82, 0x48, 0xa5, 0x21, 0x5f, 0x3c, 0x61, 0x8a, 0x11, 0x1e, 0x7, 0x96, 0x6, 0x76, 0xa9, 0x50, 0xa6, 0xc, 0xb0, 0x2a, 0xa7, 0x4c, 0x25, 0xec, 0xc5, 0x1, 0xe0, 0xc0, 0x18, 0xe, 0xaa, 0x67, 0x4c, 0x55, 0xe8, 0x7, 0xe8, 0xbc, 0x36, 0x34, 0x83, 0x52, 0xcf, 0x34, 0x2e, 0x98, 0x6, 0x9, 0xd4, 0x4b, 0x9, 0xae, 0xf4, 0x6b, 0xea, 0x46, 0x55, 0x9b, 0x2d, 0xa6, 0x49, 0x8e, 0x30, 0x6f, 0xcd, 0xba, 0x33, 0xb8, 0xa6, 0x63, 0xde, 0xb5, 0xef, 0x99, 0x36, 0x19, 0x52, 0x7b, 0x78, 0xec, 0x20, 0x70, 0x28, 0xd4, 0xed, 0x3d, 0x3d, 0x33, 0x2f, 0xe4, 0x9a, 0xab, 0xaf, 0x6f, 0xdd, 0x4e, 0xd, 0x51, 0xd2, 0x7b, 0xef, 0xe3, 0x93, 0x6a, 0x25, 0xc8, 0x43, 0xad, 0xad, 0xab, 0x96, 0x3d, 0x98, 0xc1, 0x6, 0xf8, 0xfa, 0x76, 0xfd, 0x6c, 0x6c, 0x92, 0xa7, 0xfe, 0xf7, 0xb3, 0xfe, 0xfe, 0xee, 0x5f, 0xa1, 0x5f, 0x59, 0xbd, 0x75, 0x41, 0x2b, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char option_button_pressed_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x4a, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x31, 0x2f, 0x37, 0x46, 0x43, 0x4f, 0x2b, 0x2b, 0x31, 0x2e, 0x2e, 0x34, 0x47, 0x44, 0x50, 0x2a, 0x2a, 0x30, 0x55, 0x52, 0x5f, 0x22, 0x22, 0x27, 0x3d, 0x3a, 0x45, 0x56, 0x52, 0x60, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x43, 0x40, 0x4c, 0x42, 0x40, 0x4b, 0x4c, 0x49, 0x56, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x2d, 0x2d, 0x34, 0x2f, 0x2f, 0x36, 0x2e, 0x2e, 0x35, 0x2c, 0x2c, 0x32, 0x3a, 0x38, 0x41, 0x36, 0x34, 0x3d, 0x44, 0x41, 0x4c, 0x26, 0x26, 0x2b, 0x24, 0x24, 0x28, 0x27, 0x27, 0x2d, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x25, 0x25, 0x2b, 0x23, 0x23, 0x28, 0x44, 0x42, 0x4e, 0x36, 0x34, 0x3e, 0x44, 0x41, 0x4e, 0x26, 0x26, 0x2c, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x22, 0x22, 0x26, 0x46, 0x42, 0x4f, 0x38, 0x35, 0x3f, 0x2d, 0x2d, 0x33, 0x22, 0x22, 0x27, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x27, 0x27, 0x2b, 0x2e, 0x2e, 0x34, 0x2c, 0x2c, 0x31, 0x29, 0x29, 0x2e, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x24, 0x24, 0x2a, 0x24, 0x24, 0x29, 0x20, 0x20, 0x25, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x28, 0x28, 0x2d, 0x2b, 0x2b, 0x30, 0x29, 0x29, 0x2d, 0x20, 0x20, 0x23, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x22, 0x22, 0x28, 0x27, 0x27, 0x2c, 0x1e, 0x1e, 0x22, 0x50, 0x4d, 0x5a, 0x3f, 0x3d, 0x48, 0x3f, 0x3d, 0x47, 0x4f, 0x4c, 0x59, 0x21, 0x21, 0x26, 0x21, 0x21, 0x25, 0x23, 0x23, 0x27, 0x20, 0x20, 0x24, 0x1d, 0x1d, 0x21, 0x45, 0x42, 0x4d, 0x41, 0x3e, 0x49, 0x40, 0x3e, 0x48, 0x50, 0x4e, 0x5a, 0x1f, 0x1f, 0x24, 0x1f, 0x1f, 0x23, 0x1e, 0x1e, 0x21, 0x52, 0x4e, 0x5c, 0x51, 0x4e, 0x5b, 0x5d, 0x59, 0x69, 0x10, 0x9d, 0xe0, 0x3c, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x1d, 0x16, 0xd, 0x7, 0x2, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x43, 0x3a, 0x2d, 0x1b, 0x77, 0xef, 0xe6, 0x49, 0xef, 0xe6, 0xef, 0xe7, 0x77, 0xef, 0xe4, 0x4a, 0xba, 0xea, 0xc1, 0xeb, 0x0, 0x0, 0x0, 0xe6, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6c, 0xcf, 0x3, 0x62, 0x4, 0x51, 0x10, 0x4, 0xd0, 0xaa, 0x31, 0x62, 0xdb, 0xb8, 0x49, 0x2e, 0x9e, 0x3b, 0xc4, 0xb6, 0x9d, 0xc5, 0x58, 0x1f, 0xc1, 0xd6, 0xe8, 0x77, 0xf7, 0x1b, 0x59, 0x6c, 0x2, 0x20, 0x37, 0xaa, 0xc5, 0x17, 0x7e, 0xc7, 0x62, 0x28, 0x45, 0x75, 0xfe, 0x6c, 0xe1, 0x4f, 0x68, 0x86, 0x41, 0x69, 0x44, 0x51, 0x4b, 0xb1, 0xce, 0xcc, 0xe4, 0x83, 0xd7, 0xb0, 0x48, 0x6b, 0x98, 0xe8, 0x9, 0x38, 0x70, 0x8b, 0xa, 0xcc, 0x12, 0x1a, 0xf0, 0x4d, 0xac, 0x87, 0xf3, 0x96, 0x6f, 0x8e, 0x5f, 0x56, 0xc0, 0x53, 0x20, 0x8f, 0xbf, 0xdb, 0x86, 0x58, 0x5b, 0x9, 0xbf, 0x47, 0x80, 0xa, 0x58, 0x1a, 0x38, 0xad, 0x9, 0x5f, 0xac, 0xe3, 0x20, 0xbc, 0x4b, 0x46, 0x4b, 0x0, 0x3a, 0x1a, 0x24, 0xd0, 0x69, 0x85, 0xc0, 0x63, 0x5, 0x60, 0x68, 0xf0, 0x36, 0x7f, 0xf3, 0xaa, 0xbe, 0xe1, 0x61, 0x81, 0x69, 0x5, 0x72, 0x5b, 0x83, 0xe4, 0x6a, 0x59, 0x16, 0xf7, 0x53, 0x47, 0x77, 0x8b, 0xad, 0x12, 0xe4, 0xb9, 0xa3, 0xc1, 0xe6, 0x83, 0x7b, 0x20, 0xd6, 0xb4, 0xe7, 0xbf, 0xed, 0xe1, 0x1a, 0xd8, 0xfa, 0xdf, 0xb9, 0x70, 0xb8, 0x21, 0xd6, 0xbb, 0x17, 0x1b, 0xe3, 0x4c, 0x6a, 0xb0, 0xbd, 0x25, 0x5, 0x3b, 0x5e, 0x7c, 0x21, 0xc0, 0xc2, 0x68, 0xee, 0xf1, 0xbc, 0x6, 0x46, 0xb1, 0xbd, 0x5e, 0x30, 0x5, 0x27, 0x19, 0x24, 0xb8, 0x61, 0x6e, 0xf8, 0xf5, 0xf7, 0xcd, 0x47, 0x16, 0xa0, 0x18, 0x13, 0x6a, 0x64, 0x7d, 0xff, 0x8f, 0x1e, 0x59, 0x84, 0xa2, 0x1b, 0x0, 0xe5, 0xe0, 0x4e, 0x46, 0x1d, 0x98, 0x92, 0x5c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char option_button_pressed_mirrored_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x4a, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x2d, 0x34, 0x2b, 0x2b, 0x31, 0x46, 0x43, 0x4f, 0x31, 0x2f, 0x38, 0x2a, 0x2a, 0x30, 0x47, 0x44, 0x50, 0x22, 0x22, 0x27, 0x55, 0x52, 0x5f, 0x22, 0x22, 0x29, 0x24, 0x24, 0x28, 0x56, 0x52, 0x60, 0x3c, 0x3a, 0x45, 0x2a, 0x2a, 0x30, 0x2c, 0x2c, 0x32, 0x2d, 0x2d, 0x34, 0x2e, 0x2e, 0x35, 0x2f, 0x2f, 0x36, 0x2a, 0x2a, 0x31, 0x4c, 0x49, 0x56, 0x42, 0x40, 0x4b, 0x43, 0x40, 0x4c, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2b, 0x27, 0x27, 0x2d, 0x28, 0x28, 0x2e, 0x29, 0x29, 0x2f, 0x24, 0x24, 0x28, 0x26, 0x26, 0x2b, 0x44, 0x41, 0x4c, 0x36, 0x34, 0x3d, 0x3a, 0x38, 0x41, 0x22, 0x22, 0x26, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x26, 0x26, 0x2c, 0x44, 0x41, 0x4e, 0x36, 0x34, 0x3e, 0x44, 0x42, 0x4e, 0x22, 0x22, 0x27, 0x2d, 0x2d, 0x33, 0x46, 0x42, 0x4f, 0x38, 0x35, 0x3f, 0x27, 0x27, 0x2b, 0x29, 0x29, 0x2e, 0x2c, 0x2c, 0x31, 0x2e, 0x2e, 0x34, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x20, 0x20, 0x25, 0x24, 0x24, 0x29, 0x24, 0x24, 0x2a, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x20, 0x20, 0x23, 0x29, 0x29, 0x2d, 0x2b, 0x2b, 0x30, 0x28, 0x28, 0x2d, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x1e, 0x1e, 0x22, 0x27, 0x27, 0x2c, 0x22, 0x22, 0x28, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x1d, 0x1d, 0x21, 0x20, 0x20, 0x24, 0x23, 0x23, 0x27, 0x21, 0x21, 0x25, 0x21, 0x21, 0x26, 0x4f, 0x4c, 0x59, 0x3f, 0x3d, 0x47, 0x3f, 0x3d, 0x48, 0x50, 0x4d, 0x5a, 0x1e, 0x1e, 0x21, 0x1f, 0x1f, 0x23, 0x1f, 0x1f, 0x24, 0x50, 0x4e, 0x5a, 0x40, 0x3e, 0x48, 0x41, 0x3e, 0x49, 0x45, 0x42, 0x4d, 0x5d, 0x59, 0x69, 0x51, 0x4e, 0x5b, 0x52, 0x4e, 0x5c, 0x49, 0x7e, 0x80, 0x9, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x2, 0x7, 0xd, 0x16, 0x1d, 0x22, 0x24, 0x1f, 0x19, 0x11, 0xa, 0x4, 0x1b, 0x2d, 0x3a, 0x43, 0x48, 0x47, 0x46, 0x3f, 0x34, 0x25, 0x15, 0x49, 0xe6, 0xef, 0x77, 0xe6, 0xef, 0xe7, 0xef, 0x4a, 0xe4, 0xef, 0x77, 0x7e, 0xb9, 0x59, 0x66, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x1, 0x14, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x63, 0x60, 0x40, 0x3, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x40, 0xc0, 0xc6, 0xc1, 0xc9, 0xc5, 0xcd, 0x83, 0x2e, 0xcf, 0xc0, 0xc3, 0xcc, 0xcb, 0xc7, 0x2f, 0x20, 0x28, 0x28, 0x28, 0x24, 0x2c, 0x22, 0x2a, 0x26, 0xce, 0xd, 0x14, 0x92, 0x90, 0x54, 0x51, 0x55, 0x53, 0xd7, 0x80, 0x2, 0x35, 0x15, 0x4d, 0x2d, 0x6d, 0x8, 0xd0, 0x91, 0x92, 0x16, 0xe3, 0x2, 0x2a, 0x90, 0xd1, 0xd5, 0xd5, 0xd3, 0x37, 0x30, 0x84, 0x2, 0x7d, 0x23, 0x63, 0x13, 0x53, 0x28, 0x30, 0x93, 0x15, 0xe5, 0x4, 0x2a, 0xd0, 0x37, 0xb7, 0xd0, 0xb7, 0xb4, 0x52, 0x85, 0x2, 0x4b, 0xb, 0x6b, 0x1b, 0x5b, 0x18, 0xb0, 0x13, 0xe1, 0x0, 0x29, 0xb0, 0x37, 0x36, 0xb4, 0x72, 0x50, 0x83, 0x2, 0x4d, 0x63, 0x6b, 0x47, 0x27, 0x18, 0x70, 0x14, 0x66, 0x3, 0x2a, 0x70, 0x36, 0x37, 0x76, 0x71, 0x75, 0x70, 0x83, 0x2, 0x2b, 0x63, 0x67, 0x77, 0xf, 0x18, 0x70, 0x17, 0x62, 0x7, 0x2a, 0x30, 0x6, 0x2a, 0xb0, 0x42, 0x56, 0xe0, 0xe9, 0x5, 0x3, 0x9e, 0x82, 0x20, 0x5, 0x16, 0xde, 0x3e, 0x48, 0x6e, 0x30, 0xf4, 0xd5, 0xf3, 0xf3, 0x87, 0x1, 0x3f, 0xb0, 0x2, 0xa3, 0x0, 0x5d, 0xe3, 0x40, 0xcb, 0x20, 0x28, 0x8, 0x36, 0x32, 0xa, 0x9, 0x85, 0x81, 0x10, 0xb0, 0x15, 0xf6, 0x61, 0xf6, 0xbe, 0xe1, 0x70, 0x6f, 0x86, 0xdb, 0x47, 0x44, 0x46, 0xc1, 0x40, 0x24, 0xd8, 0x91, 0xe6, 0xd1, 0x31, 0xb1, 0x16, 0xc6, 0xe1, 0x50, 0x60, 0x11, 0x17, 0x9f, 0x90, 0x8, 0x5, 0x49, 0xc9, 0x60, 0x6f, 0xca, 0xa5, 0xa4, 0xc6, 0xc5, 0xfa, 0x58, 0x40, 0x81, 0x6e, 0x5a, 0x7c, 0x7a, 0x6, 0x4, 0x64, 0x66, 0xc9, 0x83, 0x3, 0x4a, 0x41, 0xd1, 0xc8, 0xd9, 0x5, 0xee, 0x6, 0x17, 0x63, 0xe3, 0xec, 0x1c, 0x8, 0xc8, 0x55, 0x52, 0x6, 0x7, 0x35, 0xc1, 0xc8, 0xc2, 0x1f, 0xdd, 0x0, 0xa5, 0xe, 0x59, 0xe5, 0x7f, 0xe9, 0xa4, 0x40, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char overbright_indicator_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, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x1, 0x85, 0x69, 0x43, 0x43, 0x50, 0x49, 0x43, 0x43, 0x20, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x0, 0x0, 0x78, 0x9c, 0x7d, 0x91, 0x3d, 0x48, 0xc3, 0x40, 0x1c, 0xc5, 0x5f, 0x53, 0xa5, 0x2a, 0x2d, 0xe, 0x16, 0x11, 0x75, 0xc8, 0x50, 0x9d, 0x2c, 0x8a, 0x8a, 0x38, 0x6a, 0x15, 0x8a, 0x50, 0x21, 0xd4, 0xa, 0xad, 0x3a, 0x98, 0x5c, 0xfa, 0x21, 0x34, 0x69, 0x48, 0x52, 0x5c, 0x1c, 0x5, 0xd7, 0x82, 0x83, 0x1f, 0x8b, 0x55, 0x7, 0x17, 0x67, 0x5d, 0x1d, 0x5c, 0x5, 0x41, 0xf0, 0x3, 0xc4, 0xc5, 0xd5, 0x49, 0xd1, 0x45, 0x4a, 0xfc, 0x5f, 0x5a, 0x68, 0x11, 0xe3, 0xc1, 0x71, 0x3f, 0xde, 0xdd, 0x7b, 0xdc, 0xbd, 0x3, 0x84, 0x6a, 0x91, 0x69, 0x56, 0xdb, 0x18, 0xa0, 0xe9, 0xb6, 0x99, 0x8c, 0xc7, 0xc4, 0x74, 0x66, 0x45, 0xc, 0xbc, 0xa2, 0x13, 0x3, 0x8, 0xa1, 0x17, 0xa3, 0x32, 0xb3, 0x8c, 0x59, 0x49, 0x4a, 0xc0, 0x73, 0x7c, 0xdd, 0xc3, 0xc7, 0xd7, 0xbb, 0x28, 0xcf, 0xf2, 0x3e, 0xf7, 0xe7, 0x8, 0xa9, 0x59, 0x8b, 0x1, 0x3e, 0x91, 0x78, 0x86, 0x19, 0xa6, 0x4d, 0xbc, 0x4e, 0x3c, 0xb5, 0x69, 0x1b, 0x9c, 0xf7, 0x89, 0xc3, 0xac, 0x20, 0xab, 0xc4, 0xe7, 0xc4, 0x23, 0x26, 0x5d, 0x90, 0xf8, 0x91, 0xeb, 0x4a, 0x9d, 0xdf, 0x38, 0xe7, 0x5d, 0x16, 0x78, 0x66, 0xd8, 0x4c, 0x25, 0xe7, 0x88, 0xc3, 0xc4, 0x62, 0xbe, 0x85, 0x95, 0x16, 0x66, 0x5, 0x53, 0x23, 0x9e, 0x24, 0x8e, 0xa8, 0x9a, 0x4e, 0xf9, 0x42, 0xba, 0xce, 0x2a, 0xe7, 0x2d, 0xce, 0x5a, 0xb1, 0xcc, 0x1a, 0xf7, 0xe4, 0x2f, 0xc, 0x66, 0xf5, 0xe5, 0x25, 0xae, 0xd3, 0x1c, 0x44, 0x1c, 0xb, 0x58, 0x84, 0x4, 0x11, 0xa, 0xca, 0xd8, 0x40, 0x11, 0x36, 0xa2, 0xb4, 0xea, 0xa4, 0x58, 0x48, 0xd2, 0x7e, 0xcc, 0xc3, 0xdf, 0xef, 0xfa, 0x25, 0x72, 0x29, 0xe4, 0xda, 0x0, 0x23, 0xc7, 0x3c, 0x4a, 0xd0, 0x20, 0xbb, 0x7e, 0xf0, 0x3f, 0xf8, 0xdd, 0xad, 0x95, 0x9b, 0x18, 0xaf, 0x27, 0x5, 0x63, 0x40, 0xfb, 0x8b, 0xe3, 0x7c, 0xc, 0x1, 0x81, 0x5d, 0xa0, 0x56, 0x71, 0x9c, 0xef, 0x63, 0xc7, 0xa9, 0x9d, 0x0, 0xfe, 0x67, 0xe0, 0x4a, 0x6f, 0xfa, 0x4b, 0x55, 0x60, 0xfa, 0x93, 0xf4, 0x4a, 0x53, 0x8b, 0x1c, 0x1, 0xdd, 0xdb, 0xc0, 0xc5, 0x75, 0x53, 0x53, 0xf6, 0x80, 0xcb, 0x1d, 0xa0, 0xef, 0xc9, 0x90, 0x4d, 0xd9, 0x95, 0xfc, 0x34, 0x85, 0x5c, 0xe, 0x78, 0x3f, 0xa3, 0x6f, 0xca, 0x0, 0x3d, 0xb7, 0x40, 0xd7, 0x6a, 0xbd, 0xb7, 0xc6, 0x3e, 0x4e, 0x1f, 0x80, 0x14, 0x75, 0x95, 0xb8, 0x1, 0xe, 0xe, 0x81, 0xe1, 0x3c, 0x65, 0xaf, 0x79, 0xbc, 0xbb, 0xa3, 0xb5, 0xb7, 0x7f, 0xcf, 0x34, 0xfa, 0xfb, 0x1, 0x8e, 0x80, 0x72, 0xb2, 0xed, 0x78, 0xfa, 0x7b, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc4, 0x0, 0x0, 0xe, 0xc4, 0x1, 0x95, 0x2b, 0xe, 0x1b, 0x0, 0x0, 0x0, 0x15, 0x50, 0x4c, 0x54, 0x45, 0xff, 0xff, 0xff, 0x63, 0x63, 0x66, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x4c, 0x39, 0x3a, 0xe, 0x0, 0x0, 0x0, 0x6, 0x74, 0x52, 0x4e, 0x53, 0xff, 0xff, 0xff, 0x7f, 0x0, 0x80, 0x2c, 0x16, 0xc1, 0x6d, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x6, 0x61, 0x66, 0xb8, 0x7d, 0x0, 0x0, 0x0, 0x32, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x62, 0x0, 0x1, 0x46, 0x65, 0x17, 0x17, 0x30, 0x43, 0xc8, 0x4, 0x50, 0x88, 0x1c, 0x52, 0x1, 0x0, 0x2, 0x40, 0x14, 0xbb, 0x70, 0x8b, 0x40, 0xff, 0x2c, 0x18, 0xbe, 0xc6, 0xed, 0x8d, 0x42, 0xa1, 0x50, 0x28, 0x14, 0xa, 0x85, 0xbd, 0xb0, 0x13, 0xfc, 0x71, 0x1, 0xca, 0xf, 0x19, 0x62, 0x24, 0xd6, 0x8, 0xaa, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
@@ -234,6 +282,10 @@ static const unsigned char panel_bg_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x1, 0x3, 0x0, 0x0, 0x0, 0xfe, 0xc1, 0x2c, 0xc8, 0x0, 0x0, 0x0, 0x6, 0x50, 0x4c, 0x54, 0x45, 0x25, 0x25, 0x2a, 0x35, 0x32, 0x3b, 0x4a, 0x73, 0x58, 0x4a, 0x0, 0x0, 0x0, 0xa, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x40, 0x3, 0x0, 0x0, 0x10, 0x0, 0x1, 0xb3, 0xac, 0xe2, 0xd0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char picker_cursor_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 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, 0x0, 0xb9, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0x6d, 0x8f, 0x3d, 0x8a, 0xc2, 0x50, 0x18, 0x45, 0xcf, 0x6b, 0x92, 0x2a, 0x19, 0xd4, 0xa4, 0x72, 0x47, 0x3, 0x42, 0xc0, 0x9f, 0x55, 0x44, 0x17, 0x24, 0x88, 0xee, 0x24, 0x53, 0x4d, 0x7e, 0xa, 0xbf, 0x94, 0xd6, 0x71, 0x5, 0xf2, 0x5e, 0x7f, 0x2d, 0xa2, 0xa2, 0xe0, 0x29, 0xef, 0xb9, 0xcd, 0x1, 0x40, 0xb1, 0x76, 0x6a, 0x14, 0x14, 0xd4, 0x68, 0xab, 0x98, 0x11, 0xcd, 0xd5, 0xef, 0x9b, 0xac, 0x27, 0x10, 0x32, 0x3b, 0xb4, 0x32, 0xcd, 0xc7, 0x77, 0xff, 0xfb, 0xc7, 0xc0, 0x92, 0x84, 0x84, 0x82, 0xcb, 0xa2, 0x92, 0x29, 0x46, 0xbb, 0x7d, 0xc3, 0xc0, 0x94, 0x27, 0x13, 0x86, 0x63, 0xa7, 0x12, 0xb5, 0x59, 0xcf, 0x8a, 0x77, 0xd6, 0xb9, 0xa9, 0x46, 0xde, 0x5, 0x92, 0xf, 0x91, 0x3a, 0x2f, 0xff, 0x4d, 0xfc, 0x38, 0xaf, 0x1b, 0x6a, 0x33, 0xa3, 0xf8, 0x10, 0x9b, 0xfc, 0xac, 0x1a, 0x6d, 0xf, 0x2d, 0x17, 0x26, 0xaf, 0x79, 0xc6, 0xf5, 0xd4, 0xa9, 0x44, 0xb1, 0x6c, 0x51, 0x31, 0xb0, 0x26, 0x25, 0x65, 0xc3, 0xb5, 0xa8, 0x64, 0x8a, 0xc6, 0x40, 0x3b, 0x76, 0xb9, 0xb9, 0xe0, 0x42, 0x7e, 0x3e, 0x75, 0x8f, 0x40, 0x0, 0x45, 0x2a, 0x55, 0xcb, 0xcb, 0xeb, 0x5f, 0xa5, 0x22, 0x80, 0x3b, 0xa0, 0x2c, 0x6c, 0xa1, 0x40, 0x2f, 0xda, 0xfc, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char popup_bg_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, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0xa2, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3b, 0x3b, 0x43, 0x42, 0x42, 0x4b, 0x3e, 0x3e, 0x47, 0x3e, 0x3e, 0x46, 0x41, 0x41, 0x4a, 0x0, 0x0, 0x0, 0x3d, 0x3d, 0x45, 0x3b, 0x3b, 0x43, 0x3a, 0x3a, 0x42, 0x38, 0x38, 0x41, 0x37, 0x37, 0x3e, 0x36, 0x36, 0x3d, 0x35, 0x35, 0x3c, 0x0, 0x0, 0x0, 0x38, 0x38, 0x40, 0x38, 0x38, 0x40, 0x31, 0x31, 0x38, 0x34, 0x34, 0x3b, 0x34, 0x34, 0x3b, 0x39, 0x39, 0x3f, 0x31, 0x31, 0x38, 0x2f, 0x2f, 0x36, 0x2d, 0x2d, 0x33, 0x2c, 0x2c, 0x32, 0x2b, 0x2b, 0x31, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x29, 0x29, 0x30, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x28, 0x28, 0x2d, 0x27, 0x27, 0x2d, 0x27, 0x27, 0x2c, 0x29, 0x29, 0x2e, 0x26, 0x26, 0x2c, 0x36, 0xc6, 0xc8, 0x93, 0x0, 0x0, 0x0, 0x28, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x3, 0x5, 0x8, 0xa, 0xb, 0x4, 0x13, 0x19, 0x1f, 0x22, 0x23, 0x16, 0x27, 0x35, 0x3f, 0x45, 0x46, 0x94, 0xf5, 0xfa, 0xfb, 0xf5, 0x40, 0xfc, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0x1a, 0xf5, 0xf6, 0x95, 0xfa, 0xfb, 0xf4, 0x94, 0x71, 0xda, 0xac, 0x92, 0x0, 0x0, 0x0, 0x7f, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x65, 0x8f, 0x35, 0x82, 0xc3, 0x0, 0xc, 0x4, 0x77, 0x24, 0x85, 0xba, 0xe3, 0xff, 0xff, 0xee, 0xca, 0x74, 0x41, 0xdb, 0x32, 0xf3, 0x94, 0x82, 0x85, 0x10, 0x1d, 0x92, 0xb2, 0x3, 0x8e, 0x95, 0x77, 0x93, 0x6c, 0x28, 0xed, 0x15, 0x54, 0x67, 0xa6, 0x41, 0x3e, 0x8, 0x9c, 0xc3, 0xf4, 0xf2, 0xf6, 0x2a, 0x80, 0xf8, 0x44, 0x2d, 0x79, 0x2d, 0x20, 0xe0, 0x2, 0xa8, 0xc3, 0x2e, 0x6f, 0xc, 0x9e, 0x4c, 0x3c, 0x21, 0x4, 0xd8, 0xf0, 0x2, 0x28, 0x24, 0xcd, 0x3, 0xa9, 0x19, 0x64, 0xce, 0x83, 0x4c, 0x45, 0xe6, 0x69, 0x1a, 0xd8, 0xe9, 0x99, 0x96, 0x7f, 0x77, 0x37, 0x59, 0x83, 0xcc, 0xef, 0x7f, 0x89, 0x1f, 0x8e, 0xbf, 0x95, 0xd3, 0x1d, 0xf0, 0xff, 0x7a, 0x63, 0x7e, 0x86, 0xcb, 0x73, 0x8c, 0x5e, 0xee, 0xca, 0xb1, 0xad, 0x5f, 0x3, 0xaf, 0xdb, 0x49, 0x94, 0x4b, 0x90, 0x40, 0xdf, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
@@ -243,7 +295,7 @@ static const unsigned char popup_bg_disabled_png[] = {
};
static const unsigned char popup_window_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x0, 0x0, 0x46, 0x8, 0x3, 0x0, 0x0, 0x0, 0x8d, 0x2b, 0xf6, 0x48, 0x0, 0x0, 0x1, 0x6b, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xe8, 0xe5, 0xf1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x1d, 0x22, 0x0, 0x0, 0x0, 0x1a, 0x19, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x1e, 0x23, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x1d, 0x21, 0x17, 0x16, 0x19, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x21, 0x1f, 0x24, 0x1b, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x21, 0x1f, 0x24, 0x1e, 0x1c, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x22, 0x20, 0x25, 0x20, 0x1e, 0x23, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x21, 0x1f, 0x24, 0x0, 0x0, 0x0, 0x21, 0x1f, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x22, 0x20, 0x25, 0x0, 0x0, 0x0, 0x20, 0x20, 0x25, 0x20, 0x1d, 0x25, 0x20, 0x1d, 0x22, 0x1d, 0x1d, 0x22, 0x1d, 0x1d, 0x20, 0x1d, 0x1a, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x30, 0x38, 0xe8, 0xe5, 0xf1, 0xe5, 0xe2, 0xeb, 0xe3, 0xe1, 0xe8, 0xe1, 0xdf, 0xe7, 0xe0, 0xde, 0xe6, 0xdf, 0xdd, 0xe5, 0xde, 0xdc, 0xe4, 0xdd, 0xdb, 0xe3, 0xdc, 0xda, 0xe2, 0xda, 0xd8, 0xe0, 0xd9, 0xd7, 0xdf, 0xd7, 0xd6, 0xdf, 0xd6, 0xd4, 0xdd, 0xd5, 0xd3, 0xdc, 0xd4, 0xd1, 0xdb, 0xd3, 0xd0, 0xda, 0xd1, 0xce, 0xd8, 0xd0, 0xcd, 0xd7, 0xcf, 0xcd, 0xd7, 0xe2, 0xdf, 0xeb, 0x48, 0x46, 0x51, 0x42, 0x40, 0x4b, 0x40, 0x3e, 0x48, 0x40, 0x3d, 0x48, 0x48, 0x45, 0x50, 0x42, 0x3f, 0x4a, 0x3f, 0x3d, 0x48, 0x47, 0x44, 0x50, 0x41, 0x3f, 0x4a, 0x3f, 0x3d, 0x47, 0x41, 0x3e, 0x49, 0x3f, 0x3c, 0x47, 0x46, 0x43, 0x4f, 0x3e, 0x3c, 0x46, 0x40, 0x3e, 0x49, 0x3d, 0x3b, 0x46, 0x45, 0x43, 0x4e, 0x3d, 0x3b, 0x45, 0x44, 0x42, 0x4d, 0x3d, 0x3a, 0x45, 0x3e, 0x3c, 0x47, 0x3c, 0x3a, 0x44, 0x43, 0x42, 0x4c, 0x43, 0x40, 0x4c, 0x3e, 0x3b, 0x46, 0x3b, 0x39, 0x43, 0x43, 0x3f, 0x4c, 0x43, 0x3f, 0x4b, 0x3a, 0x38, 0x42, 0x42, 0x3e, 0x4b, 0x42, 0x3e, 0x49, 0x3a, 0x37, 0x41, 0x39, 0x37, 0x41, 0x3f, 0x3e, 0x48, 0x39, 0x37, 0x40, 0x38, 0x36, 0x40, 0x3e, 0x3d, 0x48, 0x38, 0x36, 0x3f, 0x3e, 0x3d, 0x47, 0x3a, 0x38, 0x41, 0x38, 0x35, 0x3f, 0x37, 0x35, 0x3e, 0x39, 0x36, 0x40, 0x37, 0x34, 0x3e, 0x3d, 0x3a, 0x46, 0x36, 0x34, 0x3d, 0x3d, 0x3a, 0x44, 0x37, 0x35, 0x3f, 0x35, 0x33, 0x3c, 0x46, 0x44, 0x4f, 0xac, 0xa5, 0x1, 0x25, 0x0, 0x0, 0x0, 0x33, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xa2, 0x3, 0x9, 0x17, 0xc, 0x20, 0xf, 0x2a, 0x5e, 0x12, 0x30, 0x68, 0x46, 0x20, 0x4e, 0xa2, 0x7d, 0x3a, 0x4f, 0xa4, 0x7d, 0x3f, 0x25, 0x60, 0xc0, 0xb8, 0x57, 0x1d, 0xba, 0x59, 0xbd, 0x5b, 0x22, 0xbf, 0x5e, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xa1, 0x9f, 0x9e, 0x52, 0x92, 0x15, 0x44, 0x7e, 0xd8, 0x5, 0xc7, 0xf4, 0xac, 0x0, 0x0, 0x1, 0x98, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xed, 0xd6, 0x55, 0x9e, 0x14, 0x31, 0x10, 0x80, 0xf1, 0xb2, 0x20, 0x1d, 0xdc, 0x9d, 0x3b, 0x2c, 0xa7, 0x87, 0x4b, 0xe0, 0xee, 0xd0, 0x82, 0xcb, 0xea, 0xb4, 0x86, 0x79, 0x23, 0x93, 0xaa, 0xcc, 0xf, 0xd7, 0xfd, 0x9e, 0xff, 0xed, 0x1d, 0x21, 0xf8, 0xe2, 0xfe, 0x1c, 0x8a, 0x17, 0x32, 0xa1, 0xa2, 0x2b, 0x48, 0x66, 0xb8, 0xa2, 0x28, 0x10, 0x9a, 0xd1, 0xf7, 0x3d, 0x16, 0x66, 0xfa, 0x45, 0x74, 0x9b, 0xd2, 0x97, 0x52, 0xe2, 0x2f, 0xa6, 0x40, 0x5f, 0x4c, 0x1d, 0xf3, 0x97, 0x52, 0x5e, 0x46, 0xf7, 0xee, 0xe3, 0x88, 0x8a, 0x48, 0x9e, 0x8a, 0xec, 0x8c, 0x28, 0x2c, 0xa7, 0xf0, 0x99, 0xd2, 0x6e, 0xe7, 0x8e, 0x10, 0x9b, 0xd1, 0x11, 0xe7, 0xe, 0xd1, 0x17, 0x8e, 0x2, 0xe6, 0x55, 0xf8, 0x42, 0x4a, 0x74, 0x18, 0xbe, 0xf8, 0xac, 0x9b, 0x5f, 0x4a, 0xb7, 0x36, 0x20, 0xa5, 0xf9, 0x2f, 0x90, 0x50, 0x11, 0x36, 0x13, 0x49, 0xa9, 0xe7, 0x6c, 0x5e, 0xcf, 0x2e, 0x99, 0xf4, 0xd, 0xb8, 0x8c, 0x5, 0xa7, 0x28, 0x8, 0x98, 0x9, 0x68, 0xba, 0x41, 0x66, 0x1b, 0x8a, 0xa2, 0xb0, 0x4d, 0x59, 0x30, 0xa5, 0x94, 0xa3, 0x94, 0x52, 0x0, 0x6f, 0x53, 0x6f, 0x7c, 0x82, 0x16, 0xcc, 0x5a, 0x51, 0x14, 0xbd, 0x98, 0x79, 0x4c, 0x29, 0x72, 0xee, 0xac, 0x8c, 0xea, 0xac, 0xb9, 0x51, 0xa0, 0xce, 0xa, 0x44, 0x60, 0x46, 0xa4, 0x28, 0x2, 0x9b, 0x1, 0x2a, 0x5a, 0xa, 0x9a, 0x49, 0xa9, 0xe8, 0x79, 0x20, 0x33, 0x38, 0xaf, 0x68, 0xc5, 0x68, 0xc6, 0x95, 0xa2, 0xe7, 0x72, 0x67, 0x3d, 0x97, 0x52, 0x24, 0xcf, 0x66, 0x9e, 0x30, 0xa5, 0xef, 0x5a, 0x34, 0x6b, 0xdf, 0xa5, 0x14, 0xa4, 0x0, 0xb3, 0x42, 0x8c, 0xe5, 0x38, 0x37, 0xb4, 0x14, 0x3d, 0xd2, 0xda, 0xb4, 0x3d, 0xa2, 0x68, 0xe3, 0xc0, 0xcc, 0x35, 0x8a, 0x9e, 0x86, 0x4c, 0xa7, 0x15, 0x85, 0x9d, 0x6c, 0xb6, 0x13, 0x16, 0xe8, 0x54, 0x78, 0xbc, 0x8e, 0x60, 0x86, 0xd7, 0xd1, 0x17, 0xd3, 0x37, 0x6e, 0x48, 0x30, 0x4f, 0x70, 0x81, 0xbe, 0xed, 0x97, 0xd1, 0xfe, 0x6d, 0x44, 0xf7, 0xed, 0xa7, 0x63, 0x39, 0x79, 0x8c, 0xf6, 0xef, 0x8b, 0xe8, 0x61, 0xe6, 0x8d, 0x55, 0x5b, 0xae, 0x9e, 0x62, 0x3e, 0x1c, 0xd1, 0x3b, 0xf7, 0x9b, 0xc7, 0xfb, 0x9b, 0xab, 0x46, 0xcd, 0xab, 0x4b, 0xcd, 0xfd, 0x3b, 0x11, 0x1d, 0xe, 0x76, 0xf5, 0xc5, 0x2b, 0xe5, 0xf3, 0x67, 0x49, 0xcf, 0xcb, 0x2b, 0x8f, 0xea, 0xee, 0xe0, 0x10, 0xd1, 0xf0, 0xb2, 0x58, 0xb, 0x61, 0x75, 0xd6, 0x26, 0xcd, 0x56, 0x43, 0x58, 0x2b, 0x5e, 0x86, 0x88, 0x4e, 0x63, 0x33, 0x84, 0xf7, 0x1f, 0x26, 0xd5, 0x87, 0xf7, 0x61, 0x68, 0xc6, 0x29, 0xa2, 0x73, 0xdb, 0x5e, 0x2d, 0x57, 0x1f, 0xab, 0x56, 0xcb, 0xab, 0xed, 0x5c, 0xfe, 0xc4, 0x5d, 0xf1, 0x27, 0x1a, 0x8f, 0xba, 0x8d, 0xd7, 0xa0, 0x9a, 0x40, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x0, 0x0, 0x48, 0x8, 0x3, 0x0, 0x0, 0x0, 0xb7, 0x21, 0x97, 0x38, 0x0, 0x0, 0x1, 0x71, 0x69, 0x43, 0x43, 0x50, 0x69, 0x63, 0x63, 0x0, 0x0, 0x28, 0x91, 0x75, 0x91, 0x3d, 0x4b, 0xc3, 0x50, 0x14, 0x86, 0xdf, 0xb6, 0x96, 0x8a, 0xad, 0x74, 0xd0, 0x41, 0xa4, 0x43, 0x86, 0x2a, 0xe, 0x2d, 0x14, 0x5, 0x71, 0xd4, 0x3a, 0x74, 0x29, 0x52, 0x6a, 0x5, 0xab, 0x2e, 0xc9, 0x6d, 0xd2, 0xa, 0x49, 0x1a, 0x6e, 0x52, 0xa4, 0xb8, 0xa, 0x2e, 0xe, 0x5, 0x7, 0xd1, 0xc5, 0xaf, 0xc1, 0x7f, 0xa0, 0xab, 0xe0, 0xaa, 0x20, 0x8, 0x8a, 0x20, 0xe2, 0xe2, 0x1f, 0xf0, 0x6b, 0x91, 0x12, 0xcf, 0x6d, 0xa, 0x2d, 0xd2, 0x9e, 0x70, 0x73, 0x1e, 0xde, 0x7b, 0xde, 0xc3, 0xbd, 0xe7, 0x2, 0xfe, 0xac, 0xce, 0xc, 0x7b, 0x20, 0x5, 0x18, 0xa6, 0xc3, 0xf3, 0x99, 0xb4, 0xb4, 0x5a, 0x5c, 0x93, 0x42, 0xef, 0x8, 0x21, 0x6, 0x1f, 0x82, 0x8, 0xcb, 0xcc, 0xb6, 0x16, 0x72, 0xb9, 0x2c, 0xfa, 0xc6, 0xcf, 0x23, 0x55, 0x52, 0x3c, 0x24, 0x45, 0xaf, 0xfe, 0x75, 0x3d, 0x23, 0x5c, 0x52, 0x6d, 0x6, 0xf8, 0x6, 0x89, 0x67, 0x99, 0xc5, 0x1d, 0xe2, 0x79, 0xe2, 0xec, 0x96, 0x63, 0x9, 0xde, 0x23, 0x1e, 0x65, 0x15, 0xb9, 0x44, 0x7c, 0x42, 0x9c, 0xe0, 0x74, 0x40, 0xe2, 0x5b, 0xa1, 0x2b, 0x1e, 0xbf, 0x9, 0x2e, 0x7b, 0xfc, 0x25, 0x98, 0x17, 0xf2, 0x8b, 0x80, 0x5f, 0xf4, 0x94, 0xca, 0x5d, 0xac, 0x74, 0x31, 0xab, 0x70, 0x83, 0x78, 0x8a, 0x38, 0x6e, 0xe8, 0x35, 0xd6, 0x3e, 0x8f, 0xb8, 0x49, 0x44, 0x35, 0x57, 0x96, 0x29, 0x8f, 0xd3, 0x8a, 0xc1, 0x46, 0x1e, 0x19, 0xa4, 0x21, 0x41, 0x41, 0xd, 0x9b, 0xd0, 0xe1, 0x20, 0x49, 0xd9, 0xa4, 0x99, 0xf5, 0xf6, 0xa5, 0x5a, 0xbe, 0x25, 0x54, 0xc9, 0xc3, 0xe8, 0x6f, 0xa1, 0xe, 0x4e, 0x8e, 0x32, 0x2a, 0xe4, 0x4d, 0x90, 0x5a, 0xa3, 0xae, 0x2a, 0x65, 0x8d, 0x74, 0x95, 0x3e, 0x1d, 0x75, 0x31, 0xf7, 0xff, 0xf3, 0xb4, 0xb5, 0x99, 0x69, 0xaf, 0x7b, 0x24, 0xd, 0x4, 0x5f, 0x5d, 0xf7, 0x73, 0x2, 0x8, 0xed, 0x3, 0xcd, 0x86, 0xeb, 0xfe, 0x9e, 0xba, 0x6e, 0xf3, 0xc, 0x8, 0xbc, 0x0, 0xd7, 0x66, 0xc7, 0x5f, 0xa5, 0x39, 0xcd, 0x7d, 0x93, 0xde, 0xe8, 0x68, 0xf1, 0x63, 0x20, 0xba, 0x3, 0x5c, 0xde, 0x74, 0x34, 0xe5, 0x0, 0xb8, 0xda, 0x5, 0xc6, 0x9e, 0x2d, 0x99, 0xcb, 0x2d, 0x29, 0x40, 0xcb, 0xaf, 0x69, 0xc0, 0xc7, 0x5, 0x30, 0x5c, 0x4, 0x46, 0xee, 0x81, 0xa1, 0x75, 0x6f, 0x56, 0xed, 0x7d, 0x9c, 0x3f, 0x1, 0x85, 0x6d, 0x7a, 0xa2, 0x3b, 0xe0, 0xf0, 0x8, 0x98, 0xa4, 0xfa, 0xe8, 0xc6, 0x1f, 0x7b, 0xde, 0x67, 0xcd, 0xda, 0x7, 0x3a, 0xfb, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x12, 0x0, 0x0, 0xb, 0x12, 0x1, 0xd2, 0xdd, 0x7e, 0xfc, 0x0, 0x0, 0x0, 0xa8, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe8, 0xe5, 0xf1, 0x20, 0x1e, 0x23, 0x21, 0x1f, 0x24, 0x21, 0x1f, 0x24, 0x22, 0x20, 0x25, 0x1d, 0x1a, 0x20, 0x1d, 0x1d, 0x20, 0x1d, 0x1d, 0x22, 0x20, 0x1d, 0x22, 0x20, 0x1d, 0x25, 0x20, 0x20, 0x25, 0x22, 0x20, 0x25, 0xcf, 0xcd, 0xd7, 0xd0, 0xcd, 0xd7, 0xd1, 0xce, 0xd8, 0xd3, 0xd0, 0xda, 0xd4, 0xd1, 0xdb, 0xd5, 0xd3, 0xdc, 0xd6, 0xd4, 0xdd, 0xd7, 0xd6, 0xdf, 0xd9, 0xd7, 0xdf, 0xda, 0xd8, 0xe0, 0xdc, 0xda, 0xe2, 0xdd, 0xdb, 0xe3, 0xde, 0xdc, 0xe4, 0xdf, 0xdd, 0xe5, 0xe0, 0xde, 0xe6, 0xe1, 0xdf, 0xe7, 0xe2, 0xdf, 0xeb, 0xe3, 0xe1, 0xe8, 0xe5, 0xe2, 0xeb, 0xe8, 0xe5, 0xf1, 0xe8, 0x4e, 0x48, 0xd9, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x3, 0x9, 0xc, 0xf, 0x15, 0x1d, 0x20, 0x22, 0x25, 0x30, 0x3a, 0x44, 0x52, 0x57, 0x59, 0x5b, 0x5e, 0x60, 0x7e, 0x92, 0x9e, 0x9f, 0xa1, 0xa2, 0xb8, 0xba, 0xbd, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xf5, 0xb3, 0x97, 0xfb, 0x0, 0x0, 0x0, 0xea, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0xed, 0x96, 0x31, 0xe, 0x83, 0x30, 0xc, 0x45, 0x6d, 0x93, 0xc2, 0xda, 0xb5, 0x13, 0xf7, 0x3f, 0x4a, 0xef, 0xd0, 0xa5, 0xc7, 0x28, 0x10, 0xff, 0xe, 0x4, 0x35, 0x88, 0xc6, 0x25, 0x48, 0x34, 0x2a, 0x6a, 0x16, 0x86, 0x3c, 0x7d, 0xbe, 0xfd, 0x49, 0x30, 0x51, 0xe1, 0xc5, 0xe1, 0xc1, 0xc2, 0xcc, 0xbc, 0xd8, 0x6, 0x0, 0x5, 0x22, 0x94, 0xa5, 0x72, 0xc2, 0x22, 0xb, 0x54, 0x15, 0x3a, 0x78, 0x5, 0x11, 0x91, 0x1b, 0xd1, 0xea, 0xd4, 0xf0, 0x1b, 0x59, 0x0, 0xc0, 0x83, 0x10, 0xa1, 0xe2, 0x9a, 0xba, 0x4d, 0x58, 0xbc, 0x13, 0x54, 0x23, 0x55, 0xe1, 0x36, 0x38, 0x5a, 0x14, 0xd3, 0xde, 0x64, 0x7c, 0x59, 0x40, 0x99, 0xc9, 0x27, 0x54, 0xdd, 0xe4, 0x4b, 0x26, 0x55, 0xa3, 0x49, 0x73, 0x55, 0x61, 0xc2, 0x3a, 0x94, 0xc8, 0x42, 0x27, 0x27, 0xaf, 0xc6, 0x7c, 0x4a, 0xcb, 0x85, 0xaa, 0xea, 0x34, 0x5a, 0xf7, 0xcc, 0xd8, 0xa0, 0x7a, 0x5c, 0x34, 0x23, 0x82, 0x1d, 0xd2, 0xda, 0x11, 0x3d, 0xaf, 0x45, 0xdd, 0x35, 0x89, 0x5c, 0x6, 0x1f, 0x9d, 0x2, 0xfb, 0xaa, 0x98, 0x1d, 0x98, 0x2, 0x68, 0x13, 0x32, 0x90, 0xf5, 0xd7, 0xdb, 0x6, 0xd4, 0x8, 0x0, 0xf4, 0xd, 0x3, 0xc5, 0xcb, 0xfa, 0xa5, 0xe, 0x1c, 0x34, 0xad, 0xff, 0xe7, 0x72, 0x44, 0x14, 0x40, 0x97, 0x46, 0x3a, 0x8c, 0x63, 0xcd, 0x9e, 0x69, 0x41, 0x2d, 0x34, 0x6c, 0xe6, 0xa3, 0xb0, 0x50, 0x64, 0xab, 0x86, 0x3f, 0x82, 0xb3, 0xc6, 0xc7, 0x7e, 0x88, 0x66, 0x42, 0x78, 0x82, 0x31, 0x94, 0x22, 0x77, 0xd4, 0x2d, 0xbe, 0x9e, 0x25, 0x9d, 0x7b, 0xb0, 0x59, 0x6d, 0x9f, 0xd7, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
static const unsigned char progress_bar_png[] = {
@@ -258,10 +310,18 @@ static const unsigned char radio_checked_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, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x42, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4, 0x3, 0x4, 0x9, 0x9, 0x9, 0x6, 0x6, 0x6, 0xa, 0xa, 0xb, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x7f, 0x7f, 0x82, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0x47, 0x47, 0x48, 0xd3, 0xd3, 0xd3, 0xa2, 0xa2, 0xa2, 0x79, 0x79, 0x79, 0x73, 0x73, 0x73, 0x1c, 0x1c, 0x1c, 0x3, 0x3, 0x3, 0x0, 0x0, 0x0, 0xd1, 0xa7, 0xf5, 0xaa, 0x0, 0x0, 0x0, 0xb, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x88, 0xd1, 0xf7, 0x64, 0xf6, 0x2, 0xb3, 0xed, 0xd7, 0x0, 0x0, 0x0, 0x63, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6d, 0x4f, 0x55, 0x2, 0x43, 0x31, 0x8, 0x4b, 0xe8, 0x6a, 0xf7, 0xbf, 0xeb, 0xc, 0x9b, 0x6b, 0x1f, 0x9f, 0xc4, 0x89, 0xbf, 0xbb, 0x3f, 0x2a, 0x49, 0x64, 0xa6, 0x3e, 0x1e, 0x1c, 0x7c, 0x3e, 0xf2, 0x14, 0xb7, 0xc7, 0xac, 0xf1, 0x10, 0xa6, 0xe8, 0x1, 0x44, 0xad, 0x42, 0xb9, 0x33, 0x22, 0x43, 0x95, 0x68, 0x55, 0xa4, 0xdc, 0x1f, 0x1e, 0xa1, 0x67, 0xa2, 0x57, 0x96, 0x22, 0x0, 0xc2, 0x3d, 0xf5, 0x44, 0x8c, 0x8a, 0x5d, 0x21, 0x80, 0x74, 0x83, 0x1e, 0x97, 0xc7, 0x22, 0x59, 0x4c, 0xd7, 0xd8, 0xb5, 0x18, 0x4a, 0x7b, 0x57, 0x57, 0xdb, 0x1a, 0xf7, 0x77, 0x17, 0x3a, 0x56, 0x4e, 0x11, 0x6f, 0x82, 0x20, 0xde, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char radio_checked_disabled_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, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x45, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x3, 0x4, 0x9, 0x9, 0x9, 0x6, 0x6, 0x6, 0xa, 0xa, 0xb, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x7f, 0x7f, 0x82, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0x47, 0x47, 0x48, 0xd3, 0xd3, 0xd3, 0xa2, 0xa2, 0xa2, 0x79, 0x79, 0x79, 0x73, 0x73, 0x73, 0x1c, 0x1c, 0x1c, 0x3, 0x3, 0x3, 0x0, 0x0, 0x0, 0x83, 0xac, 0xe9, 0xaf, 0x0, 0x0, 0x0, 0x1, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x40, 0xe6, 0xd8, 0x66, 0x0, 0x0, 0x0, 0x3f, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0x63, 0x60, 0x20, 0xe, 0xf0, 0x8, 0x2, 0x1, 0xf, 0x9c, 0x2b, 0x2c, 0x8, 0x5, 0xc2, 0x50, 0x1, 0xb0, 0x34, 0x58, 0x11, 0x5c, 0xbd, 0x10, 0x3f, 0x10, 0x8, 0xc1, 0x74, 0x1, 0x65, 0xf8, 0xc1, 0x0, 0xa6, 0x4, 0x28, 0x1, 0x11, 0xe0, 0xc1, 0x2d, 0x80, 0xa6, 0x5, 0xc3, 0x50, 0xc, 0x6b, 0x31, 0x1d, 0x86, 0xe1, 0x74, 0xfc, 0x0, 0x0, 0x1b, 0xc, 0x7, 0xb9, 0xa, 0x5e, 0x2e, 0x12, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char radio_unchecked_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, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x2a, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4, 0x3, 0x4, 0x9, 0x9, 0x9, 0x6, 0x6, 0x6, 0xa, 0xa, 0xb, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x7f, 0x7f, 0x82, 0xd9, 0xd9, 0xd9, 0x47, 0x47, 0x48, 0x2b, 0x6e, 0xf2, 0xbf, 0x0, 0x0, 0x0, 0xb, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x88, 0xd1, 0xf7, 0x64, 0xf6, 0x2, 0xb3, 0xed, 0xd7, 0x0, 0x0, 0x0, 0x49, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x40, 0x2, 0x61, 0x15, 0xed, 0xa9, 0x20, 0x5a, 0x72, 0xf5, 0x99, 0x33, 0xbb, 0x26, 0x1, 0x19, 0x73, 0xcf, 0x0, 0xc1, 0x4d, 0x6, 0x6, 0xd6, 0x35, 0x20, 0xc6, 0xa9, 0x0, 0x6, 0xb6, 0x3d, 0x20, 0xc6, 0xe9, 0x4, 0x6, 0xf6, 0x33, 0x60, 0x50, 0xc0, 0xc0, 0x1, 0x61, 0x34, 0xc0, 0x19, 0x70, 0x29, 0xb8, 0x62, 0xb8, 0x76, 0x84, 0x81, 0xc, 0x96, 0x20, 0x2b, 0xa6, 0xc0, 0x2d, 0x45, 0x0, 0x0, 0x37, 0xca, 0x3d, 0x81, 0xb4, 0x84, 0xb6, 0x80, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char radio_unchecked_disabled_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, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0xf, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x54, 0x54, 0x54, 0xad, 0xad, 0xad, 0x80, 0x80, 0x81, 0x64, 0x64, 0x64, 0xf4, 0x17, 0x36, 0x28, 0x0, 0x0, 0x0, 0x2, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1b, 0xfc, 0xf6, 0x4, 0xd4, 0x0, 0x0, 0x0, 0x40, 0x49, 0x44, 0x41, 0x54, 0x78, 0x5e, 0x7d, 0x8d, 0xb1, 0x11, 0x80, 0x40, 0xc, 0xc3, 0x94, 0xd, 0x12, 0x60, 0x1, 0xc4, 0x4, 0xb0, 0x1, 0xfb, 0x2f, 0x45, 0xe5, 0x3b, 0xaa, 0x57, 0xe5, 0xc2, 0xb6, 0x20, 0xc0, 0xcc, 0xc, 0x40, 0xed, 0x7a, 0x37, 0x70, 0xa8, 0xbe, 0x50, 0x9b, 0xea, 0xd9, 0xd4, 0xa3, 0x7a, 0x35, 0xa5, 0xaa, 0xeb, 0x90, 0x72, 0xe6, 0x39, 0xfc, 0x2b, 0x22, 0xd, 0x1f, 0xe7, 0x64, 0xa, 0x5d, 0x6c, 0xfe, 0xc1, 0x3b, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char scroll_bg_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x3, 0x0, 0x0, 0x0, 0x61, 0xab, 0xac, 0xd5, 0x0, 0x0, 0x0, 0x45, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x40, 0x3e, 0x4a, 0x2a, 0x29, 0x2f, 0x20, 0x20, 0x24, 0x3f, 0x3e, 0x49, 0x1f, 0x1f, 0x24, 0x20, 0x20, 0x24, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x3f, 0x3e, 0x49, 0x3f, 0x3e, 0x49, 0x1e, 0x1e, 0x23, 0x20, 0x20, 0x25, 0x22, 0x22, 0x27, 0x23, 0x23, 0x27, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2a, 0x14, 0xee, 0x69, 0x20, 0x0, 0x0, 0x0, 0x11, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0x19, 0x40, 0x5d, 0x66, 0x28, 0x93, 0xf0, 0xfc, 0x94, 0xfc, 0xfd, 0x67, 0x1a, 0x96, 0x95, 0x1c, 0xf0, 0x43, 0x52, 0x0, 0x0, 0x0, 0x55, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x55, 0x8e, 0x45, 0x2, 0x80, 0x50, 0x10, 0x42, 0xc1, 0xee, 0xfb, 0x5f, 0xd4, 0xd6, 0xdf, 0xfd, 0x36, 0xd3, 0x3, 0x4, 0xd, 0x90, 0x6, 0xb2, 0x25, 0x39, 0xe0, 0xd2, 0xf9, 0xcb, 0x6a, 0x60, 0x6f, 0x27, 0xb7, 0xbc, 0x58, 0xb7, 0x53, 0x4d, 0x0, 0xf2, 0x3f, 0x5e, 0x36, 0x43, 0x5f, 0xc3, 0xf0, 0xdf, 0x17, 0xd7, 0xa6, 0xae, 0x60, 0x10, 0xff, 0x57, 0x16, 0xc5, 0x5a, 0xf1, 0x60, 0xe3, 0xe7, 0x5f, 0x37, 0x46, 0x74, 0xba, 0x9a, 0x16, 0xef, 0x37, 0x1c, 0x6f, 0x61, 0x47, 0x1, 0xa5, 0xc7, 0x32, 0x47, 0x38, 0x12, 0x92, 0xb1, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
@@ -314,6 +374,10 @@ static const unsigned char submenu_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x4, 0x0, 0x0, 0x0, 0x6e, 0x6, 0x76, 0x0, 0x0, 0x0, 0x0, 0x2e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x78, 0xc0, 0xf0, 0xe0, 0x3f, 0x8, 0xde, 0x4f, 0x60, 0x0, 0x3, 0xb8, 0xc0, 0x83, 0x2f, 0xf, 0xb5, 0xe1, 0x2, 0x50, 0x78, 0xf5, 0x5, 0x37, 0xaa, 0xc0, 0xff, 0x87, 0xf3, 0x31, 0x4, 0x30, 0xb5, 0x60, 0x1a, 0x8a, 0x61, 0x2d, 0x0, 0xa6, 0x55, 0x4f, 0xb1, 0x91, 0xd6, 0xa7, 0xae, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char submenu_mirrored_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x4, 0x0, 0x0, 0x0, 0x6e, 0x6, 0x76, 0x0, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x0, 0x57, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x55, 0xcb, 0xa1, 0x11, 0x83, 0x40, 0x14, 0x45, 0xd1, 0xb3, 0x6b, 0x10, 0x34, 0x10, 0x47, 0x34, 0xf4, 0x13, 0x9d, 0x2, 0x28, 0x87, 0x2a, 0xe8, 0x84, 0x2, 0x82, 0x65, 0x71, 0x69, 0x0, 0x11, 0xf5, 0x11, 0x4c, 0x66, 0xd8, 0xe7, 0xee, 0x99, 0x79, 0x80, 0xed, 0x5d, 0xa2, 0x44, 0x9, 0x12, 0xec, 0x43, 0x2c, 0x5a, 0x78, 0xa6, 0xcc, 0xb7, 0x8d, 0xf9, 0x4a, 0xc8, 0xfc, 0x26, 0x3d, 0x37, 0xa8, 0x97, 0x69, 0x46, 0x6b, 0x5, 0x8f, 0x23, 0xbd, 0x1c, 0xd5, 0xa5, 0xfb, 0xc4, 0xf8, 0x87, 0x13, 0xd2, 0x2f, 0x14, 0x49, 0x6f, 0xb1, 0x11, 0xe1, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char tab_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x4, 0x0, 0x0, 0x0, 0x6e, 0x6, 0x76, 0x0, 0x0, 0x0, 0x0, 0x19, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xc0, 0x2, 0xfe, 0x47, 0xfe, 0x17, 0x1, 0xc2, 0x48, 0xd2, 0x84, 0x10, 0x2, 0x84, 0xb9, 0x98, 0x0, 0x0, 0xbf, 0x67, 0x1d, 0x5, 0x89, 0x9b, 0x48, 0x90, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
@@ -354,6 +418,14 @@ static const unsigned char toggle_off_disabled_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x0, 0xfc, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x14, 0x14, 0x17, 0x20, 0x20, 0x25, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x24, 0x24, 0x29, 0x25, 0x25, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x10, 0x13, 0x22, 0x22, 0x27, 0x24, 0x24, 0x28, 0x25, 0x25, 0x28, 0x25, 0x25, 0x29, 0x25, 0x25, 0x27, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x19, 0x19, 0x1c, 0x2b, 0x26, 0x2c, 0x40, 0x40, 0x44, 0x4e, 0x4e, 0x52, 0x1a, 0x1a, 0x1d, 0x32, 0x32, 0x37, 0x2c, 0x26, 0x2c, 0x26, 0x25, 0x2a, 0x27, 0x25, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x2f, 0x26, 0x2d, 0x12, 0x12, 0x14, 0x23, 0x23, 0x27, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x15, 0x15, 0x18, 0x20, 0x20, 0x25, 0x20, 0x20, 0x24, 0x5b, 0x5b, 0x5f, 0x84, 0x84, 0x87, 0x77, 0x77, 0x7a, 0x20, 0x20, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x69, 0x69, 0x6c, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x24, 0x27, 0x15, 0x15, 0x18, 0x23, 0x23, 0x28, 0x12, 0x12, 0x14, 0x0, 0x0, 0x0, 0x1a, 0x1a, 0x1e, 0x0, 0x0, 0x0, 0x11, 0x11, 0x13, 0x22, 0x22, 0x26, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x71, 0xb, 0x1b, 0xbb, 0x0, 0x0, 0x0, 0x54, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x98, 0xe5, 0xfa, 0xfe, 0xff, 0xff, 0x8, 0x17, 0x35, 0x86, 0xf3, 0xff, 0xff, 0xff, 0xff, 0x7, 0x3a, 0xb4, 0xff, 0xff, 0xff, 0xb9, 0xff, 0xff, 0xff, 0xff, 0xb, 0x28, 0x8a, 0xff, 0x8b, 0xf6, 0x45, 0x5, 0x9b, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xe6, 0x37, 0xf, 0xff, 0xfb, 0x4c, 0xfe, 0x4e, 0x4f, 0x50, 0xfb, 0x9c, 0xf6, 0x8c, 0x3b, 0xbb, 0x3c, 0x87, 0xf3, 0x53, 0x14, 0xd4, 0x6d, 0x6c, 0xf9, 0x0, 0x0, 0x2, 0x3, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xdd, 0x55, 0x85, 0x9a, 0xe2, 0x30, 0x18, 0xbc, 0x7a, 0x8b, 0xbb, 0x7b, 0x2, 0xbb, 0x4d, 0x36, 0xb8, 0x3b, 0xeb, 0xae, 0xef, 0xff, 0x2e, 0x47, 0x48, 0x3f, 0xa0, 0x7a, 0xae, 0x83, 0x56, 0xfe, 0xe9, 0xfc, 0xfe, 0xe9, 0x6f, 0x2, 0xc7, 0xb, 0x82, 0xf8, 0x45, 0x8, 0x2, 0xcf, 0x39, 0x9a, 0xf3, 0xa2, 0x24, 0x2b, 0xaa, 0xe6, 0xfb, 0x2, 0x34, 0x55, 0x91, 0x25, 0x91, 0xb7, 0x3f, 0x5d, 0xf4, 0xab, 0x81, 0x60, 0x28, 0x1c, 0x89, 0xc6, 0x3c, 0x11, 0x8d, 0x84, 0x43, 0xc1, 0x80, 0xea, 0x17, 0x2d, 0x2a, 0xf8, 0x78, 0x22, 0x99, 0x4a, 0x67, 0xb2, 0xb9, 0x7c, 0xe1, 0xb, 0xc8, 0xe7, 0xb2, 0x99, 0x74, 0x2a, 0x99, 0x88, 0xf3, 0x26, 0xfb, 0x62, 0xa9, 0x5c, 0xa9, 0xd6, 0x0, 0x80, 0x10, 0xb2, 0xfb, 0x20, 0x5, 0x80, 0x75, 0xe8, 0x48, 0x52, 0xad, 0x94, 0x4b, 0xc5, 0x23, 0x6, 0xae, 0xa1, 0x9d, 0x9c, 0xd6, 0x80, 0x8e, 0xf0, 0x1e, 0x48, 0xdf, 0xb1, 0xd4, 0x81, 0x8b, 0x8e, 0xd3, 0x13, 0xad, 0xc1, 0x1d, 0xfc, 0x57, 0x82, 0x67, 0x35, 0x48, 0x30, 0x6a, 0xb6, 0x76, 0x97, 0x5b, 0x3a, 0x41, 0x98, 0xb4, 0x77, 0x4a, 0xdc, 0x3c, 0x39, 0xb, 0x2a, 0xfb, 0x38, 0xf0, 0x9d, 0x6e, 0xaf, 0x6, 0x11, 0xea, 0x1f, 0xdf, 0x41, 0x10, 0x6a, 0x7b, 0xc6, 0x62, 0xd0, 0xed, 0xf0, 0x6, 0x81, 0x58, 0xa, 0xd, 0x1, 0xa1, 0xa2, 0x8f, 0xa1, 0x23, 0xd2, 0xf2, 0x62, 0x18, 0x8e, 0x4a, 0x63, 0x26, 0x81, 0x93, 0x2, 0x13, 0xa0, 0xe3, 0xbe, 0xf5, 0xe, 0x82, 0x3d, 0x25, 0x14, 0x26, 0x1, 0x89, 0x11, 0xf0, 0x72, 0x70, 0xba, 0x15, 0x60, 0xbf, 0x3, 0x11, 0xe3, 0xcf, 0x6c, 0xbe, 0x58, 0xa2, 0x42, 0x7f, 0xb1, 0xc5, 0xee, 0x8b, 0x9d, 0x5f, 0xad, 0x37, 0xcc, 0x7, 0x41, 0x9, 0x65, 0x21, 0xbd, 0xd9, 0x8a, 0x3d, 0xe9, 0xf9, 0x7c, 0x46, 0x16, 0xb3, 0x3e, 0x35, 0xa4, 0x5f, 0x6, 0x2e, 0x46, 0x8a, 0xc0, 0x8, 0xd4, 0xcb, 0x2b, 0x88, 0x75, 0x3b, 0x81, 0x8e, 0xd, 0x1, 0xd4, 0x68, 0x8e, 0xfa, 0xe7, 0x94, 0xe0, 0x7c, 0x4f, 0x90, 0xbf, 0x56, 0x45, 0x46, 0x50, 0xba, 0xa9, 0x41, 0xec, 0x10, 0xb0, 0x96, 0x41, 0xd0, 0x3f, 0xdf, 0x7e, 0xe1, 0x79, 0xff, 0xfc, 0xfc, 0x9c, 0x6c, 0xbf, 0xf6, 0x14, 0xb7, 0x25, 0x83, 0x40, 0xd, 0xd7, 0x3c, 0x15, 0x90, 0x9d, 0x2, 0xec, 0xae, 0x40, 0x9, 0xdd, 0x1, 0xef, 0x18, 0xa0, 0x2, 0x59, 0xda, 0x5c, 0xd8, 0xc7, 0x80, 0x97, 0xd7, 0x5f, 0xc8, 0x42, 0x7f, 0x79, 0xbe, 0x9c, 0x17, 0x2c, 0x4, 0xfb, 0x2c, 0xd0, 0x3a, 0xb8, 0xff, 0x42, 0x1d, 0x10, 0x5a, 0x95, 0x33, 0x44, 0xd8, 0x97, 0x81, 0xfb, 0xa4, 0xc4, 0xed, 0x2b, 0xf1, 0x1, 0xfe, 0x40, 0x25, 0xd2, 0x5e, 0x18, 0x7c, 0x7b, 0x2f, 0x3c, 0xd2, 0x5e, 0xf8, 0x72, 0x37, 0x16, 0xbe, 0xdc, 0x8d, 0x6c, 0x1e, 0x3c, 0x3d, 0xd7, 0x40, 0xdb, 0x32, 0xf, 0xbc, 0xec, 0x9f, 0x5f, 0xf6, 0xf3, 0x80, 0x4d, 0x24, 0x2d, 0xf8, 0xfa, 0x6, 0xea, 0x80, 0xd, 0x24, 0x68, 0x0, 0x6c, 0x5f, 0x8e, 0xf6, 0xd5, 0xd7, 0xa0, 0x56, 0x34, 0xcf, 0xb4, 0x86, 0x92, 0xc, 0xa5, 0x33, 0x17, 0xf9, 0xc2, 0x17, 0x91, 0xbf, 0xc8, 0xa4, 0x43, 0x49, 0xa5, 0xc1, 0x5b, 0xa7, 0x72, 0x47, 0xd, 0xac, 0x47, 0xd7, 0xef, 0xb1, 0x2f, 0xe0, 0xfd, 0x7a, 0xb4, 0xe, 0xa8, 0x1d, 0x91, 0xb3, 0x6f, 0x95, 0xb1, 0xb4, 0xf9, 0x28, 0x7d, 0x79, 0x2f, 0x94, 0x3e, 0x36, 0xd2, 0x98, 0xe7, 0x5c, 0x36, 0x93, 0xf8, 0x15, 0xa0, 0x9b, 0xe9, 0xff, 0xc2, 0x67, 0x14, 0xf4, 0xa5, 0xb3, 0x35, 0x5e, 0x63, 0x97, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char toggle_off_disabled_mirrored_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x0, 0x9c, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xd, 0xd, 0xf, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x24, 0x24, 0x29, 0x25, 0x25, 0x2a, 0x1d, 0x1d, 0x21, 0x11, 0x11, 0x14, 0x23, 0x23, 0x28, 0x2e, 0x2e, 0x2e, 0x46, 0x46, 0x46, 0x57, 0x57, 0x57, 0x60, 0x60, 0x60, 0x62, 0x62, 0x62, 0x5e, 0x5e, 0x5e, 0x4a, 0x4a, 0x4a, 0x12, 0x12, 0x15, 0x25, 0x27, 0x2d, 0x42, 0x42, 0x42, 0x59, 0x59, 0x59, 0x32, 0x32, 0x32, 0x25, 0x26, 0x2d, 0x25, 0x25, 0x2b, 0x25, 0x26, 0x2c, 0x39, 0x39, 0x39, 0x49, 0x49, 0x49, 0x5a, 0x5a, 0x5a, 0x48, 0x48, 0x48, 0x54, 0x54, 0x54, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x1e, 0x1e, 0x22, 0x25, 0x26, 0x2b, 0x3f, 0x3f, 0x3f, 0xe, 0xe, 0x10, 0x2c, 0x2c, 0x2c, 0x58, 0x58, 0x58, 0x5d, 0x5d, 0x5f, 0x80, 0x80, 0x80, 0x79, 0x79, 0x79, 0x40, 0x40, 0x44, 0x32, 0x32, 0x37, 0x41, 0x41, 0x41, 0x1a, 0x1a, 0x1d, 0x6a, 0x6a, 0x6d, 0x52, 0x52, 0x52, 0x3a, 0x3a, 0x3a, 0x5b, 0x5b, 0x5b, 0x2f, 0x2f, 0x2f, 0x50, 0x50, 0x51, 0x13, 0x13, 0x15, 0x2b, 0xcd, 0x4, 0x96, 0x0, 0x0, 0x0, 0x1, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x40, 0xe6, 0xd8, 0x66, 0x0, 0x0, 0x1, 0x29, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0xed, 0x94, 0xdb, 0x76, 0x83, 0x20, 0x10, 0x45, 0x8d, 0xe8, 0x24, 0x98, 0xda, 0x1a, 0x93, 0xda, 0x5b, 0xac, 0xb9, 0x50, 0x14, 0x52, 0x72, 0xfd, 0xff, 0x7f, 0xab, 0x68, 0x49, 0x28, 0x5a, 0x35, 0xf5, 0xa5, 0xf, 0x39, 0xf, 0xb3, 0x96, 0xe3, 0xda, 0x7, 0x98, 0x81, 0xb1, 0xac, 0x9b, 0xfe, 0x97, 0x6, 0x36, 0x72, 0x5c, 0x68, 0x91, 0xeb, 0x20, 0x7b, 0x50, 0xcf, 0xf, 0x5b, 0xe1, 0xb3, 0xc9, 0xb0, 0x6, 0x1f, 0xe1, 0x46, 0xc6, 0x1b, 0xdf, 0xf9, 0xf7, 0xa5, 0x1e, 0x2, 0xf, 0xf0, 0xc8, 0xe4, 0x27, 0x8d, 0xcb, 0x87, 0xd3, 0xd9, 0xf8, 0x31, 0x7a, 0x92, 0x7a, 0xf6, 0x5e, 0x5e, 0xdf, 0xa6, 0xa1, 0x3b, 0x31, 0xc, 0x1a, 0xd7, 0xf, 0xe7, 0xf1, 0xbb, 0xfe, 0x9d, 0xc4, 0xf3, 0x10, 0xff, 0xe4, 0x17, 0x4d, 0xfc, 0x72, 0x15, 0x7b, 0xc6, 0x81, 0xe2, 0xd5, 0x72, 0xa1, 0xf3, 0xeb, 0xc6, 0xf3, 0x93, 0x8f, 0xc4, 0x4c, 0x25, 0x33, 0x2, 0x6b, 0xcd, 0xc0, 0x56, 0x3f, 0x10, 0x4d, 0x33, 0x6, 0x24, 0xcd, 0x55, 0x4, 0x2e, 0x93, 0x9b, 0xa0, 0x6a, 0x1a, 0x6c, 0xe0, 0x53, 0x33, 0x40, 0x2a, 0x2f, 0x28, 0xe2, 0x29, 0x22, 0x12, 0x24, 0x25, 0x9d, 0x6b, 0xbb, 0xab, 0x1a, 0xec, 0xb6, 0x80, 0x34, 0x3, 0x47, 0x6d, 0x40, 0x42, 0x94, 0x11, 0x21, 0xd, 0x84, 0x32, 0xd8, 0x1f, 0xaa, 0x6, 0x87, 0x3d, 0xe8, 0x65, 0x54, 0x3d, 0x24, 0x22, 0xf, 0x47, 0x4a, 0x84, 0x10, 0xbc, 0x8, 0x45, 0xd6, 0x8f, 0xaa, 0x6, 0x91, 0xf, 0x50, 0xd3, 0x44, 0x5e, 0xec, 0xe0, 0x78, 0xfd, 0xe, 0x2e, 0x35, 0x60, 0xc0, 0x33, 0xf3, 0x8, 0x1d, 0x6a, 0x70, 0xee, 0x2, 0xc9, 0x44, 0x46, 0xc1, 0x30, 0xe8, 0xd0, 0x85, 0xcb, 0x3d, 0xe0, 0x4c, 0xd6, 0x92, 0xf1, 0xef, 0xd0, 0xf5, 0x1e, 0xf4, 0xbe, 0x89, 0xfd, 0xdf, 0x42, 0xff, 0xd7, 0x68, 0x9d, 0xae, 0x9b, 0x7, 0xa7, 0xba, 0x89, 0x4, 0x9d, 0x55, 0x37, 0x91, 0xca, 0x99, 0x88, 0xdb, 0x61, 0xfc, 0xeb, 0x4c, 0xbc, 0xe9, 0xaf, 0xfa, 0x2, 0xdc, 0x1a, 0x30, 0x60, 0x4e, 0xef, 0xb8, 0xbb, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
+static const unsigned char toggle_off_mirrored_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x1, 0xaa, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0xd, 0xf, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x24, 0x24, 0x29, 0x24, 0x24, 0x28, 0x20, 0x20, 0x25, 0x14, 0x14, 0x17, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0xa, 0xc, 0x1d, 0x1d, 0x21, 0x22, 0x22, 0x27, 0x10, 0x10, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x23, 0x23, 0x28, 0x19, 0x19, 0x1c, 0x12, 0x12, 0x15, 0x1a, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0xb, 0xd, 0x23, 0x23, 0x28, 0x11, 0x11, 0x14, 0x1e, 0x1e, 0x22, 0x23, 0x23, 0x27, 0xe, 0xe, 0x10, 0x15, 0x15, 0x18, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x25, 0x0, 0x0, 0x0, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x20, 0x20, 0x24, 0x24, 0x24, 0x27, 0x0, 0x0, 0x0, 0xe, 0xe, 0x10, 0x15, 0x15, 0x18, 0x23, 0x23, 0x28, 0xb, 0xb, 0xd, 0x12, 0x12, 0x14, 0x0, 0x0, 0x0, 0x13, 0x13, 0x15, 0x1a, 0x1a, 0x1e, 0xb, 0xb, 0xc, 0x1d, 0x1d, 0x21, 0x22, 0x22, 0x26, 0x11, 0x11, 0x13, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x25, 0x25, 0x2a, 0x24, 0x24, 0x29, 0x25, 0x25, 0x28, 0x25, 0x25, 0x29, 0x24, 0x24, 0x28, 0x2d, 0x26, 0x2c, 0x51, 0x2c, 0x39, 0x6c, 0x31, 0x42, 0x71, 0x32, 0x44, 0x6e, 0x31, 0x43, 0x63, 0x2f, 0x3f, 0x4d, 0x2b, 0x37, 0x27, 0x25, 0x2a, 0x47, 0x2a, 0x35, 0x68, 0x30, 0x40, 0x52, 0x2c, 0x39, 0x3c, 0x28, 0x31, 0x2e, 0x25, 0x2c, 0x26, 0x25, 0x2a, 0x32, 0x26, 0x2e, 0x4d, 0x2b, 0x38, 0x66, 0x30, 0x40, 0x50, 0x2c, 0x38, 0x5e, 0x2e, 0x3d, 0x38, 0x27, 0x30, 0x35, 0x27, 0x2f, 0x5f, 0x2e, 0x3d, 0x44, 0x2a, 0x34, 0x5f, 0x2f, 0x3e, 0x2f, 0x25, 0x2c, 0x43, 0x2a, 0x34, 0x2c, 0x26, 0x2c, 0x66, 0x2f, 0x40, 0x37, 0x27, 0x30, 0x36, 0x27, 0x30, 0x64, 0x2f, 0x3f, 0x2b, 0x26, 0x2c, 0x40, 0x40, 0x44, 0xad, 0xad, 0xaf, 0xff, 0xff, 0xff, 0xf2, 0xf2, 0xf2, 0x77, 0x77, 0x7a, 0x5b, 0x5b, 0x5f, 0x32, 0x32, 0x37, 0x46, 0x2a, 0x35, 0x53, 0x2c, 0x39, 0xc9, 0xc9, 0xca, 0xbb, 0xbb, 0xbd, 0x69, 0x69, 0x6c, 0x5d, 0x2e, 0x3d, 0x3e, 0x29, 0x32, 0x84, 0x84, 0x87, 0xd6, 0xd6, 0xd7, 0x69, 0x30, 0x41, 0x2f, 0x26, 0x2d, 0x92, 0x92, 0x94, 0xa0, 0xa0, 0xa2, 0x4e, 0x4e, 0x52, 0x48, 0x2b, 0x36, 0x2c, 0x26, 0x2b, 0x25, 0x25, 0x27, 0xea, 0xac, 0x78, 0x5d, 0x0, 0x0, 0x0, 0x51, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x80, 0xc5, 0xe7, 0xf5, 0xfe, 0xfa, 0xe5, 0x98, 0x8, 0x17, 0x35, 0x73, 0xd9, 0xf3, 0x86, 0x7, 0x3a, 0x96, 0xf9, 0xb4, 0x9a, 0xb9, 0xb, 0x28, 0x76, 0xfb, 0x8a, 0xde, 0xf6, 0x82, 0x9b, 0xc6, 0xe6, 0x4c, 0xfe, 0x4f, 0xe9, 0xfb, 0x37, 0x83, 0x9c, 0xf6, 0x77, 0x8b, 0x3b, 0x9c, 0xbb, 0x74, 0xda, 0xf3, 0x87, 0xfb, 0x45, 0x4e, 0x53, 0x5, 0xf, 0x14, 0xc7, 0x22, 0x44, 0x61, 0x0, 0x0, 0x2, 0x45, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0xd5, 0x55, 0xe7, 0x5f, 0xd3, 0x40, 0x18, 0x26, 0x3b, 0x91, 0x24, 0x65, 0x95, 0xd, 0xd, 0x8e, 0x22, 0x6a, 0x71, 0x2b, 0x82, 0x75, 0xe1, 0x66, 0xa6, 0x97, 0xa0, 0x56, 0x45, 0xa4, 0x2a, 0x2e, 0xdc, 0x76, 0x25, 0x18, 0x84, 0x6, 0x9c, 0xff, 0xb3, 0xb9, 0x26, 0x17, 0xda, 0xa4, 0x49, 0xd4, 0x4f, 0xfa, 0x7c, 0xb8, 0xdf, 0xe5, 0xcd, 0xef, 0x79, 0xee, 0xbd, 0xf7, 0xde, 0xd1, 0xd4, 0xf4, 0xf, 0x1, 0xc3, 0x9, 0x82, 0x8c, 0x4, 0x41, 0xe0, 0x58, 0x43, 0x3a, 0x4e, 0x52, 0x34, 0xc3, 0x72, 0xbb, 0x22, 0xc0, 0xb1, 0xc, 0x4d, 0x91, 0xb8, 0xff, 0x74, 0xb2, 0x99, 0xe5, 0x5, 0x31, 0xd6, 0xd2, 0xda, 0x16, 0x8a, 0xd6, 0x96, 0x98, 0x28, 0xf0, 0x6c, 0x33, 0xe9, 0xf1, 0x2, 0x6f, 0xef, 0x88, 0x77, 0x76, 0x75, 0xf7, 0xf4, 0xf6, 0xc9, 0x11, 0xc8, 0xf4, 0xf5, 0xf, 0xc, 0x76, 0xc6, 0x3b, 0xda, 0xeb, 0x9c, 0xc0, 0x13, 0xd2, 0xd0, 0xee, 0x3d, 0x99, 0x0, 0xe, 0x50, 0x20, 0xec, 0xbd, 0xb5, 0x1, 0x40, 0xdd, 0xbb, 0x6f, 0x48, 0x4a, 0xd4, 0x28, 0x60, 0x49, 0x6e, 0x78, 0xff, 0x48, 0xe0, 0xa1, 0x90, 0xbb, 0x70, 0xeb, 0xf6, 0x1d, 0x1b, 0xd9, 0xbb, 0xf7, 0x16, 0x80, 0x7a, 0x60, 0x98, 0x4b, 0x62, 0x3b, 0xf7, 0x67, 0x84, 0x83, 0xa1, 0xbe, 0xdf, 0x5f, 0x7c, 0xb0, 0xf4, 0x70, 0x39, 0x57, 0xdd, 0xe7, 0x1e, 0x3d, 0x7e, 0xb2, 0xa8, 0xa8, 0x87, 0x4, 0xc6, 0x8d, 0x3, 0x9e, 0x1a, 0x3d, 0x7c, 0x24, 0x94, 0xbf, 0xf2, 0xf4, 0x59, 0xed, 0xf7, 0xf3, 0x17, 0x2b, 0x8a, 0x7a, 0x74, 0x34, 0x85, 0x2e, 0x41, 0x4a, 0xe2, 0xb1, 0x30, 0x7e, 0xee, 0xe5, 0xea, 0xab, 0x7a, 0xcb, 0xf2, 0xea, 0x6b, 0x70, 0x5c, 0x94, 0x48, 0xe7, 0x6, 0x14, 0x7f, 0x22, 0x34, 0xf0, 0x6f, 0xde, 0xbe, 0xf3, 0x9a, 0xde, 0x7f, 0xf8, 0x8, 0x4e, 0xf2, 0x94, 0x73, 0x3, 0x5a, 0x38, 0x85, 0x7e, 0xe4, 0xb, 0xc5, 0x52, 0x59, 0xd6, 0x8a, 0x16, 0xaa, 0x8b, 0xe, 0x8d, 0x6b, 0x9f, 0xfc, 0xa2, 0x4b, 0x6b, 0xe0, 0xb4, 0x40, 0xdb, 0x2, 0x4, 0x23, 0xf6, 0x20, 0xbb, 0xb1, 0x9e, 0xff, 0x5c, 0xcc, 0x6b, 0x90, 0xa8, 0xd9, 0x6c, 0xb, 0x1b, 0x9b, 0x7e, 0x81, 0xcd, 0xd, 0xa5, 0x5f, 0x64, 0x1c, 0x1, 0x76, 0xac, 0x17, 0x39, 0x0, 0x49, 0xeb, 0x15, 0xcd, 0x84, 0x2, 0x26, 0x12, 0xd8, 0xda, 0xf6, 0xb, 0x6c, 0x6f, 0x29, 0x67, 0xc6, 0x58, 0x47, 0x40, 0x1a, 0x47, 0x6f, 0xa8, 0x99, 0xd6, 0xf2, 0xa5, 0xa0, 0x19, 0x86, 0xa1, 0x57, 0x97, 0xaa, 0x35, 0x9b, 0x6b, 0x10, 0xd8, 0xac, 0xa2, 0x8e, 0x4b, 0xc8, 0x83, 0x18, 0x4a, 0x22, 0x1d, 0x7a, 0x50, 0xf8, 0xaa, 0x41, 0xa6, 0x66, 0x44, 0x78, 0xa0, 0xc6, 0x58, 0x37, 0x6, 0x13, 0xc8, 0x6e, 0x56, 0x64, 0xbd, 0x54, 0xf6, 0x8, 0x34, 0x8e, 0x1, 0x38, 0x8b, 0x62, 0x80, 0xd3, 0x69, 0xf7, 0x15, 0xb4, 0x92, 0x59, 0x2a, 0xc8, 0x1e, 0x81, 0xa0, 0x57, 0x48, 0xd3, 0x6e, 0x1e, 0x9c, 0x73, 0x7f, 0xe8, 0x95, 0x6f, 0x56, 0x2c, 0xcb, 0xba, 0xb3, 0x84, 0xe5, 0xc1, 0x79, 0x94, 0x7, 0x7f, 0x97, 0x89, 0xca, 0x5, 0x37, 0x13, 0x61, 0x2d, 0x5c, 0xfc, 0xf3, 0x5a, 0xb8, 0xb4, 0x53, 0xb, 0xbf, 0x51, 0x8d, 0xdf, 0x43, 0xab, 0x11, 0xf6, 0x83, 0xc9, 0xcb, 0xa1, 0x3e, 0xd4, 0xf7, 0x83, 0x1f, 0x40, 0xbd, 0x32, 0x59, 0xd3, 0xf, 0x60, 0x47, 0xe2, 0x84, 0xab, 0xd7, 0x82, 0xc8, 0x76, 0x47, 0x72, 0x9a, 0x92, 0xd5, 0x91, 0x7e, 0x82, 0xeb, 0x37, 0x4, 0x2e, 0x51, 0xdf, 0xd3, 0x92, 0x4c, 0x5c, 0xec, 0xea, 0x9e, 0x18, 0x91, 0x23, 0x91, 0xb9, 0x39, 0x30, 0x28, 0xc6, 0x99, 0xa4, 0xa7, 0x31, 0x63, 0x64, 0x8a, 0xe5, 0xd3, 0x53, 0xd3, 0x33, 0x6d, 0x11, 0x98, 0x99, 0x9e, 0x4a, 0xf3, 0x6c, 0x8a, 0xf4, 0xcd, 0x6, 0xc, 0x9f, 0xa5, 0xe6, 0xe6, 0xa5, 0xe8, 0xb9, 0x20, 0xcd, 0xcf, 0x51, 0xb3, 0x8d, 0x67, 0x8b, 0x35, 0x99, 0xa2, 0x7, 0x93, 0x35, 0x9a, 0x2, 0x26, 0xd3, 0xff, 0x8b, 0x5f, 0x3d, 0xdc, 0x7c, 0xb4, 0x8c, 0xb2, 0xd8, 0x5f, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char toggle_on_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x1, 0x74, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0xd, 0xf, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x24, 0x24, 0x29, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0xa, 0xc, 0x1d, 0x1d, 0x21, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x23, 0x23, 0x28, 0x12, 0x12, 0x15, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0xb, 0xd, 0x23, 0x23, 0x28, 0xb, 0xb, 0xd, 0x1e, 0x1e, 0x22, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xe, 0x10, 0x1a, 0x1a, 0x1e, 0x1a, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x20, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xe, 0x10, 0xb, 0xb, 0xd, 0x0, 0x0, 0x0, 0x13, 0x13, 0x15, 0x0, 0x0, 0x0, 0xb, 0xb, 0xc, 0x1d, 0x1d, 0x21, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x25, 0x25, 0x2a, 0x24, 0x24, 0x29, 0x25, 0x2c, 0x36, 0x27, 0x49, 0x65, 0x29, 0x5d, 0x85, 0x2a, 0x66, 0x95, 0x2a, 0x68, 0x99, 0x29, 0x64, 0x92, 0x28, 0x4c, 0x6b, 0x25, 0x27, 0x2d, 0x27, 0x43, 0x5c, 0x29, 0x5f, 0x89, 0x27, 0x49, 0x66, 0x25, 0x30, 0x3e, 0x25, 0x26, 0x2d, 0x25, 0x25, 0x2b, 0x25, 0x26, 0x2c, 0x25, 0x2d, 0x38, 0x25, 0x3a, 0x4c, 0x27, 0x4d, 0x6b, 0x29, 0x60, 0x8c, 0x27, 0x44, 0x5c, 0x27, 0x4b, 0x69, 0x28, 0x59, 0x7f, 0x25, 0x34, 0x43, 0x25, 0x35, 0x45, 0x28, 0x58, 0x7f, 0x25, 0x26, 0x2b, 0x27, 0x40, 0x57, 0x27, 0x41, 0x57, 0x25, 0x2a, 0x33, 0x29, 0x5d, 0x87, 0x25, 0x34, 0x44, 0x25, 0x2b, 0x34, 0x40, 0x40, 0x44, 0xad, 0xad, 0xaf, 0xff, 0xff, 0xff, 0xf2, 0xf2, 0xf2, 0x77, 0x77, 0x7a, 0x5b, 0x5b, 0x5f, 0x4e, 0x4e, 0x52, 0xc9, 0xc9, 0xca, 0x27, 0x43, 0x5b, 0x27, 0x4d, 0x6c, 0x27, 0x4e, 0x6d, 0xbb, 0xbb, 0xbd, 0x69, 0x69, 0x6c, 0x28, 0x56, 0x7b, 0x26, 0x3b, 0x4e, 0x26, 0x3a, 0x4e, 0x32, 0x32, 0x37, 0x84, 0x84, 0x87, 0xd6, 0xd6, 0xd7, 0x29, 0x61, 0x8d, 0x25, 0x2e, 0x39, 0x92, 0x92, 0x94, 0xa0, 0xa0, 0xa2, 0xe4, 0xe4, 0xe5, 0x27, 0x44, 0x5d, 0xdd, 0xc9, 0xf2, 0x7e, 0x0, 0x0, 0x0, 0x41, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x80, 0xc5, 0xe7, 0xf5, 0xfe, 0x8, 0x17, 0x35, 0x73, 0xd9, 0x7, 0x3a, 0x96, 0xf9, 0x9a, 0xb, 0x28, 0x76, 0xfb, 0x77, 0xde, 0x45, 0x5, 0x82, 0xc6, 0xc6, 0x37, 0xf, 0xe9, 0x4c, 0x4e, 0x4f, 0x50, 0x83, 0x78, 0x3b, 0x9c, 0x3c, 0x74, 0xda, 0x53, 0x14, 0x37, 0x21, 0x5a, 0x6c, 0x0, 0x0, 0x2, 0x4, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xdd, 0x95, 0x63, 0x83, 0xdc, 0x60, 0x10, 0xc7, 0x1b, 0x3c, 0xc1, 0xda, 0x3e, 0xdb, 0x36, 0xe7, 0x6c, 0xdb, 0xa, 0xcf, 0xc6, 0x7e, 0xf8, 0x2a, 0x6d, 0xb8, 0xac, 0x7b, 0xbf, 0xf7, 0xf3, 0x1f, 0xcf, 0x7c, 0xf8, 0x97, 0xc0, 0x70, 0x82, 0x20, 0xb3, 0x42, 0x10, 0x38, 0x96, 0xd2, 0x1c, 0x27, 0x11, 0x45, 0x33, 0xac, 0x2d, 0xb, 0x2c, 0x43, 0x53, 0x88, 0xc4, 0xad, 0xde, 0x49, 0x3b, 0xe3, 0x70, 0xba, 0xdc, 0x1e, 0xaf, 0x2f, 0x23, 0x5e, 0x8f, 0xdb, 0xe5, 0x74, 0x30, 0x76, 0xd2, 0x14, 0x5, 0xee, 0xf, 0x4, 0x43, 0xe1, 0x48, 0x34, 0x16, 0x87, 0x2c, 0xc4, 0x63, 0xd1, 0x48, 0x38, 0x14, 0xc, 0xf8, 0x71, 0x83, 0x7d, 0xa2, 0xa0, 0xb0, 0xa8, 0x78, 0x4, 0x72, 0x64, 0xa4, 0xb8, 0xa8, 0xb0, 0x20, 0xa1, 0x53, 0xc0, 0x4a, 0xd8, 0xd2, 0xb2, 0x72, 0xc8, 0xc4, 0xe8, 0xd8, 0xf8, 0xc4, 0xa4, 0xc2, 0xd4, 0xf4, 0x28, 0x94, 0x97, 0x95, 0xb2, 0x25, 0x98, 0x96, 0x3f, 0xed, 0xac, 0xc8, 0x18, 0xfb, 0xcc, 0xec, 0xdc, 0xfc, 0xc2, 0xe2, 0xd2, 0x17, 0x96, 0x57, 0x56, 0xd7, 0xd6, 0x37, 0x66, 0xe2, 0x15, 0x4e, 0x5a, 0xad, 0x3, 0x5e, 0x59, 0x55, 0x5d, 0x93, 0xd1, 0x7e, 0x73, 0x6b, 0x1b, 0x74, 0xec, 0xec, 0x6e, 0xce, 0xd4, 0xd4, 0x56, 0x55, 0x7e, 0x4f, 0x82, 0x2c, 0x70, 0xd5, 0x41, 0x6, 0xf6, 0xf6, 0xb7, 0x56, 0xc0, 0xc0, 0xca, 0xd6, 0xc1, 0x5e, 0x5d, 0x7d, 0x41, 0x83, 0x12, 0x2, 0x86, 0x1c, 0x8d, 0x90, 0x89, 0xc3, 0xa3, 0x63, 0x30, 0xb1, 0x33, 0x77, 0x2, 0x8d, 0xe, 0xa4, 0x8, 0xe0, 0x94, 0xb3, 0x9, 0x54, 0x4e, 0xcf, 0x38, 0x5e, 0x0, 0x91, 0x93, 0x40, 0x94, 0x41, 0xe1, 0xfc, 0x2, 0x2c, 0x5c, 0x9e, 0x43, 0x73, 0x4b, 0xab, 0x92, 0x3, 0x41, 0xbb, 0xa2, 0xa0, 0x22, 0x5f, 0x9d, 0x5e, 0x73, 0xa7, 0x22, 0x27, 0x6b, 0x2, 0x37, 0xb7, 0x60, 0xe1, 0xee, 0x6, 0xda, 0xea, 0x69, 0x42, 0x11, 0x60, 0xda, 0x63, 0x5a, 0x0, 0xdc, 0x3d, 0xc0, 0xd5, 0x83, 0xf8, 0xc8, 0x8b, 0xaa, 0xc0, 0xd3, 0x33, 0x58, 0x78, 0x7e, 0x82, 0xf2, 0xe, 0x86, 0x54, 0x4, 0xa, 0x3a, 0xb5, 0x1e, 0x8a, 0x8f, 0x0, 0xf0, 0x72, 0x26, 0xca, 0x2f, 0x8f, 0xaa, 0xc0, 0xc4, 0x22, 0x58, 0x58, 0x9c, 0x0, 0xe8, 0x2a, 0xf8, 0x26, 0xc0, 0xb8, 0xb5, 0x21, 0xba, 0xff, 0x12, 0xc1, 0xd9, 0xeb, 0x67, 0xe3, 0xb7, 0xb3, 0x9c, 0x23, 0xa0, 0x5d, 0x6d, 0xa0, 0xf2, 0xf8, 0x0, 0xf7, 0xbc, 0xf0, 0x59, 0x40, 0xe0, 0x72, 0xad, 0x1, 0x4e, 0xb5, 0xe8, 0xba, 0x20, 0xf2, 0x8f, 0xfc, 0xd9, 0xd7, 0x2, 0x3e, 0xe6, 0xda, 0x5, 0xc, 0x39, 0xba, 0x41, 0xe3, 0xfe, 0x41, 0x2, 0x38, 0x15, 0x0, 0x24, 0x21, 0xf3, 0x1c, 0x74, 0x7, 0x11, 0xf6, 0xd3, 0x93, 0xa8, 0xee, 0x42, 0x6d, 0xfe, 0xbb, 0xd0, 0xa3, 0xed, 0x42, 0xe, 0xdb, 0xb8, 0x61, 0xdc, 0xc6, 0xa4, 0xba, 0x8d, 0xea, 0x3d, 0xe8, 0xed, 0xab, 0xc9, 0xe7, 0x1e, 0xd4, 0xf4, 0xf5, 0xab, 0xf7, 0x40, 0xb9, 0x48, 0xac, 0x73, 0x60, 0x10, 0x72, 0x66, 0x70, 0xc0, 0xc9, 0x26, 0x8c, 0x37, 0xad, 0x84, 0xe, 0xba, 0xc2, 0x91, 0xb6, 0x72, 0xc8, 0x4a, 0x79, 0x5b, 0x24, 0xec, 0xa, 0xd2, 0x25, 0xb8, 0xf9, 0x2a, 0x57, 0x32, 0x8e, 0x96, 0xfa, 0x8e, 0x21, 0x5f, 0x16, 0x86, 0x3a, 0xea, 0x5b, 0x1c, 0x4c, 0x25, 0x89, 0x59, 0xbf, 0x4a, 0x3, 0x6a, 0x1d, 0x2e, 0xc8, 0xfe, 0x17, 0xa, 0x86, 0x5b, 0x51, 0x3, 0x8e, 0xa5, 0xf9, 0x4c, 0x64, 0xe, 0x68, 0x9f, 0xe9, 0xbd, 0xf0, 0x9, 0xb7, 0x71, 0x36, 0xc6, 0x9b, 0x3d, 0x7f, 0x21, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
@@ -362,6 +434,14 @@ static const unsigned char toggle_on_disabled_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x1, 0x53, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0xd, 0xf, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x24, 0x24, 0x29, 0x25, 0x25, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0xa, 0xc, 0x1d, 0x1d, 0x21, 0x24, 0x24, 0x29, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x23, 0x23, 0x28, 0x2e, 0x2e, 0x2e, 0x46, 0x46, 0x46, 0x57, 0x57, 0x57, 0x60, 0x60, 0x60, 0x62, 0x62, 0x62, 0x5e, 0x5e, 0x5e, 0x4a, 0x4a, 0x4a, 0x12, 0x12, 0x15, 0x25, 0x27, 0x2d, 0x42, 0x42, 0x42, 0x59, 0x59, 0x59, 0x32, 0x32, 0x32, 0x25, 0x26, 0x2d, 0x25, 0x25, 0x2b, 0x25, 0x26, 0x2c, 0x39, 0x39, 0x39, 0x49, 0x49, 0x49, 0x5a, 0x5a, 0x5a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0xb, 0xd, 0x23, 0x23, 0x28, 0x48, 0x48, 0x48, 0x54, 0x54, 0x54, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0xb, 0xb, 0xd, 0x1e, 0x1e, 0x22, 0x25, 0x26, 0x2b, 0x3f, 0x3f, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xe, 0x10, 0x2c, 0x2c, 0x2c, 0x58, 0x58, 0x58, 0x1a, 0x1a, 0x1e, 0x40, 0x40, 0x44, 0x56, 0x56, 0x58, 0x80, 0x80, 0x80, 0x79, 0x79, 0x79, 0x3c, 0x3c, 0x3d, 0x2e, 0x2e, 0x30, 0x27, 0x27, 0x29, 0x64, 0x64, 0x66, 0x41, 0x41, 0x41, 0x1a, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 0x5d, 0x5f, 0x34, 0x34, 0x36, 0x52, 0x52, 0x52, 0x3a, 0x3a, 0x3a, 0x20, 0x20, 0x24, 0x0, 0x0, 0x0, 0x32, 0x32, 0x37, 0x42, 0x42, 0x44, 0x6a, 0x6a, 0x6d, 0x5b, 0x5b, 0x5b, 0x2f, 0x2f, 0x2f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x49, 0x4a, 0x0, 0x0, 0x0, 0x50, 0x50, 0x51, 0x70, 0x70, 0x74, 0xe, 0xe, 0x10, 0xb, 0xb, 0xd, 0x0, 0x0, 0x0, 0x13, 0x13, 0x15, 0x0, 0x0, 0x0, 0xb, 0xb, 0xc, 0x1d, 0x1d, 0x21, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbd, 0xb, 0x85, 0x35, 0x0, 0x0, 0x0, 0x71, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x80, 0xc5, 0xe7, 0xf5, 0xfe, 0xff, 0x8, 0x17, 0x35, 0x73, 0xd9, 0xff, 0x7, 0x3a, 0x96, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb, 0x28, 0x76, 0xfb, 0xff, 0xff, 0xff, 0xff, 0x77, 0xde, 0xff, 0xff, 0x45, 0x5, 0x82, 0xff, 0xff, 0xc6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc6, 0x37, 0xf, 0xff, 0xff, 0xff, 0xff, 0xe9, 0x4c, 0xff, 0xff, 0xff, 0xff, 0xff, 0x4e, 0x4f, 0xff, 0x50, 0xff, 0xff, 0x83, 0x78, 0x3b, 0x9c, 0x3c, 0x74, 0xda, 0x53, 0x14, 0x49, 0x96, 0x6e, 0xf, 0x0, 0x0, 0x1, 0xfa, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x62, 0x18, 0x5e, 0x0, 0xd0, 0x5c, 0x39, 0x28, 0x49, 0x12, 0xc0, 0x60, 0xb8, 0xda, 0xdd, 0xcb, 0x31, 0x33, 0xb6, 0x6d, 0xdb, 0x5e, 0xdb, 0xd6, 0xfb, 0x17, 0x4e, 0x7d, 0x37, 0xe6, 0xf9, 0x2b, 0x23, 0x7f, 0x9c, 0x20, 0x28, 0x86, 0xe1, 0xb, 0xc1, 0x30, 0x14, 0x99, 0x6a, 0x8e, 0xe2, 0x4, 0x49, 0xd1, 0xcc, 0xda, 0x2, 0x18, 0x9a, 0x22, 0x9, 0x1c, 0x9d, 0xf4, 0x8e, 0xaf, 0xd3, 0x1b, 0x9b, 0x5b, 0xdb, 0x1c, 0x2e, 0x6f, 0x2e, 0x5c, 0xce, 0xf6, 0xd6, 0xe6, 0x6, 0xbd, 0x8e, 0x8f, 0x45, 0x81, 0xf2, 0x5, 0x42, 0x91, 0x58, 0x22, 0x95, 0xc9, 0x61, 0x1, 0x72, 0x99, 0x54, 0x22, 0x16, 0x9, 0x5, 0x7c, 0x74, 0xc4, 0x5e, 0xa1, 0x54, 0xa9, 0x35, 0x5a, 0x58, 0x12, 0xad, 0x46, 0xad, 0x52, 0x2a, 0x86, 0x14, 0x10, 0x1d, 0xa3, 0x37, 0x18, 0x61, 0x1e, 0x26, 0xb3, 0xc5, 0x6a, 0x63, 0xb1, 0x3b, 0x4c, 0x60, 0x34, 0xe8, 0x19, 0x1d, 0x32, 0xc8, 0x9f, 0xda, 0x74, 0xce, 0x8d, 0xdd, 0xe5, 0xf6, 0x98, 0xbd, 0x3e, 0xff, 0x57, 0x2, 0xa6, 0x60, 0x28, 0xec, 0x76, 0xc9, 0x9d, 0x9b, 0x54, 0xbf, 0xe, 0x68, 0x24, 0x1a, 0x8b, 0xcf, 0xb5, 0x4f, 0x24, 0x53, 0x30, 0x44, 0x3a, 0x99, 0x70, 0xc5, 0x33, 0xd1, 0xc8, 0x8f, 0x24, 0x70, 0xe5, 0x56, 0x16, 0xe6, 0x90, 0xcb, 0x27, 0x4d, 0x63, 0x9, 0x25, 0xf3, 0xb9, 0x6c, 0x41, 0x59, 0x64, 0x43, 0x40, 0x88, 0x8d, 0x12, 0xcc, 0xa3, 0x5c, 0x49, 0xc3, 0x18, 0x69, 0x4f, 0x19, 0x4a, 0x1b, 0x4, 0x2b, 0x80, 0x92, 0x9b, 0x55, 0xe8, 0x53, 0xab, 0x37, 0x9a, 0x2d, 0x68, 0x37, 0x3a, 0xd0, 0xee, 0x2, 0x4b, 0xcf, 0x1, 0x13, 0x38, 0x7a, 0xb0, 0xb3, 0xbb, 0xc7, 0xe6, 0x80, 0x51, 0x5b, 0x52, 0xe8, 0xd3, 0xdd, 0xaf, 0x1d, 0x34, 0x6a, 0xed, 0x46, 0x77, 0x20, 0x70, 0x78, 0x4, 0x13, 0x1c, 0x1d, 0xc2, 0x71, 0x81, 0xc2, 0x58, 0x1, 0xfa, 0x44, 0x36, 0x8, 0xa0, 0x71, 0xa, 0xb0, 0x7f, 0xd6, 0x3e, 0x6f, 0xb6, 0xfb, 0x2, 0x17, 0x97, 0x30, 0xc1, 0xe5, 0x5, 0x18, 0xaf, 0x68, 0x9c, 0x15, 0x50, 0x5e, 0xf, 0x7a, 0xd8, 0x3e, 0x7, 0x80, 0x9b, 0x7a, 0xbb, 0x7b, 0x73, 0xde, 0x17, 0xb0, 0xfa, 0x60, 0x2, 0x9f, 0x15, 0xe0, 0x56, 0xf9, 0x5d, 0x80, 0xde, 0x1e, 0xc, 0xd1, 0xe9, 0xd7, 0x8, 0xea, 0x77, 0xed, 0x2e, 0xdc, 0xd7, 0x97, 0x8e, 0x80, 0xda, 0x3a, 0x86, 0x3e, 0xe7, 0x67, 0x70, 0xda, 0x6c, 0xb5, 0xbb, 0xd0, 0x6a, 0x2c, 0x5b, 0x3, 0x94, 0xdc, 0xad, 0x42, 0x9f, 0x76, 0xf3, 0xbc, 0x59, 0x87, 0xaf, 0xe1, 0x9f, 0x2f, 0xd5, 0x5, 0x76, 0xe, 0x1e, 0x60, 0xc0, 0xe9, 0x59, 0x7, 0xa0, 0xd6, 0x2, 0xe8, 0xb4, 0xe6, 0xcf, 0xc1, 0x83, 0x90, 0x40, 0x7e, 0x79, 0x12, 0xfb, 0xbb, 0x90, 0x59, 0x7d, 0x17, 0x1e, 0xd9, 0x5d, 0xf8, 0xd5, 0x6d, 0xec, 0xdf, 0x83, 0xa7, 0xe7, 0xf8, 0x2a, 0xf7, 0x20, 0xfe, 0xfc, 0xc2, 0xde, 0x83, 0xfe, 0x45, 0x62, 0x36, 0x5f, 0xdf, 0x60, 0x69, 0xde, 0x5e, 0x37, 0x19, 0xc5, 0xe8, 0x4d, 0xd3, 0x51, 0xc2, 0x2d, 0xb1, 0xe4, 0xd8, 0x8, 0xb, 0x31, 0x1e, 0x4b, 0xc4, 0x5b, 0x42, 0x4a, 0x87, 0x8e, 0x5f, 0xe5, 0x8, 0xbd, 0xb1, 0x5b, 0xb8, 0x7a, 0xe7, 0x2d, 0xe0, 0xfd, 0xaa, 0xb0, 0xbb, 0x41, 0x47, 0x70, 0x64, 0xf2, 0xab, 0x14, 0x89, 0xbd, 0xf, 0xe5, 0xe2, 0xbf, 0xa0, 0xfc, 0xd8, 0x23, 0x8a, 0x28, 0x32, 0xe3, 0x33, 0xe1, 0x4b, 0xc0, 0x7e, 0xa6, 0xff, 0x87, 0xcf, 0xb, 0x94, 0xb9, 0x37, 0x3c, 0xc6, 0xd8, 0xcd, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char toggle_on_disabled_mirrored_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x0, 0x69, 0x50, 0x4c, 0x54, 0x45, 0x93, 0x7f, 0x2b, 0x14, 0x14, 0x17, 0x20, 0x20, 0x25, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x25, 0x25, 0x2a, 0x10, 0x10, 0x13, 0x22, 0x22, 0x27, 0x25, 0x25, 0x28, 0x25, 0x25, 0x29, 0x25, 0x25, 0x27, 0x19, 0x19, 0x1c, 0x2b, 0x26, 0x2c, 0x40, 0x40, 0x44, 0x4e, 0x4e, 0x52, 0x1a, 0x1a, 0x1d, 0x32, 0x32, 0x37, 0x2c, 0x26, 0x2c, 0x26, 0x25, 0x2a, 0x27, 0x25, 0x2a, 0x11, 0x11, 0x14, 0x2f, 0x26, 0x2d, 0x12, 0x12, 0x14, 0x23, 0x23, 0x27, 0x15, 0x15, 0x18, 0x5b, 0x5b, 0x5f, 0x84, 0x84, 0x87, 0x77, 0x77, 0x7a, 0x69, 0x69, 0x6c, 0x20, 0x20, 0x24, 0x24, 0x24, 0x27, 0x23, 0x23, 0x28, 0x1a, 0x1a, 0x1e, 0x11, 0x11, 0x13, 0x22, 0x22, 0x26, 0xd7, 0x77, 0xc6, 0x92, 0x0, 0x0, 0x0, 0x1, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x40, 0xe6, 0xd8, 0x66, 0x0, 0x0, 0x1, 0x35, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0xed, 0x94, 0xd1, 0x72, 0x83, 0x20, 0x10, 0x45, 0x4d, 0x90, 0xb8, 0x18, 0x96, 0x6c, 0xb5, 0x24, 0x41, 0x68, 0x9b, 0xf4, 0xff, 0x3f, 0xb2, 0x84, 0xb4, 0x8a, 0xa, 0x3a, 0x9d, 0x74, 0xa6, 0x2f, 0xb9, 0xbe, 0x38, 0xca, 0x3d, 0x5c, 0x16, 0xd8, 0xa2, 0x78, 0xea, 0x2f, 0xb5, 0xd9, 0xb2, 0xb2, 0xe4, 0x2b, 0x2a, 0x4b, 0xb6, 0xdd, 0x24, 0xed, 0xbb, 0x8a, 0x1, 0x8, 0x21, 0xee, 0xe3, 0xc4, 0x4d, 0x20, 0x6a, 0x91, 0x84, 0x54, 0xbb, 0xb9, 0x7f, 0xcf, 0x40, 0xa2, 0xea, 0x85, 0x32, 0x50, 0x6a, 0xc8, 0xe4, 0xd8, 0x4f, 0xfd, 0x7, 0x26, 0x48, 0xe1, 0x4b, 0x13, 0x7e, 0x37, 0x92, 0x50, 0x51, 0x1b, 0x92, 0xe4, 0x56, 0x72, 0x18, 0xfb, 0x5f, 0x99, 0x40, 0xd4, 0xf1, 0x8, 0x42, 0x6c, 0x17, 0x6b, 0x71, 0x1c, 0x1, 0x4e, 0x40, 0x21, 0x74, 0x24, 0x89, 0xd4, 0x2c, 0x11, 0x4e, 0xb1, 0xff, 0xc, 0x52, 0xe9, 0xe9, 0x8, 0x52, 0x8b, 0x11, 0xf8, 0x39, 0x2, 0x6c, 0x7d, 0x80, 0xf9, 0x8, 0xa4, 0xe1, 0xd5, 0x74, 0x16, 0xb9, 0xee, 0x5a, 0xae, 0xdd, 0xcf, 0xb7, 0xb7, 0x8, 0xe0, 0x2b, 0x40, 0x73, 0x40, 0x4, 0x75, 0x6, 0xa9, 0x43, 0xdd, 0xb9, 0x8, 0xc0, 0x46, 0x0, 0x25, 0xe7, 0x0, 0xa9, 0xfa, 0x0, 0x9d, 0xe7, 0x1b, 0xd4, 0xce, 0xea, 0x1, 0x50, 0x8e, 0x1, 0x89, 0x82, 0x35, 0x3d, 0x20, 0xb8, 0x94, 0xd1, 0x4e, 0xb9, 0x1, 0xc0, 0x7f, 0x91, 0x80, 0x42, 0x2, 0xe5, 0xcd, 0xd6, 0x24, 0x13, 0xbc, 0xc3, 0x5a, 0xd, 0x90, 0x93, 0xf5, 0x4b, 0xf0, 0x8b, 0x49, 0xd6, 0x60, 0x75, 0x17, 0xb4, 0x75, 0xd6, 0x84, 0x95, 0xb8, 0xe4, 0x2e, 0xac, 0x9f, 0x3, 0xba, 0x9d, 0x4b, 0xf4, 0xb3, 0xb4, 0xfd, 0x4c, 0xf1, 0x39, 0x28, 0x3e, 0xc4, 0x63, 0x27, 0xb1, 0x38, 0x3e, 0x7a, 0x17, 0xb2, 0xb7, 0x31, 0xeb, 0x9f, 0xdc, 0xc6, 0xa2, 0xb8, 0x30, 0x68, 0xa7, 0xfd, 0x60, 0xc1, 0x7f, 0x99, 0x77, 0x94, 0xeb, 0x27, 0xd4, 0x70, 0x6f, 0x48, 0xe2, 0x5b, 0xe0, 0x9f, 0xa4, 0xbf, 0xba, 0x66, 0x7b, 0x22, 0x5f, 0x55, 0xb6, 0x27, 0x3e, 0xf5, 0x8f, 0xfa, 0x2, 0xa0, 0x14, 0x20, 0xeb, 0xde, 0xb1, 0x8c, 0x34, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
+static const unsigned char toggle_on_mirrored_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x1, 0x9b, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0xd, 0xf, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x24, 0x24, 0x29, 0x24, 0x24, 0x28, 0x20, 0x20, 0x25, 0x14, 0x14, 0x17, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0xa, 0xc, 0x1d, 0x1d, 0x21, 0x22, 0x22, 0x27, 0x10, 0x10, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x23, 0x23, 0x28, 0x19, 0x19, 0x1c, 0x12, 0x12, 0x15, 0x1a, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0xb, 0xd, 0x23, 0x23, 0x28, 0x12, 0x12, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1e, 0x1e, 0x22, 0x23, 0x23, 0x27, 0xe, 0xe, 0x10, 0x15, 0x15, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x1a, 0x1d, 0x20, 0x20, 0x24, 0x20, 0x20, 0x24, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xe, 0x10, 0x15, 0x15, 0x18, 0xb, 0xb, 0xd, 0x12, 0x12, 0x14, 0x0, 0x0, 0x0, 0x13, 0x13, 0x15, 0x1a, 0x1a, 0x1e, 0xb, 0xb, 0xc, 0x1d, 0x1d, 0x21, 0x11, 0x11, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x25, 0x25, 0x2a, 0x24, 0x24, 0x29, 0x25, 0x25, 0x29, 0x25, 0x25, 0x27, 0x25, 0x2c, 0x36, 0x28, 0x4c, 0x6b, 0x29, 0x64, 0x92, 0x2a, 0x68, 0x99, 0x2a, 0x66, 0x95, 0x29, 0x5d, 0x85, 0x27, 0x49, 0x65, 0x25, 0x25, 0x28, 0x25, 0x27, 0x2d, 0x27, 0x44, 0x5c, 0x29, 0x60, 0x8c, 0x27, 0x4d, 0x6b, 0x25, 0x3a, 0x4c, 0x25, 0x2d, 0x38, 0x25, 0x26, 0x2c, 0x25, 0x25, 0x2b, 0x25, 0x26, 0x2d, 0x25, 0x30, 0x3e, 0x27, 0x49, 0x66, 0x29, 0x5f, 0x89, 0x27, 0x43, 0x5c, 0x27, 0x4b, 0x69, 0x28, 0x58, 0x7f, 0x25, 0x35, 0x45, 0x25, 0x34, 0x43, 0x28, 0x59, 0x7f, 0x25, 0x26, 0x2b, 0x27, 0x41, 0x57, 0x27, 0x40, 0x57, 0x25, 0x2b, 0x34, 0x25, 0x34, 0x44, 0x29, 0x5d, 0x87, 0x25, 0x2a, 0x33, 0x27, 0x43, 0x5b, 0x27, 0x4e, 0x6d, 0x27, 0x4d, 0x6c, 0x40, 0x40, 0x44, 0xad, 0xad, 0xaf, 0xff, 0xff, 0xff, 0xf2, 0xf2, 0xf2, 0x77, 0x77, 0x7a, 0x5b, 0x5b, 0x5f, 0x4e, 0x4e, 0x52, 0xc9, 0xc9, 0xca, 0x28, 0x56, 0x7b, 0x26, 0x3a, 0x4e, 0x26, 0x3b, 0x4e, 0xbb, 0xbb, 0xbd, 0x69, 0x69, 0x6c, 0x29, 0x61, 0x8d, 0x25, 0x2e, 0x39, 0x32, 0x32, 0x37, 0x84, 0x84, 0x87, 0xd6, 0xd6, 0xd7, 0x92, 0x92, 0x94, 0xa0, 0xa0, 0xa2, 0x27, 0x44, 0x5d, 0xa6, 0xa2, 0x25, 0x5b, 0x0, 0x0, 0x0, 0x4c, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x80, 0xc5, 0xe7, 0xf5, 0xfe, 0xfa, 0xe5, 0x98, 0x8, 0x17, 0x35, 0x73, 0xd9, 0xf3, 0x86, 0x7, 0x3a, 0x96, 0xf9, 0xb4, 0x9a, 0xb9, 0xb, 0x28, 0x77, 0xfb, 0x8b, 0x5, 0x45, 0xde, 0xf6, 0x82, 0x9b, 0xf, 0x37, 0xc6, 0xe6, 0xe9, 0xfb, 0x4e, 0x50, 0x83, 0x9c, 0x78, 0x8c, 0x3c, 0x9c, 0xbb, 0x74, 0xda, 0x87, 0x53, 0x14, 0xd0, 0x92, 0x4e, 0x2c, 0x0, 0x0, 0x2, 0x35, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0x63, 0x60, 0x18, 0x44, 0x80, 0x91, 0x89, 0x99, 0x99, 0x85, 0x20, 0x60, 0x66, 0x66, 0x62, 0xc4, 0xaa, 0x9d, 0x89, 0x85, 0x95, 0x8d, 0x9d, 0x83, 0x93, 0x8b, 0x0, 0xe0, 0xe4, 0x60, 0x67, 0x63, 0x65, 0x61, 0xc2, 0xb4, 0x9d, 0x85, 0x9b, 0x83, 0x87, 0x97, 0x8f, 0x5f, 0x40, 0x50, 0x8, 0x2f, 0x10, 0x14, 0xe0, 0xe7, 0xe3, 0xe5, 0xe1, 0xe0, 0x66, 0x41, 0x73, 0x5, 0x93, 0xb0, 0x88, 0xa8, 0x98, 0xb8, 0x84, 0xa4, 0x94, 0xb4, 0xf, 0x1, 0xe0, 0x2b, 0x2d, 0x23, 0x2b, 0x27, 0x26, 0x2a, 0x22, 0x8c, 0xe2, 0x8, 0x26, 0x79, 0x5, 0x45, 0x25, 0x65, 0x5f, 0xc, 0xd5, 0x7e, 0xfe, 0x7e, 0x58, 0xd, 0x51, 0x51, 0x55, 0x54, 0x90, 0x47, 0x32, 0x81, 0x51, 0x8d, 0x53, 0x5d, 0x43, 0xd3, 0x27, 0x20, 0x30, 0x28, 0x18, 0x2, 0x42, 0x42, 0xc3, 0x2, 0x20, 0x6, 0x84, 0xe3, 0x70, 0x87, 0x96, 0x3a, 0xa7, 0x1a, 0x23, 0xc2, 0xff, 0xec, 0xbc, 0xda, 0xd2, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, 0x20, 0x90, 0x90, 0x98, 0x94, 0x9c, 0x12, 0x1, 0x36, 0x2, 0x97, 0x4f, 0x74, 0x78, 0xd9, 0xe1, 0xe1, 0xc0, 0xa4, 0xab, 0xa7, 0x6f, 0x10, 0x91, 0x9a, 0x96, 0x8e, 0xac, 0x22, 0x23, 0x33, 0x35, 0x2, 0x6f, 0x58, 0x18, 0xea, 0xe9, 0x42, 0x3d, 0xc1, 0x68, 0xa4, 0x60, 0x6c, 0x92, 0x95, 0x9d, 0x19, 0x8b, 0xaa, 0x22, 0x36, 0x33, 0x27, 0xb, 0x9f, 0x9, 0xa6, 0xc6, 0xa, 0x46, 0x10, 0x27, 0x30, 0xb1, 0xf2, 0x98, 0xf9, 0xe4, 0x26, 0xa7, 0xa3, 0xab, 0xc8, 0xcb, 0x2f, 0xc0, 0x1b, 0x1f, 0xe6, 0x3c, 0xac, 0x10, 0x3, 0x58, 0x2c, 0x2c, 0xad, 0x7c, 0xa, 0x8b, 0x30, 0x55, 0x14, 0x17, 0xc2, 0x99, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x3e, 0x95, 0x65, 0x55, 0x3e, 0x95, 0xd5, 0x30, 0x31, 0x6b, 0x4b, 0xb, 0x88, 0x1f, 0x84, 0xd9, 0x8d, 0x6d, 0x7c, 0x6a, 0x6a, 0x31, 0xd, 0xa8, 0xab, 0x81, 0x33, 0xab, 0xeb, 0x4b, 0x1a, 0xca, 0x4a, 0x2a, 0xcb, 0xaa, 0x91, 0xc, 0xb0, 0x35, 0x66, 0x67, 0x6, 0x1b, 0xa0, 0xc6, 0x61, 0xa7, 0xe9, 0xd3, 0xd8, 0x84, 0x69, 0x40, 0x53, 0x23, 0xdc, 0x1, 0x65, 0xcd, 0x3e, 0x3e, 0xf5, 0x2d, 0x95, 0xad, 0xe5, 0x95, 0x8, 0x3, 0x7c, 0xed, 0x38, 0x58, 0x20, 0x6, 0x28, 0xd8, 0xfb, 0xf8, 0x84, 0x24, 0x60, 0x1a, 0x90, 0x10, 0x2, 0x63, 0x55, 0xb6, 0x2, 0x89, 0xb6, 0xd2, 0xca, 0xea, 0xb6, 0x56, 0x84, 0x1, 0x3e, 0xf6, 0xa, 0x2c, 0xc4, 0xba, 0xa0, 0x19, 0xe4, 0x82, 0xd2, 0x76, 0xa0, 0xe6, 0xf2, 0x52, 0x4c, 0x17, 0x10, 0x13, 0x6, 0xad, 0x2d, 0x3e, 0xcd, 0xe5, 0x15, 0x40, 0x3, 0x2a, 0xca, 0x30, 0xc3, 0x80, 0x98, 0x58, 0xa8, 0x2c, 0x6f, 0x2d, 0x2f, 0x5, 0x7, 0x60, 0x2b, 0x66, 0x2c, 0x30, 0xb1, 0x8a, 0x3a, 0x10, 0x4c, 0x7, 0xcd, 0x2d, 0x55, 0xc0, 0xb0, 0xac, 0xf0, 0xf1, 0xa9, 0xaa, 0x80, 0x9, 0x39, 0x8a, 0x42, 0xd3, 0x1, 0xc5, 0x29, 0x11, 0x94, 0x17, 0x9c, 0x48, 0xcf, 0xb, 0xce, 0xf0, 0xbc, 0x0, 0xcb, 0x8d, 0x1d, 0xa8, 0xb9, 0x31, 0x12, 0xbf, 0x7e, 0xe4, 0xdc, 0x8, 0x2a, 0xf, 0x5c, 0x5c, 0xd, 0xb0, 0x94, 0x7, 0xb8, 0xf5, 0xbb, 0xb9, 0x20, 0x95, 0x7, 0xa0, 0x12, 0x89, 0x93, 0xd7, 0xdd, 0x3, 0x53, 0x9d, 0x5f, 0x38, 0xf6, 0xf2, 0x40, 0xc5, 0x93, 0x97, 0x53, 0x1e, 0xb5, 0x4c, 0x53, 0x63, 0x17, 0xe5, 0x13, 0x97, 0xb0, 0xd1, 0xf4, 0x21, 0x8, 0x7c, 0x6d, 0x65, 0xe5, 0xf8, 0x44, 0xd9, 0xd5, 0xd0, 0xa, 0x66, 0x46, 0x16, 0x5d, 0xe, 0x1e, 0x4b, 0x63, 0x3b, 0x2f, 0x21, 0x2, 0xc0, 0xcb, 0xce, 0xd8, 0x92, 0x87, 0x43, 0x97, 0x5, 0xa3, 0x6e, 0x60, 0x64, 0x32, 0x62, 0xb5, 0xf0, 0x56, 0x20, 0x5c, 0x2f, 0x28, 0x78, 0x5b, 0xb0, 0x1a, 0x61, 0xaf, 0x5b, 0x80, 0x35, 0x13, 0xe1, 0x8a, 0x9, 0x58, 0x35, 0xe1, 0xa8, 0x99, 0x86, 0x2e, 0x0, 0x0, 0x69, 0x2c, 0x6b, 0xc2, 0xf1, 0x2f, 0x53, 0x53, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char tooltip_bg_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, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x30, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x2c, 0x2f, 0x48, 0x46, 0x4a, 0xdd, 0xdd, 0xdd, 0x4c, 0x4a, 0x4e, 0x48, 0x46, 0x4a, 0x40, 0x3e, 0x42, 0xbc, 0x3, 0x4f, 0xe9, 0x0, 0x0, 0x0, 0xd, 0x74, 0x52, 0x4e, 0x53, 0xa, 0x1a, 0x26, 0x29, 0x2a, 0x48, 0x65, 0x6d, 0x6e, 0x66, 0xf5, 0xfe, 0xcc, 0xff, 0xb7, 0x4a, 0xbe, 0x0, 0x0, 0x0, 0x38, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0x54, 0x76, 0x1, 0x2, 0x23, 0x1, 0x6, 0xd1, 0xf4, 0xe, 0x20, 0x28, 0xb, 0x64, 0xd0, 0x5c, 0x7d, 0x17, 0x8, 0x76, 0x4d, 0x62, 0x70, 0x7f, 0x7f, 0x6, 0x8, 0xfe, 0x95, 0x30, 0x78, 0xdc, 0x1, 0x31, 0xce, 0xb6, 0x50, 0xc8, 0x80, 0x1b, 0x8, 0xb7, 0x2, 0x6e, 0x29, 0xdc, 0x19, 0x0, 0xcf, 0x24, 0x4d, 0xb3, 0xd0, 0x4d, 0xb9, 0x40, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
@@ -386,6 +466,10 @@ static const unsigned char unchecked_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, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x33, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x38, 0x37, 0x40, 0x20, 0x20, 0x24, 0x20, 0x20, 0x24, 0x38, 0x36, 0x40, 0x20, 0x20, 0x25, 0x1e, 0x1e, 0x22, 0x1f, 0x1f, 0x23, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2a, 0x23, 0xc3, 0x49, 0x39, 0x0, 0x0, 0x0, 0xb, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x50, 0x66, 0x68, 0xb4, 0xfa, 0xfb, 0xb4, 0xfa, 0xa4, 0x7f, 0xe1, 0x5a, 0x0, 0x0, 0x0, 0x4f, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xb5, 0xcf, 0x45, 0x2, 0x80, 0x40, 0x8, 0x0, 0x40, 0x97, 0x66, 0xfb, 0xff, 0x9f, 0xb5, 0xdb, 0xb3, 0x73, 0xa4, 0x19, 0xbe, 0x2, 0x20, 0xf1, 0x8a, 0x10, 0xc2, 0x1c, 0x0, 0xd1, 0x94, 0x57, 0x49, 0x5, 0xe6, 0x0, 0x6a, 0xa9, 0x6d, 0x55, 0x8b, 0xe2, 0x1c, 0xa0, 0x54, 0xfb, 0xae, 0x26, 0x9a, 0x3, 0x9c, 0xdb, 0x11, 0x68, 0x99, 0xff, 0xa, 0x7c, 0xd6, 0xde, 0xf, 0x33, 0x9c, 0x3, 0xe0, 0x76, 0x9c, 0x1e, 0x1d, 0xbe, 0xcf, 0x7d, 0x4c, 0x93, 0xe2, 0x8, 0xa4, 0x66, 0x3c, 0xec, 0xed, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char unchecked_disabled_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, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x33, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x6a, 0x69, 0x70, 0x6a, 0x68, 0x70, 0x58, 0x58, 0x5c, 0x58, 0x58, 0x5b, 0x58, 0x58, 0x5b, 0x5c, 0x5c, 0x5f, 0x5a, 0x5a, 0x5e, 0x59, 0x59, 0x5d, 0x58, 0x58, 0x5b, 0x57, 0x57, 0x5a, 0x56, 0x56, 0x59, 0x27, 0xa1, 0xa6, 0x53, 0x0, 0x0, 0x0, 0xb, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x50, 0x66, 0x68, 0xb4, 0xb4, 0xfa, 0xfa, 0xfb, 0xc7, 0x8b, 0xf6, 0x7e, 0x0, 0x0, 0x0, 0x52, 0x49, 0x44, 0x41, 0x54, 0x78, 0x5e, 0xb5, 0xcf, 0x31, 0x12, 0xc0, 0x20, 0x8, 0x44, 0xd1, 0x15, 0x50, 0x83, 0x11, 0xe5, 0xfe, 0xa7, 0xcd, 0xe8, 0xc4, 0x22, 0xf4, 0x79, 0xe5, 0x36, 0x7c, 0x80, 0x8, 0x89, 0x58, 0xf2, 0x26, 0x4c, 0x9, 0x0, 0x15, 0xf5, 0xb9, 0xb9, 0x16, 0x2, 0xc0, 0x3a, 0xac, 0x6f, 0x36, 0x94, 0x1, 0x88, 0xdb, 0xfd, 0x32, 0x17, 0x0, 0x79, 0xf6, 0x33, 0xf4, 0x99, 0xff, 0x1a, 0xe2, 0xd9, 0x4f, 0x58, 0x5b, 0x61, 0x54, 0xdb, 0x49, 0xbf, 0xea, 0x4a, 0x8f, 0xcf, 0x45, 0xf, 0x4, 0x40, 0x7, 0x90, 0xb0, 0x7b, 0x47, 0x4, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char updown_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0x81, 0x83, 0xf6, 0xf6, 0x0, 0x0, 0x0, 0x57, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x8d, 0x4e, 0xb5, 0x1, 0xc0, 0x30, 0xc, 0xf3, 0x15, 0xfe, 0xff, 0x96, 0x64, 0xa, 0x6c, 0xca, 0x56, 0xd2, 0x25, 0x65, 0xe6, 0xc8, 0x8b, 0x49, 0x20, 0x79, 0x28, 0x95, 0x81, 0xa1, 0xd4, 0x7d, 0x4, 0xbb, 0xa1, 0x50, 0xea, 0x3c, 0xa6, 0x71, 0x98, 0x96, 0x69, 0x58, 0x31, 0xcc, 0xb7, 0xe5, 0x2f, 0x48, 0x63, 0x26, 0xf6, 0xa2, 0xd4, 0x18, 0xf9, 0x7, 0x2d, 0xe3, 0x46, 0x89, 0xb4, 0xd2, 0xf8, 0xa3, 0x68, 0xe3, 0xd7, 0x14, 0x20, 0xe6, 0xc3, 0x3d, 0xd8, 0xca, 0x5e, 0x94, 0x32, 0xd0, 0x3, 0x91, 0xba, 0x5f, 0x1b, 0x4a, 0x9b, 0x12, 0x62, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
@@ -426,4 +510,8 @@ static const unsigned char window_resizer_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, 0x1e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x18, 0xbc, 0xe0, 0x45, 0x3f, 0x1, 0xe9, 0xec, 0xfe, 0x81, 0x94, 0x86, 0xb1, 0x70, 0x48, 0x23, 0x58, 0x84, 0xa4, 0x7, 0x15, 0x0, 0x0, 0xed, 0x9f, 0x18, 0xe8, 0xcd, 0x91, 0xd8, 0xe, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char window_resizer_mirrored_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, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x0, 0x27, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x63, 0x60, 0x18, 0x44, 0xe0, 0x45, 0x3f, 0x76, 0x71, 0x26, 0x18, 0xa3, 0x19, 0xa7, 0x12, 0x38, 0xc8, 0xee, 0xa7, 0xb1, 0x12, 0x98, 0x4, 0x4e, 0x25, 0x8, 0x9, 0x4a, 0x94, 0xc, 0x10, 0x0, 0x0, 0x9d, 0x84, 0x18, 0x73, 0x33, 0x1c, 0x96, 0xd6, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
// shaders block
diff --git a/scene/resources/default_theme/toggle_off_disabled_mirrored.png b/scene/resources/default_theme/toggle_off_disabled_mirrored.png
new file mode 100644
index 0000000000..799b63c098
--- /dev/null
+++ b/scene/resources/default_theme/toggle_off_disabled_mirrored.png
Binary files differ
diff --git a/scene/resources/default_theme/toggle_off_mirrored.png b/scene/resources/default_theme/toggle_off_mirrored.png
new file mode 100644
index 0000000000..3487605d58
--- /dev/null
+++ b/scene/resources/default_theme/toggle_off_mirrored.png
Binary files differ
diff --git a/scene/resources/default_theme/toggle_on_disabled_mirrored.png b/scene/resources/default_theme/toggle_on_disabled_mirrored.png
new file mode 100644
index 0000000000..0758babd4f
--- /dev/null
+++ b/scene/resources/default_theme/toggle_on_disabled_mirrored.png
Binary files differ
diff --git a/scene/resources/default_theme/toggle_on_mirrored.png b/scene/resources/default_theme/toggle_on_mirrored.png
new file mode 100644
index 0000000000..3fd953c8e2
--- /dev/null
+++ b/scene/resources/default_theme/toggle_on_mirrored.png
Binary files differ
diff --git a/scene/resources/default_theme/unchecked_disabled.png b/scene/resources/default_theme/unchecked_disabled.png
new file mode 100644
index 0000000000..bef9316f58
--- /dev/null
+++ b/scene/resources/default_theme/unchecked_disabled.png
Binary files differ
diff --git a/scene/resources/default_theme/window_resizer_mirrored.png b/scene/resources/default_theme/window_resizer_mirrored.png
new file mode 100644
index 0000000000..bbce5f1406
--- /dev/null
+++ b/scene/resources/default_theme/window_resizer_mirrored.png
Binary files differ
diff --git a/scene/resources/default_theme/xpmfix.sh b/scene/resources/default_theme/xpmfix.sh
deleted file mode 100755
index a24dede3c9..0000000000
--- a/scene/resources/default_theme/xpmfix.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-sed -i 's/static char/static const char/g' *.xpm
diff --git a/scene/resources/dynamic_font.cpp b/scene/resources/dynamic_font.cpp
deleted file mode 100644
index bc983c1d7e..0000000000
--- a/scene/resources/dynamic_font.cpp
+++ /dev/null
@@ -1,1143 +0,0 @@
-/*************************************************************************/
-/* dynamic_font.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 "modules/modules_enabled.gen.h"
-#ifdef MODULE_FREETYPE_ENABLED
-
-#include "dynamic_font.h"
-
-#include "core/os/file_access.h"
-#include "core/os/os.h"
-
-#include FT_STROKER_H
-
-#define __STDC_LIMIT_MACROS
-#include <stdint.h>
-
-bool DynamicFontData::CacheID::operator<(CacheID right) const {
- return key < right.key;
-}
-
-Ref<DynamicFontAtSize> DynamicFontData::_get_dynamic_font_at_size(CacheID p_cache_id) {
- if (size_cache.has(p_cache_id)) {
- return Ref<DynamicFontAtSize>(size_cache[p_cache_id]);
- }
-
- Ref<DynamicFontAtSize> dfas;
-
- dfas.instance();
-
- dfas->font = Ref<DynamicFontData>(this);
-
- size_cache[p_cache_id] = dfas.ptr();
- dfas->id = p_cache_id;
- dfas->_load();
-
- return dfas;
-}
-
-void DynamicFontData::set_font_ptr(const uint8_t *p_font_mem, int p_font_mem_size) {
- font_mem = p_font_mem;
- font_mem_size = p_font_mem_size;
-}
-
-void DynamicFontData::set_font_path(const String &p_path) {
- font_path = p_path;
-}
-
-String DynamicFontData::get_font_path() const {
- return font_path;
-}
-
-void DynamicFontData::set_force_autohinter(bool p_force) {
- force_autohinter = p_force;
-}
-
-void DynamicFontData::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &DynamicFontData::set_antialiased);
- ClassDB::bind_method(D_METHOD("is_antialiased"), &DynamicFontData::is_antialiased);
- ClassDB::bind_method(D_METHOD("set_font_path", "path"), &DynamicFontData::set_font_path);
- ClassDB::bind_method(D_METHOD("get_font_path"), &DynamicFontData::get_font_path);
- ClassDB::bind_method(D_METHOD("set_hinting", "mode"), &DynamicFontData::set_hinting);
- ClassDB::bind_method(D_METHOD("get_hinting"), &DynamicFontData::get_hinting);
-
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "antialiased"), "set_antialiased", "is_antialiased");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), "set_hinting", "get_hinting");
-
- BIND_ENUM_CONSTANT(HINTING_NONE);
- BIND_ENUM_CONSTANT(HINTING_LIGHT);
- BIND_ENUM_CONSTANT(HINTING_NORMAL);
-
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "font_path", PROPERTY_HINT_FILE, "*.ttf,*.otf"), "set_font_path", "get_font_path");
-}
-
-DynamicFontData::DynamicFontData() {
- antialiased = true;
- force_autohinter = false;
- hinting = DynamicFontData::HINTING_NORMAL;
- font_mem = nullptr;
- font_mem_size = 0;
-}
-
-DynamicFontData::~DynamicFontData() {
-}
-
-////////////////////
-HashMap<String, Vector<uint8_t>> DynamicFontAtSize::_fontdata;
-
-Error DynamicFontAtSize::_load() {
- int error = FT_Init_FreeType(&library);
-
- ERR_FAIL_COND_V_MSG(error != 0, ERR_CANT_CREATE, "Error initializing FreeType.");
-
- // FT_OPEN_STREAM is extremely slow only on Android.
- if (OS::get_singleton()->get_name() == "Android" && font->font_mem == nullptr && font->font_path != String()) {
- // cache font only once for each font->font_path
- if (_fontdata.has(font->font_path)) {
- font->set_font_ptr(_fontdata[font->font_path].ptr(), _fontdata[font->font_path].size());
-
- } else {
- FileAccess *f = FileAccess::open(font->font_path, FileAccess::READ);
- if (!f) {
- FT_Done_FreeType(library);
- ERR_FAIL_V_MSG(ERR_CANT_OPEN, "Cannot open font file '" + font->font_path + "'.");
- }
-
- size_t len = f->get_len();
- _fontdata[font->font_path] = Vector<uint8_t>();
- Vector<uint8_t> &fontdata = _fontdata[font->font_path];
- fontdata.resize(len);
- f->get_buffer(fontdata.ptrw(), len);
- font->set_font_ptr(fontdata.ptr(), len);
- f->close();
- }
- }
-
- if (font->font_mem == nullptr && font->font_path != String()) {
- FileAccess *f = FileAccess::open(font->font_path, FileAccess::READ);
- if (!f) {
- FT_Done_FreeType(library);
- ERR_FAIL_V_MSG(ERR_CANT_OPEN, "Cannot open font file '" + font->font_path + "'.");
- }
-
- memset(&stream, 0, sizeof(FT_StreamRec));
- stream.base = nullptr;
- stream.size = f->get_len();
- stream.pos = 0;
- stream.descriptor.pointer = f;
- stream.read = _ft_stream_io;
- stream.close = _ft_stream_close;
-
- FT_Open_Args fargs;
- memset(&fargs, 0, sizeof(FT_Open_Args));
- fargs.flags = FT_OPEN_STREAM;
- fargs.stream = &stream;
- error = FT_Open_Face(library, &fargs, 0, &face);
- } else if (font->font_mem) {
- memset(&stream, 0, sizeof(FT_StreamRec));
- stream.base = (unsigned char *)font->font_mem;
- stream.size = font->font_mem_size;
- stream.pos = 0;
-
- FT_Open_Args fargs;
- memset(&fargs, 0, sizeof(FT_Open_Args));
- fargs.memory_base = (unsigned char *)font->font_mem;
- fargs.memory_size = font->font_mem_size;
- fargs.flags = FT_OPEN_MEMORY;
- fargs.stream = &stream;
- error = FT_Open_Face(library, &fargs, 0, &face);
-
- } else {
- FT_Done_FreeType(library);
- ERR_FAIL_V_MSG(ERR_UNCONFIGURED, "DynamicFont uninitialized.");
- }
-
- //error = FT_New_Face( library, src_path.utf8().get_data(),0,&face );
-
- if (error == FT_Err_Unknown_File_Format) {
- FT_Done_FreeType(library);
- ERR_FAIL_V_MSG(ERR_FILE_CANT_OPEN, "Unknown font format.");
-
- } else if (error) {
- FT_Done_FreeType(library);
- ERR_FAIL_V_MSG(ERR_FILE_CANT_OPEN, "Error loading font.");
- }
-
- if (FT_HAS_COLOR(face) && face->num_fixed_sizes > 0) {
- int best_match = 0;
- int diff = ABS(id.size - ((int64_t)face->available_sizes[0].width));
- scale_color_font = float(id.size * oversampling) / face->available_sizes[0].width;
- for (int i = 1; i < face->num_fixed_sizes; i++) {
- int ndiff = ABS(id.size - ((int64_t)face->available_sizes[i].width));
- if (ndiff < diff) {
- best_match = i;
- diff = ndiff;
- scale_color_font = float(id.size * oversampling) / face->available_sizes[i].width;
- }
- }
- FT_Select_Size(face, best_match);
- } else {
- FT_Set_Pixel_Sizes(face, 0, id.size * oversampling);
- }
-
- ascent = (face->size->metrics.ascender / 64.0) / oversampling * scale_color_font;
- descent = (-face->size->metrics.descender / 64.0) / oversampling * scale_color_font;
- underline_position = -face->underline_position / 64.0 / oversampling * scale_color_font;
- underline_thickness = face->underline_thickness / 64.0 / oversampling * scale_color_font;
- linegap = 0;
-
- valid = true;
- return OK;
-}
-
-float DynamicFontAtSize::font_oversampling = 1.0;
-
-float DynamicFontAtSize::get_height() const {
- return ascent + descent;
-}
-
-float DynamicFontAtSize::get_ascent() const {
- return ascent;
-}
-
-float DynamicFontAtSize::get_descent() const {
- return descent;
-}
-
-float DynamicFontAtSize::get_underline_position() const {
- return underline_position;
-}
-
-float DynamicFontAtSize::get_underline_thickness() const {
- return underline_thickness;
-}
-
-const Pair<const DynamicFontAtSize::Character *, DynamicFontAtSize *> DynamicFontAtSize::_find_char_with_font(char32_t p_char, const Vector<Ref<DynamicFontAtSize>> &p_fallbacks) const {
- const Character *chr = char_map.getptr(p_char);
- ERR_FAIL_COND_V(!chr, (Pair<const Character *, DynamicFontAtSize *>(nullptr, nullptr)));
-
- if (!chr->found) {
- //not found, try in fallbacks
- for (int i = 0; i < p_fallbacks.size(); i++) {
- DynamicFontAtSize *fb = const_cast<DynamicFontAtSize *>(p_fallbacks[i].ptr());
- if (!fb->valid) {
- continue;
- }
-
- fb->_update_char(p_char);
- const Character *fallback_chr = fb->char_map.getptr(p_char);
- ERR_CONTINUE(!fallback_chr);
-
- if (!fallback_chr->found) {
- continue;
- }
-
- return Pair<const Character *, DynamicFontAtSize *>(fallback_chr, fb);
- }
-
- //not found, try 0xFFFD to display 'not found'.
- const_cast<DynamicFontAtSize *>(this)->_update_char(0xFFFD);
- chr = char_map.getptr(0xFFFD);
- ERR_FAIL_COND_V(!chr, (Pair<const Character *, DynamicFontAtSize *>(nullptr, nullptr)));
- }
-
- return Pair<const Character *, DynamicFontAtSize *>(chr, const_cast<DynamicFontAtSize *>(this));
-}
-
-Size2 DynamicFontAtSize::get_char_size(char32_t p_char, char32_t p_next, const Vector<Ref<DynamicFontAtSize>> &p_fallbacks) const {
- if (!valid) {
- return Size2(1, 1);
- }
- const_cast<DynamicFontAtSize *>(this)->_update_char(p_char);
-
- Pair<const Character *, DynamicFontAtSize *> char_pair_with_font = _find_char_with_font(p_char, p_fallbacks);
- const Character *ch = char_pair_with_font.first;
- ERR_FAIL_COND_V(!ch, Size2());
-
- Size2 ret(0, get_height());
-
- if (ch->found) {
- ret.x = ch->advance;
- }
-
- return ret;
-}
-
-String DynamicFontAtSize::get_available_chars() const {
- String chars;
-
- FT_UInt gindex;
- FT_ULong charcode = FT_Get_First_Char(face, &gindex);
- while (gindex != 0) {
- if (charcode != 0) {
- chars += char32_t(charcode);
- }
- charcode = FT_Get_Next_Char(face, charcode, &gindex);
- }
-
- return chars;
-}
-
-float DynamicFontAtSize::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next, const Color &p_modulate, const Vector<Ref<DynamicFontAtSize>> &p_fallbacks, bool p_advance_only, bool p_outline) const {
- if (!valid) {
- return 0;
- }
-
- const_cast<DynamicFontAtSize *>(this)->_update_char(p_char);
-
- Pair<const Character *, DynamicFontAtSize *> char_pair_with_font = _find_char_with_font(p_char, p_fallbacks);
- const Character *ch = char_pair_with_font.first;
- DynamicFontAtSize *font = char_pair_with_font.second;
-
- ERR_FAIL_COND_V(!ch, 0.0);
-
- float advance = 0.0;
-
- // use normal character size if there's no outline character
- if (p_outline && !ch->found) {
- FT_GlyphSlot slot = face->glyph;
- int error = FT_Load_Char(face, p_char, FT_HAS_COLOR(face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT);
- if (!error) {
- error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
- if (!error) {
- Character character = Character::not_found();
- character = const_cast<DynamicFontAtSize *>(this)->_bitmap_to_character(slot->bitmap, slot->bitmap_top, slot->bitmap_left, slot->advance.x / 64.0);
- advance = character.advance;
- }
- }
- }
-
- if (ch->found) {
- ERR_FAIL_COND_V(ch->texture_idx < -1 || ch->texture_idx >= font->textures.size(), 0);
-
- if (!p_advance_only && ch->texture_idx != -1) {
- Point2 cpos = p_pos;
- cpos.x += ch->h_align;
- cpos.y -= font->get_ascent();
- cpos.y += ch->v_align;
- Color modulate = p_modulate;
- if (FT_HAS_COLOR(face)) {
- modulate.r = modulate.g = modulate.b = 1.0;
- }
- RID texture = font->textures[ch->texture_idx].texture->get_rid();
- RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(cpos, ch->rect.size), texture, ch->rect_uv, modulate, false, RID(), RID(), Color(1, 1, 1, 1), false);
- }
-
- advance = ch->advance;
- }
-
- return advance;
-}
-
-unsigned long DynamicFontAtSize::_ft_stream_io(FT_Stream stream, unsigned long offset, unsigned char *buffer, unsigned long count) {
- FileAccess *f = (FileAccess *)stream->descriptor.pointer;
-
- if (f->get_position() != offset) {
- f->seek(offset);
- }
-
- if (count == 0) {
- return 0;
- }
-
- return f->get_buffer(buffer, count);
-}
-
-void DynamicFontAtSize::_ft_stream_close(FT_Stream stream) {
- FileAccess *f = (FileAccess *)stream->descriptor.pointer;
- f->close();
- memdelete(f);
-}
-
-DynamicFontAtSize::Character DynamicFontAtSize::Character::not_found() {
- Character ch;
- ch.texture_idx = -1;
- ch.advance = 0;
- ch.h_align = 0;
- ch.v_align = 0;
- ch.found = false;
- return ch;
-}
-
-DynamicFontAtSize::TexturePosition DynamicFontAtSize::_find_texture_pos_for_glyph(int p_color_size, Image::Format p_image_format, int p_width, int p_height) {
- TexturePosition ret;
- ret.index = -1;
- ret.x = 0;
- ret.y = 0;
-
- int mw = p_width;
- int mh = p_height;
-
- for (int i = 0; i < textures.size(); i++) {
- const CharTexture &ct = textures[i];
-
- if (ct.texture->get_format() != p_image_format) {
- continue;
- }
-
- if (mw > ct.texture_size || mh > ct.texture_size) { //too big for this texture
- continue;
- }
-
- ret.y = 0x7FFFFFFF;
- ret.x = 0;
-
- for (int j = 0; j < ct.texture_size - mw; j++) {
- int max_y = 0;
-
- for (int k = j; k < j + mw; k++) {
- int y = ct.offsets[k];
- if (y > max_y) {
- max_y = y;
- }
- }
-
- if (max_y < ret.y) {
- ret.y = max_y;
- ret.x = j;
- }
- }
-
- if (ret.y == 0x7FFFFFFF || ret.y + mh > ct.texture_size) {
- continue; //fail, could not fit it here
- }
-
- ret.index = i;
- break;
- }
-
- if (ret.index == -1) {
- //could not find texture to fit, create one
- ret.x = 0;
- ret.y = 0;
-
- int texsize = MAX(id.size * oversampling * 8, 256);
- if (mw > texsize) {
- texsize = mw; //special case, adapt to it?
- }
- if (mh > texsize) {
- texsize = mh; //special case, adapt to it?
- }
-
- texsize = next_power_of_2(texsize);
-
- texsize = MIN(texsize, 4096);
-
- CharTexture tex;
- tex.texture_size = texsize;
- tex.imgdata.resize(texsize * texsize * p_color_size); //grayscale alpha
-
- {
- //zero texture
- uint8_t *w = tex.imgdata.ptrw();
- ERR_FAIL_COND_V(texsize * texsize * p_color_size > tex.imgdata.size(), ret);
- for (int i = 0; i < texsize * texsize * p_color_size; i++) {
- w[i] = 0;
- }
- }
- tex.offsets.resize(texsize);
- for (int i = 0; i < texsize; i++) { //zero offsets
- tex.offsets.write[i] = 0;
- }
-
- textures.push_back(tex);
- ret.index = textures.size() - 1;
- }
-
- return ret;
-}
-
-DynamicFontAtSize::Character DynamicFontAtSize::_bitmap_to_character(FT_Bitmap bitmap, int yofs, int xofs, float advance) {
- int w = bitmap.width;
- int h = bitmap.rows;
-
- int mw = w + rect_margin * 2;
- int mh = h + rect_margin * 2;
-
- ERR_FAIL_COND_V(mw > 4096, Character::not_found());
- ERR_FAIL_COND_V(mh > 4096, Character::not_found());
-
- int color_size = bitmap.pixel_mode == FT_PIXEL_MODE_BGRA ? 4 : 2;
- Image::Format require_format = color_size == 4 ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8;
-
- TexturePosition tex_pos = _find_texture_pos_for_glyph(color_size, require_format, mw, mh);
- ERR_FAIL_COND_V(tex_pos.index < 0, Character::not_found());
-
- //fit character in char texture
-
- CharTexture &tex = textures.write[tex_pos.index];
-
- {
- uint8_t *wr = tex.imgdata.ptrw();
-
- for (int i = 0; i < h; i++) {
- for (int j = 0; j < w; j++) {
- int ofs = ((i + tex_pos.y + rect_margin) * tex.texture_size + j + tex_pos.x + rect_margin) * color_size;
- ERR_FAIL_COND_V(ofs >= tex.imgdata.size(), Character::not_found());
- switch (bitmap.pixel_mode) {
- case FT_PIXEL_MODE_MONO: {
- int byte = i * bitmap.pitch + (j >> 3);
- int bit = 1 << (7 - (j % 8));
- wr[ofs + 0] = 255; //grayscale as 1
- wr[ofs + 1] = (bitmap.buffer[byte] & bit) ? 255 : 0;
- } break;
- case FT_PIXEL_MODE_GRAY:
- wr[ofs + 0] = 255; //grayscale as 1
- wr[ofs + 1] = bitmap.buffer[i * bitmap.pitch + j];
- break;
- case FT_PIXEL_MODE_BGRA: {
- int ofs_color = i * bitmap.pitch + (j << 2);
- wr[ofs + 2] = bitmap.buffer[ofs_color + 0];
- wr[ofs + 1] = bitmap.buffer[ofs_color + 1];
- wr[ofs + 0] = bitmap.buffer[ofs_color + 2];
- wr[ofs + 3] = bitmap.buffer[ofs_color + 3];
- } break;
- // TODO: FT_PIXEL_MODE_LCD
- default:
- ERR_FAIL_V_MSG(Character::not_found(), "Font uses unsupported pixel format: " + itos(bitmap.pixel_mode) + ".");
- break;
- }
- }
- }
- }
-
- //blit to image and texture
- {
- Ref<Image> img = memnew(Image(tex.texture_size, tex.texture_size, 0, require_format, tex.imgdata));
-
- if (tex.texture.is_null()) {
- tex.texture.instance();
- tex.texture->create_from_image(img);
- } else {
- tex.texture->update(img); //update
- }
- }
-
- // update height array
-
- for (int k = tex_pos.x; k < tex_pos.x + mw; k++) {
- tex.offsets.write[k] = tex_pos.y + mh;
- }
-
- Character chr;
- chr.h_align = xofs * scale_color_font / oversampling;
- chr.v_align = ascent - (yofs * scale_color_font / oversampling); // + ascent - descent;
- chr.advance = advance * scale_color_font / oversampling;
- chr.texture_idx = tex_pos.index;
- chr.found = true;
-
- chr.rect_uv = Rect2(tex_pos.x + rect_margin, tex_pos.y + rect_margin, w, h);
- chr.rect = chr.rect_uv;
- chr.rect.position /= oversampling;
- chr.rect.size = chr.rect.size * scale_color_font / oversampling;
- return chr;
-}
-
-DynamicFontAtSize::Character DynamicFontAtSize::_make_outline_char(char32_t p_char) {
- Character ret = Character::not_found();
-
- if (FT_Load_Char(face, p_char, FT_LOAD_NO_BITMAP | (font->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0)) != 0) {
- return ret;
- }
-
- FT_Stroker stroker;
- if (FT_Stroker_New(library, &stroker) != 0) {
- return ret;
- }
-
- FT_Stroker_Set(stroker, (int)(id.outline_size * oversampling * 64.0), FT_STROKER_LINECAP_BUTT, FT_STROKER_LINEJOIN_ROUND, 0);
- FT_Glyph glyph;
- FT_BitmapGlyph glyph_bitmap;
-
- if (FT_Get_Glyph(face->glyph, &glyph) != 0) {
- goto cleanup_stroker;
- }
- if (FT_Glyph_Stroke(&glyph, stroker, 1) != 0) {
- goto cleanup_glyph;
- }
- if (FT_Glyph_To_Bitmap(&glyph, font->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, nullptr, 1) != 0) {
- goto cleanup_glyph;
- }
-
- glyph_bitmap = (FT_BitmapGlyph)glyph;
- ret = _bitmap_to_character(glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, glyph->advance.x / 65536.0);
-
-cleanup_glyph:
- FT_Done_Glyph(glyph);
-cleanup_stroker:
- FT_Stroker_Done(stroker);
- return ret;
-}
-
-void DynamicFontAtSize::_update_char(char32_t p_char) {
- if (char_map.has(p_char)) {
- return;
- }
-
- _THREAD_SAFE_METHOD_
-
- Character character = Character::not_found();
-
- FT_GlyphSlot slot = face->glyph;
-
- if (FT_Get_Char_Index(face, p_char) == 0) {
- char_map[p_char] = character;
- return;
- }
-
- int ft_hinting;
-
- switch (font->hinting) {
- case DynamicFontData::HINTING_NONE:
- ft_hinting = FT_LOAD_NO_HINTING;
- break;
- case DynamicFontData::HINTING_LIGHT:
- ft_hinting = FT_LOAD_TARGET_LIGHT;
- break;
- default:
- ft_hinting = FT_LOAD_TARGET_NORMAL;
- break;
- }
-
- int error = FT_Load_Char(face, p_char, FT_HAS_COLOR(face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (font->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0) | ft_hinting);
- if (error) {
- char_map[p_char] = character;
- return;
- }
-
- if (id.outline_size > 0) {
- character = _make_outline_char(p_char);
- } else {
- error = FT_Render_Glyph(face->glyph, font->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);
- if (!error) {
- character = _bitmap_to_character(slot->bitmap, slot->bitmap_top, slot->bitmap_left, slot->advance.x / 64.0);
- }
- }
-
- char_map[p_char] = character;
-}
-
-void DynamicFontAtSize::update_oversampling() {
- if (oversampling == font_oversampling || !valid) {
- return;
- }
-
- FT_Done_FreeType(library);
- textures.clear();
- char_map.clear();
- oversampling = font_oversampling;
- valid = false;
- _load();
-}
-
-DynamicFontAtSize::DynamicFontAtSize() {
- valid = false;
- rect_margin = 1;
- ascent = 1;
- descent = 1;
- linegap = 1;
- oversampling = font_oversampling;
- scale_color_font = 1;
-}
-
-DynamicFontAtSize::~DynamicFontAtSize() {
- if (valid) {
- FT_Done_FreeType(library);
- }
- font->size_cache.erase(id);
- font.unref();
-}
-
-/////////////////////////
-
-void DynamicFont::_reload_cache() {
- ERR_FAIL_COND(cache_id.size < 1);
- if (!data.is_valid()) {
- data_at_size.unref();
- outline_data_at_size.unref();
- fallbacks.resize(0);
- fallback_data_at_size.resize(0);
- fallback_outline_data_at_size.resize(0);
- return;
- }
-
- data_at_size = data->_get_dynamic_font_at_size(cache_id);
- if (outline_cache_id.outline_size > 0) {
- outline_data_at_size = data->_get_dynamic_font_at_size(outline_cache_id);
- fallback_outline_data_at_size.resize(fallback_data_at_size.size());
- } else {
- outline_data_at_size.unref();
- fallback_outline_data_at_size.resize(0);
- }
-
- for (int i = 0; i < fallbacks.size(); i++) {
- fallback_data_at_size.write[i] = fallbacks.write[i]->_get_dynamic_font_at_size(cache_id);
- if (outline_cache_id.outline_size > 0) {
- fallback_outline_data_at_size.write[i] = fallbacks.write[i]->_get_dynamic_font_at_size(outline_cache_id);
- }
- }
-
- emit_changed();
- _change_notify();
-}
-
-void DynamicFont::set_font_data(const Ref<DynamicFontData> &p_data) {
- data = p_data;
- _reload_cache();
-
- emit_changed();
- _change_notify();
-}
-
-Ref<DynamicFontData> DynamicFont::get_font_data() const {
- return data;
-}
-
-void DynamicFont::set_size(int p_size) {
- if (cache_id.size == p_size) {
- return;
- }
- cache_id.size = p_size;
- outline_cache_id.size = p_size;
- _reload_cache();
-}
-
-int DynamicFont::get_size() const {
- return cache_id.size;
-}
-
-void DynamicFont::set_outline_size(int p_size) {
- if (outline_cache_id.outline_size == p_size) {
- return;
- }
- ERR_FAIL_COND(p_size < 0 || p_size > UINT8_MAX);
- outline_cache_id.outline_size = p_size;
- _reload_cache();
-}
-
-int DynamicFont::get_outline_size() const {
- return outline_cache_id.outline_size;
-}
-
-void DynamicFont::set_outline_color(Color p_color) {
- if (p_color != outline_color) {
- outline_color = p_color;
- emit_changed();
- _change_notify();
- }
-}
-
-Color DynamicFont::get_outline_color() const {
- return outline_color;
-}
-
-bool DynamicFontData::is_antialiased() const {
- return antialiased;
-}
-
-void DynamicFontData::set_antialiased(bool p_antialiased) {
- if (antialiased == p_antialiased) {
- return;
- }
- antialiased = p_antialiased;
-}
-
-DynamicFontData::Hinting DynamicFontData::get_hinting() const {
- return hinting;
-}
-
-void DynamicFontData::set_hinting(Hinting p_hinting) {
- if (hinting == p_hinting) {
- return;
- }
- hinting = p_hinting;
-}
-
-int DynamicFont::get_spacing(int p_type) const {
- if (p_type == SPACING_TOP) {
- return spacing_top;
- } else if (p_type == SPACING_BOTTOM) {
- return spacing_bottom;
- } else if (p_type == SPACING_CHAR) {
- return spacing_char;
- } else if (p_type == SPACING_SPACE) {
- return spacing_space;
- }
-
- return 0;
-}
-
-void DynamicFont::set_spacing(int p_type, int p_value) {
- if (p_type == SPACING_TOP) {
- spacing_top = p_value;
- } else if (p_type == SPACING_BOTTOM) {
- spacing_bottom = p_value;
- } else if (p_type == SPACING_CHAR) {
- spacing_char = p_value;
- } else if (p_type == SPACING_SPACE) {
- spacing_space = p_value;
- }
-
- emit_changed();
- _change_notify();
-}
-
-float DynamicFont::get_height() const {
- if (!data_at_size.is_valid()) {
- return 1;
- }
-
- return data_at_size->get_height() + spacing_top + spacing_bottom;
-}
-
-float DynamicFont::get_ascent() const {
- if (!data_at_size.is_valid()) {
- return 1;
- }
-
- return data_at_size->get_ascent() + spacing_top;
-}
-
-float DynamicFont::get_descent() const {
- if (!data_at_size.is_valid()) {
- return 1;
- }
-
- return data_at_size->get_descent() + spacing_bottom;
-}
-
-float DynamicFont::get_underline_position() const {
- if (!data_at_size.is_valid()) {
- return 2;
- }
-
- return data_at_size->get_underline_position();
-}
-
-float DynamicFont::get_underline_thickness() const {
- if (!data_at_size.is_valid()) {
- return 1;
- }
-
- return data_at_size->get_underline_thickness();
-}
-
-Size2 DynamicFont::get_char_size(char32_t p_char, char32_t p_next) const {
- if (!data_at_size.is_valid()) {
- return Size2(1, 1);
- }
-
- Size2 ret = data_at_size->get_char_size(p_char, p_next, fallback_data_at_size);
- if (p_char == ' ') {
- ret.width += spacing_space + spacing_char;
- } else if (p_next) {
- ret.width += spacing_char;
- }
-
- return ret;
-}
-
-String DynamicFont::get_available_chars() const {
- if (!data_at_size.is_valid()) {
- return "";
- }
-
- String chars = data_at_size->get_available_chars();
-
- for (int i = 0; i < fallback_data_at_size.size(); i++) {
- String fallback_chars = fallback_data_at_size[i]->get_available_chars();
- for (int j = 0; j < fallback_chars.length(); j++) {
- if (chars.find_char(fallback_chars[j]) == -1) {
- chars += fallback_chars[j];
- }
- }
- }
-
- return chars;
-}
-
-bool DynamicFont::is_distance_field_hint() const {
- return false;
-}
-
-bool DynamicFont::has_outline() const {
- return outline_cache_id.outline_size > 0;
-}
-
-float DynamicFont::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next, const Color &p_modulate, bool p_outline) const {
- const Ref<DynamicFontAtSize> &font_at_size = p_outline && outline_cache_id.outline_size > 0 ? outline_data_at_size : data_at_size;
-
- if (!font_at_size.is_valid()) {
- return 0;
- }
-
- const Vector<Ref<DynamicFontAtSize>> &fallbacks = p_outline && outline_cache_id.outline_size > 0 ? fallback_outline_data_at_size : fallback_data_at_size;
- Color color = p_outline && outline_cache_id.outline_size > 0 ? p_modulate * outline_color : p_modulate;
-
- // If requested outline draw, but no outline is present, simply return advance without drawing anything
- bool advance_only = p_outline && outline_cache_id.outline_size == 0;
- return font_at_size->draw_char(p_canvas_item, p_pos, p_char, p_next, color, fallbacks, advance_only, p_outline) + spacing_char;
-}
-
-void DynamicFont::set_fallback(int p_idx, const Ref<DynamicFontData> &p_data) {
- ERR_FAIL_COND(p_data.is_null());
- ERR_FAIL_INDEX(p_idx, fallbacks.size());
- fallbacks.write[p_idx] = p_data;
- fallback_data_at_size.write[p_idx] = fallbacks.write[p_idx]->_get_dynamic_font_at_size(cache_id);
-}
-
-void DynamicFont::add_fallback(const Ref<DynamicFontData> &p_data) {
- ERR_FAIL_COND(p_data.is_null());
- fallbacks.push_back(p_data);
- fallback_data_at_size.push_back(fallbacks.write[fallbacks.size() - 1]->_get_dynamic_font_at_size(cache_id)); //const..
- if (outline_cache_id.outline_size > 0) {
- fallback_outline_data_at_size.push_back(fallbacks.write[fallbacks.size() - 1]->_get_dynamic_font_at_size(outline_cache_id));
- }
-
- _change_notify();
- emit_changed();
- _change_notify();
-}
-
-int DynamicFont::get_fallback_count() const {
- return fallbacks.size();
-}
-
-Ref<DynamicFontData> DynamicFont::get_fallback(int p_idx) const {
- ERR_FAIL_INDEX_V(p_idx, fallbacks.size(), Ref<DynamicFontData>());
-
- return fallbacks[p_idx];
-}
-
-void DynamicFont::remove_fallback(int p_idx) {
- ERR_FAIL_INDEX(p_idx, fallbacks.size());
- fallbacks.remove(p_idx);
- fallback_data_at_size.remove(p_idx);
- emit_changed();
- _change_notify();
-}
-
-bool DynamicFont::_set(const StringName &p_name, const Variant &p_value) {
- String str = p_name;
- if (str.begins_with("fallback/")) {
- int idx = str.get_slicec('/', 1).to_int();
- Ref<DynamicFontData> fd = p_value;
-
- if (fd.is_valid()) {
- if (idx == fallbacks.size()) {
- add_fallback(fd);
- return true;
- } else if (idx >= 0 && idx < fallbacks.size()) {
- set_fallback(idx, fd);
- return true;
- } else {
- return false;
- }
- } else if (idx >= 0 && idx < fallbacks.size()) {
- remove_fallback(idx);
- return true;
- }
- }
-
- return false;
-}
-
-bool DynamicFont::_get(const StringName &p_name, Variant &r_ret) const {
- String str = p_name;
- if (str.begins_with("fallback/")) {
- int idx = str.get_slicec('/', 1).to_int();
-
- if (idx == fallbacks.size()) {
- r_ret = Ref<DynamicFontData>();
- return true;
- } else if (idx >= 0 && idx < fallbacks.size()) {
- r_ret = get_fallback(idx);
- return true;
- }
- }
-
- return false;
-}
-
-void DynamicFont::_get_property_list(List<PropertyInfo> *p_list) const {
- for (int i = 0; i < fallbacks.size(); i++) {
- p_list->push_back(PropertyInfo(Variant::OBJECT, "fallback/" + itos(i), PROPERTY_HINT_RESOURCE_TYPE, "DynamicFontData"));
- }
-
- p_list->push_back(PropertyInfo(Variant::OBJECT, "fallback/" + itos(fallbacks.size()), PROPERTY_HINT_RESOURCE_TYPE, "DynamicFontData"));
-}
-
-void DynamicFont::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_font_data", "data"), &DynamicFont::set_font_data);
- ClassDB::bind_method(D_METHOD("get_font_data"), &DynamicFont::get_font_data);
-
- ClassDB::bind_method(D_METHOD("get_available_chars"), &DynamicFont::get_available_chars);
-
- ClassDB::bind_method(D_METHOD("set_size", "data"), &DynamicFont::set_size);
- ClassDB::bind_method(D_METHOD("get_size"), &DynamicFont::get_size);
-
- ClassDB::bind_method(D_METHOD("set_outline_size", "size"), &DynamicFont::set_outline_size);
- ClassDB::bind_method(D_METHOD("get_outline_size"), &DynamicFont::get_outline_size);
-
- ClassDB::bind_method(D_METHOD("set_outline_color", "color"), &DynamicFont::set_outline_color);
- ClassDB::bind_method(D_METHOD("get_outline_color"), &DynamicFont::get_outline_color);
-
- ClassDB::bind_method(D_METHOD("set_spacing", "type", "value"), &DynamicFont::set_spacing);
- ClassDB::bind_method(D_METHOD("get_spacing", "type"), &DynamicFont::get_spacing);
-
- ClassDB::bind_method(D_METHOD("add_fallback", "data"), &DynamicFont::add_fallback);
- ClassDB::bind_method(D_METHOD("set_fallback", "idx", "data"), &DynamicFont::set_fallback);
- ClassDB::bind_method(D_METHOD("get_fallback", "idx"), &DynamicFont::get_fallback);
- ClassDB::bind_method(D_METHOD("remove_fallback", "idx"), &DynamicFont::remove_fallback);
- ClassDB::bind_method(D_METHOD("get_fallback_count"), &DynamicFont::get_fallback_count);
-
- ADD_GROUP("Settings", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "size", PROPERTY_HINT_RANGE, "1,1024,1"), "set_size", "get_size");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "outline_size", PROPERTY_HINT_RANGE, "0,1024,1"), "set_outline_size", "get_outline_size");
- ADD_PROPERTY(PropertyInfo(Variant::COLOR, "outline_color"), "set_outline_color", "get_outline_color");
- ADD_GROUP("Extra Spacing", "extra_spacing");
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_top"), "set_spacing", "get_spacing", SPACING_TOP);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_bottom"), "set_spacing", "get_spacing", SPACING_BOTTOM);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_char"), "set_spacing", "get_spacing", SPACING_CHAR);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_space"), "set_spacing", "get_spacing", SPACING_SPACE);
- ADD_GROUP("Font", "");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "font_data", PROPERTY_HINT_RESOURCE_TYPE, "DynamicFontData"), "set_font_data", "get_font_data");
-
- BIND_ENUM_CONSTANT(SPACING_TOP);
- BIND_ENUM_CONSTANT(SPACING_BOTTOM);
- BIND_ENUM_CONSTANT(SPACING_CHAR);
- BIND_ENUM_CONSTANT(SPACING_SPACE);
-}
-
-Mutex DynamicFont::dynamic_font_mutex;
-
-SelfList<DynamicFont>::List *DynamicFont::dynamic_fonts = nullptr;
-
-DynamicFont::DynamicFont() :
- font_list(this) {
- valid = false;
- cache_id.size = 16;
- outline_cache_id.size = 16;
- spacing_top = 0;
- spacing_bottom = 0;
- spacing_char = 0;
- spacing_space = 0;
- outline_color = Color(1, 1, 1);
-
- MutexLock lock(dynamic_font_mutex);
- dynamic_fonts->add(&font_list);
-}
-
-DynamicFont::~DynamicFont() {
- MutexLock lock(dynamic_font_mutex);
- dynamic_fonts->remove(&font_list);
-}
-
-void DynamicFont::initialize_dynamic_fonts() {
- dynamic_fonts = memnew(SelfList<DynamicFont>::List());
-}
-
-void DynamicFont::finish_dynamic_fonts() {
- memdelete(dynamic_fonts);
- dynamic_fonts = nullptr;
-}
-
-void DynamicFont::update_oversampling() {
- Vector<Ref<DynamicFont>> changed;
- {
- MutexLock lock(dynamic_font_mutex);
-
- SelfList<DynamicFont> *E = dynamic_fonts->first();
- while (E) {
- if (E->self()->data_at_size.is_valid()) {
- E->self()->data_at_size->update_oversampling();
-
- if (E->self()->outline_data_at_size.is_valid()) {
- E->self()->outline_data_at_size->update_oversampling();
- }
-
- for (int i = 0; i < E->self()->fallback_data_at_size.size(); i++) {
- if (E->self()->fallback_data_at_size[i].is_valid()) {
- E->self()->fallback_data_at_size.write[i]->update_oversampling();
-
- if (E->self()->has_outline() && E->self()->fallback_outline_data_at_size[i].is_valid()) {
- E->self()->fallback_outline_data_at_size.write[i]->update_oversampling();
- }
- }
- }
-
- changed.push_back(Ref<DynamicFont>(E->self()));
- }
-
- E = E->next();
- }
- }
-
- for (int i = 0; i < changed.size(); i++) {
- changed.write[i]->emit_changed();
- }
-}
-
-/////////////////////////
-
-RES ResourceFormatLoaderDynamicFont::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
- if (r_error) {
- *r_error = ERR_FILE_CANT_OPEN;
- }
-
- Ref<DynamicFontData> dfont;
- dfont.instance();
- dfont->set_font_path(p_path);
-
- if (r_error) {
- *r_error = OK;
- }
-
- return dfont;
-}
-
-void ResourceFormatLoaderDynamicFont::get_recognized_extensions(List<String> *p_extensions) const {
- p_extensions->push_back("ttf");
- p_extensions->push_back("otf");
-}
-
-bool ResourceFormatLoaderDynamicFont::handles_type(const String &p_type) const {
- return (p_type == "DynamicFontData");
-}
-
-String ResourceFormatLoaderDynamicFont::get_resource_type(const String &p_path) const {
- String el = p_path.get_extension().to_lower();
- if (el == "ttf" || el == "otf") {
- return "DynamicFontData";
- }
- return "";
-}
-
-#endif
diff --git a/scene/resources/dynamic_font.h b/scene/resources/dynamic_font.h
deleted file mode 100644
index a881e21da8..0000000000
--- a/scene/resources/dynamic_font.h
+++ /dev/null
@@ -1,316 +0,0 @@
-/*************************************************************************/
-/* dynamic_font.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 DYNAMIC_FONT_H
-#define DYNAMIC_FONT_H
-
-#include "modules/modules_enabled.gen.h"
-#ifdef MODULE_FREETYPE_ENABLED
-
-#include "core/io/resource_loader.h"
-#include "core/os/mutex.h"
-#include "core/os/thread_safe.h"
-#include "core/pair.h"
-#include "scene/resources/font.h"
-
-#include <ft2build.h>
-#include FT_FREETYPE_H
-
-class DynamicFontAtSize;
-class DynamicFont;
-
-class DynamicFontData : public Resource {
- GDCLASS(DynamicFontData, Resource);
-
-public:
- struct CacheID {
- union {
- struct {
- uint32_t size : 16;
- uint32_t outline_size : 8;
- uint32_t unused : 8;
- };
- uint32_t key;
- };
- bool operator<(CacheID right) const;
- CacheID() {
- key = 0;
- }
- };
-
- enum Hinting {
- HINTING_NONE,
- HINTING_LIGHT,
- HINTING_NORMAL
- };
-
- bool is_antialiased() const;
- void set_antialiased(bool p_antialiased);
- Hinting get_hinting() const;
- void set_hinting(Hinting p_hinting);
-
-private:
- const uint8_t *font_mem;
- int font_mem_size;
- bool antialiased;
- bool force_autohinter;
- Hinting hinting;
-
- String font_path;
- Map<CacheID, DynamicFontAtSize *> size_cache;
-
- friend class DynamicFontAtSize;
-
- friend class DynamicFont;
-
- Ref<DynamicFontAtSize> _get_dynamic_font_at_size(CacheID p_cache_id);
-
-protected:
- static void _bind_methods();
-
-public:
- void set_font_ptr(const uint8_t *p_font_mem, int p_font_mem_size);
- void set_font_path(const String &p_path);
- String get_font_path() const;
- void set_force_autohinter(bool p_force);
-
- DynamicFontData();
- ~DynamicFontData();
-};
-
-VARIANT_ENUM_CAST(DynamicFontData::Hinting);
-
-class DynamicFontAtSize : public Reference {
- GDCLASS(DynamicFontAtSize, Reference);
-
- _THREAD_SAFE_CLASS_
-
- FT_Library library; /* handle to library */
- FT_Face face; /* handle to face object */
- FT_StreamRec stream;
-
- float ascent;
- float descent;
- float linegap;
- float rect_margin;
- float oversampling;
- float scale_color_font;
- float underline_position;
- float underline_thickness;
-
- bool valid;
-
- struct CharTexture {
- Vector<uint8_t> imgdata;
- int texture_size;
- Vector<int> offsets;
- Ref<ImageTexture> texture;
- };
-
- Vector<CharTexture> textures;
-
- struct Character {
- bool found;
- int texture_idx;
- Rect2 rect;
- Rect2 rect_uv;
- float v_align;
- float h_align;
- float advance;
-
- Character() {
- texture_idx = 0;
- v_align = 0;
- }
-
- static Character not_found();
- };
-
- struct TexturePosition {
- int index;
- int x;
- int y;
- };
-
- const Pair<const Character *, DynamicFontAtSize *> _find_char_with_font(char32_t p_char, const Vector<Ref<DynamicFontAtSize>> &p_fallbacks) const;
- Character _make_outline_char(char32_t p_char);
- TexturePosition _find_texture_pos_for_glyph(int p_color_size, Image::Format p_image_format, int p_width, int p_height);
- Character _bitmap_to_character(FT_Bitmap bitmap, int yofs, int xofs, float advance);
-
- static unsigned long _ft_stream_io(FT_Stream stream, unsigned long offset, unsigned char *buffer, unsigned long count);
- static void _ft_stream_close(FT_Stream stream);
-
- HashMap<char32_t, Character> char_map;
-
- _FORCE_INLINE_ void _update_char(char32_t p_char);
-
- friend class DynamicFontData;
- Ref<DynamicFontData> font;
- DynamicFontData::CacheID id;
-
- static HashMap<String, Vector<uint8_t>> _fontdata;
- Error _load();
-
-public:
- static float font_oversampling;
-
- float get_height() const;
-
- float get_ascent() const;
- float get_descent() const;
- float get_underline_position() const;
- float get_underline_thickness() const;
-
- Size2 get_char_size(char32_t p_char, char32_t p_next, const Vector<Ref<DynamicFontAtSize>> &p_fallbacks) const;
- String get_available_chars() const;
-
- float draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next, const Color &p_modulate, const Vector<Ref<DynamicFontAtSize>> &p_fallbacks, bool p_advance_only = false, bool p_outline = false) const;
-
- void set_texture_flags(uint32_t p_flags);
- void update_oversampling();
-
- DynamicFontAtSize();
- ~DynamicFontAtSize();
-};
-
-///////////////
-
-class DynamicFont : public Font {
- GDCLASS(DynamicFont, Font);
-
-public:
- enum SpacingType {
- SPACING_TOP,
- SPACING_BOTTOM,
- SPACING_CHAR,
- SPACING_SPACE
- };
-
-private:
- Ref<DynamicFontData> data;
- Ref<DynamicFontAtSize> data_at_size;
- Ref<DynamicFontAtSize> outline_data_at_size;
-
- Vector<Ref<DynamicFontData>> fallbacks;
- Vector<Ref<DynamicFontAtSize>> fallback_data_at_size;
- Vector<Ref<DynamicFontAtSize>> fallback_outline_data_at_size;
-
- DynamicFontData::CacheID cache_id;
- DynamicFontData::CacheID outline_cache_id;
-
- bool valid;
- int spacing_top;
- int spacing_bottom;
- int spacing_char;
- int spacing_space;
-
- Color outline_color;
-
-protected:
- void _reload_cache();
-
- bool _set(const StringName &p_name, const Variant &p_value);
- bool _get(const StringName &p_name, Variant &r_ret) const;
- void _get_property_list(List<PropertyInfo> *p_list) const;
-
- static void _bind_methods();
-
-public:
- void set_font_data(const Ref<DynamicFontData> &p_data);
- Ref<DynamicFontData> get_font_data() const;
-
- void set_size(int p_size);
- int get_size() const;
-
- void set_outline_size(int p_size);
- int get_outline_size() const;
-
- void set_outline_color(Color p_color);
- Color get_outline_color() const;
-
- bool get_use_mipmaps() const;
- void set_use_mipmaps(bool p_enable);
-
- bool get_use_filter() const;
- void set_use_filter(bool p_enable);
-
- int get_spacing(int p_type) const;
- void set_spacing(int p_type, int p_value);
-
- void add_fallback(const Ref<DynamicFontData> &p_data);
- void set_fallback(int p_idx, const Ref<DynamicFontData> &p_data);
- int get_fallback_count() const;
- Ref<DynamicFontData> get_fallback(int p_idx) const;
- void remove_fallback(int p_idx);
-
- virtual float get_height() const override;
-
- virtual float get_ascent() const override;
- virtual float get_descent() const override;
- virtual float get_underline_position() const override;
- virtual float get_underline_thickness() const override;
-
- virtual Size2 get_char_size(char32_t p_char, char32_t p_next = 0) const override;
- String get_available_chars() const;
-
- virtual bool is_distance_field_hint() const override;
-
- virtual bool has_outline() const override;
-
- virtual float draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const override;
-
- SelfList<DynamicFont> font_list;
-
- static Mutex dynamic_font_mutex;
- static SelfList<DynamicFont>::List *dynamic_fonts;
-
- static void initialize_dynamic_fonts();
- static void finish_dynamic_fonts();
- static void update_oversampling();
-
- DynamicFont();
- ~DynamicFont();
-};
-
-VARIANT_ENUM_CAST(DynamicFont::SpacingType);
-
-/////////////
-
-class ResourceFormatLoaderDynamicFont : public ResourceFormatLoader {
-public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
- virtual void get_recognized_extensions(List<String> *p_extensions) const;
- virtual bool handles_type(const String &p_type) const;
- virtual String get_resource_type(const String &p_path) const;
-};
-
-#endif
-
-#endif
diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp
index ee8e63266d..cab6c0378a 100644
--- a/scene/resources/environment.cpp
+++ b/scene/resources/environment.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,7 +30,8 @@
#include "environment.h"
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
+#include "core/core_string_names.h"
#include "servers/rendering_server.h"
#include "texture.h"
@@ -43,7 +44,10 @@ RID Environment::get_rid() const {
void Environment::set_background(BGMode p_bg) {
bg_mode = p_bg;
RS::get_singleton()->environment_set_background(environment, RS::EnvironmentBG(p_bg));
- _change_notify();
+ notify_property_list_changed();
+ if (bg_mode != BG_SKY) {
+ set_fog_aerial_perspective(0.0);
+ }
}
Environment::BGMode Environment::get_background() const {
@@ -134,7 +138,7 @@ Color Environment::get_ambient_light_color() const {
void Environment::set_ambient_source(AmbientSource p_source) {
ambient_source = p_source;
_update_ambient_light();
- _change_notify();
+ notify_property_list_changed();
}
Environment::AmbientSource Environment::get_ambient_source() const {
@@ -162,7 +166,7 @@ float Environment::get_ambient_light_sky_contribution() const {
void Environment::set_reflection_source(ReflectionSource p_source) {
reflection_source = p_source;
_update_ambient_light();
- _change_notify();
+ notify_property_list_changed();
}
Environment::ReflectionSource Environment::get_reflection_source() const {
@@ -220,7 +224,7 @@ float Environment::get_tonemap_white() const {
void Environment::set_tonemap_auto_exposure_enabled(bool p_enabled) {
tonemap_auto_exposure_enabled = p_enabled;
_update_tonemap();
- _change_notify();
+ notify_property_list_changed();
}
bool Environment::is_tonemap_auto_exposure_enabled() const {
@@ -281,7 +285,7 @@ void Environment::_update_tonemap() {
void Environment::set_ssr_enabled(bool p_enabled) {
ssr_enabled = p_enabled;
_update_ssr();
- _change_notify();
+ notify_property_list_changed();
}
bool Environment::is_ssr_enabled() const {
@@ -339,7 +343,7 @@ void Environment::_update_ssr() {
void Environment::set_ssao_enabled(bool p_enabled) {
ssao_enabled = p_enabled;
_update_ssao();
- _change_notify();
+ notify_property_list_changed();
}
bool Environment::is_ssao_enabled() const {
@@ -364,49 +368,58 @@ float Environment::get_ssao_intensity() const {
return ssao_intensity;
}
-void Environment::set_ssao_bias(float p_bias) {
- ssao_bias = p_bias;
+void Environment::set_ssao_power(float p_power) {
+ ssao_power = p_power;
_update_ssao();
}
-float Environment::get_ssao_bias() const {
- return ssao_bias;
+float Environment::get_ssao_power() const {
+ return ssao_power;
}
-void Environment::set_ssao_direct_light_affect(float p_direct_light_affect) {
- ssao_direct_light_affect = p_direct_light_affect;
+void Environment::set_ssao_detail(float p_detail) {
+ ssao_detail = p_detail;
_update_ssao();
}
-float Environment::get_ssao_direct_light_affect() const {
- return ssao_direct_light_affect;
+float Environment::get_ssao_detail() const {
+ return ssao_detail;
}
-void Environment::set_ssao_ao_channel_affect(float p_ao_channel_affect) {
- ssao_ao_channel_affect = p_ao_channel_affect;
+void Environment::set_ssao_horizon(float p_horizon) {
+ ssao_horizon = p_horizon;
_update_ssao();
}
-float Environment::get_ssao_ao_channel_affect() const {
- return ssao_ao_channel_affect;
+float Environment::get_ssao_horizon() const {
+ return ssao_horizon;
+}
+
+void Environment::set_ssao_sharpness(float p_sharpness) {
+ ssao_sharpness = p_sharpness;
+ _update_ssao();
+}
+
+float Environment::get_ssao_sharpness() const {
+ return ssao_sharpness;
}
-void Environment::set_ssao_blur(SSAOBlur p_blur) {
- ssao_blur = p_blur;
+void Environment::set_ssao_direct_light_affect(float p_direct_light_affect) {
+ ssao_direct_light_affect = p_direct_light_affect;
_update_ssao();
}
-Environment::SSAOBlur Environment::get_ssao_blur() const {
- return ssao_blur;
+float Environment::get_ssao_direct_light_affect() const {
+ return ssao_direct_light_affect;
}
-void Environment::set_ssao_edge_sharpness(float p_edge_sharpness) {
- ssao_edge_sharpness = p_edge_sharpness;
+void Environment::set_ssao_ao_channel_affect(float p_ao_channel_affect) {
+ ssao_ao_channel_affect = p_ao_channel_affect;
_update_ssao();
}
-float Environment::get_ssao_edge_sharpness() const {
- return ssao_edge_sharpness;
+float Environment::get_ssao_ao_channel_affect() const {
+ return ssao_ao_channel_affect;
}
void Environment::_update_ssao() {
@@ -415,11 +428,12 @@ void Environment::_update_ssao() {
ssao_enabled,
ssao_radius,
ssao_intensity,
- ssao_bias,
+ ssao_power,
+ ssao_detail,
+ ssao_horizon,
+ ssao_sharpness,
ssao_direct_light_affect,
- ssao_ao_channel_affect,
- RS::EnvironmentSSAOBlur(ssao_blur),
- ssao_edge_sharpness);
+ ssao_ao_channel_affect);
}
// SDFGI
@@ -427,6 +441,7 @@ void Environment::_update_ssao() {
void Environment::set_sdfgi_enabled(bool p_enabled) {
sdfgi_enabled = p_enabled;
_update_sdfgi();
+ notify_property_list_changed();
}
bool Environment::is_sdfgi_enabled() const {
@@ -434,6 +449,7 @@ bool Environment::is_sdfgi_enabled() const {
}
void Environment::set_sdfgi_cascades(SDFGICascades p_cascades) {
+ ERR_FAIL_INDEX(p_cascades, SDFGI_CASCADES_8 + 1);
sdfgi_cascades = p_cascades;
_update_sdfgi();
}
@@ -444,8 +460,6 @@ Environment::SDFGICascades Environment::get_sdfgi_cascades() const {
void Environment::set_sdfgi_min_cell_size(float p_size) {
sdfgi_min_cell_size = p_size;
- _change_notify("sdfgi_max_distance");
- _change_notify("sdfgi_cascade0_distance");
_update_sdfgi();
}
@@ -461,8 +475,6 @@ void Environment::set_sdfgi_max_distance(float p_distance) {
p_distance *= 0.5; //halve for each cascade
}
sdfgi_min_cell_size = p_distance;
- _change_notify("sdfgi_min_cell_size");
- _change_notify("sdfgi_cascade0_distance");
_update_sdfgi();
}
@@ -479,8 +491,6 @@ float Environment::get_sdfgi_max_distance() const {
void Environment::set_sdfgi_cascade0_distance(float p_distance) {
sdfgi_min_cell_size = p_distance / 64.0;
- _change_notify("sdfgi_min_cell_size");
- _change_notify("sdfgi_max_distance");
_update_sdfgi();
}
@@ -506,13 +516,12 @@ bool Environment::is_sdfgi_using_occlusion() const {
return sdfgi_use_occlusion;
}
-void Environment::set_sdfgi_use_multi_bounce(bool p_enabled) {
- sdfgi_use_multibounce = p_enabled;
+void Environment::set_sdfgi_bounce_feedback(float p_amount) {
+ sdfgi_bounce_feedback = p_amount;
_update_sdfgi();
}
-
-bool Environment::is_sdfgi_using_multi_bounce() const {
- return sdfgi_use_multibounce;
+float Environment::get_sdfgi_bounce_feedback() const {
+ return sdfgi_bounce_feedback;
}
void Environment::set_sdfgi_read_sky_light(bool p_enabled) {
@@ -559,7 +568,7 @@ void Environment::_update_sdfgi() {
sdfgi_min_cell_size,
RS::EnvironmentSDFGIYScale(sdfgi_y_scale),
sdfgi_use_occlusion,
- sdfgi_use_multibounce,
+ sdfgi_bounce_feedback,
sdfgi_read_sky_light,
sdfgi_energy,
sdfgi_normal_bias,
@@ -571,29 +580,35 @@ void Environment::_update_sdfgi() {
void Environment::set_glow_enabled(bool p_enabled) {
glow_enabled = p_enabled;
_update_glow();
- _change_notify();
+ notify_property_list_changed();
}
bool Environment::is_glow_enabled() const {
return glow_enabled;
}
-void Environment::set_glow_level_enabled(int p_level, bool p_enabled) {
+void Environment::set_glow_level(int p_level, float p_intensity) {
ERR_FAIL_INDEX(p_level, RS::MAX_GLOW_LEVELS);
- if (p_enabled) {
- glow_levels |= (1 << p_level);
- } else {
- glow_levels &= ~(1 << p_level);
- }
+ glow_levels.write[p_level] = p_intensity;
_update_glow();
}
-bool Environment::is_glow_level_enabled(int p_level) const {
- ERR_FAIL_INDEX_V(p_level, RS::MAX_GLOW_LEVELS, false);
+float Environment::get_glow_level(int p_level) const {
+ ERR_FAIL_INDEX_V(p_level, RS::MAX_GLOW_LEVELS, 0.0);
+
+ return glow_levels[p_level];
+}
+
+void Environment::set_glow_normalized(bool p_normalized) {
+ glow_normalize_levels = p_normalized;
- return glow_levels & (1 << p_level);
+ _update_glow();
+}
+
+bool Environment::is_glow_normalized() const {
+ return glow_normalize_levels;
}
void Environment::set_glow_intensity(float p_intensity) {
@@ -635,7 +650,7 @@ float Environment::get_glow_bloom() const {
void Environment::set_glow_blend_mode(GlowBlendMode p_mode) {
glow_blend_mode = p_mode;
_update_glow();
- _change_notify();
+ notify_property_list_changed();
}
Environment::GlowBlendMode Environment::get_glow_blend_mode() const {
@@ -670,10 +685,24 @@ float Environment::get_glow_hdr_luminance_cap() const {
}
void Environment::_update_glow() {
+ Vector<float> normalized_levels;
+ if (glow_normalize_levels) {
+ normalized_levels.resize(7);
+ float size = 0.0;
+ for (int i = 0; i < glow_levels.size(); i++) {
+ size += glow_levels[i];
+ }
+ for (int i = 0; i < glow_levels.size(); i++) {
+ normalized_levels.write[i] = glow_levels[i] / size;
+ }
+ } else {
+ normalized_levels = glow_levels;
+ }
+
RS::get_singleton()->environment_set_glow(
environment,
glow_enabled,
- glow_levels,
+ normalized_levels,
glow_intensity,
glow_strength,
glow_mix,
@@ -689,7 +718,7 @@ void Environment::_update_glow() {
void Environment::set_fog_enabled(bool p_enabled) {
fog_enabled = p_enabled;
_update_fog();
- _change_notify();
+ notify_property_list_changed();
}
bool Environment::is_fog_enabled() const {
@@ -739,6 +768,14 @@ float Environment::get_fog_height_density() const {
return fog_height_density;
}
+void Environment::set_fog_aerial_perspective(float p_aerial_perspective) {
+ fog_aerial_perspective = p_aerial_perspective;
+ _update_fog();
+}
+float Environment::get_fog_aerial_perspective() const {
+ return fog_aerial_perspective;
+}
+
void Environment::_update_fog() {
RS::get_singleton()->environment_set_fog(
environment,
@@ -748,19 +785,20 @@ void Environment::_update_fog() {
fog_sun_scatter,
fog_density,
fog_height,
- fog_height_density);
+ fog_height_density,
+ fog_aerial_perspective);
}
// Volumetric Fog
void Environment::_update_volumetric_fog() {
- RS::get_singleton()->environment_set_volumetric_fog(environment, volumetric_fog_enabled, volumetric_fog_density, volumetric_fog_light, volumetric_fog_light_energy, volumetric_fog_length, volumetric_fog_detail_spread, volumetric_fog_gi_inject, RS::EnvVolumetricFogShadowFilter(volumetric_fog_shadow_filter));
+ RS::get_singleton()->environment_set_volumetric_fog(environment, volumetric_fog_enabled, volumetric_fog_density, volumetric_fog_light, volumetric_fog_light_energy, volumetric_fog_length, volumetric_fog_detail_spread, volumetric_fog_gi_inject, volumetric_fog_temporal_reproject, volumetric_fog_temporal_reproject_amount);
}
void Environment::set_volumetric_fog_enabled(bool p_enable) {
volumetric_fog_enabled = p_enable;
_update_volumetric_fog();
- _change_notify();
+ notify_property_list_changed();
}
bool Environment::is_volumetric_fog_enabled() const {
@@ -811,13 +849,20 @@ float Environment::get_volumetric_fog_gi_inject() const {
return volumetric_fog_gi_inject;
}
-void Environment::set_volumetric_fog_shadow_filter(VolumetricFogShadowFilter p_filter) {
- volumetric_fog_shadow_filter = p_filter;
+void Environment::set_volumetric_fog_temporal_reprojection_enabled(bool p_enable) {
+ volumetric_fog_temporal_reproject = p_enable;
+ _update_volumetric_fog();
+}
+bool Environment::is_volumetric_fog_temporal_reprojection_enabled() const {
+ return volumetric_fog_temporal_reproject;
+}
+void Environment::set_volumetric_fog_temporal_reprojection_amount(float p_amount) {
+ volumetric_fog_temporal_reproject_amount = p_amount;
_update_volumetric_fog();
}
-Environment::VolumetricFogShadowFilter Environment::get_volumetric_fog_shadow_filter() const {
- return volumetric_fog_shadow_filter;
+float Environment::get_volumetric_fog_temporal_reprojection_amount() const {
+ return volumetric_fog_temporal_reproject_amount;
}
// Adjustment
@@ -825,7 +870,7 @@ Environment::VolumetricFogShadowFilter Environment::get_volumetric_fog_shadow_fi
void Environment::set_adjustment_enabled(bool p_enabled) {
adjustment_enabled = p_enabled;
_update_adjustment();
- _change_notify();
+ notify_property_list_changed();
}
bool Environment::is_adjustment_enabled() const {
@@ -859,23 +904,38 @@ float Environment::get_adjustment_saturation() const {
return adjustment_saturation;
}
-void Environment::set_adjustment_color_correction(const Ref<Texture2D> &p_ramp) {
- adjustment_color_correction = p_ramp;
+void Environment::set_adjustment_color_correction(Ref<Texture> p_color_correction) {
+ adjustment_color_correction = p_color_correction;
+ Ref<GradientTexture> grad_tex = p_color_correction;
+ if (grad_tex.is_valid()) {
+ if (!grad_tex->is_connected(CoreStringNames::get_singleton()->changed, callable_mp(this, &Environment::_update_adjustment))) {
+ grad_tex->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Environment::_update_adjustment));
+ }
+ }
+ Ref<Texture2D> adjustment_texture_2d = adjustment_color_correction;
+ if (adjustment_texture_2d.is_valid()) {
+ use_1d_color_correction = true;
+ } else {
+ use_1d_color_correction = false;
+ }
_update_adjustment();
}
-Ref<Texture2D> Environment::get_adjustment_color_correction() const {
+Ref<Texture> Environment::get_adjustment_color_correction() const {
return adjustment_color_correction;
}
void Environment::_update_adjustment() {
+ RID color_correction = adjustment_color_correction.is_valid() ? adjustment_color_correction->get_rid() : RID();
+
RS::get_singleton()->environment_set_adjustment(
environment,
adjustment_enabled,
adjustment_brightness,
adjustment_contrast,
adjustment_saturation,
- adjustment_color_correction.is_valid() ? adjustment_color_correction->get_rid() : RID());
+ use_1d_color_correction,
+ color_correction);
}
// Private methods, constructor and destructor
@@ -887,6 +947,12 @@ void Environment::_validate_property(PropertyInfo &property) const {
}
}
+ if (property.name == "fog_aerial_perspective") {
+ if (bg_mode != BG_SKY) {
+ property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ }
+ }
+
if (property.name == "glow_intensity" && glow_blend_mode == GLOW_BLEND_MODE_MIX) {
property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
}
@@ -919,6 +985,7 @@ void Environment::_validate_property(PropertyInfo &property) const {
"auto_exposure_",
"ss_reflections_",
"ssao_",
+ "sdfgi_",
"glow_",
"adjustment_",
nullptr
@@ -1096,26 +1163,29 @@ void Environment::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_ssao_radius"), &Environment::get_ssao_radius);
ClassDB::bind_method(D_METHOD("set_ssao_intensity", "intensity"), &Environment::set_ssao_intensity);
ClassDB::bind_method(D_METHOD("get_ssao_intensity"), &Environment::get_ssao_intensity);
- ClassDB::bind_method(D_METHOD("set_ssao_bias", "bias"), &Environment::set_ssao_bias);
- ClassDB::bind_method(D_METHOD("get_ssao_bias"), &Environment::get_ssao_bias);
+ ClassDB::bind_method(D_METHOD("set_ssao_power", "power"), &Environment::set_ssao_power);
+ ClassDB::bind_method(D_METHOD("get_ssao_power"), &Environment::get_ssao_power);
+ ClassDB::bind_method(D_METHOD("set_ssao_detail", "detail"), &Environment::set_ssao_detail);
+ ClassDB::bind_method(D_METHOD("get_ssao_detail"), &Environment::get_ssao_detail);
+ ClassDB::bind_method(D_METHOD("set_ssao_horizon", "horizon"), &Environment::set_ssao_horizon);
+ ClassDB::bind_method(D_METHOD("get_ssao_horizon"), &Environment::get_ssao_horizon);
+ ClassDB::bind_method(D_METHOD("set_ssao_sharpness", "sharpness"), &Environment::set_ssao_sharpness);
+ ClassDB::bind_method(D_METHOD("get_ssao_sharpness"), &Environment::get_ssao_sharpness);
ClassDB::bind_method(D_METHOD("set_ssao_direct_light_affect", "amount"), &Environment::set_ssao_direct_light_affect);
ClassDB::bind_method(D_METHOD("get_ssao_direct_light_affect"), &Environment::get_ssao_direct_light_affect);
ClassDB::bind_method(D_METHOD("set_ssao_ao_channel_affect", "amount"), &Environment::set_ssao_ao_channel_affect);
ClassDB::bind_method(D_METHOD("get_ssao_ao_channel_affect"), &Environment::get_ssao_ao_channel_affect);
- ClassDB::bind_method(D_METHOD("set_ssao_blur", "mode"), &Environment::set_ssao_blur);
- ClassDB::bind_method(D_METHOD("get_ssao_blur"), &Environment::get_ssao_blur);
- ClassDB::bind_method(D_METHOD("set_ssao_edge_sharpness", "edge_sharpness"), &Environment::set_ssao_edge_sharpness);
- ClassDB::bind_method(D_METHOD("get_ssao_edge_sharpness"), &Environment::get_ssao_edge_sharpness);
ADD_GROUP("SSAO", "ssao_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ssao_enabled"), "set_ssao_enabled", "is_ssao_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_radius", PROPERTY_HINT_RANGE, "0.1,128,0.01"), "set_ssao_radius", "get_ssao_radius");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_intensity", PROPERTY_HINT_RANGE, "0.0,128,0.01"), "set_ssao_intensity", "get_ssao_intensity");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_bias", PROPERTY_HINT_RANGE, "0.001,8,0.001"), "set_ssao_bias", "get_ssao_bias");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_radius", PROPERTY_HINT_RANGE, "0.01,16,0.01,or_greater"), "set_ssao_radius", "get_ssao_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_intensity", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_ssao_intensity", "get_ssao_intensity");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_power", PROPERTY_HINT_EXP_EASING), "set_ssao_power", "get_ssao_power");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_detail", PROPERTY_HINT_RANGE, "0,5,0.01"), "set_ssao_detail", "get_ssao_detail");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_horizon", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_ssao_horizon", "get_ssao_horizon");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_sharpness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_ssao_sharpness", "get_ssao_sharpness");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_light_affect", PROPERTY_HINT_RANGE, "0.00,1,0.01"), "set_ssao_direct_light_affect", "get_ssao_direct_light_affect");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_ao_channel_affect", PROPERTY_HINT_RANGE, "0.00,1,0.01"), "set_ssao_ao_channel_affect", "get_ssao_ao_channel_affect");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "ssao_blur", PROPERTY_HINT_ENUM, "Disabled,1x1,2x2,3x3"), "set_ssao_blur", "get_ssao_blur");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_edge_sharpness", PROPERTY_HINT_RANGE, "0,32,0.01"), "set_ssao_edge_sharpness", "get_ssao_edge_sharpness");
// SDFGI
@@ -1133,8 +1203,8 @@ void Environment::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_sdfgi_y_scale"), &Environment::get_sdfgi_y_scale);
ClassDB::bind_method(D_METHOD("set_sdfgi_use_occlusion", "enable"), &Environment::set_sdfgi_use_occlusion);
ClassDB::bind_method(D_METHOD("is_sdfgi_using_occlusion"), &Environment::is_sdfgi_using_occlusion);
- ClassDB::bind_method(D_METHOD("set_sdfgi_use_multi_bounce", "enable"), &Environment::set_sdfgi_use_multi_bounce);
- ClassDB::bind_method(D_METHOD("is_sdfgi_using_multi_bounce"), &Environment::is_sdfgi_using_multi_bounce);
+ ClassDB::bind_method(D_METHOD("set_sdfgi_bounce_feedback", "amount"), &Environment::set_sdfgi_bounce_feedback);
+ ClassDB::bind_method(D_METHOD("get_sdfgi_bounce_feedback"), &Environment::get_sdfgi_bounce_feedback);
ClassDB::bind_method(D_METHOD("set_sdfgi_read_sky_light", "enable"), &Environment::set_sdfgi_read_sky_light);
ClassDB::bind_method(D_METHOD("is_sdfgi_reading_sky_light"), &Environment::is_sdfgi_reading_sky_light);
ClassDB::bind_method(D_METHOD("set_sdfgi_energy", "amount"), &Environment::set_sdfgi_energy);
@@ -1146,9 +1216,9 @@ void Environment::_bind_methods() {
ADD_GROUP("SDFGI", "sdfgi_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sdfgi_enabled"), "set_sdfgi_enabled", "is_sdfgi_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sdfgi_use_multi_bounce"), "set_sdfgi_use_multi_bounce", "is_sdfgi_using_multi_bounce");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sdfgi_use_occlusion"), "set_sdfgi_use_occlusion", "is_sdfgi_using_occlusion");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sdfgi_read_sky_light"), "set_sdfgi_read_sky_light", "is_sdfgi_reading_sky_light");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_bounce_feedback", PROPERTY_HINT_RANGE, "0,1.99,0.01"), "set_sdfgi_bounce_feedback", "get_sdfgi_bounce_feedback");
ADD_PROPERTY(PropertyInfo(Variant::INT, "sdfgi_cascades", PROPERTY_HINT_ENUM, "4 Cascades,6 Cascades,8 Cascades"), "set_sdfgi_cascades", "get_sdfgi_cascades");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_min_cell_size", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_sdfgi_min_cell_size", "get_sdfgi_min_cell_size");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_cascade0_distance", PROPERTY_HINT_RANGE, "0.1,16384,0.1,or_greater"), "set_sdfgi_cascade0_distance", "get_sdfgi_cascade0_distance");
@@ -1162,8 +1232,10 @@ void Environment::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_glow_enabled", "enabled"), &Environment::set_glow_enabled);
ClassDB::bind_method(D_METHOD("is_glow_enabled"), &Environment::is_glow_enabled);
- ClassDB::bind_method(D_METHOD("set_glow_level_enabled", "idx", "enabled"), &Environment::set_glow_level_enabled);
- ClassDB::bind_method(D_METHOD("is_glow_level_enabled", "idx"), &Environment::is_glow_level_enabled);
+ ClassDB::bind_method(D_METHOD("set_glow_level", "idx", "intensity"), &Environment::set_glow_level);
+ ClassDB::bind_method(D_METHOD("get_glow_level", "idx"), &Environment::get_glow_level);
+ ClassDB::bind_method(D_METHOD("set_glow_normalized", "normalize"), &Environment::set_glow_normalized);
+ ClassDB::bind_method(D_METHOD("is_glow_normalized"), &Environment::is_glow_normalized);
ClassDB::bind_method(D_METHOD("set_glow_intensity", "intensity"), &Environment::set_glow_intensity);
ClassDB::bind_method(D_METHOD("get_glow_intensity"), &Environment::get_glow_intensity);
ClassDB::bind_method(D_METHOD("set_glow_strength", "strength"), &Environment::set_glow_strength);
@@ -1183,13 +1255,14 @@ void Environment::_bind_methods() {
ADD_GROUP("Glow", "glow_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "glow_enabled"), "set_glow_enabled", "is_glow_enabled");
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "glow_levels/1"), "set_glow_level_enabled", "is_glow_level_enabled", 0);
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "glow_levels/2"), "set_glow_level_enabled", "is_glow_level_enabled", 1);
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "glow_levels/3"), "set_glow_level_enabled", "is_glow_level_enabled", 2);
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "glow_levels/4"), "set_glow_level_enabled", "is_glow_level_enabled", 3);
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "glow_levels/5"), "set_glow_level_enabled", "is_glow_level_enabled", 4);
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "glow_levels/6"), "set_glow_level_enabled", "is_glow_level_enabled", 5);
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "glow_levels/7"), "set_glow_level_enabled", "is_glow_level_enabled", 6);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "glow_levels/1", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_glow_level", "get_glow_level", 0);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "glow_levels/2", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_glow_level", "get_glow_level", 1);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "glow_levels/3", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_glow_level", "get_glow_level", 2);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "glow_levels/4", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_glow_level", "get_glow_level", 3);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "glow_levels/5", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_glow_level", "get_glow_level", 4);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "glow_levels/6", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_glow_level", "get_glow_level", 5);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "glow_levels/7", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_glow_level", "get_glow_level", 6);
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "glow_normalized"), "set_glow_normalized", "is_glow_normalized");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "glow_intensity", PROPERTY_HINT_RANGE, "0.0,8.0,0.01"), "set_glow_intensity", "get_glow_intensity");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "glow_strength", PROPERTY_HINT_RANGE, "0.0,2.0,0.01"), "set_glow_strength", "get_glow_strength");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "glow_mix", PROPERTY_HINT_RANGE, "0.0,1.0,0.001"), "set_glow_mix", "get_glow_mix");
@@ -1219,6 +1292,9 @@ void Environment::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_fog_height_density", "height_density"), &Environment::set_fog_height_density);
ClassDB::bind_method(D_METHOD("get_fog_height_density"), &Environment::get_fog_height_density);
+ ClassDB::bind_method(D_METHOD("set_fog_aerial_perspective", "aerial_perspective"), &Environment::set_fog_aerial_perspective);
+ ClassDB::bind_method(D_METHOD("get_fog_aerial_perspective"), &Environment::get_fog_aerial_perspective);
+
ADD_GROUP("Fog", "fog_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fog_enabled"), "set_fog_enabled", "is_fog_enabled");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "fog_light_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_fog_light_color", "get_fog_light_color");
@@ -1226,6 +1302,7 @@ void Environment::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_sun_scatter", PROPERTY_HINT_RANGE, "0,1,0.01,or_greater"), "set_fog_sun_scatter", "get_fog_sun_scatter");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_density", PROPERTY_HINT_RANGE, "0,16,0.0001"), "set_fog_density", "get_fog_density");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_aerial_perspective", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_fog_aerial_perspective", "get_fog_aerial_perspective");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height", PROPERTY_HINT_RANGE, "-1024,1024,0.01,or_lesser,or_greater"), "set_fog_height", "get_fog_height");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height_density", PROPERTY_HINT_RANGE, "0,128,0.001,or_greater"), "set_fog_height_density", "get_fog_height_density");
@@ -1243,18 +1320,22 @@ void Environment::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_volumetric_fog_detail_spread"), &Environment::get_volumetric_fog_detail_spread);
ClassDB::bind_method(D_METHOD("set_volumetric_fog_gi_inject", "gi_inject"), &Environment::set_volumetric_fog_gi_inject);
ClassDB::bind_method(D_METHOD("get_volumetric_fog_gi_inject"), &Environment::get_volumetric_fog_gi_inject);
- ClassDB::bind_method(D_METHOD("set_volumetric_fog_shadow_filter", "shadow_filter"), &Environment::set_volumetric_fog_shadow_filter);
- ClassDB::bind_method(D_METHOD("get_volumetric_fog_shadow_filter"), &Environment::get_volumetric_fog_shadow_filter);
+ ClassDB::bind_method(D_METHOD("set_volumetric_fog_temporal_reprojection_enabled", "enabled"), &Environment::set_volumetric_fog_temporal_reprojection_enabled);
+ ClassDB::bind_method(D_METHOD("is_volumetric_fog_temporal_reprojection_enabled"), &Environment::is_volumetric_fog_temporal_reprojection_enabled);
+ ClassDB::bind_method(D_METHOD("set_volumetric_fog_temporal_reprojection_amount", "temporal_reprojection_amount"), &Environment::set_volumetric_fog_temporal_reprojection_amount);
+ ClassDB::bind_method(D_METHOD("get_volumetric_fog_temporal_reprojection_amount"), &Environment::get_volumetric_fog_temporal_reprojection_amount);
ADD_GROUP("Volumetric Fog", "volumetric_fog_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "volumetric_fog_enabled"), "set_volumetric_fog_enabled", "is_volumetric_fog_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_density", PROPERTY_HINT_RANGE, "0,1,0.0001,or_greater"), "set_volumetric_fog_density", "get_volumetric_fog_density");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "volumetric_fog_light", PROPERTY_HINT_COLOR_NO_ALPHA), "set_volumetric_fog_light", "get_volumetric_fog_light");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_light_energy", PROPERTY_HINT_RANGE, "0,1024,0.01,or_greater"), "set_volumetric_fog_light_energy", "get_volumetric_fog_light_energy");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_gi_inject", PROPERTY_HINT_EXP_RANGE, "0.00,16,0.01"), "set_volumetric_fog_gi_inject", "get_volumetric_fog_gi_inject");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_gi_inject", PROPERTY_HINT_RANGE, "0.00,16,0.01,exp"), "set_volumetric_fog_gi_inject", "get_volumetric_fog_gi_inject");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_length", PROPERTY_HINT_RANGE, "0,1024,0.01,or_greater"), "set_volumetric_fog_length", "get_volumetric_fog_length");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_detail_spread", PROPERTY_HINT_EXP_EASING, "0.01,16,0.01"), "set_volumetric_fog_detail_spread", "get_volumetric_fog_detail_spread");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "volumetric_fog_shadow_filter", PROPERTY_HINT_ENUM, "Disabled,Low,Medium,High"), "set_volumetric_fog_shadow_filter", "get_volumetric_fog_shadow_filter");
+ ADD_SUBGROUP("Temporal Reprojection", "volumetric_fog_temporal_reprojection_");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "volumetric_fog_temporal_reprojection_enabled"), "set_volumetric_fog_temporal_reprojection_enabled", "is_volumetric_fog_temporal_reprojection_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_temporal_reprojection_amount", PROPERTY_HINT_RANGE, "0.0,0.999,0.001"), "set_volumetric_fog_temporal_reprojection_amount", "get_volumetric_fog_temporal_reprojection_amount");
// Adjustment
@@ -1274,7 +1355,7 @@ void Environment::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "adjustment_brightness", PROPERTY_HINT_RANGE, "0.01,8,0.01"), "set_adjustment_brightness", "get_adjustment_brightness");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "adjustment_contrast", PROPERTY_HINT_RANGE, "0.01,8,0.01"), "set_adjustment_contrast", "get_adjustment_contrast");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "adjustment_saturation", PROPERTY_HINT_RANGE, "0.01,8,0.01"), "set_adjustment_saturation", "get_adjustment_saturation");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "adjustment_color_correction", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_adjustment_color_correction", "get_adjustment_color_correction");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "adjustment_color_correction", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D,Texture3D"), "set_adjustment_color_correction", "get_adjustment_color_correction");
// Constants
@@ -1306,11 +1387,6 @@ void Environment::_bind_methods() {
BIND_ENUM_CONSTANT(GLOW_BLEND_MODE_REPLACE);
BIND_ENUM_CONSTANT(GLOW_BLEND_MODE_MIX);
- BIND_ENUM_CONSTANT(SSAO_BLUR_DISABLED);
- BIND_ENUM_CONSTANT(SSAO_BLUR_1x1);
- BIND_ENUM_CONSTANT(SSAO_BLUR_2x2);
- BIND_ENUM_CONSTANT(SSAO_BLUR_3x3);
-
BIND_ENUM_CONSTANT(SDFGI_CASCADES_4);
BIND_ENUM_CONSTANT(SDFGI_CASCADES_6);
BIND_ENUM_CONSTANT(SDFGI_CASCADES_8);
@@ -1329,6 +1405,16 @@ Environment::Environment() {
environment = RS::get_singleton()->environment_create();
set_camera_feed_id(bg_camera_feed_id);
+
+ glow_levels.resize(7);
+ glow_levels.write[0] = 0.0;
+ glow_levels.write[1] = 0.0;
+ glow_levels.write[2] = 1.0;
+ glow_levels.write[3] = 0.0;
+ glow_levels.write[4] = 1.0;
+ glow_levels.write[5] = 0.0;
+ glow_levels.write[6] = 0.0;
+
_update_ambient_light();
_update_tonemap();
_update_ssr();
@@ -1338,7 +1424,7 @@ Environment::Environment() {
_update_fog();
_update_adjustment();
_update_volumetric_fog();
- _change_notify();
+ notify_property_list_changed();
}
Environment::~Environment() {
diff --git a/scene/resources/environment.h b/scene/resources/environment.h
index d4d84f31aa..0df2c3cc27 100644
--- a/scene/resources/environment.h
+++ b/scene/resources/environment.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,7 +31,7 @@
#ifndef ENVIRONMENT_H
#define ENVIRONMENT_H
-#include "core/resource.h"
+#include "core/io/resource.h"
#include "scene/resources/sky.h"
#include "scene/resources/texture.h"
#include "servers/rendering_server.h"
@@ -70,13 +70,6 @@ public:
TONE_MAPPER_ACES,
};
- enum SSAOBlur {
- SSAO_BLUR_DISABLED,
- SSAO_BLUR_1x1,
- SSAO_BLUR_2x2,
- SSAO_BLUR_3x3,
- };
-
enum SDFGICascades {
SDFGI_CASCADES_4,
SDFGI_CASCADES_6,
@@ -110,7 +103,7 @@ private:
// Background
BGMode bg_mode = BG_CLEAR_COLOR;
Ref<Sky> bg_sky;
- float bg_sky_custom_fov = 0;
+ float bg_sky_custom_fov = 0.0;
Vector3 bg_sky_rotation;
Color bg_color;
float bg_energy = 1.0;
@@ -132,7 +125,7 @@ private:
float tonemap_white = 1.0;
bool tonemap_auto_exposure_enabled = false;
float tonemap_auto_exposure_min = 0.05;
- float tonemap_auto_exposure_max = 8;
+ float tonemap_auto_exposure_max = 8.0;
float tonemap_auto_exposure_speed = 0.5;
float tonemap_auto_exposure_grey = 0.4;
void _update_tonemap();
@@ -148,12 +141,13 @@ private:
// SSAO
bool ssao_enabled = false;
float ssao_radius = 1.0;
- float ssao_intensity = 1.0;
- float ssao_bias = 0.01;
+ float ssao_intensity = 2.0;
+ float ssao_power = 1.5;
+ float ssao_detail = 0.5;
+ float ssao_horizon = 0.06;
+ float ssao_sharpness = 0.98;
float ssao_direct_light_affect = 0.0;
float ssao_ao_channel_affect = 0.0;
- SSAOBlur ssao_blur = SSAO_BLUR_3x3;
- float ssao_edge_sharpness = 4.0;
void _update_ssao();
// SDFGI
@@ -162,7 +156,7 @@ private:
float sdfgi_min_cell_size = 0.2;
SDFGIYScale sdfgi_y_scale = SDFGI_Y_SCALE_DISABLED;
bool sdfgi_use_occlusion = false;
- bool sdfgi_use_multibounce = false;
+ float sdfgi_bounce_feedback = 0.0;
bool sdfgi_read_sky_light = false;
float sdfgi_energy = 1.0;
float sdfgi_normal_bias = 1.1;
@@ -171,7 +165,8 @@ private:
// Glow
bool glow_enabled = false;
- int glow_levels = (1 << 2) | (1 << 4);
+ Vector<float> glow_levels;
+ bool glow_normalize_levels = false;
float glow_intensity = 0.8;
float glow_strength = 1.0;
float glow_mix = 0.05;
@@ -190,6 +185,7 @@ private:
float fog_density = 0.001;
float fog_height = 0.0;
float fog_height_density = 0.0; //can be negative to invert effect
+ float fog_aerial_perspective = 0.0;
void _update_fog();
@@ -200,8 +196,9 @@ private:
float volumetric_fog_light_energy = 1.0;
float volumetric_fog_length = 64.0;
float volumetric_fog_detail_spread = 2.0;
- VolumetricFogShadowFilter volumetric_fog_shadow_filter = VOLUMETRIC_FOG_SHADOW_FILTER_LOW;
float volumetric_fog_gi_inject = 0.0;
+ bool volumetric_fog_temporal_reproject = true;
+ float volumetric_fog_temporal_reproject_amount = 0.9;
void _update_volumetric_fog();
// Adjustment
@@ -209,7 +206,8 @@ private:
float adjustment_brightness = 1.0;
float adjustment_contrast = 1.0;
float adjustment_saturation = 1.0;
- Ref<Texture2D> adjustment_color_correction;
+ bool use_1d_color_correction = true;
+ Ref<Texture> adjustment_color_correction;
void _update_adjustment();
protected:
@@ -292,16 +290,18 @@ public:
float get_ssao_radius() const;
void set_ssao_intensity(float p_intensity);
float get_ssao_intensity() const;
- void set_ssao_bias(float p_bias);
- float get_ssao_bias() const;
+ void set_ssao_power(float p_power);
+ float get_ssao_power() const;
+ void set_ssao_detail(float p_detail);
+ float get_ssao_detail() const;
+ void set_ssao_horizon(float p_horizon);
+ float get_ssao_horizon() const;
+ void set_ssao_sharpness(float p_sharpness);
+ float get_ssao_sharpness() const;
void set_ssao_direct_light_affect(float p_direct_light_affect);
float get_ssao_direct_light_affect() const;
void set_ssao_ao_channel_affect(float p_ao_channel_affect);
float get_ssao_ao_channel_affect() const;
- void set_ssao_blur(SSAOBlur p_blur);
- SSAOBlur get_ssao_blur() const;
- void set_ssao_edge_sharpness(float p_edge_sharpness);
- float get_ssao_edge_sharpness() const;
// SDFGI
void set_sdfgi_enabled(bool p_enabled);
@@ -318,8 +318,8 @@ public:
SDFGIYScale get_sdfgi_y_scale() const;
void set_sdfgi_use_occlusion(bool p_enabled);
bool is_sdfgi_using_occlusion() const;
- void set_sdfgi_use_multi_bounce(bool p_enabled);
- bool is_sdfgi_using_multi_bounce() const;
+ void set_sdfgi_bounce_feedback(float p_amount);
+ float get_sdfgi_bounce_feedback() const;
void set_sdfgi_read_sky_light(bool p_enabled);
bool is_sdfgi_reading_sky_light() const;
void set_sdfgi_energy(float p_energy);
@@ -332,8 +332,10 @@ public:
// Glow
void set_glow_enabled(bool p_enabled);
bool is_glow_enabled() const;
- void set_glow_level_enabled(int p_level, bool p_enabled);
- bool is_glow_level_enabled(int p_level) const;
+ void set_glow_level(int p_level, float p_intensity);
+ float get_glow_level(int p_level) const;
+ void set_glow_normalized(bool p_normalized);
+ bool is_glow_normalized() const;
void set_glow_intensity(float p_intensity);
float get_glow_intensity() const;
void set_glow_strength(float p_strength);
@@ -368,6 +370,8 @@ public:
float get_fog_height() const;
void set_fog_height_density(float p_amount);
float get_fog_height_density() const;
+ void set_fog_aerial_perspective(float p_aerial_perspective);
+ float get_fog_aerial_perspective() const;
// Volumetric Fog
void set_volumetric_fog_enabled(bool p_enable);
@@ -382,10 +386,12 @@ public:
float get_volumetric_fog_length() const;
void set_volumetric_fog_detail_spread(float p_detail_spread);
float get_volumetric_fog_detail_spread() const;
- void set_volumetric_fog_shadow_filter(VolumetricFogShadowFilter p_filter);
- VolumetricFogShadowFilter get_volumetric_fog_shadow_filter() const;
void set_volumetric_fog_gi_inject(float p_gi_inject);
float get_volumetric_fog_gi_inject() const;
+ void set_volumetric_fog_temporal_reprojection_enabled(bool p_enable);
+ bool is_volumetric_fog_temporal_reprojection_enabled() const;
+ void set_volumetric_fog_temporal_reprojection_amount(float p_amount);
+ float get_volumetric_fog_temporal_reprojection_amount() const;
// Adjustment
void set_adjustment_enabled(bool p_enabled);
@@ -396,8 +402,8 @@ public:
float get_adjustment_contrast() const;
void set_adjustment_saturation(float p_saturation);
float get_adjustment_saturation() const;
- void set_adjustment_color_correction(const Ref<Texture2D> &p_ramp);
- Ref<Texture2D> get_adjustment_color_correction() const;
+ void set_adjustment_color_correction(Ref<Texture> p_color_correction);
+ Ref<Texture> get_adjustment_color_correction() const;
Environment();
~Environment();
@@ -407,7 +413,6 @@ VARIANT_ENUM_CAST(Environment::BGMode)
VARIANT_ENUM_CAST(Environment::AmbientSource)
VARIANT_ENUM_CAST(Environment::ReflectionSource)
VARIANT_ENUM_CAST(Environment::ToneMapper)
-VARIANT_ENUM_CAST(Environment::SSAOBlur)
VARIANT_ENUM_CAST(Environment::SDFGICascades)
VARIANT_ENUM_CAST(Environment::SDFGIYScale)
VARIANT_ENUM_CAST(Environment::GlowBlendMode)
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index 7cc39f661d..7af8e900fd 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,640 +31,1563 @@
#include "font.h"
#include "core/io/resource_loader.h"
-#include "core/method_bind_ext.gen.inc"
-#include "core/os/file_access.h"
-
-void Font::draw_halign(RID p_canvas_item, const Point2 &p_pos, HAlign p_align, float p_width, const String &p_text, const Color &p_modulate, const Color &p_outline_modulate) const {
- float length = get_string_size(p_text).width;
- if (length >= p_width) {
- draw(p_canvas_item, p_pos, p_text, p_modulate, p_width, p_outline_modulate);
- return;
+#include "core/string/translation.h"
+#include "core/templates/hashfuncs.h"
+#include "scene/resources/text_line.h"
+#include "scene/resources/text_paragraph.h"
+
+_FORCE_INLINE_ void FontData::_clear_cache() {
+ for (int i = 0; i < cache.size(); i++) {
+ if (cache[i].is_valid()) {
+ TS->free(cache[i]);
+ cache.write[i] = RID();
+ }
}
+}
- float ofs = 0.f;
- switch (p_align) {
- case HALIGN_LEFT: {
- ofs = 0;
- } break;
- case HALIGN_CENTER: {
- ofs = Math::floor((p_width - length) / 2.0);
- } break;
- case HALIGN_RIGHT: {
- ofs = p_width - length;
- } break;
- default: {
- ERR_PRINT("Unknown halignment type");
- } break;
+_FORCE_INLINE_ void FontData::_ensure_rid(int p_cache_index) const {
+ if (unlikely(p_cache_index >= cache.size())) {
+ cache.resize(p_cache_index + 1);
+ }
+ if (unlikely(!cache[p_cache_index].is_valid())) {
+ cache.write[p_cache_index] = TS->create_font();
+ TS->font_set_data_ptr(cache[p_cache_index], data_ptr, data_size);
+ TS->font_set_antialiased(cache[p_cache_index], antialiased);
+ TS->font_set_multichannel_signed_distance_field(cache[p_cache_index], msdf);
+ TS->font_set_msdf_pixel_range(cache[p_cache_index], msdf_pixel_range);
+ TS->font_set_msdf_size(cache[p_cache_index], msdf_size);
+ TS->font_set_fixed_size(cache[p_cache_index], fixed_size);
+ TS->font_set_force_autohinter(cache[p_cache_index], force_autohinter);
+ TS->font_set_hinting(cache[p_cache_index], hinting);
+ TS->font_set_oversampling(cache[p_cache_index], oversampling);
}
- draw(p_canvas_item, p_pos + Point2(ofs, 0), p_text, p_modulate, p_width, p_outline_modulate);
}
-void Font::draw(RID p_canvas_item, const Point2 &p_pos, const String &p_text, const Color &p_modulate, int p_clip_w, const Color &p_outline_modulate) const {
- Vector2 ofs;
+void FontData::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_data", "data"), &FontData::set_data);
+ ClassDB::bind_method(D_METHOD("get_data"), &FontData::get_data);
+
+ ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &FontData::set_antialiased);
+ ClassDB::bind_method(D_METHOD("is_antialiased"), &FontData::is_antialiased);
+
+ ClassDB::bind_method(D_METHOD("set_multichannel_signed_distance_field", "msdf"), &FontData::set_multichannel_signed_distance_field);
+ ClassDB::bind_method(D_METHOD("is_multichannel_signed_distance_field"), &FontData::is_multichannel_signed_distance_field);
+
+ ClassDB::bind_method(D_METHOD("set_msdf_pixel_range", "msdf_pixel_range"), &FontData::set_msdf_pixel_range);
+ ClassDB::bind_method(D_METHOD("get_msdf_pixel_range"), &FontData::get_msdf_pixel_range);
+
+ ClassDB::bind_method(D_METHOD("set_msdf_size", "msdf_size"), &FontData::set_msdf_size);
+ ClassDB::bind_method(D_METHOD("get_msdf_size"), &FontData::get_msdf_size);
+
+ ClassDB::bind_method(D_METHOD("set_force_autohinter", "force_autohinter"), &FontData::set_force_autohinter);
+ ClassDB::bind_method(D_METHOD("is_force_autohinter"), &FontData::is_force_autohinter);
+
+ ClassDB::bind_method(D_METHOD("set_hinting", "hinting"), &FontData::set_hinting);
+ ClassDB::bind_method(D_METHOD("get_hinting"), &FontData::get_hinting);
+
+ ClassDB::bind_method(D_METHOD("set_oversampling", "oversampling"), &FontData::set_oversampling);
+ ClassDB::bind_method(D_METHOD("get_oversampling"), &FontData::get_oversampling);
+
+ ClassDB::bind_method(D_METHOD("find_cache", "variation_coordinates"), &FontData::find_cache);
+
+ ClassDB::bind_method(D_METHOD("get_cache_count"), &FontData::get_cache_count);
+ ClassDB::bind_method(D_METHOD("clear_cache"), &FontData::clear_cache);
+ ClassDB::bind_method(D_METHOD("remove_cache", "cache_index"), &FontData::remove_cache);
+
+ ClassDB::bind_method(D_METHOD("get_size_cache_list", "cache_index"), &FontData::get_size_cache_list);
+ ClassDB::bind_method(D_METHOD("clear_size_cache", "cache_index"), &FontData::clear_size_cache);
+ ClassDB::bind_method(D_METHOD("remove_size_cache", "cache_index", "size"), &FontData::remove_size_cache);
+
+ ClassDB::bind_method(D_METHOD("set_variation_coordinates", "cache_index", "variation_coordinates"), &FontData::set_variation_coordinates);
+ ClassDB::bind_method(D_METHOD("get_variation_coordinates", "cache_index"), &FontData::get_variation_coordinates);
+
+ ClassDB::bind_method(D_METHOD("set_ascent", "cache_index", "size", "ascent"), &FontData::set_ascent);
+ ClassDB::bind_method(D_METHOD("get_ascent", "cache_index", "size"), &FontData::get_ascent);
+
+ ClassDB::bind_method(D_METHOD("set_descent", "cache_index", "size", "descent"), &FontData::set_descent);
+ ClassDB::bind_method(D_METHOD("get_descent", "cache_index", "size"), &FontData::get_descent);
+
+ ClassDB::bind_method(D_METHOD("set_underline_position", "cache_index", "size", "underline_position"), &FontData::set_underline_position);
+ ClassDB::bind_method(D_METHOD("get_underline_position", "cache_index", "size"), &FontData::get_underline_position);
- int chars_drawn = 0;
- bool with_outline = has_outline();
- for (int i = 0; i < p_text.length(); i++) {
- int width = get_char_size(p_text[i]).width;
+ ClassDB::bind_method(D_METHOD("set_underline_thickness", "cache_index", "size", "underline_thickness"), &FontData::set_underline_thickness);
+ ClassDB::bind_method(D_METHOD("get_underline_thickness", "cache_index", "size"), &FontData::get_underline_thickness);
+
+ ClassDB::bind_method(D_METHOD("set_scale", "cache_index", "size", "scale"), &FontData::set_scale);
+ ClassDB::bind_method(D_METHOD("get_scale", "cache_index", "size"), &FontData::get_scale);
+
+ ClassDB::bind_method(D_METHOD("set_spacing", "cache_index", "size", "spacing"), &FontData::set_spacing);
+ ClassDB::bind_method(D_METHOD("get_spacing", "cache_index", "size"), &FontData::get_spacing);
+
+ ClassDB::bind_method(D_METHOD("get_texture_count", "cache_index", "size"), &FontData::get_texture_count);
+ ClassDB::bind_method(D_METHOD("clear_textures", "cache_index", "size"), &FontData::clear_textures);
+ ClassDB::bind_method(D_METHOD("remove_texture", "cache_index", "size", "texture_index"), &FontData::remove_texture);
+
+ ClassDB::bind_method(D_METHOD("set_texture_image", "cache_index", "size", "texture_index", "image"), &FontData::set_texture_image);
+ ClassDB::bind_method(D_METHOD("get_texture_image", "cache_index", "size", "texture_index"), &FontData::get_texture_image);
+
+ ClassDB::bind_method(D_METHOD("set_texture_offsets", "cache_index", "size", "texture_index", "offset"), &FontData::set_texture_offsets);
+ ClassDB::bind_method(D_METHOD("get_texture_offsets", "cache_index", "size", "texture_index"), &FontData::get_texture_offsets);
+
+ ClassDB::bind_method(D_METHOD("get_glyph_list", "cache_index", "size"), &FontData::get_glyph_list);
+ ClassDB::bind_method(D_METHOD("clear_glyphs", "cache_index", "size"), &FontData::clear_glyphs);
+ ClassDB::bind_method(D_METHOD("remove_glyph", "cache_index", "size", "glyph"), &FontData::remove_glyph);
+
+ ClassDB::bind_method(D_METHOD("set_glyph_advance", "cache_index", "size", "glyph", "advance"), &FontData::set_glyph_advance);
+ ClassDB::bind_method(D_METHOD("get_glyph_advance", "cache_index", "size", "glyph"), &FontData::get_glyph_advance);
+
+ ClassDB::bind_method(D_METHOD("set_glyph_offset", "cache_index", "size", "glyph", "offset"), &FontData::set_glyph_offset);
+ ClassDB::bind_method(D_METHOD("get_glyph_offset", "cache_index", "size", "glyph"), &FontData::get_glyph_offset);
+
+ ClassDB::bind_method(D_METHOD("set_glyph_size", "cache_index", "size", "glyph", "gl_size"), &FontData::set_glyph_size);
+ ClassDB::bind_method(D_METHOD("get_glyph_size", "cache_index", "size", "glyph"), &FontData::get_glyph_size);
+
+ ClassDB::bind_method(D_METHOD("set_glyph_uv_rect", "cache_index", "size", "glyph", "uv_rect"), &FontData::set_glyph_uv_rect);
+ ClassDB::bind_method(D_METHOD("get_glyph_uv_rect", "cache_index", "size", "glyph"), &FontData::get_glyph_uv_rect);
+
+ ClassDB::bind_method(D_METHOD("set_glyph_texture_idx", "cache_index", "size", "glyph", "texture_idx"), &FontData::set_glyph_texture_idx);
+ ClassDB::bind_method(D_METHOD("get_glyph_texture_idx", "cache_index", "size", "glyph"), &FontData::get_glyph_texture_idx);
+
+ ClassDB::bind_method(D_METHOD("get_kerning_list", "cache_index", "size"), &FontData::get_kerning_list);
+ ClassDB::bind_method(D_METHOD("clear_kerning_map", "cache_index", "size"), &FontData::clear_kerning_map);
+ ClassDB::bind_method(D_METHOD("remove_kerning", "cache_index", "size", "glyph_pair"), &FontData::remove_kerning);
+
+ ClassDB::bind_method(D_METHOD("set_kerning", "cache_index", "size", "glyph_pair", "kerning"), &FontData::set_kerning);
+ ClassDB::bind_method(D_METHOD("get_kerning", "cache_index", "size", "glyph_pair"), &FontData::get_kerning);
+
+ ClassDB::bind_method(D_METHOD("render_range", "cache_index", "size", "start", "end"), &FontData::render_range);
+ ClassDB::bind_method(D_METHOD("render_glyph", "cache_index", "size", "index"), &FontData::render_glyph);
+
+ ClassDB::bind_method(D_METHOD("get_cache_rid", "cache_index"), &FontData::get_cache_rid);
+
+ ClassDB::bind_method(D_METHOD("is_language_supported", "language"), &FontData::is_language_supported);
+ ClassDB::bind_method(D_METHOD("set_language_support_override", "language", "supported"), &FontData::set_language_support_override);
+ ClassDB::bind_method(D_METHOD("get_language_support_override", "language"), &FontData::get_language_support_override);
+ ClassDB::bind_method(D_METHOD("remove_language_support_override", "language"), &FontData::remove_language_support_override);
+ ClassDB::bind_method(D_METHOD("get_language_support_overrides"), &FontData::get_language_support_overrides);
+
+ ClassDB::bind_method(D_METHOD("is_script_supported", "script"), &FontData::is_script_supported);
+ ClassDB::bind_method(D_METHOD("set_script_support_override", "script", "supported"), &FontData::set_script_support_override);
+ ClassDB::bind_method(D_METHOD("get_script_support_override", "script"), &FontData::get_script_support_override);
+ ClassDB::bind_method(D_METHOD("remove_script_support_override", "script"), &FontData::remove_script_support_override);
+ ClassDB::bind_method(D_METHOD("get_script_support_overrides"), &FontData::get_script_support_overrides);
+
+ ClassDB::bind_method(D_METHOD("has_char", "char"), &FontData::has_char);
+ ClassDB::bind_method(D_METHOD("get_supported_chars"), &FontData::get_supported_chars);
+
+ ClassDB::bind_method(D_METHOD("get_glyph_index", "char", "variation_selector"), &FontData::get_glyph_index);
+
+ ClassDB::bind_method(D_METHOD("get_supported_feature_list"), &FontData::get_supported_feature_list);
+ ClassDB::bind_method(D_METHOD("get_supported_variation_list"), &FontData::get_supported_variation_list);
+}
+
+bool FontData::_set(const StringName &p_name, const Variant &p_value) {
+ Vector<String> tokens = p_name.operator String().split("/");
+ if (tokens.size() == 1) {
+ if (tokens[0] == "data") {
+ set_data(p_value);
+ return true;
+ } else if (tokens[0] == "antialiased") {
+ set_antialiased(p_value);
+ return true;
+ } else if (tokens[0] == "multichannel_signed_distance_field") {
+ set_multichannel_signed_distance_field(p_value);
+ return true;
+ } else if (tokens[0] == "msdf_pixel_range") {
+ set_msdf_pixel_range(p_value);
+ return true;
+ } else if (tokens[0] == "msdf_size") {
+ set_msdf_size(p_value);
+ return true;
+ } else if (tokens[0] == "fixed_size") {
+ set_fixed_size(p_value);
+ return true;
+ } else if (tokens[0] == "hinting") {
+ set_hinting((TextServer::Hinting)p_value.operator int());
+ return true;
+ } else if (tokens[0] == "force_autohinter") {
+ set_force_autohinter(p_value);
+ return true;
+ } else if (tokens[0] == "oversampling") {
+ set_oversampling(p_value);
+ return true;
+ }
+ } else if (tokens.size() == 2 && tokens[0] == "language_support_override") {
+ String lang = tokens[1];
+ set_language_support_override(lang, p_value);
+ return true;
+ } else if (tokens.size() == 2 && tokens[0] == "script_support_override") {
+ String script = tokens[1];
+ set_script_support_override(script, p_value);
+ return true;
+ } else if (tokens.size() >= 3 && tokens[0] == "cache") {
+ int cache_index = tokens[1].to_int();
+ if (tokens.size() == 3 && tokens[2] == "variation_coordinates") {
+ set_variation_coordinates(cache_index, p_value);
+ return true;
+ }
+ if (tokens.size() >= 5) {
+ Vector2i sz = Vector2i(tokens[2].to_int(), tokens[3].to_int());
+ if (tokens[4] == "ascent") {
+ set_ascent(cache_index, sz.x, p_value);
+ return true;
+ } else if (tokens[4] == "descent") {
+ set_descent(cache_index, sz.x, p_value);
+ return true;
+ } else if (tokens[4] == "underline_position") {
+ set_underline_position(cache_index, sz.x, p_value);
+ return true;
+ } else if (tokens[4] == "underline_thickness") {
+ set_underline_thickness(cache_index, sz.x, p_value);
+ return true;
+ } else if (tokens[4] == "scale") {
+ set_scale(cache_index, sz.x, p_value);
+ return true;
+ } else if (tokens[4] == "spacing_glyph") {
+ set_spacing(cache_index, sz.x, TextServer::SPACING_GLYPH, p_value);
+ return true;
+ } else if (tokens[4] == "spacing_space") {
+ set_spacing(cache_index, sz.x, TextServer::SPACING_SPACE, p_value);
+ return true;
+ } else if (tokens.size() == 7 && tokens[4] == "textures") {
+ int texture_index = tokens[5].to_int();
+ if (tokens[6] == "image") {
+ set_texture_image(cache_index, sz, texture_index, p_value);
+ return true;
+ } else if (tokens[6] == "offsets") {
+ set_texture_offsets(cache_index, sz, texture_index, p_value);
+ return true;
+ }
+ } else if (tokens.size() == 7 && tokens[4] == "glyphs") {
+ int32_t glyph_index = tokens[5].to_int();
+ if (tokens[6] == "advance") {
+ set_glyph_advance(cache_index, sz.x, glyph_index, p_value);
+ return true;
+ } else if (tokens[6] == "offset") {
+ set_glyph_offset(cache_index, sz, glyph_index, p_value);
+ return true;
+ } else if (tokens[6] == "size") {
+ set_glyph_size(cache_index, sz, glyph_index, p_value);
+ return true;
+ } else if (tokens[6] == "uv_rect") {
+ set_glyph_uv_rect(cache_index, sz, glyph_index, p_value);
+ return true;
+ } else if (tokens[6] == "texture_idx") {
+ set_glyph_texture_idx(cache_index, sz, glyph_index, p_value);
+ return true;
+ }
+ } else if (tokens.size() == 7 && tokens[4] == "kerning_overrides") {
+ Vector2i gp = Vector2i(tokens[5].to_int(), tokens[6].to_int());
+ set_kerning(cache_index, sz.x, gp, p_value);
+ return true;
+ }
+ }
+ }
+ return false;
+}
- if (p_clip_w >= 0 && (ofs.x + width) > p_clip_w) {
- break; //clip
+bool FontData::_get(const StringName &p_name, Variant &r_ret) const {
+ Vector<String> tokens = p_name.operator String().split("/");
+ if (tokens.size() == 1) {
+ if (tokens[0] == "data") {
+ r_ret = get_data();
+ return true;
+ } else if (tokens[0] == "antialiased") {
+ r_ret = is_antialiased();
+ return true;
+ } else if (tokens[0] == "multichannel_signed_distance_field") {
+ r_ret = is_multichannel_signed_distance_field();
+ return true;
+ } else if (tokens[0] == "msdf_pixel_range") {
+ r_ret = get_msdf_pixel_range();
+ return true;
+ } else if (tokens[0] == "msdf_size") {
+ r_ret = get_msdf_size();
+ return true;
+ } else if (tokens[0] == "fixed_size") {
+ r_ret = get_fixed_size();
+ return true;
+ } else if (tokens[0] == "hinting") {
+ r_ret = get_hinting();
+ return true;
+ } else if (tokens[0] == "force_autohinter") {
+ r_ret = is_force_autohinter();
+ return true;
+ } else if (tokens[0] == "oversampling") {
+ r_ret = get_oversampling();
+ return true;
+ }
+ } else if (tokens.size() == 2 && tokens[0] == "language_support_override") {
+ String lang = tokens[1];
+ r_ret = get_language_support_override(lang);
+ return true;
+ } else if (tokens.size() == 2 && tokens[0] == "script_support_override") {
+ String script = tokens[1];
+ r_ret = get_script_support_override(script);
+ return true;
+ } else if (tokens.size() >= 3 && tokens[0] == "cache") {
+ int cache_index = tokens[1].to_int();
+ if (tokens.size() == 3 && tokens[2] == "variation_coordinates") {
+ r_ret = get_variation_coordinates(cache_index);
+ return true;
+ }
+ if (tokens.size() >= 5) {
+ Vector2i sz = Vector2i(tokens[2].to_int(), tokens[3].to_int());
+ if (tokens[4] == "ascent") {
+ r_ret = get_ascent(cache_index, sz.x);
+ return true;
+ } else if (tokens[4] == "descent") {
+ r_ret = get_descent(cache_index, sz.x);
+ return true;
+ } else if (tokens[4] == "underline_position") {
+ r_ret = get_underline_position(cache_index, sz.x);
+ return true;
+ } else if (tokens[4] == "underline_thickness") {
+ r_ret = get_underline_thickness(cache_index, sz.x);
+ return true;
+ } else if (tokens[4] == "scale") {
+ r_ret = get_scale(cache_index, sz.x);
+ return true;
+ } else if (tokens[4] == "spacing_glyph") {
+ r_ret = get_spacing(cache_index, sz.x, TextServer::SPACING_GLYPH);
+ return true;
+ } else if (tokens[4] == "spacing_space") {
+ r_ret = get_spacing(cache_index, sz.x, TextServer::SPACING_SPACE);
+ return true;
+ } else if (tokens.size() == 7 && tokens[4] == "textures") {
+ int texture_index = tokens[5].to_int();
+ if (tokens[6] == "image") {
+ r_ret = get_texture_image(cache_index, sz, texture_index);
+ return true;
+ } else if (tokens[6] == "offsets") {
+ r_ret = get_texture_offsets(cache_index, sz, texture_index);
+ return true;
+ }
+ } else if (tokens.size() == 7 && tokens[4] == "glyphs") {
+ int32_t glyph_index = tokens[5].to_int();
+ if (tokens[6] == "advance") {
+ r_ret = get_glyph_advance(cache_index, sz.x, glyph_index);
+ return true;
+ } else if (tokens[6] == "offset") {
+ r_ret = get_glyph_offset(cache_index, sz, glyph_index);
+ return true;
+ } else if (tokens[6] == "size") {
+ r_ret = get_glyph_size(cache_index, sz, glyph_index);
+ return true;
+ } else if (tokens[6] == "uv_rect") {
+ r_ret = get_glyph_uv_rect(cache_index, sz, glyph_index);
+ return true;
+ } else if (tokens[6] == "texture_idx") {
+ r_ret = get_glyph_texture_idx(cache_index, sz, glyph_index);
+ return true;
+ }
+ } else if (tokens.size() == 7 && tokens[4] == "kerning_overrides") {
+ Vector2i gp = Vector2i(tokens[5].to_int(), tokens[6].to_int());
+ r_ret = get_kerning(cache_index, sz.x, gp);
+ return true;
+ }
}
+ }
+ return false;
+}
- ofs.x += draw_char(p_canvas_item, p_pos + ofs, p_text[i], p_text[i + 1], with_outline ? p_outline_modulate : p_modulate, with_outline);
- ++chars_drawn;
+void FontData::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+
+ p_list->push_back(PropertyInfo(Variant::BOOL, "antialiased", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::INT, "msdf_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::INT, "fixed_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::BOOL, "force_autohinter", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+
+ Vector<String> lang_over = get_language_support_overrides();
+ for (int i = 0; i < lang_over.size(); i++) {
+ p_list->push_back(PropertyInfo(Variant::BOOL, "language_support_override/" + lang_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ }
+ Vector<String> scr_over = get_script_support_overrides();
+ for (int i = 0; i < scr_over.size(); i++) {
+ p_list->push_back(PropertyInfo(Variant::BOOL, "script_support_override/" + scr_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
}
+ for (int i = 0; i < cache.size(); i++) {
+ String prefix = "cache/" + itos(i) + "/";
+ Array sizes = get_size_cache_list(i);
+ p_list->push_back(PropertyInfo(Variant::DICTIONARY, prefix + "variation_coordinates", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ for (int j = 0; j < sizes.size(); j++) {
+ Vector2i sz = sizes[j];
+ String prefix_sz = prefix + itos(sz.x) + "/" + itos(sz.y) + "/";
+ if (sz.y == 0) {
+ p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "ascent", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "descent", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "underline_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "underline_thickness", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::BOOL, prefix_sz + "spacing_glyph", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::BOOL, prefix_sz + "spacing_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ }
- if (has_outline()) {
- ofs = Vector2(0, 0);
- for (int i = 0; i < chars_drawn; i++) {
- ofs.x += draw_char(p_canvas_item, p_pos + ofs, p_text[i], p_text[i + 1], p_modulate, false);
+ int tx_cnt = get_texture_count(i, sz);
+ for (int k = 0; k < tx_cnt; k++) {
+ p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, prefix_sz + "textures/" + itos(k) + "/offsets", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::OBJECT, prefix_sz + "textures/" + itos(k) + "/image", PROPERTY_HINT_RESOURCE_TYPE, "Image", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT));
+ }
+ Array glyphs = get_glyph_list(i, sz);
+ for (int k = 0; k < glyphs.size(); k++) {
+ const int32_t &gl = glyphs[k];
+ if (sz.y == 0) {
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "glyphs/" + itos(gl) + "/advance", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ }
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "glyphs/" + itos(gl) + "/offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "glyphs/" + itos(gl) + "/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::RECT2, prefix_sz + "glyphs/" + itos(gl) + "/uv_rect", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::INT, prefix_sz + "glyphs/" + itos(gl) + "/texture_idx", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ }
+ if (sz.y == 0) {
+ Array kerning_map = get_kerning_list(i, sz.x);
+ for (int k = 0; k < kerning_map.size(); k++) {
+ const Vector2i &gl_pair = kerning_map[k];
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "kerning_overrides/" + itos(gl_pair.x) + "/" + itos(gl_pair.y), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ }
+ }
}
}
}
-void Font::update_changes() {
- emit_changed();
+void FontData::reset_state() {
+ _clear_cache();
+ data.clear();
+ data_ptr = nullptr;
+ data_size = 0;
+ cache.clear();
+
+ antialiased = true;
+ msdf = false;
+ force_autohinter = false;
+ hinting = TextServer::HINTING_LIGHT;
+ msdf_pixel_range = 14;
+ msdf_size = 128;
+ oversampling = 0.f;
}
-void Font::_bind_methods() {
- ClassDB::bind_method(D_METHOD("draw", "canvas_item", "position", "string", "modulate", "clip_w", "outline_modulate"), &Font::draw, DEFVAL(Color(1, 1, 1)), DEFVAL(-1), DEFVAL(Color(1, 1, 1)));
- ClassDB::bind_method(D_METHOD("get_ascent"), &Font::get_ascent);
- ClassDB::bind_method(D_METHOD("get_descent"), &Font::get_descent);
- ClassDB::bind_method(D_METHOD("get_height"), &Font::get_height);
- ClassDB::bind_method(D_METHOD("is_distance_field_hint"), &Font::is_distance_field_hint);
- ClassDB::bind_method(D_METHOD("get_char_size", "char", "next"), &Font::get_char_size, DEFVAL(0));
- ClassDB::bind_method(D_METHOD("get_string_size", "string"), &Font::get_string_size);
- ClassDB::bind_method(D_METHOD("get_wordwrap_string_size", "string", "width"), &Font::get_wordwrap_string_size);
- ClassDB::bind_method(D_METHOD("has_outline"), &Font::has_outline);
- ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "position", "char", "next", "modulate", "outline"), &Font::draw_char, DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(false));
- ClassDB::bind_method(D_METHOD("update_changes"), &Font::update_changes);
+/*************************************************************************/
+
+void FontData::set_data_ptr(const uint8_t *p_data, size_t p_size) {
+ data.clear();
+ data_ptr = p_data;
+ data_size = p_size;
+
+ if (data_ptr != nullptr) {
+ for (int i = 0; i < cache.size(); i++) {
+ if (cache[i].is_valid()) {
+ TS->font_set_data_ptr(cache[i], data_ptr, data_size);
+ }
+ }
+ }
}
-Font::Font() {
+void FontData::set_data(const PackedByteArray &p_data) {
+ data = p_data;
+ data_ptr = data.ptr();
+ data_size = data.size();
+
+ if (data_ptr != nullptr) {
+ for (int i = 0; i < cache.size(); i++) {
+ if (cache[i].is_valid()) {
+ TS->font_set_data_ptr(cache[i], data_ptr, data_size);
+ }
+ }
+ }
}
-/////////////////////////////////////////////////////////////////
+PackedByteArray FontData::get_data() const {
+ return data;
+}
-void BitmapFont::_set_chars(const Vector<int> &p_chars) {
- int len = p_chars.size();
- //char 1 charsize 1 texture, 4 rect, 2 align, advance 1
- ERR_FAIL_COND(len % 9);
- if (!len) {
- return; //none to do
+void FontData::set_antialiased(bool p_antialiased) {
+ if (antialiased != p_antialiased) {
+ antialiased = p_antialiased;
+ for (int i = 0; i < cache.size(); i++) {
+ _ensure_rid(i);
+ TS->font_set_antialiased(cache[i], antialiased);
+ }
+ emit_changed();
}
- int chars = len / 9;
+}
+
+bool FontData::is_antialiased() const {
+ return antialiased;
+}
- const int *r = p_chars.ptr();
- for (int i = 0; i < chars; i++) {
- const int *data = &r[i * 9];
- add_char(data[0], data[1], Rect2(data[2], data[3], data[4], data[5]), Size2(data[6], data[7]), data[8]);
+void FontData::set_multichannel_signed_distance_field(bool p_msdf) {
+ if (msdf != p_msdf) {
+ msdf = p_msdf;
+ for (int i = 0; i < cache.size(); i++) {
+ _ensure_rid(i);
+ TS->font_set_multichannel_signed_distance_field(cache[i], msdf);
+ }
+ emit_changed();
}
}
-Vector<int> BitmapFont::_get_chars() const {
- Vector<int> chars;
+bool FontData::is_multichannel_signed_distance_field() const {
+ return msdf;
+}
- const char32_t *key = nullptr;
+void FontData::set_msdf_pixel_range(int p_msdf_pixel_range) {
+ if (msdf_pixel_range != p_msdf_pixel_range) {
+ msdf_pixel_range = p_msdf_pixel_range;
+ for (int i = 0; i < cache.size(); i++) {
+ _ensure_rid(i);
+ TS->font_set_msdf_pixel_range(cache[i], msdf_pixel_range);
+ }
+ emit_changed();
+ }
+}
- while ((key = char_map.next(key))) {
- const Character *c = char_map.getptr(*key);
- ERR_FAIL_COND_V(!c, Vector<int>());
- chars.push_back(*key);
- chars.push_back(c->texture_idx);
- chars.push_back(c->rect.position.x);
- chars.push_back(c->rect.position.y);
+int FontData::get_msdf_pixel_range() const {
+ return msdf_pixel_range;
+}
- chars.push_back(c->rect.size.x);
- chars.push_back(c->rect.size.y);
- chars.push_back(c->h_align);
- chars.push_back(c->v_align);
- chars.push_back(c->advance);
+void FontData::set_msdf_size(int p_msdf_size) {
+ if (msdf_size != p_msdf_size) {
+ msdf_size = p_msdf_size;
+ for (int i = 0; i < cache.size(); i++) {
+ _ensure_rid(i);
+ TS->font_set_msdf_size(cache[i], msdf_size);
+ }
+ emit_changed();
}
+}
- return chars;
+int FontData::get_msdf_size() const {
+ return msdf_size;
}
-void BitmapFont::_set_kernings(const Vector<int> &p_kernings) {
- int len = p_kernings.size();
- ERR_FAIL_COND(len % 3);
- if (!len) {
- return;
+void FontData::set_fixed_size(int p_fixed_size) {
+ if (fixed_size != p_fixed_size) {
+ fixed_size = p_fixed_size;
+ for (int i = 0; i < cache.size(); i++) {
+ _ensure_rid(i);
+ TS->font_set_fixed_size(cache[i], fixed_size);
+ }
+ emit_changed();
}
- const int *r = p_kernings.ptr();
+}
- for (int i = 0; i < len / 3; i++) {
- const int *data = &r[i * 3];
- add_kerning_pair(data[0], data[1], data[2]);
+int FontData::get_fixed_size() const {
+ return fixed_size;
+}
+
+void FontData::set_force_autohinter(bool p_force_autohinter) {
+ if (force_autohinter != p_force_autohinter) {
+ force_autohinter = p_force_autohinter;
+ for (int i = 0; i < cache.size(); i++) {
+ _ensure_rid(i);
+ TS->font_set_force_autohinter(cache[i], force_autohinter);
+ }
+ emit_changed();
}
}
-Vector<int> BitmapFont::_get_kernings() const {
- Vector<int> kernings;
+bool FontData::is_force_autohinter() const {
+ return force_autohinter;
+}
- for (Map<KerningPairKey, int>::Element *E = kerning_map.front(); E; E = E->next()) {
- kernings.push_back(E->key().A);
- kernings.push_back(E->key().B);
- kernings.push_back(E->get());
+void FontData::set_hinting(TextServer::Hinting p_hinting) {
+ if (hinting != p_hinting) {
+ hinting = p_hinting;
+ for (int i = 0; i < cache.size(); i++) {
+ _ensure_rid(i);
+ TS->font_set_hinting(cache[i], hinting);
+ }
+ emit_changed();
}
+}
- return kernings;
+TextServer::Hinting FontData::get_hinting() const {
+ return hinting;
}
-void BitmapFont::_set_textures(const Vector<Variant> &p_textures) {
- textures.clear();
- for (int i = 0; i < p_textures.size(); i++) {
- Ref<Texture2D> tex = p_textures[i];
- ERR_CONTINUE(!tex.is_valid());
- add_texture(tex);
+void FontData::set_oversampling(real_t p_oversampling) {
+ if (oversampling != p_oversampling) {
+ oversampling = p_oversampling;
+ for (int i = 0; i < cache.size(); i++) {
+ _ensure_rid(i);
+ TS->font_set_oversampling(cache[i], oversampling);
+ }
+ emit_changed();
}
}
-Vector<Variant> BitmapFont::_get_textures() const {
- Vector<Variant> rtextures;
- for (int i = 0; i < textures.size(); i++) {
- rtextures.push_back(textures[i]);
+real_t FontData::get_oversampling() const {
+ return oversampling;
+}
+
+RID FontData::find_cache(const Dictionary &p_variation_coordinates) const {
+ // Find existing variation cache.
+ const Dictionary &supported_coords = get_supported_variation_list();
+ for (int i = 0; i < cache.size(); i++) {
+ if (cache[i].is_valid()) {
+ const Dictionary &cache_var = TS->font_get_variation_coordinates(cache[i]);
+ bool match = true;
+ for (const Variant *V = supported_coords.next(nullptr); V && match; V = supported_coords.next(V)) {
+ const Vector3 &def = supported_coords[*V];
+
+ real_t c_v = def.z;
+ if (cache_var.has(*V)) {
+ real_t val = cache_var[*V];
+ c_v = CLAMP(val, def.x, def.y);
+ }
+ if (cache_var.has(TS->tag_to_name(*V))) {
+ real_t val = cache_var[TS->tag_to_name(*V)];
+ c_v = CLAMP(val, def.x, def.y);
+ }
+
+ real_t s_v = def.z;
+ if (p_variation_coordinates.has(*V)) {
+ real_t val = p_variation_coordinates[*V];
+ s_v = CLAMP(val, def.x, def.y);
+ }
+ if (p_variation_coordinates.has(TS->tag_to_name(*V))) {
+ real_t val = p_variation_coordinates[TS->tag_to_name(*V)];
+ s_v = CLAMP(val, def.x, def.y);
+ }
+
+ match = match && (c_v == s_v);
+ }
+ if (match) {
+ return cache[i];
+ }
+ }
}
- return rtextures;
+
+ // Create new variation cache.
+ int idx = cache.size();
+ _ensure_rid(idx);
+ TS->font_set_variation_coordinates(cache[idx], p_variation_coordinates);
+ return cache[idx];
}
-Error BitmapFont::create_from_fnt(const String &p_file) {
- //fnt format used by angelcode bmfont
- //http://www.angelcode.com/products/bmfont/
+int FontData::get_cache_count() const {
+ return cache.size();
+}
- FileAccess *f = FileAccess::open(p_file, FileAccess::READ);
+void FontData::clear_cache() {
+ _clear_cache();
+ cache.clear();
+}
- ERR_FAIL_COND_V_MSG(!f, ERR_FILE_NOT_FOUND, "Can't open font: " + p_file + ".");
+void FontData::remove_cache(int p_cache_index) {
+ ERR_FAIL_INDEX(p_cache_index, cache.size());
+ if (cache[p_cache_index].is_valid()) {
+ TS->free(cache.write[p_cache_index]);
+ }
+ cache.remove(p_cache_index);
+ emit_changed();
+}
- clear();
+Array FontData::get_size_cache_list(int p_cache_index) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_size_cache_list(cache[p_cache_index]);
+}
- while (true) {
- String line = f->get_line();
+void FontData::clear_size_cache(int p_cache_index) {
+ _ensure_rid(p_cache_index);
+ TS->font_clear_size_cache(cache[p_cache_index]);
+}
- int delimiter = line.find(" ");
- String type = line.substr(0, delimiter);
- int pos = delimiter + 1;
- Map<String, String> keys;
+void FontData::remove_size_cache(int p_cache_index, const Vector2i &p_size) {
+ _ensure_rid(p_cache_index);
+ TS->font_remove_size_cache(cache[p_cache_index], p_size);
+}
- while (pos < line.size() && line[pos] == ' ') {
- pos++;
- }
+void FontData::set_variation_coordinates(int p_cache_index, const Dictionary &p_variation_coordinates) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_variation_coordinates(cache[p_cache_index], p_variation_coordinates);
+ emit_changed();
+}
- while (pos < line.size()) {
- int eq = line.find("=", pos);
- if (eq == -1) {
- break;
- }
- String key = line.substr(pos, eq - pos);
- int end = -1;
- String value;
- if (line[eq + 1] == '"') {
- end = line.find("\"", eq + 2);
- if (end == -1) {
- break;
- }
- value = line.substr(eq + 2, end - 1 - eq - 1);
- pos = end + 1;
- } else {
- end = line.find(" ", eq + 1);
- if (end == -1) {
- end = line.size();
- }
+Dictionary FontData::get_variation_coordinates(int p_cache_index) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_variation_coordinates(cache[p_cache_index]);
+}
- value = line.substr(eq + 1, end - eq);
+void FontData::set_ascent(int p_cache_index, int p_size, real_t p_ascent) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_ascent(cache[p_cache_index], p_size, p_ascent);
+}
- pos = end;
- }
+real_t FontData::get_ascent(int p_cache_index, int p_size) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_ascent(cache[p_cache_index], p_size);
+}
- while (pos < line.size() && line[pos] == ' ') {
- pos++;
- }
+void FontData::set_descent(int p_cache_index, int p_size, real_t p_descent) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_descent(cache[p_cache_index], p_size, p_descent);
+}
- keys[key] = value;
- }
+real_t FontData::get_descent(int p_cache_index, int p_size) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_descent(cache[p_cache_index], p_size);
+}
- if (type == "info") {
- if (keys.has("face")) {
- set_name(keys["face"]);
- }
- /*
- if (keys.has("size"))
- font->set_height(keys["size"].to_int());
- */
-
- } else if (type == "common") {
- if (keys.has("lineHeight")) {
- set_height(keys["lineHeight"].to_int());
- }
- if (keys.has("base")) {
- set_ascent(keys["base"].to_int());
- }
+void FontData::set_underline_position(int p_cache_index, int p_size, real_t p_underline_position) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_underline_position(cache[p_cache_index], p_size, p_underline_position);
+}
- } else if (type == "page") {
- if (keys.has("file")) {
- String base_dir = p_file.get_base_dir();
- String file = base_dir.plus_file(keys["file"]);
- Ref<Texture2D> tex = ResourceLoader::load(file);
- if (tex.is_null()) {
- ERR_PRINT("Can't load font texture!");
- } else {
- add_texture(tex);
- }
- }
- } else if (type == "char") {
- char32_t idx = 0;
- if (keys.has("id")) {
- idx = keys["id"].to_int();
- }
+real_t FontData::get_underline_position(int p_cache_index, int p_size) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_underline_position(cache[p_cache_index], p_size);
+}
- Rect2 rect;
+void FontData::set_underline_thickness(int p_cache_index, int p_size, real_t p_underline_thickness) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_underline_thickness(cache[p_cache_index], p_size, p_underline_thickness);
+}
- if (keys.has("x")) {
- rect.position.x = keys["x"].to_int();
- }
- if (keys.has("y")) {
- rect.position.y = keys["y"].to_int();
- }
- if (keys.has("width")) {
- rect.size.width = keys["width"].to_int();
- }
- if (keys.has("height")) {
- rect.size.height = keys["height"].to_int();
- }
+real_t FontData::get_underline_thickness(int p_cache_index, int p_size) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_underline_thickness(cache[p_cache_index], p_size);
+}
- Point2 ofs;
+void FontData::set_scale(int p_cache_index, int p_size, real_t p_scale) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_scale(cache[p_cache_index], p_size, p_scale);
+}
- if (keys.has("xoffset")) {
- ofs.x = keys["xoffset"].to_int();
- }
- if (keys.has("yoffset")) {
- ofs.y = keys["yoffset"].to_int();
- }
+real_t FontData::get_scale(int p_cache_index, int p_size) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_scale(cache[p_cache_index], p_size);
+}
- int texture = 0;
- if (keys.has("page")) {
- texture = keys["page"].to_int();
- }
- int advance = -1;
- if (keys.has("xadvance")) {
- advance = keys["xadvance"].to_int();
- }
+void FontData::set_spacing(int p_cache_index, int p_size, TextServer::SpacingType p_spacing, int p_value) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_spacing(cache[p_cache_index], p_size, p_spacing, p_value);
+}
- add_char(idx, texture, rect, ofs, advance);
+int FontData::get_spacing(int p_cache_index, int p_size, TextServer::SpacingType p_spacing) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_spacing(cache[p_cache_index], p_size, p_spacing);
+}
- } else if (type == "kerning") {
- char32_t first = 0, second = 0;
- int k = 0;
+int FontData::get_texture_count(int p_cache_index, const Vector2i &p_size) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_texture_count(cache[p_cache_index], p_size);
+}
- if (keys.has("first")) {
- first = keys["first"].to_int();
- }
- if (keys.has("second")) {
- second = keys["second"].to_int();
- }
- if (keys.has("amount")) {
- k = keys["amount"].to_int();
- }
+void FontData::clear_textures(int p_cache_index, const Vector2i &p_size) {
+ _ensure_rid(p_cache_index);
+ TS->font_clear_textures(cache[p_cache_index], p_size);
+}
- add_kerning_pair(first, second, -k);
- }
+void FontData::remove_texture(int p_cache_index, const Vector2i &p_size, int p_texture_index) {
+ _ensure_rid(p_cache_index);
+ TS->font_remove_texture(cache[p_cache_index], p_size, p_texture_index);
+}
- if (f->eof_reached()) {
- break;
- }
- }
+void FontData::set_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_texture_image(cache[p_cache_index], p_size, p_texture_index, p_image);
+}
- memdelete(f);
+Ref<Image> FontData::get_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_texture_image(cache[p_cache_index], p_size, p_texture_index);
+}
- return OK;
+void FontData::set_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_texture_offsets(cache[p_cache_index], p_size, p_texture_index, p_offset);
}
-void BitmapFont::set_height(float p_height) {
- height = p_height;
+PackedInt32Array FontData::get_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_texture_offsets(cache[p_cache_index], p_size, p_texture_index);
}
-float BitmapFont::get_height() const {
- return height;
+Array FontData::get_glyph_list(int p_cache_index, const Vector2i &p_size) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_glyph_list(cache[p_cache_index], p_size);
}
-void BitmapFont::set_ascent(float p_ascent) {
- ascent = p_ascent;
+void FontData::clear_glyphs(int p_cache_index, const Vector2i &p_size) {
+ _ensure_rid(p_cache_index);
+ TS->font_clear_glyphs(cache[p_cache_index], p_size);
}
-float BitmapFont::get_ascent() const {
- return ascent;
+void FontData::remove_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) {
+ _ensure_rid(p_cache_index);
+ TS->font_remove_glyph(cache[p_cache_index], p_size, p_glyph);
}
-float BitmapFont::get_descent() const {
- return height - ascent;
+void FontData::set_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph, const Vector2 &p_advance) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_glyph_advance(cache[p_cache_index], p_size, p_glyph, p_advance);
}
-float BitmapFont::get_underline_position() const {
- return 2;
+Vector2 FontData::get_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_glyph_advance(cache[p_cache_index], p_size, p_glyph);
}
-float BitmapFont::get_underline_thickness() const {
- return 1;
+void FontData::set_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_glyph_offset(cache[p_cache_index], p_size, p_glyph, p_offset);
}
-void BitmapFont::add_texture(const Ref<Texture2D> &p_texture) {
- ERR_FAIL_COND_MSG(p_texture.is_null(), "It's not a reference to a valid Texture object.");
- textures.push_back(p_texture);
+Vector2 FontData::get_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_glyph_offset(cache[p_cache_index], p_size, p_glyph);
}
-int BitmapFont::get_texture_count() const {
- return textures.size();
-};
+void FontData::set_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_glyph_size(cache[p_cache_index], p_size, p_glyph, p_gl_size);
+}
-Ref<Texture2D> BitmapFont::get_texture(int p_idx) const {
- ERR_FAIL_INDEX_V(p_idx, textures.size(), Ref<Texture2D>());
- return textures[p_idx];
-};
+Vector2 FontData::get_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_glyph_size(cache[p_cache_index], p_size, p_glyph);
+}
-int BitmapFont::get_character_count() const {
- return char_map.size();
-};
+void FontData::set_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_glyph_uv_rect(cache[p_cache_index], p_size, p_glyph, p_uv_rect);
+}
-Vector<char32_t> BitmapFont::get_char_keys() const {
- Vector<char32_t> chars;
- chars.resize(char_map.size());
- const char32_t *ct = nullptr;
- int count = 0;
- while ((ct = char_map.next(ct))) {
- chars.write[count++] = *ct;
- };
+Rect2 FontData::get_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_glyph_uv_rect(cache[p_cache_index], p_size, p_glyph);
+}
- return chars;
-};
+void FontData::set_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_glyph_texture_idx(cache[p_cache_index], p_size, p_glyph, p_texture_idx);
+}
-BitmapFont::Character BitmapFont::get_character(char32_t p_char) const {
- if (!char_map.has(p_char)) {
- ERR_FAIL_V(Character());
- };
+int FontData::get_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_glyph_texture_idx(cache[p_cache_index], p_size, p_glyph);
+}
- return char_map[p_char];
-};
+Array FontData::get_kerning_list(int p_cache_index, int p_size) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_kerning_list(cache[p_cache_index], p_size);
+}
+
+void FontData::clear_kerning_map(int p_cache_index, int p_size) {
+ _ensure_rid(p_cache_index);
+ TS->font_clear_kerning_map(cache[p_cache_index], p_size);
+}
-void BitmapFont::add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) {
- if (p_advance < 0) {
- p_advance = p_rect.size.width;
+void FontData::remove_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair) {
+ _ensure_rid(p_cache_index);
+ TS->font_remove_kerning(cache[p_cache_index], p_size, p_glyph_pair);
+}
+
+void FontData::set_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) {
+ _ensure_rid(p_cache_index);
+ TS->font_set_kerning(cache[p_cache_index], p_size, p_glyph_pair, p_kerning);
+}
+
+Vector2 FontData::get_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair) const {
+ _ensure_rid(p_cache_index);
+ return TS->font_get_kerning(cache[p_cache_index], p_size, p_glyph_pair);
+}
+
+void FontData::render_range(int p_cache_index, const Vector2i &p_size, char32_t p_start, char32_t p_end) {
+ _ensure_rid(p_cache_index);
+ TS->font_render_range(cache[p_cache_index], p_size, p_start, p_end);
+}
+
+void FontData::render_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_index) {
+ _ensure_rid(p_cache_index);
+ TS->font_render_glyph(cache[p_cache_index], p_size, p_index);
+}
+
+RID FontData::get_cache_rid(int p_cache_index) const {
+ _ensure_rid(p_cache_index);
+ return cache[p_cache_index];
+}
+
+bool FontData::is_language_supported(const String &p_language) const {
+ _ensure_rid(0);
+ return TS->font_is_language_supported(cache[0], p_language);
+}
+
+void FontData::set_language_support_override(const String &p_language, bool p_supported) {
+ _ensure_rid(0);
+ TS->font_set_language_support_override(cache[0], p_language, p_supported);
+}
+
+bool FontData::get_language_support_override(const String &p_language) const {
+ _ensure_rid(0);
+ return TS->font_get_language_support_override(cache[0], p_language);
+}
+
+void FontData::remove_language_support_override(const String &p_language) {
+ _ensure_rid(0);
+ TS->font_remove_language_support_override(cache[0], p_language);
+}
+
+Vector<String> FontData::get_language_support_overrides() const {
+ _ensure_rid(0);
+ return TS->font_get_language_support_overrides(cache[0]);
+}
+
+bool FontData::is_script_supported(const String &p_script) const {
+ _ensure_rid(0);
+ return TS->font_is_script_supported(cache[0], p_script);
+}
+
+void FontData::set_script_support_override(const String &p_script, bool p_supported) {
+ _ensure_rid(0);
+ TS->font_set_script_support_override(cache[0], p_script, p_supported);
+}
+
+bool FontData::get_script_support_override(const String &p_script) const {
+ _ensure_rid(0);
+ return TS->font_get_script_support_override(cache[0], p_script);
+}
+
+void FontData::remove_script_support_override(const String &p_script) {
+ _ensure_rid(0);
+ TS->font_remove_script_support_override(cache[0], p_script);
+}
+
+Vector<String> FontData::get_script_support_overrides() const {
+ _ensure_rid(0);
+ return TS->font_get_script_support_overrides(cache[0]);
+}
+
+bool FontData::has_char(char32_t p_char) const {
+ _ensure_rid(0);
+ return TS->font_has_char(cache[0], p_char);
+}
+
+String FontData::get_supported_chars() const {
+ _ensure_rid(0);
+ return TS->font_get_supported_chars(cache[0]);
+}
+
+int32_t FontData::get_glyph_index(int p_size, char32_t p_char, char32_t p_variation_selector) const {
+ _ensure_rid(0);
+ return TS->font_get_glyph_index(cache[0], p_size, p_char, p_variation_selector);
+}
+
+Dictionary FontData::get_supported_feature_list() const {
+ _ensure_rid(0);
+ return TS->font_supported_feature_list(cache[0]);
+}
+
+Dictionary FontData::get_supported_variation_list() const {
+ _ensure_rid(0);
+ return TS->font_supported_variation_list(cache[0]);
+}
+
+FontData::FontData() {
+ /* NOP */
+}
+
+FontData::~FontData() {
+ _clear_cache();
+}
+
+/*************************************************************************/
+
+void Font::_data_changed() {
+ for (int i = 0; i < rids.size(); i++) {
+ rids.write[i] = RID();
+ }
+ emit_changed();
+}
+
+void Font::_ensure_rid(int p_index) const {
+ // Find or create cache record.
+ for (int i = 0; i < rids.size(); i++) {
+ if (!rids[i].is_valid() && data[i].is_valid()) {
+ rids.write[i] = data[i]->find_cache(variation_coordinates);
+ }
}
+}
+
+void Font::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("add_data", "data"), &Font::add_data);
+ ClassDB::bind_method(D_METHOD("set_data", "idx", "data"), &Font::set_data);
+ ClassDB::bind_method(D_METHOD("get_data_count"), &Font::get_data_count);
+ ClassDB::bind_method(D_METHOD("get_data", "idx"), &Font::get_data);
+ ClassDB::bind_method(D_METHOD("get_data_rid", "idx"), &Font::get_data_rid);
+ ClassDB::bind_method(D_METHOD("clear_data"), &Font::clear_data);
+ ClassDB::bind_method(D_METHOD("remove_data", "idx"), &Font::remove_data);
+
+ ClassDB::bind_method(D_METHOD("set_base_size", "size"), &Font::set_base_size);
+ ClassDB::bind_method(D_METHOD("get_base_size"), &Font::get_base_size);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "base_size"), "set_base_size", "get_base_size");
+
+ ClassDB::bind_method(D_METHOD("set_variation_coordinates", "variation_coordinates"), &Font::set_variation_coordinates);
+ ClassDB::bind_method(D_METHOD("get_variation_coordinates"), &Font::get_variation_coordinates);
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "variation_coordinates"), "set_variation_coordinates", "get_variation_coordinates");
- Character c;
- c.rect = p_rect;
- c.texture_idx = p_texture_idx;
- c.v_align = p_align.y;
- c.advance = p_advance;
- c.h_align = p_align.x;
+ ClassDB::bind_method(D_METHOD("set_spacing", "spacing", "value"), &Font::set_spacing);
+ ClassDB::bind_method(D_METHOD("get_spacing", "spacing"), &Font::get_spacing);
- char_map[p_char] = c;
+ ADD_GROUP("Extra Spacing", "spacing");
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_top"), "set_spacing", "get_spacing", TextServer::SPACING_TOP);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_bottom"), "set_spacing", "get_spacing", TextServer::SPACING_BOTTOM);
+
+ ClassDB::bind_method(D_METHOD("get_height", "size"), &Font::get_height, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("get_ascent", "size"), &Font::get_ascent, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("get_descent", "size"), &Font::get_descent, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("get_underline_position", "size"), &Font::get_underline_position, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("get_underline_thickness", "size"), &Font::get_underline_thickness, DEFVAL(-1));
+
+ ClassDB::bind_method(D_METHOD("get_string_size", "text", "size", "align", "width", "flags"), &Font::get_string_size, DEFVAL(-1), DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
+ ClassDB::bind_method(D_METHOD("get_multiline_string_size", "text", "width", "size", "flags"), &Font::get_multiline_string_size, DEFVAL(-1), DEFVAL(-1), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND));
+
+ ClassDB::bind_method(D_METHOD("draw_string", "canvas_item", "pos", "text", "align", "width", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
+ ClassDB::bind_method(D_METHOD("draw_multiline_string", "canvas_item", "pos", "text", "align", "width", "max_lines", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_multiline_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
+
+ ClassDB::bind_method(D_METHOD("get_char_size", "char", "next", "size"), &Font::get_char_size, DEFVAL(0), DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "pos", "char", "next", "size", "modulate", "outline_size", "outline_modulate"), &Font::draw_char, DEFVAL(0), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)));
+
+ ClassDB::bind_method(D_METHOD("has_char", "char"), &Font::has_char);
+ ClassDB::bind_method(D_METHOD("get_supported_chars"), &Font::get_supported_chars);
+
+ ClassDB::bind_method(D_METHOD("update_changes"), &Font::update_changes);
}
-void BitmapFont::add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) {
- KerningPairKey kpk;
- kpk.A = p_A;
- kpk.B = p_B;
+bool Font::_set(const StringName &p_name, const Variant &p_value) {
+ Vector<String> tokens = p_name.operator String().split("/");
+#ifndef DISABLE_DEPRECATED
+ if (tokens.size() == 1 && tokens[0] == "font_data") {
+ // Compatibility, DynamicFont main data.
+ Ref<FontData> fd = p_value;
+ if (fd.is_valid()) {
+ add_data(fd);
+ return true;
+ }
+ return false;
+ } else if (tokens.size() == 2 && tokens[0] == "fallback") {
+ // Compatibility, DynamicFont fallback data.
+ Ref<FontData> fd = p_value;
+ if (fd.is_valid()) {
+ add_data(fd);
+ return true;
+ }
+ return false;
+ } else if (tokens.size() == 1 && tokens[0] == "fallback") {
+ // Compatibility, BitmapFont fallback data.
+ Ref<Font> f = p_value;
+ if (f.is_valid()) {
+ for (int i = 0; i < f->get_data_count(); i++) {
+ add_data(f->get_data(i));
+ }
+ return true;
+ }
+ return false;
+ }
+#endif /* DISABLE_DEPRECATED */
+ if (tokens.size() == 2 && tokens[0] == "data") {
+ int idx = tokens[1].to_int();
+ Ref<FontData> fd = p_value;
+ if (fd.is_valid()) {
+ if (idx == data.size()) {
+ add_data(fd);
+ return true;
+ } else if (idx >= 0 && idx < data.size()) {
+ set_data(idx, fd);
+ return true;
+ } else {
+ return false;
+ }
+ } else if (idx >= 0 && idx < data.size()) {
+ remove_data(idx);
+ return true;
+ }
+ }
+ return false;
+}
- if (p_kerning == 0 && kerning_map.has(kpk)) {
- kerning_map.erase(kpk);
- } else {
- kerning_map[kpk] = p_kerning;
+bool Font::_get(const StringName &p_name, Variant &r_ret) const {
+ Vector<String> tokens = p_name.operator String().split("/");
+ if (tokens.size() == 2 && tokens[0] == "data") {
+ int idx = tokens[1].to_int();
+
+ if (idx == data.size()) {
+ r_ret = Ref<FontData>();
+ return true;
+ } else if (idx >= 0 && idx < data.size()) {
+ r_ret = get_data(idx);
+ return true;
+ }
}
+
+ return false;
}
-Vector<BitmapFont::KerningPairKey> BitmapFont::get_kerning_pair_keys() const {
- Vector<BitmapFont::KerningPairKey> ret;
- ret.resize(kerning_map.size());
- int i = 0;
+void Font::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (int i = 0; i < data.size(); i++) {
+ p_list->push_back(PropertyInfo(Variant::OBJECT, "data/" + itos(i), PROPERTY_HINT_RESOURCE_TYPE, "FontData"));
+ }
+ p_list->push_back(PropertyInfo(Variant::OBJECT, "data/" + itos(data.size()), PROPERTY_HINT_RESOURCE_TYPE, "FontData"));
+}
- for (Map<KerningPairKey, int>::Element *E = kerning_map.front(); E; E = E->next()) {
- ret.write[i++] = E->key();
+void Font::reset_state() {
+ for (int i = 0; i < data.size(); i++) {
+ if (data[i].is_valid()) {
+ data.write[i]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ }
}
+ cache.clear();
+ cache_wrap.clear();
+ data.clear();
+ rids.clear();
+
+ base_size = 16;
+ variation_coordinates.clear();
+ spacing_bottom = 0;
+ spacing_top = 0;
+}
- return ret;
+Dictionary Font::get_feature_list() const {
+ Dictionary out;
+ for (int i = 0; i < data.size(); i++) {
+ Dictionary data_ftrs = data[i]->get_supported_feature_list();
+ for (const Variant *ftr = data_ftrs.next(nullptr); ftr != nullptr; ftr = data_ftrs.next(ftr)) {
+ out[*ftr] = data_ftrs[*ftr];
+ }
+ }
+ return out;
}
-int BitmapFont::get_kerning_pair(char32_t p_A, char32_t p_B) const {
- KerningPairKey kpk;
- kpk.A = p_A;
- kpk.B = p_B;
+void Font::add_data(const Ref<FontData> &p_data) {
+ ERR_FAIL_COND(p_data.is_null());
+ data.push_back(p_data);
+ rids.push_back(RID());
- const Map<KerningPairKey, int>::Element *E = kerning_map.find(kpk);
- if (E) {
- return E->get();
+ if (data[data.size() - 1].is_valid()) {
+ data.write[data.size() - 1]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED);
}
- return 0;
+ cache.clear();
+ cache_wrap.clear();
+
+ emit_changed();
+ notify_property_list_changed();
}
-void BitmapFont::set_distance_field_hint(bool p_distance_field) {
- distance_field_hint = p_distance_field;
+void Font::set_data(int p_idx, const Ref<FontData> &p_data) {
+ ERR_FAIL_COND(p_data.is_null());
+ ERR_FAIL_INDEX(p_idx, data.size());
+
+ if (data[p_idx].is_valid()) {
+ data.write[p_idx]->disconnect(SNAME("changed"), callable_mp(this, &Font::_data_changed));
+ }
+
+ data.write[p_idx] = p_data;
+ rids.write[p_idx] = RID();
+
+ if (data[p_idx].is_valid()) {
+ data.write[p_idx]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ }
+
+ cache.clear();
+ cache_wrap.clear();
+
emit_changed();
+ notify_property_list_changed();
}
-bool BitmapFont::is_distance_field_hint() const {
- return distance_field_hint;
+int Font::get_data_count() const {
+ return data.size();
}
-void BitmapFont::clear() {
- height = 1;
- ascent = 0;
- char_map.clear();
- textures.clear();
- kerning_map.clear();
- distance_field_hint = false;
+Ref<FontData> Font::get_data(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx, data.size(), Ref<FontData>());
+ return data[p_idx];
}
-Size2 Font::get_string_size(const String &p_string) const {
- float w = 0;
+RID Font::get_data_rid(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx, data.size(), RID());
+ _ensure_rid(p_idx);
+ return rids[p_idx];
+}
- int l = p_string.length();
- if (l == 0) {
- return Size2(0, get_height());
+void Font::clear_data() {
+ for (int i = 0; i < data.size(); i++) {
+ if (data[i].is_valid()) {
+ data.write[i]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ }
}
- const char32_t *sptr = &p_string[0];
+ data.clear();
+ rids.clear();
+}
+
+void Font::remove_data(int p_idx) {
+ ERR_FAIL_INDEX(p_idx, data.size());
- for (int i = 0; i < l; i++) {
- w += get_char_size(sptr[i], sptr[i + 1]).width;
+ if (data[p_idx].is_valid()) {
+ data.write[p_idx]->disconnect(SNAME("changed"), callable_mp(this, &Font::_data_changed));
}
- return Size2(w, get_height());
+ data.remove(p_idx);
+ rids.remove(p_idx);
+
+ cache.clear();
+ cache_wrap.clear();
+
+ emit_changed();
+ notify_property_list_changed();
}
-Size2 Font::get_wordwrap_string_size(const String &p_string, float p_width) const {
- ERR_FAIL_COND_V(p_width <= 0, Vector2(0, get_height()));
+void Font::set_base_size(int p_size) {
+ base_size = p_size;
+}
+
+int Font::get_base_size() const {
+ return base_size;
+}
+
+void Font::set_variation_coordinates(const Dictionary &p_variation_coordinates) {
+ _data_changed();
+ variation_coordinates = p_variation_coordinates;
+}
+
+Dictionary Font::get_variation_coordinates() const {
+ return variation_coordinates;
+}
+
+void Font::set_spacing(TextServer::SpacingType p_spacing, int p_value) {
+ _data_changed();
+ switch (p_spacing) {
+ case TextServer::SPACING_TOP: {
+ spacing_top = p_value;
+ } break;
+ case TextServer::SPACING_BOTTOM: {
+ spacing_bottom = p_value;
+ } break;
+ default: {
+ ERR_FAIL_MSG("Invalid spacing type: " + itos(p_spacing));
+ } break;
+ }
+}
- int l = p_string.length();
- if (l == 0) {
- return Size2(p_width, get_height());
+int Font::get_spacing(TextServer::SpacingType p_spacing) const {
+ switch (p_spacing) {
+ case TextServer::SPACING_TOP: {
+ return spacing_top;
+ } break;
+ case TextServer::SPACING_BOTTOM: {
+ return spacing_bottom;
+ } break;
+ default: {
+ ERR_FAIL_V_MSG(0, "Invalid spacing type: " + itos(p_spacing));
+ } break;
}
+}
- float line_w = 0;
- float h = 0;
- float space_w = get_char_size(' ').width;
- Vector<String> lines = p_string.split("\n");
- for (int i = 0; i < lines.size(); i++) {
- h += get_height();
- String t = lines[i];
- line_w = 0;
- Vector<String> words = t.split(" ");
- for (int j = 0; j < words.size(); j++) {
- line_w += get_string_size(words[j]).x;
- if (line_w > p_width) {
- h += get_height();
- line_w = get_string_size(words[j]).x;
- } else {
- line_w += space_w;
- }
- }
+real_t Font::get_height(int p_size) const {
+ int size = (p_size <= 0) ? base_size : p_size;
+ real_t ret = 0.f;
+ for (int i = 0; i < data.size(); i++) {
+ _ensure_rid(i);
+ ret = MAX(ret, TS->font_get_ascent(rids[i], size) + TS->font_get_descent(rids[i], size));
}
+ return ret + spacing_bottom + spacing_top;
+}
- return Size2(p_width, h);
+real_t Font::get_ascent(int p_size) const {
+ int size = (p_size <= 0) ? base_size : p_size;
+ real_t ret = 0.f;
+ for (int i = 0; i < data.size(); i++) {
+ _ensure_rid(i);
+ ret = MAX(ret, TS->font_get_ascent(rids[i], size));
+ }
+ return ret + spacing_top;
}
-void BitmapFont::set_fallback(const Ref<BitmapFont> &p_fallback) {
- for (Ref<BitmapFont> fallback_child = p_fallback; fallback_child != nullptr; fallback_child = fallback_child->get_fallback()) {
- ERR_FAIL_COND_MSG(fallback_child == this, "Can't set as fallback one of its parents to prevent crashes due to recursive loop.");
+real_t Font::get_descent(int p_size) const {
+ int size = (p_size <= 0) ? base_size : p_size;
+ real_t ret = 0.f;
+ for (int i = 0; i < data.size(); i++) {
+ _ensure_rid(i);
+ ret = MAX(ret, TS->font_get_descent(rids[i], size));
}
+ return ret + spacing_bottom;
+}
- fallback = p_fallback;
+real_t Font::get_underline_position(int p_size) const {
+ int size = (p_size <= 0) ? base_size : p_size;
+ real_t ret = 0.f;
+ for (int i = 0; i < data.size(); i++) {
+ _ensure_rid(i);
+ ret = MAX(ret, TS->font_get_underline_position(rids[i], size));
+ }
+ return ret + spacing_top;
}
-Ref<BitmapFont> BitmapFont::get_fallback() const {
- return fallback;
+real_t Font::get_underline_thickness(int p_size) const {
+ int size = (p_size <= 0) ? base_size : p_size;
+ real_t ret = 0.f;
+ for (int i = 0; i < data.size(); i++) {
+ _ensure_rid(i);
+ ret = MAX(ret, TS->font_get_underline_thickness(rids[i], size));
+ }
+ return ret;
}
-float BitmapFont::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next, const Color &p_modulate, bool p_outline) const {
- const Character *c = char_map.getptr(p_char);
+Size2 Font::get_string_size(const String &p_text, int p_size, HAlign p_align, real_t p_width, uint8_t p_flags) const {
+ ERR_FAIL_COND_V(data.is_empty(), Size2());
- if (!c) {
- if (fallback.is_valid()) {
- return fallback->draw_char(p_canvas_item, p_pos, p_char, p_next, p_modulate, p_outline);
- }
- return 0;
+ int size = (p_size <= 0) ? base_size : p_size;
+
+ for (int i = 0; i < data.size(); i++) {
+ _ensure_rid(i);
}
- ERR_FAIL_COND_V(c->texture_idx < -1 || c->texture_idx >= textures.size(), 0);
- if (!p_outline && c->texture_idx != -1) {
- Point2 cpos = p_pos;
- cpos.x += c->h_align;
- cpos.y -= ascent;
- cpos.y += c->v_align;
- RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(cpos, c->rect.size), textures[c->texture_idx]->get_rid(), c->rect, p_modulate, false, RID(), RID(), Color(1, 1, 1, 1), false);
+ uint64_t hash = p_text.hash64();
+ if (p_align == HALIGN_FILL) {
+ hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash);
+ hash = hash_djb2_one_64(p_flags, hash);
}
+ hash = hash_djb2_one_64(size, hash);
- return get_char_size(p_char, p_next).width;
+ Ref<TextLine> buffer;
+ if (cache.has(hash)) {
+ buffer = cache.get(hash);
+ } else {
+ buffer.instantiate();
+ buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
+ cache.insert(hash, buffer);
+ }
+ return buffer->get_size();
}
-Size2 BitmapFont::get_char_size(char32_t p_char, char32_t p_next) const {
- const Character *c = char_map.getptr(p_char);
+Size2 Font::get_multiline_string_size(const String &p_text, real_t p_width, int p_size, uint8_t p_flags) const {
+ ERR_FAIL_COND_V(data.is_empty(), Size2());
- if (!c) {
- if (fallback.is_valid()) {
- return fallback->get_char_size(p_char, p_next);
- }
- return Size2();
+ int size = (p_size <= 0) ? base_size : p_size;
+
+ for (int i = 0; i < data.size(); i++) {
+ _ensure_rid(i);
}
- Size2 ret(c->advance, c->rect.size.y);
+ uint64_t hash = p_text.hash64();
+ uint64_t wrp_hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash);
+ wrp_hash = hash_djb2_one_64(p_flags, wrp_hash);
+ wrp_hash = hash_djb2_one_64(size, wrp_hash);
- if (p_next) {
- KerningPairKey kpk;
- kpk.A = p_char;
- kpk.B = p_next;
+ Ref<TextParagraph> lines_buffer;
+ if (cache_wrap.has(wrp_hash)) {
+ lines_buffer = cache_wrap.get(wrp_hash);
+ } else {
+ lines_buffer.instantiate();
+ lines_buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
+ lines_buffer->set_width(p_width);
+ lines_buffer->set_flags(p_flags);
+ cache_wrap.insert(wrp_hash, lines_buffer);
+ }
- const Map<KerningPairKey, int>::Element *E = kerning_map.find(kpk);
- if (E) {
- ret.width -= E->get();
+ Size2 ret;
+ for (int i = 0; i < lines_buffer->get_line_count(); i++) {
+ Size2 line_size = lines_buffer->get_line_size(i);
+ if (lines_buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) {
+ ret.x = MAX(ret.x, line_size.x);
+ ret.y += line_size.y;
+ } else {
+ ret.y = MAX(ret.y, line_size.y);
+ ret.x += line_size.x;
}
}
-
return ret;
}
-void BitmapFont::_bind_methods() {
- ClassDB::bind_method(D_METHOD("create_from_fnt", "path"), &BitmapFont::create_from_fnt);
- ClassDB::bind_method(D_METHOD("set_height", "px"), &BitmapFont::set_height);
+void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, real_t p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const {
+ ERR_FAIL_COND(data.is_empty());
- ClassDB::bind_method(D_METHOD("set_ascent", "px"), &BitmapFont::set_ascent);
+ int size = (p_size <= 0) ? base_size : p_size;
- ClassDB::bind_method(D_METHOD("add_kerning_pair", "char_a", "char_b", "kerning"), &BitmapFont::add_kerning_pair);
- ClassDB::bind_method(D_METHOD("get_kerning_pair", "char_a", "char_b"), &BitmapFont::get_kerning_pair);
+ for (int i = 0; i < data.size(); i++) {
+ _ensure_rid(i);
+ }
- ClassDB::bind_method(D_METHOD("add_texture", "texture"), &BitmapFont::add_texture);
- ClassDB::bind_method(D_METHOD("add_char", "character", "texture", "rect", "align", "advance"), &BitmapFont::add_char, DEFVAL(Point2()), DEFVAL(-1));
+ uint64_t hash = p_text.hash64();
+ if (p_align == HALIGN_FILL) {
+ hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash);
+ hash = hash_djb2_one_64(p_flags, hash);
+ }
+ hash = hash_djb2_one_64(size, hash);
- ClassDB::bind_method(D_METHOD("get_texture_count"), &BitmapFont::get_texture_count);
- ClassDB::bind_method(D_METHOD("get_texture", "idx"), &BitmapFont::get_texture);
+ Ref<TextLine> buffer;
+ if (cache.has(hash)) {
+ buffer = cache.get(hash);
+ } else {
+ buffer.instantiate();
+ buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
+ cache.insert(hash, buffer);
+ }
- ClassDB::bind_method(D_METHOD("set_distance_field_hint", "enable"), &BitmapFont::set_distance_field_hint);
+ Vector2 ofs = p_pos;
+ if (buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.y -= buffer->get_line_ascent();
+ } else {
+ ofs.x -= buffer->get_line_ascent();
+ }
- ClassDB::bind_method(D_METHOD("clear"), &BitmapFont::clear);
+ buffer->set_width(p_width);
+ buffer->set_align(p_align);
+ buffer->set_flags(p_flags);
- ClassDB::bind_method(D_METHOD("_set_chars"), &BitmapFont::_set_chars);
- ClassDB::bind_method(D_METHOD("_get_chars"), &BitmapFont::_get_chars);
+ if (p_outline_size > 0 && p_outline_modulate.a != 0.0f) {
+ buffer->draw_outline(p_canvas_item, ofs, p_outline_size, p_outline_modulate);
+ }
+ buffer->draw(p_canvas_item, ofs, p_modulate);
+}
- ClassDB::bind_method(D_METHOD("_set_kernings"), &BitmapFont::_set_kernings);
- ClassDB::bind_method(D_METHOD("_get_kernings"), &BitmapFont::_get_kernings);
+void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, float p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const {
+ ERR_FAIL_COND(data.is_empty());
- ClassDB::bind_method(D_METHOD("_set_textures"), &BitmapFont::_set_textures);
- ClassDB::bind_method(D_METHOD("_get_textures"), &BitmapFont::_get_textures);
+ int size = (p_size <= 0) ? base_size : p_size;
- ClassDB::bind_method(D_METHOD("set_fallback", "fallback"), &BitmapFont::set_fallback);
- ClassDB::bind_method(D_METHOD("get_fallback"), &BitmapFont::get_fallback);
+ for (int i = 0; i < data.size(); i++) {
+ _ensure_rid(i);
+ }
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "textures", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_textures", "_get_textures");
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "chars", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_chars", "_get_chars");
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "kernings", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_kernings", "_get_kernings");
+ uint64_t hash = p_text.hash64();
+ uint64_t wrp_hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash);
+ wrp_hash = hash_djb2_one_64(p_flags, wrp_hash);
+ wrp_hash = hash_djb2_one_64(size, wrp_hash);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "1,1024,1"), "set_height", "get_height");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ascent", PROPERTY_HINT_RANGE, "0,1024,1"), "set_ascent", "get_ascent");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "distance_field"), "set_distance_field_hint", "is_distance_field_hint");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "fallback", PROPERTY_HINT_RESOURCE_TYPE, "BitmapFont"), "set_fallback", "get_fallback");
-}
+ Ref<TextParagraph> lines_buffer;
+ if (cache_wrap.has(wrp_hash)) {
+ lines_buffer = cache_wrap.get(wrp_hash);
+ } else {
+ lines_buffer.instantiate();
+ lines_buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
+ lines_buffer->set_width(p_width);
+ lines_buffer->set_flags(p_flags);
+ cache_wrap.insert(wrp_hash, lines_buffer);
+ }
+
+ lines_buffer->set_align(p_align);
-BitmapFont::BitmapFont() {
- clear();
+ Vector2 lofs = p_pos;
+ for (int i = 0; i < lines_buffer->get_line_count(); i++) {
+ if (lines_buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) {
+ if (i == 0) {
+ lofs.y -= lines_buffer->get_line_ascent(0);
+ }
+ } else {
+ if (i == 0) {
+ lofs.x -= lines_buffer->get_line_ascent(0);
+ }
+ }
+ if (p_width > 0) {
+ lines_buffer->set_align(p_align);
+ }
+
+ if (p_outline_size > 0 && p_outline_modulate.a != 0.0f) {
+ lines_buffer->draw_line_outline(p_canvas_item, lofs, i, p_outline_size, p_outline_modulate);
+ }
+ lines_buffer->draw_line(p_canvas_item, lofs, i, p_modulate);
+
+ Size2 line_size = lines_buffer->get_line_size(i);
+ if (lines_buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) {
+ lofs.y += line_size.y;
+ } else {
+ lofs.x += line_size.x;
+ }
+
+ if ((p_max_lines > 0) && (i >= p_max_lines)) {
+ return;
+ }
+ }
}
-BitmapFont::~BitmapFont() {
- clear();
+Size2 Font::get_char_size(char32_t p_char, char32_t p_next, int p_size) const {
+ int size = (p_size <= 0) ? base_size : p_size;
+
+ for (int i = 0; i < data.size(); i++) {
+ _ensure_rid(i);
+ if (data[i]->has_char(p_char)) {
+ int32_t glyph_a = TS->font_get_glyph_index(rids[i], size, p_char, 0);
+ Size2 ret = Size2(TS->font_get_glyph_advance(rids[i], size, glyph_a).x, TS->font_get_ascent(rids[i], size) + TS->font_get_descent(rids[i], size));
+ if ((p_next != 0) && data[i]->has_char(p_next)) {
+ int32_t glyph_b = TS->font_get_glyph_index(rids[i], size, p_next, 0);
+ ret.x -= TS->font_get_kerning(rids[i], size, Vector2i(glyph_a, glyph_b)).x;
+ }
+ return ret;
+ }
+ }
+ return Size2();
}
-////////////
+real_t Font::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate) const {
+ int size = (p_size <= 0) ? base_size : p_size;
+
+ for (int i = 0; i < data.size(); i++) {
+ _ensure_rid(i);
+ if (data[i]->has_char(p_char)) {
+ int32_t glyph_a = TS->font_get_glyph_index(rids[i], size, p_char, 0);
+ real_t ret = TS->font_get_glyph_advance(rids[i], size, glyph_a).x;
+ if ((p_next != 0) && data[i]->has_char(p_next)) {
+ int32_t glyph_b = TS->font_get_glyph_index(rids[i], size, p_next, 0);
+ ret -= TS->font_get_kerning(rids[i], size, Vector2i(glyph_a, glyph_b)).x;
+ }
-RES ResourceFormatLoaderBMFont::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
- if (r_error) {
- *r_error = ERR_FILE_CANT_OPEN;
+ if (p_outline_size > 0 && p_outline_modulate.a != 0.0f) {
+ TS->font_draw_glyph_outline(rids[i], p_canvas_item, size, p_outline_size, p_pos, glyph_a, p_outline_modulate);
+ }
+ TS->font_draw_glyph(rids[i], p_canvas_item, size, p_pos, glyph_a, p_modulate);
+ return ret;
+ }
}
+ return 0;
+}
- Ref<BitmapFont> font;
- font.instance();
-
- Error err = font->create_from_fnt(p_path);
+bool Font::has_char(char32_t p_char) const {
+ for (int i = 0; i < data.size(); i++) {
+ if (data[i]->has_char(p_char))
+ return true;
+ }
+ return false;
+}
- if (err) {
- if (r_error) {
- *r_error = err;
+String Font::get_supported_chars() const {
+ String chars;
+ for (int i = 0; i < data.size(); i++) {
+ String data_chars = data[i]->get_supported_chars();
+ for (int j = 0; j < data_chars.length(); j++) {
+ if (chars.find_char(data_chars[j]) == -1) {
+ chars += data_chars[j];
+ }
}
- return RES();
}
+ return chars;
+}
- return font;
+Vector<RID> Font::get_rids() const {
+ for (int i = 0; i < data.size(); i++) {
+ _ensure_rid(i);
+ }
+ return rids;
}
-void ResourceFormatLoaderBMFont::get_recognized_extensions(List<String> *p_extensions) const {
- p_extensions->push_back("fnt");
+void Font::update_changes() {
+ emit_changed();
}
-bool ResourceFormatLoaderBMFont::handles_type(const String &p_type) const {
- return (p_type == "BitmapFont");
+Font::Font() {
+ cache.set_capacity(128);
+ cache_wrap.set_capacity(32);
}
-String ResourceFormatLoaderBMFont::get_resource_type(const String &p_path) const {
- String el = p_path.get_extension().to_lower();
- if (el == "fnt") {
- return "BitmapFont";
- }
- return "";
+Font::~Font() {
+ clear_data();
+ cache.clear();
+ cache_wrap.clear();
}
diff --git a/scene/resources/font.h b/scene/resources/font.h
index c739520da3..9a34edce64 100644
--- a/scene/resources/font.h
+++ b/scene/resources/font.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,179 +31,261 @@
#ifndef FONT_H
#define FONT_H
-#include "core/map.h"
-#include "core/resource.h"
+#include "core/io/resource.h"
+#include "core/templates/lru.h"
+#include "core/templates/map.h"
#include "scene/resources/texture.h"
+#include "servers/text_server.h"
-class Font : public Resource {
- GDCLASS(Font, Resource);
+/*************************************************************************/
+
+class FontData : public Resource {
+ GDCLASS(FontData, Resource);
+ RES_BASE_EXTENSION("fontdata");
+
+ // Font source data.
+ const uint8_t *data_ptr = nullptr;
+ size_t data_size = 0;
+ PackedByteArray data;
+
+ bool antialiased = true;
+ bool msdf = false;
+ int msdf_pixel_range = 16;
+ int msdf_size = 48;
+ int fixed_size = 0;
+ bool force_autohinter = false;
+ TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
+ real_t oversampling = 0.f;
+
+ // Cache.
+ mutable Vector<RID> cache;
+
+ _FORCE_INLINE_ void _clear_cache();
+ _FORCE_INLINE_ void _ensure_rid(int p_cache_index) const;
protected:
static void _bind_methods();
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+ virtual void reset_state() override;
+
public:
- virtual float get_height() const = 0;
+ // Font source data.
+ virtual void set_data_ptr(const uint8_t *p_data, size_t p_size);
+ virtual void set_data(const PackedByteArray &p_data);
+ virtual PackedByteArray get_data() const;
- virtual float get_ascent() const = 0;
- virtual float get_descent() const = 0;
- virtual float get_underline_position() const = 0;
- virtual float get_underline_thickness() const = 0;
+ // Common properties.
+ virtual void set_antialiased(bool p_antialiased);
+ virtual bool is_antialiased() const;
- virtual Size2 get_char_size(char32_t p_char, char32_t p_next = 0) const = 0;
- Size2 get_string_size(const String &p_string) const;
- Size2 get_wordwrap_string_size(const String &p_string, float p_width) const;
+ virtual void set_multichannel_signed_distance_field(bool p_msdf);
+ virtual bool is_multichannel_signed_distance_field() const;
- virtual bool is_distance_field_hint() const = 0;
+ virtual void set_msdf_pixel_range(int p_msdf_pixel_range);
+ virtual int get_msdf_pixel_range() const;
- void draw(RID p_canvas_item, const Point2 &p_pos, const String &p_text, const Color &p_modulate = Color(1, 1, 1), int p_clip_w = -1, const Color &p_outline_modulate = Color(1, 1, 1)) const;
- void draw_halign(RID p_canvas_item, const Point2 &p_pos, HAlign p_align, float p_width, const String &p_text, const Color &p_modulate = Color(1, 1, 1), const Color &p_outline_modulate = Color(1, 1, 1)) const;
+ virtual void set_msdf_size(int p_msdf_size);
+ virtual int get_msdf_size() const;
- virtual bool has_outline() const { return false; }
- virtual float draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const = 0;
+ virtual void set_fixed_size(int p_fixed_size);
+ virtual int get_fixed_size() const;
- void update_changes();
- Font();
-};
+ virtual void set_force_autohinter(bool p_force_autohinter);
+ virtual bool is_force_autohinter() const;
-// Helper class to that draws outlines immediately and draws characters in its destructor.
-class FontDrawer {
- const Ref<Font> &font;
- Color outline_color;
- bool has_outline;
+ virtual void set_hinting(TextServer::Hinting p_hinting);
+ virtual TextServer::Hinting get_hinting() const;
- struct PendingDraw {
- RID canvas_item;
- Point2 pos;
- char32_t chr;
- char32_t next;
- Color modulate;
- };
+ virtual void set_oversampling(real_t p_oversampling);
+ virtual real_t get_oversampling() const;
- Vector<PendingDraw> pending_draws;
+ // Cache.
+ virtual RID find_cache(const Dictionary &p_variation_coordinates) const;
-public:
- FontDrawer(const Ref<Font> &p_font, const Color &p_outline_color) :
- font(p_font),
- outline_color(p_outline_color) {
- has_outline = p_font->has_outline();
- }
-
- float draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, const Color &p_modulate = Color(1, 1, 1)) {
- if (has_outline) {
- PendingDraw draw = { p_canvas_item, p_pos, p_char, p_next, p_modulate };
- pending_draws.push_back(draw);
- }
- return font->draw_char(p_canvas_item, p_pos, p_char, p_next, has_outline ? outline_color : p_modulate, has_outline);
- }
-
- ~FontDrawer() {
- for (int i = 0; i < pending_draws.size(); ++i) {
- const PendingDraw &draw = pending_draws[i];
- font->draw_char(draw.canvas_item, draw.pos, draw.chr, draw.next, draw.modulate, false);
- }
- }
+ virtual int get_cache_count() const;
+ virtual void clear_cache();
+ virtual void remove_cache(int p_cache_index);
+
+ virtual Array get_size_cache_list(int p_cache_index) const;
+ virtual void clear_size_cache(int p_cache_index);
+ virtual void remove_size_cache(int p_cache_index, const Vector2i &p_size);
+
+ virtual void set_variation_coordinates(int p_cache_index, const Dictionary &p_variation_coordinates);
+ virtual Dictionary get_variation_coordinates(int p_cache_index) const;
+
+ virtual void set_ascent(int p_cache_index, int p_size, real_t p_ascent);
+ virtual real_t get_ascent(int p_cache_index, int p_size) const;
+
+ virtual void set_descent(int p_cache_index, int p_size, real_t p_descent);
+ virtual real_t get_descent(int p_cache_index, int p_size) const;
+
+ virtual void set_underline_position(int p_cache_index, int p_size, real_t p_underline_position);
+ virtual real_t get_underline_position(int p_cache_index, int p_size) const;
+
+ virtual void set_underline_thickness(int p_cache_index, int p_size, real_t p_underline_thickness);
+ virtual real_t get_underline_thickness(int p_cache_index, int p_size) const;
+
+ virtual void set_scale(int p_cache_index, int p_size, real_t p_scale); // Rendering scale for bitmap fonts (e.g. emoji fonts).
+ virtual real_t get_scale(int p_cache_index, int p_size) const;
+
+ virtual void set_spacing(int p_cache_index, int p_size, TextServer::SpacingType p_spacing, int p_value);
+ virtual int get_spacing(int p_cache_index, int p_size, TextServer::SpacingType p_spacing) const;
+
+ virtual int get_texture_count(int p_cache_index, const Vector2i &p_size) const;
+ virtual void clear_textures(int p_cache_index, const Vector2i &p_size);
+ virtual void remove_texture(int p_cache_index, const Vector2i &p_size, int p_texture_index);
+
+ virtual void set_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image);
+ virtual Ref<Image> get_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index) const;
+
+ virtual void set_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset);
+ virtual PackedInt32Array get_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index) const;
+
+ virtual Array get_glyph_list(int p_cache_index, const Vector2i &p_size) const;
+ virtual void clear_glyphs(int p_cache_index, const Vector2i &p_size);
+ virtual void remove_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_glyph);
+
+ virtual void set_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph, const Vector2 &p_advance);
+ virtual Vector2 get_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph) const;
+
+ virtual void set_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset);
+ virtual Vector2 get_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const;
+
+ virtual void set_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size);
+ virtual Vector2 get_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const;
+
+ virtual void set_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect);
+ virtual Rect2 get_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const;
+
+ virtual void set_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx);
+ virtual int get_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const;
+
+ virtual Array get_kerning_list(int p_cache_index, int p_size) const;
+ virtual void clear_kerning_map(int p_cache_index, int p_size);
+ virtual void remove_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair);
+
+ virtual void set_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning);
+ virtual Vector2 get_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair) const;
+
+ virtual void render_range(int p_cache_index, const Vector2i &p_size, char32_t p_start, char32_t p_end);
+ virtual void render_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_index);
+
+ virtual RID get_cache_rid(int p_cache_index) const;
+
+ // Language/script support override.
+ virtual bool is_language_supported(const String &p_language) const;
+ virtual void set_language_support_override(const String &p_language, bool p_supported);
+ virtual bool get_language_support_override(const String &p_language) const;
+ virtual void remove_language_support_override(const String &p_language);
+ virtual Vector<String> get_language_support_overrides() const;
+
+ virtual bool is_script_supported(const String &p_script) const;
+ virtual void set_script_support_override(const String &p_script, bool p_supported);
+ virtual bool get_script_support_override(const String &p_script) const;
+ virtual void remove_script_support_override(const String &p_script);
+ virtual Vector<String> get_script_support_overrides() const;
+
+ // Base font properties.
+ virtual bool has_char(char32_t p_char) const;
+ virtual String get_supported_chars() const;
+
+ virtual int32_t get_glyph_index(int p_size, char32_t p_char, char32_t p_variation_selector = 0x0000) const;
+
+ virtual Dictionary get_supported_feature_list() const;
+ virtual Dictionary get_supported_variation_list() const;
+
+ FontData();
+ ~FontData();
};
-class BitmapFont : public Font {
- GDCLASS(BitmapFont, Font);
- RES_BASE_EXTENSION("font");
+/*************************************************************************/
+
+class TextLine;
+class TextParagraph;
- Vector<Ref<Texture2D>> textures;
+class Font : public Resource {
+ GDCLASS(Font, Resource);
-public:
- struct Character {
- int texture_idx;
- Rect2 rect;
- float v_align;
- float h_align;
- float advance;
-
- Character() {
- texture_idx = 0;
- v_align = 0;
- }
- };
-
- struct KerningPairKey {
- union {
- struct {
- uint32_t A, B;
- };
-
- uint64_t pair;
- };
-
- _FORCE_INLINE_ bool operator<(const KerningPairKey &p_r) const { return pair < p_r.pair; }
- };
-
-private:
- HashMap<char32_t, Character> char_map;
- Map<KerningPairKey, int> kerning_map;
-
- float height;
- float ascent;
- bool distance_field_hint;
-
- void _set_chars(const Vector<int> &p_chars);
- Vector<int> _get_chars() const;
- void _set_kernings(const Vector<int> &p_kernings);
- Vector<int> _get_kernings() const;
- void _set_textures(const Vector<Variant> &p_textures);
- Vector<Variant> _get_textures() const;
-
- Ref<BitmapFont> fallback;
+ // Shaped string cache.
+ mutable LRUCache<uint64_t, Ref<TextLine>> cache;
+ mutable LRUCache<uint64_t, Ref<TextParagraph>> cache_wrap;
+
+ // Font data cache.
+ Vector<Ref<FontData>> data;
+ mutable Vector<RID> rids;
+
+ // Font config.
+ int base_size = 16;
+ Dictionary variation_coordinates;
+ int spacing_bottom = 0;
+ int spacing_top = 0;
+
+ _FORCE_INLINE_ void _data_changed();
+ _FORCE_INLINE_ void _ensure_rid(int p_index) const; // Find or create cache record.
protected:
static void _bind_methods();
-public:
- Error create_from_fnt(const String &p_file);
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
- void set_height(float p_height);
- float get_height() const override;
+ virtual void reset_state() override;
- void set_ascent(float p_ascent);
- float get_ascent() const override;
- float get_descent() const override;
- float get_underline_position() const override;
- float get_underline_thickness() const override;
+public:
+ Dictionary get_feature_list() const;
- void add_texture(const Ref<Texture2D> &p_texture);
- void add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance = -1);
+ // Font data.
+ virtual void add_data(const Ref<FontData> &p_data);
+ virtual void set_data(int p_idx, const Ref<FontData> &p_data);
+ virtual int get_data_count() const;
+ virtual Ref<FontData> get_data(int p_idx) const;
+ virtual RID get_data_rid(int p_idx) const;
+ virtual void clear_data();
+ virtual void remove_data(int p_idx);
- int get_character_count() const;
- Vector<char32_t> get_char_keys() const;
- Character get_character(char32_t p_char) const;
+ // Font configuration.
+ virtual void set_base_size(int p_size);
+ virtual int get_base_size() const;
- int get_texture_count() const;
- Ref<Texture2D> get_texture(int p_idx) const;
+ virtual void set_variation_coordinates(const Dictionary &p_variation_coordinates);
+ virtual Dictionary get_variation_coordinates() const;
- void add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning);
- int get_kerning_pair(char32_t p_A, char32_t p_B) const;
- Vector<KerningPairKey> get_kerning_pair_keys() const;
+ virtual void set_spacing(TextServer::SpacingType p_spacing, int p_value);
+ virtual int get_spacing(TextServer::SpacingType p_spacing) const;
- Size2 get_char_size(char32_t p_char, char32_t p_next = 0) const override;
+ // Font metrics.
+ virtual real_t get_height(int p_size = -1) const;
+ virtual real_t get_ascent(int p_size = -1) const;
+ virtual real_t get_descent(int p_size = -1) const;
+ virtual real_t get_underline_position(int p_size = -1) const;
+ virtual real_t get_underline_thickness(int p_size = -1) const;
- void set_fallback(const Ref<BitmapFont> &p_fallback);
- Ref<BitmapFont> get_fallback() const;
+ // Drawing string.
+ virtual Size2 get_string_size(const String &p_text, int p_size = -1, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, uint8_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
+ virtual Size2 get_multiline_string_size(const String &p_text, real_t p_width = -1, int p_size = -1, uint8_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND) const;
- void clear();
+ virtual void draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
+ virtual void draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_max_lines = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
- void set_distance_field_hint(bool p_distance_field);
- bool is_distance_field_hint() const override;
+ // Helper functions.
+ virtual bool has_char(char32_t p_char) const;
+ virtual String get_supported_chars() const;
- float draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const override;
+ // Drawing char.
+ virtual Size2 get_char_size(char32_t p_char, char32_t p_next = 0, int p_size = -1) const;
+ virtual real_t draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0)) const;
- BitmapFont();
- ~BitmapFont();
-};
+ Vector<RID> get_rids() const;
-class ResourceFormatLoaderBMFont : public ResourceFormatLoader {
-public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
- virtual void get_recognized_extensions(List<String> *p_extensions) const;
- virtual bool handles_type(const String &p_type) const;
- virtual String get_resource_type(const String &p_path) const;
+ void update_changes();
+
+ Font();
+ ~Font();
};
-#endif
+#endif /* FONT_H */
diff --git a/scene/resources/gradient.cpp b/scene/resources/gradient.cpp
index b720653f91..7b9b942142 100644
--- a/scene/resources/gradient.cpp
+++ b/scene/resources/gradient.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,20 +32,13 @@
#include "core/core_string_names.h"
-//setter and getter names for property serialization
-#define COLOR_RAMP_GET_OFFSETS "get_offsets"
-#define COLOR_RAMP_GET_COLORS "get_colors"
-#define COLOR_RAMP_SET_OFFSETS "set_offsets"
-#define COLOR_RAMP_SET_COLORS "set_colors"
-
Gradient::Gradient() {
- //Set initial color ramp transition from black to white
+ //Set initial gradient transition from black to white
points.resize(2);
points.write[0].color = Color(0, 0, 0, 1);
points.write[0].offset = 0;
points.write[1].color = Color(1, 1, 1, 1);
points.write[1].offset = 1;
- is_sorted = true;
}
Gradient::~Gradient() {
@@ -53,7 +46,7 @@ Gradient::~Gradient() {
void Gradient::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_point", "offset", "color"), &Gradient::add_point);
- ClassDB::bind_method(D_METHOD("remove_point", "offset"), &Gradient::remove_point);
+ ClassDB::bind_method(D_METHOD("remove_point", "point"), &Gradient::remove_point);
ClassDB::bind_method(D_METHOD("set_offset", "point", "offset"), &Gradient::set_offset);
ClassDB::bind_method(D_METHOD("get_offset", "point"), &Gradient::get_offset);
@@ -65,14 +58,14 @@ void Gradient::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_point_count"), &Gradient::get_points_count);
- ClassDB::bind_method(D_METHOD(COLOR_RAMP_SET_OFFSETS, "offsets"), &Gradient::set_offsets);
- ClassDB::bind_method(D_METHOD(COLOR_RAMP_GET_OFFSETS), &Gradient::get_offsets);
+ ClassDB::bind_method(D_METHOD("set_offsets", "offsets"), &Gradient::set_offsets);
+ ClassDB::bind_method(D_METHOD("get_offsets"), &Gradient::get_offsets);
- ClassDB::bind_method(D_METHOD(COLOR_RAMP_SET_COLORS, "colors"), &Gradient::set_colors);
- ClassDB::bind_method(D_METHOD(COLOR_RAMP_GET_COLORS), &Gradient::get_colors);
+ ClassDB::bind_method(D_METHOD("set_colors", "colors"), &Gradient::set_colors);
+ ClassDB::bind_method(D_METHOD("get_colors"), &Gradient::get_colors);
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT32_ARRAY, "offsets"), COLOR_RAMP_SET_OFFSETS, COLOR_RAMP_GET_OFFSETS);
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "colors"), COLOR_RAMP_SET_COLORS, COLOR_RAMP_GET_COLORS);
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT32_ARRAY, "offsets"), "set_offsets", "get_offsets");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "colors"), "set_colors", "get_colors");
}
Vector<float> Gradient::get_offsets() const {
diff --git a/scene/resources/gradient.h b/scene/resources/gradient.h
index 6518b13ee8..cf5b179c45 100644
--- a/scene/resources/gradient.h
+++ b/scene/resources/gradient.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,7 +31,7 @@
#ifndef GRADIENT_H
#define GRADIENT_H
-#include "core/resource.h"
+#include "core/io/resource.h"
class Gradient : public Resource {
GDCLASS(Gradient, Resource);
@@ -39,7 +39,7 @@ class Gradient : public Resource {
public:
struct Point {
- float offset;
+ float offset = 0.0;
Color color;
bool operator<(const Point &p_ponit) const {
return offset < p_ponit.offset;
@@ -48,7 +48,7 @@ public:
private:
Vector<Point> points;
- bool is_sorted;
+ bool is_sorted = true;
_FORCE_INLINE_ void _update_sorting() {
if (!is_sorted) {
points.sort();
@@ -82,7 +82,7 @@ public:
Vector<Color> get_colors() const;
_FORCE_INLINE_ Color get_color_at_offset(float p_offset) {
- if (points.empty()) {
+ if (points.is_empty()) {
return Color(0, 0, 0, 1);
}
diff --git a/scene/resources/height_map_shape_3d.cpp b/scene/resources/height_map_shape_3d.cpp
index 2ae47bcf3c..d1a958ad38 100644
--- a/scene/resources/height_map_shape_3d.cpp
+++ b/scene/resources/height_map_shape_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -44,7 +44,7 @@ Vector<Vector3> HeightMapShape3D::get_debug_mesh_lines() const {
const real_t *r = map_data.ptr();
// reserve some memory for our points..
- points.resize(((map_width - 1) * map_depth * 2) + (map_width * (map_depth - 1) * 2));
+ points.resize(((map_width - 1) * map_depth * 2) + (map_width * (map_depth - 1) * 2) + ((map_width - 1) * (map_depth - 1) * 2));
// now set our points
int r_offset = 0;
@@ -65,6 +65,11 @@ Vector<Vector3> HeightMapShape3D::get_debug_mesh_lines() const {
points.write[w_offset++] = Vector3(height.x, r[r_offset + map_width - 1], height.z + 1.0);
}
+ if ((w != map_width - 1) && (d != map_depth - 1)) {
+ points.write[w_offset++] = Vector3(height.x + 1.0, r[r_offset], height.z);
+ points.write[w_offset++] = Vector3(height.x, r[r_offset + map_width - 1], height.z + 1.0);
+ }
+
height.x += 1.0;
}
@@ -107,8 +112,6 @@ void HeightMapShape3D::set_map_width(int p_new) {
_update_shape();
notify_change_to_owners();
- _change_notify("map_width");
- _change_notify("map_data");
}
}
@@ -133,8 +136,6 @@ void HeightMapShape3D::set_map_depth(int p_new) {
_update_shape();
notify_change_to_owners();
- _change_notify("map_depth");
- _change_notify("map_data");
}
}
@@ -142,7 +143,7 @@ int HeightMapShape3D::get_map_depth() const {
return map_depth;
}
-void HeightMapShape3D::set_map_data(PackedFloat32Array p_new) {
+void HeightMapShape3D::set_map_data(Vector<real_t> p_new) {
int size = (map_width * map_depth);
if (p_new.size() != size) {
// fail
@@ -153,7 +154,7 @@ void HeightMapShape3D::set_map_data(PackedFloat32Array p_new) {
real_t *w = map_data.ptrw();
const real_t *r = p_new.ptr();
for (int i = 0; i < size; i++) {
- float val = r[i];
+ real_t val = r[i];
w[i] = val;
if (i == 0) {
min_height = val;
@@ -171,10 +172,9 @@ void HeightMapShape3D::set_map_data(PackedFloat32Array p_new) {
_update_shape();
notify_change_to_owners();
- _change_notify("map_data");
}
-PackedFloat32Array HeightMapShape3D::get_map_data() const {
+Vector<real_t> HeightMapShape3D::get_map_data() const {
return map_data;
}
@@ -193,16 +193,12 @@ void HeightMapShape3D::_bind_methods() {
HeightMapShape3D::HeightMapShape3D() :
Shape3D(PhysicsServer3D::get_singleton()->shape_create(PhysicsServer3D::SHAPE_HEIGHTMAP)) {
- map_width = 2;
- map_depth = 2;
map_data.resize(map_width * map_depth);
real_t *w = map_data.ptrw();
w[0] = 0.0;
w[1] = 0.0;
w[2] = 0.0;
w[3] = 0.0;
- min_height = 0.0;
- max_height = 0.0;
_update_shape();
}
diff --git a/scene/resources/height_map_shape_3d.h b/scene/resources/height_map_shape_3d.h
index 9ee8b49689..1273aee040 100644
--- a/scene/resources/height_map_shape_3d.h
+++ b/scene/resources/height_map_shape_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -36,11 +36,11 @@
class HeightMapShape3D : public Shape3D {
GDCLASS(HeightMapShape3D, Shape3D);
- int map_width;
- int map_depth;
- PackedFloat32Array map_data;
- float min_height;
- float max_height;
+ int map_width = 2;
+ int map_depth = 2;
+ Vector<real_t> map_data;
+ real_t min_height = 0.0;
+ real_t max_height = 0.0;
protected:
static void _bind_methods();
@@ -51,8 +51,8 @@ public:
int get_map_width() const;
void set_map_depth(int p_new);
int get_map_depth() const;
- void set_map_data(PackedFloat32Array p_new);
- PackedFloat32Array get_map_data() const;
+ void set_map_data(Vector<real_t> p_new);
+ Vector<real_t> get_map_data() const;
virtual Vector<Vector3> get_debug_mesh_lines() const override;
virtual real_t get_enclosing_radius() const override;
diff --git a/scene/resources/immediate_mesh.cpp b/scene/resources/immediate_mesh.cpp
new file mode 100644
index 0000000000..05d1a7bf94
--- /dev/null
+++ b/scene/resources/immediate_mesh.cpp
@@ -0,0 +1,416 @@
+/*************************************************************************/
+/* immediate_mesh.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 "immediate_mesh.h"
+
+void ImmediateMesh::surface_begin(PrimitiveType p_primitive, const Ref<Material> &p_material) {
+ ERR_FAIL_COND_MSG(surface_active, "Already creating a new surface.");
+ active_surface_data.primitive = p_primitive;
+ active_surface_data.material = p_material;
+ surface_active = true;
+}
+void ImmediateMesh::surface_set_color(const Color &p_color) {
+ ERR_FAIL_COND_MSG(!surface_active, "Not creating any surface. Use surface_begin() to do it.");
+
+ if (!uses_colors) {
+ colors.resize(vertices.size());
+ for (uint32_t i = 0; i < colors.size(); i++) {
+ colors[i] = p_color;
+ }
+ uses_colors = true;
+ }
+
+ current_color = p_color;
+}
+void ImmediateMesh::surface_set_normal(const Vector3 &p_normal) {
+ ERR_FAIL_COND_MSG(!surface_active, "Not creating any surface. Use surface_begin() to do it.");
+
+ if (!uses_normals) {
+ normals.resize(vertices.size());
+ for (uint32_t i = 0; i < normals.size(); i++) {
+ normals[i] = p_normal;
+ }
+ uses_normals = true;
+ }
+
+ current_normal = p_normal;
+}
+void ImmediateMesh::surface_set_tangent(const Plane &p_tangent) {
+ ERR_FAIL_COND_MSG(!surface_active, "Not creating any surface. Use surface_begin() to do it.");
+ if (!uses_tangents) {
+ tangents.resize(vertices.size());
+ for (uint32_t i = 0; i < tangents.size(); i++) {
+ tangents[i] = p_tangent;
+ }
+ uses_tangents = true;
+ }
+
+ current_tangent = p_tangent;
+}
+void ImmediateMesh::surface_set_uv(const Vector2 &p_uv) {
+ ERR_FAIL_COND_MSG(!surface_active, "Not creating any surface. Use surface_begin() to do it.");
+ if (!uses_uvs) {
+ uvs.resize(vertices.size());
+ for (uint32_t i = 0; i < uvs.size(); i++) {
+ uvs[i] = p_uv;
+ }
+ uses_uvs = true;
+ }
+
+ current_uv = p_uv;
+}
+void ImmediateMesh::surface_set_uv2(const Vector2 &p_uv2) {
+ ERR_FAIL_COND_MSG(!surface_active, "Not creating any surface. Use surface_begin() to do it.");
+ if (!uses_uv2s) {
+ uv2s.resize(vertices.size());
+ for (uint32_t i = 0; i < uv2s.size(); i++) {
+ uv2s[i] = p_uv2;
+ }
+ uses_uv2s = true;
+ }
+
+ current_uv2 = p_uv2;
+}
+void ImmediateMesh::surface_add_vertex(const Vector3 &p_vertex) {
+ ERR_FAIL_COND_MSG(!surface_active, "Not creating any surface. Use surface_begin() to do it.");
+ ERR_FAIL_COND_MSG(vertices.size() && active_surface_data.vertex_2d, "Can't mix 2D and 3D vertices in a surface.");
+
+ if (uses_colors) {
+ colors.push_back(current_color);
+ }
+ if (uses_normals) {
+ normals.push_back(current_normal);
+ }
+ if (uses_tangents) {
+ tangents.push_back(current_tangent);
+ }
+ if (uses_uvs) {
+ uvs.push_back(current_uv);
+ }
+ if (uses_uv2s) {
+ uv2s.push_back(current_uv2);
+ }
+ vertices.push_back(p_vertex);
+}
+
+void ImmediateMesh::surface_add_vertex_2d(const Vector2 &p_vertex) {
+ ERR_FAIL_COND_MSG(!surface_active, "Not creating any surface. Use surface_begin() to do it.");
+ ERR_FAIL_COND_MSG(vertices.size() && !active_surface_data.vertex_2d, "Can't mix 2D and 3D vertices in a surface.");
+
+ if (uses_colors) {
+ colors.push_back(current_color);
+ }
+ if (uses_normals) {
+ normals.push_back(current_normal);
+ }
+ if (uses_tangents) {
+ tangents.push_back(current_tangent);
+ }
+ if (uses_uvs) {
+ uvs.push_back(current_uv);
+ }
+ if (uses_uv2s) {
+ uv2s.push_back(current_uv2);
+ }
+ Vector3 v(p_vertex.x, p_vertex.y, 0);
+ vertices.push_back(v);
+
+ active_surface_data.vertex_2d = true;
+}
+void ImmediateMesh::surface_end() {
+ ERR_FAIL_COND_MSG(!surface_active, "Not creating any surface. Use surface_begin() to do it.");
+ ERR_FAIL_COND_MSG(!vertices.size(), "No vertices were added, surface can't be created.");
+
+ uint32_t format = ARRAY_FORMAT_VERTEX;
+
+ uint32_t vertex_stride = 0;
+ if (active_surface_data.vertex_2d) {
+ format |= ARRAY_FLAG_USE_2D_VERTICES;
+ vertex_stride = sizeof(float) * 2;
+ } else {
+ vertex_stride = sizeof(float) * 3;
+ }
+
+ uint32_t normal_offset = 0;
+ if (uses_normals) {
+ format |= ARRAY_FORMAT_NORMAL;
+ normal_offset = vertex_stride;
+ vertex_stride += sizeof(uint32_t);
+ }
+ uint32_t tangent_offset = 0;
+ if (uses_tangents) {
+ format |= ARRAY_FORMAT_TANGENT;
+ tangent_offset += vertex_stride;
+ vertex_stride += sizeof(uint32_t);
+ }
+
+ AABB aabb;
+
+ {
+ surface_vertex_create_cache.resize(vertex_stride * vertices.size());
+ uint8_t *surface_vertex_ptr = surface_vertex_create_cache.ptrw();
+ for (uint32_t i = 0; i < vertices.size(); i++) {
+ {
+ float *vtx = (float *)&surface_vertex_ptr[i * vertex_stride];
+ vtx[0] = vertices[i].x;
+ vtx[1] = vertices[i].y;
+ if (!active_surface_data.vertex_2d) {
+ vtx[2] = vertices[i].z;
+ }
+ if (i == 0) {
+ aabb.position = vertices[i];
+ } else {
+ aabb.expand_to(vertices[i]);
+ }
+ }
+ if (uses_normals) {
+ uint32_t *normal = (uint32_t *)&surface_vertex_ptr[i * vertex_stride + normal_offset];
+
+ Vector3 n = normals[i] * Vector3(0.5, 0.5, 0.5) + Vector3(0.5, 0.5, 0.5);
+
+ uint32_t value = 0;
+ value |= CLAMP(int(n.x * 1023.0), 0, 1023);
+ value |= CLAMP(int(n.y * 1023.0), 0, 1023) << 10;
+ value |= CLAMP(int(n.z * 1023.0), 0, 1023) << 20;
+
+ *normal = value;
+ }
+ if (uses_tangents) {
+ uint32_t *tangent = (uint32_t *)&surface_vertex_ptr[i * vertex_stride + tangent_offset];
+ Plane t = tangents[i];
+ uint32_t value = 0;
+ value |= CLAMP(int((t.normal.x * 0.5 + 0.5) * 1023.0), 0, 1023);
+ value |= CLAMP(int((t.normal.y * 0.5 + 0.5) * 1023.0), 0, 1023) << 10;
+ value |= CLAMP(int((t.normal.z * 0.5 + 0.5) * 1023.0), 0, 1023) << 20;
+ if (t.d > 0) {
+ value |= 3 << 30;
+ }
+
+ *tangent = value;
+ }
+ }
+ }
+
+ if (uses_colors || uses_uvs || uses_uv2s) {
+ uint32_t attribute_stride = 0;
+
+ if (uses_colors) {
+ format |= ARRAY_FORMAT_COLOR;
+ attribute_stride += sizeof(uint8_t) * 4;
+ }
+ uint32_t uv_offset = 0;
+ if (uses_uvs) {
+ format |= ARRAY_FORMAT_TEX_UV;
+ uv_offset = attribute_stride;
+ attribute_stride += sizeof(float) * 2;
+ }
+ uint32_t uv2_offset = 0;
+ if (uses_uv2s) {
+ format |= ARRAY_FORMAT_TEX_UV2;
+ uv2_offset = attribute_stride;
+ attribute_stride += sizeof(float) * 2;
+ }
+
+ surface_attribute_create_cache.resize(vertices.size() * attribute_stride);
+
+ uint8_t *surface_attribute_ptr = surface_attribute_create_cache.ptrw();
+
+ for (uint32_t i = 0; i < vertices.size(); i++) {
+ if (uses_colors) {
+ uint8_t *color8 = (uint8_t *)&surface_attribute_ptr[i * attribute_stride];
+
+ color8[0] = uint8_t(CLAMP(colors[i].r * 255.0, 0.0, 255.0));
+ color8[1] = uint8_t(CLAMP(colors[i].g * 255.0, 0.0, 255.0));
+ color8[2] = uint8_t(CLAMP(colors[i].b * 255.0, 0.0, 255.0));
+ color8[3] = uint8_t(CLAMP(colors[i].a * 255.0, 0.0, 255.0));
+ }
+ if (uses_uvs) {
+ float *uv = (float *)&surface_attribute_ptr[i * attribute_stride + uv_offset];
+
+ uv[0] = uvs[i].x;
+ uv[1] = uvs[i].y;
+ }
+
+ if (uses_uv2s) {
+ float *uv2 = (float *)&surface_attribute_ptr[i * attribute_stride + uv2_offset];
+
+ uv2[0] = uv2s[i].x;
+ uv2[1] = uv2s[i].y;
+ }
+ }
+ }
+
+ RS::SurfaceData sd;
+
+ sd.primitive = RS::PrimitiveType(active_surface_data.primitive);
+ sd.format = format;
+ sd.vertex_data = surface_vertex_create_cache;
+ if (uses_colors || uses_uvs || uses_uv2s) {
+ sd.attribute_data = surface_attribute_create_cache;
+ }
+ sd.vertex_count = vertices.size();
+ sd.aabb = aabb;
+ if (active_surface_data.material.is_valid()) {
+ sd.material = active_surface_data.material->get_rid();
+ }
+
+ RS::get_singleton()->mesh_add_surface(mesh, sd);
+
+ active_surface_data.aabb = aabb;
+
+ active_surface_data.format = format;
+ active_surface_data.array_len = vertices.size();
+
+ surfaces.push_back(active_surface_data);
+
+ colors.clear();
+ normals.clear();
+ tangents.clear();
+ uvs.clear();
+ uv2s.clear();
+ vertices.clear();
+
+ uses_colors = false;
+ uses_normals = false;
+ uses_tangents = false;
+ uses_uvs = false;
+ uses_uv2s = false;
+
+ surface_active = false;
+}
+
+void ImmediateMesh::clear_surfaces() {
+ RS::get_singleton()->mesh_clear(mesh);
+ surfaces.clear();
+ surface_active = false;
+
+ colors.clear();
+ normals.clear();
+ tangents.clear();
+ uvs.clear();
+ uv2s.clear();
+ vertices.clear();
+
+ uses_colors = false;
+ uses_normals = false;
+ uses_tangents = false;
+ uses_uvs = false;
+ uses_uv2s = false;
+}
+
+int ImmediateMesh::get_surface_count() const {
+ return surfaces.size();
+}
+int ImmediateMesh::surface_get_array_len(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx, int(surfaces.size()), -1);
+ return surfaces[p_idx].array_len;
+}
+int ImmediateMesh::surface_get_array_index_len(int p_idx) const {
+ return 0;
+}
+bool ImmediateMesh::surface_is_softbody_friendly(int p_idx) const {
+ return false;
+}
+Array ImmediateMesh::surface_get_arrays(int p_surface) const {
+ ERR_FAIL_INDEX_V(p_surface, int(surfaces.size()), Array());
+ return RS::get_singleton()->mesh_surface_get_arrays(mesh, p_surface);
+}
+Array ImmediateMesh::surface_get_blend_shape_arrays(int p_surface) const {
+ return Array();
+}
+Dictionary ImmediateMesh::surface_get_lods(int p_surface) const {
+ return Dictionary();
+}
+uint32_t ImmediateMesh::surface_get_format(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx, int(surfaces.size()), 0);
+ return surfaces[p_idx].format;
+}
+Mesh::PrimitiveType ImmediateMesh::surface_get_primitive_type(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx, int(surfaces.size()), PRIMITIVE_MAX);
+ return surfaces[p_idx].primitive;
+}
+void ImmediateMesh::surface_set_material(int p_idx, const Ref<Material> &p_material) {
+ ERR_FAIL_INDEX(p_idx, int(surfaces.size()));
+ surfaces[p_idx].material = p_material;
+ RID mat;
+ if (p_material.is_valid()) {
+ mat = p_material->get_rid();
+ }
+ RS::get_singleton()->mesh_surface_set_material(mesh, p_idx, mat);
+}
+Ref<Material> ImmediateMesh::surface_get_material(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx, int(surfaces.size()), Ref<Material>());
+ return surfaces[p_idx].material;
+}
+int ImmediateMesh::get_blend_shape_count() const {
+ return 0;
+}
+StringName ImmediateMesh::get_blend_shape_name(int p_index) const {
+ return StringName();
+}
+void ImmediateMesh::set_blend_shape_name(int p_index, const StringName &p_name) {
+}
+
+AABB ImmediateMesh::get_aabb() const {
+ AABB aabb;
+ for (uint32_t i = 0; i < surfaces.size(); i++) {
+ if (i == 0) {
+ aabb = surfaces[i].aabb;
+ } else {
+ aabb.merge(surfaces[i].aabb);
+ }
+ }
+ return aabb;
+}
+
+void ImmediateMesh::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("surface_begin", "primitive", "material"), &ImmediateMesh::surface_begin, DEFVAL(Ref<Material>()));
+ ClassDB::bind_method(D_METHOD("surface_set_color", "color"), &ImmediateMesh::surface_set_color);
+ ClassDB::bind_method(D_METHOD("surface_set_normal", "normal"), &ImmediateMesh::surface_set_normal);
+ ClassDB::bind_method(D_METHOD("surface_set_tangent", "tangent"), &ImmediateMesh::surface_set_tangent);
+ ClassDB::bind_method(D_METHOD("surface_set_uv", "uv"), &ImmediateMesh::surface_set_uv);
+ ClassDB::bind_method(D_METHOD("surface_set_uv2", "uv2"), &ImmediateMesh::surface_set_uv2);
+ ClassDB::bind_method(D_METHOD("surface_add_vertex", "vertex"), &ImmediateMesh::surface_add_vertex);
+ ClassDB::bind_method(D_METHOD("surface_add_vertex_2d", "vertex"), &ImmediateMesh::surface_add_vertex_2d);
+ ClassDB::bind_method(D_METHOD("surface_end"), &ImmediateMesh::surface_end);
+
+ ClassDB::bind_method(D_METHOD("clear_surfaces"), &ImmediateMesh::clear_surfaces);
+}
+
+RID ImmediateMesh::get_rid() const {
+ return mesh;
+}
+
+ImmediateMesh::ImmediateMesh() {
+ mesh = RS::get_singleton()->mesh_create();
+}
+ImmediateMesh::~ImmediateMesh() {
+ RS::get_singleton()->free(mesh);
+}
diff --git a/scene/resources/immediate_mesh.h b/scene/resources/immediate_mesh.h
new file mode 100644
index 0000000000..7010d40719
--- /dev/null
+++ b/scene/resources/immediate_mesh.h
@@ -0,0 +1,117 @@
+/*************************************************************************/
+/* immediate_mesh.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 IMMEDIATE_MESH_H
+#define IMMEDIATE_MESH_H
+
+#include "core/templates/local_vector.h"
+#include "scene/resources/mesh.h"
+
+class ImmediateMesh : public Mesh {
+ GDCLASS(ImmediateMesh, Mesh)
+
+ RID mesh;
+
+ bool uses_colors = false;
+ bool uses_normals = false;
+ bool uses_tangents = false;
+ bool uses_uvs = false;
+ bool uses_uv2s = false;
+
+ Color current_color;
+ Vector3 current_normal;
+ Plane current_tangent;
+ Vector2 current_uv;
+ Vector2 current_uv2;
+
+ LocalVector<Color> colors;
+ LocalVector<Vector3> normals;
+ LocalVector<Plane> tangents;
+ LocalVector<Vector2> uvs;
+ LocalVector<Vector2> uv2s;
+ LocalVector<Vector3> vertices;
+
+ struct Surface {
+ PrimitiveType primitive;
+ Ref<Material> material;
+ bool vertex_2d = false;
+ int array_len = 0;
+ uint32_t format = 0;
+ AABB aabb;
+ };
+
+ LocalVector<Surface> surfaces;
+
+ bool surface_active = false;
+ Surface active_surface_data;
+
+ Vector<uint8_t> surface_vertex_create_cache;
+ Vector<uint8_t> surface_attribute_create_cache;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void surface_begin(PrimitiveType p_primitive, const Ref<Material> &p_material = Ref<Material>());
+ void surface_set_color(const Color &p_color);
+ void surface_set_normal(const Vector3 &p_normal);
+ void surface_set_tangent(const Plane &p_tangent);
+ void surface_set_uv(const Vector2 &p_uv);
+ void surface_set_uv2(const Vector2 &p_uv2);
+ void surface_add_vertex(const Vector3 &p_vertex);
+ void surface_add_vertex_2d(const Vector2 &p_vertex);
+ void surface_end();
+
+ void clear_surfaces();
+
+ virtual int get_surface_count() const override;
+ virtual int surface_get_array_len(int p_idx) const override;
+ virtual int surface_get_array_index_len(int p_idx) const override;
+ virtual bool surface_is_softbody_friendly(int p_idx) const override;
+ virtual Array surface_get_arrays(int p_surface) const override;
+ virtual Array surface_get_blend_shape_arrays(int p_surface) const override;
+ virtual Dictionary surface_get_lods(int p_surface) const override;
+ virtual uint32_t surface_get_format(int p_idx) const override;
+ virtual PrimitiveType surface_get_primitive_type(int p_idx) const override;
+ virtual void surface_set_material(int p_idx, const Ref<Material> &p_material) override;
+ virtual Ref<Material> surface_get_material(int p_idx) const override;
+ virtual int get_blend_shape_count() const override;
+ virtual StringName get_blend_shape_name(int p_index) const override;
+ virtual void set_blend_shape_name(int p_index, const StringName &p_name) override;
+
+ virtual AABB get_aabb() const override;
+
+ virtual RID get_rid() const override;
+
+ ImmediateMesh();
+ ~ImmediateMesh();
+};
+
+#endif // IMMEDIATEMESH_H
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 1e95a35726..1d2a2ef26c 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,12 +30,14 @@
#include "material.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
+#include "core/version.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_settings.h"
#endif
+#include "scene/main/scene_tree.h"
#include "scene/scene_string_names.h"
void Material::set_next_pass(const Ref<Material> &p_pass) {
@@ -76,7 +78,15 @@ RID Material::get_rid() const {
void Material::_validate_property(PropertyInfo &property) const {
if (!_can_do_next_pass() && property.name == "next_pass") {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
+ }
+}
+
+void Material::inspect_native_shader_code() {
+ SceneTree *st = Object::cast_to<SceneTree>(OS::get_singleton()->get_main_loop());
+ RID shader = get_shader_rid();
+ if (st && shader.is_valid()) {
+ st->call_group("_native_shader_source_visualizer", "_inspect_shader", shader);
}
}
@@ -87,6 +97,9 @@ void Material::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_render_priority", "priority"), &Material::set_render_priority);
ClassDB::bind_method(D_METHOD("get_render_priority"), &Material::get_render_priority);
+ ClassDB::bind_method(D_METHOD("inspect_native_shader_code"), &Material::inspect_native_shader_code);
+ ClassDB::set_method_flags(get_class_static(), _scs_create("inspect_native_shader_code"), METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR);
+
ADD_PROPERTY(PropertyInfo(Variant::INT, "render_priority", PROPERTY_HINT_RANGE, itos(RENDER_PRIORITY_MIN) + "," + itos(RENDER_PRIORITY_MAX) + ",1"), "set_render_priority", "get_render_priority");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "next_pass", PROPERTY_HINT_RESOURCE_TYPE, "Material"), "set_next_pass", "get_next_pass");
@@ -118,7 +131,7 @@ bool ShaderMaterial::_set(const StringName &p_name, const Variant &p_value) {
}
}
if (pr) {
- RenderingServer::get_singleton()->material_set_param(_get_material(), pr, p_value);
+ set_shader_param(pr, p_value);
return true;
}
}
@@ -140,7 +153,12 @@ bool ShaderMaterial::_get(const StringName &p_name, Variant &r_ret) const {
}
if (pr) {
- r_ret = RenderingServer::get_singleton()->material_get_param(_get_material(), pr);
+ const Map<StringName, Variant>::Element *E = param_cache.find(pr);
+ if (E) {
+ r_ret = E->get();
+ } else {
+ r_ret = Variant();
+ }
return true;
}
}
@@ -180,7 +198,7 @@ Variant ShaderMaterial::property_get_revert(const String &p_name) {
void ShaderMaterial::set_shader(const Ref<Shader> &p_shader) {
// Only connect/disconnect the signal when running in the editor.
- // This can be a slow operation, and `_change_notify()` (which is called by `_shader_changed()`)
+ // This can be a slow operation, and `notify_property_list_changed()` (which is called by `_shader_changed()`)
// does nothing in non-editor builds anyway. See GH-34741 for details.
if (shader.is_valid() && Engine::get_singleton()->is_editor_hint()) {
shader->disconnect("changed", callable_mp(this, &ShaderMaterial::_shader_changed));
@@ -198,7 +216,7 @@ void ShaderMaterial::set_shader(const Ref<Shader> &p_shader) {
}
RS::get_singleton()->material_set_shader(_get_material(), rid);
- _change_notify(); //properties for shader exposed
+ notify_property_list_changed(); //properties for shader exposed
emit_changed();
}
@@ -207,15 +225,35 @@ Ref<Shader> ShaderMaterial::get_shader() const {
}
void ShaderMaterial::set_shader_param(const StringName &p_param, const Variant &p_value) {
- RS::get_singleton()->material_set_param(_get_material(), p_param, p_value);
+ if (p_value.get_type() == Variant::NIL) {
+ param_cache.erase(p_param);
+ RS::get_singleton()->material_set_param(_get_material(), p_param, Variant());
+ } else {
+ param_cache[p_param] = p_value;
+ if (p_value.get_type() == Variant::OBJECT) {
+ RID tex_rid = p_value;
+ if (tex_rid == RID()) {
+ param_cache.erase(p_param);
+ RS::get_singleton()->material_set_param(_get_material(), p_param, Variant());
+ } else {
+ RS::get_singleton()->material_set_param(_get_material(), p_param, tex_rid);
+ }
+ } else {
+ RS::get_singleton()->material_set_param(_get_material(), p_param, p_value);
+ }
+ }
}
Variant ShaderMaterial::get_shader_param(const StringName &p_param) const {
- return RS::get_singleton()->material_get_param(_get_material(), p_param);
+ if (param_cache.has(p_param)) {
+ return param_cache[p_param];
+ } else {
+ return Variant();
+ }
}
void ShaderMaterial::_shader_changed() {
- _change_notify(); //update all properties
+ notify_property_list_changed(); //update all properties
}
void ShaderMaterial::_bind_methods() {
@@ -231,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
@@ -241,8 +279,8 @@ void ShaderMaterial::get_argument_options(const StringName &p_function, int p_id
if (shader.is_valid()) {
List<PropertyInfo> pl;
shader->get_param_list(&pl);
- for (List<PropertyInfo>::Element *E = pl.front(); E; E = E->next()) {
- r_options->push_back(quote_style + E->get().name.replace_first("shader_param/", "") + quote_style);
+ for (const PropertyInfo &E : pl) {
+ r_options->push_back(E.name.replace_first("shader_param/", "").quote(quote_style));
}
}
}
@@ -260,6 +298,13 @@ Shader::Mode ShaderMaterial::get_shader_mode() const {
return Shader::MODE_SPATIAL;
}
}
+RID ShaderMaterial::get_shader_rid() const {
+ if (shader.is_valid()) {
+ return shader->get_rid();
+ } else {
+ return RID();
+ }
+}
ShaderMaterial::ShaderMaterial() {
}
@@ -324,10 +369,8 @@ void BaseMaterial3D::init_shaders() {
shader_names->rim_texture_channel = "rim_texture_channel";
shader_names->heightmap_texture_channel = "heightmap_texture_channel";
shader_names->refraction_texture_channel = "refraction_texture_channel";
- shader_names->alpha_scissor_threshold = "alpha_scissor_threshold";
shader_names->transmittance_color = "transmittance_color";
- shader_names->transmittance_curve = "transmittance_curve";
shader_names->transmittance_depth = "transmittance_depth";
shader_names->transmittance_boost = "transmittance_boost";
@@ -349,6 +392,12 @@ void BaseMaterial3D::init_shaders() {
shader_names->texture_names[TEXTURE_DETAIL_ALBEDO] = "texture_detail_albedo";
shader_names->texture_names[TEXTURE_DETAIL_NORMAL] = "texture_detail_normal";
shader_names->texture_names[TEXTURE_ORM] = "texture_orm";
+
+ shader_names->alpha_scissor_threshold = "alpha_scissor_threshold";
+ shader_names->alpha_hash_scale = "alpha_hash_scale";
+
+ shader_names->alpha_antialiasing_edge = "alpha_antialiasing_edge";
+ shader_names->albedo_texture_size = "albedo_texture_size";
}
Ref<StandardMaterial3D> BaseMaterial3D::materials_for_2d[BaseMaterial3D::MAX_MATERIALS_FOR_2D];
@@ -421,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";
@@ -435,6 +489,8 @@ void BaseMaterial3D::_update_shader() {
case BLEND_MODE_MUL:
code += "blend_mul";
break;
+ case BLEND_MODE_MAX:
+ break; // Internal value, skip.
}
DepthDrawMode ddm = depth_draw_mode;
@@ -452,10 +508,8 @@ void BaseMaterial3D::_update_shader() {
case DEPTH_DRAW_DISABLED:
code += ",depth_draw_never";
break;
- }
-
- if (transparency == TRANSPARENCY_ALPHA_DEPTH_PRE_PASS) {
- code += ",depth_prepass_alpha";
+ case DEPTH_DRAW_MAX:
+ break; // Internal value, skip.
}
switch (cull_mode) {
@@ -468,6 +522,8 @@ void BaseMaterial3D::_update_shader() {
case CULL_DISABLED:
code += ",cull_disabled";
break;
+ case CULL_MAX:
+ break; // Internal value, skip.
}
switch (diffuse_mode) {
case DIFFUSE_BURLEY:
@@ -479,12 +535,11 @@ void BaseMaterial3D::_update_shader() {
case DIFFUSE_LAMBERT_WRAP:
code += ",diffuse_lambert_wrap";
break;
- case DIFFUSE_OREN_NAYAR:
- code += ",diffuse_oren_nayar";
- break;
case DIFFUSE_TOON:
code += ",diffuse_toon";
break;
+ case DIFFUSE_MAX:
+ break; // Internal value, skip.
}
switch (specular_mode) {
case SPECULAR_SCHLICK_GGX:
@@ -502,6 +557,8 @@ void BaseMaterial3D::_update_shader() {
case SPECULAR_DISABLED:
code += ",specular_disabled";
break;
+ case SPECULAR_MAX:
+ break; // Internal value, skip.
}
if (features[FEATURE_SUBSURFACE_SCATTERING] && flags[FLAG_SUBSURFACE_MODE_SKIN]) {
code += ",sss_mode_skin";
@@ -513,6 +570,9 @@ void BaseMaterial3D::_update_shader() {
if (flags[FLAG_DISABLE_DEPTH_TEST]) {
code += ",depth_test_disabled";
}
+ if (flags[FLAG_PARTICLE_TRAILS_MODE]) {
+ code += ",particle_trails";
+ }
if (shading_mode == SHADING_MODE_PER_VERTEX) {
code += ",vertex_lighting";
}
@@ -525,6 +585,23 @@ void BaseMaterial3D::_update_shader() {
if (flags[FLAG_USE_SHADOW_TO_OPACITY]) {
code += ",shadow_to_opacity";
}
+
+ if (transparency == TRANSPARENCY_ALPHA_DEPTH_PRE_PASS) {
+ code += ",depth_prepass_alpha";
+ }
+
+ // Although its technically possible to do alpha antialiasing without using alpha hash or alpha scissor,
+ // it is restricted in the base material because it has no use, and abusing it with regular Alpha blending can
+ // saturate the MSAA mask
+ if (transparency == TRANSPARENCY_ALPHA_HASH || transparency == TRANSPARENCY_ALPHA_SCISSOR) {
+ // alpha antialiasing is only useful in ALPHA_HASH or ALPHA_SCISSOR
+ if (alpha_antialiasing_mode == ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE) {
+ code += ",alpha_to_coverage";
+ } else if (alpha_antialiasing_mode == ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE) {
+ code += ",alpha_to_coverage_and_one";
+ }
+ }
+
code += ";\n";
code += "uniform vec4 albedo : hint_color;\n";
@@ -541,8 +618,18 @@ void BaseMaterial3D::_update_shader() {
code += "uniform float distance_fade_max;\n";
}
+ // alpha scissor is only valid if there is not antialiasing edge
+ // alpha hash is valid whenever, but not with alpha scissor
if (transparency == TRANSPARENCY_ALPHA_SCISSOR) {
code += "uniform float alpha_scissor_threshold;\n";
+ } else if (transparency == TRANSPARENCY_ALPHA_HASH) {
+ code += "uniform float alpha_hash_scale;\n";
+ }
+ // if alpha antialiasing isn't off, add in the edge variable
+ if (alpha_antialiasing_mode != ALPHA_ANTIALIASING_OFF &&
+ (transparency == TRANSPARENCY_ALPHA_SCISSOR || transparency == TRANSPARENCY_ALPHA_HASH)) {
+ code += "uniform float alpha_antialiasing_edge;\n";
+ code += "uniform ivec2 albedo_texture_size;\n";
}
code += "uniform float point_size : hint_range(0,128);\n";
@@ -568,6 +655,8 @@ void BaseMaterial3D::_update_shader() {
case TEXTURE_CHANNEL_GRAYSCALE: {
code += "uniform sampler2D texture_roughness : hint_roughness_gray," + texfilter_str + ";\n";
} break;
+ case TEXTURE_CHANNEL_MAX:
+ break; // Internal value, skip.
}
code += "uniform float specular;\n";
@@ -613,7 +702,7 @@ void BaseMaterial3D::_update_shader() {
code += "uniform sampler2D texture_flowmap : hint_aniso," + texfilter_str + ";\n";
}
if (features[FEATURE_AMBIENT_OCCLUSION]) {
- code += "uniform sampler2D texture_ambient_occlusion : hint_white;\n";
+ code += "uniform sampler2D texture_ambient_occlusion : hint_white, " + texfilter_str + ";\n";
code += "uniform vec4 ao_texture_channel;\n";
code += "uniform float ao_light_affect;\n";
}
@@ -633,7 +722,6 @@ void BaseMaterial3D::_update_shader() {
code += "uniform vec4 transmittance_color : hint_color;\n";
code += "uniform float transmittance_depth;\n";
code += "uniform sampler2D texture_subsurface_transmittance : hint_white," + texfilter_str + ";\n";
- code += "uniform float transmittance_curve;\n";
code += "uniform float transmittance_boost;\n";
}
@@ -675,439 +763,476 @@ void BaseMaterial3D::_update_shader() {
code += "void vertex() {\n";
if (flags[FLAG_SRGB_VERTEX_COLOR]) {
- code += "\tif (!OUTPUT_IS_SRGB) {\n";
- code += "\t\tCOLOR.rgb = mix( pow((COLOR.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), COLOR.rgb* (1.0 / 12.92), lessThan(COLOR.rgb,vec3(0.04045)) );\n";
- code += "\t}\n";
+ code += " if (!OUTPUT_IS_SRGB) {\n";
+ code += " COLOR.rgb = mix(pow((COLOR.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), COLOR.rgb * (1.0 / 12.92), lessThan(COLOR.rgb, vec3(0.04045)));\n";
+ code += " }\n";
}
if (flags[FLAG_USE_POINT_SIZE]) {
- code += "\tPOINT_SIZE=point_size;\n";
+ code += " POINT_SIZE=point_size;\n";
}
if (shading_mode == SHADING_MODE_PER_VERTEX) {
- code += "\tROUGHNESS=roughness;\n";
+ code += " ROUGHNESS=roughness;\n";
}
if (!flags[FLAG_UV1_USE_TRIPLANAR]) {
- code += "\tUV=UV*uv1_scale.xy+uv1_offset.xy;\n";
+ code += " UV=UV*uv1_scale.xy+uv1_offset.xy;\n";
}
switch (billboard_mode) {
case BILLBOARD_DISABLED: {
} break;
case BILLBOARD_ENABLED: {
- code += "\tMODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat4(CAMERA_MATRIX[0],CAMERA_MATRIX[1],CAMERA_MATRIX[2],WORLD_MATRIX[3]);\n";
+ code += " MODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat4(CAMERA_MATRIX[0],CAMERA_MATRIX[1],CAMERA_MATRIX[2],WORLD_MATRIX[3]);\n";
if (flags[FLAG_BILLBOARD_KEEP_SCALE]) {
- code += "\tMODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(length(WORLD_MATRIX[0].xyz), 0.0, 0.0, 0.0),vec4(0.0, length(WORLD_MATRIX[1].xyz), 0.0, 0.0),vec4(0.0, 0.0, length(WORLD_MATRIX[2].xyz), 0.0),vec4(0.0, 0.0, 0.0, 1.0));\n";
+ code += " MODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(length(WORLD_MATRIX[0].xyz), 0.0, 0.0, 0.0),vec4(0.0, length(WORLD_MATRIX[1].xyz), 0.0, 0.0),vec4(0.0, 0.0, length(WORLD_MATRIX[2].xyz), 0.0),vec4(0.0, 0.0, 0.0, 1.0));\n";
}
} break;
case BILLBOARD_FIXED_Y: {
- code += "\tMODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat4(CAMERA_MATRIX[0],WORLD_MATRIX[1],vec4(normalize(cross(CAMERA_MATRIX[0].xyz,WORLD_MATRIX[1].xyz)), 0.0),WORLD_MATRIX[3]);\n";
+ code += " MODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat4(CAMERA_MATRIX[0],WORLD_MATRIX[1],vec4(normalize(cross(CAMERA_MATRIX[0].xyz,WORLD_MATRIX[1].xyz)), 0.0),WORLD_MATRIX[3]);\n";
if (flags[FLAG_BILLBOARD_KEEP_SCALE]) {
- code += "\tMODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(length(WORLD_MATRIX[0].xyz), 0.0, 0.0, 0.0),vec4(0.0, 1.0, 0.0, 0.0),vec4(0.0, 0.0, length(WORLD_MATRIX[2].xyz), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
+ code += " MODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(length(WORLD_MATRIX[0].xyz), 0.0, 0.0, 0.0),vec4(0.0, 1.0, 0.0, 0.0),vec4(0.0, 0.0, length(WORLD_MATRIX[2].xyz), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
} else {
- code += "\tMODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(1.0, 0.0, 0.0, 0.0),vec4(0.0, 1.0/length(WORLD_MATRIX[1].xyz), 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0),vec4(0.0, 0.0, 0.0 ,1.0));\n";
+ code += " MODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(1.0, 0.0, 0.0, 0.0),vec4(0.0, 1.0/length(WORLD_MATRIX[1].xyz), 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0),vec4(0.0, 0.0, 0.0 ,1.0));\n";
}
} break;
case BILLBOARD_PARTICLES: {
//make billboard
- code += "\tmat4 mat_world = mat4(normalize(CAMERA_MATRIX[0])*length(WORLD_MATRIX[0]),normalize(CAMERA_MATRIX[1])*length(WORLD_MATRIX[0]),normalize(CAMERA_MATRIX[2])*length(WORLD_MATRIX[2]),WORLD_MATRIX[3]);\n";
+ code += " mat4 mat_world = mat4(normalize(CAMERA_MATRIX[0])*length(WORLD_MATRIX[0]),normalize(CAMERA_MATRIX[1])*length(WORLD_MATRIX[0]),normalize(CAMERA_MATRIX[2])*length(WORLD_MATRIX[2]),WORLD_MATRIX[3]);\n";
//rotate by rotation
- code += "\tmat_world = mat_world * mat4( vec4(cos(INSTANCE_CUSTOM.x),-sin(INSTANCE_CUSTOM.x), 0.0, 0.0), vec4(sin(INSTANCE_CUSTOM.x), cos(INSTANCE_CUSTOM.x), 0.0, 0.0),vec4(0.0, 0.0, 1.0, 0.0),vec4(0.0, 0.0, 0.0, 1.0));\n";
+ code += " mat_world = mat_world * mat4( vec4(cos(INSTANCE_CUSTOM.x),-sin(INSTANCE_CUSTOM.x), 0.0, 0.0), vec4(sin(INSTANCE_CUSTOM.x), cos(INSTANCE_CUSTOM.x), 0.0, 0.0),vec4(0.0, 0.0, 1.0, 0.0),vec4(0.0, 0.0, 0.0, 1.0));\n";
//set modelview
- code += "\tMODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat_world;\n";
+ code += " MODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat_world;\n";
//handle animation
- code += "\tfloat h_frames = float(particles_anim_h_frames);\n";
- code += "\tfloat v_frames = float(particles_anim_v_frames);\n";
- code += "\tfloat particle_total_frames = float(particles_anim_h_frames * particles_anim_v_frames);\n";
- code += "\tfloat particle_frame = floor(INSTANCE_CUSTOM.z * float(particle_total_frames));\n";
- code += "\tif (!particles_anim_loop) {\n";
- code += "\t\tparticle_frame = clamp(particle_frame, 0.0, particle_total_frames - 1.0);\n";
- code += "\t} else {\n";
- code += "\t\tparticle_frame = mod(particle_frame, particle_total_frames);\n";
- code += "\t}";
- code += "\tUV /= vec2(h_frames, v_frames);\n";
- code += "\tUV += vec2(mod(particle_frame, h_frames) / h_frames, floor(particle_frame / h_frames) / v_frames);\n";
+ code += " float h_frames = float(particles_anim_h_frames);\n";
+ code += " float v_frames = float(particles_anim_v_frames);\n";
+ code += " float particle_total_frames = float(particles_anim_h_frames * particles_anim_v_frames);\n";
+ code += " float particle_frame = floor(INSTANCE_CUSTOM.z * float(particle_total_frames));\n";
+ code += " if (!particles_anim_loop) {\n";
+ code += " particle_frame = clamp(particle_frame, 0.0, particle_total_frames - 1.0);\n";
+ code += " } else {\n";
+ code += " particle_frame = mod(particle_frame, particle_total_frames);\n";
+ code += " }";
+ code += " UV /= vec2(h_frames, v_frames);\n";
+ code += " UV += vec2(mod(particle_frame, h_frames) / h_frames, floor(particle_frame / h_frames) / v_frames);\n";
} break;
+ case BILLBOARD_MAX:
+ break; // Internal value, skip.
}
if (flags[FLAG_FIXED_SIZE]) {
- code += "\tif (PROJECTION_MATRIX[3][3] != 0.0) {\n";
+ code += " if (PROJECTION_MATRIX[3][3] != 0.0) {\n";
//orthogonal matrix, try to do about the same
//with viewport size
- code += "\t\tfloat h = abs(1.0 / (2.0 * PROJECTION_MATRIX[1][1]));\n";
- code += "\t\tfloat sc = (h * 2.0); //consistent with Y-fov\n";
- code += "\t\tMODELVIEW_MATRIX[0]*=sc;\n";
- code += "\t\tMODELVIEW_MATRIX[1]*=sc;\n";
- code += "\t\tMODELVIEW_MATRIX[2]*=sc;\n";
- code += "\t} else {\n";
+ code += " float h = abs(1.0 / (2.0 * PROJECTION_MATRIX[1][1]));\n";
+ code += " float sc = (h * 2.0); //consistent with Y-fov\n";
+ code += " MODELVIEW_MATRIX[0]*=sc;\n";
+ code += " MODELVIEW_MATRIX[1]*=sc;\n";
+ code += " MODELVIEW_MATRIX[2]*=sc;\n";
+ code += " } else {\n";
//just scale by depth
- code += "\t\tfloat sc = -(MODELVIEW_MATRIX)[3].z;\n";
- code += "\t\tMODELVIEW_MATRIX[0]*=sc;\n";
- code += "\t\tMODELVIEW_MATRIX[1]*=sc;\n";
- code += "\t\tMODELVIEW_MATRIX[2]*=sc;\n";
- code += "\t}\n";
+ code += " float sc = -(MODELVIEW_MATRIX)[3].z;\n";
+ code += " MODELVIEW_MATRIX[0]*=sc;\n";
+ code += " MODELVIEW_MATRIX[1]*=sc;\n";
+ code += " MODELVIEW_MATRIX[2]*=sc;\n";
+ code += " }\n";
}
if (detail_uv == DETAIL_UV_2 && !flags[FLAG_UV2_USE_TRIPLANAR]) {
- code += "\tUV2=UV2*uv2_scale.xy+uv2_offset.xy;\n";
+ code += " UV2=UV2*uv2_scale.xy+uv2_offset.xy;\n";
}
if (flags[FLAG_UV1_USE_TRIPLANAR] || flags[FLAG_UV2_USE_TRIPLANAR]) {
//generate tangent and binormal in world space
- code += "\tTANGENT = vec3(0.0,0.0,-1.0) * abs(NORMAL.x);\n";
- code += "\tTANGENT+= vec3(1.0,0.0,0.0) * abs(NORMAL.y);\n";
- code += "\tTANGENT+= vec3(1.0,0.0,0.0) * abs(NORMAL.z);\n";
- code += "\tTANGENT = normalize(TANGENT);\n";
+ code += " TANGENT = vec3(0.0,0.0,-1.0) * abs(NORMAL.x);\n";
+ code += " TANGENT+= vec3(1.0,0.0,0.0) * abs(NORMAL.y);\n";
+ code += " TANGENT+= vec3(1.0,0.0,0.0) * abs(NORMAL.z);\n";
+ code += " TANGENT = normalize(TANGENT);\n";
- code += "\tBINORMAL = vec3(0.0,-1.0,0.0) * abs(NORMAL.x);\n";
- code += "\tBINORMAL+= vec3(0.0,0.0,1.0) * abs(NORMAL.y);\n";
- code += "\tBINORMAL+= vec3(0.0,-1.0,0.0) * abs(NORMAL.z);\n";
- code += "\tBINORMAL = normalize(BINORMAL);\n";
+ code += " BINORMAL = vec3(0.0,1.0,0.0) * abs(NORMAL.x);\n";
+ code += " BINORMAL+= vec3(0.0,0.0,-1.0) * abs(NORMAL.y);\n";
+ code += " BINORMAL+= vec3(0.0,1.0,0.0) * abs(NORMAL.z);\n";
+ code += " BINORMAL = normalize(BINORMAL);\n";
}
if (flags[FLAG_UV1_USE_TRIPLANAR]) {
- code += "\tuv1_power_normal=pow(abs(NORMAL),vec3(uv1_blend_sharpness));\n";
- code += "\tuv1_power_normal/=dot(uv1_power_normal,vec3(1.0));\n";
- code += "\tuv1_triplanar_pos = VERTEX * uv1_scale + uv1_offset;\n";
- code += "\tuv1_triplanar_pos *= vec3(1.0,-1.0, 1.0);\n";
+ if (flags[FLAG_UV1_USE_WORLD_TRIPLANAR]) {
+ code += " uv1_power_normal=pow(abs(mat3(WORLD_MATRIX) * NORMAL),vec3(uv1_blend_sharpness));\n";
+ code += " uv1_triplanar_pos = (WORLD_MATRIX * vec4(VERTEX, 1.0f)).xyz * uv1_scale + uv1_offset;\n";
+ } else {
+ code += " uv1_power_normal=pow(abs(NORMAL),vec3(uv1_blend_sharpness));\n";
+ code += " uv1_triplanar_pos = VERTEX * uv1_scale + uv1_offset;\n";
+ }
+ code += " uv1_power_normal/=dot(uv1_power_normal,vec3(1.0));\n";
+ code += " uv1_triplanar_pos *= vec3(1.0,-1.0, 1.0);\n";
}
if (flags[FLAG_UV2_USE_TRIPLANAR]) {
- code += "\tuv2_power_normal=pow(abs(NORMAL), vec3(uv2_blend_sharpness));\n";
- code += "\tuv2_power_normal/=dot(uv2_power_normal,vec3(1.0));\n";
- code += "\tuv2_triplanar_pos = VERTEX * uv2_scale + uv2_offset;\n";
- code += "\tuv2_triplanar_pos *= vec3(1.0,-1.0, 1.0);\n";
+ if (flags[FLAG_UV2_USE_WORLD_TRIPLANAR]) {
+ code += " uv2_power_normal=pow(abs(mat3(WORLD_MATRIX) * NORMAL), vec3(uv2_blend_sharpness));\n";
+ code += " uv2_triplanar_pos = (WORLD_MATRIX * vec4(VERTEX, 1.0f)).xyz * uv2_scale + uv2_offset;\n";
+ } else {
+ code += " uv2_power_normal=pow(abs(NORMAL), vec3(uv2_blend_sharpness));\n";
+ code += " uv2_triplanar_pos = VERTEX * uv2_scale + uv2_offset;\n";
+ }
+ code += " uv2_power_normal/=dot(uv2_power_normal,vec3(1.0));\n";
+ code += " uv2_triplanar_pos *= vec3(1.0,-1.0, 1.0);\n";
}
if (grow_enabled) {
- code += "\tVERTEX+=NORMAL*grow;\n";
+ code += " VERTEX+=NORMAL*grow;\n";
}
code += "}\n";
code += "\n\n";
if (flags[FLAG_UV1_USE_TRIPLANAR] || flags[FLAG_UV2_USE_TRIPLANAR]) {
code += "vec4 triplanar_texture(sampler2D p_sampler,vec3 p_weights,vec3 p_triplanar_pos) {\n";
- code += "\tvec4 samp=vec4(0.0);\n";
- code += "\tsamp+= texture(p_sampler,p_triplanar_pos.xy) * p_weights.z;\n";
- code += "\tsamp+= texture(p_sampler,p_triplanar_pos.xz) * p_weights.y;\n";
- code += "\tsamp+= texture(p_sampler,p_triplanar_pos.zy * vec2(-1.0,1.0)) * p_weights.x;\n";
- code += "\treturn samp;\n";
+ code += " vec4 samp=vec4(0.0);\n";
+ code += " samp+= texture(p_sampler,p_triplanar_pos.xy) * p_weights.z;\n";
+ code += " samp+= texture(p_sampler,p_triplanar_pos.xz) * p_weights.y;\n";
+ code += " samp+= texture(p_sampler,p_triplanar_pos.zy * vec2(-1.0,1.0)) * p_weights.x;\n";
+ code += " return samp;\n";
code += "}\n";
}
code += "\n\n";
code += "void fragment() {\n";
if (!flags[FLAG_UV1_USE_TRIPLANAR]) {
- code += "\tvec2 base_uv = UV;\n";
+ code += " vec2 base_uv = UV;\n";
}
if ((features[FEATURE_DETAIL] && detail_uv == DETAIL_UV_2) || (features[FEATURE_AMBIENT_OCCLUSION] && flags[FLAG_AO_ON_UV2]) || (features[FEATURE_EMISSION] && flags[FLAG_EMISSION_ON_UV2])) {
- code += "\tvec2 base_uv2 = UV2;\n";
+ code += " vec2 base_uv2 = UV2;\n";
+ }
+
+ if (features[FEATURE_HEIGHT_MAPPING] && flags[FLAG_UV1_USE_TRIPLANAR]) {
+ // Display both resource name and albedo texture name.
+ // Materials are often built-in to scenes, so displaying the resource name alone may not be meaningful.
+ // On the other hand, albedo textures are almost always external to the scene.
+ if (textures[TEXTURE_ALBEDO].is_valid()) {
+ WARN_PRINT(vformat("%s (albedo %s): Height mapping is not supported on triplanar materials. Ignoring height mapping in favor of triplanar mapping.", get_path(), textures[TEXTURE_ALBEDO]->get_path()));
+ } else if (!get_path().is_empty()) {
+ WARN_PRINT(vformat("%s: Height mapping is not supported on triplanar materials. Ignoring height mapping in favor of triplanar mapping.", get_path()));
+ } else {
+ // Resource wasn't saved yet.
+ WARN_PRINT("Height mapping is not supported on triplanar materials. Ignoring height mapping in favor of triplanar mapping.");
+ }
}
if (!RenderingServer::get_singleton()->is_low_end() && features[FEATURE_HEIGHT_MAPPING] && !flags[FLAG_UV1_USE_TRIPLANAR]) { //heightmap not supported with triplanar
- code += "\t{\n";
- code += "\t\tvec3 view_dir = normalize(normalize(-VERTEX)*mat3(TANGENT*heightmap_flip.x,-BINORMAL*heightmap_flip.y,NORMAL));\n"; // binormal is negative due to mikktspace, flip 'unflips' it ;-)
+ code += " {\n";
+ code += " vec3 view_dir = normalize(normalize(-VERTEX)*mat3(TANGENT*heightmap_flip.x,-BINORMAL*heightmap_flip.y,NORMAL));\n"; // binormal is negative due to mikktspace, flip 'unflips' it ;-)
if (deep_parallax) {
- code += "\t\tfloat num_layers = mix(float(heightmap_max_layers),float(heightmap_min_layers), abs(dot(vec3(0.0, 0.0, 1.0), view_dir)));\n";
- code += "\t\tfloat layer_depth = 1.0 / num_layers;\n";
- code += "\t\tfloat current_layer_depth = 0.0;\n";
- code += "\t\tvec2 P = view_dir.xy * heightmap_scale;\n";
- code += "\t\tvec2 delta = P / num_layers;\n";
- code += "\t\tvec2 ofs = base_uv;\n";
+ code += " float num_layers = mix(float(heightmap_max_layers),float(heightmap_min_layers), abs(dot(vec3(0.0, 0.0, 1.0), view_dir)));\n";
+ code += " float layer_depth = 1.0 / num_layers;\n";
+ code += " float current_layer_depth = 0.0;\n";
+ code += " vec2 P = view_dir.xy * heightmap_scale;\n";
+ code += " vec2 delta = P / num_layers;\n";
+ code += " vec2 ofs = base_uv;\n";
if (flags[FLAG_INVERT_HEIGHTMAP]) {
- code += "\t\tfloat depth = texture(texture_heightmap, ofs).r;\n";
+ code += " float depth = texture(texture_heightmap, ofs).r;\n";
} else {
- code += "\t\tfloat depth = 1.0 - texture(texture_heightmap, ofs).r;\n";
+ code += " float depth = 1.0 - texture(texture_heightmap, ofs).r;\n";
}
- code += "\t\tfloat current_depth = 0.0;\n";
- code += "\t\twhile(current_depth < depth) {\n";
- code += "\t\t\tofs -= delta;\n";
+ code += " float current_depth = 0.0;\n";
+ code += " while(current_depth < depth) {\n";
+ code += " ofs -= delta;\n";
if (flags[FLAG_INVERT_HEIGHTMAP]) {
- code += "\t\t\tdepth = texture(texture_heightmap, ofs).r;\n";
+ code += " depth = texture(texture_heightmap, ofs).r;\n";
} else {
- code += "\t\t\tdepth = 1.0 - texture(texture_heightmap, ofs).r;\n";
+ code += " depth = 1.0 - texture(texture_heightmap, ofs).r;\n";
}
- code += "\t\t\tcurrent_depth += layer_depth;\n";
- code += "\t\t}\n";
- code += "\t\tvec2 prev_ofs = ofs + delta;\n";
- code += "\t\tfloat after_depth = depth - current_depth;\n";
+ code += " current_depth += layer_depth;\n";
+ code += " }\n";
+ code += " vec2 prev_ofs = ofs + delta;\n";
+ code += " float after_depth = depth - current_depth;\n";
if (flags[FLAG_INVERT_HEIGHTMAP]) {
- code += "\t\tfloat before_depth = texture(texture_heightmap, prev_ofs).r - current_depth + layer_depth;\n";
+ code += " float before_depth = texture(texture_heightmap, prev_ofs).r - current_depth + layer_depth;\n";
} else {
- code += "\t\tfloat before_depth = ( 1.0 - texture(texture_heightmap, prev_ofs).r ) - current_depth + layer_depth;\n";
+ code += " float before_depth = ( 1.0 - texture(texture_heightmap, prev_ofs).r ) - current_depth + layer_depth;\n";
}
- code += "\t\tfloat weight = after_depth / (after_depth - before_depth);\n";
- code += "\t\tofs = mix(ofs,prev_ofs,weight);\n";
+ code += " float weight = after_depth / (after_depth - before_depth);\n";
+ code += " ofs = mix(ofs,prev_ofs,weight);\n";
} else {
if (flags[FLAG_INVERT_HEIGHTMAP]) {
- code += "\t\tfloat depth = texture(texture_heightmap, base_uv).r;\n";
+ code += " float depth = texture(texture_heightmap, base_uv).r;\n";
} else {
- code += "\t\tfloat depth = 1.0 - texture(texture_heightmap, base_uv).r;\n";
+ code += " float depth = 1.0 - texture(texture_heightmap, base_uv).r;\n";
}
- code += "\t\tvec2 ofs = base_uv - view_dir.xy / view_dir.z * (depth * heightmap_scale);\n";
+ code += " vec2 ofs = base_uv - view_dir.xy / view_dir.z * (depth * heightmap_scale);\n";
}
- code += "\t\tbase_uv=ofs;\n";
+ code += " base_uv=ofs;\n";
if (features[FEATURE_DETAIL] && detail_uv == DETAIL_UV_2) {
- code += "\t\tbase_uv2-=ofs;\n";
+ code += " base_uv2-=ofs;\n";
}
- code += "\t}\n";
+ code += " }\n";
}
if (flags[FLAG_USE_POINT_SIZE]) {
- code += "\tvec4 albedo_tex = texture(texture_albedo,POINT_COORD);\n";
+ code += " vec4 albedo_tex = texture(texture_albedo,POINT_COORD);\n";
} else {
if (flags[FLAG_UV1_USE_TRIPLANAR]) {
- code += "\tvec4 albedo_tex = triplanar_texture(texture_albedo,uv1_power_normal,uv1_triplanar_pos);\n";
+ code += " vec4 albedo_tex = triplanar_texture(texture_albedo,uv1_power_normal,uv1_triplanar_pos);\n";
} else {
- code += "\tvec4 albedo_tex = texture(texture_albedo,base_uv);\n";
+ code += " vec4 albedo_tex = texture(texture_albedo,base_uv);\n";
}
}
if (flags[FLAG_ALBEDO_TEXTURE_FORCE_SRGB]) {
- code += "\talbedo_tex.rgb = mix(pow((albedo_tex.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)),vec3(2.4)),albedo_tex.rgb.rgb * (1.0 / 12.92),lessThan(albedo_tex.rgb,vec3(0.04045)));\n";
+ code += " albedo_tex.rgb = mix(pow((albedo_tex.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)),vec3(2.4)),albedo_tex.rgb.rgb * (1.0 / 12.92),lessThan(albedo_tex.rgb,vec3(0.04045)));\n";
}
if (flags[FLAG_ALBEDO_FROM_VERTEX_COLOR]) {
- code += "\talbedo_tex *= COLOR;\n";
+ code += " albedo_tex *= COLOR;\n";
}
- code += "\tALBEDO = albedo.rgb * albedo_tex.rgb;\n";
+ code += " ALBEDO = albedo.rgb * albedo_tex.rgb;\n";
if (!orm) {
if (flags[FLAG_UV1_USE_TRIPLANAR]) {
- code += "\tfloat metallic_tex = dot(triplanar_texture(texture_metallic,uv1_power_normal,uv1_triplanar_pos),metallic_texture_channel);\n";
+ code += " float metallic_tex = dot(triplanar_texture(texture_metallic,uv1_power_normal,uv1_triplanar_pos),metallic_texture_channel);\n";
} else {
- code += "\tfloat metallic_tex = dot(texture(texture_metallic,base_uv),metallic_texture_channel);\n";
+ code += " float metallic_tex = dot(texture(texture_metallic,base_uv),metallic_texture_channel);\n";
}
- code += "\tMETALLIC = metallic_tex * metallic;\n";
+ code += " METALLIC = metallic_tex * metallic;\n";
switch (roughness_texture_channel) {
case TEXTURE_CHANNEL_RED: {
- code += "\tvec4 roughness_texture_channel = vec4(1.0,0.0,0.0,0.0);\n";
+ code += " vec4 roughness_texture_channel = vec4(1.0,0.0,0.0,0.0);\n";
} break;
case TEXTURE_CHANNEL_GREEN: {
- code += "\tvec4 roughness_texture_channel = vec4(0.0,1.0,0.0,0.0);\n";
+ code += " vec4 roughness_texture_channel = vec4(0.0,1.0,0.0,0.0);\n";
} break;
case TEXTURE_CHANNEL_BLUE: {
- code += "\tvec4 roughness_texture_channel = vec4(0.0,0.0,1.0,0.0);\n";
+ code += " vec4 roughness_texture_channel = vec4(0.0,0.0,1.0,0.0);\n";
} break;
case TEXTURE_CHANNEL_ALPHA: {
- code += "\tvec4 roughness_texture_channel = vec4(0.0,0.0,0.0,1.0);\n";
+ code += " vec4 roughness_texture_channel = vec4(0.0,0.0,0.0,1.0);\n";
} break;
case TEXTURE_CHANNEL_GRAYSCALE: {
- code += "\tvec4 roughness_texture_channel = vec4(0.333333,0.333333,0.333333,0.0);\n";
+ code += " vec4 roughness_texture_channel = vec4(0.333333,0.333333,0.333333,0.0);\n";
} break;
+ case TEXTURE_CHANNEL_MAX:
+ break; // Internal value, skip.
}
if (flags[FLAG_UV1_USE_TRIPLANAR]) {
- code += "\tfloat roughness_tex = dot(triplanar_texture(texture_roughness,uv1_power_normal,uv1_triplanar_pos),roughness_texture_channel);\n";
+ code += " float roughness_tex = dot(triplanar_texture(texture_roughness,uv1_power_normal,uv1_triplanar_pos),roughness_texture_channel);\n";
} else {
- code += "\tfloat roughness_tex = dot(texture(texture_roughness,base_uv),roughness_texture_channel);\n";
+ code += " float roughness_tex = dot(texture(texture_roughness,base_uv),roughness_texture_channel);\n";
}
- code += "\tROUGHNESS = roughness_tex * roughness;\n";
- code += "\tSPECULAR = specular;\n";
+ code += " ROUGHNESS = roughness_tex * roughness;\n";
+ code += " SPECULAR = specular;\n";
} else {
if (flags[FLAG_UV1_USE_TRIPLANAR]) {
- code += "\tfloat orm_tex = triplanar_texture(texture_orm,uv1_power_normal,uv1_triplanar_pos);\n";
+ code += " vec4 orm_tex = triplanar_texture(texture_orm,uv1_power_normal,uv1_triplanar_pos);\n";
} else {
- code += "\tfloat orm_tex = texture(texture_orm,base_uv);\n";
+ code += " vec4 orm_tex = texture(texture_orm,base_uv);\n";
}
- code += "\tROUGHNESS = orm_tex.g;\n";
- code += "\tMETALLIC = orm_tex.b;\n";
+ code += " ROUGHNESS = orm_tex.g;\n";
+ code += " METALLIC = orm_tex.b;\n";
}
if (features[FEATURE_NORMAL_MAPPING]) {
if (flags[FLAG_UV1_USE_TRIPLANAR]) {
- code += "\tNORMALMAP = triplanar_texture(texture_normal,uv1_power_normal,uv1_triplanar_pos).rgb;\n";
+ code += " NORMAL_MAP = triplanar_texture(texture_normal,uv1_power_normal,uv1_triplanar_pos).rgb;\n";
} else {
- code += "\tNORMALMAP = texture(texture_normal,base_uv).rgb;\n";
+ code += " NORMAL_MAP = texture(texture_normal,base_uv).rgb;\n";
}
- code += "\tNORMALMAP_DEPTH = normal_scale;\n";
+ code += " NORMAL_MAP_DEPTH = normal_scale;\n";
}
if (features[FEATURE_EMISSION]) {
if (flags[FLAG_EMISSION_ON_UV2]) {
if (flags[FLAG_UV2_USE_TRIPLANAR]) {
- code += "\tvec3 emission_tex = triplanar_texture(texture_emission,uv2_power_normal,uv2_triplanar_pos).rgb;\n";
+ code += " vec3 emission_tex = triplanar_texture(texture_emission,uv2_power_normal,uv2_triplanar_pos).rgb;\n";
} else {
- code += "\tvec3 emission_tex = texture(texture_emission,base_uv2).rgb;\n";
+ code += " vec3 emission_tex = texture(texture_emission,base_uv2).rgb;\n";
}
} else {
if (flags[FLAG_UV1_USE_TRIPLANAR]) {
- code += "\tvec3 emission_tex = triplanar_texture(texture_emission,uv1_power_normal,uv1_triplanar_pos).rgb;\n";
+ code += " vec3 emission_tex = triplanar_texture(texture_emission,uv1_power_normal,uv1_triplanar_pos).rgb;\n";
} else {
- code += "\tvec3 emission_tex = texture(texture_emission,base_uv).rgb;\n";
+ code += " vec3 emission_tex = texture(texture_emission,base_uv).rgb;\n";
}
}
if (emission_op == EMISSION_OP_ADD) {
- code += "\tEMISSION = (emission.rgb+emission_tex)*emission_energy;\n";
+ code += " EMISSION = (emission.rgb+emission_tex)*emission_energy;\n";
} else {
- code += "\tEMISSION = (emission.rgb*emission_tex)*emission_energy;\n";
+ code += " EMISSION = (emission.rgb*emission_tex)*emission_energy;\n";
}
}
if (features[FEATURE_REFRACTION]) {
if (features[FEATURE_NORMAL_MAPPING]) {
- code += "\tvec3 ref_normal = normalize( mix(NORMAL,TANGENT * NORMALMAP.x + BINORMAL * NORMALMAP.y + NORMAL * NORMALMAP.z,NORMALMAP_DEPTH) );\n";
+ code += " vec3 unpacked_normal = NORMAL_MAP;\n";
+ code += " unpacked_normal.xy = unpacked_normal.xy * 2.0 - 1.0;\n";
+ code += " unpacked_normal.z = sqrt(max(0.0, 1.0 - dot(unpacked_normal.xy, unpacked_normal.xy)));\n";
+ code += " vec3 ref_normal = normalize( mix(NORMAL,TANGENT * unpacked_normal.x + BINORMAL * unpacked_normal.y + NORMAL * unpacked_normal.z,NORMAL_MAP_DEPTH) );\n";
} else {
- code += "\tvec3 ref_normal = NORMAL;\n";
+ code += " vec3 ref_normal = NORMAL;\n";
}
if (flags[FLAG_UV1_USE_TRIPLANAR]) {
- code += "\tvec2 ref_ofs = SCREEN_UV - ref_normal.xy * dot(triplanar_texture(texture_refraction,uv1_power_normal,uv1_triplanar_pos),refraction_texture_channel) * refraction;\n";
+ code += " vec2 ref_ofs = SCREEN_UV - ref_normal.xy * dot(triplanar_texture(texture_refraction,uv1_power_normal,uv1_triplanar_pos),refraction_texture_channel) * refraction;\n";
} else {
- code += "\tvec2 ref_ofs = SCREEN_UV - ref_normal.xy * dot(texture(texture_refraction,base_uv),refraction_texture_channel) * refraction;\n";
+ code += " vec2 ref_ofs = SCREEN_UV - ref_normal.xy * dot(texture(texture_refraction,base_uv),refraction_texture_channel) * refraction;\n";
}
- code += "\tfloat ref_amount = 1.0 - albedo.a * albedo_tex.a;\n";
- code += "\tEMISSION += textureLod(SCREEN_TEXTURE,ref_ofs,ROUGHNESS * 8.0).rgb * ref_amount;\n";
- code += "\tALBEDO *= 1.0 - ref_amount;\n";
- code += "\tALPHA = 1.0;\n";
+ code += " float ref_amount = 1.0 - albedo.a * albedo_tex.a;\n";
+ code += " EMISSION += textureLod(SCREEN_TEXTURE,ref_ofs,ROUGHNESS * 8.0).rgb * ref_amount;\n";
+ code += " ALBEDO *= 1.0 - ref_amount;\n";
+ code += " ALPHA = 1.0;\n";
- } else if (transparency == TRANSPARENCY_ALPHA || transparency == TRANSPARENCY_ALPHA_DEPTH_PRE_PASS || flags[FLAG_USE_SHADOW_TO_OPACITY] || (distance_fade == DISTANCE_FADE_PIXEL_ALPHA) || proximity_fade_enabled) {
- code += "\tALPHA = albedo.a * albedo_tex.a;\n";
+ } else if (transparency != TRANSPARENCY_DISABLED || flags[FLAG_USE_SHADOW_TO_OPACITY] || (distance_fade == DISTANCE_FADE_PIXEL_ALPHA) || proximity_fade_enabled) {
+ code += " ALPHA = albedo.a * albedo_tex.a;\n";
+ }
+ if (transparency == TRANSPARENCY_ALPHA_HASH) {
+ code += " ALPHA_HASH_SCALE = alpha_hash_scale;\n";
} else if (transparency == TRANSPARENCY_ALPHA_SCISSOR) {
- code += "\tif (albedo.a * albedo_tex.a < alpha_scissor_threshold) discard;\n";
+ code += " ALPHA_SCISSOR_THRESHOLD = alpha_scissor_threshold;\n";
+ }
+ if (alpha_antialiasing_mode != ALPHA_ANTIALIASING_OFF && (transparency == TRANSPARENCY_ALPHA_HASH || transparency == TRANSPARENCY_ALPHA_SCISSOR)) {
+ code += " ALPHA_ANTIALIASING_EDGE = alpha_antialiasing_edge;\n";
+ code += " ALPHA_TEXTURE_COORDINATE = UV * vec2(albedo_texture_size);\n";
}
if (proximity_fade_enabled) {
- code += "\tfloat depth_tex = textureLod(DEPTH_TEXTURE,SCREEN_UV,0.0).r;\n";
- code += "\tvec4 world_pos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV*2.0-1.0,depth_tex*2.0-1.0,1.0);\n";
- code += "\tworld_pos.xyz/=world_pos.w;\n";
- code += "\tALPHA*=clamp(1.0-smoothstep(world_pos.z+proximity_fade_distance,world_pos.z,VERTEX.z),0.0,1.0);\n";
+ code += " float depth_tex = textureLod(DEPTH_TEXTURE,SCREEN_UV,0.0).r;\n";
+ code += " vec4 world_pos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV*2.0-1.0,depth_tex*2.0-1.0,1.0);\n";
+ code += " world_pos.xyz/=world_pos.w;\n";
+ code += " ALPHA*=clamp(1.0-smoothstep(world_pos.z+proximity_fade_distance,world_pos.z,VERTEX.z),0.0,1.0);\n";
}
if (distance_fade != DISTANCE_FADE_DISABLED) {
if ((distance_fade == DISTANCE_FADE_OBJECT_DITHER || distance_fade == DISTANCE_FADE_PIXEL_DITHER)) {
if (!RenderingServer::get_singleton()->is_low_end()) {
- code += "\t{\n";
+ code += " {\n";
if (distance_fade == DISTANCE_FADE_OBJECT_DITHER) {
- code += "\t\tfloat fade_distance = abs((INV_CAMERA_MATRIX * WORLD_MATRIX[3]).z);\n";
+ code += " float fade_distance = abs((INV_CAMERA_MATRIX * WORLD_MATRIX[3]).z);\n";
} else {
- code += "\t\tfloat fade_distance=-VERTEX.z;\n";
+ code += " float fade_distance=-VERTEX.z;\n";
}
- code += "\t\tfloat fade=clamp(smoothstep(distance_fade_min,distance_fade_max,fade_distance),0.0,1.0);\n";
- code += "\t\tint x = int(FRAGCOORD.x) % 4;\n";
- code += "\t\tint y = int(FRAGCOORD.y) % 4;\n";
- code += "\t\tint index = x + y * 4;\n";
- code += "\t\tfloat limit = 0.0;\n\n";
- code += "\t\tif (x < 8) {\n";
- code += "\t\t\tif (index == 0) limit = 0.0625;\n";
- code += "\t\t\tif (index == 1) limit = 0.5625;\n";
- code += "\t\t\tif (index == 2) limit = 0.1875;\n";
- code += "\t\t\tif (index == 3) limit = 0.6875;\n";
- code += "\t\t\tif (index == 4) limit = 0.8125;\n";
- code += "\t\t\tif (index == 5) limit = 0.3125;\n";
- code += "\t\t\tif (index == 6) limit = 0.9375;\n";
- code += "\t\t\tif (index == 7) limit = 0.4375;\n";
- code += "\t\t\tif (index == 8) limit = 0.25;\n";
- code += "\t\t\tif (index == 9) limit = 0.75;\n";
- code += "\t\t\tif (index == 10) limit = 0.125;\n";
- code += "\t\t\tif (index == 11) limit = 0.625;\n";
- code += "\t\t\tif (index == 12) limit = 1.0;\n";
- code += "\t\t\tif (index == 13) limit = 0.5;\n";
- code += "\t\t\tif (index == 14) limit = 0.875;\n";
- code += "\t\t\tif (index == 15) limit = 0.375;\n";
- code += "\t\t}\n\n";
- code += "\tif (fade < limit)\n";
- code += "\t\tdiscard;\n";
- code += "\t}\n\n";
+ code += " float fade=clamp(smoothstep(distance_fade_min,distance_fade_max,fade_distance),0.0,1.0);\n";
+ code += " int x = int(FRAGCOORD.x) % 4;\n";
+ code += " int y = int(FRAGCOORD.y) % 4;\n";
+ code += " int index = x + y * 4;\n";
+ code += " float limit = 0.0;\n\n";
+ code += " if (x < 8) {\n";
+ code += " if (index == 0) limit = 0.0625;\n";
+ code += " if (index == 1) limit = 0.5625;\n";
+ code += " if (index == 2) limit = 0.1875;\n";
+ code += " if (index == 3) limit = 0.6875;\n";
+ code += " if (index == 4) limit = 0.8125;\n";
+ code += " if (index == 5) limit = 0.3125;\n";
+ code += " if (index == 6) limit = 0.9375;\n";
+ code += " if (index == 7) limit = 0.4375;\n";
+ code += " if (index == 8) limit = 0.25;\n";
+ code += " if (index == 9) limit = 0.75;\n";
+ code += " if (index == 10) limit = 0.125;\n";
+ code += " if (index == 11) limit = 0.625;\n";
+ code += " if (index == 12) limit = 1.0;\n";
+ code += " if (index == 13) limit = 0.5;\n";
+ code += " if (index == 14) limit = 0.875;\n";
+ code += " if (index == 15) limit = 0.375;\n";
+ code += " }\n\n";
+ code += " if (fade < limit)\n";
+ code += " discard;\n";
+ code += " }\n\n";
}
} else {
- code += "\tALPHA*=clamp(smoothstep(distance_fade_min,distance_fade_max,-VERTEX.z),0.0,1.0);\n";
+ code += " ALPHA*=clamp(smoothstep(distance_fade_min,distance_fade_max,-VERTEX.z),0.0,1.0);\n";
}
}
if (features[FEATURE_RIM]) {
if (flags[FLAG_UV1_USE_TRIPLANAR]) {
- code += "\tvec2 rim_tex = triplanar_texture(texture_rim,uv1_power_normal,uv1_triplanar_pos).xy;\n";
+ code += " vec2 rim_tex = triplanar_texture(texture_rim,uv1_power_normal,uv1_triplanar_pos).xy;\n";
} else {
- code += "\tvec2 rim_tex = texture(texture_rim,base_uv).xy;\n";
+ code += " vec2 rim_tex = texture(texture_rim,base_uv).xy;\n";
}
- code += "\tRIM = rim*rim_tex.x;";
- code += "\tRIM_TINT = rim_tint*rim_tex.y;\n";
+ code += " RIM = rim*rim_tex.x;";
+ code += " RIM_TINT = rim_tint*rim_tex.y;\n";
}
if (features[FEATURE_CLEARCOAT]) {
if (flags[FLAG_UV1_USE_TRIPLANAR]) {
- code += "\tvec2 clearcoat_tex = triplanar_texture(texture_clearcoat,uv1_power_normal,uv1_triplanar_pos).xy;\n";
+ code += " vec2 clearcoat_tex = triplanar_texture(texture_clearcoat,uv1_power_normal,uv1_triplanar_pos).xy;\n";
} else {
- code += "\tvec2 clearcoat_tex = texture(texture_clearcoat,base_uv).xy;\n";
+ code += " vec2 clearcoat_tex = texture(texture_clearcoat,base_uv).xy;\n";
}
- code += "\tCLEARCOAT = clearcoat*clearcoat_tex.x;";
- code += "\tCLEARCOAT_GLOSS = clearcoat_gloss*clearcoat_tex.y;\n";
+ code += " CLEARCOAT = clearcoat*clearcoat_tex.x;";
+ code += " CLEARCOAT_GLOSS = clearcoat_gloss*clearcoat_tex.y;\n";
}
if (features[FEATURE_ANISOTROPY]) {
if (flags[FLAG_UV1_USE_TRIPLANAR]) {
- code += "\tvec3 anisotropy_tex = triplanar_texture(texture_flowmap,uv1_power_normal,uv1_triplanar_pos).rga;\n";
+ code += " vec3 anisotropy_tex = triplanar_texture(texture_flowmap,uv1_power_normal,uv1_triplanar_pos).rga;\n";
} else {
- code += "\tvec3 anisotropy_tex = texture(texture_flowmap,base_uv).rga;\n";
+ code += " vec3 anisotropy_tex = texture(texture_flowmap,base_uv).rga;\n";
}
- code += "\tANISOTROPY = anisotropy_ratio*anisotropy_tex.b;\n";
- code += "\tANISOTROPY_FLOW = anisotropy_tex.rg*2.0-1.0;\n";
+ code += " ANISOTROPY = anisotropy_ratio*anisotropy_tex.b;\n";
+ code += " ANISOTROPY_FLOW = anisotropy_tex.rg*2.0-1.0;\n";
}
if (features[FEATURE_AMBIENT_OCCLUSION]) {
if (!orm) {
if (flags[FLAG_AO_ON_UV2]) {
if (flags[FLAG_UV2_USE_TRIPLANAR]) {
- code += "\tAO = dot(triplanar_texture(texture_ambient_occlusion,uv2_power_normal,uv2_triplanar_pos),ao_texture_channel);\n";
+ code += " AO = dot(triplanar_texture(texture_ambient_occlusion,uv2_power_normal,uv2_triplanar_pos),ao_texture_channel);\n";
} else {
- code += "\tAO = dot(texture(texture_ambient_occlusion,base_uv2),ao_texture_channel);\n";
+ code += " AO = dot(texture(texture_ambient_occlusion,base_uv2),ao_texture_channel);\n";
}
} else {
if (flags[FLAG_UV1_USE_TRIPLANAR]) {
- code += "\tAO = dot(triplanar_texture(texture_ambient_occlusion,uv1_power_normal,uv1_triplanar_pos),ao_texture_channel);\n";
+ code += " AO = dot(triplanar_texture(texture_ambient_occlusion,uv1_power_normal,uv1_triplanar_pos),ao_texture_channel);\n";
} else {
- code += "\tAO = dot(texture(texture_ambient_occlusion,base_uv),ao_texture_channel);\n";
+ code += " AO = dot(texture(texture_ambient_occlusion,base_uv),ao_texture_channel);\n";
}
}
} else {
- code += "\tAO = orm_tex.r;\n";
+ code += " AO = orm_tex.r;\n";
}
- code += "\tAO_LIGHT_AFFECT = ao_light_affect;\n";
+ code += " AO_LIGHT_AFFECT = ao_light_affect;\n";
}
if (features[FEATURE_SUBSURFACE_SCATTERING]) {
if (flags[FLAG_UV1_USE_TRIPLANAR]) {
- code += "\tfloat sss_tex = triplanar_texture(texture_subsurface_scattering,uv1_power_normal,uv1_triplanar_pos).r;\n";
+ code += " float sss_tex = triplanar_texture(texture_subsurface_scattering,uv1_power_normal,uv1_triplanar_pos).r;\n";
} else {
- code += "\tfloat sss_tex = texture(texture_subsurface_scattering,base_uv).r;\n";
+ code += " float sss_tex = texture(texture_subsurface_scattering,base_uv).r;\n";
}
- code += "\tSSS_STRENGTH=subsurface_scattering_strength*sss_tex;\n";
+ code += " SSS_STRENGTH=subsurface_scattering_strength*sss_tex;\n";
}
if (features[FEATURE_SUBSURFACE_TRANSMITTANCE]) {
if (flags[FLAG_UV1_USE_TRIPLANAR]) {
- code += "\tvec4 trans_color_tex = triplanar_texture(texture_subsurface_transmittance,uv1_power_normal,uv1_triplanar_pos);\n";
+ code += " vec4 trans_color_tex = triplanar_texture(texture_subsurface_transmittance,uv1_power_normal,uv1_triplanar_pos);\n";
} else {
- code += "\tvec4 trans_color_tex = texture(texture_subsurface_transmittance,base_uv);\n";
+ code += " vec4 trans_color_tex = texture(texture_subsurface_transmittance,base_uv);\n";
}
- code += "\tSSS_TRANSMITTANCE_COLOR=transmittance_color*trans_color_tex;\n";
+ code += " SSS_TRANSMITTANCE_COLOR=transmittance_color*trans_color_tex;\n";
- code += "\tSSS_TRANSMITTANCE_DEPTH=transmittance_depth;\n";
- code += "\tSSS_TRANSMITTANCE_CURVE=transmittance_curve;\n";
- code += "\tSSS_TRANSMITTANCE_BOOST=transmittance_boost;\n";
+ code += " SSS_TRANSMITTANCE_DEPTH=transmittance_depth;\n";
+ code += " SSS_TRANSMITTANCE_BOOST=transmittance_boost;\n";
}
if (features[FEATURE_BACKLIGHT]) {
if (flags[FLAG_UV1_USE_TRIPLANAR]) {
- code += "\tvec3 backlight_tex = triplanar_texture(texture_backlight,uv1_power_normal,uv1_triplanar_pos).rgb;\n";
+ code += " vec3 backlight_tex = triplanar_texture(texture_backlight,uv1_power_normal,uv1_triplanar_pos).rgb;\n";
} else {
- code += "\tvec3 backlight_tex = texture(texture_backlight,base_uv).rgb;\n";
+ code += " vec3 backlight_tex = texture(texture_backlight,base_uv).rgb;\n";
}
- code += "\tBACKLIGHT = (backlight.rgb+backlight_tex);\n";
+ code += " BACKLIGHT = (backlight.rgb+backlight_tex);\n";
}
if (features[FEATURE_DETAIL]) {
@@ -1115,39 +1240,41 @@ void BaseMaterial3D::_update_shader() {
if (triplanar) {
String tp_uv = detail_uv == DETAIL_UV_1 ? "uv1" : "uv2";
- code += "\tvec4 detail_tex = triplanar_texture(texture_detail_albedo," + tp_uv + "_power_normal," + tp_uv + "_triplanar_pos);\n";
- code += "\tvec4 detail_norm_tex = triplanar_texture(texture_detail_normal," + tp_uv + "_power_normal," + tp_uv + "_triplanar_pos);\n";
+ code += " vec4 detail_tex = triplanar_texture(texture_detail_albedo," + tp_uv + "_power_normal," + tp_uv + "_triplanar_pos);\n";
+ code += " vec4 detail_norm_tex = triplanar_texture(texture_detail_normal," + tp_uv + "_power_normal," + tp_uv + "_triplanar_pos);\n";
} else {
String det_uv = detail_uv == DETAIL_UV_1 ? "base_uv" : "base_uv2";
- code += "\tvec4 detail_tex = texture(texture_detail_albedo," + det_uv + ");\n";
- code += "\tvec4 detail_norm_tex = texture(texture_detail_normal," + det_uv + ");\n";
+ code += " vec4 detail_tex = texture(texture_detail_albedo," + det_uv + ");\n";
+ code += " vec4 detail_norm_tex = texture(texture_detail_normal," + det_uv + ");\n";
}
if (flags[FLAG_UV1_USE_TRIPLANAR]) {
- code += "\tvec4 detail_mask_tex = triplanar_texture(texture_detail_mask,uv1_power_normal,uv1_triplanar_pos);\n";
+ code += " vec4 detail_mask_tex = triplanar_texture(texture_detail_mask,uv1_power_normal,uv1_triplanar_pos);\n";
} else {
- code += "\tvec4 detail_mask_tex = texture(texture_detail_mask,base_uv);\n";
+ code += " vec4 detail_mask_tex = texture(texture_detail_mask,base_uv);\n";
}
switch (detail_blend_mode) {
case BLEND_MODE_MIX: {
- code += "\tvec3 detail = mix(ALBEDO.rgb,detail_tex.rgb,detail_tex.a);\n";
+ code += " vec3 detail = mix(ALBEDO.rgb,detail_tex.rgb,detail_tex.a);\n";
} break;
case BLEND_MODE_ADD: {
- code += "\tvec3 detail = mix(ALBEDO.rgb,ALBEDO.rgb+detail_tex.rgb,detail_tex.a);\n";
+ code += " vec3 detail = mix(ALBEDO.rgb,ALBEDO.rgb+detail_tex.rgb,detail_tex.a);\n";
} break;
case BLEND_MODE_SUB: {
- code += "\tvec3 detail = mix(ALBEDO.rgb,ALBEDO.rgb-detail_tex.rgb,detail_tex.a);\n";
+ code += " vec3 detail = mix(ALBEDO.rgb,ALBEDO.rgb-detail_tex.rgb,detail_tex.a);\n";
} break;
case BLEND_MODE_MUL: {
- code += "\tvec3 detail = mix(ALBEDO.rgb,ALBEDO.rgb*detail_tex.rgb,detail_tex.a);\n";
+ code += " vec3 detail = mix(ALBEDO.rgb,ALBEDO.rgb*detail_tex.rgb,detail_tex.a);\n";
} break;
+ case BLEND_MODE_MAX:
+ break; // Internal value, skip.
}
- code += "\tvec3 detail_norm = mix(NORMALMAP,detail_norm_tex.rgb,detail_tex.a);\n";
- code += "\tNORMALMAP = mix(NORMALMAP,detail_norm,detail_mask_tex.r);\n";
- code += "\tALBEDO.rgb = mix(ALBEDO.rgb,detail,detail_mask_tex.r);\n";
+ code += " vec3 detail_norm = mix(NORMAL_MAP,detail_norm_tex.rgb,detail_tex.a);\n";
+ code += " NORMAL_MAP = mix(NORMAL_MAP,detail_norm,detail_mask_tex.r);\n";
+ code += " ALBEDO.rgb = mix(ALBEDO.rgb,detail,detail_mask_tex.r);\n";
}
code += "}\n";
@@ -1339,15 +1466,6 @@ float BaseMaterial3D::get_transmittance_depth() const {
return transmittance_depth;
}
-void BaseMaterial3D::set_transmittance_curve(float p_curve) {
- transmittance_curve = p_curve;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->transmittance_curve, p_curve);
-}
-
-float BaseMaterial3D::get_transmittance_curve() const {
- return transmittance_curve;
-}
-
void BaseMaterial3D::set_transmittance_boost(float p_boost) {
transmittance_boost = p_boost;
RS::get_singleton()->material_set_param(_get_material(), shader_names->transmittance_boost, p_boost);
@@ -1417,13 +1535,27 @@ void BaseMaterial3D::set_transparency(Transparency p_transparency) {
transparency = p_transparency;
_queue_shader_change();
- _change_notify();
+ notify_property_list_changed();
}
BaseMaterial3D::Transparency BaseMaterial3D::get_transparency() const {
return transparency;
}
+void BaseMaterial3D::set_alpha_antialiasing(AlphaAntiAliasing p_alpha_aa) {
+ if (alpha_antialiasing_mode == p_alpha_aa) {
+ return;
+ }
+
+ alpha_antialiasing_mode = p_alpha_aa;
+ _queue_shader_change();
+ notify_property_list_changed();
+}
+
+BaseMaterial3D::AlphaAntiAliasing BaseMaterial3D::get_alpha_antialiasing() const {
+ return alpha_antialiasing_mode;
+}
+
void BaseMaterial3D::set_shading_mode(ShadingMode p_shading_mode) {
if (shading_mode == p_shading_mode) {
return;
@@ -1431,7 +1563,7 @@ void BaseMaterial3D::set_shading_mode(ShadingMode p_shading_mode) {
shading_mode = p_shading_mode;
_queue_shader_change();
- _change_notify();
+ notify_property_list_changed();
}
BaseMaterial3D::ShadingMode BaseMaterial3D::get_shading_mode() const {
@@ -1498,8 +1630,11 @@ void BaseMaterial3D::set_flag(Flags p_flag, bool p_enabled) {
}
flags[p_flag] = p_enabled;
- if (p_flag == FLAG_USE_SHADOW_TO_OPACITY || p_flag == FLAG_USE_TEXTURE_REPEAT || p_flag == FLAG_SUBSURFACE_MODE_SKIN) {
- _change_notify();
+ if (p_flag == FLAG_USE_SHADOW_TO_OPACITY || p_flag == FLAG_USE_TEXTURE_REPEAT || p_flag == FLAG_SUBSURFACE_MODE_SKIN || p_flag == FLAG_USE_POINT_SIZE) {
+ notify_property_list_changed();
+ }
+ if (p_flag == FLAG_PARTICLE_TRAILS_MODE) {
+ update_configuration_warning();
}
_queue_shader_change();
}
@@ -1516,7 +1651,7 @@ void BaseMaterial3D::set_feature(Feature p_feature, bool p_enabled) {
}
features[p_feature] = p_enabled;
- _change_notify();
+ notify_property_list_changed();
_queue_shader_change();
}
@@ -1530,7 +1665,11 @@ void BaseMaterial3D::set_texture(TextureParam p_param, const Ref<Texture2D> &p_t
textures[p_param] = p_texture;
RID rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
RS::get_singleton()->material_set_param(_get_material(), shader_names->texture_names[p_param], rid);
- _change_notify();
+ if (p_texture.is_valid() && p_param == TEXTURE_ALBEDO) {
+ RS::get_singleton()->material_set_param(_get_material(), shader_names->albedo_texture_size,
+ Vector2i(p_texture->get_width(), p_texture->get_height()));
+ }
+ notify_property_list_changed();
_queue_shader_change();
}
@@ -1560,7 +1699,7 @@ BaseMaterial3D::TextureFilter BaseMaterial3D::get_texture_filter() const {
void BaseMaterial3D::_validate_feature(const String &text, Feature feature, PropertyInfo &property) const {
if (property.name.begins_with(text) && property.name != text + "_enabled" && !features[feature]) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NOEDITOR;
}
}
@@ -1590,44 +1729,77 @@ void BaseMaterial3D::_validate_property(PropertyInfo &property) const {
_validate_high_end("heightmap", property);
if (property.name.begins_with("particles_anim_") && billboard_mode != BILLBOARD_PARTICLES) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
- if (property.name == "params_grow_amount" && !grow_enabled) {
- property.usage = 0;
+ if (property.name == "billboard_keep_scale" && billboard_mode == BILLBOARD_DISABLED) {
+ property.usage = PROPERTY_USAGE_NOEDITOR;
+ }
+
+ if (property.name == "grow_amount" && !grow_enabled) {
+ property.usage = PROPERTY_USAGE_NOEDITOR;
+ }
+
+ if (property.name == "point_size" && !flags[FLAG_USE_POINT_SIZE]) {
+ property.usage = PROPERTY_USAGE_NOEDITOR;
}
if (property.name == "proximity_fade_distance" && !proximity_fade_enabled) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NOEDITOR;
}
if ((property.name == "distance_fade_max_distance" || property.name == "distance_fade_min_distance") && distance_fade == DISTANCE_FADE_DISABLED) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NOEDITOR;
}
+ // you can only enable anti-aliasing (in materials) on alpha scissor and alpha hash
+ const bool can_select_aa = (transparency == TRANSPARENCY_ALPHA_SCISSOR || transparency == TRANSPARENCY_ALPHA_HASH);
+ // alpha anti aliasiasing is only enabled when you can select aa
+ const bool alpha_aa_enabled = (alpha_antialiasing_mode != ALPHA_ANTIALIASING_OFF) && can_select_aa;
+
+ // alpha scissor slider isn't needed when alpha antialiasing is enabled
if (property.name == "alpha_scissor_threshold" && transparency != TRANSPARENCY_ALPHA_SCISSOR) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
+ }
+
+ // alpha hash scale slider is only needed if transparency is alpha hash
+ if (property.name == "alpha_hash_scale" && transparency != TRANSPARENCY_ALPHA_HASH) {
+ property.usage = PROPERTY_USAGE_NONE;
+ }
+
+ if (property.name == "alpha_antialiasing_mode" && !can_select_aa) {
+ property.usage = PROPERTY_USAGE_NONE;
+ }
+
+ // we can't choose an antialiasing mode if alpha isn't possible
+ if (property.name == "alpha_antialiasing_edge" && !alpha_aa_enabled) {
+ property.usage = PROPERTY_USAGE_NONE;
+ }
+
+ if (property.name == "blend_mode" && alpha_aa_enabled) {
+ property.usage = PROPERTY_USAGE_NONE;
}
if ((property.name == "heightmap_min_layers" || property.name == "heightmap_max_layers") && !deep_parallax) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
- if (flags[FLAG_SUBSURFACE_MODE_SKIN] && (property.name == "subsurf_scatter_transmittance_color" || property.name == "subsurf_scatter_transmittance_texture" || property.name == "subsurf_scatter_transmittance_curve")) {
- property.usage = 0;
+ if (flags[FLAG_SUBSURFACE_MODE_SKIN] && (property.name == "subsurf_scatter_transmittance_color" || property.name == "subsurf_scatter_transmittance_texture")) {
+ property.usage = PROPERTY_USAGE_NONE;
}
if (orm) {
if (property.name == "shading_mode") {
- property.hint_string = "Unshaded,PerPixel"; //vertex not supported in ORM mode, since no individual roughness.
+ // Vertex not supported in ORM mode, since no individual roughness.
+ property.hint_string = "Unshaded,Per-Pixel";
}
if (property.name.begins_with("roughness") || property.name.begins_with("metallic") || property.name.begins_with("ao_texture")) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
} else {
if (property.name == "orm_texture") {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
}
@@ -1635,47 +1807,47 @@ void BaseMaterial3D::_validate_property(PropertyInfo &property) const {
if (shading_mode != SHADING_MODE_PER_VERTEX) {
//these may still work per vertex
if (property.name.begins_with("ao")) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
if (property.name.begins_with("emission")) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
if (property.name.begins_with("metallic")) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
if (property.name.begins_with("rim")) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
if (property.name.begins_with("roughness")) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
if (property.name.begins_with("subsurf_scatter")) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
}
//these definitely only need per pixel
if (property.name.begins_with("anisotropy")) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
if (property.name.begins_with("clearcoat")) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
if (property.name.begins_with("normal")) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
if (property.name.begins_with("backlight")) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
if (property.name.begins_with("transmittance")) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
}
}
@@ -1746,7 +1918,7 @@ float BaseMaterial3D::get_uv2_triplanar_blend_sharpness() const {
void BaseMaterial3D::set_billboard_mode(BillboardMode p_mode) {
billboard_mode = p_mode;
_queue_shader_change();
- _change_notify();
+ notify_property_list_changed();
}
BaseMaterial3D::BillboardMode BaseMaterial3D::get_billboard_mode() const {
@@ -1783,7 +1955,7 @@ bool BaseMaterial3D::get_particles_anim_loop() const {
void BaseMaterial3D::set_heightmap_deep_parallax(bool p_enable) {
deep_parallax = p_enable;
_queue_shader_change();
- _change_notify();
+ notify_property_list_changed();
}
bool BaseMaterial3D::is_heightmap_deep_parallax_enabled() const {
@@ -1829,7 +2001,7 @@ bool BaseMaterial3D::get_heightmap_deep_parallax_flip_binormal() const {
void BaseMaterial3D::set_grow_enabled(bool p_enable) {
grow_enabled = p_enable;
_queue_shader_change();
- _change_notify();
+ notify_property_list_changed();
}
bool BaseMaterial3D::is_grow_enabled() const {
@@ -1845,6 +2017,24 @@ float BaseMaterial3D::get_alpha_scissor_threshold() const {
return alpha_scissor_threshold;
}
+void BaseMaterial3D::set_alpha_hash_scale(float p_scale) {
+ alpha_hash_scale = p_scale;
+ RS::get_singleton()->material_set_param(_get_material(), shader_names->alpha_hash_scale, p_scale);
+}
+
+float BaseMaterial3D::get_alpha_hash_scale() const {
+ return alpha_hash_scale;
+}
+
+void BaseMaterial3D::set_alpha_antialiasing_edge(float p_edge) {
+ alpha_antialiasing_edge = p_edge;
+ RS::get_singleton()->material_set_param(_get_material(), shader_names->alpha_antialiasing_edge, p_edge);
+}
+
+float BaseMaterial3D::get_alpha_antialiasing_edge() const {
+ return alpha_antialiasing_edge;
+}
+
void BaseMaterial3D::set_grow(float p_grow) {
grow = p_grow;
RS::get_singleton()->material_set_param(_get_material(), shader_names->grow, p_grow);
@@ -1906,7 +2096,7 @@ BaseMaterial3D::TextureChannel BaseMaterial3D::get_refraction_texture_channel()
return refraction_texture_channel;
}
-RID BaseMaterial3D::get_material_rid_for_2d(bool p_shaded, bool p_transparent, bool p_double_sided, bool p_cut_alpha, bool p_opaque_prepass, bool p_billboard, bool p_billboard_y) {
+Ref<Material> BaseMaterial3D::get_material_for_2d(bool p_shaded, bool p_transparent, bool p_double_sided, bool p_cut_alpha, bool p_opaque_prepass, bool p_billboard, bool p_billboard_y, RID *r_shader_rid) {
int version = 0;
if (p_shaded) {
version = 1;
@@ -1931,11 +2121,14 @@ RID BaseMaterial3D::get_material_rid_for_2d(bool p_shaded, bool p_transparent, b
}
if (materials_for_2d[version].is_valid()) {
- return materials_for_2d[version]->get_rid();
+ if (r_shader_rid) {
+ *r_shader_rid = materials_for_2d[version]->get_shader_rid();
+ }
+ return materials_for_2d[version];
}
Ref<StandardMaterial3D> material;
- material.instance();
+ material.instantiate();
material->set_shading_mode(p_shaded ? SHADING_MODE_PER_PIXEL : SHADING_MODE_UNSHADED);
material->set_transparency(p_transparent ? (p_opaque_prepass ? TRANSPARENCY_ALPHA_DEPTH_PRE_PASS : (p_cut_alpha ? TRANSPARENCY_ALPHA_SCISSOR : TRANSPARENCY_ALPHA)) : TRANSPARENCY_DISABLED);
@@ -1949,7 +2142,11 @@ RID BaseMaterial3D::get_material_rid_for_2d(bool p_shaded, bool p_transparent, b
materials_for_2d[version] = material;
- return materials_for_2d[version]->get_rid();
+ if (r_shader_rid) {
+ *r_shader_rid = materials_for_2d[version]->get_shader_rid();
+ }
+
+ return materials_for_2d[version];
}
void BaseMaterial3D::set_on_top_of_alpha() {
@@ -1961,7 +2158,7 @@ void BaseMaterial3D::set_on_top_of_alpha() {
void BaseMaterial3D::set_proximity_fade(bool p_enable) {
proximity_fade_enabled = p_enable;
_queue_shader_change();
- _change_notify();
+ notify_property_list_changed();
}
bool BaseMaterial3D::is_proximity_fade_enabled() const {
@@ -1980,7 +2177,7 @@ float BaseMaterial3D::get_proximity_fade_distance() const {
void BaseMaterial3D::set_distance_fade(DistanceFadeMode p_mode) {
distance_fade = p_mode;
_queue_shader_change();
- _change_notify();
+ notify_property_list_changed();
}
BaseMaterial3D::DistanceFadeMode BaseMaterial3D::get_distance_fade() const {
@@ -2018,6 +2215,8 @@ BaseMaterial3D::EmissionOperator BaseMaterial3D::get_emission_operator() const {
}
RID BaseMaterial3D::get_shader_rid() const {
+ MutexLock lock(material_mutex);
+ ((BaseMaterial3D *)this)->_update_shader();
ERR_FAIL_COND_V(!shader_map.has(current_key), RID());
return shader_map[current_key].shader;
}
@@ -2027,12 +2226,20 @@ Shader::Mode BaseMaterial3D::get_shader_mode() const {
}
void BaseMaterial3D::_bind_methods() {
+ static_assert(sizeof(MaterialKey) == 16, "MaterialKey should be 16 bytes");
+
ClassDB::bind_method(D_METHOD("set_albedo", "albedo"), &BaseMaterial3D::set_albedo);
ClassDB::bind_method(D_METHOD("get_albedo"), &BaseMaterial3D::get_albedo);
ClassDB::bind_method(D_METHOD("set_transparency", "transparency"), &BaseMaterial3D::set_transparency);
ClassDB::bind_method(D_METHOD("get_transparency"), &BaseMaterial3D::get_transparency);
+ ClassDB::bind_method(D_METHOD("set_alpha_antialiasing", "alpha_aa"), &BaseMaterial3D::set_alpha_antialiasing);
+ ClassDB::bind_method(D_METHOD("get_alpha_antialiasing"), &BaseMaterial3D::get_alpha_antialiasing);
+
+ ClassDB::bind_method(D_METHOD("set_alpha_antialiasing_edge", "edge"), &BaseMaterial3D::set_alpha_antialiasing_edge);
+ ClassDB::bind_method(D_METHOD("get_alpha_antialiasing_edge"), &BaseMaterial3D::get_alpha_antialiasing_edge);
+
ClassDB::bind_method(D_METHOD("set_shading_mode", "shading_mode"), &BaseMaterial3D::set_shading_mode);
ClassDB::bind_method(D_METHOD("get_shading_mode"), &BaseMaterial3D::get_shading_mode);
@@ -2081,9 +2288,6 @@ void BaseMaterial3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_transmittance_depth", "depth"), &BaseMaterial3D::set_transmittance_depth);
ClassDB::bind_method(D_METHOD("get_transmittance_depth"), &BaseMaterial3D::get_transmittance_depth);
- ClassDB::bind_method(D_METHOD("set_transmittance_curve", "curve"), &BaseMaterial3D::set_transmittance_curve);
- ClassDB::bind_method(D_METHOD("get_transmittance_curve"), &BaseMaterial3D::get_transmittance_curve);
-
ClassDB::bind_method(D_METHOD("set_transmittance_boost", "boost"), &BaseMaterial3D::set_transmittance_boost);
ClassDB::bind_method(D_METHOD("get_transmittance_boost"), &BaseMaterial3D::get_transmittance_boost);
@@ -2186,6 +2390,9 @@ void BaseMaterial3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_alpha_scissor_threshold", "threshold"), &BaseMaterial3D::set_alpha_scissor_threshold);
ClassDB::bind_method(D_METHOD("get_alpha_scissor_threshold"), &BaseMaterial3D::get_alpha_scissor_threshold);
+ ClassDB::bind_method(D_METHOD("set_alpha_hash_scale", "threshold"), &BaseMaterial3D::set_alpha_hash_scale);
+ ClassDB::bind_method(D_METHOD("get_alpha_hash_scale"), &BaseMaterial3D::get_alpha_hash_scale);
+
ClassDB::bind_method(D_METHOD("set_grow_enabled", "enable"), &BaseMaterial3D::set_grow_enabled);
ClassDB::bind_method(D_METHOD("is_grow_enabled"), &BaseMaterial3D::is_grow_enabled);
@@ -2217,16 +2424,19 @@ void BaseMaterial3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_distance_fade_min_distance"), &BaseMaterial3D::get_distance_fade_min_distance);
ADD_GROUP("Transparency", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "transparency", PROPERTY_HINT_ENUM, "Disabled,Alpha,AlphaScissor,DepthPrePass"), "set_transparency", "get_transparency");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "transparency", PROPERTY_HINT_ENUM, "Disabled,Alpha,Alpha Scissor,Alpha Hash,Depth Pre-Pass"), "set_transparency", "get_transparency");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_scissor_threshold", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_alpha_scissor_threshold", "get_alpha_scissor_threshold");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Mix,Add,Sub,Mul"), "set_blend_mode", "get_blend_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_hash_scale", PROPERTY_HINT_RANGE, "0,2,0.01"), "set_alpha_hash_scale", "get_alpha_hash_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "alpha_antialiasing_mode", PROPERTY_HINT_ENUM, "Disabled,Alpha Edge Blend,Alpha Edge Clip"), "set_alpha_antialiasing", "get_alpha_antialiasing");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_antialiasing_edge", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_alpha_antialiasing_edge", "get_alpha_antialiasing_edge");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Mix,Add,Subtract,Multiply"), "set_blend_mode", "get_blend_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mode", PROPERTY_HINT_ENUM, "Back,Front,Disabled"), "set_cull_mode", "get_cull_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "depth_draw_mode", PROPERTY_HINT_ENUM, "Opaque Only,Always,Never"), "set_depth_draw_mode", "get_depth_draw_mode");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "no_depth_test"), "set_flag", "get_flag", FLAG_DISABLE_DEPTH_TEST);
ADD_GROUP("Shading", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "shading_mode", PROPERTY_HINT_ENUM, "Unshaded,PerPixel,PerVertex"), "set_shading_mode", "get_shading_mode");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "diffuse_mode", PROPERTY_HINT_ENUM, "Burley,Lambert,Lambert Wrap,Oren Nayar,Toon"), "set_diffuse_mode", "get_diffuse_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "shading_mode", PROPERTY_HINT_ENUM, "Unshaded,Per-Pixel,Per-Vertex"), "set_shading_mode", "get_shading_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "diffuse_mode", PROPERTY_HINT_ENUM, "Burley,Lambert,Lambert Wrap,Toon"), "set_diffuse_mode", "get_diffuse_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "specular_mode", PROPERTY_HINT_ENUM, "SchlickGGX,Blinn,Phong,Toon,Disabled"), "set_specular_mode", "get_specular_mode");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "disable_ambient_light"), "set_flag", "get_flag", FLAG_DISABLE_AMBIENT_LIGHT);
@@ -2292,10 +2502,10 @@ void BaseMaterial3D::_bind_methods() {
ADD_GROUP("Height", "heightmap_");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "heightmap_enabled"), "set_feature", "get_feature", FEATURE_HEIGHT_MAPPING);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "heightmap_scale", PROPERTY_HINT_RANGE, "-16,16,0.01"), "set_heightmap_scale", "get_heightmap_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "heightmap_scale", PROPERTY_HINT_RANGE, "-16,16,0.001"), "set_heightmap_scale", "get_heightmap_scale");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "heightmap_deep_parallax"), "set_heightmap_deep_parallax", "is_heightmap_deep_parallax_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "heightmap_min_layers", PROPERTY_HINT_RANGE, "1,32,1"), "set_heightmap_deep_parallax_min_layers", "get_heightmap_deep_parallax_min_layers");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "heightmap_max_layers", PROPERTY_HINT_RANGE, "1,32,1"), "set_heightmap_deep_parallax_max_layers", "get_heightmap_deep_parallax_max_layers");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "heightmap_min_layers", PROPERTY_HINT_RANGE, "1,64,1"), "set_heightmap_deep_parallax_min_layers", "get_heightmap_deep_parallax_min_layers");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "heightmap_max_layers", PROPERTY_HINT_RANGE, "1,64,1"), "set_heightmap_deep_parallax_max_layers", "get_heightmap_deep_parallax_max_layers");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "heightmap_flip_tangent"), "set_heightmap_deep_parallax_flip_tangent", "get_heightmap_deep_parallax_flip_tangent");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "heightmap_flip_binormal"), "set_heightmap_deep_parallax_flip_binormal", "get_heightmap_deep_parallax_flip_binormal");
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "heightmap_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_HEIGHTMAP);
@@ -2312,7 +2522,6 @@ void BaseMaterial3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "subsurf_scatter_transmittance_color"), "set_transmittance_color", "get_transmittance_color");
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "subsurf_scatter_transmittance_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_SUBSURFACE_TRANSMITTANCE);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "subsurf_scatter_transmittance_depth", PROPERTY_HINT_RANGE, "0.001,8,0.001,or_greater"), "set_transmittance_depth", "get_transmittance_depth");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "subsurf_scatter_transmittance_curve", PROPERTY_HINT_EXP_EASING, "0.01,16,0.01"), "set_transmittance_curve", "get_transmittance_curve");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "subsurf_scatter_transmittance_boost", PROPERTY_HINT_RANGE, "0.00,1.0,0.01"), "set_transmittance_boost", "get_transmittance_boost");
ADD_GROUP("Back Lighting", "backlight_");
@@ -2329,7 +2538,7 @@ void BaseMaterial3D::_bind_methods() {
ADD_GROUP("Detail", "detail_");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "detail_enabled"), "set_feature", "get_feature", FEATURE_DETAIL);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "detail_mask", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_DETAIL_MASK);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "detail_blend_mode", PROPERTY_HINT_ENUM, "Mix,Add,Sub,Mul"), "set_detail_blend_mode", "get_detail_blend_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "detail_blend_mode", PROPERTY_HINT_ENUM, "Mix,Add,Subtract,Multiply"), "set_detail_blend_mode", "get_detail_blend_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "detail_uv_layer", PROPERTY_HINT_ENUM, "UV1,UV2"), "set_detail_uv", "get_detail_uv");
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "detail_albedo", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_DETAIL_ALBEDO);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "detail_normal", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_DETAIL_NORMAL);
@@ -2349,7 +2558,7 @@ void BaseMaterial3D::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "uv2_world_triplanar"), "set_flag", "get_flag", FLAG_UV2_USE_WORLD_TRIPLANAR);
ADD_GROUP("Sampling", "texture_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,MipmapNearest,MipmapLinear,MipmapNearestAniso,MipmapLinearAniso"), "set_texture_filter", "get_texture_filter");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Aniso.,Linear Mipmap Aniso."), "set_texture_filter", "get_texture_filter");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "texture_repeat"), "set_flag", "get_flag", FLAG_USE_TEXTURE_REPEAT);
ADD_GROUP("Shadows", "");
@@ -2372,6 +2581,7 @@ void BaseMaterial3D::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "fixed_size"), "set_flag", "get_flag", FLAG_FIXED_SIZE);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "use_point_size"), "set_flag", "get_flag", FLAG_USE_POINT_SIZE);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "point_size", PROPERTY_HINT_RANGE, "0.1,128,0.1"), "set_point_size", "get_point_size");
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "use_particle_trails"), "set_flag", "get_flag", FLAG_PARTICLE_TRAILS_MODE);
ADD_GROUP("Proximity Fade", "proximity_fade_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "proximity_fade_enable"), "set_proximity_fade", "is_proximity_fade_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "proximity_fade_distance", PROPERTY_HINT_RANGE, "0,4096,0.01"), "set_proximity_fade_distance", "get_proximity_fade_distance");
@@ -2414,6 +2624,7 @@ void BaseMaterial3D::_bind_methods() {
BIND_ENUM_CONSTANT(TRANSPARENCY_DISABLED);
BIND_ENUM_CONSTANT(TRANSPARENCY_ALPHA);
BIND_ENUM_CONSTANT(TRANSPARENCY_ALPHA_SCISSOR);
+ BIND_ENUM_CONSTANT(TRANSPARENCY_ALPHA_HASH);
BIND_ENUM_CONSTANT(TRANSPARENCY_ALPHA_DEPTH_PRE_PASS);
BIND_ENUM_CONSTANT(TRANSPARENCY_MAX);
@@ -2441,6 +2652,10 @@ void BaseMaterial3D::_bind_methods() {
BIND_ENUM_CONSTANT(BLEND_MODE_SUB);
BIND_ENUM_CONSTANT(BLEND_MODE_MUL);
+ BIND_ENUM_CONSTANT(ALPHA_ANTIALIASING_OFF);
+ BIND_ENUM_CONSTANT(ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE);
+ BIND_ENUM_CONSTANT(ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE);
+
BIND_ENUM_CONSTANT(DEPTH_DRAW_OPAQUE_ONLY);
BIND_ENUM_CONSTANT(DEPTH_DRAW_ALWAYS);
BIND_ENUM_CONSTANT(DEPTH_DRAW_DISABLED);
@@ -2468,12 +2683,12 @@ void BaseMaterial3D::_bind_methods() {
BIND_ENUM_CONSTANT(FLAG_USE_TEXTURE_REPEAT);
BIND_ENUM_CONSTANT(FLAG_INVERT_HEIGHTMAP);
BIND_ENUM_CONSTANT(FLAG_SUBSURFACE_MODE_SKIN);
+ BIND_ENUM_CONSTANT(FLAG_PARTICLE_TRAILS_MODE);
BIND_ENUM_CONSTANT(FLAG_MAX);
BIND_ENUM_CONSTANT(DIFFUSE_BURLEY);
BIND_ENUM_CONSTANT(DIFFUSE_LAMBERT);
BIND_ENUM_CONSTANT(DIFFUSE_LAMBERT_WRAP);
- BIND_ENUM_CONSTANT(DIFFUSE_OREN_NAYAR);
BIND_ENUM_CONSTANT(DIFFUSE_TOON);
BIND_ENUM_CONSTANT(SPECULAR_SCHLICK_GGX);
@@ -2506,8 +2721,6 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) :
element(this) {
orm = p_orm;
// Initialize to the same values as the shader
- transparency = TRANSPARENCY_DISABLED;
- shading_mode = SHADING_MODE_PER_PIXEL;
set_albedo(Color(1.0, 1.0, 1.0, 1.0));
set_specular(0.5);
set_roughness(1.0);
@@ -2525,7 +2738,6 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) :
set_backlight(Color(0, 0, 0));
set_transmittance_color(Color(1, 1, 1, 1));
set_transmittance_depth(0.1);
- set_transmittance_curve(1.0);
set_transmittance_boost(0.0);
set_refraction(0.05);
set_point_size(1);
@@ -2539,11 +2751,13 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) :
set_particles_anim_h_frames(1);
set_particles_anim_v_frames(1);
set_particles_anim_loop(false);
- set_alpha_scissor_threshold(0.98);
- emission_op = EMISSION_OP_ADD;
- proximity_fade_enabled = false;
- distance_fade = DISTANCE_FADE_DISABLED;
+ set_transparency(TRANSPARENCY_DISABLED);
+ set_alpha_antialiasing(ALPHA_ANTIALIASING_OFF);
+ set_alpha_scissor_threshold(0.05);
+ set_alpha_hash_scale(1.0);
+ set_alpha_antialiasing_edge(0.3);
+
set_proximity_fade_distance(1);
set_distance_fade_min_distance(0);
set_distance_fade_max_distance(10);
@@ -2555,37 +2769,14 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) :
set_ao_texture_channel(TEXTURE_CHANNEL_RED);
set_refraction_texture_channel(TEXTURE_CHANNEL_RED);
- grow_enabled = false;
set_grow(0.0);
- deep_parallax = false;
- heightmap_parallax_flip_tangent = false;
- heightmap_parallax_flip_binormal = false;
set_heightmap_deep_parallax_min_layers(8);
set_heightmap_deep_parallax_max_layers(32);
set_heightmap_deep_parallax_flip_tangent(false); //also sets binormal
- detail_uv = DETAIL_UV_1;
- blend_mode = BLEND_MODE_MIX;
- detail_blend_mode = BLEND_MODE_MIX;
- depth_draw_mode = DEPTH_DRAW_OPAQUE_ONLY;
- cull_mode = CULL_BACK;
- for (int i = 0; i < FLAG_MAX; i++) {
- flags[i] = false;
- }
flags[FLAG_USE_TEXTURE_REPEAT] = true;
- diffuse_mode = DIFFUSE_BURLEY;
- specular_mode = SPECULAR_SCHLICK_GGX;
-
- for (int i = 0; i < FEATURE_MAX; i++) {
- features[i] = false;
- }
-
- current_key.key0 = 0;
- current_key.key1 = 0;
- current_key.invalid_key = 1;
- texture_filter = TEXTURE_FILTER_LINEAR_WITH_MIPMAPS;
_queue_shader_change();
}
@@ -2633,6 +2824,12 @@ bool StandardMaterial3D::_set(const StringName &p_name, const Variant &p_value)
set_transparency(TRANSPARENCY_ALPHA_SCISSOR);
}
return true;
+ } else if (p_name == "params_use_alpha_hash") {
+ bool use_hash = p_value;
+ if (use_hash) {
+ set_transparency(TRANSPARENCY_ALPHA_HASH);
+ }
+ return true;
} else if (p_name == "params_depth_draw_mode") {
int mode = p_value;
if (mode == 3) {
@@ -2667,6 +2864,8 @@ bool StandardMaterial3D::_set(const StringName &p_name, const Variant &p_value)
{ "params_grow", "grow" },
{ "params_grow_amount", "grow_amount" },
{ "params_alpha_scissor_threshold", "alpha_scissor_threshold" },
+ { "params_alpha_hash_scale", "alpha_hash_scale" },
+ { "params_alpha_antialiasing_edge", "alpha_antialiasing_edge" },
{ "depth_scale", "heightmap_scale" },
{ "depth_deep_parallax", "heightmap_deep_parallax" },
diff --git a/scene/resources/material.h b/scene/resources/material.h
index b5bdd77eb5..e2838e1399 100644
--- a/scene/resources/material.h
+++ b/scene/resources/material.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,8 +31,8 @@
#ifndef MATERIAL_H
#define MATERIAL_H
-#include "core/resource.h"
-#include "core/self_list.h"
+#include "core/io/resource.h"
+#include "core/templates/self_list.h"
#include "scene/resources/shader.h"
#include "scene/resources/texture.h"
#include "servers/rendering/shader_language.h"
@@ -47,6 +47,8 @@ class Material : public Resource {
Ref<Material> next_pass;
int render_priority;
+ void inspect_native_shader_code();
+
protected:
_FORCE_INLINE_ RID _get_material() const { return material; }
static void _bind_methods();
@@ -66,6 +68,7 @@ public:
int get_render_priority() const;
virtual RID get_rid() const override;
+ virtual RID get_shader_rid() const = 0;
virtual Shader::Mode get_shader_mode() const = 0;
Material();
@@ -76,6 +79,8 @@ class ShaderMaterial : public Material {
GDCLASS(ShaderMaterial, Material);
Ref<Shader> shader;
+ Map<StringName, Variant> param_cache;
+
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
@@ -100,6 +105,8 @@ public:
virtual Shader::Mode get_shader_mode() const override;
+ virtual RID get_shader_rid() const override;
+
ShaderMaterial();
~ShaderMaterial();
};
@@ -145,17 +152,26 @@ public:
enum DetailUV {
DETAIL_UV_1,
- DETAIL_UV_2
+ DETAIL_UV_2,
+ DETAIL_UV_MAX
};
enum Transparency {
TRANSPARENCY_DISABLED,
TRANSPARENCY_ALPHA,
TRANSPARENCY_ALPHA_SCISSOR,
+ TRANSPARENCY_ALPHA_HASH,
TRANSPARENCY_ALPHA_DEPTH_PRE_PASS,
TRANSPARENCY_MAX,
};
+ enum AlphaAntiAliasing {
+ ALPHA_ANTIALIASING_OFF,
+ ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE,
+ ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE,
+ ALPHA_ANTIALIASING_MAX
+ };
+
enum ShadingMode {
SHADING_MODE_UNSHADED,
SHADING_MODE_PER_PIXEL,
@@ -184,18 +200,21 @@ public:
BLEND_MODE_ADD,
BLEND_MODE_SUB,
BLEND_MODE_MUL,
+ BLEND_MODE_MAX
};
enum DepthDrawMode {
DEPTH_DRAW_OPAQUE_ONLY,
DEPTH_DRAW_ALWAYS,
DEPTH_DRAW_DISABLED,
+ DEPTH_DRAW_MAX
};
enum CullMode {
CULL_BACK,
CULL_FRONT,
- CULL_DISABLED
+ CULL_DISABLED,
+ CULL_MAX
};
enum Flags {
@@ -218,6 +237,7 @@ public:
FLAG_USE_TEXTURE_REPEAT,
FLAG_INVERT_HEIGHTMAP,
FLAG_SUBSURFACE_MODE_SKIN,
+ FLAG_PARTICLE_TRAILS_MODE,
FLAG_MAX
};
@@ -225,8 +245,8 @@ public:
DIFFUSE_BURLEY,
DIFFUSE_LAMBERT,
DIFFUSE_LAMBERT_WRAP,
- DIFFUSE_OREN_NAYAR,
DIFFUSE_TOON,
+ DIFFUSE_MAX
};
enum SpecularMode {
@@ -235,6 +255,7 @@ public:
SPECULAR_PHONG,
SPECULAR_TOON,
SPECULAR_DISABLED,
+ SPECULAR_MAX
};
enum BillboardMode {
@@ -242,6 +263,7 @@ public:
BILLBOARD_ENABLED,
BILLBOARD_FIXED_Y,
BILLBOARD_PARTICLES,
+ BILLBOARD_MAX
};
enum TextureChannel {
@@ -249,12 +271,14 @@ public:
TEXTURE_CHANNEL_GREEN,
TEXTURE_CHANNEL_BLUE,
TEXTURE_CHANNEL_ALPHA,
- TEXTURE_CHANNEL_GRAYSCALE
+ TEXTURE_CHANNEL_GRAYSCALE,
+ TEXTURE_CHANNEL_MAX
};
enum EmissionOperator {
EMISSION_OP_ADD,
- EMISSION_OP_MULTIPLY
+ EMISSION_OP_MULTIPLY,
+ EMISSION_OP_MAX
};
enum DistanceFadeMode {
@@ -262,49 +286,52 @@ public:
DISTANCE_FADE_PIXEL_ALPHA,
DISTANCE_FADE_PIXEL_DITHER,
DISTANCE_FADE_OBJECT_DITHER,
+ DISTANCE_FADE_MAX
};
private:
- union MaterialKey {
- struct {
- uint64_t feature_mask : FEATURE_MAX;
- uint64_t detail_uv : 1;
- uint64_t blend_mode : 2;
- uint64_t depth_draw_mode : 2;
- uint64_t cull_mode : 2;
- uint64_t flags : FLAG_MAX;
- uint64_t detail_blend_mode : 2;
- uint64_t diffuse_mode : 3;
- uint64_t specular_mode : 3;
- uint64_t invalid_key : 1;
- uint64_t deep_parallax : 1;
- uint64_t billboard_mode : 2;
- uint64_t grow : 1;
- uint64_t proximity_fade : 1;
- uint64_t distance_fade : 2;
- uint64_t emission_op : 1;
- uint64_t texture_filter : 3;
- uint64_t transparency : 2;
- uint64_t shading_mode : 2;
- uint64_t roughness_channel : 3;
- };
-
- struct {
- uint64_t key0;
- uint64_t key1;
- };
+ struct MaterialKey {
+ // enum values
+ uint64_t texture_filter : get_num_bits(TEXTURE_FILTER_MAX - 1);
+ uint64_t detail_uv : get_num_bits(DETAIL_UV_MAX - 1);
+ uint64_t transparency : get_num_bits(TRANSPARENCY_MAX - 1);
+ uint64_t alpha_antialiasing_mode : get_num_bits(ALPHA_ANTIALIASING_MAX - 1);
+ uint64_t shading_mode : get_num_bits(SHADING_MODE_MAX - 1);
+ uint64_t blend_mode : get_num_bits(BLEND_MODE_MAX - 1);
+ uint64_t depth_draw_mode : get_num_bits(DEPTH_DRAW_MAX - 1);
+ uint64_t cull_mode : get_num_bits(CULL_MAX - 1);
+ uint64_t diffuse_mode : get_num_bits(DIFFUSE_MAX - 1);
+ uint64_t specular_mode : get_num_bits(SPECULAR_MAX - 1);
+ uint64_t billboard_mode : get_num_bits(BILLBOARD_MAX - 1);
+ uint64_t detail_blend_mode : get_num_bits(BLEND_MODE_MAX - 1);
+ uint64_t roughness_channel : get_num_bits(TEXTURE_CHANNEL_MAX - 1);
+ uint64_t emission_op : get_num_bits(EMISSION_OP_MAX - 1);
+ uint64_t distance_fade : get_num_bits(DISTANCE_FADE_MAX - 1);
+ // booleans
+ uint64_t deep_parallax : 1;
+ uint64_t grow : 1;
+ uint64_t proximity_fade : 1;
+
+ // flag bitfield
+ uint32_t feature_mask;
+ uint32_t flags;
+
+ MaterialKey() {
+ memset(this, 0, sizeof(MaterialKey));
+ }
bool operator==(const MaterialKey &p_key) const {
- return (key0 == p_key.key0) && (key1 == p_key.key1);
+ return memcmp(this, &p_key, sizeof(MaterialKey)) == 0;
}
+
bool operator<(const MaterialKey &p_key) const {
- return (key0 == p_key.key0) ? (key1 < p_key.key1) : (key0 < p_key.key0);
+ return memcmp(this, &p_key, sizeof(MaterialKey)) < 0;
}
};
struct ShaderData {
RID shader;
- int users;
+ int users = 0;
};
static Map<MaterialKey, ShaderData> shader_map;
@@ -313,13 +340,7 @@ private:
_FORCE_INLINE_ MaterialKey _compute_key() const {
MaterialKey mk;
- mk.key0 = 0;
- mk.key1 = 0;
- for (int i = 0; i < FEATURE_MAX; i++) {
- if (features[i]) {
- mk.feature_mask |= ((uint64_t)1 << i);
- }
- }
+
mk.detail_uv = detail_uv;
mk.blend_mode = blend_mode;
mk.depth_draw_mode = depth_draw_mode;
@@ -328,20 +349,28 @@ private:
mk.transparency = transparency;
mk.shading_mode = shading_mode;
mk.roughness_channel = roughness_texture_channel;
- for (int i = 0; i < FLAG_MAX; i++) {
- if (flags[i]) {
- mk.flags |= ((uint64_t)1 << i);
- }
- }
mk.detail_blend_mode = detail_blend_mode;
mk.diffuse_mode = diffuse_mode;
mk.specular_mode = specular_mode;
mk.billboard_mode = billboard_mode;
- mk.deep_parallax = deep_parallax ? 1 : 0;
+ mk.deep_parallax = deep_parallax;
mk.grow = grow_enabled;
mk.proximity_fade = proximity_fade_enabled;
mk.distance_fade = distance_fade;
mk.emission_op = emission_op;
+ mk.alpha_antialiasing_mode = alpha_antialiasing_mode;
+
+ for (int i = 0; i < FEATURE_MAX; i++) {
+ if (features[i]) {
+ mk.feature_mask |= ((uint64_t)1 << i);
+ }
+ }
+
+ for (int i = 0; i < FLAG_MAX; i++) {
+ if (flags[i]) {
+ mk.flags |= ((uint64_t)1 << i);
+ }
+ }
return mk;
}
@@ -362,7 +391,6 @@ private:
StringName heightmap_scale;
StringName subsurface_scattering_strength;
StringName transmittance_color;
- StringName transmittance_curve;
StringName transmittance_depth;
StringName transmittance_boost;
StringName backlight;
@@ -392,9 +420,14 @@ private:
StringName rim_texture_channel;
StringName heightmap_texture_channel;
StringName refraction_texture_channel;
- StringName alpha_scissor_threshold;
StringName texture_names[TEXTURE_MAX];
+
+ StringName alpha_scissor_threshold;
+ StringName alpha_hash_scale;
+
+ StringName alpha_antialiasing_edge;
+ StringName albedo_texture_size;
};
static Mutex material_mutex;
@@ -426,23 +459,24 @@ private:
float transmittance_amount;
Color transmittance_color;
float transmittance_depth;
- float transmittance_curve;
float transmittance_boost;
Color backlight;
float refraction;
float point_size;
float alpha_scissor_threshold;
- bool grow_enabled;
+ float alpha_hash_scale;
+ float alpha_antialiasing_edge;
+ bool grow_enabled = false;
float ao_light_affect;
float grow;
int particles_anim_h_frames;
int particles_anim_v_frames;
bool particles_anim_loop;
- Transparency transparency;
- ShadingMode shading_mode;
+ Transparency transparency = TRANSPARENCY_DISABLED;
+ ShadingMode shading_mode = SHADING_MODE_PER_PIXEL;
- TextureFilter texture_filter;
+ TextureFilter texture_filter = TEXTURE_FILTER_LINEAR_WITH_MIPMAPS;
Vector3 uv1_scale;
Vector3 uv1_offset;
@@ -452,37 +486,39 @@ private:
Vector3 uv2_offset;
float uv2_triplanar_sharpness;
- DetailUV detail_uv;
+ DetailUV detail_uv = DETAIL_UV_1;
- bool deep_parallax;
+ bool deep_parallax = false;
int deep_parallax_min_layers;
int deep_parallax_max_layers;
- bool heightmap_parallax_flip_tangent;
- bool heightmap_parallax_flip_binormal;
+ bool heightmap_parallax_flip_tangent = false;
+ bool heightmap_parallax_flip_binormal = false;
- bool proximity_fade_enabled;
+ bool proximity_fade_enabled = false;
float proximity_fade_distance;
- DistanceFadeMode distance_fade;
+ DistanceFadeMode distance_fade = DISTANCE_FADE_DISABLED;
float distance_fade_max_distance;
float distance_fade_min_distance;
- BlendMode blend_mode;
- BlendMode detail_blend_mode;
- DepthDrawMode depth_draw_mode;
- CullMode cull_mode;
- bool flags[FLAG_MAX];
- SpecularMode specular_mode;
- DiffuseMode diffuse_mode;
+ BlendMode blend_mode = BLEND_MODE_MIX;
+ BlendMode detail_blend_mode = BLEND_MODE_MIX;
+ DepthDrawMode depth_draw_mode = DEPTH_DRAW_OPAQUE_ONLY;
+ CullMode cull_mode = CULL_BACK;
+ bool flags[FLAG_MAX] = {};
+ SpecularMode specular_mode = SPECULAR_SCHLICK_GGX;
+ DiffuseMode diffuse_mode = DIFFUSE_BURLEY;
BillboardMode billboard_mode;
- EmissionOperator emission_op;
+ EmissionOperator emission_op = EMISSION_OP_ADD;
TextureChannel metallic_texture_channel;
TextureChannel roughness_texture_channel;
TextureChannel ao_texture_channel;
TextureChannel refraction_texture_channel;
- bool features[FEATURE_MAX];
+ AlphaAntiAliasing alpha_antialiasing_mode = ALPHA_ANTIALIASING_OFF;
+
+ bool features[FEATURE_MAX] = {};
Ref<Texture2D> textures[TEXTURE_MAX];
@@ -566,9 +602,6 @@ public:
void set_transmittance_depth(float p_depth);
float get_transmittance_depth() const;
- void set_transmittance_curve(float p_curve);
- float get_transmittance_curve() const;
-
void set_transmittance_boost(float p_boost);
float get_transmittance_boost() const;
@@ -584,6 +617,12 @@ public:
void set_transparency(Transparency p_transparency);
Transparency get_transparency() const;
+ void set_alpha_antialiasing(AlphaAntiAliasing p_alpha_aa);
+ AlphaAntiAliasing get_alpha_antialiasing() const;
+
+ void set_alpha_antialiasing_edge(float p_edge);
+ float get_alpha_antialiasing_edge() const;
+
void set_shading_mode(ShadingMode p_shading_mode);
ShadingMode get_shading_mode() const;
@@ -660,6 +699,9 @@ public:
void set_alpha_scissor_threshold(float p_threshold);
float get_alpha_scissor_threshold() const;
+ void set_alpha_hash_scale(float p_scale);
+ float get_alpha_hash_scale() const;
+
void set_on_top_of_alpha();
void set_proximity_fade(bool p_enable);
@@ -693,9 +735,9 @@ public:
static void finish_shaders();
static void flush_changes();
- static RID get_material_rid_for_2d(bool p_shaded, bool p_transparent, bool p_double_sided, bool p_cut_alpha, bool p_opaque_prepass, bool p_billboard = false, bool p_billboard_y = false);
+ static Ref<Material> get_material_for_2d(bool p_shaded, bool p_transparent, bool p_double_sided, bool p_cut_alpha, bool p_opaque_prepass, bool p_billboard = false, bool p_billboard_y = false, RID *r_shader_rid = nullptr);
- RID get_shader_rid() const;
+ virtual RID get_shader_rid() const override;
virtual Shader::Mode get_shader_mode() const override;
@@ -707,6 +749,7 @@ VARIANT_ENUM_CAST(BaseMaterial3D::TextureParam)
VARIANT_ENUM_CAST(BaseMaterial3D::TextureFilter)
VARIANT_ENUM_CAST(BaseMaterial3D::ShadingMode)
VARIANT_ENUM_CAST(BaseMaterial3D::Transparency)
+VARIANT_ENUM_CAST(BaseMaterial3D::AlphaAntiAliasing)
VARIANT_ENUM_CAST(BaseMaterial3D::DetailUV)
VARIANT_ENUM_CAST(BaseMaterial3D::Feature)
VARIANT_ENUM_CAST(BaseMaterial3D::BlendMode)
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index 10f0a040d0..ad589a605e 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,7 +30,8 @@
#include "mesh.h"
-#include "core/pair.h"
+#include "core/math/convex_hull.h"
+#include "core/templates/pair.h"
#include "scene/resources/concave_polygon_shape_3d.h"
#include "scene/resources/convex_polygon_shape_3d.h"
#include "surface_tool.h"
@@ -74,7 +75,7 @@ Ref<TriangleMesh> Mesh::generate_triangle_mesh() const {
}
Array a = surface_get_arrays(i);
- ERR_FAIL_COND_V(a.empty(), Ref<TriangleMesh>());
+ ERR_FAIL_COND_V(a.is_empty(), Ref<TriangleMesh>());
int vc = surface_get_array_len(i);
Vector<Vector3> vertices = a[ARRAY_VERTEX];
@@ -157,7 +158,7 @@ void Mesh::generate_debug_mesh_indices(Vector<Vector3> &r_points) {
bool Mesh::surface_is_softbody_friendly(int p_idx) const {
const uint32_t surface_format = surface_get_format(p_idx);
- return (surface_format & Mesh::ARRAY_FLAG_USE_DYNAMIC_UPDATE && (!(surface_format & Mesh::ARRAY_COMPRESS_NORMAL)));
+ return (surface_format & Mesh::ARRAY_FLAG_USE_DYNAMIC_UPDATE);
}
Vector<Face3> Mesh::get_faces() const {
@@ -168,7 +169,6 @@ Vector<Face3> Mesh::get_faces() const {
return Vector<Face3>();
/*
for (int i=0;i<surfaces.size();i++) {
-
if (RenderingServer::get_singleton()->mesh_surface_get_primitive_type( mesh, i ) != RenderingServer::PRIMITIVE_TRIANGLES )
continue;
@@ -181,12 +181,10 @@ Vector<Face3> Mesh::get_faces() const {
bool has_indices;
if (len>0) {
-
indices=RenderingServer::get_singleton()->mesh_surface_get_array(mesh, i,RenderingServer::ARRAY_INDEX);
has_indices=true;
} else {
-
len=vertices.size();
has_indices=false;
}
@@ -210,11 +208,9 @@ Vector<Face3> Mesh::get_faces() const {
for (int i=0;i<len/3;i++) {
-
Face3 face;
for (int j=0;j<3;j++) {
-
int idx=i*3+j;
face.vertex[j] = has_indices ? verticesptr[ indicesptr[ idx ] ] : verticesptr[idx];
}
@@ -226,17 +222,37 @@ Vector<Face3> Mesh::get_faces() const {
*/
}
-Ref<Shape3D> Mesh::create_convex_shape() const {
- Vector<Vector3> vertices;
+Ref<Shape3D> Mesh::create_convex_shape(bool p_clean, bool p_simplify) const {
+ if (p_simplify) {
+ Vector<Ref<Shape3D>> decomposed = convex_decompose(1);
+ if (decomposed.size() == 1) {
+ return decomposed[0];
+ } else {
+ ERR_PRINT("Convex shape simplification failed, falling back to simpler process.");
+ }
+ }
+ Vector<Vector3> vertices;
for (int i = 0; i < get_surface_count(); i++) {
Array a = surface_get_arrays(i);
- ERR_FAIL_COND_V(a.empty(), Ref<ConvexPolygonShape3D>());
+ ERR_FAIL_COND_V(a.is_empty(), Ref<ConvexPolygonShape3D>());
Vector<Vector3> v = a[ARRAY_VERTEX];
vertices.append_array(v);
}
Ref<ConvexPolygonShape3D> shape = memnew(ConvexPolygonShape3D);
+
+ if (p_clean) {
+ Geometry3D::MeshData md;
+ Error err = ConvexHullComputer::convex_hull(vertices, md);
+ if (err == OK) {
+ shape->set_points(md.vertices);
+ return shape;
+ } else {
+ ERR_PRINT("Convex shape cleaning failed, falling back to simpler process.");
+ }
+ }
+
shape->set_points(vertices);
return shape;
}
@@ -271,7 +287,7 @@ Ref<Mesh> Mesh::create_outline(float p_margin) const {
}
Array a = surface_get_arrays(i);
- ERR_FAIL_COND_V(a.empty(), Ref<ArrayMesh>());
+ ERR_FAIL_COND_V(a.is_empty(), Ref<ArrayMesh>());
if (i == 0) {
arrays = a;
@@ -485,8 +501,30 @@ void Mesh::_bind_methods() {
BIND_ENUM_CONSTANT(PRIMITIVE_TRIANGLES);
BIND_ENUM_CONSTANT(PRIMITIVE_TRIANGLE_STRIP);
- BIND_ENUM_CONSTANT(BLEND_SHAPE_MODE_NORMALIZED);
- BIND_ENUM_CONSTANT(BLEND_SHAPE_MODE_RELATIVE);
+ BIND_ENUM_CONSTANT(ARRAY_VERTEX);
+ BIND_ENUM_CONSTANT(ARRAY_NORMAL);
+ BIND_ENUM_CONSTANT(ARRAY_TANGENT);
+ BIND_ENUM_CONSTANT(ARRAY_COLOR);
+ BIND_ENUM_CONSTANT(ARRAY_TEX_UV);
+ BIND_ENUM_CONSTANT(ARRAY_TEX_UV2);
+ BIND_ENUM_CONSTANT(ARRAY_CUSTOM0);
+ BIND_ENUM_CONSTANT(ARRAY_CUSTOM1);
+ BIND_ENUM_CONSTANT(ARRAY_CUSTOM2);
+ BIND_ENUM_CONSTANT(ARRAY_CUSTOM3);
+ BIND_ENUM_CONSTANT(ARRAY_BONES);
+ BIND_ENUM_CONSTANT(ARRAY_WEIGHTS);
+ BIND_ENUM_CONSTANT(ARRAY_INDEX);
+ BIND_ENUM_CONSTANT(ARRAY_MAX);
+
+ BIND_ENUM_CONSTANT(ARRAY_CUSTOM_RGBA8_UNORM);
+ BIND_ENUM_CONSTANT(ARRAY_CUSTOM_RGBA8_SNORM);
+ BIND_ENUM_CONSTANT(ARRAY_CUSTOM_RG_HALF);
+ BIND_ENUM_CONSTANT(ARRAY_CUSTOM_RGBA_HALF);
+ BIND_ENUM_CONSTANT(ARRAY_CUSTOM_R_FLOAT);
+ BIND_ENUM_CONSTANT(ARRAY_CUSTOM_RG_FLOAT);
+ BIND_ENUM_CONSTANT(ARRAY_CUSTOM_RGB_FLOAT);
+ BIND_ENUM_CONSTANT(ARRAY_CUSTOM_RGBA_FLOAT);
+ BIND_ENUM_CONSTANT(ARRAY_CUSTOM_MAX);
BIND_ENUM_CONSTANT(ARRAY_FORMAT_VERTEX);
BIND_ENUM_CONSTANT(ARRAY_FORMAT_NORMAL);
@@ -494,31 +532,31 @@ void Mesh::_bind_methods() {
BIND_ENUM_CONSTANT(ARRAY_FORMAT_COLOR);
BIND_ENUM_CONSTANT(ARRAY_FORMAT_TEX_UV);
BIND_ENUM_CONSTANT(ARRAY_FORMAT_TEX_UV2);
+ BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM0);
+ BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM1);
+ BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM2);
+ BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM3);
BIND_ENUM_CONSTANT(ARRAY_FORMAT_BONES);
BIND_ENUM_CONSTANT(ARRAY_FORMAT_WEIGHTS);
BIND_ENUM_CONSTANT(ARRAY_FORMAT_INDEX);
- BIND_ENUM_CONSTANT(ARRAY_COMPRESS_NORMAL);
- BIND_ENUM_CONSTANT(ARRAY_COMPRESS_TANGENT);
- BIND_ENUM_CONSTANT(ARRAY_COMPRESS_COLOR);
- BIND_ENUM_CONSTANT(ARRAY_COMPRESS_TEX_UV);
- BIND_ENUM_CONSTANT(ARRAY_COMPRESS_TEX_UV2);
- BIND_ENUM_CONSTANT(ARRAY_COMPRESS_INDEX);
+ BIND_ENUM_CONSTANT(ARRAY_FORMAT_BLEND_SHAPE_MASK);
- BIND_ENUM_CONSTANT(ARRAY_FLAG_USE_2D_VERTICES);
+ BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM_BASE);
+ BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM0_SHIFT);
+ BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM1_SHIFT);
+ BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM2_SHIFT);
+ BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM3_SHIFT);
- BIND_ENUM_CONSTANT(ARRAY_COMPRESS_DEFAULT);
+ BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM_MASK);
+ BIND_ENUM_CONSTANT(ARRAY_COMPRESS_FLAGS_BASE);
- BIND_ENUM_CONSTANT(ARRAY_VERTEX);
- BIND_ENUM_CONSTANT(ARRAY_NORMAL);
- BIND_ENUM_CONSTANT(ARRAY_TANGENT);
- BIND_ENUM_CONSTANT(ARRAY_COLOR);
- BIND_ENUM_CONSTANT(ARRAY_TEX_UV);
- BIND_ENUM_CONSTANT(ARRAY_TEX_UV2);
- BIND_ENUM_CONSTANT(ARRAY_BONES);
- BIND_ENUM_CONSTANT(ARRAY_WEIGHTS);
- BIND_ENUM_CONSTANT(ARRAY_INDEX);
- BIND_ENUM_CONSTANT(ARRAY_MAX);
+ BIND_ENUM_CONSTANT(ARRAY_FLAG_USE_2D_VERTICES);
+ BIND_ENUM_CONSTANT(ARRAY_FLAG_USE_DYNAMIC_UPDATE);
+ BIND_ENUM_CONSTANT(ARRAY_FLAG_USE_8_BONE_WEIGHTS);
+
+ BIND_ENUM_CONSTANT(BLEND_SHAPE_MODE_NORMALIZED);
+ BIND_ENUM_CONSTANT(BLEND_SHAPE_MODE_RELATIVE);
}
void Mesh::clear_cache() const {
@@ -526,12 +564,12 @@ void Mesh::clear_cache() const {
debug_lines.clear();
}
-Vector<Ref<Shape3D>> Mesh::convex_decompose() const {
+Vector<Ref<Shape3D>> Mesh::convex_decompose(int p_max_convex_hulls) const {
ERR_FAIL_COND_V(!convex_composition_function, Vector<Ref<Shape3D>>());
const Vector<Face3> faces = get_faces();
- Vector<Vector<Face3>> decomposed = convex_composition_function(faces);
+ Vector<Vector<Face3>> decomposed = convex_composition_function(faces, p_max_convex_hulls);
Vector<Ref<Shape3D>> ret;
@@ -554,7 +592,7 @@ Vector<Ref<Shape3D>> Mesh::convex_decompose() const {
}
Ref<ConvexPolygonShape3D> shape;
- shape.instance();
+ shape.instantiate();
shape->set_points(convex_points);
ret.push_back(shape);
}
@@ -562,139 +600,337 @@ Vector<Ref<Shape3D>> Mesh::convex_decompose() const {
return ret;
}
+int Mesh::get_builtin_bind_pose_count() const {
+ return 0;
+}
+
+Transform3D Mesh::get_builtin_bind_pose(int p_index) const {
+ return Transform3D();
+}
+
Mesh::Mesh() {
}
-static Vector<uint8_t> _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_format, uint32_t p_elements) {
- bool vertex_16bit = p_format & ((1 << (Mesh::ARRAY_VERTEX + Mesh::ARRAY_COMPRESS_BASE)));
- bool has_bones = (p_format & Mesh::ARRAY_FORMAT_BONES);
- bool bone_8 = has_bones && !(p_format & (Mesh::ARRAY_COMPRESS_INDEX << 2));
- bool weight_32 = has_bones && !(p_format & (Mesh::ARRAY_COMPRESS_TEX_UV2 << 2));
+enum OldArrayType {
+ OLD_ARRAY_VERTEX,
+ OLD_ARRAY_NORMAL,
+ OLD_ARRAY_TANGENT,
+ OLD_ARRAY_COLOR,
+ OLD_ARRAY_TEX_UV,
+ OLD_ARRAY_TEX_UV2,
+ OLD_ARRAY_BONES,
+ OLD_ARRAY_WEIGHTS,
+ OLD_ARRAY_INDEX,
+ OLD_ARRAY_MAX,
+};
- print_line("convert vertex16: " + itos(vertex_16bit) + " convert bone 8 " + itos(bone_8) + " convert weight 32 " + itos(weight_32));
+enum OldArrayFormat {
+ /* OLD_ARRAY FORMAT FLAGS */
+ OLD_ARRAY_FORMAT_VERTEX = 1 << OLD_ARRAY_VERTEX, // mandatory
+ OLD_ARRAY_FORMAT_NORMAL = 1 << OLD_ARRAY_NORMAL,
+ OLD_ARRAY_FORMAT_TANGENT = 1 << OLD_ARRAY_TANGENT,
+ OLD_ARRAY_FORMAT_COLOR = 1 << OLD_ARRAY_COLOR,
+ OLD_ARRAY_FORMAT_TEX_UV = 1 << OLD_ARRAY_TEX_UV,
+ OLD_ARRAY_FORMAT_TEX_UV2 = 1 << OLD_ARRAY_TEX_UV2,
+ OLD_ARRAY_FORMAT_BONES = 1 << OLD_ARRAY_BONES,
+ OLD_ARRAY_FORMAT_WEIGHTS = 1 << OLD_ARRAY_WEIGHTS,
+ OLD_ARRAY_FORMAT_INDEX = 1 << OLD_ARRAY_INDEX,
+
+ OLD_ARRAY_COMPRESS_BASE = (OLD_ARRAY_INDEX + 1),
+ OLD_ARRAY_COMPRESS_VERTEX = 1 << (OLD_ARRAY_VERTEX + OLD_ARRAY_COMPRESS_BASE), // mandatory
+ OLD_ARRAY_COMPRESS_NORMAL = 1 << (OLD_ARRAY_NORMAL + OLD_ARRAY_COMPRESS_BASE),
+ OLD_ARRAY_COMPRESS_TANGENT = 1 << (OLD_ARRAY_TANGENT + OLD_ARRAY_COMPRESS_BASE),
+ OLD_ARRAY_COMPRESS_COLOR = 1 << (OLD_ARRAY_COLOR + OLD_ARRAY_COMPRESS_BASE),
+ OLD_ARRAY_COMPRESS_TEX_UV = 1 << (OLD_ARRAY_TEX_UV + OLD_ARRAY_COMPRESS_BASE),
+ OLD_ARRAY_COMPRESS_TEX_UV2 = 1 << (OLD_ARRAY_TEX_UV2 + OLD_ARRAY_COMPRESS_BASE),
+ OLD_ARRAY_COMPRESS_BONES = 1 << (OLD_ARRAY_BONES + OLD_ARRAY_COMPRESS_BASE),
+ OLD_ARRAY_COMPRESS_WEIGHTS = 1 << (OLD_ARRAY_WEIGHTS + OLD_ARRAY_COMPRESS_BASE),
+ OLD_ARRAY_COMPRESS_INDEX = 1 << (OLD_ARRAY_INDEX + OLD_ARRAY_COMPRESS_BASE),
+
+ OLD_ARRAY_FLAG_USE_2D_VERTICES = OLD_ARRAY_COMPRESS_INDEX << 1,
+ OLD_ARRAY_FLAG_USE_16_BIT_BONES = OLD_ARRAY_COMPRESS_INDEX << 2,
+ OLD_ARRAY_FLAG_USE_DYNAMIC_UPDATE = OLD_ARRAY_COMPRESS_INDEX << 3,
- if (!vertex_16bit && !bone_8 && !weight_32) {
- return p_src;
- }
+};
- bool vertex_2d = (p_format & (Mesh::ARRAY_COMPRESS_INDEX << 1));
+#ifndef DISABLE_DEPRECATED
+static Array _convert_old_array(const Array &p_old) {
+ Array new_array;
+ new_array.resize(Mesh::ARRAY_MAX);
+ new_array[Mesh::ARRAY_VERTEX] = p_old[OLD_ARRAY_VERTEX];
+ new_array[Mesh::ARRAY_NORMAL] = p_old[OLD_ARRAY_NORMAL];
+ new_array[Mesh::ARRAY_TANGENT] = p_old[OLD_ARRAY_TANGENT];
+ new_array[Mesh::ARRAY_COLOR] = p_old[OLD_ARRAY_COLOR];
+ new_array[Mesh::ARRAY_TEX_UV] = p_old[OLD_ARRAY_TEX_UV];
+ new_array[Mesh::ARRAY_TEX_UV2] = p_old[OLD_ARRAY_TEX_UV2];
+ new_array[Mesh::ARRAY_BONES] = p_old[OLD_ARRAY_BONES];
+ new_array[Mesh::ARRAY_WEIGHTS] = p_old[OLD_ARRAY_WEIGHTS];
+ new_array[Mesh::ARRAY_INDEX] = p_old[OLD_ARRAY_INDEX];
+ return new_array;
+}
- uint32_t src_stride = p_src.size() / p_elements;
- uint32_t dst_stride = src_stride + (vertex_16bit ? 4 : 0) + (bone_8 ? 4 : 0) - (weight_32 ? 8 : 0);
+static Mesh::PrimitiveType _old_primitives[7] = {
+ Mesh::PRIMITIVE_POINTS,
+ Mesh::PRIMITIVE_LINES,
+ Mesh::PRIMITIVE_LINE_STRIP,
+ Mesh::PRIMITIVE_LINES,
+ Mesh::PRIMITIVE_TRIANGLES,
+ Mesh::PRIMITIVE_TRIANGLE_STRIP,
+ Mesh::PRIMITIVE_TRIANGLE_STRIP
+};
+#endif // DISABLE_DEPRECATED
- Vector<uint8_t> ret = p_src;
+void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_format, uint32_t p_new_format, uint32_t p_elements, Vector<uint8_t> &vertex_data, Vector<uint8_t> &attribute_data, Vector<uint8_t> &skin_data) {
+ uint32_t dst_vertex_stride;
+ uint32_t dst_attribute_stride;
+ uint32_t dst_skin_stride;
+ uint32_t dst_offsets[Mesh::ARRAY_MAX];
+ RenderingServer::get_singleton()->mesh_surface_make_offsets_from_format(p_new_format & (~RS::ARRAY_FORMAT_INDEX), p_elements, 0, dst_offsets, dst_vertex_stride, dst_attribute_stride, dst_skin_stride);
- ret.resize(dst_stride * p_elements);
- {
- uint8_t *w = ret.ptrw();
- const uint8_t *r = p_src.ptr();
-
- for (uint32_t i = 0; i < p_elements; i++) {
- uint32_t remaining = src_stride;
- const uint8_t *src = (const uint8_t *)(r + src_stride * i);
- uint8_t *dst = (uint8_t *)(w + dst_stride * i);
-
- if (!vertex_2d) { //3D
- if (vertex_16bit) {
- float *dstw = (float *)dst;
- const uint16_t *srcr = (const uint16_t *)src;
- dstw[0] = Math::half_to_float(srcr[0]);
- dstw[1] = Math::half_to_float(srcr[1]);
- dstw[2] = Math::half_to_float(srcr[2]);
- remaining -= 8;
- src += 8;
+ vertex_data.resize(dst_vertex_stride * p_elements);
+ attribute_data.resize(dst_attribute_stride * p_elements);
+ skin_data.resize(dst_skin_stride * p_elements);
+
+ uint8_t *dst_vertex_ptr = vertex_data.ptrw();
+ uint8_t *dst_attribute_ptr = attribute_data.ptrw();
+ uint8_t *dst_skin_ptr = skin_data.ptrw();
+
+ const uint8_t *src_vertex_ptr = p_src.ptr();
+ uint32_t src_vertex_stride = p_src.size() / p_elements;
+
+ uint32_t src_offset = 0;
+ for (uint32_t j = 0; j < OLD_ARRAY_INDEX; j++) {
+ if (!(p_old_format & (1 << j))) {
+ continue;
+ }
+ switch (j) {
+ case OLD_ARRAY_VERTEX: {
+ if (p_old_format & OLD_ARRAY_FLAG_USE_2D_VERTICES) {
+ if (p_old_format & OLD_ARRAY_COMPRESS_VERTEX) {
+ for (uint32_t i = 0; i < p_elements; i++) {
+ const uint16_t *src = (const uint16_t *)&src_vertex_ptr[i * src_vertex_stride];
+ float *dst = (float *)&dst_vertex_ptr[i * dst_vertex_stride];
+ dst[0] = Math::half_to_float(src[0]);
+ dst[1] = Math::half_to_float(src[1]);
+ }
+ src_offset += sizeof(uint16_t) * 2;
+ } else {
+ for (uint32_t i = 0; i < p_elements; i++) {
+ const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride];
+ float *dst = (float *)&dst_vertex_ptr[i * dst_vertex_stride];
+ dst[0] = src[0];
+ dst[1] = src[1];
+ }
+ src_offset += sizeof(float) * 2;
+ }
} else {
- src += 12;
- remaining -= 12;
+ if (p_old_format & OLD_ARRAY_COMPRESS_VERTEX) {
+ for (uint32_t i = 0; i < p_elements; i++) {
+ const uint16_t *src = (const uint16_t *)&src_vertex_ptr[i * src_vertex_stride];
+ float *dst = (float *)&dst_vertex_ptr[i * dst_vertex_stride];
+ dst[0] = Math::half_to_float(src[0]);
+ dst[1] = Math::half_to_float(src[1]);
+ dst[2] = Math::half_to_float(src[2]);
+ }
+ src_offset += sizeof(uint16_t) * 4; //+pad
+ } else {
+ for (uint32_t i = 0; i < p_elements; i++) {
+ const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride];
+ float *dst = (float *)&dst_vertex_ptr[i * dst_vertex_stride];
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[2];
+ }
+ src_offset += sizeof(float) * 3;
+ }
}
- dst += 12;
- } else {
- if (vertex_16bit) {
- float *dstw = (float *)dst;
- const uint16_t *srcr = (const uint16_t *)src;
- dstw[0] = Math::half_to_float(srcr[0]);
- dstw[1] = Math::half_to_float(srcr[1]);
- remaining -= 4;
- src += 4;
+ } break;
+ case OLD_ARRAY_NORMAL: {
+ if (p_old_format & OLD_ARRAY_COMPRESS_NORMAL) {
+ const float multiplier = 1.f / 127.f * 1023.0f;
+
+ for (uint32_t i = 0; i < p_elements; i++) {
+ const int8_t *src = (const int8_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
+ uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_NORMAL]];
+
+ *dst = 0;
+ *dst |= CLAMP(int(src[0] * multiplier), 0, 1023);
+ *dst |= CLAMP(int(src[1] * multiplier), 0, 1023) << 10;
+ *dst |= CLAMP(int(src[2] * multiplier), 0, 1023) << 20;
+ }
+ src_offset += sizeof(uint32_t);
} else {
- src += 8;
- remaining -= 8;
+ for (uint32_t i = 0; i < p_elements; i++) {
+ const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
+ uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_NORMAL]];
+
+ *dst = 0;
+ *dst |= CLAMP(int(src[0] * 1023.0), 0, 1023);
+ *dst |= CLAMP(int(src[1] * 1023.0), 0, 1023) << 10;
+ *dst |= CLAMP(int(src[2] * 1023.0), 0, 1023) << 20;
+ }
+ src_offset += sizeof(float) * 3;
}
- dst += 8;
- }
-
- if (has_bones) {
- remaining -= bone_8 ? 4 : 8;
- remaining -= weight_32 ? 16 : 8;
- }
- for (uint32_t j = 0; j < remaining; j++) {
- dst[j] = src[j];
- }
-
- if (has_bones) {
- dst += remaining;
- src += remaining;
-
- if (bone_8) {
- const uint8_t *src_bones = (const uint8_t *)src;
- uint16_t *dst_bones = (uint16_t *)dst;
+ } break;
+ case OLD_ARRAY_TANGENT: {
+ if (p_old_format & OLD_ARRAY_COMPRESS_TANGENT) {
+ const float multiplier = 1.f / 127.f * 1023.0f;
+
+ for (uint32_t i = 0; i < p_elements; i++) {
+ const int8_t *src = (const int8_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
+ uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_TANGENT]];
+
+ *dst = 0;
+ *dst |= CLAMP(int(src[0] * multiplier), 0, 1023);
+ *dst |= CLAMP(int(src[1] * multiplier), 0, 1023) << 10;
+ *dst |= CLAMP(int(src[2] * multiplier), 0, 1023) << 20;
+ if (src[3] > 0) {
+ *dst |= 3 << 30;
+ }
+ }
+ src_offset += sizeof(uint32_t);
+ } else {
+ for (uint32_t i = 0; i < p_elements; i++) {
+ const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
+ uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_TANGENT]];
+
+ *dst = 0;
+ *dst |= CLAMP(int(src[0] * 1023.0), 0, 1023);
+ *dst |= CLAMP(int(src[1] * 1023.0), 0, 1023) << 10;
+ *dst |= CLAMP(int(src[2] * 1023.0), 0, 1023) << 20;
+ if (src[3] > 0) {
+ *dst |= 3 << 30;
+ }
+ }
+ src_offset += sizeof(float) * 4;
+ }
- dst_bones[0] = src_bones[0];
- dst_bones[1] = src_bones[1];
- dst_bones[2] = src_bones[2];
- dst_bones[3] = src_bones[3];
+ } break;
+ case OLD_ARRAY_COLOR: {
+ if (p_old_format & OLD_ARRAY_COMPRESS_COLOR) {
+ for (uint32_t i = 0; i < p_elements; i++) {
+ const uint32_t *src = (const uint32_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
+ uint32_t *dst = (uint32_t *)&dst_attribute_ptr[i * dst_attribute_stride + dst_offsets[Mesh::ARRAY_COLOR]];
- src += 4;
+ *dst = *src;
+ }
+ src_offset += sizeof(uint32_t);
} else {
- for (uint32_t j = 0; j < 8; j++) {
- dst[j] = src[j];
+ for (uint32_t i = 0; i < p_elements; i++) {
+ const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
+ uint8_t *dst = (uint8_t *)&dst_attribute_ptr[i * dst_attribute_stride + dst_offsets[Mesh::ARRAY_COLOR]];
+
+ dst[0] = uint8_t(CLAMP(src[0] * 255.0, 0.0, 255.0));
+ dst[1] = uint8_t(CLAMP(src[1] * 255.0, 0.0, 255.0));
+ dst[2] = uint8_t(CLAMP(src[2] * 255.0, 0.0, 255.0));
+ dst[3] = uint8_t(CLAMP(src[3] * 255.0, 0.0, 255.0));
}
-
- src += 8;
+ src_offset += sizeof(float) * 4;
}
+ } break;
+ case OLD_ARRAY_TEX_UV: {
+ if (p_old_format & OLD_ARRAY_COMPRESS_TEX_UV) {
+ for (uint32_t i = 0; i < p_elements; i++) {
+ const uint16_t *src = (const uint16_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
+ float *dst = (float *)&dst_attribute_ptr[i * dst_attribute_stride + dst_offsets[Mesh::ARRAY_TEX_UV]];
+
+ dst[0] = Math::half_to_float(src[0]);
+ dst[1] = Math::half_to_float(src[1]);
+ }
+ src_offset += sizeof(uint16_t) * 2;
+ } else {
+ for (uint32_t i = 0; i < p_elements; i++) {
+ const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
+ float *dst = (float *)&dst_attribute_ptr[i * dst_attribute_stride + dst_offsets[Mesh::ARRAY_TEX_UV]];
- dst += 8;
+ dst[0] = src[0];
+ dst[1] = src[1];
+ }
+ src_offset += sizeof(float) * 2;
+ }
- if (weight_32) {
- const float *src_weights = (const float *)src;
- uint16_t *dst_weights = (uint16_t *)dst;
+ } break;
+ case OLD_ARRAY_TEX_UV2: {
+ if (p_old_format & OLD_ARRAY_COMPRESS_TEX_UV2) {
+ for (uint32_t i = 0; i < p_elements; i++) {
+ const uint16_t *src = (const uint16_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
+ float *dst = (float *)&dst_attribute_ptr[i * dst_attribute_stride + dst_offsets[Mesh::ARRAY_TEX_UV2]];
- dst_weights[0] = CLAMP(src_weights[0] * 65535, 0, 65535); //16bits unorm
- dst_weights[1] = CLAMP(src_weights[1] * 65535, 0, 65535);
- dst_weights[2] = CLAMP(src_weights[2] * 65535, 0, 65535);
- dst_weights[3] = CLAMP(src_weights[3] * 65535, 0, 65535);
+ dst[0] = Math::half_to_float(src[0]);
+ dst[1] = Math::half_to_float(src[1]);
+ }
+ src_offset += sizeof(uint16_t) * 2;
+ } else {
+ for (uint32_t i = 0; i < p_elements; i++) {
+ const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
+ float *dst = (float *)&dst_attribute_ptr[i * dst_attribute_stride + dst_offsets[Mesh::ARRAY_TEX_UV2]];
+ dst[0] = src[0];
+ dst[1] = src[1];
+ }
+ src_offset += sizeof(float) * 2;
+ }
+ } break;
+ case OLD_ARRAY_BONES: {
+ if (p_old_format & OLD_ARRAY_FLAG_USE_16_BIT_BONES) {
+ for (uint32_t i = 0; i < p_elements; i++) {
+ const uint16_t *src = (const uint16_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
+ uint16_t *dst = (uint16_t *)&dst_skin_ptr[i * dst_skin_stride + dst_offsets[Mesh::ARRAY_BONES]];
+
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[2];
+ dst[3] = src[3];
+ }
+ src_offset += sizeof(uint16_t) * 4;
} else {
- for (uint32_t j = 0; j < 8; j++) {
- dst[j] = src[j];
+ for (uint32_t i = 0; i < p_elements; i++) {
+ const uint8_t *src = (const uint8_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
+ uint16_t *dst = (uint16_t *)&dst_skin_ptr[i * dst_skin_stride + dst_offsets[Mesh::ARRAY_BONES]];
+
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[2];
+ dst[3] = src[3];
}
+ src_offset += sizeof(uint8_t) * 4;
}
+ } break;
+ case OLD_ARRAY_WEIGHTS: {
+ if (p_old_format & OLD_ARRAY_COMPRESS_WEIGHTS) {
+ for (uint32_t i = 0; i < p_elements; i++) {
+ const uint16_t *src = (const uint16_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
+ uint16_t *dst = (uint16_t *)&dst_skin_ptr[i * dst_skin_stride + dst_offsets[Mesh::ARRAY_WEIGHTS]];
+
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[2];
+ dst[3] = src[3];
+ }
+ src_offset += sizeof(uint16_t) * 4;
+ } else {
+ for (uint32_t i = 0; i < p_elements; i++) {
+ const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
+ uint16_t *dst = (uint16_t *)&dst_skin_ptr[i * dst_skin_stride + dst_offsets[Mesh::ARRAY_WEIGHTS]];
+
+ dst[0] = uint16_t(CLAMP(src[0] * 65535.0, 0, 65535.0));
+ dst[1] = uint16_t(CLAMP(src[1] * 65535.0, 0, 65535.0));
+ dst[2] = uint16_t(CLAMP(src[2] * 65535.0, 0, 65535.0));
+ dst[3] = uint16_t(CLAMP(src[3] * 65535.0, 0, 65535.0));
+ }
+ src_offset += sizeof(float) * 4;
+ }
+ } break;
+ default: {
}
}
}
-
- return ret;
}
bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) {
String sname = p_name;
- if (p_name == "blend_shape/names") {
- Vector<String> sk = p_value;
- int sz = sk.size();
- const String *r = sk.ptr();
- for (int i = 0; i < sz; i++) {
- add_blend_shape(r[i]);
- }
- return true;
- }
-
- if (p_name == "blend_shape/mode") {
- set_blend_shape_mode(BlendShapeMode(int(p_value)));
- return true;
- }
-
if (sname.begins_with("surface_")) {
int sl = sname.find("/");
if (sl == -1) {
@@ -729,7 +965,11 @@ bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) {
if (d.has("arrays")) {
//oldest format (2.x)
ERR_FAIL_COND_V(!d.has("morph_arrays"), false);
- add_surface_from_arrays(PrimitiveType(int(d["primitive"])), d["arrays"], d["morph_arrays"]);
+ Array morph_arrays = d["morph_arrays"];
+ for (int i = 0; i < morph_arrays.size(); i++) {
+ morph_arrays[i] = _convert_old_array(morph_arrays[i]);
+ }
+ add_surface_from_arrays(_old_primitives[int(d["primitive"])], _convert_old_array(d["arrays"]), morph_arrays);
} else if (d.has("array_data")) {
//print_line("array data (old style");
@@ -741,48 +981,76 @@ bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) {
}
ERR_FAIL_COND_V(!d.has("format"), false);
- uint32_t format = d["format"];
+ uint32_t old_format = d["format"];
uint32_t primitive = d["primitive"];
- uint32_t primitive_remap[7] = {
- PRIMITIVE_POINTS,
- PRIMITIVE_LINES,
- PRIMITIVE_LINE_STRIP,
- PRIMITIVE_LINES,
- PRIMITIVE_TRIANGLES,
- PRIMITIVE_TRIANGLE_STRIP,
- PRIMITIVE_TRIANGLE_STRIP
- };
-
- primitive = primitive_remap[primitive]; //compatibility
+ primitive = _old_primitives[primitive]; //compatibility
ERR_FAIL_COND_V(!d.has("vertex_count"), false);
int vertex_count = d["vertex_count"];
- array_data = _fix_array_compatibility(array_data, format, vertex_count);
+ uint32_t new_format = ARRAY_FORMAT_VERTEX;
+
+ if (old_format & OLD_ARRAY_FORMAT_NORMAL) {
+ new_format |= ARRAY_FORMAT_NORMAL;
+ }
+ if (old_format & OLD_ARRAY_FORMAT_TANGENT) {
+ new_format |= ARRAY_FORMAT_TANGENT;
+ }
+ if (old_format & OLD_ARRAY_FORMAT_COLOR) {
+ new_format |= ARRAY_FORMAT_COLOR;
+ }
+ if (old_format & OLD_ARRAY_FORMAT_TEX_UV) {
+ new_format |= ARRAY_FORMAT_TEX_UV;
+ }
+ if (old_format & OLD_ARRAY_FORMAT_TEX_UV2) {
+ new_format |= ARRAY_FORMAT_TEX_UV2;
+ }
+ if (old_format & OLD_ARRAY_FORMAT_BONES) {
+ new_format |= ARRAY_FORMAT_BONES;
+ }
+ if (old_format & OLD_ARRAY_FORMAT_WEIGHTS) {
+ new_format |= ARRAY_FORMAT_WEIGHTS;
+ }
+ if (old_format & OLD_ARRAY_FORMAT_INDEX) {
+ new_format |= ARRAY_FORMAT_INDEX;
+ }
+ if (old_format & OLD_ARRAY_FLAG_USE_2D_VERTICES) {
+ new_format |= OLD_ARRAY_FLAG_USE_2D_VERTICES;
+ }
+
+ Vector<uint8_t> vertex_array;
+ Vector<uint8_t> attribute_array;
+ Vector<uint8_t> skin_array;
+
+ _fix_array_compatibility(array_data, old_format, new_format, vertex_count, vertex_array, attribute_array, skin_array);
int index_count = 0;
if (d.has("index_count")) {
index_count = d["index_count"];
}
- Vector<Vector<uint8_t>> blend_shapes;
+ Vector<uint8_t> blend_shapes;
if (d.has("blend_shape_data")) {
Array blend_shape_data = d["blend_shape_data"];
for (int i = 0; i < blend_shape_data.size(); i++) {
+ Vector<uint8_t> blend_vertex_array;
+ Vector<uint8_t> blend_attribute_array;
+ Vector<uint8_t> blend_skin_array;
+
Vector<uint8_t> shape = blend_shape_data[i];
- shape = _fix_array_compatibility(shape, format, vertex_count);
+ _fix_array_compatibility(shape, old_format, new_format, vertex_count, blend_vertex_array, blend_attribute_array, blend_skin_array);
- blend_shapes.push_back(shape);
+ blend_shapes.append_array(blend_vertex_array);
}
}
//clear unused flags
- print_line("format pre: " + itos(format));
- format &= ~uint32_t((1 << (ARRAY_VERTEX + ARRAY_COMPRESS_BASE)) | (ARRAY_COMPRESS_INDEX << 2) | (ARRAY_COMPRESS_TEX_UV2 << 2));
- print_line("format post: " + itos(format));
+ print_line("format pre: " + itos(old_format));
+
+ print_line("format post: " + itos(new_format));
ERR_FAIL_COND_V(!d.has("aabb"), false);
AABB aabb = d["aabb"];
@@ -797,7 +1065,8 @@ bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) {
}
}
- add_surface(format, PrimitiveType(primitive), array_data, vertex_count, array_index_data, index_count, aabb, blend_shapes, bone_aabb);
+ add_surface(new_format, PrimitiveType(primitive), vertex_array, attribute_array, skin_array, vertex_count, array_index_data, index_count, aabb, blend_shapes, bone_aabb);
+
} else {
ERR_FAIL_V(false);
}
@@ -816,6 +1085,28 @@ bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) {
return false;
}
+void ArrayMesh::_set_blend_shape_names(const PackedStringArray &p_names) {
+ ERR_FAIL_COND(surfaces.size() > 0);
+
+ blend_shapes.resize(p_names.size());
+ for (int i = 0; i < p_names.size(); i++) {
+ blend_shapes.write[i] = p_names[i];
+ }
+
+ if (mesh.is_valid()) {
+ RS::get_singleton()->mesh_set_blend_shape_count(mesh, blend_shapes.size());
+ }
+}
+
+PackedStringArray ArrayMesh::_get_blend_shape_names() const {
+ PackedStringArray sarr;
+ sarr.resize(blend_shapes.size());
+ for (int i = 0; i < blend_shapes.size(); i++) {
+ sarr.write[i] = blend_shapes[i];
+ }
+ return sarr;
+}
+
Array ArrayMesh::_get_surfaces() const {
if (mesh.is_null()) {
return Array();
@@ -829,6 +1120,12 @@ Array ArrayMesh::_get_surfaces() const {
data["primitive"] = surface.primitive;
data["vertex_data"] = surface.vertex_data;
data["vertex_count"] = surface.vertex_count;
+ if (surface.skin_data.size()) {
+ data["skin_data"] = surface.skin_data;
+ }
+ if (surface.attribute_data.size()) {
+ data["attribute_data"] = surface.attribute_data;
+ }
data["aabb"] = surface.aabb;
if (surface.index_count) {
data["index_data"] = surface.index_data;
@@ -853,9 +1150,8 @@ Array ArrayMesh::_get_surfaces() const {
data["bone_aabbs"] = bone_aabbs;
}
- Array blend_shapes;
- for (int j = 0; j < surface.blend_shapes.size(); j++) {
- blend_shapes.push_back(surface.blend_shapes[j]);
+ if (surface.blend_shape_data.size()) {
+ data["blend_shapes"] = surface.blend_shape_data;
}
if (surfaces[i].material.is_valid()) {
@@ -872,7 +1168,6 @@ Array ArrayMesh::_get_surfaces() const {
ret.push_back(data);
}
- print_line("Saving surfaces: " + itos(ret.size()));
return ret;
}
@@ -881,6 +1176,7 @@ void ArrayMesh::_create_if_empty() const {
if (!mesh.is_valid()) {
mesh = RS::get_singleton()->mesh_create();
RS::get_singleton()->mesh_set_blend_shape_mode(mesh, (RS::BlendShapeMode)blend_shape_mode);
+ RS::get_singleton()->mesh_set_blend_shape_count(mesh, blend_shapes.size());
}
}
@@ -902,6 +1198,12 @@ void ArrayMesh::_set_surfaces(const Array &p_surfaces) {
surface.primitive = RS::PrimitiveType(int(d["primitive"]));
surface.vertex_data = d["vertex_data"];
surface.vertex_count = d["vertex_count"];
+ if (d.has("attribute_data")) {
+ surface.attribute_data = d["attribute_data"];
+ }
+ if (d.has("skin_data")) {
+ surface.skin_data = d["skin_data"];
+ }
surface.aabb = d["aabb"];
if (d.has("index_data")) {
@@ -929,10 +1231,7 @@ void ArrayMesh::_set_surfaces(const Array &p_surfaces) {
}
if (d.has("blend_shapes")) {
- Array blend_shapes;
- for (int j = 0; j < blend_shapes.size(); j++) {
- surface.blend_shapes.push_back(blend_shapes[j]);
- }
+ surface.blend_shape_data = d["blend_shapes"];
}
Ref<Material> material;
@@ -952,15 +1251,7 @@ void ArrayMesh::_set_surfaces(const Array &p_surfaces) {
if (d.has("2d")) {
_2d = d["2d"];
}
- /*
- print_line("format: " + itos(surface.format));
- print_line("aabb: " + surface.aabb);
- print_line("array size: " + itos(surface.vertex_data.size()));
- print_line("vertex count: " + itos(surface.vertex_count));
- print_line("index size: " + itos(surface.index_data.size()));
- print_line("index count: " + itos(surface.index_count));
- print_line("primitive: " + itos(surface.primitive));
-*/
+
surface_data.push_back(surface);
surface_materials.push_back(material);
surface_names.push_back(name);
@@ -976,7 +1267,7 @@ void ArrayMesh::_set_surfaces(const Array &p_surfaces) {
} else {
// if mesh does not exist (first time this is loaded, most likely),
// we can create it with a single call, which is a lot more efficient and thread friendly
- mesh = RS::get_singleton()->mesh_create_from_surfaces(surface_data);
+ mesh = RS::get_singleton()->mesh_create_from_surfaces(surface_data, blend_shapes.size());
RS::get_singleton()->mesh_set_blend_shape_mode(mesh, (RS::BlendShapeMode)blend_shape_mode);
}
@@ -988,7 +1279,6 @@ void ArrayMesh::_set_surfaces(const Array &p_surfaces) {
s.aabb = surface_data[i].aabb;
if (i == 0) {
aabb = s.aabb;
- blend_shapes.resize(surface_data[i].blend_shapes.size());
} else {
aabb.merge_with(s.aabb);
}
@@ -1012,18 +1302,7 @@ bool ArrayMesh::_get(const StringName &p_name, Variant &r_ret) const {
}
String sname = p_name;
-
- if (p_name == "blend_shape/names") {
- Vector<String> sk;
- for (int i = 0; i < blend_shapes.size(); i++) {
- sk.push_back(blend_shapes[i]);
- }
- r_ret = sk;
- return true;
- } else if (p_name == "blend_shape/mode") {
- r_ret = get_blend_shape_mode();
- return true;
- } else if (sname.begins_with("surface_")) {
+ if (sname.begins_with("surface_")) {
int sl = sname.find("/");
if (sl == -1) {
return false;
@@ -1041,22 +1320,26 @@ bool ArrayMesh::_get(const StringName &p_name, Variant &r_ret) const {
return true;
}
+void ArrayMesh::reset_state() {
+ clear_surfaces();
+ clear_blend_shapes();
+
+ aabb = AABB();
+ blend_shape_mode = BLEND_SHAPE_MODE_RELATIVE;
+ custom_aabb = AABB();
+}
+
void ArrayMesh::_get_property_list(List<PropertyInfo> *p_list) const {
if (_is_generated()) {
return;
}
- if (blend_shapes.size()) {
- p_list->push_back(PropertyInfo(Variant::PACKED_STRING_ARRAY, "blend_shape/names", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::INT, "blend_shape/mode", PROPERTY_HINT_ENUM, "Normalized,Relative"));
- }
-
for (int i = 0; i < surfaces.size(); i++) {
p_list->push_back(PropertyInfo(Variant::STRING, "surface_" + itos(i + 1) + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
if (surfaces[i].is_2d) {
- p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_" + itos(i + 1) + "/material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,CanvasItemMaterial", PROPERTY_USAGE_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_" + itos(i + 1) + "/material", PROPERTY_HINT_RESOURCE_TYPE, "CanvasItemMaterial,ShaderMaterial", PROPERTY_USAGE_EDITOR));
} else {
- p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_" + itos(i + 1) + "/material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,StandardMaterial3D", PROPERTY_USAGE_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_" + itos(i + 1) + "/material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial", PROPERTY_USAGE_EDITOR));
}
}
}
@@ -1076,7 +1359,7 @@ void ArrayMesh::_recompute_aabb() {
#ifndef _MSC_VER
#warning need to add binding to add_surface using future MeshSurfaceData object
#endif
-void ArrayMesh::add_surface(uint32_t p_format, PrimitiveType p_primitive, const Vector<uint8_t> &p_array, int p_vertex_count, const Vector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<Vector<uint8_t>> &p_blend_shapes, const Vector<AABB> &p_bone_aabb, const Vector<RS::SurfaceData::LOD> &p_lods) {
+void ArrayMesh::add_surface(uint32_t p_format, PrimitiveType p_primitive, const Vector<uint8_t> &p_array, const Vector<uint8_t> &p_attribute_array, const Vector<uint8_t> &p_skin_array, int p_vertex_count, const Vector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<uint8_t> &p_blend_shape_data, const Vector<AABB> &p_bone_aabbs, const Vector<RS::SurfaceData::LOD> &p_lods) {
_create_if_empty();
Surface s;
@@ -1096,16 +1379,18 @@ void ArrayMesh::add_surface(uint32_t p_format, PrimitiveType p_primitive, const
sd.aabb = p_aabb;
sd.vertex_count = p_vertex_count;
sd.vertex_data = p_array;
+ sd.attribute_data = p_attribute_array;
+ sd.skin_data = p_skin_array;
sd.index_count = p_index_count;
sd.index_data = p_index_array;
- sd.blend_shapes = p_blend_shapes;
- sd.bone_aabbs = p_bone_aabb;
+ sd.blend_shape_data = p_blend_shape_data;
+ sd.bone_aabbs = p_bone_aabbs;
sd.lods = p_lods;
RenderingServer::get_singleton()->mesh_add_surface(mesh, sd);
clear_cache();
- _change_notify();
+ notify_property_list_changed();
emit_changed();
}
@@ -1117,15 +1402,17 @@ void ArrayMesh::add_surface_from_arrays(PrimitiveType p_primitive, const Array &
Error err = RS::get_singleton()->mesh_create_surface_data_from_arrays(&surface, (RenderingServer::PrimitiveType)p_primitive, p_arrays, p_blend_shapes, p_lods, p_flags);
ERR_FAIL_COND(err != OK);
- /* print_line("format: " + itos(surface.format));
+ /* Debug code.
+ print_line("format: " + itos(surface.format));
print_line("aabb: " + surface.aabb);
print_line("array size: " + itos(surface.vertex_data.size()));
print_line("vertex count: " + itos(surface.vertex_count));
print_line("index size: " + itos(surface.index_data.size()));
print_line("index count: " + itos(surface.index_count));
print_line("primitive: " + itos(surface.primitive));
-*/
- add_surface(surface.format, PrimitiveType(surface.primitive), surface.vertex_data, surface.vertex_count, surface.index_data, surface.index_count, surface.aabb, surface.blend_shapes, surface.bone_aabbs, surface.lods);
+ */
+
+ add_surface(surface.format, PrimitiveType(surface.primitive), surface.vertex_data, surface.attribute_data, surface.skin_data, surface.vertex_count, surface.index_data, surface.index_count, surface.aabb, surface.blend_shape_data, surface.bone_aabbs, surface.lods);
}
Array ArrayMesh::surface_get_arrays(int p_surface) const {
@@ -1161,7 +1448,10 @@ void ArrayMesh::add_blend_shape(const StringName &p_name) {
}
blend_shapes.push_back(name);
- //RS::get_singleton()->mesh_set_blend_shape_count(mesh, blend_shapes.size());
+
+ if (mesh.is_valid()) {
+ RS::get_singleton()->mesh_set_blend_shape_count(mesh, blend_shapes.size());
+ }
}
int ArrayMesh::get_blend_shape_count() const {
@@ -1173,10 +1463,30 @@ StringName ArrayMesh::get_blend_shape_name(int p_index) const {
return blend_shapes[p_index];
}
+void ArrayMesh::set_blend_shape_name(int p_index, const StringName &p_name) {
+ ERR_FAIL_INDEX(p_index, blend_shapes.size());
+
+ StringName name = p_name;
+ int found = blend_shapes.find(name);
+ if (found != -1 && found != p_index) {
+ int count = 2;
+ do {
+ name = String(p_name) + " " + itos(count);
+ count++;
+ } while (blend_shapes.find(name) != -1);
+ }
+
+ blend_shapes.write[p_index] = name;
+}
+
void ArrayMesh::clear_blend_shapes() {
ERR_FAIL_COND_MSG(surfaces.size(), "Can't set shape key count if surfaces are already created.");
blend_shapes.clear();
+
+ if (mesh.is_valid()) {
+ RS::get_singleton()->mesh_set_blend_shape_count(mesh, 0);
+ }
}
void ArrayMesh::set_blend_shape_mode(BlendShapeMode p_mode) {
@@ -1218,7 +1528,6 @@ void ArrayMesh::surface_set_material(int p_idx, const Ref<Material> &p_material)
surfaces.write[p_idx].material = p_material;
RenderingServer::get_singleton()->mesh_surface_set_material(mesh, p_idx, p_material.is_null() ? RID() : p_material->get_rid());
- _change_notify("material");
emit_changed();
}
@@ -1243,9 +1552,21 @@ String ArrayMesh::surface_get_name(int p_idx) const {
return surfaces[p_idx].name;
}
-void ArrayMesh::surface_update_region(int p_surface, int p_offset, const Vector<uint8_t> &p_data) {
+void ArrayMesh::surface_update_vertex_region(int p_surface, int p_offset, const Vector<uint8_t> &p_data) {
+ ERR_FAIL_INDEX(p_surface, surfaces.size());
+ RS::get_singleton()->mesh_surface_update_vertex_region(mesh, p_surface, p_offset, p_data);
+ emit_changed();
+}
+
+void ArrayMesh::surface_update_attribute_region(int p_surface, int p_offset, const Vector<uint8_t> &p_data) {
+ ERR_FAIL_INDEX(p_surface, surfaces.size());
+ RS::get_singleton()->mesh_surface_update_attribute_region(mesh, p_surface, p_offset, p_data);
+ emit_changed();
+}
+
+void ArrayMesh::surface_update_skin_region(int p_surface, int p_offset, const Vector<uint8_t> &p_data) {
ERR_FAIL_INDEX(p_surface, surfaces.size());
- RS::get_singleton()->mesh_surface_update_region(mesh, p_surface, p_offset, p_data);
+ RS::get_singleton()->mesh_surface_update_skin_region(mesh, p_surface, p_offset, p_data);
emit_changed();
}
@@ -1290,7 +1611,7 @@ AABB ArrayMesh::get_custom_aabb() const {
return custom_aabb;
}
-void ArrayMesh::regen_normalmaps() {
+void ArrayMesh::regen_normal_maps() {
if (surfaces.size() == 0) {
return;
}
@@ -1310,38 +1631,38 @@ void ArrayMesh::regen_normalmaps() {
}
//dirty hack
-bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y, int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache);
+bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, const uint8_t *p_cache_data, bool *r_use_cache, uint8_t **r_mesh_cache, int *r_mesh_cache_size, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y) = NULL;
struct ArrayMeshLightmapSurface {
Ref<Material> material;
- Vector<SurfaceTool::Vertex> vertices;
- Mesh::PrimitiveType primitive;
- uint32_t format;
+ LocalVector<SurfaceTool::Vertex> vertices;
+ Mesh::PrimitiveType primitive = Mesh::PrimitiveType::PRIMITIVE_MAX;
+ uint32_t format = 0;
};
-Error ArrayMesh::lightmap_unwrap(const Transform &p_base_transform, float p_texel_size) {
- int *cache_data = nullptr;
- unsigned int cache_size = 0;
- bool use_cache = false; // Don't use cache
- return lightmap_unwrap_cached(cache_data, cache_size, use_cache, p_base_transform, p_texel_size);
+Error ArrayMesh::lightmap_unwrap(const Transform3D &p_base_transform, float p_texel_size) {
+ Vector<uint8_t> null_cache;
+ return lightmap_unwrap_cached(p_base_transform, p_texel_size, null_cache, null_cache, false);
}
-Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache, const Transform &p_base_transform, float p_texel_size) {
+Error ArrayMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache, bool p_generate_cache) {
ERR_FAIL_COND_V(!array_mesh_lightmap_unwrap_callback, ERR_UNCONFIGURED);
ERR_FAIL_COND_V_MSG(blend_shapes.size() != 0, ERR_UNAVAILABLE, "Can't unwrap mesh with blend shapes.");
- Vector<float> vertices;
- Vector<float> normals;
- Vector<int> indices;
- Vector<float> uv;
- Vector<Pair<int, int>> uv_indices;
+ LocalVector<float> vertices;
+ LocalVector<float> normals;
+ LocalVector<int> indices;
+ LocalVector<float> uv;
+ LocalVector<Pair<int, int>> uv_indices;
Vector<ArrayMeshLightmapSurface> lightmap_surfaces;
// Keep only the scale
- Transform transform = p_base_transform;
- transform.origin = Vector3();
- transform.looking_at(Vector3(1, 0, 0), Vector3(0, 1, 0));
+ Basis basis = p_base_transform.get_basis();
+ Vector3 scale = Vector3(basis.get_axis(0).length(), basis.get_axis(1).length(), basis.get_axis(2).length());
+
+ Transform3D transform;
+ transform.scale(scale);
Basis normal_basis = transform.basis.inverse().transposed();
@@ -1355,14 +1676,12 @@ Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cach
Array arrays = surface_get_arrays(i);
s.material = surface_get_material(i);
- s.vertices = SurfaceTool::create_vertex_array_from_triangle_arrays(arrays);
+ SurfaceTool::create_vertex_array_from_triangle_arrays(arrays, s.vertices, &s.format);
- Vector<Vector3> rvertices = arrays[Mesh::ARRAY_VERTEX];
+ PackedVector3Array rvertices = arrays[Mesh::ARRAY_VERTEX];
int vc = rvertices.size();
- const Vector3 *r = rvertices.ptr();
- Vector<Vector3> rnormals = arrays[Mesh::ARRAY_NORMAL];
- const Vector3 *rn = rnormals.ptr();
+ PackedVector3Array rnormals = arrays[Mesh::ARRAY_NORMAL];
int vertex_ofs = vertices.size() / 3;
@@ -1371,24 +1690,29 @@ Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cach
uv_indices.resize(vertex_ofs + vc);
for (int j = 0; j < vc; j++) {
- Vector3 v = transform.xform(r[j]);
- Vector3 n = normal_basis.xform(rn[j]).normalized();
-
- vertices.write[(j + vertex_ofs) * 3 + 0] = v.x;
- vertices.write[(j + vertex_ofs) * 3 + 1] = v.y;
- vertices.write[(j + vertex_ofs) * 3 + 2] = v.z;
- normals.write[(j + vertex_ofs) * 3 + 0] = n.x;
- normals.write[(j + vertex_ofs) * 3 + 1] = n.y;
- normals.write[(j + vertex_ofs) * 3 + 2] = n.z;
- uv_indices.write[j + vertex_ofs] = Pair<int, int>(i, j);
+ Vector3 v = transform.xform(rvertices[j]);
+ Vector3 n = normal_basis.xform(rnormals[j]).normalized();
+
+ vertices[(j + vertex_ofs) * 3 + 0] = v.x;
+ vertices[(j + vertex_ofs) * 3 + 1] = v.y;
+ vertices[(j + vertex_ofs) * 3 + 2] = v.z;
+ normals[(j + vertex_ofs) * 3 + 0] = n.x;
+ normals[(j + vertex_ofs) * 3 + 1] = n.y;
+ normals[(j + vertex_ofs) * 3 + 2] = n.z;
+ uv_indices[j + vertex_ofs] = Pair<int, int>(i, j);
}
- Vector<int> rindices = arrays[Mesh::ARRAY_INDEX];
+ PackedInt32Array rindices = arrays[Mesh::ARRAY_INDEX];
int ic = rindices.size();
+ float eps = 1.19209290e-7F; // Taken from xatlas.h
if (ic == 0) {
for (int j = 0; j < vc / 3; j++) {
- if (Face3(r[j * 3 + 0], r[j * 3 + 1], r[j * 3 + 2]).is_degenerate()) {
+ Vector3 p0 = transform.xform(rvertices[j * 3 + 0]);
+ Vector3 p1 = transform.xform(rvertices[j * 3 + 1]);
+ Vector3 p2 = transform.xform(rvertices[j * 3 + 2]);
+
+ if ((p0 - p1).length_squared() < eps || (p1 - p2).length_squared() < eps || (p2 - p0).length_squared() < eps) {
continue;
}
@@ -1398,15 +1722,18 @@ Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cach
}
} else {
- const int *ri = rindices.ptr();
-
for (int j = 0; j < ic / 3; j++) {
- if (Face3(r[ri[j * 3 + 0]], r[ri[j * 3 + 1]], r[ri[j * 3 + 2]]).is_degenerate()) {
+ Vector3 p0 = transform.xform(rvertices[rindices[j * 3 + 0]]);
+ Vector3 p1 = transform.xform(rvertices[rindices[j * 3 + 1]]);
+ Vector3 p2 = transform.xform(rvertices[rindices[j * 3 + 2]]);
+
+ if ((p0 - p1).length_squared() < eps || (p1 - p2).length_squared() < eps || (p2 - p0).length_squared() < eps) {
continue;
}
- indices.push_back(vertex_ofs + ri[j * 3 + 0]);
- indices.push_back(vertex_ofs + ri[j * 3 + 1]);
- indices.push_back(vertex_ofs + ri[j * 3 + 2]);
+
+ indices.push_back(vertex_ofs + rindices[j * 3 + 0]);
+ indices.push_back(vertex_ofs + rindices[j * 3 + 1]);
+ indices.push_back(vertex_ofs + rindices[j * 3 + 2]);
}
}
@@ -1415,6 +1742,9 @@ Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cach
//unwrap
+ bool use_cache = p_generate_cache; // Used to request cache generation and to know if cache was used
+ uint8_t *gen_cache;
+ int gen_cache_size;
float *gen_uvs;
int *gen_vertices;
int *gen_indices;
@@ -1423,32 +1753,32 @@ Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cach
int size_x;
int size_y;
- bool ok = array_mesh_lightmap_unwrap_callback(p_texel_size, vertices.ptr(), normals.ptr(), vertices.size() / 3, indices.ptr(), indices.size(), &gen_uvs, &gen_vertices, &gen_vertex_count, &gen_indices, &gen_index_count, &size_x, &size_y, r_cache_data, r_cache_size, r_used_cache);
+ bool ok = array_mesh_lightmap_unwrap_callback(p_texel_size, vertices.ptr(), normals.ptr(), vertices.size() / 3, indices.ptr(), indices.size(), p_src_cache.ptr(), &use_cache, &gen_cache, &gen_cache_size, &gen_uvs, &gen_vertices, &gen_vertex_count, &gen_indices, &gen_index_count, &size_x, &size_y);
if (!ok) {
return ERR_CANT_CREATE;
}
- //remove surfaces
clear_surfaces();
//create surfacetools for each surface..
- Vector<Ref<SurfaceTool>> surfaces_tools;
+ LocalVector<Ref<SurfaceTool>> surfaces_tools;
for (int i = 0; i < lightmap_surfaces.size(); i++) {
Ref<SurfaceTool> st;
- st.instance();
+ st.instantiate();
st->begin(Mesh::PRIMITIVE_TRIANGLES);
st->set_material(lightmap_surfaces[i].material);
surfaces_tools.push_back(st); //stay there
}
print_verbose("Mesh: Gen indices: " + itos(gen_index_count));
+
//go through all indices
for (int i = 0; i < gen_index_count; i += 3) {
- ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 0]], uv_indices.size(), ERR_BUG);
- ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 1]], uv_indices.size(), ERR_BUG);
- ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 2]], uv_indices.size(), ERR_BUG);
+ ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 0]], (int)uv_indices.size(), ERR_BUG);
+ ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 1]], (int)uv_indices.size(), ERR_BUG);
+ ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 2]], (int)uv_indices.size(), ERR_BUG);
ERR_FAIL_COND_V(uv_indices[gen_vertices[gen_indices[i + 0]]].first != uv_indices[gen_vertices[gen_indices[i + 1]]].first || uv_indices[gen_vertices[gen_indices[i + 0]]].first != uv_indices[gen_vertices[gen_indices[i + 2]]].first, ERR_BUG);
@@ -1458,64 +1788,85 @@ Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cach
SurfaceTool::Vertex v = lightmap_surfaces[surface].vertices[uv_indices[gen_vertices[gen_indices[i + j]]].second];
if (lightmap_surfaces[surface].format & ARRAY_FORMAT_COLOR) {
- surfaces_tools.write[surface]->add_color(v.color);
+ surfaces_tools[surface]->set_color(v.color);
}
if (lightmap_surfaces[surface].format & ARRAY_FORMAT_TEX_UV) {
- surfaces_tools.write[surface]->add_uv(v.uv);
+ surfaces_tools[surface]->set_uv(v.uv);
}
if (lightmap_surfaces[surface].format & ARRAY_FORMAT_NORMAL) {
- surfaces_tools.write[surface]->add_normal(v.normal);
+ surfaces_tools[surface]->set_normal(v.normal);
}
if (lightmap_surfaces[surface].format & ARRAY_FORMAT_TANGENT) {
Plane t;
t.normal = v.tangent;
t.d = v.binormal.dot(v.normal.cross(v.tangent)) < 0 ? -1 : 1;
- surfaces_tools.write[surface]->add_tangent(t);
+ surfaces_tools[surface]->set_tangent(t);
}
if (lightmap_surfaces[surface].format & ARRAY_FORMAT_BONES) {
- surfaces_tools.write[surface]->add_bones(v.bones);
+ surfaces_tools[surface]->set_bones(v.bones);
}
if (lightmap_surfaces[surface].format & ARRAY_FORMAT_WEIGHTS) {
- surfaces_tools.write[surface]->add_weights(v.weights);
+ surfaces_tools[surface]->set_weights(v.weights);
}
Vector2 uv2(gen_uvs[gen_indices[i + j] * 2 + 0], gen_uvs[gen_indices[i + j] * 2 + 1]);
- surfaces_tools.write[surface]->add_uv2(uv2);
+ surfaces_tools[surface]->set_uv2(uv2);
- surfaces_tools.write[surface]->add_vertex(v.vertex);
+ surfaces_tools[surface]->add_vertex(v.vertex);
}
}
//generate surfaces
-
- for (int i = 0; i < surfaces_tools.size(); i++) {
- surfaces_tools.write[i]->index();
- surfaces_tools.write[i]->commit(Ref<ArrayMesh>((ArrayMesh *)this), lightmap_surfaces[i].format);
+ for (unsigned int i = 0; i < surfaces_tools.size(); i++) {
+ surfaces_tools[i]->index();
+ surfaces_tools[i]->commit(Ref<ArrayMesh>((ArrayMesh *)this), lightmap_surfaces[i].format);
}
set_lightmap_size_hint(Size2(size_x, size_y));
- if (!r_used_cache) {
- //free stuff
- ::free(gen_vertices);
- ::free(gen_indices);
- ::free(gen_uvs);
+ if (gen_cache_size > 0) {
+ r_dst_cache.resize(gen_cache_size);
+ memcpy(r_dst_cache.ptrw(), gen_cache, gen_cache_size);
+ memfree(gen_cache);
+ }
+
+ if (!use_cache) {
+ // Cache was not used, free the buffers
+ memfree(gen_vertices);
+ memfree(gen_indices);
+ memfree(gen_uvs);
}
return OK;
}
+void ArrayMesh::set_shadow_mesh(const Ref<ArrayMesh> &p_mesh) {
+ shadow_mesh = p_mesh;
+ if (shadow_mesh.is_valid()) {
+ RS::get_singleton()->mesh_set_shadow_mesh(mesh, shadow_mesh->get_rid());
+ } else {
+ RS::get_singleton()->mesh_set_shadow_mesh(mesh, RID());
+ }
+}
+
+Ref<ArrayMesh> ArrayMesh::get_shadow_mesh() const {
+ return shadow_mesh;
+}
+
void ArrayMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_blend_shape", "name"), &ArrayMesh::add_blend_shape);
ClassDB::bind_method(D_METHOD("get_blend_shape_count"), &ArrayMesh::get_blend_shape_count);
ClassDB::bind_method(D_METHOD("get_blend_shape_name", "index"), &ArrayMesh::get_blend_shape_name);
+ ClassDB::bind_method(D_METHOD("set_blend_shape_name", "index", "name"), &ArrayMesh::set_blend_shape_name);
ClassDB::bind_method(D_METHOD("clear_blend_shapes"), &ArrayMesh::clear_blend_shapes);
ClassDB::bind_method(D_METHOD("set_blend_shape_mode", "mode"), &ArrayMesh::set_blend_shape_mode);
ClassDB::bind_method(D_METHOD("get_blend_shape_mode"), &ArrayMesh::get_blend_shape_mode);
- ClassDB::bind_method(D_METHOD("add_surface_from_arrays", "primitive", "arrays", "blend_shapes", "lods", "compress_flags"), &ArrayMesh::add_surface_from_arrays, DEFVAL(Array()), DEFVAL(Dictionary()), DEFVAL(ARRAY_COMPRESS_DEFAULT));
+ ClassDB::bind_method(D_METHOD("add_surface_from_arrays", "primitive", "arrays", "blend_shapes", "lods", "compress_flags"), &ArrayMesh::add_surface_from_arrays, DEFVAL(Array()), DEFVAL(Dictionary()), DEFVAL(0));
ClassDB::bind_method(D_METHOD("clear_surfaces"), &ArrayMesh::clear_surfaces);
- ClassDB::bind_method(D_METHOD("surface_update_region", "surf_idx", "offset", "data"), &ArrayMesh::surface_update_region);
+ ClassDB::bind_method(D_METHOD("surface_update_vertex_region", "surf_idx", "offset", "data"), &ArrayMesh::surface_update_vertex_region);
+ ClassDB::bind_method(D_METHOD("surface_update_attribute_region", "surf_idx", "offset", "data"), &ArrayMesh::surface_update_attribute_region);
+ ClassDB::bind_method(D_METHOD("surface_update_skin_region", "surf_idx", "offset", "data"), &ArrayMesh::surface_update_skin_region);
ClassDB::bind_method(D_METHOD("surface_get_array_len", "surf_idx"), &ArrayMesh::surface_get_array_len);
ClassDB::bind_method(D_METHOD("surface_get_array_index_len", "surf_idx"), &ArrayMesh::surface_get_array_index_len);
ClassDB::bind_method(D_METHOD("surface_get_format", "surf_idx"), &ArrayMesh::surface_get_format);
@@ -1524,10 +1875,10 @@ void ArrayMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("surface_set_name", "surf_idx", "name"), &ArrayMesh::surface_set_name);
ClassDB::bind_method(D_METHOD("surface_get_name", "surf_idx"), &ArrayMesh::surface_get_name);
ClassDB::bind_method(D_METHOD("create_trimesh_shape"), &ArrayMesh::create_trimesh_shape);
- ClassDB::bind_method(D_METHOD("create_convex_shape"), &ArrayMesh::create_convex_shape);
+ ClassDB::bind_method(D_METHOD("create_convex_shape", "clean", "simplify"), &ArrayMesh::create_convex_shape, DEFVAL(true), DEFVAL(false));
ClassDB::bind_method(D_METHOD("create_outline", "margin"), &ArrayMesh::create_outline);
- ClassDB::bind_method(D_METHOD("regen_normalmaps"), &ArrayMesh::regen_normalmaps);
- ClassDB::set_method_flags(get_class_static(), _scs_create("regen_normalmaps"), METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR);
+ ClassDB::bind_method(D_METHOD("regen_normal_maps"), &ArrayMesh::regen_normal_maps);
+ ClassDB::set_method_flags(get_class_static(), _scs_create("regen_normal_maps"), METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR);
ClassDB::bind_method(D_METHOD("lightmap_unwrap", "transform", "texel_size"), &ArrayMesh::lightmap_unwrap);
ClassDB::set_method_flags(get_class_static(), _scs_create("lightmap_unwrap"), METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR);
ClassDB::bind_method(D_METHOD("get_faces"), &ArrayMesh::get_faces);
@@ -1536,36 +1887,20 @@ void ArrayMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_custom_aabb", "aabb"), &ArrayMesh::set_custom_aabb);
ClassDB::bind_method(D_METHOD("get_custom_aabb"), &ArrayMesh::get_custom_aabb);
+ ClassDB::bind_method(D_METHOD("set_shadow_mesh", "mesh"), &ArrayMesh::set_shadow_mesh);
+ ClassDB::bind_method(D_METHOD("get_shadow_mesh"), &ArrayMesh::get_shadow_mesh);
+
+ ClassDB::bind_method(D_METHOD("_set_blend_shape_names", "blend_shape_names"), &ArrayMesh::_set_blend_shape_names);
+ ClassDB::bind_method(D_METHOD("_get_blend_shape_names"), &ArrayMesh::_get_blend_shape_names);
+
ClassDB::bind_method(D_METHOD("_set_surfaces", "surfaces"), &ArrayMesh::_set_surfaces);
ClassDB::bind_method(D_METHOD("_get_surfaces"), &ArrayMesh::_get_surfaces);
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "_blend_shape_names", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_blend_shape_names", "_get_blend_shape_names");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_surfaces", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_surfaces", "_get_surfaces");
ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_shape_mode", PROPERTY_HINT_ENUM, "Normalized,Relative"), "set_blend_shape_mode", "get_blend_shape_mode");
ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, ""), "set_custom_aabb", "get_custom_aabb");
-
- BIND_CONSTANT(NO_INDEX_ARRAY);
- BIND_CONSTANT(ARRAY_WEIGHTS_SIZE);
-
- BIND_ENUM_CONSTANT(ARRAY_VERTEX);
- BIND_ENUM_CONSTANT(ARRAY_NORMAL);
- BIND_ENUM_CONSTANT(ARRAY_TANGENT);
- BIND_ENUM_CONSTANT(ARRAY_COLOR);
- BIND_ENUM_CONSTANT(ARRAY_TEX_UV);
- BIND_ENUM_CONSTANT(ARRAY_TEX_UV2);
- BIND_ENUM_CONSTANT(ARRAY_BONES);
- BIND_ENUM_CONSTANT(ARRAY_WEIGHTS);
- BIND_ENUM_CONSTANT(ARRAY_INDEX);
- BIND_ENUM_CONSTANT(ARRAY_MAX);
-
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_VERTEX);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_NORMAL);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_TANGENT);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_COLOR);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_TEX_UV);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_TEX_UV2);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_BONES);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_WEIGHTS);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_INDEX);
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shadow_mesh", PROPERTY_HINT_RESOURCE_TYPE, "ArrayMesh"), "set_shadow_mesh", "get_shadow_mesh");
}
void ArrayMesh::reload_from_file() {
@@ -1576,13 +1911,12 @@ void ArrayMesh::reload_from_file() {
Resource::reload_from_file();
- _change_notify();
+ notify_property_list_changed();
}
ArrayMesh::ArrayMesh() {
//mesh is now created on demand
//mesh = RenderingServer::get_singleton()->mesh_create();
- blend_shape_mode = BLEND_SHAPE_MODE_RELATIVE;
}
ArrayMesh::~ArrayMesh() {
diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h
index b0a30a5627..27b0eb098b 100644
--- a/scene/resources/mesh.h
+++ b/scene/resources/mesh.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,9 +31,9 @@
#ifndef MESH_H
#define MESH_H
+#include "core/io/resource.h"
#include "core/math/face3.h"
#include "core/math/triangle_mesh.h"
-#include "core/resource.h"
#include "scene/resources/material.h"
#include "scene/resources/shape_3d.h"
#include "servers/rendering_server.h"
@@ -50,19 +50,24 @@ protected:
public:
enum {
-
NO_INDEX_ARRAY = RenderingServer::NO_INDEX_ARRAY,
ARRAY_WEIGHTS_SIZE = RenderingServer::ARRAY_WEIGHTS_SIZE
};
-
+ enum BlendShapeMode {
+ BLEND_SHAPE_MODE_NORMALIZED = RS::BLEND_SHAPE_MODE_NORMALIZED,
+ BLEND_SHAPE_MODE_RELATIVE = RS::BLEND_SHAPE_MODE_RELATIVE,
+ };
enum ArrayType {
-
ARRAY_VERTEX = RenderingServer::ARRAY_VERTEX,
ARRAY_NORMAL = RenderingServer::ARRAY_NORMAL,
ARRAY_TANGENT = RenderingServer::ARRAY_TANGENT,
ARRAY_COLOR = RenderingServer::ARRAY_COLOR,
ARRAY_TEX_UV = RenderingServer::ARRAY_TEX_UV,
ARRAY_TEX_UV2 = RenderingServer::ARRAY_TEX_UV2,
+ ARRAY_CUSTOM0 = RenderingServer::ARRAY_CUSTOM0,
+ ARRAY_CUSTOM1 = RenderingServer::ARRAY_CUSTOM1,
+ ARRAY_CUSTOM2 = RenderingServer::ARRAY_CUSTOM2,
+ ARRAY_CUSTOM3 = RenderingServer::ARRAY_CUSTOM3,
ARRAY_BONES = RenderingServer::ARRAY_BONES,
ARRAY_WEIGHTS = RenderingServer::ARRAY_WEIGHTS,
ARRAY_INDEX = RenderingServer::ARRAY_INDEX,
@@ -70,30 +75,47 @@ public:
};
+ enum ArrayCustomFormat {
+ ARRAY_CUSTOM_RGBA8_UNORM,
+ ARRAY_CUSTOM_RGBA8_SNORM,
+ ARRAY_CUSTOM_RG_HALF,
+ ARRAY_CUSTOM_RGBA_HALF,
+ ARRAY_CUSTOM_R_FLOAT,
+ ARRAY_CUSTOM_RG_FLOAT,
+ ARRAY_CUSTOM_RGB_FLOAT,
+ ARRAY_CUSTOM_RGBA_FLOAT,
+ ARRAY_CUSTOM_MAX
+ };
+
enum ArrayFormat {
- /* ARRAY FORMAT FLAGS */
- ARRAY_FORMAT_VERTEX = 1 << ARRAY_VERTEX, // mandatory
- ARRAY_FORMAT_NORMAL = 1 << ARRAY_NORMAL,
- ARRAY_FORMAT_TANGENT = 1 << ARRAY_TANGENT,
- ARRAY_FORMAT_COLOR = 1 << ARRAY_COLOR,
- ARRAY_FORMAT_TEX_UV = 1 << ARRAY_TEX_UV,
- ARRAY_FORMAT_TEX_UV2 = 1 << ARRAY_TEX_UV2,
- ARRAY_FORMAT_BONES = 1 << ARRAY_BONES,
- ARRAY_FORMAT_WEIGHTS = 1 << ARRAY_WEIGHTS,
- ARRAY_FORMAT_INDEX = 1 << ARRAY_INDEX,
-
- ARRAY_COMPRESS_BASE = (ARRAY_INDEX + 1),
- ARRAY_COMPRESS_NORMAL = 1 << (ARRAY_NORMAL + ARRAY_COMPRESS_BASE),
- ARRAY_COMPRESS_TANGENT = 1 << (ARRAY_TANGENT + ARRAY_COMPRESS_BASE),
- ARRAY_COMPRESS_COLOR = 1 << (ARRAY_COLOR + ARRAY_COMPRESS_BASE),
- ARRAY_COMPRESS_TEX_UV = 1 << (ARRAY_TEX_UV + ARRAY_COMPRESS_BASE),
- ARRAY_COMPRESS_TEX_UV2 = 1 << (ARRAY_TEX_UV2 + ARRAY_COMPRESS_BASE),
- ARRAY_COMPRESS_INDEX = 1 << (ARRAY_INDEX + ARRAY_COMPRESS_BASE),
-
- ARRAY_FLAG_USE_2D_VERTICES = ARRAY_COMPRESS_INDEX << 1,
- ARRAY_FLAG_USE_DYNAMIC_UPDATE = ARRAY_COMPRESS_INDEX << 3,
-
- ARRAY_COMPRESS_DEFAULT = ARRAY_COMPRESS_NORMAL | ARRAY_COMPRESS_TANGENT | ARRAY_COMPRESS_COLOR | ARRAY_COMPRESS_TEX_UV | ARRAY_COMPRESS_TEX_UV2
+ ARRAY_FORMAT_VERTEX = RS::ARRAY_FORMAT_VERTEX,
+ ARRAY_FORMAT_NORMAL = RS::ARRAY_FORMAT_NORMAL,
+ ARRAY_FORMAT_TANGENT = RS::ARRAY_FORMAT_TANGENT,
+ ARRAY_FORMAT_COLOR = RS::ARRAY_FORMAT_COLOR,
+ ARRAY_FORMAT_TEX_UV = RS::ARRAY_FORMAT_TEX_UV,
+ ARRAY_FORMAT_TEX_UV2 = RS::ARRAY_FORMAT_TEX_UV2,
+ ARRAY_FORMAT_CUSTOM0 = RS::ARRAY_FORMAT_CUSTOM0,
+ ARRAY_FORMAT_CUSTOM1 = RS::ARRAY_FORMAT_CUSTOM1,
+ ARRAY_FORMAT_CUSTOM2 = RS::ARRAY_FORMAT_CUSTOM2,
+ ARRAY_FORMAT_CUSTOM3 = RS::ARRAY_FORMAT_CUSTOM3,
+ ARRAY_FORMAT_BONES = RS::ARRAY_FORMAT_BONES,
+ ARRAY_FORMAT_WEIGHTS = RS::ARRAY_FORMAT_WEIGHTS,
+ ARRAY_FORMAT_INDEX = RS::ARRAY_FORMAT_INDEX,
+
+ ARRAY_FORMAT_BLEND_SHAPE_MASK = RS::ARRAY_FORMAT_BLEND_SHAPE_MASK,
+
+ ARRAY_FORMAT_CUSTOM_BASE = RS::ARRAY_FORMAT_CUSTOM_BASE,
+ ARRAY_FORMAT_CUSTOM0_SHIFT = RS::ARRAY_FORMAT_CUSTOM0_SHIFT,
+ ARRAY_FORMAT_CUSTOM1_SHIFT = RS::ARRAY_FORMAT_CUSTOM1_SHIFT,
+ ARRAY_FORMAT_CUSTOM2_SHIFT = RS::ARRAY_FORMAT_CUSTOM2_SHIFT,
+ ARRAY_FORMAT_CUSTOM3_SHIFT = RS::ARRAY_FORMAT_CUSTOM3_SHIFT,
+
+ ARRAY_FORMAT_CUSTOM_MASK = RS::ARRAY_FORMAT_CUSTOM_MASK,
+ ARRAY_COMPRESS_FLAGS_BASE = RS::ARRAY_COMPRESS_FLAGS_BASE,
+
+ ARRAY_FLAG_USE_2D_VERTICES = RS::ARRAY_FLAG_USE_2D_VERTICES,
+ ARRAY_FLAG_USE_DYNAMIC_UPDATE = RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE,
+ ARRAY_FLAG_USE_8_BONE_WEIGHTS = RS::ARRAY_FLAG_USE_8_BONE_WEIGHTS,
};
@@ -106,12 +128,6 @@ public:
PRIMITIVE_MAX = RenderingServer::PRIMITIVE_MAX,
};
- enum BlendShapeMode {
-
- BLEND_SHAPE_MODE_NORMALIZED = RS::BLEND_SHAPE_MODE_NORMALIZED,
- BLEND_SHAPE_MODE_RELATIVE = RS::BLEND_SHAPE_MODE_RELATIVE,
- };
-
virtual int get_surface_count() const = 0;
virtual int surface_get_array_len(int p_idx) const = 0;
virtual int surface_get_array_index_len(int p_idx) const = 0;
@@ -125,6 +141,7 @@ public:
virtual Ref<Material> surface_get_material(int p_idx) const = 0;
virtual int get_blend_shape_count() const = 0;
virtual StringName get_blend_shape_name(int p_index) const = 0;
+ virtual void set_blend_shape_name(int p_index, const StringName &p_name) = 0;
Vector<Face3> get_faces() const;
Ref<TriangleMesh> generate_triangle_mesh() const;
@@ -132,7 +149,7 @@ public:
void generate_debug_mesh_indices(Vector<Vector3> &r_points);
Ref<Shape3D> create_trimesh_shape() const;
- Ref<Shape3D> create_convex_shape() const;
+ Ref<Shape3D> create_convex_shape(bool p_clean = true, bool p_simplify = false) const;
Ref<Mesh> create_outline(float p_margin) const;
@@ -142,11 +159,14 @@ public:
Size2i get_lightmap_size_hint() const;
void clear_cache() const;
- typedef Vector<Vector<Face3>> (*ConvexDecompositionFunc)(const Vector<Face3> &);
+ typedef Vector<Vector<Face3>> (*ConvexDecompositionFunc)(const Vector<Face3> &p_faces, int p_max_convex_hulls);
static ConvexDecompositionFunc convex_composition_function;
- Vector<Ref<Shape3D>> convex_decompose() const;
+ Vector<Ref<Shape3D>> convex_decompose(int p_max_convex_hulls = -1) const;
+
+ virtual int get_builtin_bind_pose_count() const;
+ virtual Transform3D get_builtin_bind_pose(int p_index) const;
Mesh();
};
@@ -155,25 +175,29 @@ class ArrayMesh : public Mesh {
GDCLASS(ArrayMesh, Mesh);
RES_BASE_EXTENSION("mesh");
+ PackedStringArray _get_blend_shape_names() const;
+ void _set_blend_shape_names(const PackedStringArray &p_names);
+
Array _get_surfaces() const;
void _set_surfaces(const Array &p_data);
+ Ref<ArrayMesh> shadow_mesh;
private:
struct Surface {
- uint32_t format;
- int array_length;
- int index_array_length;
- PrimitiveType primitive;
+ uint32_t format = 0;
+ int array_length = 0;
+ int index_array_length = 0;
+ PrimitiveType primitive = PrimitiveType::PRIMITIVE_MAX;
String name;
AABB aabb;
Ref<Material> material;
- bool is_2d;
+ bool is_2d = false;
};
Vector<Surface> surfaces;
mutable RID mesh;
AABB aabb;
- BlendShapeMode blend_shape_mode;
+ BlendShapeMode blend_shape_mode = BLEND_SHAPE_MODE_RELATIVE;
Vector<StringName> blend_shapes;
AABB custom_aabb;
@@ -187,12 +211,14 @@ protected:
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
+ virtual void reset_state() override;
+
static void _bind_methods();
public:
- void add_surface_from_arrays(PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes = Array(), const Dictionary &p_lods = Dictionary(), uint32_t p_flags = ARRAY_COMPRESS_DEFAULT);
+ void add_surface_from_arrays(PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes = Array(), const Dictionary &p_lods = Dictionary(), uint32_t p_flags = 0);
- void add_surface(uint32_t p_format, PrimitiveType p_primitive, const Vector<uint8_t> &p_array, int p_vertex_count, const Vector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<Vector<uint8_t>> &p_blend_shapes = Vector<Vector<uint8_t>>(), const Vector<AABB> &p_bone_aabbs = Vector<AABB>(), const Vector<RS::SurfaceData::LOD> &p_lods = Vector<RS::SurfaceData::LOD>());
+ void add_surface(uint32_t p_format, PrimitiveType p_primitive, const Vector<uint8_t> &p_array, const Vector<uint8_t> &p_attribute_array, const Vector<uint8_t> &p_skin_array, int p_vertex_count, const Vector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<uint8_t> &p_blend_shape_data = Vector<uint8_t>(), const Vector<AABB> &p_bone_aabbs = Vector<AABB>(), const Vector<RS::SurfaceData::LOD> &p_lods = Vector<RS::SurfaceData::LOD>());
Array surface_get_arrays(int p_surface) const override;
Array surface_get_blend_shape_arrays(int p_surface) const override;
@@ -201,12 +227,15 @@ public:
void add_blend_shape(const StringName &p_name);
int get_blend_shape_count() const override;
StringName get_blend_shape_name(int p_index) const override;
+ void set_blend_shape_name(int p_index, const StringName &p_name) override;
void clear_blend_shapes();
void set_blend_shape_mode(BlendShapeMode p_mode);
BlendShapeMode get_blend_shape_mode() const;
- void surface_update_region(int p_surface, int p_offset, const Vector<uint8_t> &p_data);
+ void surface_update_vertex_region(int p_surface, int p_offset, const Vector<uint8_t> &p_data);
+ void surface_update_attribute_region(int p_surface, int p_offset, const Vector<uint8_t> &p_data);
+ void surface_update_skin_region(int p_surface, int p_offset, const Vector<uint8_t> &p_data);
int get_surface_count() const override;
@@ -233,13 +262,16 @@ public:
AABB get_aabb() const override;
virtual RID get_rid() const override;
- void regen_normalmaps();
+ void regen_normal_maps();
- Error lightmap_unwrap(const Transform &p_base_transform = Transform(), float p_texel_size = 0.05);
- Error lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache, const Transform &p_base_transform = Transform(), float p_texel_size = 0.05);
+ Error lightmap_unwrap(const Transform3D &p_base_transform = Transform3D(), float p_texel_size = 0.05);
+ Error lightmap_unwrap_cached(const Transform3D &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache, bool p_generate_cache = true);
virtual void reload_from_file() override;
+ void set_shadow_mesh(const Ref<ArrayMesh> &p_mesh);
+ Ref<ArrayMesh> get_shadow_mesh() const;
+
ArrayMesh();
~ArrayMesh();
@@ -247,6 +279,7 @@ public:
VARIANT_ENUM_CAST(Mesh::ArrayType);
VARIANT_ENUM_CAST(Mesh::ArrayFormat);
+VARIANT_ENUM_CAST(Mesh::ArrayCustomFormat);
VARIANT_ENUM_CAST(Mesh::PrimitiveType);
VARIANT_ENUM_CAST(Mesh::BlendShapeMode);
diff --git a/scene/resources/mesh_data_tool.cpp b/scene/resources/mesh_data_tool.cpp
index a5c360f123..04b2437ae8 100644
--- a/scene/resources/mesh_data_tool.cpp
+++ b/scene/resources/mesh_data_tool.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -43,13 +43,35 @@ Error MeshDataTool::create_from_surface(const Ref<ArrayMesh> &p_mesh, int p_surf
ERR_FAIL_COND_V(p_mesh->surface_get_primitive_type(p_surface) != Mesh::PRIMITIVE_TRIANGLES, ERR_INVALID_PARAMETER);
Array arrays = p_mesh->surface_get_arrays(p_surface);
- ERR_FAIL_COND_V(arrays.empty(), ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(arrays.is_empty(), ERR_INVALID_PARAMETER);
Vector<Vector3> varray = arrays[Mesh::ARRAY_VERTEX];
int vcount = varray.size();
ERR_FAIL_COND_V(vcount == 0, ERR_INVALID_PARAMETER);
+ Vector<int> indices;
+
+ if (arrays[Mesh::ARRAY_INDEX].get_type() != Variant::NIL) {
+ indices = arrays[Mesh::ARRAY_INDEX];
+ } else {
+ //make code simpler
+ indices.resize(vcount);
+ int *iw = indices.ptrw();
+ for (int i = 0; i < vcount; i++) {
+ iw[i] = i;
+ }
+ }
+
+ int icount = indices.size();
+ const int *r = indices.ptr();
+
+ ERR_FAIL_COND_V(icount == 0, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(icount % 3, ERR_INVALID_PARAMETER);
+ for (int i = 0; i < icount; i++) {
+ ERR_FAIL_INDEX_V(r[i], vcount, ERR_INVALID_PARAMETER);
+ }
+
clear();
format = p_mesh->surface_get_format(p_surface);
material = p_mesh->surface_get_material(p_surface);
@@ -85,9 +107,9 @@ Error MeshDataTool::create_from_surface(const Ref<ArrayMesh> &p_mesh, int p_surf
bo = arrays[Mesh::ARRAY_BONES].operator Vector<int>().ptr();
}
- const real_t *we = nullptr;
+ const float *we = nullptr;
if (arrays[Mesh::ARRAY_WEIGHTS].get_type() != Variant::NIL) {
- we = arrays[Mesh::ARRAY_WEIGHTS].operator Vector<real_t>().ptr();
+ we = arrays[Mesh::ARRAY_WEIGHTS].operator Vector<float>().ptr();
}
vertices.resize(vcount);
@@ -128,22 +150,6 @@ Error MeshDataTool::create_from_surface(const Ref<ArrayMesh> &p_mesh, int p_surf
vertices.write[i] = v;
}
- Vector<int> indices;
-
- if (arrays[Mesh::ARRAY_INDEX].get_type() != Variant::NIL) {
- indices = arrays[Mesh::ARRAY_INDEX];
- } else {
- //make code simpler
- indices.resize(vcount);
- int *iw = indices.ptrw();
- for (int i = 0; i < vcount; i++) {
- iw[i] = i;
- }
- }
-
- int icount = indices.size();
- const int *r = indices.ptr();
-
Map<Point2i, int> edge_indices;
for (int i = 0; i < icount; i += 3) {
diff --git a/scene/resources/mesh_data_tool.h b/scene/resources/mesh_data_tool.h
index bf9f0dd25f..b0ebfba7ee 100644
--- a/scene/resources/mesh_data_tool.h
+++ b/scene/resources/mesh_data_tool.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,10 +33,10 @@
#include "scene/resources/mesh.h"
-class MeshDataTool : public Reference {
- GDCLASS(MeshDataTool, Reference);
+class MeshDataTool : public RefCounted {
+ GDCLASS(MeshDataTool, RefCounted);
- int format;
+ int format = 0;
struct Vertex {
Vector3 vertex;
Color color;
@@ -54,7 +54,7 @@ class MeshDataTool : public Reference {
Vector<Vertex> vertices;
struct Edge {
- int vertex[2];
+ int vertex[2] = {};
Vector<int> faces;
Variant meta;
};
@@ -62,8 +62,8 @@ class MeshDataTool : public Reference {
Vector<Edge> edges;
struct Face {
- int v[3];
- int edges[3];
+ int v[3] = {};
+ int edges[3] = {};
Variant meta;
};
diff --git a/scene/resources/mesh_library.cpp b/scene/resources/mesh_library.cpp
index 09b0d4b038..33c9ca6d1e 100644
--- a/scene/resources/mesh_library.cpp
+++ b/scene/resources/mesh_library.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -97,10 +97,10 @@ void MeshLibrary::_get_property_list(List<PropertyInfo> *p_list) const {
String name = "item/" + itos(E->key()) + "/";
p_list->push_back(PropertyInfo(Variant::STRING, name + "name"));
p_list->push_back(PropertyInfo(Variant::OBJECT, name + "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"));
- p_list->push_back(PropertyInfo(Variant::TRANSFORM, name + "mesh_transform"));
+ p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, name + "mesh_transform"));
p_list->push_back(PropertyInfo(Variant::ARRAY, name + "shapes"));
p_list->push_back(PropertyInfo(Variant::OBJECT, name + "navmesh", PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh"));
- p_list->push_back(PropertyInfo(Variant::TRANSFORM, name + "navmesh_transform"));
+ p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, name + "navmesh_transform"));
p_list->push_back(PropertyInfo(Variant::OBJECT, name + "preview", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_HELPER));
}
}
@@ -109,14 +109,14 @@ void MeshLibrary::create_item(int p_item) {
ERR_FAIL_COND(p_item < 0);
ERR_FAIL_COND(item_map.has(p_item));
item_map[p_item] = Item();
- _change_notify();
+ notify_property_list_changed();
}
void MeshLibrary::set_item_name(int p_item, const String &p_name) {
ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
item_map[p_item].name = p_name;
emit_changed();
- _change_notify();
+ notify_property_list_changed();
}
void MeshLibrary::set_item_mesh(int p_item, const Ref<Mesh> &p_mesh) {
@@ -124,40 +124,40 @@ void MeshLibrary::set_item_mesh(int p_item, const Ref<Mesh> &p_mesh) {
item_map[p_item].mesh = p_mesh;
notify_change_to_owners();
emit_changed();
- _change_notify();
+ notify_property_list_changed();
}
void MeshLibrary::set_item_shapes(int p_item, const Vector<ShapeData> &p_shapes) {
ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
item_map[p_item].shapes = p_shapes;
- _change_notify();
+ notify_property_list_changed();
notify_change_to_owners();
emit_changed();
- _change_notify();
+ notify_property_list_changed();
}
void MeshLibrary::set_item_navmesh(int p_item, const Ref<NavigationMesh> &p_navmesh) {
ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
item_map[p_item].navmesh = p_navmesh;
- _change_notify();
+ notify_property_list_changed();
notify_change_to_owners();
emit_changed();
- _change_notify();
+ notify_property_list_changed();
}
-void MeshLibrary::set_item_navmesh_transform(int p_item, const Transform &p_transform) {
+void MeshLibrary::set_item_navmesh_transform(int p_item, const Transform3D &p_transform) {
ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
item_map[p_item].navmesh_transform = p_transform;
notify_change_to_owners();
emit_changed();
- _change_notify();
+ notify_property_list_changed();
}
void MeshLibrary::set_item_preview(int p_item, const Ref<Texture2D> &p_preview) {
ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
item_map[p_item].preview = p_preview;
emit_changed();
- _change_notify();
+ notify_property_list_changed();
}
String MeshLibrary::get_item_name(int p_item) const {
@@ -180,8 +180,8 @@ Ref<NavigationMesh> MeshLibrary::get_item_navmesh(int p_item) const {
return item_map[p_item].navmesh;
}
-Transform MeshLibrary::get_item_navmesh_transform(int p_item) const {
- ERR_FAIL_COND_V_MSG(!item_map.has(p_item), Transform(), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
+Transform3D MeshLibrary::get_item_navmesh_transform(int p_item) const {
+ ERR_FAIL_COND_V_MSG(!item_map.has(p_item), Transform3D(), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
return item_map[p_item].navmesh_transform;
}
@@ -198,14 +198,14 @@ void MeshLibrary::remove_item(int p_item) {
ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
item_map.erase(p_item);
notify_change_to_owners();
- _change_notify();
+ notify_property_list_changed();
emit_changed();
}
void MeshLibrary::clear() {
item_map.clear();
notify_change_to_owners();
- _change_notify();
+ notify_property_list_changed();
emit_changed();
}
@@ -264,6 +264,9 @@ Array MeshLibrary::_get_item_shapes(int p_item) const {
return ret;
}
+void MeshLibrary::reset_state() {
+ clear();
+}
void MeshLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("create_item", "id"), &MeshLibrary::create_item);
ClassDB::bind_method(D_METHOD("set_item_name", "id", "name"), &MeshLibrary::set_item_name);
diff --git a/scene/resources/mesh_library.h b/scene/resources/mesh_library.h
index 450e2c16e3..1e8a6bf3ff 100644
--- a/scene/resources/mesh_library.h
+++ b/scene/resources/mesh_library.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,8 +31,8 @@
#ifndef MESH_LIBRARY_H
#define MESH_LIBRARY_H
-#include "core/map.h"
-#include "core/resource.h"
+#include "core/io/resource.h"
+#include "core/templates/map.h"
#include "mesh.h"
#include "scene/3d/navigation_region_3d.h"
#include "shape_3d.h"
@@ -44,14 +44,14 @@ class MeshLibrary : public Resource {
public:
struct ShapeData {
Ref<Shape3D> shape;
- Transform local_transform;
+ Transform3D local_transform;
};
struct Item {
String name;
Ref<Mesh> mesh;
Vector<ShapeData> shapes;
Ref<Texture2D> preview;
- Transform navmesh_transform;
+ Transform3D navmesh_transform;
Ref<NavigationMesh> navmesh;
};
@@ -65,6 +65,7 @@ protected:
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
+ virtual void reset_state() override;
static void _bind_methods();
public:
@@ -72,13 +73,13 @@ public:
void set_item_name(int p_item, const String &p_name);
void set_item_mesh(int p_item, const Ref<Mesh> &p_mesh);
void set_item_navmesh(int p_item, const Ref<NavigationMesh> &p_navmesh);
- void set_item_navmesh_transform(int p_item, const Transform &p_transform);
+ void set_item_navmesh_transform(int p_item, const Transform3D &p_transform);
void set_item_shapes(int p_item, const Vector<ShapeData> &p_shapes);
void set_item_preview(int p_item, const Ref<Texture2D> &p_preview);
String get_item_name(int p_item) const;
Ref<Mesh> get_item_mesh(int p_item) const;
Ref<NavigationMesh> get_item_navmesh(int p_item) const;
- Transform get_item_navmesh_transform(int p_item) const;
+ Transform3D get_item_navmesh_transform(int p_item) const;
Vector<ShapeData> get_item_shapes(int p_item) const;
Ref<Texture2D> get_item_preview(int p_item) const;
diff --git a/scene/resources/multimesh.cpp b/scene/resources/multimesh.cpp
index f71cf383e5..8894f0bb11 100644
--- a/scene/resources/multimesh.cpp
+++ b/scene/resources/multimesh.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -50,7 +50,7 @@ void MultiMesh::_set_transform_array(const Vector<Vector3> &p_array) {
const Vector3 *r = xforms.ptr();
for (int i = 0; i < len / 4; i++) {
- Transform t;
+ Transform3D t;
t.basis[0] = r[i * 4 + 0];
t.basis[1] = r[i * 4 + 1];
t.basis[2] = r[i * 4 + 2];
@@ -75,7 +75,7 @@ Vector<Vector3> MultiMesh::_get_transform_array() const {
Vector3 *w = xforms.ptrw();
for (int i = 0; i < instance_count; i++) {
- Transform t = get_instance_transform(i);
+ Transform3D t = get_instance_transform(i);
w[i * 4 + 0] = t.basis[0];
w[i * 4 + 1] = t.basis[1];
w[i * 4 + 2] = t.basis[2];
@@ -217,7 +217,7 @@ Ref<Mesh> MultiMesh::get_mesh() const {
void MultiMesh::set_instance_count(int p_count) {
ERR_FAIL_COND(p_count < 0);
- RenderingServer::get_singleton()->multimesh_allocate(multimesh, p_count, RS::MultimeshTransformFormat(transform_format), use_colors, use_custom_data);
+ RenderingServer::get_singleton()->multimesh_allocate_data(multimesh, p_count, RS::MultimeshTransformFormat(transform_format), use_colors, use_custom_data);
instance_count = p_count;
}
@@ -236,7 +236,7 @@ int MultiMesh::get_visible_instance_count() const {
return visible_instance_count;
}
-void MultiMesh::set_instance_transform(int p_instance, const Transform &p_transform) {
+void MultiMesh::set_instance_transform(int p_instance, const Transform3D &p_transform) {
RenderingServer::get_singleton()->multimesh_instance_set_transform(multimesh, p_instance, p_transform);
}
@@ -244,7 +244,7 @@ void MultiMesh::set_instance_transform_2d(int p_instance, const Transform2D &p_t
RenderingServer::get_singleton()->multimesh_instance_set_transform_2d(multimesh, p_instance, p_transform);
}
-Transform MultiMesh::get_instance_transform(int p_instance) const {
+Transform3D MultiMesh::get_instance_transform(int p_instance) const {
return RenderingServer::get_singleton()->multimesh_instance_get_transform(multimesh, p_instance);
}
@@ -349,10 +349,10 @@ void MultiMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_custom_data_array"), &MultiMesh::_set_custom_data_array);
ClassDB::bind_method(D_METHOD("_get_custom_data_array"), &MultiMesh::_get_custom_data_array);
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "transform_array", PROPERTY_HINT_NONE, "", 0), "_set_transform_array", "_get_transform_array");
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "transform_2d_array", PROPERTY_HINT_NONE, "", 0), "_set_transform_2d_array", "_get_transform_2d_array");
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "color_array", PROPERTY_HINT_NONE, "", 0), "_set_color_array", "_get_color_array");
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "custom_data_array", PROPERTY_HINT_NONE, "", 0), "_set_custom_data_array", "_get_custom_data_array");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "transform_array", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "_set_transform_array", "_get_transform_array");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "transform_2d_array", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "_set_transform_2d_array", "_get_transform_2d_array");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "color_array", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "_set_color_array", "_get_color_array");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "custom_data_array", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "_set_custom_data_array", "_get_custom_data_array");
#endif
BIND_ENUM_CONSTANT(TRANSFORM_2D);
@@ -361,11 +361,6 @@ void MultiMesh::_bind_methods() {
MultiMesh::MultiMesh() {
multimesh = RenderingServer::get_singleton()->multimesh_create();
- use_colors = false;
- use_custom_data = false;
- transform_format = TRANSFORM_2D;
- visible_instance_count = -1;
- instance_count = 0;
}
MultiMesh::~MultiMesh() {
diff --git a/scene/resources/multimesh.h b/scene/resources/multimesh.h
index 16f5998a04..2fe0927e6f 100644
--- a/scene/resources/multimesh.h
+++ b/scene/resources/multimesh.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -47,11 +47,11 @@ public:
private:
Ref<Mesh> mesh;
RID multimesh;
- TransformFormat transform_format;
- bool use_colors;
- bool use_custom_data;
- int instance_count;
- int visible_instance_count;
+ TransformFormat transform_format = TRANSFORM_2D;
+ bool use_colors = false;
+ bool use_custom_data = false;
+ int instance_count = 0;
+ int visible_instance_count = -1;
protected:
static void _bind_methods();
@@ -92,9 +92,9 @@ public:
void set_visible_instance_count(int p_count);
int get_visible_instance_count() const;
- void set_instance_transform(int p_instance, const Transform &p_transform);
+ void set_instance_transform(int p_instance, const Transform3D &p_transform);
void set_instance_transform_2d(int p_instance, const Transform2D &p_transform);
- Transform get_instance_transform(int p_instance) const;
+ Transform3D get_instance_transform(int p_instance) const;
Transform2D get_instance_transform_2d(int p_instance) const;
void set_instance_color(int p_instance, const Color &p_color);
diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp
index e815da5d45..00cee9269b 100644
--- a/scene/resources/navigation_mesh.cpp
+++ b/scene/resources/navigation_mesh.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,6 +31,8 @@
#include "navigation_mesh.h"
void NavigationMesh::create_from_mesh(const Ref<Mesh> &p_mesh) {
+ ERR_FAIL_COND(p_mesh.is_null());
+
vertices = Vector<Vector3>();
clear_polygons();
@@ -62,22 +64,22 @@ void NavigationMesh::create_from_mesh(const Ref<Mesh> &p_mesh) {
}
}
-void NavigationMesh::set_sample_partition_type(int p_value) {
- ERR_FAIL_COND(p_value >= SAMPLE_PARTITION_MAX);
- partition_type = static_cast<SamplePartitionType>(p_value);
+void NavigationMesh::set_sample_partition_type(SamplePartitionType p_value) {
+ ERR_FAIL_INDEX(p_value, SAMPLE_PARTITION_MAX);
+ partition_type = p_value;
}
-int NavigationMesh::get_sample_partition_type() const {
- return static_cast<int>(partition_type);
+NavigationMesh::SamplePartitionType NavigationMesh::get_sample_partition_type() const {
+ return partition_type;
}
-void NavigationMesh::set_parsed_geometry_type(int p_value) {
- ERR_FAIL_COND(p_value >= PARSED_GEOMETRY_MAX);
- parsed_geometry_type = static_cast<ParsedGeometryType>(p_value);
- _change_notify();
+void NavigationMesh::set_parsed_geometry_type(ParsedGeometryType p_value) {
+ ERR_FAIL_INDEX(p_value, PARSED_GEOMETRY_MAX);
+ parsed_geometry_type = p_value;
+ notify_property_list_changed();
}
-int NavigationMesh::get_parsed_geometry_type() const {
+NavigationMesh::ParsedGeometryType NavigationMesh::get_parsed_geometry_type() const {
return parsed_geometry_type;
}
@@ -89,27 +91,31 @@ uint32_t NavigationMesh::get_collision_mask() const {
return collision_mask;
}
-void NavigationMesh::set_collision_mask_bit(int p_bit, bool p_value) {
+void NavigationMesh::set_collision_mask_value(int p_layer_number, bool p_value) {
+ ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive.");
uint32_t mask = get_collision_mask();
if (p_value) {
- mask |= 1 << p_bit;
+ mask |= 1 << (p_layer_number - 1);
} else {
- mask &= ~(1 << p_bit);
+ mask &= ~(1 << (p_layer_number - 1));
}
set_collision_mask(mask);
}
-bool NavigationMesh::get_collision_mask_bit(int p_bit) const {
- return get_collision_mask() & (1 << p_bit);
+bool NavigationMesh::get_collision_mask_value(int p_layer_number) const {
+ ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive.");
+ return get_collision_mask() & (1 << (p_layer_number - 1));
}
-void NavigationMesh::set_source_geometry_mode(int p_geometry_mode) {
+void NavigationMesh::set_source_geometry_mode(SourceGeometryMode p_geometry_mode) {
ERR_FAIL_INDEX(p_geometry_mode, SOURCE_GEOMETRY_MAX);
- source_geometry_mode = static_cast<SourceGeometryMode>(p_geometry_mode);
- _change_notify();
+ source_geometry_mode = p_geometry_mode;
+ notify_property_list_changed();
}
-int NavigationMesh::get_source_geometry_mode() const {
+NavigationMesh::SourceGeometryMode NavigationMesh::get_source_geometry_mode() const {
return source_geometry_mode;
}
@@ -122,6 +128,7 @@ StringName NavigationMesh::get_source_group_name() const {
}
void NavigationMesh::set_cell_size(float p_value) {
+ ERR_FAIL_COND(p_value <= 0);
cell_size = p_value;
}
@@ -130,6 +137,7 @@ float NavigationMesh::get_cell_size() const {
}
void NavigationMesh::set_cell_height(float p_value) {
+ ERR_FAIL_COND(p_value <= 0);
cell_height = p_value;
}
@@ -138,6 +146,7 @@ float NavigationMesh::get_cell_height() const {
}
void NavigationMesh::set_agent_height(float p_value) {
+ ERR_FAIL_COND(p_value < 0);
agent_height = p_value;
}
@@ -146,6 +155,7 @@ float NavigationMesh::get_agent_height() const {
}
void NavigationMesh::set_agent_radius(float p_value) {
+ ERR_FAIL_COND(p_value < 0);
agent_radius = p_value;
}
@@ -154,6 +164,7 @@ float NavigationMesh::get_agent_radius() {
}
void NavigationMesh::set_agent_max_climb(float p_value) {
+ ERR_FAIL_COND(p_value < 0);
agent_max_climb = p_value;
}
@@ -162,6 +173,7 @@ float NavigationMesh::get_agent_max_climb() const {
}
void NavigationMesh::set_agent_max_slope(float p_value) {
+ ERR_FAIL_COND(p_value < 0 || p_value > 90);
agent_max_slope = p_value;
}
@@ -170,6 +182,7 @@ float NavigationMesh::get_agent_max_slope() const {
}
void NavigationMesh::set_region_min_size(float p_value) {
+ ERR_FAIL_COND(p_value < 0);
region_min_size = p_value;
}
@@ -178,6 +191,7 @@ float NavigationMesh::get_region_min_size() const {
}
void NavigationMesh::set_region_merge_size(float p_value) {
+ ERR_FAIL_COND(p_value < 0);
region_merge_size = p_value;
}
@@ -186,6 +200,7 @@ float NavigationMesh::get_region_merge_size() const {
}
void NavigationMesh::set_edge_max_length(float p_value) {
+ ERR_FAIL_COND(p_value < 0);
edge_max_length = p_value;
}
@@ -194,6 +209,7 @@ float NavigationMesh::get_edge_max_length() const {
}
void NavigationMesh::set_edge_max_error(float p_value) {
+ ERR_FAIL_COND(p_value < 0);
edge_max_error = p_value;
}
@@ -202,6 +218,7 @@ float NavigationMesh::get_edge_max_error() const {
}
void NavigationMesh::set_verts_per_poly(float p_value) {
+ ERR_FAIL_COND(p_value < 3);
verts_per_poly = p_value;
}
@@ -210,6 +227,7 @@ float NavigationMesh::get_verts_per_poly() const {
}
void NavigationMesh::set_detail_sample_distance(float p_value) {
+ ERR_FAIL_COND(p_value < 0);
detail_sample_distance = p_value;
}
@@ -218,6 +236,7 @@ float NavigationMesh::get_detail_sample_distance() const {
}
void NavigationMesh::set_detail_sample_max_error(float p_value) {
+ ERR_FAIL_COND(p_value < 0);
detail_sample_max_error = p_value;
}
@@ -251,7 +270,7 @@ bool NavigationMesh::get_filter_walkable_low_height_spans() const {
void NavigationMesh::set_vertices(const Vector<Vector3> &p_vertices) {
vertices = p_vertices;
- _change_notify();
+ notify_property_list_changed();
}
Vector<Vector3> NavigationMesh::get_vertices() const {
@@ -263,7 +282,7 @@ void NavigationMesh::_set_polygons(const Array &p_array) {
for (int i = 0; i < p_array.size(); i++) {
polygons.write[i].indices = p_array[i];
}
- _change_notify();
+ notify_property_list_changed();
}
Array NavigationMesh::_get_polygons() const {
@@ -280,7 +299,7 @@ void NavigationMesh::add_polygon(const Vector<int> &p_polygon) {
Polygon polygon;
polygon.indices = p_polygon;
polygons.push_back(polygon);
- _change_notify();
+ notify_property_list_changed();
}
int NavigationMesh::get_polygon_count() const {
@@ -325,9 +344,7 @@ Ref<Mesh> NavigationMesh::get_debug_mesh() {
Vector3 *tw = tmeshfaces.ptrw();
int tidx = 0;
- for (List<Face3>::Element *E = faces.front(); E; E = E->next()) {
- const Face3 &f = E->get();
-
+ for (const Face3 &f : faces) {
for (int j = 0; j < 3; j++) {
tw[tidx++] = f.vertex[j];
_EdgeKey ek;
@@ -362,8 +379,8 @@ Ref<Mesh> NavigationMesh::get_debug_mesh() {
{
Vector3 *w = varr.ptrw();
int idx = 0;
- for (List<Vector3>::Element *E = lines.front(); E; E = E->next()) {
- w[idx++] = E->get();
+ for (const Vector3 &E : lines) {
+ w[idx++] = E;
}
}
@@ -388,8 +405,8 @@ void NavigationMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &NavigationMesh::set_collision_mask);
ClassDB::bind_method(D_METHOD("get_collision_mask"), &NavigationMesh::get_collision_mask);
- ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &NavigationMesh::set_collision_mask_bit);
- ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &NavigationMesh::get_collision_mask_bit);
+ ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &NavigationMesh::set_collision_mask_value);
+ ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &NavigationMesh::get_collision_mask_value);
ClassDB::bind_method(D_METHOD("set_source_geometry_mode", "mask"), &NavigationMesh::set_source_geometry_mode);
ClassDB::bind_method(D_METHOD("get_source_geometry_mode"), &NavigationMesh::get_source_geometry_mode);
@@ -458,14 +475,6 @@ void NavigationMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_polygons", "polygons"), &NavigationMesh::_set_polygons);
ClassDB::bind_method(D_METHOD("_get_polygons"), &NavigationMesh::_get_polygons);
- BIND_CONSTANT(SAMPLE_PARTITION_WATERSHED);
- BIND_CONSTANT(SAMPLE_PARTITION_MONOTONE);
- BIND_CONSTANT(SAMPLE_PARTITION_LAYERS);
-
- BIND_CONSTANT(PARSED_GEOMETRY_MESH_INSTANCES);
- BIND_CONSTANT(PARSED_GEOMETRY_STATIC_COLLIDERS);
- BIND_CONSTANT(PARSED_GEOMETRY_BOTH);
-
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons");
@@ -492,45 +501,37 @@ void NavigationMesh::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter/low_hanging_obstacles"), "set_filter_low_hanging_obstacles", "get_filter_low_hanging_obstacles");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter/ledge_spans"), "set_filter_ledge_spans", "get_filter_ledge_spans");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter/filter_walkable_low_height_spans"), "set_filter_walkable_low_height_spans", "get_filter_walkable_low_height_spans");
+
+ BIND_ENUM_CONSTANT(SAMPLE_PARTITION_WATERSHED);
+ BIND_ENUM_CONSTANT(SAMPLE_PARTITION_MONOTONE);
+ BIND_ENUM_CONSTANT(SAMPLE_PARTITION_LAYERS);
+ BIND_ENUM_CONSTANT(SAMPLE_PARTITION_MAX);
+
+ BIND_ENUM_CONSTANT(PARSED_GEOMETRY_MESH_INSTANCES);
+ BIND_ENUM_CONSTANT(PARSED_GEOMETRY_STATIC_COLLIDERS);
+ BIND_ENUM_CONSTANT(PARSED_GEOMETRY_BOTH);
+ BIND_ENUM_CONSTANT(PARSED_GEOMETRY_MAX);
+
+ BIND_ENUM_CONSTANT(SOURCE_GEOMETRY_NAVMESH_CHILDREN);
+ BIND_ENUM_CONSTANT(SOURCE_GEOMETRY_GROUPS_WITH_CHILDREN);
+ BIND_ENUM_CONSTANT(SOURCE_GEOMETRY_GROUPS_EXPLICIT);
+ BIND_ENUM_CONSTANT(SOURCE_GEOMETRY_MAX);
}
void NavigationMesh::_validate_property(PropertyInfo &property) const {
if (property.name == "geometry/collision_mask") {
if (parsed_geometry_type == PARSED_GEOMETRY_MESH_INSTANCES) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
return;
}
}
if (property.name == "geometry/source_group_name") {
if (source_geometry_mode == SOURCE_GEOMETRY_NAVMESH_CHILDREN) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
return;
}
}
}
-NavigationMesh::NavigationMesh() {
- cell_size = 0.3f;
- cell_height = 0.2f;
- agent_height = 2.0f;
- agent_radius = 0.6f;
- agent_max_climb = 0.9f;
- agent_max_slope = 45.0f;
- region_min_size = 8.0f;
- region_merge_size = 20.0f;
- edge_max_length = 12.0f;
- edge_max_error = 1.3f;
- verts_per_poly = 6.0f;
- detail_sample_distance = 6.0f;
- detail_sample_max_error = 1.0f;
-
- partition_type = SAMPLE_PARTITION_WATERSHED;
- parsed_geometry_type = PARSED_GEOMETRY_MESH_INSTANCES;
- collision_mask = 0xFFFFFFFF;
- source_geometry_mode = SOURCE_GEOMETRY_NAVMESH_CHILDREN;
- source_group_name = "navmesh";
- filter_low_hanging_obstacles = false;
- filter_ledge_spans = false;
- filter_walkable_low_height_spans = false;
-}
+NavigationMesh::NavigationMesh() {}
diff --git a/scene/resources/navigation_mesh.h b/scene/resources/navigation_mesh.h
index b94f4408e1..1cdf7a07ed 100644
--- a/scene/resources/navigation_mesh.h
+++ b/scene/resources/navigation_mesh.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -82,47 +82,47 @@ public:
};
protected:
- float cell_size;
- float cell_height;
- float agent_height;
- float agent_radius;
- float agent_max_climb;
- float agent_max_slope;
- float region_min_size;
- float region_merge_size;
- float edge_max_length;
- float edge_max_error;
- float verts_per_poly;
- float detail_sample_distance;
- float detail_sample_max_error;
-
- SamplePartitionType partition_type;
- ParsedGeometryType parsed_geometry_type;
- uint32_t collision_mask;
-
- SourceGeometryMode source_geometry_mode;
- StringName source_group_name;
-
- bool filter_low_hanging_obstacles;
- bool filter_ledge_spans;
- bool filter_walkable_low_height_spans;
+ float cell_size = 0.3f;
+ float cell_height = 0.2f;
+ float agent_height = 2.0f;
+ float agent_radius = 0.6f;
+ float agent_max_climb = 0.9f;
+ float agent_max_slope = 45.0f;
+ float region_min_size = 8.0f;
+ float region_merge_size = 20.0f;
+ float edge_max_length = 12.0f;
+ float edge_max_error = 1.3f;
+ float verts_per_poly = 6.0f;
+ float detail_sample_distance = 6.0f;
+ float detail_sample_max_error = 1.0f;
+
+ SamplePartitionType partition_type = SAMPLE_PARTITION_WATERSHED;
+ ParsedGeometryType parsed_geometry_type = PARSED_GEOMETRY_MESH_INSTANCES;
+ uint32_t collision_mask = 0xFFFFFFFF;
+
+ SourceGeometryMode source_geometry_mode = SOURCE_GEOMETRY_NAVMESH_CHILDREN;
+ StringName source_group_name = "navmesh";
+
+ bool filter_low_hanging_obstacles = false;
+ bool filter_ledge_spans = false;
+ bool filter_walkable_low_height_spans = false;
public:
// Recast settings
- void set_sample_partition_type(int p_value);
- int get_sample_partition_type() const;
+ void set_sample_partition_type(SamplePartitionType p_value);
+ SamplePartitionType get_sample_partition_type() const;
- void set_parsed_geometry_type(int p_value);
- int get_parsed_geometry_type() const;
+ void set_parsed_geometry_type(ParsedGeometryType p_value);
+ ParsedGeometryType get_parsed_geometry_type() const;
void set_collision_mask(uint32_t p_mask);
uint32_t get_collision_mask() const;
- void set_collision_mask_bit(int p_bit, bool p_value);
- bool get_collision_mask_bit(int p_bit) const;
+ void set_collision_mask_value(int p_layer_number, bool p_value);
+ bool get_collision_mask_value(int p_layer_number) const;
- void set_source_geometry_mode(int p_geometry_mode);
- int get_source_geometry_mode() const;
+ void set_source_geometry_mode(SourceGeometryMode p_geometry_mode);
+ SourceGeometryMode get_source_geometry_mode() const;
void set_source_group_name(StringName p_group_name);
StringName get_source_group_name() const;
@@ -190,4 +190,8 @@ public:
NavigationMesh();
};
+VARIANT_ENUM_CAST(NavigationMesh::SamplePartitionType);
+VARIANT_ENUM_CAST(NavigationMesh::ParsedGeometryType);
+VARIANT_ENUM_CAST(NavigationMesh::SourceGeometryMode);
+
#endif // NAVIGATION_MESH_H
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index 5e8bfd9387..e74f759855 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,10 +30,10 @@
#include "packed_scene.h"
+#include "core/config/engine.h"
+#include "core/config/project_settings.h"
#include "core/core_string_names.h"
-#include "core/engine.h"
#include "core/io/resource_loader.h"
-#include "core/project_settings.h"
#include "scene/2d/node_2d.h"
#include "scene/3d/node_3d.h"
#include "scene/gui/control.h"
@@ -41,11 +41,11 @@
#define PACKED_SCENE_VERSION 2
-bool SceneState::can_instance() const {
+bool SceneState::can_instantiate() const {
return nodes.size() > 0;
}
-Node *SceneState::instance(GenEditState p_edit_state) const {
+Node *SceneState::instantiate(GenEditState p_edit_state) const {
// nodes where instancing failed (because something is missing)
List<Node *> stray_instances;
@@ -80,7 +80,7 @@ Node *SceneState::instance(GenEditState p_edit_state) const {
Node **ret_nodes = (Node **)alloca(sizeof(Node *) * nc);
- bool gen_node_path_cache = p_edit_state != GEN_EDIT_STATE_DISABLED && node_path_cache.empty();
+ bool gen_node_path_cache = p_edit_state != GEN_EDIT_STATE_DISABLED && node_path_cache.is_empty();
Map<Ref<Resource>, Ref<Resource>> resources_local_to_scene;
@@ -109,7 +109,7 @@ Node *SceneState::instance(GenEditState p_edit_state) const {
//scene inheritance on root node
Ref<PackedScene> sdata = props[base_scene_idx];
ERR_FAIL_COND_V(!sdata.is_valid(), nullptr);
- node = sdata->instance(p_edit_state == GEN_EDIT_STATE_DISABLED ? PackedScene::GEN_EDIT_STATE_DISABLED : PackedScene::GEN_EDIT_STATE_INSTANCE); //only main gets main edit state
+ node = sdata->instantiate(p_edit_state == GEN_EDIT_STATE_DISABLED ? PackedScene::GEN_EDIT_STATE_DISABLED : PackedScene::GEN_EDIT_STATE_INSTANCE); //only main gets main edit state
ERR_FAIL_COND_V(!node, nullptr);
if (p_edit_state != GEN_EDIT_STATE_DISABLED) {
node->set_scene_inherited_state(sdata->get_state());
@@ -122,7 +122,7 @@ Node *SceneState::instance(GenEditState p_edit_state) const {
if (disable_placeholders) {
Ref<PackedScene> sdata = ResourceLoader::load(path, "PackedScene");
ERR_FAIL_COND_V(!sdata.is_valid(), nullptr);
- node = sdata->instance(p_edit_state == GEN_EDIT_STATE_DISABLED ? PackedScene::GEN_EDIT_STATE_DISABLED : PackedScene::GEN_EDIT_STATE_INSTANCE);
+ node = sdata->instantiate(p_edit_state == GEN_EDIT_STATE_DISABLED ? PackedScene::GEN_EDIT_STATE_DISABLED : PackedScene::GEN_EDIT_STATE_INSTANCE);
ERR_FAIL_COND_V(!node, nullptr);
} else {
InstancePlaceholder *ip = memnew(InstancePlaceholder);
@@ -133,7 +133,7 @@ Node *SceneState::instance(GenEditState p_edit_state) const {
} else {
Ref<PackedScene> sdata = props[n.instance & FLAG_MASK];
ERR_FAIL_COND_V(!sdata.is_valid(), nullptr);
- node = sdata->instance(p_edit_state == GEN_EDIT_STATE_DISABLED ? PackedScene::GEN_EDIT_STATE_DISABLED : PackedScene::GEN_EDIT_STATE_INSTANCE);
+ node = sdata->instantiate(p_edit_state == GEN_EDIT_STATE_DISABLED ? PackedScene::GEN_EDIT_STATE_DISABLED : PackedScene::GEN_EDIT_STATE_INSTANCE);
ERR_FAIL_COND_V(!node, nullptr);
}
@@ -147,22 +147,29 @@ Node *SceneState::instance(GenEditState p_edit_state) const {
}
#endif
}
- } else if (ClassDB::is_class_enabled(snames[n.type])) {
- //node belongs to this scene and must be created
- Object *obj = ClassDB::instance(snames[n.type]);
+ } else {
+ Object *obj = nullptr;
+
+ if (ClassDB::is_class_enabled(snames[n.type])) {
+ //node belongs to this scene and must be created
+ obj = ClassDB::instantiate(snames[n.type]);
+ }
+
if (!Object::cast_to<Node>(obj)) {
if (obj) {
memdelete(obj);
obj = nullptr;
}
- WARN_PRINT(String("Warning node of type " + snames[n.type].operator String() + " does not exist.").ascii().get_data());
+ 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
}
}
@@ -172,14 +179,10 @@ Node *SceneState::instance(GenEditState p_edit_state) const {
}
node = Object::cast_to<Node>(obj);
-
- } else {
- //print_line("Class is disabled for: " + itos(n.type));
- //print_line("name: " + String(snames[n.type]));
}
if (node) {
- // may not have found the node (part of instanced scene and removed)
+ // may not have found the node (part of instantiated scene and removed)
// if found all is good, otherwise ignore
//properties
@@ -205,8 +208,8 @@ Node *SceneState::instance(GenEditState p_edit_state) const {
node->set(snames[nprops[j].name], props[nprops[j].value], &valid);
//restore old state for new script, if exists
- for (List<Pair<StringName, Variant>>::Element *E = old_state.front(); E; E = E->next()) {
- node->set(E->get().first, E->get().second);
+ for (const Pair<StringName, Variant> &E : old_state) {
+ node->set(E.first, E.second);
}
} else {
Variant value = props[nprops[j].value];
@@ -265,7 +268,7 @@ Node *SceneState::instance(GenEditState p_edit_state) const {
parent->move_child(node, n.index);
}
} else {
- //it may be possible that an instanced scene has changed
+ //it may be possible that an instantiated scene has changed
//and the node has nowhere to go anymore
stray_instances.push_back(node); //can't be added, go to stray list
}
@@ -367,7 +370,7 @@ static int _vm_get_variant(const Variant &p_variant, HashMap<Variant, int, Varia
Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, Map<Node *, int> &node_map, Map<Node *, int> &nodepath_map) {
// this function handles all the work related to properly packing scenes, be it
- // instanced or inherited.
+ // instantiated or inherited.
// given the complexity of this process, an attempt will be made to properly
// document it. if you fail to understand something, please ask!
@@ -376,16 +379,23 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
return OK;
}
- // save the child instanced scenes that are chosen as editable, so they can be restored
+ bool is_editable_instance = false;
+
+ // save the child instantiated scenes that are chosen as editable, so they can be restored
// upon load back
if (p_node != p_owner && p_node->get_filename() != String() && p_owner->is_editable_instance(p_node)) {
editable_instances.push_back(p_owner->get_path_to(p_node));
+ // Node is the root of an editable instance.
+ is_editable_instance = true;
+ } else if (p_node->get_owner() && p_node->get_owner() != p_owner && p_owner->is_editable_instance(p_node->get_owner())) {
+ // Node is part of an editable instance.
+ is_editable_instance = true;
}
NodeData nd;
nd.name = _nm_get_string(p_node->get_name(), name_map);
- nd.instance = -1; //not instanced by default
+ nd.instance = -1; //not instantiated by default
//really convoluted condition, but it basically checks that index is only saved when part of an inherited scene OR the node parent is from the edited scene
if (p_owner->get_scene_inherited_state().is_null() && (p_node == p_owner || (p_node->get_owner() == p_owner && (p_node->get_parent() == p_owner || p_node->get_parent()->get_owner() == p_owner)))) {
@@ -395,18 +405,18 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
//This (hopefully) happens if the node is a scene root, so its index is irrelevant.
nd.index = -1;
} else {
- //part of an inherited scene, or parent is from an instanced scene
+ //part of an inherited scene, or parent is from an instantiated scene
nd.index = p_node->get_index();
}
- // if this node is part of an instanced scene or sub-instanced scene
+ // if this node is part of an instantiated scene or sub-instantiated scene
// we need to get the corresponding instance states.
// with the instance states, we can query for identical properties/groups
// and only save what has changed
List<PackState> pack_state_stack;
- bool instanced_by_owner = true;
+ bool instantiated_by_owner = true;
{
Node *n = p_node;
@@ -422,11 +432,11 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
ps.node = node;
ps.state = state;
pack_state_stack.push_back(ps);
- instanced_by_owner = false;
+ instantiated_by_owner = false;
}
}
- if (p_node->get_filename() != String() && p_node->get_owner() == p_owner && instanced_by_owner) {
+ if (p_node->get_filename() != String() && p_node->get_owner() == p_owner && instantiated_by_owner) {
if (p_node->get_scene_instance_load_placeholder()) {
//it's a placeholder, use the placeholder path
nd.instance = _vm_get_variant(p_node->get_filename(), variant_map);
@@ -469,13 +479,20 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
p_node->get_property_list(&plist);
StringName type = p_node->get_class();
- for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
- if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) {
+ Ref<Script> script = p_node->get_script();
+ if (Engine::get_singleton()->is_editor_hint() && script.is_valid()) {
+ // Should be called in the editor only and not at runtime,
+ // otherwise it can cause problems because of missing instance state support.
+ script->update_exports();
+ }
+
+ for (const PropertyInfo &E : plist) {
+ if (!(E.usage & PROPERTY_USAGE_STORAGE)) {
continue;
}
- String name = E->get().name;
- Variant value = p_node->get(E->get().name);
+ String name = E.name;
+ Variant value = p_node->get(E.name);
bool isdefault = false;
Variant default_value = ClassDB::class_get_default_property_value(type, name);
@@ -484,23 +501,22 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
isdefault = bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value));
}
- Ref<Script> script = p_node->get_script();
if (!isdefault && script.is_valid() && script->get_property_default_value(name, default_value)) {
isdefault = bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value));
}
// the version above makes more sense, because it does not rely on placeholder or usage flag
// in the script, just the default value function.
- // if (E->get().usage & PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE) {
+ // if (E.usage & PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE) {
// isdefault = true; //is script default value
// }
if (pack_state_stack.size()) {
- // we are on part of an instanced subscene
- // or part of instanced scene.
+ // we are on part of an instantiated subscene
+ // or part of instantiated scene.
// only save what has been changed
// only save changed properties in instance
- if ((E->get().usage & PROPERTY_USAGE_NO_INSTANCE_STATE) || E->get().name == "__meta__") {
+ if ((E.usage & PROPERTY_USAGE_NO_INSTANCE_STATE) || E.name == "__meta__") {
//property has requested that no instance state is saved, sorry
//also, meta won't be overridden or saved
continue;
@@ -513,7 +529,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
//check all levels of pack to see if the property exists somewhere
const PackState &ps = F->get();
- original = ps.state->get_property_value(ps.node, E->get().name, exists);
+ original = ps.state->get_property_value(ps.node, E.name, exists);
if (exists) {
break;
}
@@ -558,21 +574,19 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
List<Node::GroupInfo> groups;
p_node->get_groups(&groups);
- for (List<Node::GroupInfo>::Element *E = groups.front(); E; E = E->next()) {
- Node::GroupInfo &gi = E->get();
-
+ for (const Node::GroupInfo &gi : groups) {
if (!gi.persistent) {
continue;
}
/*
if (instance_state_node>=0 && instance_state->is_node_in_group(instance_state_node,gi.name))
- continue; //group was instanced, don't add here
+ continue; //group was instantiated, don't add here
*/
bool skip = false;
- for (List<PackState>::Element *F = pack_state_stack.front(); F; F = F->next()) {
+ for (const PackState &F : pack_state_stack) {
//check all levels of pack to see if the group was added somewhere
- const PackState &ps = F->get();
+ const PackState &ps = F;
if (ps.state->is_node_in_group(ps.node, gi.name)) {
skip = true;
break;
@@ -589,7 +603,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
// save the right owner
// for the saved scene root this is -1
// for nodes of the saved scene this is 0
- // for nodes of instanced scenes this is >0
+ // for nodes of instantiated scenes this is >0
if (p_node == p_owner) {
//saved scene root
@@ -603,24 +617,24 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
// Save the right type. If this node was created by an instance
// then flag that the node should not be created but reused
- if (pack_state_stack.empty()) {
+ if (pack_state_stack.is_empty() && !is_editable_instance) {
//this node is not part of an instancing process, so save the type
nd.type = _nm_get_string(p_node->get_class(), name_map);
} else {
- // this node is part of an instanced process, so do not save the type.
- // instead, save that it was instanced
+ // this node is part of an instantiated process, so do not save the type.
+ // instead, save that it was instantiated
nd.type = TYPE_INSTANCED;
}
// determine whether to save this node or not
- // if this node is part of an instanced sub-scene, we can skip storing it if basically
+ // if this node is part of an instantiated sub-scene, we can skip storing it if basically
// no properties changed and no groups were added to it.
// below condition is true for all nodes of the scene being saved, and ones in subscenes
// that hold changes
bool save_node = nd.properties.size() || nd.groups.size(); // some local properties or groups exist
save_node = save_node || p_node == p_owner; // owner is always saved
- save_node = save_node || (p_node->get_owner() == p_owner && instanced_by_owner); //part of scene and not instanced
+ save_node = save_node || (p_node->get_owner() == p_owner && instantiated_by_owner); //part of scene and not instantiated
int idx = nodes.size();
int parent_node = NO_PARENT_SAVED;
@@ -672,14 +686,14 @@ Error SceneState::_parse_connections(Node *p_owner, Node *p_node, Map<StringName
//ERR_FAIL_COND_V( !node_map.has(p_node), ERR_BUG);
//NodeData &nd = nodes[node_map[p_node]];
- for (List<MethodInfo>::Element *E = _signals.front(); E; E = E->next()) {
+ for (const MethodInfo &E : _signals) {
List<Node::Connection> conns;
- p_node->get_signal_connection_list(E->get().name, &conns);
+ p_node->get_signal_connection_list(E.name, &conns);
conns.sort();
- for (List<Node::Connection>::Element *F = conns.front(); F; F = F->next()) {
- const Node::Connection &c = F->get();
+ for (const Node::Connection &F : conns) {
+ const Node::Connection &c = F;
if (!(c.flags & CONNECT_PERSIST)) { //only persistent connections get saved
continue;
@@ -888,6 +902,13 @@ Error SceneState::pack(Node *p_scene) {
node_paths.write[E->get()] = scene->get_path_to(E->key());
}
+ if (Engine::get_singleton()->is_editor_hint()) {
+ // Build node path cache
+ for (Map<Node *, int>::Element *E = node_map.front(); E; E = E->next()) {
+ node_path_cache[scene->get_path_to(E->key())] = E->get();
+ }
+ }
+
return OK;
}
@@ -922,10 +943,12 @@ Ref<SceneState> SceneState::_get_base_scene_state() const {
}
int SceneState::find_node_by_path(const NodePath &p_node) const {
+ ERR_FAIL_COND_V_MSG(node_path_cache.size() == 0, -1, "This operation requires the node cache to have been built.");
+
if (!node_path_cache.has(p_node)) {
if (_get_base_scene_state().is_valid()) {
int idx = _get_base_scene_state()->find_node_by_path(p_node);
- if (idx >= 0) {
+ if (idx != -1) {
int rkey = _find_base_scene_node_remap_key(idx);
if (rkey == -1) {
rkey = nodes.size() + base_scene_node_remap.size();
@@ -941,7 +964,7 @@ int SceneState::find_node_by_path(const NodePath &p_node) const {
if (_get_base_scene_state().is_valid() && !base_scene_node_remap.has(nid)) {
//for nodes that _do_ exist in current scene, still try to look for
- //the node in the instanced scene, as a property may be missing
+ //the node in the instantiated scene, as a property may be missing
//from the local one
int idx = _get_base_scene_state()->find_node_by_path(p_node);
if (idx != -1) {
@@ -1349,7 +1372,7 @@ NodePath SceneState::get_node_path(int p_idx, bool p_for_parent) const {
sub_path.insert(0, base_path.get_name(i));
}
- if (sub_path.empty()) {
+ if (sub_path.is_empty()) {
return NodePath(".");
}
@@ -1481,16 +1504,6 @@ int SceneState::add_name(const StringName &p_name) {
return names.size() - 1;
}
-int SceneState::find_name(const StringName &p_name) const {
- for (int i = 0; i < names.size(); i++) {
- if (names[i] == p_name) {
- return i;
- }
- }
-
- return -1;
-}
-
int SceneState::add_value(const Variant &p_value) {
variants.push_back(p_value);
return variants.size() - 1;
@@ -1599,8 +1612,6 @@ void SceneState::_bind_methods() {
}
SceneState::SceneState() {
- base_scene_idx = -1;
- last_modified_time = 0;
}
////////////////
@@ -1621,16 +1632,16 @@ void PackedScene::clear() {
state->clear();
}
-bool PackedScene::can_instance() const {
- return state->can_instance();
+bool PackedScene::can_instantiate() const {
+ return state->can_instantiate();
}
-Node *PackedScene::instance(GenEditState p_edit_state) const {
+Node *PackedScene::instantiate(GenEditState p_edit_state) const {
#ifndef TOOLS_ENABLED
ERR_FAIL_COND_V_MSG(p_edit_state != GEN_EDIT_STATE_DISABLED, nullptr, "Edit state is only for editors, does not work without tools compiled.");
#endif
- Node *s = state->instance((SceneState::GenEditState)p_edit_state);
+ Node *s = state->instantiate((SceneState::GenEditState)p_edit_state);
if (!s) {
return nullptr;
}
@@ -1673,10 +1684,13 @@ void PackedScene::set_path(const String &p_path, bool p_take_over) {
Resource::set_path(p_path, p_take_over);
}
+void PackedScene::reset_state() {
+ clear();
+}
void PackedScene::_bind_methods() {
ClassDB::bind_method(D_METHOD("pack", "path"), &PackedScene::pack);
- ClassDB::bind_method(D_METHOD("instance", "edit_state"), &PackedScene::instance, DEFVAL(GEN_EDIT_STATE_DISABLED));
- ClassDB::bind_method(D_METHOD("can_instance"), &PackedScene::can_instance);
+ ClassDB::bind_method(D_METHOD("instantiate", "edit_state"), &PackedScene::instantiate, DEFVAL(GEN_EDIT_STATE_DISABLED));
+ ClassDB::bind_method(D_METHOD("can_instantiate"), &PackedScene::can_instantiate);
ClassDB::bind_method(D_METHOD("_set_bundled_scene"), &PackedScene::_set_bundled_scene);
ClassDB::bind_method(D_METHOD("_get_bundled_scene"), &PackedScene::_get_bundled_scene);
ClassDB::bind_method(D_METHOD("get_state"), &PackedScene::get_state);
diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h
index 004758afa5..55708f7914 100644
--- a/scene/resources/packed_scene.h
+++ b/scene/resources/packed_scene.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,11 +31,11 @@
#ifndef PACKED_SCENE_H
#define PACKED_SCENE_H
-#include "core/resource.h"
+#include "core/io/resource.h"
#include "scene/main/node.h"
-class SceneState : public Reference {
- GDCLASS(SceneState, Reference);
+class SceneState : public RefCounted {
+ GDCLASS(SceneState, RefCounted);
Vector<StringName> names;
Vector<Variant> variants;
@@ -44,7 +44,7 @@ class SceneState : public Reference {
mutable HashMap<NodePath, int> node_path_cache;
mutable Map<int, int> base_scene_node_remap;
- int base_scene_idx;
+ int base_scene_idx = -1;
enum {
NO_PARENT_SAVED = 0x7FFFFFFF,
@@ -53,16 +53,16 @@ class SceneState : public Reference {
};
struct NodeData {
- int parent;
- int owner;
- int type;
- int name;
- int instance;
- int index;
+ int parent = 0;
+ int owner = 0;
+ int type = 0;
+ int name = 0;
+ int instance = 0;
+ int index = 0;
struct Property {
- int name;
- int value;
+ int name = 0;
+ int value = 0;
};
Vector<Property> properties;
@@ -71,18 +71,17 @@ class SceneState : public Reference {
struct PackState {
Ref<SceneState> state;
- int node;
- PackState() { node = -1; }
+ int node = -1;
};
Vector<NodeData> nodes;
struct ConnectionData {
- int from;
- int to;
- int signal;
- int method;
- int flags;
+ int from = 0;
+ int to = 0;
+ int signal = 0;
+ int method = 0;
+ int flags = 0;
Vector<int> binds;
};
@@ -93,7 +92,7 @@ class SceneState : public Reference {
String path;
- uint64_t last_modified_time;
+ uint64_t last_modified_time = 0;
_FORCE_INLINE_ Ref<SceneState> _get_base_scene_state() const;
@@ -137,8 +136,8 @@ public:
void clear();
- bool can_instance() const;
- Node *instance(GenEditState p_edit_state) const;
+ bool can_instantiate() const;
+ Node *instantiate(GenEditState p_edit_state) const;
//unbuild API
@@ -172,7 +171,6 @@ public:
//build API
int add_name(const StringName &p_name);
- int find_name(const StringName &p_name) const;
int add_value(const Variant &p_value);
int add_node_path(const NodePath &p_path);
int add_node(int p_parent, int p_owner, int p_type, int p_name, int p_instance, int p_index);
@@ -202,6 +200,7 @@ class PackedScene : public Resource {
protected:
virtual bool editor_can_reload_from_file() override { return false; } // this is handled by editor better
static void _bind_methods();
+ virtual void reset_state() override;
public:
enum GenEditState {
@@ -214,8 +213,8 @@ public:
void clear();
- bool can_instance() const;
- Node *instance(GenEditState p_edit_state = GEN_EDIT_STATE_DISABLED) const;
+ bool can_instantiate() const;
+ Node *instantiate(GenEditState p_edit_state = GEN_EDIT_STATE_DISABLED) const;
void recreate_state();
void replace_state(Ref<SceneState> p_by);
diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp
index 4bbfa8965a..0495a9e92c 100644
--- a/scene/resources/particles_material.cpp
+++ b/scene/resources/particles_material.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -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;
@@ -43,31 +45,31 @@ void ParticlesMaterial::init_shaders() {
shader_names->direction = "direction";
shader_names->spread = "spread";
shader_names->flatness = "flatness";
- shader_names->initial_linear_velocity = "initial_linear_velocity";
- shader_names->initial_angle = "initial_angle";
- shader_names->angular_velocity = "angular_velocity";
- shader_names->orbit_velocity = "orbit_velocity";
- shader_names->linear_accel = "linear_accel";
- shader_names->radial_accel = "radial_accel";
- shader_names->tangent_accel = "tangent_accel";
- shader_names->damping = "damping";
- shader_names->scale = "scale";
- shader_names->hue_variation = "hue_variation";
- shader_names->anim_speed = "anim_speed";
- shader_names->anim_offset = "anim_offset";
-
- shader_names->initial_linear_velocity_random = "initial_linear_velocity_random";
- shader_names->initial_angle_random = "initial_angle_random";
- shader_names->angular_velocity_random = "angular_velocity_random";
- shader_names->orbit_velocity_random = "orbit_velocity_random";
- shader_names->linear_accel_random = "linear_accel_random";
- shader_names->radial_accel_random = "radial_accel_random";
- shader_names->tangent_accel_random = "tangent_accel_random";
- shader_names->damping_random = "damping_random";
- shader_names->scale_random = "scale_random";
- shader_names->hue_variation_random = "hue_variation_random";
- shader_names->anim_speed_random = "anim_speed_random";
- shader_names->anim_offset_random = "anim_offset_random";
+ shader_names->initial_linear_velocity_min = "initial_linear_velocity_min";
+ shader_names->initial_angle_min = "initial_angle_min";
+ shader_names->angular_velocity_min = "angular_velocity_min";
+ shader_names->orbit_velocity_min = "orbit_velocity_min";
+ shader_names->linear_accel_min = "linear_accel_min";
+ shader_names->radial_accel_min = "radial_accel_min";
+ shader_names->tangent_accel_min = "tangent_accel_min";
+ shader_names->damping_min = "damping_min";
+ shader_names->scale_min = "scale_min";
+ shader_names->hue_variation_min = "hue_variation_min";
+ shader_names->anim_speed_min = "anim_speed_min";
+ shader_names->anim_offset_min = "anim_offset_min";
+
+ shader_names->initial_linear_velocity_max = "initial_linear_velocity_max";
+ shader_names->initial_angle_max = "initial_angle_max";
+ shader_names->angular_velocity_max = "angular_velocity_max";
+ shader_names->orbit_velocity_max = "orbit_velocity_max";
+ shader_names->linear_accel_max = "linear_accel_max";
+ shader_names->radial_accel_max = "radial_accel_max";
+ shader_names->tangent_accel_max = "tangent_accel_max";
+ shader_names->damping_max = "damping_max";
+ shader_names->scale_max = "scale_max";
+ shader_names->hue_variation_max = "hue_variation_max";
+ shader_names->anim_speed_max = "anim_speed_max";
+ shader_names->anim_offset_max = "anim_offset_max";
shader_names->angle_texture = "angle_texture";
shader_names->angular_velocity_texture = "angular_velocity_texture";
@@ -90,6 +92,10 @@ void ParticlesMaterial::init_shaders() {
shader_names->emission_texture_points = "emission_texture_points";
shader_names->emission_texture_normal = "emission_texture_normal";
shader_names->emission_texture_color = "emission_texture_color";
+ shader_names->emission_ring_axis = "emission_ring_axis";
+ shader_names->emission_ring_height = "emission_ring_height";
+ shader_names->emission_ring_radius = "emission_ring_radius";
+ shader_names->emission_ring_inner_radius = "emission_ring_inner_radius";
shader_names->gravity = "gravity";
@@ -98,6 +104,9 @@ void ParticlesMaterial::init_shaders() {
shader_names->sub_emitter_frequency = "sub_emitter_frequency";
shader_names->sub_emitter_amount_at_end = "sub_emitter_amount_at_end";
shader_names->sub_emitter_keep_velocity = "sub_emitter_keep_velocity";
+
+ shader_names->collision_friction = "collision_friction";
+ shader_names->collision_bounce = "collision_bounce";
}
void ParticlesMaterial::finish_shaders() {
@@ -134,36 +143,43 @@ 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";
+ }
code += "uniform vec3 direction;\n";
code += "uniform float spread;\n";
code += "uniform float flatness;\n";
- code += "uniform float initial_linear_velocity;\n";
- code += "uniform float initial_angle;\n";
- code += "uniform float angular_velocity;\n";
- code += "uniform float orbit_velocity;\n";
- code += "uniform float linear_accel;\n";
- code += "uniform float radial_accel;\n";
- code += "uniform float tangent_accel;\n";
- code += "uniform float damping;\n";
- code += "uniform float scale;\n";
- code += "uniform float hue_variation;\n";
- code += "uniform float anim_speed;\n";
- code += "uniform float anim_offset;\n";
-
- code += "uniform float initial_linear_velocity_random;\n";
- code += "uniform float initial_angle_random;\n";
- code += "uniform float angular_velocity_random;\n";
- code += "uniform float orbit_velocity_random;\n";
- code += "uniform float linear_accel_random;\n";
- code += "uniform float radial_accel_random;\n";
- code += "uniform float tangent_accel_random;\n";
- code += "uniform float damping_random;\n";
- code += "uniform float scale_random;\n";
- code += "uniform float hue_variation_random;\n";
- code += "uniform float anim_speed_random;\n";
- code += "uniform float anim_offset_random;\n";
+ code += "uniform float initial_linear_velocity_min;\n";
+ code += "uniform float initial_angle_min;\n";
+ code += "uniform float angular_velocity_min;\n";
+ code += "uniform float orbit_velocity_min;\n";
+ code += "uniform float linear_accel_min;\n";
+ code += "uniform float radial_accel_min;\n";
+ code += "uniform float tangent_accel_min;\n";
+ code += "uniform float damping_min;\n";
+ code += "uniform float scale_min;\n";
+ code += "uniform float hue_variation_min;\n";
+ code += "uniform float anim_speed_min;\n";
+ code += "uniform float anim_offset_min;\n";
+
+ code += "uniform float initial_linear_velocity_max;\n";
+ code += "uniform float initial_angle_max;\n";
+ code += "uniform float angular_velocity_max;\n";
+ code += "uniform float orbit_velocity_max;\n";
+ code += "uniform float linear_accel_max;\n";
+ code += "uniform float radial_accel_max;\n";
+ code += "uniform float tangent_accel_max;\n";
+ code += "uniform float damping_max;\n";
+ code += "uniform float scale_max;\n";
+ code += "uniform float hue_variation_max;\n";
+ code += "uniform float anim_speed_max;\n";
+ code += "uniform float anim_offset_max;\n";
code += "uniform float lifetime_randomness;\n";
switch (emission_shape) {
@@ -187,6 +203,12 @@ void ParticlesMaterial::_update_shader() {
code += "uniform sampler2D emission_texture_color : hint_white;\n";
}
} break;
+ case EMISSION_SHAPE_RING: {
+ code += "uniform vec3 " + shader_names->emission_ring_axis + ";\n";
+ code += "uniform float " + shader_names->emission_ring_height + ";\n";
+ code += "uniform float " + shader_names->emission_ring_radius + ";\n";
+ code += "uniform float " + shader_names->emission_ring_inner_radius + ";\n";
+ } break;
case EMISSION_SHAPE_MAX: { // Max value for validity check.
break;
}
@@ -247,6 +269,11 @@ void ParticlesMaterial::_update_shader() {
code += "uniform sampler2D anim_offset_texture;\n";
}
+ if (collision_enabled) {
+ code += "uniform float collision_friction;\n";
+ code += "uniform float collision_bounce;\n";
+ }
+
//need a random function
code += "\n\n";
code += "float rand_from_seed(inout uint seed) {\n";
@@ -277,7 +304,7 @@ void ParticlesMaterial::_update_shader() {
code += "}\n";
code += "\n";
- code += "void compute() {\n";
+ code += "void start() {\n";
code += " uint base_number = NUMBER;\n";
code += " uint alt_seed = hash(base_number + uint(1) + RANDOM_SEED);\n";
code += " float angle_rand = rand_from_seed(alt_seed);\n";
@@ -293,235 +320,287 @@ void ParticlesMaterial::_update_shader() {
code += " ivec2 emission_tex_size = textureSize(emission_texture_points, 0);\n";
code += " ivec2 emission_tex_ofs = ivec2(point % emission_tex_size.x, point / emission_tex_size.x);\n";
}
- code += " if (RESTART) {\n";
-
if (tex_parameters[PARAM_ANGLE].is_valid()) {
- code += " float tex_angle = textureLod(angle_texture, vec2(0.0, 0.0), 0.0).r;\n";
+ code += " float tex_angle = textureLod(angle_texture, vec2(0.0, 0.0), 0.0).r;\n";
} else {
- code += " float tex_angle = 0.0;\n";
+ code += " float tex_angle = 0.0;\n";
}
if (tex_parameters[PARAM_ANIM_OFFSET].is_valid()) {
- code += " float tex_anim_offset = textureLod(anim_offset_texture, vec2(0.0, 0.0), 0.0).r;\n";
+ code += " float tex_anim_offset = textureLod(anim_offset_texture, vec2(0.0, 0.0), 0.0).r;\n";
} else {
- code += " float tex_anim_offset = 0.0;\n";
+ code += " float tex_anim_offset = 1.0;\n";
}
- code += " float spread_rad = spread * degree_to_rad;\n";
+ code += " float spread_rad = spread * degree_to_rad;\n";
- code += " if (RESTART_VELOCITY) {\n";
+ code += " if (RESTART_VELOCITY) {\n";
if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
- code += " float tex_linear_velocity = textureLod(linear_velocity_texture, vec2(0.0, 0.0), 0.0).r;\n";
+ code += " float tex_linear_velocity = textureLod(linear_velocity_texture, vec2(0.0, 0.0), 0.0).r;\n";
} else {
- code += " float tex_linear_velocity = 0.0;\n";
+ code += " float tex_linear_velocity = 1.0;\n";
}
- if (flags[FLAG_DISABLE_Z]) {
+ if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
+ code += " {\n";
code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n";
code += " angle1_rad += direction.x != 0.0 ? atan(direction.y, direction.x) : sign(direction.y) * (pi / 2.0);\n";
code += " vec3 rot = vec3(cos(angle1_rad), sin(angle1_rad), 0.0);\n";
- code += " VELOCITY = rot * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n";
+ code += " VELOCITY = rot * mix(initial_linear_velocity_min,initial_linear_velocity_max, rand_from_seed(alt_seed));\n";
+ code += " }\n";
} else {
//initiate velocity spread in 3D
+ code += " {\n";
code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n";
code += " float angle2_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad * (1.0 - flatness);\n";
- code += " angle1_rad += direction.z != 0.0 ? atan(direction.x, direction.z) : sign(direction.x) * (pi / 2.0);\n";
- code += " angle2_rad += direction.z != 0.0 ? atan(direction.y, abs(direction.z)) : (direction.x != 0.0 ? atan(direction.y, abs(direction.x)) : sign(direction.y) * (pi / 2.0));\n";
code += " vec3 direction_xz = vec3(sin(angle1_rad), 0.0, cos(angle1_rad));\n";
code += " vec3 direction_yz = vec3(0.0, sin(angle2_rad), cos(angle2_rad));\n";
code += " direction_yz.z = direction_yz.z / max(0.0001,sqrt(abs(direction_yz.z))); // better uniform distribution\n";
- code += " vec3 vec_direction = vec3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z);\n";
- code += " vec_direction = normalize(vec_direction);\n";
- code += " VELOCITY = vec_direction * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n";
+ code += " vec3 spread_direction = vec3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z);\n";
+ code += " vec3 direction_nrm = length(direction) > 0.0 ? normalize(direction) : vec3(0.0, 0.0, 1.0);\n";
+ code += " // rotate spread to direction\n";
+ code += " vec3 binormal = cross(vec3(0.0, 1.0, 0.0), direction_nrm);\n";
+ code += " if (length(binormal) < 0.0001) {\n";
+ code += " // direction is parallel to Y. Choose Z as the binormal.\n";
+ code += " binormal = vec3(0.0, 0.0, 1.0);\n";
+ code += " }\n";
+ code += " binormal = normalize(binormal);\n";
+ code += " vec3 normal = cross(binormal, direction_nrm);\n";
+ code += " spread_direction = binormal * spread_direction.x + normal * spread_direction.y + direction_nrm * spread_direction.z;\n";
+ code += " VELOCITY = spread_direction * mix(initial_linear_velocity_min, initial_linear_velocity_max,rand_from_seed(alt_seed));\n";
+ code += " }\n";
}
- code += " }\n";
+ code += " }\n";
- code += " float base_angle = (initial_angle + tex_angle) * mix(1.0, angle_rand, initial_angle_random);\n";
- code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle
- code += " CUSTOM.y = 0.0;\n"; // phase
- code += " CUSTOM.w = (1.0 - lifetime_randomness * rand_from_seed(alt_seed));\n";
- code += " CUSTOM.z = (anim_offset + tex_anim_offset) * mix(1.0, anim_offset_rand, anim_offset_random);\n"; // animation offset (0-1)
+ code += " float base_angle = (tex_angle) * mix(initial_angle_min, initial_angle_max, angle_rand);\n";
+ code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle
+ code += " CUSTOM.y = 0.0;\n"; // phase
+ code += " CUSTOM.w = (1.0 - lifetime_randomness * rand_from_seed(alt_seed));\n";
+ code += " CUSTOM.z = (tex_anim_offset) * mix(anim_offset_min, anim_offset_max, anim_offset_rand);\n"; // animation offset (0-1)
- code += " if (RESTART_POSITION) {\n";
+ code += " if (RESTART_POSITION) {\n";
switch (emission_shape) {
case EMISSION_SHAPE_POINT: {
//do none, identity (will later be multiplied by emission transform)
- code += " TRANSFORM = mat4(vec4(1,0,0,0),vec4(0,1,0,0),vec4(0,0,1,0),vec4(0,0,0,1));\n";
+ code += " TRANSFORM = mat4(vec4(1,0,0,0),vec4(0,1,0,0),vec4(0,0,1,0),vec4(0,0,0,1));\n";
} break;
case EMISSION_SHAPE_SPHERE: {
- code += " float s = rand_from_seed(alt_seed) * 2.0 - 1.0;\n";
- code += " float t = rand_from_seed(alt_seed) * 2.0 * pi;\n";
- code += " float radius = emission_sphere_radius * sqrt(1.0 - s * s);\n";
- code += " TRANSFORM[3].xyz = vec3(radius * cos(t), radius * sin(t), emission_sphere_radius * s);\n";
+ code += " float s = rand_from_seed(alt_seed) * 2.0 - 1.0;\n";
+ code += " float t = rand_from_seed(alt_seed) * 2.0 * pi;\n";
+ code += " float radius = emission_sphere_radius * sqrt(1.0 - s * s);\n";
+ code += " TRANSFORM[3].xyz = vec3(radius * cos(t), radius * sin(t), emission_sphere_radius * s);\n";
} break;
case EMISSION_SHAPE_BOX: {
- code += " TRANSFORM[3].xyz = vec3(rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0) * emission_box_extents;\n";
+ code += " TRANSFORM[3].xyz = vec3(rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0) * emission_box_extents;\n";
} break;
case EMISSION_SHAPE_POINTS:
case EMISSION_SHAPE_DIRECTED_POINTS: {
- code += " TRANSFORM[3].xyz = texelFetch(emission_texture_points, emission_tex_ofs, 0).xyz;\n";
+ code += " TRANSFORM[3].xyz = texelFetch(emission_texture_points, emission_tex_ofs, 0).xyz;\n";
if (emission_shape == EMISSION_SHAPE_DIRECTED_POINTS) {
- if (flags[FLAG_DISABLE_Z]) {
+ if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
+ code += " {\n";
code += " mat2 rotm;";
code += " rotm[0] = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xy;\n";
code += " rotm[1] = rotm[0].yx * vec2(1.0, -1.0);\n";
code += " if (RESTART_VELOCITY) VELOCITY.xy = rotm * VELOCITY.xy;\n";
+ code += " }\n";
} else {
+ code += " {\n";
code += " vec3 normal = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xyz;\n";
code += " vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);\n";
code += " vec3 tangent = normalize(cross(v0, normal));\n";
code += " vec3 bitangent = normalize(cross(tangent, normal));\n";
code += " if (RESTART_VELOCITY) VELOCITY = mat3(tangent, bitangent, normal) * VELOCITY;\n";
+ code += " }\n";
}
}
} break;
+ case EMISSION_SHAPE_RING: {
+ code += " float ring_spawn_angle = rand_from_seed(alt_seed) * 2.0 * pi;\n";
+ code += " float ring_random_radius = rand_from_seed(alt_seed) * (emission_ring_radius - emission_ring_inner_radius) + emission_ring_inner_radius;\n";
+ code += " vec3 axis = normalize(emission_ring_axis);\n";
+ code += " vec3 ortho_axis = vec3(0.0);\n";
+ code += " if (axis == vec3(1.0, 0.0, 0.0)) {\n";
+ code += " ortho_axis = cross(axis, vec3(0.0, 1.0, 0.0));\n";
+ code += " } else {\n";
+ code += " ortho_axis = cross(axis, vec3(1.0, 0.0, 0.0));\n";
+ code += " }\n";
+ code += " ortho_axis = normalize(ortho_axis);\n";
+ code += " float s = sin(ring_spawn_angle);\n";
+ code += " float c = cos(ring_spawn_angle);\n";
+ code += " float oc = 1.0 - c;\n";
+ code += " ortho_axis = mat3(\n";
+ code += " vec3(c + axis.x * axis.x * oc, axis.x * axis.y * oc - axis.z * s, axis.x * axis.z *oc + axis.y * s),\n";
+ code += " vec3(axis.x * axis.y * oc + s * axis.z, c + axis.y * axis.y * oc, axis.y * axis.z * oc - axis.x * s),\n";
+ code += " vec3(axis.z * axis.x * oc - axis.y * s, axis.z * axis.y * oc + axis.x * s, c + axis.z * axis.z * oc)\n";
+ code += " ) * ortho_axis;\n";
+ code += " ortho_axis = normalize(ortho_axis);\n";
+ code += " TRANSFORM[3].xyz = ortho_axis * ring_random_radius + (rand_from_seed(alt_seed) * emission_ring_height - emission_ring_height / 2.0) * axis;\n";
+ } break;
case EMISSION_SHAPE_MAX: { // Max value for validity check.
break;
}
}
- code += " if (RESTART_VELOCITY) VELOCITY = (EMISSION_TRANSFORM * vec4(VELOCITY, 0.0)).xyz;\n";
- code += " TRANSFORM = EMISSION_TRANSFORM * TRANSFORM;\n";
- if (flags[FLAG_DISABLE_Z]) {
- code += " VELOCITY.z = 0.0;\n";
- code += " TRANSFORM[3].z = 0.0;\n";
+ code += " if (RESTART_VELOCITY) VELOCITY = (EMISSION_TRANSFORM * vec4(VELOCITY, 0.0)).xyz;\n";
+ code += " TRANSFORM = EMISSION_TRANSFORM * TRANSFORM;\n";
+ if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
+ code += " VELOCITY.z = 0.0;\n";
+ code += " TRANSFORM[3].z = 0.0;\n";
}
- code += " }\n";
+ code += " }\n";
+ code += "}\n\n";
- code += " } else {\n";
+ code += "void process() {\n";
+ code += " uint base_number = NUMBER;\n";
+ code += " uint alt_seed = hash(base_number + uint(1) + RANDOM_SEED);\n";
+ code += " float angle_rand = rand_from_seed(alt_seed);\n";
+ code += " float scale_rand = rand_from_seed(alt_seed);\n";
+ code += " float hue_rot_rand = rand_from_seed(alt_seed);\n";
+ code += " float anim_offset_rand = rand_from_seed(alt_seed);\n";
+ code += " float pi = 3.14159;\n";
+ code += " float degree_to_rad = pi / 180.0;\n";
+ code += "\n";
- code += " CUSTOM.y += DELTA / LIFETIME;\n";
+ code += " CUSTOM.y += DELTA / LIFETIME;\n";
+ code += " float tv = CUSTOM.y / CUSTOM.w;\n";
if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
- code += " float tex_linear_velocity = textureLod(linear_velocity_texture, vec2(CUSTOM.y, 0.0), 0.0).r;\n";
+ code += " float tex_linear_velocity = textureLod(linear_velocity_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_linear_velocity = 0.0;\n";
+ code += " float tex_linear_velocity = 1.0;\n";
}
- if (flags[FLAG_DISABLE_Z]) {
+ if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
if (tex_parameters[PARAM_ORBIT_VELOCITY].is_valid()) {
- code += " float tex_orbit_velocity = textureLod(orbit_velocity_texture, vec2(CUSTOM.y, 0.0), 0.0).r;\n";
+ code += " float tex_orbit_velocity = textureLod(orbit_velocity_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_orbit_velocity = 0.0;\n";
+ code += " float tex_orbit_velocity = 1.0;\n";
}
}
if (tex_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) {
- code += " float tex_angular_velocity = textureLod(angular_velocity_texture, vec2(CUSTOM.y, 0.0), 0.0).r;\n";
+ code += " float tex_angular_velocity = textureLod(angular_velocity_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_angular_velocity = 0.0;\n";
+ code += " float tex_angular_velocity = 1.0;\n";
}
if (tex_parameters[PARAM_LINEAR_ACCEL].is_valid()) {
- code += " float tex_linear_accel = textureLod(linear_accel_texture, vec2(CUSTOM.y, 0.0), 0.0).r;\n";
+ code += " float tex_linear_accel = textureLod(linear_accel_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_linear_accel = 0.0;\n";
+ code += " float tex_linear_accel = 1.0;\n";
}
if (tex_parameters[PARAM_RADIAL_ACCEL].is_valid()) {
- code += " float tex_radial_accel = textureLod(radial_accel_texture, vec2(CUSTOM.y, 0.0), 0.0).r;\n";
+ code += " float tex_radial_accel = textureLod(radial_accel_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_radial_accel = 0.0;\n";
+ code += " float tex_radial_accel = 1.0;\n";
}
if (tex_parameters[PARAM_TANGENTIAL_ACCEL].is_valid()) {
- code += " float tex_tangent_accel = textureLod(tangent_accel_texture, vec2(CUSTOM.y, 0.0), 0.0).r;\n";
+ code += " float tex_tangent_accel = textureLod(tangent_accel_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_tangent_accel = 0.0;\n";
+ code += " float tex_tangent_accel = 1.0;\n";
}
if (tex_parameters[PARAM_DAMPING].is_valid()) {
- code += " float tex_damping = textureLod(damping_texture, vec2(CUSTOM.y, 0.0), 0.0).r;\n";
+ code += " float tex_damping = textureLod(damping_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_damping = 0.0;\n";
+ code += " float tex_damping = 1.0;\n";
}
if (tex_parameters[PARAM_ANGLE].is_valid()) {
- code += " float tex_angle = textureLod(angle_texture, vec2(CUSTOM.y, 0.0), 0.0).r;\n";
+ code += " float tex_angle = textureLod(angle_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_angle = 0.0;\n";
+ code += " float tex_angle = 1.0;\n";
}
if (tex_parameters[PARAM_ANIM_SPEED].is_valid()) {
- code += " float tex_anim_speed = textureLod(anim_speed_texture, vec2(CUSTOM.y, 0.0), 0.0).r;\n";
+ code += " float tex_anim_speed = textureLod(anim_speed_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_anim_speed = 0.0;\n";
+ code += " float tex_anim_speed = 1.0;\n";
}
if (tex_parameters[PARAM_ANIM_OFFSET].is_valid()) {
- code += " float tex_anim_offset = textureLod(anim_offset_texture, vec2(CUSTOM.y, 0.0), 0.0).r;\n";
+ code += " float tex_anim_offset = textureLod(anim_offset_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_anim_offset = 0.0;\n";
+ code += " float tex_anim_offset = 1.0;\n";
}
- code += " vec3 force = gravity;\n";
- code += " vec3 pos = TRANSFORM[3].xyz;\n";
- if (flags[FLAG_DISABLE_Z]) {
- code += " pos.z = 0.0;\n";
+ code += " vec3 force = gravity;\n";
+ code += " vec3 pos = TRANSFORM[3].xyz;\n";
+ if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
+ code += " pos.z = 0.0;\n";
}
- code += " // apply linear acceleration\n";
- code += " force += length(VELOCITY) > 0.0 ? normalize(VELOCITY) * (linear_accel + tex_linear_accel) * mix(1.0, rand_from_seed(alt_seed), linear_accel_random) : vec3(0.0);\n";
- code += " // apply radial acceleration\n";
- code += " vec3 org = EMISSION_TRANSFORM[3].xyz;\n";
- code += " vec3 diff = pos - org;\n";
- code += " force += length(diff) > 0.0 ? normalize(diff) * (radial_accel + tex_radial_accel) * mix(1.0, rand_from_seed(alt_seed), radial_accel_random) : vec3(0.0);\n";
- code += " // apply tangential acceleration;\n";
- if (flags[FLAG_DISABLE_Z]) {
- code += " force += length(diff.yx) > 0.0 ? vec3(normalize(diff.yx * vec2(-1.0, 1.0)), 0.0) * ((tangent_accel + tex_tangent_accel) * mix(1.0, rand_from_seed(alt_seed), tangent_accel_random)) : vec3(0.0);\n";
+ code += " // apply linear acceleration\n";
+ code += " force += length(VELOCITY) > 0.0 ? normalize(VELOCITY) * tex_linear_accel * mix(linear_accel_min, linear_accel_max, rand_from_seed(alt_seed)) : vec3(0.0);\n";
+ code += " // apply radial acceleration\n";
+ code += " vec3 org = EMISSION_TRANSFORM[3].xyz;\n";
+ code += " vec3 diff = pos - org;\n";
+ code += " force += length(diff) > 0.0 ? normalize(diff) * tex_radial_accel * mix(radial_accel_min, radial_accel_max, rand_from_seed(alt_seed)) : vec3(0.0);\n";
+ code += " // apply tangential acceleration;\n";
+ code += " float tangent_accel_val = tex_tangent_accel * mix(tangent_accel_min, tangent_accel_max, rand_from_seed(alt_seed))\n;";
+ if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
+ code += " force += length(diff.yx) > 0.0 ? vec3(normalize(diff.yx * vec2(-1.0, 1.0)), 0.0) * tangent_accel_val : vec3(0.0);\n";
} else {
- code += " vec3 crossDiff = cross(normalize(diff), normalize(gravity));\n";
- code += " force += length(crossDiff) > 0.0 ? normalize(crossDiff) * ((tangent_accel + tex_tangent_accel) * mix(1.0, rand_from_seed(alt_seed), tangent_accel_random)) : vec3(0.0);\n";
- }
- code += " // apply attractor forces\n";
- code += " VELOCITY += force * DELTA;\n";
- code += " // orbit velocity\n";
- if (flags[FLAG_DISABLE_Z]) {
- code += " float orbit_amount = (orbit_velocity + tex_orbit_velocity) * mix(1.0, rand_from_seed(alt_seed), orbit_velocity_random);\n";
- code += " if (orbit_amount != 0.0) {\n";
- code += " float ang = orbit_amount * DELTA * pi * 2.0;\n";
- code += " mat2 rot = mat2(vec2(cos(ang), -sin(ang)), vec2(sin(ang), cos(ang)));\n";
- code += " TRANSFORM[3].xy -= diff.xy;\n";
- code += " TRANSFORM[3].xy += rot * diff.xy;\n";
- code += " }\n";
+ code += " vec3 crossDiff = cross(normalize(diff), normalize(gravity));\n";
+ code += " force += length(crossDiff) > 0.0 ? normalize(crossDiff) * tangent_accel_val : vec3(0.0);\n";
+ }
+ if (attractor_interaction_enabled) {
+ code += " force += ATTRACTOR_FORCE;\n\n";
+ }
+
+ code += " // apply attractor forces\n";
+ code += " VELOCITY += force * DELTA;\n";
+ code += " // orbit velocity\n";
+ if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
+ code += " float orbit_amount = tex_orbit_velocity * mix(orbit_velocity_min, orbit_velocity_max, rand_from_seed(alt_seed));\n";
+ code += " if (orbit_amount != 0.0) {\n";
+ code += " float ang = orbit_amount * DELTA * pi * 2.0;\n";
+ code += " mat2 rot = mat2(vec2(cos(ang), -sin(ang)), vec2(sin(ang), cos(ang)));\n";
+ code += " TRANSFORM[3].xy -= diff.xy;\n";
+ code += " TRANSFORM[3].xy += rot * diff.xy;\n";
+ code += " }\n";
}
if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
- code += " VELOCITY = normalize(VELOCITY) * tex_linear_velocity;\n";
- }
- code += " if (damping + tex_damping > 0.0) {\n";
- code += " float v = length(VELOCITY);\n";
- code += " float damp = (damping + tex_damping) * mix(1.0, rand_from_seed(alt_seed), damping_random);\n";
- code += " v -= damp * DELTA;\n";
- code += " if (v < 0.0) {\n";
- code += " VELOCITY = vec3(0.0);\n";
- code += " } else {\n";
- code += " VELOCITY = normalize(VELOCITY) * v;\n";
- code += " }\n";
+ code += " VELOCITY = normalize(VELOCITY) * tex_linear_velocity;\n";
+ }
+ code += " float dmp = mix(damping_min, damping_max, rand_from_seed(alt_seed));\n";
+ code += " if (dmp * tex_damping > 0.0) {\n";
+ code += " float v = length(VELOCITY);\n";
+ code += " float damp = tex_damping * dmp;\n";
+ code += " v -= damp * DELTA;\n";
+ code += " if (v < 0.0) {\n";
+ code += " VELOCITY = vec3(0.0);\n";
+ code += " } else {\n";
+ code += " VELOCITY = normalize(VELOCITY) * v;\n";
code += " }\n";
- code += " float base_angle = (initial_angle + tex_angle) * mix(1.0, angle_rand, initial_angle_random);\n";
- code += " base_angle += CUSTOM.y * LIFETIME * (angular_velocity + tex_angular_velocity) * mix(1.0, rand_from_seed(alt_seed) * 2.0 - 1.0, angular_velocity_random);\n";
- code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle
- code += " CUSTOM.z = (anim_offset + tex_anim_offset) * mix(1.0, anim_offset_rand, anim_offset_random) + CUSTOM.y * (anim_speed + tex_anim_speed) * mix(1.0, rand_from_seed(alt_seed), anim_speed_random);\n"; // angle
code += " }\n";
+ code += " float base_angle = (tex_angle) * mix(initial_angle_min, initial_angle_max, rand_from_seed(alt_seed));\n";
+ code += " base_angle += CUSTOM.y * LIFETIME * (tex_angular_velocity) * mix(angular_velocity_min,angular_velocity_max, rand_from_seed(alt_seed));\n";
+ code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle
+ code += " CUSTOM.z = (tex_anim_offset) * mix(anim_offset_min, anim_offset_max, rand_from_seed(alt_seed)) + CUSTOM.y * tex_anim_speed * mix(anim_speed_min, anim_speed_max, rand_from_seed(alt_seed));\n"; // angle
+
// apply color
// apply hue rotation
if (tex_parameters[PARAM_SCALE].is_valid()) {
- code += " float tex_scale = textureLod(scale_texture, vec2(CUSTOM.y, 0.0), 0.0).r;\n";
+ code += " vec3 tex_scale = textureLod(scale_texture, vec2(tv, 0.0), 0.0).rgb;\n";
} else {
- code += " float tex_scale = 1.0;\n";
+ code += " vec3 tex_scale = vec3(1.0);\n";
}
if (tex_parameters[PARAM_HUE_VARIATION].is_valid()) {
- code += " float tex_hue_variation = textureLod(hue_variation_texture, vec2(CUSTOM.y, 0.0), 0.0).r;\n";
+ code += " float tex_hue_variation = textureLod(hue_variation_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_hue_variation = 0.0;\n";
+ code += " float tex_hue_variation = 1.0;\n";
}
- code += " float hue_rot_angle = (hue_variation + tex_hue_variation) * pi * 2.0 * mix(1.0, hue_rot_rand * 2.0 - 1.0, hue_variation_random);\n";
+ code += " float hue_rot_angle = (tex_hue_variation) * pi * 2.0 * mix(hue_variation_min, hue_variation_max, rand_from_seed(alt_seed));\n";
code += " float hue_rot_c = cos(hue_rot_angle);\n";
code += " float hue_rot_s = sin(hue_rot_angle);\n";
code += " mat4 hue_rot_mat = mat4(vec4(0.299, 0.587, 0.114, 0.0),\n";
@@ -537,7 +616,7 @@ void ParticlesMaterial::_update_shader() {
code += " vec4(1.250, -1.050, -0.203, 0.0),\n";
code += " vec4(0.000, 0.000, 0.000, 0.0)) * hue_rot_s;\n";
if (color_ramp.is_valid()) {
- code += " COLOR = hue_rot_mat * textureLod(color_ramp, vec2(CUSTOM.y, 0.0), 0.0);\n";
+ code += " COLOR = hue_rot_mat * textureLod(color_ramp, vec2(tv, 0.0), 0.0) * color_value;\n";
} else {
code += " COLOR = hue_rot_mat * color_value;\n";
}
@@ -546,8 +625,8 @@ void ParticlesMaterial::_update_shader() {
}
code += "\n";
- if (flags[FLAG_DISABLE_Z]) {
- if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) {
+ if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
+ if (particle_flags[PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY]) {
code += " if (length(VELOCITY) > 0.0) {\n";
code += " TRANSFORM[1].xyz = normalize(VELOCITY);\n";
code += " } else {\n";
@@ -563,7 +642,7 @@ void ParticlesMaterial::_update_shader() {
} else {
// orient particle Y towards velocity
- if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) {
+ if (particle_flags[PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY]) {
code += " if (length(VELOCITY) > 0.0) {\n";
code += " TRANSFORM[1].xyz = normalize(VELOCITY);\n";
code += " } else {\n";
@@ -582,23 +661,30 @@ void ParticlesMaterial::_update_shader() {
code += " TRANSFORM[2].xyz = normalize(TRANSFORM[2].xyz);\n";
}
// turn particle by rotation in Y
- if (flags[FLAG_ROTATE_Y]) {
- code += " TRANSFORM = TRANSFORM * mat4(vec4(cos(CUSTOM.x), 0.0, -sin(CUSTOM.x), 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(sin(CUSTOM.x), 0.0, cos(CUSTOM.x), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
+ if (particle_flags[PARTICLE_FLAG_ROTATE_Y]) {
+ code += " vec4 origin = TRANSFORM[3];\n";
+ code += " TRANSFORM = mat4(vec4(cos(CUSTOM.x), 0.0, -sin(CUSTOM.x), 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(sin(CUSTOM.x), 0.0, cos(CUSTOM.x), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
+ code += " TRANSFORM[3] = origin;\n";
}
}
//scale by scale
- code += " float base_scale = tex_scale * mix(scale, 1.0, scale_random * scale_rand);\n";
- code += " if (base_scale < 0.000001) {\n";
- code += " base_scale = 0.000001;\n";
- code += " }\n";
+ code += " float base_scale = mix(scale_min, scale_max, scale_rand);\n";
+ code += " base_scale = sign(base_scale) * max(abs(base_scale), 0.001);\n";
- code += " TRANSFORM[0].xyz *= base_scale;\n";
- code += " TRANSFORM[1].xyz *= base_scale;\n";
- code += " TRANSFORM[2].xyz *= base_scale;\n";
- if (flags[FLAG_DISABLE_Z]) {
+ code += " TRANSFORM[0].xyz *= base_scale * sign(tex_scale.r) * max(abs(tex_scale.r), 0.001);\n";
+ code += " TRANSFORM[1].xyz *= base_scale * sign(tex_scale.g) * max(abs(tex_scale.g), 0.001);\n";
+ code += " TRANSFORM[2].xyz *= base_scale * sign(tex_scale.b) * max(abs(tex_scale.b), 0.001);\n";
+ if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
code += " VELOCITY.z = 0.0;\n";
code += " TRANSFORM[3].z = 0.0;\n";
}
+ if (collision_enabled) {
+ code += " if (COLLIDED) {\n";
+ code += " TRANSFORM[3].xyz+=COLLISION_NORMAL * COLLISION_DEPTH;\n";
+ code += " VELOCITY -= COLLISION_NORMAL * dot(COLLISION_NORMAL, VELOCITY) * (1.0 + collision_bounce);\n";
+ code += " VELOCITY = mix(VELOCITY,vec3(0.0),collision_friction * DELTA * 100.0);\n";
+ code += " }\n";
+ }
if (sub_emitter_mode != SUB_EMITTER_DISABLED) {
code += " int emit_count = 0;\n";
switch (sub_emitter_mode) {
@@ -609,6 +695,7 @@ void ParticlesMaterial::_update_shader() {
} break;
case SUB_EMITTER_AT_COLLISION: {
//not implemented yet
+ code += " if (COLLIDED) emit_count = 1;\n";
} break;
case SUB_EMITTER_AT_END: {
//not implemented yet
@@ -622,11 +709,11 @@ void ParticlesMaterial::_update_shader() {
code += " for(int i=0;i<emit_count;i++) {\n";
code += " uint flags = FLAG_EMIT_POSITION|FLAG_EMIT_ROT_SCALE;\n";
code += " if (sub_emitter_keep_velocity) flags|=FLAG_EMIT_VELOCITY;\n";
- code += " emit_particle(TRANSFORM,VELOCITY,vec4(0.0),vec4(0.0),flags);\n";
+ code += " emit_subparticle(TRANSFORM,VELOCITY,vec4(0.0),vec4(0.0),flags);\n";
code += " }";
}
- code += " if (CUSTOM.y > CUSTOM.w) {";
+ code += " if (CUSTOM.y > CUSTOM.w) {\n";
code += " ACTIVE = false;\n";
code += " }\n";
code += "}\n";
@@ -692,110 +779,116 @@ float ParticlesMaterial::get_flatness() const {
return flatness;
}
-void ParticlesMaterial::set_param(Parameter p_param, float p_value) {
+void ParticlesMaterial::set_param_min(Parameter p_param, float p_value) {
ERR_FAIL_INDEX(p_param, PARAM_MAX);
- parameters[p_param] = p_value;
+ params_min[p_param] = p_value;
+ if (params_min[p_param] > params_max[p_param]) {
+ set_param_max(p_param, p_value);
+ }
switch (p_param) {
case PARAM_INITIAL_LINEAR_VELOCITY: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_linear_velocity, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_linear_velocity_min, p_value);
} break;
case PARAM_ANGULAR_VELOCITY: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->angular_velocity, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->angular_velocity_min, p_value);
} break;
case PARAM_ORBIT_VELOCITY: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->orbit_velocity, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->orbit_velocity_min, p_value);
} break;
case PARAM_LINEAR_ACCEL: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->linear_accel, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->linear_accel_min, p_value);
} break;
case PARAM_RADIAL_ACCEL: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->radial_accel, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->radial_accel_min, p_value);
} break;
case PARAM_TANGENTIAL_ACCEL: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->tangent_accel, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->tangent_accel_min, p_value);
} break;
case PARAM_DAMPING: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->damping, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->damping_min, p_value);
} break;
case PARAM_ANGLE: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_angle, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_angle_min, p_value);
} break;
case PARAM_SCALE: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->scale, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->scale_min, p_value);
} break;
case PARAM_HUE_VARIATION: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->hue_variation, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->hue_variation_min, p_value);
} break;
case PARAM_ANIM_SPEED: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_speed, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_speed_min, p_value);
} break;
case PARAM_ANIM_OFFSET: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_min, p_value);
} break;
case PARAM_MAX:
break; // Can't happen, but silences warning
}
}
-float ParticlesMaterial::get_param(Parameter p_param) const {
+float ParticlesMaterial::get_param_min(Parameter p_param) const {
ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0);
- return parameters[p_param];
+ return params_min[p_param];
}
-void ParticlesMaterial::set_param_randomness(Parameter p_param, float p_value) {
+void ParticlesMaterial::set_param_max(Parameter p_param, float p_value) {
ERR_FAIL_INDEX(p_param, PARAM_MAX);
- randomness[p_param] = p_value;
+ params_max[p_param] = p_value;
+ if (params_min[p_param] > params_max[p_param]) {
+ set_param_min(p_param, p_value);
+ }
switch (p_param) {
case PARAM_INITIAL_LINEAR_VELOCITY: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_linear_velocity_random, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_linear_velocity_max, p_value);
} break;
case PARAM_ANGULAR_VELOCITY: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->angular_velocity_random, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->angular_velocity_max, p_value);
} break;
case PARAM_ORBIT_VELOCITY: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->orbit_velocity_random, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->orbit_velocity_max, p_value);
} break;
case PARAM_LINEAR_ACCEL: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->linear_accel_random, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->linear_accel_max, p_value);
} break;
case PARAM_RADIAL_ACCEL: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->radial_accel_random, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->radial_accel_max, p_value);
} break;
case PARAM_TANGENTIAL_ACCEL: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->tangent_accel_random, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->tangent_accel_max, p_value);
} break;
case PARAM_DAMPING: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->damping_random, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->damping_max, p_value);
} break;
case PARAM_ANGLE: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_angle_random, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_angle_max, p_value);
} break;
case PARAM_SCALE: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->scale_random, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->scale_max, p_value);
} break;
case PARAM_HUE_VARIATION: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->hue_variation_random, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->hue_variation_max, p_value);
} break;
case PARAM_ANIM_SPEED: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_speed_random, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_speed_max, p_value);
} break;
case PARAM_ANIM_OFFSET: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_random, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_max, p_value);
} break;
case PARAM_MAX:
break; // Can't happen, but silences warning
}
}
-float ParticlesMaterial::get_param_randomness(Parameter p_param) const {
+float ParticlesMaterial::get_param_max(Parameter p_param) const {
ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0);
- return randomness[p_param];
+ return params_max[p_param];
}
static void _adjust_curve_range(const Ref<Texture2D> &p_texture, float p_min, float p_max) {
@@ -812,52 +905,54 @@ void ParticlesMaterial::set_param_texture(Parameter p_param, const Ref<Texture2D
tex_parameters[p_param] = p_texture;
+ RID tex_rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
+
switch (p_param) {
case PARAM_INITIAL_LINEAR_VELOCITY: {
//do none for this one
} break;
case PARAM_ANGULAR_VELOCITY: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->angular_velocity_texture, p_texture);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->angular_velocity_texture, tex_rid);
_adjust_curve_range(p_texture, -360, 360);
} break;
case PARAM_ORBIT_VELOCITY: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->orbit_velocity_texture, p_texture);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->orbit_velocity_texture, tex_rid);
_adjust_curve_range(p_texture, -500, 500);
} break;
case PARAM_LINEAR_ACCEL: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->linear_accel_texture, p_texture);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->linear_accel_texture, tex_rid);
_adjust_curve_range(p_texture, -200, 200);
} break;
case PARAM_RADIAL_ACCEL: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->radial_accel_texture, p_texture);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->radial_accel_texture, tex_rid);
_adjust_curve_range(p_texture, -200, 200);
} break;
case PARAM_TANGENTIAL_ACCEL: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->tangent_accel_texture, p_texture);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->tangent_accel_texture, tex_rid);
_adjust_curve_range(p_texture, -200, 200);
} break;
case PARAM_DAMPING: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->damping_texture, p_texture);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->damping_texture, tex_rid);
_adjust_curve_range(p_texture, 0, 100);
} break;
case PARAM_ANGLE: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->angle_texture, p_texture);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->angle_texture, tex_rid);
_adjust_curve_range(p_texture, -360, 360);
} break;
case PARAM_SCALE: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->scale_texture, p_texture);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->scale_texture, tex_rid);
_adjust_curve_range(p_texture, 0, 1);
} break;
case PARAM_HUE_VARIATION: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->hue_variation_texture, p_texture);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->hue_variation_texture, tex_rid);
_adjust_curve_range(p_texture, -1, 1);
} break;
case PARAM_ANIM_SPEED: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_speed_texture, p_texture);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_speed_texture, tex_rid);
_adjust_curve_range(p_texture, 0, 200);
} break;
case PARAM_ANIM_OFFSET: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_texture, p_texture);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_texture, tex_rid);
} break;
case PARAM_MAX:
break; // Can't happen, but silences warning
@@ -883,37 +978,38 @@ Color ParticlesMaterial::get_color() const {
void ParticlesMaterial::set_color_ramp(const Ref<Texture2D> &p_texture) {
color_ramp = p_texture;
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->color_ramp, p_texture);
+ RID tex_rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->color_ramp, tex_rid);
_queue_shader_change();
- _change_notify();
+ notify_property_list_changed();
}
Ref<Texture2D> ParticlesMaterial::get_color_ramp() const {
return color_ramp;
}
-void ParticlesMaterial::set_flag(Flags p_flag, bool p_enable) {
- ERR_FAIL_INDEX(p_flag, FLAG_MAX);
- flags[p_flag] = p_enable;
+void ParticlesMaterial::set_particle_flag(ParticleFlags p_particle_flag, bool p_enable) {
+ ERR_FAIL_INDEX(p_particle_flag, PARTICLE_FLAG_MAX);
+ particle_flags[p_particle_flag] = p_enable;
_queue_shader_change();
- if (p_flag == FLAG_DISABLE_Z) {
- _change_notify();
+ if (p_particle_flag == PARTICLE_FLAG_DISABLE_Z) {
+ notify_property_list_changed();
}
}
-bool ParticlesMaterial::get_flag(Flags p_flag) const {
- ERR_FAIL_INDEX_V(p_flag, FLAG_MAX, false);
- return flags[p_flag];
+bool ParticlesMaterial::get_particle_flag(ParticleFlags p_particle_flag) const {
+ ERR_FAIL_INDEX_V(p_particle_flag, PARTICLE_FLAG_MAX, false);
+ return particle_flags[p_particle_flag];
}
void ParticlesMaterial::set_emission_shape(EmissionShape p_shape) {
ERR_FAIL_INDEX(p_shape, EMISSION_SHAPE_MAX);
emission_shape = p_shape;
- _change_notify();
+ notify_property_list_changed();
_queue_shader_change();
}
-void ParticlesMaterial::set_emission_sphere_radius(float p_radius) {
+void ParticlesMaterial::set_emission_sphere_radius(real_t p_radius) {
emission_sphere_radius = p_radius;
RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_sphere_radius, p_radius);
}
@@ -925,17 +1021,20 @@ void ParticlesMaterial::set_emission_box_extents(Vector3 p_extents) {
void ParticlesMaterial::set_emission_point_texture(const Ref<Texture2D> &p_points) {
emission_point_texture = p_points;
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_points, p_points);
+ RID tex_rid = p_points.is_valid() ? p_points->get_rid() : RID();
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_points, tex_rid);
}
void ParticlesMaterial::set_emission_normal_texture(const Ref<Texture2D> &p_normals) {
emission_normal_texture = p_normals;
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_normal, p_normals);
+ RID tex_rid = p_normals.is_valid() ? p_normals->get_rid() : RID();
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_normal, tex_rid);
}
void ParticlesMaterial::set_emission_color_texture(const Ref<Texture2D> &p_colors) {
emission_color_texture = p_colors;
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_color, p_colors);
+ RID tex_rid = p_colors.is_valid() ? p_colors->get_rid() : RID();
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_color, tex_rid);
_queue_shader_change();
}
@@ -944,11 +1043,31 @@ void ParticlesMaterial::set_emission_point_count(int p_count) {
RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_point_count, p_count);
}
+void ParticlesMaterial::set_emission_ring_axis(Vector3 p_axis) {
+ emission_ring_axis = p_axis;
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_ring_axis, p_axis);
+}
+
+void ParticlesMaterial::set_emission_ring_height(real_t p_height) {
+ emission_ring_height = p_height;
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_ring_height, p_height);
+}
+
+void ParticlesMaterial::set_emission_ring_radius(real_t p_radius) {
+ emission_ring_radius = p_radius;
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_ring_radius, p_radius);
+}
+
+void ParticlesMaterial::set_emission_ring_inner_radius(real_t p_radius) {
+ emission_ring_inner_radius = p_radius;
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_ring_inner_radius, p_radius);
+}
+
ParticlesMaterial::EmissionShape ParticlesMaterial::get_emission_shape() const {
return emission_shape;
}
-float ParticlesMaterial::get_emission_sphere_radius() const {
+real_t ParticlesMaterial::get_emission_sphere_radius() const {
return emission_sphere_radius;
}
@@ -972,6 +1091,22 @@ int ParticlesMaterial::get_emission_point_count() const {
return emission_point_count;
}
+Vector3 ParticlesMaterial::get_emission_ring_axis() const {
+ return emission_ring_axis;
+}
+
+real_t ParticlesMaterial::get_emission_ring_height() const {
+ return emission_ring_height;
+}
+
+real_t ParticlesMaterial::get_emission_ring_radius() const {
+ return emission_ring_radius;
+}
+
+real_t ParticlesMaterial::get_emission_ring_inner_radius() const {
+ return emission_ring_inner_radius;
+}
+
void ParticlesMaterial::set_gravity(const Vector3 &p_gravity) {
gravity = p_gravity;
Vector3 gset = gravity;
@@ -985,12 +1120,12 @@ Vector3 ParticlesMaterial::get_gravity() const {
return gravity;
}
-void ParticlesMaterial::set_lifetime_randomness(float p_lifetime) {
+void ParticlesMaterial::set_lifetime_randomness(double p_lifetime) {
lifetime_randomness = p_lifetime;
RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->lifetime_randomness, lifetime_randomness);
}
-float ParticlesMaterial::get_lifetime_randomness() const {
+double ParticlesMaterial::get_lifetime_randomness() const {
return lifetime_randomness;
}
@@ -1000,58 +1135,59 @@ RID ParticlesMaterial::get_shader_rid() const {
}
void ParticlesMaterial::_validate_property(PropertyInfo &property) const {
- if (property.name == "color" && color_ramp.is_valid()) {
- property.usage = 0;
- }
-
if (property.name == "emission_sphere_radius" && emission_shape != EMISSION_SHAPE_SPHERE) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
if (property.name == "emission_box_extents" && emission_shape != EMISSION_SHAPE_BOX) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
- if ((property.name == "emission_point_texture" || property.name == "emission_color_texture") && (emission_shape < EMISSION_SHAPE_POINTS)) {
- property.usage = 0;
+ if ((property.name == "emission_point_texture" || property.name == "emission_color_texture") && (emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS)) {
+ property.usage = PROPERTY_USAGE_NONE;
}
if (property.name == "emission_normal_texture" && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
if (property.name == "emission_point_count" && (emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS)) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
+ }
+
+ if (property.name.begins_with("emission_ring_") && emission_shape != EMISSION_SHAPE_RING) {
+ property.usage = PROPERTY_USAGE_NONE;
}
if (property.name == "sub_emitter_frequency" && sub_emitter_mode != SUB_EMITTER_CONSTANT) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
if (property.name == "sub_emitter_amount_at_end" && sub_emitter_mode != SUB_EMITTER_AT_END) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
- if (property.name.begins_with("orbit_") && !flags[FLAG_DISABLE_Z]) {
- property.usage = 0;
+ if (property.name.begins_with("orbit_") && !particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
+ property.usage = PROPERTY_USAGE_NONE;
}
}
void ParticlesMaterial::set_sub_emitter_mode(SubEmitterMode p_sub_emitter_mode) {
sub_emitter_mode = p_sub_emitter_mode;
_queue_shader_change();
- _change_notify();
+ notify_property_list_changed();
}
ParticlesMaterial::SubEmitterMode ParticlesMaterial::get_sub_emitter_mode() const {
return sub_emitter_mode;
}
-void ParticlesMaterial::set_sub_emitter_frequency(float p_frequency) {
+void ParticlesMaterial::set_sub_emitter_frequency(double p_frequency) {
sub_emitter_frequency = p_frequency;
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->sub_emitter_frequency, 1.0 / p_frequency); //pas delta instead of frequency, since its easier to compute
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->sub_emitter_frequency, 1.0 / p_frequency); //pass delta instead of frequency, since its easier to compute
}
-float ParticlesMaterial::get_sub_emitter_frequency() const {
+
+double ParticlesMaterial::get_sub_emitter_frequency() const {
return sub_emitter_frequency;
}
@@ -1072,6 +1208,51 @@ bool ParticlesMaterial::get_sub_emitter_keep_velocity() const {
return sub_emitter_keep_velocity;
}
+void ParticlesMaterial::set_attractor_interaction_enabled(bool p_enable) {
+ attractor_interaction_enabled = p_enable;
+ _queue_shader_change();
+}
+
+bool ParticlesMaterial::is_attractor_interaction_enabled() const {
+ return attractor_interaction_enabled;
+}
+
+void ParticlesMaterial::set_collision_enabled(bool p_enabled) {
+ collision_enabled = p_enabled;
+ _queue_shader_change();
+}
+
+bool ParticlesMaterial::is_collision_enabled() const {
+ return collision_enabled;
+}
+
+void ParticlesMaterial::set_collision_use_scale(bool p_scale) {
+ collision_scale = p_scale;
+ _queue_shader_change();
+}
+
+bool ParticlesMaterial::is_collision_using_scale() const {
+ return collision_scale;
+}
+
+void ParticlesMaterial::set_collision_friction(float p_friction) {
+ collision_friction = p_friction;
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->collision_friction, p_friction);
+}
+
+float ParticlesMaterial::get_collision_friction() const {
+ return collision_friction;
+}
+
+void ParticlesMaterial::set_collision_bounce(float p_bounce) {
+ collision_bounce = p_bounce;
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->collision_bounce, p_bounce);
+}
+
+float ParticlesMaterial::get_collision_bounce() const {
+ return collision_bounce;
+}
+
Shader::Mode ParticlesMaterial::get_shader_mode() const {
return Shader::MODE_PARTICLES;
}
@@ -1086,11 +1267,11 @@ void ParticlesMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_flatness", "amount"), &ParticlesMaterial::set_flatness);
ClassDB::bind_method(D_METHOD("get_flatness"), &ParticlesMaterial::get_flatness);
- ClassDB::bind_method(D_METHOD("set_param", "param", "value"), &ParticlesMaterial::set_param);
- ClassDB::bind_method(D_METHOD("get_param", "param"), &ParticlesMaterial::get_param);
+ ClassDB::bind_method(D_METHOD("set_param_min", "param", "value"), &ParticlesMaterial::set_param_min);
+ ClassDB::bind_method(D_METHOD("get_param_min", "param"), &ParticlesMaterial::get_param_min);
- ClassDB::bind_method(D_METHOD("set_param_randomness", "param", "randomness"), &ParticlesMaterial::set_param_randomness);
- ClassDB::bind_method(D_METHOD("get_param_randomness", "param"), &ParticlesMaterial::get_param_randomness);
+ ClassDB::bind_method(D_METHOD("set_param_max", "param", "value"), &ParticlesMaterial::set_param_max);
+ ClassDB::bind_method(D_METHOD("get_param_max", "param"), &ParticlesMaterial::get_param_max);
ClassDB::bind_method(D_METHOD("set_param_texture", "param", "texture"), &ParticlesMaterial::set_param_texture);
ClassDB::bind_method(D_METHOD("get_param_texture", "param"), &ParticlesMaterial::get_param_texture);
@@ -1101,8 +1282,8 @@ void ParticlesMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_color_ramp", "ramp"), &ParticlesMaterial::set_color_ramp);
ClassDB::bind_method(D_METHOD("get_color_ramp"), &ParticlesMaterial::get_color_ramp);
- ClassDB::bind_method(D_METHOD("set_flag", "flag", "enable"), &ParticlesMaterial::set_flag);
- ClassDB::bind_method(D_METHOD("get_flag", "flag"), &ParticlesMaterial::get_flag);
+ ClassDB::bind_method(D_METHOD("set_particle_flag", "particle_flag", "enable"), &ParticlesMaterial::set_particle_flag);
+ ClassDB::bind_method(D_METHOD("get_particle_flag", "particle_flag"), &ParticlesMaterial::get_particle_flag);
ClassDB::bind_method(D_METHOD("set_emission_shape", "shape"), &ParticlesMaterial::set_emission_shape);
ClassDB::bind_method(D_METHOD("get_emission_shape"), &ParticlesMaterial::get_emission_shape);
@@ -1125,6 +1306,18 @@ void ParticlesMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_emission_point_count", "point_count"), &ParticlesMaterial::set_emission_point_count);
ClassDB::bind_method(D_METHOD("get_emission_point_count"), &ParticlesMaterial::get_emission_point_count);
+ ClassDB::bind_method(D_METHOD("set_emission_ring_axis", "axis"), &ParticlesMaterial::set_emission_ring_axis);
+ ClassDB::bind_method(D_METHOD("get_emission_ring_axis"), &ParticlesMaterial::get_emission_ring_axis);
+
+ ClassDB::bind_method(D_METHOD("set_emission_ring_height", "height"), &ParticlesMaterial::set_emission_ring_height);
+ ClassDB::bind_method(D_METHOD("get_emission_ring_height"), &ParticlesMaterial::get_emission_ring_height);
+
+ ClassDB::bind_method(D_METHOD("set_emission_ring_radius", "radius"), &ParticlesMaterial::set_emission_ring_radius);
+ ClassDB::bind_method(D_METHOD("get_emission_ring_radius"), &ParticlesMaterial::get_emission_ring_radius);
+
+ ClassDB::bind_method(D_METHOD("set_emission_ring_inner_radius", "inner_radius"), &ParticlesMaterial::set_emission_ring_inner_radius);
+ ClassDB::bind_method(D_METHOD("get_emission_ring_inner_radius"), &ParticlesMaterial::get_emission_ring_inner_radius);
+
ClassDB::bind_method(D_METHOD("get_gravity"), &ParticlesMaterial::get_gravity);
ClassDB::bind_method(D_METHOD("set_gravity", "accel_vec"), &ParticlesMaterial::set_gravity);
@@ -1143,21 +1336,40 @@ void ParticlesMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_sub_emitter_keep_velocity"), &ParticlesMaterial::get_sub_emitter_keep_velocity);
ClassDB::bind_method(D_METHOD("set_sub_emitter_keep_velocity", "enable"), &ParticlesMaterial::set_sub_emitter_keep_velocity);
+ ClassDB::bind_method(D_METHOD("set_attractor_interaction_enabled", "enabled"), &ParticlesMaterial::set_attractor_interaction_enabled);
+ ClassDB::bind_method(D_METHOD("is_attractor_interaction_enabled"), &ParticlesMaterial::is_attractor_interaction_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_collision_enabled", "enabled"), &ParticlesMaterial::set_collision_enabled);
+ ClassDB::bind_method(D_METHOD("is_collision_enabled"), &ParticlesMaterial::is_collision_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_collision_use_scale", "radius"), &ParticlesMaterial::set_collision_use_scale);
+ ClassDB::bind_method(D_METHOD("is_collision_using_scale"), &ParticlesMaterial::is_collision_using_scale);
+
+ ClassDB::bind_method(D_METHOD("set_collision_friction", "friction"), &ParticlesMaterial::set_collision_friction);
+ ClassDB::bind_method(D_METHOD("get_collision_friction"), &ParticlesMaterial::get_collision_friction);
+
+ ClassDB::bind_method(D_METHOD("set_collision_bounce", "bounce"), &ParticlesMaterial::set_collision_bounce);
+ ClassDB::bind_method(D_METHOD("get_collision_bounce"), &ParticlesMaterial::get_collision_bounce);
+
ADD_GROUP("Time", "");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime_randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_lifetime_randomness", "get_lifetime_randomness");
ADD_GROUP("Emission Shape", "emission_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points"), "set_emission_shape", "get_emission_shape");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points,Ring"), "set_emission_shape", "get_emission_shape");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01,or_greater"), "set_emission_sphere_radius", "get_emission_sphere_radius");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_box_extents"), "set_emission_box_extents", "get_emission_box_extents");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_point_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_emission_point_texture", "get_emission_point_texture");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_normal_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_emission_normal_texture", "get_emission_normal_texture");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_color_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_emission_color_texture", "get_emission_color_texture");
ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_point_count", PROPERTY_HINT_RANGE, "0,1000000,1"), "set_emission_point_count", "get_emission_point_count");
- ADD_GROUP("Flags", "flag_");
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_align_y"), "set_flag", "get_flag", FLAG_ALIGN_Y_TO_VELOCITY);
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_rotate_y"), "set_flag", "get_flag", FLAG_ROTATE_Y);
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_disable_z"), "set_flag", "get_flag", FLAG_DISABLE_Z);
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_ring_axis"), "set_emission_ring_axis", "get_emission_ring_axis");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_height"), "set_emission_ring_height", "get_emission_ring_height");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_radius"), "set_emission_ring_radius", "get_emission_ring_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_inner_radius"), "set_emission_ring_inner_radius", "get_emission_ring_inner_radius");
+ ADD_GROUP("ParticleFlags", "particle_flag_");
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "particle_flag_align_y"), "set_particle_flag", "get_particle_flag", PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "particle_flag_rotate_y"), "set_particle_flag", "get_particle_flag", PARTICLE_FLAG_ROTATE_Y);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "particle_flag_disable_z"), "set_particle_flag", "get_particle_flag", PARTICLE_FLAG_DISABLE_Z);
ADD_GROUP("Direction", "");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "direction"), "set_direction", "get_direction");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "spread", PROPERTY_HINT_RANGE, "0,180,0.01"), "set_spread", "get_spread");
@@ -1165,62 +1377,70 @@ void ParticlesMaterial::_bind_methods() {
ADD_GROUP("Gravity", "");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity"), "set_gravity", "get_gravity");
ADD_GROUP("Initial Velocity", "initial_");
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "initial_velocity", PROPERTY_HINT_RANGE, "0,1000,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_INITIAL_LINEAR_VELOCITY);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "initial_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_INITIAL_LINEAR_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "initial_velocity_min", PROPERTY_HINT_RANGE, "0,1000,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_INITIAL_LINEAR_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "initial_velocity_max", PROPERTY_HINT_RANGE, "0,1000,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_INITIAL_LINEAR_VELOCITY);
ADD_GROUP("Angular Velocity", "angular_");
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity", PROPERTY_HINT_RANGE, "-720,720,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ANGULAR_VELOCITY);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGULAR_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity_min", PROPERTY_HINT_RANGE, "-720,720,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_ANGULAR_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity_max", PROPERTY_HINT_RANGE, "-720,720,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_ANGULAR_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angular_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANGULAR_VELOCITY);
ADD_GROUP("Orbit Velocity", "orbit_");
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ORBIT_VELOCITY);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ORBIT_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity_min", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_ORBIT_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity_max", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_ORBIT_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "orbit_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ORBIT_VELOCITY);
ADD_GROUP("Linear Accel", "linear_");
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_LINEAR_ACCEL);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_LINEAR_ACCEL);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_LINEAR_ACCEL);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_LINEAR_ACCEL);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "linear_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_LINEAR_ACCEL);
ADD_GROUP("Radial Accel", "radial_");
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_RADIAL_ACCEL);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_RADIAL_ACCEL);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_RADIAL_ACCEL);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_RADIAL_ACCEL);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "radial_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_RADIAL_ACCEL);
ADD_GROUP("Tangential Accel", "tangential_");
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_TANGENTIAL_ACCEL);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_TANGENTIAL_ACCEL);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_TANGENTIAL_ACCEL);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_TANGENTIAL_ACCEL);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "tangential_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_TANGENTIAL_ACCEL);
ADD_GROUP("Damping", "");
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "damping", PROPERTY_HINT_RANGE, "0,100,0.01,or_greater"), "set_param", "get_param", PARAM_DAMPING);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "damping_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "damping_min", PROPERTY_HINT_RANGE, "0,100,0.01,or_greater"), "set_param_min", "get_param_min", PARAM_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "damping_max", PROPERTY_HINT_RANGE, "0,100,0.01,or_greater"), "set_param_max", "get_param_max", PARAM_DAMPING);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "damping_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_DAMPING);
ADD_GROUP("Angle", "");
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater"), "set_param", "get_param", PARAM_ANGLE);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGLE);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_min", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater,degrees"), "set_param_min", "get_param_min", PARAM_ANGLE);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_max", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater,degrees"), "set_param_max", "get_param_max", PARAM_ANGLE);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angle_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANGLE);
ADD_GROUP("Scale", "");
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "scale", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param", "get_param", PARAM_SCALE);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "scale_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_SCALE);
- ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "scale_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_SCALE);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "scale_min", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param_min", "get_param_min", PARAM_SCALE);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "scale_max", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param_max", "get_param_max", PARAM_SCALE);
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "scale_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture,CurveXYZTexture"), "set_param_texture", "get_param_texture", PARAM_SCALE);
ADD_GROUP("Color", "");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "GradientTexture"), "set_color_ramp", "get_color_ramp");
ADD_GROUP("Hue Variation", "hue_");
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param", "get_param", PARAM_HUE_VARIATION);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_HUE_VARIATION);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation_min", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param_min", "get_param_min", PARAM_HUE_VARIATION);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation_max", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param_max", "get_param_max", PARAM_HUE_VARIATION);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "hue_variation_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_HUE_VARIATION);
ADD_GROUP("Animation", "anim_");
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater"), "set_param", "get_param", PARAM_ANIM_SPEED);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_SPEED);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_min", PROPERTY_HINT_RANGE, "0,16,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_ANIM_SPEED);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_max", PROPERTY_HINT_RANGE, "0,16,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_ANIM_SPEED);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_speed_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_SPEED);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_ANIM_OFFSET);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_OFFSET);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_min", PROPERTY_HINT_RANGE, "0,16,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_ANIM_OFFSET);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_max", PROPERTY_HINT_RANGE, "0,16,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_ANIM_OFFSET);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_offset_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_OFFSET);
ADD_GROUP("Sub Emitter", "sub_emitter_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "sub_emitter_mode", PROPERTY_HINT_ENUM, "Disabled,Constant,AtEnd,AtCollision"), "set_sub_emitter_mode", "get_sub_emitter_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "sub_emitter_mode", PROPERTY_HINT_ENUM, "Disabled,Constant,At End,At Collision"), "set_sub_emitter_mode", "get_sub_emitter_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sub_emitter_frequency", PROPERTY_HINT_RANGE, "0.01,100,0.01"), "set_sub_emitter_frequency", "get_sub_emitter_frequency");
ADD_PROPERTY(PropertyInfo(Variant::INT, "sub_emitter_amount_at_end", PROPERTY_HINT_RANGE, "1,32,1"), "set_sub_emitter_amount_at_end", "get_sub_emitter_amount_at_end");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sub_emitter_keep_velocity"), "set_sub_emitter_keep_velocity", "get_sub_emitter_keep_velocity");
+ ADD_GROUP("Attractor Interaction", "attractor_interaction_");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "attractor_interaction_enabled"), "set_attractor_interaction_enabled", "is_attractor_interaction_enabled");
+ ADD_GROUP("Collision", "collision_");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision_enabled"), "set_collision_enabled", "is_collision_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_collision_friction", "get_collision_friction");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_collision_bounce", "get_collision_bounce");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision_use_scale"), "set_collision_use_scale", "is_collision_using_scale");
+
BIND_ENUM_CONSTANT(PARAM_INITIAL_LINEAR_VELOCITY);
BIND_ENUM_CONSTANT(PARAM_ANGULAR_VELOCITY);
BIND_ENUM_CONSTANT(PARAM_ORBIT_VELOCITY);
@@ -1235,16 +1455,17 @@ void ParticlesMaterial::_bind_methods() {
BIND_ENUM_CONSTANT(PARAM_ANIM_OFFSET);
BIND_ENUM_CONSTANT(PARAM_MAX);
- BIND_ENUM_CONSTANT(FLAG_ALIGN_Y_TO_VELOCITY);
- BIND_ENUM_CONSTANT(FLAG_ROTATE_Y);
- BIND_ENUM_CONSTANT(FLAG_DISABLE_Z);
- BIND_ENUM_CONSTANT(FLAG_MAX);
+ BIND_ENUM_CONSTANT(PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY);
+ BIND_ENUM_CONSTANT(PARTICLE_FLAG_ROTATE_Y);
+ BIND_ENUM_CONSTANT(PARTICLE_FLAG_DISABLE_Z);
+ BIND_ENUM_CONSTANT(PARTICLE_FLAG_MAX);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINT);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_SPHERE);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_BOX);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINTS);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_DIRECTED_POINTS);
+ BIND_ENUM_CONSTANT(EMISSION_SHAPE_RING);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_MAX);
BIND_ENUM_CONSTANT(SUB_EMITTER_DISABLED);
@@ -1259,41 +1480,57 @@ ParticlesMaterial::ParticlesMaterial() :
set_direction(Vector3(1, 0, 0));
set_spread(45);
set_flatness(0);
- set_param(PARAM_INITIAL_LINEAR_VELOCITY, 0);
- set_param(PARAM_ANGULAR_VELOCITY, 0);
- set_param(PARAM_ORBIT_VELOCITY, 0);
- set_param(PARAM_LINEAR_ACCEL, 0);
- set_param(PARAM_RADIAL_ACCEL, 0);
- set_param(PARAM_TANGENTIAL_ACCEL, 0);
- set_param(PARAM_DAMPING, 0);
- set_param(PARAM_ANGLE, 0);
- set_param(PARAM_SCALE, 1);
- set_param(PARAM_HUE_VARIATION, 0);
- set_param(PARAM_ANIM_SPEED, 0);
- set_param(PARAM_ANIM_OFFSET, 0);
+ set_param_min(PARAM_INITIAL_LINEAR_VELOCITY, 0);
+ set_param_min(PARAM_ANGULAR_VELOCITY, 0);
+ set_param_min(PARAM_ORBIT_VELOCITY, 0);
+ set_param_min(PARAM_LINEAR_ACCEL, 0);
+ set_param_min(PARAM_RADIAL_ACCEL, 0);
+ set_param_min(PARAM_TANGENTIAL_ACCEL, 0);
+ set_param_min(PARAM_DAMPING, 0);
+ set_param_min(PARAM_ANGLE, 0);
+ set_param_min(PARAM_SCALE, 1);
+ set_param_min(PARAM_HUE_VARIATION, 0);
+ set_param_min(PARAM_ANIM_SPEED, 0);
+ set_param_min(PARAM_ANIM_OFFSET, 0);
+ set_param_max(PARAM_INITIAL_LINEAR_VELOCITY, 0);
+ set_param_max(PARAM_ANGULAR_VELOCITY, 0);
+ set_param_max(PARAM_ORBIT_VELOCITY, 0);
+ set_param_max(PARAM_LINEAR_ACCEL, 0);
+ set_param_max(PARAM_RADIAL_ACCEL, 0);
+ set_param_max(PARAM_TANGENTIAL_ACCEL, 0);
+ set_param_max(PARAM_DAMPING, 0);
+ set_param_max(PARAM_ANGLE, 0);
+ set_param_max(PARAM_SCALE, 1);
+ set_param_max(PARAM_HUE_VARIATION, 0);
+ set_param_max(PARAM_ANIM_SPEED, 0);
+ set_param_max(PARAM_ANIM_OFFSET, 0);
set_emission_shape(EMISSION_SHAPE_POINT);
set_emission_sphere_radius(1);
set_emission_box_extents(Vector3(1, 1, 1));
+ set_emission_ring_axis(Vector3(0, 0, 1.0));
+ set_emission_ring_height(1);
+ set_emission_ring_radius(1);
+ set_emission_ring_inner_radius(0);
set_gravity(Vector3(0, -9.8, 0));
set_lifetime_randomness(0);
- emission_point_count = 1;
set_sub_emitter_mode(SUB_EMITTER_DISABLED);
set_sub_emitter_frequency(4);
set_sub_emitter_amount_at_end(1);
set_sub_emitter_keep_velocity(false);
- for (int i = 0; i < PARAM_MAX; i++) {
- set_param_randomness(Parameter(i), 0);
- }
+ set_attractor_interaction_enabled(true);
+ set_collision_enabled(false);
+ set_collision_bounce(0.0);
+ set_collision_friction(0.0);
+ set_collision_use_scale(false);
- for (int i = 0; i < FLAG_MAX; i++) {
- flags[i] = false;
+ for (int i = 0; i < PARTICLE_FLAG_MAX; i++) {
+ particle_flags[i] = false;
}
set_color(Color(1, 1, 1, 1));
- current_key.key = 0;
current_key.invalid_key = 1;
_queue_shader_change();
diff --git a/scene/resources/particles_material.h b/scene/resources/particles_material.h
index fa8858f67f..8ab26aff77 100644
--- a/scene/resources/particles_material.h
+++ b/scene/resources/particles_material.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "core/rid.h"
+#include "core/templates/rid.h"
#include "scene/resources/material.h"
#ifndef PARTICLES_MATERIAL_H
@@ -37,11 +37,7 @@
/*
TODO:
-Path following
-*Manual emission
--Sub Emitters
--Attractors
-Emitter positions deformable by bones
--Collision
-Proper trails
*/
@@ -65,22 +61,26 @@ public:
PARAM_MAX
};
- enum Flags {
- FLAG_ALIGN_Y_TO_VELOCITY,
- FLAG_ROTATE_Y,
- FLAG_DISABLE_Z,
- FLAG_MAX
+ // When extending, make sure not to overflow the size of the MaterialKey below.
+ enum ParticleFlags {
+ PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY,
+ PARTICLE_FLAG_ROTATE_Y,
+ PARTICLE_FLAG_DISABLE_Z,
+ PARTICLE_FLAG_MAX
};
+ // When extending, make sure not to overflow the size of the MaterialKey below.
enum EmissionShape {
EMISSION_SHAPE_POINT,
EMISSION_SHAPE_SPHERE,
EMISSION_SHAPE_BOX,
EMISSION_SHAPE_POINTS,
EMISSION_SHAPE_DIRECTED_POINTS,
+ EMISSION_SHAPE_RING,
EMISSION_SHAPE_MAX
};
+ // When extending, make sure not to overflow the size of the MaterialKey below.
enum SubEmitterMode {
SUB_EMITTER_DISABLED,
SUB_EMITTER_CONSTANT,
@@ -91,17 +91,22 @@ public:
private:
union MaterialKey {
+ // The bit size of the struct must be kept below or equal to 32 bits.
+ // Consider this when extending ParticleFlags, EmissionShape, or SubEmitterMode.
struct {
uint32_t texture_mask : 16;
uint32_t texture_color : 1;
- uint32_t flags : 4;
- uint32_t emission_shape : 2;
+ uint32_t particle_flags : 4;
+ uint32_t emission_shape : 3;
uint32_t invalid_key : 1;
uint32_t has_emission_color : 1;
uint32_t sub_emitter : 2;
+ uint32_t attractor_enabled : 1;
+ uint32_t collision_enabled : 1;
+ uint32_t collision_scale : 1;
};
- uint32_t key;
+ uint32_t key = 0;
bool operator<(const MaterialKey &p_key) const {
return key < p_key.key;
@@ -110,7 +115,7 @@ private:
struct ShaderData {
RID shader;
- int users;
+ int users = 0;
};
static Map<MaterialKey, ShaderData> shader_map;
@@ -125,9 +130,9 @@ private:
mk.texture_mask |= (1 << i);
}
}
- for (int i = 0; i < FLAG_MAX; i++) {
- if (flags[i]) {
- mk.flags |= (1 << i);
+ for (int i = 0; i < PARTICLE_FLAG_MAX; i++) {
+ if (particle_flags[i]) {
+ mk.particle_flags |= (1 << i);
}
}
@@ -135,6 +140,9 @@ private:
mk.emission_shape = emission_shape;
mk.has_emission_color = emission_shape >= EMISSION_SHAPE_POINTS && emission_color_texture.is_valid();
mk.sub_emitter = sub_emitter_mode;
+ mk.collision_enabled = collision_enabled;
+ mk.attractor_enabled = attractor_interaction_enabled;
+ mk.collision_scale = collision_scale;
return mk;
}
@@ -146,31 +154,31 @@ private:
StringName direction;
StringName spread;
StringName flatness;
- StringName initial_linear_velocity;
- StringName initial_angle;
- StringName angular_velocity;
- StringName orbit_velocity;
- StringName linear_accel;
- StringName radial_accel;
- StringName tangent_accel;
- StringName damping;
- StringName scale;
- StringName hue_variation;
- StringName anim_speed;
- StringName anim_offset;
-
- StringName initial_linear_velocity_random;
- StringName initial_angle_random;
- StringName angular_velocity_random;
- StringName orbit_velocity_random;
- StringName linear_accel_random;
- StringName radial_accel_random;
- StringName tangent_accel_random;
- StringName damping_random;
- StringName scale_random;
- StringName hue_variation_random;
- StringName anim_speed_random;
- StringName anim_offset_random;
+ StringName initial_linear_velocity_min;
+ StringName initial_angle_min;
+ StringName angular_velocity_min;
+ StringName orbit_velocity_min;
+ StringName linear_accel_min;
+ StringName radial_accel_min;
+ StringName tangent_accel_min;
+ StringName damping_min;
+ StringName scale_min;
+ StringName hue_variation_min;
+ StringName anim_speed_min;
+ StringName anim_offset_min;
+
+ StringName initial_linear_velocity_max;
+ StringName initial_angle_max;
+ StringName angular_velocity_max;
+ StringName orbit_velocity_max;
+ StringName linear_accel_max;
+ StringName radial_accel_max;
+ StringName tangent_accel_max;
+ StringName damping_max;
+ StringName scale_max;
+ StringName hue_variation_max;
+ StringName anim_speed_max;
+ StringName anim_offset_max;
StringName angle_texture;
StringName angular_velocity_texture;
@@ -193,6 +201,10 @@ private:
StringName emission_texture_points;
StringName emission_texture_normal;
StringName emission_texture_color;
+ StringName emission_ring_axis;
+ StringName emission_ring_height;
+ StringName emission_ring_radius;
+ StringName emission_ring_inner_radius;
StringName gravity;
@@ -201,6 +213,9 @@ private:
StringName sub_emitter_frequency;
StringName sub_emitter_amount_at_end;
StringName sub_emitter_keep_velocity;
+
+ StringName collision_friction;
+ StringName collision_bounce;
};
static ShaderNames *shader_names;
@@ -215,14 +230,14 @@ private:
float spread;
float flatness;
- float parameters[PARAM_MAX];
- float randomness[PARAM_MAX];
+ float params_min[PARAM_MAX];
+ float params_max[PARAM_MAX];
Ref<Texture2D> tex_parameters[PARAM_MAX];
Color color;
Ref<Texture2D> color_ramp;
- bool flags[FLAG_MAX];
+ bool particle_flags[PARTICLE_FLAG_MAX];
EmissionShape emission_shape;
float emission_sphere_radius;
@@ -230,20 +245,30 @@ private:
Ref<Texture2D> emission_point_texture;
Ref<Texture2D> emission_normal_texture;
Ref<Texture2D> emission_color_texture;
- int emission_point_count;
+ Vector3 emission_ring_axis;
+ real_t emission_ring_height;
+ real_t emission_ring_radius;
+ real_t emission_ring_inner_radius;
+ int emission_point_count = 1;
bool anim_loop;
Vector3 gravity;
- float lifetime_randomness;
+ double lifetime_randomness;
SubEmitterMode sub_emitter_mode;
- float sub_emitter_frequency;
+ double sub_emitter_frequency;
int sub_emitter_amount_at_end;
bool sub_emitter_keep_velocity;
//do not save emission points here
+ bool attractor_interaction_enabled;
+ bool collision_enabled;
+ bool collision_scale;
+ float collision_friction;
+ float collision_bounce;
+
protected:
static void _bind_methods();
virtual void _validate_property(PropertyInfo &property) const override;
@@ -258,11 +283,11 @@ public:
void set_flatness(float p_flatness);
float get_flatness() const;
- void set_param(Parameter p_param, float p_value);
- float get_param(Parameter p_param) const;
+ void set_param_min(Parameter p_param, float p_value);
+ float get_param_min(Parameter p_param) const;
- void set_param_randomness(Parameter p_param, float p_value);
- float get_param_randomness(Parameter p_param) const;
+ void set_param_max(Parameter p_param, float p_value);
+ float get_param_max(Parameter p_param) const;
void set_param_texture(Parameter p_param, const Ref<Texture2D> &p_texture);
Ref<Texture2D> get_param_texture(Parameter p_param) const;
@@ -273,30 +298,53 @@ public:
void set_color_ramp(const Ref<Texture2D> &p_texture);
Ref<Texture2D> get_color_ramp() const;
- void set_flag(Flags p_flag, bool p_enable);
- bool get_flag(Flags p_flag) const;
+ void set_particle_flag(ParticleFlags p_particle_flag, bool p_enable);
+ bool get_particle_flag(ParticleFlags p_particle_flag) const;
void set_emission_shape(EmissionShape p_shape);
- void set_emission_sphere_radius(float p_radius);
+ void set_emission_sphere_radius(real_t p_radius);
void set_emission_box_extents(Vector3 p_extents);
void set_emission_point_texture(const Ref<Texture2D> &p_points);
void set_emission_normal_texture(const Ref<Texture2D> &p_normals);
void set_emission_color_texture(const Ref<Texture2D> &p_colors);
+ void set_emission_ring_axis(Vector3 p_axis);
+ void set_emission_ring_height(real_t p_height);
+ void set_emission_ring_radius(real_t p_radius);
+ void set_emission_ring_inner_radius(real_t p_radius);
void set_emission_point_count(int p_count);
EmissionShape get_emission_shape() const;
- float get_emission_sphere_radius() const;
+ real_t get_emission_sphere_radius() const;
Vector3 get_emission_box_extents() const;
Ref<Texture2D> get_emission_point_texture() const;
Ref<Texture2D> get_emission_normal_texture() const;
Ref<Texture2D> get_emission_color_texture() const;
+ Vector3 get_emission_ring_axis() const;
+ real_t get_emission_ring_height() const;
+ real_t get_emission_ring_radius() const;
+ real_t get_emission_ring_inner_radius() const;
int get_emission_point_count() const;
void set_gravity(const Vector3 &p_gravity);
Vector3 get_gravity() const;
- void set_lifetime_randomness(float p_lifetime);
- float get_lifetime_randomness() const;
+ void set_lifetime_randomness(double p_lifetime);
+ double get_lifetime_randomness() const;
+
+ void set_attractor_interaction_enabled(bool p_enable);
+ bool is_attractor_interaction_enabled() const;
+
+ void set_collision_enabled(bool p_enabled);
+ bool is_collision_enabled() const;
+
+ void set_collision_use_scale(bool p_scale);
+ bool is_collision_using_scale() const;
+
+ void set_collision_friction(float p_friction);
+ float get_collision_friction() const;
+
+ void set_collision_bounce(float p_bounce);
+ float get_collision_bounce() const;
static void init_shaders();
static void finish_shaders();
@@ -305,8 +353,8 @@ public:
void set_sub_emitter_mode(SubEmitterMode p_sub_emitter_mode);
SubEmitterMode get_sub_emitter_mode() const;
- void set_sub_emitter_frequency(float p_frequency);
- float get_sub_emitter_frequency() const;
+ void set_sub_emitter_frequency(double p_frequency);
+ double get_sub_emitter_frequency() const;
void set_sub_emitter_amount_at_end(int p_amount);
int get_sub_emitter_amount_at_end() const;
@@ -314,7 +362,7 @@ public:
void set_sub_emitter_keep_velocity(bool p_enable);
bool get_sub_emitter_keep_velocity() const;
- RID get_shader_rid() const;
+ virtual RID get_shader_rid() const override;
virtual Shader::Mode get_shader_mode() const override;
@@ -323,7 +371,7 @@ public:
};
VARIANT_ENUM_CAST(ParticlesMaterial::Parameter)
-VARIANT_ENUM_CAST(ParticlesMaterial::Flags)
+VARIANT_ENUM_CAST(ParticlesMaterial::ParticleFlags)
VARIANT_ENUM_CAST(ParticlesMaterial::EmissionShape)
VARIANT_ENUM_CAST(ParticlesMaterial::SubEmitterMode)
diff --git a/scene/resources/physics_material.cpp b/scene/resources/physics_material.cpp
index 59bf8c0e13..31df35aa51 100644
--- a/scene/resources/physics_material.cpp
+++ b/scene/resources/physics_material.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -43,9 +43,9 @@ void PhysicsMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_absorbent", "absorbent"), &PhysicsMaterial::set_absorbent);
ClassDB::bind_method(D_METHOD("is_absorbent"), &PhysicsMaterial::is_absorbent);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "friction", PROPERTY_HINT_RANGE, "0,1,0.01,or_greater"), "set_friction", "get_friction");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rough"), "set_rough", "is_rough");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01,or_greater"), "set_bounce", "get_bounce");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "absorbent"), "set_absorbent", "is_absorbent");
}
diff --git a/scene/resources/physics_material.h b/scene/resources/physics_material.h
index 34aa7066df..d302800823 100644
--- a/scene/resources/physics_material.h
+++ b/scene/resources/physics_material.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,7 +31,7 @@
#ifndef PHYSICS_MATERIAL_H
#define PHYSICS_MATERIAL_H
-#include "core/resource.h"
+#include "core/io/resource.h"
#include "servers/physics_server_3d.h"
class PhysicsMaterial : public Resource {
@@ -39,9 +39,9 @@ class PhysicsMaterial : public Resource {
OBJ_SAVE_TYPE(PhysicsMaterial);
RES_BASE_EXTENSION("phymat");
- real_t friction = 1;
+ real_t friction = 1.0;
bool rough = false;
- real_t bounce = 0;
+ real_t bounce = 0.0;
bool absorbent = false;
protected:
diff --git a/scene/resources/polygon_path_finder.cpp b/scene/resources/polygon_path_finder.cpp
index df98d4cfd4..4dd3c874cb 100644
--- a/scene/resources/polygon_path_finder.cpp
+++ b/scene/resources/polygon_path_finder.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -77,7 +77,7 @@ void PolygonPathFinder::setup(const Vector<Vector2> &p_points, const Vector<int>
outside_point.x += 20.451 + Math::randf() * 10.2039;
outside_point.y += 21.193 + Math::randf() * 12.5412;
- //insert edges (which are also connetions)
+ //insert edges (which are also connections)
for (int i = 0; i < p_connections.size(); i += 2) {
Edge e(p_connections[i], p_connections[i + 1]);
@@ -335,7 +335,7 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector
//oh this was visited already, can we win the cost?
if (p.distance > distance) {
- p.prev = least_cost_point; //reasign previous
+ p.prev = least_cost_point; //reassign previous
p.distance = distance;
}
} else {
@@ -368,7 +368,7 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector
path.push_back(points[at].pos);
} while (at != aidx);
- path.invert();
+ path.reverse();
}
for (int i = 0; i < points.size() - 2; i++) {
@@ -417,9 +417,9 @@ void PolygonPathFinder::_set_data(const Dictionary &p_data) {
}
if (p_data.has("penalties")) {
- Vector<float> penalties = p_data["penalties"];
+ Vector<real_t> penalties = p_data["penalties"];
if (penalties.size() == pc) {
- const float *pr2 = penalties.ptr();
+ const real_t *pr2 = penalties.ptr();
for (int i = 0; i < pc; i++) {
points.write[i].penalty = pr2[i];
}
@@ -445,11 +445,11 @@ Dictionary PolygonPathFinder::_get_data() const {
p.resize(MAX(0, points.size() - 2));
connections.resize(MAX(0, points.size() - 2));
ind.resize(edges.size() * 2);
- Vector<float> penalties;
+ Vector<real_t> penalties;
penalties.resize(MAX(0, points.size() - 2));
{
Vector2 *wp = p.ptrw();
- float *pw = penalties.ptrw();
+ real_t *pw = penalties.ptrw();
for (int i = 0; i < points.size() - 2; i++) {
wp[i] = points[i].pos;
diff --git a/scene/resources/polygon_path_finder.h b/scene/resources/polygon_path_finder.h
index baee4dc20d..2f3cb634fb 100644
--- a/scene/resources/polygon_path_finder.h
+++ b/scene/resources/polygon_path_finder.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,7 +31,7 @@
#ifndef POLYGON_PATH_FINDER_H
#define POLYGON_PATH_FINDER_H
-#include "core/resource.h"
+#include "core/io/resource.h"
class PolygonPathFinder : public Resource {
GDCLASS(PolygonPathFinder, Resource);
@@ -39,13 +39,13 @@ class PolygonPathFinder : public Resource {
struct Point {
Vector2 pos;
Set<int> connections;
- float distance;
- float penalty;
- int prev;
+ float distance = 0.0;
+ float penalty = 0.0;
+ int prev = 0;
};
struct Edge {
- int points[2];
+ int points[2] = {};
_FORCE_INLINE_ bool operator<(const Edge &p_edge) const {
if (points[0] == p_edge.points[0]) {
diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp
index 8d9c5f07b2..e7da41db9d 100644
--- a/scene/resources/primitive_meshes.cpp
+++ b/scene/resources/primitive_meshes.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -148,7 +148,7 @@ Array PrimitiveMesh::surface_get_blend_shape_arrays(int p_surface) const {
uint32_t PrimitiveMesh::surface_get_format(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, 1, 0);
- return RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL | RS::ARRAY_FORMAT_TANGENT | RS::ARRAY_FORMAT_TEX_UV | RS::ARRAY_FORMAT_INDEX | RS::ARRAY_COMPRESS_DEFAULT;
+ return RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL | RS::ARRAY_FORMAT_TANGENT | RS::ARRAY_FORMAT_TEX_UV | RS::ARRAY_FORMAT_INDEX;
}
Mesh::PrimitiveType PrimitiveMesh::surface_get_primitive_type(int p_idx) const {
@@ -175,6 +175,9 @@ StringName PrimitiveMesh::get_blend_shape_name(int p_index) const {
return StringName();
}
+void PrimitiveMesh::set_blend_shape_name(int p_index, const StringName &p_name) {
+}
+
AABB PrimitiveMesh::get_aabb() const {
if (pending_request) {
_update();
@@ -204,7 +207,7 @@ void PrimitiveMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_flip_faces", "flip_faces"), &PrimitiveMesh::set_flip_faces);
ClassDB::bind_method(D_METHOD("get_flip_faces"), &PrimitiveMesh::get_flip_faces);
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,StandardMaterial3D"), "set_material", "get_material");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material");
ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, ""), "set_custom_aabb", "get_custom_aabb");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_faces"), "set_flip_faces", "get_flip_faces");
}
@@ -214,7 +217,7 @@ void PrimitiveMesh::set_material(const Ref<Material> &p_material) {
if (!pending_request) {
// just apply it, else it'll happen when _update is called.
RenderingServer::get_singleton()->mesh_surface_set_material(mesh, 0, material.is_null() ? RID() : material->get_rid());
- _change_notify();
+ notify_property_list_changed();
emit_changed();
};
}
@@ -247,18 +250,7 @@ bool PrimitiveMesh::get_flip_faces() const {
}
PrimitiveMesh::PrimitiveMesh() {
- flip_faces = false;
- // defaults
mesh = RenderingServer::get_singleton()->mesh_create();
-
- // assume primitive triangles as the type, correct for all but one and it will change this :)
- primitive_type = Mesh::PRIMITIVE_TRIANGLES;
-
- // make sure we do an update after we've finished constructing our object
- pending_request = true;
-
- array_len = 0;
- index_array_len = 0;
}
PrimitiveMesh::~PrimitiveMesh() {
@@ -304,11 +296,11 @@ void CapsuleMesh::_create_mesh_array(Array &p_arr) const {
u = i;
u /= radial_segments;
- x = -sin(u * (Math_PI * 2.0));
- z = cos(u * (Math_PI * 2.0));
+ x = -sin(u * Math_TAU);
+ z = cos(u * Math_TAU);
Vector3 p = Vector3(x * radius * w, y, -z * radius * w);
- points.push_back(p + Vector3(0.0, 0.5 * mid_height, 0.0));
+ points.push_back(p + Vector3(0.0, 0.5 * height - radius, 0.0));
normals.push_back(p.normalized());
ADD_TANGENT(z, 0.0, x, 1.0)
uvs.push_back(Vector2(u, v * onethird));
@@ -336,15 +328,15 @@ void CapsuleMesh::_create_mesh_array(Array &p_arr) const {
v = j;
v /= (rings + 1);
- y = mid_height * v;
- y = (mid_height * 0.5) - y;
+ y = (height - 2.0 * radius) * v;
+ y = (0.5 * height - radius) - y;
for (i = 0; i <= radial_segments; i++) {
u = i;
u /= radial_segments;
- x = -sin(u * (Math_PI * 2.0));
- z = cos(u * (Math_PI * 2.0));
+ x = -sin(u * Math_TAU);
+ z = cos(u * Math_TAU);
Vector3 p = Vector3(x * radius, y, -z * radius);
points.push_back(p);
@@ -383,11 +375,11 @@ void CapsuleMesh::_create_mesh_array(Array &p_arr) const {
float u2 = i;
u2 /= radial_segments;
- x = -sin(u2 * (Math_PI * 2.0));
- z = cos(u2 * (Math_PI * 2.0));
+ x = -sin(u2 * Math_TAU);
+ z = cos(u2 * Math_TAU);
Vector3 p = Vector3(x * radius * w, y, -z * radius * w);
- points.push_back(p + Vector3(0.0, -0.5 * mid_height, 0.0));
+ points.push_back(p + Vector3(0.0, -0.5 * height + radius, 0.0));
normals.push_back(p.normalized());
ADD_TANGENT(z, 0.0, x, 1.0)
uvs.push_back(Vector2(u2, twothirds + ((v - 1.0) * onethird)));
@@ -418,8 +410,8 @@ void CapsuleMesh::_create_mesh_array(Array &p_arr) const {
void CapsuleMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_radius", "radius"), &CapsuleMesh::set_radius);
ClassDB::bind_method(D_METHOD("get_radius"), &CapsuleMesh::get_radius);
- ClassDB::bind_method(D_METHOD("set_mid_height", "mid_height"), &CapsuleMesh::set_mid_height);
- ClassDB::bind_method(D_METHOD("get_mid_height"), &CapsuleMesh::get_mid_height);
+ ClassDB::bind_method(D_METHOD("set_height", "height"), &CapsuleMesh::set_height);
+ ClassDB::bind_method(D_METHOD("get_height"), &CapsuleMesh::get_height);
ClassDB::bind_method(D_METHOD("set_radial_segments", "segments"), &CapsuleMesh::set_radial_segments);
ClassDB::bind_method(D_METHOD("get_radial_segments"), &CapsuleMesh::get_radial_segments);
@@ -427,13 +419,16 @@ void CapsuleMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_rings"), &CapsuleMesh::get_rings);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_radius", "get_radius");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mid_height", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_mid_height", "get_mid_height");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_height", "get_height");
ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_radial_segments", "get_radial_segments");
ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_rings", "get_rings");
}
void CapsuleMesh::set_radius(const float p_radius) {
radius = p_radius;
+ if (radius > height * 0.5) {
+ radius = height * 0.5;
+ }
_request_update();
}
@@ -441,13 +436,16 @@ float CapsuleMesh::get_radius() const {
return radius;
}
-void CapsuleMesh::set_mid_height(const float p_mid_height) {
- mid_height = p_mid_height;
+void CapsuleMesh::set_height(const float p_height) {
+ height = p_height;
+ if (radius > height * 0.5) {
+ height = radius * 2;
+ }
_request_update();
}
-float CapsuleMesh::get_mid_height() const {
- return mid_height;
+float CapsuleMesh::get_height() const {
+ return height;
}
void CapsuleMesh::set_radial_segments(const int p_segments) {
@@ -468,19 +466,13 @@ int CapsuleMesh::get_rings() const {
return rings;
}
-CapsuleMesh::CapsuleMesh() {
- // defaults
- radius = 1.0;
- mid_height = 1.0;
- radial_segments = 64;
- rings = 8;
-}
+CapsuleMesh::CapsuleMesh() {}
/**
- CubeMesh
+ BoxMesh
*/
-void CubeMesh::_create_mesh_array(Array &p_arr) const {
+void BoxMesh::_create_mesh_array(Array &p_arr) const {
int i, j, prevrow, thisrow, point;
float x, y, z;
float onethird = 1.0 / 3.0;
@@ -672,16 +664,16 @@ void CubeMesh::_create_mesh_array(Array &p_arr) const {
p_arr[RS::ARRAY_INDEX] = indices;
}
-void CubeMesh::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_size", "size"), &CubeMesh::set_size);
- ClassDB::bind_method(D_METHOD("get_size"), &CubeMesh::get_size);
+void BoxMesh::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_size", "size"), &BoxMesh::set_size);
+ ClassDB::bind_method(D_METHOD("get_size"), &BoxMesh::get_size);
- ClassDB::bind_method(D_METHOD("set_subdivide_width", "subdivide"), &CubeMesh::set_subdivide_width);
- ClassDB::bind_method(D_METHOD("get_subdivide_width"), &CubeMesh::get_subdivide_width);
- ClassDB::bind_method(D_METHOD("set_subdivide_height", "divisions"), &CubeMesh::set_subdivide_height);
- ClassDB::bind_method(D_METHOD("get_subdivide_height"), &CubeMesh::get_subdivide_height);
- ClassDB::bind_method(D_METHOD("set_subdivide_depth", "divisions"), &CubeMesh::set_subdivide_depth);
- ClassDB::bind_method(D_METHOD("get_subdivide_depth"), &CubeMesh::get_subdivide_depth);
+ ClassDB::bind_method(D_METHOD("set_subdivide_width", "subdivide"), &BoxMesh::set_subdivide_width);
+ ClassDB::bind_method(D_METHOD("get_subdivide_width"), &BoxMesh::get_subdivide_width);
+ ClassDB::bind_method(D_METHOD("set_subdivide_height", "divisions"), &BoxMesh::set_subdivide_height);
+ ClassDB::bind_method(D_METHOD("get_subdivide_height"), &BoxMesh::get_subdivide_height);
+ ClassDB::bind_method(D_METHOD("set_subdivide_depth", "divisions"), &BoxMesh::set_subdivide_depth);
+ ClassDB::bind_method(D_METHOD("get_subdivide_depth"), &BoxMesh::get_subdivide_depth);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size"), "set_size", "get_size");
ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_width", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_width", "get_subdivide_width");
@@ -689,52 +681,46 @@ void CubeMesh::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_depth", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_depth", "get_subdivide_depth");
}
-void CubeMesh::set_size(const Vector3 &p_size) {
+void BoxMesh::set_size(const Vector3 &p_size) {
size = p_size;
_request_update();
}
-Vector3 CubeMesh::get_size() const {
+Vector3 BoxMesh::get_size() const {
return size;
}
-void CubeMesh::set_subdivide_width(const int p_divisions) {
+void BoxMesh::set_subdivide_width(const int p_divisions) {
subdivide_w = p_divisions > 0 ? p_divisions : 0;
_request_update();
}
-int CubeMesh::get_subdivide_width() const {
+int BoxMesh::get_subdivide_width() const {
return subdivide_w;
}
-void CubeMesh::set_subdivide_height(const int p_divisions) {
+void BoxMesh::set_subdivide_height(const int p_divisions) {
subdivide_h = p_divisions > 0 ? p_divisions : 0;
_request_update();
}
-int CubeMesh::get_subdivide_height() const {
+int BoxMesh::get_subdivide_height() const {
return subdivide_h;
}
-void CubeMesh::set_subdivide_depth(const int p_divisions) {
+void BoxMesh::set_subdivide_depth(const int p_divisions) {
subdivide_d = p_divisions > 0 ? p_divisions : 0;
_request_update();
}
-int CubeMesh::get_subdivide_depth() const {
+int BoxMesh::get_subdivide_depth() const {
return subdivide_d;
}
-CubeMesh::CubeMesh() {
- // defaults
- size = Vector3(2.0, 2.0, 2.0);
- subdivide_w = 0;
- subdivide_h = 0;
- subdivide_d = 0;
-}
+BoxMesh::BoxMesh() {}
/**
- CylinderMesh
+ CylinderMesh
*/
void CylinderMesh::_create_mesh_array(Array &p_arr) const {
@@ -769,8 +755,8 @@ void CylinderMesh::_create_mesh_array(Array &p_arr) const {
u = i;
u /= radial_segments;
- x = sin(u * (Math_PI * 2.0));
- z = cos(u * (Math_PI * 2.0));
+ x = sin(u * Math_TAU);
+ z = cos(u * Math_TAU);
Vector3 p = Vector3(x * radius, y, z * radius);
points.push_back(p);
@@ -809,8 +795,8 @@ void CylinderMesh::_create_mesh_array(Array &p_arr) const {
float r = i;
r /= radial_segments;
- x = sin(r * (Math_PI * 2.0));
- z = cos(r * (Math_PI * 2.0));
+ x = sin(r * Math_TAU);
+ z = cos(r * Math_TAU);
u = ((x + 1.0) * 0.25);
v = 0.5 + ((z + 1.0) * 0.25);
@@ -845,8 +831,8 @@ void CylinderMesh::_create_mesh_array(Array &p_arr) const {
float r = i;
r /= radial_segments;
- x = sin(r * (Math_PI * 2.0));
- z = cos(r * (Math_PI * 2.0));
+ x = sin(r * Math_TAU);
+ z = cos(r * Math_TAU);
u = 0.5 + ((x + 1.0) * 0.25);
v = 1.0 - ((z + 1.0) * 0.25);
@@ -886,9 +872,9 @@ void CylinderMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_rings", "rings"), &CylinderMesh::set_rings);
ClassDB::bind_method(D_METHOD("get_rings"), &CylinderMesh::get_rings);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "top_radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_top_radius", "get_top_radius");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bottom_radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_bottom_radius", "get_bottom_radius");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "top_radius", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_top_radius", "get_top_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bottom_radius", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_bottom_radius", "get_bottom_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), "set_height", "get_height");
ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_radial_segments", "get_radial_segments");
ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_rings", "get_rings");
}
@@ -938,14 +924,7 @@ int CylinderMesh::get_rings() const {
return rings;
}
-CylinderMesh::CylinderMesh() {
- // defaults
- top_radius = 1.0;
- bottom_radius = 1.0;
- height = 2.0;
- radial_segments = 64;
- rings = 4;
-}
+CylinderMesh::CylinderMesh() {}
/**
PlaneMesh
@@ -982,7 +961,7 @@ void PlaneMesh::_create_mesh_array(Array &p_arr) const {
u /= (subdivide_w + 1.0);
v /= (subdivide_d + 1.0);
- points.push_back(Vector3(-x, 0.0, -z));
+ points.push_back(Vector3(-x, 0.0, -z) + center_offset);
normals.push_back(Vector3(0.0, 1.0, 0.0));
ADD_TANGENT(1.0, 0.0, 0.0, 1.0);
uvs.push_back(Vector2(1.0 - u, 1.0 - v)); /* 1.0 - uv to match orientation with Quad */
@@ -1020,10 +999,13 @@ void PlaneMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_subdivide_width"), &PlaneMesh::get_subdivide_width);
ClassDB::bind_method(D_METHOD("set_subdivide_depth", "subdivide"), &PlaneMesh::set_subdivide_depth);
ClassDB::bind_method(D_METHOD("get_subdivide_depth"), &PlaneMesh::get_subdivide_depth);
+ ClassDB::bind_method(D_METHOD("set_center_offset", "offset"), &PlaneMesh::set_center_offset);
+ ClassDB::bind_method(D_METHOD("get_center_offset"), &PlaneMesh::get_center_offset);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size");
ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_width", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_width", "get_subdivide_width");
ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_depth", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_depth", "get_subdivide_depth");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "center_offset"), "set_center_offset", "get_center_offset");
}
void PlaneMesh::set_size(const Size2 &p_size) {
@@ -1053,13 +1035,17 @@ int PlaneMesh::get_subdivide_depth() const {
return subdivide_d;
}
-PlaneMesh::PlaneMesh() {
- // defaults
- size = Size2(2.0, 2.0);
- subdivide_w = 0;
- subdivide_d = 0;
+void PlaneMesh::set_center_offset(const Vector3 p_offset) {
+ center_offset = p_offset;
+ _request_update();
+}
+
+Vector3 PlaneMesh::get_center_offset() const {
+ return center_offset;
}
+PlaneMesh::PlaneMesh() {}
+
/**
PrismMesh
*/
@@ -1165,7 +1151,7 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const {
Vector3 normal_left, normal_right;
normal_left = Vector3(-size.y, size.x * left_to_right, 0.0);
- normal_right = Vector3(size.y, size.x * left_to_right, 0.0);
+ normal_right = Vector3(size.y, size.x * (1.0 - left_to_right), 0.0);
normal_left.normalize();
normal_right.normalize();
@@ -1338,14 +1324,7 @@ int PrismMesh::get_subdivide_depth() const {
return subdivide_d;
}
-PrismMesh::PrismMesh() {
- // defaults
- left_to_right = 0.5;
- size = Vector3(2.0, 2.0, 2.0);
- subdivide_w = 0;
- subdivide_h = 0;
- subdivide_d = 0;
-}
+PrismMesh::PrismMesh() {}
/**
QuadMesh
@@ -1365,10 +1344,10 @@ void QuadMesh::_create_mesh_array(Array &p_arr) const {
Vector2 _size = Vector2(size.x / 2.0f, size.y / 2.0f);
Vector3 quad_faces[4] = {
- Vector3(-_size.x, -_size.y, 0),
- Vector3(-_size.x, _size.y, 0),
- Vector3(_size.x, _size.y, 0),
- Vector3(_size.x, -_size.y, 0),
+ Vector3(-_size.x, -_size.y, 0) + center_offset,
+ Vector3(-_size.x, _size.y, 0) + center_offset,
+ Vector3(_size.x, _size.y, 0) + center_offset,
+ Vector3(_size.x, -_size.y, 0) + center_offset,
};
static const int indices[6] = {
@@ -1404,12 +1383,15 @@ void QuadMesh::_create_mesh_array(Array &p_arr) const {
void QuadMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_size", "size"), &QuadMesh::set_size);
ClassDB::bind_method(D_METHOD("get_size"), &QuadMesh::get_size);
+ ClassDB::bind_method(D_METHOD("set_center_offset", "center_offset"), &QuadMesh::set_center_offset);
+ ClassDB::bind_method(D_METHOD("get_center_offset"), &QuadMesh::get_center_offset);
+
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "center_offset"), "set_center_offset", "get_center_offset");
}
QuadMesh::QuadMesh() {
primitive_type = PRIMITIVE_TRIANGLES;
- size = Size2(1.0, 1.0);
}
void QuadMesh::set_size(const Size2 &p_size) {
@@ -1421,6 +1403,15 @@ Size2 QuadMesh::get_size() const {
return size;
}
+void QuadMesh::set_center_offset(Vector3 p_center_offset) {
+ center_offset = p_center_offset;
+ _request_update();
+}
+
+Vector3 QuadMesh::get_center_offset() const {
+ return center_offset;
+}
+
/**
SphereMesh
*/
@@ -1429,6 +1420,8 @@ void SphereMesh::_create_mesh_array(Array &p_arr) const {
int i, j, prevrow, thisrow, point;
float x, y, z;
+ float scale = height * (is_hemisphere ? 1.0 : 0.5);
+
// set our bounding box
Vector<Vector3> points;
@@ -1452,14 +1445,14 @@ void SphereMesh::_create_mesh_array(Array &p_arr) const {
v /= (rings + 1);
w = sin(Math_PI * v);
- y = height * (is_hemisphere ? 1.0 : 0.5) * cos(Math_PI * v);
+ y = scale * cos(Math_PI * v);
for (i = 0; i <= radial_segments; i++) {
float u = i;
u /= radial_segments;
- x = sin(u * (Math_PI * 2.0));
- z = cos(u * (Math_PI * 2.0));
+ x = sin(u * Math_TAU);
+ z = cos(u * Math_TAU);
if (is_hemisphere && y < 0.0) {
points.push_back(Vector3(x * radius * w, 0.0, z * radius * w));
@@ -1467,7 +1460,8 @@ void SphereMesh::_create_mesh_array(Array &p_arr) const {
} else {
Vector3 p = Vector3(x * radius * w, y, z * radius * w);
points.push_back(p);
- normals.push_back(p.normalized());
+ Vector3 normal = Vector3(x * radius * w * scale, y / scale, z * radius * w * scale);
+ normals.push_back(normal.normalized());
};
ADD_TANGENT(z, 0.0, -x, 1.0)
uvs.push_back(Vector2(u, v));
@@ -1561,14 +1555,7 @@ bool SphereMesh::get_is_hemisphere() const {
return is_hemisphere;
}
-SphereMesh::SphereMesh() {
- // defaults
- radius = 1.0;
- height = 2.0;
- radial_segments = 64;
- rings = 32;
- is_hemisphere = false;
-}
+SphereMesh::SphereMesh() {}
/**
PointMesh
@@ -1585,3 +1572,552 @@ void PointMesh::_create_mesh_array(Array &p_arr) const {
PointMesh::PointMesh() {
primitive_type = PRIMITIVE_POINTS;
}
+// TUBE TRAIL
+
+void TubeTrailMesh::set_radius(const float p_radius) {
+ radius = p_radius;
+ _request_update();
+}
+float TubeTrailMesh::get_radius() const {
+ return radius;
+}
+
+void TubeTrailMesh::set_radial_steps(const int p_radial_steps) {
+ ERR_FAIL_COND(p_radial_steps < 3 || p_radial_steps > 128);
+ radial_steps = p_radial_steps;
+ _request_update();
+}
+int TubeTrailMesh::get_radial_steps() const {
+ return radial_steps;
+}
+
+void TubeTrailMesh::set_sections(const int p_sections) {
+ ERR_FAIL_COND(p_sections < 2 || p_sections > 128);
+ sections = p_sections;
+ _request_update();
+}
+int TubeTrailMesh::get_sections() const {
+ return sections;
+}
+
+void TubeTrailMesh::set_section_length(float p_section_length) {
+ section_length = p_section_length;
+ _request_update();
+}
+float TubeTrailMesh::get_section_length() const {
+ return section_length;
+}
+
+void TubeTrailMesh::set_section_rings(const int p_section_rings) {
+ ERR_FAIL_COND(p_section_rings < 1 || p_section_rings > 1024);
+ section_rings = p_section_rings;
+ _request_update();
+}
+int TubeTrailMesh::get_section_rings() const {
+ return section_rings;
+}
+
+void TubeTrailMesh::set_curve(const Ref<Curve> &p_curve) {
+ if (curve == p_curve) {
+ return;
+ }
+ if (curve.is_valid()) {
+ curve->disconnect("changed", callable_mp(this, &TubeTrailMesh::_curve_changed));
+ }
+ curve = p_curve;
+ if (curve.is_valid()) {
+ curve->connect("changed", callable_mp(this, &TubeTrailMesh::_curve_changed));
+ }
+ _request_update();
+}
+Ref<Curve> TubeTrailMesh::get_curve() const {
+ return curve;
+}
+
+void TubeTrailMesh::_curve_changed() {
+ _request_update();
+}
+int TubeTrailMesh::get_builtin_bind_pose_count() const {
+ return sections + 1;
+}
+
+Transform3D TubeTrailMesh::get_builtin_bind_pose(int p_index) const {
+ float depth = section_length * sections;
+
+ Transform3D xform;
+ xform.origin.y = depth / 2.0 - section_length * float(p_index);
+ xform.origin.y = -xform.origin.y; //bind is an inverse transform, so negate y
+
+ return xform;
+}
+
+void TubeTrailMesh::_create_mesh_array(Array &p_arr) const {
+ PackedVector3Array points;
+ PackedVector3Array normals;
+ PackedFloat32Array tangents;
+ PackedVector2Array uvs;
+ PackedInt32Array bone_indices;
+ PackedFloat32Array bone_weights;
+ PackedInt32Array indices;
+
+ int point = 0;
+
+#define ADD_TANGENT(m_x, m_y, m_z, m_d) \
+ tangents.push_back(m_x); \
+ tangents.push_back(m_y); \
+ tangents.push_back(m_z); \
+ tangents.push_back(m_d);
+
+ int thisrow = 0;
+ int prevrow = 0;
+
+ int total_rings = section_rings * sections;
+ float depth = section_length * sections;
+
+ for (int j = 0; j <= total_rings; j++) {
+ float v = j;
+ v /= total_rings;
+
+ float y = depth * v;
+ y = (depth * 0.5) - y;
+
+ int bone = j / section_rings;
+ float blend = 1.0 - float(j % section_rings) / float(section_rings);
+
+ for (int i = 0; i <= radial_steps; i++) {
+ float u = i;
+ u /= radial_steps;
+
+ float r = radius;
+ if (curve.is_valid() && curve->get_point_count() > 0) {
+ r *= curve->interpolate_baked(v);
+ }
+ float x = sin(u * Math_TAU);
+ float z = cos(u * Math_TAU);
+
+ Vector3 p = Vector3(x * r, y, z * r);
+ points.push_back(p);
+ normals.push_back(Vector3(x, 0, z));
+ ADD_TANGENT(z, 0.0, -x, 1.0)
+ uvs.push_back(Vector2(u, v * 0.5));
+ point++;
+ {
+ bone_indices.push_back(bone);
+ bone_indices.push_back(MIN(sections, bone + 1));
+ bone_indices.push_back(0);
+ bone_indices.push_back(0);
+
+ bone_weights.push_back(blend);
+ bone_weights.push_back(1.0 - blend);
+ bone_weights.push_back(0);
+ bone_weights.push_back(0);
+ }
+
+ if (i > 0 && j > 0) {
+ indices.push_back(prevrow + i - 1);
+ indices.push_back(prevrow + i);
+ indices.push_back(thisrow + i - 1);
+
+ indices.push_back(prevrow + i);
+ indices.push_back(thisrow + i);
+ indices.push_back(thisrow + i - 1);
+ }
+ }
+
+ prevrow = thisrow;
+ thisrow = point;
+ }
+
+ // add top
+ float scale_pos = 1.0;
+ if (curve.is_valid() && curve->get_point_count() > 0) {
+ scale_pos = curve->interpolate_baked(0);
+ }
+
+ if (scale_pos > CMP_EPSILON) {
+ float y = depth * 0.5;
+
+ thisrow = point;
+ points.push_back(Vector3(0.0, y, 0));
+ normals.push_back(Vector3(0.0, 1.0, 0.0));
+ ADD_TANGENT(1.0, 0.0, 0.0, 1.0)
+ uvs.push_back(Vector2(0.25, 0.75));
+ point++;
+
+ bone_indices.push_back(0);
+ bone_indices.push_back(0);
+ bone_indices.push_back(0);
+ bone_indices.push_back(0);
+
+ bone_weights.push_back(1.0);
+ bone_weights.push_back(0);
+ bone_weights.push_back(0);
+ bone_weights.push_back(0);
+
+ float rm = radius * scale_pos;
+
+ for (int i = 0; i <= radial_steps; i++) {
+ float r = i;
+ r /= radial_steps;
+
+ float x = sin(r * Math_TAU);
+ float z = cos(r * Math_TAU);
+
+ float u = ((x + 1.0) * 0.25);
+ float v = 0.5 + ((z + 1.0) * 0.25);
+
+ Vector3 p = Vector3(x * rm, y, z * rm);
+ points.push_back(p);
+ normals.push_back(Vector3(0.0, 1.0, 0.0));
+ ADD_TANGENT(1.0, 0.0, 0.0, 1.0)
+ uvs.push_back(Vector2(u, v));
+ point++;
+
+ bone_indices.push_back(0);
+ bone_indices.push_back(0);
+ bone_indices.push_back(0);
+ bone_indices.push_back(0);
+
+ bone_weights.push_back(1.0);
+ bone_weights.push_back(0);
+ bone_weights.push_back(0);
+ bone_weights.push_back(0);
+
+ if (i > 0) {
+ indices.push_back(thisrow);
+ indices.push_back(point - 1);
+ indices.push_back(point - 2);
+ };
+ };
+ };
+
+ float scale_neg = 1.0;
+ if (curve.is_valid() && curve->get_point_count() > 0) {
+ scale_neg = curve->interpolate_baked(1.0);
+ }
+
+ // add bottom
+ if (scale_neg > CMP_EPSILON) {
+ float y = depth * -0.5;
+
+ thisrow = point;
+ points.push_back(Vector3(0.0, y, 0.0));
+ normals.push_back(Vector3(0.0, -1.0, 0.0));
+ ADD_TANGENT(1.0, 0.0, 0.0, 1.0)
+ uvs.push_back(Vector2(0.75, 0.75));
+ point++;
+
+ bone_indices.push_back(sections);
+ bone_indices.push_back(0);
+ bone_indices.push_back(0);
+ bone_indices.push_back(0);
+
+ bone_weights.push_back(1.0);
+ bone_weights.push_back(0);
+ bone_weights.push_back(0);
+ bone_weights.push_back(0);
+
+ float rm = radius * scale_neg;
+
+ for (int i = 0; i <= radial_steps; i++) {
+ float r = i;
+ r /= radial_steps;
+
+ float x = sin(r * Math_TAU);
+ float z = cos(r * Math_TAU);
+
+ float u = 0.5 + ((x + 1.0) * 0.25);
+ float v = 1.0 - ((z + 1.0) * 0.25);
+
+ Vector3 p = Vector3(x * rm, y, z * rm);
+ points.push_back(p);
+ normals.push_back(Vector3(0.0, -1.0, 0.0));
+ ADD_TANGENT(1.0, 0.0, 0.0, 1.0)
+ uvs.push_back(Vector2(u, v));
+ point++;
+
+ bone_indices.push_back(sections);
+ bone_indices.push_back(0);
+ bone_indices.push_back(0);
+ bone_indices.push_back(0);
+
+ bone_weights.push_back(1.0);
+ bone_weights.push_back(0);
+ bone_weights.push_back(0);
+ bone_weights.push_back(0);
+
+ if (i > 0) {
+ indices.push_back(thisrow);
+ indices.push_back(point - 2);
+ indices.push_back(point - 1);
+ };
+ };
+ };
+
+ p_arr[RS::ARRAY_VERTEX] = points;
+ p_arr[RS::ARRAY_NORMAL] = normals;
+ p_arr[RS::ARRAY_TANGENT] = tangents;
+ p_arr[RS::ARRAY_TEX_UV] = uvs;
+ p_arr[RS::ARRAY_BONES] = bone_indices;
+ p_arr[RS::ARRAY_WEIGHTS] = bone_weights;
+ p_arr[RS::ARRAY_INDEX] = indices;
+}
+
+void TubeTrailMesh::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_radius", "radius"), &TubeTrailMesh::set_radius);
+ ClassDB::bind_method(D_METHOD("get_radius"), &TubeTrailMesh::get_radius);
+
+ ClassDB::bind_method(D_METHOD("set_radial_steps", "radial_steps"), &TubeTrailMesh::set_radial_steps);
+ ClassDB::bind_method(D_METHOD("get_radial_steps"), &TubeTrailMesh::get_radial_steps);
+
+ ClassDB::bind_method(D_METHOD("set_sections", "sections"), &TubeTrailMesh::set_sections);
+ ClassDB::bind_method(D_METHOD("get_sections"), &TubeTrailMesh::get_sections);
+
+ ClassDB::bind_method(D_METHOD("set_section_length", "section_length"), &TubeTrailMesh::set_section_length);
+ ClassDB::bind_method(D_METHOD("get_section_length"), &TubeTrailMesh::get_section_length);
+
+ ClassDB::bind_method(D_METHOD("set_section_rings", "section_rings"), &TubeTrailMesh::set_section_rings);
+ ClassDB::bind_method(D_METHOD("get_section_rings"), &TubeTrailMesh::get_section_rings);
+
+ ClassDB::bind_method(D_METHOD("set_curve", "curve"), &TubeTrailMesh::set_curve);
+ ClassDB::bind_method(D_METHOD("get_curve"), &TubeTrailMesh::get_curve);
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_radius", "get_radius");
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_steps", PROPERTY_HINT_RANGE, "3,128,1"), "set_radial_steps", "get_radial_steps");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "sections", PROPERTY_HINT_RANGE, "2,128,1"), "set_sections", "get_sections");
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "section_length", PROPERTY_HINT_RANGE, "0.001,1024.0,0.001,or_greater"), "set_section_length", "get_section_length");
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "section_rings", PROPERTY_HINT_RANGE, "1,128,1"), "set_section_rings", "get_section_rings");
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve", "get_curve");
+}
+
+TubeTrailMesh::TubeTrailMesh() {
+}
+
+// TUBE TRAIL
+
+void RibbonTrailMesh::set_shape(Shape p_shape) {
+ shape = p_shape;
+ _request_update();
+}
+RibbonTrailMesh::Shape RibbonTrailMesh::get_shape() const {
+ return shape;
+}
+
+void RibbonTrailMesh::set_size(const float p_size) {
+ size = p_size;
+ _request_update();
+}
+float RibbonTrailMesh::get_size() const {
+ return size;
+}
+
+void RibbonTrailMesh::set_sections(const int p_sections) {
+ ERR_FAIL_COND(p_sections < 2 || p_sections > 128);
+ sections = p_sections;
+ _request_update();
+}
+int RibbonTrailMesh::get_sections() const {
+ return sections;
+}
+
+void RibbonTrailMesh::set_section_length(float p_section_length) {
+ section_length = p_section_length;
+ _request_update();
+}
+float RibbonTrailMesh::get_section_length() const {
+ return section_length;
+}
+
+void RibbonTrailMesh::set_section_segments(const int p_section_segments) {
+ ERR_FAIL_COND(p_section_segments < 1 || p_section_segments > 1024);
+ section_segments = p_section_segments;
+ _request_update();
+}
+int RibbonTrailMesh::get_section_segments() const {
+ return section_segments;
+}
+
+void RibbonTrailMesh::set_curve(const Ref<Curve> &p_curve) {
+ if (curve == p_curve) {
+ return;
+ }
+ if (curve.is_valid()) {
+ curve->disconnect("changed", callable_mp(this, &RibbonTrailMesh::_curve_changed));
+ }
+ curve = p_curve;
+ if (curve.is_valid()) {
+ curve->connect("changed", callable_mp(this, &RibbonTrailMesh::_curve_changed));
+ }
+ _request_update();
+}
+Ref<Curve> RibbonTrailMesh::get_curve() const {
+ return curve;
+}
+
+void RibbonTrailMesh::_curve_changed() {
+ _request_update();
+}
+int RibbonTrailMesh::get_builtin_bind_pose_count() const {
+ return sections + 1;
+}
+
+Transform3D RibbonTrailMesh::get_builtin_bind_pose(int p_index) const {
+ float depth = section_length * sections;
+
+ Transform3D xform;
+ xform.origin.y = depth / 2.0 - section_length * float(p_index);
+ xform.origin.y = -xform.origin.y; //bind is an inverse transform, so negate y
+
+ return xform;
+}
+
+void RibbonTrailMesh::_create_mesh_array(Array &p_arr) const {
+ PackedVector3Array points;
+ PackedVector3Array normals;
+ PackedFloat32Array tangents;
+ PackedVector2Array uvs;
+ PackedInt32Array bone_indices;
+ PackedFloat32Array bone_weights;
+ PackedInt32Array indices;
+
+#define ADD_TANGENT(m_x, m_y, m_z, m_d) \
+ tangents.push_back(m_x); \
+ tangents.push_back(m_y); \
+ tangents.push_back(m_z); \
+ tangents.push_back(m_d);
+
+ int total_segments = section_segments * sections;
+ float depth = section_length * sections;
+
+ for (int j = 0; j <= total_segments; j++) {
+ float v = j;
+ v /= total_segments;
+
+ float y = depth * v;
+ y = (depth * 0.5) - y;
+
+ int bone = j / section_segments;
+ float blend = 1.0 - float(j % section_segments) / float(section_segments);
+
+ float s = size;
+
+ if (curve.is_valid() && curve->get_point_count() > 0) {
+ s *= curve->interpolate_baked(v);
+ }
+
+ points.push_back(Vector3(-s * 0.5, y, 0));
+ points.push_back(Vector3(+s * 0.5, y, 0));
+ if (shape == SHAPE_CROSS) {
+ points.push_back(Vector3(0, y, -s * 0.5));
+ points.push_back(Vector3(0, y, +s * 0.5));
+ }
+
+ normals.push_back(Vector3(0, 0, 1));
+ normals.push_back(Vector3(0, 0, 1));
+ if (shape == SHAPE_CROSS) {
+ normals.push_back(Vector3(1, 0, 0));
+ normals.push_back(Vector3(1, 0, 0));
+ }
+
+ uvs.push_back(Vector2(0, v));
+ uvs.push_back(Vector2(1, v));
+ if (shape == SHAPE_CROSS) {
+ uvs.push_back(Vector2(0, v));
+ uvs.push_back(Vector2(1, v));
+ }
+
+ ADD_TANGENT(0.0, 1.0, 0.0, 1.0)
+ ADD_TANGENT(0.0, 1.0, 0.0, 1.0)
+ if (shape == SHAPE_CROSS) {
+ ADD_TANGENT(0.0, 1.0, 0.0, 1.0)
+ ADD_TANGENT(0.0, 1.0, 0.0, 1.0)
+ }
+
+ for (int i = 0; i < (shape == SHAPE_CROSS ? 4 : 2); i++) {
+ bone_indices.push_back(bone);
+ bone_indices.push_back(MIN(sections, bone + 1));
+ bone_indices.push_back(0);
+ bone_indices.push_back(0);
+
+ bone_weights.push_back(blend);
+ bone_weights.push_back(1.0 - blend);
+ bone_weights.push_back(0);
+ bone_weights.push_back(0);
+ }
+
+ if (j > 0) {
+ if (shape == SHAPE_CROSS) {
+ int base = j * 4 - 4;
+ indices.push_back(base + 0);
+ indices.push_back(base + 1);
+ indices.push_back(base + 4);
+
+ indices.push_back(base + 1);
+ indices.push_back(base + 5);
+ indices.push_back(base + 4);
+
+ indices.push_back(base + 2);
+ indices.push_back(base + 3);
+ indices.push_back(base + 6);
+
+ indices.push_back(base + 3);
+ indices.push_back(base + 7);
+ indices.push_back(base + 6);
+ } else {
+ int base = j * 2 - 2;
+ indices.push_back(base + 0);
+ indices.push_back(base + 1);
+ indices.push_back(base + 2);
+
+ indices.push_back(base + 1);
+ indices.push_back(base + 3);
+ indices.push_back(base + 2);
+ }
+ }
+ }
+
+ p_arr[RS::ARRAY_VERTEX] = points;
+ p_arr[RS::ARRAY_NORMAL] = normals;
+ p_arr[RS::ARRAY_TANGENT] = tangents;
+ p_arr[RS::ARRAY_TEX_UV] = uvs;
+ p_arr[RS::ARRAY_BONES] = bone_indices;
+ p_arr[RS::ARRAY_WEIGHTS] = bone_weights;
+ p_arr[RS::ARRAY_INDEX] = indices;
+}
+
+void RibbonTrailMesh::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_size", "size"), &RibbonTrailMesh::set_size);
+ ClassDB::bind_method(D_METHOD("get_size"), &RibbonTrailMesh::get_size);
+
+ ClassDB::bind_method(D_METHOD("set_sections", "sections"), &RibbonTrailMesh::set_sections);
+ ClassDB::bind_method(D_METHOD("get_sections"), &RibbonTrailMesh::get_sections);
+
+ ClassDB::bind_method(D_METHOD("set_section_length", "section_length"), &RibbonTrailMesh::set_section_length);
+ ClassDB::bind_method(D_METHOD("get_section_length"), &RibbonTrailMesh::get_section_length);
+
+ ClassDB::bind_method(D_METHOD("set_section_segments", "section_segments"), &RibbonTrailMesh::set_section_segments);
+ ClassDB::bind_method(D_METHOD("get_section_segments"), &RibbonTrailMesh::get_section_segments);
+
+ ClassDB::bind_method(D_METHOD("set_curve", "curve"), &RibbonTrailMesh::set_curve);
+ ClassDB::bind_method(D_METHOD("get_curve"), &RibbonTrailMesh::get_curve);
+
+ ClassDB::bind_method(D_METHOD("set_shape", "shape"), &RibbonTrailMesh::set_shape);
+ ClassDB::bind_method(D_METHOD("get_shape"), &RibbonTrailMesh::get_shape);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "shape", PROPERTY_HINT_ENUM, "Flat,Cross"), "set_shape", "get_shape");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_size", "get_size");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "sections", PROPERTY_HINT_RANGE, "2,128,1"), "set_sections", "get_sections");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "section_length", PROPERTY_HINT_RANGE, "0.001,1024.0,0.001,or_greater"), "set_section_length", "get_section_length");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "section_segments", PROPERTY_HINT_RANGE, "1,128,1"), "set_section_segments", "get_section_segments");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve", "get_curve");
+
+ BIND_ENUM_CONSTANT(SHAPE_FLAT)
+ BIND_ENUM_CONSTANT(SHAPE_CROSS)
+}
+
+RibbonTrailMesh::RibbonTrailMesh() {
+}
diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h
index f0ae611b5e..7915cb0028 100644
--- a/scene/resources/primitive_meshes.h
+++ b/scene/resources/primitive_meshes.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -49,17 +49,19 @@ private:
mutable AABB aabb;
AABB custom_aabb;
- mutable int array_len;
- mutable int index_array_len;
+ mutable int array_len = 0;
+ mutable int index_array_len = 0;
Ref<Material> material;
- bool flip_faces;
+ bool flip_faces = false;
- mutable bool pending_request;
+ // make sure we do an update after we've finished constructing our object
+ mutable bool pending_request = true;
void _update() const;
protected:
- Mesh::PrimitiveType primitive_type;
+ // assume primitive triangles as the type, correct for all but one and it will change this :)
+ Mesh::PrimitiveType primitive_type = Mesh::PRIMITIVE_TRIANGLES;
static void _bind_methods();
@@ -79,6 +81,7 @@ public:
virtual Ref<Material> surface_get_material(int p_idx) const override;
virtual int get_blend_shape_count() const override;
virtual StringName get_blend_shape_name(int p_index) const override;
+ virtual void set_blend_shape_name(int p_index, const StringName &p_name) override;
virtual AABB get_aabb() const override;
virtual RID get_rid() const override;
@@ -104,10 +107,10 @@ class CapsuleMesh : public PrimitiveMesh {
GDCLASS(CapsuleMesh, PrimitiveMesh);
private:
- float radius;
- float mid_height;
- int radial_segments;
- int rings;
+ float radius = 1.0;
+ float height = 3.0;
+ int radial_segments = 64;
+ int rings = 8;
protected:
static void _bind_methods();
@@ -117,8 +120,8 @@ public:
void set_radius(const float p_radius);
float get_radius() const;
- void set_mid_height(const float p_mid_height);
- float get_mid_height() const;
+ void set_height(const float p_height);
+ float get_height() const;
void set_radial_segments(const int p_segments);
int get_radial_segments() const;
@@ -130,16 +133,16 @@ public:
};
/**
- Similar to test cube but with subdivision support and different texture coordinates
+ A box
*/
-class CubeMesh : public PrimitiveMesh {
- GDCLASS(CubeMesh, PrimitiveMesh);
+class BoxMesh : public PrimitiveMesh {
+ GDCLASS(BoxMesh, PrimitiveMesh);
private:
- Vector3 size;
- int subdivide_w;
- int subdivide_h;
- int subdivide_d;
+ Vector3 size = Vector3(2.0, 2.0, 2.0);
+ int subdivide_w = 0;
+ int subdivide_h = 0;
+ int subdivide_d = 0;
protected:
static void _bind_methods();
@@ -158,7 +161,7 @@ public:
void set_subdivide_depth(const int p_divisions);
int get_subdivide_depth() const;
- CubeMesh();
+ BoxMesh();
};
/**
@@ -169,11 +172,11 @@ class CylinderMesh : public PrimitiveMesh {
GDCLASS(CylinderMesh, PrimitiveMesh);
private:
- float top_radius;
- float bottom_radius;
- float height;
- int radial_segments;
- int rings;
+ float top_radius = 1.0;
+ float bottom_radius = 1.0;
+ float height = 2.0;
+ int radial_segments = 64;
+ int rings = 4;
protected:
static void _bind_methods();
@@ -205,9 +208,10 @@ class PlaneMesh : public PrimitiveMesh {
GDCLASS(PlaneMesh, PrimitiveMesh);
private:
- Size2 size;
- int subdivide_w;
- int subdivide_d;
+ Size2 size = Size2(2.0, 2.0);
+ int subdivide_w = 0;
+ int subdivide_d = 0;
+ Vector3 center_offset;
protected:
static void _bind_methods();
@@ -223,6 +227,9 @@ public:
void set_subdivide_depth(const int p_divisions);
int get_subdivide_depth() const;
+ void set_center_offset(const Vector3 p_offset);
+ Vector3 get_center_offset() const;
+
PlaneMesh();
};
@@ -233,11 +240,11 @@ class PrismMesh : public PrimitiveMesh {
GDCLASS(PrismMesh, PrimitiveMesh);
private:
- float left_to_right;
- Vector3 size;
- int subdivide_w;
- int subdivide_h;
- int subdivide_d;
+ float left_to_right = 0.5;
+ Vector3 size = Vector3(2.0, 2.0, 2.0);
+ int subdivide_w = 0;
+ int subdivide_h = 0;
+ int subdivide_d = 0;
protected:
static void _bind_methods();
@@ -270,7 +277,8 @@ class QuadMesh : public PrimitiveMesh {
GDCLASS(QuadMesh, PrimitiveMesh);
private:
- Size2 size;
+ Size2 size = Size2(1.0, 1.0);
+ Vector3 center_offset;
protected:
static void _bind_methods();
@@ -281,6 +289,9 @@ public:
void set_size(const Size2 &p_size);
Size2 get_size() const;
+
+ void set_center_offset(const Vector3 p_offset);
+ Vector3 get_center_offset() const;
};
/**
@@ -290,11 +301,11 @@ class SphereMesh : public PrimitiveMesh {
GDCLASS(SphereMesh, PrimitiveMesh);
private:
- float radius;
- float height;
- int radial_segments;
- int rings;
- bool is_hemisphere;
+ float radius = 1.0;
+ float height = 2.0;
+ int radial_segments = 64;
+ int rings = 32;
+ bool is_hemisphere = false;
protected:
static void _bind_methods();
@@ -333,4 +344,98 @@ public:
PointMesh();
};
+class TubeTrailMesh : public PrimitiveMesh {
+ GDCLASS(TubeTrailMesh, PrimitiveMesh);
+
+private:
+ float radius = 1.0;
+ int radial_steps = 8;
+ int sections = 5;
+ float section_length = 0.2;
+ int section_rings = 3;
+
+ Ref<Curve> curve;
+
+ void _curve_changed();
+
+protected:
+ static void _bind_methods();
+ virtual void _create_mesh_array(Array &p_arr) const override;
+
+public:
+ void set_radius(const float p_radius);
+ float get_radius() const;
+
+ void set_radial_steps(const int p_radial_steps);
+ int get_radial_steps() const;
+
+ void set_sections(const int p_sections);
+ int get_sections() const;
+
+ void set_section_length(float p_sectionlength);
+ float get_section_length() const;
+
+ void set_section_rings(const int p_section_rings);
+ int get_section_rings() const;
+
+ void set_curve(const Ref<Curve> &p_curve);
+ Ref<Curve> get_curve() const;
+
+ virtual int get_builtin_bind_pose_count() const override;
+ virtual Transform3D get_builtin_bind_pose(int p_index) const override;
+
+ TubeTrailMesh();
+};
+
+class RibbonTrailMesh : public PrimitiveMesh {
+ GDCLASS(RibbonTrailMesh, PrimitiveMesh);
+
+public:
+ enum Shape {
+ SHAPE_FLAT,
+ SHAPE_CROSS
+ };
+
+private:
+ float size = 1.0;
+ int sections = 5;
+ float section_length = 0.2;
+ int section_segments = 3;
+
+ Shape shape = SHAPE_CROSS;
+
+ Ref<Curve> curve;
+
+ void _curve_changed();
+
+protected:
+ static void _bind_methods();
+ virtual void _create_mesh_array(Array &p_arr) const override;
+
+public:
+ void set_shape(Shape p_shape);
+ Shape get_shape() const;
+
+ void set_size(const float p_size);
+ float get_size() const;
+
+ void set_sections(const int p_sections);
+ int get_sections() const;
+
+ void set_section_length(float p_sectionlength);
+ float get_section_length() const;
+
+ void set_section_segments(const int p_section_segments);
+ int get_section_segments() const;
+
+ void set_curve(const Ref<Curve> &p_curve);
+ Ref<Curve> get_curve() const;
+
+ virtual int get_builtin_bind_pose_count() const override;
+ virtual Transform3D get_builtin_bind_pose(int p_index) const override;
+
+ RibbonTrailMesh();
+};
+
+VARIANT_ENUM_CAST(RibbonTrailMesh::Shape)
#endif
diff --git a/scene/resources/rectangle_shape_2d.cpp b/scene/resources/rectangle_shape_2d.cpp
index 949fddf2e7..17ce0b34ac 100644
--- a/scene/resources/rectangle_shape_2d.cpp
+++ b/scene/resources/rectangle_shape_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,40 +33,78 @@
#include "servers/physics_server_2d.h"
#include "servers/rendering_server.h"
void RectangleShape2D::_update_shape() {
- PhysicsServer2D::get_singleton()->shape_set_data(get_rid(), extents);
+ PhysicsServer2D::get_singleton()->shape_set_data(get_rid(), size * 0.5);
emit_changed();
}
-void RectangleShape2D::set_extents(const Vector2 &p_extents) {
- extents = p_extents;
+#ifndef DISABLE_DEPRECATED
+bool RectangleShape2D::_set(const StringName &p_name, const Variant &p_value) {
+ if (p_name == "extents") { // Compatibility with Godot 3.x.
+ // Convert to `size`, twice as big.
+ set_size((Vector2)p_value * 2);
+ return true;
+ }
+ return false;
+}
+
+bool RectangleShape2D::_get(const StringName &p_name, Variant &r_property) const {
+ if (p_name == "extents") { // Compatibility with Godot 3.x.
+ // Convert to `extents`, half as big.
+ r_property = size / 2;
+ return true;
+ }
+ return false;
+}
+#endif // DISABLE_DEPRECATED
+
+void RectangleShape2D::set_size(const Vector2 &p_size) {
+ size = p_size;
_update_shape();
}
-Vector2 RectangleShape2D::get_extents() const {
- return extents;
+Vector2 RectangleShape2D::get_size() const {
+ return size;
}
void RectangleShape2D::draw(const RID &p_to_rid, const Color &p_color) {
- RenderingServer::get_singleton()->canvas_item_add_rect(p_to_rid, Rect2(-extents, extents * 2.0), p_color);
+ RenderingServer::get_singleton()->canvas_item_add_rect(p_to_rid, Rect2(-size * 0.5, size), p_color);
+ if (is_collision_outline_enabled()) {
+ // Draw an outlined rectangle to make individual shapes easier to distinguish.
+ Vector<Vector2> stroke_points;
+ stroke_points.resize(5);
+ stroke_points.write[0] = -size * 0.5;
+ stroke_points.write[1] = Vector2(size.x, -size.y) * 0.5;
+ stroke_points.write[2] = size * 0.5;
+ stroke_points.write[3] = Vector2(-size.x, size.y) * 0.5;
+ stroke_points.write[4] = -size * 0.5;
+
+ Vector<Color> stroke_colors;
+ stroke_colors.resize(5);
+ for (int i = 0; i < 5; i++) {
+ stroke_colors.write[i] = (p_color);
+ }
+
+ RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, stroke_points, stroke_colors);
+ }
}
Rect2 RectangleShape2D::get_rect() const {
- return Rect2(-extents, extents * 2.0);
+ return Rect2(-size * 0.5, size);
}
real_t RectangleShape2D::get_enclosing_radius() const {
- return extents.length();
+ return size.length() / 2;
}
void RectangleShape2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_extents", "extents"), &RectangleShape2D::set_extents);
- ClassDB::bind_method(D_METHOD("get_extents"), &RectangleShape2D::get_extents);
+ ClassDB::bind_method(D_METHOD("set_size", "size"), &RectangleShape2D::set_size);
+ ClassDB::bind_method(D_METHOD("get_size"), &RectangleShape2D::get_size);
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "extents"), "set_extents", "get_extents");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size");
}
RectangleShape2D::RectangleShape2D() :
Shape2D(PhysicsServer2D::get_singleton()->rectangle_shape_create()) {
- extents = Vector2(10, 10);
+ size = Vector2(20, 20);
_update_shape();
}
diff --git a/scene/resources/rectangle_shape_2d.h b/scene/resources/rectangle_shape_2d.h
index 6efa7ab9c8..f1e8be4c5b 100644
--- a/scene/resources/rectangle_shape_2d.h
+++ b/scene/resources/rectangle_shape_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -36,15 +36,19 @@
class RectangleShape2D : public Shape2D {
GDCLASS(RectangleShape2D, Shape2D);
- Vector2 extents;
+ Vector2 size;
void _update_shape();
protected:
static void _bind_methods();
+#ifndef DISABLE_DEPRECATED
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_property) const;
+#endif // DISABLE_DEPRECATED
public:
- void set_extents(const Vector2 &p_extents);
- Vector2 get_extents() const;
+ void set_size(const Vector2 &p_size);
+ Vector2 get_size() const;
virtual void draw(const RID &p_to_rid, const Color &p_color) override;
virtual Rect2 get_rect() const override;
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index ba39593690..dbe118a262 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,15 +30,16 @@
#include "resource_format_text.h"
+#include "core/config/project_settings.h"
+#include "core/io/dir_access.h"
#include "core/io/resource_format_binary.h"
-#include "core/os/dir_access.h"
-#include "core/project_settings.h"
#include "core/version.h"
-//version 2: changed names for basis, aabb, Vectors, etc.
-#define FORMAT_VERSION 2
+// Version 2: changed names for Basis, AABB, Vectors, etc.
+// Version 3: new string ID for ext/subresources, breaks forward compat.
+#define FORMAT_VERSION 3
-#include "core/os/dir_access.h"
+#include "core/io/dir_access.h"
#include "core/version.h"
#define _printerr() ERR_PRINT(String(res_path + ":" + itos(lines) + " - Parse Error: " + error_text).utf8().get_data());
@@ -56,22 +57,23 @@ Ref<Resource> ResourceLoaderText::get_resource() {
Error ResourceLoaderText::_parse_sub_resource_dummy(DummyReadData *p_data, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) {
VariantParser::Token token;
VariantParser::get_token(p_stream, token, line, r_err_str);
- if (token.type != VariantParser::TK_NUMBER) {
- r_err_str = "Expected number (sub-resource index)";
+ if (token.type != VariantParser::TK_NUMBER && token.type != VariantParser::TK_STRING) {
+ r_err_str = "Expected number (old style) or string (sub-resource index)";
return ERR_PARSE_ERROR;
}
- int index = token.value;
+ String unique_id = token.value;
- if (!p_data->resource_map.has(index)) {
+ if (!p_data->resource_map.has(unique_id)) {
Ref<DummyResource> dr;
- dr.instance();
- dr->set_subindex(index);
- p_data->resource_map[index] = dr;
- p_data->resource_set.insert(dr);
+ dr.instantiate();
+ dr->set_scene_unique_id(unique_id);
+ p_data->resource_map[unique_id] = dr;
+ uint32_t im_size = p_data->resource_index_map.size();
+ p_data->resource_index_map.insert(dr, im_size);
}
- r_res = p_data->resource_map[index];
+ r_res = p_data->resource_map[unique_id];
VariantParser::get_token(p_stream, token, line, r_err_str);
if (token.type != VariantParser::TK_PARENTHESIS_CLOSE) {
@@ -85,12 +87,12 @@ Error ResourceLoaderText::_parse_sub_resource_dummy(DummyReadData *p_data, Varia
Error ResourceLoaderText::_parse_ext_resource_dummy(DummyReadData *p_data, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) {
VariantParser::Token token;
VariantParser::get_token(p_stream, token, line, r_err_str);
- if (token.type != VariantParser::TK_NUMBER) {
- r_err_str = "Expected number (sub-resource index)";
+ if (token.type != VariantParser::TK_NUMBER && token.type != VariantParser::TK_STRING) {
+ r_err_str = "Expected number (old style sub-resource index) or String (ext-resource ID)";
return ERR_PARSE_ERROR;
}
- int id = token.value;
+ String id = token.value;
ERR_FAIL_COND_V(!p_data->rev_external_resources.has(id), ERR_PARSE_ERROR);
@@ -108,29 +110,14 @@ Error ResourceLoaderText::_parse_ext_resource_dummy(DummyReadData *p_data, Varia
Error ResourceLoaderText::_parse_sub_resource(VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) {
VariantParser::Token token;
VariantParser::get_token(p_stream, token, line, r_err_str);
- if (token.type != VariantParser::TK_NUMBER) {
- r_err_str = "Expected number (sub-resource index)";
+ if (token.type != VariantParser::TK_NUMBER && token.type != VariantParser::TK_STRING) {
+ r_err_str = "Expected number (old style sub-resource index) or string";
return ERR_PARSE_ERROR;
}
- int index = token.value;
-
- if (use_nocache) {
- r_res = int_resources[index];
- } else {
- String path = local_path + "::" + itos(index);
-
- if (!ignore_resource_parsing) {
- if (!ResourceCache::has(path)) {
- r_err_str = "Can't load cached sub-resource: " + path;
- return ERR_PARSE_ERROR;
- }
-
- r_res = RES(ResourceCache::get(path));
- } else {
- r_res = RES();
- }
- }
+ String id = token.value;
+ ERR_FAIL_COND_V(!int_resources.has(id), ERR_INVALID_PARAMETER);
+ r_res = int_resources[id];
VariantParser::get_token(p_stream, token, line, r_err_str);
if (token.type != VariantParser::TK_PARENTHESIS_CLOSE) {
@@ -144,16 +131,16 @@ Error ResourceLoaderText::_parse_sub_resource(VariantParser::Stream *p_stream, R
Error ResourceLoaderText::_parse_ext_resource(VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) {
VariantParser::Token token;
VariantParser::get_token(p_stream, token, line, r_err_str);
- if (token.type != VariantParser::TK_NUMBER) {
- r_err_str = "Expected number (sub-resource index)";
+ if (token.type != VariantParser::TK_NUMBER && token.type != VariantParser::TK_STRING) {
+ r_err_str = "Expected number (old style sub-resource index) or String (ext-resource ID)";
return ERR_PARSE_ERROR;
}
- int id = token.value;
+ String id = token.value;
if (!ignore_resource_parsing) {
if (!ext_resources.has(id)) {
- r_err_str = "Can't load cached ext-resource #" + itos(id);
+ r_err_str = "Can't load cached ext-resource id: " + id;
return ERR_PARSE_ERROR;
}
@@ -198,7 +185,7 @@ Error ResourceLoaderText::_parse_ext_resource(VariantParser::Stream *p_stream, R
Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourceParser &parser) {
Ref<PackedScene> packed_scene;
- packed_scene.instance();
+ packed_scene.instantiate();
while (true) {
if (next_tag.name == "node") {
@@ -223,7 +210,7 @@ Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourcePars
if (next_tag.fields.has("type")) {
type = packed_scene->get_state()->add_name(next_tag.fields["type"]);
} else {
- type = SceneState::TYPE_INSTANCED; //no type? assume this was instanced
+ type = SceneState::TYPE_INSTANCED; //no type? assume this was instantiated
}
if (next_tag.fields.has("instance")) {
@@ -424,7 +411,18 @@ Error ResourceLoaderText::load() {
String path = next_tag.fields["path"];
String type = next_tag.fields["type"];
- int index = next_tag.fields["id"];
+ String id = next_tag.fields["id"];
+
+ if (next_tag.fields.has("uid")) {
+ String uidt = next_tag.fields["uid"];
+ ResourceUID::ID uid = ResourceUID::get_singleton()->text_to_id(uidt);
+ if (uid != ResourceUID::INVALID_ID && ResourceUID::get_singleton()->has_id(uid)) {
+ // If a UID is found and the path is valid, it will be used, otherwise, it falls back to the path.
+ path = ResourceUID::get_singleton()->get_id_path(uid);
+ } else {
+ WARN_PRINT(String(res_path + ":" + itos(lines) + " - ext_resource, invalid UUID: " + uidt + " - using text path instead: " + path).utf8().get_data());
+ }
+ }
if (path.find("://") == -1 && path.is_rel_path()) {
// path is relative to file being loaded, so convert to a resource path
@@ -440,7 +438,7 @@ Error ResourceLoaderText::load() {
er.type = type;
if (use_sub_threads) {
- Error err = ResourceLoader::load_threaded_request(path, type, use_sub_threads, local_path);
+ Error err = ResourceLoader::load_threaded_request(path, type, use_sub_threads, ResourceFormatLoader::CACHE_MODE_REUSE, local_path);
if (err != OK) {
if (ResourceLoader::get_abort_on_missing_resources()) {
@@ -468,14 +466,14 @@ Error ResourceLoaderText::load() {
} else {
#ifdef TOOLS_ENABLED
//remember ID for saving
- res->set_id_for_path(local_path, index);
+ res->set_id_for_path(local_path, id);
#endif
}
er.cache = res;
}
- ext_resources[index] = er;
+ ext_resources[id] = er;
error = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &rp);
@@ -504,47 +502,68 @@ Error ResourceLoaderText::load() {
if (!next_tag.fields.has("id")) {
error = ERR_FILE_CORRUPT;
- error_text = "Missing 'index' in external resource tag";
+ error_text = "Missing 'id' in external resource tag";
_printerr();
return error;
}
String type = next_tag.fields["type"];
- int id = next_tag.fields["id"];
+ String id = next_tag.fields["id"];
- String path = local_path + "::" + itos(id);
+ String path = local_path + "::" + id;
//bool exists=ResourceCache::has(path);
Ref<Resource> res;
+ bool do_assign = false;
- if (use_nocache || !ResourceCache::has(path)) { //only if it doesn't exist
-
- Object *obj = ClassDB::instance(type);
- if (!obj) {
- error_text += "Can't create sub resource of type: " + type;
- _printerr();
- error = ERR_FILE_CORRUPT;
- return error;
+ if (cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE && ResourceCache::has(path)) {
+ //reuse existing
+ Resource *r = ResourceCache::get(path);
+ if (r && r->get_class() == type) {
+ res = Ref<Resource>(r);
+ res->reset_state();
+ do_assign = true;
}
+ }
- Resource *r = Object::cast_to<Resource>(obj);
- if (!r) {
- error_text += "Can't create sub resource of type, because not a resource: " + type;
- _printerr();
- error = ERR_FILE_CORRUPT;
- return error;
- }
+ if (res.is_null()) { //not reuse
+ if (cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE && ResourceCache::has(path)) { //only if it doesn't exist
+ //cached, do not assign
+ Resource *r = ResourceCache::get(path);
+ res = Ref<Resource>(r);
+ } else {
+ //create
- res = Ref<Resource>(r);
- int_resources[id] = res;
- if (!use_nocache) {
- res->set_path(path);
+ Object *obj = ClassDB::instantiate(type);
+ if (!obj) {
+ error_text += "Can't create sub resource of type: " + type;
+ _printerr();
+ error = ERR_FILE_CORRUPT;
+ return error;
+ }
+
+ Resource *r = Object::cast_to<Resource>(obj);
+ if (!r) {
+ error_text += "Can't create sub resource of type, because not a resource: " + type;
+ _printerr();
+ error = ERR_FILE_CORRUPT;
+ return error;
+ }
+
+ res = Ref<Resource>(r);
+ do_assign = true;
}
}
resource_current++;
+ int_resources[id] = res; //always assign int resources
+ if (do_assign && cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
+ res->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE);
+ res->set_scene_unique_id(id);
+ }
+
while (true) {
String assign;
Variant value;
@@ -557,7 +576,7 @@ Error ResourceLoaderText::load() {
}
if (assign != String()) {
- if (res.is_valid()) {
+ if (do_assign) {
res->set(assign, value);
}
//it's assignment
@@ -589,23 +608,33 @@ Error ResourceLoaderText::load() {
return error;
}
- Object *obj = ClassDB::instance(res_type);
- if (!obj) {
- error_text += "Can't create sub resource of type: " + res_type;
- _printerr();
- error = ERR_FILE_CORRUPT;
- return error;
+ if (cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE && ResourceCache::has(local_path)) {
+ Resource *r = ResourceCache::get(local_path);
+ if (r->get_class() == res_type) {
+ r->reset_state();
+ resource = Ref<Resource>(r);
+ }
}
- Resource *r = Object::cast_to<Resource>(obj);
- if (!r) {
- error_text += "Can't create sub resource of type, because not a resource: " + res_type;
- _printerr();
- error = ERR_FILE_CORRUPT;
- return error;
- }
+ if (!resource.is_valid()) {
+ Object *obj = ClassDB::instantiate(res_type);
+ if (!obj) {
+ error_text += "Can't create sub resource of type: " + res_type;
+ _printerr();
+ error = ERR_FILE_CORRUPT;
+ return error;
+ }
+
+ Resource *r = Object::cast_to<Resource>(obj);
+ if (!r) {
+ error_text += "Can't create sub resource of type, because not a resource: " + res_type;
+ _printerr();
+ error = ERR_FILE_CORRUPT;
+ return error;
+ }
- resource = Ref<Resource>(r);
+ resource = Ref<Resource>(r);
+ }
resource_current++;
@@ -620,7 +649,7 @@ Error ResourceLoaderText::load() {
_printerr();
} else {
error = OK;
- if (!use_nocache) {
+ if (cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
if (!ResourceCache::has(res_path)) {
resource->set_path(res_path);
}
@@ -668,7 +697,7 @@ Error ResourceLoaderText::load() {
error = OK;
//get it here
resource = packed_scene;
- if (!use_nocache && !ResourceCache::has(res_path)) {
+ if (cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE && !ResourceCache::has(res_path)) {
packed_scene->set_path(res_path);
}
@@ -699,19 +728,7 @@ void ResourceLoaderText::set_translation_remapped(bool p_remapped) {
translation_remapped = p_remapped;
}
-ResourceLoaderText::ResourceLoaderText() {
- use_nocache = false;
-
- resources_total = 0;
- resource_current = 0;
- use_sub_threads = false;
-
- progress = nullptr;
- lines = false;
- translation_remapped = false;
- use_sub_threads = false;
- error = OK;
-}
+ResourceLoaderText::ResourceLoaderText() {}
ResourceLoaderText::~ResourceLoaderText() {
memdelete(f);
@@ -732,7 +749,7 @@ void ResourceLoaderText::get_dependencies(FileAccess *p_f, List<String> *p_depen
if (!next_tag.fields.has("id")) {
error = ERR_FILE_CORRUPT;
- error_text = "Missing 'index' in external resource tag";
+ error_text = "Missing 'id' in external resource tag";
_printerr();
return;
}
@@ -740,7 +757,18 @@ void ResourceLoaderText::get_dependencies(FileAccess *p_f, List<String> *p_depen
String path = next_tag.fields["path"];
String type = next_tag.fields["type"];
- if (path.find("://") == -1 && path.is_rel_path()) {
+ bool using_uid = false;
+ if (next_tag.fields.has("uid")) {
+ //if uid exists, return uid in text format, not the path
+ String uidt = next_tag.fields["uid"];
+ ResourceUID::ID uid = ResourceUID::get_singleton()->text_to_id(uidt);
+ if (uid != ResourceUID::INVALID_ID) {
+ path = ResourceUID::get_singleton()->id_to_text(uid);
+ using_uid = true;
+ }
+ }
+
+ if (!using_uid && path.find("://") == -1 && path.is_rel_path()) {
// path is relative to file being loaded, so convert to a resource path
path = ProjectSettings::get_singleton()->localize_path(local_path.get_base_dir().plus_file(path));
}
@@ -810,9 +838,17 @@ Error ResourceLoaderText::rename_dependencies(FileAccess *p_f, const String &p_p
}
String path = next_tag.fields["path"];
- int index = next_tag.fields["id"];
+ String id = next_tag.fields["id"];
String type = next_tag.fields["type"];
+ if (next_tag.fields.has("uid")) {
+ String uidt = next_tag.fields["uid"];
+ ResourceUID::ID uid = ResourceUID::get_singleton()->text_to_id(uidt);
+ if (uid != ResourceUID::INVALID_ID && ResourceUID::get_singleton()->has_id(uid)) {
+ // If a UID is found and the path is valid, it will be used, otherwise, it falls back to the path.
+ path = ResourceUID::get_singleton()->get_id_path(uid);
+ }
+ }
bool relative = false;
if (!path.begins_with("res://")) {
path = base_path.plus_file(path).simplify_path();
@@ -829,7 +865,14 @@ Error ResourceLoaderText::rename_dependencies(FileAccess *p_f, const String &p_p
path = base_path.path_to_file(path);
}
- fw->store_line("[ext_resource path=\"" + path + "\" type=\"" + type + "\" id=" + itos(index) + "]");
+ String s = "[ext_resource type=\"" + type + "\"";
+
+ ResourceUID::ID uid = ResourceSaver::get_resource_id_for_path(path);
+ if (uid != ResourceUID::INVALID_ID) {
+ s += " uid=\"" + ResourceUID::get_singleton()->id_to_text(uid) + "\"";
+ }
+ s += " path=\"" + path + "\" id=\"" + id + "\"]";
+ fw->store_line(s); // Bundled.
tag_end = f->get_position();
}
@@ -838,6 +881,11 @@ Error ResourceLoaderText::rename_dependencies(FileAccess *p_f, const String &p_p
f->seek(tag_end);
uint8_t c = f->get_8();
+ if (c == '\n' && !f->eof_reached()) {
+ // Skip first newline character since we added one
+ c = f->get_8();
+ }
+
while (!f->eof_reached()) {
fw->store_8(c);
c = f->get_8();
@@ -910,6 +958,12 @@ void ResourceLoaderText::open(FileAccess *p_f, bool p_skip_first_tag) {
return;
}
+ if (tag.fields.has("uid")) {
+ res_uid = ResourceUID::get_singleton()->text_to_id(tag.fields["uid"]);
+ } else {
+ res_uid = ResourceUID::INVALID_ID;
+ }
+
if (tag.fields.has("load_steps")) {
resources_total = tag.fields["load_steps"];
} else {
@@ -928,7 +982,6 @@ void ResourceLoaderText::open(FileAccess *p_f, bool p_skip_first_tag) {
rp.ext_func = _parse_ext_resources;
rp.sub_func = _parse_sub_resources;
- rp.func = nullptr;
rp.userdata = this;
}
@@ -965,12 +1018,17 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path)
bs_save_unicode_string(wf.f, is_scene ? "PackedScene" : resource_type);
wf->store_64(0); //offset to import metadata, this is no longer used
- for (int i = 0; i < 14; i++) {
+
+ wf->store_32(ResourceFormatSaverBinaryInstance::FORMAT_FLAG_NAMED_SCENE_IDS | ResourceFormatSaverBinaryInstance::FORMAT_FLAG_UIDS);
+
+ wf->store_64(res_uid);
+
+ for (int i = 0; i < ResourceFormatSaverBinaryInstance::RESERVED_FIELDS; i++) {
wf->store_32(0); // reserved
}
wf->store_32(0); //string table size, will not be in use
- size_t ext_res_count_pos = wf->get_position();
+ uint64_t ext_res_count_pos = wf->get_position();
wf->store_32(0); //zero ext resources, still parsing them
@@ -1006,17 +1064,23 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path)
String path = next_tag.fields["path"];
String type = next_tag.fields["type"];
- int index = next_tag.fields["id"];
+ String id = next_tag.fields["id"];
+ ResourceUID::ID uid = ResourceUID::INVALID_ID;
+ if (next_tag.fields.has("uid")) {
+ String uidt = next_tag.fields["uid"];
+ uid = ResourceUID::get_singleton()->text_to_id(uidt);
+ }
bs_save_unicode_string(wf.f, type);
bs_save_unicode_string(wf.f, path);
+ wf->store_64(uid);
int lindex = dummy_read.external_resources.size();
Ref<DummyResource> dr;
- dr.instance();
+ dr.instantiate();
dr->set_path("res://dummy" + itos(lindex)); //anything is good to detect it for saving as external
dummy_read.external_resources[dr] = lindex;
- dummy_read.rev_external_resources[index] = dr;
+ dummy_read.rev_external_resources[id] = dr;
error = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &rp);
@@ -1033,7 +1097,7 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path)
//now, save resources to a separate file, for now
- size_t sub_res_count_pos = wf->get_position();
+ uint64_t sub_res_count_pos = wf->get_position();
wf->store_32(0); //zero sub resources, still parsing them
String temp_file = p_path + ".temp";
@@ -1042,8 +1106,8 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path)
return ERR_CANT_OPEN;
}
- Vector<size_t> local_offsets;
- Vector<size_t> local_pointers_pos;
+ Vector<uint64_t> local_offsets;
+ Vector<uint64_t> local_pointers_pos;
while (next_tag.name == "sub_resource" || next_tag.name == "resource") {
String type;
@@ -1060,7 +1124,7 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path)
if (!next_tag.fields.has("id")) {
error = ERR_FILE_CORRUPT;
- error_text = "Missing 'index' in external resource tag";
+ error_text = "Missing 'id' in external resource tag";
_printerr();
return error;
}
@@ -1081,7 +1145,7 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path)
wf->store_64(0); //temp local offset
bs_save_unicode_string(wf2, type);
- size_t propcount_ofs = wf2->get_position();
+ uint64_t propcount_ofs = wf2->get_position();
wf2->store_32(0);
int prop_count = 0;
@@ -1105,7 +1169,7 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path)
if (assign != String()) {
Map<StringName, int> empty_string_map; //unused
bs_save_unicode_string(wf2, assign, true);
- ResourceFormatSaverBinaryInstance::write_variant(wf2, value, dummy_read.resource_set, dummy_read.external_resources, empty_string_map);
+ ResourceFormatSaverBinaryInstance::write_variant(wf2, value, dummy_read.resource_index_map, dummy_read.external_resources, empty_string_map);
prop_count++;
} else if (next_tag.name != String()) {
@@ -1151,22 +1215,22 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path)
local_offsets.push_back(wf2->get_position());
bs_save_unicode_string(wf2, "PackedScene");
- size_t propcount_ofs = wf2->get_position();
+ uint64_t propcount_ofs = wf2->get_position();
wf2->store_32(0);
int prop_count = 0;
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
- if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) {
+ for (const PropertyInfo &E : props) {
+ if (!(E.usage & PROPERTY_USAGE_STORAGE)) {
continue;
}
- String name = E->get().name;
+ String name = E.name;
Variant value = packed_scene->get(name);
Map<StringName, int> empty_string_map; //unused
bs_save_unicode_string(wf2, name, true);
- ResourceFormatSaverBinaryInstance::write_variant(wf2, value, dummy_read.resource_set, dummy_read.external_resources, empty_string_map);
+ ResourceFormatSaverBinaryInstance::write_variant(wf2, value, dummy_read.resource_index_map, dummy_read.external_resources, empty_string_map);
prop_count++;
}
@@ -1177,7 +1241,7 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path)
wf2->close();
- size_t offset_from = wf->get_position();
+ uint64_t offset_from = wf->get_position();
wf->seek(sub_res_count_pos); //plus one because the saved one
wf->store_32(local_offsets.size());
@@ -1246,9 +1310,35 @@ String ResourceLoaderText::recognize(FileAccess *p_f) {
return tag.fields["type"];
}
+ResourceUID::ID ResourceLoaderText::get_uid(FileAccess *p_f) {
+ error = OK;
+
+ lines = 1;
+ f = p_f;
+
+ stream.f = f;
+
+ ignore_resource_parsing = true;
+
+ VariantParser::Tag tag;
+ Error err = VariantParser::parse_tag(&stream, lines, error_text, tag);
+
+ if (err) {
+ _printerr();
+ return ResourceUID::INVALID_ID;
+ }
+
+ if (tag.fields.has("uid")) { //field is optional
+ String uidt = tag.fields["uid"];
+ return ResourceUID::get_singleton()->text_to_id(uidt);
+ }
+
+ return ResourceUID::INVALID_ID;
+}
+
/////////////////////
-RES ResourceFormatLoaderText::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoaderText::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
if (r_error) {
*r_error = ERR_CANT_OPEN;
}
@@ -1261,12 +1351,11 @@ RES ResourceFormatLoaderText::load(const String &p_path, const String &p_origina
ResourceLoaderText loader;
String path = p_original_path != "" ? p_original_path : p_path;
- loader.use_nocache = p_no_cache;
+ loader.cache_mode = p_cache_mode;
loader.use_sub_threads = p_use_sub_threads;
loader.local_path = ProjectSettings::get_singleton()->localize_path(path);
loader.progress = r_progress;
loader.res_path = loader.local_path;
- //loader.set_local_path( ProjectSettings::get_singleton()->localize_path(p_path) );
loader.open(f);
err = loader.load();
if (r_error) {
@@ -1309,21 +1398,38 @@ String ResourceFormatLoaderText::get_resource_type(const String &p_path) const {
return String();
}
- //for anyhting else must test..
+ // ...for anything else must test...
FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
if (!f) {
- return ""; //could not rwead
+ return ""; //could not read
}
ResourceLoaderText loader;
loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path);
loader.res_path = loader.local_path;
- //loader.set_local_path( ProjectSettings::get_singleton()->localize_path(p_path) );
String r = loader.recognize(f);
return ClassDB::get_compatibility_remapped_class(r);
}
+ResourceUID::ID ResourceFormatLoaderText::get_resource_uid(const String &p_path) const {
+ String ext = p_path.get_extension().to_lower();
+
+ if (ext != "tscn" && ext != "tres") {
+ return ResourceUID::INVALID_ID;
+ }
+
+ FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
+ if (!f) {
+ return ResourceUID::INVALID_ID; //could not read
+ }
+
+ ResourceLoaderText loader;
+ loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path);
+ loader.res_path = loader.local_path;
+ return loader.get_uid(f);
+}
+
void ResourceFormatLoaderText::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) {
FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
if (!f) {
@@ -1333,7 +1439,6 @@ void ResourceFormatLoaderText::get_dependencies(const String &p_path, List<Strin
ResourceLoaderText loader;
loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path);
loader.res_path = loader.local_path;
- //loader.set_local_path( ProjectSettings::get_singleton()->localize_path(p_path) );
loader.get_dependencies(f, p_dependencies, p_add_types);
}
@@ -1346,7 +1451,6 @@ Error ResourceFormatLoaderText::rename_dependencies(const String &p_path, const
ResourceLoaderText loader;
loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path);
loader.res_path = loader.local_path;
- //loader.set_local_path( ProjectSettings::get_singleton()->localize_path(p_path) );
return loader.rename_dependencies(f, p_path, p_map);
}
@@ -1362,7 +1466,6 @@ Error ResourceFormatLoaderText::convert_file_to_binary(const String &p_src_path,
const String &path = p_src_path;
loader.local_path = ProjectSettings::get_singleton()->localize_path(path);
loader.res_path = loader.local_path;
- //loader.set_local_path( ProjectSettings::get_singleton()->localize_path(p_path) );
loader.open(f);
return loader.save_as_binary(f, p_dst_path);
}
@@ -1385,10 +1488,10 @@ String ResourceFormatSaverTextInstance::_write_resources(void *ud, const RES &p_
String ResourceFormatSaverTextInstance::_write_resource(const RES &res) {
if (external_resources.has(res)) {
- return "ExtResource( " + itos(external_resources[res]) + " )";
+ return "ExtResource( \"" + external_resources[res] + "\" )";
} else {
if (internal_resources.has(res)) {
- return "SubResource( " + itos(internal_resources[res]) + " )";
+ return "SubResource( \"" + internal_resources[res] + "\" )";
} else if (res->get_path().length() && res->get_path().find("::") == -1) {
if (res->get_path() == local_path) { //circular reference attempt
return "null";
@@ -1417,8 +1520,11 @@ void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant,
ERR_PRINT("Circular reference to resource being saved found: '" + local_path + "' will be null next time it's loaded.");
return;
}
- int index = external_resources.size();
- external_resources[res] = index;
+
+ // Use a numeric ID as a base, because they are sorted in natural order before saving.
+ // This increases the chances of thread loading to fetch them first.
+ String id = itos(external_resources.size() + 1) + "_" + Resource::generate_scene_unique_id();
+ external_resources[res] = id;
return;
}
@@ -1474,8 +1580,8 @@ void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant,
Dictionary d = p_variant;
List<Variant> keys;
d.get_key_list(&keys);
- for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
- Variant v = d[E->get()];
+ for (const Variant &E : keys) {
+ Variant v = d[E];
_find_resources(v);
}
} break;
@@ -1504,11 +1610,11 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
takeover_paths = false;
}
- // save resources
+ // Save resources.
_find_resources(p_resource, true);
if (packed_scene.is_valid()) {
- //add instances to external resources if saving a packed scene
+ // Add instances to external resources if saving a packed scene.
for (int i = 0; i < packed_scene->get_state()->get_node_count(); i++) {
if (packed_scene->get_state()->is_node_instance_placeholder(i)) {
continue;
@@ -1516,8 +1622,8 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
Ref<PackedScene> instance = packed_scene->get_state()->get_node_instance(i);
if (instance.is_valid() && !external_resources.has(instance)) {
- int index = external_resources.size();
- external_resources[instance] = index;
+ int index = external_resources.size() + 1;
+ external_resources[instance] = itos(index) + "_" + Resource::generate_scene_unique_id(); // Keep the order for improved thread loading performance.
}
}
}
@@ -1528,64 +1634,74 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
title += "type=\"" + p_resource->get_class() + "\" ";
}
int load_steps = saved_resources.size() + external_resources.size();
- /*
- if (packed_scene.is_valid()) {
- load_steps+=packed_scene->get_node_count();
- }
- //no, better to not use load steps from nodes, no point to that
- */
if (load_steps > 1) {
title += "load_steps=" + itos(load_steps) + " ";
}
title += "format=" + itos(FORMAT_VERSION) + "";
+ ResourceUID::ID uid = ResourceSaver::get_resource_id_for_path(local_path, true);
+
+ if (uid != ResourceUID::INVALID_ID) {
+ title += " uid=\"" + ResourceUID::get_singleton()->id_to_text(uid) + "\"";
+ }
+
f->store_string(title);
- f->store_line("]\n"); //one empty line
+ f->store_line("]\n"); // One empty line.
}
#ifdef TOOLS_ENABLED
- //keep order from cached ids
- Set<int> cached_ids_found;
- for (Map<RES, int>::Element *E = external_resources.front(); E; E = E->next()) {
- int cached_id = E->key()->get_id_for_path(local_path);
- if (cached_id < 0 || cached_ids_found.has(cached_id)) {
- E->get() = -1; //reset
+ // Keep order from cached ids.
+ Set<String> cached_ids_found;
+ for (Map<RES, String>::Element *E = external_resources.front(); E; E = E->next()) {
+ String cached_id = E->key()->get_id_for_path(local_path);
+ if (cached_id == "" || cached_ids_found.has(cached_id)) {
+ int sep_pos = E->get().find("_");
+ if (sep_pos != -1) {
+ E->get() = E->get().substr(0, sep_pos + 1); // Keep the order found, for improved thread loading performance.
+ } else {
+ E->get() = "";
+ }
+
} else {
E->get() = cached_id;
cached_ids_found.insert(cached_id);
}
}
- //create IDs for non cached resources
- for (Map<RES, int>::Element *E = external_resources.front(); E; E = E->next()) {
- if (cached_ids_found.has(E->get())) { //already cached, go on
+ // Create IDs for non cached resources.
+ for (Map<RES, String>::Element *E = external_resources.front(); E; E = E->next()) {
+ if (cached_ids_found.has(E->get())) { // Already cached, go on.
continue;
}
- int attempt = 1; //start from one, more readable format
- while (cached_ids_found.has(attempt)) {
- attempt++;
+ String attempt;
+ while (true) {
+ attempt = E->get() + Resource::generate_scene_unique_id();
+ if (!cached_ids_found.has(attempt)) {
+ break;
+ }
}
cached_ids_found.insert(attempt);
E->get() = attempt;
- //update also in resource
+ // Update also in resource.
Ref<Resource> res = E->key();
res->set_id_for_path(local_path, attempt);
}
#else
- //make sure to start from one, as it makes format more readable
- for (Map<RES, int>::Element *E = external_resources.front(); E; E = E->next()) {
- E->get() = E->get() + 1;
+ // Make sure to start from one, as it makes format more readable.
+ int counter = 1;
+ for (Map<RES, String>::Element *E = external_resources.front(); E; E = E->next()) {
+ E->get() = itos(counter++);
}
#endif
Vector<ResourceSort> sorted_er;
- for (Map<RES, int>::Element *E = external_resources.front(); E; E = E->next()) {
+ for (Map<RES, String>::Element *E = external_resources.front(); E; E = E->next()) {
ResourceSort rs;
rs.resource = E->key();
- rs.index = E->get();
+ rs.id = E->get();
sorted_er.push_back(rs);
}
@@ -1594,23 +1710,30 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
for (int i = 0; i < sorted_er.size(); i++) {
String p = sorted_er[i].resource->get_path();
- f->store_string("[ext_resource path=\"" + p + "\" type=\"" + sorted_er[i].resource->get_save_class() + "\" id=" + itos(sorted_er[i].index) + "]\n"); //bundled
+ String s = "[ext_resource type=\"" + sorted_er[i].resource->get_save_class() + "\"";
+
+ ResourceUID::ID uid = ResourceSaver::get_resource_id_for_path(p, false);
+ if (uid != ResourceUID::INVALID_ID) {
+ s += " uid=\"" + ResourceUID::get_singleton()->id_to_text(uid) + "\"";
+ }
+ s += " path=\"" + p + "\" id=\"" + sorted_er[i].id + "\"]\n";
+ f->store_string(s); // Bundled.
}
if (external_resources.size()) {
- f->store_line(String()); //separate
+ f->store_line(String()); // Separate.
}
- Set<int> used_indices;
+ Set<String> used_unique_ids;
for (List<RES>::Element *E = saved_resources.front(); E; E = E->next()) {
RES res = E->get();
if (E->next() && (res->get_path() == "" || res->get_path().find("::") != -1)) {
- if (res->get_subindex() != 0) {
- if (used_indices.has(res->get_subindex())) {
- res->set_subindex(0); //repeated
+ if (res->get_scene_unique_id() != "") {
+ if (used_unique_ids.has(res->get_scene_unique_id())) {
+ res->set_scene_unique_id(""); // Repeated.
} else {
- used_indices.insert(res->get_subindex());
+ used_unique_ids.insert(res->get_scene_unique_id());
}
}
}
@@ -1622,31 +1745,35 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
bool main = (E->next() == nullptr);
if (main && packed_scene.is_valid()) {
- break; //save as a scene
+ break; // Save as a scene.
}
if (main) {
f->store_line("[resource]");
} else {
String line = "[sub_resource ";
- if (res->get_subindex() == 0) {
- int new_subindex = 1;
- if (used_indices.size()) {
- new_subindex = used_indices.back()->get() + 1;
+ if (res->get_scene_unique_id() == "") {
+ String new_id;
+ while (true) {
+ new_id = res->get_class() + "_" + Resource::generate_scene_unique_id();
+
+ if (!used_unique_ids.has(new_id)) {
+ break;
+ }
}
- res->set_subindex(new_subindex);
- used_indices.insert(new_subindex);
+ res->set_scene_unique_id(new_id);
+ used_unique_ids.insert(new_id);
}
- int idx = res->get_subindex();
- line += "type=\"" + res->get_class() + "\" id=" + itos(idx);
- f->store_line(line + "]");
+ String id = res->get_scene_unique_id();
+ line += "type=\"" + res->get_class() + "\" id=\"" + id;
+ f->store_line(line + "\"]");
if (takeover_paths) {
- res->set_path(p_path + "::" + itos(idx), true);
+ res->set_path(p_path + "::" + id, true);
}
- internal_resources[res] = idx;
+ internal_resources[res] = id;
#ifdef TOOLS_ENABLED
res->set_edited(false);
#endif
@@ -1654,7 +1781,6 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
List<PropertyInfo> property_list;
res->get_property_list(&property_list);
- //property_list.sort();
for (List<PropertyInfo>::Element *PE = property_list.front(); PE; PE = PE->next()) {
if (skip_editor && PE->get().name.begins_with("__editor")) {
continue;
@@ -1695,7 +1821,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
}
if (packed_scene.is_valid()) {
- //if this is a scene, save nodes and connections!
+ // If this is a scene, save nodes and connections!
Ref<SceneState> state = packed_scene->get_state();
for (int i = 0; i < state->get_node_count(); i++) {
StringName type = state->get_node_type(i);
@@ -1803,7 +1929,6 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
}
f->close();
- //memdelete(f);
return OK;
}
diff --git a/scene/resources/resource_format_text.h b/scene/resources/resource_format_text.h
index cf522c9364..373e71b2c4 100644
--- a/scene/resources/resource_format_text.h
+++ b/scene/resources/resource_format_text.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,19 +31,19 @@
#ifndef RESOURCE_FORMAT_TEXT_H
#define RESOURCE_FORMAT_TEXT_H
+#include "core/io/file_access.h"
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
-#include "core/os/file_access.h"
-#include "core/variant_parser.h"
+#include "core/variant/variant_parser.h"
#include "scene/resources/packed_scene.h"
class ResourceLoaderText {
- bool translation_remapped;
+ bool translation_remapped = false;
String local_path;
String res_path;
String error_text;
- FileAccess *f;
+ FileAccess *f = nullptr;
VariantParser::StreamFile stream;
@@ -53,31 +53,30 @@ class ResourceLoaderText {
String type;
};
- bool is_scene;
+ bool is_scene = false;
String res_type;
- bool ignore_resource_parsing;
+ bool ignore_resource_parsing = false;
- //Map<String,String> remaps;
+ Map<String, ExtResource> ext_resources;
+ Map<String, RES> int_resources;
- Map<int, ExtResource> ext_resources;
- Map<int, RES> int_resources;
-
- int resources_total;
- int resource_current;
+ int resources_total = 0;
+ int resource_current = 0;
String resource_type;
VariantParser::Tag next_tag;
- bool use_nocache;
+ ResourceFormatLoader::CacheMode cache_mode = ResourceFormatLoader::CACHE_MODE_REUSE;
+
+ bool use_sub_threads = false;
+ float *progress = nullptr;
- bool use_sub_threads;
- float *progress;
+ mutable int lines = 0;
- mutable int lines;
+ ResourceUID::ID res_uid = ResourceUID::INVALID_ID;
Map<String, String> remaps;
- //void _printerr();
static Error _parse_sub_resources(void *p_self, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) { return reinterpret_cast<ResourceLoaderText *>(p_self)->_parse_sub_resource(p_stream, r_res, line, r_err_str); }
static Error _parse_ext_resources(void *p_self, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) { return reinterpret_cast<ResourceLoaderText *>(p_self)->_parse_ext_resource(p_stream, r_res, line, r_err_str); }
@@ -92,9 +91,9 @@ class ResourceLoaderText {
struct DummyReadData {
Map<RES, int> external_resources;
- Map<int, RES> rev_external_resources;
- Set<RES> resource_set;
- Map<int, RES> resource_map;
+ Map<String, RES> rev_external_resources;
+ Map<RES, int> resource_index_map;
+ Map<String, RES> resource_map;
};
static Error _parse_sub_resource_dummys(void *p_self, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) { return _parse_sub_resource_dummy((DummyReadData *)(p_self), p_stream, r_res, line, r_err_str); }
@@ -107,7 +106,7 @@ class ResourceLoaderText {
friend class ResourceFormatLoaderText;
- Error error;
+ Error error = OK;
RES resource;
@@ -123,6 +122,7 @@ public:
void open(FileAccess *p_f, bool p_skip_first_tag = false);
String recognize(FileAccess *p_f);
+ ResourceUID::ID get_uid(FileAccess *p_f);
void get_dependencies(FileAccess *p_f, List<String> *p_dependencies, bool p_add_types);
Error rename_dependencies(FileAccess *p_f, const String &p_path, const Map<String, String> &p_map);
@@ -134,11 +134,12 @@ public:
class ResourceFormatLoaderText : public ResourceFormatLoader {
public:
static ResourceFormatLoaderText *singleton;
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+ virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const;
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
+ virtual ResourceUID::ID get_resource_uid(const String &p_path) const;
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
virtual Error rename_dependencies(const String &p_path, const Map<String, String> &p_map);
@@ -152,11 +153,11 @@ class ResourceFormatSaverTextInstance {
Ref<PackedScene> packed_scene;
- bool takeover_paths;
- bool relative_paths;
- bool bundle_resources;
- bool skip_editor;
- FileAccess *f;
+ bool takeover_paths = false;
+ bool relative_paths = false;
+ bool bundle_resources = false;
+ bool skip_editor = false;
+ FileAccess *f = nullptr;
struct NonPersistentKey { //for resource properties generated on the fly
RES base;
@@ -168,14 +169,14 @@ class ResourceFormatSaverTextInstance {
Set<RES> resource_set;
List<RES> saved_resources;
- Map<RES, int> external_resources;
- Map<RES, int> internal_resources;
+ Map<RES, String> external_resources;
+ Map<RES, String> internal_resources;
struct ResourceSort {
RES resource;
- int index;
+ String id;
bool operator<(const ResourceSort &p_right) const {
- return index < p_right.index;
+ return id.naturalnocasecmp_to(p_right.id) < 0;
}
};
diff --git a/scene/resources/segment_shape_2d.cpp b/scene/resources/segment_shape_2d.cpp
index b1001203a1..35439634f8 100644
--- a/scene/resources/segment_shape_2d.cpp
+++ b/scene/resources/segment_shape_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/segment_shape_2d.h b/scene/resources/segment_shape_2d.h
index 31a61ea564..f218955061 100644
--- a/scene/resources/segment_shape_2d.h
+++ b/scene/resources/segment_shape_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/ray_shape_2d.cpp b/scene/resources/separation_ray_shape_2d.cpp
index 67c4f84749..0acd6d268d 100644
--- a/scene/resources/ray_shape_2d.cpp
+++ b/scene/resources/separation_ray_shape_2d.cpp
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* ray_shape_2d.cpp */
+/* separation_ray_shape_2d.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,35 +28,51 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "ray_shape_2d.h"
+#include "separation_ray_shape_2d.h"
#include "servers/physics_server_2d.h"
#include "servers/rendering_server.h"
-void RayShape2D::_update_shape() {
+void SeparationRayShape2D::_update_shape() {
Dictionary d;
d["length"] = length;
- d["slips_on_slope"] = slips_on_slope;
+ d["slide_on_slope"] = slide_on_slope;
PhysicsServer2D::get_singleton()->shape_set_data(get_rid(), d);
emit_changed();
}
-void RayShape2D::draw(const RID &p_to_rid, const Color &p_color) {
- Vector2 tip = Vector2(0, get_length());
- RS::get_singleton()->canvas_item_add_line(p_to_rid, Vector2(), tip, p_color, 3);
+void SeparationRayShape2D::draw(const RID &p_to_rid, const Color &p_color) {
+ const Vector2 target_position = Vector2(0, get_length());
+
+ const float max_arrow_size = 6;
+ const float line_width = 1.4;
+ bool no_line = target_position.length() < line_width;
+ float arrow_size = CLAMP(target_position.length() * 2 / 3, line_width, max_arrow_size);
+
+ if (no_line) {
+ arrow_size = target_position.length();
+ } else {
+ RS::get_singleton()->canvas_item_add_line(p_to_rid, Vector2(), target_position - target_position.normalized() * arrow_size, p_color, line_width);
+ }
+
+ Transform2D xf;
+ xf.rotate(target_position.angle());
+ xf.translate(Vector2(no_line ? 0 : target_position.length() - arrow_size, 0));
+
Vector<Vector2> pts;
- float tsize = 4;
- pts.push_back(tip + Vector2(0, tsize));
- pts.push_back(tip + Vector2(Math_SQRT12 * tsize, 0));
- pts.push_back(tip + Vector2(-Math_SQRT12 * tsize, 0));
+ pts.push_back(xf.xform(Vector2(arrow_size, 0)));
+ pts.push_back(xf.xform(Vector2(0, 0.5 * arrow_size)));
+ pts.push_back(xf.xform(Vector2(0, -0.5 * arrow_size)));
+
Vector<Color> cols;
for (int i = 0; i < 3; i++) {
cols.push_back(p_color);
}
+
RS::get_singleton()->canvas_item_add_primitive(p_to_rid, pts, cols, Vector<Point2>(), RID());
}
-Rect2 RayShape2D::get_rect() const {
+Rect2 SeparationRayShape2D::get_rect() const {
Rect2 rect;
rect.position = Vector2();
rect.expand_to(Vector2(0, length));
@@ -64,42 +80,40 @@ Rect2 RayShape2D::get_rect() const {
return rect;
}
-real_t RayShape2D::get_enclosing_radius() const {
+real_t SeparationRayShape2D::get_enclosing_radius() const {
return length;
}
-void RayShape2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_length", "length"), &RayShape2D::set_length);
- ClassDB::bind_method(D_METHOD("get_length"), &RayShape2D::get_length);
+void SeparationRayShape2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_length", "length"), &SeparationRayShape2D::set_length);
+ ClassDB::bind_method(D_METHOD("get_length"), &SeparationRayShape2D::get_length);
- ClassDB::bind_method(D_METHOD("set_slips_on_slope", "active"), &RayShape2D::set_slips_on_slope);
- ClassDB::bind_method(D_METHOD("get_slips_on_slope"), &RayShape2D::get_slips_on_slope);
+ ClassDB::bind_method(D_METHOD("set_slide_on_slope", "active"), &SeparationRayShape2D::set_slide_on_slope);
+ ClassDB::bind_method(D_METHOD("get_slide_on_slope"), &SeparationRayShape2D::get_slide_on_slope);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length"), "set_length", "get_length");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slips_on_slope"), "set_slips_on_slope", "get_slips_on_slope");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_slope"), "set_slide_on_slope", "get_slide_on_slope");
}
-void RayShape2D::set_length(real_t p_length) {
+void SeparationRayShape2D::set_length(real_t p_length) {
length = p_length;
_update_shape();
}
-real_t RayShape2D::get_length() const {
+real_t SeparationRayShape2D::get_length() const {
return length;
}
-void RayShape2D::set_slips_on_slope(bool p_active) {
- slips_on_slope = p_active;
+void SeparationRayShape2D::set_slide_on_slope(bool p_active) {
+ slide_on_slope = p_active;
_update_shape();
}
-bool RayShape2D::get_slips_on_slope() const {
- return slips_on_slope;
+bool SeparationRayShape2D::get_slide_on_slope() const {
+ return slide_on_slope;
}
-RayShape2D::RayShape2D() :
- Shape2D(PhysicsServer2D::get_singleton()->ray_shape_create()) {
- length = 20;
- slips_on_slope = false;
+SeparationRayShape2D::SeparationRayShape2D() :
+ Shape2D(PhysicsServer2D::get_singleton()->separation_ray_shape_create()) {
_update_shape();
}
diff --git a/scene/resources/ray_shape_2d.h b/scene/resources/separation_ray_shape_2d.h
index c8202ca16c..5b74e6c727 100644
--- a/scene/resources/ray_shape_2d.h
+++ b/scene/resources/separation_ray_shape_2d.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* ray_shape_2d.h */
+/* separation_ray_shape_2d.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,16 +28,16 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef RAY_SHAPE_2D_H
-#define RAY_SHAPE_2D_H
+#ifndef SEPARATION_RAY_SHAPE_2D_H
+#define SEPARATION_RAY_SHAPE_2D_H
#include "scene/resources/shape_2d.h"
-class RayShape2D : public Shape2D {
- GDCLASS(RayShape2D, Shape2D);
+class SeparationRayShape2D : public Shape2D {
+ GDCLASS(SeparationRayShape2D, Shape2D);
- real_t length;
- bool slips_on_slope;
+ real_t length = 20.0;
+ bool slide_on_slope = false;
void _update_shape();
@@ -48,14 +48,14 @@ public:
void set_length(real_t p_length);
real_t get_length() const;
- void set_slips_on_slope(bool p_active);
- bool get_slips_on_slope() const;
+ void set_slide_on_slope(bool p_active);
+ bool get_slide_on_slope() const;
virtual void draw(const RID &p_to_rid, const Color &p_color) override;
virtual Rect2 get_rect() const override;
virtual real_t get_enclosing_radius() const override;
- RayShape2D();
+ SeparationRayShape2D();
};
-#endif // RAY_SHAPE_2D_H
+#endif // SEPARATION_RAY_SHAPE_2D_H
diff --git a/scene/resources/ray_shape_3d.cpp b/scene/resources/separation_ray_shape_3d.cpp
index 39df4c22f9..376e04c844 100644
--- a/scene/resources/ray_shape_3d.cpp
+++ b/scene/resources/separation_ray_shape_3d.cpp
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* ray_shape_3d.cpp */
+/* separation_ray_shape_3d.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,11 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "ray_shape_3d.h"
+#include "separation_ray_shape_3d.h"
#include "servers/physics_server_3d.h"
-Vector<Vector3> RayShape3D::get_debug_mesh_lines() const {
+Vector<Vector3> SeparationRayShape3D::get_debug_mesh_lines() const {
Vector<Vector3> points;
points.push_back(Vector3());
points.push_back(Vector3(0, 0, get_length()));
@@ -40,59 +40,52 @@ Vector<Vector3> RayShape3D::get_debug_mesh_lines() const {
return points;
}
-real_t RayShape3D::get_enclosing_radius() const {
+real_t SeparationRayShape3D::get_enclosing_radius() const {
return length;
}
-void RayShape3D::_update_shape() {
+void SeparationRayShape3D::_update_shape() {
Dictionary d;
d["length"] = length;
- d["slips_on_slope"] = slips_on_slope;
+ d["slide_on_slope"] = slide_on_slope;
PhysicsServer3D::get_singleton()->shape_set_data(get_shape(), d);
Shape3D::_update_shape();
}
-void RayShape3D::set_length(float p_length) {
+void SeparationRayShape3D::set_length(float p_length) {
length = p_length;
_update_shape();
notify_change_to_owners();
- _change_notify("length");
}
-float RayShape3D::get_length() const {
+float SeparationRayShape3D::get_length() const {
return length;
}
-void RayShape3D::set_slips_on_slope(bool p_active) {
- slips_on_slope = p_active;
+void SeparationRayShape3D::set_slide_on_slope(bool p_active) {
+ slide_on_slope = p_active;
_update_shape();
notify_change_to_owners();
- _change_notify("slips_on_slope");
}
-bool RayShape3D::get_slips_on_slope() const {
- return slips_on_slope;
+bool SeparationRayShape3D::get_slide_on_slope() const {
+ return slide_on_slope;
}
-void RayShape3D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_length", "length"), &RayShape3D::set_length);
- ClassDB::bind_method(D_METHOD("get_length"), &RayShape3D::get_length);
+void SeparationRayShape3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_length", "length"), &SeparationRayShape3D::set_length);
+ ClassDB::bind_method(D_METHOD("get_length"), &SeparationRayShape3D::get_length);
- ClassDB::bind_method(D_METHOD("set_slips_on_slope", "active"), &RayShape3D::set_slips_on_slope);
- ClassDB::bind_method(D_METHOD("get_slips_on_slope"), &RayShape3D::get_slips_on_slope);
+ ClassDB::bind_method(D_METHOD("set_slide_on_slope", "active"), &SeparationRayShape3D::set_slide_on_slope);
+ ClassDB::bind_method(D_METHOD("get_slide_on_slope"), &SeparationRayShape3D::get_slide_on_slope);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "0,4096,0.01"), "set_length", "get_length");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slips_on_slope"), "set_slips_on_slope", "get_slips_on_slope");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "0,4096,0.001"), "set_length", "get_length");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_slope"), "set_slide_on_slope", "get_slide_on_slope");
}
-RayShape3D::RayShape3D() :
- Shape3D(PhysicsServer3D::get_singleton()->shape_create(PhysicsServer3D::SHAPE_RAY)) {
- length = 1.0;
- slips_on_slope = false;
-
+SeparationRayShape3D::SeparationRayShape3D() :
+ Shape3D(PhysicsServer3D::get_singleton()->shape_create(PhysicsServer3D::SHAPE_SEPARATION_RAY)) {
/* Code copied from setters to prevent the use of uninitialized variables */
_update_shape();
notify_change_to_owners();
- _change_notify("length");
- _change_notify("slips_on_slope");
}
diff --git a/scene/resources/ray_shape_3d.h b/scene/resources/separation_ray_shape_3d.h
index a1a6702564..54058b6095 100644
--- a/scene/resources/ray_shape_3d.h
+++ b/scene/resources/separation_ray_shape_3d.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* ray_shape_3d.h */
+/* separation_ray_shape_3d.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,14 +28,14 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef RAY_SHAPE_H
-#define RAY_SHAPE_H
+#ifndef SEPARATION_RAY_SHAPE_H
+#define SEPARATION_RAY_SHAPE_H
#include "scene/resources/shape_3d.h"
-class RayShape3D : public Shape3D {
- GDCLASS(RayShape3D, Shape3D);
- float length;
- bool slips_on_slope;
+class SeparationRayShape3D : public Shape3D {
+ GDCLASS(SeparationRayShape3D, Shape3D);
+ float length = 1.0;
+ bool slide_on_slope = false;
protected:
static void _bind_methods();
@@ -45,12 +45,12 @@ public:
void set_length(float p_length);
float get_length() const;
- void set_slips_on_slope(bool p_active);
- bool get_slips_on_slope() const;
+ void set_slide_on_slope(bool p_active);
+ bool get_slide_on_slope() const;
virtual Vector<Vector3> get_debug_mesh_lines() const override;
virtual real_t get_enclosing_radius() const override;
- RayShape3D();
+ SeparationRayShape3D();
};
-#endif // RAY_SHAPE_H
+#endif // SEPARATION_RAY_SHAPE_H
diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp
index 92f0353abf..44d524f142 100644
--- a/scene/resources/shader.cpp
+++ b/scene/resources/shader.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,7 +29,8 @@
/*************************************************************************/
#include "shader.h"
-#include "core/os/file_access.h"
+
+#include "core/io/file_access.h"
#include "scene/scene_string_names.h"
#include "servers/rendering/shader_language.h"
#include "servers/rendering_server.h"
@@ -71,16 +72,16 @@ void Shader::get_param_list(List<PropertyInfo> *p_params) const {
params_cache.clear();
params_cache_dirty = false;
- for (List<PropertyInfo>::Element *E = local.front(); E; E = E->next()) {
- PropertyInfo pi = E->get();
+ for (PropertyInfo &pi : local) {
if (default_textures.has(pi.name)) { //do not show default textures
continue;
}
+ String original_name = pi.name;
pi.name = "shader_param/" + pi.name;
- params_cache[pi.name] = E->get().name;
+ params_cache[pi.name] = original_name;
if (p_params) {
//small little hack
- if (pi.type == Variant::_RID) {
+ if (pi.type == Variant::RID) {
pi.type = Variant::OBJECT;
}
p_params->push_back(pi);
@@ -151,9 +152,7 @@ void Shader::_bind_methods() {
}
Shader::Shader() {
- mode = MODE_SPATIAL;
shader = RenderingServer::get_singleton()->shader_create();
- params_cache_dirty = true;
}
Shader::~Shader() {
@@ -162,13 +161,13 @@ Shader::~Shader() {
////////////
-RES ResourceFormatLoaderShader::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoaderShader::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
if (r_error) {
*r_error = ERR_FILE_CANT_OPEN;
}
Ref<Shader> shader;
- shader.instance();
+ shader.instantiate();
Vector<uint8_t> buffer = FileAccess::get_file_as_array(p_path);
@@ -185,7 +184,7 @@ RES ResourceFormatLoaderShader::load(const String &p_path, const String &p_origi
}
void ResourceFormatLoaderShader::get_recognized_extensions(List<String> *p_extensions) const {
- p_extensions->push_back("shader");
+ p_extensions->push_back("gdshader");
}
bool ResourceFormatLoaderShader::handles_type(const String &p_type) const {
@@ -194,7 +193,7 @@ bool ResourceFormatLoaderShader::handles_type(const String &p_type) const {
String ResourceFormatLoaderShader::get_resource_type(const String &p_path) const {
String el = p_path.get_extension().to_lower();
- if (el == "shader") {
+ if (el == "gdshader") {
return "Shader";
}
return "";
@@ -225,7 +224,7 @@ Error ResourceFormatSaverShader::save(const String &p_path, const RES &p_resourc
void ResourceFormatSaverShader::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const {
if (const Shader *shader = Object::cast_to<Shader>(*p_resource)) {
if (shader->is_text_shader()) {
- p_extensions->push_back("shader");
+ p_extensions->push_back("gdshader");
}
}
}
diff --git a/scene/resources/shader.h b/scene/resources/shader.h
index 7dcec7811a..6563181ca2 100644
--- a/scene/resources/shader.h
+++ b/scene/resources/shader.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,9 +31,9 @@
#ifndef SHADER_H
#define SHADER_H
+#include "core/io/resource.h"
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
-#include "core/resource.h"
#include "scene/resources/texture.h"
class Shader : public Resource {
@@ -42,7 +42,6 @@ class Shader : public Resource {
public:
enum Mode {
-
MODE_SPATIAL,
MODE_CANVAS_ITEM,
MODE_PARTICLES,
@@ -52,12 +51,12 @@ public:
private:
RID shader;
- Mode mode;
+ Mode mode = MODE_SPATIAL;
// hack the name of performance
// shaders keep a list of ShaderMaterial -> RenderingServer name translations, to make
// conversion fast and save memory.
- mutable bool params_cache_dirty;
+ mutable bool params_cache_dirty = true;
mutable Map<StringName, StringName> params_cache; //map a shader param to a material param..
Map<StringName, Ref<Texture2D>> default_textures;
@@ -103,7 +102,7 @@ VARIANT_ENUM_CAST(Shader::Mode);
class ResourceFormatLoaderShader : public ResourceFormatLoader {
public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+ virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
diff --git a/scene/resources/shape_2d.cpp b/scene/resources/shape_2d.cpp
index 94cecc76eb..013b1ef1a9 100644
--- a/scene/resources/shape_2d.cpp
+++ b/scene/resources/shape_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,7 +29,11 @@
/*************************************************************************/
#include "shape_2d.h"
+
+#include "core/config/engine.h"
+#include "core/config/project_settings.h"
#include "servers/physics_server_2d.h"
+
RID Shape2D::get_rid() const {
return shape;
}
@@ -105,9 +109,17 @@ void Shape2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "custom_solver_bias", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_custom_solver_bias", "get_custom_solver_bias");
}
+bool Shape2D::is_collision_outline_enabled() {
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ return true;
+ }
+#endif
+ return GLOBAL_DEF("debug/shapes/collision/draw_2d_outlines", true);
+}
+
Shape2D::Shape2D(const RID &p_rid) {
shape = p_rid;
- custom_bias = 0;
}
Shape2D::~Shape2D() {
diff --git a/scene/resources/shape_2d.h b/scene/resources/shape_2d.h
index bb4688a02d..14bdd60e4b 100644
--- a/scene/resources/shape_2d.h
+++ b/scene/resources/shape_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,14 +31,14 @@
#ifndef SHAPE_2D_H
#define SHAPE_2D_H
-#include "core/resource.h"
+#include "core/io/resource.h"
class Shape2D : public Resource {
GDCLASS(Shape2D, Resource);
OBJ_SAVE_TYPE(Shape2D);
RID shape;
- real_t custom_bias;
+ real_t custom_bias = 0.0;
protected:
static void _bind_methods();
@@ -61,6 +61,9 @@ public:
/// Returns the radius of a circle that fully enclose this shape
virtual real_t get_enclosing_radius() const = 0;
virtual RID get_rid() const override;
+
+ static bool is_collision_outline_enabled();
+
Shape2D();
~Shape2D();
};
diff --git a/scene/resources/shape_3d.cpp b/scene/resources/shape_3d.cpp
index 59766f4f1f..a02a0e5488 100644
--- a/scene/resources/shape_3d.cpp
+++ b/scene/resources/shape_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,7 +35,7 @@
#include "scene/resources/mesh.h"
#include "servers/physics_server_3d.h"
-void Shape3D::add_vertices_to_array(Vector<Vector3> &array, const Transform &p_xform) {
+void Shape3D::add_vertices_to_array(Vector<Vector3> &array, const Transform3D &p_xform) {
Vector<Vector3> toadd = get_debug_mesh_lines();
if (toadd.size()) {
@@ -66,7 +66,7 @@ Ref<ArrayMesh> Shape3D::get_debug_mesh() {
debug_mesh_cache = Ref<ArrayMesh>(memnew(ArrayMesh));
- if (!lines.empty()) {
+ if (!lines.is_empty()) {
//make mesh
Vector<Vector3> array;
array.resize(lines.size());
@@ -102,6 +102,8 @@ void Shape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_margin", "margin"), &Shape3D::set_margin);
ClassDB::bind_method(D_METHOD("get_margin"), &Shape3D::get_margin);
+ ClassDB::bind_method(D_METHOD("get_debug_mesh"), &Shape3D::get_debug_mesh);
+
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_RANGE, "0.001,10,0.001"), "set_margin", "get_margin");
}
diff --git a/scene/resources/shape_3d.h b/scene/resources/shape_3d.h
index eb9607e3a6..b8e529cd3c 100644
--- a/scene/resources/shape_3d.h
+++ b/scene/resources/shape_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,7 +31,7 @@
#ifndef SHAPE_3D_H
#define SHAPE_3D_H
-#include "core/resource.h"
+#include "core/io/resource.h"
class ArrayMesh;
@@ -60,7 +60,7 @@ public:
/// Returns the radius of a sphere that fully enclose this shape
virtual real_t get_enclosing_radius() const = 0;
- void add_vertices_to_array(Vector<Vector3> &array, const Transform &p_xform);
+ void add_vertices_to_array(Vector<Vector3> &array, const Transform3D &p_xform);
real_t get_margin() const;
void set_margin(real_t p_margin);
diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp
new file mode 100644
index 0000000000..e533fb054a
--- /dev/null
+++ b/scene/resources/skeleton_modification_2d.cpp
@@ -0,0 +1,253 @@
+/*************************************************************************/
+/* skeleton_modification_2d.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_2d.h"
+#include "scene/2d/skeleton_2d.h"
+
+#include "scene/2d/collision_object_2d.h"
+#include "scene/2d/collision_shape_2d.h"
+#include "scene/2d/physical_bone_2d.h"
+
+#ifdef TOOLS_ENABLED
+#include "editor/editor_settings.h"
+#endif // TOOLS_ENABLED
+
+///////////////////////////////////////
+// Modification2D
+///////////////////////////////////////
+
+void SkeletonModification2D::_execute(float p_delta) {
+ GDVIRTUAL_CALL(_execute, p_delta);
+
+ if (!enabled) {
+ return;
+ }
+}
+
+void SkeletonModification2D::_setup_modification(SkeletonModificationStack2D *p_stack) {
+ stack = p_stack;
+ if (stack) {
+ is_setup = true;
+ } else {
+ WARN_PRINT("Could not setup modification with name " + get_name());
+ }
+
+ GDVIRTUAL_CALL(_setup_modification, Ref<SkeletonModificationStack2D>(p_stack));
+}
+
+void SkeletonModification2D::_draw_editor_gizmo() {
+ GDVIRTUAL_CALL(_draw_editor_gizmo);
+}
+
+void SkeletonModification2D::set_enabled(bool p_enabled) {
+ enabled = p_enabled;
+
+#ifdef TOOLS_ENABLED
+ if (editor_draw_gizmo) {
+ if (stack) {
+ stack->set_editor_gizmos_dirty(true);
+ }
+ }
+#endif // TOOLS_ENABLED
+}
+
+bool SkeletonModification2D::get_enabled() {
+ return enabled;
+}
+
+float SkeletonModification2D::clamp_angle(float p_angle, float p_min_bound, float 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) {
+ float 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;
+}
+
+void SkeletonModification2D::editor_draw_angle_constraints(Bone2D *p_operation_bone, float p_min_bound, float p_max_bound,
+ bool p_constraint_enabled, bool p_constraint_in_localspace, bool p_constraint_inverted) {
+ if (!p_operation_bone) {
+ return;
+ }
+
+ Color bone_ik_color = Color(1.0, 0.65, 0.0, 0.4);
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ bone_ik_color = EditorSettings::get_singleton()->get("editors/2d/bone_ik_color");
+ }
+#endif // TOOLS_ENABLED
+
+ float arc_angle_min = p_min_bound;
+ float arc_angle_max = p_max_bound;
+ if (arc_angle_min < 0) {
+ arc_angle_min = (Math_PI * 2) + arc_angle_min;
+ }
+ if (arc_angle_max < 0) {
+ arc_angle_max = (Math_PI * 2) + arc_angle_max;
+ }
+ if (arc_angle_min > arc_angle_max) {
+ float tmp = arc_angle_min;
+ arc_angle_min = arc_angle_max;
+ arc_angle_max = tmp;
+ }
+ arc_angle_min += p_operation_bone->get_bone_angle();
+ arc_angle_max += p_operation_bone->get_bone_angle();
+
+ if (p_constraint_enabled) {
+ if (p_constraint_in_localspace) {
+ Node *operation_bone_parent = p_operation_bone->get_parent();
+ Bone2D *operation_bone_parent_bone = Object::cast_to<Bone2D>(operation_bone_parent);
+
+ if (operation_bone_parent_bone) {
+ stack->skeleton->draw_set_transform(
+ 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->to_local(p_operation_bone->get_global_position()));
+ }
+ } else {
+ stack->skeleton->draw_set_transform(stack->skeleton->to_local(p_operation_bone->get_global_position()));
+ }
+
+ if (p_constraint_inverted) {
+ stack->skeleton->draw_arc(Vector2(0, 0), p_operation_bone->get_length(),
+ arc_angle_min + (Math_PI * 2), arc_angle_max, 32, bone_ik_color, 1.0);
+ } else {
+ stack->skeleton->draw_arc(Vector2(0, 0), p_operation_bone->get_length(),
+ arc_angle_min, arc_angle_max, 32, bone_ik_color, 1.0);
+ }
+ stack->skeleton->draw_line(Vector2(0, 0), Vector2(Math::cos(arc_angle_min), Math::sin(arc_angle_min)) * p_operation_bone->get_length(), bone_ik_color, 1.0);
+ 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->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);
+ }
+}
+
+Ref<SkeletonModificationStack2D> SkeletonModification2D::get_modification_stack() {
+ return stack;
+}
+
+void SkeletonModification2D::set_is_setup(bool p_setup) {
+ is_setup = p_setup;
+}
+
+bool SkeletonModification2D::get_is_setup() const {
+ return is_setup;
+}
+
+void SkeletonModification2D::set_execution_mode(int p_mode) {
+ execution_mode = p_mode;
+}
+
+int SkeletonModification2D::get_execution_mode() const {
+ return execution_mode;
+}
+
+void SkeletonModification2D::set_editor_draw_gizmo(bool p_draw_gizmo) {
+ editor_draw_gizmo = p_draw_gizmo;
+#ifdef TOOLS_ENABLED
+ if (is_setup) {
+ if (stack) {
+ stack->set_editor_gizmos_dirty(true);
+ }
+ }
+#endif // TOOLS_ENABLED
+}
+
+bool SkeletonModification2D::get_editor_draw_gizmo() const {
+ return editor_draw_gizmo;
+}
+
+void SkeletonModification2D::_bind_methods() {
+ GDVIRTUAL_BIND(_execute, "delta");
+ GDVIRTUAL_BIND(_setup_modification, "modification_stack")
+ GDVIRTUAL_BIND(_draw_editor_gizmo)
+
+ ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &SkeletonModification2D::set_enabled);
+ ClassDB::bind_method(D_METHOD("get_enabled"), &SkeletonModification2D::get_enabled);
+ ClassDB::bind_method(D_METHOD("get_modification_stack"), &SkeletonModification2D::get_modification_stack);
+ ClassDB::bind_method(D_METHOD("set_is_setup", "is_setup"), &SkeletonModification2D::set_is_setup);
+ ClassDB::bind_method(D_METHOD("get_is_setup"), &SkeletonModification2D::get_is_setup);
+ ClassDB::bind_method(D_METHOD("set_execution_mode", "execution_mode"), &SkeletonModification2D::set_execution_mode);
+ ClassDB::bind_method(D_METHOD("get_execution_mode"), &SkeletonModification2D::get_execution_mode);
+ ClassDB::bind_method(D_METHOD("clamp_angle", "angle", "min", "max", "invert"), &SkeletonModification2D::clamp_angle);
+ ClassDB::bind_method(D_METHOD("set_editor_draw_gizmo", "draw_gizmo"), &SkeletonModification2D::set_editor_draw_gizmo);
+ ClassDB::bind_method(D_METHOD("get_editor_draw_gizmo"), &SkeletonModification2D::get_editor_draw_gizmo);
+
+ 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");
+}
+
+SkeletonModification2D::SkeletonModification2D() {
+ stack = nullptr;
+ is_setup = false;
+}
diff --git a/scene/resources/skeleton_modification_2d.h b/scene/resources/skeleton_modification_2d.h
new file mode 100644
index 0000000000..aaddb9136e
--- /dev/null
+++ b/scene/resources/skeleton_modification_2d.h
@@ -0,0 +1,89 @@
+/*************************************************************************/
+/* skeleton_modification_2d.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 SKELETONMODIFICATION2D_H
+#define SKELETONMODIFICATION2D_H
+
+#include "scene/2d/skeleton_2d.h"
+#include "scene/resources/skeleton_modification_stack_2d.h"
+
+///////////////////////////////////////
+// SkeletonModification2D
+///////////////////////////////////////
+
+class SkeletonModificationStack2D;
+class Bone2D;
+
+class SkeletonModification2D : public Resource {
+ GDCLASS(SkeletonModification2D, Resource);
+ friend class Skeleton2D;
+ friend class Bone2D;
+
+protected:
+ static void _bind_methods();
+
+ SkeletonModificationStack2D *stack;
+ int execution_mode = 0; // 0 = process
+
+ bool enabled = true;
+ bool is_setup = false;
+
+ bool _print_execution_error(bool p_condition, String p_message);
+
+ GDVIRTUAL1(_execute, double)
+ GDVIRTUAL1(_setup_modification, Ref<SkeletonModificationStack2D>)
+ GDVIRTUAL0(_draw_editor_gizmo)
+
+public:
+ virtual void _execute(float _delta);
+ virtual void _setup_modification(SkeletonModificationStack2D *p_stack);
+ virtual void _draw_editor_gizmo();
+
+ bool editor_draw_gizmo = false;
+ void set_editor_draw_gizmo(bool p_draw_gizmo);
+ bool get_editor_draw_gizmo() const;
+
+ void set_enabled(bool p_enabled);
+ bool get_enabled();
+
+ Ref<SkeletonModificationStack2D> get_modification_stack();
+ void set_is_setup(bool p_setup);
+ bool get_is_setup() const;
+
+ void set_execution_mode(int p_mode);
+ int get_execution_mode() const;
+
+ float clamp_angle(float p_angle, float p_min_bound, float p_max_bound, bool p_invert_clamp = false);
+ void editor_draw_angle_constraints(Bone2D *p_operation_bone, float p_min_bound, float p_max_bound, bool p_constraint_enabled, bool p_constraint_in_localspace, bool p_constraint_inverted);
+
+ SkeletonModification2D();
+};
+
+#endif // SKELETONMODIFICATION2D_H
diff --git a/scene/resources/skeleton_modification_2d_ccdik.cpp b/scene/resources/skeleton_modification_2d_ccdik.cpp
new file mode 100644
index 0000000000..6eab8d4afe
--- /dev/null
+++ b/scene/resources/skeleton_modification_2d_ccdik.cpp
@@ -0,0 +1,545 @@
+/*************************************************************************/
+/* skeleton_modification_2d_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 "skeleton_modification_2d_ccdik.h"
+#include "scene/2d/skeleton_2d.h"
+
+#ifdef TOOLS_ENABLED
+#include "editor/editor_settings.h"
+#endif // TOOLS_ENABLED
+
+bool SkeletonModification2DCCDIK::_set(const StringName &p_path, const Variant &p_value) {
+ String path = p_path;
+
+ if (path.begins_with("joint_data/")) {
+ int which = path.get_slicec('/', 1).to_int();
+ String what = path.get_slicec('/', 2);
+ ERR_FAIL_INDEX_V(which, ccdik_data_chain.size(), false);
+
+ if (what == "bone2d_node") {
+ set_ccdik_joint_bone2d_node(which, p_value);
+ } else if (what == "bone_index") {
+ set_ccdik_joint_bone_index(which, p_value);
+ } else if (what == "rotate_from_joint") {
+ set_ccdik_joint_rotate_from_joint(which, p_value);
+ } else if (what == "enable_constraint") {
+ set_ccdik_joint_enable_constraint(which, p_value);
+ } else if (what == "constraint_angle_min") {
+ set_ccdik_joint_constraint_angle_min(which, Math::deg2rad(float(p_value)));
+ } else if (what == "constraint_angle_max") {
+ set_ccdik_joint_constraint_angle_max(which, Math::deg2rad(float(p_value)));
+ } else if (what == "constraint_angle_invert") {
+ set_ccdik_joint_constraint_angle_invert(which, p_value);
+ } else if (what == "constraint_in_localspace") {
+ set_ccdik_joint_constraint_in_localspace(which, p_value);
+ }
+
+#ifdef TOOLS_ENABLED
+ if (what.begins_with("editor_draw_gizmo")) {
+ set_ccdik_joint_editor_draw_gizmo(which, p_value);
+ }
+#endif // TOOLS_ENABLED
+
+ return true;
+ }
+
+#ifdef TOOLS_ENABLED
+ if (path.begins_with("editor/draw_gizmo")) {
+ set_editor_draw_gizmo(p_value);
+ }
+#endif // TOOLS_ENABLED
+
+ return true;
+}
+
+bool SkeletonModification2DCCDIK::_get(const StringName &p_path, Variant &r_ret) const {
+ String path = p_path;
+
+ if (path.begins_with("joint_data/")) {
+ int which = path.get_slicec('/', 1).to_int();
+ String what = path.get_slicec('/', 2);
+ ERR_FAIL_INDEX_V(which, ccdik_data_chain.size(), false);
+
+ if (what == "bone2d_node") {
+ r_ret = get_ccdik_joint_bone2d_node(which);
+ } else if (what == "bone_index") {
+ r_ret = get_ccdik_joint_bone_index(which);
+ } else if (what == "rotate_from_joint") {
+ r_ret = get_ccdik_joint_rotate_from_joint(which);
+ } else if (what == "enable_constraint") {
+ r_ret = get_ccdik_joint_enable_constraint(which);
+ } else if (what == "constraint_angle_min") {
+ r_ret = Math::rad2deg(get_ccdik_joint_constraint_angle_min(which));
+ } else if (what == "constraint_angle_max") {
+ r_ret = Math::rad2deg(get_ccdik_joint_constraint_angle_max(which));
+ } else if (what == "constraint_angle_invert") {
+ r_ret = get_ccdik_joint_constraint_angle_invert(which);
+ } else if (what == "constraint_in_localspace") {
+ r_ret = get_ccdik_joint_constraint_in_localspace(which);
+ }
+
+#ifdef TOOLS_ENABLED
+ if (what.begins_with("editor_draw_gizmo")) {
+ r_ret = get_ccdik_joint_editor_draw_gizmo(which);
+ }
+#endif // TOOLS_ENABLED
+
+ return true;
+ }
+
+#ifdef TOOLS_ENABLED
+ if (path.begins_with("editor/draw_gizmo")) {
+ r_ret = get_editor_draw_gizmo();
+ }
+#endif // TOOLS_ENABLED
+
+ return true;
+}
+
+void SkeletonModification2DCCDIK::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (int i = 0; i < ccdik_data_chain.size(); i++) {
+ String base_string = "joint_data/" + itos(i) + "/";
+
+ p_list->push_back(PropertyInfo(Variant::INT, base_string + "bone_index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::NODE_PATH, base_string + "bone2d_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Bone2D", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "rotate_from_joint", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+
+ p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "enable_constraint", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ if (ccdik_data_chain[i].enable_constraint) {
+ p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "constraint_angle_min", PROPERTY_HINT_RANGE, "-360, 360, 0.01", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "constraint_angle_max", PROPERTY_HINT_RANGE, "-360, 360, 0.01", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "constraint_angle_invert", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "constraint_in_localspace", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ }
+
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "editor_draw_gizmo", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ }
+#endif // TOOLS_ENABLED
+ }
+
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ p_list->push_back(PropertyInfo(Variant::BOOL, "editor/draw_gizmo", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ }
+#endif // TOOLS_ENABLED
+}
+
+void SkeletonModification2DCCDIK::_execute(float 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()) {
+ WARN_PRINT_ONCE("Target cache is out of date. Attempting to update...");
+ update_target_cache();
+ return;
+ }
+ if (tip_node_cache.is_null()) {
+ WARN_PRINT_ONCE("Tip cache is out of date. Attempting to update...");
+ update_tip_cache();
+ return;
+ }
+
+ Node2D *target = Object::cast_to<Node2D>(ObjectDB::get_instance(target_node_cache));
+ if (!target || !target->is_inside_tree()) {
+ ERR_PRINT_ONCE("Target node is not in the scene tree. Cannot execute modification!");
+ return;
+ }
+
+ Node2D *tip = Object::cast_to<Node2D>(ObjectDB::get_instance(tip_node_cache));
+ if (!tip || !tip->is_inside_tree()) {
+ ERR_PRINT_ONCE("Tip node is not in the scene tree. Cannot execute modification!");
+ return;
+ }
+
+ for (int i = 0; i < ccdik_data_chain.size(); i++) {
+ _execute_ccdik_joint(i, target, tip);
+ }
+}
+
+void SkeletonModification2DCCDIK::_execute_ccdik_joint(int p_joint_idx, Node2D *p_target, Node2D *p_tip) {
+ CCDIK_Joint_Data2D ccdik_data = ccdik_data_chain[p_joint_idx];
+ if (ccdik_data.bone_idx < 0 || ccdik_data.bone_idx > stack->skeleton->get_bone_count()) {
+ ERR_PRINT_ONCE("2D CCDIK joint: bone index not found!");
+ return;
+ }
+
+ Bone2D *operation_bone = stack->skeleton->get_bone(ccdik_data.bone_idx);
+ Transform2D operation_transform = operation_bone->get_global_transform();
+
+ 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_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_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_scale());
+
+ // Apply constraints in globalspace:
+ if (ccdik_data.enable_constraint && !ccdik_data.constraint_in_localspace) {
+ operation_transform.set_rotation(clamp_angle(operation_transform.get_rotation(), ccdik_data.constraint_angle_min, ccdik_data.constraint_angle_max, ccdik_data.constraint_angle_invert));
+ }
+
+ // Convert from a global transform to a delta and then apply the delta to the local transform.
+ operation_bone->set_global_transform(operation_transform);
+ operation_transform = operation_bone->get_transform();
+
+ // Apply constraints in localspace:
+ if (ccdik_data.enable_constraint && ccdik_data.constraint_in_localspace) {
+ operation_transform.set_rotation(clamp_angle(operation_transform.get_rotation(), ccdik_data.constraint_angle_min, ccdik_data.constraint_angle_max, ccdik_data.constraint_angle_invert));
+ }
+
+ // Set the local pose override, and to make sure child bones are also updated, set the transform of the bone.
+ stack->skeleton->set_bone_local_pose_override(ccdik_data.bone_idx, operation_transform, stack->strength, true);
+ operation_bone->set_transform(operation_transform);
+ operation_bone->notification(operation_bone->NOTIFICATION_TRANSFORM_CHANGED);
+}
+
+void SkeletonModification2DCCDIK::_setup_modification(SkeletonModificationStack2D *p_stack) {
+ stack = p_stack;
+
+ if (stack != nullptr) {
+ is_setup = true;
+ update_target_cache();
+ update_tip_cache();
+ }
+}
+
+void SkeletonModification2DCCDIK::_draw_editor_gizmo() {
+ if (!enabled || !is_setup) {
+ return;
+ }
+
+ for (int i = 0; i < ccdik_data_chain.size(); i++) {
+ if (!ccdik_data_chain[i].editor_draw_gizmo) {
+ continue;
+ }
+
+ Bone2D *operation_bone = stack->skeleton->get_bone(ccdik_data_chain[i].bone_idx);
+ editor_draw_angle_constraints(operation_bone, ccdik_data_chain[i].constraint_angle_min, ccdik_data_chain[i].constraint_angle_max,
+ ccdik_data_chain[i].enable_constraint, ccdik_data_chain[i].constraint_in_localspace, ccdik_data_chain[i].constraint_angle_invert);
+ }
+}
+
+void SkeletonModification2DCCDIK::update_target_cache() {
+ if (!is_setup || !stack) {
+ ERR_PRINT_ONCE("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();
+ }
+ }
+ }
+}
+
+void SkeletonModification2DCCDIK::update_tip_cache() {
+ if (!is_setup || !stack) {
+ ERR_PRINT_ONCE("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 the scene tree!");
+ tip_node_cache = node->get_instance_id();
+ }
+ }
+ }
+}
+
+void SkeletonModification2DCCDIK::ccdik_joint_update_bone2d_cache(int p_joint_idx) {
+ ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "Cannot update bone2d cache: joint index out of range!");
+ if (!is_setup || !stack) {
+ ERR_PRINT_ONCE("Cannot update CCDIK Bone2D cache: modification is not properly setup!");
+ return;
+ }
+
+ ccdik_data_chain.write[p_joint_idx].bone2d_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree()) {
+ if (stack->skeleton->has_node(ccdik_data_chain[p_joint_idx].bone2d_node)) {
+ Node *node = stack->skeleton->get_node(ccdik_data_chain[p_joint_idx].bone2d_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update CCDIK joint " + itos(p_joint_idx) + " Bone2D cache: node is this modification's skeleton or cannot be found!");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update CCDIK joint " + itos(p_joint_idx) + " Bone2D cache: node is not in the scene tree!");
+ ccdik_data_chain.write[p_joint_idx].bone2d_node_cache = node->get_instance_id();
+
+ Bone2D *bone = Object::cast_to<Bone2D>(node);
+ if (bone) {
+ ccdik_data_chain.write[p_joint_idx].bone_idx = bone->get_index_in_skeleton();
+ } else {
+ ERR_FAIL_MSG("CCDIK joint " + itos(p_joint_idx) + " Bone2D cache: Nodepath to Bone2D is not a Bone2D node!");
+ }
+ }
+ }
+ }
+}
+
+void SkeletonModification2DCCDIK::set_target_node(const NodePath &p_target_node) {
+ target_node = p_target_node;
+ update_target_cache();
+}
+
+NodePath SkeletonModification2DCCDIK::get_target_node() const {
+ return target_node;
+}
+
+void SkeletonModification2DCCDIK::set_tip_node(const NodePath &p_tip_node) {
+ tip_node = p_tip_node;
+ update_tip_cache();
+}
+
+NodePath SkeletonModification2DCCDIK::get_tip_node() const {
+ return tip_node;
+}
+
+void SkeletonModification2DCCDIK::set_ccdik_data_chain_length(int p_length) {
+ ccdik_data_chain.resize(p_length);
+ notify_property_list_changed();
+}
+
+int SkeletonModification2DCCDIK::get_ccdik_data_chain_length() {
+ return ccdik_data_chain.size();
+}
+
+void SkeletonModification2DCCDIK::set_ccdik_joint_bone2d_node(int p_joint_idx, const NodePath &p_target_node) {
+ ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!");
+ ccdik_data_chain.write[p_joint_idx].bone2d_node = p_target_node;
+ ccdik_joint_update_bone2d_cache(p_joint_idx);
+
+ notify_property_list_changed();
+}
+
+NodePath SkeletonModification2DCCDIK::get_ccdik_joint_bone2d_node(int p_joint_idx) const {
+ ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), NodePath(), "CCDIK joint out of range!");
+ return ccdik_data_chain[p_joint_idx].bone2d_node;
+}
+
+void SkeletonModification2DCCDIK::set_ccdik_joint_bone_index(int p_joint_idx, int p_bone_idx) {
+ ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCCDIK joint out of range!");
+ ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!");
+
+ if (is_setup) {
+ if (stack->skeleton) {
+ ERR_FAIL_INDEX_MSG(p_bone_idx, stack->skeleton->get_bone_count(), "Passed-in Bone index is out of range!");
+ ccdik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx;
+ ccdik_data_chain.write[p_joint_idx].bone2d_node_cache = stack->skeleton->get_bone(p_bone_idx)->get_instance_id();
+ ccdik_data_chain.write[p_joint_idx].bone2d_node = stack->skeleton->get_path_to(stack->skeleton->get_bone(p_bone_idx));
+ } else {
+ WARN_PRINT("Cannot verify the CCDIK joint " + itos(p_joint_idx) + " bone index for this modification...");
+ ccdik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx;
+ }
+ } else {
+ WARN_PRINT("Cannot verify the CCDIK joint " + itos(p_joint_idx) + " bone index for this modification...");
+ ccdik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx;
+ }
+
+ notify_property_list_changed();
+}
+
+int SkeletonModification2DCCDIK::get_ccdik_joint_bone_index(int p_joint_idx) const {
+ ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), -1, "CCDIK joint out of range!");
+ return ccdik_data_chain[p_joint_idx].bone_idx;
+}
+
+void SkeletonModification2DCCDIK::set_ccdik_joint_rotate_from_joint(int p_joint_idx, bool p_rotate_from_joint) {
+ ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!");
+ ccdik_data_chain.write[p_joint_idx].rotate_from_joint = p_rotate_from_joint;
+}
+
+bool SkeletonModification2DCCDIK::get_ccdik_joint_rotate_from_joint(int p_joint_idx) const {
+ ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), false, "CCDIK joint out of range!");
+ return ccdik_data_chain[p_joint_idx].rotate_from_joint;
+}
+
+void SkeletonModification2DCCDIK::set_ccdik_joint_enable_constraint(int p_joint_idx, bool p_constraint) {
+ ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!");
+ ccdik_data_chain.write[p_joint_idx].enable_constraint = p_constraint;
+ notify_property_list_changed();
+
+#ifdef TOOLS_ENABLED
+ if (stack && is_setup) {
+ stack->set_editor_gizmos_dirty(true);
+ }
+#endif // TOOLS_ENABLED
+}
+
+bool SkeletonModification2DCCDIK::get_ccdik_joint_enable_constraint(int p_joint_idx) const {
+ ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), false, "CCDIK joint out of range!");
+ return ccdik_data_chain[p_joint_idx].enable_constraint;
+}
+
+void SkeletonModification2DCCDIK::set_ccdik_joint_constraint_angle_min(int p_joint_idx, float p_angle_min) {
+ ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!");
+ ccdik_data_chain.write[p_joint_idx].constraint_angle_min = p_angle_min;
+
+#ifdef TOOLS_ENABLED
+ if (stack && is_setup) {
+ stack->set_editor_gizmos_dirty(true);
+ }
+#endif // TOOLS_ENABLED
+}
+
+float SkeletonModification2DCCDIK::get_ccdik_joint_constraint_angle_min(int p_joint_idx) const {
+ ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), 0.0, "CCDIK joint out of range!");
+ return ccdik_data_chain[p_joint_idx].constraint_angle_min;
+}
+
+void SkeletonModification2DCCDIK::set_ccdik_joint_constraint_angle_max(int p_joint_idx, float p_angle_max) {
+ ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!");
+ ccdik_data_chain.write[p_joint_idx].constraint_angle_max = p_angle_max;
+
+#ifdef TOOLS_ENABLED
+ if (stack && is_setup) {
+ stack->set_editor_gizmos_dirty(true);
+ }
+#endif // TOOLS_ENABLED
+}
+
+float SkeletonModification2DCCDIK::get_ccdik_joint_constraint_angle_max(int p_joint_idx) const {
+ ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), 0.0, "CCDIK joint out of range!");
+ return ccdik_data_chain[p_joint_idx].constraint_angle_max;
+}
+
+void SkeletonModification2DCCDIK::set_ccdik_joint_constraint_angle_invert(int p_joint_idx, bool p_invert) {
+ ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!");
+ ccdik_data_chain.write[p_joint_idx].constraint_angle_invert = p_invert;
+
+#ifdef TOOLS_ENABLED
+ if (stack && is_setup) {
+ stack->set_editor_gizmos_dirty(true);
+ }
+#endif // TOOLS_ENABLED
+}
+
+bool SkeletonModification2DCCDIK::get_ccdik_joint_constraint_angle_invert(int p_joint_idx) const {
+ ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), false, "CCDIK joint out of range!");
+ return ccdik_data_chain[p_joint_idx].constraint_angle_invert;
+}
+
+void SkeletonModification2DCCDIK::set_ccdik_joint_constraint_in_localspace(int p_joint_idx, bool p_constraint_in_localspace) {
+ ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!");
+ ccdik_data_chain.write[p_joint_idx].constraint_in_localspace = p_constraint_in_localspace;
+
+#ifdef TOOLS_ENABLED
+ if (stack && is_setup) {
+ stack->set_editor_gizmos_dirty(true);
+ }
+#endif // TOOLS_ENABLED
+}
+
+bool SkeletonModification2DCCDIK::get_ccdik_joint_constraint_in_localspace(int p_joint_idx) const {
+ ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), false, "CCDIK joint out of range!");
+ return ccdik_data_chain[p_joint_idx].constraint_in_localspace;
+}
+
+void SkeletonModification2DCCDIK::set_ccdik_joint_editor_draw_gizmo(int p_joint_idx, bool p_draw_gizmo) {
+ ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!");
+ ccdik_data_chain.write[p_joint_idx].editor_draw_gizmo = p_draw_gizmo;
+
+#ifdef TOOLS_ENABLED
+ if (stack && is_setup) {
+ stack->set_editor_gizmos_dirty(true);
+ }
+#endif // TOOLS_ENABLED
+}
+
+bool SkeletonModification2DCCDIK::get_ccdik_joint_editor_draw_gizmo(int p_joint_idx) const {
+ ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), false, "CCDIK joint out of range!");
+ return ccdik_data_chain[p_joint_idx].editor_draw_gizmo;
+}
+
+void SkeletonModification2DCCDIK::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification2DCCDIK::set_target_node);
+ ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification2DCCDIK::get_target_node);
+ ClassDB::bind_method(D_METHOD("set_tip_node", "tip_nodepath"), &SkeletonModification2DCCDIK::set_tip_node);
+ ClassDB::bind_method(D_METHOD("get_tip_node"), &SkeletonModification2DCCDIK::get_tip_node);
+
+ ClassDB::bind_method(D_METHOD("set_ccdik_data_chain_length", "length"), &SkeletonModification2DCCDIK::set_ccdik_data_chain_length);
+ ClassDB::bind_method(D_METHOD("get_ccdik_data_chain_length"), &SkeletonModification2DCCDIK::get_ccdik_data_chain_length);
+
+ ClassDB::bind_method(D_METHOD("set_ccdik_joint_bone2d_node", "joint_idx", "bone2d_nodepath"), &SkeletonModification2DCCDIK::set_ccdik_joint_bone2d_node);
+ ClassDB::bind_method(D_METHOD("get_ccdik_joint_bone2d_node", "joint_idx"), &SkeletonModification2DCCDIK::get_ccdik_joint_bone2d_node);
+ ClassDB::bind_method(D_METHOD("set_ccdik_joint_bone_index", "joint_idx", "bone_idx"), &SkeletonModification2DCCDIK::set_ccdik_joint_bone_index);
+ ClassDB::bind_method(D_METHOD("get_ccdik_joint_bone_index", "joint_idx"), &SkeletonModification2DCCDIK::get_ccdik_joint_bone_index);
+ ClassDB::bind_method(D_METHOD("set_ccdik_joint_rotate_from_joint", "joint_idx", "rotate_from_joint"), &SkeletonModification2DCCDIK::set_ccdik_joint_rotate_from_joint);
+ ClassDB::bind_method(D_METHOD("get_ccdik_joint_rotate_from_joint", "joint_idx"), &SkeletonModification2DCCDIK::get_ccdik_joint_rotate_from_joint);
+ ClassDB::bind_method(D_METHOD("set_ccdik_joint_enable_constraint", "joint_idx", "enable_constraint"), &SkeletonModification2DCCDIK::set_ccdik_joint_enable_constraint);
+ ClassDB::bind_method(D_METHOD("get_ccdik_joint_enable_constraint", "joint_idx"), &SkeletonModification2DCCDIK::get_ccdik_joint_enable_constraint);
+ ClassDB::bind_method(D_METHOD("set_ccdik_joint_constraint_angle_min", "joint_idx", "angle_min"), &SkeletonModification2DCCDIK::set_ccdik_joint_constraint_angle_min);
+ ClassDB::bind_method(D_METHOD("get_ccdik_joint_constraint_angle_min", "joint_idx"), &SkeletonModification2DCCDIK::get_ccdik_joint_constraint_angle_min);
+ ClassDB::bind_method(D_METHOD("set_ccdik_joint_constraint_angle_max", "joint_idx", "angle_max"), &SkeletonModification2DCCDIK::set_ccdik_joint_constraint_angle_max);
+ ClassDB::bind_method(D_METHOD("get_ccdik_joint_constraint_angle_max", "joint_idx"), &SkeletonModification2DCCDIK::get_ccdik_joint_constraint_angle_max);
+ ClassDB::bind_method(D_METHOD("set_ccdik_joint_constraint_angle_invert", "joint_idx", "invert"), &SkeletonModification2DCCDIK::set_ccdik_joint_constraint_angle_invert);
+ ClassDB::bind_method(D_METHOD("get_ccdik_joint_constraint_angle_invert", "joint_idx"), &SkeletonModification2DCCDIK::get_ccdik_joint_constraint_angle_invert);
+
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node2D"), "set_target_node", "get_target_node");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "tip_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node2D"), "set_tip_node", "get_tip_node");
+ 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");
+}
+
+SkeletonModification2DCCDIK::SkeletonModification2DCCDIK() {
+ stack = nullptr;
+ is_setup = false;
+ enabled = true;
+ editor_draw_gizmo = true;
+}
+
+SkeletonModification2DCCDIK::~SkeletonModification2DCCDIK() {
+}
diff --git a/scene/resources/skeleton_modification_2d_ccdik.h b/scene/resources/skeleton_modification_2d_ccdik.h
new file mode 100644
index 0000000000..dc48291f62
--- /dev/null
+++ b/scene/resources/skeleton_modification_2d_ccdik.h
@@ -0,0 +1,116 @@
+/*************************************************************************/
+/* skeleton_modification_2d_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. */
+/*************************************************************************/
+
+#ifndef SKELETONMODIFICATION2DCCDIK_H
+#define SKELETONMODIFICATION2DCCDIK_H
+
+#include "scene/2d/skeleton_2d.h"
+#include "scene/resources/skeleton_modification_2d.h"
+
+///////////////////////////////////////
+// SkeletonModification2DCCDIK
+///////////////////////////////////////
+
+class SkeletonModification2DCCDIK : public SkeletonModification2D {
+ GDCLASS(SkeletonModification2DCCDIK, SkeletonModification2D);
+
+private:
+ struct CCDIK_Joint_Data2D {
+ int bone_idx = -1;
+ NodePath bone2d_node;
+ ObjectID bone2d_node_cache;
+ bool rotate_from_joint = false;
+
+ bool enable_constraint = false;
+ float constraint_angle_min = 0;
+ float constraint_angle_max = (2.0 * Math_PI);
+ bool constraint_angle_invert = false;
+ bool constraint_in_localspace = true;
+
+ bool editor_draw_gizmo = true;
+ };
+
+ Vector<CCDIK_Joint_Data2D> ccdik_data_chain;
+
+ NodePath target_node;
+ ObjectID target_node_cache;
+ void update_target_cache();
+
+ NodePath tip_node;
+ ObjectID tip_node_cache;
+ void update_tip_cache();
+
+ void ccdik_joint_update_bone2d_cache(int p_joint_idx);
+ void _execute_ccdik_joint(int p_joint_idx, Node2D *p_target, Node2D *p_tip);
+
+protected:
+ static void _bind_methods();
+ bool _set(const StringName &p_path, const Variant &p_value);
+ bool _get(const StringName &p_path, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+public:
+ void _execute(float p_delta) override;
+ void _setup_modification(SkeletonModificationStack2D *p_stack) override;
+ void _draw_editor_gizmo() 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;
+
+ int get_ccdik_data_chain_length();
+ void set_ccdik_data_chain_length(int p_new_length);
+
+ void set_ccdik_joint_bone2d_node(int p_joint_idx, const NodePath &p_target_node);
+ NodePath get_ccdik_joint_bone2d_node(int p_joint_idx) const;
+ void set_ccdik_joint_bone_index(int p_joint_idx, int p_bone_idx);
+ int get_ccdik_joint_bone_index(int p_joint_idx) const;
+
+ void set_ccdik_joint_rotate_from_joint(int p_joint_idx, bool p_rotate_from_joint);
+ bool get_ccdik_joint_rotate_from_joint(int p_joint_idx) const;
+ void set_ccdik_joint_enable_constraint(int p_joint_idx, bool p_constraint);
+ bool get_ccdik_joint_enable_constraint(int p_joint_idx) const;
+ void set_ccdik_joint_constraint_angle_min(int p_joint_idx, float p_angle_min);
+ float get_ccdik_joint_constraint_angle_min(int p_joint_idx) const;
+ void set_ccdik_joint_constraint_angle_max(int p_joint_idx, float p_angle_max);
+ float get_ccdik_joint_constraint_angle_max(int p_joint_idx) const;
+ void set_ccdik_joint_constraint_angle_invert(int p_joint_idx, bool p_invert);
+ bool get_ccdik_joint_constraint_angle_invert(int p_joint_idx) const;
+ void set_ccdik_joint_constraint_in_localspace(int p_joint_idx, bool p_constraint_in_localspace);
+ bool get_ccdik_joint_constraint_in_localspace(int p_joint_idx) const;
+ void set_ccdik_joint_editor_draw_gizmo(int p_joint_idx, bool p_draw_gizmo);
+ bool get_ccdik_joint_editor_draw_gizmo(int p_joint_idx) const;
+
+ SkeletonModification2DCCDIK();
+ ~SkeletonModification2DCCDIK();
+};
+
+#endif // SKELETONMODIFICATION2DCCDIK_H
diff --git a/scene/resources/skeleton_modification_2d_fabrik.cpp b/scene/resources/skeleton_modification_2d_fabrik.cpp
new file mode 100644
index 0000000000..6e9429034f
--- /dev/null
+++ b/scene/resources/skeleton_modification_2d_fabrik.cpp
@@ -0,0 +1,445 @@
+/*************************************************************************/
+/* skeleton_modification_2d_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 "skeleton_modification_2d_fabrik.h"
+#include "scene/2d/skeleton_2d.h"
+
+#ifdef TOOLS_ENABLED
+#include "editor/editor_settings.h"
+#endif // TOOLS_ENABLED
+
+bool SkeletonModification2DFABRIK::_set(const StringName &p_path, const Variant &p_value) {
+ String path = p_path;
+
+ if (path.begins_with("joint_data/")) {
+ int which = path.get_slicec('/', 1).to_int();
+ String what = path.get_slicec('/', 2);
+ ERR_FAIL_INDEX_V(which, fabrik_data_chain.size(), false);
+
+ if (what == "bone2d_node") {
+ set_fabrik_joint_bone2d_node(which, p_value);
+ } else if (what == "bone_index") {
+ set_fabrik_joint_bone_index(which, p_value);
+ } else if (what == "magnet_position") {
+ set_fabrik_joint_magnet_position(which, p_value);
+ } else if (what == "use_target_rotation") {
+ set_fabrik_joint_use_target_rotation(which, p_value);
+ }
+ }
+
+ return true;
+}
+
+bool SkeletonModification2DFABRIK::_get(const StringName &p_path, Variant &r_ret) const {
+ String path = p_path;
+
+ if (path.begins_with("joint_data/")) {
+ int which = path.get_slicec('/', 1).to_int();
+ String what = path.get_slicec('/', 2);
+ ERR_FAIL_INDEX_V(which, fabrik_data_chain.size(), false);
+
+ if (what == "bone2d_node") {
+ r_ret = get_fabrik_joint_bone2d_node(which);
+ } else if (what == "bone_index") {
+ r_ret = get_fabrik_joint_bone_index(which);
+ } else if (what == "magnet_position") {
+ r_ret = get_fabrik_joint_magnet_position(which);
+ } else if (what == "use_target_rotation") {
+ r_ret = get_fabrik_joint_use_target_rotation(which);
+ }
+ return true;
+ }
+ return true;
+}
+
+void SkeletonModification2DFABRIK::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (int i = 0; i < fabrik_data_chain.size(); i++) {
+ String base_string = "joint_data/" + itos(i) + "/";
+
+ p_list->push_back(PropertyInfo(Variant::INT, base_string + "bone_index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::NODE_PATH, base_string + "bone2d_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Bone2D", PROPERTY_USAGE_DEFAULT));
+
+ if (i > 0) {
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, base_string + "magnet_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ }
+ if (i == fabrik_data_chain.size() - 1) {
+ p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "use_target_rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ }
+ }
+}
+
+void SkeletonModification2DFABRIK::_execute(float 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()) {
+ WARN_PRINT_ONCE("Target cache is out of date. Attempting to update...");
+ update_target_cache();
+ return;
+ }
+
+ if (fabrik_data_chain.size() <= 1) {
+ ERR_PRINT_ONCE("FABRIK requires at least two joints to operate! Cannot execute modification!");
+ return;
+ }
+
+ Node2D *target = Object::cast_to<Node2D>(ObjectDB::get_instance(target_node_cache));
+ if (!target || !target->is_inside_tree()) {
+ ERR_PRINT_ONCE("Target node is not in the scene tree. Cannot execute modification!");
+ return;
+ }
+ target_global_pose = target->get_global_transform();
+
+ if (fabrik_data_chain[0].bone2d_node_cache.is_null() && !fabrik_data_chain[0].bone2d_node.is_empty()) {
+ fabrik_joint_update_bone2d_cache(0);
+ WARN_PRINT("Bone2D cache for origin joint is out of date. Updating...");
+ }
+
+ Bone2D *origin_bone2d_node = Object::cast_to<Bone2D>(ObjectDB::get_instance(fabrik_data_chain[0].bone2d_node_cache));
+ if (!origin_bone2d_node || !origin_bone2d_node->is_inside_tree()) {
+ ERR_PRINT_ONCE("Origin joint's Bone2D node is not in the scene tree. Cannot execute modification!");
+ return;
+ }
+
+ origin_global_pose = origin_bone2d_node->get_global_transform();
+
+ if (fabrik_transform_chain.size() != fabrik_data_chain.size()) {
+ fabrik_transform_chain.resize(fabrik_data_chain.size());
+ }
+
+ for (int i = 0; i < fabrik_data_chain.size(); i++) {
+ // Update the transform chain
+ if (fabrik_data_chain[i].bone2d_node_cache.is_null() && !fabrik_data_chain[i].bone2d_node.is_empty()) {
+ WARN_PRINT_ONCE("Bone2D cache for joint " + itos(i) + " is out of date.. Attempting to update...");
+ fabrik_joint_update_bone2d_cache(i);
+ }
+ Bone2D *joint_bone2d_node = Object::cast_to<Bone2D>(ObjectDB::get_instance(fabrik_data_chain[i].bone2d_node_cache));
+ if (!joint_bone2d_node) {
+ ERR_PRINT_ONCE("FABRIK Joint " + itos(i) + " does not have a Bone2D node set! Cannot execute modification!");
+ return;
+ }
+ fabrik_transform_chain.write[i] = joint_bone2d_node->get_global_transform();
+ }
+
+ 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_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_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_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_position() + (final_bone2d_direction * final_bone2d_length)).distance_to(target->get_global_position());
+
+ chain_iterations += 1;
+ if (chain_iterations >= chain_max_iterations) {
+ break;
+ }
+ }
+
+ // Apply all of the saved transforms to the Bone2D nodes
+ for (int i = 0; i < fabrik_data_chain.size(); i++) {
+ Bone2D *joint_bone2d_node = Object::cast_to<Bone2D>(ObjectDB::get_instance(fabrik_data_chain[i].bone2d_node_cache));
+ if (!joint_bone2d_node) {
+ ERR_PRINT_ONCE("FABRIK Joint " + itos(i) + " does not have a Bone2D node set!");
+ continue;
+ }
+ Transform2D chain_trans = fabrik_transform_chain[i];
+
+ // Apply rotation
+ if (i + 1 < fabrik_data_chain.size()) {
+ chain_trans = chain_trans.looking_at(fabrik_transform_chain[i + 1].get_origin());
+ } else {
+ if (fabrik_data_chain[i].use_target_rotation) {
+ chain_trans.set_rotation(target_global_pose.get_rotation());
+ } else {
+ chain_trans = chain_trans.looking_at(target_global_pose.get_origin());
+ }
+ }
+ // Adjust for the bone angle
+ 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_scale());
+
+ // Apply to the bone, and to the override
+ joint_bone2d_node->set_global_transform(chain_trans);
+ stack->skeleton->set_bone_local_pose_override(fabrik_data_chain[i].bone_idx, joint_bone2d_node->get_transform(), stack->strength, true);
+ }
+}
+
+void SkeletonModification2DFABRIK::chain_backwards() {
+ int final_joint_index = fabrik_data_chain.size() - 1;
+ Bone2D *final_bone2d_node = Object::cast_to<Bone2D>(ObjectDB::get_instance(fabrik_data_chain[final_joint_index].bone2d_node_cache));
+ Transform2D final_bone2d_trans = fabrik_transform_chain[final_joint_index];
+
+ // Apply magnet position
+ if (final_joint_index != 0) {
+ final_bone2d_trans.set_origin(final_bone2d_trans.get_origin() + fabrik_data_chain[final_joint_index].magnet_position);
+ }
+
+ // Set the rotation of the tip bone
+ final_bone2d_trans = final_bone2d_trans.looking_at(target_global_pose.get_origin());
+
+ // Set the position of the tip bone
+ float final_bone2d_angle = final_bone2d_trans.get_rotation();
+ if (fabrik_data_chain[final_joint_index].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);
+ final_bone2d_trans.set_origin(target_global_pose.get_origin() - (final_bone2d_direction * final_bone2d_length));
+
+ // Save the transform
+ fabrik_transform_chain.write[final_joint_index] = final_bone2d_trans;
+
+ int i = final_joint_index;
+ while (i >= 1) {
+ Transform2D previous_pose = fabrik_transform_chain[i];
+ i -= 1;
+ Bone2D *current_bone2d_node = Object::cast_to<Bone2D>(ObjectDB::get_instance(fabrik_data_chain[i].bone2d_node_cache));
+ Transform2D current_pose = fabrik_transform_chain[i];
+
+ // Apply magnet position
+ if (i != 0) {
+ current_pose.set_origin(current_pose.get_origin() + fabrik_data_chain[i].magnet_position);
+ }
+
+ float current_bone2d_node_length = current_bone2d_node->get_length() * MIN(current_bone2d_node->get_global_scale().x, current_bone2d_node->get_global_scale().y);
+ float length = current_bone2d_node_length / (previous_pose.get_origin() - current_pose.get_origin()).length();
+ Vector2 finish_position = previous_pose.get_origin().lerp(current_pose.get_origin(), length);
+ current_pose.set_origin(finish_position);
+
+ // Save the transform
+ fabrik_transform_chain.write[i] = current_pose;
+ }
+}
+
+void SkeletonModification2DFABRIK::chain_forwards() {
+ Transform2D origin_bone2d_trans = fabrik_transform_chain[0];
+ origin_bone2d_trans.set_origin(origin_global_pose.get_origin());
+ // Save the position
+ fabrik_transform_chain.write[0] = origin_bone2d_trans;
+
+ for (int i = 0; i < fabrik_data_chain.size() - 1; i++) {
+ Bone2D *current_bone2d_node = Object::cast_to<Bone2D>(ObjectDB::get_instance(fabrik_data_chain[i].bone2d_node_cache));
+ Transform2D current_pose = fabrik_transform_chain[i];
+ Transform2D next_pose = fabrik_transform_chain[i + 1];
+
+ float current_bone2d_node_length = current_bone2d_node->get_length() * MIN(current_bone2d_node->get_global_scale().x, current_bone2d_node->get_global_scale().y);
+ float length = current_bone2d_node_length / (current_pose.get_origin() - next_pose.get_origin()).length();
+ Vector2 finish_position = current_pose.get_origin().lerp(next_pose.get_origin(), length);
+ current_pose.set_origin(finish_position);
+
+ // Apply to the bone
+ fabrik_transform_chain.write[i + 1] = current_pose;
+ }
+}
+
+void SkeletonModification2DFABRIK::_setup_modification(SkeletonModificationStack2D *p_stack) {
+ stack = p_stack;
+
+ if (stack != nullptr) {
+ is_setup = true;
+ update_target_cache();
+ }
+}
+
+void SkeletonModification2DFABRIK::update_target_cache() {
+ if (!is_setup || !stack) {
+ ERR_PRINT_ONCE("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();
+ }
+ }
+ }
+}
+
+void SkeletonModification2DFABRIK::fabrik_joint_update_bone2d_cache(int p_joint_idx) {
+ ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "Cannot update bone2d cache: joint index out of range!");
+ if (!is_setup || !stack) {
+ ERR_PRINT_ONCE("Cannot update FABRIK Bone2D cache: modification is not properly setup!");
+ return;
+ }
+
+ fabrik_data_chain.write[p_joint_idx].bone2d_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree()) {
+ if (stack->skeleton->has_node(fabrik_data_chain[p_joint_idx].bone2d_node)) {
+ Node *node = stack->skeleton->get_node(fabrik_data_chain[p_joint_idx].bone2d_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update FABRIK joint " + itos(p_joint_idx) + " Bone2D cache: node is this modification's skeleton or cannot be found!");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update FABRIK joint " + itos(p_joint_idx) + " Bone2D cache: node is not in scene tree!");
+ fabrik_data_chain.write[p_joint_idx].bone2d_node_cache = node->get_instance_id();
+
+ Bone2D *bone = Object::cast_to<Bone2D>(node);
+ if (bone) {
+ fabrik_data_chain.write[p_joint_idx].bone_idx = bone->get_index_in_skeleton();
+ } else {
+ ERR_FAIL_MSG("FABRIK joint " + itos(p_joint_idx) + " Bone2D cache: Nodepath to Bone2D is not a Bone2D node!");
+ }
+ }
+ }
+ }
+}
+
+void SkeletonModification2DFABRIK::set_target_node(const NodePath &p_target_node) {
+ target_node = p_target_node;
+ update_target_cache();
+}
+
+NodePath SkeletonModification2DFABRIK::get_target_node() const {
+ return target_node;
+}
+
+void SkeletonModification2DFABRIK::set_fabrik_data_chain_length(int p_length) {
+ fabrik_data_chain.resize(p_length);
+ notify_property_list_changed();
+}
+
+int SkeletonModification2DFABRIK::get_fabrik_data_chain_length() {
+ return fabrik_data_chain.size();
+}
+
+void SkeletonModification2DFABRIK::set_fabrik_joint_bone2d_node(int p_joint_idx, const NodePath &p_target_node) {
+ ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!");
+ fabrik_data_chain.write[p_joint_idx].bone2d_node = p_target_node;
+ fabrik_joint_update_bone2d_cache(p_joint_idx);
+
+ notify_property_list_changed();
+}
+
+NodePath SkeletonModification2DFABRIK::get_fabrik_joint_bone2d_node(int p_joint_idx) const {
+ ERR_FAIL_INDEX_V_MSG(p_joint_idx, fabrik_data_chain.size(), NodePath(), "FABRIK joint out of range!");
+ return fabrik_data_chain[p_joint_idx].bone2d_node;
+}
+
+void SkeletonModification2DFABRIK::set_fabrik_joint_bone_index(int p_joint_idx, int p_bone_idx) {
+ ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!");
+ ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!");
+
+ if (is_setup) {
+ if (stack->skeleton) {
+ ERR_FAIL_INDEX_MSG(p_bone_idx, stack->skeleton->get_bone_count(), "Passed-in Bone index is out of range!");
+ fabrik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx;
+ fabrik_data_chain.write[p_joint_idx].bone2d_node_cache = stack->skeleton->get_bone(p_bone_idx)->get_instance_id();
+ fabrik_data_chain.write[p_joint_idx].bone2d_node = stack->skeleton->get_path_to(stack->skeleton->get_bone(p_bone_idx));
+ } else {
+ WARN_PRINT("Cannot verify the FABRIK joint " + itos(p_joint_idx) + " bone index for this modification...");
+ fabrik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx;
+ }
+ } else {
+ WARN_PRINT("Cannot verify the FABRIK joint " + itos(p_joint_idx) + " bone index for this modification...");
+ fabrik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx;
+ }
+
+ notify_property_list_changed();
+}
+
+int SkeletonModification2DFABRIK::get_fabrik_joint_bone_index(int p_joint_idx) const {
+ ERR_FAIL_INDEX_V_MSG(p_joint_idx, fabrik_data_chain.size(), -1, "FABRIK joint out of range!");
+ return fabrik_data_chain[p_joint_idx].bone_idx;
+}
+
+void SkeletonModification2DFABRIK::set_fabrik_joint_magnet_position(int p_joint_idx, Vector2 p_magnet_position) {
+ ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!");
+ fabrik_data_chain.write[p_joint_idx].magnet_position = p_magnet_position;
+}
+
+Vector2 SkeletonModification2DFABRIK::get_fabrik_joint_magnet_position(int p_joint_idx) const {
+ ERR_FAIL_INDEX_V_MSG(p_joint_idx, fabrik_data_chain.size(), Vector2(), "FABRIK joint out of range!");
+ return fabrik_data_chain[p_joint_idx].magnet_position;
+}
+
+void SkeletonModification2DFABRIK::set_fabrik_joint_use_target_rotation(int p_joint_idx, bool p_use_target_rotation) {
+ ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!");
+ fabrik_data_chain.write[p_joint_idx].use_target_rotation = p_use_target_rotation;
+}
+
+bool SkeletonModification2DFABRIK::get_fabrik_joint_use_target_rotation(int p_joint_idx) const {
+ ERR_FAIL_INDEX_V_MSG(p_joint_idx, fabrik_data_chain.size(), false, "FABRIK joint out of range!");
+ return fabrik_data_chain[p_joint_idx].use_target_rotation;
+}
+
+void SkeletonModification2DFABRIK::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification2DFABRIK::set_target_node);
+ ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification2DFABRIK::get_target_node);
+
+ ClassDB::bind_method(D_METHOD("set_fabrik_data_chain_length", "length"), &SkeletonModification2DFABRIK::set_fabrik_data_chain_length);
+ ClassDB::bind_method(D_METHOD("get_fabrik_data_chain_length"), &SkeletonModification2DFABRIK::get_fabrik_data_chain_length);
+
+ ClassDB::bind_method(D_METHOD("set_fabrik_joint_bone2d_node", "joint_idx", "bone2d_nodepath"), &SkeletonModification2DFABRIK::set_fabrik_joint_bone2d_node);
+ ClassDB::bind_method(D_METHOD("get_fabrik_joint_bone2d_node", "joint_idx"), &SkeletonModification2DFABRIK::get_fabrik_joint_bone2d_node);
+ ClassDB::bind_method(D_METHOD("set_fabrik_joint_bone_index", "joint_idx", "bone_idx"), &SkeletonModification2DFABRIK::set_fabrik_joint_bone_index);
+ ClassDB::bind_method(D_METHOD("get_fabrik_joint_bone_index", "joint_idx"), &SkeletonModification2DFABRIK::get_fabrik_joint_bone_index);
+ ClassDB::bind_method(D_METHOD("set_fabrik_joint_magnet_position", "joint_idx", "magnet_position"), &SkeletonModification2DFABRIK::set_fabrik_joint_magnet_position);
+ ClassDB::bind_method(D_METHOD("get_fabrik_joint_magnet_position", "joint_idx"), &SkeletonModification2DFABRIK::get_fabrik_joint_magnet_position);
+ ClassDB::bind_method(D_METHOD("set_fabrik_joint_use_target_rotation", "joint_idx", "use_target_rotation"), &SkeletonModification2DFABRIK::set_fabrik_joint_use_target_rotation);
+ ClassDB::bind_method(D_METHOD("get_fabrik_joint_use_target_rotation", "joint_idx"), &SkeletonModification2DFABRIK::get_fabrik_joint_use_target_rotation);
+
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node2D"), "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");
+}
+
+SkeletonModification2DFABRIK::SkeletonModification2DFABRIK() {
+ stack = nullptr;
+ is_setup = false;
+ enabled = true;
+ editor_draw_gizmo = false;
+}
+
+SkeletonModification2DFABRIK::~SkeletonModification2DFABRIK() {
+}
diff --git a/scene/resources/skeleton_modification_2d_fabrik.h b/scene/resources/skeleton_modification_2d_fabrik.h
new file mode 100644
index 0000000000..79e0106e26
--- /dev/null
+++ b/scene/resources/skeleton_modification_2d_fabrik.h
@@ -0,0 +1,108 @@
+/*************************************************************************/
+/* skeleton_modification_2d_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. */
+/*************************************************************************/
+
+#ifndef SKELETONMODIFICATION2DFABRIK_H
+#define SKELETONMODIFICATION2DFABRIK_H
+
+#include "scene/2d/skeleton_2d.h"
+#include "scene/resources/skeleton_modification_2d.h"
+
+///////////////////////////////////////
+// SkeletonModification2DFABRIK
+///////////////////////////////////////
+
+class SkeletonModification2DFABRIK : public SkeletonModification2D {
+ GDCLASS(SkeletonModification2DFABRIK, SkeletonModification2D);
+
+private:
+ struct FABRIK_Joint_Data2D {
+ int bone_idx = -1;
+ NodePath bone2d_node;
+ ObjectID bone2d_node_cache;
+
+ Vector2 magnet_position = Vector2(0, 0);
+ bool use_target_rotation = false;
+
+ bool editor_draw_gizmo = true;
+ };
+
+ Vector<FABRIK_Joint_Data2D> fabrik_data_chain;
+
+ // Unlike in 3D, we need a vector of Transform2D objects to perform FABRIK.
+ // This is because FABRIK (unlike CCDIK) needs to operate on transforms that are NOT
+ // affected by each other, making the transforms stored in Bone2D unusable, as well as those in Skeleton2D.
+ // For this reason, this modification stores a vector of Transform2Ds used for the calculations, which are then applied at the end.
+ Vector<Transform2D> fabrik_transform_chain;
+
+ NodePath target_node;
+ ObjectID target_node_cache;
+ void update_target_cache();
+
+ float chain_tolarance = 0.01;
+ int chain_max_iterations = 10;
+ int chain_iterations = 0;
+ Transform2D target_global_pose = Transform2D();
+ Transform2D origin_global_pose = Transform2D();
+
+ void fabrik_joint_update_bone2d_cache(int p_joint_idx);
+ void chain_backwards();
+ void chain_forwards();
+
+protected:
+ static void _bind_methods();
+ bool _set(const StringName &p_path, const Variant &p_value);
+ bool _get(const StringName &p_path, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+public:
+ void _execute(float p_delta) override;
+ void _setup_modification(SkeletonModificationStack2D *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);
+
+ void set_fabrik_joint_bone2d_node(int p_joint_idx, const NodePath &p_target_node);
+ NodePath get_fabrik_joint_bone2d_node(int p_joint_idx) const;
+ void set_fabrik_joint_bone_index(int p_joint_idx, int p_bone_idx);
+ int get_fabrik_joint_bone_index(int p_joint_idx) const;
+
+ void set_fabrik_joint_magnet_position(int p_joint_idx, Vector2 p_magnet_position);
+ Vector2 get_fabrik_joint_magnet_position(int p_joint_idx) const;
+ void set_fabrik_joint_use_target_rotation(int p_joint_idx, bool p_use_target_rotation);
+ bool get_fabrik_joint_use_target_rotation(int p_joint_idx) const;
+
+ SkeletonModification2DFABRIK();
+ ~SkeletonModification2DFABRIK();
+};
+
+#endif // SKELETONMODIFICATION2DFABRIK_H
diff --git a/scene/resources/skeleton_modification_2d_jiggle.cpp b/scene/resources/skeleton_modification_2d_jiggle.cpp
new file mode 100644
index 0000000000..84abc9d020
--- /dev/null
+++ b/scene/resources/skeleton_modification_2d_jiggle.cpp
@@ -0,0 +1,564 @@
+/*************************************************************************/
+/* skeleton_modification_2d_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 "skeleton_modification_2d_jiggle.h"
+#include "scene/2d/skeleton_2d.h"
+
+bool SkeletonModification2DJiggle::_set(const StringName &p_path, const Variant &p_value) {
+ String path = p_path;
+
+ if (path.begins_with("joint_data/")) {
+ int which = path.get_slicec('/', 1).to_int();
+ String what = path.get_slicec('/', 2);
+ ERR_FAIL_INDEX_V(which, jiggle_data_chain.size(), false);
+
+ if (what == "bone2d_node") {
+ set_jiggle_joint_bone2d_node(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);
+ }
+ return true;
+ } else {
+ if (path == "use_colliders") {
+ set_use_colliders(p_value);
+ } else if (path == "collision_mask") {
+ set_collision_mask(p_value);
+ }
+ }
+ return true;
+}
+
+bool SkeletonModification2DJiggle::_get(const StringName &p_path, Variant &r_ret) const {
+ String path = p_path;
+
+ if (path.begins_with("joint_data/")) {
+ int which = path.get_slicec('/', 1).to_int();
+ String what = path.get_slicec('/', 2);
+ ERR_FAIL_INDEX_V(which, jiggle_data_chain.size(), false);
+
+ if (what == "bone2d_node") {
+ r_ret = get_jiggle_joint_bone2d_node(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);
+ }
+ return true;
+ } else {
+ if (path == "use_colliders") {
+ r_ret = get_use_colliders();
+ } else if (path == "collision_mask") {
+ r_ret = get_collision_mask();
+ }
+ }
+ return true;
+}
+
+void SkeletonModification2DJiggle::_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_2D_PHYSICS, "", PROPERTY_USAGE_DEFAULT));
+ }
+
+ for (int i = 0; i < jiggle_data_chain.size(); i++) {
+ String base_string = "joint_data/" + itos(i) + "/";
+
+ p_list->push_back(PropertyInfo(Variant::INT, base_string + "bone_index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::NODE_PATH, base_string + "bone2d_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Bone2D", 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::VECTOR2, base_string + "gravity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ }
+ }
+ }
+}
+
+void SkeletonModification2DJiggle::_execute(float 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()) {
+ WARN_PRINT_ONCE("Target cache is out of date. Attempting to update...");
+ update_target_cache();
+ return;
+ }
+ Node2D *target = Object::cast_to<Node2D>(ObjectDB::get_instance(target_node_cache));
+ if (!target || !target->is_inside_tree()) {
+ ERR_PRINT_ONCE("Target node is not in the scene tree. Cannot execute modification!");
+ return;
+ }
+
+ for (int i = 0; i < jiggle_data_chain.size(); i++) {
+ _execute_jiggle_joint(i, target, p_delta);
+ }
+}
+
+void SkeletonModification2DJiggle::_execute_jiggle_joint(int p_joint_idx, Node2D *p_target, float p_delta) {
+ // Adopted from: https://wiki.unity3d.com/index.php/JiggleBone
+ // With modifications by TwistedTwigleg.
+
+ if (jiggle_data_chain[p_joint_idx].bone_idx <= -1 || jiggle_data_chain[p_joint_idx].bone_idx > stack->skeleton->get_bone_count()) {
+ ERR_PRINT_ONCE("Jiggle joint " + itos(p_joint_idx) + " bone index is invalid. Cannot execute modification on joint...");
+ return;
+ }
+
+ if (jiggle_data_chain[p_joint_idx].bone2d_node_cache.is_null() && !jiggle_data_chain[p_joint_idx].bone2d_node.is_empty()) {
+ WARN_PRINT_ONCE("Bone2D cache for joint " + itos(p_joint_idx) + " is out of date. Updating...");
+ jiggle_joint_update_bone2d_cache(p_joint_idx);
+ }
+
+ Bone2D *operation_bone = stack->skeleton->get_bone(jiggle_data_chain[p_joint_idx].bone_idx);
+ if (!operation_bone) {
+ ERR_PRINT_ONCE("Jiggle joint " + itos(p_joint_idx) + " does not have a Bone2D node or it cannot be found!");
+ return;
+ }
+
+ Transform2D operation_bone_trans = operation_bone->get_global_transform();
+ 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;
+
+ if (jiggle_data_chain[p_joint_idx].use_gravity) {
+ jiggle_data_chain.write[p_joint_idx].force += jiggle_data_chain[p_joint_idx].gravity * p_delta;
+ }
+
+ jiggle_data_chain.write[p_joint_idx].acceleration = jiggle_data_chain[p_joint_idx].force / jiggle_data_chain[p_joint_idx].mass;
+ jiggle_data_chain.write[p_joint_idx].velocity += jiggle_data_chain[p_joint_idx].acceleration * (1 - jiggle_data_chain[p_joint_idx].damping);
+
+ jiggle_data_chain.write[p_joint_idx].dynamic_position += jiggle_data_chain[p_joint_idx].velocity + jiggle_data_chain[p_joint_idx].force;
+ jiggle_data_chain.write[p_joint_idx].dynamic_position += operation_bone_trans.get_origin() - jiggle_data_chain[p_joint_idx].last_position;
+ jiggle_data_chain.write[p_joint_idx].last_position = operation_bone_trans.get_origin();
+
+ // Collision detection/response
+ if (use_colliders) {
+ if (execution_mode == SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_physics_process) {
+ Ref<World2D> world_2d = stack->skeleton->get_world_2d();
+ ERR_FAIL_COND(world_2d.is_null());
+ PhysicsDirectSpaceState2D *space_state = PhysicsServer2D::get_singleton()->space_get_direct_state(world_2d->get_space());
+ PhysicsDirectSpaceState2D::RayResult ray_result;
+
+ // Add exception support?
+ bool ray_hit = space_state->intersect_ray(operation_bone_trans.get_origin(), jiggle_data_chain[p_joint_idx].dynamic_position,
+ ray_result, Set<RID>(), collision_mask);
+
+ if (ray_hit) {
+ jiggle_data_chain.write[p_joint_idx].dynamic_position = jiggle_data_chain[p_joint_idx].last_noncollision_position;
+ jiggle_data_chain.write[p_joint_idx].acceleration = Vector2(0, 0);
+ jiggle_data_chain.write[p_joint_idx].velocity = Vector2(0, 0);
+ } else {
+ jiggle_data_chain.write[p_joint_idx].last_noncollision_position = jiggle_data_chain[p_joint_idx].dynamic_position;
+ }
+ } else {
+ WARN_PRINT_ONCE("Jiggle 2D modifier: You cannot detect colliders without the stack mode being set to _physics_process!");
+ }
+ }
+
+ // Rotate the bone using the dynamic position!
+ operation_bone_trans = operation_bone_trans.looking_at(jiggle_data_chain[p_joint_idx].dynamic_position);
+ 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_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);
+}
+
+void SkeletonModification2DJiggle::_update_jiggle_joint_data() {
+ for (int 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 SkeletonModification2DJiggle::_setup_modification(SkeletonModificationStack2D *p_stack) {
+ stack = p_stack;
+
+ if (stack) {
+ is_setup = true;
+
+ if (stack->skeleton) {
+ for (int 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()) {
+ Bone2D *bone2d_node = stack->skeleton->get_bone(bone_idx);
+ jiggle_data_chain.write[i].dynamic_position = bone2d_node->get_global_position();
+ }
+ }
+ }
+
+ update_target_cache();
+ }
+}
+
+void SkeletonModification2DJiggle::update_target_cache() {
+ if (!is_setup || !stack) {
+ ERR_PRINT_ONCE("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();
+ }
+ }
+ }
+}
+
+void SkeletonModification2DJiggle::jiggle_joint_update_bone2d_cache(int p_joint_idx) {
+ ERR_FAIL_INDEX_MSG(p_joint_idx, jiggle_data_chain.size(), "Cannot update bone2d cache: joint index out of range!");
+ if (!is_setup || !stack) {
+ ERR_PRINT_ONCE("Cannot update Jiggle " + itos(p_joint_idx) + " Bone2D cache: modification is not properly setup!");
+ return;
+ }
+
+ jiggle_data_chain.write[p_joint_idx].bone2d_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree()) {
+ if (stack->skeleton->has_node(jiggle_data_chain[p_joint_idx].bone2d_node)) {
+ Node *node = stack->skeleton->get_node(jiggle_data_chain[p_joint_idx].bone2d_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update Jiggle joint " + itos(p_joint_idx) + " Bone2D cache: node is this modification's skeleton or cannot be found!");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update Jiggle joint " + itos(p_joint_idx) + " Bone2D cache: node is not in scene tree!");
+ jiggle_data_chain.write[p_joint_idx].bone2d_node_cache = node->get_instance_id();
+
+ Bone2D *bone = Object::cast_to<Bone2D>(node);
+ if (bone) {
+ jiggle_data_chain.write[p_joint_idx].bone_idx = bone->get_index_in_skeleton();
+ } else {
+ ERR_FAIL_MSG("Jiggle joint " + itos(p_joint_idx) + " Bone2D cache: Nodepath to Bone2D is not a Bone2D node!");
+ }
+ }
+ }
+ }
+}
+
+void SkeletonModification2DJiggle::set_target_node(const NodePath &p_target_node) {
+ target_node = p_target_node;
+ update_target_cache();
+}
+
+NodePath SkeletonModification2DJiggle::get_target_node() const {
+ return target_node;
+}
+
+void SkeletonModification2DJiggle::set_stiffness(float p_stiffness) {
+ ERR_FAIL_COND_MSG(p_stiffness < 0, "Stiffness cannot be set to a negative value!");
+ stiffness = p_stiffness;
+ _update_jiggle_joint_data();
+}
+
+float SkeletonModification2DJiggle::get_stiffness() const {
+ return stiffness;
+}
+
+void SkeletonModification2DJiggle::set_mass(float p_mass) {
+ ERR_FAIL_COND_MSG(p_mass < 0, "Mass cannot be set to a negative value!");
+ mass = p_mass;
+ _update_jiggle_joint_data();
+}
+
+float SkeletonModification2DJiggle::get_mass() const {
+ return mass;
+}
+
+void SkeletonModification2DJiggle::set_damping(float 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();
+}
+
+float SkeletonModification2DJiggle::get_damping() const {
+ return damping;
+}
+
+void SkeletonModification2DJiggle::set_use_gravity(bool p_use_gravity) {
+ use_gravity = p_use_gravity;
+ _update_jiggle_joint_data();
+}
+
+bool SkeletonModification2DJiggle::get_use_gravity() const {
+ return use_gravity;
+}
+
+void SkeletonModification2DJiggle::set_gravity(Vector2 p_gravity) {
+ gravity = p_gravity;
+ _update_jiggle_joint_data();
+}
+
+Vector2 SkeletonModification2DJiggle::get_gravity() const {
+ return gravity;
+}
+
+void SkeletonModification2DJiggle::set_use_colliders(bool p_use_colliders) {
+ use_colliders = p_use_colliders;
+ notify_property_list_changed();
+}
+
+bool SkeletonModification2DJiggle::get_use_colliders() const {
+ return use_colliders;
+}
+
+void SkeletonModification2DJiggle::set_collision_mask(int p_mask) {
+ collision_mask = p_mask;
+}
+
+int SkeletonModification2DJiggle::get_collision_mask() const {
+ return collision_mask;
+}
+
+// Jiggle joint data functions
+int SkeletonModification2DJiggle::get_jiggle_data_chain_length() {
+ return jiggle_data_chain.size();
+}
+
+void SkeletonModification2DJiggle::set_jiggle_data_chain_length(int p_length) {
+ ERR_FAIL_COND(p_length < 0);
+ jiggle_data_chain.resize(p_length);
+ notify_property_list_changed();
+}
+
+void SkeletonModification2DJiggle::set_jiggle_joint_bone2d_node(int p_joint_idx, const NodePath &p_target_node) {
+ ERR_FAIL_INDEX_MSG(p_joint_idx, jiggle_data_chain.size(), "Jiggle joint out of range!");
+ jiggle_data_chain.write[p_joint_idx].bone2d_node = p_target_node;
+ jiggle_joint_update_bone2d_cache(p_joint_idx);
+
+ notify_property_list_changed();
+}
+
+NodePath SkeletonModification2DJiggle::get_jiggle_joint_bone2d_node(int p_joint_idx) const {
+ ERR_FAIL_INDEX_V_MSG(p_joint_idx, jiggle_data_chain.size(), NodePath(), "Jiggle joint out of range!");
+ return jiggle_data_chain[p_joint_idx].bone2d_node;
+}
+
+void SkeletonModification2DJiggle::set_jiggle_joint_bone_index(int p_joint_idx, int p_bone_idx) {
+ ERR_FAIL_INDEX_MSG(p_joint_idx, jiggle_data_chain.size(), "Jiggle joint out of range!");
+ ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!");
+
+ if (is_setup) {
+ if (stack->skeleton) {
+ ERR_FAIL_INDEX_MSG(p_bone_idx, stack->skeleton->get_bone_count(), "Passed-in Bone index is out of range!");
+ jiggle_data_chain.write[p_joint_idx].bone_idx = p_bone_idx;
+ jiggle_data_chain.write[p_joint_idx].bone2d_node_cache = stack->skeleton->get_bone(p_bone_idx)->get_instance_id();
+ jiggle_data_chain.write[p_joint_idx].bone2d_node = stack->skeleton->get_path_to(stack->skeleton->get_bone(p_bone_idx));
+ } else {
+ WARN_PRINT("Cannot verify the Jiggle joint " + itos(p_joint_idx) + " bone index for this modification...");
+ jiggle_data_chain.write[p_joint_idx].bone_idx = p_bone_idx;
+ }
+ } else {
+ WARN_PRINT("Cannot verify the Jiggle joint " + itos(p_joint_idx) + " bone index for this modification...");
+ jiggle_data_chain.write[p_joint_idx].bone_idx = p_bone_idx;
+ }
+
+ notify_property_list_changed();
+}
+
+int SkeletonModification2DJiggle::get_jiggle_joint_bone_index(int p_joint_idx) const {
+ ERR_FAIL_INDEX_V_MSG(p_joint_idx, jiggle_data_chain.size(), -1, "Jiggle joint out of range!");
+ return jiggle_data_chain[p_joint_idx].bone_idx;
+}
+
+void SkeletonModification2DJiggle::set_jiggle_joint_override(int p_joint_idx, bool p_override) {
+ ERR_FAIL_INDEX(p_joint_idx, jiggle_data_chain.size());
+ jiggle_data_chain.write[p_joint_idx].override_defaults = p_override;
+ _update_jiggle_joint_data();
+ notify_property_list_changed();
+}
+
+bool SkeletonModification2DJiggle::get_jiggle_joint_override(int p_joint_idx) const {
+ ERR_FAIL_INDEX_V(p_joint_idx, jiggle_data_chain.size(), false);
+ return jiggle_data_chain[p_joint_idx].override_defaults;
+}
+
+void SkeletonModification2DJiggle::set_jiggle_joint_stiffness(int p_joint_idx, float p_stiffness) {
+ ERR_FAIL_COND_MSG(p_stiffness < 0, "Stiffness cannot be set to a negative value!");
+ ERR_FAIL_INDEX(p_joint_idx, jiggle_data_chain.size());
+ jiggle_data_chain.write[p_joint_idx].stiffness = p_stiffness;
+}
+
+float SkeletonModification2DJiggle::get_jiggle_joint_stiffness(int p_joint_idx) const {
+ ERR_FAIL_INDEX_V(p_joint_idx, jiggle_data_chain.size(), -1);
+ return jiggle_data_chain[p_joint_idx].stiffness;
+}
+
+void SkeletonModification2DJiggle::set_jiggle_joint_mass(int p_joint_idx, float p_mass) {
+ ERR_FAIL_COND_MSG(p_mass < 0, "Mass cannot be set to a negative value!");
+ ERR_FAIL_INDEX(p_joint_idx, jiggle_data_chain.size());
+ jiggle_data_chain.write[p_joint_idx].mass = p_mass;
+}
+
+float SkeletonModification2DJiggle::get_jiggle_joint_mass(int p_joint_idx) const {
+ ERR_FAIL_INDEX_V(p_joint_idx, jiggle_data_chain.size(), -1);
+ return jiggle_data_chain[p_joint_idx].mass;
+}
+
+void SkeletonModification2DJiggle::set_jiggle_joint_damping(int p_joint_idx, float p_damping) {
+ ERR_FAIL_COND_MSG(p_damping < 0, "Damping cannot be set to a negative value!");
+ ERR_FAIL_INDEX(p_joint_idx, jiggle_data_chain.size());
+ jiggle_data_chain.write[p_joint_idx].damping = p_damping;
+}
+
+float SkeletonModification2DJiggle::get_jiggle_joint_damping(int p_joint_idx) const {
+ ERR_FAIL_INDEX_V(p_joint_idx, jiggle_data_chain.size(), -1);
+ return jiggle_data_chain[p_joint_idx].damping;
+}
+
+void SkeletonModification2DJiggle::set_jiggle_joint_use_gravity(int p_joint_idx, bool p_use_gravity) {
+ ERR_FAIL_INDEX(p_joint_idx, jiggle_data_chain.size());
+ jiggle_data_chain.write[p_joint_idx].use_gravity = p_use_gravity;
+ notify_property_list_changed();
+}
+
+bool SkeletonModification2DJiggle::get_jiggle_joint_use_gravity(int p_joint_idx) const {
+ ERR_FAIL_INDEX_V(p_joint_idx, jiggle_data_chain.size(), false);
+ return jiggle_data_chain[p_joint_idx].use_gravity;
+}
+
+void SkeletonModification2DJiggle::set_jiggle_joint_gravity(int p_joint_idx, Vector2 p_gravity) {
+ ERR_FAIL_INDEX(p_joint_idx, jiggle_data_chain.size());
+ jiggle_data_chain.write[p_joint_idx].gravity = p_gravity;
+}
+
+Vector2 SkeletonModification2DJiggle::get_jiggle_joint_gravity(int p_joint_idx) const {
+ ERR_FAIL_INDEX_V(p_joint_idx, jiggle_data_chain.size(), Vector2(0, 0));
+ return jiggle_data_chain[p_joint_idx].gravity;
+}
+
+void SkeletonModification2DJiggle::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification2DJiggle::set_target_node);
+ ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification2DJiggle::get_target_node);
+
+ ClassDB::bind_method(D_METHOD("set_jiggle_data_chain_length", "length"), &SkeletonModification2DJiggle::set_jiggle_data_chain_length);
+ ClassDB::bind_method(D_METHOD("get_jiggle_data_chain_length"), &SkeletonModification2DJiggle::get_jiggle_data_chain_length);
+
+ ClassDB::bind_method(D_METHOD("set_stiffness", "stiffness"), &SkeletonModification2DJiggle::set_stiffness);
+ ClassDB::bind_method(D_METHOD("get_stiffness"), &SkeletonModification2DJiggle::get_stiffness);
+ ClassDB::bind_method(D_METHOD("set_mass", "mass"), &SkeletonModification2DJiggle::set_mass);
+ ClassDB::bind_method(D_METHOD("get_mass"), &SkeletonModification2DJiggle::get_mass);
+ ClassDB::bind_method(D_METHOD("set_damping", "damping"), &SkeletonModification2DJiggle::set_damping);
+ ClassDB::bind_method(D_METHOD("get_damping"), &SkeletonModification2DJiggle::get_damping);
+ ClassDB::bind_method(D_METHOD("set_use_gravity", "use_gravity"), &SkeletonModification2DJiggle::set_use_gravity);
+ ClassDB::bind_method(D_METHOD("get_use_gravity"), &SkeletonModification2DJiggle::get_use_gravity);
+ ClassDB::bind_method(D_METHOD("set_gravity", "gravity"), &SkeletonModification2DJiggle::set_gravity);
+ ClassDB::bind_method(D_METHOD("get_gravity"), &SkeletonModification2DJiggle::get_gravity);
+
+ ClassDB::bind_method(D_METHOD("set_use_colliders", "use_colliders"), &SkeletonModification2DJiggle::set_use_colliders);
+ ClassDB::bind_method(D_METHOD("get_use_colliders"), &SkeletonModification2DJiggle::get_use_colliders);
+ ClassDB::bind_method(D_METHOD("set_collision_mask", "collision_mask"), &SkeletonModification2DJiggle::set_collision_mask);
+ ClassDB::bind_method(D_METHOD("get_collision_mask"), &SkeletonModification2DJiggle::get_collision_mask);
+
+ // Jiggle joint data functions
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_bone2d_node", "joint_idx", "bone2d_node"), &SkeletonModification2DJiggle::set_jiggle_joint_bone2d_node);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_bone2d_node", "joint_idx"), &SkeletonModification2DJiggle::get_jiggle_joint_bone2d_node);
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_bone_index", "joint_idx", "bone_idx"), &SkeletonModification2DJiggle::set_jiggle_joint_bone_index);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_bone_index", "joint_idx"), &SkeletonModification2DJiggle::get_jiggle_joint_bone_index);
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_override", "joint_idx", "override"), &SkeletonModification2DJiggle::set_jiggle_joint_override);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_override", "joint_idx"), &SkeletonModification2DJiggle::get_jiggle_joint_override);
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_stiffness", "joint_idx", "stiffness"), &SkeletonModification2DJiggle::set_jiggle_joint_stiffness);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_stiffness", "joint_idx"), &SkeletonModification2DJiggle::get_jiggle_joint_stiffness);
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_mass", "joint_idx", "mass"), &SkeletonModification2DJiggle::set_jiggle_joint_mass);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_mass", "joint_idx"), &SkeletonModification2DJiggle::get_jiggle_joint_mass);
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_damping", "joint_idx", "damping"), &SkeletonModification2DJiggle::set_jiggle_joint_damping);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_damping", "joint_idx"), &SkeletonModification2DJiggle::get_jiggle_joint_damping);
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_use_gravity", "joint_idx", "use_gravity"), &SkeletonModification2DJiggle::set_jiggle_joint_use_gravity);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_use_gravity", "joint_idx"), &SkeletonModification2DJiggle::get_jiggle_joint_use_gravity);
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_gravity", "joint_idx", "gravity"), &SkeletonModification2DJiggle::set_jiggle_joint_gravity);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_gravity", "joint_idx"), &SkeletonModification2DJiggle::get_jiggle_joint_gravity);
+
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node2D"), "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::VECTOR2, "gravity"), "set_gravity", "get_gravity");
+ ADD_GROUP("", "");
+}
+
+SkeletonModification2DJiggle::SkeletonModification2DJiggle() {
+ stack = nullptr;
+ is_setup = false;
+ jiggle_data_chain = Vector<Jiggle_Joint_Data2D>();
+ stiffness = 3;
+ mass = 0.75;
+ damping = 0.75;
+ use_gravity = false;
+ gravity = Vector2(0, 6.0);
+ enabled = true;
+ editor_draw_gizmo = false; // Nothing to really show in a gizmo right now.
+}
+
+SkeletonModification2DJiggle::~SkeletonModification2DJiggle() {
+}
diff --git a/scene/resources/skeleton_modification_2d_jiggle.h b/scene/resources/skeleton_modification_2d_jiggle.h
new file mode 100644
index 0000000000..e24038a1db
--- /dev/null
+++ b/scene/resources/skeleton_modification_2d_jiggle.h
@@ -0,0 +1,139 @@
+/*************************************************************************/
+/* skeleton_modification_2d_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. */
+/*************************************************************************/
+
+#ifndef SKELETONMODIFICATION2DJIGGLE_H
+#define SKELETONMODIFICATION2DJIGGLE_H
+
+#include "scene/2d/skeleton_2d.h"
+#include "scene/resources/skeleton_modification_2d.h"
+
+///////////////////////////////////////
+// SkeletonModification2DJIGGLE
+///////////////////////////////////////
+
+class SkeletonModification2DJiggle : public SkeletonModification2D {
+ GDCLASS(SkeletonModification2DJiggle, SkeletonModification2D);
+
+private:
+ struct Jiggle_Joint_Data2D {
+ int bone_idx = -1;
+ NodePath bone2d_node;
+ ObjectID bone2d_node_cache;
+
+ bool override_defaults = false;
+ float stiffness = 3;
+ float mass = 0.75;
+ float damping = 0.75;
+ bool use_gravity = false;
+ Vector2 gravity = Vector2(0, 6.0);
+
+ Vector2 force = Vector2(0, 0);
+ Vector2 acceleration = Vector2(0, 0);
+ Vector2 velocity = Vector2(0, 0);
+ Vector2 last_position = Vector2(0, 0);
+ Vector2 dynamic_position = Vector2(0, 0);
+
+ Vector2 last_noncollision_position = Vector2(0, 0);
+ };
+
+ Vector<Jiggle_Joint_Data2D> jiggle_data_chain;
+
+ NodePath target_node;
+ ObjectID target_node_cache;
+ void update_target_cache();
+
+ float stiffness = 3;
+ float mass = 0.75;
+ float damping = 0.75;
+ bool use_gravity = false;
+ Vector2 gravity = Vector2(0, 6);
+
+ bool use_colliders = false;
+ uint32_t collision_mask = 1;
+
+ void jiggle_joint_update_bone2d_cache(int p_joint_idx);
+ void _execute_jiggle_joint(int p_joint_idx, Node2D *p_target, float p_delta);
+ void _update_jiggle_joint_data();
+
+protected:
+ static void _bind_methods();
+ bool _set(const StringName &p_path, const Variant &p_value);
+ bool _get(const StringName &p_path, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+public:
+ void _execute(float p_delta) override;
+ void _setup_modification(SkeletonModificationStack2D *p_stack) override;
+
+ void set_target_node(const NodePath &p_target_node);
+ NodePath get_target_node() const;
+
+ void set_stiffness(float p_stiffness);
+ float get_stiffness() const;
+ void set_mass(float p_mass);
+ float get_mass() const;
+ void set_damping(float p_damping);
+ float get_damping() const;
+ void set_use_gravity(bool p_use_gravity);
+ bool get_use_gravity() const;
+ void set_gravity(Vector2 p_gravity);
+ Vector2 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_bone2d_node(int p_joint_idx, const NodePath &p_target_node);
+ NodePath get_jiggle_joint_bone2d_node(int p_joint_idx) const;
+ void set_jiggle_joint_bone_index(int p_joint_idx, int p_bone_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, float p_stiffness);
+ float get_jiggle_joint_stiffness(int p_joint_idx) const;
+ void set_jiggle_joint_mass(int p_joint_idx, float p_mass);
+ float get_jiggle_joint_mass(int p_joint_idx) const;
+ void set_jiggle_joint_damping(int p_joint_idx, float p_damping);
+ float 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, Vector2 p_gravity);
+ Vector2 get_jiggle_joint_gravity(int p_joint_idx) const;
+
+ SkeletonModification2DJiggle();
+ ~SkeletonModification2DJiggle();
+};
+
+#endif // SKELETONMODIFICATION2DJIGGLE_H
diff --git a/scene/resources/skeleton_modification_2d_lookat.cpp b/scene/resources/skeleton_modification_2d_lookat.cpp
new file mode 100644
index 0000000000..2da770f012
--- /dev/null
+++ b/scene/resources/skeleton_modification_2d_lookat.cpp
@@ -0,0 +1,407 @@
+/*************************************************************************/
+/* skeleton_modification_2d_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 "skeleton_modification_2d_lookat.h"
+#include "scene/2d/skeleton_2d.h"
+
+#ifdef TOOLS_ENABLED
+#include "editor/editor_settings.h"
+#endif // TOOLS_ENABLED
+
+bool SkeletonModification2DLookAt::_set(const StringName &p_path, const Variant &p_value) {
+ String path = p_path;
+
+ if (path.begins_with("enable_constraint")) {
+ set_enable_constraint(p_value);
+ } else if (path.begins_with("constraint_angle_min")) {
+ set_constraint_angle_min(Math::deg2rad(float(p_value)));
+ } else if (path.begins_with("constraint_angle_max")) {
+ set_constraint_angle_max(Math::deg2rad(float(p_value)));
+ } else if (path.begins_with("constraint_angle_invert")) {
+ set_constraint_angle_invert(p_value);
+ } else if (path.begins_with("constraint_in_localspace")) {
+ set_constraint_in_localspace(p_value);
+ } else if (path.begins_with("additional_rotation")) {
+ set_additional_rotation(Math::deg2rad(float(p_value)));
+ }
+
+#ifdef TOOLS_ENABLED
+ if (path.begins_with("editor/draw_gizmo")) {
+ set_editor_draw_gizmo(p_value);
+ }
+#endif // TOOLS_ENABLED
+
+ return true;
+}
+
+bool SkeletonModification2DLookAt::_get(const StringName &p_path, Variant &r_ret) const {
+ String path = p_path;
+
+ if (path.begins_with("enable_constraint")) {
+ r_ret = get_enable_constraint();
+ } else if (path.begins_with("constraint_angle_min")) {
+ r_ret = Math::rad2deg(get_constraint_angle_min());
+ } else if (path.begins_with("constraint_angle_max")) {
+ r_ret = Math::rad2deg(get_constraint_angle_max());
+ } else if (path.begins_with("constraint_angle_invert")) {
+ r_ret = get_constraint_angle_invert();
+ } else if (path.begins_with("constraint_in_localspace")) {
+ r_ret = get_constraint_in_localspace();
+ } else if (path.begins_with("additional_rotation")) {
+ r_ret = Math::rad2deg(get_additional_rotation());
+ }
+
+#ifdef TOOLS_ENABLED
+ if (path.begins_with("editor/draw_gizmo")) {
+ r_ret = get_editor_draw_gizmo();
+ }
+#endif // TOOLS_ENABLED
+
+ return true;
+}
+
+void SkeletonModification2DLookAt::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::BOOL, "enable_constraint", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ if (enable_constraint) {
+ p_list->push_back(PropertyInfo(Variant::FLOAT, "constraint_angle_min", PROPERTY_HINT_RANGE, "-360, 360, 0.01", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, "constraint_angle_max", PROPERTY_HINT_RANGE, "-360, 360, 0.01", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::BOOL, "constraint_angle_invert", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::BOOL, "constraint_in_localspace", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ }
+ p_list->push_back(PropertyInfo(Variant::FLOAT, "additional_rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ p_list->push_back(PropertyInfo(Variant::BOOL, "editor/draw_gizmo", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ }
+#endif // TOOLS_ENABLED
+}
+
+void SkeletonModification2DLookAt::_execute(float 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()) {
+ WARN_PRINT_ONCE("Target cache is out of date. Attempting to update...");
+ update_target_cache();
+ return;
+ }
+
+ if (bone2d_node_cache.is_null() && !bone2d_node.is_empty()) {
+ update_bone2d_cache();
+ WARN_PRINT_ONCE("Bone2D node cache is out of date. Attempting to update...");
+ return;
+ }
+
+ if (target_node_reference == nullptr) {
+ target_node_reference = Object::cast_to<Node2D>(ObjectDB::get_instance(target_node_cache));
+ }
+ if (!target_node_reference || !target_node_reference->is_inside_tree()) {
+ ERR_PRINT_ONCE("Target node is not in the scene tree. Cannot execute modification!");
+ return;
+ }
+ if (bone_idx <= -1) {
+ ERR_PRINT_ONCE("Bone index is invalid. Cannot execute modification!");
+ return;
+ }
+
+ Bone2D *operation_bone = stack->skeleton->get_bone(bone_idx);
+ if (operation_bone == nullptr) {
+ ERR_PRINT_ONCE("bone_idx for modification does not point to a valid bone! Cannot execute modification");
+ return;
+ }
+
+ Transform2D operation_transform = operation_bone->get_global_transform();
+ Transform2D target_trans = target_node_reference->get_global_transform();
+
+ // 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_scale());
+
+ // Account for the direction the bone faces in:
+ operation_transform.set_rotation(operation_transform.get_rotation() - operation_bone->get_bone_angle());
+
+ // Apply additional rotation
+ operation_transform.set_rotation(operation_transform.get_rotation() + additional_rotation);
+
+ // Apply constraints in globalspace:
+ if (enable_constraint && !constraint_in_localspace) {
+ operation_transform.set_rotation(clamp_angle(operation_transform.get_rotation(), constraint_angle_min, constraint_angle_max, constraint_angle_invert));
+ }
+
+ // Convert from a global transform to a local transform via the Bone2D node
+ operation_bone->set_global_transform(operation_transform);
+ operation_transform = operation_bone->get_transform();
+
+ // Apply constraints in localspace:
+ if (enable_constraint && constraint_in_localspace) {
+ operation_transform.set_rotation(clamp_angle(operation_transform.get_rotation(), constraint_angle_min, constraint_angle_max, constraint_angle_invert));
+ }
+
+ // Set the local pose override, and to make sure child bones are also updated, set the transform of the bone.
+ stack->skeleton->set_bone_local_pose_override(bone_idx, operation_transform, stack->strength, true);
+ operation_bone->set_transform(operation_transform);
+}
+
+void SkeletonModification2DLookAt::_setup_modification(SkeletonModificationStack2D *p_stack) {
+ stack = p_stack;
+
+ if (stack != nullptr) {
+ is_setup = true;
+ update_target_cache();
+ update_bone2d_cache();
+ }
+}
+
+void SkeletonModification2DLookAt::_draw_editor_gizmo() {
+ if (!enabled || !is_setup) {
+ return;
+ }
+
+ Bone2D *operation_bone = stack->skeleton->get_bone(bone_idx);
+ editor_draw_angle_constraints(operation_bone, constraint_angle_min, constraint_angle_max,
+ enable_constraint, constraint_in_localspace, constraint_angle_invert);
+}
+
+void SkeletonModification2DLookAt::update_bone2d_cache() {
+ if (!is_setup || !stack) {
+ ERR_PRINT_ONCE("Cannot update Bone2D cache: modification is not properly setup!");
+ return;
+ }
+
+ bone2d_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree()) {
+ if (stack->skeleton->has_node(bone2d_node)) {
+ Node *node = stack->skeleton->get_node(bone2d_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update Bone2D cache: node is this modification's skeleton or cannot be found!");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update Bone2D cache: node is not in the scene tree!");
+ bone2d_node_cache = node->get_instance_id();
+
+ Bone2D *bone = Object::cast_to<Bone2D>(node);
+ if (bone) {
+ bone_idx = bone->get_index_in_skeleton();
+ } else {
+ ERR_FAIL_MSG("Error Bone2D cache: Nodepath to Bone2D is not a Bone2D node!");
+ }
+
+ // Set this to null so we update it
+ target_node_reference = nullptr;
+ }
+ }
+ }
+}
+
+void SkeletonModification2DLookAt::set_bone2d_node(const NodePath &p_target_node) {
+ bone2d_node = p_target_node;
+ update_bone2d_cache();
+}
+
+NodePath SkeletonModification2DLookAt::get_bone2d_node() const {
+ return bone2d_node;
+}
+
+int SkeletonModification2DLookAt::get_bone_index() const {
+ return bone_idx;
+}
+
+void SkeletonModification2DLookAt::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!");
+
+ if (is_setup) {
+ if (stack->skeleton) {
+ ERR_FAIL_INDEX_MSG(p_bone_idx, stack->skeleton->get_bone_count(), "Passed-in Bone index is out of range!");
+ bone_idx = p_bone_idx;
+ bone2d_node_cache = stack->skeleton->get_bone(p_bone_idx)->get_instance_id();
+ bone2d_node = stack->skeleton->get_path_to(stack->skeleton->get_bone(p_bone_idx));
+ } else {
+ WARN_PRINT("Cannot verify the bone index for this modification...");
+ bone_idx = p_bone_idx;
+ }
+ } else {
+ WARN_PRINT("Cannot verify the bone index for this modification...");
+ bone_idx = p_bone_idx;
+ }
+
+ notify_property_list_changed();
+}
+
+void SkeletonModification2DLookAt::update_target_cache() {
+ if (!is_setup || !stack) {
+ ERR_PRINT_ONCE("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();
+ }
+ }
+ }
+}
+
+void SkeletonModification2DLookAt::set_target_node(const NodePath &p_target_node) {
+ target_node = p_target_node;
+ update_target_cache();
+}
+
+NodePath SkeletonModification2DLookAt::get_target_node() const {
+ return target_node;
+}
+
+float SkeletonModification2DLookAt::get_additional_rotation() const {
+ return additional_rotation;
+}
+
+void SkeletonModification2DLookAt::set_additional_rotation(float p_rotation) {
+ additional_rotation = p_rotation;
+}
+
+void SkeletonModification2DLookAt::set_enable_constraint(bool p_constraint) {
+ enable_constraint = p_constraint;
+ notify_property_list_changed();
+#ifdef TOOLS_ENABLED
+ if (stack && is_setup) {
+ stack->set_editor_gizmos_dirty(true);
+ }
+#endif // TOOLS_ENABLED
+}
+
+bool SkeletonModification2DLookAt::get_enable_constraint() const {
+ return enable_constraint;
+}
+
+void SkeletonModification2DLookAt::set_constraint_angle_min(float p_angle_min) {
+ constraint_angle_min = p_angle_min;
+#ifdef TOOLS_ENABLED
+ if (stack && is_setup) {
+ stack->set_editor_gizmos_dirty(true);
+ }
+#endif // TOOLS_ENABLED
+}
+
+float SkeletonModification2DLookAt::get_constraint_angle_min() const {
+ return constraint_angle_min;
+}
+
+void SkeletonModification2DLookAt::set_constraint_angle_max(float p_angle_max) {
+ constraint_angle_max = p_angle_max;
+#ifdef TOOLS_ENABLED
+ if (stack && is_setup) {
+ stack->set_editor_gizmos_dirty(true);
+ }
+#endif // TOOLS_ENABLED
+}
+
+float SkeletonModification2DLookAt::get_constraint_angle_max() const {
+ return constraint_angle_max;
+}
+
+void SkeletonModification2DLookAt::set_constraint_angle_invert(bool p_invert) {
+ constraint_angle_invert = p_invert;
+#ifdef TOOLS_ENABLED
+ if (stack && is_setup) {
+ stack->set_editor_gizmos_dirty(true);
+ }
+#endif // TOOLS_ENABLED
+}
+
+bool SkeletonModification2DLookAt::get_constraint_angle_invert() const {
+ return constraint_angle_invert;
+}
+
+void SkeletonModification2DLookAt::set_constraint_in_localspace(bool p_constraint_in_localspace) {
+ constraint_in_localspace = p_constraint_in_localspace;
+#ifdef TOOLS_ENABLED
+ if (stack && is_setup) {
+ stack->set_editor_gizmos_dirty(true);
+ }
+#endif // TOOLS_ENABLED
+}
+
+bool SkeletonModification2DLookAt::get_constraint_in_localspace() const {
+ return constraint_in_localspace;
+}
+
+void SkeletonModification2DLookAt::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_bone2d_node", "bone2d_nodepath"), &SkeletonModification2DLookAt::set_bone2d_node);
+ ClassDB::bind_method(D_METHOD("get_bone2d_node"), &SkeletonModification2DLookAt::get_bone2d_node);
+ ClassDB::bind_method(D_METHOD("set_bone_index", "bone_idx"), &SkeletonModification2DLookAt::set_bone_index);
+ ClassDB::bind_method(D_METHOD("get_bone_index"), &SkeletonModification2DLookAt::get_bone_index);
+
+ ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification2DLookAt::set_target_node);
+ ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification2DLookAt::get_target_node);
+
+ ClassDB::bind_method(D_METHOD("set_additional_rotation", "rotation"), &SkeletonModification2DLookAt::set_additional_rotation);
+ ClassDB::bind_method(D_METHOD("get_additional_rotation"), &SkeletonModification2DLookAt::get_additional_rotation);
+
+ ClassDB::bind_method(D_METHOD("set_enable_constraint", "enable_constraint"), &SkeletonModification2DLookAt::set_enable_constraint);
+ ClassDB::bind_method(D_METHOD("get_enable_constraint"), &SkeletonModification2DLookAt::get_enable_constraint);
+ ClassDB::bind_method(D_METHOD("set_constraint_angle_min", "angle_min"), &SkeletonModification2DLookAt::set_constraint_angle_min);
+ ClassDB::bind_method(D_METHOD("get_constraint_angle_min"), &SkeletonModification2DLookAt::get_constraint_angle_min);
+ ClassDB::bind_method(D_METHOD("set_constraint_angle_max", "angle_max"), &SkeletonModification2DLookAt::set_constraint_angle_max);
+ ClassDB::bind_method(D_METHOD("get_constraint_angle_max"), &SkeletonModification2DLookAt::get_constraint_angle_max);
+ ClassDB::bind_method(D_METHOD("set_constraint_angle_invert", "invert"), &SkeletonModification2DLookAt::set_constraint_angle_invert);
+ ClassDB::bind_method(D_METHOD("get_constraint_angle_invert"), &SkeletonModification2DLookAt::get_constraint_angle_invert);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_index"), "set_bone_index", "get_bone_index");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "bone2d_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Bone2D"), "set_bone2d_node", "get_bone2d_node");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node2D"), "set_target_node", "get_target_node");
+}
+
+SkeletonModification2DLookAt::SkeletonModification2DLookAt() {
+ stack = nullptr;
+ is_setup = false;
+ bone_idx = -1;
+ additional_rotation = 0;
+ enable_constraint = false;
+ constraint_angle_min = 0;
+ constraint_angle_max = Math_PI * 2;
+ constraint_angle_invert = false;
+ enabled = true;
+
+ editor_draw_gizmo = true;
+}
+
+SkeletonModification2DLookAt::~SkeletonModification2DLookAt() {
+}
diff --git a/scene/resources/skeleton_modification_2d_lookat.h b/scene/resources/skeleton_modification_2d_lookat.h
new file mode 100644
index 0000000000..6aff30b826
--- /dev/null
+++ b/scene/resources/skeleton_modification_2d_lookat.h
@@ -0,0 +1,100 @@
+/*************************************************************************/
+/* skeleton_modification_2d_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. */
+/*************************************************************************/
+
+#ifndef SKELETONMODIFICATION2DLOOKAT_H
+#define SKELETONMODIFICATION2DLOOKAT_H
+
+#include "scene/2d/skeleton_2d.h"
+#include "scene/resources/skeleton_modification_2d.h"
+
+///////////////////////////////////////
+// SkeletonModification2DLookAt
+///////////////////////////////////////
+
+class SkeletonModification2DLookAt : public SkeletonModification2D {
+ GDCLASS(SkeletonModification2DLookAt, SkeletonModification2D);
+
+private:
+ int bone_idx = -1;
+ NodePath bone2d_node;
+ ObjectID bone2d_node_cache;
+
+ NodePath target_node;
+ ObjectID target_node_cache;
+ Node2D *target_node_reference = nullptr;
+
+ float additional_rotation = 0;
+ bool enable_constraint = false;
+ float constraint_angle_min = 0;
+ float constraint_angle_max = (2.0 * Math_PI);
+ bool constraint_angle_invert = false;
+ bool constraint_in_localspace = true;
+
+ void update_bone2d_cache();
+ void update_target_cache();
+
+protected:
+ static void _bind_methods();
+ bool _set(const StringName &p_path, const Variant &p_value);
+ bool _get(const StringName &p_path, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+public:
+ void _execute(float p_delta) override;
+ void _setup_modification(SkeletonModificationStack2D *p_stack) override;
+ void _draw_editor_gizmo() override;
+
+ void set_bone2d_node(const NodePath &p_target_node);
+ NodePath get_bone2d_node() 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(float p_rotation);
+ float get_additional_rotation() const;
+
+ void set_enable_constraint(bool p_constraint);
+ bool get_enable_constraint() const;
+ void set_constraint_angle_min(float p_angle_min);
+ float get_constraint_angle_min() const;
+ void set_constraint_angle_max(float p_angle_max);
+ float get_constraint_angle_max() const;
+ void set_constraint_angle_invert(bool p_invert);
+ bool get_constraint_angle_invert() const;
+ void set_constraint_in_localspace(bool p_constraint_in_localspace);
+ bool get_constraint_in_localspace() const;
+
+ SkeletonModification2DLookAt();
+ ~SkeletonModification2DLookAt();
+};
+
+#endif // SKELETONMODIFICATION2DLOOKAT_H
diff --git a/scene/resources/skeleton_modification_2d_physicalbones.cpp b/scene/resources/skeleton_modification_2d_physicalbones.cpp
new file mode 100644
index 0000000000..9dedb93f36
--- /dev/null
+++ b/scene/resources/skeleton_modification_2d_physicalbones.cpp
@@ -0,0 +1,297 @@
+/*************************************************************************/
+/* skeleton_modification_2d_physicalbones.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_2d_physicalbones.h"
+#include "scene/2d/physical_bone_2d.h"
+#include "scene/2d/skeleton_2d.h"
+
+bool SkeletonModification2DPhysicalBones::_set(const StringName &p_path, const Variant &p_value) {
+ String path = p_path;
+
+#ifdef TOOLS_ENABLED
+ // Exposes a way to fetch the PhysicalBone2D nodes from the Godot editor.
+ if (is_setup) {
+ if (Engine::get_singleton()->is_editor_hint()) {
+ if (path.begins_with("fetch_bones")) {
+ fetch_physical_bones();
+ notify_property_list_changed();
+ return true;
+ }
+ }
+ }
+#endif //TOOLS_ENABLED
+
+ if (path.begins_with("joint_")) {
+ int which = path.get_slicec('_', 1).to_int();
+ String what = path.get_slicec('_', 2);
+ ERR_FAIL_INDEX_V(which, physical_bone_chain.size(), false);
+
+ if (what == "nodepath") {
+ set_physical_bone_node(which, p_value);
+ }
+ return true;
+ }
+ return true;
+}
+
+bool SkeletonModification2DPhysicalBones::_get(const StringName &p_path, Variant &r_ret) const {
+ String path = p_path;
+
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ if (path.begins_with("fetch_bones")) {
+ return true; // Do nothing!
+ }
+ }
+#endif //TOOLS_ENABLED
+
+ if (path.begins_with("joint_")) {
+ int which = path.get_slicec('_', 1).to_int();
+ String what = path.get_slicec('_', 2);
+ ERR_FAIL_INDEX_V(which, physical_bone_chain.size(), false);
+
+ if (what == "nodepath") {
+ r_ret = get_physical_bone_node(which);
+ }
+ return true;
+ }
+ return true;
+}
+
+void SkeletonModification2DPhysicalBones::_get_property_list(List<PropertyInfo> *p_list) const {
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ p_list->push_back(PropertyInfo(Variant::BOOL, "fetch_bones", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ }
+#endif //TOOLS_ENABLED
+
+ for (int i = 0; i < physical_bone_chain.size(); i++) {
+ String base_string = "joint_" + itos(i) + "_";
+
+ p_list->push_back(PropertyInfo(Variant::NODE_PATH, base_string + "nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "PhysicalBone2D", PROPERTY_USAGE_DEFAULT));
+ }
+}
+
+void SkeletonModification2DPhysicalBones::_execute(float p_delta) {
+ ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr,
+ "Modification is not setup and therefore cannot execute!");
+ if (!enabled) {
+ return;
+ }
+
+ if (_simulation_state_dirty) {
+ _update_simulation_state();
+ }
+
+ for (int i = 0; i < physical_bone_chain.size(); i++) {
+ PhysicalBone_Data2D bone_data = physical_bone_chain[i];
+ if (bone_data.physical_bone_node_cache.is_null()) {
+ WARN_PRINT_ONCE("PhysicalBone2D cache " + itos(i) + " is out of date. Attempting to update...");
+ _physical_bone_update_cache(i);
+ continue;
+ }
+
+ PhysicalBone2D *physical_bone = Object::cast_to<PhysicalBone2D>(ObjectDB::get_instance(bone_data.physical_bone_node_cache));
+ if (!physical_bone) {
+ ERR_PRINT_ONCE("PhysicalBone2D not found at index " + itos(i) + "!");
+ return;
+ }
+ if (physical_bone->get_bone2d_index() < 0 || physical_bone->get_bone2d_index() > stack->skeleton->get_bone_count()) {
+ ERR_PRINT_ONCE("PhysicalBone2D at index " + itos(i) + " has invalid Bone2D!");
+ return;
+ }
+ Bone2D *bone_2d = stack->skeleton->get_bone(physical_bone->get_bone2d_index());
+
+ if (physical_bone->get_simulate_physics() && !physical_bone->get_follow_bone_when_simulating()) {
+ bone_2d->set_global_transform(physical_bone->get_global_transform());
+ stack->skeleton->set_bone_local_pose_override(physical_bone->get_bone2d_index(), bone_2d->get_transform(), stack->strength, true);
+ }
+ }
+}
+
+void SkeletonModification2DPhysicalBones::_setup_modification(SkeletonModificationStack2D *p_stack) {
+ stack = p_stack;
+
+ if (stack) {
+ is_setup = true;
+
+ if (stack->skeleton) {
+ for (int i = 0; i < physical_bone_chain.size(); i++) {
+ _physical_bone_update_cache(i);
+ }
+ }
+ }
+}
+
+void SkeletonModification2DPhysicalBones::_physical_bone_update_cache(int p_joint_idx) {
+ ERR_FAIL_INDEX_MSG(p_joint_idx, physical_bone_chain.size(), "Cannot update PhysicalBone2D cache: joint index out of range!");
+ if (!is_setup || !stack) {
+ if (!stack) {
+ ERR_PRINT_ONCE("Cannot update PhysicalBone2D cache: modification is not properly setup!");
+ }
+ return;
+ }
+
+ physical_bone_chain.write[p_joint_idx].physical_bone_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree()) {
+ if (stack->skeleton->has_node(physical_bone_chain[p_joint_idx].physical_bone_node)) {
+ Node *node = stack->skeleton->get_node(physical_bone_chain[p_joint_idx].physical_bone_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update Physical Bone2D " + itos(p_joint_idx) + " cache: node is this modification's skeleton or cannot be found!");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update Physical Bone2D " + itos(p_joint_idx) + " cache: node is not in scene tree!");
+ physical_bone_chain.write[p_joint_idx].physical_bone_node_cache = node->get_instance_id();
+ }
+ }
+ }
+}
+
+int SkeletonModification2DPhysicalBones::get_physical_bone_chain_length() {
+ return physical_bone_chain.size();
+}
+
+void SkeletonModification2DPhysicalBones::set_physical_bone_chain_length(int p_length) {
+ ERR_FAIL_COND(p_length < 0);
+ physical_bone_chain.resize(p_length);
+ notify_property_list_changed();
+}
+
+void SkeletonModification2DPhysicalBones::fetch_physical_bones() {
+ ERR_FAIL_COND_MSG(!stack, "No modification stack found! Cannot fetch physical bones!");
+ ERR_FAIL_COND_MSG(!stack->skeleton, "No skeleton found! Cannot fetch physical bones!");
+
+ physical_bone_chain.clear();
+
+ List<Node *> node_queue = List<Node *>();
+ node_queue.push_back(stack->skeleton);
+
+ while (node_queue.size() > 0) {
+ Node *node_to_process = node_queue[0];
+ node_queue.pop_front();
+
+ if (node_to_process != nullptr) {
+ PhysicalBone2D *potential_bone = Object::cast_to<PhysicalBone2D>(node_to_process);
+ if (potential_bone) {
+ PhysicalBone_Data2D new_data = PhysicalBone_Data2D();
+ new_data.physical_bone_node = stack->skeleton->get_path_to(potential_bone);
+ new_data.physical_bone_node_cache = potential_bone->get_instance_id();
+ physical_bone_chain.push_back(new_data);
+ }
+ for (int i = 0; i < node_to_process->get_child_count(); i++) {
+ node_queue.push_back(node_to_process->get_child(i));
+ }
+ }
+ }
+}
+
+void SkeletonModification2DPhysicalBones::start_simulation(const TypedArray<StringName> &p_bones) {
+ _simulation_state_dirty = true;
+ _simulation_state_dirty_names = p_bones;
+ _simulation_state_dirty_process = true;
+
+ if (is_setup) {
+ _update_simulation_state();
+ }
+}
+
+void SkeletonModification2DPhysicalBones::stop_simulation(const TypedArray<StringName> &p_bones) {
+ _simulation_state_dirty = true;
+ _simulation_state_dirty_names = p_bones;
+ _simulation_state_dirty_process = false;
+
+ if (is_setup) {
+ _update_simulation_state();
+ }
+}
+
+void SkeletonModification2DPhysicalBones::_update_simulation_state() {
+ if (!_simulation_state_dirty) {
+ return;
+ }
+ _simulation_state_dirty = false;
+
+ if (_simulation_state_dirty_names.size() <= 0) {
+ for (int i = 0; i < physical_bone_chain.size(); i++) {
+ PhysicalBone2D *physical_bone = Object::cast_to<PhysicalBone2D>(stack->skeleton->get_node(physical_bone_chain[i].physical_bone_node));
+ if (!physical_bone) {
+ continue;
+ }
+
+ physical_bone->set_simulate_physics(_simulation_state_dirty_process);
+ }
+ } else {
+ for (int i = 0; i < physical_bone_chain.size(); i++) {
+ PhysicalBone2D *physical_bone = Object::cast_to<PhysicalBone2D>(ObjectDB::get_instance(physical_bone_chain[i].physical_bone_node_cache));
+ if (!physical_bone) {
+ continue;
+ }
+ if (_simulation_state_dirty_names.has(physical_bone->get_name())) {
+ physical_bone->set_simulate_physics(_simulation_state_dirty_process);
+ }
+ }
+ }
+}
+
+void SkeletonModification2DPhysicalBones::set_physical_bone_node(int p_joint_idx, const NodePath &p_nodepath) {
+ ERR_FAIL_INDEX_MSG(p_joint_idx, physical_bone_chain.size(), "Joint index out of range!");
+ physical_bone_chain.write[p_joint_idx].physical_bone_node = p_nodepath;
+ _physical_bone_update_cache(p_joint_idx);
+}
+
+NodePath SkeletonModification2DPhysicalBones::get_physical_bone_node(int p_joint_idx) const {
+ ERR_FAIL_INDEX_V_MSG(p_joint_idx, physical_bone_chain.size(), NodePath(), "Joint index out of range!");
+ return physical_bone_chain[p_joint_idx].physical_bone_node;
+}
+
+void SkeletonModification2DPhysicalBones::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_physical_bone_chain_length", "length"), &SkeletonModification2DPhysicalBones::set_physical_bone_chain_length);
+ ClassDB::bind_method(D_METHOD("get_physical_bone_chain_length"), &SkeletonModification2DPhysicalBones::get_physical_bone_chain_length);
+
+ ClassDB::bind_method(D_METHOD("set_physical_bone_node", "joint_idx", "physicalbone2d_node"), &SkeletonModification2DPhysicalBones::set_physical_bone_node);
+ ClassDB::bind_method(D_METHOD("get_physical_bone_node", "joint_idx"), &SkeletonModification2DPhysicalBones::get_physical_bone_node);
+
+ ClassDB::bind_method(D_METHOD("fetch_physical_bones"), &SkeletonModification2DPhysicalBones::fetch_physical_bones);
+ ClassDB::bind_method(D_METHOD("start_simulation", "bones"), &SkeletonModification2DPhysicalBones::start_simulation, DEFVAL(Array()));
+ ClassDB::bind_method(D_METHOD("stop_simulation", "bones"), &SkeletonModification2DPhysicalBones::stop_simulation, DEFVAL(Array()));
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "physical_bone_chain_length", PROPERTY_HINT_RANGE, "0,100,1"), "set_physical_bone_chain_length", "get_physical_bone_chain_length");
+}
+
+SkeletonModification2DPhysicalBones::SkeletonModification2DPhysicalBones() {
+ stack = nullptr;
+ is_setup = false;
+ physical_bone_chain = Vector<PhysicalBone_Data2D>();
+ enabled = true;
+ editor_draw_gizmo = false; // Nothing to really show in a gizmo right now.
+}
+
+SkeletonModification2DPhysicalBones::~SkeletonModification2DPhysicalBones() {
+}
diff --git a/scene/resources/skeleton_modification_2d_physicalbones.h b/scene/resources/skeleton_modification_2d_physicalbones.h
new file mode 100644
index 0000000000..cdf6a5f570
--- /dev/null
+++ b/scene/resources/skeleton_modification_2d_physicalbones.h
@@ -0,0 +1,82 @@
+/*************************************************************************/
+/* skeleton_modification_2d_physicalbones.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 SKELETONMODIFICATION2DPHYSICALBONES_H
+#define SKELETONMODIFICATION2DPHYSICALBONES_H
+
+#include "scene/2d/skeleton_2d.h"
+#include "scene/resources/skeleton_modification_2d.h"
+
+///////////////////////////////////////
+// SkeletonModification2DJIGGLE
+///////////////////////////////////////
+
+class SkeletonModification2DPhysicalBones : public SkeletonModification2D {
+ GDCLASS(SkeletonModification2DPhysicalBones, SkeletonModification2D);
+
+private:
+ struct PhysicalBone_Data2D {
+ NodePath physical_bone_node;
+ ObjectID physical_bone_node_cache;
+ };
+ Vector<PhysicalBone_Data2D> physical_bone_chain;
+
+ void _physical_bone_update_cache(int p_joint_idx);
+
+ bool _simulation_state_dirty = false;
+ TypedArray<StringName> _simulation_state_dirty_names;
+ bool _simulation_state_dirty_process;
+ void _update_simulation_state();
+
+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:
+ void _execute(float p_delta) override;
+ void _setup_modification(SkeletonModificationStack2D *p_stack) override;
+
+ int get_physical_bone_chain_length();
+ void set_physical_bone_chain_length(int p_new_length);
+
+ void set_physical_bone_node(int p_joint_idx, const NodePath &p_path);
+ NodePath get_physical_bone_node(int p_joint_idx) const;
+
+ void fetch_physical_bones();
+ void start_simulation(const TypedArray<StringName> &p_bones);
+ void stop_simulation(const TypedArray<StringName> &p_bones);
+
+ SkeletonModification2DPhysicalBones();
+ ~SkeletonModification2DPhysicalBones();
+};
+
+#endif // SKELETONMODIFICATION2DPHYSICALBONES_H
diff --git a/scene/resources/skeleton_modification_2d_stackholder.cpp b/scene/resources/skeleton_modification_2d_stackholder.cpp
new file mode 100644
index 0000000000..9436092cd9
--- /dev/null
+++ b/scene/resources/skeleton_modification_2d_stackholder.cpp
@@ -0,0 +1,131 @@
+/*************************************************************************/
+/* skeleton_modification_2d_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 "skeleton_modification_2d_stackholder.h"
+#include "scene/2d/skeleton_2d.h"
+
+bool SkeletonModification2DStackHolder::_set(const StringName &p_path, const Variant &p_value) {
+ String path = p_path;
+
+ if (path == "held_modification_stack") {
+ set_held_modification_stack(p_value);
+ }
+
+#ifdef TOOLS_ENABLED
+ if (path == "editor/draw_gizmo") {
+ set_editor_draw_gizmo(p_value);
+ }
+#endif // TOOLS_ENABLED
+
+ return true;
+}
+
+bool SkeletonModification2DStackHolder::_get(const StringName &p_path, Variant &r_ret) const {
+ String path = p_path;
+
+ if (path == "held_modification_stack") {
+ r_ret = get_held_modification_stack();
+ }
+
+#ifdef TOOLS_ENABLED
+ if (path == "editor/draw_gizmo") {
+ r_ret = get_editor_draw_gizmo();
+ }
+#endif // TOOLS_ENABLED
+
+ return true;
+}
+
+void SkeletonModification2DStackHolder::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::OBJECT, "held_modification_stack", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonModificationStack2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
+
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ p_list->push_back(PropertyInfo(Variant::BOOL, "editor/draw_gizmo", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ }
+#endif // TOOLS_ENABLED
+}
+
+void SkeletonModification2DStackHolder::_execute(float 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 SkeletonModification2DStackHolder::_setup_modification(SkeletonModificationStack2D *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 SkeletonModification2DStackHolder::_draw_editor_gizmo() {
+ if (stack) {
+ if (held_modification_stack.is_valid()) {
+ held_modification_stack->draw_editor_gizmos();
+ }
+ }
+}
+
+void SkeletonModification2DStackHolder::set_held_modification_stack(Ref<SkeletonModificationStack2D> 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<SkeletonModificationStack2D> SkeletonModification2DStackHolder::get_held_modification_stack() const {
+ return held_modification_stack;
+}
+
+void SkeletonModification2DStackHolder::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_held_modification_stack", "held_modification_stack"), &SkeletonModification2DStackHolder::set_held_modification_stack);
+ ClassDB::bind_method(D_METHOD("get_held_modification_stack"), &SkeletonModification2DStackHolder::get_held_modification_stack);
+}
+
+SkeletonModification2DStackHolder::SkeletonModification2DStackHolder() {
+ stack = nullptr;
+ is_setup = false;
+ enabled = true;
+}
+
+SkeletonModification2DStackHolder::~SkeletonModification2DStackHolder() {
+}
diff --git a/scene/resources/skeleton_modification_2d_stackholder.h b/scene/resources/skeleton_modification_2d_stackholder.h
new file mode 100644
index 0000000000..9cc38e3942
--- /dev/null
+++ b/scene/resources/skeleton_modification_2d_stackholder.h
@@ -0,0 +1,64 @@
+/*************************************************************************/
+/* skeleton_modification_2d_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. */
+/*************************************************************************/
+
+#ifndef SKELETONMODIFICATION2DSTACKHOLDER_H
+#define SKELETONMODIFICATION2DSTACKHOLDER_H
+
+#include "scene/2d/skeleton_2d.h"
+#include "scene/resources/skeleton_modification_2d.h"
+
+///////////////////////////////////////
+// SkeletonModification2DJIGGLE
+///////////////////////////////////////
+
+class SkeletonModification2DStackHolder : public SkeletonModification2D {
+ GDCLASS(SkeletonModification2DStackHolder, SkeletonModification2D);
+
+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<SkeletonModificationStack2D> held_modification_stack;
+
+ void _execute(float p_delta) override;
+ void _setup_modification(SkeletonModificationStack2D *p_stack) override;
+ void _draw_editor_gizmo() override;
+
+ void set_held_modification_stack(Ref<SkeletonModificationStack2D> p_held_stack);
+ Ref<SkeletonModificationStack2D> get_held_modification_stack() const;
+
+ SkeletonModification2DStackHolder();
+ ~SkeletonModification2DStackHolder();
+};
+
+#endif // SKELETONMODIFICATION2DSTACKHOLDER_H
diff --git a/scene/resources/skeleton_modification_2d_twoboneik.cpp b/scene/resources/skeleton_modification_2d_twoboneik.cpp
new file mode 100644
index 0000000000..88d80a501f
--- /dev/null
+++ b/scene/resources/skeleton_modification_2d_twoboneik.cpp
@@ -0,0 +1,481 @@
+/*************************************************************************/
+/* skeleton_modification_2d_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 "skeleton_modification_2d_twoboneik.h"
+#include "scene/2d/skeleton_2d.h"
+
+#ifdef TOOLS_ENABLED
+#include "editor/editor_settings.h"
+#endif // TOOLS_ENABLED
+
+bool SkeletonModification2DTwoBoneIK::_set(const StringName &p_path, const Variant &p_value) {
+ String path = p_path;
+
+ if (path == "joint_one_bone_idx") {
+ set_joint_one_bone_idx(p_value);
+ } else if (path == "joint_one_bone2d_node") {
+ set_joint_one_bone2d_node(p_value);
+ } else if (path == "joint_two_bone_idx") {
+ set_joint_two_bone_idx(p_value);
+ } else if (path == "joint_two_bone2d_node") {
+ set_joint_two_bone2d_node(p_value);
+ }
+
+#ifdef TOOLS_ENABLED
+ if (path.begins_with("editor/draw_gizmo")) {
+ set_editor_draw_gizmo(p_value);
+ } else if (path.begins_with("editor/draw_min_max")) {
+ set_editor_draw_min_max(p_value);
+ }
+#endif // TOOLS_ENABLED
+
+ return true;
+}
+
+bool SkeletonModification2DTwoBoneIK::_get(const StringName &p_path, Variant &r_ret) const {
+ String path = p_path;
+
+ if (path == "joint_one_bone_idx") {
+ r_ret = get_joint_one_bone_idx();
+ } else if (path == "joint_one_bone2d_node") {
+ r_ret = get_joint_one_bone2d_node();
+ } else if (path == "joint_two_bone_idx") {
+ r_ret = get_joint_two_bone_idx();
+ } else if (path == "joint_two_bone2d_node") {
+ r_ret = get_joint_two_bone2d_node();
+ }
+
+#ifdef TOOLS_ENABLED
+ if (path.begins_with("editor/draw_gizmo")) {
+ r_ret = get_editor_draw_gizmo();
+ } else if (path.begins_with("editor/draw_min_max")) {
+ r_ret = get_editor_draw_min_max();
+ }
+#endif // TOOLS_ENABLED
+
+ return true;
+}
+
+void SkeletonModification2DTwoBoneIK::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::INT, "joint_one_bone_idx", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::NODE_PATH, "joint_one_bone2d_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Bone2D", 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::NODE_PATH, "joint_two_bone2d_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Bone2D", PROPERTY_USAGE_DEFAULT));
+
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ p_list->push_back(PropertyInfo(Variant::BOOL, "editor/draw_gizmo", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::BOOL, "editor/draw_min_max", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ }
+#endif // TOOLS_ENABLED
+}
+
+void SkeletonModification2DTwoBoneIK::_execute(float 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()) {
+ WARN_PRINT_ONCE("Target cache is out of date. Attempting to update...");
+ update_target_cache();
+ return;
+ }
+
+ if (joint_one_bone2d_node_cache.is_null() && !joint_one_bone2d_node.is_empty()) {
+ WARN_PRINT_ONCE("Joint one Bone2D node cache is out of date. Attempting to update...");
+ update_joint_one_bone2d_cache();
+ }
+ if (joint_two_bone2d_node_cache.is_null() && !joint_two_bone2d_node.is_empty()) {
+ WARN_PRINT_ONCE("Joint two Bone2D node cache is out of date. Attempting to update...");
+ update_joint_two_bone2d_cache();
+ }
+
+ Node2D *target = Object::cast_to<Node2D>(ObjectDB::get_instance(target_node_cache));
+ if (!target || !target->is_inside_tree()) {
+ ERR_PRINT_ONCE("Target node is not in the scene tree. Cannot execute modification!");
+ return;
+ }
+
+ Bone2D *joint_one_bone = stack->skeleton->get_bone(joint_one_bone_idx);
+ if (joint_one_bone == nullptr) {
+ ERR_PRINT_ONCE("Joint one bone_idx does not point to a valid bone! Cannot execute modification!");
+ return;
+ }
+
+ Bone2D *joint_two_bone = stack->skeleton->get_bone(joint_two_bone_idx);
+ if (joint_two_bone == nullptr) {
+ ERR_PRINT_ONCE("Joint two bone_idx does not point to a valid bone! Cannot execute modification!");
+ return;
+ }
+
+ // 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
+ 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);
+
+ float bone_one_length = joint_one_bone->get_length() * MIN(joint_one_bone->get_global_scale().x, joint_one_bone->get_global_scale().y);
+ float bone_two_length = joint_two_bone->get_length() * MIN(joint_two_bone->get_global_scale().x, joint_two_bone->get_global_scale().y);
+ bool override_angles_due_to_out_of_range = false;
+
+ if (joint_one_to_target < target_minimum_distance) {
+ joint_one_to_target = target_minimum_distance;
+ }
+ if (joint_one_to_target > target_maximum_distance && target_maximum_distance > 0.0) {
+ joint_one_to_target = target_maximum_distance;
+ }
+
+ if (bone_one_length + bone_two_length < joint_one_to_target) {
+ override_angles_due_to_out_of_range = true;
+ }
+
+ if (!override_angles_due_to_out_of_range) {
+ float angle_0 = Math::acos(((joint_one_to_target * joint_one_to_target) + (bone_one_length * bone_one_length) - (bone_two_length * bone_two_length)) / (2.0 * joint_one_to_target * bone_one_length));
+ float angle_1 = Math::acos(((bone_two_length * bone_two_length) + (bone_one_length * bone_one_length) - (joint_one_to_target * joint_one_to_target)) / (2.0 * bone_two_length * bone_one_length));
+
+ if (flip_bend_direction) {
+ angle_0 = -angle_0;
+ angle_1 = -angle_1;
+ }
+
+ if (isnan(angle_0) || isnan(angle_1)) {
+ // We cannot solve for this angle! Do nothing to avoid setting the rotation (and scale) to NaN.
+ } else {
+ joint_one_bone->set_global_rotation(angle_atan - angle_0 - joint_one_bone->get_bone_angle());
+ joint_two_bone->set_rotation(-Math_PI - angle_1 - joint_two_bone->get_bone_angle() + joint_one_bone->get_bone_angle());
+ }
+ } else {
+ joint_one_bone->set_global_rotation(angle_atan - joint_one_bone->get_bone_angle());
+ joint_two_bone->set_global_rotation(angle_atan - joint_two_bone->get_bone_angle());
+ }
+
+ stack->skeleton->set_bone_local_pose_override(joint_one_bone_idx, joint_one_bone->get_transform(), stack->strength, true);
+ stack->skeleton->set_bone_local_pose_override(joint_two_bone_idx, joint_two_bone->get_transform(), stack->strength, true);
+}
+
+void SkeletonModification2DTwoBoneIK::_setup_modification(SkeletonModificationStack2D *p_stack) {
+ stack = p_stack;
+
+ if (stack) {
+ is_setup = true;
+ update_target_cache();
+ update_joint_one_bone2d_cache();
+ update_joint_two_bone2d_cache();
+ }
+}
+
+void SkeletonModification2DTwoBoneIK::_draw_editor_gizmo() {
+ if (!enabled || !is_setup) {
+ return;
+ }
+
+ Bone2D *operation_bone_one = stack->skeleton->get_bone(joint_one_bone_idx);
+ if (!operation_bone_one) {
+ return;
+ }
+ stack->skeleton->draw_set_transform(
+ 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);
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ bone_ik_color = EditorSettings::get_singleton()->get("editors/2d/bone_ik_color");
+ }
+#endif // TOOLS_ENABLED
+
+ if (flip_bend_direction) {
+ float angle = -(Math_PI * 0.5) + operation_bone_one->get_bone_angle();
+ stack->skeleton->draw_line(Vector2(0, 0), Vector2(Math::cos(angle), sin(angle)) * (operation_bone_one->get_length() * 0.5), bone_ik_color, 2.0);
+ } else {
+ float angle = (Math_PI * 0.5) + operation_bone_one->get_bone_angle();
+ stack->skeleton->draw_line(Vector2(0, 0), Vector2(Math::cos(angle), sin(angle)) * (operation_bone_one->get_length() * 0.5), bone_ik_color, 2.0);
+ }
+
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ if (editor_draw_min_max) {
+ if (target_maximum_distance != 0.0 || target_minimum_distance != 0.0) {
+ Vector2 target_direction = Vector2(0, 1);
+ if (target_node_cache.is_valid()) {
+ stack->skeleton->draw_set_transform(Vector2(0, 0), 0.0);
+ Node2D *target = Object::cast_to<Node2D>(ObjectDB::get_instance(target_node_cache));
+ target_direction = operation_bone_one->get_global_position().direction_to(target->get_global_position());
+ }
+
+ stack->skeleton->draw_circle(target_direction * target_minimum_distance, 8, bone_ik_color);
+ stack->skeleton->draw_circle(target_direction * target_maximum_distance, 8, bone_ik_color);
+ stack->skeleton->draw_line(target_direction * target_minimum_distance, target_direction * target_maximum_distance, bone_ik_color, 2.0);
+ }
+ }
+ }
+#endif // TOOLS_ENABLED
+}
+
+void SkeletonModification2DTwoBoneIK::update_target_cache() {
+ if (!is_setup || !stack) {
+ ERR_PRINT_ONCE("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();
+ }
+ }
+ }
+}
+
+void SkeletonModification2DTwoBoneIK::update_joint_one_bone2d_cache() {
+ if (!is_setup || !stack) {
+ ERR_PRINT_ONCE("Cannot update joint one Bone2D cache: modification is not properly setup!");
+ return;
+ }
+
+ joint_one_bone2d_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree()) {
+ if (stack->skeleton->has_node(joint_one_bone2d_node)) {
+ Node *node = stack->skeleton->get_node(joint_one_bone2d_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update update joint one Bone2D cache: node is this modification's skeleton or cannot be found!");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update update joint one Bone2D cache: node is not in the scene tree!");
+ joint_one_bone2d_node_cache = node->get_instance_id();
+
+ Bone2D *bone = Object::cast_to<Bone2D>(node);
+ if (bone) {
+ joint_one_bone_idx = bone->get_index_in_skeleton();
+ } else {
+ ERR_FAIL_MSG("update joint one Bone2D cache: Nodepath to Bone2D is not a Bone2D node!");
+ }
+ }
+ }
+ }
+}
+
+void SkeletonModification2DTwoBoneIK::update_joint_two_bone2d_cache() {
+ if (!is_setup || !stack) {
+ ERR_PRINT_ONCE("Cannot update joint two Bone2D cache: modification is not properly setup!");
+ return;
+ }
+
+ joint_two_bone2d_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree()) {
+ if (stack->skeleton->has_node(joint_two_bone2d_node)) {
+ Node *node = stack->skeleton->get_node(joint_two_bone2d_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update update joint two Bone2D cache: node is this modification's skeleton or cannot be found!");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update update joint two Bone2D cache: node is not in scene tree!");
+ joint_two_bone2d_node_cache = node->get_instance_id();
+
+ Bone2D *bone = Object::cast_to<Bone2D>(node);
+ if (bone) {
+ joint_two_bone_idx = bone->get_index_in_skeleton();
+ } else {
+ ERR_FAIL_MSG("update joint two Bone2D cache: Nodepath to Bone2D is not a Bone2D node!");
+ }
+ }
+ }
+ }
+}
+
+void SkeletonModification2DTwoBoneIK::set_target_node(const NodePath &p_target_node) {
+ target_node = p_target_node;
+ update_target_cache();
+}
+
+NodePath SkeletonModification2DTwoBoneIK::get_target_node() const {
+ return target_node;
+}
+
+void SkeletonModification2DTwoBoneIK::set_joint_one_bone2d_node(const NodePath &p_target_node) {
+ joint_one_bone2d_node = p_target_node;
+ update_joint_one_bone2d_cache();
+ notify_property_list_changed();
+}
+
+void SkeletonModification2DTwoBoneIK::set_target_minimum_distance(float p_distance) {
+ ERR_FAIL_COND_MSG(p_distance < 0, "Target minimum distance cannot be less than zero!");
+ target_minimum_distance = p_distance;
+}
+
+float SkeletonModification2DTwoBoneIK::get_target_minimum_distance() const {
+ return target_minimum_distance;
+}
+
+void SkeletonModification2DTwoBoneIK::set_target_maximum_distance(float p_distance) {
+ ERR_FAIL_COND_MSG(p_distance < 0, "Target maximum distance cannot be less than zero!");
+ target_maximum_distance = p_distance;
+}
+
+float SkeletonModification2DTwoBoneIK::get_target_maximum_distance() const {
+ return target_maximum_distance;
+}
+
+void SkeletonModification2DTwoBoneIK::set_flip_bend_direction(bool p_flip_direction) {
+ flip_bend_direction = p_flip_direction;
+
+#ifdef TOOLS_ENABLED
+ if (stack && is_setup) {
+ stack->set_editor_gizmos_dirty(true);
+ }
+#endif // TOOLS_ENABLED
+}
+
+bool SkeletonModification2DTwoBoneIK::get_flip_bend_direction() const {
+ return flip_bend_direction;
+}
+
+NodePath SkeletonModification2DTwoBoneIK::get_joint_one_bone2d_node() const {
+ return joint_one_bone2d_node;
+}
+
+void SkeletonModification2DTwoBoneIK::set_joint_two_bone2d_node(const NodePath &p_target_node) {
+ joint_two_bone2d_node = p_target_node;
+ update_joint_two_bone2d_cache();
+ notify_property_list_changed();
+}
+
+NodePath SkeletonModification2DTwoBoneIK::get_joint_two_bone2d_node() const {
+ return joint_two_bone2d_node;
+}
+
+void SkeletonModification2DTwoBoneIK::set_joint_one_bone_idx(int p_bone_idx) {
+ ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!");
+
+ if (is_setup) {
+ if (stack->skeleton) {
+ ERR_FAIL_INDEX_MSG(p_bone_idx, stack->skeleton->get_bone_count(), "Passed-in Bone index is out of range!");
+ joint_one_bone_idx = p_bone_idx;
+ joint_one_bone2d_node_cache = stack->skeleton->get_bone(p_bone_idx)->get_instance_id();
+ joint_one_bone2d_node = stack->skeleton->get_path_to(stack->skeleton->get_bone(p_bone_idx));
+ } else {
+ WARN_PRINT("TwoBoneIK: Cannot verify the joint bone index for joint one...");
+ joint_one_bone_idx = p_bone_idx;
+ }
+ } else {
+ WARN_PRINT("TwoBoneIK: Cannot verify the joint bone index for joint one...");
+ joint_one_bone_idx = p_bone_idx;
+ }
+
+ notify_property_list_changed();
+}
+
+int SkeletonModification2DTwoBoneIK::get_joint_one_bone_idx() const {
+ return joint_one_bone_idx;
+}
+
+void SkeletonModification2DTwoBoneIK::set_joint_two_bone_idx(int p_bone_idx) {
+ ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!");
+
+ if (is_setup) {
+ if (stack->skeleton) {
+ ERR_FAIL_INDEX_MSG(p_bone_idx, stack->skeleton->get_bone_count(), "Passed-in Bone index is out of range!");
+ joint_two_bone_idx = p_bone_idx;
+ joint_two_bone2d_node_cache = stack->skeleton->get_bone(p_bone_idx)->get_instance_id();
+ joint_two_bone2d_node = stack->skeleton->get_path_to(stack->skeleton->get_bone(p_bone_idx));
+ } else {
+ WARN_PRINT("TwoBoneIK: Cannot verify the joint bone index for joint two...");
+ joint_two_bone_idx = p_bone_idx;
+ }
+ } else {
+ WARN_PRINT("TwoBoneIK: Cannot verify the joint bone index for joint two...");
+ joint_two_bone_idx = p_bone_idx;
+ }
+
+ notify_property_list_changed();
+}
+
+int SkeletonModification2DTwoBoneIK::get_joint_two_bone_idx() const {
+ return joint_two_bone_idx;
+}
+
+#ifdef TOOLS_ENABLED
+void SkeletonModification2DTwoBoneIK::set_editor_draw_min_max(bool p_draw) {
+ editor_draw_min_max = p_draw;
+}
+
+bool SkeletonModification2DTwoBoneIK::get_editor_draw_min_max() const {
+ return editor_draw_min_max;
+}
+#endif // TOOLS_ENABLED
+
+void SkeletonModification2DTwoBoneIK::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification2DTwoBoneIK::set_target_node);
+ ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification2DTwoBoneIK::get_target_node);
+
+ ClassDB::bind_method(D_METHOD("set_target_minimum_distance", "minimum_distance"), &SkeletonModification2DTwoBoneIK::set_target_minimum_distance);
+ ClassDB::bind_method(D_METHOD("get_target_minimum_distance"), &SkeletonModification2DTwoBoneIK::get_target_minimum_distance);
+ ClassDB::bind_method(D_METHOD("set_target_maximum_distance", "maximum_distance"), &SkeletonModification2DTwoBoneIK::set_target_maximum_distance);
+ ClassDB::bind_method(D_METHOD("get_target_maximum_distance"), &SkeletonModification2DTwoBoneIK::get_target_maximum_distance);
+ ClassDB::bind_method(D_METHOD("set_flip_bend_direction", "flip_direction"), &SkeletonModification2DTwoBoneIK::set_flip_bend_direction);
+ ClassDB::bind_method(D_METHOD("get_flip_bend_direction"), &SkeletonModification2DTwoBoneIK::get_flip_bend_direction);
+
+ ClassDB::bind_method(D_METHOD("set_joint_one_bone2d_node", "bone2d_node"), &SkeletonModification2DTwoBoneIK::set_joint_one_bone2d_node);
+ ClassDB::bind_method(D_METHOD("get_joint_one_bone2d_node"), &SkeletonModification2DTwoBoneIK::get_joint_one_bone2d_node);
+ ClassDB::bind_method(D_METHOD("set_joint_one_bone_idx", "bone_idx"), &SkeletonModification2DTwoBoneIK::set_joint_one_bone_idx);
+ ClassDB::bind_method(D_METHOD("get_joint_one_bone_idx"), &SkeletonModification2DTwoBoneIK::get_joint_one_bone_idx);
+
+ ClassDB::bind_method(D_METHOD("set_joint_two_bone2d_node", "bone2d_node"), &SkeletonModification2DTwoBoneIK::set_joint_two_bone2d_node);
+ ClassDB::bind_method(D_METHOD("get_joint_two_bone2d_node"), &SkeletonModification2DTwoBoneIK::get_joint_two_bone2d_node);
+ ClassDB::bind_method(D_METHOD("set_joint_two_bone_idx", "bone_idx"), &SkeletonModification2DTwoBoneIK::set_joint_two_bone_idx);
+ ClassDB::bind_method(D_METHOD("get_joint_two_bone_idx"), &SkeletonModification2DTwoBoneIK::get_joint_two_bone_idx);
+
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node2D"), "set_target_node", "get_target_node");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_minimum_distance", PROPERTY_HINT_RANGE, "0, 100000000, 0.01"), "set_target_minimum_distance", "get_target_minimum_distance");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_maximum_distance", PROPERTY_HINT_NONE, "0, 100000000, 0.01"), "set_target_maximum_distance", "get_target_maximum_distance");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_bend_direction", PROPERTY_HINT_NONE, ""), "set_flip_bend_direction", "get_flip_bend_direction");
+ ADD_GROUP("", "");
+}
+
+SkeletonModification2DTwoBoneIK::SkeletonModification2DTwoBoneIK() {
+ stack = nullptr;
+ is_setup = false;
+ enabled = true;
+ editor_draw_gizmo = true;
+}
+
+SkeletonModification2DTwoBoneIK::~SkeletonModification2DTwoBoneIK() {
+}
diff --git a/scene/resources/skeleton_modification_2d_twoboneik.h b/scene/resources/skeleton_modification_2d_twoboneik.h
new file mode 100644
index 0000000000..c7e545a488
--- /dev/null
+++ b/scene/resources/skeleton_modification_2d_twoboneik.h
@@ -0,0 +1,107 @@
+/*************************************************************************/
+/* skeleton_modification_2d_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. */
+/*************************************************************************/
+
+#ifndef SKELETONMODIFICATION2DTWOBONEIK_H
+#define SKELETONMODIFICATION2DTWOBONEIK_H
+
+#include "scene/2d/skeleton_2d.h"
+#include "scene/resources/skeleton_modification_2d.h"
+
+///////////////////////////////////////
+// SkeletonModification2DJIGGLE
+///////////////////////////////////////
+
+class SkeletonModification2DTwoBoneIK : public SkeletonModification2D {
+ GDCLASS(SkeletonModification2DTwoBoneIK, SkeletonModification2D);
+
+private:
+ NodePath target_node;
+ ObjectID target_node_cache;
+ float target_minimum_distance = 0;
+ float target_maximum_distance = 0;
+ bool flip_bend_direction = false;
+
+ NodePath joint_one_bone2d_node;
+ ObjectID joint_one_bone2d_node_cache;
+ int joint_one_bone_idx = -1;
+
+ NodePath joint_two_bone2d_node;
+ ObjectID joint_two_bone2d_node_cache;
+ int joint_two_bone_idx = -1;
+
+#ifdef TOOLS_ENABLED
+ bool editor_draw_min_max = false;
+#endif // TOOLS_ENABLED
+
+ void update_target_cache();
+ void update_joint_one_bone2d_cache();
+ void update_joint_two_bone2d_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:
+ void _execute(float p_delta) override;
+ void _setup_modification(SkeletonModificationStack2D *p_stack) override;
+ void _draw_editor_gizmo() override;
+
+ void set_target_node(const NodePath &p_target_node);
+ NodePath get_target_node() const;
+
+ void set_target_minimum_distance(float p_minimum_distance);
+ float get_target_minimum_distance() const;
+ void set_target_maximum_distance(float p_maximum_distance);
+ float get_target_maximum_distance() const;
+ void set_flip_bend_direction(bool p_flip_direction);
+ bool get_flip_bend_direction() const;
+
+ void set_joint_one_bone2d_node(const NodePath &p_node);
+ NodePath get_joint_one_bone2d_node() const;
+ void set_joint_one_bone_idx(int p_bone_idx);
+ int get_joint_one_bone_idx() const;
+
+ void set_joint_two_bone2d_node(const NodePath &p_node);
+ NodePath get_joint_two_bone2d_node() const;
+ void set_joint_two_bone_idx(int p_bone_idx);
+ int get_joint_two_bone_idx() const;
+
+#ifdef TOOLS_ENABLED
+ void set_editor_draw_min_max(bool p_draw);
+ bool get_editor_draw_min_max() const;
+#endif // TOOLS_ENABLED
+
+ SkeletonModification2DTwoBoneIK();
+ ~SkeletonModification2DTwoBoneIK();
+};
+
+#endif // SKELETONMODIFICATION2DTWOBONEIK_H
diff --git a/scene/resources/skeleton_modification_3d.cpp b/scene/resources/skeleton_modification_3d.cpp
new file mode 100644
index 0000000000..ee02ede2d5
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d.cpp
@@ -0,0 +1,162 @@
+/*************************************************************************/
+/* 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) {
+ GDVIRTUAL_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());
+ }
+
+ GDVIRTUAL_CALL(_setup_modification, Ref<SkeletonModificationStack3D>(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() {
+ GDVIRTUAL_BIND(_execute, "delta");
+ GDVIRTUAL_BIND(_setup_modification, "modification_stack")
+
+ 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..fb1f3d33d1
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d.h
@@ -0,0 +1,79 @@
+/*************************************************************************/
+/* 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);
+
+ GDVIRTUAL1(_execute, double)
+ GDVIRTUAL1(_setup_modification, Ref<SkeletonModificationStack3D>)
+
+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_2d.cpp b/scene/resources/skeleton_modification_stack_2d.cpp
new file mode 100644
index 0000000000..72c1c330ef
--- /dev/null
+++ b/scene/resources/skeleton_modification_stack_2d.cpp
@@ -0,0 +1,269 @@
+/*************************************************************************/
+/* skeleton_modification_stack_2d.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_2d.h"
+#include "scene/2d/skeleton_2d.h"
+
+void SkeletonModificationStack2D::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (int i = 0; i < modifications.size(); i++) {
+ p_list->push_back(
+ PropertyInfo(Variant::OBJECT, "modifications/" + itos(i),
+ PROPERTY_HINT_RESOURCE_TYPE,
+ "SkeletonModification2D",
+ PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
+ }
+}
+
+bool SkeletonModificationStack2D::_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 SkeletonModificationStack2D::_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 SkeletonModificationStack2D::setup() {
+ if (is_setup) {
+ return;
+ }
+
+ if (skeleton != nullptr) {
+ is_setup = true;
+ for (int i = 0; i < modifications.size(); i++) {
+ if (!modifications[i].is_valid()) {
+ continue;
+ }
+ modifications.get(i)->_setup_modification(this);
+ }
+
+#ifdef TOOLS_ENABLED
+ set_editor_gizmos_dirty(true);
+#endif // TOOLS_ENABLED
+
+ } else {
+ WARN_PRINT("Cannot setup SkeletonModificationStack2D: no Skeleton2D set!");
+ }
+}
+
+void SkeletonModificationStack2D::execute(float 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 (int i = 0; i < modifications.size(); i++) {
+ if (!modifications[i].is_valid()) {
+ continue;
+ }
+
+ if (modifications[i]->get_execution_mode() == p_execution_mode) {
+ modifications.get(i)->_execute(p_delta);
+ }
+ }
+}
+
+void SkeletonModificationStack2D::draw_editor_gizmos() {
+ if (!is_setup) {
+ return;
+ }
+
+ if (editor_gizmo_dirty) {
+ for (int i = 0; i < modifications.size(); i++) {
+ if (!modifications[i].is_valid()) {
+ continue;
+ }
+
+ if (modifications[i]->editor_draw_gizmo) {
+ modifications.get(i)->_draw_editor_gizmo();
+ }
+ }
+ skeleton->draw_set_transform(Vector2(0, 0));
+ editor_gizmo_dirty = false;
+ }
+}
+
+void SkeletonModificationStack2D::set_editor_gizmos_dirty(bool p_dirty) {
+ if (!is_setup) {
+ return;
+ }
+
+ if (!editor_gizmo_dirty && p_dirty) {
+ editor_gizmo_dirty = p_dirty;
+ if (skeleton) {
+ skeleton->update();
+ }
+ } else {
+ editor_gizmo_dirty = p_dirty;
+ }
+}
+
+void SkeletonModificationStack2D::enable_all_modifications(bool p_enabled) {
+ for (int i = 0; i < modifications.size(); i++) {
+ if (!modifications[i].is_valid()) {
+ continue;
+ }
+ modifications.get(i)->set_enabled(p_enabled);
+ }
+}
+
+Ref<SkeletonModification2D> SkeletonModificationStack2D::get_modification(int p_mod_idx) const {
+ ERR_FAIL_INDEX_V(p_mod_idx, modifications.size(), nullptr);
+ return modifications[p_mod_idx];
+}
+
+void SkeletonModificationStack2D::add_modification(Ref<SkeletonModification2D> p_mod) {
+ ERR_FAIL_COND(!p_mod.is_valid());
+
+ p_mod->_setup_modification(this);
+ modifications.push_back(p_mod);
+
+#ifdef TOOLS_ENABLED
+ set_editor_gizmos_dirty(true);
+#endif // TOOLS_ENABLED
+}
+
+void SkeletonModificationStack2D::delete_modification(int p_mod_idx) {
+ ERR_FAIL_INDEX(p_mod_idx, modifications.size());
+ modifications.remove(p_mod_idx);
+
+#ifdef TOOLS_ENABLED
+ set_editor_gizmos_dirty(true);
+#endif // TOOLS_ENABLED
+}
+
+void SkeletonModificationStack2D::set_modification(int p_mod_idx, Ref<SkeletonModification2D> p_mod) {
+ ERR_FAIL_INDEX(p_mod_idx, modifications.size());
+
+ if (p_mod == nullptr) {
+ modifications.insert(p_mod_idx, nullptr);
+ } else {
+ p_mod->_setup_modification(this);
+ modifications.insert(p_mod_idx, p_mod);
+ }
+
+#ifdef TOOLS_ENABLED
+ set_editor_gizmos_dirty(true);
+#endif // TOOLS_ENABLED
+}
+
+void SkeletonModificationStack2D::set_modification_count(int p_count) {
+ modifications.resize(p_count);
+ notify_property_list_changed();
+
+#ifdef TOOLS_ENABLED
+ set_editor_gizmos_dirty(true);
+#endif // TOOLS_ENABLED
+}
+
+int SkeletonModificationStack2D::get_modification_count() const {
+ return modifications.size();
+}
+
+void SkeletonModificationStack2D::set_skeleton(Skeleton2D *p_skeleton) {
+ skeleton = p_skeleton;
+}
+
+Skeleton2D *SkeletonModificationStack2D::get_skeleton() const {
+ return skeleton;
+}
+
+bool SkeletonModificationStack2D::get_is_setup() const {
+ return is_setup;
+}
+
+void SkeletonModificationStack2D::set_enabled(bool p_enabled) {
+ enabled = p_enabled;
+}
+
+bool SkeletonModificationStack2D::get_enabled() const {
+ return enabled;
+}
+
+void SkeletonModificationStack2D::set_strength(float 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;
+}
+
+float SkeletonModificationStack2D::get_strength() const {
+ return strength;
+}
+
+void SkeletonModificationStack2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("setup"), &SkeletonModificationStack2D::setup);
+ ClassDB::bind_method(D_METHOD("execute", "delta", "execution_mode"), &SkeletonModificationStack2D::execute);
+
+ ClassDB::bind_method(D_METHOD("enable_all_modifications", "enabled"), &SkeletonModificationStack2D::enable_all_modifications);
+ ClassDB::bind_method(D_METHOD("get_modification", "mod_idx"), &SkeletonModificationStack2D::get_modification);
+ ClassDB::bind_method(D_METHOD("add_modification", "modification"), &SkeletonModificationStack2D::add_modification);
+ ClassDB::bind_method(D_METHOD("delete_modification", "mod_idx"), &SkeletonModificationStack2D::delete_modification);
+ ClassDB::bind_method(D_METHOD("set_modification", "mod_idx", "modification"), &SkeletonModificationStack2D::set_modification);
+
+ ClassDB::bind_method(D_METHOD("set_modification_count"), &SkeletonModificationStack2D::set_modification_count);
+ ClassDB::bind_method(D_METHOD("get_modification_count"), &SkeletonModificationStack2D::get_modification_count);
+
+ ClassDB::bind_method(D_METHOD("get_is_setup"), &SkeletonModificationStack2D::get_is_setup);
+
+ ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &SkeletonModificationStack2D::set_enabled);
+ ClassDB::bind_method(D_METHOD("get_enabled"), &SkeletonModificationStack2D::get_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_strength", "strength"), &SkeletonModificationStack2D::set_strength);
+ ClassDB::bind_method(D_METHOD("get_strength"), &SkeletonModificationStack2D::get_strength);
+
+ ClassDB::bind_method(D_METHOD("get_skeleton"), &SkeletonModificationStack2D::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");
+}
+
+SkeletonModificationStack2D::SkeletonModificationStack2D() {
+}
diff --git a/scene/resources/skeleton_modification_stack_2d.h b/scene/resources/skeleton_modification_stack_2d.h
new file mode 100644
index 0000000000..58855701a1
--- /dev/null
+++ b/scene/resources/skeleton_modification_stack_2d.h
@@ -0,0 +1,99 @@
+/*************************************************************************/
+/* skeleton_modification_stack_2d.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 SKELETONMODIFICATIONSTACK2D_H
+#define SKELETONMODIFICATIONSTACK2D_H
+
+#include "scene/2d/skeleton_2d.h"
+#include "scene/resources/skeleton_modification_2d.h"
+
+///////////////////////////////////////
+// SkeletonModificationStack2D
+///////////////////////////////////////
+
+class Skeleton2D;
+class SkeletonModification2D;
+class Bone2D;
+
+class SkeletonModificationStack2D : public Resource {
+ GDCLASS(SkeletonModificationStack2D, Resource);
+ friend class Skeleton2D;
+ friend class SkeletonModification2D;
+
+protected:
+ static void _bind_methods();
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+ bool _set(const StringName &p_path, const Variant &p_value);
+ bool _get(const StringName &p_path, Variant &r_ret) const;
+
+public:
+ Skeleton2D *skeleton = nullptr;
+ bool is_setup = false;
+ bool enabled = false;
+ float strength = 1.0;
+
+ enum EXECUTION_MODE {
+ execution_mode_process,
+ execution_mode_physics_process
+ };
+
+ Vector<Ref<SkeletonModification2D>> modifications = Vector<Ref<SkeletonModification2D>>();
+
+ void setup();
+ void execute(float p_delta, int p_execution_mode);
+
+ bool editor_gizmo_dirty = false;
+ void draw_editor_gizmos();
+ void set_editor_gizmos_dirty(bool p_dirty);
+
+ void enable_all_modifications(bool p_enable);
+ Ref<SkeletonModification2D> get_modification(int p_mod_idx) const;
+ void add_modification(Ref<SkeletonModification2D> p_mod);
+ void delete_modification(int p_mod_idx);
+ void set_modification(int p_mod_idx, Ref<SkeletonModification2D> p_mod);
+
+ void set_modification_count(int p_count);
+ int get_modification_count() const;
+
+ void set_skeleton(Skeleton2D *p_skeleton);
+ Skeleton2D *get_skeleton() const;
+
+ bool get_is_setup() const;
+
+ void set_enabled(bool p_enabled);
+ bool get_enabled() const;
+
+ void set_strength(float p_strength);
+ float get_strength() const;
+
+ SkeletonModificationStack2D();
+};
+
+#endif // SKELETONMODIFICATION2D_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/skin.cpp b/scene/resources/skin.cpp
index e88841a531..710612ae05 100644
--- a/scene/resources/skin.cpp
+++ b/scene/resources/skin.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -38,14 +38,14 @@ void Skin::set_bind_count(int p_size) {
emit_changed();
}
-void Skin::add_bind(int p_bone, const Transform &p_pose) {
+void Skin::add_bind(int p_bone, const Transform3D &p_pose) {
uint32_t index = bind_count;
set_bind_count(bind_count + 1);
set_bind_bone(index, p_bone);
set_bind_pose(index, p_pose);
}
-void Skin::add_named_bind(const String &p_name, const Transform &p_pose) {
+void Skin::add_named_bind(const String &p_name, const Transform3D &p_pose) {
uint32_t index = bind_count;
set_bind_count(bind_count + 1);
set_bind_name(index, p_name);
@@ -58,7 +58,7 @@ void Skin::set_bind_name(int p_index, const StringName &p_name) {
binds_ptr[p_index].name = p_name;
emit_changed();
if (notify_change) {
- _change_notify();
+ notify_property_list_changed();
}
}
@@ -68,7 +68,7 @@ void Skin::set_bind_bone(int p_index, int p_bone) {
emit_changed();
}
-void Skin::set_bind_pose(int p_index, const Transform &p_pose) {
+void Skin::set_bind_pose(int p_index, const Transform3D &p_pose) {
ERR_FAIL_INDEX(p_index, bind_count);
binds_ptr[p_index].pose = p_pose;
emit_changed();
@@ -81,6 +81,10 @@ void Skin::clear_binds() {
emit_changed();
}
+void Skin::reset_state() {
+ clear_binds();
+}
+
bool Skin::_set(const StringName &p_name, const Variant &p_value) {
String name = p_name;
if (name == "bind_count") {
@@ -130,7 +134,7 @@ void Skin::_get_property_list(List<PropertyInfo> *p_list) const {
for (int i = 0; i < get_bind_count(); i++) {
p_list->push_back(PropertyInfo(Variant::STRING_NAME, "bind/" + itos(i) + "/name"));
p_list->push_back(PropertyInfo(Variant::INT, "bind/" + itos(i) + "/bone", PROPERTY_HINT_RANGE, "0,16384,1,or_greater", get_bind_name(i) != StringName() ? PROPERTY_USAGE_NOEDITOR : PROPERTY_USAGE_DEFAULT));
- p_list->push_back(PropertyInfo(Variant::TRANSFORM, "bind/" + itos(i) + "/pose"));
+ p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, "bind/" + itos(i) + "/pose"));
}
}
@@ -153,6 +157,4 @@ void Skin::_bind_methods() {
}
Skin::Skin() {
- bind_count = 0;
- binds_ptr = nullptr;
}
diff --git a/scene/resources/skin.h b/scene/resources/skin.h
index 57aaf1afd4..6857bf743a 100644
--- a/scene/resources/skin.h
+++ b/scene/resources/skin.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,7 +31,7 @@
#ifndef SKIN_H
#define SKIN_H
-#include "core/resource.h"
+#include "core/io/resource.h"
class Skin : public Resource {
GDCLASS(Skin, Resource)
@@ -39,30 +39,31 @@ class Skin : public Resource {
struct Bind {
int bone = -1;
StringName name;
- Transform pose;
+ Transform3D pose;
};
Vector<Bind> binds;
- Bind *binds_ptr;
- int bind_count;
+ Bind *binds_ptr = nullptr;
+ int bind_count = 0;
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
+ virtual void reset_state() override;
static void _bind_methods();
public:
void set_bind_count(int p_size);
inline int get_bind_count() const { return bind_count; }
- void add_bind(int p_bone, const Transform &p_pose);
- void add_named_bind(const String &p_name, const Transform &p_pose);
+ void add_bind(int p_bone, const Transform3D &p_pose);
+ void add_named_bind(const String &p_name, const Transform3D &p_pose);
void set_bind_bone(int p_index, int p_bone);
- void set_bind_pose(int p_index, const Transform &p_pose);
+ void set_bind_pose(int p_index, const Transform3D &p_pose);
void set_bind_name(int p_index, const StringName &p_name);
inline int get_bind_bone(int p_index) const {
@@ -79,9 +80,9 @@ public:
return binds_ptr[p_index].name;
}
- inline Transform get_bind_pose(int p_index) const {
+ inline Transform3D get_bind_pose(int p_index) const {
#ifdef DEBUG_ENABLED
- ERR_FAIL_INDEX_V(p_index, bind_count, Transform());
+ ERR_FAIL_INDEX_V(p_index, bind_count, Transform3D());
#endif
return binds_ptr[p_index].pose;
}
diff --git a/scene/resources/sky.cpp b/scene/resources/sky.cpp
index 7e32516f94..71424ba8ac 100644
--- a/scene/resources/sky.cpp
+++ b/scene/resources/sky.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -102,8 +102,6 @@ void Sky::_bind_methods() {
}
Sky::Sky() {
- mode = PROCESS_MODE_AUTOMATIC;
- radiance_size = RADIANCE_SIZE_256;
sky = RS::get_singleton()->sky_create();
}
diff --git a/scene/resources/sky.h b/scene/resources/sky.h
index 526ca94317..f0226d321d 100644
--- a/scene/resources/sky.h
+++ b/scene/resources/sky.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -59,8 +59,8 @@ public:
private:
RID sky;
- ProcessMode mode;
- RadianceSize radiance_size;
+ ProcessMode mode = PROCESS_MODE_AUTOMATIC;
+ RadianceSize radiance_size = RADIANCE_SIZE_256;
Ref<Material> sky_material;
protected:
diff --git a/scene/resources/sky_material.cpp b/scene/resources/sky_material.cpp
index 69e8e0b5bd..39082b6f7a 100644
--- a/scene/resources/sky_material.cpp
+++ b/scene/resources/sky_material.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,6 +30,11 @@
#include "sky_material.h"
+#include "core/version.h"
+
+Mutex ProceduralSkyMaterial::shader_mutex;
+RID ProceduralSkyMaterial::shader;
+
void ProceduralSkyMaterial::set_sky_top_color(const Color &p_sky_top) {
sky_top_color = p_sky_top;
RS::get_singleton()->material_set_param(_get_material(), "sky_top_color", sky_top_color.to_linear());
@@ -128,7 +133,17 @@ Shader::Mode ProceduralSkyMaterial::get_shader_mode() const {
return Shader::MODE_SKY;
}
+RID ProceduralSkyMaterial::get_rid() const {
+ _update_shader();
+ if (!shader_set) {
+ RS::get_singleton()->material_set_shader(_get_material(), shader);
+ shader_set = true;
+ }
+ return _get_material();
+}
+
RID ProceduralSkyMaterial::get_shader_rid() const {
+ _update_shader();
return shader;
}
@@ -180,73 +195,92 @@ void ProceduralSkyMaterial::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sun_curve", PROPERTY_HINT_EXP_EASING), "set_sun_curve", "get_sun_curve");
}
-ProceduralSkyMaterial::ProceduralSkyMaterial() {
- String code = "shader_type sky;\n\n";
-
- code += "uniform vec4 sky_top_color : hint_color = vec4(0.35, 0.46, 0.71, 1.0);\n";
- code += "uniform vec4 sky_horizon_color : hint_color = vec4(0.55, 0.69, 0.81, 1.0);\n";
- code += "uniform float sky_curve : hint_range(0, 1) = 0.09;\n";
- code += "uniform float sky_energy = 1.0;\n\n";
- code += "uniform vec4 ground_bottom_color : hint_color = vec4(0.12, 0.12, 0.13, 1.0);\n";
- code += "uniform vec4 ground_horizon_color : hint_color = vec4(0.37, 0.33, 0.31, 1.0);\n";
- code += "uniform float ground_curve : hint_range(0, 1) = 0.02;\n";
- code += "uniform float ground_energy = 1.0;\n\n";
- code += "uniform float sun_angle_max = 1.74;\n";
- code += "uniform float sun_curve : hint_range(0, 1) = 0.05;\n\n";
- code += "const float PI = 3.1415926535897932384626433833;\n\n";
- code += "void fragment() {\n";
- code += "\tfloat v_angle = acos(clamp(EYEDIR.y, -1.0, 1.0));\n";
- code += "\tfloat c = (1.0 - v_angle / (PI * 0.5));\n";
- code += "\tvec3 sky = mix(sky_horizon_color.rgb, sky_top_color.rgb, clamp(1.0 - pow(1.0 - c, 1.0 / sky_curve), 0.0, 1.0));\n";
- code += "\tsky *= sky_energy;\n";
- code += "\tif (LIGHT0_ENABLED) {\n";
- code += "\t\tfloat sun_angle = acos(dot(LIGHT0_DIRECTION, EYEDIR));\n";
- code += "\t\tif (sun_angle < LIGHT0_SIZE) {\n";
- code += "\t\t\tsky = LIGHT0_COLOR * LIGHT0_ENERGY;\n";
- code += "\t\t} else if (sun_angle < sun_angle_max) {\n";
- code += "\t\t\tfloat c2 = (sun_angle - LIGHT0_SIZE) / (sun_angle_max - LIGHT0_SIZE);\n";
- code += "\t\t\tsky = mix(LIGHT0_COLOR * LIGHT0_ENERGY, sky, clamp(1.0 - pow(1.0 - c2, 1.0 / sun_curve), 0.0, 1.0));\n";
- code += "\t\t}\n";
- code += "\t}\n";
- code += "\tif (LIGHT1_ENABLED) {\n";
- code += "\t\tfloat sun_angle = acos(dot(LIGHT1_DIRECTION, EYEDIR));\n";
- code += "\t\tif (sun_angle < LIGHT1_SIZE) {\n";
- code += "\t\t\tsky = LIGHT1_COLOR * LIGHT1_ENERGY;\n";
- code += "\t\t} else if (sun_angle < sun_angle_max) {\n";
- code += "\t\t\tfloat c2 = (sun_angle - LIGHT1_SIZE) / (sun_angle_max - LIGHT1_SIZE);\n";
- code += "\t\t\tsky = mix(LIGHT1_COLOR * LIGHT1_ENERGY, sky, clamp(1.0 - pow(1.0 - c2, 1.0 / sun_curve), 0.0, 1.0));\n";
- code += "\t\t}\n";
- code += "\t}\n";
- code += "\tif (LIGHT2_ENABLED) {\n";
- code += "\t\tfloat sun_angle = acos(dot(LIGHT2_DIRECTION, EYEDIR));\n";
- code += "\t\tif (sun_angle < LIGHT2_SIZE) {\n";
- code += "\t\t\tsky = LIGHT2_COLOR * LIGHT2_ENERGY;\n";
- code += "\t\t} else if (sun_angle < sun_angle_max) {\n";
- code += "\t\t\tfloat c2 = (sun_angle - LIGHT2_SIZE) / (sun_angle_max - LIGHT2_SIZE);\n";
- code += "\t\t\tsky = mix(LIGHT2_COLOR * LIGHT2_ENERGY, sky, clamp(1.0 - pow(1.0 - c2, 1.0 / sun_curve), 0.0, 1.0));\n";
- code += "\t\t}\n";
- code += "\t}\n";
- code += "\tif (LIGHT3_ENABLED) {\n";
- code += "\t\tfloat sun_angle = acos(dot(LIGHT3_DIRECTION, EYEDIR));\n";
- code += "\t\tif (sun_angle < LIGHT3_SIZE) {\n";
- code += "\t\t\tsky = LIGHT3_COLOR * LIGHT3_ENERGY;\n";
- code += "\t\t} else if (sun_angle < sun_angle_max) {\n";
- code += "\t\t\tfloat c2 = (sun_angle - LIGHT3_SIZE) / (sun_angle_max - LIGHT3_SIZE);\n";
- code += "\t\t\tsky = mix(LIGHT3_COLOR * LIGHT3_ENERGY, sky, clamp(1.0 - pow(1.0 - c2, 1.0 / sun_curve), 0.0, 1.0));\n";
- code += "\t\t}\n";
- code += "\t}\n";
- code += "\tc = (v_angle - (PI * 0.5)) / (PI * 0.5);\n";
- code += "\tvec3 ground = mix(ground_horizon_color.rgb, ground_bottom_color.rgb, clamp(1.0 - pow(1.0 - c, 1.0 / ground_curve), 0.0, 1.0));\n";
- code += "\tground *= ground_energy;\n";
- code += "\tCOLOR = mix(ground, sky, step(0.0, EYEDIR.y));\n";
- code += "}\n";
-
- shader = RS::get_singleton()->shader_create();
-
- RS::get_singleton()->shader_set_code(shader, code);
-
- RS::get_singleton()->material_set_shader(_get_material(), shader);
+void ProceduralSkyMaterial::cleanup_shader() {
+ if (shader.is_valid()) {
+ RS::get_singleton()->free(shader);
+ }
+}
+
+void ProceduralSkyMaterial::_update_shader() {
+ shader_mutex.lock();
+ 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);
+uniform vec4 sky_horizon_color : hint_color = vec4(0.55, 0.69, 0.81, 1.0);
+uniform float sky_curve : hint_range(0, 1) = 0.09;
+uniform float sky_energy = 1.0;
+uniform vec4 ground_bottom_color : hint_color = vec4(0.12, 0.12, 0.13, 1.0);
+uniform vec4 ground_horizon_color : hint_color = vec4(0.37, 0.33, 0.31, 1.0);
+uniform float ground_curve : hint_range(0, 1) = 0.02;
+uniform float ground_energy = 1.0;
+uniform float sun_angle_max = 1.74;
+uniform float sun_curve : hint_range(0, 1) = 0.05;
+
+void sky() {
+ float v_angle = acos(clamp(EYEDIR.y, -1.0, 1.0));
+ float c = (1.0 - v_angle / (PI * 0.5));
+ vec3 sky = mix(sky_horizon_color.rgb, sky_top_color.rgb, clamp(1.0 - pow(1.0 - c, 1.0 / sky_curve), 0.0, 1.0));
+ sky *= sky_energy;
+
+ if (LIGHT0_ENABLED) {
+ float sun_angle = acos(dot(LIGHT0_DIRECTION, EYEDIR));
+ if (sun_angle < LIGHT0_SIZE) {
+ sky = LIGHT0_COLOR * LIGHT0_ENERGY;
+ } else if (sun_angle < sun_angle_max) {
+ float c2 = (sun_angle - LIGHT0_SIZE) / (sun_angle_max - LIGHT0_SIZE);
+ sky = mix(LIGHT0_COLOR * LIGHT0_ENERGY, sky, clamp(1.0 - pow(1.0 - c2, 1.0 / sun_curve), 0.0, 1.0));
+ }
+ }
+
+ if (LIGHT1_ENABLED) {
+ float sun_angle = acos(dot(LIGHT1_DIRECTION, EYEDIR));
+ if (sun_angle < LIGHT1_SIZE) {
+ sky = LIGHT1_COLOR * LIGHT1_ENERGY;
+ } else if (sun_angle < sun_angle_max) {
+ float c2 = (sun_angle - LIGHT1_SIZE) / (sun_angle_max - LIGHT1_SIZE);
+ sky = mix(LIGHT1_COLOR * LIGHT1_ENERGY, sky, clamp(1.0 - pow(1.0 - c2, 1.0 / sun_curve), 0.0, 1.0));
+ }
+ }
+
+ if (LIGHT2_ENABLED) {
+ float sun_angle = acos(dot(LIGHT2_DIRECTION, EYEDIR));
+ if (sun_angle < LIGHT2_SIZE) {
+ sky = LIGHT2_COLOR * LIGHT2_ENERGY;
+ } else if (sun_angle < sun_angle_max) {
+ float c2 = (sun_angle - LIGHT2_SIZE) / (sun_angle_max - LIGHT2_SIZE);
+ sky = mix(LIGHT2_COLOR * LIGHT2_ENERGY, sky, clamp(1.0 - pow(1.0 - c2, 1.0 / sun_curve), 0.0, 1.0));
+ }
+ }
+
+ if (LIGHT3_ENABLED) {
+ float sun_angle = acos(dot(LIGHT3_DIRECTION, EYEDIR));
+ if (sun_angle < LIGHT3_SIZE) {
+ sky = LIGHT3_COLOR * LIGHT3_ENERGY;
+ } else if (sun_angle < sun_angle_max) {
+ float c2 = (sun_angle - LIGHT3_SIZE) / (sun_angle_max - LIGHT3_SIZE);
+ sky = mix(LIGHT3_COLOR * LIGHT3_ENERGY, sky, clamp(1.0 - pow(1.0 - c2, 1.0 / sun_curve), 0.0, 1.0));
+ }
+ }
+
+ c = (v_angle - (PI * 0.5)) / (PI * 0.5);
+ vec3 ground = mix(ground_horizon_color.rgb, ground_bottom_color.rgb, clamp(1.0 - pow(1.0 - c, 1.0 / ground_curve), 0.0, 1.0));
+ ground *= ground_energy;
+
+ COLOR = mix(ground, sky, step(0.0, EYEDIR.y));
+}
+)");
+ }
+ shader_mutex.unlock();
+}
+ProceduralSkyMaterial::ProceduralSkyMaterial() {
set_sky_top_color(Color(0.35, 0.46, 0.71));
set_sky_horizon_color(Color(0.55, 0.69, 0.81));
set_sky_curve(0.09);
@@ -262,7 +296,6 @@ ProceduralSkyMaterial::ProceduralSkyMaterial() {
}
ProceduralSkyMaterial::~ProceduralSkyMaterial() {
- RS::get_singleton()->free(shader);
RS::get_singleton()->material_set_shader(_get_material(), RID());
}
@@ -271,7 +304,8 @@ ProceduralSkyMaterial::~ProceduralSkyMaterial() {
void PanoramaSkyMaterial::set_panorama(const Ref<Texture2D> &p_panorama) {
panorama = p_panorama;
- RS::get_singleton()->material_set_param(_get_material(), "source_panorama", panorama);
+ RID tex_rid = p_panorama.is_valid() ? p_panorama->get_rid() : RID();
+ RS::get_singleton()->material_set_param(_get_material(), "source_panorama", tex_rid);
}
Ref<Texture2D> PanoramaSkyMaterial::get_panorama() const {
@@ -286,7 +320,17 @@ Shader::Mode PanoramaSkyMaterial::get_shader_mode() const {
return Shader::MODE_SKY;
}
+RID PanoramaSkyMaterial::get_rid() const {
+ _update_shader();
+ if (!shader_set) {
+ RS::get_singleton()->material_set_shader(_get_material(), shader);
+ shader_set = true;
+ }
+ return _get_material();
+}
+
RID PanoramaSkyMaterial::get_shader_rid() const {
+ _update_shader();
return shader;
}
@@ -297,23 +341,41 @@ void PanoramaSkyMaterial::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "panorama", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_panorama", "get_panorama");
}
-PanoramaSkyMaterial::PanoramaSkyMaterial() {
- String code = "shader_type sky;\n\n";
+Mutex PanoramaSkyMaterial::shader_mutex;
+RID PanoramaSkyMaterial::shader;
+
+void PanoramaSkyMaterial::cleanup_shader() {
+ if (shader.is_valid()) {
+ RS::get_singleton()->free(shader);
+ }
+}
+
+void PanoramaSkyMaterial::_update_shader() {
+ shader_mutex.lock();
+ 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;
- code += "uniform sampler2D source_panorama : filter_linear;\n";
- code += "void fragment() {\n";
- code += "\tCOLOR = texture(source_panorama, SKY_COORDS).rgb;\n";
- code += "}";
+uniform sampler2D source_panorama : filter_linear, hint_albedo;
- shader = RS::get_singleton()->shader_create();
+void sky() {
+ COLOR = texture(source_panorama, SKY_COORDS).rgb;
+}
+)");
+ }
- RS::get_singleton()->shader_set_code(shader, code);
+ shader_mutex.unlock();
+}
- RS::get_singleton()->material_set_shader(_get_material(), shader);
+PanoramaSkyMaterial::PanoramaSkyMaterial() {
}
PanoramaSkyMaterial::~PanoramaSkyMaterial() {
- RS::get_singleton()->free(shader);
RS::get_singleton()->material_set_shader(_get_material(), RID());
}
@@ -412,7 +474,8 @@ float PhysicalSkyMaterial::get_dither_strength() const {
void PhysicalSkyMaterial::set_night_sky(const Ref<Texture2D> &p_night_sky) {
night_sky = p_night_sky;
- RS::get_singleton()->material_set_param(_get_material(), "night_sky", night_sky);
+ RID tex_rid = p_night_sky.is_valid() ? p_night_sky->get_rid() : RID();
+ RS::get_singleton()->material_set_param(_get_material(), "night_sky", tex_rid);
}
Ref<Texture2D> PhysicalSkyMaterial::get_night_sky() const {
@@ -427,10 +490,23 @@ Shader::Mode PhysicalSkyMaterial::get_shader_mode() const {
return Shader::MODE_SKY;
}
+RID PhysicalSkyMaterial::get_rid() const {
+ _update_shader();
+ if (!shader_set) {
+ RS::get_singleton()->material_set_shader(_get_material(), shader);
+ shader_set = true;
+ }
+ return _get_material();
+}
+
RID PhysicalSkyMaterial::get_shader_rid() const {
+ _update_shader();
return shader;
}
+Mutex PhysicalSkyMaterial::shader_mutex;
+RID PhysicalSkyMaterial::shader;
+
void PhysicalSkyMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_rayleigh_coefficient", "rayleigh"), &PhysicalSkyMaterial::set_rayleigh_coefficient);
ClassDB::bind_method(D_METHOD("get_rayleigh_coefficient"), &PhysicalSkyMaterial::get_rayleigh_coefficient);
@@ -482,101 +558,120 @@ void PhysicalSkyMaterial::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "night_sky", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_night_sky", "get_night_sky");
}
-PhysicalSkyMaterial::PhysicalSkyMaterial() {
- String code = "shader_type sky;\n\n";
+void PhysicalSkyMaterial::cleanup_shader() {
+ if (shader.is_valid()) {
+ RS::get_singleton()->free(shader);
+ }
+}
- code += "uniform float rayleigh : hint_range(0, 64) = 2.0;\n";
- code += "uniform vec4 rayleigh_color : hint_color = vec4(0.056, 0.14, 0.3, 1.0);\n";
- code += "uniform float mie : hint_range(0, 1) = 0.005;\n";
- code += "uniform float mie_eccentricity : hint_range(-1, 1) = 0.8;\n";
- code += "uniform vec4 mie_color : hint_color = vec4(0.36, 0.56, 0.82, 1.0);\n\n";
+void PhysicalSkyMaterial::_update_shader() {
+ shader_mutex.lock();
+ if (shader.is_null()) {
+ shader = RS::get_singleton()->shader_create();
- code += "uniform float turbidity : hint_range(0, 1000) = 10.0;\n";
- code += "uniform float sun_disk_scale : hint_range(0, 360) = 1.0;\n";
- code += "uniform vec4 ground_color : hint_color = vec4(1.0);\n";
- code += "uniform float exposure : hint_range(0, 128) = 0.1;\n";
- code += "uniform float dither_strength : hint_range(0, 10) = 1.0;\n\n";
+ // 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.
- code += "uniform sampler2D night_sky : hint_black;";
+shader_type sky;
- code += "const float PI = 3.141592653589793238462643383279502884197169;\n";
- code += "const vec3 UP = vec3( 0.0, 1.0, 0.0 );\n\n";
+uniform float rayleigh : hint_range(0, 64) = 2.0;
+uniform vec4 rayleigh_color : hint_color = vec4(0.056, 0.14, 0.3, 1.0);
+uniform float mie : hint_range(0, 1) = 0.005;
+uniform float mie_eccentricity : hint_range(-1, 1) = 0.8;
+uniform vec4 mie_color : hint_color = vec4(0.36, 0.56, 0.82, 1.0);
- code += "// Sun constants\n";
- code += "const float SUN_ENERGY = 1000.0;\n\n";
+uniform float turbidity : hint_range(0, 1000) = 10.0;
+uniform float sun_disk_scale : hint_range(0, 360) = 1.0;
+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;
- code += "// optical length at zenith for molecules\n";
- code += "const float rayleigh_zenith_size = 8.4e3;\n";
- code += "const float mie_zenith_size = 1.25e3;\n\n";
+uniform sampler2D night_sky : hint_black_albedo;
- code += "float henyey_greenstein(float cos_theta, float g) {\n";
- code += "\tconst float k = 0.0795774715459;\n";
- code += "\treturn k * (1.0 - g * g) / (pow(1.0 + g * g - 2.0 * g * cos_theta, 1.5));\n";
- code += "}\n\n";
+const vec3 UP = vec3( 0.0, 1.0, 0.0 );
- code += "// From: https://www.shadertoy.com/view/4sfGzS credit to iq\n";
- code += "float hash(vec3 p) {\n";
- code += "\tp = fract( p * 0.3183099 + 0.1 );\n";
- code += "\tp *= 17.0;\n";
- code += "\treturn fract(p.x * p.y * p.z * (p.x + p.y + p.z));\n";
- code += "}\n\n";
+// Sun constants
+const float SUN_ENERGY = 1000.0;
- code += "void fragment() {\n";
- code += "\tfloat zenith_angle = clamp( dot(UP, normalize(LIGHT0_DIRECTION)), -1.0, 1.0 );\n";
- code += "\tfloat sun_energy = max(0.0, 1.0 - exp(-((PI * 0.5) - acos(zenith_angle)))) * SUN_ENERGY * LIGHT0_ENERGY;\n";
- code += "\tfloat sun_fade = 1.0 - clamp(1.0 - exp(LIGHT0_DIRECTION.y), 0.0, 1.0);\n\n";
+// Optical length at zenith for molecules.
+const float rayleigh_zenith_size = 8.4e3;
+const float mie_zenith_size = 1.25e3;
- code += "\t// rayleigh coefficients\n";
- code += "\tfloat rayleigh_coefficient = rayleigh - ( 1.0 * ( 1.0 - sun_fade ) );\n";
- code += "\tvec3 rayleigh_beta = rayleigh_coefficient * rayleigh_color.rgb * 0.0001;\n";
- code += "\t// mie coefficients from Preetham\n";
- code += "\tvec3 mie_beta = turbidity * mie * mie_color.rgb * 0.000434;\n\n";
+float henyey_greenstein(float cos_theta, float g) {
+ const float k = 0.0795774715459;
+ return k * (1.0 - g * g) / (pow(1.0 + g * g - 2.0 * g * cos_theta, 1.5));
+}
- code += "\t// optical length\n";
- code += "\tfloat zenith = acos(max(0.0, dot(UP, EYEDIR)));\n";
- code += "\tfloat optical_mass = 1.0 / (cos(zenith) + 0.15 * pow(93.885 - degrees(zenith), -1.253));\n";
- code += "\tfloat rayleigh_scatter = rayleigh_zenith_size * optical_mass;\n";
- code += "\tfloat mie_scatter = mie_zenith_size * optical_mass;\n\n";
+// From: https://www.shadertoy.com/view/4sfGzS credit to iq
+float hash(vec3 p) {
+ p = fract( p * 0.3183099 + 0.1 );
+ p *= 17.0;
+ return fract(p.x * p.y * p.z * (p.x + p.y + p.z));
+}
- code += "\t// light extinction based on thickness of atmosphere\n";
- code += "\tvec3 extinction = exp(-(rayleigh_beta * rayleigh_scatter + mie_beta * mie_scatter));\n\n";
+void sky() {
+ if (LIGHT0_ENABLED) {
+ float zenith_angle = clamp( dot(UP, normalize(LIGHT0_DIRECTION)), -1.0, 1.0 );
+ float sun_energy = max(0.0, 1.0 - exp(-((PI * 0.5) - acos(zenith_angle)))) * SUN_ENERGY * LIGHT0_ENERGY;
+ float sun_fade = 1.0 - clamp(1.0 - exp(LIGHT0_DIRECTION.y), 0.0, 1.0);
- code += "\t// in scattering\n";
- code += "\tfloat cos_theta = dot(EYEDIR, normalize(LIGHT0_DIRECTION));\n\n";
+ // Rayleigh coefficients.
+ float rayleigh_coefficient = rayleigh - ( 1.0 * ( 1.0 - sun_fade ) );
+ vec3 rayleigh_beta = rayleigh_coefficient * rayleigh_color.rgb * 0.0001;
+ // mie coefficients from Preetham
+ vec3 mie_beta = turbidity * mie * mie_color.rgb * 0.000434;
- code += "\tfloat rayleigh_phase = (3.0 / (16.0 * PI)) * (1.0 + pow(cos_theta * 0.5 + 0.5, 2.0));\n";
- code += "\tvec3 betaRTheta = rayleigh_beta * rayleigh_phase;\n\n";
+ // Optical length.
+ float zenith = acos(max(0.0, dot(UP, EYEDIR)));
+ float optical_mass = 1.0 / (cos(zenith) + 0.15 * pow(93.885 - degrees(zenith), -1.253));
+ float rayleigh_scatter = rayleigh_zenith_size * optical_mass;
+ float mie_scatter = mie_zenith_size * optical_mass;
- code += "\tfloat mie_phase = henyey_greenstein(cos_theta, mie_eccentricity);\n";
- code += "\tvec3 betaMTheta = mie_beta * mie_phase;\n\n";
+ // Light extinction based on thickness of atmosphere.
+ vec3 extinction = exp(-(rayleigh_beta * rayleigh_scatter + mie_beta * mie_scatter));
- code += "\tvec3 Lin = pow(sun_energy * ((betaRTheta + betaMTheta) / (rayleigh_beta + mie_beta)) * (1.0 - extinction), vec3(1.5));\n";
- code += "\t// Hack from https://github.com/mrdoob/three.js/blob/master/examples/jsm/objects/Sky.js\n";
- code += "\tLin *= mix(vec3(1.0), pow(sun_energy * ((betaRTheta + betaMTheta) / (rayleigh_beta + mie_beta)) * extinction, vec3(0.5)), clamp(pow(1.0 - zenith_angle, 5.0), 0.0, 1.0));\n\n";
+ // In scattering.
+ float cos_theta = dot(EYEDIR, normalize(LIGHT0_DIRECTION));
- code += "\t// Hack in the ground color\n";
- code += "\tLin *= mix(ground_color.rgb, vec3(1.0), smoothstep(-0.1, 0.1, dot(UP, EYEDIR)));\n\n";
+ float rayleigh_phase = (3.0 / (16.0 * PI)) * (1.0 + pow(cos_theta * 0.5 + 0.5, 2.0));
+ vec3 betaRTheta = rayleigh_beta * rayleigh_phase;
- code += "\t// Solar disk and out-scattering\n";
- code += "\tfloat sunAngularDiameterCos = cos(LIGHT0_SIZE * sun_disk_scale);\n";
- code += "\tfloat sunAngularDiameterCos2 = cos(LIGHT0_SIZE * sun_disk_scale*0.5);\n";
- code += "\tfloat sundisk = smoothstep(sunAngularDiameterCos, sunAngularDiameterCos2, cos_theta);\n";
- code += "\tvec3 L0 = (sun_energy * 1900.0 * extinction) * sundisk * LIGHT0_COLOR;\n";
- code += "\tL0 += texture(night_sky, SKY_COORDS).xyz * extinction;\n\n";
+ float mie_phase = henyey_greenstein(cos_theta, mie_eccentricity);
+ vec3 betaMTheta = mie_beta * mie_phase;
- code += "\tvec3 color = (Lin + L0) * 0.04;\n";
- code += "\tCOLOR = pow(color, vec3(1.0 / (1.2 + (1.2 * sun_fade))));\n";
- code += "\tCOLOR *= exposure;\n";
- code += "\t// Make optional, eliminates banding\n";
- code += "\tCOLOR += (hash(EYEDIR * 1741.9782) * 0.08 - 0.04) * 0.008 * dither_strength;\n";
- code += "}\n";
+ vec3 Lin = pow(sun_energy * ((betaRTheta + betaMTheta) / (rayleigh_beta + mie_beta)) * (1.0 - extinction), vec3(1.5));
+ // Hack from https://github.com/mrdoob/three.js/blob/master/examples/jsm/objects/Sky.js
+ Lin *= mix(vec3(1.0), pow(sun_energy * ((betaRTheta + betaMTheta) / (rayleigh_beta + mie_beta)) * extinction, vec3(0.5)), clamp(pow(1.0 - zenith_angle, 5.0), 0.0, 1.0));
- shader = RS::get_singleton()->shader_create();
+ // Hack in the ground color.
+ Lin *= mix(ground_color.rgb, vec3(1.0), smoothstep(-0.1, 0.1, dot(UP, EYEDIR)));
- RS::get_singleton()->shader_set_code(shader, code);
+ // Solar disk and out-scattering.
+ float sunAngularDiameterCos = cos(LIGHT0_SIZE * sun_disk_scale);
+ float sunAngularDiameterCos2 = cos(LIGHT0_SIZE * sun_disk_scale*0.5);
+ float sundisk = smoothstep(sunAngularDiameterCos, sunAngularDiameterCos2, cos_theta);
+ vec3 L0 = (sun_energy * 1900.0 * extinction) * sundisk * LIGHT0_COLOR;
+ L0 += texture(night_sky, SKY_COORDS).xyz * extinction;
- RS::get_singleton()->material_set_shader(_get_material(), shader);
+ vec3 color = (Lin + L0) * 0.04;
+ COLOR = pow(color, vec3(1.0 / (1.2 + (1.2 * sun_fade))));
+ COLOR *= exposure;
+ // Make optional, eliminates banding.
+ COLOR += (hash(EYEDIR * 1741.9782) * 0.08 - 0.04) * 0.016 * dither_strength;
+ } else {
+ // There is no sun, so display night_sky and nothing else.
+ COLOR = texture(night_sky, SKY_COORDS).xyz * 0.04;
+ COLOR *= exposure;
+ }
+}
+)");
+ }
+ shader_mutex.unlock();
+}
+
+PhysicalSkyMaterial::PhysicalSkyMaterial() {
set_rayleigh_coefficient(2.0);
set_rayleigh_color(Color(0.056, 0.14, 0.3));
set_mie_coefficient(0.005);
@@ -590,6 +685,4 @@ PhysicalSkyMaterial::PhysicalSkyMaterial() {
}
PhysicalSkyMaterial::~PhysicalSkyMaterial() {
- RS::get_singleton()->free(shader);
- RS::get_singleton()->material_set_shader(_get_material(), RID());
}
diff --git a/scene/resources/sky_material.h b/scene/resources/sky_material.h
index 5e23020d66..63e730617b 100644
--- a/scene/resources/sky_material.h
+++ b/scene/resources/sky_material.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "core/rid.h"
+#include "core/templates/rid.h"
#include "scene/resources/material.h"
#ifndef SKY_MATERIAL_H
@@ -51,7 +51,10 @@ private:
float sun_angle_max;
float sun_curve;
- RID shader;
+ static Mutex shader_mutex;
+ static RID shader;
+ static void _update_shader();
+ mutable bool shader_set = false;
protected:
static void _bind_methods();
@@ -89,7 +92,10 @@ public:
float get_sun_curve() const;
virtual Shader::Mode get_shader_mode() const override;
- RID get_shader_rid() const;
+ virtual RID get_shader_rid() const override;
+ virtual RID get_rid() const override;
+
+ static void cleanup_shader();
ProceduralSkyMaterial();
~ProceduralSkyMaterial();
@@ -103,7 +109,11 @@ class PanoramaSkyMaterial : public Material {
private:
Ref<Texture2D> panorama;
- RID shader;
+
+ static Mutex shader_mutex;
+ static RID shader;
+ static void _update_shader();
+ mutable bool shader_set = false;
protected:
static void _bind_methods();
@@ -114,7 +124,10 @@ public:
Ref<Texture2D> get_panorama() const;
virtual Shader::Mode get_shader_mode() const override;
- RID get_shader_rid() const;
+ virtual RID get_shader_rid() const override;
+ virtual RID get_rid() const override;
+
+ static void cleanup_shader();
PanoramaSkyMaterial();
~PanoramaSkyMaterial();
@@ -127,7 +140,8 @@ class PhysicalSkyMaterial : public Material {
GDCLASS(PhysicalSkyMaterial, Material);
private:
- RID shader;
+ static Mutex shader_mutex;
+ static RID shader;
float rayleigh;
Color rayleigh_color;
@@ -140,6 +154,8 @@ private:
float exposure;
float dither_strength;
Ref<Texture2D> night_sky;
+ static void _update_shader();
+ mutable bool shader_set = false;
protected:
static void _bind_methods();
@@ -180,7 +196,10 @@ public:
Ref<Texture2D> get_night_sky() const;
virtual Shader::Mode get_shader_mode() const override;
- RID get_shader_rid() const;
+ virtual RID get_shader_rid() const override;
+
+ static void cleanup_shader();
+ virtual RID get_rid() const override;
PhysicalSkyMaterial();
~PhysicalSkyMaterial();
diff --git a/scene/resources/sphere_shape_3d.cpp b/scene/resources/sphere_shape_3d.cpp
index fd33387df6..e4b4398063 100644
--- a/scene/resources/sphere_shape_3d.cpp
+++ b/scene/resources/sphere_shape_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -66,7 +66,6 @@ void SphereShape3D::set_radius(float p_radius) {
radius = p_radius;
_update_shape();
notify_change_to_owners();
- _change_notify("radius");
}
float SphereShape3D::get_radius() const {
@@ -77,7 +76,7 @@ void SphereShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_radius", "radius"), &SphereShape3D::set_radius);
ClassDB::bind_method(D_METHOD("get_radius"), &SphereShape3D::get_radius);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0,4096,0.01"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0,4096,0.001"), "set_radius", "get_radius");
}
SphereShape3D::SphereShape3D() :
diff --git a/scene/resources/sphere_shape_3d.h b/scene/resources/sphere_shape_3d.h
index 5cad67aea5..eddd2a2132 100644
--- a/scene/resources/sphere_shape_3d.h
+++ b/scene/resources/sphere_shape_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/sprite_frames.cpp b/scene/resources/sprite_frames.cpp
new file mode 100644
index 0000000000..140c6f821f
--- /dev/null
+++ b/scene/resources/sprite_frames.cpp
@@ -0,0 +1,241 @@
+/*************************************************************************/
+/* sprite_frames.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 "sprite_frames.h"
+
+#include "scene/scene_string_names.h"
+
+void SpriteFrames::add_frame(const StringName &p_anim, const Ref<Texture2D> &p_frame, int p_at_pos) {
+ Map<StringName, Anim>::Element *E = animations.find(p_anim);
+ ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist.");
+
+ if (p_at_pos >= 0 && p_at_pos < E->get().frames.size()) {
+ E->get().frames.insert(p_at_pos, p_frame);
+ } else {
+ E->get().frames.push_back(p_frame);
+ }
+
+ emit_changed();
+}
+
+int SpriteFrames::get_frame_count(const StringName &p_anim) const {
+ const Map<StringName, Anim>::Element *E = animations.find(p_anim);
+ ERR_FAIL_COND_V_MSG(!E, 0, "Animation '" + String(p_anim) + "' doesn't exist.");
+
+ return E->get().frames.size();
+}
+
+void SpriteFrames::remove_frame(const StringName &p_anim, int p_idx) {
+ Map<StringName, Anim>::Element *E = animations.find(p_anim);
+ ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist.");
+
+ E->get().frames.remove(p_idx);
+ emit_changed();
+}
+
+void SpriteFrames::clear(const StringName &p_anim) {
+ Map<StringName, Anim>::Element *E = animations.find(p_anim);
+ ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist.");
+
+ E->get().frames.clear();
+ emit_changed();
+}
+
+void SpriteFrames::clear_all() {
+ animations.clear();
+ add_animation("default");
+}
+
+void SpriteFrames::add_animation(const StringName &p_anim) {
+ ERR_FAIL_COND_MSG(animations.has(p_anim), "SpriteFrames already has animation '" + p_anim + "'.");
+
+ animations[p_anim] = Anim();
+}
+
+bool SpriteFrames::has_animation(const StringName &p_anim) const {
+ return animations.has(p_anim);
+}
+
+void SpriteFrames::remove_animation(const StringName &p_anim) {
+ animations.erase(p_anim);
+}
+
+void SpriteFrames::rename_animation(const StringName &p_prev, const StringName &p_next) {
+ ERR_FAIL_COND_MSG(!animations.has(p_prev), "SpriteFrames doesn't have animation '" + String(p_prev) + "'.");
+ ERR_FAIL_COND_MSG(animations.has(p_next), "Animation '" + String(p_next) + "' already exists.");
+
+ Anim anim = animations[p_prev];
+ animations.erase(p_prev);
+ animations[p_next] = anim;
+}
+
+Vector<String> SpriteFrames::_get_animation_list() const {
+ Vector<String> ret;
+ List<StringName> al;
+ get_animation_list(&al);
+ for (const StringName &E : al) {
+ ret.push_back(E);
+ }
+
+ return ret;
+}
+
+void SpriteFrames::get_animation_list(List<StringName> *r_animations) const {
+ for (const Map<StringName, Anim>::Element *E = animations.front(); E; E = E->next()) {
+ r_animations->push_back(E->key());
+ }
+}
+
+Vector<String> SpriteFrames::get_animation_names() const {
+ Vector<String> names;
+ for (const Map<StringName, Anim>::Element *E = animations.front(); E; E = E->next()) {
+ names.push_back(E->key());
+ }
+ names.sort();
+ return names;
+}
+
+void SpriteFrames::set_animation_speed(const StringName &p_anim, double p_fps) {
+ ERR_FAIL_COND_MSG(p_fps < 0, "Animation speed cannot be negative (" + itos(p_fps) + ").");
+ Map<StringName, Anim>::Element *E = animations.find(p_anim);
+ ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist.");
+ E->get().speed = p_fps;
+}
+
+double SpriteFrames::get_animation_speed(const StringName &p_anim) const {
+ const Map<StringName, Anim>::Element *E = animations.find(p_anim);
+ ERR_FAIL_COND_V_MSG(!E, 0, "Animation '" + String(p_anim) + "' doesn't exist.");
+ return E->get().speed;
+}
+
+void SpriteFrames::set_animation_loop(const StringName &p_anim, bool p_loop) {
+ Map<StringName, Anim>::Element *E = animations.find(p_anim);
+ ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist.");
+ E->get().loop = p_loop;
+}
+
+bool SpriteFrames::get_animation_loop(const StringName &p_anim) const {
+ const Map<StringName, Anim>::Element *E = animations.find(p_anim);
+ ERR_FAIL_COND_V_MSG(!E, false, "Animation '" + String(p_anim) + "' doesn't exist.");
+ return E->get().loop;
+}
+
+void SpriteFrames::_set_frames(const Array &p_frames) {
+ clear_all();
+ Map<StringName, Anim>::Element *E = animations.find(SceneStringNames::get_singleton()->_default);
+ ERR_FAIL_COND(!E);
+
+ E->get().frames.resize(p_frames.size());
+ for (int i = 0; i < E->get().frames.size(); i++) {
+ E->get().frames.write[i] = p_frames[i];
+ }
+}
+
+Array SpriteFrames::_get_frames() const {
+ return Array();
+}
+
+Array SpriteFrames::_get_animations() const {
+ Array anims;
+ for (Map<StringName, Anim>::Element *E = animations.front(); E; E = E->next()) {
+ Dictionary d;
+ d["name"] = E->key();
+ d["speed"] = E->get().speed;
+ d["loop"] = E->get().loop;
+ Array frames;
+ for (int i = 0; i < E->get().frames.size(); i++) {
+ frames.push_back(E->get().frames[i]);
+ }
+ d["frames"] = frames;
+ anims.push_back(d);
+ }
+
+ return anims;
+}
+
+void SpriteFrames::_set_animations(const Array &p_animations) {
+ animations.clear();
+ for (int i = 0; i < p_animations.size(); i++) {
+ Dictionary d = p_animations[i];
+
+ ERR_CONTINUE(!d.has("name"));
+ ERR_CONTINUE(!d.has("speed"));
+ ERR_CONTINUE(!d.has("loop"));
+ ERR_CONTINUE(!d.has("frames"));
+
+ Anim anim;
+ anim.speed = d["speed"];
+ anim.loop = d["loop"];
+ Array frames = d["frames"];
+ for (int j = 0; j < frames.size(); j++) {
+ RES res = frames[j];
+ anim.frames.push_back(res);
+ }
+
+ animations[d["name"]] = anim;
+ }
+}
+
+void SpriteFrames::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("add_animation", "anim"), &SpriteFrames::add_animation);
+ ClassDB::bind_method(D_METHOD("has_animation", "anim"), &SpriteFrames::has_animation);
+ ClassDB::bind_method(D_METHOD("remove_animation", "anim"), &SpriteFrames::remove_animation);
+ ClassDB::bind_method(D_METHOD("rename_animation", "anim", "newname"), &SpriteFrames::rename_animation);
+
+ ClassDB::bind_method(D_METHOD("get_animation_names"), &SpriteFrames::get_animation_names);
+
+ ClassDB::bind_method(D_METHOD("set_animation_speed", "anim", "speed"), &SpriteFrames::set_animation_speed);
+ ClassDB::bind_method(D_METHOD("get_animation_speed", "anim"), &SpriteFrames::get_animation_speed);
+
+ ClassDB::bind_method(D_METHOD("set_animation_loop", "anim", "loop"), &SpriteFrames::set_animation_loop);
+ ClassDB::bind_method(D_METHOD("get_animation_loop", "anim"), &SpriteFrames::get_animation_loop);
+
+ ClassDB::bind_method(D_METHOD("add_frame", "anim", "frame", "at_position"), &SpriteFrames::add_frame, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("get_frame_count", "anim"), &SpriteFrames::get_frame_count);
+ ClassDB::bind_method(D_METHOD("get_frame", "anim", "idx"), &SpriteFrames::get_frame);
+ ClassDB::bind_method(D_METHOD("set_frame", "anim", "idx", "txt"), &SpriteFrames::set_frame);
+ ClassDB::bind_method(D_METHOD("remove_frame", "anim", "idx"), &SpriteFrames::remove_frame);
+ ClassDB::bind_method(D_METHOD("clear", "anim"), &SpriteFrames::clear);
+ ClassDB::bind_method(D_METHOD("clear_all"), &SpriteFrames::clear_all);
+
+ ClassDB::bind_method(D_METHOD("_set_frames"), &SpriteFrames::_set_frames);
+ ClassDB::bind_method(D_METHOD("_get_frames"), &SpriteFrames::_get_frames);
+
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "frames", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "_set_frames", "_get_frames"); //compatibility
+
+ ClassDB::bind_method(D_METHOD("_set_animations"), &SpriteFrames::_set_animations);
+ ClassDB::bind_method(D_METHOD("_get_animations"), &SpriteFrames::_get_animations);
+
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_animations", "_get_animations"); //compatibility
+}
+
+SpriteFrames::SpriteFrames() {
+ add_animation(SceneStringNames::get_singleton()->_default);
+}
diff --git a/scene/resources/sprite_frames.h b/scene/resources/sprite_frames.h
new file mode 100644
index 0000000000..fdfd6af5ab
--- /dev/null
+++ b/scene/resources/sprite_frames.h
@@ -0,0 +1,102 @@
+/*************************************************************************/
+/* sprite_frames.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 SPRITE_FRAMES_H
+#define SPRITE_FRAMES_H
+
+#include "scene/resources/texture.h"
+
+class SpriteFrames : public Resource {
+ GDCLASS(SpriteFrames, Resource);
+
+ struct Anim {
+ double speed = 5.0;
+ bool loop = true;
+ Vector<Ref<Texture2D>> frames;
+ };
+
+ Map<StringName, Anim> animations;
+
+ Array _get_frames() const;
+ void _set_frames(const Array &p_frames);
+
+ Array _get_animations() const;
+ void _set_animations(const Array &p_animations);
+
+ Vector<String> _get_animation_list() const;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void add_animation(const StringName &p_anim);
+ bool has_animation(const StringName &p_anim) const;
+ void remove_animation(const StringName &p_anim);
+ void rename_animation(const StringName &p_prev, const StringName &p_next);
+
+ void get_animation_list(List<StringName> *r_animations) const;
+ Vector<String> get_animation_names() const;
+
+ void set_animation_speed(const StringName &p_anim, double p_fps);
+ double get_animation_speed(const StringName &p_anim) const;
+
+ void set_animation_loop(const StringName &p_anim, bool p_loop);
+ bool get_animation_loop(const StringName &p_anim) const;
+
+ void add_frame(const StringName &p_anim, const Ref<Texture2D> &p_frame, int p_at_pos = -1);
+ int get_frame_count(const StringName &p_anim) const;
+ _FORCE_INLINE_ Ref<Texture2D> get_frame(const StringName &p_anim, int p_idx) const {
+ const Map<StringName, Anim>::Element *E = animations.find(p_anim);
+ ERR_FAIL_COND_V_MSG(!E, Ref<Texture2D>(), "Animation '" + String(p_anim) + "' doesn't exist.");
+ ERR_FAIL_COND_V(p_idx < 0, Ref<Texture2D>());
+ if (p_idx >= E->get().frames.size()) {
+ return Ref<Texture2D>();
+ }
+
+ return E->get().frames[p_idx];
+ }
+
+ void set_frame(const StringName &p_anim, int p_idx, const Ref<Texture2D> &p_frame) {
+ Map<StringName, Anim>::Element *E = animations.find(p_anim);
+ ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist.");
+ ERR_FAIL_COND(p_idx < 0);
+ if (p_idx >= E->get().frames.size()) {
+ return;
+ }
+ E->get().frames.write[p_idx] = p_frame;
+ }
+ void remove_frame(const StringName &p_anim, int p_idx);
+ void clear(const StringName &p_anim);
+ void clear_all();
+
+ SpriteFrames();
+};
+
+#endif // SPRITE_FRAMES_H
diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp
index cdb0ec5111..3381043d29 100644
--- a/scene/resources/style_box.cpp
+++ b/scene/resources/style_box.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -38,26 +38,26 @@ bool StyleBox::test_mask(const Point2 &p_point, const Rect2 &p_rect) const {
return true;
}
-void StyleBox::set_default_margin(Margin p_margin, float p_value) {
- ERR_FAIL_INDEX((int)p_margin, 4);
+void StyleBox::set_default_margin(Side p_side, float p_value) {
+ ERR_FAIL_INDEX((int)p_side, 4);
- margin[p_margin] = p_value;
+ margin[p_side] = p_value;
emit_changed();
}
-float StyleBox::get_default_margin(Margin p_margin) const {
- ERR_FAIL_INDEX_V((int)p_margin, 4, 0.0);
+float StyleBox::get_default_margin(Side p_side) const {
+ ERR_FAIL_INDEX_V((int)p_side, 4, 0.0);
- return margin[p_margin];
+ return margin[p_side];
}
-float StyleBox::get_margin(Margin p_margin) const {
- ERR_FAIL_INDEX_V((int)p_margin, 4, 0.0);
+float StyleBox::get_margin(Side p_side) const {
+ ERR_FAIL_INDEX_V((int)p_side, 4, 0.0);
- if (margin[p_margin] < 0) {
- return get_style_margin(p_margin);
+ if (margin[p_side] < 0) {
+ return get_style_margin(p_side);
} else {
- return margin[p_margin];
+ return margin[p_side];
}
}
@@ -66,11 +66,11 @@ CanvasItem *StyleBox::get_current_item_drawn() const {
}
Size2 StyleBox::get_minimum_size() const {
- return Size2(get_margin(MARGIN_LEFT) + get_margin(MARGIN_RIGHT), get_margin(MARGIN_TOP) + get_margin(MARGIN_BOTTOM));
+ return Size2(get_margin(SIDE_LEFT) + get_margin(SIDE_RIGHT), get_margin(SIDE_TOP) + get_margin(SIDE_BOTTOM));
}
Point2 StyleBox::get_offset() const {
- return Point2(get_margin(MARGIN_LEFT), get_margin(MARGIN_TOP));
+ return Point2(get_margin(SIDE_LEFT), get_margin(SIDE_TOP));
}
Size2 StyleBox::get_center_size() const {
@@ -87,9 +87,6 @@ void StyleBox::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_default_margin", "margin", "offset"), &StyleBox::set_default_margin);
ClassDB::bind_method(D_METHOD("get_default_margin", "margin"), &StyleBox::get_default_margin);
- //ClassDB::bind_method(D_METHOD("set_default_margin"),&StyleBox::set_default_margin);
- //ClassDB::bind_method(D_METHOD("get_default_margin"),&StyleBox::get_default_margin);
-
ClassDB::bind_method(D_METHOD("get_margin", "margin"), &StyleBox::get_margin);
ClassDB::bind_method(D_METHOD("get_minimum_size"), &StyleBox::get_minimum_size);
ClassDB::bind_method(D_METHOD("get_center_size"), &StyleBox::get_center_size);
@@ -99,10 +96,10 @@ void StyleBox::_bind_methods() {
ClassDB::bind_method(D_METHOD("draw", "canvas_item", "rect"), &StyleBox::draw);
ADD_GROUP("Content Margin", "content_margin_");
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_left", PROPERTY_HINT_RANGE, "-1,2048,1"), "set_default_margin", "get_default_margin", MARGIN_LEFT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_right", PROPERTY_HINT_RANGE, "-1,2048,1"), "set_default_margin", "get_default_margin", MARGIN_RIGHT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_top", PROPERTY_HINT_RANGE, "-1,2048,1"), "set_default_margin", "get_default_margin", MARGIN_TOP);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_bottom", PROPERTY_HINT_RANGE, "-1,2048,1"), "set_default_margin", "get_default_margin", MARGIN_BOTTOM);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_left", PROPERTY_HINT_RANGE, "-1,2048,1"), "set_default_margin", "get_default_margin", SIDE_LEFT);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_right", PROPERTY_HINT_RANGE, "-1,2048,1"), "set_default_margin", "get_default_margin", SIDE_RIGHT);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_top", PROPERTY_HINT_RANGE, "-1,2048,1"), "set_default_margin", "get_default_margin", SIDE_TOP);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_bottom", PROPERTY_HINT_RANGE, "-1,2048,1"), "set_default_margin", "get_default_margin", SIDE_BOTTOM);
}
StyleBox::StyleBox() {
@@ -121,55 +118,34 @@ void StyleBoxTexture::set_texture(Ref<Texture2D> p_texture) {
} else {
region_rect = Rect2(Point2(), texture->get_size());
}
- emit_signal("texture_changed");
emit_changed();
- _change_notify("texture");
}
Ref<Texture2D> StyleBoxTexture::get_texture() const {
return texture;
}
-void StyleBoxTexture::set_normal_map(Ref<Texture2D> p_normal_map) {
- if (normal_map == p_normal_map) {
- return;
- }
- normal_map = p_normal_map;
- emit_changed();
-}
+void StyleBoxTexture::set_margin_size(Side p_side, float p_size) {
+ ERR_FAIL_INDEX((int)p_side, 4);
-Ref<Texture2D> StyleBoxTexture::get_normal_map() const {
- return normal_map;
-}
-
-void StyleBoxTexture::set_margin_size(Margin p_margin, float p_size) {
- ERR_FAIL_INDEX((int)p_margin, 4);
-
- margin[p_margin] = p_size;
+ margin[p_side] = p_size;
emit_changed();
- static const char *margin_prop[4] = {
- "content_margin_left",
- "content_margin_top",
- "content_margin_right",
- "content_margin_bottom",
- };
- _change_notify(margin_prop[p_margin]);
}
-float StyleBoxTexture::get_margin_size(Margin p_margin) const {
- ERR_FAIL_INDEX_V((int)p_margin, 4, 0.0);
+float StyleBoxTexture::get_margin_size(Side p_side) const {
+ ERR_FAIL_INDEX_V((int)p_side, 4, 0.0);
- return margin[p_margin];
+ return margin[p_side];
}
-float StyleBoxTexture::get_style_margin(Margin p_margin) const {
- ERR_FAIL_INDEX_V((int)p_margin, 4, 0.0);
+float StyleBoxTexture::get_style_margin(Side p_side) const {
+ ERR_FAIL_INDEX_V((int)p_side, 4, 0.0);
- return margin[p_margin];
+ return margin[p_side];
}
Rect2 StyleBoxTexture::get_draw_rect(const Rect2 &p_rect) const {
- return p_rect.grow_individual(expand_margin[MARGIN_LEFT], expand_margin[MARGIN_TOP], expand_margin[MARGIN_RIGHT], expand_margin[MARGIN_BOTTOM]);
+ return p_rect.grow_individual(expand_margin[SIDE_LEFT], expand_margin[SIDE_TOP], expand_margin[SIDE_RIGHT], expand_margin[SIDE_BOTTOM]);
}
void StyleBoxTexture::draw(RID p_canvas_item, const Rect2 &p_rect) const {
@@ -182,17 +158,12 @@ void StyleBoxTexture::draw(RID p_canvas_item, const Rect2 &p_rect) const {
texture->get_rect_region(rect, src_rect, rect, src_rect);
- rect.position.x -= expand_margin[MARGIN_LEFT];
- rect.position.y -= expand_margin[MARGIN_TOP];
- rect.size.x += expand_margin[MARGIN_LEFT] + expand_margin[MARGIN_RIGHT];
- rect.size.y += expand_margin[MARGIN_TOP] + expand_margin[MARGIN_BOTTOM];
+ rect.position.x -= expand_margin[SIDE_LEFT];
+ rect.position.y -= expand_margin[SIDE_TOP];
+ rect.size.x += expand_margin[SIDE_LEFT] + expand_margin[SIDE_RIGHT];
+ rect.size.y += expand_margin[SIDE_TOP] + expand_margin[SIDE_BOTTOM];
- RID normal_rid;
- if (normal_map.is_valid()) {
- normal_rid = normal_map->get_rid();
- }
-
- RenderingServer::get_singleton()->canvas_item_add_nine_patch(p_canvas_item, rect, src_rect, texture->get_rid(), Vector2(margin[MARGIN_LEFT], margin[MARGIN_TOP]), Vector2(margin[MARGIN_RIGHT], margin[MARGIN_BOTTOM]), RS::NinePatchAxisMode(axis_h), RS::NinePatchAxisMode(axis_v), draw_center, modulate, normal_rid);
+ RenderingServer::get_singleton()->canvas_item_add_nine_patch(p_canvas_item, rect, src_rect, texture->get_rid(), Vector2(margin[SIDE_LEFT], margin[SIDE_TOP]), Vector2(margin[SIDE_RIGHT], margin[SIDE_BOTTOM]), RS::NinePatchAxisMode(axis_h), RS::NinePatchAxisMode(axis_v), draw_center, modulate);
}
void StyleBoxTexture::set_draw_center(bool p_enabled) {
@@ -212,17 +183,17 @@ Size2 StyleBoxTexture::get_center_size() const {
return region_rect.size - get_minimum_size();
}
-void StyleBoxTexture::set_expand_margin_size(Margin p_expand_margin, float p_size) {
- ERR_FAIL_INDEX((int)p_expand_margin, 4);
- expand_margin[p_expand_margin] = p_size;
+void StyleBoxTexture::set_expand_margin_size(Side p_side, float p_size) {
+ ERR_FAIL_INDEX((int)p_side, 4);
+ expand_margin[p_side] = p_size;
emit_changed();
}
void StyleBoxTexture::set_expand_margin_size_individual(float p_left, float p_top, float p_right, float p_bottom) {
- expand_margin[MARGIN_LEFT] = p_left;
- expand_margin[MARGIN_TOP] = p_top;
- expand_margin[MARGIN_RIGHT] = p_right;
- expand_margin[MARGIN_BOTTOM] = p_bottom;
+ expand_margin[SIDE_LEFT] = p_left;
+ expand_margin[SIDE_TOP] = p_top;
+ expand_margin[SIDE_RIGHT] = p_right;
+ expand_margin[SIDE_BOTTOM] = p_bottom;
emit_changed();
}
@@ -233,9 +204,9 @@ void StyleBoxTexture::set_expand_margin_size_all(float p_expand_margin_size) {
emit_changed();
}
-float StyleBoxTexture::get_expand_margin_size(Margin p_expand_margin) const {
- ERR_FAIL_INDEX_V((int)p_expand_margin, 4, 0);
- return expand_margin[p_expand_margin];
+float StyleBoxTexture::get_expand_margin_size(Side p_side) const {
+ ERR_FAIL_INDEX_V((int)p_side, 4, 0);
+ return expand_margin[p_side];
}
void StyleBoxTexture::set_region_rect(const Rect2 &p_region_rect) {
@@ -245,7 +216,6 @@ void StyleBoxTexture::set_region_rect(const Rect2 &p_region_rect) {
region_rect = p_region_rect;
emit_changed();
- _change_notify("region");
}
Rect2 StyleBoxTexture::get_region_rect() const {
@@ -288,9 +258,6 @@ void StyleBoxTexture::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_texture", "texture"), &StyleBoxTexture::set_texture);
ClassDB::bind_method(D_METHOD("get_texture"), &StyleBoxTexture::get_texture);
- ClassDB::bind_method(D_METHOD("set_normal_map", "normal_map"), &StyleBoxTexture::set_normal_map);
- ClassDB::bind_method(D_METHOD("get_normal_map"), &StyleBoxTexture::get_normal_map);
-
ClassDB::bind_method(D_METHOD("set_margin_size", "margin", "size"), &StyleBoxTexture::set_margin_size);
ClassDB::bind_method(D_METHOD("get_margin_size", "margin"), &StyleBoxTexture::get_margin_size);
@@ -314,21 +281,18 @@ void StyleBoxTexture::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_v_axis_stretch_mode", "mode"), &StyleBoxTexture::set_v_axis_stretch_mode);
ClassDB::bind_method(D_METHOD("get_v_axis_stretch_mode"), &StyleBoxTexture::get_v_axis_stretch_mode);
- ADD_SIGNAL(MethodInfo("texture_changed"));
-
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_normal_map", "get_normal_map");
ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region_rect"), "set_region_rect", "get_region_rect");
ADD_GROUP("Margin", "margin_");
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_left", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", MARGIN_LEFT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_right", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", MARGIN_RIGHT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_top", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", MARGIN_TOP);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", MARGIN_BOTTOM);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_left", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", SIDE_LEFT);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_right", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", SIDE_RIGHT);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_top", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", SIDE_TOP);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", SIDE_BOTTOM);
ADD_GROUP("Expand Margin", "expand_margin_");
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_left", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", MARGIN_LEFT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", MARGIN_RIGHT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_top", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", MARGIN_TOP);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", MARGIN_BOTTOM);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_left", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", SIDE_LEFT);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", SIDE_RIGHT);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_top", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", SIDE_TOP);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", SIDE_BOTTOM);
ADD_GROUP("Axis Stretch", "axis_stretch_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "axis_stretch_horizontal", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_h_axis_stretch_mode", "get_h_axis_stretch_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "axis_stretch_vertical", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_v_axis_stretch_mode", "get_v_axis_stretch_mode");
@@ -341,20 +305,9 @@ void StyleBoxTexture::_bind_methods() {
BIND_ENUM_CONSTANT(AXIS_STRETCH_MODE_TILE_FIT);
}
-StyleBoxTexture::StyleBoxTexture() {
- for (int i = 0; i < 4; i++) {
- margin[i] = 0;
- expand_margin[i] = 0;
- }
- draw_center = true;
- modulate = Color(1, 1, 1, 1);
-
- axis_h = AXIS_STRETCH_MODE_STRETCH;
- axis_v = AXIS_STRETCH_MODE_STRETCH;
-}
+StyleBoxTexture::StyleBoxTexture() {}
-StyleBoxTexture::~StyleBoxTexture() {
-}
+StyleBoxTexture::~StyleBoxTexture() {}
////////////////
@@ -388,15 +341,15 @@ int StyleBoxFlat::get_border_width_min() const {
return MIN(MIN(border_width[0], border_width[1]), MIN(border_width[2], border_width[3]));
}
-void StyleBoxFlat::set_border_width(Margin p_margin, int p_width) {
- ERR_FAIL_INDEX((int)p_margin, 4);
- border_width[p_margin] = p_width;
+void StyleBoxFlat::set_border_width(Side p_side, int p_width) {
+ ERR_FAIL_INDEX((int)p_side, 4);
+ border_width[p_side] = p_width;
emit_changed();
}
-int StyleBoxFlat::get_border_width(Margin p_margin) const {
- ERR_FAIL_INDEX_V((int)p_margin, 4, 0);
- return border_width[p_margin];
+int StyleBoxFlat::get_border_width(Side p_side) const {
+ ERR_FAIL_INDEX_V((int)p_side, 4, 0);
+ return border_width[p_side];
}
void StyleBoxFlat::set_border_blend(bool p_blend) {
@@ -416,25 +369,15 @@ void StyleBoxFlat::set_corner_radius_all(int radius) {
emit_changed();
}
-void StyleBoxFlat::set_corner_radius_individual(const int radius_top_left, const int radius_top_right, const int radius_botton_right, const int radius_bottom_left) {
+void StyleBoxFlat::set_corner_radius_individual(const int radius_top_left, const int radius_top_right, const int radius_bottom_right, const int radius_bottom_left) {
corner_radius[0] = radius_top_left;
corner_radius[1] = radius_top_right;
- corner_radius[2] = radius_botton_right;
+ corner_radius[2] = radius_bottom_right;
corner_radius[3] = radius_bottom_left;
emit_changed();
}
-int StyleBoxFlat::get_corner_radius_min() const {
- int smallest = corner_radius[0];
- for (int i = 1; i < 4; i++) {
- if (smallest > corner_radius[i]) {
- smallest = corner_radius[i];
- }
- }
- return smallest;
-}
-
void StyleBoxFlat::set_corner_radius(const Corner p_corner, const int radius) {
ERR_FAIL_INDEX((int)p_corner, 4);
corner_radius[p_corner] = radius;
@@ -446,17 +389,17 @@ int StyleBoxFlat::get_corner_radius(const Corner p_corner) const {
return corner_radius[p_corner];
}
-void StyleBoxFlat::set_expand_margin_size(Margin p_expand_margin, float p_size) {
- ERR_FAIL_INDEX((int)p_expand_margin, 4);
- expand_margin[p_expand_margin] = p_size;
+void StyleBoxFlat::set_expand_margin_size(Side p_side, float p_size) {
+ ERR_FAIL_INDEX((int)p_side, 4);
+ expand_margin[p_side] = p_size;
emit_changed();
}
void StyleBoxFlat::set_expand_margin_size_individual(float p_left, float p_top, float p_right, float p_bottom) {
- expand_margin[MARGIN_LEFT] = p_left;
- expand_margin[MARGIN_TOP] = p_top;
- expand_margin[MARGIN_RIGHT] = p_right;
- expand_margin[MARGIN_BOTTOM] = p_bottom;
+ expand_margin[SIDE_LEFT] = p_left;
+ expand_margin[SIDE_TOP] = p_top;
+ expand_margin[SIDE_RIGHT] = p_right;
+ expand_margin[SIDE_BOTTOM] = p_bottom;
emit_changed();
}
@@ -467,9 +410,9 @@ void StyleBoxFlat::set_expand_margin_size_all(float p_expand_margin_size) {
emit_changed();
}
-float StyleBoxFlat::get_expand_margin_size(Margin p_expand_margin) const {
- ERR_FAIL_INDEX_V((int)p_expand_margin, 4, 0.0);
- return expand_margin[p_expand_margin];
+float StyleBoxFlat::get_expand_margin_size(Side p_side) const {
+ ERR_FAIL_INDEX_V((int)p_side, 4, 0.0);
+ return expand_margin[p_side];
}
void StyleBoxFlat::set_draw_center(bool p_enabled) {
@@ -511,18 +454,19 @@ Point2 StyleBoxFlat::get_shadow_offset() const {
void StyleBoxFlat::set_anti_aliased(const bool &p_anti_aliased) {
anti_aliased = p_anti_aliased;
emit_changed();
+ notify_property_list_changed();
}
bool StyleBoxFlat::is_anti_aliased() const {
return anti_aliased;
}
-void StyleBoxFlat::set_aa_size(const int &p_aa_size) {
- aa_size = CLAMP(p_aa_size, 1, 5);
+void StyleBoxFlat::set_aa_size(const real_t p_aa_size) {
+ aa_size = CLAMP(p_aa_size, 0.01, 10);
emit_changed();
}
-int StyleBoxFlat::get_aa_size() const {
+real_t StyleBoxFlat::get_aa_size() const {
return aa_size;
}
@@ -539,31 +483,32 @@ Size2 StyleBoxFlat::get_center_size() const {
return Size2();
}
-inline void set_inner_corner_radius(const Rect2 style_rect, const Rect2 inner_rect, const int corner_radius[4], int *inner_corner_radius) {
- int border_left = inner_rect.position.x - style_rect.position.x;
- int border_top = inner_rect.position.y - style_rect.position.y;
- int border_right = style_rect.size.width - inner_rect.size.width - border_left;
- int border_bottom = style_rect.size.height - inner_rect.size.height - border_top;
+inline void set_inner_corner_radius(const Rect2 style_rect, const Rect2 inner_rect, const real_t corner_radius[4], real_t *inner_corner_radius) {
+ real_t border_left = inner_rect.position.x - style_rect.position.x;
+ real_t border_top = inner_rect.position.y - style_rect.position.y;
+ real_t border_right = style_rect.size.width - inner_rect.size.width - border_left;
+ real_t border_bottom = style_rect.size.height - inner_rect.size.height - border_top;
- int rad;
- //tl
+ real_t rad;
+
+ // Top left.
rad = MIN(border_top, border_left);
inner_corner_radius[0] = MAX(corner_radius[0] - rad, 0);
- //tr
+ // Top right;
rad = MIN(border_top, border_right);
inner_corner_radius[1] = MAX(corner_radius[1] - rad, 0);
- //br
+ // Bottom right.
rad = MIN(border_bottom, border_right);
inner_corner_radius[2] = MAX(corner_radius[2] - rad, 0);
- //bl
+ // Bottom left.
rad = MIN(border_bottom, border_left);
inner_corner_radius[3] = MAX(corner_radius[3] - rad, 0);
}
-inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color> &colors, const Rect2 &style_rect, const int corner_radius[4],
+inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color> &colors, const Rect2 &style_rect, const real_t corner_radius[4],
const Rect2 &ring_rect, const Rect2 &inner_rect, const Color &inner_color, const Color &outer_color, const int corner_detail, const bool fill_center = false) {
int vert_offset = verts.size();
if (!vert_offset) {
@@ -572,17 +517,17 @@ inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color
int adapted_corner_detail = (corner_radius[0] == 0 && corner_radius[1] == 0 && corner_radius[2] == 0 && corner_radius[3] == 0) ? 1 : corner_detail;
- int ring_corner_radius[4];
+ real_t ring_corner_radius[4];
set_inner_corner_radius(style_rect, ring_rect, corner_radius, ring_corner_radius);
- //corner radius center points
+ // Corner radius center points.
Vector<Point2> outer_points;
outer_points.push_back(ring_rect.position + Vector2(ring_corner_radius[0], ring_corner_radius[0])); //tl
outer_points.push_back(Point2(ring_rect.position.x + ring_rect.size.x - ring_corner_radius[1], ring_rect.position.y + ring_corner_radius[1])); //tr
outer_points.push_back(ring_rect.position + ring_rect.size - Vector2(ring_corner_radius[2], ring_corner_radius[2])); //br
outer_points.push_back(Point2(ring_rect.position.x + ring_corner_radius[3], ring_rect.position.y + ring_rect.size.y - ring_corner_radius[3])); //bl
- int inner_corner_radius[4];
+ real_t inner_corner_radius[4];
set_inner_corner_radius(style_rect, inner_rect, corner_radius, inner_corner_radius);
Vector<Point2> inner_points;
@@ -591,11 +536,11 @@ inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color
inner_points.push_back(inner_rect.position + inner_rect.size - Vector2(inner_corner_radius[2], inner_corner_radius[2])); //br
inner_points.push_back(Point2(inner_rect.position.x + inner_corner_radius[3], inner_rect.position.y + inner_rect.size.y - inner_corner_radius[3])); //bl
- //calculate the vert array
+ // Calculate the vertices.
for (int corner_index = 0; corner_index < 4; corner_index++) {
for (int detail = 0; detail <= adapted_corner_detail; detail++) {
for (int inner_outer = 0; inner_outer < 2; inner_outer++) {
- float radius;
+ real_t radius;
Color color;
Point2 corner_point;
if (inner_outer == 0) {
@@ -607,8 +552,8 @@ inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color
color = outer_color;
corner_point = outer_points[corner_index];
}
- float x = radius * (float)cos((double)corner_index * Math_PI / 2.0 + (double)detail / (double)adapted_corner_detail * Math_PI / 2.0 + Math_PI) + corner_point.x;
- float y = radius * (float)sin((double)corner_index * Math_PI / 2.0 + (double)detail / (double)adapted_corner_detail * Math_PI / 2.0 + Math_PI) + corner_point.y;
+ real_t x = radius * (real_t)cos((corner_index + detail / (double)adapted_corner_detail) * (Math_TAU / 4.0) + Math_PI) + corner_point.x;
+ real_t y = radius * (real_t)sin((corner_index + detail / (double)adapted_corner_detail) * (Math_TAU / 4.0) + Math_PI) + corner_point.y;
verts.push_back(Vector2(x, y));
colors.push_back(color);
}
@@ -617,7 +562,7 @@ inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color
int ring_vert_count = verts.size() - vert_offset;
- //fill the indices and the colors for the border
+ // Fill the indices and the colors for the border.
for (int i = 0; i < ring_vert_count; i++) {
indices.push_back(vert_offset + ((i + 0) % ring_vert_count));
indices.push_back(vert_offset + ((i + 2) % ring_vert_count));
@@ -625,14 +570,14 @@ inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color
}
if (fill_center) {
- //fill the indices and the colors for the center
+ //Fill the indices and the colors for the center.
for (int index = 0; index < ring_vert_count / 2; index += 2) {
int i = index;
- //poly 1
+ // Polygon 1.
indices.push_back(vert_offset + i);
indices.push_back(vert_offset + ring_vert_count - 4 - i);
indices.push_back(vert_offset + i + 2);
- //poly 2
+ // Polygon 2.
indices.push_back(vert_offset + i);
indices.push_back(vert_offset + ring_vert_count - 2 - i);
indices.push_back(vert_offset + ring_vert_count - 4 - i);
@@ -640,20 +585,20 @@ inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color
}
}
-inline void adapt_values(int p_index_a, int p_index_b, int *adapted_values, const int *p_values, const real_t p_width, const int p_max_a, const int p_max_b) {
+inline void adapt_values(int p_index_a, int p_index_b, real_t *adapted_values, const real_t *p_values, const real_t p_width, const real_t p_max_a, const real_t p_max_b) {
if (p_values[p_index_a] + p_values[p_index_b] > p_width) {
- float factor;
- int newValue;
+ real_t factor;
+ real_t new_value;
- factor = (float)p_width / (float)(p_values[p_index_a] + p_values[p_index_b]);
+ factor = (real_t)p_width / (real_t)(p_values[p_index_a] + p_values[p_index_b]);
- newValue = (int)(p_values[p_index_a] * factor);
- if (newValue < adapted_values[p_index_a]) {
- adapted_values[p_index_a] = newValue;
+ new_value = (p_values[p_index_a] * factor);
+ if (new_value < adapted_values[p_index_a]) {
+ adapted_values[p_index_a] = new_value;
}
- newValue = (int)(p_values[p_index_b] * factor);
- if (newValue < adapted_values[p_index_b]) {
- adapted_values[p_index_b] = newValue;
+ new_value = (p_values[p_index_b] * factor);
+ if (new_value < adapted_values[p_index_b]) {
+ adapted_values[p_index_b] = new_value;
}
} else {
adapted_values[p_index_a] = MIN(p_values[p_index_a], adapted_values[p_index_a]);
@@ -664,7 +609,7 @@ inline void adapt_values(int p_index_a, int p_index_b, int *adapted_values, cons
}
Rect2 StyleBoxFlat::get_draw_rect(const Rect2 &p_rect) const {
- Rect2 draw_rect = p_rect.grow_individual(expand_margin[MARGIN_LEFT], expand_margin[MARGIN_TOP], expand_margin[MARGIN_RIGHT], expand_margin[MARGIN_BOTTOM]);
+ Rect2 draw_rect = p_rect.grow_individual(expand_margin[SIDE_LEFT], expand_margin[SIDE_TOP], expand_margin[SIDE_RIGHT], expand_margin[SIDE_BOTTOM]);
if (shadow_size > 0) {
Rect2 shadow_rect = draw_rect.grow(shadow_size);
@@ -676,21 +621,19 @@ Rect2 StyleBoxFlat::get_draw_rect(const Rect2 &p_rect) const {
}
void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const {
- //PREPARATIONS
bool draw_border = (border_width[0] > 0) || (border_width[1] > 0) || (border_width[2] > 0) || (border_width[3] > 0);
bool draw_shadow = (shadow_size > 0);
if (!draw_border && !draw_center && !draw_shadow) {
return;
}
- Rect2 style_rect = p_rect.grow_individual(expand_margin[MARGIN_LEFT], expand_margin[MARGIN_TOP], expand_margin[MARGIN_RIGHT], expand_margin[MARGIN_BOTTOM]);
+ Rect2 style_rect = p_rect.grow_individual(expand_margin[SIDE_LEFT], expand_margin[SIDE_TOP], expand_margin[SIDE_RIGHT], expand_margin[SIDE_BOTTOM]);
if (Math::is_zero_approx(style_rect.size.width) || Math::is_zero_approx(style_rect.size.height)) {
return;
}
bool rounded_corners = (corner_radius[0] > 0) || (corner_radius[1] > 0) || (corner_radius[2] > 0) || (corner_radius[3] > 0);
bool aa_on = rounded_corners && anti_aliased;
- float aa_size_grow = 0.5 * ((float)aa_size + 1.0);
bool blend_on = blend_border && draw_border;
@@ -698,27 +641,27 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const {
Color border_color_blend = (draw_center ? bg_color : border_color_alpha);
Color border_color_inner = blend_on ? border_color_blend : border_color;
- //adapt borders (prevent weird overlapping/glitchy drawings)
- int width = MAX(style_rect.size.width, 0);
- int height = MAX(style_rect.size.height, 0);
- int adapted_border[4] = { INT_MAX, INT_MAX, INT_MAX, INT_MAX };
- adapt_values(MARGIN_TOP, MARGIN_BOTTOM, adapted_border, border_width, height, height, height);
- adapt_values(MARGIN_LEFT, MARGIN_RIGHT, adapted_border, border_width, width, width, width);
+ // Adapt borders (prevent weird overlapping/glitchy drawings).
+ real_t width = MAX(style_rect.size.width, 0);
+ real_t height = MAX(style_rect.size.height, 0);
+ real_t adapted_border[4] = { 1000000.0, 1000000.0, 1000000.0, 1000000.0 };
+ adapt_values(SIDE_TOP, SIDE_BOTTOM, adapted_border, border_width, height, height, height);
+ adapt_values(SIDE_LEFT, SIDE_RIGHT, adapted_border, border_width, width, width, width);
- //adapt corners (prevent weird overlapping/glitchy drawings)
- int adapted_corner[4] = { INT_MAX, INT_MAX, INT_MAX, INT_MAX };
- adapt_values(CORNER_TOP_RIGHT, CORNER_BOTTOM_RIGHT, adapted_corner, corner_radius, height, height - adapted_border[MARGIN_BOTTOM], height - adapted_border[MARGIN_TOP]);
- adapt_values(CORNER_TOP_LEFT, CORNER_BOTTOM_LEFT, adapted_corner, corner_radius, height, height - adapted_border[MARGIN_BOTTOM], height - adapted_border[MARGIN_TOP]);
- adapt_values(CORNER_TOP_LEFT, CORNER_TOP_RIGHT, adapted_corner, corner_radius, width, width - adapted_border[MARGIN_RIGHT], width - adapted_border[MARGIN_LEFT]);
- adapt_values(CORNER_BOTTOM_LEFT, CORNER_BOTTOM_RIGHT, adapted_corner, corner_radius, width, width - adapted_border[MARGIN_RIGHT], width - adapted_border[MARGIN_LEFT]);
+ // Adapt corners (prevent weird overlapping/glitchy drawings).
+ real_t adapted_corner[4] = { 1000000.0, 1000000.0, 1000000.0, 1000000.0 };
+ adapt_values(CORNER_TOP_RIGHT, CORNER_BOTTOM_RIGHT, adapted_corner, corner_radius, height, height - adapted_border[SIDE_BOTTOM], height - adapted_border[SIDE_TOP]);
+ adapt_values(CORNER_TOP_LEFT, CORNER_BOTTOM_LEFT, adapted_corner, corner_radius, height, height - adapted_border[SIDE_BOTTOM], height - adapted_border[SIDE_TOP]);
+ adapt_values(CORNER_TOP_LEFT, CORNER_TOP_RIGHT, adapted_corner, corner_radius, width, width - adapted_border[SIDE_RIGHT], width - adapted_border[SIDE_LEFT]);
+ adapt_values(CORNER_BOTTOM_LEFT, CORNER_BOTTOM_RIGHT, adapted_corner, corner_radius, width, width - adapted_border[SIDE_RIGHT], width - adapted_border[SIDE_LEFT]);
- Rect2 infill_rect = style_rect.grow_individual(-adapted_border[MARGIN_LEFT], -adapted_border[MARGIN_TOP], -adapted_border[MARGIN_RIGHT], -adapted_border[MARGIN_BOTTOM]);
+ Rect2 infill_rect = style_rect.grow_individual(-adapted_border[SIDE_LEFT], -adapted_border[SIDE_TOP], -adapted_border[SIDE_RIGHT], -adapted_border[SIDE_BOTTOM]);
Rect2 border_style_rect = style_rect;
if (aa_on) {
for (int i = 0; i < 4; i++) {
if (border_width[i] > 0) {
- border_style_rect = border_style_rect.grow_margin((Margin)i, -aa_size_grow);
+ border_style_rect = border_style_rect.grow_side((Side)i, -aa_size);
}
}
}
@@ -728,7 +671,7 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const {
Vector<Color> colors;
Vector<Point2> uvs;
- //DRAW SHADOW
+ // Create shadow
if (draw_shadow) {
Rect2 shadow_inner_rect = style_rect;
shadow_inner_rect.position += shadow_offset;
@@ -747,89 +690,108 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const {
}
}
- //DRAW border
- if (draw_border) {
+ // Create border (no AA).
+ if (draw_border && !aa_on) {
draw_ring(verts, indices, colors, border_style_rect, adapted_corner,
border_style_rect, infill_rect, border_color_inner, border_color, corner_detail);
}
- //DRAW INFILL
+ // Create infill (no AA).
if (draw_center && (!aa_on || blend_on || !draw_border)) {
draw_ring(verts, indices, colors, border_style_rect, adapted_corner,
infill_rect, infill_rect, bg_color, bg_color, corner_detail, true);
}
if (aa_on) {
- int aa_border_width[4];
- int aa_fill_width[4];
+ real_t aa_border_width[4];
+ real_t aa_fill_width[4];
if (draw_border) {
for (int i = 0; i < 4; i++) {
if (border_width[i] > 0) {
- aa_border_width[i] = aa_size_grow;
+ aa_border_width[i] = aa_size;
aa_fill_width[i] = 0;
} else {
aa_border_width[i] = 0;
- aa_fill_width[i] = aa_size_grow;
+ aa_fill_width[i] = aa_size;
}
}
} else {
for (int i = 0; i < 4; i++) {
aa_border_width[i] = 0;
- aa_fill_width[i] = aa_size_grow;
+ aa_fill_width[i] = aa_size;
}
}
- Rect2 infill_inner_rect = infill_rect.grow_individual(-aa_border_width[MARGIN_LEFT], -aa_border_width[MARGIN_TOP],
- -aa_border_width[MARGIN_RIGHT], -aa_border_width[MARGIN_BOTTOM]);
+ Rect2 infill_inner_rect = infill_rect.grow_individual(-aa_border_width[SIDE_LEFT], -aa_border_width[SIDE_TOP],
+ -aa_border_width[SIDE_RIGHT], -aa_border_width[SIDE_BOTTOM]);
if (draw_center) {
if (!blend_on && draw_border) {
- //DRAW INFILL WITHIN BORDER AA
+ Rect2 infill_inner_rect_aa = infill_inner_rect.grow_individual(aa_border_width[SIDE_LEFT], aa_border_width[SIDE_TOP],
+ aa_border_width[SIDE_RIGHT], aa_border_width[SIDE_BOTTOM]);
+ // Create infill within AA border.
draw_ring(verts, indices, colors, border_style_rect, adapted_corner,
- infill_inner_rect, infill_inner_rect, bg_color, bg_color, corner_detail, true);
+ infill_inner_rect_aa, infill_inner_rect_aa, bg_color, bg_color, corner_detail, true);
}
if (!blend_on || !draw_border) {
- Rect2 infill_aa_rect = infill_rect.grow_individual(aa_fill_width[MARGIN_LEFT], aa_fill_width[MARGIN_TOP],
- aa_fill_width[MARGIN_RIGHT], aa_fill_width[MARGIN_BOTTOM]);
+ Rect2 infill_rect_aa = infill_rect.grow_individual(aa_fill_width[SIDE_LEFT], aa_fill_width[SIDE_TOP],
+ aa_fill_width[SIDE_RIGHT], aa_fill_width[SIDE_BOTTOM]);
Color alpha_bg = Color(bg_color.r, bg_color.g, bg_color.b, 0);
- //INFILL AA
+ // Create infill fake AA gradient.
draw_ring(verts, indices, colors, style_rect, adapted_corner,
- infill_aa_rect, infill_rect, bg_color, alpha_bg, corner_detail);
+ infill_rect_aa, infill_rect, bg_color, alpha_bg, corner_detail);
}
}
if (draw_border) {
+ Rect2 infill_rect_aa = infill_rect.grow_individual(aa_border_width[SIDE_LEFT], aa_border_width[SIDE_TOP],
+ aa_border_width[SIDE_RIGHT], aa_border_width[SIDE_BOTTOM]);
+ Rect2 style_rect_aa = style_rect.grow_individual(aa_border_width[SIDE_LEFT], aa_border_width[SIDE_TOP],
+ aa_border_width[SIDE_RIGHT], aa_border_width[SIDE_BOTTOM]);
+ Rect2 border_style_rect_aa = border_style_rect.grow_individual(aa_border_width[SIDE_LEFT], aa_border_width[SIDE_TOP],
+ aa_border_width[SIDE_RIGHT], aa_border_width[SIDE_BOTTOM]);
+
+ // Create border.
+ draw_ring(verts, indices, colors, border_style_rect, adapted_corner,
+ border_style_rect_aa, ((blend_on) ? infill_rect : infill_rect_aa), border_color_inner, border_color, corner_detail);
+
if (!blend_on) {
- //DRAW INNER BORDER AA
+ // Create inner border fake AA gradient.
draw_ring(verts, indices, colors, border_style_rect, adapted_corner,
- infill_rect, infill_inner_rect, border_color_blend, border_color, corner_detail);
+ infill_rect_aa, infill_rect, border_color_blend, border_color, corner_detail);
}
- //DRAW OUTER BORDER AA
+ // Create outer border fake AA gradient.
draw_ring(verts, indices, colors, border_style_rect, adapted_corner,
- style_rect, border_style_rect, border_color, border_color_alpha, corner_detail);
+ style_rect_aa, border_style_rect_aa, border_color, border_color_alpha, corner_detail);
}
}
- //COMPUTE UV COORDINATES
- Rect2 uv_rect = style_rect.grow(aa_on ? aa_size_grow : 0);
+ // Compute UV coordinates.
+ Rect2 uv_rect = style_rect.grow(aa_on ? aa_size : 0);
uvs.resize(verts.size());
for (int i = 0; i < verts.size(); i++) {
uvs.write[i].x = (verts[i].x - uv_rect.position.x) / uv_rect.size.width;
uvs.write[i].y = (verts[i].y - uv_rect.position.y) / uv_rect.size.height;
}
- //DRAWING
+ // Draw stylebox.
RenderingServer *vs = RenderingServer::get_singleton();
vs->canvas_item_add_triangle_array(p_canvas_item, indices, verts, colors, uvs);
}
-float StyleBoxFlat::get_style_margin(Margin p_margin) const {
- ERR_FAIL_INDEX_V((int)p_margin, 4, 0.0);
- return border_width[p_margin];
+float StyleBoxFlat::get_style_margin(Side p_side) const {
+ ERR_FAIL_INDEX_V((int)p_side, 4, 0.0);
+ return border_width[p_side];
+}
+
+void StyleBoxFlat::_validate_property(PropertyInfo &property) const {
+ if (!anti_aliased && property.name == "anti_aliasing_size") {
+ property.usage = PROPERTY_USAGE_NOEDITOR;
+ }
}
void StyleBoxFlat::_bind_methods() {
@@ -885,10 +847,10 @@ void StyleBoxFlat::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_center"), "set_draw_center", "is_draw_center_enabled");
ADD_GROUP("Border Width", "border_width_");
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_left", PROPERTY_HINT_RANGE, "0,1024,1"), "set_border_width", "get_border_width", MARGIN_LEFT);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_top", PROPERTY_HINT_RANGE, "0,1024,1"), "set_border_width", "get_border_width", MARGIN_TOP);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_right", PROPERTY_HINT_RANGE, "0,1024,1"), "set_border_width", "get_border_width", MARGIN_RIGHT);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_bottom", PROPERTY_HINT_RANGE, "0,1024,1"), "set_border_width", "get_border_width", MARGIN_BOTTOM);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_left", PROPERTY_HINT_RANGE, "0,1024,1"), "set_border_width", "get_border_width", SIDE_LEFT);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_top", PROPERTY_HINT_RANGE, "0,1024,1"), "set_border_width", "get_border_width", SIDE_TOP);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_right", PROPERTY_HINT_RANGE, "0,1024,1"), "set_border_width", "get_border_width", SIDE_RIGHT);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_bottom", PROPERTY_HINT_RANGE, "0,1024,1"), "set_border_width", "get_border_width", SIDE_BOTTOM);
ADD_GROUP("Border", "border_");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "border_color"), "set_border_color", "get_border_color");
@@ -904,10 +866,10 @@ void StyleBoxFlat::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "corner_detail", PROPERTY_HINT_RANGE, "1,20,1"), "set_corner_detail", "get_corner_detail");
ADD_GROUP("Expand Margin", "expand_margin_");
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_left", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin", "get_expand_margin", MARGIN_LEFT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin", "get_expand_margin", MARGIN_RIGHT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_top", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin", "get_expand_margin", MARGIN_TOP);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin", "get_expand_margin", MARGIN_BOTTOM);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_left", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin", "get_expand_margin", SIDE_LEFT);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin", "get_expand_margin", SIDE_RIGHT);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_top", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin", "get_expand_margin", SIDE_TOP);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin", "get_expand_margin", SIDE_BOTTOM);
ADD_GROUP("Shadow", "shadow_");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "shadow_color"), "set_shadow_color", "get_shadow_color");
@@ -916,41 +878,12 @@ void StyleBoxFlat::_bind_methods() {
ADD_GROUP("Anti Aliasing", "anti_aliasing_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "anti_aliasing"), "set_anti_aliased", "is_anti_aliased");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "anti_aliasing_size", PROPERTY_HINT_RANGE, "1,5,1"), "set_aa_size", "get_aa_size");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "anti_aliasing_size", PROPERTY_HINT_RANGE, "0.01,10,0.001"), "set_aa_size", "get_aa_size");
}
-StyleBoxFlat::StyleBoxFlat() {
- bg_color = Color(0.6, 0.6, 0.6);
- shadow_color = Color(0, 0, 0, 0.6);
- border_color = Color(0.8, 0.8, 0.8);
-
- blend_border = false;
- draw_center = true;
- anti_aliased = true;
-
- shadow_size = 0;
- shadow_offset = Point2(0, 0);
- corner_detail = 8;
- aa_size = 1;
-
- border_width[0] = 0;
- border_width[1] = 0;
- border_width[2] = 0;
- border_width[3] = 0;
-
- expand_margin[0] = 0;
- expand_margin[1] = 0;
- expand_margin[2] = 0;
- expand_margin[3] = 0;
+StyleBoxFlat::StyleBoxFlat() {}
- corner_radius[0] = 0;
- corner_radius[1] = 0;
- corner_radius[2] = 0;
- corner_radius[3] = 0;
-}
-
-StyleBoxFlat::~StyleBoxFlat() {
-}
+StyleBoxFlat::~StyleBoxFlat() {}
void StyleBoxLine::set_color(const Color &p_color) {
color = p_color;
@@ -1016,9 +949,18 @@ void StyleBoxLine::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical");
}
-float StyleBoxLine::get_style_margin(Margin p_margin) const {
- ERR_FAIL_INDEX_V((int)p_margin, 4, thickness);
- return thickness;
+float StyleBoxLine::get_style_margin(Side p_side) const {
+ ERR_FAIL_INDEX_V((int)p_side, 4, 0);
+
+ if (vertical) {
+ if (p_side == SIDE_LEFT || p_side == SIDE_RIGHT) {
+ return thickness / 2.0;
+ }
+ } else if (p_side == SIDE_TOP || p_side == SIDE_BOTTOM) {
+ return thickness / 2.0;
+ }
+
+ return 0;
}
Size2 StyleBoxLine::get_center_size() const {
@@ -1042,12 +984,6 @@ void StyleBoxLine::draw(RID p_canvas_item, const Rect2 &p_rect) const {
vs->canvas_item_add_rect(p_canvas_item, r, color);
}
-StyleBoxLine::StyleBoxLine() {
- grow_begin = 1.0;
- grow_end = 1.0;
- thickness = 1;
- color = Color(0.0, 0.0, 0.0);
- vertical = false;
-}
+StyleBoxLine::StyleBoxLine() {}
StyleBoxLine::~StyleBoxLine() {}
diff --git a/scene/resources/style_box.h b/scene/resources/style_box.h
index 3d29e3bd0f..a6cd5b7fb7 100644
--- a/scene/resources/style_box.h
+++ b/scene/resources/style_box.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,7 +31,7 @@
#ifndef STYLE_BOX_H
#define STYLE_BOX_H
-#include "core/resource.h"
+#include "core/io/resource.h"
#include "scene/resources/texture.h"
#include "servers/rendering_server.h"
@@ -44,15 +44,15 @@ class StyleBox : public Resource {
float margin[4];
protected:
- virtual float get_style_margin(Margin p_margin) const = 0;
+ virtual float get_style_margin(Side p_side) const = 0;
static void _bind_methods();
public:
virtual bool test_mask(const Point2 &p_point, const Rect2 &p_rect) const;
- void set_default_margin(Margin p_margin, float p_value);
- float get_default_margin(Margin p_margin) const;
- float get_margin(Margin p_margin) const;
+ void set_default_margin(Side p_side, float p_value);
+ float get_default_margin(Side p_side) const;
+ float get_margin(Side p_side) const;
virtual Size2 get_center_size() const;
virtual Rect2 get_draw_rect(const Rect2 &p_rect) const;
@@ -68,7 +68,7 @@ public:
class StyleBoxEmpty : public StyleBox {
GDCLASS(StyleBoxEmpty, StyleBox);
- virtual float get_style_margin(Margin p_margin) const override { return 0; }
+ virtual float get_style_margin(Side p_side) const override { return 0; }
public:
virtual void draw(RID p_canvas_item, const Rect2 &p_rect) const override {}
@@ -86,28 +86,27 @@ public:
};
private:
- float expand_margin[4];
- float margin[4];
+ float expand_margin[4] = {};
+ float margin[4] = {};
Rect2 region_rect;
Ref<Texture2D> texture;
- Ref<Texture2D> normal_map;
- bool draw_center;
- Color modulate;
- AxisStretchMode axis_h;
- AxisStretchMode axis_v;
+ bool draw_center = true;
+ Color modulate = Color(1, 1, 1, 1);
+ AxisStretchMode axis_h = AXIS_STRETCH_MODE_STRETCH;
+ AxisStretchMode axis_v = AXIS_STRETCH_MODE_STRETCH;
protected:
- virtual float get_style_margin(Margin p_margin) const override;
+ virtual float get_style_margin(Side p_side) const override;
static void _bind_methods();
public:
- void set_expand_margin_size(Margin p_expand_margin, float p_size);
+ void set_expand_margin_size(Side p_expand_side, float p_size);
void set_expand_margin_size_all(float p_expand_margin_size);
void set_expand_margin_size_individual(float p_left, float p_top, float p_right, float p_bottom);
- float get_expand_margin_size(Margin p_expand_margin) const;
+ float get_expand_margin_size(Side p_expand_side) const;
- void set_margin_size(Margin p_margin, float p_size);
- float get_margin_size(Margin p_margin) const;
+ void set_margin_size(Side p_side, float p_size);
+ float get_margin_size(Side p_side) const;
void set_region_rect(const Rect2 &p_region_rect);
Rect2 get_region_rect() const;
@@ -115,9 +114,6 @@ public:
void set_texture(Ref<Texture2D> p_texture);
Ref<Texture2D> get_texture() const;
- void set_normal_map(Ref<Texture2D> p_normal_map);
- Ref<Texture2D> get_normal_map() const;
-
void set_draw_center(bool p_enabled);
bool is_draw_center_enabled() const;
virtual Size2 get_center_size() const override;
@@ -143,52 +139,46 @@ VARIANT_ENUM_CAST(StyleBoxTexture::AxisStretchMode)
class StyleBoxFlat : public StyleBox {
GDCLASS(StyleBoxFlat, StyleBox);
- Color bg_color;
- Color shadow_color;
- Color border_color;
+ Color bg_color = Color(0.6, 0.6, 0.6);
+ Color shadow_color = Color(0, 0, 0, 0.6);
+ Color border_color = Color(0.8, 0.8, 0.8);
- int border_width[4];
- int expand_margin[4];
- int corner_radius[4];
+ real_t border_width[4] = {};
+ real_t expand_margin[4] = {};
+ real_t corner_radius[4] = {};
- bool draw_center;
- bool blend_border;
- bool anti_aliased;
+ bool draw_center = true;
+ bool blend_border = false;
+ bool anti_aliased = true;
- int corner_detail;
- int shadow_size;
+ int corner_detail = 8;
+ int shadow_size = 0;
Point2 shadow_offset;
- int aa_size;
+ real_t aa_size = 0.625;
protected:
- virtual float get_style_margin(Margin p_margin) const override;
+ virtual float get_style_margin(Side p_side) const override;
static void _bind_methods();
+ void _validate_property(PropertyInfo &property) const override;
public:
- //Color
void set_bg_color(const Color &p_color);
Color get_bg_color() const;
- //Border Color
void set_border_color(const Color &p_color);
Color get_border_color() const;
- //BORDER
- //width
void set_border_width_all(int p_size);
int get_border_width_min() const;
- void set_border_width(Margin p_margin, int p_width);
- int get_border_width(Margin p_margin) const;
+ void set_border_width(Side p_side, int p_width);
+ int get_border_width(Side p_side) const;
- //blend
void set_border_blend(bool p_blend);
bool get_border_blend() const;
- //CORNER
void set_corner_radius_all(int radius);
- void set_corner_radius_individual(const int radius_top_left, const int radius_top_right, const int radius_botton_right, const int radius_bottom_left);
- int get_corner_radius_min() const;
+ void set_corner_radius_individual(const int radius_top_left, const int radius_top_right, const int radius_bottom_right, const int radius_bottom_left);
void set_corner_radius(Corner p_corner, const int radius);
int get_corner_radius(Corner p_corner) const;
@@ -196,17 +186,14 @@ public:
void set_corner_detail(const int &p_corner_detail);
int get_corner_detail() const;
- //EXPANDS
- void set_expand_margin_size(Margin p_expand_margin, float p_size);
+ void set_expand_margin_size(Side p_expand_side, float p_size);
void set_expand_margin_size_all(float p_expand_margin_size);
void set_expand_margin_size_individual(float p_left, float p_top, float p_right, float p_bottom);
- float get_expand_margin_size(Margin p_expand_margin) const;
+ float get_expand_margin_size(Side p_expand_side) const;
- //DRAW CENTER
void set_draw_center(bool p_enabled);
bool is_draw_center_enabled() const;
- //SHADOW
void set_shadow_color(const Color &p_color);
Color get_shadow_color() const;
@@ -216,12 +203,10 @@ public:
void set_shadow_offset(const Point2 &p_offset);
Point2 get_shadow_offset() const;
- //ANTI_ALIASING
void set_anti_aliased(const bool &p_anti_aliased);
bool is_anti_aliased() const;
- //tempAA
- void set_aa_size(const int &p_aa_size);
- int get_aa_size() const;
+ void set_aa_size(const real_t p_aa_size);
+ real_t get_aa_size() const;
virtual Size2 get_center_size() const override;
@@ -232,17 +217,17 @@ public:
~StyleBoxFlat();
};
-// just used to draw lines.
+// Just used to draw lines.
class StyleBoxLine : public StyleBox {
GDCLASS(StyleBoxLine, StyleBox);
Color color;
- int thickness;
- bool vertical;
- float grow_begin;
- float grow_end;
+ int thickness = 1;
+ bool vertical = false;
+ float grow_begin = 1.0;
+ float grow_end = 1.0;
protected:
- virtual float get_style_margin(Margin p_margin) const override;
+ virtual float get_style_margin(Side p_side) const override;
static void _bind_methods();
public:
diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp
index 1a2dcc84bb..875aa30824 100644
--- a/scene/resources/surface_tool.cpp
+++ b/scene/resources/surface_tool.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,11 +30,15 @@
#include "surface_tool.h"
-#include "core/method_bind_ext.gen.inc"
-
#define _VERTEX_SNAP 0.0001
#define EQ_VERTEX_DIST 0.00001
+SurfaceTool::OptimizeVertexCacheFunc SurfaceTool::optimize_vertex_cache_func = nullptr;
+SurfaceTool::SimplifyFunc SurfaceTool::simplify_func = nullptr;
+SurfaceTool::SimplifyWithAttribFunc SurfaceTool::simplify_with_attrib_func = nullptr;
+SurfaceTool::SimplifyScaleFunc SurfaceTool::simplify_scale_func = nullptr;
+SurfaceTool::SimplifySloppyFunc SurfaceTool::simplify_sloppy_func = nullptr;
+
bool SurfaceTool::Vertex::operator==(const Vertex &p_vertex) const {
if (vertex != p_vertex.vertex) {
return false;
@@ -76,6 +80,16 @@ bool SurfaceTool::Vertex::operator==(const Vertex &p_vertex) const {
}
}
+ for (int i = 0; i < RS::ARRAY_CUSTOM_COUNT; i++) {
+ if (custom[i] != p_vertex.custom[i]) {
+ return false;
+ }
+ }
+
+ if (smooth_group != p_vertex.smooth_group) {
+ return false;
+ }
+
return true;
}
@@ -89,6 +103,8 @@ uint32_t SurfaceTool::VertexHasher::hash(const Vertex &p_vtx) {
h = hash_djb2_buffer((const uint8_t *)&p_vtx.color, sizeof(real_t) * 4, h);
h = hash_djb2_buffer((const uint8_t *)p_vtx.bones.ptr(), p_vtx.bones.size() * sizeof(int), h);
h = hash_djb2_buffer((const uint8_t *)p_vtx.weights.ptr(), p_vtx.weights.size() * sizeof(float), h);
+ h = hash_djb2_buffer((const uint8_t *)&p_vtx.custom[0], sizeof(Color) * RS::ARRAY_CUSTOM_COUNT, h);
+ h = hash_djb2_one_32(p_vtx.smooth_group, h);
return h;
}
@@ -113,8 +129,13 @@ void SurfaceTool::add_vertex(const Vector3 &p_vertex) {
vtx.bones = last_bones;
vtx.tangent = last_tangent.normal;
vtx.binormal = last_normal.cross(last_tangent.normal).normalized() * last_tangent.d;
+ vtx.smooth_group = last_smooth_group;
+
+ for (int i = 0; i < RS::ARRAY_CUSTOM_COUNT; i++) {
+ vtx.custom[i] = last_custom[i];
+ }
- const int expected_vertices = 4;
+ const int expected_vertices = skin_weights == SKIN_8_WEIGHTS ? 8 : 4;
if ((format & Mesh::ARRAY_FORMAT_WEIGHTS || format & Mesh::ARRAY_FORMAT_BONES) && (vtx.weights.size() != expected_vertices || vtx.bones.size() != expected_vertices)) {
//ensure vertices are the expected amount
@@ -140,7 +161,7 @@ void SurfaceTool::add_vertex(const Vector3 &p_vertex) {
//cap
weights.resize(expected_vertices);
//renormalize
- float total = 0;
+ float total = 0.0;
for (int i = 0; i < expected_vertices; i++) {
total += weights[i].weight;
}
@@ -165,7 +186,7 @@ void SurfaceTool::add_vertex(const Vector3 &p_vertex) {
format |= Mesh::ARRAY_FORMAT_VERTEX;
}
-void SurfaceTool::add_color(Color p_color) {
+void SurfaceTool::set_color(Color p_color) {
ERR_FAIL_COND(!begun);
ERR_FAIL_COND(!first && !(format & Mesh::ARRAY_FORMAT_COLOR));
@@ -174,7 +195,7 @@ void SurfaceTool::add_color(Color p_color) {
last_color = p_color;
}
-void SurfaceTool::add_normal(const Vector3 &p_normal) {
+void SurfaceTool::set_normal(const Vector3 &p_normal) {
ERR_FAIL_COND(!begun);
ERR_FAIL_COND(!first && !(format & Mesh::ARRAY_FORMAT_NORMAL));
@@ -183,7 +204,7 @@ void SurfaceTool::add_normal(const Vector3 &p_normal) {
last_normal = p_normal;
}
-void SurfaceTool::add_tangent(const Plane &p_tangent) {
+void SurfaceTool::set_tangent(const Plane &p_tangent) {
ERR_FAIL_COND(!begun);
ERR_FAIL_COND(!first && !(format & Mesh::ARRAY_FORMAT_TANGENT));
@@ -191,7 +212,7 @@ void SurfaceTool::add_tangent(const Plane &p_tangent) {
last_tangent = p_tangent;
}
-void SurfaceTool::add_uv(const Vector2 &p_uv) {
+void SurfaceTool::set_uv(const Vector2 &p_uv) {
ERR_FAIL_COND(!begun);
ERR_FAIL_COND(!first && !(format & Mesh::ARRAY_FORMAT_TEX_UV));
@@ -199,7 +220,7 @@ void SurfaceTool::add_uv(const Vector2 &p_uv) {
last_uv = p_uv;
}
-void SurfaceTool::add_uv2(const Vector2 &p_uv2) {
+void SurfaceTool::set_uv2(const Vector2 &p_uv2) {
ERR_FAIL_COND(!begun);
ERR_FAIL_COND(!first && !(format & Mesh::ARRAY_FORMAT_TEX_UV2));
@@ -207,29 +228,45 @@ void SurfaceTool::add_uv2(const Vector2 &p_uv2) {
last_uv2 = p_uv2;
}
-void SurfaceTool::add_bones(const Vector<int> &p_bones) {
+void SurfaceTool::set_custom(int p_index, const Color &p_custom) {
+ ERR_FAIL_INDEX(p_index, RS::ARRAY_CUSTOM_COUNT);
+ ERR_FAIL_COND(!begun);
+ ERR_FAIL_COND(last_custom_format[p_index] == CUSTOM_MAX);
+ static const uint32_t mask[RS::ARRAY_CUSTOM_COUNT] = { Mesh::ARRAY_FORMAT_CUSTOM0, Mesh::ARRAY_FORMAT_CUSTOM1, Mesh::ARRAY_FORMAT_CUSTOM2, Mesh::ARRAY_FORMAT_CUSTOM3 };
+ static const uint32_t shift[RS::ARRAY_CUSTOM_COUNT] = { Mesh::ARRAY_FORMAT_CUSTOM0_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM1_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM2_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM3_SHIFT };
+ ERR_FAIL_COND(!first && !(format & mask[p_index]));
+
+ if (first) {
+ format |= mask[p_index];
+ format |= last_custom_format[p_index] << shift[p_index];
+ }
+ last_custom[p_index] = p_custom;
+}
+
+void SurfaceTool::set_bones(const Vector<int> &p_bones) {
ERR_FAIL_COND(!begun);
ERR_FAIL_COND(!first && !(format & Mesh::ARRAY_FORMAT_BONES));
format |= Mesh::ARRAY_FORMAT_BONES;
+ if (skin_weights == SKIN_8_WEIGHTS) {
+ format |= Mesh::ARRAY_FLAG_USE_8_BONE_WEIGHTS;
+ }
last_bones = p_bones;
}
-void SurfaceTool::add_weights(const Vector<float> &p_weights) {
+void SurfaceTool::set_weights(const Vector<float> &p_weights) {
ERR_FAIL_COND(!begun);
ERR_FAIL_COND(!first && !(format & Mesh::ARRAY_FORMAT_WEIGHTS));
format |= Mesh::ARRAY_FORMAT_WEIGHTS;
+ if (skin_weights == SKIN_8_WEIGHTS) {
+ format |= Mesh::ARRAY_FLAG_USE_8_BONE_WEIGHTS;
+ }
last_weights = p_weights;
}
-void SurfaceTool::add_smooth_group(bool p_smooth) {
- ERR_FAIL_COND(!begun);
- if (index_array.size()) {
- smooth_groups[index_array.size()] = p_smooth;
- } else {
- smooth_groups[vertex_array.size()] = p_smooth;
- }
+void SurfaceTool::set_smooth_group(uint32_t p_group) {
+ last_smooth_group = p_group;
}
void SurfaceTool::add_triangle_fan(const Vector<Vector3> &p_vertices, const Vector<Vector2> &p_uvs, const Vector<Color> &p_colors, const Vector<Vector2> &p_uv2s, const Vector<Vector3> &p_normals, const Vector<Plane> &p_tangents) {
@@ -240,15 +277,15 @@ void SurfaceTool::add_triangle_fan(const Vector<Vector3> &p_vertices, const Vect
#define ADD_POINT(n) \
{ \
if (p_colors.size() > n) \
- add_color(p_colors[n]); \
+ set_color(p_colors[n]); \
if (p_uvs.size() > n) \
- add_uv(p_uvs[n]); \
+ set_uv(p_uvs[n]); \
if (p_uv2s.size() > n) \
- add_uv2(p_uv2s[n]); \
+ set_uv2(p_uv2s[n]); \
if (p_normals.size() > n) \
- add_normal(p_normals[n]); \
+ set_normal(p_normals[n]); \
if (p_tangents.size() > n) \
- add_tangent(p_tangents[n]); \
+ set_tangent(p_tangents[n]); \
add_vertex(p_vertices[n]); \
}
@@ -263,6 +300,7 @@ void SurfaceTool::add_triangle_fan(const Vector<Vector3> &p_vertices, const Vect
void SurfaceTool::add_index(int p_index) {
ERR_FAIL_COND(!begun);
+ ERR_FAIL_COND(p_index < 0);
format |= Mesh::ARRAY_FORMAT_INDEX;
index_array.push_back(p_index);
@@ -286,9 +324,8 @@ Array SurfaceTool::commit_to_arrays() {
array.resize(varr_len);
Vector3 *w = array.ptrw();
- int idx = 0;
- for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx++) {
- const Vertex &v = E->get();
+ for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
+ const Vertex &v = vertex_array[idx];
switch (i) {
case Mesh::ARRAY_VERTEX: {
@@ -310,9 +347,8 @@ Array SurfaceTool::commit_to_arrays() {
array.resize(varr_len);
Vector2 *w = array.ptrw();
- int idx = 0;
- for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx++) {
- const Vertex &v = E->get();
+ for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
+ const Vertex &v = vertex_array[idx];
switch (i) {
case Mesh::ARRAY_TEX_UV: {
@@ -331,17 +367,16 @@ Array SurfaceTool::commit_to_arrays() {
array.resize(varr_len * 4);
float *w = array.ptrw();
- int idx = 0;
- for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx += 4) {
- const Vertex &v = E->get();
+ for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
+ const Vertex &v = vertex_array[idx];
- w[idx + 0] = v.tangent.x;
- w[idx + 1] = v.tangent.y;
- w[idx + 2] = v.tangent.z;
+ w[idx * 4 + 0] = v.tangent.x;
+ w[idx * 4 + 1] = v.tangent.y;
+ w[idx * 4 + 2] = v.tangent.z;
//float d = v.tangent.dot(v.binormal,v.normal);
float d = v.binormal.dot(v.normal.cross(v.tangent));
- w[idx + 3] = d < 0 ? -1 : 1;
+ w[idx * 4 + 3] = d < 0 ? -1 : 1;
}
a[i] = array;
@@ -352,27 +387,169 @@ Array SurfaceTool::commit_to_arrays() {
array.resize(varr_len);
Color *w = array.ptrw();
- int idx = 0;
- for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx++) {
- const Vertex &v = E->get();
+ for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
+ const Vertex &v = vertex_array[idx];
+
w[idx] = v.color;
}
a[i] = array;
} break;
+ case Mesh::ARRAY_CUSTOM0:
+ case Mesh::ARRAY_CUSTOM1:
+ case Mesh::ARRAY_CUSTOM2:
+ case Mesh::ARRAY_CUSTOM3: {
+ int fmt = i - Mesh::ARRAY_CUSTOM0;
+ switch (last_custom_format[fmt]) {
+ case CUSTOM_RGBA8_UNORM: {
+ Vector<uint8_t> array;
+ array.resize(varr_len * 4);
+ uint8_t *w = array.ptrw();
+
+ for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
+ const Vertex &v = vertex_array[idx];
+
+ const Color &c = v.custom[idx];
+ w[idx * 4 + 0] = CLAMP(int32_t(c.r * 255.0), 0, 255);
+ w[idx * 4 + 1] = CLAMP(int32_t(c.g * 255.0), 0, 255);
+ w[idx * 4 + 2] = CLAMP(int32_t(c.b * 255.0), 0, 255);
+ w[idx * 4 + 3] = CLAMP(int32_t(c.a * 255.0), 0, 255);
+ }
+
+ a[i] = array;
+ } break;
+ case CUSTOM_RGBA8_SNORM: {
+ Vector<uint8_t> array;
+ array.resize(varr_len * 4);
+ uint8_t *w = array.ptrw();
+
+ for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
+ const Vertex &v = vertex_array[idx];
+
+ const Color &c = v.custom[idx];
+ w[idx * 4 + 0] = uint8_t(int8_t(CLAMP(int32_t(c.r * 127.0), -128, 127)));
+ w[idx * 4 + 1] = uint8_t(int8_t(CLAMP(int32_t(c.g * 127.0), -128, 127)));
+ w[idx * 4 + 2] = uint8_t(int8_t(CLAMP(int32_t(c.b * 127.0), -128, 127)));
+ w[idx * 4 + 3] = uint8_t(int8_t(CLAMP(int32_t(c.a * 127.0), -128, 127)));
+ }
+
+ a[i] = array;
+ } break;
+ case CUSTOM_RG_HALF: {
+ Vector<uint8_t> array;
+ array.resize(varr_len * 4);
+ uint16_t *w = (uint16_t *)array.ptrw();
+
+ for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
+ const Vertex &v = vertex_array[idx];
+
+ const Color &c = v.custom[idx];
+ w[idx * 2 + 0] = Math::make_half_float(c.r);
+ w[idx * 2 + 1] = Math::make_half_float(c.g);
+ }
+
+ a[i] = array;
+ } break;
+ case CUSTOM_RGBA_HALF: {
+ Vector<uint8_t> array;
+ array.resize(varr_len * 8);
+ uint16_t *w = (uint16_t *)array.ptrw();
+
+ for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
+ const Vertex &v = vertex_array[idx];
+
+ const Color &c = v.custom[idx];
+ w[idx * 4 + 0] = Math::make_half_float(c.r);
+ w[idx * 4 + 1] = Math::make_half_float(c.g);
+ w[idx * 4 + 2] = Math::make_half_float(c.b);
+ w[idx * 4 + 3] = Math::make_half_float(c.a);
+ }
+
+ a[i] = array;
+ } break;
+ case CUSTOM_R_FLOAT: {
+ Vector<float> array;
+ array.resize(varr_len);
+ float *w = (float *)array.ptrw();
+
+ for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
+ const Vertex &v = vertex_array[idx];
+
+ const Color &c = v.custom[idx];
+ w[idx] = c.r;
+ }
+
+ a[i] = array;
+ } break;
+ case CUSTOM_RG_FLOAT: {
+ Vector<float> array;
+ array.resize(varr_len * 2);
+ float *w = (float *)array.ptrw();
+
+ for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
+ const Vertex &v = vertex_array[idx];
+
+ const Color &c = v.custom[idx];
+ w[idx * 2 + 0] = c.r;
+ w[idx * 2 + 1] = c.g;
+ }
+
+ a[i] = array;
+ } break;
+ case CUSTOM_RGB_FLOAT: {
+ Vector<float> array;
+ array.resize(varr_len * 3);
+ float *w = (float *)array.ptrw();
+
+ for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
+ const Vertex &v = vertex_array[idx];
+
+ const Color &c = v.custom[idx];
+ w[idx * 3 + 0] = c.r;
+ w[idx * 3 + 1] = c.g;
+ w[idx * 3 + 2] = c.b;
+ }
+
+ a[i] = array;
+ } break;
+ case CUSTOM_RGBA_FLOAT: {
+ Vector<float> array;
+ array.resize(varr_len * 4);
+ float *w = (float *)array.ptrw();
+
+ for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
+ const Vertex &v = vertex_array[idx];
+
+ const Color &c = v.custom[idx];
+ w[idx * 4 + 0] = c.r;
+ w[idx * 4 + 1] = c.g;
+ w[idx * 4 + 2] = c.b;
+ w[idx * 4 + 3] = c.a;
+ }
+
+ a[i] = array;
+ } break;
+ default: {
+ } //unreachable but compiler warning anyway
+ }
+ } break;
case Mesh::ARRAY_BONES: {
+ int count = skin_weights == SKIN_8_WEIGHTS ? 8 : 4;
Vector<int> array;
- array.resize(varr_len * 4);
+ array.resize(varr_len * count);
+ array.fill(0);
int *w = array.ptrw();
- int idx = 0;
- for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx += 4) {
- const Vertex &v = E->get();
+ for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
+ const Vertex &v = vertex_array[idx];
- ERR_CONTINUE(v.bones.size() != 4);
+ if (v.bones.size() > count) {
+ ERR_PRINT_ONCE(vformat("Invalid bones size %d vs count %d", v.bones.size(), count));
+ continue;
+ }
- for (int j = 0; j < 4; j++) {
- w[idx + j] = v.bones[j];
+ for (int j = 0; j < count; j++) {
+ w[idx * count + j] = v.bones[j];
}
}
@@ -381,16 +558,22 @@ Array SurfaceTool::commit_to_arrays() {
} break;
case Mesh::ARRAY_WEIGHTS: {
Vector<float> array;
- array.resize(varr_len * 4);
+ int count = skin_weights == SKIN_8_WEIGHTS ? 8 : 4;
+
+ array.resize(varr_len * count);
+ array.fill(0.0f);
float *w = array.ptrw();
- int idx = 0;
- for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx += 4) {
- const Vertex &v = E->get();
- ERR_CONTINUE(v.weights.size() != 4);
+ for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
+ const Vertex &v = vertex_array[idx];
+
+ if (v.weights.size() > count) {
+ ERR_PRINT_ONCE(vformat("Invalid weight size %d vs count %d", v.weights.size(), count));
+ continue;
+ }
- for (int j = 0; j < 4; j++) {
- w[idx + j] = v.weights[j];
+ for (int j = 0; j < count; j++) {
+ w[idx * count + j] = v.weights[j];
}
}
@@ -404,9 +587,8 @@ Array SurfaceTool::commit_to_arrays() {
array.resize(index_array.size());
int *w = array.ptrw();
- int idx = 0;
- for (List<int>::Element *E = index_array.front(); E; E = E->next(), idx++) {
- w[idx] = E->get();
+ for (uint32_t idx = 0; idx < index_array.size(); idx++) {
+ w[idx] = index_array[idx];
}
a[i] = array;
@@ -425,7 +607,7 @@ Ref<ArrayMesh> SurfaceTool::commit(const Ref<ArrayMesh> &p_existing, uint32_t p_
if (p_existing.is_valid()) {
mesh = p_existing;
} else {
- mesh.instance();
+ mesh.instantiate();
}
int varr_len = vertex_array.size();
@@ -453,15 +635,16 @@ void SurfaceTool::index() {
}
HashMap<Vertex, int, VertexHasher> indices;
- List<Vertex> new_vertices;
+ LocalVector<Vertex> old_vertex_array = vertex_array;
+ vertex_array.clear();
- for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next()) {
- int *idxptr = indices.getptr(E->get());
+ for (uint32_t i = 0; i < old_vertex_array.size(); i++) {
+ int *idxptr = indices.getptr(old_vertex_array[i]);
int idx;
if (!idxptr) {
idx = indices.size();
- new_vertices.push_back(E->get());
- indices[E->get()] = idx;
+ vertex_array.push_back(old_vertex_array[i]);
+ indices[old_vertex_array[i]] = idx;
} else {
idx = *idxptr;
}
@@ -469,9 +652,6 @@ void SurfaceTool::index() {
index_array.push_back(idx);
}
- vertex_array.clear();
- vertex_array = new_vertices;
-
format |= Mesh::ARRAY_FORMAT_INDEX;
}
@@ -479,29 +659,28 @@ void SurfaceTool::deindex() {
if (index_array.size() == 0) {
return; //nothing to deindex
}
- Vector<Vertex> varr;
- varr.resize(vertex_array.size());
- int idx = 0;
- for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next()) {
- varr.write[idx++] = E->get();
- }
+
+ LocalVector<Vertex> old_vertex_array = vertex_array;
vertex_array.clear();
- for (List<int>::Element *E = index_array.front(); E; E = E->next()) {
- ERR_FAIL_INDEX(E->get(), varr.size());
- vertex_array.push_back(varr[E->get()]);
+ for (uint32_t i = 0; i < index_array.size(); i++) {
+ uint32_t index = index_array[i];
+ ERR_FAIL_COND(index >= old_vertex_array.size());
+ vertex_array.push_back(old_vertex_array[index]);
}
format &= ~Mesh::ARRAY_FORMAT_INDEX;
index_array.clear();
}
-void SurfaceTool::_create_list(const Ref<Mesh> &p_existing, int p_surface, List<Vertex> *r_vertex, List<int> *r_index, int &lformat) {
+void SurfaceTool::_create_list(const Ref<Mesh> &p_existing, int p_surface, LocalVector<Vertex> *r_vertex, LocalVector<int> *r_index, uint32_t &lformat) {
+ ERR_FAIL_NULL_MSG(p_existing, "First argument in SurfaceTool::_create_list() must be a valid object of type Mesh");
+
Array arr = p_existing->surface_get_arrays(p_surface);
ERR_FAIL_COND(arr.size() != RS::ARRAY_MAX);
_create_list_from_arrays(arr, r_vertex, r_index, lformat);
}
-Vector<SurfaceTool::Vertex> SurfaceTool::create_vertex_array_from_triangle_arrays(const Array &p_arrays) {
- Vector<SurfaceTool::Vertex> ret;
+void SurfaceTool::create_vertex_array_from_triangle_arrays(const Array &p_arrays, LocalVector<SurfaceTool::Vertex> &ret, uint32_t *r_format) {
+ ret.clear();
Vector<Vector3> varr = p_arrays[RS::ARRAY_VERTEX];
Vector<Vector3> narr = p_arrays[RS::ARRAY_NORMAL];
@@ -511,10 +690,14 @@ Vector<SurfaceTool::Vertex> SurfaceTool::create_vertex_array_from_triangle_array
Vector<Vector2> uv2arr = p_arrays[RS::ARRAY_TEX_UV2];
Vector<int> barr = p_arrays[RS::ARRAY_BONES];
Vector<float> warr = p_arrays[RS::ARRAY_WEIGHTS];
+ Vector<float> custom_float[RS::ARRAY_CUSTOM_COUNT];
int vc = varr.size();
if (vc == 0) {
- return ret;
+ if (r_format) {
+ *r_format = 0;
+ }
+ return;
}
int lformat = 0;
@@ -536,100 +719,40 @@ Vector<SurfaceTool::Vertex> SurfaceTool::create_vertex_array_from_triangle_array
if (uv2arr.size()) {
lformat |= RS::ARRAY_FORMAT_TEX_UV2;
}
- if (barr.size()) {
+ int wcount = 0;
+ if (barr.size() && warr.size()) {
lformat |= RS::ARRAY_FORMAT_BONES;
- }
- if (warr.size()) {
lformat |= RS::ARRAY_FORMAT_WEIGHTS;
- }
- for (int i = 0; i < vc; i++) {
- Vertex v;
- if (lformat & RS::ARRAY_FORMAT_VERTEX) {
- v.vertex = varr[i];
- }
- if (lformat & RS::ARRAY_FORMAT_NORMAL) {
- v.normal = narr[i];
- }
- if (lformat & RS::ARRAY_FORMAT_TANGENT) {
- Plane p(tarr[i * 4 + 0], tarr[i * 4 + 1], tarr[i * 4 + 2], tarr[i * 4 + 3]);
- v.tangent = p.normal;
- v.binormal = p.normal.cross(v.tangent).normalized() * p.d;
- }
- if (lformat & RS::ARRAY_FORMAT_COLOR) {
- v.color = carr[i];
- }
- if (lformat & RS::ARRAY_FORMAT_TEX_UV) {
- v.uv = uvarr[i];
- }
- if (lformat & RS::ARRAY_FORMAT_TEX_UV2) {
- v.uv2 = uv2arr[i];
- }
- if (lformat & RS::ARRAY_FORMAT_BONES) {
- Vector<int> b;
- b.resize(4);
- b.write[0] = barr[i * 4 + 0];
- b.write[1] = barr[i * 4 + 1];
- b.write[2] = barr[i * 4 + 2];
- b.write[3] = barr[i * 4 + 3];
- v.bones = b;
+ wcount = barr.size() / varr.size();
+ if (wcount == 8) {
+ lformat |= RS::ARRAY_FLAG_USE_8_BONE_WEIGHTS;
}
- if (lformat & RS::ARRAY_FORMAT_WEIGHTS) {
- Vector<float> w;
- w.resize(4);
- w.write[0] = warr[i * 4 + 0];
- w.write[1] = warr[i * 4 + 1];
- w.write[2] = warr[i * 4 + 2];
- w.write[3] = warr[i * 4 + 3];
- v.weights = w;
- }
-
- ret.push_back(v);
}
- return ret;
-}
-
-void SurfaceTool::_create_list_from_arrays(Array arr, List<Vertex> *r_vertex, List<int> *r_index, int &lformat) {
- Vector<Vector3> varr = arr[RS::ARRAY_VERTEX];
- Vector<Vector3> narr = arr[RS::ARRAY_NORMAL];
- Vector<float> tarr = arr[RS::ARRAY_TANGENT];
- Vector<Color> carr = arr[RS::ARRAY_COLOR];
- Vector<Vector2> uvarr = arr[RS::ARRAY_TEX_UV];
- Vector<Vector2> uv2arr = arr[RS::ARRAY_TEX_UV2];
- Vector<int> barr = arr[RS::ARRAY_BONES];
- Vector<float> warr = arr[RS::ARRAY_WEIGHTS];
-
- int vc = varr.size();
- if (vc == 0) {
- return;
- }
-
- lformat = 0;
- if (varr.size()) {
- lformat |= RS::ARRAY_FORMAT_VERTEX;
- }
- if (narr.size()) {
- lformat |= RS::ARRAY_FORMAT_NORMAL;
- }
- if (tarr.size()) {
- lformat |= RS::ARRAY_FORMAT_TANGENT;
- }
- if (carr.size()) {
- lformat |= RS::ARRAY_FORMAT_COLOR;
- }
- if (uvarr.size()) {
- lformat |= RS::ARRAY_FORMAT_TEX_UV;
- }
- if (uv2arr.size()) {
- lformat |= RS::ARRAY_FORMAT_TEX_UV2;
- }
- if (barr.size()) {
- lformat |= RS::ARRAY_FORMAT_BONES;
- }
if (warr.size()) {
lformat |= RS::ARRAY_FORMAT_WEIGHTS;
}
+ static const uint32_t custom_mask[RS::ARRAY_CUSTOM_COUNT] = { Mesh::ARRAY_FORMAT_CUSTOM0, Mesh::ARRAY_FORMAT_CUSTOM1, Mesh::ARRAY_FORMAT_CUSTOM2, Mesh::ARRAY_FORMAT_CUSTOM3 };
+ static const uint32_t custom_shift[RS::ARRAY_CUSTOM_COUNT] = { Mesh::ARRAY_FORMAT_CUSTOM0_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM1_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM2_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM3_SHIFT };
+
+ for (int i = 0; i < RS::ARRAY_CUSTOM_COUNT; i++) {
+ ERR_CONTINUE_MSG(p_arrays[RS::ARRAY_CUSTOM0 + i].get_type() == Variant::PACKED_BYTE_ARRAY, "Extracting Byte/Half formats is not supported");
+ if (p_arrays[RS::ARRAY_CUSTOM0 + i].get_type() == Variant::PACKED_FLOAT32_ARRAY) {
+ lformat |= custom_mask[i];
+ custom_float[i] = p_arrays[RS::ARRAY_CUSTOM0 + i];
+ int fmt = custom_float[i].size() / varr.size();
+ if (fmt == 1) {
+ lformat |= CUSTOM_R_FLOAT << custom_shift[i];
+ } else if (fmt == 2) {
+ lformat |= CUSTOM_RG_FLOAT << custom_shift[i];
+ } else if (fmt == 3) {
+ lformat |= CUSTOM_RGB_FLOAT << custom_shift[i];
+ } else if (fmt == 4) {
+ lformat |= CUSTOM_RGBA_FLOAT << custom_shift[i];
+ }
+ }
+ }
for (int i = 0; i < vc; i++) {
Vertex v;
@@ -655,27 +778,44 @@ void SurfaceTool::_create_list_from_arrays(Array arr, List<Vertex> *r_vertex, Li
}
if (lformat & RS::ARRAY_FORMAT_BONES) {
Vector<int> b;
- b.resize(4);
- b.write[0] = barr[i * 4 + 0];
- b.write[1] = barr[i * 4 + 1];
- b.write[2] = barr[i * 4 + 2];
- b.write[3] = barr[i * 4 + 3];
+ b.resize(wcount);
+ for (int j = 0; j < wcount; j++) {
+ b.write[j] = barr[i * wcount + j];
+ }
v.bones = b;
}
if (lformat & RS::ARRAY_FORMAT_WEIGHTS) {
Vector<float> w;
- w.resize(4);
- w.write[0] = warr[i * 4 + 0];
- w.write[1] = warr[i * 4 + 1];
- w.write[2] = warr[i * 4 + 2];
- w.write[3] = warr[i * 4 + 3];
+ w.resize(wcount);
+ for (int j = 0; j < wcount; j++) {
+ w.write[j] = warr[i * wcount + j];
+ }
v.weights = w;
}
- r_vertex->push_back(v);
+ for (int j = 0; j < RS::ARRAY_CUSTOM_COUNT; j++) {
+ if (lformat & custom_mask[j]) {
+ int cc = custom_float[j].size() / varr.size();
+ for (int k = 0; k < cc; k++) {
+ v.custom[j][k] = custom_float[j][i * cc + k];
+ }
+ }
+ }
+
+ ret.push_back(v);
}
+ if (r_format) {
+ *r_format = lformat;
+ }
+}
+
+void SurfaceTool::_create_list_from_arrays(Array arr, LocalVector<Vertex> *r_vertex, LocalVector<int> *r_index, uint32_t &lformat) {
+ create_vertex_array_from_triangle_arrays(arr, *r_vertex, &lformat);
+ ERR_FAIL_COND(r_vertex->size() == 0);
+
//indices
+ r_index->clear();
Vector<int> idx = arr[RS::ARRAY_INDEX];
int is = idx.size();
@@ -695,6 +835,8 @@ void SurfaceTool::create_from_triangle_arrays(const Array &p_arrays) {
}
void SurfaceTool::create_from(const Ref<Mesh> &p_existing, int p_surface) {
+ ERR_FAIL_NULL_MSG(p_existing, "First argument in SurfaceTool::create_from() must be a valid object of type Mesh");
+
clear();
primitive = p_existing->surface_get_primitive_type(p_surface);
_create_list(p_existing, p_surface, &vertex_array, &index_array, format);
@@ -702,6 +844,8 @@ void SurfaceTool::create_from(const Ref<Mesh> &p_existing, int p_surface) {
}
void SurfaceTool::create_from_blend_shape(const Ref<Mesh> &p_existing, int p_surface, const String &p_blend_shape_name) {
+ ERR_FAIL_NULL_MSG(p_existing, "First argument in SurfaceTool::create_from_blend_shape() must be a valid object of type Mesh");
+
clear();
primitive = p_existing->surface_get_primitive_type(p_surface);
Array arr = p_existing->surface_get_blend_shape_arrays(p_surface);
@@ -721,21 +865,23 @@ void SurfaceTool::create_from_blend_shape(const Ref<Mesh> &p_existing, int p_sur
_create_list_from_arrays(arr[shape_idx], &vertex_array, &index_array, format);
}
-void SurfaceTool::append_from(const Ref<Mesh> &p_existing, int p_surface, const Transform &p_xform) {
+void SurfaceTool::append_from(const Ref<Mesh> &p_existing, int p_surface, const Transform3D &p_xform) {
+ ERR_FAIL_NULL_MSG(p_existing, "First argument in SurfaceTool::append_from() must be a valid object of type Mesh");
+
if (vertex_array.size() == 0) {
primitive = p_existing->surface_get_primitive_type(p_surface);
format = 0;
}
- int nformat;
- List<Vertex> nvertices;
- List<int> nindices;
+ uint32_t nformat;
+ LocalVector<Vertex> nvertices;
+ LocalVector<int> nindices;
_create_list(p_existing, p_surface, &nvertices, &nindices, nformat);
format |= nformat;
int vfrom = vertex_array.size();
- for (List<Vertex>::Element *E = nvertices.front(); E; E = E->next()) {
- Vertex v = E->get();
+ for (uint32_t vi = 0; vi < nvertices.size(); vi++) {
+ Vertex v = nvertices[vi];
v.vertex = p_xform.xform(v.vertex);
if (nformat & RS::ARRAY_FORMAT_NORMAL) {
v.normal = p_xform.basis.xform(v.normal);
@@ -748,8 +894,8 @@ void SurfaceTool::append_from(const Ref<Mesh> &p_existing, int p_surface, const
vertex_array.push_back(v);
}
- for (List<int>::Element *E = nindices.front(); E; E = E->next()) {
- int dst_index = E->get() + vfrom;
+ for (uint32_t i = 0; i < nindices.size(); i++) {
+ int dst_index = nindices[i] + vfrom;
index_array.push_back(dst_index);
}
if (index_array.size() % 3) {
@@ -760,18 +906,18 @@ void SurfaceTool::append_from(const Ref<Mesh> &p_existing, int p_surface, const
//mikktspace callbacks
namespace {
struct TangentGenerationContextUserData {
- Vector<List<SurfaceTool::Vertex>::Element *> vertices;
- Vector<List<int>::Element *> indices;
+ LocalVector<SurfaceTool::Vertex> *vertices;
+ LocalVector<int> *indices;
};
} // namespace
int SurfaceTool::mikktGetNumFaces(const SMikkTSpaceContext *pContext) {
TangentGenerationContextUserData &triangle_data = *reinterpret_cast<TangentGenerationContextUserData *>(pContext->m_pUserData);
- if (triangle_data.indices.size() > 0) {
- return triangle_data.indices.size() / 3;
+ if (triangle_data.indices->size() > 0) {
+ return triangle_data.indices->size() / 3;
} else {
- return triangle_data.vertices.size() / 3;
+ return triangle_data.vertices->size() / 3;
}
}
@@ -782,13 +928,13 @@ int SurfaceTool::mikktGetNumVerticesOfFace(const SMikkTSpaceContext *pContext, c
void SurfaceTool::mikktGetPosition(const SMikkTSpaceContext *pContext, float fvPosOut[], const int iFace, const int iVert) {
TangentGenerationContextUserData &triangle_data = *reinterpret_cast<TangentGenerationContextUserData *>(pContext->m_pUserData);
Vector3 v;
- if (triangle_data.indices.size() > 0) {
- int index = triangle_data.indices[iFace * 3 + iVert]->get();
- if (index < triangle_data.vertices.size()) {
- v = triangle_data.vertices[index]->get().vertex;
+ if (triangle_data.indices->size() > 0) {
+ uint32_t index = triangle_data.indices->operator[](iFace * 3 + iVert);
+ if (index < triangle_data.vertices->size()) {
+ v = triangle_data.vertices->operator[](index).vertex;
}
} else {
- v = triangle_data.vertices[iFace * 3 + iVert]->get().vertex;
+ v = triangle_data.vertices->operator[](iFace * 3 + iVert).vertex;
}
fvPosOut[0] = v.x;
@@ -799,13 +945,13 @@ void SurfaceTool::mikktGetPosition(const SMikkTSpaceContext *pContext, float fvP
void SurfaceTool::mikktGetNormal(const SMikkTSpaceContext *pContext, float fvNormOut[], const int iFace, const int iVert) {
TangentGenerationContextUserData &triangle_data = *reinterpret_cast<TangentGenerationContextUserData *>(pContext->m_pUserData);
Vector3 v;
- if (triangle_data.indices.size() > 0) {
- int index = triangle_data.indices[iFace * 3 + iVert]->get();
- if (index < triangle_data.vertices.size()) {
- v = triangle_data.vertices[index]->get().normal;
+ if (triangle_data.indices->size() > 0) {
+ uint32_t index = triangle_data.indices->operator[](iFace * 3 + iVert);
+ if (index < triangle_data.vertices->size()) {
+ v = triangle_data.vertices->operator[](index).normal;
}
} else {
- v = triangle_data.vertices[iFace * 3 + iVert]->get().normal;
+ v = triangle_data.vertices->operator[](iFace * 3 + iVert).normal;
}
fvNormOut[0] = v.x;
@@ -816,13 +962,13 @@ void SurfaceTool::mikktGetNormal(const SMikkTSpaceContext *pContext, float fvNor
void SurfaceTool::mikktGetTexCoord(const SMikkTSpaceContext *pContext, float fvTexcOut[], const int iFace, const int iVert) {
TangentGenerationContextUserData &triangle_data = *reinterpret_cast<TangentGenerationContextUserData *>(pContext->m_pUserData);
Vector2 v;
- if (triangle_data.indices.size() > 0) {
- int index = triangle_data.indices[iFace * 3 + iVert]->get();
- if (index < triangle_data.vertices.size()) {
- v = triangle_data.vertices[index]->get().uv;
+ if (triangle_data.indices->size() > 0) {
+ uint32_t index = triangle_data.indices->operator[](iFace * 3 + iVert);
+ if (index < triangle_data.vertices->size()) {
+ v = triangle_data.vertices->operator[](index).uv;
}
} else {
- v = triangle_data.vertices[iFace * 3 + iVert]->get().uv;
+ v = triangle_data.vertices->operator[](iFace * 3 + iVert).uv;
}
fvTexcOut[0] = v.x;
@@ -833,13 +979,13 @@ void SurfaceTool::mikktSetTSpaceDefault(const SMikkTSpaceContext *pContext, cons
const tbool bIsOrientationPreserving, const int iFace, const int iVert) {
TangentGenerationContextUserData &triangle_data = *reinterpret_cast<TangentGenerationContextUserData *>(pContext->m_pUserData);
Vertex *vtx = nullptr;
- if (triangle_data.indices.size() > 0) {
- int index = triangle_data.indices[iFace * 3 + iVert]->get();
- if (index < triangle_data.vertices.size()) {
- vtx = &triangle_data.vertices[index]->get();
+ if (triangle_data.indices->size() > 0) {
+ uint32_t index = triangle_data.indices->operator[](iFace * 3 + iVert);
+ if (index < triangle_data.vertices->size()) {
+ vtx = &triangle_data.vertices->operator[](index);
}
} else {
- vtx = &triangle_data.vertices[iFace * 3 + iVert]->get();
+ vtx = &triangle_data.vertices->operator[](iFace * 3 + iVert);
}
if (vtx != nullptr) {
@@ -865,18 +1011,12 @@ void SurfaceTool::generate_tangents() {
msc.m_pInterface = &mkif;
TangentGenerationContextUserData triangle_data;
- triangle_data.vertices.resize(vertex_array.size());
- int idx = 0;
- for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next()) {
- triangle_data.vertices.write[idx++] = E;
- E->get().binormal = Vector3();
- E->get().tangent = Vector3();
- }
- triangle_data.indices.resize(index_array.size());
- idx = 0;
- for (List<int>::Element *E = index_array.front(); E; E = E->next()) {
- triangle_data.indices.write[idx++] = E;
+ triangle_data.vertices = &vertex_array;
+ for (uint32_t i = 0; i < vertex_array.size(); i++) {
+ vertex_array[i].binormal = Vector3();
+ vertex_array[i].tangent = Vector3();
}
+ triangle_data.indices = &index_array;
msc.m_pUserData = &triangle_data;
bool res = genTangSpaceDefault(&msc);
@@ -892,66 +1032,36 @@ void SurfaceTool::generate_normals(bool p_flip) {
deindex();
- HashMap<Vertex, Vector3, VertexHasher> vertex_hash;
+ ERR_FAIL_COND((vertex_array.size() % 3) != 0);
- int count = 0;
- bool smooth = false;
- if (smooth_groups.has(0)) {
- smooth = smooth_groups[0];
- }
+ HashMap<Vertex, Vector3, VertexHasher> vertex_hash;
- List<Vertex>::Element *B = vertex_array.front();
- for (List<Vertex>::Element *E = B; E;) {
- List<Vertex>::Element *v[3];
- v[0] = E;
- v[1] = v[0]->next();
- ERR_FAIL_COND(!v[1]);
- v[2] = v[1]->next();
- ERR_FAIL_COND(!v[2]);
- E = v[2]->next();
+ for (uint32_t vi = 0; vi < vertex_array.size(); vi += 3) {
+ Vertex *v = &vertex_array[vi];
Vector3 normal;
if (!p_flip) {
- normal = Plane(v[0]->get().vertex, v[1]->get().vertex, v[2]->get().vertex).normal;
+ normal = Plane(v[0].vertex, v[1].vertex, v[2].vertex).normal;
} else {
- normal = Plane(v[2]->get().vertex, v[1]->get().vertex, v[0]->get().vertex).normal;
+ normal = Plane(v[2].vertex, v[1].vertex, v[0].vertex).normal;
}
- if (smooth) {
- for (int i = 0; i < 3; i++) {
- Vector3 *lv = vertex_hash.getptr(v[i]->get());
- if (!lv) {
- vertex_hash.set(v[i]->get(), normal);
- } else {
- (*lv) += normal;
- }
- }
- } else {
- for (int i = 0; i < 3; i++) {
- v[i]->get().normal = normal;
- }
- }
- count += 3;
-
- if (smooth_groups.has(count) || !E) {
- if (vertex_hash.size()) {
- while (B != E) {
- Vector3 *lv = vertex_hash.getptr(B->get());
- if (lv) {
- B->get().normal = lv->normalized();
- }
-
- B = B->next();
- }
-
+ for (int i = 0; i < 3; i++) {
+ Vector3 *lv = vertex_hash.getptr(v[i]);
+ if (!lv) {
+ vertex_hash.set(v[i], normal);
} else {
- B = E;
+ (*lv) += normal;
}
+ }
+ }
- vertex_hash.clear();
- if (E) {
- smooth = smooth_groups[count];
- }
+ for (uint32_t vi = 0; vi < vertex_array.size(); vi++) {
+ Vector3 *lv = vertex_hash.getptr(vertex_array[vi]);
+ if (!lv) {
+ vertex_array[vi].normal = Vector3();
+ } else {
+ vertex_array[vi].normal = lv->normalized();
}
}
@@ -959,7 +1069,6 @@ void SurfaceTool::generate_normals(bool p_flip) {
if (was_indexed) {
index();
- smooth_groups.clear();
}
}
@@ -967,6 +1076,10 @@ void SurfaceTool::set_material(const Ref<Material> &p_material) {
material = p_material;
}
+Ref<Material> SurfaceTool::get_material() const {
+ return material;
+}
+
void SurfaceTool::clear() {
begun = false;
primitive = Mesh::PRIMITIVE_LINES;
@@ -975,22 +1088,102 @@ void SurfaceTool::clear() {
last_weights.clear();
index_array.clear();
vertex_array.clear();
- smooth_groups.clear();
material.unref();
+ last_smooth_group = 0;
+ for (int i = 0; i < RS::ARRAY_CUSTOM_COUNT; i++) {
+ last_custom_format[i] = CUSTOM_MAX;
+ }
+ skin_weights = SKIN_4_WEIGHTS;
+}
+
+void SurfaceTool::set_skin_weight_count(SkinWeightCount p_weights) {
+ ERR_FAIL_COND(begun);
+ skin_weights = p_weights;
+}
+SurfaceTool::SkinWeightCount SurfaceTool::get_skin_weight_count() const {
+ return skin_weights;
+}
+
+void SurfaceTool::set_custom_format(int p_index, CustomFormat p_format) {
+ ERR_FAIL_INDEX(p_index, RS::ARRAY_CUSTOM_COUNT);
+ ERR_FAIL_COND(begun);
+ last_custom_format[p_index] = p_format;
+}
+
+Mesh::PrimitiveType SurfaceTool::get_primitive() const {
+ return primitive;
+}
+SurfaceTool::CustomFormat SurfaceTool::get_custom_format(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, RS::ARRAY_CUSTOM_COUNT, CUSTOM_MAX);
+ return last_custom_format[p_index];
+}
+void SurfaceTool::optimize_indices_for_cache() {
+ ERR_FAIL_COND(optimize_vertex_cache_func == nullptr);
+ ERR_FAIL_COND(index_array.size() == 0);
+ ERR_FAIL_COND(index_array.size() % 3 != 0);
+
+ LocalVector old_index_array = index_array;
+ memset(index_array.ptr(), 0, index_array.size() * sizeof(int));
+ optimize_vertex_cache_func((unsigned int *)index_array.ptr(), (unsigned int *)old_index_array.ptr(), old_index_array.size(), vertex_array.size());
+}
+
+float SurfaceTool::get_max_axis_length() const {
+ ERR_FAIL_COND_V(vertex_array.size() == 0, 0);
+
+ AABB aabb;
+ for (uint32_t i = 0; i < vertex_array.size(); i++) {
+ if (i == 0) {
+ aabb.position = vertex_array[i].vertex;
+ } else {
+ aabb.expand_to(vertex_array[i].vertex);
+ }
+ }
+
+ return aabb.get_longest_axis_size();
+}
+Vector<int> SurfaceTool::generate_lod(float p_threshold, int p_target_index_count) {
+ Vector<int> lod;
+
+ ERR_FAIL_COND_V(simplify_func == nullptr, lod);
+ ERR_FAIL_COND_V(vertex_array.size() == 0, lod);
+ ERR_FAIL_COND_V(index_array.size() == 0, lod);
+
+ lod.resize(index_array.size());
+ LocalVector<float> vertices; //uses floats
+ vertices.resize(vertex_array.size() * 3);
+ for (uint32_t i = 0; i < vertex_array.size(); i++) {
+ vertices[i * 3 + 0] = vertex_array[i].vertex.x;
+ vertices[i * 3 + 1] = vertex_array[i].vertex.y;
+ vertices[i * 3 + 2] = vertex_array[i].vertex.z;
+ }
+
+ float error;
+ uint32_t index_count = simplify_func((unsigned int *)lod.ptrw(), (unsigned int *)index_array.ptr(), index_array.size(), vertices.ptr(), vertex_array.size(), sizeof(float) * 3, p_target_index_count, p_threshold, &error);
+ ERR_FAIL_COND_V(index_count == 0, lod);
+ lod.resize(index_count);
+
+ return lod;
}
void SurfaceTool::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_skin_weight_count", "count"), &SurfaceTool::set_skin_weight_count);
+ ClassDB::bind_method(D_METHOD("get_skin_weight_count"), &SurfaceTool::get_skin_weight_count);
+
+ ClassDB::bind_method(D_METHOD("set_custom_format", "index", "format"), &SurfaceTool::set_custom_format);
+ ClassDB::bind_method(D_METHOD("get_custom_format", "index"), &SurfaceTool::get_custom_format);
+
ClassDB::bind_method(D_METHOD("begin", "primitive"), &SurfaceTool::begin);
ClassDB::bind_method(D_METHOD("add_vertex", "vertex"), &SurfaceTool::add_vertex);
- ClassDB::bind_method(D_METHOD("add_color", "color"), &SurfaceTool::add_color);
- ClassDB::bind_method(D_METHOD("add_normal", "normal"), &SurfaceTool::add_normal);
- ClassDB::bind_method(D_METHOD("add_tangent", "tangent"), &SurfaceTool::add_tangent);
- ClassDB::bind_method(D_METHOD("add_uv", "uv"), &SurfaceTool::add_uv);
- ClassDB::bind_method(D_METHOD("add_uv2", "uv2"), &SurfaceTool::add_uv2);
- ClassDB::bind_method(D_METHOD("add_bones", "bones"), &SurfaceTool::add_bones);
- ClassDB::bind_method(D_METHOD("add_weights", "weights"), &SurfaceTool::add_weights);
- ClassDB::bind_method(D_METHOD("add_smooth_group", "smooth"), &SurfaceTool::add_smooth_group);
+ ClassDB::bind_method(D_METHOD("set_color", "color"), &SurfaceTool::set_color);
+ ClassDB::bind_method(D_METHOD("set_normal", "normal"), &SurfaceTool::set_normal);
+ ClassDB::bind_method(D_METHOD("set_tangent", "tangent"), &SurfaceTool::set_tangent);
+ ClassDB::bind_method(D_METHOD("set_uv", "uv"), &SurfaceTool::set_uv);
+ ClassDB::bind_method(D_METHOD("set_uv2", "uv2"), &SurfaceTool::set_uv2);
+ ClassDB::bind_method(D_METHOD("set_bones", "bones"), &SurfaceTool::set_bones);
+ ClassDB::bind_method(D_METHOD("set_weights", "weights"), &SurfaceTool::set_weights);
+ ClassDB::bind_method(D_METHOD("set_custom", "index", "custom"), &SurfaceTool::set_custom);
+ ClassDB::bind_method(D_METHOD("set_smooth_group", "index"), &SurfaceTool::set_smooth_group);
ClassDB::bind_method(D_METHOD("add_triangle_fan", "vertices", "uvs", "colors", "uv2s", "normals", "tangents"), &SurfaceTool::add_triangle_fan, DEFVAL(Vector<Vector2>()), DEFVAL(Vector<Color>()), DEFVAL(Vector<Vector2>()), DEFVAL(Vector<Vector3>()), DEFVAL(Vector<Plane>()));
@@ -1001,20 +1194,37 @@ void SurfaceTool::_bind_methods() {
ClassDB::bind_method(D_METHOD("generate_normals", "flip"), &SurfaceTool::generate_normals, DEFVAL(false));
ClassDB::bind_method(D_METHOD("generate_tangents"), &SurfaceTool::generate_tangents);
+ ClassDB::bind_method(D_METHOD("optimize_indices_for_cache"), &SurfaceTool::optimize_indices_for_cache);
+
+ ClassDB::bind_method(D_METHOD("get_max_axis_length"), &SurfaceTool::get_max_axis_length);
+ ClassDB::bind_method(D_METHOD("generate_lod", "nd_threshold", "target_index_count"), &SurfaceTool::generate_lod, DEFVAL(3));
+
ClassDB::bind_method(D_METHOD("set_material", "material"), &SurfaceTool::set_material);
+ ClassDB::bind_method(D_METHOD("get_primitive"), &SurfaceTool::get_primitive);
ClassDB::bind_method(D_METHOD("clear"), &SurfaceTool::clear);
ClassDB::bind_method(D_METHOD("create_from", "existing", "surface"), &SurfaceTool::create_from);
ClassDB::bind_method(D_METHOD("create_from_blend_shape", "existing", "surface", "blend_shape"), &SurfaceTool::create_from_blend_shape);
ClassDB::bind_method(D_METHOD("append_from", "existing", "surface", "transform"), &SurfaceTool::append_from);
- ClassDB::bind_method(D_METHOD("commit", "existing", "flags"), &SurfaceTool::commit, DEFVAL(Variant()), DEFVAL(Mesh::ARRAY_COMPRESS_DEFAULT));
+ ClassDB::bind_method(D_METHOD("commit", "existing", "flags"), &SurfaceTool::commit, DEFVAL(Variant()), DEFVAL(0));
ClassDB::bind_method(D_METHOD("commit_to_arrays"), &SurfaceTool::commit_to_arrays);
+
+ BIND_ENUM_CONSTANT(CUSTOM_RGBA8_UNORM);
+ BIND_ENUM_CONSTANT(CUSTOM_RGBA8_SNORM);
+ BIND_ENUM_CONSTANT(CUSTOM_RG_HALF);
+ BIND_ENUM_CONSTANT(CUSTOM_RGBA_HALF);
+ BIND_ENUM_CONSTANT(CUSTOM_R_FLOAT);
+ BIND_ENUM_CONSTANT(CUSTOM_RG_FLOAT);
+ BIND_ENUM_CONSTANT(CUSTOM_RGB_FLOAT);
+ BIND_ENUM_CONSTANT(CUSTOM_RGBA_FLOAT);
+ BIND_ENUM_CONSTANT(CUSTOM_MAX);
+ BIND_ENUM_CONSTANT(SKIN_4_WEIGHTS);
+ BIND_ENUM_CONSTANT(SKIN_8_WEIGHTS);
}
SurfaceTool::SurfaceTool() {
- first = false;
- begun = false;
- primitive = Mesh::PRIMITIVE_LINES;
- format = 0;
+ for (int i = 0; i < RS::ARRAY_CUSTOM_COUNT; i++) {
+ last_custom_format[i] = CUSTOM_MAX;
+ }
}
diff --git a/scene/resources/surface_tool.h b/scene/resources/surface_tool.h
index d7b255e695..bde6702759 100644
--- a/scene/resources/surface_tool.h
+++ b/scene/resources/surface_tool.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,12 +31,12 @@
#ifndef SURFACE_TOOL_H
#define SURFACE_TOOL_H
+#include "core/templates/local_vector.h"
#include "scene/resources/mesh.h"
-
#include "thirdparty/misc/mikktspace.h"
-class SurfaceTool : public Reference {
- GDCLASS(SurfaceTool, Reference);
+class SurfaceTool : public RefCounted {
+ GDCLASS(SurfaceTool, RefCounted);
public:
struct Vertex {
@@ -49,34 +49,63 @@ public:
Vector2 uv2;
Vector<int> bones;
Vector<float> weights;
+ Color custom[RS::ARRAY_CUSTOM_COUNT];
+ uint32_t smooth_group = 0;
bool operator==(const Vertex &p_vertex) const;
Vertex() {}
};
+ enum CustomFormat {
+ CUSTOM_RGBA8_UNORM = RS::ARRAY_CUSTOM_RGBA8_UNORM,
+ CUSTOM_RGBA8_SNORM = RS::ARRAY_CUSTOM_RGBA8_SNORM,
+ CUSTOM_RG_HALF = RS::ARRAY_CUSTOM_RG_HALF,
+ CUSTOM_RGBA_HALF = RS::ARRAY_CUSTOM_RGBA_HALF,
+ CUSTOM_R_FLOAT = RS::ARRAY_CUSTOM_R_FLOAT,
+ CUSTOM_RG_FLOAT = RS::ARRAY_CUSTOM_RG_FLOAT,
+ CUSTOM_RGB_FLOAT = RS::ARRAY_CUSTOM_RGB_FLOAT,
+ CUSTOM_RGBA_FLOAT = RS::ARRAY_CUSTOM_RGBA_FLOAT,
+ CUSTOM_MAX = RS::ARRAY_CUSTOM_MAX
+ };
+
+ enum SkinWeightCount {
+ SKIN_4_WEIGHTS,
+ SKIN_8_WEIGHTS
+ };
+
+ typedef void (*OptimizeVertexCacheFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, size_t vertex_count);
+ static OptimizeVertexCacheFunc optimize_vertex_cache_func;
+ typedef size_t (*SimplifyFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const float *vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, float *r_error);
+ static SimplifyFunc simplify_func;
+ typedef size_t (*SimplifyWithAttribFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const float *vertex_data, size_t vertex_count, size_t vertex_stride, size_t target_index_count, float target_error, float *result_error, const float *attributes, const float *attribute_weights, size_t attribute_count);
+ static SimplifyWithAttribFunc simplify_with_attrib_func;
+ typedef float (*SimplifyScaleFunc)(const float *vertex_positions, size_t vertex_count, size_t vertex_positions_stride);
+ static SimplifyScaleFunc simplify_scale_func;
+ typedef size_t (*SimplifySloppyFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const float *vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, float *out_result_error);
+ static SimplifySloppyFunc simplify_sloppy_func;
+
private:
struct VertexHasher {
static _FORCE_INLINE_ uint32_t hash(const Vertex &p_vtx);
};
struct WeightSort {
- int index;
- float weight;
+ int index = 0;
+ float weight = 0.0;
bool operator<(const WeightSort &p_right) const {
return weight < p_right.weight;
}
};
- bool begun;
- bool first;
- Mesh::PrimitiveType primitive;
- int format;
+ bool begun = false;
+ bool first = false;
+ Mesh::PrimitiveType primitive = Mesh::PRIMITIVE_LINES;
+ uint32_t format = 0;
Ref<Material> material;
//arrays
- List<Vertex> vertex_array;
- List<int> index_array;
- Map<int, bool> smooth_groups;
+ LocalVector<Vertex> vertex_array;
+ LocalVector<int> index_array;
//memory
Color last_color;
@@ -86,9 +115,16 @@ private:
Vector<int> last_bones;
Vector<float> last_weights;
Plane last_tangent;
+ uint32_t last_smooth_group = 0;
+
+ SkinWeightCount skin_weights = SKIN_4_WEIGHTS;
+
+ Color last_custom[RS::ARRAY_CUSTOM_COUNT];
- void _create_list_from_arrays(Array arr, List<Vertex> *r_vertex, List<int> *r_index, int &lformat);
- void _create_list(const Ref<Mesh> &p_existing, int p_surface, List<Vertex> *r_vertex, List<int> *r_index, int &lformat);
+ CustomFormat last_custom_format[RS::ARRAY_CUSTOM_COUNT];
+
+ void _create_list_from_arrays(Array arr, LocalVector<Vertex> *r_vertex, LocalVector<int> *r_index, uint32_t &lformat);
+ void _create_list(const Ref<Mesh> &p_existing, int p_surface, LocalVector<Vertex> *r_vertex, LocalVector<int> *r_index, uint32_t &lformat);
//mikktspace callbacks
static int mikktGetNumFaces(const SMikkTSpaceContext *pContext);
@@ -103,17 +139,27 @@ protected:
static void _bind_methods();
public:
+ void set_skin_weight_count(SkinWeightCount p_weights);
+ SkinWeightCount get_skin_weight_count() const;
+
+ void set_custom_format(int p_index, CustomFormat p_format);
+ CustomFormat get_custom_format(int p_index) const;
+
+ Mesh::PrimitiveType get_primitive() const;
+
void begin(Mesh::PrimitiveType p_primitive);
+ void set_color(Color p_color);
+ void set_normal(const Vector3 &p_normal);
+ void set_tangent(const Plane &p_tangent);
+ void set_uv(const Vector2 &p_uv);
+ void set_uv2(const Vector2 &p_uv2);
+ void set_custom(int p_index, const Color &p_custom);
+ void set_bones(const Vector<int> &p_bones);
+ void set_weights(const Vector<float> &p_weights);
+ void set_smooth_group(uint32_t p_group);
+
void add_vertex(const Vector3 &p_vertex);
- void add_color(Color p_color);
- void add_normal(const Vector3 &p_normal);
- void add_tangent(const Plane &p_tangent);
- void add_uv(const Vector2 &p_uv);
- void add_uv2(const Vector2 &p_uv2);
- void add_bones(const Vector<int> &p_bones);
- void add_weights(const Vector<float> &p_weights);
- void add_smooth_group(bool p_smooth);
void add_triangle_fan(const Vector<Vector3> &p_vertices, const Vector<Vector2> &p_uvs = Vector<Vector2>(), const Vector<Color> &p_colors = Vector<Color>(), const Vector<Vector2> &p_uv2s = Vector<Vector2>(), const Vector<Vector3> &p_normals = Vector<Vector3>(), const Vector<Plane> &p_tangents = Vector<Plane>());
@@ -124,21 +170,29 @@ public:
void generate_normals(bool p_flip = false);
void generate_tangents();
+ void optimize_indices_for_cache();
+ float get_max_axis_length() const;
+ Vector<int> generate_lod(float p_threshold, int p_target_index_count = 3);
+
void set_material(const Ref<Material> &p_material);
+ Ref<Material> get_material() const;
void clear();
- List<Vertex> &get_vertex_array() { return vertex_array; }
+ LocalVector<Vertex> &get_vertex_array() { return vertex_array; }
void create_from_triangle_arrays(const Array &p_arrays);
- static Vector<Vertex> create_vertex_array_from_triangle_arrays(const Array &p_arrays);
+ static void create_vertex_array_from_triangle_arrays(const Array &p_arrays, LocalVector<Vertex> &ret, uint32_t *r_format = nullptr);
Array commit_to_arrays();
void create_from(const Ref<Mesh> &p_existing, int p_surface);
void create_from_blend_shape(const Ref<Mesh> &p_existing, int p_surface, const String &p_blend_shape_name);
- void append_from(const Ref<Mesh> &p_existing, int p_surface, const Transform &p_xform);
- Ref<ArrayMesh> commit(const Ref<ArrayMesh> &p_existing = Ref<ArrayMesh>(), uint32_t p_flags = Mesh::ARRAY_COMPRESS_DEFAULT);
+ void append_from(const Ref<Mesh> &p_existing, int p_surface, const Transform3D &p_xform);
+ Ref<ArrayMesh> commit(const Ref<ArrayMesh> &p_existing = Ref<ArrayMesh>(), uint32_t p_flags = 0);
SurfaceTool();
};
+VARIANT_ENUM_CAST(SurfaceTool::CustomFormat)
+VARIANT_ENUM_CAST(SurfaceTool::SkinWeightCount)
+
#endif
diff --git a/scene/resources/syntax_highlighter.cpp b/scene/resources/syntax_highlighter.cpp
index 5d58e71fc5..52a3abf74d 100644
--- a/scene/resources/syntax_highlighter.cpp
+++ b/scene/resources/syntax_highlighter.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,7 +30,7 @@
#include "syntax_highlighter.h"
-#include "core/script_language.h"
+#include "core/object/script_language.h"
#include "scene/gui/text_edit.h"
Dictionary SyntaxHighlighter::get_line_syntax_highlighting(int p_line) {
@@ -43,12 +43,10 @@ Dictionary SyntaxHighlighter::get_line_syntax_highlighting(int p_line) {
return color_map;
}
- ScriptInstance *si = get_script_instance();
- if (si && si->has_method("_get_line_syntax_highlighting")) {
- color_map = si->call("_get_line_syntax_highlighting", p_line);
- } else {
- color_map = _get_line_syntax_highlighting(p_line);
+ if (!GDVIRTUAL_CALL(_get_line_syntax_highlighting, p_line, color_map)) {
+ color_map = _get_line_syntax_highlighting_impl(p_line);
}
+
highlighting_cache[p_line] = color_map;
return color_map;
}
@@ -69,9 +67,7 @@ void SyntaxHighlighter::_lines_edited_from(int p_from_line, int p_to_line) {
void SyntaxHighlighter::clear_highlighting_cache() {
highlighting_cache.clear();
- ScriptInstance *si = get_script_instance();
- if (si && si->has_method("_clear_highlighting_cache")) {
- si->call("_clear_highlighting_cache");
+ if (GDVIRTUAL_CALL(_clear_highlighting_cache)) {
return;
}
_clear_highlighting_cache();
@@ -83,9 +79,7 @@ void SyntaxHighlighter::update_cache() {
if (text_edit == nullptr) {
return;
}
- ScriptInstance *si = get_script_instance();
- if (si && si->has_method("_update_cache")) {
- si->call("_update_cache");
+ if (GDVIRTUAL_CALL(_update_cache)) {
return;
}
_update_cache();
@@ -110,17 +104,14 @@ TextEdit *SyntaxHighlighter::get_text_edit() {
}
void SyntaxHighlighter::_bind_methods() {
- ClassDB::bind_method(D_METHOD("get_line_syntax_highlighting", "p_line"), &SyntaxHighlighter::get_line_syntax_highlighting);
+ ClassDB::bind_method(D_METHOD("get_line_syntax_highlighting", "line"), &SyntaxHighlighter::get_line_syntax_highlighting);
ClassDB::bind_method(D_METHOD("update_cache"), &SyntaxHighlighter::update_cache);
ClassDB::bind_method(D_METHOD("clear_highlighting_cache"), &SyntaxHighlighter::clear_highlighting_cache);
ClassDB::bind_method(D_METHOD("get_text_edit"), &SyntaxHighlighter::get_text_edit);
- ClassDB::bind_method(D_METHOD("_get_line_syntax_highlighting", "p_line"), &SyntaxHighlighter::_get_line_syntax_highlighting);
- ClassDB::bind_method(D_METHOD("_update_cache"), &SyntaxHighlighter::_update_cache);
- ClassDB::bind_method(D_METHOD("_clear_highlighting_cache"), &SyntaxHighlighter::_clear_highlighting_cache);
-
- BIND_VMETHOD(MethodInfo(Variant::DICTIONARY, "_get_line_syntax_highlighting", PropertyInfo(Variant::INT, "p_line")));
- BIND_VMETHOD(MethodInfo("_update_cache"));
+ GDVIRTUAL_BIND(_get_line_syntax_highlighting, "line")
+ GDVIRTUAL_BIND(_clear_highlighting_cache)
+ GDVIRTUAL_BIND(_update_cache)
}
////////////////////////////////////////////////////////////////////////////////
@@ -133,7 +124,7 @@ static bool _is_hex_symbol(char32_t c) {
return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
}
-Dictionary CodeHighlighter::_get_line_syntax_highlighting(int p_line) {
+Dictionary CodeHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
Dictionary color_map;
bool prev_is_char = false;
@@ -149,6 +140,13 @@ Dictionary CodeHighlighter::_get_line_syntax_highlighting(int p_line) {
color_region_cache[p_line] = -1;
int in_region = -1;
if (p_line != 0) {
+ int prev_region_line = p_line - 1;
+ while (prev_region_line > 0 && !color_region_cache.has(prev_region_line)) {
+ prev_region_line--;
+ }
+ for (int i = prev_region_line; i < p_line - 1; i++) {
+ get_line_syntax_highlighting(i);
+ }
if (!color_region_cache.has(p_line - 1)) {
get_line_syntax_highlighting(p_line - 1);
}
@@ -401,7 +399,7 @@ void CodeHighlighter::_clear_highlighting_cache() {
}
void CodeHighlighter::_update_cache() {
- font_color = text_edit->get_theme_color("font_color");
+ font_color = text_edit->get_theme_color(SNAME("font_color"));
}
void CodeHighlighter::add_keyword_color(const String &p_keyword, const Color &p_color) {
@@ -525,8 +523,8 @@ void CodeHighlighter::set_color_regions(const Dictionary &p_color_regions) {
List<Variant> keys;
p_color_regions.get_key_list(&keys);
- for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
- String key = E->get();
+ for (const Variant &E : keys) {
+ String key = E;
String start_key = key.get_slice(" ", 0);
String end_key = key.get_slice_count(" ") > 1 ? key.get_slice(" ", 1) : String();
@@ -545,7 +543,7 @@ Dictionary CodeHighlighter::get_color_regions() const {
Dictionary r_color_regions;
for (int i = 0; i < color_regions.size(); i++) {
ColorRegion region = color_regions[i];
- r_color_regions[region.start_key + (region.end_key.empty() ? "" : " " + region.end_key)] = region.color;
+ r_color_regions[region.start_key + (region.end_key.is_empty() ? "" : " " + region.end_key)] = region.color;
}
return r_color_regions;
}
@@ -569,11 +567,11 @@ void CodeHighlighter::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear_member_keyword_colors"), &CodeHighlighter::clear_member_keyword_colors);
ClassDB::bind_method(D_METHOD("get_member_keyword_colors"), &CodeHighlighter::get_member_keyword_colors);
- ClassDB::bind_method(D_METHOD("add_color_region", "p_start_key", "p_end_key", "p_color", "p_line_only"), &CodeHighlighter::add_color_region, DEFVAL(false));
- ClassDB::bind_method(D_METHOD("remove_color_region", "p_start_key"), &CodeHighlighter::remove_color_region);
- ClassDB::bind_method(D_METHOD("has_color_region", "p_start_key"), &CodeHighlighter::has_color_region);
+ ClassDB::bind_method(D_METHOD("add_color_region", "start_key", "end_key", "color", "line_only"), &CodeHighlighter::add_color_region, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("remove_color_region", "start_key"), &CodeHighlighter::remove_color_region);
+ ClassDB::bind_method(D_METHOD("has_color_region", "start_key"), &CodeHighlighter::has_color_region);
- ClassDB::bind_method(D_METHOD("set_color_regions", "p_color_regions"), &CodeHighlighter::set_color_regions);
+ ClassDB::bind_method(D_METHOD("set_color_regions", "color_regions"), &CodeHighlighter::set_color_regions);
ClassDB::bind_method(D_METHOD("clear_color_regions"), &CodeHighlighter::clear_color_regions);
ClassDB::bind_method(D_METHOD("get_color_regions"), &CodeHighlighter::get_color_regions);
diff --git a/scene/resources/syntax_highlighter.h b/scene/resources/syntax_highlighter.h
index 720227a256..0fe39ccff6 100644
--- a/scene/resources/syntax_highlighter.h
+++ b/scene/resources/syntax_highlighter.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,7 +31,9 @@
#ifndef SYNTAX_HIGHLIGHTER_H
#define SYNTAX_HIGHLIGHTER_H
-#include "core/resource.h"
+#include "core/io/resource.h"
+#include "core/object/gdvirtual.gen.inc"
+#include "core/object/script_language.h"
class TextEdit;
@@ -48,9 +50,12 @@ protected:
static void _bind_methods();
+ GDVIRTUAL1RC(Dictionary, _get_line_syntax_highlighting, int)
+ GDVIRTUAL0(_clear_highlighting_cache)
+ GDVIRTUAL0(_update_cache)
public:
Dictionary get_line_syntax_highlighting(int p_line);
- virtual Dictionary _get_line_syntax_highlighting(int p_line) { return Dictionary(); }
+ virtual Dictionary _get_line_syntax_highlighting_impl(int p_line) { return Dictionary(); }
void clear_highlighting_cache();
virtual void _clear_highlighting_cache() {}
@@ -75,7 +80,7 @@ private:
Color color;
String start_key;
String end_key;
- bool line_only;
+ bool line_only = false;
};
Vector<ColorRegion> color_regions;
Map<int, int> color_region_cache;
@@ -93,7 +98,7 @@ protected:
static void _bind_methods();
public:
- virtual Dictionary _get_line_syntax_highlighting(int p_line) override;
+ virtual Dictionary _get_line_syntax_highlighting_impl(int p_line) override;
virtual void _clear_highlighting_cache() override;
virtual void _update_cache() override;
diff --git a/scene/resources/text_file.cpp b/scene/resources/text_file.cpp
index e3bd5ce0ae..33bb0a83e9 100644
--- a/scene/resources/text_file.cpp
+++ b/scene/resources/text_file.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,7 +30,7 @@
#include "text_file.h"
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
bool TextFile::has_text() const {
return text != "";
@@ -55,10 +55,10 @@ Error TextFile::load_text(const String &p_path) {
ERR_FAIL_COND_V_MSG(err, err, "Cannot open TextFile '" + p_path + "'.");
- int len = f->get_len();
+ uint64_t len = f->get_length();
sourcef.resize(len + 1);
uint8_t *w = sourcef.ptrw();
- int r = f->get_buffer(w, len);
+ uint64_t r = f->get_buffer(w, len);
f->close();
memdelete(f);
ERR_FAIL_COND_V(r != len, ERR_CANT_OPEN);
diff --git a/scene/resources/text_file.h b/scene/resources/text_file.h
index fa812b1e67..005075a218 100644
--- a/scene/resources/text_file.h
+++ b/scene/resources/text_file.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp
new file mode 100644
index 0000000000..d2f38ba836
--- /dev/null
+++ b/scene/resources/text_line.cpp
@@ -0,0 +1,423 @@
+/*************************************************************************/
+/* text_line.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 "text_line.h"
+
+void TextLine::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("clear"), &TextLine::clear);
+
+ ClassDB::bind_method(D_METHOD("set_direction", "direction"), &TextLine::set_direction);
+ ClassDB::bind_method(D_METHOD("get_direction"), &TextLine::get_direction);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "direction", PROPERTY_HINT_ENUM, "Auto,Light-to-right,Right-to-left"), "set_direction", "get_direction");
+
+ ClassDB::bind_method(D_METHOD("set_orientation", "orientation"), &TextLine::set_orientation);
+ ClassDB::bind_method(D_METHOD("get_orientation"), &TextLine::get_orientation);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "orientation", PROPERTY_HINT_ENUM, "Horizontal,Orientation"), "set_orientation", "get_orientation");
+
+ ClassDB::bind_method(D_METHOD("set_preserve_invalid", "enabled"), &TextLine::set_preserve_invalid);
+ ClassDB::bind_method(D_METHOD("get_preserve_invalid"), &TextLine::get_preserve_invalid);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "preserve_invalid"), "set_preserve_invalid", "get_preserve_invalid");
+
+ ClassDB::bind_method(D_METHOD("set_preserve_control", "enabled"), &TextLine::set_preserve_control);
+ ClassDB::bind_method(D_METHOD("get_preserve_control"), &TextLine::get_preserve_control);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "preserve_control"), "set_preserve_control", "get_preserve_control");
+
+ ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextLine::_set_bidi_override);
+
+ ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language"), &TextLine::add_string, DEFVAL(Dictionary()), DEFVAL(""));
+ ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextLine::add_object, DEFVAL(INLINE_ALIGN_CENTER), DEFVAL(1));
+ ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextLine::resize_object, DEFVAL(INLINE_ALIGN_CENTER));
+
+ ClassDB::bind_method(D_METHOD("set_width", "width"), &TextLine::set_width);
+ ClassDB::bind_method(D_METHOD("get_width"), &TextLine::get_width);
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width"), "set_width", "get_width");
+
+ ClassDB::bind_method(D_METHOD("set_align", "align"), &TextLine::set_align);
+ ClassDB::bind_method(D_METHOD("get_align"), &TextLine::get_align);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_align", "get_align");
+
+ ClassDB::bind_method(D_METHOD("tab_align", "tab_stops"), &TextLine::tab_align);
+
+ ClassDB::bind_method(D_METHOD("set_flags", "flags"), &TextLine::set_flags);
+ ClassDB::bind_method(D_METHOD("get_flags"), &TextLine::get_flags);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Kashida Justify,Word Justify,Trim Edge Spaces After Justify,Justify Only After Last Tab"), "set_flags", "get_flags");
+
+ ClassDB::bind_method(D_METHOD("set_text_overrun_behavior", "overrun_behavior"), &TextLine::set_text_overrun_behavior);
+ ClassDB::bind_method(D_METHOD("get_text_overrun_behavior"), &TextLine::get_text_overrun_behavior);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis"), "set_text_overrun_behavior", "get_text_overrun_behavior");
+
+ ClassDB::bind_method(D_METHOD("get_objects"), &TextLine::get_objects);
+ ClassDB::bind_method(D_METHOD("get_object_rect", "key"), &TextLine::get_object_rect);
+
+ ClassDB::bind_method(D_METHOD("get_size"), &TextLine::get_size);
+
+ ClassDB::bind_method(D_METHOD("get_rid"), &TextLine::get_rid);
+
+ ClassDB::bind_method(D_METHOD("get_line_ascent"), &TextLine::get_line_ascent);
+ ClassDB::bind_method(D_METHOD("get_line_descent"), &TextLine::get_line_descent);
+ ClassDB::bind_method(D_METHOD("get_line_width"), &TextLine::get_line_width);
+ ClassDB::bind_method(D_METHOD("get_line_underline_position"), &TextLine::get_line_underline_position);
+ ClassDB::bind_method(D_METHOD("get_line_underline_thickness"), &TextLine::get_line_underline_thickness);
+
+ ClassDB::bind_method(D_METHOD("draw", "canvas", "pos", "color"), &TextLine::draw, DEFVAL(Color(1, 1, 1)));
+ ClassDB::bind_method(D_METHOD("draw_outline", "canvas", "pos", "outline_size", "color"), &TextLine::draw_outline, DEFVAL(1), DEFVAL(Color(1, 1, 1)));
+
+ ClassDB::bind_method(D_METHOD("hit_test", "coords"), &TextLine::hit_test);
+
+ BIND_ENUM_CONSTANT(OVERRUN_NO_TRIMMING);
+ BIND_ENUM_CONSTANT(OVERRUN_TRIM_CHAR);
+ BIND_ENUM_CONSTANT(OVERRUN_TRIM_WORD);
+ BIND_ENUM_CONSTANT(OVERRUN_TRIM_ELLIPSIS);
+ BIND_ENUM_CONSTANT(OVERRUN_TRIM_WORD_ELLIPSIS);
+}
+
+void TextLine::_shape() {
+ if (dirty) {
+ if (!tab_stops.is_empty()) {
+ TS->shaped_text_tab_align(rid, tab_stops);
+ }
+
+ uint8_t overrun_flags = TextServer::OVERRUN_NO_TRIMMING;
+ if (overrun_behavior != OVERRUN_NO_TRIMMING) {
+ switch (overrun_behavior) {
+ case OVERRUN_TRIM_WORD_ELLIPSIS:
+ overrun_flags |= TextServer::OVERRUN_TRIM;
+ overrun_flags |= TextServer::OVERRUN_TRIM_WORD_ONLY;
+ overrun_flags |= TextServer::OVERRUN_ADD_ELLIPSIS;
+ break;
+ case OVERRUN_TRIM_ELLIPSIS:
+ overrun_flags |= TextServer::OVERRUN_TRIM;
+ overrun_flags |= TextServer::OVERRUN_ADD_ELLIPSIS;
+ break;
+ case OVERRUN_TRIM_WORD:
+ overrun_flags |= TextServer::OVERRUN_TRIM;
+ overrun_flags |= TextServer::OVERRUN_TRIM_WORD_ONLY;
+ break;
+ case OVERRUN_TRIM_CHAR:
+ overrun_flags |= TextServer::OVERRUN_TRIM;
+ break;
+ case OVERRUN_NO_TRIMMING:
+ break;
+ }
+
+ if (align == HALIGN_FILL) {
+ TS->shaped_text_fit_to_width(rid, width, flags);
+ overrun_flags |= TextServer::OVERRUN_JUSTIFICATION_AWARE;
+ TS->shaped_text_overrun_trim_to_width(rid, width, overrun_flags);
+ } else {
+ TS->shaped_text_overrun_trim_to_width(rid, width, overrun_flags);
+ }
+ } else if (align == HALIGN_FILL) {
+ TS->shaped_text_fit_to_width(rid, width, flags);
+ }
+ dirty = false;
+ }
+}
+
+RID TextLine::get_rid() const {
+ return rid;
+}
+
+void TextLine::clear() {
+ TS->shaped_text_clear(rid);
+ spacing_top = 0;
+ spacing_bottom = 0;
+}
+
+void TextLine::set_preserve_invalid(bool p_enabled) {
+ TS->shaped_text_set_preserve_invalid(rid, p_enabled);
+ dirty = true;
+}
+
+bool TextLine::get_preserve_invalid() const {
+ return TS->shaped_text_get_preserve_invalid(rid);
+}
+
+void TextLine::set_preserve_control(bool p_enabled) {
+ TS->shaped_text_set_preserve_control(rid, p_enabled);
+ dirty = true;
+}
+
+bool TextLine::get_preserve_control() const {
+ return TS->shaped_text_get_preserve_control(rid);
+}
+
+void TextLine::set_direction(TextServer::Direction p_direction) {
+ TS->shaped_text_set_direction(rid, p_direction);
+ dirty = true;
+}
+
+TextServer::Direction TextLine::get_direction() const {
+ return TS->shaped_text_get_direction(rid);
+}
+
+void TextLine::set_orientation(TextServer::Orientation p_orientation) {
+ TS->shaped_text_set_orientation(rid, p_orientation);
+ dirty = true;
+}
+
+TextServer::Orientation TextLine::get_orientation() const {
+ return TS->shaped_text_get_orientation(rid);
+}
+
+void TextLine::_set_bidi_override(const Array &p_override) {
+ Vector<Vector2i> overrides;
+ for (int i = 0; i < p_override.size(); i++) {
+ overrides.push_back(p_override[i]);
+ }
+ set_bidi_override(overrides);
+}
+
+void TextLine::set_bidi_override(const Vector<Vector2i> &p_override) {
+ TS->shaped_text_set_bidi_override(rid, p_override);
+ dirty = true;
+}
+
+bool TextLine::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
+ ERR_FAIL_COND_V(p_fonts.is_null(), false);
+ bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
+ spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP);
+ spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM);
+ dirty = true;
+ return res;
+}
+
+bool TextLine::add_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align, int p_length) {
+ bool res = TS->shaped_text_add_object(rid, p_key, p_size, p_inline_align, p_length);
+ dirty = true;
+ return res;
+}
+
+bool TextLine::resize_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align) {
+ const_cast<TextLine *>(this)->_shape();
+ return TS->shaped_text_resize_object(rid, p_key, p_size, p_inline_align);
+}
+
+Array TextLine::get_objects() const {
+ return TS->shaped_text_get_objects(rid);
+}
+
+Rect2 TextLine::get_object_rect(Variant p_key) const {
+ return TS->shaped_text_get_object_rect(rid, p_key);
+}
+
+void TextLine::set_align(HAlign p_align) {
+ if (align != p_align) {
+ if (align == HALIGN_FILL || p_align == HALIGN_FILL) {
+ align = p_align;
+ dirty = true;
+ } else {
+ align = p_align;
+ }
+ }
+}
+
+HAlign TextLine::get_align() const {
+ return align;
+}
+
+void TextLine::tab_align(const Vector<float> &p_tab_stops) {
+ tab_stops = p_tab_stops;
+ dirty = true;
+}
+
+void TextLine::set_flags(uint8_t p_flags) {
+ if (flags != p_flags) {
+ flags = p_flags;
+ dirty = true;
+ }
+}
+
+uint8_t TextLine::get_flags() const {
+ return flags;
+}
+
+void TextLine::set_text_overrun_behavior(TextLine::OverrunBehavior p_behavior) {
+ if (overrun_behavior != p_behavior) {
+ overrun_behavior = p_behavior;
+ dirty = true;
+ }
+}
+
+TextLine::OverrunBehavior TextLine::get_text_overrun_behavior() const {
+ return overrun_behavior;
+}
+
+void TextLine::set_width(float p_width) {
+ width = p_width;
+ if (align == HALIGN_FILL || overrun_behavior != OVERRUN_NO_TRIMMING) {
+ dirty = true;
+ }
+}
+
+float TextLine::get_width() const {
+ return width;
+}
+
+Size2 TextLine::get_size() const {
+ const_cast<TextLine *>(this)->_shape();
+ if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
+ return Size2(TS->shaped_text_get_size(rid).x, TS->shaped_text_get_size(rid).y + spacing_top + spacing_bottom);
+ } else {
+ return Size2(TS->shaped_text_get_size(rid).x + spacing_top + spacing_bottom, TS->shaped_text_get_size(rid).y);
+ }
+}
+
+float TextLine::get_line_ascent() const {
+ const_cast<TextLine *>(this)->_shape();
+ return TS->shaped_text_get_ascent(rid) + spacing_top;
+}
+
+float TextLine::get_line_descent() const {
+ const_cast<TextLine *>(this)->_shape();
+ return TS->shaped_text_get_descent(rid) + spacing_bottom;
+}
+
+float TextLine::get_line_width() const {
+ const_cast<TextLine *>(this)->_shape();
+ return TS->shaped_text_get_width(rid);
+}
+
+float TextLine::get_line_underline_position() const {
+ const_cast<TextLine *>(this)->_shape();
+ return TS->shaped_text_get_underline_position(rid);
+}
+
+float TextLine::get_line_underline_thickness() const {
+ const_cast<TextLine *>(this)->_shape();
+ return TS->shaped_text_get_underline_thickness(rid);
+}
+
+void TextLine::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_color) const {
+ const_cast<TextLine *>(this)->_shape();
+
+ Vector2 ofs = p_pos;
+
+ float length = TS->shaped_text_get_width(rid);
+ if (width > 0) {
+ switch (align) {
+ case HALIGN_FILL:
+ case HALIGN_LEFT:
+ break;
+ case HALIGN_CENTER: {
+ if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += Math::floor((width - length) / 2.0);
+ } else {
+ ofs.y += Math::floor((width - length) / 2.0);
+ }
+ } break;
+ case HALIGN_RIGHT: {
+ if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += width - length;
+ } else {
+ ofs.y += width - length;
+ }
+ } break;
+ }
+ }
+
+ float clip_l;
+ if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.y += TS->shaped_text_get_ascent(rid) + spacing_top;
+ clip_l = MAX(0, p_pos.x - ofs.x);
+ } else {
+ ofs.x += TS->shaped_text_get_ascent(rid) + spacing_top;
+ clip_l = MAX(0, p_pos.y - ofs.y);
+ }
+ return TS->shaped_text_draw(rid, p_canvas, ofs, clip_l, clip_l + width, p_color);
+}
+
+void TextLine::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size, const Color &p_color) const {
+ const_cast<TextLine *>(this)->_shape();
+
+ Vector2 ofs = p_pos;
+
+ float length = TS->shaped_text_get_width(rid);
+ if (width > 0) {
+ switch (align) {
+ case HALIGN_FILL:
+ case HALIGN_LEFT:
+ break;
+ case HALIGN_CENTER: {
+ if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += Math::floor((width - length) / 2.0);
+ } else {
+ ofs.y += Math::floor((width - length) / 2.0);
+ }
+ } break;
+ case HALIGN_RIGHT: {
+ if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += width - length;
+ } else {
+ ofs.y += width - length;
+ }
+ } break;
+ }
+ }
+
+ float clip_l;
+ if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.y += TS->shaped_text_get_ascent(rid) + spacing_top;
+ clip_l = MAX(0, p_pos.x - ofs.x);
+ } else {
+ ofs.x += TS->shaped_text_get_ascent(rid) + spacing_top;
+ clip_l = MAX(0, p_pos.y - ofs.y);
+ }
+ return TS->shaped_text_draw_outline(rid, p_canvas, ofs, clip_l, clip_l + width, p_outline_size, p_color);
+}
+
+int TextLine::hit_test(float p_coords) const {
+ const_cast<TextLine *>(this)->_shape();
+
+ return TS->shaped_text_hit_test_position(rid, p_coords);
+}
+
+TextLine::TextLine(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
+ rid = TS->create_shaped_text(p_direction, p_orientation);
+ spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP);
+ spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM);
+ TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
+}
+
+TextLine::TextLine() {
+ rid = TS->create_shaped_text();
+}
+
+TextLine::~TextLine() {
+ TS->free(rid);
+}
diff --git a/scene/resources/text_line.h b/scene/resources/text_line.h
new file mode 100644
index 0000000000..9ed9c2f177
--- /dev/null
+++ b/scene/resources/text_line.h
@@ -0,0 +1,132 @@
+/*************************************************************************/
+/* text_line.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 TEXT_LINE_H
+#define TEXT_LINE_H
+
+#include "scene/resources/font.h"
+#include "servers/text_server.h"
+
+/*************************************************************************/
+
+class TextLine : public RefCounted {
+ GDCLASS(TextLine, RefCounted);
+
+public:
+ enum OverrunBehavior {
+ OVERRUN_NO_TRIMMING,
+ OVERRUN_TRIM_CHAR,
+ OVERRUN_TRIM_WORD,
+ OVERRUN_TRIM_ELLIPSIS,
+ OVERRUN_TRIM_WORD_ELLIPSIS,
+ };
+
+private:
+ RID rid;
+ int spacing_top = 0;
+ int spacing_bottom = 0;
+
+ bool dirty = true;
+
+ float width = -1.0;
+ uint8_t flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA;
+ HAlign align = HALIGN_LEFT;
+ OverrunBehavior overrun_behavior = OVERRUN_TRIM_ELLIPSIS;
+
+ Vector<float> tab_stops;
+
+protected:
+ static void _bind_methods();
+
+ void _shape();
+
+public:
+ RID get_rid() const;
+
+ void clear();
+
+ void set_direction(TextServer::Direction p_direction);
+ TextServer::Direction get_direction() const;
+
+ void set_bidi_override(const Vector<Vector2i> &p_override);
+
+ void set_orientation(TextServer::Orientation p_orientation);
+ TextServer::Orientation get_orientation() const;
+
+ void set_preserve_invalid(bool p_enabled);
+ bool get_preserve_invalid() const;
+
+ void set_preserve_control(bool p_enabled);
+ bool get_preserve_control() const;
+
+ bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "");
+ bool add_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER, int p_length = 1);
+ bool resize_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER);
+
+ void set_align(HAlign p_align);
+ HAlign get_align() const;
+
+ void tab_align(const Vector<float> &p_tab_stops);
+
+ void set_flags(uint8_t p_flags);
+ uint8_t get_flags() const;
+
+ void set_text_overrun_behavior(OverrunBehavior p_behavior);
+ OverrunBehavior get_text_overrun_behavior() const;
+
+ void set_width(float p_width);
+ float get_width() const;
+
+ Array get_objects() const;
+ Rect2 get_object_rect(Variant p_key) const;
+
+ Size2 get_size() const;
+
+ float get_line_ascent() const;
+ float get_line_descent() const;
+ float get_line_width() const;
+ float get_line_underline_position() const;
+ float get_line_underline_thickness() const;
+
+ void draw(RID p_canvas, const Vector2 &p_pos, const Color &p_color = Color(1, 1, 1)) const;
+ void draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size = 1, const Color &p_color = Color(1, 1, 1)) const;
+
+ int hit_test(float p_coords) const;
+
+ void _set_bidi_override(const Array &p_override);
+
+ TextLine(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL);
+ TextLine();
+ ~TextLine();
+};
+
+VARIANT_ENUM_CAST(TextLine::OverrunBehavior);
+
+#endif // TEXT_LINE_H
diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp
new file mode 100644
index 0000000000..62949b9b98
--- /dev/null
+++ b/scene/resources/text_paragraph.cpp
@@ -0,0 +1,849 @@
+/*************************************************************************/
+/* text_paragraph.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/text_paragraph.h"
+
+void TextParagraph::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("clear"), &TextParagraph::clear);
+
+ ClassDB::bind_method(D_METHOD("set_direction", "direction"), &TextParagraph::set_direction);
+ ClassDB::bind_method(D_METHOD("get_direction"), &TextParagraph::get_direction);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "direction", PROPERTY_HINT_ENUM, "Auto,Light-to-right,Right-to-left"), "set_direction", "get_direction");
+
+ ClassDB::bind_method(D_METHOD("set_orientation", "orientation"), &TextParagraph::set_orientation);
+ ClassDB::bind_method(D_METHOD("get_orientation"), &TextParagraph::get_orientation);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "orientation", PROPERTY_HINT_ENUM, "Horizontal,Orientation"), "set_orientation", "get_orientation");
+
+ ClassDB::bind_method(D_METHOD("set_preserve_invalid", "enabled"), &TextParagraph::set_preserve_invalid);
+ ClassDB::bind_method(D_METHOD("get_preserve_invalid"), &TextParagraph::get_preserve_invalid);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "preserve_invalid"), "set_preserve_invalid", "get_preserve_invalid");
+
+ ClassDB::bind_method(D_METHOD("set_preserve_control", "enabled"), &TextParagraph::set_preserve_control);
+ ClassDB::bind_method(D_METHOD("get_preserve_control"), &TextParagraph::get_preserve_control);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "preserve_control"), "set_preserve_control", "get_preserve_control");
+
+ ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextParagraph::_set_bidi_override);
+
+ ClassDB::bind_method(D_METHOD("set_dropcap", "text", "fonts", "size", "dropcap_margins", "opentype_features", "language"), &TextParagraph::set_dropcap, DEFVAL(Rect2()), DEFVAL(Dictionary()), DEFVAL(""));
+ ClassDB::bind_method(D_METHOD("clear_dropcap"), &TextParagraph::clear_dropcap);
+
+ ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language"), &TextParagraph::add_string, DEFVAL(Dictionary()), DEFVAL(""));
+ ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextParagraph::add_object, DEFVAL(INLINE_ALIGN_CENTER), DEFVAL(1));
+ ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextParagraph::resize_object, DEFVAL(INLINE_ALIGN_CENTER));
+
+ ClassDB::bind_method(D_METHOD("set_align", "align"), &TextParagraph::set_align);
+ ClassDB::bind_method(D_METHOD("get_align"), &TextParagraph::get_align);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_align", "get_align");
+
+ ClassDB::bind_method(D_METHOD("tab_align", "tab_stops"), &TextParagraph::tab_align);
+
+ ClassDB::bind_method(D_METHOD("set_flags", "flags"), &TextParagraph::set_flags);
+ ClassDB::bind_method(D_METHOD("get_flags"), &TextParagraph::get_flags);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Kashida Justify,Word Justify,Trim Edge Spaces After Justify,Justify Only After Last Tab,Break Mandatory,Break Words,Break Graphemes"), "set_flags", "get_flags");
+
+ ClassDB::bind_method(D_METHOD("set_text_overrun_behavior", "overrun_behavior"), &TextParagraph::set_text_overrun_behavior);
+ ClassDB::bind_method(D_METHOD("get_text_overrun_behavior"), &TextParagraph::get_text_overrun_behavior);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis"), "set_text_overrun_behavior", "get_text_overrun_behavior");
+
+ ClassDB::bind_method(D_METHOD("set_width", "width"), &TextParagraph::set_width);
+ ClassDB::bind_method(D_METHOD("get_width"), &TextParagraph::get_width);
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width"), "set_width", "get_width");
+
+ ClassDB::bind_method(D_METHOD("get_non_wraped_size"), &TextParagraph::get_non_wraped_size);
+ ClassDB::bind_method(D_METHOD("get_size"), &TextParagraph::get_size);
+
+ ClassDB::bind_method(D_METHOD("get_rid"), &TextParagraph::get_rid);
+ ClassDB::bind_method(D_METHOD("get_line_rid", "line"), &TextParagraph::get_line_rid);
+ ClassDB::bind_method(D_METHOD("get_dropcap_rid"), &TextParagraph::get_dropcap_rid);
+
+ ClassDB::bind_method(D_METHOD("get_line_count"), &TextParagraph::get_line_count);
+
+ ClassDB::bind_method(D_METHOD("set_max_lines_visible", "max_lines_visible"), &TextParagraph::set_max_lines_visible);
+ ClassDB::bind_method(D_METHOD("get_max_lines_visible"), &TextParagraph::get_max_lines_visible);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "max_lines_visible"), "set_max_lines_visible", "get_max_lines_visible");
+
+ ClassDB::bind_method(D_METHOD("get_line_objects", "line"), &TextParagraph::get_line_objects);
+ ClassDB::bind_method(D_METHOD("get_line_object_rect", "line", "key"), &TextParagraph::get_line_object_rect);
+ ClassDB::bind_method(D_METHOD("get_line_size", "line"), &TextParagraph::get_line_size);
+ ClassDB::bind_method(D_METHOD("get_line_range", "line"), &TextParagraph::get_line_range);
+ ClassDB::bind_method(D_METHOD("get_line_ascent", "line"), &TextParagraph::get_line_ascent);
+ ClassDB::bind_method(D_METHOD("get_line_descent", "line"), &TextParagraph::get_line_descent);
+ ClassDB::bind_method(D_METHOD("get_line_width", "line"), &TextParagraph::get_line_width);
+ ClassDB::bind_method(D_METHOD("get_line_underline_position", "line"), &TextParagraph::get_line_underline_position);
+ ClassDB::bind_method(D_METHOD("get_line_underline_thickness", "line"), &TextParagraph::get_line_underline_thickness);
+
+ ClassDB::bind_method(D_METHOD("get_spacing_top"), &TextParagraph::get_spacing_top);
+ ClassDB::bind_method(D_METHOD("get_spacing_bottom"), &TextParagraph::get_spacing_bottom);
+
+ ClassDB::bind_method(D_METHOD("get_dropcap_size"), &TextParagraph::get_dropcap_size);
+ ClassDB::bind_method(D_METHOD("get_dropcap_lines"), &TextParagraph::get_dropcap_lines);
+
+ ClassDB::bind_method(D_METHOD("draw", "canvas", "pos", "color", "dc_color"), &TextParagraph::draw, DEFVAL(Color(1, 1, 1)), DEFVAL(Color(1, 1, 1)));
+ ClassDB::bind_method(D_METHOD("draw_outline", "canvas", "pos", "outline_size", "color", "dc_color"), &TextParagraph::draw_outline, DEFVAL(1), DEFVAL(Color(1, 1, 1)), DEFVAL(Color(1, 1, 1)));
+
+ ClassDB::bind_method(D_METHOD("draw_line", "canvas", "pos", "line", "color"), &TextParagraph::draw_line, DEFVAL(Color(1, 1, 1)));
+ ClassDB::bind_method(D_METHOD("draw_line_outline", "canvas", "pos", "line", "outline_size", "color"), &TextParagraph::draw_line_outline, DEFVAL(1), DEFVAL(Color(1, 1, 1)));
+
+ ClassDB::bind_method(D_METHOD("draw_dropcap", "canvas", "pos", "color"), &TextParagraph::draw_dropcap, DEFVAL(Color(1, 1, 1)));
+ ClassDB::bind_method(D_METHOD("draw_dropcap_outline", "canvas", "pos", "outline_size", "color"), &TextParagraph::draw_dropcap_outline, DEFVAL(1), DEFVAL(Color(1, 1, 1)));
+
+ ClassDB::bind_method(D_METHOD("hit_test", "coords"), &TextParagraph::hit_test);
+
+ BIND_ENUM_CONSTANT(OVERRUN_NO_TRIMMING);
+ BIND_ENUM_CONSTANT(OVERRUN_TRIM_CHAR);
+ BIND_ENUM_CONSTANT(OVERRUN_TRIM_WORD);
+ BIND_ENUM_CONSTANT(OVERRUN_TRIM_ELLIPSIS);
+ BIND_ENUM_CONSTANT(OVERRUN_TRIM_WORD_ELLIPSIS);
+}
+
+void TextParagraph::_shape_lines() {
+ if (lines_dirty) {
+ for (int i = 0; i < lines_rid.size(); i++) {
+ TS->free(lines_rid[i]);
+ }
+ lines_rid.clear();
+
+ if (!tab_stops.is_empty()) {
+ TS->shaped_text_tab_align(rid, tab_stops);
+ }
+
+ float h_offset = 0.f;
+ float v_offset = 0.f;
+ int start = 0;
+ dropcap_lines = 0;
+
+ if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
+ h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x;
+ v_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y;
+ } else {
+ h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y;
+ v_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x;
+ }
+
+ if (h_offset > 0) {
+ // Dropcap, flow around.
+ Vector<Vector2i> line_breaks = TS->shaped_text_get_line_breaks(rid, width - h_offset, 0, flags);
+ for (int i = 0; i < line_breaks.size(); i++) {
+ RID line = TS->shaped_text_substr(rid, line_breaks[i].x, line_breaks[i].y - line_breaks[i].x);
+ float h = (TS->shaped_text_get_orientation(line) == TextServer::ORIENTATION_HORIZONTAL) ? TS->shaped_text_get_size(line).y : TS->shaped_text_get_size(line).x;
+ if (v_offset < h) {
+ TS->free(line);
+ break;
+ }
+ if (!tab_stops.is_empty()) {
+ TS->shaped_text_tab_align(line, tab_stops);
+ }
+ dropcap_lines++;
+ v_offset -= h;
+ start = line_breaks[i].y;
+ lines_rid.push_back(line);
+ }
+ }
+ // Use fixed for the rest of lines.
+ Vector<Vector2i> line_breaks = TS->shaped_text_get_line_breaks(rid, width, start, flags);
+ for (int i = 0; i < line_breaks.size(); i++) {
+ RID line = TS->shaped_text_substr(rid, line_breaks[i].x, line_breaks[i].y - line_breaks[i].x);
+ if (!tab_stops.is_empty()) {
+ TS->shaped_text_tab_align(line, tab_stops);
+ }
+ lines_rid.push_back(line);
+ }
+
+ uint8_t overrun_flags = TextServer::OVERRUN_NO_TRIMMING;
+ if (overrun_behavior != OVERRUN_NO_TRIMMING) {
+ switch (overrun_behavior) {
+ case OVERRUN_TRIM_WORD_ELLIPSIS:
+ overrun_flags |= TextServer::OVERRUN_TRIM;
+ overrun_flags |= TextServer::OVERRUN_TRIM_WORD_ONLY;
+ overrun_flags |= TextServer::OVERRUN_ADD_ELLIPSIS;
+ break;
+ case OVERRUN_TRIM_ELLIPSIS:
+ overrun_flags |= TextServer::OVERRUN_TRIM;
+ overrun_flags |= TextServer::OVERRUN_ADD_ELLIPSIS;
+ break;
+ case OVERRUN_TRIM_WORD:
+ overrun_flags |= TextServer::OVERRUN_TRIM;
+ overrun_flags |= TextServer::OVERRUN_TRIM_WORD_ONLY;
+ break;
+ case OVERRUN_TRIM_CHAR:
+ overrun_flags |= TextServer::OVERRUN_TRIM;
+ break;
+ case OVERRUN_NO_TRIMMING:
+ break;
+ }
+ }
+
+ bool autowrap_enabled = ((flags & TextServer::BREAK_WORD_BOUND) == TextServer::BREAK_WORD_BOUND) || ((flags & TextServer::BREAK_GRAPHEME_BOUND) == TextServer::BREAK_GRAPHEME_BOUND);
+
+ // Fill after min_size calculation.
+ if (autowrap_enabled) {
+ int visible_lines = (max_lines_visible >= 0) ? MIN(max_lines_visible, lines_rid.size()) : lines_rid.size();
+ bool lines_hidden = visible_lines > 0 && visible_lines < lines_rid.size();
+ if (lines_hidden) {
+ overrun_flags |= TextServer::OVERRUN_ENFORCE_ELLIPSIS;
+ }
+ if (align == HALIGN_FILL) {
+ for (int i = 0; i < lines_rid.size(); i++) {
+ if (i < visible_lines - 1 || lines_rid.size() == 1) {
+ TS->shaped_text_fit_to_width(lines_rid[i], width, flags);
+ } else if (i == (visible_lines - 1)) {
+ TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], width, overrun_flags);
+ }
+ }
+
+ } else if (lines_hidden) {
+ TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], width, overrun_flags);
+ }
+
+ } else {
+ // Autowrap disabled.
+ for (int i = 0; i < lines_rid.size(); i++) {
+ if (align == HALIGN_FILL) {
+ TS->shaped_text_fit_to_width(lines_rid[i], width, flags);
+ overrun_flags |= TextServer::OVERRUN_JUSTIFICATION_AWARE;
+ TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags);
+ TS->shaped_text_fit_to_width(lines_rid[i], width, flags | TextServer::JUSTIFICATION_CONSTRAIN_ELLIPSIS);
+ } else {
+ TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags);
+ }
+ }
+ }
+ lines_dirty = false;
+ }
+}
+
+RID TextParagraph::get_rid() const {
+ return rid;
+}
+
+RID TextParagraph::get_line_rid(int p_line) const {
+ const_cast<TextParagraph *>(this)->_shape_lines();
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), RID());
+ return lines_rid[p_line];
+}
+
+RID TextParagraph::get_dropcap_rid() const {
+ return dropcap_rid;
+}
+
+void TextParagraph::clear() {
+ spacing_top = 0;
+ spacing_bottom = 0;
+ for (int i = 0; i < lines_rid.size(); i++) {
+ TS->free(lines_rid[i]);
+ }
+ lines_rid.clear();
+ TS->shaped_text_clear(rid);
+ TS->shaped_text_clear(dropcap_rid);
+}
+
+void TextParagraph::set_preserve_invalid(bool p_enabled) {
+ TS->shaped_text_set_preserve_invalid(rid, p_enabled);
+ TS->shaped_text_set_preserve_invalid(dropcap_rid, p_enabled);
+ lines_dirty = true;
+}
+
+bool TextParagraph::get_preserve_invalid() const {
+ return TS->shaped_text_get_preserve_invalid(rid);
+}
+
+void TextParagraph::set_preserve_control(bool p_enabled) {
+ TS->shaped_text_set_preserve_control(rid, p_enabled);
+ TS->shaped_text_set_preserve_control(dropcap_rid, p_enabled);
+ lines_dirty = true;
+}
+
+bool TextParagraph::get_preserve_control() const {
+ return TS->shaped_text_get_preserve_control(rid);
+}
+
+void TextParagraph::set_direction(TextServer::Direction p_direction) {
+ TS->shaped_text_set_direction(rid, p_direction);
+ TS->shaped_text_set_direction(dropcap_rid, p_direction);
+ lines_dirty = true;
+}
+
+TextServer::Direction TextParagraph::get_direction() const {
+ const_cast<TextParagraph *>(this)->_shape_lines();
+ return TS->shaped_text_get_direction(rid);
+}
+
+void TextParagraph::set_orientation(TextServer::Orientation p_orientation) {
+ TS->shaped_text_set_orientation(rid, p_orientation);
+ TS->shaped_text_set_orientation(dropcap_rid, p_orientation);
+ lines_dirty = true;
+}
+
+TextServer::Orientation TextParagraph::get_orientation() const {
+ const_cast<TextParagraph *>(this)->_shape_lines();
+ return TS->shaped_text_get_orientation(rid);
+}
+
+bool TextParagraph::set_dropcap(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Rect2 &p_dropcap_margins, const Dictionary &p_opentype_features, const String &p_language) {
+ ERR_FAIL_COND_V(p_fonts.is_null(), false);
+ TS->shaped_text_clear(dropcap_rid);
+ dropcap_margins = p_dropcap_margins;
+ bool res = TS->shaped_text_add_string(dropcap_rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
+ lines_dirty = true;
+ return res;
+}
+
+void TextParagraph::clear_dropcap() {
+ dropcap_margins = Rect2();
+ TS->shaped_text_clear(dropcap_rid);
+ lines_dirty = true;
+}
+
+bool TextParagraph::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
+ ERR_FAIL_COND_V(p_fonts.is_null(), false);
+ bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
+ spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP);
+ spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM);
+ lines_dirty = true;
+ return res;
+}
+
+int TextParagraph::get_spacing_top() const {
+ return spacing_top;
+}
+
+int TextParagraph::get_spacing_bottom() const {
+ return spacing_bottom;
+}
+
+void TextParagraph::_set_bidi_override(const Array &p_override) {
+ Vector<Vector2i> overrides;
+ for (int i = 0; i < p_override.size(); i++) {
+ overrides.push_back(p_override[i]);
+ }
+ set_bidi_override(overrides);
+}
+
+void TextParagraph::set_bidi_override(const Vector<Vector2i> &p_override) {
+ TS->shaped_text_set_bidi_override(rid, p_override);
+ lines_dirty = true;
+}
+
+bool TextParagraph::add_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align, int p_length) {
+ bool res = TS->shaped_text_add_object(rid, p_key, p_size, p_inline_align, p_length);
+ lines_dirty = true;
+ return res;
+}
+
+bool TextParagraph::resize_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align) {
+ bool res = TS->shaped_text_resize_object(rid, p_key, p_size, p_inline_align);
+ lines_dirty = true;
+ return res;
+}
+
+void TextParagraph::set_align(HAlign p_align) {
+ if (align != p_align) {
+ if (align == HALIGN_FILL || p_align == HALIGN_FILL) {
+ align = p_align;
+ lines_dirty = true;
+ } else {
+ align = p_align;
+ }
+ }
+}
+
+HAlign TextParagraph::get_align() const {
+ return align;
+}
+
+void TextParagraph::tab_align(const Vector<float> &p_tab_stops) {
+ tab_stops = p_tab_stops;
+ lines_dirty = true;
+}
+
+void TextParagraph::set_flags(uint8_t p_flags) {
+ if (flags != p_flags) {
+ flags = p_flags;
+ lines_dirty = true;
+ }
+}
+
+uint8_t TextParagraph::get_flags() const {
+ return flags;
+}
+
+void TextParagraph::set_text_overrun_behavior(TextParagraph::OverrunBehavior p_behavior) {
+ if (overrun_behavior != p_behavior) {
+ overrun_behavior = p_behavior;
+ lines_dirty = true;
+ }
+}
+
+TextParagraph::OverrunBehavior TextParagraph::get_text_overrun_behavior() const {
+ return overrun_behavior;
+}
+
+void TextParagraph::set_width(float p_width) {
+ if (width != p_width) {
+ width = p_width;
+ lines_dirty = true;
+ }
+}
+
+float TextParagraph::get_width() const {
+ return width;
+}
+
+Size2 TextParagraph::get_non_wraped_size() const {
+ const_cast<TextParagraph *>(this)->_shape_lines();
+ if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
+ return Size2(TS->shaped_text_get_size(rid).x, TS->shaped_text_get_size(rid).y + spacing_top + spacing_bottom);
+ } else {
+ return Size2(TS->shaped_text_get_size(rid).x + spacing_top + spacing_bottom, TS->shaped_text_get_size(rid).y);
+ }
+}
+
+Size2 TextParagraph::get_size() const {
+ const_cast<TextParagraph *>(this)->_shape_lines();
+ Size2 size;
+ int visible_lines = (max_lines_visible >= 0) ? MIN(max_lines_visible, lines_rid.size()) : lines_rid.size();
+ for (int i = 0; i < visible_lines; i++) {
+ Size2 lsize = TS->shaped_text_get_size(lines_rid[i]);
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ size.x = MAX(size.x, lsize.x);
+ size.y += lsize.y + spacing_top + spacing_bottom;
+ } else {
+ size.x += lsize.x + spacing_top + spacing_bottom;
+ size.y = MAX(size.y, lsize.y);
+ }
+ }
+ return size;
+}
+
+int TextParagraph::get_line_count() const {
+ const_cast<TextParagraph *>(this)->_shape_lines();
+ return lines_rid.size();
+}
+
+void TextParagraph::set_max_lines_visible(int p_lines) {
+ if (p_lines != max_lines_visible) {
+ max_lines_visible = p_lines;
+ lines_dirty = true;
+ }
+}
+
+int TextParagraph::get_max_lines_visible() const {
+ return max_lines_visible;
+}
+
+Array TextParagraph::get_line_objects(int p_line) const {
+ const_cast<TextParagraph *>(this)->_shape_lines();
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), Array());
+ return TS->shaped_text_get_objects(lines_rid[p_line]);
+}
+
+Rect2 TextParagraph::get_line_object_rect(int p_line, Variant p_key) const {
+ const_cast<TextParagraph *>(this)->_shape_lines();
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), Rect2());
+ Rect2 xrect = TS->shaped_text_get_object_rect(lines_rid[p_line], p_key);
+ for (int i = 0; i < p_line; i++) {
+ Size2 lsize = TS->shaped_text_get_size(lines_rid[i]);
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ xrect.position.y += lsize.y + spacing_top + spacing_bottom;
+ } else {
+ xrect.position.x += lsize.x + spacing_top + spacing_bottom;
+ }
+ }
+ return xrect;
+}
+
+Size2 TextParagraph::get_line_size(int p_line) const {
+ const_cast<TextParagraph *>(this)->_shape_lines();
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), Size2());
+ if (TS->shaped_text_get_orientation(lines_rid[p_line]) == TextServer::ORIENTATION_HORIZONTAL) {
+ return Size2(TS->shaped_text_get_size(lines_rid[p_line]).x, TS->shaped_text_get_size(lines_rid[p_line]).y + spacing_top + spacing_bottom);
+ } else {
+ return Size2(TS->shaped_text_get_size(lines_rid[p_line]).x + spacing_top + spacing_bottom, TS->shaped_text_get_size(lines_rid[p_line]).y);
+ }
+}
+
+Vector2i TextParagraph::get_line_range(int p_line) const {
+ const_cast<TextParagraph *>(this)->_shape_lines();
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), Vector2i());
+ return TS->shaped_text_get_range(lines_rid[p_line]);
+}
+
+float TextParagraph::get_line_ascent(int p_line) const {
+ const_cast<TextParagraph *>(this)->_shape_lines();
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), 0.f);
+ return TS->shaped_text_get_ascent(lines_rid[p_line]) + spacing_top;
+}
+
+float TextParagraph::get_line_descent(int p_line) const {
+ const_cast<TextParagraph *>(this)->_shape_lines();
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), 0.f);
+ return TS->shaped_text_get_descent(lines_rid[p_line]) + spacing_bottom;
+}
+
+float TextParagraph::get_line_width(int p_line) const {
+ const_cast<TextParagraph *>(this)->_shape_lines();
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), 0.f);
+ return TS->shaped_text_get_width(lines_rid[p_line]);
+}
+
+float TextParagraph::get_line_underline_position(int p_line) const {
+ const_cast<TextParagraph *>(this)->_shape_lines();
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), 0.f);
+ return TS->shaped_text_get_underline_position(lines_rid[p_line]);
+}
+
+float TextParagraph::get_line_underline_thickness(int p_line) const {
+ const_cast<TextParagraph *>(this)->_shape_lines();
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), 0.f);
+ return TS->shaped_text_get_underline_thickness(lines_rid[p_line]);
+}
+
+Size2 TextParagraph::get_dropcap_size() const {
+ return TS->shaped_text_get_size(dropcap_rid) + dropcap_margins.size + dropcap_margins.position;
+}
+
+int TextParagraph::get_dropcap_lines() const {
+ return dropcap_lines;
+}
+
+void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_color, const Color &p_dc_color) const {
+ const_cast<TextParagraph *>(this)->_shape_lines();
+ Vector2 ofs = p_pos;
+ float h_offset = 0.f;
+ if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
+ h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x;
+ } else {
+ h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y;
+ }
+
+ if (h_offset > 0) {
+ // Draw dropcap.
+ Vector2 dc_off = ofs;
+ if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
+ if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
+ dc_off.x += width - h_offset;
+ } else {
+ dc_off.y += width - h_offset;
+ }
+ }
+ TS->shaped_text_draw(dropcap_rid, p_canvas, dc_off + Vector2(0, TS->shaped_text_get_ascent(dropcap_rid) + dropcap_margins.size.y + dropcap_margins.position.y / 2), -1, -1, p_dc_color);
+ }
+
+ int lines_visible = (max_lines_visible >= 0) ? MIN(max_lines_visible, lines_rid.size()) : lines_rid.size();
+
+ for (int i = 0; i < lines_visible; i++) {
+ float l_width = width;
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x = p_pos.x;
+ ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + spacing_top;
+ if (i <= dropcap_lines) {
+ if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
+ ofs.x -= h_offset;
+ }
+ l_width -= h_offset;
+ }
+ } else {
+ ofs.y = p_pos.y;
+ ofs.x += TS->shaped_text_get_ascent(lines_rid[i]) + spacing_top;
+ if (i <= dropcap_lines) {
+ if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
+ ofs.x -= h_offset;
+ }
+ l_width -= h_offset;
+ }
+ }
+ float line_width = TS->shaped_text_get_width(lines_rid[i]);
+ if (width > 0) {
+ switch (align) {
+ case HALIGN_FILL:
+ if (TS->shaped_text_get_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) {
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += l_width - line_width;
+ } else {
+ ofs.y += l_width - line_width;
+ }
+ }
+ break;
+ case HALIGN_LEFT:
+ break;
+ case HALIGN_CENTER: {
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += Math::floor((l_width - line_width) / 2.0);
+ } else {
+ ofs.y += Math::floor((l_width - line_width) / 2.0);
+ }
+ } break;
+ case HALIGN_RIGHT: {
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += l_width - line_width;
+ } else {
+ ofs.y += l_width - line_width;
+ }
+ } break;
+ }
+ }
+ float clip_l;
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ clip_l = MAX(0, p_pos.x - ofs.x);
+ } else {
+ clip_l = MAX(0, p_pos.y - ofs.y);
+ }
+ TS->shaped_text_draw(lines_rid[i], p_canvas, ofs, clip_l, clip_l + l_width, p_color);
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x = p_pos.x;
+ ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + spacing_bottom;
+ } else {
+ ofs.y = p_pos.y;
+ ofs.x += TS->shaped_text_get_descent(lines_rid[i]) + spacing_bottom;
+ }
+ }
+}
+
+void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size, const Color &p_color, const Color &p_dc_color) const {
+ const_cast<TextParagraph *>(this)->_shape_lines();
+ Vector2 ofs = p_pos;
+
+ float h_offset = 0.f;
+ if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
+ h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x;
+ } else {
+ h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y;
+ }
+
+ if (h_offset > 0) {
+ // Draw dropcap.
+ Vector2 dc_off = ofs;
+ if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
+ if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
+ dc_off.x += width - h_offset;
+ } else {
+ dc_off.y += width - h_offset;
+ }
+ }
+ TS->shaped_text_draw_outline(dropcap_rid, p_canvas, dc_off + Vector2(dropcap_margins.position.x, TS->shaped_text_get_ascent(dropcap_rid) + dropcap_margins.position.y), -1, -1, p_outline_size, p_dc_color);
+ }
+
+ for (int i = 0; i < lines_rid.size(); i++) {
+ float l_width = width;
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x = p_pos.x;
+ ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + spacing_top;
+ if (i <= dropcap_lines) {
+ if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
+ ofs.x -= h_offset;
+ }
+ l_width -= h_offset;
+ }
+ } else {
+ ofs.y = p_pos.y;
+ ofs.x += TS->shaped_text_get_ascent(lines_rid[i]) + spacing_top;
+ if (i <= dropcap_lines) {
+ if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
+ ofs.x -= h_offset;
+ }
+ l_width -= h_offset;
+ }
+ }
+ float length = TS->shaped_text_get_width(lines_rid[i]);
+ if (width > 0) {
+ switch (align) {
+ case HALIGN_FILL:
+ if (TS->shaped_text_get_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) {
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += l_width - length;
+ } else {
+ ofs.y += l_width - length;
+ }
+ }
+ break;
+ case HALIGN_LEFT:
+ break;
+ case HALIGN_CENTER: {
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += Math::floor((l_width - length) / 2.0);
+ } else {
+ ofs.y += Math::floor((l_width - length) / 2.0);
+ }
+ } break;
+ case HALIGN_RIGHT: {
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += l_width - length;
+ } else {
+ ofs.y += l_width - length;
+ }
+ } break;
+ }
+ }
+ float clip_l;
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ clip_l = MAX(0, p_pos.x - ofs.x);
+ } else {
+ clip_l = MAX(0, p_pos.y - ofs.y);
+ }
+ TS->shaped_text_draw_outline(lines_rid[i], p_canvas, ofs, clip_l, clip_l + l_width, p_outline_size, p_color);
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x = p_pos.x;
+ ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + spacing_bottom;
+ } else {
+ ofs.y = p_pos.y;
+ ofs.x += TS->shaped_text_get_descent(lines_rid[i]) + spacing_bottom;
+ }
+ }
+}
+
+int TextParagraph::hit_test(const Point2 &p_coords) const {
+ const_cast<TextParagraph *>(this)->_shape_lines();
+ Vector2 ofs;
+ if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
+ if (ofs.y < 0) {
+ return 0;
+ }
+ } else {
+ if (ofs.x < 0) {
+ return 0;
+ }
+ }
+ for (int i = 0; i < lines_rid.size(); i++) {
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ if ((p_coords.y >= ofs.y) && (p_coords.y <= ofs.y + TS->shaped_text_get_size(lines_rid[i]).y)) {
+ return TS->shaped_text_hit_test_position(lines_rid[i], p_coords.x);
+ }
+ ofs.y += TS->shaped_text_get_size(lines_rid[i]).y + spacing_bottom + spacing_top;
+ } else {
+ if ((p_coords.x >= ofs.x) && (p_coords.x <= ofs.x + TS->shaped_text_get_size(lines_rid[i]).x)) {
+ return TS->shaped_text_hit_test_position(lines_rid[i], p_coords.y);
+ }
+ ofs.y += TS->shaped_text_get_size(lines_rid[i]).x + spacing_bottom + spacing_top;
+ }
+ }
+ return TS->shaped_text_get_range(rid).y;
+}
+
+void TextParagraph::draw_dropcap(RID p_canvas, const Vector2 &p_pos, const Color &p_color) const {
+ Vector2 ofs = p_pos;
+ float h_offset = 0.f;
+ if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
+ h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x;
+ } else {
+ h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y;
+ }
+
+ if (h_offset > 0) {
+ // Draw dropcap.
+ if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
+ if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += width - h_offset;
+ } else {
+ ofs.y += width - h_offset;
+ }
+ }
+ TS->shaped_text_draw(dropcap_rid, p_canvas, ofs + Vector2(dropcap_margins.position.x, TS->shaped_text_get_ascent(dropcap_rid) + dropcap_margins.position.y), -1, -1, p_color);
+ }
+}
+
+void TextParagraph::draw_dropcap_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size, const Color &p_color) const {
+ Vector2 ofs = p_pos;
+ float h_offset = 0.f;
+ if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
+ h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x;
+ } else {
+ h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y;
+ }
+
+ if (h_offset > 0) {
+ // Draw dropcap.
+ if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
+ if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += width - h_offset;
+ } else {
+ ofs.y += width - h_offset;
+ }
+ }
+ TS->shaped_text_draw_outline(dropcap_rid, p_canvas, ofs + Vector2(dropcap_margins.position.x, TS->shaped_text_get_ascent(dropcap_rid) + dropcap_margins.position.y), -1, -1, p_outline_size, p_color);
+ }
+}
+
+void TextParagraph::draw_line(RID p_canvas, const Vector2 &p_pos, int p_line, const Color &p_color) const {
+ const_cast<TextParagraph *>(this)->_shape_lines();
+ ERR_FAIL_COND(p_line < 0 || p_line >= lines_rid.size());
+
+ Vector2 ofs = p_pos;
+
+ if (TS->shaped_text_get_orientation(lines_rid[p_line]) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.y += TS->shaped_text_get_ascent(lines_rid[p_line]) + spacing_top;
+ } else {
+ ofs.x += TS->shaped_text_get_ascent(lines_rid[p_line]) + spacing_top;
+ }
+ return TS->shaped_text_draw(lines_rid[p_line], p_canvas, ofs, -1, -1, p_color);
+}
+
+void TextParagraph::draw_line_outline(RID p_canvas, const Vector2 &p_pos, int p_line, int p_outline_size, const Color &p_color) const {
+ const_cast<TextParagraph *>(this)->_shape_lines();
+ ERR_FAIL_COND(p_line < 0 || p_line >= lines_rid.size());
+
+ Vector2 ofs = p_pos;
+ if (TS->shaped_text_get_orientation(lines_rid[p_line]) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.y += TS->shaped_text_get_ascent(lines_rid[p_line]) + spacing_top;
+ } else {
+ ofs.x += TS->shaped_text_get_ascent(lines_rid[p_line]) + spacing_top;
+ }
+ return TS->shaped_text_draw_outline(lines_rid[p_line], p_canvas, ofs, -1, -1, p_outline_size, p_color);
+}
+
+TextParagraph::TextParagraph(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, float p_width, TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
+ rid = TS->create_shaped_text(p_direction, p_orientation);
+ TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
+ spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP);
+ spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM);
+ width = p_width;
+}
+
+TextParagraph::TextParagraph() {
+ rid = TS->create_shaped_text();
+ dropcap_rid = TS->create_shaped_text();
+}
+
+TextParagraph::~TextParagraph() {
+ for (int i = 0; i < lines_rid.size(); i++) {
+ TS->free(lines_rid[i]);
+ }
+ lines_rid.clear();
+ TS->free(rid);
+ TS->free(dropcap_rid);
+}
diff --git a/scene/resources/text_paragraph.h b/scene/resources/text_paragraph.h
new file mode 100644
index 0000000000..ee7bbab9c5
--- /dev/null
+++ b/scene/resources/text_paragraph.h
@@ -0,0 +1,165 @@
+/*************************************************************************/
+/* text_paragraph.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 TEXT_PARAGRAPH_H
+#define TEXT_PARAGRAPH_H
+
+#include "scene/resources/font.h"
+#include "servers/text_server.h"
+
+/*************************************************************************/
+
+class TextParagraph : public RefCounted {
+ GDCLASS(TextParagraph, RefCounted);
+
+public:
+ enum OverrunBehavior {
+ OVERRUN_NO_TRIMMING,
+ OVERRUN_TRIM_CHAR,
+ OVERRUN_TRIM_WORD,
+ OVERRUN_TRIM_ELLIPSIS,
+ OVERRUN_TRIM_WORD_ELLIPSIS,
+ };
+
+private:
+ RID dropcap_rid;
+ int dropcap_lines = 0;
+ Rect2 dropcap_margins;
+
+ RID rid;
+ Vector<RID> lines_rid;
+ int spacing_top = 0;
+ int spacing_bottom = 0;
+
+ bool lines_dirty = true;
+
+ float width = -1.0;
+ int max_lines_visible = -1;
+
+ uint8_t flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA;
+ OverrunBehavior overrun_behavior = OVERRUN_NO_TRIMMING;
+
+ HAlign align = HALIGN_LEFT;
+
+ Vector<float> tab_stops;
+
+protected:
+ static void _bind_methods();
+
+ void _shape_lines();
+
+public:
+ RID get_rid() const;
+ RID get_line_rid(int p_line) const;
+ RID get_dropcap_rid() const;
+
+ void clear();
+
+ void set_direction(TextServer::Direction p_direction);
+ TextServer::Direction get_direction() const;
+
+ void set_orientation(TextServer::Orientation p_orientation);
+ TextServer::Orientation get_orientation() const;
+
+ void set_preserve_invalid(bool p_enabled);
+ bool get_preserve_invalid() const;
+
+ void set_preserve_control(bool p_enabled);
+ bool get_preserve_control() const;
+
+ void set_bidi_override(const Vector<Vector2i> &p_override);
+
+ bool set_dropcap(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Rect2 &p_dropcap_margins = Rect2(), const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "");
+ void clear_dropcap();
+
+ bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "");
+ bool add_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER, int p_length = 1);
+ bool resize_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER);
+
+ void set_align(HAlign p_align);
+ HAlign get_align() const;
+
+ void tab_align(const Vector<float> &p_tab_stops);
+
+ void set_flags(uint8_t p_flags);
+ uint8_t get_flags() const;
+
+ void set_text_overrun_behavior(OverrunBehavior p_behavior);
+ OverrunBehavior get_text_overrun_behavior() const;
+
+ void set_width(float p_width);
+ float get_width() const;
+
+ void set_max_lines_visible(int p_lines);
+ int get_max_lines_visible() const;
+
+ Size2 get_non_wraped_size() const;
+
+ Size2 get_size() const;
+
+ int get_line_count() const;
+
+ Array get_line_objects(int p_line) const;
+ Rect2 get_line_object_rect(int p_line, Variant p_key) const;
+ Size2 get_line_size(int p_line) const;
+ float get_line_ascent(int p_line) const;
+ float get_line_descent(int p_line) const;
+ float get_line_width(int p_line) const;
+ Vector2i get_line_range(int p_line) const;
+ float get_line_underline_position(int p_line) const;
+ float get_line_underline_thickness(int p_line) const;
+
+ int get_spacing_top() const;
+ int get_spacing_bottom() const;
+
+ Size2 get_dropcap_size() const;
+ int get_dropcap_lines() const;
+
+ void draw(RID p_canvas, const Vector2 &p_pos, const Color &p_color = Color(1, 1, 1), const Color &p_dc_color = Color(1, 1, 1)) const;
+ void draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size = 1, const Color &p_color = Color(1, 1, 1), const Color &p_dc_color = Color(1, 1, 1)) const;
+
+ void draw_line(RID p_canvas, const Vector2 &p_pos, int p_line, const Color &p_color = Color(1, 1, 1)) const;
+ void draw_line_outline(RID p_canvas, const Vector2 &p_pos, int p_line, int p_outline_size = 1, const Color &p_color = Color(1, 1, 1)) const;
+
+ void draw_dropcap(RID p_canvas, const Vector2 &p_pos, const Color &p_color = Color(1, 1, 1)) const;
+ void draw_dropcap_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size = 1, const Color &p_color = Color(1, 1, 1)) const;
+
+ int hit_test(const Point2 &p_coords) const;
+
+ void _set_bidi_override(const Array &p_override);
+
+ TextParagraph(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", float p_width = -1.f, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL);
+ TextParagraph();
+ ~TextParagraph();
+};
+
+VARIANT_ENUM_CAST(TextParagraph::OverrunBehavior);
+
+#endif // TEXT_PARAGRAPH_H
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 39237e1a33..063a13efc0 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,7 +32,6 @@
#include "core/core_string_names.h"
#include "core/io/image_loader.h"
-#include "core/method_bind_ext.gen.inc"
#include "core/os/os.h"
#include "mesh.h"
#include "scene/resources/bit_map.h"
@@ -46,28 +45,21 @@ bool Texture2D::is_pixel_opaque(int p_x, int p_y) const {
return true;
}
-void Texture2D::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat) const {
- RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, get_size()), get_rid(), false, p_modulate, p_transpose, normal_rid, specular_rid, p_specular_color_shininess, p_texture_filter, p_texture_repeat);
+void Texture2D::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, get_size()), get_rid(), false, p_modulate, p_transpose);
}
-void Texture2D::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat) const {
- RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, get_rid(), p_tile, p_modulate, p_transpose, normal_rid, specular_rid, p_specular_color_shininess, p_texture_filter, p_texture_repeat);
+void Texture2D::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, get_rid(), p_tile, p_modulate, p_transpose);
}
-void Texture2D::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat, bool p_clip_uv) const {
- RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, get_rid(), p_src_rect, p_modulate, p_transpose, normal_rid, specular_rid, p_specular_color_shininess, p_clip_uv, p_texture_filter, p_texture_repeat);
+void Texture2D::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, get_rid(), p_src_rect, p_modulate, p_transpose, p_clip_uv);
}
bool Texture2D::get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const {
r_rect = p_rect;
r_src_rect = p_src_rect;
-
return true;
}
@@ -76,10 +68,10 @@ void Texture2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_height"), &Texture2D::get_height);
ClassDB::bind_method(D_METHOD("get_size"), &Texture2D::get_size);
ClassDB::bind_method(D_METHOD("has_alpha"), &Texture2D::has_alpha);
- ClassDB::bind_method(D_METHOD("draw", "canvas_item", "position", "modulate", "transpose", "normal_map", "specular_map", "specular_color_shininess", "texture_filter", "texture_repeat"), &Texture2D::draw, DEFVAL(Color(1, 1, 1)), DEFVAL(false), DEFVAL(Variant()), DEFVAL(Variant()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT), DEFVAL(RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT));
- ClassDB::bind_method(D_METHOD("draw_rect", "canvas_item", "rect", "tile", "modulate", "transpose", "normal_map", "specular_map", "specular_color_shininess", "texture_filter", "texture_repeat"), &Texture2D::draw_rect, DEFVAL(Color(1, 1, 1)), DEFVAL(false), DEFVAL(Variant()), DEFVAL(Variant()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT), DEFVAL(RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT));
- ClassDB::bind_method(D_METHOD("draw_rect_region", "canvas_item", "rect", "src_rect", "modulate", "transpose", "normal_map", "specular_map", "specular_color_shininess", "texture_filter", "texture_repeat", "clip_uv"), &Texture2D::draw_rect_region, DEFVAL(Color(1, 1, 1)), DEFVAL(false), DEFVAL(Variant()), DEFVAL(Variant()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT), DEFVAL(RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT), DEFVAL(true));
- ClassDB::bind_method(D_METHOD("get_data"), &Texture2D::get_data);
+ ClassDB::bind_method(D_METHOD("draw", "canvas_item", "position", "modulate", "transpose"), &Texture2D::draw, DEFVAL(Color(1, 1, 1)), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("draw_rect", "canvas_item", "rect", "tile", "modulate", "transpose"), &Texture2D::draw_rect, DEFVAL(Color(1, 1, 1)), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("draw_rect_region", "canvas_item", "rect", "src_rect", "modulate", "transpose", "clip_uv"), &Texture2D::draw_rect_region, DEFVAL(Color(1, 1, 1)), DEFVAL(false), DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("get_image"), &Texture2D::get_image);
ADD_GROUP("", "");
}
@@ -96,13 +88,13 @@ void ImageTexture::reload_from_file() {
}
Ref<Image> img;
- img.instance();
+ img.instantiate();
if (ImageLoader::load_image(path, img) == OK) {
create_from_image(img);
} else {
Resource::reload_from_file();
- _change_notify();
+ notify_property_list_changed();
emit_changed();
}
}
@@ -124,7 +116,7 @@ bool ImageTexture::_set(const StringName &p_name, const Variant &p_value) {
bool ImageTexture::_get(const StringName &p_name, Variant &r_ret) const {
if (p_name == "image") {
- r_ret = get_data();
+ r_ret = get_image();
} else if (p_name == "size") {
r_ret = Size2(w, h);
} else {
@@ -146,7 +138,7 @@ void ImageTexture::_reload_hook(const RID &p_hook) {
}
Ref<Image> img;
- img.instance();
+ img.instantiate();
Error err = ImageLoader::load_image(path, img);
ERR_FAIL_COND_MSG(err != OK, "Cannot load image from path '" + path + "'.");
@@ -154,12 +146,12 @@ void ImageTexture::_reload_hook(const RID &p_hook) {
RID new_texture = RenderingServer::get_singleton()->texture_2d_create(img);
RenderingServer::get_singleton()->texture_replace(texture, new_texture);
- _change_notify();
+ notify_property_list_changed();
emit_changed();
}
void ImageTexture::create_from_image(const Ref<Image> &p_image) {
- ERR_FAIL_COND(p_image.is_null());
+ ERR_FAIL_COND_MSG(p_image.is_null() || p_image->is_empty(), "Invalid image");
w = p_image->get_width();
h = p_image->get_height();
format = p_image->get_format();
@@ -171,7 +163,7 @@ void ImageTexture::create_from_image(const Ref<Image> &p_image) {
RID new_texture = RenderingServer::get_singleton()->texture_2d_create(p_image);
RenderingServer::get_singleton()->texture_replace(texture, new_texture);
}
- _change_notify();
+ notify_property_list_changed();
emit_changed();
image_stored = true;
@@ -181,20 +173,19 @@ Image::Format ImageTexture::get_format() const {
return format;
}
-void ImageTexture::update(const Ref<Image> &p_image, bool p_immediate) {
- ERR_FAIL_COND(p_image.is_null());
- ERR_FAIL_COND(texture.is_null());
- ERR_FAIL_COND(p_image->get_width() != w || p_image->get_height() != h);
- ERR_FAIL_COND(p_image->get_format() != format);
- ERR_FAIL_COND(mipmaps != p_image->has_mipmaps());
+void ImageTexture::update(const Ref<Image> &p_image) {
+ ERR_FAIL_COND_MSG(p_image.is_null(), "Invalid image");
+ ERR_FAIL_COND_MSG(texture.is_null(), "Texture is not initialized.");
+ ERR_FAIL_COND_MSG(p_image->get_width() != w || p_image->get_height() != h,
+ "The new image dimensions must match the texture size.");
+ ERR_FAIL_COND_MSG(p_image->get_format() != format,
+ "The new image format must match the texture's image format.");
+ ERR_FAIL_COND_MSG(mipmaps != p_image->has_mipmaps(),
+ "The new image mipmaps configuration must match the texture's image mipmaps configuration");
- if (p_immediate) {
- RenderingServer::get_singleton()->texture_2d_update_immediate(texture, p_image);
- } else {
- RenderingServer::get_singleton()->texture_2d_update(texture, p_image);
- }
+ RenderingServer::get_singleton()->texture_2d_update(texture, p_image);
- _change_notify();
+ notify_property_list_changed();
emit_changed();
alpha_cache.unref();
@@ -205,7 +196,7 @@ void ImageTexture::_resource_path_changed() {
String path = get_path();
}
-Ref<Image> ImageTexture::get_data() const {
+Ref<Image> ImageTexture::get_image() const {
if (image_stored) {
return RenderingServer::get_singleton()->texture_2d_get(texture);
} else {
@@ -233,43 +224,37 @@ bool ImageTexture::has_alpha() const {
return (format == Image::FORMAT_LA8 || format == Image::FORMAT_RGBA8);
}
-void ImageTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat) const {
+void ImageTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
if ((w | h) == 0) {
return;
}
- RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, Size2(w, h)), texture, false, p_modulate, p_transpose, normal_rid, specular_rid, p_specular_color_shininess, p_texture_filter, p_texture_repeat);
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, Size2(w, h)), texture, false, p_modulate, p_transpose);
}
-void ImageTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat) const {
+void ImageTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
if ((w | h) == 0) {
return;
}
- RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, texture, p_tile, p_modulate, p_transpose, normal_rid, specular_rid, p_specular_color_shininess, p_texture_filter, p_texture_repeat);
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, texture, p_tile, p_modulate, p_transpose);
}
-void ImageTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat, bool p_clip_uv) const {
+void ImageTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
if ((w | h) == 0) {
return;
}
- RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, texture, p_src_rect, p_modulate, p_transpose, normal_rid, specular_rid, p_specular_color_shininess, p_clip_uv, p_texture_filter, p_texture_repeat);
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, texture, p_src_rect, p_modulate, p_transpose, p_clip_uv);
}
bool ImageTexture::is_pixel_opaque(int p_x, int p_y) const {
if (!alpha_cache.is_valid()) {
- Ref<Image> img = get_data();
+ Ref<Image> img = get_image();
if (img.is_valid()) {
if (img->is_compressed()) { //must decompress, if compressed
Ref<Image> decom = img->duplicate();
decom->decompress();
img = decom;
}
- alpha_cache.instance();
+ alpha_cache.instantiate();
alpha_cache->create_from_image_alpha(img);
}
}
@@ -316,17 +301,12 @@ void ImageTexture::_bind_methods() {
ClassDB::bind_method(D_METHOD("create_from_image", "image"), &ImageTexture::create_from_image);
ClassDB::bind_method(D_METHOD("get_format"), &ImageTexture::get_format);
- ClassDB::bind_method(D_METHOD("update", "image", "immediate"), &ImageTexture::update, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("update", "image"), &ImageTexture::update);
ClassDB::bind_method(D_METHOD("set_size_override", "size"), &ImageTexture::set_size_override);
ClassDB::bind_method(D_METHOD("_reload_hook", "rid"), &ImageTexture::_reload_hook);
}
-ImageTexture::ImageTexture() {
- w = h = 0;
- image_stored = false;
- mipmaps = false;
- format = Image::FORMAT_L8;
-}
+ImageTexture::ImageTexture() {}
ImageTexture::~ImageTexture() {
if (texture.is_valid()) {
@@ -343,7 +323,7 @@ Ref<Image> StreamTexture2D::load_image_from_file(FileAccess *f, int p_size_limit
uint32_t mipmaps = f->get_32();
Image::Format format = Image::Format(f->get_32());
- if (data_format == DATA_FORMAT_LOSSLESS || data_format == DATA_FORMAT_LOSSY || data_format == DATA_FORMAT_BASIS_UNIVERSAL) {
+ if (data_format == DATA_FORMAT_PNG || data_format == DATA_FORMAT_WEBP || data_format == DATA_FORMAT_BASIS_UNIVERSAL) {
//look for a PNG or WEBP file inside
int sw = w;
@@ -351,7 +331,7 @@ Ref<Image> StreamTexture2D::load_image_from_file(FileAccess *f, int p_size_limit
//mipmaps need to be read independently, they will be later combined
Vector<Ref<Image>> mipmap_images;
- int total_size = 0;
+ uint64_t total_size = 0;
bool first = true;
@@ -374,16 +354,16 @@ Ref<Image> StreamTexture2D::load_image_from_file(FileAccess *f, int p_size_limit
}
Ref<Image> img;
- if (data_format == DATA_FORMAT_BASIS_UNIVERSAL) {
+ if (data_format == DATA_FORMAT_BASIS_UNIVERSAL && Image::basis_universal_unpacker) {
img = Image::basis_universal_unpacker(pv);
- } else if (data_format == DATA_FORMAT_LOSSLESS) {
- img = Image::lossless_unpacker(pv);
- } else {
- img = Image::lossy_unpacker(pv);
+ } else if (data_format == DATA_FORMAT_PNG && Image::png_unpacker) {
+ img = Image::png_unpacker(pv);
+ } else if (data_format == DATA_FORMAT_WEBP && Image::webp_unpacker) {
+ img = Image::webp_unpacker(pv);
}
- if (img.is_null() || img->empty()) {
- ERR_FAIL_COND_V(img.is_null() || img->empty(), Ref<Image>());
+ if (img.is_null() || img->is_empty()) {
+ ERR_FAIL_COND_V(img.is_null() || img->is_empty(), Ref<Image>());
}
if (first) {
@@ -406,7 +386,7 @@ Ref<Image> StreamTexture2D::load_image_from_file(FileAccess *f, int p_size_limit
//print_line("mipmap read total: " + itos(mipmap_images.size()));
Ref<Image> image;
- image.instance();
+ image.instantiate();
if (mipmap_images.size() == 1) {
//only one image (which will most likely be the case anyway for this format)
@@ -426,7 +406,7 @@ Ref<Image> StreamTexture2D::load_image_from_file(FileAccess *f, int p_size_limit
Vector<uint8_t> id = mipmap_images[i]->get_data();
int len = id.size();
const uint8_t *r = id.ptr();
- copymem(&wr[ofs], r, len);
+ memcpy(&wr[ofs], r, len);
ofs += len;
}
}
@@ -458,7 +438,7 @@ Ref<Image> StreamTexture2D::load_image_from_file(FileAccess *f, int p_size_limit
}
Ref<Image> image;
- image.instance();
+ image.instantiate();
image->create(tw, th, mipmaps - i ? true : false, format, data);
@@ -506,13 +486,13 @@ Image::Format StreamTexture2D::get_format() const {
return format;
}
-Error StreamTexture2D::_load_data(const String &p_path, int &tw, int &th, int &tw_custom, int &th_custom, Ref<Image> &image, bool &r_request_3d, bool &r_request_normal, bool &r_request_roughness, int &mipmap_limit, int p_size_limit) {
+Error StreamTexture2D::_load_data(const String &p_path, int &r_width, int &r_height, Ref<Image> &image, bool &r_request_3d, bool &r_request_normal, bool &r_request_roughness, int &mipmap_limit, int p_size_limit) {
alpha_cache.unref();
ERR_FAIL_COND_V(image.is_null(), ERR_INVALID_PARAMETER);
FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
- ERR_FAIL_COND_V(!f, ERR_CANT_OPEN);
+ ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path));
uint8_t header[4];
f->get_buffer(header, 4);
@@ -527,8 +507,8 @@ Error StreamTexture2D::_load_data(const String &p_path, int &tw, int &th, int &t
memdelete(f);
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Stream texture file is too new.");
}
- tw_custom = f->get_32();
- th_custom = f->get_32();
+ r_width = f->get_32();
+ r_height = f->get_32();
uint32_t df = f->get_32(); //data format
//skip reserved
@@ -559,7 +539,7 @@ Error StreamTexture2D::_load_data(const String &p_path, int &tw, int &th, int &t
memdelete(f);
- if (image.is_null() || image->empty()) {
+ if (image.is_null() || image->is_empty()) {
return ERR_CANT_OPEN;
}
@@ -567,16 +547,16 @@ Error StreamTexture2D::_load_data(const String &p_path, int &tw, int &th, int &t
}
Error StreamTexture2D::load(const String &p_path) {
- int lw, lh, lwc, lhc;
+ int lw, lh;
Ref<Image> image;
- image.instance();
+ image.instantiate();
bool request_3d;
bool request_normal;
bool request_roughness;
int mipmap_limit;
- Error err = _load_data(p_path, lw, lh, lwc, lhc, image, request_3d, request_normal, request_roughness, mipmap_limit);
+ Error err = _load_data(p_path, lw, lh, image, request_3d, request_normal, request_roughness, mipmap_limit);
if (err) {
return err;
}
@@ -587,12 +567,12 @@ Error StreamTexture2D::load(const String &p_path) {
} else {
texture = RS::get_singleton()->texture_2d_create(image);
}
- if (lwc || lhc) {
- RS::get_singleton()->texture_set_size_override(texture, lwc, lhc);
+ if (lw || lh) {
+ RS::get_singleton()->texture_set_size_override(texture, lw, lh);
}
- w = lwc ? lwc : lw;
- h = lhc ? lhc : lh;
+ w = lw;
+ h = lh;
path_to_file = p_path;
format = image->get_format();
@@ -628,7 +608,7 @@ Error StreamTexture2D::load(const String &p_path) {
}
#endif
- _change_notify();
+ notify_property_list_changed();
emit_changed();
return OK;
}
@@ -652,38 +632,32 @@ RID StreamTexture2D::get_rid() const {
return texture;
}
-void StreamTexture2D::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat) const {
+void StreamTexture2D::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
if ((w | h) == 0) {
return;
}
- RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, Size2(w, h)), texture, false, p_modulate, p_transpose, normal_rid, specular_rid, p_specular_color_shininess, p_texture_filter, p_texture_repeat);
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, Size2(w, h)), texture, false, p_modulate, p_transpose);
}
-void StreamTexture2D::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat) const {
+void StreamTexture2D::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
if ((w | h) == 0) {
return;
}
- RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, texture, p_tile, p_modulate, p_transpose, normal_rid, specular_rid, p_specular_color_shininess, p_texture_filter, p_texture_repeat);
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, texture, p_tile, p_modulate, p_transpose);
}
-void StreamTexture2D::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat, bool p_clip_uv) const {
+void StreamTexture2D::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
if ((w | h) == 0) {
return;
}
- RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, texture, p_src_rect, p_modulate, p_transpose, normal_rid, specular_rid, p_specular_color_shininess, p_clip_uv, p_texture_filter, p_texture_repeat);
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, texture, p_src_rect, p_modulate, p_transpose, p_clip_uv);
}
bool StreamTexture2D::has_alpha() const {
return false;
}
-Ref<Image> StreamTexture2D::get_data() const {
+Ref<Image> StreamTexture2D::get_image() const {
if (texture.is_valid()) {
return RS::get_singleton()->texture_2d_get(texture);
} else {
@@ -693,7 +667,7 @@ Ref<Image> StreamTexture2D::get_data() const {
bool StreamTexture2D::is_pixel_opaque(int p_x, int p_y) const {
if (!alpha_cache.is_valid()) {
- Ref<Image> img = get_data();
+ Ref<Image> img = get_image();
if (img.is_valid()) {
if (img->is_compressed()) { //must decompress, if compressed
Ref<Image> decom = img->duplicate();
@@ -701,7 +675,7 @@ bool StreamTexture2D::is_pixel_opaque(int p_x, int p_y) const {
img = decom;
}
- alpha_cache.instance();
+ alpha_cache.instantiate();
alpha_cache->create_from_image_alpha(img);
}
}
@@ -750,11 +724,7 @@ void StreamTexture2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.stex"), "load", "get_load_path");
}
-StreamTexture2D::StreamTexture2D() {
- format = Image::FORMAT_MAX;
- w = 0;
- h = 0;
-}
+StreamTexture2D::StreamTexture2D() {}
StreamTexture2D::~StreamTexture2D() {
if (texture.is_valid()) {
@@ -762,9 +732,9 @@ StreamTexture2D::~StreamTexture2D() {
}
}
-RES ResourceFormatLoaderStreamTexture2D::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoaderStreamTexture2D::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
Ref<StreamTexture2D> st;
- st.instance();
+ st.instantiate();
Error err = st->load(p_path);
if (r_error) {
*r_error = err;
@@ -913,7 +883,7 @@ Image::Format StreamTexture3D::get_format() const {
Error StreamTexture3D::_load_data(const String &p_path, Vector<Ref<Image>> &r_data, Image::Format &r_format, int &r_width, int &r_height, int &r_depth, bool &r_mipmaps) {
FileAccessRef f = FileAccess::open(p_path, FileAccess::READ);
- ERR_FAIL_COND_V(!f, ERR_CANT_OPEN);
+ ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path));
uint8_t header[4];
f->get_buffer(header, 4);
@@ -941,7 +911,7 @@ Error StreamTexture3D::_load_data(const String &p_path, Vector<Ref<Image>> &r_da
for (int i = 0; i < (r_depth + mipmaps); i++) {
Ref<Image> image = StreamTexture2D::load_image_from_file(f, 0);
- ERR_FAIL_COND_V(image.is_null() || image->empty(), ERR_CANT_OPEN);
+ ERR_FAIL_COND_V(image.is_null() || image->is_empty(), ERR_CANT_OPEN);
if (i == 0) {
r_format = image->get_format();
r_width = image->get_width();
@@ -985,7 +955,7 @@ Error StreamTexture3D::load(const String &p_path) {
RenderingServer::get_singleton()->texture_set_path(texture, p_path);
}
- _change_notify();
+ notify_property_list_changed();
emit_changed();
return OK;
}
@@ -1050,13 +1020,7 @@ void StreamTexture3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.stex"), "load", "get_load_path");
}
-StreamTexture3D::StreamTexture3D() {
- format = Image::FORMAT_MAX;
- w = 0;
- h = 0;
- d = 0;
- mipmaps = false;
-}
+StreamTexture3D::StreamTexture3D() {}
StreamTexture3D::~StreamTexture3D() {
if (texture.is_valid()) {
@@ -1066,9 +1030,9 @@ StreamTexture3D::~StreamTexture3D() {
/////////////////////////////
-RES ResourceFormatLoaderStreamTexture3D::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoaderStreamTexture3D::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
Ref<StreamTexture3D> st;
- st.instance();
+ st.instantiate();
Error err = st->load(p_path);
if (r_error) {
*r_error = err;
@@ -1142,7 +1106,6 @@ void AtlasTexture::set_atlas(const Ref<Texture2D> &p_atlas) {
}
atlas = p_atlas;
emit_changed();
- _change_notify("atlas");
}
Ref<Texture2D> AtlasTexture::get_atlas() const {
@@ -1155,7 +1118,6 @@ void AtlasTexture::set_region(const Rect2 &p_region) {
}
region = p_region;
emit_changed();
- _change_notify("region");
}
Rect2 AtlasTexture::get_region() const {
@@ -1168,7 +1130,6 @@ void AtlasTexture::set_margin(const Rect2 &p_margin) {
}
margin = p_margin;
emit_changed();
- _change_notify("margin");
}
Rect2 AtlasTexture::get_margin() const {
@@ -1178,7 +1139,6 @@ Rect2 AtlasTexture::get_margin() const {
void AtlasTexture::set_filter_clip(const bool p_enable) {
filter_clip = p_enable;
emit_changed();
- _change_notify("filter_clip");
}
bool AtlasTexture::has_filter_clip() const {
@@ -1204,7 +1164,7 @@ void AtlasTexture::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter_clip"), "set_filter_clip", "has_filter_clip");
}
-void AtlasTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat) const {
+void AtlasTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
if (!atlas.is_valid()) {
return;
}
@@ -1219,12 +1179,10 @@ void AtlasTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_m
rc.size.height = atlas->get_height();
}
- RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RS::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(p_pos + margin.position, rc.size), atlas->get_rid(), rc, p_modulate, p_transpose, normal_rid, specular_rid, p_specular_color_shininess, filter_clip, p_texture_filter, p_texture_repeat);
+ RS::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(p_pos + margin.position, rc.size), atlas->get_rid(), rc, p_modulate, p_transpose, filter_clip);
}
-void AtlasTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat) const {
+void AtlasTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
if (!atlas.is_valid()) {
return;
}
@@ -1242,12 +1200,10 @@ void AtlasTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile
Vector2 scale = p_rect.size / (region.size + margin.size);
Rect2 dr(p_rect.position + margin.position * scale, rc.size * scale);
- RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RS::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, dr, atlas->get_rid(), rc, p_modulate, p_transpose, normal_rid, specular_rid, p_specular_color_shininess, filter_clip, p_texture_filter, p_texture_repeat);
+ RS::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, dr, atlas->get_rid(), rc, p_modulate, p_transpose, filter_clip);
}
-void AtlasTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat, bool p_clip_uv) const {
+void AtlasTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
//this might not necessarily work well if using a rect, needs to be fixed properly
if (!atlas.is_valid()) {
return;
@@ -1257,9 +1213,7 @@ void AtlasTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, cons
Rect2 src_c;
get_rect_region(p_rect, p_src_rect, dr, src_c);
- RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RS::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, dr, atlas->get_rid(), src_c, p_modulate, p_transpose, normal_rid, specular_rid, p_specular_color_shininess, filter_clip, p_texture_filter, p_texture_repeat);
+ RS::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, dr, atlas->get_rid(), src_c, p_modulate, p_transpose, filter_clip);
}
bool AtlasTexture::get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const {
@@ -1276,7 +1230,7 @@ bool AtlasTexture::get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect,
Vector2 scale = p_rect.size / src.size;
src.position += (rc.position - margin.position);
- Rect2 src_c = rc.clip(src);
+ Rect2 src_c = rc.intersection(src);
if (src_c.size == Size2()) {
return false;
}
@@ -1318,9 +1272,7 @@ bool AtlasTexture::is_pixel_opaque(int p_x, int p_y) const {
return atlas->is_pixel_opaque(x, y);
}
-AtlasTexture::AtlasTexture() {
- filter_clip = false;
-}
+AtlasTexture::AtlasTexture() {}
/////////////////////////////////////////
@@ -1364,7 +1316,7 @@ Ref<Texture2D> MeshTexture::get_base_texture() const {
return base_texture;
}
-void MeshTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat) const {
+void MeshTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
if (mesh.is_null() || base_texture.is_null()) {
return;
}
@@ -1374,12 +1326,10 @@ void MeshTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_mo
SWAP(xform.elements[0][1], xform.elements[1][0]);
SWAP(xform.elements[0][0], xform.elements[1][1]);
}
- RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RenderingServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid(), normal_rid, specular_rid, p_specular_color_shininess, p_texture_filter, p_texture_repeat);
+ RenderingServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid());
}
-void MeshTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat) const {
+void MeshTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
if (mesh.is_null() || base_texture.is_null()) {
return;
}
@@ -1398,12 +1348,10 @@ void MeshTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile,
SWAP(xform.elements[0][1], xform.elements[1][0]);
SWAP(xform.elements[0][0], xform.elements[1][1]);
}
- RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RenderingServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid(), normal_rid, specular_rid, p_specular_color_shininess, p_texture_filter, p_texture_repeat);
+ RenderingServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid());
}
-void MeshTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat, bool p_clip_uv) const {
+void MeshTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
if (mesh.is_null() || base_texture.is_null()) {
return;
}
@@ -1422,9 +1370,7 @@ void MeshTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const
SWAP(xform.elements[0][1], xform.elements[1][0]);
SWAP(xform.elements[0][0], xform.elements[1][1]);
}
- RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RenderingServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid(), normal_rid, specular_rid, p_specular_color_shininess, p_texture_filter, p_texture_repeat);
+ RenderingServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid());
}
bool MeshTexture::get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const {
@@ -1455,283 +1401,341 @@ MeshTexture::MeshTexture() {
//////////////////////////////////////////
-int LargeTexture::get_width() const {
- return size.width;
-}
-
-int LargeTexture::get_height() const {
- return size.height;
-}
+void CurveTexture::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_width", "width"), &CurveTexture::set_width);
-RID LargeTexture::get_rid() const {
- return RID();
-}
+ ClassDB::bind_method(D_METHOD("set_curve", "curve"), &CurveTexture::set_curve);
+ ClassDB::bind_method(D_METHOD("get_curve"), &CurveTexture::get_curve);
-bool LargeTexture::has_alpha() const {
- for (int i = 0; i < pieces.size(); i++) {
- if (pieces[i].texture->has_alpha()) {
- return true;
- }
- }
+ ClassDB::bind_method(D_METHOD("set_texture_mode", "texture_mode"), &CurveTexture::set_texture_mode);
+ ClassDB::bind_method(D_METHOD("get_texture_mode"), &CurveTexture::get_texture_mode);
- return false;
-}
+ ClassDB::bind_method(D_METHOD("_update"), &CurveTexture::_update);
-int LargeTexture::add_piece(const Point2 &p_offset, const Ref<Texture2D> &p_texture) {
- ERR_FAIL_COND_V(p_texture.is_null(), -1);
- Piece p;
- p.offset = p_offset;
- p.texture = p_texture;
- pieces.push_back(p);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,4096"), "set_width", "get_width");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_mode", PROPERTY_HINT_ENUM, "RGB,Red"), "set_texture_mode", "get_texture_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve", "get_curve");
- return pieces.size() - 1;
+ BIND_ENUM_CONSTANT(TEXTURE_MODE_RGB);
+ BIND_ENUM_CONSTANT(TEXTURE_MODE_RED);
}
-void LargeTexture::set_piece_offset(int p_idx, const Point2 &p_offset) {
- ERR_FAIL_INDEX(p_idx, pieces.size());
- pieces.write[p_idx].offset = p_offset;
-};
+void CurveTexture::set_width(int p_width) {
+ ERR_FAIL_COND(p_width < 32 || p_width > 4096);
-void LargeTexture::set_piece_texture(int p_idx, const Ref<Texture2D> &p_texture) {
- ERR_FAIL_COND(p_texture == this);
- ERR_FAIL_COND(p_texture.is_null());
- ERR_FAIL_INDEX(p_idx, pieces.size());
- pieces.write[p_idx].texture = p_texture;
-};
+ if (_width == p_width) {
+ return;
+ }
-void LargeTexture::set_size(const Size2 &p_size) {
- size = p_size;
+ _width = p_width;
+ _update();
}
-void LargeTexture::clear() {
- pieces.clear();
- size = Size2i();
+int CurveTexture::get_width() const {
+ return _width;
}
-Array LargeTexture::_get_data() const {
- Array arr;
- for (int i = 0; i < pieces.size(); i++) {
- arr.push_back(pieces[i].offset);
- arr.push_back(pieces[i].texture);
+void CurveTexture::ensure_default_setup(float p_min, float p_max) {
+ if (_curve.is_null()) {
+ Ref<Curve> curve = Ref<Curve>(memnew(Curve));
+ curve->add_point(Vector2(0, 1));
+ curve->add_point(Vector2(1, 1));
+ curve->set_min_value(p_min);
+ curve->set_max_value(p_max);
+ set_curve(curve);
+ // Min and max is 0..1 by default
}
- arr.push_back(Size2(size));
- return arr;
}
-void LargeTexture::_set_data(const Array &p_array) {
- ERR_FAIL_COND(p_array.size() < 1);
- ERR_FAIL_COND(!(p_array.size() & 1));
- clear();
- for (int i = 0; i < p_array.size() - 1; i += 2) {
- add_piece(p_array[i], p_array[i + 1]);
+void CurveTexture::set_curve(Ref<Curve> p_curve) {
+ if (_curve != p_curve) {
+ if (_curve.is_valid()) {
+ _curve->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveTexture::_update));
+ }
+ _curve = p_curve;
+ if (_curve.is_valid()) {
+ _curve->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveTexture::_update));
+ }
+ _update();
}
- size = Size2(p_array[p_array.size() - 1]);
}
-int LargeTexture::get_piece_count() const {
- return pieces.size();
-}
+void CurveTexture::_update() {
+ Vector<uint8_t> data;
+ data.resize(_width * sizeof(float) * (texture_mode == TEXTURE_MODE_RGB ? 3 : 1));
-Vector2 LargeTexture::get_piece_offset(int p_idx) const {
- ERR_FAIL_INDEX_V(p_idx, pieces.size(), Vector2());
- return pieces[p_idx].offset;
-}
+ // The array is locked in that scope
+ {
+ uint8_t *wd8 = data.ptrw();
+ float *wd = (float *)wd8;
-Ref<Texture2D> LargeTexture::get_piece_texture(int p_idx) const {
- ERR_FAIL_INDEX_V(p_idx, pieces.size(), Ref<Texture2D>());
- return pieces[p_idx].texture;
-}
+ if (_curve.is_valid()) {
+ Curve &curve = **_curve;
+ for (int i = 0; i < _width; ++i) {
+ float t = i / static_cast<float>(_width);
+ if (texture_mode == TEXTURE_MODE_RGB) {
+ wd[i * 3 + 0] = curve.interpolate_baked(t);
+ wd[i * 3 + 1] = wd[i * 3 + 0];
+ wd[i * 3 + 2] = wd[i * 3 + 0];
+ } else {
+ wd[i] = curve.interpolate_baked(t);
+ }
+ }
-Ref<Image> LargeTexture::to_image() const {
- Ref<Image> img = memnew(Image(this->get_width(), this->get_height(), false, Image::FORMAT_RGBA8));
- for (int i = 0; i < pieces.size(); i++) {
- Ref<Image> src_img = pieces[i].texture->get_data();
- img->blit_rect(src_img, Rect2(0, 0, src_img->get_width(), src_img->get_height()), pieces[i].offset);
+ } else {
+ for (int i = 0; i < _width; ++i) {
+ if (texture_mode == TEXTURE_MODE_RGB) {
+ wd[i * 3 + 0] = 0;
+ wd[i * 3 + 1] = 0;
+ wd[i * 3 + 2] = 0;
+ } else {
+ wd[i] = 0;
+ }
+ }
+ }
}
- return img;
-}
-
-void LargeTexture::_bind_methods() {
- ClassDB::bind_method(D_METHOD("add_piece", "ofs", "texture"), &LargeTexture::add_piece);
- ClassDB::bind_method(D_METHOD("set_piece_offset", "idx", "ofs"), &LargeTexture::set_piece_offset);
- ClassDB::bind_method(D_METHOD("set_piece_texture", "idx", "texture"), &LargeTexture::set_piece_texture);
- ClassDB::bind_method(D_METHOD("set_size", "size"), &LargeTexture::set_size);
- ClassDB::bind_method(D_METHOD("clear"), &LargeTexture::clear);
+ Ref<Image> image = memnew(Image(_width, 1, false, texture_mode == TEXTURE_MODE_RGB ? Image::FORMAT_RGBF : Image::FORMAT_RF, data));
- ClassDB::bind_method(D_METHOD("get_piece_count"), &LargeTexture::get_piece_count);
- ClassDB::bind_method(D_METHOD("get_piece_offset", "idx"), &LargeTexture::get_piece_offset);
- ClassDB::bind_method(D_METHOD("get_piece_texture", "idx"), &LargeTexture::get_piece_texture);
-
- ClassDB::bind_method(D_METHOD("_set_data", "data"), &LargeTexture::_set_data);
- ClassDB::bind_method(D_METHOD("_get_data"), &LargeTexture::_get_data);
+ if (_texture.is_valid()) {
+ if (_current_texture_mode != texture_mode || _current_width != _width) {
+ RID new_texture = RS::get_singleton()->texture_2d_create(image);
+ RS::get_singleton()->texture_replace(_texture, new_texture);
+ } else {
+ RS::get_singleton()->texture_2d_update(_texture, image);
+ }
+ } else {
+ _texture = RS::get_singleton()->texture_2d_create(image);
+ }
+ _current_texture_mode = texture_mode;
+ _current_width = _width;
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
+ emit_changed();
}
-void LargeTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat) const {
- for (int i = 0; i < pieces.size(); i++) {
- // TODO
- pieces[i].texture->draw(p_canvas_item, pieces[i].offset + p_pos, p_modulate, p_transpose, p_normal_map, p_specular_map, p_specular_color_shininess, p_texture_filter, p_texture_repeat);
- }
+Ref<Curve> CurveTexture::get_curve() const {
+ return _curve;
}
-void LargeTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat) const {
- //tiling not supported for this
- if (size.x == 0 || size.y == 0) {
+void CurveTexture::set_texture_mode(TextureMode p_mode) {
+ if (texture_mode == p_mode) {
return;
}
-
- Size2 scale = p_rect.size / size;
-
- for (int i = 0; i < pieces.size(); i++) {
- // TODO
- pieces[i].texture->draw_rect(p_canvas_item, Rect2(pieces[i].offset * scale + p_rect.position, pieces[i].texture->get_size() * scale), false, p_modulate, p_transpose, p_normal_map, p_specular_map, p_specular_color_shininess, p_texture_filter, p_texture_repeat);
- }
+ texture_mode = p_mode;
+ _update();
+}
+CurveTexture::TextureMode CurveTexture::get_texture_mode() const {
+ return texture_mode;
}
-void LargeTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat, bool p_clip_uv) const {
- //tiling not supported for this
- if (p_src_rect.size.x == 0 || p_src_rect.size.y == 0) {
- return;
- }
-
- Size2 scale = p_rect.size / p_src_rect.size;
-
- for (int i = 0; i < pieces.size(); i++) {
- // TODO
- Rect2 rect(pieces[i].offset, pieces[i].texture->get_size());
- if (!p_src_rect.intersects(rect)) {
- continue;
- }
- Rect2 local = p_src_rect.clip(rect);
- Rect2 target = local;
- target.size *= scale;
- target.position = p_rect.position + (p_src_rect.position + rect.position) * scale;
- local.position -= rect.position;
- pieces[i].texture->draw_rect_region(p_canvas_item, target, local, p_modulate, p_transpose, p_normal_map, p_specular_map, p_specular_color_shininess, p_texture_filter, p_texture_repeat, false);
+RID CurveTexture::get_rid() const {
+ if (!_texture.is_valid()) {
+ _texture = RS::get_singleton()->texture_2d_placeholder_create();
}
+ return _texture;
}
-bool LargeTexture::is_pixel_opaque(int p_x, int p_y) const {
- for (int i = 0; i < pieces.size(); i++) {
- // TODO
- if (!pieces[i].texture.is_valid()) {
- continue;
- }
+CurveTexture::CurveTexture() {}
- Rect2 rect(pieces[i].offset, pieces[i].texture->get_size());
- if (rect.has_point(Point2(p_x, p_y))) {
- return pieces[i].texture->is_pixel_opaque(p_x - rect.position.x, p_y - rect.position.y);
- }
+CurveTexture::~CurveTexture() {
+ if (_texture.is_valid()) {
+ RS::get_singleton()->free(_texture);
}
-
- return true;
}
-LargeTexture::LargeTexture() {
-}
+//////////////////
-///////////////////
+void CurveXYZTexture::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_width", "width"), &CurveXYZTexture::set_width);
-void CurveTexture::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_width", "width"), &CurveTexture::set_width);
+ ClassDB::bind_method(D_METHOD("set_curve_x", "curve"), &CurveXYZTexture::set_curve_x);
+ ClassDB::bind_method(D_METHOD("get_curve_x"), &CurveXYZTexture::get_curve_x);
- ClassDB::bind_method(D_METHOD("set_curve", "curve"), &CurveTexture::set_curve);
- ClassDB::bind_method(D_METHOD("get_curve"), &CurveTexture::get_curve);
+ ClassDB::bind_method(D_METHOD("set_curve_y", "curve"), &CurveXYZTexture::set_curve_y);
+ ClassDB::bind_method(D_METHOD("get_curve_y"), &CurveXYZTexture::get_curve_y);
- ClassDB::bind_method(D_METHOD("_update"), &CurveTexture::_update);
+ ClassDB::bind_method(D_METHOD("set_curve_z", "curve"), &CurveXYZTexture::set_curve_z);
+ ClassDB::bind_method(D_METHOD("get_curve_z"), &CurveXYZTexture::get_curve_z);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "32,4096"), "set_width", "get_width");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve", "get_curve");
+ ClassDB::bind_method(D_METHOD("_update"), &CurveXYZTexture::_update);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,4096"), "set_width", "get_width");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve_x", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve_x", "get_curve_x");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve_y", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve_y", "get_curve_y");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve_z", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve_z", "get_curve_z");
}
-void CurveTexture::set_width(int p_width) {
+void CurveXYZTexture::set_width(int p_width) {
ERR_FAIL_COND(p_width < 32 || p_width > 4096);
+
+ if (_width == p_width) {
+ return;
+ }
+
_width = p_width;
_update();
}
-int CurveTexture::get_width() const {
+int CurveXYZTexture::get_width() const {
return _width;
}
-void CurveTexture::ensure_default_setup(float p_min, float p_max) {
- if (_curve.is_null()) {
+void CurveXYZTexture::ensure_default_setup(float p_min, float p_max) {
+ if (_curve_x.is_null()) {
Ref<Curve> curve = Ref<Curve>(memnew(Curve));
curve->add_point(Vector2(0, 1));
curve->add_point(Vector2(1, 1));
curve->set_min_value(p_min);
curve->set_max_value(p_max);
- set_curve(curve);
- // Min and max is 0..1 by default
+ set_curve_x(curve);
+ }
+
+ if (_curve_y.is_null()) {
+ Ref<Curve> curve = Ref<Curve>(memnew(Curve));
+ curve->add_point(Vector2(0, 1));
+ curve->add_point(Vector2(1, 1));
+ curve->set_min_value(p_min);
+ curve->set_max_value(p_max);
+ set_curve_y(curve);
+ }
+
+ if (_curve_z.is_null()) {
+ Ref<Curve> curve = Ref<Curve>(memnew(Curve));
+ curve->add_point(Vector2(0, 1));
+ curve->add_point(Vector2(1, 1));
+ curve->set_min_value(p_min);
+ curve->set_max_value(p_max);
+ set_curve_z(curve);
}
}
-void CurveTexture::set_curve(Ref<Curve> p_curve) {
- if (_curve != p_curve) {
- if (_curve.is_valid()) {
- _curve->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveTexture::_update));
+void CurveXYZTexture::set_curve_x(Ref<Curve> p_curve) {
+ if (_curve_x != p_curve) {
+ if (_curve_x.is_valid()) {
+ _curve_x->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update));
}
- _curve = p_curve;
- if (_curve.is_valid()) {
- _curve->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveTexture::_update));
+ _curve_x = p_curve;
+ if (_curve_x.is_valid()) {
+ _curve_x->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update), varray(), CONNECT_REFERENCE_COUNTED);
}
_update();
}
}
-void CurveTexture::_update() {
+void CurveXYZTexture::set_curve_y(Ref<Curve> p_curve) {
+ if (_curve_y != p_curve) {
+ if (_curve_y.is_valid()) {
+ _curve_y->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update));
+ }
+ _curve_y = p_curve;
+ if (_curve_y.is_valid()) {
+ _curve_y->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update), varray(), CONNECT_REFERENCE_COUNTED);
+ }
+ _update();
+ }
+}
+
+void CurveXYZTexture::set_curve_z(Ref<Curve> p_curve) {
+ if (_curve_z != p_curve) {
+ if (_curve_z.is_valid()) {
+ _curve_z->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update));
+ }
+ _curve_z = p_curve;
+ if (_curve_z.is_valid()) {
+ _curve_z->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update), varray(), CONNECT_REFERENCE_COUNTED);
+ }
+ _update();
+ }
+}
+
+void CurveXYZTexture::_update() {
Vector<uint8_t> data;
- data.resize(_width * sizeof(float));
+ data.resize(_width * sizeof(float) * 3);
// The array is locked in that scope
{
uint8_t *wd8 = data.ptrw();
float *wd = (float *)wd8;
- if (_curve.is_valid()) {
- Curve &curve = **_curve;
+ if (_curve_x.is_valid()) {
+ Curve &curve_x = **_curve_x;
+ for (int i = 0; i < _width; ++i) {
+ float t = i / static_cast<float>(_width);
+ wd[i * 3 + 0] = curve_x.interpolate_baked(t);
+ }
+
+ } else {
+ for (int i = 0; i < _width; ++i) {
+ wd[i * 3 + 0] = 0;
+ }
+ }
+
+ if (_curve_y.is_valid()) {
+ Curve &curve_y = **_curve_y;
+ for (int i = 0; i < _width; ++i) {
+ float t = i / static_cast<float>(_width);
+ wd[i * 3 + 1] = curve_y.interpolate_baked(t);
+ }
+
+ } else {
+ for (int i = 0; i < _width; ++i) {
+ wd[i * 3 + 1] = 0;
+ }
+ }
+
+ if (_curve_z.is_valid()) {
+ Curve &curve_z = **_curve_z;
for (int i = 0; i < _width; ++i) {
float t = i / static_cast<float>(_width);
- wd[i] = curve.interpolate_baked(t);
+ wd[i * 3 + 2] = curve_z.interpolate_baked(t);
}
} else {
for (int i = 0; i < _width; ++i) {
- wd[i] = 0;
+ wd[i * 3 + 2] = 0;
}
}
}
- Ref<Image> image = memnew(Image(_width, 1, false, Image::FORMAT_RF, data));
+ Ref<Image> image = memnew(Image(_width, 1, false, Image::FORMAT_RGBF, data));
if (_texture.is_valid()) {
- RID new_texture = RS::get_singleton()->texture_2d_create(image);
- RS::get_singleton()->texture_replace(_texture, new_texture);
+ if (_current_width != _width) {
+ RID new_texture = RS::get_singleton()->texture_2d_create(image);
+ RS::get_singleton()->texture_replace(_texture, new_texture);
+ } else {
+ RS::get_singleton()->texture_2d_update(_texture, image);
+ }
} else {
_texture = RS::get_singleton()->texture_2d_create(image);
}
+ _current_width = _width;
emit_changed();
}
-Ref<Curve> CurveTexture::get_curve() const {
- return _curve;
+Ref<Curve> CurveXYZTexture::get_curve_x() const {
+ return _curve_x;
}
-RID CurveTexture::get_rid() const {
+Ref<Curve> CurveXYZTexture::get_curve_y() const {
+ return _curve_y;
+}
+
+Ref<Curve> CurveXYZTexture::get_curve_z() const {
+ return _curve_z;
+}
+
+RID CurveXYZTexture::get_rid() const {
if (!_texture.is_valid()) {
_texture = RS::get_singleton()->texture_2d_placeholder_create();
}
return _texture;
}
-CurveTexture::CurveTexture() {
- _width = 2048;
-}
+CurveXYZTexture::CurveXYZTexture() {}
-CurveTexture::~CurveTexture() {
+CurveXYZTexture::~CurveXYZTexture() {
if (_texture.is_valid()) {
RS::get_singleton()->free(_texture);
}
@@ -1739,16 +1743,7 @@ CurveTexture::~CurveTexture() {
//////////////////
-//setter and getter names for property serialization
-#define COLOR_RAMP_GET_OFFSETS "get_offsets"
-#define COLOR_RAMP_GET_COLORS "get_colors"
-#define COLOR_RAMP_SET_OFFSETS "set_offsets"
-#define COLOR_RAMP_SET_COLORS "set_colors"
-
GradientTexture::GradientTexture() {
- update_pending = false;
- width = 2048;
-
_queue_update();
}
@@ -1763,11 +1758,16 @@ void GradientTexture::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_gradient"), &GradientTexture::get_gradient);
ClassDB::bind_method(D_METHOD("set_width", "width"), &GradientTexture::set_width);
+ // The `get_width()` method is already exposed by the parent class Texture2D.
+
+ ClassDB::bind_method(D_METHOD("set_use_hdr", "enabled"), &GradientTexture::set_use_hdr);
+ ClassDB::bind_method(D_METHOD("is_using_hdr"), &GradientTexture::is_using_hdr);
ClassDB::bind_method(D_METHOD("_update"), &GradientTexture::_update);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_gradient", "get_gradient");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,2048,1,or_greater"), "set_width", "get_width");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,4096"), "set_width", "get_width");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hdr"), "set_use_hdr", "is_using_hdr");
}
void GradientTexture::set_gradient(Ref<Gradient> p_gradient) {
@@ -1795,7 +1795,7 @@ void GradientTexture::_queue_update() {
}
update_pending = true;
- call_deferred("_update");
+ call_deferred(SNAME("_update"));
}
void GradientTexture::_update() {
@@ -1805,36 +1805,56 @@ void GradientTexture::_update() {
return;
}
- Vector<uint8_t> data;
- data.resize(width * 4);
- {
- uint8_t *wd8 = data.ptrw();
+ if (use_hdr) {
+ // High dynamic range.
+ Ref<Image> image = memnew(Image(width, 1, false, Image::FORMAT_RGBAF));
Gradient &g = **gradient;
-
+ // `create()` isn't available for non-uint8_t data, so fill in the data manually.
for (int i = 0; i < width; i++) {
float ofs = float(i) / (width - 1);
- Color color = g.get_color_at_offset(ofs);
+ image->set_pixel(i, 0, g.get_color_at_offset(ofs));
+ }
- wd8[i * 4 + 0] = uint8_t(CLAMP(color.r * 255.0, 0, 255));
- wd8[i * 4 + 1] = uint8_t(CLAMP(color.g * 255.0, 0, 255));
- wd8[i * 4 + 2] = uint8_t(CLAMP(color.b * 255.0, 0, 255));
- wd8[i * 4 + 3] = uint8_t(CLAMP(color.a * 255.0, 0, 255));
+ if (texture.is_valid()) {
+ RID new_texture = RS::get_singleton()->texture_2d_create(image);
+ RS::get_singleton()->texture_replace(texture, new_texture);
+ } else {
+ texture = RS::get_singleton()->texture_2d_create(image);
+ }
+ } else {
+ // Low dynamic range. "Overbright" colors will be clamped.
+ Vector<uint8_t> data;
+ data.resize(width * 4);
+ {
+ uint8_t *wd8 = data.ptrw();
+ Gradient &g = **gradient;
+
+ for (int i = 0; i < width; i++) {
+ float ofs = float(i) / (width - 1);
+ Color color = g.get_color_at_offset(ofs);
+
+ wd8[i * 4 + 0] = uint8_t(CLAMP(color.r * 255.0, 0, 255));
+ wd8[i * 4 + 1] = uint8_t(CLAMP(color.g * 255.0, 0, 255));
+ wd8[i * 4 + 2] = uint8_t(CLAMP(color.b * 255.0, 0, 255));
+ wd8[i * 4 + 3] = uint8_t(CLAMP(color.a * 255.0, 0, 255));
+ }
}
- }
- Ref<Image> image = memnew(Image(width, 1, false, Image::FORMAT_RGBA8, data));
+ Ref<Image> image = memnew(Image(width, 1, false, Image::FORMAT_RGBA8, data));
- if (texture.is_valid()) {
- RID new_texture = RS::get_singleton()->texture_2d_create(image);
- RS::get_singleton()->texture_replace(texture, new_texture);
- } else {
- texture = RS::get_singleton()->texture_2d_create(image);
+ if (texture.is_valid()) {
+ RID new_texture = RS::get_singleton()->texture_2d_create(image);
+ RS::get_singleton()->texture_replace(texture, new_texture);
+ } else {
+ texture = RS::get_singleton()->texture_2d_create(image);
+ }
}
emit_changed();
}
void GradientTexture::set_width(int p_width) {
+ ERR_FAIL_COND(p_width <= 0);
width = p_width;
_queue_update();
}
@@ -1843,7 +1863,20 @@ int GradientTexture::get_width() const {
return width;
}
-Ref<Image> GradientTexture::get_data() const {
+void GradientTexture::set_use_hdr(bool p_enabled) {
+ if (p_enabled == use_hdr) {
+ return;
+ }
+
+ use_hdr = p_enabled;
+ _queue_update();
+}
+
+bool GradientTexture::is_using_hdr() const {
+ return use_hdr;
+}
+
+Ref<Image> GradientTexture::get_image() const {
if (!texture.is_valid()) {
return Ref<Image>();
}
@@ -1961,7 +1994,7 @@ void AnimatedTexture::_update_proxy() {
}
}
time -= frame_limit;
- _change_notify("current_frame");
+
} else {
break;
}
@@ -2092,14 +2125,14 @@ bool AnimatedTexture::has_alpha() const {
return frames[current_frame].texture->has_alpha();
}
-Ref<Image> AnimatedTexture::get_data() const {
+Ref<Image> AnimatedTexture::get_image() const {
RWLockRead r(rw_lock);
if (!frames[current_frame].texture.is_valid()) {
return Ref<Image>();
}
- return frames[current_frame].texture->get_data();
+ return frames[current_frame].texture->get_image();
}
bool AnimatedTexture::is_pixel_opaque(int p_x, int p_y) const {
@@ -2116,7 +2149,7 @@ void AnimatedTexture::_validate_property(PropertyInfo &property) const {
if (prop.begins_with("frame_")) {
int frame = prop.get_slicec('/', 0).get_slicec('_', 1).to_int();
if (frame >= frame_count) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
}
}
@@ -2144,7 +2177,7 @@ void AnimatedTexture::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_frame_delay", "frame"), &AnimatedTexture::get_frame_delay);
ADD_PROPERTY(PropertyInfo(Variant::INT, "frames", PROPERTY_HINT_RANGE, "1," + itos(MAX_FRAMES), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_frames", "get_frames");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "current_frame", PROPERTY_HINT_NONE, "", 0), "set_current_frame", "get_current_frame");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "current_frame", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_current_frame", "get_current_frame");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "pause"), "set_pause", "get_pause");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "oneshot"), "set_oneshot", "get_oneshot");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fps", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_fps", "get_fps");
@@ -2163,28 +2196,12 @@ AnimatedTexture::AnimatedTexture() {
proxy = RS::get_singleton()->texture_proxy_create(proxy_ph);
RenderingServer::get_singleton()->texture_set_force_redraw_if_visible(proxy, true);
- time = 0;
- frame_count = 1;
- fps = 4;
- prev_ticks = 0;
- current_frame = 0;
- pause = false;
- oneshot = false;
RenderingServer::get_singleton()->connect("frame_pre_draw", callable_mp(this, &AnimatedTexture::_update_proxy));
-
-#ifndef NO_THREADS
- rw_lock = RWLock::create();
-#else
- rw_lock = nullptr;
-#endif
}
AnimatedTexture::~AnimatedTexture() {
RS::get_singleton()->free(proxy);
RS::get_singleton()->free(proxy_ph);
- if (rw_lock) {
- memdelete(rw_lock);
- }
}
///////////////////////////////
@@ -2258,7 +2275,7 @@ Error ImageTextureLayered::create_from_images(Vector<Ref<Image>> p_images) {
"Cubemap array layers must be a multiple of 6");
}
- ERR_FAIL_COND_V(p_images[0].is_null() || p_images[0]->empty(), ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(p_images[0].is_null() || p_images[0]->is_empty(), ERR_INVALID_PARAMETER);
Image::Format new_format = p_images[0]->get_format();
int new_width = p_images[0]->get_width();
@@ -2332,11 +2349,6 @@ void ImageTextureLayered::_bind_methods() {
ImageTextureLayered::ImageTextureLayered(LayeredType p_layered_type) {
layered_type = p_layered_type;
- format = Image::FORMAT_MAX;
-
- width = 0;
- height = 0;
- layers = 0;
}
ImageTextureLayered::~ImageTextureLayered() {
@@ -2363,7 +2375,7 @@ Error StreamTextureLayered::_load_data(const String &p_path, Vector<Ref<Image>>
ERR_FAIL_COND_V(images.size() != 0, ERR_INVALID_PARAMETER);
FileAccessRef f = FileAccess::open(p_path, FileAccess::READ);
- ERR_FAIL_COND_V(!f, ERR_CANT_OPEN);
+ ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path));
uint8_t header[4];
f->get_buffer(header, 4);
@@ -2396,7 +2408,7 @@ Error StreamTextureLayered::_load_data(const String &p_path, Vector<Ref<Image>>
for (uint32_t i = 0; i < layer_count; i++) {
Ref<Image> image = StreamTexture2D::load_image_from_file(f, p_size_limit);
- ERR_FAIL_COND_V(image.is_null() || image->empty(), ERR_CANT_OPEN);
+ ERR_FAIL_COND_V(image.is_null() || image->is_empty(), ERR_CANT_OPEN);
images.write[i] = image;
}
@@ -2433,7 +2445,7 @@ Error StreamTextureLayered::load(const String &p_path) {
RenderingServer::get_singleton()->texture_set_path(texture, p_path);
}
- _change_notify();
+ notify_property_list_changed();
emit_changed();
return OK;
}
@@ -2504,11 +2516,6 @@ void StreamTextureLayered::_bind_methods() {
StreamTextureLayered::StreamTextureLayered(LayeredType p_type) {
layered_type = p_type;
- format = Image::FORMAT_MAX;
- w = 0;
- h = 0;
- layers = 0;
- mipmaps = false;
}
StreamTextureLayered::~StreamTextureLayered() {
@@ -2519,19 +2526,19 @@ StreamTextureLayered::~StreamTextureLayered() {
/////////////////////////////////////////////////
-RES ResourceFormatLoaderStreamTextureLayered::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoaderStreamTextureLayered::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
Ref<StreamTextureLayered> st;
if (p_path.get_extension().to_lower() == "stexarray") {
Ref<StreamTexture2DArray> s;
- s.instance();
+ s.instantiate();
st = s;
} else if (p_path.get_extension().to_lower() == "scube") {
Ref<StreamCubemap> s;
- s.instance();
+ s.instantiate();
st = s;
} else if (p_path.get_extension().to_lower() == "scubearray") {
Ref<StreamCubemapArray> s;
- s.instance();
+ s.instantiate();
st = s;
} else {
if (r_error) {
@@ -2617,7 +2624,10 @@ RID CameraTexture::get_rid() const {
if (feed.is_valid()) {
return feed->get_texture(which_feed);
} else {
- return RID();
+ if (_texture.is_null()) {
+ _texture = RenderingServer::get_singleton()->texture_2d_placeholder_create();
+ }
+ return _texture;
}
}
@@ -2630,14 +2640,14 @@ uint32_t CameraTexture::get_flags() const {
return 0;
}
-Ref<Image> CameraTexture::get_data() const {
+Ref<Image> CameraTexture::get_image() const {
// not (yet) supported
return Ref<Image>();
}
void CameraTexture::set_camera_feed_id(int p_new_id) {
camera_feed_id = p_new_id;
- _change_notify();
+ notify_property_list_changed();
}
int CameraTexture::get_camera_feed_id() const {
@@ -2646,7 +2656,7 @@ int CameraTexture::get_camera_feed_id() const {
void CameraTexture::set_which_feed(CameraServer::FeedImage p_which) {
which_feed = p_which;
- _change_notify();
+ notify_property_list_changed();
}
CameraServer::FeedImage CameraTexture::get_which_feed() const {
@@ -2657,7 +2667,7 @@ void CameraTexture::set_camera_active(bool p_active) {
Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
if (feed.is_valid()) {
feed->set_active(p_active);
- _change_notify();
+ notify_property_list_changed();
}
}
@@ -2670,11 +2680,10 @@ bool CameraTexture::get_camera_active() const {
}
}
-CameraTexture::CameraTexture() {
- camera_feed_id = 0;
- which_feed = CameraServer::FEED_RGBA_IMAGE;
-}
+CameraTexture::CameraTexture() {}
CameraTexture::~CameraTexture() {
- // nothing to do here yet
+ if (_texture.is_valid()) {
+ RenderingServer::get_singleton()->free(_texture);
+ }
}
diff --git a/scene/resources/texture.h b/scene/resources/texture.h
index eebbf4f233..f6b991c335 100644
--- a/scene/resources/texture.h
+++ b/scene/resources/texture.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,13 +31,13 @@
#ifndef TEXTURE_H
#define TEXTURE_H
+#include "core/io/file_access.h"
+#include "core/io/resource.h"
#include "core/io/resource_loader.h"
#include "core/math/rect2.h"
-#include "core/os/file_access.h"
#include "core/os/mutex.h"
#include "core/os/rw_lock.h"
#include "core/os/thread_safe.h"
-#include "core/resource.h"
#include "scene/resources/curve.h"
#include "scene/resources/gradient.h"
#include "servers/camera_server.h"
@@ -66,12 +66,12 @@ public:
virtual bool has_alpha() const = 0;
- virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) const;
- virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) const;
- virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT, bool p_clip_uv = true) const;
+ virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const;
+ virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const;
+ virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = true) const;
virtual bool get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const;
- virtual Ref<Image> get_data() const { return Ref<Image>(); }
+ virtual Ref<Image> get_image() const { return Ref<Image>(); }
Texture2D();
};
@@ -83,12 +83,13 @@ class ImageTexture : public Texture2D {
RES_BASE_EXTENSION("tex");
mutable RID texture;
- Image::Format format;
- bool mipmaps;
- int w, h;
+ Image::Format format = Image::FORMAT_L8;
+ bool mipmaps = false;
+ int w = 0;
+ int h = 0;
Size2 size_override;
mutable Ref<BitMap> alpha_cache;
- bool image_stored;
+ bool image_stored = false;
protected:
virtual void reload_from_file() override;
@@ -106,8 +107,8 @@ public:
Image::Format get_format() const;
- void update(const Ref<Image> &p_image, bool p_immediate = false);
- Ref<Image> get_data() const override;
+ void update(const Ref<Image> &p_image);
+ Ref<Image> get_image() const override;
int get_width() const override;
int get_height() const override;
@@ -115,9 +116,9 @@ public:
virtual RID get_rid() const override;
bool has_alpha() const override;
- virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) const override;
- virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) const override;
- virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT, bool p_clip_uv = true) const override;
+ virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = true) const override;
bool is_pixel_opaque(int p_x, int p_y) const override;
@@ -135,8 +136,8 @@ class StreamTexture2D : public Texture2D {
public:
enum DataFormat {
DATA_FORMAT_IMAGE,
- DATA_FORMAT_LOSSLESS,
- DATA_FORMAT_LOSSY,
+ DATA_FORMAT_PNG,
+ DATA_FORMAT_WEBP,
DATA_FORMAT_BASIS_UNIVERSAL,
};
@@ -145,9 +146,6 @@ public:
};
enum FormatBits {
- FORMAT_MASK_IMAGE_FORMAT = (1 << 20) - 1,
- FORMAT_BIT_LOSSLESS = 1 << 20,
- FORMAT_BIT_LOSSY = 1 << 21,
FORMAT_BIT_STREAM = 1 << 22,
FORMAT_BIT_HAS_MIPMAPS = 1 << 23,
FORMAT_BIT_DETECT_3D = 1 << 24,
@@ -157,11 +155,12 @@ public:
};
private:
- Error _load_data(const String &p_path, int &tw, int &th, int &tw_custom, int &th_custom, Ref<Image> &image, bool &r_request_3d, bool &r_request_normal, bool &r_request_roughness, int &mipmap_limit, int p_size_limit = 0);
+ Error _load_data(const String &p_path, int &r_width, int &r_height, Ref<Image> &image, bool &r_request_3d, bool &r_request_normal, bool &r_request_roughness, int &mipmap_limit, int p_size_limit = 0);
String path_to_file;
mutable RID texture;
- Image::Format format;
- int w, h;
+ Image::Format format = Image::FORMAT_MAX;
+ int w = 0;
+ int h = 0;
mutable Ref<BitMap> alpha_cache;
virtual void reload_from_file() override;
@@ -194,14 +193,14 @@ public:
virtual void set_path(const String &p_path, bool p_take_over) override;
- virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) const override;
- virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) const override;
- virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT, bool p_clip_uv = true) const override;
+ virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = true) const override;
virtual bool has_alpha() const override;
bool is_pixel_opaque(int p_x, int p_y) const override;
- virtual Ref<Image> get_data() const override;
+ virtual Ref<Image> get_image() const override;
StreamTexture2D();
~StreamTexture2D();
@@ -209,7 +208,7 @@ public:
class ResourceFormatLoaderStreamTexture2D : public ResourceFormatLoader {
public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+ virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
@@ -223,7 +222,7 @@ protected:
Ref<Texture2D> atlas;
Rect2 region;
Rect2 margin;
- bool filter_clip;
+ bool filter_clip = false;
static void _bind_methods();
@@ -246,9 +245,9 @@ public:
void set_filter_clip(const bool p_enable);
bool has_filter_clip() const;
- virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) const override;
- virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) const override;
- virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT, bool p_clip_uv = true) const override;
+ virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = true) const override;
virtual bool get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const override;
bool is_pixel_opaque(int p_x, int p_y) const override;
@@ -285,9 +284,9 @@ public:
void set_base_texture(const Ref<Texture2D> &p_texture);
Ref<Texture2D> get_base_texture() const;
- virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) const override;
- virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) const override;
- virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT, bool p_clip_uv = true) const override;
+ virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = true) const override;
virtual bool get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const override;
bool is_pixel_opaque(int p_x, int p_y) const override;
@@ -295,51 +294,6 @@ public:
MeshTexture();
};
-class LargeTexture : public Texture2D {
- GDCLASS(LargeTexture, Texture2D);
- RES_BASE_EXTENSION("largetex");
-
-protected:
- struct Piece {
- Point2 offset;
- Ref<Texture2D> texture;
- };
-
- Vector<Piece> pieces;
- Size2i size;
-
- Array _get_data() const;
- void _set_data(const Array &p_array);
- static void _bind_methods();
-
-public:
- virtual int get_width() const override;
- virtual int get_height() const override;
- virtual RID get_rid() const override;
-
- virtual bool has_alpha() const override;
-
- int add_piece(const Point2 &p_offset, const Ref<Texture2D> &p_texture);
- void set_piece_offset(int p_idx, const Point2 &p_offset);
- void set_piece_texture(int p_idx, const Ref<Texture2D> &p_texture);
-
- void set_size(const Size2 &p_size);
- void clear();
-
- int get_piece_count() const;
- Vector2 get_piece_offset(int p_idx) const;
- Ref<Texture2D> get_piece_texture(int p_idx) const;
- Ref<Image> to_image() const;
-
- virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) const override;
- virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) const override;
- virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT, bool p_clip_uv = true) const override;
-
- bool is_pixel_opaque(int p_x, int p_y) const override;
-
- LargeTexture();
-};
-
class TextureLayered : public Texture {
GDCLASS(TextureLayered, Texture);
@@ -370,12 +324,12 @@ class ImageTextureLayered : public TextureLayered {
LayeredType layered_type;
mutable RID texture;
- Image::Format format;
+ Image::Format format = Image::FORMAT_MAX;
- int width;
- int height;
- int layers;
- bool mipmaps;
+ int width = 0;
+ int height = 0;
+ int layers = 0;
+ bool mipmaps = false;
Error _create_from_images(const Array &p_images);
@@ -432,8 +386,8 @@ class StreamTextureLayered : public TextureLayered {
public:
enum DataFormat {
DATA_FORMAT_IMAGE,
- DATA_FORMAT_LOSSLESS,
- DATA_FORMAT_LOSSY,
+ DATA_FORMAT_PNG,
+ DATA_FORMAT_WEBP,
DATA_FORMAT_BASIS_UNIVERSAL,
};
@@ -442,9 +396,6 @@ public:
};
enum FormatBits {
- FORMAT_MASK_IMAGE_FORMAT = (1 << 20) - 1,
- FORMAT_BIT_LOSSLESS = 1 << 20,
- FORMAT_BIT_LOSSY = 1 << 21,
FORMAT_BIT_STREAM = 1 << 22,
FORMAT_BIT_HAS_MIPMAPS = 1 << 23,
};
@@ -453,10 +404,12 @@ private:
Error _load_data(const String &p_path, Vector<Ref<Image>> &images, int &mipmap_limit, int p_size_limit = 0);
String path_to_file;
mutable RID texture;
- Image::Format format;
- int w, h, layers;
- bool mipmaps;
- LayeredType layered_type;
+ Image::Format format = Image::FORMAT_MAX;
+ int w = 0;
+ int h = 0;
+ int layers = 0;
+ bool mipmaps = false;
+ LayeredType layered_type = LayeredType::LAYERED_TYPE_2D_ARRAY;
virtual void reload_from_file() override;
@@ -509,7 +462,7 @@ public:
class ResourceFormatLoaderStreamTextureLayered : public ResourceFormatLoader {
public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+ virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
@@ -573,8 +526,8 @@ class StreamTexture3D : public Texture3D {
public:
enum DataFormat {
DATA_FORMAT_IMAGE,
- DATA_FORMAT_LOSSLESS,
- DATA_FORMAT_LOSSY,
+ DATA_FORMAT_PNG,
+ DATA_FORMAT_WEBP,
DATA_FORMAT_BASIS_UNIVERSAL,
};
@@ -583,9 +536,6 @@ public:
};
enum FormatBits {
- FORMAT_MASK_IMAGE_FORMAT = (1 << 20) - 1,
- FORMAT_BIT_LOSSLESS = 1 << 20,
- FORMAT_BIT_LOSSY = 1 << 21,
FORMAT_BIT_STREAM = 1 << 22,
FORMAT_BIT_HAS_MIPMAPS = 1 << 23,
};
@@ -594,9 +544,11 @@ private:
Error _load_data(const String &p_path, Vector<Ref<Image>> &r_data, Image::Format &r_format, int &r_width, int &r_height, int &r_depth, bool &r_mipmaps);
String path_to_file;
mutable RID texture;
- Image::Format format;
- int w, h, d;
- bool mipmaps;
+ Image::Format format = Image::FORMAT_MAX;
+ int w = 0;
+ int h = 0;
+ int d = 0;
+ bool mipmaps = false;
virtual void reload_from_file() override;
@@ -625,7 +577,7 @@ public:
class ResourceFormatLoaderStreamTexture3D : public ResourceFormatLoader {
public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+ virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
@@ -634,11 +586,19 @@ public:
class CurveTexture : public Texture2D {
GDCLASS(CurveTexture, Texture2D);
RES_BASE_EXTENSION("curvetex")
+public:
+ enum TextureMode {
+ TEXTURE_MODE_RGB,
+ TEXTURE_MODE_RED,
+ };
private:
mutable RID _texture;
Ref<Curve> _curve;
- int _width;
+ int _width = 2048;
+ int _current_width = 0;
+ TextureMode texture_mode = TEXTURE_MODE_RGB;
+ TextureMode _current_texture_mode = TEXTURE_MODE_RGB;
void _update();
@@ -649,6 +609,9 @@ public:
void set_width(int p_width);
int get_width() const override;
+ void set_texture_mode(TextureMode p_mode);
+ TextureMode get_texture_mode() const;
+
void ensure_default_setup(float p_min = 0, float p_max = 1);
void set_curve(Ref<Curve> p_curve);
@@ -662,26 +625,56 @@ public:
CurveTexture();
~CurveTexture();
};
-/*
- enum CubeMapSide {
-
- CUBEMAP_LEFT,
- CUBEMAP_RIGHT,
- CUBEMAP_BOTTOM,
- CUBEMAP_TOP,
- CUBEMAP_FRONT,
- CUBEMAP_BACK,
- };
-*/
-//VARIANT_ENUM_CAST( Texture::CubeMapSide );
+VARIANT_ENUM_CAST(CurveTexture::TextureMode)
+
+class CurveXYZTexture : public Texture2D {
+ GDCLASS(CurveXYZTexture, Texture2D);
+ RES_BASE_EXTENSION("curvetex")
+
+private:
+ mutable RID _texture;
+ Ref<Curve> _curve_x;
+ Ref<Curve> _curve_y;
+ Ref<Curve> _curve_z;
+ int _width = 2048;
+ int _current_width = 0;
+
+ void _update();
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_width(int p_width);
+ int get_width() const override;
+
+ void ensure_default_setup(float p_min = 0, float p_max = 1);
+
+ void set_curve_x(Ref<Curve> p_curve);
+ Ref<Curve> get_curve_x() const;
+
+ void set_curve_y(Ref<Curve> p_curve);
+ Ref<Curve> get_curve_y() const;
+
+ void set_curve_z(Ref<Curve> p_curve);
+ Ref<Curve> get_curve_z() const;
+
+ virtual RID get_rid() const override;
+
+ virtual int get_height() const override { return 1; }
+ virtual bool has_alpha() const override { return false; }
+
+ CurveXYZTexture();
+ ~CurveXYZTexture();
+};
class GradientTexture : public Texture2D {
GDCLASS(GradientTexture, Texture2D);
public:
struct Point {
- float offset;
+ float offset = 0.0;
Color color;
bool operator<(const Point &p_ponit) const {
return offset < p_ponit.offset;
@@ -690,9 +683,10 @@ public:
private:
Ref<Gradient> gradient;
- bool update_pending;
+ bool update_pending = false;
RID texture;
- int width;
+ int width = 2048;
+ bool use_hdr = false;
void _queue_update();
void _update();
@@ -707,11 +701,14 @@ public:
void set_width(int p_width);
int get_width() const override;
+ void set_use_hdr(bool p_enabled);
+ bool is_using_hdr() const;
+
virtual RID get_rid() const override { return texture; }
virtual int get_height() const override { return 1; }
virtual bool has_alpha() const override { return true; }
- virtual Ref<Image> get_data() const override;
+ virtual Ref<Image> get_image() const override;
GradientTexture();
virtual ~GradientTexture();
@@ -746,7 +743,7 @@ class AnimatedTexture : public Texture2D {
GDCLASS(AnimatedTexture, Texture2D);
//use readers writers lock for this, since its far more times read than written to
- RWLock *rw_lock;
+ RWLock rw_lock;
public:
enum {
@@ -759,23 +756,19 @@ private:
struct Frame {
Ref<Texture2D> texture;
- float delay_sec;
-
- Frame() {
- delay_sec = 0;
- }
+ float delay_sec = 0.0;
};
Frame frames[MAX_FRAMES];
- int frame_count;
- int current_frame;
- bool pause;
- bool oneshot;
- float fps;
+ int frame_count = 1.0;
+ int current_frame = 0;
+ bool pause = false;
+ bool oneshot = false;
+ float fps = 4.0;
- float time;
+ float time = 0.0;
- uint64_t prev_ticks;
+ uint64_t prev_ticks = 0;
void _update_proxy();
@@ -811,7 +804,7 @@ public:
virtual bool has_alpha() const override;
- virtual Ref<Image> get_data() const override;
+ virtual Ref<Image> get_image() const override;
bool is_pixel_opaque(int p_x, int p_y) const override;
@@ -823,8 +816,9 @@ class CameraTexture : public Texture2D {
GDCLASS(CameraTexture, Texture2D);
private:
- int camera_feed_id;
- CameraServer::FeedImage which_feed;
+ mutable RID _texture;
+ int camera_feed_id = 0;
+ CameraServer::FeedImage which_feed = CameraServer::FEED_RGBA_IMAGE;
protected:
static void _bind_methods();
@@ -838,7 +832,7 @@ public:
virtual void set_flags(uint32_t p_flags);
virtual uint32_t get_flags() const;
- virtual Ref<Image> get_data() const override;
+ virtual Ref<Image> get_image() const override;
void set_camera_feed_id(int p_new_id);
int get_camera_feed_id() const;
diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp
index 6a85d357ff..e49d883ba4 100644
--- a/scene/resources/theme.cpp
+++ b/scene/resources/theme.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,18 +29,98 @@
/*************************************************************************/
#include "theme.h"
-#include "core/os/file_access.h"
-#include "core/print_string.h"
+#include "core/io/file_access.h"
+#include "core/string/print_string.h"
void Theme::_emit_theme_changed() {
+ if (no_change_propagation) {
+ return;
+ }
+
+ notify_property_list_changed();
emit_changed();
}
-Vector<String> Theme::_get_icon_list(const String &p_type) const {
+Vector<String> Theme::_get_icon_list(const String &p_theme_type) const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_icon_list(p_theme_type, &il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_icon_type_list() const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_icon_type_list(&il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_stylebox_list(const String &p_theme_type) const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_stylebox_list(p_theme_type, &il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_stylebox_type_list() const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_stylebox_type_list(&il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_font_list(const String &p_theme_type) const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_font_list(p_theme_type, &il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_font_type_list() const {
Vector<String> ilret;
List<StringName> il;
- get_icon_list(p_type, &il);
+ get_font_type_list(&il);
ilret.resize(il.size());
int i = 0;
@@ -51,11 +131,11 @@ Vector<String> Theme::_get_icon_list(const String &p_type) const {
return ilret;
}
-Vector<String> Theme::_get_stylebox_list(const String &p_type) const {
+Vector<String> Theme::_get_font_size_list(const String &p_theme_type) const {
Vector<String> ilret;
List<StringName> il;
- get_stylebox_list(p_type, &il);
+ get_font_size_list(p_theme_type, &il);
ilret.resize(il.size());
int i = 0;
@@ -66,11 +146,11 @@ Vector<String> Theme::_get_stylebox_list(const String &p_type) const {
return ilret;
}
-Vector<String> Theme::_get_stylebox_types() const {
+Vector<String> Theme::_get_font_size_type_list() const {
Vector<String> ilret;
List<StringName> il;
- get_stylebox_types(&il);
+ get_font_size_type_list(&il);
ilret.resize(il.size());
int i = 0;
@@ -81,11 +161,11 @@ Vector<String> Theme::_get_stylebox_types() const {
return ilret;
}
-Vector<String> Theme::_get_font_list(const String &p_type) const {
+Vector<String> Theme::_get_color_list(const String &p_theme_type) const {
Vector<String> ilret;
List<StringName> il;
- get_font_list(p_type, &il);
+ get_color_list(p_theme_type, &il);
ilret.resize(il.size());
int i = 0;
@@ -96,11 +176,11 @@ Vector<String> Theme::_get_font_list(const String &p_type) const {
return ilret;
}
-Vector<String> Theme::_get_color_list(const String &p_type) const {
+Vector<String> Theme::_get_color_type_list() const {
Vector<String> ilret;
List<StringName> il;
- get_color_list(p_type, &il);
+ get_color_type_list(&il);
ilret.resize(il.size());
int i = 0;
@@ -111,11 +191,11 @@ Vector<String> Theme::_get_color_list(const String &p_type) const {
return ilret;
}
-Vector<String> Theme::_get_constant_list(const String &p_type) const {
+Vector<String> Theme::_get_constant_list(const String &p_theme_type) const {
Vector<String> ilret;
List<StringName> il;
- get_constant_list(p_type, &il);
+ get_constant_list(p_theme_type, &il);
ilret.resize(il.size());
int i = 0;
@@ -126,7 +206,79 @@ Vector<String> Theme::_get_constant_list(const String &p_type) const {
return ilret;
}
-Vector<String> Theme::_get_type_list(const String &p_type) const {
+Vector<String> Theme::_get_constant_type_list() const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_constant_type_list(&il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_theme_item_list(DataType p_data_type, const String &p_theme_type) const {
+ switch (p_data_type) {
+ case DATA_TYPE_COLOR:
+ return _get_color_list(p_theme_type);
+ case DATA_TYPE_CONSTANT:
+ return _get_constant_list(p_theme_type);
+ case DATA_TYPE_FONT:
+ return _get_font_list(p_theme_type);
+ case DATA_TYPE_FONT_SIZE:
+ return _get_font_size_list(p_theme_type);
+ case DATA_TYPE_ICON:
+ return _get_icon_list(p_theme_type);
+ case DATA_TYPE_STYLEBOX:
+ return _get_stylebox_list(p_theme_type);
+ case DATA_TYPE_MAX:
+ break; // Can't happen, but silences warning.
+ }
+
+ return Vector<String>();
+}
+
+Vector<String> Theme::_get_theme_item_type_list(DataType p_data_type) const {
+ switch (p_data_type) {
+ case DATA_TYPE_COLOR:
+ return _get_color_type_list();
+ case DATA_TYPE_CONSTANT:
+ return _get_constant_type_list();
+ case DATA_TYPE_FONT:
+ return _get_font_type_list();
+ case DATA_TYPE_FONT_SIZE:
+ return _get_font_size_type_list();
+ case DATA_TYPE_ICON:
+ return _get_icon_type_list();
+ case DATA_TYPE_STYLEBOX:
+ return _get_stylebox_type_list();
+ case DATA_TYPE_MAX:
+ break; // Can't happen, but silences warning.
+ }
+
+ return Vector<String>();
+}
+
+Vector<String> Theme::_get_type_variation_list(const StringName &p_theme_type) const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_type_variation_list(p_theme_type, &il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_type_list() const {
Vector<String> ilret;
List<StringName> il;
@@ -146,19 +298,23 @@ bool Theme::_set(const StringName &p_name, const Variant &p_value) {
if (sname.find("/") != -1) {
String type = sname.get_slicec('/', 1);
- String node_type = sname.get_slicec('/', 0);
+ String theme_type = sname.get_slicec('/', 0);
String name = sname.get_slicec('/', 2);
if (type == "icons") {
- set_icon(name, node_type, p_value);
+ set_icon(name, theme_type, p_value);
} else if (type == "styles") {
- set_stylebox(name, node_type, p_value);
+ set_stylebox(name, theme_type, p_value);
} else if (type == "fonts") {
- set_font(name, node_type, p_value);
+ set_font(name, theme_type, p_value);
+ } else if (type == "font_sizes") {
+ set_font_size(name, theme_type, p_value);
} else if (type == "colors") {
- set_color(name, node_type, p_value);
+ set_color(name, theme_type, p_value);
} else if (type == "constants") {
- set_constant(name, node_type, p_value);
+ set_constant(name, theme_type, p_value);
+ } else if (type == "base_type") {
+ set_type_variation(theme_type, p_value);
} else {
return false;
}
@@ -174,31 +330,35 @@ bool Theme::_get(const StringName &p_name, Variant &r_ret) const {
if (sname.find("/") != -1) {
String type = sname.get_slicec('/', 1);
- String node_type = sname.get_slicec('/', 0);
+ String theme_type = sname.get_slicec('/', 0);
String name = sname.get_slicec('/', 2);
if (type == "icons") {
- if (!has_icon(name, node_type)) {
+ if (!has_icon(name, theme_type)) {
r_ret = Ref<Texture2D>();
} else {
- r_ret = get_icon(name, node_type);
+ r_ret = get_icon(name, theme_type);
}
} else if (type == "styles") {
- if (!has_stylebox(name, node_type)) {
+ if (!has_stylebox(name, theme_type)) {
r_ret = Ref<StyleBox>();
} else {
- r_ret = get_stylebox(name, node_type);
+ r_ret = get_stylebox(name, theme_type);
}
} else if (type == "fonts") {
- if (!has_font(name, node_type)) {
+ if (!has_font(name, theme_type)) {
r_ret = Ref<Font>();
} else {
- r_ret = get_font(name, node_type);
+ r_ret = get_font(name, theme_type);
}
+ } else if (type == "font_sizes") {
+ r_ret = get_font_size(name, theme_type);
} else if (type == "colors") {
- r_ret = get_color(name, node_type);
+ r_ret = get_color(name, theme_type);
} else if (type == "constants") {
- r_ret = get_constant(name, node_type);
+ r_ret = get_constant(name, theme_type);
+ } else if (type == "base_type") {
+ r_ret = get_type_variation_base(theme_type);
} else {
return false;
}
@@ -214,6 +374,14 @@ void Theme::_get_property_list(List<PropertyInfo> *p_list) const {
const StringName *key = nullptr;
+ // Type variations.
+ while ((key = variation_map.next(key))) {
+ list.push_back(PropertyInfo(Variant::STRING_NAME, String() + *key + "/base_type"));
+ }
+
+ key = nullptr;
+
+ // Icons.
while ((key = icon_map.next(key))) {
const StringName *key2 = nullptr;
@@ -224,6 +392,7 @@ void Theme::_get_property_list(List<PropertyInfo> *p_list) const {
key = nullptr;
+ // Styles.
while ((key = style_map.next(key))) {
const StringName *key2 = nullptr;
@@ -234,6 +403,7 @@ void Theme::_get_property_list(List<PropertyInfo> *p_list) const {
key = nullptr;
+ // Fonts.
while ((key = font_map.next(key))) {
const StringName *key2 = nullptr;
@@ -244,6 +414,18 @@ void Theme::_get_property_list(List<PropertyInfo> *p_list) const {
key = nullptr;
+ // Font sizes.
+ while ((key = font_size_map.next(key))) {
+ const StringName *key2 = nullptr;
+
+ while ((key2 = font_size_map[*key].next(key2))) {
+ list.push_back(PropertyInfo(Variant::INT, String() + *key + "/font_sizes/" + *key2));
+ }
+ }
+
+ key = nullptr;
+
+ // Colors.
while ((key = color_map.next(key))) {
const StringName *key2 = nullptr;
@@ -254,6 +436,7 @@ void Theme::_get_property_list(List<PropertyInfo> *p_list) const {
key = nullptr;
+ // Constants.
while ((key = constant_map.next(key))) {
const StringName *key2 = nullptr;
@@ -262,9 +445,10 @@ void Theme::_get_property_list(List<PropertyInfo> *p_list) const {
}
}
+ // Sort and store properties.
list.sort();
- for (List<PropertyInfo>::Element *E = list.front(); E; E = E->next()) {
- p_list->push_back(E->get());
+ for (const PropertyInfo &E : list) {
+ p_list->push_back(E);
}
}
@@ -283,19 +467,33 @@ void Theme::set_default_theme_font(const Ref<Font> &p_default_font) {
default_theme_font->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED);
}
- _change_notify();
- emit_changed();
+ _emit_theme_changed();
}
Ref<Font> Theme::get_default_theme_font() const {
return default_theme_font;
}
+void Theme::set_default_theme_font_size(int p_font_size) {
+ if (default_theme_font_size == p_font_size) {
+ return;
+ }
+
+ default_theme_font_size = p_font_size;
+
+ _emit_theme_changed();
+}
+
+int Theme::get_default_theme_font_size() const {
+ return default_theme_font_size;
+}
+
Ref<Theme> Theme::project_default_theme;
Ref<Theme> Theme::default_theme;
Ref<Texture2D> Theme::default_icon;
Ref<StyleBox> Theme::default_style;
Ref<Font> Theme::default_font;
+int Theme::default_font_size = 16;
Ref<Theme> Theme::get_default() {
return default_theme;
@@ -325,338 +523,781 @@ void Theme::set_default_font(const Ref<Font> &p_font) {
default_font = p_font;
}
-void Theme::set_icon(const StringName &p_name, const StringName &p_type, const Ref<Texture2D> &p_icon) {
- //ERR_FAIL_COND(p_icon.is_null());
-
- bool new_value = !icon_map.has(p_type) || !icon_map[p_type].has(p_name);
+void Theme::set_default_font_size(int p_font_size) {
+ default_font_size = p_font_size;
+}
- if (icon_map[p_type].has(p_name) && icon_map[p_type][p_name].is_valid()) {
- icon_map[p_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+void Theme::set_icon(const StringName &p_name, const StringName &p_theme_type, const Ref<Texture2D> &p_icon) {
+ if (icon_map[p_theme_type].has(p_name) && icon_map[p_theme_type][p_name].is_valid()) {
+ icon_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
}
- icon_map[p_type][p_name] = p_icon;
+ icon_map[p_theme_type][p_name] = p_icon;
if (p_icon.is_valid()) {
- icon_map[p_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ icon_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED);
}
- if (new_value) {
- _change_notify();
- emit_changed();
- }
+ _emit_theme_changed();
}
-Ref<Texture2D> Theme::get_icon(const StringName &p_name, const StringName &p_type) const {
- if (icon_map.has(p_type) && icon_map[p_type].has(p_name) && icon_map[p_type][p_name].is_valid()) {
- return icon_map[p_type][p_name];
+Ref<Texture2D> Theme::get_icon(const StringName &p_name, const StringName &p_theme_type) const {
+ if (icon_map.has(p_theme_type) && icon_map[p_theme_type].has(p_name) && icon_map[p_theme_type][p_name].is_valid()) {
+ return icon_map[p_theme_type][p_name];
} else {
return default_icon;
}
}
-bool Theme::has_icon(const StringName &p_name, const StringName &p_type) const {
- return (icon_map.has(p_type) && icon_map[p_type].has(p_name) && icon_map[p_type][p_name].is_valid());
+bool Theme::has_icon(const StringName &p_name, const StringName &p_theme_type) const {
+ return (icon_map.has(p_theme_type) && icon_map[p_theme_type].has(p_name) && icon_map[p_theme_type][p_name].is_valid());
+}
+
+bool Theme::has_icon_nocheck(const StringName &p_name, const StringName &p_theme_type) const {
+ return (icon_map.has(p_theme_type) && icon_map[p_theme_type].has(p_name));
}
-void Theme::clear_icon(const StringName &p_name, const StringName &p_type) {
- ERR_FAIL_COND(!icon_map.has(p_type));
- ERR_FAIL_COND(!icon_map[p_type].has(p_name));
+void Theme::rename_icon(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
+ ERR_FAIL_COND_MSG(!icon_map.has(p_theme_type), "Cannot rename the icon '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist.");
+ ERR_FAIL_COND_MSG(icon_map[p_theme_type].has(p_name), "Cannot rename the icon '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists.");
+ ERR_FAIL_COND_MSG(!icon_map[p_theme_type].has(p_old_name), "Cannot rename the icon '" + String(p_old_name) + "' because it does not exist.");
- if (icon_map[p_type][p_name].is_valid()) {
- icon_map[p_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ icon_map[p_theme_type][p_name] = icon_map[p_theme_type][p_old_name];
+ icon_map[p_theme_type].erase(p_old_name);
+
+ _emit_theme_changed();
+}
+
+void Theme::clear_icon(const StringName &p_name, const StringName &p_theme_type) {
+ ERR_FAIL_COND_MSG(!icon_map.has(p_theme_type), "Cannot clear the icon '" + String(p_name) + "' because the node type '" + String(p_theme_type) + "' does not exist.");
+ ERR_FAIL_COND_MSG(!icon_map[p_theme_type].has(p_name), "Cannot clear the icon '" + String(p_name) + "' because it does not exist.");
+
+ if (icon_map[p_theme_type][p_name].is_valid()) {
+ icon_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
}
- icon_map[p_type].erase(p_name);
+ icon_map[p_theme_type].erase(p_name);
- _change_notify();
- emit_changed();
+ _emit_theme_changed();
}
-void Theme::get_icon_list(StringName p_type, List<StringName> *p_list) const {
+void Theme::get_icon_list(StringName p_theme_type, List<StringName> *p_list) const {
ERR_FAIL_NULL(p_list);
- if (!icon_map.has(p_type)) {
+ if (!icon_map.has(p_theme_type)) {
return;
}
const StringName *key = nullptr;
- while ((key = icon_map[p_type].next(key))) {
+ while ((key = icon_map[p_theme_type].next(key))) {
p_list->push_back(*key);
}
}
-void Theme::set_shader(const StringName &p_name, const StringName &p_type, const Ref<Shader> &p_shader) {
- bool new_value = !shader_map.has(p_type) || !shader_map[p_type].has(p_name);
+void Theme::add_icon_type(const StringName &p_theme_type) {
+ if (icon_map.has(p_theme_type)) {
+ return;
+ }
+ icon_map[p_theme_type] = HashMap<StringName, Ref<Texture2D>>();
+}
- shader_map[p_type][p_name] = p_shader;
+void Theme::get_icon_type_list(List<StringName> *p_list) const {
+ ERR_FAIL_NULL(p_list);
- if (new_value) {
- _change_notify();
- emit_changed();
+ const StringName *key = nullptr;
+ while ((key = icon_map.next(key))) {
+ p_list->push_back(*key);
}
}
-Ref<Shader> Theme::get_shader(const StringName &p_name, const StringName &p_type) const {
- if (shader_map.has(p_type) && shader_map[p_type].has(p_name) && shader_map[p_type][p_name].is_valid()) {
- return shader_map[p_type][p_name];
+void Theme::set_stylebox(const StringName &p_name, const StringName &p_theme_type, const Ref<StyleBox> &p_style) {
+ if (style_map[p_theme_type].has(p_name) && style_map[p_theme_type][p_name].is_valid()) {
+ style_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ }
+
+ style_map[p_theme_type][p_name] = p_style;
+
+ if (p_style.is_valid()) {
+ style_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ }
+
+ _emit_theme_changed();
+}
+
+Ref<StyleBox> Theme::get_stylebox(const StringName &p_name, const StringName &p_theme_type) const {
+ if (style_map.has(p_theme_type) && style_map[p_theme_type].has(p_name) && style_map[p_theme_type][p_name].is_valid()) {
+ return style_map[p_theme_type][p_name];
} else {
- return nullptr;
+ return default_style;
}
}
-bool Theme::has_shader(const StringName &p_name, const StringName &p_type) const {
- return (shader_map.has(p_type) && shader_map[p_type].has(p_name) && shader_map[p_type][p_name].is_valid());
+bool Theme::has_stylebox(const StringName &p_name, const StringName &p_theme_type) const {
+ return (style_map.has(p_theme_type) && style_map[p_theme_type].has(p_name) && style_map[p_theme_type][p_name].is_valid());
}
-void Theme::clear_shader(const StringName &p_name, const StringName &p_type) {
- ERR_FAIL_COND(!shader_map.has(p_type));
- ERR_FAIL_COND(!shader_map[p_type].has(p_name));
+bool Theme::has_stylebox_nocheck(const StringName &p_name, const StringName &p_theme_type) const {
+ return (style_map.has(p_theme_type) && style_map[p_theme_type].has(p_name));
+}
- shader_map[p_type].erase(p_name);
- _change_notify();
- emit_changed();
+void Theme::rename_stylebox(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
+ ERR_FAIL_COND_MSG(!style_map.has(p_theme_type), "Cannot rename the stylebox '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist.");
+ ERR_FAIL_COND_MSG(style_map[p_theme_type].has(p_name), "Cannot rename the stylebox '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists.");
+ ERR_FAIL_COND_MSG(!style_map[p_theme_type].has(p_old_name), "Cannot rename the stylebox '" + String(p_old_name) + "' because it does not exist.");
+
+ style_map[p_theme_type][p_name] = style_map[p_theme_type][p_old_name];
+ style_map[p_theme_type].erase(p_old_name);
+
+ _emit_theme_changed();
}
-void Theme::get_shader_list(const StringName &p_type, List<StringName> *p_list) const {
+void Theme::clear_stylebox(const StringName &p_name, const StringName &p_theme_type) {
+ ERR_FAIL_COND_MSG(!style_map.has(p_theme_type), "Cannot clear the stylebox '" + String(p_name) + "' because the node type '" + String(p_theme_type) + "' does not exist.");
+ ERR_FAIL_COND_MSG(!style_map[p_theme_type].has(p_name), "Cannot clear the stylebox '" + String(p_name) + "' because it does not exist.");
+
+ if (style_map[p_theme_type][p_name].is_valid()) {
+ style_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ }
+
+ style_map[p_theme_type].erase(p_name);
+
+ _emit_theme_changed();
+}
+
+void Theme::get_stylebox_list(StringName p_theme_type, List<StringName> *p_list) const {
ERR_FAIL_NULL(p_list);
- if (!shader_map.has(p_type)) {
+ if (!style_map.has(p_theme_type)) {
return;
}
const StringName *key = nullptr;
- while ((key = shader_map[p_type].next(key))) {
+ while ((key = style_map[p_theme_type].next(key))) {
p_list->push_back(*key);
}
}
-void Theme::set_stylebox(const StringName &p_name, const StringName &p_type, const Ref<StyleBox> &p_style) {
- //ERR_FAIL_COND(p_style.is_null());
+void Theme::add_stylebox_type(const StringName &p_theme_type) {
+ if (style_map.has(p_theme_type)) {
+ return;
+ }
+ style_map[p_theme_type] = HashMap<StringName, Ref<StyleBox>>();
+}
- bool new_value = !style_map.has(p_type) || !style_map[p_type].has(p_name);
+void Theme::get_stylebox_type_list(List<StringName> *p_list) const {
+ ERR_FAIL_NULL(p_list);
- if (style_map[p_type].has(p_name) && style_map[p_type][p_name].is_valid()) {
- style_map[p_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ const StringName *key = nullptr;
+ while ((key = style_map.next(key))) {
+ p_list->push_back(*key);
}
+}
- style_map[p_type][p_name] = p_style;
-
- if (p_style.is_valid()) {
- style_map[p_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED);
+void Theme::set_font(const StringName &p_name, const StringName &p_theme_type, const Ref<Font> &p_font) {
+ if (font_map[p_theme_type][p_name].is_valid()) {
+ font_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
}
- if (new_value) {
- _change_notify();
+ font_map[p_theme_type][p_name] = p_font;
+
+ if (p_font.is_valid()) {
+ font_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED);
}
- emit_changed();
+
+ _emit_theme_changed();
}
-Ref<StyleBox> Theme::get_stylebox(const StringName &p_name, const StringName &p_type) const {
- if (style_map.has(p_type) && style_map[p_type].has(p_name) && style_map[p_type][p_name].is_valid()) {
- return style_map[p_type][p_name];
+Ref<Font> Theme::get_font(const StringName &p_name, const StringName &p_theme_type) const {
+ if (font_map.has(p_theme_type) && font_map[p_theme_type].has(p_name) && font_map[p_theme_type][p_name].is_valid()) {
+ return font_map[p_theme_type][p_name];
+ } else if (default_theme_font.is_valid()) {
+ return default_theme_font;
} else {
- return default_style;
+ return default_font;
}
}
-bool Theme::has_stylebox(const StringName &p_name, const StringName &p_type) const {
- return (style_map.has(p_type) && style_map[p_type].has(p_name) && style_map[p_type][p_name].is_valid());
+bool Theme::has_font(const StringName &p_name, const StringName &p_theme_type) const {
+ return ((font_map.has(p_theme_type) && font_map[p_theme_type].has(p_name) && font_map[p_theme_type][p_name].is_valid()) || default_theme_font.is_valid());
}
-void Theme::clear_stylebox(const StringName &p_name, const StringName &p_type) {
- ERR_FAIL_COND(!style_map.has(p_type));
- ERR_FAIL_COND(!style_map[p_type].has(p_name));
+bool Theme::has_font_nocheck(const StringName &p_name, const StringName &p_theme_type) const {
+ return (font_map.has(p_theme_type) && font_map[p_theme_type].has(p_name));
+}
+
+void Theme::rename_font(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
+ ERR_FAIL_COND_MSG(!font_map.has(p_theme_type), "Cannot rename the font '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist.");
+ ERR_FAIL_COND_MSG(font_map[p_theme_type].has(p_name), "Cannot rename the font '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists.");
+ ERR_FAIL_COND_MSG(!font_map[p_theme_type].has(p_old_name), "Cannot rename the font '" + String(p_old_name) + "' because it does not exist.");
+
+ font_map[p_theme_type][p_name] = font_map[p_theme_type][p_old_name];
+ font_map[p_theme_type].erase(p_old_name);
- if (style_map[p_type][p_name].is_valid()) {
- style_map[p_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ _emit_theme_changed();
+}
+
+void Theme::clear_font(const StringName &p_name, const StringName &p_theme_type) {
+ ERR_FAIL_COND_MSG(!font_map.has(p_theme_type), "Cannot clear the font '" + String(p_name) + "' because the node type '" + String(p_theme_type) + "' does not exist.");
+ ERR_FAIL_COND_MSG(!font_map[p_theme_type].has(p_name), "Cannot clear the font '" + String(p_name) + "' because it does not exist.");
+
+ if (font_map[p_theme_type][p_name].is_valid()) {
+ font_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
}
- style_map[p_type].erase(p_name);
+ font_map[p_theme_type].erase(p_name);
- _change_notify();
- emit_changed();
+ _emit_theme_changed();
}
-void Theme::get_stylebox_list(StringName p_type, List<StringName> *p_list) const {
+void Theme::get_font_list(StringName p_theme_type, List<StringName> *p_list) const {
ERR_FAIL_NULL(p_list);
- if (!style_map.has(p_type)) {
+ if (!font_map.has(p_theme_type)) {
return;
}
const StringName *key = nullptr;
- while ((key = style_map[p_type].next(key))) {
+ while ((key = font_map[p_theme_type].next(key))) {
p_list->push_back(*key);
}
}
-void Theme::get_stylebox_types(List<StringName> *p_list) const {
+void Theme::add_font_type(const StringName &p_theme_type) {
+ if (font_map.has(p_theme_type)) {
+ return;
+ }
+ font_map[p_theme_type] = HashMap<StringName, Ref<Font>>();
+}
+
+void Theme::get_font_type_list(List<StringName> *p_list) const {
ERR_FAIL_NULL(p_list);
const StringName *key = nullptr;
- while ((key = style_map.next(key))) {
+ while ((key = font_map.next(key))) {
p_list->push_back(*key);
}
}
-void Theme::set_font(const StringName &p_name, const StringName &p_type, const Ref<Font> &p_font) {
- //ERR_FAIL_COND(p_font.is_null());
+void Theme::set_font_size(const StringName &p_name, const StringName &p_theme_type, int p_font_size) {
+ font_size_map[p_theme_type][p_name] = p_font_size;
- bool new_value = !font_map.has(p_type) || !font_map[p_type].has(p_name);
+ _emit_theme_changed();
+}
- if (font_map[p_type][p_name].is_valid()) {
- font_map[p_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+int Theme::get_font_size(const StringName &p_name, const StringName &p_theme_type) const {
+ if (font_size_map.has(p_theme_type) && font_size_map[p_theme_type].has(p_name) && (font_size_map[p_theme_type][p_name] > 0)) {
+ return font_size_map[p_theme_type][p_name];
+ } else if (default_theme_font_size > 0) {
+ return default_theme_font_size;
+ } else {
+ return default_font_size;
}
+}
+
+bool Theme::has_font_size(const StringName &p_name, const StringName &p_theme_type) const {
+ return ((font_size_map.has(p_theme_type) && font_size_map[p_theme_type].has(p_name) && (font_size_map[p_theme_type][p_name] > 0)) || (default_theme_font_size > 0));
+}
- font_map[p_type][p_name] = p_font;
+bool Theme::has_font_size_nocheck(const StringName &p_name, const StringName &p_theme_type) const {
+ return (font_size_map.has(p_theme_type) && font_size_map[p_theme_type].has(p_name));
+}
- if (p_font.is_valid()) {
- font_map[p_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED);
+void Theme::rename_font_size(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
+ ERR_FAIL_COND_MSG(!font_size_map.has(p_theme_type), "Cannot rename the font size '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist.");
+ ERR_FAIL_COND_MSG(font_size_map[p_theme_type].has(p_name), "Cannot rename the font size '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists.");
+ ERR_FAIL_COND_MSG(!font_size_map[p_theme_type].has(p_old_name), "Cannot rename the font size '" + String(p_old_name) + "' because it does not exist.");
+
+ font_size_map[p_theme_type][p_name] = font_size_map[p_theme_type][p_old_name];
+ font_size_map[p_theme_type].erase(p_old_name);
+
+ _emit_theme_changed();
+}
+
+void Theme::clear_font_size(const StringName &p_name, const StringName &p_theme_type) {
+ ERR_FAIL_COND_MSG(!font_size_map.has(p_theme_type), "Cannot clear the font size '" + String(p_name) + "' because the node type '" + String(p_theme_type) + "' does not exist.");
+ ERR_FAIL_COND_MSG(!font_size_map[p_theme_type].has(p_name), "Cannot clear the font size '" + String(p_name) + "' because it does not exist.");
+
+ font_size_map[p_theme_type].erase(p_name);
+
+ _emit_theme_changed();
+}
+
+void Theme::get_font_size_list(StringName p_theme_type, List<StringName> *p_list) const {
+ ERR_FAIL_NULL(p_list);
+
+ if (!font_size_map.has(p_theme_type)) {
+ return;
}
- if (new_value) {
- _change_notify();
- emit_changed();
+ const StringName *key = nullptr;
+
+ while ((key = font_size_map[p_theme_type].next(key))) {
+ p_list->push_back(*key);
}
}
-Ref<Font> Theme::get_font(const StringName &p_name, const StringName &p_type) const {
- if (font_map.has(p_type) && font_map[p_type].has(p_name) && font_map[p_type][p_name].is_valid()) {
- return font_map[p_type][p_name];
- } else if (default_theme_font.is_valid()) {
- return default_theme_font;
- } else {
- return default_font;
+void Theme::add_font_size_type(const StringName &p_theme_type) {
+ if (font_size_map.has(p_theme_type)) {
+ return;
}
+ font_size_map[p_theme_type] = HashMap<StringName, int>();
}
-bool Theme::has_font(const StringName &p_name, const StringName &p_type) const {
- return (font_map.has(p_type) && font_map[p_type].has(p_name) && font_map[p_type][p_name].is_valid());
+void Theme::get_font_size_type_list(List<StringName> *p_list) const {
+ ERR_FAIL_NULL(p_list);
+
+ const StringName *key = nullptr;
+ while ((key = font_size_map.next(key))) {
+ p_list->push_back(*key);
+ }
}
-void Theme::clear_font(const StringName &p_name, const StringName &p_type) {
- ERR_FAIL_COND(!font_map.has(p_type));
- ERR_FAIL_COND(!font_map[p_type].has(p_name));
+void Theme::set_color(const StringName &p_name, const StringName &p_theme_type, const Color &p_color) {
+ color_map[p_theme_type][p_name] = p_color;
- if (font_map[p_type][p_name].is_valid()) {
- font_map[p_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ _emit_theme_changed();
+}
+
+Color Theme::get_color(const StringName &p_name, const StringName &p_theme_type) const {
+ if (color_map.has(p_theme_type) && color_map[p_theme_type].has(p_name)) {
+ return color_map[p_theme_type][p_name];
+ } else {
+ return Color();
}
+}
- font_map[p_type].erase(p_name);
- _change_notify();
- emit_changed();
+bool Theme::has_color(const StringName &p_name, const StringName &p_theme_type) const {
+ return (color_map.has(p_theme_type) && color_map[p_theme_type].has(p_name));
}
-void Theme::get_font_list(StringName p_type, List<StringName> *p_list) const {
+bool Theme::has_color_nocheck(const StringName &p_name, const StringName &p_theme_type) const {
+ return (color_map.has(p_theme_type) && color_map[p_theme_type].has(p_name));
+}
+
+void Theme::rename_color(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
+ ERR_FAIL_COND_MSG(!color_map.has(p_theme_type), "Cannot rename the color '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist.");
+ ERR_FAIL_COND_MSG(color_map[p_theme_type].has(p_name), "Cannot rename the color '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists.");
+ ERR_FAIL_COND_MSG(!color_map[p_theme_type].has(p_old_name), "Cannot rename the color '" + String(p_old_name) + "' because it does not exist.");
+
+ color_map[p_theme_type][p_name] = color_map[p_theme_type][p_old_name];
+ color_map[p_theme_type].erase(p_old_name);
+
+ _emit_theme_changed();
+}
+
+void Theme::clear_color(const StringName &p_name, const StringName &p_theme_type) {
+ ERR_FAIL_COND_MSG(!color_map.has(p_theme_type), "Cannot clear the color '" + String(p_name) + "' because the node type '" + String(p_theme_type) + "' does not exist.");
+ ERR_FAIL_COND_MSG(!color_map[p_theme_type].has(p_name), "Cannot clear the color '" + String(p_name) + "' because it does not exist.");
+
+ color_map[p_theme_type].erase(p_name);
+
+ _emit_theme_changed();
+}
+
+void Theme::get_color_list(StringName p_theme_type, List<StringName> *p_list) const {
ERR_FAIL_NULL(p_list);
- if (!font_map.has(p_type)) {
+ if (!color_map.has(p_theme_type)) {
return;
}
const StringName *key = nullptr;
- while ((key = font_map[p_type].next(key))) {
+ while ((key = color_map[p_theme_type].next(key))) {
p_list->push_back(*key);
}
}
-void Theme::set_color(const StringName &p_name, const StringName &p_type, const Color &p_color) {
- bool new_value = !color_map.has(p_type) || !color_map[p_type].has(p_name);
+void Theme::add_color_type(const StringName &p_theme_type) {
+ if (color_map.has(p_theme_type)) {
+ return;
+ }
+ color_map[p_theme_type] = HashMap<StringName, Color>();
+}
- color_map[p_type][p_name] = p_color;
+void Theme::get_color_type_list(List<StringName> *p_list) const {
+ ERR_FAIL_NULL(p_list);
- if (new_value) {
- _change_notify();
- emit_changed();
+ const StringName *key = nullptr;
+ while ((key = color_map.next(key))) {
+ p_list->push_back(*key);
}
}
-Color Theme::get_color(const StringName &p_name, const StringName &p_type) const {
- if (color_map.has(p_type) && color_map[p_type].has(p_name)) {
- return color_map[p_type][p_name];
+void Theme::set_constant(const StringName &p_name, const StringName &p_theme_type, int p_constant) {
+ constant_map[p_theme_type][p_name] = p_constant;
+
+ _emit_theme_changed();
+}
+
+int Theme::get_constant(const StringName &p_name, const StringName &p_theme_type) const {
+ if (constant_map.has(p_theme_type) && constant_map[p_theme_type].has(p_name)) {
+ return constant_map[p_theme_type][p_name];
} else {
- return Color();
+ return 0;
}
}
-bool Theme::has_color(const StringName &p_name, const StringName &p_type) const {
- return (color_map.has(p_type) && color_map[p_type].has(p_name));
+bool Theme::has_constant(const StringName &p_name, const StringName &p_theme_type) const {
+ return (constant_map.has(p_theme_type) && constant_map[p_theme_type].has(p_name));
}
-void Theme::clear_color(const StringName &p_name, const StringName &p_type) {
- ERR_FAIL_COND(!color_map.has(p_type));
- ERR_FAIL_COND(!color_map[p_type].has(p_name));
+bool Theme::has_constant_nocheck(const StringName &p_name, const StringName &p_theme_type) const {
+ return (constant_map.has(p_theme_type) && constant_map[p_theme_type].has(p_name));
+}
- color_map[p_type].erase(p_name);
- _change_notify();
- emit_changed();
+void Theme::rename_constant(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
+ ERR_FAIL_COND_MSG(!constant_map.has(p_theme_type), "Cannot rename the constant '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist.");
+ ERR_FAIL_COND_MSG(constant_map[p_theme_type].has(p_name), "Cannot rename the constant '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists.");
+ ERR_FAIL_COND_MSG(!constant_map[p_theme_type].has(p_old_name), "Cannot rename the constant '" + String(p_old_name) + "' because it does not exist.");
+
+ constant_map[p_theme_type][p_name] = constant_map[p_theme_type][p_old_name];
+ constant_map[p_theme_type].erase(p_old_name);
+
+ _emit_theme_changed();
}
-void Theme::get_color_list(StringName p_type, List<StringName> *p_list) const {
+void Theme::clear_constant(const StringName &p_name, const StringName &p_theme_type) {
+ ERR_FAIL_COND_MSG(!constant_map.has(p_theme_type), "Cannot clear the constant '" + String(p_name) + "' because the node type '" + String(p_theme_type) + "' does not exist.");
+ ERR_FAIL_COND_MSG(!constant_map[p_theme_type].has(p_name), "Cannot clear the constant '" + String(p_name) + "' because it does not exist.");
+
+ constant_map[p_theme_type].erase(p_name);
+
+ _emit_theme_changed();
+}
+
+void Theme::get_constant_list(StringName p_theme_type, List<StringName> *p_list) const {
ERR_FAIL_NULL(p_list);
- if (!color_map.has(p_type)) {
+ if (!constant_map.has(p_theme_type)) {
return;
}
const StringName *key = nullptr;
- while ((key = color_map[p_type].next(key))) {
+ while ((key = constant_map[p_theme_type].next(key))) {
+ p_list->push_back(*key);
+ }
+}
+
+void Theme::add_constant_type(const StringName &p_theme_type) {
+ if (constant_map.has(p_theme_type)) {
+ return;
+ }
+ constant_map[p_theme_type] = HashMap<StringName, int>();
+}
+
+void Theme::get_constant_type_list(List<StringName> *p_list) const {
+ ERR_FAIL_NULL(p_list);
+
+ const StringName *key = nullptr;
+ while ((key = constant_map.next(key))) {
p_list->push_back(*key);
}
}
-void Theme::set_constant(const StringName &p_name, const StringName &p_type, int p_constant) {
- bool new_value = !constant_map.has(p_type) || !constant_map[p_type].has(p_name);
- constant_map[p_type][p_name] = p_constant;
+void Theme::set_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type, const Variant &p_value) {
+ switch (p_data_type) {
+ case DATA_TYPE_COLOR: {
+ ERR_FAIL_COND_MSG(p_value.get_type() != Variant::COLOR, "Theme item's data type (Color) does not match Variant's type (" + Variant::get_type_name(p_value.get_type()) + ").");
+
+ Color color_value = p_value;
+ set_color(p_name, p_theme_type, color_value);
+ } break;
+ case DATA_TYPE_CONSTANT: {
+ ERR_FAIL_COND_MSG(p_value.get_type() != Variant::INT, "Theme item's data type (int) does not match Variant's type (" + Variant::get_type_name(p_value.get_type()) + ").");
+
+ int constant_value = p_value;
+ set_constant(p_name, p_theme_type, constant_value);
+ } break;
+ case DATA_TYPE_FONT: {
+ ERR_FAIL_COND_MSG(p_value.get_type() != Variant::OBJECT, "Theme item's data type (Object) does not match Variant's type (" + Variant::get_type_name(p_value.get_type()) + ").");
+
+ Ref<Font> font_value = Object::cast_to<Font>(p_value.get_validated_object());
+ set_font(p_name, p_theme_type, font_value);
+ } break;
+ case DATA_TYPE_FONT_SIZE: {
+ ERR_FAIL_COND_MSG(p_value.get_type() != Variant::INT, "Theme item's data type (int) does not match Variant's type (" + Variant::get_type_name(p_value.get_type()) + ").");
+
+ int font_size_value = p_value;
+ set_font_size(p_name, p_theme_type, font_size_value);
+ } break;
+ case DATA_TYPE_ICON: {
+ ERR_FAIL_COND_MSG(p_value.get_type() != Variant::OBJECT, "Theme item's data type (Object) does not match Variant's type (" + Variant::get_type_name(p_value.get_type()) + ").");
+
+ Ref<Texture2D> icon_value = Object::cast_to<Texture2D>(p_value.get_validated_object());
+ set_icon(p_name, p_theme_type, icon_value);
+ } break;
+ case DATA_TYPE_STYLEBOX: {
+ ERR_FAIL_COND_MSG(p_value.get_type() != Variant::OBJECT, "Theme item's data type (Object) does not match Variant's type (" + Variant::get_type_name(p_value.get_type()) + ").");
+
+ Ref<StyleBox> stylebox_value = Object::cast_to<StyleBox>(p_value.get_validated_object());
+ set_stylebox(p_name, p_theme_type, stylebox_value);
+ } break;
+ case DATA_TYPE_MAX:
+ break; // Can't happen, but silences warning.
+ }
+}
+
+Variant Theme::get_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type) const {
+ switch (p_data_type) {
+ case DATA_TYPE_COLOR:
+ return get_color(p_name, p_theme_type);
+ case DATA_TYPE_CONSTANT:
+ return get_constant(p_name, p_theme_type);
+ case DATA_TYPE_FONT:
+ return get_font(p_name, p_theme_type);
+ case DATA_TYPE_FONT_SIZE:
+ return get_font_size(p_name, p_theme_type);
+ case DATA_TYPE_ICON:
+ return get_icon(p_name, p_theme_type);
+ case DATA_TYPE_STYLEBOX:
+ return get_stylebox(p_name, p_theme_type);
+ case DATA_TYPE_MAX:
+ break; // Can't happen, but silences warning.
+ }
+
+ return Variant();
+}
- if (new_value) {
- _change_notify();
- emit_changed();
+bool Theme::has_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type) const {
+ switch (p_data_type) {
+ case DATA_TYPE_COLOR:
+ return has_color(p_name, p_theme_type);
+ case DATA_TYPE_CONSTANT:
+ return has_constant(p_name, p_theme_type);
+ case DATA_TYPE_FONT:
+ return has_font(p_name, p_theme_type);
+ case DATA_TYPE_FONT_SIZE:
+ return has_font_size(p_name, p_theme_type);
+ case DATA_TYPE_ICON:
+ return has_icon(p_name, p_theme_type);
+ case DATA_TYPE_STYLEBOX:
+ return has_stylebox(p_name, p_theme_type);
+ case DATA_TYPE_MAX:
+ break; // Can't happen, but silences warning.
}
+
+ return false;
}
-int Theme::get_constant(const StringName &p_name, const StringName &p_type) const {
- if (constant_map.has(p_type) && constant_map[p_type].has(p_name)) {
- return constant_map[p_type][p_name];
- } else {
- return 0;
+bool Theme::has_theme_item_nocheck(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type) const {
+ switch (p_data_type) {
+ case DATA_TYPE_COLOR:
+ return has_color_nocheck(p_name, p_theme_type);
+ case DATA_TYPE_CONSTANT:
+ return has_constant_nocheck(p_name, p_theme_type);
+ case DATA_TYPE_FONT:
+ return has_font_nocheck(p_name, p_theme_type);
+ case DATA_TYPE_FONT_SIZE:
+ return has_font_size_nocheck(p_name, p_theme_type);
+ case DATA_TYPE_ICON:
+ return has_icon_nocheck(p_name, p_theme_type);
+ case DATA_TYPE_STYLEBOX:
+ return has_stylebox_nocheck(p_name, p_theme_type);
+ case DATA_TYPE_MAX:
+ break; // Can't happen, but silences warning.
}
+
+ return false;
}
-bool Theme::has_constant(const StringName &p_name, const StringName &p_type) const {
- return (constant_map.has(p_type) && constant_map[p_type].has(p_name));
+void Theme::rename_theme_item(DataType p_data_type, const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
+ switch (p_data_type) {
+ case DATA_TYPE_COLOR:
+ rename_color(p_old_name, p_name, p_theme_type);
+ break;
+ case DATA_TYPE_CONSTANT:
+ rename_constant(p_old_name, p_name, p_theme_type);
+ break;
+ case DATA_TYPE_FONT:
+ rename_font(p_old_name, p_name, p_theme_type);
+ break;
+ case DATA_TYPE_FONT_SIZE:
+ rename_font_size(p_old_name, p_name, p_theme_type);
+ break;
+ case DATA_TYPE_ICON:
+ rename_icon(p_old_name, p_name, p_theme_type);
+ break;
+ case DATA_TYPE_STYLEBOX:
+ rename_stylebox(p_old_name, p_name, p_theme_type);
+ break;
+ case DATA_TYPE_MAX:
+ break; // Can't happen, but silences warning.
+ }
}
-void Theme::clear_constant(const StringName &p_name, const StringName &p_type) {
- ERR_FAIL_COND(!constant_map.has(p_type));
- ERR_FAIL_COND(!constant_map[p_type].has(p_name));
+void Theme::clear_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type) {
+ switch (p_data_type) {
+ case DATA_TYPE_COLOR:
+ clear_color(p_name, p_theme_type);
+ break;
+ case DATA_TYPE_CONSTANT:
+ clear_constant(p_name, p_theme_type);
+ break;
+ case DATA_TYPE_FONT:
+ clear_font(p_name, p_theme_type);
+ break;
+ case DATA_TYPE_FONT_SIZE:
+ clear_font_size(p_name, p_theme_type);
+ break;
+ case DATA_TYPE_ICON:
+ clear_icon(p_name, p_theme_type);
+ break;
+ case DATA_TYPE_STYLEBOX:
+ clear_stylebox(p_name, p_theme_type);
+ break;
+ case DATA_TYPE_MAX:
+ break; // Can't happen, but silences warning.
+ }
+}
- constant_map[p_type].erase(p_name);
- _change_notify();
- emit_changed();
+void Theme::get_theme_item_list(DataType p_data_type, StringName p_theme_type, List<StringName> *p_list) const {
+ switch (p_data_type) {
+ case DATA_TYPE_COLOR:
+ get_color_list(p_theme_type, p_list);
+ break;
+ case DATA_TYPE_CONSTANT:
+ get_constant_list(p_theme_type, p_list);
+ break;
+ case DATA_TYPE_FONT:
+ get_font_list(p_theme_type, p_list);
+ break;
+ case DATA_TYPE_FONT_SIZE:
+ get_font_size_list(p_theme_type, p_list);
+ break;
+ case DATA_TYPE_ICON:
+ get_icon_list(p_theme_type, p_list);
+ break;
+ case DATA_TYPE_STYLEBOX:
+ get_stylebox_list(p_theme_type, p_list);
+ break;
+ case DATA_TYPE_MAX:
+ break; // Can't happen, but silences warning.
+ }
}
-void Theme::get_constant_list(StringName p_type, List<StringName> *p_list) const {
+void Theme::add_theme_item_type(DataType p_data_type, const StringName &p_theme_type) {
+ switch (p_data_type) {
+ case DATA_TYPE_COLOR:
+ add_color_type(p_theme_type);
+ break;
+ case DATA_TYPE_CONSTANT:
+ add_constant_type(p_theme_type);
+ break;
+ case DATA_TYPE_FONT:
+ add_font_type(p_theme_type);
+ break;
+ case DATA_TYPE_FONT_SIZE:
+ add_font_size_type(p_theme_type);
+ break;
+ case DATA_TYPE_ICON:
+ add_icon_type(p_theme_type);
+ break;
+ case DATA_TYPE_STYLEBOX:
+ add_stylebox_type(p_theme_type);
+ break;
+ case DATA_TYPE_MAX:
+ break; // Can't happen, but silences warning.
+ }
+}
+
+void Theme::get_theme_item_type_list(DataType p_data_type, List<StringName> *p_list) const {
+ switch (p_data_type) {
+ case DATA_TYPE_COLOR:
+ get_color_type_list(p_list);
+ break;
+ case DATA_TYPE_CONSTANT:
+ get_constant_type_list(p_list);
+ break;
+ case DATA_TYPE_FONT:
+ get_font_type_list(p_list);
+ break;
+ case DATA_TYPE_FONT_SIZE:
+ get_font_size_type_list(p_list);
+ break;
+ case DATA_TYPE_ICON:
+ get_icon_type_list(p_list);
+ break;
+ case DATA_TYPE_STYLEBOX:
+ get_stylebox_type_list(p_list);
+ break;
+ case DATA_TYPE_MAX:
+ break; // Can't happen, but silences warning.
+ }
+}
+
+void Theme::set_type_variation(const StringName &p_theme_type, const StringName &p_base_type) {
+ ERR_FAIL_COND_MSG(p_theme_type == StringName(), "An empty theme type cannot be marked as a variation of another type.");
+ ERR_FAIL_COND_MSG(ClassDB::class_exists(p_theme_type), "A type associated with a built-in class cannot be marked as a variation of another type.");
+ ERR_FAIL_COND_MSG(p_base_type == StringName(), "An empty theme type cannot be the base type of a variation. Use clear_type_variation() instead if you want to unmark '" + String(p_theme_type) + "' as a variation.");
+
+ if (variation_map.has(p_theme_type)) {
+ StringName old_base = variation_map[p_theme_type];
+ variation_base_map[old_base].erase(p_theme_type);
+ }
+
+ variation_map[p_theme_type] = p_base_type;
+ variation_base_map[p_base_type].push_back(p_theme_type);
+
+ _emit_theme_changed();
+}
+
+bool Theme::is_type_variation(const StringName &p_theme_type, const StringName &p_base_type) const {
+ return (variation_map.has(p_theme_type) && variation_map[p_theme_type] == p_base_type);
+}
+
+void Theme::clear_type_variation(const StringName &p_theme_type) {
+ ERR_FAIL_COND_MSG(!variation_map.has(p_theme_type), "Cannot clear the type variation '" + String(p_theme_type) + "' because it does not exist.");
+
+ StringName base_type = variation_map[p_theme_type];
+ variation_base_map[base_type].erase(p_theme_type);
+ variation_map.erase(p_theme_type);
+
+ _emit_theme_changed();
+}
+
+StringName Theme::get_type_variation_base(const StringName &p_theme_type) const {
+ if (!variation_map.has(p_theme_type)) {
+ return StringName();
+ }
+
+ return variation_map[p_theme_type];
+}
+
+void Theme::get_type_variation_list(const StringName &p_base_type, List<StringName> *p_list) const {
ERR_FAIL_NULL(p_list);
- if (!constant_map.has(p_type)) {
+ if (!variation_base_map.has(p_base_type)) {
return;
}
- const StringName *key = nullptr;
+ for (const StringName &E : variation_base_map[p_base_type]) {
+ // Prevent infinite loops if variants were set to be cross-dependent (that's still invalid usage, but handling for stability sake).
+ if (p_list->find(E)) {
+ continue;
+ }
- while ((key = constant_map[p_type].next(key))) {
- p_list->push_back(*key);
+ p_list->push_back(E);
+ // Continue looking for sub-variations.
+ get_type_variation_list(E, p_list);
}
}
+void Theme::_freeze_change_propagation() {
+ no_change_propagation = true;
+}
+
+void Theme::_unfreeze_and_propagate_changes() {
+ no_change_propagation = false;
+ _emit_theme_changed();
+}
+
void Theme::clear() {
- //these need disconnecting
+ // These items need disconnecting.
{
const StringName *K = nullptr;
while ((K = icon_map.next(K))) {
@@ -699,47 +1340,46 @@ void Theme::clear() {
icon_map.clear();
style_map.clear();
font_map.clear();
- shader_map.clear();
+ font_size_map.clear();
color_map.clear();
constant_map.clear();
- _change_notify();
- emit_changed();
-}
+ variation_map.clear();
+ variation_base_map.clear();
-void Theme::copy_default_theme() {
- Ref<Theme> default_theme2 = get_default();
- copy_theme(default_theme2);
+ _emit_theme_changed();
}
-void Theme::copy_theme(const Ref<Theme> &p_other) {
+void Theme::merge_with(const Ref<Theme> &p_other) {
if (p_other.is_null()) {
- clear();
-
return;
}
- //these need reconnecting, so add normally
+ _freeze_change_propagation();
+
+ // Colors.
{
const StringName *K = nullptr;
- while ((K = p_other->icon_map.next(K))) {
+ while ((K = p_other->color_map.next(K))) {
const StringName *L = nullptr;
- while ((L = p_other->icon_map[*K].next(L))) {
- set_icon(*L, *K, p_other->icon_map[*K][*L]);
+ while ((L = p_other->color_map[*K].next(L))) {
+ set_color(*L, *K, p_other->color_map[*K][*L]);
}
}
}
+ // Constants.
{
const StringName *K = nullptr;
- while ((K = p_other->style_map.next(K))) {
+ while ((K = p_other->constant_map.next(K))) {
const StringName *L = nullptr;
- while ((L = p_other->style_map[*K].next(L))) {
- set_stylebox(*L, *K, p_other->style_map[*K][*L]);
+ while ((L = p_other->constant_map[*K].next(L))) {
+ set_constant(*L, *K, p_other->constant_map[*K][*L]);
}
}
}
+ // Fonts.
{
const StringName *K = nullptr;
while ((K = p_other->font_map.next(K))) {
@@ -750,14 +1390,48 @@ void Theme::copy_theme(const Ref<Theme> &p_other) {
}
}
- //these are ok to just copy
+ // Font sizes.
+ {
+ const StringName *K = nullptr;
+ while ((K = p_other->font_size_map.next(K))) {
+ const StringName *L = nullptr;
+ while ((L = p_other->font_size_map[*K].next(L))) {
+ set_font_size(*L, *K, p_other->font_size_map[*K][*L]);
+ }
+ }
+ }
- color_map = p_other->color_map;
- constant_map = p_other->constant_map;
- shader_map = p_other->shader_map;
+ // Icons.
+ {
+ const StringName *K = nullptr;
+ while ((K = p_other->icon_map.next(K))) {
+ const StringName *L = nullptr;
+ while ((L = p_other->icon_map[*K].next(L))) {
+ set_icon(*L, *K, p_other->icon_map[*K][*L]);
+ }
+ }
+ }
- _change_notify();
- emit_changed();
+ // Styleboxes.
+ {
+ const StringName *K = nullptr;
+ while ((K = p_other->style_map.next(K))) {
+ const StringName *L = nullptr;
+ while ((L = p_other->style_map[*K].next(L))) {
+ set_stylebox(*L, *K, p_other->style_map[*K][*L]);
+ }
+ }
+ }
+
+ // Type variations.
+ {
+ const StringName *K = nullptr;
+ while ((K = p_other->variation_map.next(K))) {
+ set_type_variation(*K, p_other->variation_map[*K]);
+ }
+ }
+
+ _unfreeze_and_propagate_changes();
}
void Theme::get_type_list(List<StringName> *p_list) const {
@@ -766,30 +1440,42 @@ void Theme::get_type_list(List<StringName> *p_list) const {
Set<StringName> types;
const StringName *key = nullptr;
+ // Icons.
while ((key = icon_map.next(key))) {
types.insert(*key);
}
key = nullptr;
+ // StyleBoxes.
while ((key = style_map.next(key))) {
types.insert(*key);
}
key = nullptr;
+ // Fonts.
while ((key = font_map.next(key))) {
types.insert(*key);
}
key = nullptr;
+ // Font sizes.
+ while ((key = font_size_map.next(key))) {
+ types.insert(*key);
+ }
+
+ key = nullptr;
+
+ // Colors.
while ((key = color_map.next(key))) {
types.insert(*key);
}
key = nullptr;
+ // Constants.
while ((key = constant_map.next(key))) {
types.insert(*key);
}
@@ -799,49 +1485,119 @@ void Theme::get_type_list(List<StringName> *p_list) const {
}
}
-void Theme::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_icon", "name", "type", "texture"), &Theme::set_icon);
- ClassDB::bind_method(D_METHOD("get_icon", "name", "type"), &Theme::get_icon);
- ClassDB::bind_method(D_METHOD("has_icon", "name", "type"), &Theme::has_icon);
- ClassDB::bind_method(D_METHOD("clear_icon", "name", "type"), &Theme::clear_icon);
- ClassDB::bind_method(D_METHOD("get_icon_list", "type"), &Theme::_get_icon_list);
-
- ClassDB::bind_method(D_METHOD("set_stylebox", "name", "type", "texture"), &Theme::set_stylebox);
- ClassDB::bind_method(D_METHOD("get_stylebox", "name", "type"), &Theme::get_stylebox);
- ClassDB::bind_method(D_METHOD("has_stylebox", "name", "type"), &Theme::has_stylebox);
- ClassDB::bind_method(D_METHOD("clear_stylebox", "name", "type"), &Theme::clear_stylebox);
- ClassDB::bind_method(D_METHOD("get_stylebox_list", "type"), &Theme::_get_stylebox_list);
- ClassDB::bind_method(D_METHOD("get_stylebox_types"), &Theme::_get_stylebox_types);
-
- ClassDB::bind_method(D_METHOD("set_font", "name", "type", "font"), &Theme::set_font);
- ClassDB::bind_method(D_METHOD("get_font", "name", "type"), &Theme::get_font);
- ClassDB::bind_method(D_METHOD("has_font", "name", "type"), &Theme::has_font);
- ClassDB::bind_method(D_METHOD("clear_font", "name", "type"), &Theme::clear_font);
- ClassDB::bind_method(D_METHOD("get_font_list", "type"), &Theme::_get_font_list);
-
- ClassDB::bind_method(D_METHOD("set_color", "name", "type", "color"), &Theme::set_color);
- ClassDB::bind_method(D_METHOD("get_color", "name", "type"), &Theme::get_color);
- ClassDB::bind_method(D_METHOD("has_color", "name", "type"), &Theme::has_color);
- ClassDB::bind_method(D_METHOD("clear_color", "name", "type"), &Theme::clear_color);
- ClassDB::bind_method(D_METHOD("get_color_list", "type"), &Theme::_get_color_list);
-
- ClassDB::bind_method(D_METHOD("set_constant", "name", "type", "constant"), &Theme::set_constant);
- ClassDB::bind_method(D_METHOD("get_constant", "name", "type"), &Theme::get_constant);
- ClassDB::bind_method(D_METHOD("has_constant", "name", "type"), &Theme::has_constant);
- ClassDB::bind_method(D_METHOD("clear_constant", "name", "type"), &Theme::clear_constant);
- ClassDB::bind_method(D_METHOD("get_constant_list", "type"), &Theme::_get_constant_list);
+void Theme::get_type_dependencies(const StringName &p_base_type, const StringName &p_type_variation, List<StringName> *p_list) {
+ ERR_FAIL_NULL(p_list);
- ClassDB::bind_method(D_METHOD("clear"), &Theme::clear);
+ // Build the dependency chain for type variations.
+ if (p_type_variation != StringName()) {
+ StringName variation_name = p_type_variation;
+ while (variation_name != StringName()) {
+ p_list->push_back(variation_name);
+ variation_name = get_type_variation_base(variation_name);
+
+ // If we have reached the base type dependency, it's safe to stop (assuming no funny business was done to the Theme).
+ if (variation_name == p_base_type) {
+ break;
+ }
+ }
+ }
+
+ // Continue building the chain using native class hierarchy.
+ StringName class_name = p_base_type;
+ while (class_name != StringName()) {
+ p_list->push_back(class_name);
+ class_name = ClassDB::get_parent_class_nocheck(class_name);
+ }
+}
+
+void Theme::reset_state() {
+ clear();
+}
+
+void Theme::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_icon", "name", "theme_type", "texture"), &Theme::set_icon);
+ ClassDB::bind_method(D_METHOD("get_icon", "name", "theme_type"), &Theme::get_icon);
+ ClassDB::bind_method(D_METHOD("has_icon", "name", "theme_type"), &Theme::has_icon);
+ ClassDB::bind_method(D_METHOD("rename_icon", "old_name", "name", "theme_type"), &Theme::rename_icon);
+ ClassDB::bind_method(D_METHOD("clear_icon", "name", "theme_type"), &Theme::clear_icon);
+ ClassDB::bind_method(D_METHOD("get_icon_list", "theme_type"), &Theme::_get_icon_list);
+ ClassDB::bind_method(D_METHOD("get_icon_type_list"), &Theme::_get_icon_type_list);
+
+ ClassDB::bind_method(D_METHOD("set_stylebox", "name", "theme_type", "texture"), &Theme::set_stylebox);
+ ClassDB::bind_method(D_METHOD("get_stylebox", "name", "theme_type"), &Theme::get_stylebox);
+ ClassDB::bind_method(D_METHOD("has_stylebox", "name", "theme_type"), &Theme::has_stylebox);
+ ClassDB::bind_method(D_METHOD("rename_stylebox", "old_name", "name", "theme_type"), &Theme::rename_stylebox);
+ ClassDB::bind_method(D_METHOD("clear_stylebox", "name", "theme_type"), &Theme::clear_stylebox);
+ ClassDB::bind_method(D_METHOD("get_stylebox_list", "theme_type"), &Theme::_get_stylebox_list);
+ ClassDB::bind_method(D_METHOD("get_stylebox_type_list"), &Theme::_get_stylebox_type_list);
+
+ ClassDB::bind_method(D_METHOD("set_font", "name", "theme_type", "font"), &Theme::set_font);
+ ClassDB::bind_method(D_METHOD("get_font", "name", "theme_type"), &Theme::get_font);
+ ClassDB::bind_method(D_METHOD("has_font", "name", "theme_type"), &Theme::has_font);
+ ClassDB::bind_method(D_METHOD("rename_font", "old_name", "name", "theme_type"), &Theme::rename_font);
+ ClassDB::bind_method(D_METHOD("clear_font", "name", "theme_type"), &Theme::clear_font);
+ ClassDB::bind_method(D_METHOD("get_font_list", "theme_type"), &Theme::_get_font_list);
+ ClassDB::bind_method(D_METHOD("get_font_type_list"), &Theme::_get_font_type_list);
+
+ ClassDB::bind_method(D_METHOD("set_font_size", "name", "theme_type", "font_size"), &Theme::set_font_size);
+ ClassDB::bind_method(D_METHOD("get_font_size", "name", "theme_type"), &Theme::get_font_size);
+ ClassDB::bind_method(D_METHOD("has_font_size", "name", "theme_type"), &Theme::has_font_size);
+ ClassDB::bind_method(D_METHOD("rename_font_size", "old_name", "name", "theme_type"), &Theme::rename_font_size);
+ ClassDB::bind_method(D_METHOD("clear_font_size", "name", "theme_type"), &Theme::clear_font_size);
+ ClassDB::bind_method(D_METHOD("get_font_size_list", "theme_type"), &Theme::_get_font_size_list);
+ ClassDB::bind_method(D_METHOD("get_font_size_type_list"), &Theme::_get_font_size_type_list);
+
+ ClassDB::bind_method(D_METHOD("set_color", "name", "theme_type", "color"), &Theme::set_color);
+ ClassDB::bind_method(D_METHOD("get_color", "name", "theme_type"), &Theme::get_color);
+ ClassDB::bind_method(D_METHOD("has_color", "name", "theme_type"), &Theme::has_color);
+ ClassDB::bind_method(D_METHOD("rename_color", "old_name", "name", "theme_type"), &Theme::rename_color);
+ ClassDB::bind_method(D_METHOD("clear_color", "name", "theme_type"), &Theme::clear_color);
+ ClassDB::bind_method(D_METHOD("get_color_list", "theme_type"), &Theme::_get_color_list);
+ ClassDB::bind_method(D_METHOD("get_color_type_list"), &Theme::_get_color_type_list);
+
+ ClassDB::bind_method(D_METHOD("set_constant", "name", "theme_type", "constant"), &Theme::set_constant);
+ ClassDB::bind_method(D_METHOD("get_constant", "name", "theme_type"), &Theme::get_constant);
+ ClassDB::bind_method(D_METHOD("has_constant", "name", "theme_type"), &Theme::has_constant);
+ ClassDB::bind_method(D_METHOD("rename_constant", "old_name", "name", "theme_type"), &Theme::rename_constant);
+ ClassDB::bind_method(D_METHOD("clear_constant", "name", "theme_type"), &Theme::clear_constant);
+ ClassDB::bind_method(D_METHOD("get_constant_list", "theme_type"), &Theme::_get_constant_list);
+ ClassDB::bind_method(D_METHOD("get_constant_type_list"), &Theme::_get_constant_type_list);
ClassDB::bind_method(D_METHOD("set_default_font", "font"), &Theme::set_default_theme_font);
ClassDB::bind_method(D_METHOD("get_default_font"), &Theme::get_default_theme_font);
- ClassDB::bind_method(D_METHOD("get_type_list", "type"), &Theme::_get_type_list);
+ ClassDB::bind_method(D_METHOD("set_default_font_size", "font_size"), &Theme::set_default_theme_font_size);
+ ClassDB::bind_method(D_METHOD("get_default_font_size"), &Theme::get_default_theme_font_size);
+
+ ClassDB::bind_method(D_METHOD("set_theme_item", "data_type", "name", "theme_type", "value"), &Theme::set_theme_item);
+ ClassDB::bind_method(D_METHOD("get_theme_item", "data_type", "name", "theme_type"), &Theme::get_theme_item);
+ ClassDB::bind_method(D_METHOD("has_theme_item", "data_type", "name", "theme_type"), &Theme::has_theme_item);
+ ClassDB::bind_method(D_METHOD("rename_theme_item", "data_type", "old_name", "name", "theme_type"), &Theme::rename_theme_item);
+ ClassDB::bind_method(D_METHOD("clear_theme_item", "data_type", "name", "theme_type"), &Theme::clear_theme_item);
+ ClassDB::bind_method(D_METHOD("get_theme_item_list", "data_type", "theme_type"), &Theme::_get_theme_item_list);
+ ClassDB::bind_method(D_METHOD("get_theme_item_type_list", "data_type"), &Theme::_get_theme_item_type_list);
+
+ ClassDB::bind_method(D_METHOD("set_type_variation", "theme_type", "base_type"), &Theme::set_type_variation);
+ ClassDB::bind_method(D_METHOD("is_type_variation", "theme_type", "base_type"), &Theme::is_type_variation);
+ ClassDB::bind_method(D_METHOD("clear_type_variation", "theme_type"), &Theme::clear_type_variation);
+ ClassDB::bind_method(D_METHOD("get_type_variation_base", "theme_type"), &Theme::get_type_variation_base);
+ ClassDB::bind_method(D_METHOD("get_type_variation_list", "base_type"), &Theme::_get_type_variation_list);
- ClassDB::bind_method("copy_default_theme", &Theme::copy_default_theme);
- ClassDB::bind_method(D_METHOD("copy_theme", "other"), &Theme::copy_theme);
+ ClassDB::bind_method(D_METHOD("get_type_list"), &Theme::_get_type_list);
+
+ ClassDB::bind_method(D_METHOD("merge_with", "other"), &Theme::merge_with);
+ ClassDB::bind_method(D_METHOD("clear"), &Theme::clear);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "default_font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_default_font", "get_default_font");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "default_font_size"), "set_default_font_size", "get_default_font_size");
+
+ BIND_ENUM_CONSTANT(DATA_TYPE_COLOR);
+ BIND_ENUM_CONSTANT(DATA_TYPE_CONSTANT);
+ BIND_ENUM_CONSTANT(DATA_TYPE_FONT);
+ BIND_ENUM_CONSTANT(DATA_TYPE_FONT_SIZE);
+ BIND_ENUM_CONSTANT(DATA_TYPE_ICON);
+ BIND_ENUM_CONSTANT(DATA_TYPE_STYLEBOX);
+ BIND_ENUM_CONSTANT(DATA_TYPE_MAX);
}
Theme::Theme() {
diff --git a/scene/resources/theme.h b/scene/resources/theme.h
index 3c72ddd8a2..15f21b91b8 100644
--- a/scene/resources/theme.h
+++ b/scene/resources/theme.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,10 +31,9 @@
#ifndef THEME_H
#define THEME_H
+#include "core/io/resource.h"
#include "core/io/resource_loader.h"
-#include "core/resource.h"
#include "scene/resources/font.h"
-#include "scene/resources/shader.h"
#include "scene/resources/style_box.h"
#include "scene/resources/texture.h"
@@ -42,22 +41,55 @@ class Theme : public Resource {
GDCLASS(Theme, Resource);
RES_BASE_EXTENSION("theme");
+#ifdef TOOLS_ENABLED
+ friend class ThemeItemImportTree;
+ friend class ThemeItemEditorDialog;
+ friend class ThemeTypeEditor;
+#endif
+
+public:
+ enum DataType {
+ DATA_TYPE_COLOR,
+ DATA_TYPE_CONSTANT,
+ DATA_TYPE_FONT,
+ DATA_TYPE_FONT_SIZE,
+ DATA_TYPE_ICON,
+ DATA_TYPE_STYLEBOX,
+ DATA_TYPE_MAX
+ };
+
+private:
+ bool no_change_propagation = false;
+
void _emit_theme_changed();
HashMap<StringName, HashMap<StringName, Ref<Texture2D>>> icon_map;
HashMap<StringName, HashMap<StringName, Ref<StyleBox>>> style_map;
HashMap<StringName, HashMap<StringName, Ref<Font>>> font_map;
- HashMap<StringName, HashMap<StringName, Ref<Shader>>> shader_map;
+ HashMap<StringName, HashMap<StringName, int>> font_size_map;
HashMap<StringName, HashMap<StringName, Color>> color_map;
HashMap<StringName, HashMap<StringName, int>> constant_map;
-
- Vector<String> _get_icon_list(const String &p_type) const;
- Vector<String> _get_stylebox_list(const String &p_type) const;
- Vector<String> _get_stylebox_types() const;
- Vector<String> _get_font_list(const String &p_type) const;
- Vector<String> _get_color_list(const String &p_type) const;
- Vector<String> _get_constant_list(const String &p_type) const;
- Vector<String> _get_type_list(const String &p_type) const;
+ HashMap<StringName, StringName> variation_map;
+ HashMap<StringName, List<StringName>> variation_base_map;
+
+ Vector<String> _get_icon_list(const String &p_theme_type) const;
+ Vector<String> _get_icon_type_list() const;
+ Vector<String> _get_stylebox_list(const String &p_theme_type) const;
+ Vector<String> _get_stylebox_type_list() const;
+ Vector<String> _get_font_list(const String &p_theme_type) const;
+ Vector<String> _get_font_type_list() const;
+ Vector<String> _get_font_size_list(const String &p_theme_type) const;
+ Vector<String> _get_font_size_type_list() const;
+ Vector<String> _get_color_list(const String &p_theme_type) const;
+ Vector<String> _get_color_type_list() const;
+ Vector<String> _get_constant_list(const String &p_theme_type) const;
+ Vector<String> _get_constant_type_list() const;
+
+ Vector<String> _get_theme_item_list(DataType p_data_type, const String &p_theme_type) const;
+ Vector<String> _get_theme_item_type_list(DataType p_data_type) const;
+
+ Vector<String> _get_type_variation_list(const StringName &p_theme_type) const;
+ Vector<String> _get_type_list() const;
protected:
bool _set(const StringName &p_name, const Variant &p_value);
@@ -69,11 +101,18 @@ protected:
static Ref<Texture2D> default_icon;
static Ref<StyleBox> default_style;
static Ref<Font> default_font;
+ static int default_font_size;
Ref<Font> default_theme_font;
+ int default_theme_font_size = -1;
static void _bind_methods();
+ void _freeze_change_propagation();
+ void _unfreeze_and_propagate_changes();
+
+ virtual void reset_state() override;
+
public:
static Ref<Theme> get_default();
static void set_default(const Ref<Theme> &p_default);
@@ -84,55 +123,100 @@ public:
static void set_default_icon(const Ref<Texture2D> &p_icon);
static void set_default_style(const Ref<StyleBox> &p_style);
static void set_default_font(const Ref<Font> &p_font);
+ static void set_default_font_size(int p_font_size);
void set_default_theme_font(const Ref<Font> &p_default_font);
Ref<Font> get_default_theme_font() const;
- void set_icon(const StringName &p_name, const StringName &p_type, const Ref<Texture2D> &p_icon);
- Ref<Texture2D> get_icon(const StringName &p_name, const StringName &p_type) const;
- bool has_icon(const StringName &p_name, const StringName &p_type) const;
- void clear_icon(const StringName &p_name, const StringName &p_type);
- void get_icon_list(StringName p_type, List<StringName> *p_list) const;
-
- void set_shader(const StringName &p_name, const StringName &p_type, const Ref<Shader> &p_shader);
- Ref<Shader> get_shader(const StringName &p_name, const StringName &p_type) const;
- bool has_shader(const StringName &p_name, const StringName &p_type) const;
- void clear_shader(const StringName &p_name, const StringName &p_type);
- void get_shader_list(const StringName &p_type, List<StringName> *p_list) const;
-
- void set_stylebox(const StringName &p_name, const StringName &p_type, const Ref<StyleBox> &p_style);
- Ref<StyleBox> get_stylebox(const StringName &p_name, const StringName &p_type) const;
- bool has_stylebox(const StringName &p_name, const StringName &p_type) const;
- void clear_stylebox(const StringName &p_name, const StringName &p_type);
- void get_stylebox_list(StringName p_type, List<StringName> *p_list) const;
- void get_stylebox_types(List<StringName> *p_list) const;
-
- void set_font(const StringName &p_name, const StringName &p_type, const Ref<Font> &p_font);
- Ref<Font> get_font(const StringName &p_name, const StringName &p_type) const;
- bool has_font(const StringName &p_name, const StringName &p_type) const;
- void clear_font(const StringName &p_name, const StringName &p_type);
- void get_font_list(StringName p_type, List<StringName> *p_list) const;
-
- void set_color(const StringName &p_name, const StringName &p_type, const Color &p_color);
- Color get_color(const StringName &p_name, const StringName &p_type) const;
- bool has_color(const StringName &p_name, const StringName &p_type) const;
- void clear_color(const StringName &p_name, const StringName &p_type);
- void get_color_list(StringName p_type, List<StringName> *p_list) const;
-
- void set_constant(const StringName &p_name, const StringName &p_type, int p_constant);
- int get_constant(const StringName &p_name, const StringName &p_type) const;
- bool has_constant(const StringName &p_name, const StringName &p_type) const;
- void clear_constant(const StringName &p_name, const StringName &p_type);
- void get_constant_list(StringName p_type, List<StringName> *p_list) const;
+ void set_default_theme_font_size(int p_font_size);
+ int get_default_theme_font_size() const;
+
+ void set_icon(const StringName &p_name, const StringName &p_theme_type, const Ref<Texture2D> &p_icon);
+ Ref<Texture2D> get_icon(const StringName &p_name, const StringName &p_theme_type) const;
+ bool has_icon(const StringName &p_name, const StringName &p_theme_type) const;
+ bool has_icon_nocheck(const StringName &p_name, const StringName &p_theme_type) const;
+ void rename_icon(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type);
+ void clear_icon(const StringName &p_name, const StringName &p_theme_type);
+ void get_icon_list(StringName p_theme_type, List<StringName> *p_list) const;
+ void add_icon_type(const StringName &p_theme_type);
+ void get_icon_type_list(List<StringName> *p_list) const;
+
+ void set_stylebox(const StringName &p_name, const StringName &p_theme_type, const Ref<StyleBox> &p_style);
+ Ref<StyleBox> get_stylebox(const StringName &p_name, const StringName &p_theme_type) const;
+ bool has_stylebox(const StringName &p_name, const StringName &p_theme_type) const;
+ bool has_stylebox_nocheck(const StringName &p_name, const StringName &p_theme_type) const;
+ void rename_stylebox(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type);
+ void clear_stylebox(const StringName &p_name, const StringName &p_theme_type);
+ void get_stylebox_list(StringName p_theme_type, List<StringName> *p_list) const;
+ void add_stylebox_type(const StringName &p_theme_type);
+ void get_stylebox_type_list(List<StringName> *p_list) const;
+
+ void set_font(const StringName &p_name, const StringName &p_theme_type, const Ref<Font> &p_font);
+ Ref<Font> get_font(const StringName &p_name, const StringName &p_theme_type) const;
+ bool has_font(const StringName &p_name, const StringName &p_theme_type) const;
+ bool has_font_nocheck(const StringName &p_name, const StringName &p_theme_type) const;
+ void rename_font(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type);
+ void clear_font(const StringName &p_name, const StringName &p_theme_type);
+ void get_font_list(StringName p_theme_type, List<StringName> *p_list) const;
+ void add_font_type(const StringName &p_theme_type);
+ void get_font_type_list(List<StringName> *p_list) const;
+
+ void set_font_size(const StringName &p_name, const StringName &p_theme_type, int p_font_size);
+ int get_font_size(const StringName &p_name, const StringName &p_theme_type) const;
+ bool has_font_size(const StringName &p_name, const StringName &p_theme_type) const;
+ bool has_font_size_nocheck(const StringName &p_name, const StringName &p_theme_type) const;
+ void rename_font_size(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type);
+ void clear_font_size(const StringName &p_name, const StringName &p_theme_type);
+ void get_font_size_list(StringName p_theme_type, List<StringName> *p_list) const;
+ void add_font_size_type(const StringName &p_theme_type);
+ void get_font_size_type_list(List<StringName> *p_list) const;
+
+ void set_color(const StringName &p_name, const StringName &p_theme_type, const Color &p_color);
+ Color get_color(const StringName &p_name, const StringName &p_theme_type) const;
+ bool has_color(const StringName &p_name, const StringName &p_theme_type) const;
+ bool has_color_nocheck(const StringName &p_name, const StringName &p_theme_type) const;
+ void rename_color(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type);
+ void clear_color(const StringName &p_name, const StringName &p_theme_type);
+ void get_color_list(StringName p_theme_type, List<StringName> *p_list) const;
+ void add_color_type(const StringName &p_theme_type);
+ void get_color_type_list(List<StringName> *p_list) const;
+
+ void set_constant(const StringName &p_name, const StringName &p_theme_type, int p_constant);
+ int get_constant(const StringName &p_name, const StringName &p_theme_type) const;
+ bool has_constant(const StringName &p_name, const StringName &p_theme_type) const;
+ bool has_constant_nocheck(const StringName &p_name, const StringName &p_theme_type) const;
+ void rename_constant(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type);
+ void clear_constant(const StringName &p_name, const StringName &p_theme_type);
+ void get_constant_list(StringName p_theme_type, List<StringName> *p_list) const;
+ void add_constant_type(const StringName &p_theme_type);
+ void get_constant_type_list(List<StringName> *p_list) const;
+
+ void set_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type, const Variant &p_value);
+ Variant get_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type) const;
+ bool has_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type) const;
+ bool has_theme_item_nocheck(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type) const;
+ void rename_theme_item(DataType p_data_type, const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type);
+ void clear_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type);
+ void get_theme_item_list(DataType p_data_type, StringName p_theme_type, List<StringName> *p_list) const;
+ void add_theme_item_type(DataType p_data_type, const StringName &p_theme_type);
+ void get_theme_item_type_list(DataType p_data_type, List<StringName> *p_list) const;
+
+ void set_type_variation(const StringName &p_theme_type, const StringName &p_base_type);
+ bool is_type_variation(const StringName &p_theme_type, const StringName &p_base_type) const;
+ void clear_type_variation(const StringName &p_theme_type);
+ StringName get_type_variation_base(const StringName &p_theme_type) const;
+ void get_type_variation_list(const StringName &p_base_type, List<StringName> *p_list) const;
void get_type_list(List<StringName> *p_list) const;
+ void get_type_dependencies(const StringName &p_base_type, const StringName &p_type_variant, List<StringName> *p_list);
- void copy_default_theme();
- void copy_theme(const Ref<Theme> &p_other);
+ void merge_with(const Ref<Theme> &p_other);
void clear();
Theme();
~Theme();
};
+VARIANT_ENUM_CAST(Theme::DataType);
+
#endif
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index 84b067d1e2..fcd31143a8 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,1169 +30,4184 @@
#include "tile_set.h"
-#include "core/array.h"
-#include "core/engine.h"
+#include "core/core_string_names.h"
#include "core/math/geometry_2d.h"
+#include "core/templates/local_vector.h"
+
+#include "scene/2d/navigation_region_2d.h"
+#include "scene/gui/control.h"
+#include "scene/resources/convex_polygon_shape_2d.h"
+#include "servers/navigation_server_2d.h"
+
+/////////////////////////////// TileSet //////////////////////////////////////
+
+const int TileSet::INVALID_SOURCE = -1;
+
+const char *TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[] = {
+ "right_side",
+ "right_corner",
+ "bottom_right_side",
+ "bottom_right_corner",
+ "bottom_side",
+ "bottom_corner",
+ "bottom_left_side",
+ "bottom_left_corner",
+ "left_side",
+ "left_corner",
+ "top_left_side",
+ "top_left_corner",
+ "top_side",
+ "top_corner",
+ "top_right_side",
+ "top_right_corner"
+};
+
+// -- Shape and layout --
+void TileSet::set_tile_shape(TileSet::TileShape p_shape) {
+ tile_shape = p_shape;
+
+ for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
+ E_source->get()->notify_tile_data_properties_should_change();
+ }
-bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
- String n = p_name;
- int slash = n.find("/");
- if (slash == -1) {
- return false;
+ terrain_bits_meshes_dirty = true;
+ tile_meshes_dirty = true;
+ emit_changed();
+}
+TileSet::TileShape TileSet::get_tile_shape() const {
+ return tile_shape;
+}
+
+void TileSet::set_tile_layout(TileSet::TileLayout p_layout) {
+ tile_layout = p_layout;
+ emit_changed();
+}
+TileSet::TileLayout TileSet::get_tile_layout() const {
+ return tile_layout;
+}
+
+void TileSet::set_tile_offset_axis(TileSet::TileOffsetAxis p_alignment) {
+ tile_offset_axis = p_alignment;
+
+ for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
+ E_source->get()->notify_tile_data_properties_should_change();
}
- int id = String::to_int(n.get_data(), slash);
-
- if (!tile_map.has(id)) {
- create_tile(id);
- }
- String what = n.substr(slash + 1, n.length());
-
- if (what == "name") {
- tile_set_name(id, p_value);
- } else if (what == "texture") {
- tile_set_texture(id, p_value);
- } else if (what == "normal_map") {
- tile_set_normal_map(id, p_value);
- } else if (what == "tex_offset") {
- tile_set_texture_offset(id, p_value);
- } else if (what == "material") {
- tile_set_material(id, p_value);
- } else if (what == "modulate") {
- tile_set_modulate(id, p_value);
- } else if (what == "region") {
- tile_set_region(id, p_value);
- } else if (what == "tile_mode") {
- tile_set_tile_mode(id, (TileMode)((int)p_value));
- } else if (what == "is_autotile") {
- // backward compatibility for Godot 3.0.x
- // autotile used to be a bool, it's now an enum
- bool is_autotile = p_value;
- if (is_autotile) {
- tile_set_tile_mode(id, AUTO_TILE);
- }
- } else if (what.left(9) == "autotile/") {
- what = what.right(9);
- if (what == "bitmask_mode") {
- autotile_set_bitmask_mode(id, (BitmaskMode)((int)p_value));
- } else if (what == "icon_coordinate") {
- autotile_set_icon_coordinate(id, p_value);
- } else if (what == "tile_size") {
- autotile_set_size(id, p_value);
- } else if (what == "spacing") {
- autotile_set_spacing(id, p_value);
- } else if (what == "bitmask_flags") {
- tile_map[id].autotile_data.flags.clear();
- if (p_value.is_array()) {
- Array p = p_value;
- Vector2 last_coord;
- while (p.size() > 0) {
- if (p[0].get_type() == Variant::VECTOR2) {
- last_coord = p[0];
- } else if (p[0].get_type() == Variant::INT) {
- autotile_set_bitmask(id, last_coord, p[0]);
- }
- p.pop_front();
- }
- }
- } else if (what == "occluder_map") {
- tile_map[id].autotile_data.occluder_map.clear();
- Array p = p_value;
- Vector2 last_coord;
- while (p.size() > 0) {
- if (p[0].get_type() == Variant::VECTOR2) {
- last_coord = p[0];
- } else if (p[0].get_type() == Variant::OBJECT) {
- autotile_set_light_occluder(id, p[0], last_coord);
- }
- p.pop_front();
- }
- } else if (what == "navpoly_map") {
- tile_map[id].autotile_data.navpoly_map.clear();
- Array p = p_value;
- Vector2 last_coord;
- while (p.size() > 0) {
- if (p[0].get_type() == Variant::VECTOR2) {
- last_coord = p[0];
- } else if (p[0].get_type() == Variant::OBJECT) {
- autotile_set_navigation_polygon(id, p[0], last_coord);
- }
- p.pop_front();
- }
- } else if (what == "priority_map") {
- tile_map[id].autotile_data.priority_map.clear();
- Array p = p_value;
- Vector3 val;
- Vector2 v;
- int priority;
- while (p.size() > 0) {
- val = p[0];
- if (val.z > 1) {
- v.x = val.x;
- v.y = val.y;
- priority = (int)val.z;
- tile_map[id].autotile_data.priority_map[v] = priority;
- }
- p.pop_front();
- }
- } else if (what == "z_index_map") {
- tile_map[id].autotile_data.z_index_map.clear();
- Array p = p_value;
- Vector3 val;
- Vector2 v;
- int z_index;
- while (p.size() > 0) {
- val = p[0];
- if (val.z != 0) {
- v.x = val.x;
- v.y = val.y;
- z_index = (int)val.z;
- tile_map[id].autotile_data.z_index_map[v] = z_index;
- }
- p.pop_front();
- }
- }
- } else if (what == "shape") {
- if (tile_get_shape_count(id) > 0) {
- for (int i = 0; i < tile_get_shape_count(id); i++) {
- tile_set_shape(id, i, p_value);
+
+ terrain_bits_meshes_dirty = true;
+ tile_meshes_dirty = true;
+ emit_changed();
+}
+TileSet::TileOffsetAxis TileSet::get_tile_offset_axis() const {
+ return tile_offset_axis;
+}
+
+void TileSet::set_tile_size(Size2i p_size) {
+ ERR_FAIL_COND(p_size.x < 1 || p_size.y < 1);
+ tile_size = p_size;
+ terrain_bits_meshes_dirty = true;
+ tile_meshes_dirty = true;
+ emit_changed();
+}
+Size2i TileSet::get_tile_size() const {
+ return tile_size;
+}
+
+int TileSet::get_next_source_id() const {
+ return next_source_id;
+}
+
+void TileSet::_compute_next_source_id() {
+ while (sources.has(next_source_id)) {
+ next_source_id = (next_source_id + 1) % 1073741824; // 2 ** 30
+ };
+}
+
+// Sources management
+int TileSet::add_source(Ref<TileSetSource> p_tile_set_source, int p_atlas_source_id_override) {
+ ERR_FAIL_COND_V(!p_tile_set_source.is_valid(), TileSet::INVALID_SOURCE);
+ ERR_FAIL_COND_V_MSG(p_atlas_source_id_override >= 0 && (sources.has(p_atlas_source_id_override)), TileSet::INVALID_SOURCE, vformat("Cannot create TileSet atlas source. Another atlas source exists with id %d.", p_atlas_source_id_override));
+
+ int new_source_id = p_atlas_source_id_override >= 0 ? p_atlas_source_id_override : next_source_id;
+ sources[new_source_id] = p_tile_set_source;
+ source_ids.append(new_source_id);
+ source_ids.sort();
+ p_tile_set_source->set_tile_set(this);
+ _compute_next_source_id();
+
+ sources[new_source_id]->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TileSet::_source_changed));
+
+ emit_changed();
+
+ return new_source_id;
+}
+
+void TileSet::remove_source(int p_source_id) {
+ ERR_FAIL_COND_MSG(!sources.has(p_source_id), vformat("Cannot remove TileSet atlas source. No tileset atlas source with id %d.", p_source_id));
+
+ sources[p_source_id]->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TileSet::_source_changed));
+
+ sources[p_source_id]->set_tile_set(nullptr);
+ sources.erase(p_source_id);
+ source_ids.erase(p_source_id);
+ source_ids.sort();
+
+ emit_changed();
+}
+
+void TileSet::set_source_id(int p_source_id, int p_new_source_id) {
+ ERR_FAIL_COND(p_new_source_id < 0);
+ ERR_FAIL_COND_MSG(!sources.has(p_source_id), vformat("Cannot change TileSet atlas source ID. No tileset atlas source with id %d.", p_source_id));
+ if (p_source_id == p_new_source_id) {
+ return;
+ }
+
+ ERR_FAIL_COND_MSG(sources.has(p_new_source_id), vformat("Cannot change TileSet atlas source ID. Another atlas source exists with id %d.", p_new_source_id));
+
+ sources[p_new_source_id] = sources[p_source_id];
+ sources.erase(p_source_id);
+
+ source_ids.erase(p_source_id);
+ source_ids.append(p_new_source_id);
+ source_ids.sort();
+
+ emit_changed();
+}
+
+bool TileSet::has_source(int p_source_id) const {
+ return sources.has(p_source_id);
+}
+
+Ref<TileSetSource> TileSet::get_source(int p_source_id) const {
+ ERR_FAIL_COND_V_MSG(!sources.has(p_source_id), nullptr, vformat("No TileSet atlas source with id %d.", p_source_id));
+
+ return sources[p_source_id];
+}
+
+int TileSet::get_source_count() const {
+ return source_ids.size();
+}
+
+int TileSet::get_source_id(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, source_ids.size(), TileSet::INVALID_SOURCE);
+ return source_ids[p_index];
+}
+
+// Rendering
+void TileSet::set_uv_clipping(bool p_uv_clipping) {
+ if (uv_clipping == p_uv_clipping) {
+ return;
+ }
+ uv_clipping = p_uv_clipping;
+ emit_changed();
+}
+
+bool TileSet::is_uv_clipping() const {
+ return uv_clipping;
+};
+
+void TileSet::set_occlusion_layers_count(int p_occlusion_layers_count) {
+ ERR_FAIL_COND(p_occlusion_layers_count < 0);
+ if (occlusion_layers.size() == p_occlusion_layers_count) {
+ return;
+ }
+
+ occlusion_layers.resize(p_occlusion_layers_count);
+
+ for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
+ E_source->get()->notify_tile_data_properties_should_change();
+ }
+
+ notify_property_list_changed();
+ emit_changed();
+}
+
+int TileSet::get_occlusion_layers_count() const {
+ return occlusion_layers.size();
+};
+
+void TileSet::set_occlusion_layer_light_mask(int p_layer_index, int p_light_mask) {
+ ERR_FAIL_INDEX(p_layer_index, occlusion_layers.size());
+ occlusion_layers.write[p_layer_index].light_mask = p_light_mask;
+ emit_changed();
+}
+
+int TileSet::get_occlusion_layer_light_mask(int p_layer_index) const {
+ ERR_FAIL_INDEX_V(p_layer_index, occlusion_layers.size(), 0);
+ return occlusion_layers[p_layer_index].light_mask;
+}
+
+void TileSet::set_occlusion_layer_sdf_collision(int p_layer_index, int p_sdf_collision) {
+ ERR_FAIL_INDEX(p_layer_index, occlusion_layers.size());
+ occlusion_layers.write[p_layer_index].sdf_collision = p_sdf_collision;
+ emit_changed();
+}
+
+bool TileSet::get_occlusion_layer_sdf_collision(int p_layer_index) const {
+ ERR_FAIL_INDEX_V(p_layer_index, occlusion_layers.size(), false);
+ return occlusion_layers[p_layer_index].sdf_collision;
+}
+
+// Physics
+void TileSet::set_physics_layers_count(int p_physics_layers_count) {
+ ERR_FAIL_COND(p_physics_layers_count < 0);
+ if (physics_layers.size() == p_physics_layers_count) {
+ return;
+ }
+
+ physics_layers.resize(p_physics_layers_count);
+
+ for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
+ E_source->get()->notify_tile_data_properties_should_change();
+ }
+
+ notify_property_list_changed();
+ emit_changed();
+}
+
+int TileSet::get_physics_layers_count() const {
+ return physics_layers.size();
+}
+
+void TileSet::set_physics_layer_collision_layer(int p_layer_index, uint32_t p_layer) {
+ ERR_FAIL_INDEX(p_layer_index, physics_layers.size());
+ physics_layers.write[p_layer_index].collision_layer = p_layer;
+ emit_changed();
+}
+
+uint32_t TileSet::get_physics_layer_collision_layer(int p_layer_index) const {
+ ERR_FAIL_INDEX_V(p_layer_index, physics_layers.size(), 0);
+ return physics_layers[p_layer_index].collision_layer;
+}
+
+void TileSet::set_physics_layer_collision_mask(int p_layer_index, uint32_t p_mask) {
+ ERR_FAIL_INDEX(p_layer_index, physics_layers.size());
+ physics_layers.write[p_layer_index].collision_mask = p_mask;
+ emit_changed();
+}
+
+uint32_t TileSet::get_physics_layer_collision_mask(int p_layer_index) const {
+ ERR_FAIL_INDEX_V(p_layer_index, physics_layers.size(), 0);
+ return physics_layers[p_layer_index].collision_mask;
+}
+
+void TileSet::set_physics_layer_physics_material(int p_layer_index, Ref<PhysicsMaterial> p_physics_material) {
+ ERR_FAIL_INDEX(p_layer_index, physics_layers.size());
+ physics_layers.write[p_layer_index].physics_material = p_physics_material;
+}
+
+Ref<PhysicsMaterial> TileSet::get_physics_layer_physics_material(int p_layer_index) const {
+ ERR_FAIL_INDEX_V(p_layer_index, physics_layers.size(), Ref<PhysicsMaterial>());
+ return physics_layers[p_layer_index].physics_material;
+}
+
+// Terrains
+void TileSet::set_terrain_sets_count(int p_terrains_sets_count) {
+ ERR_FAIL_COND(p_terrains_sets_count < 0);
+
+ terrain_sets.resize(p_terrains_sets_count);
+
+ notify_property_list_changed();
+ emit_changed();
+}
+
+int TileSet::get_terrain_sets_count() const {
+ return terrain_sets.size();
+}
+
+void TileSet::set_terrain_set_mode(int p_terrain_set, TerrainMode p_terrain_mode) {
+ ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size());
+ terrain_sets.write[p_terrain_set].mode = p_terrain_mode;
+ for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
+ E_source->get()->notify_tile_data_properties_should_change();
+ }
+
+ notify_property_list_changed();
+ emit_changed();
+}
+
+TileSet::TerrainMode TileSet::get_terrain_set_mode(int p_terrain_set) const {
+ ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES);
+ return terrain_sets[p_terrain_set].mode;
+}
+
+void TileSet::set_terrains_count(int p_terrain_set, int p_terrains_layers_count) {
+ ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size());
+ ERR_FAIL_COND(p_terrains_layers_count < 0);
+ if (terrain_sets[p_terrain_set].terrains.size() == p_terrains_layers_count) {
+ return;
+ }
+
+ int old_size = terrain_sets[p_terrain_set].terrains.size();
+ terrain_sets.write[p_terrain_set].terrains.resize(p_terrains_layers_count);
+
+ // Default name and color
+ for (int i = old_size; i < terrain_sets.write[p_terrain_set].terrains.size(); i++) {
+ float hue_rotate = (i * 2 % 16) / 16.0;
+ Color c;
+ c.set_hsv(Math::fmod(float(hue_rotate), float(1.0)), 0.5, 0.5);
+ terrain_sets.write[p_terrain_set].terrains.write[i].color = c;
+ terrain_sets.write[p_terrain_set].terrains.write[i].name = String(vformat("Terrain %d", i));
+ }
+
+ for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
+ E_source->get()->notify_tile_data_properties_should_change();
+ }
+
+ notify_property_list_changed();
+ emit_changed();
+}
+
+int TileSet::get_terrains_count(int p_terrain_set) const {
+ ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), -1);
+ return terrain_sets[p_terrain_set].terrains.size();
+}
+
+void TileSet::set_terrain_name(int p_terrain_set, int p_terrain_index, String p_name) {
+ ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size());
+ ERR_FAIL_INDEX(p_terrain_index, terrain_sets[p_terrain_set].terrains.size());
+ terrain_sets.write[p_terrain_set].terrains.write[p_terrain_index].name = p_name;
+ emit_changed();
+}
+
+String TileSet::get_terrain_name(int p_terrain_set, int p_terrain_index) const {
+ ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), String());
+ ERR_FAIL_INDEX_V(p_terrain_index, terrain_sets[p_terrain_set].terrains.size(), String());
+ return terrain_sets[p_terrain_set].terrains[p_terrain_index].name;
+}
+
+void TileSet::set_terrain_color(int p_terrain_set, int p_terrain_index, Color p_color) {
+ ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size());
+ ERR_FAIL_INDEX(p_terrain_index, terrain_sets[p_terrain_set].terrains.size());
+ if (p_color.a != 1.0) {
+ WARN_PRINT("Terrain color should have alpha == 1.0");
+ p_color.a = 1.0;
+ }
+ terrain_sets.write[p_terrain_set].terrains.write[p_terrain_index].color = p_color;
+ emit_changed();
+}
+
+Color TileSet::get_terrain_color(int p_terrain_set, int p_terrain_index) const {
+ ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), Color());
+ ERR_FAIL_INDEX_V(p_terrain_index, terrain_sets[p_terrain_set].terrains.size(), Color());
+ return terrain_sets[p_terrain_set].terrains[p_terrain_index].color;
+}
+
+bool TileSet::is_valid_peering_bit_for_mode(TileSet::TerrainMode p_terrain_mode, TileSet::CellNeighbor p_peering_bit) const {
+ if (tile_shape == TileSet::TILE_SHAPE_SQUARE) {
+ if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) {
+ if (p_peering_bit == TileSet::CELL_NEIGHBOR_RIGHT_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_LEFT_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_SIDE) {
+ return true;
}
- } else {
- tile_set_shape(id, 0, p_value);
}
- } else if (what == "shape_offset") {
- if (tile_get_shape_count(id) > 0) {
- for (int i = 0; i < tile_get_shape_count(id); i++) {
- tile_set_shape_offset(id, i, p_value);
+ if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
+ if (p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER) {
+ return true;
}
- } else {
- tile_set_shape_offset(id, 0, p_value);
}
- } else if (what == "shape_transform") {
- if (tile_get_shape_count(id) > 0) {
- for (int i = 0; i < tile_get_shape_count(id); i++) {
- tile_set_shape_transform(id, i, p_value);
+ } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) {
+ if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) {
+ if (p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) {
+ return true;
}
- } else {
- tile_set_shape_transform(id, 0, p_value);
}
- } else if (what == "shape_one_way") {
- if (tile_get_shape_count(id) > 0) {
- for (int i = 0; i < tile_get_shape_count(id); i++) {
- tile_set_shape_one_way(id, i, p_value);
+ if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
+ if (p_peering_bit == TileSet::CELL_NEIGHBOR_RIGHT_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_LEFT_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_CORNER) {
+ return true;
}
- } else {
- tile_set_shape_one_way(id, 0, p_value);
}
- } else if (what == "shape_one_way_margin") {
- if (tile_get_shape_count(id) > 0) {
- for (int i = 0; i < tile_get_shape_count(id); i++) {
- tile_set_shape_one_way_margin(id, i, p_value);
- }
- } else {
- tile_set_shape_one_way_margin(id, 0, p_value);
- }
- } else if (what == "shapes") {
- _tile_set_shapes(id, p_value);
- } else if (what == "occluder") {
- tile_set_light_occluder(id, p_value);
- } else if (what == "occluder_offset") {
- tile_set_occluder_offset(id, p_value);
- } else if (what == "navigation") {
- tile_set_navigation_polygon(id, p_value);
- } else if (what == "navigation_offset") {
- tile_set_navigation_polygon_offset(id, p_value);
- } else if (what == "z_index") {
- tile_set_z_index(id, p_value);
} else {
- return false;
- }
-
- return true;
-}
-
-bool TileSet::_get(const StringName &p_name, Variant &r_ret) const {
- String n = p_name;
- int slash = n.find("/");
- if (slash == -1) {
- return false;
- }
- int id = String::to_int(n.get_data(), slash);
-
- ERR_FAIL_COND_V(!tile_map.has(id), false);
-
- String what = n.substr(slash + 1, n.length());
-
- if (what == "name") {
- r_ret = tile_get_name(id);
- } else if (what == "texture") {
- r_ret = tile_get_texture(id);
- } else if (what == "normal_map") {
- r_ret = tile_get_normal_map(id);
- } else if (what == "tex_offset") {
- r_ret = tile_get_texture_offset(id);
- } else if (what == "material") {
- r_ret = tile_get_material(id);
- } else if (what == "modulate") {
- r_ret = tile_get_modulate(id);
- } else if (what == "region") {
- r_ret = tile_get_region(id);
- } else if (what == "tile_mode") {
- r_ret = tile_get_tile_mode(id);
- } else if (what.left(9) == "autotile/") {
- what = what.right(9);
- if (what == "bitmask_mode") {
- r_ret = autotile_get_bitmask_mode(id);
- } else if (what == "icon_coordinate") {
- r_ret = autotile_get_icon_coordinate(id);
- } else if (what == "tile_size") {
- r_ret = autotile_get_size(id);
- } else if (what == "spacing") {
- r_ret = autotile_get_spacing(id);
- } else if (what == "bitmask_flags") {
- Array p;
- for (Map<Vector2, uint32_t>::Element *E = tile_map[id].autotile_data.flags.front(); E; E = E->next()) {
- p.push_back(E->key());
- p.push_back(E->value());
- }
- r_ret = p;
- } else if (what == "occluder_map") {
- Array p;
- for (Map<Vector2, Ref<OccluderPolygon2D>>::Element *E = tile_map[id].autotile_data.occluder_map.front(); E; E = E->next()) {
- p.push_back(E->key());
- p.push_back(E->value());
+ if (get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
+ if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) {
+ if (p_peering_bit == TileSet::CELL_NEIGHBOR_RIGHT_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_LEFT_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) {
+ return true;
+ }
}
- r_ret = p;
- } else if (what == "navpoly_map") {
- Array p;
- for (Map<Vector2, Ref<NavigationPolygon>>::Element *E = tile_map[id].autotile_data.navpoly_map.front(); E; E = E->next()) {
- p.push_back(E->key());
- p.push_back(E->value());
+ if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
+ if (p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER) {
+ return true;
+ }
}
- r_ret = p;
- } else if (what == "priority_map") {
- Array p;
- Vector3 v;
- for (Map<Vector2, int>::Element *E = tile_map[id].autotile_data.priority_map.front(); E; E = E->next()) {
- if (E->value() > 1) {
- //Don't save default value
- v.x = E->key().x;
- v.y = E->key().y;
- v.z = E->value();
- p.push_back(v);
+ } else {
+ if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) {
+ if (p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) {
+ return true;
}
}
- r_ret = p;
- } else if (what == "z_index_map") {
- Array p;
- Vector3 v;
- for (Map<Vector2, int>::Element *E = tile_map[id].autotile_data.z_index_map.front(); E; E = E->next()) {
- if (E->value() != 0) {
- //Don't save default value
- v.x = E->key().x;
- v.y = E->key().y;
- v.z = E->value();
- p.push_back(v);
+ if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
+ if (p_peering_bit == TileSet::CELL_NEIGHBOR_RIGHT_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_LEFT_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER) {
+ return true;
}
}
- r_ret = p;
- }
- } else if (what == "shape") {
- r_ret = tile_get_shape(id, 0);
- } else if (what == "shape_offset") {
- r_ret = tile_get_shape_offset(id, 0);
- } else if (what == "shape_transform") {
- r_ret = tile_get_shape_transform(id, 0);
- } else if (what == "shape_one_way") {
- r_ret = tile_get_shape_one_way(id, 0);
- } else if (what == "shape_one_way_margin") {
- r_ret = tile_get_shape_one_way_margin(id, 0);
- } else if (what == "shapes") {
- r_ret = _tile_get_shapes(id);
- } else if (what == "occluder") {
- r_ret = tile_get_light_occluder(id);
- } else if (what == "occluder_offset") {
- r_ret = tile_get_occluder_offset(id);
- } else if (what == "navigation") {
- r_ret = tile_get_navigation_polygon(id);
- } else if (what == "navigation_offset") {
- r_ret = tile_get_navigation_polygon_offset(id);
- } else if (what == "z_index") {
- r_ret = tile_get_z_index(id);
- } else {
+ }
+ }
+ return false;
+}
+
+bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeighbor p_peering_bit) const {
+ if (p_terrain_set < 0 || p_terrain_set >= get_terrain_sets_count()) {
return false;
}
- return true;
+ TileSet::TerrainMode terrain_mode = get_terrain_set_mode(p_terrain_set);
+ return is_valid_peering_bit_for_mode(terrain_mode, p_peering_bit);
}
-void TileSet::_get_property_list(List<PropertyInfo> *p_list) const {
- for (Map<int, TileData>::Element *E = tile_map.front(); E; E = E->next()) {
- int id = E->key();
- String pre = itos(id) + "/";
- p_list->push_back(PropertyInfo(Variant::STRING, pre + "name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::OBJECT, pre + "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::OBJECT, pre + "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "tex_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::OBJECT, pre + "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::COLOR, pre + "modulate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::RECT2, pre + "region", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::INT, pre + "tile_mode", PROPERTY_HINT_ENUM, "SINGLE_TILE,AUTO_TILE,ATLAS_TILE", PROPERTY_USAGE_NOEDITOR));
- if (tile_get_tile_mode(id) == AUTO_TILE) {
- p_list->push_back(PropertyInfo(Variant::INT, pre + "autotile/bitmask_mode", PROPERTY_HINT_ENUM, "2X2,3X3 (minimal),3X3", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/bitmask_flags", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "autotile/icon_coordinate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "autotile/tile_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::INT, pre + "autotile/spacing", PROPERTY_HINT_RANGE, "0,256,1", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/occluder_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/navpoly_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/priority_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/z_index_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- } else if (tile_get_tile_mode(id) == ATLAS_TILE) {
- p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "autotile/icon_coordinate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "autotile/tile_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::INT, pre + "autotile/spacing", PROPERTY_HINT_RANGE, "0,256,1", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/occluder_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/navpoly_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/priority_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/z_index_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- }
- p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "occluder_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::OBJECT, pre + "occluder", PROPERTY_HINT_RESOURCE_TYPE, "OccluderPolygon2D", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "navigation_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::OBJECT, pre + "navigation", PROPERTY_HINT_RESOURCE_TYPE, "NavigationPolygon", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "shape_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "shape_transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::OBJECT, pre + "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape2D", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::BOOL, pre + "shape_one_way", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::FLOAT, pre + "shape_one_way_margin", PROPERTY_HINT_RANGE, "0,128,0.01", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "shapes", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::INT, pre + "z_index", PROPERTY_HINT_RANGE, itos(RS::CANVAS_ITEM_Z_MIN) + "," + itos(RS::CANVAS_ITEM_Z_MAX) + ",1", PROPERTY_USAGE_NOEDITOR));
- }
-}
-
-void TileSet::create_tile(int p_id) {
- ERR_FAIL_COND(tile_map.has(p_id));
- tile_map[p_id] = TileData();
- tile_map[p_id].autotile_data = AutotileData();
- _change_notify("");
- emit_changed();
-}
+// Navigation
+void TileSet::set_navigation_layers_count(int p_navigation_layers_count) {
+ ERR_FAIL_COND(p_navigation_layers_count < 0);
+ if (navigation_layers.size() == p_navigation_layers_count) {
+ return;
+ }
-void TileSet::autotile_set_bitmask_mode(int p_id, BitmaskMode p_mode) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].autotile_data.bitmask_mode = p_mode;
- _change_notify("");
+ navigation_layers.resize(p_navigation_layers_count);
+
+ for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
+ E_source->get()->notify_tile_data_properties_should_change();
+ }
+
+ notify_property_list_changed();
emit_changed();
}
-TileSet::BitmaskMode TileSet::autotile_get_bitmask_mode(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), BITMASK_2X2);
- return tile_map[p_id].autotile_data.bitmask_mode;
+int TileSet::get_navigation_layers_count() const {
+ return navigation_layers.size();
}
-void TileSet::tile_set_texture(int p_id, const Ref<Texture2D> &p_texture) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].texture = p_texture;
+void TileSet::set_navigation_layer_layers(int p_layer_index, uint32_t p_layers) {
+ ERR_FAIL_INDEX(p_layer_index, navigation_layers.size());
+ navigation_layers.write[p_layer_index].layers = p_layers;
emit_changed();
- _change_notify("texture");
}
-Ref<Texture2D> TileSet::tile_get_texture(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Ref<Texture2D>());
- return tile_map[p_id].texture;
+uint32_t TileSet::get_navigation_layer_layers(int p_layer_index) const {
+ ERR_FAIL_INDEX_V(p_layer_index, navigation_layers.size(), 0);
+ return navigation_layers[p_layer_index].layers;
}
-void TileSet::tile_set_normal_map(int p_id, const Ref<Texture2D> &p_normal_map) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].normal_map = p_normal_map;
+// Custom data.
+void TileSet::set_custom_data_layers_count(int p_custom_data_layers_count) {
+ ERR_FAIL_COND(p_custom_data_layers_count < 0);
+ if (custom_data_layers.size() == p_custom_data_layers_count) {
+ return;
+ }
+
+ custom_data_layers.resize(p_custom_data_layers_count);
+
+ for (Map<String, int>::Element *E = custom_data_layers_by_name.front(); E; E = E->next()) {
+ if (E->get() >= custom_data_layers.size()) {
+ custom_data_layers_by_name.erase(E);
+ }
+ }
+
+ for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
+ E_source->get()->notify_tile_data_properties_should_change();
+ }
+
+ notify_property_list_changed();
emit_changed();
}
-Ref<Texture2D> TileSet::tile_get_normal_map(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Ref<Texture2D>());
- return tile_map[p_id].normal_map;
+int TileSet::get_custom_data_layers_count() const {
+ return custom_data_layers.size();
+}
+
+int TileSet::get_custom_data_layer_by_name(String p_value) const {
+ if (custom_data_layers_by_name.has(p_value)) {
+ return custom_data_layers_by_name[p_value];
+ } else {
+ return -1;
+ }
}
-void TileSet::tile_set_material(int p_id, const Ref<ShaderMaterial> &p_material) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].material = p_material;
+void TileSet::set_custom_data_name(int p_layer_id, String p_value) {
+ ERR_FAIL_INDEX(p_layer_id, custom_data_layers.size());
+
+ // Exit if another property has the same name.
+ if (!p_value.is_empty()) {
+ for (int other_layer_id = 0; other_layer_id < get_custom_data_layers_count(); other_layer_id++) {
+ if (other_layer_id != p_layer_id && get_custom_data_name(other_layer_id) == p_value) {
+ ERR_FAIL_MSG(vformat("There is already a custom property named %s", p_value));
+ }
+ }
+ }
+
+ if (p_value.is_empty() && custom_data_layers_by_name.has(p_value)) {
+ custom_data_layers_by_name.erase(p_value);
+ } else {
+ custom_data_layers_by_name[p_value] = p_layer_id;
+ }
+
+ custom_data_layers.write[p_layer_id].name = p_value;
emit_changed();
}
-Ref<ShaderMaterial> TileSet::tile_get_material(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Ref<ShaderMaterial>());
- return tile_map[p_id].material;
+String TileSet::get_custom_data_name(int p_layer_id) const {
+ ERR_FAIL_INDEX_V(p_layer_id, custom_data_layers.size(), "");
+ return custom_data_layers[p_layer_id].name;
}
-void TileSet::tile_set_modulate(int p_id, const Color &p_modulate) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].modulate = p_modulate;
+void TileSet::set_custom_data_type(int p_layer_id, Variant::Type p_value) {
+ ERR_FAIL_INDEX(p_layer_id, custom_data_layers.size());
+ custom_data_layers.write[p_layer_id].type = p_value;
+
+ for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
+ E_source->get()->notify_tile_data_properties_should_change();
+ }
+
emit_changed();
- _change_notify("modulate");
}
-Color TileSet::tile_get_modulate(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Color(1, 1, 1));
- return tile_map[p_id].modulate;
+Variant::Type TileSet::get_custom_data_type(int p_layer_id) const {
+ ERR_FAIL_INDEX_V(p_layer_id, custom_data_layers.size(), Variant::NIL);
+ return custom_data_layers[p_layer_id].type;
}
-void TileSet::tile_set_texture_offset(int p_id, const Vector2 &p_offset) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].offset = p_offset;
+void TileSet::set_source_level_tile_proxy(int p_source_from, int p_source_to) {
+ ERR_FAIL_COND(p_source_from == TileSet::INVALID_SOURCE || p_source_to == TileSet::INVALID_SOURCE);
+
+ source_level_proxies[p_source_from] = p_source_to;
+
emit_changed();
}
-Vector2 TileSet::tile_get_texture_offset(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Vector2());
- return tile_map[p_id].offset;
+int TileSet::get_source_level_tile_proxy(int p_source_from) {
+ ERR_FAIL_COND_V(!source_level_proxies.has(p_source_from), TileSet::INVALID_SOURCE);
+
+ return source_level_proxies[p_source_from];
}
-void TileSet::tile_set_region(int p_id, const Rect2 &p_region) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].region = p_region;
- emit_changed();
- _change_notify("region");
+bool TileSet::has_source_level_tile_proxy(int p_source_from) {
+ return source_level_proxies.has(p_source_from);
}
-Rect2 TileSet::tile_get_region(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Rect2());
- return tile_map[p_id].region;
+void TileSet::remove_source_level_tile_proxy(int p_source_from) {
+ ERR_FAIL_COND(!source_level_proxies.has(p_source_from));
+
+ source_level_proxies.erase(p_source_from);
+
+ emit_changed();
}
-void TileSet::tile_set_tile_mode(int p_id, TileMode p_tile_mode) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].tile_mode = p_tile_mode;
+void TileSet::set_coords_level_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_source_to, Vector2i p_coords_to) {
+ ERR_FAIL_COND(p_source_from == TileSet::INVALID_SOURCE || p_source_to == TileSet::INVALID_SOURCE);
+ ERR_FAIL_COND(p_coords_from == TileSetSource::INVALID_ATLAS_COORDS || p_coords_to == TileSetSource::INVALID_ATLAS_COORDS);
+
+ Array from;
+ from.push_back(p_source_from);
+ from.push_back(p_coords_from);
+
+ Array to;
+ to.push_back(p_source_to);
+ to.push_back(p_coords_to);
+
+ coords_level_proxies[from] = to;
+
emit_changed();
- _change_notify("tile_mode");
}
-TileSet::TileMode TileSet::tile_get_tile_mode(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), SINGLE_TILE);
- return tile_map[p_id].tile_mode;
+Array TileSet::get_coords_level_tile_proxy(int p_source_from, Vector2i p_coords_from) {
+ Array from;
+ from.push_back(p_source_from);
+ from.push_back(p_coords_from);
+
+ ERR_FAIL_COND_V(!coords_level_proxies.has(from), Array());
+
+ return coords_level_proxies[from];
+}
+
+bool TileSet::has_coords_level_tile_proxy(int p_source_from, Vector2i p_coords_from) {
+ Array from;
+ from.push_back(p_source_from);
+ from.push_back(p_coords_from);
+
+ return coords_level_proxies.has(from);
}
-void TileSet::autotile_set_icon_coordinate(int p_id, Vector2 coord) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].autotile_data.icon_coord = coord;
+void TileSet::remove_coords_level_tile_proxy(int p_source_from, Vector2i p_coords_from) {
+ Array from;
+ from.push_back(p_source_from);
+ from.push_back(p_coords_from);
+
+ ERR_FAIL_COND(!coords_level_proxies.has(from));
+
+ coords_level_proxies.erase(from);
+
emit_changed();
}
-Vector2 TileSet::autotile_get_icon_coordinate(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Vector2());
- return tile_map[p_id].autotile_data.icon_coord;
-}
+void TileSet::set_alternative_level_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_alternative_from, int p_source_to, Vector2i p_coords_to, int p_alternative_to) {
+ ERR_FAIL_COND(p_source_from == TileSet::INVALID_SOURCE || p_source_to == TileSet::INVALID_SOURCE);
+ ERR_FAIL_COND(p_coords_from == TileSetSource::INVALID_ATLAS_COORDS || p_coords_to == TileSetSource::INVALID_ATLAS_COORDS);
+
+ Array from;
+ from.push_back(p_source_from);
+ from.push_back(p_coords_from);
+ from.push_back(p_alternative_from);
+
+ Array to;
+ to.push_back(p_source_to);
+ to.push_back(p_coords_to);
+ to.push_back(p_alternative_to);
+
+ alternative_level_proxies[from] = to;
-void TileSet::autotile_set_spacing(int p_id, int p_spacing) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- ERR_FAIL_COND(p_spacing < 0);
- tile_map[p_id].autotile_data.spacing = p_spacing;
emit_changed();
}
-int TileSet::autotile_get_spacing(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), 0);
- return tile_map[p_id].autotile_data.spacing;
+Array TileSet::get_alternative_level_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_alternative_from) {
+ Array from;
+ from.push_back(p_source_from);
+ from.push_back(p_coords_from);
+ from.push_back(p_alternative_from);
+
+ ERR_FAIL_COND_V(!alternative_level_proxies.has(from), Array());
+
+ return alternative_level_proxies[from];
}
-void TileSet::autotile_set_size(int p_id, Size2 p_size) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- ERR_FAIL_COND(p_size.x <= 0 || p_size.y <= 0);
- tile_map[p_id].autotile_data.size = p_size;
+bool TileSet::has_alternative_level_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_alternative_from) {
+ Array from;
+ from.push_back(p_source_from);
+ from.push_back(p_coords_from);
+ from.push_back(p_alternative_from);
+
+ return alternative_level_proxies.has(from);
+}
+
+void TileSet::remove_alternative_level_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_alternative_from) {
+ Array from;
+ from.push_back(p_source_from);
+ from.push_back(p_coords_from);
+ from.push_back(p_alternative_from);
+
+ ERR_FAIL_COND(!alternative_level_proxies.has(from));
+
+ alternative_level_proxies.erase(from);
+
+ emit_changed();
}
-Size2 TileSet::autotile_get_size(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Size2());
- return tile_map[p_id].autotile_data.size;
+Array TileSet::get_source_level_tile_proxies() const {
+ Array output;
+ for (Map<int, int>::Element *E = source_level_proxies.front(); E; E = E->next()) {
+ Array proxy;
+ proxy.push_back(E->key());
+ proxy.push_back(E->get());
+ output.push_back(proxy);
+ }
+ return output;
}
-void TileSet::autotile_clear_bitmask_map(int p_id) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].autotile_data.flags.clear();
+Array TileSet::get_coords_level_tile_proxies() const {
+ Array output;
+ for (Map<Array, Array>::Element *E = coords_level_proxies.front(); E; E = E->next()) {
+ Array proxy;
+ proxy.append_array(E->key());
+ proxy.append_array(E->get());
+ output.push_back(proxy);
+ }
+ return output;
}
-void TileSet::autotile_set_subtile_priority(int p_id, const Vector2 &p_coord, int p_priority) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- ERR_FAIL_COND(p_priority <= 0);
- tile_map[p_id].autotile_data.priority_map[p_coord] = p_priority;
+Array TileSet::get_alternative_level_tile_proxies() const {
+ Array output;
+ for (Map<Array, Array>::Element *E = alternative_level_proxies.front(); E; E = E->next()) {
+ Array proxy;
+ proxy.append_array(E->key());
+ proxy.append_array(E->get());
+ output.push_back(proxy);
+ }
+ return output;
}
-int TileSet::autotile_get_subtile_priority(int p_id, const Vector2 &p_coord) {
- ERR_FAIL_COND_V(!tile_map.has(p_id), 1);
- if (tile_map[p_id].autotile_data.priority_map.has(p_coord)) {
- return tile_map[p_id].autotile_data.priority_map[p_coord];
+Array TileSet::map_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_alternative_from) const {
+ Array from;
+ from.push_back(p_source_from);
+ from.push_back(p_coords_from);
+ from.push_back(p_alternative_from);
+
+ // Check if the tile is valid, and if so, don't map the tile and return the input.
+ if (has_source(p_source_from)) {
+ Ref<TileSetSource> source = get_source(p_source_from);
+ if (source->has_tile(p_coords_from) && source->has_alternative_tile(p_coords_from, p_alternative_from)) {
+ return from;
+ }
}
- //When not custom priority set return the default value
- return 1;
+
+ // Source, coords and alternative match.
+ if (alternative_level_proxies.has(from)) {
+ return alternative_level_proxies[from].duplicate();
+ }
+
+ // Source and coords match.
+ from.pop_back();
+ if (coords_level_proxies.has(from)) {
+ Array output = coords_level_proxies[from].duplicate();
+ output.push_back(p_alternative_from);
+ return output;
+ }
+
+ // Source matches.
+ if (source_level_proxies.has(p_source_from)) {
+ Array output;
+ output.push_back(source_level_proxies[p_source_from]);
+ output.push_back(p_coords_from);
+ output.push_back(p_alternative_from);
+ return output;
+ }
+
+ Array output;
+ output.push_back(p_source_from);
+ output.push_back(p_coords_from);
+ output.push_back(p_alternative_from);
+ return output;
}
-const Map<Vector2, int> &TileSet::autotile_get_priority_map(int p_id) const {
- static Map<Vector2, int> dummy;
- ERR_FAIL_COND_V(!tile_map.has(p_id), dummy);
- return tile_map[p_id].autotile_data.priority_map;
+void TileSet::cleanup_invalid_tile_proxies() {
+ // Source level.
+ Vector<int> source_to_remove;
+ for (Map<int, int>::Element *E = source_level_proxies.front(); E; E = E->next()) {
+ if (has_source(E->key())) {
+ source_to_remove.append(E->key());
+ }
+ }
+ for (int i = 0; i < source_to_remove.size(); i++) {
+ remove_source_level_tile_proxy(source_to_remove[i]);
+ }
+
+ // Coords level.
+ Vector<Array> coords_to_remove;
+ for (Map<Array, Array>::Element *E = coords_level_proxies.front(); E; E = E->next()) {
+ Array a = E->key();
+ if (has_source(a[0]) && get_source(a[0])->has_tile(a[1])) {
+ coords_to_remove.append(a);
+ }
+ }
+ for (int i = 0; i < coords_to_remove.size(); i++) {
+ Array a = coords_to_remove[i];
+ remove_coords_level_tile_proxy(a[0], a[1]);
+ }
+
+ // Alternative level.
+ Vector<Array> alternative_to_remove;
+ for (Map<Array, Array>::Element *E = alternative_level_proxies.front(); E; E = E->next()) {
+ Array a = E->key();
+ if (has_source(a[0]) && get_source(a[0])->has_tile(a[1]) && get_source(a[0])->has_alternative_tile(a[1], a[2])) {
+ alternative_to_remove.append(a);
+ }
+ }
+ for (int i = 0; i < alternative_to_remove.size(); i++) {
+ Array a = alternative_to_remove[i];
+ remove_alternative_level_tile_proxy(a[0], a[1], a[2]);
+ }
}
-void TileSet::autotile_set_z_index(int p_id, const Vector2 &p_coord, int p_z_index) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].autotile_data.z_index_map[p_coord] = p_z_index;
+void TileSet::clear_tile_proxies() {
+ source_level_proxies.clear();
+ coords_level_proxies.clear();
+ alternative_level_proxies.clear();
+
emit_changed();
}
-int TileSet::autotile_get_z_index(int p_id, const Vector2 &p_coord) {
- ERR_FAIL_COND_V(!tile_map.has(p_id), 1);
- if (tile_map[p_id].autotile_data.z_index_map.has(p_coord)) {
- return tile_map[p_id].autotile_data.z_index_map[p_coord];
+Vector<Vector2> TileSet::get_tile_shape_polygon() {
+ Vector<Vector2> points;
+ if (tile_shape == TileSet::TILE_SHAPE_SQUARE) {
+ points.append(Vector2(0.0, 0.0));
+ points.append(Vector2(1.0, 0.0));
+ points.append(Vector2(1.0, 1.0));
+ points.append(Vector2(0.0, 1.0));
+ } else {
+ float overlap = 0.0;
+ switch (tile_shape) {
+ case TileSet::TILE_SHAPE_ISOMETRIC:
+ overlap = 0.5;
+ break;
+ case TileSet::TILE_SHAPE_HEXAGON:
+ overlap = 0.25;
+ break;
+ case TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE:
+ overlap = 0.0;
+ break;
+ default:
+ break;
+ }
+
+ points.append(Vector2(0.5, 0.0));
+ points.append(Vector2(0.0, overlap));
+ points.append(Vector2(0.0, 1.0 - overlap));
+ points.append(Vector2(0.5, 1.0));
+ points.append(Vector2(1.0, 1.0 - overlap));
+ points.append(Vector2(1.0, overlap));
+ points.append(Vector2(0.5, 0.0));
+ if (get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL) {
+ for (int i = 0; i < points.size(); i++) {
+ points.write[i] = Vector2(points[i].y, points[i].x);
+ }
+ }
}
- //When not custom z index set return the default value
- return 0;
+ for (int i = 0; i < points.size(); i++) {
+ points.write[i] = points[i] * tile_size - tile_size / 2;
+ }
+ return points;
}
-const Map<Vector2, int> &TileSet::autotile_get_z_index_map(int p_id) const {
- static Map<Vector2, int> dummy;
- ERR_FAIL_COND_V(!tile_map.has(p_id), dummy);
- return tile_map[p_id].autotile_data.z_index_map;
+void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Rect2 p_region, Color p_color, bool p_filled, Ref<Texture2D> p_texture) {
+ if (tile_meshes_dirty) {
+ Vector<Vector2> uvs = get_tile_shape_polygon();
+ for (int i = 0; i < uvs.size(); i++) {
+ uvs.write[i] = (uvs[i] + tile_size / 2) / tile_size;
+ }
+
+ Vector<Color> colors;
+ colors.resize(uvs.size());
+ colors.fill(Color(1.0, 1.0, 1.0, 1.0));
+
+ // Filled mesh.
+ tile_filled_mesh->clear_surfaces();
+ Array a;
+ a.resize(Mesh::ARRAY_MAX);
+ a[Mesh::ARRAY_VERTEX] = uvs;
+ a[Mesh::ARRAY_TEX_UV] = uvs;
+ a[Mesh::ARRAY_COLOR] = colors;
+ a[Mesh::ARRAY_INDEX] = Geometry2D::triangulate_polygon(uvs);
+ tile_filled_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES);
+
+ // Lines mesh.
+ tile_lines_mesh->clear_surfaces();
+ a.clear();
+ a.resize(Mesh::ARRAY_MAX);
+ // Add the first point again when drawing lines.
+ uvs.push_back(uvs[0]);
+ colors.push_back(colors[0]);
+ a[Mesh::ARRAY_VERTEX] = uvs;
+ a[Mesh::ARRAY_COLOR] = colors;
+ tile_lines_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINE_STRIP, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES);
+
+ tile_meshes_dirty = false;
+ }
+
+ Transform2D xform;
+ xform.scale(p_region.size);
+ xform.set_origin(p_region.get_position());
+ if (p_filled) {
+ p_canvas_item->draw_mesh(tile_filled_mesh, p_texture, xform, p_color);
+ } else {
+ p_canvas_item->draw_mesh(tile_lines_mesh, Ref<Texture2D>(), xform, p_color);
+ }
}
-void TileSet::autotile_set_bitmask(int p_id, Vector2 p_coord, uint32_t p_flag) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- if (p_flag == 0) {
- if (tile_map[p_id].autotile_data.flags.has(p_coord)) {
- tile_map[p_id].autotile_data.flags.erase(p_coord);
+Vector<Point2> TileSet::get_terrain_bit_polygon(int p_terrain_set, TileSet::CellNeighbor p_bit) {
+ ERR_FAIL_COND_V(p_terrain_set < 0 || p_terrain_set >= get_terrain_sets_count(), Vector<Point2>());
+
+ TileSet::TerrainMode terrain_mode = get_terrain_set_mode(p_terrain_set);
+
+ if (tile_shape == TileSet::TILE_SHAPE_SQUARE) {
+ if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) {
+ return _get_square_corner_or_side_terrain_bit_polygon(tile_size, p_bit);
+ } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
+ return _get_square_corner_terrain_bit_polygon(tile_size, p_bit);
+ } else { // TileData::TERRAIN_MODE_MATCH_SIDES
+ return _get_square_side_terrain_bit_polygon(tile_size, p_bit);
+ }
+ } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) {
+ if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) {
+ return _get_isometric_corner_or_side_terrain_bit_polygon(tile_size, p_bit);
+ } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
+ return _get_isometric_corner_terrain_bit_polygon(tile_size, p_bit);
+ } else { // TileData::TERRAIN_MODE_MATCH_SIDES
+ return _get_isometric_side_terrain_bit_polygon(tile_size, p_bit);
}
} else {
- tile_map[p_id].autotile_data.flags[p_coord] = p_flag;
+ float overlap = 0.0;
+ switch (tile_shape) {
+ case TileSet::TILE_SHAPE_HEXAGON:
+ overlap = 0.25;
+ break;
+ case TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE:
+ overlap = 0.0;
+ break;
+ default:
+ break;
+ }
+ if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) {
+ return _get_half_offset_corner_or_side_terrain_bit_polygon(tile_size, p_bit, overlap, tile_offset_axis);
+ } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
+ return _get_half_offset_corner_terrain_bit_polygon(tile_size, p_bit, overlap, tile_offset_axis);
+ } else { // TileData::TERRAIN_MODE_MATCH_SIDES
+ return _get_half_offset_side_terrain_bit_polygon(tile_size, p_bit, overlap, tile_offset_axis);
+ }
}
}
-uint32_t TileSet::autotile_get_bitmask(int p_id, Vector2 p_coord) {
- ERR_FAIL_COND_V(!tile_map.has(p_id), 0);
- if (!tile_map[p_id].autotile_data.flags.has(p_coord)) {
- return 0;
+#define TERRAIN_ALPHA 0.6
+
+void TileSet::draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, const TileData *p_tile_data) {
+ ERR_FAIL_COND(!p_tile_data);
+
+ if (terrain_bits_meshes_dirty) {
+ // Recompute the meshes.
+ terrain_bits_meshes.clear();
+
+ for (int terrain_mode_index = 0; terrain_mode_index < 3; terrain_mode_index++) {
+ TerrainMode terrain_mode = TerrainMode(terrain_mode_index);
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ CellNeighbor bit = CellNeighbor(i);
+
+ if (is_valid_peering_bit_for_mode(terrain_mode, bit)) {
+ Vector<Vector2> polygon;
+ if (tile_shape == TileSet::TILE_SHAPE_SQUARE) {
+ if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) {
+ polygon = _get_square_corner_or_side_terrain_bit_polygon(tile_size, bit);
+ } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
+ polygon = _get_square_corner_terrain_bit_polygon(tile_size, bit);
+ } else { // TileData::TERRAIN_MODE_MATCH_SIDES
+ polygon = _get_square_side_terrain_bit_polygon(tile_size, bit);
+ }
+ } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) {
+ if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) {
+ polygon = _get_isometric_corner_or_side_terrain_bit_polygon(tile_size, bit);
+ } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
+ polygon = _get_isometric_corner_terrain_bit_polygon(tile_size, bit);
+ } else { // TileData::TERRAIN_MODE_MATCH_SIDES
+ polygon = _get_isometric_side_terrain_bit_polygon(tile_size, bit);
+ }
+ } else {
+ float overlap = 0.0;
+ switch (tile_shape) {
+ case TileSet::TILE_SHAPE_HEXAGON:
+ overlap = 0.25;
+ break;
+ case TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE:
+ overlap = 0.0;
+ break;
+ default:
+ break;
+ }
+ if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) {
+ polygon = _get_half_offset_corner_or_side_terrain_bit_polygon(tile_size, bit, overlap, tile_offset_axis);
+ } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
+ polygon = _get_half_offset_corner_terrain_bit_polygon(tile_size, bit, overlap, tile_offset_axis);
+ } else { // TileData::TERRAIN_MODE_MATCH_SIDES
+ polygon = _get_half_offset_side_terrain_bit_polygon(tile_size, bit, overlap, tile_offset_axis);
+ }
+ }
+
+ Ref<ArrayMesh> mesh;
+ mesh.instantiate();
+ Vector<Vector2> uvs;
+ uvs.resize(polygon.size());
+ Vector<Color> colors;
+ colors.resize(polygon.size());
+ colors.fill(Color(1.0, 1.0, 1.0, 1.0));
+ Array a;
+ a.resize(Mesh::ARRAY_MAX);
+ a[Mesh::ARRAY_VERTEX] = polygon;
+ a[Mesh::ARRAY_TEX_UV] = uvs;
+ a[Mesh::ARRAY_COLOR] = colors;
+ a[Mesh::ARRAY_INDEX] = Geometry2D::triangulate_polygon(polygon);
+ mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES);
+ terrain_bits_meshes[terrain_mode][bit] = mesh;
+ }
+ }
+ }
+ terrain_bits_meshes_dirty = false;
}
- return tile_map[p_id].autotile_data.flags[p_coord];
-}
-const Map<Vector2, uint32_t> &TileSet::autotile_get_bitmask_map(int p_id) {
- static Map<Vector2, uint32_t> dummy;
- static Map<Vector2, uint32_t> dummy_atlas;
- ERR_FAIL_COND_V(!tile_map.has(p_id), dummy);
- if (tile_get_tile_mode(p_id) == ATLAS_TILE) {
- dummy_atlas = Map<Vector2, uint32_t>();
- Rect2 region = tile_get_region(p_id);
- Size2 size = autotile_get_size(p_id);
- float spacing = autotile_get_spacing(p_id);
- for (int x = 0; x < (region.size.x / (size.x + spacing)); x++) {
- for (int y = 0; y < (region.size.y / (size.y + spacing)); y++) {
- dummy_atlas.insert(Vector2(x, y), 0);
+ int terrain_set = p_tile_data->get_terrain_set();
+ if (terrain_set < 0) {
+ return;
+ }
+ TileSet::TerrainMode terrain_mode = get_terrain_set_mode(terrain_set);
+
+ RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform);
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ CellNeighbor bit = CellNeighbor(i);
+ if (is_valid_peering_bit_terrain(terrain_set, bit)) {
+ int terrain_id = p_tile_data->get_peering_bit_terrain(bit);
+ if (terrain_id >= 0) {
+ Color color = get_terrain_color(terrain_set, terrain_id);
+ color.a = TERRAIN_ALPHA;
+ p_canvas_item->draw_mesh(terrain_bits_meshes[terrain_mode][bit], Ref<Texture2D>(), Transform2D(), color);
}
}
- return dummy_atlas;
- } else {
- return tile_map[p_id].autotile_data.flags;
}
+ RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D());
}
-Vector2 TileSet::autotile_get_subtile_for_bitmask(int p_id, uint16_t p_bitmask, const Node *p_tilemap_node, const Vector2 &p_tile_location) {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Vector2());
- //First try to forward selection to script
- if (p_tilemap_node->get_class_name() == "TileMap") {
- if (get_script_instance() != nullptr) {
- if (get_script_instance()->has_method("_forward_subtile_selection")) {
- Variant ret = get_script_instance()->call("_forward_subtile_selection", p_id, p_bitmask, p_tilemap_node, p_tile_location);
- if (ret.get_type() == Variant::VECTOR2) {
- return ret;
+Vector<Vector<Ref<Texture2D>>> TileSet::generate_terrains_icons(Size2i p_size) {
+ // Counts the number of matching terrain tiles and find the best matching icon.
+ struct Count {
+ int count = 0;
+ float probability = 0.0;
+ Ref<Texture2D> texture;
+ Rect2i region;
+ };
+ Vector<Vector<Ref<Texture2D>>> output;
+ LocalVector<LocalVector<Count>> counts;
+ output.resize(get_terrain_sets_count());
+ counts.resize(get_terrain_sets_count());
+ for (int terrain_set = 0; terrain_set < get_terrain_sets_count(); terrain_set++) {
+ output.write[terrain_set].resize(get_terrains_count(terrain_set));
+ counts[terrain_set].resize(get_terrains_count(terrain_set));
+ }
+
+ for (int source_index = 0; source_index < get_source_count(); source_index++) {
+ int source_id = get_source_id(source_index);
+ Ref<TileSetSource> source = get_source(source_id);
+
+ Ref<TileSetAtlasSource> atlas_source = source;
+ if (atlas_source.is_valid()) {
+ for (int tile_index = 0; tile_index < source->get_tiles_count(); tile_index++) {
+ Vector2i tile_id = source->get_tile_id(tile_index);
+ for (int alternative_index = 0; alternative_index < source->get_alternative_tiles_count(tile_id); alternative_index++) {
+ int alternative_id = source->get_alternative_tile_id(tile_id, alternative_index);
+
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(tile_id, alternative_id));
+ int terrain_set = tile_data->get_terrain_set();
+ if (terrain_set >= 0) {
+ ERR_FAIL_INDEX_V(terrain_set, get_terrain_sets_count(), Vector<Vector<Ref<Texture2D>>>());
+
+ LocalVector<int> bit_counts;
+ bit_counts.resize(get_terrains_count(terrain_set));
+ for (int terrain = 0; terrain < get_terrains_count(terrain_set); terrain++) {
+ bit_counts[terrain] = 0;
+ }
+ for (int terrain_bit = 0; terrain_bit < TileSet::CELL_NEIGHBOR_MAX; terrain_bit++) {
+ TileSet::CellNeighbor cell_neighbor = TileSet::CellNeighbor(terrain_bit);
+ if (is_valid_peering_bit_terrain(terrain_set, cell_neighbor)) {
+ int terrain = tile_data->get_peering_bit_terrain(cell_neighbor);
+ if (terrain >= 0) {
+ bit_counts[terrain] += 1;
+ }
+ }
+ }
+
+ for (int terrain = 0; terrain < get_terrains_count(terrain_set); terrain++) {
+ if ((bit_counts[terrain] > counts[terrain_set][terrain].count) || (bit_counts[terrain] == counts[terrain_set][terrain].count && tile_data->get_probability() > counts[terrain_set][terrain].probability)) {
+ counts[terrain_set][terrain].count = bit_counts[terrain];
+ counts[terrain_set][terrain].probability = tile_data->get_probability();
+ counts[terrain_set][terrain].texture = atlas_source->get_texture();
+ counts[terrain_set][terrain].region = atlas_source->get_tile_texture_region(tile_id);
+ }
+ }
+ }
}
}
}
}
- List<Vector2> coords;
- List<uint32_t> priorities;
- uint32_t priority_sum = 0;
- uint32_t mask;
- uint16_t mask_;
- uint16_t mask_ignore;
- for (Map<Vector2, uint32_t>::Element *E = tile_map[p_id].autotile_data.flags.front(); E; E = E->next()) {
- mask = E->get();
- if (tile_map[p_id].autotile_data.bitmask_mode == BITMASK_2X2) {
- mask |= (BIND_IGNORE_TOP | BIND_IGNORE_LEFT | BIND_IGNORE_CENTER | BIND_IGNORE_RIGHT | BIND_IGNORE_BOTTOM);
+ // Generate the icons.
+ for (int terrain_set = 0; terrain_set < get_terrain_sets_count(); terrain_set++) {
+ for (int terrain = 0; terrain < get_terrains_count(terrain_set); terrain++) {
+ Ref<Image> image;
+ image.instantiate();
+ if (counts[terrain_set][terrain].count > 0) {
+ // Get the best tile.
+ Ref<Texture2D> texture = counts[terrain_set][terrain].texture;
+ Rect2 region = counts[terrain_set][terrain].region;
+ image->create(region.size.x, region.size.y, false, Image::FORMAT_RGBA8);
+ image->blit_rect(texture->get_image(), region, Point2());
+ image->resize(p_size.x, p_size.y, Image::INTERPOLATE_NEAREST);
+ } else {
+ image->create(1, 1, false, Image::FORMAT_RGBA8);
+ image->set_pixel(0, 0, get_terrain_color(terrain_set, terrain));
+ }
+ Ref<ImageTexture> icon;
+ icon.instantiate();
+ icon->create_from_image(image);
+ icon->set_size_override(p_size);
+
+ output.write[terrain_set].write[terrain] = icon;
}
+ }
+ return output;
+}
- mask_ = mask & 0xFFFF;
- mask_ignore = mask >> 16;
+void TileSet::_source_changed() {
+ emit_changed();
+}
- if (((mask_ & (~mask_ignore)) == (p_bitmask & (~mask_ignore))) && (((~mask_) | mask_ignore) == ((~p_bitmask) | mask_ignore))) {
- uint32_t priority = autotile_get_subtile_priority(p_id, E->key());
- priority_sum += priority;
- priorities.push_back(priority);
- coords.push_back(E->key());
- }
+Vector<Point2> TileSet::_get_square_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) {
+ Rect2 bit_rect;
+ bit_rect.size = Vector2(p_size) / 3;
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
+ bit_rect.position = Vector2(1, -1);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
+ bit_rect.position = Vector2(1, 1);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE:
+ bit_rect.position = Vector2(-1, 1);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
+ bit_rect.position = Vector2(-3, 1);
+ break;
+ case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
+ bit_rect.position = Vector2(-3, -1);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
+ bit_rect.position = Vector2(-3, -3);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_SIDE:
+ bit_rect.position = Vector2(-1, -3);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
+ bit_rect.position = Vector2(1, -3);
+ break;
+ default:
+ break;
}
+ bit_rect.position *= Vector2(p_size) / 6.0;
+ Vector<Vector2> polygon;
+ polygon.push_back(bit_rect.position);
+ polygon.push_back(Vector2(bit_rect.get_end().x, bit_rect.position.y));
+ polygon.push_back(bit_rect.get_end());
+ polygon.push_back(Vector2(bit_rect.position.x, bit_rect.get_end().y));
+ return polygon;
+}
- if (coords.size() == 0) {
- return autotile_get_icon_coordinate(p_id);
+Vector<Point2> TileSet::_get_square_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) {
+ Vector2 unit = Vector2(p_size) / 6.0;
+ Vector<Vector2> polygon;
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
+ polygon.push_back(Vector2(0, 3) * unit);
+ polygon.push_back(Vector2(3, 3) * unit);
+ polygon.push_back(Vector2(3, 0) * unit);
+ polygon.push_back(Vector2(1, 0) * unit);
+ polygon.push_back(Vector2(1, 1) * unit);
+ polygon.push_back(Vector2(0, 1) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
+ polygon.push_back(Vector2(0, 3) * unit);
+ polygon.push_back(Vector2(-3, 3) * unit);
+ polygon.push_back(Vector2(-3, 0) * unit);
+ polygon.push_back(Vector2(-1, 0) * unit);
+ polygon.push_back(Vector2(-1, 1) * unit);
+ polygon.push_back(Vector2(0, 1) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
+ polygon.push_back(Vector2(0, -3) * unit);
+ polygon.push_back(Vector2(-3, -3) * unit);
+ polygon.push_back(Vector2(-3, 0) * unit);
+ polygon.push_back(Vector2(-1, 0) * unit);
+ polygon.push_back(Vector2(-1, -1) * unit);
+ polygon.push_back(Vector2(0, -1) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
+ polygon.push_back(Vector2(0, -3) * unit);
+ polygon.push_back(Vector2(3, -3) * unit);
+ polygon.push_back(Vector2(3, 0) * unit);
+ polygon.push_back(Vector2(1, 0) * unit);
+ polygon.push_back(Vector2(1, -1) * unit);
+ polygon.push_back(Vector2(0, -1) * unit);
+ break;
+ default:
+ break;
+ }
+ return polygon;
+}
+
+Vector<Point2> TileSet::_get_square_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) {
+ Vector2 unit = Vector2(p_size) / 6.0;
+ Vector<Vector2> polygon;
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
+ polygon.push_back(Vector2(1, -1) * unit);
+ polygon.push_back(Vector2(3, -3) * unit);
+ polygon.push_back(Vector2(3, 3) * unit);
+ polygon.push_back(Vector2(1, 1) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE:
+ polygon.push_back(Vector2(-1, 1) * unit);
+ polygon.push_back(Vector2(-3, 3) * unit);
+ polygon.push_back(Vector2(3, 3) * unit);
+ polygon.push_back(Vector2(1, 1) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
+ polygon.push_back(Vector2(-1, -1) * unit);
+ polygon.push_back(Vector2(-3, -3) * unit);
+ polygon.push_back(Vector2(-3, 3) * unit);
+ polygon.push_back(Vector2(-1, 1) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_SIDE:
+ polygon.push_back(Vector2(-1, -1) * unit);
+ polygon.push_back(Vector2(-3, -3) * unit);
+ polygon.push_back(Vector2(3, -3) * unit);
+ polygon.push_back(Vector2(1, -1) * unit);
+ break;
+ default:
+ break;
+ }
+ return polygon;
+}
+
+Vector<Point2> TileSet::_get_isometric_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) {
+ Vector2 unit = Vector2(p_size) / 6.0;
+ Vector<Vector2> polygon;
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
+ polygon.push_back(Vector2(1, 0) * unit);
+ polygon.push_back(Vector2(2, -1) * unit);
+ polygon.push_back(Vector2(3, 0) * unit);
+ polygon.push_back(Vector2(2, 1) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
+ polygon.push_back(Vector2(0, 1) * unit);
+ polygon.push_back(Vector2(1, 2) * unit);
+ polygon.push_back(Vector2(2, 1) * unit);
+ polygon.push_back(Vector2(1, 0) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER:
+ polygon.push_back(Vector2(0, 1) * unit);
+ polygon.push_back(Vector2(-1, 2) * unit);
+ polygon.push_back(Vector2(0, 3) * unit);
+ polygon.push_back(Vector2(1, 2) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
+ polygon.push_back(Vector2(0, 1) * unit);
+ polygon.push_back(Vector2(-1, 2) * unit);
+ polygon.push_back(Vector2(-2, 1) * unit);
+ polygon.push_back(Vector2(-1, 0) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_LEFT_CORNER:
+ polygon.push_back(Vector2(-1, 0) * unit);
+ polygon.push_back(Vector2(-2, -1) * unit);
+ polygon.push_back(Vector2(-3, 0) * unit);
+ polygon.push_back(Vector2(-2, 1) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
+ polygon.push_back(Vector2(0, -1) * unit);
+ polygon.push_back(Vector2(-1, -2) * unit);
+ polygon.push_back(Vector2(-2, -1) * unit);
+ polygon.push_back(Vector2(-1, 0) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_CORNER:
+ polygon.push_back(Vector2(0, -1) * unit);
+ polygon.push_back(Vector2(-1, -2) * unit);
+ polygon.push_back(Vector2(0, -3) * unit);
+ polygon.push_back(Vector2(1, -2) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
+ polygon.push_back(Vector2(0, -1) * unit);
+ polygon.push_back(Vector2(1, -2) * unit);
+ polygon.push_back(Vector2(2, -1) * unit);
+ polygon.push_back(Vector2(1, 0) * unit);
+ break;
+ default:
+ break;
+ }
+ return polygon;
+}
+
+Vector<Point2> TileSet::_get_isometric_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) {
+ Vector2 unit = Vector2(p_size) / 6.0;
+ Vector<Vector2> polygon;
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
+ polygon.push_back(Vector2(0.5, -0.5) * unit);
+ polygon.push_back(Vector2(1.5, -1.5) * unit);
+ polygon.push_back(Vector2(3, 0) * unit);
+ polygon.push_back(Vector2(1.5, 1.5) * unit);
+ polygon.push_back(Vector2(0.5, 0.5) * unit);
+ polygon.push_back(Vector2(1, 0) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER:
+ polygon.push_back(Vector2(-0.5, 0.5) * unit);
+ polygon.push_back(Vector2(-1.5, 1.5) * unit);
+ polygon.push_back(Vector2(0, 3) * unit);
+ polygon.push_back(Vector2(1.5, 1.5) * unit);
+ polygon.push_back(Vector2(0.5, 0.5) * unit);
+ polygon.push_back(Vector2(0, 1) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_LEFT_CORNER:
+ polygon.push_back(Vector2(-0.5, -0.5) * unit);
+ polygon.push_back(Vector2(-1.5, -1.5) * unit);
+ polygon.push_back(Vector2(-3, 0) * unit);
+ polygon.push_back(Vector2(-1.5, 1.5) * unit);
+ polygon.push_back(Vector2(-0.5, 0.5) * unit);
+ polygon.push_back(Vector2(-1, 0) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_CORNER:
+ polygon.push_back(Vector2(-0.5, -0.5) * unit);
+ polygon.push_back(Vector2(-1.5, -1.5) * unit);
+ polygon.push_back(Vector2(0, -3) * unit);
+ polygon.push_back(Vector2(1.5, -1.5) * unit);
+ polygon.push_back(Vector2(0.5, -0.5) * unit);
+ polygon.push_back(Vector2(0, -1) * unit);
+ break;
+ default:
+ break;
+ }
+ return polygon;
+}
+
+Vector<Point2> TileSet::_get_isometric_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) {
+ Vector2 unit = Vector2(p_size) / 6.0;
+ Vector<Vector2> polygon;
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
+ polygon.push_back(Vector2(1, 0) * unit);
+ polygon.push_back(Vector2(3, 0) * unit);
+ polygon.push_back(Vector2(0, 3) * unit);
+ polygon.push_back(Vector2(0, 1) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
+ polygon.push_back(Vector2(-1, 0) * unit);
+ polygon.push_back(Vector2(-3, 0) * unit);
+ polygon.push_back(Vector2(0, 3) * unit);
+ polygon.push_back(Vector2(0, 1) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
+ polygon.push_back(Vector2(-1, 0) * unit);
+ polygon.push_back(Vector2(-3, 0) * unit);
+ polygon.push_back(Vector2(0, -3) * unit);
+ polygon.push_back(Vector2(0, -1) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
+ polygon.push_back(Vector2(1, 0) * unit);
+ polygon.push_back(Vector2(3, 0) * unit);
+ polygon.push_back(Vector2(0, -3) * unit);
+ polygon.push_back(Vector2(0, -1) * unit);
+ break;
+ default:
+ break;
+ }
+ return polygon;
+}
+
+Vector<Point2> TileSet::_get_half_offset_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) {
+ Vector<Vector2> point_list;
+ point_list.push_back(Vector2(3, (3.0 * (1.0 - p_overlap * 2.0)) / 2.0));
+ point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0)));
+ point_list.push_back(Vector2(2, 3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0)));
+ point_list.push_back(Vector2(1, 3.0 - p_overlap * 2.0));
+ point_list.push_back(Vector2(0, 3));
+ point_list.push_back(Vector2(-1, 3.0 - p_overlap * 2.0));
+ point_list.push_back(Vector2(-2, 3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0)));
+ point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0)));
+ point_list.push_back(Vector2(-3, (3.0 * (1.0 - p_overlap * 2.0)) / 2.0));
+ point_list.push_back(Vector2(-3, -(3.0 * (1.0 - p_overlap * 2.0)) / 2.0));
+ point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0)));
+ point_list.push_back(Vector2(-2, -3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0)));
+ point_list.push_back(Vector2(-1, -(3.0 - p_overlap * 2.0)));
+ point_list.push_back(Vector2(0, -3));
+ point_list.push_back(Vector2(1, -(3.0 - p_overlap * 2.0)));
+ point_list.push_back(Vector2(2, -3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0)));
+ point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0)));
+ point_list.push_back(Vector2(3, -(3.0 * (1.0 - p_overlap * 2.0)) / 2.0));
+
+ Vector2 unit = Vector2(p_size) / 6.0;
+ for (int i = 0; i < point_list.size(); i++) {
+ point_list.write[i] = point_list[i] * unit;
+ }
+
+ Vector<Vector2> polygon;
+ if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
+ polygon.push_back(point_list[17]);
+ polygon.push_back(point_list[0]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
+ polygon.push_back(point_list[0]);
+ polygon.push_back(point_list[1]);
+ polygon.push_back(point_list[2]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
+ polygon.push_back(point_list[2]);
+ polygon.push_back(point_list[3]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER:
+ polygon.push_back(point_list[3]);
+ polygon.push_back(point_list[4]);
+ polygon.push_back(point_list[5]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
+ polygon.push_back(point_list[5]);
+ polygon.push_back(point_list[6]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
+ polygon.push_back(point_list[6]);
+ polygon.push_back(point_list[7]);
+ polygon.push_back(point_list[8]);
+ break;
+ case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
+ polygon.push_back(point_list[8]);
+ polygon.push_back(point_list[9]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
+ polygon.push_back(point_list[9]);
+ polygon.push_back(point_list[10]);
+ polygon.push_back(point_list[11]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
+ polygon.push_back(point_list[11]);
+ polygon.push_back(point_list[12]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_CORNER:
+ polygon.push_back(point_list[12]);
+ polygon.push_back(point_list[13]);
+ polygon.push_back(point_list[14]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
+ polygon.push_back(point_list[14]);
+ polygon.push_back(point_list[15]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
+ polygon.push_back(point_list[15]);
+ polygon.push_back(point_list[16]);
+ polygon.push_back(point_list[17]);
+ break;
+ default:
+ break;
+ }
} else {
- uint32_t picked_value = Math::rand() % priority_sum;
- uint32_t upper_bound;
- uint32_t lower_bound = 0;
- Vector2 result = coords.front()->get();
- List<Vector2>::Element *coords_E = coords.front();
- List<uint32_t>::Element *priorities_E = priorities.front();
- while (priorities_E) {
- upper_bound = lower_bound + priorities_E->get();
- if (lower_bound <= picked_value && picked_value < upper_bound) {
- result = coords_E->get();
+ if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) {
+ for (int i = 0; i < point_list.size(); i++) {
+ point_list.write[i] = Vector2(point_list[i].y, point_list[i].x);
+ }
+ }
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
+ polygon.push_back(point_list[3]);
+ polygon.push_back(point_list[4]);
+ polygon.push_back(point_list[5]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
+ polygon.push_back(point_list[2]);
+ polygon.push_back(point_list[3]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
+ polygon.push_back(point_list[0]);
+ polygon.push_back(point_list[1]);
+ polygon.push_back(point_list[2]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE:
+ polygon.push_back(point_list[17]);
+ polygon.push_back(point_list[0]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
+ polygon.push_back(point_list[15]);
+ polygon.push_back(point_list[16]);
+ polygon.push_back(point_list[17]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
+ polygon.push_back(point_list[14]);
+ polygon.push_back(point_list[15]);
+ break;
+ case TileSet::CELL_NEIGHBOR_LEFT_CORNER:
+ polygon.push_back(point_list[12]);
+ polygon.push_back(point_list[13]);
+ polygon.push_back(point_list[14]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
+ polygon.push_back(point_list[11]);
+ polygon.push_back(point_list[12]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
+ polygon.push_back(point_list[9]);
+ polygon.push_back(point_list[10]);
+ polygon.push_back(point_list[11]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_SIDE:
+ polygon.push_back(point_list[8]);
+ polygon.push_back(point_list[9]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
+ polygon.push_back(point_list[6]);
+ polygon.push_back(point_list[7]);
+ polygon.push_back(point_list[8]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
+ polygon.push_back(point_list[5]);
+ polygon.push_back(point_list[6]);
+ break;
+ default:
+ break;
+ }
+ }
+
+ int half_polygon_size = polygon.size();
+ for (int i = 0; i < half_polygon_size; i++) {
+ polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0);
+ }
+
+ return polygon;
+}
+
+Vector<Point2> TileSet::_get_half_offset_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) {
+ Vector<Vector2> point_list;
+ point_list.push_back(Vector2(3, 0));
+ point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0)));
+ point_list.push_back(Vector2(1.5, (3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0));
+ point_list.push_back(Vector2(0, 3));
+ point_list.push_back(Vector2(-1.5, (3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0));
+ point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0)));
+ point_list.push_back(Vector2(-3, 0));
+ point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0)));
+ point_list.push_back(Vector2(-1.5, -(3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0));
+ point_list.push_back(Vector2(0, -3));
+ point_list.push_back(Vector2(1.5, -(3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0));
+ point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0)));
+
+ Vector2 unit = Vector2(p_size) / 6.0;
+ for (int i = 0; i < point_list.size(); i++) {
+ point_list.write[i] = point_list[i] * unit;
+ }
+
+ Vector<Vector2> polygon;
+ if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
+ polygon.push_back(point_list[0]);
+ polygon.push_back(point_list[1]);
+ polygon.push_back(point_list[2]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER:
+ polygon.push_back(point_list[2]);
+ polygon.push_back(point_list[3]);
+ polygon.push_back(point_list[4]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
+ polygon.push_back(point_list[4]);
+ polygon.push_back(point_list[5]);
+ polygon.push_back(point_list[6]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
+ polygon.push_back(point_list[6]);
+ polygon.push_back(point_list[7]);
+ polygon.push_back(point_list[8]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_CORNER:
+ polygon.push_back(point_list[8]);
+ polygon.push_back(point_list[9]);
+ polygon.push_back(point_list[10]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
+ polygon.push_back(point_list[10]);
+ polygon.push_back(point_list[11]);
+ polygon.push_back(point_list[0]);
break;
+ default:
+ break;
+ }
+ } else {
+ if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) {
+ for (int i = 0; i < point_list.size(); i++) {
+ point_list.write[i] = Vector2(point_list[i].y, point_list[i].x);
}
- lower_bound = upper_bound;
- priorities_E = priorities_E->next();
- coords_E = coords_E->next();
}
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
+ polygon.push_back(point_list[2]);
+ polygon.push_back(point_list[3]);
+ polygon.push_back(point_list[4]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
+ polygon.push_back(point_list[0]);
+ polygon.push_back(point_list[1]);
+ polygon.push_back(point_list[2]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
+ polygon.push_back(point_list[10]);
+ polygon.push_back(point_list[11]);
+ polygon.push_back(point_list[0]);
+ break;
+ case TileSet::CELL_NEIGHBOR_LEFT_CORNER:
+ polygon.push_back(point_list[8]);
+ polygon.push_back(point_list[9]);
+ polygon.push_back(point_list[10]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
+ polygon.push_back(point_list[6]);
+ polygon.push_back(point_list[7]);
+ polygon.push_back(point_list[8]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
+ polygon.push_back(point_list[4]);
+ polygon.push_back(point_list[5]);
+ polygon.push_back(point_list[6]);
+ break;
+ default:
+ break;
+ }
+ }
- return result;
+ int half_polygon_size = polygon.size();
+ for (int i = 0; i < half_polygon_size; i++) {
+ polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0);
}
+
+ return polygon;
}
-Vector2 TileSet::atlastile_get_subtile_by_priority(int p_id, const Node *p_tilemap_node, const Vector2 &p_tile_location) {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Vector2());
- //First try to forward selection to script
- if (get_script_instance() != nullptr) {
- if (get_script_instance()->has_method("_forward_atlas_subtile_selection")) {
- Variant ret = get_script_instance()->call("_forward_atlas_subtile_selection", p_id, p_tilemap_node, p_tile_location);
- if (ret.get_type() == Variant::VECTOR2) {
- return ret;
+Vector<Point2> TileSet::_get_half_offset_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) {
+ Vector<Vector2> point_list;
+ point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0)));
+ point_list.push_back(Vector2(0, 3));
+ point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0)));
+ point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0)));
+ point_list.push_back(Vector2(0, -3));
+ point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0)));
+
+ Vector2 unit = Vector2(p_size) / 6.0;
+ for (int i = 0; i < point_list.size(); i++) {
+ point_list.write[i] = point_list[i] * unit;
+ }
+
+ Vector<Vector2> polygon;
+ if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
+ polygon.push_back(point_list[5]);
+ polygon.push_back(point_list[0]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
+ polygon.push_back(point_list[0]);
+ polygon.push_back(point_list[1]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
+ polygon.push_back(point_list[1]);
+ polygon.push_back(point_list[2]);
+ break;
+ case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
+ polygon.push_back(point_list[2]);
+ polygon.push_back(point_list[3]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
+ polygon.push_back(point_list[3]);
+ polygon.push_back(point_list[4]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
+ polygon.push_back(point_list[4]);
+ polygon.push_back(point_list[5]);
+ break;
+ default:
+ break;
+ }
+ } else {
+ if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) {
+ for (int i = 0; i < point_list.size(); i++) {
+ point_list.write[i] = Vector2(point_list[i].y, point_list[i].x);
}
}
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
+ polygon.push_back(point_list[0]);
+ polygon.push_back(point_list[1]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE:
+ polygon.push_back(point_list[5]);
+ polygon.push_back(point_list[0]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
+ polygon.push_back(point_list[4]);
+ polygon.push_back(point_list[5]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
+ polygon.push_back(point_list[3]);
+ polygon.push_back(point_list[4]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_SIDE:
+ polygon.push_back(point_list[2]);
+ polygon.push_back(point_list[3]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
+ polygon.push_back(point_list[1]);
+ polygon.push_back(point_list[2]);
+ break;
+ default:
+ break;
+ }
+ }
+
+ int half_polygon_size = polygon.size();
+ for (int i = 0; i < half_polygon_size; i++) {
+ polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0);
}
- Vector2 coord = tile_get_region(p_id).size / autotile_get_size(p_id);
+ return polygon;
+}
+
+void TileSet::reset_state() {
+ occlusion_layers.clear();
+ physics_layers.clear();
+ custom_data_layers.clear();
+}
+
+const Vector2i TileSetSource::INVALID_ATLAS_COORDS = Vector2i(-1, -1);
+const int TileSetSource::INVALID_TILE_ALTERNATIVE = -1;
+
+#ifndef DISABLE_DEPRECATED
+void TileSet::_compatibility_conversion() {
+ for (Map<int, CompatibilityTileData *>::Element *E = compatibility_data.front(); E; E = E->next()) {
+ CompatibilityTileData *ctd = E->value();
+
+ // Add the texture
+ TileSetAtlasSource *atlas_source = memnew(TileSetAtlasSource);
+ int source_id = add_source(Ref<TileSetSource>(atlas_source));
+
+ atlas_source->set_texture(ctd->texture);
+
+ // Handle each tile as a new source. Not optimal but at least it should stay compatible.
+ switch (ctd->tile_mode) {
+ case COMPATIBILITY_TILE_MODE_SINGLE_TILE: {
+ atlas_source->set_margins(ctd->region.get_position());
+ atlas_source->set_texture_region_size(ctd->region.get_size());
+
+ Vector2i coords;
+ for (int flags = 0; flags < 8; flags++) {
+ bool flip_h = flags & 1;
+ bool flip_v = flags & 2;
+ bool transpose = flags & 4;
+
+ int alternative_tile = 0;
+ if (!atlas_source->has_tile(coords)) {
+ atlas_source->create_tile(coords);
+ } else {
+ alternative_tile = atlas_source->create_alternative_tile(coords);
+ }
+
+ // Add to the mapping.
+ Array key_array;
+ key_array.push_back(flip_h);
+ key_array.push_back(flip_v);
+ key_array.push_back(transpose);
+
+ Array value_array;
+ value_array.push_back(source_id);
+ value_array.push_back(coords);
+ value_array.push_back(alternative_tile);
+
+ if (!compatibility_tilemap_mapping.has(E->key())) {
+ compatibility_tilemap_mapping[E->key()] = Map<Array, Array>();
+ }
+ compatibility_tilemap_mapping[E->key()][key_array] = value_array;
+ compatibility_tilemap_mapping_tile_modes[E->key()] = COMPATIBILITY_TILE_MODE_SINGLE_TILE;
+
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(coords, alternative_tile));
+
+ tile_data->set_flip_h(flip_h);
+ tile_data->set_flip_v(flip_v);
+ tile_data->set_transpose(transpose);
+ tile_data->tile_set_material(ctd->material);
+ tile_data->set_modulate(ctd->modulate);
+ tile_data->set_z_index(ctd->z_index);
+
+ if (ctd->occluder.is_valid()) {
+ if (get_occlusion_layers_count() < 1) {
+ set_occlusion_layers_count(1);
+ }
+ tile_data->set_occluder(0, ctd->occluder);
+ }
+ if (ctd->navigation.is_valid()) {
+ if (get_navigation_layers_count() < 1) {
+ set_navigation_layers_count(1);
+ }
+ tile_data->set_navigation_polygon(0, ctd->autotile_navpoly_map[coords]);
+ }
+
+ tile_data->set_z_index(ctd->z_index);
+
+ // Add the shapes.
+ if (ctd->shapes.size() > 0) {
+ if (get_physics_layers_count() < 1) {
+ set_physics_layers_count(1);
+ }
+ }
+ for (int k = 0; k < ctd->shapes.size(); k++) {
+ CompatibilityShapeData csd = ctd->shapes[k];
+ if (csd.autotile_coords == coords) {
+ Ref<ConvexPolygonShape2D> convex_shape = csd.shape; // Only ConvexPolygonShape2D are supported, which is the default type used by the 3.x editor
+ if (convex_shape.is_valid()) {
+ Vector<Vector2> polygon = convex_shape->get_points();
+ for (int point_index = 0; point_index < polygon.size(); point_index++) {
+ polygon.write[point_index] = csd.transform.xform(polygon[point_index]);
+ }
+ tile_data->set_collision_polygons_count(0, tile_data->get_collision_polygons_count(0) + 1);
+ int index = tile_data->get_collision_polygons_count(0) - 1;
+ tile_data->set_collision_polygon_one_way(0, index, csd.one_way);
+ tile_data->set_collision_polygon_one_way_margin(0, index, csd.one_way_margin);
+ tile_data->set_collision_polygon_points(0, index, polygon);
+ }
+ }
+ }
+ }
+ } break;
+ case COMPATIBILITY_TILE_MODE_AUTO_TILE: {
+ // Not supported. It would need manual conversion.
+ } break;
+ case COMPATIBILITY_TILE_MODE_ATLAS_TILE: {
+ atlas_source->set_margins(ctd->region.get_position());
+ atlas_source->set_separation(Vector2i(ctd->autotile_spacing, ctd->autotile_spacing));
+ atlas_source->set_texture_region_size(ctd->autotile_tile_size);
+
+ Size2i atlas_size = ctd->region.get_size() / (ctd->autotile_tile_size + atlas_source->get_separation());
+ for (int i = 0; i < atlas_size.x; i++) {
+ for (int j = 0; j < atlas_size.y; j++) {
+ Vector2i coords = Vector2i(i, j);
+
+ for (int flags = 0; flags < 8; flags++) {
+ bool flip_h = flags & 1;
+ bool flip_v = flags & 2;
+ bool transpose = flags & 4;
+
+ int alternative_tile = 0;
+ if (!atlas_source->has_tile(coords)) {
+ atlas_source->create_tile(coords);
+ } else {
+ alternative_tile = atlas_source->create_alternative_tile(coords);
+ }
+
+ // Add to the mapping.
+ Array key_array;
+ key_array.push_back(coords);
+ key_array.push_back(flip_h);
+ key_array.push_back(flip_v);
+ key_array.push_back(transpose);
+
+ Array value_array;
+ value_array.push_back(source_id);
+ value_array.push_back(coords);
+ value_array.push_back(alternative_tile);
+
+ if (!compatibility_tilemap_mapping.has(E->key())) {
+ compatibility_tilemap_mapping[E->key()] = Map<Array, Array>();
+ }
+ compatibility_tilemap_mapping[E->key()][key_array] = value_array;
+ compatibility_tilemap_mapping_tile_modes[E->key()] = COMPATIBILITY_TILE_MODE_ATLAS_TILE;
+
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(coords, alternative_tile));
+
+ tile_data->set_flip_h(flip_h);
+ tile_data->set_flip_v(flip_v);
+ tile_data->set_transpose(transpose);
+ tile_data->tile_set_material(ctd->material);
+ tile_data->set_modulate(ctd->modulate);
+ tile_data->set_z_index(ctd->z_index);
+ if (ctd->autotile_occluder_map.has(coords)) {
+ if (get_occlusion_layers_count() < 1) {
+ set_occlusion_layers_count(1);
+ }
+ tile_data->set_occluder(0, ctd->autotile_occluder_map[coords]);
+ }
+ if (ctd->autotile_navpoly_map.has(coords)) {
+ if (get_navigation_layers_count() < 1) {
+ set_navigation_layers_count(1);
+ }
+ tile_data->set_navigation_polygon(0, ctd->autotile_navpoly_map[coords]);
+ }
+ if (ctd->autotile_priority_map.has(coords)) {
+ tile_data->set_probability(ctd->autotile_priority_map[coords]);
+ }
+ if (ctd->autotile_z_index_map.has(coords)) {
+ tile_data->set_z_index(ctd->autotile_z_index_map[coords]);
+ }
+
+ // Add the shapes.
+ if (ctd->shapes.size() > 0) {
+ if (get_physics_layers_count() < 1) {
+ set_physics_layers_count(1);
+ }
+ }
+ for (int k = 0; k < ctd->shapes.size(); k++) {
+ CompatibilityShapeData csd = ctd->shapes[k];
+ if (csd.autotile_coords == coords) {
+ Ref<ConvexPolygonShape2D> convex_shape = csd.shape; // Only ConvexPolygonShape2D are supported, which is the default type used by the 3.x editor
+ if (convex_shape.is_valid()) {
+ Vector<Vector2> polygon = convex_shape->get_points();
+ for (int point_index = 0; point_index < polygon.size(); point_index++) {
+ polygon.write[point_index] = csd.transform.xform(polygon[point_index]);
+ }
+ tile_data->set_collision_polygons_count(0, tile_data->get_collision_polygons_count(0) + 1);
+ int index = tile_data->get_collision_polygons_count(0) - 1;
+ tile_data->set_collision_polygon_one_way(0, index, csd.one_way);
+ tile_data->set_collision_polygon_one_way_margin(0, index, csd.one_way_margin);
+ tile_data->set_collision_polygon_points(0, index, polygon);
+ }
+ }
+ }
+
+ // -- TODO: handle --
+ // Those are offset for the whole atlas, they are likely useless for the atlases, but might make sense for single tiles.
+ // texture offset
+ // occluder_offset
+ // navigation_offset
+
+ // For terrains, ignored for now?
+ // bitmask_mode
+ // bitmask_flags
+ }
+ }
+ }
+ } break;
+ }
- List<Vector2> coords;
- for (int x = 0; x < coord.x; x++) {
- for (int y = 0; y < coord.y; y++) {
- for (int i = 0; i < autotile_get_subtile_priority(p_id, Vector2(x, y)); i++) {
- coords.push_back(Vector2(x, y));
+ // Offset all shapes
+ for (int k = 0; k < ctd->shapes.size(); k++) {
+ Ref<ConvexPolygonShape2D> convex = ctd->shapes[k].shape;
+ if (convex.is_valid()) {
+ Vector<Vector2> points = convex->get_points();
+ for (int i_point = 0; i_point < points.size(); i_point++) {
+ points.write[i_point] = points[i_point] - get_tile_size() / 2;
+ }
+ convex->set_points(points);
}
}
}
- if (coords.size() == 0) {
- return autotile_get_icon_coordinate(p_id);
+
+ // Reset compatibility data
+ for (Map<int, CompatibilityTileData *>::Element *E = compatibility_data.front(); E; E = E->next()) {
+ memdelete(E->get());
+ }
+ compatibility_data = Map<int, CompatibilityTileData *>();
+}
+
+Array TileSet::compatibility_tilemap_map(int p_tile_id, Vector2i p_coords, bool p_flip_h, bool p_flip_v, bool p_transpose) {
+ Array cannot_convert_array;
+ cannot_convert_array.push_back(TileSet::INVALID_SOURCE);
+ cannot_convert_array.push_back(TileSetAtlasSource::INVALID_ATLAS_COORDS);
+ cannot_convert_array.push_back(TileSetAtlasSource::INVALID_TILE_ALTERNATIVE);
+
+ if (!compatibility_tilemap_mapping.has(p_tile_id)) {
+ return cannot_convert_array;
+ }
+
+ int tile_mode = compatibility_tilemap_mapping_tile_modes[p_tile_id];
+ switch (tile_mode) {
+ case COMPATIBILITY_TILE_MODE_SINGLE_TILE: {
+ Array a;
+ a.push_back(p_flip_h);
+ a.push_back(p_flip_v);
+ a.push_back(p_transpose);
+ return compatibility_tilemap_mapping[p_tile_id][a];
+ }
+ case COMPATIBILITY_TILE_MODE_AUTO_TILE:
+ return cannot_convert_array;
+ break;
+ case COMPATIBILITY_TILE_MODE_ATLAS_TILE: {
+ Array a;
+ a.push_back(p_coords);
+ a.push_back(p_flip_h);
+ a.push_back(p_flip_v);
+ a.push_back(p_transpose);
+ return compatibility_tilemap_mapping[p_tile_id][a];
+ }
+ default:
+ return cannot_convert_array;
+ break;
+ }
+};
+
+#endif // DISABLE_DEPRECATED
+
+bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
+ Vector<String> components = String(p_name).split("/", true, 2);
+
+#ifndef DISABLE_DEPRECATED
+ // TODO: This should be moved to a dedicated conversion system (see #50691)
+ if (components.size() >= 1 && components[0].is_valid_int()) {
+ int id = components[0].to_int();
+
+ // Get or create the compatibility object
+ CompatibilityTileData *ctd;
+ Map<int, CompatibilityTileData *>::Element *E = compatibility_data.find(id);
+ if (!E) {
+ ctd = memnew(CompatibilityTileData);
+ compatibility_data.insert(id, ctd);
+ } else {
+ ctd = E->get();
+ }
+
+ if (components.size() < 2) {
+ return false;
+ }
+
+ String what = components[1];
+
+ if (what == "name") {
+ ctd->name = p_value;
+ } else if (what == "texture") {
+ ctd->texture = p_value;
+ } else if (what == "tex_offset") {
+ ctd->tex_offset = p_value;
+ } else if (what == "material") {
+ ctd->material = p_value;
+ } else if (what == "modulate") {
+ ctd->modulate = p_value;
+ } else if (what == "region") {
+ ctd->region = p_value;
+ } else if (what == "tile_mode") {
+ ctd->tile_mode = p_value;
+ } else if (what.left(9) == "autotile") {
+ what = what.substr(9);
+ if (what == "bitmask_mode") {
+ ctd->autotile_bitmask_mode = p_value;
+ } else if (what == "icon_coordinate") {
+ ctd->autotile_icon_coordinate = p_value;
+ } else if (what == "tile_size") {
+ ctd->autotile_tile_size = p_value;
+ } else if (what == "spacing") {
+ ctd->autotile_spacing = p_value;
+ } else if (what == "bitmask_flags") {
+ if (p_value.is_array()) {
+ Array p = p_value;
+ Vector2i last_coord;
+ while (p.size() > 0) {
+ if (p[0].get_type() == Variant::VECTOR2) {
+ last_coord = p[0];
+ } else if (p[0].get_type() == Variant::INT) {
+ ctd->autotile_bitmask_flags.insert(last_coord, p[0]);
+ }
+ p.pop_front();
+ }
+ }
+ } else if (what == "occluder_map") {
+ Array p = p_value;
+ Vector2 last_coord;
+ while (p.size() > 0) {
+ if (p[0].get_type() == Variant::VECTOR2) {
+ last_coord = p[0];
+ } else if (p[0].get_type() == Variant::OBJECT) {
+ ctd->autotile_occluder_map.insert(last_coord, p[0]);
+ }
+ p.pop_front();
+ }
+ } else if (what == "navpoly_map") {
+ Array p = p_value;
+ Vector2 last_coord;
+ while (p.size() > 0) {
+ if (p[0].get_type() == Variant::VECTOR2) {
+ last_coord = p[0];
+ } else if (p[0].get_type() == Variant::OBJECT) {
+ ctd->autotile_navpoly_map.insert(last_coord, p[0]);
+ }
+ p.pop_front();
+ }
+ } else if (what == "priority_map") {
+ Array p = p_value;
+ Vector3 val;
+ Vector2 v;
+ int priority;
+ while (p.size() > 0) {
+ val = p[0];
+ if (val.z > 1) {
+ v.x = val.x;
+ v.y = val.y;
+ priority = (int)val.z;
+ ctd->autotile_priority_map.insert(v, priority);
+ }
+ p.pop_front();
+ }
+ } else if (what == "z_index_map") {
+ Array p = p_value;
+ Vector3 val;
+ Vector2 v;
+ int z_index;
+ while (p.size() > 0) {
+ val = p[0];
+ if (val.z != 0) {
+ v.x = val.x;
+ v.y = val.y;
+ z_index = (int)val.z;
+ ctd->autotile_z_index_map.insert(v, z_index);
+ }
+ p.pop_front();
+ }
+ }
+
+ } else if (what == "shapes") {
+ Array p = p_value;
+ for (int i = 0; i < p.size(); i++) {
+ CompatibilityShapeData csd;
+ Dictionary d = p[i];
+ for (int j = 0; j < d.size(); j++) {
+ String key = d.get_key_at_index(j);
+ if (key == "autotile_coord") {
+ csd.autotile_coords = d[key];
+ } else if (key == "one_way") {
+ csd.one_way = d[key];
+ } else if (key == "one_way_margin") {
+ csd.one_way_margin = d[key];
+ } else if (key == "shape") {
+ csd.shape = d[key];
+ } else if (key == "shape_transform") {
+ csd.transform = d[key];
+ }
+ }
+ ctd->shapes.push_back(csd);
+ }
+
+ /*
+ // IGNORED FOR NOW, they seem duplicated data compared to the shapes array
+ } else if (what == "shape") {
+ } else if (what == "shape_offset") {
+ } else if (what == "shape_transform") {
+ } else if (what == "shape_one_way") {
+ } else if (what == "shape_one_way_margin") {
+ }
+ // IGNORED FOR NOW, maybe useless ?
+ else if (what == "occluder_offset") {
+ // Not
+ } else if (what == "navigation_offset") {
+ }
+ */
+
+ } else if (what == "z_index") {
+ ctd->z_index = p_value;
+
+ // TODO: remove the conversion from here, it's not where it should be done (see #50691)
+ _compatibility_conversion();
+ } else {
+ return false;
+ }
} else {
- return coords[Math::random(0, (int)coords.size())];
+#endif // DISABLE_DEPRECATED
+
+ // This is now a new property.
+ if (components.size() == 2 && components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) {
+ // Occlusion layers.
+ int index = components[0].trim_prefix("occlusion_layer_").to_int();
+ ERR_FAIL_COND_V(index < 0, false);
+ if (components[1] == "light_mask") {
+ ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
+ if (index >= occlusion_layers.size()) {
+ set_occlusion_layers_count(index + 1);
+ }
+ set_occlusion_layer_light_mask(index, p_value);
+ return true;
+ } else if (components[1] == "sdf_collision") {
+ ERR_FAIL_COND_V(p_value.get_type() != Variant::BOOL, false);
+ if (index >= occlusion_layers.size()) {
+ set_occlusion_layers_count(index + 1);
+ }
+ set_occlusion_layer_sdf_collision(index, p_value);
+ return true;
+ }
+ } else if (components.size() == 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_int()) {
+ // Physics layers.
+ int index = components[0].trim_prefix("physics_layer_").to_int();
+ ERR_FAIL_COND_V(index < 0, false);
+ if (components[1] == "collision_layer") {
+ ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
+ if (index >= physics_layers.size()) {
+ set_physics_layers_count(index + 1);
+ }
+ set_physics_layer_collision_layer(index, p_value);
+ return true;
+ } else if (components[1] == "collision_mask") {
+ ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
+ if (index >= physics_layers.size()) {
+ set_physics_layers_count(index + 1);
+ }
+ set_physics_layer_collision_mask(index, p_value);
+ return true;
+ } else if (components[1] == "physics_material") {
+ Ref<PhysicsMaterial> physics_material = p_value;
+ ERR_FAIL_COND_V(!physics_material.is_valid(), false);
+ if (index >= physics_layers.size()) {
+ set_physics_layers_count(index + 1);
+ }
+ set_physics_layer_physics_material(index, physics_material);
+ return true;
+ }
+ } else if (components.size() >= 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int()) {
+ // Terrains.
+ int terrain_set_index = components[0].trim_prefix("terrain_set_").to_int();
+ ERR_FAIL_COND_V(terrain_set_index < 0, false);
+ if (components[1] == "mode") {
+ ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
+ if (terrain_set_index >= terrain_sets.size()) {
+ set_terrain_sets_count(terrain_set_index + 1);
+ }
+ set_terrain_set_mode(terrain_set_index, TerrainMode(int(p_value)));
+ } else if (components[1] == "terrains_count") {
+ ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
+ if (terrain_set_index >= terrain_sets.size()) {
+ set_terrain_sets_count(terrain_set_index + 1);
+ }
+ set_terrains_count(terrain_set_index, p_value);
+ return true;
+ } else if (components.size() >= 3 && components[1].begins_with("terrain_") && components[1].trim_prefix("terrain_").is_valid_int()) {
+ int terrain_index = components[1].trim_prefix("terrain_").to_int();
+ ERR_FAIL_COND_V(terrain_index < 0, false);
+ if (components[2] == "name") {
+ ERR_FAIL_COND_V(p_value.get_type() != Variant::STRING, false);
+ if (terrain_set_index >= terrain_sets.size()) {
+ set_terrain_sets_count(terrain_set_index + 1);
+ }
+ if (terrain_index >= terrain_sets[terrain_set_index].terrains.size()) {
+ set_terrains_count(terrain_set_index, terrain_index + 1);
+ }
+ set_terrain_name(terrain_set_index, terrain_index, p_value);
+ return true;
+ } else if (components[2] == "color") {
+ ERR_FAIL_COND_V(p_value.get_type() != Variant::COLOR, false);
+ if (terrain_set_index >= terrain_sets.size()) {
+ set_terrain_sets_count(terrain_set_index + 1);
+ }
+ if (terrain_index >= terrain_sets[terrain_set_index].terrains.size()) {
+ set_terrains_count(terrain_set_index, terrain_index + 1);
+ }
+ set_terrain_color(terrain_set_index, terrain_index, p_value);
+ return true;
+ }
+ }
+ } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_int()) {
+ // Navigation layers.
+ int index = components[0].trim_prefix("navigation_layer_").to_int();
+ ERR_FAIL_COND_V(index < 0, false);
+ if (components[1] == "layers") {
+ ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
+ if (index >= navigation_layers.size()) {
+ set_navigation_layers_count(index + 1);
+ }
+ set_navigation_layer_layers(index, p_value);
+ return true;
+ }
+ } else if (components.size() == 2 && components[0].begins_with("custom_data_layer_") && components[0].trim_prefix("custom_data_layer_").is_valid_int()) {
+ // Custom data layers.
+ int index = components[0].trim_prefix("custom_data_layer_").to_int();
+ ERR_FAIL_COND_V(index < 0, false);
+ if (components[1] == "name") {
+ ERR_FAIL_COND_V(p_value.get_type() != Variant::STRING, false);
+ if (index >= custom_data_layers.size()) {
+ set_custom_data_layers_count(index + 1);
+ }
+ set_custom_data_name(index, p_value);
+ return true;
+ } else if (components[1] == "type") {
+ ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
+ if (index >= custom_data_layers.size()) {
+ set_custom_data_layers_count(index + 1);
+ }
+ set_custom_data_type(index, Variant::Type(int(p_value)));
+ return true;
+ }
+ } else if (components.size() == 2 && components[0] == "sources" && components[1].is_valid_int()) {
+ // Create source only if it does not exists.
+ int source_id = components[1].to_int();
+
+ if (!has_source(source_id)) {
+ add_source(p_value, source_id);
+ }
+ return true;
+ } else if (components.size() == 2 && components[0] == "tile_proxies") {
+ ERR_FAIL_COND_V(p_value.get_type() != Variant::ARRAY, false);
+ Array a = p_value;
+ ERR_FAIL_COND_V(a.size() % 2 != 0, false);
+ if (components[1] == "source_level") {
+ for (int i = 0; i < a.size(); i += 2) {
+ set_source_level_tile_proxy(a[i], a[i + 1]);
+ }
+ return true;
+ } else if (components[1] == "coords_level") {
+ for (int i = 0; i < a.size(); i += 2) {
+ Array key = a[i];
+ Array value = a[i + 1];
+ set_coords_level_tile_proxy(key[0], key[1], value[0], value[1]);
+ }
+ return true;
+ } else if (components[1] == "alternative_level") {
+ for (int i = 0; i < a.size(); i += 2) {
+ Array key = a[i];
+ Array value = a[i + 1];
+ set_alternative_level_tile_proxy(key[0], key[1], key[2], value[0], value[1], value[2]);
+ }
+ return true;
+ }
+ return false;
+ }
+
+#ifndef DISABLE_DEPRECATED
}
+#endif // DISABLE_DEPRECATED
+
+ return false;
}
-void TileSet::tile_set_name(int p_id, const String &p_name) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].name = p_name;
- emit_changed();
- _change_notify("name");
+bool TileSet::_get(const StringName &p_name, Variant &r_ret) const {
+ Vector<String> components = String(p_name).split("/", true, 2);
+
+ if (components.size() == 2 && components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) {
+ // Occlusion layers.
+ int index = components[0].trim_prefix("occlusion_layer_").to_int();
+ if (index < 0 || index >= occlusion_layers.size()) {
+ return false;
+ }
+ if (components[1] == "light_mask") {
+ r_ret = get_occlusion_layer_light_mask(index);
+ return true;
+ } else if (components[1] == "sdf_collision") {
+ r_ret = get_occlusion_layer_sdf_collision(index);
+ return true;
+ }
+ } else if (components.size() == 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_int()) {
+ // Physics layers.
+ int index = components[0].trim_prefix("physics_layer_").to_int();
+ if (index < 0 || index >= physics_layers.size()) {
+ return false;
+ }
+ if (components[1] == "collision_layer") {
+ r_ret = get_physics_layer_collision_layer(index);
+ return true;
+ } else if (components[1] == "collision_mask") {
+ r_ret = get_physics_layer_collision_mask(index);
+ return true;
+ } else if (components[1] == "physics_material") {
+ r_ret = get_physics_layer_physics_material(index);
+ return true;
+ }
+ } else if (components.size() >= 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int()) {
+ // Terrains.
+ int terrain_set_index = components[0].trim_prefix("terrain_set_").to_int();
+ if (terrain_set_index < 0 || terrain_set_index >= terrain_sets.size()) {
+ return false;
+ }
+ if (components[1] == "mode") {
+ r_ret = get_terrain_set_mode(terrain_set_index);
+ return true;
+ } else if (components[1] == "terrains_count") {
+ r_ret = get_terrains_count(terrain_set_index);
+ return true;
+ } else if (components.size() >= 3 && components[1].begins_with("terrain_") && components[1].trim_prefix("terrain_").is_valid_int()) {
+ int terrain_index = components[1].trim_prefix("terrain_").to_int();
+ if (terrain_index < 0 || terrain_index >= terrain_sets[terrain_set_index].terrains.size()) {
+ return false;
+ }
+ if (components[2] == "name") {
+ r_ret = get_terrain_name(terrain_set_index, terrain_index);
+ return true;
+ } else if (components[2] == "color") {
+ r_ret = get_terrain_color(terrain_set_index, terrain_index);
+ return true;
+ }
+ }
+ } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_int()) {
+ // navigation layers.
+ int index = components[0].trim_prefix("navigation_layer_").to_int();
+ if (index < 0 || index >= navigation_layers.size()) {
+ return false;
+ }
+ if (components[1] == "layers") {
+ r_ret = get_navigation_layer_layers(index);
+ return true;
+ }
+ } else if (components.size() == 2 && components[0].begins_with("custom_data_layer_") && components[0].trim_prefix("custom_data_layer_").is_valid_int()) {
+ // Custom data layers.
+ int index = components[0].trim_prefix("custom_data_layer_").to_int();
+ if (index < 0 || index >= custom_data_layers.size()) {
+ return false;
+ }
+ if (components[1] == "name") {
+ r_ret = get_custom_data_name(index);
+ return true;
+ } else if (components[1] == "type") {
+ r_ret = get_custom_data_type(index);
+ return true;
+ }
+ } else if (components.size() == 2 && components[0] == "sources" && components[1].is_valid_int()) {
+ // Atlases data.
+ int source_id = components[1].to_int();
+
+ if (has_source(source_id)) {
+ r_ret = get_source(source_id);
+ return true;
+ } else {
+ return false;
+ }
+ } else if (components.size() == 2 && components[0] == "tile_proxies") {
+ if (components[1] == "source_level") {
+ Array a;
+ for (Map<int, int>::Element *E = source_level_proxies.front(); E; E = E->next()) {
+ a.push_back(E->key());
+ a.push_back(E->get());
+ }
+ r_ret = a;
+ return true;
+ } else if (components[1] == "coords_level") {
+ Array a;
+ for (Map<Array, Array>::Element *E = coords_level_proxies.front(); E; E = E->next()) {
+ a.push_back(E->key());
+ a.push_back(E->get());
+ }
+ r_ret = a;
+ return true;
+ } else if (components[1] == "alternative_level") {
+ Array a;
+ for (Map<Array, Array>::Element *E = alternative_level_proxies.front(); E; E = E->next()) {
+ a.push_back(E->key());
+ a.push_back(E->get());
+ }
+ r_ret = a;
+ return true;
+ }
+ return false;
+ }
+
+ return false;
}
-String TileSet::tile_get_name(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), String());
- return tile_map[p_id].name;
+void TileSet::_get_property_list(List<PropertyInfo> *p_list) const {
+ PropertyInfo property_info;
+ // Rendering.
+ p_list->push_back(PropertyInfo(Variant::NIL, "Rendering", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
+ for (int i = 0; i < occlusion_layers.size(); i++) {
+ p_list->push_back(PropertyInfo(Variant::INT, vformat("occlusion_layer_%d/light_mask", i), PROPERTY_HINT_LAYERS_2D_RENDER));
+
+ // occlusion_layer_%d/sdf_collision
+ property_info = PropertyInfo(Variant::BOOL, vformat("occlusion_layer_%d/sdf_collision", i));
+ if (occlusion_layers[i].sdf_collision == false) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+ }
+
+ // Physics.
+ p_list->push_back(PropertyInfo(Variant::NIL, "Physics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
+ for (int i = 0; i < physics_layers.size(); i++) {
+ p_list->push_back(PropertyInfo(Variant::INT, vformat("physics_layer_%d/collision_layer", i), PROPERTY_HINT_LAYERS_2D_PHYSICS));
+
+ // physics_layer_%d/collision_mask
+ property_info = PropertyInfo(Variant::INT, vformat("physics_layer_%d/collision_mask", i), PROPERTY_HINT_LAYERS_2D_PHYSICS);
+ if (physics_layers[i].collision_mask == 1) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+
+ // physics_layer_%d/physics_material
+ property_info = PropertyInfo(Variant::OBJECT, vformat("physics_layer_%d/physics_material", i), PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial");
+ if (!physics_layers[i].physics_material.is_valid()) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+ }
+
+ // Terrains.
+ p_list->push_back(PropertyInfo(Variant::NIL, "Terrains", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
+ for (int terrain_set_index = 0; terrain_set_index < terrain_sets.size(); terrain_set_index++) {
+ p_list->push_back(PropertyInfo(Variant::INT, vformat("terrain_set_%d/mode", terrain_set_index), PROPERTY_HINT_ENUM, "Match corners and sides,Match corners,Match sides"));
+ p_list->push_back(PropertyInfo(Variant::INT, vformat("terrain_set_%d/terrains_count", terrain_set_index), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
+ for (int terrain_index = 0; terrain_index < terrain_sets[terrain_set_index].terrains.size(); terrain_index++) {
+ p_list->push_back(PropertyInfo(Variant::STRING, vformat("terrain_set_%d/terrain_%d/name", terrain_set_index, terrain_index)));
+ p_list->push_back(PropertyInfo(Variant::COLOR, vformat("terrain_set_%d/terrain_%d/color", terrain_set_index, terrain_index)));
+ }
+ }
+
+ // Navigation.
+ p_list->push_back(PropertyInfo(Variant::NIL, "Navigation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
+ for (int i = 0; i < navigation_layers.size(); i++) {
+ p_list->push_back(PropertyInfo(Variant::INT, vformat("navigation_layer_%d/layers", i), PROPERTY_HINT_LAYERS_2D_NAVIGATION));
+ }
+
+ // Custom data.
+ String argt = "Any";
+ for (int i = 1; i < Variant::VARIANT_MAX; i++) {
+ argt += "," + Variant::get_type_name(Variant::Type(i));
+ }
+ p_list->push_back(PropertyInfo(Variant::NIL, "Custom data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
+ for (int i = 0; i < custom_data_layers.size(); i++) {
+ p_list->push_back(PropertyInfo(Variant::STRING, vformat("custom_data_layer_%d/name", i)));
+ p_list->push_back(PropertyInfo(Variant::INT, vformat("custom_data_layer_%d/type", i), PROPERTY_HINT_ENUM, argt));
+ }
+
+ // Sources.
+ // Note: sources have to be listed in at the end as some TileData rely on the TileSet properties being initialized first.
+ for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
+ p_list->push_back(PropertyInfo(Variant::INT, vformat("sources/%d", E_source->key()), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ }
+
+ // Tile Proxies.
+ // Note: proxies need to be set after sources are set.
+ p_list->push_back(PropertyInfo(Variant::NIL, "Tile Proxies", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
+ p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/source_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/coords_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/alternative_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
}
-void TileSet::tile_clear_shapes(int p_id) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].shapes_data.clear();
+void TileSet::_bind_methods() {
+ // Sources management.
+ ClassDB::bind_method(D_METHOD("get_next_source_id"), &TileSet::get_next_source_id);
+ ClassDB::bind_method(D_METHOD("add_source", "atlas_source_id_override"), &TileSet::add_source, DEFVAL(TileSet::INVALID_SOURCE));
+ ClassDB::bind_method(D_METHOD("remove_source", "source_id"), &TileSet::remove_source);
+ ClassDB::bind_method(D_METHOD("set_source_id", "source_id"), &TileSet::set_source_id);
+ ClassDB::bind_method(D_METHOD("get_source_count"), &TileSet::get_source_count);
+ ClassDB::bind_method(D_METHOD("get_source_id", "index"), &TileSet::get_source_id);
+ ClassDB::bind_method(D_METHOD("has_source", "index"), &TileSet::has_source);
+ ClassDB::bind_method(D_METHOD("get_source", "index"), &TileSet::get_source);
+
+ // Shape and layout.
+ ClassDB::bind_method(D_METHOD("set_tile_shape", "shape"), &TileSet::set_tile_shape);
+ ClassDB::bind_method(D_METHOD("get_tile_shape"), &TileSet::get_tile_shape);
+ ClassDB::bind_method(D_METHOD("set_tile_layout", "layout"), &TileSet::set_tile_layout);
+ ClassDB::bind_method(D_METHOD("get_tile_layout"), &TileSet::get_tile_layout);
+ ClassDB::bind_method(D_METHOD("set_tile_offset_axis", "alignment"), &TileSet::set_tile_offset_axis);
+ ClassDB::bind_method(D_METHOD("get_tile_offset_axis"), &TileSet::get_tile_offset_axis);
+ ClassDB::bind_method(D_METHOD("set_tile_size", "size"), &TileSet::set_tile_size);
+ ClassDB::bind_method(D_METHOD("get_tile_size"), &TileSet::get_tile_size);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "tile_shape", PROPERTY_HINT_ENUM, "Square,Isometric,Half-Offset Square,Hexagon"), "set_tile_shape", "get_tile_shape");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "tile_layout", PROPERTY_HINT_ENUM, "Stacked,Stacked Offset,Stairs Right,Stairs Down,Diamond Right,Diamond Down"), "set_tile_layout", "get_tile_layout");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "tile_offset_axis", PROPERTY_HINT_ENUM, "Horizontal Offset,Vertical Offset"), "set_tile_offset_axis", "get_tile_offset_axis");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "tile_size"), "set_tile_size", "get_tile_size");
+
+ // Rendering.
+ ClassDB::bind_method(D_METHOD("set_uv_clipping", "uv_clipping"), &TileSet::set_uv_clipping);
+ ClassDB::bind_method(D_METHOD("is_uv_clipping"), &TileSet::is_uv_clipping);
+
+ ClassDB::bind_method(D_METHOD("set_occlusion_layers_count", "occlusion_layers_count"), &TileSet::set_occlusion_layers_count);
+ ClassDB::bind_method(D_METHOD("get_occlusion_layers_count"), &TileSet::get_occlusion_layers_count);
+ ClassDB::bind_method(D_METHOD("set_occlusion_layer_light_mask", "layer_index", "light_mask"), &TileSet::set_occlusion_layer_light_mask);
+ ClassDB::bind_method(D_METHOD("get_occlusion_layer_light_mask"), &TileSet::get_occlusion_layer_light_mask);
+ ClassDB::bind_method(D_METHOD("set_occlusion_layer_sdf_collision", "layer_index", "sdf_collision"), &TileSet::set_occlusion_layer_sdf_collision);
+ ClassDB::bind_method(D_METHOD("get_occlusion_layer_sdf_collision"), &TileSet::get_occlusion_layer_sdf_collision);
+
+ // Physics
+ ClassDB::bind_method(D_METHOD("set_physics_layers_count", "physics_layers_count"), &TileSet::set_physics_layers_count);
+ ClassDB::bind_method(D_METHOD("get_physics_layers_count"), &TileSet::get_physics_layers_count);
+ ClassDB::bind_method(D_METHOD("set_physics_layer_collision_layer", "layer_index", "layer"), &TileSet::set_physics_layer_collision_layer);
+ ClassDB::bind_method(D_METHOD("get_physics_layer_collision_layer", "layer_index"), &TileSet::get_physics_layer_collision_layer);
+ ClassDB::bind_method(D_METHOD("set_physics_layer_collision_mask", "layer_index", "mask"), &TileSet::set_physics_layer_collision_mask);
+ ClassDB::bind_method(D_METHOD("get_physics_layer_collision_mask", "layer_index"), &TileSet::get_physics_layer_collision_mask);
+ ClassDB::bind_method(D_METHOD("set_physics_layer_physics_material", "layer_index", "physics_material"), &TileSet::set_physics_layer_physics_material);
+ ClassDB::bind_method(D_METHOD("get_physics_layer_physics_material", "layer_index"), &TileSet::get_physics_layer_physics_material);
+
+ // Terrains
+ ClassDB::bind_method(D_METHOD("set_terrain_sets_count", "terrain_sets_count"), &TileSet::set_terrain_sets_count);
+ ClassDB::bind_method(D_METHOD("get_terrain_sets_count"), &TileSet::get_terrain_sets_count);
+ ClassDB::bind_method(D_METHOD("set_terrain_set_mode", "terrain_set", "mode"), &TileSet::set_terrain_set_mode);
+ ClassDB::bind_method(D_METHOD("get_terrain_set_mode", "terrain_set"), &TileSet::get_terrain_set_mode);
+
+ ClassDB::bind_method(D_METHOD("set_terrains_count", "terrain_set", "terrains_count"), &TileSet::set_terrains_count);
+ ClassDB::bind_method(D_METHOD("get_terrains_count", "terrain_set"), &TileSet::get_terrains_count);
+ ClassDB::bind_method(D_METHOD("set_terrain_name", "terrain_set", "terrain_index", "name"), &TileSet::set_terrain_name);
+ ClassDB::bind_method(D_METHOD("get_terrain_name", "terrain_set", "terrain_index"), &TileSet::get_terrain_name);
+ ClassDB::bind_method(D_METHOD("set_terrain_color", "terrain_set", "terrain_index", "color"), &TileSet::set_terrain_color);
+ ClassDB::bind_method(D_METHOD("get_terrain_color", "terrain_set", "terrain_index"), &TileSet::get_terrain_color);
+
+ // Navigation
+ ClassDB::bind_method(D_METHOD("set_navigation_layers_count", "navigation_layers_count"), &TileSet::set_navigation_layers_count);
+ ClassDB::bind_method(D_METHOD("get_navigation_layers_count"), &TileSet::get_navigation_layers_count);
+ ClassDB::bind_method(D_METHOD("set_navigation_layer_layers", "layer_index", "layers"), &TileSet::set_navigation_layer_layers);
+ ClassDB::bind_method(D_METHOD("get_navigation_layer_layers", "layer_index"), &TileSet::get_navigation_layer_layers);
+
+ // Custom data
+ ClassDB::bind_method(D_METHOD("set_custom_data_layers_count", "custom_data_layers_count"), &TileSet::set_custom_data_layers_count);
+ ClassDB::bind_method(D_METHOD("get_custom_data_layers_count"), &TileSet::get_custom_data_layers_count);
+
+ // Tile proxies
+ ClassDB::bind_method(D_METHOD("set_source_level_tile_proxy", "source_from", "source_to"), &TileSet::set_source_level_tile_proxy);
+ ClassDB::bind_method(D_METHOD("get_source_level_tile_proxy", "source_from"), &TileSet::get_source_level_tile_proxy);
+ ClassDB::bind_method(D_METHOD("has_source_level_tile_proxy", "source_from"), &TileSet::has_source_level_tile_proxy);
+ ClassDB::bind_method(D_METHOD("remove_source_level_tile_proxy", "source_from"), &TileSet::remove_source_level_tile_proxy);
+
+ ClassDB::bind_method(D_METHOD("set_coords_level_tile_proxy", "p_source_from", "coords_from", "source_to", "coords_to"), &TileSet::set_coords_level_tile_proxy);
+ ClassDB::bind_method(D_METHOD("get_coords_level_tile_proxy", "source_from", "coords_from"), &TileSet::get_coords_level_tile_proxy);
+ ClassDB::bind_method(D_METHOD("has_coords_level_tile_proxy", "source_from", "coords_from"), &TileSet::has_coords_level_tile_proxy);
+ ClassDB::bind_method(D_METHOD("remove_coords_level_tile_proxy", "source_from", "coords_from"), &TileSet::remove_coords_level_tile_proxy);
+
+ ClassDB::bind_method(D_METHOD("set_alternative_level_tile_proxy", "source_from", "coords_from", "alternative_from", "source_to", "coords_to", "alternative_to"), &TileSet::set_alternative_level_tile_proxy);
+ ClassDB::bind_method(D_METHOD("get_alternative_level_tile_proxy", "source_from", "coords_from", "alternative_from"), &TileSet::get_alternative_level_tile_proxy);
+ ClassDB::bind_method(D_METHOD("has_alternative_level_tile_proxy", "source_from", "coords_from", "alternative_from"), &TileSet::has_alternative_level_tile_proxy);
+ ClassDB::bind_method(D_METHOD("remove_alternative_level_tile_proxy", "source_from", "coords_from", "alternative_from"), &TileSet::remove_alternative_level_tile_proxy);
+
+ ClassDB::bind_method(D_METHOD("map_tile_proxy", "source_from", "coords_from", "alternative_from"), &TileSet::map_tile_proxy);
+
+ ClassDB::bind_method(D_METHOD("cleanup_invalid_tile_proxies"), &TileSet::cleanup_invalid_tile_proxies);
+ ClassDB::bind_method(D_METHOD("clear_tile_proxies"), &TileSet::clear_tile_proxies);
+
+ ADD_GROUP("Rendering", "");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uv_clipping"), "set_uv_clipping", "is_uv_clipping");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "occlusion_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_occlusion_layers_count", "get_occlusion_layers_count");
+
+ ADD_GROUP("Physics", "");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "physics_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_physics_layers_count", "get_physics_layers_count");
+
+ ADD_GROUP("Terrains", "");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "terrains_sets_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_terrain_sets_count", "get_terrain_sets_count");
+
+ ADD_GROUP("Navigation", "");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_navigation_layers_count", "get_navigation_layers_count");
+
+ ADD_GROUP("Custom data", "");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "custom_data_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_custom_data_layers_count", "get_custom_data_layers_count");
+
+ // -- Enum binding --
+ BIND_ENUM_CONSTANT(TILE_SHAPE_SQUARE);
+ BIND_ENUM_CONSTANT(TILE_SHAPE_ISOMETRIC);
+ BIND_ENUM_CONSTANT(TILE_SHAPE_HALF_OFFSET_SQUARE);
+ BIND_ENUM_CONSTANT(TILE_SHAPE_HEXAGON);
+
+ BIND_ENUM_CONSTANT(TILE_LAYOUT_STACKED);
+ BIND_ENUM_CONSTANT(TILE_LAYOUT_STACKED_OFFSET);
+ BIND_ENUM_CONSTANT(TILE_LAYOUT_STAIRS_RIGHT);
+ BIND_ENUM_CONSTANT(TILE_LAYOUT_STAIRS_DOWN);
+ BIND_ENUM_CONSTANT(TILE_LAYOUT_DIAMOND_RIGHT);
+ BIND_ENUM_CONSTANT(TILE_LAYOUT_DIAMOND_DOWN);
+
+ BIND_ENUM_CONSTANT(TILE_OFFSET_AXIS_HORIZONTAL);
+ BIND_ENUM_CONSTANT(TILE_OFFSET_AXIS_VERTICAL);
+
+ BIND_ENUM_CONSTANT(CELL_NEIGHBOR_RIGHT_SIDE);
+ BIND_ENUM_CONSTANT(CELL_NEIGHBOR_RIGHT_CORNER);
+ BIND_ENUM_CONSTANT(CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE);
+ BIND_ENUM_CONSTANT(CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER);
+ BIND_ENUM_CONSTANT(CELL_NEIGHBOR_BOTTOM_SIDE);
+ BIND_ENUM_CONSTANT(CELL_NEIGHBOR_BOTTOM_CORNER);
+ BIND_ENUM_CONSTANT(CELL_NEIGHBOR_BOTTOM_LEFT_SIDE);
+ BIND_ENUM_CONSTANT(CELL_NEIGHBOR_BOTTOM_LEFT_CORNER);
+ BIND_ENUM_CONSTANT(CELL_NEIGHBOR_LEFT_SIDE);
+ BIND_ENUM_CONSTANT(CELL_NEIGHBOR_LEFT_CORNER);
+ BIND_ENUM_CONSTANT(CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ BIND_ENUM_CONSTANT(CELL_NEIGHBOR_TOP_LEFT_CORNER);
+ BIND_ENUM_CONSTANT(CELL_NEIGHBOR_TOP_SIDE);
+ BIND_ENUM_CONSTANT(CELL_NEIGHBOR_TOP_CORNER);
+ BIND_ENUM_CONSTANT(CELL_NEIGHBOR_TOP_RIGHT_SIDE);
+ BIND_ENUM_CONSTANT(CELL_NEIGHBOR_TOP_RIGHT_CORNER);
+
+ BIND_ENUM_CONSTANT(TERRAIN_MODE_MATCH_CORNERS_AND_SIDES);
+ BIND_ENUM_CONSTANT(TERRAIN_MODE_MATCH_CORNERS);
+ BIND_ENUM_CONSTANT(TERRAIN_MODE_MATCH_SIDES);
}
-void TileSet::tile_add_shape(int p_id, const Ref<Shape2D> &p_shape, const Transform2D &p_transform, bool p_one_way, const Vector2 &p_autotile_coord) {
- ERR_FAIL_COND(!tile_map.has(p_id));
+TileSet::TileSet() {
+ // Instantiate the tile meshes.
+ tile_lines_mesh.instantiate();
+ tile_filled_mesh.instantiate();
+}
- ShapeData new_data = ShapeData();
- new_data.shape = p_shape;
- new_data.shape_transform = p_transform;
- new_data.one_way_collision = p_one_way;
- new_data.autotile_coord = p_autotile_coord;
+TileSet::~TileSet() {
+#ifndef DISABLE_DEPRECATED
+ for (Map<int, CompatibilityTileData *>::Element *E = compatibility_data.front(); E; E = E->next()) {
+ memdelete(E->get());
+ }
+#endif // DISABLE_DEPRECATED
+ while (!source_ids.is_empty()) {
+ remove_source(source_ids[0]);
+ }
+}
+
+/////////////////////////////// TileSetSource //////////////////////////////////////
- tile_map[p_id].shapes_data.push_back(new_data);
+void TileSetSource::set_tile_set(const TileSet *p_tile_set) {
+ tile_set = p_tile_set;
}
-int TileSet::tile_get_shape_count(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), 0);
- return tile_map[p_id].shapes_data.size();
+/////////////////////////////// TileSetAtlasSource //////////////////////////////////////
+
+void TileSetAtlasSource::set_tile_set(const TileSet *p_tile_set) {
+ tile_set = p_tile_set;
+
+ // Set the TileSet on all TileData.
+ for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) {
+ for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) {
+ E_alternative->get()->set_tile_set(tile_set);
+ }
+ }
}
-void TileSet::tile_set_shape(int p_id, int p_shape_id, const Ref<Shape2D> &p_shape) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- ERR_FAIL_COND(p_shape_id < 0);
+void TileSetAtlasSource::notify_tile_data_properties_should_change() {
+ // Set the TileSet on all TileData.
+ for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) {
+ for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) {
+ E_alternative->get()->notify_tile_data_properties_should_change();
+ }
+ }
+}
- if (p_shape_id >= tile_map[p_id].shapes_data.size()) {
- tile_map[p_id].shapes_data.resize(p_shape_id + 1);
+void TileSetAtlasSource::reset_state() {
+ // Reset all TileData.
+ for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) {
+ for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) {
+ E_alternative->get()->reset_state();
+ }
}
- tile_map[p_id].shapes_data.write[p_shape_id].shape = p_shape;
- _decompose_convex_shape(p_shape);
+}
+
+void TileSetAtlasSource::set_texture(Ref<Texture2D> p_texture) {
+ texture = p_texture;
+
emit_changed();
}
-Ref<Shape2D> TileSet::tile_get_shape(int p_id, int p_shape_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Ref<Shape2D>());
- ERR_FAIL_COND_V(p_shape_id < 0, Ref<Shape2D>());
+Ref<Texture2D> TileSetAtlasSource::get_texture() const {
+ return texture;
+}
- if (p_shape_id < tile_map[p_id].shapes_data.size()) {
- return tile_map[p_id].shapes_data[p_shape_id].shape;
+void TileSetAtlasSource::set_margins(Vector2i p_margins) {
+ if (p_margins.x < 0 || p_margins.y < 0) {
+ WARN_PRINT("Atlas source margins should be positive.");
+ margins = Vector2i(MAX(0, p_margins.x), MAX(0, p_margins.y));
+ } else {
+ margins = p_margins;
}
- return Ref<Shape2D>();
+ emit_changed();
+}
+Vector2i TileSetAtlasSource::get_margins() const {
+ return margins;
}
-void TileSet::tile_set_shape_transform(int p_id, int p_shape_id, const Transform2D &p_offset) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- ERR_FAIL_COND(p_shape_id < 0);
+void TileSetAtlasSource::set_separation(Vector2i p_separation) {
+ if (p_separation.x < 0 || p_separation.y < 0) {
+ WARN_PRINT("Atlas source separation should be positive.");
+ separation = Vector2i(MAX(0, p_separation.x), MAX(0, p_separation.y));
+ } else {
+ separation = p_separation;
+ }
- if (p_shape_id >= tile_map[p_id].shapes_data.size()) {
- tile_map[p_id].shapes_data.resize(p_shape_id + 1);
+ emit_changed();
+}
+Vector2i TileSetAtlasSource::get_separation() const {
+ return separation;
+}
+
+void TileSetAtlasSource::set_texture_region_size(Vector2i p_tile_size) {
+ if (p_tile_size.x <= 0 || p_tile_size.y <= 0) {
+ WARN_PRINT("Atlas source tile_size should be strictly positive.");
+ texture_region_size = Vector2i(MAX(1, p_tile_size.x), MAX(1, p_tile_size.y));
+ } else {
+ texture_region_size = p_tile_size;
}
- tile_map[p_id].shapes_data.write[p_shape_id].shape_transform = p_offset;
+
emit_changed();
}
+Vector2i TileSetAtlasSource::get_texture_region_size() const {
+ return texture_region_size;
+}
+
+Vector2i TileSetAtlasSource::get_atlas_grid_size() const {
+ Ref<Texture2D> texture = get_texture();
+ if (!texture.is_valid()) {
+ return Vector2i();
+ }
+
+ ERR_FAIL_COND_V(texture_region_size.x <= 0 || texture_region_size.y <= 0, Vector2i());
+
+ Size2i valid_area = texture->get_size() - margins;
+
+ // Compute the number of valid tiles in the tiles atlas
+ Size2i grid_size = Size2i();
+ if (valid_area.x >= texture_region_size.x && valid_area.y >= texture_region_size.y) {
+ valid_area -= texture_region_size;
+ grid_size = Size2i(1, 1) + valid_area / (texture_region_size + separation);
+ }
+ return grid_size;
+}
-Transform2D TileSet::tile_get_shape_transform(int p_id, int p_shape_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Transform2D());
- ERR_FAIL_COND_V(p_shape_id < 0, Transform2D());
+bool TileSetAtlasSource::_set(const StringName &p_name, const Variant &p_value) {
+ Vector<String> components = String(p_name).split("/", true, 2);
- if (p_shape_id < tile_map[p_id].shapes_data.size()) {
- return tile_map[p_id].shapes_data[p_shape_id].shape_transform;
+ // Compute the vector2i if we have coordinates.
+ Vector<String> coords_split = components[0].split(":");
+ Vector2i coords = TileSetSource::INVALID_ATLAS_COORDS;
+ if (coords_split.size() == 2 && coords_split[0].is_valid_int() && coords_split[1].is_valid_int()) {
+ coords = Vector2i(coords_split[0].to_int(), coords_split[1].to_int());
}
- return Transform2D();
+ // Properties.
+ if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
+ // Create the tile if needed.
+ if (!has_tile(coords)) {
+ create_tile(coords);
+ }
+ if (components.size() >= 2) {
+ // Properties.
+ if (components[1] == "size_in_atlas") {
+ move_tile_in_atlas(coords, coords, p_value);
+ } else if (components[1] == "next_alternative_id") {
+ tiles[coords].next_alternative_id = p_value;
+ } else if (components[1].is_valid_int()) {
+ int alternative_id = components[1].to_int();
+ if (alternative_id != TileSetSource::INVALID_TILE_ALTERNATIVE) {
+ // Create the alternative if needed ?
+ if (!has_alternative_tile(coords, alternative_id)) {
+ create_alternative_tile(coords, alternative_id);
+ }
+ if (!tiles[coords].alternatives.has(alternative_id)) {
+ tiles[coords].alternatives[alternative_id] = memnew(TileData);
+ tiles[coords].alternatives[alternative_id]->set_tile_set(tile_set);
+ tiles[coords].alternatives[alternative_id]->set_allow_transform(alternative_id > 0);
+ tiles[coords].alternatives_ids.append(alternative_id);
+ }
+ if (components.size() >= 3) {
+ bool valid;
+ tiles[coords].alternatives[alternative_id]->set(components[2], p_value, &valid);
+ return valid;
+ } else {
+ // Only create the alternative if it did not exist yet.
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
}
-void TileSet::tile_set_shape_offset(int p_id, int p_shape_id, const Vector2 &p_offset) {
- Transform2D transform = tile_get_shape_transform(p_id, p_shape_id);
- transform.set_origin(p_offset);
- tile_set_shape_transform(p_id, p_shape_id, transform);
+bool TileSetAtlasSource::_get(const StringName &p_name, Variant &r_ret) const {
+ Vector<String> components = String(p_name).split("/", true, 2);
+
+ // Properties.
+ Vector<String> coords_split = components[0].split(":");
+ if (coords_split.size() == 2 && coords_split[0].is_valid_int() && coords_split[1].is_valid_int()) {
+ Vector2i coords = Vector2i(coords_split[0].to_int(), coords_split[1].to_int());
+ if (tiles.has(coords)) {
+ if (components.size() >= 2) {
+ // Properties.
+ if (components[1] == "size_in_atlas") {
+ r_ret = tiles[coords].size_in_atlas;
+ return true;
+ } else if (components[1] == "next_alternative_id") {
+ r_ret = tiles[coords].next_alternative_id;
+ return true;
+ } else if (components[1].is_valid_int()) {
+ int alternative_id = components[1].to_int();
+ if (alternative_id != TileSetSource::INVALID_TILE_ALTERNATIVE && tiles[coords].alternatives.has(alternative_id)) {
+ if (components.size() >= 3) {
+ bool valid;
+ r_ret = tiles[coords].alternatives[alternative_id]->get(components[2], &valid);
+ return valid;
+ } else {
+ // Only to notify the tile alternative exists.
+ r_ret = alternative_id;
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return false;
}
-Vector2 TileSet::tile_get_shape_offset(int p_id, int p_shape_id) const {
- return tile_get_shape_transform(p_id, p_shape_id).get_origin();
+void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const {
+ // Atlases data.
+ PropertyInfo property_info;
+ for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) {
+ List<PropertyInfo> tile_property_list;
+
+ // size_in_atlas
+ property_info = PropertyInfo(Variant::VECTOR2I, "size_in_atlas", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR);
+ if (E_tile->get().size_in_atlas == Vector2i(1, 1)) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ tile_property_list.push_back(property_info);
+
+ // next_alternative_id
+ property_info = PropertyInfo(Variant::INT, "next_alternative_id", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR);
+ if (E_tile->get().next_alternative_id == 1) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ tile_property_list.push_back(property_info);
+
+ for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) {
+ // Add a dummy property to show the alternative exists.
+ tile_property_list.push_back(PropertyInfo(Variant::INT, vformat("%d", E_alternative->key()), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+
+ // Get the alternative tile's properties and append them to the list of properties.
+ List<PropertyInfo> alternative_property_list;
+ E_alternative->get()->get_property_list(&alternative_property_list);
+ for (PropertyInfo &alternative_property_info : alternative_property_list) {
+ bool valid;
+ Variant default_value = ClassDB::class_get_default_property_value("TileData", alternative_property_info.name, &valid);
+ Variant value = E_alternative->get()->get(alternative_property_info.name);
+ if (valid && value == default_value) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ alternative_property_info.name = vformat("%s/%s", vformat("%d", E_alternative->key()), alternative_property_info.name);
+ tile_property_list.push_back(alternative_property_info);
+ }
+ }
+
+ // Add all alternative.
+ for (PropertyInfo &tile_property_info : tile_property_list) {
+ tile_property_info.name = vformat("%s/%s", vformat("%d:%d", E_tile->key().x, E_tile->key().y), tile_property_info.name);
+ p_list->push_back(tile_property_info);
+ }
+ }
}
-void TileSet::tile_set_shape_one_way(int p_id, int p_shape_id, const bool p_one_way) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- ERR_FAIL_COND(p_shape_id < 0);
+void TileSetAtlasSource::create_tile(const Vector2i p_atlas_coords, const Vector2i p_size) {
+ // Create a tile if it does not exists.
+ ERR_FAIL_COND(p_atlas_coords.x < 0 || p_atlas_coords.y < 0);
+ ERR_FAIL_COND(p_size.x <= 0 || p_size.y <= 0);
+ for (int x = 0; x < p_size.x; x++) {
+ for (int y = 0; y < p_size.y; y++) {
+ Vector2i coords = p_atlas_coords + Vector2i(x, y);
+ ERR_FAIL_COND_MSG(tiles.has(coords), vformat("Cannot create tile at position %s with size %s. Already a tile present at %s.", p_atlas_coords, p_size, coords));
+ }
+ }
- if (p_shape_id >= tile_map[p_id].shapes_data.size()) {
- tile_map[p_id].shapes_data.resize(p_shape_id + 1);
+ // Create and resize the tile.
+ tiles.insert(p_atlas_coords, TileSetAtlasSource::TileAlternativesData());
+ tiles_ids.append(p_atlas_coords);
+ tiles_ids.sort();
+
+ tiles[p_atlas_coords].size_in_atlas = p_size;
+ tiles[p_atlas_coords].alternatives[0] = memnew(TileData);
+ tiles[p_atlas_coords].alternatives[0]->set_tile_set(tile_set);
+ tiles[p_atlas_coords].alternatives[0]->set_allow_transform(false);
+ tiles[p_atlas_coords].alternatives[0]->connect("changed", callable_mp((Resource *)this, &TileSetAtlasSource::emit_changed));
+ tiles[p_atlas_coords].alternatives[0]->notify_property_list_changed();
+ tiles[p_atlas_coords].alternatives_ids.append(0);
+
+ // Add all covered positions to the mapping cache
+ for (int x = 0; x < p_size.x; x++) {
+ for (int y = 0; y < p_size.y; y++) {
+ Vector2i coords = p_atlas_coords + Vector2i(x, y);
+ _coords_mapping_cache[coords] = p_atlas_coords;
+ }
}
- tile_map[p_id].shapes_data.write[p_shape_id].one_way_collision = p_one_way;
- emit_changed();
+
+ emit_signal(SNAME("changed"));
}
-bool TileSet::tile_get_shape_one_way(int p_id, int p_shape_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), false);
- ERR_FAIL_COND_V(p_shape_id < 0, false);
+void TileSetAtlasSource::remove_tile(Vector2i p_atlas_coords) {
+ ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
+
+ // Remove all covered positions from the mapping cache
+ Size2i size = tiles[p_atlas_coords].size_in_atlas;
- if (p_shape_id < tile_map[p_id].shapes_data.size()) {
- return tile_map[p_id].shapes_data[p_shape_id].one_way_collision;
+ for (int x = 0; x < size.x; x++) {
+ for (int y = 0; y < size.y; y++) {
+ Vector2i coords = p_atlas_coords + Vector2i(x, y);
+ _coords_mapping_cache.erase(coords);
+ }
}
- return false;
+ // Free tile data.
+ for (Map<int, TileData *>::Element *E_tile_data = tiles[p_atlas_coords].alternatives.front(); E_tile_data; E_tile_data = E_tile_data->next()) {
+ memdelete(E_tile_data->get());
+ }
+
+ // Delete the tile
+ tiles.erase(p_atlas_coords);
+ tiles_ids.erase(p_atlas_coords);
+ tiles_ids.sort();
+
+ emit_signal(SNAME("changed"));
}
-void TileSet::tile_set_shape_one_way_margin(int p_id, int p_shape_id, float p_margin) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- ERR_FAIL_COND(p_shape_id < 0);
+bool TileSetAtlasSource::has_tile(Vector2i p_atlas_coords) const {
+ return tiles.has(p_atlas_coords);
+}
- if (p_shape_id >= tile_map[p_id].shapes_data.size()) {
- tile_map[p_id].shapes_data.resize(p_shape_id + 1);
+Vector2i TileSetAtlasSource::get_tile_at_coords(Vector2i p_atlas_coords) const {
+ if (!_coords_mapping_cache.has(p_atlas_coords)) {
+ return INVALID_ATLAS_COORDS;
}
- tile_map[p_id].shapes_data.write[p_shape_id].one_way_collision_margin = p_margin;
- emit_changed();
+
+ return _coords_mapping_cache[p_atlas_coords];
+}
+
+Vector2i TileSetAtlasSource::get_tile_size_in_atlas(Vector2i p_atlas_coords) const {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Vector2i(-1, -1), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
+
+ return tiles[p_atlas_coords].size_in_atlas;
+}
+
+int TileSetAtlasSource::get_tiles_count() const {
+ return tiles_ids.size();
+}
+
+Vector2i TileSetAtlasSource::get_tile_id(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, tiles_ids.size(), TileSetSource::INVALID_ATLAS_COORDS);
+ return tiles_ids[p_index];
}
-float TileSet::tile_get_shape_one_way_margin(int p_id, int p_shape_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), 0);
- ERR_FAIL_COND_V(p_shape_id < 0, 0);
+Rect2i TileSetAtlasSource::get_tile_texture_region(Vector2i p_atlas_coords) const {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Rect2i(), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
- if (p_shape_id < tile_map[p_id].shapes_data.size()) {
- return tile_map[p_id].shapes_data[p_shape_id].one_way_collision_margin;
+ Vector2i size_in_atlas = tiles[p_atlas_coords].size_in_atlas;
+ Vector2 region_size = texture_region_size * size_in_atlas + separation * (size_in_atlas - Vector2i(1, 1));
+
+ Vector2 origin = margins + (p_atlas_coords * (texture_region_size + separation));
+
+ return Rect2(origin, region_size);
+ ;
+}
+
+Vector2i TileSetAtlasSource::get_tile_effective_texture_offset(Vector2i p_atlas_coords, int p_alternative_tile) const {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Vector2i(), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
+ ERR_FAIL_COND_V_MSG(!has_alternative_tile(p_atlas_coords, p_alternative_tile), Vector2i(), vformat("TileSetAtlasSource has no alternative tile with id %d at %s.", p_alternative_tile, String(p_atlas_coords)));
+ ERR_FAIL_COND_V(!tile_set, Vector2i());
+
+ Vector2 margin = (get_tile_texture_region(p_atlas_coords).size - tile_set->get_tile_size()) / 2;
+ margin = Vector2i(MAX(0, margin.x), MAX(0, margin.y));
+ Vector2i effective_texture_offset = Object::cast_to<TileData>(get_tile_data(p_atlas_coords, p_alternative_tile))->get_texture_offset();
+ if (ABS(effective_texture_offset.x) > margin.x || ABS(effective_texture_offset.y) > margin.y) {
+ effective_texture_offset.x = CLAMP(effective_texture_offset.x, -margin.x, margin.x);
+ effective_texture_offset.y = CLAMP(effective_texture_offset.y, -margin.y, margin.y);
}
- return 0;
+ return effective_texture_offset;
}
-void TileSet::tile_set_light_occluder(int p_id, const Ref<OccluderPolygon2D> &p_light_occluder) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].occluder = p_light_occluder;
+bool TileSetAtlasSource::can_move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords, Vector2i p_new_size) const {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), false, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
+
+ Vector2i new_atlas_coords = (p_new_atlas_coords != INVALID_ATLAS_COORDS) ? p_new_atlas_coords : p_atlas_coords;
+ if (new_atlas_coords.x < 0 || new_atlas_coords.y < 0) {
+ return false;
+ }
+
+ Vector2i size = (p_new_size != Vector2i(-1, -1)) ? p_new_size : tiles[p_atlas_coords].size_in_atlas;
+ ERR_FAIL_COND_V(size.x <= 0 || size.y <= 0, false);
+
+ Size2i grid_size = get_atlas_grid_size();
+ if (new_atlas_coords.x + size.x > grid_size.x || new_atlas_coords.y + size.y > grid_size.y) {
+ return false;
+ }
+
+ Rect2i new_rect = Rect2i(new_atlas_coords, size);
+ // Check if the new tile can fit in the new rect.
+ for (int x = new_rect.position.x; x < new_rect.get_end().x; x++) {
+ for (int y = new_rect.position.y; y < new_rect.get_end().y; y++) {
+ Vector2i coords = get_tile_at_coords(Vector2i(x, y));
+ if (coords != p_atlas_coords && coords != TileSetSource::INVALID_ATLAS_COORDS) {
+ return false;
+ }
+ }
+ }
+
+ return true;
}
-Ref<OccluderPolygon2D> TileSet::tile_get_light_occluder(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Ref<OccluderPolygon2D>());
- return tile_map[p_id].occluder;
+void TileSetAtlasSource::move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords, Vector2i p_new_size) {
+ bool can_move = can_move_tile_in_atlas(p_atlas_coords, p_new_atlas_coords, p_new_size);
+ ERR_FAIL_COND_MSG(!can_move, vformat("Cannot move tile at position %s with size %s. Tile already present.", p_new_atlas_coords, p_new_size));
+
+ // Compute the actual new rect from arguments.
+ Vector2i new_atlas_coords = (p_new_atlas_coords != INVALID_ATLAS_COORDS) ? p_new_atlas_coords : p_atlas_coords;
+ Vector2i size = (p_new_size != Vector2i(-1, -1)) ? p_new_size : tiles[p_atlas_coords].size_in_atlas;
+
+ if (new_atlas_coords == p_atlas_coords && size == tiles[p_atlas_coords].size_in_atlas) {
+ return;
+ }
+
+ // Remove all covered positions from the mapping cache.
+ Size2i old_size = tiles[p_atlas_coords].size_in_atlas;
+ for (int x = 0; x < old_size.x; x++) {
+ for (int y = 0; y < old_size.y; y++) {
+ Vector2i coords = p_atlas_coords + Vector2i(x, y);
+ _coords_mapping_cache.erase(coords);
+ }
+ }
+
+ // Move the tile and update its size.
+ if (new_atlas_coords != p_atlas_coords) {
+ tiles[new_atlas_coords] = tiles[p_atlas_coords];
+ tiles.erase(p_atlas_coords);
+
+ tiles_ids.erase(p_atlas_coords);
+ tiles_ids.append(new_atlas_coords);
+ tiles_ids.sort();
+ }
+ tiles[new_atlas_coords].size_in_atlas = size;
+
+ // Add all covered positions to the mapping cache again.
+ for (int x = 0; x < size.x; x++) {
+ for (int y = 0; y < size.y; y++) {
+ Vector2i coords = new_atlas_coords + Vector2i(x, y);
+ _coords_mapping_cache[coords] = new_atlas_coords;
+ }
+ }
+
+ emit_signal(SNAME("changed"));
}
-void TileSet::autotile_set_light_occluder(int p_id, const Ref<OccluderPolygon2D> &p_light_occluder, const Vector2 &p_coord) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- if (p_light_occluder.is_null()) {
- if (tile_map[p_id].autotile_data.occluder_map.has(p_coord)) {
- tile_map[p_id].autotile_data.occluder_map.erase(p_coord);
+bool TileSetAtlasSource::has_tiles_outside_texture() {
+ Vector2i grid_size = get_atlas_grid_size();
+ Vector<Vector2i> to_remove;
+
+ for (Map<Vector2i, TileSetAtlasSource::TileAlternativesData>::Element *E = tiles.front(); E; E = E->next()) {
+ if (E->key().x >= grid_size.x || E->key().y >= grid_size.y) {
+ return true;
}
- } else {
- tile_map[p_id].autotile_data.occluder_map[p_coord] = p_light_occluder;
}
+
+ return false;
}
-Ref<OccluderPolygon2D> TileSet::autotile_get_light_occluder(int p_id, const Vector2 &p_coord) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Ref<OccluderPolygon2D>());
+void TileSetAtlasSource::clear_tiles_outside_texture() {
+ Vector2i grid_size = get_atlas_grid_size();
+ Vector<Vector2i> to_remove;
- if (!tile_map[p_id].autotile_data.occluder_map.has(p_coord)) {
- return Ref<OccluderPolygon2D>();
- } else {
- return tile_map[p_id].autotile_data.occluder_map[p_coord];
+ for (Map<Vector2i, TileSetAtlasSource::TileAlternativesData>::Element *E = tiles.front(); E; E = E->next()) {
+ if (E->key().x >= grid_size.x || E->key().y >= grid_size.y) {
+ to_remove.append(E->key());
+ }
}
+
+ for (int i = 0; i < to_remove.size(); i++) {
+ remove_tile(to_remove[i]);
+ }
+}
+
+int TileSetAtlasSource::create_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_id_override) {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), TileSetSource::INVALID_TILE_ALTERNATIVE, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
+ ERR_FAIL_COND_V_MSG(p_alternative_id_override >= 0 && tiles[p_atlas_coords].alternatives.has(p_alternative_id_override), TileSetSource::INVALID_TILE_ALTERNATIVE, vformat("Cannot create alternative tile. Another alternative exists with id %d.", p_alternative_id_override));
+
+ int new_alternative_id = p_alternative_id_override >= 0 ? p_alternative_id_override : tiles[p_atlas_coords].next_alternative_id;
+
+ tiles[p_atlas_coords].alternatives[new_alternative_id] = memnew(TileData);
+ tiles[p_atlas_coords].alternatives[new_alternative_id]->set_tile_set(tile_set);
+ tiles[p_atlas_coords].alternatives[new_alternative_id]->set_allow_transform(true);
+ tiles[p_atlas_coords].alternatives[new_alternative_id]->notify_property_list_changed();
+ tiles[p_atlas_coords].alternatives_ids.append(new_alternative_id);
+ tiles[p_atlas_coords].alternatives_ids.sort();
+ _compute_next_alternative_id(p_atlas_coords);
+
+ emit_signal(SNAME("changed"));
+
+ return new_alternative_id;
}
-void TileSet::tile_set_navigation_polygon_offset(int p_id, const Vector2 &p_offset) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].navigation_polygon_offset = p_offset;
+void TileSetAtlasSource::remove_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) {
+ ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
+ ERR_FAIL_COND_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords)));
+ ERR_FAIL_COND_MSG(p_alternative_tile == 0, "Cannot remove the alternative with id 0, the base tile alternative cannot be removed.");
+
+ memdelete(tiles[p_atlas_coords].alternatives[p_alternative_tile]);
+ tiles[p_atlas_coords].alternatives.erase(p_alternative_tile);
+ tiles[p_atlas_coords].alternatives_ids.erase(p_alternative_tile);
+ tiles[p_atlas_coords].alternatives_ids.sort();
+
+ emit_signal(SNAME("changed"));
}
-Vector2 TileSet::tile_get_navigation_polygon_offset(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Vector2());
- return tile_map[p_id].navigation_polygon_offset;
+void TileSetAtlasSource::set_alternative_tile_id(const Vector2i p_atlas_coords, int p_alternative_tile, int p_new_id) {
+ ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
+ ERR_FAIL_COND_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords)));
+ ERR_FAIL_COND_MSG(p_alternative_tile == 0, "Cannot change the alternative with id 0, the base tile alternative cannot be modified.");
+
+ ERR_FAIL_COND_MSG(tiles[p_atlas_coords].alternatives.has(p_new_id), vformat("TileSetAtlasSource has already an alternative with id %d at %s.", p_new_id, String(p_atlas_coords)));
+
+ tiles[p_atlas_coords].alternatives[p_new_id] = tiles[p_atlas_coords].alternatives[p_alternative_tile];
+ tiles[p_atlas_coords].alternatives_ids.append(p_new_id);
+
+ tiles[p_atlas_coords].alternatives.erase(p_alternative_tile);
+ tiles[p_atlas_coords].alternatives_ids.erase(p_alternative_tile);
+ tiles[p_atlas_coords].alternatives_ids.sort();
+
+ emit_signal(SNAME("changed"));
}
-void TileSet::tile_set_navigation_polygon(int p_id, const Ref<NavigationPolygon> &p_navigation_polygon) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].navigation_polygon = p_navigation_polygon;
+bool TileSetAtlasSource::has_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) const {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), false, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords)));
+ return tiles[p_atlas_coords].alternatives.has(p_alternative_tile);
}
-Ref<NavigationPolygon> TileSet::tile_get_navigation_polygon(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Ref<NavigationPolygon>());
- return tile_map[p_id].navigation_polygon;
+int TileSetAtlasSource::get_next_alternative_tile_id(const Vector2i p_atlas_coords) const {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), TileSetSource::INVALID_TILE_ALTERNATIVE, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords)));
+ return tiles[p_atlas_coords].next_alternative_id;
}
-const Map<Vector2, Ref<OccluderPolygon2D>> &TileSet::autotile_get_light_oclusion_map(int p_id) const {
- static Map<Vector2, Ref<OccluderPolygon2D>> dummy;
- ERR_FAIL_COND_V(!tile_map.has(p_id), dummy);
- return tile_map[p_id].autotile_data.occluder_map;
+int TileSetAtlasSource::get_alternative_tiles_count(const Vector2i p_atlas_coords) const {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), -1, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords)));
+ return tiles[p_atlas_coords].alternatives_ids.size();
}
-void TileSet::autotile_set_navigation_polygon(int p_id, const Ref<NavigationPolygon> &p_navigation_polygon, const Vector2 &p_coord) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- if (p_navigation_polygon.is_null()) {
- if (tile_map[p_id].autotile_data.navpoly_map.has(p_coord)) {
- tile_map[p_id].autotile_data.navpoly_map.erase(p_coord);
+int TileSetAtlasSource::get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), TileSetSource::INVALID_TILE_ALTERNATIVE, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords)));
+ ERR_FAIL_INDEX_V(p_index, tiles[p_atlas_coords].alternatives_ids.size(), TileSetSource::INVALID_TILE_ALTERNATIVE);
+
+ return tiles[p_atlas_coords].alternatives_ids[p_index];
+}
+
+Object *TileSetAtlasSource::get_tile_data(const Vector2i p_atlas_coords, int p_alternative_tile) const {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), nullptr, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords)));
+ ERR_FAIL_COND_V_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), nullptr, vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords)));
+
+ return tiles[p_atlas_coords].alternatives[p_alternative_tile];
+}
+
+void TileSetAtlasSource::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_texture", "texture"), &TileSetAtlasSource::set_texture);
+ ClassDB::bind_method(D_METHOD("get_texture"), &TileSetAtlasSource::get_texture);
+ ClassDB::bind_method(D_METHOD("set_margins", "margins"), &TileSetAtlasSource::set_margins);
+ ClassDB::bind_method(D_METHOD("get_margins"), &TileSetAtlasSource::get_margins);
+ ClassDB::bind_method(D_METHOD("set_separation", "separation"), &TileSetAtlasSource::set_separation);
+ ClassDB::bind_method(D_METHOD("get_separation"), &TileSetAtlasSource::get_separation);
+ ClassDB::bind_method(D_METHOD("set_texture_region_size", "texture_region_size"), &TileSetAtlasSource::set_texture_region_size);
+ ClassDB::bind_method(D_METHOD("get_texture_region_size"), &TileSetAtlasSource::get_texture_region_size);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_NOEDITOR), "set_texture", "get_texture");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "margins", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_margins", "get_margins");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "separation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_separation", "get_separation");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "tile_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_texture_region_size", "get_texture_region_size");
+
+ // Base tiles
+ ClassDB::bind_method(D_METHOD("create_tile", "atlas_coords", "size"), &TileSetAtlasSource::create_tile, DEFVAL(Vector2i(1, 1)));
+ ClassDB::bind_method(D_METHOD("remove_tile", "atlas_coords"), &TileSetAtlasSource::remove_tile); // Remove a tile. If p_tile_key.alternative_tile if different from 0, remove the alternative
+ ClassDB::bind_method(D_METHOD("has_tile", "atlas_coords"), &TileSetAtlasSource::has_tile);
+ ClassDB::bind_method(D_METHOD("can_move_tile_in_atlas", "atlas_coords", "new_atlas_coords", "new_size"), &TileSetAtlasSource::can_move_tile_in_atlas, DEFVAL(INVALID_ATLAS_COORDS), DEFVAL(Vector2i(-1, -1)));
+ ClassDB::bind_method(D_METHOD("move_tile_in_atlas", "atlas_coords", "new_atlas_coords", "new_size"), &TileSetAtlasSource::move_tile_in_atlas, DEFVAL(INVALID_ATLAS_COORDS), DEFVAL(Vector2i(-1, -1)));
+ ClassDB::bind_method(D_METHOD("get_tile_size_in_atlas", "atlas_coords"), &TileSetAtlasSource::get_tile_size_in_atlas);
+
+ ClassDB::bind_method(D_METHOD("get_tiles_count"), &TileSetAtlasSource::get_tiles_count);
+ ClassDB::bind_method(D_METHOD("get_tile_id", "index"), &TileSetAtlasSource::get_tile_id);
+
+ ClassDB::bind_method(D_METHOD("get_tile_at_coords", "atlas_coords"), &TileSetAtlasSource::get_tile_at_coords);
+
+ // Alternative tiles
+ ClassDB::bind_method(D_METHOD("create_alternative_tile", "atlas_coords", "alternative_id_override"), &TileSetAtlasSource::create_alternative_tile, DEFVAL(INVALID_TILE_ALTERNATIVE));
+ ClassDB::bind_method(D_METHOD("remove_alternative_tile", "atlas_coords", "alternative_tile"), &TileSetAtlasSource::remove_alternative_tile);
+ ClassDB::bind_method(D_METHOD("set_alternative_tile_id", "atlas_coords", "alternative_tile", "new_id"), &TileSetAtlasSource::set_alternative_tile_id);
+ ClassDB::bind_method(D_METHOD("has_alternative_tile", "atlas_coords", "alternative_tile"), &TileSetAtlasSource::has_alternative_tile);
+ ClassDB::bind_method(D_METHOD("get_next_alternative_tile_id", "atlas_coords"), &TileSetAtlasSource::get_next_alternative_tile_id);
+
+ ClassDB::bind_method(D_METHOD("get_alternative_tiles_count", "atlas_coords"), &TileSetAtlasSource::get_alternative_tiles_count);
+ ClassDB::bind_method(D_METHOD("get_alternative_tile_id", "atlas_coords", "index"), &TileSetAtlasSource::get_alternative_tile_id);
+
+ ClassDB::bind_method(D_METHOD("get_tile_data", "atlas_coords", "index"), &TileSetAtlasSource::get_tile_data);
+
+ // Helpers.
+ ClassDB::bind_method(D_METHOD("get_atlas_grid_size"), &TileSetAtlasSource::get_atlas_grid_size);
+ ClassDB::bind_method(D_METHOD("has_tiles_outside_texture"), &TileSetAtlasSource::has_tiles_outside_texture);
+ ClassDB::bind_method(D_METHOD("clear_tiles_outside_texture"), &TileSetAtlasSource::clear_tiles_outside_texture);
+ ClassDB::bind_method(D_METHOD("get_tile_texture_region", "atlas_coords"), &TileSetAtlasSource::get_tile_texture_region);
+}
+
+TileSetAtlasSource::~TileSetAtlasSource() {
+ // Free everything needed.
+ for (Map<Vector2i, TileAlternativesData>::Element *E_alternatives = tiles.front(); E_alternatives; E_alternatives = E_alternatives->next()) {
+ for (Map<int, TileData *>::Element *E_tile_data = E_alternatives->get().alternatives.front(); E_tile_data; E_tile_data = E_tile_data->next()) {
+ memdelete(E_tile_data->get());
}
- } else {
- tile_map[p_id].autotile_data.navpoly_map[p_coord] = p_navigation_polygon;
}
}
-Ref<NavigationPolygon> TileSet::autotile_get_navigation_polygon(int p_id, const Vector2 &p_coord) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Ref<NavigationPolygon>());
- if (!tile_map[p_id].autotile_data.navpoly_map.has(p_coord)) {
- return Ref<NavigationPolygon>();
- } else {
- return tile_map[p_id].autotile_data.navpoly_map[p_coord];
- }
+TileData *TileSetAtlasSource::_get_atlas_tile_data(Vector2i p_atlas_coords, int p_alternative_tile) {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), nullptr, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
+ ERR_FAIL_COND_V_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), nullptr, vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords)));
+
+ return tiles[p_atlas_coords].alternatives[p_alternative_tile];
}
-const Map<Vector2, Ref<NavigationPolygon>> &TileSet::autotile_get_navigation_map(int p_id) const {
- static Map<Vector2, Ref<NavigationPolygon>> dummy;
- ERR_FAIL_COND_V(!tile_map.has(p_id), dummy);
- return tile_map[p_id].autotile_data.navpoly_map;
+const TileData *TileSetAtlasSource::_get_atlas_tile_data(Vector2i p_atlas_coords, int p_alternative_tile) const {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), nullptr, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
+ ERR_FAIL_COND_V_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), nullptr, vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords)));
+
+ return tiles[p_atlas_coords].alternatives[p_alternative_tile];
}
-void TileSet::tile_set_occluder_offset(int p_id, const Vector2 &p_offset) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].occluder_offset = p_offset;
+void TileSetAtlasSource::_compute_next_alternative_id(const Vector2i p_atlas_coords) {
+ ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
+
+ while (tiles[p_atlas_coords].alternatives.has(tiles[p_atlas_coords].next_alternative_id)) {
+ tiles[p_atlas_coords].next_alternative_id = (tiles[p_atlas_coords].next_alternative_id % 1073741823) + 1; // 2 ** 30
+ };
}
-Vector2 TileSet::tile_get_occluder_offset(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Vector2());
- return tile_map[p_id].occluder_offset;
+/////////////////////////////// TileSetScenesCollectionSource //////////////////////////////////////
+
+void TileSetScenesCollectionSource::_compute_next_alternative_id() {
+ while (scenes.has(next_scene_id)) {
+ next_scene_id = (next_scene_id % 1073741823) + 1; // 2 ** 30
+ };
}
-void TileSet::tile_set_shapes(int p_id, const Vector<ShapeData> &p_shapes) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].shapes_data = p_shapes;
- for (int i = 0; i < p_shapes.size(); i++) {
- _decompose_convex_shape(p_shapes[i].shape);
- }
- emit_changed();
+int TileSetScenesCollectionSource::get_tiles_count() const {
+ return 1;
}
-Vector<TileSet::ShapeData> TileSet::tile_get_shapes(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Vector<ShapeData>());
+Vector2i TileSetScenesCollectionSource::get_tile_id(int p_tile_index) const {
+ ERR_FAIL_COND_V(p_tile_index != 0, TileSetSource::INVALID_ATLAS_COORDS);
+ return Vector2i();
+}
- return tile_map[p_id].shapes_data;
+bool TileSetScenesCollectionSource::has_tile(Vector2i p_atlas_coords) const {
+ return p_atlas_coords == Vector2i();
}
-int TileSet::tile_get_z_index(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), 0);
- return tile_map[p_id].z_index;
+int TileSetScenesCollectionSource::get_alternative_tiles_count(const Vector2i p_atlas_coords) const {
+ return scenes_ids.size();
}
-void TileSet::tile_set_z_index(int p_id, int p_z_index) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].z_index = p_z_index;
- emit_changed();
+int TileSetScenesCollectionSource::get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const {
+ ERR_FAIL_COND_V(p_atlas_coords != Vector2i(), TileSetSource::INVALID_TILE_ALTERNATIVE);
+ ERR_FAIL_INDEX_V(p_index, scenes_ids.size(), TileSetSource::INVALID_TILE_ALTERNATIVE);
+
+ return scenes_ids[p_index];
}
-void TileSet::_tile_set_shapes(int p_id, const Array &p_shapes) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- Vector<ShapeData> shapes_data;
- Transform2D default_transform = tile_get_shape_transform(p_id, 0);
- bool default_one_way = tile_get_shape_one_way(p_id, 0);
- Vector2 default_autotile_coord = Vector2();
- for (int i = 0; i < p_shapes.size(); i++) {
- ShapeData s = ShapeData();
+bool TileSetScenesCollectionSource::has_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) const {
+ ERR_FAIL_COND_V(p_atlas_coords != Vector2i(), false);
+ return scenes.has(p_alternative_tile);
+}
- if (p_shapes[i].get_type() == Variant::OBJECT) {
- Ref<Shape2D> shape = p_shapes[i];
- if (shape.is_null()) {
- continue;
- }
+int TileSetScenesCollectionSource::create_scene_tile(Ref<PackedScene> p_packed_scene, int p_id_override) {
+ ERR_FAIL_COND_V_MSG(p_id_override >= 0 && scenes.has(p_id_override), INVALID_TILE_ALTERNATIVE, vformat("Cannot create scene tile. Another scene tile exists with id %d.", p_id_override));
- s.shape = shape;
- s.shape_transform = default_transform;
- s.one_way_collision = default_one_way;
- s.autotile_coord = default_autotile_coord;
- } else if (p_shapes[i].get_type() == Variant::DICTIONARY) {
- Dictionary d = p_shapes[i];
+ int new_scene_id = p_id_override >= 0 ? p_id_override : next_scene_id;
- if (d.has("shape") && d["shape"].get_type() == Variant::OBJECT) {
- s.shape = d["shape"];
- _decompose_convex_shape(s.shape);
- } else {
- continue;
- }
+ scenes[new_scene_id] = SceneData();
+ scenes_ids.append(new_scene_id);
+ scenes_ids.sort();
+ set_scene_tile_scene(new_scene_id, p_packed_scene);
+ _compute_next_alternative_id();
- if (d.has("shape_transform") && d["shape_transform"].get_type() == Variant::TRANSFORM2D) {
- s.shape_transform = d["shape_transform"];
- } else if (d.has("shape_offset") && d["shape_offset"].get_type() == Variant::VECTOR2) {
- s.shape_transform = Transform2D(0, (Vector2)d["shape_offset"]);
- } else {
- s.shape_transform = default_transform;
- }
+ emit_signal(SNAME("changed"));
- if (d.has("one_way") && d["one_way"].get_type() == Variant::BOOL) {
- s.one_way_collision = d["one_way"];
- } else {
- s.one_way_collision = default_one_way;
- }
+ return new_scene_id;
+}
- if (d.has("one_way_margin") && d["one_way_margin"].is_num()) {
- s.one_way_collision_margin = d["one_way_margin"];
- } else {
- s.one_way_collision_margin = 1.0;
- }
+void TileSetScenesCollectionSource::set_scene_tile_id(int p_id, int p_new_id) {
+ ERR_FAIL_COND(p_new_id < 0);
+ ERR_FAIL_COND(!has_scene_tile_id(p_id));
+ ERR_FAIL_COND(has_scene_tile_id(p_new_id));
+
+ scenes[p_new_id] = SceneData();
+ scenes[p_new_id] = scenes[p_id];
+ scenes_ids.append(p_new_id);
+ scenes_ids.sort();
+
+ _compute_next_alternative_id();
+
+ scenes.erase(p_id);
+ scenes_ids.erase(p_id);
+
+ emit_signal(SNAME("changed"));
+}
+
+void TileSetScenesCollectionSource::set_scene_tile_scene(int p_id, Ref<PackedScene> p_packed_scene) {
+ ERR_FAIL_COND(!scenes.has(p_id));
+ if (p_packed_scene.is_valid()) {
+ // Make sure we have a root node. Supposed to be at 0 index because find_node_by_path() does not seem to work.
+ ERR_FAIL_COND(!p_packed_scene->get_state().is_valid());
+ ERR_FAIL_COND(p_packed_scene->get_state()->get_node_count() < 1);
+
+ // Check if it extends CanvasItem.
+ String type = p_packed_scene->get_state()->get_node_type(0);
+ bool extends_correct_class = ClassDB::is_parent_class(type, "Control") || ClassDB::is_parent_class(type, "Node2D");
+ ERR_FAIL_COND_MSG(!extends_correct_class, vformat("Invalid PackedScene for TileSetScenesCollectionSource: %s. Root node should extend Control or Node2D.", p_packed_scene->get_path()));
+
+ scenes[p_id].scene = p_packed_scene;
+ } else {
+ scenes[p_id].scene = Ref<PackedScene>();
+ }
+ emit_signal(SNAME("changed"));
+}
+
+Ref<PackedScene> TileSetScenesCollectionSource::get_scene_tile_scene(int p_id) const {
+ ERR_FAIL_COND_V(!scenes.has(p_id), Ref<PackedScene>());
+ return scenes[p_id].scene;
+}
- if (d.has("autotile_coord") && d["autotile_coord"].get_type() == Variant::VECTOR2) {
- s.autotile_coord = d["autotile_coord"];
+void TileSetScenesCollectionSource::set_scene_tile_display_placeholder(int p_id, bool p_display_placeholder) {
+ ERR_FAIL_COND(!scenes.has(p_id));
+
+ scenes[p_id].display_placeholder = p_display_placeholder;
+
+ emit_signal(SNAME("changed"));
+}
+
+bool TileSetScenesCollectionSource::get_scene_tile_display_placeholder(int p_id) const {
+ ERR_FAIL_COND_V(!scenes.has(p_id), false);
+ return scenes[p_id].display_placeholder;
+}
+
+void TileSetScenesCollectionSource::remove_scene_tile(int p_id) {
+ ERR_FAIL_COND(!scenes.has(p_id));
+
+ scenes.erase(p_id);
+ scenes_ids.erase(p_id);
+ emit_signal(SNAME("changed"));
+}
+
+int TileSetScenesCollectionSource::get_next_scene_tile_id() const {
+ return next_scene_id;
+}
+
+bool TileSetScenesCollectionSource::_set(const StringName &p_name, const Variant &p_value) {
+ Vector<String> components = String(p_name).split("/", true, 2);
+
+ if (components.size() >= 2 && components[0] == "scenes" && components[1].is_valid_int()) {
+ int scene_id = components[1].to_int();
+ if (components.size() >= 3 && components[2] == "scene") {
+ if (has_scene_tile_id(scene_id)) {
+ set_scene_tile_scene(scene_id, p_value);
} else {
- s.autotile_coord = default_autotile_coord;
+ create_scene_tile(p_value, scene_id);
+ }
+ return true;
+ } else if (components.size() >= 3 && components[2] == "display_placeholder") {
+ if (!has_scene_tile_id(scene_id)) {
+ create_scene_tile(p_value, scene_id);
}
- } else {
- ERR_CONTINUE_MSG(true, "Expected an array of objects or dictionaries for tile_set_shapes.");
+ return true;
}
-
- shapes_data.push_back(s);
}
- tile_map[p_id].shapes_data = shapes_data;
- emit_changed();
+ return false;
}
-Array TileSet::_tile_get_shapes(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Array());
- Array arr;
+bool TileSetScenesCollectionSource::_get(const StringName &p_name, Variant &r_ret) const {
+ Vector<String> components = String(p_name).split("/", true, 2);
- Vector<ShapeData> data = tile_map[p_id].shapes_data;
- for (int i = 0; i < data.size(); i++) {
- Dictionary shape_data;
- shape_data["shape"] = data[i].shape;
- shape_data["shape_transform"] = data[i].shape_transform;
- shape_data["one_way"] = data[i].one_way_collision;
- shape_data["one_way_margin"] = data[i].one_way_collision_margin;
- shape_data["autotile_coord"] = data[i].autotile_coord;
- arr.push_back(shape_data);
+ if (components.size() >= 2 && components[0] == "scenes" && components[1].is_valid_int() && scenes.has(components[1].to_int())) {
+ if (components.size() >= 3 && components[2] == "scene") {
+ r_ret = scenes[components[1].to_int()].scene;
+ return true;
+ } else if (components.size() >= 3 && components[2] == "display_placeholder") {
+ r_ret = scenes[components[1].to_int()].scene;
+ return true;
+ }
}
- return arr;
+ return false;
}
-Array TileSet::_get_tiles_ids() const {
- Array arr;
+void TileSetScenesCollectionSource::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (int i = 0; i < scenes_ids.size(); i++) {
+ p_list->push_back(PropertyInfo(Variant::OBJECT, vformat("scenes/%d/scene", scenes_ids[i]), PROPERTY_HINT_RESOURCE_TYPE, "TileSetScenesCollectionSource"));
- for (Map<int, TileData>::Element *E = tile_map.front(); E; E = E->next()) {
- arr.push_back(E->key());
+ PropertyInfo property_info = PropertyInfo(Variant::BOOL, vformat("scenes/%d/display_placeholder", scenes_ids[i]));
+ if (scenes[scenes_ids[i]].display_placeholder == false) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
}
+}
- return arr;
+void TileSetScenesCollectionSource::_bind_methods() {
+ // Base tiles
+ ClassDB::bind_method(D_METHOD("get_tiles_count"), &TileSetScenesCollectionSource::get_tiles_count);
+ ClassDB::bind_method(D_METHOD("get_tile_id", "index"), &TileSetScenesCollectionSource::get_tile_id);
+ ClassDB::bind_method(D_METHOD("has_tile", "atlas_coords"), &TileSetScenesCollectionSource::has_tile);
+
+ // Alternative tiles
+ ClassDB::bind_method(D_METHOD("get_alternative_tiles_count", "atlas_coords"), &TileSetScenesCollectionSource::get_alternative_tiles_count);
+ ClassDB::bind_method(D_METHOD("get_alternative_tile_id", "atlas_coords", "index"), &TileSetScenesCollectionSource::get_alternative_tile_id);
+ ClassDB::bind_method(D_METHOD("has_alternative_tile", "atlas_coords", "alternative_tile"), &TileSetScenesCollectionSource::has_alternative_tile);
+
+ ClassDB::bind_method(D_METHOD("get_scene_tiles_count"), &TileSetScenesCollectionSource::get_scene_tiles_count);
+ ClassDB::bind_method(D_METHOD("get_scene_tile_id", "index"), &TileSetScenesCollectionSource::get_scene_tile_id);
+ ClassDB::bind_method(D_METHOD("has_scene_tile_id", "id"), &TileSetScenesCollectionSource::has_scene_tile_id);
+ ClassDB::bind_method(D_METHOD("create_scene_tile", "packed_scene", "id_override"), &TileSetScenesCollectionSource::create_scene_tile, DEFVAL(INVALID_TILE_ALTERNATIVE));
+ ClassDB::bind_method(D_METHOD("set_scene_tile_id", "id", "new_id"), &TileSetScenesCollectionSource::set_scene_tile_id);
+ ClassDB::bind_method(D_METHOD("set_scene_tile_scene", "id", "packed_scene"), &TileSetScenesCollectionSource::set_scene_tile_scene);
+ ClassDB::bind_method(D_METHOD("get_scene_tile_scene", "id"), &TileSetScenesCollectionSource::get_scene_tile_scene);
+ ClassDB::bind_method(D_METHOD("set_scene_tile_display_placeholder", "id", "display_placeholder"), &TileSetScenesCollectionSource::set_scene_tile_display_placeholder);
+ ClassDB::bind_method(D_METHOD("get_scene_tile_display_placeholder", "id"), &TileSetScenesCollectionSource::get_scene_tile_display_placeholder);
+ ClassDB::bind_method(D_METHOD("remove_scene_tile", "id"), &TileSetScenesCollectionSource::remove_scene_tile);
+ ClassDB::bind_method(D_METHOD("get_next_scene_tile_id"), &TileSetScenesCollectionSource::get_next_scene_tile_id);
}
-void TileSet::_decompose_convex_shape(Ref<Shape2D> p_shape) {
- if (Engine::get_singleton()->is_editor_hint()) {
+/////////////////////////////// TileData //////////////////////////////////////
+
+void TileData::set_tile_set(const TileSet *p_tile_set) {
+ tile_set = p_tile_set;
+ notify_tile_data_properties_should_change();
+}
+
+void TileData::notify_tile_data_properties_should_change() {
+ if (!tile_set) {
return;
}
- Ref<ConvexPolygonShape2D> convex = p_shape;
- if (!convex.is_valid()) {
- return;
+
+ occluders.resize(tile_set->get_occlusion_layers_count());
+ physics.resize(tile_set->get_physics_layers_count());
+ for (int bit_index = 0; bit_index < 16; bit_index++) {
+ if (terrain_set < 0 || terrain_peering_bits[bit_index] >= tile_set->get_terrains_count(terrain_set)) {
+ terrain_peering_bits[bit_index] = -1;
+ }
}
- Vector<Vector<Vector2>> decomp = Geometry2D::decompose_polygon_in_convex(convex->get_points());
- if (decomp.size() > 1) {
- Array sub_shapes;
- for (int i = 0; i < decomp.size(); i++) {
- Ref<ConvexPolygonShape2D> _convex = memnew(ConvexPolygonShape2D);
- _convex->set_points(decomp[i]);
- sub_shapes.append(_convex);
+ navigation.resize(tile_set->get_navigation_layers_count());
+
+ // Convert custom data to the new type.
+ custom_data.resize(tile_set->get_custom_data_layers_count());
+ for (int i = 0; i < custom_data.size(); i++) {
+ if (custom_data[i].get_type() != tile_set->get_custom_data_type(i)) {
+ Variant new_val;
+ Callable::CallError error;
+ if (Variant::can_convert(custom_data[i].get_type(), tile_set->get_custom_data_type(i))) {
+ const Variant *args[] = { &custom_data[i] };
+ Variant::construct(tile_set->get_custom_data_type(i), new_val, args, 1, error);
+ } else {
+ Variant::construct(tile_set->get_custom_data_type(i), new_val, nullptr, 0, error);
+ }
+ custom_data.write[i] = new_val;
}
- convex->set_meta("decomposed", sub_shapes);
- } else {
- convex->set_meta("decomposed", Variant());
}
+
+ notify_property_list_changed();
+ emit_signal(SNAME("changed"));
+}
+
+void TileData::reset_state() {
+ occluders.clear();
+ physics.clear();
+ navigation.clear();
+ custom_data.clear();
+}
+
+void TileData::set_allow_transform(bool p_allow_transform) {
+ allow_transform = p_allow_transform;
+}
+
+bool TileData::is_allowing_transform() const {
+ return allow_transform;
+}
+
+// Rendering
+void TileData::set_flip_h(bool p_flip_h) {
+ ERR_FAIL_COND_MSG(!allow_transform && p_flip_h, "Transform is only allowed for alternative tiles (with its alternative_id != 0)");
+ flip_h = p_flip_h;
+ emit_signal(SNAME("changed"));
+}
+bool TileData::get_flip_h() const {
+ return flip_h;
+}
+
+void TileData::set_flip_v(bool p_flip_v) {
+ ERR_FAIL_COND_MSG(!allow_transform && p_flip_v, "Transform is only allowed for alternative tiles (with its alternative_id != 0)");
+ flip_v = p_flip_v;
+ emit_signal(SNAME("changed"));
+}
+
+bool TileData::get_flip_v() const {
+ return flip_v;
+}
+
+void TileData::set_transpose(bool p_transpose) {
+ ERR_FAIL_COND_MSG(!allow_transform && p_transpose, "Transform is only allowed for alternative tiles (with its alternative_id != 0)");
+ transpose = p_transpose;
+ emit_signal(SNAME("changed"));
+}
+bool TileData::get_transpose() const {
+ return transpose;
+}
+
+void TileData::set_texture_offset(Vector2i p_texture_offset) {
+ tex_offset = p_texture_offset;
+ emit_signal(SNAME("changed"));
+}
+
+Vector2i TileData::get_texture_offset() const {
+ return tex_offset;
+}
+
+void TileData::tile_set_material(Ref<ShaderMaterial> p_material) {
+ material = p_material;
+ emit_signal(SNAME("changed"));
+}
+Ref<ShaderMaterial> TileData::tile_get_material() const {
+ return material;
+}
+
+void TileData::set_modulate(Color p_modulate) {
+ modulate = p_modulate;
+ emit_signal(SNAME("changed"));
+}
+Color TileData::get_modulate() const {
+ return modulate;
+}
+
+void TileData::set_z_index(int p_z_index) {
+ z_index = p_z_index;
+ emit_signal(SNAME("changed"));
+}
+int TileData::get_z_index() const {
+ return z_index;
+}
+
+void TileData::set_y_sort_origin(int p_y_sort_origin) {
+ y_sort_origin = p_y_sort_origin;
+ emit_signal(SNAME("changed"));
+}
+int TileData::get_y_sort_origin() const {
+ return y_sort_origin;
+}
+
+void TileData::set_occluder(int p_layer_id, Ref<OccluderPolygon2D> p_occluder_polygon) {
+ ERR_FAIL_INDEX(p_layer_id, occluders.size());
+ occluders.write[p_layer_id] = p_occluder_polygon;
+ emit_signal(SNAME("changed"));
+}
+
+Ref<OccluderPolygon2D> TileData::get_occluder(int p_layer_id) const {
+ ERR_FAIL_INDEX_V(p_layer_id, occluders.size(), Ref<OccluderPolygon2D>());
+ return occluders[p_layer_id];
+}
+
+// Physics
+int TileData::get_collision_polygons_count(int p_layer_id) const {
+ ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0);
+ return physics[p_layer_id].polygons.size();
+}
+
+void TileData::set_collision_polygons_count(int p_layer_id, int p_polygons_count) {
+ ERR_FAIL_INDEX(p_layer_id, physics.size());
+ ERR_FAIL_COND(p_polygons_count < 0);
+ physics.write[p_layer_id].polygons.resize(p_polygons_count);
+ notify_property_list_changed();
+ emit_signal(SNAME("changed"));
+}
+
+void TileData::add_collision_polygon(int p_layer_id) {
+ ERR_FAIL_INDEX(p_layer_id, physics.size());
+ physics.write[p_layer_id].polygons.push_back(PhysicsLayerTileData::PolygonShapeTileData());
+ emit_signal(SNAME("changed"));
}
-void TileSet::get_tile_list(List<int> *p_tiles) const {
- for (Map<int, TileData>::Element *E = tile_map.front(); E; E = E->next()) {
- p_tiles->push_back(E->key());
+void TileData::remove_collision_polygon(int p_layer_id, int p_polygon_index) {
+ ERR_FAIL_INDEX(p_layer_id, physics.size());
+ ERR_FAIL_INDEX(p_polygon_index, physics[p_layer_id].polygons.size());
+ physics.write[p_layer_id].polygons.remove(p_polygon_index);
+ emit_signal(SNAME("changed"));
+}
+
+void TileData::set_collision_polygon_points(int p_layer_id, int p_polygon_index, Vector<Vector2> p_polygon) {
+ ERR_FAIL_INDEX(p_layer_id, physics.size());
+ ERR_FAIL_INDEX(p_polygon_index, physics[p_layer_id].polygons.size());
+ ERR_FAIL_COND_MSG(p_polygon.size() != 0 && p_polygon.size() < 3, "Invalid polygon. Needs either 0 or more than 3 points.");
+
+ if (p_polygon.is_empty()) {
+ physics.write[p_layer_id].polygons.write[p_polygon_index].shapes.clear();
+ } else {
+ // Decompose into convex shapes.
+ Vector<Vector<Vector2>> decomp = Geometry2D::decompose_polygon_in_convex(p_polygon);
+ ERR_FAIL_COND_MSG(decomp.is_empty(), "Could not decompose the polygon into convex shapes.");
+
+ physics.write[p_layer_id].polygons.write[p_polygon_index].shapes.resize(decomp.size());
+ for (int i = 0; i < decomp.size(); i++) {
+ Ref<ConvexPolygonShape2D> shape;
+ shape.instantiate();
+ shape->set_points(decomp[i]);
+ physics.write[p_layer_id].polygons.write[p_polygon_index].shapes[i] = shape;
+ }
}
+ physics.write[p_layer_id].polygons.write[p_polygon_index].polygon = p_polygon;
+ emit_signal(SNAME("changed"));
}
-bool TileSet::has_tile(int p_id) const {
- return tile_map.has(p_id);
+Vector<Vector2> TileData::get_collision_polygon_points(int p_layer_id, int p_polygon_index) const {
+ ERR_FAIL_INDEX_V(p_layer_id, physics.size(), Vector<Vector2>());
+ ERR_FAIL_INDEX_V(p_polygon_index, physics[p_layer_id].polygons.size(), Vector<Vector2>());
+ return physics[p_layer_id].polygons[p_polygon_index].polygon;
}
-bool TileSet::is_tile_bound(int p_drawn_id, int p_neighbor_id) {
- if (p_drawn_id == p_neighbor_id) {
- return true;
- } else if (get_script_instance() != nullptr) {
- if (get_script_instance()->has_method("_is_tile_bound")) {
- Variant ret = get_script_instance()->call("_is_tile_bound", p_drawn_id, p_neighbor_id);
- if (ret.get_type() == Variant::BOOL) {
- return ret;
- }
+void TileData::set_collision_polygon_one_way(int p_layer_id, int p_polygon_index, bool p_one_way) {
+ ERR_FAIL_INDEX(p_layer_id, physics.size());
+ ERR_FAIL_INDEX(p_polygon_index, physics[p_layer_id].polygons.size());
+ physics.write[p_layer_id].polygons.write[p_polygon_index].one_way = p_one_way;
+ emit_signal(SNAME("changed"));
+}
+
+bool TileData::is_collision_polygon_one_way(int p_layer_id, int p_polygon_index) const {
+ ERR_FAIL_INDEX_V(p_layer_id, physics.size(), false);
+ ERR_FAIL_INDEX_V(p_polygon_index, physics[p_layer_id].polygons.size(), false);
+ return physics[p_layer_id].polygons[p_polygon_index].one_way;
+}
+
+void TileData::set_collision_polygon_one_way_margin(int p_layer_id, int p_polygon_index, float p_one_way_margin) {
+ ERR_FAIL_INDEX(p_layer_id, physics.size());
+ ERR_FAIL_INDEX(p_polygon_index, physics[p_layer_id].polygons.size());
+ physics.write[p_layer_id].polygons.write[p_polygon_index].one_way_margin = p_one_way_margin;
+ emit_signal(SNAME("changed"));
+}
+
+float TileData::get_collision_polygon_one_way_margin(int p_layer_id, int p_polygon_index) const {
+ ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0.0);
+ ERR_FAIL_INDEX_V(p_polygon_index, physics[p_layer_id].polygons.size(), 0.0);
+ return physics[p_layer_id].polygons[p_polygon_index].one_way_margin;
+}
+
+int TileData::get_collision_polygon_shapes_count(int p_layer_id, int p_polygon_index) const {
+ ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0);
+ ERR_FAIL_INDEX_V(p_polygon_index, physics[p_layer_id].polygons.size(), 0);
+ return physics[p_layer_id].polygons[p_polygon_index].shapes.size();
+}
+
+Ref<ConvexPolygonShape2D> TileData::get_collision_polygon_shape(int p_layer_id, int p_polygon_index, int shape_index) const {
+ ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0);
+ ERR_FAIL_INDEX_V(p_polygon_index, physics[p_layer_id].polygons.size(), Ref<ConvexPolygonShape2D>());
+ ERR_FAIL_INDEX_V(shape_index, (int)physics[p_layer_id].polygons[shape_index].shapes.size(), Ref<ConvexPolygonShape2D>());
+ return physics[p_layer_id].polygons[shape_index].shapes[shape_index];
+}
+
+// Terrain
+void TileData::set_terrain_set(int p_terrain_set) {
+ ERR_FAIL_COND(p_terrain_set < -1);
+ if (p_terrain_set == terrain_set) {
+ return;
+ }
+ if (tile_set) {
+ ERR_FAIL_COND(p_terrain_set >= tile_set->get_terrain_sets_count());
+ for (int i = 0; i < 16; i++) {
+ terrain_peering_bits[i] = -1;
}
}
- return false;
+ terrain_set = p_terrain_set;
+ notify_property_list_changed();
+ emit_signal(SNAME("changed"));
}
-void TileSet::remove_tile(int p_id) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map.erase(p_id);
- _change_notify("");
- emit_changed();
+int TileData::get_terrain_set() const {
+ return terrain_set;
}
-int TileSet::get_last_unused_tile_id() const {
- if (tile_map.size()) {
- return tile_map.back()->key() + 1;
- } else {
- return 0;
+void TileData::set_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit, int p_terrain_index) {
+ ERR_FAIL_INDEX(p_peering_bit, TileSet::CellNeighbor::CELL_NEIGHBOR_MAX);
+ ERR_FAIL_COND(terrain_set < 0);
+ ERR_FAIL_COND(p_terrain_index < -1);
+ if (tile_set) {
+ ERR_FAIL_COND(p_terrain_index >= tile_set->get_terrains_count(terrain_set));
+ ERR_FAIL_COND(!is_valid_peering_bit_terrain(p_peering_bit));
}
+ terrain_peering_bits[p_peering_bit] = p_terrain_index;
+ emit_signal(SNAME("changed"));
}
-int TileSet::find_tile_by_name(const String &p_name) const {
- for (Map<int, TileData>::Element *E = tile_map.front(); E; E = E->next()) {
- if (p_name == E->get().name) {
- return E->key();
+int TileData::get_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const {
+ ERR_FAIL_COND_V(!is_valid_peering_bit_terrain(p_peering_bit), -1);
+ return terrain_peering_bits[p_peering_bit];
+}
+
+bool TileData::is_valid_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const {
+ ERR_FAIL_COND_V(!tile_set, false);
+
+ return tile_set->is_valid_peering_bit_terrain(terrain_set, p_peering_bit);
+}
+
+// Navigation
+void TileData::set_navigation_polygon(int p_layer_id, Ref<NavigationPolygon> p_navigation_polygon) {
+ ERR_FAIL_INDEX(p_layer_id, navigation.size());
+ navigation.write[p_layer_id] = p_navigation_polygon;
+ emit_signal(SNAME("changed"));
+}
+
+Ref<NavigationPolygon> TileData::get_navigation_polygon(int p_layer_id) const {
+ ERR_FAIL_INDEX_V(p_layer_id, navigation.size(), Ref<NavigationPolygon>());
+ return navigation[p_layer_id];
+}
+
+// Misc
+void TileData::set_probability(float p_probability) {
+ ERR_FAIL_COND(p_probability < 0.0);
+ probability = p_probability;
+ emit_signal(SNAME("changed"));
+}
+float TileData::get_probability() const {
+ return probability;
+}
+
+// Custom data
+void TileData::set_custom_data(String p_layer_name, Variant p_value) {
+ ERR_FAIL_COND(!tile_set);
+ int p_layer_id = tile_set->get_custom_data_layer_by_name(p_layer_name);
+ ERR_FAIL_COND_MSG(p_layer_id < 0, vformat("TileSet has no layer with name: %s", p_layer_name));
+ set_custom_data_by_layer_id(p_layer_id, p_value);
+}
+
+Variant TileData::get_custom_data(String p_layer_name) const {
+ ERR_FAIL_COND_V(!tile_set, Variant());
+ int p_layer_id = tile_set->get_custom_data_layer_by_name(p_layer_name);
+ ERR_FAIL_COND_V_MSG(p_layer_id < 0, Variant(), vformat("TileSet has no layer with name: %s", p_layer_name));
+ return get_custom_data_by_layer_id(p_layer_id);
+}
+
+void TileData::set_custom_data_by_layer_id(int p_layer_id, Variant p_value) {
+ ERR_FAIL_INDEX(p_layer_id, custom_data.size());
+ custom_data.write[p_layer_id] = p_value;
+ emit_signal(SNAME("changed"));
+}
+
+Variant TileData::get_custom_data_by_layer_id(int p_layer_id) const {
+ ERR_FAIL_INDEX_V(p_layer_id, custom_data.size(), Variant());
+ return custom_data[p_layer_id];
+}
+
+bool TileData::_set(const StringName &p_name, const Variant &p_value) {
+ Vector<String> components = String(p_name).split("/", true, 2);
+
+ if (components.size() == 2 && components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) {
+ // Occlusion layers.
+ int layer_index = components[0].trim_prefix("occlusion_layer_").to_int();
+ ERR_FAIL_COND_V(layer_index < 0, false);
+ if (components[1] == "polygon") {
+ Ref<OccluderPolygon2D> polygon = p_value;
+ if (!polygon.is_valid()) {
+ return false;
+ }
+
+ if (layer_index >= occluders.size()) {
+ if (tile_set) {
+ return false;
+ } else {
+ occluders.resize(layer_index + 1);
+ }
+ }
+ set_occluder(layer_index, polygon);
+ return true;
+ }
+ } else if (components.size() >= 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_int()) {
+ // Physics layers.
+ int layer_index = components[0].trim_prefix("physics_layer_").to_int();
+ ERR_FAIL_COND_V(layer_index < 0, false);
+ if (components.size() == 2 && components[1] == "polygons_count") {
+ if (p_value.get_type() != Variant::INT) {
+ return false;
+ }
+
+ if (layer_index >= physics.size()) {
+ if (tile_set) {
+ return false;
+ } else {
+ physics.resize(layer_index + 1);
+ }
+ }
+ set_collision_polygons_count(layer_index, p_value);
+ return true;
+ } else if (components.size() == 3 && components[1].begins_with("polygon_") && components[1].trim_prefix("polygon_").is_valid_int()) {
+ int polygon_index = components[1].trim_prefix("polygon_").to_int();
+ ERR_FAIL_COND_V(polygon_index < 0, false);
+
+ if (components[2] == "points" || components[2] == "one_way" || components[2] == "one_way_margin") {
+ if (layer_index >= physics.size()) {
+ if (tile_set) {
+ return false;
+ } else {
+ physics.resize(layer_index + 1);
+ }
+ }
+
+ if (polygon_index >= physics[layer_index].polygons.size()) {
+ physics.write[layer_index].polygons.resize(polygon_index + 1);
+ }
+ }
+ if (components[2] == "points") {
+ Vector<Vector2> polygon = p_value;
+ set_collision_polygon_points(layer_index, polygon_index, polygon);
+ return true;
+ } else if (components[2] == "one_way") {
+ set_collision_polygon_one_way(layer_index, polygon_index, p_value);
+ return true;
+ } else if (components[2] == "one_way_margin") {
+ set_collision_polygon_one_way_margin(layer_index, polygon_index, p_value);
+ return true;
+ }
+ }
+ } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_int()) {
+ // Navigation layers.
+ int layer_index = components[0].trim_prefix("navigation_layer_").to_int();
+ ERR_FAIL_COND_V(layer_index < 0, false);
+ if (components[1] == "polygon") {
+ Ref<NavigationPolygon> polygon = p_value;
+ if (!polygon.is_valid()) {
+ return false;
+ }
+
+ if (layer_index >= navigation.size()) {
+ if (tile_set) {
+ return false;
+ } else {
+ navigation.resize(layer_index + 1);
+ }
+ }
+ set_navigation_polygon(layer_index, polygon);
+ return true;
}
+ } else if (components.size() == 2 && components[0] == "terrains_peering_bit") {
+ // Terrains.
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
+ if (components[1] == TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]) {
+ set_peering_bit_terrain(bit, p_value);
+ return true;
+ }
+ }
+ return false;
+ } else if (components.size() == 1 && components[0].begins_with("custom_data_") && components[0].trim_prefix("custom_data_").is_valid_int()) {
+ // Custom data layers.
+ int layer_index = components[0].trim_prefix("custom_data_").to_int();
+ ERR_FAIL_COND_V(layer_index < 0, false);
+
+ if (layer_index >= custom_data.size()) {
+ if (tile_set) {
+ return false;
+ } else {
+ custom_data.resize(layer_index + 1);
+ }
+ }
+ set_custom_data_by_layer_id(layer_index, p_value);
+
+ return true;
}
- return -1;
+
+ return false;
}
-void TileSet::clear() {
- tile_map.clear();
- _change_notify("");
- emit_changed();
+bool TileData::_get(const StringName &p_name, Variant &r_ret) const {
+ Vector<String> components = String(p_name).split("/", true, 2);
+
+ if (tile_set) {
+ if (components.size() == 2 && components[0].begins_with("occlusion_layer") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) {
+ // Occlusion layers.
+ int layer_index = components[0].trim_prefix("occlusion_layer_").to_int();
+ ERR_FAIL_COND_V(layer_index < 0, false);
+ if (layer_index >= occluders.size()) {
+ return false;
+ }
+ if (components[1] == "polygon") {
+ r_ret = get_occluder(layer_index);
+ return true;
+ }
+ } else if (components.size() >= 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_int()) {
+ // Physics layers.
+ int layer_index = components[0].trim_prefix("physics_layer_").to_int();
+ ERR_FAIL_COND_V(layer_index < 0, false);
+ if (layer_index >= physics.size()) {
+ return false;
+ }
+ if (components.size() == 2 && components[1] == "polygons_count") {
+ r_ret = get_collision_polygons_count(layer_index);
+ return true;
+ } else if (components.size() == 3 && components[1].begins_with("polygon_") && components[1].trim_prefix("polygon_").is_valid_int()) {
+ int polygon_index = components[1].trim_prefix("polygon_").to_int();
+ ERR_FAIL_COND_V(polygon_index < 0, false);
+ if (polygon_index >= physics[layer_index].polygons.size()) {
+ return false;
+ }
+ if (components[2] == "points") {
+ r_ret = get_collision_polygon_points(layer_index, polygon_index);
+ return true;
+ } else if (components[2] == "one_way") {
+ r_ret = is_collision_polygon_one_way(layer_index, polygon_index);
+ return true;
+ } else if (components[2] == "one_way_margin") {
+ r_ret = get_collision_polygon_one_way_margin(layer_index, polygon_index);
+ return true;
+ }
+ }
+ } else if (components.size() == 2 && components[0] == "terrains_peering_bit") {
+ // Terrains.
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ if (components[1] == TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]) {
+ r_ret = terrain_peering_bits[i];
+ return true;
+ }
+ }
+ return false;
+ } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_int()) {
+ // Occlusion layers.
+ int layer_index = components[0].trim_prefix("navigation_layer_").to_int();
+ ERR_FAIL_COND_V(layer_index < 0, false);
+ if (layer_index >= navigation.size()) {
+ return false;
+ }
+ if (components[1] == "polygon") {
+ r_ret = get_navigation_polygon(layer_index);
+ return true;
+ }
+ } else if (components.size() == 1 && components[0].begins_with("custom_data_") && components[0].trim_prefix("custom_data_").is_valid_int()) {
+ // Custom data layers.
+ int layer_index = components[0].trim_prefix("custom_data_").to_int();
+ ERR_FAIL_COND_V(layer_index < 0, false);
+ if (layer_index >= custom_data.size()) {
+ return false;
+ }
+ r_ret = get_custom_data_by_layer_id(layer_index);
+ return true;
+ }
+ }
+
+ return false;
}
-void TileSet::_bind_methods() {
- ClassDB::bind_method(D_METHOD("create_tile", "id"), &TileSet::create_tile);
- ClassDB::bind_method(D_METHOD("autotile_clear_bitmask_map", "id"), &TileSet::autotile_clear_bitmask_map);
- ClassDB::bind_method(D_METHOD("autotile_set_icon_coordinate", "id", "coord"), &TileSet::autotile_set_icon_coordinate);
- ClassDB::bind_method(D_METHOD("autotile_get_icon_coordinate", "id"), &TileSet::autotile_get_icon_coordinate);
- ClassDB::bind_method(D_METHOD("autotile_set_subtile_priority", "id", "coord", "priority"), &TileSet::autotile_set_subtile_priority);
- ClassDB::bind_method(D_METHOD("autotile_get_subtile_priority", "id", "coord"), &TileSet::autotile_get_subtile_priority);
- ClassDB::bind_method(D_METHOD("autotile_set_z_index", "id", "coord", "z_index"), &TileSet::autotile_set_z_index);
- ClassDB::bind_method(D_METHOD("autotile_get_z_index", "id", "coord"), &TileSet::autotile_get_z_index);
- ClassDB::bind_method(D_METHOD("autotile_set_light_occluder", "id", "light_occluder", "coord"), &TileSet::autotile_set_light_occluder);
- ClassDB::bind_method(D_METHOD("autotile_get_light_occluder", "id", "coord"), &TileSet::autotile_get_light_occluder);
- ClassDB::bind_method(D_METHOD("autotile_set_navigation_polygon", "id", "navigation_polygon", "coord"), &TileSet::autotile_set_navigation_polygon);
- ClassDB::bind_method(D_METHOD("autotile_get_navigation_polygon", "id", "coord"), &TileSet::autotile_get_navigation_polygon);
- ClassDB::bind_method(D_METHOD("autotile_set_bitmask", "id", "bitmask", "flag"), &TileSet::autotile_set_bitmask);
- ClassDB::bind_method(D_METHOD("autotile_get_bitmask", "id", "coord"), &TileSet::autotile_get_bitmask);
- ClassDB::bind_method(D_METHOD("autotile_set_bitmask_mode", "id", "mode"), &TileSet::autotile_set_bitmask_mode);
- ClassDB::bind_method(D_METHOD("autotile_get_bitmask_mode", "id"), &TileSet::autotile_get_bitmask_mode);
- ClassDB::bind_method(D_METHOD("autotile_set_spacing", "id", "spacing"), &TileSet::autotile_set_spacing);
- ClassDB::bind_method(D_METHOD("autotile_get_spacing", "id"), &TileSet::autotile_get_spacing);
- ClassDB::bind_method(D_METHOD("autotile_set_size", "id", "size"), &TileSet::autotile_set_size);
- ClassDB::bind_method(D_METHOD("autotile_get_size", "id"), &TileSet::autotile_get_size);
- ClassDB::bind_method(D_METHOD("tile_set_name", "id", "name"), &TileSet::tile_set_name);
- ClassDB::bind_method(D_METHOD("tile_get_name", "id"), &TileSet::tile_get_name);
- ClassDB::bind_method(D_METHOD("tile_set_texture", "id", "texture"), &TileSet::tile_set_texture);
- ClassDB::bind_method(D_METHOD("tile_get_texture", "id"), &TileSet::tile_get_texture);
- ClassDB::bind_method(D_METHOD("tile_set_normal_map", "id", "normal_map"), &TileSet::tile_set_normal_map);
- ClassDB::bind_method(D_METHOD("tile_get_normal_map", "id"), &TileSet::tile_get_normal_map);
- ClassDB::bind_method(D_METHOD("tile_set_material", "id", "material"), &TileSet::tile_set_material);
- ClassDB::bind_method(D_METHOD("tile_get_material", "id"), &TileSet::tile_get_material);
- ClassDB::bind_method(D_METHOD("tile_set_modulate", "id", "color"), &TileSet::tile_set_modulate);
- ClassDB::bind_method(D_METHOD("tile_get_modulate", "id"), &TileSet::tile_get_modulate);
- ClassDB::bind_method(D_METHOD("tile_set_texture_offset", "id", "texture_offset"), &TileSet::tile_set_texture_offset);
- ClassDB::bind_method(D_METHOD("tile_get_texture_offset", "id"), &TileSet::tile_get_texture_offset);
- ClassDB::bind_method(D_METHOD("tile_set_region", "id", "region"), &TileSet::tile_set_region);
- ClassDB::bind_method(D_METHOD("tile_get_region", "id"), &TileSet::tile_get_region);
- ClassDB::bind_method(D_METHOD("tile_set_shape", "id", "shape_id", "shape"), &TileSet::tile_set_shape);
- ClassDB::bind_method(D_METHOD("tile_get_shape", "id", "shape_id"), &TileSet::tile_get_shape);
- ClassDB::bind_method(D_METHOD("tile_set_shape_offset", "id", "shape_id", "shape_offset"), &TileSet::tile_set_shape_offset);
- ClassDB::bind_method(D_METHOD("tile_get_shape_offset", "id", "shape_id"), &TileSet::tile_get_shape_offset);
- ClassDB::bind_method(D_METHOD("tile_set_shape_transform", "id", "shape_id", "shape_transform"), &TileSet::tile_set_shape_transform);
- ClassDB::bind_method(D_METHOD("tile_get_shape_transform", "id", "shape_id"), &TileSet::tile_get_shape_transform);
- ClassDB::bind_method(D_METHOD("tile_set_shape_one_way", "id", "shape_id", "one_way"), &TileSet::tile_set_shape_one_way);
- ClassDB::bind_method(D_METHOD("tile_get_shape_one_way", "id", "shape_id"), &TileSet::tile_get_shape_one_way);
- ClassDB::bind_method(D_METHOD("tile_set_shape_one_way_margin", "id", "shape_id", "one_way"), &TileSet::tile_set_shape_one_way_margin);
- ClassDB::bind_method(D_METHOD("tile_get_shape_one_way_margin", "id", "shape_id"), &TileSet::tile_get_shape_one_way_margin);
- ClassDB::bind_method(D_METHOD("tile_add_shape", "id", "shape", "shape_transform", "one_way", "autotile_coord"), &TileSet::tile_add_shape, DEFVAL(false), DEFVAL(Vector2()));
- ClassDB::bind_method(D_METHOD("tile_get_shape_count", "id"), &TileSet::tile_get_shape_count);
- ClassDB::bind_method(D_METHOD("tile_set_shapes", "id", "shapes"), &TileSet::_tile_set_shapes);
- ClassDB::bind_method(D_METHOD("tile_get_shapes", "id"), &TileSet::_tile_get_shapes);
- ClassDB::bind_method(D_METHOD("tile_set_tile_mode", "id", "tilemode"), &TileSet::tile_set_tile_mode);
- ClassDB::bind_method(D_METHOD("tile_get_tile_mode", "id"), &TileSet::tile_get_tile_mode);
- ClassDB::bind_method(D_METHOD("tile_set_navigation_polygon", "id", "navigation_polygon"), &TileSet::tile_set_navigation_polygon);
- ClassDB::bind_method(D_METHOD("tile_get_navigation_polygon", "id"), &TileSet::tile_get_navigation_polygon);
- ClassDB::bind_method(D_METHOD("tile_set_navigation_polygon_offset", "id", "navigation_polygon_offset"), &TileSet::tile_set_navigation_polygon_offset);
- ClassDB::bind_method(D_METHOD("tile_get_navigation_polygon_offset", "id"), &TileSet::tile_get_navigation_polygon_offset);
- ClassDB::bind_method(D_METHOD("tile_set_light_occluder", "id", "light_occluder"), &TileSet::tile_set_light_occluder);
- ClassDB::bind_method(D_METHOD("tile_get_light_occluder", "id"), &TileSet::tile_get_light_occluder);
- ClassDB::bind_method(D_METHOD("tile_set_occluder_offset", "id", "occluder_offset"), &TileSet::tile_set_occluder_offset);
- ClassDB::bind_method(D_METHOD("tile_get_occluder_offset", "id"), &TileSet::tile_get_occluder_offset);
- ClassDB::bind_method(D_METHOD("tile_set_z_index", "id", "z_index"), &TileSet::tile_set_z_index);
- ClassDB::bind_method(D_METHOD("tile_get_z_index", "id"), &TileSet::tile_get_z_index);
-
- ClassDB::bind_method(D_METHOD("remove_tile", "id"), &TileSet::remove_tile);
- ClassDB::bind_method(D_METHOD("clear"), &TileSet::clear);
- ClassDB::bind_method(D_METHOD("get_last_unused_tile_id"), &TileSet::get_last_unused_tile_id);
- ClassDB::bind_method(D_METHOD("find_tile_by_name", "name"), &TileSet::find_tile_by_name);
- ClassDB::bind_method(D_METHOD("get_tiles_ids"), &TileSet::_get_tiles_ids);
-
- BIND_VMETHOD(MethodInfo(Variant::BOOL, "_is_tile_bound", PropertyInfo(Variant::INT, "drawn_id"), PropertyInfo(Variant::INT, "neighbor_id")));
- BIND_VMETHOD(MethodInfo(Variant::VECTOR2, "_forward_subtile_selection", PropertyInfo(Variant::INT, "autotile_id"), PropertyInfo(Variant::INT, "bitmask"), PropertyInfo(Variant::OBJECT, "tilemap", PROPERTY_HINT_NONE, "TileMap"), PropertyInfo(Variant::VECTOR2, "tile_location")));
- BIND_VMETHOD(MethodInfo(Variant::VECTOR2, "_forward_atlas_subtile_selection", PropertyInfo(Variant::INT, "atlastile_id"), PropertyInfo(Variant::OBJECT, "tilemap", PROPERTY_HINT_NONE, "TileMap"), PropertyInfo(Variant::VECTOR2, "tile_location")));
-
- BIND_ENUM_CONSTANT(BITMASK_2X2);
- BIND_ENUM_CONSTANT(BITMASK_3X3_MINIMAL);
- BIND_ENUM_CONSTANT(BITMASK_3X3);
-
- BIND_ENUM_CONSTANT(BIND_TOPLEFT);
- BIND_ENUM_CONSTANT(BIND_TOP);
- BIND_ENUM_CONSTANT(BIND_TOPRIGHT);
- BIND_ENUM_CONSTANT(BIND_LEFT);
- BIND_ENUM_CONSTANT(BIND_CENTER);
- BIND_ENUM_CONSTANT(BIND_RIGHT);
- BIND_ENUM_CONSTANT(BIND_BOTTOMLEFT);
- BIND_ENUM_CONSTANT(BIND_BOTTOM);
- BIND_ENUM_CONSTANT(BIND_BOTTOMRIGHT);
-
- BIND_ENUM_CONSTANT(SINGLE_TILE);
- BIND_ENUM_CONSTANT(AUTO_TILE);
- BIND_ENUM_CONSTANT(ATLAS_TILE);
+void TileData::_get_property_list(List<PropertyInfo> *p_list) const {
+ PropertyInfo property_info;
+ // Add the groups manually.
+ if (tile_set) {
+ // Occlusion layers.
+ p_list->push_back(PropertyInfo(Variant::NIL, "Rendering", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
+ for (int i = 0; i < occluders.size(); i++) {
+ // occlusion_layer_%d/polygon
+ property_info = PropertyInfo(Variant::OBJECT, vformat("occlusion_layer_%d/polygon", i), PROPERTY_HINT_RESOURCE_TYPE, "OccluderPolygon2D", PROPERTY_USAGE_DEFAULT);
+ if (!occluders[i].is_valid()) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+ }
+
+ // Physics layers.
+ p_list->push_back(PropertyInfo(Variant::NIL, "Physics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
+ for (int i = 0; i < physics.size(); i++) {
+ p_list->push_back(PropertyInfo(Variant::INT, vformat("physics_layer_%d/polygons_count", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
+
+ for (int j = 0; j < physics[i].polygons.size(); j++) {
+ // physics_layer_%d/points
+ property_info = PropertyInfo(Variant::ARRAY, vformat("physics_layer_%d/polygon_%d/points", i, j), PROPERTY_HINT_ARRAY_TYPE, "Vector2", PROPERTY_USAGE_DEFAULT);
+ if (physics[i].polygons[j].polygon.is_empty()) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+
+ // physics_layer_%d/polygon_%d/one_way
+ property_info = PropertyInfo(Variant::BOOL, vformat("physics_layer_%d/polygon_%d/one_way", i, j));
+ if (physics[i].polygons[j].one_way == false) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+
+ // physics_layer_%d/polygon_%d/one_way_margin
+ property_info = PropertyInfo(Variant::FLOAT, vformat("physics_layer_%d/polygon_%d/one_way_margin", i, j));
+ if (physics[i].polygons[j].one_way_margin == 1.0) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+ }
+ }
+
+ // Terrain data
+ if (terrain_set >= 0) {
+ p_list->push_back(PropertyInfo(Variant::NIL, "Terrains", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
+ if (is_valid_peering_bit_terrain(bit)) {
+ property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]));
+ if (get_peering_bit_terrain(bit) == -1) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+ }
+ }
+ }
+
+ // Navigation layers.
+ p_list->push_back(PropertyInfo(Variant::NIL, "Navigation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
+ for (int i = 0; i < navigation.size(); i++) {
+ property_info = PropertyInfo(Variant::OBJECT, vformat("navigation_layer_%d/polygon", i), PROPERTY_HINT_RESOURCE_TYPE, "NavigationPolygon", PROPERTY_USAGE_DEFAULT);
+ if (!navigation[i].is_valid()) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+ }
+
+ // Custom data layers.
+ p_list->push_back(PropertyInfo(Variant::NIL, "Custom data", PROPERTY_HINT_NONE, "custom_data_", PROPERTY_USAGE_GROUP));
+ for (int i = 0; i < custom_data.size(); i++) {
+ Variant default_val;
+ Callable::CallError error;
+ Variant::construct(custom_data[i].get_type(), default_val, nullptr, 0, error);
+ property_info = PropertyInfo(tile_set->get_custom_data_type(i), vformat("custom_data_%d", i), PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT);
+ if (custom_data[i] == default_val) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+ }
+ }
}
-TileSet::TileSet() {
+void TileData::_bind_methods() {
+ // Rendering.
+ ClassDB::bind_method(D_METHOD("set_flip_h", "flip_h"), &TileData::set_flip_h);
+ ClassDB::bind_method(D_METHOD("get_flip_h"), &TileData::get_flip_h);
+ ClassDB::bind_method(D_METHOD("set_flip_v", "flip_v"), &TileData::set_flip_v);
+ ClassDB::bind_method(D_METHOD("get_flip_v"), &TileData::get_flip_v);
+ ClassDB::bind_method(D_METHOD("set_transpose", "transpose"), &TileData::set_transpose);
+ ClassDB::bind_method(D_METHOD("get_transpose"), &TileData::get_transpose);
+ ClassDB::bind_method(D_METHOD("tile_set_material", "material"), &TileData::tile_set_material);
+ ClassDB::bind_method(D_METHOD("tile_get_material"), &TileData::tile_get_material);
+ ClassDB::bind_method(D_METHOD("set_texture_offset", "texture_offset"), &TileData::set_texture_offset);
+ ClassDB::bind_method(D_METHOD("get_texture_offset"), &TileData::get_texture_offset);
+ ClassDB::bind_method(D_METHOD("set_modulate", "modulate"), &TileData::set_modulate);
+ ClassDB::bind_method(D_METHOD("get_modulate"), &TileData::get_modulate);
+ ClassDB::bind_method(D_METHOD("set_z_index", "z_index"), &TileData::set_z_index);
+ ClassDB::bind_method(D_METHOD("get_z_index"), &TileData::get_z_index);
+ ClassDB::bind_method(D_METHOD("set_y_sort_origin", "y_sort_origin"), &TileData::set_y_sort_origin);
+ ClassDB::bind_method(D_METHOD("get_y_sort_origin"), &TileData::get_y_sort_origin);
+
+ ClassDB::bind_method(D_METHOD("set_occluder", "layer_id", "occluder_polygon"), &TileData::set_occluder);
+ ClassDB::bind_method(D_METHOD("get_occluder", "layer_id"), &TileData::get_occluder);
+
+ // Physics.
+ ClassDB::bind_method(D_METHOD("get_collision_polygons_count", "layer_id"), &TileData::get_collision_polygons_count);
+ ClassDB::bind_method(D_METHOD("set_collision_polygons_count", "layer_id", "polygons_count"), &TileData::set_collision_polygons_count);
+ ClassDB::bind_method(D_METHOD("add_collision_polygon", "layer_id"), &TileData::add_collision_polygon);
+ ClassDB::bind_method(D_METHOD("remove_collision_polygon", "layer_id", "polygon_index"), &TileData::remove_collision_polygon);
+ ClassDB::bind_method(D_METHOD("set_collision_polygon_points", "layer_id", "polygon_index", "polygon"), &TileData::set_collision_polygon_points);
+ ClassDB::bind_method(D_METHOD("get_collision_polygon_points", "layer_id", "polygon_index"), &TileData::get_collision_polygon_points);
+ ClassDB::bind_method(D_METHOD("set_collision_polygon_one_way", "layer_id", "polygon_index", "one_way"), &TileData::set_collision_polygon_one_way);
+ ClassDB::bind_method(D_METHOD("is_collision_polygon_one_way", "layer_id", "polygon_index"), &TileData::is_collision_polygon_one_way);
+ ClassDB::bind_method(D_METHOD("set_collision_polygon_one_way_margin", "layer_id", "polygon_index", "one_way_margin"), &TileData::set_collision_polygon_one_way_margin);
+ ClassDB::bind_method(D_METHOD("get_collision_polygon_one_way_margin", "layer_id", "polygon_index"), &TileData::get_collision_polygon_one_way_margin);
+
+ // Terrain
+ ClassDB::bind_method(D_METHOD("set_terrain_set", "terrain_set"), &TileData::set_terrain_set);
+ ClassDB::bind_method(D_METHOD("get_terrain_set"), &TileData::get_terrain_set);
+ ClassDB::bind_method(D_METHOD("set_peering_bit_terrain", "peering_bit", "terrain"), &TileData::set_peering_bit_terrain);
+ ClassDB::bind_method(D_METHOD("get_peering_bit_terrain", "peering_bit"), &TileData::get_peering_bit_terrain);
+
+ // Navigation
+ ClassDB::bind_method(D_METHOD("set_navigation_polygon", "layer_id", "navigation_polygon"), &TileData::set_navigation_polygon);
+ ClassDB::bind_method(D_METHOD("get_navigation_polygon", "layer_id"), &TileData::get_navigation_polygon);
+
+ // Misc.
+ ClassDB::bind_method(D_METHOD("set_probability", "probability"), &TileData::set_probability);
+ ClassDB::bind_method(D_METHOD("get_probability"), &TileData::get_probability);
+
+ // Custom data.
+ ClassDB::bind_method(D_METHOD("set_custom_data", "layer_name", "value"), &TileData::set_custom_data);
+ ClassDB::bind_method(D_METHOD("get_custom_data", "layer_name"), &TileData::get_custom_data);
+ ClassDB::bind_method(D_METHOD("set_custom_data_by_layer_id", "layer_id", "value"), &TileData::set_custom_data_by_layer_id);
+ ClassDB::bind_method(D_METHOD("get_custom_data_by_layer_id", "layer_id"), &TileData::get_custom_data_by_layer_id);
+
+ ADD_GROUP("Rendering", "");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_h"), "set_flip_h", "get_flip_h");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_v"), "set_flip_v", "get_flip_v");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "transpose"), "set_transpose", "get_transpose");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "texture_offset"), "set_texture_offset", "get_texture_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "z_index"), "set_z_index", "get_z_index");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "y_sort_origin"), "set_y_sort_origin", "get_y_sort_origin");
+
+ ADD_GROUP("Terrains", "");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "terrain_set"), "set_terrain_set", "get_terrain_set");
+
+ ADD_GROUP("Miscellaneous", "");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "probability"), "set_probability", "get_probability");
+
+ ADD_SIGNAL(MethodInfo("changed"));
}
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index 78f34e46ce..35e6999d13 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,229 +31,638 @@
#ifndef TILE_SET_H
#define TILE_SET_H
-#include "core/array.h"
-#include "core/resource.h"
+#include "core/io/resource.h"
+#include "core/object/object.h"
+#include "core/templates/local_vector.h"
#include "scene/2d/light_occluder_2d.h"
#include "scene/2d/navigation_region_2d.h"
+#include "scene/main/canvas_item.h"
+#include "scene/resources/concave_polygon_shape_2d.h"
#include "scene/resources/convex_polygon_shape_2d.h"
+#include "scene/resources/packed_scene.h"
+#include "scene/resources/physics_material.h"
#include "scene/resources/shape_2d.h"
+
+#ifndef DISABLE_DEPRECATED
+#include "scene/resources/shader.h"
#include "scene/resources/texture.h"
+#endif
+
+class TileMap;
+struct TileMapQuadrant;
+class TileSetSource;
+class TileSetAtlasSource;
+class TileData;
+
+// Forward-declare the plugins.
+class TileSetPlugin;
+class TileSetPluginAtlasRendering;
+class TileSetPluginAtlasPhysics;
+class TileSetPluginAtlasNavigation;
class TileSet : public Resource {
GDCLASS(TileSet, Resource);
-public:
- struct ShapeData {
+#ifndef DISABLE_DEPRECATED
+private:
+ struct CompatibilityShapeData {
+ Vector2i autotile_coords;
+ bool one_way;
+ float one_way_margin;
Ref<Shape2D> shape;
- Transform2D shape_transform;
- Vector2 autotile_coord;
- bool one_way_collision = false;
- float one_way_collision_margin = 1.0;
+ Transform2D transform;
+ };
+
+ struct CompatibilityTileData {
+ String name;
+ Ref<Texture2D> texture;
+ Vector2 tex_offset;
+ Ref<ShaderMaterial> material;
+ Rect2 region;
+ int tile_mode = 0;
+ Color modulate = Color(1, 1, 1);
- ShapeData() {}
+ // Atlas or autotiles data
+ int autotile_bitmask_mode = 0;
+ Vector2 autotile_icon_coordinate;
+ Size2i autotile_tile_size = Size2i(16, 16);
+
+ int autotile_spacing = 0;
+ Map<Vector2i, int> autotile_bitmask_flags;
+ Map<Vector2i, Ref<OccluderPolygon2D>> autotile_occluder_map;
+ Map<Vector2i, Ref<NavigationPolygon>> autotile_navpoly_map;
+ Map<Vector2i, int> autotile_priority_map;
+ Map<Vector2i, int> autotile_z_index_map;
+
+ Vector<CompatibilityShapeData> shapes;
+ Ref<OccluderPolygon2D> occluder;
+ Vector2 occluder_offset;
+ Ref<NavigationPolygon> navigation;
+ Vector2 navigation_offset;
+ int z_index = 0;
};
- enum BitmaskMode {
- BITMASK_2X2,
- BITMASK_3X3_MINIMAL,
- BITMASK_3X3
+ enum CompatibilityTileMode {
+ COMPATIBILITY_TILE_MODE_SINGLE_TILE = 0,
+ COMPATIBILITY_TILE_MODE_AUTO_TILE,
+ COMPATIBILITY_TILE_MODE_ATLAS_TILE,
};
- enum AutotileBindings {
- BIND_TOPLEFT = 1,
- BIND_TOP = 2,
- BIND_TOPRIGHT = 4,
- BIND_LEFT = 8,
- BIND_CENTER = 16,
- BIND_RIGHT = 32,
- BIND_BOTTOMLEFT = 64,
- BIND_BOTTOM = 128,
- BIND_BOTTOMRIGHT = 256,
-
- BIND_IGNORE_TOPLEFT = 1 << 16,
- BIND_IGNORE_TOP = 1 << 17,
- BIND_IGNORE_TOPRIGHT = 1 << 18,
- BIND_IGNORE_LEFT = 1 << 19,
- BIND_IGNORE_CENTER = 1 << 20,
- BIND_IGNORE_RIGHT = 1 << 21,
- BIND_IGNORE_BOTTOMLEFT = 1 << 22,
- BIND_IGNORE_BOTTOM = 1 << 23,
- BIND_IGNORE_BOTTOMRIGHT = 1 << 24
+ Map<int, CompatibilityTileData *> compatibility_data;
+ Map<int, int> compatibility_tilemap_mapping_tile_modes;
+ Map<int, Map<Array, Array>> compatibility_tilemap_mapping;
+
+ void _compatibility_conversion();
+
+public:
+ // Format of output array [source_id, atlas_coords, alternative]
+ Array compatibility_tilemap_map(int p_tile_id, Vector2i p_coords, bool p_flip_h, bool p_flip_v, bool p_transpose);
+#endif // DISABLE_DEPRECATED
+
+public:
+ static const int INVALID_SOURCE; // -1;
+
+ enum CellNeighbor {
+ CELL_NEIGHBOR_RIGHT_SIDE = 0,
+ CELL_NEIGHBOR_RIGHT_CORNER,
+ CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE,
+ CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER,
+ CELL_NEIGHBOR_BOTTOM_SIDE,
+ CELL_NEIGHBOR_BOTTOM_CORNER,
+ CELL_NEIGHBOR_BOTTOM_LEFT_SIDE,
+ CELL_NEIGHBOR_BOTTOM_LEFT_CORNER,
+ CELL_NEIGHBOR_LEFT_SIDE,
+ CELL_NEIGHBOR_LEFT_CORNER,
+ CELL_NEIGHBOR_TOP_LEFT_SIDE,
+ CELL_NEIGHBOR_TOP_LEFT_CORNER,
+ CELL_NEIGHBOR_TOP_SIDE,
+ CELL_NEIGHBOR_TOP_CORNER,
+ CELL_NEIGHBOR_TOP_RIGHT_SIDE,
+ CELL_NEIGHBOR_TOP_RIGHT_CORNER,
+ CELL_NEIGHBOR_MAX,
};
- enum TileMode {
- SINGLE_TILE,
- AUTO_TILE,
- ATLAS_TILE
+ static const char *CELL_NEIGHBOR_ENUM_TO_TEXT[];
+
+ enum TerrainMode {
+ TERRAIN_MODE_MATCH_CORNERS_AND_SIDES = 0,
+ TERRAIN_MODE_MATCH_CORNERS,
+ TERRAIN_MODE_MATCH_SIDES,
};
- struct AutotileData {
- BitmaskMode bitmask_mode = BITMASK_2X2;
- // Default size to prevent invalid value
- Size2 size = Size2(64, 64);
- Vector2 icon_coord = Vector2(0, 0);
- int spacing = 0;
- Map<Vector2, uint32_t> flags;
- Map<Vector2, Ref<OccluderPolygon2D>> occluder_map;
- Map<Vector2, Ref<NavigationPolygon>> navpoly_map;
- Map<Vector2, int> priority_map;
- Map<Vector2, int> z_index_map;
-
- explicit AutotileData() {}
+ enum TileShape {
+ TILE_SHAPE_SQUARE,
+ TILE_SHAPE_ISOMETRIC,
+ TILE_SHAPE_HALF_OFFSET_SQUARE,
+ TILE_SHAPE_HEXAGON,
};
-private:
- struct TileData {
- String name;
- Ref<Texture2D> texture;
- Ref<Texture2D> normal_map;
- Vector2 offset;
- Rect2i region;
- Vector<ShapeData> shapes_data;
- Vector2 occluder_offset;
- Ref<OccluderPolygon2D> occluder;
- Vector2 navigation_polygon_offset;
- Ref<NavigationPolygon> navigation_polygon;
- Ref<ShaderMaterial> material;
- TileMode tile_mode = SINGLE_TILE;
- // Default modulate for back-compat
- Color modulate = Color(1, 1, 1);
- AutotileData autotile_data;
- int z_index = 0;
+ enum TileLayout {
+ TILE_LAYOUT_STACKED,
+ TILE_LAYOUT_STACKED_OFFSET,
+ TILE_LAYOUT_STAIRS_RIGHT,
+ TILE_LAYOUT_STAIRS_DOWN,
+ TILE_LAYOUT_DIAMOND_RIGHT,
+ TILE_LAYOUT_DIAMOND_DOWN,
+ };
- explicit TileData() {}
+ enum TileOffsetAxis {
+ TILE_OFFSET_AXIS_HORIZONTAL,
+ TILE_OFFSET_AXIS_VERTICAL,
};
- Map<int, TileData> tile_map;
+ struct PackedSceneSource {
+ Ref<PackedScene> scene;
+ Vector2 offset;
+ };
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
- void _tile_set_shapes(int p_id, const Array &p_shapes);
- Array _tile_get_shapes(int p_id) const;
- Array _get_tiles_ids() const;
- void _decompose_convex_shape(Ref<Shape2D> p_shape);
- static void _bind_methods();
+private:
+ // --- TileSet data ---
+ // Basic shape and layout.
+ TileShape tile_shape = TILE_SHAPE_SQUARE;
+ TileLayout tile_layout = TILE_LAYOUT_STACKED;
+ TileOffsetAxis tile_offset_axis = TILE_OFFSET_AXIS_HORIZONTAL;
+ Size2i tile_size = Size2i(16, 16); //Size2(64, 64);
+ Vector2 tile_skew = Vector2(0, 0);
+
+ // Rendering.
+ bool uv_clipping = false;
+ struct OcclusionLayer {
+ uint32_t light_mask = 1;
+ bool sdf_collision = false;
+ };
+ Vector<OcclusionLayer> occlusion_layers;
-public:
- void create_tile(int p_id);
+ Ref<ArrayMesh> tile_lines_mesh;
+ Ref<ArrayMesh> tile_filled_mesh;
+ bool tile_meshes_dirty = true;
- void autotile_set_bitmask_mode(int p_id, BitmaskMode p_mode);
- BitmaskMode autotile_get_bitmask_mode(int p_id) const;
+ // Physics
+ struct PhysicsLayer {
+ uint32_t collision_layer = 1;
+ uint32_t collision_mask = 1;
+ Ref<PhysicsMaterial> physics_material;
+ };
+ Vector<PhysicsLayer> physics_layers;
- void tile_set_name(int p_id, const String &p_name);
- String tile_get_name(int p_id) const;
+ // Terrains
+ struct Terrain {
+ String name;
+ Color color;
+ };
+ struct TerrainSet {
+ TerrainMode mode = TERRAIN_MODE_MATCH_CORNERS_AND_SIDES;
+ Vector<Terrain> terrains;
+ };
+ Vector<TerrainSet> terrain_sets;
- void tile_set_texture(int p_id, const Ref<Texture2D> &p_texture);
- Ref<Texture2D> tile_get_texture(int p_id) const;
+ Map<TerrainMode, Map<CellNeighbor, Ref<ArrayMesh>>> terrain_bits_meshes;
+ bool terrain_bits_meshes_dirty = true;
- void tile_set_normal_map(int p_id, const Ref<Texture2D> &p_normal_map);
- Ref<Texture2D> tile_get_normal_map(int p_id) const;
+ // Navigation
+ struct Navigationlayer {
+ uint32_t layers = 1;
+ };
+ Vector<Navigationlayer> navigation_layers;
- void tile_set_texture_offset(int p_id, const Vector2 &p_offset);
- Vector2 tile_get_texture_offset(int p_id) const;
+ // CustomData
+ struct CustomDataLayer {
+ String name;
+ Variant::Type type = Variant::NIL;
+ };
+ Vector<CustomDataLayer> custom_data_layers;
+ Map<String, int> custom_data_layers_by_name;
- void tile_set_region(int p_id, const Rect2 &p_region);
- Rect2 tile_get_region(int p_id) const;
+ // Per Atlas source data.
+ Map<int, Ref<TileSetSource>> sources;
+ Vector<int> source_ids;
+ int next_source_id = 0;
+ // ---------------------
- void tile_set_tile_mode(int p_id, TileMode p_tile_mode);
- TileMode tile_get_tile_mode(int p_id) const;
+ void _compute_next_source_id();
+ void _source_changed();
- void autotile_set_icon_coordinate(int p_id, Vector2 coord);
- Vector2 autotile_get_icon_coordinate(int p_id) const;
+ // Tile proxies
+ Map<int, int> source_level_proxies;
+ Map<Array, Array> coords_level_proxies;
+ Map<Array, Array> alternative_level_proxies;
- void autotile_set_spacing(int p_id, int p_spacing);
- int autotile_get_spacing(int p_id) const;
+ // Helpers
+ Vector<Point2> _get_square_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit);
+ Vector<Point2> _get_square_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit);
+ Vector<Point2> _get_square_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit);
- void autotile_set_size(int p_id, Size2 p_size);
- Size2 autotile_get_size(int p_id) const;
+ Vector<Point2> _get_isometric_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit);
+ Vector<Point2> _get_isometric_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit);
+ Vector<Point2> _get_isometric_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit);
- void autotile_clear_bitmask_map(int p_id);
- void autotile_set_subtile_priority(int p_id, const Vector2 &p_coord, int p_priority);
- int autotile_get_subtile_priority(int p_id, const Vector2 &p_coord);
- const Map<Vector2, int> &autotile_get_priority_map(int p_id) const;
+ Vector<Point2> _get_half_offset_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis);
+ Vector<Point2> _get_half_offset_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis);
+ Vector<Point2> _get_half_offset_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis);
- void autotile_set_z_index(int p_id, const Vector2 &p_coord, int p_z_index);
- int autotile_get_z_index(int p_id, const Vector2 &p_coord);
- const Map<Vector2, int> &autotile_get_z_index_map(int p_id) const;
+protected:
+ static void _bind_methods();
- void autotile_set_bitmask(int p_id, Vector2 p_coord, uint32_t p_flag);
- uint32_t autotile_get_bitmask(int p_id, Vector2 p_coord);
- const Map<Vector2, uint32_t> &autotile_get_bitmask_map(int p_id);
- Vector2 autotile_get_subtile_for_bitmask(int p_id, uint16_t p_bitmask, const Node *p_tilemap_node = nullptr, const Vector2 &p_tile_location = Vector2());
- Vector2 atlastile_get_subtile_by_priority(int p_id, const Node *p_tilemap_node = nullptr, const Vector2 &p_tile_location = Vector2());
+public:
+ // --- Plugins ---
+ Vector<TileSetPlugin *> get_tile_set_atlas_plugins() const;
+
+ // --- Accessors for TileSet data ---
+
+ // -- Shape and layout --
+ void set_tile_shape(TileShape p_shape);
+ TileShape get_tile_shape() const;
+ void set_tile_layout(TileLayout p_layout);
+ TileLayout get_tile_layout() const;
+ void set_tile_offset_axis(TileOffsetAxis p_alignment);
+ TileOffsetAxis get_tile_offset_axis() const;
+ void set_tile_size(Size2i p_size);
+ Size2i get_tile_size() const;
+
+ // -- Sources management --
+ int get_next_source_id() const;
+ int get_source_count() const;
+ int get_source_id(int p_index) const;
+ int add_source(Ref<TileSetSource> p_tile_set_source, int p_source_id_override = -1);
+ void set_source_id(int p_source_id, int p_new_id);
+ void remove_source(int p_source_id);
+ bool has_source(int p_source_id) const;
+ Ref<TileSetSource> get_source(int p_source_id) const;
+
+ // Rendering
+ void set_uv_clipping(bool p_uv_clipping);
+ bool is_uv_clipping() const;
+
+ void set_occlusion_layers_count(int p_occlusion_layers_count);
+ int get_occlusion_layers_count() const;
+ void set_occlusion_layer_light_mask(int p_layer_index, int p_light_mask);
+ int get_occlusion_layer_light_mask(int p_layer_index) const;
+ void set_occlusion_layer_sdf_collision(int p_layer_index, int p_sdf_collision);
+ bool get_occlusion_layer_sdf_collision(int p_layer_index) const;
+
+ // Physics
+ void set_physics_layers_count(int p_physics_layers_count);
+ int get_physics_layers_count() const;
+ void set_physics_layer_collision_layer(int p_layer_index, uint32_t p_layer);
+ uint32_t get_physics_layer_collision_layer(int p_layer_index) const;
+ void set_physics_layer_collision_mask(int p_layer_index, uint32_t p_mask);
+ uint32_t get_physics_layer_collision_mask(int p_layer_index) const;
+ void set_physics_layer_physics_material(int p_layer_index, Ref<PhysicsMaterial> p_physics_material);
+ Ref<PhysicsMaterial> get_physics_layer_physics_material(int p_layer_index) const;
+
+ // Terrains
+ void set_terrain_sets_count(int p_terrains_sets_count);
+ int get_terrain_sets_count() const;
+ void set_terrain_set_mode(int p_terrain_set, TerrainMode p_terrain_mode);
+ TerrainMode get_terrain_set_mode(int p_terrain_set) const;
+ void set_terrains_count(int p_terrain_set, int p_terrains_count);
+ int get_terrains_count(int p_terrain_set) const;
+ void set_terrain_name(int p_terrain_set, int p_terrain_index, String p_name);
+ String get_terrain_name(int p_terrain_set, int p_terrain_index) const;
+ void set_terrain_color(int p_terrain_set, int p_terrain_index, Color p_color);
+ Color get_terrain_color(int p_terrain_set, int p_terrain_index) const;
+ bool is_valid_peering_bit_for_mode(TileSet::TerrainMode p_terrain_mode, TileSet::CellNeighbor p_peering_bit) const;
+ bool is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeighbor p_peering_bit) const;
+
+ // Navigation
+ void set_navigation_layers_count(int p_navigation_layers_count);
+ int get_navigation_layers_count() const;
+ void set_navigation_layer_layers(int p_layer_index, uint32_t p_layers);
+ uint32_t get_navigation_layer_layers(int p_layer_index) const;
+
+ // Custom data
+ void set_custom_data_layers_count(int p_custom_data_layers_count);
+ int get_custom_data_layers_count() const;
+ int get_custom_data_layer_by_name(String p_value) const;
+ void set_custom_data_name(int p_layer_id, String p_value);
+ String get_custom_data_name(int p_layer_id) const;
+ void set_custom_data_type(int p_layer_id, Variant::Type p_value);
+ Variant::Type get_custom_data_type(int p_layer_id) const;
+
+ // Tiles proxies.
+ void set_source_level_tile_proxy(int p_source_from, int p_source_to);
+ int get_source_level_tile_proxy(int p_source_from);
+ bool has_source_level_tile_proxy(int p_source_from);
+ void remove_source_level_tile_proxy(int p_source_from);
+
+ void set_coords_level_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_source_to, Vector2i p_coords_to);
+ Array get_coords_level_tile_proxy(int p_source_from, Vector2i p_coords_from);
+ bool has_coords_level_tile_proxy(int p_source_from, Vector2i p_coords_from);
+ void remove_coords_level_tile_proxy(int p_source_from, Vector2i p_coords_from);
+
+ void set_alternative_level_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_alternative_from, int p_source_to, Vector2i p_coords_to, int p_alternative_to);
+ Array get_alternative_level_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_alternative_from);
+ bool has_alternative_level_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_alternative_from);
+ void remove_alternative_level_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_alternative_from);
+
+ Array get_source_level_tile_proxies() const;
+ Array get_coords_level_tile_proxies() const;
+ Array get_alternative_level_tile_proxies() const;
+
+ Array map_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_alternative_from) const;
+
+ void cleanup_invalid_tile_proxies();
+ void clear_tile_proxies();
+
+ // Helpers
+ Vector<Vector2> get_tile_shape_polygon();
+ void draw_tile_shape(CanvasItem *p_canvas_item, Rect2 p_region, Color p_color, bool p_filled = false, Ref<Texture2D> p_texture = Ref<Texture2D>());
+
+ Vector<Point2> get_terrain_bit_polygon(int p_terrain_set, TileSet::CellNeighbor p_bit);
+ void draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, const TileData *p_tile_data);
+ Vector<Vector<Ref<Texture2D>>> generate_terrains_icons(Size2i p_size);
+
+ // Resource management
+ virtual void reset_state() override;
- void tile_set_shape(int p_id, int p_shape_id, const Ref<Shape2D> &p_shape);
- Ref<Shape2D> tile_get_shape(int p_id, int p_shape_id) const;
+ TileSet();
+ ~TileSet();
+};
- void tile_set_shape_transform(int p_id, int p_shape_id, const Transform2D &p_offset);
- Transform2D tile_get_shape_transform(int p_id, int p_shape_id) const;
+class TileSetSource : public Resource {
+ GDCLASS(TileSetSource, Resource);
- void tile_set_shape_offset(int p_id, int p_shape_id, const Vector2 &p_offset);
- Vector2 tile_get_shape_offset(int p_id, int p_shape_id) const;
+protected:
+ const TileSet *tile_set = nullptr;
- void tile_set_shape_one_way(int p_id, int p_shape_id, bool p_one_way);
- bool tile_get_shape_one_way(int p_id, int p_shape_id) const;
+public:
+ static const Vector2i INVALID_ATLAS_COORDS; // Vector2i(-1, -1);
+ static const int INVALID_TILE_ALTERNATIVE; // -1;
+
+ // Not exposed.
+ virtual void set_tile_set(const TileSet *p_tile_set);
+ virtual void notify_tile_data_properties_should_change(){};
+ virtual void reset_state() override{};
+
+ // Tiles.
+ virtual int get_tiles_count() const = 0;
+ virtual Vector2i get_tile_id(int tile_index) const = 0;
+ virtual bool has_tile(Vector2i p_atlas_coords) const = 0;
+
+ // Alternative tiles.
+ virtual int get_alternative_tiles_count(const Vector2i p_atlas_coords) const = 0;
+ virtual int get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const = 0;
+ virtual bool has_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) const = 0;
+};
- void tile_set_shape_one_way_margin(int p_id, int p_shape_id, float p_margin);
- float tile_get_shape_one_way_margin(int p_id, int p_shape_id) const;
+class TileSetAtlasSource : public TileSetSource {
+ GDCLASS(TileSetAtlasSource, TileSetSource);
- void tile_clear_shapes(int p_id);
- void tile_add_shape(int p_id, const Ref<Shape2D> &p_shape, const Transform2D &p_transform, bool p_one_way = false, const Vector2 &p_autotile_coord = Vector2());
- int tile_get_shape_count(int p_id) const;
+public:
+ struct TileAlternativesData {
+ Vector2i size_in_atlas = Vector2i(1, 1);
+ Vector2i texture_offset;
+ Map<int, TileData *> alternatives;
+ Vector<int> alternatives_ids;
+ int next_alternative_id = 1;
+ };
+
+private:
+ Ref<Texture2D> texture;
+ Vector2i margins;
+ Vector2i separation;
+ Size2i texture_region_size = Size2i(16, 16);
+
+ Map<Vector2i, TileAlternativesData> tiles;
+ Vector<Vector2i> tiles_ids;
+ Map<Vector2i, Vector2i> _coords_mapping_cache; // Maps any coordinate to the including tile
+
+ TileData *_get_atlas_tile_data(Vector2i p_atlas_coords, int p_alternative_tile);
+ const TileData *_get_atlas_tile_data(Vector2i p_atlas_coords, int p_alternative_tile) const;
- void tile_set_shapes(int p_id, const Vector<ShapeData> &p_shapes);
- Vector<ShapeData> tile_get_shapes(int p_id) const;
+ void _compute_next_alternative_id(const Vector2i p_atlas_coords);
+
+protected:
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
- void tile_set_material(int p_id, const Ref<ShaderMaterial> &p_material);
- Ref<ShaderMaterial> tile_get_material(int p_id) const;
+ static void _bind_methods();
- void tile_set_modulate(int p_id, const Color &p_modulate);
- Color tile_get_modulate(int p_id) const;
+public:
+ // Not exposed.
+ virtual void set_tile_set(const TileSet *p_tile_set) override;
+ virtual void notify_tile_data_properties_should_change() override;
+ virtual void reset_state() override;
+
+ // Base properties.
+ void set_texture(Ref<Texture2D> p_texture);
+ Ref<Texture2D> get_texture() const;
+ void set_margins(Vector2i p_margins);
+ Vector2i get_margins() const;
+ void set_separation(Vector2i p_separation);
+ Vector2i get_separation() const;
+ void set_texture_region_size(Vector2i p_tile_size);
+ Vector2i get_texture_region_size() const;
+
+ // Base tiles.
+ void create_tile(const Vector2i p_atlas_coords, const Vector2i p_size = Vector2i(1, 1)); // Create a tile if it does not exists, or add alternative tile if it does.
+ void remove_tile(Vector2i p_atlas_coords); // Remove a tile. If p_tile_key.alternative_tile if different from 0, remove the alternative
+ virtual bool has_tile(Vector2i p_atlas_coords) const override;
+ bool can_move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords = INVALID_ATLAS_COORDS, Vector2i p_new_size = Vector2i(-1, -1)) const;
+ void move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords = INVALID_ATLAS_COORDS, Vector2i p_new_size = Vector2i(-1, -1));
+ Vector2i get_tile_size_in_atlas(Vector2i p_atlas_coords) const;
+
+ virtual int get_tiles_count() const override;
+ virtual Vector2i get_tile_id(int p_index) const override;
+
+ Vector2i get_tile_at_coords(Vector2i p_atlas_coords) const;
+
+ // Alternative tiles.
+ int create_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_id_override = -1);
+ void remove_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile);
+ void set_alternative_tile_id(const Vector2i p_atlas_coords, int p_alternative_tile, int p_new_id);
+ virtual bool has_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) const override;
+ int get_next_alternative_tile_id(const Vector2i p_atlas_coords) const;
+
+ virtual int get_alternative_tiles_count(const Vector2i p_atlas_coords) const override;
+ virtual int get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const override;
+
+ // Get data associated to a tile.
+ Object *get_tile_data(const Vector2i p_atlas_coords, int p_alternative_tile) const;
+
+ // Helpers.
+ Vector2i get_atlas_grid_size() const;
+ bool has_tiles_outside_texture();
+ void clear_tiles_outside_texture();
+ Rect2i get_tile_texture_region(Vector2i p_atlas_coords) const;
+ Vector2i get_tile_effective_texture_offset(Vector2i p_atlas_coords, int p_alternative_tile) const;
+
+ ~TileSetAtlasSource();
+};
- void tile_set_occluder_offset(int p_id, const Vector2 &p_offset);
- Vector2 tile_get_occluder_offset(int p_id) const;
+class TileSetScenesCollectionSource : public TileSetSource {
+ GDCLASS(TileSetScenesCollectionSource, TileSetSource);
- void tile_set_light_occluder(int p_id, const Ref<OccluderPolygon2D> &p_light_occluder);
- Ref<OccluderPolygon2D> tile_get_light_occluder(int p_id) const;
+private:
+ struct SceneData {
+ Ref<PackedScene> scene;
+ bool display_placeholder = false;
+ };
+ Vector<int> scenes_ids;
+ Map<int, SceneData> scenes;
+ int next_scene_id = 1;
- void autotile_set_light_occluder(int p_id, const Ref<OccluderPolygon2D> &p_light_occluder, const Vector2 &p_coord);
- Ref<OccluderPolygon2D> autotile_get_light_occluder(int p_id, const Vector2 &p_coord) const;
- const Map<Vector2, Ref<OccluderPolygon2D>> &autotile_get_light_oclusion_map(int p_id) const;
+ void _compute_next_alternative_id();
- void tile_set_navigation_polygon_offset(int p_id, const Vector2 &p_offset);
- Vector2 tile_get_navigation_polygon_offset(int p_id) const;
+protected:
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
- void tile_set_navigation_polygon(int p_id, const Ref<NavigationPolygon> &p_navigation_polygon);
- Ref<NavigationPolygon> tile_get_navigation_polygon(int p_id) const;
+ static void _bind_methods();
- void autotile_set_navigation_polygon(int p_id, const Ref<NavigationPolygon> &p_navigation_polygon, const Vector2 &p_coord);
- Ref<NavigationPolygon> autotile_get_navigation_polygon(int p_id, const Vector2 &p_coord) const;
- const Map<Vector2, Ref<NavigationPolygon>> &autotile_get_navigation_map(int p_id) const;
+public:
+ // Tiles.
+ int get_tiles_count() const override;
+ Vector2i get_tile_id(int p_tile_index) const override;
+ bool has_tile(Vector2i p_atlas_coords) const override;
+
+ // Alternative tiles.
+ int get_alternative_tiles_count(const Vector2i p_atlas_coords) const override;
+ int get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const override;
+ bool has_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) const override;
+
+ // Scenes sccessors. Lot are similar to "Alternative tiles".
+ int get_scene_tiles_count() { return get_alternative_tiles_count(Vector2i()); }
+ int get_scene_tile_id(int p_index) { return get_alternative_tile_id(Vector2i(), p_index); };
+ bool has_scene_tile_id(int p_id) { return has_alternative_tile(Vector2i(), p_id); };
+ int create_scene_tile(Ref<PackedScene> p_packed_scene = Ref<PackedScene>(), int p_id_override = -1);
+ void set_scene_tile_id(int p_id, int p_new_id);
+ void set_scene_tile_scene(int p_id, Ref<PackedScene> p_packed_scene);
+ Ref<PackedScene> get_scene_tile_scene(int p_id) const;
+ void set_scene_tile_display_placeholder(int p_id, bool p_packed_scene);
+ bool get_scene_tile_display_placeholder(int p_id) const;
+ void remove_scene_tile(int p_id);
+ int get_next_scene_tile_id() const;
+};
- void tile_set_z_index(int p_id, int p_z_index);
- int tile_get_z_index(int p_id) const;
+class TileData : public Object {
+ GDCLASS(TileData, Object);
- void remove_tile(int p_id);
+private:
+ const TileSet *tile_set = nullptr;
+ bool allow_transform = true;
+
+ // Rendering
+ bool flip_h = false;
+ bool flip_v = false;
+ bool transpose = false;
+ Vector2i tex_offset = Vector2i();
+ Ref<ShaderMaterial> material = Ref<ShaderMaterial>();
+ Color modulate = Color(1.0, 1.0, 1.0, 1.0);
+ int z_index = 0;
+ int y_sort_origin = 0;
+ Vector<Ref<OccluderPolygon2D>> occluders;
+
+ // Physics
+ struct PhysicsLayerTileData {
+ struct PolygonShapeTileData {
+ LocalVector<Vector2> polygon;
+ LocalVector<Ref<ConvexPolygonShape2D>> shapes;
+ bool one_way = false;
+ float one_way_margin = 1.0;
+ };
+
+ Vector<PolygonShapeTileData> polygons;
+ };
+ Vector<PhysicsLayerTileData> physics;
+ // TODO add support for areas.
- bool has_tile(int p_id) const;
+ // Terrain
+ int terrain_set = -1;
+ int terrain_peering_bits[16] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
- bool is_tile_bound(int p_drawn_id, int p_neighbor_id);
+ // Navigation
+ Vector<Ref<NavigationPolygon>> navigation;
- int find_tile_by_name(const String &p_name) const;
- void get_tile_list(List<int> *p_tiles) const;
+ // Misc
+ double probability = 1.0;
- void clear();
+ // Custom data
+ Vector<Variant> custom_data;
- int get_last_unused_tile_id() const;
+protected:
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+ static void _bind_methods();
- TileSet();
+public:
+ // Not exposed.
+ void set_tile_set(const TileSet *p_tile_set);
+ void notify_tile_data_properties_should_change();
+ void reset_state();
+ void set_allow_transform(bool p_allow_transform);
+ bool is_allowing_transform() const;
+
+ // Rendering
+ void set_flip_h(bool p_flip_h);
+ bool get_flip_h() const;
+ void set_flip_v(bool p_flip_v);
+ bool get_flip_v() const;
+ void set_transpose(bool p_transpose);
+ bool get_transpose() const;
+
+ void set_texture_offset(Vector2i p_texture_offset);
+ Vector2i get_texture_offset() const;
+ void tile_set_material(Ref<ShaderMaterial> p_material);
+ Ref<ShaderMaterial> tile_get_material() const;
+ void set_modulate(Color p_modulate);
+ Color get_modulate() const;
+ void set_z_index(int p_z_index);
+ int get_z_index() const;
+ void set_y_sort_origin(int p_y_sort_origin);
+ int get_y_sort_origin() const;
+
+ void set_occluder(int p_layer_id, Ref<OccluderPolygon2D> p_occluder_polygon);
+ Ref<OccluderPolygon2D> get_occluder(int p_layer_id) const;
+
+ // Physics
+ int get_collision_polygons_count(int p_layer_id) const;
+ void set_collision_polygons_count(int p_layer_id, int p_shapes_count);
+ void add_collision_polygon(int p_layer_id);
+ void remove_collision_polygon(int p_layer_id, int p_polygon_index);
+ void set_collision_polygon_points(int p_layer_id, int p_polygon_index, Vector<Vector2> p_polygon);
+ Vector<Vector2> get_collision_polygon_points(int p_layer_id, int p_polygon_index) const;
+ void set_collision_polygon_one_way(int p_layer_id, int p_polygon_index, bool p_one_way);
+ bool is_collision_polygon_one_way(int p_layer_id, int p_polygon_index) const;
+ void set_collision_polygon_one_way_margin(int p_layer_id, int p_polygon_index, float p_one_way_margin);
+ float get_collision_polygon_one_way_margin(int p_layer_id, int p_polygon_index) const;
+ int get_collision_polygon_shapes_count(int p_layer_id, int p_polygon_index) const;
+ Ref<ConvexPolygonShape2D> get_collision_polygon_shape(int p_layer_id, int p_polygon_index, int shape_index) const;
+
+ // Terrain
+ void set_terrain_set(int p_terrain_id);
+ int get_terrain_set() const;
+ void set_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit, int p_terrain_id);
+ int get_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const;
+ bool is_valid_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const;
+
+ // Navigation
+ void set_navigation_polygon(int p_layer_id, Ref<NavigationPolygon> p_navigation_polygon);
+ Ref<NavigationPolygon> get_navigation_polygon(int p_layer_id) const;
+
+ // Misc
+ void set_probability(float p_probability);
+ float get_probability() const;
+
+ // Custom data.
+ void set_custom_data(String p_layer_name, Variant p_value);
+ Variant get_custom_data(String p_layer_name) const;
+ void set_custom_data_by_layer_id(int p_layer_id, Variant p_value);
+ Variant get_custom_data_by_layer_id(int p_layer_id) const;
};
-VARIANT_ENUM_CAST(TileSet::AutotileBindings);
-VARIANT_ENUM_CAST(TileSet::BitmaskMode);
-VARIANT_ENUM_CAST(TileSet::TileMode);
+VARIANT_ENUM_CAST(TileSet::CellNeighbor);
+VARIANT_ENUM_CAST(TileSet::TerrainMode);
+VARIANT_ENUM_CAST(TileSet::TileShape);
+VARIANT_ENUM_CAST(TileSet::TileLayout);
+VARIANT_ENUM_CAST(TileSet::TileOffsetAxis);
#endif // TILE_SET_H
diff --git a/scene/resources/video_stream.h b/scene/resources/video_stream.h
index 379ba53a34..f960f85521 100644
--- a/scene/resources/video_stream.h
+++ b/scene/resources/video_stream.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index 84437faca0..e8fe3ff3cd 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,9 +30,11 @@
#include "visual_shader.h"
-#include "core/vmap.h"
+#include "core/templates/vmap.h"
#include "servers/rendering/shader_types.h"
#include "visual_shader_nodes.h"
+#include "visual_shader_particle_nodes.h"
+#include "visual_shader_sdf_nodes.h"
bool VisualShaderNode::is_simple_decl() const {
return simple_decl;
@@ -59,6 +61,20 @@ Variant VisualShaderNode::get_input_port_default_value(int p_port) const {
return Variant();
}
+void VisualShaderNode::remove_input_port_default_value(int p_port) {
+ if (default_input_values.has(p_port)) {
+ default_input_values.erase(p_port);
+ emit_changed();
+ }
+}
+
+void VisualShaderNode::clear_default_input_values() {
+ if (!default_input_values.is_empty()) {
+ default_input_values.clear();
+ emit_changed();
+ }
+}
+
bool VisualShaderNode::is_port_separator(int p_index) const {
return false;
}
@@ -93,6 +109,52 @@ bool VisualShaderNode::is_generate_input_var(int p_port) const {
return true;
}
+bool VisualShaderNode::is_output_port_expandable(int p_port) const {
+ return false;
+}
+
+void VisualShaderNode::_set_output_ports_expanded(const Array &p_values) {
+ for (int i = 0; i < p_values.size(); i++) {
+ expanded_output_ports[p_values[i]] = true;
+ }
+ emit_changed();
+}
+
+Array VisualShaderNode::_get_output_ports_expanded() const {
+ Array arr;
+ for (int i = 0; i < get_output_port_count(); i++) {
+ if (_is_output_port_expanded(i)) {
+ arr.push_back(i);
+ }
+ }
+ return arr;
+}
+
+void VisualShaderNode::_set_output_port_expanded(int p_port, bool p_expanded) {
+ expanded_output_ports[p_port] = p_expanded;
+ emit_changed();
+}
+
+bool VisualShaderNode::_is_output_port_expanded(int p_port) const {
+ if (expanded_output_ports.has(p_port)) {
+ return expanded_output_ports[p_port];
+ }
+ return false;
+}
+
+int VisualShaderNode::get_expanded_output_port_count() const {
+ int count = get_output_port_count();
+ int count2 = count;
+ for (int i = 0; i < count; i++) {
+ if (is_output_port_expandable(i) && _is_output_port_expanded(i)) {
+ if (get_output_port_type(i) == PORT_TYPE_VECTOR) {
+ count2 += 3;
+ }
+ }
+ }
+ return count2;
+}
+
bool VisualShaderNode::is_code_generated() const {
return true;
}
@@ -105,6 +167,14 @@ bool VisualShaderNode::is_use_prop_slots() const {
return false;
}
+bool VisualShaderNode::is_disabled() const {
+ return disabled;
+}
+
+void VisualShaderNode::set_disabled(bool p_disabled) {
+ disabled = p_disabled;
+}
+
Vector<VisualShader::DefaultTextureParam> VisualShaderNode::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
return Vector<VisualShader::DefaultTextureParam>();
}
@@ -156,14 +226,24 @@ void VisualShaderNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_output_port_for_preview", "port"), &VisualShaderNode::set_output_port_for_preview);
ClassDB::bind_method(D_METHOD("get_output_port_for_preview"), &VisualShaderNode::get_output_port_for_preview);
+ ClassDB::bind_method(D_METHOD("_set_output_port_expanded", "port"), &VisualShaderNode::_set_output_port_expanded);
+ ClassDB::bind_method(D_METHOD("_is_output_port_expanded"), &VisualShaderNode::_is_output_port_expanded);
+
+ ClassDB::bind_method(D_METHOD("_set_output_ports_expanded", "values"), &VisualShaderNode::_set_output_ports_expanded);
+ ClassDB::bind_method(D_METHOD("_get_output_ports_expanded"), &VisualShaderNode::_get_output_ports_expanded);
+
ClassDB::bind_method(D_METHOD("set_input_port_default_value", "port", "value"), &VisualShaderNode::set_input_port_default_value);
ClassDB::bind_method(D_METHOD("get_input_port_default_value", "port"), &VisualShaderNode::get_input_port_default_value);
+ ClassDB::bind_method(D_METHOD("remove_input_port_default_value", "port"), &VisualShaderNode::remove_input_port_default_value);
+ ClassDB::bind_method(D_METHOD("clear_default_input_values"), &VisualShaderNode::clear_default_input_values);
+
ClassDB::bind_method(D_METHOD("set_default_input_values", "values"), &VisualShaderNode::set_default_input_values);
ClassDB::bind_method(D_METHOD("get_default_input_values"), &VisualShaderNode::get_default_input_values);
ADD_PROPERTY(PropertyInfo(Variant::INT, "output_port_for_preview"), "set_output_port_for_preview", "get_output_port_for_preview");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "default_input_values", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_default_input_values", "get_default_input_values");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "expanded_output_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_output_ports_expanded", "_get_output_ports_expanded");
ADD_SIGNAL(MethodInfo("editor_refresh_request"));
BIND_ENUM_CONSTANT(PORT_TYPE_SCALAR);
@@ -181,54 +261,47 @@ VisualShaderNode::VisualShaderNode() {
/////////////////////////////////////////////////////////
void VisualShaderNodeCustom::update_ports() {
- ERR_FAIL_COND(!get_script_instance());
+ {
+ input_ports.clear();
+ int input_port_count;
+ if (GDVIRTUAL_CALL(_get_input_port_count, input_port_count)) {
+ for (int i = 0; i < input_port_count; i++) {
+ Port port;
+ if (!GDVIRTUAL_CALL(_get_input_port_name, i, port.name)) {
+ port.name = "in" + itos(i);
+ }
+ if (!GDVIRTUAL_CALL(_get_input_port_type, i, port.type)) {
+ port.type = (int)PortType::PORT_TYPE_SCALAR;
+ }
- input_ports.clear();
- if (get_script_instance()->has_method("_get_input_port_count")) {
- int input_port_count = (int)get_script_instance()->call("_get_input_port_count");
- bool has_name = get_script_instance()->has_method("_get_input_port_name");
- bool has_type = get_script_instance()->has_method("_get_input_port_type");
- for (int i = 0; i < input_port_count; i++) {
- Port port;
- if (has_name) {
- port.name = (String)get_script_instance()->call("_get_input_port_name", i);
- } else {
- port.name = "in" + itos(i);
+ input_ports.push_back(port);
}
- if (has_type) {
- port.type = (int)get_script_instance()->call("_get_input_port_type", i);
- } else {
- port.type = (int)PortType::PORT_TYPE_SCALAR;
- }
- input_ports.push_back(port);
}
}
- output_ports.clear();
- if (get_script_instance()->has_method("_get_output_port_count")) {
- int output_port_count = (int)get_script_instance()->call("_get_output_port_count");
- bool has_name = get_script_instance()->has_method("_get_output_port_name");
- bool has_type = get_script_instance()->has_method("_get_output_port_type");
- for (int i = 0; i < output_port_count; i++) {
- Port port;
- if (has_name) {
- port.name = (String)get_script_instance()->call("_get_output_port_name", i);
- } else {
- port.name = "out" + itos(i);
- }
- if (has_type) {
- port.type = (int)get_script_instance()->call("_get_output_port_type", i);
- } else {
- port.type = (int)PortType::PORT_TYPE_SCALAR;
+
+ {
+ output_ports.clear();
+ int output_port_count;
+ if (GDVIRTUAL_CALL(_get_output_port_count, output_port_count)) {
+ for (int i = 0; i < output_port_count; i++) {
+ Port port;
+ if (!GDVIRTUAL_CALL(_get_output_port_name, i, port.name)) {
+ port.name = "out" + itos(i);
+ }
+ if (!GDVIRTUAL_CALL(_get_output_port_type, i, port.type)) {
+ port.type = (int)PortType::PORT_TYPE_SCALAR;
+ }
+
+ output_ports.push_back(port);
}
- output_ports.push_back(port);
}
}
}
String VisualShaderNodeCustom::get_caption() const {
- ERR_FAIL_COND_V(!get_script_instance(), "");
- if (get_script_instance()->has_method("_get_name")) {
- return (String)get_script_instance()->call("_get_name");
+ String ret;
+ if (GDVIRTUAL_CALL(_get_name, ret)) {
+ return ret;
}
return "Unnamed";
}
@@ -262,9 +335,8 @@ String VisualShaderNodeCustom::get_output_port_name(int p_port) const {
}
String VisualShaderNodeCustom::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 {
- ERR_FAIL_COND_V(!get_script_instance(), "");
- ERR_FAIL_COND_V(!get_script_instance()->has_method("_get_code"), "");
- Array input_vars;
+ ERR_FAIL_COND_V(!GDVIRTUAL_IS_OVERRIDDEN(_get_code), "");
+ Vector<String> input_vars;
for (int i = 0; i < get_input_port_count(); i++) {
input_vars.push_back(p_input_vars[i]);
}
@@ -272,14 +344,15 @@ String VisualShaderNodeCustom::generate_code(Shader::Mode p_mode, VisualShader::
for (int i = 0; i < get_output_port_count(); i++) {
output_vars.push_back(p_output_vars[i]);
}
- String code = "\t{\n";
- String _code = (String)get_script_instance()->call("_get_code", input_vars, output_vars, (int)p_mode, (int)p_type);
+ String code = " {\n";
+ String _code;
+ GDVIRTUAL_CALL(_get_code, input_vars, output_vars, (int)p_mode, (int)p_type, _code);
bool nend = _code.ends_with("\n");
- _code = _code.insert(0, "\t\t");
- _code = _code.replace("\n", "\n\t\t");
+ _code = _code.insert(0, " ");
+ _code = _code.replace("\n", "\n ");
code += _code;
if (!nend) {
- code += "\n\t}";
+ code += "\n }";
} else {
code.remove(code.size() - 1);
code += "}";
@@ -289,30 +362,72 @@ String VisualShaderNodeCustom::generate_code(Shader::Mode p_mode, VisualShader::
}
String VisualShaderNodeCustom::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
- ERR_FAIL_COND_V(!get_script_instance(), "");
- if (get_script_instance()->has_method("_get_global_code")) {
+ String ret;
+ if (GDVIRTUAL_CALL(_get_global_code, (int)p_mode, ret)) {
String code = "// " + get_caption() + "\n";
- code += (String)get_script_instance()->call("_get_global_code", (int)p_mode);
+ code += ret;
code += "\n";
return code;
}
return "";
}
+void VisualShaderNodeCustom::set_input_port_default_value(int p_port, const Variant &p_value) {
+ if (!is_initialized) {
+ VisualShaderNode::set_input_port_default_value(p_port, p_value);
+ }
+}
+
+void VisualShaderNodeCustom::set_default_input_values(const Array &p_values) {
+ if (!is_initialized) {
+ VisualShaderNode::set_default_input_values(p_values);
+ }
+}
+
+void VisualShaderNodeCustom::remove_input_port_default_value(int p_port) {
+ if (!is_initialized) {
+ VisualShaderNode::remove_input_port_default_value(p_port);
+ }
+}
+
+void VisualShaderNodeCustom::clear_default_input_values() {
+ if (!is_initialized) {
+ VisualShaderNode::clear_default_input_values();
+ }
+}
+
+void VisualShaderNodeCustom::_set_input_port_default_value(int p_port, const Variant &p_value) {
+ VisualShaderNode::set_input_port_default_value(p_port, p_value);
+}
+
+bool VisualShaderNodeCustom::_is_initialized() {
+ return is_initialized;
+}
+
+void VisualShaderNodeCustom::_set_initialized(bool p_enabled) {
+ is_initialized = p_enabled;
+}
+
void VisualShaderNodeCustom::_bind_methods() {
- BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_name"));
- BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_description"));
- BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_category"));
- BIND_VMETHOD(MethodInfo(Variant::INT, "_get_return_icon_type"));
- BIND_VMETHOD(MethodInfo(Variant::INT, "_get_input_port_count"));
- BIND_VMETHOD(MethodInfo(Variant::INT, "_get_input_port_type", PropertyInfo(Variant::INT, "port")));
- BIND_VMETHOD(MethodInfo(Variant::STRING_NAME, "_get_input_port_name", PropertyInfo(Variant::INT, "port")));
- BIND_VMETHOD(MethodInfo(Variant::INT, "_get_output_port_count"));
- BIND_VMETHOD(MethodInfo(Variant::INT, "_get_output_port_type", PropertyInfo(Variant::INT, "port")));
- BIND_VMETHOD(MethodInfo(Variant::STRING_NAME, "_get_output_port_name", PropertyInfo(Variant::INT, "port")));
- BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_code", PropertyInfo(Variant::ARRAY, "input_vars"), PropertyInfo(Variant::ARRAY, "output_vars"), PropertyInfo(Variant::INT, "mode"), PropertyInfo(Variant::INT, "type")));
- BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_global_code", PropertyInfo(Variant::INT, "mode")));
- BIND_VMETHOD(MethodInfo(Variant::BOOL, "_is_highend"));
+ GDVIRTUAL_BIND(_get_name);
+ GDVIRTUAL_BIND(_get_description);
+ GDVIRTUAL_BIND(_get_category);
+ GDVIRTUAL_BIND(_get_return_icon_type);
+ GDVIRTUAL_BIND(_get_input_port_count);
+ GDVIRTUAL_BIND(_get_input_port_type, "port");
+ GDVIRTUAL_BIND(_get_input_port_name, "port");
+ GDVIRTUAL_BIND(_get_output_port_count);
+ GDVIRTUAL_BIND(_get_output_port_type, "port");
+ GDVIRTUAL_BIND(_get_output_port_name, "port");
+ GDVIRTUAL_BIND(_get_code, "input_vars", "output_vars", "mode", "type");
+ GDVIRTUAL_BIND(_get_global_code, "mode");
+ GDVIRTUAL_BIND(_is_highend);
+
+ ClassDB::bind_method(D_METHOD("_set_initialized", "enabled"), &VisualShaderNodeCustom::_set_initialized);
+ ClassDB::bind_method(D_METHOD("_is_initialized"), &VisualShaderNodeCustom::_is_initialized);
+ ClassDB::bind_method(D_METHOD("_set_input_port_default_value", "port", "value"), &VisualShaderNodeCustom::_set_input_port_default_value);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "initialized", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_initialized", "_is_initialized");
}
VisualShaderNodeCustom::VisualShaderNodeCustom() {
@@ -329,18 +444,29 @@ VisualShader::Type VisualShader::get_shader_type() const {
return current_type;
}
-void VisualShader::set_version(const String &p_version) {
- version = p_version;
+void VisualShader::set_engine_version(const Dictionary &p_engine_version) {
+ ERR_FAIL_COND(!p_engine_version.has("major"));
+ ERR_FAIL_COND(!p_engine_version.has("minor"));
+ engine_version["major"] = p_engine_version["major"];
+ engine_version["minor"] = p_engine_version["minor"];
}
-String VisualShader::get_version() const {
- return version;
+Dictionary VisualShader::get_engine_version() const {
+ return engine_version;
}
-void VisualShader::update_version(const String &p_new_version) {
- if (version == "") {
+#ifndef DISABLE_DEPRECATED
+
+void VisualShader::update_engine_version(const Dictionary &p_new_version) {
+ if (engine_version.is_empty()) { // before 4.0
for (int i = 0; i < TYPE_MAX; i++) {
for (Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) {
+ Ref<VisualShaderNodeInput> input = Object::cast_to<VisualShaderNodeInput>(E->get().node.ptr());
+ if (input.is_valid()) {
+ if (input->get_input_name() == "side") {
+ input->set_input_name("front_facing");
+ }
+ }
Ref<VisualShaderNodeExpression> expression = Object::cast_to<VisualShaderNodeExpression>(E->get().node.ptr());
if (expression.is_valid()) {
for (int j = 0; j < expression->get_input_port_count(); j++) {
@@ -369,9 +495,11 @@ void VisualShader::update_version(const String &p_new_version) {
}
}
}
- set_version(p_new_version);
+ set_engine_version(p_new_version);
}
+#endif /* DISABLE_DEPRECATED */
+
void VisualShader::add_node(Type p_type, const Ref<VisualShaderNode> &p_node, const Vector2 &p_position, int p_id) {
ERR_FAIL_COND(p_node.is_null());
ERR_FAIL_COND(p_id < 2);
@@ -486,12 +614,28 @@ void VisualShader::remove_node(Type p_type, int p_id) {
_queue_update();
}
+void VisualShader::replace_node(Type p_type, int p_id, const StringName &p_new_class) {
+ ERR_FAIL_INDEX(p_type, TYPE_MAX);
+ ERR_FAIL_COND(p_id < 2);
+ Graph *g = &graph[p_type];
+ ERR_FAIL_COND(!g->nodes.has(p_id));
+
+ if (g->nodes[p_id].node->get_class_name() == p_new_class) {
+ return;
+ }
+ VisualShaderNode *vsn = Object::cast_to<VisualShaderNode>(ClassDB::instantiate(p_new_class));
+ vsn->connect("changed", callable_mp(this, &VisualShader::_queue_update));
+ g->nodes[p_id].node = Ref<VisualShaderNode>(vsn);
+
+ _queue_update();
+}
+
bool VisualShader::is_node_connection(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) const {
ERR_FAIL_INDEX_V(p_type, TYPE_MAX, false);
const Graph *g = &graph[p_type];
- for (const List<Connection>::Element *E = g->connections.front(); E; E = E->next()) {
- if (E->get().from_node == p_from_node && E->get().from_port == p_from_port && E->get().to_node == p_to_node && E->get().to_port == p_to_port) {
+ for (const Connection &E : g->connections) {
+ if (E.from_node == p_from_node && E.from_port == p_from_port && E.to_node == p_to_node && E.to_port == p_to_port) {
return true;
}
}
@@ -504,12 +648,12 @@ bool VisualShader::is_nodes_connected_relatively(const Graph *p_graph, int p_nod
const VisualShader::Node &node = p_graph->nodes[p_node];
- for (const List<int>::Element *E = node.prev_connected_nodes.front(); E; E = E->next()) {
- if (E->get() == p_target) {
+ for (const int &E : node.prev_connected_nodes) {
+ if (E == p_target) {
return true;
}
- result = is_nodes_connected_relatively(p_graph, E->get(), p_target);
+ result = is_nodes_connected_relatively(p_graph, E, p_target);
if (result) {
break;
}
@@ -529,7 +673,7 @@ bool VisualShader::can_connect_nodes(Type p_type, int p_from_node, int p_from_po
return false;
}
- if (p_from_port < 0 || p_from_port >= g->nodes[p_from_node].node->get_output_port_count()) {
+ if (p_from_port < 0 || p_from_port >= g->nodes[p_from_node].node->get_expanded_output_port_count()) {
return false;
}
@@ -548,8 +692,8 @@ bool VisualShader::can_connect_nodes(Type p_type, int p_from_node, int p_from_po
return false;
}
- for (const List<Connection>::Element *E = g->connections.front(); E; E = E->next()) {
- if (E->get().from_node == p_from_node && E->get().from_port == p_from_port && E->get().to_node == p_to_node && E->get().to_port == p_to_port) {
+ for (const Connection &E : g->connections) {
+ if (E.from_node == p_from_node && E.from_port == p_from_port && E.to_node == p_to_node && E.to_port == p_to_port) {
return false;
}
}
@@ -568,6 +712,12 @@ bool VisualShader::is_port_types_compatible(int p_a, int p_b) const {
void VisualShader::connect_nodes_forced(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) {
ERR_FAIL_INDEX(p_type, TYPE_MAX);
Graph *g = &graph[p_type];
+
+ ERR_FAIL_COND(!g->nodes.has(p_from_node));
+ ERR_FAIL_INDEX(p_from_port, g->nodes[p_from_node].node->get_expanded_output_port_count());
+ ERR_FAIL_COND(!g->nodes.has(p_to_node));
+ ERR_FAIL_INDEX(p_to_port, g->nodes[p_to_node].node->get_input_port_count());
+
Connection c;
c.from_node = p_from_node;
c.from_port = p_from_port;
@@ -586,7 +736,7 @@ Error VisualShader::connect_nodes(Type p_type, int p_from_node, int p_from_port,
Graph *g = &graph[p_type];
ERR_FAIL_COND_V(!g->nodes.has(p_from_node), ERR_INVALID_PARAMETER);
- ERR_FAIL_INDEX_V(p_from_port, g->nodes[p_from_node].node->get_output_port_count(), ERR_INVALID_PARAMETER);
+ ERR_FAIL_INDEX_V(p_from_port, g->nodes[p_from_node].node->get_expanded_output_port_count(), ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(!g->nodes.has(p_to_node), ERR_INVALID_PARAMETER);
ERR_FAIL_INDEX_V(p_to_port, g->nodes[p_to_node].node->get_input_port_count(), ERR_INVALID_PARAMETER);
@@ -595,8 +745,8 @@ Error VisualShader::connect_nodes(Type p_type, int p_from_node, int p_from_port,
ERR_FAIL_COND_V_MSG(!is_port_types_compatible(from_port_type, to_port_type), ERR_INVALID_PARAMETER, "Incompatible port types (scalar/vec/bool) with transform.");
- for (List<Connection>::Element *E = g->connections.front(); E; E = E->next()) {
- if (E->get().from_node == p_from_node && E->get().from_port == p_from_port && E->get().to_node == p_to_node && E->get().to_port == p_to_port) {
+ for (const Connection &E : g->connections) {
+ if (E.from_node == p_from_node && E.from_port == p_from_port && E.to_node == p_to_node && E.to_port == p_to_port) {
ERR_FAIL_V(ERR_ALREADY_EXISTS);
}
}
@@ -619,7 +769,7 @@ void VisualShader::disconnect_nodes(Type p_type, int p_from_node, int p_from_por
ERR_FAIL_INDEX(p_type, TYPE_MAX);
Graph *g = &graph[p_type];
- for (List<Connection>::Element *E = g->connections.front(); E; E = E->next()) {
+ for (const List<Connection>::Element *E = g->connections.front(); E; E = E->next()) {
if (E->get().from_node == p_from_node && E->get().from_port == p_from_port && E->get().to_node == p_to_node && E->get().to_port == p_to_port) {
g->connections.erase(E);
g->nodes[p_to_node].prev_connected_nodes.erase(p_from_node);
@@ -636,12 +786,12 @@ Array VisualShader::_get_node_connections(Type p_type) const {
const Graph *g = &graph[p_type];
Array ret;
- for (const List<Connection>::Element *E = g->connections.front(); E; E = E->next()) {
+ for (const Connection &E : g->connections) {
Dictionary d;
- d["from_node"] = E->get().from_node;
- d["from_port"] = E->get().from_port;
- d["to_node"] = E->get().to_node;
- d["to_port"] = E->get().to_port;
+ d["from_node"] = E.from_node;
+ d["from_port"] = E.from_port;
+ d["to_node"] = E.to_node;
+ d["to_port"] = E.to_port;
ret.push_back(d);
}
@@ -652,12 +802,14 @@ void VisualShader::get_node_connections(Type p_type, List<Connection> *r_connect
ERR_FAIL_INDEX(p_type, TYPE_MAX);
const Graph *g = &graph[p_type];
- for (const List<Connection>::Element *E = g->connections.front(); E; E = E->next()) {
- r_connections->push_back(E->get());
+ for (const Connection &E : g->connections) {
+ r_connections->push_back(E);
}
}
void VisualShader::set_mode(Mode p_mode) {
+ ERR_FAIL_INDEX_MSG(p_mode, Mode::MODE_MAX, vformat("Invalid shader mode: %d.", p_mode));
+
if (shader_mode == p_mode) {
return;
}
@@ -713,7 +865,7 @@ void VisualShader::set_mode(Mode p_mode) {
}
_queue_update();
- _change_notify();
+ notify_property_list_changed();
}
void VisualShader::set_graph_offset(const Vector2 &p_offset) {
@@ -735,7 +887,7 @@ bool VisualShader::is_text_shader() const {
String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port, Vector<DefaultTextureParam> &default_tex_params) const {
Ref<VisualShaderNode> node = get_node(p_type, p_node);
ERR_FAIL_COND_V(!node.is_valid(), String());
- ERR_FAIL_COND_V(p_port < 0 || p_port >= node->get_output_port_count(), String());
+ ERR_FAIL_COND_V(p_port < 0 || p_port >= node->get_expanded_output_port_count(), String());
ERR_FAIL_COND_V(node->get_output_port_type(p_port) == VisualShaderNode::PORT_TYPE_TRANSFORM, String());
StringBuilder global_code;
@@ -754,7 +906,7 @@ String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port
String expr = "";
expr += "// " + global_expression->get_caption() + ":" + itos(index++) + "\n";
expr += global_expression->generate_global(get_mode(), Type(i), -1);
- expr = expr.replace("\n", "\n\t");
+ expr = expr.replace("\n", "\n ");
expr += "\n";
global_expressions += expr;
}
@@ -789,13 +941,13 @@ String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port
ERR_FAIL_COND_V(err != OK, String());
if (node->get_output_port_type(p_port) == VisualShaderNode::PORT_TYPE_SCALAR) {
- code += "\tCOLOR.rgb = vec3(n_out" + itos(p_node) + "p" + itos(p_port) + " );\n";
+ code += " COLOR.rgb = vec3(n_out" + itos(p_node) + "p" + itos(p_port) + " );\n";
} else if (node->get_output_port_type(p_port) == VisualShaderNode::PORT_TYPE_SCALAR_INT) {
- code += "\tCOLOR.rgb = vec3(float(n_out" + itos(p_node) + "p" + itos(p_port) + "));\n";
+ code += " COLOR.rgb = vec3(float(n_out" + itos(p_node) + "p" + itos(p_port) + "));\n";
} else if (node->get_output_port_type(p_port) == VisualShaderNode::PORT_TYPE_BOOLEAN) {
- code += "\tCOLOR.rgb = vec3(n_out" + itos(p_node) + "p" + itos(p_port) + " ? 1.0 : 0.0);\n";
+ code += " COLOR.rgb = vec3(n_out" + itos(p_node) + "p" + itos(p_port) + " ? 1.0 : 0.0);\n";
} else {
- code += "\tCOLOR.rgb = n_out" + itos(p_node) + "p" + itos(p_port) + ";\n";
+ code += " COLOR.rgb = n_out" + itos(p_node) + "p" + itos(p_port) + ";\n";
}
code += "}\n";
@@ -811,8 +963,12 @@ String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port
#define IS_SYMBOL_CHAR(m_d) (((m_d) >= 'a' && (m_d) <= 'z') || ((m_d) >= 'A' && (m_d) <= 'Z') || ((m_d) >= '0' && (m_d) <= '9') || (m_d) == '_')
-String VisualShader::validate_port_name(const String &p_name, const List<String> &p_input_ports, const List<String> &p_output_ports) const {
- String name = p_name;
+String VisualShader::validate_port_name(const String &p_port_name, VisualShaderNode *p_node, int p_port_id, bool p_output) const {
+ String name = p_port_name;
+
+ if (name == String()) {
+ return String();
+ }
while (name.length() && !IS_INITIAL_CHAR(name[0])) {
name = name.substr(1, name.length() - 1);
@@ -830,29 +986,28 @@ String VisualShader::validate_port_name(const String &p_name, const List<String>
}
name = valid_name;
+ } else {
+ return String();
}
- String valid_name = name;
- bool is_equal = false;
+ List<String> input_names;
+ List<String> output_names;
- for (int i = 0; i < p_input_ports.size(); i++) {
- if (name == p_input_ports[i]) {
- is_equal = true;
- break;
+ for (int i = 0; i < p_node->get_input_port_count(); i++) {
+ if (!p_output && i == p_port_id) {
+ continue;
}
- }
-
- if (!is_equal) {
- for (int i = 0; i < p_output_ports.size(); i++) {
- if (name == p_output_ports[i]) {
- is_equal = true;
- break;
- }
+ if (name == p_node->get_input_port_name(i)) {
+ return String();
}
}
-
- if (is_equal) {
- name = "";
+ for (int i = 0; i < p_node->get_output_port_count(); i++) {
+ if (p_output && i == p_port_id) {
+ continue;
+ }
+ if (name == p_node->get_output_port_name(i)) {
+ return String();
+ }
}
return name;
@@ -931,9 +1086,12 @@ static const char *type_string[VisualShader::TYPE_MAX] = {
"vertex",
"fragment",
"light",
- "emit",
+ "start",
"process",
- "end"
+ "collide",
+ "start_custom",
+ "process_custom",
+ "sky",
};
bool VisualShader::_set(const StringName &p_name, const Variant &p_value) {
@@ -992,7 +1150,7 @@ bool VisualShader::_set(const StringName &p_name, const Variant &p_value) {
set_node_position(type, id, p_value);
return true;
} else if (what == "size") {
- ((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->set_size(p_value);
+ ((VisualShaderNodeResizableBase *)get_node(type, id).ptr())->set_size(p_value);
return true;
} else if (what == "input_ports") {
((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->set_inputs(p_value);
@@ -1038,11 +1196,11 @@ bool VisualShader::_get(const StringName &p_name, Variant &r_ret) const {
String index = name.get_slicec('/', 2);
if (index == "connections") {
Vector<int> conns;
- for (const List<Connection>::Element *E = graph[type].connections.front(); E; E = E->next()) {
- conns.push_back(E->get().from_node);
- conns.push_back(E->get().from_port);
- conns.push_back(E->get().to_node);
- conns.push_back(E->get().to_port);
+ for (const Connection &E : graph[type].connections) {
+ conns.push_back(E.from_node);
+ conns.push_back(E.from_port);
+ conns.push_back(E.to_node);
+ conns.push_back(E.to_port);
}
r_ret = conns;
@@ -1059,7 +1217,7 @@ bool VisualShader::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = get_node_position(type, id);
return true;
} else if (what == "size") {
- r_ret = ((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->get_size();
+ r_ret = ((VisualShaderNodeResizableBase *)get_node(type, id).ptr())->get_size();
return true;
} else if (what == "input_ports") {
r_ret = ((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->get_inputs();
@@ -1075,6 +1233,12 @@ bool VisualShader::_get(const StringName &p_name, Variant &r_ret) const {
return false;
}
+void VisualShader::reset_state() {
+#ifndef _MSC_VER
+#warning everything needs to be cleared here
+#endif
+ emit_changed();
+}
void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const {
//mode
p_list->push_back(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Node3D,CanvasItem,Particles,Sky"));
@@ -1142,6 +1306,12 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const {
Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBuilder &global_code_per_node, Map<Type, StringBuilder> &global_code_per_func, StringBuilder &code, Vector<VisualShader::DefaultTextureParam> &def_tex_params, const VMap<ConnectionKey, const List<Connection>::Element *> &input_connections, const VMap<ConnectionKey, const List<Connection>::Element *> &output_connections, int node, Set<int> &processed, bool for_preview, Set<StringName> &r_classes) const {
const Ref<VisualShaderNode> vsnode = graph[type].nodes[node].node;
+ if (vsnode->is_disabled()) {
+ code += "// " + vsnode->get_caption() + ":" + itos(node) + "\n";
+ code += " // Node is disabled and code is not generated.\n";
+ return OK;
+ }
+
//check inputs recursively first
int input_count = vsnode->get_input_port_count();
for (int i = 0; i < input_count; i++) {
@@ -1196,7 +1366,8 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
return OK;
}
- code += "// " + vsnode->get_caption() + ":" + itos(node) + "\n";
+ String node_name = "// " + vsnode->get_caption() + ":" + itos(node) + "\n";
+ String node_code;
Vector<String> input_vars;
input_vars.resize(vsnode->get_input_port_count());
@@ -1210,6 +1381,11 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
if (input_connections.has(ck)) {
//connected to something, use that output
int from_node = input_connections[ck]->get().from_node;
+
+ if (graph[type].nodes[from_node].node->is_disabled()) {
+ continue;
+ }
+
int from_port = input_connections[ck]->get().from_port;
VisualShaderNode::PortType in_type = vsnode->get_input_port_type(i);
@@ -1243,9 +1419,9 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
} else if (in_type == VisualShaderNode::PORT_TYPE_BOOLEAN && out_type == VisualShaderNode::PORT_TYPE_SCALAR_INT) {
inputs[i] = src_var + " > 0 ? true : false";
} else if (in_type == VisualShaderNode::PORT_TYPE_SCALAR && out_type == VisualShaderNode::PORT_TYPE_BOOLEAN) {
- inputs[i] = src_var + " ? 1.0 : 0.0";
+ inputs[i] = "(" + src_var + " ? 1.0 : 0.0)";
} else if (in_type == VisualShaderNode::PORT_TYPE_SCALAR_INT && out_type == VisualShaderNode::PORT_TYPE_BOOLEAN) {
- inputs[i] = src_var + " ? 1 : 0";
+ inputs[i] = "(" + src_var + " ? 1 : 0)";
} else if (in_type == VisualShaderNode::PORT_TYPE_VECTOR && out_type == VisualShaderNode::PORT_TYPE_BOOLEAN) {
inputs[i] = "vec3(" + src_var + " ? 1.0 : 0.0)";
} else if (in_type == VisualShaderNode::PORT_TYPE_SCALAR && out_type == VisualShaderNode::PORT_TYPE_SCALAR_INT) {
@@ -1262,21 +1438,21 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
if (defval.get_type() == Variant::FLOAT) {
float val = defval;
inputs[i] = "n_in" + itos(node) + "p" + itos(i);
- code += "\tfloat " + inputs[i] + " = " + vformat("%.5f", val) + ";\n";
+ node_code += " float " + inputs[i] + " = " + vformat("%.5f", val) + ";\n";
} else if (defval.get_type() == Variant::INT) {
int val = defval;
inputs[i] = "n_in" + itos(node) + "p" + itos(i);
- code += "\tint " + inputs[i] + " = " + itos(val) + ";\n";
+ node_code += " int " + inputs[i] + " = " + itos(val) + ";\n";
} else if (defval.get_type() == Variant::BOOL) {
bool val = defval;
inputs[i] = "n_in" + itos(node) + "p" + itos(i);
- code += "\tbool " + inputs[i] + " = " + (val ? "true" : "false") + ";\n";
+ node_code += " bool " + inputs[i] + " = " + (val ? "true" : "false") + ";\n";
} else if (defval.get_type() == Variant::VECTOR3) {
Vector3 val = defval;
inputs[i] = "n_in" + itos(node) + "p" + itos(i);
- code += "\tvec3 " + inputs[i] + " = " + vformat("vec3(%.5f, %.5f, %.5f);\n", val.x, val.y, val.z);
- } else if (defval.get_type() == Variant::TRANSFORM) {
- Transform val = defval;
+ node_code += " vec3 " + inputs[i] + " = " + vformat("vec3(%.5f, %.5f, %.5f);\n", val.x, val.y, val.z);
+ } else if (defval.get_type() == Variant::TRANSFORM3D) {
+ Transform3D val = defval;
val.basis.transpose();
inputs[i] = "n_in" + itos(node) + "p" + itos(i);
Array values;
@@ -1289,7 +1465,7 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
values.push_back(val.origin.y);
values.push_back(val.origin.z);
bool err = false;
- code += "\tmat4 " + inputs[i] + " = " + String("mat4(vec4(%.5f, %.5f, %.5f, 0.0), vec4(%.5f, %.5f, %.5f, 0.0), vec4(%.5f, %.5f, %.5f, 0.0), vec4(%.5f, %.5f, %.5f, 1.0));\n").sprintf(values, &err);
+ node_code += " mat4 " + inputs[i] + " = " + String("mat4(vec4(%.5f, %.5f, %.5f, 0.0), vec4(%.5f, %.5f, %.5f, 0.0), vec4(%.5f, %.5f, %.5f, 0.0), vec4(%.5f, %.5f, %.5f, 1.0));\n").sprintf(values, &err);
} else {
//will go empty, node is expected to know what it is doing at this point and handle it
}
@@ -1297,13 +1473,30 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
}
int output_count = vsnode->get_output_port_count();
+ int initial_output_count = output_count;
+
+ Map<int, bool> expanded_output_ports;
+
+ for (int i = 0; i < initial_output_count; i++) {
+ bool expanded = false;
+
+ if (vsnode->is_output_port_expandable(i) && vsnode->_is_output_port_expanded(i)) {
+ expanded = true;
+
+ if (vsnode->get_output_port_type(i) == VisualShaderNode::PORT_TYPE_VECTOR) {
+ output_count += 3;
+ }
+ }
+ expanded_output_ports.insert(i, expanded);
+ }
+
Vector<String> output_vars;
- output_vars.resize(vsnode->get_output_port_count());
+ output_vars.resize(output_count);
String *outputs = output_vars.ptrw();
if (vsnode->is_simple_decl()) { // less code to generate for some simple_decl nodes
- for (int i = 0; i < output_count; i++) {
- String var_name = "n_out" + itos(node) + "p" + itos(i);
+ for (int i = 0, j = 0; i < initial_output_count; i++, j++) {
+ String var_name = "n_out" + itos(node) + "p" + itos(j);
switch (vsnode->get_output_port_type(i)) {
case VisualShaderNode::PORT_TYPE_SCALAR:
outputs[i] = "float " + var_name;
@@ -1323,34 +1516,88 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
default: {
}
}
+ if (expanded_output_ports[i]) {
+ if (vsnode->get_output_port_type(i) == VisualShaderNode::PORT_TYPE_VECTOR) {
+ j += 3;
+ }
+ }
}
} else {
- for (int i = 0; i < output_count; i++) {
- outputs[i] = "n_out" + itos(node) + "p" + itos(i);
+ for (int i = 0, j = 0; i < initial_output_count; i++, j++) {
+ outputs[i] = "n_out" + itos(node) + "p" + itos(j);
switch (vsnode->get_output_port_type(i)) {
case VisualShaderNode::PORT_TYPE_SCALAR:
- code += String() + "\tfloat " + outputs[i] + ";\n";
+ code += " float " + outputs[i] + ";\n";
break;
case VisualShaderNode::PORT_TYPE_SCALAR_INT:
- code += String() + "\tint " + outputs[i] + ";\n";
+ code += " int " + outputs[i] + ";\n";
break;
case VisualShaderNode::PORT_TYPE_VECTOR:
- code += String() + "\tvec3 " + outputs[i] + ";\n";
+ code += " vec3 " + outputs[i] + ";\n";
break;
case VisualShaderNode::PORT_TYPE_BOOLEAN:
- code += String() + "\tbool " + outputs[i] + ";\n";
+ code += " bool " + outputs[i] + ";\n";
break;
case VisualShaderNode::PORT_TYPE_TRANSFORM:
- code += String() + "\tmat4 " + outputs[i] + ";\n";
+ code += " mat4 " + outputs[i] + ";\n";
break;
default: {
}
}
+ if (expanded_output_ports[i]) {
+ if (vsnode->get_output_port_type(i) == VisualShaderNode::PORT_TYPE_VECTOR) {
+ j += 3;
+ }
+ }
}
}
- code += vsnode->generate_code(get_mode(), type, node, inputs, outputs, for_preview);
+ node_code += vsnode->generate_code(get_mode(), type, node, inputs, outputs, for_preview);
+ if (node_code != String()) {
+ code += node_name;
+ code += node_code;
+ code += "\n";
+ }
+
+ for (int i = 0; i < output_count; i++) {
+ bool new_line_inserted = false;
+ if (expanded_output_ports[i]) {
+ if (vsnode->get_output_port_type(i) == VisualShaderNode::PORT_TYPE_VECTOR) {
+ if (vsnode->is_output_port_connected(i + 1) || (for_preview && vsnode->get_output_port_for_preview() == (i + 1))) { // red-component
+ if (!new_line_inserted) {
+ code += "\n";
+ new_line_inserted = true;
+ }
+ String r = "n_out" + itos(node) + "p" + itos(i + 1);
+ code += " float " + r + " = n_out" + itos(node) + "p" + itos(i) + ".r;\n";
+ outputs[i + 1] = r;
+ }
+
+ if (vsnode->is_output_port_connected(i + 2) || (for_preview && vsnode->get_output_port_for_preview() == (i + 2))) { // green-component
+ if (!new_line_inserted) {
+ code += "\n";
+ new_line_inserted = true;
+ }
+ String g = "n_out" + itos(node) + "p" + itos(i + 2);
+ code += " float " + g + " = n_out" + itos(node) + "p" + itos(i) + ".g;\n";
+ outputs[i + 2] = g;
+ }
+
+ if (vsnode->is_output_port_connected(i + 3) || (for_preview && vsnode->get_output_port_for_preview() == (i + 3))) { // blue-component
+ if (!new_line_inserted) {
+ code += "\n";
+ new_line_inserted = true;
+ }
+ String b = "n_out" + itos(node) + "p" + itos(i + 3);
+ code += " float " + b + " = n_out" + itos(node) + "p" + itos(i) + ".b;\n";
+ outputs[i + 3] = b;
+ }
+
+ i += 3;
+ }
+ }
+ }
code += "\n"; //
processed.insert(node);
@@ -1361,7 +1608,7 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
bool VisualShader::has_func_name(RenderingServer::ShaderMode p_mode, const String &p_func_name) const {
if (!ShaderTypes::get_singleton()->get_functions(p_mode).has(p_func_name)) {
if (p_mode == RenderingServer::ShaderMode::SHADER_PARTICLES) {
- if (p_func_name == "emit" || p_func_name == "process" || p_func_name == "end") {
+ if (p_func_name == "start_custom" || p_func_name == "process_custom" || p_func_name == "collide") {
return true;
}
}
@@ -1372,11 +1619,11 @@ bool VisualShader::has_func_name(RenderingServer::ShaderMode p_mode, const Strin
}
void VisualShader::_update_shader() const {
- if (!dirty) {
+ if (!dirty.is_set()) {
return;
}
- dirty = false;
+ dirty.clear();
StringBuilder global_code;
StringBuilder global_code_per_node;
@@ -1394,19 +1641,10 @@ void VisualShader::_update_shader() const {
{
//fill render mode enums
int idx = 0;
- bool specular = false;
while (render_mode_enums[idx].string) {
if (shader_mode == render_mode_enums[idx].mode) {
- if (shader_mode == Shader::MODE_SPATIAL) {
- if (String(render_mode_enums[idx].string) == "specular") {
- specular = true;
- }
- }
- if (modes.has(render_mode_enums[idx].string) || specular) {
- int which = 0;
- if (modes.has(render_mode_enums[idx].string)) {
- which = modes[render_mode_enums[idx].string];
- }
+ if (modes.has(render_mode_enums[idx].string)) {
+ int which = modes[render_mode_enums[idx].string];
int count = 0;
for (int i = 0; i < ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader_mode)).size(); i++) {
String mode = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader_mode))[i];
@@ -1442,11 +1680,12 @@ void VisualShader::_update_shader() const {
global_code += "render_mode " + render_mode + ";\n\n";
}
- static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light", "emit", "process", "end" };
+ static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light", "start", "process", "collide", "start_custom", "process_custom", "sky" };
String global_expressions;
Set<String> used_uniform_names;
List<VisualShaderNodeUniform *> uniforms;
+ Map<int, List<int>> emitters;
for (int i = 0, index = 0; i < TYPE_MAX; i++) {
if (!has_func_name(RenderingServer::ShaderMode(shader_mode), func_name[i])) {
@@ -1459,7 +1698,7 @@ void VisualShader::_update_shader() const {
String expr = "";
expr += "// " + global_expression->get_caption() + ":" + itos(index++) + "\n";
expr += global_expression->generate_global(get_mode(), Type(i), -1);
- expr = expr.replace("\n", "\n\t");
+ expr = expr.replace("\n", "\n ");
expr += "\n";
global_expressions += expr;
}
@@ -1471,6 +1710,19 @@ void VisualShader::_update_shader() const {
if (uniform.is_valid()) {
uniforms.push_back(uniform.ptr());
}
+ Ref<VisualShaderNodeParticleEmit> emit_particle = Object::cast_to<VisualShaderNodeParticleEmit>(E->get().node.ptr());
+ if (emit_particle.is_valid()) {
+ if (!emitters.has(i)) {
+ emitters.insert(i, List<int>());
+ }
+
+ for (Map<int, Node>::Element *M = graph[i].nodes.front(); M; M = M->next()) {
+ if (M->get().node == emit_particle.ptr()) {
+ emitters[i].push_back(M->key());
+ break;
+ }
+ }
+ }
}
}
@@ -1485,6 +1737,7 @@ void VisualShader::_update_shader() const {
}
Map<int, String> code_map;
+ Set<int> empty_funcs;
for (int i = 0; i < TYPE_MAX; i++) {
if (!has_func_name(RenderingServer::ShaderMode(shader_mode), func_name[i])) {
@@ -1497,6 +1750,11 @@ void VisualShader::_update_shader() const {
StringBuilder func_code;
+ bool is_empty_func = false;
+ if (shader_mode != Shader::MODE_PARTICLES && shader_mode != Shader::MODE_SKY) {
+ is_empty_func = true;
+ }
+
for (const List<Connection>::Element *E = graph[i].connections.front(); E; E = E->next()) {
ConnectionKey from_key;
from_key.node = E->get().from_node;
@@ -1509,7 +1767,17 @@ void VisualShader::_update_shader() const {
to_key.port = E->get().to_port;
input_connections.insert(to_key, E);
+
+ if (is_empty_func && to_key.node == NODE_ID_OUTPUT) {
+ is_empty_func = false;
+ }
}
+
+ if (is_empty_func) {
+ empty_funcs.insert(i);
+ continue;
+ }
+
if (shader_mode != Shader::MODE_PARTICLES) {
func_code += "\nvoid " + String(func_name[i]) + "() {\n";
}
@@ -1519,6 +1787,13 @@ void VisualShader::_update_shader() const {
Error err = _write_node(Type(i), global_code, global_code_per_node, global_code_per_func, func_code, default_tex_params, input_connections, output_connections, NODE_ID_OUTPUT, processed, false, classes);
ERR_FAIL_COND(err != OK);
+ if (emitters.has(i)) {
+ for (int &E : emitters[i]) {
+ err = _write_node(Type(i), global_code, global_code_per_node, global_code_per_func, func_code, default_tex_params, input_connections, output_connections, E, processed, false, classes);
+ ERR_FAIL_COND(err != OK);
+ }
+ }
+
if (shader_mode == Shader::MODE_PARTICLES) {
code_map.insert(i, func_code);
} else {
@@ -1527,19 +1802,130 @@ void VisualShader::_update_shader() const {
}
}
+ String global_compute_code;
+
if (shader_mode == Shader::MODE_PARTICLES) {
- code += "\nvoid compute() {\n";
- code += "\tif (RESTART) {\n";
- code += code_map[TYPE_EMIT];
- code += "\t} else {\n";
- code += code_map[TYPE_PROCESS];
- code += "\t}\n";
- code += "}\n";
+ bool has_start = !code_map[TYPE_START].is_empty();
+ bool has_start_custom = !code_map[TYPE_START_CUSTOM].is_empty();
+ bool has_process = !code_map[TYPE_PROCESS].is_empty();
+ bool has_process_custom = !code_map[TYPE_PROCESS_CUSTOM].is_empty();
+ bool has_collide = !code_map[TYPE_COLLIDE].is_empty();
+
+ code += "void start() {\n";
+ if (has_start || has_start_custom) {
+ code += " uint __seed = __hash(NUMBER + uint(1) + RANDOM_SEED);\n";
+ code += " vec3 __diff = TRANSFORM[3].xyz - EMISSION_TRANSFORM[3].xyz;\n";
+ code += " float __radians;\n";
+ code += " vec3 __vec3_buff1;\n";
+ code += " vec3 __vec3_buff2;\n";
+ code += " float __scalar_buff1;\n";
+ code += " float __scalar_buff2;\n";
+ code += " vec3 __ndiff = normalize(__diff);\n\n";
+ }
+ if (has_start) {
+ code += " {\n";
+ code += code_map[TYPE_START].replace("\n ", "\n ");
+ code += " }\n";
+ if (has_start_custom) {
+ code += " \n";
+ }
+ }
+ if (has_start_custom) {
+ code += " {\n";
+ code += code_map[TYPE_START_CUSTOM].replace("\n ", "\n ");
+ code += " }\n";
+ }
+ code += "}\n\n";
+ code += "void process() {\n";
+ if (has_process || has_process_custom || has_collide) {
+ code += " uint __seed = __hash(NUMBER + uint(1) + RANDOM_SEED);\n";
+ code += " vec3 __vec3_buff1;\n";
+ code += " vec3 __diff = TRANSFORM[3].xyz - EMISSION_TRANSFORM[3].xyz;\n";
+ code += " vec3 __ndiff = normalize(__diff);\n\n";
+ }
+ code += " {\n";
+ String tab = " ";
+ if (has_collide) {
+ code += " if (COLLIDED) {\n\n";
+ code += code_map[TYPE_COLLIDE].replace("\n ", "\n ");
+ if (has_process) {
+ code += " } else {\n\n";
+ tab += " ";
+ }
+ }
+ if (has_process) {
+ code += code_map[TYPE_PROCESS].replace("\n ", "\n " + tab);
+ }
+ if (has_collide) {
+ code += " }\n";
+ }
+ code += " }\n";
+
+ if (has_process_custom) {
+ code += " {\n\n";
+ code += code_map[TYPE_PROCESS_CUSTOM].replace("\n ", "\n ");
+ code += " }\n";
+ }
+
+ code += "}\n\n";
+
+ global_compute_code += "float __rand_from_seed(inout uint seed) {\n";
+ global_compute_code += " int k;\n";
+ global_compute_code += " int s = int(seed);\n";
+ global_compute_code += " if (s == 0)\n";
+ global_compute_code += " s = 305420679;\n";
+ global_compute_code += " k = s / 127773;\n";
+ global_compute_code += " s = 16807 * (s - k * 127773) - 2836 * k;\n";
+ global_compute_code += " if (s < 0)\n";
+ global_compute_code += " s += 2147483647;\n";
+ global_compute_code += " seed = uint(s);\n";
+ global_compute_code += " return float(seed % uint(65536)) / 65535.0;\n";
+ global_compute_code += "}\n\n";
+
+ global_compute_code += "float __rand_from_seed_m1_p1(inout uint seed) {\n";
+ global_compute_code += " return __rand_from_seed(seed) * 2.0 - 1.0;\n";
+ global_compute_code += "}\n\n";
+
+ global_compute_code += "float __randf_range(inout uint seed, float from, float to) {\n";
+ global_compute_code += " return __rand_from_seed(seed) * (to - from) + from;\n";
+ global_compute_code += "}\n\n";
+
+ global_compute_code += "vec3 __randv_range(inout uint seed, vec3 from, vec3 to) {\n";
+ global_compute_code += " return vec3(__randf_range(seed, from.x, to.x), __randf_range(seed, from.y, to.y), __randf_range(seed, from.z, to.z));\n";
+ global_compute_code += "}\n\n";
+
+ global_compute_code += "uint __hash(uint x) {\n";
+ global_compute_code += " x = ((x >> uint(16)) ^ x) * uint(73244475);\n";
+ global_compute_code += " x = ((x >> uint(16)) ^ x) * uint(73244475);\n";
+ global_compute_code += " x = (x >> uint(16)) ^ x;\n";
+ global_compute_code += " return x;\n";
+ global_compute_code += "}\n\n";
+
+ global_compute_code += "mat3 __build_rotation_mat3(vec3 axis, float angle) {\n";
+ global_compute_code += " axis = normalize(axis);\n";
+ global_compute_code += " float s = sin(angle);\n";
+ global_compute_code += " float c = cos(angle);\n";
+ global_compute_code += " float oc = 1.0 - c;\n";
+ global_compute_code += " return mat3(vec3(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s), vec3(oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s), vec3(oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c));\n";
+ global_compute_code += "}\n\n";
+
+ global_compute_code += "mat4 __build_rotation_mat4(vec3 axis, float angle) {\n";
+ global_compute_code += " axis = normalize(axis);\n";
+ global_compute_code += " float s = sin(angle);\n";
+ global_compute_code += " float c = cos(angle);\n";
+ global_compute_code += " float oc = 1.0 - c;\n";
+ global_compute_code += " return mat4(vec4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0), vec4(oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0), vec4(oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0), vec4(0, 0, 0, 1));\n";
+ global_compute_code += "}\n\n";
+
+ global_compute_code += "vec3 __get_random_unit_vec3(inout uint seed) {\n";
+ global_compute_code += " return normalize(vec3(__rand_from_seed_m1_p1(seed), __rand_from_seed_m1_p1(seed), __rand_from_seed_m1_p1(seed)));\n";
+ global_compute_code += "}\n\n";
}
//set code secretly
global_code += "\n\n";
String final_code = global_code;
+ final_code += global_compute_code;
final_code += global_code_per_node;
final_code += global_expressions;
String tcode = code;
@@ -1547,7 +1933,11 @@ void VisualShader::_update_shader() const {
if (!has_func_name(RenderingServer::ShaderMode(shader_mode), func_name[i])) {
continue;
}
- tcode = tcode.insert(insertion_pos[i], global_code_per_func[Type(i)]);
+ String func_code = global_code_per_func[Type(i)].as_string();
+ if (empty_funcs.has(Type(i)) && !func_code.is_empty()) {
+ func_code = vformat("%s%s%s", String("\nvoid " + String(func_name[i]) + "() {\n"), func_code, "}\n");
+ }
+ tcode = tcode.insert(insertion_pos[i], func_code);
}
final_code += tcode;
@@ -1556,21 +1946,22 @@ void VisualShader::_update_shader() const {
const_cast<VisualShader *>(this)->set_default_texture_param(default_tex_params[i].name, default_tex_params[i].param);
}
if (previous_code != final_code) {
- const_cast<VisualShader *>(this)->emit_signal("changed");
+ const_cast<VisualShader *>(this)->emit_signal(SNAME("changed"));
}
previous_code = final_code;
}
void VisualShader::_queue_update() {
- if (dirty) {
+ if (dirty.is_set()) {
return;
}
- dirty = true;
- call_deferred("_update_shader");
+ dirty.set();
+ call_deferred(SNAME("_update_shader"));
}
void VisualShader::_input_type_changed(Type p_type, int p_id) {
+ ERR_FAIL_INDEX(p_type, TYPE_MAX);
//erase connections using this input, as type changed
Graph *g = &graph[p_type];
@@ -1585,7 +1976,7 @@ void VisualShader::_input_type_changed(Type p_type, int p_id) {
}
void VisualShader::rebuild() {
- dirty = true;
+ dirty.set();
_update_shader();
}
@@ -1602,6 +1993,7 @@ void VisualShader::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_valid_node_id", "type"), &VisualShader::get_valid_node_id);
ClassDB::bind_method(D_METHOD("remove_node", "type", "id"), &VisualShader::remove_node);
+ ClassDB::bind_method(D_METHOD("replace_node", "type", "id", "new_class"), &VisualShader::replace_node);
ClassDB::bind_method(D_METHOD("is_node_connection", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::is_node_connection);
ClassDB::bind_method(D_METHOD("can_connect_nodes", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::can_connect_nodes);
@@ -1612,8 +2004,8 @@ void VisualShader::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_node_connections", "type"), &VisualShader::_get_node_connections);
- ClassDB::bind_method(D_METHOD("set_version", "version"), &VisualShader::set_version);
- ClassDB::bind_method(D_METHOD("get_version"), &VisualShader::get_version);
+ ClassDB::bind_method(D_METHOD("set_engine_version", "version"), &VisualShader::set_engine_version);
+ ClassDB::bind_method(D_METHOD("get_engine_version"), &VisualShader::get_engine_version);
ClassDB::bind_method(D_METHOD("set_graph_offset", "offset"), &VisualShader::set_graph_offset);
ClassDB::bind_method(D_METHOD("get_graph_offset"), &VisualShader::get_graph_offset);
@@ -1621,16 +2013,19 @@ void VisualShader::_bind_methods() {
ClassDB::bind_method(D_METHOD("_update_shader"), &VisualShader::_update_shader);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_graph_offset", "get_graph_offset");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "version", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_version", "get_version");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "engine_version", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_engine_version", "get_engine_version");
ADD_PROPERTY_DEFAULT("code", ""); // Inherited from Shader, prevents showing default code as override in docs.
BIND_ENUM_CONSTANT(TYPE_VERTEX);
BIND_ENUM_CONSTANT(TYPE_FRAGMENT);
BIND_ENUM_CONSTANT(TYPE_LIGHT);
- BIND_ENUM_CONSTANT(TYPE_EMIT);
+ BIND_ENUM_CONSTANT(TYPE_START);
BIND_ENUM_CONSTANT(TYPE_PROCESS);
- BIND_ENUM_CONSTANT(TYPE_END);
+ BIND_ENUM_CONSTANT(TYPE_COLLIDE);
+ BIND_ENUM_CONSTANT(TYPE_START_CUSTOM);
+ BIND_ENUM_CONSTANT(TYPE_PROCESS_CUSTOM);
+ BIND_ENUM_CONSTANT(TYPE_SKY);
BIND_ENUM_CONSTANT(TYPE_MAX);
BIND_CONSTANT(NODE_ID_INVALID);
@@ -1638,12 +2033,22 @@ void VisualShader::_bind_methods() {
}
VisualShader::VisualShader() {
+ dirty.set();
for (int i = 0; i < TYPE_MAX; i++) {
- Ref<VisualShaderNodeOutput> output;
- output.instance();
- output->shader_type = Type(i);
- output->shader_mode = shader_mode;
- graph[i].nodes[NODE_ID_OUTPUT].node = output;
+ if (i > (int)TYPE_LIGHT && i < (int)TYPE_SKY) {
+ Ref<VisualShaderNodeParticleOutput> output;
+ output.instantiate();
+ output->shader_type = Type(i);
+ output->shader_mode = shader_mode;
+ graph[i].nodes[NODE_ID_OUTPUT].node = output;
+ } else {
+ Ref<VisualShaderNodeOutput> output;
+ output.instantiate();
+ output->shader_type = Type(i);
+ output->shader_mode = shader_mode;
+ graph[i].nodes[NODE_ID_OUTPUT].node = output;
+ }
+
graph[i].nodes[NODE_ID_OUTPUT].position = Vector2(400, 150);
}
}
@@ -1651,7 +2056,9 @@ VisualShader::VisualShader() {
///////////////////////////////////////////////////////////
const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
- // Spatial, Vertex
+ // Node3D
+
+ // Node3D, Vertex
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "TANGENT" },
@@ -1661,7 +2068,10 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" },
-
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_INT, "instance_id", "INSTANCE_ID" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "instance_custom", "INSTANCE_CUSTOM.rgb" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "instance_custom_alpha", "INSTANCE_CUSTOM.a" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "modelview", "MODELVIEW_MATRIX" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "camera", "CAMERA_MATRIX" },
@@ -1672,7 +2082,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(VIEWPORT_SIZE, 0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_BOOLEAN, "output_is_srgb", "OUTPUT_IS_SRGB" },
- // Spatial, Fragment
+ // Node3D, Fragment
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
@@ -1684,10 +2094,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "point_coord", "vec3(POINT_COORD, 0.0)" },
-
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "side", "float(FRONT_FACING ? 1.0 : 0.0)" },
-
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_camera", "INV_CAMERA_MATRIX" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "camera", "CAMERA_MATRIX" },
@@ -1698,21 +2105,24 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "output_is_srgb", "OUTPUT_IS_SRGB" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "front_facing", "FRONT_FACING" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "screen_texture", "SCREEN_TEXTURE" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "normal_roughness_texture", "NORMAL_ROUGHNESS_TEXTURE" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "depth_texture", "DEPTH_TEXTURE" },
- // Spatial, Light
+ // Node3D, Light
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV2, 0.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "view", "VIEW" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light", "LIGHT" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_color", "LIGHT_COLOR" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "attenuation", "ATTENUATION" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "attenuation", "ATTENUATION" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "albedo", "ALBEDO" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "transmission", "TRANSMISSION" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "backlight", "BACKLIGHT" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "diffuse", "DIFFUSE_LIGHT" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "specular", "SPECULAR_LIGHT" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" },
-
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "metallic", "METALLIC" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_camera", "INV_CAMERA_MATRIX" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "camera", "CAMERA_MATRIX" },
@@ -1721,19 +2131,24 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(VIEWPORT_SIZE, 0.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_BOOLEAN, "output_is_srgb", "OUTPUT_IS_SRGB" },
+
+ // Canvas Item
+
// Canvas Item, Vertex
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "vec3(VERTEX, 0.0)" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "texture_pixel_size", "vec3(TEXTURE_PIXEL_SIZE, 1.0)" },
-
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection", "PROJECTION_MATRIX" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "extra", "EXTRA_MATRIX" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "canvas", "CANVAS_MATRIX" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "screen", "SCREEN_MATRIX" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "light_pass", "float(AT_LIGHT_PASS ? 1.0 : 0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_light_pass", "AT_LIGHT_PASS" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "instance_custom", "INSTANCE_CUSTOM.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "instance_custom_alpha", "INSTANCE_CUSTOM.a" },
+
// Canvas Item, Fragment
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
@@ -1744,51 +2159,76 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_pixel_size", "vec3(SCREEN_PIXEL_SIZE, 1.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "point_coord", "vec3(POINT_COORD, 0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "light_pass", "float(AT_LIGHT_PASS ? 1.0 : 0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_light_pass", "AT_LIGHT_PASS" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "texture", "TEXTURE" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "normal_texture", "NORMAL_TEXTURE" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "screen_texture", "SCREEN_TEXTURE" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "specular_shininess", "SPECULAR_SHININESS.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "specular_shininess_alpha", "SPECULAR_SHININESS.a" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "specular_shininess_texture", "SPECULAR_SHININESS_TEXTURE" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "vec3(VERTEX, 0.0)" },
+
// Canvas Item, Light
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_vec", "vec3(LIGHT_VEC, 0.0)" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_height", "LIGHT_HEIGHT" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light", "LIGHT.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_alpha", "LIGHT.a" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_color", "LIGHT_COLOR.rgb" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_alpha", "LIGHT_COLOR.a" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_uv", "vec3(LIGHT_UV, 0.0)" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "shadow_color", "SHADOW_COLOR.rgb" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "shadow_alpha", "SHADOW_COLOR.a" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "shadow_vec", "vec3(SHADOW_VEC, 0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_color_alpha", "LIGHT_COLOR.a" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_position", "LIGHT_POSITION" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_vertex", "LIGHT_VERTEX" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "shadow", "SHADOW_MODULATE.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "shadow_alpha", "SHADOW_MODULATE.a" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "texture_pixel_size", "vec3(TEXTURE_PIXEL_SIZE, 1.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "point_coord", "vec3(POINT_COORD, 0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SAMPLER, "texture", "TEXTURE" },
-
- // Particles, Emit
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "restart", "float(RESTART ? 1.0 : 0.0)" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "active", "float(ACTIVE ? 1.0 : 0.0)" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR_INT, "index", "INDEX" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "specular_shininess", "SPECULAR_SHININESS.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "specular_shininess_alpha", "SPECULAR_SHININESS.a" },
+
+ // Particles, Start
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR, "attractor_force", "ATTRACTOR_FORCE" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_BOOLEAN, "restart", "RESTART" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR_INT, "index", "INDEX" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
+ // Particles, Start (Custom)
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "attractor_force", "ATTRACTOR_FORCE" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_BOOLEAN, "restart", "RESTART" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR_INT, "index", "INDEX" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Particles, Process
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "attractor_force", "ATTRACTOR_FORCE" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "restart", "float(RESTART ? 1.0 : 0.0)" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "active", "float(ACTIVE ? 1.0 : 0.0)" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_BOOLEAN, "restart", "RESTART" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
@@ -1798,57 +2238,75 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
- // Particles, End
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "restart", "float(RESTART ? 1.0 : 0.0)" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "active", "float(ACTIVE ? 1.0 : 0.0)" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR_INT, "index", "INDEX" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
-
- // Sky, Fragment
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_cubemap_pass", "AT_CUBEMAP_PASS" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_half_res_pass", "AT_HALF_RES_PASS" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_quarter_res_pass", "AT_QUARTER_RES_PASS" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "eyedir", "EYEDIR" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "half_res_color", "HALF_RES_COLOR.rgb" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "half_res_alpha", "HALF_RES_COLOR.a" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light0_color", "LIGHT0_COLOR" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light0_direction", "LIGHT0_DIRECTION" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "light0_enabled", "LIGHT0_ENABLED" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "light0_energy", "LIGHT0_ENERGY" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light1_color", "LIGHT1_COLOR" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light1_direction", "LIGHT1_DIRECTION" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "light1_enabled", "LIGHT1_ENABLED" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "light1_energy", "LIGHT1_ENERGY" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light2_color", "LIGHT2_COLOR" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light2_direction", "LIGHT2_DIRECTION" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "light2_enabled", "LIGHT2_ENABLED" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "light2_energy", "LIGHT2_ENERGY" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light3_color", "LIGHT3_COLOR" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light3_direction", "LIGHT3_DIRECTION" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "light3_enabled", "LIGHT3_ENABLED" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "light3_energy", "LIGHT3_ENERGY" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "position", "POSITION" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "quarter_res_color", "QUARTER_RES_COLOR.rgb" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "quarter_res_alpha", "QUARTER_RES_COLOR.a" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "radiance", "RADIANCE" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "sky_coords", "vec3(SKY_COORDS, 0.0)" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ // Particles, Process (Custom)
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "attractor_force", "ATTRACTOR_FORCE" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_BOOLEAN, "restart", "RESTART" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR_INT, "index", "INDEX" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
+ // Particles, Collide
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR, "attractor_force", "ATTRACTOR_FORCE" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "collision_depth", "COLLISION_DEPTH" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR, "collision_normal", "COLLISION_NORMAL" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_BOOLEAN, "restart", "RESTART" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR_INT, "index", "INDEX" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
+ // Sky, Sky
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_cubemap_pass", "AT_CUBEMAP_PASS" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_half_res_pass", "AT_HALF_RES_PASS" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_quarter_res_pass", "AT_QUARTER_RES_PASS" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "eyedir", "EYEDIR" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "half_res_color", "HALF_RES_COLOR.rgb" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "half_res_alpha", "HALF_RES_COLOR.a" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light0_color", "LIGHT0_COLOR" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light0_direction", "LIGHT0_DIRECTION" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "light0_enabled", "LIGHT0_ENABLED" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "light0_energy", "LIGHT0_ENERGY" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light1_color", "LIGHT1_COLOR" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light1_direction", "LIGHT1_DIRECTION" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "light1_enabled", "LIGHT1_ENABLED" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "light1_energy", "LIGHT1_ENERGY" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light2_color", "LIGHT2_COLOR" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light2_direction", "LIGHT2_DIRECTION" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "light2_enabled", "LIGHT2_ENABLED" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "light2_energy", "LIGHT2_ENERGY" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light3_color", "LIGHT3_COLOR" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light3_direction", "LIGHT3_DIRECTION" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "light3_enabled", "LIGHT3_ENABLED" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "light3_energy", "LIGHT3_ENERGY" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "position", "POSITION" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "quarter_res_color", "QUARTER_RES_COLOR.rgb" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "quarter_res_alpha", "QUARTER_RES_COLOR.a" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SAMPLER, "radiance", "RADIANCE" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "sky_coords", "vec3(SKY_COORDS, 0.0)" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, nullptr, nullptr },
};
const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = {
-
// Spatial, Fragment
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "vec3(0.0, 1.0, 0.0)" },
@@ -1862,7 +2320,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "side", "1.0" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(1.0,1.0, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(1.0, 1.0, 0.0)" },
// Spatial, Light
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" },
@@ -1888,11 +2346,13 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = {
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
- // Particles, Vertex
- { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "vec3(0.0, 0.0, 1.0)" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
+ // Particles
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, nullptr, nullptr },
};
@@ -1936,7 +2396,7 @@ String VisualShaderNodeInput::generate_code(Shader::Mode p_mode, VisualShader::T
while (preview_ports[idx].mode != Shader::MODE_MAX) {
if (preview_ports[idx].mode == shader_mode && preview_ports[idx].shader_type == shader_type && preview_ports[idx].name == input_name) {
- code = "\t" + p_output_vars[0] + " = " + preview_ports[idx].string + ";\n";
+ code = " " + p_output_vars[0] + " = " + preview_ports[idx].string + ";\n";
break;
}
idx++;
@@ -1945,19 +2405,19 @@ String VisualShaderNodeInput::generate_code(Shader::Mode p_mode, VisualShader::T
if (code == String()) {
switch (get_output_port_type(0)) {
case PORT_TYPE_SCALAR: {
- code = "\t" + p_output_vars[0] + " = 0.0;\n";
+ code = " " + p_output_vars[0] + " = 0.0;\n";
} break;
case PORT_TYPE_SCALAR_INT: {
- code = "\t" + p_output_vars[0] + " = 0;\n";
+ code = " " + p_output_vars[0] + " = 0;\n";
} break;
case PORT_TYPE_VECTOR: {
- code = "\t" + p_output_vars[0] + " = vec3(0.0);\n";
+ code = " " + p_output_vars[0] + " = vec3(0.0);\n";
} break;
case PORT_TYPE_TRANSFORM: {
- code = "\t" + p_output_vars[0] + " = mat4( vec4(1.0,0.0,0.0,0.0), vec4(0.0,1.0,0.0,0.0), vec4(0.0,0.0,1.0,0.0), vec4(0.0,0.0,0.0,1.0) );\n";
+ code = " " + p_output_vars[0] + " = mat4(vec4(1.0, 0.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
} break;
case PORT_TYPE_BOOLEAN: {
- code = "\t" + p_output_vars[0] + " = false;\n";
+ code = " " + p_output_vars[0] + " = false;\n";
} break;
default: //default (none found) is scalar
break;
@@ -1973,14 +2433,14 @@ String VisualShaderNodeInput::generate_code(Shader::Mode p_mode, VisualShader::T
while (ports[idx].mode != Shader::MODE_MAX) {
if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type && ports[idx].name == input_name) {
- code = "\t" + p_output_vars[0] + " = " + ports[idx].string + ";\n";
+ code = " " + p_output_vars[0] + " = " + ports[idx].string + ";\n";
break;
}
idx++;
}
if (code == String()) {
- code = "\t" + p_output_vars[0] + " = 0.0;\n"; //default (none found) is scalar
+ code = " " + p_output_vars[0] + " = 0.0;\n"; //default (none found) is scalar
}
return code;
@@ -1992,7 +2452,7 @@ void VisualShaderNodeInput::set_input_name(String p_name) {
input_name = p_name;
emit_changed();
if (get_input_type_by_name(input_name) != prev_type) {
- emit_signal("input_type_changed");
+ emit_signal(SNAME("input_type_changed"));
}
}
@@ -2103,6 +2563,14 @@ Vector<StringName> VisualShaderNodeInput::get_editable_properties() const {
return props;
}
+void VisualShaderNodeInput::set_shader_type(VisualShader::Type p_shader_type) {
+ shader_type = p_shader_type;
+}
+
+void VisualShaderNodeInput::set_shader_mode(Shader::Mode p_shader_mode) {
+ shader_mode = p_shader_mode;
+}
+
void VisualShaderNodeInput::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_input_name", "name"), &VisualShaderNodeInput::set_input_name);
ClassDB::bind_method(D_METHOD("get_input_name"), &VisualShaderNodeInput::get_input_name);
@@ -2128,8 +2596,8 @@ void VisualShaderNodeUniformRef::clear_uniforms() {
}
bool VisualShaderNodeUniformRef::has_uniform(const String &p_name) {
- for (List<VisualShaderNodeUniformRef::Uniform>::Element *E = uniforms.front(); E; E = E->next()) {
- if (E->get().name == p_name) {
+ for (const VisualShaderNodeUniformRef::Uniform &E : uniforms) {
+ if (E.name == p_name) {
return true;
}
}
@@ -2270,24 +2738,46 @@ VisualShaderNodeUniformRef::UniformType VisualShaderNodeUniformRef::get_uniform_
return UniformType::UNIFORM_TYPE_FLOAT;
}
+VisualShaderNodeUniformRef::PortType VisualShaderNodeUniformRef::get_port_type_by_index(int p_idx) const {
+ if (p_idx >= 0 && p_idx < uniforms.size()) {
+ switch (uniforms[p_idx].type) {
+ case UniformType::UNIFORM_TYPE_FLOAT:
+ return PORT_TYPE_SCALAR;
+ case UniformType::UNIFORM_TYPE_INT:
+ return PORT_TYPE_SCALAR_INT;
+ case UniformType::UNIFORM_TYPE_SAMPLER:
+ return PORT_TYPE_SAMPLER;
+ case UniformType::UNIFORM_TYPE_VECTOR:
+ return PORT_TYPE_VECTOR;
+ case UniformType::UNIFORM_TYPE_TRANSFORM:
+ return PORT_TYPE_TRANSFORM;
+ case UniformType::UNIFORM_TYPE_COLOR:
+ return PORT_TYPE_VECTOR;
+ default:
+ break;
+ }
+ }
+ return PORT_TYPE_SCALAR;
+}
+
String VisualShaderNodeUniformRef::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 {
switch (uniform_type) {
case UniformType::UNIFORM_TYPE_FLOAT:
if (uniform_name == "[None]") {
- return "\t" + p_output_vars[0] + " = 0.0;\n";
+ return " " + p_output_vars[0] + " = 0.0;\n";
}
- return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
+ return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
case UniformType::UNIFORM_TYPE_INT:
- return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
+ return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
case UniformType::UNIFORM_TYPE_BOOLEAN:
- return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
+ return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
case UniformType::UNIFORM_TYPE_VECTOR:
- return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
+ return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
case UniformType::UNIFORM_TYPE_TRANSFORM:
- return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
+ return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
case UniformType::UNIFORM_TYPE_COLOR: {
- String code = "\t" + p_output_vars[0] + " = " + get_uniform_name() + ".rgb;\n";
- code += "\t" + p_output_vars[1] + " = " + get_uniform_name() + ".a;\n";
+ String code = " " + p_output_vars[0] + " = " + get_uniform_name() + ".rgb;\n";
+ code += " " + p_output_vars[1] + " = " + get_uniform_name() + ".a;\n";
return code;
} break;
case UniformType::UNIFORM_TYPE_SAMPLER:
@@ -2330,7 +2820,11 @@ VisualShaderNodeUniformRef::VisualShaderNodeUniformRef() {
////////////////////////////////////////////
const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = {
- // Spatial, Vertex
+ ////////////////////////////////////////////////////////////////////////
+ // Node3D.
+ ////////////////////////////////////////////////////////////////////////
+ // Node3D, Vertex.
+ ////////////////////////////////////////////////////////////////////////
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "TANGENT" },
@@ -2340,8 +2834,10 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" },
- // Spatial, Fragment
-
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "model_view_matrix", "MODELVIEW_MATRIX" },
+ ////////////////////////////////////////////////////////////////////////
+ // Node3D, Fragment.
+ ////////////////////////////////////////////////////////////////////////
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "albedo", "ALBEDO" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "metallic", "METALLIC" },
@@ -2351,8 +2847,8 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "ao", "AO" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normalmap", "NORMALMAP" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "normalmap_depth", "NORMALMAP_DEPTH" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal_map", "NORMAL_MAP" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "normal_map_depth", "NORMAL_MAP_DEPTH" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "rim", "RIM" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "rim_tint", "RIM_TINT" },
@@ -2361,56 +2857,52 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "anisotropy", "ANISOTROPY" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "anisotropy_flow", "ANISOTROPY_FLOW:xy" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "subsurf_scatter", "SSS_STRENGTH" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "transmission", "TRANSMISSION" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "backlight", "BACKLIGHT" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha_scissor", "ALPHA_SCISSOR" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha_scissor_threshold", "ALPHA_SCISSOR_THRESHOLD" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "ao_light_affect", "AO_LIGHT_AFFECT" },
-
- // Spatial, Light
+ ////////////////////////////////////////////////////////////////////////
+ // Node3D, Light.
+ ////////////////////////////////////////////////////////////////////////
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "diffuse", "DIFFUSE_LIGHT" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "specular", "SPECULAR_LIGHT" },
- // Canvas Item, Vertex
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" },
+
+ ////////////////////////////////////////////////////////////////////////
+ // Canvas Item.
+ ////////////////////////////////////////////////////////////////////////
+ // Canvas Item, Vertex.
+ ////////////////////////////////////////////////////////////////////////
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX:xy" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "UV:xy" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- // Canvas Item, Fragment
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" },
+ ////////////////////////////////////////////////////////////////////////
+ // Canvas Item, Fragment.
+ ////////////////////////////////////////////////////////////////////////
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normalmap", "NORMALMAP" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "normalmap_depth", "NORMALMAP_DEPTH" },
- // Canvas Item, Light
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal_map", "NORMAL_MAP" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "normal_map_depth", "NORMAL_MAP_DEPTH" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light_vertex", "LIGHT_VERTEX" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "shadow_vertex", "SHADOW_VERTEX:xy" },
+ ////////////////////////////////////////////////////////////////////////
+ // Canvas Item, Light.
+ ////////////////////////////////////////////////////////////////////////
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light", "LIGHT.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_alpha", "LIGHT.a" },
- // Particles, Emit
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" },
- // Particles, Process
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" },
- // Particles, End
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" },
- // Sky, Fragment
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR" },
- { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" },
+ ////////////////////////////////////////////////////////////////////////
+ // Sky, Sky.
+ ////////////////////////////////////////////////////////////////////////
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "fog", "FOG.rgb" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "fog_alpha", "FOG.a" },
+
+ ////////////////////////////////////////////////////////////////////////
{ Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, nullptr, nullptr },
};
@@ -2479,9 +2971,13 @@ String VisualShaderNodeOutput::get_output_port_name(int p_port) const {
}
bool VisualShaderNodeOutput::is_port_separator(int p_index) const {
+ if (shader_mode == Shader::MODE_SPATIAL && shader_type == VisualShader::TYPE_VERTEX) {
+ String name = get_input_port_name(p_index);
+ return bool(name == "Model View Matrix");
+ }
if (shader_mode == Shader::MODE_SPATIAL && shader_type == VisualShader::TYPE_FRAGMENT) {
String name = get_input_port_name(p_index);
- return (name == "Normal" || name == "Rim" || name == "Alpha Scissor");
+ return bool(name == "Normal" || name == "Rim" || name == "Alpha Scissor Threshold");
}
return false;
}
@@ -2500,9 +2996,9 @@ String VisualShaderNodeOutput::generate_code(Shader::Mode p_mode, VisualShader::
if (p_input_vars[count] != String()) {
String s = ports[idx].string;
if (s.find(":") != -1) {
- code += "\t" + s.get_slicec(':', 0) + " = " + p_input_vars[count] + "." + s.get_slicec(':', 1) + ";\n";
+ code += " " + s.get_slicec(':', 0) + " = " + p_input_vars[count] + "." + s.get_slicec(':', 1) + ";\n";
} else {
- code += "\t" + s + " = " + p_input_vars[count] + ";\n";
+ code += " " + s + " = " + p_input_vars[count] + ";\n";
}
}
count++;
@@ -2520,7 +3016,7 @@ VisualShaderNodeOutput::VisualShaderNodeOutput() {
void VisualShaderNodeUniform::set_uniform_name(const String &p_name) {
uniform_name = p_name;
- emit_signal("name_changed");
+ emit_signal(SNAME("name_changed"));
emit_changed();
}
@@ -2529,6 +3025,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();
}
@@ -2558,6 +3058,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 {
@@ -2569,6 +3070,8 @@ String VisualShaderNodeUniform::_get_qual_str() const {
return "global ";
case QUAL_INSTANCE:
return "instance ";
+ default:
+ break;
}
}
return String();
@@ -2578,10 +3081,86 @@ String VisualShaderNodeUniform::get_warning(Shader::Mode p_mode, VisualShader::T
List<String> keyword_list;
ShaderLanguage::get_keyword_list(&keyword_list);
if (keyword_list.find(uniform_name)) {
- return TTR("Uniform name cannot be equal to a shader keyword. Choose another name.");
+ return TTR("Shader keywords cannot be used as uniform names.\nChoose another name.");
}
if (!is_qualifier_supported(qualifier)) {
- return "This uniform type does not support that qualifier.";
+ String qualifier_str;
+ switch (qualifier) {
+ case QUAL_NONE:
+ break;
+ case QUAL_GLOBAL:
+ qualifier_str = "global";
+ break;
+ 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) {
+ RS::GlobalVariableType gvt = RS::get_singleton()->global_variable_get_type(uniform_name);
+ if (gvt == RS::GLOBAL_VAR_TYPE_MAX) {
+ return vformat(TTR("Global uniform '%s' does not exist.\nCreate it in the Project Settings."), uniform_name);
+ }
+ bool incompatible_type = false;
+ switch (gvt) {
+ case RS::GLOBAL_VAR_TYPE_FLOAT: {
+ if (!Object::cast_to<VisualShaderNodeFloatUniform>(this)) {
+ incompatible_type = true;
+ }
+ } break;
+ case RS::GLOBAL_VAR_TYPE_INT: {
+ if (!Object::cast_to<VisualShaderNodeIntUniform>(this)) {
+ incompatible_type = true;
+ }
+ } break;
+ case RS::GLOBAL_VAR_TYPE_BOOL: {
+ if (!Object::cast_to<VisualShaderNodeBooleanUniform>(this)) {
+ incompatible_type = true;
+ }
+ } break;
+ case RS::GLOBAL_VAR_TYPE_COLOR: {
+ if (!Object::cast_to<VisualShaderNodeColorUniform>(this)) {
+ incompatible_type = true;
+ }
+ } break;
+ case RS::GLOBAL_VAR_TYPE_VEC3: {
+ if (!Object::cast_to<VisualShaderNodeVec3Uniform>(this)) {
+ incompatible_type = true;
+ }
+ } break;
+ case RS::GLOBAL_VAR_TYPE_TRANSFORM: {
+ if (!Object::cast_to<VisualShaderNodeTransformUniform>(this)) {
+ incompatible_type = true;
+ }
+ } break;
+ case RS::GLOBAL_VAR_TYPE_SAMPLER2D: {
+ if (!Object::cast_to<VisualShaderNodeTextureUniform>(this)) {
+ incompatible_type = true;
+ }
+ } break;
+ case RS::GLOBAL_VAR_TYPE_SAMPLER3D: {
+ if (!Object::cast_to<VisualShaderNodeTexture3DUniform>(this)) {
+ incompatible_type = true;
+ }
+ } break;
+ case RS::GLOBAL_VAR_TYPE_SAMPLER2DARRAY: {
+ if (!Object::cast_to<VisualShaderNodeTexture2DArrayUniform>(this)) {
+ incompatible_type = true;
+ }
+ } break;
+ case RS::GLOBAL_VAR_TYPE_SAMPLERCUBE: {
+ if (!Object::cast_to<VisualShaderNodeCubemapUniform>(this)) {
+ incompatible_type = true;
+ }
+ } break;
+ default:
+ break;
+ }
+ if (incompatible_type) {
+ return vformat(TTR("Global uniform '%s' has an incompatible type for this kind of node.\nChange it in the Project Settings."), uniform_name);
+ }
}
return String();
@@ -2596,20 +3175,101 @@ Vector<StringName> VisualShaderNodeUniform::get_editable_properties() const {
VisualShaderNodeUniform::VisualShaderNodeUniform() {
}
-////////////// GroupBase
+////////////// ResizeableBase
-String VisualShaderNodeGroupBase::get_caption() const {
- return "Group";
-}
-
-void VisualShaderNodeGroupBase::set_size(const Vector2 &p_size) {
+void VisualShaderNodeResizableBase::set_size(const Vector2 &p_size) {
size = p_size;
}
-Vector2 VisualShaderNodeGroupBase::get_size() const {
+Vector2 VisualShaderNodeResizableBase::get_size() const {
return size;
}
+void VisualShaderNodeResizableBase::set_allow_v_resize(bool p_enabled) {
+ allow_v_resize = p_enabled;
+}
+
+bool VisualShaderNodeResizableBase::is_allow_v_resize() const {
+ return allow_v_resize;
+}
+
+void VisualShaderNodeResizableBase::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_size", "size"), &VisualShaderNodeResizableBase::set_size);
+ ClassDB::bind_method(D_METHOD("get_size"), &VisualShaderNodeResizableBase::get_size);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size");
+}
+
+VisualShaderNodeResizableBase::VisualShaderNodeResizableBase() {
+ set_allow_v_resize(true);
+}
+
+////////////// Comment
+
+String VisualShaderNodeComment::get_caption() const {
+ return title;
+}
+
+int VisualShaderNodeComment::get_input_port_count() const {
+ return 0;
+}
+
+VisualShaderNodeComment::PortType VisualShaderNodeComment::get_input_port_type(int p_port) const {
+ return PortType::PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeComment::get_input_port_name(int p_port) const {
+ return String();
+}
+
+int VisualShaderNodeComment::get_output_port_count() const {
+ return 0;
+}
+
+VisualShaderNodeComment::PortType VisualShaderNodeComment::get_output_port_type(int p_port) const {
+ return PortType::PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeComment::get_output_port_name(int p_port) const {
+ return String();
+}
+
+void VisualShaderNodeComment::set_title(const String &p_title) {
+ title = p_title;
+}
+
+String VisualShaderNodeComment::get_title() const {
+ return title;
+}
+
+void VisualShaderNodeComment::set_description(const String &p_description) {
+ description = p_description;
+}
+
+String VisualShaderNodeComment::get_description() const {
+ return description;
+}
+
+String VisualShaderNodeComment::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 {
+ return String();
+}
+
+void VisualShaderNodeComment::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_title", "title"), &VisualShaderNodeComment::set_title);
+ ClassDB::bind_method(D_METHOD("get_title"), &VisualShaderNodeComment::get_title);
+
+ ClassDB::bind_method(D_METHOD("set_description", "description"), &VisualShaderNodeComment::set_description);
+ ClassDB::bind_method(D_METHOD("get_description"), &VisualShaderNodeComment::get_description);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "title"), "set_title", "get_title");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "description"), "set_description", "get_description");
+}
+
+VisualShaderNodeComment::VisualShaderNodeComment() {
+}
+
+////////////// GroupBase
+
void VisualShaderNodeGroupBase::set_inputs(const String &p_inputs) {
if (inputs == p_inputs) {
return;
@@ -2692,6 +3352,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;
@@ -2745,8 +3409,10 @@ void VisualShaderNodeGroupBase::remove_input_port(int p_id) {
inputs.erase(index, count);
inputs_strings = inputs.split(";", false);
+ inputs = inputs.substr(0, index);
+
for (int i = p_id; i < inputs_strings.size(); i++) {
- inputs = inputs.replace_first(inputs_strings[i].split(",")[0], itos(i));
+ inputs += inputs_strings[i].replace_first(inputs_strings[i].split(",")[0], itos(i)) + ";";
}
_apply_port_changes();
@@ -2762,6 +3428,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;
@@ -2815,8 +3485,10 @@ void VisualShaderNodeGroupBase::remove_output_port(int p_id) {
outputs.erase(index, count);
outputs_strings = outputs.split(";", false);
+ outputs = outputs.substr(0, index);
+
for (int i = p_id; i < outputs_strings.size(); i++) {
- outputs = outputs.replace_first(outputs_strings[i].split(",")[0], itos(i));
+ outputs += outputs_strings[i].replace_first(outputs_strings[i].split(",")[0], itos(i)) + ";";
}
_apply_port_changes();
@@ -2841,7 +3513,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;
@@ -2913,7 +3585,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;
@@ -2991,11 +3663,11 @@ int VisualShaderNodeGroupBase::get_free_output_port_id() const {
return output_ports.size();
}
-void VisualShaderNodeGroupBase::set_control(Control *p_control, int p_index) {
+void VisualShaderNodeGroupBase::set_ctrl_pressed(Control *p_control, int p_index) {
controls[p_index] = p_control;
}
-Control *VisualShaderNodeGroupBase::get_control(int p_index) {
+Control *VisualShaderNodeGroupBase::is_ctrl_pressed(int p_index) {
ERR_FAIL_COND_V(!controls.has(p_index), nullptr);
return controls[p_index];
}
@@ -3036,9 +3708,6 @@ bool VisualShaderNodeGroupBase::is_editable() const {
}
void VisualShaderNodeGroupBase::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_size", "size"), &VisualShaderNodeGroupBase::set_size);
- ClassDB::bind_method(D_METHOD("get_size"), &VisualShaderNodeGroupBase::get_size);
-
ClassDB::bind_method(D_METHOD("set_inputs", "inputs"), &VisualShaderNodeGroupBase::set_inputs);
ClassDB::bind_method(D_METHOD("get_inputs"), &VisualShaderNodeGroupBase::get_inputs);
@@ -3066,8 +3735,6 @@ void VisualShaderNodeGroupBase::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_free_input_port_id"), &VisualShaderNodeGroupBase::get_free_input_port_id);
ClassDB::bind_method(D_METHOD("get_free_output_port_id"), &VisualShaderNodeGroupBase::get_free_output_port_id);
-
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size");
}
String VisualShaderNodeGroupBase::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 {
@@ -3097,11 +3764,11 @@ String VisualShaderNodeExpression::generate_code(Shader::Mode p_mode, VisualShad
String _expression = expression;
_expression = _expression.insert(0, "\n");
- _expression = _expression.replace("\n", "\n\t\t");
+ _expression = _expression.replace("\n", "\n ");
static Vector<String> pre_symbols;
- if (pre_symbols.empty()) {
- pre_symbols.push_back("\t");
+ if (pre_symbols.is_empty()) {
+ pre_symbols.push_back(" ");
pre_symbols.push_back(",");
pre_symbols.push_back(";");
pre_symbols.push_back("{");
@@ -3120,8 +3787,8 @@ String VisualShaderNodeExpression::generate_code(Shader::Mode p_mode, VisualShad
}
static Vector<String> post_symbols;
- if (post_symbols.empty()) {
- post_symbols.push_back("\t");
+ if (post_symbols.is_empty()) {
+ post_symbols.push_back(" ");
post_symbols.push_back("\n");
post_symbols.push_back(",");
post_symbols.push_back(";");
@@ -3180,14 +3847,14 @@ String VisualShaderNodeExpression::generate_code(Shader::Mode p_mode, VisualShad
default:
continue;
}
- output_initializer += "\t" + p_output_vars[i] + " = " + tk + ";\n";
+ output_initializer += " " + p_output_vars[i] + " = " + tk + ";\n";
}
String code;
code += output_initializer;
- code += "\t{";
+ code += " {";
code += _expression;
- code += "\n\t}\n";
+ code += "\n }\n";
return code;
}
diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h
index e3d5200e6b..b3efac02aa 100644
--- a/scene/resources/visual_shader.h
+++ b/scene/resources/visual_shader.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,7 +31,8 @@
#ifndef VISUAL_SHADER_H
#define VISUAL_SHADER_H
-#include "core/string_builder.h"
+#include "core/string/string_builder.h"
+#include "core/templates/safe_refcount.h"
#include "scene/gui/control.h"
#include "scene/resources/shader.h"
@@ -43,24 +44,27 @@ class VisualShader : public Shader {
friend class VisualShaderNodeVersionChecker;
- String version = "";
+ Dictionary engine_version;
public:
enum Type {
TYPE_VERTEX,
TYPE_FRAGMENT,
TYPE_LIGHT,
- TYPE_EMIT,
+ TYPE_START,
TYPE_PROCESS,
- TYPE_END,
+ TYPE_COLLIDE,
+ TYPE_START_CUSTOM,
+ TYPE_PROCESS_CUSTOM,
+ TYPE_SKY,
TYPE_MAX
};
struct Connection {
- int from_node;
- int from_port;
- int to_node;
- int to_port;
+ int from_node = 0;
+ int from_port = 0;
+ int to_node = 0;
+ int to_port = 0;
};
struct DefaultTextureParam {
@@ -90,7 +94,7 @@ private:
Vector2 graph_offset;
struct RenderModeEnums {
- Shader::Mode mode;
+ Shader::Mode mode = Shader::Mode::MODE_MAX;
const char *string;
};
@@ -99,7 +103,7 @@ private:
static RenderModeEnums render_mode_enums[];
- volatile mutable bool dirty = true;
+ mutable SafeFlag dirty;
void _queue_update();
union ConnectionKey {
@@ -107,7 +111,7 @@ private:
uint64_t node : 32;
uint64_t port : 32;
};
- uint64_t key;
+ uint64_t key = 0;
bool operator<(const ConnectionKey &p_key) const {
return key < p_key.key;
}
@@ -126,15 +130,19 @@ protected:
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
+ virtual void reset_state() override;
+
public: // internal methods
void set_shader_type(Type p_type);
Type get_shader_type() const;
public:
- void set_version(const String &p_version);
- String get_version() const;
+ void set_engine_version(const Dictionary &p_version);
+ Dictionary get_engine_version() const;
- void update_version(const String &p_new_version);
+#ifndef DISABLE_DEPRECATED
+ void update_engine_version(const Dictionary &p_new_version);
+#endif /* DISABLE_DEPRECATED */
enum {
NODE_ID_INVALID = -1,
@@ -152,6 +160,7 @@ public:
int find_node_id(Type p_type, const Ref<VisualShaderNode> &p_node) const;
void remove_node(Type p_type, int p_id);
+ void replace_node(Type p_type, int p_id, const StringName &p_new_class);
bool is_node_connection(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) const;
@@ -175,7 +184,7 @@ public:
String generate_preview_shader(Type p_type, int p_node, int p_port, Vector<DefaultTextureParam> &r_default_tex_params) const;
- String validate_port_name(const String &p_name, const List<String> &p_input_ports, const List<String> &p_output_ports) const;
+ String validate_port_name(const String &p_port_name, VisualShaderNode *p_node, int p_port_id, bool p_output) const;
String validate_uniform_name(const String &p_name, const Ref<VisualShaderNodeUniform> &p_uniform) const;
VisualShader();
@@ -194,9 +203,12 @@ class VisualShaderNode : public Resource {
Map<int, Variant> default_input_values;
Map<int, bool> connected_input_ports;
Map<int, int> connected_output_ports;
+ Map<int, bool> expanded_output_ports;
protected:
bool simple_decl = true;
+ bool disabled = false;
+
static void _bind_methods();
public:
@@ -218,10 +230,12 @@ public:
virtual PortType get_input_port_type(int p_port) const = 0;
virtual String get_input_port_name(int p_port) const = 0;
- void set_input_port_default_value(int p_port, const Variant &p_value);
+ virtual void set_input_port_default_value(int p_port, const Variant &p_value);
Variant get_input_port_default_value(int p_port) const; // if NIL (default if node does not set anything) is returned, it means no default value is wanted if disconnected, thus no input var must be supplied (empty string will be supplied)
Array get_default_input_values() const;
- void set_default_input_values(const Array &p_values);
+ virtual void set_default_input_values(const Array &p_values);
+ virtual void remove_input_port_default_value(int p_port);
+ virtual void clear_default_input_values();
virtual int get_output_port_count() const = 0;
virtual PortType get_output_port_type(int p_port) const = 0;
@@ -240,17 +254,28 @@ public:
void set_input_port_connected(int p_port, bool p_connected);
virtual bool is_generate_input_var(int p_port) const;
+ virtual bool is_output_port_expandable(int p_port) const;
+ void _set_output_ports_expanded(const Array &p_data);
+ Array _get_output_ports_expanded() const;
+ void _set_output_port_expanded(int p_port, bool p_expanded);
+ bool _is_output_port_expanded(int p_port) const;
+ int get_expanded_output_port_count() const;
+
virtual bool is_code_generated() const;
virtual bool is_show_prop_names() const;
virtual bool is_use_prop_slots() const;
+ bool is_disabled() const;
+ void set_disabled(bool p_disabled = true);
+
virtual Vector<StringName> get_editable_properties() const;
virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const;
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const;
virtual String generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const;
virtual String generate_global_per_func(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const;
- 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 = 0; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ // If no output is connected, the output var passed will be empty. If no input is connected and input is NIL, the input var passed will be empty.
+ 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 = 0;
virtual String get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const;
@@ -264,9 +289,10 @@ class VisualShaderNodeCustom : public VisualShaderNode {
struct Port {
String name;
- int type;
+ int type = 0;
};
+ bool is_initialized = false;
List<Port> input_ports;
List<Port> output_ports;
@@ -283,7 +309,28 @@ protected:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual void set_input_port_default_value(int p_port, const Variant &p_value) override;
+ virtual void set_default_input_values(const Array &p_values) override;
+ virtual void remove_input_port_default_value(int p_port) override;
+ virtual void clear_default_input_values() override;
+
+ GDVIRTUAL0RC(String, _get_name)
+ GDVIRTUAL0RC(String, _get_description)
+ GDVIRTUAL0RC(String, _get_category)
+ GDVIRTUAL0RC(int, _get_return_icon_type)
+ GDVIRTUAL0RC(int, _get_input_port_count)
+ GDVIRTUAL1RC(int, _get_input_port_type, int)
+ GDVIRTUAL1RC(String, _get_input_port_name, int)
+ GDVIRTUAL0RC(int, _get_output_port_count)
+ GDVIRTUAL1RC(int, _get_output_port_type, int)
+ GDVIRTUAL1RC(String, _get_output_port_name, int)
+ GDVIRTUAL4RC(String, _get_code, Vector<String>, TypedArray<String>, int, int)
+ GDVIRTUAL1RC(String, _get_global_code, int)
+ GDVIRTUAL0RC(bool, _is_highend)
+
protected:
+ void _set_input_port_default_value(int p_port, const Variant &p_value);
+
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;
virtual String generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
@@ -292,6 +339,9 @@ protected:
public:
VisualShaderNodeCustom();
void update_ports();
+
+ bool _is_initialized();
+ void _set_initialized(bool p_enabled);
};
/////
@@ -304,9 +354,9 @@ class VisualShaderNodeInput : public VisualShaderNode {
Shader::Mode shader_mode = Shader::MODE_MAX;
struct Port {
- Shader::Mode mode;
- VisualShader::Type shader_type;
- PortType type;
+ Shader::Mode mode = Shader::Mode::MODE_MAX;
+ VisualShader::Type shader_type = VisualShader::Type::TYPE_MAX;
+ PortType type = PortType::PORT_TYPE_MAX;
const char *name;
const char *string;
};
@@ -316,6 +366,10 @@ class VisualShaderNodeInput : public VisualShaderNode {
String input_name = "[None]";
+public:
+ void set_shader_type(VisualShader::Type p_shader_type);
+ void set_shader_mode(Shader::Mode p_shader_mode);
+
protected:
static void _bind_methods();
void _validate_property(PropertyInfo &property) const override;
@@ -355,13 +409,13 @@ class VisualShaderNodeOutput : public VisualShaderNode {
public:
friend class VisualShader;
- VisualShader::Type shader_type;
- Shader::Mode shader_mode;
+ VisualShader::Type shader_type = VisualShader::Type::TYPE_MAX;
+ Shader::Mode shader_mode = Shader::Mode::MODE_MAX;
struct Port {
- Shader::Mode mode;
- VisualShader::Type shader_type;
- PortType type;
+ Shader::Mode mode = Shader::Mode::MODE_MAX;
+ VisualShader::Type shader_type = VisualShader::Type::TYPE_MAX;
+ PortType type = PortType::PORT_TYPE_MAX;
const char *name;
const char *string;
};
@@ -395,6 +449,7 @@ public:
QUAL_NONE,
QUAL_GLOBAL,
QUAL_INSTANCE,
+ QUAL_MAX,
};
private:
@@ -417,6 +472,7 @@ public:
bool is_global_code_generated() const;
virtual bool is_qualifier_supported(Qualifier p_qual) const = 0;
+ virtual bool is_convertible_to_constant() const = 0;
virtual Vector<StringName> get_editable_properties() const override;
virtual String get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const override;
@@ -478,6 +534,7 @@ public:
String get_uniform_name_by_index(int p_idx) const;
UniformType get_uniform_type_by_name(const String &p_name) const;
UniformType get_uniform_type_by_index(int p_idx) const;
+ PortType get_port_type_by_index(int p_idx) const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -486,20 +543,71 @@ public:
VisualShaderNodeUniformRef();
};
-class VisualShaderNodeGroupBase : public VisualShaderNode {
- GDCLASS(VisualShaderNodeGroupBase, VisualShaderNode);
+class VisualShaderNodeResizableBase : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeResizableBase, VisualShaderNode);
+
+protected:
+ Vector2 size = Size2(0, 0);
+ bool allow_v_resize = true;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_size(const Vector2 &p_size);
+ Vector2 get_size() const;
+
+ bool is_allow_v_resize() const;
+ void set_allow_v_resize(bool p_enabled);
+
+ VisualShaderNodeResizableBase();
+};
+
+class VisualShaderNodeComment : public VisualShaderNodeResizableBase {
+ GDCLASS(VisualShaderNodeComment, VisualShaderNodeResizableBase);
+
+protected:
+ String title = "Comment";
+ String description = "";
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+
+ virtual int get_output_port_count() const override;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) 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_title(const String &p_title);
+ String get_title() const;
+
+ void set_description(const String &p_description);
+ String get_description() const;
+
+ VisualShaderNodeComment();
+};
+
+class VisualShaderNodeGroupBase : public VisualShaderNodeResizableBase {
+ GDCLASS(VisualShaderNodeGroupBase, VisualShaderNodeResizableBase);
private:
void _apply_port_changes();
protected:
- Vector2 size = Size2(0, 0);
String inputs = "";
String outputs = "";
bool editable = false;
struct Port {
- PortType type;
+ PortType type = PortType::PORT_TYPE_MAX;
String name;
};
@@ -511,11 +619,6 @@ protected:
static void _bind_methods();
public:
- virtual String get_caption() const override;
-
- void set_size(const Vector2 &p_size);
- Vector2 get_size() const;
-
void set_inputs(const String &p_inputs);
String get_inputs() const;
@@ -549,8 +652,8 @@ public:
int get_free_input_port_id() const;
int get_free_output_port_id() const;
- void set_control(Control *p_control, int p_index);
- Control *get_control(int p_index);
+ void set_ctrl_pressed(Control *p_control, int p_index);
+ Control *is_ctrl_pressed(int p_index);
void set_editable(bool p_enabled);
bool is_editable() const;
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
index 7c4500468b..e45dfdcb1b 100644
--- a/scene/resources/visual_shader_nodes.cpp
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,10 +30,15 @@
#include "visual_shader_nodes.h"
+////////////// Constants Base
+
+VisualShaderNodeConstant::VisualShaderNodeConstant() {
+}
+
////////////// Scalar(Float)
String VisualShaderNodeFloatConstant::get_caption() const {
- return "ScalarFloat";
+ return "FloatConstant";
}
int VisualShaderNodeFloatConstant::get_input_port_count() const {
@@ -61,11 +66,14 @@ String VisualShaderNodeFloatConstant::get_output_port_name(int p_port) const {
}
String VisualShaderNodeFloatConstant::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 {
- return "\t" + p_output_vars[0] + " = " + vformat("%.6f", constant) + ";\n";
+ 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();
}
@@ -80,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");
@@ -92,7 +100,7 @@ VisualShaderNodeFloatConstant::VisualShaderNodeFloatConstant() {
////////////// Scalar(Int)
String VisualShaderNodeIntConstant::get_caption() const {
- return "ScalarInt";
+ return "IntConstant";
}
int VisualShaderNodeIntConstant::get_input_port_count() const {
@@ -120,11 +128,14 @@ String VisualShaderNodeIntConstant::get_output_port_name(int p_port) const {
}
String VisualShaderNodeIntConstant::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 {
- return "\t" + p_output_vars[0] + " = " + itos(constant) + ";\n";
+ 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();
}
@@ -139,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");
@@ -151,7 +162,7 @@ VisualShaderNodeIntConstant::VisualShaderNodeIntConstant() {
////////////// Boolean
String VisualShaderNodeBooleanConstant::get_caption() const {
- return "Boolean";
+ return "BooleanConstant";
}
int VisualShaderNodeBooleanConstant::get_input_port_count() const {
@@ -179,11 +190,14 @@ String VisualShaderNodeBooleanConstant::get_output_port_name(int p_port) const {
}
String VisualShaderNodeBooleanConstant::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 {
- return "\t" + p_output_vars[0] + " = " + (constant ? "true" : "false") + ";\n";
+ 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();
}
@@ -198,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");
@@ -210,7 +224,7 @@ VisualShaderNodeBooleanConstant::VisualShaderNodeBooleanConstant() {
////////////// Color
String VisualShaderNodeColorConstant::get_caption() const {
- return "Color";
+ return "ColorConstant";
}
int VisualShaderNodeColorConstant::get_input_port_count() const {
@@ -237,16 +251,26 @@ String VisualShaderNodeColorConstant::get_output_port_name(int p_port) const {
return p_port == 0 ? "" : "alpha"; //no output port means the editor will be used as port
}
+bool VisualShaderNodeColorConstant::is_output_port_expandable(int p_port) const {
+ if (p_port == 0) {
+ return true;
+ }
+ return false;
+}
+
String VisualShaderNodeColorConstant::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 {
String code;
- code += "\t" + p_output_vars[0] + " = " + vformat("vec3(%.6f, %.6f, %.6f)", constant.r, constant.g, constant.b) + ";\n";
- code += "\t" + p_output_vars[1] + " = " + vformat("%.6f", constant.a) + ";\n";
+ code += " " + p_output_vars[0] + " = " + vformat("vec3(%.6f, %.6f, %.6f)", constant.r, constant.g, constant.b) + ";\n";
+ code += " " + p_output_vars[1] + " = " + vformat("%.6f", constant.a) + ";\n";
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();
}
@@ -261,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");
@@ -273,7 +297,7 @@ VisualShaderNodeColorConstant::VisualShaderNodeColorConstant() {
////////////// Vector
String VisualShaderNodeVec3Constant::get_caption() const {
- return "Vector";
+ return "VectorConstant";
}
int VisualShaderNodeVec3Constant::get_input_port_count() const {
@@ -301,11 +325,14 @@ String VisualShaderNodeVec3Constant::get_output_port_name(int p_port) const {
}
String VisualShaderNodeVec3Constant::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 {
- return "\t" + p_output_vars[0] + " = " + vformat("vec3(%.6f, %.6f, %.6f)", constant.x, constant.y, constant.z) + ";\n";
+ 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();
}
@@ -320,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");
@@ -329,10 +356,10 @@ void VisualShaderNodeVec3Constant::_bind_methods() {
VisualShaderNodeVec3Constant::VisualShaderNodeVec3Constant() {
}
-////////////// Transform
+////////////// Transform3D
String VisualShaderNodeTransformConstant::get_caption() const {
- return "Transform";
+ return "TransformConstant";
}
int VisualShaderNodeTransformConstant::get_input_port_count() const {
@@ -360,10 +387,10 @@ String VisualShaderNodeTransformConstant::get_output_port_name(int p_port) const
}
String VisualShaderNodeTransformConstant::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 {
- Transform t = constant;
+ Transform3D t = constant;
t.basis.transpose();
- String code = "\t" + p_output_vars[0] + " = mat4(";
+ String code = " " + p_output_vars[0] + " = mat4(";
code += vformat("vec4(%.6f, %.6f, %.6f, 0.0), ", t.basis[0].x, t.basis[0].y, t.basis[0].z);
code += vformat("vec4(%.6f, %.6f, %.6f, 0.0), ", t.basis[1].x, t.basis[1].y, t.basis[1].z);
code += vformat("vec4(%.6f, %.6f, %.6f, 0.0), ", t.basis[2].x, t.basis[2].y, t.basis[2].z);
@@ -371,12 +398,15 @@ String VisualShaderNodeTransformConstant::generate_code(Shader::Mode p_mode, Vis
return code;
}
-void VisualShaderNodeTransformConstant::set_constant(Transform 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();
}
-Transform VisualShaderNodeTransformConstant::get_constant() const {
+Transform3D VisualShaderNodeTransformConstant::get_constant() const {
return constant;
}
@@ -387,10 +417,10 @@ 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::TRANSFORM, "constant"), "set_constant", "get_constant");
+ ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "constant"), "set_constant", "get_constant");
}
VisualShaderNodeTransformConstant::VisualShaderNodeTransformConstant() {
@@ -450,9 +480,16 @@ String VisualShaderNodeTexture::get_output_port_name(int p_port) const {
return p_port == 0 ? "rgb" : "alpha";
}
+bool VisualShaderNodeTexture::is_output_port_expandable(int p_port) const {
+ if (p_port == 0) {
+ return true;
+ }
+ return false;
+}
+
String VisualShaderNodeTexture::get_input_port_default_hint(int p_port) const {
if (p_port == 0) {
- return "UV.xy";
+ return "default";
}
return "";
}
@@ -480,9 +517,11 @@ String VisualShaderNodeTexture::generate_global(Shader::Mode p_mode, VisualShade
case TYPE_COLOR:
u += " : hint_albedo";
break;
- case TYPE_NORMALMAP:
+ case TYPE_NORMAL_MAP:
u += " : hint_normal";
break;
+ default:
+ break;
}
return u + ";\n";
}
@@ -491,26 +530,33 @@ String VisualShaderNodeTexture::generate_global(Shader::Mode p_mode, VisualShade
}
String VisualShaderNodeTexture::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 {
+ String default_uv;
+ if (p_mode != Shader::MODE_PARTICLES && p_mode != Shader::MODE_SKY) {
+ default_uv = "UV.xy";
+ } else {
+ default_uv = "vec2(0.0)";
+ }
+
if (source == SOURCE_TEXTURE) {
String id = make_unique_id(p_type, p_id, "tex");
String code;
if (p_input_vars[0] == String()) { // Use UV by default.
if (p_input_vars[1] == String()) {
- code += "\tvec4 " + id + "_read = texture(" + id + ", UV.xy);\n";
+ code += " vec4 " + id + "_read = texture(" + id + ", " + default_uv + ");\n";
} else {
- code += "\tvec4 " + id + "_read = textureLod(" + id + ", UV.xy, " + p_input_vars[1] + ");\n";
+ code += " vec4 " + id + "_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n";
}
} else if (p_input_vars[1] == String()) {
//no lod
- code += "\tvec4 " + id + "_read = texture(" + id + ", " + p_input_vars[0] + ".xy);\n";
+ code += " vec4 " + id + "_read = texture(" + id + ", " + p_input_vars[0] + ".xy);\n";
} else {
- code += "\tvec4 " + id + "_read = textureLod(" + id + ", " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n";
+ code += " vec4 " + id + "_read = textureLod(" + id + ", " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n";
}
- code += "\t" + p_output_vars[0] + " = " + id + "_read.rgb;\n";
- code += "\t" + p_output_vars[1] + " = " + id + "_read.a;\n";
+ code += " " + p_output_vars[0] + " = " + id + "_read.rgb;\n";
+ code += " " + p_output_vars[1] + " = " + id + "_read.a;\n";
return code;
}
@@ -518,98 +564,98 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader:
String id = p_input_vars[2];
String code;
- code += "\t{\n";
+ code += " {\n";
if (id == String()) {
- code += "\t\tvec4 " + id + "_tex_read = vec4(0.0);\n";
+ code += " vec4 " + id + "_tex_read = vec4(0.0);\n";
} else {
if (p_input_vars[0] == String()) { // Use UV by default.
if (p_input_vars[1] == String()) {
- code += "\t\tvec4 " + id + "_tex_read = texture(" + id + ", UV.xy);\n";
+ code += " vec4 " + id + "_tex_read = texture(" + id + ", " + default_uv + ");\n";
} else {
- code += "\t\tvec4 " + id + "_tex_read = textureLod(" + id + ", UV.xy, " + p_input_vars[1] + ");\n";
+ code += " vec4 " + id + "_tex_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n";
}
} else if (p_input_vars[1] == String()) {
//no lod
- code += "\t\tvec4 " + id + "_tex_read = texture(" + id + ", " + p_input_vars[0] + ".xy);\n";
+ code += " vec4 " + id + "_tex_read = texture(" + id + ", " + p_input_vars[0] + ".xy);\n";
} else {
- code += "\t\tvec4 " + id + "_tex_read = textureLod(" + id + ", " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n";
+ code += " vec4 " + id + "_tex_read = textureLod(" + id + ", " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n";
}
- code += "\t\t" + p_output_vars[0] + " = " + id + "_tex_read.rgb;\n";
- code += "\t\t" + p_output_vars[1] + " = " + id + "_tex_read.a;\n";
+ code += " " + p_output_vars[0] + " = " + id + "_tex_read.rgb;\n";
+ code += " " + p_output_vars[1] + " = " + id + "_tex_read.a;\n";
}
- code += "\t}\n";
+ code += " }\n";
return code;
}
if (source == SOURCE_SCREEN && (p_mode == Shader::MODE_SPATIAL || p_mode == Shader::MODE_CANVAS_ITEM) && p_type == VisualShader::TYPE_FRAGMENT) {
- String code = "\t{\n";
+ String code = " {\n";
if (p_input_vars[0] == String() || p_for_preview) { // Use UV by default.
if (p_input_vars[1] == String()) {
- code += "\t\tvec4 _tex_read = textureLod(SCREEN_TEXTURE, UV.xy, 0.0 );\n";
+ code += " vec4 _tex_read = textureLod(SCREEN_TEXTURE, " + default_uv + ", 0.0 );\n";
} else {
- code += "\t\tvec4 _tex_read = textureLod(SCREEN_TEXTURE, UV.xy, " + p_input_vars[1] + ");\n";
+ code += " vec4 _tex_read = textureLod(SCREEN_TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n";
}
} else if (p_input_vars[1] == String()) {
//no lod
- code += "\t\tvec4 _tex_read = textureLod(SCREEN_TEXTURE, " + p_input_vars[0] + ".xy, 0.0);\n";
+ code += " vec4 _tex_read = textureLod(SCREEN_TEXTURE, " + p_input_vars[0] + ".xy, 0.0);\n";
} else {
- code += "\t\tvec4 _tex_read = textureLod(SCREEN_TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n";
+ code += " vec4 _tex_read = textureLod(SCREEN_TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n";
}
- code += "\t\t" + p_output_vars[0] + " = _tex_read.rgb;\n";
- code += "\t\t" + p_output_vars[1] + " = _tex_read.a;\n";
- code += "\t}\n";
+ code += " " + p_output_vars[0] + " = _tex_read.rgb;\n";
+ code += " " + p_output_vars[1] + " = _tex_read.a;\n";
+ code += " }\n";
return code;
}
if (source == SOURCE_2D_TEXTURE && p_mode == Shader::MODE_CANVAS_ITEM && p_type == VisualShader::TYPE_FRAGMENT) {
- String code = "\t{\n";
+ String code = " {\n";
if (p_input_vars[0] == String()) { // Use UV by default.
if (p_input_vars[1] == String()) {
- code += "\t\tvec4 _tex_read = texture(TEXTURE , UV.xy);\n";
+ code += " vec4 _tex_read = texture(TEXTURE, " + default_uv + ");\n";
} else {
- code += "\t\tvec4 _tex_read = textureLod(TEXTURE, UV.xy, " + p_input_vars[1] + ");\n";
+ code += " vec4 _tex_read = textureLod(TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n";
}
} else if (p_input_vars[1] == String()) {
//no lod
- code += "\t\tvec4 _tex_read = texture(TEXTURE, " + p_input_vars[0] + ".xy);\n";
+ code += " vec4 _tex_read = texture(TEXTURE, " + p_input_vars[0] + ".xy);\n";
} else {
- code += "\t\tvec4 _tex_read = textureLod(TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n";
+ code += " vec4 _tex_read = textureLod(TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n";
}
- code += "\t\t" + p_output_vars[0] + " = _tex_read.rgb;\n";
- code += "\t\t" + p_output_vars[1] + " = _tex_read.a;\n";
- code += "\t}\n";
+ code += " " + p_output_vars[0] + " = _tex_read.rgb;\n";
+ code += " " + p_output_vars[1] + " = _tex_read.a;\n";
+ code += " }\n";
return code;
}
if (source == SOURCE_2D_NORMAL && p_mode == Shader::MODE_CANVAS_ITEM && p_type == VisualShader::TYPE_FRAGMENT) {
- String code = "\t{\n";
+ String code = " {\n";
if (p_input_vars[0] == String()) { // Use UV by default.
if (p_input_vars[1] == String()) {
- code += "\t\tvec4 _tex_read = texture(NORMAL_TEXTURE, UV.xy);\n";
+ code += " vec4 _tex_read = texture(NORMAL_TEXTURE, " + default_uv + ");\n";
} else {
- code += "\t\tvec4 _tex_read = textureLod(NORMAL_TEXTURE, UV.xy, " + p_input_vars[1] + ");\n";
+ code += " vec4 _tex_read = textureLod(NORMAL_TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n";
}
} else if (p_input_vars[1] == String()) {
//no lod
- code += "\t\tvec4 _tex_read = texture(NORMAL_TEXTURE, " + p_input_vars[0] + ".xy);\n";
+ code += " vec4 _tex_read = texture(NORMAL_TEXTURE, " + p_input_vars[0] + ".xy);\n";
} else {
- code += "\t\tvec4 _tex_read = textureLod(NORMAL_TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n";
+ code += " vec4 _tex_read = textureLod(NORMAL_TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n";
}
- code += "\t\t" + p_output_vars[0] + " = _tex_read.rgb;\n";
- code += "\t\t" + p_output_vars[1] + " = _tex_read.a;\n";
- code += "\t}\n";
+ code += " " + p_output_vars[0] + " = _tex_read.rgb;\n";
+ code += " " + p_output_vars[1] + " = _tex_read.a;\n";
+ code += " }\n";
return code;
}
@@ -617,50 +663,53 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader:
{
if (source == SOURCE_DEPTH) {
String code;
- code += "\t" + p_output_vars[0] + " = 0.0;\n";
- code += "\t" + p_output_vars[1] + " = 1.0;\n";
+ code += " " + p_output_vars[0] + " = 0.0;\n";
+ code += " " + p_output_vars[1] + " = 1.0;\n";
return code;
}
}
if (source == SOURCE_DEPTH && p_mode == Shader::MODE_SPATIAL && p_type == VisualShader::TYPE_FRAGMENT) {
- String code = "\t{\n";
+ String code = " {\n";
if (p_input_vars[0] == String()) { // Use UV by default.
if (p_input_vars[1] == String()) {
- code += "\t\tfloat _depth = texture(DEPTH_TEXTURE, UV.xy).r;\n";
+ code += " float _depth = texture(DEPTH_TEXTURE, " + default_uv + ").r;\n";
} else {
- code += "\t\tfloat _depth = textureLod(DEPTH_TEXTURE, UV.xy, " + p_input_vars[1] + ").r;\n";
+ code += " float _depth = textureLod(DEPTH_TEXTURE, " + default_uv + ", " + p_input_vars[1] + ").r;\n";
}
} else if (p_input_vars[1] == String()) {
//no lod
- code += "\t\tfloat _depth = texture(DEPTH_TEXTURE, " + p_input_vars[0] + ".xy).r;\n";
+ code += " float _depth = texture(DEPTH_TEXTURE, " + p_input_vars[0] + ".xy).r;\n";
} else {
- code += "\t\tfloat _depth = textureLod(DEPTH_TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ").r;\n";
+ code += " float _depth = textureLod(DEPTH_TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ").r;\n";
}
- code += "\t\t" + p_output_vars[0] + " = _depth;\n";
- code += "\t\t" + p_output_vars[1] + " = 1.0;\n";
- code += "\t}\n";
+ code += " " + p_output_vars[0] + " = _depth;\n";
+ code += " " + p_output_vars[1] + " = 1.0;\n";
+ code += " }\n";
return code;
} else if (source == SOURCE_DEPTH) {
String code;
- code += "\t" + p_output_vars[0] + " = 0.0;\n";
- code += "\t" + p_output_vars[1] + " = 1.0;\n";
+ code += " " + p_output_vars[0] + " = 0.0;\n";
+ code += " " + p_output_vars[1] + " = 1.0;\n";
return code;
}
//none
String code;
- code += "\t" + p_output_vars[0] + " = vec3(0.0);\n";
- code += "\t" + p_output_vars[1] + " = 1.0;\n";
+ code += " " + p_output_vars[0] + " = vec3(0.0);\n";
+ code += " " + p_output_vars[1] + " = 1.0;\n";
return code;
}
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;
@@ -679,17 +728,20 @@ void VisualShaderNodeTexture::set_source(Source p_source) {
case SOURCE_PORT:
simple_decl = false;
break;
+ default:
+ break;
}
+ source = p_source;
emit_changed();
- emit_signal("editor_refresh_request");
+ emit_signal(SNAME("editor_refresh_request"));
}
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();
}
@@ -697,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();
}
@@ -717,6 +773,10 @@ Vector<StringName> VisualShaderNodeTexture::get_editable_properties() const {
}
String VisualShaderNodeTexture::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const {
+ if (is_input_port_connected(2) && source != SOURCE_PORT) {
+ return TTR("The sampler port is connected but not used. Consider changing the source to 'SamplerPort'.");
+ }
+
if (source == SOURCE_TEXTURE) {
return String(); // all good
}
@@ -759,7 +819,7 @@ void VisualShaderNodeTexture::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "source", PROPERTY_HINT_ENUM, "Texture,Screen,Texture2D,NormalMap2D,Depth,SamplerPort"), "set_source", "get_source");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normalmap"), "set_texture_type", "get_texture_type");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normal Map"), "set_texture_type", "get_texture_type");
BIND_ENUM_CONSTANT(SOURCE_TEXTURE);
BIND_ENUM_CONSTANT(SOURCE_SCREEN);
@@ -767,14 +827,185 @@ 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_NORMALMAP);
+ BIND_ENUM_CONSTANT(TYPE_NORMAL_MAP);
+ BIND_ENUM_CONSTANT(TYPE_MAX);
}
VisualShaderNodeTexture::VisualShaderNodeTexture() {
}
+////////////// CurveTexture
+
+String VisualShaderNodeCurveTexture::get_caption() const {
+ return "CurveTexture";
+}
+
+int VisualShaderNodeCurveTexture::get_input_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeCurveTexture::PortType VisualShaderNodeCurveTexture::get_input_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeCurveTexture::get_input_port_name(int p_port) const {
+ return String();
+}
+
+int VisualShaderNodeCurveTexture::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeCurveTexture::PortType VisualShaderNodeCurveTexture::get_output_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeCurveTexture::get_output_port_name(int p_port) const {
+ return String();
+}
+
+void VisualShaderNodeCurveTexture::set_texture(Ref<CurveTexture> p_texture) {
+ texture = p_texture;
+ emit_changed();
+}
+
+Ref<CurveTexture> VisualShaderNodeCurveTexture::get_texture() const {
+ return texture;
+}
+
+Vector<StringName> VisualShaderNodeCurveTexture::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("texture");
+ return props;
+}
+
+String VisualShaderNodeCurveTexture::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ return "uniform sampler2D " + make_unique_id(p_type, p_id, "curve") + ";\n";
+}
+
+String VisualShaderNodeCurveTexture::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 {
+ if (p_input_vars[0] == String()) {
+ return " " + p_output_vars[0] + " = 0.0;\n";
+ }
+ String id = make_unique_id(p_type, p_id, "curve");
+ String code;
+ code += " " + p_output_vars[0] + " = texture(" + id + ", vec2(" + p_input_vars[0] + ")).r;\n";
+ return code;
+}
+
+Vector<VisualShader::DefaultTextureParam> VisualShaderNodeCurveTexture::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
+ VisualShader::DefaultTextureParam dtp;
+ dtp.name = make_unique_id(p_type, p_id, "curve");
+ dtp.param = texture;
+ Vector<VisualShader::DefaultTextureParam> ret;
+ ret.push_back(dtp);
+ return ret;
+}
+
+void VisualShaderNodeCurveTexture::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_texture", "texture"), &VisualShaderNodeCurveTexture::set_texture);
+ ClassDB::bind_method(D_METHOD("get_texture"), &VisualShaderNodeCurveTexture::get_texture);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_texture", "get_texture");
+}
+
+bool VisualShaderNodeCurveTexture::is_use_prop_slots() const {
+ return true;
+}
+
+VisualShaderNodeCurveTexture::VisualShaderNodeCurveTexture() {
+ simple_decl = true;
+ allow_v_resize = false;
+}
+
+////////////// CurveXYZTexture
+
+String VisualShaderNodeCurveXYZTexture::get_caption() const {
+ return "CurveXYZTexture";
+}
+
+int VisualShaderNodeCurveXYZTexture::get_input_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeCurveXYZTexture::PortType VisualShaderNodeCurveXYZTexture::get_input_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeCurveXYZTexture::get_input_port_name(int p_port) const {
+ return String();
+}
+
+int VisualShaderNodeCurveXYZTexture::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeCurveXYZTexture::PortType VisualShaderNodeCurveXYZTexture::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeCurveXYZTexture::get_output_port_name(int p_port) const {
+ return String();
+}
+
+void VisualShaderNodeCurveXYZTexture::set_texture(Ref<CurveXYZTexture> p_texture) {
+ texture = p_texture;
+ emit_changed();
+}
+
+Ref<CurveXYZTexture> VisualShaderNodeCurveXYZTexture::get_texture() const {
+ return texture;
+}
+
+Vector<StringName> VisualShaderNodeCurveXYZTexture::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("texture");
+ return props;
+}
+
+String VisualShaderNodeCurveXYZTexture::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ return "uniform sampler2D " + make_unique_id(p_type, p_id, "curve3d") + ";\n";
+}
+
+String VisualShaderNodeCurveXYZTexture::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 {
+ if (p_input_vars[0] == String()) {
+ return " " + p_output_vars[0] + " = vec3(0.0);\n";
+ }
+ String id = make_unique_id(p_type, p_id, "curve3d");
+ String code;
+ code += " " + p_output_vars[0] + " = texture(" + id + ", vec2(" + p_input_vars[0] + ")).rgb;\n";
+ return code;
+}
+
+Vector<VisualShader::DefaultTextureParam> VisualShaderNodeCurveXYZTexture::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
+ VisualShader::DefaultTextureParam dtp;
+ dtp.name = make_unique_id(p_type, p_id, "curve3d");
+ dtp.param = texture;
+ Vector<VisualShader::DefaultTextureParam> ret;
+ ret.push_back(dtp);
+ return ret;
+}
+
+void VisualShaderNodeCurveXYZTexture::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_texture", "texture"), &VisualShaderNodeCurveXYZTexture::set_texture);
+ ClassDB::bind_method(D_METHOD("get_texture"), &VisualShaderNodeCurveXYZTexture::get_texture);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "CurveXYZTexture"), "set_texture", "get_texture");
+}
+
+bool VisualShaderNodeCurveXYZTexture::is_use_prop_slots() const {
+ return true;
+}
+
+VisualShaderNodeCurveXYZTexture::VisualShaderNodeCurveXYZTexture() {
+ simple_decl = true;
+ allow_v_resize = false;
+}
+
////////////// Sample3D
int VisualShaderNodeSample3D::get_input_port_count() const {
@@ -817,18 +1048,32 @@ String VisualShaderNodeSample3D::get_output_port_name(int p_port) const {
return p_port == 0 ? "rgb" : "alpha";
}
+bool VisualShaderNodeSample3D::is_output_port_expandable(int p_port) const {
+ if (p_port == 0) {
+ return true;
+ }
+ return false;
+}
+
String VisualShaderNodeSample3D::get_input_port_default_hint(int p_port) const {
if (p_port == 0) {
- return "vec3(UV.xy, 0.0)";
+ return "default";
}
return "";
}
String VisualShaderNodeSample3D::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 {
+ String default_uv;
+ if (p_mode != Shader::MODE_PARTICLES && p_mode != Shader::MODE_SKY) {
+ default_uv = "vec3(UV, 0.0)";
+ } else {
+ default_uv = "vec3(0.0)";
+ }
+
String code;
if (source == SOURCE_TEXTURE || source == SOURCE_PORT) {
String id;
- code += "\t{\n";
+ code += " {\n";
if (source == SOURCE_TEXTURE) {
id = make_unique_id(p_type, p_id, "tex3d");
} else {
@@ -837,34 +1082,38 @@ String VisualShaderNodeSample3D::generate_code(Shader::Mode p_mode, VisualShader
if (id != String()) {
if (p_input_vars[0] == String()) { // Use UV by default.
if (p_input_vars[1] == String()) {
- code += "\t\tvec4 " + id + "_tex_read = texture(" + id + ", vec3(UV.xy, 0.0));\n";
+ code += " vec4 " + id + "_tex_read = texture(" + id + ", " + default_uv + ");\n";
} else {
- code += "\t\tvec4 " + id + "_tex_read = textureLod(" + id + ", vec3(UV.xy, 0.0), " + p_input_vars[1] + ");\n";
+ code += " vec4 " + id + "_tex_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n";
}
} else if (p_input_vars[1] == String()) {
//no lod
- code += "\t\tvec4 " + id + "_tex_read = texture(" + id + ", " + p_input_vars[0] + ");\n";
+ code += " vec4 " + id + "_tex_read = texture(" + id + ", " + p_input_vars[0] + ");\n";
} else {
- code += "\t\tvec4 " + id + "_tex_read = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
+ code += " vec4 " + id + "_tex_read = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
}
} else {
- code += "\t\tvec4 " + id + "_tex_read = vec4(0.0);\n";
+ code += " vec4 " + id + "_tex_read = vec4(0.0);\n";
}
- code += "\t\t" + p_output_vars[0] + " = " + id + "_tex_read.rgb;\n";
- code += "\t\t" + p_output_vars[1] + " = " + id + "_tex_read.a;\n";
- code += "\t}\n";
+ code += " " + p_output_vars[0] + " = " + id + "_tex_read.rgb;\n";
+ code += " " + p_output_vars[1] + " = " + id + "_tex_read.a;\n";
+ code += " }\n";
return code;
}
- code += "\t" + p_output_vars[0] + " = vec3(0.0);\n";
- code += "\t" + p_output_vars[1] + " = 1.0;\n";
+ code += " " + p_output_vars[0] + " = vec3(0.0);\n";
+ code += " " + p_output_vars[1] + " = 1.0;\n";
return code;
}
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("editor_refresh_request");
+ emit_signal(SNAME("editor_refresh_request"));
}
VisualShaderNodeSample3D::Source VisualShaderNodeSample3D::get_source() const {
@@ -879,9 +1128,14 @@ 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 {
+ if (is_input_port_connected(2) && source != SOURCE_PORT) {
+ return TTR("The sampler port is connected but not used. Consider changing the source to 'SamplerPort'.");
+ }
+
if (source == SOURCE_TEXTURE) {
return String(); // all good
}
@@ -911,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;
@@ -924,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 {
@@ -981,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();
}
@@ -1057,6 +1311,13 @@ String VisualShaderNodeCubemap::get_output_port_name(int p_port) const {
return p_port == 0 ? "rgb" : "alpha";
}
+bool VisualShaderNodeCubemap::is_output_port_expandable(int p_port) const {
+ if (p_port == 0) {
+ return true;
+ }
+ return false;
+}
+
Vector<VisualShader::DefaultTextureParam> VisualShaderNodeCubemap::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
VisualShader::DefaultTextureParam dtp;
dtp.name = make_unique_id(p_type, p_id, "cube");
@@ -1075,9 +1336,11 @@ String VisualShaderNodeCubemap::generate_global(Shader::Mode p_mode, VisualShade
case TYPE_COLOR:
u += " : hint_albedo";
break;
- case TYPE_NORMALMAP:
+ case TYPE_NORMAL_MAP:
u += " : hint_normal";
break;
+ default:
+ break;
}
return u + ";\n";
}
@@ -1085,6 +1348,13 @@ String VisualShaderNodeCubemap::generate_global(Shader::Mode p_mode, VisualShade
}
String VisualShaderNodeCubemap::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 {
+ String default_uv;
+ if (p_mode != Shader::MODE_PARTICLES && p_mode != Shader::MODE_SKY) {
+ default_uv = "vec3(UV, 0.0)";
+ } else {
+ default_uv = "vec3(0.0)";
+ }
+
String code;
String id;
if (source == SOURCE_TEXTURE) {
@@ -1095,56 +1365,60 @@ String VisualShaderNodeCubemap::generate_code(Shader::Mode p_mode, VisualShader:
return String();
}
- code += "\t{\n";
+ code += " {\n";
if (id == String()) {
- code += "\t\tvec4 " + id + "_read = vec4(0.0);\n";
- code += "\t\t" + p_output_vars[0] + " = " + id + "_read.rgb;\n";
- code += "\t\t" + p_output_vars[1] + " = " + id + "_read.a;\n";
- code += "\t}\n";
+ code += " vec4 " + id + "_read = vec4(0.0);\n";
+ code += " " + p_output_vars[0] + " = " + id + "_read.rgb;\n";
+ code += " " + p_output_vars[1] + " = " + id + "_read.a;\n";
+ code += " }\n";
return code;
}
if (p_input_vars[0] == String()) { // Use UV by default.
if (p_input_vars[1] == String()) {
- code += "\t\tvec4 " + id + "_read = texture(" + id + " , vec3(UV, 0.0));\n";
+ code += " vec4 " + id + "_read = texture(" + id + ", " + default_uv + ");\n";
} else {
- code += "\t\tvec4 " + id + "_read = textureLod(" + id + " , vec3(UV, 0.0)" + " , " + p_input_vars[1] + " );\n";
+ code += " vec4 " + id + "_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + " );\n";
}
} else if (p_input_vars[1] == String()) {
//no lod
- code += "\t\tvec4 " + id + "_read = texture(" + id + ", " + p_input_vars[0] + ");\n";
+ code += " vec4 " + id + "_read = texture(" + id + ", " + p_input_vars[0] + ");\n";
} else {
- code += "\t\tvec4 " + id + "_read = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
+ code += " vec4 " + id + "_read = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
}
- code += "\t\t" + p_output_vars[0] + " = " + id + "_read.rgb;\n";
- code += "\t\t" + p_output_vars[1] + " = " + id + "_read.a;\n";
- code += "\t}\n";
+ code += " " + p_output_vars[0] + " = " + id + "_read.rgb;\n";
+ code += " " + p_output_vars[1] + " = " + id + "_read.a;\n";
+ code += " }\n";
return code;
}
String VisualShaderNodeCubemap::get_input_port_default_hint(int p_port) const {
if (p_port == 0) {
- return "vec3(UV, 0.0)";
+ return "default";
}
return "";
}
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("editor_refresh_request");
+ emit_signal(SNAME("editor_refresh_request"));
}
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();
}
@@ -1152,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();
}
@@ -1171,6 +1449,13 @@ Vector<StringName> VisualShaderNodeCubemap::get_editable_properties() const {
return props;
}
+String VisualShaderNodeCubemap::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const {
+ if (is_input_port_connected(2) && source != SOURCE_PORT) {
+ return TTR("The sampler port is connected but not used. Consider changing the source to 'SamplerPort'.");
+ }
+ return String();
+}
+
void VisualShaderNodeCubemap::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_source", "value"), &VisualShaderNodeCubemap::set_source);
ClassDB::bind_method(D_METHOD("get_source"), &VisualShaderNodeCubemap::get_source);
@@ -1183,14 +1468,16 @@ void VisualShaderNodeCubemap::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "source", PROPERTY_HINT_ENUM, "Texture,SamplerPort"), "set_source", "get_source");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "cube_map", PROPERTY_HINT_RESOURCE_TYPE, "Cubemap"), "set_cube_map", "get_cube_map");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normalmap"), "set_texture_type", "get_texture_type");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normal Map"), "set_texture_type", "get_texture_type");
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_NORMALMAP);
+ BIND_ENUM_CONSTANT(TYPE_NORMAL_MAP);
+ BIND_ENUM_CONSTANT(TYPE_MAX);
}
VisualShaderNodeCubemap::VisualShaderNodeCubemap() {
@@ -1228,7 +1515,7 @@ String VisualShaderNodeFloatOp::get_output_port_name(int p_port) const {
}
String VisualShaderNodeFloatOp::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 {
- String code = "\t" + p_output_vars[0] + " = ";
+ String code = " " + p_output_vars[0] + " = ";
switch (op) {
case OP_ADD:
code += p_input_vars[0] + " + " + p_input_vars[1] + ";\n";
@@ -1260,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();
}
@@ -1284,7 +1576,7 @@ void VisualShaderNodeFloatOp::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeFloatOp::set_operator);
ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeFloatOp::get_operator);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Sub,Multiply,Divide,Remainder,Power,Max,Min,Atan2,Step"), "set_operator", "get_operator");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Subtract,Multiply,Divide,Remainder,Power,Max,Min,ATan2,Step"), "set_operator", "get_operator");
BIND_ENUM_CONSTANT(OP_ADD);
BIND_ENUM_CONSTANT(OP_SUB);
@@ -1296,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() {
@@ -1334,7 +1627,7 @@ String VisualShaderNodeIntOp::get_output_port_name(int p_port) const {
}
String VisualShaderNodeIntOp::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 {
- String code = "\t" + p_output_vars[0] + " = ";
+ String code = " " + p_output_vars[0] + " = ";
switch (op) {
case OP_ADD:
code += p_input_vars[0] + " + " + p_input_vars[1] + ";\n";
@@ -1357,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();
}
@@ -1381,7 +1680,7 @@ void VisualShaderNodeIntOp::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeIntOp::set_operator);
ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeIntOp::get_operator);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Sub,Multiply,Divide,Remainder,Max,Min"), "set_operator", "get_operator");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Subtract,Multiply,Divide,Remainder,Max,Min"), "set_operator", "get_operator");
BIND_ENUM_CONSTANT(OP_ADD);
BIND_ENUM_CONSTANT(OP_SUB);
@@ -1390,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() {
@@ -1428,7 +1728,7 @@ String VisualShaderNodeVectorOp::get_output_port_name(int p_port) const {
}
String VisualShaderNodeVectorOp::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 {
- String code = "\t" + p_output_vars[0] + " = ";
+ String code = " " + p_output_vars[0] + " = ";
switch (op) {
case OP_ADD:
code += p_input_vars[0] + " + " + p_input_vars[1] + ";\n";
@@ -1466,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();
}
@@ -1490,7 +1796,7 @@ void VisualShaderNodeVectorOp::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeVectorOp::set_operator);
ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeVectorOp::get_operator);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Sub,Multiply,Divide,Remainder,Power,Max,Min,Cross,Atan2,Reflect,Step"), "set_operator", "get_operator");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Subtract,Multiply,Divide,Remainder,Power,Max,Min,Cross,ATan2,Reflect,Step"), "set_operator", "get_operator");
BIND_ENUM_CONSTANT(OP_ADD);
BIND_ENUM_CONSTANT(OP_SUB);
@@ -1504,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() {
@@ -1546,75 +1853,80 @@ String VisualShaderNodeColorOp::generate_code(Shader::Mode p_mode, VisualShader:
static const char *axisn[3] = { "x", "y", "z" };
switch (op) {
case OP_SCREEN: {
- code += "\t" + p_output_vars[0] + " = vec3(1.0) - (vec3(1.0) - " + p_input_vars[0] + ") * (vec3(1.0) - " + p_input_vars[1] + ");\n";
+ code += " " + p_output_vars[0] + " = vec3(1.0) - (vec3(1.0) - " + p_input_vars[0] + ") * (vec3(1.0) - " + p_input_vars[1] + ");\n";
} break;
case OP_DIFFERENCE: {
- code += "\t" + p_output_vars[0] + " = abs(" + p_input_vars[0] + " - " + p_input_vars[1] + ");\n";
+ code += " " + p_output_vars[0] + " = abs(" + p_input_vars[0] + " - " + p_input_vars[1] + ");\n";
} break;
case OP_DARKEN: {
- code += "\t" + p_output_vars[0] + " = min(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
+ code += " " + p_output_vars[0] + " = min(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
} break;
case OP_LIGHTEN: {
- code += "\t" + p_output_vars[0] + " = max(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
+ code += " " + p_output_vars[0] + " = max(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
} break;
case OP_OVERLAY: {
for (int i = 0; i < 3; i++) {
- code += "\t{\n";
- code += "\t\tfloat base = " + p_input_vars[0] + "." + axisn[i] + ";\n";
- code += "\t\tfloat blend = " + p_input_vars[1] + "." + axisn[i] + ";\n";
- code += "\t\tif (base < 0.5) {\n";
- code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = 2.0 * base * blend;\n";
- code += "\t\t} else {\n";
- code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = 1.0 - 2.0 * (1.0 - blend) * (1.0 - base);\n";
- code += "\t\t}\n";
- code += "\t}\n";
+ code += " {\n";
+ code += " float base = " + p_input_vars[0] + "." + axisn[i] + ";\n";
+ code += " float blend = " + p_input_vars[1] + "." + axisn[i] + ";\n";
+ code += " if (base < 0.5) {\n";
+ code += " " + p_output_vars[0] + "." + axisn[i] + " = 2.0 * base * blend;\n";
+ code += " } else {\n";
+ code += " " + p_output_vars[0] + "." + axisn[i] + " = 1.0 - 2.0 * (1.0 - blend) * (1.0 - base);\n";
+ code += " }\n";
+ code += " }\n";
}
} break;
case OP_DODGE: {
- code += "\t" + p_output_vars[0] + " = (" + p_input_vars[0] + ") / (vec3(1.0) - " + p_input_vars[1] + ");\n";
+ code += " " + p_output_vars[0] + " = (" + p_input_vars[0] + ") / (vec3(1.0) - " + p_input_vars[1] + ");\n";
} break;
case OP_BURN: {
- code += "\t" + p_output_vars[0] + " = vec3(1.0) - (vec3(1.0) - " + p_input_vars[0] + ") / (" + p_input_vars[1] + ");\n";
+ code += " " + p_output_vars[0] + " = vec3(1.0) - (vec3(1.0) - " + p_input_vars[0] + ") / (" + p_input_vars[1] + ");\n";
} break;
case OP_SOFT_LIGHT: {
for (int i = 0; i < 3; i++) {
- code += "\t{\n";
- code += "\t\tfloat base = " + p_input_vars[0] + "." + axisn[i] + ";\n";
- code += "\t\tfloat blend = " + p_input_vars[1] + "." + axisn[i] + ";\n";
- code += "\t\tif (base < 0.5) {\n";
- code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = (base * (blend + 0.5));\n";
- code += "\t\t} else {\n";
- code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = (1.0 - (1.0 - base) * (1.0 - (blend - 0.5)));\n";
- code += "\t\t}\n";
- code += "\t}\n";
+ code += " {\n";
+ code += " float base = " + p_input_vars[0] + "." + axisn[i] + ";\n";
+ code += " float blend = " + p_input_vars[1] + "." + axisn[i] + ";\n";
+ code += " if (base < 0.5) {\n";
+ code += " " + p_output_vars[0] + "." + axisn[i] + " = (base * (blend + 0.5));\n";
+ code += " } else {\n";
+ code += " " + p_output_vars[0] + "." + axisn[i] + " = (1.0 - (1.0 - base) * (1.0 - (blend - 0.5)));\n";
+ code += " }\n";
+ code += " }\n";
}
} break;
case OP_HARD_LIGHT: {
for (int i = 0; i < 3; i++) {
- code += "\t{\n";
- code += "\t\tfloat base = " + p_input_vars[0] + "." + axisn[i] + ";\n";
- code += "\t\tfloat blend = " + p_input_vars[1] + "." + axisn[i] + ";\n";
- code += "\t\tif (base < 0.5) {\n";
- code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = (base * (2.0 * blend));\n";
- code += "\t\t} else {\n";
- code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = (1.0 - (1.0 - base) * (1.0 - 2.0 * (blend - 0.5)));\n";
- code += "\t\t}\n";
- code += "\t}\n";
+ code += " {\n";
+ code += " float base = " + p_input_vars[0] + "." + axisn[i] + ";\n";
+ code += " float blend = " + p_input_vars[1] + "." + axisn[i] + ";\n";
+ code += " if (base < 0.5) {\n";
+ code += " " + p_output_vars[0] + "." + axisn[i] + " = (base * (2.0 * blend));\n";
+ code += " } else {\n";
+ code += " " + p_output_vars[0] + "." + axisn[i] + " = (1.0 - (1.0 - base) * (1.0 - 2.0 * (blend - 0.5)));\n";
+ code += " }\n";
+ code += " }\n";
}
} 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;
@@ -1642,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();
}
@@ -1660,7 +1975,7 @@ void VisualShaderNodeColorOp::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeColorOp::set_operator);
ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeColorOp::get_operator);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Screen,Difference,Darken,Lighten,Overlay,Dodge,Burn,SoftLight,HardLight"), "set_operator", "get_operator");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Screen,Difference,Darken,Lighten,Overlay,Dodge,Burn,Soft Light,Hard Light"), "set_operator", "get_operator");
BIND_ENUM_CONSTANT(OP_SCREEN);
BIND_ENUM_CONSTANT(OP_DIFFERENCE);
@@ -1671,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() {
@@ -1678,78 +1994,101 @@ VisualShaderNodeColorOp::VisualShaderNodeColorOp() {
set_input_port_default_value(1, Vector3());
}
-////////////// Transform Mult
+////////////// Transform Op
-String VisualShaderNodeTransformMult::get_caption() const {
- return "TransformMult";
+String VisualShaderNodeTransformOp::get_caption() const {
+ return "TransformOp";
}
-int VisualShaderNodeTransformMult::get_input_port_count() const {
+int VisualShaderNodeTransformOp::get_input_port_count() const {
return 2;
}
-VisualShaderNodeTransformMult::PortType VisualShaderNodeTransformMult::get_input_port_type(int p_port) const {
+VisualShaderNodeTransformOp::PortType VisualShaderNodeTransformOp::get_input_port_type(int p_port) const {
return PORT_TYPE_TRANSFORM;
}
-String VisualShaderNodeTransformMult::get_input_port_name(int p_port) const {
+String VisualShaderNodeTransformOp::get_input_port_name(int p_port) const {
return p_port == 0 ? "a" : "b";
}
-int VisualShaderNodeTransformMult::get_output_port_count() const {
+int VisualShaderNodeTransformOp::get_output_port_count() const {
return 1;
}
-VisualShaderNodeTransformMult::PortType VisualShaderNodeTransformMult::get_output_port_type(int p_port) const {
+VisualShaderNodeTransformOp::PortType VisualShaderNodeTransformOp::get_output_port_type(int p_port) const {
return PORT_TYPE_TRANSFORM;
}
-String VisualShaderNodeTransformMult::get_output_port_name(int p_port) const {
+String VisualShaderNodeTransformOp::get_output_port_name(int p_port) const {
return "mult"; //no output port means the editor will be used as port
}
-String VisualShaderNodeTransformMult::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 {
- if (op == OP_AxB) {
- return "\t" + p_output_vars[0] + " = " + p_input_vars[0] + " * " + p_input_vars[1] + ";\n";
- } else if (op == OP_BxA) {
- return "\t" + p_output_vars[0] + " = " + p_input_vars[1] + " * " + p_input_vars[0] + ";\n";
- } else if (op == OP_AxB_COMP) {
- return "\t" + p_output_vars[0] + " = matrixCompMult(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
- } else {
- return "\t" + p_output_vars[0] + " = matrixCompMult(" + p_input_vars[1] + ", " + p_input_vars[0] + ");\n";
+String VisualShaderNodeTransformOp::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 {
+ switch (op) {
+ case OP_AxB:
+ return " " + p_output_vars[0] + " = " + p_input_vars[0] + " * " + p_input_vars[1] + ";\n";
+ case OP_BxA:
+ return " " + p_output_vars[0] + " = " + p_input_vars[1] + " * " + p_input_vars[0] + ";\n";
+ case OP_AxB_COMP:
+ return " " + p_output_vars[0] + " = matrixCompMult(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
+ case OP_BxA_COMP:
+ return " " + p_output_vars[0] + " = matrixCompMult(" + p_input_vars[1] + ", " + p_input_vars[0] + ");\n";
+ case OP_ADD:
+ return " " + p_output_vars[0] + " = " + p_input_vars[0] + " + " + p_input_vars[1] + ";\n";
+ case OP_A_MINUS_B:
+ return " " + p_output_vars[0] + " = " + p_input_vars[0] + " - " + p_input_vars[1] + ";\n";
+ case OP_B_MINUS_A:
+ return " " + p_output_vars[0] + " = " + p_input_vars[1] + " - " + p_input_vars[0] + ";\n";
+ case OP_A_DIV_B:
+ return " " + p_output_vars[0] + " = " + p_input_vars[0] + " / " + p_input_vars[1] + ";\n";
+ case OP_B_DIV_A:
+ return " " + p_output_vars[0] + " = " + p_input_vars[1] + " / " + p_input_vars[0] + ";\n";
+ default:
+ return "";
}
}
-void VisualShaderNodeTransformMult::set_operator(Operator p_op) {
+void VisualShaderNodeTransformOp::set_operator(Operator p_op) {
+ ERR_FAIL_INDEX(int(p_op), int(OP_MAX));
+ if (op == p_op) {
+ return;
+ }
op = p_op;
emit_changed();
}
-VisualShaderNodeTransformMult::Operator VisualShaderNodeTransformMult::get_operator() const {
+VisualShaderNodeTransformOp::Operator VisualShaderNodeTransformOp::get_operator() const {
return op;
}
-Vector<StringName> VisualShaderNodeTransformMult::get_editable_properties() const {
+Vector<StringName> VisualShaderNodeTransformOp::get_editable_properties() const {
Vector<StringName> props;
props.push_back("operator");
return props;
}
-void VisualShaderNodeTransformMult::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeTransformMult::set_operator);
- ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeTransformMult::get_operator);
+void VisualShaderNodeTransformOp::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeTransformOp::set_operator);
+ ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeTransformOp::get_operator);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "A x B,B x A,A x B(per component),B x A(per component)"), "set_operator", "get_operator");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "A x B,B x A,A x B(per component),B x A(per component),A + B,A - B,B - A,A / B,B / A"), "set_operator", "get_operator");
BIND_ENUM_CONSTANT(OP_AxB);
BIND_ENUM_CONSTANT(OP_BxA);
BIND_ENUM_CONSTANT(OP_AxB_COMP);
BIND_ENUM_CONSTANT(OP_BxA_COMP);
+ BIND_ENUM_CONSTANT(OP_ADD);
+ BIND_ENUM_CONSTANT(OP_A_MINUS_B);
+ 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_MAX);
}
-VisualShaderNodeTransformMult::VisualShaderNodeTransformMult() {
- set_input_port_default_value(0, Transform());
- set_input_port_default_value(1, Transform());
+VisualShaderNodeTransformOp::VisualShaderNodeTransformOp() {
+ set_input_port_default_value(0, Transform3D());
+ set_input_port_default_value(1, Transform3D());
}
////////////// TransformVec Mult
@@ -1784,17 +2123,21 @@ String VisualShaderNodeTransformVecMult::get_output_port_name(int p_port) const
String VisualShaderNodeTransformVecMult::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 {
if (op == OP_AxB) {
- return "\t" + p_output_vars[0] + " = (" + p_input_vars[0] + " * vec4(" + p_input_vars[1] + ", 1.0)).xyz;\n";
+ return " " + p_output_vars[0] + " = (" + p_input_vars[0] + " * vec4(" + p_input_vars[1] + ", 1.0)).xyz;\n";
} else if (op == OP_BxA) {
- return "\t" + p_output_vars[0] + " = (vec4(" + p_input_vars[1] + ", 1.0) * " + p_input_vars[0] + ").xyz;\n";
+ return " " + p_output_vars[0] + " = (vec4(" + p_input_vars[1] + ", 1.0) * " + p_input_vars[0] + ").xyz;\n";
} else if (op == OP_3x3_AxB) {
- return "\t" + p_output_vars[0] + " = (" + p_input_vars[0] + " * vec4(" + p_input_vars[1] + ", 0.0)).xyz;\n";
+ return " " + p_output_vars[0] + " = (" + p_input_vars[0] + " * vec4(" + p_input_vars[1] + ", 0.0)).xyz;\n";
} else {
- return "\t" + p_output_vars[0] + " = (vec4(" + p_input_vars[1] + ", 0.0) * " + p_input_vars[0] + ").xyz;\n";
+ return " " + p_output_vars[0] + " = (vec4(" + p_input_vars[1] + ", 0.0) * " + p_input_vars[0] + ").xyz;\n";
}
}
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();
}
@@ -1819,10 +2162,11 @@ 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() {
- set_input_port_default_value(0, Transform());
+ set_input_port_default_value(0, Transform3D());
set_input_port_default_value(1, Vector3());
}
@@ -1857,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($)",
@@ -1891,11 +2235,14 @@ String VisualShaderNodeFloatFunc::generate_code(Shader::Mode p_mode, VisualShade
"trunc($)",
"1.0 - $"
};
-
- return "\t" + 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();
}
@@ -1948,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() {
@@ -1961,9 +2309,6 @@ String VisualShaderNodeIntFunc::get_caption() const {
}
int VisualShaderNodeIntFunc::get_input_port_count() const {
- if (func == FUNC_CLAMP) {
- return 3;
- }
return 1;
}
@@ -1972,15 +2317,6 @@ VisualShaderNodeIntFunc::PortType VisualShaderNodeIntFunc::get_input_port_type(i
}
String VisualShaderNodeIntFunc::get_input_port_name(int p_port) const {
- if (func == FUNC_CLAMP) {
- if (p_port == 0) {
- return "";
- } else if (p_port == 1) {
- return "min";
- } else if (p_port == 2) {
- return "max";
- }
- }
return "";
}
@@ -1997,26 +2333,19 @@ 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 {
- if (func == FUNC_CLAMP) {
- return "\t" + p_output_vars[0] + " = clamp(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n";
- }
-
- static const char *int_func_id[FUNC_SIGN + 1] = {
+ static const char *functions[FUNC_MAX] = {
"abs($)",
- "",
"-($)",
"sign($)"
};
- return "\t" + 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) {
- if (func != p_func) {
- if (p_func == FUNC_CLAMP) {
- set_input_port_default_value(1, 0);
- set_input_port_default_value(2, 0);
- }
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
+ if (func == p_func) {
+ return;
}
func = p_func;
emit_changed();
@@ -2036,12 +2365,12 @@ void VisualShaderNodeIntFunc::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeIntFunc::set_function);
ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeIntFunc::get_function);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Abs,Clamp,Negate,Sign"), "set_function", "get_function");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Abs,Negate,Sign"), "set_function", "get_function");
BIND_ENUM_CONSTANT(FUNC_ABS);
- BIND_ENUM_CONSTANT(FUNC_CLAMP);
BIND_ENUM_CONSTANT(FUNC_NEGATE);
BIND_ENUM_CONSTANT(FUNC_SIGN);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
}
VisualShaderNodeIntFunc::VisualShaderNodeIntFunc() {
@@ -2079,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))",
"-($)",
@@ -2120,39 +2449,43 @@ String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShad
String code;
if (func == FUNC_RGB2HSV) {
- code += "\t{\n";
- code += "\t\tvec3 c = " + p_input_vars[0] + ";\n";
- code += "\t\tvec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n";
- code += "\t\tvec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n";
- code += "\t\tvec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n";
- code += "\t\tfloat d = q.x - min(q.w, q.y);\n";
- code += "\t\tfloat e = 1.0e-10;\n";
- code += "\t\t" + p_output_vars[0] + " = vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\n";
- code += "\t}\n";
+ code += " {\n";
+ code += " vec3 c = " + p_input_vars[0] + ";\n";
+ code += " vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n";
+ code += " vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n";
+ code += " vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n";
+ code += " float d = q.x - min(q.w, q.y);\n";
+ code += " float e = 1.0e-10;\n";
+ code += " " + p_output_vars[0] + " = vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\n";
+ code += " }\n";
} else if (func == FUNC_HSV2RGB) {
- code += "\t{\n";
- code += "\t\tvec3 c = " + p_input_vars[0] + ";\n";
- code += "\t\tvec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n";
- code += "\t\tvec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n";
- code += "\t\t" + p_output_vars[0] + " = c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n";
- code += "\t}\n";
+ code += " {\n";
+ code += " vec3 c = " + p_input_vars[0] + ";\n";
+ code += " vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n";
+ code += " vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n";
+ code += " " + p_output_vars[0] + " = c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n";
+ code += " }\n";
} else {
- code += "\t" + p_output_vars[0] + " = " + String(vec_func_id[func]).replace("$", p_input_vars[0]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + String(vec_func_id[func]).replace("$", p_input_vars[0]) + ";\n";
}
return code;
}
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();
}
@@ -2207,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() {
@@ -2248,22 +2582,24 @@ String VisualShaderNodeColorFunc::generate_code(Shader::Mode p_mode, VisualShade
switch (func) {
case FUNC_GRAYSCALE:
- code += "\t{\n";
- code += "\t\tvec3 c = " + p_input_vars[0] + ";\n";
- code += "\t\tfloat max1 = max(c.r, c.g);\n";
- code += "\t\tfloat max2 = max(max1, c.b);\n";
- code += "\t\tfloat max3 = max(max1, max2);\n";
- code += "\t\t" + p_output_vars[0] + " = vec3(max3, max3, max3);\n";
- code += "\t}\n";
+ code += " {\n";
+ code += " vec3 c = " + p_input_vars[0] + ";\n";
+ code += " float max1 = max(c.r, c.g);\n";
+ code += " float max2 = max(max1, c.b);\n";
+ code += " float max3 = max(max1, max2);\n";
+ code += " " + p_output_vars[0] + " = vec3(max3, max3, max3);\n";
+ code += " }\n";
break;
case FUNC_SEPIA:
- code += "\t{\n";
- code += "\t\tvec3 c = " + p_input_vars[0] + ";\n";
- code += "\t\tfloat r = (c.r * .393) + (c.g *.769) + (c.b * .189);\n";
- code += "\t\tfloat g = (c.r * .349) + (c.g *.686) + (c.b * .168);\n";
- code += "\t\tfloat b = (c.r * .272) + (c.g *.534) + (c.b * .131);\n";
- code += "\t\t" + p_output_vars[0] + " = vec3(r, g, b);\n";
- code += "\t}\n";
+ code += " {\n";
+ code += " vec3 c = " + p_input_vars[0] + ";\n";
+ code += " float r = (c.r * .393) + (c.g *.769) + (c.b * .189);\n";
+ code += " float g = (c.r * .349) + (c.g *.686) + (c.b * .168);\n";
+ code += " float b = (c.r * .272) + (c.g *.534) + (c.b * .131);\n";
+ code += " " + p_output_vars[0] + " = vec3(r, g, b);\n";
+ code += " }\n";
+ break;
+ default:
break;
}
@@ -2271,6 +2607,10 @@ String VisualShaderNodeColorFunc::generate_code(Shader::Mode p_mode, VisualShade
}
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();
}
@@ -2293,6 +2633,7 @@ void VisualShaderNodeColorFunc::_bind_methods() {
BIND_ENUM_CONSTANT(FUNC_GRAYSCALE);
BIND_ENUM_CONSTANT(FUNC_SEPIA);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
}
VisualShaderNodeColorFunc::VisualShaderNodeColorFunc() {
@@ -2331,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 += "\t" + 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();
}
@@ -2364,10 +2709,145 @@ void VisualShaderNodeTransformFunc::_bind_methods() {
BIND_ENUM_CONSTANT(FUNC_INVERSE);
BIND_ENUM_CONSTANT(FUNC_TRANSPOSE);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
}
VisualShaderNodeTransformFunc::VisualShaderNodeTransformFunc() {
- set_input_port_default_value(0, Transform());
+ set_input_port_default_value(0, Transform3D());
+}
+
+////////////// UV Func
+
+String VisualShaderNodeUVFunc::get_caption() const {
+ return "UVFunc";
+}
+
+int VisualShaderNodeUVFunc::get_input_port_count() const {
+ return 3;
+}
+
+VisualShaderNodeUVFunc::PortType VisualShaderNodeUVFunc::get_input_port_type(int p_port) const {
+ switch (p_port) {
+ case 0:
+ [[fallthrough]]; // uv
+ case 1:
+ return PORT_TYPE_VECTOR; // scale
+ case 2:
+ return PORT_TYPE_VECTOR; // offset & pivot
+ default:
+ break;
+ }
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeUVFunc::get_input_port_name(int p_port) const {
+ switch (p_port) {
+ case 0:
+ return "uv";
+ case 1:
+ return "scale";
+ case 2:
+ switch (func) {
+ case FUNC_PANNING:
+ return "offset";
+ case FUNC_SCALING:
+ return "pivot";
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ return "";
+}
+
+String VisualShaderNodeUVFunc::get_input_port_default_hint(int p_port) const {
+ if (p_port == 0) {
+ return "UV";
+ }
+ return "";
+}
+
+int VisualShaderNodeUVFunc::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeUVFunc::PortType VisualShaderNodeUVFunc::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeUVFunc::get_output_port_name(int p_port) const {
+ return "uv";
+}
+
+bool VisualShaderNodeUVFunc::is_show_prop_names() const {
+ return true;
+}
+
+String VisualShaderNodeUVFunc::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 {
+ String code;
+
+ String uv;
+ if (p_input_vars[0].is_empty()) {
+ uv = "vec3(UV.xy, 0.0)";
+ } else {
+ uv = vformat("%s", p_input_vars[0]);
+ }
+ String scale = vformat("%s", p_input_vars[1]);
+ String offset_pivot = vformat("%s", p_input_vars[2]);
+
+ switch (func) {
+ case FUNC_PANNING: {
+ code += vformat(" %s = fma(%s, %s, %s);\n", p_output_vars[0], offset_pivot, scale, uv);
+ } break;
+ case FUNC_SCALING: {
+ code += vformat(" %s = fma((%s - %s), %s, %s);\n", p_output_vars[0], uv, offset_pivot, scale, offset_pivot);
+ } break;
+ default:
+ break;
+ }
+ return code;
+}
+
+void VisualShaderNodeUVFunc::set_function(VisualShaderNodeUVFunc::Function p_func) {
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
+ if (func == p_func) {
+ return;
+ }
+ 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();
+}
+
+VisualShaderNodeUVFunc::Function VisualShaderNodeUVFunc::get_function() const {
+ return func;
+}
+
+Vector<StringName> VisualShaderNodeUVFunc::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("function");
+ return props;
+}
+
+void VisualShaderNodeUVFunc::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeUVFunc::set_function);
+ ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeUVFunc::get_function);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Panning,Scaling"), "set_function", "get_function");
+
+ BIND_ENUM_CONSTANT(FUNC_PANNING);
+ BIND_ENUM_CONSTANT(FUNC_SCALING);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
+}
+
+VisualShaderNodeUVFunc::VisualShaderNodeUVFunc() {
+ set_input_port_default_value(1, Vector3(1.0, 1.0, 0.0)); // scale
+ set_input_port_default_value(2, Vector3()); // offset
}
////////////// Dot Product
@@ -2401,7 +2881,7 @@ String VisualShaderNodeDotProduct::get_output_port_name(int p_port) const {
}
String VisualShaderNodeDotProduct::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 {
- return "\t" + p_output_vars[0] + " = dot(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
+ return " " + p_output_vars[0] + " = dot(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
}
VisualShaderNodeDotProduct::VisualShaderNodeDotProduct() {
@@ -2440,7 +2920,7 @@ String VisualShaderNodeVectorLen::get_output_port_name(int p_port) const {
}
String VisualShaderNodeVectorLen::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 {
- return "\t" + p_output_vars[0] + " = length(" + p_input_vars[0] + ");\n";
+ return " " + p_output_vars[0] + " = length(" + p_input_vars[0] + ");\n";
}
VisualShaderNodeVectorLen::VisualShaderNodeVectorLen() {
@@ -2478,11 +2958,11 @@ String VisualShaderNodeDeterminant::get_output_port_name(int p_port) const {
}
String VisualShaderNodeDeterminant::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 {
- return "\t" + p_output_vars[0] + " = determinant(" + p_input_vars[0] + ");\n";
+ return " " + p_output_vars[0] + " = determinant(" + p_input_vars[0] + ");\n";
}
VisualShaderNodeDeterminant::VisualShaderNodeDeterminant() {
- set_input_port_default_value(0, Transform());
+ set_input_port_default_value(0, Transform3D());
}
////////////// Scalar Derivative Function
@@ -2516,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 += "\t" + 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();
}
@@ -2551,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() {
@@ -2588,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 += "\t" + 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();
}
@@ -2623,27 +3112,36 @@ 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() {
set_input_port_default_value(0, Vector3());
}
-////////////// Scalar Clamp
+////////////// Clamp
-String VisualShaderNodeScalarClamp::get_caption() const {
- return "ScalarClamp";
+String VisualShaderNodeClamp::get_caption() const {
+ return "Clamp";
}
-int VisualShaderNodeScalarClamp::get_input_port_count() const {
+int VisualShaderNodeClamp::get_input_port_count() const {
return 3;
}
-VisualShaderNodeScalarClamp::PortType VisualShaderNodeScalarClamp::get_input_port_type(int p_port) const {
+VisualShaderNodeClamp::PortType VisualShaderNodeClamp::get_input_port_type(int p_port) const {
+ switch (op_type) {
+ case OP_TYPE_INT:
+ return PORT_TYPE_SCALAR_INT;
+ case OP_TYPE_VECTOR:
+ return PORT_TYPE_VECTOR;
+ default:
+ break;
+ }
return PORT_TYPE_SCALAR;
}
-String VisualShaderNodeScalarClamp::get_input_port_name(int p_port) const {
+String VisualShaderNodeClamp::get_input_port_name(int p_port) const {
if (p_port == 0) {
return "";
} else if (p_port == 1) {
@@ -2654,73 +3152,84 @@ String VisualShaderNodeScalarClamp::get_input_port_name(int p_port) const {
return "";
}
-int VisualShaderNodeScalarClamp::get_output_port_count() const {
+int VisualShaderNodeClamp::get_output_port_count() const {
return 1;
}
-VisualShaderNodeScalarClamp::PortType VisualShaderNodeScalarClamp::get_output_port_type(int p_port) const {
+VisualShaderNodeClamp::PortType VisualShaderNodeClamp::get_output_port_type(int p_port) const {
+ switch (op_type) {
+ case OP_TYPE_INT:
+ return PORT_TYPE_SCALAR_INT;
+ case OP_TYPE_VECTOR:
+ return PORT_TYPE_VECTOR;
+ default:
+ break;
+ }
return PORT_TYPE_SCALAR;
}
-String VisualShaderNodeScalarClamp::get_output_port_name(int p_port) const {
+String VisualShaderNodeClamp::get_output_port_name(int p_port) const {
return "";
}
-String VisualShaderNodeScalarClamp::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 {
- return "\t" + p_output_vars[0] + " = clamp(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n";
-}
-
-VisualShaderNodeScalarClamp::VisualShaderNodeScalarClamp() {
- set_input_port_default_value(0, 0.0);
- set_input_port_default_value(1, 0.0);
- set_input_port_default_value(2, 1.0);
-}
-
-////////////// Vector Clamp
-
-String VisualShaderNodeVectorClamp::get_caption() const {
- return "VectorClamp";
-}
-
-int VisualShaderNodeVectorClamp::get_input_port_count() const {
- return 3;
-}
-
-VisualShaderNodeVectorClamp::PortType VisualShaderNodeVectorClamp::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+String VisualShaderNodeClamp::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 {
+ return " " + p_output_vars[0] + " = clamp(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n";
}
-String VisualShaderNodeVectorClamp::get_input_port_name(int p_port) const {
- if (p_port == 0) {
- return "";
- } else if (p_port == 1) {
- return "min";
- } else if (p_port == 2) {
- return "max";
+void VisualShaderNodeClamp::set_op_type(OpType p_op_type) {
+ ERR_FAIL_INDEX((int)p_op_type, int(OP_TYPE_MAX));
+ if (op_type == p_op_type) {
+ return;
}
- return "";
+ switch (p_op_type) {
+ case OP_TYPE_FLOAT:
+ set_input_port_default_value(0, 0.0);
+ set_input_port_default_value(1, 0.0);
+ set_input_port_default_value(2, 0.0);
+ break;
+ case OP_TYPE_INT:
+ set_input_port_default_value(0, 0);
+ set_input_port_default_value(1, 0);
+ set_input_port_default_value(2, 0);
+ break;
+ case OP_TYPE_VECTOR:
+ set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0));
+ set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0));
+ set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0));
+ break;
+ default:
+ break;
+ }
+ op_type = p_op_type;
+ emit_changed();
}
-int VisualShaderNodeVectorClamp::get_output_port_count() const {
- return 1;
+VisualShaderNodeClamp::OpType VisualShaderNodeClamp::get_op_type() const {
+ return op_type;
}
-VisualShaderNodeVectorClamp::PortType VisualShaderNodeVectorClamp::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+Vector<StringName> VisualShaderNodeClamp::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("op_type");
+ return props;
}
-String VisualShaderNodeVectorClamp::get_output_port_name(int p_port) const {
- return "";
-}
+void VisualShaderNodeClamp::_bind_methods() {
+ 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");
-String VisualShaderNodeVectorClamp::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 {
- return "\t" + p_output_vars[0] + " = clamp(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n";
+ BIND_ENUM_CONSTANT(OP_TYPE_FLOAT);
+ BIND_ENUM_CONSTANT(OP_TYPE_INT);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR);
+ BIND_ENUM_CONSTANT(OP_TYPE_MAX);
}
-VisualShaderNodeVectorClamp::VisualShaderNodeVectorClamp() {
- set_input_port_default_value(0, Vector3(0, 0, 0));
- set_input_port_default_value(1, Vector3(0, 0, 0));
- set_input_port_default_value(2, Vector3(1, 1, 1));
+VisualShaderNodeClamp::VisualShaderNodeClamp() {
+ set_input_port_default_value(0, 0.0);
+ set_input_port_default_value(1, 0.0);
+ set_input_port_default_value(2, 1.0);
}
////////////// FaceForward
@@ -2763,7 +3272,7 @@ String VisualShaderNodeFaceForward::get_output_port_name(int p_port) const {
}
String VisualShaderNodeFaceForward::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 {
- return "\t" + p_output_vars[0] + " = faceforward(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n";
+ return " " + p_output_vars[0] + " = faceforward(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n";
}
VisualShaderNodeFaceForward::VisualShaderNodeFaceForward() {
@@ -2810,7 +3319,7 @@ String VisualShaderNodeOuterProduct::get_output_port_name(int p_port) const {
}
String VisualShaderNodeOuterProduct::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 {
- return "\t" + p_output_vars[0] + " = outerProduct(vec4(" + p_input_vars[0] + ", 0.0), vec4(" + p_input_vars[1] + ", 0.0));\n";
+ return " " + p_output_vars[0] + " = outerProduct(vec4(" + p_input_vars[0] + ", 0.0), vec4(" + p_input_vars[1] + ", 0.0));\n";
}
VisualShaderNodeOuterProduct::VisualShaderNodeOuterProduct() {
@@ -2818,24 +3327,32 @@ VisualShaderNodeOuterProduct::VisualShaderNodeOuterProduct() {
set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0));
}
-////////////// Vector-Scalar Step
+////////////// Step
-String VisualShaderNodeVectorScalarStep::get_caption() const {
- return "VectorScalarStep";
+String VisualShaderNodeStep::get_caption() const {
+ return "Step";
}
-int VisualShaderNodeVectorScalarStep::get_input_port_count() const {
+int VisualShaderNodeStep::get_input_port_count() const {
return 2;
}
-VisualShaderNodeVectorScalarStep::PortType VisualShaderNodeVectorScalarStep::get_input_port_type(int p_port) const {
- if (p_port == 0) {
- return PORT_TYPE_SCALAR;
+VisualShaderNodeStep::PortType VisualShaderNodeStep::get_input_port_type(int p_port) const {
+ switch (op_type) {
+ case OP_TYPE_VECTOR:
+ return PORT_TYPE_VECTOR;
+ case OP_TYPE_VECTOR_SCALAR:
+ if (p_port == 1) {
+ return PORT_TYPE_VECTOR;
+ }
+ break;
+ default:
+ break;
}
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_SCALAR;
}
-String VisualShaderNodeVectorScalarStep::get_input_port_name(int p_port) const {
+String VisualShaderNodeStep::get_input_port_name(int p_port) const {
if (p_port == 0) {
return "edge";
} else if (p_port == 1) {
@@ -2844,89 +3361,120 @@ String VisualShaderNodeVectorScalarStep::get_input_port_name(int p_port) const {
return "";
}
-int VisualShaderNodeVectorScalarStep::get_output_port_count() const {
+int VisualShaderNodeStep::get_output_port_count() const {
return 1;
}
-VisualShaderNodeVectorScalarStep::PortType VisualShaderNodeVectorScalarStep::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+VisualShaderNodeStep::PortType VisualShaderNodeStep::get_output_port_type(int p_port) const {
+ switch (op_type) {
+ case OP_TYPE_VECTOR:
+ return PORT_TYPE_VECTOR;
+ case OP_TYPE_VECTOR_SCALAR:
+ return PORT_TYPE_VECTOR;
+ default:
+ break;
+ }
+ return PORT_TYPE_SCALAR;
}
-String VisualShaderNodeVectorScalarStep::get_output_port_name(int p_port) const {
+String VisualShaderNodeStep::get_output_port_name(int p_port) const {
return "";
}
-String VisualShaderNodeVectorScalarStep::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 {
- return "\t" + p_output_vars[0] + " = step(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
-}
-
-VisualShaderNodeVectorScalarStep::VisualShaderNodeVectorScalarStep() {
- set_input_port_default_value(0, 0.0);
- set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0));
-}
-
-////////////// Scalar SmoothStep
-
-String VisualShaderNodeScalarSmoothStep::get_caption() const {
- return "ScalarSmoothStep";
-}
-
-int VisualShaderNodeScalarSmoothStep::get_input_port_count() const {
- return 3;
+void VisualShaderNodeStep::set_op_type(OpType p_op_type) {
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
+ if (op_type == p_op_type) {
+ return;
+ }
+ switch (p_op_type) {
+ case OP_TYPE_SCALAR:
+ if (op_type == OP_TYPE_VECTOR) {
+ set_input_port_default_value(0, 0.0); // edge
+ }
+ if (op_type == OP_TYPE_VECTOR || op_type == OP_TYPE_VECTOR_SCALAR) {
+ set_input_port_default_value(1, 0.0); // x
+ }
+ break;
+ case OP_TYPE_VECTOR:
+ if (op_type == OP_TYPE_SCALAR || op_type == OP_TYPE_VECTOR_SCALAR) {
+ set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0)); // edge
+ }
+ if (op_type == OP_TYPE_SCALAR) {
+ set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0)); // x
+ }
+ break;
+ case OP_TYPE_VECTOR_SCALAR:
+ if (op_type == OP_TYPE_VECTOR) {
+ set_input_port_default_value(0, 0.0); // edge
+ }
+ if (op_type == OP_TYPE_SCALAR) {
+ set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0)); // x
+ }
+ break;
+ default:
+ break;
+ }
+ op_type = p_op_type;
+ emit_changed();
}
-VisualShaderNodeScalarSmoothStep::PortType VisualShaderNodeScalarSmoothStep::get_input_port_type(int p_port) const {
- return PORT_TYPE_SCALAR;
+VisualShaderNodeStep::OpType VisualShaderNodeStep::get_op_type() const {
+ return op_type;
}
-String VisualShaderNodeScalarSmoothStep::get_input_port_name(int p_port) const {
- if (p_port == 0) {
- return "edge0";
- } else if (p_port == 1) {
- return "edge1";
- } else if (p_port == 2) {
- return "x";
- }
- return "";
+String VisualShaderNodeStep::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 {
+ return " " + p_output_vars[0] + " = step(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
}
-int VisualShaderNodeScalarSmoothStep::get_output_port_count() const {
- return 1;
+Vector<StringName> VisualShaderNodeStep::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("op_type");
+ return props;
}
-VisualShaderNodeScalarSmoothStep::PortType VisualShaderNodeScalarSmoothStep::get_output_port_type(int p_port) const {
- return PORT_TYPE_SCALAR;
-}
+void VisualShaderNodeStep::_bind_methods() {
+ 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);
-String VisualShaderNodeScalarSmoothStep::get_output_port_name(int p_port) const {
- return "";
-}
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector,VectorScalar"), "set_op_type", "get_op_type");
-String VisualShaderNodeScalarSmoothStep::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 {
- return "\t" + p_output_vars[0] + " = smoothstep(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n";
+ BIND_ENUM_CONSTANT(OP_TYPE_SCALAR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_SCALAR);
+ BIND_ENUM_CONSTANT(OP_TYPE_MAX);
}
-VisualShaderNodeScalarSmoothStep::VisualShaderNodeScalarSmoothStep() {
+VisualShaderNodeStep::VisualShaderNodeStep() {
set_input_port_default_value(0, 0.0);
set_input_port_default_value(1, 0.0);
- set_input_port_default_value(2, 0.0);
}
-////////////// Vector SmoothStep
+////////////// SmoothStep
-String VisualShaderNodeVectorSmoothStep::get_caption() const {
- return "VectorSmoothStep";
+String VisualShaderNodeSmoothStep::get_caption() const {
+ return "SmoothStep";
}
-int VisualShaderNodeVectorSmoothStep::get_input_port_count() const {
+int VisualShaderNodeSmoothStep::get_input_port_count() const {
return 3;
}
-VisualShaderNodeVectorSmoothStep::PortType VisualShaderNodeVectorSmoothStep::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+VisualShaderNodeSmoothStep::PortType VisualShaderNodeSmoothStep::get_input_port_type(int p_port) const {
+ switch (op_type) {
+ case OP_TYPE_VECTOR:
+ return PORT_TYPE_VECTOR;
+ case OP_TYPE_VECTOR_SCALAR:
+ if (p_port == 2) {
+ return PORT_TYPE_VECTOR; // x
+ }
+ break;
+ default:
+ break;
+ }
+ return PORT_TYPE_SCALAR;
}
-String VisualShaderNodeVectorSmoothStep::get_input_port_name(int p_port) const {
+String VisualShaderNodeSmoothStep::get_input_port_name(int p_port) const {
if (p_port == 0) {
return "edge0";
} else if (p_port == 1) {
@@ -2937,78 +3485,96 @@ String VisualShaderNodeVectorSmoothStep::get_input_port_name(int p_port) const {
return "";
}
-int VisualShaderNodeVectorSmoothStep::get_output_port_count() const {
+int VisualShaderNodeSmoothStep::get_output_port_count() const {
return 1;
}
-VisualShaderNodeVectorSmoothStep::PortType VisualShaderNodeVectorSmoothStep::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+VisualShaderNodeSmoothStep::PortType VisualShaderNodeSmoothStep::get_output_port_type(int p_port) const {
+ switch (op_type) {
+ case OP_TYPE_VECTOR:
+ return PORT_TYPE_VECTOR;
+ case OP_TYPE_VECTOR_SCALAR:
+ return PORT_TYPE_VECTOR;
+ default:
+ break;
+ }
+ return PORT_TYPE_SCALAR;
}
-String VisualShaderNodeVectorSmoothStep::get_output_port_name(int p_port) const {
+String VisualShaderNodeSmoothStep::get_output_port_name(int p_port) const {
return "";
}
-String VisualShaderNodeVectorSmoothStep::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 {
- return "\t" + p_output_vars[0] + " = smoothstep(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n";
-}
-
-VisualShaderNodeVectorSmoothStep::VisualShaderNodeVectorSmoothStep() {
- set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0));
- set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0));
- set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0));
-}
-
-////////////// Vector-Scalar SmoothStep
-
-String VisualShaderNodeVectorScalarSmoothStep::get_caption() const {
- return "VectorScalarSmoothStep";
-}
-
-int VisualShaderNodeVectorScalarSmoothStep::get_input_port_count() const {
- return 3;
-}
-
-VisualShaderNodeVectorScalarSmoothStep::PortType VisualShaderNodeVectorScalarSmoothStep::get_input_port_type(int p_port) const {
- if (p_port == 0) {
- return PORT_TYPE_SCALAR;
- } else if (p_port == 1) {
- return PORT_TYPE_SCALAR;
+void VisualShaderNodeSmoothStep::set_op_type(OpType p_op_type) {
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
+ if (op_type == p_op_type) {
+ return;
}
- return PORT_TYPE_VECTOR;
-}
-
-String VisualShaderNodeVectorScalarSmoothStep::get_input_port_name(int p_port) const {
- if (p_port == 0) {
- return "edge0";
- } else if (p_port == 1) {
- return "edge1";
- } else if (p_port == 2) {
- return "x";
+ switch (p_op_type) {
+ case OP_TYPE_SCALAR:
+ if (op_type == OP_TYPE_VECTOR) {
+ set_input_port_default_value(0, 0.0); // edge0
+ set_input_port_default_value(1, 0.0); // edge1
+ }
+ if (op_type == OP_TYPE_VECTOR || op_type == OP_TYPE_VECTOR_SCALAR) {
+ set_input_port_default_value(2, 0.0); // x
+ }
+ break;
+ case OP_TYPE_VECTOR:
+ if (op_type == OP_TYPE_SCALAR || op_type == OP_TYPE_VECTOR_SCALAR) {
+ set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0)); // edge0
+ set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0)); // edge1
+ }
+ if (op_type == OP_TYPE_SCALAR) {
+ set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0)); // x
+ }
+ break;
+ case OP_TYPE_VECTOR_SCALAR:
+ if (op_type == OP_TYPE_VECTOR) {
+ set_input_port_default_value(0, 0.0); // edge0
+ set_input_port_default_value(1, 0.0); // edge1
+ }
+ if (op_type == OP_TYPE_SCALAR) {
+ set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0)); // x
+ }
+ break;
+ default:
+ break;
}
- return "";
+ op_type = p_op_type;
+ emit_changed();
}
-int VisualShaderNodeVectorScalarSmoothStep::get_output_port_count() const {
- return 1;
+VisualShaderNodeSmoothStep::OpType VisualShaderNodeSmoothStep::get_op_type() const {
+ return op_type;
}
-VisualShaderNodeVectorScalarSmoothStep::PortType VisualShaderNodeVectorScalarSmoothStep::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+String VisualShaderNodeSmoothStep::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 {
+ return " " + p_output_vars[0] + " = smoothstep(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n";
}
-String VisualShaderNodeVectorScalarSmoothStep::get_output_port_name(int p_port) const {
- return "";
+Vector<StringName> VisualShaderNodeSmoothStep::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("op_type");
+ return props;
}
-String VisualShaderNodeVectorScalarSmoothStep::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 {
- return "\t" + p_output_vars[0] + " = smoothstep(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n";
+void VisualShaderNodeSmoothStep::_bind_methods() {
+ 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");
+
+ BIND_ENUM_CONSTANT(OP_TYPE_SCALAR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_SCALAR);
+ BIND_ENUM_CONSTANT(OP_TYPE_MAX);
}
-VisualShaderNodeVectorScalarSmoothStep::VisualShaderNodeVectorScalarSmoothStep() {
+VisualShaderNodeSmoothStep::VisualShaderNodeSmoothStep() {
set_input_port_default_value(0, 0.0);
set_input_port_default_value(1, 0.0);
- set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0));
+ set_input_port_default_value(2, 0.0);
}
////////////// Distance
@@ -3047,7 +3613,7 @@ String VisualShaderNodeVectorDistance::get_output_port_name(int p_port) const {
}
String VisualShaderNodeVectorDistance::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 {
- return "\t" + p_output_vars[0] + " = distance(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
+ return " " + p_output_vars[0] + " = distance(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
}
VisualShaderNodeVectorDistance::VisualShaderNodeVectorDistance() {
@@ -3097,7 +3663,7 @@ String VisualShaderNodeVectorRefract::get_output_port_name(int p_port) const {
}
String VisualShaderNodeVectorRefract::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 {
- return "\t" + p_output_vars[0] + " = refract(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n";
+ return " " + p_output_vars[0] + " = refract(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n";
}
VisualShaderNodeVectorRefract::VisualShaderNodeVectorRefract() {
@@ -3106,21 +3672,32 @@ VisualShaderNodeVectorRefract::VisualShaderNodeVectorRefract() {
set_input_port_default_value(2, 0.0);
}
-////////////// Scalar Mix
+////////////// Mix
-String VisualShaderNodeScalarInterp::get_caption() const {
- return "ScalarMix";
+String VisualShaderNodeMix::get_caption() const {
+ return "Mix";
}
-int VisualShaderNodeScalarInterp::get_input_port_count() const {
+int VisualShaderNodeMix::get_input_port_count() const {
return 3;
}
-VisualShaderNodeScalarInterp::PortType VisualShaderNodeScalarInterp::get_input_port_type(int p_port) const {
+VisualShaderNodeMix::PortType VisualShaderNodeMix::get_input_port_type(int p_port) const {
+ switch (op_type) {
+ case OP_TYPE_VECTOR:
+ return PORT_TYPE_VECTOR;
+ case OP_TYPE_VECTOR_SCALAR:
+ if (p_port == 2) {
+ break;
+ }
+ return PORT_TYPE_VECTOR;
+ default:
+ break;
+ }
return PORT_TYPE_SCALAR;
}
-String VisualShaderNodeScalarInterp::get_input_port_name(int p_port) const {
+String VisualShaderNodeMix::get_input_port_name(int p_port) const {
if (p_port == 0) {
return "a";
} else if (p_port == 1) {
@@ -3130,121 +3707,90 @@ String VisualShaderNodeScalarInterp::get_input_port_name(int p_port) const {
}
}
-int VisualShaderNodeScalarInterp::get_output_port_count() const {
+int VisualShaderNodeMix::get_output_port_count() const {
return 1;
}
-VisualShaderNodeScalarInterp::PortType VisualShaderNodeScalarInterp::get_output_port_type(int p_port) const {
+VisualShaderNodeMix::PortType VisualShaderNodeMix::get_output_port_type(int p_port) const {
+ switch (op_type) {
+ case OP_TYPE_VECTOR:
+ return PORT_TYPE_VECTOR;
+ case OP_TYPE_VECTOR_SCALAR:
+ return PORT_TYPE_VECTOR;
+ default:
+ break;
+ }
return PORT_TYPE_SCALAR;
}
-String VisualShaderNodeScalarInterp::get_output_port_name(int p_port) const {
+String VisualShaderNodeMix::get_output_port_name(int p_port) const {
return "mix";
}
-String VisualShaderNodeScalarInterp::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 {
- return "\t" + p_output_vars[0] + " = mix(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n";
-}
-
-VisualShaderNodeScalarInterp::VisualShaderNodeScalarInterp() {
- set_input_port_default_value(0, 0.0);
- set_input_port_default_value(1, 1.0);
- set_input_port_default_value(2, 0.5);
-}
-
-////////////// Vector Mix
-
-String VisualShaderNodeVectorInterp::get_caption() const {
- return "VectorMix";
-}
-
-int VisualShaderNodeVectorInterp::get_input_port_count() const {
- return 3;
-}
-
-VisualShaderNodeVectorInterp::PortType VisualShaderNodeVectorInterp::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
-}
-
-String VisualShaderNodeVectorInterp::get_input_port_name(int p_port) const {
- if (p_port == 0) {
- return "a";
- } else if (p_port == 1) {
- return "b";
- } else {
- return "weight";
+void VisualShaderNodeMix::set_op_type(OpType p_op_type) {
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
+ if (op_type == p_op_type) {
+ return;
}
-}
-
-int VisualShaderNodeVectorInterp::get_output_port_count() const {
- return 1;
-}
-
-VisualShaderNodeVectorInterp::PortType VisualShaderNodeVectorInterp::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
-}
-
-String VisualShaderNodeVectorInterp::get_output_port_name(int p_port) const {
- return "mix";
-}
-
-String VisualShaderNodeVectorInterp::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 {
- return "\t" + p_output_vars[0] + " = mix(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n";
-}
-
-VisualShaderNodeVectorInterp::VisualShaderNodeVectorInterp() {
- set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0));
- set_input_port_default_value(1, Vector3(1.0, 1.0, 1.0));
- set_input_port_default_value(2, Vector3(0.5, 0.5, 0.5));
-}
-
-////////////// Vector Mix (by scalar)
-
-String VisualShaderNodeVectorScalarMix::get_caption() const {
- return "VectorScalarMix";
-}
-
-int VisualShaderNodeVectorScalarMix::get_input_port_count() const {
- return 3;
-}
-
-VisualShaderNodeVectorScalarMix::PortType VisualShaderNodeVectorScalarMix::get_input_port_type(int p_port) const {
- if (p_port == 2) {
- return PORT_TYPE_SCALAR;
+ switch (p_op_type) {
+ case OP_TYPE_SCALAR:
+ set_input_port_default_value(0, 0.0); // a
+ set_input_port_default_value(1, 1.0); // b
+ if (op_type == OP_TYPE_VECTOR) {
+ set_input_port_default_value(2, 0.5); // weight
+ }
+ break;
+ case OP_TYPE_VECTOR:
+ set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0)); // a
+ set_input_port_default_value(1, Vector3(1.0, 1.0, 1.0)); // b
+ if (op_type == OP_TYPE_SCALAR || op_type == OP_TYPE_VECTOR_SCALAR) {
+ set_input_port_default_value(2, Vector3(0.5, 0.5, 0.5)); // weight
+ }
+ break;
+ case OP_TYPE_VECTOR_SCALAR:
+ set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0)); // a
+ set_input_port_default_value(1, Vector3(1.0, 1.0, 1.0)); // b
+ if (op_type == OP_TYPE_VECTOR) {
+ set_input_port_default_value(2, 0.5); // weight
+ }
+ break;
+ default:
+ break;
}
- return PORT_TYPE_VECTOR;
+ op_type = p_op_type;
+ emit_changed();
}
-String VisualShaderNodeVectorScalarMix::get_input_port_name(int p_port) const {
- if (p_port == 0) {
- return "a";
- } else if (p_port == 1) {
- return "b";
- } else {
- return "weight";
- }
+VisualShaderNodeMix::OpType VisualShaderNodeMix::get_op_type() const {
+ return op_type;
}
-int VisualShaderNodeVectorScalarMix::get_output_port_count() const {
- return 1;
+String VisualShaderNodeMix::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 {
+ return " " + p_output_vars[0] + " = mix(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n";
}
-VisualShaderNodeVectorScalarMix::PortType VisualShaderNodeVectorScalarMix::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+Vector<StringName> VisualShaderNodeMix::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("op_type");
+ return props;
}
-String VisualShaderNodeVectorScalarMix::get_output_port_name(int p_port) const {
- return "mix";
-}
+void VisualShaderNodeMix::_bind_methods() {
+ 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);
-String VisualShaderNodeVectorScalarMix::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 {
- return "\t" + p_output_vars[0] + " = mix(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n";
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector,VectorScalar"), "set_op_type", "get_op_type");
+
+ BIND_ENUM_CONSTANT(OP_TYPE_SCALAR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_SCALAR);
+ BIND_ENUM_CONSTANT(OP_TYPE_MAX);
}
-VisualShaderNodeVectorScalarMix::VisualShaderNodeVectorScalarMix() {
- set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0));
- set_input_port_default_value(1, Vector3(1.0, 1.0, 1.0));
- set_input_port_default_value(2, 0.5);
+VisualShaderNodeMix::VisualShaderNodeMix() {
+ set_input_port_default_value(0, 0.0); // a
+ set_input_port_default_value(1, 1.0); // b
+ set_input_port_default_value(2, 0.5); // weight
}
////////////// Vector Compose
@@ -3284,7 +3830,7 @@ String VisualShaderNodeVectorCompose::get_output_port_name(int p_port) const {
}
String VisualShaderNodeVectorCompose::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 {
- return "\t" + p_output_vars[0] + " = vec3(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n";
+ return " " + p_output_vars[0] + " = vec3(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n";
}
VisualShaderNodeVectorCompose::VisualShaderNodeVectorCompose() {
@@ -3332,7 +3878,7 @@ String VisualShaderNodeTransformCompose::get_output_port_name(int p_port) const
}
String VisualShaderNodeTransformCompose::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 {
- return "\t" + p_output_vars[0] + " = mat4(vec4(" + p_input_vars[0] + ", 0.0), vec4(" + p_input_vars[1] + ", 0.0), vec4(" + p_input_vars[2] + ", 0.0), vec4(" + p_input_vars[3] + ", 1.0));\n";
+ return " " + p_output_vars[0] + " = mat4(vec4(" + p_input_vars[0] + ", 0.0), vec4(" + p_input_vars[1] + ", 0.0), vec4(" + p_input_vars[2] + ", 0.0), vec4(" + p_input_vars[3] + ", 1.0));\n";
}
VisualShaderNodeTransformCompose::VisualShaderNodeTransformCompose() {
@@ -3379,9 +3925,9 @@ String VisualShaderNodeVectorDecompose::get_output_port_name(int p_port) const {
String VisualShaderNodeVectorDecompose::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 {
String code;
- code += "\t" + p_output_vars[0] + " = " + p_input_vars[0] + ".x;\n";
- code += "\t" + p_output_vars[1] + " = " + p_input_vars[0] + ".y;\n";
- code += "\t" + p_output_vars[2] + " = " + p_input_vars[0] + ".z;\n";
+ code += " " + p_output_vars[0] + " = " + p_input_vars[0] + ".x;\n";
+ code += " " + p_output_vars[1] + " = " + p_input_vars[0] + ".y;\n";
+ code += " " + p_output_vars[2] + " = " + p_input_vars[0] + ".z;\n";
return code;
}
@@ -3429,15 +3975,15 @@ String VisualShaderNodeTransformDecompose::get_output_port_name(int p_port) cons
String VisualShaderNodeTransformDecompose::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 {
String code;
- code += "\t" + p_output_vars[0] + " = " + p_input_vars[0] + "[0].xyz;\n";
- code += "\t" + p_output_vars[1] + " = " + p_input_vars[0] + "[1].xyz;\n";
- code += "\t" + p_output_vars[2] + " = " + p_input_vars[0] + "[2].xyz;\n";
- code += "\t" + p_output_vars[3] + " = " + p_input_vars[0] + "[3].xyz;\n";
+ code += " " + p_output_vars[0] + " = " + p_input_vars[0] + "[0].xyz;\n";
+ code += " " + p_output_vars[1] + " = " + p_input_vars[0] + "[1].xyz;\n";
+ code += " " + p_output_vars[2] + " = " + p_input_vars[0] + "[2].xyz;\n";
+ code += " " + p_output_vars[3] + " = " + p_input_vars[0] + "[3].xyz;\n";
return code;
}
VisualShaderNodeTransformDecompose::VisualShaderNodeTransformDecompose() {
- set_input_port_default_value(0, Transform());
+ set_input_port_default_value(0, Transform3D());
}
////////////// Float Uniform
@@ -3487,7 +4033,7 @@ String VisualShaderNodeFloatUniform::generate_global(Shader::Mode p_mode, Visual
}
String VisualShaderNodeFloatUniform::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 {
- return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
+ return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
}
bool VisualShaderNodeFloatUniform::is_show_prop_names() const {
@@ -3499,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();
}
@@ -3508,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();
}
@@ -3517,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();
}
@@ -3526,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();
}
@@ -3535,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();
}
@@ -3544,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();
}
@@ -3581,12 +4146,17 @@ 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 {
return true; // all qualifiers are supported
}
+bool VisualShaderNodeFloatUniform::is_convertible_to_constant() const {
+ return true; // conversion is allowed
+}
+
Vector<StringName> VisualShaderNodeFloatUniform::get_editable_properties() const {
Vector<StringName> props = VisualShaderNodeUniform::get_editable_properties();
props.push_back("hint");
@@ -3654,7 +4224,7 @@ String VisualShaderNodeIntUniform::generate_global(Shader::Mode p_mode, VisualSh
}
String VisualShaderNodeIntUniform::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 {
- return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
+ return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
}
bool VisualShaderNodeIntUniform::is_show_prop_names() const {
@@ -3666,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();
}
@@ -3675,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();
}
@@ -3684,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();
}
@@ -3693,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();
}
@@ -3701,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();
}
@@ -3710,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();
}
@@ -3738,7 +4327,7 @@ void VisualShaderNodeIntUniform::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_default_value", "value"), &VisualShaderNodeIntUniform::set_default_value);
ClassDB::bind_method(D_METHOD("get_default_value"), &VisualShaderNodeIntUniform::get_default_value);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "hint", PROPERTY_HINT_ENUM, "None,Range,Range+Step"), "set_hint", "get_hint");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "hint", PROPERTY_HINT_ENUM, "None,Range,Range + Step"), "set_hint", "get_hint");
ADD_PROPERTY(PropertyInfo(Variant::INT, "min"), "set_min", "get_min");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max"), "set_max", "get_max");
ADD_PROPERTY(PropertyInfo(Variant::INT, "step"), "set_step", "get_step");
@@ -3748,12 +4337,17 @@ 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 {
return true; // all qualifiers are supported
}
+bool VisualShaderNodeIntUniform::is_convertible_to_constant() const {
+ return true; // conversion is allowed
+}
+
Vector<StringName> VisualShaderNodeIntUniform::get_editable_properties() const {
Vector<StringName> props = VisualShaderNodeUniform::get_editable_properties();
props.push_back("hint");
@@ -3804,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();
}
@@ -3813,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();
}
@@ -3836,7 +4436,7 @@ String VisualShaderNodeBooleanUniform::generate_global(Shader::Mode p_mode, Visu
}
String VisualShaderNodeBooleanUniform::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 {
- return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
+ return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
}
bool VisualShaderNodeBooleanUniform::is_show_prop_names() const {
@@ -3862,6 +4462,10 @@ bool VisualShaderNodeBooleanUniform::is_qualifier_supported(Qualifier p_qual) co
return true; // all qualifiers are supported
}
+bool VisualShaderNodeBooleanUniform::is_convertible_to_constant() const {
+ return true; // conversion is allowed
+}
+
Vector<StringName> VisualShaderNodeBooleanUniform::get_editable_properties() const {
Vector<StringName> props = VisualShaderNodeUniform::get_editable_properties();
props.push_back("default_value_enabled");
@@ -3905,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();
}
@@ -3914,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();
}
@@ -3932,8 +4542,8 @@ String VisualShaderNodeColorUniform::generate_global(Shader::Mode p_mode, Visual
}
String VisualShaderNodeColorUniform::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 {
- String code = "\t" + p_output_vars[0] + " = " + get_uniform_name() + ".rgb;\n";
- code += "\t" + p_output_vars[1] + " = " + get_uniform_name() + ".a;\n";
+ String code = " " + p_output_vars[0] + " = " + get_uniform_name() + ".rgb;\n";
+ code += " " + p_output_vars[1] + " = " + get_uniform_name() + ".a;\n";
return code;
}
@@ -3956,6 +4566,10 @@ bool VisualShaderNodeColorUniform::is_qualifier_supported(Qualifier p_qual) cons
return true; // all qualifiers are supported
}
+bool VisualShaderNodeColorUniform::is_convertible_to_constant() const {
+ return true; // conversion is allowed
+}
+
Vector<StringName> VisualShaderNodeColorUniform::get_editable_properties() const {
Vector<StringName> props = VisualShaderNodeUniform::get_editable_properties();
props.push_back("default_value_enabled");
@@ -4026,7 +4640,7 @@ String VisualShaderNodeVec3Uniform::generate_global(Shader::Mode p_mode, VisualS
}
String VisualShaderNodeVec3Uniform::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 {
- return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
+ return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
}
void VisualShaderNodeVec3Uniform::_bind_methods() {
@@ -4052,6 +4666,10 @@ bool VisualShaderNodeVec3Uniform::is_qualifier_supported(Qualifier p_qual) const
return true; // all qualifiers are supported
}
+bool VisualShaderNodeVec3Uniform::is_convertible_to_constant() const {
+ return true; // conversion is allowed
+}
+
Vector<StringName> VisualShaderNodeVec3Uniform::get_editable_properties() const {
Vector<StringName> props = VisualShaderNodeUniform::get_editable_properties();
props.push_back("default_value_enabled");
@@ -4103,12 +4721,12 @@ bool VisualShaderNodeTransformUniform::is_default_value_enabled() const {
return default_value_enabled;
}
-void VisualShaderNodeTransformUniform::set_default_value(const Transform &p_value) {
+void VisualShaderNodeTransformUniform::set_default_value(const Transform3D &p_value) {
default_value = p_value;
emit_changed();
}
-Transform VisualShaderNodeTransformUniform::get_default_value() const {
+Transform3D VisualShaderNodeTransformUniform::get_default_value() const {
return default_value;
}
@@ -4126,7 +4744,7 @@ String VisualShaderNodeTransformUniform::generate_global(Shader::Mode p_mode, Vi
}
String VisualShaderNodeTransformUniform::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 {
- return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
+ return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
}
void VisualShaderNodeTransformUniform::_bind_methods() {
@@ -4137,7 +4755,7 @@ void VisualShaderNodeTransformUniform::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_default_value"), &VisualShaderNodeTransformUniform::get_default_value);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "default_value_enabled"), "set_default_value_enabled", "is_default_value_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "default_value"), "set_default_value", "get_default_value");
+ ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "default_value"), "set_default_value", "get_default_value");
}
bool VisualShaderNodeTransformUniform::is_show_prop_names() const {
@@ -4149,7 +4767,14 @@ bool VisualShaderNodeTransformUniform::is_use_prop_slots() const {
}
bool VisualShaderNodeTransformUniform::is_qualifier_supported(Qualifier p_qual) const {
- return true; // all qualifiers are supported
+ if (p_qual == Qualifier::QUAL_INSTANCE) {
+ return false;
+ }
+ return true;
+}
+
+bool VisualShaderNodeTransformUniform::is_convertible_to_constant() const {
+ return true; // conversion is allowed
}
Vector<StringName> VisualShaderNodeTransformUniform::get_editable_properties() const {
@@ -4230,12 +4855,15 @@ String VisualShaderNodeTextureUniform::generate_global(Shader::Mode p_mode, Visu
code += " : hint_albedo;\n";
}
break;
- case TYPE_NORMALMAP:
+ case TYPE_NORMAL_MAP:
code += " : hint_normal;\n";
break;
case TYPE_ANISO:
code += " : hint_aniso;\n";
break;
+ default:
+ code += ";\n";
+ break;
}
return code;
@@ -4246,29 +4874,40 @@ bool VisualShaderNodeTextureUniform::is_code_generated() const {
}
String VisualShaderNodeTextureUniform::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 {
+ String default_uv;
+ if (p_mode != Shader::MODE_PARTICLES && p_mode != Shader::MODE_SKY) {
+ default_uv = "UV.xy";
+ } else {
+ default_uv = "vec2(0.0)";
+ }
+
String id = get_uniform_name();
- String code = "\t{\n";
+ String code = " {\n";
if (p_input_vars[0] == String()) { // Use UV by default.
if (p_input_vars[1] == String()) {
- code += "\t\tvec4 n_tex_read = texture(" + id + ", UV.xy);\n";
+ code += " vec4 n_tex_read = texture(" + id + ", " + default_uv + ");\n";
} else {
- code += "\t\tvec4 n_tex_read = textureLod(" + id + ", UV.xy, " + p_input_vars[1] + ");\n";
+ code += " vec4 n_tex_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n";
}
} else if (p_input_vars[1] == String()) {
//no lod
- code += "\t\tvec4 n_tex_read = texture(" + id + ", " + p_input_vars[0] + ".xy);\n";
+ code += " vec4 n_tex_read = texture(" + id + ", " + p_input_vars[0] + ".xy);\n";
} else {
- code += "\t\tvec4 n_tex_read = textureLod(" + id + ", " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n";
+ code += " vec4 n_tex_read = textureLod(" + id + ", " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n";
}
- code += "\t\t" + p_output_vars[0] + " = n_tex_read.rgb;\n";
- code += "\t\t" + p_output_vars[1] + " = n_tex_read.a;\n";
- code += "\t}\n";
+ code += " " + p_output_vars[0] + " = n_tex_read.rgb;\n";
+ code += " " + p_output_vars[1] + " = n_tex_read.a;\n";
+ code += " }\n";
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();
}
@@ -4276,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();
}
@@ -4299,21 +4942,23 @@ void VisualShaderNodeTextureUniform::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_color_default", "type"), &VisualShaderNodeTextureUniform::set_color_default);
ClassDB::bind_method(D_METHOD("get_color_default"), &VisualShaderNodeTextureUniform::get_color_default);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normalmap,Aniso"), "set_texture_type", "get_texture_type");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normal Map,Anisotropic"), "set_texture_type", "get_texture_type");
ADD_PROPERTY(PropertyInfo(Variant::INT, "color_default", PROPERTY_HINT_ENUM, "White Default,Black Default"), "set_color_default", "get_color_default");
BIND_ENUM_CONSTANT(TYPE_DATA);
BIND_ENUM_CONSTANT(TYPE_COLOR);
- BIND_ENUM_CONSTANT(TYPE_NORMALMAP);
+ 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 {
if (p_port == 0) {
- return "UV.xy";
+ return "default";
}
return "";
}
@@ -4326,10 +4971,16 @@ bool VisualShaderNodeTextureUniform::is_qualifier_supported(Qualifier p_qual) co
return true;
case Qualifier::QUAL_INSTANCE:
return false;
+ default:
+ break;
}
return false;
}
+bool VisualShaderNodeTextureUniform::is_convertible_to_constant() const {
+ return false; // conversion is not allowed
+}
+
VisualShaderNodeTextureUniform::VisualShaderNodeTextureUniform() {
simple_decl = false;
}
@@ -4345,9 +4996,7 @@ int VisualShaderNodeTextureUniformTriplanar::get_input_port_count() const {
}
VisualShaderNodeTextureUniform::PortType VisualShaderNodeTextureUniformTriplanar::get_input_port_type(int p_port) const {
- if (p_port == 0) {
- return PORT_TYPE_VECTOR;
- } else if (p_port == 1) {
+ if (p_port == 0 || p_port == 1) {
return PORT_TYPE_VECTOR;
}
return PORT_TYPE_SCALAR;
@@ -4366,20 +5015,20 @@ String VisualShaderNodeTextureUniformTriplanar::generate_global_per_node(Shader:
String code;
code += "// TRIPLANAR FUNCTION GLOBAL CODE\n";
- code += "\tvec4 triplanar_texture(sampler2D p_sampler, vec3 p_weights, vec3 p_triplanar_pos) {\n";
- code += "\t\tvec4 samp = vec4(0.0);\n";
- code += "\t\tsamp += texture(p_sampler, p_triplanar_pos.xy) * p_weights.z;\n";
- code += "\t\tsamp += texture(p_sampler, p_triplanar_pos.xz) * p_weights.y;\n";
- code += "\t\tsamp += texture(p_sampler, p_triplanar_pos.zy * vec2(-1.0, 1.0)) * p_weights.x;\n";
- code += "\t\treturn samp;\n";
- code += "\t}\n";
+ code += " vec4 triplanar_texture(sampler2D p_sampler, vec3 p_weights, vec3 p_triplanar_pos) {\n";
+ code += " vec4 samp = vec4(0.0);\n";
+ code += " samp += texture(p_sampler, p_triplanar_pos.xy) * p_weights.z;\n";
+ code += " samp += texture(p_sampler, p_triplanar_pos.xz) * p_weights.y;\n";
+ code += " samp += texture(p_sampler, p_triplanar_pos.zy * vec2(-1.0, 1.0)) * p_weights.x;\n";
+ code += " return samp;\n";
+ code += " }\n";
code += "\n";
- code += "\tuniform vec3 triplanar_scale = vec3(1.0, 1.0, 1.0);\n";
- code += "\tuniform vec3 triplanar_offset;\n";
- code += "\tuniform float triplanar_sharpness = 0.5;\n";
+ code += " uniform vec3 triplanar_scale = vec3(1.0, 1.0, 1.0);\n";
+ code += " uniform vec3 triplanar_offset;\n";
+ code += " uniform float triplanar_sharpness = 0.5;\n";
code += "\n";
- code += "\tvarying vec3 triplanar_power_normal;\n";
- code += "\tvarying vec3 triplanar_pos;\n";
+ code += " varying vec3 triplanar_power_normal;\n";
+ code += " varying vec3 triplanar_pos;\n";
return code;
}
@@ -4388,11 +5037,11 @@ String VisualShaderNodeTextureUniformTriplanar::generate_global_per_func(Shader:
String code;
if (p_type == VisualShader::TYPE_VERTEX) {
- code += "\t// TRIPLANAR FUNCTION VERTEX CODE\n";
- code += "\t\ttriplanar_power_normal = pow(abs(NORMAL), vec3(triplanar_sharpness));\n";
- code += "\t\ttriplanar_power_normal /= dot(triplanar_power_normal, vec3(1.0));\n";
- code += "\t\ttriplanar_pos = VERTEX * triplanar_scale + triplanar_offset;\n";
- code += "\t\ttriplanar_pos *= vec3(1.0, -1.0, 1.0);\n";
+ code += " // TRIPLANAR FUNCTION VERTEX CODE\n";
+ code += " triplanar_power_normal = pow(abs(NORMAL), vec3(triplanar_sharpness));\n";
+ code += " triplanar_power_normal /= dot(triplanar_power_normal, vec3(1.0));\n";
+ code += " triplanar_pos = VERTEX * triplanar_scale + triplanar_offset;\n";
+ code += " triplanar_pos *= vec3(1.0, -1.0, 1.0);\n";
}
return code;
@@ -4400,21 +5049,21 @@ String VisualShaderNodeTextureUniformTriplanar::generate_global_per_func(Shader:
String VisualShaderNodeTextureUniformTriplanar::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 {
String id = get_uniform_name();
- String code = "\t{\n";
+ String code = " {\n";
if (p_input_vars[0] == String() && p_input_vars[1] == String()) {
- code += "\t\tvec4 n_tex_read = triplanar_texture(" + id + ", triplanar_power_normal, triplanar_pos);\n";
+ code += " vec4 n_tex_read = triplanar_texture(" + id + ", triplanar_power_normal, triplanar_pos);\n";
} else if (p_input_vars[0] != String() && p_input_vars[1] == String()) {
- code += "\t\tvec4 n_tex_read = triplanar_texture(" + id + ", " + p_input_vars[0] + ", triplanar_pos);\n";
+ code += " vec4 n_tex_read = triplanar_texture(" + id + ", " + p_input_vars[0] + ", triplanar_pos);\n";
} else if (p_input_vars[0] == String() && p_input_vars[1] != String()) {
- code += "\t\tvec4 n_tex_read = triplanar_texture(" + id + ", triplanar_power_normal, " + p_input_vars[1] + ");\n";
+ code += " vec4 n_tex_read = triplanar_texture(" + id + ", triplanar_power_normal, " + p_input_vars[1] + ");\n";
} else {
- code += "\t\tvec4 n_tex_read = triplanar_texture(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
+ code += " vec4 n_tex_read = triplanar_texture(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
}
- code += "\t\t" + p_output_vars[0] + " = n_tex_read.rgb;\n";
- code += "\t\t" + p_output_vars[1] + " = n_tex_read.a;\n";
- code += "\t}\n";
+ code += " " + p_output_vars[0] + " = n_tex_read.rgb;\n";
+ code += " " + p_output_vars[1] + " = n_tex_read.a;\n";
+ code += " }\n";
return code;
}
@@ -4470,23 +5119,28 @@ String VisualShaderNodeTexture2DArrayUniform::generate_global(Shader::Mode p_mod
switch (texture_type) {
case TYPE_DATA:
- if (color_default == COLOR_DEFAULT_BLACK)
+ if (color_default == COLOR_DEFAULT_BLACK) {
code += " : hint_black;\n";
- else
+ } else {
code += ";\n";
+ }
break;
case TYPE_COLOR:
- if (color_default == COLOR_DEFAULT_BLACK)
+ if (color_default == COLOR_DEFAULT_BLACK) {
code += " : hint_black_albedo;\n";
- else
+ } else {
code += " : hint_albedo;\n";
+ }
break;
- case TYPE_NORMALMAP:
+ case TYPE_NORMAL_MAP:
code += " : hint_normal;\n";
break;
case TYPE_ANISO:
code += " : hint_aniso;\n";
break;
+ default:
+ code += ";\n";
+ break;
}
return code;
@@ -4538,23 +5192,28 @@ String VisualShaderNodeTexture3DUniform::generate_global(Shader::Mode p_mode, Vi
switch (texture_type) {
case TYPE_DATA:
- if (color_default == COLOR_DEFAULT_BLACK)
+ if (color_default == COLOR_DEFAULT_BLACK) {
code += " : hint_black;\n";
- else
+ } else {
code += ";\n";
+ }
break;
case TYPE_COLOR:
- if (color_default == COLOR_DEFAULT_BLACK)
+ if (color_default == COLOR_DEFAULT_BLACK) {
code += " : hint_black_albedo;\n";
- else
+ } else {
code += " : hint_albedo;\n";
+ }
break;
- case TYPE_NORMALMAP:
+ case TYPE_NORMAL_MAP:
code += " : hint_normal;\n";
break;
case TYPE_ANISO:
code += " : hint_aniso;\n";
break;
+ default:
+ code += ";\n";
+ break;
}
return code;
@@ -4619,12 +5278,15 @@ String VisualShaderNodeCubemapUniform::generate_global(Shader::Mode p_mode, Visu
code += " : hint_albedo;\n";
}
break;
- case TYPE_NORMALMAP:
+ case TYPE_NORMAL_MAP:
code += " : hint_normal;\n";
break;
case TYPE_ANISO:
code += " : hint_aniso;\n";
break;
+ default:
+ code += ";\n";
+ break;
}
return code;
@@ -4687,18 +5349,18 @@ String VisualShaderNodeIf::get_output_port_name(int p_port) const {
String VisualShaderNodeIf::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 {
String code;
- code += "\tif(abs(" + p_input_vars[0] + " - " + p_input_vars[1] + ") < " + p_input_vars[2] + ")\n"; // abs(a - b) < tolerance eg. a == b
- code += "\t{\n";
- code += "\t\t" + p_output_vars[0] + " = " + p_input_vars[3] + ";\n";
- code += "\t}\n";
- code += "\telse if(" + p_input_vars[0] + " < " + p_input_vars[1] + ")\n"; // a < b
- code += "\t{\n";
- code += "\t\t" + p_output_vars[0] + " = " + p_input_vars[5] + ";\n";
- code += "\t}\n";
- code += "\telse\n"; // a > b (or a >= b if abs(a - b) < tolerance is false)
- code += "\t{\n";
- code += "\t\t" + p_output_vars[0] + " = " + p_input_vars[4] + ";\n";
- code += "\t}\n";
+ code += " if(abs(" + p_input_vars[0] + " - " + p_input_vars[1] + ") < " + p_input_vars[2] + ")\n"; // abs(a - b) < tolerance eg. a == b
+ code += " {\n";
+ code += " " + p_output_vars[0] + " = " + p_input_vars[3] + ";\n";
+ code += " }\n";
+ code += " else if(" + p_input_vars[0] + " < " + p_input_vars[1] + ")\n"; // a < b
+ code += " {\n";
+ code += " " + p_output_vars[0] + " = " + p_input_vars[5] + ";\n";
+ code += " }\n";
+ code += " else\n"; // a > b (or a >= b if abs(a - b) < tolerance is false)
+ code += " {\n";
+ code += " " + p_output_vars[0] + " = " + p_input_vars[4] + ";\n";
+ code += " }\n";
return code;
}
@@ -4715,7 +5377,7 @@ VisualShaderNodeIf::VisualShaderNodeIf() {
////////////// Switch
String VisualShaderNodeSwitch::get_caption() const {
- return "VectorSwitch";
+ return "Switch";
}
int VisualShaderNodeSwitch::get_input_port_count() const {
@@ -4726,7 +5388,21 @@ VisualShaderNodeSwitch::PortType VisualShaderNodeSwitch::get_input_port_type(int
if (p_port == 0) {
return PORT_TYPE_BOOLEAN;
}
- return PORT_TYPE_VECTOR;
+ if (p_port == 1 || p_port == 2) {
+ switch (op_type) {
+ case OP_TYPE_INT:
+ return PORT_TYPE_SCALAR_INT;
+ case OP_TYPE_VECTOR:
+ return PORT_TYPE_VECTOR;
+ case OP_TYPE_BOOLEAN:
+ return PORT_TYPE_BOOLEAN;
+ case OP_TYPE_TRANSFORM:
+ return PORT_TYPE_TRANSFORM;
+ default:
+ break;
+ }
+ }
+ return PORT_TYPE_SCALAR;
}
String VisualShaderNodeSwitch::get_input_port_name(int p_port) const {
@@ -4747,51 +5423,97 @@ int VisualShaderNodeSwitch::get_output_port_count() const {
}
VisualShaderNodeSwitch::PortType VisualShaderNodeSwitch::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ switch (op_type) {
+ case OP_TYPE_INT:
+ return PORT_TYPE_SCALAR_INT;
+ case OP_TYPE_VECTOR:
+ return PORT_TYPE_VECTOR;
+ case OP_TYPE_BOOLEAN:
+ return PORT_TYPE_BOOLEAN;
+ case OP_TYPE_TRANSFORM:
+ return PORT_TYPE_TRANSFORM;
+ default:
+ break;
+ }
+ return PORT_TYPE_SCALAR;
}
String VisualShaderNodeSwitch::get_output_port_name(int p_port) const {
return "result";
}
-String VisualShaderNodeSwitch::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 {
- String code;
- code += "\tif(" + p_input_vars[0] + ")\n";
- code += "\t{\n";
- code += "\t\t" + p_output_vars[0] + " = " + p_input_vars[1] + ";\n";
- code += "\t}\n";
- code += "\telse\n";
- code += "\t{\n";
- code += "\t\t" + p_output_vars[0] + " = " + p_input_vars[2] + ";\n";
- code += "\t}\n";
- return code;
+void VisualShaderNodeSwitch::set_op_type(OpType p_op_type) {
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
+ if (op_type == p_op_type) {
+ return;
+ }
+ switch (p_op_type) {
+ case OP_TYPE_FLOAT:
+ set_input_port_default_value(1, 1.0);
+ set_input_port_default_value(2, 0.0);
+ break;
+ case OP_TYPE_INT:
+ set_input_port_default_value(1, 1);
+ set_input_port_default_value(2, 0);
+ break;
+ case OP_TYPE_VECTOR:
+ set_input_port_default_value(1, Vector3(1.0, 1.0, 1.0));
+ set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0));
+ break;
+ case OP_TYPE_BOOLEAN:
+ set_input_port_default_value(1, true);
+ set_input_port_default_value(2, false);
+ break;
+ case OP_TYPE_TRANSFORM:
+ set_input_port_default_value(1, Transform3D());
+ set_input_port_default_value(2, Transform3D());
+ break;
+ default:
+ break;
+ }
+ op_type = p_op_type;
+ emit_changed();
}
-VisualShaderNodeSwitch::VisualShaderNodeSwitch() {
- simple_decl = false;
- set_input_port_default_value(0, false);
- set_input_port_default_value(1, Vector3(1.0, 1.0, 1.0));
- set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0));
+VisualShaderNodeSwitch::OpType VisualShaderNodeSwitch::get_op_type() const {
+ return op_type;
}
-////////////// Switch(scalar)
-
-String VisualShaderNodeScalarSwitch::get_caption() const {
- return "ScalarSwitch";
+Vector<StringName> VisualShaderNodeSwitch::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("op_type");
+ return props;
}
-VisualShaderNodeScalarSwitch::PortType VisualShaderNodeScalarSwitch::get_input_port_type(int p_port) const {
- if (p_port == 0) {
- return PORT_TYPE_BOOLEAN;
- }
- return PORT_TYPE_SCALAR;
+void VisualShaderNodeSwitch::_bind_methods() { // static
+ ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeSwitch::set_op_type);
+ ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeSwitch::get_op_type);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Float,Int,Vector,Boolean,Transform"), "set_op_type", "get_op_type");
+
+ BIND_ENUM_CONSTANT(OP_TYPE_FLOAT);
+ BIND_ENUM_CONSTANT(OP_TYPE_INT);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR);
+ BIND_ENUM_CONSTANT(OP_TYPE_BOOLEAN);
+ BIND_ENUM_CONSTANT(OP_TYPE_TRANSFORM);
+ BIND_ENUM_CONSTANT(OP_TYPE_MAX);
}
-VisualShaderNodeScalarSwitch::PortType VisualShaderNodeScalarSwitch::get_output_port_type(int p_port) const {
- return PORT_TYPE_SCALAR;
+String VisualShaderNodeSwitch::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 {
+ String code;
+ code += " if(" + p_input_vars[0] + ")\n";
+ code += " {\n";
+ code += " " + p_output_vars[0] + " = " + p_input_vars[1] + ";\n";
+ code += " }\n";
+ code += " else\n";
+ code += " {\n";
+ code += " " + p_output_vars[0] + " = " + p_input_vars[2] + ";\n";
+ code += " }\n";
+ return code;
}
-VisualShaderNodeScalarSwitch::VisualShaderNodeScalarSwitch() {
+VisualShaderNodeSwitch::VisualShaderNodeSwitch() {
+ simple_decl = false;
set_input_port_default_value(0, false);
set_input_port_default_value(1, 1.0);
set_input_port_default_value(2, 0.0);
@@ -4871,12 +5593,12 @@ String VisualShaderNodeFresnel::generate_code(Shader::Mode p_mode, VisualShader:
}
if (is_input_port_connected(2)) {
- return "\t" + p_output_vars[0] + " = " + p_input_vars[2] + " ? (pow(clamp(dot(" + normal + ", " + view + "), 0.0, 1.0), " + p_input_vars[3] + ")) : (pow(1.0 - clamp(dot(" + normal + ", " + view + "), 0.0, 1.0), " + p_input_vars[3] + "));\n";
+ return " " + p_output_vars[0] + " = " + p_input_vars[2] + " ? (pow(clamp(dot(" + normal + ", " + view + "), 0.0, 1.0), " + p_input_vars[3] + ")) : (pow(1.0 - clamp(dot(" + normal + ", " + view + "), 0.0, 1.0), " + p_input_vars[3] + "));\n";
} else {
if (get_input_port_default_value(2)) {
- return "\t" + p_output_vars[0] + " = pow(clamp(dot(" + normal + ", " + view + "), 0.0, 1.0), " + p_input_vars[3] + ");\n";
+ return " " + p_output_vars[0] + " = pow(clamp(dot(" + normal + ", " + view + "), 0.0, 1.0), " + p_input_vars[3] + ");\n";
} else {
- return "\t" + p_output_vars[0] + " = pow(1.0 - clamp(dot(" + normal + ", " + view + "), 0.0, 1.0), " + p_input_vars[3] + ");\n";
+ return " " + p_output_vars[0] + " = pow(1.0 - clamp(dot(" + normal + ", " + view + "), 0.0, 1.0), " + p_input_vars[3] + ");\n";
}
}
}
@@ -4926,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 += "\t" + 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();
}
@@ -4959,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() {
@@ -4972,17 +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 {
- if (p_port == 2) {
- return PORT_TYPE_SCALAR;
- }
- switch (ctype) {
+ switch (comparison_type) {
case CTYPE_SCALAR:
return PORT_TYPE_SCALAR;
case CTYPE_SCALAR_INT:
@@ -4993,8 +5717,9 @@ VisualShaderNodeCompare::PortType VisualShaderNodeCompare::get_input_port_type(i
return PORT_TYPE_BOOLEAN;
case CTYPE_TRANSFORM:
return PORT_TYPE_TRANSFORM;
+ default:
+ return PORT_TYPE_SCALAR;
}
- return PORT_TYPE_VECTOR;
}
String VisualShaderNodeCompare::get_input_port_name(int p_port) const {
@@ -5024,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] = {
"==",
"!=",
">",
@@ -5043,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($)",
@@ -5052,46 +5776,46 @@ 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 += "\t" + p_output_vars[0] + " = (abs(" + p_input_vars[0] + " - " + p_input_vars[1] + ") < " + p_input_vars[2] + ");";
+ 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 += "\t" + p_output_vars[0] + " = !(abs(" + p_input_vars[0] + " - " + p_input_vars[1] + ") < " + p_input_vars[2] + ");";
+ code += " " + p_output_vars[0] + " = !(abs(" + p_input_vars[0] + " - " + p_input_vars[1] + ") < " + p_input_vars[2] + ");";
} else {
- code += "\t" + 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 += "\t" + 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 += "\t{\n";
- code += "\t\tbvec3 _bv = " + String(funcs[func]).replace("$", p_input_vars[0] + ", " + p_input_vars[1]) + ";\n";
- code += "\t\t" + p_output_vars[0] + " = " + String(conds[condition]).replace("$", "_bv") + ";\n";
- code += "\t}\n";
+ code += " {\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;
case CTYPE_BOOLEAN:
if (func > FUNC_NOT_EQUAL) {
- return "\t" + p_output_vars[0] + " = false;\n";
+ return " " + p_output_vars[0] + " = false;\n";
}
- code += "\t" + 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 "\t" + p_output_vars[0] + " = false;\n";
+ return " " + p_output_vars[0] + " = false;\n";
}
- code += "\t" + 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:
@@ -5100,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);
@@ -5125,19 +5851,26 @@ void VisualShaderNodeCompare::set_comparison_type(ComparisonType p_type) {
simple_decl = true;
break;
case CTYPE_TRANSFORM:
- set_input_port_default_value(0, Transform());
- set_input_port_default_value(1, Transform());
+ set_input_port_default_value(0, Transform3D());
+ 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();
}
@@ -5146,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();
}
@@ -5159,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;
@@ -5184,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);
@@ -5191,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() {
@@ -5213,10 +5953,10 @@ int VisualShaderNodeMultiplyAdd::get_input_port_count() const {
}
VisualShaderNodeMultiplyAdd::PortType VisualShaderNodeMultiplyAdd::get_input_port_type(int p_port) const {
- if (op_type == OP_TYPE_SCALAR) {
- return PORT_TYPE_SCALAR;
+ if (op_type == OP_TYPE_VECTOR) {
+ return PORT_TYPE_VECTOR;
}
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_SCALAR;
}
String VisualShaderNodeMultiplyAdd::get_input_port_name(int p_port) const {
@@ -5247,21 +5987,27 @@ String VisualShaderNodeMultiplyAdd::get_output_port_name(int p_port) const {
}
String VisualShaderNodeMultiplyAdd::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 {
- return "\t" + p_output_vars[0] + " = fma(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n";
+ return " " + p_output_vars[0] + " = fma(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n";
}
void VisualShaderNodeMultiplyAdd::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) {
+ ERR_FAIL_INDEX((int)p_op_type, int(OP_TYPE_MAX));
+ if (op_type == p_op_type) {
+ return;
+ }
+ switch (p_op_type) {
+ case OP_TYPE_SCALAR:
set_input_port_default_value(0, 0.0);
set_input_port_default_value(1, 0.0);
set_input_port_default_value(2, 0.0);
- } else {
+ break;
+ case OP_TYPE_VECTOR:
set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0));
set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0));
set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0));
- }
+ break;
+ default:
+ break;
}
op_type = p_op_type;
emit_changed();
@@ -5293,3 +6039,130 @@ VisualShaderNodeMultiplyAdd::VisualShaderNodeMultiplyAdd() {
set_input_port_default_value(1, 0.0);
set_input_port_default_value(2, 0.0);
}
+
+////////////// Billboard
+
+String VisualShaderNodeBillboard::get_caption() const {
+ return "GetBillboardMatrix";
+}
+
+int VisualShaderNodeBillboard::get_input_port_count() const {
+ return 0;
+}
+
+VisualShaderNodeBillboard::PortType VisualShaderNodeBillboard::get_input_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeBillboard::get_input_port_name(int p_port) const {
+ return "";
+}
+
+int VisualShaderNodeBillboard::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeBillboard::PortType VisualShaderNodeBillboard::get_output_port_type(int p_port) const {
+ return PORT_TYPE_TRANSFORM;
+}
+
+String VisualShaderNodeBillboard::get_output_port_name(int p_port) const {
+ return "model_view_matrix";
+}
+
+String VisualShaderNodeBillboard::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 {
+ String code;
+
+ switch (billboard_type) {
+ case BILLBOARD_TYPE_ENABLED:
+ code += " {\n";
+ code += " mat4 __mvm = INV_CAMERA_MATRIX * mat4(CAMERA_MATRIX[0], CAMERA_MATRIX[1], CAMERA_MATRIX[2], WORLD_MATRIX[3]);\n";
+ if (keep_scale) {
+ code += " __mvm = __mvm * mat4(vec4(length(WORLD_MATRIX[0].xyz), 0.0, 0.0, 0.0), vec4(0.0, length(WORLD_MATRIX[1].xyz), 0.0, 0.0), vec4(0.0, 0.0, length(WORLD_MATRIX[2].xyz), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
+ }
+ code += " " + p_output_vars[0] + " = __mvm;\n";
+ code += " }\n";
+ break;
+ case BILLBOARD_TYPE_FIXED_Y:
+ code += " {\n";
+ code += " mat4 __mvm = INV_CAMERA_MATRIX * mat4(CAMERA_MATRIX[0], WORLD_MATRIX[1], vec4(normalize(cross(CAMERA_MATRIX[0].xyz, WORLD_MATRIX[1].xyz)), 0.0), WORLD_MATRIX[3]);\n";
+ if (keep_scale) {
+ code += " __mvm = __mvm * mat4(vec4(length(WORLD_MATRIX[0].xyz), 0.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(0.0, 0.0, length(WORLD_MATRIX[2].xyz), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
+ } else {
+ code += " __mvm = __mvm * mat4(vec4(1.0, 0.0, 0.0, 0.0), vec4(0.0, 1.0 / length(WORLD_MATRIX[1].xyz), 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
+ }
+ code += " " + p_output_vars[0] + " = __mvm;\n";
+ code += " }\n";
+ break;
+ case BILLBOARD_TYPE_PARTICLES:
+ code += " {\n";
+ code += " mat4 __wm = mat4(normalize(CAMERA_MATRIX[0]) * length(WORLD_MATRIX[0]), normalize(CAMERA_MATRIX[1]) * length(WORLD_MATRIX[0]), normalize(CAMERA_MATRIX[2]) * length(WORLD_MATRIX[2]), WORLD_MATRIX[3]);\n";
+ code += " __wm = __wm * mat4(vec4(cos(INSTANCE_CUSTOM.x), -sin(INSTANCE_CUSTOM.x), 0.0, 0.0), vec4(sin(INSTANCE_CUSTOM.x), cos(INSTANCE_CUSTOM.x), 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
+ code += " " + p_output_vars[0] + " = INV_CAMERA_MATRIX * __wm;\n";
+ code += " }\n";
+ break;
+ default:
+ code += " " + p_output_vars[0] + " = mat4(1.0);\n";
+ break;
+ }
+
+ return code;
+}
+
+bool VisualShaderNodeBillboard::is_show_prop_names() const {
+ return true;
+}
+
+void VisualShaderNodeBillboard::set_billboard_type(BillboardType p_billboard_type) {
+ 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);
+ emit_changed();
+}
+
+VisualShaderNodeBillboard::BillboardType VisualShaderNodeBillboard::get_billboard_type() const {
+ return billboard_type;
+}
+
+void VisualShaderNodeBillboard::set_keep_scale_enabled(bool p_enabled) {
+ keep_scale = p_enabled;
+ emit_changed();
+}
+
+bool VisualShaderNodeBillboard::is_keep_scale_enabled() const {
+ return keep_scale;
+}
+
+Vector<StringName> VisualShaderNodeBillboard::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("billboard_type");
+ if (billboard_type == BILLBOARD_TYPE_ENABLED || billboard_type == BILLBOARD_TYPE_FIXED_Y) {
+ props.push_back("keep_scale");
+ }
+ return props;
+}
+
+void VisualShaderNodeBillboard::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_billboard_type", "billboard_type"), &VisualShaderNodeBillboard::set_billboard_type);
+ ClassDB::bind_method(D_METHOD("get_billboard_type"), &VisualShaderNodeBillboard::get_billboard_type);
+
+ ClassDB::bind_method(D_METHOD("set_keep_scale_enabled", "enabled"), &VisualShaderNodeBillboard::set_keep_scale_enabled);
+ ClassDB::bind_method(D_METHOD("is_keep_scale_enabled"), &VisualShaderNodeBillboard::is_keep_scale_enabled);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "billboard_type", PROPERTY_HINT_ENUM, "Disabled,Enabled,Y-Billboard,Particles"), "set_billboard_type", "get_billboard_type");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_scale"), "set_keep_scale_enabled", "is_keep_scale_enabled");
+
+ BIND_ENUM_CONSTANT(BILLBOARD_TYPE_DISABLED);
+ BIND_ENUM_CONSTANT(BILLBOARD_TYPE_ENABLED);
+ BIND_ENUM_CONSTANT(BILLBOARD_TYPE_FIXED_Y);
+ BIND_ENUM_CONSTANT(BILLBOARD_TYPE_PARTICLES);
+ BIND_ENUM_CONSTANT(BILLBOARD_TYPE_MAX);
+}
+
+VisualShaderNodeBillboard::VisualShaderNodeBillboard() {
+ simple_decl = false;
+}
diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h
index 95042f8e56..2c952300fe 100644
--- a/scene/resources/visual_shader_nodes.h
+++ b/scene/resources/visual_shader_nodes.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -37,8 +37,27 @@
/// CONSTANTS
///////////////////////////////////////
-class VisualShaderNodeFloatConstant : public VisualShaderNode {
- GDCLASS(VisualShaderNodeFloatConstant, VisualShaderNode);
+class VisualShaderNodeConstant : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeConstant, VisualShaderNode);
+
+public:
+ virtual String get_caption() const override = 0;
+
+ virtual int get_input_port_count() const override = 0;
+ virtual PortType get_input_port_type(int p_port) const override = 0;
+ virtual String get_input_port_name(int p_port) const override = 0;
+
+ virtual int get_output_port_count() const override = 0;
+ virtual PortType get_output_port_type(int p_port) const override = 0;
+ virtual String get_output_port_name(int p_port) const override = 0;
+
+ 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 = 0;
+
+ VisualShaderNodeConstant();
+};
+
+class VisualShaderNodeFloatConstant : public VisualShaderNodeConstant {
+ GDCLASS(VisualShaderNodeFloatConstant, VisualShaderNodeConstant);
float constant = 0.0f;
protected:
@@ -55,9 +74,9 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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;
@@ -67,8 +86,8 @@ public:
///////////////////////////////////////
-class VisualShaderNodeIntConstant : public VisualShaderNode {
- GDCLASS(VisualShaderNodeIntConstant, VisualShaderNode);
+class VisualShaderNodeIntConstant : public VisualShaderNodeConstant {
+ GDCLASS(VisualShaderNodeIntConstant, VisualShaderNodeConstant);
int constant = 0;
protected:
@@ -85,9 +104,9 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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;
@@ -97,8 +116,8 @@ public:
///////////////////////////////////////
-class VisualShaderNodeBooleanConstant : public VisualShaderNode {
- GDCLASS(VisualShaderNodeBooleanConstant, VisualShaderNode);
+class VisualShaderNodeBooleanConstant : public VisualShaderNodeConstant {
+ GDCLASS(VisualShaderNodeBooleanConstant, VisualShaderNodeConstant);
bool constant = false;
protected:
@@ -115,9 +134,9 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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;
@@ -127,8 +146,8 @@ public:
///////////////////////////////////////
-class VisualShaderNodeColorConstant : public VisualShaderNode {
- GDCLASS(VisualShaderNodeColorConstant, VisualShaderNode);
+class VisualShaderNodeColorConstant : public VisualShaderNodeConstant {
+ GDCLASS(VisualShaderNodeColorConstant, VisualShaderNodeConstant);
Color constant = Color(1, 1, 1, 1);
protected:
@@ -144,10 +163,11 @@ public:
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual bool is_output_port_expandable(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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;
@@ -157,8 +177,8 @@ public:
///////////////////////////////////////
-class VisualShaderNodeVec3Constant : public VisualShaderNode {
- GDCLASS(VisualShaderNodeVec3Constant, VisualShaderNode);
+class VisualShaderNodeVec3Constant : public VisualShaderNodeConstant {
+ GDCLASS(VisualShaderNodeVec3Constant, VisualShaderNodeConstant);
Vector3 constant;
protected:
@@ -175,9 +195,9 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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;
@@ -187,9 +207,9 @@ public:
///////////////////////////////////////
-class VisualShaderNodeTransformConstant : public VisualShaderNode {
- GDCLASS(VisualShaderNodeTransformConstant, VisualShaderNode);
- Transform constant;
+class VisualShaderNodeTransformConstant : public VisualShaderNodeConstant {
+ GDCLASS(VisualShaderNodeTransformConstant, VisualShaderNodeConstant);
+ Transform3D constant;
protected:
static void _bind_methods();
@@ -205,10 +225,10 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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(Transform p_value);
- Transform get_constant() const;
+ void set_constant(const Transform3D &p_constant);
+ Transform3D get_constant() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -231,12 +251,14 @@ public:
SOURCE_2D_NORMAL,
SOURCE_DEPTH,
SOURCE_PORT,
+ SOURCE_MAX,
};
enum TextureType {
TYPE_DATA,
TYPE_COLOR,
- TYPE_NORMALMAP,
+ TYPE_NORMAL_MAP,
+ TYPE_MAX,
};
private:
@@ -256,20 +278,21 @@ public:
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual bool is_output_port_expandable(int p_port) const override;
virtual String get_input_port_default_hint(int p_port) const override;
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;
- 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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_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;
@@ -284,6 +307,72 @@ VARIANT_ENUM_CAST(VisualShaderNodeTexture::Source)
///////////////////////////////////////
+class VisualShaderNodeCurveTexture : public VisualShaderNodeResizableBase {
+ GDCLASS(VisualShaderNodeCurveTexture, VisualShaderNodeResizableBase);
+ Ref<CurveTexture> texture;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+
+ virtual int get_output_port_count() const override;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) const override;
+
+ 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;
+ 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_texture);
+ Ref<CurveTexture> get_texture() const;
+
+ virtual Vector<StringName> get_editable_properties() const override;
+ virtual bool is_use_prop_slots() const override;
+
+ VisualShaderNodeCurveTexture();
+};
+
+///////////////////////////////////////
+
+class VisualShaderNodeCurveXYZTexture : public VisualShaderNodeResizableBase {
+ GDCLASS(VisualShaderNodeCurveXYZTexture, VisualShaderNodeResizableBase);
+ Ref<CurveXYZTexture> texture;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+
+ virtual int get_output_port_count() const override;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) const override;
+
+ 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;
+ 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_texture);
+ Ref<CurveXYZTexture> get_texture() const;
+
+ virtual Vector<StringName> get_editable_properties() const override;
+ virtual bool is_use_prop_slots() const override;
+
+ VisualShaderNodeCurveXYZTexture();
+};
+
+///////////////////////////////////////
+
class VisualShaderNodeSample3D : public VisualShaderNode {
GDCLASS(VisualShaderNodeSample3D, VisualShaderNode);
@@ -291,6 +380,7 @@ public:
enum Source {
SOURCE_TEXTURE,
SOURCE_PORT,
+ SOURCE_MAX,
};
protected:
@@ -307,8 +397,9 @@ public:
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual bool is_output_port_expandable(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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_source(Source p_source);
Source get_source() const;
@@ -322,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();
@@ -335,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;
@@ -358,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;
@@ -373,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_NORMALMAP
+ TYPE_NORMAL_MAP,
+ TYPE_MAX,
};
private:
@@ -400,21 +493,23 @@ public:
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual bool is_output_port_expandable(int p_port) const override;
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;
- 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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_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;
+ virtual String get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const override;
VisualShaderNodeCubemap();
};
@@ -440,7 +535,8 @@ public:
OP_MAX,
OP_MIN,
OP_ATAN2,
- OP_STEP
+ OP_STEP,
+ OP_ENUM_SIZE,
};
protected:
@@ -459,7 +555,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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_operator(Operator p_op);
Operator get_operator() const;
@@ -483,6 +579,7 @@ public:
OP_MOD,
OP_MAX,
OP_MIN,
+ OP_ENUM_SIZE,
};
protected:
@@ -501,7 +598,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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_operator(Operator p_op);
Operator get_operator() const;
@@ -529,7 +626,8 @@ public:
OP_CROSS,
OP_ATAN2,
OP_REFLECT,
- OP_STEP
+ OP_STEP,
+ OP_ENUM_SIZE,
};
protected:
@@ -548,7 +646,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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_operator(Operator p_op);
Operator get_operator() const;
@@ -575,7 +673,8 @@ public:
OP_DODGE,
OP_BURN,
OP_SOFT_LIGHT,
- OP_HARD_LIGHT
+ OP_HARD_LIGHT,
+ OP_MAX,
};
protected:
@@ -594,7 +693,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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_operator(Operator p_op);
Operator get_operator() const;
@@ -606,19 +705,25 @@ public:
VARIANT_ENUM_CAST(VisualShaderNodeColorOp::Operator)
-///////////////////////////////////////
-/// TRANSFORM-TRANSFORM MULTIPLICATION
-///////////////////////////////////////
+////////////////////////////////
+/// TRANSFORM-TRANSFORM OPERATOR
+////////////////////////////////
-class VisualShaderNodeTransformMult : public VisualShaderNode {
- GDCLASS(VisualShaderNodeTransformMult, VisualShaderNode);
+class VisualShaderNodeTransformOp : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeTransformOp, VisualShaderNode);
public:
enum Operator {
OP_AxB,
OP_BxA,
OP_AxB_COMP,
- OP_BxA_COMP
+ OP_BxA_COMP,
+ OP_ADD,
+ OP_A_MINUS_B,
+ OP_B_MINUS_A,
+ OP_A_DIV_B,
+ OP_B_DIV_A,
+ OP_MAX,
};
protected:
@@ -637,17 +742,17 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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_operator(Operator p_op);
Operator get_operator() const;
virtual Vector<StringName> get_editable_properties() const override;
- VisualShaderNodeTransformMult();
+ VisualShaderNodeTransformOp();
};
-VARIANT_ENUM_CAST(VisualShaderNodeTransformMult::Operator)
+VARIANT_ENUM_CAST(VisualShaderNodeTransformOp::Operator)
///////////////////////////////////////
/// TRANSFORM-VECTOR MULTIPLICATION
@@ -662,6 +767,7 @@ public:
OP_BxA,
OP_3x3_AxB,
OP_3x3_BxA,
+ OP_MAX,
};
protected:
@@ -680,7 +786,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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_operator(Operator p_op);
Operator get_operator() const;
@@ -732,7 +838,8 @@ public:
FUNC_RECIPROCAL,
FUNC_ROUNDEVEN,
FUNC_TRUNC,
- FUNC_ONEMINUS
+ FUNC_ONEMINUS,
+ FUNC_MAX,
};
protected:
@@ -751,7 +858,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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_func);
Function get_function() const;
@@ -773,9 +880,9 @@ class VisualShaderNodeIntFunc : public VisualShaderNode {
public:
enum Function {
FUNC_ABS,
- FUNC_CLAMP,
FUNC_NEGATE,
FUNC_SIGN,
+ FUNC_MAX,
};
protected:
@@ -794,7 +901,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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_func);
Function get_function() const;
@@ -849,7 +956,8 @@ public:
FUNC_TAN,
FUNC_TANH,
FUNC_TRUNC,
- FUNC_ONEMINUS
+ FUNC_ONEMINUS,
+ FUNC_MAX,
};
protected:
@@ -868,7 +976,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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_func);
Function get_function() const;
@@ -890,7 +998,8 @@ class VisualShaderNodeColorFunc : public VisualShaderNode {
public:
enum Function {
FUNC_GRAYSCALE,
- FUNC_SEPIA
+ FUNC_SEPIA,
+ FUNC_MAX,
};
protected:
@@ -909,7 +1018,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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_func);
Function get_function() const;
@@ -931,7 +1040,8 @@ class VisualShaderNodeTransformFunc : public VisualShaderNode {
public:
enum Function {
FUNC_INVERSE,
- FUNC_TRANSPOSE
+ FUNC_TRANSPOSE,
+ FUNC_MAX,
};
protected:
@@ -950,7 +1060,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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_func);
Function get_function() const;
@@ -963,11 +1073,23 @@ public:
VARIANT_ENUM_CAST(VisualShaderNodeTransformFunc::Function)
///////////////////////////////////////
-/// DOT
+/// UV FUNC
///////////////////////////////////////
-class VisualShaderNodeDotProduct : public VisualShaderNode {
- GDCLASS(VisualShaderNodeDotProduct, VisualShaderNode);
+class VisualShaderNodeUVFunc : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeUVFunc, VisualShaderNode);
+
+public:
+ enum Function {
+ FUNC_PANNING,
+ FUNC_SCALING,
+ FUNC_MAX,
+ };
+
+protected:
+ Function func = FUNC_PANNING;
+
+ static void _bind_methods();
public:
virtual String get_caption() const override;
@@ -975,22 +1097,32 @@ public:
virtual int get_input_port_count() const override;
virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
+ virtual String get_input_port_default_hint(int p_port) const override;
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ virtual bool is_show_prop_names() const override;
- VisualShaderNodeDotProduct();
+ 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_func);
+ Function get_function() const;
+
+ virtual Vector<StringName> get_editable_properties() const override;
+
+ VisualShaderNodeUVFunc();
};
+VARIANT_ENUM_CAST(VisualShaderNodeUVFunc::Function)
+
///////////////////////////////////////
-/// LENGTH
+/// DOT
///////////////////////////////////////
-class VisualShaderNodeVectorLen : public VisualShaderNode {
- GDCLASS(VisualShaderNodeVectorLen, VisualShaderNode);
+class VisualShaderNodeDotProduct : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeDotProduct, VisualShaderNode);
public:
virtual String get_caption() const override;
@@ -1003,17 +1135,17 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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;
- VisualShaderNodeVectorLen();
+ VisualShaderNodeDotProduct();
};
///////////////////////////////////////
-/// DETERMINANT
+/// LENGTH
///////////////////////////////////////
-class VisualShaderNodeDeterminant : public VisualShaderNode {
- GDCLASS(VisualShaderNodeDeterminant, VisualShaderNode);
+class VisualShaderNodeVectorLen : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeVectorLen, VisualShaderNode);
public:
virtual String get_caption() const override;
@@ -1026,17 +1158,17 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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;
- VisualShaderNodeDeterminant();
+ VisualShaderNodeVectorLen();
};
///////////////////////////////////////
-/// CLAMP
+/// DETERMINANT
///////////////////////////////////////
-class VisualShaderNodeScalarClamp : public VisualShaderNode {
- GDCLASS(VisualShaderNodeScalarClamp, VisualShaderNode);
+class VisualShaderNodeDeterminant : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeDeterminant, VisualShaderNode);
public:
virtual String get_caption() const override;
@@ -1049,15 +1181,29 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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;
- VisualShaderNodeScalarClamp();
+ VisualShaderNodeDeterminant();
};
///////////////////////////////////////
+/// CLAMP
+///////////////////////////////////////
-class VisualShaderNodeVectorClamp : public VisualShaderNode {
- GDCLASS(VisualShaderNodeVectorClamp, VisualShaderNode);
+class VisualShaderNodeClamp : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeClamp, VisualShaderNode);
+
+public:
+ enum OpType {
+ OP_TYPE_FLOAT,
+ OP_TYPE_INT,
+ OP_TYPE_VECTOR,
+ OP_TYPE_MAX,
+ };
+
+protected:
+ OpType op_type = OP_TYPE_FLOAT;
+ static void _bind_methods();
public:
virtual String get_caption() const override;
@@ -1070,11 +1216,18 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ void set_op_type(OpType p_op_type);
+ OpType get_op_type() const;
+
+ virtual Vector<StringName> get_editable_properties() 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;
- VisualShaderNodeVectorClamp();
+ VisualShaderNodeClamp();
};
+VARIANT_ENUM_CAST(VisualShaderNodeClamp::OpType)
+
///////////////////////////////////////
/// DERIVATIVE FUNCTIONS
///////////////////////////////////////
@@ -1086,7 +1239,8 @@ public:
enum Function {
FUNC_SUM,
FUNC_X,
- FUNC_Y
+ FUNC_Y,
+ FUNC_MAX,
};
protected:
@@ -1105,7 +1259,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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_func);
Function get_function() const;
@@ -1126,7 +1280,8 @@ public:
enum Function {
FUNC_SUM,
FUNC_X,
- FUNC_Y
+ FUNC_Y,
+ FUNC_MAX,
};
protected:
@@ -1145,7 +1300,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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_func);
Function get_function() const;
@@ -1175,7 +1330,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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;
VisualShaderNodeFaceForward();
};
@@ -1198,7 +1353,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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;
VisualShaderNodeOuterProduct();
};
@@ -1207,8 +1362,20 @@ public:
/// STEP
///////////////////////////////////////
-class VisualShaderNodeVectorScalarStep : public VisualShaderNode {
- GDCLASS(VisualShaderNodeVectorScalarStep, VisualShaderNode);
+class VisualShaderNodeStep : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeStep, VisualShaderNode);
+
+public:
+ enum OpType {
+ OP_TYPE_SCALAR,
+ OP_TYPE_VECTOR,
+ OP_TYPE_VECTOR_SCALAR,
+ OP_TYPE_MAX,
+ };
+
+protected:
+ OpType op_type = OP_TYPE_SCALAR;
+ static void _bind_methods();
public:
virtual String get_caption() const override;
@@ -1221,38 +1388,36 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ void set_op_type(OpType p_op_type);
+ OpType get_op_type() const;
- VisualShaderNodeVectorScalarStep();
+ virtual Vector<StringName> get_editable_properties() 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;
+
+ VisualShaderNodeStep();
};
+VARIANT_ENUM_CAST(VisualShaderNodeStep::OpType)
+
///////////////////////////////////////
/// SMOOTHSTEP
///////////////////////////////////////
-class VisualShaderNodeScalarSmoothStep : public VisualShaderNode {
- GDCLASS(VisualShaderNodeScalarSmoothStep, VisualShaderNode);
+class VisualShaderNodeSmoothStep : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeSmoothStep, VisualShaderNode);
public:
- virtual String get_caption() const override;
-
- virtual int get_input_port_count() const override;
- virtual PortType get_input_port_type(int p_port) const override;
- virtual String get_input_port_name(int p_port) const override;
-
- virtual int get_output_port_count() const override;
- virtual PortType get_output_port_type(int p_port) const override;
- virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
-
- VisualShaderNodeScalarSmoothStep();
-};
-
-///////////////////////////////////////
+ enum OpType {
+ OP_TYPE_SCALAR,
+ OP_TYPE_VECTOR,
+ OP_TYPE_VECTOR_SCALAR,
+ OP_TYPE_MAX,
+ };
-class VisualShaderNodeVectorSmoothStep : public VisualShaderNode {
- GDCLASS(VisualShaderNodeVectorSmoothStep, VisualShaderNode);
+protected:
+ OpType op_type = OP_TYPE_SCALAR;
+ static void _bind_methods();
public:
virtual String get_caption() const override;
@@ -1265,32 +1430,18 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
-
- VisualShaderNodeVectorSmoothStep();
-};
-
-///////////////////////////////////////
-
-class VisualShaderNodeVectorScalarSmoothStep : public VisualShaderNode {
- GDCLASS(VisualShaderNodeVectorScalarSmoothStep, VisualShaderNode);
-
-public:
- virtual String get_caption() const override;
-
- virtual int get_input_port_count() const override;
- virtual PortType get_input_port_type(int p_port) const override;
- virtual String get_input_port_name(int p_port) const override;
+ void set_op_type(OpType p_op_type);
+ OpType get_op_type() const;
- virtual int get_output_port_count() const override;
- virtual PortType get_output_port_type(int p_port) const override;
- virtual String get_output_port_name(int p_port) const override;
+ virtual Vector<StringName> get_editable_properties() 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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;
- VisualShaderNodeVectorScalarSmoothStep();
+ VisualShaderNodeSmoothStep();
};
+VARIANT_ENUM_CAST(VisualShaderNodeSmoothStep::OpType)
+
///////////////////////////////////////
/// DISTANCE
///////////////////////////////////////
@@ -1309,7 +1460,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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;
VisualShaderNodeVectorDistance();
};
@@ -1332,7 +1483,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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;
VisualShaderNodeVectorRefract();
};
@@ -1341,29 +1492,20 @@ public:
/// MIX
///////////////////////////////////////
-class VisualShaderNodeScalarInterp : public VisualShaderNode {
- GDCLASS(VisualShaderNodeScalarInterp, VisualShaderNode);
+class VisualShaderNodeMix : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeMix, VisualShaderNode);
public:
- virtual String get_caption() const override;
-
- virtual int get_input_port_count() const override;
- virtual PortType get_input_port_type(int p_port) const override;
- virtual String get_input_port_name(int p_port) const override;
-
- virtual int get_output_port_count() const override;
- virtual PortType get_output_port_type(int p_port) const override;
- virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
-
- VisualShaderNodeScalarInterp();
-};
-
-///////////////////////////////////////
+ enum OpType {
+ OP_TYPE_SCALAR,
+ OP_TYPE_VECTOR,
+ OP_TYPE_VECTOR_SCALAR,
+ OP_TYPE_MAX,
+ };
-class VisualShaderNodeVectorInterp : public VisualShaderNode {
- GDCLASS(VisualShaderNodeVectorInterp, VisualShaderNode);
+protected:
+ OpType op_type = OP_TYPE_SCALAR;
+ static void _bind_methods();
public:
virtual String get_caption() const override;
@@ -1376,32 +1518,18 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
-
- VisualShaderNodeVectorInterp();
-};
-
-///////////////////////////////////////
-
-class VisualShaderNodeVectorScalarMix : public VisualShaderNode {
- GDCLASS(VisualShaderNodeVectorScalarMix, VisualShaderNode);
-
-public:
- virtual String get_caption() const override;
-
- virtual int get_input_port_count() const override;
- virtual PortType get_input_port_type(int p_port) const override;
- virtual String get_input_port_name(int p_port) const override;
+ void set_op_type(OpType p_op_type);
+ OpType get_op_type() const;
- virtual int get_output_port_count() const override;
- virtual PortType get_output_port_type(int p_port) const override;
- virtual String get_output_port_name(int p_port) const override;
+ virtual Vector<StringName> get_editable_properties() 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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;
- VisualShaderNodeVectorScalarMix();
+ VisualShaderNodeMix();
};
+VARIANT_ENUM_CAST(VisualShaderNodeMix::OpType)
+
///////////////////////////////////////
/// COMPOSE
///////////////////////////////////////
@@ -1420,7 +1548,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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;
VisualShaderNodeVectorCompose();
};
@@ -1441,7 +1569,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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;
VisualShaderNodeTransformCompose();
};
@@ -1464,7 +1592,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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;
VisualShaderNodeVectorDecompose();
};
@@ -1485,7 +1613,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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;
VisualShaderNodeTransformDecompose();
};
@@ -1502,6 +1630,7 @@ public:
HINT_NONE,
HINT_RANGE,
HINT_RANGE_STEP,
+ HINT_MAX,
};
private:
@@ -1527,7 +1656,7 @@ public:
virtual String get_output_port_name(int p_port) const override;
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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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;
virtual bool is_show_prop_names() const override;
virtual bool is_use_prop_slots() const override;
@@ -1551,6 +1680,7 @@ public:
float get_default_value() const;
bool is_qualifier_supported(Qualifier p_qual) const override;
+ bool is_convertible_to_constant() const override;
virtual Vector<StringName> get_editable_properties() const override;
@@ -1567,6 +1697,7 @@ public:
HINT_NONE,
HINT_RANGE,
HINT_RANGE_STEP,
+ HINT_MAX,
};
private:
@@ -1592,7 +1723,7 @@ public:
virtual String get_output_port_name(int p_port) const override;
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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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;
virtual bool is_show_prop_names() const override;
virtual bool is_use_prop_slots() const override;
@@ -1616,6 +1747,7 @@ public:
int get_default_value() const;
bool is_qualifier_supported(Qualifier p_qual) const override;
+ bool is_convertible_to_constant() const override;
virtual Vector<StringName> get_editable_properties() const override;
@@ -1648,7 +1780,7 @@ public:
virtual String get_output_port_name(int p_port) const override;
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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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;
virtual bool is_show_prop_names() const override;
virtual bool is_use_prop_slots() const override;
@@ -1660,6 +1792,7 @@ public:
bool get_default_value() const;
bool is_qualifier_supported(Qualifier p_qual) const override;
+ bool is_convertible_to_constant() const override;
virtual Vector<StringName> get_editable_properties() const override;
@@ -1690,7 +1823,7 @@ public:
virtual String get_output_port_name(int p_port) const override;
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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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;
virtual bool is_show_prop_names() const override;
@@ -1701,6 +1834,7 @@ public:
Color get_default_value() const;
bool is_qualifier_supported(Qualifier p_qual) const override;
+ bool is_convertible_to_constant() const override;
virtual Vector<StringName> get_editable_properties() const override;
@@ -1731,7 +1865,7 @@ public:
virtual String get_output_port_name(int p_port) const override;
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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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;
virtual bool is_show_prop_names() const override;
virtual bool is_use_prop_slots() const override;
@@ -1743,6 +1877,7 @@ public:
Vector3 get_default_value() const;
bool is_qualifier_supported(Qualifier p_qual) const override;
+ bool is_convertible_to_constant() const override;
virtual Vector<StringName> get_editable_properties() const override;
@@ -1756,7 +1891,7 @@ class VisualShaderNodeTransformUniform : public VisualShaderNodeUniform {
private:
bool default_value_enabled = false;
- Transform default_value = Transform(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0);
+ Transform3D default_value = Transform3D(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0);
protected:
static void _bind_methods();
@@ -1773,7 +1908,7 @@ public:
virtual String get_output_port_name(int p_port) const override;
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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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;
virtual bool is_show_prop_names() const override;
virtual bool is_use_prop_slots() const override;
@@ -1781,10 +1916,11 @@ public:
void set_default_value_enabled(bool p_enabled);
bool is_default_value_enabled() const;
- void set_default_value(const Transform &p_value);
- Transform get_default_value() const;
+ void set_default_value(const Transform3D &p_value);
+ Transform3D get_default_value() const;
bool is_qualifier_supported(Qualifier p_qual) const override;
+ bool is_convertible_to_constant() const override;
virtual Vector<StringName> get_editable_properties() const override;
@@ -1800,13 +1936,15 @@ public:
enum TextureType {
TYPE_DATA,
TYPE_COLOR,
- TYPE_NORMALMAP,
+ TYPE_NORMAL_MAP,
TYPE_ANISO,
+ TYPE_MAX,
};
enum ColorDefault {
COLOR_DEFAULT_WHITE,
- COLOR_DEFAULT_BLACK
+ COLOR_DEFAULT_BLACK,
+ COLOR_DEFAULT_MAX,
};
protected:
@@ -1829,7 +1967,7 @@ public:
virtual String get_output_port_name(int p_port) const override;
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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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;
virtual bool is_code_generated() const override;
@@ -1842,6 +1980,7 @@ public:
ColorDefault get_color_default() const;
bool is_qualifier_supported(Qualifier p_qual) const override;
+ bool is_convertible_to_constant() const override;
VisualShaderNodeTextureUniform();
};
@@ -1865,7 +2004,7 @@ public:
virtual String generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
virtual String generate_global_per_func(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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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;
VisualShaderNodeTextureUniformTriplanar();
};
@@ -1888,7 +2027,7 @@ public:
virtual String get_input_port_default_hint(int p_port) const override;
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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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;
VisualShaderNodeTexture2DArrayUniform();
};
@@ -1911,7 +2050,7 @@ public:
virtual String get_input_port_default_hint(int p_port) const override;
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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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;
VisualShaderNodeTexture3DUniform();
};
@@ -1934,7 +2073,7 @@ public:
virtual String get_input_port_default_hint(int p_port) const override;
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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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;
VisualShaderNodeCubemapUniform();
};
@@ -1970,6 +2109,21 @@ class VisualShaderNodeSwitch : public VisualShaderNode {
GDCLASS(VisualShaderNodeSwitch, VisualShaderNode);
public:
+ enum OpType {
+ OP_TYPE_FLOAT,
+ OP_TYPE_INT,
+ OP_TYPE_VECTOR,
+ OP_TYPE_BOOLEAN,
+ OP_TYPE_TRANSFORM,
+ OP_TYPE_MAX,
+ };
+
+protected:
+ OpType op_type = OP_TYPE_FLOAT;
+
+ static void _bind_methods();
+
+public:
virtual String get_caption() const override;
virtual int get_input_port_count() const override;
@@ -1980,22 +2134,17 @@ 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_op_type);
+ OpType get_op_type() const;
+
+ virtual Vector<StringName> get_editable_properties() 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;
VisualShaderNodeSwitch();
};
-class VisualShaderNodeScalarSwitch : public VisualShaderNodeSwitch {
- GDCLASS(VisualShaderNodeScalarSwitch, VisualShaderNodeSwitch);
-
-public:
- virtual String get_caption() const override;
-
- virtual PortType get_input_port_type(int p_port) const override;
- virtual PortType get_output_port_type(int p_port) const override;
-
- VisualShaderNodeScalarSwitch();
-};
+VARIANT_ENUM_CAST(VisualShaderNodeSwitch::OpType)
///////////////////////////////////////
/// FRESNEL
@@ -2033,6 +2182,7 @@ public:
enum Function {
FUNC_IS_INF,
FUNC_IS_NAN,
+ FUNC_MAX,
};
protected:
@@ -2052,7 +2202,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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_func);
Function get_function() const;
@@ -2078,6 +2228,7 @@ public:
CTYPE_VECTOR,
CTYPE_BOOLEAN,
CTYPE_TRANSFORM,
+ CTYPE_MAX,
};
enum Function {
@@ -2087,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;
@@ -2113,7 +2266,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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_comparison_type(ComparisonType p_type);
ComparisonType get_comparison_type() const;
@@ -2161,9 +2314,9 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) 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; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ 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;
@@ -2173,4 +2326,51 @@ public:
VARIANT_ENUM_CAST(VisualShaderNodeMultiplyAdd::OpType)
+class VisualShaderNodeBillboard : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeBillboard, VisualShaderNode);
+
+public:
+ enum BillboardType {
+ BILLBOARD_TYPE_DISABLED,
+ BILLBOARD_TYPE_ENABLED,
+ BILLBOARD_TYPE_FIXED_Y,
+ BILLBOARD_TYPE_PARTICLES,
+ BILLBOARD_TYPE_MAX,
+ };
+
+protected:
+ BillboardType billboard_type = BILLBOARD_TYPE_ENABLED;
+ bool keep_scale = false;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+
+ virtual int get_output_port_count() const override;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) 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;
+
+ virtual bool is_show_prop_names() const override;
+
+ void set_billboard_type(BillboardType p_billboard_type);
+ BillboardType get_billboard_type() const;
+
+ void set_keep_scale_enabled(bool p_enabled);
+ bool is_keep_scale_enabled() const;
+
+ virtual Vector<StringName> get_editable_properties() const override;
+
+ VisualShaderNodeBillboard();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeBillboard::BillboardType)
+
#endif // VISUAL_SHADER_NODES_H
diff --git a/scene/resources/visual_shader_particle_nodes.cpp b/scene/resources/visual_shader_particle_nodes.cpp
new file mode 100644
index 0000000000..5fe801e037
--- /dev/null
+++ b/scene/resources/visual_shader_particle_nodes.cpp
@@ -0,0 +1,1028 @@
+/*************************************************************************/
+/* visual_shader_particle_nodes.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 "visual_shader_particle_nodes.h"
+
+// VisualShaderNodeParticleEmitter
+
+int VisualShaderNodeParticleEmitter::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeParticleEmitter::PortType VisualShaderNodeParticleEmitter::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeParticleEmitter::get_output_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "position";
+ }
+ return String();
+}
+
+VisualShaderNodeParticleEmitter::VisualShaderNodeParticleEmitter() {
+}
+
+// VisualShaderNodeParticleSphereEmitter
+
+String VisualShaderNodeParticleSphereEmitter::get_caption() const {
+ return "SphereEmitter";
+}
+
+int VisualShaderNodeParticleSphereEmitter::get_input_port_count() const {
+ return 2;
+}
+
+VisualShaderNodeParticleSphereEmitter::PortType VisualShaderNodeParticleSphereEmitter::get_input_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeParticleSphereEmitter::get_input_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "radius";
+ } else if (p_port == 1) {
+ return "inner_radius";
+ }
+ return String();
+}
+
+String VisualShaderNodeParticleSphereEmitter::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ String code;
+ code += "vec3 __get_random_point_in_sphere(inout uint seed, float radius, float inner_radius) {\n";
+ code += " return __get_random_unit_vec3(seed) * __randf_range(seed, inner_radius, radius);\n";
+ code += "}\n\n";
+ return code;
+}
+
+String VisualShaderNodeParticleSphereEmitter::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 {
+ String code;
+ code += " " + p_output_vars[0] + " = __get_random_point_in_sphere(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ");\n";
+ return code;
+}
+
+VisualShaderNodeParticleSphereEmitter::VisualShaderNodeParticleSphereEmitter() {
+ set_input_port_default_value(0, 10.0);
+ set_input_port_default_value(1, 0.0);
+}
+
+// VisualShaderNodeParticleBoxEmitter
+
+String VisualShaderNodeParticleBoxEmitter::get_caption() const {
+ return "BoxEmitter";
+}
+
+int VisualShaderNodeParticleBoxEmitter::get_input_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeParticleBoxEmitter::PortType VisualShaderNodeParticleBoxEmitter::get_input_port_type(int p_port) const {
+ if (p_port == 0) {
+ return PORT_TYPE_VECTOR;
+ }
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeParticleBoxEmitter::get_input_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "extents";
+ }
+ return String();
+}
+
+String VisualShaderNodeParticleBoxEmitter::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ String code;
+ code += "vec3 __get_random_point_in_box(inout uint seed, vec3 extents) {\n";
+ code += " vec3 half_extents = extents / 2.0;\n";
+ code += " return vec3(__randf_range(seed, -half_extents.x, half_extents.x), __randf_range(seed, -half_extents.y, half_extents.y), __randf_range(seed, -half_extents.z, half_extents.z));\n";
+ code += "}\n\n";
+ return code;
+}
+
+String VisualShaderNodeParticleBoxEmitter::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 {
+ String code;
+ code += " " + p_output_vars[0] + " = __get_random_point_in_box(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ");\n";
+ return code;
+}
+
+VisualShaderNodeParticleBoxEmitter::VisualShaderNodeParticleBoxEmitter() {
+ set_input_port_default_value(0, Vector3(1.0, 1.0, 1.0));
+}
+
+// VisualShaderNodeParticleRingEmitter
+
+String VisualShaderNodeParticleRingEmitter::get_caption() const {
+ return "RingEmitter";
+}
+
+int VisualShaderNodeParticleRingEmitter::get_input_port_count() const {
+ return 3;
+}
+
+VisualShaderNodeParticleRingEmitter::PortType VisualShaderNodeParticleRingEmitter::get_input_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeParticleRingEmitter::get_input_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "radius";
+ } else if (p_port == 1) {
+ return "inner_radius";
+ } else if (p_port == 2) {
+ return "height";
+ }
+ return String();
+}
+
+String VisualShaderNodeParticleRingEmitter::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ String code;
+ code += "vec3 __get_random_point_on_ring(inout uint seed, float radius, float inner_radius, float height) {\n";
+ code += " float angle = __rand_from_seed(seed) * PI * 2.0;\n";
+ code += " vec2 ring = vec2(sin(angle), cos(angle)) * __randf_range(seed, inner_radius, radius);\n";
+ code += " return vec3(ring.x, __randf_range(seed, min(0.0, height), max(0.0, height)), ring.y);\n";
+ code += "}\n\n";
+ return code;
+}
+
+String VisualShaderNodeParticleRingEmitter::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 {
+ String code;
+ code = " " + p_output_vars[0] + " = __get_random_point_on_ring(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ", " + (p_input_vars[2].is_empty() ? (String)get_input_port_default_value(2) : p_input_vars[2]) + ");\n";
+ return code;
+}
+
+VisualShaderNodeParticleRingEmitter::VisualShaderNodeParticleRingEmitter() {
+ set_input_port_default_value(0, 10.0);
+ set_input_port_default_value(1, 0.0);
+ set_input_port_default_value(2, 0.0);
+}
+
+// VisualShaderNodeParticleMultiplyByAxisAngle
+
+void VisualShaderNodeParticleMultiplyByAxisAngle::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_degrees_mode", "enabled"), &VisualShaderNodeParticleMultiplyByAxisAngle::set_degrees_mode);
+ ClassDB::bind_method(D_METHOD("is_degrees_mode"), &VisualShaderNodeParticleMultiplyByAxisAngle::is_degrees_mode);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "degrees_mode"), "set_degrees_mode", "is_degrees_mode");
+}
+
+String VisualShaderNodeParticleMultiplyByAxisAngle::get_caption() const {
+ return "MultiplyByAxisAngle";
+}
+
+int VisualShaderNodeParticleMultiplyByAxisAngle::get_input_port_count() const {
+ return 3;
+}
+
+VisualShaderNodeParticleMultiplyByAxisAngle::PortType VisualShaderNodeParticleMultiplyByAxisAngle::get_input_port_type(int p_port) const {
+ if (p_port == 0 || p_port == 1) { // position, rotation_axis
+ return PORT_TYPE_VECTOR;
+ }
+ return PORT_TYPE_SCALAR; // angle (degrees/radians)
+}
+
+String VisualShaderNodeParticleMultiplyByAxisAngle::get_input_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "position";
+ }
+ if (p_port == 1) {
+ return "axis";
+ }
+ if (p_port == 2) {
+ if (degrees_mode) {
+ return "angle (degrees)";
+ } else {
+ return "angle (radians)";
+ }
+ }
+ return String();
+}
+
+bool VisualShaderNodeParticleMultiplyByAxisAngle::is_show_prop_names() const {
+ return true;
+}
+
+int VisualShaderNodeParticleMultiplyByAxisAngle::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeParticleMultiplyByAxisAngle::PortType VisualShaderNodeParticleMultiplyByAxisAngle::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeParticleMultiplyByAxisAngle::get_output_port_name(int p_port) const {
+ return "position";
+}
+
+String VisualShaderNodeParticleMultiplyByAxisAngle::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 {
+ String code;
+ if (degrees_mode) {
+ code += " " + p_output_vars[0] + " = __build_rotation_mat3(" + (p_input_vars[1].is_empty() ? ("vec3" + (String)get_input_port_default_value(1)) : p_input_vars[1]) + ", radians(" + (p_input_vars[2].is_empty() ? (String)get_input_port_default_value(2) : p_input_vars[2]) + ")) * " + (p_input_vars[0].is_empty() ? "vec3(0.0)" : p_input_vars[0]) + ";\n";
+ } else {
+ code += " " + p_output_vars[0] + " = __build_rotation_mat3(" + (p_input_vars[1].is_empty() ? ("vec3" + (String)get_input_port_default_value(1)) : p_input_vars[1]) + ", " + (p_input_vars[2].is_empty() ? (String)get_input_port_default_value(2) : p_input_vars[2]) + ") * " + (p_input_vars[0].is_empty() ? "vec3(0.0)" : p_input_vars[0]) + ";\n";
+ }
+ return code;
+}
+
+void VisualShaderNodeParticleMultiplyByAxisAngle::set_degrees_mode(bool p_enabled) {
+ degrees_mode = p_enabled;
+ emit_changed();
+}
+
+bool VisualShaderNodeParticleMultiplyByAxisAngle::is_degrees_mode() const {
+ return degrees_mode;
+}
+
+Vector<StringName> VisualShaderNodeParticleMultiplyByAxisAngle::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("degrees_mode");
+ props.push_back("axis_amount");
+ return props;
+}
+
+VisualShaderNodeParticleMultiplyByAxisAngle::VisualShaderNodeParticleMultiplyByAxisAngle() {
+ set_input_port_default_value(1, Vector3(1, 0, 0));
+ set_input_port_default_value(2, 0.0);
+}
+
+// VisualShaderNodeParticleConeVelocity
+
+String VisualShaderNodeParticleConeVelocity::get_caption() const {
+ return "ConeVelocity";
+}
+
+int VisualShaderNodeParticleConeVelocity::get_input_port_count() const {
+ return 2;
+}
+
+VisualShaderNodeParticleConeVelocity::PortType VisualShaderNodeParticleConeVelocity::get_input_port_type(int p_port) const {
+ if (p_port == 0) {
+ return PORT_TYPE_VECTOR;
+ } else if (p_port == 1) {
+ return PORT_TYPE_SCALAR;
+ }
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeParticleConeVelocity::get_input_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "direction";
+ } else if (p_port == 1) {
+ return "spread(degrees)";
+ }
+ return String();
+}
+
+int VisualShaderNodeParticleConeVelocity::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeParticleConeVelocity::PortType VisualShaderNodeParticleConeVelocity::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeParticleConeVelocity::get_output_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "velocity";
+ }
+ return String();
+}
+
+String VisualShaderNodeParticleConeVelocity::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 {
+ String code;
+ code += " __radians = radians(" + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ");\n";
+ code += " __scalar_buff1 = __rand_from_seed_m1_p1(__seed) * __radians;\n";
+ code += " __scalar_buff2 = __rand_from_seed_m1_p1(__seed) * __radians;\n";
+ code += " __vec3_buff1 = " + (p_input_vars[0].is_empty() ? "vec3" + (String)get_input_port_default_value(0) : p_input_vars[0]) + ";\n";
+ code += " __scalar_buff1 += __vec3_buff1.z != 0.0 ? atan(__vec3_buff1.x, __vec3_buff1.z) : sign(__vec3_buff1.x) * (PI / 2.0);\n";
+ code += " __scalar_buff2 += __vec3_buff1.z != 0.0 ? atan(__vec3_buff1.y, abs(__vec3_buff1.z)) : (__vec3_buff1.x != 0.0 ? atan(__vec3_buff1.y, abs(__vec3_buff1.x)) : sign(__vec3_buff1.y) * (PI / 2.0));\n";
+ code += " __vec3_buff1 = vec3(sin(__scalar_buff1), 0.0, cos(__scalar_buff1));\n";
+ code += " __vec3_buff2 = vec3(0.0, sin(__scalar_buff2), cos(__scalar_buff2));\n";
+ code += " __vec3_buff2.z = __vec3_buff2.z / max(0.0001, sqrt(abs(__vec3_buff2.z)));\n";
+ code += " " + p_output_vars[0] + " = normalize(vec3(__vec3_buff1.x * __vec3_buff2.z, __vec3_buff2.y, __vec3_buff1.z * __vec3_buff2.z));\n";
+ return code;
+}
+
+VisualShaderNodeParticleConeVelocity::VisualShaderNodeParticleConeVelocity() {
+ set_input_port_default_value(0, Vector3(1, 0, 0));
+ set_input_port_default_value(1, 45.0);
+}
+
+// VisualShaderNodeParticleRandomness
+
+void VisualShaderNodeParticleRandomness::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeParticleRandomness::set_op_type);
+ ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeParticleRandomness::get_op_type);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector"), "set_op_type", "get_op_type");
+
+ BIND_ENUM_CONSTANT(OP_TYPE_SCALAR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR);
+ BIND_ENUM_CONSTANT(OP_TYPE_MAX);
+}
+
+Vector<StringName> VisualShaderNodeParticleRandomness::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("op_type");
+ return props;
+}
+
+String VisualShaderNodeParticleRandomness::get_caption() const {
+ return "ParticleRandomness";
+}
+
+int VisualShaderNodeParticleRandomness::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeParticleRandomness::PortType VisualShaderNodeParticleRandomness::get_output_port_type(int p_port) const {
+ if (op_type == OP_TYPE_VECTOR) {
+ return PORT_TYPE_VECTOR;
+ }
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeParticleRandomness::get_output_port_name(int p_port) const {
+ return "random";
+}
+
+int VisualShaderNodeParticleRandomness::get_input_port_count() const {
+ return 2;
+}
+
+VisualShaderNodeParticleRandomness::PortType VisualShaderNodeParticleRandomness::get_input_port_type(int p_port) const {
+ if (op_type == OP_TYPE_VECTOR) {
+ return PORT_TYPE_VECTOR;
+ }
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeParticleRandomness::get_input_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "min";
+ } else if (p_port == 1) {
+ return "max";
+ }
+ return String();
+}
+
+String VisualShaderNodeParticleRandomness::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 {
+ String code;
+ if (op_type == OP_TYPE_SCALAR) {
+ code += vformat(" %s = __randf_range(__seed, %s, %s);\n", p_output_vars[0], p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0], p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]);
+ } else if (op_type == OP_TYPE_VECTOR) {
+ code += vformat(" %s = __randv_range(__seed, %s, %s);\n", p_output_vars[0], p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0], p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]);
+ }
+ return code;
+}
+
+void VisualShaderNodeParticleRandomness::set_op_type(OpType p_op_type) {
+ 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();
+}
+
+VisualShaderNodeParticleRandomness::OpType VisualShaderNodeParticleRandomness::get_op_type() const {
+ return op_type;
+}
+
+VisualShaderNodeParticleRandomness::VisualShaderNodeParticleRandomness() {
+ set_input_port_default_value(0, 0.0);
+ set_input_port_default_value(1, 1.0);
+}
+
+// VisualShaderNodeParticleAccelerator
+
+void VisualShaderNodeParticleAccelerator::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_mode", "mode"), &VisualShaderNodeParticleAccelerator::set_mode);
+ ClassDB::bind_method(D_METHOD("get_mode"), &VisualShaderNodeParticleAccelerator::get_mode);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Linear,Radial,Tangential"), "set_mode", "get_mode");
+
+ BIND_ENUM_CONSTANT(MODE_LINEAR);
+ BIND_ENUM_CONSTANT(MODE_RADIAL)
+ BIND_ENUM_CONSTANT(MODE_TANGENTIAL);
+ BIND_ENUM_CONSTANT(MODE_MAX);
+}
+
+Vector<StringName> VisualShaderNodeParticleAccelerator::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("mode");
+ return props;
+}
+
+String VisualShaderNodeParticleAccelerator::get_caption() const {
+ return "ParticleAccelerator";
+}
+
+int VisualShaderNodeParticleAccelerator::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeParticleAccelerator::PortType VisualShaderNodeParticleAccelerator::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeParticleAccelerator::get_output_port_name(int p_port) const {
+ return String();
+}
+
+int VisualShaderNodeParticleAccelerator::get_input_port_count() const {
+ return 3;
+}
+
+VisualShaderNodeParticleAccelerator::PortType VisualShaderNodeParticleAccelerator::get_input_port_type(int p_port) const {
+ if (p_port == 0) {
+ return PORT_TYPE_VECTOR;
+ } else if (p_port == 1) {
+ return PORT_TYPE_SCALAR;
+ } else if (p_port == 2) {
+ return PORT_TYPE_VECTOR;
+ }
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeParticleAccelerator::get_input_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "amount";
+ } else if (p_port == 1) {
+ return "randomness";
+ } else if (p_port == 2) {
+ return "axis";
+ }
+ return String();
+}
+
+String VisualShaderNodeParticleAccelerator::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 {
+ String code;
+ switch (mode) {
+ case MODE_LINEAR:
+ code += " " + p_output_vars[0] + " = length(VELOCITY) > 0.0 ? " + "normalize(VELOCITY) * " + (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_RADIAL:
+ code += " " + p_output_vars[0] + " = length(__diff) > 0.0 ? __ndiff * " + (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_TANGENTIAL:
+ 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;
+ default:
+ break;
+ }
+
+ return code;
+}
+
+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();
+}
+
+VisualShaderNodeParticleAccelerator::Mode VisualShaderNodeParticleAccelerator::get_mode() const {
+ return mode;
+}
+
+VisualShaderNodeParticleAccelerator::VisualShaderNodeParticleAccelerator() {
+ set_input_port_default_value(0, Vector3(1, 1, 1));
+ set_input_port_default_value(1, 0.0);
+ set_input_port_default_value(2, Vector3(0, -9.8, 0));
+}
+
+// VisualShaderNodeParticleOutput
+
+String VisualShaderNodeParticleOutput::get_caption() const {
+ if (shader_type == VisualShader::TYPE_START) {
+ return "StartOutput";
+ } else if (shader_type == VisualShader::TYPE_PROCESS) {
+ return "ProcessOutput";
+ } else if (shader_type == VisualShader::TYPE_COLLIDE) {
+ return "CollideOutput";
+ } else if (shader_type == VisualShader::TYPE_START_CUSTOM) {
+ return "CustomStartOutput";
+ } else if (shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
+ return "CustomProcessOutput";
+ }
+ return String();
+}
+
+int VisualShaderNodeParticleOutput::get_input_port_count() const {
+ if (shader_type == VisualShader::TYPE_START) {
+ return 8;
+ } else if (shader_type == VisualShader::TYPE_COLLIDE) {
+ return 5;
+ } else if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
+ return 6;
+ } else { // TYPE_PROCESS
+ return 7;
+ }
+ return 0;
+}
+
+VisualShaderNodeParticleOutput::PortType VisualShaderNodeParticleOutput::get_input_port_type(int p_port) const {
+ switch (p_port) {
+ case 0:
+ if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
+ return PORT_TYPE_VECTOR; // custom.rgb
+ }
+ return PORT_TYPE_BOOLEAN; // active
+ case 1:
+ if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
+ break; // custom.a (scalar)
+ }
+ return PORT_TYPE_VECTOR; // velocity
+ case 2:
+ return PORT_TYPE_VECTOR; // color & velocity
+ case 3:
+ if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
+ return PORT_TYPE_VECTOR; // color
+ }
+ break; // alpha (scalar)
+ case 4:
+ if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
+ break; // alpha
+ }
+ if (shader_type == VisualShader::TYPE_PROCESS) {
+ break; // scale
+ }
+ if (shader_type == VisualShader::TYPE_COLLIDE) {
+ return PORT_TYPE_TRANSFORM; // transform
+ }
+ return PORT_TYPE_VECTOR; // position
+ case 5:
+ if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
+ return PORT_TYPE_TRANSFORM; // transform
+ }
+ if (shader_type == VisualShader::TYPE_PROCESS) {
+ return PORT_TYPE_VECTOR; // rotation_axis
+ }
+ break; // scale (scalar)
+ case 6:
+ if (shader_type == VisualShader::TYPE_START) {
+ return PORT_TYPE_VECTOR; // rotation_axis
+ }
+ break;
+ case 7:
+ break; // angle (scalar)
+ }
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeParticleOutput::get_input_port_name(int p_port) const {
+ String port_name;
+ switch (p_port) {
+ case 0:
+ if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
+ port_name = "custom";
+ break;
+ }
+ port_name = "active";
+ break;
+ case 1:
+ if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
+ port_name = "custom_alpha";
+ break;
+ }
+ port_name = "velocity";
+ break;
+ case 2:
+ if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
+ port_name = "velocity";
+ break;
+ }
+ port_name = "color";
+ break;
+ case 3:
+ if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
+ port_name = "color";
+ break;
+ }
+ port_name = "alpha";
+ break;
+ case 4:
+ if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
+ port_name = "alpha";
+ break;
+ }
+ if (shader_type == VisualShader::TYPE_PROCESS) {
+ port_name = "scale";
+ break;
+ }
+ if (shader_type == VisualShader::TYPE_COLLIDE) {
+ port_name = "transform";
+ break;
+ }
+ port_name = "position";
+ break;
+ case 5:
+ if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
+ port_name = "transform";
+ break;
+ }
+ if (shader_type == VisualShader::TYPE_PROCESS) {
+ port_name = "rotation_axis";
+ break;
+ }
+ port_name = "scale";
+ break;
+ case 6:
+ if (shader_type == VisualShader::TYPE_PROCESS) {
+ port_name = "angle_in_radians";
+ break;
+ }
+ port_name = "rotation_axis";
+ break;
+ case 7:
+ port_name = "angle_in_radians";
+ break;
+ default:
+ break;
+ }
+ if (!port_name.is_empty()) {
+ return port_name.capitalize();
+ }
+ return String();
+}
+
+bool VisualShaderNodeParticleOutput::is_port_separator(int p_index) const {
+ if (shader_type == VisualShader::TYPE_START || shader_type == VisualShader::TYPE_PROCESS) {
+ String name = get_input_port_name(p_index);
+ return bool(name == "Scale");
+ }
+ if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
+ String name = get_input_port_name(p_index);
+ return bool(name == "Velocity");
+ }
+ return false;
+}
+
+String VisualShaderNodeParticleOutput::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 {
+ String code;
+ String tab = " ";
+
+ if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
+ if (!p_input_vars[0].is_empty()) { // custom.rgb
+ code += tab + "CUSTOM.rgb = " + p_input_vars[0] + ";\n";
+ }
+ if (!p_input_vars[1].is_empty()) { // custom.a
+ code += tab + "CUSTOM.a = " + p_input_vars[1] + ";\n";
+ }
+ if (!p_input_vars[2].is_empty()) { // velocity
+ code += tab + "VELOCITY = " + p_input_vars[2] + ";\n";
+ }
+ if (!p_input_vars[3].is_empty()) { // color.rgb
+ code += tab + "COLOR.rgb = " + p_input_vars[3] + ";\n";
+ }
+ if (!p_input_vars[4].is_empty()) { // color.a
+ code += tab + "COLOR.a = " + p_input_vars[4] + ";\n";
+ }
+ if (!p_input_vars[5].is_empty()) { // transform
+ code += tab + "TRANSFORM = " + p_input_vars[5] + ";\n";
+ }
+ } else {
+ if (!p_input_vars[0].is_empty()) { // active (begin)
+ code += tab + "ACTIVE = " + p_input_vars[0] + ";\n";
+ code += tab + "if(ACTIVE) {\n";
+ tab += " ";
+ }
+ if (!p_input_vars[1].is_empty()) { // velocity
+ code += tab + "VELOCITY = " + p_input_vars[1] + ";\n";
+ }
+ if (!p_input_vars[2].is_empty()) { // color
+ code += tab + "COLOR.rgb = " + p_input_vars[2] + ";\n";
+ }
+ if (!p_input_vars[3].is_empty()) { // alpha
+ code += tab + "COLOR.a = " + p_input_vars[3] + ";\n";
+ }
+
+ // position
+ if (shader_type == VisualShader::TYPE_START) {
+ code += tab + "if (RESTART_POSITION) {\n";
+ if (!p_input_vars[4].is_empty()) {
+ code += tab + " TRANSFORM = mat4(vec4(1.0, 0.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(" + p_input_vars[4] + ", 1.0));\n";
+ } else {
+ code += tab + " TRANSFORM = mat4(vec4(1.0, 0.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
+ }
+ code += tab + " if (RESTART_VELOCITY) {\n";
+ code += tab + " VELOCITY = (EMISSION_TRANSFORM * vec4(VELOCITY, 0.0)).xyz;\n";
+ code += tab + " }\n";
+ code += tab + " TRANSFORM = EMISSION_TRANSFORM * TRANSFORM;\n";
+ code += tab + "}\n";
+ } else if (shader_type == VisualShader::TYPE_COLLIDE) { // position
+ if (!p_input_vars[4].is_empty()) {
+ code += tab + "TRANSFORM = " + p_input_vars[4] + ";\n";
+ }
+ }
+
+ if (shader_type == VisualShader::TYPE_START || shader_type == VisualShader::TYPE_PROCESS) {
+ int scale = 5;
+ int rotation_axis = 6;
+ int rotation = 7;
+ if (shader_type == VisualShader::TYPE_PROCESS) {
+ scale = 4;
+ rotation_axis = 5;
+ rotation = 6;
+ }
+ String op;
+ if (shader_type == VisualShader::TYPE_START) {
+ op = "*=";
+ } else {
+ op = "=";
+ }
+
+ if (!p_input_vars[rotation].is_empty()) { // rotation_axis & angle_in_radians
+ String axis;
+ if (p_input_vars[rotation_axis].is_empty()) {
+ axis = "vec3(0, 1, 0)";
+ } else {
+ axis = p_input_vars[rotation_axis];
+ }
+ code += tab + "TRANSFORM " + op + " __build_rotation_mat4(" + axis + ", " + p_input_vars[rotation] + ");\n";
+ }
+ if (!p_input_vars[scale].is_empty()) { // scale
+ code += tab + "TRANSFORM " + op + " mat4(vec4(" + p_input_vars[scale] + ", 0, 0, 0), vec4(0, " + p_input_vars[scale] + ", 0, 0), vec4(0, 0, " + p_input_vars[scale] + ", 0), vec4(0, 0, 0, 1));\n";
+ }
+ }
+ if (!p_input_vars[0].is_empty()) { // active (end)
+ code += " }\n";
+ }
+ }
+ return code;
+}
+
+VisualShaderNodeParticleOutput::VisualShaderNodeParticleOutput() {
+}
+
+// EmitParticle
+
+Vector<StringName> VisualShaderNodeParticleEmit::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("flags");
+ return props;
+}
+
+void VisualShaderNodeParticleEmit::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_flags", "flags"), &VisualShaderNodeParticleEmit::set_flags);
+ ClassDB::bind_method(D_METHOD("get_flags"), &VisualShaderNodeParticleEmit::get_flags);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Position,RotScale,Velocity,Color,Custom"), "set_flags", "get_flags");
+
+ BIND_ENUM_CONSTANT(EMIT_FLAG_POSITION);
+ BIND_ENUM_CONSTANT(EMIT_FLAG_ROT_SCALE);
+ BIND_ENUM_CONSTANT(EMIT_FLAG_VELOCITY);
+ BIND_ENUM_CONSTANT(EMIT_FLAG_COLOR);
+ BIND_ENUM_CONSTANT(EMIT_FLAG_CUSTOM);
+}
+
+String VisualShaderNodeParticleEmit::get_caption() const {
+ return "EmitParticle";
+}
+
+int VisualShaderNodeParticleEmit::get_input_port_count() const {
+ return 7;
+}
+
+VisualShaderNodeParticleEmit::PortType VisualShaderNodeParticleEmit::get_input_port_type(int p_port) const {
+ switch (p_port) {
+ case 0:
+ return PORT_TYPE_BOOLEAN;
+ case 1:
+ return PORT_TYPE_TRANSFORM;
+ case 2:
+ return PORT_TYPE_VECTOR;
+ case 3:
+ return PORT_TYPE_VECTOR;
+ case 4:
+ return PORT_TYPE_SCALAR;
+ case 5:
+ return PORT_TYPE_VECTOR;
+ case 6:
+ return PORT_TYPE_SCALAR;
+ }
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeParticleEmit::get_input_port_name(int p_port) const {
+ switch (p_port) {
+ case 0:
+ return "condition";
+ case 1:
+ return "transform";
+ case 2:
+ return "velocity";
+ case 3:
+ return "color";
+ case 4:
+ return "alpha";
+ case 5:
+ return "custom";
+ case 6:
+ return "custom_alpha";
+ }
+ return String();
+}
+
+int VisualShaderNodeParticleEmit::get_output_port_count() const {
+ return 0;
+}
+
+VisualShaderNodeParticleEmit::PortType VisualShaderNodeParticleEmit::get_output_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeParticleEmit::get_output_port_name(int p_port) const {
+ return String();
+}
+
+void VisualShaderNodeParticleEmit::add_flag(EmitFlags p_flag) {
+ flags |= p_flag;
+ emit_changed();
+}
+
+bool VisualShaderNodeParticleEmit::has_flag(EmitFlags p_flag) const {
+ return flags & p_flag;
+}
+
+void VisualShaderNodeParticleEmit::set_flags(EmitFlags p_flags) {
+ flags = (int)p_flags;
+ emit_changed();
+}
+
+VisualShaderNodeParticleEmit::EmitFlags VisualShaderNodeParticleEmit::get_flags() const {
+ return EmitFlags(flags);
+}
+
+bool VisualShaderNodeParticleEmit::is_show_prop_names() const {
+ return true;
+}
+
+bool VisualShaderNodeParticleEmit::is_generate_input_var(int p_port) const {
+ if (p_port == 0) {
+ if (!is_input_port_connected(0)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+String VisualShaderNodeParticleEmit::get_input_port_default_hint(int p_port) const {
+ switch (p_port) {
+ case 1:
+ return "default";
+ case 2:
+ return "default";
+ case 3:
+ return "default";
+ case 4:
+ return "default";
+ case 5:
+ return "default";
+ case 6:
+ return "default";
+ }
+ return String();
+}
+
+String VisualShaderNodeParticleEmit::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 {
+ String code;
+ String tab;
+ bool default_condition = false;
+
+ if (!is_input_port_connected(0)) {
+ default_condition = true;
+ if (get_input_port_default_value(0)) {
+ tab = " ";
+ } else {
+ return code;
+ }
+ } else {
+ tab = " ";
+ }
+
+ String transform;
+ if (p_input_vars[1].is_empty()) {
+ transform = "TRANSFORM";
+ } else {
+ transform = p_input_vars[1];
+ }
+
+ String velocity;
+ if (p_input_vars[2].is_empty()) {
+ velocity = "VELOCITY";
+ } else {
+ velocity = p_input_vars[2];
+ }
+
+ String color;
+ if (p_input_vars[3].is_empty()) {
+ color = "COLOR.rgb";
+ } else {
+ color = p_input_vars[3];
+ }
+
+ String alpha;
+ if (p_input_vars[4].is_empty()) {
+ alpha = "COLOR.a";
+ } else {
+ alpha = p_input_vars[4];
+ }
+
+ String custom;
+ if (p_input_vars[5].is_empty()) {
+ custom = "CUSTOM.rgb";
+ } else {
+ custom = p_input_vars[5];
+ }
+
+ String custom_alpha;
+ if (p_input_vars[6].is_empty()) {
+ custom_alpha = "CUSTOM.a";
+ } else {
+ custom_alpha = p_input_vars[6];
+ }
+
+ List<String> flags_arr;
+
+ if (has_flag(EmitFlags::EMIT_FLAG_POSITION)) {
+ flags_arr.push_back("FLAG_EMIT_POSITION");
+ }
+ if (has_flag(EmitFlags::EMIT_FLAG_ROT_SCALE)) {
+ flags_arr.push_back("FLAG_EMIT_ROT_SCALE");
+ }
+ if (has_flag(EmitFlags::EMIT_FLAG_VELOCITY)) {
+ flags_arr.push_back("FLAG_EMIT_VELOCITY");
+ }
+ if (has_flag(EmitFlags::EMIT_FLAG_COLOR)) {
+ flags_arr.push_back("FLAG_EMIT_COLOR");
+ }
+ if (has_flag(EmitFlags::EMIT_FLAG_CUSTOM)) {
+ flags_arr.push_back("FLAG_EMIT_CUSTOM");
+ }
+
+ String flags;
+
+ for (int i = 0; i < flags_arr.size(); i++) {
+ if (i > 0) {
+ flags += "|";
+ }
+ flags += flags_arr[i];
+ }
+
+ if (flags.is_empty()) {
+ flags = "uint(0)";
+ }
+
+ if (!default_condition) {
+ code += " if (" + p_input_vars[0] + ") {\n";
+ }
+
+ code += tab + "emit_subparticle(" + transform + ", " + velocity + ", vec4(" + color + ", " + alpha + "), vec4(" + custom + ", " + custom_alpha + "), " + flags + ");\n";
+
+ if (!default_condition) {
+ code += " }\n";
+ }
+
+ return code;
+}
+
+VisualShaderNodeParticleEmit::VisualShaderNodeParticleEmit() {
+ set_input_port_default_value(0, true);
+}
diff --git a/scene/resources/visual_shader_particle_nodes.h b/scene/resources/visual_shader_particle_nodes.h
new file mode 100644
index 0000000000..f5435c3511
--- /dev/null
+++ b/scene/resources/visual_shader_particle_nodes.h
@@ -0,0 +1,285 @@
+/*************************************************************************/
+/* visual_shader_particle_nodes.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 VISUAL_SHADER_PARTICLE_NODES_H
+#define VISUAL_SHADER_PARTICLE_NODES_H
+
+#include "scene/resources/visual_shader.h"
+
+// Emit nodes
+
+class VisualShaderNodeParticleEmitter : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeParticleEmitter, VisualShaderNode);
+
+public:
+ virtual int get_output_port_count() const override;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) const override;
+
+ VisualShaderNodeParticleEmitter();
+};
+
+class VisualShaderNodeParticleSphereEmitter : public VisualShaderNodeParticleEmitter {
+ GDCLASS(VisualShaderNodeParticleSphereEmitter, VisualShaderNodeParticleEmitter);
+
+public:
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+
+ virtual String generate_global_per_node(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;
+
+ VisualShaderNodeParticleSphereEmitter();
+};
+
+class VisualShaderNodeParticleBoxEmitter : public VisualShaderNodeParticleEmitter {
+ GDCLASS(VisualShaderNodeParticleBoxEmitter, VisualShaderNodeParticleEmitter);
+
+public:
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+
+ virtual String generate_global_per_node(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;
+
+ VisualShaderNodeParticleBoxEmitter();
+};
+
+class VisualShaderNodeParticleRingEmitter : public VisualShaderNodeParticleEmitter {
+ GDCLASS(VisualShaderNodeParticleRingEmitter, VisualShaderNodeParticleEmitter);
+
+public:
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+
+ virtual String generate_global_per_node(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;
+
+ VisualShaderNodeParticleRingEmitter();
+};
+
+class VisualShaderNodeParticleMultiplyByAxisAngle : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeParticleMultiplyByAxisAngle, VisualShaderNode);
+ bool degrees_mode = true;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+ virtual bool is_show_prop_names() const override;
+
+ virtual int get_output_port_count() const override;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) 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_degrees_mode(bool p_enabled);
+ bool is_degrees_mode() const;
+ Vector<StringName> get_editable_properties() const override;
+
+ VisualShaderNodeParticleMultiplyByAxisAngle();
+};
+
+class VisualShaderNodeParticleConeVelocity : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeParticleConeVelocity, VisualShaderNode);
+
+public:
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+
+ virtual int get_output_port_count() const override;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) 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;
+
+ VisualShaderNodeParticleConeVelocity();
+};
+
+class VisualShaderNodeParticleRandomness : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeParticleRandomness, VisualShaderNode);
+
+public:
+ enum OpType {
+ OP_TYPE_SCALAR,
+ OP_TYPE_VECTOR,
+ OP_TYPE_MAX,
+ };
+
+private:
+ OpType op_type = OP_TYPE_SCALAR;
+
+protected:
+ static void _bind_methods();
+
+public:
+ Vector<StringName> get_editable_properties() const override;
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+
+ virtual int get_output_port_count() const override;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) 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_op_type(OpType p_type);
+ OpType get_op_type() const;
+
+ VisualShaderNodeParticleRandomness();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeParticleRandomness::OpType)
+
+// Process nodes
+
+class VisualShaderNodeParticleAccelerator : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeParticleAccelerator, VisualShaderNode);
+
+public:
+ enum Mode {
+ MODE_LINEAR,
+ MODE_RADIAL,
+ MODE_TANGENTIAL,
+ MODE_MAX,
+ };
+
+private:
+ Mode mode = MODE_LINEAR;
+
+protected:
+ static void _bind_methods();
+
+public:
+ Vector<StringName> get_editable_properties() const override;
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+
+ virtual int get_output_port_count() const override;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) 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_mode(Mode p_mode);
+ Mode get_mode() const;
+
+ VisualShaderNodeParticleAccelerator();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeParticleAccelerator::Mode)
+
+// Common nodes
+
+class VisualShaderNodeParticleOutput : public VisualShaderNodeOutput {
+ GDCLASS(VisualShaderNodeParticleOutput, VisualShaderNodeOutput);
+
+public:
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+ virtual bool is_port_separator(int p_index) 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;
+
+ VisualShaderNodeParticleOutput();
+};
+
+class VisualShaderNodeParticleEmit : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeParticleEmit, VisualShaderNode);
+
+public:
+ enum EmitFlags {
+ EMIT_FLAG_POSITION = 1,
+ EMIT_FLAG_ROT_SCALE = 2,
+ EMIT_FLAG_VELOCITY = 4,
+ EMIT_FLAG_COLOR = 8,
+ EMIT_FLAG_CUSTOM = 16,
+ };
+
+protected:
+ int flags = EMIT_FLAG_POSITION | EMIT_FLAG_ROT_SCALE | EMIT_FLAG_VELOCITY | EMIT_FLAG_COLOR | EMIT_FLAG_CUSTOM;
+ static void _bind_methods();
+
+public:
+ Vector<StringName> get_editable_properties() const override;
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+
+ virtual int get_output_port_count() const override;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) const override;
+
+ void add_flag(EmitFlags p_flag);
+ bool has_flag(EmitFlags p_flag) const;
+
+ void set_flags(EmitFlags p_flags);
+ EmitFlags get_flags() const;
+
+ virtual bool is_show_prop_names() const override;
+ virtual bool is_generate_input_var(int p_port) const override;
+ virtual String get_input_port_default_hint(int p_port) 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;
+
+ VisualShaderNodeParticleEmit();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeParticleEmit::EmitFlags)
+
+#endif
diff --git a/scene/resources/visual_shader_sdf_nodes.cpp b/scene/resources/visual_shader_sdf_nodes.cpp
new file mode 100644
index 0000000000..14c655b129
--- /dev/null
+++ b/scene/resources/visual_shader_sdf_nodes.cpp
@@ -0,0 +1,283 @@
+/*************************************************************************/
+/* visual_shader_sdf_nodes.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 "visual_shader_sdf_nodes.h"
+
+// VisualShaderNodeSDFToScreenUV
+
+String VisualShaderNodeSDFToScreenUV::get_caption() const {
+ return "SDFToScreenUV";
+}
+
+int VisualShaderNodeSDFToScreenUV::get_input_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeSDFToScreenUV::PortType VisualShaderNodeSDFToScreenUV::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeSDFToScreenUV::get_input_port_name(int p_port) const {
+ return "sdf_pos";
+}
+
+int VisualShaderNodeSDFToScreenUV::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeSDFToScreenUV::PortType VisualShaderNodeSDFToScreenUV::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeSDFToScreenUV::get_output_port_name(int p_port) const {
+ return "";
+}
+
+String VisualShaderNodeSDFToScreenUV::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 {
+ return " " + p_output_vars[0] + " = vec3(sdf_to_screen_uv(" + (p_input_vars[0] == String() ? "vec2(0.0)" : p_input_vars[0] + ".xy") + "), 0.0f);\n";
+}
+
+VisualShaderNodeSDFToScreenUV::VisualShaderNodeSDFToScreenUV() {
+}
+
+// VisualShaderNodeScreenUVToSDF
+
+String VisualShaderNodeScreenUVToSDF::get_caption() const {
+ return "ScreenUVToSDF";
+}
+
+int VisualShaderNodeScreenUVToSDF::get_input_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeScreenUVToSDF::PortType VisualShaderNodeScreenUVToSDF::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeScreenUVToSDF::get_input_port_name(int p_port) const {
+ return "uv";
+}
+
+int VisualShaderNodeScreenUVToSDF::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeScreenUVToSDF::PortType VisualShaderNodeScreenUVToSDF::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeScreenUVToSDF::get_output_port_name(int p_port) const {
+ return "";
+}
+
+String VisualShaderNodeScreenUVToSDF::get_input_port_default_hint(int p_port) const {
+ if (p_port == 0) {
+ return "default";
+ }
+ return "";
+}
+
+String VisualShaderNodeScreenUVToSDF::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 {
+ return " " + p_output_vars[0] + " = vec3(screen_uv_to_sdf(" + (p_input_vars[0] == String() ? "SCREEN_UV" : p_input_vars[0] + ".xy") + "), 0.0f);\n";
+}
+
+VisualShaderNodeScreenUVToSDF::VisualShaderNodeScreenUVToSDF() {
+}
+
+// VisualShaderNodeTextureSDF
+
+String VisualShaderNodeTextureSDF::get_caption() const {
+ return "TextureSDF";
+}
+
+int VisualShaderNodeTextureSDF::get_input_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeTextureSDF::PortType VisualShaderNodeTextureSDF::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeTextureSDF::get_input_port_name(int p_port) const {
+ return "sdf_pos";
+}
+
+int VisualShaderNodeTextureSDF::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeTextureSDF::PortType VisualShaderNodeTextureSDF::get_output_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeTextureSDF::get_output_port_name(int p_port) const {
+ return "";
+}
+
+String VisualShaderNodeTextureSDF::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 {
+ return " " + p_output_vars[0] + " = texture_sdf(" + (p_input_vars[0] == String() ? "vec2(0.0)" : p_input_vars[0] + ".xy") + ");\n";
+}
+
+VisualShaderNodeTextureSDF::VisualShaderNodeTextureSDF() {
+}
+
+// VisualShaderNodeTextureSDFNormal
+
+String VisualShaderNodeTextureSDFNormal::get_caption() const {
+ return "TextureSDFNormal";
+}
+
+int VisualShaderNodeTextureSDFNormal::get_input_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeTextureSDFNormal::PortType VisualShaderNodeTextureSDFNormal::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeTextureSDFNormal::get_input_port_name(int p_port) const {
+ return "sdf_pos";
+}
+
+int VisualShaderNodeTextureSDFNormal::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeTextureSDFNormal::PortType VisualShaderNodeTextureSDFNormal::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeTextureSDFNormal::get_output_port_name(int p_port) const {
+ return "";
+}
+
+String VisualShaderNodeTextureSDFNormal::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 {
+ return " " + p_output_vars[0] + " = vec3(texture_sdf_normal(" + (p_input_vars[0] == String() ? "vec2(0.0)" : p_input_vars[0] + ".xy") + "), 0.0f);\n";
+}
+
+VisualShaderNodeTextureSDFNormal::VisualShaderNodeTextureSDFNormal() {
+}
+
+// VisualShaderNodeSDFRaymarch
+
+String VisualShaderNodeSDFRaymarch::get_caption() const {
+ return "SDFRaymarch";
+}
+
+int VisualShaderNodeSDFRaymarch::get_input_port_count() const {
+ return 2;
+}
+
+VisualShaderNodeSDFRaymarch::PortType VisualShaderNodeSDFRaymarch::get_input_port_type(int p_port) const {
+ if (p_port == 0 || p_port == 1) {
+ return PORT_TYPE_VECTOR;
+ }
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeSDFRaymarch::get_input_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "from_pos";
+ } else if (p_port == 1) {
+ return "to_pos";
+ }
+ return String();
+}
+
+int VisualShaderNodeSDFRaymarch::get_output_port_count() const {
+ return 3;
+}
+
+VisualShaderNodeSDFRaymarch::PortType VisualShaderNodeSDFRaymarch::get_output_port_type(int p_port) const {
+ if (p_port == 0) {
+ return PORT_TYPE_SCALAR;
+ } else if (p_port == 1) {
+ return PORT_TYPE_BOOLEAN;
+ } else if (p_port == 2) {
+ return PORT_TYPE_VECTOR;
+ }
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeSDFRaymarch::get_output_port_name(int p_port) const {
+ if (p_port == 0) {
+ return "distance";
+ } else if (p_port == 1) {
+ return "hit";
+ } else if (p_port == 2) {
+ return "end_pos";
+ }
+ return String();
+}
+
+String VisualShaderNodeSDFRaymarch::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 {
+ String code;
+
+ code += " {\n";
+
+ if (p_input_vars[0] == String()) {
+ code += " vec2 __from_pos = vec2(0.0f);\n";
+ } else {
+ code += " vec2 __from_pos = " + p_input_vars[0] + ".xy;\n";
+ }
+
+ if (p_input_vars[1] == String()) {
+ code += " vec2 __to_pos = vec2(0.0f);\n";
+ } else {
+ code += " vec2 __to_pos = " + p_input_vars[1] + ".xy;\n";
+ }
+
+ code += "\n vec2 __at = __from_pos;\n";
+ code += " float __max_dist = distance(__from_pos, __to_pos);\n";
+ code += " vec2 __dir = normalize(__to_pos - __from_pos);\n\n";
+
+ code += " float __accum = 0.0f;\n";
+ code += " while(__accum < __max_dist) {\n";
+ code += " float __d = texture_sdf(__at);\n";
+ code += " __accum += __d;\n";
+ code += " if (__d < 0.01f) {\n";
+ code += " break;\n";
+ code += " }\n";
+ code += " __at += __d * __dir;\n";
+ code += " }\n";
+
+ code += " float __dist = min(__max_dist, __accum);\n";
+ code += " " + p_output_vars[0] + " = __dist;\n";
+ code += " " + p_output_vars[1] + " = __accum < __max_dist;\n";
+ code += " " + p_output_vars[2] + " = vec3(__from_pos + __dir * __dist, 0.0f);\n";
+
+ code += " }\n";
+
+ return code;
+}
+
+VisualShaderNodeSDFRaymarch::VisualShaderNodeSDFRaymarch() {
+ simple_decl = false;
+}
diff --git a/scene/resources/visual_shader_sdf_nodes.h b/scene/resources/visual_shader_sdf_nodes.h
new file mode 100644
index 0000000000..0fcf5ec0b5
--- /dev/null
+++ b/scene/resources/visual_shader_sdf_nodes.h
@@ -0,0 +1,132 @@
+/*************************************************************************/
+/* visual_shader_sdf_nodes.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 VISUAL_SHADER_SDF_NODES_H
+#define VISUAL_SHADER_SDF_NODES_H
+
+#include "scene/resources/visual_shader.h"
+
+class VisualShaderNodeSDFToScreenUV : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeSDFToScreenUV, VisualShaderNode);
+
+public:
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+
+ virtual int get_output_port_count() const override;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) 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;
+
+ VisualShaderNodeSDFToScreenUV();
+};
+
+class VisualShaderNodeScreenUVToSDF : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeScreenUVToSDF, VisualShaderNode);
+
+public:
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+
+ virtual int get_output_port_count() const override;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) const override;
+
+ virtual String get_input_port_default_hint(int p_port) 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;
+
+ VisualShaderNodeScreenUVToSDF();
+};
+
+class VisualShaderNodeTextureSDF : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeTextureSDF, VisualShaderNode);
+
+public:
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+
+ virtual int get_output_port_count() const override;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) 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;
+
+ VisualShaderNodeTextureSDF();
+};
+
+class VisualShaderNodeTextureSDFNormal : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeTextureSDFNormal, VisualShaderNode);
+
+public:
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+
+ virtual int get_output_port_count() const override;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) 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;
+
+ VisualShaderNodeTextureSDFNormal();
+};
+
+class VisualShaderNodeSDFRaymarch : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeSDFRaymarch, VisualShaderNode);
+
+public:
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+
+ virtual int get_output_port_count() const override;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) 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;
+
+ VisualShaderNodeSDFRaymarch();
+};
+
+#endif // VISUAL_SHADER_SDF_NODES_H
diff --git a/scene/resources/world_2d.cpp b/scene/resources/world_2d.cpp
index d2bc2bea31..eceb42ee14 100644
--- a/scene/resources/world_2d.cpp
+++ b/scene/resources/world_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,320 +30,37 @@
#include "world_2d.h"
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
#include "scene/2d/camera_2d.h"
-#include "scene/2d/visibility_notifier_2d.h"
+#include "scene/2d/visible_on_screen_notifier_2d.h"
#include "scene/main/window.h"
+#include "servers/navigation_server_2d.h"
#include "servers/physics_server_2d.h"
#include "servers/rendering_server.h"
-struct SpatialIndexer2D {
- struct CellRef {
- int ref;
-
- _FORCE_INLINE_ int inc() {
- ref++;
- return ref;
- }
- _FORCE_INLINE_ int dec() {
- ref--;
- return ref;
- }
-
- _FORCE_INLINE_ CellRef() {
- ref = 0;
- }
- };
-
- struct CellKey {
- union {
- struct {
- int32_t x;
- int32_t y;
- };
- uint64_t key;
- };
-
- bool operator==(const CellKey &p_key) const { return key == p_key.key; }
- _FORCE_INLINE_ bool operator<(const CellKey &p_key) const {
- return key < p_key.key;
- }
- };
-
- struct CellData {
- Map<VisibilityNotifier2D *, CellRef> notifiers;
- };
-
- Map<CellKey, CellData> cells;
- int cell_size;
-
- Map<VisibilityNotifier2D *, Rect2> notifiers;
-
- struct ViewportData {
- Map<VisibilityNotifier2D *, uint64_t> notifiers;
- Rect2 rect;
- };
-
- Map<Viewport *, ViewportData> viewports;
-
- bool changed;
-
- uint64_t pass;
-
- void _notifier_update_cells(VisibilityNotifier2D *p_notifier, const Rect2 &p_rect, bool p_add) {
- Point2i begin = p_rect.position;
- begin /= cell_size;
- Point2i end = p_rect.position + p_rect.size;
- end /= cell_size;
- for (int i = begin.x; i <= end.x; i++) {
- for (int j = begin.y; j <= end.y; j++) {
- CellKey ck;
- ck.x = i;
- ck.y = j;
- Map<CellKey, CellData>::Element *E = cells.find(ck);
-
- if (p_add) {
- if (!E) {
- E = cells.insert(ck, CellData());
- }
- E->get().notifiers[p_notifier].inc();
- } else {
- ERR_CONTINUE(!E);
- if (E->get().notifiers[p_notifier].dec() == 0) {
- E->get().notifiers.erase(p_notifier);
- if (E->get().notifiers.empty()) {
- cells.erase(E);
- }
- }
- }
- }
- }
- }
-
- void _notifier_add(VisibilityNotifier2D *p_notifier, const Rect2 &p_rect) {
- ERR_FAIL_COND(notifiers.has(p_notifier));
- notifiers[p_notifier] = p_rect;
- _notifier_update_cells(p_notifier, p_rect, true);
- changed = true;
- }
-
- void _notifier_update(VisibilityNotifier2D *p_notifier, const Rect2 &p_rect) {
- Map<VisibilityNotifier2D *, Rect2>::Element *E = notifiers.find(p_notifier);
- ERR_FAIL_COND(!E);
- if (E->get() == p_rect) {
- return;
- }
-
- _notifier_update_cells(p_notifier, p_rect, true);
- _notifier_update_cells(p_notifier, E->get(), false);
- E->get() = p_rect;
- changed = true;
- }
-
- void _notifier_remove(VisibilityNotifier2D *p_notifier) {
- Map<VisibilityNotifier2D *, Rect2>::Element *E = notifiers.find(p_notifier);
- ERR_FAIL_COND(!E);
- _notifier_update_cells(p_notifier, E->get(), false);
- notifiers.erase(p_notifier);
-
- List<Viewport *> removed;
- for (Map<Viewport *, ViewportData>::Element *F = viewports.front(); F; F = F->next()) {
- Map<VisibilityNotifier2D *, uint64_t>::Element *G = F->get().notifiers.find(p_notifier);
-
- if (G) {
- F->get().notifiers.erase(G);
- removed.push_back(F->key());
- }
- }
-
- while (!removed.empty()) {
- p_notifier->_exit_viewport(removed.front()->get());
- removed.pop_front();
- }
-
- changed = true;
- }
-
- void _add_viewport(Viewport *p_viewport, const Rect2 &p_rect) {
- ERR_FAIL_COND(viewports.has(p_viewport));
- ViewportData vd;
- vd.rect = p_rect;
- viewports[p_viewport] = vd;
- changed = true;
- }
-
- void _update_viewport(Viewport *p_viewport, const Rect2 &p_rect) {
- Map<Viewport *, ViewportData>::Element *E = viewports.find(p_viewport);
- ERR_FAIL_COND(!E);
- if (E->get().rect == p_rect) {
- return;
- }
- E->get().rect = p_rect;
- changed = true;
- }
-
- void _remove_viewport(Viewport *p_viewport) {
- ERR_FAIL_COND(!viewports.has(p_viewport));
- List<VisibilityNotifier2D *> removed;
- for (Map<VisibilityNotifier2D *, uint64_t>::Element *E = viewports[p_viewport].notifiers.front(); E; E = E->next()) {
- removed.push_back(E->key());
- }
-
- while (!removed.empty()) {
- removed.front()->get()->_exit_viewport(p_viewport);
- removed.pop_front();
- }
-
- viewports.erase(p_viewport);
- }
-
- void _update() {
- if (!changed) {
- return;
- }
-
- for (Map<Viewport *, ViewportData>::Element *E = viewports.front(); E; E = E->next()) {
- Point2i begin = E->get().rect.position;
- begin /= cell_size;
- Point2i end = E->get().rect.position + E->get().rect.size;
- end /= cell_size;
- pass++;
- List<VisibilityNotifier2D *> added;
- List<VisibilityNotifier2D *> removed;
-
- uint64_t visible_cells = (uint64_t)(end.x - begin.x) * (uint64_t)(end.y - begin.y);
-
- if (visible_cells > 10000) {
- //well you zoomed out a lot, it's your problem. To avoid freezing in the for loops below, we'll manually check cell by cell
-
- for (Map<CellKey, CellData>::Element *F = cells.front(); F; F = F->next()) {
- const CellKey &ck = F->key();
-
- if (ck.x < begin.x || ck.x > end.x) {
- continue;
- }
- if (ck.y < begin.y || ck.y > end.y) {
- continue;
- }
-
- //notifiers in cell
- for (Map<VisibilityNotifier2D *, CellRef>::Element *G = F->get().notifiers.front(); G; G = G->next()) {
- Map<VisibilityNotifier2D *, uint64_t>::Element *H = E->get().notifiers.find(G->key());
- if (!H) {
- H = E->get().notifiers.insert(G->key(), pass);
- added.push_back(G->key());
- } else {
- H->get() = pass;
- }
- }
- }
-
- } else {
- //check cells in grid fashion
- for (int i = begin.x; i <= end.x; i++) {
- for (int j = begin.y; j <= end.y; j++) {
- CellKey ck;
- ck.x = i;
- ck.y = j;
-
- Map<CellKey, CellData>::Element *F = cells.find(ck);
- if (!F) {
- continue;
- }
-
- //notifiers in cell
- for (Map<VisibilityNotifier2D *, CellRef>::Element *G = F->get().notifiers.front(); G; G = G->next()) {
- Map<VisibilityNotifier2D *, uint64_t>::Element *H = E->get().notifiers.find(G->key());
- if (!H) {
- H = E->get().notifiers.insert(G->key(), pass);
- added.push_back(G->key());
- } else {
- H->get() = pass;
- }
- }
- }
- }
- }
-
- for (Map<VisibilityNotifier2D *, uint64_t>::Element *F = E->get().notifiers.front(); F; F = F->next()) {
- if (F->get() != pass) {
- removed.push_back(F->key());
- }
- }
-
- while (!added.empty()) {
- added.front()->get()->_enter_viewport(E->key());
- added.pop_front();
- }
-
- while (!removed.empty()) {
- E->get().notifiers.erase(removed.front()->get());
- removed.front()->get()->_exit_viewport(E->key());
- removed.pop_front();
- }
- }
-
- changed = false;
- }
-
- SpatialIndexer2D() {
- pass = 0;
- changed = false;
- cell_size = GLOBAL_DEF("world/2d/cell_size", 100);
- }
-};
-
-void World2D::_register_viewport(Viewport *p_viewport, const Rect2 &p_rect) {
- indexer->_add_viewport(p_viewport, p_rect);
-}
-
-void World2D::_update_viewport(Viewport *p_viewport, const Rect2 &p_rect) {
- indexer->_update_viewport(p_viewport, p_rect);
-}
-
-void World2D::_remove_viewport(Viewport *p_viewport) {
- indexer->_remove_viewport(p_viewport);
-}
-
-void World2D::_register_notifier(VisibilityNotifier2D *p_notifier, const Rect2 &p_rect) {
- indexer->_notifier_add(p_notifier, p_rect);
-}
-
-void World2D::_update_notifier(VisibilityNotifier2D *p_notifier, const Rect2 &p_rect) {
- indexer->_notifier_update(p_notifier, p_rect);
-}
-
-void World2D::_remove_notifier(VisibilityNotifier2D *p_notifier) {
- indexer->_notifier_remove(p_notifier);
-}
-
-void World2D::_update() {
- indexer->_update();
-}
-
-RID World2D::get_canvas() {
+RID World2D::get_canvas() const {
return canvas;
}
-RID World2D::get_space() {
+RID World2D::get_space() const {
return space;
}
-void World2D::get_viewport_list(List<Viewport *> *r_viewports) {
- for (Map<Viewport *, SpatialIndexer2D::ViewportData>::Element *E = indexer->viewports.front(); E; E = E->next()) {
- r_viewports->push_back(E->key());
- }
+RID World2D::get_navigation_map() const {
+ return navigation_map;
}
void World2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_canvas"), &World2D::get_canvas);
ClassDB::bind_method(D_METHOD("get_space"), &World2D::get_space);
+ ClassDB::bind_method(D_METHOD("get_navigation_map"), &World2D::get_navigation_map);
ClassDB::bind_method(D_METHOD("get_direct_space_state"), &World2D::get_direct_space_state);
- ADD_PROPERTY(PropertyInfo(Variant::_RID, "canvas", PROPERTY_HINT_NONE, "", 0), "", "get_canvas");
- ADD_PROPERTY(PropertyInfo(Variant::_RID, "space", PROPERTY_HINT_NONE, "", 0), "", "get_space");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "direct_space_state", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsDirectSpaceState2D", 0), "", "get_direct_space_state");
+ ADD_PROPERTY(PropertyInfo(Variant::RID, "canvas", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_canvas");
+ ADD_PROPERTY(PropertyInfo(Variant::RID, "space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_space");
+ ADD_PROPERTY(PropertyInfo(Variant::RID, "navigation_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_navigation_map");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "direct_space_state", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsDirectSpaceState2D", PROPERTY_USAGE_NONE), "", "get_direct_space_state");
}
PhysicsDirectSpaceState2D *World2D::get_direct_space_state() {
@@ -352,21 +69,26 @@ PhysicsDirectSpaceState2D *World2D::get_direct_space_state() {
World2D::World2D() {
canvas = RenderingServer::get_singleton()->canvas_create();
- space = PhysicsServer2D::get_singleton()->space_create();
- //set space2D to be more friendly with pixels than meters, by adjusting some constants
+ // Create and configure space2D to be more friendly with pixels than meters
+ space = PhysicsServer2D::get_singleton()->space_create();
PhysicsServer2D::get_singleton()->space_set_active(space, true);
- PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_GRAVITY, GLOBAL_DEF("physics/2d/default_gravity", 98));
+ PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_GRAVITY, GLOBAL_DEF("physics/2d/default_gravity", 980.0));
PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_GRAVITY_VECTOR, GLOBAL_DEF("physics/2d/default_gravity_vector", Vector2(0, 1)));
PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_LINEAR_DAMP, GLOBAL_DEF("physics/2d/default_linear_damp", 0.1));
ProjectSettings::get_singleton()->set_custom_property_info("physics/2d/default_linear_damp", PropertyInfo(Variant::FLOAT, "physics/2d/default_linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"));
PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_ANGULAR_DAMP, GLOBAL_DEF("physics/2d/default_angular_damp", 1.0));
ProjectSettings::get_singleton()->set_custom_property_info("physics/2d/default_angular_damp", PropertyInfo(Variant::FLOAT, "physics/2d/default_angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"));
- indexer = memnew(SpatialIndexer2D);
+
+ // Create and configure the navigation_map to be more friendly with pixels than meters.
+ navigation_map = NavigationServer2D::get_singleton()->map_create();
+ NavigationServer2D::get_singleton()->map_set_active(navigation_map, true);
+ NavigationServer2D::get_singleton()->map_set_cell_size(navigation_map, GLOBAL_DEF("navigation/2d/default_cell_size", 10));
+ NavigationServer2D::get_singleton()->map_set_edge_connection_margin(navigation_map, GLOBAL_DEF("navigation/2d/default_edge_connection_margin", 5));
}
World2D::~World2D() {
RenderingServer::get_singleton()->free(canvas);
PhysicsServer2D::get_singleton()->free(space);
- memdelete(indexer);
+ NavigationServer2D::get_singleton()->free(navigation_map);
}
diff --git a/scene/resources/world_2d.h b/scene/resources/world_2d.h
index c330719104..65f89c8f64 100644
--- a/scene/resources/world_2d.h
+++ b/scene/resources/world_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,11 +31,11 @@
#ifndef WORLD_2D_H
#define WORLD_2D_H
-#include "core/project_settings.h"
-#include "core/resource.h"
+#include "core/config/project_settings.h"
+#include "core/io/resource.h"
#include "servers/physics_server_2d.h"
-class VisibilityNotifier2D;
+class VisibleOnScreenNotifier2D;
class Viewport;
struct SpatialIndexer2D;
@@ -44,31 +44,25 @@ class World2D : public Resource {
RID canvas;
RID space;
+ RID navigation_map;
- SpatialIndexer2D *indexer;
+ Set<Viewport *> viewports;
protected:
static void _bind_methods();
friend class Viewport;
- friend class VisibilityNotifier2D;
- void _register_viewport(Viewport *p_viewport, const Rect2 &p_rect);
- void _update_viewport(Viewport *p_viewport, const Rect2 &p_rect);
+ void _register_viewport(Viewport *p_viewport);
void _remove_viewport(Viewport *p_viewport);
- void _register_notifier(VisibilityNotifier2D *p_notifier, const Rect2 &p_rect);
- void _update_notifier(VisibilityNotifier2D *p_notifier, const Rect2 &p_rect);
- void _remove_notifier(VisibilityNotifier2D *p_notifier);
-
- void _update();
-
public:
- RID get_canvas();
- RID get_space();
+ RID get_canvas() const;
+ RID get_space() const;
+ RID get_navigation_map() const;
PhysicsDirectSpaceState2D *get_direct_space_state();
- void get_viewport_list(List<Viewport *> *r_viewports);
+ _FORCE_INLINE_ const Set<Viewport *> &get_viewports() { return viewports; }
World2D();
~World2D();
diff --git a/scene/resources/world_3d.cpp b/scene/resources/world_3d.cpp
index 8100f150ef..42047f104f 100644
--- a/scene/resources/world_3d.cpp
+++ b/scene/resources/world_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,209 +33,19 @@
#include "core/math/camera_matrix.h"
#include "core/math/octree.h"
#include "scene/3d/camera_3d.h"
-#include "scene/3d/visibility_notifier_3d.h"
+#include "scene/3d/visible_on_screen_notifier_3d.h"
#include "scene/scene_string_names.h"
-
-struct SpatialIndexer {
- Octree<VisibilityNotifier3D> octree;
-
- struct NotifierData {
- AABB aabb;
- OctreeElementID id;
- };
-
- Map<VisibilityNotifier3D *, NotifierData> notifiers;
- struct CameraData {
- Map<VisibilityNotifier3D *, uint64_t> notifiers;
- };
-
- Map<Camera3D *, CameraData> cameras;
-
- enum {
- VISIBILITY_CULL_MAX = 32768
- };
-
- Vector<VisibilityNotifier3D *> cull;
-
- bool changed;
- uint64_t pass;
- uint64_t last_frame;
-
- void _notifier_add(VisibilityNotifier3D *p_notifier, const AABB &p_rect) {
- ERR_FAIL_COND(notifiers.has(p_notifier));
- notifiers[p_notifier].aabb = p_rect;
- notifiers[p_notifier].id = octree.create(p_notifier, p_rect);
- changed = true;
- }
-
- void _notifier_update(VisibilityNotifier3D *p_notifier, const AABB &p_rect) {
- Map<VisibilityNotifier3D *, NotifierData>::Element *E = notifiers.find(p_notifier);
- ERR_FAIL_COND(!E);
- if (E->get().aabb == p_rect) {
- return;
- }
-
- E->get().aabb = p_rect;
- octree.move(E->get().id, E->get().aabb);
- changed = true;
- }
-
- void _notifier_remove(VisibilityNotifier3D *p_notifier) {
- Map<VisibilityNotifier3D *, NotifierData>::Element *E = notifiers.find(p_notifier);
- ERR_FAIL_COND(!E);
-
- octree.erase(E->get().id);
- notifiers.erase(p_notifier);
-
- List<Camera3D *> removed;
- for (Map<Camera3D *, CameraData>::Element *F = cameras.front(); F; F = F->next()) {
- Map<VisibilityNotifier3D *, uint64_t>::Element *G = F->get().notifiers.find(p_notifier);
-
- if (G) {
- F->get().notifiers.erase(G);
- removed.push_back(F->key());
- }
- }
-
- while (!removed.empty()) {
- p_notifier->_exit_camera(removed.front()->get());
- removed.pop_front();
- }
-
- changed = true;
- }
-
- void _add_camera(Camera3D *p_camera) {
- ERR_FAIL_COND(cameras.has(p_camera));
- CameraData vd;
- cameras[p_camera] = vd;
- changed = true;
- }
-
- void _update_camera(Camera3D *p_camera) {
- Map<Camera3D *, CameraData>::Element *E = cameras.find(p_camera);
- ERR_FAIL_COND(!E);
- changed = true;
- }
-
- void _remove_camera(Camera3D *p_camera) {
- ERR_FAIL_COND(!cameras.has(p_camera));
- List<VisibilityNotifier3D *> removed;
- for (Map<VisibilityNotifier3D *, uint64_t>::Element *E = cameras[p_camera].notifiers.front(); E; E = E->next()) {
- removed.push_back(E->key());
- }
-
- while (!removed.empty()) {
- removed.front()->get()->_exit_camera(p_camera);
- removed.pop_front();
- }
-
- cameras.erase(p_camera);
- }
-
- void _update(uint64_t p_frame) {
- if (p_frame == last_frame) {
- return;
- }
- last_frame = p_frame;
-
- if (!changed) {
- return;
- }
-
- for (Map<Camera3D *, CameraData>::Element *E = cameras.front(); E; E = E->next()) {
- pass++;
-
- Camera3D *c = E->key();
-
- Vector<Plane> planes = c->get_frustum();
-
- int culled = octree.cull_convex(planes, cull.ptrw(), cull.size());
-
- VisibilityNotifier3D **ptr = cull.ptrw();
-
- List<VisibilityNotifier3D *> added;
- List<VisibilityNotifier3D *> removed;
-
- for (int i = 0; i < culled; i++) {
- //notifiers in frustum
-
- Map<VisibilityNotifier3D *, uint64_t>::Element *H = E->get().notifiers.find(ptr[i]);
- if (!H) {
- E->get().notifiers.insert(ptr[i], pass);
- added.push_back(ptr[i]);
- } else {
- H->get() = pass;
- }
- }
-
- for (Map<VisibilityNotifier3D *, uint64_t>::Element *F = E->get().notifiers.front(); F; F = F->next()) {
- if (F->get() != pass) {
- removed.push_back(F->key());
- }
- }
-
- while (!added.empty()) {
- added.front()->get()->_enter_camera(E->key());
- added.pop_front();
- }
-
- while (!removed.empty()) {
- E->get().notifiers.erase(removed.front()->get());
- removed.front()->get()->_exit_camera(E->key());
- removed.pop_front();
- }
- }
- changed = false;
- }
-
- SpatialIndexer() {
- pass = 0;
- last_frame = 0;
- changed = false;
- cull.resize(VISIBILITY_CULL_MAX);
- }
-};
+#include "servers/navigation_server_3d.h"
void World3D::_register_camera(Camera3D *p_camera) {
#ifndef _3D_DISABLED
- indexer->_add_camera(p_camera);
-#endif
-}
-
-void World3D::_update_camera(Camera3D *p_camera) {
-#ifndef _3D_DISABLED
- indexer->_update_camera(p_camera);
+ cameras.insert(p_camera);
#endif
}
void World3D::_remove_camera(Camera3D *p_camera) {
#ifndef _3D_DISABLED
- indexer->_remove_camera(p_camera);
-#endif
-}
-
-void World3D::_register_notifier(VisibilityNotifier3D *p_notifier, const AABB &p_rect) {
-#ifndef _3D_DISABLED
- indexer->_notifier_add(p_notifier, p_rect);
-#endif
-}
-
-void World3D::_update_notifier(VisibilityNotifier3D *p_notifier, const AABB &p_rect) {
-#ifndef _3D_DISABLED
- indexer->_notifier_update(p_notifier, p_rect);
-#endif
-}
-
-void World3D::_remove_notifier(VisibilityNotifier3D *p_notifier) {
-#ifndef _3D_DISABLED
- indexer->_notifier_remove(p_notifier);
-#endif
-}
-
-void World3D::_update(uint64_t p_frame) {
-#ifndef _3D_DISABLED
- indexer->_update(p_frame);
+ cameras.erase(p_camera);
#endif
}
@@ -243,6 +53,10 @@ RID World3D::get_space() const {
return space;
}
+RID World3D::get_navigation_map() const {
+ return navigation_map;
+}
+
RID World3D::get_scenario() const {
return scenario;
}
@@ -302,28 +116,24 @@ PhysicsDirectSpaceState3D *World3D::get_direct_space_state() {
return PhysicsServer3D::get_singleton()->space_get_direct_state(space);
}
-void World3D::get_camera_list(List<Camera3D *> *r_cameras) {
- for (Map<Camera3D *, SpatialIndexer::CameraData>::Element *E = indexer->cameras.front(); E; E = E->next()) {
- r_cameras->push_back(E->key());
- }
-}
-
void World3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_space"), &World3D::get_space);
+ ClassDB::bind_method(D_METHOD("get_navigation_map"), &World3D::get_navigation_map);
ClassDB::bind_method(D_METHOD("get_scenario"), &World3D::get_scenario);
ClassDB::bind_method(D_METHOD("set_environment", "env"), &World3D::set_environment);
ClassDB::bind_method(D_METHOD("get_environment"), &World3D::get_environment);
ClassDB::bind_method(D_METHOD("set_fallback_environment", "env"), &World3D::set_fallback_environment);
ClassDB::bind_method(D_METHOD("get_fallback_environment"), &World3D::get_fallback_environment);
- ClassDB::bind_method(D_METHOD("set_camera_effects", "env"), &World3D::set_camera_effects);
+ ClassDB::bind_method(D_METHOD("set_camera_effects", "effects"), &World3D::set_camera_effects);
ClassDB::bind_method(D_METHOD("get_camera_effects"), &World3D::get_camera_effects);
ClassDB::bind_method(D_METHOD("get_direct_space_state"), &World3D::get_direct_space_state);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_environment", "get_environment");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "fallback_environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_fallback_environment", "get_fallback_environment");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "camera_effects", PROPERTY_HINT_RESOURCE_TYPE, "CameraEffects"), "set_camera_effects", "get_camera_effects");
- ADD_PROPERTY(PropertyInfo(Variant::_RID, "space", PROPERTY_HINT_NONE, "", 0), "", "get_space");
- ADD_PROPERTY(PropertyInfo(Variant::_RID, "scenario", PROPERTY_HINT_NONE, "", 0), "", "get_scenario");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "direct_space_state", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsDirectSpaceState3D", 0), "", "get_direct_space_state");
+ ADD_PROPERTY(PropertyInfo(Variant::RID, "space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_space");
+ ADD_PROPERTY(PropertyInfo(Variant::RID, "navigation_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_navigation_map");
+ ADD_PROPERTY(PropertyInfo(Variant::RID, "scenario", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_scenario");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "direct_space_state", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsDirectSpaceState3D", PROPERTY_USAGE_NONE), "", "get_direct_space_state");
}
World3D::World3D() {
@@ -338,18 +148,14 @@ World3D::World3D() {
PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_ANGULAR_DAMP, GLOBAL_DEF("physics/3d/default_angular_damp", 0.1));
ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/default_angular_damp", PropertyInfo(Variant::FLOAT, "physics/3d/default_angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"));
-#ifdef _3D_DISABLED
- indexer = nullptr;
-#else
- indexer = memnew(SpatialIndexer);
-#endif
+ navigation_map = NavigationServer3D::get_singleton()->map_create();
+ NavigationServer3D::get_singleton()->map_set_active(navigation_map, true);
+ NavigationServer3D::get_singleton()->map_set_cell_size(navigation_map, GLOBAL_DEF("navigation/3d/default_cell_size", 0.3));
+ NavigationServer3D::get_singleton()->map_set_edge_connection_margin(navigation_map, GLOBAL_DEF("navigation/3d/default_edge_connection_margin", 0.3));
}
World3D::~World3D() {
PhysicsServer3D::get_singleton()->free(space);
RenderingServer::get_singleton()->free(scenario);
-
-#ifndef _3D_DISABLED
- memdelete(indexer);
-#endif
+ NavigationServer3D::get_singleton()->free(navigation_map);
}
diff --git a/scene/resources/world_3d.h b/scene/resources/world_3d.h
index 02a821637f..2c5be35609 100644
--- a/scene/resources/world_3d.h
+++ b/scene/resources/world_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,14 +31,14 @@
#ifndef WORLD_3D_H
#define WORLD_3D_H
-#include "core/resource.h"
+#include "core/io/resource.h"
#include "scene/resources/camera_effects.h"
#include "scene/resources/environment.h"
#include "servers/physics_server_3d.h"
#include "servers/rendering_server.h"
class Camera3D;
-class VisibilityNotifier3D;
+class VisibleOnScreenNotifier3D;
struct SpatialIndexer;
class World3D : public Resource {
@@ -46,30 +46,26 @@ class World3D : public Resource {
private:
RID space;
+ RID navigation_map;
RID scenario;
- SpatialIndexer *indexer;
+
Ref<Environment> environment;
Ref<Environment> fallback_environment;
Ref<CameraEffects> camera_effects;
+ Set<Camera3D *> cameras;
+
protected:
static void _bind_methods();
friend class Camera3D;
- friend class VisibilityNotifier3D;
void _register_camera(Camera3D *p_camera);
- void _update_camera(Camera3D *p_camera);
void _remove_camera(Camera3D *p_camera);
- void _register_notifier(VisibilityNotifier3D *p_notifier, const AABB &p_rect);
- void _update_notifier(VisibilityNotifier3D *p_notifier, const AABB &p_rect);
- void _remove_notifier(VisibilityNotifier3D *p_notifier);
- friend class Viewport;
- void _update(uint64_t p_frame);
-
public:
RID get_space() const;
+ RID get_navigation_map() const;
RID get_scenario() const;
void set_environment(const Ref<Environment> &p_environment);
@@ -81,7 +77,7 @@ public:
void set_camera_effects(const Ref<CameraEffects> &p_camera_effects);
Ref<CameraEffects> get_camera_effects() const;
- void get_camera_list(List<Camera3D *> *r_cameras);
+ _FORCE_INLINE_ const Set<Camera3D *> &get_cameras() const { return cameras; }
PhysicsDirectSpaceState3D *get_direct_space_state();
diff --git a/scene/resources/line_shape_2d.cpp b/scene/resources/world_margin_shape_2d.cpp
index 58653c5f4a..3b43681528 100644
--- a/scene/resources/line_shape_2d.cpp
+++ b/scene/resources/world_margin_shape_2d.cpp
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* line_shape_2d.cpp */
+/* world_margin_shape_2d.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,15 +28,15 @@
/* 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().tangent() * 100, point + get_normal().tangent() * 100 }, { point, point + get_normal() * 30 } };
+ Vector2 l[2][2] = { { point - get_normal().orthogonal() * 100, point + get_normal().orthogonal() * 100 }, { point, point + get_normal() * 30 } };
for (int i = 0; i < 2; i++) {
Vector2 closest = Geometry2D::get_closest_point_to_segment(p_point, l[i]);
@@ -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,37 +56,37 @@ 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().tangent() * 100, point + get_normal().tangent() * 100 };
+ Vector2 l1[2] = { point - get_normal().orthogonal() * 100, point + get_normal().orthogonal() * 100 };
RS::get_singleton()->canvas_item_add_line(p_to_rid, l1[0], l1[1], p_color, 3);
Vector2 l2[2] = { point, point + get_normal() * 30 };
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().tangent() * 100, point + get_normal().tangent() * 100 };
+ Vector2 l1[2] = { point - get_normal().orthogonal() * 100, point + get_normal().orthogonal() * 100 };
Vector2 l2[2] = { point, point + get_normal() * 30 };
Rect2 rect;
rect.position = l1[0];
@@ -96,24 +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()) {
- normal = Vector2(0, 1);
- distance = 0;
+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 7e67a8f67c..3c1d593ffe 100644
--- a/scene/resources/line_shape_2d.h
+++ b/scene/resources/world_margin_shape_2d.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* line_shape_2d.h */
+/* world_margin_shape_2d.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,16 +28,17 @@
/* 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);
- Vector2 normal;
- real_t distance;
+ // 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;
void _update_shape();
@@ -57,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
diff --git a/scene/resources/world_margin_shape_3d.cpp b/scene/resources/world_margin_shape_3d.cpp
index 0936fcc657..28d50e1921 100644
--- a/scene/resources/world_margin_shape_3d.cpp
+++ b/scene/resources/world_margin_shape_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -69,7 +69,6 @@ void WorldMarginShape3D::set_plane(Plane p_plane) {
plane = p_plane;
_update_shape();
notify_change_to_owners();
- _change_notify("plane");
}
Plane WorldMarginShape3D::get_plane() const {
diff --git a/scene/resources/world_margin_shape_3d.h b/scene/resources/world_margin_shape_3d.h
index 8099592d80..00417c4408 100644
--- a/scene/resources/world_margin_shape_3d.h
+++ b/scene/resources/world_margin_shape_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */