summaryrefslogtreecommitdiff
path: root/scene/2d/navigation_polygon.cpp
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/2d/navigation_polygon.cpp
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/2d/navigation_polygon.cpp')
-rw-r--r--scene/2d/navigation_polygon.cpp450
1 files changed, 450 insertions, 0 deletions
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;
+
+}