summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitattributes10
-rw-r--r--core/SCsub1
-rw-r--r--core/bind/core_bind.cpp135
-rw-r--r--core/bind/core_bind.h39
-rw-r--r--core/math/geometry.cpp106
-rw-r--r--core/math/geometry.h77
-rw-r--r--core/os/os.h2
-rw-r--r--doc/classes/EditorExportPlugin.xml6
-rw-r--r--doc/classes/EditorResourcePreviewGenerator.xml16
-rw-r--r--doc/classes/Geometry.xml160
-rw-r--r--drivers/gles2/shader_compiler_gles2.cpp1
-rw-r--r--drivers/gles2/shaders/scene.glsl50
-rw-r--r--drivers/gles3/shader_compiler_gles3.cpp1
-rw-r--r--drivers/gles3/shaders/scene.glsl59
-rw-r--r--drivers/unix/os_unix.cpp2
-rw-r--r--drivers/unix/os_unix.h2
-rw-r--r--editor/SCsub3
-rw-r--r--editor/editor_export.cpp1
-rw-r--r--editor/editor_fonts.cpp3
-rw-r--r--editor/editor_resource_preview.cpp35
-rw-r--r--editor/editor_resource_preview.h3
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp3
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp71
-rw-r--r--editor/plugins/editor_preview_plugins.cpp8
-rw-r--r--editor/plugins/editor_preview_plugins.h8
-rw-r--r--editor/plugins/theme_editor_plugin.cpp8
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp565
-rw-r--r--editor/plugins/visual_shader_editor_plugin.h19
-rw-r--r--editor/project_export.cpp6
-rw-r--r--modules/gdnative/config.py3
-rw-r--r--modules/gdnative/doc_classes/WebRTCDataChannelGDNative.xml (renamed from modules/gdnative/doc_classes/WebRTCPeerGDNative.xml)2
-rw-r--r--modules/gdnative/doc_classes/WebRTCPeerConnectionGDNative.xml13
-rw-r--r--modules/gdnative/gdnative/dictionary.cpp9
-rw-r--r--modules/gdnative/gdnative/variant.cpp2
-rw-r--r--modules/gdnative/gdnative_api.json38
-rw-r--r--modules/gdnative/include/gdnative/dictionary.h2
-rw-r--r--modules/gdnative/include/net/godot_net.h32
-rw-r--r--modules/gdnative/include/net/godot_webrtc.h122
-rw-r--r--modules/gdnative/net/webrtc_gdnative.cpp (renamed from modules/gdnative/net/webrtc_peer_gdnative.cpp)23
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp2
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.h2
-rw-r--r--modules/mono/csharp_script.cpp28
-rw-r--r--modules/mono/csharp_script.h2
-rw-r--r--modules/mono/editor/bindings_generator.cpp16
-rw-r--r--modules/mono/editor/bindings_generator.h4
-rw-r--r--modules/mono/glue/Managed/Files/Extensions/NodeExtensions.cs4
-rw-r--r--modules/mono/glue/Managed/Files/MarshalUtils.cs40
-rw-r--r--modules/mono/glue/Managed/IgnoredFiles/Node.cs5
-rw-r--r--modules/mono/glue/collections_glue.cpp4
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.cpp24
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp45
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h12
-rw-r--r--modules/webrtc/config.py3
-rw-r--r--modules/webrtc/doc_classes/WebRTCDataChannel.xml95
-rw-r--r--modules/webrtc/doc_classes/WebRTCPeerConnection.xml (renamed from modules/webrtc/doc_classes/WebRTCPeer.xml)56
-rw-r--r--modules/webrtc/register_types.cpp22
-rw-r--r--modules/webrtc/register_types.h4
-rw-r--r--modules/webrtc/webrtc_data_channel.cpp64
-rw-r--r--modules/webrtc/webrtc_data_channel.h (renamed from modules/webrtc/webrtc_peer.h)51
-rw-r--r--modules/webrtc/webrtc_data_channel_gdnative.cpp (renamed from modules/webrtc/webrtc_peer_gdnative.cpp)96
-rw-r--r--modules/webrtc/webrtc_data_channel_gdnative.h (renamed from modules/webrtc/webrtc_peer_gdnative.h)44
-rw-r--r--modules/webrtc/webrtc_data_channel_js.cpp352
-rw-r--r--modules/webrtc/webrtc_data_channel_js.h (renamed from modules/webrtc/webrtc_peer_js.h)63
-rw-r--r--modules/webrtc/webrtc_peer_connection.cpp (renamed from modules/webrtc/webrtc_peer.cpp)52
-rw-r--r--modules/webrtc/webrtc_peer_connection.h74
-rw-r--r--modules/webrtc/webrtc_peer_connection_gdnative.cpp124
-rw-r--r--modules/webrtc/webrtc_peer_connection_gdnative.h73
-rw-r--r--modules/webrtc/webrtc_peer_connection_js.cpp314
-rw-r--r--modules/webrtc/webrtc_peer_connection_js.h66
-rw-r--r--modules/webrtc/webrtc_peer_js.cpp455
-rw-r--r--platform/android/detect.py5
-rw-r--r--platform/android/os_android.cpp2
-rw-r--r--platform/android/os_android.h2
-rw-r--r--platform/haiku/os_haiku.cpp2
-rw-r--r--platform/haiku/os_haiku.h2
-rw-r--r--platform/iphone/detect.py10
-rw-r--r--platform/iphone/os_iphone.cpp2
-rw-r--r--platform/iphone/os_iphone.h2
-rw-r--r--platform/javascript/detect.py10
-rw-r--r--platform/javascript/os_javascript.cpp2
-rw-r--r--platform/javascript/os_javascript.h2
-rw-r--r--platform/osx/crash_handler_osx.mm7
-rw-r--r--platform/osx/os_osx.h2
-rw-r--r--platform/osx/os_osx.mm2
-rw-r--r--platform/server/os_server.cpp2
-rw-r--r--platform/server/os_server.h2
-rw-r--r--platform/uwp/os_uwp.cpp2
-rw-r--r--platform/uwp/os_uwp.h2
-rw-r--r--platform/windows/crash_handler_windows.cpp9
-rw-r--r--platform/windows/os_windows.cpp2
-rw-r--r--platform/windows/os_windows.h2
-rw-r--r--platform/x11/crash_handler_x11.cpp7
-rw-r--r--platform/x11/os_x11.cpp30
-rw-r--r--platform/x11/os_x11.h2
-rw-r--r--scene/2d/skeleton_2d.h6
-rw-r--r--scene/3d/spatial.cpp23
-rw-r--r--scene/animation/animation_node_state_machine.cpp3
-rw-r--r--scene/animation/animation_player.cpp8
-rw-r--r--scene/gui/control.cpp55
-rw-r--r--scene/gui/line_edit.cpp1
-rw-r--r--scene/gui/text_edit.cpp3
-rw-r--r--scene/gui/text_edit.h2
-rw-r--r--scene/register_scene_types.cpp2
-rw-r--r--scene/resources/material.cpp9
-rw-r--r--scene/resources/material.h1
-rw-r--r--scene/resources/visual_shader.cpp661
-rw-r--r--scene/resources/visual_shader.h92
-rw-r--r--servers/visual/shader_types.cpp2
-rw-r--r--thirdparty/README.md2
-rw-r--r--thirdparty/assimp/code/res/resource.h14
-rw-r--r--thirdparty/misc/clipper-exceptions.patch154
-rw-r--r--thirdparty/misc/clipper.cpp68
-rw-r--r--thirdparty/xatlas/xatlas.cpp44
113 files changed, 4160 insertions, 944 deletions
diff --git a/.gitattributes b/.gitattributes
index 47ba2b45bb..f2cf6ab63a 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,12 +1,6 @@
# Properly detect languages on Github
*.h linguist-language=cpp
*.inc linguist-language=cpp
-drivers/* linguist-vendored
+thirdparty/* linguist-vendored
-*.cpp eol=lf
-*.mm eol=lf
-*.h eol=lf
-*.py eol=lf
-*.hpp eol=lf
-*.xml eol=lf
-*.natvis eol=lf
+* text=auto eol=lf
diff --git a/core/SCsub b/core/SCsub
index 06efc8408d..166b7083e4 100644
--- a/core/SCsub
+++ b/core/SCsub
@@ -58,6 +58,7 @@ thirdparty_misc_sources = [
"md5.cpp",
"pcg.cpp",
"triangulator.cpp",
+ "clipper.cpp",
]
thirdparty_misc_sources = [thirdparty_misc_dir + file for file in thirdparty_misc_sources]
env_thirdparty.add_source_files(env.core_sources, thirdparty_misc_sources)
diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp
index ba595b9627..7562c82e8a 100644
--- a/core/bind/core_bind.cpp
+++ b/core/bind/core_bind.cpp
@@ -1491,6 +1491,11 @@ PoolVector<Vector3> _Geometry::segment_intersects_convex(const Vector3 &p_from,
return r;
}
+bool _Geometry::is_polygon_clockwise(const Vector<Vector2> &p_polygon) {
+
+ return Geometry::is_polygon_clockwise(p_polygon);
+}
+
Vector<int> _Geometry::triangulate_polygon(const Vector<Vector2> &p_polygon) {
return Geometry::triangulate_polygon(p_polygon);
@@ -1506,6 +1511,107 @@ Vector<Vector3> _Geometry::clip_polygon(const Vector<Vector3> &p_points, const P
return Geometry::clip_polygon(p_points, p_plane);
}
+Array _Geometry::merge_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) {
+
+ Vector<Vector<Point2> > polys = Geometry::merge_polygons_2d(p_polygon_a, p_polygon_b);
+
+ Array ret;
+
+ for (int i = 0; i < polys.size(); ++i) {
+ ret.push_back(polys[i]);
+ }
+ return ret;
+}
+
+Array _Geometry::clip_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) {
+
+ Vector<Vector<Point2> > polys = Geometry::clip_polygons_2d(p_polygon_a, p_polygon_b);
+
+ Array ret;
+
+ for (int i = 0; i < polys.size(); ++i) {
+ ret.push_back(polys[i]);
+ }
+ return ret;
+}
+
+Array _Geometry::intersect_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) {
+
+ Vector<Vector<Point2> > polys = Geometry::intersect_polygons_2d(p_polygon_a, p_polygon_b);
+
+ Array ret;
+
+ for (int i = 0; i < polys.size(); ++i) {
+ ret.push_back(polys[i]);
+ }
+ return ret;
+}
+
+Array _Geometry::exclude_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) {
+
+ Vector<Vector<Point2> > polys = Geometry::exclude_polygons_2d(p_polygon_a, p_polygon_b);
+
+ Array ret;
+
+ for (int i = 0; i < polys.size(); ++i) {
+ ret.push_back(polys[i]);
+ }
+ return ret;
+}
+
+Array _Geometry::clip_polyline_with_polygon_2d(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) {
+
+ Vector<Vector<Point2> > polys = Geometry::clip_polyline_with_polygon_2d(p_polyline, p_polygon);
+
+ Array ret;
+
+ for (int i = 0; i < polys.size(); ++i) {
+ ret.push_back(polys[i]);
+ }
+ return ret;
+}
+
+Array _Geometry::intersect_polyline_with_polygon_2d(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) {
+
+ Vector<Vector<Point2> > polys = Geometry::intersect_polyline_with_polygon_2d(p_polyline, p_polygon);
+
+ Array ret;
+
+ for (int i = 0; i < polys.size(); ++i) {
+ ret.push_back(polys[i]);
+ }
+ return ret;
+}
+
+Array _Geometry::offset_polygon_2d(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type) {
+
+ Vector<Vector<Point2> > polys = Geometry::offset_polygon_2d(p_polygon, p_delta, Geometry::PolyJoinType(p_join_type));
+
+ Array ret;
+
+ for (int i = 0; i < polys.size(); ++i) {
+ ret.push_back(polys[i]);
+ }
+ return ret;
+}
+
+Array _Geometry::offset_polyline_2d(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) {
+
+ Vector<Vector<Point2> > polys = Geometry::offset_polyline_2d(p_polygon, p_delta, Geometry::PolyJoinType(p_join_type), Geometry::PolyEndType(p_end_type));
+
+ Array ret;
+
+ for (int i = 0; i < polys.size(); ++i) {
+ ret.push_back(polys[i]);
+ }
+ return ret;
+}
+
+Vector<Point2> _Geometry::transform_points_2d(const Vector<Point2> &p_points, const Transform2D &p_mat) {
+
+ return Geometry::transform_points_2d(p_points, p_mat);
+}
+
Dictionary _Geometry::make_atlas(const Vector<Size2> &p_rects) {
Dictionary ret;
@@ -1566,11 +1672,40 @@ void _Geometry::_bind_methods() {
ClassDB::bind_method(D_METHOD("segment_intersects_convex", "from", "to", "planes"), &_Geometry::segment_intersects_convex);
ClassDB::bind_method(D_METHOD("point_is_inside_triangle", "point", "a", "b", "c"), &_Geometry::point_is_inside_triangle);
+ ClassDB::bind_method(D_METHOD("is_polygon_clockwise", "polygon"), &_Geometry::is_polygon_clockwise);
ClassDB::bind_method(D_METHOD("triangulate_polygon", "polygon"), &_Geometry::triangulate_polygon);
ClassDB::bind_method(D_METHOD("convex_hull_2d", "points"), &_Geometry::convex_hull_2d);
ClassDB::bind_method(D_METHOD("clip_polygon", "points", "plane"), &_Geometry::clip_polygon);
+ ClassDB::bind_method(D_METHOD("merge_polygons_2d", "polygon_a", "polygon_b"), &_Geometry::merge_polygons_2d);
+ ClassDB::bind_method(D_METHOD("clip_polygons_2d", "polygon_a", "polygon_b"), &_Geometry::clip_polygons_2d);
+ ClassDB::bind_method(D_METHOD("intersect_polygons_2d", "polygon_a", "polygon_b"), &_Geometry::intersect_polygons_2d);
+ ClassDB::bind_method(D_METHOD("exclude_polygons_2d", "polygon_a", "polygon_b"), &_Geometry::exclude_polygons_2d);
+
+ ClassDB::bind_method(D_METHOD("clip_polyline_with_polygon_2d", "polyline", "polygon"), &_Geometry::clip_polyline_with_polygon_2d);
+ ClassDB::bind_method(D_METHOD("intersect_polyline_with_polygon_2d", "polyline", "polygon"), &_Geometry::intersect_polyline_with_polygon_2d);
+
+ ClassDB::bind_method(D_METHOD("offset_polygon_2d", "polygon", "delta", "join_type"), &_Geometry::offset_polygon_2d, DEFVAL(JOIN_SQUARE));
+ ClassDB::bind_method(D_METHOD("offset_polyline_2d", "polyline", "delta", "join_type", "end_type"), &_Geometry::offset_polyline_2d, DEFVAL(JOIN_SQUARE), DEFVAL(END_SQUARE));
+
+ ClassDB::bind_method(D_METHOD("transform_points_2d", "points", "transform"), &_Geometry::transform_points_2d);
+
ClassDB::bind_method(D_METHOD("make_atlas", "sizes"), &_Geometry::make_atlas);
+
+ BIND_ENUM_CONSTANT(OPERATION_UNION);
+ BIND_ENUM_CONSTANT(OPERATION_DIFFERENCE);
+ BIND_ENUM_CONSTANT(OPERATION_INTERSECTION);
+ BIND_ENUM_CONSTANT(OPERATION_XOR);
+
+ BIND_ENUM_CONSTANT(JOIN_SQUARE);
+ BIND_ENUM_CONSTANT(JOIN_ROUND);
+ BIND_ENUM_CONSTANT(JOIN_MITER);
+
+ BIND_ENUM_CONSTANT(END_POLYGON);
+ BIND_ENUM_CONSTANT(END_JOINED);
+ BIND_ENUM_CONSTANT(END_BUTT);
+ BIND_ENUM_CONSTANT(END_SQUARE);
+ BIND_ENUM_CONSTANT(END_ROUND);
}
_Geometry::_Geometry() {
diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h
index 2906de4a4a..561449f29e 100644
--- a/core/bind/core_bind.h
+++ b/core/bind/core_bind.h
@@ -402,15 +402,54 @@ public:
real_t segment_intersects_circle(const Vector2 &p_from, const Vector2 &p_to, const Vector2 &p_circle_pos, real_t p_circle_radius);
int get_uv84_normal_bit(const Vector3 &p_vector);
+ bool is_polygon_clockwise(const Vector<Vector2> &p_polygon);
Vector<int> triangulate_polygon(const Vector<Vector2> &p_polygon);
Vector<Point2> convex_hull_2d(const Vector<Point2> &p_points);
Vector<Vector3> clip_polygon(const Vector<Vector3> &p_points, const Plane &p_plane);
+ enum PolyBooleanOperation {
+ OPERATION_UNION,
+ OPERATION_DIFFERENCE,
+ OPERATION_INTERSECTION,
+ OPERATION_XOR
+ };
+ // 2D polygon boolean operations
+ Array merge_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b); // union (add)
+ Array clip_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b); // difference (subtract)
+ Array intersect_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b); // common area (multiply)
+ Array exclude_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b); // all but common area (xor)
+
+ // 2D polyline vs polygon operations
+ Array clip_polyline_with_polygon_2d(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon); // cut
+ Array intersect_polyline_with_polygon_2d(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon); // chop
+
+ // 2D offset polygons/polylines
+ enum PolyJoinType {
+ JOIN_SQUARE,
+ JOIN_ROUND,
+ JOIN_MITER
+ };
+ enum PolyEndType {
+ END_POLYGON,
+ END_JOINED,
+ END_BUTT,
+ END_SQUARE,
+ END_ROUND
+ };
+ Array offset_polygon_2d(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type = JOIN_SQUARE);
+ Array offset_polyline_2d(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type = JOIN_SQUARE, PolyEndType p_end_type = END_SQUARE);
+
+ Vector<Point2> transform_points_2d(const Vector<Point2> &p_points, const Transform2D &p_mat);
+
Dictionary make_atlas(const Vector<Size2> &p_rects);
_Geometry();
};
+VARIANT_ENUM_CAST(_Geometry::PolyBooleanOperation);
+VARIANT_ENUM_CAST(_Geometry::PolyJoinType);
+VARIANT_ENUM_CAST(_Geometry::PolyEndType);
+
class _File : public Reference {
GDCLASS(_File, Reference);
diff --git a/core/math/geometry.cpp b/core/math/geometry.cpp
index 0ab8707d3a..8314cb827c 100644
--- a/core/math/geometry.cpp
+++ b/core/math/geometry.cpp
@@ -31,8 +31,11 @@
#include "geometry.h"
#include "core/print_string.h"
+#include "thirdparty/misc/clipper.hpp"
#include "thirdparty/misc/triangulator.h"
+#define SCALE_FACTOR 100000.0 // based on CMP_EPSILON
+
/* this implementation is very inefficient, commenting unless bugs happen. See the other one.
bool Geometry::is_point_in_polygon(const Vector2 &p_point, const Vector<Vector2> &p_polygon) {
@@ -1134,3 +1137,106 @@ void Geometry::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_resu
r_size = Size2(results[best].max_w, results[best].max_h);
}
+
+Vector<Vector<Point2> > Geometry::_polypaths_do_operation(PolyBooleanOperation p_op, const Vector<Point2> &p_polypath_a, const Vector<Point2> &p_polypath_b, bool is_a_open) {
+
+ using namespace ClipperLib;
+
+ ClipType op = ctUnion;
+
+ switch (p_op) {
+ case OPERATION_UNION: op = ctUnion; break;
+ case OPERATION_DIFFERENCE: op = ctDifference; break;
+ case OPERATION_INTERSECTION: op = ctIntersection; break;
+ case OPERATION_XOR: op = ctXor; break;
+ }
+ Path path_a, path_b;
+
+ // Need to scale points (Clipper's requirement for robust computation)
+ for (int i = 0; i != p_polypath_a.size(); ++i) {
+ path_a << IntPoint(p_polypath_a[i].x * SCALE_FACTOR, p_polypath_a[i].y * SCALE_FACTOR);
+ }
+ for (int i = 0; i != p_polypath_b.size(); ++i) {
+ path_b << IntPoint(p_polypath_b[i].x * SCALE_FACTOR, p_polypath_b[i].y * SCALE_FACTOR);
+ }
+ Clipper clp;
+ clp.AddPath(path_a, ptSubject, !is_a_open); // forward compatible with Clipper 10.0.0
+ clp.AddPath(path_b, ptClip, true); // polylines cannot be set as clip
+
+ Paths paths;
+
+ if (is_a_open) {
+ PolyTree tree; // needed to populate polylines
+ clp.Execute(op, tree);
+ OpenPathsFromPolyTree(tree, paths);
+ } else {
+ clp.Execute(op, paths); // works on closed polygons only
+ }
+ // Have to scale points down now
+ Vector<Vector<Point2> > polypaths;
+
+ for (Paths::size_type i = 0; i < paths.size(); ++i) {
+ Vector<Vector2> polypath;
+
+ const Path &scaled_path = paths[i];
+
+ for (Paths::size_type j = 0; j < scaled_path.size(); ++j) {
+ polypath.push_back(Point2(
+ static_cast<real_t>(scaled_path[j].X) / SCALE_FACTOR,
+ static_cast<real_t>(scaled_path[j].Y) / SCALE_FACTOR));
+ }
+ polypaths.push_back(polypath);
+ }
+ return polypaths;
+}
+
+Vector<Vector<Point2> > Geometry::_polypath_offset(const Vector<Point2> &p_polypath, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) {
+
+ using namespace ClipperLib;
+
+ JoinType jt = jtSquare;
+
+ switch (p_join_type) {
+ case JOIN_SQUARE: jt = jtSquare; break;
+ case JOIN_ROUND: jt = jtRound; break;
+ case JOIN_MITER: jt = jtMiter; break;
+ }
+
+ EndType et = etClosedPolygon;
+
+ switch (p_end_type) {
+ case END_POLYGON: et = etClosedPolygon; break;
+ case END_JOINED: et = etClosedLine; break;
+ case END_BUTT: et = etOpenButt; break;
+ case END_SQUARE: et = etOpenSquare; break;
+ case END_ROUND: et = etOpenRound; break;
+ }
+ ClipperOffset co;
+ Path path;
+
+ // Need to scale points (Clipper's requirement for robust computation)
+ for (int i = 0; i != p_polypath.size(); ++i) {
+ path << IntPoint(p_polypath[i].x * SCALE_FACTOR, p_polypath[i].y * SCALE_FACTOR);
+ }
+ co.AddPath(path, jt, et);
+
+ Paths paths;
+ co.Execute(paths, p_delta * SCALE_FACTOR); // inflate/deflate
+
+ // Have to scale points down now
+ Vector<Vector<Point2> > polypaths;
+
+ for (Paths::size_type i = 0; i < paths.size(); ++i) {
+ Vector<Vector2> polypath;
+
+ const Path &scaled_path = paths[i];
+
+ for (Paths::size_type j = 0; j < scaled_path.size(); ++j) {
+ polypath.push_back(Point2(
+ static_cast<real_t>(scaled_path[j].X) / SCALE_FACTOR,
+ static_cast<real_t>(scaled_path[j].Y) / SCALE_FACTOR));
+ }
+ polypaths.push_back(polypath);
+ }
+ return polypaths;
+}
diff --git a/core/math/geometry.h b/core/math/geometry.h
index 0b2adf9513..c62573dd13 100644
--- a/core/math/geometry.h
+++ b/core/math/geometry.h
@@ -785,6 +785,78 @@ public:
return clipped;
}
+ enum PolyBooleanOperation {
+ OPERATION_UNION,
+ OPERATION_DIFFERENCE,
+ OPERATION_INTERSECTION,
+ OPERATION_XOR
+ };
+ enum PolyJoinType {
+ JOIN_SQUARE,
+ JOIN_ROUND,
+ JOIN_MITER
+ };
+ enum PolyEndType {
+ END_POLYGON,
+ END_JOINED,
+ END_BUTT,
+ END_SQUARE,
+ END_ROUND
+ };
+
+ static Vector<Vector<Point2> > merge_polygons_2d(const Vector<Point2> &p_polygon_a, const Vector<Point2> &p_polygon_b) {
+
+ return _polypaths_do_operation(OPERATION_UNION, p_polygon_a, p_polygon_b);
+ }
+
+ static Vector<Vector<Point2> > clip_polygons_2d(const Vector<Point2> &p_polygon_a, const Vector<Point2> &p_polygon_b) {
+
+ return _polypaths_do_operation(OPERATION_DIFFERENCE, p_polygon_a, p_polygon_b);
+ }
+
+ static Vector<Vector<Point2> > intersect_polygons_2d(const Vector<Point2> &p_polygon_a, const Vector<Point2> &p_polygon_b) {
+
+ return _polypaths_do_operation(OPERATION_INTERSECTION, p_polygon_a, p_polygon_b);
+ }
+
+ static Vector<Vector<Point2> > exclude_polygons_2d(const Vector<Point2> &p_polygon_a, const Vector<Point2> &p_polygon_b) {
+
+ return _polypaths_do_operation(OPERATION_XOR, p_polygon_a, p_polygon_b);
+ }
+
+ static Vector<Vector<Point2> > clip_polyline_with_polygon_2d(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) {
+
+ return _polypaths_do_operation(OPERATION_DIFFERENCE, p_polyline, p_polygon, true);
+ }
+
+ static Vector<Vector<Point2> > intersect_polyline_with_polygon_2d(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) {
+
+ return _polypaths_do_operation(OPERATION_INTERSECTION, p_polyline, p_polygon, true);
+ }
+
+ static Vector<Vector<Point2> > offset_polygon_2d(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type) {
+
+ return _polypath_offset(p_polygon, p_delta, p_join_type, END_POLYGON);
+ }
+
+ static Vector<Vector<Point2> > offset_polyline_2d(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) {
+
+ ERR_EXPLAIN("Attempt to offset a polyline like a polygon (use offset_polygon_2d instead).");
+ ERR_FAIL_COND_V(p_end_type == END_POLYGON, Vector<Vector<Point2> >());
+
+ return _polypath_offset(p_polygon, p_delta, p_join_type, p_end_type);
+ }
+
+ static Vector<Point2> transform_points_2d(const Vector<Point2> &p_points, const Transform2D &p_mat) {
+
+ Vector<Point2> points;
+
+ for (int i = 0; i < p_points.size(); ++i) {
+ points.push_back(p_mat.xform(p_points[i]));
+ }
+ return points;
+ }
+
static Vector<int> triangulate_polygon(const Vector<Vector2> &p_polygon) {
Vector<int> triangles;
@@ -951,7 +1023,6 @@ public:
H.resize(k);
return H;
}
-
static Vector<Vector<Vector2> > decompose_polygon_in_convex(Vector<Point2> polygon);
static MeshData build_convex_mesh(const PoolVector<Plane> &p_planes);
@@ -961,6 +1032,10 @@ public:
static PoolVector<Plane> build_capsule_planes(real_t p_radius, real_t p_height, int p_sides, int p_lats, Vector3::Axis p_axis = Vector3::AXIS_Z);
static void make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_result, Size2i &r_size);
+
+private:
+ static Vector<Vector<Point2> > _polypaths_do_operation(PolyBooleanOperation p_op, const Vector<Point2> &p_polypath_a, const Vector<Point2> &p_polypath_b, bool is_a_open = false);
+ static Vector<Vector<Point2> > _polypath_offset(const Vector<Point2> &p_polypath, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type);
};
#endif
diff --git a/core/os/os.h b/core/os/os.h
index 4ae8a354e5..07865b636e 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -273,7 +273,7 @@ public:
virtual String get_environment(const String &p_var) const = 0;
virtual bool set_environment(const String &p_var, const String &p_value) const = 0;
- virtual String get_name() = 0;
+ virtual String get_name() const = 0;
virtual List<String> get_cmdline_args() const { return _cmdline; }
virtual String get_model_name() const;
diff --git a/doc/classes/EditorExportPlugin.xml b/doc/classes/EditorExportPlugin.xml
index 998b05d495..4e4e29dc4e 100644
--- a/doc/classes/EditorExportPlugin.xml
+++ b/doc/classes/EditorExportPlugin.xml
@@ -21,6 +21,12 @@
<description>
</description>
</method>
+ <method name="_export_end" qualifiers="virtual">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="_export_file" qualifiers="virtual">
<return type="void">
</return>
diff --git a/doc/classes/EditorResourcePreviewGenerator.xml b/doc/classes/EditorResourcePreviewGenerator.xml
index 6592ffd1a8..76b9445933 100644
--- a/doc/classes/EditorResourcePreviewGenerator.xml
+++ b/doc/classes/EditorResourcePreviewGenerator.xml
@@ -9,6 +9,14 @@
<tutorials>
</tutorials>
<methods>
+ <method name="can_generate_small_preview" qualifiers="virtual">
+ <return type="bool">
+ </return>
+ <description>
+ If this function returns true the generator will call [method generate] or [method generate_from_path] for small previews too.
+ By default it returns false.
+ </description>
+ </method>
<method name="generate" qualifiers="virtual">
<return type="Texture">
</return>
@@ -35,6 +43,14 @@
Care must be taken because this function is always called from a thread (not the main thread).
</description>
</method>
+ <method name="generate_small_preview_automatically" qualifiers="virtual">
+ <return type="bool">
+ </return>
+ <description>
+ If this function returns true the generator will automatically generate the small previews from the normal preview texture generated by the methods [method generate] or [method generate_from_path].
+ By default it returns false.
+ </description>
+ </method>
<method name="handles" qualifiers="virtual">
<return type="bool">
</return>
diff --git a/doc/classes/Geometry.xml b/doc/classes/Geometry.xml
index 68539d7ecb..b95b888cf1 100644
--- a/doc/classes/Geometry.xml
+++ b/doc/classes/Geometry.xml
@@ -59,6 +59,29 @@
Clips the polygon defined by the points in [code]points[/code] against the [code]plane[/code] and returns the points of the clipped polygon.
</description>
</method>
+ <method name="clip_polygons_2d">
+ <return type="Array">
+ </return>
+ <argument index="0" name="polygon_a" type="PoolVector2Array">
+ </argument>
+ <argument index="1" name="polygon_b" type="PoolVector2Array">
+ </argument>
+ <description>
+ Clips [code]polygon_a[/code] against [code]polygon_b[/code] and returns an array of clipped polygons. This performs [code]OPERATION_DIFFERENCE[/code] between polygons. Returns an empty array if [code]polygon_b[/code] completely overlaps [code]polygon_a[/code].
+ If [code]polygon_b[/code] is enclosed by [code]polygon_a[/code], returns an outer polygon (boundary) and inner polygon (hole) which could be distiguished by calling [method is_polygon_clockwise].
+ </description>
+ </method>
+ <method name="clip_polyline_with_polygon_2d">
+ <return type="Array">
+ </return>
+ <argument index="0" name="polyline" type="PoolVector2Array">
+ </argument>
+ <argument index="1" name="polygon" type="PoolVector2Array">
+ </argument>
+ <description>
+ Clips [code]polyline[/code] against [code]polygon[/code] and returns an array of clipped polylines. This performs [code]OPERATION_DIFFERENCE[/code] between the polyline and the polygon. This operation can be thought of as cutting a line with a closed shape.
+ </description>
+ </method>
<method name="convex_hull_2d">
<return type="PoolVector2Array">
</return>
@@ -68,6 +91,18 @@
Given an array of [Vector2]s, returns the convex hull as a list of points in counter-clockwise order. The last point is the same as the first one.
</description>
</method>
+ <method name="exclude_polygons_2d">
+ <return type="Array">
+ </return>
+ <argument index="0" name="polygon_a" type="PoolVector2Array">
+ </argument>
+ <argument index="1" name="polygon_b" type="PoolVector2Array">
+ </argument>
+ <description>
+ Mutually excludes common area defined by intersection of [code]polygon_a[/code] and [code]polygon_b[/code] (see [method intersect_polygons_2d]) and returns an array of excluded polygons. This performs [code]OPERATION_XOR[/code] between polygons. In other words, returns all but common area between polygons.
+ The operation may result in an outer polygon (boundary) and inner polygon (hole) produced which could be distiguished by calling [method is_polygon_clockwise].
+ </description>
+ </method>
<method name="get_closest_point_to_segment">
<return type="Vector3">
</return>
@@ -158,6 +193,38 @@
<description>
</description>
</method>
+ <method name="intersect_polygons_2d">
+ <return type="Array">
+ </return>
+ <argument index="0" name="polygon_a" type="PoolVector2Array">
+ </argument>
+ <argument index="1" name="polygon_b" type="PoolVector2Array">
+ </argument>
+ <description>
+ Intersects [code]polygon_a[/code] with [code]polygon_b[/code] and returns an array of intersected polygons. This performs [code]OPERATION_INTERSECTION[/code] between polygons. In other words, returns common area shared by polygons. Returns an empty array if no intersection occurs.
+ The operation may result in an outer polygon (boundary) and inner polygon (hole) produced which could be distiguished by calling [method is_polygon_clockwise].
+ </description>
+ </method>
+ <method name="intersect_polyline_with_polygon_2d">
+ <return type="Array">
+ </return>
+ <argument index="0" name="polyline" type="PoolVector2Array">
+ </argument>
+ <argument index="1" name="polygon" type="PoolVector2Array">
+ </argument>
+ <description>
+ Intersects [code]polyline[/code] with [code]polygon[/code] and returns an array of intersected polylines. This performs [code]OPERATION_INTERSECTION[/code] between the polyline and the polygon. This operation can be thought of as chopping a line with a closed shape.
+ </description>
+ </method>
+ <method name="is_polygon_clockwise">
+ <return type="bool">
+ </return>
+ <argument index="0" name="polygon" type="PoolVector2Array">
+ </argument>
+ <description>
+ Returns [code]true[/code] if [code]polygon[/code]'s vertices are ordered in clockwise order, otherwise returns [code]false[/code].
+ </description>
+ </method>
<method name="line_intersects_line_2d">
<return type="Variant">
</return>
@@ -182,6 +249,51 @@
Given an array of [Vector2]s representing tiles, builds an atlas. The returned dictionary has two keys: [code]points[/code] is a vector of [Vector2] that specifies the positions of each tile, [code]size[/code] contains the overall size of the whole atlas as [Vector2].
</description>
</method>
+ <method name="merge_polygons_2d">
+ <return type="Array">
+ </return>
+ <argument index="0" name="polygon_a" type="PoolVector2Array">
+ </argument>
+ <argument index="1" name="polygon_b" type="PoolVector2Array">
+ </argument>
+ <description>
+ Merges (combines) [code]polygon_a[/code] and [code]polygon_b[/code] and returns an array of merged polygons. This performs [code]OPERATION_UNION[/code] between polygons.
+ The operation may result in an outer polygon (boundary) and inner polygon (hole) produced which could be distiguished by calling [method is_polygon_clockwise].
+ </description>
+ </method>
+ <method name="offset_polygon_2d">
+ <return type="Array">
+ </return>
+ <argument index="0" name="polygon" type="PoolVector2Array">
+ </argument>
+ <argument index="1" name="delta" type="float">
+ </argument>
+ <argument index="2" name="join_type" type="int" enum="Geometry.PolyJoinType" default="0">
+ </argument>
+ <description>
+ Inflates or deflates [code]polygon[/code] by [code]delta[/code] units (pixels). If [code]delta[/code] is positive, makes the polygon grow outward. If [code]delta[/code] is negative, shrinks the polygon inward. Returns an array of polygons because inflating/deflating may result in multiple discrete polygons. Returns an empty array if [code]delta[/code] is negative and the absolute value of it approximately exceeds the minimum bounding rectangle dimensions of the polygon.
+ Each polygon's vertices will be rounded as determined by [code]join_type[/code], see [enum Geometry.PolyJoinType].
+ The operation may result in an outer polygon (boundary) and inner polygon (hole) produced which could be distiguished by calling [method is_polygon_clockwise].
+ </description>
+ </method>
+ <method name="offset_polyline_2d">
+ <return type="Array">
+ </return>
+ <argument index="0" name="polyline" type="PoolVector2Array">
+ </argument>
+ <argument index="1" name="delta" type="float">
+ </argument>
+ <argument index="2" name="join_type" type="int" enum="Geometry.PolyJoinType" default="0">
+ </argument>
+ <argument index="3" name="end_type" type="int" enum="Geometry.PolyEndType" default="3">
+ </argument>
+ <description>
+ Inflates or deflates [code]polyline[/code] by [code]delta[/code] units (pixels), producing polygons. If [code]delta[/code] is positive, makes the polyline grow outward. Returns an array of polygons because inflating/deflating may result in multiple discrete polygons. If [code]delta[/code] is negative, returns an empty array.
+ Each polygon's vertices will be rounded as determined by [code]join_type[/code], see [enum Geometry.PolyJoinType].
+ Each polygon's endpoints will be rounded as determined by [code]end_type[/code], see [enum Geometry.PolyEndType].
+ The operation may result in an outer polygon (boundary) and inner polygon (hole) produced which could be distiguished by calling [method is_polygon_clockwise].
+ </description>
+ </method>
<method name="point_is_inside_triangle" qualifiers="const">
<return type="bool">
</return>
@@ -304,6 +416,18 @@
Tests if the segment ([code]from[/code], [code]to[/code]) intersects the triangle [code]a[/code], [code]b[/code], [code]c[/code]. If yes, returns the point of intersection as [Vector3]. If no intersection takes place, an empty [Variant] is returned.
</description>
</method>
+ <method name="transform_points_2d">
+ <return type="PoolVector2Array">
+ </return>
+ <argument index="0" name="points" type="PoolVector2Array">
+ </argument>
+ <argument index="1" name="transform" type="Transform2D">
+ </argument>
+ <description>
+ Transforms an array of points by [code]transform[/code] and returns the result.
+ Can be useful in conjuction with performing polygon boolean operations in CSG manner, see [method merge_polygons_2d], [method clip_polygons_2d], [method intersect_polygons_2d], [method exclude_polygons_2d].
+ </description>
+ </method>
<method name="triangulate_polygon">
<return type="PoolIntArray">
</return>
@@ -315,5 +439,41 @@
</method>
</methods>
<constants>
+ <constant name="OPERATION_UNION" value="0" enum="PolyBooleanOperation">
+ Create regions where either subject or clip polygons (or both) are filled.
+ </constant>
+ <constant name="OPERATION_DIFFERENCE" value="1" enum="PolyBooleanOperation">
+ Create regions where subject polygons are filled except where clip polygons are filled.
+ </constant>
+ <constant name="OPERATION_INTERSECTION" value="2" enum="PolyBooleanOperation">
+ Create regions where both subject and clip polygons are filled.
+ </constant>
+ <constant name="OPERATION_XOR" value="3" enum="PolyBooleanOperation">
+ Create regions where either subject or clip polygons are filled but not where both are filled.
+ </constant>
+ <constant name="JOIN_SQUARE" value="0" enum="PolyJoinType">
+ Squaring is applied uniformally at all convex edge joins at [code]1 * delta[/code].
+ </constant>
+ <constant name="JOIN_ROUND" value="1" enum="PolyJoinType">
+ While flattened paths can never perfectly trace an arc, they are approximated by a series of arc chords.
+ </constant>
+ <constant name="JOIN_MITER" value="2" enum="PolyJoinType">
+ There's a necessary limit to mitered joins since offsetting edges that join at very acute angles will produce excessively long and narrow 'spikes'. For any given edge join, when miter offsetting would exceed that maximum distance, 'square' joining is applied.
+ </constant>
+ <constant name="END_POLYGON" value="0" enum="PolyEndType">
+ Endpoints are joined using the [enum PolyJoinType] value and the path filled as a polygon.
+ </constant>
+ <constant name="END_JOINED" value="1" enum="PolyEndType">
+ Endpoints are joined using the [enum PolyJoinType] value and the path filled as a polyline.
+ </constant>
+ <constant name="END_BUTT" value="2" enum="PolyEndType">
+ Endpoints are squared off with no extension.
+ </constant>
+ <constant name="END_SQUARE" value="3" enum="PolyEndType">
+ Endpoints are squared off and extended by [code]delta[/code] units.
+ </constant>
+ <constant name="END_ROUND" value="4" enum="PolyEndType">
+ Endpoints are rounded off and extended by [code]delta[/code] units.
+ </constant>
</constants>
</class>
diff --git a/drivers/gles2/shader_compiler_gles2.cpp b/drivers/gles2/shader_compiler_gles2.cpp
index d00b03fb8a..6ba4004f20 100644
--- a/drivers/gles2/shader_compiler_gles2.cpp
+++ b/drivers/gles2/shader_compiler_gles2.cpp
@@ -934,6 +934,7 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() {
actions[VS::SHADER_SPATIAL].render_mode_defines["specular_disabled"] = "#define SPECULAR_DISABLED\n";
actions[VS::SHADER_SPATIAL].render_mode_defines["shadows_disabled"] = "#define SHADOWS_DISABLED\n";
actions[VS::SHADER_SPATIAL].render_mode_defines["ambient_light_disabled"] = "#define AMBIENT_LIGHT_DISABLED\n";
+ actions[VS::SHADER_SPATIAL].render_mode_defines["shadow_to_opacity"] = "#define USE_SHADOW_TO_OPACITY\n";
// No defines for particle shaders in GLES2, there are no GPU particles
diff --git a/drivers/gles2/shaders/scene.glsl b/drivers/gles2/shaders/scene.glsl
index 0fa290e57c..ac5af31153 100644
--- a/drivers/gles2/shaders/scene.glsl
+++ b/drivers/gles2/shaders/scene.glsl
@@ -1151,7 +1151,8 @@ void light_compute(
float clearcoat_gloss,
float anisotropy,
inout vec3 diffuse_light,
- inout vec3 specular_light) {
+ inout vec3 specular_light,
+ inout float alpha) {
//this makes lights behave closer to linear, but then addition of lights looks bad
//better left disabled
@@ -1306,10 +1307,10 @@ LIGHT_SHADER_CODE
// shlick+ggx as default
#if defined(LIGHT_USE_ANISOTROPY)
- float alpha = roughness * roughness;
+ float alpha_ggx = roughness * roughness;
float aspect = sqrt(1.0 - anisotropy * 0.9);
- float ax = alpha / aspect;
- float ay = alpha * aspect;
+ float ax = alpha_ggx / aspect;
+ float ay = alpha_ggx * aspect;
float XdotH = dot(T, H);
float YdotH = dot(B, H);
float D = D_GGX_anisotropic(cNdotH, ax, ay, XdotH, YdotH, cNdotH);
@@ -1317,10 +1318,10 @@ LIGHT_SHADER_CODE
float G = V_GGX_anisotropic(ax, ay, dot(T, V), dot(T, L), dot(B, V), dot(B, L), cNdotV, cNdotL);
#else
- float alpha = roughness * roughness;
- float D = D_GGX(cNdotH, alpha);
- //float G = G_GGX_2cos(cNdotL, alpha) * G_GGX_2cos(cNdotV, alpha);
- float G = V_GGX(cNdotL, cNdotV, alpha);
+ float alpha_ggx = roughness * roughness;
+ float D = D_GGX(cNdotH, alpha_ggx);
+ //float G = G_GGX_2cos(cNdotL, alpha_ggx) * G_GGX_2cos(cNdotV, alpha_ggx);
+ float G = V_GGX(cNdotL, cNdotV, alpha_ggx);
#endif
// F
vec3 f0 = F0(metallic, specular, diffuse_color);
@@ -1350,6 +1351,10 @@ LIGHT_SHADER_CODE
#endif
}
+#ifdef USE_SHADOW_TO_OPACITY
+ alpha = min(alpha, clamp(1.0 - length(attenuation), 0.0, 1.0));
+#endif
+
#endif //defined(USE_LIGHT_SHADER_CODE)
}
@@ -1535,17 +1540,21 @@ FRAGMENT_SHADER_CODE
vec3 eye_position = view;
+#if !defined(USE_SHADOW_TO_OPACITY)
+
#if defined(ALPHA_SCISSOR_USED)
if (alpha < alpha_scissor) {
discard;
}
-#endif
+#endif // ALPHA_SCISSOR_USED
#ifdef USE_DEPTH_PREPASS
if (alpha < 0.99) {
discard;
}
-#endif
+#endif // USE_DEPTH_PREPASS
+
+#endif // !USE_SHADOW_TO_OPACITY
#ifdef BASE_PASS
//none
@@ -2061,13 +2070,32 @@ FRAGMENT_SHADER_CODE
clearcoat_gloss,
anisotropy,
diffuse_light,
- specular_light);
+ specular_light,
+ alpha);
#endif //vertex lighting
#endif //USE_LIGHTING
//compute and merge
+#ifdef USE_SHADOW_TO_OPACITY
+
+ alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0));
+
+#if defined(ALPHA_SCISSOR_USED)
+ if (alpha < alpha_scissor) {
+ discard;
+ }
+#endif // ALPHA_SCISSOR_USED
+
+#ifdef USE_DEPTH_PREPASS
+ if (alpha < 0.99) {
+ discard;
+ }
+#endif // USE_DEPTH_PREPASS
+
+#endif // !USE_SHADOW_TO_OPACITY
+
#ifndef RENDER_DEPTH
#ifdef SHADELESS
diff --git a/drivers/gles3/shader_compiler_gles3.cpp b/drivers/gles3/shader_compiler_gles3.cpp
index ad26294527..e8417900ea 100644
--- a/drivers/gles3/shader_compiler_gles3.cpp
+++ b/drivers/gles3/shader_compiler_gles3.cpp
@@ -944,6 +944,7 @@ ShaderCompilerGLES3::ShaderCompilerGLES3() {
actions[VS::SHADER_SPATIAL].render_mode_defines["specular_disabled"] = "#define SPECULAR_DISABLED\n";
actions[VS::SHADER_SPATIAL].render_mode_defines["shadows_disabled"] = "#define SHADOWS_DISABLED\n";
actions[VS::SHADER_SPATIAL].render_mode_defines["ambient_light_disabled"] = "#define AMBIENT_LIGHT_DISABLED\n";
+ actions[VS::SHADER_SPATIAL].render_mode_defines["shadow_to_opacity"] = "#define USE_SHADOW_TO_OPACITY\n";
/* PARTICLES SHADER */
diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl
index 584668a09c..f08d3f4d23 100644
--- a/drivers/gles3/shaders/scene.glsl
+++ b/drivers/gles3/shaders/scene.glsl
@@ -993,7 +993,7 @@ vec3 F0(float metallic, float specular, vec3 albedo) {
return mix(vec3(dielectric), albedo, vec3(metallic));
}
-void light_compute(vec3 N, vec3 L, vec3 V, vec3 B, vec3 T, vec3 light_color, vec3 attenuation, vec3 diffuse_color, vec3 transmission, float specular_blob_intensity, float roughness, float metallic, float specular, float rim, float rim_tint, float clearcoat, float clearcoat_gloss, float anisotropy, inout vec3 diffuse_light, inout vec3 specular_light) {
+void light_compute(vec3 N, vec3 L, vec3 V, vec3 B, vec3 T, vec3 light_color, vec3 attenuation, vec3 diffuse_color, vec3 transmission, float specular_blob_intensity, float roughness, float metallic, float specular, float rim, float rim_tint, float clearcoat, float clearcoat_gloss, float anisotropy, inout vec3 diffuse_light, inout vec3 specular_light, inout float alpha) {
#if defined(USE_LIGHT_SHADER_CODE)
// light is written by the light shader
@@ -1135,19 +1135,19 @@ LIGHT_SHADER_CODE
#if defined(LIGHT_USE_ANISOTROPY)
- float alpha = roughness * roughness;
+ float alpha_ggx = roughness * roughness;
float aspect = sqrt(1.0 - anisotropy * 0.9);
- float ax = alpha / aspect;
- float ay = alpha * aspect;
+ float ax = alpha_ggx / aspect;
+ float ay = alpha_ggx * aspect;
float XdotH = dot(T, H);
float YdotH = dot(B, H);
float D = D_GGX_anisotropic(cNdotH, ax, ay, XdotH, YdotH);
float G = G_GGX_anisotropic_2cos(cNdotL, ax, ay, XdotH, YdotH) * G_GGX_anisotropic_2cos(cNdotV, ax, ay, XdotH, YdotH);
#else
- float alpha = roughness * roughness;
- float D = D_GGX(cNdotH, alpha);
- float G = G_GGX_2cos(cNdotL, alpha) * G_GGX_2cos(cNdotV, alpha);
+ float alpha_ggx = roughness * roughness;
+ float D = D_GGX(cNdotH, alpha_ggx);
+ float G = G_GGX_2cos(cNdotL, alpha_ggx) * G_GGX_2cos(cNdotV, alpha_ggx);
#endif
// F
vec3 f0 = F0(metallic, specular, diffuse_color);
@@ -1174,6 +1174,10 @@ LIGHT_SHADER_CODE
#endif
}
+#ifdef USE_SHADOW_TO_OPACITY
+ alpha = min(alpha, clamp(1.0 - length(attenuation), 0.0, 1.0));
+#endif
+
#endif //defined(USE_LIGHT_SHADER_CODE)
}
@@ -1250,7 +1254,7 @@ vec3 light_transmittance(float translucency,vec3 light_vec, vec3 normal, vec3 po
}
#endif
-void light_process_omni(int idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 binormal, vec3 tangent, vec3 albedo, vec3 transmission, float roughness, float metallic, float specular, float rim, float rim_tint, float clearcoat, float clearcoat_gloss, float anisotropy, float p_blob_intensity, inout vec3 diffuse_light, inout vec3 specular_light) {
+void light_process_omni(int idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 binormal, vec3 tangent, vec3 albedo, vec3 transmission, float roughness, float metallic, float specular, float rim, float rim_tint, float clearcoat, float clearcoat_gloss, float anisotropy, float p_blob_intensity, inout vec3 diffuse_light, inout vec3 specular_light, inout float alpha) {
vec3 light_rel_vec = omni_lights[idx].light_pos_inv_radius.xyz - vertex;
float light_length = length(light_rel_vec);
@@ -1304,10 +1308,10 @@ void light_process_omni(int idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 bi
light_attenuation *= mix(omni_lights[idx].shadow_color_contact.rgb, vec3(1.0), shadow);
}
#endif //SHADOWS_DISABLED
- light_compute(normal, normalize(light_rel_vec), eye_vec, binormal, tangent, omni_lights[idx].light_color_energy.rgb, light_attenuation, albedo, transmission, omni_lights[idx].light_params.z * p_blob_intensity, roughness, metallic, specular, rim * omni_attenuation, rim_tint, clearcoat, clearcoat_gloss, anisotropy, diffuse_light, specular_light);
+ light_compute(normal, normalize(light_rel_vec), eye_vec, binormal, tangent, omni_lights[idx].light_color_energy.rgb, light_attenuation, albedo, transmission, omni_lights[idx].light_params.z * p_blob_intensity, roughness, metallic, specular, rim * omni_attenuation, rim_tint, clearcoat, clearcoat_gloss, anisotropy, diffuse_light, specular_light, alpha);
}
-void light_process_spot(int idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 binormal, vec3 tangent, vec3 albedo, vec3 transmission, float roughness, float metallic, float specular, float rim, float rim_tint, float clearcoat, float clearcoat_gloss, float anisotropy, float p_blob_intensity, inout vec3 diffuse_light, inout vec3 specular_light) {
+void light_process_spot(int idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 binormal, vec3 tangent, vec3 albedo, vec3 transmission, float roughness, float metallic, float specular, float rim, float rim_tint, float clearcoat, float clearcoat_gloss, float anisotropy, float p_blob_intensity, inout vec3 diffuse_light, inout vec3 specular_light, inout float alpha) {
vec3 light_rel_vec = spot_lights[idx].light_pos_inv_radius.xyz - vertex;
float light_length = length(light_rel_vec);
@@ -1339,7 +1343,7 @@ void light_process_spot(int idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 bi
}
#endif //SHADOWS_DISABLED
- light_compute(normal, normalize(light_rel_vec), eye_vec, binormal, tangent, spot_lights[idx].light_color_energy.rgb, light_attenuation, albedo, transmission, spot_lights[idx].light_params.z * p_blob_intensity, roughness, metallic, specular, rim * spot_attenuation, rim_tint, clearcoat, clearcoat_gloss, anisotropy, diffuse_light, specular_light);
+ light_compute(normal, normalize(light_rel_vec), eye_vec, binormal, tangent, spot_lights[idx].light_color_energy.rgb, light_attenuation, albedo, transmission, spot_lights[idx].light_params.z * p_blob_intensity, roughness, metallic, specular, rim * spot_attenuation, rim_tint, clearcoat, clearcoat_gloss, anisotropy, diffuse_light, specular_light, alpha);
}
void reflection_process(int idx, vec3 vertex, vec3 normal, vec3 binormal, vec3 tangent, float roughness, float anisotropy, vec3 ambient, vec3 skybox, inout highp vec4 reflection_accum, inout highp vec4 ambient_accum) {
@@ -1705,11 +1709,13 @@ FRAGMENT_SHADER_CODE
/* clang-format on */
}
+#if !defined(USE_SHADOW_TO_OPACITY)
+
#if defined(ALPHA_SCISSOR_USED)
if (alpha < alpha_scissor) {
discard;
}
-#endif
+#endif // ALPHA_SCISSOR_USED
#ifdef USE_OPAQUE_PREPASS
@@ -1717,7 +1723,9 @@ FRAGMENT_SHADER_CODE
discard;
}
-#endif
+#endif // USE_OPAQUE_PREPASS
+
+#endif // !USE_SHADOW_TO_OPACITY
#if defined(ENABLE_NORMALMAP)
@@ -2044,7 +2052,7 @@ FRAGMENT_SHADER_CODE
specular_light *= mix(vec3(1.0), light_attenuation, specular_light_interp.a);
#else
- light_compute(normal, -light_direction_attenuation.xyz, eye_vec, binormal, tangent, light_color_energy.rgb, light_attenuation, albedo, transmission, light_params.z * specular_blob_intensity, roughness, metallic, specular, rim, rim_tint, clearcoat, clearcoat_gloss, anisotropy, diffuse_light, specular_light);
+ light_compute(normal, -light_direction_attenuation.xyz, eye_vec, binormal, tangent, light_color_energy.rgb, light_attenuation, albedo, transmission, light_params.z * specular_blob_intensity, roughness, metallic, specular, rim, rim_tint, clearcoat, clearcoat_gloss, anisotropy, diffuse_light, specular_light, alpha);
#endif
#endif //#USE_LIGHT_DIRECTIONAL
@@ -2057,17 +2065,36 @@ FRAGMENT_SHADER_CODE
#else
for (int i = 0; i < omni_light_count; i++) {
- light_process_omni(omni_light_indices[i], vertex, eye_vec, normal, binormal, tangent, albedo, transmission, roughness, metallic, specular, rim, rim_tint, clearcoat, clearcoat_gloss, anisotropy, specular_blob_intensity, diffuse_light, specular_light);
+ light_process_omni(omni_light_indices[i], vertex, eye_vec, normal, binormal, tangent, albedo, transmission, roughness, metallic, specular, rim, rim_tint, clearcoat, clearcoat_gloss, anisotropy, specular_blob_intensity, diffuse_light, specular_light, alpha);
}
for (int i = 0; i < spot_light_count; i++) {
- light_process_spot(spot_light_indices[i], vertex, eye_vec, normal, binormal, tangent, albedo, transmission, roughness, metallic, specular, rim, rim_tint, clearcoat, clearcoat_gloss, anisotropy, specular_blob_intensity, diffuse_light, specular_light);
+ light_process_spot(spot_light_indices[i], vertex, eye_vec, normal, binormal, tangent, albedo, transmission, roughness, metallic, specular, rim, rim_tint, clearcoat, clearcoat_gloss, anisotropy, specular_blob_intensity, diffuse_light, specular_light, alpha);
}
#endif //USE_VERTEX_LIGHTING
#endif
+#ifdef USE_SHADOW_TO_OPACITY
+ alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0));
+
+#if defined(ALPHA_SCISSOR_USED)
+ if (alpha < alpha_scissor) {
+ discard;
+ }
+#endif // ALPHA_SCISSOR_USED
+
+#ifdef USE_OPAQUE_PREPASS
+
+ if (alpha < opaque_prepass_threshold) {
+ discard;
+ }
+
+#endif // USE_OPAQUE_PREPASS
+
+#endif // USE_SHADOW_TO_OPACITY
+
#ifdef RENDER_DEPTH
//nothing happens, so a tree-ssa optimizer will result in no fragment shader :)
#else
diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp
index 25cdc8d8a0..4de910ee1c 100644
--- a/drivers/unix/os_unix.cpp
+++ b/drivers/unix/os_unix.cpp
@@ -170,7 +170,7 @@ String OS_Unix::get_stdin_string(bool p_block) {
return "";
}
-String OS_Unix::get_name() {
+String OS_Unix::get_name() const {
return "Unix";
}
diff --git a/drivers/unix/os_unix.h b/drivers/unix/os_unix.h
index 6a05877a8c..53446a6b6f 100644
--- a/drivers/unix/os_unix.h
+++ b/drivers/unix/os_unix.h
@@ -76,7 +76,7 @@ public:
virtual Error set_cwd(const String &p_cwd);
- virtual String get_name();
+ virtual String get_name() const;
virtual Date get_date(bool utc) const;
virtual Time get_time(bool utc) const;
diff --git a/editor/SCsub b/editor/SCsub
index 82a4ecb6c0..7d48e47c9f 100644
--- a/editor/SCsub
+++ b/editor/SCsub
@@ -79,9 +79,6 @@ if env['tools']:
env.CommandNoCache('#editor/builtin_fonts.gen.h', flist, run_in_subprocess(editor_builders.make_fonts_header))
env.add_source_files(env.editor_sources, "*.cpp")
- env_thirdparty = env.Clone()
- env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(env.editor_sources, ["#thirdparty/misc/clipper.cpp"])
SConscript('collada/SCsub')
SConscript('doc/SCsub')
diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp
index 6751e58bb2..df481e0855 100644
--- a/editor/editor_export.cpp
+++ b/editor/editor_export.cpp
@@ -615,6 +615,7 @@ void EditorExportPlugin::_bind_methods() {
BIND_VMETHOD(MethodInfo("_export_file", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::STRING, "type"), PropertyInfo(Variant::POOL_STRING_ARRAY, "features")));
BIND_VMETHOD(MethodInfo("_export_begin", PropertyInfo(Variant::POOL_STRING_ARRAY, "features"), PropertyInfo(Variant::BOOL, "is_debug"), PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::INT, "flags")));
+ BIND_VMETHOD(MethodInfo("_export_end"));
}
EditorExportPlugin::EditorExportPlugin() {
diff --git a/editor/editor_fonts.cpp b/editor/editor_fonts.cpp
index fa4172cded..5b16b8f6d5 100644
--- a/editor/editor_fonts.cpp
+++ b/editor/editor_fonts.cpp
@@ -239,6 +239,9 @@ void editor_register_fonts(Ref<Theme> p_theme) {
MAKE_SOURCE_FONT(df_code, int(EditorSettings::get_singleton()->get("interface/editor/code_font_size")) * EDSCALE);
p_theme->set_font("source", "EditorFonts", df_code);
+ MAKE_SOURCE_FONT(df_expression, (int(EditorSettings::get_singleton()->get("interface/editor/code_font_size")) - 1) * EDSCALE);
+ p_theme->set_font("expression", "EditorFonts", df_expression);
+
MAKE_SOURCE_FONT(df_output_code, int(EDITOR_DEF("run/output/font_size", 13)) * EDSCALE);
p_theme->set_font("output_source", "EditorFonts", df_output_code);
diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp
index 173333dac9..2baad8c904 100644
--- a/editor/editor_resource_preview.cpp
+++ b/editor/editor_resource_preview.cpp
@@ -71,7 +71,21 @@ Ref<Texture> EditorResourcePreviewGenerator::generate_from_path(const String &p_
return generate(res, p_size);
}
-bool EditorResourcePreviewGenerator::should_generate_small_preview() const {
+bool EditorResourcePreviewGenerator::generate_small_preview_automatically() const {
+
+ if (get_script_instance() && get_script_instance()->has_method("generate_small_preview_automatically")) {
+ return get_script_instance()->call("generate_small_preview_automatically");
+ }
+
+ return false;
+}
+
+bool EditorResourcePreviewGenerator::can_generate_small_preview() const {
+
+ if (get_script_instance() && get_script_instance()->has_method("can_generate_small_preview")) {
+ return get_script_instance()->call("can_generate_small_preview");
+ }
+
return false;
}
@@ -80,6 +94,8 @@ void EditorResourcePreviewGenerator::_bind_methods() {
ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "handles", PropertyInfo(Variant::STRING, "type")));
ClassDB::add_virtual_method(get_class_static(), MethodInfo(CLASS_INFO(Texture), "generate", PropertyInfo(Variant::OBJECT, "from", PROPERTY_HINT_RESOURCE_TYPE, "Resource"), PropertyInfo(Variant::VECTOR2, "size")));
ClassDB::add_virtual_method(get_class_static(), MethodInfo(CLASS_INFO(Texture), "generate_from_path", PropertyInfo(Variant::STRING, "path", PROPERTY_HINT_FILE), PropertyInfo(Variant::VECTOR2, "size")));
+ ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "generate_small_preview_automatically"));
+ ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "can_generate_small_preview"));
}
EditorResourcePreviewGenerator::EditorResourcePreviewGenerator() {
@@ -154,16 +170,27 @@ void EditorResourcePreview::_generate_preview(Ref<ImageTexture> &r_texture, Ref<
}
r_texture = generated;
- if (r_texture.is_valid() && preview_generators[i]->should_generate_small_preview()) {
- int small_thumbnail_size = EditorNode::get_singleton()->get_theme_base()->get_icon("Object", "EditorIcons")->get_width(); // Kind of a workaround to retrieve the default icon size
- small_thumbnail_size *= EDSCALE;
+ int small_thumbnail_size = EditorNode::get_singleton()->get_theme_base()->get_icon("Object", "EditorIcons")->get_width(); // Kind of a workaround to retrieve the default icon size
+ small_thumbnail_size *= EDSCALE;
+ if (preview_generators[i]->can_generate_small_preview()) {
+ Ref<Texture> generated_small;
+ if (p_item.resource.is_valid()) {
+ generated_small = preview_generators[i]->generate(p_item.resource, Vector2(small_thumbnail_size, small_thumbnail_size));
+ } else {
+ generated_small = preview_generators[i]->generate_from_path(p_item.path, Vector2(small_thumbnail_size, small_thumbnail_size));
+ }
+ r_small_texture = generated_small;
+ }
+
+ if (!r_small_texture.is_valid() && r_texture.is_valid() && preview_generators[i]->generate_small_preview_automatically()) {
Ref<Image> small_image = r_texture->get_data();
small_image = small_image->duplicate();
small_image->resize(small_thumbnail_size, small_thumbnail_size, Image::INTERPOLATE_CUBIC);
r_small_texture.instance();
r_small_texture->create_from_image(small_image);
}
+
break;
}
diff --git a/editor/editor_resource_preview.h b/editor/editor_resource_preview.h
index 9b9223a818..e0fd54c924 100644
--- a/editor/editor_resource_preview.h
+++ b/editor/editor_resource_preview.h
@@ -48,7 +48,8 @@ public:
virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const;
virtual Ref<Texture> generate_from_path(const String &p_path, const Size2 p_size) const;
- virtual bool should_generate_small_preview() const;
+ virtual bool generate_small_preview_automatically() const;
+ virtual bool can_generate_small_preview() const;
EditorResourcePreviewGenerator();
};
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index 0a9436952b..3c67d26828 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -1431,6 +1431,7 @@ void AnimationPlayerEditor::_prepare_onion_layers_2() {
new_state["show_rulers"] = false;
new_state["show_guides"] = false;
new_state["show_helpers"] = false;
+ new_state["show_zoom_control"] = false;
// TODO: Save/restore only affected entries
CanvasItemEditor::get_singleton()->set_state(new_state);
}
@@ -1483,7 +1484,7 @@ void AnimationPlayerEditor::_prepare_onion_layers_2() {
if (valid) {
player->seek(pos, true);
get_tree()->flush_transform_notifications(); // Needed for transforms of Spatials
- values_backup.update_skeletons(); // Needed for Skeletons
+ values_backup.update_skeletons(); // Needed for Skeletons (2D & 3D)
VS::get_singleton()->viewport_set_active(onion.captures[cidx], true);
VS::get_singleton()->viewport_set_parent_viewport(root_vp, onion.captures[cidx]);
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 126cd20c53..92cc12d931 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -4009,6 +4009,7 @@ void CanvasItemEditor::_popup_callback(int p_op) {
show_rulers = !show_rulers;
int idx = view_menu->get_popup()->get_item_index(SHOW_RULERS);
view_menu->get_popup()->set_item_checked(idx, show_rulers);
+ _update_scrollbars();
viewport->update();
} break;
case SHOW_GUIDES: {
@@ -4321,6 +4322,7 @@ void CanvasItemEditor::_popup_callback(int p_op) {
Map<Node *, Object *> &selection = editor_selection->get_selection();
+ undo_redo->create_action(TTR("Create Custom Bone(s) from Node(s)"));
for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) {
Node2D *n2d = Object::cast_to<Node2D>(E->key());
@@ -4330,19 +4332,24 @@ void CanvasItemEditor::_popup_callback(int p_op) {
continue;
if (!n2d->get_parent_item())
continue;
+ if (n2d->has_meta("_edit_bone_") && (bool)n2d->get_meta("_edit_bone_") == true)
+ continue;
- n2d->set_meta("_edit_bone_", true);
- if (!skeleton_show_bones)
- skeleton_menu->get_popup()->activate_item(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES));
+ undo_redo->add_do_method(n2d, "set_meta", "_edit_bone_", true);
+ undo_redo->add_undo_method(n2d, "remove_meta", "_edit_bone_");
}
- _queue_update_bone_list();
- viewport->update();
+ undo_redo->add_do_method(this, "_queue_update_bone_list");
+ undo_redo->add_undo_method(this, "_queue_update_bone_list");
+ undo_redo->add_do_method(viewport, "update");
+ undo_redo->add_undo_method(viewport, "update");
+ undo_redo->commit_action();
} break;
case SKELETON_CLEAR_BONES: {
Map<Node *, Object *> &selection = editor_selection->get_selection();
+ undo_redo->create_action(TTR("Clear Bones"));
for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) {
Node2D *n2d = Object::cast_to<Node2D>(E->key());
@@ -4350,40 +4357,47 @@ void CanvasItemEditor::_popup_callback(int p_op) {
continue;
if (!n2d->is_visible_in_tree())
continue;
+ if (!n2d->has_meta("_edit_bone_"))
+ continue;
- n2d->set_meta("_edit_bone_", Variant());
- if (!skeleton_show_bones)
- skeleton_menu->get_popup()->activate_item(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES));
+ undo_redo->add_do_method(n2d, "remove_meta", "_edit_bone_");
+ undo_redo->add_undo_method(n2d, "set_meta", "_edit_bone_", n2d->get_meta("_edit_bone_"));
}
- _queue_update_bone_list();
- viewport->update();
+ undo_redo->add_do_method(this, "_queue_update_bone_list");
+ undo_redo->add_undo_method(this, "_queue_update_bone_list");
+ undo_redo->add_do_method(viewport, "update");
+ undo_redo->add_undo_method(viewport, "update");
+ undo_redo->commit_action();
} break;
case SKELETON_SET_IK_CHAIN: {
List<Node *> selection = editor_selection->get_selected_node_list();
+ undo_redo->create_action(TTR("Make IK Chain"));
for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->get());
if (!canvas_item || !canvas_item->is_visible_in_tree())
continue;
-
if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root())
continue;
+ if (canvas_item->has_meta("_edit_ik_") && (bool)canvas_item->get_meta("_edit_ik_") == true)
+ continue;
- canvas_item->set_meta("_edit_ik_", true);
- if (!skeleton_show_bones)
- skeleton_menu->get_popup()->activate_item(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES));
+ undo_redo->add_do_method(canvas_item, "set_meta", "_edit_ik_", true);
+ undo_redo->add_undo_method(canvas_item, "remove_meta", "_edit_ik_");
}
-
- viewport->update();
+ undo_redo->add_do_method(viewport, "update");
+ undo_redo->add_undo_method(viewport, "update");
+ undo_redo->commit_action();
} break;
case SKELETON_CLEAR_IK_CHAIN: {
Map<Node *, Object *> &selection = editor_selection->get_selection();
+ undo_redo->create_action(TTR("Clear IK Chain"));
for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) {
CanvasItem *n2d = Object::cast_to<CanvasItem>(E->key());
@@ -4391,12 +4405,15 @@ void CanvasItemEditor::_popup_callback(int p_op) {
continue;
if (!n2d->is_visible_in_tree())
continue;
+ if (!n2d->has_meta("_edit_ik_"))
+ continue;
- n2d->set_meta("_edit_ik_", Variant());
- if (!skeleton_show_bones)
- skeleton_menu->get_popup()->activate_item(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES));
+ undo_redo->add_do_method(n2d, "remove_meta", "_edit_ik_");
+ undo_redo->add_undo_method(n2d, "set_meta", "_edit_ik_", n2d->get_meta("_edit_ik_"));
}
- viewport->update();
+ undo_redo->add_do_method(viewport, "update");
+ undo_redo->add_undo_method(viewport, "update");
+ undo_redo->commit_action();
} break;
}
@@ -4478,6 +4495,7 @@ void CanvasItemEditor::_bind_methods() {
ClassDB::bind_method("_draw_viewport", &CanvasItemEditor::_draw_viewport);
ClassDB::bind_method("_gui_input_viewport", &CanvasItemEditor::_gui_input_viewport);
ClassDB::bind_method("_snap_changed", &CanvasItemEditor::_snap_changed);
+ ClassDB::bind_method("_queue_update_bone_list", &CanvasItemEditor::_update_bone_list);
ClassDB::bind_method("_update_bone_list", &CanvasItemEditor::_update_bone_list);
ClassDB::bind_method("_tree_changed", &CanvasItemEditor::_tree_changed);
ClassDB::bind_method("_selection_changed", &CanvasItemEditor::_selection_changed);
@@ -4514,6 +4532,7 @@ Dictionary CanvasItemEditor::get_state() const {
state["show_rulers"] = show_rulers;
state["show_guides"] = show_guides;
state["show_helpers"] = show_helpers;
+ state["show_zoom_control"] = zoom_hb->is_visible();
state["show_edit_locks"] = show_edit_locks;
state["snap_rotation"] = snap_rotation;
state["snap_relative"] = snap_relative;
@@ -4524,6 +4543,7 @@ Dictionary CanvasItemEditor::get_state() const {
void CanvasItemEditor::set_state(const Dictionary &p_state) {
+ bool update_scrollbars = false;
Dictionary state = p_state;
if (state.has("zoom")) {
zoom = p_state["zoom"];
@@ -4532,7 +4552,7 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) {
if (state.has("ofs")) {
view_offset = p_state["ofs"];
previous_update_view_offset = view_offset;
- _update_scrollbars();
+ update_scrollbars = true;
}
if (state.has("grid_offset")) {
@@ -4620,6 +4640,7 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) {
show_rulers = state["show_rulers"];
int idx = view_menu->get_popup()->get_item_index(SHOW_RULERS);
view_menu->get_popup()->set_item_checked(idx, show_rulers);
+ update_scrollbars = true;
}
if (state.has("show_guides")) {
@@ -4640,6 +4661,11 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) {
view_menu->get_popup()->set_item_checked(idx, show_edit_locks);
}
+ if (state.has("show_zoom_control")) {
+ // This one is not user-controllable, but instrumentable
+ zoom_hb->set_visible(state["show_zoom_control"]);
+ }
+
if (state.has("snap_rotation")) {
snap_rotation = state["snap_rotation"];
int idx = snap_config_menu->get_popup()->get_item_index(SNAP_USE_ROTATION);
@@ -4664,6 +4690,9 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) {
skeleton_menu->get_popup()->set_item_checked(idx, skeleton_show_bones);
}
+ if (update_scrollbars) {
+ _update_scrollbars();
+ }
viewport->update();
}
diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp
index 58d7968723..28e57ac48a 100644
--- a/editor/plugins/editor_preview_plugins.cpp
+++ b/editor/plugins/editor_preview_plugins.cpp
@@ -78,7 +78,7 @@ bool EditorTexturePreviewPlugin::handles(const String &p_type) const {
return ClassDB::is_parent_class(p_type, "Texture");
}
-bool EditorTexturePreviewPlugin::should_generate_small_preview() const {
+bool EditorTexturePreviewPlugin::generate_small_preview_automatically() const {
return true;
}
@@ -186,7 +186,7 @@ Ref<Texture> EditorImagePreviewPlugin::generate(const RES &p_from, const Size2 p
EditorImagePreviewPlugin::EditorImagePreviewPlugin() {
}
-bool EditorImagePreviewPlugin::should_generate_small_preview() const {
+bool EditorImagePreviewPlugin::generate_small_preview_automatically() const {
return true;
}
////////////////////////////////////////////////////////////////////////////
@@ -250,7 +250,7 @@ Ref<Texture> EditorBitmapPreviewPlugin::generate(const RES &p_from, const Size2
return ptex;
}
-bool EditorBitmapPreviewPlugin::should_generate_small_preview() const {
+bool EditorBitmapPreviewPlugin::generate_small_preview_automatically() const {
return true;
}
@@ -317,7 +317,7 @@ bool EditorMaterialPreviewPlugin::handles(const String &p_type) const {
return ClassDB::is_parent_class(p_type, "Material"); //any material
}
-bool EditorMaterialPreviewPlugin::should_generate_small_preview() const {
+bool EditorMaterialPreviewPlugin::generate_small_preview_automatically() const {
return true;
}
diff --git a/editor/plugins/editor_preview_plugins.h b/editor/plugins/editor_preview_plugins.h
index ed2c003a0a..16b1f3082b 100644
--- a/editor/plugins/editor_preview_plugins.h
+++ b/editor/plugins/editor_preview_plugins.h
@@ -39,7 +39,7 @@ class EditorTexturePreviewPlugin : public EditorResourcePreviewGenerator {
GDCLASS(EditorTexturePreviewPlugin, EditorResourcePreviewGenerator)
public:
virtual bool handles(const String &p_type) const;
- virtual bool should_generate_small_preview() const;
+ virtual bool generate_small_preview_automatically() const;
virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const;
EditorTexturePreviewPlugin();
@@ -49,7 +49,7 @@ class EditorImagePreviewPlugin : public EditorResourcePreviewGenerator {
GDCLASS(EditorImagePreviewPlugin, EditorResourcePreviewGenerator)
public:
virtual bool handles(const String &p_type) const;
- virtual bool should_generate_small_preview() const;
+ virtual bool generate_small_preview_automatically() const;
virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const;
EditorImagePreviewPlugin();
@@ -59,7 +59,7 @@ class EditorBitmapPreviewPlugin : public EditorResourcePreviewGenerator {
GDCLASS(EditorBitmapPreviewPlugin, EditorResourcePreviewGenerator)
public:
virtual bool handles(const String &p_type) const;
- virtual bool should_generate_small_preview() const;
+ virtual bool generate_small_preview_automatically() const;
virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const;
EditorBitmapPreviewPlugin();
@@ -98,7 +98,7 @@ protected:
public:
virtual bool handles(const String &p_type) const;
- virtual bool should_generate_small_preview() const;
+ virtual bool generate_small_preview_automatically() const;
virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const;
EditorMaterialPreviewPlugin();
diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp
index 85743c96d6..5b67d259ba 100644
--- a/editor/plugins/theme_editor_plugin.cpp
+++ b/editor/plugins/theme_editor_plugin.cpp
@@ -631,12 +631,14 @@ ThemeEditor::ThemeEditor() {
scroll = memnew(ScrollContainer);
add_child(scroll);
+ scroll->set_theme(Theme::get_default());
scroll->set_enable_v_scroll(true);
scroll->set_enable_h_scroll(false);
scroll->set_v_size_flags(SIZE_EXPAND_FILL);
main_container = memnew(MarginContainer);
scroll->add_child(main_container);
+ main_container->set_theme(Theme::get_default());
main_container->set_clip_contents(true);
main_container->set_custom_minimum_size(Size2(700, 0) * EDSCALE);
main_container->set_v_size_flags(SIZE_EXPAND_FILL);
@@ -646,11 +648,9 @@ ThemeEditor::ThemeEditor() {
Panel *panel = memnew(Panel);
main_container->add_child(panel);
- panel->set_theme(Theme::get_default());
MarginContainer *mc = memnew(MarginContainer);
main_container->add_child(mc);
- mc->set_theme(Theme::get_default());
mc->add_constant_override("margin_right", 4 * EDSCALE);
mc->add_constant_override("margin_top", 4 * EDSCALE);
mc->add_constant_override("margin_left", 4 * EDSCALE);
@@ -879,11 +879,9 @@ ThemeEditor::ThemeEditor() {
void ThemeEditorPlugin::edit(Object *p_node) {
if (Object::cast_to<Theme>(p_node)) {
- theme_editor->show();
theme_editor->edit(Object::cast_to<Theme>(p_node));
} else {
theme_editor->edit(Ref<Theme>());
- theme_editor->hide();
}
}
@@ -898,11 +896,11 @@ void ThemeEditorPlugin::make_visible(bool p_visible) {
theme_editor->set_process(true);
button->show();
editor->make_bottom_panel_item_visible(theme_editor);
-
} else {
theme_editor->set_process(false);
if (theme_editor->is_visible_in_tree())
editor->hide_bottom_panel();
+
button->hide();
}
}
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index ad698be0a3..2c68633a3c 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -358,7 +358,9 @@ void VisualShaderEditor::_update_graph() {
for (int i = 0; i < graph->get_child_count(); i++) {
if (Object::cast_to<GraphNode>(graph->get_child(i))) {
- memdelete(graph->get_child(i));
+ Node *node = graph->get_child(i);
+ graph->remove_child(node);
+ memdelete(node);
i--;
}
}
@@ -377,13 +379,33 @@ void VisualShaderEditor::_update_graph() {
Vector<int> nodes = visual_shader->get_node_list(type);
+ Control *offset;
+
for (int n_i = 0; n_i < nodes.size(); n_i++) {
Vector2 position = visual_shader->get_node_position(type, nodes[n_i]);
Ref<VisualShaderNode> vsnode = visual_shader->get_node(type, nodes[n_i]);
+ Ref<VisualShaderNodeGroupBase> group_node = Object::cast_to<VisualShaderNodeGroupBase>(vsnode.ptr());
+ bool is_group = !group_node.is_null();
+ Size2 size = Size2(0, 0);
+
+ Ref<VisualShaderNodeExpression> expression_node = Object::cast_to<VisualShaderNodeExpression>(group_node.ptr());
+ bool is_expression = !expression_node.is_null();
+ String expression = "";
+
GraphNode *node = memnew(GraphNode);
+ if (is_group) {
+ size = group_node->get_size();
+
+ node->set_resizable(true);
+ node->connect("resize_request", this, "_node_resized", varray((int)type, nodes[n_i]));
+ }
+ if (is_expression) {
+ expression = expression_node->get_expression();
+ }
+
/*if (!vsnode->is_connected("changed", this, "_node_changed")) {
vsnode->connect("changed", this, "_node_changed", varray(vsnode->get_instance_id()), CONNECT_DEFERRED);
}*/
@@ -403,6 +425,10 @@ void VisualShaderEditor::_update_graph() {
Control *custom_editor = NULL;
int port_offset = 0;
+ if (is_group) {
+ port_offset++;
+ }
+
Ref<VisualShaderNodeUniform> uniform = vsnode;
if (uniform.is_valid()) {
graph->add_child(node);
@@ -438,6 +464,24 @@ void VisualShaderEditor::_update_graph() {
custom_editor = NULL;
}
+ if (is_group) {
+ HBoxContainer *hb2 = memnew(HBoxContainer);
+
+ Button *add_input_btn = memnew(Button);
+ add_input_btn->set_text(TTR("Add input +"));
+ add_input_btn->connect("pressed", this, "_add_input_port", varray(nodes[n_i], group_node->get_free_input_port_id(), VisualShaderNode::PORT_TYPE_VECTOR, "input" + itos(group_node->get_free_input_port_id())), CONNECT_DEFERRED);
+ hb2->add_child(add_input_btn);
+
+ hb2->add_spacer();
+
+ Button *add_output_btn = memnew(Button);
+ add_output_btn->set_text(TTR("Add output +"));
+ add_output_btn->connect("pressed", this, "_add_output_port", varray(nodes[n_i], group_node->get_free_output_port_id(), VisualShaderNode::PORT_TYPE_VECTOR, "output" + itos(group_node->get_free_output_port_id())), CONNECT_DEFERRED);
+ hb2->add_child(add_output_btn);
+
+ node->add_child(hb2);
+ }
+
for (int i = 0; i < MAX(vsnode->get_input_port_count(), vsnode->get_output_port_count()); i++) {
if (vsnode->is_port_separator(i)) {
@@ -507,21 +551,75 @@ void VisualShaderEditor::_update_graph() {
if (valid_left) {
- Label *label = memnew(Label);
- label->set_text(name_left);
- label->add_style_override("normal", label_style); //more compact
- hb->add_child(label);
+ if (is_group) {
+
+ OptionButton *type_box = memnew(OptionButton);
+ hb->add_child(type_box);
+ type_box->add_item(TTR("Scalar"));
+ type_box->add_item(TTR("Vector"));
+ type_box->add_item(TTR("Boolean"));
+ type_box->add_item(TTR("Transform"));
+ type_box->select(group_node->get_input_port_type(i));
+ type_box->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
+ type_box->connect("item_selected", this, "_change_input_port_type", varray(nodes[n_i], i), CONNECT_DEFERRED);
+
+ LineEdit *name_box = memnew(LineEdit);
+ hb->add_child(name_box);
+ name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0));
+ name_box->set_text(name_left);
+ name_box->set_expand_to_text_length(true);
+ name_box->connect("text_entered", this, "_change_input_port_name", varray(name_box, nodes[n_i], i));
+ name_box->connect("focus_exited", this, "_port_name_focus_out", varray(name_box, nodes[n_i], i, false));
+
+ if (is_group) {
+ Button *remove_btn = memnew(Button);
+ remove_btn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Remove", "EditorIcons"));
+ remove_btn->set_tooltip(TTR("Remove") + " " + name_left);
+ remove_btn->connect("pressed", this, "_remove_input_port", varray(nodes[n_i], i), CONNECT_DEFERRED);
+ hb->add_child(remove_btn);
+ }
+ } else {
+
+ Label *label = memnew(Label);
+ label->set_text(name_left);
+ label->add_style_override("normal", label_style); //more compact
+ hb->add_child(label);
+ }
}
hb->add_spacer();
if (valid_right) {
-
- Label *label = memnew(Label);
- label->set_text(name_right);
- label->set_align(Label::ALIGN_RIGHT);
- label->add_style_override("normal", label_style); //more compact
- hb->add_child(label);
+ if (is_group) {
+ Button *remove_btn = memnew(Button);
+ remove_btn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Remove", "EditorIcons"));
+ remove_btn->set_tooltip(TTR("Remove") + " " + name_left);
+ remove_btn->connect("pressed", this, "_remove_output_port", varray(nodes[n_i], i), CONNECT_DEFERRED);
+ hb->add_child(remove_btn);
+
+ LineEdit *name_box = memnew(LineEdit);
+ hb->add_child(name_box);
+ name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0));
+ name_box->set_text(name_right);
+ name_box->set_expand_to_text_length(true);
+ name_box->connect("text_entered", this, "_change_output_port_name", varray(name_box, nodes[n_i], i));
+ name_box->connect("focus_exited", this, "_port_name_focus_out", varray(name_box, nodes[n_i], i, true));
+
+ OptionButton *type_box = memnew(OptionButton);
+ hb->add_child(type_box);
+ type_box->add_item(TTR("Scalar"));
+ type_box->add_item(TTR("Vector"));
+ type_box->add_item(TTR("Boolean"));
+ type_box->add_item(TTR("Transform"));
+ type_box->select(group_node->get_output_port_type(i));
+ type_box->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
+ type_box->connect("item_selected", this, "_change_output_port_type", varray(nodes[n_i], i), CONNECT_DEFERRED);
+ } else {
+ Label *label = memnew(Label);
+ label->set_text(name_right);
+ label->add_style_override("normal", label_style); //more compact
+ hb->add_child(label);
+ }
}
}
@@ -540,18 +638,33 @@ void VisualShaderEditor::_update_graph() {
hb->add_child(preview);
}
+ if (is_group) {
+ offset = memnew(Control);
+ offset->set_custom_minimum_size(Size2(0, 5 * EDSCALE));
+ node->add_child(offset);
+ port_offset++;
+ }
+
node->add_child(hb);
node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], valid_right, port_right, type_color[port_right]);
}
if (vsnode->get_output_port_for_preview() >= 0 && vsnode->get_output_port_type(vsnode->get_output_port_for_preview()) != VisualShaderNode::PORT_TYPE_TRANSFORM) {
+ offset = memnew(Control);
+ offset->set_custom_minimum_size(Size2(0, 5 * EDSCALE));
+ node->add_child(offset);
+
VisualShaderNodePortPreview *port_preview = memnew(VisualShaderNodePortPreview);
port_preview->setup(visual_shader, type, nodes[n_i], vsnode->get_output_port_for_preview());
port_preview->set_h_size_flags(SIZE_SHRINK_CENTER);
node->add_child(port_preview);
}
+ offset = memnew(Control);
+ offset->set_custom_minimum_size(Size2(0, 5 * EDSCALE));
+ node->add_child(offset);
+
String error = vsnode->get_warning(visual_shader->get_mode(), type);
if (error != String()) {
Label *error_label = memnew(Label);
@@ -560,9 +673,42 @@ void VisualShaderEditor::_update_graph() {
node->add_child(error_label);
}
+ if (is_expression) {
+
+ TextEdit *expression_box = memnew(TextEdit);
+ expression_node->set_control(expression_box, 0);
+ node->add_child(expression_box);
+
+ Color text_color = EDITOR_GET("text_editor/highlighting/text_color");
+ Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color");
+ Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color");
+ Color symbol_color = EDITOR_GET("text_editor/highlighting/symbol_color");
+
+ expression_box->set_syntax_coloring(true);
+
+ for (List<String>::Element *E = keyword_list.front(); E; E = E->next()) {
+
+ expression_box->add_keyword_color(E->get(), keyword_color);
+ }
+
+ expression_box->add_font_override("font", get_font("expression", "EditorFonts"));
+ expression_box->add_color_override("font_color", text_color);
+ expression_box->add_color_override("symbol_color", symbol_color);
+ expression_box->add_color_region("/*", "*/", comment_color, false);
+ expression_box->add_color_region("//", "", comment_color, false);
+
+ expression_box->set_text(expression);
+ expression_box->set_context_menu_enabled(false);
+ expression_box->set_show_line_numbers(true);
+
+ expression_box->connect("focus_exited", this, "_expression_focus_out", varray(expression_box, nodes[n_i]));
+ }
+
if (!uniform.is_valid()) {
graph->add_child(node);
_update_created_node(node);
+ if (is_group)
+ call_deferred("_set_node_size", (int)type, nodes[n_i], size);
}
}
@@ -577,6 +723,285 @@ void VisualShaderEditor::_update_graph() {
}
}
+void VisualShaderEditor::_add_input_port(int p_node, int p_port, int p_port_type, const String &p_name) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+ Ref<VisualShaderNodeExpression> node = visual_shader->get_node(type, p_node);
+ if (node.is_null()) {
+ return;
+ }
+
+ undo_redo->create_action(TTR("Add input port"));
+ undo_redo->add_do_method(node.ptr(), "add_input_port", p_port, p_port_type, p_name);
+ undo_redo->add_undo_method(node.ptr(), "remove_input_port", p_port);
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->add_do_method(this, "_rebuild");
+ undo_redo->add_undo_method(this, "_rebuild");
+ undo_redo->commit_action();
+}
+
+void VisualShaderEditor::_add_output_port(int p_node, int p_port, int p_port_type, const String &p_name) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+ Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node);
+ if (node.is_null()) {
+ return;
+ }
+
+ undo_redo->create_action(TTR("Add output port"));
+ undo_redo->add_do_method(node.ptr(), "add_output_port", p_port, p_port_type, p_name);
+ undo_redo->add_undo_method(node.ptr(), "remove_output_port", p_port);
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->add_do_method(this, "_rebuild");
+ undo_redo->add_undo_method(this, "_rebuild");
+ undo_redo->commit_action();
+}
+
+void VisualShaderEditor::_change_input_port_type(int p_type, int p_node, int p_port) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+ Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node);
+ if (node.is_null()) {
+ return;
+ }
+
+ undo_redo->create_action(TTR("Change input port type"));
+ undo_redo->add_do_method(node.ptr(), "set_input_port_type", p_port, p_type);
+ undo_redo->add_undo_method(node.ptr(), "set_input_port_type", p_port, node->get_input_port_type(p_port));
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->add_do_method(this, "_rebuild");
+ undo_redo->add_undo_method(this, "_rebuild");
+ undo_redo->commit_action();
+}
+
+void VisualShaderEditor::_change_output_port_type(int p_type, int p_node, int p_port) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+ Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node);
+ if (node.is_null()) {
+ return;
+ }
+
+ undo_redo->create_action(TTR("Change output port type"));
+ undo_redo->add_do_method(node.ptr(), "set_output_port_type", p_port, p_type);
+ undo_redo->add_undo_method(node.ptr(), "set_output_port_type", p_port, node->get_output_port_type(p_port));
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->add_do_method(this, "_rebuild");
+ undo_redo->add_undo_method(this, "_rebuild");
+ undo_redo->commit_action();
+}
+
+void VisualShaderEditor::_change_input_port_name(const String &p_text, Object *line_edit, int p_node_id, int p_port_id) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+
+ Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node_id);
+ ERR_FAIL_COND(!node.is_valid());
+
+ undo_redo->create_action(TTR("Change input port name"));
+ undo_redo->add_do_method(node.ptr(), "set_input_port_name", p_port_id, p_text);
+ undo_redo->add_undo_method(node.ptr(), "set_input_port_name", p_port_id, node->get_input_port_name(p_port_id));
+ undo_redo->add_do_method(this, "_rebuild");
+ undo_redo->add_undo_method(this, "_rebuild");
+ undo_redo->commit_action();
+}
+
+void VisualShaderEditor::_change_output_port_name(const String &p_text, Object *line_edit, int p_node_id, int p_port_id) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+
+ Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node_id);
+ ERR_FAIL_COND(!node.is_valid());
+
+ undo_redo->create_action(TTR("Change output port name"));
+ undo_redo->add_do_method(node.ptr(), "set_output_port_name", p_port_id, p_text);
+ undo_redo->add_undo_method(node.ptr(), "set_output_port_name", p_port_id, node->get_output_port_name(p_port_id));
+ undo_redo->add_do_method(this, "_rebuild");
+ undo_redo->add_undo_method(this, "_rebuild");
+ undo_redo->commit_action();
+}
+
+void VisualShaderEditor::_remove_input_port(int p_node, int p_port) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+ Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node);
+ if (node.is_null()) {
+ return;
+ }
+
+ undo_redo->create_action(TTR("Remove input port"));
+
+ List<VisualShader::Connection> conns;
+ visual_shader->get_node_connections(type, &conns);
+ for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) {
+
+ int from_node = E->get().from_node;
+ int from_port = E->get().from_port;
+ int to_node = E->get().to_node;
+ int to_port = E->get().to_port;
+
+ if (to_node == p_node) {
+ if (to_port == p_port) {
+ undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port);
+ undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port, to_node, to_port);
+ } else if (to_port > p_port) {
+ undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port);
+ undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port, to_node, to_port);
+
+ undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port, to_node, to_port - 1);
+ undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port - 1);
+ }
+ }
+ }
+
+ undo_redo->add_do_method(node.ptr(), "remove_input_port", p_port);
+ undo_redo->add_undo_method(node.ptr(), "add_input_port", p_port, (int)node->get_input_port_type(p_port), node->get_input_port_name(p_port));
+
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+
+ undo_redo->add_do_method(this, "_rebuild");
+ undo_redo->add_undo_method(this, "_rebuild");
+
+ undo_redo->commit_action();
+}
+
+void VisualShaderEditor::_remove_output_port(int p_node, int p_port) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+ Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node);
+ if (node.is_null()) {
+ return;
+ }
+
+ undo_redo->create_action(TTR("Remove output port"));
+
+ List<VisualShader::Connection> conns;
+ visual_shader->get_node_connections(type, &conns);
+ for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) {
+
+ int from_node = E->get().from_node;
+ int from_port = E->get().from_port;
+ int to_node = E->get().to_node;
+ int to_port = E->get().to_port;
+
+ if (from_node == p_node) {
+ if (from_port == p_port) {
+ undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port);
+ undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port, to_node, to_port);
+ } else if (from_port > p_port) {
+ undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port);
+ undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port, to_node, to_port);
+
+ undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port - 1, to_node, to_port);
+ undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port - 1, to_node, to_port);
+ }
+ }
+ }
+
+ undo_redo->add_do_method(node.ptr(), "remove_output_port", p_port);
+ undo_redo->add_undo_method(node.ptr(), "add_output_port", p_port, (int)node->get_output_port_type(p_port), node->get_output_port_name(p_port));
+
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+
+ undo_redo->add_do_method(this, "_rebuild");
+ undo_redo->add_undo_method(this, "_rebuild");
+
+ undo_redo->commit_action();
+}
+
+void VisualShaderEditor::_expression_focus_out(Object *text_edit, int p_node) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+ Ref<VisualShaderNodeExpression> node = visual_shader->get_node(type, p_node);
+ if (node.is_null()) {
+ return;
+ }
+
+ TextEdit *expression_box = Object::cast_to<TextEdit>(text_edit);
+
+ if (node->get_expression() == expression_box->get_text())
+ return;
+
+ undo_redo->create_action(TTR("Set expression"));
+ undo_redo->add_do_method(node.ptr(), "set_expression", expression_box->get_text());
+ undo_redo->add_undo_method(node.ptr(), "set_expression", node->get_expression());
+ undo_redo->add_do_method(this, "_rebuild");
+ undo_redo->add_undo_method(this, "_rebuild");
+ undo_redo->commit_action();
+}
+
+void VisualShaderEditor::_rebuild() {
+ EditorNode::get_singleton()->get_log()->clear();
+ visual_shader->rebuild();
+}
+
+void VisualShaderEditor::_set_node_size(int p_type, int p_node, const Vector2 &p_size) {
+
+ VisualShader::Type type = VisualShader::Type(p_type);
+ Ref<VisualShaderNode> node = visual_shader->get_node(type, p_node);
+ if (node.is_null()) {
+ return;
+ }
+
+ Ref<VisualShaderNodeGroupBase> group_node = Object::cast_to<VisualShaderNodeGroupBase>(node.ptr());
+
+ if (group_node.is_null()) {
+ return;
+ }
+
+ Vector2 size = p_size;
+
+ group_node->set_size(size);
+
+ GraphNode *gn = NULL;
+ if (edit_type->get_selected() == p_type) { // check - otherwise the error will be emitted
+ Node *node2 = graph->get_node(itos(p_node));
+ gn = Object::cast_to<GraphNode>(node2);
+ if (!gn)
+ return;
+
+ gn->set_custom_minimum_size(size);
+ gn->set_size(Size2(1, 1));
+ }
+
+ Ref<VisualShaderNodeExpression> expression_node = Object::cast_to<VisualShaderNodeExpression>(node.ptr());
+ if (!expression_node.is_null()) {
+ Control *text_box = expression_node->get_control(0);
+ Size2 box_size = size;
+ if (gn != NULL) {
+ if (box_size.x < 150 * EDSCALE || box_size.y < 0) {
+ box_size.x = gn->get_size().x;
+ }
+ }
+ box_size.x -= text_box->get_margin(MARGIN_LEFT);
+ box_size.x -= 28 * EDSCALE;
+ box_size.y -= text_box->get_margin(MARGIN_TOP);
+ box_size.y -= 28 * EDSCALE;
+ text_box->set_custom_minimum_size(Size2(box_size.x, box_size.y));
+ text_box->set_size(Size2(1, 1));
+ }
+}
+
+void VisualShaderEditor::_node_resized(const Vector2 &p_new_size, int p_type, int p_node) {
+
+ VisualShader::Type type = VisualShader::Type(p_type);
+ Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node);
+ if (node.is_null()) {
+ return;
+ }
+
+ undo_redo->create_action(TTR("Resize VisualShader node"), UndoRedo::MERGE_ENDS);
+ undo_redo->add_do_method(this, "_set_node_size", p_type, p_node, p_new_size / EDSCALE);
+ undo_redo->add_undo_method(this, "_set_node_size", p_type, p_node, node->get_size());
+ undo_redo->commit_action();
+}
+
void VisualShaderEditor::_preview_select_port(int p_node, int p_port) {
VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
@@ -623,6 +1048,52 @@ void VisualShaderEditor::_line_edit_focus_out(Object *line_edit, int p_node_id)
_line_edit_changed(text, line_edit, p_node_id);
}
+void VisualShaderEditor::_port_name_focus_out(Object *line_edit, int p_node_id, int p_port_id, bool p_output) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+
+ Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node_id);
+ ERR_FAIL_COND(!node.is_valid());
+
+ String text = Object::cast_to<LineEdit>(line_edit)->get_text();
+
+ if (!p_output) {
+ if (node->get_input_port_name(p_port_id) == text)
+ return;
+ } else {
+ if (node->get_output_port_name(p_port_id) == text)
+ return;
+ }
+
+ List<String> input_names;
+ List<String> output_names;
+
+ for (int i = 0; i < node->get_input_port_count(); i++) {
+ if (!p_output && i == p_port_id) continue;
+ input_names.push_back(node->get_input_port_name(i));
+ }
+ for (int i = 0; i < node->get_output_port_count(); i++) {
+ if (p_output && i == p_port_id) continue;
+ output_names.push_back(node->get_output_port_name(i));
+ }
+
+ String validated_name = visual_shader->validate_port_name(text, input_names, output_names);
+ if (validated_name == "") {
+ if (!p_output) {
+ Object::cast_to<LineEdit>(line_edit)->set_text(node->get_input_port_name(p_port_id));
+ } else {
+ Object::cast_to<LineEdit>(line_edit)->set_text(node->get_output_port_name(p_port_id));
+ }
+ return;
+ }
+
+ if (!p_output) {
+ _change_input_port_name(validated_name, line_edit, p_node_id, p_port_id);
+ } else {
+ _change_output_port_name(validated_name, line_edit, p_node_id, p_port_id);
+ }
+}
+
void VisualShaderEditor::_port_edited() {
VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
@@ -757,6 +1228,12 @@ void VisualShaderEditor::_add_node(int p_idx, int p_op_idx) {
undo_redo->create_action(TTR("Add Node to Visual Shader"));
undo_redo->add_do_method(visual_shader.ptr(), "add_node", type, vsnode, position, id_to_use);
undo_redo->add_undo_method(visual_shader.ptr(), "remove_node", type, id_to_use);
+
+ VisualShaderNodeExpression *expr = Object::cast_to<VisualShaderNodeExpression>(vsnode.ptr());
+ if (expr) {
+ undo_redo->add_do_method(expr, "set_size", Size2(250 * EDSCALE, 150 * EDSCALE));
+ }
+
undo_redo->add_do_method(this, "_update_graph");
undo_redo->add_undo_method(this, "_update_graph");
undo_redo->commit_action();
@@ -831,10 +1308,25 @@ void VisualShaderEditor::_connection_to_empty(const String &p_from, int p_from_s
void VisualShaderEditor::_delete_request(int which) {
VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+ Ref<VisualShaderNode> node = Ref<VisualShaderNode>(visual_shader->get_node(type, which));
undo_redo->create_action(TTR("Delete Node"));
undo_redo->add_do_method(visual_shader.ptr(), "remove_node", type, which);
- undo_redo->add_undo_method(visual_shader.ptr(), "add_node", type, visual_shader->get_node(type, which), visual_shader->get_node_position(type, which), which);
+ undo_redo->add_undo_method(visual_shader.ptr(), "add_node", type, node, visual_shader->get_node_position(type, which), which);
+
+ // restore size, inputs and outputs if node is group
+ VisualShaderNodeGroupBase *group = Object::cast_to<VisualShaderNodeGroupBase>(node.ptr());
+ if (group) {
+ undo_redo->add_undo_method(group, "set_size", group->get_size());
+ undo_redo->add_undo_method(group, "set_inputs", group->get_inputs());
+ undo_redo->add_undo_method(group, "set_outputs", group->get_outputs());
+ }
+
+ // restore expression text if node is expression
+ VisualShaderNodeExpression *expression = Object::cast_to<VisualShaderNodeExpression>(node.ptr());
+ if (expression) {
+ undo_redo->add_undo_method(expression, "set_expression", expression->get_expression());
+ }
List<VisualShader::Connection> conns;
visual_shader->get_node_connections(type, &conns);
@@ -1022,6 +1514,19 @@ void VisualShaderEditor::_duplicate_nodes() {
undo_redo->add_do_method(visual_shader.ptr(), "add_node", type, dupli, visual_shader->get_node_position(type, E->get()) + Vector2(10, 10) * EDSCALE, id_from);
undo_redo->add_undo_method(visual_shader.ptr(), "remove_node", type, id_from);
+ // duplicate size, inputs and outputs if node is group
+ Ref<VisualShaderNodeGroupBase> group = Object::cast_to<VisualShaderNodeGroupBase>(node.ptr());
+ if (!group.is_null()) {
+ undo_redo->add_do_method(dupli.ptr(), "set_size", group->get_size());
+ undo_redo->add_do_method(dupli.ptr(), "set_inputs", group->get_inputs());
+ undo_redo->add_do_method(dupli.ptr(), "set_outputs", group->get_outputs());
+ }
+ // duplicate expression text if node is expression
+ Ref<VisualShaderNodeExpression> expression = Object::cast_to<VisualShaderNodeExpression>(node.ptr());
+ if (!expression.is_null()) {
+ undo_redo->add_do_method(dupli.ptr(), "set_expression", expression->get_expression());
+ }
+
id_from++;
}
@@ -1030,7 +1535,7 @@ void VisualShaderEditor::_duplicate_nodes() {
for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) {
if (connection_remap.has(E->get().from_node) && connection_remap.has(E->get().to_node)) {
- undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", type, connection_remap[E->get().from_node], E->get().from_port, connection_remap[E->get().to_node], E->get().to_port);
+ undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes_forced", type, connection_remap[E->get().from_node], E->get().from_port, connection_remap[E->get().to_node], E->get().to_port);
}
}
@@ -1073,8 +1578,25 @@ void VisualShaderEditor::_on_nodes_delete() {
undo_redo->create_action(TTR("Delete Nodes"));
for (List<int>::Element *F = to_erase.front(); F; F = F->next()) {
+
+ Ref<VisualShaderNode> node = visual_shader->get_node(type, F->get());
+
undo_redo->add_do_method(visual_shader.ptr(), "remove_node", type, F->get());
- undo_redo->add_undo_method(visual_shader.ptr(), "add_node", type, visual_shader->get_node(type, F->get()), visual_shader->get_node_position(type, F->get()), F->get());
+ undo_redo->add_undo_method(visual_shader.ptr(), "add_node", type, node, visual_shader->get_node_position(type, F->get()), F->get());
+
+ // restore size, inputs and outputs if node is group
+ VisualShaderNodeGroupBase *group = Object::cast_to<VisualShaderNodeGroupBase>(node.ptr());
+ if (group) {
+ undo_redo->add_undo_method(group, "set_size", group->get_size());
+ undo_redo->add_undo_method(group, "set_inputs", group->get_inputs());
+ undo_redo->add_undo_method(group, "set_outputs", group->get_outputs());
+ }
+
+ // restore expression text if node is expression
+ VisualShaderNodeExpression *expression = Object::cast_to<VisualShaderNodeExpression>(node.ptr());
+ if (expression) {
+ undo_redo->add_undo_method(expression, "set_expression", expression->get_expression());
+ }
}
List<VisualShader::Connection> conns;
@@ -1267,8 +1789,10 @@ void VisualShaderEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
}
void VisualShaderEditor::_bind_methods() {
+ ClassDB::bind_method("_rebuild", &VisualShaderEditor::_rebuild);
ClassDB::bind_method("_update_graph", &VisualShaderEditor::_update_graph);
ClassDB::bind_method("_update_options_menu", &VisualShaderEditor::_update_options_menu);
+ ClassDB::bind_method("_expression_focus_out", &VisualShaderEditor::_expression_focus_out);
ClassDB::bind_method("_add_node", &VisualShaderEditor::_add_node);
ClassDB::bind_method("_node_dragged", &VisualShaderEditor::_node_dragged);
ClassDB::bind_method("_connection_request", &VisualShaderEditor::_connection_request);
@@ -1283,11 +1807,22 @@ void VisualShaderEditor::_bind_methods() {
ClassDB::bind_method("_connection_to_empty", &VisualShaderEditor::_connection_to_empty);
ClassDB::bind_method("_line_edit_focus_out", &VisualShaderEditor::_line_edit_focus_out);
ClassDB::bind_method("_line_edit_changed", &VisualShaderEditor::_line_edit_changed);
+ ClassDB::bind_method("_port_name_focus_out", &VisualShaderEditor::_port_name_focus_out);
ClassDB::bind_method("_duplicate_nodes", &VisualShaderEditor::_duplicate_nodes);
ClassDB::bind_method("_mode_selected", &VisualShaderEditor::_mode_selected);
ClassDB::bind_method("_input_select_item", &VisualShaderEditor::_input_select_item);
ClassDB::bind_method("_preview_select_port", &VisualShaderEditor::_preview_select_port);
ClassDB::bind_method("_graph_gui_input", &VisualShaderEditor::_graph_gui_input);
+ ClassDB::bind_method("_add_input_port", &VisualShaderEditor::_add_input_port);
+ ClassDB::bind_method("_change_input_port_type", &VisualShaderEditor::_change_input_port_type);
+ ClassDB::bind_method("_change_input_port_name", &VisualShaderEditor::_change_input_port_name);
+ ClassDB::bind_method("_remove_input_port", &VisualShaderEditor::_remove_input_port);
+ ClassDB::bind_method("_add_output_port", &VisualShaderEditor::_add_output_port);
+ ClassDB::bind_method("_change_output_port_type", &VisualShaderEditor::_change_output_port_type);
+ ClassDB::bind_method("_change_output_port_name", &VisualShaderEditor::_change_output_port_name);
+ ClassDB::bind_method("_remove_output_port", &VisualShaderEditor::_remove_output_port);
+ ClassDB::bind_method("_node_resized", &VisualShaderEditor::_node_resized);
+ ClassDB::bind_method("_set_node_size", &VisualShaderEditor::_set_node_size);
ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &VisualShaderEditor::get_drag_data_fw);
ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &VisualShaderEditor::can_drop_data_fw);
@@ -1311,6 +1846,7 @@ VisualShaderEditor::VisualShaderEditor() {
updating = false;
saved_node_pos_dirty = false;
saved_node_pos = Point2(0, 0);
+ ShaderLanguage::get_keyword_list(&keyword_list);
graph = memnew(GraphEdit);
add_child(graph);
@@ -1710,6 +2246,7 @@ VisualShaderEditor::VisualShaderEditor() {
// SPECIAL
+ add_options.push_back(AddOption("Expression", "Special", "", "VisualShaderNodeExpression", TTR("Custom Godot Shader Language expression, with custom amount of input and output ports. This is a direct injection of code into the vertex/fragment/light function, do not use it to write the function declarations inside.")));
add_options.push_back(AddOption("Fresnel", "Special", "", "VisualShaderNodeFresnel", TTR("Returns falloff based on the dot product of surface normal and view direction of camera (pass associated inputs to it)."), -1, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("ScalarDerivativeFunc", "Special", "Common", "VisualShaderNodeScalarDerivativeFunc", TTR("(GLES3 only) (Fragment/Light mode only) Scalar derivative function."), -1, VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT));
diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h
index eb0dee7594..e851030ab4 100644
--- a/editor/plugins/visual_shader_editor_plugin.h
+++ b/editor/plugins/visual_shader_editor_plugin.h
@@ -128,6 +128,7 @@ class VisualShaderEditor : public VBoxContainer {
};
Vector<AddOption> add_options;
+ List<String> keyword_list;
void _draw_color_over_button(Object *obj, Color p_color);
@@ -160,14 +161,32 @@ class VisualShaderEditor : public VBoxContainer {
void _line_edit_changed(const String &p_text, Object *line_edit, int p_node_id);
void _line_edit_focus_out(Object *line_edit, int p_node_id);
+ void _port_name_focus_out(Object *line_edit, int p_node_id, int p_port_id, bool p_output);
+
void _duplicate_nodes();
Vector<Ref<VisualShaderNodePlugin> > plugins;
void _mode_selected(int p_id);
+ void _rebuild();
void _input_select_item(Ref<VisualShaderNodeInput> input, String name);
+ void _add_input_port(int p_node, int p_port, int p_type, const String &p_name);
+ void _remove_input_port(int p_node, int p_port);
+ void _change_input_port_type(int p_type, int p_node, int p_port);
+ void _change_input_port_name(const String &p_text, Object *line_edit, int p_node, int p_port);
+
+ void _add_output_port(int p_node, int p_port, int p_type, const String &p_name);
+ void _remove_output_port(int p_node, int p_port);
+ void _change_output_port_type(int p_type, int p_node, int p_port);
+ void _change_output_port_name(const String &p_text, Object *line_edit, int p_node, int p_port);
+
+ void _expression_focus_out(Object *text_edit, int p_node);
+
+ void _set_node_size(int p_type, int p_node, const Size2 &p_size);
+ void _node_resized(const Vector2 &p_new_size, int p_type, int p_node);
+
void _preview_select_port(int p_node, int p_port);
void _graph_gui_input(const Ref<InputEvent> p_event);
diff --git a/editor/project_export.cpp b/editor/project_export.cpp
index e1950c666c..cc110f309c 100644
--- a/editor/project_export.cpp
+++ b/editor/project_export.cpp
@@ -571,9 +571,8 @@ void ProjectExportDialog::_duplicate_preset() {
Ref<EditorExportPreset> preset = current->get_platform()->create_preset();
ERR_FAIL_COND(!preset.is_valid());
- String name = current->get_name() + "" + itos(1);
+ String name = current->get_name() + " (copy)";
bool make_runnable = true;
- int attempt = 2;
while (true) {
bool valid = true;
@@ -592,8 +591,7 @@ void ProjectExportDialog::_duplicate_preset() {
if (valid)
break;
- attempt++;
- name = current->get_name() + " " + itos(attempt);
+ name += " (copy)";
}
preset->set_name(name);
diff --git a/modules/gdnative/config.py b/modules/gdnative/config.py
index fde7f1a6e0..7898de5523 100644
--- a/modules/gdnative/config.py
+++ b/modules/gdnative/config.py
@@ -16,7 +16,8 @@ def get_doc_classes():
"ResourceFormatLoaderVideoStreamGDNative",
"StreamPeerGDNative",
"VideoStreamGDNative",
- "WebRTCPeerGDNative",
+ "WebRTCPeerConnectionGDNative",
+ "WebRTCDataChannelGDNative",
]
def get_doc_path():
diff --git a/modules/gdnative/doc_classes/WebRTCPeerGDNative.xml b/modules/gdnative/doc_classes/WebRTCDataChannelGDNative.xml
index 478889e031..ac18ec6020 100644
--- a/modules/gdnative/doc_classes/WebRTCPeerGDNative.xml
+++ b/modules/gdnative/doc_classes/WebRTCDataChannelGDNative.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="WebRTCPeerGDNative" inherits="WebRTCPeer" category="Core" version="3.2">
+<class name="WebRTCDataChannelGDNative" inherits="WebRTCDataChannel" category="Core" version="3.2">
<brief_description>
</brief_description>
<description>
diff --git a/modules/gdnative/doc_classes/WebRTCPeerConnectionGDNative.xml b/modules/gdnative/doc_classes/WebRTCPeerConnectionGDNative.xml
new file mode 100644
index 0000000000..44cb8e5db8
--- /dev/null
+++ b/modules/gdnative/doc_classes/WebRTCPeerConnectionGDNative.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="WebRTCPeerConnectionGDNative" inherits="WebRTCPeerConnection" category="Core" version="3.2">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/modules/gdnative/gdnative/dictionary.cpp b/modules/gdnative/gdnative/dictionary.cpp
index 2c6c9e2de2..fff3fc3625 100644
--- a/modules/gdnative/gdnative/dictionary.cpp
+++ b/modules/gdnative/gdnative/dictionary.cpp
@@ -55,6 +55,15 @@ void GDAPI godot_dictionary_destroy(godot_dictionary *p_self) {
self->~Dictionary();
}
+godot_dictionary GDAPI godot_dictionary_duplicate(const godot_dictionary *p_self, const godot_bool p_deep) {
+ const Dictionary *self = (const Dictionary *)p_self;
+ godot_dictionary res;
+ Dictionary *val = (Dictionary *)&res;
+ memnew_placement(val, Dictionary);
+ *val = self->duplicate(p_deep);
+ return res;
+}
+
godot_int GDAPI godot_dictionary_size(const godot_dictionary *p_self) {
const Dictionary *self = (const Dictionary *)p_self;
return self->size();
diff --git a/modules/gdnative/gdnative/variant.cpp b/modules/gdnative/gdnative/variant.cpp
index 8f0d5a2db4..ac4d5a86b2 100644
--- a/modules/gdnative/gdnative/variant.cpp
+++ b/modules/gdnative/gdnative/variant.cpp
@@ -518,7 +518,7 @@ void GDAPI godot_variant_evaluate(godot_variant_operator p_op, const godot_varia
const Variant *a = (const Variant *)p_a;
const Variant *b = (const Variant *)p_b;
Variant *ret = (Variant *)r_ret;
- Variant::evaluate(op, a, b, *ret, *r_valid);
+ Variant::evaluate(op, *a, *b, *ret, *r_valid);
}
#ifdef __cplusplus
diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json
index 9882a89794..8afe988102 100644
--- a/modules/gdnative/gdnative_api.json
+++ b/modules/gdnative/gdnative_api.json
@@ -11,7 +11,24 @@
"major": 1,
"minor": 1
},
- "next": null,
+ "next": {
+ "type": "CORE",
+ "version": {
+ "major": 1,
+ "minor": 2
+ },
+ "next": null,
+ "api": [
+ {
+ "name": "godot_dictionary_duplicate",
+ "return_type": "godot_dictionary",
+ "arguments": [
+ ["const godot_dictionary *", "p_self"],
+ ["const godot_bool", "p_deep"]
+ ]
+ }
+ ]
+ },
"api": [
{
"name": "godot_color_to_abgr32",
@@ -6436,11 +6453,26 @@
"next": null,
"api": [
{
- "name": "godot_net_bind_webrtc_peer",
+ "name": "godot_net_set_webrtc_library",
+ "return_type": "godot_error",
+ "arguments": [
+ ["const godot_net_webrtc_library *", "p_library"]
+ ]
+ },
+ {
+ "name": "godot_net_bind_webrtc_peer_connection",
+ "return_type": "void",
+ "arguments": [
+ ["godot_object *", "p_obj"],
+ ["const godot_net_webrtc_peer_connection *", "p_interface"]
+ ]
+ },
+ {
+ "name": "godot_net_bind_webrtc_data_channel",
"return_type": "void",
"arguments": [
["godot_object *", "p_obj"],
- ["const godot_net_webrtc_peer *", "p_interface"]
+ ["const godot_net_webrtc_data_channel *", "p_interface"]
]
}
]
diff --git a/modules/gdnative/include/gdnative/dictionary.h b/modules/gdnative/include/gdnative/dictionary.h
index 14e35b4692..483cd9c4e3 100644
--- a/modules/gdnative/include/gdnative/dictionary.h
+++ b/modules/gdnative/include/gdnative/dictionary.h
@@ -63,6 +63,8 @@ void GDAPI godot_dictionary_new(godot_dictionary *r_dest);
void GDAPI godot_dictionary_new_copy(godot_dictionary *r_dest, const godot_dictionary *p_src);
void GDAPI godot_dictionary_destroy(godot_dictionary *p_self);
+godot_dictionary GDAPI godot_dictionary_duplicate(const godot_dictionary *p_self, const godot_bool p_deep);
+
godot_int GDAPI godot_dictionary_size(const godot_dictionary *p_self);
godot_bool GDAPI godot_dictionary_empty(const godot_dictionary *p_self);
diff --git a/modules/gdnative/include/net/godot_net.h b/modules/gdnative/include/net/godot_net.h
index c1bc9daab5..3a411755c1 100644
--- a/modules/gdnative/include/net/godot_net.h
+++ b/modules/gdnative/include/net/godot_net.h
@@ -111,37 +111,11 @@ typedef struct {
/* Binds a MultiplayerPeerGDNative to the provided interface */
void GDAPI godot_net_bind_multiplayer_peer(godot_object *p_obj, const godot_net_multiplayer_peer *);
-typedef struct {
- godot_gdnative_api_version version; /* version of our API */
-
- godot_object *data; /* User reference */
-
- /* This is PacketPeer */
- godot_error (*get_packet)(void *, const uint8_t **, int *);
- godot_error (*put_packet)(void *, const uint8_t *, int);
- godot_int (*get_available_packet_count)(const void *);
- godot_int (*get_max_packet_size)(const void *);
-
- /* This is WebRTCPeer */
- void (*set_write_mode)(void *, godot_int);
- godot_int (*get_write_mode)(const void *);
- bool (*was_string_packet)(const void *);
- godot_int (*get_connection_state)(const void *);
-
- godot_error (*create_offer)(void *);
- godot_error (*set_remote_description)(void *, const char *, const char *);
- godot_error (*set_local_description)(void *, const char *, const char *);
- godot_error (*add_ice_candidate)(void *, const char *, int, const char *);
- godot_error (*poll)(void *);
-
- void *next; /* For extension? */
-} godot_net_webrtc_peer;
-
-/* Binds a PacketPeerGDNative to the provided interface */
-void GDAPI godot_net_bind_webrtc_peer(godot_object *p_obj, const godot_net_webrtc_peer *);
-
#ifdef __cplusplus
}
#endif
+// WebRTC Bindings
+#include "net/godot_webrtc.h"
+
#endif /* GODOT_NATIVENET_H */
diff --git a/modules/gdnative/include/net/godot_webrtc.h b/modules/gdnative/include/net/godot_webrtc.h
new file mode 100644
index 0000000000..783f7b727d
--- /dev/null
+++ b/modules/gdnative/include/net/godot_webrtc.h
@@ -0,0 +1,122 @@
+/*************************************************************************/
+/* godot_webrtc.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 GODOT_NATIVEWEBRTC_H
+#define GODOT_NATIVEWEBRTC_H
+
+#include <gdnative/gdnative.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define GODOT_NET_WEBRTC_API_MAJOR 3
+#define GODOT_NET_WEBRTC_API_MINOR 2
+
+/* Library Interface (used to set default GDNative WebRTC implementation */
+typedef struct {
+ godot_gdnative_api_version version; /* version of our API */
+
+ /* Called when the library is unset as default interface via godot_net_set_webrtc_library */
+ void (*unregistered)();
+
+ /* Used by WebRTCPeerConnection create when GDNative is the default implementation. */
+ /* Takes a pointer to WebRTCPeerConnectionGDNative, should bind and return OK, failure if binding was unsuccessful. */
+ godot_error (*create_peer_connection)(godot_object *);
+
+ void *next; /* For extension */
+} godot_net_webrtc_library;
+
+/* WebRTCPeerConnection interface */
+typedef struct {
+ godot_gdnative_api_version version; /* version of our API */
+
+ godot_object *data; /* User reference */
+
+ /* This is WebRTCPeerConnection */
+ godot_int (*get_connection_state)(const void *);
+
+ godot_error (*initialize)(void *, const godot_dictionary *);
+ godot_object *(*create_data_channel)(void *, const char *p_channel_name, const godot_dictionary *);
+ godot_error (*create_offer)(void *);
+ godot_error (*create_answer)(void *); /* unused for now, should be done automatically on set_local_description */
+ godot_error (*set_remote_description)(void *, const char *, const char *);
+ godot_error (*set_local_description)(void *, const char *, const char *);
+ godot_error (*add_ice_candidate)(void *, const char *, int, const char *);
+ godot_error (*poll)(void *);
+ void (*close)(void *);
+
+ void *next; /* For extension? */
+} godot_net_webrtc_peer_connection;
+
+/* WebRTCDataChannel interface */
+typedef struct {
+ godot_gdnative_api_version version; /* version of our API */
+
+ godot_object *data; /* User reference */
+
+ /* This is PacketPeer */
+ godot_error (*get_packet)(void *, const uint8_t **, int *);
+ godot_error (*put_packet)(void *, const uint8_t *, int);
+ godot_int (*get_available_packet_count)(const void *);
+ godot_int (*get_max_packet_size)(const void *);
+
+ /* This is WebRTCDataChannel */
+ void (*set_write_mode)(void *, godot_int);
+ godot_int (*get_write_mode)(const void *);
+ bool (*was_string_packet)(const void *);
+
+ godot_int (*get_ready_state)(const void *);
+ const char *(*get_label)(const void *);
+ bool (*is_ordered)(const void *);
+ int (*get_id)(const void *);
+ int (*get_max_packet_life_time)(const void *);
+ int (*get_max_retransmits)(const void *);
+ const char *(*get_protocol)(const void *);
+ bool (*is_negotiated)(const void *);
+
+ godot_error (*poll)(void *);
+ void (*close)(void *);
+
+ void *next; /* For extension? */
+} godot_net_webrtc_data_channel;
+
+/* Set the default GDNative library */
+godot_error GDAPI godot_net_set_webrtc_library(const godot_net_webrtc_library *);
+/* Binds a WebRTCPeerConnectionGDNative to the provided interface */
+void GDAPI godot_net_bind_webrtc_peer_connection(godot_object *p_obj, const godot_net_webrtc_peer_connection *);
+/* Binds a WebRTCDataChannelGDNative to the provided interface */
+void GDAPI godot_net_bind_webrtc_data_channel(godot_object *p_obj, const godot_net_webrtc_data_channel *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/modules/gdnative/net/webrtc_peer_gdnative.cpp b/modules/gdnative/net/webrtc_gdnative.cpp
index 60b1ed4fe4..d77fa057c5 100644
--- a/modules/gdnative/net/webrtc_peer_gdnative.cpp
+++ b/modules/gdnative/net/webrtc_gdnative.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* packet_peer_gdnative.cpp */
+/* webrtc_gdnative.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -32,14 +32,29 @@
#include "modules/gdnative/include/net/godot_net.h"
#ifdef WEBRTC_GDNATIVE_ENABLED
-#include "modules/webrtc/webrtc_peer_gdnative.h"
+#include "modules/webrtc/webrtc_data_channel_gdnative.h"
+#include "modules/webrtc/webrtc_peer_connection_gdnative.h"
#endif
extern "C" {
-void GDAPI godot_net_bind_webrtc_peer(godot_object *p_obj, const godot_net_webrtc_peer *p_impl) {
+void GDAPI godot_net_bind_webrtc_peer_connection(godot_object *p_obj, const godot_net_webrtc_peer_connection *p_impl) {
#ifdef WEBRTC_GDNATIVE_ENABLED
- ((WebRTCPeerGDNative *)p_obj)->set_native_webrtc_peer(p_impl);
+ ((WebRTCPeerConnectionGDNative *)p_obj)->set_native_webrtc_peer_connection(p_impl);
+#endif
+}
+
+void GDAPI godot_net_bind_webrtc_data_channel(godot_object *p_obj, const godot_net_webrtc_data_channel *p_impl) {
+#ifdef WEBRTC_GDNATIVE_ENABLED
+ ((WebRTCDataChannelGDNative *)p_obj)->set_native_webrtc_data_channel(p_impl);
+#endif
+}
+
+godot_error GDAPI godot_net_set_webrtc_library(const godot_net_webrtc_library *p_lib) {
+#ifdef WEBRTC_GDNATIVE_ENABLED
+ return (godot_error)WebRTCPeerConnectionGDNative::set_default_library(p_lib);
+#else
+ return ERR_UNAVAILABLE;
#endif
}
}
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index 4f59b06ae6..060a9d6c91 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -330,7 +330,7 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
return color_map;
}
-String GDScriptSyntaxHighlighter::get_name() {
+String GDScriptSyntaxHighlighter::get_name() const {
return "GDScript";
}
diff --git a/modules/gdscript/editor/gdscript_highlighter.h b/modules/gdscript/editor/gdscript_highlighter.h
index 9dc10a5d1b..9ba2c80552 100644
--- a/modules/gdscript/editor/gdscript_highlighter.h
+++ b/modules/gdscript/editor/gdscript_highlighter.h
@@ -65,7 +65,7 @@ public:
virtual void _update_cache();
virtual Map<int, TextEdit::HighlighterInfo> _get_line_syntax_highlighting(int p_line);
- virtual String get_name();
+ virtual String get_name() const;
virtual List<String> get_supported_languages();
};
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index bfbd6ca80e..72199281ff 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -1855,6 +1855,34 @@ void CSharpInstance::_call_notification(int p_notification) {
}
}
+String CSharpInstance::to_string(bool *r_valid) {
+ MonoObject *mono_object = get_mono_object();
+
+ if (mono_object == NULL) {
+ if (r_valid)
+ *r_valid = false;
+ return String();
+ }
+
+ MonoException *exc = NULL;
+ MonoString *result = GDMonoUtils::object_to_string(mono_object, &exc);
+
+ if (exc) {
+ GDMonoUtils::set_pending_exception(exc);
+ if (r_valid)
+ *r_valid = false;
+ return String();
+ }
+
+ if (result == NULL) {
+ if (r_valid)
+ *r_valid = false;
+ return String();
+ }
+
+ return GDMonoMarshal::mono_string_to_godot(result);
+}
+
Ref<Script> CSharpInstance::get_script() const {
return script;
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index 298d55c4df..e735e0f741 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -261,6 +261,8 @@ public:
virtual void notification(int p_notification);
void _call_notification(int p_notification);
+ virtual String to_string(bool *r_valid);
+
virtual Ref<Script> get_script() const;
virtual ScriptLanguage *get_language();
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index a408716641..cd7774e7a1 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -2300,9 +2300,14 @@ void BindingsGenerator::_populate_object_type_interfaces() {
if (method_info.name.empty())
continue;
+ String cname = method_info.name;
+
+ if (blacklisted_methods.find(itype.cname) && blacklisted_methods[itype.cname].find(cname))
+ continue;
+
MethodInterface imethod;
imethod.name = method_info.name;
- imethod.cname = imethod.name;
+ imethod.cname = cname;
if (method_info.flags & METHOD_FLAG_VIRTUAL)
imethod.is_virtual = true;
@@ -2975,6 +2980,13 @@ void BindingsGenerator::_populate_global_constants() {
}
}
+void BindingsGenerator::_initialize_blacklisted_methods() {
+
+ blacklisted_methods["Object"].push_back("to_string"); // there is already ToString
+ blacklisted_methods["Object"].push_back("_to_string"); // override ToString instead
+ blacklisted_methods["Object"].push_back("_init"); // never called in C# (TODO: implement it)
+}
+
void BindingsGenerator::_log(const char *p_format, ...) {
if (log_print_enabled) {
@@ -2992,6 +3004,8 @@ void BindingsGenerator::_initialize() {
enum_types.clear();
+ _initialize_blacklisted_methods();
+
_populate_object_type_interfaces();
_populate_builtin_type_interfaces();
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index bdba28c267..ffc73a7e3e 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -491,6 +491,10 @@ class BindingsGenerator {
List<InternalCall> core_custom_icalls;
List<InternalCall> editor_custom_icalls;
+ Map<StringName, List<StringName> > blacklisted_methods;
+
+ void _initialize_blacklisted_methods();
+
struct NameCache {
StringName type_void;
StringName type_Array;
diff --git a/modules/mono/glue/Managed/Files/Extensions/NodeExtensions.cs b/modules/mono/glue/Managed/Files/Extensions/NodeExtensions.cs
index 366d89b1c2..5023725f17 100644
--- a/modules/mono/glue/Managed/Files/Extensions/NodeExtensions.cs
+++ b/modules/mono/glue/Managed/Files/Extensions/NodeExtensions.cs
@@ -24,12 +24,12 @@ namespace Godot
public T GetOwner<T>() where T : class
{
- return (T)(object)GetOwner();
+ return (T)(object)Owner;
}
public T GetOwnerOrNull<T>() where T : class
{
- return GetOwner() as T;
+ return Owner as T;
}
public T GetParent<T>() where T : class
diff --git a/modules/mono/glue/Managed/Files/MarshalUtils.cs b/modules/mono/glue/Managed/Files/MarshalUtils.cs
index 730a1e1585..a1d63a62ef 100644
--- a/modules/mono/glue/Managed/Files/MarshalUtils.cs
+++ b/modules/mono/glue/Managed/Files/MarshalUtils.cs
@@ -23,7 +23,7 @@ namespace Godot
/// <summary>
/// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
- /// is <see cref="Godot.Collections.Dictionary{T}"/>; otherwise returns <see langword="false"/>.
+ /// is <see cref="Godot.Collections.Dictionary{TKey, TValue}"/>; otherwise returns <see langword="false"/>.
/// </summary>
/// <exception cref="System.InvalidOperationException">
/// <paramref name="type"/> is not a generic type. That is, IsGenericType returns false.
@@ -45,6 +45,44 @@ namespace Godot
valueType = genericArgs[1];
}
+ static bool GenericIEnumerableIsAssignableFromType(Type type)
+ {
+ if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
+ return true;
+
+ foreach (var interfaceType in type.GetInterfaces())
+ {
+ if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
+ return true;
+ }
+
+ Type baseType = type.BaseType;
+
+ if (baseType == null)
+ return false;
+
+ return GenericIEnumerableIsAssignableFromType(baseType);
+ }
+
+ static bool GenericIDictionaryIsAssignableFromType(Type type)
+ {
+ if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary<,>))
+ return true;
+
+ foreach (var interfaceType in type.GetInterfaces())
+ {
+ if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IDictionary<,>))
+ return true;
+ }
+
+ Type baseType = type.BaseType;
+
+ if (baseType == null)
+ return false;
+
+ return GenericIDictionaryIsAssignableFromType(baseType);
+ }
+
static bool GenericIEnumerableIsAssignableFromType(Type type, out Type elementType)
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
diff --git a/modules/mono/glue/Managed/IgnoredFiles/Node.cs b/modules/mono/glue/Managed/IgnoredFiles/Node.cs
index 99ba0f827a..cff61b1e0b 100644
--- a/modules/mono/glue/Managed/IgnoredFiles/Node.cs
+++ b/modules/mono/glue/Managed/IgnoredFiles/Node.cs
@@ -15,9 +15,10 @@ namespace Godot
throw new NotImplementedException();
}
- public Node GetOwner()
+ public Node Owner
{
- throw new NotImplementedException();
+ get => throw new NotImplementedException();
+ set => throw new NotImplementedException();
}
public Node GetParent()
diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp
index 4aef5684fd..47239f1260 100644
--- a/modules/mono/glue/collections_glue.cpp
+++ b/modules/mono/glue/collections_glue.cpp
@@ -162,7 +162,7 @@ MonoObject *godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key) {
#ifdef DEBUG_ENABLED
CRASH_COND(!exc);
#endif
- GDMonoUtils::runtime_object_init(exc);
+ GDMonoUtils::runtime_object_init(exc, CACHED_CLASS(KeyNotFoundException));
GDMonoUtils::set_pending_exception((MonoException *)exc);
return NULL;
}
@@ -176,7 +176,7 @@ MonoObject *godot_icall_Dictionary_GetValue_Generic(Dictionary *ptr, MonoObject
#ifdef DEBUG_ENABLED
CRASH_COND(!exc);
#endif
- GDMonoUtils::runtime_object_init(exc);
+ GDMonoUtils::runtime_object_init(exc, CACHED_CLASS(KeyNotFoundException));
GDMonoUtils::set_pending_exception((MonoException *)exc);
return NULL;
}
diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp
index 461dcf3ec0..c462b8f71d 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.cpp
+++ b/modules/mono/mono_gd/gd_mono_marshal.cpp
@@ -161,8 +161,7 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type_class->get_mono_type());
- MonoReflectionType *key_reftype, *value_reftype;
- if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) {
+ if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype)) {
return Variant::DICTIONARY;
}
@@ -170,8 +169,7 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
return Variant::DICTIONARY;
}
- MonoReflectionType *elem_reftype;
- if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) {
+ if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype)) {
return Variant::ARRAY;
}
@@ -193,16 +191,14 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
// The order in which we check the following interfaces is very important (dictionaries and generics first)
- MonoReflectionType *key_reftype, *value_reftype;
- if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype))
+ if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype))
return Variant::DICTIONARY;
if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
return Variant::DICTIONARY;
}
- MonoReflectionType *elem_reftype;
- if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype))
+ if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype))
return Variant::ARRAY;
if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
@@ -850,8 +846,7 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type_class->get_mono_type());
- MonoReflectionType *key_reftype, *value_reftype;
- if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) {
+ if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype)) {
return GDMonoUtils::Marshal::generic_idictionary_to_dictionary(p_obj);
}
@@ -859,8 +854,7 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
return GDMonoUtils::Marshal::idictionary_to_dictionary(p_obj);
}
- MonoReflectionType *elem_reftype;
- if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) {
+ if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype)) {
return GDMonoUtils::Marshal::enumerable_to_array(p_obj);
}
@@ -888,8 +882,7 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
// The order in which we check the following interfaces is very important (dictionaries and generics first)
- MonoReflectionType *key_reftype, *value_reftype;
- if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) {
+ if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype)) {
return GDMonoUtils::Marshal::generic_idictionary_to_dictionary(p_obj);
}
@@ -897,8 +890,7 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
return GDMonoUtils::Marshal::idictionary_to_dictionary(p_obj);
}
- MonoReflectionType *elem_reftype;
- if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) {
+ if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype)) {
return GDMonoUtils::Marshal::enumerable_to_array(p_obj);
}
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
index 5236e43c90..413c8cba85 100644
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ b/modules/mono/mono_gd/gd_mono_utils.cpp
@@ -161,6 +161,8 @@ void MonoCache::clear_members() {
methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType = NULL;
methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType = NULL;
+ methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType_with_info = NULL;
+ methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType_with_info = NULL;
methodthunk_MarshalUtils_MakeGenericArrayType = NULL;
methodthunk_MarshalUtils_MakeGenericDictionaryType = NULL;
@@ -282,8 +284,10 @@ void update_godot_api_cache() {
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArrayGetElementType, (ArrayGetElementType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("ArrayGetElementType", 2));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryGetKeyValueTypes, (DictionaryGetKeyValueTypes)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("DictionaryGetKeyValueTypes", 3));
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType, (GenericIEnumerableIsAssignableFromType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIEnumerableIsAssignableFromType", 2));
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryIsAssignableFromType, (GenericIDictionaryIsAssignableFromType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIDictionaryIsAssignableFromType", 3));
+ CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType, (GenericIEnumerableIsAssignableFromType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIEnumerableIsAssignableFromType", 1));
+ CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryIsAssignableFromType, (GenericIDictionaryIsAssignableFromType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIDictionaryIsAssignableFromType", 1));
+ CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info, (GenericIEnumerableIsAssignableFromType_with_info)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIEnumerableIsAssignableFromType", 2));
+ CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info, (GenericIEnumerableIsAssignableFromType_with_info)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIDictionaryIsAssignableFromType", 3));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericArrayType, (MakeGenericArrayType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("MakeGenericArrayType", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericDictionaryType, (MakeGenericDictionaryType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("MakeGenericDictionaryType", 2));
@@ -300,7 +304,7 @@ void update_godot_api_cache() {
// TODO Move to CSharpLanguage::init() and do handle disposal
MonoObject *task_scheduler = mono_object_new(SCRIPTS_DOMAIN, GODOT_API_CLASS(GodotTaskScheduler)->get_mono_ptr());
- GDMonoUtils::runtime_object_init(task_scheduler);
+ GDMonoUtils::runtime_object_init(task_scheduler, GODOT_API_CLASS(GodotTaskScheduler));
mono_cache.task_scheduler_handle = MonoGCHandle::create_strong(task_scheduler);
mono_cache.godot_api_cache_updated = true;
@@ -401,11 +405,10 @@ MonoThread *get_current_thread() {
return mono_thread_current();
}
-void runtime_object_init(MonoObject *p_this_obj) {
- GD_MONO_BEGIN_RUNTIME_INVOKE;
- // FIXME: Do not use mono_runtime_object_init, it aborts if an exception is thrown
- mono_runtime_object_init(p_this_obj);
- GD_MONO_END_RUNTIME_INVOKE;
+void runtime_object_init(MonoObject *p_this_obj, GDMonoClass *p_class, MonoException **r_exc) {
+ GDMonoMethod *ctor = p_class->get_method(".ctor", 0);
+ ERR_FAIL_NULL(ctor);
+ ctor->invoke_raw(p_this_obj, NULL, r_exc);
}
GDMonoClass *get_object_class(MonoObject *p_object) {
@@ -467,7 +470,7 @@ MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringNa
CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, p_object);
// Construct
- GDMonoUtils::runtime_object_init(mono_object);
+ GDMonoUtils::runtime_object_init(mono_object, p_class);
return mono_object;
}
@@ -477,7 +480,7 @@ MonoObject *create_managed_from(const NodePath &p_from) {
ERR_FAIL_NULL_V(mono_object, NULL);
// Construct
- GDMonoUtils::runtime_object_init(mono_object);
+ GDMonoUtils::runtime_object_init(mono_object, CACHED_CLASS(NodePath));
CACHED_FIELD(NodePath, ptr)->set_value_raw(mono_object, memnew(NodePath(p_from)));
@@ -489,7 +492,7 @@ MonoObject *create_managed_from(const RID &p_from) {
ERR_FAIL_NULL_V(mono_object, NULL);
// Construct
- GDMonoUtils::runtime_object_init(mono_object);
+ GDMonoUtils::runtime_object_init(mono_object, CACHED_CLASS(RID));
CACHED_FIELD(RID, ptr)->set_value_raw(mono_object, memnew(RID(p_from)));
@@ -788,16 +791,32 @@ void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoRefl
UNLIKELY_UNHANDLED_EXCEPTION(exc);
}
-MonoBoolean generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype) {
+MonoBoolean generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype) {
GenericIEnumerableIsAssignableFromType thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType);
MonoException *exc = NULL;
+ MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ return res;
+}
+
+MonoBoolean generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype) {
+ GenericIDictionaryIsAssignableFromType thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType);
+ MonoException *exc = NULL;
+ MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ return res;
+}
+
+MonoBoolean generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype) {
+ GenericIEnumerableIsAssignableFromType_with_info thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info);
+ MonoException *exc = NULL;
MonoBoolean res = invoke_method_thunk(thunk, p_reftype, r_elem_reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
return res;
}
MonoBoolean generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) {
- GenericIDictionaryIsAssignableFromType thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType);
+ GenericIDictionaryIsAssignableFromType_with_info thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType_with_info);
MonoException *exc = NULL;
MonoBoolean res = invoke_method_thunk(thunk, p_reftype, r_key_reftype, r_value_reftype, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h
index 081a8a9813..ee239be959 100644
--- a/modules/mono/mono_gd/gd_mono_utils.h
+++ b/modules/mono/mono_gd/gd_mono_utils.h
@@ -64,8 +64,10 @@ typedef MonoBoolean (*TypeIsGenericDictionary)(MonoReflectionType *, MonoExcepti
typedef void (*ArrayGetElementType)(MonoReflectionType *, MonoReflectionType **, MonoException **);
typedef void (*DictionaryGetKeyValueTypes)(MonoReflectionType *, MonoReflectionType **, MonoReflectionType **, MonoException **);
-typedef MonoBoolean (*GenericIEnumerableIsAssignableFromType)(MonoReflectionType *, MonoReflectionType **, MonoException **);
-typedef MonoBoolean (*GenericIDictionaryIsAssignableFromType)(MonoReflectionType *, MonoReflectionType **, MonoReflectionType **, MonoException **);
+typedef MonoBoolean (*GenericIEnumerableIsAssignableFromType)(MonoReflectionType *, MonoException **);
+typedef MonoBoolean (*GenericIDictionaryIsAssignableFromType)(MonoReflectionType *, MonoException **);
+typedef MonoBoolean (*GenericIEnumerableIsAssignableFromType_with_info)(MonoReflectionType *, MonoReflectionType **, MonoException **);
+typedef MonoBoolean (*GenericIDictionaryIsAssignableFromType_with_info)(MonoReflectionType *, MonoReflectionType **, MonoReflectionType **, MonoException **);
typedef MonoReflectionType *(*MakeGenericArrayType)(MonoReflectionType *, MonoException **);
typedef MonoReflectionType *(*MakeGenericDictionaryType)(MonoReflectionType *, MonoReflectionType *, MonoException **);
@@ -82,6 +84,8 @@ MonoBoolean type_is_generic_dictionary(MonoReflectionType *p_reftype);
void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype);
void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype);
+MonoBoolean generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype);
+MonoBoolean generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype);
MonoBoolean generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype);
MonoBoolean generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype);
@@ -197,6 +201,8 @@ struct MonoCache {
GenericIEnumerableIsAssignableFromType methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType;
GenericIDictionaryIsAssignableFromType methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType;
+ GenericIEnumerableIsAssignableFromType_with_info methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType_with_info;
+ GenericIDictionaryIsAssignableFromType_with_info methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType_with_info;
MakeGenericArrayType methodthunk_MarshalUtils_MakeGenericArrayType;
MakeGenericDictionaryType methodthunk_MarshalUtils_MakeGenericDictionaryType;
@@ -249,7 +255,7 @@ _FORCE_INLINE_ bool is_main_thread() {
return mono_domain_get() != NULL && mono_thread_get_main() == mono_thread_current();
}
-void runtime_object_init(MonoObject *p_this_obj);
+void runtime_object_init(MonoObject *p_this_obj, GDMonoClass *p_class, MonoException **r_exc = NULL);
GDMonoClass *get_object_class(MonoObject *p_object);
GDMonoClass *type_get_proxy_class(const StringName &p_type);
diff --git a/modules/webrtc/config.py b/modules/webrtc/config.py
index 5ed245bad2..2e3a18ad0e 100644
--- a/modules/webrtc/config.py
+++ b/modules/webrtc/config.py
@@ -6,7 +6,8 @@ def configure(env):
def get_doc_classes():
return [
- "WebRTCPeer"
+ "WebRTCPeerConnection",
+ "WebRTCDataChannel"
]
def get_doc_path():
diff --git a/modules/webrtc/doc_classes/WebRTCDataChannel.xml b/modules/webrtc/doc_classes/WebRTCDataChannel.xml
new file mode 100644
index 0000000000..dcc14d4ddb
--- /dev/null
+++ b/modules/webrtc/doc_classes/WebRTCDataChannel.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="WebRTCDataChannel" inherits="PacketPeer" category="Core" version="3.2">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="close">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_id" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_label" qualifiers="const">
+ <return type="String">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_max_packet_life_time" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_max_retransmits" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_protocol" qualifiers="const">
+ <return type="String">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_ready_state" qualifiers="const">
+ <return type="int" enum="WebRTCDataChannel.ChannelState">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="is_negotiated" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="is_ordered" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="poll">
+ <return type="int" enum="Error">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="was_string_packet" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="write_mode" type="int" setter="set_write_mode" getter="get_write_mode" enum="WebRTCDataChannel.WriteMode">
+ </member>
+ </members>
+ <constants>
+ <constant name="WRITE_MODE_TEXT" value="0" enum="WriteMode">
+ </constant>
+ <constant name="WRITE_MODE_BINARY" value="1" enum="WriteMode">
+ </constant>
+ <constant name="STATE_CONNECTING" value="0" enum="ChannelState">
+ </constant>
+ <constant name="STATE_OPEN" value="1" enum="ChannelState">
+ </constant>
+ <constant name="STATE_CLOSING" value="2" enum="ChannelState">
+ </constant>
+ <constant name="STATE_CLOSED" value="3" enum="ChannelState">
+ </constant>
+ </constants>
+</class>
diff --git a/modules/webrtc/doc_classes/WebRTCPeer.xml b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
index 18d1345623..8b14c60deb 100644
--- a/modules/webrtc/doc_classes/WebRTCPeer.xml
+++ b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="WebRTCPeer" inherits="PacketPeer" category="Core" version="3.2">
+<class name="WebRTCPeerConnection" inherits="Reference" category="Core" version="3.2">
<brief_description>
</brief_description>
<description>
@@ -19,6 +19,24 @@
<description>
</description>
</method>
+ <method name="close">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="create_data_channel">
+ <return type="WebRTCDataChannel">
+ </return>
+ <argument index="0" name="label" type="String">
+ </argument>
+ <argument index="1" name="options" type="Dictionary" default="{
+
+}">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="create_offer">
<return type="int" enum="Error">
</return>
@@ -26,8 +44,18 @@
</description>
</method>
<method name="get_connection_state" qualifiers="const">
- <return type="int" enum="WebRTCPeer.ConnectionState">
+ <return type="int" enum="WebRTCPeerConnection.ConnectionState">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="initialize">
+ <return type="int" enum="Error">
</return>
+ <argument index="0" name="configuration" type="Dictionary" default="{
+
+}">
+ </argument>
<description>
</description>
</method>
@@ -57,19 +85,15 @@
<description>
</description>
</method>
- <method name="was_string_packet" qualifiers="const">
- <return type="bool">
- </return>
- <description>
- </description>
- </method>
</methods>
- <members>
- <member name="write_mode" type="int" setter="set_write_mode" getter="get_write_mode" enum="WebRTCPeer.WriteMode">
- </member>
- </members>
<signals>
- <signal name="new_ice_candidate">
+ <signal name="data_channel_received">
+ <argument index="0" name="channel" type="Object">
+ </argument>
+ <description>
+ </description>
+ </signal>
+ <signal name="ice_candidate_created">
<argument index="0" name="media" type="String">
</argument>
<argument index="1" name="index" type="int">
@@ -79,7 +103,7 @@
<description>
</description>
</signal>
- <signal name="offer_created">
+ <signal name="session_description_created">
<argument index="0" name="type" type="String">
</argument>
<argument index="1" name="sdp" type="String">
@@ -89,10 +113,6 @@
</signal>
</signals>
<constants>
- <constant name="WRITE_MODE_TEXT" value="0" enum="WriteMode">
- </constant>
- <constant name="WRITE_MODE_BINARY" value="1" enum="WriteMode">
- </constant>
<constant name="STATE_NEW" value="0" enum="ConnectionState">
</constant>
<constant name="STATE_CONNECTING" value="1" enum="ConnectionState">
diff --git a/modules/webrtc/register_types.cpp b/modules/webrtc/register_types.cpp
index ee7a766bd9..44e072cc89 100644
--- a/modules/webrtc/register_types.cpp
+++ b/modules/webrtc/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -29,27 +29,31 @@
/*************************************************************************/
#include "register_types.h"
-#include "webrtc_peer.h"
+#include "webrtc_data_channel.h"
+#include "webrtc_peer_connection.h"
#ifdef JAVASCRIPT_ENABLED
#include "emscripten.h"
-#include "webrtc_peer_js.h"
+#include "webrtc_peer_connection_js.h"
#endif
#ifdef WEBRTC_GDNATIVE_ENABLED
-#include "webrtc_peer_gdnative.h"
+#include "webrtc_data_channel_gdnative.h"
+#include "webrtc_peer_connection_gdnative.h"
#endif
void register_webrtc_types() {
#ifdef JAVASCRIPT_ENABLED
- WebRTCPeerJS::make_default();
+ WebRTCPeerConnectionJS::make_default();
#elif defined(WEBRTC_GDNATIVE_ENABLED)
- WebRTCPeerGDNative::make_default();
+ WebRTCPeerConnectionGDNative::make_default();
#endif
- ClassDB::register_custom_instance_class<WebRTCPeer>();
+ ClassDB::register_custom_instance_class<WebRTCPeerConnection>();
#ifdef WEBRTC_GDNATIVE_ENABLED
- ClassDB::register_class<WebRTCPeerGDNative>();
+ ClassDB::register_class<WebRTCPeerConnectionGDNative>();
+ ClassDB::register_class<WebRTCDataChannelGDNative>();
#endif
+ ClassDB::register_virtual_class<WebRTCDataChannel>();
}
void unregister_webrtc_types() {}
diff --git a/modules/webrtc/register_types.h b/modules/webrtc/register_types.h
index 18a5dcc5aa..4923547a95 100644
--- a/modules/webrtc/register_types.h
+++ b/modules/webrtc/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/modules/webrtc/webrtc_data_channel.cpp b/modules/webrtc/webrtc_data_channel.cpp
new file mode 100644
index 0000000000..2bd30e68f5
--- /dev/null
+++ b/modules/webrtc/webrtc_data_channel.cpp
@@ -0,0 +1,64 @@
+/*************************************************************************/
+/* webrtc_data_channel.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 "webrtc_data_channel.h"
+
+void WebRTCDataChannel::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("poll"), &WebRTCDataChannel::poll);
+ ClassDB::bind_method(D_METHOD("close"), &WebRTCDataChannel::close);
+
+ ClassDB::bind_method(D_METHOD("was_string_packet"), &WebRTCDataChannel::was_string_packet);
+ ClassDB::bind_method(D_METHOD("set_write_mode", "write_mode"), &WebRTCDataChannel::set_write_mode);
+ ClassDB::bind_method(D_METHOD("get_write_mode"), &WebRTCDataChannel::get_write_mode);
+ ClassDB::bind_method(D_METHOD("get_ready_state"), &WebRTCDataChannel::get_ready_state);
+ ClassDB::bind_method(D_METHOD("get_label"), &WebRTCDataChannel::get_label);
+ ClassDB::bind_method(D_METHOD("is_ordered"), &WebRTCDataChannel::is_ordered);
+ ClassDB::bind_method(D_METHOD("get_id"), &WebRTCDataChannel::get_id);
+ ClassDB::bind_method(D_METHOD("get_max_packet_life_time"), &WebRTCDataChannel::get_max_packet_life_time);
+ ClassDB::bind_method(D_METHOD("get_max_retransmits"), &WebRTCDataChannel::get_max_retransmits);
+ ClassDB::bind_method(D_METHOD("get_protocol"), &WebRTCDataChannel::get_protocol);
+ ClassDB::bind_method(D_METHOD("is_negotiated"), &WebRTCDataChannel::is_negotiated);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "write_mode", PROPERTY_HINT_ENUM), "set_write_mode", "get_write_mode");
+
+ BIND_ENUM_CONSTANT(WRITE_MODE_TEXT);
+ BIND_ENUM_CONSTANT(WRITE_MODE_BINARY);
+
+ BIND_ENUM_CONSTANT(STATE_CONNECTING);
+ BIND_ENUM_CONSTANT(STATE_OPEN);
+ BIND_ENUM_CONSTANT(STATE_CLOSING);
+ BIND_ENUM_CONSTANT(STATE_CLOSED);
+}
+
+WebRTCDataChannel::WebRTCDataChannel() {
+}
+
+WebRTCDataChannel::~WebRTCDataChannel() {
+}
diff --git a/modules/webrtc/webrtc_peer.h b/modules/webrtc/webrtc_data_channel.h
index e141c14655..0b161da784 100644
--- a/modules/webrtc/webrtc_peer.h
+++ b/modules/webrtc/webrtc_data_channel.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* webrtc_peer.h */
+/* webrtc_data_channel.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,13 +28,13 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef WEBRTC_PEER_H
-#define WEBRTC_PEER_H
+#ifndef WEBRTC_DATA_CHANNEL_H
+#define WEBRTC_DATA_CHANNEL_H
#include "core/io/packet_peer.h"
-class WebRTCPeer : public PacketPeer {
- GDCLASS(WebRTCPeer, PacketPeer);
+class WebRTCDataChannel : public PacketPeer {
+ GDCLASS(WebRTCDataChannel, PacketPeer);
public:
enum WriteMode {
@@ -42,30 +42,32 @@ public:
WRITE_MODE_BINARY,
};
- enum ConnectionState {
- STATE_NEW,
+ enum ChannelState {
STATE_CONNECTING,
- STATE_CONNECTED,
- STATE_DISCONNECTED,
- STATE_FAILED,
+ STATE_OPEN,
+ STATE_CLOSING,
STATE_CLOSED
};
protected:
static void _bind_methods();
- static WebRTCPeer *(*_create)();
public:
virtual void set_write_mode(WriteMode mode) = 0;
virtual WriteMode get_write_mode() const = 0;
virtual bool was_string_packet() const = 0;
- virtual ConnectionState get_connection_state() const = 0;
- virtual Error create_offer() = 0;
- virtual Error set_remote_description(String type, String sdp) = 0;
- virtual Error set_local_description(String type, String sdp) = 0;
- virtual Error add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName) = 0;
+ virtual ChannelState get_ready_state() const = 0;
+ virtual String get_label() const = 0;
+ virtual bool is_ordered() const = 0;
+ virtual int get_id() const = 0;
+ virtual int get_max_packet_life_time() const = 0;
+ virtual int get_max_retransmits() const = 0;
+ virtual String get_protocol() const = 0;
+ virtual bool is_negotiated() const = 0;
+
virtual Error poll() = 0;
+ virtual void close() = 0;
/** Inherited from PacketPeer: **/
virtual int get_available_packet_count() const = 0;
@@ -74,13 +76,10 @@ public:
virtual int get_max_packet_size() const = 0;
- static Ref<WebRTCPeer> create_ref();
- static WebRTCPeer *create();
-
- WebRTCPeer();
- ~WebRTCPeer();
+ WebRTCDataChannel();
+ ~WebRTCDataChannel();
};
-VARIANT_ENUM_CAST(WebRTCPeer::WriteMode);
-VARIANT_ENUM_CAST(WebRTCPeer::ConnectionState);
-#endif // WEBRTC_PEER_H
+VARIANT_ENUM_CAST(WebRTCDataChannel::WriteMode);
+VARIANT_ENUM_CAST(WebRTCDataChannel::ChannelState);
+#endif // WEBRTC_DATA_CHANNEL_H
diff --git a/modules/webrtc/webrtc_peer_gdnative.cpp b/modules/webrtc/webrtc_data_channel_gdnative.cpp
index f782944980..4f33491af2 100644
--- a/modules/webrtc/webrtc_peer_gdnative.cpp
+++ b/modules/webrtc/webrtc_data_channel_gdnative.cpp
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* webrtc_peer_gdnative.cpp */
+/* webrtc_data_channel_gdnative.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,84 +30,106 @@
#ifdef WEBRTC_GDNATIVE_ENABLED
-#include "webrtc_peer_gdnative.h"
+#include "webrtc_data_channel_gdnative.h"
+#include "core/io/resource_loader.h"
+#include "modules/gdnative/nativescript/nativescript.h"
-void WebRTCPeerGDNative::_bind_methods() {
+void WebRTCDataChannelGDNative::_bind_methods() {
}
-WebRTCPeerGDNative::WebRTCPeerGDNative() {
+WebRTCDataChannelGDNative::WebRTCDataChannelGDNative() {
interface = NULL;
}
-WebRTCPeerGDNative::~WebRTCPeerGDNative() {
+WebRTCDataChannelGDNative::~WebRTCDataChannelGDNative() {
}
-Error WebRTCPeerGDNative::create_offer() {
+Error WebRTCDataChannelGDNative::poll() {
ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
- return (Error)interface->create_offer(interface->data);
+ return (Error)interface->poll(interface->data);
}
-Error WebRTCPeerGDNative::set_local_description(String p_type, String p_sdp) {
- ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
- return (Error)interface->set_local_description(interface->data, p_type.utf8().get_data(), p_sdp.utf8().get_data());
+void WebRTCDataChannelGDNative::close() {
+ ERR_FAIL_COND(interface == NULL);
+ interface->close(interface->data);
}
-Error WebRTCPeerGDNative::set_remote_description(String p_type, String p_sdp) {
- ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
- return (Error)interface->set_remote_description(interface->data, p_type.utf8().get_data(), p_sdp.utf8().get_data());
+void WebRTCDataChannelGDNative::set_write_mode(WriteMode p_mode) {
+ ERR_FAIL_COND(interface == NULL);
+ interface->set_write_mode(interface->data, p_mode);
}
-Error WebRTCPeerGDNative::add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName) {
- ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
- return (Error)interface->add_ice_candidate(interface->data, sdpMidName.utf8().get_data(), sdpMlineIndexName, sdpName.utf8().get_data());
+WebRTCDataChannel::WriteMode WebRTCDataChannelGDNative::get_write_mode() const {
+ ERR_FAIL_COND_V(interface == NULL, WRITE_MODE_BINARY);
+ return (WriteMode)interface->get_write_mode(interface->data);
}
-Error WebRTCPeerGDNative::poll() {
- ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
- return (Error)interface->poll(interface->data);
+bool WebRTCDataChannelGDNative::was_string_packet() const {
+ ERR_FAIL_COND_V(interface == NULL, false);
+ return interface->was_string_packet(interface->data);
}
-void WebRTCPeerGDNative::set_write_mode(WriteMode p_mode) {
- ERR_FAIL_COND(interface == NULL);
- interface->set_write_mode(interface->data, p_mode);
+WebRTCDataChannel::ChannelState WebRTCDataChannelGDNative::get_ready_state() const {
+ ERR_FAIL_COND_V(interface == NULL, STATE_CLOSED);
+ return (ChannelState)interface->get_ready_state(interface->data);
}
-WebRTCPeer::WriteMode WebRTCPeerGDNative::get_write_mode() const {
- ERR_FAIL_COND_V(interface == NULL, WRITE_MODE_BINARY);
- return (WriteMode)interface->get_write_mode(interface->data);
+String WebRTCDataChannelGDNative::get_label() const {
+ ERR_FAIL_COND_V(interface == NULL, "");
+ return String(interface->get_label(interface->data));
}
-bool WebRTCPeerGDNative::was_string_packet() const {
+bool WebRTCDataChannelGDNative::is_ordered() const {
ERR_FAIL_COND_V(interface == NULL, false);
- return interface->was_string_packet(interface->data);
+ return interface->is_ordered(interface->data);
}
-WebRTCPeer::ConnectionState WebRTCPeerGDNative::get_connection_state() const {
- ERR_FAIL_COND_V(interface == NULL, STATE_DISCONNECTED);
- return STATE_DISCONNECTED;
+int WebRTCDataChannelGDNative::get_id() const {
+ ERR_FAIL_COND_V(interface == NULL, -1);
+ return interface->get_id(interface->data);
+}
+
+int WebRTCDataChannelGDNative::get_max_packet_life_time() const {
+ ERR_FAIL_COND_V(interface == NULL, -1);
+ return interface->get_max_packet_life_time(interface->data);
+}
+
+int WebRTCDataChannelGDNative::get_max_retransmits() const {
+ ERR_FAIL_COND_V(interface == NULL, -1);
+ return interface->get_max_retransmits(interface->data);
+}
+
+String WebRTCDataChannelGDNative::get_protocol() const {
+ ERR_FAIL_COND_V(interface == NULL, "");
+ return String(interface->get_protocol(interface->data));
+}
+
+bool WebRTCDataChannelGDNative::is_negotiated() const {
+ ERR_FAIL_COND_V(interface == NULL, false);
+ return interface->is_negotiated(interface->data);
}
-Error WebRTCPeerGDNative::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
+Error WebRTCDataChannelGDNative::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
return (Error)interface->get_packet(interface->data, r_buffer, &r_buffer_size);
}
-Error WebRTCPeerGDNative::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
+Error WebRTCDataChannelGDNative::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
return (Error)interface->put_packet(interface->data, p_buffer, p_buffer_size);
}
-int WebRTCPeerGDNative::get_max_packet_size() const {
+int WebRTCDataChannelGDNative::get_max_packet_size() const {
ERR_FAIL_COND_V(interface == NULL, 0);
return interface->get_max_packet_size(interface->data);
}
-int WebRTCPeerGDNative::get_available_packet_count() const {
+int WebRTCDataChannelGDNative::get_available_packet_count() const {
ERR_FAIL_COND_V(interface == NULL, 0);
return interface->get_available_packet_count(interface->data);
}
-void WebRTCPeerGDNative::set_native_webrtc_peer(const godot_net_webrtc_peer *p_impl) {
+void WebRTCDataChannelGDNative::set_native_webrtc_data_channel(const godot_net_webrtc_data_channel *p_impl) {
interface = p_impl;
}
diff --git a/modules/webrtc/webrtc_peer_gdnative.h b/modules/webrtc/webrtc_data_channel_gdnative.h
index 6786cec8ea..3685f86353 100644
--- a/modules/webrtc/webrtc_peer_gdnative.h
+++ b/modules/webrtc/webrtc_data_channel_gdnative.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* webrtc_peer_gdnative.h */
+/* webrtc_data_channel_gdnative.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,37 +30,39 @@
#ifdef WEBRTC_GDNATIVE_ENABLED
-#ifndef WEBRTC_PEER_GDNATIVE_H
-#define WEBRTC_PEER_GDNATIVE_H
+#ifndef WEBRTC_DATA_CHANNEL_GDNATIVE_H
+#define WEBRTC_DATA_CHANNEL_GDNATIVE_H
#include "modules/gdnative/include/net/godot_net.h"
-#include "webrtc_peer.h"
+#include "webrtc_data_channel.h"
-class WebRTCPeerGDNative : public WebRTCPeer {
- GDCLASS(WebRTCPeerGDNative, WebRTCPeer);
+class WebRTCDataChannelGDNative : public WebRTCDataChannel {
+ GDCLASS(WebRTCDataChannelGDNative, WebRTCDataChannel);
protected:
static void _bind_methods();
private:
- const godot_net_webrtc_peer *interface;
+ const godot_net_webrtc_data_channel *interface;
public:
- static WebRTCPeer *_create() { return memnew(WebRTCPeerGDNative); }
- static void make_default() { WebRTCPeer::_create = WebRTCPeerGDNative::_create; }
-
- void set_native_webrtc_peer(const godot_net_webrtc_peer *p_impl);
+ void set_native_webrtc_data_channel(const godot_net_webrtc_data_channel *p_impl);
virtual void set_write_mode(WriteMode mode);
virtual WriteMode get_write_mode() const;
virtual bool was_string_packet() const;
- virtual ConnectionState get_connection_state() const;
- virtual Error create_offer();
- virtual Error set_remote_description(String type, String sdp);
- virtual Error set_local_description(String type, String sdp);
- virtual Error add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName);
+ virtual ChannelState get_ready_state() const;
+ virtual String get_label() const;
+ virtual bool is_ordered() const;
+ virtual int get_id() const;
+ virtual int get_max_packet_life_time() const;
+ virtual int get_max_retransmits() const;
+ virtual String get_protocol() const;
+ virtual bool is_negotiated() const;
+
virtual Error poll();
+ virtual void close();
/** Inherited from PacketPeer: **/
virtual int get_available_packet_count() const;
@@ -69,10 +71,10 @@ public:
virtual int get_max_packet_size() const;
- WebRTCPeerGDNative();
- ~WebRTCPeerGDNative();
+ WebRTCDataChannelGDNative();
+ ~WebRTCDataChannelGDNative();
};
-#endif // WEBRTC_PEER_GDNATIVE_H
+#endif // WEBRTC_DATA_CHANNEL_GDNATIVE_H
#endif // WEBRTC_GDNATIVE_ENABLED
diff --git a/modules/webrtc/webrtc_data_channel_js.cpp b/modules/webrtc/webrtc_data_channel_js.cpp
new file mode 100644
index 0000000000..2e7c64aa72
--- /dev/null
+++ b/modules/webrtc/webrtc_data_channel_js.cpp
@@ -0,0 +1,352 @@
+/*************************************************************************/
+/* webrtc_data_channel_js.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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. */
+/*************************************************************************/
+
+#ifdef JAVASCRIPT_ENABLED
+
+#include "webrtc_data_channel_js.h"
+#include "emscripten.h"
+
+extern "C" {
+EMSCRIPTEN_KEEPALIVE void _emrtc_on_ch_error(void *obj) {
+ WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(obj);
+ peer->_on_error();
+}
+
+EMSCRIPTEN_KEEPALIVE void _emrtc_on_ch_open(void *obj) {
+ WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(obj);
+ peer->_on_open();
+}
+
+EMSCRIPTEN_KEEPALIVE void _emrtc_on_ch_close(void *obj) {
+ WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(obj);
+ peer->_on_close();
+}
+
+EMSCRIPTEN_KEEPALIVE void _emrtc_on_ch_message(void *obj, uint8_t *p_data, uint32_t p_size, bool p_is_string) {
+ WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(obj);
+ peer->_on_message(p_data, p_size, p_is_string);
+}
+}
+
+void WebRTCDataChannelJS::_on_open() {
+ in_buffer.resize(16);
+}
+
+void WebRTCDataChannelJS::_on_close() {
+ close();
+}
+
+void WebRTCDataChannelJS::_on_error() {
+ close();
+}
+
+void WebRTCDataChannelJS::_on_message(uint8_t *p_data, uint32_t p_size, bool p_is_string) {
+ if (in_buffer.space_left() < p_size + 5) {
+ ERR_EXPLAIN("Buffer full! Dropping data");
+ ERR_FAIL();
+ }
+
+ uint8_t is_string = p_is_string ? 1 : 0;
+ in_buffer.write((uint8_t *)&p_size, 4);
+ in_buffer.write((uint8_t *)&is_string, 1);
+ in_buffer.write(p_data, p_size);
+ queue_count++;
+}
+
+void WebRTCDataChannelJS::close() {
+ in_buffer.resize(0);
+ queue_count = 0;
+ _was_string = false;
+ /* clang-format off */
+ EM_ASM({
+ var dict = Module.IDHandler.get($0);
+ if (!dict) return;
+ var channel = dict["channel"];
+ channel.onopen = null;
+ channel.onclose = null;
+ channel.onerror = null;
+ channel.onmessage = null;
+ channel.close();
+ }, _js_id);
+ /* clang-format on */
+}
+
+Error WebRTCDataChannelJS::poll() {
+ return OK;
+}
+
+WebRTCDataChannelJS::ChannelState WebRTCDataChannelJS::get_ready_state() const {
+ /* clang-format off */
+ return (ChannelState) EM_ASM_INT({
+ var dict = Module.IDHandler.get($0);
+ if (!dict) return 3; // CLOSED
+ var channel = dict["channel"];
+ switch(channel.readyState) {
+ case "connecting":
+ return 0;
+ case "open":
+ return 1;
+ case "closing":
+ return 2;
+ case "closed":
+ return 3;
+ }
+ return 3; // CLOSED
+ }, _js_id);
+ /* clang-format on */
+}
+
+int WebRTCDataChannelJS::get_available_packet_count() const {
+ return queue_count;
+}
+
+Error WebRTCDataChannelJS::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
+ ERR_FAIL_COND_V(get_ready_state() != STATE_OPEN, ERR_UNCONFIGURED);
+
+ if (queue_count == 0)
+ return ERR_UNAVAILABLE;
+
+ uint32_t to_read = 0;
+ uint32_t left = 0;
+ uint8_t is_string = 0;
+ r_buffer_size = 0;
+
+ in_buffer.read((uint8_t *)&to_read, 4);
+ --queue_count;
+ left = in_buffer.data_left();
+
+ if (left < to_read + 1) {
+ in_buffer.advance_read(left);
+ return FAILED;
+ }
+
+ in_buffer.read(&is_string, 1);
+ _was_string = is_string == 1;
+ in_buffer.read(packet_buffer, to_read);
+ *r_buffer = packet_buffer;
+ r_buffer_size = to_read;
+
+ return OK;
+}
+
+Error WebRTCDataChannelJS::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
+ ERR_FAIL_COND_V(get_ready_state() != STATE_OPEN, ERR_UNCONFIGURED);
+
+ int is_bin = _write_mode == WebRTCDataChannel::WRITE_MODE_BINARY ? 1 : 0;
+
+ /* clang-format off */
+ EM_ASM({
+ var dict = Module.IDHandler.get($0);
+ var channel = dict["channel"];
+ var bytes_array = new Uint8Array($2);
+ var i = 0;
+
+ for(i=0; i<$2; i++) {
+ bytes_array[i] = getValue($1+i, 'i8');
+ }
+
+ if ($3) {
+ channel.send(bytes_array.buffer);
+ } else {
+ var string = new TextDecoder("utf-8").decode(bytes_array);
+ channel.send(string);
+ }
+ }, _js_id, p_buffer, p_buffer_size, is_bin);
+ /* clang-format on */
+
+ return OK;
+}
+
+int WebRTCDataChannelJS::get_max_packet_size() const {
+ return 1200;
+}
+
+void WebRTCDataChannelJS::set_write_mode(WriteMode p_mode) {
+ _write_mode = p_mode;
+}
+
+WebRTCDataChannel::WriteMode WebRTCDataChannelJS::get_write_mode() const {
+ return _write_mode;
+}
+
+bool WebRTCDataChannelJS::was_string_packet() const {
+ return _was_string;
+}
+
+String WebRTCDataChannelJS::get_label() const {
+ return _label;
+}
+
+/* clang-format off */
+#define _JS_GET(PROP) \
+EM_ASM_INT({ \
+ var dict = Module.IDHandler.get($0); \
+ if (!dict || !dict["channel"]) { \
+ return 0; \
+ }; \
+ return dict["channel"].PROP; \
+}, _js_id)
+/* clang-format on */
+
+bool WebRTCDataChannelJS::is_ordered() const {
+ return _JS_GET(ordered);
+}
+
+int WebRTCDataChannelJS::get_id() const {
+ return _JS_GET(id);
+}
+
+int WebRTCDataChannelJS::get_max_packet_life_time() const {
+ return _JS_GET(maxPacketLifeTime);
+}
+
+int WebRTCDataChannelJS::get_max_retransmits() const {
+ return _JS_GET(maxRetransmits);
+}
+
+String WebRTCDataChannelJS::get_protocol() const {
+ return _protocol;
+}
+
+bool WebRTCDataChannelJS::is_negotiated() const {
+ return _JS_GET(negotiated);
+}
+
+WebRTCDataChannelJS::WebRTCDataChannelJS() {
+ queue_count = 0;
+ _was_string = false;
+ _write_mode = WRITE_MODE_BINARY;
+ _js_id = 0;
+}
+
+WebRTCDataChannelJS::WebRTCDataChannelJS(int js_id) {
+ queue_count = 0;
+ _was_string = false;
+ _write_mode = WRITE_MODE_BINARY;
+ _js_id = js_id;
+
+ /* clang-format off */
+ EM_ASM({
+ var c_ptr = $0;
+ var dict = Module.IDHandler.get($1);
+ if (!dict) return;
+ var channel = dict["channel"];
+ dict["ptr"] = c_ptr;
+
+ channel.binaryType = "arraybuffer";
+ channel.onopen = function (evt) {
+ ccall("_emrtc_on_ch_open",
+ "void",
+ ["number"],
+ [c_ptr]
+ );
+ };
+ channel.onclose = function (evt) {
+ ccall("_emrtc_on_ch_close",
+ "void",
+ ["number"],
+ [c_ptr]
+ );
+ };
+ channel.onerror = function (evt) {
+ ccall("_emrtc_on_ch_error",
+ "void",
+ ["number"],
+ [c_ptr]
+ );
+ };
+ channel.onmessage = function(event) {
+ var buffer;
+ var is_string = 0;
+ if (event.data instanceof ArrayBuffer) {
+ buffer = new Uint8Array(event.data);
+ } else if (event.data instanceof Blob) {
+ console.error("Blob type not supported");
+ return;
+ } else if (typeof event.data === "string") {
+ is_string = 1;
+ var enc = new TextEncoder("utf-8");
+ buffer = new Uint8Array(enc.encode(event.data));
+ } else {
+ console.error("Unknown message type");
+ return;
+ }
+ var len = buffer.length*buffer.BYTES_PER_ELEMENT;
+ var out = Module._malloc(len);
+ Module.HEAPU8.set(buffer, out);
+ ccall("_emrtc_on_ch_message",
+ "void",
+ ["number", "number", "number", "number"],
+ [c_ptr, out, len, is_string]
+ );
+ Module._free(out);
+ }
+
+ }, this, js_id);
+ // Parse label
+ char *str;
+ str = (char *)EM_ASM_INT({
+ var dict = Module.IDHandler.get($0);
+ if (!dict || !dict["channel"]) return 0;
+ var str = dict["channel"].label;
+ var len = lengthBytesUTF8(str)+1;
+ var ptr = _malloc(str);
+ stringToUTF8(str, ptr, len+1);
+ return ptr;
+ }, js_id);
+ if(str != NULL) {
+ _label.parse_utf8(str);
+ EM_ASM({ _free($0) }, str);
+ }
+ str = (char *)EM_ASM_INT({
+ var dict = Module.IDHandler.get($0);
+ if (!dict || !dict["channel"]) return 0;
+ var str = dict["channel"].protocol;
+ var len = lengthBytesUTF8(str)+1;
+ var ptr = _malloc(str);
+ stringToUTF8(str, ptr, len+1);
+ return ptr;
+ }, js_id);
+ if(str != NULL) {
+ _protocol.parse_utf8(str);
+ EM_ASM({ _free($0) }, str);
+ }
+ /* clang-format on */
+}
+
+WebRTCDataChannelJS::~WebRTCDataChannelJS() {
+ close();
+ /* clang-format off */
+ EM_ASM({
+ Module.IDHandler.remove($0);
+ }, _js_id);
+ /* clang-format on */
+};
+#endif
diff --git a/modules/webrtc/webrtc_peer_js.h b/modules/webrtc/webrtc_data_channel_js.h
index 02f0c9b55d..b87f8e9326 100644
--- a/modules/webrtc/webrtc_peer_js.h
+++ b/modules/webrtc/webrtc_data_channel_js.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* webrtc_peer_js.h */
+/* webrtc_data_channel_js.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,43 +28,53 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef WEBRTC_PEER_JS_H
-#define WEBRTC_PEER_JS_H
-
#ifdef JAVASCRIPT_ENABLED
-#include "webrtc_peer.h"
+#ifndef WEBRTC_DATA_CHANNEL_JS_H
+#define WEBRTC_DATA_CHANNEL_JS_H
+
+#include "webrtc_data_channel.h"
-class WebRTCPeerJS : public WebRTCPeer {
+class WebRTCDataChannelJS : public WebRTCDataChannel {
+ GDCLASS(WebRTCDataChannelJS, WebRTCDataChannel);
private:
- enum {
- PACKET_BUFFER_SIZE = 65536 - 5 // 4 bytes for the size, 1 for for type
- };
+ String _label;
+ String _protocol;
bool _was_string;
WriteMode _write_mode;
+ enum {
+ PACKET_BUFFER_SIZE = 65536 - 5 // 4 bytes for the size, 1 for for type
+ };
+
int _js_id;
RingBuffer<uint8_t> in_buffer;
int queue_count;
uint8_t packet_buffer[PACKET_BUFFER_SIZE];
- ConnectionState _conn_state;
public:
- static WebRTCPeer *_create() { return memnew(WebRTCPeerJS); }
- static void make_default() { WebRTCPeer::_create = WebRTCPeerJS::_create; }
+ void _on_open();
+ void _on_close();
+ void _on_error();
+ void _on_message(uint8_t *p_data, uint32_t p_size, bool p_is_string);
virtual void set_write_mode(WriteMode mode);
virtual WriteMode get_write_mode() const;
virtual bool was_string_packet() const;
- virtual ConnectionState get_connection_state() const;
- virtual Error create_offer();
- virtual Error set_remote_description(String type, String sdp);
- virtual Error set_local_description(String type, String sdp);
- virtual Error add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName);
+ virtual ChannelState get_ready_state() const;
+ virtual String get_label() const;
+ virtual bool is_ordered() const;
+ virtual int get_id() const;
+ virtual int get_max_packet_life_time() const;
+ virtual int get_max_retransmits() const;
+ virtual String get_protocol() const;
+ virtual bool is_negotiated() const;
+
virtual Error poll();
+ virtual void close();
/** Inherited from PacketPeer: **/
virtual int get_available_packet_count() const;
@@ -73,16 +83,11 @@ public:
virtual int get_max_packet_size() const;
- void close();
- void _on_open();
- void _on_close();
- void _on_error();
- void _on_message(uint8_t *p_data, uint32_t p_size, bool p_is_string);
-
- WebRTCPeerJS();
- ~WebRTCPeerJS();
+ WebRTCDataChannelJS();
+ WebRTCDataChannelJS(int js_id);
+ ~WebRTCDataChannelJS();
};
-#endif
+#endif // WEBRTC_DATA_CHANNEL_JS_H
-#endif // WEBRTC_PEER_JS_H
+#endif // JAVASCRIPT_ENABLED
diff --git a/modules/webrtc/webrtc_peer.cpp b/modules/webrtc/webrtc_peer_connection.cpp
index 30c4505df9..69c7a51a40 100644
--- a/modules/webrtc/webrtc_peer.cpp
+++ b/modules/webrtc/webrtc_peer_connection.cpp
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* webrtc_peer.cpp */
+/* webrtc_peer_connection.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,43 +28,37 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "webrtc_peer.h"
+#include "webrtc_peer_connection.h"
-WebRTCPeer *(*WebRTCPeer::_create)() = NULL;
+WebRTCPeerConnection *(*WebRTCPeerConnection::_create)() = NULL;
-Ref<WebRTCPeer> WebRTCPeer::create_ref() {
+Ref<WebRTCPeerConnection> WebRTCPeerConnection::create_ref() {
- if (!_create)
- return Ref<WebRTCPeer>();
- return Ref<WebRTCPeer>(_create());
+ return create();
}
-WebRTCPeer *WebRTCPeer::create() {
+WebRTCPeerConnection *WebRTCPeerConnection::create() {
if (!_create)
return NULL;
return _create();
}
-void WebRTCPeer::_bind_methods() {
- ClassDB::bind_method(D_METHOD("create_offer"), &WebRTCPeer::create_offer);
- ClassDB::bind_method(D_METHOD("set_local_description", "type", "sdp"), &WebRTCPeer::set_local_description);
- ClassDB::bind_method(D_METHOD("set_remote_description", "type", "sdp"), &WebRTCPeer::set_remote_description);
- ClassDB::bind_method(D_METHOD("poll"), &WebRTCPeer::poll);
- ClassDB::bind_method(D_METHOD("add_ice_candidate", "media", "index", "name"), &WebRTCPeer::add_ice_candidate);
-
- ClassDB::bind_method(D_METHOD("was_string_packet"), &WebRTCPeer::was_string_packet);
- ClassDB::bind_method(D_METHOD("set_write_mode", "write_mode"), &WebRTCPeer::set_write_mode);
- ClassDB::bind_method(D_METHOD("get_write_mode"), &WebRTCPeer::get_write_mode);
- ClassDB::bind_method(D_METHOD("get_connection_state"), &WebRTCPeer::get_connection_state);
-
- ADD_SIGNAL(MethodInfo("offer_created", PropertyInfo(Variant::STRING, "type"), PropertyInfo(Variant::STRING, "sdp")));
- ADD_SIGNAL(MethodInfo("new_ice_candidate", PropertyInfo(Variant::STRING, "media"), PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::STRING, "name")));
+void WebRTCPeerConnection::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("initialize", "configuration"), &WebRTCPeerConnection::initialize, DEFVAL(Dictionary()));
+ ClassDB::bind_method(D_METHOD("create_data_channel", "label", "options"), &WebRTCPeerConnection::create_data_channel, DEFVAL(Dictionary()));
+ ClassDB::bind_method(D_METHOD("create_offer"), &WebRTCPeerConnection::create_offer);
+ ClassDB::bind_method(D_METHOD("set_local_description", "type", "sdp"), &WebRTCPeerConnection::set_local_description);
+ ClassDB::bind_method(D_METHOD("set_remote_description", "type", "sdp"), &WebRTCPeerConnection::set_remote_description);
+ ClassDB::bind_method(D_METHOD("add_ice_candidate", "media", "index", "name"), &WebRTCPeerConnection::add_ice_candidate);
+ ClassDB::bind_method(D_METHOD("poll"), &WebRTCPeerConnection::poll);
+ ClassDB::bind_method(D_METHOD("close"), &WebRTCPeerConnection::close);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "write_mode", PROPERTY_HINT_ENUM), "set_write_mode", "get_write_mode");
+ ClassDB::bind_method(D_METHOD("get_connection_state"), &WebRTCPeerConnection::get_connection_state);
- BIND_ENUM_CONSTANT(WRITE_MODE_TEXT);
- BIND_ENUM_CONSTANT(WRITE_MODE_BINARY);
+ ADD_SIGNAL(MethodInfo("session_description_created", PropertyInfo(Variant::STRING, "type"), PropertyInfo(Variant::STRING, "sdp")));
+ ADD_SIGNAL(MethodInfo("ice_candidate_created", PropertyInfo(Variant::STRING, "media"), PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::STRING, "name")));
+ ADD_SIGNAL(MethodInfo("data_channel_received", PropertyInfo(Variant::OBJECT, "channel")));
BIND_ENUM_CONSTANT(STATE_NEW);
BIND_ENUM_CONSTANT(STATE_CONNECTING);
@@ -74,8 +68,8 @@ void WebRTCPeer::_bind_methods() {
BIND_ENUM_CONSTANT(STATE_CLOSED);
}
-WebRTCPeer::WebRTCPeer() {
+WebRTCPeerConnection::WebRTCPeerConnection() {
}
-WebRTCPeer::~WebRTCPeer() {
+WebRTCPeerConnection::~WebRTCPeerConnection() {
}
diff --git a/modules/webrtc/webrtc_peer_connection.h b/modules/webrtc/webrtc_peer_connection.h
new file mode 100644
index 0000000000..7be1390dab
--- /dev/null
+++ b/modules/webrtc/webrtc_peer_connection.h
@@ -0,0 +1,74 @@
+/*************************************************************************/
+/* webrtc_peer_connection.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 WEBRTC_PEER_CONNECTION_H
+#define WEBRTC_PEER_CONNECTION_H
+
+#include "core/io/packet_peer.h"
+#include "modules/webrtc/webrtc_data_channel.h"
+
+class WebRTCPeerConnection : public Reference {
+ GDCLASS(WebRTCPeerConnection, Reference);
+
+public:
+ enum ConnectionState {
+ STATE_NEW,
+ STATE_CONNECTING,
+ STATE_CONNECTED,
+ STATE_DISCONNECTED,
+ STATE_FAILED,
+ STATE_CLOSED
+ };
+
+protected:
+ static void _bind_methods();
+ static WebRTCPeerConnection *(*_create)();
+
+public:
+ virtual ConnectionState get_connection_state() const = 0;
+
+ virtual Error initialize(Dictionary p_config = Dictionary()) = 0;
+ virtual Ref<WebRTCDataChannel> create_data_channel(String p_label, Dictionary p_options = Dictionary()) = 0;
+ virtual Error create_offer() = 0;
+ virtual Error set_remote_description(String type, String sdp) = 0;
+ virtual Error set_local_description(String type, String sdp) = 0;
+ virtual Error add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName) = 0;
+ virtual Error poll() = 0;
+ virtual void close() = 0;
+
+ static Ref<WebRTCPeerConnection> create_ref();
+ static WebRTCPeerConnection *create();
+
+ WebRTCPeerConnection();
+ ~WebRTCPeerConnection();
+};
+
+VARIANT_ENUM_CAST(WebRTCPeerConnection::ConnectionState);
+#endif // WEBRTC_PEER_CONNECTION_H
diff --git a/modules/webrtc/webrtc_peer_connection_gdnative.cpp b/modules/webrtc/webrtc_peer_connection_gdnative.cpp
new file mode 100644
index 0000000000..af98aa750a
--- /dev/null
+++ b/modules/webrtc/webrtc_peer_connection_gdnative.cpp
@@ -0,0 +1,124 @@
+/*************************************************************************/
+/* webrtc_peer_connection_gdnative.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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. */
+/*************************************************************************/
+
+#ifdef WEBRTC_GDNATIVE_ENABLED
+
+#include "webrtc_peer_connection_gdnative.h"
+
+#include "core/io/resource_loader.h"
+#include "modules/gdnative/nativescript/nativescript.h"
+#include "webrtc_data_channel_gdnative.h"
+
+const godot_net_webrtc_library *WebRTCPeerConnectionGDNative::default_library = NULL;
+
+Error WebRTCPeerConnectionGDNative::set_default_library(const godot_net_webrtc_library *p_lib) {
+ if (default_library) {
+ const godot_net_webrtc_library *old = default_library;
+ default_library = NULL;
+ old->unregistered();
+ }
+ default_library = p_lib;
+ return OK; // Maybe add version check and fail accordingly
+}
+
+WebRTCPeerConnection *WebRTCPeerConnectionGDNative::_create() {
+
+ WebRTCPeerConnectionGDNative *obj = memnew(WebRTCPeerConnectionGDNative);
+ ERR_EXPLAIN("Default GDNative WebRTC implementation not defined.");
+ ERR_FAIL_COND_V(!default_library, obj);
+
+ // Call GDNative constructor
+ Error err = (Error)default_library->create_peer_connection(obj);
+ ERR_EXPLAIN("GDNative default library constructor returned an error");
+ ERR_FAIL_COND_V(err != OK, obj);
+
+ return obj;
+}
+
+void WebRTCPeerConnectionGDNative::_bind_methods() {
+}
+
+WebRTCPeerConnectionGDNative::WebRTCPeerConnectionGDNative() {
+ interface = NULL;
+}
+
+WebRTCPeerConnectionGDNative::~WebRTCPeerConnectionGDNative() {
+}
+
+Error WebRTCPeerConnectionGDNative::initialize(Dictionary p_config) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)interface->initialize(interface->data, (const godot_dictionary *)&p_config);
+}
+
+Ref<WebRTCDataChannel> WebRTCPeerConnectionGDNative::create_data_channel(String p_label, Dictionary p_options) {
+ ERR_FAIL_COND_V(interface == NULL, NULL);
+ return (WebRTCDataChannel *)interface->create_data_channel(interface->data, p_label.utf8().get_data(), (const godot_dictionary *)&p_options);
+}
+
+Error WebRTCPeerConnectionGDNative::create_offer() {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)interface->create_offer(interface->data);
+}
+
+Error WebRTCPeerConnectionGDNative::set_local_description(String p_type, String p_sdp) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)interface->set_local_description(interface->data, p_type.utf8().get_data(), p_sdp.utf8().get_data());
+}
+
+Error WebRTCPeerConnectionGDNative::set_remote_description(String p_type, String p_sdp) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)interface->set_remote_description(interface->data, p_type.utf8().get_data(), p_sdp.utf8().get_data());
+}
+
+Error WebRTCPeerConnectionGDNative::add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)interface->add_ice_candidate(interface->data, sdpMidName.utf8().get_data(), sdpMlineIndexName, sdpName.utf8().get_data());
+}
+
+Error WebRTCPeerConnectionGDNative::poll() {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)interface->poll(interface->data);
+}
+
+void WebRTCPeerConnectionGDNative::close() {
+ ERR_FAIL_COND(interface == NULL);
+ interface->close(interface->data);
+}
+
+WebRTCPeerConnection::ConnectionState WebRTCPeerConnectionGDNative::get_connection_state() const {
+ ERR_FAIL_COND_V(interface == NULL, STATE_DISCONNECTED);
+ return (ConnectionState)interface->get_connection_state(interface->data);
+}
+
+void WebRTCPeerConnectionGDNative::set_native_webrtc_peer_connection(const godot_net_webrtc_peer_connection *p_impl) {
+ interface = p_impl;
+}
+
+#endif // WEBRTC_GDNATIVE_ENABLED
diff --git a/modules/webrtc/webrtc_peer_connection_gdnative.h b/modules/webrtc/webrtc_peer_connection_gdnative.h
new file mode 100644
index 0000000000..0a281c3d89
--- /dev/null
+++ b/modules/webrtc/webrtc_peer_connection_gdnative.h
@@ -0,0 +1,73 @@
+/*************************************************************************/
+/* webrtc_peer_connection_gdnative.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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. */
+/*************************************************************************/
+
+#ifdef WEBRTC_GDNATIVE_ENABLED
+
+#ifndef WEBRTC_PEER_CONNECTION_GDNATIVE_H
+#define WEBRTC_PEER_CONNECTION_GDNATIVE_H
+
+#include "modules/gdnative/include/net/godot_net.h"
+#include "webrtc_peer_connection.h"
+
+class WebRTCPeerConnectionGDNative : public WebRTCPeerConnection {
+ GDCLASS(WebRTCPeerConnectionGDNative, WebRTCPeerConnection);
+
+protected:
+ static void _bind_methods();
+ static WebRTCPeerConnection *_create();
+
+private:
+ static const godot_net_webrtc_library *default_library;
+ const godot_net_webrtc_peer_connection *interface;
+
+public:
+ static Error set_default_library(const godot_net_webrtc_library *p_library);
+ static void make_default() { WebRTCPeerConnection::_create = WebRTCPeerConnectionGDNative::_create; }
+
+ void set_native_webrtc_peer_connection(const godot_net_webrtc_peer_connection *p_impl);
+
+ virtual ConnectionState get_connection_state() const;
+
+ virtual Error initialize(Dictionary p_config = Dictionary());
+ virtual Ref<WebRTCDataChannel> create_data_channel(String p_label, Dictionary p_options = Dictionary());
+ virtual Error create_offer();
+ virtual Error set_remote_description(String type, String sdp);
+ virtual Error set_local_description(String type, String sdp);
+ virtual Error add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName);
+ virtual Error poll();
+ virtual void close();
+
+ WebRTCPeerConnectionGDNative();
+ ~WebRTCPeerConnectionGDNative();
+};
+
+#endif // WEBRTC_PEER_CONNECTION_GDNATIVE_H
+
+#endif // WEBRTC_GDNATIVE_ENABLED
diff --git a/modules/webrtc/webrtc_peer_connection_js.cpp b/modules/webrtc/webrtc_peer_connection_js.cpp
new file mode 100644
index 0000000000..9758ab3644
--- /dev/null
+++ b/modules/webrtc/webrtc_peer_connection_js.cpp
@@ -0,0 +1,314 @@
+/*************************************************************************/
+/* webrtc_peer_connection_js.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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. */
+/*************************************************************************/
+
+#ifdef JAVASCRIPT_ENABLED
+
+#include "webrtc_peer_connection_js.h"
+
+#include "webrtc_data_channel_js.h"
+
+#include "core/io/json.h"
+#include "emscripten.h"
+
+extern "C" {
+EMSCRIPTEN_KEEPALIVE void _emrtc_on_ice_candidate(void *obj, char *p_MidName, int p_MlineIndexName, char *p_sdpName) {
+ WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(obj);
+ peer->emit_signal("ice_candidate_created", String(p_MidName), p_MlineIndexName, String(p_sdpName));
+}
+
+EMSCRIPTEN_KEEPALIVE void _emrtc_session_description_created(void *obj, char *p_type, char *p_offer) {
+ WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(obj);
+ peer->emit_signal("session_description_created", String(p_type), String(p_offer));
+}
+
+EMSCRIPTEN_KEEPALIVE void _emrtc_on_connection_state_changed(void *obj) {
+ WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(obj);
+ peer->_on_connection_state_changed();
+}
+
+EMSCRIPTEN_KEEPALIVE void _emrtc_on_error() {
+ ERR_PRINT("RTCPeerConnection error!");
+}
+
+EMSCRIPTEN_KEEPALIVE void _emrtc_emit_channel(void *obj, int p_id) {
+ WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(obj);
+ peer->emit_signal("data_channel_received", Ref<WebRTCDataChannelJS>(new WebRTCDataChannelJS(p_id)));
+}
+}
+
+void _emrtc_create_pc(int p_id, const Dictionary &p_config) {
+ String config = JSON::print(p_config);
+ /* clang-format off */
+ EM_ASM({
+ var dict = Module.IDHandler.get($0);
+ var c_ptr = dict["ptr"];
+ var config = JSON.parse(UTF8ToString($1));
+ // Setup local connaction
+ var conn = null;
+ try {
+ conn = new RTCPeerConnection(config);
+ } catch (e) {
+ console.log(e);
+ return;
+ }
+ conn.oniceconnectionstatechange = function(event) {
+ if (!Module.IDHandler.get($0)) return;
+ ccall("_emrtc_on_connection_state_changed", "void", ["number"], [c_ptr]);
+ };
+ conn.onicecandidate = function(event) {
+ if (!Module.IDHandler.get($0)) return;
+ if (!event.candidate) return;
+
+ var c = event.candidate;
+ // should emit on ice candidate
+ ccall("_emrtc_on_ice_candidate",
+ "void",
+ ["number", "string", "number", "string"],
+ [c_ptr, c.sdpMid, c.sdpMLineIndex, c.candidate]
+ );
+ };
+ conn.ondatachannel = function (evt) {
+ var dict = Module.IDHandler.get($0);
+ if (!dict) {
+ return;
+ }
+ var id = Module.IDHandler.add({"channel": evt.channel, "ptr": null});
+ ccall("_emrtc_emit_channel",
+ "void",
+ ["number", "number"],
+ [c_ptr, id]
+ );
+ };
+ dict["conn"] = conn;
+ }, p_id, config.utf8().get_data());
+ /* clang-format on */
+}
+
+void WebRTCPeerConnectionJS::_on_connection_state_changed() {
+ /* clang-format off */
+ _conn_state = (ConnectionState)EM_ASM_INT({
+ var dict = Module.IDHandler.get($0);
+ if (!dict) return 5; // CLOSED
+ var conn = dict["conn"];
+ switch(conn.iceConnectionState) {
+ case "new":
+ return 0;
+ case "checking":
+ return 1;
+ case "connected":
+ case "completed":
+ return 2;
+ case "disconnected":
+ return 3;
+ case "failed":
+ return 4;
+ case "closed":
+ return 5;
+ }
+ return 5; // CLOSED
+ }, _js_id);
+ /* clang-format on */
+}
+
+void WebRTCPeerConnectionJS::close() {
+ /* clang-format off */
+ EM_ASM({
+ var dict = Module.IDHandler.get($0);
+ if (!dict) return;
+ if (dict["conn"]) {
+ dict["conn"].close();
+ }
+ }, _js_id);
+ /* clang-format on */
+ _conn_state = STATE_CLOSED;
+}
+
+Error WebRTCPeerConnectionJS::create_offer() {
+ ERR_FAIL_COND_V(_conn_state != STATE_NEW, FAILED);
+
+ _conn_state = STATE_CONNECTING;
+ /* clang-format off */
+ EM_ASM({
+ var dict = Module.IDHandler.get($0);
+ var conn = dict["conn"];
+ var c_ptr = dict["ptr"];
+ var onError = function(error) {
+ console.error(error);
+ ccall("_emrtc_on_error", "void", [], []);
+ };
+ var onCreated = function(offer) {
+ ccall("_emrtc_session_description_created",
+ "void",
+ ["number", "string", "string"],
+ [c_ptr, offer.type, offer.sdp]
+ );
+ };
+ conn.createOffer().then(onCreated).catch(onError);
+ }, _js_id);
+ /* clang-format on */
+ return OK;
+}
+
+Error WebRTCPeerConnectionJS::set_local_description(String type, String sdp) {
+ /* clang-format off */
+ EM_ASM({
+ var dict = Module.IDHandler.get($0);
+ var conn = dict["conn"];
+ var c_ptr = dict["ptr"];
+ var type = UTF8ToString($1);
+ var sdp = UTF8ToString($2);
+ var onError = function(error) {
+ console.error(error);
+ ccall("_emrtc_on_error", "void", [], []);
+ };
+ conn.setLocalDescription({
+ "sdp": sdp,
+ "type": type
+ }).catch(onError);
+ }, _js_id, type.utf8().get_data(), sdp.utf8().get_data());
+ /* clang-format on */
+ return OK;
+}
+
+Error WebRTCPeerConnectionJS::set_remote_description(String type, String sdp) {
+ if (type == "offer") {
+ ERR_FAIL_COND_V(_conn_state != STATE_NEW, FAILED);
+ _conn_state = STATE_CONNECTING;
+ }
+ /* clang-format off */
+ EM_ASM({
+ var dict = Module.IDHandler.get($0);
+ var conn = dict["conn"];
+ var c_ptr = dict["ptr"];
+ var type = UTF8ToString($1);
+ var sdp = UTF8ToString($2);
+
+ var onError = function(error) {
+ console.error(error);
+ ccall("_emrtc_on_error", "void", [], []);
+ };
+ var onCreated = function(offer) {
+ ccall("_emrtc_session_description_created",
+ "void",
+ ["number", "string", "string"],
+ [c_ptr, offer.type, offer.sdp]
+ );
+ };
+ var onSet = function() {
+ if (type != "offer") {
+ return;
+ }
+ conn.createAnswer().then(onCreated);
+ };
+ conn.setRemoteDescription({
+ "sdp": sdp,
+ "type": type
+ }).then(onSet).catch(onError);
+ }, _js_id, type.utf8().get_data(), sdp.utf8().get_data());
+ /* clang-format on */
+ return OK;
+}
+
+Error WebRTCPeerConnectionJS::add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName) {
+ /* clang-format off */
+ EM_ASM({
+ var dict = Module.IDHandler.get($0);
+ var conn = dict["conn"];
+ var c_ptr = dict["ptr"];
+ var sdpMidName = UTF8ToString($1);
+ var sdpMlineIndexName = UTF8ToString($2);
+ var sdpName = UTF8ToString($3);
+ conn.addIceCandidate(new RTCIceCandidate({
+ "candidate": sdpName,
+ "sdpMid": sdpMidName,
+ "sdpMlineIndex": sdpMlineIndexName
+ }));
+ }, _js_id, sdpMidName.utf8().get_data(), sdpMlineIndexName, sdpName.utf8().get_data());
+ /* clang-format on */
+ return OK;
+}
+
+Error WebRTCPeerConnectionJS::initialize(Dictionary p_config) {
+ _emrtc_create_pc(_js_id, p_config);
+ return OK;
+}
+
+Ref<WebRTCDataChannel> WebRTCPeerConnectionJS::create_data_channel(String p_channel, Dictionary p_channel_config) {
+ String config = JSON::print(p_channel_config);
+ /* clang-format off */
+ int id = EM_ASM_INT({
+ try {
+ var dict = Module.IDHandler.get($0);
+ if (!dict) return 0;
+ var label = UTF8ToString($1);
+ var config = JSON.parse(UTF8ToString($2));
+ var conn = dict["conn"];
+ return Module.IDHandler.add({
+ "channel": conn.createDataChannel(label, config),
+ "ptr": null
+ })
+ } catch (e) {
+ return 0;
+ }
+ }, _js_id, p_channel.utf8().get_data(), config.utf8().get_data());
+ /* clang-format on */
+ ERR_FAIL_COND_V(id == 0, NULL);
+ return memnew(WebRTCDataChannelJS(id));
+}
+
+Error WebRTCPeerConnectionJS::poll() {
+ return OK;
+}
+
+WebRTCPeerConnection::ConnectionState WebRTCPeerConnectionJS::get_connection_state() const {
+ return _conn_state;
+}
+
+WebRTCPeerConnectionJS::WebRTCPeerConnectionJS() {
+ _conn_state = STATE_NEW;
+
+ /* clang-format off */
+ _js_id = EM_ASM_INT({
+ return Module.IDHandler.add({"conn": null, "ptr": $0});
+ }, this);
+ /* clang-format on */
+ Dictionary config;
+ _emrtc_create_pc(_js_id, config);
+}
+
+WebRTCPeerConnectionJS::~WebRTCPeerConnectionJS() {
+ close();
+ /* clang-format off */
+ EM_ASM({
+ Module.IDHandler.remove($0);
+ }, _js_id);
+ /* clang-format on */
+};
+#endif
diff --git a/modules/webrtc/webrtc_peer_connection_js.h b/modules/webrtc/webrtc_peer_connection_js.h
new file mode 100644
index 0000000000..43c0e3d6ee
--- /dev/null
+++ b/modules/webrtc/webrtc_peer_connection_js.h
@@ -0,0 +1,66 @@
+/*************************************************************************/
+/* webrtc_peer_connection_js.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 WEBRTC_PEER_CONNECTION_JS_H
+#define WEBRTC_PEER_CONNECTION_JS_H
+
+#ifdef JAVASCRIPT_ENABLED
+
+#include "webrtc_peer_connection.h"
+
+class WebRTCPeerConnectionJS : public WebRTCPeerConnection {
+
+private:
+ int _js_id;
+ ConnectionState _conn_state;
+
+public:
+ static WebRTCPeerConnection *_create() { return memnew(WebRTCPeerConnectionJS); }
+ static void make_default() { WebRTCPeerConnection::_create = WebRTCPeerConnectionJS::_create; }
+
+ void _on_connection_state_changed();
+ virtual ConnectionState get_connection_state() const;
+
+ virtual Error initialize(Dictionary configuration = Dictionary());
+ virtual Ref<WebRTCDataChannel> create_data_channel(String p_channel_name, Dictionary p_channel_config = Dictionary());
+ virtual Error create_offer();
+ virtual Error set_remote_description(String type, String sdp);
+ virtual Error set_local_description(String type, String sdp);
+ virtual Error add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName);
+ virtual Error poll();
+ virtual void close();
+
+ WebRTCPeerConnectionJS();
+ ~WebRTCPeerConnectionJS();
+};
+
+#endif
+
+#endif // WEBRTC_PEER_CONNECTION_JS_H
diff --git a/modules/webrtc/webrtc_peer_js.cpp b/modules/webrtc/webrtc_peer_js.cpp
deleted file mode 100644
index 1282e075ab..0000000000
--- a/modules/webrtc/webrtc_peer_js.cpp
+++ /dev/null
@@ -1,455 +0,0 @@
-/*************************************************************************/
-/* webrtc_peer_js.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
-/* */
-/* 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. */
-/*************************************************************************/
-
-#ifdef JAVASCRIPT_ENABLED
-
-#include "webrtc_peer_js.h"
-#include "emscripten.h"
-
-extern "C" {
-EMSCRIPTEN_KEEPALIVE void _emrtc_on_ice_candidate(void *obj, char *p_MidName, int p_MlineIndexName, char *p_sdpName) {
- WebRTCPeerJS *peer = static_cast<WebRTCPeerJS *>(obj);
- peer->emit_signal("new_ice_candidate", String(p_MidName), p_MlineIndexName, String(p_sdpName));
-}
-
-EMSCRIPTEN_KEEPALIVE void _emrtc_offer_created(void *obj, char *p_type, char *p_offer) {
- WebRTCPeerJS *peer = static_cast<WebRTCPeerJS *>(obj);
- peer->emit_signal("offer_created", String(p_type), String(p_offer));
-}
-
-EMSCRIPTEN_KEEPALIVE void _emrtc_on_error(void *obj) {
- WebRTCPeerJS *peer = static_cast<WebRTCPeerJS *>(obj);
- peer->_on_error();
-}
-
-EMSCRIPTEN_KEEPALIVE void _emrtc_on_open(void *obj) {
- WebRTCPeerJS *peer = static_cast<WebRTCPeerJS *>(obj);
- peer->_on_open();
-}
-
-EMSCRIPTEN_KEEPALIVE void _emrtc_on_close(void *obj) {
- WebRTCPeerJS *peer = static_cast<WebRTCPeerJS *>(obj);
- peer->_on_close();
-}
-
-EMSCRIPTEN_KEEPALIVE void _emrtc_on_message(void *obj, uint8_t *p_data, uint32_t p_size, bool p_is_string) {
- WebRTCPeerJS *peer = static_cast<WebRTCPeerJS *>(obj);
- peer->_on_message(p_data, p_size, p_is_string);
-}
-
-EMSCRIPTEN_KEEPALIVE void _emrtc_bind_channel(int p_id) {
- /* clang-format off */
- EM_ASM({
- if (!Module.IDHandler.has($0)) {
- return; // Godot Object is gone!
- }
- var dict = Module.IDHandler.get($0);
- var channel = dict["channel"];
- var c_ptr = dict["ptr"];
-
- channel.onopen = function (evt) {
- ccall("_emrtc_on_open",
- "void",
- ["number"],
- [c_ptr]
- );
- };
- channel.onclose = function (evt) {
- ccall("_emrtc_on_close",
- "void",
- ["number"],
- [c_ptr]
- );
- };
- channel.onerror = function (evt) {
- ccall("_emrtc_on_error",
- "void",
- ["number"],
- [c_ptr]
- );
- };
-
- channel.binaryType = "arraybuffer";
- channel.onmessage = function(event) {
- var buffer;
- var is_string = 0;
- if (event.data instanceof ArrayBuffer) {
-
- buffer = new Uint8Array(event.data);
-
- } else if (event.data instanceof Blob) {
-
- alert("Blob type not supported");
- return;
-
- } else if (typeof event.data === "string") {
-
- is_string = 1;
- var enc = new TextEncoder("utf-8");
- buffer = new Uint8Array(enc.encode(event.data));
-
- } else {
-
- alert("Unknown message type");
- return;
-
- }
- var len = buffer.length*buffer.BYTES_PER_ELEMENT;
- var out = Module._malloc(len);
- Module.HEAPU8.set(buffer, out);
- ccall("_emrtc_on_message",
- "void",
- ["number", "number", "number", "number"],
- [c_ptr, out, len, is_string]
- );
- Module._free(out);
- }
- }, p_id);
- /* clang-format on */
-}
-}
-
-void _emrtc_create_pc(int p_id) {
- /* clang-format off */
- EM_ASM({
- var dict = Module.IDHandler.get($0);
- var c_ptr = dict["ptr"];
- // Setup local connaction
- var conn = new RTCPeerConnection();
- conn.onicecandidate = function(event) {
- if (!Module.IDHandler.get($0)) return;
- if (!event.candidate) return;
-
- var c = event.candidate;
- // should emit on ice candidate
- ccall("_emrtc_on_ice_candidate",
- "void",
- ["number", "string", "number", "string"],
- [c_ptr, c.sdpMid, c.sdpMLineIndex, c.candidate]
- );
- };
- conn.ondatachannel = function (evt) {
- var dict = Module.IDHandler.get($0);
- if (!dict || dict["channel"]) {
- return;
- }
- var channel = evt.channel;
- dict["channel"] = channel;
- ccall("_emrtc_bind_channel",
- "void",
- ["number"],
- [$0]
- );
- };
- dict["conn"] = conn;
- }, p_id);
- /* clang-format on */
-}
-
-void WebRTCPeerJS::_on_open() {
- in_buffer.resize(16);
- _conn_state = STATE_CONNECTED;
-}
-
-void WebRTCPeerJS::_on_close() {
- close();
-}
-
-void WebRTCPeerJS::_on_error() {
- close();
- _conn_state = STATE_FAILED;
-}
-
-void WebRTCPeerJS::_on_message(uint8_t *p_data, uint32_t p_size, bool p_is_string) {
- if (in_buffer.space_left() < p_size + 5) {
- ERR_EXPLAIN("Buffer full! Dropping data");
- ERR_FAIL();
- }
-
- uint8_t is_string = p_is_string ? 1 : 0;
- in_buffer.write((uint8_t *)&p_size, 4);
- in_buffer.write((uint8_t *)&is_string, 1);
- in_buffer.write(p_data, p_size);
- queue_count++;
-}
-
-void WebRTCPeerJS::close() {
- in_buffer.resize(0);
- queue_count = 0;
- _was_string = false;
- /* clang-format off */
- EM_ASM({
- var dict = Module.IDHandler.get($0);
- if (!dict) return;
- if (dict["channel"]) {
- dict["channel"].close();
- dict["channel"] = null;
- }
- if (dict["conn"]) {
- dict["conn"].close();
- }
- }, _js_id);
- /* clang-format on */
- _conn_state = STATE_CLOSED;
-}
-
-Error WebRTCPeerJS::create_offer() {
- ERR_FAIL_COND_V(_conn_state != STATE_NEW, FAILED);
-
- _conn_state = STATE_CONNECTING;
- /* clang-format off */
- EM_ASM({
- var dict = Module.IDHandler.get($0);
- var conn = dict["conn"];
- var c_ptr = dict["ptr"];
- var onError = function(error) {
- console.log(error);
- ccall("_emrtc_on_error",
- "void",
- ["number"],
- [c_ptr]
- );
- };
- var onCreated = function(offer) {
- ccall("_emrtc_offer_created",
- "void",
- ["number", "string", "string"],
- [c_ptr, offer.type, offer.sdp]
- );
- };
-
- var channel = conn.createDataChannel("default");
- dict["channel"] = channel;
- ccall("_emrtc_bind_channel",
- "void",
- ["number"],
- [$0]
- );
- conn.createOffer().then(onCreated).catch(onError);
- }, _js_id);
- /* clang-format on */
- return OK;
-}
-
-Error WebRTCPeerJS::set_local_description(String type, String sdp) {
- /* clang-format off */
- EM_ASM({
- var dict = Module.IDHandler.get($0);
- var conn = dict["conn"];
- var c_ptr = dict["ptr"];
- var type = UTF8ToString($1);
- var sdp = UTF8ToString($2);
- var onError = function(error) {
- console.log(error);
- ccall("_emrtc_on_error",
- "void",
- ["number"],
- [c_ptr]
- );
- };
- conn.setLocalDescription({
- "sdp": sdp,
- "type": type
- }).catch(onError);
- }, _js_id, type.utf8().get_data(), sdp.utf8().get_data());
- /* clang-format on */
- return OK;
-}
-
-Error WebRTCPeerJS::set_remote_description(String type, String sdp) {
- if (type == "offer") {
- ERR_FAIL_COND_V(_conn_state != STATE_NEW, FAILED);
- _conn_state = STATE_CONNECTING;
- }
- /* clang-format off */
- EM_ASM({
- var dict = Module.IDHandler.get($0);
- var conn = dict["conn"];
- var c_ptr = dict["ptr"];
- var type = UTF8ToString($1);
- var sdp = UTF8ToString($2);
-
- var onError = function(error) {
- console.log(error);
- ccall("_emrtc_on_error",
- "void",
- ["number"],
- [c_ptr]
- );
- };
- var onCreated = function(offer) {
- ccall("_emrtc_offer_created",
- "void",
- ["number", "string", "string"],
- [c_ptr, offer.type, offer.sdp]
- );
- };
- var onSet = function() {
- if (type != "offer") {
- return;
- }
- conn.createAnswer().then(onCreated);
- };
- conn.setRemoteDescription({
- "sdp": sdp,
- "type": type
- }).then(onSet).catch(onError);
- }, _js_id, type.utf8().get_data(), sdp.utf8().get_data());
- /* clang-format on */
- return OK;
-}
-
-Error WebRTCPeerJS::add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName) {
- /* clang-format off */
- EM_ASM({
- var dict = Module.IDHandler.get($0);
- var conn = dict["conn"];
- var c_ptr = dict["ptr"];
- var sdpMidName = UTF8ToString($1);
- var sdpMlineIndexName = UTF8ToString($2);
- var sdpName = UTF8ToString($3);
- conn.addIceCandidate(new RTCIceCandidate({
- "candidate": sdpName,
- "sdpMid": sdpMidName,
- "sdpMlineIndex": sdpMlineIndexName
- }));
- }, _js_id, sdpMidName.utf8().get_data(), sdpMlineIndexName, sdpName.utf8().get_data());
- /* clang-format on */
- return OK;
-}
-
-Error WebRTCPeerJS::poll() {
- return OK;
-}
-
-WebRTCPeer::ConnectionState WebRTCPeerJS::get_connection_state() const {
- return _conn_state;
-}
-
-int WebRTCPeerJS::get_available_packet_count() const {
- return queue_count;
-}
-
-Error WebRTCPeerJS::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
- ERR_FAIL_COND_V(_conn_state != STATE_CONNECTED, ERR_UNCONFIGURED);
-
- if (queue_count == 0)
- return ERR_UNAVAILABLE;
-
- uint32_t to_read = 0;
- uint32_t left = 0;
- uint8_t is_string = 0;
- r_buffer_size = 0;
-
- in_buffer.read((uint8_t *)&to_read, 4);
- --queue_count;
- left = in_buffer.data_left();
-
- if (left < to_read + 1) {
- in_buffer.advance_read(left);
- return FAILED;
- }
-
- in_buffer.read(&is_string, 1);
- _was_string = is_string == 1;
- in_buffer.read(packet_buffer, to_read);
- *r_buffer = packet_buffer;
- r_buffer_size = to_read;
-
- return OK;
-}
-
-Error WebRTCPeerJS::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
- ERR_FAIL_COND_V(_conn_state != STATE_CONNECTED, ERR_UNCONFIGURED);
-
- int is_bin = _write_mode == WebRTCPeer::WRITE_MODE_BINARY ? 1 : 0;
-
- /* clang-format off */
- EM_ASM({
- var dict = Module.IDHandler.get($0);
- var channel = dict["channel"];
- var bytes_array = new Uint8Array($2);
- var i = 0;
-
- for(i=0; i<$2; i++) {
- bytes_array[i] = getValue($1+i, 'i8');
- }
-
- if ($3) {
- channel.send(bytes_array.buffer);
- } else {
- var string = new TextDecoder("utf-8").decode(bytes_array);
- channel.send(string);
- }
- }, _js_id, p_buffer, p_buffer_size, is_bin);
- /* clang-format on */
-
- return OK;
-}
-
-int WebRTCPeerJS::get_max_packet_size() const {
- return 1200;
-}
-
-void WebRTCPeerJS::set_write_mode(WriteMode p_mode) {
- _write_mode = p_mode;
-}
-
-WebRTCPeer::WriteMode WebRTCPeerJS::get_write_mode() const {
- return _write_mode;
-}
-
-bool WebRTCPeerJS::was_string_packet() const {
- return _was_string;
-}
-
-WebRTCPeerJS::WebRTCPeerJS() {
- queue_count = 0;
- _was_string = false;
- _write_mode = WRITE_MODE_BINARY;
- _conn_state = STATE_NEW;
-
- /* clang-format off */
- _js_id = EM_ASM_INT({
- return Module.IDHandler.add({"conn": null, "ptr": $0, "channel": null});
- }, this);
- /* clang-format on */
- _emrtc_create_pc(_js_id);
-}
-
-WebRTCPeerJS::~WebRTCPeerJS() {
- close();
- /* clang-format off */
- EM_ASM({
- Module.IDHandler.remove($0);
- }, _js_id);
- /* clang-format on */
-};
-#endif
diff --git a/platform/android/detect.py b/platform/android/detect.py
index ea70fefbc5..b7641172e4 100644
--- a/platform/android/detect.py
+++ b/platform/android/detect.py
@@ -214,13 +214,14 @@ def configure(env):
lib_sysroot = env["ANDROID_NDK_ROOT"] + "/platforms/" + env['ndk_platform'] + "/" + env['ARCH']
## Compile flags
-
- if env['android_stl']:
+ # Disable exceptions and rtti on non-tools (template) builds
+ if env['tools'] or env['android_stl']:
env.Append(CPPFLAGS=["-isystem", env["ANDROID_NDK_ROOT"] + "/sources/cxx-stl/llvm-libc++/include"])
env.Append(CPPFLAGS=["-isystem", env["ANDROID_NDK_ROOT"] + "/sources/cxx-stl/llvm-libc++abi/include"])
env.Append(CXXFLAGS=['-frtti', "-std=gnu++14"])
else:
env.Append(CXXFLAGS=['-fno-rtti', '-fno-exceptions'])
+ # Don't use dynamic_cast, necessary with no-rtti.
env.Append(CPPFLAGS=['-DNO_SAFE_CAST'])
ndk_version = get_ndk_version(env["ANDROID_NDK_ROOT"])
diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp
index 93d39859f2..ff1632cba8 100644
--- a/platform/android/os_android.cpp
+++ b/platform/android/os_android.cpp
@@ -277,7 +277,7 @@ Size2 OS_Android::get_window_size() const {
return Vector2(default_videomode.width, default_videomode.height);
}
-String OS_Android::get_name() {
+String OS_Android::get_name() const {
return "Android";
}
diff --git a/platform/android/os_android.h b/platform/android/os_android.h
index d2198b0579..4dbc96f4da 100644
--- a/platform/android/os_android.h
+++ b/platform/android/os_android.h
@@ -139,7 +139,7 @@ public:
virtual Size2 get_window_size() const;
- virtual String get_name();
+ virtual String get_name() const;
virtual MainLoop *get_main_loop() const;
virtual bool can_draw() const;
diff --git a/platform/haiku/os_haiku.cpp b/platform/haiku/os_haiku.cpp
index f3fed6669b..438b50053f 100644
--- a/platform/haiku/os_haiku.cpp
+++ b/platform/haiku/os_haiku.cpp
@@ -69,7 +69,7 @@ void OS_Haiku::run() {
main_loop->finish();
}
-String OS_Haiku::get_name() {
+String OS_Haiku::get_name() const {
return "Haiku";
}
diff --git a/platform/haiku/os_haiku.h b/platform/haiku/os_haiku.h
index 6ab006843a..e1d4cf8d87 100644
--- a/platform/haiku/os_haiku.h
+++ b/platform/haiku/os_haiku.h
@@ -74,7 +74,7 @@ public:
OS_Haiku();
void run();
- virtual String get_name();
+ virtual String get_name() const;
virtual MainLoop *get_main_loop() const;
diff --git a/platform/iphone/detect.py b/platform/iphone/detect.py
index 3ed0a4ade7..d9f710e456 100644
--- a/platform/iphone/detect.py
+++ b/platform/iphone/detect.py
@@ -115,10 +115,12 @@ def configure(env):
env.Append(CPPFLAGS=['-DNEED_LONG_INT'])
env.Append(CPPFLAGS=['-DLIBYUV_DISABLE_NEON'])
- if env['ios_exceptions']:
- env.Append(CCFLAGS=['-fexceptions'])
- else:
- env.Append(CCFLAGS=['-fno-exceptions'])
+ # Disable exceptions on non-tools (template) builds
+ if not env['tools']:
+ if env['ios_exceptions']:
+ env.Append(CCFLAGS=['-fexceptions'])
+ else:
+ env.Append(CCFLAGS=['-fno-exceptions'])
## Link flags
diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp
index d8fb5992cc..6a65cadf09 100644
--- a/platform/iphone/os_iphone.cpp
+++ b/platform/iphone/os_iphone.cpp
@@ -495,7 +495,7 @@ String OSIPhone::get_user_data_dir() const {
return data_dir;
};
-String OSIPhone::get_name() {
+String OSIPhone::get_name() const {
return "iOS";
};
diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h
index 460dfacd9b..017125209c 100644
--- a/platform/iphone/os_iphone.h
+++ b/platform/iphone/os_iphone.h
@@ -174,7 +174,7 @@ public:
void set_data_dir(String p_dir);
- virtual String get_name();
+ virtual String get_name() const;
Error shell_open(String p_uri);
diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py
index 145ce8d83d..145ac42863 100644
--- a/platform/javascript/detect.py
+++ b/platform/javascript/detect.py
@@ -110,10 +110,12 @@ def configure(env):
# once feasible also consider memory buffer size issues.
env.Append(CPPDEFINES=['NO_THREADS'])
- # These flags help keep the file size down.
- env.Append(CCFLAGS=['-fno-exceptions', '-fno-rtti'])
- # Don't use dynamic_cast, necessary with no-rtti.
- env.Append(CPPDEFINES=['NO_SAFE_CAST'])
+ # Disable exceptions and rtti on non-tools (template) builds
+ if not env['tools']:
+ # These flags help keep the file size down.
+ env.Append(CCFLAGS=['-fno-exceptions', '-fno-rtti'])
+ # Don't use dynamic_cast, necessary with no-rtti.
+ env.Append(CPPDEFINES=['NO_SAFE_CAST'])
if env['javascript_eval']:
env.Append(CPPDEFINES=['JAVASCRIPT_EVAL_ENABLED'])
diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp
index 2e3e10e222..c69e6f0cb8 100644
--- a/platform/javascript/os_javascript.cpp
+++ b/platform/javascript/os_javascript.cpp
@@ -1159,7 +1159,7 @@ Error OS_JavaScript::shell_open(String p_uri) {
return OK;
}
-String OS_JavaScript::get_name() {
+String OS_JavaScript::get_name() const {
return "HTML5";
}
diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h
index f7ce28e660..a0c7c31f2d 100644
--- a/platform/javascript/os_javascript.h
+++ b/platform/javascript/os_javascript.h
@@ -146,7 +146,7 @@ public:
virtual void set_icon(const Ref<Image> &p_icon);
String get_executable_path() const;
virtual Error shell_open(String p_uri);
- virtual String get_name();
+ virtual String get_name() const;
virtual bool can_draw() const;
virtual String get_resource_dir() const;
diff --git a/platform/osx/crash_handler_osx.mm b/platform/osx/crash_handler_osx.mm
index ed8a955ae5..e19fdf1b9f 100644
--- a/platform/osx/crash_handler_osx.mm
+++ b/platform/osx/crash_handler_osx.mm
@@ -77,7 +77,12 @@ static void handle_crash(int sig) {
void *bt_buffer[256];
size_t size = backtrace(bt_buffer, 256);
String _execpath = OS::get_singleton()->get_executable_path();
- String msg = GLOBAL_GET("debug/settings/crash_handler/message");
+
+ String msg;
+ const ProjectSettings *proj_settings = ProjectSettings::get_singleton();
+ if (proj_settings) {
+ msg = proj_settings->get("debug/settings/crash_handler/message");
+ }
// Dump the backtrace to stderr with a message to the user
fprintf(stderr, "%s: Program crashed with signal %d\n", __FUNCTION__, sig);
diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h
index 125a88ab6d..d2a6f38b01 100644
--- a/platform/osx/os_osx.h
+++ b/platform/osx/os_osx.h
@@ -165,7 +165,7 @@ public:
void wm_minimized(bool p_minimized);
- virtual String get_name();
+ virtual String get_name() const;
virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm
index fec524c04b..fc97bd4a20 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -1551,7 +1551,7 @@ void OS_OSX::delete_main_loop() {
main_loop = NULL;
}
-String OS_OSX::get_name() {
+String OS_OSX::get_name() const {
return "OSX";
}
diff --git a/platform/server/os_server.cpp b/platform/server/os_server.cpp
index 53f2a65c8e..12e53054bc 100644
--- a/platform/server/os_server.cpp
+++ b/platform/server/os_server.cpp
@@ -190,7 +190,7 @@ bool OS_Server::can_draw() const {
return false; //can never draw
};
-String OS_Server::get_name() {
+String OS_Server::get_name() const {
return "Server";
}
diff --git a/platform/server/os_server.h b/platform/server/os_server.h
index 7441064790..e3488a693d 100644
--- a/platform/server/os_server.h
+++ b/platform/server/os_server.h
@@ -93,7 +93,7 @@ protected:
virtual void set_main_loop(MainLoop *p_main_loop);
public:
- virtual String get_name();
+ virtual String get_name() const;
virtual void set_mouse_show(bool p_show);
virtual void set_mouse_grab(bool p_grab);
diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp
index 1678d351b3..f9d22481dd 100644
--- a/platform/uwp/os_uwp.cpp
+++ b/platform/uwp/os_uwp.cpp
@@ -530,7 +530,7 @@ OS::VideoMode OS_UWP::get_video_mode(int p_screen) const {
void OS_UWP::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) const {
}
-String OS_UWP::get_name() {
+String OS_UWP::get_name() const {
return "UWP";
}
diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h
index 7b00224017..1bb68bc75d 100644
--- a/platform/uwp/os_uwp.h
+++ b/platform/uwp/os_uwp.h
@@ -195,7 +195,7 @@ public:
virtual MainLoop *get_main_loop() const;
- virtual String get_name();
+ virtual String get_name() const;
virtual Date get_date(bool utc) const;
virtual Time get_time(bool utc) const;
diff --git a/platform/windows/crash_handler_windows.cpp b/platform/windows/crash_handler_windows.cpp
index 4006c4c60e..0716ee67f4 100644
--- a/platform/windows/crash_handler_windows.cpp
+++ b/platform/windows/crash_handler_windows.cpp
@@ -166,11 +166,16 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) {
line.SizeOfStruct = sizeof(line);
IMAGE_NT_HEADERS *h = ImageNtHeader(base);
DWORD image_type = h->FileHeader.Machine;
- int n = 0;
- String msg = GLOBAL_GET("debug/settings/crash_handler/message");
+
+ String msg;
+ const ProjectSettings *proj_settings = ProjectSettings::get_singleton();
+ if (proj_settings) {
+ msg = proj_settings->get("debug/settings/crash_handler/message");
+ }
fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str());
+ int n = 0;
do {
if (skip_first) {
skip_first = false;
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index c386fed367..4c6e4e96b5 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -2119,7 +2119,7 @@ void OS_Windows::request_attention() {
FlashWindowEx(&info);
}
-String OS_Windows::get_name() {
+String OS_Windows::get_name() const {
return "Windows";
}
diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h
index 0e0b9bf3f6..59aeb01b51 100644
--- a/platform/windows/os_windows.h
+++ b/platform/windows/os_windows.h
@@ -246,7 +246,7 @@ public:
virtual MainLoop *get_main_loop() const;
- virtual String get_name();
+ virtual String get_name() const;
virtual Date get_date(bool utc) const;
virtual Time get_time(bool utc) const;
diff --git a/platform/x11/crash_handler_x11.cpp b/platform/x11/crash_handler_x11.cpp
index 44d3cf1910..ca7251078f 100644
--- a/platform/x11/crash_handler_x11.cpp
+++ b/platform/x11/crash_handler_x11.cpp
@@ -53,7 +53,12 @@ static void handle_crash(int sig) {
void *bt_buffer[256];
size_t size = backtrace(bt_buffer, 256);
String _execpath = OS::get_singleton()->get_executable_path();
- String msg = GLOBAL_GET("debug/settings/crash_handler/message");
+
+ String msg;
+ const ProjectSettings *proj_settings = ProjectSettings::get_singleton();
+ if (proj_settings) {
+ msg = proj_settings->get("debug/settings/crash_handler/message");
+ }
// Dump the backtrace to stderr with a message to the user
fprintf(stderr, "%s: Program crashed with signal %d\n", __FUNCTION__, sig);
diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp
index c2e7b561d3..f034b2389b 100644
--- a/platform/x11/os_x11.cpp
+++ b/platform/x11/os_x11.cpp
@@ -534,22 +534,26 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
}
{
- Pixmap cursormask;
- XGCValues xgc;
- GC gc;
- XColor col;
- Cursor cursor;
+ // Creating an empty/transparent cursor
+
+ // Create 1x1 bitmap
+ Pixmap cursormask = XCreatePixmap(x11_display,
+ RootWindow(x11_display, DefaultScreen(x11_display)), 1, 1, 1);
- cursormask = XCreatePixmap(x11_display, RootWindow(x11_display, DefaultScreen(x11_display)), 1, 1, 1);
+ // Fill with zero
+ XGCValues xgc;
xgc.function = GXclear;
- gc = XCreateGC(x11_display, cursormask, GCFunction, &xgc);
+ GC gc = XCreateGC(x11_display, cursormask, GCFunction, &xgc);
XFillRectangle(x11_display, cursormask, gc, 0, 0, 1, 1);
- col.pixel = 0;
- col.red = 0;
- col.flags = 4;
- cursor = XCreatePixmapCursor(x11_display,
- cursormask, cursormask,
+
+ // Color value doesn't matter. Mask zero means no foreground or background will be drawn
+ XColor col = {};
+
+ Cursor cursor = XCreatePixmapCursor(x11_display,
+ cursormask, // source (using cursor mask as placeholder, since it'll all be ignored)
+ cursormask, // mask
&col, &col, 0, 0);
+
XFreePixmap(x11_display, cursormask);
XFreeGC(x11_display, gc);
@@ -2592,7 +2596,7 @@ String OS_X11::get_clipboard() const {
return ret;
}
-String OS_X11::get_name() {
+String OS_X11::get_name() const {
return "X11";
}
diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h
index a54851d4e7..ad35cdb4f9 100644
--- a/platform/x11/os_x11.h
+++ b/platform/x11/os_x11.h
@@ -216,7 +216,7 @@ protected:
bool is_window_maximize_allowed();
public:
- virtual String get_name();
+ virtual String get_name() const;
virtual void set_cursor_shape(CursorShape p_shape);
virtual CursorShape get_cursor_shape() const;
diff --git a/scene/2d/skeleton_2d.h b/scene/2d/skeleton_2d.h
index cf9877e6f8..d24c0a1561 100644
--- a/scene/2d/skeleton_2d.h
+++ b/scene/2d/skeleton_2d.h
@@ -39,6 +39,9 @@ class Bone2D : public Node2D {
GDCLASS(Bone2D, Node2D)
friend class Skeleton2D;
+#ifdef TOOLS_ENABLED
+ friend class AnimatedValuesBackup;
+#endif
Bone2D *parent_bone;
Skeleton2D *skeleton;
@@ -71,6 +74,9 @@ class Skeleton2D : public Node2D {
GDCLASS(Skeleton2D, Node2D);
friend class Bone2D;
+#ifdef TOOLS_ENABLED
+ friend class AnimatedValuesBackup;
+#endif
struct Bone {
bool operator<(const Bone &p_bone) const {
diff --git a/scene/3d/spatial.cpp b/scene/3d/spatial.cpp
index 05fd984f93..efd418e3c7 100644
--- a/scene/3d/spatial.cpp
+++ b/scene/3d/spatial.cpp
@@ -676,28 +676,29 @@ void Spatial::set_identity() {
void Spatial::look_at(const Vector3 &p_target, const Vector3 &p_up) {
- Transform lookat(get_global_transform());
- if (lookat.origin == p_target) {
+ Vector3 origin(get_global_transform().origin);
+ look_at_from_position(origin, p_target, p_up);
+}
+
+void Spatial::look_at_from_position(const Vector3 &p_pos, const Vector3 &p_target, const Vector3 &p_up) {
+
+ if (p_pos == p_target) {
ERR_EXPLAIN("Node origin and target are in the same position, look_at() failed");
ERR_FAIL();
}
- if (p_up.cross(p_target - lookat.origin) == Vector3()) {
+ if (p_up.cross(p_target - p_pos) == Vector3()) {
ERR_EXPLAIN("Up vector and direction between node origin and target are aligned, look_at() failed");
ERR_FAIL();
}
- Vector3 original_scale(lookat.basis.get_scale());
- lookat = lookat.looking_at(p_target, p_up);
- // as basis was normalized, we just need to apply original scale back
- lookat.basis.scale(original_scale);
- set_global_transform(lookat);
-}
-
-void Spatial::look_at_from_position(const Vector3 &p_pos, const Vector3 &p_target, const Vector3 &p_up) {
Transform lookat;
lookat.origin = p_pos;
+
+ Vector3 original_scale(get_global_transform().basis.get_scale());
lookat = lookat.looking_at(p_target, p_up);
+ // as basis was normalized, we just need to apply original scale back
+ lookat.basis.scale(original_scale);
set_global_transform(lookat);
}
diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp
index 1e3470cd90..60f8806b25 100644
--- a/scene/animation/animation_node_state_machine.cpp
+++ b/scene/animation/animation_node_state_machine.cpp
@@ -314,8 +314,9 @@ float AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *sm,
if (start_request_travel) {
if (!playing) {
+ String node_name = start_request;
start_request = StringName();
- ERR_EXPLAIN("Can't travel to '" + String(start_request) + "' if state machine is not active.");
+ ERR_EXPLAIN("Can't travel to '" + node_name + "' if state machine is not playing.");
ERR_FAIL_V(0);
}
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index a5955d57f7..75088c79fe 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -37,12 +37,20 @@
#ifdef TOOLS_ENABLED
#include "editor/editor_settings.h"
+#include "scene/2d/skeleton_2d.h"
void AnimatedValuesBackup::update_skeletons() {
for (int i = 0; i < entries.size(); i++) {
if (entries[i].bone_idx != -1) {
+ // 3D bone
Object::cast_to<Skeleton>(entries[i].object)->notification(Skeleton::NOTIFICATION_UPDATE_SKELETON);
+ } else {
+ Bone2D *bone = Object::cast_to<Bone2D>(entries[i].object);
+ if (bone && bone->skeleton) {
+ // 2D bone
+ bone->skeleton->_update_transform();
+ }
}
}
}
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 4f71ae63c3..76275c2420 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -1840,53 +1840,72 @@ Rect2 Control::get_anchorable_rect() const {
void Control::add_icon_override(const StringName &p_name, const Ref<Texture> &p_icon) {
- ERR_FAIL_COND(p_icon.is_null());
if (data.icon_override.has(p_name)) {
data.icon_override[p_name]->disconnect("changed", this, "_override_changed");
}
- data.icon_override[p_name] = p_icon;
- if (data.icon_override[p_name].is_valid()) {
- data.icon_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
+
+ // clear if "null" is passed instead of a icon
+ if (p_icon.is_null()) {
+ data.icon_override.erase(p_name);
+ } else {
+ data.icon_override[p_name] = p_icon;
+ if (data.icon_override[p_name].is_valid()) {
+ data.icon_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
+ }
}
notification(NOTIFICATION_THEME_CHANGED);
}
void Control::add_shader_override(const StringName &p_name, const Ref<Shader> &p_shader) {
- ERR_FAIL_COND(p_shader.is_null());
+
if (data.shader_override.has(p_name)) {
data.shader_override[p_name]->disconnect("changed", this, "_override_changed");
}
- data.shader_override[p_name] = p_shader;
- if (data.shader_override[p_name].is_valid()) {
- data.shader_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
+
+ // clear if "null" is passed instead of a shader
+ if (p_shader.is_null()) {
+ data.shader_override.erase(p_name);
+ } else {
+ data.shader_override[p_name] = p_shader;
+ if (data.shader_override[p_name].is_valid()) {
+ data.shader_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
+ }
}
notification(NOTIFICATION_THEME_CHANGED);
}
void Control::add_style_override(const StringName &p_name, const Ref<StyleBox> &p_style) {
- ERR_FAIL_COND(p_style.is_null());
if (data.style_override.has(p_name)) {
data.style_override[p_name]->disconnect("changed", this, "_override_changed");
}
- data.style_override[p_name] = p_style;
- if (data.style_override[p_name].is_valid()) {
- data.style_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
- }
+ // clear if "null" is passed instead of a style
+ if (p_style.is_null()) {
+ data.style_override.erase(p_name);
+ } else {
+ data.style_override[p_name] = p_style;
+ if (data.style_override[p_name].is_valid()) {
+ data.style_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
+ }
+ }
notification(NOTIFICATION_THEME_CHANGED);
}
void Control::add_font_override(const StringName &p_name, const Ref<Font> &p_font) {
- ERR_FAIL_COND(p_font.is_null());
if (data.font_override.has(p_name)) {
data.font_override[p_name]->disconnect("changed", this, "_override_changed");
}
- data.font_override[p_name] = p_font;
- if (data.font_override[p_name].is_valid()) {
- data.font_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
- }
+ // clear if "null" is passed instead of a font
+ if (p_font.is_null()) {
+ data.font_override.erase(p_name);
+ } else {
+ data.font_override[p_name] = p_font;
+ if (data.font_override[p_name].is_valid()) {
+ data.font_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
+ }
+ }
notification(NOTIFICATION_THEME_CHANGED);
}
void Control::add_color_override(const StringName &p_name, const Color &p_color) {
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 2d18a80833..ed74851755 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -1656,6 +1656,7 @@ LineEdit::LineEdit() {
context_menu_enabled = true;
menu = memnew(PopupMenu);
add_child(menu);
+ editable = false; // initialise to opposite first, so we get past the early-out in set_editable
set_editable(true);
menu->connect("id_pressed", this, "menu_option");
expand_to_text_length = false;
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 1a000d8da5..e039558abc 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -1106,7 +1106,7 @@ void TextEdit::_notification(int p_what) {
if (line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) {
// draw the wrap indent offset highlight
if (line_wrap_index != 0 && j == 0) {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(char_ofs + char_margin - indent_px, ofs_y, (char_ofs + char_margin), get_row_height()), cache.current_line_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(char_ofs + char_margin - indent_px, ofs_y, indent_px, get_row_height()), cache.current_line_color);
}
// if its the last char draw to end of the line
if (j == str.length() - 1) {
@@ -6661,6 +6661,7 @@ TextEdit::TextEdit() {
context_menu_enabled = true;
menu = memnew(PopupMenu);
add_child(menu);
+ readonly = true; // initialise to opposite first, so we get past the early-out in set_readonly
set_readonly(false);
menu->connect("id_pressed", this, "menu_option");
first_draw = true;
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 3f83c9975e..64cbafc717 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -732,7 +732,7 @@ public:
virtual void _update_cache() = 0;
virtual Map<int, TextEdit::HighlighterInfo> _get_line_syntax_highlighting(int p_line) = 0;
- virtual String get_name() = 0;
+ virtual String get_name() const = 0;
virtual List<String> get_supported_languages() = 0;
void set_text_editor(TextEdit *p_text_editor);
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 8bfb50c68d..5440c88fa8 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -476,6 +476,7 @@ void register_scene_types() {
ClassDB::register_virtual_class<VisualShaderNode>();
ClassDB::register_class<VisualShaderNodeInput>();
ClassDB::register_virtual_class<VisualShaderNodeOutput>();
+ ClassDB::register_class<VisualShaderNodeGroupBase>();
ClassDB::register_class<VisualShaderNodeScalarConstant>();
ClassDB::register_class<VisualShaderNodeBooleanConstant>();
ClassDB::register_class<VisualShaderNodeColorConstant>();
@@ -524,6 +525,7 @@ void register_scene_types() {
ClassDB::register_class<VisualShaderNodeIf>();
ClassDB::register_class<VisualShaderNodeSwitch>();
ClassDB::register_class<VisualShaderNodeFresnel>();
+ ClassDB::register_class<VisualShaderNodeExpression>();
ClassDB::register_class<ShaderMaterial>();
ClassDB::register_virtual_class<CanvasItem>();
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 766c7bbd9f..2e035bbd1f 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -468,6 +468,9 @@ void SpatialMaterial::_update_shader() {
if (flags[FLAG_ENSURE_CORRECT_NORMALS]) {
code += ",ensure_correct_normals";
}
+ if (flags[FLAG_USE_SHADOW_TO_OPACITY]) {
+ code += ",shadow_to_opacity";
+ }
code += ";\n";
code += "uniform vec4 albedo : hint_color;\n";
@@ -849,7 +852,7 @@ void SpatialMaterial::_update_shader() {
code += "\tALBEDO *= 1.0 - ref_amount;\n";
code += "\tALPHA = 1.0;\n";
- } else if (features[FEATURE_TRANSPARENT] || flags[FLAG_USE_ALPHA_SCISSOR] || (distance_fade == DISTANCE_FADE_PIXEL_ALPHA) || proximity_fade_enabled) {
+ } else if (features[FEATURE_TRANSPARENT] || flags[FLAG_USE_ALPHA_SCISSOR] || flags[FLAG_USE_SHADOW_TO_OPACITY] || (distance_fade == DISTANCE_FADE_PIXEL_ALPHA) || proximity_fade_enabled) {
code += "\tALPHA = albedo.a * albedo_tex.a;\n";
}
@@ -1349,7 +1352,7 @@ void SpatialMaterial::set_flag(Flags p_flag, bool p_enabled) {
return;
flags[p_flag] = p_enabled;
- if (p_flag == FLAG_USE_ALPHA_SCISSOR || p_flag == FLAG_UNSHADED) {
+ if ((p_flag == FLAG_USE_ALPHA_SCISSOR) || (p_flag == FLAG_UNSHADED) || (p_flag == FLAG_USE_SHADOW_TO_OPACITY)) {
_change_notify();
}
_queue_shader_change();
@@ -2060,6 +2063,7 @@ void SpatialMaterial::_bind_methods() {
ADD_GROUP("Flags", "flags_");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_transparent"), "set_feature", "get_feature", FEATURE_TRANSPARENT);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_use_shadow_to_opacity"), "set_flag", "get_flag", FLAG_USE_SHADOW_TO_OPACITY);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_unshaded"), "set_flag", "get_flag", FLAG_UNSHADED);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_vertex_lighting"), "set_flag", "get_flag", FLAG_USE_VERTEX_LIGHTING);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_no_depth_test"), "set_flag", "get_flag", FLAG_DISABLE_DEPTH_TEST);
@@ -2266,6 +2270,7 @@ void SpatialMaterial::_bind_methods() {
BIND_ENUM_CONSTANT(FLAG_DONT_RECEIVE_SHADOWS);
BIND_ENUM_CONSTANT(FLAG_DISABLE_AMBIENT_LIGHT);
BIND_ENUM_CONSTANT(FLAG_ENSURE_CORRECT_NORMALS);
+ BIND_ENUM_CONSTANT(FLAG_USE_SHADOW_TO_OPACITY);
BIND_ENUM_CONSTANT(FLAG_MAX);
BIND_ENUM_CONSTANT(DIFFUSE_BURLEY);
diff --git a/scene/resources/material.h b/scene/resources/material.h
index 1f7fc267af..29b45f769b 100644
--- a/scene/resources/material.h
+++ b/scene/resources/material.h
@@ -196,6 +196,7 @@ public:
FLAG_DONT_RECEIVE_SHADOWS,
FLAG_ENSURE_CORRECT_NORMALS,
FLAG_DISABLE_AMBIENT_LIGHT,
+ FLAG_USE_SHADOW_TO_OPACITY,
FLAG_MAX
};
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index b8f21948c3..1781777fcd 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -159,6 +159,7 @@ Vector2 VisualShader::get_node_position(Type p_type, int p_id) const {
ERR_FAIL_COND_V(!g->nodes.has(p_id), Vector2());
return g->nodes[p_id].position;
}
+
Ref<VisualShaderNode> VisualShader::get_node(Type p_type, int p_id) const {
ERR_FAIL_INDEX_V(p_type, TYPE_MAX, Ref<VisualShaderNode>());
const Graph *g = &graph[p_type];
@@ -269,6 +270,18 @@ bool VisualShader::can_connect_nodes(Type p_type, int p_from_node, int p_from_po
return true;
}
+void VisualShader::connect_nodes_forced(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) {
+ ERR_FAIL_INDEX(p_type, TYPE_MAX);
+ Graph *g = &graph[p_type];
+ Connection c;
+ c.from_node = p_from_node;
+ c.from_port = p_from_port;
+ c.to_node = p_to_node;
+ c.to_port = p_to_port;
+ g->connections.push_back(c);
+ _queue_update();
+}
+
Error VisualShader::connect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) {
ERR_FAIL_INDEX_V(p_type, TYPE_MAX, ERR_CANT_CONNECT);
Graph *g = &graph[p_type];
@@ -304,6 +317,7 @@ Error VisualShader::connect_nodes(Type p_type, int p_from_node, int p_from_port,
_queue_update();
return OK;
}
+
void VisualShader::disconnect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) {
ERR_FAIL_INDEX(p_type, TYPE_MAX);
Graph *g = &graph[p_type];
@@ -334,6 +348,7 @@ Array VisualShader::_get_node_connections(Type p_type) const {
return ret;
}
+
void VisualShader::get_node_connections(Type p_type, List<Connection> *r_connections) const {
ERR_FAIL_INDEX(p_type, TYPE_MAX);
const Graph *g = &graph[p_type];
@@ -477,6 +492,54 @@ String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port
#define IS_SYMBOL_CHAR(m_d) (((m_d) >= 'a' && (m_d) <= 'z') || ((m_d) >= 'A' && (m_d) <= 'Z') || ((m_d) >= '0' && (m_d) <= '9') || (m_d) == '_')
+String VisualShader::validate_port_name(const String &p_name, const List<String> &p_input_ports, const List<String> &p_output_ports) const {
+ String name = p_name;
+
+ while (name.length() && !IS_INITIAL_CHAR(name[0])) {
+ name = name.substr(1, name.length() - 1);
+ }
+
+ if (name != String()) {
+
+ String valid_name;
+
+ for (int i = 0; i < name.length(); i++) {
+ if (IS_SYMBOL_CHAR(name[i])) {
+ valid_name += String::chr(name[i]);
+ } else if (name[i] == ' ') {
+ valid_name += "_";
+ }
+ }
+
+ name = valid_name;
+ }
+
+ String valid_name = name;
+ bool is_equal = false;
+
+ for (int i = 0; i < p_input_ports.size(); i++) {
+ if (name == p_input_ports[i]) {
+ is_equal = true;
+ break;
+ }
+ }
+
+ if (!is_equal) {
+ for (int i = 0; i < p_output_ports.size(); i++) {
+ if (name == p_output_ports[i]) {
+ is_equal = true;
+ break;
+ }
+ }
+ }
+
+ if (is_equal) {
+ name = "";
+ }
+
+ return name;
+}
+
String VisualShader::validate_uniform_name(const String &p_name, const Ref<VisualShaderNodeUniform> &p_uniform) const {
String name = p_name; //validate name first
@@ -596,7 +659,7 @@ bool VisualShader::_set(const StringName &p_name, const Variant &p_value) {
Vector<int> conns = p_value;
if (conns.size() % 4 == 0) {
for (int i = 0; i < conns.size(); i += 4) {
- connect_nodes(type, conns[i + 0], conns[i + 1], conns[i + 2], conns[i + 3]);
+ connect_nodes_forced(type, conns[i + 0], conns[i + 1], conns[i + 2], conns[i + 3]);
}
}
return true;
@@ -611,6 +674,18 @@ bool VisualShader::_set(const StringName &p_name, const Variant &p_value) {
} else if (what == "position") {
set_node_position(type, id, p_value);
return true;
+ } else if (what == "size") {
+ ((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->set_size(p_value);
+ return true;
+ } else if (what == "input_ports") {
+ ((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->set_inputs(p_value);
+ return true;
+ } else if (what == "output_ports") {
+ ((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->set_outputs(p_value);
+ return true;
+ } else if (what == "expression") {
+ ((VisualShaderNodeExpression *)get_node(type, id).ptr())->set_expression(p_value);
+ return true;
}
}
return false;
@@ -668,6 +743,18 @@ bool VisualShader::_get(const StringName &p_name, Variant &r_ret) const {
} else if (what == "position") {
r_ret = get_node_position(type, id);
return true;
+ } else if (what == "size") {
+ r_ret = ((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->get_size();
+ return true;
+ } else if (what == "input_ports") {
+ r_ret = ((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->get_inputs();
+ return true;
+ } else if (what == "output_ports") {
+ r_ret = ((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->get_outputs();
+ return true;
+ } else if (what == "expression") {
+ r_ret = ((VisualShaderNodeExpression *)get_node(type, id).ptr())->get_expression();
+ return true;
}
}
return false;
@@ -727,6 +814,15 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::OBJECT, prop_name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "VisualShaderNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
}
p_list->push_back(PropertyInfo(Variant::VECTOR2, prop_name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+
+ if (Object::cast_to<VisualShaderNodeGroupBase>(E->get().node.ptr()) != NULL) {
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, prop_name + "/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/input_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/output_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ }
+ if (Object::cast_to<VisualShaderNodeExpression>(E->get().node.ptr()) != NULL) {
+ p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/expression", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ }
}
p_list->push_back(PropertyInfo(Variant::POOL_INT_ARRAY, "nodes/" + String(type_string[i]) + "/connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
}
@@ -993,26 +1089,33 @@ void VisualShader::_input_type_changed(Type p_type, int p_id) {
}
}
+void VisualShader::rebuild() {
+ dirty = true;
+ _update_shader();
+}
+
void VisualShader::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_mode", "mode"), &VisualShader::set_mode);
ClassDB::bind_method(D_METHOD("add_node", "type", "node", "position", "id"), &VisualShader::add_node);
- ClassDB::bind_method(D_METHOD("set_node_position", "type", "id", "position"), &VisualShader::set_node_position);
-
ClassDB::bind_method(D_METHOD("get_node", "type", "id"), &VisualShader::get_node);
+
+ ClassDB::bind_method(D_METHOD("set_node_position", "type", "id", "position"), &VisualShader::set_node_position);
ClassDB::bind_method(D_METHOD("get_node_position", "type", "id"), &VisualShader::get_node_position);
ClassDB::bind_method(D_METHOD("get_node_list", "type"), &VisualShader::get_node_list);
ClassDB::bind_method(D_METHOD("get_valid_node_id", "type"), &VisualShader::get_valid_node_id);
ClassDB::bind_method(D_METHOD("remove_node", "type", "id"), &VisualShader::remove_node);
+ ClassDB::bind_method(D_METHOD("rebuild"), &VisualShader::rebuild);
ClassDB::bind_method(D_METHOD("is_node_connection", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::is_node_connection);
ClassDB::bind_method(D_METHOD("can_connect_nodes", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::is_node_connection);
ClassDB::bind_method(D_METHOD("connect_nodes", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::connect_nodes);
ClassDB::bind_method(D_METHOD("disconnect_nodes", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::disconnect_nodes);
+ ClassDB::bind_method(D_METHOD("connect_nodes_forced", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::connect_nodes_forced);
ClassDB::bind_method(D_METHOD("get_node_connections", "type"), &VisualShader::_get_node_connections);
@@ -1627,3 +1730,555 @@ void VisualShaderNodeUniform::_bind_methods() {
VisualShaderNodeUniform::VisualShaderNodeUniform() {
}
+
+////////////// GroupBase
+
+String VisualShaderNodeGroupBase::get_caption() const {
+ return "Group";
+}
+
+void VisualShaderNodeGroupBase::set_size(const Vector2 &p_size) {
+ size = p_size;
+}
+
+Vector2 VisualShaderNodeGroupBase::get_size() const {
+ return size;
+}
+
+void VisualShaderNodeGroupBase::set_inputs(const String &p_inputs) {
+
+ if (inputs == p_inputs)
+ return;
+
+ clear_input_ports();
+
+ inputs = p_inputs;
+
+ Vector<String> input_strings = inputs.split(";", false);
+
+ int input_port_count = input_strings.size();
+
+ for (int i = 0; i < input_port_count; i++) {
+
+ Vector<String> arr = input_strings[i].split(",");
+
+ int port_idx = arr[0].to_int();
+ int port_type = arr[1].to_int();
+ String port_name = arr[2];
+
+ Port port;
+ port.type = (PortType)port_type;
+ port.name = port_name;
+ input_ports[port_idx] = port;
+ }
+}
+
+String VisualShaderNodeGroupBase::get_inputs() const {
+ return inputs;
+}
+
+void VisualShaderNodeGroupBase::set_outputs(const String &p_outputs) {
+
+ if (outputs == p_outputs)
+ return;
+
+ clear_output_ports();
+
+ outputs = p_outputs;
+
+ Vector<String> output_strings = outputs.split(";", false);
+
+ int output_port_count = output_strings.size();
+
+ for (int i = 0; i < output_port_count; i++) {
+
+ Vector<String> arr = output_strings[i].split(",");
+
+ int port_idx = arr[0].to_int();
+ int port_type = arr[1].to_int();
+ String port_name = arr[2];
+
+ Port port;
+ port.type = (PortType)port_type;
+ port.name = port_name;
+ output_ports[port_idx] = port;
+ }
+}
+
+String VisualShaderNodeGroupBase::get_outputs() const {
+ return outputs;
+}
+
+void VisualShaderNodeGroupBase::add_input_port(int p_id, int p_type, const String &p_name) {
+
+ String str = itos(p_id) + "," + itos(p_type) + "," + p_name + ";";
+ Vector<String> inputs_strings = inputs.split(";", false);
+ int index = 0;
+ if (p_id < inputs_strings.size()) {
+ for (int i = 0; i < inputs_strings.size(); i++) {
+ if (i == p_id) {
+ inputs = inputs.insert(index, str);
+ break;
+ }
+ index += inputs_strings[i].size();
+ }
+ } else {
+ inputs += str;
+ }
+
+ inputs_strings = inputs.split(";", false);
+ index = 0;
+
+ for (int i = 0; i < inputs_strings.size(); i++) {
+ int count = 0;
+ for (int j = 0; j < inputs_strings[i].size(); j++) {
+ if (inputs_strings[i][j] == ',') {
+ break;
+ }
+ count++;
+ }
+
+ inputs.erase(index, count);
+ inputs = inputs.insert(index, itos(i));
+ index += inputs_strings[i].size();
+ }
+
+ _apply_port_changes();
+}
+
+void VisualShaderNodeGroupBase::remove_input_port(int p_id) {
+
+ Vector<String> inputs_strings = inputs.split(";", false);
+ int count = 0;
+ int index = 0;
+ for (int i = 0; i < inputs_strings.size(); i++) {
+ Vector<String> arr = inputs_strings[i].split(",");
+ if (arr[0].to_int() == p_id) {
+ count = inputs_strings[i].size();
+ break;
+ }
+ index += inputs_strings[i].size();
+ }
+ inputs.erase(index, count);
+
+ inputs_strings = inputs.split(";", false);
+ for (int i = p_id; i < inputs_strings.size(); i++) {
+ inputs = inputs.replace_first(inputs_strings[i].split(",")[0], itos(i));
+ }
+
+ _apply_port_changes();
+}
+
+int VisualShaderNodeGroupBase::get_input_port_count() const {
+ return input_ports.size();
+}
+
+bool VisualShaderNodeGroupBase::has_input_port(int p_id) const {
+ return input_ports.has(p_id);
+}
+
+void VisualShaderNodeGroupBase::add_output_port(int p_id, int p_type, const String &p_name) {
+
+ String str = itos(p_id) + "," + itos(p_type) + "," + p_name + ";";
+ Vector<String> outputs_strings = outputs.split(";", false);
+ int index = 0;
+ if (p_id < outputs_strings.size()) {
+ for (int i = 0; i < outputs_strings.size(); i++) {
+ if (i == p_id) {
+ outputs = outputs.insert(index, str);
+ break;
+ }
+ index += outputs_strings[i].size();
+ }
+ } else {
+ outputs += str;
+ }
+
+ outputs_strings = outputs.split(";", false);
+ index = 0;
+
+ for (int i = 0; i < outputs_strings.size(); i++) {
+ int count = 0;
+ for (int j = 0; j < outputs_strings[i].size(); j++) {
+ if (outputs_strings[i][j] == ',') {
+ break;
+ }
+ count++;
+ }
+
+ outputs.erase(index, count);
+ outputs = outputs.insert(index, itos(i));
+ index += outputs_strings[i].size();
+ }
+
+ _apply_port_changes();
+}
+
+void VisualShaderNodeGroupBase::remove_output_port(int p_id) {
+
+ Vector<String> outputs_strings = outputs.split(";", false);
+ int count = 0;
+ int index = 0;
+ for (int i = 0; i < outputs_strings.size(); i++) {
+ Vector<String> arr = outputs_strings[i].split(",");
+ if (arr[0].to_int() == p_id) {
+ count = outputs_strings[i].size();
+ break;
+ }
+ index += outputs_strings[i].size();
+ }
+ outputs.erase(index, count);
+
+ outputs_strings = outputs.split(";", false);
+ for (int i = p_id; i < outputs_strings.size(); i++) {
+ outputs = outputs.replace_first(outputs_strings[i].split(",")[0], itos(i));
+ }
+
+ _apply_port_changes();
+}
+
+int VisualShaderNodeGroupBase::get_output_port_count() const {
+ return output_ports.size();
+}
+
+bool VisualShaderNodeGroupBase::has_output_port(int p_id) const {
+ return output_ports.has(p_id);
+}
+
+void VisualShaderNodeGroupBase::clear_input_ports() {
+ input_ports.clear();
+}
+
+void VisualShaderNodeGroupBase::clear_output_ports() {
+ output_ports.clear();
+}
+
+void VisualShaderNodeGroupBase::set_input_port_type(int p_id, int p_type) {
+
+ if (input_ports[p_id].type == p_type)
+ return;
+
+ Vector<String> inputs_strings = inputs.split(";", false);
+ int count = 0;
+ int index = 0;
+ for (int i = 0; i < inputs_strings.size(); i++) {
+ Vector<String> arr = inputs_strings[i].split(",");
+ if (arr[0].to_int() == p_id) {
+ index += arr[0].size();
+ count = arr[1].size() - 1;
+ break;
+ }
+ index += inputs_strings[i].size();
+ }
+
+ inputs.erase(index, count);
+
+ inputs = inputs.insert(index, itos(p_type));
+
+ _apply_port_changes();
+}
+
+VisualShaderNodeGroupBase::PortType VisualShaderNodeGroupBase::get_input_port_type(int p_id) const {
+ ERR_FAIL_COND_V(!input_ports.has(p_id), (PortType)0);
+ return input_ports[p_id].type;
+}
+
+void VisualShaderNodeGroupBase::set_input_port_name(int p_id, const String &p_name) {
+
+ if (input_ports[p_id].name == p_name)
+ return;
+
+ Vector<String> inputs_strings = inputs.split(";", false);
+ int count = 0;
+ int index = 0;
+ for (int i = 0; i < inputs_strings.size(); i++) {
+ Vector<String> arr = inputs_strings[i].split(",");
+ if (arr[0].to_int() == p_id) {
+ index += arr[0].size() + arr[1].size();
+ count = arr[2].size() - 1;
+ break;
+ }
+ index += inputs_strings[i].size();
+ }
+
+ inputs.erase(index, count);
+
+ inputs = inputs.insert(index, p_name);
+
+ _apply_port_changes();
+}
+
+String VisualShaderNodeGroupBase::get_input_port_name(int p_id) const {
+ ERR_FAIL_COND_V(!input_ports.has(p_id), "");
+ return input_ports[p_id].name;
+}
+
+void VisualShaderNodeGroupBase::set_output_port_type(int p_id, int p_type) {
+
+ if (output_ports[p_id].type == p_type)
+ return;
+
+ Vector<String> output_strings = outputs.split(";", false);
+ int count = 0;
+ int index = 0;
+ for (int i = 0; i < output_strings.size(); i++) {
+ Vector<String> arr = output_strings[i].split(",");
+ if (arr[0].to_int() == p_id) {
+ index += arr[0].size();
+ count = arr[1].size() - 1;
+ break;
+ }
+ index += output_strings[i].size();
+ }
+
+ outputs.erase(index, count);
+
+ outputs = outputs.insert(index, itos(p_type));
+
+ _apply_port_changes();
+}
+
+VisualShaderNodeGroupBase::PortType VisualShaderNodeGroupBase::get_output_port_type(int p_id) const {
+ ERR_FAIL_COND_V(!output_ports.has(p_id), (PortType)0);
+ return output_ports[p_id].type;
+}
+
+void VisualShaderNodeGroupBase::set_output_port_name(int p_id, const String &p_name) {
+ if (output_ports[p_id].name == p_name)
+ return;
+
+ Vector<String> output_strings = outputs.split(";", false);
+ int count = 0;
+ int index = 0;
+ for (int i = 0; i < output_strings.size(); i++) {
+ Vector<String> arr = output_strings[i].split(",");
+ if (arr[0].to_int() == p_id) {
+ index += arr[0].size() + arr[1].size();
+ count = arr[2].size() - 1;
+ break;
+ }
+ index += output_strings[i].size();
+ }
+
+ outputs.erase(index, count);
+
+ outputs = outputs.insert(index, p_name);
+
+ _apply_port_changes();
+}
+
+String VisualShaderNodeGroupBase::get_output_port_name(int p_id) const {
+ ERR_FAIL_COND_V(!output_ports.has(p_id), "");
+ return output_ports[p_id].name;
+}
+
+int VisualShaderNodeGroupBase::get_free_input_port_id() const {
+ return input_ports.size();
+}
+
+int VisualShaderNodeGroupBase::get_free_output_port_id() const {
+ return output_ports.size();
+}
+
+void VisualShaderNodeGroupBase::set_control(Control *p_control, int p_index) {
+ controls[p_index] = p_control;
+}
+
+Control *VisualShaderNodeGroupBase::get_control(int p_index) {
+ ERR_FAIL_COND_V(!controls.has(p_index), NULL);
+ return controls[p_index];
+}
+
+void VisualShaderNodeGroupBase::_apply_port_changes() {
+
+ Vector<String> inputs_strings = inputs.split(";", false);
+ Vector<String> outputs_strings = outputs.split(";", false);
+
+ clear_input_ports();
+ clear_output_ports();
+
+ for (int i = 0; i < inputs_strings.size(); i++) {
+ Vector<String> arr = inputs_strings[i].split(",");
+ Port port;
+ port.type = (PortType)arr[1].to_int();
+ port.name = arr[2];
+ input_ports[i] = port;
+ }
+ for (int i = 0; i < outputs_strings.size(); i++) {
+ Vector<String> arr = outputs_strings[i].split(",");
+ Port port;
+ port.type = (PortType)arr[1].to_int();
+ port.name = arr[2];
+ output_ports[i] = port;
+ }
+}
+
+void VisualShaderNodeGroupBase::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_size", "size"), &VisualShaderNodeGroupBase::set_size);
+ ClassDB::bind_method(D_METHOD("get_size"), &VisualShaderNodeGroupBase::get_size);
+
+ ClassDB::bind_method(D_METHOD("set_inputs", "inputs"), &VisualShaderNodeGroupBase::set_inputs);
+ ClassDB::bind_method(D_METHOD("get_inputs"), &VisualShaderNodeGroupBase::get_inputs);
+
+ ClassDB::bind_method(D_METHOD("set_outputs", "outputs"), &VisualShaderNodeGroupBase::set_outputs);
+ ClassDB::bind_method(D_METHOD("get_outputs"), &VisualShaderNodeGroupBase::get_outputs);
+
+ ClassDB::bind_method(D_METHOD("add_input_port", "id", "type", "name"), &VisualShaderNodeGroupBase::add_input_port);
+ ClassDB::bind_method(D_METHOD("remove_input_port", "id"), &VisualShaderNodeGroupBase::remove_input_port);
+ ClassDB::bind_method(D_METHOD("get_input_port_count"), &VisualShaderNodeGroupBase::get_input_port_count);
+ ClassDB::bind_method(D_METHOD("has_input_port", "id"), &VisualShaderNodeGroupBase::has_input_port);
+ ClassDB::bind_method(D_METHOD("clear_input_ports"), &VisualShaderNodeGroupBase::clear_input_ports);
+
+ ClassDB::bind_method(D_METHOD("add_output_port", "id", "type", "name"), &VisualShaderNodeGroupBase::add_output_port);
+ ClassDB::bind_method(D_METHOD("remove_output_port", "id"), &VisualShaderNodeGroupBase::remove_output_port);
+ ClassDB::bind_method(D_METHOD("get_output_port_count"), &VisualShaderNodeGroupBase::get_output_port_count);
+ ClassDB::bind_method(D_METHOD("has_output_port", "id"), &VisualShaderNodeGroupBase::has_output_port);
+ ClassDB::bind_method(D_METHOD("clear_output_ports"), &VisualShaderNodeGroupBase::clear_output_ports);
+
+ ClassDB::bind_method(D_METHOD("set_input_port_name"), &VisualShaderNodeGroupBase::set_input_port_name);
+ ClassDB::bind_method(D_METHOD("set_input_port_type"), &VisualShaderNodeGroupBase::set_input_port_type);
+ ClassDB::bind_method(D_METHOD("set_output_port_name"), &VisualShaderNodeGroupBase::set_output_port_name);
+ ClassDB::bind_method(D_METHOD("set_output_port_type"), &VisualShaderNodeGroupBase::set_output_port_type);
+
+ ClassDB::bind_method(D_METHOD("get_free_input_port_id"), &VisualShaderNodeGroupBase::get_free_input_port_id);
+ ClassDB::bind_method(D_METHOD("get_free_output_port_id"), &VisualShaderNodeGroupBase::get_free_output_port_id);
+
+ ClassDB::bind_method(D_METHOD("set_control", "control", "index"), &VisualShaderNodeGroupBase::set_control);
+ ClassDB::bind_method(D_METHOD("get_control", "index"), &VisualShaderNodeGroupBase::get_control);
+}
+
+String VisualShaderNodeGroupBase::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ return "";
+}
+
+VisualShaderNodeGroupBase::VisualShaderNodeGroupBase() {
+ size = Size2(0, 0);
+ inputs = "";
+ outputs = "";
+}
+
+////////////// Expression
+
+String VisualShaderNodeExpression::get_caption() const {
+ return "Expression";
+}
+
+void VisualShaderNodeExpression::set_expression(const String &p_expression) {
+ expression = p_expression;
+}
+
+void VisualShaderNodeExpression::build() {
+ emit_changed();
+}
+
+String VisualShaderNodeExpression::get_expression() const {
+ return expression;
+}
+
+String VisualShaderNodeExpression::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+
+ String _expression = expression;
+
+ _expression = _expression.insert(0, "\n");
+ _expression = _expression.replace("\n", "\n\t\t");
+
+ static Vector<String> pre_symbols;
+ if (pre_symbols.empty()) {
+ pre_symbols.push_back("\t");
+ pre_symbols.push_back("{");
+ pre_symbols.push_back("[");
+ pre_symbols.push_back("(");
+ pre_symbols.push_back(" ");
+ pre_symbols.push_back("-");
+ pre_symbols.push_back("*");
+ pre_symbols.push_back("/");
+ pre_symbols.push_back("+");
+ pre_symbols.push_back("=");
+ pre_symbols.push_back("&");
+ pre_symbols.push_back("|");
+ pre_symbols.push_back("!");
+ }
+
+ static Vector<String> post_symbols;
+ if (post_symbols.empty()) {
+ post_symbols.push_back("\0");
+ post_symbols.push_back("\t");
+ post_symbols.push_back("\n");
+ post_symbols.push_back(";");
+ post_symbols.push_back("}");
+ post_symbols.push_back("]");
+ post_symbols.push_back(")");
+ post_symbols.push_back(" ");
+ post_symbols.push_back(".");
+ post_symbols.push_back("-");
+ post_symbols.push_back("*");
+ post_symbols.push_back("/");
+ post_symbols.push_back("+");
+ post_symbols.push_back("=");
+ post_symbols.push_back("&");
+ post_symbols.push_back("|");
+ post_symbols.push_back("!");
+ }
+
+ for (int i = 0; i < get_input_port_count(); i++) {
+ for (int j = 0; j < pre_symbols.size(); j++) {
+ for (int k = 0; k < post_symbols.size(); k++) {
+ _expression = _expression.replace(pre_symbols[j] + get_input_port_name(i) + post_symbols[k], pre_symbols[j] + p_input_vars[i] + post_symbols[k]);
+ }
+ }
+ }
+ for (int i = 0; i < get_output_port_count(); i++) {
+ for (int j = 0; j < pre_symbols.size(); j++) {
+ for (int k = 0; k < post_symbols.size(); k++) {
+ _expression = _expression.replace(pre_symbols[j] + get_output_port_name(i) + post_symbols[k], pre_symbols[j] + p_output_vars[i] + post_symbols[k]);
+ }
+ }
+ }
+
+ String output_initializer;
+
+ for (int i = 0; i < get_output_port_count(); i++) {
+ int port_type = get_output_port_type(i);
+ String tk = "";
+ switch (port_type) {
+ case PORT_TYPE_SCALAR:
+ tk = "0.0";
+ break;
+ case PORT_TYPE_VECTOR:
+ tk = "vec3(0.0, 0.0, 0.0)";
+ break;
+ case PORT_TYPE_BOOLEAN:
+ tk = "false";
+ break;
+ case PORT_TYPE_TRANSFORM:
+ tk = "mat4(1.0)";
+ break;
+ default:
+ continue;
+ }
+ output_initializer += "\t" + p_output_vars[i] + "=" + tk + ";\n";
+ }
+
+ String code;
+ code += output_initializer;
+ code += "\t{";
+ code += _expression;
+ code += "\n\t}";
+
+ return code;
+}
+
+void VisualShaderNodeExpression::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_expression", "expression"), &VisualShaderNodeExpression::set_expression);
+ ClassDB::bind_method(D_METHOD("get_expression"), &VisualShaderNodeExpression::get_expression);
+
+ ClassDB::bind_method(D_METHOD("build"), &VisualShaderNodeExpression::build);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "expression"), "set_expression", "get_expression");
+}
+
+VisualShaderNodeExpression::VisualShaderNodeExpression() {
+ expression = "";
+}
diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h
index cf10de9bf5..ea39d118e4 100644
--- a/scene/resources/visual_shader.h
+++ b/scene/resources/visual_shader.h
@@ -135,7 +135,9 @@ public:
bool can_connect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) const;
Error connect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port);
void disconnect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port);
+ void connect_nodes_forced(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port);
+ void rebuild();
void get_node_connections(Type p_type, List<Connection> *r_connections) const;
void set_mode(Mode p_mode);
@@ -148,6 +150,7 @@ public:
String generate_preview_shader(Type p_type, int p_node, int p_port, Vector<DefaultTextureParam> &r_default_tex_params) const;
+ String validate_port_name(const String &p_name, const List<String> &p_input_ports, const List<String> &p_output_ports) const;
String validate_uniform_name(const String &p_name, const Ref<VisualShaderNodeUniform> &p_uniform) const;
VisualShader();
@@ -314,4 +317,93 @@ public:
VisualShaderNodeUniform();
};
+class VisualShaderNodeGroupBase : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeGroupBase, VisualShaderNode)
+private:
+ void _apply_port_changes();
+
+protected:
+ Vector2 size;
+ String inputs;
+ String outputs;
+
+ struct Port {
+ PortType type;
+ String name;
+ };
+
+ Map<int, Port> input_ports;
+ Map<int, Port> output_ports;
+ Map<int, Control *> controls;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const;
+
+ void set_size(const Vector2 &p_size);
+ Vector2 get_size() const;
+
+ void set_inputs(const String &p_inputs);
+ String get_inputs() const;
+
+ void set_outputs(const String &p_outputs);
+ String get_outputs() const;
+
+ void add_input_port(int p_id, int p_type, const String &p_name);
+ void remove_input_port(int p_id);
+ virtual int get_input_port_count() const;
+ bool has_input_port(int p_id) const;
+ void clear_input_ports();
+
+ void add_output_port(int p_id, int p_type, const String &p_name);
+ void remove_output_port(int p_id);
+ virtual int get_output_port_count() const;
+ bool has_output_port(int p_id) const;
+ void clear_output_ports();
+
+ void set_input_port_type(int p_id, int p_type);
+ virtual PortType get_input_port_type(int p_id) const;
+ void set_input_port_name(int p_id, const String &p_name);
+ virtual String get_input_port_name(int p_id) const;
+
+ void set_output_port_type(int p_id, int p_type);
+ virtual PortType get_output_port_type(int p_id) const;
+ void set_output_port_name(int p_id, const String &p_name);
+ virtual String get_output_port_name(int p_id) const;
+
+ int get_free_input_port_id() const;
+ int get_free_output_port_id() const;
+
+ void set_control(Control *p_control, int p_index);
+ Control *get_control(int p_index);
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const;
+
+ VisualShaderNodeGroupBase();
+};
+
+class VisualShaderNodeExpression : public VisualShaderNodeGroupBase {
+ GDCLASS(VisualShaderNodeExpression, VisualShaderNodeGroupBase)
+
+private:
+ String expression;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const;
+
+ void set_expression(const String &p_expression);
+ String get_expression() const;
+
+ void build();
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const;
+
+ VisualShaderNodeExpression();
+};
+
#endif // VISUAL_SHADER_H
diff --git a/servers/visual/shader_types.cpp b/servers/visual/shader_types.cpp
index 8fd5dfc738..bc6010117c 100644
--- a/servers/visual/shader_types.cpp
+++ b/servers/visual/shader_types.cpp
@@ -147,6 +147,7 @@ ShaderTypes::ShaderTypes() {
shader_modes[VS::SHADER_SPATIAL].functions["light"].built_ins["DIFFUSE_LIGHT"] = ShaderLanguage::TYPE_VEC3;
shader_modes[VS::SHADER_SPATIAL].functions["light"].built_ins["SPECULAR_LIGHT"] = ShaderLanguage::TYPE_VEC3;
shader_modes[VS::SHADER_SPATIAL].functions["light"].built_ins["OUTPUT_IS_SRGB"] = constt(ShaderLanguage::TYPE_BOOL);
+ shader_modes[VS::SHADER_SPATIAL].functions["light"].built_ins["ALPHA"] = ShaderLanguage::TYPE_FLOAT;
shader_modes[VS::SHADER_SPATIAL].functions["light"].can_discard = true;
@@ -187,6 +188,7 @@ ShaderTypes::ShaderTypes() {
shader_modes[VS::SHADER_SPATIAL].modes.push_back("shadows_disabled");
shader_modes[VS::SHADER_SPATIAL].modes.push_back("ambient_light_disabled");
+ shader_modes[VS::SHADER_SPATIAL].modes.push_back("shadow_to_opacity");
shader_modes[VS::SHADER_SPATIAL].modes.push_back("vertex_lighting");
diff --git a/thirdparty/README.md b/thirdparty/README.md
index bc820634bb..732c08fdea 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -346,7 +346,7 @@ Collection of single-file libraries used in Godot components.
* License: Public Domain
- `clipper.{cpp,hpp}`
* Upstream: https://sourceforge.net/projects/polyclipping
- * Version: 6.4.2
+ * Version: 6.4.2 + Godot changes (added optional exceptions handling)
* License: BSL-1.0
- `fastlz.{c,h}`
* Upstream: https://github.com/ariya/FastLZ
diff --git a/thirdparty/assimp/code/res/resource.h b/thirdparty/assimp/code/res/resource.h
deleted file mode 100644
index 37d39284fe..0000000000
--- a/thirdparty/assimp/code/res/resource.h
+++ /dev/null
@@ -1,14 +0,0 @@
-//{{NO_DEPENDENCIES}}
-// Microsoft Visual C++ generated include file.
-// Used by assimp.rc
-
-// Nächste Standardwerte für neue Objekte
-//
-#ifdef APSTUDIO_INVOKED
-#ifndef APSTUDIO_READONLY_SYMBOLS
-#define _APS_NEXT_RESOURCE_VALUE 101
-#define _APS_NEXT_COMMAND_VALUE 40001
-#define _APS_NEXT_CONTROL_VALUE 1001
-#define _APS_NEXT_SYMED_VALUE 101
-#endif
-#endif
diff --git a/thirdparty/misc/clipper-exceptions.patch b/thirdparty/misc/clipper-exceptions.patch
new file mode 100644
index 0000000000..537afd59b3
--- /dev/null
+++ b/thirdparty/misc/clipper-exceptions.patch
@@ -0,0 +1,154 @@
+diff --git a/thirdparty/misc/clipper.cpp b/thirdparty/misc/clipper.cpp
+index 8c3a59c4ca..c67045d113 100644
+--- a/thirdparty/misc/clipper.cpp
++++ b/thirdparty/misc/clipper.cpp
+@@ -48,6 +48,38 @@
+ #include <ostream>
+ #include <functional>
+
++//Explicitly disables exceptions handling for target platform
++//#define CLIPPER_NOEXCEPTION
++
++#define CLIPPER_THROW(exception) std::abort()
++#define CLIPPER_TRY if(true)
++#define CLIPPER_CATCH(exception) if(false)
++
++#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
++ #ifndef CLIPPER_NOEXCEPTION
++ #undef CLIPPER_THROW
++ #define CLIPPER_THROW(exception) throw exception
++ #undef CLIPPER_TRY
++ #define CLIPPER_TRY try
++ #undef CLIPPER_CATCH
++ #define CLIPPER_CATCH(exception) catch(exception)
++ #endif
++#endif
++
++//Optionally allows to override exception macros
++#if defined(CLIPPER_THROW_USER)
++ #undef CLIPPER_THROW
++ #define CLIPPER_THROW CLIPPER_THROW_USER
++#endif
++#if defined(CLIPPER_TRY_USER)
++ #undef CLIPPER_TRY
++ #define CLIPPER_TRY CLIPPER_TRY_USER
++#endif
++#if defined(CLIPPER_CATCH_USER)
++ #undef CLIPPER_CATCH
++ #define CLIPPER_CATCH CLIPPER_CATCH_USER
++#endif
++
+ namespace ClipperLib {
+
+ static double const pi = 3.141592653589793238;
+@@ -898,7 +930,7 @@ void RangeTest(const IntPoint& Pt, bool& useFullRange)
+ if (useFullRange)
+ {
+ if (Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange)
+- throw clipperException("Coordinate outside allowed range");
++ CLIPPER_THROW(clipperException("Coordinate outside allowed range"));
+ }
+ else if (Pt.X > loRange|| Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange)
+ {
+@@ -1046,10 +1078,10 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed)
+ {
+ #ifdef use_lines
+ if (!Closed && PolyTyp == ptClip)
+- throw clipperException("AddPath: Open paths must be subject.");
++ CLIPPER_THROW(clipperException("AddPath: Open paths must be subject."));
+ #else
+ if (!Closed)
+- throw clipperException("AddPath: Open paths have been disabled.");
++ CLIPPER_THROW(clipperException("AddPath: Open paths have been disabled."));
+ #endif
+
+ int highI = (int)pg.size() -1;
+@@ -1062,7 +1094,7 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed)
+
+ bool IsFlat = true;
+ //1. Basic (first) edge initialization ...
+- try
++ CLIPPER_TRY
+ {
+ edges[1].Curr = pg[1];
+ RangeTest(pg[0], m_UseFullRange);
+@@ -1075,10 +1107,10 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed)
+ InitEdge(&edges[i], &edges[i+1], &edges[i-1], pg[i]);
+ }
+ }
+- catch(...)
++ CLIPPER_CATCH(...)
+ {
+ delete [] edges;
+- throw; //range test fails
++ CLIPPER_THROW(); //range test fails
+ }
+ TEdge *eStart = &edges[0];
+
+@@ -1442,7 +1474,7 @@ void ClipperBase::SwapPositionsInAEL(TEdge *Edge1, TEdge *Edge2)
+ void ClipperBase::UpdateEdgeIntoAEL(TEdge *&e)
+ {
+ if (!e->NextInLML)
+- throw clipperException("UpdateEdgeIntoAEL: invalid call");
++ CLIPPER_THROW(clipperException("UpdateEdgeIntoAEL: invalid call"));
+
+ e->NextInLML->OutIdx = e->OutIdx;
+ TEdge* AelPrev = e->PrevInAEL;
+@@ -1510,7 +1542,7 @@ bool Clipper::Execute(ClipType clipType, Paths &solution,
+ {
+ if( m_ExecuteLocked ) return false;
+ if (m_HasOpenPaths)
+- throw clipperException("Error: PolyTree struct is needed for open path clipping.");
++ CLIPPER_THROW(clipperException("Error: PolyTree struct is needed for open path clipping."));
+ m_ExecuteLocked = true;
+ solution.resize(0);
+ m_SubjFillType = subjFillType;
+@@ -1560,7 +1592,7 @@ void Clipper::FixHoleLinkage(OutRec &outrec)
+ bool Clipper::ExecuteInternal()
+ {
+ bool succeeded = true;
+- try {
++ CLIPPER_TRY {
+ Reset();
+ m_Maxima = MaximaList();
+ m_SortedEdges = 0;
+@@ -1583,7 +1615,7 @@ bool Clipper::ExecuteInternal()
+ InsertLocalMinimaIntoAEL(botY);
+ }
+ }
+- catch(...)
++ CLIPPER_CATCH(...)
+ {
+ succeeded = false;
+ }
+@@ -2827,18 +2859,18 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge)
+ bool Clipper::ProcessIntersections(const cInt topY)
+ {
+ if( !m_ActiveEdges ) return true;
+- try {
++ CLIPPER_TRY {
+ BuildIntersectList(topY);
+ size_t IlSize = m_IntersectList.size();
+ if (IlSize == 0) return true;
+ if (IlSize == 1 || FixupIntersectionOrder()) ProcessIntersectList();
+ else return false;
+ }
+- catch(...)
++ CLIPPER_CATCH(...)
+ {
+ m_SortedEdges = 0;
+ DisposeIntersectNodes();
+- throw clipperException("ProcessIntersections error");
++ CLIPPER_THROW(clipperException("ProcessIntersections error"));
+ }
+ m_SortedEdges = 0;
+ return true;
+@@ -3002,7 +3034,7 @@ void Clipper::DoMaxima(TEdge *e)
+ DeleteFromAEL(eMaxPair);
+ }
+ #endif
+- else throw clipperException("DoMaxima error");
++ else CLIPPER_THROW(clipperException("DoMaxima error"));
+ }
+ //------------------------------------------------------------------------------
+
diff --git a/thirdparty/misc/clipper.cpp b/thirdparty/misc/clipper.cpp
index d3143fe5ab..c67045d113 100644
--- a/thirdparty/misc/clipper.cpp
+++ b/thirdparty/misc/clipper.cpp
@@ -48,6 +48,38 @@
#include <ostream>
#include <functional>
+//Explicitly disables exceptions handling for target platform
+//#define CLIPPER_NOEXCEPTION
+
+#define CLIPPER_THROW(exception) std::abort()
+#define CLIPPER_TRY if(true)
+#define CLIPPER_CATCH(exception) if(false)
+
+#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
+ #ifndef CLIPPER_NOEXCEPTION
+ #undef CLIPPER_THROW
+ #define CLIPPER_THROW(exception) throw exception
+ #undef CLIPPER_TRY
+ #define CLIPPER_TRY try
+ #undef CLIPPER_CATCH
+ #define CLIPPER_CATCH(exception) catch(exception)
+ #endif
+#endif
+
+//Optionally allows to override exception macros
+#if defined(CLIPPER_THROW_USER)
+ #undef CLIPPER_THROW
+ #define CLIPPER_THROW CLIPPER_THROW_USER
+#endif
+#if defined(CLIPPER_TRY_USER)
+ #undef CLIPPER_TRY
+ #define CLIPPER_TRY CLIPPER_TRY_USER
+#endif
+#if defined(CLIPPER_CATCH_USER)
+ #undef CLIPPER_CATCH
+ #define CLIPPER_CATCH CLIPPER_CATCH_USER
+#endif
+
namespace ClipperLib {
static double const pi = 3.141592653589793238;
@@ -898,7 +930,7 @@ void RangeTest(const IntPoint& Pt, bool& useFullRange)
if (useFullRange)
{
if (Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange)
- throw clipperException("Coordinate outside allowed range");
+ CLIPPER_THROW(clipperException("Coordinate outside allowed range"));
}
else if (Pt.X > loRange|| Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange)
{
@@ -1046,10 +1078,10 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed)
{
#ifdef use_lines
if (!Closed && PolyTyp == ptClip)
- throw clipperException("AddPath: Open paths must be subject.");
+ CLIPPER_THROW(clipperException("AddPath: Open paths must be subject."));
#else
if (!Closed)
- throw clipperException("AddPath: Open paths have been disabled.");
+ CLIPPER_THROW(clipperException("AddPath: Open paths have been disabled."));
#endif
int highI = (int)pg.size() -1;
@@ -1062,7 +1094,7 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed)
bool IsFlat = true;
//1. Basic (first) edge initialization ...
- try
+ CLIPPER_TRY
{
edges[1].Curr = pg[1];
RangeTest(pg[0], m_UseFullRange);
@@ -1075,10 +1107,10 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed)
InitEdge(&edges[i], &edges[i+1], &edges[i-1], pg[i]);
}
}
- catch(...)
+ CLIPPER_CATCH(...)
{
delete [] edges;
- throw; //range test fails
+ CLIPPER_THROW(); //range test fails
}
TEdge *eStart = &edges[0];
@@ -1442,7 +1474,7 @@ void ClipperBase::SwapPositionsInAEL(TEdge *Edge1, TEdge *Edge2)
void ClipperBase::UpdateEdgeIntoAEL(TEdge *&e)
{
if (!e->NextInLML)
- throw clipperException("UpdateEdgeIntoAEL: invalid call");
+ CLIPPER_THROW(clipperException("UpdateEdgeIntoAEL: invalid call"));
e->NextInLML->OutIdx = e->OutIdx;
TEdge* AelPrev = e->PrevInAEL;
@@ -1510,7 +1542,7 @@ bool Clipper::Execute(ClipType clipType, Paths &solution,
{
if( m_ExecuteLocked ) return false;
if (m_HasOpenPaths)
- throw clipperException("Error: PolyTree struct is needed for open path clipping.");
+ CLIPPER_THROW(clipperException("Error: PolyTree struct is needed for open path clipping."));
m_ExecuteLocked = true;
solution.resize(0);
m_SubjFillType = subjFillType;
@@ -1560,7 +1592,7 @@ void Clipper::FixHoleLinkage(OutRec &outrec)
bool Clipper::ExecuteInternal()
{
bool succeeded = true;
- try {
+ CLIPPER_TRY {
Reset();
m_Maxima = MaximaList();
m_SortedEdges = 0;
@@ -1583,7 +1615,7 @@ bool Clipper::ExecuteInternal()
InsertLocalMinimaIntoAEL(botY);
}
}
- catch(...)
+ CLIPPER_CATCH(...)
{
succeeded = false;
}
@@ -2827,18 +2859,18 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge)
bool Clipper::ProcessIntersections(const cInt topY)
{
if( !m_ActiveEdges ) return true;
- try {
+ CLIPPER_TRY {
BuildIntersectList(topY);
size_t IlSize = m_IntersectList.size();
if (IlSize == 0) return true;
if (IlSize == 1 || FixupIntersectionOrder()) ProcessIntersectList();
else return false;
}
- catch(...)
+ CLIPPER_CATCH(...)
{
m_SortedEdges = 0;
DisposeIntersectNodes();
- throw clipperException("ProcessIntersections error");
+ CLIPPER_THROW(clipperException("ProcessIntersections error"));
}
m_SortedEdges = 0;
return true;
@@ -3002,7 +3034,7 @@ void Clipper::DoMaxima(TEdge *e)
DeleteFromAEL(eMaxPair);
}
#endif
- else throw clipperException("DoMaxima error");
+ else CLIPPER_THROW(clipperException("DoMaxima error"));
}
//------------------------------------------------------------------------------
@@ -4329,10 +4361,10 @@ double DistanceFromLineSqrd(
const IntPoint& pt, const IntPoint& ln1, const IntPoint& ln2)
{
//The equation of a line in general form (Ax + By + C = 0)
- //given 2 points (x¹,y¹) & (x²,y²) is ...
- //(y¹ - y²)x + (x² - x¹)y + (y² - y¹)x¹ - (x² - x¹)y¹ = 0
- //A = (y¹ - y²); B = (x² - x¹); C = (y² - y¹)x¹ - (x² - x¹)y¹
- //perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²)
+ //given 2 points (x¹,y¹) & (x²,y²) is ...
+ //(y¹ - y²)x + (x² - x¹)y + (y² - y¹)x¹ - (x² - x¹)y¹ = 0
+ //A = (y¹ - y²); B = (x² - x¹); C = (y² - y¹)x¹ - (x² - x¹)y¹
+ //perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²)
//see http://en.wikipedia.org/wiki/Perpendicular_distance
double A = double(ln1.Y - ln2.Y);
double B = double(ln2.X - ln1.X);
diff --git a/thirdparty/xatlas/xatlas.cpp b/thirdparty/xatlas/xatlas.cpp
index eb0824a517..2cc2905eee 100644
--- a/thirdparty/xatlas/xatlas.cpp
+++ b/thirdparty/xatlas/xatlas.cpp
@@ -4388,7 +4388,7 @@ private:
class Solver
{
public:
- // Solve the symmetric system: At·A·x = At·b
+ // Solve the symmetric system: At·A·x = At·b
static bool LeastSquaresSolver(const sparse::Matrix &A, const FullVector &b, FullVector &x, float epsilon = 1e-5f)
{
xaDebugAssert(A.width() == x.dimension());
@@ -4477,22 +4477,22 @@ private:
* Gradient method.
*
* Solving sparse linear systems:
- * (1) A·x = b
+ * (1) A·x = b
*
* The conjugate gradient algorithm solves (1) only in the case that A is
* symmetric and positive definite. It is based on the idea of minimizing the
* function
*
- * (2) f(x) = 1/2·x·A·x - b·x
+ * (2) f(x) = 1/2·x·A·x - b·x
*
* This function is minimized when its gradient
*
- * (3) df = A·x - b
+ * (3) df = A·x - b
*
* is zero, which is equivalent to (1). The minimization is carried out by
* generating a succession of search directions p.k and improved minimizers x.k.
- * At each stage a quantity alfa.k is found that minimizes f(x.k + alfa.k·p.k),
- * and x.k+1 is set equal to the new point x.k + alfa.k·p.k. The p.k and x.k are
+ * At each stage a quantity alfa.k is found that minimizes f(x.k + alfa.k·p.k),
+ * and x.k+1 is set equal to the new point x.k + alfa.k·p.k. The p.k and x.k are
* built up in such a way that x.k+1 is also the minimizer of f over the whole
* vector space of directions already taken, {p.1, p.2, . . . , p.k}. After N
* iterations you arrive at the minimizer over the entire vector space, i.e., the
@@ -4520,7 +4520,7 @@ private:
float delta_new;
float alpha;
float beta;
- // r = b - A·x;
+ // r = b - A·x;
sparse::copy(b, r);
sparse::sgemv(-1, A, x, 1, r);
// p = r;
@@ -4529,24 +4529,24 @@ private:
delta_0 = delta_new;
while (i < i_max && delta_new > epsilon * epsilon * delta_0) {
i++;
- // q = A·p
+ // q = A·p
mult(A, p, q);
- // alpha = delta_new / p·q
+ // alpha = delta_new / p·q
alpha = delta_new / sparse::dot( p, q );
- // x = alfa·p + x
+ // x = alfa·p + x
sparse::saxpy(alpha, p, x);
if ((i & 31) == 0) { // recompute r after 32 steps
- // r = b - A·x
+ // r = b - A·x
sparse::copy(b, r);
sparse::sgemv(-1, A, x, 1, r);
} else {
- // r = r - alpha·q
+ // r = r - alpha·q
sparse::saxpy(-alpha, q, r);
}
delta_old = delta_new;
delta_new = sparse::dot( r, r );
beta = delta_new / delta_old;
- // p = beta·p + r
+ // p = beta·p + r
sparse::scal(beta, p);
sparse::saxpy(1, r, p);
}
@@ -4572,35 +4572,35 @@ private:
float delta_new;
float alpha;
float beta;
- // r = b - A·x
+ // r = b - A·x
sparse::copy(b, r);
sparse::sgemv(-1, A, x, 1, r);
- // p = M^-1 · r
+ // p = M^-1 · r
preconditioner.apply(r, p);
delta_new = sparse::dot(r, p);
delta_0 = delta_new;
while (i < i_max && delta_new > epsilon * epsilon * delta_0) {
i++;
- // q = A·p
+ // q = A·p
mult(A, p, q);
- // alpha = delta_new / p·q
+ // alpha = delta_new / p·q
alpha = delta_new / sparse::dot(p, q);
- // x = alfa·p + x
+ // x = alfa·p + x
sparse::saxpy(alpha, p, x);
if ((i & 31) == 0) { // recompute r after 32 steps
- // r = b - A·x
+ // r = b - A·x
sparse::copy(b, r);
sparse::sgemv(-1, A, x, 1, r);
} else {
- // r = r - alfa·q
+ // r = r - alfa·q
sparse::saxpy(-alpha, q, r);
}
- // s = M^-1 · r
+ // s = M^-1 · r
preconditioner.apply(r, s);
delta_old = delta_new;
delta_new = sparse::dot( r, s );
beta = delta_new / delta_old;
- // p = s + beta·p
+ // p = s + beta·p
sparse::scal(beta, p);
sparse::saxpy(1, s, p);
}