summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/animated_sprite.cpp1
-rw-r--r--scene/2d/animated_sprite.h2
-rw-r--r--scene/2d/audio_stream_player_2d.cpp12
-rw-r--r--scene/2d/canvas_item.cpp214
-rw-r--r--scene/2d/canvas_item.h94
-rw-r--r--scene/2d/collision_object_2d.cpp339
-rw-r--r--scene/2d/collision_object_2d.h62
-rw-r--r--scene/2d/collision_polygon_2d.cpp186
-rw-r--r--scene/2d/collision_polygon_2d.h32
-rw-r--r--scene/2d/collision_shape_2d.cpp176
-rw-r--r--scene/2d/collision_shape_2d.h26
-rw-r--r--scene/2d/particles_2d.cpp1185
-rw-r--r--scene/2d/particles_2d.h247
-rw-r--r--scene/2d/physics_body_2d.cpp452
-rw-r--r--scene/2d/physics_body_2d.h86
-rw-r--r--scene/3d/mesh_instance.cpp2
-rw-r--r--scene/3d/particles.cpp222
-rw-r--r--scene/3d/particles.h12
-rw-r--r--scene/3d/path.cpp66
-rw-r--r--scene/3d/scenario_fx.cpp35
-rw-r--r--scene/3d/scenario_fx.h4
-rw-r--r--scene/3d/sprite_3d.cpp8
-rw-r--r--scene/3d/sprite_3d.h1
-rw-r--r--scene/3d/visual_instance.cpp2
-rw-r--r--scene/gui/input_action.cpp2
-rw-r--r--scene/gui/patch_9_rect.cpp36
-rw-r--r--scene/gui/patch_9_rect.h17
-rw-r--r--scene/gui/popup_menu.cpp21
-rw-r--r--scene/gui/popup_menu.h4
-rw-r--r--scene/gui/rich_text_label.cpp4
-rw-r--r--scene/main/viewport.cpp11
-rw-r--r--scene/register_scene_types.cpp7
-rw-r--r--scene/resources/curve.cpp359
-rw-r--r--scene/resources/curve.h72
-rw-r--r--scene/resources/environment.cpp3
-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
39 files changed, 1996 insertions, 2287 deletions
diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp
index 6a3238b64e..a1ab51b3c8 100644
--- a/scene/2d/animated_sprite.cpp
+++ b/scene/2d/animated_sprite.cpp
@@ -30,7 +30,6 @@
#include "animated_sprite.h"
#include "os/os.h"
#include "scene/scene_string_names.h"
-#include "scene/scene_string_names.h"
#define NORMAL_SUFFIX "_normal"
diff --git a/scene/2d/animated_sprite.h b/scene/2d/animated_sprite.h
index 2274450647..80defac079 100644
--- a/scene/2d/animated_sprite.h
+++ b/scene/2d/animated_sprite.h
@@ -99,7 +99,7 @@ public:
const Map<StringName, Anim>::Element *EN = animations.find(E->get().normal_name);
- if (p_idx >= EN->get().frames.size())
+ if (!EN || p_idx >= EN->get().frames.size())
return Ref<Texture>();
return EN->get().frames[p_idx];
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 471f529713..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 {
@@ -417,14 +607,22 @@ void CanvasItem::draw_line(const Point2 &p_from, const Point2 &p_to, const Color
VisualServer::get_singleton()->canvas_item_add_line(canvas_item, p_from, p_to, p_color, p_width, p_antialiased);
}
-void CanvasItem::draw_rect(const Rect2 &p_rect, const Color &p_color) {
+void CanvasItem::draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled) {
if (!drawing) {
ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
ERR_FAIL();
}
- VisualServer::get_singleton()->canvas_item_add_rect(canvas_item, p_rect, p_color);
+ if (p_filled) {
+
+ VisualServer::get_singleton()->canvas_item_add_rect(canvas_item, p_rect, p_color);
+ } else {
+ VisualServer::get_singleton()->canvas_item_add_line(canvas_item, p_rect.position, p_rect.position + Size2(p_rect.size.width, 0), p_color);
+ VisualServer::get_singleton()->canvas_item_add_line(canvas_item, p_rect.position, p_rect.position + Size2(0, p_rect.size.height), p_color);
+ VisualServer::get_singleton()->canvas_item_add_line(canvas_item, p_rect.position + Point2(0, p_rect.size.height), p_rect.position + p_rect.size, p_color);
+ VisualServer::get_singleton()->canvas_item_add_line(canvas_item, p_rect.position + Point2(p_rect.size.width, 0), p_rect.position + p_rect.size, p_color);
+ }
}
void CanvasItem::draw_circle(const Point2 &p_pos, float p_radius, const Color &p_color) {
@@ -657,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;
@@ -678,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;
}
@@ -754,7 +952,7 @@ void CanvasItem::_bind_methods() {
//ClassDB::bind_method(D_METHOD("get_transform"),&CanvasItem::get_transform);
ClassDB::bind_method(D_METHOD("draw_line", "from", "to", "color", "width", "antialiased"), &CanvasItem::draw_line, DEFVAL(1.0), DEFVAL(false));
- ClassDB::bind_method(D_METHOD("draw_rect", "rect", "color"), &CanvasItem::draw_rect);
+ ClassDB::bind_method(D_METHOD("draw_rect", "rect", "color", "filled"), &CanvasItem::draw_rect, DEFVAL(true));
ClassDB::bind_method(D_METHOD("draw_circle", "pos", "radius", "color"), &CanvasItem::draw_circle);
ClassDB::bind_method(D_METHOD("draw_texture", "texture:Texture", "pos", "modulate", "normal_map:Texture"), &CanvasItem::draw_texture, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(Variant()));
ClassDB::bind_method(D_METHOD("draw_texture_rect", "texture:Texture", "rect", "tile", "modulate", "transpose", "normal_map:Texture"), &CanvasItem::draw_texture_rect, DEFVAL(Color(1, 1, 1)), DEFVAL(false), DEFVAL(Variant()));
@@ -780,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);
@@ -807,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 595dd03057..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;
@@ -156,7 +242,7 @@ public:
/* DRAWING API */
void draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width = 1.0, bool p_antialiased = false);
- void draw_rect(const Rect2 &p_rect, const Color &p_color);
+ void draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled = true);
void draw_circle(const Point2 &p_pos, float p_radius, const Color &p_color);
void draw_texture(const Ref<Texture> &p_texture, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1, 1), const Ref<Texture> &p_normal_map = Ref<Texture>());
void draw_texture_rect(const Ref<Texture> &p_texture, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture> &p_normal_map = Ref<Texture>());
@@ -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/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp
index caf9cf201a..c5c274e225 100644
--- a/scene/2d/collision_object_2d.cpp
+++ b/scene/2d/collision_object_2d.cpp
@@ -31,18 +31,6 @@
#include "scene/scene_string_names.h"
#include "servers/physics_2d_server.h"
-void CollisionObject2D::_update_shapes_from_children() {
-
- shapes.clear();
- for (int i = 0; i < get_child_count(); i++) {
-
- Node *n = get_child(i);
- n->call("_add_to_collision_object", this);
- }
-
- _update_shapes();
-}
-
void CollisionObject2D::_notification(int p_what) {
switch (p_what) {
@@ -88,82 +76,197 @@ void CollisionObject2D::_notification(int p_what) {
}
}
-void CollisionObject2D::_update_shapes() {
+uint32_t CollisionObject2D::create_shape_owner(Object *p_owner) {
- if (!rid.is_valid())
- return;
+ ShapeData sd;
+ uint32_t id;
+
+ if (shapes.size() == 0) {
+ id = 1;
+ } else {
+ id = shapes.back()->key() + 1;
+ }
+
+ sd.owner = p_owner;
+
+ shapes[id] = sd;
+
+ return id;
+}
+
+void CollisionObject2D::remove_shape_owner(uint32_t owner) {
+
+ ERR_FAIL_COND(!shapes.has(owner));
+
+ shape_owner_clear_shapes(owner);
+
+ shapes.erase(owner);
+}
+
+void CollisionObject2D::shape_owner_set_disabled(uint32_t p_owner, bool p_disabled) {
+ ERR_FAIL_COND(!shapes.has(p_owner));
+
+ ShapeData &sd = shapes[p_owner];
+ sd.disabled = p_disabled;
+ for (int i = 0; i < sd.shapes.size(); i++) {
+ if (area) {
+ Physics2DServer::get_singleton()->area_set_shape_disabled(rid, sd.shapes[i].index, p_disabled);
+ } else {
+ Physics2DServer::get_singleton()->body_set_shape_disabled(rid, sd.shapes[i].index, p_disabled);
+ }
+ }
+}
+
+bool CollisionObject2D::is_shape_owner_disabled(uint32_t p_owner) const {
+
+ ERR_FAIL_COND_V(!shapes.has(p_owner), false);
+
+ return shapes[p_owner].disabled;
+}
+
+void CollisionObject2D::shape_owner_set_one_way_collision(uint32_t p_owner, bool p_enable) {
if (area)
- Physics2DServer::get_singleton()->area_clear_shapes(rid);
- else
- Physics2DServer::get_singleton()->body_clear_shapes(rid);
-
- for (int i = 0; i < shapes.size(); i++) {
-
- if (shapes[i].shape.is_null())
- continue;
- if (area)
- Physics2DServer::get_singleton()->area_add_shape(rid, shapes[i].shape->get_rid(), shapes[i].xform);
- else {
- Physics2DServer::get_singleton()->body_add_shape(rid, shapes[i].shape->get_rid(), shapes[i].xform);
- if (shapes[i].trigger)
- Physics2DServer::get_singleton()->body_set_shape_as_trigger(rid, i, shapes[i].trigger);
+ return; //not for areas
+
+ ERR_FAIL_COND(!shapes.has(p_owner));
+
+ ShapeData &sd = shapes[p_owner];
+ sd.one_way_collision = p_enable;
+ for (int i = 0; i < sd.shapes.size(); i++) {
+ Physics2DServer::get_singleton()->body_set_shape_as_one_way_collision(rid, sd.shapes[i].index, p_enable);
+ }
+}
+
+bool CollisionObject2D::is_shape_owner_one_way_collision_enabled(uint32_t p_owner) const {
+
+ ERR_FAIL_COND_V(!shapes.has(p_owner), false);
+
+ return shapes[p_owner].one_way_collision;
+}
+
+void CollisionObject2D::get_shape_owners(List<uint32_t> *r_owners) {
+
+ for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) {
+ r_owners->push_back(E->key());
+ }
+}
+
+void CollisionObject2D::shape_owner_set_transform(uint32_t p_owner, const Transform2D &p_transform) {
+
+ ERR_FAIL_COND(!shapes.has(p_owner));
+
+ ShapeData &sd = shapes[p_owner];
+ sd.xform = p_transform;
+ for (int i = 0; i < sd.shapes.size(); i++) {
+ if (area) {
+ Physics2DServer::get_singleton()->area_set_shape_transform(rid, sd.shapes[i].index, p_transform);
+ } else {
+ Physics2DServer::get_singleton()->body_set_shape_transform(rid, sd.shapes[i].index, p_transform);
}
}
}
+Transform2D CollisionObject2D::shape_owner_get_transform(uint32_t p_owner) const {
-bool CollisionObject2D::_set(const StringName &p_name, const Variant &p_value) {
- String name = p_name;
+ ERR_FAIL_COND_V(!shapes.has(p_owner), Transform2D());
- if (name.begins_with("shapes/")) {
+ return shapes[p_owner].xform;
+}
- int idx = name.get_slicec('/', 1).to_int();
- String what = name.get_slicec('/', 2);
- if (what == "shape") {
- if (idx >= shapes.size())
- add_shape(RefPtr(p_value));
- else
- set_shape(idx, RefPtr(p_value));
- } else if (what == "transform")
- set_shape_transform(idx, p_value);
- else if (what == "trigger")
- set_shape_as_trigger(idx, p_value);
- } else
- return false;
-
- return true;
+Object *CollisionObject2D::shape_owner_get_owner(uint32_t p_owner) const {
+
+ ERR_FAIL_COND_V(!shapes.has(p_owner), NULL);
+
+ return shapes[p_owner].owner;
+}
+
+void CollisionObject2D::shape_owner_add_shape(uint32_t p_owner, const Ref<Shape2D> &p_shape) {
+
+ ERR_FAIL_COND(!shapes.has(p_owner));
+ ERR_FAIL_COND(p_shape.is_null());
+
+ ShapeData &sd = shapes[p_owner];
+ ShapeData::Shape s;
+ s.index = total_subshapes;
+ s.shape = p_shape;
+ if (area) {
+ Physics2DServer::get_singleton()->area_add_shape(rid, p_shape->get_rid(), sd.xform);
+ } else {
+ Physics2DServer::get_singleton()->body_add_shape(rid, p_shape->get_rid(), sd.xform);
+ }
+ sd.shapes.push_back(s);
+
+ total_subshapes++;
+}
+int CollisionObject2D::shape_owner_get_shape_count(uint32_t p_owner) const {
+
+ ERR_FAIL_COND_V(!shapes.has(p_owner), 0);
+
+ return shapes[p_owner].shapes.size();
+}
+Ref<Shape> CollisionObject2D::shape_owner_get_shape(uint32_t p_owner, int p_shape) const {
+
+ ERR_FAIL_COND_V(!shapes.has(p_owner), Ref<Shape>());
+ ERR_FAIL_INDEX_V(p_shape, shapes[p_owner].shapes.size(), Ref<Shape>());
+
+ return shapes[p_owner].shapes[p_shape].shape;
+}
+int CollisionObject2D::shape_owner_get_shape_index(uint32_t p_owner, int p_shape) const {
+
+ ERR_FAIL_COND_V(!shapes.has(p_owner), -1);
+ ERR_FAIL_INDEX_V(p_shape, shapes[p_owner].shapes.size(), -1);
+
+ return shapes[p_owner].shapes[p_shape].index;
}
-bool CollisionObject2D::_get(const StringName &p_name, Variant &r_ret) const {
+void CollisionObject2D::shape_owner_remove_shape(uint32_t p_owner, int p_shape) {
+
+ ERR_FAIL_COND(!shapes.has(p_owner));
+ ERR_FAIL_INDEX(p_shape, shapes[p_owner].shapes.size());
+
+ int index_to_remove = shapes[p_owner].shapes[p_shape].index;
+ if (area) {
+ Physics2DServer::get_singleton()->area_remove_shape(rid, index_to_remove);
+ } else {
+ Physics2DServer::get_singleton()->body_remove_shape(rid, index_to_remove);
+ }
+
+ shapes[p_owner].shapes.remove(p_shape);
+
+ for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) {
+ for (int i = 0; i < E->get().shapes.size(); i++) {
+ if (E->get().shapes[i].index > index_to_remove) {
+ E->get().shapes[i].index -= 1;
+ }
+ }
+ }
- String name = p_name;
+ total_subshapes--;
+}
- if (name.begins_with("shapes/")) {
+void CollisionObject2D::shape_owner_clear_shapes(uint32_t p_owner) {
- int idx = name.get_slicec('/', 1).to_int();
- String what = name.get_slicec('/', 2);
- if (what == "shape")
- r_ret = get_shape(idx);
- else if (what == "transform")
- r_ret = get_shape_transform(idx);
- else if (what == "trigger")
- r_ret = is_shape_set_as_trigger(idx);
- } else
- return false;
+ ERR_FAIL_COND(!shapes.has(p_owner));
- return true;
+ while (shape_owner_get_shape_count(p_owner) > 0) {
+ shape_owner_remove_shape(p_owner, 0);
+ }
}
-void CollisionObject2D::_get_property_list(List<PropertyInfo> *p_list) const {
+uint32_t CollisionObject2D::shape_find_owner(int p_shape_index) const {
- //p_list->push_back( PropertyInfo(Variant::INT,"shape_count",PROPERTY_HINT_RANGE,"0,256,1",PROPERTY_USAGE_NOEDITOR|PROPERTY_USAGE_NO_INSTANCE_STATE) );
+ ERR_FAIL_INDEX_V(p_shape_index, total_subshapes, 0);
- for (int i = 0; i < shapes.size(); i++) {
- String path = "shapes/" + itos(i) + "/";
- p_list->push_back(PropertyInfo(Variant::OBJECT, path + "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape2D", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_NO_INSTANCE_STATE));
- p_list->push_back(PropertyInfo(Variant::TRANSFORM, path + "transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_NO_INSTANCE_STATE));
- p_list->push_back(PropertyInfo(Variant::BOOL, path + "trigger", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_NO_INSTANCE_STATE));
+ for (const Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) {
+ for (int i = 0; i < E->get().shapes.size(); i++) {
+ if (E->get().shapes[i].index == p_shape_index) {
+ return E->key();
+ }
+ }
}
+
+ //in theory it should be unreachable
+ return 0;
}
void CollisionObject2D::set_pickable(bool p_enabled) {
@@ -216,16 +319,6 @@ void CollisionObject2D::_update_pickable() {
void CollisionObject2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("add_shape", "shape:Shape2D", "transform"), &CollisionObject2D::add_shape, DEFVAL(Transform2D()));
- ClassDB::bind_method(D_METHOD("get_shape_count"), &CollisionObject2D::get_shape_count);
- ClassDB::bind_method(D_METHOD("set_shape", "shape_idx", "shape:Shape"), &CollisionObject2D::set_shape);
- ClassDB::bind_method(D_METHOD("set_shape_transform", "shape_idx", "transform"), &CollisionObject2D::set_shape_transform);
- ClassDB::bind_method(D_METHOD("set_shape_as_trigger", "shape_idx", "enable"), &CollisionObject2D::set_shape_as_trigger);
- ClassDB::bind_method(D_METHOD("get_shape:Shape2D", "shape_idx"), &CollisionObject2D::get_shape);
- ClassDB::bind_method(D_METHOD("get_shape_transform", "shape_idx"), &CollisionObject2D::get_shape_transform);
- ClassDB::bind_method(D_METHOD("is_shape_set_as_trigger", "shape_idx"), &CollisionObject2D::is_shape_set_as_trigger);
- ClassDB::bind_method(D_METHOD("remove_shape", "shape_idx"), &CollisionObject2D::remove_shape);
- ClassDB::bind_method(D_METHOD("clear_shapes"), &CollisionObject2D::clear_shapes);
ClassDB::bind_method(D_METHOD("get_rid"), &CollisionObject2D::get_rid);
ClassDB::bind_method(D_METHOD("set_pickable", "enabled"), &CollisionObject2D::set_pickable);
@@ -242,100 +335,13 @@ void CollisionObject2D::_bind_methods() {
ADD_GROUP("", "");
}
-void CollisionObject2D::add_shape(const Ref<Shape2D> &p_shape, const Transform2D &p_transform) {
-
- ERR_FAIL_COND(p_shape.is_null());
-
- ShapeData sdata;
- sdata.shape = p_shape;
- sdata.xform = p_transform;
- sdata.trigger = false;
-
- if (area)
- Physics2DServer::get_singleton()->area_add_shape(get_rid(), p_shape->get_rid(), p_transform);
- else
- Physics2DServer::get_singleton()->body_add_shape(get_rid(), p_shape->get_rid(), p_transform);
-
- shapes.push_back(sdata);
-}
-int CollisionObject2D::get_shape_count() const {
-
- return shapes.size();
-}
-void CollisionObject2D::set_shape(int p_shape_idx, const Ref<Shape2D> &p_shape) {
-
- ERR_FAIL_INDEX(p_shape_idx, shapes.size());
- ERR_FAIL_COND(p_shape.is_null());
-
- shapes[p_shape_idx].shape = p_shape;
- if (area)
- Physics2DServer::get_singleton()->area_set_shape(get_rid(), p_shape_idx, p_shape->get_rid());
- else
- Physics2DServer::get_singleton()->body_set_shape(get_rid(), p_shape_idx, p_shape->get_rid());
-
- //_update_shapes();
-}
-
-void CollisionObject2D::set_shape_transform(int p_shape_idx, const Transform2D &p_transform) {
-
- ERR_FAIL_INDEX(p_shape_idx, shapes.size());
- shapes[p_shape_idx].xform = p_transform;
-
- if (area)
- Physics2DServer::get_singleton()->area_set_shape_transform(get_rid(), p_shape_idx, p_transform);
- else
- Physics2DServer::get_singleton()->body_set_shape_transform(get_rid(), p_shape_idx, p_transform);
-
- //_update_shapes();
-}
-
-Ref<Shape2D> CollisionObject2D::get_shape(int p_shape_idx) const {
-
- ERR_FAIL_INDEX_V(p_shape_idx, shapes.size(), Ref<Shape2D>());
- return shapes[p_shape_idx].shape;
-}
-Transform2D CollisionObject2D::get_shape_transform(int p_shape_idx) const {
-
- ERR_FAIL_INDEX_V(p_shape_idx, shapes.size(), Transform2D());
- return shapes[p_shape_idx].xform;
-}
-void CollisionObject2D::remove_shape(int p_shape_idx) {
-
- ERR_FAIL_INDEX(p_shape_idx, shapes.size());
- shapes.remove(p_shape_idx);
-
- _update_shapes();
-}
-
-void CollisionObject2D::set_shape_as_trigger(int p_shape_idx, bool p_trigger) {
-
- ERR_FAIL_INDEX(p_shape_idx, shapes.size());
- shapes[p_shape_idx].trigger = p_trigger;
- if (!area && rid.is_valid()) {
-
- Physics2DServer::get_singleton()->body_set_shape_as_trigger(rid, p_shape_idx, p_trigger);
- }
-}
-
-bool CollisionObject2D::is_shape_set_as_trigger(int p_shape_idx) const {
-
- ERR_FAIL_INDEX_V(p_shape_idx, shapes.size(), false);
- return shapes[p_shape_idx].trigger;
-}
-
-void CollisionObject2D::clear_shapes() {
-
- shapes.clear();
-
- _update_shapes();
-}
-
CollisionObject2D::CollisionObject2D(RID p_rid, bool p_area) {
rid = p_rid;
area = p_area;
pickable = true;
set_notify_transform(true);
+ total_subshapes = 0;
if (p_area) {
@@ -348,6 +354,7 @@ CollisionObject2D::CollisionObject2D(RID p_rid, bool p_area) {
CollisionObject2D::CollisionObject2D() {
//owner=
+
set_notify_transform(true);
}
diff --git a/scene/2d/collision_object_2d.h b/scene/2d/collision_object_2d.h
index 4d4611afd1..deffe8a002 100644
--- a/scene/2d/collision_object_2d.h
+++ b/scene/2d/collision_object_2d.h
@@ -35,37 +35,40 @@
class CollisionObject2D : public Node2D {
- GDCLASS(CollisionObject2D, Node2D);
+ GDCLASS(CollisionObject2D, Node2D)
bool area;
RID rid;
bool pickable;
struct ShapeData {
+
+ Object *owner;
Transform2D xform;
- Ref<Shape2D> shape;
- bool trigger;
+ struct Shape {
+ Ref<Shape2D> shape;
+ int index;
+ };
+
+ Vector<Shape> shapes;
+ bool disabled;
+ bool one_way_collision;
ShapeData() {
- trigger = false;
+ disabled = false;
+ one_way_collision = false;
+ owner = NULL;
}
};
- Vector<ShapeData> shapes;
-
- void _update_shapes();
+ int total_subshapes;
- friend class CollisionShape2D;
- friend class CollisionPolygon2D;
- void _update_shapes_from_children();
+ Map<uint32_t, ShapeData> shapes;
protected:
CollisionObject2D(RID p_rid, bool p_area);
void _notification(int p_what);
- bool _set(const StringName &p_name, const Variant &p_value);
- bool _get(const StringName &p_name, Variant &r_ret) const;
- void _get_property_list(List<PropertyInfo> *p_list) const;
static void _bind_methods();
void _update_pickable();
@@ -75,16 +78,29 @@ protected:
void _mouse_exit();
public:
- void add_shape(const Ref<Shape2D> &p_shape, const Transform2D &p_transform = Transform2D());
- int get_shape_count() const;
- void set_shape(int p_shape_idx, const Ref<Shape2D> &p_shape);
- void set_shape_transform(int p_shape_idx, const Transform2D &p_transform);
- Ref<Shape2D> get_shape(int p_shape_idx) const;
- Transform2D get_shape_transform(int p_shape_idx) const;
- void set_shape_as_trigger(int p_shape_idx, bool p_trigger);
- bool is_shape_set_as_trigger(int p_shape_idx) const;
- void remove_shape(int p_shape_idx);
- void clear_shapes();
+ uint32_t create_shape_owner(Object *p_owner);
+ void remove_shape_owner(uint32_t owner);
+ void get_shape_owners(List<uint32_t> *r_owners);
+
+ void shape_owner_set_transform(uint32_t p_owner, const Transform2D &p_transform);
+ Transform2D shape_owner_get_transform(uint32_t p_owner) const;
+ Object *shape_owner_get_owner(uint32_t p_owner) const;
+
+ void shape_owner_set_disabled(uint32_t p_owner, bool p_disabled);
+ bool is_shape_owner_disabled(uint32_t p_owner) const;
+
+ void shape_owner_set_one_way_collision(uint32_t p_owner, bool p_enable);
+ bool is_shape_owner_one_way_collision_enabled(uint32_t p_owner) const;
+
+ void shape_owner_add_shape(uint32_t p_owner, const Ref<Shape2D> &p_shape);
+ int shape_owner_get_shape_count(uint32_t p_owner) const;
+ Ref<Shape> shape_owner_get_shape(uint32_t p_owner, int p_shape) const;
+ int shape_owner_get_shape_index(uint32_t p_owner, int p_shape) const;
+
+ void shape_owner_remove_shape(uint32_t p_owner, int p_shape);
+ void shape_owner_clear_shapes(uint32_t p_owner);
+
+ uint32_t shape_find_owner(int p_shape_index) const;
void set_pickable(bool p_enabled);
bool is_pickable() const;
diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp
index 6b603c6473..bd669eb4c8 100644
--- a/scene/2d/collision_polygon_2d.cpp
+++ b/scene/2d/collision_polygon_2d.cpp
@@ -35,13 +35,9 @@
#include "thirdparty/misc/triangulator.h"
-void CollisionPolygon2D::_add_to_collision_object(Object *p_obj) {
+void CollisionPolygon2D::_build_polygon() {
- if (unparenting || !can_update_body)
- return;
-
- CollisionObject2D *co = p_obj->cast_to<CollisionObject2D>();
- ERR_FAIL_COND(!co);
+ parent->shape_owner_clear_shapes(owner_id);
if (polygon.size() == 0)
return;
@@ -53,18 +49,10 @@ void CollisionPolygon2D::_add_to_collision_object(Object *p_obj) {
//here comes the sun, lalalala
//decompose concave into multiple convex polygons and add them
Vector<Vector<Vector2> > decomp = _decompose_in_convex();
- shape_from = co->get_shape_count();
for (int i = 0; i < decomp.size(); i++) {
Ref<ConvexPolygonShape2D> convex = memnew(ConvexPolygonShape2D);
convex->set_points(decomp[i]);
- co->add_shape(convex, get_transform());
- if (trigger)
- co->set_shape_as_trigger(co->get_shape_count() - 1, true);
- }
- shape_to = co->get_shape_count() - 1;
- if (shape_to < shape_from) {
- shape_from = -1;
- shape_to = -1;
+ parent->shape_owner_add_shape(owner_id, convex);
}
} else {
@@ -83,28 +71,8 @@ void CollisionPolygon2D::_add_to_collision_object(Object *p_obj) {
w = PoolVector<Vector2>::Write();
concave->set_segments(segments);
- co->add_shape(concave, get_transform());
- if (trigger)
- co->set_shape_as_trigger(co->get_shape_count() - 1, true);
-
- shape_from = co->get_shape_count() - 1;
- shape_to = co->get_shape_count() - 1;
+ parent->shape_owner_add_shape(owner_id, concave);
}
-
- //co->add_shape(shape,get_transform());
-}
-
-void CollisionPolygon2D::_update_parent() {
-
- if (!can_update_body)
- return;
- Node *parent = get_parent();
- if (!parent)
- return;
- CollisionObject2D *co = parent->cast_to<CollisionObject2D>();
- if (!co)
- return;
- co->_update_shapes_from_children();
}
Vector<Vector<Vector2> > CollisionPolygon2D::_decompose_in_convex() {
@@ -155,33 +123,38 @@ Vector<Vector<Vector2> > CollisionPolygon2D::_decompose_in_convex() {
void CollisionPolygon2D::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE: {
- unparenting = false;
- can_update_body = get_tree()->is_editor_hint();
- if (!get_tree()->is_editor_hint()) {
+ case NOTIFICATION_PARENTED: {
+
+ parent = get_parent()->cast_to<CollisionObject2D>();
+ if (parent) {
+ owner_id = parent->create_shape_owner(this);
+ _build_polygon();
+ parent->shape_owner_set_transform(owner_id, get_transform());
+ parent->shape_owner_set_disabled(owner_id, disabled);
+ parent->shape_owner_set_one_way_collision(owner_id, one_way_collision);
+ }
+
+ /*if (get_tree()->is_editor_hint()) {
//display above all else
set_z_as_relative(false);
set_z(VS::CANVAS_ITEM_Z_MAX - 1);
- }
+ }*/
} break;
- case NOTIFICATION_EXIT_TREE: {
- can_update_body = false;
- } break;
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
- if (!is_inside_tree())
- break;
- if (can_update_body) {
- _update_parent();
- } else if (shape_from >= 0 && shape_to >= 0) {
- CollisionObject2D *co = get_parent()->cast_to<CollisionObject2D>();
- for (int i = shape_from; i <= shape_to; i++) {
- co->set_shape_transform(i, get_transform());
- }
+ if (parent) {
+ parent->shape_owner_set_transform(owner_id, get_transform());
}
} break;
+ case NOTIFICATION_UNPARENTED: {
+ if (parent) {
+ parent->remove_shape_owner(owner_id);
+ }
+ owner_id = 0;
+ parent = NULL;
+ } break;
case NOTIFICATION_DRAW: {
@@ -210,10 +183,22 @@ void CollisionPolygon2D::_notification(int p_what) {
draw_colored_polygon(polygon, get_tree()->get_debug_collisions_color());
#endif
- } break;
- case NOTIFICATION_UNPARENTED: {
- unparenting = true;
- _update_parent();
+ if (one_way_collision) {
+ Color dcol = get_tree()->get_debug_collisions_color(); //0.9,0.2,0.2,0.4);
+ dcol.a = 1.0;
+ Vector2 line_to(0, 20);
+ draw_line(Vector2(), line_to, dcol, 3);
+ Vector<Vector2> pts;
+ float tsize = 8;
+ pts.push_back(line_to + (Vector2(0, tsize)));
+ pts.push_back(line_to + (Vector2(0.707 * tsize, 0)));
+ pts.push_back(line_to + (Vector2(-0.707 * tsize, 0)));
+ Vector<Color> cols;
+ for (int i = 0; i < 3; i++)
+ cols.push_back(dcol);
+
+ draw_primitive(pts, cols, Vector<Vector2>()); //small arrow
+ }
} break;
}
}
@@ -222,7 +207,7 @@ void CollisionPolygon2D::set_polygon(const Vector<Point2> &p_polygon) {
polygon = p_polygon;
- if (can_update_body) {
+ {
for (int i = 0; i < polygon.size(); i++) {
if (i == 0)
aabb = Rect2(polygon[i], Size2());
@@ -236,7 +221,10 @@ void CollisionPolygon2D::set_polygon(const Vector<Point2> &p_polygon) {
aabb.position -= aabb.size * 0.3;
aabb.size += aabb.size * 0.6;
}
- _update_parent();
+ }
+
+ if (parent) {
+ _build_polygon();
}
update();
update_configuration_warning();
@@ -251,7 +239,9 @@ void CollisionPolygon2D::set_build_mode(BuildMode p_mode) {
ERR_FAIL_INDEX(p_mode, 2);
build_mode = p_mode;
- _update_parent();
+ if (parent) {
+ _build_polygon();
+ }
}
CollisionPolygon2D::BuildMode CollisionPolygon2D::get_build_mode() const {
@@ -264,79 +254,69 @@ Rect2 CollisionPolygon2D::get_item_rect() const {
return aabb;
}
-void CollisionPolygon2D::set_trigger(bool p_trigger) {
+String CollisionPolygon2D::get_configuration_warning() const {
- trigger = p_trigger;
- _update_parent();
- if (!can_update_body && is_inside_tree() && shape_from >= 0 && shape_to >= 0) {
- CollisionObject2D *co = get_parent()->cast_to<CollisionObject2D>();
- for (int i = shape_from; i <= shape_to; i++) {
- co->set_shape_as_trigger(i, p_trigger);
- }
+ if (!get_parent()->cast_to<CollisionObject2D>()) {
+ return TTR("CollisionPolygon2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape.");
}
-}
-bool CollisionPolygon2D::is_trigger() const {
+ if (polygon.empty()) {
+ return TTR("An empty CollisionPolygon2D has no effect on collision.");
+ }
- return trigger;
+ return String();
}
-void CollisionPolygon2D::_set_shape_range(const Vector2 &p_range) {
-
- shape_from = p_range.x;
- shape_to = p_range.y;
+void CollisionPolygon2D::set_disabled(bool p_disabled) {
+ disabled = p_disabled;
+ update();
+ if (parent) {
+ parent->shape_owner_set_disabled(owner_id, p_disabled);
+ }
}
-Vector2 CollisionPolygon2D::_get_shape_range() const {
-
- return Vector2(shape_from, shape_to);
+bool CollisionPolygon2D::is_disabled() const {
+ return disabled;
}
-String CollisionPolygon2D::get_configuration_warning() const {
-
- if (!get_parent()->cast_to<CollisionObject2D>()) {
- return TTR("CollisionPolygon2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape.");
+void CollisionPolygon2D::set_one_way_collision(bool p_enable) {
+ one_way_collision = p_enable;
+ update();
+ if (parent) {
+ parent->shape_owner_set_one_way_collision(owner_id, p_enable);
}
+}
- if (polygon.empty()) {
- return TTR("An empty CollisionPolygon2D has no effect on collision.");
- }
+bool CollisionPolygon2D::is_one_way_collision_enabled() const {
- return String();
+ return one_way_collision;
}
void CollisionPolygon2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_add_to_collision_object"), &CollisionPolygon2D::_add_to_collision_object);
ClassDB::bind_method(D_METHOD("set_polygon", "polygon"), &CollisionPolygon2D::set_polygon);
ClassDB::bind_method(D_METHOD("get_polygon"), &CollisionPolygon2D::get_polygon);
ClassDB::bind_method(D_METHOD("set_build_mode", "build_mode"), &CollisionPolygon2D::set_build_mode);
ClassDB::bind_method(D_METHOD("get_build_mode"), &CollisionPolygon2D::get_build_mode);
-
- ClassDB::bind_method(D_METHOD("set_trigger", "trigger"), &CollisionPolygon2D::set_trigger);
- ClassDB::bind_method(D_METHOD("is_trigger"), &CollisionPolygon2D::is_trigger);
-
- ClassDB::bind_method(D_METHOD("_set_shape_range", "shape_range"), &CollisionPolygon2D::_set_shape_range);
- ClassDB::bind_method(D_METHOD("_get_shape_range"), &CollisionPolygon2D::_get_shape_range);
-
- ClassDB::bind_method(D_METHOD("get_collision_object_first_shape"), &CollisionPolygon2D::get_collision_object_first_shape);
- ClassDB::bind_method(D_METHOD("get_collision_object_last_shape"), &CollisionPolygon2D::get_collision_object_last_shape);
+ ClassDB::bind_method(D_METHOD("set_disabled", "disabled"), &CollisionPolygon2D::set_disabled);
+ ClassDB::bind_method(D_METHOD("is_disabled"), &CollisionPolygon2D::is_disabled);
+ ClassDB::bind_method(D_METHOD("set_one_way_collision", "enabled"), &CollisionPolygon2D::set_one_way_collision);
+ ClassDB::bind_method(D_METHOD("is_one_way_collision_enabled"), &CollisionPolygon2D::is_one_way_collision_enabled);
ADD_PROPERTY(PropertyInfo(Variant::INT, "build_mode", PROPERTY_HINT_ENUM, "Solids,Segments"), "set_build_mode", "get_build_mode");
ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "shape_range", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_shape_range", "_get_shape_range");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "trigger"), "set_trigger", "is_trigger");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "one_way_collision"), "set_one_way_collision", "is_one_way_collision_enabled");
}
CollisionPolygon2D::CollisionPolygon2D() {
aabb = Rect2(-10, -10, 20, 20);
build_mode = BUILD_SOLIDS;
- trigger = false;
- unparenting = false;
- shape_from = -1;
- shape_to = -1;
- can_update_body = false;
set_notify_local_transform(true);
+ parent = NULL;
+ owner_id = 0;
+ disabled = false;
+ one_way_collision = false;
}
diff --git a/scene/2d/collision_polygon_2d.h b/scene/2d/collision_polygon_2d.h
index b1a4a4822d..f0666ba9de 100644
--- a/scene/2d/collision_polygon_2d.h
+++ b/scene/2d/collision_polygon_2d.h
@@ -33,6 +33,8 @@
#include "scene/2d/node_2d.h"
#include "scene/resources/shape_2d.h"
+class CollisionObject2D;
+
class CollisionPolygon2D : public Node2D {
GDCLASS(CollisionPolygon2D, Node2D);
@@ -47,29 +49,20 @@ protected:
Rect2 aabb;
BuildMode build_mode;
Vector<Point2> polygon;
- bool trigger;
- bool unparenting;
-
- void _add_to_collision_object(Object *p_obj);
- void _update_parent();
-
- bool can_update_body;
- int shape_from;
- int shape_to;
-
- void _set_shape_range(const Vector2 &p_range);
- Vector2 _get_shape_range() const;
+ uint32_t owner_id;
+ CollisionObject2D *parent;
+ bool disabled;
+ bool one_way_collision;
Vector<Vector<Vector2> > _decompose_in_convex();
+ void _build_polygon();
+
protected:
void _notification(int p_what);
static void _bind_methods();
public:
- void set_trigger(bool p_trigger);
- bool is_trigger() const;
-
void set_build_mode(BuildMode p_mode);
BuildMode get_build_mode() const;
@@ -78,11 +71,14 @@ public:
virtual Rect2 get_item_rect() const;
- int get_collision_object_first_shape() const { return shape_from; }
- int get_collision_object_last_shape() const { return shape_to; }
-
virtual String get_configuration_warning() const;
+ void set_disabled(bool p_disabled);
+ bool is_disabled() const;
+
+ void set_one_way_collision(bool p_enable);
+ bool is_one_way_collision_enabled() const;
+
CollisionPolygon2D();
};
diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp
index 1687a898db..ff4aa245ec 100644
--- a/scene/2d/collision_shape_2d.cpp
+++ b/scene/2d/collision_shape_2d.cpp
@@ -37,68 +37,48 @@
#include "scene/resources/segment_shape_2d.h"
#include "scene/resources/shape_line_2d.h"
-void CollisionShape2D::_add_to_collision_object(Object *p_obj) {
-
- if (unparenting)
- return;
-
- CollisionObject2D *co = p_obj->cast_to<CollisionObject2D>();
- ERR_FAIL_COND(!co);
- update_shape_index = co->get_shape_count();
- co->add_shape(shape, get_transform());
- if (trigger)
- co->set_shape_as_trigger(co->get_shape_count() - 1, true);
-}
-
void CollisionShape2D::_shape_changed() {
update();
- _update_parent();
-}
-
-void CollisionShape2D::_update_parent() {
-
- Node *parent = get_parent();
- if (!parent)
- return;
- CollisionObject2D *co = parent->cast_to<CollisionObject2D>();
- if (!co)
- return;
- co->_update_shapes_from_children();
}
void CollisionShape2D::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE: {
- unparenting = false;
- can_update_body = get_tree()->is_editor_hint();
- if (!get_tree()->is_editor_hint()) {
+ case NOTIFICATION_PARENTED: {
+
+ parent = get_parent()->cast_to<CollisionObject2D>();
+ if (parent) {
+ owner_id = parent->create_shape_owner(this);
+ if (shape.is_valid()) {
+ parent->shape_owner_add_shape(owner_id, shape);
+ }
+ parent->shape_owner_set_transform(owner_id, get_transform());
+ parent->shape_owner_set_disabled(owner_id, disabled);
+ parent->shape_owner_set_one_way_collision(owner_id, one_way_collision);
+ }
+
+ /*if (get_tree()->is_editor_hint()) {
//display above all else
set_z_as_relative(false);
set_z(VS::CANVAS_ITEM_Z_MAX - 1);
- }
+ }*/
} break;
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
- if (!is_inside_tree())
- break;
- if (can_update_body) {
- _update_parent();
- } else if (update_shape_index >= 0) {
-
- CollisionObject2D *co = get_parent()->cast_to<CollisionObject2D>();
- if (co) {
- co->set_shape_transform(update_shape_index, get_transform());
- }
+ if (parent) {
+ parent->shape_owner_set_transform(owner_id, get_transform());
}
} break;
- case NOTIFICATION_EXIT_TREE: {
- can_update_body = false;
-
+ case NOTIFICATION_UNPARENTED: {
+ if (parent) {
+ parent->remove_shape_owner(owner_id);
+ }
+ owner_id = 0;
+ parent = NULL;
} break;
/*
case NOTIFICATION_TRANSFORM_CHANGED: {
@@ -121,15 +101,33 @@ void CollisionShape2D::_notification(int p_what) {
rect = Rect2();
Color draw_col = get_tree()->get_debug_collisions_color();
+ if (disabled) {
+ float g = draw_col.gray();
+ draw_col.r = g;
+ draw_col.g = g;
+ draw_col.b = g;
+ }
shape->draw(get_canvas_item(), draw_col);
rect = shape->get_rect();
rect = rect.grow(3);
- } break;
- case NOTIFICATION_UNPARENTED: {
- unparenting = true;
- _update_parent();
+ if (one_way_collision) {
+ Color dcol = get_tree()->get_debug_collisions_color(); //0.9,0.2,0.2,0.4);
+ dcol.a = 1.0;
+ Vector2 line_to(0, 20);
+ draw_line(Vector2(), line_to, dcol, 3);
+ Vector<Vector2> pts;
+ float tsize = 8;
+ pts.push_back(line_to + (Vector2(0, tsize)));
+ pts.push_back(line_to + (Vector2(0.707 * tsize, 0)));
+ pts.push_back(line_to + (Vector2(-0.707 * tsize, 0)));
+ Vector<Color> cols;
+ for (int i = 0; i < 3; i++)
+ cols.push_back(dcol);
+
+ draw_primitive(pts, cols, Vector<Vector2>()); //small arrow
+ }
} break;
}
}
@@ -140,14 +138,13 @@ void CollisionShape2D::set_shape(const Ref<Shape2D> &p_shape) {
shape->disconnect("changed", this, "_shape_changed");
shape = p_shape;
update();
- if (is_inside_tree() && can_update_body)
- _update_parent();
- if (is_inside_tree() && !can_update_body && update_shape_index >= 0) {
- CollisionObject2D *co = get_parent()->cast_to<CollisionObject2D>();
- if (co) {
- co->set_shape(update_shape_index, p_shape);
+ if (parent) {
+ parent->shape_owner_clear_shapes(owner_id);
+ if (shape.is_valid()) {
+ parent->shape_owner_add_shape(owner_id, shape);
}
}
+
if (shape.is_valid())
shape->connect("changed", this, "_shape_changed");
@@ -164,72 +161,65 @@ Rect2 CollisionShape2D::get_item_rect() const {
return rect;
}
-void CollisionShape2D::set_trigger(bool p_trigger) {
+String CollisionShape2D::get_configuration_warning() const {
- trigger = p_trigger;
- if (can_update_body) {
- _update_parent();
- } else if (is_inside_tree() && update_shape_index >= 0) {
- CollisionObject2D *co = get_parent()->cast_to<CollisionObject2D>();
- if (co) {
- co->set_shape_as_trigger(update_shape_index, p_trigger);
- }
+ if (!get_parent()->cast_to<CollisionObject2D>()) {
+ return TTR("CollisionShape2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape.");
}
-}
-bool CollisionShape2D::is_trigger() const {
+ if (!shape.is_valid()) {
+ return TTR("A shape must be provided for CollisionShape2D to function. Please create a shape resource for it!");
+ }
- return trigger;
+ return String();
}
-void CollisionShape2D::_set_update_shape_index(int p_index) {
-
- update_shape_index = p_index;
+void CollisionShape2D::set_disabled(bool p_disabled) {
+ disabled = p_disabled;
+ update();
+ if (parent) {
+ parent->shape_owner_set_disabled(owner_id, p_disabled);
+ }
}
-int CollisionShape2D::_get_update_shape_index() const {
-
- return update_shape_index;
+bool CollisionShape2D::is_disabled() const {
+ return disabled;
}
-String CollisionShape2D::get_configuration_warning() const {
-
- if (!get_parent()->cast_to<CollisionObject2D>()) {
- return TTR("CollisionShape2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape.");
+void CollisionShape2D::set_one_way_collision(bool p_enable) {
+ one_way_collision = p_enable;
+ update();
+ if (parent) {
+ parent->shape_owner_set_one_way_collision(owner_id, p_enable);
}
+}
- if (!shape.is_valid()) {
- return TTR("A shape must be provided for CollisionShape2D to function. Please create a shape resource for it!");
- }
+bool CollisionShape2D::is_one_way_collision_enabled() const {
- return String();
+ return one_way_collision;
}
void CollisionShape2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_shape", "shape"), &CollisionShape2D::set_shape);
ClassDB::bind_method(D_METHOD("get_shape"), &CollisionShape2D::get_shape);
+ ClassDB::bind_method(D_METHOD("set_disabled", "disabled"), &CollisionShape2D::set_disabled);
+ ClassDB::bind_method(D_METHOD("is_disabled"), &CollisionShape2D::is_disabled);
+ ClassDB::bind_method(D_METHOD("set_one_way_collision", "enabled"), &CollisionShape2D::set_one_way_collision);
+ ClassDB::bind_method(D_METHOD("is_one_way_collision_enabled"), &CollisionShape2D::is_one_way_collision_enabled);
ClassDB::bind_method(D_METHOD("_shape_changed"), &CollisionShape2D::_shape_changed);
- ClassDB::bind_method(D_METHOD("_add_to_collision_object"), &CollisionShape2D::_add_to_collision_object);
- ClassDB::bind_method(D_METHOD("set_trigger", "enable"), &CollisionShape2D::set_trigger);
- ClassDB::bind_method(D_METHOD("is_trigger"), &CollisionShape2D::is_trigger);
-
- ClassDB::bind_method(D_METHOD("_set_update_shape_index", "index"), &CollisionShape2D::_set_update_shape_index);
- ClassDB::bind_method(D_METHOD("_get_update_shape_index"), &CollisionShape2D::_get_update_shape_index);
-
- ClassDB::bind_method(D_METHOD("get_collision_object_shape_index"), &CollisionShape2D::get_collision_object_shape_index);
ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape2D"), "set_shape", "get_shape");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "trigger"), "set_trigger", "is_trigger");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "_update_shape_index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_update_shape_index", "_get_update_shape_index");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "one_way_collision"), "set_one_way_collision", "is_one_way_collision_enabled");
}
CollisionShape2D::CollisionShape2D() {
rect = Rect2(-Point2(10, 10), Point2(20, 20));
set_notify_local_transform(true);
- trigger = false;
- unparenting = false;
- can_update_body = false;
- update_shape_index = -1;
+ owner_id = 0;
+ parent = NULL;
+ disabled = false;
+ one_way_collision = false;
}
diff --git a/scene/2d/collision_shape_2d.h b/scene/2d/collision_shape_2d.h
index 3e63981010..1f2b96b91f 100644
--- a/scene/2d/collision_shape_2d.h
+++ b/scene/2d/collision_shape_2d.h
@@ -33,35 +33,33 @@
#include "scene/2d/node_2d.h"
#include "scene/resources/shape_2d.h"
+class CollisionObject2D;
+
class CollisionShape2D : public Node2D {
- GDCLASS(CollisionShape2D, Node2D);
+ GDCLASS(CollisionShape2D, Node2D)
Ref<Shape2D> shape;
Rect2 rect;
- bool trigger;
- bool unparenting;
- bool can_update_body;
+ uint32_t owner_id;
+ CollisionObject2D *parent;
void _shape_changed();
- int update_shape_index;
-
- void _set_update_shape_index(int p_index);
- int _get_update_shape_index() const;
+ bool disabled;
+ bool one_way_collision;
protected:
- void _update_parent();
void _notification(int p_what);
static void _bind_methods();
- void _add_to_collision_object(Object *p_obj);
-
public:
void set_shape(const Ref<Shape2D> &p_shape);
Ref<Shape2D> get_shape() const;
virtual Rect2 get_item_rect() const;
- void set_trigger(bool p_trigger);
- bool is_trigger() const;
- int get_collision_object_shape_index() const { return _get_update_shape_index(); }
+ void set_disabled(bool p_disabled);
+ bool is_disabled() const;
+
+ void set_one_way_collision(bool p_enable);
+ bool is_one_way_collision_enabled() const;
virtual String get_configuration_warning() const;
diff --git a/scene/2d/particles_2d.cpp b/scene/2d/particles_2d.cpp
index 21d64c5d64..beff247264 100644
--- a/scene/2d/particles_2d.cpp
+++ b/scene/2d/particles_2d.cpp
@@ -28,903 +28,235 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "particles_2d.h"
-#include "scene/scene_string_names.h"
-
-void ParticleAttractor2D::_notification(int p_what) {
-
- switch (p_what) {
-
- case NOTIFICATION_ENTER_TREE: {
-
- _update_owner();
- } break;
- case NOTIFICATION_DRAW: {
-
- if (!get_tree()->is_editor_hint())
- return;
-
- Vector2 pv;
- float dr = MIN(disable_radius, radius);
- for (int i = 0; i <= 32; i++) {
- Vector2 v(Math::sin(i / 32.0 * Math_PI * 2), Math::cos(i / 32.0 * Math_PI * 2));
- if (i > 0) {
- draw_line(pv * radius, v * radius, Color(0, 0, 0.5, 0.9));
- if (dr > 0) {
- draw_line(pv * dr, v * dr, Color(0.5, 0, 0.0, 0.9));
- }
- }
- pv = v;
- }
+#include "scene/3d/particles.h"
+#include "scene/scene_string_names.h"
- } break;
- case NOTIFICATION_EXIT_TREE: {
- if (owner) {
- _set_owner(NULL);
- }
+void Particles2D::set_emitting(bool p_emitting) {
- } break;
- }
+ emitting = p_emitting;
+ VS::get_singleton()->particles_set_emitting(particles, emitting);
}
-void ParticleAttractor2D::_owner_exited() {
+void Particles2D::set_amount(int p_amount) {
- ERR_FAIL_COND(!owner);
- owner->attractors.erase(this);
- owner = NULL;
+ ERR_FAIL_COND(p_amount < 1);
+ amount = p_amount;
+ VS::get_singleton()->particles_set_amount(particles, amount);
}
+void Particles2D::set_lifetime(float p_lifetime) {
-void ParticleAttractor2D::_update_owner() {
-
- if (!is_inside_tree() || !has_node(path)) {
- _set_owner(NULL);
- return;
- }
-
- Node *n = get_node(path);
- ERR_FAIL_COND(!n);
- Particles2D *pn = n->cast_to<Particles2D>();
- if (!pn) {
- _set_owner(NULL);
- return;
- }
-
- _set_owner(pn);
+ ERR_FAIL_COND(p_lifetime <= 0);
+ lifetime = p_lifetime;
+ VS::get_singleton()->particles_set_lifetime(particles, lifetime);
}
+void Particles2D::set_pre_process_time(float p_time) {
-void ParticleAttractor2D::_set_owner(Particles2D *p_owner) {
-
- if (owner == p_owner)
- return;
-
- if (owner) {
- owner->disconnect("tree_exited", this, "_owner_exited");
- owner->attractors.erase(this);
- owner = NULL;
- }
- owner = p_owner;
-
- if (owner) {
-
- owner->connect("tree_exited", this, "_owner_exited", varray(), CONNECT_ONESHOT);
- owner->attractors.insert(this);
- }
+ pre_process_time = p_time;
+ VS::get_singleton()->particles_set_pre_process_time(particles, pre_process_time);
}
+void Particles2D::set_explosiveness_ratio(float p_ratio) {
-void ParticleAttractor2D::_bind_methods() {
-
- ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &ParticleAttractor2D::set_enabled);
- ClassDB::bind_method(D_METHOD("is_enabled"), &ParticleAttractor2D::is_enabled);
-
- ClassDB::bind_method(D_METHOD("set_radius", "radius"), &ParticleAttractor2D::set_radius);
- ClassDB::bind_method(D_METHOD("get_radius"), &ParticleAttractor2D::get_radius);
-
- ClassDB::bind_method(D_METHOD("set_disable_radius", "radius"), &ParticleAttractor2D::set_disable_radius);
- ClassDB::bind_method(D_METHOD("get_disable_radius"), &ParticleAttractor2D::get_disable_radius);
-
- ClassDB::bind_method(D_METHOD("set_gravity", "gravity"), &ParticleAttractor2D::set_gravity);
- ClassDB::bind_method(D_METHOD("get_gravity"), &ParticleAttractor2D::get_gravity);
-
- ClassDB::bind_method(D_METHOD("set_absorption", "absorption"), &ParticleAttractor2D::set_absorption);
- ClassDB::bind_method(D_METHOD("get_absorption"), &ParticleAttractor2D::get_absorption);
-
- ClassDB::bind_method(D_METHOD("set_particles_path", "path"), &ParticleAttractor2D::set_particles_path);
- ClassDB::bind_method(D_METHOD("get_particles_path"), &ParticleAttractor2D::get_particles_path);
-
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.1,16000,0.1"), "set_radius", "get_radius");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "disable_radius", PROPERTY_HINT_RANGE, "0.1,16000,0.1"), "set_disable_radius", "get_disable_radius");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity", PROPERTY_HINT_RANGE, "-512,512,0.01"), "set_gravity", "get_gravity");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "absorption", PROPERTY_HINT_RANGE, "0,512,0.01"), "set_absorption", "get_absorption");
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "particles_path", PROPERTY_HINT_RESOURCE_TYPE, "Particles2D"), "set_particles_path", "get_particles_path");
+ explosiveness_ratio = p_ratio;
+ VS::get_singleton()->particles_set_explosiveness_ratio(particles, explosiveness_ratio);
}
+void Particles2D::set_randomness_ratio(float p_ratio) {
-void ParticleAttractor2D::set_enabled(bool p_enabled) {
-
- enabled = p_enabled;
+ randomness_ratio = p_ratio;
+ VS::get_singleton()->particles_set_randomness_ratio(particles, randomness_ratio);
}
+void Particles2D::set_visibility_rect(const Rect2 &p_aabb) {
-bool ParticleAttractor2D::is_enabled() const {
+ visibility_rect = p_aabb;
+ Rect3 aabb;
+ aabb.position.x = p_aabb.position.x;
+ aabb.position.y = p_aabb.position.y;
+ aabb.size.x = p_aabb.size.x;
+ aabb.size.y = p_aabb.size.y;
- return enabled;
-}
-
-void ParticleAttractor2D::set_radius(float p_radius) {
+ VS::get_singleton()->particles_set_custom_aabb(particles, aabb);
- radius = p_radius;
+ _change_notify("visibility_rect");
update();
}
+void Particles2D::set_use_local_coordinates(bool p_enable) {
-float ParticleAttractor2D::get_radius() const {
-
- return radius;
-}
-
-void ParticleAttractor2D::set_disable_radius(float p_disable_radius) {
-
- disable_radius = p_disable_radius;
- update();
-}
-float ParticleAttractor2D::get_disable_radius() const {
-
- return disable_radius;
-}
-
-void ParticleAttractor2D::set_gravity(float p_gravity) {
-
- gravity = p_gravity;
-}
-float ParticleAttractor2D::get_gravity() const {
-
- return gravity;
-}
-
-void ParticleAttractor2D::set_absorption(float p_absorption) {
-
- absorption = p_absorption;
-}
-float ParticleAttractor2D::get_absorption() const {
-
- return absorption;
-}
-
-void ParticleAttractor2D::set_particles_path(NodePath p_path) {
-
- path = p_path;
- _update_owner();
- update_configuration_warning();
-}
-NodePath ParticleAttractor2D::get_particles_path() const {
-
- return path;
-}
-
-String ParticleAttractor2D::get_configuration_warning() const {
-
- if (!has_node(path) || !get_node(path) || !get_node(path)->cast_to<Particles2D>()) {
- return TTR("Path property must point to a valid Particles2D node to work.");
+ local_coords = p_enable;
+ VS::get_singleton()->particles_set_use_local_coordinates(particles, local_coords);
+ set_notify_transform(!p_enable);
+ if (!p_enable) {
+ _update_particle_emission_transform();
}
-
- return String();
-}
-
-ParticleAttractor2D::ParticleAttractor2D() {
-
- owner = NULL;
- radius = 50;
- disable_radius = 0;
- gravity = 100;
- absorption = 0;
- path = String("..");
- enabled = true;
}
-/****************************************/
+void Particles2D::_update_particle_emission_transform() {
-_FORCE_INLINE_ static float _rand_from_seed(uint64_t *seed) {
+ Transform2D xf2d = get_global_transform();
+ Transform xf;
+ xf.basis.set_axis(0, Vector3(xf2d.get_axis(0).x, xf2d.get_axis(0).y, 0));
+ xf.basis.set_axis(1, Vector3(xf2d.get_axis(1).x, xf2d.get_axis(1).y, 0));
+ xf.set_origin(Vector3(xf2d.get_origin().x, xf2d.get_origin().y, 0));
- uint32_t r = Math::rand_from_seed(seed);
- return 2.0f * (float)r / (float)Math::RANDOM_MAX - 1.0f;
+ VS::get_singleton()->particles_set_emission_transform(particles, xf);
}
-void Particles2D::_process_particles(float p_delta) {
-
- if (particles.size() == 0 || lifetime == 0)
- return;
-
- p_delta *= time_scale;
-
- float frame_time = p_delta;
-
- if (emit_timeout > 0) {
- time_to_live -= frame_time;
- if (time_to_live < 0) {
-
- emitting = false;
- _change_notify("config/emitting");
- };
- };
-
- float next_time = time + frame_time;
-
- if (next_time > lifetime)
- next_time = Math::fmod(next_time, lifetime);
-
- Particle *pdata = &particles[0];
- int particle_count = particles.size();
- Transform2D xform;
- if (!local_space)
- xform = get_global_transform();
+void Particles2D::set_process_material(const Ref<Material> &p_material) {
- active_count = 0;
-
- PoolVector<Point2>::Read r;
- int emission_point_count = 0;
- if (emission_points.size()) {
-
- emission_point_count = emission_points.size();
- r = emission_points.read();
- }
-
- int attractor_count = 0;
- AttractorCache *attractor_ptr = NULL;
-
- if (attractors.size()) {
- if (attractors.size() != attractor_cache.size()) {
- attractor_cache.resize(attractors.size());
- }
-
- int idx = 0;
- Transform2D m;
- if (local_space) {
- m = get_global_transform().affine_inverse();
- }
- for (Set<ParticleAttractor2D *>::Element *E = attractors.front(); E; E = E->next()) {
-
- attractor_cache[idx].pos = m.xform(E->get()->get_global_position());
- attractor_cache[idx].attractor = E->get();
- idx++;
- }
-
- attractor_ptr = attractor_cache.ptr();
- attractor_count = attractor_cache.size();
- }
-
- for (int i = 0; i < particle_count; i++) {
-
- Particle &p = pdata[i];
-
- float restart_time = (i * lifetime / particle_count) * explosiveness;
-
- bool restart = false;
-
- if (next_time < time) {
-
- if (restart_time > time || restart_time < next_time)
- restart = true;
-
- } else if (restart_time > time && restart_time < next_time) {
- restart = true;
- }
-
- if (restart) {
-
- if (emitting) {
-
- p.pos = emissor_offset;
- if (emission_point_count) {
-
- Vector2 ep = r[Math::rand() % emission_point_count];
- if (!local_space) {
- p.pos = xform.xform(p.pos + ep * extents);
- } else {
- p.pos += ep * extents;
- }
- } else {
- if (!local_space) {
- p.pos = xform.xform(p.pos + Vector2(Math::random(-extents.x, extents.x), Math::random(-extents.y, extents.y)));
- } else {
- p.pos += Vector2(Math::random(-extents.x, extents.x), Math::random(-extents.y, extents.y));
- }
- }
- p.seed = Math::rand() % 12345678;
- uint64_t rand_seed = p.seed * (i + 1);
-
- float angle = Math::deg2rad(param[PARAM_DIRECTION] + _rand_from_seed(&rand_seed) * param[PARAM_SPREAD]);
-
- p.velocity = Vector2(Math::sin(angle), Math::cos(angle));
- if (!local_space) {
-
- p.velocity = xform.basis_xform(p.velocity).normalized();
- }
-
- p.velocity *= param[PARAM_LINEAR_VELOCITY] + param[PARAM_LINEAR_VELOCITY] * _rand_from_seed(&rand_seed) * randomness[PARAM_LINEAR_VELOCITY];
- p.velocity += initial_velocity;
- p.active = true;
- p.rot = Math::deg2rad(param[PARAM_INITIAL_ANGLE] + param[PARAM_INITIAL_ANGLE] * randomness[PARAM_INITIAL_ANGLE] * _rand_from_seed(&rand_seed));
- active_count++;
-
- p.frame = Math::fmod(param[PARAM_ANIM_INITIAL_POS] + randomness[PARAM_ANIM_INITIAL_POS] * _rand_from_seed(&rand_seed), 1.0f);
-
- } else {
-
- p.active = false;
- }
-
- } else {
-
- if (!p.active)
- continue;
-
- uint64_t rand_seed = p.seed * (i + 1);
-
- Vector2 force;
-
- //apply gravity
- float gravity_dir = Math::deg2rad(param[PARAM_GRAVITY_DIRECTION] + 180 * randomness[PARAM_GRAVITY_DIRECTION] * _rand_from_seed(&rand_seed));
- force += Vector2(Math::sin(gravity_dir), Math::cos(gravity_dir)) * (param[PARAM_GRAVITY_STRENGTH] + param[PARAM_GRAVITY_STRENGTH] * randomness[PARAM_GRAVITY_STRENGTH] * _rand_from_seed(&rand_seed));
- //apply radial
- Vector2 rvec = (p.pos - emissor_offset).normalized();
- force += rvec * (param[PARAM_RADIAL_ACCEL] + param[PARAM_RADIAL_ACCEL] * randomness[PARAM_RADIAL_ACCEL] * _rand_from_seed(&rand_seed));
- //apply orbit
- float orbitvel = (param[PARAM_ORBIT_VELOCITY] + param[PARAM_ORBIT_VELOCITY] * randomness[PARAM_ORBIT_VELOCITY] * _rand_from_seed(&rand_seed));
- if (orbitvel != 0) {
- Vector2 rel = p.pos - xform.elements[2];
- Transform2D rot(orbitvel * frame_time, Vector2());
- p.pos = rot.xform(rel) + xform.elements[2];
- }
-
- Vector2 tvec = rvec.tangent();
- force += tvec * (param[PARAM_TANGENTIAL_ACCEL] + param[PARAM_TANGENTIAL_ACCEL] * randomness[PARAM_TANGENTIAL_ACCEL] * _rand_from_seed(&rand_seed));
-
- for (int j = 0; j < attractor_count; j++) {
-
- Vector2 vec = (attractor_ptr[j].pos - p.pos);
- float vl = vec.length();
-
- if (!attractor_ptr[j].attractor->enabled || vl == 0 || vl > attractor_ptr[j].attractor->radius)
- continue;
-
- force += vec * attractor_ptr[j].attractor->gravity;
- float fvl = p.velocity.length();
- if (fvl && attractor_ptr[j].attractor->absorption) {
- Vector2 target = vec.normalized();
- p.velocity = p.velocity.normalized().linear_interpolate(target, MIN(frame_time * attractor_ptr[j].attractor->absorption, 1)) * fvl;
- }
-
- if (attractor_ptr[j].attractor->disable_radius && vl < attractor_ptr[j].attractor->disable_radius) {
- p.active = false;
- }
- }
-
- p.velocity += force * frame_time;
-
- if (param[PARAM_DAMPING]) {
- float dmp = param[PARAM_DAMPING] + param[PARAM_DAMPING] * randomness[PARAM_DAMPING] * _rand_from_seed(&rand_seed);
- float v = p.velocity.length();
- v -= dmp * frame_time;
- if (v <= 0) {
- p.velocity = Vector2();
- } else {
- p.velocity = p.velocity.normalized() * v;
- }
- }
-
- p.pos += p.velocity * frame_time;
- p.rot += Math::lerp(param[PARAM_SPIN_VELOCITY], param[PARAM_SPIN_VELOCITY] * randomness[PARAM_SPIN_VELOCITY] * _rand_from_seed(&rand_seed), randomness[PARAM_SPIN_VELOCITY]) * frame_time;
- float anim_spd = param[PARAM_ANIM_SPEED_SCALE] + param[PARAM_ANIM_SPEED_SCALE] * randomness[PARAM_ANIM_SPEED_SCALE] * _rand_from_seed(&rand_seed);
- p.frame = Math::fposmod(p.frame + (frame_time / lifetime) * anim_spd, 1.0f);
-
- active_count++;
- }
+ process_material = p_material;
+ Ref<ParticlesMaterial> pm = p_material;
+ if (pm.is_valid() && !pm->get_flag(ParticlesMaterial::FLAG_DISABLE_Z) && pm->get_gravity() == Vector3(0, -9.8, 0)) {
+ //likely a new material, modify it!
+ pm->set_flag(ParticlesMaterial::FLAG_DISABLE_Z, true);
+ pm->set_gravity(Vector3(0, 98, 0));
}
+ RID material_rid;
+ if (process_material.is_valid())
+ material_rid = process_material->get_rid();
+ VS::get_singleton()->particles_set_process_material(particles, material_rid);
- time = Math::fmod(time + frame_time, lifetime);
- if (!emitting && active_count == 0) {
- emit_signal(SceneStringNames::get_singleton()->emission_finished);
- set_process(false);
- set_fixed_process(false);
- }
-
- update();
+ update_configuration_warning();
}
-void Particles2D::_notification(int p_what) {
-
- switch (p_what) {
-
- case NOTIFICATION_PROCESS: {
-
- _process_particles(get_process_delta_time());
- } break;
-
- case NOTIFICATION_FIXED_PROCESS: {
-
- _process_particles(get_fixed_process_delta_time());
- } break;
-
- case NOTIFICATION_ENTER_TREE: {
-
- float ppt = preprocess;
- while (ppt > 0) {
- _process_particles(0.1);
- ppt -= 0.1;
- }
- } break;
- case NOTIFICATION_DRAW: {
-
- if (particles.size() == 0 || lifetime == 0)
- return;
-
- RID ci = get_canvas_item();
- Size2 size(1, 1);
- Point2 center;
- int total_frames = 1;
+void Particles2D::set_speed_scale(float p_scale) {
- if (!texture.is_null()) {
- size = texture->get_size();
- size.x /= h_frames;
- size.y /= v_frames;
- total_frames = h_frames * v_frames;
- }
-
- float time_pos = (time / lifetime);
-
- Particle *pdata = &particles[0];
- int particle_count = particles.size();
-
- RID texrid;
-
- if (texture.is_valid())
- texrid = texture->get_rid();
-
- Transform2D invxform;
- if (!local_space)
- invxform = get_global_transform().affine_inverse();
-
- int start_particle = (int)(time * (float)particle_count / lifetime);
-
- for (int id = 0; id < particle_count; ++id) {
- int i = start_particle + id;
- if (i >= particle_count) {
- i -= particle_count;
- }
-
- Particle &p = pdata[i];
- if (!p.active)
- continue;
-
- float ptime = ((float)i / particle_count) * explosiveness;
-
- if (ptime < time_pos)
- ptime = time_pos - ptime;
- else
- ptime = (1.0 - ptime) + time_pos;
-
- uint64_t rand_seed = p.seed * (i + 1);
-
- Color color;
-
- if (gradient.is_valid()) {
- color = gradient->get_color_at_offset(ptime);
- } else {
- color = default_color;
- }
-
- {
- float huerand = _rand_from_seed(&rand_seed);
- float huerot = param[PARAM_HUE_VARIATION] + randomness[PARAM_HUE_VARIATION] * huerand;
-
- if (Math::abs(huerot) > CMP_EPSILON) {
-
- float h = color.get_h();
- float s = color.get_s();
- float v = color.get_v();
- float a = color.a;
- //float preh=h;
- h += huerot;
- h = Math::abs(Math::fposmod(h, 1.0f));
- //print_line("rand: "+rtos(randomness[PARAM_HUE_VARIATION])+" rand: "+rtos(huerand));
- //print_line(itos(i)+":hue: "+rtos(preh)+" + "+rtos(huerot)+" = "+rtos(h));
- color.set_hsv(h, s, v);
- color.a = a;
- }
- }
-
- float initial_size = param[PARAM_INITIAL_SIZE] + param[PARAM_INITIAL_SIZE] * _rand_from_seed(&rand_seed) * randomness[PARAM_INITIAL_SIZE];
- float final_size = param[PARAM_FINAL_SIZE] + param[PARAM_FINAL_SIZE] * _rand_from_seed(&rand_seed) * randomness[PARAM_FINAL_SIZE];
-
- float size_mult = initial_size * (1.0 - ptime) + final_size * ptime;
-
- //Size2 rectsize=size * size_mult;
- //rectsize=rectsize.floor();
-
- //Rect2 r = Rect2(Vecto,rectsize);
-
- Transform2D xform;
-
- if (p.rot) {
-
- xform.set_rotation(p.rot);
- xform.translate(-size * size_mult / 2.0);
- xform.elements[2] += p.pos;
- } else {
- xform.elements[2] = -size * size_mult / 2.0;
- xform.elements[2] += p.pos;
- }
-
- if (!local_space) {
- xform = invxform * xform;
- }
-
- xform.scale_basis(Size2(size_mult, size_mult));
-
- VisualServer::get_singleton()->canvas_item_add_set_transform(ci, xform);
-
- if (texrid.is_valid()) {
-
- Rect2 src_rect;
- src_rect.size = size;
-
- if (total_frames > 1) {
- int frame = Math::fast_ftoi(Math::floor(p.frame * total_frames)) % total_frames;
- src_rect.position.x = size.x * (frame % h_frames);
- src_rect.position.y = size.y * (frame / h_frames);
- }
- Rect2 dst_rect(Point2(), size);
- if (flip_h)
- dst_rect.size.x = -dst_rect.size.x;
- if (flip_v)
- dst_rect.size.y = -dst_rect.size.y;
-
- texture->draw_rect_region(ci, dst_rect, src_rect, color);
- //VisualServer::get_singleton()->canvas_item_add_texture_rect(ci,r,texrid,false,color);
- } else {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(), size), color);
- }
- }
-
- } break;
- }
-}
-
-static const char *_particlesframe_property_names[Particles2D::PARAM_MAX] = {
- "params/direction",
- "params/spread",
- "params/linear_velocity",
- "params/spin_velocity",
- "params/orbit_velocity",
- "params/gravity_direction",
- "params/gravity_strength",
- "params/radial_accel",
- "params/tangential_accel",
- "params/damping",
- "params/initial_angle",
- "params/initial_size",
- "params/final_size",
- "params/hue_variation",
- "params/anim_speed_scale",
- "params/anim_initial_pos",
-};
-
-static const char *_particlesframe_property_rnames[Particles2D::PARAM_MAX] = {
- "randomness/direction",
- "randomness/spread",
- "randomness/linear_velocity",
- "randomness/spin_velocity",
- "randomness/orbit_velocity",
- "randomness/gravity_direction",
- "randomness/gravity_strength",
- "randomness/radial_accel",
- "randomness/tangential_accel",
- "randomness/damping",
- "randomness/initial_angle",
- "randomness/initial_size",
- "randomness/final_size",
- "randomness/hue_variation",
- "randomness/anim_speed_scale",
- "randomness/anim_initial_pos",
-};
-
-static const char *_particlesframe_property_ranges[Particles2D::PARAM_MAX] = {
- "0,360,0.01",
- "0,180,0.01",
- "-1024,1024,0.01",
- "-1024,1024,0.01",
- "-1024,1024,0.01",
- "0,360,0.01",
- "0,1024,0.01",
- "-128,128,0.01",
- "-128,128,0.01",
- "0,1024,0.001",
- "0,360,0.01",
- "0,1024,0.01",
- "0,1024,0.01",
- "0,1,0.01",
- "0,128,0.01",
- "0,1,0.01",
-};
-
-void Particles2D::set_emitting(bool p_emitting) {
-
- if (emitting == p_emitting)
- return;
-
- if (p_emitting) {
-
- if (active_count == 0)
- time = 0;
- set_process(process_mode == PROCESS_IDLE);
- set_fixed_process(process_mode == PROCESS_FIXED);
- time_to_live = emit_timeout;
- };
- emitting = p_emitting;
- _change_notify("config/emitting");
+ speed_scale = p_scale;
+ VS::get_singleton()->particles_set_speed_scale(particles, p_scale);
}
bool Particles2D::is_emitting() const {
return emitting;
}
-
-void Particles2D::set_process_mode(ProcessMode p_mode) {
-
- process_mode = p_mode;
- const bool should_process = emitting || active_count != 0;
- set_process(should_process && process_mode == PROCESS_IDLE);
- set_fixed_process(should_process && process_mode == PROCESS_FIXED);
-}
-
-Particles2D::ProcessMode Particles2D::get_process_mode() const {
-
- return process_mode;
-}
-
-void Particles2D::set_amount(int p_amount) {
-
- ERR_FAIL_INDEX(p_amount, 1024 + 1);
-
- particles.resize(p_amount);
-}
int Particles2D::get_amount() const {
- return particles.size();
-}
-
-void Particles2D::set_emit_timeout(float p_timeout) {
-
- emit_timeout = p_timeout;
- time_to_live = p_timeout;
-};
-
-float Particles2D::get_emit_timeout() const {
-
- return emit_timeout;
-};
-
-void Particles2D::set_lifetime(float p_lifetime) {
-
- ERR_FAIL_INDEX(p_lifetime, 3600 + 1);
-
- lifetime = p_lifetime;
+ return amount;
}
float Particles2D::get_lifetime() const {
return lifetime;
}
-
-void Particles2D::set_time_scale(float p_time_scale) {
-
- time_scale = p_time_scale;
-}
-float Particles2D::get_time_scale() const {
-
- return time_scale;
-}
-
-void Particles2D::set_pre_process_time(float p_pre_process_time) {
-
- preprocess = p_pre_process_time;
-}
-
float Particles2D::get_pre_process_time() const {
- return preprocess;
-}
-
-void Particles2D::set_param(Parameter p_param, float p_value) {
-
- ERR_FAIL_INDEX(p_param, PARAM_MAX);
- param[p_param] = p_value;
-}
-float Particles2D::get_param(Parameter p_param) const {
-
- ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0);
- return param[p_param];
-}
-
-void Particles2D::set_randomness(Parameter p_param, float p_value) {
-
- ERR_FAIL_INDEX(p_param, PARAM_MAX);
- randomness[p_param] = p_value;
-}
-float Particles2D::get_randomness(Parameter p_param) const {
-
- ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0);
- return randomness[p_param];
-}
-
-void Particles2D::set_texture(const Ref<Texture> &p_texture) {
-
- texture = p_texture;
-}
-
-Ref<Texture> Particles2D::get_texture() const {
-
- return texture;
+ return pre_process_time;
}
+float Particles2D::get_explosiveness_ratio() const {
-void Particles2D::set_color(const Color &p_color) {
-
- default_color = p_color;
+ return explosiveness_ratio;
}
+float Particles2D::get_randomness_ratio() const {
-Color Particles2D::get_color() const {
-
- return default_color;
+ return randomness_ratio;
}
+Rect2 Particles2D::get_visibility_rect() const {
-void Particles2D::set_gradient(const Ref<Gradient> &p_gradient) {
-
- gradient = p_gradient;
+ return visibility_rect;
}
+bool Particles2D::get_use_local_coordinates() const {
-Ref<Gradient> Particles2D::get_gradient() const {
-
- return gradient;
+ return local_coords;
}
+Ref<Material> Particles2D::get_process_material() const {
-void Particles2D::set_emissor_offset(const Point2 &p_offset) {
-
- emissor_offset = p_offset;
+ return process_material;
}
-Point2 Particles2D::get_emissor_offset() const {
+float Particles2D::get_speed_scale() const {
- return emissor_offset;
+ return speed_scale;
}
-void Particles2D::set_use_local_space(bool p_use) {
+void Particles2D::set_draw_order(DrawOrder p_order) {
- local_space = p_use;
+ draw_order = p_order;
+ VS::get_singleton()->particles_set_draw_order(particles, VS::ParticlesDrawOrder(p_order));
}
-bool Particles2D::is_using_local_space() const {
+Particles2D::DrawOrder Particles2D::get_draw_order() const {
- return local_space;
+ return draw_order;
}
-//Deprecated. Converts color phases to color ramp
-void Particles2D::set_color_phases(int p_phases) {
-
- //Create color ramp if we have 2 or more phases.
- //Otherwise first phase phase will be assigned to default color.
- if (p_phases > 1 && gradient.is_null()) {
- gradient = Ref<Gradient>(memnew(Gradient()));
- }
- if (gradient.is_valid()) {
- gradient->get_points().resize(p_phases);
- }
+void Particles2D::set_fixed_fps(int p_count) {
+ fixed_fps = p_count;
+ VS::get_singleton()->particles_set_fixed_fps(particles, p_count);
}
-//Deprecated.
-int Particles2D::get_color_phases() const {
-
- if (gradient.is_valid()) {
- return gradient->get_points_count();
- }
- return 0;
+int Particles2D::get_fixed_fps() const {
+ return fixed_fps;
}
-//Deprecated. Converts color phases to color ramp
-void Particles2D::set_color_phase_color(int p_phase, const Color &p_color) {
-
- ERR_FAIL_INDEX(p_phase, MAX_COLOR_PHASES);
- if (gradient.is_valid()) {
- if (gradient->get_points_count() > p_phase)
- gradient->set_color(p_phase, p_color);
- } else {
- if (p_phase == 0)
- default_color = p_color;
- }
+void Particles2D::set_fractional_delta(bool p_enable) {
+ fractional_delta = p_enable;
+ VS::get_singleton()->particles_set_fractional_delta(particles, p_enable);
}
-//Deprecated.
-Color Particles2D::get_color_phase_color(int p_phase) const {
-
- ERR_FAIL_INDEX_V(p_phase, MAX_COLOR_PHASES, Color());
- if (gradient.is_valid()) {
- return gradient->get_color(p_phase);
- }
- return Color(0, 0, 0, 1);
+bool Particles2D::get_fractional_delta() const {
+ return fractional_delta;
}
-//Deprecated. Converts color phases to color ramp
-void Particles2D::set_color_phase_pos(int p_phase, float p_pos) {
- ERR_FAIL_INDEX(p_phase, MAX_COLOR_PHASES);
- ERR_FAIL_COND(p_pos < 0.0 || p_pos > 1.0);
- if (gradient.is_valid() && gradient->get_points_count() > p_phase) {
- return gradient->set_offset(p_phase, p_pos);
- }
-}
+String Particles2D::get_configuration_warning() const {
-//Deprecated.
-float Particles2D::get_color_phase_pos(int p_phase) const {
+ String warnings;
- ERR_FAIL_INDEX_V(p_phase, MAX_COLOR_PHASES, 0);
- if (gradient.is_valid()) {
- return gradient->get_offset(p_phase);
+ if (process_material.is_null()) {
+ if (warnings != String())
+ warnings += "\n";
+ warnings += "- " + TTR("A material to process the particles is not assigned, so no behavior is imprinted.");
}
- return 0;
-}
-
-void Particles2D::set_emission_half_extents(const Vector2 &p_extents) {
- extents = p_extents;
+ return warnings;
}
-Vector2 Particles2D::get_emission_half_extents() const {
+Rect2 Particles2D::capture_rect() const {
- return extents;
+ Rect3 aabb = VS::get_singleton()->particles_get_current_aabb(particles);
+ Rect2 r;
+ r.position.x = aabb.position.x;
+ r.position.y = aabb.position.y;
+ r.size.x = aabb.size.x;
+ r.size.y = aabb.size.y;
+ return r;
}
-void Particles2D::set_initial_velocity(const Vector2 &p_velocity) {
-
- initial_velocity = p_velocity;
-}
-Vector2 Particles2D::get_initial_velocity() const {
-
- return initial_velocity;
+void Particles2D::set_texture(const Ref<Texture> &p_texture) {
+ texture = p_texture;
+ update();
}
-void Particles2D::pre_process(float p_delta) {
-
- _process_particles(p_delta);
+Ref<Texture> Particles2D::get_texture() const {
+ return texture;
}
-void Particles2D::set_explosiveness(float p_value) {
+void Particles2D::set_normal_map(const Ref<Texture> &p_normal_map) {
- explosiveness = p_value;
+ normal_map = p_normal_map;
+ update();
}
-float Particles2D::get_explosiveness() const {
-
- return explosiveness;
+Ref<Texture> Particles2D::get_normal_map() const {
+ return normal_map;
}
-void Particles2D::set_flip_h(bool p_flip) {
-
- flip_h = p_flip;
+void Particles2D::_validate_property(PropertyInfo &property) const {
}
-bool Particles2D::is_flipped_h() const {
+void Particles2D::set_v_frames(int p_count) {
- return flip_h;
+ ERR_FAIL_COND(p_count < 1);
+ v_frames = p_count;
+ update();
}
-void Particles2D::set_flip_v(bool p_flip) {
-
- flip_v = p_flip;
-}
-bool Particles2D::is_flipped_v() const {
+int Particles2D::get_v_frames() const {
- return flip_v;
+ return v_frames;
}
-void Particles2D::set_h_frames(int p_frames) {
+void Particles2D::set_h_frames(int p_count) {
- ERR_FAIL_COND(p_frames < 1);
- h_frames = p_frames;
+ ERR_FAIL_COND(p_count < 1);
+ h_frames = p_count;
+ update();
}
int Particles2D::get_h_frames() const {
@@ -932,215 +264,122 @@ int Particles2D::get_h_frames() const {
return h_frames;
}
-void Particles2D::set_v_frames(int p_frames) {
-
- ERR_FAIL_COND(p_frames < 1);
- v_frames = p_frames;
-}
-int Particles2D::get_v_frames() const {
-
- return v_frames;
-}
+void Particles2D::_notification(int p_what) {
-void Particles2D::set_emission_points(const PoolVector<Vector2> &p_points) {
+ if (p_what == NOTIFICATION_DRAW) {
- emission_points = p_points;
-}
+ RID texture_rid;
+ if (texture.is_valid())
+ texture_rid = texture->get_rid();
+ RID normal_rid;
+ if (normal_map.is_valid())
+ normal_rid = texture->get_rid();
-PoolVector<Vector2> Particles2D::get_emission_points() const {
+ VS::get_singleton()->canvas_item_add_particles(get_canvas_item(), particles, texture_rid, normal_rid, h_frames, v_frames);
- return emission_points;
-}
+#ifdef TOOLS_ENABLED
+ if (get_tree()->is_editor_hint() && (this == get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->is_a_parent_of(this))) {
-void Particles2D::reset() {
+ draw_rect(visibility_rect, Color(0, 0.7, 0.9, 0.4), false);
+ }
+#endif
+ }
- for (int i = 0; i < particles.size(); i++) {
- particles[i].active = false;
+ if (p_what == NOTIFICATION_TRANSFORM_CHANGED) {
+ _update_particle_emission_transform();
}
- time = 0;
- active_count = 0;
}
void Particles2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_emitting", "active"), &Particles2D::set_emitting);
- ClassDB::bind_method(D_METHOD("is_emitting"), &Particles2D::is_emitting);
-
- ClassDB::bind_method(D_METHOD("set_process_mode", "mode"), &Particles2D::set_process_mode);
- ClassDB::bind_method(D_METHOD("get_process_mode"), &Particles2D::get_process_mode);
-
+ ClassDB::bind_method(D_METHOD("set_emitting", "emitting"), &Particles2D::set_emitting);
ClassDB::bind_method(D_METHOD("set_amount", "amount"), &Particles2D::set_amount);
- ClassDB::bind_method(D_METHOD("get_amount"), &Particles2D::get_amount);
+ ClassDB::bind_method(D_METHOD("set_lifetime", "secs"), &Particles2D::set_lifetime);
+ ClassDB::bind_method(D_METHOD("set_pre_process_time", "secs"), &Particles2D::set_pre_process_time);
+ ClassDB::bind_method(D_METHOD("set_explosiveness_ratio", "ratio"), &Particles2D::set_explosiveness_ratio);
+ ClassDB::bind_method(D_METHOD("set_randomness_ratio", "ratio"), &Particles2D::set_randomness_ratio);
+ ClassDB::bind_method(D_METHOD("set_visibility_rect", "aabb"), &Particles2D::set_visibility_rect);
+ ClassDB::bind_method(D_METHOD("set_use_local_coordinates", "enable"), &Particles2D::set_use_local_coordinates);
+ ClassDB::bind_method(D_METHOD("set_fixed_fps", "fps"), &Particles2D::set_fixed_fps);
+ ClassDB::bind_method(D_METHOD("set_fractional_delta", "enable"), &Particles2D::set_fractional_delta);
+ ClassDB::bind_method(D_METHOD("set_process_material", "material:Material"), &Particles2D::set_process_material);
+ ClassDB::bind_method(D_METHOD("set_speed_scale", "scale"), &Particles2D::set_speed_scale);
- ClassDB::bind_method(D_METHOD("set_lifetime", "lifetime"), &Particles2D::set_lifetime);
+ ClassDB::bind_method(D_METHOD("is_emitting"), &Particles2D::is_emitting);
+ ClassDB::bind_method(D_METHOD("get_amount"), &Particles2D::get_amount);
ClassDB::bind_method(D_METHOD("get_lifetime"), &Particles2D::get_lifetime);
-
- ClassDB::bind_method(D_METHOD("set_time_scale", "time_scale"), &Particles2D::set_time_scale);
- ClassDB::bind_method(D_METHOD("get_time_scale"), &Particles2D::get_time_scale);
-
- ClassDB::bind_method(D_METHOD("set_pre_process_time", "time"), &Particles2D::set_pre_process_time);
ClassDB::bind_method(D_METHOD("get_pre_process_time"), &Particles2D::get_pre_process_time);
-
- ClassDB::bind_method(D_METHOD("set_emit_timeout", "value"), &Particles2D::set_emit_timeout);
- ClassDB::bind_method(D_METHOD("get_emit_timeout"), &Particles2D::get_emit_timeout);
-
- ClassDB::bind_method(D_METHOD("set_param", "param", "value"), &Particles2D::set_param);
- ClassDB::bind_method(D_METHOD("get_param", "param"), &Particles2D::get_param);
-
- ClassDB::bind_method(D_METHOD("set_randomness", "param", "value"), &Particles2D::set_randomness);
- ClassDB::bind_method(D_METHOD("get_randomness", "param"), &Particles2D::get_randomness);
-
- ClassDB::bind_method(D_METHOD("set_texture:Texture", "texture"), &Particles2D::set_texture);
+ ClassDB::bind_method(D_METHOD("get_explosiveness_ratio"), &Particles2D::get_explosiveness_ratio);
+ ClassDB::bind_method(D_METHOD("get_randomness_ratio"), &Particles2D::get_randomness_ratio);
+ ClassDB::bind_method(D_METHOD("get_visibility_rect"), &Particles2D::get_visibility_rect);
+ ClassDB::bind_method(D_METHOD("get_use_local_coordinates"), &Particles2D::get_use_local_coordinates);
+ ClassDB::bind_method(D_METHOD("get_fixed_fps"), &Particles2D::get_fixed_fps);
+ ClassDB::bind_method(D_METHOD("get_fractional_delta"), &Particles2D::get_fractional_delta);
+ ClassDB::bind_method(D_METHOD("get_process_material:Material"), &Particles2D::get_process_material);
+ ClassDB::bind_method(D_METHOD("get_speed_scale"), &Particles2D::get_speed_scale);
+
+ ClassDB::bind_method(D_METHOD("set_draw_order", "order"), &Particles2D::set_draw_order);
+ ClassDB::bind_method(D_METHOD("get_draw_order"), &Particles2D::get_draw_order);
+
+ ClassDB::bind_method(D_METHOD("set_texture", "texture:Texture"), &Particles2D::set_texture);
ClassDB::bind_method(D_METHOD("get_texture:Texture"), &Particles2D::get_texture);
- ClassDB::bind_method(D_METHOD("set_color", "color"), &Particles2D::set_color);
- ClassDB::bind_method(D_METHOD("get_color"), &Particles2D::get_color);
-
- ClassDB::bind_method(D_METHOD("set_gradient:Gradient", "gradient"), &Particles2D::set_gradient);
- ClassDB::bind_method(D_METHOD("get_gradient:Gradient"), &Particles2D::get_gradient);
-
- ClassDB::bind_method(D_METHOD("set_emissor_offset", "offset"), &Particles2D::set_emissor_offset);
- ClassDB::bind_method(D_METHOD("get_emissor_offset"), &Particles2D::get_emissor_offset);
-
- ClassDB::bind_method(D_METHOD("set_flip_h", "enable"), &Particles2D::set_flip_h);
- ClassDB::bind_method(D_METHOD("is_flipped_h"), &Particles2D::is_flipped_h);
-
- ClassDB::bind_method(D_METHOD("set_flip_v", "enable"), &Particles2D::set_flip_v);
- ClassDB::bind_method(D_METHOD("is_flipped_v"), &Particles2D::is_flipped_v);
+ ClassDB::bind_method(D_METHOD("set_normal_map", "texture:Texture"), &Particles2D::set_normal_map);
+ ClassDB::bind_method(D_METHOD("get_normal_map:Texture"), &Particles2D::get_normal_map);
- ClassDB::bind_method(D_METHOD("set_h_frames", "enable"), &Particles2D::set_h_frames);
- ClassDB::bind_method(D_METHOD("get_h_frames"), &Particles2D::get_h_frames);
+ ClassDB::bind_method(D_METHOD("capture_rect"), &Particles2D::capture_rect);
- ClassDB::bind_method(D_METHOD("set_v_frames", "enable"), &Particles2D::set_v_frames);
+ ClassDB::bind_method(D_METHOD("set_v_frames", "frames"), &Particles2D::set_v_frames);
ClassDB::bind_method(D_METHOD("get_v_frames"), &Particles2D::get_v_frames);
- ClassDB::bind_method(D_METHOD("set_emission_half_extents", "extents"), &Particles2D::set_emission_half_extents);
- ClassDB::bind_method(D_METHOD("get_emission_half_extents"), &Particles2D::get_emission_half_extents);
-
- ClassDB::bind_method(D_METHOD("set_color_phases", "phases"), &Particles2D::set_color_phases);
- ClassDB::bind_method(D_METHOD("get_color_phases"), &Particles2D::get_color_phases);
-
- ClassDB::bind_method(D_METHOD("set_color_phase_color", "phase", "color"), &Particles2D::set_color_phase_color);
- ClassDB::bind_method(D_METHOD("get_color_phase_color", "phase"), &Particles2D::get_color_phase_color);
-
- ClassDB::bind_method(D_METHOD("set_color_phase_pos", "phase", "pos"), &Particles2D::set_color_phase_pos);
- ClassDB::bind_method(D_METHOD("get_color_phase_pos", "phase"), &Particles2D::get_color_phase_pos);
-
- ClassDB::bind_method(D_METHOD("pre_process", "time"), &Particles2D::pre_process);
- ClassDB::bind_method(D_METHOD("reset"), &Particles2D::reset);
-
- ClassDB::bind_method(D_METHOD("set_use_local_space", "enable"), &Particles2D::set_use_local_space);
- ClassDB::bind_method(D_METHOD("is_using_local_space"), &Particles2D::is_using_local_space);
-
- ClassDB::bind_method(D_METHOD("set_initial_velocity", "velocity"), &Particles2D::set_initial_velocity);
- ClassDB::bind_method(D_METHOD("get_initial_velocity"), &Particles2D::get_initial_velocity);
-
- ClassDB::bind_method(D_METHOD("set_explosiveness", "amount"), &Particles2D::set_explosiveness);
- ClassDB::bind_method(D_METHOD("get_explosiveness"), &Particles2D::get_explosiveness);
-
- ClassDB::bind_method(D_METHOD("set_emission_points", "points"), &Particles2D::set_emission_points);
- ClassDB::bind_method(D_METHOD("get_emission_points"), &Particles2D::get_emission_points);
-
- ADD_SIGNAL(MethodInfo("emission_finished"));
-
- ADD_PROPERTY(PropertyInfo(Variant::INT, "config/amount", PROPERTY_HINT_EXP_RANGE, "1,1024"), "set_amount", "get_amount");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "config/lifetime", PROPERTY_HINT_EXP_RANGE, "0.1,3600,0.1"), "set_lifetime", "get_lifetime");
- ADD_PROPERTYNO(PropertyInfo(Variant::REAL, "config/time_scale", PROPERTY_HINT_EXP_RANGE, "0.01,128,0.01"), "set_time_scale", "get_time_scale");
- ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "config/preprocess", PROPERTY_HINT_EXP_RANGE, "0,3600,0.1"), "set_pre_process_time", "get_pre_process_time");
- ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "config/emit_timeout", PROPERTY_HINT_RANGE, "0,3600,0.1"), "set_emit_timeout", "get_emit_timeout");
- ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "config/emitting"), "set_emitting", "is_emitting");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "config/process_mode", PROPERTY_HINT_ENUM, "Fixed,Idle"), "set_process_mode", "get_process_mode");
- ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "config/offset"), "set_emissor_offset", "get_emissor_offset");
- ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "config/half_extents"), "set_emission_half_extents", "get_emission_half_extents");
- ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "config/local_space"), "set_use_local_space", "is_using_local_space");
- ADD_PROPERTYNO(PropertyInfo(Variant::REAL, "config/explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness", "get_explosiveness");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "config/flip_h"), "set_flip_h", "is_flipped_h");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "config/flip_v"), "set_flip_v", "is_flipped_v");
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "config/texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
- ADD_PROPERTYNO(PropertyInfo(Variant::INT, "config/h_frames", PROPERTY_HINT_RANGE, "1,512,1"), "set_h_frames", "get_h_frames");
- ADD_PROPERTYNO(PropertyInfo(Variant::INT, "config/v_frames", PROPERTY_HINT_RANGE, "1,512,1"), "set_v_frames", "get_v_frames");
-
- for (int i = 0; i < PARAM_MAX; i++) {
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, _particlesframe_property_names[i], PROPERTY_HINT_RANGE, _particlesframe_property_ranges[i]), "set_param", "get_param", i);
- }
-
- for (int i = 0; i < PARAM_MAX; i++) {
- ADD_PROPERTYINZ(PropertyInfo(Variant::REAL, _particlesframe_property_rnames[i], PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_randomness", "get_randomness", i);
- }
-
- ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "color_phases/count", PROPERTY_HINT_RANGE, "0,4,1", 0), "set_color_phases", "get_color_phases");
-
- //Backward compatibility. They will be converted to color ramp
- for (int i = 0; i < MAX_COLOR_PHASES; i++) {
- String phase = "phase_" + itos(i) + "/";
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, phase + "pos", PROPERTY_HINT_RANGE, "0,1,0.01", 0), "set_color_phase_pos", "get_color_phase_pos", i);
- ADD_PROPERTYI(PropertyInfo(Variant::COLOR, phase + "color", PROPERTY_HINT_NONE, "", 0), "set_color_phase_color", "get_color_phase_color", i);
- }
+ ClassDB::bind_method(D_METHOD("set_h_frames", "frames"), &Particles2D::set_h_frames);
+ ClassDB::bind_method(D_METHOD("get_h_frames"), &Particles2D::get_h_frames);
- ADD_PROPERTYNO(PropertyInfo(Variant::COLOR, "color/color"), "set_color", "get_color");
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "color/color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_gradient", "get_gradient");
-
- ADD_PROPERTYNZ(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "emission_points", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_emission_points", "get_emission_points");
-
- BIND_CONSTANT(PARAM_DIRECTION);
- BIND_CONSTANT(PARAM_SPREAD);
- BIND_CONSTANT(PARAM_LINEAR_VELOCITY);
- BIND_CONSTANT(PARAM_SPIN_VELOCITY);
- BIND_CONSTANT(PARAM_ORBIT_VELOCITY);
- BIND_CONSTANT(PARAM_GRAVITY_DIRECTION);
- BIND_CONSTANT(PARAM_GRAVITY_STRENGTH);
- BIND_CONSTANT(PARAM_RADIAL_ACCEL);
- BIND_CONSTANT(PARAM_TANGENTIAL_ACCEL);
- BIND_CONSTANT(PARAM_DAMPING);
- BIND_CONSTANT(PARAM_INITIAL_ANGLE);
- BIND_CONSTANT(PARAM_INITIAL_SIZE);
- BIND_CONSTANT(PARAM_FINAL_SIZE);
- BIND_CONSTANT(PARAM_HUE_VARIATION);
- BIND_CONSTANT(PARAM_ANIM_SPEED_SCALE);
- BIND_CONSTANT(PARAM_ANIM_INITIAL_POS);
- BIND_CONSTANT(PARAM_MAX);
-
- BIND_CONSTANT(MAX_COLOR_PHASES);
+ ADD_GROUP("Parameters", "");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,100000,1"), "set_amount", "get_amount");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01"), "set_lifetime", "get_lifetime");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "preprocess", PROPERTY_HINT_RANGE, "0.00,600.0,0.01"), "set_pre_process_time", "get_pre_process_time");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "speed_scale", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_speed_scale", "get_speed_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio");
+ ADD_PROPERTY(PropertyInfo(Variant::RECT3, "visibility_rect"), "set_visibility_rect", "get_visibility_rect");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "local_coords"), "set_use_local_coordinates", "get_use_local_coordinates");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_fps", PROPERTY_HINT_RANGE, "0,1000,1"), "set_fixed_fps", "get_fixed_fps");
+ 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, "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");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "h_frames", PROPERTY_HINT_RANGE, "1,1024,1"), "set_h_frames", "get_h_frames");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "v_frames", PROPERTY_HINT_RANGE, "1,1024,1"), "set_v_frames", "get_v_frames");
+
+ BIND_CONSTANT(DRAW_ORDER_INDEX);
+ BIND_CONSTANT(DRAW_ORDER_LIFETIME);
}
Particles2D::Particles2D() {
- for (int i = 0; i < PARAM_MAX; i++) {
-
- param[i] = 0;
- randomness[i] = 0;
- }
+ particles = VS::get_singleton()->particles_create();
- set_param(PARAM_SPREAD, 10);
- set_param(PARAM_LINEAR_VELOCITY, 20);
- set_param(PARAM_GRAVITY_STRENGTH, 9.8);
- set_param(PARAM_RADIAL_ACCEL, 0);
- set_param(PARAM_TANGENTIAL_ACCEL, 0);
- set_param(PARAM_INITIAL_ANGLE, 0.0);
- set_param(PARAM_INITIAL_SIZE, 1.0);
- set_param(PARAM_FINAL_SIZE, 1.0);
- set_param(PARAM_ANIM_SPEED_SCALE, 1.0);
-
- set_color(Color(1, 1, 1, 1));
-
- time = 0;
- lifetime = 2;
- emitting = false;
- particles.resize(32);
- active_count = -1;
set_emitting(true);
- process_mode = PROCESS_IDLE;
- local_space = true;
- preprocess = 0;
- time_scale = 1.0;
-
- flip_h = false;
- flip_v = false;
-
- v_frames = 1;
+ set_amount(8);
+ set_lifetime(1);
+ set_fixed_fps(0);
+ set_fractional_delta(true);
+ set_pre_process_time(0);
+ set_explosiveness_ratio(0);
+ set_randomness_ratio(0);
+ set_visibility_rect(Rect2(Vector2(-100, -100), Vector2(200, 200)));
+ set_use_local_coordinates(true);
+ set_speed_scale(1);
h_frames = 1;
+ v_frames = 1;
+}
+
+Particles2D::~Particles2D() {
- emit_timeout = 0;
- time_to_live = 0;
- explosiveness = 1.0;
+ VS::get_singleton()->free(particles);
}
diff --git a/scene/2d/particles_2d.h b/scene/2d/particles_2d.h
index 856beaa836..ab7dcb1464 100644
--- a/scene/2d/particles_2d.h
+++ b/scene/2d/particles_2d.h
@@ -34,235 +34,98 @@
#include "scene/resources/color_ramp.h"
#include "scene/resources/texture.h"
-class Particles2D;
-class ParticleAttractor2D : public Node2D {
-
- GDCLASS(ParticleAttractor2D, Node2D);
-
- friend class Particles2D;
- bool enabled;
- float radius;
- float disable_radius;
- float gravity;
- float absorption;
- NodePath path;
-
- Particles2D *owner;
-
- void _update_owner();
- void _owner_exited();
- void _set_owner(Particles2D *p_owner);
-
- void _notification(int p_what);
- static void _bind_methods();
-
-public:
- void set_enabled(bool p_enabled);
- bool is_enabled() const;
-
- void set_radius(float p_radius);
- float get_radius() const;
-
- void set_disable_radius(float p_disable_radius);
- float get_disable_radius() const;
-
- void set_gravity(float p_gravity);
- float get_gravity() const;
-
- void set_absorption(float p_absorption);
- float get_absorption() const;
-
- void set_particles_path(NodePath p_path);
- NodePath get_particles_path() const;
-
- virtual String get_configuration_warning() const;
-
- ParticleAttractor2D();
-};
-
class Particles2D : public Node2D {
-
- GDCLASS(Particles2D, Node2D);
+private:
+ GDCLASS(Particles2D, Node2D)
public:
- enum Parameter {
- PARAM_DIRECTION,
- PARAM_SPREAD,
- PARAM_LINEAR_VELOCITY,
- PARAM_SPIN_VELOCITY,
- PARAM_ORBIT_VELOCITY,
- PARAM_GRAVITY_DIRECTION,
- PARAM_GRAVITY_STRENGTH,
- PARAM_RADIAL_ACCEL,
- PARAM_TANGENTIAL_ACCEL,
- PARAM_DAMPING,
- PARAM_INITIAL_ANGLE,
- PARAM_INITIAL_SIZE,
- PARAM_FINAL_SIZE,
- PARAM_HUE_VARIATION,
- PARAM_ANIM_SPEED_SCALE,
- PARAM_ANIM_INITIAL_POS,
- PARAM_MAX
- };
-
- enum {
- MAX_COLOR_PHASES = 4
- };
-
- enum ProcessMode {
- PROCESS_FIXED,
- PROCESS_IDLE,
+ enum DrawOrder {
+ DRAW_ORDER_INDEX,
+ DRAW_ORDER_LIFETIME,
};
private:
- float param[PARAM_MAX];
- float randomness[PARAM_MAX];
+ RID particles;
- struct Particle {
- bool active;
- Point2 pos;
- Vector2 velocity;
- float rot;
- float frame;
- uint64_t seed;
- Particle() {
- active = false;
- seed = 123465789;
- rot = 0;
- frame = 0;
- }
- };
-
- Vector<Particle> particles;
-
- struct AttractorCache {
-
- Vector2 pos;
- ParticleAttractor2D *attractor;
- };
-
- Vector<AttractorCache> attractor_cache;
-
- float explosiveness;
- float preprocess;
- float lifetime;
bool emitting;
- bool local_space;
- float emit_timeout;
- float time_to_live;
- float time_scale;
- bool flip_h;
- bool flip_v;
- int h_frames;
+ int amount;
+ float lifetime;
+ float pre_process_time;
+ float explosiveness_ratio;
+ float randomness_ratio;
+ float speed_scale;
+ Rect2 visibility_rect;
+ bool local_coords;
+ int fixed_fps;
+ bool fractional_delta;
int v_frames;
- Point2 emissor_offset;
- Vector2 initial_velocity;
- Vector2 extents;
- PoolVector<Vector2> emission_points;
+ int h_frames;
- ProcessMode process_mode;
+ Ref<Material> process_material;
- float time;
- int active_count;
+ DrawOrder draw_order;
Ref<Texture> texture;
+ Ref<Texture> normal_map;
- //If no color ramp is set then default color is used. Created as simple alternative to color_ramp.
- Color default_color;
- Ref<Gradient> gradient;
-
- void _process_particles(float p_delta);
- friend class ParticleAttractor2D;
-
- Set<ParticleAttractor2D *> attractors;
+ void _update_particle_emission_transform();
protected:
- void _notification(int p_what);
static void _bind_methods();
+ virtual void _validate_property(PropertyInfo &property) const;
+ void _notification(int p_what);
public:
void set_emitting(bool p_emitting);
- bool is_emitting() const;
-
- void set_process_mode(ProcessMode p_mode);
- ProcessMode get_process_mode() const;
-
void set_amount(int p_amount);
- int get_amount() const;
-
void set_lifetime(float p_lifetime);
- float get_lifetime() const;
-
- void set_time_scale(float p_time_scale);
- float get_time_scale() const;
+ void set_pre_process_time(float p_time);
+ void set_explosiveness_ratio(float p_ratio);
+ void set_randomness_ratio(float p_ratio);
+ void set_visibility_rect(const Rect2 &p_aabb);
+ void set_use_local_coordinates(bool p_enable);
+ void set_process_material(const Ref<Material> &p_material);
+ void set_speed_scale(float p_scale);
- void set_pre_process_time(float p_pre_process_time);
+ bool is_emitting() const;
+ int get_amount() const;
+ float get_lifetime() const;
float get_pre_process_time() const;
+ float get_explosiveness_ratio() const;
+ float get_randomness_ratio() const;
+ Rect2 get_visibility_rect() const;
+ bool get_use_local_coordinates() const;
+ Ref<Material> get_process_material() const;
+ float get_speed_scale() const;
- void set_emit_timeout(float p_timeout);
- float get_emit_timeout() const;
-
- void set_emission_half_extents(const Vector2 &p_extents);
- Vector2 get_emission_half_extents() const;
+ void set_fixed_fps(int p_count);
+ int get_fixed_fps() const;
- void set_param(Parameter p_param, float p_value);
- float get_param(Parameter p_param) const;
+ void set_fractional_delta(bool p_enable);
+ bool get_fractional_delta() const;
- void set_randomness(Parameter p_randomness, float p_value);
- float get_randomness(Parameter p_randomness) const;
-
- void set_explosiveness(float p_value);
- float get_explosiveness() const;
-
- void set_flip_h(bool p_flip);
- bool is_flipped_h() const;
-
- void set_flip_v(bool p_flip);
- bool is_flipped_v() const;
-
- void set_h_frames(int p_frames);
- int get_h_frames() const;
-
- void set_v_frames(int p_frames);
- int get_v_frames() const;
-
- void set_color_phases(int p_phases);
- int get_color_phases() const;
-
- void set_color_phase_color(int p_phase, const Color &p_color);
- Color get_color_phase_color(int p_phase) const;
-
- void set_color_phase_pos(int p_phase, float p_pos);
- float get_color_phase_pos(int p_phase) const;
+ void set_draw_order(DrawOrder p_order);
+ DrawOrder get_draw_order() const;
void set_texture(const Ref<Texture> &p_texture);
Ref<Texture> get_texture() const;
- void set_color(const Color &p_color);
- Color get_color() const;
-
- void set_gradient(const Ref<Gradient> &p_texture);
- Ref<Gradient> get_gradient() const;
-
- void set_emissor_offset(const Point2 &p_offset);
- Point2 get_emissor_offset() const;
+ void set_normal_map(const Ref<Texture> &p_normal_map);
+ Ref<Texture> get_normal_map() const;
- void set_use_local_space(bool p_use);
- bool is_using_local_space() const;
-
- void set_initial_velocity(const Vector2 &p_velocity);
- Vector2 get_initial_velocity() const;
+ virtual String get_configuration_warning() const;
- void set_emission_points(const PoolVector<Vector2> &p_points);
- PoolVector<Vector2> get_emission_points() const;
+ void set_v_frames(int p_count);
+ int get_v_frames() const;
- void pre_process(float p_delta);
- void reset();
+ void set_h_frames(int p_count);
+ int get_h_frames() const;
+ Rect2 capture_rect() const;
Particles2D();
+ ~Particles2D();
};
-VARIANT_ENUM_CAST(Particles2D::ProcessMode);
-VARIANT_ENUM_CAST(Particles2D::Parameter);
+VARIANT_ENUM_CAST(Particles2D::DrawOrder)
#endif // PARTICLES_FRAME_H
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index 68270ed771..fd261117e1 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -44,28 +44,6 @@ void PhysicsBody2D::_notification(int p_what) {
*/
}
-void PhysicsBody2D::set_one_way_collision_direction(const Vector2 &p_dir) {
-
- one_way_collision_direction = p_dir;
- Physics2DServer::get_singleton()->body_set_one_way_collision_direction(get_rid(), p_dir);
-}
-
-Vector2 PhysicsBody2D::get_one_way_collision_direction() const {
-
- return one_way_collision_direction;
-}
-
-void PhysicsBody2D::set_one_way_collision_max_depth(float p_depth) {
-
- one_way_collision_max_depth = p_depth;
- Physics2DServer::get_singleton()->body_set_one_way_collision_max_depth(get_rid(), p_depth);
-}
-
-float PhysicsBody2D::get_one_way_collision_max_depth() const {
-
- return one_way_collision_max_depth;
-}
-
void PhysicsBody2D::_set_layers(uint32_t p_mask) {
set_collision_layer(p_mask);
@@ -92,10 +70,6 @@ void PhysicsBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_layers", "mask"), &PhysicsBody2D::_set_layers);
ClassDB::bind_method(D_METHOD("_get_layers"), &PhysicsBody2D::_get_layers);
- ClassDB::bind_method(D_METHOD("set_one_way_collision_direction", "dir"), &PhysicsBody2D::set_one_way_collision_direction);
- ClassDB::bind_method(D_METHOD("get_one_way_collision_direction"), &PhysicsBody2D::get_one_way_collision_direction);
- ClassDB::bind_method(D_METHOD("set_one_way_collision_max_depth", "depth"), &PhysicsBody2D::set_one_way_collision_max_depth);
- ClassDB::bind_method(D_METHOD("get_one_way_collision_max_depth"), &PhysicsBody2D::get_one_way_collision_max_depth);
ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body:PhysicsBody2D"), &PhysicsBody2D::add_collision_exception_with);
ClassDB::bind_method(D_METHOD("remove_collision_exception_with", "body:PhysicsBody2D"), &PhysicsBody2D::remove_collision_exception_with);
ADD_PROPERTY(PropertyInfo(Variant::INT, "layers", PROPERTY_HINT_LAYERS_2D_PHYSICS, "", 0), "_set_layers", "_get_layers"); //for backwards compat
@@ -103,9 +77,6 @@ void PhysicsBody2D::_bind_methods() {
ADD_GROUP("Collision", "collision_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_layer", "get_collision_layer");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_mask", "get_collision_mask");
- ADD_GROUP("", "");
- ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "one_way_collision/direction"), "set_one_way_collision_direction", "get_one_way_collision_direction");
- ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "one_way_collision/max_depth"), "set_one_way_collision_max_depth", "get_one_way_collision_max_depth");
}
void PhysicsBody2D::set_collision_layer(uint32_t p_layer) {
@@ -164,7 +135,6 @@ PhysicsBody2D::PhysicsBody2D(Physics2DServer::BodyMode p_mode)
collision_layer = 1;
collision_mask = 1;
- set_one_way_collision_max_depth(0);
set_pickable(false);
}
@@ -971,248 +941,105 @@ RigidBody2D::~RigidBody2D() {
//////////////////////////
-Variant KinematicBody2D::_get_collider() const {
-
- ObjectID oid = get_collider();
- if (oid == 0)
- return Variant();
- Object *obj = ObjectDB::get_instance(oid);
- if (!obj)
- return Variant();
-
- Reference *ref = obj->cast_to<Reference>();
- if (ref) {
- return Ref<Reference>(ref);
- }
-
- return obj;
-}
-
-void KinematicBody2D::revert_motion() {
+Dictionary KinematicBody2D::_move(const Vector2 &p_motion) {
+
+ Collision col;
+ if (move(p_motion, col)) {
+ Dictionary d;
+ d["position"] = col.collision;
+ d["normal"] = col.collision;
+ d["local_shape"] = col.local_shape;
+ d["travel"] = col.travel;
+ d["remainder"] = col.remainder;
+ d["collider_id"] = col.collider;
+ if (col.collider) {
+ d["collider"] = ObjectDB::get_instance(col.collider);
+ } else {
+ d["collider"] = Variant();
+ }
- Transform2D gt = get_global_transform();
- gt.elements[2] -= travel;
- travel = Vector2();
- set_global_transform(gt);
-}
+ d["collider_shape_index"] = col.collider_shape;
+ d["collider_metadata"] = col.collider_metadata;
-Vector2 KinematicBody2D::get_travel() const {
+ return d;
- return travel;
+ } else {
+ return Dictionary();
+ }
}
-Vector2 KinematicBody2D::move(const Vector2 &p_motion) {
-
-#if 1
+bool KinematicBody2D::move(const Vector2 &p_motion, Collision &r_collision) {
Transform2D gt = get_global_transform();
Physics2DServer::MotionResult result;
- colliding = Physics2DServer::get_singleton()->body_test_motion(get_rid(), gt, p_motion, margin, &result);
-
- collider_metadata = result.collider_metadata;
- collider_shape = result.collider_shape;
- collider_vel = result.collider_velocity;
- collision = result.collision_point;
- normal = result.collision_normal;
- collider = result.collider_id;
-
- gt.elements[2] += result.motion;
- set_global_transform(gt);
- travel = result.motion;
-
- return result.remainder;
-
-#else
- //give me back regular physics engine logic
- //this is madness
- //and most people using this function will think
- //what it does is simpler than using physics
- //this took about a week to get right..
- //but is it right? who knows at this point..
-
- colliding = false;
- ERR_FAIL_COND_V(!is_inside_tree(), Vector2());
- Physics2DDirectSpaceState *dss = Physics2DServer::get_singleton()->space_get_direct_state(get_world_2d()->get_space());
- ERR_FAIL_COND_V(!dss, Vector2());
- const int max_shapes = 32;
- Vector2 sr[max_shapes * 2];
- int res_shapes;
-
- Set<RID> exclude;
- exclude.insert(get_rid());
-
- //recover first
- int recover_attempts = 4;
-
- bool collided = false;
- uint32_t mask = 0;
- if (true)
- mask |= Physics2DDirectSpaceState::TYPE_MASK_STATIC_BODY;
- if (true)
- mask |= Physics2DDirectSpaceState::TYPE_MASK_KINEMATIC_BODY;
- if (true)
- mask |= Physics2DDirectSpaceState::TYPE_MASK_RIGID_BODY;
- if (true)
- mask |= Physics2DDirectSpaceState::TYPE_MASK_CHARACTER_BODY;
-
- //print_line("margin: "+rtos(margin));
- do {
-
- //motion recover
- for (int i = 0; i < get_shape_count(); i++) {
-
- if (is_shape_set_as_trigger(i))
- continue;
- if (dss->collide_shape(get_shape(i)->get_rid(), get_global_transform() * get_shape_transform(i), Vector2(), margin, sr, max_shapes, res_shapes, exclude, get_layer_mask(), mask))
- collided = true;
- }
-
- if (!collided)
- break;
-
- Vector2 recover_motion;
-
- for (int i = 0; i < res_shapes; i++) {
-
- Vector2 a = sr[i * 2 + 0];
- Vector2 b = sr[i * 2 + 1];
-
- float d = a.distance_to(b);
-
- /*
- if (d<margin)
- continue;
- */
- recover_motion += (b - a) * 0.4;
- }
-
- if (recover_motion == Vector2()) {
- collided = false;
- break;
- }
-
- Transform2D gt = get_global_transform();
- gt.elements[2] += recover_motion;
- set_global_transform(gt);
-
- recover_attempts--;
-
- } while (recover_attempts);
-
- //move second
- float safe = 1.0;
- float unsafe = 1.0;
- int best_shape = -1;
-
- for (int i = 0; i < get_shape_count(); i++) {
-
- if (is_shape_set_as_trigger(i))
- continue;
-
- float lsafe, lunsafe;
- bool valid = dss->cast_motion(get_shape(i)->get_rid(), get_global_transform() * get_shape_transform(i), p_motion, 0, lsafe, lunsafe, exclude, get_layer_mask(), mask);
- //print_line("shape: "+itos(i)+" travel:"+rtos(ltravel));
- if (!valid) {
-
- safe = 0;
- unsafe = 0;
- best_shape = i; //sadly it's the best
- break;
- }
- if (lsafe == 1.0) {
- continue;
- }
- if (lsafe < safe) {
-
- safe = lsafe;
- unsafe = lunsafe;
- best_shape = i;
- }
+ bool colliding = Physics2DServer::get_singleton()->body_test_motion(get_rid(), gt, p_motion, margin, &result);
+
+ if (colliding) {
+ r_collision.collider_metadata = result.collider_metadata;
+ r_collision.collider_shape = result.collider_shape;
+ r_collision.collider_vel = result.collider_velocity;
+ r_collision.collision = result.collision_point;
+ r_collision.normal = result.collision_normal;
+ r_collision.collider = result.collider_id;
+ r_collision.travel = result.motion;
+ r_collision.remainder = result.remainder;
+ r_collision.local_shape = result.collision_local_shape;
}
- //print_line("best shape: "+itos(best_shape)+" motion "+p_motion);
-
- if (safe >= 1) {
- //not collided
- colliding = false;
-
- } else {
-
- //it collided, let's get the rest info in unsafe advance
- Transform2D ugt = get_global_transform();
- ugt.elements[2] += p_motion * unsafe;
- Physics2DDirectSpaceState::ShapeRestInfo rest_info;
- bool c2 = dss->rest_info(get_shape(best_shape)->get_rid(), ugt * get_shape_transform(best_shape), Vector2(), margin, &rest_info, exclude, get_layer_mask(), mask);
- if (!c2) {
- //should not happen, but floating point precision is so weird..
-
- colliding = false;
- } else {
-
- //print_line("Travel: "+rtos(travel));
- colliding = true;
- collision = rest_info.point;
- normal = rest_info.normal;
- collider = rest_info.collider_id;
- collider_vel = rest_info.linear_velocity;
- collider_shape = rest_info.shape;
- collider_metadata = rest_info.metadata;
- }
- }
-
- Vector2 motion = p_motion * safe;
- Transform2D gt = get_global_transform();
- gt.elements[2] += motion;
+ gt.elements[2] += result.motion;
set_global_transform(gt);
- return p_motion - motion;
-#endif
+ return colliding;
}
Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction, float p_slope_stop_min_velocity, int p_max_bounces, float p_floor_max_angle) {
- Vector2 motion = (move_and_slide_floor_velocity + p_linear_velocity) * get_fixed_process_delta_time();
+ Vector2 motion = (floor_velocity + p_linear_velocity) * get_fixed_process_delta_time();
Vector2 lv = p_linear_velocity;
- move_and_slide_on_floor = false;
- move_and_slide_on_ceiling = false;
- move_and_slide_on_wall = false;
- move_and_slide_colliders.clear();
- move_and_slide_floor_velocity = Vector2();
+ on_floor = false;
+ on_ceiling = false;
+ on_wall = false;
+ colliders.clear();
+ floor_velocity = Vector2();
while (p_max_bounces) {
- motion = move(motion);
+ Collision collision;
+
+ bool collided = move(motion, collision);
- if (is_colliding()) {
+ if (collided) {
+
+ motion = collision.remainder;
if (p_floor_direction == Vector2()) {
//all is a wall
- move_and_slide_on_wall = true;
+ on_wall = true;
} else {
- if (get_collision_normal().dot(p_floor_direction) >= Math::cos(p_floor_max_angle)) { //floor
+ if (collision.normal.dot(p_floor_direction) >= Math::cos(p_floor_max_angle)) { //floor
- move_and_slide_on_floor = true;
- move_and_slide_floor_velocity = get_collider_velocity();
+ on_floor = true;
+ floor_velocity = collision.collider_vel;
- if (get_travel().length() < 1 && ABS((lv.x - move_and_slide_floor_velocity.x)) < p_slope_stop_min_velocity) {
- revert_motion();
+ if (collision.travel.length() < 1 && ABS((lv.x - floor_velocity.x)) < p_slope_stop_min_velocity) {
+ Transform2D gt = get_global_transform();
+ gt.elements[2] -= collision.travel;
+ set_global_transform(gt);
return Vector2();
}
- } else if (get_collision_normal().dot(-p_floor_direction) >= Math::cos(p_floor_max_angle)) { //ceiling
- move_and_slide_on_ceiling = true;
+ } else if (collision.normal.dot(-p_floor_direction) >= Math::cos(p_floor_max_angle)) { //ceiling
+ on_ceiling = true;
} else {
- move_and_slide_on_wall = true;
+ on_wall = true;
}
}
- Vector2 n = get_collision_normal();
+ Vector2 n = collision.normal;
motion = motion.slide(n);
lv = lv.slide(n);
- Variant collider = _get_collider();
- if (collider.get_type() != Variant::NIL) {
- move_and_slide_colliders.push_back(collider);
- }
+
+ colliders.push_back(collision);
} else {
break;
@@ -1226,26 +1053,22 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const
return lv;
}
-bool KinematicBody2D::is_move_and_slide_on_floor() const {
+bool KinematicBody2D::is_on_floor() const {
- return move_and_slide_on_floor;
+ return on_floor;
}
-bool KinematicBody2D::is_move_and_slide_on_wall() const {
+bool KinematicBody2D::is_on_wall() const {
- return move_and_slide_on_wall;
+ return on_wall;
}
-bool KinematicBody2D::is_move_and_slide_on_ceiling() const {
+bool KinematicBody2D::is_on_ceiling() const {
- return move_and_slide_on_ceiling;
+ return on_ceiling;
}
-Array KinematicBody2D::get_move_and_slide_colliders() const {
- return move_and_slide_colliders;
-}
+Vector2 KinematicBody2D::get_floor_velocity() const {
-Vector2 KinematicBody2D::move_to(const Vector2 &p_position) {
-
- return move(p_position - get_global_position());
+ return floor_velocity;
}
bool KinematicBody2D::test_move(const Transform2D &p_from, const Vector2 &p_motion) {
@@ -1255,98 +1078,123 @@ bool KinematicBody2D::test_move(const Transform2D &p_from, const Vector2 &p_moti
return Physics2DServer::get_singleton()->body_test_motion(get_rid(), p_from, p_motion, margin);
}
-Vector2 KinematicBody2D::get_collision_pos() const {
+void KinematicBody2D::set_safe_margin(float p_margin) {
- ERR_FAIL_COND_V(!colliding, Vector2());
- return collision;
+ margin = p_margin;
}
-Vector2 KinematicBody2D::get_collision_normal() const {
+float KinematicBody2D::get_safe_margin() const {
- ERR_FAIL_COND_V(!colliding, Vector2());
- return normal;
+ return margin;
}
-Vector2 KinematicBody2D::get_collider_velocity() const {
+int KinematicBody2D::get_collision_count() const {
- return collider_vel;
+ return colliders.size();
}
+Vector2 KinematicBody2D::get_collision_position(int p_collision) const {
-ObjectID KinematicBody2D::get_collider() const {
+ ERR_FAIL_INDEX_V(p_collision, colliders.size(), Vector2());
- ERR_FAIL_COND_V(!colliding, 0);
- return collider;
+ return colliders[p_collision].collision;
}
-
-int KinematicBody2D::get_collider_shape() const {
-
- ERR_FAIL_COND_V(!colliding, 0);
- return collider_shape;
+Vector2 KinematicBody2D::get_collision_normal(int p_collision) const {
+ ERR_FAIL_INDEX_V(p_collision, colliders.size(), Vector2());
+ return colliders[p_collision].normal;
}
-Variant KinematicBody2D::get_collider_metadata() const {
-
- ERR_FAIL_COND_V(!colliding, 0);
- return collider_metadata;
+Vector2 KinematicBody2D::get_collision_travel(int p_collision) const {
+ ERR_FAIL_INDEX_V(p_collision, colliders.size(), Vector2());
+ return colliders[p_collision].travel;
}
-
-bool KinematicBody2D::is_colliding() const {
-
- return colliding;
+Vector2 KinematicBody2D::get_collision_remainder(int p_collision) const {
+ ERR_FAIL_INDEX_V(p_collision, colliders.size(), Vector2());
+ return colliders[p_collision].remainder;
}
+Object *KinematicBody2D::get_collision_local_shape(int p_collision) const {
+ ERR_FAIL_INDEX_V(p_collision, colliders.size(), NULL);
+ uint32_t owner = shape_find_owner(colliders[p_collision].local_shape);
+ return shape_owner_get_owner(owner);
+}
+Object *KinematicBody2D::get_collision_collider(int p_collision) const {
+ ERR_FAIL_INDEX_V(p_collision, colliders.size(), NULL);
-void KinematicBody2D::set_collision_margin(float p_margin) {
+ if (colliders[p_collision].collider) {
+ return ObjectDB::get_instance(colliders[p_collision].collider);
+ }
- margin = p_margin;
+ return NULL;
}
+ObjectID KinematicBody2D::get_collision_collider_id(int p_collision) const {
+ ERR_FAIL_INDEX_V(p_collision, colliders.size(), 0);
-float KinematicBody2D::get_collision_margin() const {
+ return colliders[p_collision].collider;
+}
+Object *KinematicBody2D::get_collision_collider_shape(int p_collision) const {
+ ERR_FAIL_INDEX_V(p_collision, colliders.size(), NULL);
+ Object *collider = get_collision_collider(p_collision);
+ if (collider) {
+ CollisionObject2D *obj2d = collider->cast_to<CollisionObject2D>();
+ if (obj2d) {
+ uint32_t owner = shape_find_owner(colliders[p_collision].collider_shape);
+ return obj2d->shape_owner_get_owner(owner);
+ }
+ }
- return margin;
+ return NULL;
+}
+int KinematicBody2D::get_collision_collider_shape_index(int p_collision) const {
+ ERR_FAIL_INDEX_V(p_collision, colliders.size(), -1);
+ return colliders[p_collision].collider_shape;
+}
+Vector2 KinematicBody2D::get_collision_collider_velocity(int p_collision) const {
+ ERR_FAIL_INDEX_V(p_collision, colliders.size(), Vector2());
+ return colliders[p_collision].collider_vel;
+}
+Variant KinematicBody2D::get_collision_collider_metadata(int p_collision) const {
+ ERR_FAIL_INDEX_V(p_collision, colliders.size(), Variant());
+ return colliders[p_collision].collider_metadata;
}
void KinematicBody2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("move", "rel_vec"), &KinematicBody2D::move);
- ClassDB::bind_method(D_METHOD("move_to", "position"), &KinematicBody2D::move_to);
+ ClassDB::bind_method(D_METHOD("move", "rel_vec"), &KinematicBody2D::_move);
ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "floor_normal", "slope_stop_min_velocity", "max_bounces", "floor_max_angle"), &KinematicBody2D::move_and_slide, DEFVAL(Vector2(0, 0)), DEFVAL(5), DEFVAL(4), DEFVAL(Math::deg2rad((float)45)));
ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec"), &KinematicBody2D::test_move);
- ClassDB::bind_method(D_METHOD("get_travel"), &KinematicBody2D::get_travel);
- ClassDB::bind_method(D_METHOD("revert_motion"), &KinematicBody2D::revert_motion);
- ClassDB::bind_method(D_METHOD("is_colliding"), &KinematicBody2D::is_colliding);
+ ClassDB::bind_method(D_METHOD("is_on_floor"), &KinematicBody2D::is_on_floor);
+ ClassDB::bind_method(D_METHOD("is_on_ceiling"), &KinematicBody2D::is_on_ceiling);
+ ClassDB::bind_method(D_METHOD("is_on_wall"), &KinematicBody2D::is_on_wall);
+ ClassDB::bind_method(D_METHOD("get_floor_velocity"), &KinematicBody2D::get_floor_velocity);
- ClassDB::bind_method(D_METHOD("get_collision_pos"), &KinematicBody2D::get_collision_pos);
- ClassDB::bind_method(D_METHOD("get_collision_normal"), &KinematicBody2D::get_collision_normal);
- ClassDB::bind_method(D_METHOD("get_collider_velocity"), &KinematicBody2D::get_collider_velocity);
- ClassDB::bind_method(D_METHOD("get_collider:Variant"), &KinematicBody2D::_get_collider);
- ClassDB::bind_method(D_METHOD("get_collider_shape"), &KinematicBody2D::get_collider_shape);
- ClassDB::bind_method(D_METHOD("get_collider_metadata:Variant"), &KinematicBody2D::get_collider_metadata);
- ClassDB::bind_method(D_METHOD("get_move_and_slide_colliders"), &KinematicBody2D::get_move_and_slide_colliders);
- ClassDB::bind_method(D_METHOD("is_move_and_slide_on_floor"), &KinematicBody2D::is_move_and_slide_on_floor);
- ClassDB::bind_method(D_METHOD("is_move_and_slide_on_ceiling"), &KinematicBody2D::is_move_and_slide_on_ceiling);
- ClassDB::bind_method(D_METHOD("is_move_and_slide_on_wall"), &KinematicBody2D::is_move_and_slide_on_wall);
+ ClassDB::bind_method(D_METHOD("set_safe_margin", "pixels"), &KinematicBody2D::set_safe_margin);
+ ClassDB::bind_method(D_METHOD("get_safe_margin", "pixels"), &KinematicBody2D::get_safe_margin);
- ClassDB::bind_method(D_METHOD("set_collision_margin", "pixels"), &KinematicBody2D::set_collision_margin);
- ClassDB::bind_method(D_METHOD("get_collision_margin", "pixels"), &KinematicBody2D::get_collision_margin);
+ ClassDB::bind_method(D_METHOD("get_collision_count"), &KinematicBody2D::get_collision_count);
+ ClassDB::bind_method(D_METHOD("get_collision_position", "collision"), &KinematicBody2D::get_collision_position);
+ ClassDB::bind_method(D_METHOD("get_collision_normal", "collision"), &KinematicBody2D::get_collision_normal);
+ ClassDB::bind_method(D_METHOD("get_collision_travel", "collision"), &KinematicBody2D::get_collision_travel);
+ ClassDB::bind_method(D_METHOD("get_collision_remainder", "collision"), &KinematicBody2D::get_collision_remainder);
+ ClassDB::bind_method(D_METHOD("get_collision_local_shape", "collision"), &KinematicBody2D::get_collision_local_shape);
+ ClassDB::bind_method(D_METHOD("get_collision_collider", "collision"), &KinematicBody2D::get_collision_collider);
+ ClassDB::bind_method(D_METHOD("get_collision_collider_id", "collision"), &KinematicBody2D::get_collision_collider_id);
+ ClassDB::bind_method(D_METHOD("get_collision_collider_shape", "collision"), &KinematicBody2D::get_collision_collider_shape);
+ ClassDB::bind_method(D_METHOD("get_collision_collider_shape_index", "collision"), &KinematicBody2D::get_collision_collider_shape_index);
+ ClassDB::bind_method(D_METHOD("get_collision_collider_velocity", "collision"), &KinematicBody2D::get_collision_collider_velocity);
+ ClassDB::bind_method(D_METHOD("get_collision_collider_metadata", "collision"), &KinematicBody2D::get_collision_collider_metadata);
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "collision/margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_collision_margin", "get_collision_margin");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin");
}
KinematicBody2D::KinematicBody2D()
: PhysicsBody2D(Physics2DServer::BODY_MODE_KINEMATIC) {
- colliding = false;
- collider = 0;
-
- collider_shape = 0;
-
margin = 0.08;
- move_and_slide_on_floor = false;
- move_and_slide_on_ceiling = false;
- move_and_slide_on_wall = false;
+ on_floor = false;
+ on_ceiling = false;
+ on_wall = false;
}
KinematicBody2D::~KinematicBody2D() {
}
diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h
index 50c9865f18..8c8e4ebc77 100644
--- a/scene/2d/physics_body_2d.h
+++ b/scene/2d/physics_body_2d.h
@@ -40,8 +40,6 @@ class PhysicsBody2D : public CollisionObject2D {
uint32_t collision_layer;
uint32_t collision_mask;
- Vector2 one_way_collision_direction;
- float one_way_collision_max_depth;
void _set_layers(uint32_t p_mask);
uint32_t _get_layers() const;
@@ -68,12 +66,6 @@ public:
void add_collision_exception_with(Node *p_node); //must be physicsbody
void remove_collision_exception_with(Node *p_node);
- void set_one_way_collision_direction(const Vector2 &p_dir);
- Vector2 get_one_way_collision_direction() const;
-
- void set_one_way_collision_max_depth(float p_dir);
- float get_one_way_collision_max_depth() const;
-
PhysicsBody2D();
};
@@ -272,54 +264,60 @@ class KinematicBody2D : public PhysicsBody2D {
GDCLASS(KinematicBody2D, PhysicsBody2D);
+public:
+ struct Collision {
+ Vector2 collision;
+ Vector2 normal;
+ Vector2 collider_vel;
+ ObjectID collider;
+ int collider_shape;
+ Variant collider_metadata;
+ Vector2 remainder;
+ Vector2 travel;
+ int local_shape;
+ };
+
+private:
float margin;
- bool colliding;
- Vector2 collision;
- Vector2 normal;
- Vector2 collider_vel;
- ObjectID collider;
- int collider_shape;
- Variant collider_metadata;
- Vector2 travel;
-
- Vector2 move_and_slide_floor_velocity;
- bool move_and_slide_on_floor;
- bool move_and_slide_on_ceiling;
- bool move_and_slide_on_wall;
- Array move_and_slide_colliders;
-
- Variant _get_collider() const;
+
+ Vector2 floor_velocity;
+ bool on_floor;
+ bool on_ceiling;
+ bool on_wall;
+ Vector<Collision> colliders;
_FORCE_INLINE_ bool _ignores_mode(Physics2DServer::BodyMode) const;
+ Dictionary _move(const Vector2 &p_motion);
+
protected:
static void _bind_methods();
public:
- Vector2 move(const Vector2 &p_motion);
- Vector2 move_to(const Vector2 &p_position);
-
+ bool move(const Vector2 &p_motion, Collision &r_collision);
bool test_move(const Transform2D &p_from, const Vector2 &p_motion);
- bool is_colliding() const;
-
- Vector2 get_travel() const;
- void revert_motion();
-
- Vector2 get_collision_pos() const;
- Vector2 get_collision_normal() const;
- Vector2 get_collider_velocity() const;
- ObjectID get_collider() const;
- int get_collider_shape() const;
- Variant get_collider_metadata() const;
- void set_collision_margin(float p_margin);
- float get_collision_margin() const;
+ void set_safe_margin(float p_margin);
+ float get_safe_margin() const;
Vector2 move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction = Vector2(0, 0), float p_slope_stop_min_velocity = 5, int p_max_bounces = 4, float p_floor_max_angle = Math::deg2rad((float)45));
- bool is_move_and_slide_on_floor() const;
- bool is_move_and_slide_on_wall() const;
- bool is_move_and_slide_on_ceiling() const;
- Array get_move_and_slide_colliders() const;
+ bool is_on_floor() const;
+ bool is_on_wall() const;
+ bool is_on_ceiling() const;
+ Vector2 get_floor_velocity() const;
+
+ int get_collision_count() const;
+ Vector2 get_collision_position(int p_collision) const;
+ Vector2 get_collision_normal(int p_collision) const;
+ Vector2 get_collision_travel(int p_collision) const;
+ Vector2 get_collision_remainder(int p_collision) const;
+ Object *get_collision_local_shape(int p_collision) const;
+ Object *get_collision_collider(int p_collision) const;
+ ObjectID get_collision_collider_id(int p_collision) const;
+ Object *get_collision_collider_shape(int p_collision) const;
+ int get_collision_collider_shape_index(int p_collision) const;
+ Vector2 get_collision_collider_velocity(int p_collision) const;
+ Variant get_collision_collider_metadata(int p_collision) const;
KinematicBody2D();
~KinematicBody2D();
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 71079ea780..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++) {
@@ -404,6 +404,7 @@ void ParticlesMaterial::init_shaders() {
shader_names->emission_texture_point_count = "emission_texture_point_count";
shader_names->emission_texture_points = "emission_texture_points";
shader_names->emission_texture_normal = "emission_texture_normal";
+ shader_names->emission_texture_color = "emission_texture_color";
shader_names->trail_divisor = "trail_divisor";
shader_names->trail_size_modifier = "trail_size_modifier";
@@ -481,6 +482,28 @@ void ParticlesMaterial::_update_shader() {
code += "uniform float anim_speed_random;\n";
code += "uniform float anim_offset_random;\n";
+ switch (emission_shape) {
+ case EMISSION_SHAPE_POINT: {
+ //do none
+ } break;
+ case EMISSION_SHAPE_SPHERE: {
+ code += "uniform float emission_sphere_radius;\n";
+ } break;
+ case EMISSION_SHAPE_BOX: {
+ code += "uniform vec3 emission_box_extents;\n";
+ } break;
+ case EMISSION_SHAPE_DIRECTED_POINTS: {
+ code += "uniform sampler2D emission_texture_normal : hint_black;\n";
+ } //fallthrough
+ case EMISSION_SHAPE_POINTS: {
+ code += "uniform sampler2D emission_texture_points : hint_black;\n";
+ code += "uniform int emission_texture_point_count;\n";
+ if (emission_color_texture.is_valid()) {
+ code += "uniform sampler2D emission_texture_color : hint_white;\n";
+ }
+ } break;
+ }
+
code += "uniform vec4 color_value : hint_color;\n";
code += "uniform int trail_divisor;\n";
@@ -515,25 +538,6 @@ void ParticlesMaterial::_update_shader() {
if (tex_parameters[PARAM_ANIM_OFFSET].is_valid())
code += "uniform sampler2D anim_offset_texture;\n";
- switch (emission_shape) {
- case EMISSION_SHAPE_POINT: {
- //do none
- } break;
- case EMISSION_SHAPE_SPHERE: {
- code += "uniform float emission_sphere_radius;\n";
- } break;
- case EMISSION_SHAPE_BOX: {
- code += "uniform vec3 emission_box_extents;\n";
- } break;
- case EMISSION_SHAPE_DIRECTED_POINTS: {
- code += "uniform sampler2D emission_texture_normal : hint_black;\n";
- } //fallthrough
- case EMISSION_SHAPE_POINTS: {
- code += "uniform sampler2D emission_texture_points : hint_black;\n";
- code += "uniform int emission_texture_point_count;\n";
- } break;
- }
-
if (trail_size_modifier.is_valid()) {
code += "uniform sampler2D trail_size_modifier;\n";
}
@@ -567,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";
@@ -576,6 +580,11 @@ void ParticlesMaterial::_update_shader() {
code += "\n";
code += "\n";
code += "\n";
+ if (emission_shape >= EMISSION_SHAPE_POINTS) {
+ code += " int point = min(emission_texture_point_count-1,int(rand_from_seed(alt_seed) * float(emission_texture_point_count)));\n";
+ code += " ivec2 emission_tex_size = textureSize( emission_texture_points, 0 );\n";
+ code += " ivec2 emission_tex_ofs = ivec2( point % emission_tex_size.x, point / emission_tex_size.x );\n";
+ }
code += " if (RESTART) {\n";
if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid())
@@ -593,11 +602,21 @@ void ParticlesMaterial::_update_shader() {
else
code += " float tex_anim_offset = 0.0;\n";
- code += " float angle1 = rand_from_seed(alt_seed)*spread*3.1416;\n";
- code += " float angle2 = rand_from_seed(alt_seed)*20.0*3.1416; // make it more random like\n";
- code += " vec3 rot_xz=vec3( sin(angle1), 0.0, cos(angle1) );\n";
- code += " vec3 rot = vec3( cos(angle2)*rot_xz.x,sin(angle2)*rot_xz.x, rot_xz.z);\n";
- code += " VELOCITY=(rot*initial_linear_velocity+rot*initial_linear_velocity_random*rand_from_seed(alt_seed));\n";
+ if (flags[FLAG_DISABLE_Z]) {
+
+ code += " float angle1 = rand_from_seed(alt_seed)*spread*3.1416;\n";
+ code += " vec3 rot=vec3( cos(angle1), sin(angle1),0.0 );\n";
+ code += " VELOCITY=(rot*initial_linear_velocity+rot*initial_linear_velocity_random*rand_from_seed(alt_seed));\n";
+
+ } else {
+ //initiate velocity spread in 3D
+ code += " float angle1 = rand_from_seed(alt_seed)*spread*3.1416;\n";
+ code += " float angle2 = rand_from_seed(alt_seed)*20.0*3.1416; // make it more random like\n";
+ code += " vec3 rot_xz=vec3( sin(angle1), 0.0, cos(angle1) );\n";
+ code += " vec3 rot = vec3( cos(angle2)*rot_xz.x,sin(angle2)*rot_xz.x, rot_xz.z);\n";
+ code += " VELOCITY=(rot*initial_linear_velocity+rot*initial_linear_velocity_random*rand_from_seed(alt_seed));\n";
+ }
+
code += " float base_angle=(initial_angle+tex_angle)*mix(1.0,angle_rand,initial_angle_random);\n";
code += " CUSTOM.x=base_angle*3.1416/180.0;\n"; //angle
code += " CUSTOM.y=0.0;\n"; //phase
@@ -614,21 +633,31 @@ void ParticlesMaterial::_update_shader() {
} break;
case EMISSION_SHAPE_POINTS:
case EMISSION_SHAPE_DIRECTED_POINTS: {
- code += " int point = min(emission_texture_point_count-1,int(rand_from_seed(alt_seed) * float(emission_texture_point_count)));\n";
- code += " ivec2 tex_size = textureSize( emission_texture_points, 0 );\n";
- code += " ivec2 tex_ofs = ivec2( point % tex_size.x, point / tex_size.x );\n";
- code += " TRANSFORM[3].xyz = texelFetch(emission_texture_points, tex_ofs,0).xyz;\n";
+ code += " TRANSFORM[3].xyz = texelFetch(emission_texture_points, emission_tex_ofs,0).xyz;\n";
+
if (emission_shape == EMISSION_SHAPE_DIRECTED_POINTS) {
- code += " vec3 normal = texelFetch(emission_texture_normal, tex_ofs,0).xyz;\n";
- code += " vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0, 1.0, 0.0);\n";
- code += " vec3 tangent = normalize(cross(v0, normal));\n";
- code += " vec3 bitangent = normalize(cross(tangent, normal));\n";
- code += " VELOCITY = mat3(tangent,bitangent,normal) * VELOCITY;\n";
+ if (flags[FLAG_DISABLE_Z]) {
+
+ code += " mat2 rotm;";
+ code += " rotm[0]=texelFetch(emission_texture_normal, emission_tex_ofs,0).xy;\n";
+ code += " rotm[1]=rotm[0].yx * vec2(1.0,-1.0);\n";
+ code += " VELOCITY.xy = rotm * VELOCITY.xy;\n";
+ } else {
+ code += " vec3 normal = texelFetch(emission_texture_normal, emission_tex_ofs,0).xyz;\n";
+ code += " vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0, 1.0, 0.0);\n";
+ code += " vec3 tangent = normalize(cross(v0, normal));\n";
+ code += " vec3 bitangent = normalize(cross(tangent, normal));\n";
+ code += " VELOCITY = mat3(tangent,bitangent,normal) * VELOCITY;\n";
+ }
}
} break;
}
code += " VELOCITY = (EMISSION_TRANSFORM * vec4(VELOCITY,0.0)).xyz;\n";
code += " TRANSFORM = EMISSION_TRANSFORM * TRANSFORM;\n";
+ if (flags[FLAG_DISABLE_Z]) {
+ code += " VELOCITY.z=0.0;\n";
+ code += " TRANSFORM[3].z=0.0;\n";
+ }
code += " } else {\n";
@@ -685,6 +714,9 @@ void ParticlesMaterial::_update_shader() {
code += " vec3 force = gravity; \n";
code += " vec3 pos = TRANSFORM[3].xyz; \n";
+ if (flags[FLAG_DISABLE_Z]) {
+ code += " pos.z=0.0; \n";
+ }
code += " //apply linear acceleration\n";
code += " force+=normalize(VELOCITY) * (linear_accel+tex_linear_accel)*mix(1.0,rand_from_seed(alt_seed),linear_accel_random);\n";
code += " //apply radial acceleration\n";
@@ -693,11 +725,17 @@ void ParticlesMaterial::_update_shader() {
code += " //org=p_transform.origin;\n";
code += " force+=normalize(pos-org) * (radial_accel+tex_radial_accel)*mix(1.0,rand_from_seed(alt_seed),radial_accel_random);\n";
code += " //apply tangential acceleration;\n";
- code += " force+=normalize(cross(normalize(pos-org),normalize(gravity))) * ((tangent_accel+tex_tangent_accel)*mix(1.0,rand_from_seed(alt_seed),radial_accel_random));\n";
+ if (flags[FLAG_DISABLE_Z]) {
+ code += " force+=vec3(normalize((pos-org).yx * vec2(-1.0,1.0)),0.0) * ((tangent_accel+tex_tangent_accel)*mix(1.0,rand_from_seed(alt_seed),radial_accel_random));\n";
+
+ } else {
+ code += " force+=normalize(cross(normalize(pos-org),normalize(gravity))) * ((tangent_accel+tex_tangent_accel)*mix(1.0,rand_from_seed(alt_seed),radial_accel_random));\n";
+ }
code += " //apply attractor forces\n";
code += " VELOCITY+=force * DELTA;\n";
- if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid())
+ if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
code += " VELOCITY=normalize(VELOCITY)*tex_linear_velocity;\n";
+ }
code += " if (damping+tex_damping>0.0) {\n";
code += " \n";
code += " float v = length(VELOCITY);\n";
@@ -709,9 +747,16 @@ void ParticlesMaterial::_update_shader() {
code += " VELOCITY=normalize(VELOCITY) * v;\n";
code += " }\n";
code += " }\n";
- code += " float base_angle=(initial_angle+tex_angle)*mix(1.0,angle_rand,initial_angle_random)*3.1416/180.0;\n";
- code += " CUSTOM.x=((base_angle+tex_angle)+CUSTOM.y*LIFETIME*(angular_velocity+tex_angular_velocity)*mix(1.0,rand_from_seed(alt_seed)*2.0-1.0,angular_velocity_random))*3.1416/180.0;\n"; //angle
- code += " CUSTOM.z=(anim_offset+tex_anim_offset)*mix(1.0,anim_offset_rand,anim_offset_random)+CUSTOM.y*LIFETIME*(anim_speed+tex_anim_speed)*mix(1.0,rand_from_seed(alt_seed),anim_speed_random);\n"; //angle
+ code += " float base_angle=(initial_angle+tex_angle)*mix(1.0,angle_rand,initial_angle_random);\n";
+ code += " base_angle+=CUSTOM.y*LIFETIME*(angular_velocity+tex_angular_velocity)*mix(1.0,rand_from_seed(alt_seed)*2.0-1.0,angular_velocity_random);\n";
+ code += " CUSTOM.x=base_angle*3.1416/180.0;\n"; //angle
+ code += " CUSTOM.z=(anim_offset+tex_anim_offset)*mix(1.0,anim_offset_rand,anim_offset_random)+CUSTOM.y*(anim_speed+tex_anim_speed)*mix(1.0,rand_from_seed(alt_seed),anim_speed_random);\n"; //angle
+ if (flags[FLAG_ANIM_LOOP]) {
+ code += " CUSTOM.z=mod(CUSTOM.z,1.0);\n"; //loop
+
+ } else {
+ code += " CUSTOM.z=clamp(CUSTOM.z,0.0,1.0);\n"; //0 to 1 only
+ }
code += " }\n";
//apply color
//apply hue rotation
@@ -747,28 +792,40 @@ void ParticlesMaterial::_update_shader() {
} else {
code += " COLOR = color_value * hue_rot_mat;\n";
}
+ if (emission_color_texture.is_valid() && emission_shape >= EMISSION_SHAPE_POINTS) {
+ code += " COLOR*= texelFetch(emission_texture_color,emission_tex_ofs,0);\n";
+ }
if (trail_color_modifier.is_valid()) {
code += "if (trail_divisor>1) { COLOR*=textureLod(trail_color_modifier,vec2(float(int(NUMBER)%trail_divisor)/float(trail_divisor-1),0.0),0.0); }\n";
}
code += "\n";
- //orient particle Y towards velocity
- if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) {
- code += " if (length(VELOCITY)>0.0) {TRANSFORM[1].xyz=normalize(VELOCITY);} else {TRANSFORM[1].xyz=normalize(TRANSFORM[1].xyz);}\n";
- code += " if (TRANSFORM[1].xyz==normalize(TRANSFORM[0].xyz)) {\n";
- code += "\tTRANSFORM[0].xyz=normalize(cross(normalize(TRANSFORM[1].xyz),normalize(TRANSFORM[2].xyz)));\n";
- code += "\tTRANSFORM[2].xyz=normalize(cross(normalize(TRANSFORM[0].xyz),normalize(TRANSFORM[1].xyz)));\n";
- code += " } else {\n";
- code += "\tTRANSFORM[2].xyz=normalize(cross(normalize(TRANSFORM[0].xyz),normalize(TRANSFORM[1].xyz)));\n";
- code += "\tTRANSFORM[0].xyz=normalize(cross(normalize(TRANSFORM[1].xyz),normalize(TRANSFORM[2].xyz)));\n";
- code += " }\n";
+
+ if (flags[FLAG_DISABLE_Z]) {
+
+ code += " TRANSFORM[0]=vec4(cos(CUSTOM.x),-sin(CUSTOM.x),0.0,0.0);\n";
+ code += " TRANSFORM[1]=vec4(sin(CUSTOM.x),cos(CUSTOM.x),0.0,0.0);\n";
+ code += " TRANSFORM[2]=vec4(0.0,0.0,1.0,0.0);\n";
+
} else {
- code += "\tTRANSFORM[0].xyz=normalize(TRANSFORM[0].xyz);\n";
- code += "\tTRANSFORM[1].xyz=normalize(TRANSFORM[1].xyz);\n";
- code += "\tTRANSFORM[2].xyz=normalize(TRANSFORM[2].xyz);\n";
- }
- //turn particle by rotation in Y
- if (flags[FLAG_ROTATE_Y]) {
- code += "\tTRANSFORM = TRANSFORM * mat4( vec4(cos(CUSTOM.x),0.0,-sin(CUSTOM.x),0.0), vec4(0.0,1.0,0.0,0.0),vec4(sin(CUSTOM.x),0.0,cos(CUSTOM.x),0.0),vec4(0.0,0.0,0.0,1.0));\n";
+ //orient particle Y towards velocity
+ if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) {
+ code += " if (length(VELOCITY)>0.0) {TRANSFORM[1].xyz=normalize(VELOCITY);} else {TRANSFORM[1].xyz=normalize(TRANSFORM[1].xyz);}\n";
+ code += " if (TRANSFORM[1].xyz==normalize(TRANSFORM[0].xyz)) {\n";
+ code += "\tTRANSFORM[0].xyz=normalize(cross(normalize(TRANSFORM[1].xyz),normalize(TRANSFORM[2].xyz)));\n";
+ code += "\tTRANSFORM[2].xyz=normalize(cross(normalize(TRANSFORM[0].xyz),normalize(TRANSFORM[1].xyz)));\n";
+ code += " } else {\n";
+ code += "\tTRANSFORM[2].xyz=normalize(cross(normalize(TRANSFORM[0].xyz),normalize(TRANSFORM[1].xyz)));\n";
+ code += "\tTRANSFORM[0].xyz=normalize(cross(normalize(TRANSFORM[1].xyz),normalize(TRANSFORM[2].xyz)));\n";
+ code += " }\n";
+ } else {
+ code += "\tTRANSFORM[0].xyz=normalize(TRANSFORM[0].xyz);\n";
+ code += "\tTRANSFORM[1].xyz=normalize(TRANSFORM[1].xyz);\n";
+ code += "\tTRANSFORM[2].xyz=normalize(TRANSFORM[2].xyz);\n";
+ }
+ //turn particle by rotation in Y
+ if (flags[FLAG_ROTATE_Y]) {
+ code += "\tTRANSFORM = TRANSFORM * mat4( vec4(cos(CUSTOM.x),0.0,-sin(CUSTOM.x),0.0), vec4(0.0,1.0,0.0,0.0),vec4(sin(CUSTOM.x),0.0,cos(CUSTOM.x),0.0),vec4(0.0,0.0,0.0,1.0));\n";
+ }
}
//scale by scale
code += " float base_scale=mix(scale*tex_scale,1.0,scale_random*scale_rand);\n";
@@ -779,6 +836,10 @@ void ParticlesMaterial::_update_shader() {
code += " TRANSFORM[0].xyz*=base_scale;\n";
code += " TRANSFORM[1].xyz*=base_scale;\n";
code += " TRANSFORM[2].xyz*=base_scale;\n";
+ if (flags[FLAG_DISABLE_Z]) {
+ code += " VELOCITY.z=0.0;\n";
+ code += " TRANSFORM[3].z=0.0;\n";
+ }
code += "}\n";
code += "\n";
@@ -1021,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;
@@ -1130,6 +1184,16 @@ void ParticlesMaterial::set_emission_normal_texture(const Ref<Texture> &p_normal
VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_normal, texture);
}
+void ParticlesMaterial::set_emission_color_texture(const Ref<Texture> &p_colors) {
+
+ emission_color_texture = p_colors;
+ RID texture;
+ if (p_colors.is_valid())
+ texture = p_colors->get_rid();
+ VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_color, texture);
+ _queue_shader_change();
+}
+
void ParticlesMaterial::set_emission_point_count(int p_count) {
emission_point_count = p_count;
@@ -1158,6 +1222,11 @@ Ref<Texture> ParticlesMaterial::get_emission_normal_texture() const {
return emission_normal_texture;
}
+Ref<Texture> ParticlesMaterial::get_emission_color_texture() const {
+
+ return emission_color_texture;
+}
+
int ParticlesMaterial::get_emission_point_count() const {
return emission_point_count;
@@ -1181,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;
@@ -1247,7 +1309,7 @@ void ParticlesMaterial::_validate_property(PropertyInfo &property) const {
property.usage = 0;
}
- if (property.name == "emission_point_texture" && (emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS)) {
+ if ((property.name == "emission_point_texture" || property.name == "emission_color_texture") && (emission_shape < EMISSION_SHAPE_POINTS)) {
property.usage = 0;
}
@@ -1301,6 +1363,9 @@ void ParticlesMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_emission_normal_texture", "texture:Texture"), &ParticlesMaterial::set_emission_normal_texture);
ClassDB::bind_method(D_METHOD("get_emission_normal_texture:Texture"), &ParticlesMaterial::get_emission_normal_texture);
+ ClassDB::bind_method(D_METHOD("set_emission_color_texture", "texture:Texture"), &ParticlesMaterial::set_emission_color_texture);
+ ClassDB::bind_method(D_METHOD("get_emission_color_texture:Texture"), &ParticlesMaterial::get_emission_color_texture);
+
ClassDB::bind_method(D_METHOD("set_emission_point_count", "point_count"), &ParticlesMaterial::set_emission_point_count);
ClassDB::bind_method(D_METHOD("get_emission_point_count"), &ParticlesMaterial::get_emission_point_count);
@@ -1326,10 +1391,12 @@ void ParticlesMaterial::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_box_extents"), "set_emission_box_extents", "get_emission_box_extents");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_point_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_emission_point_texture", "get_emission_point_texture");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_normal_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_emission_normal_texture", "get_emission_normal_texture");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_color_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_emission_color_texture", "get_emission_color_texture");
ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_point_count", PROPERTY_HINT_RANGE, "0,1000000,1"), "set_emission_point_count", "get_emission_point_count");
ADD_GROUP("Flags", "flag_");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_align_y"), "set_flag", "get_flag", FLAG_ALIGN_Y_TO_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_rotate_y"), "set_flag", "get_flag", FLAG_ROTATE_Y);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_disable_z"), "set_flag", "get_flag", FLAG_DISABLE_Z);
ADD_GROUP("Spread", "");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "spread", PROPERTY_HINT_RANGE, "0,180,0.01"), "set_spread", "get_spread");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "flatness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_flatness", "get_flatness");
@@ -1379,12 +1446,13 @@ void ParticlesMaterial::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "hue_variation_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_HUE_VARIATION);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "hue_variation_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_HUE_VARIATION);
ADD_GROUP("Animation", "anim_");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_speed", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_ANIM_SPEED);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_speed", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_param", "get_param", PARAM_ANIM_SPEED);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_speed_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_SPEED);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_speed_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_SPEED);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_ANIM_OFFSET);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_OFFSET);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_offset_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_OFFSET);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "anim_loop"), "set_flag", "get_flag", FLAG_ANIM_LOOP);
BIND_CONSTANT(PARAM_INITIAL_LINEAR_VELOCITY);
BIND_CONSTANT(PARAM_ANGULAR_VELOCITY);
diff --git a/scene/3d/particles.h b/scene/3d/particles.h
index 63ebd7ed7b..dad8ed4585 100644
--- a/scene/3d/particles.h
+++ b/scene/3d/particles.h
@@ -154,6 +154,8 @@ public:
enum Flags {
FLAG_ALIGN_Y_TO_VELOCITY,
FLAG_ROTATE_Y,
+ FLAG_DISABLE_Z,
+ FLAG_ANIM_LOOP,
FLAG_MAX
};
@@ -171,11 +173,12 @@ private:
struct {
uint32_t texture_mask : 16;
uint32_t texture_color : 1;
- uint32_t flags : 2;
+ uint32_t flags : 4;
uint32_t emission_shape : 2;
uint32_t trail_size_texture : 1;
uint32_t trail_color_texture : 1;
uint32_t invalid_key : 1;
+ uint32_t has_emission_color : 1;
};
uint32_t key;
@@ -213,6 +216,7 @@ private:
mk.emission_shape = emission_shape;
mk.trail_color_texture = trail_color_modifier.is_valid() ? 1 : 0;
mk.trail_size_texture = trail_size_modifier.is_valid() ? 1 : 0;
+ mk.has_emission_color = emission_shape >= EMISSION_SHAPE_POINTS && emission_color_texture.is_valid();
return mk;
}
@@ -269,6 +273,7 @@ private:
StringName emission_texture_point_count;
StringName emission_texture_points;
StringName emission_texture_normal;
+ StringName emission_texture_color;
StringName trail_divisor;
StringName trail_size_modifier;
@@ -302,8 +307,11 @@ private:
Vector3 emission_box_extents;
Ref<Texture> emission_point_texture;
Ref<Texture> emission_normal_texture;
+ Ref<Texture> emission_color_texture;
int emission_point_count;
+ bool anim_loop;
+
int trail_divisor;
Ref<CurveTexture> trail_size_modifier;
@@ -347,6 +355,7 @@ public:
void set_emission_box_extents(Vector3 p_extents);
void set_emission_point_texture(const Ref<Texture> &p_points);
void set_emission_normal_texture(const Ref<Texture> &p_normals);
+ void set_emission_color_texture(const Ref<Texture> &p_colors);
void set_emission_point_count(int p_count);
EmissionShape get_emission_shape() const;
@@ -354,6 +363,7 @@ public:
Vector3 get_emission_box_extents() const;
Ref<Texture> get_emission_point_texture() const;
Ref<Texture> get_emission_normal_texture() const;
+ Ref<Texture> get_emission_color_texture() const;
int get_emission_point_count() const;
void set_trail_divisor(int p_divisor);
diff --git a/scene/3d/path.cpp b/scene/3d/path.cpp
index d535c545a8..5c29918118 100644
--- a/scene/3d/path.cpp
+++ b/scene/3d/path.cpp
@@ -108,40 +108,58 @@ void PathFollow::_update_transform() {
Vector3 pos = c->interpolate_baked(o, cubic);
Transform t = get_transform();
- if (rotation_mode != ROTATION_NONE) {
-
- Vector3 n = (c->interpolate_baked(o + lookahead, cubic) - pos).normalized();
-
- if (rotation_mode == ROTATION_Y) {
+ t.origin = pos;
+ Vector3 pos_offset = Vector3(h_offset, v_offset, 0);
- n.y = 0;
- n.normalize();
- }
+ if (rotation_mode != ROTATION_NONE) {
+ // perform parallel transport
+ //
+ // see C. Dougan, The Parallel Transport Frame, Game Programming Gems 2 for example
+ // for a discussion about why not Frenet frame.
+
+ Vector3 t_prev = pos - c->interpolate_baked(o - lookahead, cubic);
+ Vector3 t_cur = c->interpolate_baked(o + lookahead, cubic) - pos;
+
+ Vector3 axis = t_prev.cross(t_cur);
+ float dot = t_prev.normalized().dot(t_cur.normalized());
+ float angle = Math::acos(CLAMP(dot, -1, 1));
+
+ if (axis.length() > CMP_EPSILON && angle > CMP_EPSILON) {
+ if (rotation_mode == ROTATION_Y) {
+ // assuming we're referring to global Y-axis. is this correct?
+ axis.x = 0;
+ axis.z = 0;
+ } else if (rotation_mode == ROTATION_XY) {
+ axis.z = 0;
+ } else if (rotation_mode == ROTATION_XYZ) {
+ // all components are OK
+ }
- if (n.length() < CMP_EPSILON) { //nothing, use previous
- n = -t.get_basis().get_axis(2).normalized();
+ t.rotate_basis(axis.normalized(), angle);
}
- Vector3 up = Vector3(0, 1, 0);
-
- if (rotation_mode == ROTATION_XYZ) {
-
- float tilt = c->interpolate_baked_tilt(o);
- if (tilt != 0) {
-
- Basis rot(-n, tilt); //remember.. lookat will be znegative.. znegative!! we abide by opengl clan.
- up = rot.xform(up);
+ // do the additional tilting
+ float tilt_angle = c->interpolate_baked_tilt(o);
+ Vector3 tilt_axis = t_cur; // is this correct??
+
+ if (tilt_axis.length() > CMP_EPSILON && tilt_angle > CMP_EPSILON) {
+ if (rotation_mode == ROTATION_Y) {
+ tilt_axis.x = 0;
+ tilt_axis.z = 0;
+ } else if (rotation_mode == ROTATION_XY) {
+ tilt_axis.z = 0;
+ } else if (rotation_mode == ROTATION_XYZ) {
+ // all components are OK
}
- }
- t.set_look_at(pos, pos + n, up);
+ t.rotate_basis(tilt_axis.normalized(), tilt_angle);
+ }
+ t.translate(pos_offset);
} else {
-
- t.origin = pos;
+ t.origin += pos_offset;
}
- t.origin += t.basis.get_axis(0) * h_offset + t.basis.get_axis(1) * v_offset;
set_transform(t);
}
diff --git a/scene/3d/scenario_fx.cpp b/scene/3d/scenario_fx.cpp
index 874c21546d..abc7766ecb 100644
--- a/scene/3d/scenario_fx.cpp
+++ b/scene/3d/scenario_fx.cpp
@@ -28,43 +28,44 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "scenario_fx.h"
+#include "scene/main/viewport.h"
void WorldEnvironment::_notification(int p_what) {
- if (p_what == NOTIFICATION_ENTER_WORLD) {
+ if (p_what == Spatial::NOTIFICATION_ENTER_WORLD || p_what == Spatial::NOTIFICATION_ENTER_TREE) {
if (environment.is_valid()) {
- if (get_world()->get_environment().is_valid()) {
+ if (get_viewport()->find_world()->get_environment().is_valid()) {
WARN_PRINT("World already has an environment (Another WorldEnvironment?), overriding.");
}
- get_world()->set_environment(environment);
- add_to_group("_world_environment_" + itos(get_world()->get_scenario().get_id()));
+ get_viewport()->find_world()->set_environment(environment);
+ add_to_group("_world_environment_" + itos(get_viewport()->find_world()->get_scenario().get_id()));
}
- } else if (p_what == NOTIFICATION_EXIT_WORLD) {
+ } else if (p_what == Spatial::NOTIFICATION_EXIT_WORLD || p_what == Spatial::NOTIFICATION_EXIT_TREE) {
- if (environment.is_valid() && get_world()->get_environment() == environment) {
- get_world()->set_environment(Ref<Environment>());
- remove_from_group("_world_environment_" + itos(get_world()->get_scenario().get_id()));
+ if (environment.is_valid() && get_viewport()->find_world()->get_environment() == environment) {
+ get_viewport()->find_world()->set_environment(Ref<Environment>());
+ remove_from_group("_world_environment_" + itos(get_viewport()->find_world()->get_scenario().get_id()));
}
}
}
void WorldEnvironment::set_environment(const Ref<Environment> &p_environment) {
- if (is_inside_world() && environment.is_valid() && get_world()->get_environment() == environment) {
- get_world()->set_environment(Ref<Environment>());
- remove_from_group("_world_environment_" + itos(get_world()->get_scenario().get_id()));
+ if (is_inside_tree() && environment.is_valid() && get_viewport()->find_world()->get_environment() == environment) {
+ get_viewport()->find_world()->set_environment(Ref<Environment>());
+ remove_from_group("_world_environment_" + itos(get_viewport()->find_world()->get_scenario().get_id()));
//clean up
}
environment = p_environment;
- if (is_inside_world() && environment.is_valid()) {
- if (get_world()->get_environment().is_valid()) {
+ if (is_inside_tree() && environment.is_valid()) {
+ if (get_viewport()->find_world()->get_environment().is_valid()) {
WARN_PRINT("World already has an environment (Another WorldEnvironment?), overriding.");
}
- get_world()->set_environment(environment);
- add_to_group("_world_environment_" + itos(get_world()->get_scenario().get_id()));
+ get_viewport()->find_world()->set_environment(environment);
+ add_to_group("_world_environment_" + itos(get_viewport()->find_world()->get_scenario().get_id()));
}
update_configuration_warning();
@@ -77,11 +78,11 @@ Ref<Environment> WorldEnvironment::get_environment() const {
String WorldEnvironment::get_configuration_warning() const {
- if (!is_visible_in_tree() || !is_inside_tree() || !environment.is_valid())
+ if (/*!is_visible_in_tree() ||*/ !is_inside_tree() || !environment.is_valid())
return String();
List<Node *> nodes;
- get_tree()->get_nodes_in_group("_world_environment_" + itos(get_world()->get_scenario().get_id()), &nodes);
+ get_tree()->get_nodes_in_group("_world_environment_" + itos(get_viewport()->find_world()->get_scenario().get_id()), &nodes);
if (nodes.size() > 1) {
return TTR("Only one WorldEnvironment is allowed per scene (or set of instanced scenes).");
diff --git a/scene/3d/scenario_fx.h b/scene/3d/scenario_fx.h
index b2a4bc5472..d1e0a63130 100644
--- a/scene/3d/scenario_fx.h
+++ b/scene/3d/scenario_fx.h
@@ -36,9 +36,9 @@
@author Juan Linietsky <reduzio@gmail.com>
*/
-class WorldEnvironment : public Spatial {
+class WorldEnvironment : public Node {
- GDCLASS(WorldEnvironment, Spatial);
+ GDCLASS(WorldEnvironment, Node);
Ref<Environment> environment;
diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp
index a95bb4a67f..b6c59ba277 100644
--- a/scene/3d/sprite_3d.cpp
+++ b/scene/3d/sprite_3d.cpp
@@ -273,10 +273,12 @@ void SpriteBase3D::_bind_methods() {
ADD_GROUP("Flags", "");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "transparent"), "set_draw_flag", "get_draw_flag", FLAG_TRANSPARENT);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "shaded"), "set_draw_flag", "get_draw_flag", FLAG_SHADED);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "double_sided"), "set_draw_flag", "get_draw_flag", FLAG_DOUBLE_SIDED);
ADD_PROPERTY(PropertyInfo(Variant::INT, "alpha_cut", PROPERTY_HINT_ENUM, "Disabled,Discard,Opaque Pre-Pass"), "set_alpha_cut_mode", "get_alpha_cut_mode");
BIND_CONSTANT(FLAG_TRANSPARENT);
BIND_CONSTANT(FLAG_SHADED);
+ BIND_CONSTANT(FLAG_DOUBLE_SIDED);
BIND_CONSTANT(FLAG_MAX);
BIND_CONSTANT(ALPHA_CUT_DISABLED);
@@ -294,7 +296,7 @@ SpriteBase3D::SpriteBase3D() {
pI = NULL;
for (int i = 0; i < FLAG_MAX; i++)
- flags[i] = i == FLAG_TRANSPARENT;
+ flags[i] = i == FLAG_TRANSPARENT || i == FLAG_DOUBLE_SIDED;
axis = Vector3::AXIS_Z;
pixel_size = 0.01;
@@ -387,7 +389,7 @@ void Sprite3D::_draw() {
int axis = get_axis();
normal[axis] = 1.0;
- RID mat = VS::get_singleton()->material_2d_get(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS);
+ RID mat = VS::get_singleton()->material_2d_get(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS);
VS::get_singleton()->immediate_set_material(immediate, mat);
VS::get_singleton()->immediate_begin(immediate, VS::PRIMITIVE_TRIANGLE_FAN, texture->get_rid());
@@ -888,7 +890,7 @@ void AnimatedSprite3D::_draw() {
int axis = get_axis();
normal[axis] = 1.0;
- RID mat = VS::get_singleton()->material_2d_get(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS);
+ RID mat = VS::get_singleton()->material_2d_get(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS);
VS::get_singleton()->immediate_set_material(immediate, mat);
VS::get_singleton()->immediate_begin(immediate, VS::PRIMITIVE_TRIANGLE_FAN, texture->get_rid());
diff --git a/scene/3d/sprite_3d.h b/scene/3d/sprite_3d.h
index 625b37c32e..b4600c00b8 100644
--- a/scene/3d/sprite_3d.h
+++ b/scene/3d/sprite_3d.h
@@ -41,6 +41,7 @@ public:
enum DrawFlags {
FLAG_TRANSPARENT,
FLAG_SHADED,
+ FLAG_DOUBLE_SIDED,
FLAG_MAX
};
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/gui/patch_9_rect.cpp b/scene/gui/patch_9_rect.cpp
index 0c2b94d700..735f36b55d 100644
--- a/scene/gui/patch_9_rect.cpp
+++ b/scene/gui/patch_9_rect.cpp
@@ -44,7 +44,7 @@ void NinePatchRect::_notification(int p_what) {
texture->get_rect_region(rect, src_rect, rect, src_rect);
RID ci = get_canvas_item();
- VS::get_singleton()->canvas_item_add_nine_patch(ci, rect, src_rect, texture->get_rid(), Vector2(margin[MARGIN_LEFT], margin[MARGIN_TOP]), Vector2(margin[MARGIN_RIGHT], margin[MARGIN_BOTTOM]), VS::NINE_PATCH_STRETCH, VS::NINE_PATCH_STRETCH, draw_center);
+ VS::get_singleton()->canvas_item_add_nine_patch(ci, rect, src_rect, texture->get_rid(), Vector2(margin[MARGIN_LEFT], margin[MARGIN_TOP]), Vector2(margin[MARGIN_RIGHT], margin[MARGIN_BOTTOM]), VS::NinePatchAxisMode(axis_h), VS::NinePatchAxisMode(axis_v), draw_center);
}
}
@@ -62,6 +62,10 @@ void NinePatchRect::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_region_rect"), &NinePatchRect::get_region_rect);
ClassDB::bind_method(D_METHOD("set_draw_center", "draw_center"), &NinePatchRect::set_draw_center);
ClassDB::bind_method(D_METHOD("get_draw_center"), &NinePatchRect::get_draw_center);
+ ClassDB::bind_method(D_METHOD("set_h_axis_stretch_mode", "mode"), &NinePatchRect::set_h_axis_stretch_mode);
+ ClassDB::bind_method(D_METHOD("get_h_axis_stretch_mode"), &NinePatchRect::get_h_axis_stretch_mode);
+ ClassDB::bind_method(D_METHOD("set_v_axis_stretch_mode", "mode"), &NinePatchRect::set_v_axis_stretch_mode);
+ ClassDB::bind_method(D_METHOD("get_v_axis_stretch_mode"), &NinePatchRect::get_v_axis_stretch_mode);
ADD_SIGNAL(MethodInfo("texture_changed"));
@@ -74,6 +78,13 @@ void NinePatchRect::_bind_methods() {
ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "patch_margin_top", PROPERTY_HINT_RANGE, "0,16384,1"), "set_patch_margin", "get_patch_margin", MARGIN_TOP);
ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "patch_margin_right", PROPERTY_HINT_RANGE, "0,16384,1"), "set_patch_margin", "get_patch_margin", MARGIN_RIGHT);
ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "patch_margin_bottom", PROPERTY_HINT_RANGE, "0,16384,1"), "set_patch_margin", "get_patch_margin", MARGIN_BOTTOM);
+ ADD_GROUP("Axis Stretch", "axis_stretch_");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "axis_stretch_horizontal", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_h_axis_stretch_mode", "get_h_axis_stretch_mode");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "axis_stretch_vertical", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_v_axis_stretch_mode", "get_v_axis_stretch_mode");
+
+ BIND_CONSTANT(AXIS_STRETCH_MODE_STRETCH);
+ BIND_CONSTANT(AXIS_STRETCH_MODE_TILE);
+ BIND_CONSTANT(AXIS_STRETCH_MODE_TILE_FIT);
}
void NinePatchRect::set_texture(const Ref<Texture> &p_tex) {
@@ -150,6 +161,26 @@ bool NinePatchRect::get_draw_center() const {
return draw_center;
}
+void NinePatchRect::set_h_axis_stretch_mode(AxisStretchMode p_mode) {
+ axis_h = p_mode;
+ update();
+}
+
+NinePatchRect::AxisStretchMode NinePatchRect::get_h_axis_stretch_mode() const {
+ return axis_h;
+}
+
+void NinePatchRect::set_v_axis_stretch_mode(AxisStretchMode p_mode) {
+
+ axis_v = p_mode;
+ update();
+}
+
+NinePatchRect::AxisStretchMode NinePatchRect::get_v_axis_stretch_mode() const {
+
+ return axis_v;
+}
+
NinePatchRect::NinePatchRect() {
margin[MARGIN_LEFT] = 0;
@@ -159,6 +190,9 @@ NinePatchRect::NinePatchRect() {
set_mouse_filter(MOUSE_FILTER_IGNORE);
draw_center = true;
+
+ axis_h = AXIS_STRETCH_MODE_STRETCH;
+ axis_v = AXIS_STRETCH_MODE_STRETCH;
}
NinePatchRect::~NinePatchRect() {
diff --git a/scene/gui/patch_9_rect.h b/scene/gui/patch_9_rect.h
index ba978f2f81..602a6d22bf 100644
--- a/scene/gui/patch_9_rect.h
+++ b/scene/gui/patch_9_rect.h
@@ -38,11 +38,20 @@ class NinePatchRect : public Control {
GDCLASS(NinePatchRect, Control);
+public:
+ enum AxisStretchMode {
+ AXIS_STRETCH_MODE_STRETCH,
+ AXIS_STRETCH_MODE_TILE,
+ AXIS_STRETCH_MODE_TILE_FIT,
+ };
+
bool draw_center;
int margin[4];
Rect2 region_rect;
Ref<Texture> texture;
+ AxisStretchMode axis_h, axis_v;
+
protected:
void _notification(int p_what);
virtual Size2 get_minimum_size() const;
@@ -61,7 +70,15 @@ public:
void set_draw_center(bool p_enable);
bool get_draw_center() const;
+ void set_h_axis_stretch_mode(AxisStretchMode p_mode);
+ AxisStretchMode get_h_axis_stretch_mode() const;
+
+ void set_v_axis_stretch_mode(AxisStretchMode p_mode);
+ AxisStretchMode get_v_axis_stretch_mode() const;
+
NinePatchRect();
~NinePatchRect();
};
+
+VARIANT_ENUM_CAST(NinePatchRect::AxisStretchMode)
#endif // PATCH_9_FRAME_H
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index c4991700aa..74b26da580 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -884,7 +884,7 @@ void PopupMenu::activate_item(int p_item) {
while (pop) {
// We close all parents that are chained together,
// with hide_on_item_selection enabled
- if (hide_on_item_selection && pop->is_hide_on_item_selection()) {
+ if ((items[p_item].checkable && hide_on_checkable_item_selection && pop->is_hide_on_checkable_item_selection()) || (!items[p_item].checkable && hide_on_item_selection && pop->is_hide_on_item_selection())) {
pop->hide();
next = next->get_parent();
pop = next->cast_to<PopupMenu>();
@@ -895,8 +895,8 @@ void PopupMenu::activate_item(int p_item) {
}
}
// Hides popup by default; unless otherwise specified
- // by using set_hide_on_item_selection
- if (hide_on_item_selection) {
+ // by using set_hide_on_item_selection and set_hide_on_checkable_item_selection
+ if ((items[p_item].checkable && hide_on_checkable_item_selection) || (!items[p_item].checkable && hide_on_item_selection)) {
hide();
}
}
@@ -1019,6 +1019,16 @@ bool PopupMenu::is_hide_on_item_selection() {
return hide_on_item_selection;
}
+void PopupMenu::set_hide_on_checkable_item_selection(bool p_enabled) {
+
+ hide_on_checkable_item_selection = p_enabled;
+}
+
+bool PopupMenu::is_hide_on_checkable_item_selection() {
+
+ return hide_on_checkable_item_selection;
+}
+
String PopupMenu::get_tooltip(const Point2 &p_pos) const {
int over = _get_mouse_over(p_pos);
@@ -1107,10 +1117,14 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_hide_on_item_selection", "enable"), &PopupMenu::set_hide_on_item_selection);
ClassDB::bind_method(D_METHOD("is_hide_on_item_selection"), &PopupMenu::is_hide_on_item_selection);
+ ClassDB::bind_method(D_METHOD("set_hide_on_checkable_item_selection", "enable"), &PopupMenu::set_hide_on_checkable_item_selection);
+ ClassDB::bind_method(D_METHOD("is_hide_on_checkable_item_selection"), &PopupMenu::is_hide_on_checkable_item_selection);
+
ClassDB::bind_method(D_METHOD("_submenu_timeout"), &PopupMenu::_submenu_timeout);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_items", "_get_items");
ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "hide_on_item_selection"), "set_hide_on_item_selection", "is_hide_on_item_selection");
+ ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "hide_on_checkable_item_selection"), "set_hide_on_checkable_item_selection", "is_hide_on_checkable_item_selection");
ADD_SIGNAL(MethodInfo("id_pressed", PropertyInfo(Variant::INT, "ID")));
ADD_SIGNAL(MethodInfo("index_pressed", PropertyInfo(Variant::INT, "index")));
@@ -1128,6 +1142,7 @@ PopupMenu::PopupMenu() {
set_focus_mode(FOCUS_ALL);
set_as_toplevel(true);
set_hide_on_item_selection(true);
+ set_hide_on_checkable_item_selection(true);
submenu_timer = memnew(Timer);
submenu_timer->set_wait_time(0.3);
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index 7ef532453d..a9bd8f7e50 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -85,6 +85,7 @@ class PopupMenu : public Popup {
bool invalidated_click;
bool hide_on_item_selection;
+ bool hide_on_checkable_item_selection;
Vector2 moved;
Array _get_items() const;
@@ -168,6 +169,9 @@ public:
void set_hide_on_item_selection(bool p_enabled);
bool is_hide_on_item_selection();
+ void set_hide_on_checkable_item_selection(bool p_enabled);
+ bool is_hide_on_checkable_item_selection();
+
PopupMenu();
~PopupMenu();
};
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 0b8595de42..78ede6e494 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -1831,7 +1831,7 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_image", "image:Texture"), &RichTextLabel::add_image);
ClassDB::bind_method(D_METHOD("newline"), &RichTextLabel::add_newline);
ClassDB::bind_method(D_METHOD("remove_line"), &RichTextLabel::remove_line);
- ClassDB::bind_method(D_METHOD("push_font", "font"), &RichTextLabel::push_font);
+ ClassDB::bind_method(D_METHOD("push_font", "font:Font"), &RichTextLabel::push_font);
ClassDB::bind_method(D_METHOD("push_color", "color"), &RichTextLabel::push_color);
ClassDB::bind_method(D_METHOD("push_align", "align"), &RichTextLabel::push_align);
ClassDB::bind_method(D_METHOD("push_indent", "level"), &RichTextLabel::push_indent);
@@ -1854,7 +1854,7 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_scroll_follow", "follow"), &RichTextLabel::set_scroll_follow);
ClassDB::bind_method(D_METHOD("is_scroll_following"), &RichTextLabel::is_scroll_following);
- ClassDB::bind_method(D_METHOD("get_v_scroll"), &RichTextLabel::get_v_scroll);
+ ClassDB::bind_method(D_METHOD("get_v_scroll:VScrollBar"), &RichTextLabel::get_v_scroll);
ClassDB::bind_method(D_METHOD("scroll_to_line", "line"), &RichTextLabel::scroll_to_line);
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 714327c5b7..a87c83f17c 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -49,6 +49,7 @@
#include "scene/scene_string_names.h"
#include "global_config.h"
+#include "scene/3d/scenario_fx.h"
void ViewportTexture::setup_local_to_scene() {
@@ -1017,10 +1018,9 @@ void Viewport::_propagate_enter_world(Node *p_node) {
if (!p_node->is_inside_tree()) //may not have entered scene yet
return;
- Spatial *s = p_node->cast_to<Spatial>();
- if (s) {
+ if (p_node->cast_to<Spatial>() || p_node->cast_to<WorldEnvironment>()) {
- s->notification(Spatial::NOTIFICATION_ENTER_WORLD);
+ p_node->notification(Spatial::NOTIFICATION_ENTER_WORLD);
} else {
Viewport *v = p_node->cast_to<Viewport>();
if (v) {
@@ -1055,10 +1055,9 @@ void Viewport::_propagate_exit_world(Node *p_node) {
if (!p_node->is_inside_tree()) //may have exited scene already
return;
- Spatial *s = p_node->cast_to<Spatial>();
- if (s) {
+ if (p_node->cast_to<Spatial>() || p_node->cast_to<WorldEnvironment>()) {
- s->notification(Spatial::NOTIFICATION_EXIT_WORLD, true);
+ p_node->notification(Spatial::NOTIFICATION_EXIT_WORLD);
} else {
Viewport *v = p_node->cast_to<Viewport>();
if (v) {
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index b2737353fb..151bc80321 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -469,9 +469,12 @@ 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>();
+ //ClassDB::register_class<ParticleAttractor2D>();
ClassDB::register_class<Sprite>();
//ClassDB::register_type<ViewportSprite>();
ClassDB::register_class<SpriteFrames>();
@@ -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/environment.cpp b/scene/resources/environment.cpp
index a435ba06cc..24e3977de8 100644
--- a/scene/resources/environment.cpp
+++ b/scene/resources/environment.cpp
@@ -959,7 +959,6 @@ void Environment::_bind_methods() {
ADD_GROUP("SS Reflections", "ss_reflections_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ss_reflections_enabled"), "set_ssr_enabled", "is_ssr_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "ss_reflections_max_steps", PROPERTY_HINT_RANGE, "1,512,1"), "set_ssr_max_steps", "get_ssr_max_steps");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "ss_reflections_accel", PROPERTY_HINT_RANGE, "0,4,0.01"), "set_ssr_accel", "get_ssr_accel");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "ss_reflections_fade_in", PROPERTY_HINT_EXP_EASING), "set_ssr_fade_in", "get_ssr_fade_in");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "ss_reflections_fade_out", PROPERTY_HINT_EXP_EASING), "set_ssr_fade_out", "get_ssr_fade_out");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "ss_reflections_depth_tolerance", PROPERTY_HINT_RANGE, "0.1,128,0.1"), "set_ssr_depth_tolerance", "get_ssr_depth_tolerance");
@@ -1040,7 +1039,7 @@ void Environment::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "dof_blur_far_amount", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_dof_blur_far_amount", "get_dof_blur_far_amount");
ADD_PROPERTY(PropertyInfo(Variant::INT, "dof_blur_far_quality", PROPERTY_HINT_ENUM, "Low,Medium,High"), "set_dof_blur_far_quality", "get_dof_blur_far_quality");
- ADD_GROUP("DOF Far Near", "dof_blur_near_");
+ ADD_GROUP("DOF Near Blur", "dof_blur_near_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dof_blur_near_enabled"), "set_dof_blur_near_enabled", "is_dof_blur_near_enabled");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "dof_blur_near_distance", PROPERTY_HINT_EXP_RANGE, "0.01,8192,0.01"), "set_dof_blur_near_distance", "get_dof_blur_near_distance");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "dof_blur_near_transition", PROPERTY_HINT_EXP_RANGE, "0.01,8192,0.01"), "set_dof_blur_near_transition", "get_dof_blur_near_transition");
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;