summaryrefslogtreecommitdiff
path: root/scene/2d
diff options
context:
space:
mode:
Diffstat (limited to 'scene/2d')
-rw-r--r--scene/2d/canvas_item.cpp315
-rw-r--r--scene/2d/canvas_item.h65
-rw-r--r--scene/2d/canvas_modulate.cpp46
-rw-r--r--scene/2d/canvas_modulate.h23
-rw-r--r--scene/2d/light_2d.cpp300
-rw-r--r--scene/2d/light_2d.h86
-rw-r--r--scene/2d/light_occluder_2d.cpp201
-rw-r--r--scene/2d/light_occluder_2d.h73
-rw-r--r--scene/2d/navigation2d.cpp660
-rw-r--r--scene/2d/navigation2d.h138
-rw-r--r--scene/2d/navigation_polygon.cpp450
-rw-r--r--scene/2d/navigation_polygon.h84
-rw-r--r--scene/2d/node_2d.cpp14
-rw-r--r--scene/2d/node_2d.h3
-rw-r--r--scene/2d/tile_map.cpp104
-rw-r--r--scene/2d/tile_map.h14
16 files changed, 2433 insertions, 143 deletions
diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp
index ae857bbce9..4a1842100a 100644
--- a/scene/2d/canvas_item.cpp
+++ b/scene/2d/canvas_item.cpp
@@ -36,6 +36,192 @@
#include "scene/resources/texture.h"
#include "scene/resources/style_box.h"
+
+bool CanvasItemMaterial::_set(const StringName& p_name, const Variant& p_value) {
+
+ if (p_name==SceneStringNames::get_singleton()->shader_shader) {
+ set_shader(p_value);
+ return true;
+ } else if (p_name==SceneStringNames::get_singleton()->shader_unshaded) {
+ set_unshaded(p_value);
+ print_line("set unshaded");
+ return true;
+ } else {
+
+ if (shader.is_valid()) {
+
+
+ StringName pr = shader->remap_param(p_name);
+ if (!pr) {
+ String n = p_name;
+ if (n.find("param/")==0) { //backwards compatibility
+ pr = n.substr(6,n.length());
+ }
+ }
+ if (pr) {
+ VisualServer::get_singleton()->canvas_item_material_set_shader_param(material,pr,p_value);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool CanvasItemMaterial::_get(const StringName& p_name,Variant &r_ret) const {
+
+
+ if (p_name==SceneStringNames::get_singleton()->shader_shader) {
+
+ r_ret=get_shader();
+ return true;
+ } else if (p_name==SceneStringNames::get_singleton()->shader_unshaded) {
+
+
+ r_ret=unshaded;
+ return true;
+ } else {
+
+ if (shader.is_valid()) {
+
+ StringName pr = shader->remap_param(p_name);
+ if (pr) {
+ r_ret=VisualServer::get_singleton()->canvas_item_material_get_shader_param(material,pr);
+ return true;
+ }
+ }
+
+ }
+
+
+ return false;
+}
+
+
+void CanvasItemMaterial::_get_property_list( List<PropertyInfo> *p_list) const {
+
+ p_list->push_back( PropertyInfo( Variant::OBJECT, "shader/shader", PROPERTY_HINT_RESOURCE_TYPE,"CanvasItemShader,CanvasItemShaderGraph" ) );
+ p_list->push_back( PropertyInfo( Variant::BOOL, "shader/unshaded") );
+
+ if (!shader.is_null()) {
+
+ shader->get_param_list(p_list);
+ }
+
+}
+
+void CanvasItemMaterial::set_shader(const Ref<Shader>& p_shader) {
+
+ ERR_FAIL_COND(p_shader.is_valid() && p_shader->get_mode()!=Shader::MODE_CANVAS_ITEM);
+#ifdef TOOLS_ENABLED
+
+ if (shader.is_valid()) {
+ shader->disconnect("changed",this,"_shader_changed");
+ }
+#endif
+ shader=p_shader;
+
+#ifdef TOOLS_ENABLED
+
+ if (shader.is_valid()) {
+ shader->connect("changed",this,"_shader_changed");
+ }
+#endif
+
+ RID rid;
+ if (shader.is_valid())
+ rid=shader->get_rid();
+
+ VS::get_singleton()->canvas_item_material_set_shader(material,rid);
+ _change_notify(); //properties for shader exposed
+ emit_changed();
+}
+
+Ref<Shader> CanvasItemMaterial::get_shader() const{
+
+ return shader;
+}
+
+void CanvasItemMaterial::set_shader_param(const StringName& p_param,const Variant& p_value){
+
+ VS::get_singleton()->canvas_item_material_set_shader_param(material,p_param,p_value);
+}
+
+Variant CanvasItemMaterial::get_shader_param(const StringName& p_param) const{
+
+ return VS::get_singleton()->canvas_item_material_get_shader_param(material,p_param);
+}
+
+void CanvasItemMaterial::_shader_changed() {
+
+
+}
+
+RID CanvasItemMaterial::get_rid() const {
+
+ return material;
+}
+
+void CanvasItemMaterial::set_unshaded(bool p_unshaded) {
+
+ unshaded=p_unshaded;
+ VS::get_singleton()->canvas_item_material_set_unshaded(material,p_unshaded);
+}
+
+bool CanvasItemMaterial::is_unshaded() const{
+
+ return unshaded;
+}
+
+void CanvasItemMaterial::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_shader","shader:Shader"),&CanvasItemMaterial::set_shader);
+ ObjectTypeDB::bind_method(_MD("get_shader:Shader"),&CanvasItemMaterial::get_shader);
+ ObjectTypeDB::bind_method(_MD("set_shader_param","param","value"),&CanvasItemMaterial::set_shader_param);
+ ObjectTypeDB::bind_method(_MD("get_shader_param","param"),&CanvasItemMaterial::get_shader_param);
+ ObjectTypeDB::bind_method(_MD("set_unshaded","unshaded"),&CanvasItemMaterial::set_unshaded);
+ ObjectTypeDB::bind_method(_MD("is_unshaded"),&CanvasItemMaterial::is_unshaded);
+
+}
+
+void CanvasItemMaterial::get_argument_options(const StringName& p_function,int p_idx,List<String>*r_options) const {
+
+ String f = p_function.operator String();
+ if ((f=="get_shader_param" || f=="set_shader_param") && p_idx==0) {
+
+ if (shader.is_valid()) {
+ List<PropertyInfo> pl;
+ shader->get_param_list(&pl);
+ for (List<PropertyInfo>::Element *E=pl.front();E;E=E->next()) {
+ r_options->push_back("\""+E->get().name.replace_first("shader_param/","")+"\"");
+ }
+ }
+ }
+ Resource::get_argument_options(p_function,p_idx,r_options);
+}
+
+CanvasItemMaterial::CanvasItemMaterial() {
+
+ material=VS::get_singleton()->canvas_item_material_create();
+ unshaded=false;
+}
+
+CanvasItemMaterial::~CanvasItemMaterial(){
+
+ VS::get_singleton()->free(material);
+}
+
+
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////
+
+
+
bool CanvasItem::is_visible() const {
if (!is_inside_tree())
@@ -458,6 +644,16 @@ CanvasItem::BlendMode CanvasItem::get_blend_mode() const {
return blend_mode;
}
+void CanvasItem::set_light_mask(int p_light_mask) {
+
+ light_mask=p_light_mask;
+ VS::get_singleton()->canvas_item_set_light_mask(canvas_item,p_light_mask);
+}
+
+int CanvasItem::get_light_mask() const{
+
+ return light_mask;
+}
void CanvasItem::item_rect_changed() {
@@ -511,7 +707,7 @@ void CanvasItem::draw_texture(const Ref<Texture>& p_texture,const Point2& p_pos)
p_texture->draw(canvas_item,p_pos);
}
-void CanvasItem::draw_texture_rect(const Ref<Texture>& p_texture,const Rect2& p_rect, bool p_tile,const Color& p_modulate) {
+void CanvasItem::draw_texture_rect(const Ref<Texture>& p_texture,const Rect2& p_rect, bool p_tile,const Color& p_modulate, bool p_transpose) {
if (!drawing) {
ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
@@ -519,17 +715,17 @@ void CanvasItem::draw_texture_rect(const Ref<Texture>& p_texture,const Rect2& p_
}
ERR_FAIL_COND(p_texture.is_null());
- p_texture->draw_rect(canvas_item,p_rect,p_tile,p_modulate);
+ p_texture->draw_rect(canvas_item,p_rect,p_tile,p_modulate,p_transpose);
}
-void CanvasItem::draw_texture_rect_region(const Ref<Texture>& p_texture,const Rect2& p_rect, const Rect2& p_src_rect,const Color& p_modulate) {
+void CanvasItem::draw_texture_rect_region(const Ref<Texture>& p_texture,const Rect2& p_rect, const Rect2& p_src_rect,const Color& p_modulate, bool p_transpose) {
if (!drawing) {
ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
ERR_FAIL();
}
ERR_FAIL_COND(p_texture.is_null());
- p_texture->draw_rect_region(canvas_item,p_rect,p_src_rect,p_modulate);
+ p_texture->draw_rect_region(canvas_item,p_rect,p_src_rect,p_modulate,p_transpose);
}
void CanvasItem::draw_style_box(const Ref<StyleBox>& p_style_box,const Rect2& p_rect) {
@@ -720,95 +916,36 @@ bool CanvasItem::is_draw_behind_parent_enabled() const{
return behind;
}
-void CanvasItem::set_shader(const Ref<Shader>& p_shader) {
-
- ERR_FAIL_COND(p_shader.is_valid() && p_shader->get_mode()!=Shader::MODE_CANVAS_ITEM);
-
-#ifdef TOOLS_ENABLED
+void CanvasItem::set_material(const Ref<CanvasItemMaterial>& p_material) {
- if (shader.is_valid()) {
- shader->disconnect("changed",this,"_shader_changed");
- }
-#endif
- shader=p_shader;
-
-#ifdef TOOLS_ENABLED
-
- if (shader.is_valid()) {
- shader->connect("changed",this,"_shader_changed");
- }
-#endif
+ material=p_material;
RID rid;
- if (shader.is_valid())
- rid=shader->get_rid();
- VS::get_singleton()->canvas_item_set_shader(canvas_item,rid);
- _change_notify(); //properties for shader exposed
+ if (material.is_valid())
+ rid=material->get_rid();
+ VS::get_singleton()->canvas_item_set_material(canvas_item,rid);
+ _change_notify(); //properties for material exposed
}
-void CanvasItem::set_use_parent_shader(bool p_use_parent_shader) {
+void CanvasItem::set_use_parent_material(bool p_use_parent_material) {
- use_parent_shader=p_use_parent_shader;
- VS::get_singleton()->canvas_item_set_use_parent_shader(canvas_item,p_use_parent_shader);
+ use_parent_material=p_use_parent_material;
+ VS::get_singleton()->canvas_item_set_use_parent_material(canvas_item,p_use_parent_material);
}
-bool CanvasItem::get_use_parent_shader() const{
+bool CanvasItem::get_use_parent_material() const{
- return use_parent_shader;
+ return use_parent_material;
}
-Ref<Shader> CanvasItem::get_shader() const{
+Ref<CanvasItemMaterial> CanvasItem::get_material() const{
- return shader;
-}
-
-void CanvasItem::set_shader_param(const StringName& p_param,const Variant& p_value) {
-
- VS::get_singleton()->canvas_item_set_shader_param(canvas_item,p_param,p_value);
-}
-
-Variant CanvasItem::get_shader_param(const StringName& p_param) const {
-
- return VS::get_singleton()->canvas_item_get_shader_param(canvas_item,p_param);
-}
-
-bool CanvasItem::_set(const StringName& p_name, const Variant& p_value) {
-
- if (shader.is_valid()) {
- StringName pr = shader->remap_param(p_name);
- if (pr) {
- set_shader_param(pr,p_value);
- return true;
- }
- }
- return false;
+ return material;
}
-bool CanvasItem::_get(const StringName& p_name,Variant &r_ret) const{
- if (shader.is_valid()) {
- StringName pr = shader->remap_param(p_name);
- if (pr) {
- r_ret=get_shader_param(pr);
- return true;
- }
- }
- return false;
-}
-void CanvasItem::_get_property_list( List<PropertyInfo> *p_list) const{
- if (shader.is_valid()) {
- shader->get_param_list(p_list);
- }
-}
-
-#ifdef TOOLS_ENABLED
-void CanvasItem::_shader_changed() {
-
- _change_notify();
-}
-#endif
void CanvasItem::_bind_methods() {
@@ -840,19 +977,19 @@ void CanvasItem::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_blend_mode","blend_mode"),&CanvasItem::set_blend_mode);
ObjectTypeDB::bind_method(_MD("get_blend_mode"),&CanvasItem::get_blend_mode);
+ ObjectTypeDB::bind_method(_MD("set_light_mask","light_mask"),&CanvasItem::set_light_mask);
+ ObjectTypeDB::bind_method(_MD("get_light_mask"),&CanvasItem::get_light_mask);
+
ObjectTypeDB::bind_method(_MD("set_opacity","opacity"),&CanvasItem::set_opacity);
ObjectTypeDB::bind_method(_MD("get_opacity"),&CanvasItem::get_opacity);
ObjectTypeDB::bind_method(_MD("set_self_opacity","self_opacity"),&CanvasItem::set_self_opacity);
ObjectTypeDB::bind_method(_MD("get_self_opacity"),&CanvasItem::get_self_opacity);
- ObjectTypeDB::bind_method(_MD("set_draw_behind_parent","enabe"),&CanvasItem::set_draw_behind_parent);
+ ObjectTypeDB::bind_method(_MD("set_draw_behind_parent","enable"),&CanvasItem::set_draw_behind_parent);
ObjectTypeDB::bind_method(_MD("is_draw_behind_parent_enabled"),&CanvasItem::is_draw_behind_parent_enabled);
ObjectTypeDB::bind_method(_MD("_set_on_top","on_top"),&CanvasItem::_set_on_top);
ObjectTypeDB::bind_method(_MD("_is_on_top"),&CanvasItem::_is_on_top);
-#ifdef TOOLS_ENABLED
- ObjectTypeDB::bind_method(_MD("_shader_changed"),&CanvasItem::_shader_changed);
-#endif
//ObjectTypeDB::bind_method(_MD("get_transform"),&CanvasItem::get_transform);
ObjectTypeDB::bind_method(_MD("draw_line","from","to","color","width"),&CanvasItem::draw_line,DEFVAL(1.0));
@@ -871,16 +1008,18 @@ void CanvasItem::_bind_methods() {
ObjectTypeDB::bind_method(_MD("draw_set_transform","pos","rot","scale"),&CanvasItem::draw_set_transform);
ObjectTypeDB::bind_method(_MD("get_transform"),&CanvasItem::get_transform);
ObjectTypeDB::bind_method(_MD("get_global_transform"),&CanvasItem::get_global_transform);
+ ObjectTypeDB::bind_method(_MD("get_global_transform_with_canvas"),&CanvasItem::get_global_transform_with_canvas);
ObjectTypeDB::bind_method(_MD("get_viewport_transform"),&CanvasItem::get_viewport_transform);
ObjectTypeDB::bind_method(_MD("get_viewport_rect"),&CanvasItem::get_viewport_rect);
ObjectTypeDB::bind_method(_MD("get_canvas"),&CanvasItem::get_canvas);
ObjectTypeDB::bind_method(_MD("get_world_2d"),&CanvasItem::get_world_2d);
//ObjectTypeDB::bind_method(_MD("get_viewport"),&CanvasItem::get_viewport);
- ObjectTypeDB::bind_method(_MD("set_shader","shader"),&CanvasItem::set_shader);
- ObjectTypeDB::bind_method(_MD("get_shader"),&CanvasItem::get_shader);
- ObjectTypeDB::bind_method(_MD("set_use_parent_shader","enable"),&CanvasItem::set_use_parent_shader);
- ObjectTypeDB::bind_method(_MD("get_use_parent_shader"),&CanvasItem::get_use_parent_shader);
+ ObjectTypeDB::bind_method(_MD("set_material","material:CanvasItemMaterial"),&CanvasItem::set_material);
+ ObjectTypeDB::bind_method(_MD("get_material:CanvasItemMaterial"),&CanvasItem::get_material);
+
+ ObjectTypeDB::bind_method(_MD("set_use_parent_material","enable"),&CanvasItem::set_use_parent_material);
+ ObjectTypeDB::bind_method(_MD("get_use_parent_material"),&CanvasItem::get_use_parent_material);
BIND_VMETHOD(MethodInfo("_draw"));
@@ -891,8 +1030,9 @@ void CanvasItem::_bind_methods() {
ADD_PROPERTY( PropertyInfo(Variant::BOOL,"visibility/on_top",PROPERTY_HINT_NONE,"",0), _SCS("_set_on_top"),_SCS("_is_on_top") ); //compatibility
ADD_PROPERTYNZ( PropertyInfo(Variant::INT,"visibility/blend_mode",PROPERTY_HINT_ENUM, "Mix,Add,Sub,Mul,PMAlpha"), _SCS("set_blend_mode"),_SCS("get_blend_mode") );
- ADD_PROPERTYNZ( PropertyInfo(Variant::OBJECT,"shader/shader",PROPERTY_HINT_RESOURCE_TYPE, "CanvasItemShader,CanvasItemShaderGraph"), _SCS("set_shader"),_SCS("get_shader") );
- ADD_PROPERTYNZ( PropertyInfo(Variant::BOOL,"shader/use_parent"), _SCS("set_use_parent_shader"),_SCS("get_use_parent_shader") );
+ ADD_PROPERTYNZ( PropertyInfo(Variant::INT,"visibility/light_mask",PROPERTY_HINT_ALL_FLAGS), _SCS("set_light_mask"),_SCS("get_light_mask") );
+ ADD_PROPERTYNZ( PropertyInfo(Variant::OBJECT,"material/material",PROPERTY_HINT_RESOURCE_TYPE, "CanvasItemMaterial"), _SCS("set_material"),_SCS("get_material") );
+ ADD_PROPERTYNZ( PropertyInfo(Variant::BOOL,"material/use_parent"), _SCS("set_use_parent_material"),_SCS("get_use_parent_material") );
//exporting these two things doesn't really make much sense i think
//ADD_PROPERTY( PropertyInfo(Variant::BOOL,"transform/toplevel"), _SCS("set_as_toplevel"),_SCS("is_set_as_toplevel") );
//ADD_PROPERTY(PropertyInfo(Variant::BOOL,"transform/notify"),_SCS("set_transform_notify"),_SCS("is_transform_notify_enabled"));
@@ -969,8 +1109,9 @@ CanvasItem::CanvasItem() : xform_change(this) {
block_transform_notify=false;
// viewport=NULL;
canvas_layer=NULL;
- use_parent_shader;
+ use_parent_material=false;
global_invalid=true;
+ light_mask=1;
C=NULL;
diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h
index ed3ade9df2..0c7be261ab 100644
--- a/scene/2d/canvas_item.h
+++ b/scene/2d/canvas_item.h
@@ -40,6 +40,41 @@ class Font;
class StyleBox;
+class CanvasItemMaterial : public Resource{
+
+ OBJ_TYPE(CanvasItemMaterial,Resource);
+ RID material;
+ Ref<Shader> shader;
+ bool unshaded;
+
+protected:
+
+ bool _set(const StringName& p_name, const Variant& p_value);
+ bool _get(const StringName& p_name,Variant &r_ret) const;
+ void _get_property_list( List<PropertyInfo> *p_list) const;
+
+ void _shader_changed();
+ static void _bind_methods();
+
+ void get_argument_options(const StringName& p_function,int p_idx,List<String>*r_options) const;
+
+public:
+
+ void set_shader(const Ref<Shader>& p_shader);
+ Ref<Shader> get_shader() const;
+
+ void set_shader_param(const StringName& p_param,const Variant& p_value);
+ Variant get_shader_param(const StringName& p_param) const;
+
+ void set_unshaded(bool p_unshaded);
+ bool is_unshaded() const;
+
+ virtual RID get_rid() const;
+ CanvasItemMaterial();
+ ~CanvasItemMaterial();
+};
+
+
class CanvasItem : public Node {
OBJ_TYPE( CanvasItem, Node );
@@ -71,6 +106,7 @@ private:
List<CanvasItem*>::Element *C;
BlendMode blend_mode;
+ int light_mask;
bool first_draw;
bool hidden;
@@ -80,9 +116,9 @@ private:
bool drawing;
bool block_transform_notify;
bool behind;
+ bool use_parent_material;
- bool use_parent_shader;
- Ref<Shader> shader;
+ Ref<CanvasItemMaterial> material;
mutable Matrix32 global_transform;
mutable bool global_invalid;
@@ -103,9 +139,6 @@ private:
void _queue_sort_children();
void _sort_children();
-#ifdef TOOLS_ENABLED
- void _shader_changed();
-#endif
void _notify_transform(CanvasItem *p_node);
void _set_on_top(bool p_on_top) { set_draw_behind_parent(!p_on_top); }
@@ -113,11 +146,6 @@ private:
protected:
- bool _set(const StringName& p_name, const Variant& p_value);
- bool _get(const StringName& p_name,Variant &r_ret) const;
- void _get_property_list( List<PropertyInfo> *p_list) const;
-
-
_FORCE_INLINE_ void _notify_transform() { if (!is_inside_tree()) return; _notify_transform(this); if (!block_transform_notify) notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED); }
void item_rect_changed();
@@ -158,6 +186,9 @@ public:
void set_blend_mode(BlendMode p_blend_mode);
BlendMode get_blend_mode() const;
+ void set_light_mask(int p_light_mask);
+ int get_light_mask() const;
+
void set_opacity(float p_opacity);
float get_opacity() const;
@@ -170,8 +201,8 @@ public:
void draw_rect(const Rect2& p_rect, const Color& p_color);
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);
- 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));
- void draw_texture_rect_region(const Ref<Texture>& p_texture,const Rect2& p_rect, const Rect2& p_src_rect,const Color& p_modulate=Color(1,1,1));
+ 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);
+ void draw_texture_rect_region(const Ref<Texture>& p_texture,const Rect2& p_rect, const Rect2& p_src_rect,const Color& p_modulate=Color(1,1,1), bool p_transpose=false);
void draw_style_box(const Ref<StyleBox>& p_style_box,const Rect2& p_rect);
void draw_primitive(const Vector<Point2>& p_points, const Vector<Color>& p_colors,const Vector<Point2>& p_uvs, Ref<Texture> p_texture=Ref<Texture>(),float p_width=1);
void draw_polygon(const Vector<Point2>& p_points, const Vector<Color>& p_colors,const Vector<Point2>& p_uvs=Vector<Point2>(), Ref<Texture> p_texture=Ref<Texture>());
@@ -212,14 +243,12 @@ public:
RID get_canvas() const;
Ref<World2D> get_world_2d() const;
- void set_shader(const Ref<Shader>& p_shader);
- Ref<Shader> get_shader() const;
+ void set_material(const Ref<CanvasItemMaterial>& p_material);
+ Ref<CanvasItemMaterial> get_material() const;
- void set_use_parent_shader(bool p_use_parent_shader);
- bool get_use_parent_shader() const;
+ void set_use_parent_material(bool p_use_parent_material);
+ bool get_use_parent_material() const;
- void set_shader_param(const StringName& p_param,const Variant& p_value);
- Variant get_shader_param(const StringName& p_param) const;
CanvasItem();
~CanvasItem();
diff --git a/scene/2d/canvas_modulate.cpp b/scene/2d/canvas_modulate.cpp
new file mode 100644
index 0000000000..82dd8012a5
--- /dev/null
+++ b/scene/2d/canvas_modulate.cpp
@@ -0,0 +1,46 @@
+#include "canvas_modulate.h"
+
+
+void CanvasModulate::_notification(int p_what) {
+
+ if (p_what==NOTIFICATION_ENTER_CANVAS) {
+
+ VS::get_singleton()->canvas_set_modulate(get_canvas(),color);
+ } else if (p_what==NOTIFICATION_EXIT_CANVAS) {
+
+ VS::get_singleton()->canvas_set_modulate(get_canvas(),Color(1,1,1,1));
+ }
+}
+
+void CanvasModulate::_bind_methods(){
+
+ ObjectTypeDB::bind_method(_MD("set_color","color"),&CanvasModulate::set_color);
+ ObjectTypeDB::bind_method(_MD("get_color"),&CanvasModulate::get_color);
+
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR,"color"),_SCS("set_color"),_SCS("get_color"));
+}
+
+
+void CanvasModulate::set_color(const Color& p_color){
+
+ color=p_color;
+ if (is_inside_tree()) {
+ VS::get_singleton()->canvas_set_modulate(get_canvas(),color);
+ }
+}
+Color CanvasModulate::get_color() const {
+
+ return color;
+}
+
+
+CanvasModulate::CanvasModulate()
+{
+ color=Color(1,1,1,1);
+}
+
+CanvasModulate::~CanvasModulate()
+{
+
+}
+
diff --git a/scene/2d/canvas_modulate.h b/scene/2d/canvas_modulate.h
new file mode 100644
index 0000000000..a6894f29c2
--- /dev/null
+++ b/scene/2d/canvas_modulate.h
@@ -0,0 +1,23 @@
+#ifndef CANVASMODULATE_H
+#define CANVASMODULATE_H
+
+#include "scene/2d/node_2d.h"
+
+class CanvasModulate : public Node2D {
+
+ OBJ_TYPE(CanvasModulate,Node2D);
+
+ Color color;
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+ void set_color(const Color& p_color);
+ Color get_color() const;
+
+ CanvasModulate();
+ ~CanvasModulate();
+};
+
+#endif // CANVASMODULATE_H
diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp
new file mode 100644
index 0000000000..93be0c397f
--- /dev/null
+++ b/scene/2d/light_2d.cpp
@@ -0,0 +1,300 @@
+#include "light_2d.h"
+#include "servers/visual_server.h"
+
+void Light2D::edit_set_pivot(const Point2& p_pivot) {
+
+ set_texture_offset(p_pivot);
+
+}
+
+Point2 Light2D::edit_get_pivot() const {
+
+ return get_texture_offset();
+}
+bool Light2D::edit_has_pivot() const {
+
+ return true;
+}
+
+Rect2 Light2D::get_item_rect() const {
+
+ if (texture.is_null())
+ return Rect2(0,0,1,1);
+
+ Size2i s;
+
+ s = texture->get_size();
+ Point2i ofs=texture_offset;
+ ofs-=s/2;
+
+ if (s==Size2(0,0))
+ s=Size2(1,1);
+
+ return Rect2(ofs,s);
+}
+
+
+void Light2D::set_enabled( bool p_enabled) {
+
+ VS::get_singleton()->canvas_light_set_enabled(canvas_light,p_enabled);
+ enabled=p_enabled;
+}
+
+bool Light2D::is_enabled() const {
+
+ return enabled;
+}
+
+void Light2D::set_texture( const Ref<Texture>& p_texture) {
+
+ texture=p_texture;
+ if (texture.is_valid())
+ VS::get_singleton()->canvas_light_set_texture(canvas_light,texture->get_rid());
+ else
+ VS::get_singleton()->canvas_light_set_texture(canvas_light,RID());
+}
+
+Ref<Texture> Light2D::get_texture() const {
+
+ return texture;
+}
+
+void Light2D::set_texture_offset( const Vector2& p_offset) {
+
+ texture_offset=p_offset;
+ VS::get_singleton()->canvas_light_set_texture_offset(canvas_light,texture_offset);
+}
+
+Vector2 Light2D::get_texture_offset() const {
+
+ return texture_offset;
+}
+
+void Light2D::set_color( const Color& p_color) {
+
+ color=p_color;
+ VS::get_singleton()->canvas_light_set_color(canvas_light,color);
+
+}
+Color Light2D::get_color() const {
+
+ return color;
+}
+
+void Light2D::set_height( float p_height) {
+
+ height=p_height;
+ VS::get_singleton()->canvas_light_set_height(canvas_light,height);
+
+}
+float Light2D::get_height() const {
+
+ return height;
+}
+
+void Light2D::set_z_range_min( int p_min_z) {
+
+ z_min=p_min_z;
+ VS::get_singleton()->canvas_light_set_z_range(canvas_light,z_min,z_max);
+
+}
+int Light2D::get_z_range_min() const {
+
+ return z_min;
+}
+
+void Light2D::set_z_range_max( int p_max_z) {
+
+ z_max=p_max_z;
+ VS::get_singleton()->canvas_light_set_z_range(canvas_light,z_min,z_max);
+
+}
+int Light2D::get_z_range_max() const {
+
+ return z_max;
+}
+
+void Light2D::set_layer_range_min( int p_min_layer) {
+
+ layer_min=p_min_layer;
+ VS::get_singleton()->canvas_light_set_layer_range(canvas_light,layer_min,layer_max);
+
+}
+int Light2D::get_layer_range_min() const {
+
+ return layer_min;
+}
+
+void Light2D::set_layer_range_max( int p_max_layer) {
+
+ layer_max=p_max_layer;
+ VS::get_singleton()->canvas_light_set_layer_range(canvas_light,layer_min,layer_max);
+
+}
+int Light2D::get_layer_range_max() const {
+
+ return layer_max;
+}
+
+void Light2D::set_item_mask( int p_mask) {
+
+ item_mask=p_mask;
+ VS::get_singleton()->canvas_light_set_item_mask(canvas_light,item_mask);
+
+}
+
+int Light2D::get_item_mask() const {
+
+ return item_mask;
+}
+
+void Light2D::set_subtract_mode( bool p_enable ) {
+
+ subtract_mode=p_enable;
+ VS::get_singleton()->canvas_light_set_subtract_mode(canvas_light,p_enable);
+}
+
+bool Light2D::get_subtract_mode() const {
+
+ return subtract_mode;
+}
+
+void Light2D::set_shadow_enabled( bool p_enabled) {
+
+ shadow=p_enabled;
+ VS::get_singleton()->canvas_light_set_shadow_enabled(canvas_light,shadow);
+
+}
+bool Light2D::is_shadow_enabled() const {
+
+ return shadow;
+}
+
+void Light2D::set_shadow_buffer_size( int p_size ) {
+
+ shadow_buffer_size=p_size;
+ VS::get_singleton()->canvas_light_set_shadow_buffer_size(canvas_light,shadow_buffer_size);
+}
+
+int Light2D::get_shadow_buffer_size() const {
+
+ return shadow_buffer_size;
+}
+
+void Light2D::set_shadow_esm_multiplier( float p_multiplier) {
+
+ shadow_esm_multiplier=p_multiplier;
+ VS::get_singleton()->canvas_light_set_shadow_esm_multiplier(canvas_light,p_multiplier);
+}
+
+float Light2D::get_shadow_esm_multiplier() const{
+
+ return shadow_esm_multiplier;
+}
+
+
+void Light2D::_notification(int p_what) {
+
+ if (p_what==NOTIFICATION_ENTER_TREE) {
+
+ VS::get_singleton()->canvas_light_attach_to_canvas( canvas_light, get_canvas() );
+ }
+
+ if (p_what==NOTIFICATION_TRANSFORM_CHANGED) {
+
+ VS::get_singleton()->canvas_light_set_transform( canvas_light, get_global_transform());
+ }
+
+ if (p_what==NOTIFICATION_EXIT_TREE) {
+
+ VS::get_singleton()->canvas_light_attach_to_canvas( canvas_light, RID() );
+ }
+
+}
+
+void Light2D::_bind_methods() {
+
+
+ ObjectTypeDB::bind_method(_MD("set_enabled","enabled"),&Light2D::set_enabled);
+ ObjectTypeDB::bind_method(_MD("is_enabled"),&Light2D::is_enabled);
+
+ ObjectTypeDB::bind_method(_MD("set_texture","texture"),&Light2D::set_texture);
+ ObjectTypeDB::bind_method(_MD("get_texture"),&Light2D::get_texture);
+
+ ObjectTypeDB::bind_method(_MD("set_texture_offset","texture_offset"),&Light2D::set_texture_offset);
+ ObjectTypeDB::bind_method(_MD("get_texture_offset"),&Light2D::get_texture_offset);
+
+ ObjectTypeDB::bind_method(_MD("set_color","color"),&Light2D::set_color);
+ ObjectTypeDB::bind_method(_MD("get_color"),&Light2D::get_color);
+
+ ObjectTypeDB::bind_method(_MD("set_height","height"),&Light2D::set_height);
+ ObjectTypeDB::bind_method(_MD("get_height"),&Light2D::get_height);
+
+ ObjectTypeDB::bind_method(_MD("set_z_range_min","z"),&Light2D::set_z_range_min);
+ ObjectTypeDB::bind_method(_MD("get_z_range_min"),&Light2D::get_z_range_min);
+
+ ObjectTypeDB::bind_method(_MD("set_z_range_max","z"),&Light2D::set_z_range_max);
+ ObjectTypeDB::bind_method(_MD("get_z_range_max"),&Light2D::get_z_range_max);
+
+ ObjectTypeDB::bind_method(_MD("set_layer_range_min","layer"),&Light2D::set_layer_range_min);
+ ObjectTypeDB::bind_method(_MD("get_layer_range_min"),&Light2D::get_layer_range_min);
+
+ ObjectTypeDB::bind_method(_MD("set_layer_range_max","layer"),&Light2D::set_layer_range_max);
+ ObjectTypeDB::bind_method(_MD("get_layer_range_max"),&Light2D::get_layer_range_max);
+
+
+ ObjectTypeDB::bind_method(_MD("set_item_mask","item_mask"),&Light2D::set_item_mask);
+ ObjectTypeDB::bind_method(_MD("get_item_mask"),&Light2D::get_item_mask);
+
+ ObjectTypeDB::bind_method(_MD("set_subtract_mode","enable"),&Light2D::set_subtract_mode);
+ ObjectTypeDB::bind_method(_MD("get_subtract_mode"),&Light2D::get_subtract_mode);
+
+ ObjectTypeDB::bind_method(_MD("set_shadow_enabled","enabled"),&Light2D::set_shadow_enabled);
+ ObjectTypeDB::bind_method(_MD("is_shadow_enabled"),&Light2D::is_shadow_enabled);
+
+ ObjectTypeDB::bind_method(_MD("set_shadow_buffer_size","size"),&Light2D::set_shadow_buffer_size);
+ ObjectTypeDB::bind_method(_MD("get_shadow_buffer_size"),&Light2D::get_shadow_buffer_size);
+
+ ObjectTypeDB::bind_method(_MD("set_shadow_esm_multiplier","multiplier"),&Light2D::set_shadow_esm_multiplier);
+ ObjectTypeDB::bind_method(_MD("get_shadow_esm_multiplier"),&Light2D::get_shadow_esm_multiplier);
+
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"enabled"),_SCS("set_enabled"),_SCS("is_enabled"));
+ ADD_PROPERTY( PropertyInfo(Variant::OBJECT,"texture",PROPERTY_HINT_RESOURCE_TYPE,"Texture"),_SCS("set_texture"),_SCS("get_texture"));
+ ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"offset"),_SCS("set_texture_offset"),_SCS("get_texture_offset"));
+ ADD_PROPERTY( PropertyInfo(Variant::COLOR,"color"),_SCS("set_color"),_SCS("get_color"));
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"subtract"),_SCS("set_subtract_mode"),_SCS("get_subtract_mode"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"range/height"),_SCS("set_height"),_SCS("get_height"));
+ ADD_PROPERTY( PropertyInfo(Variant::INT,"range/z_min",PROPERTY_HINT_RANGE,itos(VS::CANVAS_ITEM_Z_MIN)+","+itos(VS::CANVAS_ITEM_Z_MAX)+",1"),_SCS("set_z_range_min"),_SCS("get_z_range_min"));
+ ADD_PROPERTY( PropertyInfo(Variant::INT,"range/z_max",PROPERTY_HINT_RANGE,itos(VS::CANVAS_ITEM_Z_MIN)+","+itos(VS::CANVAS_ITEM_Z_MAX)+",1"),_SCS("set_z_range_max"),_SCS("get_z_range_max"));
+ ADD_PROPERTY( PropertyInfo(Variant::INT,"range/layer_min",PROPERTY_HINT_RANGE,"-512,512,1"),_SCS("set_layer_range_min"),_SCS("get_layer_range_min"));
+ ADD_PROPERTY( PropertyInfo(Variant::INT,"range/layer_max",PROPERTY_HINT_RANGE,"-512,512,1"),_SCS("set_layer_range_max"),_SCS("get_layer_range_max"));
+ ADD_PROPERTY( PropertyInfo(Variant::INT,"range/item_mask",PROPERTY_HINT_ALL_FLAGS),_SCS("set_item_mask"),_SCS("get_item_mask"));
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"shadow/enabled"),_SCS("set_shadow_enabled"),_SCS("is_shadow_enabled"));
+ ADD_PROPERTY( PropertyInfo(Variant::INT,"shadow/buffer_size",PROPERTY_HINT_RANGE,"32,16384,1"),_SCS("set_shadow_buffer_size"),_SCS("get_shadow_buffer_size"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"shadow/esm_multiplier",PROPERTY_HINT_RANGE,"1,4096,0.1"),_SCS("set_shadow_esm_multiplier"),_SCS("get_shadow_esm_multiplier"));
+
+
+}
+
+Light2D::Light2D() {
+
+ canvas_light=VisualServer::get_singleton()->canvas_light_create();
+ enabled=true;
+ shadow=false;
+ color=Color(1,1,1);
+ height=0;
+ z_min=-1024;
+ z_max=1024;
+ layer_min=0;
+ layer_max=0;
+ item_mask=1;
+ subtract_mode=false;
+ shadow_buffer_size=2048;
+ shadow_esm_multiplier=80;
+
+}
+
+Light2D::~Light2D() {
+
+ VisualServer::get_singleton()->free(canvas_light);
+}
diff --git a/scene/2d/light_2d.h b/scene/2d/light_2d.h
new file mode 100644
index 0000000000..89f351c3cd
--- /dev/null
+++ b/scene/2d/light_2d.h
@@ -0,0 +1,86 @@
+#ifndef LIGHT_2D_H
+#define LIGHT_2D_H
+
+#include "scene/2d/node_2d.h"
+
+class Light2D : public Node2D {
+
+ OBJ_TYPE(Light2D,Node2D);
+private:
+ RID canvas_light;
+ bool enabled;
+ bool shadow;
+ Color color;
+ float height;
+ int z_min;
+ int z_max;
+ int layer_min;
+ int layer_max;
+ int item_mask;
+ int shadow_buffer_size;
+ float shadow_esm_multiplier;
+ bool subtract_mode;
+ Ref<Texture> texture;
+ Vector2 texture_offset;
+
+protected:
+
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+
+ virtual void edit_set_pivot(const Point2& p_pivot);
+ virtual Point2 edit_get_pivot() const;
+ virtual bool edit_has_pivot() const;
+
+ void set_enabled( bool p_enabled);
+ bool is_enabled() const;
+
+ void set_texture( const Ref<Texture>& p_texture);
+ Ref<Texture> get_texture() const;
+
+ void set_texture_offset( const Vector2& p_offset);
+ Vector2 get_texture_offset() const;
+
+ void set_color( const Color& p_color);
+ Color get_color() const;
+
+ void set_height( float p_height);
+ float get_height() const;
+
+ void set_z_range_min( int p_min_z);
+ int get_z_range_min() const;
+
+ void set_z_range_max( int p_max_z);
+ int get_z_range_max() const;
+
+ void set_layer_range_min( int p_min_layer);
+ int get_layer_range_min() const;
+
+ void set_layer_range_max( int p_max_layer);
+ int get_layer_range_max() const;
+
+ void set_item_mask( int p_mask);
+ int get_item_mask() const;
+
+ void set_subtract_mode( bool p_enable );
+ bool get_subtract_mode() const;
+
+ void set_shadow_enabled( bool p_enabled);
+ bool is_shadow_enabled() const;
+
+ void set_shadow_buffer_size( int p_size );
+ int get_shadow_buffer_size() const;
+
+ void set_shadow_esm_multiplier( float p_multiplier);
+ float get_shadow_esm_multiplier() const;
+
+ virtual Rect2 get_item_rect() const;
+
+ Light2D();
+ ~Light2D();
+};
+
+
+#endif // LIGHT_2D_H
diff --git a/scene/2d/light_occluder_2d.cpp b/scene/2d/light_occluder_2d.cpp
new file mode 100644
index 0000000000..186ea2e248
--- /dev/null
+++ b/scene/2d/light_occluder_2d.cpp
@@ -0,0 +1,201 @@
+#include "light_occluder_2d.h"
+
+
+void OccluderPolygon2D::set_polygon(const DVector<Vector2>& p_polygon) {
+
+ polygon=p_polygon;
+ VS::get_singleton()->canvas_occluder_polygon_set_shape(occ_polygon,p_polygon,closed);
+ emit_changed();
+}
+
+DVector<Vector2> OccluderPolygon2D::get_polygon() const{
+
+ return polygon;
+}
+
+void OccluderPolygon2D::set_closed(bool p_closed) {
+
+ if (closed==p_closed)
+ return;
+ closed=p_closed;
+ VS::get_singleton()->canvas_occluder_polygon_set_shape(occ_polygon,polygon,closed);
+ emit_changed();
+}
+
+bool OccluderPolygon2D::is_closed() const{
+
+ return closed;
+}
+
+void OccluderPolygon2D::set_cull_mode(CullMode p_mode){
+
+ cull=p_mode;
+ VS::get_singleton()->canvas_occluder_polygon_set_cull_mode(occ_polygon,VS::CanvasOccluderPolygonCullMode(p_mode));
+}
+
+OccluderPolygon2D::CullMode OccluderPolygon2D::get_cull_mode() const{
+
+ return cull;
+}
+
+
+RID OccluderPolygon2D::get_rid() const {
+
+ return occ_polygon;
+}
+
+void OccluderPolygon2D::_bind_methods() {
+
+
+ ObjectTypeDB::bind_method(_MD("set_closed","closed"),&OccluderPolygon2D::set_closed);
+ ObjectTypeDB::bind_method(_MD("is_closed"),&OccluderPolygon2D::is_closed);
+
+ ObjectTypeDB::bind_method(_MD("set_cull_mode","cull_mode"),&OccluderPolygon2D::set_cull_mode);
+ ObjectTypeDB::bind_method(_MD("get_cull_mode"),&OccluderPolygon2D::get_cull_mode);
+
+ ObjectTypeDB::bind_method(_MD("set_polygon","polygon"),&OccluderPolygon2D::set_polygon);
+ ObjectTypeDB::bind_method(_MD("get_polygon"),&OccluderPolygon2D::get_polygon);
+
+ ADD_PROPERTY( PropertyInfo(Variant::VECTOR2_ARRAY,"polygon"),_SCS("set_polygon"),_SCS("get_polygon"));
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"closed"),_SCS("set_closed"),_SCS("is_closed"));
+ ADD_PROPERTY( PropertyInfo(Variant::INT,"cull_mode",PROPERTY_HINT_ENUM,"Disabled,ClockWise,CounterClockWise"),_SCS("set_cull_mode"),_SCS("get_cull_mode"));
+
+ BIND_CONSTANT(CULL_DISABLED);
+ BIND_CONSTANT(CULL_CLOCKWISE);
+ BIND_CONSTANT(CULL_COUNTER_CLOCKWISE);
+}
+
+
+OccluderPolygon2D::OccluderPolygon2D() {
+
+ occ_polygon=VS::get_singleton()->canvas_occluder_polygon_create();
+ closed=true;
+ cull=CULL_DISABLED;
+}
+
+OccluderPolygon2D::~OccluderPolygon2D() {
+
+ VS::get_singleton()->free(occ_polygon);
+}
+
+#ifdef DEBUG_ENABLED
+void LightOccluder2D::_poly_changed() {
+
+ update();
+}
+#endif
+
+
+void LightOccluder2D::_notification(int p_what) {
+
+ if (p_what==NOTIFICATION_ENTER_CANVAS) {
+
+ VS::get_singleton()->canvas_light_occluder_attach_to_canvas(occluder,get_canvas());
+ VS::get_singleton()->canvas_light_occluder_set_transform(occluder,get_global_transform());
+
+ }
+ if (p_what==NOTIFICATION_TRANSFORM_CHANGED) {
+
+ VS::get_singleton()->canvas_light_occluder_set_transform(occluder,get_global_transform());
+ }
+
+ if (p_what==NOTIFICATION_DRAW) {
+
+ if (get_tree()->is_editor_hint()) {
+
+ if (occluder_polygon.is_valid()) {
+
+ DVector<Vector2> poly = occluder_polygon->get_polygon();
+
+ if (poly.size()) {
+ if (occluder_polygon->is_closed()) {
+ Vector<Color> color;
+ color.push_back(Color(0,0,0,0.6));
+ draw_polygon(Variant(poly),color);
+ } else {
+
+ int ps=poly.size();
+ DVector<Vector2>::Read r = poly.read();
+ for(int i=0;i<ps-1;i++) {
+
+ draw_line(r[i],r[i+1],Color(0,0,0,0.6),3);
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ if (p_what==NOTIFICATION_EXIT_CANVAS) {
+
+ VS::get_singleton()->canvas_light_occluder_attach_to_canvas(occluder,RID());
+ }
+
+
+}
+
+void LightOccluder2D::set_occluder_polygon(const Ref<OccluderPolygon2D>& p_polygon) {
+
+#ifdef DEBUG_ENABLED
+ if (occluder_polygon.is_valid())
+ occluder_polygon->disconnect("changed",this,"_poly_changed");
+#endif
+ occluder_polygon=p_polygon;
+
+ if (occluder_polygon.is_valid())
+ VS::get_singleton()->canvas_light_occluder_set_polygon(occluder,occluder_polygon->get_rid());
+ else
+ VS::get_singleton()->canvas_light_occluder_set_polygon(occluder,RID());
+
+#ifdef DEBUG_ENABLED
+ if (occluder_polygon.is_valid())
+ occluder_polygon->connect("changed",this,"_poly_changed");
+ update();
+#endif
+
+}
+
+Ref<OccluderPolygon2D> LightOccluder2D::get_occluder_polygon() const {
+
+ return occluder_polygon;
+}
+
+void LightOccluder2D::set_occluder_light_mask(int p_mask) {
+
+ mask=p_mask;
+ VS::get_singleton()->canvas_light_occluder_set_light_mask(occluder,mask);
+}
+
+int LightOccluder2D::get_occluder_light_mask() const{
+
+ return mask;
+}
+
+void LightOccluder2D::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_occluder_polygon","polygon:OccluderPolygon2D"),&LightOccluder2D::set_occluder_polygon);
+ ObjectTypeDB::bind_method(_MD("get_occluder_polygon:OccluderPolygon2D"),&LightOccluder2D::get_occluder_polygon);
+
+ ObjectTypeDB::bind_method(_MD("set_occluder_light_mask","mask"),&LightOccluder2D::set_occluder_light_mask);
+ ObjectTypeDB::bind_method(_MD("get_occluder_light_mask"),&LightOccluder2D::get_occluder_light_mask);
+
+#ifdef DEBUG_ENABLED
+ ObjectTypeDB::bind_method("_poly_changed",&LightOccluder2D::_poly_changed);
+#endif
+
+ ADD_PROPERTY( PropertyInfo(Variant::OBJECT,"occluder",PROPERTY_HINT_RESOURCE_TYPE,"OccluderPolygon2D"),_SCS("set_occluder_polygon"),_SCS("get_occluder_polygon"));
+ ADD_PROPERTY( PropertyInfo(Variant::INT,"light_mask",PROPERTY_HINT_ALL_FLAGS),_SCS("set_occluder_light_mask"),_SCS("get_occluder_light_mask"));
+}
+
+LightOccluder2D::LightOccluder2D() {
+
+ occluder=VS::get_singleton()->canvas_light_occluder_create();
+ mask=1;
+}
+
+LightOccluder2D::~LightOccluder2D() {
+
+ VS::get_singleton()->free(occluder);
+}
+
diff --git a/scene/2d/light_occluder_2d.h b/scene/2d/light_occluder_2d.h
new file mode 100644
index 0000000000..0343e3697e
--- /dev/null
+++ b/scene/2d/light_occluder_2d.h
@@ -0,0 +1,73 @@
+#ifndef LIGHTOCCLUDER2D_H
+#define LIGHTOCCLUDER2D_H
+
+#include "scene/2d/node_2d.h"
+
+class OccluderPolygon2D : public Resource {
+
+ OBJ_TYPE(OccluderPolygon2D,Resource);
+public:
+
+ enum CullMode {
+ CULL_DISABLED,
+ CULL_CLOCKWISE,
+ CULL_COUNTER_CLOCKWISE
+ };
+private:
+
+
+ RID occ_polygon;
+ DVector<Vector2> polygon;
+ bool closed;
+ CullMode cull;
+
+protected:
+
+ static void _bind_methods();
+public:
+
+ void set_polygon(const DVector<Vector2>& p_polygon);
+ DVector<Vector2> get_polygon() const;
+
+ void set_closed(bool p_closed);
+ bool is_closed() const;
+
+ void set_cull_mode(CullMode p_mode);
+ CullMode get_cull_mode() const;
+
+ virtual RID get_rid() const;
+ OccluderPolygon2D();
+ ~OccluderPolygon2D();
+
+};
+
+VARIANT_ENUM_CAST(OccluderPolygon2D::CullMode);
+
+class LightOccluder2D : public Node2D {
+ OBJ_TYPE(LightOccluder2D,Node2D);
+
+ RID occluder;
+ bool enabled;
+ int mask;
+ Ref<OccluderPolygon2D> occluder_polygon;
+
+#ifdef DEBUG_ENABLED
+ void _poly_changed();
+#endif
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+ void set_occluder_polygon(const Ref<OccluderPolygon2D>& p_polygon);
+ Ref<OccluderPolygon2D> get_occluder_polygon() const;
+
+ void set_occluder_light_mask(int p_mask);
+ int get_occluder_light_mask() const;
+
+ LightOccluder2D();
+ ~LightOccluder2D();
+};
+
+#endif // LIGHTOCCLUDER2D_H
diff --git a/scene/2d/navigation2d.cpp b/scene/2d/navigation2d.cpp
new file mode 100644
index 0000000000..46af68444a
--- /dev/null
+++ b/scene/2d/navigation2d.cpp
@@ -0,0 +1,660 @@
+#include "navigation2d.h"
+
+#define USE_ENTRY_POINT
+
+void Navigation2D::_navpoly_link(int p_id) {
+
+ ERR_FAIL_COND(!navpoly_map.has(p_id));
+ NavMesh &nm=navpoly_map[p_id];
+ ERR_FAIL_COND(nm.linked);
+
+ print_line("LINK");
+
+ DVector<Vector2> vertices=nm.navpoly->get_vertices();
+ int len = vertices.size();
+ if (len==0)
+ return;
+
+ DVector<Vector2>::Read r=vertices.read();
+
+ for(int i=0;i<nm.navpoly->get_polygon_count();i++) {
+
+ //build
+
+ List<Polygon>::Element *P=nm.polygons.push_back(Polygon());
+ Polygon &p=P->get();
+ p.owner=&nm;
+
+ Vector<int> poly = nm.navpoly->get_polygon(i);
+ int plen=poly.size();
+ const int *indices=poly.ptr();
+ bool valid=true;
+ p.edges.resize(plen);
+
+ Vector2 center;
+
+ for(int j=0;j<plen;j++) {
+
+ int idx = indices[j];
+ if (idx<0 || idx>=len) {
+ valid=false;
+ break;
+ }
+
+ Polygon::Edge e;
+ Vector2 ep=nm.xform.xform(r[idx]);
+ center+=ep;
+ e.point=_get_point(ep);
+ p.edges[j]=e;
+ }
+
+ if (!valid) {
+ nm.polygons.pop_back();
+ ERR_CONTINUE(!valid);
+ continue;
+ }
+
+ p.center=center/plen;
+
+ //connect
+
+ for(int j=0;j<plen;j++) {
+
+ int next = (j+1)%plen;
+ EdgeKey ek(p.edges[j].point,p.edges[next].point);
+
+ Map<EdgeKey,Connection>::Element *C=connections.find(ek);
+ if (!C) {
+
+ Connection c;
+ c.A=&p;
+ c.A_edge=j;
+ c.B=NULL;
+ c.B_edge=-1;
+ connections[ek]=c;
+ } else {
+
+ if (C->get().B!=NULL) {
+ print_line(String()+_get_vertex(ek.a)+" -> "+_get_vertex(ek.b));
+ }
+ ERR_CONTINUE(C->get().B!=NULL); //wut
+
+ C->get().B=&p;
+ C->get().B_edge=j;
+ C->get().A->edges[C->get().A_edge].C=&p;
+ C->get().A->edges[C->get().A_edge].C_edge=j;;
+ p.edges[j].C=C->get().A;
+ p.edges[j].C_edge=C->get().A_edge;
+ //connection successful.
+ }
+ }
+ }
+
+ nm.linked=true;
+
+}
+
+
+void Navigation2D::_navpoly_unlink(int p_id) {
+
+ ERR_FAIL_COND(!navpoly_map.has(p_id));
+ NavMesh &nm=navpoly_map[p_id];
+ ERR_FAIL_COND(!nm.linked);
+
+ print_line("UNLINK");
+
+ for (List<Polygon>::Element *E=nm.polygons.front();E;E=E->next()) {
+
+
+ Polygon &p=E->get();
+
+ int ec = p.edges.size();
+ Polygon::Edge *edges=p.edges.ptr();
+
+ for(int i=0;i<ec;i++) {
+ int next = (i+1)%ec;
+
+ EdgeKey ek(edges[i].point,edges[next].point);
+ Map<EdgeKey,Connection>::Element *C=connections.find(ek);
+ ERR_CONTINUE(!C);
+ if (C->get().B) {
+ //disconnect
+
+ C->get().B->edges[C->get().B_edge].C=NULL;
+ C->get().B->edges[C->get().B_edge].C_edge=-1;
+ C->get().A->edges[C->get().A_edge].C=NULL;
+ C->get().A->edges[C->get().A_edge].C_edge=-1;
+
+ if (C->get().A==&E->get()) {
+
+ C->get().A=C->get().B;
+ C->get().A_edge=C->get().B_edge;
+ }
+ C->get().B=NULL;
+ C->get().B_edge=-1;
+
+ } else {
+ connections.erase(C);
+ //erase
+ }
+ }
+ }
+
+ nm.polygons.clear();
+
+ nm.linked=false;
+
+
+}
+
+
+int Navigation2D::navpoly_create(const Ref<NavigationPolygon>& p_mesh, const Matrix32& p_xform, Object *p_owner) {
+
+ int id = last_id++;
+ NavMesh nm;
+ nm.linked=false;
+ nm.navpoly=p_mesh;
+ nm.xform=p_xform;
+ nm.owner=p_owner;
+ navpoly_map[id]=nm;
+
+ _navpoly_link(id);
+
+ return id;
+}
+
+void Navigation2D::navpoly_set_transform(int p_id, const Matrix32& p_xform){
+
+ ERR_FAIL_COND(!navpoly_map.has(p_id));
+ NavMesh &nm=navpoly_map[p_id];
+ if (nm.xform==p_xform)
+ return; //bleh
+ _navpoly_unlink(p_id);
+ nm.xform=p_xform;
+ _navpoly_link(p_id);
+
+
+
+}
+void Navigation2D::navpoly_remove(int p_id){
+
+ ERR_FAIL_COND(!navpoly_map.has(p_id));
+ _navpoly_unlink(p_id);
+ navpoly_map.erase(p_id);
+
+}
+#if 0
+void Navigation2D::_clip_path(Vector<Vector2>& path, Polygon *from_poly, const Vector2& p_to_point, Polygon* p_to_poly) {
+
+ Vector2 from = path[path.size()-1];
+
+ if (from.distance_to(p_to_point)<CMP_EPSILON)
+ return;
+ Plane cut_plane;
+ cut_plane.normal = (from-p_to_point).cross(up);
+ if (cut_plane.normal==Vector2())
+ return;
+ cut_plane.normal.normalize();
+ cut_plane.d = cut_plane.normal.dot(from);
+
+
+ while(from_poly!=p_to_poly) {
+
+ int pe = from_poly->prev_edge;
+ Vector2 a = _get_vertex(from_poly->edges[pe].point);
+ Vector2 b = _get_vertex(from_poly->edges[(pe+1)%from_poly->edges.size()].point);
+
+ from_poly=from_poly->edges[pe].C;
+ ERR_FAIL_COND(!from_poly);
+
+ if (a.distance_to(b)>CMP_EPSILON) {
+
+ Vector2 inters;
+ if (cut_plane.intersects_segment(a,b,&inters)) {
+ if (inters.distance_to(p_to_point)>CMP_EPSILON && inters.distance_to(path[path.size()-1])>CMP_EPSILON) {
+ path.push_back(inters);
+ }
+ }
+ }
+ }
+}
+#endif
+
+Vector<Vector2> Navigation2D::get_simple_path(const Vector2& p_start, const Vector2& p_end, bool p_optimize) {
+
+
+ Polygon *begin_poly=NULL;
+ Polygon *end_poly=NULL;
+ Vector2 begin_point;
+ Vector2 end_point;
+ float begin_d=1e20;
+ float end_d=1e20;
+
+ //look for point inside triangle
+
+ for (Map<int,NavMesh>::Element*E=navpoly_map.front();E;E=E->next()) {
+
+ if (!E->get().linked)
+ continue;
+ for(List<Polygon>::Element *F=E->get().polygons.front();F;F=F->next()) {
+
+
+ Polygon &p=F->get();
+ if (begin_d || end_d) {
+ for(int i=2;i<p.edges.size();i++) {
+
+ if (begin_d>0) {
+
+ if (Geometry::is_point_in_triangle(p_start,_get_vertex(p.edges[0].point),_get_vertex(p.edges[i-1].point),_get_vertex(p.edges[i].point))) {
+
+ begin_poly=&p;
+ begin_point=p_start;
+ begin_d=0;
+ if (end_d==0)
+ break;
+
+ }
+ }
+
+ if (end_d>0) {
+
+ if (Geometry::is_point_in_triangle(p_end,_get_vertex(p.edges[0].point),_get_vertex(p.edges[i-1].point),_get_vertex(p.edges[i].point))) {
+
+ end_poly=&p;
+ end_point=p_end;
+ end_d=0;
+ if (begin_d==0)
+ break;
+ }
+ }
+
+ }
+ }
+
+ p.prev_edge=-1;
+ }
+ }
+
+ //start or end not inside triangle.. look for closest segment :|
+ if (begin_d || end_d) {
+ for (Map<int,NavMesh>::Element*E=navpoly_map.front();E;E=E->next()) {
+
+ if (!E->get().linked)
+ continue;
+ for(List<Polygon>::Element *F=E->get().polygons.front();F;F=F->next()) {
+
+ Polygon &p=F->get();
+ int es = p.edges.size();
+ for(int i=0;i<es;i++) {
+
+ Vector2 edge[2]={
+ _get_vertex(p.edges[i].point),
+ _get_vertex(p.edges[(i+1)%es].point)
+ };
+
+
+ if (begin_d>0) {
+ Vector2 spoint=Geometry::get_closest_point_to_segment_2d(p_start,edge);
+ float d = spoint.distance_to(p_start);
+ if (d<begin_d) {
+ begin_poly=&p;
+ begin_point=spoint;
+ begin_d=d;
+ }
+ }
+
+ if (end_d>0) {
+ Vector2 spoint=Geometry::get_closest_point_to_segment_2d(p_end,edge);
+ float d = spoint.distance_to(p_end);
+ if (d<end_d) {
+ end_poly=&p;
+ end_point=spoint;
+ end_d=d;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (!begin_poly || !end_poly) {
+
+ //print_line("No Path Path");
+ return Vector<Vector2>(); //no path
+ }
+
+ if (begin_poly==end_poly) {
+
+ Vector<Vector2> path;
+ path.resize(2);
+ path[0]=begin_point;
+ path[1]=end_point;
+ //print_line("Direct Path");
+ return path;
+ }
+
+
+ bool found_route=false;
+
+ List<Polygon*> open_list;
+
+ begin_poly->entry=p_start;
+
+ for(int i=0;i<begin_poly->edges.size();i++) {
+
+ if (begin_poly->edges[i].C) {
+
+ begin_poly->edges[i].C->prev_edge=begin_poly->edges[i].C_edge;
+#ifdef USE_ENTRY_POINT
+ Vector2 edge[2]={
+ _get_vertex(begin_poly->edges[i].point),
+ _get_vertex(begin_poly->edges[(i+1)%begin_poly->edges.size()].point)
+ };
+
+ Vector2 entry = Geometry::get_closest_point_to_segment_2d(begin_poly->entry,edge);
+ begin_poly->edges[i].C->distance = begin_poly->entry.distance_to(entry);
+ begin_poly->edges[i].C->entry=entry;
+#else
+ begin_poly->edges[i].C->distance=begin_poly->center.distance_to(begin_poly->edges[i].C->center);
+#endif
+ open_list.push_back(begin_poly->edges[i].C);
+
+ if (begin_poly->edges[i].C==end_poly) {
+ found_route=true;
+ }
+ }
+ }
+
+
+ while(!found_route) {
+
+ if (open_list.size()==0) {
+ // print_line("NOU OPEN LIST");
+ break;
+ }
+ //check open list
+
+ List<Polygon*>::Element *least_cost_poly=NULL;
+ float least_cost=1e30;
+
+ //this could be faster (cache previous results)
+ for (List<Polygon*>::Element *E=open_list.front();E;E=E->next()) {
+
+ Polygon *p=E->get();
+
+
+ float cost=p->distance;
+ cost+=p->center.distance_to(end_point);
+
+ if (cost<least_cost) {
+
+ least_cost_poly=E;
+ least_cost=cost;
+ }
+ }
+
+
+ Polygon *p=least_cost_poly->get();
+ //open the neighbours for search
+ int es = p->edges.size();
+
+ for(int i=0;i<es;i++) {
+
+
+ Polygon::Edge &e=p->edges[i];
+
+ if (!e.C)
+ continue;
+
+#ifdef USE_ENTRY_POINT
+ Vector2 edge[2]={
+ _get_vertex(p->edges[i].point),
+ _get_vertex(p->edges[(i+1)%es].point)
+ };
+
+ Vector2 edge_entry = Geometry::get_closest_point_to_segment_2d(p->entry,edge);
+ float distance = p->entry.distance_to(edge_entry) + p->distance;
+
+#else
+
+ float distance = p->center.distance_to(e.C->center) + p->distance;
+
+#endif
+
+
+ if (e.C->prev_edge!=-1) {
+ //oh this was visited already, can we win the cost?
+
+ if (e.C->distance>distance) {
+
+ e.C->prev_edge=e.C_edge;
+ e.C->distance=distance;
+#ifdef USE_ENTRY_POINT
+ e.C->entry=edge_entry;
+#endif
+ }
+ } else {
+ //add to open neighbours
+
+ e.C->prev_edge=e.C_edge;
+ e.C->distance=distance;
+#ifdef USE_ENTRY_POINT
+ e.C->entry=edge_entry;
+#endif
+
+ open_list.push_back(e.C);
+
+ if (e.C==end_poly) {
+ //oh my reached end! stop algorithm
+ found_route=true;
+ break;
+
+ }
+
+ }
+ }
+
+ if (found_route)
+ break;
+
+ open_list.erase(least_cost_poly);
+ }
+
+ if (found_route) {
+
+ Vector<Vector2> path;
+
+ if (p_optimize) {
+ //string pulling
+
+ Polygon *apex_poly=end_poly;
+ Vector2 apex_point=end_point;
+ Vector2 portal_left=apex_point;
+ Vector2 portal_right=apex_point;
+ Polygon *left_poly=end_poly;
+ Polygon *right_poly=end_poly;
+ Polygon *p=end_poly;
+ path.push_back(end_point);
+
+ while(p) {
+
+ Vector2 left;
+ Vector2 right;
+
+//#define CLOCK_TANGENT(m_a,m_b,m_c) ( ((m_a)-(m_c)).cross((m_a)-(m_b)) )
+#define CLOCK_TANGENT(m_a,m_b,m_c) ((((m_a).x - (m_c).x) * ((m_b).y - (m_c).y) - ((m_b).x - (m_c).x) * ((m_a).y - (m_c).y)))
+
+ if (p==begin_poly) {
+ left=begin_point;
+ right=begin_point;
+ } else {
+ int prev = p->prev_edge;
+ int prev_n = (p->prev_edge+1)%p->edges.size();
+ left = _get_vertex(p->edges[prev].point);
+ right = _get_vertex(p->edges[prev_n].point);
+
+ if (CLOCK_TANGENT(apex_point,left,(left+right)*0.5) < 0){
+ SWAP(left,right);
+ }
+ }
+
+ bool skip=false;
+
+
+ if (CLOCK_TANGENT(apex_point,portal_left,left) >= 0){
+ //process
+ if (portal_left==apex_point || CLOCK_TANGENT(apex_point,left,portal_right) > 0) {
+ left_poly=p;
+ portal_left=left;
+ } else {
+
+ //_clip_path(path,apex_poly,portal_right,right_poly);
+
+ apex_point=portal_right;
+ p=right_poly;
+ left_poly=p;
+ apex_poly=p;
+ portal_left=apex_point;
+ portal_right=apex_point;
+ path.push_back(apex_point);
+ skip=true;
+ }
+ }
+
+ if (!skip && CLOCK_TANGENT(apex_point,portal_right,right) <= 0){
+ //process
+ if (portal_right==apex_point || CLOCK_TANGENT(apex_point,right,portal_left) < 0) {
+ right_poly=p;
+ portal_right=right;
+ } else {
+
+ //_clip_path(path,apex_poly,portal_left,left_poly);
+
+ apex_point=portal_left;
+ p=left_poly;
+ right_poly=p;
+ apex_poly=p;
+ portal_right=apex_point;
+ portal_left=apex_point;
+ path.push_back(apex_point);
+ }
+ }
+
+ if (p!=begin_poly)
+ p=p->edges[p->prev_edge].C;
+ else
+ p=NULL;
+
+ }
+
+ if (path[path.size()-1]!=begin_point)
+ path.push_back(begin_point);
+
+ path.invert();
+
+
+
+
+ } else {
+ //midpoints
+ Polygon *p=end_poly;
+
+ path.push_back(end_point);
+ while(true) {
+ int prev = p->prev_edge;
+ int prev_n = (p->prev_edge+1)%p->edges.size();
+ Vector2 point = (_get_vertex(p->edges[prev].point) + _get_vertex(p->edges[prev_n].point))*0.5;
+ path.push_back(point);
+ p = p->edges[prev].C;
+ if (p==begin_poly)
+ break;
+ }
+
+ path.push_back(begin_point);
+
+
+ path.invert();;
+ }
+
+ return path;
+ }
+
+
+ return Vector<Vector2>();
+
+}
+
+
+Vector2 Navigation2D::get_closest_point(const Vector2& p_point) {
+
+ Vector2 closest_point=Vector2();
+ float closest_point_d=1e20;
+
+ for (Map<int,NavMesh>::Element*E=navpoly_map.front();E;E=E->next()) {
+
+ if (!E->get().linked)
+ continue;
+ for(List<Polygon>::Element *F=E->get().polygons.front();F;F=F->next()) {
+
+ Polygon &p=F->get();
+ for(int i=2;i<p.edges.size();i++) {
+
+ if (Geometry::is_point_in_triangle(p_point,_get_vertex(p.edges[0].point),_get_vertex(p.edges[i-1].point),_get_vertex(p.edges[i].point))) {
+
+ return p_point; //inside triangle, nothing else to discuss
+ }
+
+ }
+ }
+ }
+
+ for (Map<int,NavMesh>::Element*E=navpoly_map.front();E;E=E->next()) {
+
+ if (!E->get().linked)
+ continue;
+ for(List<Polygon>::Element *F=E->get().polygons.front();F;F=F->next()) {
+
+ Polygon &p=F->get();
+ int es = p.edges.size();
+ for(int i=0;i<es;i++) {
+
+ Vector2 edge[2]={
+ _get_vertex(p.edges[i].point),
+ _get_vertex(p.edges[(i+1)%es].point)
+ };
+
+
+ Vector2 spoint=Geometry::get_closest_point_to_segment_2d(p_point,edge);
+ float d = spoint.distance_squared_to(p_point);
+ if (d<closest_point_d) {
+
+ closest_point=spoint;
+ closest_point_d=d;
+ }
+ }
+ }
+ }
+
+ return closest_point;
+
+}
+
+
+void Navigation2D::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("navpoly_create","mesh:NavigationPolygon","xform","owner"),&Navigation2D::navpoly_create,DEFVAL(Variant()));
+ ObjectTypeDB::bind_method(_MD("navpoly_set_transform","id","xform"),&Navigation2D::navpoly_set_transform);
+ ObjectTypeDB::bind_method(_MD("navpoly_remove","id"),&Navigation2D::navpoly_remove);
+
+ ObjectTypeDB::bind_method(_MD("get_simple_path","start","end","optimize"),&Navigation2D::get_simple_path,DEFVAL(true));
+ ObjectTypeDB::bind_method(_MD("get_closest_point","to_point"),&Navigation2D::get_closest_point);
+
+}
+
+Navigation2D::Navigation2D() {
+
+ ERR_FAIL_COND( sizeof(Point)!=8 );
+ cell_size=1; // one pixel
+ last_id=1;
+
+}
diff --git a/scene/2d/navigation2d.h b/scene/2d/navigation2d.h
new file mode 100644
index 0000000000..7ff01bb442
--- /dev/null
+++ b/scene/2d/navigation2d.h
@@ -0,0 +1,138 @@
+#ifndef NAVIGATION_2D_H
+#define NAVIGATION_2D_H
+
+#include "scene/2d/node_2d.h"
+#include "scene/2d/navigation_polygon.h"
+
+class Navigation2D : public Node2D {
+
+ OBJ_TYPE( Navigation2D, Node2D);
+
+
+ union Point {
+
+ struct {
+ int64_t x:32;
+ int64_t y:32;
+ };
+
+ uint64_t key;
+ bool operator<(const Point& p_key) const { return key < p_key.key; }
+ };
+
+
+ struct EdgeKey {
+
+ Point a;
+ Point b;
+
+ bool operator<(const EdgeKey& p_key) const {
+ return (a.key==p_key.a.key)?(b.key<p_key.b.key):(a.key<p_key.a.key);
+ };
+
+ EdgeKey(const Point& p_a=Point(),const Point& p_b=Point()) {
+ a=p_a;
+ b=p_b;
+ if (a.key > b.key) {
+ SWAP(a,b);
+ }
+ }
+ };
+
+
+ struct NavMesh;
+
+
+ struct Polygon {
+
+ struct Edge {
+ Point point;
+ Polygon *C; //connection
+ int C_edge;
+ Edge() { C=NULL; C_edge=-1; }
+ };
+
+ Vector<Edge> edges;
+
+ Vector2 center;
+ Vector2 entry;
+
+ float distance;
+ int prev_edge;
+
+ NavMesh *owner;
+ };
+
+
+ struct Connection {
+
+ Polygon *A;
+ int A_edge;
+ Polygon *B;
+ int B_edge;
+ Connection() { A=NULL; B=NULL; A_edge=-1; B_edge=-1;}
+ };
+
+ Map<EdgeKey,Connection> connections;
+
+
+ struct NavMesh {
+
+ Object *owner;
+ Matrix32 xform;
+ bool linked;
+ Ref<NavigationPolygon> navpoly;
+ List<Polygon> polygons;
+
+ };
+
+
+
+ _FORCE_INLINE_ Point _get_point(const Vector2& p_pos) const {
+
+ int x = int(Math::floor(p_pos.x/cell_size));
+ int y = int(Math::floor(p_pos.y/cell_size));
+
+ Point p;
+ p.key=0;
+ p.x=x;
+ p.y=y;
+ return p;
+
+ }
+
+ _FORCE_INLINE_ Vector2 _get_vertex(const Point& p_point) const {
+
+ return Vector2(p_point.x,p_point.y)*cell_size;
+ }
+
+
+
+ void _navpoly_link(int p_id);
+ void _navpoly_unlink(int p_id);
+
+ float cell_size;
+ Map<int,NavMesh> navpoly_map;
+ int last_id;
+#if 0
+ void _clip_path(Vector<Vector2>& path,Polygon *from_poly, const Vector2& p_to_point, Polygon* p_to_poly);
+#endif
+protected:
+
+ static void _bind_methods();
+
+public:
+
+ //API should be as dynamic as possible
+ int navpoly_create(const Ref<NavigationPolygon>& p_mesh,const Matrix32& p_xform,Object* p_owner=NULL);
+ void navpoly_set_transform(int p_id, const Matrix32& p_xform);
+ void navpoly_remove(int p_id);
+
+ Vector<Vector2> get_simple_path(const Vector2& p_start, const Vector2& p_end,bool p_optimize=true);
+ Vector2 get_closest_point(const Vector2& p_point);
+
+ Navigation2D();
+};
+
+
+#endif // Navigation2D2D_H
diff --git a/scene/2d/navigation_polygon.cpp b/scene/2d/navigation_polygon.cpp
new file mode 100644
index 0000000000..fc69ea8a0d
--- /dev/null
+++ b/scene/2d/navigation_polygon.cpp
@@ -0,0 +1,450 @@
+#include "navigation_polygon.h"
+#include "navigation2d.h"
+#include "triangulator.h"
+#include "core_string_names.h"
+
+void NavigationPolygon::set_vertices(const DVector<Vector2>& p_vertices) {
+
+ vertices=p_vertices;
+}
+
+DVector<Vector2> NavigationPolygon::get_vertices() const{
+
+ return vertices;
+}
+
+
+void NavigationPolygon::_set_polygons(const Array& p_array) {
+
+ polygons.resize(p_array.size());
+ for(int i=0;i<p_array.size();i++) {
+ polygons[i].indices=p_array[i];
+ }
+}
+
+Array NavigationPolygon::_get_polygons() const {
+
+ Array ret;
+ ret.resize(polygons.size());
+ for(int i=0;i<ret.size();i++) {
+ ret[i]=polygons[i].indices;
+ }
+
+ return ret;
+}
+
+void NavigationPolygon::_set_outlines(const Array& p_array) {
+
+ outlines.resize(p_array.size());
+ for(int i=0;i<p_array.size();i++) {
+ outlines[i]=p_array[i];
+ }
+}
+
+Array NavigationPolygon::_get_outlines() const {
+
+ Array ret;
+ ret.resize(outlines.size());
+ for(int i=0;i<ret.size();i++) {
+ ret[i]=outlines[i];
+ }
+
+ return ret;
+}
+
+
+void NavigationPolygon::add_polygon(const Vector<int>& p_polygon){
+
+ Polygon polygon;
+ polygon.indices=p_polygon;
+ polygons.push_back(polygon);
+
+}
+
+void NavigationPolygon::add_outline_at_index(const DVector<Vector2>& p_outline,int p_index) {
+
+ outlines.insert(p_index,p_outline);
+}
+
+int NavigationPolygon::get_polygon_count() const{
+
+ return polygons.size();
+}
+Vector<int> NavigationPolygon::get_polygon(int p_idx){
+
+ ERR_FAIL_INDEX_V(p_idx,polygons.size(),Vector<int>());
+ return polygons[p_idx].indices;
+}
+void NavigationPolygon::clear_polygons(){
+
+ polygons.clear();
+}
+
+void NavigationPolygon::add_outline(const DVector<Vector2>& p_outline) {
+
+ outlines.push_back(p_outline);
+}
+
+int NavigationPolygon::get_outline_count() const{
+
+ return outlines.size();
+}
+
+void NavigationPolygon::set_outline(int p_idx,const DVector<Vector2>& p_outline) {
+ ERR_FAIL_INDEX(p_idx,outlines.size());
+ outlines[p_idx]=p_outline;
+}
+
+void NavigationPolygon::remove_outline(int p_idx) {
+
+ ERR_FAIL_INDEX(p_idx,outlines.size());
+ outlines.remove(p_idx);
+
+}
+
+DVector<Vector2> NavigationPolygon::get_outline(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx,outlines.size(),DVector<Vector2>());
+ return outlines[p_idx];
+}
+
+void NavigationPolygon::clear_outlines(){
+
+ outlines.clear();;
+}
+void NavigationPolygon::make_polygons_from_outlines(){
+
+ List<TriangulatorPoly> in_poly,out_poly;
+
+ Vector2 outside_point(-1e10,-1e10);
+
+ for(int i=0;i<outlines.size();i++) {
+
+ DVector<Vector2> ol = outlines[i];
+ int olsize = ol.size();
+ if (olsize<3)
+ continue;
+ DVector<Vector2>::Read r=ol.read();
+ for(int j=0;j<olsize;j++) {
+ outside_point.x = MAX( r[j].x, outside_point.x );
+ outside_point.y = MAX( r[j].y, outside_point.y );
+ }
+
+ }
+
+ outside_point+=Vector2(0.7239784,0.819238); //avoid precision issues
+
+
+
+ for(int i=0;i<outlines.size();i++) {
+
+ DVector<Vector2> ol = outlines[i];
+ int olsize = ol.size();
+ if (olsize<3)
+ continue;
+ DVector<Vector2>::Read r=ol.read();
+
+ int interscount=0;
+ //test if this is an outer outline
+ for(int k=0;k<outlines.size();k++) {
+
+ if (i==k)
+ continue; //no self intersect
+
+ DVector<Vector2> ol2 = outlines[k];
+ int olsize2 = ol2.size();
+ if (olsize2<3)
+ continue;
+ DVector<Vector2>::Read r2=ol2.read();
+
+ for(int l=0;l<olsize2;l++) {
+
+ if (Geometry::segment_intersects_segment_2d(r[0],outside_point,r2[l],r2[(l+1)%olsize2],NULL)) {
+ interscount++;
+ }
+ }
+
+ }
+
+ bool outer = (interscount%2)==0;
+
+ TriangulatorPoly tp;
+ tp.Init(olsize);
+ for(int j=0;j<olsize;j++) {
+ tp[j]=r[j];
+ }
+
+ if (outer)
+ tp.SetOrientation(TRIANGULATOR_CCW);
+ else {
+ tp.SetOrientation(TRIANGULATOR_CW);
+ tp.SetHole(true);
+ }
+
+ in_poly.push_back(tp);
+ }
+
+
+ TriangulatorPartition tpart;
+ if (tpart.ConvexPartition_HM(&in_poly,&out_poly)==0) { //failed!
+ print_line("convex partition failed!");
+ return;
+ }
+
+ polygons.clear();
+ vertices.resize(0);
+
+ Map<Vector2,int> points;
+ for(List<TriangulatorPoly>::Element*I = out_poly.front();I;I=I->next()) {
+
+ TriangulatorPoly& tp = I->get();
+
+ struct Polygon p;
+
+ for(int i=0;i<tp.GetNumPoints();i++) {
+
+ Map<Vector2,int>::Element *E=points.find(tp[i]);
+ if (!E) {
+ E=points.insert(tp[i],vertices.size());
+ vertices.push_back(tp[i]);
+ }
+ p.indices.push_back(E->get());
+ }
+
+ polygons.push_back(p);
+ }
+
+ emit_signal(CoreStringNames::get_singleton()->changed);
+}
+
+
+void NavigationPolygon::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_vertices","vertices"),&NavigationPolygon::set_vertices);
+ ObjectTypeDB::bind_method(_MD("get_vertices"),&NavigationPolygon::get_vertices);
+
+ ObjectTypeDB::bind_method(_MD("add_polygon","polygon"),&NavigationPolygon::add_polygon);
+ ObjectTypeDB::bind_method(_MD("get_polygon_count"),&NavigationPolygon::get_polygon_count);
+ ObjectTypeDB::bind_method(_MD("get_polygon","idx"),&NavigationPolygon::get_polygon);
+ ObjectTypeDB::bind_method(_MD("clear_polygons"),&NavigationPolygon::clear_polygons);
+
+ ObjectTypeDB::bind_method(_MD("add_outline","outline"),&NavigationPolygon::add_outline);
+ ObjectTypeDB::bind_method(_MD("add_outline_at_index","outline","index"),&NavigationPolygon::add_outline_at_index);
+ ObjectTypeDB::bind_method(_MD("get_outline_count"),&NavigationPolygon::get_outline_count);
+ ObjectTypeDB::bind_method(_MD("set_outline","idx","outline"),&NavigationPolygon::set_outline);
+ ObjectTypeDB::bind_method(_MD("get_outline","idx"),&NavigationPolygon::get_outline);
+ ObjectTypeDB::bind_method(_MD("remove_outline","idx"),&NavigationPolygon::remove_outline);
+ ObjectTypeDB::bind_method(_MD("clear_outlines"),&NavigationPolygon::clear_outlines);
+ ObjectTypeDB::bind_method(_MD("make_polygons_from_outlines"),&NavigationPolygon::make_polygons_from_outlines);
+
+ ObjectTypeDB::bind_method(_MD("_set_polygons","polygons"),&NavigationPolygon::_set_polygons);
+ ObjectTypeDB::bind_method(_MD("_get_polygons"),&NavigationPolygon::_get_polygons);
+
+ ObjectTypeDB::bind_method(_MD("_set_outlines","outlines"),&NavigationPolygon::_set_outlines);
+ ObjectTypeDB::bind_method(_MD("_get_outlines"),&NavigationPolygon::_get_outlines);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3_ARRAY,"vertices",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR),_SCS("set_vertices"),_SCS("get_vertices"));
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY,"polygons",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR),_SCS("_set_polygons"),_SCS("_get_polygons"));
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY,"outlines",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR),_SCS("_set_outlines"),_SCS("_get_outlines"));
+}
+
+NavigationPolygon::NavigationPolygon() {
+
+
+}
+
+void NavigationPolygonInstance::set_enabled(bool p_enabled) {
+
+ if (enabled==p_enabled)
+ return;
+ enabled=p_enabled;
+
+ if (!is_inside_tree())
+ return;
+
+ if (!enabled) {
+
+ if (nav_id!=-1) {
+ navigation->navpoly_remove(nav_id);
+ nav_id=-1;
+ }
+ } else {
+
+ if (navigation) {
+
+ if (navpoly.is_valid()) {
+
+ nav_id = navigation->navpoly_create(navpoly,get_relative_transform(navigation),this);
+ }
+ }
+
+ }
+
+ if (get_tree()->is_editor_hint())
+ update();
+
+// update_gizmo();
+}
+
+bool NavigationPolygonInstance::is_enabled() const {
+
+
+ return enabled;
+}
+
+
+/////////////////////////////
+
+
+void NavigationPolygonInstance::_notification(int p_what) {
+
+
+ switch(p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+
+ Node2D *c=this;
+ while(c) {
+
+ navigation=c->cast_to<Navigation2D>();
+ if (navigation) {
+
+ if (enabled && navpoly.is_valid()) {
+
+ nav_id = navigation->navpoly_create(navpoly,get_relative_transform(navigation),this);
+ }
+ break;
+ }
+
+ c=c->get_parent()->cast_to<Node2D>();
+ }
+
+ } break;
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+
+ if (navigation && nav_id!=-1) {
+ navigation->navpoly_set_transform(nav_id,get_relative_transform(navigation));
+ }
+
+ } break;
+ case NOTIFICATION_EXIT_TREE: {
+
+ if (navigation) {
+
+ if (nav_id!=-1) {
+ navigation->navpoly_remove(nav_id);
+ nav_id=-1;
+ }
+ }
+ navigation=NULL;
+ } break;
+ case NOTIFICATION_DRAW: {
+
+ if (is_inside_tree() && get_tree()->is_editor_hint() && navpoly.is_valid()) {
+
+ DVector<Vector2> verts=navpoly->get_vertices();
+ int vsize = verts.size();
+ if (vsize<3)
+ return;
+
+
+ Color color;
+ if (enabled) {
+ color=Color(0.1,0.8,1.0,0.4);
+ } else {
+ color=Color(1.0,0.8,0.1,0.4);
+ }
+ Vector<Color> colors;
+ Vector<Vector2> vertices;
+ vertices.resize(vsize);
+ colors.resize(vsize);
+ {
+ DVector<Vector2>::Read vr = verts.read();
+ for(int i=0;i<vsize;i++) {
+ vertices[i]=vr[i];
+ colors[i]=color;
+ }
+ }
+
+ Vector<int> indices;
+
+
+ for(int i=0;i<navpoly->get_polygon_count();i++) {
+ Vector<int> polygon = navpoly->get_polygon(i);
+
+ for(int j=2;j<polygon.size();j++) {
+
+ int kofs[3]={0,j-1,j};
+ for(int k=0;k<3;k++) {
+
+ int idx = polygon[ kofs[k] ];
+ ERR_FAIL_INDEX(idx,vsize);
+ indices.push_back(idx);
+ }
+ }
+ }
+ VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(),indices,vertices,colors);
+
+ }
+ } break;
+
+ }
+}
+
+
+void NavigationPolygonInstance::set_navigation_polygon(const Ref<NavigationPolygon>& p_navpoly) {
+
+ if (p_navpoly==navpoly)
+ return;
+
+ if (navigation && nav_id!=-1) {
+ navigation->navpoly_remove(nav_id);
+ nav_id=-1;
+ }
+ if (navpoly.is_valid()) {
+ navpoly->disconnect(CoreStringNames::get_singleton()->changed,this,"_navpoly_changed");
+ }
+ navpoly=p_navpoly;
+
+ if (navpoly.is_valid()) {
+ navpoly->connect(CoreStringNames::get_singleton()->changed,this,"_navpoly_changed");
+ }
+
+ if (navigation && navpoly.is_valid() && enabled) {
+ nav_id = navigation->navpoly_create(navpoly,get_relative_transform(navigation),this);
+ }
+ //update_gizmo();
+ _change_notify("navpoly");
+
+}
+
+Ref<NavigationPolygon> NavigationPolygonInstance::get_navigation_polygon() const{
+
+ return navpoly;
+}
+
+void NavigationPolygonInstance::_navpoly_changed() {
+
+ if (is_inside_tree() && get_tree()->is_editor_hint())
+ update();
+}
+
+void NavigationPolygonInstance::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_navigation_polygon","navpoly"),&NavigationPolygonInstance::set_navigation_polygon);
+ ObjectTypeDB::bind_method(_MD("get_navigation_polygon"),&NavigationPolygonInstance::get_navigation_polygon);
+
+ ObjectTypeDB::bind_method(_MD("set_enabled","enabled"),&NavigationPolygonInstance::set_enabled);
+ ObjectTypeDB::bind_method(_MD("is_enabled"),&NavigationPolygonInstance::is_enabled);
+
+ ObjectTypeDB::bind_method(_MD("_navpoly_changed"),&NavigationPolygonInstance::_navpoly_changed);
+
+ ADD_PROPERTY( PropertyInfo(Variant::OBJECT,"navpoly",PROPERTY_HINT_RESOURCE_TYPE,"NavigationPolygon"),_SCS("set_navigation_polygon"),_SCS("get_navigation_polygon"));
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"enabled"),_SCS("set_enabled"),_SCS("is_enabled"));
+}
+
+NavigationPolygonInstance::NavigationPolygonInstance() {
+
+ navigation=NULL;
+ nav_id=-1;
+ enabled=true;
+
+}
diff --git a/scene/2d/navigation_polygon.h b/scene/2d/navigation_polygon.h
new file mode 100644
index 0000000000..01307a170b
--- /dev/null
+++ b/scene/2d/navigation_polygon.h
@@ -0,0 +1,84 @@
+#ifndef NAVIGATION_POLYGON_H
+#define NAVIGATION_POLYGON_H
+
+#include "scene/2d/node_2d.h"
+
+
+class NavigationPolygon : public Resource {
+
+ OBJ_TYPE( NavigationPolygon, Resource );
+
+ DVector<Vector2> vertices;
+ struct Polygon {
+ Vector<int> indices;
+ };
+ Vector<Polygon> polygons;
+ Vector< DVector<Vector2> > outlines;
+
+protected:
+
+ static void _bind_methods();
+
+ void _set_polygons(const Array& p_array);
+ Array _get_polygons() const;
+
+ void _set_outlines(const Array& p_array);
+ Array _get_outlines() const;
+
+public:
+
+
+
+ void set_vertices(const DVector<Vector2>& p_vertices);
+ DVector<Vector2> get_vertices() const;
+
+ void add_polygon(const Vector<int>& p_polygon);
+ int get_polygon_count() const;
+
+ void add_outline(const DVector<Vector2>& p_outline);
+ void add_outline_at_index(const DVector<Vector2>& p_outline,int p_index);
+ void set_outline(int p_idx,const DVector<Vector2>& p_outline);
+ DVector<Vector2> get_outline(int p_idx) const;
+ void remove_outline(int p_idx);
+ int get_outline_count() const;
+
+ void clear_outlines();
+ void make_polygons_from_outlines();
+
+ Vector<int> get_polygon(int p_idx);
+ void clear_polygons();
+
+ NavigationPolygon();
+};
+
+
+class Navigation2D;
+
+class NavigationPolygonInstance : public Node2D {
+
+ OBJ_TYPE(NavigationPolygonInstance,Node2D);
+
+ bool enabled;
+ int nav_id;
+ Navigation2D *navigation;
+ Ref<NavigationPolygon> navpoly;
+
+ void _navpoly_changed();
+
+protected:
+
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+ void set_enabled(bool p_enabled);
+ bool is_enabled() const;
+
+ void set_navigation_polygon(const Ref<NavigationPolygon>& p_navpoly);
+ Ref<NavigationPolygon> get_navigation_polygon() const;
+
+ NavigationPolygonInstance();
+};
+
+
+#endif // NAVIGATIONPOLYGON_H
diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp
index 8b4196ee7f..36b6b220b3 100644
--- a/scene/2d/node_2d.cpp
+++ b/scene/2d/node_2d.cpp
@@ -317,6 +317,18 @@ int Node2D::get_z() const{
return z;
}
+Matrix32 Node2D::get_relative_transform(const Node *p_parent) const {
+
+ if (p_parent==this)
+ return Matrix32();
+
+ Node2D *parent_2d = get_parent()->cast_to<Node2D>();
+ ERR_FAIL_COND_V(!parent_2d,Matrix32());
+ if (p_parent==parent_2d)
+ return get_transform();
+ else
+ return parent_2d->get_relative_transform(p_parent) * get_transform();
+}
void Node2D::_bind_methods() {
@@ -351,6 +363,8 @@ void Node2D::_bind_methods() {
ObjectTypeDB::bind_method(_MD("edit_set_pivot"),&Node2D::edit_set_pivot);
+ ObjectTypeDB::bind_method(_MD("get_relative_transform"),&Node2D::get_relative_transform);
+
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2,"transform/pos"),_SCS("set_pos"),_SCS("get_pos"));
ADD_PROPERTY(PropertyInfo(Variant::REAL,"transform/rot",PROPERTY_HINT_RANGE,"-1440,1440,0.1"),_SCS("_set_rotd"),_SCS("_get_rotd"));
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2,"transform/scale"),_SCS("set_scale"),_SCS("get_scale"));
diff --git a/scene/2d/node_2d.h b/scene/2d/node_2d.h
index 61b8c829d6..7b059008c2 100644
--- a/scene/2d/node_2d.h
+++ b/scene/2d/node_2d.h
@@ -93,6 +93,9 @@ public:
void set_z_as_relative(bool p_enabled);
bool is_z_relative() const;
+ Matrix32 get_relative_transform(const Node *p_parent) const;
+
+
Matrix32 get_transform() const;
Node2D();
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 9fcf34cee6..75e7957cb8 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -29,6 +29,7 @@
#include "tile_map.h"
#include "io/marshalls.h"
#include "servers/physics_2d_server.h"
+#include "method_bind_ext.inc"
void TileMap::_notification(int p_what) {
switch(p_what) {
@@ -62,7 +63,7 @@ void TileMap::_update_quadrant_space(const RID& p_space) {
for (Map<PosKey,Quadrant>::Element *E=quadrant_map.front();E;E=E->next()) {
Quadrant &q=E->get();
- Physics2DServer::get_singleton()->body_set_space(q.static_body,p_space);
+ Physics2DServer::get_singleton()->body_set_space(q.body,p_space);
}
}
@@ -79,7 +80,7 @@ void TileMap::_update_quadrant_transform() {
Matrix32 xform;
xform.set_origin( q.pos );
xform = global_transform * xform;
- Physics2DServer::get_singleton()->body_set_state(q.static_body,Physics2DServer::BODY_STATE_TRANSFORM,xform);
+ Physics2DServer::get_singleton()->body_set_state(q.body,Physics2DServer::BODY_STATE_TRANSFORM,xform);
}
}
@@ -178,7 +179,7 @@ void TileMap::_update_dirty_quadrants() {
Quadrant &q = *dirty_quadrant_list.first()->self();
vs->canvas_item_clear(q.canvas_item);
- ps->body_clear_shapes(q.static_body);
+ ps->body_clear_shapes(q.body);
int shape_idx=0;
for(int i=0;i<q.cells.size();i++) {
@@ -225,11 +226,9 @@ void TileMap::_update_dirty_quadrants() {
rect.pos+=tile_ofs;
if (r==Rect2()) {
-
- tex->draw_rect(q.canvas_item,rect);
+ tex->draw_rect(q.canvas_item,rect,false,Color(1,1,1),c.transpose);
} else {
-
- tex->draw_rect_region(q.canvas_item,rect,r);
+ tex->draw_rect_region(q.canvas_item,rect,r,Color(1,1,1),c.transpose);
}
Vector< Ref<Shape2D> > shapes = tile_set->tile_get_shapes(c.id);
@@ -243,24 +242,29 @@ void TileMap::_update_dirty_quadrants() {
Vector2 shape_ofs = tile_set->tile_get_shape_offset(c.id);
Matrix32 xform;
xform.set_origin(offset.floor());
+ if (c.transpose) {
+ SWAP(xform.elements[0].x, xform.elements[0].y);
+ SWAP(xform.elements[1].x, xform.elements[1].y);
+ SWAP(shape_ofs.x, shape_ofs.y);
+ SWAP(s.x, s.y);
+ }
if (c.flip_h) {
- xform.elements[0]=-xform.elements[0];
- xform.elements[2].x+=s.x-shape_ofs.x;
- } else {
-
- xform.elements[2].x+=shape_ofs.x;
+ xform.elements[0].x=-xform.elements[0].x;
+ xform.elements[1].x=-xform.elements[1].x;
+ shape_ofs.x=s.x-shape_ofs.x;
}
if (c.flip_v) {
- xform.elements[1]=-xform.elements[1];
- xform.elements[2].y+=s.y-shape_ofs.y;
- } else {
-
- xform.elements[2].y+=shape_ofs.y;
+ xform.elements[0].y=-xform.elements[0].y;
+ xform.elements[1].y=-xform.elements[1].y;
+ shape_ofs.y=s.y-shape_ofs.y;
}
+ xform.elements[2].x+=shape_ofs.x;
+ xform.elements[2].y+=shape_ofs.y;
+
- ps->body_add_shape(q.static_body,shape->get_rid(),xform);
- ps->body_set_shape_metadata(q.static_body,shape_idx++,Vector2(E->key().x,E->key().y));
+ ps->body_add_shape(q.body,shape->get_rid(),xform);
+ ps->body_set_shape_metadata(q.body,shape_idx++,Vector2(E->key().x,E->key().y));
}
}
@@ -339,19 +343,19 @@ Map<TileMap::PosKey,TileMap::Quadrant>::Element *TileMap::_create_quadrant(const
q.canvas_item = VisualServer::get_singleton()->canvas_item_create();
VisualServer::get_singleton()->canvas_item_set_parent( q.canvas_item, get_canvas_item() );
VisualServer::get_singleton()->canvas_item_set_transform( q.canvas_item, xform );
- q.static_body=Physics2DServer::get_singleton()->body_create(Physics2DServer::BODY_MODE_STATIC);
- Physics2DServer::get_singleton()->body_attach_object_instance_ID(q.static_body,get_instance_ID());
- Physics2DServer::get_singleton()->body_set_layer_mask(q.static_body,collision_layer);
- Physics2DServer::get_singleton()->body_set_param(q.static_body,Physics2DServer::BODY_PARAM_FRICTION,friction);
- Physics2DServer::get_singleton()->body_set_param(q.static_body,Physics2DServer::BODY_PARAM_BOUNCE,bounce);
+ q.body=Physics2DServer::get_singleton()->body_create(use_kinematic?Physics2DServer::BODY_MODE_KINEMATIC:Physics2DServer::BODY_MODE_STATIC);
+ Physics2DServer::get_singleton()->body_attach_object_instance_ID(q.body,get_instance_ID());
+ Physics2DServer::get_singleton()->body_set_layer_mask(q.body,collision_layer);
+ Physics2DServer::get_singleton()->body_set_param(q.body,Physics2DServer::BODY_PARAM_FRICTION,friction);
+ Physics2DServer::get_singleton()->body_set_param(q.body,Physics2DServer::BODY_PARAM_BOUNCE,bounce);
if (is_inside_tree()) {
xform = get_global_transform() * xform;
RID space = get_world_2d()->get_space();
- Physics2DServer::get_singleton()->body_set_space(q.static_body,space);
+ Physics2DServer::get_singleton()->body_set_space(q.body,space);
}
- Physics2DServer::get_singleton()->body_set_state(q.static_body,Physics2DServer::BODY_STATE_TRANSFORM,xform);
+ Physics2DServer::get_singleton()->body_set_state(q.body,Physics2DServer::BODY_STATE_TRANSFORM,xform);
rect_cache_dirty=true;
quadrant_order_dirty=true;
@@ -361,7 +365,7 @@ Map<TileMap::PosKey,TileMap::Quadrant>::Element *TileMap::_create_quadrant(const
void TileMap::_erase_quadrant(Map<PosKey,Quadrant>::Element *Q) {
Quadrant &q=Q->get();
- Physics2DServer::get_singleton()->free(q.static_body);
+ Physics2DServer::get_singleton()->free(q.body);
VisualServer::get_singleton()->free(q.canvas_item);
if (q.dirty_list.in_list())
dirty_quadrant_list.remove(&q.dirty_list);
@@ -385,7 +389,7 @@ void TileMap::_make_quadrant_dirty(Map<PosKey,Quadrant>::Element *Q) {
}
-void TileMap::set_cell(int p_x,int p_y,int p_tile,bool p_flip_x,bool p_flip_y) {
+void TileMap::set_cell(int p_x,int p_y,int p_tile,bool p_flip_x,bool p_flip_y,bool p_transpose) {
PosKey pk(p_x,p_y);
@@ -421,7 +425,7 @@ void TileMap::set_cell(int p_x,int p_y,int p_tile,bool p_flip_x,bool p_flip_y) {
} else {
ERR_FAIL_COND(!Q); // quadrant should exist...
- if (E->get().id==p_tile && E->get().flip_h==p_flip_x && E->get().flip_v==p_flip_y)
+ if (E->get().id==p_tile && E->get().flip_h==p_flip_x && E->get().flip_v==p_flip_y && E->get().transpose==p_transpose)
return; //nothing changed
}
@@ -432,6 +436,7 @@ void TileMap::set_cell(int p_x,int p_y,int p_tile,bool p_flip_x,bool p_flip_y) {
c.id=p_tile;
c.flip_h=p_flip_x;
c.flip_v=p_flip_y;
+ c.transpose=p_transpose;
_make_quadrant_dirty(Q);
@@ -471,6 +476,17 @@ bool TileMap::is_cell_y_flipped(int p_x,int p_y) const {
return E->get().flip_v;
}
+bool TileMap::is_cell_transposed(int p_x,int p_y) const {
+
+ PosKey pk(p_x,p_y);
+
+ const Map<PosKey,Cell>::Element *E=tile_map.find(pk);
+
+ if (!E)
+ return false;
+
+ return E->get().transpose;
+}
void TileMap::_recreate_quadrants() {
@@ -535,11 +551,12 @@ void TileMap::_set_tile_data(const DVector<int>& p_data) {
uint32_t v = decode_uint32(&local[4]);
bool flip_h = v&(1<<29);
bool flip_v = v&(1<<30);
+ bool transpose = v&(1<<31);
v&=(1<<29)-1;
// if (x<-20 || y <-20 || x>4000 || y>4000)
// continue;
- set_cell(x,y,v,flip_h,flip_v);
+ set_cell(x,y,v,flip_h,flip_v,transpose);
}
@@ -562,6 +579,8 @@ DVector<int> TileMap::_get_tile_data() const {
val|=(1<<29);
if (E->get().flip_v)
val|=(1<<30);
+ if (E->get().transpose)
+ val|=(1<<31);
encode_uint32(val,&ptr[4]);
idx+=2;
@@ -586,17 +605,29 @@ void TileMap::set_collision_layer_mask(uint32_t p_layer) {
for (Map<PosKey,Quadrant>::Element *E=quadrant_map.front();E;E=E->next()) {
Quadrant &q=E->get();
- Physics2DServer::get_singleton()->body_set_layer_mask(q.static_body,collision_layer);
+ Physics2DServer::get_singleton()->body_set_layer_mask(q.body,collision_layer);
}
}
+bool TileMap::get_collision_use_kinematic() const{
+
+ return use_kinematic;
+}
+
+void TileMap::set_collision_use_kinematic(bool p_use_kinematic) {
+
+ _clear_quadrants();
+ use_kinematic=p_use_kinematic;
+ _recreate_quadrants();
+}
+
void TileMap::set_collision_friction(float p_friction) {
friction=p_friction;
for (Map<PosKey,Quadrant>::Element *E=quadrant_map.front();E;E=E->next()) {
Quadrant &q=E->get();
- Physics2DServer::get_singleton()->body_set_param(q.static_body,Physics2DServer::BODY_PARAM_FRICTION,p_friction);
+ Physics2DServer::get_singleton()->body_set_param(q.body,Physics2DServer::BODY_PARAM_FRICTION,p_friction);
}
}
@@ -612,7 +643,7 @@ void TileMap::set_collision_bounce(float p_bounce){
for (Map<PosKey,Quadrant>::Element *E=quadrant_map.front();E;E=E->next()) {
Quadrant &q=E->get();
- Physics2DServer::get_singleton()->body_set_param(q.static_body,Physics2DServer::BODY_PARAM_BOUNCE,p_bounce);
+ Physics2DServer::get_singleton()->body_set_param(q.body,Physics2DServer::BODY_PARAM_BOUNCE,p_bounce);
}
}
@@ -804,6 +835,9 @@ void TileMap::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_center_y","enable"),&TileMap::set_center_y);
ObjectTypeDB::bind_method(_MD("get_center_y"),&TileMap::get_center_y);
+ ObjectTypeDB::bind_method(_MD("set_collision_use_kinematic","use_kinematic"),&TileMap::set_collision_use_kinematic);
+ ObjectTypeDB::bind_method(_MD("get_collision_use_kinematic"),&TileMap::get_collision_use_kinematic);
+
ObjectTypeDB::bind_method(_MD("set_collision_layer_mask","mask"),&TileMap::set_collision_layer_mask);
ObjectTypeDB::bind_method(_MD("get_collision_layer_mask"),&TileMap::get_collision_layer_mask);
@@ -813,7 +847,7 @@ void TileMap::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_collision_bounce","value"),&TileMap::set_collision_bounce);
ObjectTypeDB::bind_method(_MD("get_collision_bounce"),&TileMap::get_collision_bounce);
- ObjectTypeDB::bind_method(_MD("set_cell","x","y","tile","flip_x","flip_y"),&TileMap::set_cell,DEFVAL(false),DEFVAL(false));
+ ObjectTypeDB::bind_method(_MD("set_cell","x","y","tile","flip_x","flip_y","transpose"),&TileMap::set_cell,DEFVAL(false),DEFVAL(false),DEFVAL(false));
ObjectTypeDB::bind_method(_MD("get_cell","x","y"),&TileMap::get_cell);
ObjectTypeDB::bind_method(_MD("is_cell_x_flipped","x","y"),&TileMap::is_cell_x_flipped);
ObjectTypeDB::bind_method(_MD("is_cell_y_flipped","x","y"),&TileMap::is_cell_y_flipped);
@@ -837,6 +871,7 @@ void TileMap::_bind_methods() {
ADD_PROPERTY( PropertyInfo(Variant::INT,"cell/quadrant_size",PROPERTY_HINT_RANGE,"1,128,1"),_SCS("set_quadrant_size"),_SCS("get_quadrant_size"));
ADD_PROPERTY( PropertyInfo(Variant::MATRIX32,"cell/custom_transform"),_SCS("set_custom_transform"),_SCS("get_custom_transform"));
ADD_PROPERTY( PropertyInfo(Variant::INT,"cell/half_offset",PROPERTY_HINT_ENUM,"Offset X,Offset Y,Disabled"),_SCS("set_half_offset"),_SCS("get_half_offset"));
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"collision/use_kinematic",PROPERTY_HINT_NONE,""),_SCS("set_collision_use_kinematic"),_SCS("get_collision_use_kinematic"));
ADD_PROPERTY( PropertyInfo(Variant::REAL,"collision/friction",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_collision_friction"),_SCS("get_collision_friction"));
ADD_PROPERTY( PropertyInfo(Variant::REAL,"collision/bounce",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_collision_bounce"),_SCS("get_collision_bounce"));
ADD_PROPERTY( PropertyInfo(Variant::INT,"collision/layers",PROPERTY_HINT_ALL_FLAGS),_SCS("set_collision_layer_mask"),_SCS("get_collision_layer_mask"));
@@ -870,6 +905,7 @@ TileMap::TileMap() {
bounce=0;
mode=MODE_SQUARE;
half_offset=HALF_OFFSET_DISABLED;
+ use_kinematic=false;
fp_adjust=0.01;
fp_adjust=0.01;
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index 4e9e2e7e97..fe1067fc1d 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -60,6 +60,7 @@ private:
Mode mode;
Matrix32 custom_transform;
HalfOffset half_offset;
+ bool use_kinematic;
union PosKey {
@@ -85,6 +86,7 @@ private:
int32_t id:24;
bool flip_h:1;
bool flip_v:1;
+ bool transpose:1;
};
uint32_t _u32t;
@@ -97,14 +99,14 @@ private:
Vector2 pos;
RID canvas_item;
- RID static_body;
+ RID body;
SelfList<Quadrant> dirty_list;
VSet<PosKey> cells;
- void operator=(const Quadrant& q) { pos=q.pos; canvas_item=q.canvas_item; static_body=q.static_body; cells=q.cells; }
- Quadrant(const Quadrant& q) : dirty_list(this) { pos=q.pos; canvas_item=q.canvas_item; static_body=q.static_body; cells=q.cells;}
+ void operator=(const Quadrant& q) { pos=q.pos; canvas_item=q.canvas_item; body=q.body; cells=q.cells; }
+ Quadrant(const Quadrant& q) : dirty_list(this) { pos=q.pos; canvas_item=q.canvas_item; body=q.body; cells=q.cells;}
Quadrant() : dirty_list(this) {}
};
@@ -167,16 +169,20 @@ public:
void set_center_y(bool p_enable);
bool get_center_y() const;
- void set_cell(int p_x,int p_y,int p_tile,bool p_flip_x=false,bool p_flip_y=false);
+ void set_cell(int p_x,int p_y,int p_tile,bool p_flip_x=false,bool p_flip_y=false,bool p_transpose=false);
int get_cell(int p_x,int p_y) const;
bool is_cell_x_flipped(int p_x,int p_y) const;
bool is_cell_y_flipped(int p_x,int p_y) const;
+ bool is_cell_transposed(int p_x,int p_y) const;
Rect2 get_item_rect() const;
void set_collision_layer_mask(uint32_t p_layer);
uint32_t get_collision_layer_mask() const;
+ void set_collision_use_kinematic(bool p_use_kinematic);
+ bool get_collision_use_kinematic() const;
+
void set_collision_friction(float p_friction);
float get_collision_friction() const;