diff options
Diffstat (limited to 'scene/2d')
-rw-r--r-- | scene/2d/navigation2d.cpp | 623 | ||||
-rw-r--r-- | scene/2d/navigation2d.h | 137 | ||||
-rw-r--r-- | scene/2d/navigation_polygon.cpp | 450 | ||||
-rw-r--r-- | scene/2d/navigation_polygon.h | 84 | ||||
-rw-r--r-- | scene/2d/node_2d.cpp | 14 | ||||
-rw-r--r-- | scene/2d/node_2d.h | 3 | ||||
-rw-r--r-- | scene/2d/tile_map.cpp | 50 | ||||
-rw-r--r-- | scene/2d/tile_map.h | 10 |
8 files changed, 1352 insertions, 19 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..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..52f4d27497 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" + 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++) { @@ -259,8 +260,8 @@ void TileMap::_update_dirty_quadrants() { } - 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 +340,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 +362,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); @@ -586,17 +587,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 +625,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 +817,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); @@ -837,6 +853,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 +887,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..c8708e1bed 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 { @@ -97,14 +98,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) {} }; @@ -177,6 +178,9 @@ public: 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; |