diff options
Diffstat (limited to 'scene/2d/navigation_polygon.cpp')
-rw-r--r-- | scene/2d/navigation_polygon.cpp | 450 |
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..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; + +} |