浏览代码

Merge branch 'master' of https://github.com/okamstudio/godot

Anton Yabchinskiy 10 年之前
父节点
当前提交
e024ff89b2
共有 93 个文件被更改,包括 6880 次插入397 次删除
  1. 3 0
      .gitignore
  2. 339 1
      bin/tests/test_string.cpp
  3. 14 0
      core/bind/core_bind.cpp
  4. 4 0
      core/bind/core_bind.h
  5. 17 0
      core/dvector.h
  6. 34 9
      core/io/http_client.cpp
  7. 5 1
      core/io/http_client.h
  8. 54 2
      core/list.h
  9. 14 0
      core/math/geometry.h
  10. 1550 0
      core/math/triangulator.cpp
  11. 306 0
      core/math/triangulator.h
  12. 1 1
      core/resource.cpp
  13. 37 0
      core/set.h
  14. 292 4
      core/ustring.cpp
  15. 6 2
      core/ustring.h
  16. 26 17
      core/variant.cpp
  17. 5 1
      core/variant.h
  18. 2 2
      core/variant_call.cpp
  19. 433 0
      core/variant_construct_string.cpp
  20. 14 0
      core/variant_op.cpp
  21. 0 33
      demos/2d/hexamap/.fscache
  22. 二进制
      demos/2d/navpoly/agent.png
  23. 4 0
      demos/2d/navpoly/engine.cfg
  24. 63 0
      demos/2d/navpoly/navigation.gd
  25. 二进制
      demos/2d/navpoly/navigation.scn
  26. 二进制
      demos/2d/navpoly/path.png
  27. 12 11
      demos/2d/platformer/stage.xml
  28. 二进制
      demos/2d/platformer/tiles_demo.png
  29. 121 64
      demos/2d/platformer/tileset.xml
  30. 88 52
      demos/2d/platformer/tileset_edit.xml
  31. 0 4
      demos/2d/polygon_path_finder_demo/.fscache
  32. 354 13
      demos/3d/platformer/stage.xml
  33. 96 60
      drivers/gles2/rasterizer_gles2.cpp
  34. 21 7
      drivers/gles2/rasterizer_gles2.h
  35. 6 1
      drivers/unix/ip_unix.cpp
  36. 19 1
      drivers/unix/os_unix.cpp
  37. 1 1
      modules/gdscript/gd_editor.cpp
  38. 29 3
      modules/gdscript/gd_functions.cpp
  39. 2 0
      modules/gdscript/gd_functions.h
  40. 12 7
      platform/iphone/app_delegate.mm
  41. 8 0
      platform/iphone/gl_view.h
  42. 32 0
      platform/iphone/gl_view.mm
  43. 1 1
      platform/isim/detect.py
  44. 12 1
      platform/osx/os_osx.mm
  45. 61 6
      platform/windows/os_windows.cpp
  46. 1 0
      platform/windows/os_windows.h
  47. 11 17
      platform/x11/detect.py
  48. 5 1
      platform/x11/os_x11.cpp
  49. 22 1
      scene/2d/canvas_item.cpp
  50. 2 0
      scene/2d/canvas_item.h
  51. 182 0
      scene/2d/light_2d.cpp
  52. 80 0
      scene/2d/light_2d.h
  53. 623 0
      scene/2d/navigation2d.cpp
  54. 137 0
      scene/2d/navigation2d.h
  55. 450 0
      scene/2d/navigation_polygon.cpp
  56. 84 0
      scene/2d/navigation_polygon.h
  57. 14 0
      scene/2d/node_2d.cpp
  58. 3 0
      scene/2d/node_2d.h
  59. 34 16
      scene/2d/tile_map.cpp
  60. 7 3
      scene/2d/tile_map.h
  61. 1 1
      scene/3d/camera.cpp
  62. 11 0
      scene/gui/control.cpp
  63. 1 1
      scene/gui/control.h
  64. 2 2
      scene/gui/dialogs.cpp
  65. 4 0
      scene/gui/popup.cpp
  66. 4 0
      scene/gui/tree.cpp
  67. 9 0
      scene/main/viewport.cpp
  68. 2 0
      scene/main/viewport.h
  69. 7 0
      scene/register_scene_types.cpp
  70. 1 1
      scene/resources/material.cpp
  71. 17 9
      scene/resources/polygon_path_finder.cpp
  72. 1 1
      scene/resources/shader_graph.cpp
  73. 6 0
      scene/resources/style_box.cpp
  74. 18 18
      scene/resources/texture.cpp
  75. 33 0
      servers/visual/rasterizer.h
  76. 134 0
      servers/visual/visual_server_raster.cpp
  77. 36 0
      servers/visual/visual_server_raster.h
  78. 23 0
      servers/visual/visual_server_wrap_mt.h
  79. 33 0
      servers/visual_server.h
  80. 26 0
      tools/editor/editor_import_export.cpp
  81. 1 0
      tools/editor/editor_import_export.h
  82. 61 11
      tools/editor/editor_node.cpp
  83. 10 0
      tools/editor/editor_node.h
  84. 二进制
      tools/editor/icons/icon_light_2d.png
  85. 二进制
      tools/editor/icons/icon_navigation_2d.png
  86. 二进制
      tools/editor/icons/icon_navigation_polygon_instance.png
  87. 8 6
      tools/editor/plugins/collision_polygon_editor_plugin.cpp
  88. 547 0
      tools/editor/plugins/navigation_polygon_editor_plugin.cpp
  89. 91 0
      tools/editor/plugins/navigation_polygon_editor_plugin.h
  90. 5 1
      tools/editor/plugins/script_editor_plugin.cpp
  91. 32 1
      tools/editor/scene_tree_dock.cpp
  92. 1 0
      tools/editor/scene_tree_dock.h
  93. 2 2
      tools/export/blender25/io_scene_dae/export_dae.py

+ 3 - 0
.gitignore

@@ -19,6 +19,9 @@ tools/editor/register_exporters.cpp
 tools/editor/doc_data_compressed.h
 tools/editor/editor_icons.cpp
 -fpic
+.fscache
+make.bat
+log.txt
 
 # Android specific
 platform/android/java/local.properties

+ 339 - 1
bin/tests/test_string.cpp

