summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/class_db.cpp14
-rw-r--r--core/class_db.h3
-rw-r--r--core/image.cpp2
-rw-r--r--core/os/input_event.cpp39
-rw-r--r--core/os/input_event.h4
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.cpp30
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.h6
-rw-r--r--drivers/gles3/shader_compiler_gles3.cpp1
-rw-r--r--drivers/gles3/shaders/particles.glsl9
-rw-r--r--editor/editor_node.cpp63
-rw-r--r--editor/plugins/curve_editor_plugin.cpp935
-rw-r--r--editor/plugins/curve_editor_plugin.h128
-rw-r--r--editor/plugins/shader_editor_plugin.cpp2
-rw-r--r--editor/plugins/texture_editor_plugin.cpp12
-rw-r--r--main/input_default.cpp2
-rw-r--r--modules/gdscript/gd_editor.cpp6
-rw-r--r--platform/windows/godot.icobin370070 -> 110755 bytes
-rw-r--r--scene/2d/audio_stream_player_2d.cpp12
-rw-r--r--scene/2d/canvas_item.cpp200
-rw-r--r--scene/2d/canvas_item.h92
-rw-r--r--scene/2d/particles_2d.cpp2
-rw-r--r--scene/3d/mesh_instance.cpp2
-rw-r--r--scene/3d/particles.cpp26
-rw-r--r--scene/3d/visual_instance.cpp2
-rw-r--r--scene/gui/input_action.cpp2
-rw-r--r--scene/register_scene_types.cpp5
-rw-r--r--scene/resources/curve.cpp359
-rw-r--r--scene/resources/curve.h72
-rw-r--r--scene/resources/mesh.cpp11
-rw-r--r--scene/resources/mesh.h1
-rw-r--r--scene/resources/texture.cpp242
-rw-r--r--scene/resources/texture.h27
-rw-r--r--servers/visual/shader_types.cpp1
-rw-r--r--servers/visual/visual_server_canvas.cpp12
-rw-r--r--servers/visual/visual_server_canvas.h8
35 files changed, 1631 insertions, 701 deletions
diff --git a/core/class_db.cpp b/core/class_db.cpp
index 1fe02c8cd9..6b8c290a99 100644
--- a/core/class_db.cpp
+++ b/core/class_db.cpp
@@ -497,7 +497,7 @@ void ClassDB::_add_class2(const StringName &p_class, const StringName &p_inherit
}
}
-void ClassDB::get_method_list(StringName p_class, List<MethodInfo> *p_methods, bool p_no_inheritance) {
+void ClassDB::get_method_list(StringName p_class, List<MethodInfo> *p_methods, bool p_no_inheritance, bool p_exclude_from_properties) {
OBJTYPE_RLOCK;
@@ -528,6 +528,9 @@ void ClassDB::get_method_list(StringName p_class, List<MethodInfo> *p_methods, b
minfo.name = E->get();
minfo.id = method->get_method_id();
+ if (p_exclude_from_properties && type->methods_in_properties.has(minfo.name))
+ continue;
+
for (int i = 0; i < method->get_argument_count(); i++) {
//Variant::Type t=method->get_argument_type(i);
@@ -802,7 +805,14 @@ void ClassDB::add_property(StringName p_class, const PropertyInfo &p_pinfo, cons
OBJTYPE_WLOCK
type->property_list.push_back(p_pinfo);
-
+#ifdef DEBUG_METHODS_ENABLED
+ if (mb_get) {
+ type->methods_in_properties.insert(p_getter);
+ }
+ if (mb_set) {
+ type->methods_in_properties.insert(p_setter);
+ }
+#endif
PropertySetGet psg;
psg.setter = p_setter;
psg.getter = p_getter;
diff --git a/core/class_db.h b/core/class_db.h
index 547068da5f..4f00a16e91 100644
--- a/core/class_db.h
+++ b/core/class_db.h
@@ -139,6 +139,7 @@ public:
#ifdef DEBUG_METHODS_ENABLED
List<StringName> constant_order;
List<StringName> method_order;
+ Set<StringName> methods_in_properties;
List<MethodInfo> virtual_methods;
StringName category;
#endif
@@ -486,7 +487,7 @@ public:
static bool has_method(StringName p_class, StringName p_method, bool p_no_inheritance = false);
static void set_method_flags(StringName p_class, StringName p_method, int p_flags);
- static void get_method_list(StringName p_class, List<MethodInfo> *p_methods, bool p_no_inheritance = false);
+ static void get_method_list(StringName p_class, List<MethodInfo> *p_methods, bool p_no_inheritance = false, bool p_exclude_from_properties = false);
static MethodBind *get_method(StringName p_class, StringName p_name);
static void add_virtual_method(const StringName &p_class, const MethodInfo &p_method, bool p_virtual = true);
diff --git a/core/image.cpp b/core/image.cpp
index ec21260b19..e0cf82d920 100644
--- a/core/image.cpp
+++ b/core/image.cpp
@@ -2167,7 +2167,7 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_mipmap_offset", "mipmap"), &Image::get_mipmap_offset);
- ClassDB::bind_method(D_METHOD("resize_to_po2", "square"), &Image::resize_to_po2, DEFVAL("false"));
+ ClassDB::bind_method(D_METHOD("resize_to_po2", "square"), &Image::resize_to_po2, DEFVAL(false));
ClassDB::bind_method(D_METHOD("resize", "width", "height", "interpolation"), &Image::resize, DEFVAL(INTERPOLATE_BILINEAR));
ClassDB::bind_method(D_METHOD("shrink_x2"), &Image::shrink_x2);
ClassDB::bind_method(D_METHOD("expand_x2_hq2x"), &Image::expand_x2_hq2x);
diff --git a/core/os/input_event.cpp b/core/os/input_event.cpp
index dbdf9628e3..e60f588be3 100644
--- a/core/os/input_event.cpp
+++ b/core/os/input_event.cpp
@@ -89,6 +89,11 @@ bool InputEvent::action_match(const Ref<InputEvent> &p_event) const {
return false;
}
+bool InputEvent::shortcut_match(const Ref<InputEvent> &p_event) const {
+
+ return false;
+}
+
bool InputEvent::is_action_type() const {
return false;
@@ -130,6 +135,7 @@ void InputEvent::_bind_methods() {
ClassDB::bind_method(D_METHOD("as_text"), &InputEvent::as_text);
ClassDB::bind_method(D_METHOD("action_match", "event:InputEvent"), &InputEvent::action_match);
+ ClassDB::bind_method(D_METHOD("shortcut_match", "event:InputEvent"), &InputEvent::shortcut_match);
ClassDB::bind_method(D_METHOD("is_action_type"), &InputEvent::is_action_type);
@@ -276,6 +282,27 @@ uint32_t InputEventKey::get_scancode_with_modifiers() const {
return sc;
}
+String InputEventKey::as_text() const {
+
+ String kc = keycode_get_string(scancode);
+ if (kc == String())
+ return kc;
+
+ if (get_metakey()) {
+ kc = "Meta+" + kc;
+ }
+ if (get_alt()) {
+ kc = "Alt+" + kc;
+ }
+ if (get_shift()) {
+ kc = "Shift+" + kc;
+ }
+ if (get_control()) {
+ kc = "Ctrl+" + kc;
+ }
+ return kc;
+}
+
bool InputEventKey::action_match(const Ref<InputEvent> &p_event) const {
Ref<InputEventKey> key = p_event;
@@ -288,6 +315,18 @@ bool InputEventKey::action_match(const Ref<InputEvent> &p_event) const {
return get_scancode() == key->get_scancode() && (!key->is_pressed() || (code & event_code) == code);
}
+bool InputEventKey::shortcut_match(const Ref<InputEvent> &p_event) const {
+
+ Ref<InputEventKey> key = p_event;
+ if (key.is_null())
+ return false;
+
+ uint32_t code = get_scancode_with_modifiers();
+ uint32_t event_code = key->get_scancode_with_modifiers();
+
+ return code == event_code;
+}
+
void InputEventKey::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_pressed", "pressed"), &InputEventKey::set_pressed);
diff --git a/core/os/input_event.h b/core/os/input_event.h
index 6a694df345..b120d4b840 100644
--- a/core/os/input_event.h
+++ b/core/os/input_event.h
@@ -165,6 +165,7 @@ public:
virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const;
virtual bool action_match(const Ref<InputEvent> &p_event) const;
+ virtual bool shortcut_match(const Ref<InputEvent> &p_event) const;
virtual bool is_action_type() const;
InputEvent();
@@ -243,9 +244,12 @@ public:
uint32_t get_scancode_with_modifiers() const;
virtual bool action_match(const Ref<InputEvent> &p_event) const;
+ virtual bool shortcut_match(const Ref<InputEvent> &p_event) const;
virtual bool is_action_type() const { return true; }
+ virtual String as_text() const;
+
InputEventKey();
};
diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp
index 81baf542c0..f7e1fdee9d 100644
--- a/drivers/gles3/rasterizer_storage_gles3.cpp
+++ b/drivers/gles3/rasterizer_storage_gles3.cpp
@@ -5288,6 +5288,7 @@ void RasterizerStorageGLES3::_particles_process(Particles *particles, float p_de
if (particles->clear) {
particles->cycle_number = 0;
+ particles->random_seed = Math::rand();
} else if (new_phase < particles->phase) {
particles->cycle_number++;
}
@@ -5298,6 +5299,8 @@ void RasterizerStorageGLES3::_particles_process(Particles *particles, float p_de
shaders.particles.set_uniform(ParticlesShaderGLES3::DELTA, p_delta * particles->speed_scale);
shaders.particles.set_uniform(ParticlesShaderGLES3::CLEAR, particles->clear);
+ glUniform1ui(shaders.particles.get_uniform_location(ParticlesShaderGLES3::RANDOM_SEED), particles->random_seed);
+
if (particles->use_local_coords)
shaders.particles.set_uniform(ParticlesShaderGLES3::EMISSION_TRANSFORM, Transform());
else
@@ -5353,6 +5356,33 @@ void RasterizerStorageGLES3::update_particles() {
Particles *particles = particle_update_list.first()->self();
+ if (particles->inactive && !particles->emitting) {
+
+ particle_update_list.remove(particle_update_list.first());
+ continue;
+ }
+
+ if (particles->emitting) {
+ if (particles->inactive) {
+ //restart system from scratch
+ particles->prev_ticks = 0;
+ particles->phase = 0;
+ particles->prev_phase = 0;
+ particles->clear = true;
+ particles->particle_valid_histories[0] = false;
+ particles->particle_valid_histories[1] = false;
+ }
+ particles->inactive = false;
+ particles->inactive_time = 0;
+ } else {
+ particles->inactive_time += particles->speed_scale * frame.delta;
+ if (particles->inactive_time > particles->lifetime * 1.2) {
+ particles->inactive = true;
+ particle_update_list.remove(particle_update_list.first());
+ continue;
+ }
+ }
+
Material *material = material_owner.getornull(particles->process_material);
if (!material || !material->shader || material->shader->mode != VS::SHADER_PARTICLES) {
diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h
index ca0194bd5e..65026a16ec 100644
--- a/drivers/gles3/rasterizer_storage_gles3.h
+++ b/drivers/gles3/rasterizer_storage_gles3.h
@@ -1033,6 +1033,8 @@ public:
struct Particles : public GeometryOwner {
+ bool inactive;
+ float inactive_time;
bool emitting;
int amount;
float lifetime;
@@ -1060,6 +1062,7 @@ public:
float phase;
float prev_phase;
uint64_t prev_ticks;
+ uint32_t random_seed;
uint32_t cycle_number;
@@ -1088,6 +1091,7 @@ public:
frame_remainder = 0;
histories_enabled = false;
speed_scale = 1.0;
+ random_seed = 0;
custom_aabb = Rect3(Vector3(-4, -4, -4), Vector3(8, 8, 8));
@@ -1098,6 +1102,8 @@ public:
prev_ticks = 0;
clear = true;
+ inactive = true;
+ inactive_time = false;
glGenBuffers(2, particle_buffers);
glGenVertexArrays(2, particle_vaos);
diff --git a/drivers/gles3/shader_compiler_gles3.cpp b/drivers/gles3/shader_compiler_gles3.cpp
index 3376f99112..41421a3e2f 100644
--- a/drivers/gles3/shader_compiler_gles3.cpp
+++ b/drivers/gles3/shader_compiler_gles3.cpp
@@ -795,6 +795,7 @@ ShaderCompilerGLES3::ShaderCompilerGLES3() {
actions[VS::SHADER_PARTICLES].renames["INDEX"] = "index";
actions[VS::SHADER_PARTICLES].renames["GRAVITY"] = "current_gravity";
actions[VS::SHADER_PARTICLES].renames["EMISSION_TRANSFORM"] = "emission_transform";
+ actions[VS::SHADER_PARTICLES].renames["RANDOM_SEED"] = "random_seed";
actions[VS::SHADER_SPATIAL].render_mode_defines["disable_force"] = "#define DISABLE_FORCE\n";
actions[VS::SHADER_SPATIAL].render_mode_defines["disable_velocity"] = "#define DISABLE_VELOCITY\n";
diff --git a/drivers/gles3/shaders/particles.glsl b/drivers/gles3/shaders/particles.glsl
index 7e7b083f73..ec2577538c 100644
--- a/drivers/gles3/shaders/particles.glsl
+++ b/drivers/gles3/shaders/particles.glsl
@@ -37,6 +37,7 @@ uniform bool clear;
uniform uint cycle;
uniform float lifetime;
uniform mat4 emission_transform;
+uniform uint random_seed;
out highp vec4 out_color; //tfb:
@@ -104,7 +105,9 @@ void main() {
bool shader_active = velocity_active.a > 0.5;
if (system_phase > prev_system_phase) {
- if (prev_system_phase < restart_phase && system_phase >= restart_phase) {
+ // restart_phase >= prev_system_phase is used so particles emit in the first frame they are processed
+
+ if (restart_phase >= prev_system_phase && restart_phase < system_phase ) {
restart=true;
#ifdef USE_FRACTIONAL_DELTA
local_delta = (system_phase - restart_phase) * lifetime;
@@ -112,12 +115,12 @@ void main() {
}
} else {
- if (prev_system_phase < restart_phase) {
+ if (restart_phase >= prev_system_phase) {
restart=true;
#ifdef USE_FRACTIONAL_DELTA
local_delta = (1.0 - restart_phase + system_phase) * lifetime;
#endif
- } else if (system_phase >= restart_phase) {
+ } else if (restart_phase < system_phase ) {
restart=true;
#ifdef USE_FRACTIONAL_DELTA
local_delta = (system_phase - restart_phase) * lifetime;
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 56b62cdf6e..2b29e4b08a 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -5311,36 +5311,6 @@ EditorNode::EditorNode() {
top_region->add_child(left_menu_hb);
menu_hb->add_child(top_region);
- PopupMenu *p;
-
- project_menu = memnew(MenuButton);
- project_menu->set_tooltip(TTR("Miscellaneous project or scene-wide tools."));
- project_menu->set_text(TTR("Project"));
- project_menu->add_style_override("hover", gui_base->get_stylebox("MenuHover", "EditorStyles"));
- left_menu_hb->add_child(project_menu);
-
- p = project_menu->get_popup();
- p->connect("id_pressed", this, "_menu_option");
- p->add_item(TTR("Run Script"), FILE_RUN_SCRIPT, KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_R);
- p->add_item(TTR("Export"), FILE_EXPORT_PROJECT);
-
- PopupMenu *tool_menu = memnew(PopupMenu);
- tool_menu->set_name("Tools");
- tool_menu->connect("id_pressed", this, "_menu_option");
- p->add_child(tool_menu);
- p->add_submenu_item(TTR("Tools"), "Tools");
- tool_menu->add_item(TTR("Orphan Resource Explorer"), TOOLS_ORPHAN_RESOURCES);
- p->add_separator();
- p->add_item(TTR("Project Settings"), RUN_SETTINGS);
- p->add_separator();
-
-#ifdef OSX_ENABLED
- p->add_item(TTR("Quit to Project List"), RUN_PROJECT_MANAGER, KEY_MASK_SHIFT + KEY_MASK_ALT + KEY_Q);
-#else
- p->add_item(TTR("Quit to Project List"), RUN_PROJECT_MANAGER, KEY_MASK_SHIFT + KEY_MASK_CTRL + KEY_Q);
-#endif
- p->add_item(TTR("Quit"), FILE_QUIT, KEY_MASK_CMD + KEY_Q);
-
file_menu = memnew(MenuButton);
file_menu->set_text(TTR("Scene"));
//file_menu->set_icon(gui_base->get_icon("Save","EditorIcons"));
@@ -5360,6 +5330,7 @@ EditorNode::EditorNode() {
ED_SHORTCUT("editor/next_tab", TTR("Next tab"), KEY_MASK_CMD + KEY_TAB);
ED_SHORTCUT("editor/prev_tab", TTR("Previous tab"), KEY_MASK_CMD + KEY_MASK_SHIFT + KEY_TAB);
ED_SHORTCUT("editor/filter_files", TTR("Filter Files.."), KEY_MASK_ALT + KEY_MASK_CMD + KEY_P);
+ PopupMenu *p;
file_menu->set_tooltip(TTR("Operations with scene files."));
p = file_menu->get_popup();
@@ -5379,7 +5350,6 @@ EditorNode::EditorNode() {
p->add_shortcut(ED_SHORTCUT("editor/quick_open_scene", TTR("Quick Open Scene.."), KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_O), FILE_QUICK_OPEN_SCENE);
p->add_shortcut(ED_SHORTCUT("editor/quick_open_script", TTR("Quick Open Script.."), KEY_MASK_ALT + KEY_MASK_CMD + KEY_O), FILE_QUICK_OPEN_SCRIPT);
p->add_separator();
-
PopupMenu *pm_export = memnew(PopupMenu);
pm_export->set_name("Export");
p->add_child(pm_export);
@@ -5405,6 +5375,35 @@ EditorNode::EditorNode() {
sp->set_custom_minimum_size(Size2(30, 0) * EDSCALE);
menu_hb->add_child(sp);
}
+ p->add_separator();
+ p->add_item(TTR("Quit"), FILE_QUIT, KEY_MASK_CMD + KEY_Q);
+
+ project_menu = memnew(MenuButton);
+ project_menu->set_tooltip(TTR("Miscellaneous project or scene-wide tools."));
+ project_menu->set_text(TTR("Project"));
+ project_menu->add_style_override("hover", gui_base->get_stylebox("MenuHover", "EditorStyles"));
+ left_menu_hb->add_child(project_menu);
+
+ p = project_menu->get_popup();
+ p->add_item(TTR("Project Settings"), RUN_SETTINGS);
+ p->add_separator();
+ p->connect("id_pressed", this, "_menu_option");
+ p->add_item(TTR("Run Script"), FILE_RUN_SCRIPT, KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_R);
+ p->add_item(TTR("Export"), FILE_EXPORT_PROJECT);
+
+ PopupMenu *tool_menu = memnew(PopupMenu);
+ tool_menu->set_name("Tools");
+ tool_menu->connect("id_pressed", this, "_menu_option");
+ p->add_child(tool_menu);
+ p->add_submenu_item(TTR("Tools"), "Tools");
+ tool_menu->add_item(TTR("Orphan Resource Explorer"), TOOLS_ORPHAN_RESOURCES);
+ p->add_separator();
+
+#ifdef OSX_ENABLED
+ p->add_item(TTR("Quit to Project List"), RUN_PROJECT_MANAGER, KEY_MASK_SHIFT + KEY_MASK_ALT + KEY_Q);
+#else
+ p->add_item(TTR("Quit to Project List"), RUN_PROJECT_MANAGER, KEY_MASK_SHIFT + KEY_MASK_CTRL + KEY_Q);
+#endif
PanelContainer *editor_region = memnew(PanelContainer);
main_editor_button_vb = memnew(HBoxContainer);
@@ -6115,7 +6114,7 @@ EditorNode::EditorNode() {
add_editor_plugin(memnew(GradientEditorPlugin(this)));
add_editor_plugin(memnew(GradientTextureEditorPlugin(this)));
add_editor_plugin(memnew(CollisionShape2DEditorPlugin(this)));
- add_editor_plugin(memnew(CurveTextureEditorPlugin(this)));
+ add_editor_plugin(memnew(CurveEditorPlugin(this)));
add_editor_plugin(memnew(TextureEditorPlugin(this)));
add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor)));
//add_editor_plugin( memnew( MaterialEditorPlugin(this) ) );
diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp
index d869d703f1..50a625ddc1 100644
--- a/editor/plugins/curve_editor_plugin.cpp
+++ b/editor/plugins/curve_editor_plugin.cpp
@@ -27,528 +27,695 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+
#include "curve_editor_plugin.h"
#include "canvas_item_editor_plugin.h"
+#include "core_string_names.h"
+#include "os/input.h"
#include "os/keyboard.h"
-#include "spatial_editor_plugin.h"
-void CurveTextureEdit::_gui_input(const Ref<InputEvent> &p_event) {
+CurveEditor::CurveEditor() {
+ _selected_point = -1;
+ _hover_point = -1;
+ _selected_tangent = TANGENT_NONE;
+ _hover_radius = 6;
+ _tangents_length = 40;
+ _dragging = false;
+ _has_undo_data = false;
+ _world_rect = Rect2(0, 0, 1, 1);
- Ref<InputEventKey> k = p_event;
- if (k.is_valid() && k->is_pressed() && k->get_scancode() == KEY_DELETE && grabbed != -1) {
+ set_focus_mode(FOCUS_ALL);
+ set_clip_contents(true);
+
+ _context_menu = memnew(PopupMenu);
+ _context_menu->connect("id_pressed", this, "_on_context_menu_item_selected");
+ add_child(_context_menu);
+
+ _presets_menu = memnew(PopupMenu);
+ _presets_menu->set_name("_presets_menu");
+ _presets_menu->add_item("Flat0", PRESET_FLAT0);
+ _presets_menu->add_item("Flat1", PRESET_FLAT1);
+ _presets_menu->add_item("Linear", PRESET_LINEAR);
+ _presets_menu->add_item("Ease in", PRESET_EASE_IN);
+ _presets_menu->add_item("Ease out", PRESET_EASE_OUT);
+ _presets_menu->add_item("Smoothstep", PRESET_SMOOTHSTEP);
+ _presets_menu->connect("id_pressed", this, "_on_preset_item_selected");
+ _context_menu->add_child(_presets_menu);
+}
- points.remove(grabbed);
- grabbed = -1;
- update();
- emit_signal("curve_changed");
- accept_event();
+void CurveEditor::set_curve(Ref<Curve> curve) {
+
+ if (curve == _curve_ref)
+ return;
+
+ if (_curve_ref.is_valid()) {
+ _curve_ref->disconnect("changed", this, "_curve_changed");
+ }
+ _curve_ref = curve;
+ if (_curve_ref.is_valid()) {
+ _curve_ref->connect("changed", this, "_curve_changed");
}
- Ref<InputEventMouseButton> mb = p_event;
+ _selected_point = -1;
+ _hover_point = -1;
+ _selected_tangent = TANGENT_NONE;
- if (mb.is_valid() && mb->get_button_index() == 1 && mb->is_pressed()) {
+ update();
- update();
- Ref<Font> font = get_font("font", "Label");
+ // Note: if you edit a curve, then set another, and try to undo,
+ // it will normally apply on the previous curve, but you won't see it
+}
+
+Size2 CurveEditor::get_minimum_size() const {
+ return Vector2(64, 64);
+}
+
+void CurveEditor::_notification(int p_what) {
+ if (p_what == NOTIFICATION_DRAW)
+ _draw();
+}
+
+void CurveEditor::on_gui_input(const Ref<InputEvent> &p_event) {
+
+ Ref<InputEventMouseButton> mb_ref = p_event;
+ if (mb_ref.is_valid()) {
+
+ const InputEventMouseButton &mb = **mb_ref;
- int font_h = font->get_height();
+ if (mb.is_pressed() && !_dragging) {
- Vector2 size = get_size();
- size.y -= font_h;
+ Vector2 mpos = mb.get_position();
- Point2 p = Vector2(mb->get_position().x, mb->get_position().y) / size;
- p.y = CLAMP(1.0 - p.y, 0, 1) * (max - min) + min;
- grabbed = -1;
- grabbing = true;
+ _selected_tangent = get_tangent_at(mpos);
+ if (_selected_tangent == TANGENT_NONE)
+ set_selected_point(get_point_at(mpos));
- for (int i = 0; i < points.size(); i++) {
+ switch (mb.get_button_index()) {
+ case BUTTON_RIGHT:
+ _context_click_pos = mpos;
+ open_context_menu(get_global_transform().xform(mpos));
+ break;
- Vector2 ps = p * get_size();
- Vector2 pt = Vector2(points[i].offset, points[i].height) * get_size();
- if (ps.distance_to(pt) < 4) {
- grabbed = i;
+ case BUTTON_MIDDLE:
+ remove_point(_hover_point);
+ break;
+
+ case BUTTON_LEFT:
+ _dragging = true;
+ break;
}
}
- //grab or select
- if (grabbed != -1) {
- return;
- }
- //insert
-
- Point np;
- np.offset = p.x;
- np.height = p.y;
-
- points.push_back(np);
- points.sort();
- for (int i = 0; i < points.size(); i++) {
- if (points[i].offset == p.x && points[i].height == p.y) {
- grabbed = i;
- break;
+ if (!mb.is_pressed() && _dragging && mb.get_button_index() == BUTTON_LEFT) {
+ _dragging = false;
+ if (_has_undo_data) {
+ push_undo(_undo_data);
+ _has_undo_data = false;
}
}
-
- emit_signal("curve_changed");
}
- if (mb.is_valid() && mb->get_button_index() == 1 && !mb->is_pressed()) {
+ Ref<InputEventMouseMotion> mm_ref = p_event;
+ if (mm_ref.is_valid()) {
- if (grabbing) {
- grabbing = false;
- emit_signal("curve_changed");
- }
- update();
- }
+ const InputEventMouseMotion &mm = **mm_ref;
- Ref<InputEventMouseMotion> mm = p_event;
+ Vector2 mpos = mm.get_position();
- if (mm.is_valid() && grabbing && grabbed != -1) {
+ if (_dragging && _curve_ref.is_valid()) {
+ if (_selected_point != -1) {
- Ref<Font> font = get_font("font", "Label");
- int font_h = font->get_height();
- Vector2 size = get_size();
- size.y -= font_h;
+ if (!_has_undo_data) {
+ // Save curve state before dragging points
+ _undo_data = _curve_ref->get_data();
+ _has_undo_data = true;
+ }
- Point2 p = mm->get_position() / size;
- p.y = CLAMP(1.0 - p.y, 0, 1) * (max - min) + min;
- p.x = CLAMP(p.x, 0.0, 1.0);
+ if (_selected_tangent == TANGENT_NONE) {
+ // Drag point
- bool valid = true;
+ Vector2 point_pos = get_world_pos(mpos);
- for (int i = 0; i < points.size(); i++) {
+ int i = _curve_ref->set_point_offset(_selected_point, point_pos.x);
+ // The index may change if the point is dragged across another one
+ set_hover_point_index(i);
+ set_selected_point(i);
- if (points[i].offset == p.x && points[i].height == p.y && i != grabbed) {
- valid = false;
- }
- }
+ // TODO Get rid of this clamp if zoom is implemented in this editor.
+ // This is to prevent the user from loosing a point out of view.
+ if (point_pos.y < 0.0)
+ point_pos.y = 0.0;
+ else if (point_pos.y > 1.0)
+ point_pos.y = 1.0;
+
+ _curve_ref->set_point_value(_selected_point, point_pos.y);
- if (!valid)
- return;
+ //auto_calculate_tangents(i);
- points[grabbed].offset = p.x;
- points[grabbed].height = p.y;
+ } else {
+ // Drag tangent
- points.sort();
- for (int i = 0; i < points.size(); i++) {
- if (points[i].offset == p.x && points[i].height == p.y) {
- grabbed = i;
- break;
+ Vector2 point_pos = _curve_ref->get_point_pos(_selected_point);
+ Vector2 control_pos = get_world_pos(mpos);
+
+ Vector2 dir = (control_pos - point_pos).normalized();
+
+ real_t tangent;
+ if (Math::abs(dir.x) > CMP_EPSILON)
+ tangent = dir.y / dir.x;
+ else
+ tangent = 9999 * (dir.y >= 0 ? 1 : -1);
+
+ bool link = !Input::get_singleton()->is_key_pressed(KEY_SHIFT);
+
+ if (_selected_tangent == TANGENT_LEFT) {
+ _curve_ref->set_point_left_tangent(_selected_point, tangent);
+ if (link && _selected_point != _curve_ref->get_point_count() - 1)
+ _curve_ref->set_point_right_tangent(_selected_point, tangent);
+ } else {
+ _curve_ref->set_point_right_tangent(_selected_point, tangent);
+ if (link && _selected_point != 0)
+ _curve_ref->set_point_left_tangent(_selected_point, tangent);
+ }
+ }
}
+
+ } else {
+ set_hover_point_index(get_point_at(mpos));
}
+ }
- emit_signal("curve_changed");
+ Ref<InputEventKey> key_ref = p_event;
+ if (key_ref.is_valid()) {
+ const InputEventKey &key = **key_ref;
- update();
+ if (key.is_pressed() && _selected_point != -1) {
+ if (key.get_scancode() == KEY_DELETE)
+ remove_point(_selected_point);
+ }
}
}
-void CurveTextureEdit::_plot_curve(const Vector2 &p_a, const Vector2 &p_b, const Vector2 &p_c, const Vector2 &p_d) {
+void CurveEditor::on_preset_item_selected(int preset_id) {
+ ERR_FAIL_COND(preset_id < 0 || preset_id >= PRESET_COUNT);
+ ERR_FAIL_COND(_curve_ref.is_null());
- Ref<Font> font = get_font("font", "Label");
-
- int font_h = font->get_height();
+ Curve &curve = **_curve_ref;
+ Array previous_data = curve.get_data();
- float geometry[4][4];
- float tmp1[4][4];
- float tmp2[4][4];
- float deltas[4][4];
- double x, dx, dx2, dx3;
- double y, dy, dy2, dy3;
- double d, d2, d3;
- int lastx, lasty;
- int newx, newy;
- int ntimes;
- int i, j;
+ curve.clear_points();
- int xmax = get_size().x;
- int ymax = get_size().y - font_h;
+ switch (preset_id) {
+ case PRESET_FLAT0:
+ curve.add_point(Vector2(0, 0));
+ curve.add_point(Vector2(1, 0));
+ break;
- int vsplits = 4;
+ case PRESET_FLAT1:
+ curve.add_point(Vector2(0, 1));
+ curve.add_point(Vector2(1, 1));
+ break;
- int zero_ofs = (1.0 - (0.0 - min) / (max - min)) * ymax;
+ case PRESET_LINEAR:
+ curve.add_point(Vector2(0, 0), 0, 1);
+ curve.add_point(Vector2(1, 1), 1, 0);
+ break;
- draw_line(Vector2(0, zero_ofs), Vector2(xmax, zero_ofs), Color(0.8, 0.8, 0.8, 0.15), 2.0);
+ case PRESET_EASE_IN:
+ curve.add_point(Vector2(0, 0));
+ curve.add_point(Vector2(1, 1), 1.4, 0);
+ break;
- for (int i = 0; i <= vsplits; i++) {
- float fofs = float(i) / vsplits;
- int yofs = fofs * ymax;
- draw_line(Vector2(xmax, yofs), Vector2(xmax - 4, yofs), Color(0.8, 0.8, 0.8, 0.8), 2.0);
+ case PRESET_EASE_OUT:
+ curve.add_point(Vector2(0, 0), 0, 1.4);
+ curve.add_point(Vector2(1, 1));
+ break;
- String text = rtos((1.0 - fofs) * (max - min) + min);
- int ppos = text.find(".");
- if (ppos != -1) {
- if (text.length() > ppos + 2)
- text = text.substr(0, ppos + 2);
- }
+ case PRESET_SMOOTHSTEP:
+ curve.add_point(Vector2(0, 0));
+ curve.add_point(Vector2(1, 1));
+ break;
- int size = font->get_string_size(text).x;
- int xofs = xmax - size - 4;
- yofs -= font_h / 2;
+ default:
+ break;
+ }
- if (yofs < 2) {
- yofs = 2;
- } else if (yofs + font_h > ymax - 2) {
- yofs = ymax - font_h - 2;
- }
+ push_undo(previous_data);
+}
- draw_string(font, Vector2(xofs, yofs + font->get_ascent()), text, Color(0.8, 0.8, 0.8, 1));
+void CurveEditor::_curve_changed() {
+ update();
+ // Point count can change in case of undo
+ if (_selected_point >= _curve_ref->get_point_count()) {
+ set_selected_point(-1);
}
+}
- /* construct the geometry matrix from the segment */
- for (i = 0; i < 4; i++) {
- geometry[i][2] = 0;
- geometry[i][3] = 0;
- }
+void CurveEditor::on_context_menu_item_selected(int action_id) {
+ switch (action_id) {
+ case CONTEXT_ADD_POINT:
+ add_point(_context_click_pos);
+ break;
- geometry[0][0] = (p_a[0] * xmax);
- geometry[1][0] = (p_b[0] * xmax);
- geometry[2][0] = (p_c[0] * xmax);
- geometry[3][0] = (p_d[0] * xmax);
-
- geometry[0][1] = ((p_a[1] - min) / (max - min) * ymax);
- geometry[1][1] = ((p_b[1] - min) / (max - min) * ymax);
- geometry[2][1] = ((p_c[1] - min) / (max - min) * ymax);
- geometry[3][1] = ((p_d[1] - min) / (max - min) * ymax);
-
- /* subdivide the curve ntimes (1000) times */
- ntimes = 4 * xmax;
- /* ntimes can be adjusted to give a finer or coarser curve */
- d = 1.0 / ntimes;
- d2 = d * d;
- d3 = d * d * d;
-
- /* construct a temporary matrix for determining the forward differencing deltas */
- tmp2[0][0] = 0;
- tmp2[0][1] = 0;
- tmp2[0][2] = 0;
- tmp2[0][3] = 1;
- tmp2[1][0] = d3;
- tmp2[1][1] = d2;
- tmp2[1][2] = d;
- tmp2[1][3] = 0;
- tmp2[2][0] = 6 * d3;
- tmp2[2][1] = 2 * d2;
- tmp2[2][2] = 0;
- tmp2[2][3] = 0;
- tmp2[3][0] = 6 * d3;
- tmp2[3][1] = 0;
- tmp2[3][2] = 0;
- tmp2[3][3] = 0;
-
- /* compose the basis and geometry matrices */
-
- static const float CR_basis[4][4] = {
- { -0.5, 1.5, -1.5, 0.5 },
- { 1.0, -2.5, 2.0, -0.5 },
- { -0.5, 0.0, 0.5, 0.0 },
- { 0.0, 1.0, 0.0, 0.0 },
- };
-
- for (i = 0; i < 4; i++) {
- for (j = 0; j < 4; j++) {
- tmp1[i][j] = (CR_basis[i][0] * geometry[0][j] +
- CR_basis[i][1] * geometry[1][j] +
- CR_basis[i][2] * geometry[2][j] +
- CR_basis[i][3] * geometry[3][j]);
- }
+ case CONTEXT_REMOVE_POINT:
+ remove_point(_selected_point);
+ break;
}
- /* compose the above results to get the deltas matrix */
-
- for (i = 0; i < 4; i++) {
- for (j = 0; j < 4; j++) {
- deltas[i][j] = (tmp2[i][0] * tmp1[0][j] +
- tmp2[i][1] * tmp1[1][j] +
- tmp2[i][2] * tmp1[2][j] +
- tmp2[i][3] * tmp1[3][j]);
+}
+
+void CurveEditor::open_context_menu(Vector2 pos) {
+ _context_menu->set_position(pos);
+
+ _context_menu->clear();
+
+ if (_curve_ref.is_valid()) {
+ _context_menu->add_item(TTR("Add point"), CONTEXT_ADD_POINT);
+ if (_selected_point >= 0) {
+ _context_menu->add_item(TTR("Remove point"), CONTEXT_REMOVE_POINT);
}
+ _context_menu->add_separator();
}
- /* extract the x deltas */
- x = deltas[0][0];
- dx = deltas[1][0];
- dx2 = deltas[2][0];
- dx3 = deltas[3][0];
+ _context_menu->add_submenu_item(TTR("Load preset"), _presets_menu->get_name());
- /* extract the y deltas */
- y = deltas[0][1];
- dy = deltas[1][1];
- dy2 = deltas[2][1];
- dy3 = deltas[3][1];
+ _context_menu->popup();
+}
- lastx = CLAMP(x, 0, xmax);
- lasty = CLAMP(y, 0, ymax);
+int CurveEditor::get_point_at(Vector2 pos) const {
+ if (_curve_ref.is_null())
+ return -1;
+ const Curve &curve = **_curve_ref;
- /* if (fix255)
- {
- cd->curve[cd->outline][lastx] = lasty;
- }
- else
- {
- cd->curve_ptr[cd->outline][lastx] = lasty;
- if(gb_debug) printf("bender_plot_curve xmax:%d ymax:%d\n", (int)xmax, (int)ymax);
+ const float r = _hover_radius * _hover_radius;
+
+ for (int i = 0; i < curve.get_point_count(); ++i) {
+ Vector2 p = get_view_pos(curve.get_point_pos(i));
+ if (p.distance_squared_to(pos) <= r) {
+ return i;
}
-*/
- /* loop over the curve */
- for (i = 0; i < ntimes; i++) {
- /* increment the x values */
- x += dx;
- dx += dx2;
- dx2 += dx3;
-
- /* increment the y values */
- y += dy;
- dy += dy2;
- dy2 += dy3;
-
- newx = CLAMP((Math::round(x)), 0, xmax);
- newy = CLAMP((Math::round(y)), 0, ymax);
-
- /* if this point is different than the last one...then draw it */
- if ((lastx != newx) || (lasty != newy)) {
-#if 0
- if(fix255)
- {
- /* use fixed array size (for the curve graph) */
- cd->curve[cd->outline][newx] = newy;
- }
- else
- {
- /* use dynamic allocated curve_ptr (for the real curve) */
- cd->curve_ptr[cd->outline][newx] = newy;
+ }
- if(gb_debug) printf("outline: %d cX: %d cY: %d\n", (int)cd->outline, (int)newx, (int)newy);
- }
-#endif
- draw_line(Vector2(lastx, ymax - lasty), Vector2(newx, ymax - newy), Color(0.8, 0.8, 0.8, 0.8), 2.0);
+ return -1;
+}
+
+int CurveEditor::get_tangent_at(Vector2 pos) const {
+ if (_curve_ref.is_null() || _selected_point < 0)
+ return TANGENT_NONE;
+
+ if (_selected_point != 0) {
+ Vector2 control_pos = get_tangent_view_pos(_selected_point, TANGENT_LEFT);
+ if (control_pos.distance_to(pos) < _hover_radius) {
+ return TANGENT_LEFT;
}
+ }
- lastx = newx;
- lasty = newy;
+ if (_selected_point != _curve_ref->get_point_count() - 1) {
+ Vector2 control_pos = get_tangent_view_pos(_selected_point, TANGENT_RIGHT);
+ if (control_pos.distance_to(pos) < _hover_radius) {
+ return TANGENT_RIGHT;
+ }
}
- int splits = 8;
+ return TANGENT_NONE;
+}
- draw_line(Vector2(0, ymax - 1), Vector2(xmax, ymax - 1), Color(0.8, 0.8, 0.8, 0.3), 2.0);
+void CurveEditor::add_point(Vector2 pos) {
+ ERR_FAIL_COND(_curve_ref.is_null());
- for (int i = 0; i <= splits; i++) {
- float fofs = float(i) / splits;
- draw_line(Vector2(fofs * xmax, ymax), Vector2(fofs * xmax, ymax - 2), Color(0.8, 0.8, 0.8, 0.8), 2.0);
+ Array prev_data = _curve_ref->get_data();
- String text = rtos(fofs);
- int size = font->get_string_size(text).x;
- int ofs = fofs * xmax - size * 0.5;
- if (ofs < 2) {
- ofs = 2;
- } else if (ofs + size > xmax - 2) {
- ofs = xmax - size - 2;
- }
+ Vector2 point_pos = get_world_pos(pos);
+ if (point_pos.y < 0.0)
+ point_pos.y = 0.0;
+ else if (point_pos.y > 1.0)
+ point_pos.y = 1.0;
- draw_string(font, Vector2(ofs, ymax + font->get_ascent()), text, Color(0.8, 0.8, 0.8, 1));
- }
+ _curve_ref->add_point(point_pos);
+
+ push_undo(prev_data);
}
-void CurveTextureEdit::_notification(int p_what) {
+void CurveEditor::remove_point(int index) {
+ ERR_FAIL_COND(_curve_ref.is_null());
- if (p_what == NOTIFICATION_DRAW) {
+ Array prev_data = _curve_ref->get_data();
- Ref<Font> font = get_font("font", "Label");
+ _curve_ref->remove_point(index);
- int font_h = font->get_height();
+ if (index == _selected_point)
+ set_selected_point(-1);
- draw_style_box(get_stylebox("bg", "Tree"), Rect2(Point2(), get_size()));
+ push_undo(prev_data);
+}
- int w = get_size().x;
- int h = get_size().y;
+void CurveEditor::set_selected_point(int index) {
+ if (index != _selected_point) {
+ _selected_point = index;
+ update();
+ }
+}
- Vector2 prev = Vector2(0, 0);
- Vector2 prev2 = Vector2(0, 0);
+void CurveEditor::set_hover_point_index(int index) {
+ if (index != _hover_point) {
+ _hover_point = index;
+ update();
+ }
+}
- for (int i = -1; i < points.size(); i++) {
+void CurveEditor::push_undo(Array previous_curve_data) {
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
- Vector2 next;
- Vector2 next2;
- if (i + 1 >= points.size()) {
- next = Vector2(1, 0);
- } else {
- next = Vector2(points[i + 1].offset, points[i + 1].height);
- }
+ ur->create_action(TTR("Modify Curve"));
+ ur->add_do_method(*_curve_ref, "_set_data", _curve_ref->get_data());
+ ur->add_undo_method(*_curve_ref, "_set_data", previous_curve_data);
- if (i + 2 >= points.size()) {
- next2 = Vector2(1, 0);
- } else {
- next2 = Vector2(points[i + 2].offset, points[i + 2].height);
- }
+ // This boolean is to prevent commit_action from executing the do method,
+ // because at this point it's already done, there is no point in doing it twice
+ _curve_ref->_disable_set_data = true;
+ ur->commit_action();
+ _curve_ref->_disable_set_data = false;
+}
- /*if (i==-1 && prev.offset==next.offset) {
- prev=next;
- continue;
- }*/
+void CurveEditor::update_view_transform() {
+ Vector2 control_size = get_size();
+ const real_t margin = 24;
- _plot_curve(prev2, prev, next, next2);
+ _world_rect = Rect2(Curve::MIN_X, 0, Curve::MAX_X, 1);
+ Vector2 wm = Vector2(margin, margin) / control_size;
+ _world_rect.position -= wm;
+ _world_rect.size += 2.0 * wm;
- prev2 = prev;
- prev = next;
- }
+ _world_to_view = Transform2D();
+ _world_to_view.translate(-_world_rect.position - Vector2(0, _world_rect.size.y));
+ _world_to_view.scale(Vector2(control_size.x, -control_size.y) / _world_rect.size);
+}
- Vector2 size = get_size();
- size.y -= font_h;
- for (int i = 0; i < points.size(); i++) {
+Vector2 CurveEditor::get_tangent_view_pos(int i, TangentIndex tangent) const {
- Color col = i == grabbed ? Color(1, 0.0, 0.0, 0.9) : Color(1, 1, 1, 0.8);
+ Vector2 dir;
+ if (tangent == TANGENT_LEFT)
+ dir = -Vector2(1, _curve_ref->get_point_left_tangent(i));
+ else
+ dir = Vector2(1, _curve_ref->get_point_right_tangent(i));
- float h = (points[i].height - min) / (max - min);
- draw_rect(Rect2(Vector2(points[i].offset, 1.0 - h) * size - Vector2(2, 2), Vector2(5, 5)), col);
- }
+ Vector2 point_pos = get_view_pos(_curve_ref->get_point_pos(i));
+ Vector2 control_pos = get_view_pos(_curve_ref->get_point_pos(i) + dir);
- /* if (grabbed!=-1) {
+ return point_pos + _tangents_length * (control_pos - point_pos).normalized();
+}
- draw_rect(Rect2(total_w+3,0,h,h),points[grabbed].color);
- }
-*/
- if (has_focus()) {
+Vector2 CurveEditor::get_view_pos(Vector2 world_pos) const {
+ return _world_to_view.xform(world_pos);
+}
+
+Vector2 CurveEditor::get_world_pos(Vector2 view_pos) const {
+ return _world_to_view.affine_inverse().xform(view_pos);
+}
- draw_line(Vector2(-1, -1), Vector2(w + 1, -1), Color(1, 1, 1, 0.6));
- draw_line(Vector2(w + 1, -1), Vector2(w + 1, h + 1), Color(1, 1, 1, 0.6));
- draw_line(Vector2(w + 1, h + 1), Vector2(-1, h + 1), Color(1, 1, 1, 0.6));
- draw_line(Vector2(-1, -1), Vector2(-1, h + 1), Color(1, 1, 1, 0.6));
+// Uses non-baked points, but takes advantage of ordered iteration to be faster
+template <typename T>
+static void plot_curve_accurate(const Curve &curve, float step, T plot_func) {
+
+ if (curve.get_point_count() <= 1) {
+ // Not enough points to make a curve, so it's just a straight line
+ float y = curve.interpolate(0);
+ plot_func(Vector2(0, y), Vector2(1.f, y), true);
+
+ } else {
+ Vector2 first_point = curve.get_point_pos(0);
+ Vector2 last_point = curve.get_point_pos(curve.get_point_count() - 1);
+
+ // Edge lines
+ plot_func(Vector2(0, first_point.y), first_point, false);
+ plot_func(Vector2(Curve::MAX_X, last_point.y), last_point, false);
+
+ // Draw section by section, so that we get maximum precision near points.
+ // It's an accurate representation, but slower than using the baked one.
+ for (int i = 1; i < curve.get_point_count(); ++i) {
+ Vector2 a = curve.get_point_pos(i - 1);
+ Vector2 b = curve.get_point_pos(i);
+
+ Vector2 pos = a;
+ Vector2 prev_pos = a;
+
+ float len = b.x - a.x;
+ //float step = 4.f / view_size.x;
+
+ for (float x = step; x < len; x += step) {
+ pos.x = a.x + x;
+ pos.y = curve.interpolate_local_nocheck(i - 1, x);
+ plot_func(prev_pos, pos, true);
+ prev_pos = pos;
+ }
+
+ plot_func(prev_pos, b, true);
}
}
}
-Size2 CurveTextureEdit::get_minimum_size() const {
+struct CanvasItemPlotCurve {
- return Vector2(64, 64);
-}
+ CanvasItem &ci;
+ Color color1;
+ Color color2;
-void CurveTextureEdit::set_range(float p_min, float p_max) {
- max = p_max;
- min = p_min;
- update();
-}
+ CanvasItemPlotCurve(CanvasItem &p_ci, Color p_color1, Color p_color2)
+ : ci(p_ci), color1(p_color1), color2(p_color2) {}
-void CurveTextureEdit::set_points(const Vector<Vector2> &p_points) {
+ void operator()(Vector2 pos0, Vector2 pos1, bool in_definition) {
+ ci.draw_line(pos0, pos1, in_definition ? color1 : color2);
+ }
+};
+
+void CurveEditor::_draw() {
+ if (_curve_ref.is_null())
+ return;
+ Curve &curve = **_curve_ref;
+
+ update_view_transform();
+
+ // Background
+
+ Vector2 view_size = get_rect().size;
+ draw_style_box(get_stylebox("bg", "Tree"), Rect2(Point2(), view_size));
+
+ // Grid
+
+ draw_set_transform_matrix(_world_to_view);
+
+ Vector2 min_edge = get_world_pos(Vector2(0, view_size.y));
+ Vector2 max_edge = get_world_pos(Vector2(view_size.x, 0));
+
+ const Color grid_color0(0, 0, 0, 0.5);
+ const Color grid_color1(0, 0, 0, 0.15);
+ draw_line(Vector2(min_edge.x, 0), Vector2(max_edge.x, 0), grid_color0);
+ draw_line(Vector2(0, min_edge.y), Vector2(0, max_edge.y), grid_color0);
+ draw_line(Vector2(1, max_edge.y), Vector2(1, min_edge.y), grid_color0);
+ draw_line(Vector2(max_edge.x, 1), Vector2(min_edge.x, 1), grid_color0);
- points.clear();
- for (int i = 0; i < p_points.size(); i++) {
- Point p;
- p.offset = p_points[i].x;
- p.height = p_points[i].y;
- points.push_back(p);
+ const Vector2 grid_step(0.25, 0.5);
+
+ for (real_t x = 0; x < 1.0; x += grid_step.x) {
+ draw_line(Vector2(x, min_edge.y), Vector2(x, max_edge.y), grid_color1);
+ }
+ for (real_t y = 0; y < 1.0; y += grid_step.y) {
+ draw_line(Vector2(min_edge.x, y), Vector2(max_edge.x, y), grid_color1);
}
- points.sort();
- update();
-}
+ // Markings
-Vector<Vector2> CurveTextureEdit::get_points() const {
- Vector<Vector2> ret;
- for (int i = 0; i < points.size(); i++)
- ret.push_back(Vector2(points[i].offset, points[i].height));
- return ret;
-}
+ draw_set_transform_matrix(Transform2D());
-void CurveTextureEdit::_bind_methods() {
+ Ref<Font> font = get_font("font", "Label");
+ const Color text_color(1, 1, 1, 0.3);
- ClassDB::bind_method(D_METHOD("_gui_input"), &CurveTextureEdit::_gui_input);
+ draw_string(font, get_view_pos(Vector2(0, 0)), "0.0", text_color);
- ADD_SIGNAL(MethodInfo("curve_changed"));
-}
+ draw_string(font, get_view_pos(Vector2(0.25, 0)), "0.25", text_color);
+ draw_string(font, get_view_pos(Vector2(0.5, 0)), "0.5", text_color);
+ draw_string(font, get_view_pos(Vector2(0.75, 0)), "0.75", text_color);
+ draw_string(font, get_view_pos(Vector2(1, 0)), "1.0", text_color);
-CurveTextureEdit::CurveTextureEdit() {
+ draw_string(font, get_view_pos(Vector2(0, 0.5)), "0.5", text_color);
+ draw_string(font, get_view_pos(Vector2(0, 1)), "1.0", text_color);
- grabbed = -1;
- grabbing = false;
- max = 1;
- min = 0;
- set_focus_mode(FOCUS_ALL);
-}
+ // Draw tangents for current point
-void CurveTextureEditorPlugin::_curve_settings_changed() {
+ if (_selected_point >= 0) {
- if (!curve_texture_ref.is_valid())
- return;
- curve_editor->set_points(Variant(curve_texture_ref->get_points()));
- curve_editor->set_range(curve_texture_ref->get_min(), curve_texture_ref->get_max());
-}
+ const Color tangent_color(0.5, 0.5, 1, 1);
+
+ int i = _selected_point;
+ Vector2 pos = curve.get_point_pos(i);
+
+ if (i != 0) {
+ Vector2 control_pos = get_tangent_view_pos(i, TANGENT_LEFT);
+ draw_line(get_view_pos(pos), control_pos, tangent_color);
+ draw_rect(Rect2(control_pos, Vector2(1, 1)).grow(2), tangent_color);
+ }
-CurveTextureEditorPlugin::CurveTextureEditorPlugin(EditorNode *p_node) {
+ if (i != curve.get_point_count() - 1) {
+ Vector2 control_pos = get_tangent_view_pos(i, TANGENT_RIGHT);
+ draw_line(get_view_pos(pos), control_pos, tangent_color);
+ draw_rect(Rect2(control_pos, Vector2(1, 1)).grow(2), tangent_color);
+ }
+ }
- editor = p_node;
- curve_editor = memnew(CurveTextureEdit);
+ // Draw lines
- curve_button = editor->add_bottom_panel_item("CurveTexture", curve_editor);
+ draw_set_transform_matrix(_world_to_view);
- curve_button->hide();
- curve_editor->set_custom_minimum_size(Size2(100, 128 * EDSCALE));
- curve_editor->hide();
- curve_editor->connect("curve_changed", this, "curve_changed");
-}
+ const Color line_color(1, 1, 1, 0.85);
+ const Color edge_line_color(1, 1, 1, 0.4);
+
+ CanvasItemPlotCurve plot_func(*this, line_color, edge_line_color);
+ plot_curve_accurate(curve, 4.f / view_size.x, plot_func);
+
+ /*// TEST draw baked curve
+ {
+ Vector2 pos = Vector2(0, curve.interpolate_baked(0));
+ Vector2 prev_pos = pos;
-void CurveTextureEditorPlugin::edit(Object *p_object) {
+ float len = 1.0;
+ float step = 4.f / view_size.x;
- if (curve_texture_ref.is_valid()) {
- curve_texture_ref->disconnect("changed", this, "_curve_settings_changed");
+ for(float x = step; x < len; x += step) {
+ pos.x = x;
+ pos.y = curve.interpolate_baked(x);
+ draw_line(get_point_view_pos(prev_pos), get_point_view_pos(pos), Color(0,1,0));
+ prev_pos = pos;
+ }
+
+ draw_line(get_point_view_pos(prev_pos), get_point_view_pos(Vector2(1, curve.interpolate_baked(1))), Color(0,1,0));
+ }//*/
+
+ // Draw points
+
+ draw_set_transform_matrix(Transform2D());
+
+ const Color point_color(1, 1, 1);
+ const Color selected_point_color(1, 0.5, 0.5);
+
+ for (int i = 0; i < curve.get_point_count(); ++i) {
+ Vector2 pos = curve.get_point_pos(i);
+ draw_rect(Rect2(get_view_pos(pos), Vector2(1, 1)).grow(3), i == _selected_point ? selected_point_color : point_color);
+ // TODO Circles are prettier. Needs a fix! Or a texture
+ //draw_circle(pos, 2, point_color);
}
- CurveTexture *curve_texture = p_object->cast_to<CurveTexture>();
- if (!curve_texture)
- return;
- curve_texture_ref = Ref<CurveTexture>(curve_texture);
- curve_editor->set_points(Variant(curve_texture_ref->get_points()));
- curve_editor->set_range(curve_texture_ref->get_min(), curve_texture_ref->get_max());
- if (!curve_texture_ref->is_connected("changed", this, "_curve_settings_changed")) {
- curve_texture_ref->connect("changed", this, "_curve_settings_changed");
+
+ // Hover
+
+ if (_hover_point != -1) {
+ const Color hover_color = line_color;
+ Vector2 pos = curve.get_point_pos(_hover_point);
+ stroke_rect(Rect2(get_view_pos(pos), Vector2(1, 1)).grow(_hover_radius), hover_color);
}
}
-bool CurveTextureEditorPlugin::handles(Object *p_object) const {
+// TODO That should be part of the drawing API...
+void CurveEditor::stroke_rect(Rect2 rect, Color color) {
+
+ // a---b
+ // | |
+ // c---d
+ Vector2 a(rect.position);
+ Vector2 b(rect.position.x + rect.size.x, rect.position.y);
+ Vector2 c(rect.position.x, rect.position.y + rect.size.y);
+ Vector2 d(rect.position + rect.size);
+
+ draw_line(a, b, color);
+ draw_line(b, d, color);
+ draw_line(d, c, color);
+ draw_line(c, a, color);
+}
- return p_object->is_class("CurveTexture");
+void CurveEditor::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_gui_input"), &CurveEditor::on_gui_input);
+ ClassDB::bind_method(D_METHOD("_on_preset_item_selected"), &CurveEditor::on_preset_item_selected);
+ ClassDB::bind_method(D_METHOD("_curve_changed"), &CurveEditor::_curve_changed);
+ ClassDB::bind_method(D_METHOD("_on_context_menu_item_selected"), &CurveEditor::on_context_menu_item_selected);
}
-void CurveTextureEditorPlugin::make_visible(bool p_visible) {
+//---------------
- if (p_visible) {
- curve_button->show();
- editor->make_bottom_panel_item_visible(curve_editor);
+CurveEditorPlugin::CurveEditorPlugin(EditorNode *p_node) {
+ _editor_node = p_node;
- } else {
+ _view = memnew(CurveEditor);
+ _view->set_custom_minimum_size(Size2(100, 128 * EDSCALE));
+ _view->hide();
- curve_button->hide();
- if (curve_editor->is_visible_in_tree())
- editor->hide_bottom_panel();
- }
+ _toggle_button = _editor_node->add_bottom_panel_item(get_name(), _view);
+ _toggle_button->hide();
}
-void CurveTextureEditorPlugin::_curve_changed() {
+CurveEditorPlugin::~CurveEditorPlugin() {
+}
- if (curve_texture_ref.is_valid()) {
+void CurveEditorPlugin::edit(Object *p_object) {
- UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ Ref<Curve> curve_ref;
- Vector<Vector2> points = curve_editor->get_points();
- PoolVector<Vector2> ppoints = Variant(points);
+ if (_current_ref.is_valid()) {
+ CurveTexture *ct = _current_ref->cast_to<CurveTexture>();
+ if (ct)
+ ct->disconnect(CoreStringNames::get_singleton()->changed, this, "_curve_texture_changed");
+ }
+
+ if (p_object) {
+ Resource *res = p_object->cast_to<Resource>();
+ ERR_FAIL_COND(res == NULL);
+ ERR_FAIL_COND(!handles(p_object));
+
+ _current_ref = Ref<Resource>(p_object->cast_to<Resource>());
+
+ if (_current_ref.is_valid()) {
+ Curve *curve = _current_ref->cast_to<Curve>();
+ if (curve)
+ curve_ref = Ref<Curve>(curve);
+ else {
+ CurveTexture *ct = _current_ref->cast_to<CurveTexture>();
+ if (ct) {
+ ct->connect(CoreStringNames::get_singleton()->changed, this, "_curve_texture_changed");
+ curve_ref = ct->get_curve();
+ }
+ }
+ }
- ur->create_action(TTR("Modify Curve"), UndoRedo::MERGE_ENDS);
- ur->add_do_method(this, "undo_redo_curve_texture", ppoints);
- ur->add_undo_method(this, "undo_redo_curve_texture", curve_texture_ref->get_points());
- ur->commit_action();
+ } else {
+ _current_ref = Ref<Resource>();
}
+
+ _view->set_curve(curve_ref);
}
-void CurveTextureEditorPlugin::_undo_redo_curve_texture(const PoolVector<Vector2> &points) {
+bool CurveEditorPlugin::handles(Object *p_object) const {
+ // Both handled so that we can keep the curve editor open
+ return p_object->cast_to<Curve>() || p_object->cast_to<CurveTexture>();
+}
- curve_texture_ref->set_points(points);
- curve_editor->set_points(Variant(curve_texture_ref->get_points()));
- curve_editor->update();
+void CurveEditorPlugin::make_visible(bool p_visible) {
+ if (p_visible) {
+ _toggle_button->show();
+ _editor_node->make_bottom_panel_item_visible(_view);
+ } else {
+ _toggle_button->hide();
+ if (_view->is_visible_in_tree())
+ _editor_node->hide_bottom_panel();
+ }
}
-CurveTextureEditorPlugin::~CurveTextureEditorPlugin() {
+void CurveEditorPlugin::_curve_texture_changed() {
+ // If the curve is shown indirectly as a CurveTexture is edited,
+ // we need to monitor when the curve property gets assigned
+ CurveTexture *ct = _current_ref->cast_to<CurveTexture>();
+ if (ct) {
+ _view->set_curve(ct->get_curve());
+ }
}
-void CurveTextureEditorPlugin::_bind_methods() {
- ClassDB::bind_method(D_METHOD("curve_changed"), &CurveTextureEditorPlugin::_curve_changed);
- ClassDB::bind_method(D_METHOD("_curve_settings_changed"), &CurveTextureEditorPlugin::_curve_settings_changed);
- ClassDB::bind_method(D_METHOD("undo_redo_curve_texture", "points"), &CurveTextureEditorPlugin::_undo_redo_curve_texture);
+void CurveEditorPlugin::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_curve_texture_changed"), &CurveEditorPlugin::_curve_texture_changed);
}
diff --git a/editor/plugins/curve_editor_plugin.h b/editor/plugins/curve_editor_plugin.h
index 4e75ba407c..0ed4ee3517 100644
--- a/editor/plugins/curve_editor_plugin.h
+++ b/editor/plugins/curve_editor_plugin.h
@@ -27,69 +27,119 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+
#ifndef CURVE_EDITOR_PLUGIN_H
#define CURVE_EDITOR_PLUGIN_H
#include "editor/editor_node.h"
#include "editor/editor_plugin.h"
+#include "scene/resources/curve.h"
-class CurveTextureEdit : public Control {
+// Edits a y(x) curve
+class CurveEditor : public Control {
+ GDCLASS(CurveEditor, Control)
+public:
+ CurveEditor();
- GDCLASS(CurveTextureEdit, Control);
+ Size2 get_minimum_size() const;
- struct Point {
+ void set_curve(Ref<Curve> curve);
- float offset;
- float height;
- bool operator<(const Point &p_ponit) const {
- return offset < p_ponit.offset;
- }
+ enum PresetID {
+ PRESET_FLAT0 = 0,
+ PRESET_FLAT1,
+ PRESET_LINEAR,
+ PRESET_EASE_IN,
+ PRESET_EASE_OUT,
+ PRESET_SMOOTHSTEP,
+ PRESET_COUNT
};
- bool grabbing;
- int grabbed;
- Vector<Point> points;
- float max, min;
+ enum ContextAction {
+ CONTEXT_ADD_POINT = 0,
+ CONTEXT_REMOVE_POINT
+ };
- void _plot_curve(const Vector2 &p_a, const Vector2 &p_b, const Vector2 &p_c, const Vector2 &p_d);
+ enum TangentIndex {
+ TANGENT_NONE = -1,
+ TANGENT_LEFT = 0,
+ TANGENT_RIGHT = 1
+ };
protected:
- void _gui_input(const Ref<InputEvent> &p_event);
void _notification(int p_what);
+
static void _bind_methods();
-public:
- void set_range(float p_min, float p_max);
- void set_points(const Vector<Vector2> &p_points);
- Vector<Vector2> get_points() const;
- virtual Size2 get_minimum_size() const;
- CurveTextureEdit();
+private:
+ void on_gui_input(const Ref<InputEvent> &p_event);
+ void on_preset_item_selected(int preset_id);
+ void _curve_changed();
+ void on_context_menu_item_selected(int action_id);
+
+ void open_context_menu(Vector2 pos);
+ int get_point_at(Vector2 pos) const;
+ int get_tangent_at(Vector2 pos) const;
+ void add_point(Vector2 pos);
+ void remove_point(int index);
+ void set_selected_point(int index);
+ void set_hover_point_index(int index);
+ void push_undo(Array previous_curve_data);
+ void update_view_transform();
+
+ Vector2 get_tangent_view_pos(int i, TangentIndex tangent) const;
+ Vector2 get_view_pos(Vector2 world_pos) const;
+ Vector2 get_world_pos(Vector2 view_pos) const;
+
+ void _draw();
+
+ void stroke_rect(Rect2 rect, Color color);
+
+private:
+ Rect2 _world_rect;
+ Transform2D _world_to_view;
+
+ Ref<Curve> _curve_ref;
+ PopupMenu *_context_menu;
+ PopupMenu *_presets_menu;
+
+ Array _undo_data;
+ bool _has_undo_data;
+ bool _undo_no_commit;
+
+ Vector2 _context_click_pos;
+ int _selected_point;
+ int _hover_point;
+ int _selected_tangent;
+ bool _dragging;
+
+ // Constant
+ float _hover_radius;
+ float _tangents_length;
};
-class CurveTextureEditorPlugin : public EditorPlugin {
-
- GDCLASS(CurveTextureEditorPlugin, EditorPlugin);
+class CurveEditorPlugin : public EditorPlugin {
+ GDCLASS(CurveEditorPlugin, EditorPlugin)
+public:
+ CurveEditorPlugin(EditorNode *p_node);
+ ~CurveEditorPlugin();
- CurveTextureEdit *curve_editor;
- Ref<CurveTexture> curve_texture_ref;
- EditorNode *editor;
- ToolButton *curve_button;
+ String get_name() const { return "Curve"; }
+ bool has_main_screen() const { return false; }
+ void edit(Object *p_object);
+ bool handles(Object *p_object) const;
+ void make_visible(bool p_visible);
-protected:
+private:
static void _bind_methods();
- void _curve_changed();
- void _undo_redo_curve_texture(const PoolVector<Vector2> &points);
- void _curve_settings_changed();
-public:
- virtual String get_name() const { return "CurveTexture"; }
- bool has_main_screen() const { return false; }
- virtual void edit(Object *p_node);
- virtual bool handles(Object *p_node) const;
- virtual void make_visible(bool p_visible);
+ void _curve_texture_changed();
- CurveTextureEditorPlugin(EditorNode *p_node);
- ~CurveTextureEditorPlugin();
+private:
+ CurveEditor *_view;
+ Ref<Resource> _current_ref;
+ EditorNode *_editor_node;
+ ToolButton *_toggle_button;
};
#endif // CURVE_EDITOR_PLUGIN_H
diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp
index 7c8ee97f22..bad88979ac 100644
--- a/editor/plugins/shader_editor_plugin.cpp
+++ b/editor/plugins/shader_editor_plugin.cpp
@@ -188,7 +188,7 @@ void ShaderTextEditor::_validate_script() {
if (err != OK) {
String error_text = "error(" + itos(sl.get_error_line()) + "): " + sl.get_error_text();
set_error(error_text);
- get_text_edit()->set_line_as_marked(sl.get_error_line(), true);
+ get_text_edit()->set_line_as_marked(sl.get_error_line() - 1, true);
} else {
for (int i = 0; i < get_text_edit()->get_line_count(); i++)
diff --git a/editor/plugins/texture_editor_plugin.cpp b/editor/plugins/texture_editor_plugin.cpp
index 676e50d61c..c4fe15e61c 100644
--- a/editor/plugins/texture_editor_plugin.cpp
+++ b/editor/plugins/texture_editor_plugin.cpp
@@ -61,9 +61,21 @@ void TextureEditor::_notification(int p_what) {
tex_height = texture->get_height() * tex_width / texture->get_width();
}
+ // Prevent the texture from being unpreviewable after the rescale, so that we can still see something
+ if (tex_height <= 0)
+ tex_height = 1;
+ if (tex_width <= 0)
+ tex_width = 1;
+
int ofs_x = (size.width - tex_width) / 2;
int ofs_y = (size.height - tex_height) / 2;
+ if (texture->cast_to<CurveTexture>()) {
+ // In the case of CurveTextures we know they are 1 in height, so fill the preview to see the gradient
+ ofs_y = 0;
+ tex_height = size.height;
+ }
+
draw_texture_rect(texture, Rect2(ofs_x, ofs_y, tex_width, tex_height));
Ref<Font> font = get_font("font", "Label");
diff --git a/main/input_default.cpp b/main/input_default.cpp
index e488438059..bde1e84926 100644
--- a/main/input_default.cpp
+++ b/main/input_default.cpp
@@ -874,6 +874,8 @@ void InputDefault::joy_axis(int p_device, int p_axis, const JoyAxis &p_value) {
_THREAD_SAFE_METHOD_;
+ ERR_FAIL_INDEX(p_axis, JOY_AXIS_MAX);
+
Joypad &joy = joy_names[p_device];
if (joy.last_axis[p_axis] == p_value.value) {
diff --git a/modules/gdscript/gd_editor.cpp b/modules/gdscript/gd_editor.cpp
index 5e3ce31dd6..adf3c8edc4 100644
--- a/modules/gdscript/gd_editor.cpp
+++ b/modules/gdscript/gd_editor.cpp
@@ -1276,7 +1276,7 @@ static void _find_identifiers_in_class(GDCompletionContext &context, bool p_stat
}
}
List<MethodInfo> methods;
- ClassDB::get_method_list(type, &methods);
+ ClassDB::get_method_list(type, &methods, false, true);
for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
if (E->get().name.begins_with("_"))
continue;
@@ -1643,7 +1643,7 @@ static void _find_type_arguments(GDCompletionContext &context, const GDParser::N
} else {
//regular method
- if (p_method.operator String() == "connect") {
+ if (p_method.operator String() == "connect" || (p_method.operator String() == "emit_signal" && p_argidx == 0)) {
if (p_argidx == 0) {
List<MethodInfo> sigs;
@@ -2251,7 +2251,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base
}
List<MethodInfo> mi;
- ClassDB::get_method_list(t.obj_type, &mi);
+ ClassDB::get_method_list(t.obj_type, &mi, false, true);
for (List<MethodInfo>::Element *E = mi.front(); E; E = E->next()) {
if (E->get().name.begins_with("_"))
diff --git a/platform/windows/godot.ico b/platform/windows/godot.ico
index fd5c28944f..dd611e07da 100644
--- a/platform/windows/godot.ico
+++ b/platform/windows/godot.ico
Binary files differ
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp
index 3d9e64ae79..cef473dcdf 100644
--- a/scene/2d/audio_stream_player_2d.cpp
+++ b/scene/2d/audio_stream_player_2d.cpp
@@ -61,7 +61,7 @@ void AudioStreamPlayer2D::_mix_audio() {
for (int j = 0; j < buffer_size; j++) {
- target[j] = buffer[j] * vol;
+ target[j] += buffer[j] * vol;
vol += vol_inc;
}
@@ -76,8 +76,8 @@ void AudioStreamPlayer2D::_mix_audio() {
for (int j = 0; j < buffer_size; j++) {
AudioFrame frame = buffer[j] * vol;
- targets[0][j] = frame;
- targets[1][j] = frame;
+ targets[0][j] += frame;
+ targets[1][j] += frame;
vol += vol_inc;
}
@@ -93,9 +93,9 @@ void AudioStreamPlayer2D::_mix_audio() {
for (int j = 0; j < buffer_size; j++) {
AudioFrame frame = buffer[j] * vol;
- targets[0][j] = frame;
- targets[1][j] = frame;
- targets[2][j] = frame;
+ targets[0][j] += frame;
+ targets[1][j] += frame;
+ targets[2][j] += frame;
vol += vol_inc;
}
diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp
index 89b89a50d8..189dd66a26 100644
--- a/scene/2d/canvas_item.cpp
+++ b/scene/2d/canvas_item.cpp
@@ -39,6 +39,196 @@
#include "scene/scene_string_names.h"
#include "servers/visual_server.h"
+Mutex *CanvasItemMaterial::material_mutex = NULL;
+SelfList<CanvasItemMaterial>::List CanvasItemMaterial::dirty_materials;
+Map<CanvasItemMaterial::MaterialKey, CanvasItemMaterial::ShaderData> CanvasItemMaterial::shader_map;
+
+void CanvasItemMaterial::init_shaders() {
+
+#ifndef NO_THREADS
+ material_mutex = Mutex::create();
+#endif
+}
+
+void CanvasItemMaterial::finish_shaders() {
+
+#ifndef NO_THREADS
+ memdelete(material_mutex);
+#endif
+}
+
+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
+ VS::get_singleton()->free(shader_map[current_key].shader);
+ shader_map.erase(current_key);
+ }
+ }
+
+ current_key = mk;
+
+ if (shader_map.has(mk)) {
+
+ VS::get_singleton()->material_set_shader(_get_material(), shader_map[mk].shader);
+ shader_map[mk].users++;
+ return;
+ }
+
+ //must create a shader!
+
+ String 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;
+ }
+
+ 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"; //thats it.
+
+ ShaderData shader_data;
+ shader_data.shader = VS::get_singleton()->shader_create();
+ shader_data.users = 1;
+
+ VS::get_singleton()->shader_set_code(shader_data.shader, code);
+
+ shader_map[mk] = shader_data;
+
+ VS::get_singleton()->material_set_shader(_get_material(), shader_data.shader);
+}
+
+void CanvasItemMaterial::flush_changes() {
+
+ if (material_mutex)
+ material_mutex->lock();
+
+ while (dirty_materials.first()) {
+
+ dirty_materials.first()->self()->_update_shader();
+ }
+
+ if (material_mutex)
+ material_mutex->unlock();
+}
+
+void CanvasItemMaterial::_queue_shader_change() {
+
+ if (material_mutex)
+ material_mutex->lock();
+
+ if (!element.in_list()) {
+ dirty_materials.add(&element);
+ }
+
+ if (material_mutex)
+ material_mutex->unlock();
+}
+
+bool CanvasItemMaterial::_is_shader_dirty() const {
+
+ bool dirty = false;
+
+ if (material_mutex)
+ material_mutex->lock();
+
+ dirty = element.in_list();
+
+ if (material_mutex)
+ material_mutex->unlock();
+
+ return dirty;
+}
+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::_validate_property(PropertyInfo &property) const {
+}
+
+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);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Mix,Add,Sub,Mul,Premult 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");
+
+ BIND_CONSTANT(BLEND_MODE_MIX);
+ BIND_CONSTANT(BLEND_MODE_ADD);
+ BIND_CONSTANT(BLEND_MODE_SUB);
+ BIND_CONSTANT(BLEND_MODE_MUL);
+ BIND_CONSTANT(BLEND_MODE_PREMULT_ALPHA);
+ BIND_CONSTANT(LIGHT_MODE_NORMAL);
+ BIND_CONSTANT(LIGHT_MODE_UNSHADED);
+ BIND_CONSTANT(LIGHT_MODE_LIGHT_ONLY);
+}
+
+CanvasItemMaterial::CanvasItemMaterial()
+ : element(this) {
+
+ blend_mode = BLEND_MODE_MIX;
+ light_mode = LIGHT_MODE_NORMAL;
+
+ current_key.key = 0;
+ current_key.invalid_key = 1;
+ _queue_shader_change();
+}
+
+CanvasItemMaterial::~CanvasItemMaterial() {
+
+ if (material_mutex)
+ material_mutex->lock();
+
+ 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
+ VS::get_singleton()->free(shader_map[current_key].shader);
+ shader_map.erase(current_key);
+ }
+
+ VS::get_singleton()->material_set_shader(_get_material(), RID());
+ }
+
+ if (material_mutex)
+ material_mutex->unlock();
+}
+
///////////////////////////////////////////////////////////////////
bool CanvasItem::is_visible_in_tree() const {
@@ -665,7 +855,7 @@ bool CanvasItem::is_draw_behind_parent_enabled() const {
return behind;
}
-void CanvasItem::set_material(const Ref<ShaderMaterial> &p_material) {
+void CanvasItem::set_material(const Ref<Material> &p_material) {
material = p_material;
RID rid;
@@ -686,7 +876,7 @@ bool CanvasItem::get_use_parent_material() const {
return use_parent_material;
}
-Ref<ShaderMaterial> CanvasItem::get_material() const {
+Ref<Material> CanvasItem::get_material() const {
return material;
}
@@ -788,8 +978,8 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_world_2d"), &CanvasItem::get_world_2d);
//ClassDB::bind_method(D_METHOD("get_viewport"),&CanvasItem::get_viewport);
- ClassDB::bind_method(D_METHOD("set_material", "material:ShaderMaterial"), &CanvasItem::set_material);
- ClassDB::bind_method(D_METHOD("get_material:ShaderMaterial"), &CanvasItem::get_material);
+ ClassDB::bind_method(D_METHOD("set_material", "material:Material"), &CanvasItem::set_material);
+ ClassDB::bind_method(D_METHOD("get_material:Material"), &CanvasItem::get_material);
ClassDB::bind_method(D_METHOD("set_use_parent_material", "enable"), &CanvasItem::set_use_parent_material);
ClassDB::bind_method(D_METHOD("get_use_parent_material"), &CanvasItem::get_use_parent_material);
@@ -815,7 +1005,7 @@ void CanvasItem::_bind_methods() {
ADD_PROPERTYNO(PropertyInfo(Variant::INT, "light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_light_mask", "get_light_mask");
ADD_GROUP("Material", "");
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial"), "set_material", "get_material");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,CanvasItemMaterial"), "set_material", "get_material");
ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "use_parent_material"), "set_use_parent_material", "get_use_parent_material");
//exporting these two things doesn't really make much sense i think
//ADD_PROPERTY( PropertyInfo(Variant::BOOL,"transform/toplevel"), "set_as_toplevel","is_set_as_toplevel") ;
diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h
index 906a08d219..bffc171fc1 100644
--- a/scene/2d/canvas_item.h
+++ b/scene/2d/canvas_item.h
@@ -42,6 +42,92 @@ class Font;
class StyleBox;
+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
+ };
+
+ 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 invalid_key : 1;
+ };
+
+ uint32_t key;
+
+ bool operator<(const MaterialKey &p_key) const {
+ return key < p_key.key;
+ }
+ };
+
+ struct ShaderData {
+ RID shader;
+ int users;
+ };
+
+ 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;
+ 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;
+ LightMode light_mode;
+
+protected:
+ static void _bind_methods();
+ void _validate_property(PropertyInfo &property) const;
+
+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;
+
+ static void init_shaders();
+ static void finish_shaders();
+ static void flush_changes();
+
+ CanvasItemMaterial();
+ virtual ~CanvasItemMaterial();
+};
+
+VARIANT_ENUM_CAST(CanvasItemMaterial::BlendMode)
+VARIANT_ENUM_CAST(CanvasItemMaterial::LightMode)
+
class CanvasItem : public Node {
GDCLASS(CanvasItem, Node);
@@ -83,7 +169,7 @@ private:
bool notify_local_transform;
bool notify_transform;
- Ref<ShaderMaterial> material;
+ Ref<Material> material;
mutable Transform2D global_transform;
mutable bool global_invalid;
@@ -203,8 +289,8 @@ public:
RID get_canvas() const;
Ref<World2D> get_world_2d() const;
- void set_material(const Ref<ShaderMaterial> &p_material);
- Ref<ShaderMaterial> get_material() const;
+ void set_material(const Ref<Material> &p_material);
+ Ref<Material> get_material() const;
void set_use_parent_material(bool p_use_parent_material);
bool get_use_parent_material() const;
diff --git a/scene/2d/particles_2d.cpp b/scene/2d/particles_2d.cpp
index cbecf00d52..beff247264 100644
--- a/scene/2d/particles_2d.cpp
+++ b/scene/2d/particles_2d.cpp
@@ -349,7 +349,7 @@ void Particles2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta");
ADD_PROPERTY(PropertyInfo(Variant::INT, "draw_order", PROPERTY_HINT_ENUM, "Index,Lifetime"), "set_draw_order", "get_draw_order");
ADD_GROUP("Process Material", "process_");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "process_material", PROPERTY_HINT_RESOURCE_TYPE, "ParticlesMaterial,ShaderMaterial"), "set_process_material", "get_process_material");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "process_material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,ParticlesMaterial"), "set_process_material", "get_process_material");
ADD_GROUP("Textures", "");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_normal_map", "get_normal_map");
diff --git a/scene/3d/mesh_instance.cpp b/scene/3d/mesh_instance.cpp
index 5b5bce342d..e755b1480b 100644
--- a/scene/3d/mesh_instance.cpp
+++ b/scene/3d/mesh_instance.cpp
@@ -98,7 +98,7 @@ void MeshInstance::_get_property_list(List<PropertyInfo> *p_list) const {
if (mesh.is_valid()) {
for (int i = 0; i < mesh->get_surface_count(); i++) {
- p_list->push_back(PropertyInfo(Variant::OBJECT, "material/" + itos(i), PROPERTY_HINT_RESOURCE_TYPE, "Material"));
+ p_list->push_back(PropertyInfo(Variant::OBJECT, "material/" + itos(i), PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,SpatialMaterial"));
}
}
}
diff --git a/scene/3d/particles.cpp b/scene/3d/particles.cpp
index 3394c1e204..722b698b75 100644
--- a/scene/3d/particles.cpp
+++ b/scene/3d/particles.cpp
@@ -303,7 +303,7 @@ void Particles::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta");
ADD_PROPERTY(PropertyInfo(Variant::INT, "draw_order", PROPERTY_HINT_ENUM, "Index,Lifetime,View Depth"), "set_draw_order", "get_draw_order");
ADD_GROUP("Process Material", "");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "process_material", PROPERTY_HINT_RESOURCE_TYPE, "ParticlesMaterial,ShaderMaterial"), "set_process_material", "get_process_material");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "process_material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,ParticlesMaterial"), "set_process_material", "get_process_material");
ADD_GROUP("Draw Passes", "draw_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "draw_passes", PROPERTY_HINT_RANGE, "0," + itos(MAX_DRAW_PASSES) + ",1"), "set_draw_passes", "get_draw_passes");
for (int i = 0; i < MAX_DRAW_PASSES; i++) {
@@ -571,7 +571,7 @@ void ParticlesMaterial::_update_shader() {
code += "\n";
code += " uint base_number=NUMBER/uint(trail_divisor);\n";
- code += " uint alt_seed=hash(base_number+uint(1));\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";
@@ -1082,16 +1082,9 @@ void ParticlesMaterial::set_param_texture(Parameter p_param, const Ref<Texture>
case PARAM_SCALE: {
VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->scale_texture, p_texture);
- Ref<CurveTexture> curve = p_texture;
- if (curve.is_valid()) {
- if (curve->get_min() == 0 && curve->get_max() == 1) {
-
- curve->set_max(32);
- PoolVector<Vector2> points;
- points.push_back(Vector2(0, 1));
- points.push_back(Vector2(1, 1));
- curve->set_points(points);
- }
+ Ref<CurveTexture> curve_tex = p_texture;
+ if (curve_tex.is_valid()) {
+ curve_tex->ensure_default_setup();
}
} break;
@@ -1257,14 +1250,7 @@ void ParticlesMaterial::set_trail_size_modifier(const Ref<CurveTexture> &p_trail
Ref<CurveTexture> curve = trail_size_modifier;
if (curve.is_valid()) {
- if (curve->get_min() == 0 && curve->get_max() == 1) {
-
- curve->set_max(32);
- PoolVector<Vector2> points;
- points.push_back(Vector2(0, 1));
- points.push_back(Vector2(1, 1));
- curve->set_points(points);
- }
+ curve->ensure_default_setup();
}
RID texture;
diff --git a/scene/3d/visual_instance.cpp b/scene/3d/visual_instance.cpp
index 104fa0f70f..6f8c38eddd 100644
--- a/scene/3d/visual_instance.cpp
+++ b/scene/3d/visual_instance.cpp
@@ -316,7 +316,7 @@ void GeometryInstance::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_aabb"), &GeometryInstance::get_aabb);
ADD_GROUP("Geometry", "");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material_override", PROPERTY_HINT_RESOURCE_TYPE, "Material"), "set_material_override", "get_material_override");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material_override", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,SpatialMaterial"), "set_material_override", "get_material_override");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cast_shadow", PROPERTY_HINT_ENUM, "Off,On,Double-Sided,Shadows Only"), "set_cast_shadows_setting", "get_cast_shadows_setting");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "extra_cull_margin", PROPERTY_HINT_RANGE, "0,16384,0"), "set_extra_cull_margin", "get_extra_cull_margin");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "visible_in_all_rooms"), "set_flag", "get_flag", FLAG_VISIBLE_IN_ALL_ROOMS);
diff --git a/scene/gui/input_action.cpp b/scene/gui/input_action.cpp
index 311cb4ab13..3f80c31c8b 100644
--- a/scene/gui/input_action.cpp
+++ b/scene/gui/input_action.cpp
@@ -43,7 +43,7 @@ Ref<InputEvent> ShortCut::get_shortcut() const {
bool ShortCut::is_shortcut(const Ref<InputEvent> &p_event) const {
- return shortcut.is_valid() && shortcut->action_match(p_event);
+ return shortcut.is_valid() && shortcut->shortcut_match(p_event);
}
String ShortCut::get_as_text() const {
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index d4ca55346a..151bc80321 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -469,6 +469,9 @@ void register_scene_types() {
ClassDB::register_class<Shader>();
ClassDB::register_class<ShaderMaterial>();
ClassDB::register_virtual_class<CanvasItem>();
+ ClassDB::register_class<CanvasItemMaterial>();
+ SceneTree::add_idle_callback(CanvasItemMaterial::flush_changes);
+ CanvasItemMaterial::init_shaders();
ClassDB::register_class<Node2D>();
ClassDB::register_class<Particles2D>();
//ClassDB::register_class<ParticleAttractor2D>();
@@ -577,6 +580,7 @@ void register_scene_types() {
ClassDB::register_class<Animation>();
ClassDB::register_virtual_class<Font>();
ClassDB::register_class<BitmapFont>();
+ ClassDB::register_class<Curve>();
ClassDB::register_class<DynamicFontData>();
ClassDB::register_class<DynamicFont>();
@@ -663,5 +667,6 @@ void unregister_scene_types() {
SpatialMaterial::finish_shaders();
ParticlesMaterial::finish_shaders();
+ CanvasItemMaterial::finish_shaders();
SceneStringNames::free();
}
diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp
index 10c12c9411..006e7de562 100644
--- a/scene/resources/curve.cpp
+++ b/scene/resources/curve.cpp
@@ -380,6 +380,365 @@ Curve2D::Curve2D()
#endif
+Curve::Curve() {
+ _bake_resolution = 100;
+ _baked_cache_dirty = false;
+#ifdef TOOLS_ENABLED
+ _disable_set_data = false;
+#endif
+}
+
+int Curve::add_point(Vector2 p_pos, real_t left_tangent, real_t right_tangent) {
+ // Add a point and preserve order
+
+ // Curve bounds is in 0..1
+ if (p_pos.x > MAX_X)
+ p_pos.x = MAX_X;
+ else if (p_pos.x < MIN_X)
+ p_pos.x = MIN_X;
+
+ int ret = -1;
+
+ if (_points.size() == 0) {
+ _points.push_back(Point(p_pos, left_tangent, right_tangent));
+ ret = 0;
+
+ } else if (_points.size() == 1) {
+ // TODO Is the `else` able to handle this block already?
+
+ real_t diff = p_pos.x - _points[0].pos.x;
+
+ if (diff > 0) {
+ _points.push_back(Point(p_pos, left_tangent, right_tangent));
+ ret = 1;
+ } else {
+ _points.insert(0, Point(p_pos, left_tangent, right_tangent));
+ ret = 0;
+ }
+
+ } else {
+
+ int i = get_index(p_pos.x);
+
+ int nearest_index = i;
+ if (i + 1 < _points.size()) {
+ real_t diff0 = p_pos.x - _points[i].pos.x;
+ real_t diff1 = _points[i + 1].pos.x - p_pos.x;
+
+ if (diff1 < diff0)
+ nearest_index = i + 1;
+ }
+
+ if (i == 0 && p_pos.x < _points[0].pos.x) {
+ // Insert before anything else
+ _points.insert(0, Point(p_pos, left_tangent, right_tangent));
+ ret = 0;
+ } else {
+ // Insert between i and i+1
+ ++i;
+ _points.insert(i, Point(p_pos, left_tangent, right_tangent));
+ ret = i;
+ }
+ }
+
+ mark_dirty();
+
+ return ret;
+}
+
+int Curve::get_index(real_t offset) const {
+
+ // Lower-bound float binary search
+
+ int imin = 0;
+ int imax = _points.size() - 1;
+
+ while (imax - imin > 1) {
+ int m = (imin + imax) / 2;
+
+ real_t a = _points[m].pos.x;
+ real_t b = _points[m + 1].pos.x;
+
+ if (a < offset && b < offset) {
+ imin = m;
+
+ } else if (a > offset) {
+ imax = m;
+
+ } else {
+ return m;
+ }
+ }
+
+ // Will happen if the offset is out of bounds
+ if (offset > _points[imax].pos.x)
+ return imax;
+ return imin;
+}
+
+void Curve::clean_dupes() {
+
+ bool dirty = false;
+
+ for (int i = 1; i < _points.size(); ++i) {
+ real_t diff = _points[i - 1].pos.x - _points[i].pos.x;
+ if (diff <= CMP_EPSILON) {
+ _points.remove(i);
+ --i;
+ dirty = true;
+ }
+ }
+
+ if (dirty)
+ mark_dirty();
+}
+
+void Curve::set_point_left_tangent(int i, real_t tangent) {
+ ERR_FAIL_INDEX(i, _points.size());
+ _points[i].left_tangent = tangent;
+ mark_dirty();
+}
+
+void Curve::set_point_right_tangent(int i, real_t tangent) {
+ ERR_FAIL_INDEX(i, _points.size());
+ _points[i].right_tangent = tangent;
+ mark_dirty();
+}
+
+real_t Curve::get_point_left_tangent(int i) const {
+ ERR_FAIL_INDEX_V(i, _points.size(), 0);
+ return _points[i].left_tangent;
+}
+
+real_t Curve::get_point_right_tangent(int i) const {
+ ERR_FAIL_INDEX_V(i, _points.size(), 0);
+ return _points[i].right_tangent;
+}
+
+void Curve::remove_point(int p_index) {
+ ERR_FAIL_INDEX(p_index, _points.size());
+ _points.remove(p_index);
+ mark_dirty();
+}
+
+void Curve::clear_points() {
+ _points.clear();
+ mark_dirty();
+}
+
+void Curve::set_point_value(int p_index, real_t pos) {
+ ERR_FAIL_INDEX(p_index, _points.size());
+ _points[p_index].pos.y = pos;
+ mark_dirty();
+}
+
+int Curve::set_point_offset(int p_index, float offset) {
+ ERR_FAIL_INDEX_V(p_index, _points.size(), -1);
+ Point p = _points[p_index];
+ remove_point(p_index);
+ int i = add_point(Vector2(offset, p.pos.y));
+ _points[i].left_tangent = p.left_tangent;
+ _points[i].right_tangent = p.right_tangent;
+ return i;
+}
+
+Vector2 Curve::get_point_pos(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, _points.size(), Vector2(0, 0));
+ return _points[p_index].pos;
+}
+
+real_t Curve::interpolate(real_t offset) const {
+ if (_points.size() == 0)
+ return 0;
+ if (_points.size() == 1)
+ return _points[0].pos.y;
+
+ int i = get_index(offset);
+
+ if (i == _points.size() - 1)
+ return _points[i].pos.y;
+
+ real_t local = offset - _points[i].pos.x;
+
+ if (i == 0 && local <= 0)
+ return _points[0].pos.y;
+
+ return interpolate_local_nocheck(i, local);
+}
+
+real_t Curve::interpolate_local_nocheck(int index, real_t local_offset) const {
+
+ const Point a = _points[index];
+ const Point b = _points[index + 1];
+
+ // Cubic bezier
+
+ // ac-----bc
+ // / \
+ // / \ Here with a.right_tangent > 0
+ // / \ and b.left_tangent < 0
+ // / \
+ // a b
+ //
+ // |-d1--|-d2--|-d3--|
+ //
+ // d1 == d2 == d3 == d / 3
+
+ // Control points are chosen at equal distances
+ real_t d = b.pos.x - a.pos.x;
+ if (Math::abs(d) <= CMP_EPSILON)
+ return b.pos.y;
+ local_offset /= d;
+ d /= 3.0;
+ real_t yac = a.pos.y + d * a.right_tangent;
+ real_t ybc = b.pos.y - d * b.left_tangent;
+
+ real_t y = _bezier_interp(local_offset, a.pos.y, yac, ybc, b.pos.y);
+
+ return y;
+}
+
+void Curve::mark_dirty() {
+ _baked_cache_dirty = true;
+ emit_signal(CoreStringNames::get_singleton()->changed);
+}
+
+Array Curve::get_data() const {
+
+ Array output;
+ output.resize(_points.size() * 3);
+
+ for (int j = 0; j < _points.size(); ++j) {
+
+ const Point p = _points[j];
+ int i = j * 3;
+
+ output[i] = p.pos;
+ output[i + 1] = p.left_tangent;
+ output[i + 2] = p.right_tangent;
+ }
+
+ return output;
+}
+
+void Curve::set_data(Array input) {
+ ERR_FAIL_COND(input.size() % 3 != 0);
+
+#ifdef TOOLS_ENABLED
+ if (_disable_set_data)
+ return;
+#endif
+
+ _points.clear();
+
+ // Validate input
+ for (int i = 0; i < input.size(); i += 3) {
+ ERR_FAIL_COND(input[i].get_type() != Variant::VECTOR2);
+ ERR_FAIL_COND(input[i + 1].get_type() != Variant::REAL);
+ ERR_FAIL_COND(input[i + 2].get_type() != Variant::REAL);
+ }
+
+ _points.resize(input.size() / 3);
+
+ for (int j = 0; j < _points.size(); ++j) {
+
+ Point &p = _points[j];
+ int i = j * 3;
+
+ p.pos = input[i];
+ p.left_tangent = input[i + 1];
+ p.right_tangent = input[i + 2];
+ }
+
+ mark_dirty();
+}
+
+void Curve::bake() {
+ _baked_cache.clear();
+
+ _baked_cache.resize(_bake_resolution);
+
+ for (int i = 1; i < _bake_resolution - 1; ++i) {
+ real_t x = i / static_cast<real_t>(_bake_resolution);
+ real_t y = interpolate(x);
+ _baked_cache[i] = y;
+ }
+
+ if (_points.size() != 0) {
+ _baked_cache[0] = _points[0].pos.y;
+ _baked_cache[_baked_cache.size() - 1] = _points[_points.size() - 1].pos.y;
+ }
+
+ _baked_cache_dirty = false;
+}
+
+void Curve::set_bake_resolution(int p_resolution) {
+ ERR_FAIL_COND(p_resolution < 1);
+ ERR_FAIL_COND(p_resolution > 1000);
+ _bake_resolution = p_resolution;
+ _baked_cache_dirty = true;
+}
+
+real_t Curve::interpolate_baked(real_t offset) {
+ if (_baked_cache_dirty) {
+ // Last-second bake if not done already
+ bake();
+ }
+
+ // Special cases if the cache is too small
+ if (_baked_cache.size() == 0) {
+ if (_points.size() == 0)
+ return 0;
+ return _points[0].pos.y;
+ } else if (_baked_cache.size() == 1) {
+ return _baked_cache[0];
+ }
+
+ // Get interpolation index
+ real_t fi = offset * _baked_cache.size();
+ int i = Math::floor(fi);
+ if (i < 0) {
+ i = 0;
+ fi = 0;
+ } else if (i >= _baked_cache.size()) {
+ i = _baked_cache.size() - 1;
+ fi = 0;
+ }
+
+ // Interpolate
+ if (i + 1 < _baked_cache.size()) {
+ real_t t = fi - i;
+ return Math::lerp(_baked_cache[i], _baked_cache[i + 1], t);
+ } else {
+ return _baked_cache[_baked_cache.size() - 1];
+ }
+}
+
+void Curve::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("add_point", "pos", "left_tangent", "right_tangent"), &Curve::add_point, DEFVAL(0), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("remove_point", "index"), &Curve::remove_point);
+ ClassDB::bind_method(D_METHOD("clear_points"), &Curve::clear_points);
+ ClassDB::bind_method(D_METHOD("get_point_pos", "index"), &Curve::get_point_pos);
+ ClassDB::bind_method(D_METHOD("set_point_value", "index, y"), &Curve::set_point_value);
+ ClassDB::bind_method(D_METHOD("set_point_offset", "index, offset"), &Curve::set_point_value);
+ ClassDB::bind_method(D_METHOD("interpolate", "offset"), &Curve::interpolate);
+ ClassDB::bind_method(D_METHOD("interpolate_baked", "offset"), &Curve::interpolate_baked);
+ ClassDB::bind_method(D_METHOD("get_point_left_tangent", "index"), &Curve::get_point_left_tangent);
+ ClassDB::bind_method(D_METHOD("get_point_right_tangent", "index"), &Curve::get_point_left_tangent);
+ ClassDB::bind_method(D_METHOD("set_point_left_tangent", "index", "tangent"), &Curve::set_point_left_tangent);
+ ClassDB::bind_method(D_METHOD("set_point_right_tangent", "index", "tangent"), &Curve::set_point_left_tangent);
+ ClassDB::bind_method(D_METHOD("clean_dupes"), &Curve::clean_dupes);
+ ClassDB::bind_method(D_METHOD("bake"), &Curve::bake);
+ ClassDB::bind_method(D_METHOD("get_bake_resolution"), &Curve::get_bake_resolution);
+ ClassDB::bind_method(D_METHOD("set_bake_resolution", "resolution"), &Curve::set_bake_resolution);
+ ClassDB::bind_method(D_METHOD("_get_data"), &Curve::get_data);
+ ClassDB::bind_method(D_METHOD("_set_data", "data"), &Curve::set_data);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_resolution", PROPERTY_HINT_RANGE, "1,1000,1"), "set_bake_resolution", "get_bake_resolution");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_data", "_get_data");
+}
+
int Curve2D::get_point_count() const {
return points.size();
diff --git a/scene/resources/curve.h b/scene/resources/curve.h
index 17c0ac9f5e..63b9a07f07 100644
--- a/scene/resources/curve.h
+++ b/scene/resources/curve.h
@@ -82,6 +82,78 @@ public:
#endif
+// y(x) curve
+class Curve : public Resource {
+ GDCLASS(Curve, Resource)
+public:
+ static const int MIN_X = 0.f;
+ static const int MAX_X = 1.f;
+
+#ifdef TOOLS_ENABLED
+ bool _disable_set_data;
+#endif
+
+ struct Point {
+ Vector2 pos;
+ real_t left_tangent;
+ real_t right_tangent;
+
+ Point() {
+ left_tangent = 0;
+ right_tangent = 0;
+ }
+
+ Point(Vector2 p, real_t left = 0, real_t right = 0) {
+ pos = p;
+ left_tangent = left;
+ right_tangent = right;
+ }
+ };
+
+ Curve();
+
+ int get_point_count() const { return _points.size(); }
+
+ int add_point(Vector2 p_pos, real_t left_tangent = 0, real_t right_tangent = 0);
+ void remove_point(int p_index);
+ void clear_points();
+
+ int get_index(real_t offset) const;
+
+ void set_point_value(int p_index, real_t pos);
+ int set_point_offset(int p_index, float offset);
+ Vector2 get_point_pos(int p_index) const;
+
+ real_t interpolate(real_t offset) const;
+ real_t interpolate_local_nocheck(int index, real_t local_offset) const;
+
+ void clean_dupes();
+
+ void set_point_left_tangent(int i, real_t tangent);
+ void set_point_right_tangent(int i, real_t tangent);
+ real_t get_point_left_tangent(int i) const;
+ real_t get_point_right_tangent(int i) const;
+
+ Array get_data() const;
+ void set_data(Array input);
+
+ void bake();
+ int get_bake_resolution() const { return _bake_resolution; }
+ void set_bake_resolution(int p_interval);
+ real_t interpolate_baked(real_t offset);
+
+protected:
+ static void _bind_methods();
+
+private:
+ void mark_dirty();
+
+ Vector<Point> _points;
+ bool _baked_cache_dirty;
+ Vector<real_t> _baked_cache;
+ int _bake_resolution;
+};
+
class Curve2D : public Resource {
GDCLASS(Curve2D, Resource);
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index a3180ee1df..ef7011b2af 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -668,7 +668,11 @@ void ArrayMesh::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::DICTIONARY, "surfaces/" + itos(i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
p_list->push_back(PropertyInfo(Variant::STRING, "surface_" + itos(i + 1) + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
- p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_" + itos(i + 1) + "/material", PROPERTY_HINT_RESOURCE_TYPE, "Material", 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));
+ } else {
+ p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_" + itos(i + 1) + "/material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,SpatialMaterial", PROPERTY_USAGE_EDITOR));
+ }
}
p_list->push_back(PropertyInfo(Variant::RECT3, "custom_aabb/custom_aabb"));
@@ -692,6 +696,7 @@ void ArrayMesh::add_surface(uint32_t p_format, PrimitiveType p_primitive, const
Surface s;
s.aabb = p_aabb;
+ s.is_2d = p_format & ARRAY_FLAG_USE_2D_VERTICES;
surfaces.push_back(s);
_recompute_aabb();
@@ -709,7 +714,8 @@ void ArrayMesh::add_surface_from_arrays(PrimitiveType p_primitive, const Array &
/* make aABB? */ {
- PoolVector<Vector3> vertices = p_arrays[ARRAY_VERTEX];
+ Variant arr = p_arrays[ARRAY_VERTEX];
+ PoolVector<Vector3> vertices = arr;
int len = vertices.size();
ERR_FAIL_COND(len == 0);
PoolVector<Vector3>::Read r = vertices.read();
@@ -726,6 +732,7 @@ void ArrayMesh::add_surface_from_arrays(PrimitiveType p_primitive, const Array &
}
surfaces[surfaces.size() - 1].aabb = aabb;
+ surfaces[surfaces.size() - 1].is_2d = arr.get_type() == Variant::POOL_VECTOR2_ARRAY;
_recompute_aabb();
}
diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h
index 37fddf3aa6..f716b59fe9 100644
--- a/scene/resources/mesh.h
+++ b/scene/resources/mesh.h
@@ -147,6 +147,7 @@ private:
String name;
Rect3 aabb;
Ref<Material> material;
+ bool is_2d;
};
Vector<Surface> surfaces;
RID mesh;
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 2120b37497..7b393233f1 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -30,7 +30,9 @@
#include "texture.h"
#include "core/method_bind_ext.inc"
#include "core/os/os.h"
+#include "core_string_names.h"
#include "io/image_loader.h"
+
Size2 Texture::get_size() const {
return Size2(get_width(), get_height());
@@ -1376,254 +1378,126 @@ void CurveTexture::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_width", "width"), &CurveTexture::set_width);
- ClassDB::bind_method(D_METHOD("set_points", "points"), &CurveTexture::set_points);
- ClassDB::bind_method(D_METHOD("get_points"), &CurveTexture::get_points);
+ ClassDB::bind_method(D_METHOD("set_curve", "curve:Curve"), &CurveTexture::set_curve);
+ ClassDB::bind_method(D_METHOD("get_curve:Curve"), &CurveTexture::get_curve);
+
+ ClassDB::bind_method(D_METHOD("_update"), &CurveTexture::_update);
ADD_PROPERTY(PropertyInfo(Variant::REAL, "min", PROPERTY_HINT_RANGE, "-1024,1024"), "set_min", "get_min");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "max", PROPERTY_HINT_RANGE, "-1024,1024"), "set_max", "get_max");
ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "32,4096"), "set_width", "get_width");
- ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "points"), "set_points", "get_points");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve", "get_curve");
}
void CurveTexture::set_max(float p_max) {
- max = p_max;
+ _max = p_max;
emit_changed();
}
float CurveTexture::get_max() const {
- return max;
+ return _max;
}
void CurveTexture::set_min(float p_min) {
- min = p_min;
+ _min = p_min;
emit_changed();
}
float CurveTexture::get_min() const {
- return min;
+ return _min;
}
void CurveTexture::set_width(int p_width) {
ERR_FAIL_COND(p_width < 32 || p_width > 4096);
- width = p_width;
- if (points.size())
- set_points(points);
+ _width = p_width;
+ _update();
}
int CurveTexture::get_width() const {
- return width;
+ return _width;
}
-static void _plot_curve(const Vector2 &p_a, const Vector2 &p_b, const Vector2 &p_c, const Vector2 &p_d, float *p_heights, bool *p_useds, int p_width, float p_min, float p_max) {
-
- float geometry[4][4];
- float tmp1[4][4];
- float tmp2[4][4];
- float deltas[4][4];
- double x, dx, dx2, dx3;
- double y, dy, dy2, dy3;
- double d, d2, d3;
- int lastx;
- int newx;
- float lasty;
- float newy;
- int ntimes;
- int i, j;
-
- int xmax = p_width;
-
- /* construct the geometry matrix from the segment */
- for (i = 0; i < 4; i++) {
- geometry[i][2] = 0;
- geometry[i][3] = 0;
- }
+void CurveTexture::ensure_default_setup() {
- geometry[0][0] = (p_a[0] * xmax);
- geometry[1][0] = (p_b[0] * xmax);
- geometry[2][0] = (p_c[0] * xmax);
- geometry[3][0] = (p_d[0] * xmax);
-
- geometry[0][1] = (p_a[1]);
- geometry[1][1] = (p_b[1]);
- geometry[2][1] = (p_c[1]);
- geometry[3][1] = (p_d[1]);
-
- /* subdivide the curve ntimes (1000) times */
- ntimes = 4 * xmax;
- /* ntimes can be adjusted to give a finer or coarser curve */
- d = 1.0 / ntimes;
- d2 = d * d;
- d3 = d * d * d;
-
- /* construct a temporary matrix for determining the forward differencing deltas */
- tmp2[0][0] = 0;
- tmp2[0][1] = 0;
- tmp2[0][2] = 0;
- tmp2[0][3] = 1;
- tmp2[1][0] = d3;
- tmp2[1][1] = d2;
- tmp2[1][2] = d;
- tmp2[1][3] = 0;
- tmp2[2][0] = 6 * d3;
- tmp2[2][1] = 2 * d2;
- tmp2[2][2] = 0;
- tmp2[2][3] = 0;
- tmp2[3][0] = 6 * d3;
- tmp2[3][1] = 0;
- tmp2[3][2] = 0;
- tmp2[3][3] = 0;
-
- /* compose the basis and geometry matrices */
-
- static const float CR_basis[4][4] = {
- { -0.5, 1.5, -1.5, 0.5 },
- { 1.0, -2.5, 2.0, -0.5 },
- { -0.5, 0.0, 0.5, 0.0 },
- { 0.0, 1.0, 0.0, 0.0 },
- };
-
- for (i = 0; i < 4; i++) {
- for (j = 0; j < 4; j++) {
- tmp1[i][j] = (CR_basis[i][0] * geometry[0][j] +
- CR_basis[i][1] * geometry[1][j] +
- CR_basis[i][2] * geometry[2][j] +
- CR_basis[i][3] * geometry[3][j]);
- }
+ if (_curve.is_null()) {
+ Ref<Curve> curve = Ref<Curve>(memnew(Curve));
+ curve->add_point(Vector2(0, 1));
+ curve->add_point(Vector2(1, 1));
+ set_curve(curve);
}
- /* compose the above results to get the deltas matrix */
-
- for (i = 0; i < 4; i++) {
- for (j = 0; j < 4; j++) {
- deltas[i][j] = (tmp2[i][0] * tmp1[0][j] +
- tmp2[i][1] * tmp1[1][j] +
- tmp2[i][2] * tmp1[2][j] +
- tmp2[i][3] * tmp1[3][j]);
- }
+
+ if (get_min() == 0 && get_max() == 1) {
+ set_max(32);
}
+}
- /* extract the x deltas */
- x = deltas[0][0];
- dx = deltas[1][0];
- dx2 = deltas[2][0];
- dx3 = deltas[3][0];
-
- /* extract the y deltas */
- y = deltas[0][1];
- dy = deltas[1][1];
- dy2 = deltas[2][1];
- dy3 = deltas[3][1];
-
- lastx = CLAMP(x, 0, xmax);
- lasty = y;
-
- p_heights[lastx] = lasty;
- p_useds[lastx] = true;
-
- /* loop over the curve */
- for (i = 0; i < ntimes; i++) {
- /* increment the x values */
- x += dx;
- dx += dx2;
- dx2 += dx3;
-
- /* increment the y values */
- y += dy;
- dy += dy2;
- dy2 += dy3;
-
- newx = CLAMP((Math::round(x)), 0, xmax);
- newy = CLAMP(y, p_min, p_max);
-
- /* if this point is different than the last one...then draw it */
- if ((lastx != newx) || (lasty != newy)) {
- p_useds[newx] = true;
- p_heights[newx] = newy;
+void CurveTexture::set_curve(Ref<Curve> p_curve) {
+ if (_curve != p_curve) {
+ if (_curve.is_valid()) {
+ _curve->disconnect(CoreStringNames::get_singleton()->changed, this, "_update");
}
-
- lastx = newx;
- lasty = newy;
+ _curve = p_curve;
+ if (_curve.is_valid()) {
+ _curve->connect(CoreStringNames::get_singleton()->changed, this, "_update");
+ }
+ _update();
}
}
-void CurveTexture::set_points(const PoolVector<Vector2> &p_points) {
-
- points = p_points;
+void CurveTexture::_update() {
PoolVector<uint8_t> data;
- PoolVector<bool> used;
- data.resize(width * sizeof(float));
- used.resize(width);
+ data.resize(_width * sizeof(float));
+
+ // The array is locked in that scope
{
PoolVector<uint8_t>::Write wd8 = data.write();
float *wd = (float *)wd8.ptr();
- PoolVector<bool>::Write wu = used.write();
- int pc = p_points.size();
- PoolVector<Vector2>::Read pr = p_points.read();
- for (int i = 0; i < width; i++) {
- wd[i] = 0.0;
- wu[i] = false;
- }
-
- Vector2 prev = Vector2(0, 0);
- Vector2 prev2 = Vector2(0, 0);
-
- for (int i = -1; i < pc; i++) {
-
- Vector2 next;
- Vector2 next2;
- if (i + 1 >= pc) {
- next = Vector2(1, 0);
- } else {
- next = Vector2(pr[i + 1].x, pr[i + 1].y);
+ if (_curve.is_valid()) {
+ Curve &curve = **_curve;
+ float height = _max - _min;
+ for (int i = 0; i < _width; ++i) {
+ float t = i / static_cast<float>(_width);
+ float v = curve.interpolate_baked(t);
+ wd[i] = CLAMP(_min + v * height, _min, _max);
}
- if (i + 2 >= pc) {
- next2 = Vector2(1, 0);
- } else {
- next2 = Vector2(pr[i + 2].x, pr[i + 2].y);
+ } else {
+ for (int i = 0; i < _width; ++i) {
+ wd[i] = 0;
}
-
- /*if (i==-1 && prev.offset==next.offset) {
- prev=next;
- continue;
- }*/
-
- _plot_curve(prev2, prev, next, next2, wd, wu.ptr(), width, min, max);
-
- prev2 = prev;
- prev = next;
}
}
- Ref<Image> image = memnew(Image(width, 1, false, Image::FORMAT_RF, data));
+ Ref<Image> image = memnew(Image(_width, 1, false, Image::FORMAT_RF, data));
- VS::get_singleton()->texture_allocate(texture, width, 1, Image::FORMAT_RF, VS::TEXTURE_FLAG_FILTER);
- VS::get_singleton()->texture_set_data(texture, image);
+ VS::get_singleton()->texture_allocate(_texture, _width, 1, Image::FORMAT_RF, VS::TEXTURE_FLAG_FILTER);
+ VS::get_singleton()->texture_set_data(_texture, image);
emit_changed();
}
-PoolVector<Vector2> CurveTexture::get_points() const {
+Ref<Curve> CurveTexture::get_curve() const {
- return points;
+ return _curve;
}
RID CurveTexture::get_rid() const {
- return texture;
+ return _texture;
}
CurveTexture::CurveTexture() {
- max = 1;
- min = 0;
- width = 2048;
- texture = VS::get_singleton()->texture_create();
+ _max = 1;
+ _min = 0;
+ _width = 2048;
+ _texture = VS::get_singleton()->texture_create();
}
CurveTexture::~CurveTexture() {
- VS::get_singleton()->free(texture);
+ VS::get_singleton()->free(_texture);
}
//////////////////
diff --git a/scene/resources/texture.h b/scene/resources/texture.h
index 2b82dbd21f..2c36cdef87 100644
--- a/scene/resources/texture.h
+++ b/scene/resources/texture.h
@@ -30,6 +30,7 @@
#ifndef TEXTURE_H
#define TEXTURE_H
+#include "curve.h"
#include "io/resource_loader.h"
#include "math_2d.h"
#include "resource.h"
@@ -389,20 +390,22 @@ public:
~CubeMap();
};
-VARIANT_ENUM_CAST(CubeMap::Flags);
-VARIANT_ENUM_CAST(CubeMap::Side);
-VARIANT_ENUM_CAST(CubeMap::Storage);
+VARIANT_ENUM_CAST(CubeMap::Flags)
+VARIANT_ENUM_CAST(CubeMap::Side)
+VARIANT_ENUM_CAST(CubeMap::Storage)
class CurveTexture : public Texture {
- GDCLASS(CurveTexture, Texture);
- RES_BASE_EXTENSION("curvetex");
+ GDCLASS(CurveTexture, Texture)
+ RES_BASE_EXTENSION("curvetex")
private:
- RID texture;
- PoolVector<Vector2> points;
- float min, max;
- int width;
+ RID _texture;
+ Ref<Curve> _curve;
+ float _min, _max;
+ int _width;
+
+ void _update();
protected:
static void _bind_methods();
@@ -417,8 +420,10 @@ public:
void set_width(int p_width);
int get_width() const;
- void set_points(const PoolVector<Vector2> &p_points);
- PoolVector<Vector2> get_points() const;
+ void ensure_default_setup();
+
+ void set_curve(Ref<Curve> p_curve);
+ Ref<Curve> get_curve() const;
virtual RID get_rid() const;
diff --git a/servers/visual/shader_types.cpp b/servers/visual/shader_types.cpp
index 81139ecc1c..42f1a98826 100644
--- a/servers/visual/shader_types.cpp
+++ b/servers/visual/shader_types.cpp
@@ -212,6 +212,7 @@ ShaderTypes::ShaderTypes() {
shader_modes[VS::SHADER_PARTICLES].functions["vertex"]["NUMBER"] = ShaderLanguage::TYPE_UINT;
shader_modes[VS::SHADER_PARTICLES].functions["vertex"]["INDEX"] = ShaderLanguage::TYPE_INT;
shader_modes[VS::SHADER_PARTICLES].functions["vertex"]["EMISSION_TRANSFORM"] = ShaderLanguage::TYPE_MAT4;
+ shader_modes[VS::SHADER_PARTICLES].functions["vertex"]["RANDOM_SEED"] = ShaderLanguage::TYPE_UINT;
shader_modes[VS::SHADER_PARTICLES].modes.insert("billboard");
shader_modes[VS::SHADER_PARTICLES].modes.insert("disable_force");
diff --git a/servers/visual/visual_server_canvas.cpp b/servers/visual/visual_server_canvas.cpp
index 616990b311..1227863b72 100644
--- a/servers/visual/visual_server_canvas.cpp
+++ b/servers/visual/visual_server_canvas.cpp
@@ -58,6 +58,12 @@ void VisualServerCanvas::_render_canvas_item(Item *p_canvas_item, const Transfor
if (!ci->visible)
return;
+ if (p_canvas_item->children_order_dirty) {
+
+ p_canvas_item->child_items.sort_custom<ItemIndexSort>();
+ p_canvas_item->children_order_dirty = false;
+ }
+
Rect2 rect = ci->get_rect();
Transform2D xform = p_transform * ci->xform;
Rect2 global_rect = xform.xform(rect);
@@ -171,6 +177,12 @@ void VisualServerCanvas::render_canvas(Canvas *p_canvas, const Transform2D &p_tr
VSG::canvas_render->canvas_begin();
+ if (p_canvas->children_order_dirty) {
+
+ p_canvas->child_items.sort();
+ p_canvas->children_order_dirty = false;
+ }
+
int l = p_canvas->child_items.size();
Canvas::ChildItem *ci = p_canvas->child_items.ptr();
diff --git a/servers/visual/visual_server_canvas.h b/servers/visual/visual_server_canvas.h
index f78ef635f4..57c7515367 100644
--- a/servers/visual/visual_server_canvas.h
+++ b/servers/visual/visual_server_canvas.h
@@ -63,6 +63,14 @@ public:
}
};
+ struct ItemIndexSort {
+
+ _FORCE_INLINE_ bool operator()(const Item *p_left, const Item *p_right) const {
+
+ return p_left->index < p_right->index;
+ }
+ };
+
struct ItemPtrSort {
_FORCE_INLINE_ bool operator()(const Item *p_left, const Item *p_right) const {