summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
authorJuan Linietsky <reduzio@gmail.com>2015-02-14 12:09:52 -0300
committerJuan Linietsky <reduzio@gmail.com>2015-02-14 12:10:15 -0300
commitc5f509f238576dba39ffcce74ab2066f24e67b58 (patch)
tree888a1bc97d9fdf303a663e626599f74fb268dbff /scene
parentd0ea4754057663d2caefbabe32421fd597a8a15d (diff)
New Navigation & Pathfinding support for 2D
-Added Navigation & NavigationPolygon nodes -Added corresponding visual editor -New pathfinding algorithm is modern and fast! -Similar API to 3D Pathfinding (more coherent)
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/navigation2d.cpp623
-rw-r--r--scene/2d/navigation2d.h137
-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/gui/dialogs.cpp4
-rw-r--r--scene/gui/popup.cpp4
-rw-r--r--scene/register_scene_types.cpp5
9 files changed, 1322 insertions, 2 deletions
diff --git a/scene/2d/navigation2d.cpp b/scene/2d/navigation2d.cpp
new file mode 100644
index 0000000000..5e93dac61d
--- /dev/null
+++ b/scene/2d/navigation2d.cpp
@@ -0,0 +1,623 @@
+#include "navigation2d.h"
+
+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;
+
+ 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;
+ begin_poly->edges[i].C->distance=begin_poly->center.distance_to(begin_poly->edges[i].C->center);
+ 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
+
+ for(int i=0;i<p->edges.size();i++) {
+
+
+ Polygon::Edge &e=p->edges[i];
+
+ if (!e.C)
+ continue;
+
+ float distance = p->center.distance_to(e.C->center) + p->distance;
+
+ 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;
+ }
+ } else {
+ //add to open neighbours
+
+ e.C->prev_edge=e.C_edge;
+ e.C->distance=distance;
+ 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..1fca80dc5c
--- /dev/null
+++ b/scene/2d/navigation2d.h
@@ -0,0 +1,137 @@
+#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;
+
+ 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..570a5b95b0
--- /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(){
+
+ std::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(std::list<TriangulatorPoly>::iterator I = out_poly.begin();I!=out_poly.end();I++) {
+
+ TriangulatorPoly& tp = *I;
+
+ 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/gui/dialogs.cpp b/scene/gui/dialogs.cpp
index a82cfc7ea6..30e0241f23 100644
--- a/scene/gui/dialogs.cpp
+++ b/scene/gui/dialogs.cpp
@@ -328,8 +328,8 @@ AcceptDialog::AcceptDialog() {
label->set_anchor(MARGIN_RIGHT,ANCHOR_END);
label->set_anchor(MARGIN_BOTTOM,ANCHOR_END);
label->set_begin( Point2( margin, margin) );
- label->set_end( Point2( margin, button_margin) );
- label->set_autowrap(true);
+ label->set_end( Point2( margin, button_margin+10) );
+ //label->set_autowrap(true);
add_child(label);
hbc = memnew( HBoxContainer );
diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp
index bccd05d4fe..d58cb3da79 100644
--- a/scene/gui/popup.cpp
+++ b/scene/gui/popup.cpp
@@ -94,6 +94,8 @@ void Popup::popup_centered_minsize(const Size2& p_minsize) {
Control *c=get_child(i)->cast_to<Control>();
if (!c)
continue;
+ if (c->is_hidden())
+ continue;
Size2 minsize = c->get_combined_minimum_size();
@@ -114,6 +116,8 @@ void Popup::popup_centered_minsize(const Size2& p_minsize) {
}
+ print_line(String(c->get_type())+": "+minsize);
+
total_minsize.width = MAX( total_minsize.width, minsize.width );
total_minsize.height = MAX( total_minsize.height, minsize.height );
}
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 9d907391ec..9600469e8a 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -102,6 +102,7 @@
#include "scene/2d/screen_button.h"
#include "scene/2d/remote_transform_2d.h"
#include "scene/2d/y_sort.h"
+#include "scene/2d/navigation2d.h"
#include "scene/2d/position_2d.h"
#include "scene/2d/tile_map.h"
@@ -575,6 +576,10 @@ void register_scene_types() {
ObjectTypeDB::register_type<Path2D>();
ObjectTypeDB::register_type<PathFollow2D>();
+ ObjectTypeDB::register_type<Navigation2D>();
+ ObjectTypeDB::register_type<NavigationPolygon>();
+ ObjectTypeDB::register_type<NavigationPolygonInstance>();
+
OS::get_singleton()->yield(); //may take time to init
ObjectTypeDB::register_type<PackedScene>();