@@ -487,7 +487,7 @@ struct test_27_data {
 
 bool test_27() {
 
-	OS::get_singleton()->print("\n\nTest 26: begins_with\n");
+	OS::get_singleton()->print("\n\nTest 27: begins_with\n");
 	test_27_data tc[] = {
 		{"res://foobar", "res://", true},
 		{"res", "res://", false},
@@ -504,11 +504,348 @@ bool test_27() {
 		}
 		if (!state) {
 			OS::get_singleton()->print("\n\t Failure on:\n\t\tstring: ", tc[i].data, "\n\t\tbegin: ", tc[i].begin, "\n\t\texpected: ", tc[i].expected ? "true" : "false", "\n");
+			break;
 		}
 	};
 	return state;
 };
 
+
+bool test_28() {
+
+	OS::get_singleton()->print("\n\nTest 28: sprintf\n");
+
+	bool success, state = true;
+	char output_format[] = "\tTest:\t%ls => %ls (%s)\n";
+	String format, output;
+	Array args;
+	
+	// %%
+	format = "fish %% frog";
+	args.clear();
+	output = format.sprintf(args);
+	success = (output == String("fish % frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	//////// INTS
+
+	// Int
+	format = "fish %d frog";
+	args.clear();
+	args.push_back(5);
+	output = format.sprintf(args);
+	success = (output == String("fish 5 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Int left padded with zeroes.
+	format = "fish %05d frog";
+	args.clear();
+	args.push_back(5);
+	output = format.sprintf(args);
+	success = (output == String("fish 00005 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Int left padded with spaces.
+	format = "fish %5d frog";
+	args.clear();
+	args.push_back(5);
+	output = format.sprintf(args);
+	success = (output == String("fish     5 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Int right padded with spaces.
+	format = "fish %-5d frog";
+	args.clear();
+	args.push_back(5);
+	output = format.sprintf(args);
+	success = (output == String("fish 5     frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Int with sign (positive).
+	format = "fish %+d frog";
+	args.clear();
+	args.push_back(5);
+	output = format.sprintf(args);
+	success = (output == String("fish +5 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Negative int.
+	format = "fish %d frog";
+	args.clear();
+	args.push_back(-5);
+	output = format.sprintf(args);
+	success = (output == String("fish -5 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Hex (lower)
+	format = "fish %x frog";
+	args.clear();
+	args.push_back(45);
+	output = format.sprintf(args);
+	success = (output == String("fish 2d frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Hex (upper)
+	format = "fish %X frog";
+	args.clear();
+	args.push_back(45);
+	output = format.sprintf(args);
+	success = (output == String("fish 2D frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Octal
+	format = "fish %o frog";
+	args.clear();
+	args.push_back(99);
+	output = format.sprintf(args);
+	success = (output == String("fish 143 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	////// REALS
+
+	// Real
+	format = "fish %f frog";
+	args.clear();
+	args.push_back(99.99);
+	output = format.sprintf(args);
+	success = (output == String("fish 99.990000 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Real left-padded
+	format = "fish %11f frog";
+	args.clear();
+	args.push_back(99.99);
+	output = format.sprintf(args);
+	success = (output == String("fish   99.990000 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Real right-padded
+	format = "fish %-11f frog";
+	args.clear();
+	args.push_back(99.99);
+	output = format.sprintf(args);
+	success = (output == String("fish 99.990000   frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Real given int.
+	format = "fish %f frog";
+	args.clear();
+	args.push_back(99);
+	output = format.sprintf(args);
+	success = (output == String("fish 99.000000 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Real with sign (positive).
+	format = "fish %+f frog";
+	args.clear();
+	args.push_back(99.99);
+	output = format.sprintf(args);
+	success = (output == String("fish +99.990000 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Real with 1 decimals.
+	format = "fish %.1f frog";
+	args.clear();
+	args.push_back(99.99);
+	output = format.sprintf(args);
+	success = (output == String("fish 100.0 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Real with 12 decimals.
+	format = "fish %.12f frog";
+	args.clear();
+	args.push_back(99.99);
+	output = format.sprintf(args);
+	success = (output == String("fish 99.990000000000 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Real with no decimals.
+	format = "fish %.f frog";
+	args.clear();
+	args.push_back(99.99);
+	output = format.sprintf(args);
+	success = (output == String("fish 100 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	/////// Strings.
+
+	// String
+	format = "fish %s frog";
+	args.clear();
+	args.push_back("cheese");
+	output = format.sprintf(args);
+	success = (output == String("fish cheese frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// String left-padded
+	format = "fish %10s frog";
+	args.clear();
+	args.push_back("cheese");
+	output = format.sprintf(args);
+	success = (output == String("fish     cheese frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// String right-padded
+	format = "fish %-10s frog";
+	args.clear();
+	args.push_back("cheese");
+	output = format.sprintf(args);
+	success = (output == String("fish cheese     frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	///// Characters
+
+	// Character as string.
+	format = "fish %c frog";
+	args.clear();
+	args.push_back("A");
+	output = format.sprintf(args);
+	success = (output == String("fish A frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Character as int.
+	format = "fish %c frog";
+	args.clear();
+	args.push_back(65);
+	output = format.sprintf(args);
+	success = (output == String("fish A frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	///// Dynamic width
+
+	// String dynamic width
+	format = "fish %*s frog";
+	args.clear();
+	args.push_back(10);
+	args.push_back("cheese");
+	output = format.sprintf(args);
+	success = (output == String("fish     cheese frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Int dynamic width
+	format = "fish %*d frog";
+	args.clear();
+	args.push_back(10);
+	args.push_back(99);
+	output = format.sprintf(args);
+	success = (output == String("fish         99 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Float dynamic width
+	format = "fish %*.*f frog";
+	args.clear();
+	args.push_back(10);
+	args.push_back(3);
+	args.push_back(99.99);
+	output = format.sprintf(args);
+	success = (output == String("fish     99.990 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	///// Errors
+
+	// More formats than arguments.
+	format = "fish %s %s frog";
+	args.clear();
+	args.push_back("cheese");
+	output = format.sprintf(args);
+	success = (output == "");
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// More arguments than formats.
+	format = "fish %s frog";
+	args.clear();
+	args.push_back("hello");
+	args.push_back("cheese");
+	output = format.sprintf(args);
+	success = (output == "");
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Incomplete format.
+	format = "fish %10";
+	args.clear();
+	args.push_back("cheese");
+	output = format.sprintf(args);
+	success = (output == "");
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Bad character in format string
+	format = "fish %&f frog";
+	args.clear();
+	args.push_back("cheese");
+	output = format.sprintf(args);
+	success = (output == "");
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Too many decimals.
+	format = "fish %2.2.2f frog";
+	args.clear();
+	args.push_back(99.99);
+	output = format.sprintf(args);
+	success = (output == "");
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// * not a number
+	format = "fish %*f frog";
+	args.clear();
+	args.push_back("cheese");
+	args.push_back(99.99);
+	output = format.sprintf(args);
+	success = (output == "");
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Character too long.
+	format = "fish %c frog";
+	args.clear();
+	args.push_back("sc");
+	output = format.sprintf(args);
+	success = (output == "");
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Character bad type.
+	format = "fish %c frog";
+	args.clear();
+	args.push_back(Array());
+	output = format.sprintf(args);
+	success = (output == "");
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	return state;
+}
+
 typedef bool (*TestFunc)(void);
 
 TestFunc test_funcs[] = {
@@ -540,6 +877,7 @@ TestFunc test_funcs[] = {
 	test_25,
 	test_26,
 	test_27,
+	test_28,
 	0
 	
 };

+ 14 - 0
core/bind/core_bind.cpp

@@ -316,6 +316,11 @@ float _OS::get_time_scale() {
 	return OS::get_singleton()->get_time_scale();
 }
 
+bool _OS::is_ok_left_and_cancel_right() const {
+
+	return OS::get_singleton()->get_swap_ok_cancel();
+}
+
 /*
 enum Weekday {
 	DAY_SUNDAY,
@@ -699,6 +704,8 @@ void _OS::_bind_methods() {
 	ObjectTypeDB::bind_method(_MD("get_system_dir","dir"),&_OS::get_system_dir);
 	ObjectTypeDB::bind_method(_MD("get_unique_ID"),&_OS::get_unique_ID);
 
+	ObjectTypeDB::bind_method(_MD("is_ok_left_and_cancel_right"),&_OS::is_ok_left_and_cancel_right);
+
 	ObjectTypeDB::bind_method(_MD("get_frames_per_second"),&_OS::get_frames_per_second);
 
 	ObjectTypeDB::bind_method(_MD("print_all_textures_by_size"),&_OS::print_all_textures_by_size);
@@ -838,6 +845,12 @@ Variant _Geometry::segment_intersects_triangle( const Vector3& p_from, const Vec
 		return Variant();
 
 }
+
+bool _Geometry::point_is_inside_triangle(const Vector2& s, const Vector2& a, const Vector2& b, const Vector2& c) const {
+
+	return Geometry::is_point_in_triangle(s,a,b,c);
+}
+
 DVector<Vector3> _Geometry::segment_intersects_sphere( const Vector3& p_from, const Vector3& p_to, const Vector3& p_sphere_pos,real_t p_sphere_radius) {
 
 	DVector<Vector3> r;
@@ -938,6 +951,7 @@ void _Geometry::_bind_methods() {
 	ObjectTypeDB::bind_method(_MD("segment_intersects_sphere","from","to","spos","sradius"),&_Geometry::segment_intersects_sphere);
 	ObjectTypeDB::bind_method(_MD("segment_intersects_cylinder","from","to","height","radius"),&_Geometry::segment_intersects_cylinder);
 	ObjectTypeDB::bind_method(_MD("segment_intersects_convex","from","to","planes"),&_Geometry::segment_intersects_convex);
+	ObjectTypeDB::bind_method(_MD("point_is_inside_triangle","point","a","b","c"),&_Geometry::point_is_inside_triangle);
 
 	ObjectTypeDB::bind_method(_MD("triangulate_polygon","polygon"),&_Geometry::triangulate_polygon);
 

+ 4 - 0
core/bind/core_bind.h

@@ -220,6 +220,8 @@ public:
 	void set_time_scale(float p_scale);
 	float get_time_scale();
 
+	bool is_ok_left_and_cancel_right() const;
+
 	static _OS *get_singleton() { return singleton; }
 
 	_OS();
@@ -248,6 +250,8 @@ public:
 	Vector3 get_closest_point_to_segment(const Vector3& p_point, const Vector3& p_a,const Vector3& p_b);
 	Variant ray_intersects_triangle( const Vector3& p_from, const Vector3& p_dir, const Vector3& p_v0,const Vector3& p_v1,const Vector3& p_v2);
 	Variant segment_intersects_triangle( const Vector3& p_from, const Vector3& p_to, const Vector3& p_v0,const Vector3& p_v1,const Vector3& p_v2);
+	bool point_is_inside_triangle(const Vector2& s, const Vector2& a, const Vector2& b, const Vector2& c) const;
+
 	DVector<Vector3> segment_intersects_sphere( const Vector3& p_from, const Vector3& p_to, const Vector3& p_sphere_pos,real_t p_sphere_radius);
 	DVector<Vector3> segment_intersects_cylinder( const Vector3& p_from, const Vector3& p_to, float p_height,float p_radius);
 	DVector<Vector3> segment_intersects_convex(const Vector3& p_from, const Vector3& p_to,const Vector<Plane>& p_planes);

+ 17 - 0
core/dvector.h

@@ -262,6 +262,23 @@ public:
 			w[bs+i]=r[i];
 	}
 
+
+	Error insert(int p_pos,const T& p_val) {
+
+		int s=size();
+		ERR_FAIL_INDEX_V(p_pos,s+1,ERR_INVALID_PARAMETER);
+		resize(s+1);
+		{
+			Write w = write();
+			for (int i=s;i>p_pos;i--)
+				w[i]=w[i-1];
+			w[p_pos]=p_val;
+		}
+
+		return OK;
+	}
+
+
 	bool is_locked() const { return mem.is_locked(); }
 	
 	inline const T operator[](int p_index) const;

+ 34 - 9
core/io/http_client.cpp

@@ -273,7 +273,7 @@ Error HTTPClient::poll(){
 			while(true) {
 				uint8_t byte;
 				int rec=0;
-				Error err = connection->get_partial_data(&byte,1,rec);
+				Error err = _get_http_data(&byte,1,rec);
 				if (err!=OK) {
 					close();
 					status=STATUS_CONNECTION_ERROR;
@@ -417,7 +417,7 @@ ByteArray HTTPClient::read_response_body_chunk() {
 				//reading len
 				uint8_t b;
 				int rec=0;
-				err = connection->get_partial_data(&b,1,rec);
+				err = _get_http_data(&b,1,rec);
 
 				if (rec==0)
 					break;
@@ -471,7 +471,7 @@ ByteArray HTTPClient::read_response_body_chunk() {
 			} else {
 
 				int rec=0;
-				err = connection->get_partial_data(&chunk[chunk.size()-chunk_left],chunk_left,rec);
+				err = _get_http_data(&chunk[chunk.size()-chunk_left],chunk_left,rec);
 				if (rec==0) {
 					break;
 				}
@@ -502,18 +502,23 @@ ByteArray HTTPClient::read_response_body_chunk() {
 		}
 
 	} else {
+
+		int to_read = MIN(body_left,read_chunk_size);
 		ByteArray ret;
-		ret.resize(MAX(body_left,tmp_read.size()));
+		ret.resize(to_read);
 		ByteArray::Write w = ret.write();
 		int _offset = 0;
-		while (body_left > 0) {
-			ByteArray::Write r = tmp_read.write();
+		while (to_read > 0) {
 			int rec=0;
-			err = connection->get_partial_data(r.ptr(),MIN(body_left,tmp_read.size()),rec);
+			err = _get_http_data(w.ptr()+_offset,to_read,rec);
 			if (rec>0) {
-				copymem(w.ptr()+_offset,r.ptr(),rec);
 				body_left-=rec;
+				to_read-=rec;
 				_offset += rec;
+			} else {
+				if (to_read>0) //ended up reading less
+					ret.resize(_offset);
+				break;
 			}
 		}
 		if (body_left==0) {
@@ -557,6 +562,20 @@ bool HTTPClient::is_blocking_mode_enabled() const{
 	return blocking;
 }
 
+Error HTTPClient::_get_http_data(uint8_t* p_buffer, int p_bytes,int &r_received) {
+
+	if (blocking) {
+
+		Error err = connection->get_data(p_buffer,p_bytes);
+		if (err==OK)
+			r_received=p_bytes;
+		else
+			r_received=0;
+		return err;
+	} else {
+		return connection->get_partial_data(p_buffer,p_bytes,r_received);
+	}
+}
 
 void HTTPClient::_bind_methods() {
 
@@ -574,6 +593,7 @@ void HTTPClient::_bind_methods() {
 	ObjectTypeDB::bind_method(_MD("get_response_headers_as_dictionary"),&HTTPClient::_get_response_headers_as_dictionary);
 	ObjectTypeDB::bind_method(_MD("get_response_body_length"),&HTTPClient::get_response_body_length);
 	ObjectTypeDB::bind_method(_MD("read_response_body_chunk"),&HTTPClient::read_response_body_chunk);
+	ObjectTypeDB::bind_method(_MD("set_read_chunk_size","bytes"),&HTTPClient::set_read_chunk_size);
 
 	ObjectTypeDB::bind_method(_MD("set_blocking_mode","enabled"),&HTTPClient::set_blocking_mode);
 	ObjectTypeDB::bind_method(_MD("is_blocking_mode_enabled"),&HTTPClient::is_blocking_mode_enabled);
@@ -664,6 +684,11 @@ void HTTPClient::_bind_methods() {
 
 }
 
+void HTTPClient::set_read_chunk_size(int p_size) {
+	ERR_FAIL_COND(p_size<256 || p_size>(1<<24));
+	read_chunk_size=p_size;
+}
+
 HTTPClient::HTTPClient(){
 
 	tcp_connection = StreamPeerTCP::create_ref();
@@ -677,7 +702,7 @@ HTTPClient::HTTPClient(){
 	response_num=0;
 	ssl=false;
 	blocking=false;
-	tmp_read.resize(4096);
+	read_chunk_size=4096;
 }
 
 HTTPClient::~HTTPClient(){

+ 5 - 1
core/io/http_client.h

@@ -157,7 +157,10 @@ private:
 	static void _bind_methods();
 	StringArray _get_response_headers();
 	Dictionary _get_response_headers_as_dictionary();
-	ByteArray tmp_read;
+	int read_chunk_size;
+
+	Error _get_http_data(uint8_t* p_buffer, int p_bytes,int &r_received);
+
 public:
 
 
@@ -185,6 +188,7 @@ public:
 	void set_blocking_mode(bool p_enable); //useful mostly if running in a thread
 	bool is_blocking_mode_enabled() const;
 
+	void set_read_chunk_size(int p_size);
 
 	Error poll();
 

+ 54 - 2
core/list.h

@@ -30,7 +30,7 @@
 #define GLOBALS_LIST_H
 
 #include "os/memory.h"
-
+#include "sort.h"
 
 /**
  * Generic Templatized Linked List Implementation.
@@ -551,7 +551,7 @@ public:
 	}
 			
 	template<class C>
-	void sort_custom() {
+	void sort_custom_inplace() {
 
 		if(size()<2)
 			return;
@@ -603,6 +603,58 @@ public:
 		_data->last=to;
 	}
 
+	template<class C>
+	struct AuxiliaryComparator {
+
+		C compare;
+		_FORCE_INLINE_ bool operator()(const Element *a,const Element* b) const {
+
+			return compare(a->value,b->value);
+		}
+	};
+
+	template<class C>
+	void sort_custom() {
+
+		//this version uses auxiliary memory for speed.
+		//if you don't want to use auxiliary memory, use the in_place version
+
+		int s = size();
+		if(s<2)
+			return;
+
+
+		Element **aux_buffer = memnew_arr(Element*,s);
+
+		int idx=0;
+		for(Element *E=front();E;E=E->next_ptr) {
+
+			aux_buffer[idx]=E;
+			idx++;
+		}
+
+		SortArray<Element*,AuxiliaryComparator<C> > sort;
+		sort.sort(aux_buffer,s);
+
+		_data->first=aux_buffer[0];
+		aux_buffer[0]->prev_ptr=NULL;
+		aux_buffer[0]->next_ptr=aux_buffer[1];
+
+		_data->last=aux_buffer[s-1];
+		aux_buffer[s-1]->prev_ptr=aux_buffer[s-2];
+		aux_buffer[s-1]->next_ptr=NULL;
+
+		for(int i=1;i<s-1;i++) {
+
+			aux_buffer[i]->prev_ptr=aux_buffer[i-1];
+			aux_buffer[i]->next_ptr=aux_buffer[i+1];
+
+		}
+
+		memdelete_arr(aux_buffer);
+	}
+
+
 	/**
 	 * copy constructor for the list
 	 */

+ 14 - 0
core/math/geometry.h

@@ -511,6 +511,20 @@ public:
 		else
 			return p_segment[0]+n*d; // inside
 	}
+
+	static bool is_point_in_triangle(const Vector2& s, const Vector2& a, const Vector2& b, const Vector2& c)
+	{
+	    int as_x = s.x-a.x;
+	    int as_y = s.y-a.y;
+
+	    bool s_ab = (b.x-a.x)*as_y-(b.y-a.y)*as_x > 0;
+
+	    if((c.x-a.x)*as_y-(c.y-a.y)*as_x > 0 == s_ab) return false;
+
+	    if((c.x-b.x)*(s.y-b.y)-(c.y-b.y)*(s.x-b.x) > 0 != s_ab) return false;
+
+	    return true;
+	}
 	static Vector2 get_closest_point_to_segment_uncapped_2d(const Vector2& p_point, const Vector2 *p_segment) {
 
 		Vector2 p=p_point-p_segment[0];

+ 1550 - 0
core/math/triangulator.cpp

@@ -0,0 +1,1550 @@
+//Copyright (C) 2011 by Ivan Fratric
+//
+//Permission is hereby granted, free of charge, to any person obtaining a copy
+//of this software and associated documentation files (the "Software"), to deal
+//in the Software without restriction, including without limitation the rights
+//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//copies of the Software, and to permit persons to whom the Software is
+//furnished to do so, subject to the following conditions:
+//
+//The above copyright notice and this permission notice shall be included in
+//all copies or substantial portions of the Software.
+//
+//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//THE SOFTWARE.
+
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+#include "triangulator.h"
+
+
+#define TRIANGULATOR_VERTEXTYPE_REGULAR 0
+#define TRIANGULATOR_VERTEXTYPE_START 1
+#define TRIANGULATOR_VERTEXTYPE_END 2
+#define TRIANGULATOR_VERTEXTYPE_SPLIT 3
+#define TRIANGULATOR_VERTEXTYPE_MERGE 4
+
+TriangulatorPoly::TriangulatorPoly() {
+	hole = false;
+	numpoints = 0;
+	points = NULL;
+}
+
+TriangulatorPoly::~TriangulatorPoly() {
+	if(points) delete [] points;
+}
+
+void TriangulatorPoly::Clear() {
+	if(points) delete [] points;
+	hole = false;
+	numpoints = 0;
+	points = NULL;
+}
+
+void TriangulatorPoly::Init(long numpoints) {
+	Clear();
+	this->numpoints = numpoints;
+	points = new Vector2[numpoints];
+}
+
+void TriangulatorPoly::Triangle(Vector2 &p1, Vector2 &p2, Vector2 &p3) {
+	Init(3);
+	points[0] = p1;
+	points[1] = p2;
+	points[2] = p3;
+}
+
+TriangulatorPoly::TriangulatorPoly(const TriangulatorPoly &src) {
+	hole = src.hole;
+	numpoints = src.numpoints;
+	points = new Vector2[numpoints];
+	memcpy(points, src.points, numpoints*sizeof(Vector2));
+}
+
+TriangulatorPoly& TriangulatorPoly::operator=(const TriangulatorPoly &src) {
+	Clear();
+	hole = src.hole;
+	numpoints = src.numpoints;
+	points = new Vector2[numpoints];
+	memcpy(points, src.points, numpoints*sizeof(Vector2));
+	return *this;
+}
+
+int TriangulatorPoly::GetOrientation() {
+	long i1,i2;
+	real_t area = 0;
+	for(i1=0; i1<numpoints; i1++) {
+		i2 = i1+1;
+		if(i2 == numpoints) i2 = 0;
+		area += points[i1].x * points[i2].y - points[i1].y * points[i2].x;
+	}
+	if(area>0) return TRIANGULATOR_CCW;
+	if(area<0) return TRIANGULATOR_CW;
+	return 0;
+}
+
+void TriangulatorPoly::SetOrientation(int orientation) {
+	int polyorientation = GetOrientation();
+	if(polyorientation&&(polyorientation!=orientation)) {
+		Invert();
+	}
+}
+
+void TriangulatorPoly::Invert() {
+	long i;
+	Vector2 *invpoints;
+
+	invpoints = new Vector2[numpoints];
+	for(i=0;i<numpoints;i++) {
+		invpoints[i] = points[numpoints-i-1];
+	}
+
+	delete [] points;
+	points = invpoints;
+}
+
+Vector2 TriangulatorPartition::Normalize(const Vector2 &p) {
+	Vector2 r;
+	real_t n = sqrt(p.x*p.x + p.y*p.y);
+	if(n!=0) {
+		r = p/n;
+	} else {
+		r.x = 0;
+		r.y = 0;
+	}
+	return r;
+}
+
+real_t TriangulatorPartition::Distance(const Vector2 &p1, const Vector2 &p2) {
+	real_t dx,dy;
+	dx = p2.x - p1.x;
+	dy = p2.y - p1.y;
+	return(sqrt(dx*dx + dy*dy));
+}
+
+//checks if two lines intersect
+int TriangulatorPartition::Intersects(Vector2 &p11, Vector2 &p12, Vector2 &p21, Vector2 &p22) {
+	if((p11.x == p21.x)&&(p11.y == p21.y)) return 0;
+	if((p11.x == p22.x)&&(p11.y == p22.y)) return 0;
+	if((p12.x == p21.x)&&(p12.y == p21.y)) return 0;
+	if((p12.x == p22.x)&&(p12.y == p22.y)) return 0;
+
+	Vector2 v1ort,v2ort,v;
+	real_t dot11,dot12,dot21,dot22;
+
+	v1ort.x = p12.y-p11.y;
+	v1ort.y = p11.x-p12.x;
+
+	v2ort.x = p22.y-p21.y;
+	v2ort.y = p21.x-p22.x;
+
+	v = p21-p11;
+	dot21 = v.x*v1ort.x + v.y*v1ort.y;
+	v = p22-p11;
+	dot22 = v.x*v1ort.x + v.y*v1ort.y;
+
+	v = p11-p21;
+	dot11 = v.x*v2ort.x + v.y*v2ort.y;
+	v = p12-p21;
+	dot12 = v.x*v2ort.x + v.y*v2ort.y;
+
+	if(dot11*dot12>0) return 0;
+	if(dot21*dot22>0) return 0;
+
+	return 1;
+}
+
+//removes holes from inpolys by merging them with non-holes
+int TriangulatorPartition::RemoveHoles(List<TriangulatorPoly> *inpolys, List<TriangulatorPoly> *outpolys) {
+	List<TriangulatorPoly> polys;
+	List<TriangulatorPoly>::Element *holeiter,*polyiter,*iter,*iter2;
+	long i,i2,holepointindex,polypointindex;
+	Vector2 holepoint,polypoint,bestpolypoint;
+	Vector2 linep1,linep2;
+	Vector2 v1,v2;
+	TriangulatorPoly newpoly;
+	bool hasholes;
+	bool pointvisible;
+	bool pointfound;
+
+	//check for trivial case (no holes)
+	hasholes = false;
+	for(iter = inpolys->front(); iter; iter=iter->next()) {
+		if(iter->get().IsHole()) {
+			hasholes = true;
+			break;
+		}
+	}
+	if(!hasholes) {
+		for(iter = inpolys->front(); iter; iter=iter->next()) {
+			outpolys->push_back(iter->get());
+		}
+		return 1;
+	}
+
+	polys = *inpolys;
+
+	while(1) {
+		//find the hole point with the largest x
+		hasholes = false;
+		for(iter = polys.front(); iter; iter=iter->next()) {
+			if(!iter->get().IsHole()) continue;
+
+			if(!hasholes) {
+				hasholes = true;
+				holeiter = iter;
+				holepointindex = 0;
+			}
+
+			for(i=0; i < iter->get().GetNumPoints(); i++) {
+				if(iter->get().GetPoint(i).x > holeiter->get().GetPoint(holepointindex).x) {
+					holeiter = iter;
+					holepointindex = i;
+				}
+			}
+		}
+		if(!hasholes) break;
+		holepoint = holeiter->get().GetPoint(holepointindex);
+
+		pointfound = false;
+		for(iter = polys.front(); iter; iter=iter->next()) {
+			if(iter->get().IsHole()) continue;
+			for(i=0; i < iter->get().GetNumPoints(); i++) {
+				if(iter->get().GetPoint(i).x <= holepoint.x) continue;
+				if(!InCone(iter->get().GetPoint((i+iter->get().GetNumPoints()-1)%(iter->get().GetNumPoints())),
+					   iter->get().GetPoint(i),
+					   iter->get().GetPoint((i+1)%(iter->get().GetNumPoints())),
+					   holepoint))
+					continue;
+				polypoint = iter->get().GetPoint(i);
+				if(pointfound) {
+					v1 = Normalize(polypoint-holepoint);
+					v2 = Normalize(bestpolypoint-holepoint);
+					if(v2.x > v1.x) continue;
+				}
+				pointvisible = true;
+				for(iter2 = polys.front(); iter2; iter2=iter2->next()) {
+					if(iter2->get().IsHole()) continue;
+					for(i2=0; i2 < iter2->get().GetNumPoints(); i2++) {
+						linep1 = iter2->get().GetPoint(i2);
+						linep2 = iter2->get().GetPoint((i2+1)%(iter2->get().GetNumPoints()));
+						if(Intersects(holepoint,polypoint,linep1,linep2)) {
+							pointvisible = false;
+							break;
+						}
+					}
+					if(!pointvisible) break;
+				}
+				if(pointvisible) {
+					pointfound = true;
+					bestpolypoint = polypoint;
+					polyiter = iter;
+					polypointindex = i;
+				}
+			}
+		}
+
+		if(!pointfound) return 0;
+
+		newpoly.Init(holeiter->get().GetNumPoints() + polyiter->get().GetNumPoints() + 2);
+		i2 = 0;
+		for(i=0;i<=polypointindex;i++) {
+			newpoly[i2] = polyiter->get().GetPoint(i);
+			i2++;
+		}
+		for(i=0;i<=holeiter->get().GetNumPoints();i++) {
+			newpoly[i2] = holeiter->get().GetPoint((i+holepointindex)%holeiter->get().GetNumPoints());
+			i2++;
+		}
+		for(i=polypointindex;i<polyiter->get().GetNumPoints();i++) {
+			newpoly[i2] = polyiter->get().GetPoint(i);
+			i2++;
+		}
+
+		polys.erase(holeiter);
+		polys.erase(polyiter);
+		polys.push_back(newpoly);
+	}
+
+	for(iter = polys.front(); iter; iter=iter->next()) {
+		outpolys->push_back(iter->get());
+	}
+
+	return 1;
+}
+
+bool TriangulatorPartition::IsConvex(Vector2& p1, Vector2& p2, Vector2& p3) {
+	real_t tmp;
+	tmp = (p3.y-p1.y)*(p2.x-p1.x)-(p3.x-p1.x)*(p2.y-p1.y);
+	if(tmp>0) return 1;
+	else return 0;
+}
+
+bool TriangulatorPartition::IsReflex(Vector2& p1, Vector2& p2, Vector2& p3) {
+	real_t tmp;
+	tmp = (p3.y-p1.y)*(p2.x-p1.x)-(p3.x-p1.x)*(p2.y-p1.y);
+	if(tmp<0) return 1;
+	else return 0;
+}
+
+bool TriangulatorPartition::IsInside(Vector2& p1, Vector2& p2, Vector2& p3, Vector2 &p) {
+	if(IsConvex(p1,p,p2)) return false;
+	if(IsConvex(p2,p,p3)) return false;
+	if(IsConvex(p3,p,p1)) return false;
+	return true;
+}
+
+bool TriangulatorPartition::InCone(Vector2 &p1, Vector2 &p2, Vector2 &p3, Vector2 &p) {
+	bool convex;
+
+	convex = IsConvex(p1,p2,p3);
+
+	if(convex) {
+		if(!IsConvex(p1,p2,p)) return false;
+		if(!IsConvex(p2,p3,p)) return false;
+		return true;
+	} else {
+		if(IsConvex(p1,p2,p)) return true;
+		if(IsConvex(p2,p3,p)) return true;
+		return false;
+	}
+}
+
+bool TriangulatorPartition::InCone(PartitionVertex *v, Vector2 &p) {
+	Vector2 p1,p2,p3;
+
+	p1 = v->previous->p;
+	p2 = v->p;
+	p3 = v->next->p;
+
+	return InCone(p1,p2,p3,p);
+}
+
+void TriangulatorPartition::UpdateVertexReflexity(PartitionVertex *v) {
+	PartitionVertex *v1,*v3;
+	v1 = v->previous;
+	v3 = v->next;
+	v->isConvex = !IsReflex(v1->p,v->p,v3->p);
+}
+
+void TriangulatorPartition::UpdateVertex(PartitionVertex *v, PartitionVertex *vertices, long numvertices) {
+	long i;
+	PartitionVertex *v1,*v3;
+	Vector2 vec1,vec3;
+
+	v1 = v->previous;
+	v3 = v->next;
+
+	v->isConvex = IsConvex(v1->p,v->p,v3->p);
+
+	vec1 = Normalize(v1->p - v->p);
+	vec3 = Normalize(v3->p - v->p);
+	v->angle = vec1.x*vec3.x + vec1.y*vec3.y;
+
+	if(v->isConvex) {
+		v->isEar = true;
+		for(i=0;i<numvertices;i++) {
+			if((vertices[i].p.x==v->p.x)&&(vertices[i].p.y==v->p.y)) continue;
+			if((vertices[i].p.x==v1->p.x)&&(vertices[i].p.y==v1->p.y)) continue;
+			if((vertices[i].p.x==v3->p.x)&&(vertices[i].p.y==v3->p.y)) continue;
+			if(IsInside(v1->p,v->p,v3->p,vertices[i].p)) {
+				v->isEar = false;
+				break;
+			}
+		}
+	} else {
+		v->isEar = false;
+	}
+}
+
+//triangulation by ear removal
+int TriangulatorPartition::Triangulate_EC(TriangulatorPoly *poly, List<TriangulatorPoly> *triangles) {
+	long numvertices;
+	PartitionVertex *vertices;
+	PartitionVertex *ear;
+	TriangulatorPoly triangle;
+	long i,j;
+	bool earfound;
+
+	if(poly->GetNumPoints() < 3) return 0;
+	if(poly->GetNumPoints() == 3) {
+		triangles->push_back(*poly);
+		return 1;
+	}
+
+	numvertices = poly->GetNumPoints();
+
+	vertices = new PartitionVertex[numvertices];
+	for(i=0;i<numvertices;i++) {
+		vertices[i].isActive = true;
+		vertices[i].p = poly->GetPoint(i);
+		if(i==(numvertices-1)) vertices[i].next=&(vertices[0]);
+		else vertices[i].next=&(vertices[i+1]);
+		if(i==0) vertices[i].previous = &(vertices[numvertices-1]);
+		else vertices[i].previous = &(vertices[i-1]);
+	}
+	for(i=0;i<numvertices;i++) {
+		UpdateVertex(&vertices[i],vertices,numvertices);
+	}
+
+	for(i=0;i<numvertices-3;i++) {
+		earfound = false;
+		//find the most extruded ear
+		for(j=0;j<numvertices;j++) {
+			if(!vertices[j].isActive) continue;
+			if(!vertices[j].isEar) continue;
+			if(!earfound) {
+				earfound = true;
+				ear = &(vertices[j]);
+			} else {
+				if(vertices[j].angle > ear->angle) {
+					ear = &(vertices[j]);
+				}
+			}
+		}
+		if(!earfound) {
+			delete [] vertices;
+			return 0;
+		}
+
+		triangle.Triangle(ear->previous->p,ear->p,ear->next->p);
+		triangles->push_back(triangle);
+
+		ear->isActive = false;
+		ear->previous->next = ear->next;
+		ear->next->previous = ear->previous;
+
+		if(i==numvertices-4) break;
+
+		UpdateVertex(ear->previous,vertices,numvertices);
+		UpdateVertex(ear->next,vertices,numvertices);
+	}
+	for(i=0;i<numvertices;i++) {
+		if(vertices[i].isActive) {
+			triangle.Triangle(vertices[i].previous->p,vertices[i].p,vertices[i].next->p);
+			triangles->push_back(triangle);
+			break;
+		}
+	}
+
+	delete [] vertices;
+
+	return 1;
+}
+
+int TriangulatorPartition::Triangulate_EC(List<TriangulatorPoly> *inpolys, List<TriangulatorPoly> *triangles) {
+	List<TriangulatorPoly> outpolys;
+	List<TriangulatorPoly>::Element*iter;
+
+	if(!RemoveHoles(inpolys,&outpolys)) return 0;
+	for(iter=outpolys.front();iter;iter=iter->next()) {
+		if(!Triangulate_EC(&(iter->get()),triangles)) return 0;
+	}
+	return 1;
+}
+
+int TriangulatorPartition::ConvexPartition_HM(TriangulatorPoly *poly, List<TriangulatorPoly> *parts) {
+	List<TriangulatorPoly> triangles;
+	List<TriangulatorPoly>::Element *iter1,*iter2;
+	TriangulatorPoly *poly1,*poly2;
+	TriangulatorPoly newpoly;
+	Vector2 d1,d2,p1,p2,p3;
+	long i11,i12,i21,i22,i13,i23,j,k;
+	bool isdiagonal;
+	long numreflex;
+
+	//check if the poly is already convex
+	numreflex = 0;
+	for(i11=0;i11<poly->GetNumPoints();i11++) {
+		if(i11==0) i12 = poly->GetNumPoints()-1;
+		else i12=i11-1;
+		if(i11==(poly->GetNumPoints()-1)) i13=0;
+		else i13=i11+1;
+		if(IsReflex(poly->GetPoint(i12),poly->GetPoint(i11),poly->GetPoint(i13))) {
+			numreflex = 1;
+			break;
+		}
+	}
+	if(numreflex == 0) {
+		parts->push_back(*poly);
+		return 1;
+	}
+
+	if(!Triangulate_EC(poly,&triangles)) return 0;
+
+	for(iter1 = triangles.front(); iter1 ; iter1=iter1->next()) {
+		poly1 = &(iter1->get());
+		for(i11=0;i11<poly1->GetNumPoints();i11++) {
+			d1 = poly1->GetPoint(i11);
+			i12 = (i11+1)%(poly1->GetNumPoints());
+			d2 = poly1->GetPoint(i12);
+
+			isdiagonal = false;
+			for(iter2 = iter1; iter2 ; iter2=iter2->next()) {
+				if(iter1 == iter2) continue;
+				poly2 = &(iter2->get());
+
+				for(i21=0;i21<poly2->GetNumPoints();i21++) {
+					if((d2.x != poly2->GetPoint(i21).x)||(d2.y != poly2->GetPoint(i21).y)) continue;
+					i22 = (i21+1)%(poly2->GetNumPoints());
+					if((d1.x != poly2->GetPoint(i22).x)||(d1.y != poly2->GetPoint(i22).y)) continue;
+					isdiagonal = true;
+					break;
+				}
+				if(isdiagonal) break;
+			}
+
+			if(!isdiagonal) continue;
+
+			p2 = poly1->GetPoint(i11);
+			if(i11 == 0) i13 = poly1->GetNumPoints()-1;
+			else i13 = i11-1;
+			p1 = poly1->GetPoint(i13);
+			if(i22 == (poly2->GetNumPoints()-1)) i23 = 0;
+			else i23 = i22+1;
+			p3 = poly2->GetPoint(i23);
+
+			if(!IsConvex(p1,p2,p3)) continue;
+
+			p2 = poly1->GetPoint(i12);
+			if(i12 == (poly1->GetNumPoints()-1)) i13 = 0;
+			else i13 = i12+1;
+			p3 = poly1->GetPoint(i13);
+			if(i21 == 0) i23 = poly2->GetNumPoints()-1;
+			else i23 = i21-1;
+			p1 = poly2->GetPoint(i23);
+
+			if(!IsConvex(p1,p2,p3)) continue;
+
+			newpoly.Init(poly1->GetNumPoints()+poly2->GetNumPoints()-2);
+			k = 0;
+			for(j=i12;j!=i11;j=(j+1)%(poly1->GetNumPoints())) {
+				newpoly[k] = poly1->GetPoint(j);
+				k++;
+			}
+			for(j=i22;j!=i21;j=(j+1)%(poly2->GetNumPoints())) {
+				newpoly[k] = poly2->GetPoint(j);
+				k++;
+			}
+
+			triangles.erase(iter2);
+			iter1->get() = newpoly;
+			poly1 = &(iter1->get());
+			i11 = -1;
+
+			continue;
+		}
+	}
+
+	for(iter1 = triangles.front(); iter1 ; iter1=iter1->next()) {
+		parts->push_back(iter1->get());
+	}
+
+	return 1;
+}
+
+int TriangulatorPartition::ConvexPartition_HM(List<TriangulatorPoly> *inpolys, List<TriangulatorPoly> *parts) {
+	List<TriangulatorPoly> outpolys;
+	List<TriangulatorPoly>::Element* iter;
+
+	if(!RemoveHoles(inpolys,&outpolys)) return 0;
+	for(iter=outpolys.front();iter;iter=iter->next()) {
+		if(!ConvexPartition_HM(&(iter->get()),parts)) return 0;
+	}
+	return 1;
+}
+
+//minimum-weight polygon triangulation by dynamic programming
+//O(n^3) time complexity
+//O(n^2) space complexity
+int TriangulatorPartition::Triangulate_OPT(TriangulatorPoly *poly, List<TriangulatorPoly> *triangles) {
+	long i,j,k,gap,n;
+	DPState **dpstates;
+	Vector2 p1,p2,p3,p4;
+	long bestvertex;
+	real_t weight,minweight,d1,d2;
+	Diagonal diagonal,newdiagonal;
+	List<Diagonal> diagonals;
+	TriangulatorPoly triangle;
+	int ret = 1;
+
+	n = poly->GetNumPoints();
+	dpstates = new DPState *[n];
+	for(i=1;i<n;i++) {
+		dpstates[i] = new DPState[i];
+	}
+
+	//init states and visibility
+	for(i=0;i<(n-1);i++) {
+		p1 = poly->GetPoint(i);
+		for(j=i+1;j<n;j++) {
+			dpstates[j][i].visible = true;
+			dpstates[j][i].weight = 0;
+			dpstates[j][i].bestvertex = -1;
+			if(j!=(i+1)) {
+				p2 = poly->GetPoint(j);
+
+				//visibility check
+				if(i==0) p3 = poly->GetPoint(n-1);
+				else p3 = poly->GetPoint(i-1);
+				if(i==(n-1)) p4 = poly->GetPoint(0);
+				else p4 = poly->GetPoint(i+1);
+				if(!InCone(p3,p1,p4,p2)) {
+					dpstates[j][i].visible = false;
+					continue;
+				}
+
+				if(j==0) p3 = poly->GetPoint(n-1);
+				else p3 = poly->GetPoint(j-1);
+				if(j==(n-1)) p4 = poly->GetPoint(0);
+				else p4 = poly->GetPoint(j+1);
+				if(!InCone(p3,p2,p4,p1)) {
+					dpstates[j][i].visible = false;
+					continue;
+				}
+
+				for(k=0;k<n;k++) {
+					p3 = poly->GetPoint(k);
+					if(k==(n-1)) p4 = poly->GetPoint(0);
+					else p4 = poly->GetPoint(k+1);
+					if(Intersects(p1,p2,p3,p4)) {
+						dpstates[j][i].visible = false;
+						break;
+					}
+				}
+			}
+		}
+	}
+	dpstates[n-1][0].visible = true;
+	dpstates[n-1][0].weight = 0;
+	dpstates[n-1][0].bestvertex = -1;
+
+	for(gap = 2; gap<n; gap++) {
+		for(i=0; i<(n-gap); i++) {
+			j = i+gap;
+			if(!dpstates[j][i].visible) continue;
+			bestvertex = -1;
+			for(k=(i+1);k<j;k++) {
+				if(!dpstates[k][i].visible) continue;
+				if(!dpstates[j][k].visible) continue;
+
+				if(k<=(i+1)) d1=0;
+				else d1 = Distance(poly->GetPoint(i),poly->GetPoint(k));
+				if(j<=(k+1)) d2=0;
+				else d2 = Distance(poly->GetPoint(k),poly->GetPoint(j));
+
+				weight = dpstates[k][i].weight + dpstates[j][k].weight + d1 + d2;
+
+				if((bestvertex == -1)||(weight<minweight)) {
+					bestvertex = k;
+					minweight = weight;
+				}
+			}
+			if(bestvertex == -1) {
+				for(i=1;i<n;i++) {
+					delete [] dpstates[i];
+				}
+				delete [] dpstates;
+
+				return 0;
+			}
+
+			dpstates[j][i].bestvertex = bestvertex;
+			dpstates[j][i].weight = minweight;
+		}
+	}
+
+	newdiagonal.index1 = 0;
+	newdiagonal.index2 = n-1;
+	diagonals.push_back(newdiagonal);
+	while(!diagonals.empty()) {
+		diagonal = (diagonals.front()->get());
+		diagonals.pop_front();
+		bestvertex = dpstates[diagonal.index2][diagonal.index1].bestvertex;
+		if(bestvertex == -1) {
+			ret = 0;
+			break;
+		}
+		triangle.Triangle(poly->GetPoint(diagonal.index1),poly->GetPoint(bestvertex),poly->GetPoint(diagonal.index2));
+		triangles->push_back(triangle);
+		if(bestvertex > (diagonal.index1+1)) {
+			newdiagonal.index1 = diagonal.index1;
+			newdiagonal.index2 = bestvertex;
+			diagonals.push_back(newdiagonal);
+		}
+		if(diagonal.index2 > (bestvertex+1)) {
+			newdiagonal.index1 = bestvertex;
+			newdiagonal.index2 = diagonal.index2;
+			diagonals.push_back(newdiagonal);
+		}
+	}
+
+	for(i=1;i<n;i++) {
+		delete [] dpstates[i];
+	}
+	delete [] dpstates;
+
+	return ret;
+}
+
+void TriangulatorPartition::UpdateState(long a, long b, long w, long i, long j, DPState2 **dpstates) {
+	Diagonal newdiagonal;
+	List<Diagonal> *pairs;
+	long w2;
+
+	w2 = dpstates[a][b].weight;
+	if(w>w2) return;
+
+	pairs = &(dpstates[a][b].pairs);
+	newdiagonal.index1 = i;
+	newdiagonal.index2 = j;
+
+	if(w<w2) {
+		pairs->clear();
+		pairs->push_front(newdiagonal);
+		dpstates[a][b].weight = w;
+	} else {
+		if((!pairs->empty())&&(i <= pairs->front()->get().index1)) return;
+		while((!pairs->empty())&&(pairs->front()->get().index2 >= j)) pairs->pop_front();
+		pairs->push_front(newdiagonal);
+	}
+}
+
+void TriangulatorPartition::TypeA(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates) {
+	List<Diagonal> *pairs;
+	List<Diagonal>::Element *iter,*lastiter;
+	long top;
+	long w;
+
+	if(!dpstates[i][j].visible) return;
+	top = j;
+	w = dpstates[i][j].weight;
+	if(k-j > 1) {
+		if (!dpstates[j][k].visible) return;
+		w += dpstates[j][k].weight + 1;
+	}
+	if(j-i > 1) {
+		pairs = &(dpstates[i][j].pairs);
+		iter = NULL;
+		lastiter = NULL;
+		while(iter!=pairs->front()) {
+			if (!iter)
+				iter=pairs->back();
+			else
+				iter=iter->prev();
+
+			if(!IsReflex(vertices[iter->get().index2].p,vertices[j].p,vertices[k].p)) lastiter = iter;
+			else break;
+		}
+		if(lastiter == NULL) w++;
+		else {
+			if(IsReflex(vertices[k].p,vertices[i].p,vertices[lastiter->get().index1].p)) w++;
+			else top = lastiter->get().index1;
+		}
+	}
+	UpdateState(i,k,w,top,j,dpstates);
+}
+
+void TriangulatorPartition::TypeB(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates) {
+	List<Diagonal> *pairs;
+	List<Diagonal>::Element* iter,*lastiter;
+	long top;
+	long w;
+
+	if(!dpstates[j][k].visible) return;
+	top = j;
+	w = dpstates[j][k].weight;
+
+	if (j-i > 1) {
+		if (!dpstates[i][j].visible) return;
+		w += dpstates[i][j].weight + 1;
+	}
+	if (k-j > 1) {
+		pairs = &(dpstates[j][k].pairs);
+
+		iter = pairs->front();
+		if((!pairs->empty())&&(!IsReflex(vertices[i].p,vertices[j].p,vertices[iter->get().index1].p))) {
+			lastiter = iter;
+			while(iter!=NULL) {
+				if(!IsReflex(vertices[i].p,vertices[j].p,vertices[iter->get().index1].p)) {
+					lastiter = iter;
+					iter=iter->next();
+				}
+				else break;
+			}
+			if(IsReflex(vertices[lastiter->get().index2].p,vertices[k].p,vertices[i].p)) w++;
+			else top = lastiter->get().index2;
+		} else w++;
+	}
+	UpdateState(i,k,w,j,top,dpstates);
+}
+
+int TriangulatorPartition::ConvexPartition_OPT(TriangulatorPoly *poly, List<TriangulatorPoly> *parts) {
+	Vector2 p1,p2,p3,p4;
+	PartitionVertex *vertices;
+	DPState2 **dpstates;
+	long i,j,k,n,gap;
+	List<Diagonal> diagonals,diagonals2;
+	Diagonal diagonal,newdiagonal;
+	List<Diagonal> *pairs,*pairs2;
+	List<Diagonal>::Element* iter,*iter2;
+	int ret;
+	TriangulatorPoly newpoly;
+	List<long> indices;
+	List<long>::Element* iiter;
+	bool ijreal,jkreal;
+
+	n = poly->GetNumPoints();
+	vertices = new PartitionVertex[n];
+
+	dpstates = new DPState2 *[n];
+	for(i=0;i<n;i++) {
+		dpstates[i] = new DPState2[n];
+	}
+
+	//init vertex information
+	for(i=0;i<n;i++) {
+		vertices[i].p = poly->GetPoint(i);
+		vertices[i].isActive = true;
+		if(i==0) vertices[i].previous = &(vertices[n-1]);
+		else vertices[i].previous = &(vertices[i-1]);
+		if(i==(poly->GetNumPoints()-1)) vertices[i].next = &(vertices[0]);
+		else vertices[i].next = &(vertices[i+1]);
+	}
+	for(i=1;i<n;i++) {
+		UpdateVertexReflexity(&(vertices[i]));
+	}
+
+	//init states and visibility
+	for(i=0;i<(n-1);i++) {
+		p1 = poly->GetPoint(i);
+		for(j=i+1;j<n;j++) {
+			dpstates[i][j].visible = true;
+			if(j==i+1) {
+				dpstates[i][j].weight = 0;
+			} else {
+				dpstates[i][j].weight = 2147483647;
+			}
+			if(j!=(i+1)) {
+				p2 = poly->GetPoint(j);
+
+				//visibility check
+				if(!InCone(&vertices[i],p2)) {
+					dpstates[i][j].visible = false;
+					continue;
+				}
+				if(!InCone(&vertices[j],p1)) {
+					dpstates[i][j].visible = false;
+					continue;
+				}
+
+				for(k=0;k<n;k++) {
+					p3 = poly->GetPoint(k);
+					if(k==(n-1)) p4 = poly->GetPoint(0);
+					else p4 = poly->GetPoint(k+1);
+					if(Intersects(p1,p2,p3,p4)) {
+						dpstates[i][j].visible = false;
+						break;
+					}
+				}
+			}
+		}
+	}
+	for(i=0;i<(n-2);i++) {
+		j = i+2;
+		if(dpstates[i][j].visible) {
+			dpstates[i][j].weight = 0;
+			newdiagonal.index1 = i+1;
+			newdiagonal.index2 = i+1;
+			dpstates[i][j].pairs.push_back(newdiagonal);
+		}
+	}
+
+	dpstates[0][n-1].visible = true;
+	vertices[0].isConvex = false; //by convention
+
+	for(gap=3; gap<n; gap++) {
+		for(i=0;i<n-gap;i++) {
+			if(vertices[i].isConvex) continue;
+			k = i+gap;
+			if(dpstates[i][k].visible) {
+				if(!vertices[k].isConvex) {
+					for(j=i+1;j<k;j++) TypeA(i,j,k,vertices,dpstates);
+				} else {
+					for(j=i+1;j<(k-1);j++) {
+						if(vertices[j].isConvex) continue;
+						TypeA(i,j,k,vertices,dpstates);
+					}
+					TypeA(i,k-1,k,vertices,dpstates);
+				}
+			}
+		}
+		for(k=gap;k<n;k++) {
+			if(vertices[k].isConvex) continue;
+			i = k-gap;
+			if((vertices[i].isConvex)&&(dpstates[i][k].visible)) {
+				TypeB(i,i+1,k,vertices,dpstates);
+				for(j=i+2;j<k;j++) {
+					if(vertices[j].isConvex) continue;
+					TypeB(i,j,k,vertices,dpstates);
+				}
+			}
+		}
+	}
+
+
+	//recover solution
+	ret = 1;
+	newdiagonal.index1 = 0;
+	newdiagonal.index2 = n-1;
+	diagonals.push_front(newdiagonal);
+	while(!diagonals.empty()) {
+		diagonal = (diagonals.front()->get());
+		diagonals.pop_front();
+		if((diagonal.index2 - diagonal.index1) <=1) continue;
+		pairs = &(dpstates[diagonal.index1][diagonal.index2].pairs);
+		if(pairs->empty()) {
+			ret = 0;
+			break;
+		}
+		if(!vertices[diagonal.index1].isConvex) {
+			iter = pairs->back();
+
+			j = iter->get().index2;
+			newdiagonal.index1 = j;
+			newdiagonal.index2 = diagonal.index2;
+			diagonals.push_front(newdiagonal);
+			if((j - diagonal.index1)>1) {
+				if(iter->get().index1 != iter->get().index2) {
+					pairs2 = &(dpstates[diagonal.index1][j].pairs);
+					while(1) {
+						if(pairs2->empty()) {
+							ret = 0;
+							break;
+						}
+						iter2 = pairs2->back();
+
+						if(iter->get().index1 != iter2->get().index1) pairs2->pop_back();
+						else break;
+					}
+					if(ret == 0) break;
+				}
+				newdiagonal.index1 = diagonal.index1;
+				newdiagonal.index2 = j;
+				diagonals.push_front(newdiagonal);
+			}
+		} else {
+			iter = pairs->front();
+			j = iter->get().index1;
+			newdiagonal.index1 = diagonal.index1;
+			newdiagonal.index2 = j;
+			diagonals.push_front(newdiagonal);
+			if((diagonal.index2 - j) > 1) {
+				if(iter->get().index1 != iter->get().index2) {
+					pairs2 = &(dpstates[j][diagonal.index2].pairs);
+					while(1) {
+						if(pairs2->empty()) {
+							ret = 0;
+							break;
+						}
+						iter2 = pairs2->front();
+						if(iter->get().index2 != iter2->get().index2) pairs2->pop_front();
+						else break;
+					}
+					if(ret == 0) break;
+				}
+				newdiagonal.index1 = j;
+				newdiagonal.index2 = diagonal.index2;
+				diagonals.push_front(newdiagonal);
+			}
+		}
+	}
+
+	if(ret == 0) {
+		for(i=0;i<n;i++) {
+			delete [] dpstates[i];
+		}
+		delete [] dpstates;
+		delete [] vertices;
+
+		return ret;
+	}
+
+	newdiagonal.index1 = 0;
+	newdiagonal.index2 = n-1;
+	diagonals.push_front(newdiagonal);
+	while(!diagonals.empty()) {
+		diagonal = (diagonals.front())->get();
+		diagonals.pop_front();
+		if((diagonal.index2 - diagonal.index1) <= 1) continue;
+
+		indices.clear();
+		diagonals2.clear();
+		indices.push_back(diagonal.index1);
+		indices.push_back(diagonal.index2);
+		diagonals2.push_front(diagonal);
+
+		while(!diagonals2.empty()) {
+			diagonal = (diagonals2.front()->get());
+			diagonals2.pop_front();
+			if((diagonal.index2 - diagonal.index1) <= 1) continue;
+			ijreal = true;
+			jkreal = true;
+			pairs = &(dpstates[diagonal.index1][diagonal.index2].pairs);
+			if(!vertices[diagonal.index1].isConvex) {
+				iter = pairs->back();
+				j = iter->get().index2;
+				if(iter->get().index1 != iter->get().index2) ijreal = false;
+			} else {
+				iter = pairs->front();
+				j = iter->get().index1;
+				if(iter->get().index1 != iter->get().index2) jkreal = false;
+			}
+
+			newdiagonal.index1 = diagonal.index1;
+			newdiagonal.index2 = j;
+			if(ijreal) {
+				diagonals.push_back(newdiagonal);
+			} else {
+				diagonals2.push_back(newdiagonal);
+			}
+
+			newdiagonal.index1 = j;
+			newdiagonal.index2 = diagonal.index2;
+			if(jkreal) {
+				diagonals.push_back(newdiagonal);
+			} else {
+				diagonals2.push_back(newdiagonal);
+			}
+
+			indices.push_back(j);
+		}
+
+		indices.sort();
+		newpoly.Init((long)indices.size());
+		k=0;
+		for(iiter = indices.front();iiter;iiter=iiter->next()) {
+			newpoly[k] = vertices[iiter->get()].p;
+			k++;
+		}
+		parts->push_back(newpoly);
+	}
+
+	for(i=0;i<n;i++) {
+		delete [] dpstates[i];
+	}
+	delete [] dpstates;
+	delete [] vertices;
+
+	return ret;
+}
+
+//triangulates a set of polygons by first partitioning them into monotone polygons
+//O(n*log(n)) time complexity, O(n) space complexity
+//the algorithm used here is outlined in the book
+//"Computational Geometry: Algorithms and Applications"
+//by Mark de Berg, Otfried Cheong, Marc van Kreveld and Mark Overmars
+int TriangulatorPartition::MonotonePartition(List<TriangulatorPoly> *inpolys, List<TriangulatorPoly> *monotonePolys) {
+	List<TriangulatorPoly>::Element *iter;
+	MonotoneVertex *vertices;
+	long i,numvertices,vindex,vindex2,newnumvertices,maxnumvertices;
+	long polystartindex, polyendindex;
+	TriangulatorPoly *poly;
+	MonotoneVertex *v,*v2,*vprev,*vnext;
+	ScanLineEdge newedge;
+	bool error = false;
+
+	numvertices = 0;
+	for(iter = inpolys->front(); iter ; iter=iter->next()) {
+		numvertices += iter->get().GetNumPoints();
+	}
+
+	maxnumvertices = numvertices*3;
+	vertices = new MonotoneVertex[maxnumvertices];
+	newnumvertices = numvertices;
+
+	polystartindex = 0;
+	for(iter = inpolys->front(); iter ; iter=iter->next()) {
+		poly = &(iter->get());
+		polyendindex = polystartindex + poly->GetNumPoints()-1;
+		for(i=0;i<poly->GetNumPoints();i++) {
+			vertices[i+polystartindex].p = poly->GetPoint(i);
+			if(i==0) vertices[i+polystartindex].previous = polyendindex;
+			else vertices[i+polystartindex].previous = i+polystartindex-1;
+			if(i==(poly->GetNumPoints()-1)) vertices[i+polystartindex].next = polystartindex;
+			else vertices[i+polystartindex].next = i+polystartindex+1;
+		}
+		polystartindex = polyendindex+1;
+	}
+
+	//construct the priority queue
+	long *priority = new long [numvertices];
+	for(i=0;i<numvertices;i++) priority[i] = i;
+	SortArray<long,VertexSorter> sorter;
+	sorter.compare.vertices=vertices;
+	sorter.sort(priority,numvertices);
+
+	//determine vertex types
+	char *vertextypes = new char[maxnumvertices];
+	for(i=0;i<numvertices;i++) {
+		v = &(vertices[i]);
+		vprev = &(vertices[v->previous]);
+		vnext = &(vertices[v->next]);
+
+		if(Below(vprev->p,v->p)&&Below(vnext->p,v->p)) {
+			if(IsConvex(vnext->p,vprev->p,v->p)) {
+				vertextypes[i] = TRIANGULATOR_VERTEXTYPE_START;
+			} else {
+				vertextypes[i] = TRIANGULATOR_VERTEXTYPE_SPLIT;
+			}
+		} else if(Below(v->p,vprev->p)&&Below(v->p,vnext->p)) {
+			if(IsConvex(vnext->p,vprev->p,v->p))
+			{
+				vertextypes[i] = TRIANGULATOR_VERTEXTYPE_END;
+			} else {
+				vertextypes[i] = TRIANGULATOR_VERTEXTYPE_MERGE;
+			}
+		} else {
+			vertextypes[i] = TRIANGULATOR_VERTEXTYPE_REGULAR;
+		}
+	}
+
+	//helpers
+	long *helpers = new long[maxnumvertices];
+
+	//binary search tree that holds edges intersecting the scanline
+	//note that while set doesn't actually have to be implemented as a tree
+	//complexity requirements for operations are the same as for the balanced binary search tree
+	Set<ScanLineEdge> edgeTree;
+	//store iterators to the edge tree elements
+	//this makes deleting existing edges much faster
+	Set<ScanLineEdge>::Element **edgeTreeIterators,*edgeIter;
+	edgeTreeIterators = new Set<ScanLineEdge>::Element*[maxnumvertices];
+//	Pair<Set<ScanLineEdge>::Element*,bool> edgeTreeRet;
+	for(i = 0; i<numvertices; i++) edgeTreeIterators[i] = NULL;
+
+	//for each vertex
+	for(i=0;i<numvertices;i++) {
+		vindex = priority[i];
+		v = &(vertices[vindex]);
+		vindex2 = vindex;
+		v2 = v;
+
+		//depending on the vertex type, do the appropriate action
+		//comments in the following sections are copied from "Computational Geometry: Algorithms and Applications"
+		switch(vertextypes[vindex]) {
+			case TRIANGULATOR_VERTEXTYPE_START:
+				//Insert ei in T and set helper(ei) to vi.
+				newedge.p1 = v->p;
+				newedge.p2 = vertices[v->next].p;
+				newedge.index = vindex;
+				edgeTreeIterators[vindex] = edgeTree.insert(newedge);
+				helpers[vindex] = vindex;
+				break;
+
+			case TRIANGULATOR_VERTEXTYPE_END:
+				//if helper(ei-1) is a merge vertex
+				if(vertextypes[helpers[v->previous]]==TRIANGULATOR_VERTEXTYPE_MERGE) {
+					//Insert the diagonal connecting vi to helper(ei-1) in D.
+					AddDiagonal(vertices,&newnumvertices,vindex,helpers[v->previous],
+							vertextypes, edgeTreeIterators, &edgeTree, helpers);
+				}
+				//Delete ei-1 from T
+				edgeTree.erase(edgeTreeIterators[v->previous]);
+				break;
+
+			case TRIANGULATOR_VERTEXTYPE_SPLIT:
+				//Search in T to find the edge e j directly left of vi.
+				newedge.p1 = v->p;
+				newedge.p2 = v->p;
+				edgeIter = edgeTree.lower_bound(newedge);
+				if(edgeIter == edgeTree.front()) {
+					error = true;
+					break;
+				}
+				edgeIter=edgeIter->prev();
+				//Insert the diagonal connecting vi to helper(ej) in D.
+				AddDiagonal(vertices,&newnumvertices,vindex,helpers[edgeIter->get().index],
+						vertextypes, edgeTreeIterators, &edgeTree, helpers);
+				vindex2 = newnumvertices-2;
+				v2 = &(vertices[vindex2]);
+				//helper(e j)�vi
+				helpers[edgeIter->get().index] = vindex;
+				//Insert ei in T and set helper(ei) to vi.
+				newedge.p1 = v2->p;
+				newedge.p2 = vertices[v2->next].p;
+				newedge.index = vindex2;
+
+				edgeTreeIterators[vindex2] = edgeTree.insert(newedge);
+				helpers[vindex2] = vindex2;
+				break;
+
+			case TRIANGULATOR_VERTEXTYPE_MERGE:
+				//if helper(ei-1) is a merge vertex
+				if(vertextypes[helpers[v->previous]]==TRIANGULATOR_VERTEXTYPE_MERGE) {
+					//Insert the diagonal connecting vi to helper(ei-1) in D.
+					AddDiagonal(vertices,&newnumvertices,vindex,helpers[v->previous],
+							vertextypes, edgeTreeIterators, &edgeTree, helpers);
+					vindex2 = newnumvertices-2;
+					v2 = &(vertices[vindex2]);
+				}
+				//Delete ei-1 from T.
+				edgeTree.erase(edgeTreeIterators[v->previous]);
+				//Search in T to find the edge e j directly left of vi.
+				newedge.p1 = v->p;
+				newedge.p2 = v->p;
+				edgeIter = edgeTree.lower_bound(newedge);
+				if(edgeIter == edgeTree.front()) {
+					error = true;
+					break;
+				}
+				edgeIter=edgeIter->prev();
+				//if helper(ej) is a merge vertex
+				if(vertextypes[helpers[edgeIter->get().index]]==TRIANGULATOR_VERTEXTYPE_MERGE) {
+					//Insert the diagonal connecting vi to helper(e j) in D.
+					AddDiagonal(vertices,&newnumvertices,vindex2,helpers[edgeIter->get().index],
+							vertextypes, edgeTreeIterators, &edgeTree, helpers);
+				}
+				//helper(e j)�vi
+				helpers[edgeIter->get().index] = vindex2;
+				break;
+
+			case TRIANGULATOR_VERTEXTYPE_REGULAR:
+				//if the interior of P lies to the right of vi
+				if(Below(v->p,vertices[v->previous].p)) {
+					//if helper(ei-1) is a merge vertex
+					if(vertextypes[helpers[v->previous]]==TRIANGULATOR_VERTEXTYPE_MERGE) {
+						//Insert the diagonal connecting vi to helper(ei-1) in D.
+						AddDiagonal(vertices,&newnumvertices,vindex,helpers[v->previous],
+								vertextypes, edgeTreeIterators, &edgeTree, helpers);
+						vindex2 = newnumvertices-2;
+						v2 = &(vertices[vindex2]);
+					}
+					//Delete ei-1 from T.
+					edgeTree.erase(edgeTreeIterators[v->previous]);
+					//Insert ei in T and set helper(ei) to vi.
+					newedge.p1 = v2->p;
+					newedge.p2 = vertices[v2->next].p;
+					newedge.index = vindex2;
+					edgeTreeIterators[vindex2] = edgeTree.insert(newedge);
+					helpers[vindex2] = vindex;
+				} else {
+					//Search in T to find the edge ej directly left of vi.
+					newedge.p1 = v->p;
+					newedge.p2 = v->p;
+					edgeIter = edgeTree.lower_bound(newedge);
+					if(edgeIter == edgeTree.front()) {
+						error = true;
+						break;
+					}
+					edgeIter=edgeIter->prev();
+					//if helper(ej) is a merge vertex
+					if(vertextypes[helpers[edgeIter->get().index]]==TRIANGULATOR_VERTEXTYPE_MERGE) {
+						//Insert the diagonal connecting vi to helper(e j) in D.
+						AddDiagonal(vertices,&newnumvertices,vindex,helpers[edgeIter->get().index],
+								vertextypes, edgeTreeIterators, &edgeTree, helpers);
+					}
+					//helper(e j)�vi
+					helpers[edgeIter->get().index] = vindex;
+				}
+				break;
+		}
+
+		if(error) break;
+	}
+
+	char *used = new char[newnumvertices];
+	memset(used,0,newnumvertices*sizeof(char));
+
+	if(!error) {
+		//return result
+		long size;
+		TriangulatorPoly mpoly;
+		for(i=0;i<newnumvertices;i++) {
+			if(used[i]) continue;
+			v = &(vertices[i]);
+			vnext = &(vertices[v->next]);
+			size = 1;
+			while(vnext!=v) {
+				vnext = &(vertices[vnext->next]);
+				size++;
+			}
+			mpoly.Init(size);
+			v = &(vertices[i]);
+			mpoly[0] = v->p;
+			vnext = &(vertices[v->next]);
+			size = 1;
+			used[i] = 1;
+			used[v->next] = 1;
+			while(vnext!=v) {
+				mpoly[size] = vnext->p;
+				used[vnext->next] = 1;
+				vnext = &(vertices[vnext->next]);
+				size++;
+			}
+			monotonePolys->push_back(mpoly);
+		}
+	}
+
+	//cleanup
+	delete [] vertices;
+	delete [] priority;
+	delete [] vertextypes;
+	delete [] edgeTreeIterators;
+	delete [] helpers;
+	delete [] used;
+
+	if(error) {
+		return 0;
+	} else {
+		return 1;
+	}
+}
+
+//adds a diagonal to the doubly-connected list of vertices
+void TriangulatorPartition::AddDiagonal(MonotoneVertex *vertices, long *numvertices, long index1, long index2,
+					char *vertextypes, Set<ScanLineEdge>::Element **edgeTreeIterators,
+					Set<ScanLineEdge> *edgeTree, long *helpers)
+{
+	long newindex1,newindex2;
+
+	newindex1 = *numvertices;
+	(*numvertices)++;
+	newindex2 = *numvertices;
+	(*numvertices)++;
+
+	vertices[newindex1].p = vertices[index1].p;
+	vertices[newindex2].p = vertices[index2].p;
+
+	vertices[newindex2].next = vertices[index2].next;
+	vertices[newindex1].next = vertices[index1].next;
+
+	vertices[vertices[index2].next].previous = newindex2;
+	vertices[vertices[index1].next].previous = newindex1;
+
+	vertices[index1].next = newindex2;
+	vertices[newindex2].previous = index1;
+
+	vertices[index2].next = newindex1;
+	vertices[newindex1].previous = index2;
+
+	//update all relevant structures
+	vertextypes[newindex1] = vertextypes[index1];
+	edgeTreeIterators[newindex1] = edgeTreeIterators[index1];
+	helpers[newindex1] = helpers[index1];
+	if(edgeTreeIterators[newindex1] != NULL)
+		edgeTreeIterators[newindex1]->get().index = newindex1;
+	vertextypes[newindex2] = vertextypes[index2];
+	edgeTreeIterators[newindex2] = edgeTreeIterators[index2];
+	helpers[newindex2] = helpers[index2];
+	if(edgeTreeIterators[newindex2] != NULL)
+		edgeTreeIterators[newindex2]->get().index = newindex2;
+}
+
+bool TriangulatorPartition::Below(Vector2 &p1, Vector2 &p2) {
+	if(p1.y < p2.y) return true;
+	else if(p1.y == p2.y) {
+		if(p1.x < p2.x) return true;
+	}
+	return false;
+}
+
+
+
+
+
+//sorts in the falling order of y values, if y is equal, x is used instead
+bool TriangulatorPartition::VertexSorter::operator() (long index1, long index2) const {
+	if(vertices[index1].p.y > vertices[index2].p.y) return true;
+	else if(vertices[index1].p.y == vertices[index2].p.y) {
+		if(vertices[index1].p.x > vertices[index2].p.x) return true;
+	}
+	return false;
+}
+
+bool TriangulatorPartition::ScanLineEdge::IsConvex(const Vector2& p1, const Vector2& p2, const Vector2& p3) const {
+	real_t tmp;
+	tmp = (p3.y-p1.y)*(p2.x-p1.x)-(p3.x-p1.x)*(p2.y-p1.y);
+	if(tmp>0) return 1;
+	else return 0;
+}
+
+bool TriangulatorPartition::ScanLineEdge::operator < (const ScanLineEdge & other) const {
+	if(other.p1.y == other.p2.y) {
+		if(p1.y == p2.y) {
+			if(p1.y < other.p1.y) return true;
+			else return false;
+		}
+		if(IsConvex(p1,p2,other.p1)) return true;
+		else return false;
+	} else if(p1.y == p2.y) {
+		if(IsConvex(other.p1,other.p2,p1)) return false;
+		else return true;
+	} else if(p1.y < other.p1.y) {
+		if(IsConvex(other.p1,other.p2,p1)) return false;
+		else return true;
+	} else {
+		if(IsConvex(p1,p2,other.p1)) return true;
+		else return false;
+	}
+}
+
+//triangulates monotone polygon
+//O(n) time, O(n) space complexity
+int TriangulatorPartition::TriangulateMonotone(TriangulatorPoly *inPoly, List<TriangulatorPoly> *triangles) {
+	long i,i2,j,topindex,bottomindex,leftindex,rightindex,vindex;
+	Vector2 *points;
+	long numpoints;
+	TriangulatorPoly triangle;
+
+	numpoints = inPoly->GetNumPoints();
+	points = inPoly->GetPoints();
+
+	//trivial calses
+	if(numpoints < 3) return 0;
+	if(numpoints == 3) {
+		triangles->push_back(*inPoly);
+	}
+
+	topindex = 0; bottomindex=0;
+	for(i=1;i<numpoints;i++) {
+		if(Below(points[i],points[bottomindex])) bottomindex = i;
+		if(Below(points[topindex],points[i])) topindex = i;
+	}
+
+	//check if the poly is really monotone
+	i = topindex;
+	while(i!=bottomindex) {
+		i2 = i+1; if(i2>=numpoints) i2 = 0;
+		if(!Below(points[i2],points[i])) return 0;
+		i = i2;
+	}
+	i = bottomindex;
+	while(i!=topindex) {
+		i2 = i+1; if(i2>=numpoints) i2 = 0;
+		if(!Below(points[i],points[i2])) return 0;
+		i = i2;
+	}
+
+	char *vertextypes = new char[numpoints];
+	long *priority = new long[numpoints];
+
+	//merge left and right vertex chains
+	priority[0] = topindex;
+	vertextypes[topindex] = 0;
+	leftindex = topindex+1; if(leftindex>=numpoints) leftindex = 0;
+	rightindex = topindex-1; if(rightindex<0) rightindex = numpoints-1;
+	for(i=1;i<(numpoints-1);i++) {
+		if(leftindex==bottomindex) {
+			priority[i] = rightindex;
+			rightindex--; if(rightindex<0) rightindex = numpoints-1;
+			vertextypes[priority[i]] = -1;
+		} else if(rightindex==bottomindex) {
+			priority[i] = leftindex;
+			leftindex++;  if(leftindex>=numpoints) leftindex = 0;
+			vertextypes[priority[i]] = 1;
+		} else {
+			if(Below(points[leftindex],points[rightindex])) {
+				priority[i] = rightindex;
+				rightindex--; if(rightindex<0) rightindex = numpoints-1;
+				vertextypes[priority[i]] = -1;
+			} else {
+				priority[i] = leftindex;
+				leftindex++;  if(leftindex>=numpoints) leftindex = 0;
+				vertextypes[priority[i]] = 1;
+			}
+		}
+	}
+	priority[i] = bottomindex;
+	vertextypes[bottomindex] = 0;
+
+	long *stack = new long[numpoints];
+	long stackptr = 0;
+
+	stack[0] = priority[0];
+	stack[1] = priority[1];
+	stackptr = 2;
+
+	//for each vertex from top to bottom trim as many triangles as possible
+	for(i=2;i<(numpoints-1);i++) {
+		vindex = priority[i];
+		if(vertextypes[vindex]!=vertextypes[stack[stackptr-1]]) {
+			for(j=0;j<(stackptr-1);j++) {
+				if(vertextypes[vindex]==1) {
+					triangle.Triangle(points[stack[j+1]],points[stack[j]],points[vindex]);
+				} else {
+					triangle.Triangle(points[stack[j]],points[stack[j+1]],points[vindex]);
+				}
+				triangles->push_back(triangle);
+			}
+			stack[0] = priority[i-1];
+			stack[1] = priority[i];
+			stackptr = 2;
+		} else {
+			stackptr--;
+			while(stackptr>0) {
+				if(vertextypes[vindex]==1) {
+					if(IsConvex(points[vindex],points[stack[stackptr-1]],points[stack[stackptr]])) {
+						triangle.Triangle(points[vindex],points[stack[stackptr-1]],points[stack[stackptr]]);
+						triangles->push_back(triangle);
+						stackptr--;
+					} else {
+						break;
+					}
+				} else {
+					if(IsConvex(points[vindex],points[stack[stackptr]],points[stack[stackptr-1]])) {
+						triangle.Triangle(points[vindex],points[stack[stackptr]],points[stack[stackptr-1]]);
+						triangles->push_back(triangle);
+						stackptr--;
+					} else {
+						break;
+					}
+				}
+			}
+			stackptr++;
+			stack[stackptr] = vindex;
+			stackptr++;
+		}
+	}
+	vindex = priority[i];
+	for(j=0;j<(stackptr-1);j++) {
+		if(vertextypes[stack[j+1]]==1) {
+			triangle.Triangle(points[stack[j]],points[stack[j+1]],points[vindex]);
+		} else {
+			triangle.Triangle(points[stack[j+1]],points[stack[j]],points[vindex]);
+		}
+		triangles->push_back(triangle);
+	}
+
+	delete [] priority;
+	delete [] vertextypes;
+	delete [] stack;
+
+	return 1;
+}
+
+int TriangulatorPartition::Triangulate_MONO(List<TriangulatorPoly> *inpolys, List<TriangulatorPoly> *triangles) {
+	List<TriangulatorPoly> monotone;
+	List<TriangulatorPoly>::Element* iter;
+
+	if(!MonotonePartition(inpolys,&monotone)) return 0;
+	for(iter = monotone.front(); iter;iter=iter->next()) {
+		if(!TriangulateMonotone(&(iter->get()),triangles)) return 0;
+	}
+	return 1;
+}
+
+int TriangulatorPartition::Triangulate_MONO(TriangulatorPoly *poly, List<TriangulatorPoly> *triangles) {
+	List<TriangulatorPoly> polys;
+	polys.push_back(*poly);
+
+	return Triangulate_MONO(&polys, triangles);
+}

+ 306 - 0
core/math/triangulator.h

@@ -0,0 +1,306 @@
+//Copyright (C) 2011 by Ivan Fratric
+//
+//Permission is hereby granted, free of charge, to any person obtaining a copy
+//of this software and associated documentation files (the "Software"), to deal
+//in the Software without restriction, including without limitation the rights
+//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//copies of the Software, and to permit persons to whom the Software is
+//furnished to do so, subject to the following conditions:
+//
+//The above copyright notice and this permission notice shall be included in
+//all copies or substantial portions of the Software.
+//
+//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//THE SOFTWARE.
+
+#ifndef TRIANGULATOR_H
+#define TRIANGULATOR_H
+
+#include "math_2d.h"
+#include "list.h"
+#include "set.h"
+//2D point structure
+
+
+#define TRIANGULATOR_CCW 1
+#define TRIANGULATOR_CW -1
+//Polygon implemented as an array of points with a 'hole' flag
+class TriangulatorPoly {
+protected:
+
+
+
+	Vector2 *points;
+	long numpoints;
+	bool hole;
+
+public:
+
+	//constructors/destructors
+	TriangulatorPoly();
+	~TriangulatorPoly();
+
+	TriangulatorPoly(const TriangulatorPoly &src);
+	TriangulatorPoly& operator=(const TriangulatorPoly &src);
+
+	//getters and setters
+	long GetNumPoints() {
+		return numpoints;
+	}
+
+	bool IsHole() {
+		return hole;
+	}
+
+	void SetHole(bool hole) {
+		this->hole = hole;
+	}
+
+	Vector2 &GetPoint(long i) {
+		return points[i];
+	}
+
+	Vector2 *GetPoints() {
+		return points;
+	}
+
+	Vector2& operator[] (int i) {
+		return points[i];
+	}
+
+	//clears the polygon points
+	void Clear();
+
+	//inits the polygon with numpoints vertices
+	void Init(long numpoints);
+
+	//creates a triangle with points p1,p2,p3
+	void Triangle(Vector2 &p1, Vector2 &p2, Vector2 &p3);
+
+	//inverts the orfer of vertices
+	void Invert();
+
+	//returns the orientation of the polygon
+	//possible values:
+	//   Triangulator_CCW : polygon vertices are in counter-clockwise order
+	//   Triangulator_CW : polygon vertices are in clockwise order
+	//       0 : the polygon has no (measurable) area
+	int GetOrientation();
+
+	//sets the polygon orientation
+	//orientation can be
+	//   Triangulator_CCW : sets vertices in counter-clockwise order
+	//   Triangulator_CW : sets vertices in clockwise order
+	void SetOrientation(int orientation);
+};
+
+class TriangulatorPartition {
+protected:
+	struct PartitionVertex {
+		bool isActive;
+		bool isConvex;
+		bool isEar;
+
+		Vector2 p;
+		real_t angle;
+		PartitionVertex *previous;
+		PartitionVertex *next;
+	};
+
+	struct MonotoneVertex {
+		Vector2 p;
+		long previous;
+		long next;
+	};
+
+	struct VertexSorter{
+		mutable MonotoneVertex *vertices;
+		bool operator() (long index1, long index2) const;
+	};
+
+	struct Diagonal {
+		long index1;
+		long index2;
+	};
+
+	//dynamic programming state for minimum-weight triangulation
+	struct DPState {
+		bool visible;
+		real_t weight;
+		long bestvertex;
+	};
+
+	//dynamic programming state for convex partitioning
+	struct DPState2 {
+		bool visible;
+		long weight;
+		List<Diagonal> pairs;
+	};
+
+	//edge that intersects the scanline
+	struct ScanLineEdge {
+		mutable long index;
+		Vector2 p1;
+		Vector2 p2;
+
+		//determines if the edge is to the left of another edge
+		bool operator< (const ScanLineEdge & other) const;
+
+		bool IsConvex(const Vector2& p1, const Vector2& p2, const Vector2& p3) const;
+	};
+
+	//standard helper functions
+	bool IsConvex(Vector2& p1, Vector2& p2, Vector2& p3);
+	bool IsReflex(Vector2& p1, Vector2& p2, Vector2& p3);
+	bool IsInside(Vector2& p1, Vector2& p2, Vector2& p3, Vector2 &p);
+
+	bool InCone(Vector2 &p1, Vector2 &p2, Vector2 &p3, Vector2 &p);
+	bool InCone(PartitionVertex *v, Vector2 &p);
+
+	int Intersects(Vector2 &p11, Vector2 &p12, Vector2 &p21, Vector2 &p22);
+
+	Vector2 Normalize(const Vector2 &p);
+	real_t Distance(const Vector2 &p1, const Vector2 &p2);
+
+	//helper functions for Triangulate_EC
+	void UpdateVertexReflexity(PartitionVertex *v);
+	void UpdateVertex(PartitionVertex *v,PartitionVertex *vertices, long numvertices);
+
+	//helper functions for ConvexPartition_OPT
+	void UpdateState(long a, long b, long w, long i, long j, DPState2 **dpstates);
+	void TypeA(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates);
+	void TypeB(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates);
+
+	//helper functions for MonotonePartition
+	bool Below(Vector2 &p1, Vector2 &p2);
+	void AddDiagonal(MonotoneVertex *vertices, long *numvertices, long index1, long index2,
+			 char *vertextypes, Set<ScanLineEdge>::Element **edgeTreeIterators,
+			 Set<ScanLineEdge> *edgeTree, long *helpers);
+
+	//triangulates a monotone polygon, used in Triangulate_MONO
+	int TriangulateMonotone(TriangulatorPoly *inPoly, List<TriangulatorPoly> *triangles);
+
+public:
+
+	//simple heuristic procedure for removing holes from a list of polygons
+	//works by creating a diagonal from the rightmost hole vertex to some visible vertex
+	//time complexity: O(h*(n^2)), h is the number of holes, n is the number of vertices
+	//space complexity: O(n)
+	//params:
+	//   inpolys : a list of polygons that can contain holes
+	//             vertices of all non-hole polys have to be in counter-clockwise order
+	//             vertices of all hole polys have to be in clockwise order
+	//   outpolys : a list of polygons without holes
+	//returns 1 on success, 0 on failure
+	int RemoveHoles(List<TriangulatorPoly> *inpolys, List<TriangulatorPoly> *outpolys);
+
+	//triangulates a polygon by ear clipping
+	//time complexity O(n^2), n is the number of vertices
+	//space complexity: O(n)
+	//params:
+	//   poly : an input polygon to be triangulated
+	//          vertices have to be in counter-clockwise order
+	//   triangles : a list of triangles (result)
+	//returns 1 on success, 0 on failure
+	int Triangulate_EC(TriangulatorPoly *poly, List<TriangulatorPoly> *triangles);
+
+	//triangulates a list of polygons that may contain holes by ear clipping algorithm
+	//first calls RemoveHoles to get rid of the holes, and then Triangulate_EC for each resulting polygon
+	//time complexity: O(h*(n^2)), h is the number of holes, n is the number of vertices
+	//space complexity: O(n)
+	//params:
+	//   inpolys : a list of polygons to be triangulated (can contain holes)
+	//             vertices of all non-hole polys have to be in counter-clockwise order
+	//             vertices of all hole polys have to be in clockwise order
+	//   triangles : a list of triangles (result)
+	//returns 1 on success, 0 on failure
+	int Triangulate_EC(List<TriangulatorPoly> *inpolys, List<TriangulatorPoly> *triangles);
+
+	//creates an optimal polygon triangulation in terms of minimal edge length
+	//time complexity: O(n^3), n is the number of vertices
+	//space complexity: O(n^2)
+	//params:
+	//   poly : an input polygon to be triangulated
+	//          vertices have to be in counter-clockwise order
+	//   triangles : a list of triangles (result)
+	//returns 1 on success, 0 on failure
+	int Triangulate_OPT(TriangulatorPoly *poly, List<TriangulatorPoly> *triangles);
+
+	//triangulates a polygons by firstly partitioning it into monotone polygons
+	//time complexity: O(n*log(n)), n is the number of vertices
+	//space complexity: O(n)
+	//params:
+	//   poly : an input polygon to be triangulated
+	//          vertices have to be in counter-clockwise order
+	//   triangles : a list of triangles (result)
+	//returns 1 on success, 0 on failure
+	int Triangulate_MONO(TriangulatorPoly *poly, List<TriangulatorPoly> *triangles);
+
+	//triangulates a list of polygons by firstly partitioning them into monotone polygons
+	//time complexity: O(n*log(n)), n is the number of vertices
+	//space complexity: O(n)
+	//params:
+	//   inpolys : a list of polygons to be triangulated (can contain holes)
+	//             vertices of all non-hole polys have to be in counter-clockwise order
+	//             vertices of all hole polys have to be in clockwise order
+	//   triangles : a list of triangles (result)
+	//returns 1 on success, 0 on failure
+	int Triangulate_MONO(List<TriangulatorPoly> *inpolys, List<TriangulatorPoly> *triangles);
+
+	//creates a monotone partition of a list of polygons that can contain holes
+	//time complexity: O(n*log(n)), n is the number of vertices
+	//space complexity: O(n)
+	//params:
+	//   inpolys : a list of polygons to be triangulated (can contain holes)
+	//             vertices of all non-hole polys have to be in counter-clockwise order
+	//             vertices of all hole polys have to be in clockwise order
+	//   monotonePolys : a list of monotone polygons (result)
+	//returns 1 on success, 0 on failure
+	int MonotonePartition(List<TriangulatorPoly> *inpolys, List<TriangulatorPoly> *monotonePolys);
+
+	//partitions a polygon into convex polygons by using Hertel-Mehlhorn algorithm
+	//the algorithm gives at most four times the number of parts as the optimal algorithm
+	//however, in practice it works much better than that and often gives optimal partition
+	//uses triangulation obtained by ear clipping as intermediate result
+	//time complexity O(n^2), n is the number of vertices
+	//space complexity: O(n)
+	//params:
+	//   poly : an input polygon to be partitioned
+	//          vertices have to be in counter-clockwise order
+	//   parts : resulting list of convex polygons
+	//returns 1 on success, 0 on failure
+	int ConvexPartition_HM(TriangulatorPoly *poly, List<TriangulatorPoly> *parts);
+
+	//partitions a list of polygons into convex parts by using Hertel-Mehlhorn algorithm
+	//the algorithm gives at most four times the number of parts as the optimal algorithm
+	//however, in practice it works much better than that and often gives optimal partition
+	//uses triangulation obtained by ear clipping as intermediate result
+	//time complexity O(n^2), n is the number of vertices
+	//space complexity: O(n)
+	//params:
+	//   inpolys : an input list of polygons to be partitioned
+	//             vertices of all non-hole polys have to be in counter-clockwise order
+	//             vertices of all hole polys have to be in clockwise order
+	//   parts : resulting list of convex polygons
+	//returns 1 on success, 0 on failure
+	int ConvexPartition_HM(List<TriangulatorPoly> *inpolys, List<TriangulatorPoly> *parts);
+
+	//optimal convex partitioning (in terms of number of resulting convex polygons)
+	//using the Keil-Snoeyink algorithm
+	//M. Keil, J. Snoeyink, "On the time bound for convex decomposition of simple polygons", 1998
+	//time complexity O(n^3), n is the number of vertices
+	//space complexity: O(n^3)
+	//   poly : an input polygon to be partitioned
+	//          vertices have to be in counter-clockwise order
+	//   parts : resulting list of convex polygons
+	//returns 1 on success, 0 on failure
+	int ConvexPartition_OPT(TriangulatorPoly *poly, List<TriangulatorPoly> *parts);
+};
+
+
+#endif

+ 1 - 1
core/resource.cpp

@@ -130,7 +130,7 @@ void ResourceImportMetadata::_bind_methods() {
 
 	ObjectTypeDB::bind_method(_MD("set_editor","name"),&ResourceImportMetadata::set_editor);
 	ObjectTypeDB::bind_method(_MD("get_editor"),&ResourceImportMetadata::get_editor);
-	ObjectTypeDB::bind_method(_MD("add_source","path","md5"),&ResourceImportMetadata::add_source);
+	ObjectTypeDB::bind_method(_MD("add_source","path","md5"),&ResourceImportMetadata::add_source, "");
 	ObjectTypeDB::bind_method(_MD("get_source_path","idx"),&ResourceImportMetadata::get_source_path);
 	ObjectTypeDB::bind_method(_MD("get_source_md5","idx"),&ResourceImportMetadata::get_source_md5);
 	ObjectTypeDB::bind_method(_MD("remove_source","idx"),&ResourceImportMetadata::remove_source);

+ 37 - 0
core/set.h

@@ -249,6 +249,37 @@ private:
 		return (node!=_data._nil)?node:NULL;
 	}
 
+	Element *_lower_bound(const T& p_value) const {
+
+		Element *node = _data._root->left;
+		Element *prev = NULL;
+		C less;
+
+		while(node!=_data._nil) {
+			prev=node;
+
+			if (less(p_value,node->value))
+				node=node->left;
+			else if (less(node->value,p_value))
+				node=node->right;
+			else
+				break; // found
+		}
+
+		if (node==_data._nil) {
+			if (prev==NULL)
+				return NULL;
+			if (less(prev->value,p_value)) {
+
+				prev=prev->_next;
+			}
+
+			return prev;
+
+		} else
+			return node;
+	}
+
 
 	Element *_insert(const T& p_value, bool& r_exists) {
 		
@@ -582,6 +613,12 @@ public:
 		
 		return e;
 	}
+
+	Element *lower_bound(const T& p_value) const {
+
+		return _lower_bound(p_value);
+	}
+
 	
 	inline int size() const { return _data.size_cache; }
 	int calculate_depth() const {

+ 292 - 4
core/ustring.cpp

@@ -34,6 +34,7 @@
 #include "io/md5.h"
 #include "ucaps.h"
 #include "color.h"
+#include "variant.h"
 #define MAX_DIGITS 6
 #define UPPERCASE(m_c) (((m_c)>='a' && (m_c)<='z')?((m_c)-('a'-'A')):(m_c))
 #define LOWERCASE(m_c) (((m_c)>='A' && (m_c)<='Z')?((m_c)+('a'-'A')):(m_c))
@@ -981,7 +982,7 @@ String String::num(double p_num,int p_decimals) {
 
 }
 
-String String::num_int64(int64_t p_num) {
+String String::num_int64(int64_t p_num, int base, bool capitalize_hex) {
 
 	bool sign=p_num<0;
 	int64_t num=ABS(p_num);
@@ -990,7 +991,7 @@ String String::num_int64(int64_t p_num) {
 
 	int chars=0;
 	do {
-		n/=10;
+		n/=base;
 		chars++;
 	} while(n);
 
@@ -1002,8 +1003,15 @@ String String::num_int64(int64_t p_num) {
 	c[chars]=0;
 	n=num;
 	do {
-		c[--chars]='0'+(n%10);
-		n/=10;
+		int mod = n%base;
+		if (mod >= 10) {
+			char a = (capitalize_hex ? 'A' : 'a');
+			c[--chars]=a+(mod - 10);
+		} else {
+			c[--chars]='0'+mod;
+		}
+
+		n/=base;
 	} while(n);
 
 	if (sign)
@@ -3518,4 +3526,284 @@ String rtoss(double p_val) {
 	return String::num_scientific(p_val);
 }
 
+// Right-pad with a character.
+String String::rpad(int min_length, const String& character) const {
+	String s = *this;
+	int padding = min_length - s.length();
+	if (padding > 0) {
+		for (int i = 0; i < padding; i++) s = s + character;
+	}
+
+	return s;
+}
+// Left-pad with a character.
+String String::lpad(int min_length, const String& character) const {
+	String s = *this;
+	int padding = min_length - s.length();
+	if (padding > 0) {
+		for (int i = 0; i < padding; i++) s = character + s;
+	}
+
+	return s;
+}
+
+// sprintf is implemented in GDScript via:
+//   "fish %s pie" % "frog"
+//   "fish %s %d pie" % ["frog", 12]
+String String::sprintf(const Array& values) const {
+
+	String formatted;
+	CharType* self = (CharType*)c_str();
+	int num_items = values.size();
+	bool in_format = false;
+	int value_index = 0;
+	int min_chars;
+	int min_decimals;
+	bool in_decimals;
+	bool pad_with_zeroes;
+	bool left_justified;
+	bool show_sign;
+
+
+	for (; *self; self++) {
+		const CharType c = *self;
+
+		if (in_format) { // We have % - lets see what else we get.
+			switch (c) {
+				case '%': { // Replace %% with %
+					formatted += chr(c);
+					in_format = false;
+					break;
+				}
+				case 'd': // Integer (signed)
+				case 'o': // Octal
+				case 'x': // Hexadecimal (lowercase)
+				case 'X': { // Hexadecimal (uppercase)
+					if (value_index >= values.size()) {
+						ERR_EXPLAIN("not enough arguments for format string");
+						ERR_FAIL_V("");
+					}
+
+					if (!values[value_index].is_num()) {
+						ERR_EXPLAIN("a number is required");
+						ERR_FAIL_V("");
+					}
+					
+					int64_t value = values[value_index];
+					int base;
+					bool capitalize = false;
+					switch (c) {
+						case 'd': base = 10; break;
+						case 'o': base = 8; break;
+						case 'x': base = 16; break;
+						case 'X': base = 16; capitalize = true; break;
+					}
+					// Get basic number.
+					String str = String::num_int64(value, base, capitalize);
+
+					// Sign.
+					if (show_sign && value >= 0) {
+						str = str.insert(0, "+");
+					}
+
+					// Padding.
+					String pad_char = pad_with_zeroes ? String("0") : String(" ");
+					if (left_justified) {
+						str = str.rpad(min_chars, pad_char);
+					} else {
+						str = str.lpad(min_chars, pad_char);
+					}
+
+					formatted += str;
+					++value_index;
+					in_format = false;
+
+					break;
+				}
+				case 'f': { // Float
+					if (value_index >= values.size()) {
+						ERR_EXPLAIN("not enough arguments for format string");
+						ERR_FAIL_V("");
+					}
+
+					if (!values[value_index].is_num()) {
+						ERR_EXPLAIN("a number is required");
+						ERR_FAIL_V("");
+					}
+
+					double value = values[value_index];
+					String str = String::num(value, min_decimals);
+
+					// Pad decimals out.
+					str = str.pad_decimals(min_decimals);
+
+					// Show sign
+					if (show_sign && value >= 0) {
+						str = str.insert(0, "+");
+					}
+
+					// Padding
+					if (left_justified) {
+						str = str.rpad(min_chars);
+					} else {
+						str = str.lpad(min_chars);
+					}
+
+					formatted += str;
+					++value_index;
+					in_format = false;
+					
+					break;
+				}
+				case 's': { // String
+					if (value_index >= values.size()) {
+						ERR_EXPLAIN("not enough arguments for format string");
+						ERR_FAIL_V("");
+					}
+
+					String str = values[value_index];
+					// Padding.
+					if (left_justified) {
+						str = str.rpad(min_chars);
+					} else {
+						str = str.lpad(min_chars);
+					}
+
+					formatted += str;
+					++value_index;
+					in_format = false;
+					break;
+				}
+				case 'c': {
+					if (value_index >= values.size()) {
+						ERR_EXPLAIN("not enough arguments for format string");
+						ERR_FAIL_V("");
+					}
+
+					// Convert to character.
+					String str;
+					if (values[value_index].is_num()) {
+						int value = values[value_index];
+						if (value < 0) {
+							ERR_EXPLAIN("unsigned byte integer is lower than maximum")
+							ERR_FAIL_V("");
+						} else if (value > 255) {
+							ERR_EXPLAIN("unsigned byte integer is greater than maximum")
+							ERR_FAIL_V("");
+						}
+						str = chr(values[value_index]);
+					} else if (values[value_index].get_type() == Variant::STRING) {
+						str = values[value_index];
+						if (str.length() != 1) {
+							ERR_EXPLAIN("%c requires number or single-character string");
+							ERR_FAIL_V("");
+						}
+					} else {
+						ERR_EXPLAIN("%c requires number or single-character string");
+						ERR_FAIL_V("");
+					}
+
+					// Padding.
+					if (left_justified) {
+						str = str.rpad(min_chars);
+					} else {
+						str = str.lpad(min_chars);
+					}
 
+					formatted += str;
+					++value_index;
+					in_format = false;
+					break;
+				}
+				case '-': { // Left justify
+					left_justified = true;
+					break;
+				}
+				case '+': { // Show + if positive.
+					show_sign = true;
+					break;
+				}
+				case '0': case '1': case '2': case '3': case '4':
+				case '5': case '6': case '7': case '8': case '9': {
+					int n = c - '0';
+					if (in_decimals) {
+						min_decimals *= 10;
+						min_decimals += n;
+					} else {
+						if (c == '0' && min_chars == 0) {
+							pad_with_zeroes = true;
+						} else {
+							min_chars *= 10;
+							min_chars += n;
+						}
+					}
+					break;
+				}
+				case '.': { // Float separtor.
+					if (in_decimals) {
+						ERR_EXPLAIN("too many decimal points in format");
+						ERR_FAIL_V("");
+					}
+					in_decimals = true;
+					min_decimals = 0; // We want to add the value manually.
+					break;
+				}
+
+				case '*': { // Dyanmic width, based on value.
+					if (value_index >= values.size()) {
+						ERR_EXPLAIN("not enough arguments for format string");
+						ERR_FAIL_V("");
+					}
+
+					if (!values[value_index].is_num()) {
+						ERR_EXPLAIN("* wants number");
+						ERR_FAIL_V("");
+					}
+
+					int size = values[value_index];
+
+					if (in_decimals) {
+						min_decimals = size;
+					} else {
+						min_chars = size;
+					}
+
+					++value_index;
+					break;
+				}
+
+				default: {
+					ERR_EXPLAIN("unsupported format character");
+  					ERR_FAIL_V("");
+  				}
+			}
+		} else { // Not in format string.
+			switch (c) {
+				case '%':
+					in_format = true;
+					// Back to defaults:
+					min_chars = 0;
+					min_decimals = 6;
+					pad_with_zeroes = false;
+					left_justified = false;
+					show_sign = false;
+					in_decimals = false;
+					break;
+				default:
+					formatted += chr(c);
+			}
+		}
+	}
+
+	if (in_format) {
+		ERR_EXPLAIN("incomplete format");
+  		ERR_FAIL_V("");
+	}
+
+	if (value_index != values.size()) {
+		ERR_EXPLAIN("not all arguments converted during string formatting");
+  		ERR_FAIL_V("");
+	}
+
+	return formatted;
+}

+ 6 - 2
core/ustring.h

@@ -31,6 +31,7 @@
 
 #include "typedefs.h"
 #include "vector.h"
+#include "array.h"
 
 /**
 	@author red <red@killy>
@@ -127,10 +128,13 @@ public:
 	String insert(int p_at_pos,String p_string) const;
 	String pad_decimals(int p_digits) const;
 	String pad_zeros(int p_digits) const;
+	String lpad(int min_length,const String& character=" ") const;
+	String rpad(int min_length,const String& character=" ") const;
+	String sprintf(const Array& values) const;
 	static String num(double p_num,int p_decimals=-1);
 	static String num_scientific(double p_num);
 	static String num_real(double p_num);
-	static String num_int64(int64_t p_num);
+	static String num_int64(int64_t p_num,int base=10,bool capitalize_hex=false);
 	static String chr(CharType p_char);
 	static String md5(const uint8_t *p_md5);
 	bool is_numeric() const;
@@ -203,7 +207,7 @@ public:
 	String xml_unescape() const;
 	String c_escape() const;
 	String c_unescape() const;
-
+	
 	String percent_encode() const;
 	String percent_decode() const;
 

+ 26 - 17
core/variant.cpp

@@ -2631,8 +2631,13 @@ Variant Variant::call(const StringName& p_method,VARIANT_ARG_DECLARE) {
 	return ret;
 }
 
+void Variant::construct_from_string(const String& p_string,Variant& r_value,ObjectConstruct p_obj_construct,void *p_construct_ud) {
+
+	r_value=Variant();
+}
 
-String Variant::get_construct_string() const {
+
+String Variant::get_construct_string(ObjectDeConstruct p_obj_deconstruct,void *p_deconstruct_ud) const {
 
 	switch( type ) {
 
@@ -2640,7 +2645,7 @@ String Variant::get_construct_string() const {
 		case BOOL: return _data._bool ? "true" : "false";
 		case INT: return String::num(_data._int);
 		case REAL: return String::num(_data._real);
-		case STRING: return "\""+*reinterpret_cast<const String*>(_data._mem)+"\"";
+		case STRING: return "\""+reinterpret_cast<const String*>(_data._mem)->c_escape()+"\"";
 		case VECTOR2: return "Vector2("+operator Vector2()+")";
 		case RECT2: return "Rect2("+operator Rect2()+")";
 		case MATRIX32: return "Matrix32("+operator Matrix32()+")";
@@ -2651,7 +2656,7 @@ String Variant::get_construct_string() const {
 		case QUAT: return "Quat("+operator Quat()+")";
 		case MATRIX3: return "Matrix3("+operator Matrix3()+")";
 		case TRANSFORM: return "Transform("+operator Transform()+")";
-		case NODE_PATH: return "@\""+operator NodePath()+"\"";
+		case NODE_PATH: return "@\""+String(operator NodePath()).c_escape()+"\"";
 		case INPUT_EVENT: return "InputEvent()";
 		case COLOR: return "Color("+String::num( operator Color().r)+","+String::num( operator Color().g)+","+String::num( operator Color().b)+","+String::num( operator Color().a)+")" ;
 		case DICTIONARY: {
@@ -2667,8 +2672,8 @@ String Variant::get_construct_string() const {
 			for(List<Variant>::Element *E=keys.front();E;E=E->next()) {
 
 				_VariantStrPair sp;
-				sp.key=E->get().get_construct_string();
-				sp.value=d[E->get()].get_construct_string();
+				sp.key=E->get().get_construct_string(p_obj_deconstruct,p_deconstruct_ud);
+				sp.value=d[E->get()].get_construct_string(p_obj_deconstruct,p_deconstruct_ud);
 				pairs.push_back(sp);
 			}
 
@@ -2686,50 +2691,50 @@ String Variant::get_construct_string() const {
 		case VECTOR3_ARRAY: {
 
 			DVector<Vector3> vec = operator DVector<Vector3>();
-			String str="[";
+			String str="Vector3Array([";
 			for(int i=0;i<vec.size();i++) {
 
 				if (i>0)
 					str+=", ";
 				str+=Variant( vec[i] ).get_construct_string();
 			}
-			return str+"]";
+			return str+"])";
 		} break;
 		case STRING_ARRAY: {
 
 			DVector<String> vec = operator DVector<String>();
-			String str="[";
+			String str="StringArray([";
 			for(int i=0;i<vec.size();i++) {
 
 				if (i>0)
 					str+=", ";
 				str=str+=Variant( vec[i] ).get_construct_string();
 			}
-			return str+"]";
+			return str+"])";
 		} break;
 		case INT_ARRAY: {
 
 			DVector<int> vec = operator DVector<int>();
-			String str="[";
+			String str="IntArray([";
 			for(int i=0;i<vec.size();i++) {
 
 				if (i>0)
 					str+=", ";
 				str=str+itos(vec[i]);
 			}
-			return str+"]";
+			return str+"])";
 		} break;
 		case REAL_ARRAY: {
 
 			DVector<real_t> vec = operator DVector<real_t>();
-			String str="[";
+			String str="FloatArray([";
 			for(int i=0;i<vec.size();i++) {
 
 				if (i>0)
 					str+=", ";
 				str=str+rtos(vec[i]);
 			}
-			return str+"]";
+			return str+"])";
 		} break;
 		case ARRAY: {
 
@@ -2738,16 +2743,20 @@ String Variant::get_construct_string() const {
 			for (int i=0; i<arr.size(); i++) {
 				if (i)
 					str+=", ";
-				str += arr[i].get_construct_string();
+				str += arr[i].get_construct_string(p_obj_deconstruct,p_deconstruct_ud);
 			};
 			return str+"]";
 
 		} break;
 		case OBJECT: {
 
-			if (_get_obj().obj)
-				return _get_obj().obj->get_type()+".new()";
-			else
+			if (_get_obj().obj) {
+				if (p_obj_deconstruct) {
+					return "Object(\""+p_obj_deconstruct(Variant(*this),p_deconstruct_ud).c_escape()+")";
+				} else {
+					return _get_obj().obj->get_type()+".new()";
+				}
+			} else
 				return "null";
 
 		} break;

+ 5 - 1
core/variant.h

@@ -419,7 +419,11 @@ public:
 	static bool has_numeric_constant(Variant::Type p_type, const StringName& p_value);
 	static int get_numeric_constant_value(Variant::Type p_type, const StringName& p_value);
 
-	String get_construct_string() const;
+	typedef String (*ObjectDeConstruct)(const Variant& p_object,void *ud);
+	typedef void (*ObjectConstruct)(const String& p_text,void *ud,Variant& r_value);
+
+	String get_construct_string(ObjectDeConstruct p_obj_deconstruct=NULL,void *p_deconstruct_ud=NULL) const;
+	static void construct_from_string(const String& p_string,Variant& r_value,ObjectConstruct p_obj_construct=NULL,void *p_construct_ud=NULL);
 
 	void operator=(const Variant& p_variant); // only this is enough for all the other types
 	Variant(const Variant& p_variant);

+ 2 - 2
core/variant_call.cpp

@@ -1263,8 +1263,8 @@ _VariantCall::addfunc(Variant::m_vtype,Variant::m_ret,_SCS(#m_method),VCALL(m_cl
 	ADDFUNC1(VECTOR2,VECTOR2,Vector2,snapped,VECTOR2,"by",varray());
 	ADDFUNC0(VECTOR2,REAL,Vector2,get_aspect,varray());
 	ADDFUNC1(VECTOR2,REAL,Vector2,dot,VECTOR2,"with",varray());
-	ADDFUNC1(VECTOR2,REAL,Vector2,slide,VECTOR2,"vec",varray());
-	ADDFUNC1(VECTOR2,REAL,Vector2,reflect,VECTOR2,"vec",varray());
+	ADDFUNC1(VECTOR2,VECTOR2,Vector2,slide,VECTOR2,"vec",varray());
+	ADDFUNC1(VECTOR2,VECTOR2,Vector2,reflect,VECTOR2,"vec",varray());
 	//ADDFUNC1(VECTOR2,REAL,Vector2,cross,VECTOR2,"with",varray());
 
 	ADDFUNC0(RECT2,REAL,Rect2,get_area,varray());

+ 433 - 0
core/variant_construct_string.cpp

@@ -0,0 +1,433 @@
+
+#include "variant.h"
+
+class VariantConstruct {
+
+	enum TokenType {
+		TK_CURLY_BRACKET_OPEN,
+		TK_CURLY_BRACKET_CLOSE,
+		TK_BRACKET_OPEN,
+		TK_BRACKET_CLOSE,
+		TK_IDENTIFIER,
+		TK_STRING,
+		TK_NUMBER,
+		TK_COLON,
+		TK_COMMA,
+		TK_EOF,
+		TK_MAX
+	};
+
+	enum Expecting {
+
+		EXPECT_OBJECT,
+		EXPECT_OBJECT_KEY,
+		EXPECT_COLON,
+		EXPECT_OBJECT_VALUE,
+	};
+
+	struct Token {
+
+		TokenType type;
+		Variant value;
+	};
+
+	static const char * tk_name[TK_MAX];
+
+	static String _print_var(const Variant& p_var);
+
+	static Error _get_token(const CharType *p_str,int &index, int p_len,Token& r_token,int &line,String &r_err_str);
+	static Error _parse_value(Variant &value,Token& token,const CharType *p_str,int &index, int p_len,int &line,String &r_err_str,Variant::ObjectConstruct* p_construct,void* p_ud);
+	static Error _parse_array(Array &array,const CharType *p_str,int &index, int p_len,int &line,String &r_err_str,Variant::ObjectConstruct* p_construct,void* p_ud);
+	static Error _parse_dict(Dictionary &object,const CharType *p_str,int &index, int p_len,int &line,String &r_err_str,Variant::ObjectConstruct* p_construct,void* p_ud);
+
+public:
+
+	static Error parse(const String& p_string,Variant& r_ret,String &r_err_str,int &r_err_line,Variant::ObjectConstruct* p_construct,void* p_ud);
+};
+
+
+const char * VariantConstruct::tk_name[TK_MAX] = {
+	"'{'",
+	"'}'",
+	"'['",
+	"']'",
+	"identifier",
+	"string",
+	"number",
+	"':'",
+	"','",
+	"EOF",
+};
+
+
+
+Error VariantConstruct::_get_token(const CharType *p_str, int &idx, int p_len, Token& r_token,int &line,String &r_err_str) {
+
+	while (true) {
+		switch(p_str[idx]) {
+
+			case '\n': {
+
+				line++;
+				idx++;
+				break;
+			};
+			case 0: {
+				r_token.type=TK_EOF;
+				return OK;
+			} break;
+			case '{': {
+
+				r_token.type=TK_CURLY_BRACKET_OPEN;
+				idx++;
+				return OK;
+			};
+			case '}': {
+
+				r_token.type=TK_CURLY_BRACKET_CLOSE;
+				idx++;
+				return OK;
+			};
+			case '[': {
+
+				r_token.type=TK_BRACKET_OPEN;
+				idx++;
+				return OK;
+			};
+			case ']': {
+
+				r_token.type=TK_BRACKET_CLOSE;
+				idx++;
+				return OK;
+			};
+			case ':': {
+
+				r_token.type=TK_COLON;
+				idx++;
+				return OK;
+			};
+			case ',': {
+
+				r_token.type=TK_COMMA;
+				idx++;
+				return OK;
+			};
+			case '"': {
+
+				idx++;
+				String str;
+				while(true) {
+					if (p_str[idx]==0) {
+						r_err_str="Unterminated String";
+						return ERR_PARSE_ERROR;
+					} else if (p_str[idx]=='"') {
+						idx++;
+						break;
+					} else if (p_str[idx]=='\\') {
+						//escaped characters...
+						idx++;
+						CharType next = p_str[idx];
+						if (next==0) {
+							r_err_str="Unterminated String";
+							return  ERR_PARSE_ERROR;
+						}
+						CharType res=0;
+
+						switch(next) {
+
+							case 'b': res=8; break;
+							case 't': res=9; break;
+							case 'n': res=10; break;
+							case 'f': res=12; break;
+							case 'r': res=13; break;
+							case '\"': res='\"'; break;
+							case '\\': res='\\'; break;
+							case '/': res='/'; break; //wtf
+							case 'u': {
+								//hexnumbarh - oct is deprecated
+
+
+								for(int j=0;j<4;j++) {
+									CharType c = p_str[idx+j+1];
+									if (c==0) {
+										r_err_str="Unterminated String";
+										return ERR_PARSE_ERROR;
+									}
+									if (!((c>='0' && c<='9') || (c>='a' && c<='f') || (c>='A' && c<='F'))) {
+
+										r_err_str="Malformed hex constant in string";
+										return ERR_PARSE_ERROR;
+									}
+									CharType v;
+									if (c>='0' && c<='9') {
+										v=c-'0';
+									} else if (c>='a' && c<='f') {
+										v=c-'a';
+										v+=10;
+									} else if (c>='A' && c<='F') {
+										v=c-'A';
+										v+=10;
+									} else {
+										ERR_PRINT("BUG");
+										v=0;
+									}
+
+									res<<=4;
+									res|=v;
+
+
+								}
+								idx+=4; //will add at the end anyway
+
+
+							} break;
+							default: {
+
+								r_err_str="Invalid escape sequence";
+								return ERR_PARSE_ERROR;
+							} break;
+						}
+
+						str+=res;
+
+					} else {
+						if (p_str[idx]=='\n')
+							line++;
+						str+=p_str[idx];
+					}
+					idx++;
+				}
+
+				r_token.type=TK_STRING;
+				r_token.value=str;
+				return OK;
+
+			} break;
+			default: {
+
+				if (p_str[idx]<=32) {
+					idx++;
+					break;
+				}
+
+				if (p_str[idx]=='-' || (p_str[idx]>='0' && p_str[idx]<='9')) {
+					//a number
+					const CharType *rptr;
+					double number = String::to_double(&p_str[idx],&rptr);
+					idx+=(rptr - &p_str[idx]);
+					r_token.type=TK_NUMBER;
+					r_token.value=number;
+					return OK;
+
+				} else if ((p_str[idx]>='A' && p_str[idx]<='Z') || (p_str[idx]>='a' && p_str[idx]<='z')) {
+
+					String id;
+
+					while((p_str[idx]>='A' && p_str[idx]<='Z') || (p_str[idx]>='a' && p_str[idx]<='z')) {
+
+						id+=p_str[idx];
+						idx++;
+					}
+
+					r_token.type=TK_IDENTIFIER;
+					r_token.value=id;
+					return OK;
+				} else {
+					r_err_str="Unexpected character.";
+					return ERR_PARSE_ERROR;
+				}
+			}
+
+		}
+	}
+
+	return ERR_PARSE_ERROR;
+}
+
+
+
+Error VariantConstruct::_parse_value(Variant &value,Token& token,const CharType *p_str,int &index, int p_len,int &line,String &r_err_str,Variant::ObjectConstruct* p_construct,void* p_ud) {
+
+
+	if (token.type==TK_CURLY_BRACKET_OPEN) {
+
+		Dictionary d;
+		Error err = _parse_dict(d,p_str,index,p_len,line,r_err_str,p_construct,p_ud);
+		if (err)
+			return err;
+		value=d;
+		return OK;
+	} else if (token.type==TK_BRACKET_OPEN) {
+
+		Array a;
+		Error err = _parse_array(a,p_str,index,p_len,line,r_err_str,p_construct,p_ud);
+		if (err)
+			return err;
+		value=a;
+		return OK;
+
+	} else if (token.type==TK_IDENTIFIER) {
+
+		String id = token.value;
+		if (id=="true")
+			value=true;
+		else if (id=="false")
+			value=false;
+		else if (id=="null")
+			value=Variant();
+		else {
+			r_err_str="Expected 'true','false' or 'null', got '"+id+"'.";
+			return ERR_PARSE_ERROR;
+		}
+		return OK;
+
+	} else if (token.type==TK_NUMBER) {
+
+		value=token.value;
+		return OK;
+	} else if (token.type==TK_STRING) {
+
+		value=token.value;
+		return OK;
+	} else {
+		r_err_str="Expected value, got "+String(tk_name[token.type])+".";
+		return ERR_PARSE_ERROR;
+	}
+
+	return ERR_PARSE_ERROR;
+}
+
+
+Error VariantConstruct::_parse_array(Array &array,const CharType *p_str,int &index, int p_len,int &line,String &r_err_str,Variant::ObjectConstruct* p_construct,void* p_ud) {
+
+	Token token;
+	bool need_comma=false;
+
+
+	while(index<p_len) {
+
+		Error err = _get_token(p_str,index,p_len,token,line,r_err_str);
+		if (err!=OK)
+			return err;
+
+		if (token.type==TK_BRACKET_CLOSE) {
+
+			return OK;
+		}
+
+		if (need_comma) {
+
+			if (token.type!=TK_COMMA) {
+
+				r_err_str="Expected ','";
+				return ERR_PARSE_ERROR;
+			} else {
+				need_comma=false;
+				continue;
+			}
+		}
+
+		Variant v;
+		err = _parse_value(v,token,p_str,index,p_len,line,r_err_str,p_construct,p_ud);
+		if (err)
+			return err;
+
+		array.push_back(v);
+		need_comma=true;
+
+	}
+
+	return OK;
+
+}
+
+Error VariantConstruct::_parse_dict(Dictionary &dict,const CharType *p_str,int &index, int p_len,int &line,String &r_err_str,Variant::ObjectConstruct* p_construct,void* p_ud) {
+
+	bool at_key=true;
+	Variant key;
+	Token token;
+	bool need_comma=false;
+
+
+	while(index<p_len) {
+
+
+		if (at_key) {
+
+			Error err = _get_token(p_str,index,p_len,token,line,r_err_str);
+			if (err!=OK)
+				return err;
+
+			if (token.type==TK_CURLY_BRACKET_CLOSE) {
+
+				return OK;
+			}
+
+			if (need_comma) {
+
+				if (token.type!=TK_COMMA) {
+
+					r_err_str="Expected '}' or ','";
+					return ERR_PARSE_ERROR;
+				} else {
+					need_comma=false;
+					continue;
+				}
+			}
+
+			err = _parse_value(key,token,p_str,index,p_len,line,r_err_str,p_construct,p_ud);
+
+
+			if (err!=OK)
+				return err;
+
+			err = _get_token(p_str,index,p_len,token,line,r_err_str);
+
+			if (err!=OK)
+				return err;
+
+			if (token.type!=TK_COLON) {
+
+				r_err_str="Expected ':'";
+				return ERR_PARSE_ERROR;
+			}
+			at_key=false;
+		} else {
+
+
+			Error err = _get_token(p_str,index,p_len,token,line,r_err_str);
+			if (err!=OK)
+				return err;
+
+			Variant v;
+			err = _parse_value(v,token,p_str,index,p_len,line,r_err_str,p_construct,p_ud);
+			if (err)
+				return err;
+			dict[key]=v;
+			need_comma=true;
+			at_key=true;
+		}
+	}
+
+	return OK;
+}
+
+
+Error VariantConstruct::parse(const String& p_string,Variant& r_ret,String &r_err_str,int &r_err_line,Variant::ObjectConstruct* p_construct,void* p_ud) {
+
+
+	const CharType *str = p_string.ptr();
+	int idx = 0;
+	int len = p_string.length();
+	Token token;
+	r_err_line=0;
+	String aux_key;
+
+	Error err = _get_token(str,idx,len,token,r_err_line,r_err_str);
+	if (err)
+		return err;
+
+	return _parse_value(r_ret,token,str,idx,len,r_err_line,r_err_str,p_construct,p_ud);
+}
+
+

+ 14 - 0
core/variant_op.cpp

@@ -736,6 +736,20 @@ void Variant::evaluate(const Operator& p_op, const Variant& p_a, const Variant&
 				}
 #endif
 				_RETURN( p_a._data._int % p_b._data._int );
+				
+			} else if (p_a.type==STRING) {
+				const String *str=reinterpret_cast<const String*>(p_a._data._mem);
+
+				if (p_b.type==ARRAY) {
+					// e.g. "frog %s %d" % ["fish", 12]
+					const Array *arr=reinterpret_cast<const Array*>(p_b._data._mem);
+					_RETURN(str->sprintf(*arr));
+				} else {
+					// e.g. "frog %d" % 12
+					Array arr;
+					arr.push_back(p_b);
+					_RETURN(str->sprintf(arr));
+				}
 			}
 
 			r_valid=false;

+ 0 - 33
demos/2d/hexamap/.fscache

@@ -1,33 +0,0 @@
-::res://::1412302385
-WWT-01.png::ImageTexture::1412126473::
-WWT-02.png::ImageTexture::1412126474::
-WWT-03.png::ImageTexture::1412126474::
-WWT-04.png::ImageTexture::1412126474::
-WWT-05.png::ImageTexture::1412126474::
-WWT-06.png::ImageTexture::1412126474::
-WWT-07.png::ImageTexture::1412126474::
-WWT-08.png::ImageTexture::1412126474::
-WWT-09.png::ImageTexture::1412126474::
-WWT-10.png::ImageTexture::1412126474::
-WWT-11.png::ImageTexture::1412126475::
-WWT-12.png::ImageTexture::1412126475::
-WWT-13.png::ImageTexture::1412126475::
-WWT-14.png::ImageTexture::1412126475::
-WWT-15.png::ImageTexture::1412126475::
-WWT-16.png::ImageTexture::1412126475::
-WWT-17.png::ImageTexture::1412126475::
-WWT-18.png::ImageTexture::1412126475::
-WWT-19.png::ImageTexture::1412126476::
-WWT-20.png::ImageTexture::1412126476::
-WWT-21.png::ImageTexture::1412126476::
-WWT-22.png::ImageTexture::1412126476::
-WWT-23.png::ImageTexture::1412126476::
-WWT-24.png::ImageTexture::1412126476::
-WWT-25.png::ImageTexture::1412126476::
-WWT-26.png::ImageTexture::1412126476::
-map.scn::PackedScene::1412127344::
-tiles.scn::PackedScene::1412126994::
-tileset.res::TileSet::1412127001::
-troll.gd::GDScript::1412302377::
-troll.png::ImageTexture::1412302385::
-troll.scn::PackedScene::1412302380::

二进制
demos/2d/navpoly/agent.png


+ 4 - 0
demos/2d/navpoly/engine.cfg

@@ -0,0 +1,4 @@
+[application]
+
+name="Navigation Polygon (2D)"
+main_scene="res://navigation.scn"

+ 63 - 0
demos/2d/navpoly/navigation.gd

@@ -0,0 +1,63 @@
+
+extends Navigation2D
+
+# member variables here, example:
+# var a=2
+# var b="textvar"
+var begin=Vector2()
+var end=Vector2()
+var path=[]
+
+const SPEED=200.0
+
+func _process(delta):
+
+
+	if (path.size()>1):
+	
+		var to_walk = delta*SPEED
+		while(to_walk>0 and path.size()>=2):
+			var pfrom = path[path.size()-1]
+			var pto = path[path.size()-2]
+			var d = pfrom.distance_to(pto)
+			if (d<=to_walk):
+				path.remove(path.size()-1)
+				to_walk-=d
+			else:
+				path[path.size()-1] = pfrom.linear_interpolate(pto,to_walk/d)
+				to_walk=0
+				
+		var atpos = path[path.size()-1]	
+		get_node("agent").set_pos(atpos)
+		
+		if (path.size()<2):
+			path=[]
+			set_process(false)
+				
+	else:
+		set_process(false)
+
+
+
+func _update_path():
+
+	var p = get_simple_path(begin,end,true)
+	path=Array(p) # Vector2array to complex to use, convert to regular array
+	path.invert()
+	
+	set_process(true)
+
+
+func _input(ev):
+	if (ev.type==InputEvent.MOUSE_BUTTON and ev.pressed and ev.button_index==1):
+		begin=get_node("agent").get_pos()
+		#mouse to local navigatio cooards
+		end=ev.pos - get_pos()
+		_update_path()
+
+func _ready():
+	# Initialization here
+	set_process_input(true)
+	pass
+
+

二进制
demos/2d/navpoly/navigation.scn


二进制
demos/2d/navpoly/path.png


文件差异内容过多而无法显示
+ 12 - 11
demos/2d/platformer/stage.xml


二进制
demos/2d/platformer/tiles_demo.png


+ 121 - 64
demos/2d/platformer/tileset.xml

@@ -1,134 +1,191 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<resource_file type="TileSet" subresource_count="12" version="0.99" version_name="Godot Engine v0.99.3037-pre-beta">
+<resource_file type="TileSet" subresource_count="14" version="1.0" version_name="Godot Engine v1.0.stable.custom_build">
 	<ext_resource path="res://tiles_demo.png" type="Texture"></ext_resource>
-	<resource type="ConvexPolygonShape2D" path="local://0">
-		<string name="resource/name"> "" </string>
-		<real name="custom_solver_bias"> 0 </real>
-		<vector2_array name="points" len="4"> 			0, 8, 64, 8, 64, 64, 0, 64 </vector2_array>
-		<resource name="script/script"></resource>
-	</resource>
 	<resource type="ConvexPolygonShape2D" path="local://1">
-		<string name="resource/name"> "" </string>
 		<real name="custom_solver_bias"> 0 </real>
-		<vector2_array name="points" len="4"> 			0, 64, 0, 8, 56, 8, 56, 64 </vector2_array>
-		<resource name="script/script"></resource>
+		<vector2_array name="points" len="4"> 			-32, -24, 32, -24, 32, 32, -32, 32 </vector2_array>
+
 	</resource>
 	<resource type="ConvexPolygonShape2D" path="local://2">
-		<string name="resource/name"> "" </string>
 		<real name="custom_solver_bias"> 0 </real>
-		<vector2_array name="points" len="4"> 			0, 64, 0, 0, 56, 0, 56, 64 </vector2_array>
-		<resource name="script/script"></resource>
+		<vector2_array name="points" len="4"> 			-32, 32, -32, -24, 24, -24, 24, 32 </vector2_array>
+
 	</resource>
 	<resource type="ConvexPolygonShape2D" path="local://3">
-		<string name="resource/name"> "" </string>
 		<real name="custom_solver_bias"> 0 </real>
-		<vector2_array name="points" len="4"> 			0, 64, 0, 0, 56, 0, 56, 64 </vector2_array>
-		<resource name="script/script"></resource>
+		<vector2_array name="points" len="4"> 			-32, 32, -32, -32, 24, -32, 24, 32 </vector2_array>
+
 	</resource>
 	<resource type="ConvexPolygonShape2D" path="local://4">
-		<string name="resource/name"> "" </string>
 		<real name="custom_solver_bias"> 0 </real>
-		<vector2_array name="points" len="5"> 			0, 64, 0, 0, 56, 0, 64, 8, 64, 64 </vector2_array>
-		<resource name="script/script"></resource>
+		<vector2_array name="points" len="4"> 			-64, 32, -64, -32, -8, -32, -8, 32 </vector2_array>
+
 	</resource>
 	<resource type="ConvexPolygonShape2D" path="local://5">
-		<string name="resource/name"> "" </string>
 		<real name="custom_solver_bias"> 0 </real>
-		<vector2_array name="points" len="4"> 			0, 64, 0, 8, 64, 8, 64, 64 </vector2_array>
-		<resource name="script/script"></resource>
+		<vector2_array name="points" len="5"> 			-32, 32, -32, -32, 24, -32, 32, -24, 32, 32 </vector2_array>
+
 	</resource>
 	<resource type="ConvexPolygonShape2D" path="local://6">
-		<string name="resource/name"> "" </string>
 		<real name="custom_solver_bias"> 0 </real>
-		<vector2_array name="points" len="4"> 			0, 64, 0, 8, 64, 8, 64, 64 </vector2_array>
-		<resource name="script/script"></resource>
+		<vector2_array name="points" len="4"> 			-32, 32, -32, -24, 32, -24, 32, 32 </vector2_array>
+
 	</resource>
 	<resource type="ConvexPolygonShape2D" path="local://7">
-		<string name="resource/name"> "" </string>
 		<real name="custom_solver_bias"> 0 </real>
-		<vector2_array name="points" len="4"> 			0, 0, 64, 0, 64, 64, 0, 64 </vector2_array>
-		<resource name="script/script"></resource>
+		<vector2_array name="points" len="4"> 			-32, 32, -32, -24, 32, -24, 32, 32 </vector2_array>
+
 	</resource>
 	<resource type="ConvexPolygonShape2D" path="local://8">
-		<string name="resource/name"> "" </string>
 		<real name="custom_solver_bias"> 0 </real>
-		<vector2_array name="points" len="4"> 			0, 8, 64, 72, 64, 128, 0, 128 </vector2_array>
-		<resource name="script/script"></resource>
+		<vector2_array name="points" len="4"> 			-32, -32, 32, -32, 32, 32, -32, 32 </vector2_array>
+
 	</resource>
 	<resource type="ConvexPolygonShape2D" path="local://9">
-		<string name="resource/name"> "" </string>
 		<real name="custom_solver_bias"> 0 </real>
-		<vector2_array name="points" len="4"> 			0, 64, 0, 0, 56, 0, 56, 64 </vector2_array>
-		<resource name="script/script"></resource>
+		<vector2_array name="points" len="4"> 			-32, -56, 32, 8, 32, 64, -32, 64 </vector2_array>
+
+	</resource>
+	<resource type="ConvexPolygonShape2D" path="local://10">
+		<real name="custom_solver_bias"> 0 </real>
+		<vector2_array name="points" len="4"> 			-32, 32, -32, -32, 24, -32, 24, 32 </vector2_array>
+
+	</resource>
+	<resource type="ConvexPolygonShape2D" path="local://11">
+		<real name="custom_solver_bias"> 0 </real>
+		<vector2_array name="points" len="4"> 			-32, -24, 32, -24, 32, 24, -32, 24 </vector2_array>
+
+	</resource>
+	<resource type="ConvexPolygonShape2D" path="local://12">
+		<real name="custom_solver_bias"> 0 </real>
+		<vector2_array name="points" len="4"> 			-32, -24, 24, -24, 24, 24, -32, 24 </vector2_array>
+
 	</resource>
 	<main_resource>
-		<string name="resource/name"> "" </string>
 		<string name="0/name"> "floor" </string>
 		<resource name="0/texture" resource_type="Texture" path="res://tiles_demo.png">  </resource>
-		<vector2 name="0/offset"> 0, 0 </vector2>
+		<vector2 name="0/tex_offset"> 0, 0 </vector2>
+		<vector2 name="0/shape_offset"> 32, 32 </vector2>
 		<rect2 name="0/region"> 0, 0, 64, 64 </rect2>
-		<resource name="0/shape" resource_type="ConvexPolygonShape2D" path="local://0">  </resource>
+		<array name="0/shapes" len="1" shared="false">
+			<resource  resource_type="Shape2D" path="local://1">  </resource>
+		</array>
 		<string name="1/name"> "edge" </string>
 		<resource name="1/texture" resource_type="Texture" path="res://tiles_demo.png">  </resource>
-		<vector2 name="1/offset"> 0, 0 </vector2>
+		<vector2 name="1/tex_offset"> 0, 0 </vector2>
+		<vector2 name="1/shape_offset"> 32, 32 </vector2>
 		<rect2 name="1/region"> 64, 0, 64, 64 </rect2>
-		<resource name="1/shape" resource_type="ConvexPolygonShape2D" path="local://1">  </resource>
+		<array name="1/shapes" len="1" shared="false">
+			<resource  resource_type="Shape2D" path="local://2">  </resource>
+		</array>
 		<string name="2/name"> "wall" </string>
 		<resource name="2/texture" resource_type="Texture" path="res://tiles_demo.png">  </resource>
-		<vector2 name="2/offset"> 0, 0 </vector2>
+		<vector2 name="2/tex_offset"> 0, 0 </vector2>
+		<vector2 name="2/shape_offset"> 32, 32 </vector2>
 		<rect2 name="2/region"> 64, 64, 64, 64 </rect2>
-		<resource name="2/shape" resource_type="ConvexPolygonShape2D" path="local://2">  </resource>
+		<array name="2/shapes" len="1" shared="false">
+			<resource  resource_type="Shape2D" path="local://3">  </resource>
+		</array>
 		<string name="3/name"> "wall_deco" </string>
 		<resource name="3/texture" resource_type="Texture" path="res://tiles_demo.png">  </resource>
-		<vector2 name="3/offset"> 0, 0 </vector2>
+		<vector2 name="3/tex_offset"> 0, 0 </vector2>
+		<vector2 name="3/shape_offset"> 64, 32 </vector2>
 		<rect2 name="3/region"> 320, 128, 128, 64 </rect2>
-		<resource name="3/shape" resource_type="ConvexPolygonShape2D" path="local://3">  </resource>
+		<array name="3/shapes" len="1" shared="false">
+			<resource  resource_type="Shape2D" path="local://4">  </resource>
+		</array>
 		<string name="4/name"> "corner" </string>
 		<resource name="4/texture" resource_type="Texture" path="res://tiles_demo.png">  </resource>
-		<vector2 name="4/offset"> 0, 0 </vector2>
+		<vector2 name="4/tex_offset"> 0, 0 </vector2>
+		<vector2 name="4/shape_offset"> 32, 32 </vector2>
 		<rect2 name="4/region"> 64, 128, 64, 64 </rect2>
-		<resource name="4/shape" resource_type="ConvexPolygonShape2D" path="local://4">  </resource>
+		<array name="4/shapes" len="1" shared="false">
+			<resource  resource_type="Shape2D" path="local://5">  </resource>
+		</array>
 		<string name="5/name"> "flowers" </string>
 		<resource name="5/texture" resource_type="Texture" path="res://tiles_demo.png">  </resource>
-		<vector2 name="5/offset"> 0, 0 </vector2>
+		<vector2 name="5/tex_offset"> 0, 0 </vector2>
+		<vector2 name="5/shape_offset"> 32, 32 </vector2>
 		<rect2 name="5/region"> 192, 192, 64, 64 </rect2>
-		<resource name="5/shape" resource_type="ConvexPolygonShape2D" path="local://5">  </resource>
+		<array name="5/shapes" len="1" shared="false">
+			<resource  resource_type="Shape2D" path="local://6">  </resource>
+		</array>
 		<string name="6/name"> "tree_base" </string>
 		<resource name="6/texture" resource_type="Texture" path="res://tiles_demo.png">  </resource>
-		<vector2 name="6/offset"> 0, 0 </vector2>
+		<vector2 name="6/tex_offset"> 0, 0 </vector2>
+		<vector2 name="6/shape_offset"> 32, 32 </vector2>
 		<rect2 name="6/region"> 256, 192, 64, 64 </rect2>
-		<resource name="6/shape" resource_type="ConvexPolygonShape2D" path="local://6">  </resource>
+		<array name="6/shapes" len="1" shared="false">
+			<resource  resource_type="Shape2D" path="local://7">  </resource>
+		</array>
 		<string name="7/name"> "tree_mid" </string>
 		<resource name="7/texture" resource_type="Texture" path="res://tiles_demo.png">  </resource>
-		<vector2 name="7/offset"> 0, 0 </vector2>
+		<vector2 name="7/tex_offset"> 0, 0 </vector2>
+		<vector2 name="7/shape_offset"> 0, 0 </vector2>
 		<rect2 name="7/region"> 256, 128, 64, 64 </rect2>
-		<resource name="7/shape"></resource>		<string name="8/name"> "tree_mid 2" </string>
+		<array name="7/shapes" len="0" shared="false">
+		</array>
+		<string name="8/name"> "tree_mid 2" </string>
 		<resource name="8/texture" resource_type="Texture" path="res://tiles_demo.png">  </resource>
-		<vector2 name="8/offset"> 0, 0 </vector2>
+		<vector2 name="8/tex_offset"> 0, 0 </vector2>
+		<vector2 name="8/shape_offset"> 0, 0 </vector2>
 		<rect2 name="8/region"> 256, 64, 64, 64 </rect2>
-		<resource name="8/shape"></resource>		<string name="9/name"> "tree_top" </string>
+		<array name="8/shapes" len="0" shared="false">
+		</array>
+		<string name="9/name"> "tree_top" </string>
 		<resource name="9/texture" resource_type="Texture" path="res://tiles_demo.png">  </resource>
-		<vector2 name="9/offset"> 0, 0 </vector2>
+		<vector2 name="9/tex_offset"> 0, 0 </vector2>
+		<vector2 name="9/shape_offset"> 0, 0 </vector2>
 		<rect2 name="9/region"> 256, 0, 64, 64 </rect2>
-		<resource name="9/shape"></resource>		<string name="10/name"> "solid" </string>
+		<array name="9/shapes" len="0" shared="false">
+		</array>
+		<string name="10/name"> "solid" </string>
 		<resource name="10/texture" resource_type="Texture" path="res://tiles_demo.png">  </resource>
-		<vector2 name="10/offset"> 0, 0 </vector2>
+		<vector2 name="10/tex_offset"> 0, 0 </vector2>
+		<vector2 name="10/shape_offset"> 0, 0 </vector2>
 		<rect2 name="10/region"> 0, 64, 64, 64 </rect2>
-		<resource name="10/shape"></resource>		<string name="11/name"> "ceiling" </string>
+		<array name="10/shapes" len="0" shared="false">
+		</array>
+		<string name="11/name"> "ceiling" </string>
 		<resource name="11/texture" resource_type="Texture" path="res://tiles_demo.png">  </resource>
-		<vector2 name="11/offset"> 0, 0 </vector2>
+		<vector2 name="11/tex_offset"> 0, 0 </vector2>
+		<vector2 name="11/shape_offset"> 32, 32 </vector2>
 		<rect2 name="11/region"> 384, 64, 64, 64 </rect2>
-		<resource name="11/shape" resource_type="ConvexPolygonShape2D" path="local://7">  </resource>
+		<array name="11/shapes" len="1" shared="false">
+			<resource  resource_type="Shape2D" path="local://8">  </resource>
+		</array>
 		<string name="12/name"> "ramp" </string>
 		<resource name="12/texture" resource_type="Texture" path="res://tiles_demo.png">  </resource>
-		<vector2 name="12/offset"> 0, 0 </vector2>
+		<vector2 name="12/tex_offset"> 0, 0 </vector2>
+		<vector2 name="12/shape_offset"> 32, 64 </vector2>
 		<rect2 name="12/region"> 128, 128, 64, 128 </rect2>
-		<resource name="12/shape" resource_type="ConvexPolygonShape2D" path="local://8">  </resource>
+		<array name="12/shapes" len="1" shared="false">
+			<resource  resource_type="Shape2D" path="local://9">  </resource>
+		</array>
 		<string name="13/name"> "ceiling2wall" </string>
 		<resource name="13/texture" resource_type="Texture" path="res://tiles_demo.png">  </resource>
-		<vector2 name="13/offset"> 0, 0 </vector2>
+		<vector2 name="13/tex_offset"> 0, 0 </vector2>
+		<vector2 name="13/shape_offset"> 32, 32 </vector2>
 		<rect2 name="13/region"> 448, 64, 64, 64 </rect2>
-		<resource name="13/shape" resource_type="ConvexPolygonShape2D" path="local://9">  </resource>
-		<resource name="script/script"></resource>
+		<array name="13/shapes" len="1" shared="false">
+			<resource  resource_type="Shape2D" path="local://10">  </resource>
+		</array>
+		<string name="14/name"> "platform_floor" </string>
+		<resource name="14/texture" resource_type="Texture" path="res://tiles_demo.png">  </resource>
+		<vector2 name="14/tex_offset"> 0, 0 </vector2>
+		<vector2 name="14/shape_offset"> 32, 32 </vector2>
+		<rect2 name="14/region"> 128, 0, 64, 64 </rect2>
+		<array name="14/shapes" len="1" shared="false">
+			<resource  resource_type="Shape2D" path="local://11">  </resource>
+		</array>
+		<string name="15/name"> "platform_edge" </string>
+		<resource name="15/texture" resource_type="Texture" path="res://tiles_demo.png">  </resource>
+		<vector2 name="15/tex_offset"> 0, 0 </vector2>
+		<vector2 name="15/shape_offset"> 32, 32 </vector2>
+		<rect2 name="15/region"> 192, 0, 64, 64 </rect2>
+		<array name="15/shapes" len="1" shared="false">
+			<resource  resource_type="Shape2D" path="local://12">  </resource>
+		</array>
+
 	</main_resource>
 </resource_file>

文件差异内容过多而无法显示
+ 88 - 52
demos/2d/platformer/tileset_edit.xml


+ 0 - 4
demos/2d/polygon_path_finder_demo/.fscache

@@ -1,4 +0,0 @@
-::res://::1421147952
-icon.png::ImageTexture::1420046079::
-new_scene_poly_with_holes.scn::PackedScene::1421147952::
-polygonpathfinder.gd::GDScript::1421146502::

文件差异内容过多而无法显示
+ 354 - 13
demos/3d/platformer/stage.xml


+ 96 - 60
drivers/gles2/rasterizer_gles2.cpp

@@ -4038,8 +4038,16 @@ void RasterizerGLES2::render_target_set_size(RID p_render_target,int p_width,int
 	glGenTextures(1, &rt->color);
 	glBindTexture(GL_TEXTURE_2D, rt->color);
 	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,  rt->width, rt->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+	if (rt->texture_ptr->flags&VS::TEXTURE_FLAG_FILTER) {
+
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+	} else {
+
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+	}
 	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0);
@@ -5494,13 +5502,15 @@ Error RasterizerGLES2::_setup_geometry(const Geometry *p_geometry, const Materia
 				base = surf->array_local;
 				glBindBuffer(GL_ARRAY_BUFFER, 0);
 				bool can_copy_to_local=surf->local_stride * surf->array_len <= skinned_buffer_size;
+				if (p_morphs && surf->stride * surf->array_len > skinned_buffer_size)
+					can_copy_to_local=false;
+
+
 				if (!can_copy_to_local)
 					skeleton_valid=false;
 
-
 				/* compute morphs */
 
-
 				if (p_morphs && surf->morph_target_count && can_copy_to_local) {
 
 
@@ -8320,7 +8330,7 @@ void RasterizerGLES2::canvas_render_items(CanvasItem *p_item_list) {
 
 
 	CanvasItem *current_clip=NULL;
-
+	Shader *shader_cache=NULL;
 	canvas_opacity=1.0;
 	while(p_item_list) {
 
@@ -8365,6 +8375,8 @@ void RasterizerGLES2::canvas_render_items(CanvasItem *p_item_list) {
 				}
 			}
 
+			shader_cache=shader;
+
 			if (shader) {
 				canvas_shader.set_custom_shader(shader->custom_code_id);
 				if (canvas_shader.bind())
@@ -8374,50 +8386,6 @@ void RasterizerGLES2::canvas_render_items(CanvasItem *p_item_list) {
 					//todo optimize uniforms
 					shader_owner->shader_version=shader->version;
 				}
-				//this can be optimized..
-				int tex_id=1;
-				int idx=0;
-				for(Map<StringName,ShaderLanguage::Uniform>::Element *E=shader->uniforms.front();E;E=E->next()) {
-
-					Map<StringName,Variant>::Element *F=shader_owner->shader_param.find(E->key());
-
-					if ((E->get().type==ShaderLanguage::TYPE_TEXTURE || E->get().type==ShaderLanguage::TYPE_CUBEMAP)) {
-
-						RID rid;
-						if (F) {
-							rid=F->get();
-						}
-
-						if (!rid.is_valid()) {
-
-							Map<StringName,RID>::Element *DT=shader->default_textures.find(E->key());
-							if (DT) {
-								rid=DT->get();
-							}
-						}
-
-						if (rid.is_valid()) {
-
-							int loc = canvas_shader.get_custom_uniform_location(idx); //should be automatic..
-
-							glActiveTexture(GL_TEXTURE0+tex_id);
-							Texture *t=texture_owner.get(rid);
-							if (!t)
-								glBindTexture(GL_TEXTURE_2D,white_tex);
-							else
-								glBindTexture(t->target,t->tex_id);
-
-							glUniform1i(loc,tex_id);
-							tex_id++;
-						}
-					} else {
-						Variant &v=F?F->get():E->get().default_value;
-						canvas_shader.set_custom_uniform(idx,v);
-					}
-
-					idx++;
-				}
-
 
 				if (shader->has_texscreen && framebuffer.active) {
 
@@ -8426,8 +8394,8 @@ void RasterizerGLES2::canvas_render_items(CanvasItem *p_item_list) {
 
 					canvas_shader.set_uniform(CanvasShaderGLES2::TEXSCREEN_SCREEN_MULT,Vector2(float(viewport.width)/framebuffer.width,float(viewport.height)/framebuffer.height));
 					canvas_shader.set_uniform(CanvasShaderGLES2::TEXSCREEN_SCREEN_CLAMP,Color(float(x)/framebuffer.width,float(y)/framebuffer.height,float(x+viewport.width)/framebuffer.width,float(y+viewport.height)/framebuffer.height));
-					canvas_shader.set_uniform(CanvasShaderGLES2::TEXSCREEN_TEX,tex_id);
-					glActiveTexture(GL_TEXTURE0+tex_id);
+					canvas_shader.set_uniform(CanvasShaderGLES2::TEXSCREEN_TEX,max_texture_units-1);
+					glActiveTexture(GL_TEXTURE0+max_texture_units-1);
 					glBindTexture(GL_TEXTURE_2D,framebuffer.sample_color);
 					if (framebuffer.scale==1 && !canvas_texscreen_used) {
 #ifdef GLEW_ENABLED
@@ -8439,14 +8407,12 @@ void RasterizerGLES2::canvas_render_items(CanvasItem *p_item_list) {
 						}
 
 						canvas_texscreen_used=true;
-					}
-					tex_id++;
+					}	
 
-				}
-
-				if (tex_id>1) {
 					glActiveTexture(GL_TEXTURE0);
+
 				}
+
 				if (shader->has_screen_uv) {
 					canvas_shader.set_uniform(CanvasShaderGLES2::SCREEN_UV_MULT,Vector2(1.0/viewport.width,1.0/viewport.height));
 				}
@@ -8460,6 +8426,7 @@ void RasterizerGLES2::canvas_render_items(CanvasItem *p_item_list) {
 				uses_texpixel_size=shader->uses_texpixel_size;
 
 			} else {
+				shader_cache=NULL;
 				canvas_shader.set_custom_shader(0);
 				canvas_shader.bind();
 				uses_texpixel_size=false;
@@ -8471,6 +8438,59 @@ void RasterizerGLES2::canvas_render_items(CanvasItem *p_item_list) {
 			canvas_last_shader=shader_owner->shader;
 		}
 
+		if (shader_cache) {
+
+			Shader *shader = shader_cache;
+			//this can be optimized..
+			int tex_id=1;
+			int idx=0;
+			for(Map<StringName,ShaderLanguage::Uniform>::Element *E=shader->uniforms.front();E;E=E->next()) {
+
+				Map<StringName,Variant>::Element *F=shader_owner->shader_param.find(E->key());
+
+				if ((E->get().type==ShaderLanguage::TYPE_TEXTURE || E->get().type==ShaderLanguage::TYPE_CUBEMAP)) {
+
+					RID rid;
+					if (F) {
+						rid=F->get();
+					}
+
+					if (!rid.is_valid()) {
+
+						Map<StringName,RID>::Element *DT=shader->default_textures.find(E->key());
+						if (DT) {
+							rid=DT->get();
+						}
+					}
+
+					if (rid.is_valid()) {
+
+						int loc = canvas_shader.get_custom_uniform_location(idx); //should be automatic..
+
+						glActiveTexture(GL_TEXTURE0+tex_id);
+						Texture *t=texture_owner.get(rid);
+						if (!t)
+							glBindTexture(GL_TEXTURE_2D,white_tex);
+						else
+							glBindTexture(t->target,t->tex_id);
+
+						glUniform1i(loc,tex_id);
+						tex_id++;
+					}
+				} else {
+					Variant &v=F?F->get():E->get().default_value;
+					canvas_shader.set_custom_uniform(idx,v);
+				}
+
+				idx++;
+			}
+
+			if (tex_id>1) {
+				glActiveTexture(GL_TEXTURE0);
+			}
+
+		}
+
 		canvas_shader.set_uniform(CanvasShaderGLES2::MODELVIEW_MATRIX,ci->final_transform);
 		canvas_shader.set_uniform(CanvasShaderGLES2::EXTRA_MATRIX,Matrix32());
 
@@ -9581,9 +9601,6 @@ void RasterizerGLES2::init() {
 	//glClearDepth(1.0);
 	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
 
-	skinned_buffer_size = GLOBAL_DEF("rasterizer/skinned_buffer_size",DEFAULT_SKINNED_BUFFER_SIZE);
-	skinned_buffer = memnew_arr( uint8_t, skinned_buffer_size );
-
 	glGenTextures(1, &white_tex);
 	unsigned char whitetexdata[8*8*3];
 	for(int i=0;i<8*8*3;i++) {
@@ -9759,7 +9776,6 @@ void RasterizerGLES2::init() {
 void RasterizerGLES2::finish() {
 
 
-	memdelete_arr(skinned_buffer);
 }
 
 int RasterizerGLES2::get_render_info(VS::RenderInfo p_info) {
@@ -10039,10 +10055,29 @@ RasterizerGLES2* RasterizerGLES2::get_singleton() {
 	return _singleton;
 };
 
+int RasterizerGLES2::RenderList::max_elements=RenderList::DEFAULT_MAX_ELEMENTS;
+
 RasterizerGLES2::RasterizerGLES2(bool p_compress_arrays,bool p_keep_ram_copy,bool p_default_fragment_lighting,bool p_use_reload_hooks) {
 
 	_singleton = this;
 
+	RenderList::max_elements=GLOBAL_DEF("rasterizer/max_render_elements",(int)RenderList::DEFAULT_MAX_ELEMENTS);
+	if (RenderList::max_elements>64000)
+		RenderList::max_elements=64000;
+	if (RenderList::max_elements<1024)
+		RenderList::max_elements=1024;
+
+	opaque_render_list.init();
+	alpha_render_list.init();
+
+	skinned_buffer_size = GLOBAL_DEF("rasterizer/skeleton_buffer_size_kb",DEFAULT_SKINNED_BUFFER_SIZE);
+	if (skinned_buffer_size<256)
+		skinned_buffer_size=256;
+	if (skinned_buffer_size>16384)
+		skinned_buffer_size=16384;
+	skinned_buffer_size*=1024;
+	skinned_buffer = memnew_arr( uint8_t, skinned_buffer_size );
+
 	keep_copies=p_keep_ram_copy;
 	use_reload_hooks=p_use_reload_hooks;
 	pack_arrays=p_compress_arrays;
@@ -10086,6 +10121,7 @@ RasterizerGLES2::RasterizerGLES2(bool p_compress_arrays,bool p_keep_ram_copy,boo
 
 RasterizerGLES2::~RasterizerGLES2() {
 
+	memdelete_arr(skinned_buffer);
 };
 
 

+ 21 - 7
drivers/gles2/rasterizer_gles2.h

@@ -65,7 +65,7 @@ class RasterizerGLES2 : public Rasterizer {
 
 		MAX_SCENE_LIGHTS=2048,
 		LIGHT_SPOT_BIT=0x80,
-		DEFAULT_SKINNED_BUFFER_SIZE = 2048 * 1024, // 10k vertices
+		DEFAULT_SKINNED_BUFFER_SIZE = 2048, // 10k vertices
 		MAX_HW_LIGHTS = 1,
 	};
 
@@ -827,15 +827,18 @@ class RasterizerGLES2 : public Rasterizer {
 	GLuint gui_quad_buffer;
 
 
+
 	struct RenderList {
 
 		enum {
-			MAX_ELEMENTS=4096,
+			DEFAULT_MAX_ELEMENTS=4096,
 			MAX_LIGHTS=4,
 			SORT_FLAG_SKELETON=1,
 			SORT_FLAG_INSTANCING=2,
 		};
 
+		static int max_elements;
+
 		struct Element {
 
 
@@ -868,8 +871,8 @@ class RasterizerGLES2 : public Rasterizer {
 		};
 
 
-		Element _elements[MAX_ELEMENTS];
-		Element *elements[MAX_ELEMENTS];
+		Element *_elements;
+		Element **elements;
 		int element_count;
 
 		void clear() {
@@ -1004,17 +1007,28 @@ class RasterizerGLES2 : public Rasterizer {
 		}
 		_FORCE_INLINE_ Element* add_element() {
 
-			if (element_count>MAX_ELEMENTS)
+			if (element_count>=max_elements)
 				return NULL;
 			elements[element_count]=&_elements[element_count];
 			return elements[element_count++];
 		}
 
-		RenderList() {
+		void init() {
 
 			element_count = 0;
-			for (int i=0;i<MAX_ELEMENTS;i++)
+			elements=memnew_arr(Element*,max_elements);
+			_elements=memnew_arr(Element,max_elements);
+			for (int i=0;i<max_elements;i++)
 				elements[i]=&_elements[i]; // assign elements
+
+		}
+
+		RenderList() {
+
+		}
+		~RenderList() {
+			memdelete_arr(elements);
+			memdelete_arr(_elements);
 		}
 	};
 

+ 6 - 1
drivers/unix/ip_unix.cpp

@@ -50,11 +50,16 @@
  #ifdef ANDROID_ENABLED
   #include "platform/android/ifaddrs_android.h"
  #else
+  #ifdef __FreeBSD__
+   #include <sys/types.h>
+  #endif
   #include <ifaddrs.h>
  #endif
  #include <arpa/inet.h>
  #include <sys/socket.h>
-
+ #ifdef __FreeBSD__
+  #include <netinet/in.h>
+ #endif
 #endif
 
 IP_Address IP_Unix::_resolve_hostname(const String& p_hostname) {

+ 19 - 1
drivers/unix/os_unix.cpp

@@ -44,7 +44,9 @@
 #include "stream_peer_tcp_posix.h"
 #include "packet_peer_udp_posix.h"
 
-
+#ifdef __FreeBSD__
+#include <sys/param.h>
+#endif
 #include <stdarg.h>
 #include <sys/time.h>
 #include <sys/wait.h>
@@ -305,7 +307,17 @@ Error OS_Unix::execute(const String& p_path, const List<String>& p_arguments,boo
 			args.push_back((char*)cs[i].get_data());// shitty C cast
 		args.push_back(0);
 
+#ifdef __FreeBSD__
+		if(p_path.find("/")) {
+			// exec name contains path so use it
+			execv(p_path.utf8().get_data(),&args[0]);
+		}else{
+			// use program name and search through PATH to find it
+			execvp(getprogname(),&args[0]);
+		}
+#else
 		execv(p_path.utf8().get_data(),&args[0]);
+#endif
 		// still alive? something failed..
 		fprintf(stderr,"**ERROR** OS_Unix::execute - Could not create child process while executing: %s\n",p_path.utf8().get_data());
 		abort();
@@ -421,6 +433,12 @@ String OS_Unix::get_executable_path() const {
 		return OS::get_executable_path();
 	}
 	return b;
+#elif defined(__FreeBSD__)
+	char resolved_path[MAXPATHLEN];
+
+	realpath(OS::get_executable_path().utf8().get_data(), resolved_path);
+
+	return String(resolved_path);
 #else
 	ERR_PRINT("Warning, don't know how to obtain executable path on this OS! Please override this function properly.");
 	return OS::get_executable_path();

+ 1 - 1
modules/gdscript/gd_editor.cpp

@@ -51,7 +51,7 @@ String GDScriptLanguage::get_template(const String& p_class_name, const String&
 	"# var a=2\n"+
 	"# var b=\"textvar\"\n\n"+
 	"func _ready():\n"+
-	"\t# Initalization here\n"+
+	"\t# Initialization here\n"+
 	"\tpass\n"+
 	"\n"+
 	"\n";

+ 29 - 3
modules/gdscript/gd_functions.cpp

@@ -89,6 +89,8 @@ const char *GDFunctions::get_func_name(Function p_func) {
 		"printt",
 		"printerr",
 		"printraw",
+		"var2str",
+		"str2var",
 		"range",
 		"load",
 		"inst2dict",
@@ -577,10 +579,23 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
 			r_ret=Variant();
 
 		} break;
+		case VAR_TO_STR: {
+			VALIDATE_ARG_COUNT(1);
+			r_ret=p_args[0]->get_construct_string();
+		} break;
+		case STR_TO_VAR: {
+			VALIDATE_ARG_COUNT(1);
+			if (p_args[0]->get_type()!=Variant::STRING) {
+				r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+				r_error.argument=0;
+				r_error.expected=Variant::STRING;
+				r_ret=Variant();
+				return;
+			}
+			Variant::construct_from_string(*p_args[0],r_ret);
+		} break;
 		case GEN_RANGE: {
 
-
-
 			switch(p_arg_count) {
 
 				case 0: {
@@ -861,7 +876,6 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
 				}
 			}
 
-
 			r_ret = gdscr->_new(NULL,0,r_error);
 
 		} break;
@@ -1224,6 +1238,18 @@ MethodInfo GDFunctions::get_info(Function p_func) {
 			return mi;
 
 		} break;
+		case VAR_TO_STR: {
+			MethodInfo mi("var2str",PropertyInfo(Variant::NIL,"var"));
+			mi.return_val.type=Variant::STRING;
+			return mi;
+
+		} break;
+		case STR_TO_VAR: {
+
+			MethodInfo mi("str2var:var",PropertyInfo(Variant::STRING,"string"));
+			mi.return_val.type=Variant::NIL;
+			return mi;
+		} break;
 		case GEN_RANGE: {
 
 			MethodInfo mi("range",PropertyInfo(Variant::NIL,"..."));

+ 2 - 0
modules/gdscript/gd_functions.h

@@ -85,6 +85,8 @@ public:
 		TEXT_PRINT_TABBED,
 		TEXT_PRINTERR,
 		TEXT_PRINTRAW,
+		VAR_TO_STR,
+		STR_TO_VAR,
 		GEN_RANGE,
 		RESOURCE_LOAD,
 		INST2DICT,

+ 12 - 7
platform/iphone/app_delegate.mm

@@ -84,13 +84,11 @@ static int frame_count = 0;
 	switch (frame_count) {
 
 	case 0: {
-
-		int backingWidth;
-		int backingHeight;
-		glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
-		glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
-
-		iphone_main(backingWidth, backingHeight, gargc, gargv);
+        int backingWidth;
+        int backingHeight;
+        glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
+        glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
+		
 
 		OS::VideoMode vm;
 		vm.fullscreen = true;
@@ -198,6 +196,13 @@ static int frame_count = 0;
 	//glView.autoresizesSubviews = YES;
 	//[glView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleWidth];
 
+    int backingWidth;
+    int backingHeight;
+    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
+    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
+    
+    iphone_main(backingWidth, backingHeight, gargc, gargv);
+    
 	view_controller = [[ViewController alloc] init];
 	view_controller.view = glView;
 	window.rootViewController = view_controller;

+ 8 - 0
platform/iphone/gl_view.h

@@ -34,6 +34,8 @@
 #import <MediaPlayer/MediaPlayer.h>
 #import <AVFoundation/AVFoundation.h>
 
+#define USE_CADISPLAYLINK      1   //iOS version 3.1+ is required
+
 @protocol GLViewDelegate;
 
 @interface GLView : UIView<UIKeyInput>
@@ -51,8 +53,14 @@
 	// OpenGL name for the depth buffer that is attached to viewFramebuffer, if it exists (0 if it does not exist)
 	GLuint depthRenderbuffer;
 	
+#if USE_CADISPLAYLINK
+	// CADisplayLink available on 3.1+ synchronizes the animation timer & drawing with the refresh rate of the display, only supports animation intervals of 1/60 1/30 & 1/15
+	CADisplayLink *displayLink;
+#else
 	// An animation timer that, when animation is started, will periodically call -drawView at the given rate.
 	NSTimer *animationTimer;
+#endif
+	
 	NSTimeInterval animationInterval;
 	
 	// Delegate to do our drawing, called by -drawView, which can be called manually or via the animation timer.

+ 32 - 0
platform/iphone/gl_view.mm

@@ -413,7 +413,19 @@ static void clear_touches() {
 		return;
 	active = TRUE;
 	printf("start animation!\n");
+#if USE_CADISPLAYLINK
+	// Approximate frame rate
+	// assumes device refreshes at 60 fps
+	int frameInterval = (int) floor(animationInterval * 60.0f);
+
+	displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(drawView)];
+	[displayLink setFrameInterval:frameInterval];
+
+	// Setup DisplayLink in main thread
+	[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
+#else
 	animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:@selector(drawView) userInfo:nil repeats:YES];
+#endif
 
 	if (video_playing)
 	{
@@ -427,8 +439,13 @@ static void clear_touches() {
 		return;
 	active = FALSE;
 	printf("******** stop animation!\n");
+#if USE_CADISPLAYLINK
+	[displayLink invalidate];
+	displayLink = nil;
+#else
 	[animationTimer invalidate];
 	animationTimer = nil;
+#endif
 	clear_touches();
 
 	if (video_playing)
@@ -441,7 +458,11 @@ static void clear_touches() {
 {
 	animationInterval = interval;
 	
+#if USE_CADISPLAYLINK
+	if(displayLink)
+#else
 	if(animationTimer)
+#endif
 	{
 		[self stopAnimation];
 		[self startAnimation];
@@ -451,6 +472,17 @@ static void clear_touches() {
 // Updates the OpenGL view when the timer fires
 - (void)drawView
 {
+#if USE_CADISPLAYLINK
+	// Pause the CADisplayLink to avoid recursion
+	[displayLink setPaused: YES];
+
+	// Process all input events
+	while(CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, TRUE) == kCFRunLoopRunHandledSource);
+
+	// We are good to go, resume the CADisplayLink
+	[displayLink setPaused: NO];
+#endif
+
 	if (!active) {
 		printf("draw view not active!\n");
 		return;

+ 1 - 1
platform/isim/detect.py

@@ -22,7 +22,7 @@ def get_opts():
 	return [
 		('ISIMPLATFORM', 'name of the iphone platform', 'iPhoneSimulator'),
 		('ISIMPATH', 'the path to iphone toolchain', '/Applications/Xcode.app/Contents/Developer/Platforms/${ISIMPLATFORM}.platform'),
-		('ISIMSDK', 'path to the iphone SDK', '$ISIMPATH/Developer/SDKs/${ISIMPLATFORM}7.1.sdk'),
+		('ISIMSDK', 'path to the iphone SDK', '$ISIMPATH/Developer/SDKs/${ISIMPLATFORM}.sdk'),
 		('game_center', 'Support for game center', 'yes'),
 		('store_kit', 'Support for in-app store', 'yes'),
 		('ios_gles22_override', 'Force GLES2.0 on iOS', 'yes'),

+ 12 - 1
platform/osx/os_osx.mm

@@ -1093,8 +1093,19 @@ void OS_OSX::warp_mouse_pos(const Point2& p_to) {
         mouse_y = p_to.y;
     }
     else{ //set OS position
-        CGPoint lMouseWarpPos = {p_to.x, p_to.y};
         
+	/* this code has not been tested, please be a kind soul and fix it if it fails! */
+
+	//local point in window coords
+	NSPoint localPoint = { p_to.x, p_to.y };
+
+	NSPoint pointInWindow = [window_view convertPoint:localPoint toView:nil];
+	NSPoint pointOnScreen = [[window_view window] convertRectToScreen:(CGRect){.origin=pointInWindow}];
+
+	//point in scren coords
+	CGPoint lMouseWarpPos = { pointOnScreen.x, pointOnScreen.y};
+
+	//do the warping
         CGEventSourceRef lEventRef = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
         CGEventSourceSetLocalEventsSuppressionInterval(lEventRef, 0.0);
         CGAssociateMouseAndMouseCursorPosition(false);

+ 61 - 6
platform/windows/os_windows.cpp

@@ -54,6 +54,8 @@
 #include "io/marshalls.h"
 
 #include "shlobj.h"
+#include <regstr.h>
+
 static const WORD MAX_CONSOLE_LINES = 1500;
 
 extern "C" {
@@ -593,10 +595,11 @@ LRESULT OS_Windows::WndProc(HWND hWnd,UINT uMsg, WPARAM	wParam,	LPARAM	lParam) {
 
 			ERR_BREAK(key_event_pos >= KEY_EVENT_BUFFER_SIZE);
 
+			// Make sure we don't include modifiers for the modifier key itself.
 			KeyEvent ke;
-			ke.mod_state.shift=shift_mem;
-			ke.mod_state.alt=alt_mem;
-			ke.mod_state.control=control_mem;
+			ke.mod_state.shift= (wParam != VK_SHIFT) ? shift_mem : false;
+			ke.mod_state.alt= (! (wParam == VK_MENU && (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN))) ? alt_mem : false;
+			ke.mod_state.control= (wParam != VK_CONTROL) ? control_mem : false;
 			ke.mod_state.meta=meta_mem;
 			ke.uMsg=uMsg;
 
@@ -684,6 +687,48 @@ LRESULT CALLBACK WndProc(HWND	hWnd,UINT uMsg,	WPARAM	wParam,	LPARAM	lParam)	{
 
 }
 
+
+String OS_Windows::get_joystick_name(int id, JOYCAPS jcaps)
+{
+	char buffer [256];
+	char OEM [256];
+	HKEY hKey;
+	DWORD sz;
+	int res;
+
+	_snprintf(buffer, sizeof(buffer), "%s\\%s\\%s",
+				REGSTR_PATH_JOYCONFIG, jcaps.szRegKey,
+				REGSTR_KEY_JOYCURR );
+	res = RegOpenKeyEx(HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey);
+	if (res != ERROR_SUCCESS)
+	{
+		res = RegOpenKeyEx(HKEY_CURRENT_USER, buffer, 0, KEY_QUERY_VALUE, &hKey);
+		if (res != ERROR_SUCCESS) 
+			return "";
+	}
+
+	sz = sizeof(OEM);
+	_snprintf( buffer, sizeof(buffer), "Joystick%d%s", id + 1, REGSTR_VAL_JOYOEMNAME);
+	res = RegQueryValueEx ( hKey, buffer, 0, 0, (LPBYTE) OEM, &sz);
+	RegCloseKey ( hKey );
+	if (res != ERROR_SUCCESS) 
+		return "";
+
+	_snprintf( buffer, sizeof(buffer), "%s\\%s", REGSTR_PATH_JOYOEM, OEM);
+	res = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, buffer, 0, KEY_QUERY_VALUE, &hKey);
+	if (res != ERROR_SUCCESS) 
+		return "";
+
+	sz = sizeof(buffer);
+	res = RegQueryValueEx(hKey, REGSTR_VAL_JOYOEMNAME, 0, 0, (LPBYTE) buffer,
+						  &sz);
+	RegCloseKey(hKey);
+	if (res != ERROR_SUCCESS) 
+		return "";
+
+	return String(buffer);
+}
+
 void OS_Windows::probe_joysticks() {
 
 	static uint32_t last_attached = 0;
@@ -725,7 +770,13 @@ void OS_Windows::probe_joysticks() {
 			JOYCAPS jcaps;
 			MMRESULT res = joyGetDevCaps(JOYSTICKID1 + i, &jcaps, sizeof(jcaps));
 			if (res == JOYERR_NOERROR) {
-				joy.name = jcaps.szPname;
+				String name = get_joystick_name(JOYSTICKID1 + i, jcaps);
+				if ( name == "")
+					joy.name = jcaps.szPname;
+				else
+					joy.name = name;
+				
+					
 			};
 		};
 
@@ -1381,9 +1432,13 @@ void OS_Windows::warp_mouse_pos(const Point2& p_to) {
 		old_y=p_to.y;
 	} else {
 
-		SetCursorPos(p_to.x, p_to.y);
-	}
+		POINT p;
+		p.x=p_to.x;
+		p.y=p_to.y;
+		ClientToScreen(hWnd,&p);
 
+		SetCursorPos(p.x,p.y);
+	}
 }
 
 Point2 OS_Windows::get_mouse_pos() const {

+ 1 - 0
platform/windows/os_windows.h

@@ -187,6 +187,7 @@ protected:
 	void probe_joysticks();
 	void process_joysticks();
 	void process_key_events();
+	String get_joystick_name( int id, JOYCAPS jcaps);
 	
 	struct ProcessInfo {
 

+ 11 - 17
platform/x11/detect.py

@@ -71,24 +71,23 @@ def configure(env):
 		else:
 			env["bits"]="32"
 
-
 	env.Append(CPPPATH=['#platform/x11'])
 	if (env["use_llvm"]=="yes"):
-		env["CC"]="clang"
-		env["CXX"]="clang++"
-		env["LD"]="clang++"
-		if (env["use_sanitizer"]=="yes"):
-			env.Append(CXXFLAGS=['-fsanitize=address','-fno-omit-frame-pointer'])
-			env.Append(LINKFLAGS=['-fsanitize=address'])
-			env.extra_suffix=".llvms"
-		else:
-			env.extra_suffix=".llvm"
+		if 'clang++' not in env['CXX']:
+			env["CC"]="clang"
+			env["CXX"]="clang++"
+			env["LD"]="clang++"
+		env.Append(CPPFLAGS=['-DTYPED_METHOD_BIND'])
+		env.extra_suffix=".llvm"
+
 		if (env["colored"]=="yes"):
 			if sys.stdout.isatty():
 				env.Append(CXXFLAGS=["-fcolor-diagnostics"])
 
-
-
+	if (env["use_sanitizer"]=="yes"):
+		env.Append(CXXFLAGS=['-fsanitize=address','-fno-omit-frame-pointer'])
+		env.Append(LINKFLAGS=['-fsanitize=address'])
+		env.extra_suffix+="s"
 
 	#if (env["tools"]=="no"):
 	#	#no tools suffix
@@ -146,11 +145,6 @@ def configure(env):
 		env.Append(LINKFLAGS=['-m64','-L/usr/lib/i686-linux-gnu'])
 
 
-	if (env["CXX"]=="clang++"):
-		env.Append(CPPFLAGS=['-DTYPED_METHOD_BIND'])
-		env["CC"]="clang"
-		env["LD"]="clang++"
-
 	import methods
 
 	env.Append( BUILDERS = { 'GLSL120' : env.Builder(action = methods.build_legacygl_headers, suffix = 'glsl.h',src_suffix = '.glsl') } )

+ 5 - 1
platform/x11/os_x11.cpp

@@ -480,8 +480,12 @@ void OS_X11::warp_mouse_pos(const Point2& p_to) {
 		last_mouse_pos=p_to;
 	} else {
 
+		/*XWindowAttributes xwa;
+		XGetWindowAttributes(x11_display, x11_window, &xwa);
+		printf("%d %d\n", xwa.x, xwa.y); needed? */
+
 		XWarpPointer(x11_display, None, x11_window,
-			      0,0,0,0, (int)p_to.x, (int)p_to.y);
+			      0,0,0,0, (int)p_to.x , (int)p_to.y);
 	}
 
 }

+ 22 - 1
scene/2d/canvas_item.cpp

@@ -810,6 +810,23 @@ void CanvasItem::_shader_changed() {
 }
 #endif
 
+void CanvasItem::get_argument_options(const StringName& p_function,int p_idx,List<String>*r_options) const {
+
+	if (p_idx==0 && shader.is_valid() && (p_function.operator String()=="get_shader_param" || p_function.operator String()=="set_shader_param")) {
+
+		List<PropertyInfo> pl;
+		shader->get_param_list(&pl);
+		for(List<PropertyInfo>::Element *E=pl.front();E;E=E->next()) {
+			r_options->push_back("\""+E->get().name.replace_first("shader_param/","")+"\"");
+		}
+
+		return;
+	}
+
+	Node::get_argument_options(p_function,p_idx,r_options);
+}
+
+
 void CanvasItem::_bind_methods() {
 
 	ObjectTypeDB::bind_method(_MD("_sort_children"),&CanvasItem::_sort_children);
@@ -845,7 +862,7 @@ void CanvasItem::_bind_methods() {
 	ObjectTypeDB::bind_method(_MD("set_self_opacity","self_opacity"),&CanvasItem::set_self_opacity);
 	ObjectTypeDB::bind_method(_MD("get_self_opacity"),&CanvasItem::get_self_opacity);
 
-	ObjectTypeDB::bind_method(_MD("set_draw_behind_parent","enabe"),&CanvasItem::set_draw_behind_parent);
+	ObjectTypeDB::bind_method(_MD("set_draw_behind_parent","enable"),&CanvasItem::set_draw_behind_parent);
 	ObjectTypeDB::bind_method(_MD("is_draw_behind_parent_enabled"),&CanvasItem::is_draw_behind_parent_enabled);
 
 	ObjectTypeDB::bind_method(_MD("_set_on_top","on_top"),&CanvasItem::_set_on_top);
@@ -882,6 +899,10 @@ void CanvasItem::_bind_methods() {
 	ObjectTypeDB::bind_method(_MD("set_use_parent_shader","enable"),&CanvasItem::set_use_parent_shader);
 	ObjectTypeDB::bind_method(_MD("get_use_parent_shader"),&CanvasItem::get_use_parent_shader);
 
+	ObjectTypeDB::bind_method(_MD("set_shader_param","param","value"),&CanvasItem::set_shader_param);
+	ObjectTypeDB::bind_method(_MD("get_shader_param","param"),&CanvasItem::get_shader_param);
+
+
 	BIND_VMETHOD(MethodInfo("_draw"));
 
 	ADD_PROPERTY( PropertyInfo(Variant::BOOL,"visibility/visible"), _SCS("_set_visible_"),_SCS("_is_visible_") );

+ 2 - 0
scene/2d/canvas_item.h

@@ -221,6 +221,8 @@ public:
 	void set_shader_param(const StringName& p_param,const Variant& p_value);
 	Variant get_shader_param(const StringName& p_param) const;
 
+	void get_argument_options(const StringName& p_function,int p_idx,List<String>*r_options) const;
+
 	CanvasItem();
 	~CanvasItem();
 };

+ 182 - 0
scene/2d/light_2d.cpp

@@ -0,0 +1,182 @@
+#include "light_2d.h"
+#include "servers/visual_server.h"
+
+void Light2D::set_enabled( bool p_enabled) {
+
+	VS::get_singleton()->canvas_light_set_enabled(canvas_light,p_enabled);
+	enabled=p_enabled;
+}
+
+bool Light2D::is_enabled() const {
+
+	return enabled;
+}
+
+void Light2D::set_texture( const Ref<Texture>& p_texture) {
+
+	texture=p_texture;
+	if (texture.is_valid())
+		VS::get_singleton()->canvas_light_set_texture(canvas_light,texture->get_rid());
+	else
+		VS::get_singleton()->canvas_light_set_texture(canvas_light,RID());
+}
+
+Ref<Texture> Light2D::get_texture() const {
+
+	return texture;
+}
+
+void Light2D::set_texture_offset( const Vector2& p_offset) {
+
+	texture_offset=p_offset;
+	VS::get_singleton()->canvas_light_set_texture_offset(canvas_light,texture_offset);
+}
+
+Vector2 Light2D::get_texture_offset() const {
+
+	return texture_offset;
+}
+
+void Light2D::set_color( const Color& p_color) {
+
+	color=p_color;
+	VS::get_singleton()->canvas_light_set_color(canvas_light,color);
+
+}
+Color Light2D::get_color() const {
+
+	return color;
+}
+
+void Light2D::set_height( float p_height) {
+
+	height=p_height;
+	VS::get_singleton()->canvas_light_set_height(canvas_light,height);
+
+}
+float Light2D::get_height() const {
+
+	return height;
+}
+
+void Light2D::set_z_range_min( int p_min_z) {
+
+	z_min=p_min_z;
+	VS::get_singleton()->canvas_light_set_z_range(canvas_light,z_min,z_max);
+
+}
+int Light2D::get_z_range_min() const {
+
+	return z_min;
+}
+
+void Light2D::set_z_range_max( int p_max_z) {
+
+	z_max=p_max_z;
+	VS::get_singleton()->canvas_light_set_z_range(canvas_light,z_min,z_max);
+
+}
+int Light2D::get_z_range_max() const {
+
+	return z_max;
+}
+
+void Light2D::set_item_mask( int p_mask) {
+
+	item_mask=p_mask;
+	VS::get_singleton()->canvas_light_set_item_mask(canvas_light,item_mask);
+
+}
+
+int Light2D::get_item_mask() const {
+
+	return item_mask;
+}
+
+void Light2D::set_blend_mode( LightBlendMode p_blend_mode ) {
+
+	blend_mode=p_blend_mode;
+	VS::get_singleton()->canvas_light_set_blend_mode(canvas_light,VS::CanvasLightBlendMode(blend_mode));
+}
+
+Light2D::LightBlendMode Light2D::get_blend_mode() const {
+
+	return blend_mode;
+}
+
+void Light2D::set_shadow_enabled( bool p_enabled) {
+
+	shadow=p_enabled;
+	VS::get_singleton()->canvas_light_set_shadow_enabled(canvas_light,shadow);
+
+}
+bool Light2D::is_shadow_enabled() const {
+
+	return shadow;
+}
+
+void Light2D::_bind_methods() {
+
+
+	ObjectTypeDB::bind_method(_MD("set_enabled","enabled"),&Light2D::set_enabled);
+	ObjectTypeDB::bind_method(_MD("is_enabled"),&Light2D::is_enabled);
+
+	ObjectTypeDB::bind_method(_MD("set_texture","texture"),&Light2D::set_texture);
+	ObjectTypeDB::bind_method(_MD("get_texture"),&Light2D::get_texture);
+
+	ObjectTypeDB::bind_method(_MD("set_texture_offset","texture_offset"),&Light2D::set_texture_offset);
+	ObjectTypeDB::bind_method(_MD("get_texture_offset"),&Light2D::get_texture_offset);
+
+	ObjectTypeDB::bind_method(_MD("set_color","color"),&Light2D::set_color);
+	ObjectTypeDB::bind_method(_MD("get_color"),&Light2D::get_color);
+
+	ObjectTypeDB::bind_method(_MD("set_height","height"),&Light2D::set_height);
+	ObjectTypeDB::bind_method(_MD("get_height"),&Light2D::get_height);
+
+	ObjectTypeDB::bind_method(_MD("set_z_range_min","z"),&Light2D::set_z_range_min);
+	ObjectTypeDB::bind_method(_MD("get_z_range_min"),&Light2D::get_z_range_min);
+
+	ObjectTypeDB::bind_method(_MD("set_z_range_max","z"),&Light2D::set_z_range_max);
+	ObjectTypeDB::bind_method(_MD("get_z_range_max"),&Light2D::get_z_range_max);
+
+	ObjectTypeDB::bind_method(_MD("set_item_mask","item_mask"),&Light2D::set_item_mask);
+	ObjectTypeDB::bind_method(_MD("get_item_mask"),&Light2D::get_item_mask);
+
+	ObjectTypeDB::bind_method(_MD("set_blend_mode","blend_mode"),&Light2D::set_blend_mode);
+	ObjectTypeDB::bind_method(_MD("get_blend_mode"),&Light2D::get_blend_mode);
+
+	ObjectTypeDB::bind_method(_MD("set_shadow_enabled","enabled"),&Light2D::set_shadow_enabled);
+	ObjectTypeDB::bind_method(_MD("is_shadow_enabled"),&Light2D::is_shadow_enabled);
+
+	ADD_PROPERTY( PropertyInfo(Variant::BOOL,"enabled"),_SCS("set_enabled"),_SCS("is_enabled"));
+	ADD_PROPERTY( PropertyInfo(Variant::OBJECT,"texture",PROPERTY_HINT_RESOURCE_TYPE,"Texture"),_SCS("set_texture"),_SCS("get_texture"));
+	ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"texture_offset"),_SCS("set_texture_offset"),_SCS("get_texture_offset"));
+	ADD_PROPERTY( PropertyInfo(Variant::COLOR,"color"),_SCS("set_color"),_SCS("get_color"));
+	ADD_PROPERTY( PropertyInfo(Variant::REAL,"height"),_SCS("set_height"),_SCS("get_height"));
+	ADD_PROPERTY( PropertyInfo(Variant::INT,"z_range_min",PROPERTY_HINT_RANGE,itos(VS::CANVAS_ITEM_Z_MIN)+","+itos(VS::CANVAS_ITEM_Z_MAX)+",1"),_SCS("set_z_range_min"),_SCS("get_z_range_min"));
+	ADD_PROPERTY( PropertyInfo(Variant::INT,"z_range_max",PROPERTY_HINT_RANGE,itos(VS::CANVAS_ITEM_Z_MIN)+","+itos(VS::CANVAS_ITEM_Z_MAX)+",1"),_SCS("set_z_range_max"),_SCS("get_z_range_max"));
+	ADD_PROPERTY( PropertyInfo(Variant::INT,"item_mask",PROPERTY_HINT_ALL_FLAGS),_SCS("set_item_mask"),_SCS("get_item_mask"));
+	ADD_PROPERTY( PropertyInfo(Variant::INT,"blend_mode",PROPERTY_HINT_ENUM,"Add,Sub,Mul,Dodge,Burn,Lighten,Darken,Overlay,Screen"),_SCS("set_blend_mode"),_SCS("get_blend_mode"));
+	ADD_PROPERTY( PropertyInfo(Variant::BOOL,"shadow_enabled"),_SCS("set_shadow_enabled"),_SCS("is_shadow_enabled"));
+
+
+}
+
+Light2D::Light2D() {
+
+	canvas_light=VisualServer::get_singleton()->canvas_light_create();
+	enabled=true;
+	shadow=false;
+	color=Color(1,1,1);
+	height=0;
+	z_min=-1024;
+	z_max=1024;
+	item_mask=1;
+	blend_mode=LIGHT_BLEND_ADD;
+
+}
+
+Light2D::~Light2D() {
+
+	VisualServer::get_singleton()->free(canvas_light);
+}

+ 80 - 0
scene/2d/light_2d.h

@@ -0,0 +1,80 @@
+#ifndef LIGHT_2D_H
+#define LIGHT_2D_H
+
+#include "scene/2d/node_2d.h"
+
+class Light2D : public Node2D {
+
+	OBJ_TYPE(Light2D,Node2D);
+public:
+
+	enum LightBlendMode {
+		LIGHT_BLEND_ADD,
+		LIGHT_BLEND_SUB,
+		LIGHT_BLEND_MULTIPLY,
+		LIGHT_BLEND_DODGE,
+		LIGHT_BLEND_BURN,
+		LIGHT_BLEND_LIGHTEN,
+		LIGHT_BLEND_DARKEN,
+		LIGHT_BLEND_OVERLAY,
+		LIGHT_BLEND_SCREEN,
+	};
+
+private:
+	RID canvas_light;
+	bool enabled;
+	bool shadow;
+	Color color;
+	float height;
+	int z_min;
+	int z_max;
+	int item_mask;
+	LightBlendMode blend_mode;
+	Ref<Texture> texture;
+	Vector2 texture_offset;
+
+protected:
+
+	static void _bind_methods();
+public:
+
+
+	void set_enabled( bool p_enabled);
+	bool is_enabled() const;
+
+	void set_texture( const Ref<Texture>& p_texture);
+	Ref<Texture> get_texture() const;
+
+	void set_texture_offset( const Vector2& p_offset);
+	Vector2 get_texture_offset() const;
+
+	void set_color( const Color& p_color);
+	Color get_color() const;
+
+	void set_height( float p_height);
+	float get_height() const;
+
+	void set_z_range_min( int p_min_z);
+	int get_z_range_min() const;
+
+	void set_z_range_max( int p_max_z);
+	int get_z_range_max() const;
+
+	void set_item_mask( int p_mask);
+	int get_item_mask() const;
+
+	void set_blend_mode( LightBlendMode p_blend_mode );
+	LightBlendMode get_blend_mode() const;
+
+	void set_shadow_enabled( bool p_enabled);
+	bool is_shadow_enabled() const;
+
+
+	Light2D();
+	~Light2D();
+};
+
+
+VARIANT_ENUM_CAST(Light2D::LightBlendMode);
+
+#endif // LIGHT_2D_H

+ 623 - 0
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;
+
+}

+ 137 - 0
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

+ 450 - 0
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;
+
+}

+ 84 - 0
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

+ 14 - 0
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"));

+ 3 - 0
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();

+ 34 - 16
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;

+ 7 - 3
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;
 

+ 1 - 1
scene/3d/camera.cpp

@@ -604,7 +604,7 @@ Vector3 Camera::project_position(const Point2& p_point) const {
 
 	Vector2 point;
 	point.x = (p_point.x/viewport_size.x) * 2.0 - 1.0;
-	point.y = (p_point.y/viewport_size.y) * 2.0 - 1.0;
+	point.y = (1.0-(p_point.y/viewport_size.y)) * 2.0 - 1.0;
 	point*=vp_size;
 
 	Vector3 p(point.x,point.y,-near);

+ 11 - 0
scene/gui/control.cpp

@@ -2267,8 +2267,10 @@ void Control::_window_sort_subwindows() {
 	if (!window->subwindow_order_dirty)
 		return;
 
+
 	window->modal_stack.sort_custom<CComparator>();
 	window->subwindows.sort_custom<CComparator>();
+
 	window->subwindow_order_dirty=false;
 
 }
@@ -2688,6 +2690,12 @@ Control *Control::get_focus_owner() const {
 	return data.window->window->key_focus;
 }
 
+
+void Control::warp_mouse(const Point2& p_to_pos) {
+	ERR_FAIL_COND(!is_inside_tree());
+	get_viewport()->warp_mouse(get_global_transform().xform(p_to_pos));
+}
+
 void Control::_bind_methods() {
 
 	ObjectTypeDB::bind_method(_MD("_window_input_event"),&Control::_window_input_event);
@@ -2784,6 +2792,9 @@ void Control::_bind_methods() {
 
 	ObjectTypeDB::bind_method(_MD("set_drag_preview","control:Control"),&Control::set_drag_preview);
 
+	ObjectTypeDB::bind_method(_MD("warp_mouse","to_pos"),&Control::warp_mouse);
+
+
 	BIND_VMETHOD(MethodInfo("_input_event",PropertyInfo(Variant::INPUT_EVENT,"event")));
 	BIND_VMETHOD(MethodInfo(Variant::VECTOR2,"get_minimum_size"));
 	BIND_VMETHOD(MethodInfo(Variant::OBJECT,"get_drag_data",PropertyInfo(Variant::VECTOR2,"pos")));

+ 1 - 1
scene/gui/control.h

@@ -380,7 +380,7 @@ public:
 
 	void grab_click_focus();
 
-
+	void warp_mouse(const Point2& p_to_pos);
 
 	Control();	
 	~Control();

+ 2 - 2
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 );

+ 4 - 0
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 );
 	}

+ 4 - 0
scene/gui/tree.cpp

@@ -2472,6 +2472,10 @@ void Tree::_notification(int p_what) {
 		}
 	}
 
+	if (p_what==NOTIFICATION_THEME_CHANGED) {
+		update_cache();
+	}
+
 }
 
 

+ 9 - 0
scene/main/viewport.cpp

@@ -29,6 +29,8 @@
 #include "viewport.h"
 #include "os/os.h"
 #include "scene/3d/spatial.h"
+#include "os/input.h"
+
 //#include "scene/3d/camera.h"
 
 #include "servers/spatial_sound_server.h"
@@ -1100,6 +1102,12 @@ void Viewport::_vp_unhandled_input(const InputEvent& p_ev) {
 
 }
 
+void Viewport::warp_mouse(const Vector2& p_pos) {
+
+	Vector2 gpos = (get_final_transform().affine_inverse() * _get_input_pre_xform()).affine_inverse().xform(p_pos);
+	Input::get_singleton()->warp_mouse_pos(gpos);
+}
+
 void Viewport::input(const InputEvent& p_event) {
 
 	ERR_FAIL_COND(!is_inside_tree());
@@ -1289,6 +1297,7 @@ void Viewport::_bind_methods() {
 	ObjectTypeDB::bind_method(_MD("is_audio_listener_2d","enable"), &Viewport::is_audio_listener_2d);
 	ObjectTypeDB::bind_method(_MD("set_render_target_to_screen_rect"), &Viewport::set_render_target_to_screen_rect);
 
+	ObjectTypeDB::bind_method(_MD("warp_mouse","to_pos"), &Viewport::warp_mouse);
 
 	ADD_PROPERTY( PropertyInfo(Variant::RECT2,"rect"), _SCS("set_rect"), _SCS("get_rect") );
 	ADD_PROPERTY( PropertyInfo(Variant::BOOL,"own_world"), _SCS("set_use_own_world"), _SCS("is_using_own_world") );

+ 2 - 0
scene/main/viewport.h

@@ -246,6 +246,8 @@ public:
 	void set_render_target_to_screen_rect(const Rect2& p_rect);
 	Rect2 get_render_target_to_screen_rect() const;
 
+	void warp_mouse(const Vector2& p_pos);
+
 	void set_physics_object_picking(bool p_enable);
 	bool get_physics_object_picking();
 

+ 7 - 0
scene/register_scene_types.cpp

@@ -79,6 +79,7 @@
 #include "scene/resources/video_stream.h"
 #include "scene/2d/particles_2d.h"
 #include "scene/2d/path_2d.h"
+#include "scene/2d/light_2d.h"
 
 #include "scene/2d/canvas_item.h"
 #include "scene/2d/sprite.h"
@@ -102,6 +103,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"
@@ -471,6 +473,7 @@ void register_scene_types() {
 	ObjectTypeDB::register_type<VisibilityNotifier2D>();
 	ObjectTypeDB::register_type<VisibilityEnabler2D>();
 	ObjectTypeDB::register_type<Polygon2D>();
+	ObjectTypeDB::register_type<Light2D>();
 	ObjectTypeDB::register_type<YSort>();
 
 	ObjectTypeDB::set_type_enabled("CollisionShape2D",false);
@@ -575,6 +578,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>();

+ 1 - 1
scene/resources/material.cpp

@@ -582,7 +582,7 @@ void ShaderMaterial::get_argument_options(const StringName& p_function,int p_idx
 			List<PropertyInfo> pl;
 			shader->get_param_list(&pl);
 			for (List<PropertyInfo>::Element *E=pl.front();E;E=E->next()) {
-				r_options->push_back("\""+E->get().name.replace("shader_param/","")+"\"");
+				r_options->push_back("\""+E->get().name.replace_first("shader_param/","")+"\"");
 			}
 		}
 	}

+ 17 - 9
scene/resources/polygon_path_finder.cpp

@@ -525,24 +525,32 @@ bool PolygonPathFinder::is_point_inside(const Vector2& p_point) const {
 
 Vector2 PolygonPathFinder::get_closest_point(const Vector2& p_point) const {
 
-	int closest_idx=-1;
 	float closest_dist=1e20;
-	for(int i=0;i<points.size()-2;i++) {
+	Vector2 closest_point;
+
+	for (Set<Edge>::Element *E=edges.front();E;E=E->next()) {
+
+		const Edge& e=E->get();
+		Vector2 seg[2]={
+			points[e.points[0]].pos,
+			points[e.points[1]].pos
+		};
+
+
+		Vector2 closest = Geometry::get_closest_point_to_segment_2d(p_point,seg);
+		float d = p_point.distance_squared_to(closest);
 
-		float d = p_point.distance_squared_to(points[i].pos);
 		if (d<closest_dist) {
 			closest_dist=d;
-			closest_idx=i;
+			closest_point=closest;
 		}
-
 	}
+	
+	ERR_FAIL_COND_V(closest_dist==1e20,Vector2());
 
-	ERR_FAIL_COND_V(closest_idx==-1,Vector2());
-
-	return points[closest_idx].pos;
+	return closest_point;
 }
 
-
 Vector<Vector2> PolygonPathFinder::get_intersections(const Vector2& p_from, const Vector2& p_to) const {
 
 	Vector<Vector2> inters;

+ 1 - 1
scene/resources/shader_graph.cpp

@@ -1289,7 +1289,7 @@ const ShaderGraph::InOutParamInfo ShaderGraph::inout_param_info[]={
 	{MODE_MATERIAL,SHADER_TYPE_FRAGMENT,"Binormal","BINORMAL","",SLOT_TYPE_VEC,SLOT_IN},
 	{MODE_MATERIAL,SHADER_TYPE_FRAGMENT,"UV","vec3(UV,0);","",SLOT_TYPE_VEC,SLOT_IN},
 	{MODE_MATERIAL,SHADER_TYPE_FRAGMENT,"UV2","UV2","",SLOT_TYPE_VEC,SLOT_IN},
-	{MODE_MATERIAL,SHADER_TYPE_FRAGMENT,"UVScreen","SCREEN_UV","",SLOT_TYPE_VEC,SLOT_IN},
+	{MODE_MATERIAL,SHADER_TYPE_FRAGMENT,"UVScreen","vec3(SCREEN_UV,0)","",SLOT_TYPE_VEC,SLOT_IN},
 	{MODE_MATERIAL,SHADER_TYPE_FRAGMENT,"PointCoord","POINT_COORD","",SLOT_TYPE_VEC,SLOT_IN},
 	{MODE_MATERIAL,SHADER_TYPE_FRAGMENT,"Color","COLOR.rgb","",SLOT_TYPE_VEC,SLOT_IN},
 	{MODE_MATERIAL,SHADER_TYPE_FRAGMENT,"Alpha","COLOR.a","",SLOT_TYPE_SCALAR,SLOT_IN},

+ 6 - 0
scene/resources/style_box.cpp

@@ -85,6 +85,12 @@ void StyleBox::_bind_methods() {
 
 	ObjectTypeDB::bind_method(_MD("draw"),&StyleBox::draw);
 
+	ADD_PROPERTYI( PropertyInfo( Variant::REAL, "content_margin/left", PROPERTY_HINT_RANGE,"-1,2048,1" ), _SCS("set_default_margin"),_SCS("get_default_margin"), MARGIN_LEFT );
+	ADD_PROPERTYI( PropertyInfo( Variant::REAL, "content_margin/right", PROPERTY_HINT_RANGE,"-1,2048,1" ), _SCS("set_default_margin"),_SCS("get_default_margin"), MARGIN_RIGHT );
+	ADD_PROPERTYI( PropertyInfo( Variant::REAL, "content_margin/top", PROPERTY_HINT_RANGE,"-1,2048,1" ), _SCS("set_default_margin"),_SCS("get_default_margin"), MARGIN_TOP);
+	ADD_PROPERTYI( PropertyInfo( Variant::REAL, "content_margin/bottom", PROPERTY_HINT_RANGE,"-1,2048,1" ), _SCS("set_default_margin"),_SCS("get_default_margin"), MARGIN_BOTTOM );
+
+
 }
 
 StyleBox::StyleBox() {

+ 18 - 18
scene/resources/texture.cpp

@@ -935,21 +935,21 @@ float CubeMap::get_lossy_storage_quality() const {
 
 bool CubeMap::_set(const StringName& p_name, const Variant& p_value) {
 
-	if (p_name=="side/left")
+	if (p_name=="side/left") {
 		set_side(SIDE_LEFT,p_value);
-	if (p_name=="side/right")
+	} else if (p_name=="side/right") {
 		set_side(SIDE_RIGHT,p_value);
-	if (p_name=="side/bottom")
+	} else if (p_name=="side/bottom") {
 		set_side(SIDE_BOTTOM,p_value);
-	if (p_name=="side/top")
+	} else if (p_name=="side/top") {
 		set_side(SIDE_TOP,p_value);
-	if (p_name=="side/front")
+	} else if (p_name=="side/front") {
 		set_side(SIDE_FRONT,p_value);
-	if (p_name=="side/back")
+	} else if (p_name=="side/back") {
 		set_side(SIDE_BACK,p_value);
-	else if (p_name=="flags")
+	} else if (p_name=="flags") {
 		set_flags(p_value);
-	else if (p_name=="storage") {
+	} else if (p_name=="storage") {
 		storage=Storage(p_value.operator int());
 	} else if (p_name=="lossy_quality") {
 		lossy_storage_quality=p_value;
@@ -962,25 +962,25 @@ bool CubeMap::_set(const StringName& p_name, const Variant& p_value) {
 
 bool CubeMap::_get(const StringName& p_name,Variant &r_ret) const {
 
-	if (p_name=="side/left")
+	if (p_name=="side/left") {
 		r_ret=get_side(SIDE_LEFT);
-	if (p_name=="side/right")
+	} else if (p_name=="side/right") {
 		r_ret=get_side(SIDE_RIGHT);
-	if (p_name=="side/bottom")
+	} else if (p_name=="side/bottom") {
 		r_ret=get_side(SIDE_BOTTOM);
-	if (p_name=="side/top")
+	} else if (p_name=="side/top") {
 		r_ret=get_side(SIDE_TOP);
-	if (p_name=="side/front")
+	} else if (p_name=="side/front") {
 		r_ret=get_side(SIDE_FRONT);
-	if (p_name=="side/back")
+	} else if (p_name=="side/back") {
 		r_ret=get_side(SIDE_BACK);
-	else if (p_name=="flags")
+	} else if (p_name=="flags") {
 		r_ret= flags;
-	else if (p_name=="storage")
+	} else if (p_name=="storage") {
 		r_ret= storage;
-	else if (p_name=="lossy_quality")
+	} else if (p_name=="lossy_quality") {
 		r_ret= lossy_storage_quality;
-	else
+	} else
 		return false;
 
 	return true;

+ 33 - 0
servers/visual/rasterizer.h

@@ -567,6 +567,39 @@ public:
 		CANVAS_RECT_FLIP_V=8
 	};
 
+
+	struct CanvasLight {
+
+		bool enabled;
+		bool shadow;
+		Color color;
+		Matrix32 xform;
+		float height;
+		int z_min;
+		int z_max;
+		int item_mask;
+		VS::CanvasLightBlendMode blend_mode;
+		RID texture;
+		void *texture_cache; // implementation dependent
+		Vector2 texture_offset;
+
+		CanvasLight *next_ptr;
+
+		CanvasLight() {
+			enabled=true;
+			shadow=false;
+			color=Color(1,1,1);
+			height=0;
+			z_min=-1024;
+			z_max=1024;
+			item_mask=1;
+			blend_mode=VS::CANVAS_LIGHT_BLEND_ADD;
+			texture_cache=NULL;
+			next_ptr=NULL;
+		}
+	};
+
+
 	struct CanvasItem {
 
 		struct Command {

+ 134 - 0
servers/visual/visual_server_raster.cpp

@@ -3819,6 +3819,131 @@ void VisualServerRaster::canvas_item_raise(RID p_item) {
 
 }
 
+/***** CANVAS LIGHT *******/
+
+RID VisualServerRaster::canvas_light_create() {
+
+	Rasterizer::CanvasLight *clight = memnew( Rasterizer::CanvasLight );
+	return canvas_light_owner.make_rid(clight);
+}
+
+void VisualServerRaster::canvas_light_attach_to_canvas(RID p_light,RID p_canvas){
+
+	Rasterizer::CanvasLight *clight = canvas_light_owner.get(p_light);
+	ERR_FAIL_COND(!clight);
+
+
+}
+void VisualServerRaster::canvas_light_set_enabled(RID p_light, bool p_enabled){
+
+	Rasterizer::CanvasLight *clight = canvas_light_owner.get(p_light);
+	ERR_FAIL_COND(!clight);
+	clight->enabled=p_enabled;
+
+}
+void VisualServerRaster::canvas_light_set_transform(RID p_light, const Matrix32& p_transform){
+
+	Rasterizer::CanvasLight *clight = canvas_light_owner.get(p_light);
+	ERR_FAIL_COND(!clight);
+	clight->xform=p_transform;
+
+}
+void VisualServerRaster::canvas_light_set_texture(RID p_light, RID p_texture){
+
+	Rasterizer::CanvasLight *clight = canvas_light_owner.get(p_light);
+	ERR_FAIL_COND(!clight);
+	clight->texture=p_texture;
+
+}
+void VisualServerRaster::canvas_light_set_texture_offset(RID p_light, const Vector2& p_offset){
+
+	Rasterizer::CanvasLight *clight = canvas_light_owner.get(p_light);
+	ERR_FAIL_COND(!clight);
+	clight->texture_offset=p_offset;
+
+}
+void VisualServerRaster::canvas_light_set_color(RID p_light, const Color& p_color){
+
+	Rasterizer::CanvasLight *clight = canvas_light_owner.get(p_light);
+	ERR_FAIL_COND(!clight);
+	clight->color=p_color;
+
+
+}
+void VisualServerRaster::canvas_light_set_height(RID p_light, float p_height){
+
+	Rasterizer::CanvasLight *clight = canvas_light_owner.get(p_light);
+	ERR_FAIL_COND(!clight);
+	clight->height=p_height;
+
+}
+void VisualServerRaster::canvas_light_set_z_range(RID p_light, int p_min_z,int p_max_z){
+
+	Rasterizer::CanvasLight *clight = canvas_light_owner.get(p_light);
+	ERR_FAIL_COND(!clight);
+	clight->z_min=p_min_z;
+	clight->z_max=p_max_z;
+
+}
+void VisualServerRaster::canvas_light_set_item_mask(RID p_light, int p_mask){
+
+	Rasterizer::CanvasLight *clight = canvas_light_owner.get(p_light);
+	ERR_FAIL_COND(!clight);
+	clight->item_mask=p_mask;
+
+}
+
+void VisualServerRaster::canvas_light_set_blend_mode(RID p_light, CanvasLightBlendMode p_blend_mode){
+
+	Rasterizer::CanvasLight *clight = canvas_light_owner.get(p_light);
+	ERR_FAIL_COND(!clight);
+	clight->blend_mode=p_blend_mode;
+
+}
+void VisualServerRaster::canvas_light_set_shadow_enabled(RID p_light, bool p_enabled){
+
+	Rasterizer::CanvasLight *clight = canvas_light_owner.get(p_light);
+	ERR_FAIL_COND(!clight);
+	clight->shadow=p_enabled;
+
+}
+void VisualServerRaster::canvas_light_set_shadow_buffer_size(RID p_light, int p_size){
+
+	Rasterizer::CanvasLight *clight = canvas_light_owner.get(p_light);
+	ERR_FAIL_COND(!clight);
+
+}
+void VisualServerRaster::canvas_light_set_shadow_filter(RID p_light, int p_size){
+
+	Rasterizer::CanvasLight *clight = canvas_light_owner.get(p_light);
+	ERR_FAIL_COND(!clight);
+
+}
+
+/****** CANVAS LIGHT OCCLUDER ******/
+
+RID VisualServerRaster::canvas_light_occluder_create() {
+
+	return RID();
+}
+
+void VisualServerRaster::canvas_light_occluder_attach_to_canvas(RID p_occluder,RID p_canvas) {
+
+
+}
+
+void VisualServerRaster::canvas_light_occluder_set_enabled(RID p_occluder,bool p_enabled){
+
+
+}
+
+void VisualServerRaster::canvas_light_occluder_set_shape(RID p_occluder,const DVector<Vector2>& p_shape){
+
+
+}
+
+
+
 /******** CANVAS *********/
 
 
@@ -4101,6 +4226,15 @@ void VisualServerRaster::free( RID p_rid ) {
 		canvas_item_owner.free( p_rid );
 		
 		memdelete( canvas_item );
+
+	} else if (canvas_light_owner.owns(p_rid)) {
+
+		Rasterizer::CanvasLight *canvas_light = canvas_light_owner.get(p_rid);
+		ERR_FAIL_COND(!canvas_light);
+
+		canvas_light_owner.free( p_rid );
+		memdelete( canvas_light );
+
 	} else if (scenario_owner.owns(p_rid)) {
 		
 		Scenario *scenario=scenario_owner.get(p_rid);

+ 36 - 0
servers/visual/visual_server_raster.h

@@ -440,6 +440,8 @@ class VisualServerRaster : public VisualServer {
 	};
 
 
+	RID_Owner<Rasterizer::CanvasLight> canvas_light_owner;
+
 
 	struct Viewport {
 
@@ -1122,9 +1124,43 @@ public:
 	virtual void canvas_item_set_use_parent_shader(RID p_item, bool p_enable);
 
 
+
 	virtual void canvas_item_set_shader_param(RID p_canvas_item, const StringName& p_param, const Variant& p_value);
 	virtual Variant canvas_item_get_shader_param(RID p_canvas_item, const StringName& p_param) const;
 
+	virtual RID canvas_light_create();
+	virtual void canvas_light_attach_to_canvas(RID p_light,RID p_canvas);
+	virtual void canvas_light_set_enabled(RID p_light, bool p_enabled);
+	virtual void canvas_light_set_transform(RID p_light, const Matrix32& p_transform);
+	virtual void canvas_light_set_texture(RID p_light, RID p_texture);
+	virtual void canvas_light_set_texture_offset(RID p_light, const Vector2& p_offset);
+	virtual void canvas_light_set_color(RID p_light, const Color& p_color);
+	virtual void canvas_light_set_height(RID p_light, float p_height);
+	virtual void canvas_light_set_z_range(RID p_light, int p_min_z,int p_max_z);
+	virtual void canvas_light_set_item_mask(RID p_light, int p_mask);
+
+	enum CanvasightBlendMode {
+		CANVAS_LIGHT_BLEND_ADD,
+		CANVAS_LIGHT_BLEND_SUB,
+		CANVAS_LIGHT_BLEND_MULTIPLY,
+		CANVAS_LIGHT_BLEND_DODGE,
+		CANVAS_LIGHT_BLEND_BURN,
+		CANVAS_LIGHT_BLEND_LIGHTEN,
+		CANVAS_LIGHT_BLEND_DARKEN,
+		CANVAS_LIGHT_BLEND_OVERLAY,
+		CANVAS_LIGHT_BLEND_SCREEN,
+	};
+	virtual void canvas_light_set_blend_mode(RID p_light, CanvasLightBlendMode p_blend_mode);
+	virtual void canvas_light_set_shadow_enabled(RID p_light, bool p_enabled);
+	virtual void canvas_light_set_shadow_buffer_size(RID p_light, int p_size);
+	virtual void canvas_light_set_shadow_filter(RID p_light, int p_size);
+
+
+	virtual RID canvas_light_occluder_create();
+	virtual void canvas_light_occluder_attach_to_canvas(RID p_occluder,RID p_canvas);
+	virtual void canvas_light_occluder_set_enabled(RID p_occluder,bool p_enabled);
+	virtual void canvas_light_occluder_set_shape(RID p_occluder,const DVector<Vector2>& p_shape);
+
 	virtual void canvas_item_clear(RID p_item);
 	virtual void canvas_item_raise(RID p_item);
 

+ 23 - 0
servers/visual/visual_server_wrap_mt.h

@@ -1146,6 +1146,29 @@ public:
 	FUNC1(canvas_item_clear,RID);
 	FUNC1(canvas_item_raise,RID);
 
+	/* CANVAS LIGHT */
+	FUNC0R(RID,canvas_light_create);
+	FUNC2(canvas_light_attach_to_canvas,RID,RID);
+	FUNC2(canvas_light_set_enabled,RID,bool);
+	FUNC2(canvas_light_set_transform,RID,const Matrix32&);
+	FUNC2(canvas_light_set_texture,RID,RID);
+	FUNC2(canvas_light_set_texture_offset,RID,const Vector2&);
+	FUNC2(canvas_light_set_color,RID,const Color&);
+	FUNC2(canvas_light_set_height,RID,float);
+	FUNC3(canvas_light_set_z_range,RID,int,int);
+	FUNC2(canvas_light_set_item_mask,RID,int);
+
+	FUNC2(canvas_light_set_blend_mode,RID,CanvasLightBlendMode);
+	FUNC2(canvas_light_set_shadow_enabled,RID,bool);
+	FUNC2(canvas_light_set_shadow_buffer_size,RID,int);
+	FUNC2(canvas_light_set_shadow_filter,RID,int);
+
+	/* CANVAS OCCLUDER */
+
+	FUNC0R(RID,canvas_light_occluder_create);
+	FUNC2(canvas_light_occluder_attach_to_canvas,RID,RID);
+	FUNC2(canvas_light_occluder_set_enabled,RID,bool);
+	FUNC2(canvas_light_occluder_set_shape,RID,const DVector<Vector2>&);
 
 	/* CURSOR */
 	FUNC2(cursor_set_rotation,float , int ); // radians

+ 33 - 0
servers/visual_server.h

@@ -999,6 +999,39 @@ public:
 	virtual void canvas_item_set_shader_param(RID p_canvas_item, const StringName& p_param, const Variant& p_value)=0;
 	virtual Variant canvas_item_get_shader_param(RID p_canvas_item, const StringName& p_param) const=0;
 
+	virtual RID canvas_light_create()=0;
+	virtual void canvas_light_attach_to_canvas(RID p_light,RID p_canvas)=0;
+	virtual void canvas_light_set_enabled(RID p_light, bool p_enabled)=0;
+	virtual void canvas_light_set_transform(RID p_light, const Matrix32& p_transform)=0;
+	virtual void canvas_light_set_texture(RID p_light, RID p_texture)=0;
+	virtual void canvas_light_set_texture_offset(RID p_light, const Vector2& p_offset)=0;
+	virtual void canvas_light_set_color(RID p_light, const Color& p_color)=0;
+	virtual void canvas_light_set_height(RID p_light, float p_height)=0;
+	virtual void canvas_light_set_z_range(RID p_light, int p_min_z,int p_max_z)=0;
+	virtual void canvas_light_set_item_mask(RID p_light, int p_mask)=0;
+
+	enum CanvasLightBlendMode {
+		CANVAS_LIGHT_BLEND_ADD,
+		CANVAS_LIGHT_BLEND_SUB,
+		CANVAS_LIGHT_BLEND_MULTIPLY,
+		CANVAS_LIGHT_BLEND_DODGE,
+		CANVAS_LIGHT_BLEND_BURN,
+		CANVAS_LIGHT_BLEND_LIGHTEN,
+		CANVAS_LIGHT_BLEND_DARKEN,
+		CANVAS_LIGHT_BLEND_OVERLAY,
+		CANVAS_LIGHT_BLEND_SCREEN,
+	};
+	virtual void canvas_light_set_blend_mode(RID p_light, CanvasLightBlendMode p_blend_mode)=0;
+	virtual void canvas_light_set_shadow_enabled(RID p_light, bool p_enabled)=0;
+	virtual void canvas_light_set_shadow_buffer_size(RID p_light, int p_size)=0;
+	virtual void canvas_light_set_shadow_filter(RID p_light, int p_size)=0;
+
+
+	virtual RID canvas_light_occluder_create()=0;
+	virtual void canvas_light_occluder_attach_to_canvas(RID p_occluder,RID p_canvas)=0;
+	virtual void canvas_light_occluder_set_enabled(RID p_occluder,bool p_enabled)=0;
+	virtual void canvas_light_occluder_set_shape(RID p_occluder,const DVector<Vector2>& p_shape)=0;
+
 	/* CURSOR */
 	virtual void cursor_set_rotation(float p_rotation, int p_cursor = 0)=0; // radians
 	virtual void cursor_set_texture(RID p_texture, const Point2 &p_center_offset = Point2(0, 0), int p_cursor=0)=0;

+ 26 - 0
tools/editor/editor_import_export.cpp

@@ -1141,10 +1141,36 @@ EditorImportExport* EditorImportExport::singleton=NULL;
 
 void EditorImportExport::add_import_plugin(const Ref<EditorImportPlugin>& p_plugin) {
 
+	// Need to make sure the name is unique if we are going to lookup by it
+	ERR_FAIL_COND(by_idx.has(p_plugin->get_name()));
+
 	by_idx[ p_plugin->get_name() ]=plugins.size();
 	plugins.push_back(p_plugin);
 }
 
+void EditorImportExport::remove_import_plugin(const Ref<EditorImportPlugin>& p_plugin) {
+
+	String plugin_name = p_plugin->get_name();
+
+	// Keep the indices the same
+	// Find the index of the target plugin
+	ERR_FAIL_COND(!by_idx.has(plugin_name));
+	int idx = by_idx[plugin_name];
+	int last_idx = plugins.size() - 1;
+
+	// Swap the last plugin and the target one
+	SWAP(plugins[idx], plugins[last_idx]);
+
+	// Update the index of the old last one
+	by_idx[plugins[idx]->get_name()] = idx;
+
+	// Remove the target plugin's by_idx entry
+	by_idx.erase(plugin_name);
+
+	// Erase the plugin
+	plugins.remove(last_idx);
+}
+
 int EditorImportExport::get_import_plugin_count() const{
 
 	return plugins.size();

+ 1 - 0
tools/editor/editor_import_export.h

@@ -270,6 +270,7 @@ public:
 	static EditorImportExport* get_singleton() { return singleton; }
 
 	void add_import_plugin(const Ref<EditorImportPlugin>& p_plugin);
+	void remove_import_plugin(const Ref<EditorImportPlugin>& p_plugin);
 	int get_import_plugin_count() const;
 	Ref<EditorImportPlugin> get_import_plugin(int p_idx) const;
 	Ref<EditorImportPlugin> get_import_plugin_by_name(const String& p_string) const;

+ 61 - 11
tools/editor/editor_node.cpp

@@ -89,6 +89,7 @@
 #include "plugins/animation_player_editor_plugin.h"
 #include "plugins/baked_light_editor_plugin.h"
 #include "plugins/polygon_2d_editor_plugin.h"
+#include "plugins/navigation_polygon_editor_plugin.h"
 // end
 #include "tools/editor/io_plugins/editor_texture_import_plugin.h"
 #include "tools/editor/io_plugins/editor_scene_import_plugin.h"
@@ -336,6 +337,19 @@ void EditorNode::_vp_resized() {
 
 }
 
+void EditorNode::_rebuild_import_menu()
+{
+	PopupMenu* p = import_menu->get_popup();
+	p->clear();
+	p->add_item("Sub-Scene", FILE_IMPORT_SUBSCENE);
+	p->add_separator();
+	for (int i = 0; i < editor_import_export->get_import_plugin_count(); i++) {
+		p->add_item(editor_import_export->get_import_plugin(i)->get_visible_name(), IMPORT_PLUGIN_BASE + i);
+	}
+	p->add_separator();
+	p->add_item("Re-Import..", SETTINGS_IMPORT);
+}
+
 void EditorNode::_node_renamed() {
 
 	if (property_editor)
@@ -1967,6 +1981,25 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) {
 				log->add_message("REDO: "+action);
 
 		} break;
+
+		case EDIT_REVERT: {
+
+			Node *scene = get_edited_scene();
+
+			if (!scene)
+				break;
+			
+			if (unsaved_cache && !p_confirmed) {
+				confirmation->get_ok()->set_text("Revert");
+				confirmation->set_text("This action cannot be undone. Revert anyway?");
+				confirmation->popup_centered(Size2(300,70));
+				break;
+			}
+
+			Error err = load_scene(scene->get_filename());
+
+		} break;
+
 #if 0
 		case NODE_EXTERNAL_INSTANCE: {
 
@@ -2388,6 +2421,19 @@ void EditorNode::remove_editor_plugin(EditorPlugin *p_editor) {
 }
 
 
+void EditorNode::add_editor_import_plugin(const Ref<EditorImportPlugin>& p_editor_import) {
+
+	editor_import_export->add_import_plugin(p_editor_import);
+	_rebuild_import_menu();
+}
+
+void EditorNode::remove_editor_import_plugin(const Ref<EditorImportPlugin>& p_editor_import) {
+
+	editor_import_export->remove_import_plugin(p_editor_import);
+	_rebuild_import_menu();
+}
+
+
 void EditorNode::set_edited_scene(Node *p_scene) {
 
 	if (edited_scene) {
@@ -3152,6 +3198,9 @@ void EditorNode::_bind_methods() {
 	ObjectTypeDB::bind_method("_sources_changed",&EditorNode::_sources_changed);
 	ObjectTypeDB::bind_method("_fs_changed",&EditorNode::_fs_changed);
 
+	ObjectTypeDB::bind_method(_MD("add_editor_import_plugin", "plugin"), &EditorNode::add_editor_import_plugin);
+	ObjectTypeDB::bind_method(_MD("remove_editor_import_plugin", "plugin"), &EditorNode::remove_editor_import_plugin);
+	ObjectTypeDB::bind_method(_MD("get_gui_base"), &EditorNode::get_gui_base);
 
 	ADD_SIGNAL( MethodInfo("play_pressed") );
 	ADD_SIGNAL( MethodInfo("pause_pressed") );
@@ -3212,6 +3261,11 @@ Error EditorNode::export_platform(const String& p_platform, const String& p_path
 	return OK;
 }
 
+void EditorNode::show_warning(const String& p_text) {
+
+	warning->set_text(p_text);
+	warning->popup_centered_minsize();
+}
 
 
 EditorNode::EditorNode() {
@@ -3469,6 +3523,8 @@ EditorNode::EditorNode() {
 	p->add_separator();
 	p->add_item("Project Settings",RUN_SETTINGS);
 	p->add_separator();
+	p->add_item("Revert Scene",EDIT_REVERT);
+	p->add_separator();
 	p->add_item("Quit to Project List",RUN_PROJECT_MANAGER,KEY_MASK_SHIFT+KEY_MASK_CMD+KEY_Q);
 	p->add_item("Quit",FILE_QUIT,KEY_MASK_CMD+KEY_Q);
 
@@ -3513,8 +3569,6 @@ EditorNode::EditorNode() {
 	left_menu_hb->add_child( import_menu );
 
 	p=import_menu->get_popup();
-	p->add_item("Sub-Scene",FILE_IMPORT_SUBSCENE);
-	p->add_separator();
 	p->connect("item_pressed",this,"_menu_option");
 
 	export_button = memnew( ToolButton );
@@ -3552,7 +3606,7 @@ EditorNode::EditorNode() {
 	play_button->set_icon(gui_base->get_icon("MainPlay","EditorIcons"));
 	play_button->set_focus_mode(Control::FOCUS_NONE);
 	play_button->connect("pressed", this,"_menu_option",make_binds(RUN_PLAY));
-	play_button->set_tooltip("Start the scene (F5).");
+	play_button->set_tooltip("Play the project (F5).");
 
 
 
@@ -3922,6 +3976,8 @@ EditorNode::EditorNode() {
 	logo->set_pos(Point2(20,20));
 	logo->set_texture(gui_base->get_icon("Logo","EditorIcons") );
 
+	warning = memnew( AcceptDialog );
+	add_child(warning);
 
 
 
@@ -4023,11 +4079,6 @@ EditorNode::EditorNode() {
 	editor_import_export->add_import_plugin( Ref<EditorSampleImportPlugin>( memnew(EditorSampleImportPlugin(this))));
 	editor_import_export->add_import_plugin( Ref<EditorTranslationImportPlugin>( memnew(EditorTranslationImportPlugin(this))));
 
-
-	for(int i=0;i<editor_import_export->get_import_plugin_count();i++) {
-		    import_menu->get_popup()->add_item(editor_import_export->get_import_plugin(i)->get_visible_name(),IMPORT_PLUGIN_BASE+i);
-	}
-
 	editor_import_export->add_export_plugin( Ref<EditorTextureExportPlugin>( memnew(EditorTextureExportPlugin)));
 
 	add_editor_plugin( memnew( CanvasItemEditorPlugin(this) ) );
@@ -4064,6 +4115,7 @@ EditorNode::EditorNode() {
 	add_editor_plugin( memnew( PathEditorPlugin(this) ) );
 	add_editor_plugin( memnew( BakedLightEditorPlugin(this) ) );
 	add_editor_plugin( memnew( Polygon2DEditorPlugin(this) ) );
+	add_editor_plugin( memnew( NavigationPolygonEditorPlugin(this) ) );
 
 	for(int i=0;i<EditorPlugins::get_plugin_count();i++)
 		add_editor_plugin( EditorPlugins::create(i,this) );
@@ -4072,9 +4124,7 @@ EditorNode::EditorNode() {
 	circle_step_frame=OS::get_singleton()->get_frames_drawn();;
 	circle_step=0;
 
-
-	import_menu->get_popup()->add_separator();
-	import_menu->get_popup()->add_item("Re-Import..",SETTINGS_IMPORT);
+	_rebuild_import_menu();
 
 	editor_plugin_screen=NULL;
 	editor_plugin_over=NULL;

+ 10 - 0
tools/editor/editor_node.h

@@ -127,6 +127,7 @@ class EditorNode : public Node {
 		FILE_EXTERNAL_OPEN_SCENE,
 		EDIT_UNDO,
 		EDIT_REDO,
+		EDIT_REVERT,
 		RESOURCE_NEW,
 		RESOURCE_LOAD,
 		RESOURCE_SAVE,
@@ -231,6 +232,7 @@ class EditorNode : public Node {
 	ConfirmationDialog *open_recent_confirmation;
 	AcceptDialog *accept;
 	AcceptDialog *about;
+	AcceptDialog *warning;
 
 	//OptimizedPresetsDialog *optimized_presets;
 	EditorSettingsDialog *settings_config_dialog;
@@ -339,6 +341,8 @@ class EditorNode : public Node {
 	void _show_messages();
 	void _vp_resized();
 
+	void _rebuild_import_menu();
+
 	void _save_scene(String p_file);
 
 
@@ -420,6 +424,9 @@ public:
 	static void add_editor_plugin(EditorPlugin *p_editor);
 	static void remove_editor_plugin(EditorPlugin *p_editor);
 
+	void add_editor_import_plugin(const Ref<EditorImportPlugin>& p_editor_import);
+	void remove_editor_import_plugin(const Ref<EditorImportPlugin>& p_editor_import);
+
 
 	void edit_node(Node *p_node);
 	void edit_resource(const Ref<Resource>& p_resource);
@@ -478,6 +485,9 @@ public:
 	Ref<Theme> get_editor_theme() const { return theme; }
 
 
+	void show_warning(const String& p_text);
+
+
 	Error export_platform(const String& p_platform, const String& p_path, bool p_debug,const String& p_password,bool p_quit_after=false);
 
 	static void register_editor_types();

二进制
tools/editor/icons/icon_light_2d.png


二进制
tools/editor/icons/icon_navigation_2d.png


二进制
tools/editor/icons/icon_navigation_polygon_instance.png


+ 8 - 6
tools/editor/plugins/collision_polygon_editor_plugin.cpp

@@ -31,6 +31,8 @@
 #include "os/file_access.h"
 #include "tools/editor/editor_settings.h"
 #include "scene/3d/camera.h"
+#include "canvas_item_editor_plugin.h"
+
 void CollisionPolygonEditor::_notification(int p_what) {
 
 	switch(p_what) {
@@ -71,14 +73,14 @@ void CollisionPolygonEditor::_node_removed(Node *p_node) {
 Vector2 CollisionPolygonEditor::snap_point(const Vector2& p_point) const {
 
 	return p_point;
-	/*
-	if (canvas_item_editor->is_snap_active()) {
+	
+	if (CanvasItemEditor::get_singleton()->is_snap_active()) {
 
-		return p_point.snapped(Vector2(1,1)*canvas_item_editor->get_snap());
+		return p_point.snapped(Vector2(1,1)*CanvasItemEditor::get_singleton()->get_snap());
 
 	} else {
 		return p_point;
-	} ??? */
+	}
 }
 
 void CollisionPolygonEditor::_menu_option(int p_option) {
@@ -148,7 +150,7 @@ bool CollisionPolygonEditor::forward_spatial_input_event(Camera* p_camera,const
 
 			Vector2 cpoint(spoint.x,spoint.y);
 
-			//cpoint=snap_point(cpoint); snap?
+			cpoint=snap_point(cpoint);
 
 			Vector<Vector2> poly = node->get_polygon();
 
@@ -362,7 +364,7 @@ bool CollisionPolygonEditor::forward_spatial_input_event(Camera* p_camera,const
 
 				Vector2 cpoint(spoint.x,spoint.y);
 
-				//cpoint=snap_point(cpoint);
+				cpoint=snap_point(cpoint);
 				edited_point_pos = cpoint;
 
 				_polygon_draw();

+ 547 - 0
tools/editor/plugins/navigation_polygon_editor_plugin.cpp

@@ -0,0 +1,547 @@
+#include "navigation_polygon_editor_plugin.h"
+
+#include "canvas_item_editor_plugin.h"
+#include "os/file_access.h"
+#include "tools/editor/editor_settings.h"
+
+void NavigationPolygonEditor::_notification(int p_what) {
+
+	switch(p_what) {
+
+		case NOTIFICATION_READY: {
+
+			button_create->set_icon( get_icon("Edit","EditorIcons"));
+			button_edit->set_icon( get_icon("MovePoint","EditorIcons"));
+			button_edit->set_pressed(true);
+			get_tree()->connect("node_removed",this,"_node_removed");
+			create_nav->connect("confirmed",this,"_create_nav");
+
+		} break;
+		case NOTIFICATION_FIXED_PROCESS: {
+
+
+		} break;
+	}
+
+}
+void NavigationPolygonEditor::_node_removed(Node *p_node) {
+
+	if(p_node==node) {
+		node=NULL;
+		hide();
+		canvas_item_editor->get_viewport_control()->update();
+	}
+
+}
+
+void NavigationPolygonEditor::_create_nav()  {
+
+	undo_redo->create_action("Create Navigation Polygon");
+	undo_redo->add_do_method(node,"set_navigation_polygon",Ref<NavigationPolygon>(memnew( NavigationPolygon)));
+	undo_redo->add_undo_method(node,"set_navigation_polygon",Variant(REF()));
+	undo_redo->commit_action();
+}
+
+Vector2 NavigationPolygonEditor::snap_point(const Vector2& p_point) const {
+
+	if (canvas_item_editor->is_snap_active()) {
+
+		return p_point.snapped(Vector2(1,1)*canvas_item_editor->get_snap());
+
+	} else {
+		return p_point;
+	}
+}
+
+void NavigationPolygonEditor::_menu_option(int p_option) {
+
+	switch(p_option) {
+
+		case MODE_CREATE: {
+
+			mode=MODE_CREATE;
+			button_create->set_pressed(true);
+			button_edit->set_pressed(false);
+		} break;
+		case MODE_EDIT: {
+
+			mode=MODE_EDIT;
+			button_create->set_pressed(false);
+			button_edit->set_pressed(true);
+		} break;
+
+	}
+}
+
+void NavigationPolygonEditor::_wip_close() {
+
+
+	if (wip.size()>=3) {
+
+		undo_redo->create_action("Create Poly");
+		undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"remove_outline",node->get_navigation_polygon()->get_outline_count());
+		undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"add_outline",wip);
+		undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines");
+		undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines");
+		undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update");
+		undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+		undo_redo->commit_action();
+		mode=MODE_EDIT;
+		button_edit->set_pressed(true);
+		button_create->set_pressed(false);
+	}
+
+	wip.clear();
+	wip_active=false;
+	edited_point=-1;
+}
+
+bool NavigationPolygonEditor::forward_input_event(const InputEvent& p_event) {
+
+
+	if (!node)
+		return false;
+
+	if (node->get_navigation_polygon().is_null()) {
+		if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1 && p_event.mouse_button.pressed) {
+			create_nav->set_text("No NavigationPolygon resource on this node.\nCreate and assign one?");
+			create_nav->popup_centered_minsize();
+		}
+		return false;
+	}
+
+
+	switch(p_event.type) {
+
+		case InputEvent::MOUSE_BUTTON: {
+
+			const InputEventMouseButton &mb=p_event.mouse_button;
+
+			Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
+
+
+			Vector2 gpoint = Point2(mb.x,mb.y);
+			Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint);
+			cpoint=snap_point(cpoint);
+			cpoint = node->get_global_transform().affine_inverse().xform(cpoint);
+
+
+
+			//first check if a point is to be added (segment split)
+			real_t grab_treshold=EDITOR_DEF("poly_editor/point_grab_radius",8);
+
+			switch(mode) {
+
+
+				case MODE_CREATE: {
+
+					if (mb.button_index==BUTTON_LEFT && mb.pressed) {
+
+
+						if (!wip_active) {
+
+							wip.clear();
+							wip.push_back( cpoint );
+							wip_active=true;
+							edited_point_pos=cpoint;
+							edited_outline=-1;
+							canvas_item_editor->get_viewport_control()->update();
+							edited_point=1;
+							return true;
+						} else {
+
+
+							if (wip.size()>1 && xform.xform(wip[0]).distance_to(gpoint)<grab_treshold) {
+								//wip closed
+								_wip_close();
+
+								return true;
+							} else {
+
+								wip.push_back( cpoint );
+								edited_point=wip.size();
+								canvas_item_editor->get_viewport_control()->update();
+								return true;
+
+								//add wip point
+							}
+						}
+					} else if (mb.button_index==BUTTON_RIGHT && mb.pressed && wip_active) {
+						_wip_close();
+					}
+
+
+
+				} break;
+
+				case MODE_EDIT: {
+
+					if (mb.button_index==BUTTON_LEFT) {
+						if (mb.pressed) {
+
+							if (mb.mod.control) {
+
+
+								//search edges
+								int closest_outline=-1;
+								int closest_idx=-1;
+								Vector2 closest_pos;
+								real_t closest_dist=1e10;
+
+								for(int j=0;j<node->get_navigation_polygon()->get_outline_count();j++) {
+
+
+									DVector<Vector2> points=node->get_navigation_polygon()->get_outline(j);
+
+									int pc=points.size();
+									DVector<Vector2>::Read poly=points.read();
+
+									for(int i=0;i<pc;i++) {
+
+										Vector2 points[2] ={ xform.xform(poly[i]),
+											xform.xform(poly[(i+1)%pc]) };
+
+										Vector2 cp = Geometry::get_closest_point_to_segment_2d(gpoint,points);
+										if (cp.distance_squared_to(points[0])<CMP_EPSILON2 || cp.distance_squared_to(points[1])<CMP_EPSILON2)
+											continue; //not valid to reuse point
+
+										real_t d = cp.distance_to(gpoint);
+										if (d<closest_dist && d<grab_treshold) {
+											closest_dist=d;
+											closest_outline=j;
+											closest_pos=cp;
+											closest_idx=i;
+										}
+
+
+									}
+								}
+
+								if (closest_idx>=0) {
+
+									pre_move_edit=node->get_navigation_polygon()->get_outline(closest_outline);
+									DVector<Point2> poly = pre_move_edit;
+									poly.insert(closest_idx+1,xform.affine_inverse().xform(closest_pos));
+									edited_point=closest_idx+1;
+									edited_outline=closest_outline;
+									edited_point_pos=xform.affine_inverse().xform(closest_pos);
+									node->get_navigation_polygon()->set_outline(closest_outline,poly);
+									canvas_item_editor->get_viewport_control()->update();
+									return true;
+								}
+							} else {
+
+								//look for points to move
+								int closest_outline=-1;
+								int closest_idx=-1;
+								Vector2 closest_pos;
+								real_t closest_dist=1e10;
+
+								for(int j=0;j<node->get_navigation_polygon()->get_outline_count();j++) {
+
+
+									DVector<Vector2> points=node->get_navigation_polygon()->get_outline(j);
+
+									int pc=points.size();
+									DVector<Vector2>::Read poly=points.read();
+
+									for(int i=0;i<pc;i++) {
+
+
+										Vector2 cp =xform.xform(poly[i]);
+
+										real_t d = cp.distance_to(gpoint);
+										if (d<closest_dist && d<grab_treshold) {
+											closest_dist=d;
+											closest_pos=cp;
+											closest_outline=j;
+											closest_idx=i;
+										}
+									}
+								}
+
+								if (closest_idx>=0) {
+
+									pre_move_edit=node->get_navigation_polygon()->get_outline(closest_outline);
+									edited_point=closest_idx;
+									edited_outline=closest_outline;
+									edited_point_pos=xform.affine_inverse().xform(closest_pos);
+									canvas_item_editor->get_viewport_control()->update();
+									return true;
+								}
+							}
+						} else {
+
+							if (edited_point!=-1) {
+
+								//apply
+
+								DVector<Vector2> poly = node->get_navigation_polygon()->get_outline(edited_outline);
+								ERR_FAIL_INDEX_V(edited_point,poly.size(),false);
+								poly.set(edited_point,edited_point_pos);
+								undo_redo->create_action("Edit Poly");
+								undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"set_outline",edited_outline,poly);
+								undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"set_outline",edited_outline,pre_move_edit);
+								undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines");
+								undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines");
+								undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update");
+								undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+								undo_redo->commit_action();
+
+								edited_point=-1;
+								return true;
+							}
+						}
+					} if (mb.button_index==BUTTON_RIGHT && mb.pressed && edited_point==-1) {
+
+						int closest_outline=-1;
+						int closest_idx=-1;
+						Vector2 closest_pos;
+						real_t closest_dist=1e10;
+
+						for(int j=0;j<node->get_navigation_polygon()->get_outline_count();j++) {
+
+
+							DVector<Vector2> points=node->get_navigation_polygon()->get_outline(j);
+
+							int pc=points.size();
+							DVector<Vector2>::Read poly=points.read();
+
+							for(int i=0;i<pc;i++) {
+
+
+								Vector2 cp =xform.xform(poly[i]);
+
+								real_t d = cp.distance_to(gpoint);
+								if (d<closest_dist && d<grab_treshold) {
+									closest_dist=d;
+									closest_pos=cp;
+									closest_outline=j;
+									closest_idx=i;
+								}
+							}
+						}
+
+						if (closest_idx>=0) {
+
+
+							DVector<Vector2> poly = node->get_navigation_polygon()->get_outline(closest_outline);
+
+							if (poly.size()>3) {
+								undo_redo->create_action("Edit Poly (Remove Point)");
+								undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"set_outline",closest_outline,poly);
+								poly.remove(closest_idx);
+								undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"set_outline",closest_outline,poly);
+								undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines");
+								undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines");
+								undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update");
+								undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+								undo_redo->commit_action();
+							} else {
+
+								undo_redo->create_action("Remove Poly And Point");
+								undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"add_outline_at_index",poly,closest_outline);
+								poly.remove(closest_idx);
+								undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"remove_outline",closest_outline);
+								undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines");
+								undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines");
+								undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update");
+								undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+								undo_redo->commit_action();
+
+							}
+							return true;
+						}
+					}
+
+
+
+				} break;
+			}
+
+
+
+		} break;
+		case InputEvent::MOUSE_MOTION: {
+
+			const InputEventMouseMotion &mm=p_event.mouse_motion;
+
+			if (edited_point!=-1 && (wip_active || mm.button_mask&BUTTON_MASK_LEFT)) {
+
+				Vector2 gpoint = Point2(mm.x,mm.y);
+				Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint);
+				cpoint=snap_point(cpoint);
+				edited_point_pos = node->get_global_transform().affine_inverse().xform(cpoint);
+
+				canvas_item_editor->get_viewport_control()->update();
+
+			}
+
+		} break;
+	}
+
+	return false;
+}
+void NavigationPolygonEditor::_canvas_draw() {
+
+	if (!node)
+		return;
+
+	Control *vpc = canvas_item_editor->get_viewport_control();
+	if (node->get_navigation_polygon().is_null())
+			return;
+
+	Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
+	Ref<Texture> handle= get_icon("EditorHandle","EditorIcons");
+
+
+
+	for(int j=-1;j<node->get_navigation_polygon()->get_outline_count();j++)	{
+		Vector<Vector2> poly;
+
+		if (wip_active && j==edited_outline) {
+			poly=wip;
+		} else {
+			if (j==-1)
+				continue;
+			poly = Variant(node->get_navigation_polygon()->get_outline(j));
+		}
+
+		int len = poly.size();
+
+		for(int i=0;i<poly.size();i++) {
+
+
+			Vector2 p,p2;
+			p = (j==edited_outline && i==edited_point) ? edited_point_pos : poly[i];
+			if (j==edited_outline && ((wip_active && i==poly.size()-1) || (((i+1)%poly.size())==edited_point)))
+				p2=edited_point_pos;
+			else
+				p2 = poly[(i+1)%poly.size()];
+
+			Vector2 point = xform.xform(p);
+			Vector2 next_point = xform.xform(p2);
+
+			Color col=Color(1,0.3,0.1,0.8);
+			vpc->draw_line(point,next_point,col,2);
+			vpc->draw_texture(handle,point-handle->get_size()*0.5);
+		}
+	}
+}
+
+
+
+void NavigationPolygonEditor::edit(Node *p_collision_polygon) {
+
+	if (!canvas_item_editor) {
+		canvas_item_editor=CanvasItemEditor::get_singleton();
+	}
+
+	if (p_collision_polygon) {
+
+		node=p_collision_polygon->cast_to<NavigationPolygonInstance>();
+		if (!canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw"))
+			canvas_item_editor->get_viewport_control()->connect("draw",this,"_canvas_draw");
+		wip.clear();
+		wip_active=false;
+		edited_point=-1;
+
+	} else {
+		node=NULL;
+
+		if (canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw"))
+			canvas_item_editor->get_viewport_control()->disconnect("draw",this,"_canvas_draw");
+
+	}
+
+}
+
+void NavigationPolygonEditor::_bind_methods() {
+
+	ObjectTypeDB::bind_method(_MD("_menu_option"),&NavigationPolygonEditor::_menu_option);
+	ObjectTypeDB::bind_method(_MD("_canvas_draw"),&NavigationPolygonEditor::_canvas_draw);
+	ObjectTypeDB::bind_method(_MD("_node_removed"),&NavigationPolygonEditor::_node_removed);
+	ObjectTypeDB::bind_method(_MD("_create_nav"),&NavigationPolygonEditor::_create_nav);
+
+}
+
+NavigationPolygonEditor::NavigationPolygonEditor(EditorNode *p_editor) {
+
+	canvas_item_editor=NULL;
+	editor=p_editor;
+	undo_redo = editor->get_undo_redo();
+
+	add_child( memnew( VSeparator ));
+	button_create = memnew( ToolButton );
+	add_child(button_create);
+	button_create->connect("pressed",this,"_menu_option",varray(MODE_CREATE));
+	button_create->set_toggle_mode(true);
+	button_create->set_tooltip("Create a new polygon from scratch");
+
+	button_edit = memnew( ToolButton );
+	add_child(button_edit);
+	button_edit->connect("pressed",this,"_menu_option",varray(MODE_EDIT));
+	button_edit->set_toggle_mode(true);
+	button_edit->set_tooltip("Edit existing polygon:\nLMB: Move Point.\nCtrl+LMB: Split Segment.\nRMB: Erase Point.");
+	create_nav = memnew( ConfirmationDialog );
+	add_child(create_nav);
+	create_nav->get_ok()->set_text("Create");
+
+
+	//add_constant_override("separation",0);
+
+#if 0
+	options = memnew( MenuButton );
+	add_child(options);
+	options->set_area_as_parent_rect();
+	options->set_text("Polygon");
+	//options->get_popup()->add_item("Parse BBCODE",PARSE_BBCODE);
+	options->get_popup()->connect("item_pressed", this,"_menu_option");
+#endif
+
+	mode = MODE_EDIT;
+	wip_active=false;
+	edited_outline=-1;
+
+}
+
+
+void NavigationPolygonEditorPlugin::edit(Object *p_object) {
+
+	collision_polygon_editor->edit(p_object->cast_to<Node>());
+}
+
+bool NavigationPolygonEditorPlugin::handles(Object *p_object) const {
+
+	return p_object->is_type("NavigationPolygonInstance");
+}
+
+void NavigationPolygonEditorPlugin::make_visible(bool p_visible) {
+
+	if (p_visible) {
+		collision_polygon_editor->show();
+	} else {
+
+		collision_polygon_editor->hide();
+		collision_polygon_editor->edit(NULL);
+	}
+
+}
+
+NavigationPolygonEditorPlugin::NavigationPolygonEditorPlugin(EditorNode *p_node) {
+
+	editor=p_node;
+	collision_polygon_editor = memnew( NavigationPolygonEditor(p_node) );
+	CanvasItemEditor::get_singleton()->add_control_to_menu_panel(collision_polygon_editor);
+
+	collision_polygon_editor->hide();
+
+
+
+}
+
+
+NavigationPolygonEditorPlugin::~NavigationPolygonEditorPlugin()
+{
+}
+

+ 91 - 0
tools/editor/plugins/navigation_polygon_editor_plugin.h

@@ -0,0 +1,91 @@
+#ifndef NAVIGATIONPOLYGONEDITORPLUGIN_H
+#define NAVIGATIONPOLYGONEDITORPLUGIN_H
+
+
+
+#include "tools/editor/editor_plugin.h"
+#include "tools/editor/editor_node.h"
+#include "scene/2d/navigation_polygon.h"
+#include "scene/gui/tool_button.h"
+#include "scene/gui/button_group.h"
+
+/**
+	@author Juan Linietsky <[email protected]>
+*/
+class CanvasItemEditor;
+
+class NavigationPolygonEditor : public HBoxContainer {
+
+	OBJ_TYPE(NavigationPolygonEditor, HBoxContainer );
+
+	UndoRedo *undo_redo;
+	enum Mode {
+
+		MODE_CREATE,
+		MODE_EDIT,
+
+	};
+
+	Mode mode;
+
+	ToolButton *button_create;
+	ToolButton *button_edit;
+
+	ConfirmationDialog *create_nav;
+
+	CanvasItemEditor *canvas_item_editor;
+	EditorNode *editor;
+	Panel *panel;
+	NavigationPolygonInstance *node;
+	MenuButton *options;
+
+	int edited_outline;
+	int edited_point;
+	Vector2 edited_point_pos;
+	DVector<Vector2> pre_move_edit;
+	Vector<Vector2> wip;
+	bool wip_active;
+
+
+	void _wip_close();
+	void _canvas_draw();
+	void _create_nav();
+
+	void _menu_option(int p_option);
+
+protected:
+	void _notification(int p_what);
+	void _node_removed(Node *p_node);
+	static void _bind_methods();
+public:
+
+	Vector2 snap_point(const Vector2& p_point) const;
+	bool forward_input_event(const InputEvent& p_event);
+	void edit(Node *p_collision_polygon);
+	NavigationPolygonEditor(EditorNode *p_editor);
+};
+
+class NavigationPolygonEditorPlugin : public EditorPlugin {
+
+	OBJ_TYPE( NavigationPolygonEditorPlugin, EditorPlugin );
+
+	NavigationPolygonEditor *collision_polygon_editor;
+	EditorNode *editor;
+
+public:
+
+	virtual bool forward_input_event(const InputEvent& p_event) { return collision_polygon_editor->forward_input_event(p_event); }
+
+	virtual String get_name() const { return "NavigationPolygonInstance"; }
+	bool has_main_screen() const { return false; }
+	virtual void edit(Object *p_node);
+	virtual bool handles(Object *p_node) const;
+	virtual void make_visible(bool p_visible);
+
+	NavigationPolygonEditorPlugin(EditorNode *p_node);
+	~NavigationPolygonEditorPlugin();
+
+};
+
+
+#endif // NAVIGATIONPOLYGONEDITORPLUGIN_H

+ 5 - 1
tools/editor/plugins/script_editor_plugin.cpp

@@ -631,7 +631,10 @@ bool ScriptEditor::_test_script_times_on_disk() {
 
 
 	if (!all_ok)
-		disk_changed->call_deferred("popup_centered_ratio",0.5);
+		if (bool(EDITOR_DEF("text_editor/auto_reload_changed_scripts",false)))
+			script_editor->_reload_scripts();
+		else
+			disk_changed->call_deferred("popup_centered_ratio",0.5);
 
 	return all_ok;
 }
@@ -1806,6 +1809,7 @@ ScriptEditorPlugin::ScriptEditorPlugin(EditorNode *p_node) {
 
 	script_editor->hide();
 
+	EDITOR_DEF("text_editor/auto_reload_changed_scripts",false);
 	EDITOR_DEF("external_editor/use_external_editor",false);
 	EDITOR_DEF("external_editor/exec_path","");
 	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING,"external_editor/exec_path",PROPERTY_HINT_GLOBAL_FILE));

+ 32 - 1
tools/editor/scene_tree_dock.cpp

@@ -79,10 +79,22 @@ Node* SceneTreeDock::instance(const String& p_file) {
 		//accept->get_cancel()->hide();
 		accept->get_ok()->set_text("Ugh");
 		accept->set_text(String("Error loading scene from ")+p_file);
-		accept->popup_centered(Size2(300,70));;
+		accept->popup_centered(Size2(300,70));
 		return NULL;
 	}
 
+	// If the scene hasn't been saved yet a cyclical dependency cannot exist.
+	if (edited_scene->get_filename()!="") {
+
+		if (_cyclical_dependency_exists(edited_scene->get_filename(), instanced_scene)) {
+
+			accept->get_ok()->set_text("Ok");
+			accept->set_text(String("Cannot instance the scene '")+p_file+String("' because the current scene exists within one of its' nodes."));
+			accept->popup_centered(Size2(300,90));
+			return NULL;
+		}
+	}
+
 	instanced_scene->generate_instance_state();
 	instanced_scene->set_filename( Globals::get_singleton()->localize_path(p_file) );
 
@@ -100,6 +112,25 @@ Node* SceneTreeDock::instance(const String& p_file) {
 
 }
 
+bool SceneTreeDock::_cyclical_dependency_exists(const String& p_target_scene_path, Node* p_desired_node) {
+	int childCount = p_desired_node->get_child_count();
+
+	if (p_desired_node->get_filename()==p_target_scene_path) {
+		return true;
+	}
+
+	for (int i=0;i<childCount;i++) {
+		Node* child=p_desired_node->get_child(i);
+
+		if(_cyclical_dependency_exists(p_target_scene_path,child)) {
+			return true;
+		}
+	}
+
+	return false;
+}
+
+
 static String _get_name_num_separator() {
 	switch(EditorSettings::get_singleton()->get("scenetree_editor/duplicate_node_name_num_separator").operator int()) {
 		case 0: return "";

+ 1 - 0
tools/editor/scene_tree_dock.h

@@ -102,6 +102,7 @@ class SceneTreeDock : public VBoxContainer {
 	void _load_request(const String& p_path);
 	void _script_open_request(const Ref<Script>& p_script);
 
+	bool _cyclical_dependency_exists(const String& p_target_scene_path, Node* p_desired_node);
 
 	void _node_selected();
 	void _node_renamed();

+ 2 - 2
tools/export/blender25/io_scene_dae/export_dae.py

@@ -212,8 +212,8 @@ class DaeExporter:
 		imgid = self.new_id("image")
 		
 		if (not os.path.isfile(imgpath)):
-			if img_tmp_path.endswith((".bmp",".rgb",".png",".jpeg",".jpg",".jp2",".tga",".cin",".dpx",".exr",".hdr",".tif")):
-				imgpath="images/"+os.path.basename(img_tmp_path)
+			if imgpath.endswith((".bmp",".rgb",".png",".jpeg",".jpg",".jp2",".tga",".cin",".dpx",".exr",".hdr",".tif")):
+				imgpath="images/"+os.path.basename(imgpath)
 			else:
 				imgpath="images/"+image.name+".png"
 		

部分文件因为文件数量过多而无法显示