summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/csg/csg.cpp2117
-rw-r--r--modules/csg/csg.h158
-rw-r--r--modules/csg/csg_shape.cpp21
-rw-r--r--modules/dds/texture_loader_dds.cpp2
-rw-r--r--modules/dds/texture_loader_dds.h2
-rw-r--r--modules/etc/image_etc.cpp6
-rw-r--r--modules/etc/texture_loader_pkm.cpp2
-rw-r--r--modules/etc/texture_loader_pkm.h2
-rw-r--r--modules/gdnative/gdnative.cpp2
-rw-r--r--modules/gdnative/gdnative.h2
-rw-r--r--modules/gdnative/gdnative_library_editor_plugin.cpp22
-rw-r--r--modules/gdnative/gdnative_library_singleton_editor.cpp6
-rw-r--r--modules/gdnative/nativescript/nativescript.cpp2
-rw-r--r--modules/gdnative/nativescript/nativescript.h2
-rw-r--r--modules/gdnative/pluginscript/pluginscript_loader.cpp2
-rw-r--r--modules/gdnative/pluginscript/pluginscript_loader.h2
-rw-r--r--modules/gdnative/videodecoder/video_stream_gdnative.cpp2
-rw-r--r--modules/gdnative/videodecoder/video_stream_gdnative.h2
-rw-r--r--modules/gdnavigation/navigation_mesh_editor_plugin.cpp7
-rw-r--r--modules/gdscript/gdscript.cpp2
-rw-r--r--modules/gdscript/gdscript.h2
-rw-r--r--modules/gridmap/grid_map_editor_plugin.cpp50
-rw-r--r--modules/mono/csharp_script.cpp2
-rw-r--r--modules/mono/csharp_script.h2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs31
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs2
-rw-r--r--modules/opensimplex/noise_texture.cpp5
-rw-r--r--modules/opus/audio_stream_opus.cpp2
-rw-r--r--modules/opus/audio_stream_opus.h2
-rw-r--r--modules/pvr/texture_loader_pvr.cpp2
-rw-r--r--modules/pvr/texture_loader_pvr.h2
-rw-r--r--modules/stb_vorbis/audio_stream_ogg_vorbis.cpp3
-rw-r--r--modules/theora/video_stream_theora.cpp2
-rw-r--r--modules/theora/video_stream_theora.h2
-rw-r--r--modules/visual_script/visual_script.cpp12
-rw-r--r--modules/visual_script/visual_script_editor.cpp164
-rw-r--r--modules/visual_script/visual_script_nodes.cpp4
-rw-r--r--modules/visual_script/visual_script_property_selector.cpp23
-rw-r--r--modules/visual_script/visual_script_property_selector.h10
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.cpp2
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.h2
-rw-r--r--modules/webm/video_stream_webm.cpp2
-rw-r--r--modules/webm/video_stream_webm.h2
43 files changed, 1267 insertions, 1426 deletions
diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp
index 36055ce840..e9ca1d3e5b 100644
--- a/modules/csg/csg.cpp
+++ b/modules/csg/csg.cpp
@@ -29,19 +29,159 @@
/*************************************************************************/
#include "csg.h"
-#include "core/math/face3.h"
+
#include "core/math/geometry.h"
-#include "core/os/os.h"
+#include "core/math/math_funcs.h"
#include "core/sort_array.h"
-#include "thirdparty/misc/triangulator.h"
-void CSGBrush::clear() {
- faces.clear();
+// Static helper functions.
+
+inline static bool is_snapable(const Vector3 &p_point1, const Vector3 &p_point2, real_t p_distance) {
+
+ return (p_point1 - p_point2).length_squared() < p_distance * p_distance;
+}
+
+inline static Vector2 interpolate_segment_uv(const Vector2 p_segement_points[2], const Vector2 p_uvs[2], const Vector2 &p_interpolation_point) {
+
+ float segment_length = (p_segement_points[1] - p_segement_points[0]).length();
+ if (segment_length < CMP_EPSILON)
+ return p_uvs[0];
+
+ float distance = (p_interpolation_point - p_segement_points[0]).length();
+ float fraction = distance / segment_length;
+
+ return p_uvs[0].linear_interpolate(p_uvs[1], fraction);
+}
+
+inline static Vector2 interpolate_triangle_uv(const Vector2 p_vertices[3], const Vector2 p_uvs[3], const Vector2 &p_interpolation_point) {
+
+ if (p_interpolation_point.distance_squared_to(p_vertices[0]) < CMP_EPSILON2)
+ return p_uvs[0];
+ if (p_interpolation_point.distance_squared_to(p_vertices[1]) < CMP_EPSILON2)
+ return p_uvs[1];
+ if (p_interpolation_point.distance_squared_to(p_vertices[2]) < CMP_EPSILON2)
+ return p_uvs[2];
+
+ Vector2 edge1 = p_vertices[1] - p_vertices[0];
+ Vector2 edge2 = p_vertices[2] - p_vertices[0];
+ Vector2 interpolation = p_interpolation_point - p_vertices[0];
+
+ float edge1_on_edge1 = edge1.dot(edge1);
+ float edge1_on_edge2 = edge1.dot(edge2);
+ float edge2_on_edge2 = edge2.dot(edge2);
+ float inter_on_edge1 = interpolation.dot(edge1);
+ float inter_on_edge2 = interpolation.dot(edge2);
+ float scale = (edge1_on_edge1 * edge2_on_edge2 - edge1_on_edge2 * edge1_on_edge2);
+ if (scale == 0)
+ return p_uvs[0];
+
+ float v = (edge2_on_edge2 * inter_on_edge1 - edge1_on_edge2 * inter_on_edge2) / scale;
+ float w = (edge1_on_edge1 * inter_on_edge2 - edge1_on_edge2 * inter_on_edge1) / scale;
+ float u = 1.0f - v - w;
+
+ return p_uvs[0] * u + p_uvs[1] * v + p_uvs[2] * w;
+}
+
+static inline bool ray_intersects_triangle(const Vector3 &p_from, const Vector3 &p_dir, const Vector3 p_vertices[3], float p_tolerance, Vector3 &r_intersection_point) {
+
+ Vector3 edge1 = p_vertices[1] - p_vertices[0];
+ Vector3 edge2 = p_vertices[2] - p_vertices[0];
+ Vector3 h = p_dir.cross(edge2);
+ real_t a = edge1.dot(h);
+ // Check if ray is parrallel to triangle.
+ if (Math::is_zero_approx(a))
+ return false;
+ real_t f = 1.0 / a;
+
+ Vector3 s = p_from - p_vertices[0];
+ real_t u = f * s.dot(h);
+ if (u < 0.0 - p_tolerance || u > 1.0 + p_tolerance)
+ return false;
+
+ Vector3 q = s.cross(edge1);
+ real_t v = f * p_dir.dot(q);
+ if (v < 0.0 - p_tolerance || u + v > 1.0 + p_tolerance)
+ return false;
+
+ // Ray intersects triangle.
+ // Calculate distance.
+ real_t t = f * edge2.dot(q);
+ // Confirm triangle is in front of ray.
+ if (t >= p_tolerance) {
+ r_intersection_point = p_from + p_dir * t;
+ return true;
+ } else
+ return false;
+}
+
+inline bool is_point_in_triangle(const Vector3 &p_point, const Vector3 p_vertices[3], int p_shifted = 0) {
+
+ real_t det = p_vertices[0].dot(p_vertices[1].cross(p_vertices[2]));
+
+ // If determinant is, zero try shift the triangle and the point.
+ if (Math::is_zero_approx(det)) {
+ if (p_shifted > 2) {
+ // Triangle appears degenerate, so ignore it.
+ return false;
+ }
+ Vector3 shift_by;
+ shift_by[p_shifted] = 1;
+ Vector3 shifted_point = p_point + shift_by;
+ Vector3 shifted_vertices[3] = { p_vertices[0] + shift_by, p_vertices[1] + shift_by, p_vertices[2] + shift_by };
+ return is_point_in_triangle(shifted_point, shifted_vertices, p_shifted + 1);
+ }
+
+ // Find the barycentric coordinates of the point with respect to the vertices.
+ real_t lambda[3];
+ lambda[0] = p_vertices[1].cross(p_vertices[2]).dot(p_point) / det;
+ lambda[1] = p_vertices[2].cross(p_vertices[0]).dot(p_point) / det;
+ lambda[2] = p_vertices[0].cross(p_vertices[1]).dot(p_point) / det;
+
+ // Point is in the plane if all lambdas sum to 1.
+ if (!Math::is_equal_approx(lambda[0] + lambda[1] + lambda[2], 1)) return false;
+
+ // Point is inside the triangle if all lambdas are positive.
+ if (lambda[0] < 0 || lambda[1] < 0 || lambda[2] < 0) return false;
+
+ return true;
+}
+
+inline static bool are_segements_parallel(const Vector2 p_segment1_points[2], const Vector2 p_segment2_points[2], float p_vertex_snap2) {
+
+ Vector2 segment1 = p_segment1_points[1] - p_segment1_points[0];
+ Vector2 segment2 = p_segment2_points[1] - p_segment2_points[0];
+ real_t segment1_length2 = segment1.dot(segment1);
+ real_t segment2_length2 = segment2.dot(segment2);
+ real_t segment_onto_segment = segment2.dot(segment1);
+
+ if (segment1_length2 < p_vertex_snap2 || segment2_length2 < p_vertex_snap2)
+ return true;
+
+ real_t max_separation2;
+ if (segment1_length2 > segment2_length2) {
+ max_separation2 = segment2_length2 - segment_onto_segment * segment_onto_segment / segment1_length2;
+ } else {
+ max_separation2 = segment1_length2 - segment_onto_segment * segment_onto_segment / segment2_length2;
+ }
+
+ return max_separation2 < p_vertex_snap2;
+}
+
+// CSGBrush
+
+void CSGBrush::_regen_face_aabbs() {
+
+ for (int i = 0; i < faces.size(); i++) {
+ faces.write[i].aabb = AABB();
+ faces.write[i].aabb.position = faces[i].vertices[0];
+ faces.write[i].aabb.expand_to(faces[i].vertices[1]);
+ faces.write[i].aabb.expand_to(faces[i].vertices[2]);
+ }
}
void CSGBrush::build_from_faces(const Vector<Vector3> &p_vertices, const Vector<Vector2> &p_uvs, const Vector<bool> &p_smooth, const Vector<Ref<Material> > &p_materials, const Vector<bool> &p_invert_faces) {
- clear();
+ faces.clear();
int vc = p_vertices.size();
@@ -62,37 +202,42 @@ void CSGBrush::build_from_faces(const Vector<Vector3> &p_vertices, const Vector<
faces.resize(p_vertices.size() / 3);
for (int i = 0; i < faces.size(); i++) {
+
Face &f = faces.write[i];
f.vertices[0] = rv[i * 3 + 0];
f.vertices[1] = rv[i * 3 + 1];
f.vertices[2] = rv[i * 3 + 2];
+
if (uvc == vc) {
f.uvs[0] = ruv[i * 3 + 0];
f.uvs[1] = ruv[i * 3 + 1];
f.uvs[2] = ruv[i * 3 + 2];
}
- if (sc == vc / 3) {
+
+ if (sc == vc / 3)
f.smooth = rs[i];
- } else {
+ else
f.smooth = false;
- }
- if (ic == vc / 3) {
+ if (ic == vc / 3)
f.invert = ri[i];
- } else {
+ else
f.invert = false;
- }
if (mc == vc / 3) {
+
Ref<Material> mat = rm[i];
if (mat.is_valid()) {
+
const Map<Ref<Material>, int>::Element *E = material_map.find(mat);
+
if (E) {
f.material = E->get();
} else {
f.material = material_map.size();
material_map[mat] = f.material;
}
+
} else {
f.material = -1;
}
@@ -107,17 +252,6 @@ void CSGBrush::build_from_faces(const Vector<Vector3> &p_vertices, const Vector<
_regen_face_aabbs();
}
-void CSGBrush::_regen_face_aabbs() {
-
- for (int i = 0; i < faces.size(); i++) {
-
- faces.write[i].aabb.position = faces[i].vertices[0];
- faces.write[i].aabb.expand_to(faces[i].vertices[1]);
- faces.write[i].aabb.expand_to(faces[i].vertices[2]);
- faces.write[i].aabb.grow_by(faces[i].aabb.get_longest_axis_size() * 0.001); //make it a tad bigger to avoid num precision errors
- }
-}
-
void CSGBrush::copy_from(const CSGBrush &p_brush, const Transform &p_xform) {
faces = p_brush.faces;
@@ -132,908 +266,218 @@ void CSGBrush::copy_from(const CSGBrush &p_brush, const Transform &p_xform) {
_regen_face_aabbs();
}
-////////////////////////
-
-void CSGBrushOperation::BuildPoly::create(const CSGBrush *p_brush, int p_face, MeshMerge &mesh_merge, bool p_for_B) {
-
- //creates the initial face that will be used for clipping against the other faces
-
- Vector3 va[3] = {
- p_brush->faces[p_face].vertices[0],
- p_brush->faces[p_face].vertices[1],
- p_brush->faces[p_face].vertices[2],
- };
-
- plane = Plane(va[0], va[1], va[2]);
-
- to_world.origin = va[0];
-
- to_world.basis.set_axis(2, plane.normal);
- to_world.basis.set_axis(0, (va[1] - va[2]).normalized());
- to_world.basis.set_axis(1, to_world.basis.get_axis(0).cross(to_world.basis.get_axis(2)).normalized());
-
- to_poly = to_world.affine_inverse();
-
- face_index = p_face;
-
- for (int i = 0; i < 3; i++) {
-
- Point p;
- Vector3 localp = to_poly.xform(va[i]);
- p.point.x = localp.x;
- p.point.y = localp.y;
- p.uv = p_brush->faces[p_face].uvs[i];
-
- points.push_back(p);
-
- ///edge
-
- Edge e;
- e.points[0] = i;
- e.points[1] = (i + 1) % 3;
- e.outer = true;
- edges.push_back(e);
- }
-
- smooth = p_brush->faces[p_face].smooth;
- invert = p_brush->faces[p_face].invert;
-
- if (p_brush->faces[p_face].material != -1) {
- material = p_brush->materials[p_brush->faces[p_face].material];
- }
-
- base_edges = 3;
-}
-
-static Vector2 interpolate_uv(const Vector2 &p_vertex_a, const Vector2 &p_vertex_b, const Vector2 &p_vertex_c, const Vector2 &p_uv_a, const Vector2 &p_uv_c) {
-
- float len_a_c = (p_vertex_c - p_vertex_a).length();
- if (len_a_c < CMP_EPSILON) {
- return p_uv_a;
- }
-
- float len_a_b = (p_vertex_b - p_vertex_a).length();
-
- float c = len_a_b / len_a_c;
-
- return p_uv_a.linear_interpolate(p_uv_c, c);
-}
-
-static Vector2 interpolate_triangle_uv(const Vector2 &p_pos, const Vector2 *p_vtx, const Vector2 *p_uv) {
-
- if (p_pos.distance_squared_to(p_vtx[0]) < CMP_EPSILON2) {
- return p_uv[0];
- }
- if (p_pos.distance_squared_to(p_vtx[1]) < CMP_EPSILON2) {
- return p_uv[1];
- }
- if (p_pos.distance_squared_to(p_vtx[2]) < CMP_EPSILON2) {
- return p_uv[2];
- }
-
- Vector2 v0 = p_vtx[1] - p_vtx[0];
- Vector2 v1 = p_vtx[2] - p_vtx[0];
- Vector2 v2 = p_pos - p_vtx[0];
-
- float d00 = v0.dot(v0);
- float d01 = v0.dot(v1);
- float d11 = v1.dot(v1);
- float d20 = v2.dot(v0);
- float d21 = v2.dot(v1);
- float denom = (d00 * d11 - d01 * d01);
- if (denom == 0) {
- return p_uv[0];
- }
- float v = (d11 * d20 - d01 * d21) / denom;
- float w = (d00 * d21 - d01 * d20) / denom;
- float u = 1.0f - v - w;
-
- return p_uv[0] * u + p_uv[1] * v + p_uv[2] * w;
-}
-
-void CSGBrushOperation::BuildPoly::_clip_segment(const CSGBrush *p_brush, int p_face, const Vector2 *segment, MeshMerge &mesh_merge, bool p_for_B) {
+// CSGBrushOperation
- //keep track of what was inserted
- Vector<int> inserted_points;
+void CSGBrushOperation::merge_brushes(Operation p_operation, const CSGBrush &p_brush_a, const CSGBrush &p_brush_b, CSGBrush &r_merged_brush, float p_vertex_snap) {
- //keep track of point indices for what was inserted, allowing reuse of points.
- int segment_idx[2] = { -1, -1 };
-
- //check if edge and poly share a vertex, of so, assign it to segment_idx
- for (int i = 0; i < points.size(); i++) {
- for (int j = 0; j < 2; j++) {
- if (segment[j].is_equal_approx(points[i].point)) {
- segment_idx[j] = i;
- inserted_points.push_back(i);
- break;
+ // Check for face collisions and add necessary faces.
+ Build2DFaceCollection build2DFaceCollection;
+ for (int i = 0; i < p_brush_a.faces.size(); i++) {
+ for (int j = 0; j < p_brush_b.faces.size(); j++) {
+ if (p_brush_a.faces[i].aabb.intersects_inclusive(p_brush_b.faces[j].aabb)) {
+ update_faces(p_brush_a, i, p_brush_b, j, build2DFaceCollection, p_vertex_snap);
}
}
}
- //check if both segment points are shared with other vertices
- if (segment_idx[0] != -1 && segment_idx[1] != -1) {
-
- if (segment_idx[0] == segment_idx[1]) {
- return; //segment was too tiny, both mapped to same point
- }
-
- bool found = false;
-
- //check if the segment already exists
- for (int i = 0; i < edges.size(); i++) {
-
- if (
- (edges[i].points[0] == segment_idx[0] && edges[i].points[1] == segment_idx[1]) ||
- (edges[i].points[0] == segment_idx[1] && edges[i].points[1] == segment_idx[0])) {
- found = true;
- break;
- }
- }
-
- if (found) {
- //it does already exist, do nothing
- return;
- }
-
- //directly add the new segment
- Edge new_edge;
- new_edge.points[0] = segment_idx[0];
- new_edge.points[1] = segment_idx[1];
- edges.push_back(new_edge);
- return;
- }
-
- //check edge by edge against the segment points to see if intersects
-
- for (int i = 0; i < base_edges; i++) {
-
- //if a point is shared with one of the edge points, then this edge must not be tested, as it will result in a numerical precision error.
- bool edge_valid = true;
- for (int j = 0; j < 2; j++) {
-
- if (edges[i].points[0] == segment_idx[0] || edges[i].points[1] == segment_idx[1] || edges[i].points[0] == segment_idx[1] || edges[i].points[1] == segment_idx[0]) {
- edge_valid = false; //segment has this point, can't check against this
- break;
- }
- }
-
- if (!edge_valid) //already hit a point in this edge, so don't test it
- continue;
-
- //see if either points are within the edge isntead of crossing it
- Vector2 res;
- bool found = false;
- int assign_segment_id = -1;
-
- for (int j = 0; j < 2; j++) {
+ // Add faces to MeshMerge.
+ MeshMerge mesh_merge;
+ mesh_merge.vertex_snap = p_vertex_snap;
- Vector2 edgeseg[2] = { points[edges[i].points[0]].point, points[edges[i].points[1]].point };
- Vector2 closest = Geometry::get_closest_point_to_segment_2d(segment[j], edgeseg);
+ for (int i = 0; i < p_brush_a.faces.size(); i++) {
- if (closest.is_equal_approx(segment[j])) {
- //point rest of this edge
- res = closest;
- found = true;
- assign_segment_id = j;
- }
- }
-
- //test if the point crosses the edge
- if (!found && Geometry::segment_intersects_segment_2d(segment[0], segment[1], points[edges[i].points[0]].point, points[edges[i].points[1]].point, &res)) {
- //point does cross the edge
- found = true;
+ Ref<Material> material;
+ if (p_brush_a.faces[i].material != -1) {
+ material = p_brush_a.materials[p_brush_a.faces[i].material];
}
- //check whether an intersection against the segment happened
- if (found) {
-
- //It did! so first, must slice the segment
- Point new_point;
- new_point.point = res;
- //make sure to interpolate UV too
- new_point.uv = interpolate_uv(points[edges[i].points[0]].point, new_point.point, points[edges[i].points[1]].point, points[edges[i].points[0]].uv, points[edges[i].points[1]].uv);
-
- int point_idx = points.size();
- points.push_back(new_point);
-
- //split the edge in 2
- Edge new_edge;
- new_edge.points[0] = edges[i].points[0];
- new_edge.points[1] = point_idx;
- new_edge.outer = edges[i].outer;
- edges.write[i].points[0] = point_idx;
- edges.insert(i, new_edge);
- i++; //skip newly inserted edge
- base_edges++; //will need an extra one in the base triangle
- if (assign_segment_id >= 0) {
- //point did split a segment, so make sure to remember this
- segment_idx[assign_segment_id] = point_idx;
+ if (build2DFaceCollection.build2DFacesA.has(i)) {
+ build2DFaceCollection.build2DFacesA[i].addFacesToMesh(mesh_merge, p_brush_a.faces[i].smooth, p_brush_a.faces[i].invert, material, false);
+ } else {
+ Vector3 points[3];
+ Vector2 uvs[3];
+ for (int j = 0; j < 3; j++) {
+ points[j] = p_brush_a.faces[i].vertices[j];
+ uvs[j] = p_brush_a.faces[i].uvs[j];
}
- inserted_points.push_back(point_idx);
+ mesh_merge.add_face(points, uvs, p_brush_a.faces[i].smooth, p_brush_a.faces[i].invert, material, false);
}
}
- //final step: after cutting the original triangle, try to see if we can still insert
- //this segment
-
- //if already inserted two points, just use them for a segment
-
- if (inserted_points.size() >= 2) { //should never be >2 on non-manifold geometry, but cope with error
- //two points were inserted, create the new edge
- Edge new_edge;
- new_edge.points[0] = inserted_points[0];
- new_edge.points[1] = inserted_points[1];
- edges.push_back(new_edge);
- return;
- }
-
- // One or no points were inserted (besides splitting), so try to see if extra points can be placed inside the triangle.
- // This needs to be done here, after the previous tests were exhausted
- for (int i = 0; i < 2; i++) {
-
- if (segment_idx[i] != -1)
- continue; //already assigned to something, so skip
-
- //check whether one of the segment endpoints is inside the triangle. If it is, this points needs to be inserted
- if (Geometry::is_point_in_triangle(segment[i], points[0].point, points[1].point, points[2].point)) {
-
- Point new_point;
- new_point.point = segment[i];
+ for (int i = 0; i < p_brush_b.faces.size(); i++) {
- Vector2 point3[3] = { points[0].point, points[1].point, points[2].point };
- Vector2 uv3[3] = { points[0].uv, points[1].uv, points[2].uv };
-
- new_point.uv = interpolate_triangle_uv(new_point.point, point3, uv3);
-
- int point_idx = points.size();
- points.push_back(new_point);
- inserted_points.push_back(point_idx);
+ Ref<Material> material;
+ if (p_brush_b.faces[i].material != -1) {
+ material = p_brush_b.materials[p_brush_b.faces[i].material];
}
- }
-
- //check again whether two points were inserted, if so then create the new edge
- if (inserted_points.size() >= 2) { //should never be >2 on non-manifold geometry, but cope with error
- Edge new_edge;
- new_edge.points[0] = inserted_points[0];
- new_edge.points[1] = inserted_points[1];
- edges.push_back(new_edge);
- }
-}
-
-void CSGBrushOperation::BuildPoly::clip(const CSGBrush *p_brush, int p_face, MeshMerge &mesh_merge, bool p_for_B) {
-
- //Clip function.. find triangle points that will be mapped to the plane and form a segment
-
- Vector2 segment[3]; //2D
- int src_points = 0;
-
- for (int i = 0; i < 3; i++) {
- Vector3 p = p_brush->faces[p_face].vertices[i];
- if (plane.has_point(p)) {
- Vector3 pp = plane.project(p);
- pp = to_poly.xform(pp);
- segment[src_points++] = Vector2(pp.x, pp.y);
+ if (build2DFaceCollection.build2DFacesB.has(i)) {
+ build2DFaceCollection.build2DFacesB[i].addFacesToMesh(mesh_merge, p_brush_b.faces[i].smooth, p_brush_b.faces[i].invert, material, true);
} else {
- Vector3 q = p_brush->faces[p_face].vertices[(i + 1) % 3];
- if (plane.has_point(q))
- continue; //next point is in plane, will be added eventually
- if (plane.is_point_over(p) == plane.is_point_over(q))
- continue; // both on same side of the plane, don't add
-
- Vector3 res;
- if (plane.intersects_segment(p, q, &res)) {
- res = to_poly.xform(res);
- segment[src_points++] = Vector2(res.x, res.y);
- }
- }
- }
-
- //all above or all below, nothing to do. Should not happen though since a precheck was done before.
- if (src_points == 0)
- return;
-
- //just one point in plane is not worth doing anything
- if (src_points == 1)
- return;
-
- //transform A points to 2D
-
- if (segment[0].is_equal_approx(segment[1]))
- return; //too small
-
- _clip_segment(p_brush, p_face, segment, mesh_merge, p_for_B);
-}
-
-void CSGBrushOperation::_collision_callback(const CSGBrush *A, int p_face_a, Map<int, BuildPoly> &build_polys_a, const CSGBrush *B, int p_face_b, Map<int, BuildPoly> &build_polys_b, MeshMerge &mesh_merge) {
-
- //construct a frame of reference for both transforms, in order to do intersection test
- Vector3 va[3] = {
- A->faces[p_face_a].vertices[0],
- A->faces[p_face_a].vertices[1],
- A->faces[p_face_a].vertices[2],
- };
- Vector3 vb[3] = {
- B->faces[p_face_b].vertices[0],
- B->faces[p_face_b].vertices[1],
- B->faces[p_face_b].vertices[2],
- };
-
- {
- //check if either is a degenerate
- if (va[0].is_equal_approx(va[1]) || va[0].is_equal_approx(va[2]) || va[1].is_equal_approx(va[2]))
- return;
-
- if (vb[0].is_equal_approx(vb[1]) || vb[0].is_equal_approx(vb[2]) || vb[1].is_equal_approx(vb[2]))
- return;
- }
-
- {
- //check if points are the same
- int equal_count = 0;
-
- for (int i = 0; i < 3; i++) {
-
+ Vector3 points[3];
+ Vector2 uvs[3];
for (int j = 0; j < 3; j++) {
- if (va[i].distance_to(vb[j]) < mesh_merge.vertex_snap) {
- equal_count++;
- break;
- }
+ points[j] = p_brush_b.faces[i].vertices[j];
+ uvs[j] = p_brush_b.faces[i].uvs[j];
}
- }
-
- //if 2 or 3 points are the same, there is no point in doing anything. They can't
- //be clipped either, so add both.
- if (equal_count == 2 || equal_count == 3) {
- return;
+ mesh_merge.add_face(points, uvs, p_brush_b.faces[i].smooth, p_brush_b.faces[i].invert, material, true);
}
}
- // do a quick pre-check for no-intersection using the SAT theorem
-
- {
-
- //b under or over a plane
- int over_count = 0, in_plane_count = 0, under_count = 0;
- Plane plane_a(va[0], va[1], va[2]);
- if (plane_a.normal == Vector3()) {
- return; //degenerate
- }
-
- for (int i = 0; i < 3; i++) {
- if (plane_a.has_point(vb[i]))
- in_plane_count++;
- else if (plane_a.is_point_over(vb[i]))
- over_count++;
- else
- under_count++;
- }
-
- if (over_count == 0 || under_count == 0)
- return; //no intersection, something needs to be under AND over
-
- //a under or over b plane
- over_count = 0;
- under_count = 0;
- in_plane_count = 0;
-
- Plane plane_b(vb[0], vb[1], vb[2]);
- if (plane_b.normal == Vector3())
- return; //degenerate
+ // Mark faces that ended up inside the intersection.
+ mesh_merge.mark_inside_faces();
- for (int i = 0; i < 3; i++) {
- if (plane_b.has_point(va[i]))
- in_plane_count++;
- else if (plane_b.is_point_over(va[i]))
- over_count++;
- else
- under_count++;
- }
+ // Create new brush and fill with new faces.
+ r_merged_brush.faces.clear();
- if (over_count == 0 || under_count == 0)
- return; //no intersection, something needs to be under AND over
+ switch (p_operation) {
- //edge pairs (cross product combinations), see SAT theorem
+ case OPERATION_UNION: {
- for (int i = 0; i < 3; i++) {
+ int outside_count = 0;
- Vector3 axis_a = (va[i] - va[(i + 1) % 3]).normalized();
+ for (int i = 0; i < mesh_merge.faces.size(); i++) {
+ if (mesh_merge.faces[i].inside)
+ continue;
+ outside_count++;
+ }
- for (int j = 0; j < 3; j++) {
+ r_merged_brush.faces.resize(outside_count);
- Vector3 axis_b = (vb[j] - vb[(j + 1) % 3]).normalized();
+ outside_count = 0;
- Vector3 sep_axis = axis_a.cross(axis_b);
- if (sep_axis == Vector3())
- continue; //colineal
- sep_axis.normalize();
+ for (int i = 0; i < mesh_merge.faces.size(); i++) {
- real_t min_a = 1e20, max_a = -1e20;
- real_t min_b = 1e20, max_b = -1e20;
+ if (mesh_merge.faces[i].inside)
+ continue;
- for (int k = 0; k < 3; k++) {
- real_t d = sep_axis.dot(va[k]);
- min_a = MIN(min_a, d);
- max_a = MAX(max_a, d);
- d = sep_axis.dot(vb[k]);
- min_b = MIN(min_b, d);
- max_b = MAX(max_b, d);
+ for (int j = 0; j < 3; j++) {
+ r_merged_brush.faces.write[outside_count].vertices[j] = mesh_merge.points[mesh_merge.faces[i].points[j]];
+ r_merged_brush.faces.write[outside_count].uvs[j] = mesh_merge.faces[i].uvs[j];
}
- min_b -= (max_a - min_a) * 0.5;
- max_b += (max_a - min_a) * 0.5;
-
- real_t dmin = min_b - (min_a + max_a) * 0.5;
- real_t dmax = max_b - (min_a + max_a) * 0.5;
-
- if (dmin > CMP_EPSILON || dmax < -CMP_EPSILON) {
- return; //does not contain zero, so they don't overlap
- }
+ r_merged_brush.faces.write[outside_count].smooth = mesh_merge.faces[i].smooth;
+ r_merged_brush.faces.write[outside_count].invert = mesh_merge.faces[i].invert;
+ r_merged_brush.faces.write[outside_count].material = mesh_merge.faces[i].material_idx;
+ outside_count++;
}
- }
- }
-
- //if we are still here, it means they most likely intersect, so create BuildPolys if they don't exist
-
- BuildPoly *poly_a = NULL;
-
- if (!build_polys_a.has(p_face_a)) {
- BuildPoly bp;
- bp.create(A, p_face_a, mesh_merge, false);
- build_polys_a[p_face_a] = bp;
- }
-
- poly_a = &build_polys_a[p_face_a];
-
- BuildPoly *poly_b = NULL;
+ r_merged_brush._regen_face_aabbs();
- if (!build_polys_b.has(p_face_b)) {
-
- BuildPoly bp;
- bp.create(B, p_face_b, mesh_merge, true);
- build_polys_b[p_face_b] = bp;
- }
-
- poly_b = &build_polys_b[p_face_b];
-
- //clip each other, this could be improved by using vertex unique IDs (more vertices may be shared instead of using snap)
- poly_a->clip(B, p_face_b, mesh_merge, false);
- poly_b->clip(A, p_face_a, mesh_merge, true);
-}
-
-void CSGBrushOperation::_add_poly_points(const BuildPoly &p_poly, int p_edge, int p_from_point, int p_to_point, const Vector<Vector<int> > &vertex_process, Vector<bool> &edge_process, Vector<PolyPoints> &r_poly) {
-
- //this function follows the polygon points counter clockwise and adds them. It creates lists of unique polygons
- //every time an unused edge is found, it's pushed to a stack and continues from there.
-
- List<EdgeSort> edge_stack;
-
- {
- EdgeSort es;
- es.angle = 0; //won't be checked here
- es.edge = p_edge;
- es.prev_point = p_from_point;
- es.edge_point = p_to_point;
-
- edge_stack.push_back(es);
- }
-
- //attempt to empty the stack.
- while (edge_stack.size()) {
-
- EdgeSort e = edge_stack.front()->get();
- edge_stack.pop_front();
-
- if (edge_process[e.edge]) {
- //nothing to do here
- continue;
- }
-
- Vector<int> points;
- points.push_back(e.prev_point);
-
- int prev_point = e.prev_point;
- int to_point = e.edge_point;
- int current_edge = e.edge;
-
- edge_process.write[e.edge] = true; //mark as processed
-
- int limit = p_poly.points.size() * 4; //avoid infinite recursion
-
- while (to_point != e.prev_point && limit) {
-
- Vector2 segment[2] = { p_poly.points[prev_point].point, p_poly.points[to_point].point };
-
- //construct a basis transform from the segment, which will be used to check the angle
- Transform2D t2d;
- t2d[0] = (segment[1] - segment[0]).normalized(); //use as Y
- t2d[1] = Vector2(-t2d[0].y, t2d[0].x); // use as tangent
- t2d[2] = segment[1]; //origin
-
- if (t2d.basis_determinant() == 0)
- break; //abort poly
-
- t2d.affine_invert();
-
- //push all edges found here, they will be sorted by minimum angle later.
- Vector<EdgeSort> next_edges;
-
- for (int i = 0; i < vertex_process[to_point].size(); i++) {
+ } break;
- int edge = vertex_process[to_point][i];
- int opposite_point = p_poly.edges[edge].points[0] == to_point ? p_poly.edges[edge].points[1] : p_poly.edges[edge].points[0];
- if (opposite_point == prev_point)
- continue; //not going back
+ case OPERATION_INTERSECTION: {
- EdgeSort e2;
- Vector2 local_vec = t2d.xform(p_poly.points[opposite_point].point);
- e2.angle = -local_vec.angle(); //negate so we can sort by minimum angle
- e2.edge = edge;
- e2.edge_point = opposite_point;
- e2.prev_point = to_point;
+ int inside_count = 0;
- next_edges.push_back(e2);
+ for (int i = 0; i < mesh_merge.faces.size(); i++) {
+ if (!mesh_merge.faces[i].inside)
+ continue;
+ inside_count++;
}
- //finally, sort by minimum angle
- next_edges.sort();
+ r_merged_brush.faces.resize(inside_count);
- int next_point = -1;
- int next_edge = -1;
+ inside_count = 0;
- for (int i = 0; i < next_edges.size(); i++) {
+ for (int i = 0; i < mesh_merge.faces.size(); i++) {
- if (i == 0) {
- //minimum angle found is the next point
- next_point = next_edges[i].edge_point;
- next_edge = next_edges[i].edge;
+ if (!mesh_merge.faces[i].inside)
+ continue;
- } else {
- //the rest are pushed to the stack IF they were not processed yet.
- if (!edge_process[next_edges[i].edge]) {
- edge_stack.push_back(next_edges[i]);
- }
+ for (int j = 0; j < 3; j++) {
+ r_merged_brush.faces.write[inside_count].vertices[j] = mesh_merge.points[mesh_merge.faces[i].points[j]];
+ r_merged_brush.faces.write[inside_count].uvs[j] = mesh_merge.faces[i].uvs[j];
}
- }
- if (next_edge == -1) {
- //did not find anything, may be a dead-end edge (this should normally not happen)
- //just flip the direction and go back
- next_point = prev_point;
- next_edge = current_edge;
+ r_merged_brush.faces.write[inside_count].smooth = mesh_merge.faces[i].smooth;
+ r_merged_brush.faces.write[inside_count].invert = mesh_merge.faces[i].invert;
+ r_merged_brush.faces.write[inside_count].material = mesh_merge.faces[i].material_idx;
+ inside_count++;
}
- points.push_back(to_point);
-
- prev_point = to_point;
- to_point = next_point;
- edge_process.write[next_edge] = true; //mark this edge as processed
- current_edge = next_edge;
-
- limit--;
- }
-
- //if more than 2 points were added to the polygon, add it to the list of polygons.
- if (points.size() > 2) {
- PolyPoints pp;
- pp.points = points;
- r_poly.push_back(pp);
- }
- }
-}
-
-void CSGBrushOperation::_add_poly_outline(const BuildPoly &p_poly, int p_from_point, int p_to_point, const Vector<Vector<int> > &vertex_process, Vector<int> &r_outline) {
-
- //this is the opposite of the function above. It adds polygon outlines instead.
- //this is used for triangulating holes.
- //no stack is used here because only the bigger outline is interesting.
-
- r_outline.push_back(p_from_point);
+ r_merged_brush._regen_face_aabbs();
- int prev_point = p_from_point;
- int to_point = p_to_point;
-
- int limit = p_poly.points.size() * 4; //avoid infinite recursion
-
- while (to_point != p_from_point && limit) {
-
- Vector2 segment[2] = { p_poly.points[prev_point].point, p_poly.points[to_point].point };
- //again create a transform to compute the angle.
- Transform2D t2d;
- t2d[0] = (segment[1] - segment[0]).normalized(); //use as Y
- t2d[1] = Vector2(-t2d[0].y, t2d[0].x); // use as tangent
- t2d[2] = segment[1]; //origin
-
- if (t2d.basis_determinant() == 0)
- break; //abort poly
-
- t2d.affine_invert();
-
- float max_angle = 0;
- int next_point_angle = -1;
+ } break;
- for (int i = 0; i < vertex_process[to_point].size(); i++) {
+ case OPERATION_SUBSTRACTION: {
- int edge = vertex_process[to_point][i];
- int opposite_point = p_poly.edges[edge].points[0] == to_point ? p_poly.edges[edge].points[1] : p_poly.edges[edge].points[0];
- if (opposite_point == prev_point)
- continue; //not going back
+ int face_count = 0;
- float angle = -t2d.xform(p_poly.points[opposite_point].point).angle();
- if (next_point_angle == -1 || angle > max_angle) { //same as before but use greater to check.
- max_angle = angle;
- next_point_angle = opposite_point;
+ for (int i = 0; i < mesh_merge.faces.size(); i++) {
+ if (mesh_merge.faces[i].from_b && !mesh_merge.faces[i].inside)
+ continue;
+ if (!mesh_merge.faces[i].from_b && mesh_merge.faces[i].inside)
+ continue;
+ face_count++;
}
- }
-
- if (next_point_angle == -1) {
- //go back because no route found
- next_point_angle = prev_point;
- }
-
- r_outline.push_back(to_point);
- prev_point = to_point;
- to_point = next_point_angle;
-
- limit--;
- }
-}
-
-void CSGBrushOperation::_merge_poly(MeshMerge &mesh, int p_face_idx, const BuildPoly &p_poly, bool p_from_b) {
-
- //finally, merge the 2D polygon back to 3D
-
- Vector<Vector<int> > vertex_process;
- Vector<bool> edge_process;
-
- vertex_process.resize(p_poly.points.size());
- edge_process.resize(p_poly.edges.size());
-
- //none processed by default
- for (int i = 0; i < edge_process.size(); i++) {
- edge_process.write[i] = false;
- }
-
- //put edges in points, so points can go through them
- for (int i = 0; i < p_poly.edges.size(); i++) {
- vertex_process.write[p_poly.edges[i].points[0]].push_back(i);
- vertex_process.write[p_poly.edges[i].points[1]].push_back(i);
- }
- Vector<PolyPoints> polys;
+ r_merged_brush.faces.resize(face_count);
- //process points that were not processed
- for (int i = 0; i < edge_process.size(); i++) {
- if (edge_process[i])
- continue; //already processed
-
- int intersect_poly = -1;
-
- if (i > 0) {
- //this is disconnected, so it's clearly a hole. lets find where it belongs
- Vector2 ref_point = p_poly.points[p_poly.edges[i].points[0]].point;
-
- for (int j = 0; j < polys.size(); j++) {
-
- //find a point outside poly
- Vector2 out_point(-1e20, -1e20);
-
- const PolyPoints &pp = polys[j];
-
- for (int k = 0; k < pp.points.size(); k++) {
- Vector2 p = p_poly.points[pp.points[k]].point;
- out_point.x = MAX(out_point.x, p.x);
- out_point.y = MAX(out_point.y, p.y);
- }
-
- out_point += Vector2(0.12341234, 0.4123412); // move to a random place to avoid direct edge-point chances
+ face_count = 0;
- int intersections = 0;
+ for (int i = 0; i < mesh_merge.faces.size(); i++) {
- for (int k = 0; k < pp.points.size(); k++) {
- Vector2 p1 = p_poly.points[pp.points[k]].point;
- Vector2 p2 = p_poly.points[pp.points[(k + 1) % pp.points.size()]].point;
+ if (mesh_merge.faces[i].from_b && !mesh_merge.faces[i].inside)
+ continue;
+ if (!mesh_merge.faces[i].from_b && mesh_merge.faces[i].inside)
+ continue;
- if (Geometry::segment_intersects_segment_2d(ref_point, out_point, p1, p2, NULL)) {
- intersections++;
- }
+ for (int j = 0; j < 3; j++) {
+ r_merged_brush.faces.write[face_count].vertices[j] = mesh_merge.points[mesh_merge.faces[i].points[j]];
+ r_merged_brush.faces.write[face_count].uvs[j] = mesh_merge.faces[i].uvs[j];
}
- if (intersections % 2 == 1) {
- //hole is inside this poly
- intersect_poly = j;
- break;
+ if (mesh_merge.faces[i].from_b) {
+ //invert facing of insides of B
+ SWAP(r_merged_brush.faces.write[face_count].vertices[1], r_merged_brush.faces.write[face_count].vertices[2]);
+ SWAP(r_merged_brush.faces.write[face_count].uvs[1], r_merged_brush.faces.write[face_count].uvs[2]);
}
- }
- }
- if (intersect_poly != -1) {
- //must add this as a hole
- Vector<int> outline;
- _add_poly_outline(p_poly, p_poly.edges[i].points[0], p_poly.edges[i].points[1], vertex_process, outline);
-
- if (outline.size() > 1) {
- polys.write[intersect_poly].holes.push_back(outline);
+ r_merged_brush.faces.write[face_count].smooth = mesh_merge.faces[i].smooth;
+ r_merged_brush.faces.write[face_count].invert = mesh_merge.faces[i].invert;
+ r_merged_brush.faces.write[face_count].material = mesh_merge.faces[i].material_idx;
+ face_count++;
}
- }
- _add_poly_points(p_poly, i, p_poly.edges[i].points[0], p_poly.edges[i].points[1], vertex_process, edge_process, polys);
- }
-
- //get rid of holes, not the most optiomal way, but also not a common case at all to be inoptimal
- for (int i = 0; i < polys.size(); i++) {
-
- if (!polys[i].holes.size())
- continue;
-
- //repeat until no more holes are left to be merged
- while (polys[i].holes.size()) {
-
- //try to merge a hole with the outline
- bool added_hole = false;
-
- for (int j = 0; j < polys[i].holes.size(); j++) {
-
- //try hole vertices
- int with_outline_vertex = -1;
- int from_hole_vertex = -1;
-
- bool found = false;
-
- for (int k = 0; k < polys[i].holes[j].size(); k++) {
-
- int from_idx = polys[i].holes[j][k];
- Vector2 from = p_poly.points[from_idx].point;
-
- //try a segment from hole vertex to outline vertices
- from_hole_vertex = k;
-
- bool valid = true;
-
- for (int l = 0; l < polys[i].points.size(); l++) {
-
- int to_idx = polys[i].points[l];
- Vector2 to = p_poly.points[to_idx].point;
- with_outline_vertex = l;
-
- //try against outline (other points) first
-
- valid = true;
-
- for (int m = 0; m < polys[i].points.size(); m++) {
- int m_next = (m + 1) % polys[i].points.size();
- if (m == with_outline_vertex || m_next == with_outline_vertex) //do not test with edges that share this point
- continue;
+ r_merged_brush._regen_face_aabbs();
- if (Geometry::segment_intersects_segment_2d(from, to, p_poly.points[polys[i].points[m]].point, p_poly.points[polys[i].points[m_next]].point, NULL)) {
- valid = false;
- break;
- }
- }
-
- if (!valid)
- continue;
-
- //try against all holes including self
-
- for (int m = 0; m < polys[i].holes.size(); m++) {
-
- for (int n = 0; n < polys[i].holes[m].size(); n++) {
-
- int n_next = (n + 1) % polys[i].holes[m].size();
- if (m == j && (n == from_hole_vertex || n_next == from_hole_vertex)) //contains vertex being tested from current hole, skip
- continue;
-
- if (Geometry::segment_intersects_segment_2d(from, to, p_poly.points[polys[i].holes[m][n]].point, p_poly.points[polys[i].holes[m][n_next]].point, NULL)) {
- valid = false;
- break;
- }
- }
-
- if (!valid)
- break;
- }
-
- if (valid) //all passed! exit loop
- break;
- else
- continue; //something went wrong, go on.
- }
-
- if (valid) {
- found = true; //if in the end this was valid, use it
- break;
- }
- }
-
- if (found) {
-
- //hook this hole with outline, and remove from list of holes
-
- //duplicate point
- int insert_at = with_outline_vertex;
- int point = polys[i].points[insert_at];
- polys.write[i].points.insert(insert_at, point);
- insert_at++;
- //insert all others, outline should be backwards (must check)
- int holesize = polys[i].holes[j].size();
- for (int k = 0; k <= holesize; k++) {
- int idx = (from_hole_vertex + k) % holesize;
- int point2 = polys[i].holes[j][idx];
- polys.write[i].points.insert(insert_at, point2);
- insert_at++;
- }
-
- added_hole = true;
- polys.write[i].holes.remove(j);
- break; //got rid of hole, break and continue
- }
- }
-
- ERR_BREAK(!added_hole);
- }
+ } break;
}
- //triangulate polygons
-
- for (int i = 0; i < polys.size(); i++) {
-
- Vector<Vector2> vertices;
- vertices.resize(polys[i].points.size());
- for (int j = 0; j < vertices.size(); j++) {
- vertices.write[j] = p_poly.points[polys[i].points[j]].point;
- }
-
- Vector<int> indices = Geometry::triangulate_polygon(vertices);
-
- for (int j = 0; j < indices.size(); j += 3) {
-
- //obtain the vertex
-
- Vector3 face[3];
- Vector2 uv[3];
- float cp = Geometry::vec2_cross(p_poly.points[polys[i].points[indices[j + 0]]].point, p_poly.points[polys[i].points[indices[j + 1]]].point, p_poly.points[polys[i].points[indices[j + 2]]].point);
- if (Math::abs(cp) < CMP_EPSILON)
- continue;
-
- for (int k = 0; k < 3; k++) {
-
- Vector2 p = p_poly.points[polys[i].points[indices[j + k]]].point;
- face[k] = p_poly.to_world.xform(Vector3(p.x, p.y, 0));
- uv[k] = p_poly.points[polys[i].points[indices[j + k]]].uv;
- }
-
- mesh.add_face(face[0], face[1], face[2], uv[0], uv[1], uv[2], p_poly.smooth, p_poly.invert, p_poly.material, p_from_b);
- }
+ // Update the list of materials.
+ r_merged_brush.materials.resize(mesh_merge.materials.size());
+ for (const Map<Ref<Material>, int>::Element *E = mesh_merge.materials.front(); E; E = E->next()) {
+ r_merged_brush.materials.write[E->get()] = E->key();
}
}
-//use a limit to speed up bvh and limit the depth
+// CSGBrushOperation::MeshMerge
+
+// Use a limit to speed up bvh and limit the depth.
#define BVH_LIMIT 8
-int CSGBrushOperation::MeshMerge::_create_bvh(BVH *p_bvh, BVH **p_bb, int p_from, int p_size, int p_depth, int &max_depth, int &max_alloc) {
+int CSGBrushOperation::MeshMerge::_create_bvh(FaceBVH *facebvhptr, FaceBVH **facebvhptrptr, int p_from, int p_size, int p_depth, int &r_max_depth, int &r_max_alloc) {
- if (p_depth > max_depth) {
- max_depth = p_depth;
+ if (p_depth > r_max_depth) {
+ r_max_depth = p_depth;
}
if (p_size == 0) {
-
return -1;
- } else if (p_size <= BVH_LIMIT) {
+ }
+ if (p_size <= BVH_LIMIT) {
for (int i = 0; i < p_size - 1; i++) {
- p_bb[p_from + i]->next = p_bb[p_from + i + 1] - p_bvh;
+ facebvhptrptr[p_from + i]->next = facebvhptrptr[p_from + i + 1] - facebvhptr;
}
- return p_bb[p_from] - p_bvh;
+ return facebvhptrptr[p_from] - facebvhptr;
}
AABB aabb;
- aabb = p_bb[p_from]->aabb;
+ aabb = facebvhptrptr[p_from]->aabb;
for (int i = 1; i < p_size; i++) {
-
- aabb.merge_with(p_bb[p_from + i]->aabb);
+ aabb.merge_with(facebvhptrptr[p_from + i]->aabb);
}
int li = aabb.get_longest_axis_index();
@@ -1041,28 +485,29 @@ int CSGBrushOperation::MeshMerge::_create_bvh(BVH *p_bvh, BVH **p_bb, int p_from
switch (li) {
case Vector3::AXIS_X: {
- SortArray<BVH *, BVHCmpX> sort_x;
- sort_x.nth_element(0, p_size, p_size / 2, &p_bb[p_from]);
+ SortArray<FaceBVH *, FaceBVHCmpX> sort_x;
+ sort_x.nth_element(0, p_size, p_size / 2, &facebvhptrptr[p_from]);
//sort_x.sort(&p_bb[p_from],p_size);
} break;
+
case Vector3::AXIS_Y: {
- SortArray<BVH *, BVHCmpY> sort_y;
- sort_y.nth_element(0, p_size, p_size / 2, &p_bb[p_from]);
+ SortArray<FaceBVH *, FaceBVHCmpY> sort_y;
+ sort_y.nth_element(0, p_size, p_size / 2, &facebvhptrptr[p_from]);
//sort_y.sort(&p_bb[p_from],p_size);
} break;
+
case Vector3::AXIS_Z: {
- SortArray<BVH *, BVHCmpZ> sort_z;
- sort_z.nth_element(0, p_size, p_size / 2, &p_bb[p_from]);
+ SortArray<FaceBVH *, FaceBVHCmpZ> sort_z;
+ sort_z.nth_element(0, p_size, p_size / 2, &facebvhptrptr[p_from]);
//sort_z.sort(&p_bb[p_from],p_size);
-
} break;
}
- int left = _create_bvh(p_bvh, p_bb, p_from, p_size / 2, p_depth + 1, max_depth, max_alloc);
- int right = _create_bvh(p_bvh, p_bb, p_from + p_size / 2, p_size - p_size / 2, p_depth + 1, max_depth, max_alloc);
+ int left = _create_bvh(facebvhptr, facebvhptrptr, p_from, p_size / 2, p_depth + 1, r_max_depth, r_max_alloc);
+ int right = _create_bvh(facebvhptr, facebvhptrptr, p_from + p_size / 2, p_size - p_size / 2, p_depth + 1, r_max_depth, r_max_alloc);
- int index = max_alloc++;
- BVH *_new = &p_bvh[index];
+ int index = r_max_alloc++;
+ FaceBVH *_new = &facebvhptr[index];
_new->aabb = aabb;
_new->center = aabb.position + aabb.size * 0.5;
_new->face = -1;
@@ -1073,7 +518,27 @@ int CSGBrushOperation::MeshMerge::_create_bvh(BVH *p_bvh, BVH **p_bb, int p_from
return index;
}
-int CSGBrushOperation::MeshMerge::_bvh_count_intersections(BVH *bvhptr, int p_max_depth, int p_bvh_first, const Vector3 &p_begin, const Vector3 &p_end, int p_exclude) const {
+void CSGBrushOperation::MeshMerge::_add_distance(List<real_t> &r_intersectionsA, List<real_t> &r_intersectionsB, bool p_from_B, real_t p_distance) const {
+
+ List<real_t> &intersections = p_from_B ? r_intersectionsB : r_intersectionsA;
+
+ // Check if distance exists.
+ for (const List<real_t>::Element *E = intersections.front(); E; E = E->next())
+ if (Math::abs(**E - p_distance) < vertex_snap) return;
+
+ intersections.push_back(p_distance);
+}
+
+bool CSGBrushOperation::MeshMerge::_bvh_inside(FaceBVH *facebvhptr, int p_max_depth, int p_bvh_first, int p_face_idx) const {
+
+ Face face = faces[p_face_idx];
+ Vector3 face_points[3] = {
+ points[face.points[0]],
+ points[face.points[1]],
+ points[face.points[2]]
+ };
+ Vector3 face_center = (face_points[0] + face_points[1] + face_points[2]) / 3.0;
+ Vector3 face_normal = Plane(face_points[0], face_points[1], face_points[2]).normal;
uint32_t *stack = (uint32_t *)alloca(sizeof(int) * p_max_depth);
@@ -1084,54 +549,58 @@ int CSGBrushOperation::MeshMerge::_bvh_count_intersections(BVH *bvhptr, int p_ma
VISIT_DONE_BIT = 3,
VISITED_BIT_SHIFT = 29,
NODE_IDX_MASK = (1 << VISITED_BIT_SHIFT) - 1,
- VISITED_BIT_MASK = ~NODE_IDX_MASK,
-
+ VISITED_BIT_MASK = ~NODE_IDX_MASK
};
- int intersections = 0;
+ List<real_t> intersectionsA;
+ List<real_t> intersectionsB;
int level = 0;
-
- const Vector3 *vertexptr = points.ptr();
- const Face *facesptr = faces.ptr();
- AABB segment_aabb;
- segment_aabb.position = p_begin;
- segment_aabb.expand_to(p_end);
-
int pos = p_bvh_first;
-
stack[0] = pos;
+
while (true) {
uint32_t node = stack[level] & NODE_IDX_MASK;
- const BVH &b = bvhptr[node];
+ const FaceBVH *current_facebvhptr = &(facebvhptr[node]);
bool done = false;
switch (stack[level] >> VISITED_BIT_SHIFT) {
- case TEST_AABB_BIT: {
-
- if (b.face >= 0) {
-
- const BVH *bp = &b;
- while (bp) {
-
- bool valid = segment_aabb.intersects(bp->aabb) && bp->aabb.intersects_segment(p_begin, p_end);
-
- if (valid && p_exclude != bp->face) {
- const Face &s = facesptr[bp->face];
- Face3 f3(vertexptr[s.points[0]], vertexptr[s.points[1]], vertexptr[s.points[2]]);
-
- Vector3 res;
+ case TEST_AABB_BIT: {
- if (f3.intersects_segment(p_begin, p_end, &res)) {
- intersections++;
+ if (current_facebvhptr->face >= 0) {
+
+ while (current_facebvhptr) {
+
+ if (p_face_idx != current_facebvhptr->face &&
+ current_facebvhptr->aabb.intersects_ray(face_center, face_normal)) {
+
+ const Face &current_face = faces[current_facebvhptr->face];
+ Vector3 current_points[3] = {
+ points[current_face.points[0]],
+ points[current_face.points[1]],
+ points[current_face.points[2]]
+ };
+ Vector3 current_normal = Plane(current_points[0], current_points[1], current_points[2]).normal;
+ Vector3 intersection_point;
+
+ // Check if faces are co-planar.
+ if ((current_normal - face_normal).length_squared() < CMP_EPSILON2 &&
+ is_point_in_triangle(face_center, current_points)) {
+ // Only add an intersection if checking a B face.
+ if (face.from_b)
+ _add_distance(intersectionsA, intersectionsB, current_face.from_b, 0);
+ } else if (ray_intersects_triangle(face_center, face_normal, current_points, CMP_EPSILON, intersection_point)) {
+ real_t distance = (intersection_point - face_center).length();
+ _add_distance(intersectionsA, intersectionsB, current_face.from_b, distance);
}
}
- if (bp->next != -1) {
- bp = &bvhptr[bp->next];
+
+ if (current_facebvhptr->next != -1) {
+ current_facebvhptr = &facebvhptr[current_facebvhptr->next];
} else {
- bp = NULL;
+ current_facebvhptr = nullptr;
}
}
@@ -1139,32 +608,33 @@ int CSGBrushOperation::MeshMerge::_bvh_count_intersections(BVH *bvhptr, int p_ma
} else {
- bool valid = segment_aabb.intersects(b.aabb) && b.aabb.intersects_segment(p_begin, p_end);
+ bool valid = current_facebvhptr->aabb.intersects_ray(face_center, face_normal);
if (!valid) {
-
stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
-
} else {
stack[level] = (VISIT_LEFT_BIT << VISITED_BIT_SHIFT) | node;
}
}
continue;
}
+
case VISIT_LEFT_BIT: {
stack[level] = (VISIT_RIGHT_BIT << VISITED_BIT_SHIFT) | node;
- stack[level + 1] = b.left | TEST_AABB_BIT;
+ stack[level + 1] = current_facebvhptr->left | TEST_AABB_BIT;
level++;
continue;
}
+
case VISIT_RIGHT_BIT: {
stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
- stack[level + 1] = b.right | TEST_AABB_BIT;
+ stack[level + 1] = current_facebvhptr->right | TEST_AABB_BIT;
level++;
continue;
}
+
case VISIT_DONE_BIT: {
if (level == 0) {
@@ -1180,130 +650,112 @@ int CSGBrushOperation::MeshMerge::_bvh_count_intersections(BVH *bvhptr, int p_ma
break;
}
- return intersections;
+ // Inside if face normal intersects other faces an odd number of times.
+ return (intersectionsA.size() + intersectionsB.size()) & 1;
}
void CSGBrushOperation::MeshMerge::mark_inside_faces() {
- // mark faces that are inside. This helps later do the boolean ops when merging.
- // this approach is very brute force (with a bunch of optimizatios, such as BVH and pre AABB intersection test)
+ // Mark faces that are inside. This helps later do the boolean ops when merging.
+ // This approach is very brute force with a bunch of optimizations,
+ // such as BVH and pre AABB intersection test.
- AABB aabb;
+ Vector<FaceBVH> bvhvec;
+ bvhvec.resize(faces.size() * 3); // Will never be larger than this (TODO: Make better)
+ FaceBVH *facebvh = bvhvec.ptrw();
- for (int i = 0; i < points.size(); i++) {
- if (i == 0) {
- aabb.position = points[i];
- } else {
- aabb.expand_to(points[i]);
- }
- }
-
- float max_distance = aabb.size.length() * 1.2;
-
- Vector<BVH> bvhvec;
- bvhvec.resize(faces.size() * 3); //will never be larger than this (todo make better)
- BVH *bvh = bvhvec.ptrw();
-
- AABB faces_a;
- AABB faces_b;
+ AABB aabb_a;
+ AABB aabb_b;
bool first_a = true;
bool first_b = true;
for (int i = 0; i < faces.size(); i++) {
- bvh[i].left = -1;
- bvh[i].right = -1;
- bvh[i].face = i;
- bvh[i].aabb.position = points[faces[i].points[0]];
- bvh[i].aabb.expand_to(points[faces[i].points[1]]);
- bvh[i].aabb.expand_to(points[faces[i].points[2]]);
- bvh[i].center = bvh[i].aabb.position + bvh[i].aabb.size * 0.5;
- bvh[i].next = -1;
+ facebvh[i].left = -1;
+ facebvh[i].right = -1;
+ facebvh[i].face = i;
+ facebvh[i].aabb.position = points[faces[i].points[0]];
+ facebvh[i].aabb.expand_to(points[faces[i].points[1]]);
+ facebvh[i].aabb.expand_to(points[faces[i].points[2]]);
+ facebvh[i].center = facebvh[i].aabb.position + facebvh[i].aabb.size * 0.5;
+ facebvh[i].aabb.grow_by(vertex_snap);
+ facebvh[i].next = -1;
+
if (faces[i].from_b) {
if (first_b) {
- faces_b = bvh[i].aabb;
+ aabb_b = facebvh[i].aabb;
first_b = false;
} else {
- faces_b.merge_with(bvh[i].aabb);
+ aabb_b.merge_with(facebvh[i].aabb);
}
} else {
if (first_a) {
- faces_a = bvh[i].aabb;
+ aabb_a = facebvh[i].aabb;
first_a = false;
} else {
- faces_a.merge_with(bvh[i].aabb);
+ aabb_a.merge_with(facebvh[i].aabb);
}
}
}
- AABB intersection_aabb = faces_a.intersection(faces_b);
- intersection_aabb.grow_by(intersection_aabb.get_longest_axis_size() * 0.01); //grow a little, avoid numerical error
+ AABB intersection_aabb = aabb_a.intersection(aabb_b);
- if (intersection_aabb.size == Vector3()) //AABB do not intersect, so neither do shapes.
+ // Check if shape AABBs intersect.
+ if (intersection_aabb.size == Vector3())
return;
- Vector<BVH *> bvhtrvec;
+ Vector<FaceBVH *> bvhtrvec;
bvhtrvec.resize(faces.size());
- BVH **bvhptr = bvhtrvec.ptrw();
+ FaceBVH **bvhptr = bvhtrvec.ptrw();
for (int i = 0; i < faces.size(); i++) {
-
- bvhptr[i] = &bvh[i];
+ bvhptr[i] = &facebvh[i];
}
int max_depth = 0;
int max_alloc = faces.size();
- _create_bvh(bvh, bvhptr, 0, faces.size(), 1, max_depth, max_alloc);
+ _create_bvh(facebvh, bvhptr, 0, faces.size(), 1, max_depth, max_alloc);
for (int i = 0; i < faces.size(); i++) {
- if (!intersection_aabb.intersects(bvh[i].aabb))
- continue; //not in AABB intersection, so not in face intersection
- Vector3 center = points[faces[i].points[0]];
- center += points[faces[i].points[1]];
- center += points[faces[i].points[2]];
- center /= 3.0;
-
- Plane plane(points[faces[i].points[0]], points[faces[i].points[1]], points[faces[i].points[2]]);
- Vector3 target = center + plane.normal * max_distance + Vector3(0.0001234, 0.000512, 0.00013423); //reduce chance of edge hits by doing a small increment
-
- int intersections = _bvh_count_intersections(bvh, max_depth, max_alloc - 1, center, target, i);
+ // Check if face AABB intersects the intersection AABB.
+ if (!intersection_aabb.intersects_inclusive(facebvh[i].aabb))
+ continue;
- if (intersections & 1) {
+ if (_bvh_inside(facebvh, max_depth, max_alloc - 1, i))
faces.write[i].inside = true;
- }
}
}
-void CSGBrushOperation::MeshMerge::add_face(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, const Vector2 &p_uv_a, const Vector2 &p_uv_b, const Vector2 &p_uv_c, bool p_smooth, bool p_invert, const Ref<Material> &p_material, bool p_from_b) {
+void CSGBrushOperation::MeshMerge::add_face(const Vector3 p_points[], const Vector2 p_uvs[], bool p_smooth, bool p_invert, const Ref<Material> &p_material, bool p_from_b) {
- Vector3 src_points[3] = { p_a, p_b, p_c };
- Vector2 src_uvs[3] = { p_uv_a, p_uv_b, p_uv_c };
int indices[3];
for (int i = 0; i < 3; i++) {
VertexKey vk;
- vk.x = int((double(src_points[i].x) + double(vertex_snap) * 0.31234) / double(vertex_snap));
- vk.y = int((double(src_points[i].y) + double(vertex_snap) * 0.31234) / double(vertex_snap));
- vk.z = int((double(src_points[i].z) + double(vertex_snap) * 0.31234) / double(vertex_snap));
+ vk.x = int((double(p_points[i].x) + double(vertex_snap) * 0.31234) / double(vertex_snap));
+ vk.y = int((double(p_points[i].y) + double(vertex_snap) * 0.31234) / double(vertex_snap));
+ vk.z = int((double(p_points[i].z) + double(vertex_snap) * 0.31234) / double(vertex_snap));
int res;
if (snap_cache.lookup(vk, res)) {
indices[i] = res;
} else {
indices[i] = points.size();
- points.push_back(src_points[i]);
+ points.push_back(p_points[i]);
snap_cache.set(vk, indices[i]);
}
}
+ // Don't add degenerate faces.
if (indices[0] == indices[2] || indices[0] == indices[1] || indices[1] == indices[2])
- return; //not adding degenerate
+ return;
MeshMerge::Face face;
face.from_b = p_from_b;
face.inside = false;
face.smooth = p_smooth;
face.invert = p_invert;
+
if (p_material.is_valid()) {
if (!materials.has(p_material)) {
face.material_idx = materials.size();
@@ -1316,205 +768,708 @@ void CSGBrushOperation::MeshMerge::add_face(const Vector3 &p_a, const Vector3 &p
}
for (int k = 0; k < 3; k++) {
-
face.points[k] = indices[k];
- face.uvs[k] = src_uvs[k];
- ;
+ face.uvs[k] = p_uvs[k];
}
faces.push_back(face);
}
-void CSGBrushOperation::merge_brushes(Operation p_operation, const CSGBrush &p_A, const CSGBrush &p_B, CSGBrush &result, float p_snap) {
+// CSGBrushOperation::Build2DFaces
- CallbackData cd;
- cd.self = this;
- cd.A = &p_A;
- cd.B = &p_B;
+int CSGBrushOperation::Build2DFaces::_get_point_idx(const Vector2 &p_point) {
- MeshMerge mesh_merge;
- mesh_merge.vertex_snap = p_snap;
-
- //check intersections between faces. Use AABB to speed up precheck
- //this generates list of buildpolys and clips them.
- //this was originally BVH optimized, but its not really worth it.
- for (int i = 0; i < p_A.faces.size(); i++) {
- cd.face_a = i;
- for (int j = 0; j < p_B.faces.size(); j++) {
- if (p_A.faces[i].aabb.intersects(p_B.faces[j].aabb)) {
- _collision_callback(&p_A, i, cd.build_polys_A, &p_B, j, cd.build_polys_B, mesh_merge);
- }
- }
+ for (int vertex_idx = 0; vertex_idx < vertices.size(); ++vertex_idx) {
+ if ((p_point - vertices[vertex_idx].point).length_squared() < vertex_snap2)
+ return vertex_idx;
}
+ return -1;
+}
- //merge the already cliped polys back to 3D
- for (Map<int, BuildPoly>::Element *E = cd.build_polys_A.front(); E; E = E->next()) {
- _merge_poly(mesh_merge, E->key(), E->get(), false);
- }
+int CSGBrushOperation::Build2DFaces::_add_vertex(const Vertex2D &p_vertex) {
- for (Map<int, BuildPoly>::Element *E = cd.build_polys_B.front(); E; E = E->next()) {
- _merge_poly(mesh_merge, E->key(), E->get(), true);
- }
+ // Check if vertex exists.
+ int vertex_id = _get_point_idx(p_vertex.point);
+ if (vertex_id != -1) return vertex_id;
- //merge the non clipped faces back
+ vertices.push_back(p_vertex);
+ return vertices.size() - 1;
+}
- for (int i = 0; i < p_A.faces.size(); i++) {
+void CSGBrushOperation::Build2DFaces::_add_vertex_idx_sorted(Vector<int> &r_vertex_indices, int p_new_vertex_index) {
- if (cd.build_polys_A.has(i))
- continue; //made from buildpoly, skipping
+ if (p_new_vertex_index >= 0 && r_vertex_indices.find(p_new_vertex_index) == -1) {
+ ERR_FAIL_COND_MSG(p_new_vertex_index >= vertices.size(), "Invalid vertex index.");
- Vector3 points[3];
- Vector2 uvs[3];
- for (int j = 0; j < 3; j++) {
- points[j] = p_A.faces[i].vertices[j];
- uvs[j] = p_A.faces[i].uvs[j];
- }
- Ref<Material> material;
- if (p_A.faces[i].material != -1) {
- material = p_A.materials[p_A.faces[i].material];
+ // The first vertex.
+ if (r_vertex_indices.size() == 0) {
+ // Simply add it.
+ r_vertex_indices.push_back(p_new_vertex_index);
+ return;
}
- mesh_merge.add_face(points[0], points[1], points[2], uvs[0], uvs[1], uvs[2], p_A.faces[i].smooth, p_A.faces[i].invert, material, false);
- }
- for (int i = 0; i < p_B.faces.size(); i++) {
+ // The second vertex.
+ if (r_vertex_indices.size() == 1) {
- if (cd.build_polys_B.has(i))
- continue; //made from buildpoly, skipping
+ Vector2 first_point = vertices[r_vertex_indices[0]].point;
+ Vector2 new_point = vertices[p_new_vertex_index].point;
- Vector3 points[3];
- Vector2 uvs[3];
- for (int j = 0; j < 3; j++) {
- points[j] = p_B.faces[i].vertices[j];
- uvs[j] = p_B.faces[i].uvs[j];
+ // Sort along the axis with the greatest difference.
+ int axis = 0;
+ if (Math::abs(new_point.x - first_point.x) < Math::abs(new_point.y - first_point.y)) axis = 1;
+
+ // Add it to the beginnig or the end appropriately.
+ if (new_point[axis] < first_point[axis])
+ r_vertex_indices.insert(0, p_new_vertex_index);
+ else
+ r_vertex_indices.push_back(p_new_vertex_index);
+
+ return;
}
- Ref<Material> material;
- if (p_B.faces[i].material != -1) {
- material = p_B.materials[p_B.faces[i].material];
+
+ // Third or later vertices.
+ Vector2 first_point = vertices[r_vertex_indices[0]].point;
+ Vector2 last_point = vertices[r_vertex_indices[r_vertex_indices.size() - 1]].point;
+ Vector2 new_point = vertices[p_new_vertex_index].point;
+
+ // Determine axis being sorted against i.e. the axis with the greatest difference.
+ int axis = 0;
+ if (Math::abs(last_point.x - first_point.x) < Math::abs(last_point.y - first_point.y)) axis = 1;
+
+ // Insert the point at the appropriate index.
+ for (int insert_idx = 0; insert_idx < r_vertex_indices.size(); ++insert_idx) {
+ Vector2 insert_point = vertices[r_vertex_indices[insert_idx]].point;
+ if (new_point[axis] < insert_point[axis]) {
+ r_vertex_indices.insert(insert_idx, p_new_vertex_index);
+ return;
+ }
}
- mesh_merge.add_face(points[0], points[1], points[2], uvs[0], uvs[1], uvs[2], p_B.faces[i].smooth, p_B.faces[i].invert, material, true);
+
+ // New largest, add it to the end.
+ r_vertex_indices.push_back(p_new_vertex_index);
}
+}
- //mark faces that ended up inside the intersection
- mesh_merge.mark_inside_faces();
+void CSGBrushOperation::Build2DFaces::_merge_faces(const Vector<int> &p_segment_indices) {
- //regen new brush to start filling it again
- result.clear();
+ int segments = p_segment_indices.size() - 1;
+ if (segments < 2) return;
- switch (p_operation) {
+ // Faces around an inner vertex are merged by moving the inner vertex to the first vertex.
+ for (int sorted_idx = 1; sorted_idx < segments; ++sorted_idx) {
- case OPERATION_UNION: {
+ int closest_idx = 0;
+ int inner_idx = p_segment_indices[sorted_idx];
- int outside_count = 0;
+ if (sorted_idx > segments / 2) {
+ // Merge to other segment end.
+ closest_idx = segments;
+ // Reverse the merge order.
+ inner_idx = p_segment_indices[segments + segments / 2 - sorted_idx];
+ }
- for (int i = 0; i < mesh_merge.faces.size(); i++) {
- if (mesh_merge.faces[i].inside)
- continue;
+ // Find the mergable faces.
+ Vector<int> merge_faces_idx;
+ Vector<Face2D> merge_faces;
+ Vector<int> merge_faces_inner_vertex_idx;
+ for (int face_idx = 0; face_idx < faces.size(); ++face_idx) {
+ for (int face_vertex_idx = 0; face_vertex_idx < 3; ++face_vertex_idx) {
+ if (faces[face_idx].vertex_idx[face_vertex_idx] == inner_idx) {
+ merge_faces_idx.push_back(face_idx);
+ merge_faces.push_back(faces[face_idx]);
+ merge_faces_inner_vertex_idx.push_back(face_vertex_idx);
+ }
+ }
+ }
- outside_count++;
+ Vector<int> degenerate_points;
+
+ // Create the new faces.
+ for (int merge_idx = 0; merge_idx < merge_faces.size(); ++merge_idx) {
+
+ int outer_edge_idx[2];
+ outer_edge_idx[0] = merge_faces[merge_idx].vertex_idx[(merge_faces_inner_vertex_idx[merge_idx] + 1) % 3];
+ outer_edge_idx[1] = merge_faces[merge_idx].vertex_idx[(merge_faces_inner_vertex_idx[merge_idx] + 2) % 3];
+
+ // Skip flattened faces.
+ if (outer_edge_idx[0] == p_segment_indices[closest_idx] ||
+ outer_edge_idx[1] == p_segment_indices[closest_idx]) continue;
+
+ //Don't create degenerate triangles.
+ Vector2 edge1[2] = {
+ vertices[outer_edge_idx[0]].point,
+ vertices[p_segment_indices[closest_idx]].point
+ };
+ Vector2 edge2[2] = {
+ vertices[outer_edge_idx[1]].point,
+ vertices[p_segment_indices[closest_idx]].point
+ };
+ if (are_segements_parallel(edge1, edge2, vertex_snap2)) {
+ degenerate_points.push_back(outer_edge_idx[0]);
+ degenerate_points.push_back(outer_edge_idx[1]);
+ continue;
}
- result.faces.resize(outside_count);
+ // Create new faces.
+ Face2D new_face;
+ new_face.vertex_idx[0] = p_segment_indices[closest_idx];
+ new_face.vertex_idx[1] = outer_edge_idx[0];
+ new_face.vertex_idx[2] = outer_edge_idx[1];
+ faces.push_back(new_face);
+ }
- outside_count = 0;
+ // Delete the old faces in reverse index order.
+ merge_faces_idx.sort();
+ merge_faces_idx.invert();
+ for (int i = 0; i < merge_faces_idx.size(); ++i)
+ faces.remove(merge_faces_idx[i]);
+
+ if (degenerate_points.size() == 0) continue;
+
+ // Split faces using degenerate points.
+ for (int face_idx = 0; face_idx < faces.size(); ++face_idx) {
+
+ Face2D face = faces[face_idx];
+ Vertex2D face_vertices[3] = {
+ vertices[face.vertex_idx[0]],
+ vertices[face.vertex_idx[1]],
+ vertices[face.vertex_idx[2]]
+ };
+ Vector2 face_points[3] = {
+ face_vertices[0].point,
+ face_vertices[1].point,
+ face_vertices[2].point
+ };
+
+ for (int point_idx = 0; point_idx < degenerate_points.size(); ++point_idx) {
+
+ int degenerate_idx = degenerate_points[point_idx];
+ Vector2 point_2D = vertices[degenerate_idx].point;
+
+ // Check if point is existing face vertex.
+ bool existing = false;
+ for (int i = 0; i < 3; ++i) {
+ if ((point_2D - face_vertices[i].point).length_squared() < vertex_snap2) {
+ existing = true;
+ break;
+ }
+ }
+ if (existing) continue;
- for (int i = 0; i < mesh_merge.faces.size(); i++) {
- if (mesh_merge.faces[i].inside)
- continue;
- for (int j = 0; j < 3; j++) {
- result.faces.write[outside_count].vertices[j] = mesh_merge.points[mesh_merge.faces[i].points[j]];
- result.faces.write[outside_count].uvs[j] = mesh_merge.faces[i].uvs[j];
+ // Check if point is on an each edge.
+ for (int face_edge_idx = 0; face_edge_idx < 3; ++face_edge_idx) {
+
+ Vector2 edge_points[2] = {
+ face_points[face_edge_idx],
+ face_points[(face_edge_idx + 1) % 3]
+ };
+ Vector2 closest_point = Geometry::get_closest_point_to_segment_2d(point_2D, edge_points);
+
+ if ((closest_point - point_2D).length_squared() < vertex_snap2) {
+
+ int opposite_vertex_idx = face.vertex_idx[(face_edge_idx + 2) % 3];
+
+ // If new vertex snaps to degenerate vertex, just delete this face.
+ if (degenerate_idx == opposite_vertex_idx) {
+ faces.remove(face_idx);
+ // Update index.
+ --face_idx;
+ break;
+ }
+
+ // Create two new faces around the new edge and remove this face.
+ // The new edge is the last edge.
+ Face2D left_face;
+ left_face.vertex_idx[0] = degenerate_idx;
+ left_face.vertex_idx[1] = face.vertex_idx[(face_edge_idx + 1) % 3];
+ left_face.vertex_idx[2] = opposite_vertex_idx;
+ Face2D right_face;
+ right_face.vertex_idx[0] = opposite_vertex_idx;
+ right_face.vertex_idx[1] = face.vertex_idx[face_edge_idx];
+ right_face.vertex_idx[2] = degenerate_idx;
+ faces.remove(face_idx);
+ faces.insert(face_idx, right_face);
+ faces.insert(face_idx, left_face);
+
+ // Don't check against the new faces.
+ ++face_idx;
+
+ // No need to check other edges.
+ break;
+ }
}
+ }
+ }
+ }
+}
- result.faces.write[outside_count].smooth = mesh_merge.faces[i].smooth;
- result.faces.write[outside_count].invert = mesh_merge.faces[i].invert;
- result.faces.write[outside_count].material = mesh_merge.faces[i].material_idx;
- outside_count++;
+void CSGBrushOperation::Build2DFaces::_find_edge_intersections(const Vector2 p_segment_points[2], Vector<int> &r_segment_indices) {
+
+ // For each face.
+ for (int face_idx = 0; face_idx < faces.size(); ++face_idx) {
+
+ Face2D face = faces[face_idx];
+ Vertex2D face_vertices[3] = {
+ vertices[face.vertex_idx[0]],
+ vertices[face.vertex_idx[1]],
+ vertices[face.vertex_idx[2]]
+ };
+
+ // Check each edge.
+ for (int face_edge_idx = 0; face_edge_idx < 3; ++face_edge_idx) {
+
+ Vector2 edge_points[2] = {
+ face_vertices[face_edge_idx].point,
+ face_vertices[(face_edge_idx + 1) % 3].point
+ };
+ Vector2 edge_uvs[2] = {
+ face_vertices[face_edge_idx].uv,
+ face_vertices[(face_edge_idx + 1) % 3].uv
+ };
+ Vector2 intersection_point;
+
+ // First check if the ends of the segment are on the edge.
+ bool on_edge = false;
+ for (int edge_point_idx = 0; edge_point_idx < 2; ++edge_point_idx) {
+ intersection_point = Geometry::get_closest_point_to_segment_2d(p_segment_points[edge_point_idx], edge_points);
+ if ((intersection_point - p_segment_points[edge_point_idx]).length_squared() < vertex_snap2) {
+ on_edge = true;
+ break;
+ }
}
- result._regen_face_aabbs();
+ // Else check if the segment intersects the edge.
+ if (on_edge || Geometry::segment_intersects_segment_2d(p_segment_points[0], p_segment_points[1], edge_points[0], edge_points[1], &intersection_point)) {
+
+ // Check if intersection point is an edge point.
+ if ((intersection_point - edge_points[0]).length_squared() < vertex_snap2 ||
+ (intersection_point - edge_points[1]).length_squared() < vertex_snap2) continue;
+
+ // Check if edge exists, by checking if the intersecting segment is parallel to the edge.
+ if (are_segements_parallel(p_segment_points, edge_points, vertex_snap2)) continue;
+
+ // Add the intersection point as a new vertex.
+ Vertex2D new_vertex;
+ new_vertex.point = intersection_point;
+ new_vertex.uv = interpolate_segment_uv(edge_points, edge_uvs, intersection_point);
+ int new_vertex_idx = _add_vertex(new_vertex);
+ int opposite_vertex_idx = face.vertex_idx[(face_edge_idx + 2) % 3];
+ _add_vertex_idx_sorted(r_segment_indices, new_vertex_idx);
+
+ // If new vertex snaps to opposite vertex, just delete this face.
+ if (new_vertex_idx == opposite_vertex_idx) {
+ faces.remove(face_idx);
+ // Update index.
+ --face_idx;
+ break;
+ }
- } break;
- case OPERATION_INTERSECTION: {
+ // Don't create degenerate triangles.
+ Vector2 split_edge1[2] = { vertices[new_vertex_idx].point, edge_points[0] };
+ Vector2 split_edge2[2] = { vertices[new_vertex_idx].point, edge_points[1] };
+ Vector2 new_edge[2] = { vertices[new_vertex_idx].point, vertices[opposite_vertex_idx].point };
+ if (are_segements_parallel(split_edge1, new_edge, vertex_snap2) &&
+ are_segements_parallel(split_edge2, new_edge, vertex_snap2)) {
+ break;
+ }
- int inside_count = 0;
+ // If opposite point is on the segemnt, add its index to segment indices too.
+ Vector2 closest_point = Geometry::get_closest_point_to_segment_2d(vertices[opposite_vertex_idx].point, p_segment_points);
+ if ((closest_point - vertices[opposite_vertex_idx].point).length_squared() < vertex_snap2)
+ _add_vertex_idx_sorted(r_segment_indices, opposite_vertex_idx);
+
+ // Create two new faces around the new edge and remove this face.
+ // The new edge is the last edge.
+ Face2D left_face;
+ left_face.vertex_idx[0] = new_vertex_idx;
+ left_face.vertex_idx[1] = face.vertex_idx[(face_edge_idx + 1) % 3];
+ left_face.vertex_idx[2] = opposite_vertex_idx;
+ Face2D right_face;
+ right_face.vertex_idx[0] = opposite_vertex_idx;
+ right_face.vertex_idx[1] = face.vertex_idx[face_edge_idx];
+ right_face.vertex_idx[2] = new_vertex_idx;
+ faces.remove(face_idx);
+ faces.insert(face_idx, right_face);
+ faces.insert(face_idx, left_face);
+
+ // Check against the new faces.
+ --face_idx;
+ break;
+ }
+ }
+ }
+}
- for (int i = 0; i < mesh_merge.faces.size(); i++) {
- if (!mesh_merge.faces[i].inside)
- continue;
+int CSGBrushOperation::Build2DFaces::_insert_point(const Vector2 &p_point) {
+
+ int new_vertex_idx = -1;
+
+ for (int face_idx = 0; face_idx < faces.size(); ++face_idx) {
+
+ Face2D face = faces[face_idx];
+ Vertex2D face_vertices[3] = {
+ vertices[face.vertex_idx[0]],
+ vertices[face.vertex_idx[1]],
+ vertices[face.vertex_idx[2]]
+ };
+ Vector2 points[3] = {
+ face_vertices[0].point,
+ face_vertices[1].point,
+ face_vertices[2].point
+ };
+ Vector2 uvs[3] = {
+ face_vertices[0].uv,
+ face_vertices[1].uv,
+ face_vertices[2].uv
+ };
+
+ // Check if point is existing face vertex.
+ for (int i = 0; i < 3; ++i) {
+ if ((p_point - face_vertices[i].point).length_squared() < vertex_snap2)
+ return face.vertex_idx[i];
+ }
- inside_count++;
- }
+ // Check if point is on an each edge.
+ bool on_edge = false;
+ for (int face_edge_idx = 0; face_edge_idx < 3; ++face_edge_idx) {
+
+ Vector2 edge_points[2] = {
+ points[face_edge_idx],
+ points[(face_edge_idx + 1) % 3]
+ };
+ Vector2 edge_uvs[2] = {
+ uvs[face_edge_idx],
+ uvs[(face_edge_idx + 1) % 3]
+ };
+
+ Vector2 closest_point = Geometry::get_closest_point_to_segment_2d(p_point, edge_points);
+ if ((closest_point - p_point).length_squared() < vertex_snap2) {
+ on_edge = true;
+
+ // Add the point as a new vertex.
+ Vertex2D new_vertex;
+ new_vertex.point = p_point;
+ new_vertex.uv = interpolate_segment_uv(edge_points, edge_uvs, p_point);
+ new_vertex_idx = _add_vertex(new_vertex);
+ int opposite_vertex_idx = face.vertex_idx[(face_edge_idx + 2) % 3];
+
+ // If new vertex snaps to opposite vertex, just delete this face.
+ if (new_vertex_idx == opposite_vertex_idx) {
+ faces.remove(face_idx);
+ // Update index.
+ --face_idx;
+ break;
+ }
- result.faces.resize(inside_count);
+ // Don't create degenerate triangles.
+ Vector2 split_edge1[2] = { vertices[new_vertex_idx].point, edge_points[0] };
+ Vector2 split_edge2[2] = { vertices[new_vertex_idx].point, edge_points[1] };
+ Vector2 new_edge[2] = { vertices[new_vertex_idx].point, vertices[opposite_vertex_idx].point };
+ if (are_segements_parallel(split_edge1, new_edge, vertex_snap2) &&
+ are_segements_parallel(split_edge2, new_edge, vertex_snap2)) {
+ break;
+ }
- inside_count = 0;
+ // Create two new faces around the new edge and remove this face.
+ // The new edge is the last edge.
+ Face2D left_face;
+ left_face.vertex_idx[0] = new_vertex_idx;
+ left_face.vertex_idx[1] = face.vertex_idx[(face_edge_idx + 1) % 3];
+ left_face.vertex_idx[2] = opposite_vertex_idx;
+ Face2D right_face;
+ right_face.vertex_idx[0] = opposite_vertex_idx;
+ right_face.vertex_idx[1] = face.vertex_idx[face_edge_idx];
+ right_face.vertex_idx[2] = new_vertex_idx;
+ faces.remove(face_idx);
+ faces.insert(face_idx, right_face);
+ faces.insert(face_idx, left_face);
+
+ // Don't check against the new faces.
+ ++face_idx;
+
+ // No need to check other edges.
+ break;
+ }
+ }
- for (int i = 0; i < mesh_merge.faces.size(); i++) {
- if (!mesh_merge.faces[i].inside)
+ // If not on an edge, check if the point is inside the face.
+ if (!on_edge && Geometry::is_point_in_triangle(p_point, face_vertices[0].point, face_vertices[1].point, face_vertices[2].point)) {
+
+ // Add the point as a new vertex.
+ Vertex2D new_vertex;
+ new_vertex.point = p_point;
+ new_vertex.uv = interpolate_triangle_uv(points, uvs, p_point);
+ new_vertex_idx = _add_vertex(new_vertex);
+
+ // Create three new faces around this point and remove this face.
+ // The new vertex is the last vertex.
+ for (int i = 0; i < 3; ++i) {
+
+ // Don't create degenerate triangles.
+ Vector2 edge[2] = { points[i], points[(i + 1) % 3] };
+ Vector2 new_edge1[2] = { vertices[new_vertex_idx].point, points[i] };
+ Vector2 new_edge2[2] = { vertices[new_vertex_idx].point, points[(i + 1) % 3] };
+ if (are_segements_parallel(edge, new_edge1, vertex_snap2) &&
+ are_segements_parallel(edge, new_edge2, vertex_snap2)) {
continue;
- for (int j = 0; j < 3; j++) {
- result.faces.write[inside_count].vertices[j] = mesh_merge.points[mesh_merge.faces[i].points[j]];
- result.faces.write[inside_count].uvs[j] = mesh_merge.faces[i].uvs[j];
}
- result.faces.write[inside_count].smooth = mesh_merge.faces[i].smooth;
- result.faces.write[inside_count].invert = mesh_merge.faces[i].invert;
- result.faces.write[inside_count].material = mesh_merge.faces[i].material_idx;
- inside_count++;
+ Face2D new_face;
+ new_face.vertex_idx[0] = face.vertex_idx[i];
+ new_face.vertex_idx[1] = face.vertex_idx[(i + 1) % 3];
+ new_face.vertex_idx[2] = new_vertex_idx;
+ faces.push_back(new_face);
}
+ faces.remove(face_idx);
- result._regen_face_aabbs();
+ // No need to check other faces.
+ break;
+ }
+ }
- } break;
- case OPERATION_SUBSTRACTION: {
+ return new_vertex_idx;
+}
- int face_count = 0;
+void CSGBrushOperation::Build2DFaces::insert(const CSGBrush &p_brush, int p_face_idx) {
- for (int i = 0; i < mesh_merge.faces.size(); i++) {
- if (mesh_merge.faces[i].from_b && !mesh_merge.faces[i].inside)
- continue;
- if (!mesh_merge.faces[i].from_b && mesh_merge.faces[i].inside)
- continue;
+ // Find edge points that cross the plane and face points that are in the plane.
+ // Map those points to 2D.
+ // Create new faces from those points.
- face_count++;
+ Vector2 points_2D[3];
+ int points_count = 0;
+
+ for (int i = 0; i < 3; i++) {
+
+ Vector3 point_3D = p_brush.faces[p_face_idx].vertices[i];
+
+ if (plane.has_point(point_3D)) {
+ // Point is in the plane, add it.
+ Vector3 point_2D = plane.project(point_3D);
+ point_2D = to_2D.xform(point_2D);
+ points_2D[points_count++] = Vector2(point_2D.x, point_2D.y);
+
+ } else {
+
+ Vector3 next_point_3D = p_brush.faces[p_face_idx].vertices[(i + 1) % 3];
+
+ if (plane.has_point(next_point_3D))
+ continue; // Next point is in plane, it will be added separately.
+ if (plane.is_point_over(point_3D) == plane.is_point_over(next_point_3D))
+ continue; // Both points on the same side of the plane, ignore.
+
+ // Edge crosses the plane, find and add the intersection point.
+ Vector3 point_2D;
+ if (plane.intersects_segment(point_3D, next_point_3D, &point_2D)) {
+ point_2D = to_2D.xform(point_2D);
+ points_2D[points_count++] = Vector2(point_2D.x, point_2D.y);
}
+ }
+ }
- result.faces.resize(face_count);
+ Vector<int> segment_indices;
+ Vector2 segment[2];
+ int inserted_index[3] = { -1, -1, -1 };
- face_count = 0;
+ // Insert points.
+ for (int i = 0; i < points_count; ++i) {
+ inserted_index[i] = _insert_point(points_2D[i]);
+ }
- for (int i = 0; i < mesh_merge.faces.size(); i++) {
+ if (points_count == 2) {
+ // Insert a single segment.
+ segment[0] = points_2D[0];
+ segment[1] = points_2D[1];
+ _find_edge_intersections(segment, segment_indices);
+ for (int i = 0; i < 2; ++i) {
+ _add_vertex_idx_sorted(segment_indices, inserted_index[i]);
+ }
+ _merge_faces(segment_indices);
+ }
- if (mesh_merge.faces[i].from_b && !mesh_merge.faces[i].inside)
- continue;
- if (!mesh_merge.faces[i].from_b && mesh_merge.faces[i].inside)
- continue;
+ if (points_count == 3) {
+ // Insert three segments.
+ for (int edge_idx = 0; edge_idx < 3; ++edge_idx) {
+ segment[0] = points_2D[edge_idx];
+ segment[1] = points_2D[(edge_idx + 1) % 3];
+ _find_edge_intersections(segment, segment_indices);
+ for (int i = 0; i < 2; ++i) {
+ _add_vertex_idx_sorted(segment_indices, inserted_index[(edge_idx + i) % 3]);
+ }
+ _merge_faces(segment_indices);
+ segment_indices.clear();
+ }
+ }
+}
- for (int j = 0; j < 3; j++) {
- result.faces.write[face_count].vertices[j] = mesh_merge.points[mesh_merge.faces[i].points[j]];
- result.faces.write[face_count].uvs[j] = mesh_merge.faces[i].uvs[j];
- }
+void CSGBrushOperation::Build2DFaces::addFacesToMesh(MeshMerge &r_mesh_merge, bool p_smooth, bool p_invert, const Ref<Material> &p_material, bool p_from_b) {
- if (mesh_merge.faces[i].from_b) {
- //invert facing of insides of B
- SWAP(result.faces.write[face_count].vertices[1], result.faces.write[face_count].vertices[2]);
- SWAP(result.faces.write[face_count].uvs[1], result.faces.write[face_count].uvs[2]);
+ for (int face_idx = 0; face_idx < faces.size(); ++face_idx) {
+ Face2D face = faces[face_idx];
+ Vertex2D fv[3] = {
+ vertices[face.vertex_idx[0]],
+ vertices[face.vertex_idx[1]],
+ vertices[face.vertex_idx[2]]
+ };
+
+ // Convert 2D vertex points to 3D.
+ Vector3 points_3D[3];
+ Vector2 uvs[3];
+ for (int i = 0; i < 3; ++i) {
+ Vector3 point_2D(fv[i].point.x, fv[i].point.y, 0);
+ points_3D[i] = to_3D.xform(point_2D);
+ uvs[i] = fv[i].uv;
+ }
+
+ r_mesh_merge.add_face(points_3D, uvs, p_smooth, p_invert, p_material, p_from_b);
+ }
+}
+
+CSGBrushOperation::Build2DFaces::Build2DFaces(const CSGBrush &p_brush, int p_face_idx, float p_vertex_snap2) :
+ vertex_snap2(p_vertex_snap2 * p_vertex_snap2) {
+
+ // Convert 3D vertex points to 2D.
+ Vector3 points_3D[3] = {
+ p_brush.faces[p_face_idx].vertices[0],
+ p_brush.faces[p_face_idx].vertices[1],
+ p_brush.faces[p_face_idx].vertices[2],
+ };
+
+ plane = Plane(points_3D[0], points_3D[1], points_3D[2]);
+ to_3D.origin = points_3D[0];
+ to_3D.basis.set_axis(2, plane.normal);
+ to_3D.basis.set_axis(0, (points_3D[1] - points_3D[2]).normalized());
+ to_3D.basis.set_axis(1, to_3D.basis.get_axis(0).cross(to_3D.basis.get_axis(2)).normalized());
+ to_2D = to_3D.affine_inverse();
+
+ Face2D face;
+ for (int i = 0; i < 3; i++) {
+ Vertex2D vertex;
+ Vector3 point_2D = to_2D.xform(points_3D[i]);
+ vertex.point.x = point_2D.x;
+ vertex.point.y = point_2D.y;
+ vertex.uv = p_brush.faces[p_face_idx].uvs[i];
+ vertices.push_back(vertex);
+ face.vertex_idx[i] = i;
+ }
+ faces.push_back(face);
+}
+
+void CSGBrushOperation::update_faces(const CSGBrush &p_brush_a, const int p_face_idx_a, const CSGBrush &p_brush_b, const int p_face_idx_b, Build2DFaceCollection &p_collection, float p_vertex_snap) {
+
+ Vector3 vertices_a[3] = {
+ p_brush_a.faces[p_face_idx_a].vertices[0],
+ p_brush_a.faces[p_face_idx_a].vertices[1],
+ p_brush_a.faces[p_face_idx_a].vertices[2],
+ };
+
+ Vector3 vertices_b[3] = {
+ p_brush_b.faces[p_face_idx_b].vertices[0],
+ p_brush_b.faces[p_face_idx_b].vertices[1],
+ p_brush_b.faces[p_face_idx_b].vertices[2],
+ };
+
+ // Don't use degenerate faces.
+ bool has_degenerate = false;
+ if (is_snapable(vertices_a[0], vertices_a[1], p_vertex_snap) ||
+ is_snapable(vertices_a[0], vertices_a[2], p_vertex_snap) ||
+ is_snapable(vertices_a[1], vertices_a[2], p_vertex_snap)) {
+ p_collection.build2DFacesA[p_face_idx_a] = Build2DFaces();
+ has_degenerate = true;
+ }
+
+ if (is_snapable(vertices_b[0], vertices_b[1], p_vertex_snap) ||
+ is_snapable(vertices_b[0], vertices_b[2], p_vertex_snap) ||
+ is_snapable(vertices_b[1], vertices_b[2], p_vertex_snap)) {
+ p_collection.build2DFacesB[p_face_idx_b] = Build2DFaces();
+ has_degenerate = true;
+ }
+ if (has_degenerate) return;
+
+ // Ensure B has points either side of or in the plane of A.
+ int in_plane_count = 0, over_count = 0, under_count = 0;
+ Plane plane_a(vertices_a[0], vertices_a[1], vertices_a[2]);
+ ERR_FAIL_COND_MSG(plane_a.normal == Vector3(), "Couldn't form plane from Brush A face.");
+
+ for (int i = 0; i < 3; i++) {
+ if (plane_a.has_point(vertices_b[i]))
+ in_plane_count++;
+ else if (plane_a.is_point_over(vertices_b[i]))
+ over_count++;
+ else
+ under_count++;
+ }
+ // If all points under or over the plane, there is no intesection.
+ if (over_count == 3 || under_count == 3) return;
+
+ // Ensure A has points either side of or in the plane of B.
+ in_plane_count = 0;
+ over_count = 0;
+ under_count = 0;
+ Plane plane_b(vertices_b[0], vertices_b[1], vertices_b[2]);
+ ERR_FAIL_COND_MSG(plane_b.normal == Vector3(), "Couldn't form plane from Brush B face.");
+
+ for (int i = 0; i < 3; i++) {
+ if (plane_b.has_point(vertices_a[i]))
+ in_plane_count++;
+ else if (plane_b.is_point_over(vertices_a[i]))
+ over_count++;
+ else
+ under_count++;
+ }
+ // If all points under or over the plane, there is no intesection.
+ if (over_count == 3 || under_count == 3) return;
+
+ // Check for intersection using the SAT theorem.
+ {
+
+ // Edge pair cross product combinations.
+ for (int i = 0; i < 3; i++) {
+
+ Vector3 axis_a = (vertices_a[i] - vertices_a[(i + 1) % 3]).normalized();
+
+ for (int j = 0; j < 3; j++) {
+
+ Vector3 axis_b = (vertices_b[j] - vertices_b[(j + 1) % 3]).normalized();
+
+ Vector3 sep_axis = axis_a.cross(axis_b);
+ if (sep_axis == Vector3())
+ continue; //colineal
+ sep_axis.normalize();
+
+ real_t min_a = 1e20, max_a = -1e20;
+ real_t min_b = 1e20, max_b = -1e20;
+
+ for (int k = 0; k < 3; k++) {
+ real_t d = sep_axis.dot(vertices_a[k]);
+ min_a = MIN(min_a, d);
+ max_a = MAX(max_a, d);
+ d = sep_axis.dot(vertices_b[k]);
+ min_b = MIN(min_b, d);
+ max_b = MAX(max_b, d);
}
- result.faces.write[face_count].smooth = mesh_merge.faces[i].smooth;
- result.faces.write[face_count].invert = mesh_merge.faces[i].invert;
- result.faces.write[face_count].material = mesh_merge.faces[i].material_idx;
- face_count++;
- }
+ min_b -= (max_a - min_a) * 0.5;
+ max_b += (max_a - min_a) * 0.5;
- result._regen_face_aabbs();
+ real_t dmin = min_b - (min_a + max_a) * 0.5;
+ real_t dmax = max_b - (min_a + max_a) * 0.5;
- } break;
+ if (dmin > CMP_EPSILON || dmax < -CMP_EPSILON) {
+ return; // Does not contain zero, so they don't overlap.
+ }
+ }
+ }
}
- //updatelist of materials
- result.materials.resize(mesh_merge.materials.size());
- for (const Map<Ref<Material>, int>::Element *E = mesh_merge.materials.front(); E; E = E->next()) {
- result.materials.write[E->get()] = E->key();
+ // If we're still here, the faces probably intersect, so add new faces.
+ if (!p_collection.build2DFacesA.has(p_face_idx_a)) {
+ p_collection.build2DFacesA[p_face_idx_a] = Build2DFaces(p_brush_a, p_face_idx_a, p_vertex_snap);
+ }
+ p_collection.build2DFacesA[p_face_idx_a].insert(p_brush_b, p_face_idx_b);
+
+ if (!p_collection.build2DFacesB.has(p_face_idx_b)) {
+ p_collection.build2DFacesB[p_face_idx_b] = Build2DFaces(p_brush_b, p_face_idx_b, p_vertex_snap);
}
+ p_collection.build2DFacesB[p_face_idx_b].insert(p_brush_a, p_face_idx_a);
}
diff --git a/modules/csg/csg.h b/modules/csg/csg.h
index 472a96d5df..bb83c84cb5 100644
--- a/modules/csg/csg.h
+++ b/modules/csg/csg.h
@@ -31,20 +31,21 @@
#ifndef CSG_H
#define CSG_H
+#include "core/list.h"
#include "core/map.h"
#include "core/math/aabb.h"
#include "core/math/plane.h"
-#include "core/math/rect2.h"
#include "core/math/transform.h"
+#include "core/math/vector2.h"
#include "core/math/vector3.h"
#include "core/oa_hash_map.h"
-
+#include "core/reference.h"
+#include "core/vector.h"
#include "scene/resources/material.h"
struct CSGBrush {
struct Face {
-
Vector3 vertices[3];
Vector2 uvs[3];
AABB aabb;
@@ -56,12 +57,11 @@ struct CSGBrush {
Vector<Face> faces;
Vector<Ref<Material> > materials;
- void _regen_face_aabbs();
- //create a brush from faces
+ inline void _regen_face_aabbs();
+
+ // Create a brush from faces.
void build_from_faces(const Vector<Vector3> &p_vertices, const Vector<Vector2> &p_uvs, const Vector<bool> &p_smooth, const Vector<Ref<Material> > &p_materials, const Vector<bool> &p_invert_faces);
void copy_from(const CSGBrush &p_brush, const Transform &p_xform);
-
- void clear();
};
struct CSGBrushOperation {
@@ -70,12 +70,23 @@ struct CSGBrushOperation {
OPERATION_UNION,
OPERATION_INTERSECTION,
OPERATION_SUBSTRACTION,
-
};
+ void merge_brushes(Operation p_operation, const CSGBrush &p_brush_a, const CSGBrush &p_brush_b, CSGBrush &r_merged_brush, float p_vertex_snap);
+
struct MeshMerge {
- struct BVH {
+ struct Face {
+ bool from_b;
+ bool inside;
+ int points[3];
+ Vector2 uvs[3];
+ bool smooth;
+ bool invert;
+ int material_idx;
+ };
+
+ struct FaceBVH {
int face;
int left;
int right;
@@ -84,32 +95,23 @@ struct CSGBrushOperation {
AABB aabb;
};
- struct BVHCmpX {
-
- bool operator()(const BVH *p_left, const BVH *p_right) const {
-
+ struct FaceBVHCmpX {
+ _FORCE_INLINE_ bool operator()(const FaceBVH *p_left, const FaceBVH *p_right) const {
return p_left->center.x < p_right->center.x;
}
};
- struct BVHCmpY {
-
- bool operator()(const BVH *p_left, const BVH *p_right) const {
-
+ struct FaceBVHCmpY {
+ _FORCE_INLINE_ bool operator()(const FaceBVH *p_left, const FaceBVH *p_right) const {
return p_left->center.y < p_right->center.y;
}
};
- struct BVHCmpZ {
-
- bool operator()(const BVH *p_left, const BVH *p_right) const {
-
+ struct FaceBVHCmpZ {
+ _FORCE_INLINE_ bool operator()(const FaceBVH *p_left, const FaceBVH *p_right) const {
return p_left->center.z < p_right->center.z;
}
};
- int _bvh_count_intersections(BVH *bvhptr, int p_max_depth, int p_bvh_first, const Vector3 &p_begin, const Vector3 &p_end, int p_exclude) const;
- int _create_bvh(BVH *p_bvh, BVH **p_bb, int p_from, int p_size, int p_depth, int &max_depth, int &max_alloc);
-
struct VertexKey {
int32_t x, y, z;
_FORCE_INLINE_ bool operator<(const VertexKey &p_key) const {
@@ -138,99 +140,59 @@ struct CSGBrushOperation {
}
};
- OAHashMap<VertexKey, int, VertexKeyHash> snap_cache;
-
Vector<Vector3> points;
-
- struct Face {
- bool from_b;
- bool inside;
- int points[3];
- Vector2 uvs[3];
- bool smooth;
- bool invert;
- int material_idx;
- };
-
Vector<Face> faces;
-
Map<Ref<Material>, int> materials;
-
Map<Vector3, int> vertex_map;
- void add_face(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, const Vector2 &p_uv_a, const Vector2 &p_uv_b, const Vector2 &p_uv_c, bool p_smooth, bool p_invert, const Ref<Material> &p_material, bool p_from_b);
- // void add_face(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, bool p_from_b);
-
+ OAHashMap<VertexKey, int, VertexKeyHash> snap_cache;
float vertex_snap;
+
+ inline void _add_distance(List<real_t> &r_intersectionsA, List<real_t> &r_intersectionsB, bool p_from_B, real_t p_distance) const;
+ inline bool _bvh_inside(FaceBVH *facebvhptr, int p_max_depth, int p_bvh_first, int p_face_idx) const;
+ inline int _create_bvh(FaceBVH *facebvhptr, FaceBVH **facebvhptrptr, int p_from, int p_size, int p_depth, int &r_max_depth, int &r_max_alloc);
+
+ void add_face(const Vector3 p_points[3], const Vector2 p_uvs[3], bool p_smooth, bool p_invert, const Ref<Material> &p_material, bool p_from_b);
void mark_inside_faces();
};
- struct BuildPoly {
+ struct Build2DFaces {
- Plane plane;
- Transform to_poly;
- Transform to_world;
- int face_index;
-
- struct Point {
+ struct Vertex2D {
Vector2 point;
Vector2 uv;
};
- Vector<Point> points;
-
- struct Edge {
- bool outer;
- int points[2];
- Edge() {
- outer = false;
- }
+ struct Face2D {
+ int vertex_idx[3];
};
- Vector<Edge> edges;
- Ref<Material> material;
- bool smooth;
- bool invert;
-
- int base_edges; //edges from original triangle, even if split
-
- void _clip_segment(const CSGBrush *p_brush, int p_face, const Vector2 *segment, MeshMerge &mesh_merge, bool p_for_B);
-
- void create(const CSGBrush *p_brush, int p_face, MeshMerge &mesh_merge, bool p_for_B);
- void clip(const CSGBrush *p_brush, int p_face, MeshMerge &mesh_merge, bool p_for_B);
- };
-
- struct PolyPoints {
-
- Vector<int> points;
-
- Vector<Vector<int> > holes;
- };
-
- struct EdgeSort {
- int edge;
- int prev_point;
- int edge_point;
- float angle;
- bool operator<(const EdgeSort &p_edge) const { return angle < p_edge.angle; }
+ Vector<Vertex2D> vertices;
+ Vector<Face2D> faces;
+ Plane plane;
+ Transform to_2D;
+ Transform to_3D;
+ float vertex_snap2;
+
+ inline int _get_point_idx(const Vector2 &p_point);
+ inline int _add_vertex(const Vertex2D &p_vertex);
+ inline void _add_vertex_idx_sorted(Vector<int> &r_vertex_indices, int p_new_vertex_index);
+ inline void _merge_faces(const Vector<int> &p_segment_indices);
+ inline void _find_edge_intersections(const Vector2 p_segment_points[2], Vector<int> &r_segment_indices);
+ inline int _insert_point(const Vector2 &p_point);
+
+ void insert(const CSGBrush &p_brush, int p_brush_face);
+ void addFacesToMesh(MeshMerge &r_mesh_merge, bool p_smooth, bool p_invert, const Ref<Material> &p_material, bool p_from_b);
+
+ Build2DFaces() {}
+ Build2DFaces(const CSGBrush &p_brush, int p_brush_face, float p_vertex_snap2);
};
- struct CallbackData {
- const CSGBrush *A;
- const CSGBrush *B;
- int face_a;
- CSGBrushOperation *self;
- Map<int, BuildPoly> build_polys_A;
- Map<int, BuildPoly> build_polys_B;
+ struct Build2DFaceCollection {
+ Map<int, Build2DFaces> build2DFacesA;
+ Map<int, Build2DFaces> build2DFacesB;
};
- void _add_poly_points(const BuildPoly &p_poly, int p_edge, int p_from_point, int p_to_point, const Vector<Vector<int> > &vertex_process, Vector<bool> &edge_process, Vector<PolyPoints> &r_poly);
- void _add_poly_outline(const BuildPoly &p_poly, int p_from_point, int p_to_point, const Vector<Vector<int> > &vertex_process, Vector<int> &r_outline);
- void _merge_poly(MeshMerge &mesh, int p_face_idx, const BuildPoly &p_poly, bool p_from_b);
-
- void _collision_callback(const CSGBrush *A, int p_face_a, Map<int, BuildPoly> &build_polys_a, const CSGBrush *B, int p_face_b, Map<int, BuildPoly> &build_polys_b, MeshMerge &mesh_merge);
-
- static void _collision_callbacks(void *ud, int p_face_b);
- void merge_brushes(Operation p_operation, const CSGBrush &p_A, const CSGBrush &p_B, CSGBrush &result, float p_snap = 0.001);
+ void update_faces(const CSGBrush &p_brush_a, const int p_face_idx_a, const CSGBrush &p_brush_b, const int p_face_idx_b, Build2DFaceCollection &p_collection, float p_vertex_snap);
};
#endif // CSG_H
diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp
index d017afd792..a227d49892 100644
--- a/modules/csg/csg_shape.cpp
+++ b/modules/csg/csg_shape.cpp
@@ -884,8 +884,6 @@ void CSGMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &CSGMesh::set_mesh);
ClassDB::bind_method(D_METHOD("get_mesh"), &CSGMesh::get_mesh);
- ClassDB::bind_method(D_METHOD("_mesh_changed"), &CSGMesh::_mesh_changed);
-
ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGMesh::set_material);
ClassDB::bind_method(D_METHOD("get_material"), &CSGMesh::get_material);
@@ -898,12 +896,12 @@ void CSGMesh::set_mesh(const Ref<Mesh> &p_mesh) {
if (mesh == p_mesh)
return;
if (mesh.is_valid()) {
- mesh->disconnect_compat("changed", this, "_mesh_changed");
+ mesh->disconnect("changed", callable_mp(this, &CSGMesh::_mesh_changed));
}
mesh = p_mesh;
if (mesh.is_valid()) {
- mesh->connect_compat("changed", this, "_mesh_changed");
+ mesh->connect("changed", callable_mp(this, &CSGMesh::_mesh_changed));
}
_make_dirty();
@@ -1812,15 +1810,15 @@ CSGBrush *CSGPolygon::_build_brush() {
if (path != path_cache) {
if (path_cache) {
- path_cache->disconnect_compat("tree_exited", this, "_path_exited");
- path_cache->disconnect_compat("curve_changed", this, "_path_changed");
+ path_cache->disconnect("tree_exited", callable_mp(this, &CSGPolygon::_path_exited));
+ path_cache->disconnect("curve_changed", callable_mp(this, &CSGPolygon::_path_changed));
path_cache = NULL;
}
path_cache = path;
- path_cache->connect_compat("tree_exited", this, "_path_exited");
- path_cache->connect_compat("curve_changed", this, "_path_changed");
+ path_cache->connect("tree_exited", callable_mp(this, &CSGPolygon::_path_exited));
+ path_cache->connect("curve_changed", callable_mp(this, &CSGPolygon::_path_changed));
path_cache = NULL;
}
curve = path->get_curve();
@@ -2236,8 +2234,8 @@ CSGBrush *CSGPolygon::_build_brush() {
void CSGPolygon::_notification(int p_what) {
if (p_what == NOTIFICATION_EXIT_TREE) {
if (path_cache) {
- path_cache->disconnect_compat("tree_exited", this, "_path_exited");
- path_cache->disconnect_compat("curve_changed", this, "_path_changed");
+ path_cache->disconnect("tree_exited", callable_mp(this, &CSGPolygon::_path_exited));
+ path_cache->disconnect("curve_changed", callable_mp(this, &CSGPolygon::_path_changed));
path_cache = NULL;
}
}
@@ -2309,9 +2307,6 @@ void CSGPolygon::_bind_methods() {
ClassDB::bind_method(D_METHOD("_is_editable_3d_polygon"), &CSGPolygon::_is_editable_3d_polygon);
ClassDB::bind_method(D_METHOD("_has_editable_3d_polygon_no_depth"), &CSGPolygon::_has_editable_3d_polygon_no_depth);
- ClassDB::bind_method(D_METHOD("_path_exited"), &CSGPolygon::_path_exited);
- ClassDB::bind_method(D_METHOD("_path_changed"), &CSGPolygon::_path_changed);
-
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon");
ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Depth,Spin,Path"), "set_mode", "get_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_depth", "get_depth");
diff --git a/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp
index a04989449c..ae21156b8b 100644
--- a/modules/dds/texture_loader_dds.cpp
+++ b/modules/dds/texture_loader_dds.cpp
@@ -94,7 +94,7 @@ static const DDSFormatInfo dds_format_info[DDS_MAX] = {
{ "GRAYSCALE_ALPHA", false, false, 1, 2, Image::FORMAT_LA8 }
};
-RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path, Error *r_error) {
+RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress) {
if (r_error)
*r_error = ERR_CANT_OPEN;
diff --git a/modules/dds/texture_loader_dds.h b/modules/dds/texture_loader_dds.h
index 34f4e9b548..de8088af90 100644
--- a/modules/dds/texture_loader_dds.h
+++ b/modules/dds/texture_loader_dds.h
@@ -36,7 +36,7 @@
class ResourceFormatDDS : public ResourceFormatLoader {
public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL);
+ virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL, bool p_use_sub_threads = false, float *r_progress = nullptr);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
diff --git a/modules/etc/image_etc.cpp b/modules/etc/image_etc.cpp
index 0dbd5ca905..b3f7b1d94f 100644
--- a/modules/etc/image_etc.cpp
+++ b/modules/etc/image_etc.cpp
@@ -106,9 +106,15 @@ static void _compress_etc(Image *p_img, float p_lossy_quality, bool force_etc1_f
// If VRAM compression is using ETC, but image has alpha, convert to RGBA4444 or LA8
// This saves space while maintaining the alpha channel
if (detected_channels == Image::USED_CHANNELS_RGBA) {
+
+ if (p_img->has_mipmaps()) {
+ // Image doesn't support mipmaps with RGBA4444 textures
+ p_img->clear_mipmaps();
+ }
p_img->convert(Image::FORMAT_RGBA4444);
return;
} else if (detected_channels == Image::USE_CHANNELS_LA) {
+
p_img->convert(Image::FORMAT_LA8);
return;
}
diff --git a/modules/etc/texture_loader_pkm.cpp b/modules/etc/texture_loader_pkm.cpp
index e460c70cec..ad0cc91c96 100644
--- a/modules/etc/texture_loader_pkm.cpp
+++ b/modules/etc/texture_loader_pkm.cpp
@@ -42,7 +42,7 @@ struct ETC1Header {
uint16_t origHeight;
};
-RES ResourceFormatPKM::load(const String &p_path, const String &p_original_path, Error *r_error) {
+RES ResourceFormatPKM::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress) {
if (r_error)
*r_error = ERR_CANT_OPEN;
diff --git a/modules/etc/texture_loader_pkm.h b/modules/etc/texture_loader_pkm.h
index 3d52a9ea95..d6011993e3 100644
--- a/modules/etc/texture_loader_pkm.h
+++ b/modules/etc/texture_loader_pkm.h
@@ -36,7 +36,7 @@
class ResourceFormatPKM : public ResourceFormatLoader {
public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL);
+ virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL, bool p_use_sub_threads = false, float *r_progress = nullptr);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp
index 1571b821a5..33b734f672 100644
--- a/modules/gdnative/gdnative.cpp
+++ b/modules/gdnative/gdnative.cpp
@@ -493,7 +493,7 @@ Error GDNative::get_symbol(StringName p_procedure_name, void *&r_handle, bool p_
return result;
}
-RES GDNativeLibraryResourceLoader::load(const String &p_path, const String &p_original_path, Error *r_error) {
+RES GDNativeLibraryResourceLoader::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress) {
Ref<GDNativeLibrary> lib;
lib.instance();
diff --git a/modules/gdnative/gdnative.h b/modules/gdnative/gdnative.h
index c0c2cdf24c..b4c5ec9d00 100644
--- a/modules/gdnative/gdnative.h
+++ b/modules/gdnative/gdnative.h
@@ -166,7 +166,7 @@ public:
class GDNativeLibraryResourceLoader : public ResourceFormatLoader {
public:
- virtual RES load(const String &p_path, const String &p_original_path, Error *r_error);
+ virtual RES load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads = false, float *r_progress = nullptr);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
diff --git a/modules/gdnative/gdnative_library_editor_plugin.cpp b/modules/gdnative/gdnative_library_editor_plugin.cpp
index b434bc8385..db90199e63 100644
--- a/modules/gdnative/gdnative_library_editor_plugin.cpp
+++ b/modules/gdnative/gdnative_library_editor_plugin.cpp
@@ -53,14 +53,6 @@ void GDNativeLibraryEditor::edit(Ref<GDNativeLibrary> p_library) {
}
void GDNativeLibraryEditor::_bind_methods() {
-
- ClassDB::bind_method("_on_item_button", &GDNativeLibraryEditor::_on_item_button);
- ClassDB::bind_method("_on_library_selected", &GDNativeLibraryEditor::_on_library_selected);
- ClassDB::bind_method("_on_dependencies_selected", &GDNativeLibraryEditor::_on_dependencies_selected);
- ClassDB::bind_method("_on_filter_selected", &GDNativeLibraryEditor::_on_filter_selected);
- ClassDB::bind_method("_on_item_collapsed", &GDNativeLibraryEditor::_on_item_collapsed);
- ClassDB::bind_method("_on_item_activated", &GDNativeLibraryEditor::_on_item_activated);
- ClassDB::bind_method("_on_create_new_entry", &GDNativeLibraryEditor::_on_create_new_entry);
}
void GDNativeLibraryEditor::_update_tree() {
@@ -359,7 +351,7 @@ GDNativeLibraryEditor::GDNativeLibraryEditor() {
filter_list->set_item_checked(idx, true);
idx += 1;
}
- filter_list->connect_compat("index_pressed", this, "_on_filter_selected");
+ filter_list->connect("index_pressed", callable_mp(this, &GDNativeLibraryEditor::_on_filter_selected));
tree = memnew(Tree);
container->add_child(tree);
@@ -374,16 +366,16 @@ GDNativeLibraryEditor::GDNativeLibraryEditor() {
tree->set_column_title(2, TTR("Dependencies"));
tree->set_column_expand(3, false);
tree->set_column_min_width(3, int(110 * EDSCALE));
- tree->connect_compat("button_pressed", this, "_on_item_button");
- tree->connect_compat("item_collapsed", this, "_on_item_collapsed");
- tree->connect_compat("item_activated", this, "_on_item_activated");
+ tree->connect("button_pressed", callable_mp(this, &GDNativeLibraryEditor::_on_item_button));
+ tree->connect("item_collapsed", callable_mp(this, &GDNativeLibraryEditor::_on_item_collapsed));
+ tree->connect("item_activated", callable_mp(this, &GDNativeLibraryEditor::_on_item_activated));
file_dialog = memnew(EditorFileDialog);
file_dialog->set_access(EditorFileDialog::ACCESS_RESOURCES);
file_dialog->set_resizable(true);
add_child(file_dialog);
- file_dialog->connect_compat("file_selected", this, "_on_library_selected");
- file_dialog->connect_compat("files_selected", this, "_on_dependencies_selected");
+ file_dialog->connect("file_selected", callable_mp(this, &GDNativeLibraryEditor::_on_library_selected));
+ file_dialog->connect("files_selected", callable_mp(this, &GDNativeLibraryEditor::_on_dependencies_selected));
new_architecture_dialog = memnew(ConfirmationDialog);
add_child(new_architecture_dialog);
@@ -392,7 +384,7 @@ GDNativeLibraryEditor::GDNativeLibraryEditor() {
new_architecture_dialog->add_child(new_architecture_input);
new_architecture_dialog->set_custom_minimum_size(Vector2(300, 80) * EDSCALE);
new_architecture_input->set_anchors_and_margins_preset(PRESET_HCENTER_WIDE, PRESET_MODE_MINSIZE, 5 * EDSCALE);
- new_architecture_dialog->get_ok()->connect_compat("pressed", this, "_on_create_new_entry");
+ new_architecture_dialog->get_ok()->connect("pressed", callable_mp(this, &GDNativeLibraryEditor::_on_create_new_entry));
}
void GDNativeLibraryEditorPlugin::edit(Object *p_node) {
diff --git a/modules/gdnative/gdnative_library_singleton_editor.cpp b/modules/gdnative/gdnative_library_singleton_editor.cpp
index e5eb3c44d0..378339ecea 100644
--- a/modules/gdnative/gdnative_library_singleton_editor.cpp
+++ b/modules/gdnative/gdnative_library_singleton_editor.cpp
@@ -192,8 +192,6 @@ void GDNativeLibrarySingletonEditor::_notification(int p_what) {
void GDNativeLibrarySingletonEditor::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_item_edited"), &GDNativeLibrarySingletonEditor::_item_edited);
- ClassDB::bind_method(D_METHOD("_discover_singletons"), &GDNativeLibrarySingletonEditor::_discover_singletons);
ClassDB::bind_method(D_METHOD("_update_libraries"), &GDNativeLibrarySingletonEditor::_update_libraries);
}
@@ -207,8 +205,8 @@ GDNativeLibrarySingletonEditor::GDNativeLibrarySingletonEditor() {
libraries->set_hide_root(true);
add_margin_child(TTR("Libraries: "), libraries, true);
updating = false;
- libraries->connect_compat("item_edited", this, "_item_edited");
- EditorFileSystem::get_singleton()->connect_compat("filesystem_changed", this, "_discover_singletons");
+ libraries->connect("item_edited", callable_mp(this, &GDNativeLibrarySingletonEditor::_item_edited));
+ EditorFileSystem::get_singleton()->connect("filesystem_changed", callable_mp(this, &GDNativeLibrarySingletonEditor::_discover_singletons));
}
#endif // TOOLS_ENABLED
diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp
index 0ec6ad71d9..d0e196b3e6 100644
--- a/modules/gdnative/nativescript/nativescript.cpp
+++ b/modules/gdnative/nativescript/nativescript.cpp
@@ -1931,7 +1931,7 @@ void NativeReloadNode::_notification(int p_what) {
#endif
}
-RES ResourceFormatLoaderNativeScript::load(const String &p_path, const String &p_original_path, Error *r_error) {
+RES ResourceFormatLoaderNativeScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress) {
return ResourceFormatLoaderText::singleton->load(p_path, p_original_path, r_error);
}
diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h
index 04670b952b..90542c96b7 100644
--- a/modules/gdnative/nativescript/nativescript.h
+++ b/modules/gdnative/nativescript/nativescript.h
@@ -406,7 +406,7 @@ public:
class ResourceFormatLoaderNativeScript : public ResourceFormatLoader {
public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL);
+ virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL, bool p_use_sub_threads = false, float *r_progress = nullptr);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
diff --git a/modules/gdnative/pluginscript/pluginscript_loader.cpp b/modules/gdnative/pluginscript/pluginscript_loader.cpp
index fe1cc83c69..46db20b6c2 100644
--- a/modules/gdnative/pluginscript/pluginscript_loader.cpp
+++ b/modules/gdnative/pluginscript/pluginscript_loader.cpp
@@ -39,7 +39,7 @@ ResourceFormatLoaderPluginScript::ResourceFormatLoaderPluginScript(PluginScriptL
_language = language;
}
-RES ResourceFormatLoaderPluginScript::load(const String &p_path, const String &p_original_path, Error *r_error) {
+RES ResourceFormatLoaderPluginScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress) {
if (r_error)
*r_error = ERR_FILE_CANT_OPEN;
diff --git a/modules/gdnative/pluginscript/pluginscript_loader.h b/modules/gdnative/pluginscript/pluginscript_loader.h
index ede31c027e..a039072fdc 100644
--- a/modules/gdnative/pluginscript/pluginscript_loader.h
+++ b/modules/gdnative/pluginscript/pluginscript_loader.h
@@ -44,7 +44,7 @@ class ResourceFormatLoaderPluginScript : public ResourceFormatLoader {
public:
ResourceFormatLoaderPluginScript(PluginScriptLanguage *language);
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL);
+ virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL, bool p_use_sub_threads = false, float *r_progress = nullptr);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
diff --git a/modules/gdnative/videodecoder/video_stream_gdnative.cpp b/modules/gdnative/videodecoder/video_stream_gdnative.cpp
index bd16563ff0..f6e2bad739 100644
--- a/modules/gdnative/videodecoder/video_stream_gdnative.cpp
+++ b/modules/gdnative/videodecoder/video_stream_gdnative.cpp
@@ -373,7 +373,7 @@ void VideoStreamGDNative::set_audio_track(int p_track) {
/* --- NOTE ResourceFormatLoaderVideoStreamGDNative starts here. ----- */
-RES ResourceFormatLoaderVideoStreamGDNative::load(const String &p_path, const String &p_original_path, Error *r_error) {
+RES ResourceFormatLoaderVideoStreamGDNative::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress) {
FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
if (!f) {
if (r_error) {
diff --git a/modules/gdnative/videodecoder/video_stream_gdnative.h b/modules/gdnative/videodecoder/video_stream_gdnative.h
index 024cdec196..21b5245a16 100644
--- a/modules/gdnative/videodecoder/video_stream_gdnative.h
+++ b/modules/gdnative/videodecoder/video_stream_gdnative.h
@@ -199,7 +199,7 @@ public:
class ResourceFormatLoaderVideoStreamGDNative : public ResourceFormatLoader {
public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL);
+ virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL, bool p_use_sub_threads = false, float *r_progress = nullptr);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
diff --git a/modules/gdnavigation/navigation_mesh_editor_plugin.cpp b/modules/gdnavigation/navigation_mesh_editor_plugin.cpp
index 4ab90ac7eb..5c298ae9e4 100644
--- a/modules/gdnavigation/navigation_mesh_editor_plugin.cpp
+++ b/modules/gdnavigation/navigation_mesh_editor_plugin.cpp
@@ -94,9 +94,6 @@ void NavigationMeshEditor::edit(NavigationRegion *p_nav_mesh_instance) {
}
void NavigationMeshEditor::_bind_methods() {
-
- ClassDB::bind_method("_bake_pressed", &NavigationMeshEditor::_bake_pressed);
- ClassDB::bind_method("_clear_pressed", &NavigationMeshEditor::_clear_pressed);
}
NavigationMeshEditor::NavigationMeshEditor() {
@@ -107,13 +104,13 @@ NavigationMeshEditor::NavigationMeshEditor() {
bake_hbox->add_child(button_bake);
button_bake->set_toggle_mode(true);
button_bake->set_text(TTR("Bake NavMesh"));
- button_bake->connect_compat("pressed", this, "_bake_pressed");
+ button_bake->connect("pressed", callable_mp(this, &NavigationMeshEditor::_bake_pressed));
button_reset = memnew(ToolButton);
bake_hbox->add_child(button_reset);
// No button text, we only use a revert icon which is set when entering the tree.
button_reset->set_tooltip(TTR("Clear the navigation mesh."));
- button_reset->connect_compat("pressed", this, "_clear_pressed");
+ button_reset->connect("pressed", callable_mp(this, &NavigationMeshEditor::_clear_pressed));
bake_info = memnew(Label);
bake_hbox->add_child(bake_info);
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 6a4bf801f3..c641ce37c5 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -2251,7 +2251,7 @@ Ref<GDScript> GDScriptLanguage::get_orphan_subclass(const String &p_qualified_na
/*************** RESOURCE ***************/
-RES ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_original_path, Error *r_error) {
+RES ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress) {
if (r_error)
*r_error = ERR_FILE_CANT_OPEN;
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 6b53acd064..3a90f0fc20 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -542,7 +542,7 @@ public:
class ResourceFormatLoaderGDScript : public ResourceFormatLoader {
public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL);
+ virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL, bool p_use_sub_threads = false, float *r_progress = nullptr);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp
index 761771a02c..a656ee8b63 100644
--- a/modules/gridmap/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/grid_map_editor_plugin.cpp
@@ -752,7 +752,7 @@ bool GridMapEditor::forward_spatial_input_event(Camera *p_camera, const Ref<Inpu
if (k.is_valid()) {
if (k->is_pressed()) {
- if (k->get_scancode() == KEY_ESCAPE) {
+ if (k->get_keycode() == KEY_ESCAPE) {
if (input_action == INPUT_PASTE) {
_clear_clipboard_data();
@@ -773,12 +773,12 @@ bool GridMapEditor::forward_spatial_input_event(Camera *p_camera, const Ref<Inpu
if (k->get_shift() && selection.active && input_action != INPUT_PASTE) {
- if (k->get_scancode() == options->get_popup()->get_item_accelerator(options->get_popup()->get_item_index(MENU_OPTION_PREV_LEVEL))) {
+ if (k->get_keycode() == options->get_popup()->get_item_accelerator(options->get_popup()->get_item_index(MENU_OPTION_PREV_LEVEL))) {
selection.click[edit_axis]--;
_validate_selection();
return true;
}
- if (k->get_scancode() == options->get_popup()->get_item_accelerator(options->get_popup()->get_item_index(MENU_OPTION_NEXT_LEVEL))) {
+ if (k->get_keycode() == options->get_popup()->get_item_accelerator(options->get_popup()->get_item_index(MENU_OPTION_NEXT_LEVEL))) {
selection.click[edit_axis]++;
_validate_selection();
return true;
@@ -842,7 +842,7 @@ void GridMapEditor::_sbox_input(const Ref<InputEvent> &p_ie) {
const Ref<InputEventKey> k = p_ie;
- if (k.is_valid() && (k->get_scancode() == KEY_UP || k->get_scancode() == KEY_DOWN || k->get_scancode() == KEY_PAGEUP || k->get_scancode() == KEY_PAGEDOWN)) {
+ if (k.is_valid() && (k->get_keycode() == KEY_UP || k->get_keycode() == KEY_DOWN || k->get_keycode() == KEY_PAGEUP || k->get_keycode() == KEY_PAGEDOWN)) {
// Forward the key input to the ItemList so it can be scrolled
mesh_library_palette->call("_gui_input", k);
@@ -954,7 +954,7 @@ void GridMapEditor::update_palette() {
void GridMapEditor::edit(GridMap *p_gridmap) {
if (!p_gridmap && node)
- node->disconnect_compat("cell_size_changed", this, "_draw_grids");
+ node->disconnect("cell_size_changed", callable_mp(this, &GridMapEditor::_draw_grids));
node = p_gridmap;
@@ -988,7 +988,7 @@ void GridMapEditor::edit(GridMap *p_gridmap) {
update_grid();
_update_clip();
- node->connect_compat("cell_size_changed", this, "_draw_grids");
+ node->connect("cell_size_changed", callable_mp(this, &GridMapEditor::_draw_grids));
}
void GridMapEditor::_update_clip() {
@@ -1077,8 +1077,8 @@ void GridMapEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
- get_tree()->connect_compat("node_removed", this, "_node_removed");
- mesh_library_palette->connect_compat("item_selected", this, "_item_selected_cbk");
+ get_tree()->connect("node_removed", callable_mp(this, &GridMapEditor::_node_removed));
+ mesh_library_palette->connect("item_selected", callable_mp(this, &GridMapEditor::_item_selected_cbk));
for (int i = 0; i < 3; i++) {
grid[i] = VS::get_singleton()->mesh_create();
@@ -1094,7 +1094,7 @@ void GridMapEditor::_notification(int p_what) {
} break;
case NOTIFICATION_EXIT_TREE: {
- get_tree()->disconnect_compat("node_removed", this, "_node_removed");
+ get_tree()->disconnect("node_removed", callable_mp(this, &GridMapEditor::_node_removed));
_clear_clipboard_data();
for (int i = 0; i < 3; i++) {
@@ -1198,20 +1198,8 @@ void GridMapEditor::_floor_mouse_exited() {
void GridMapEditor::_bind_methods() {
- ClassDB::bind_method("_text_changed", &GridMapEditor::_text_changed);
- ClassDB::bind_method("_sbox_input", &GridMapEditor::_sbox_input);
- ClassDB::bind_method("_mesh_library_palette_input", &GridMapEditor::_mesh_library_palette_input);
- ClassDB::bind_method("_icon_size_changed", &GridMapEditor::_icon_size_changed);
- ClassDB::bind_method("_menu_option", &GridMapEditor::_menu_option);
ClassDB::bind_method("_configure", &GridMapEditor::_configure);
- ClassDB::bind_method("_item_selected_cbk", &GridMapEditor::_item_selected_cbk);
- ClassDB::bind_method("_floor_changed", &GridMapEditor::_floor_changed);
- ClassDB::bind_method("_floor_mouse_exited", &GridMapEditor::_floor_mouse_exited);
ClassDB::bind_method("_set_selection", &GridMapEditor::_set_selection);
- ClassDB::bind_method("_node_removed", &GridMapEditor::_node_removed);
-
- ClassDB::bind_method(D_METHOD("_set_display_mode", "mode"), &GridMapEditor::_set_display_mode);
- ClassDB::bind_method("_draw_grids", &GridMapEditor::_draw_grids);
}
GridMapEditor::GridMapEditor(EditorNode *p_editor) {
@@ -1241,9 +1229,9 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
floor->get_line_edit()->add_constant_override("minimum_spaces", 16);
spatial_editor_hb->add_child(floor);
- floor->connect_compat("value_changed", this, "_floor_changed");
- floor->connect_compat("mouse_exited", this, "_floor_mouse_exited");
- floor->get_line_edit()->connect_compat("mouse_exited", this, "_floor_mouse_exited");
+ floor->connect("value_changed", callable_mp(this, &GridMapEditor::_floor_changed));
+ floor->connect("mouse_exited", callable_mp(this, &GridMapEditor::_floor_mouse_exited));
+ floor->get_line_edit()->connect("mouse_exited", callable_mp(this, &GridMapEditor::_floor_mouse_exited));
spatial_editor_hb->add_child(memnew(VSeparator));
@@ -1300,7 +1288,7 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
settings_vbc->add_margin_child(TTR("Pick Distance:"), settings_pick_distance);
clip_mode = CLIP_DISABLED;
- options->get_popup()->connect_compat("id_pressed", this, "_menu_option");
+ options->get_popup()->connect("id_pressed", callable_mp(this, &GridMapEditor::_menu_option));
HBoxContainer *hb = memnew(HBoxContainer);
add_child(hb);
@@ -1310,22 +1298,22 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
search_box->set_h_size_flags(SIZE_EXPAND_FILL);
search_box->set_placeholder(TTR("Filter meshes"));
hb->add_child(search_box);
- search_box->connect_compat("text_changed", this, "_text_changed");
- search_box->connect_compat("gui_input", this, "_sbox_input");
+ search_box->connect("text_changed", callable_mp(this, &GridMapEditor::_text_changed));
+ search_box->connect("gui_input", callable_mp(this, &GridMapEditor::_sbox_input));
mode_thumbnail = memnew(ToolButton);
mode_thumbnail->set_toggle_mode(true);
mode_thumbnail->set_pressed(true);
mode_thumbnail->set_icon(p_editor->get_gui_base()->get_icon("FileThumbnail", "EditorIcons"));
hb->add_child(mode_thumbnail);
- mode_thumbnail->connect_compat("pressed", this, "_set_display_mode", varray(DISPLAY_THUMBNAIL));
+ mode_thumbnail->connect("pressed", callable_mp(this, &GridMapEditor::_set_display_mode), varray(DISPLAY_THUMBNAIL));
mode_list = memnew(ToolButton);
mode_list->set_toggle_mode(true);
mode_list->set_pressed(false);
mode_list->set_icon(p_editor->get_gui_base()->get_icon("FileList", "EditorIcons"));
hb->add_child(mode_list);
- mode_list->connect_compat("pressed", this, "_set_display_mode", varray(DISPLAY_LIST));
+ mode_list->connect("pressed", callable_mp(this, &GridMapEditor::_set_display_mode), varray(DISPLAY_LIST));
size_slider = memnew(HSlider);
size_slider->set_h_size_flags(SIZE_EXPAND_FILL);
@@ -1333,7 +1321,7 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
size_slider->set_max(4.0f);
size_slider->set_step(0.1f);
size_slider->set_value(1.0f);
- size_slider->connect_compat("value_changed", this, "_icon_size_changed");
+ size_slider->connect("value_changed", callable_mp(this, &GridMapEditor::_icon_size_changed));
add_child(size_slider);
EDITOR_DEF("editors/grid_map/preview_size", 64);
@@ -1343,7 +1331,7 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
mesh_library_palette = memnew(ItemList);
add_child(mesh_library_palette);
mesh_library_palette->set_v_size_flags(SIZE_EXPAND_FILL);
- mesh_library_palette->connect_compat("gui_input", this, "_mesh_library_palette_input");
+ mesh_library_palette->connect("gui_input", callable_mp(this, &GridMapEditor::_mesh_library_palette_input));
info_message = memnew(Label);
info_message->set_text(TTR("Give a MeshLibrary resource to this GridMap to use its meshes."));
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 404d2eb022..6809cbdff9 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -3451,7 +3451,7 @@ CSharpScript::~CSharpScript() {
/*************** RESOURCE ***************/
-RES ResourceFormatLoaderCSharpScript::load(const String &p_path, const String &p_original_path, Error *r_error) {
+RES ResourceFormatLoaderCSharpScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress) {
if (r_error)
*r_error = ERR_FILE_CANT_OPEN;
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index f44c4aebc4..18c53aab52 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -497,7 +497,7 @@ public:
class ResourceFormatLoaderCSharpScript : public ResourceFormatLoader {
public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL);
+ virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL, bool p_use_sub_threads = false, float *r_progress = nullptr);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs
index 5965e0fbcf..77740f0e53 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs
@@ -218,20 +218,29 @@ namespace GodotTools.Ides.Rider
private static void CollectPathsFromRegistry(string registryKey, List<string> installPaths)
{
+ using (var key = Registry.CurrentUser.OpenSubKey(registryKey))
+ {
+ CollectPathsFromRegistry(installPaths, key);
+ }
using (var key = Registry.LocalMachine.OpenSubKey(registryKey))
{
- if (key == null) return;
- foreach (var subkeyName in key.GetSubKeyNames().Where(a => a.Contains("Rider")))
+ CollectPathsFromRegistry(installPaths, key);
+ }
+ }
+
+ private static void CollectPathsFromRegistry(List<string> installPaths, RegistryKey key)
+ {
+ if (key == null) return;
+ foreach (var subkeyName in key.GetSubKeyNames().Where(a => a.Contains("Rider")))
+ {
+ using (var subkey = key.OpenSubKey(subkeyName))
{
- using (var subkey = key.OpenSubKey(subkeyName))
- {
- var folderObject = subkey?.GetValue("InstallLocation");
- if (folderObject == null) continue;
- var folder = folderObject.ToString();
- var possiblePath = Path.Combine(folder, @"bin\rider64.exe");
- if (File.Exists(possiblePath))
- installPaths.Add(possiblePath);
- }
+ var folderObject = subkey?.GetValue("InstallLocation");
+ if (folderObject == null) continue;
+ var folder = folderObject.ToString();
+ var possiblePath = Path.Combine(folder, @"bin\rider64.exe");
+ if (File.Exists(possiblePath))
+ installPaths.Add(possiblePath);
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
index 19962d418a..2a9c2d73b1 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
@@ -83,7 +83,7 @@ namespace Godot
public static void Print(params object[] what)
{
- godot_icall_GD_print(Array.ConvertAll(what, x => x.ToString()));
+ godot_icall_GD_print(Array.ConvertAll(what, x => x?.ToString()));
}
public static void PrintStack()
diff --git a/modules/opensimplex/noise_texture.cpp b/modules/opensimplex/noise_texture.cpp
index d60099e676..8e5b04f995 100644
--- a/modules/opensimplex/noise_texture.cpp
+++ b/modules/opensimplex/noise_texture.cpp
@@ -76,7 +76,6 @@ void NoiseTexture::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_bump_strength"), &NoiseTexture::get_bump_strength);
ClassDB::bind_method(D_METHOD("_update_texture"), &NoiseTexture::_update_texture);
- ClassDB::bind_method(D_METHOD("_queue_update"), &NoiseTexture::_queue_update);
ClassDB::bind_method(D_METHOD("_generate_texture"), &NoiseTexture::_generate_texture);
ClassDB::bind_method(D_METHOD("_thread_done", "image"), &NoiseTexture::_thread_done);
@@ -184,11 +183,11 @@ void NoiseTexture::set_noise(Ref<OpenSimplexNoise> p_noise) {
if (p_noise == noise)
return;
if (noise.is_valid()) {
- noise->disconnect_compat(CoreStringNames::get_singleton()->changed, this, "_queue_update");
+ noise->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture::_queue_update));
}
noise = p_noise;
if (noise.is_valid()) {
- noise->connect_compat(CoreStringNames::get_singleton()->changed, this, "_queue_update");
+ noise->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture::_queue_update));
}
_queue_update();
}
diff --git a/modules/opus/audio_stream_opus.cpp b/modules/opus/audio_stream_opus.cpp
index 67a07628d9..a983edee91 100644
--- a/modules/opus/audio_stream_opus.cpp
+++ b/modules/opus/audio_stream_opus.cpp
@@ -354,7 +354,7 @@ AudioStreamPlaybackOpus::~AudioStreamPlaybackOpus() {
_clear_stream();
}
-RES ResourceFormatLoaderAudioStreamOpus::load(const String &p_path, const String &p_original_path, Error *r_error) {
+RES ResourceFormatLoaderAudioStreamOpus::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress) {
if (r_error)
*r_error = OK;
diff --git a/modules/opus/audio_stream_opus.h b/modules/opus/audio_stream_opus.h
index 5c0a02a9d0..343cbc6b83 100644
--- a/modules/opus/audio_stream_opus.h
+++ b/modules/opus/audio_stream_opus.h
@@ -133,7 +133,7 @@ public:
class ResourceFormatLoaderAudioStreamOpus : public ResourceFormatLoader {
public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL);
+ virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL, bool p_use_sub_threads = false, float *r_progress = nullptr);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
diff --git a/modules/pvr/texture_loader_pvr.cpp b/modules/pvr/texture_loader_pvr.cpp
index dab4f64393..179c6f692b 100644
--- a/modules/pvr/texture_loader_pvr.cpp
+++ b/modules/pvr/texture_loader_pvr.cpp
@@ -51,7 +51,7 @@ enum PVRFLags {
};
-RES ResourceFormatPVR::load(const String &p_path, const String &p_original_path, Error *r_error) {
+RES ResourceFormatPVR::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress) {
if (r_error)
*r_error = ERR_CANT_OPEN;
diff --git a/modules/pvr/texture_loader_pvr.h b/modules/pvr/texture_loader_pvr.h
index e384ed2b4c..c990c3612b 100644
--- a/modules/pvr/texture_loader_pvr.h
+++ b/modules/pvr/texture_loader_pvr.h
@@ -36,7 +36,7 @@
class ResourceFormatPVR : public ResourceFormatLoader {
public:
- virtual RES load(const String &p_path, const String &p_original_path, Error *r_error = NULL);
+ virtual RES load(const String &p_path, const String &p_original_path, Error *r_error = NULL, bool p_use_sub_threads = false, float *r_progress = nullptr);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
index 054ed088df..42f341cef7 100644
--- a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
+++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
@@ -57,7 +57,8 @@ void AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_fra
if (todo) {
//end of file!
- if (vorbis_stream->loop && mixed > 0) {
+ bool is_not_empty = mixed > 0 || stb_vorbis_stream_length_in_samples(ogg_stream) > 0;
+ if (vorbis_stream->loop && is_not_empty) {
//loop
seek(vorbis_stream->loop_offset);
loops++;
diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp
index a44ed0304d..ee44c6bf05 100644
--- a/modules/theora/video_stream_theora.cpp
+++ b/modules/theora/video_stream_theora.cpp
@@ -725,7 +725,7 @@ void VideoStreamTheora::_bind_methods() {
////////////
-RES ResourceFormatLoaderTheora::load(const String &p_path, const String &p_original_path, Error *r_error) {
+RES ResourceFormatLoaderTheora::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress) {
FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
if (!f) {
diff --git a/modules/theora/video_stream_theora.h b/modules/theora/video_stream_theora.h
index 258b3a1cce..da3558ce34 100644
--- a/modules/theora/video_stream_theora.h
+++ b/modules/theora/video_stream_theora.h
@@ -187,7 +187,7 @@ public:
class ResourceFormatLoaderTheora : public ResourceFormatLoader {
public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL);
+ virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL, bool p_use_sub_threads = false, float *r_progress = nullptr);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp
index 5036840413..471f43cadd 100644
--- a/modules/visual_script/visual_script.cpp
+++ b/modules/visual_script/visual_script.cpp
@@ -30,14 +30,14 @@
#include "visual_script.h"
-#include <stdint.h>
-
#include "core/core_string_names.h"
#include "core/os/os.h"
#include "core/project_settings.h"
#include "scene/main/node.h"
#include "visual_script_nodes.h"
+#include <stdint.h>
+
//used by editor, this is not really saved
void VisualScriptNode::set_breakpoint(bool p_breakpoint) {
breakpoint = p_breakpoint;
@@ -198,7 +198,7 @@ void VisualScript::remove_function(const StringName &p_name) {
for (Map<int, Function::NodeData>::Element *E = functions[p_name].nodes.front(); E; E = E->next()) {
- E->get().node->disconnect_compat("ports_changed", this, "_node_ports_changed");
+ E->get().node->disconnect("ports_changed", callable_mp(this, &VisualScript::_node_ports_changed));
E->get().node->scripts_used.erase(this);
}
@@ -340,7 +340,7 @@ void VisualScript::add_node(const StringName &p_func, int p_id, const Ref<Visual
nd.pos = p_pos;
Ref<VisualScriptNode> vsn = p_node;
- vsn->connect_compat("ports_changed", this, "_node_ports_changed", varray(p_id));
+ vsn->connect("ports_changed", callable_mp(this, &VisualScript::_node_ports_changed), varray(p_id));
vsn->scripts_used.insert(this);
vsn->validate_input_default_values(); // Validate when fully loaded
@@ -389,7 +389,7 @@ void VisualScript::remove_node(const StringName &p_func, int p_id) {
func.function_id = -1; //revert to invalid
}
- func.nodes[p_id].node->disconnect_compat("ports_changed", this, "_node_ports_changed");
+ func.nodes[p_id].node->disconnect("ports_changed", callable_mp(this, &VisualScript::_node_ports_changed));
func.nodes[p_id].node->scripts_used.erase(this);
func.nodes.erase(p_id);
@@ -1373,8 +1373,6 @@ Dictionary VisualScript::_get_data() const {
void VisualScript::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_node_ports_changed"), &VisualScript::_node_ports_changed);
-
ClassDB::bind_method(D_METHOD("add_function", "name"), &VisualScript::add_function);
ClassDB::bind_method(D_METHOD("has_function", "name"), &VisualScript::has_function);
ClassDB::bind_method(D_METHOD("remove_function", "name"), &VisualScript::remove_function);
diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp
index 0bbef066a4..8840b9f7cf 100644
--- a/modules/visual_script/visual_script_editor.cpp
+++ b/modules/visual_script/visual_script_editor.cpp
@@ -560,8 +560,8 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
gnode->set_meta("__vnode", node);
gnode->set_name(itos(E->get()));
- gnode->connect_compat("dragged", this, "_node_moved", varray(E->get()));
- gnode->connect_compat("close_request", this, "_remove_node", varray(E->get()), CONNECT_DEFERRED);
+ gnode->connect("dragged", callable_mp(this, &VisualScriptEditor::_node_moved), varray(E->get()));
+ gnode->connect("close_request", callable_mp(this, &VisualScriptEditor::_remove_node), varray(E->get()), CONNECT_DEFERRED);
if (E->get() != script->get_function_node_id(F->get())) {
//function can't be erased
@@ -579,7 +579,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
Button *btn = memnew(Button);
btn->set_text(TTR("Add Input Port"));
hbnc->add_child(btn);
- btn->connect_compat("pressed", this, "_add_input_port", varray(E->get()), CONNECT_DEFERRED);
+ btn->connect("pressed", callable_mp(this, &VisualScriptEditor::_add_input_port), varray(E->get()), CONNECT_DEFERRED);
}
if (nd_list->is_output_port_editable()) {
if (nd_list->is_input_port_editable())
@@ -588,7 +588,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
Button *btn = memnew(Button);
btn->set_text(TTR("Add Output Port"));
hbnc->add_child(btn);
- btn->connect_compat("pressed", this, "_add_output_port", varray(E->get()), CONNECT_DEFERRED);
+ btn->connect("pressed", callable_mp(this, &VisualScriptEditor::_add_output_port), varray(E->get()), CONNECT_DEFERRED);
}
gnode->add_child(hbnc);
} else if (Object::cast_to<VisualScriptExpression>(node.ptr())) {
@@ -598,7 +598,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
line_edit->set_expand_to_text_length(true);
line_edit->add_font_override("font", get_font("source", "EditorFonts"));
gnode->add_child(line_edit);
- line_edit->connect_compat("text_changed", this, "_expression_text_changed", varray(E->get()));
+ line_edit->connect("text_changed", callable_mp(this, &VisualScriptEditor::_expression_text_changed), varray(E->get()));
} else {
String text = node->get_text();
if (!text.empty()) {
@@ -614,7 +614,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
gnode->set_comment(true);
gnode->set_resizable(true);
gnode->set_custom_minimum_size(vsc->get_size() * EDSCALE);
- gnode->connect_compat("resize_request", this, "_comment_node_resized", varray(E->get()));
+ gnode->connect("resize_request", callable_mp(this, &VisualScriptEditor::_comment_node_resized), varray(E->get()));
}
if (node_styles.has(node->get_category())) {
@@ -732,8 +732,8 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0));
name_box->set_text(left_name);
name_box->set_expand_to_text_length(true);
- name_box->connect_compat("resized", this, "_update_node_size", varray(E->get()));
- name_box->connect_compat("focus_exited", this, "_port_name_focus_out", varray(name_box, E->get(), i, true));
+ name_box->connect("resized", callable_mp(this, &VisualScriptEditor::_update_node_size), varray(E->get()));
+ name_box->connect("focus_exited", callable_mp(this, &VisualScriptEditor::_port_name_focus_out), varray(name_box, E->get(), i, true));
} else {
hbc->add_child(memnew(Label(left_name)));
}
@@ -746,13 +746,13 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
opbtn->select(left_type);
opbtn->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
hbc->add_child(opbtn);
- opbtn->connect_compat("item_selected", this, "_change_port_type", varray(E->get(), i, true), CONNECT_DEFERRED);
+ opbtn->connect("item_selected", callable_mp(this, &VisualScriptEditor::_change_port_type), varray(E->get(), i, true), CONNECT_DEFERRED);
}
Button *rmbtn = memnew(Button);
rmbtn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Remove", "EditorIcons"));
hbc->add_child(rmbtn);
- rmbtn->connect_compat("pressed", this, "_remove_input_port", varray(E->get(), i), CONNECT_DEFERRED);
+ rmbtn->connect("pressed", callable_mp(this, &VisualScriptEditor::_remove_input_port), varray(E->get(), i), CONNECT_DEFERRED);
} else {
hbc->add_child(memnew(Label(left_name)));
}
@@ -772,7 +772,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
if (left_type == Variant::COLOR) {
button->set_custom_minimum_size(Size2(30, 0) * EDSCALE);
- button->connect_compat("draw", this, "_draw_color_over_button", varray(button, value));
+ button->connect("draw", callable_mp(this, &VisualScriptEditor::_draw_color_over_button), varray(button, value));
} else if (left_type == Variant::OBJECT && Ref<Resource>(value).is_valid()) {
Ref<Resource> res = value;
@@ -788,7 +788,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
button->set_text(value);
}
- button->connect_compat("pressed", this, "_default_value_edited", varray(button, E->get(), i));
+ button->connect("pressed", callable_mp(this, &VisualScriptEditor::_default_value_edited), varray(button, E->get(), i));
hbc2->add_child(button);
}
} else {
@@ -814,7 +814,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
Button *rmbtn = memnew(Button);
rmbtn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Remove", "EditorIcons"));
hbc->add_child(rmbtn);
- rmbtn->connect_compat("pressed", this, "_remove_output_port", varray(E->get(), i), CONNECT_DEFERRED);
+ rmbtn->connect("pressed", callable_mp(this, &VisualScriptEditor::_remove_output_port), varray(E->get(), i), CONNECT_DEFERRED);
if (nd_list->is_output_port_type_editable()) {
OptionButton *opbtn = memnew(OptionButton);
@@ -824,7 +824,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
opbtn->select(right_type);
opbtn->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
hbc->add_child(opbtn);
- opbtn->connect_compat("item_selected", this, "_change_port_type", varray(E->get(), i, false), CONNECT_DEFERRED);
+ opbtn->connect("item_selected", callable_mp(this, &VisualScriptEditor::_change_port_type), varray(E->get(), i, false), CONNECT_DEFERRED);
}
if (nd_list->is_output_port_name_editable()) {
@@ -833,8 +833,8 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0));
name_box->set_text(right_name);
name_box->set_expand_to_text_length(true);
- name_box->connect_compat("resized", this, "_update_node_size", varray(E->get()));
- name_box->connect_compat("focus_exited", this, "_port_name_focus_out", varray(name_box, E->get(), i, false));
+ name_box->connect("resized", callable_mp(this, &VisualScriptEditor::_update_node_size), varray(E->get()));
+ name_box->connect("focus_exited", callable_mp(this, &VisualScriptEditor::_port_name_focus_out), varray(name_box, E->get(), i, false));
} else {
hbc->add_child(memnew(Label(right_name)));
}
@@ -1237,7 +1237,7 @@ void VisualScriptEditor::_add_func_input() {
LineEdit *name_box = memnew(LineEdit);
name_box->set_h_size_flags(SIZE_EXPAND_FILL);
name_box->set_text("input");
- name_box->connect_compat("focus_entered", this, "_deselect_input_names");
+ name_box->connect("focus_entered", callable_mp(this, &VisualScriptEditor::_deselect_input_names));
hbox->add_child(name_box);
Label *type_label = memnew(Label);
@@ -1264,7 +1264,7 @@ void VisualScriptEditor::_add_func_input() {
func_input_vbox->add_child(hbox);
hbox->set_meta("id", hbox->get_position_in_parent());
- delete_button->connect_compat("pressed", this, "_remove_func_input", varray(hbox));
+ delete_button->connect("pressed", callable_mp(this, &VisualScriptEditor::_remove_func_input), varray(hbox));
name_box->select_all();
name_box->grab_focus();
@@ -1832,7 +1832,7 @@ void VisualScriptEditor::_fn_name_box_input(const Ref<InputEvent> &p_event) {
return;
Ref<InputEventKey> key = p_event;
- if (key.is_valid() && key->is_pressed() && key->get_scancode() == KEY_ENTER) {
+ if (key.is_valid() && key->is_pressed() && key->get_keycode() == KEY_ENTER) {
function_name_edit->hide();
_rename_function(selected, function_name_box->get_text());
function_name_box->clear();
@@ -2420,7 +2420,7 @@ void VisualScriptEditor::set_edited_resource(const RES &p_res) {
variable_editor->script = script;
variable_editor->undo_redo = undo_redo;
- script->connect_compat("node_ports_changed", this, "_node_ports_changed");
+ script->connect("node_ports_changed", callable_mp(this, &VisualScriptEditor::_node_ports_changed));
default_func = script->get_default_func();
@@ -3916,8 +3916,8 @@ void VisualScriptEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_READY: {
- variable_editor->connect_compat("changed", this, "_update_members");
- signal_editor->connect_compat("changed", this, "_update_members");
+ variable_editor->connect("changed", callable_mp(this, &VisualScriptEditor::_update_members));
+ signal_editor->connect("changed", callable_mp(this, &VisualScriptEditor::_update_members));
[[fallthrough]];
}
case NOTIFICATION_THEME_CHANGED: {
@@ -4613,77 +4613,23 @@ void VisualScriptEditor::set_syntax_highlighter(SyntaxHighlighter *p_highlighter
void VisualScriptEditor::_bind_methods() {
- ClassDB::bind_method("_member_button", &VisualScriptEditor::_member_button);
- ClassDB::bind_method("_member_edited", &VisualScriptEditor::_member_edited);
- ClassDB::bind_method("_member_selected", &VisualScriptEditor::_member_selected);
- ClassDB::bind_method("_update_members", &VisualScriptEditor::_update_members);
- ClassDB::bind_method("_members_gui_input", &VisualScriptEditor::_members_gui_input);
- ClassDB::bind_method("_member_rmb_selected", &VisualScriptEditor::_member_rmb_selected);
- ClassDB::bind_method("_member_option", &VisualScriptEditor::_member_option);
- ClassDB::bind_method("_fn_name_box_input", &VisualScriptEditor::_fn_name_box_input);
-
- ClassDB::bind_method("_change_base_type", &VisualScriptEditor::_change_base_type);
- ClassDB::bind_method("_change_base_type_callback", &VisualScriptEditor::_change_base_type_callback);
- ClassDB::bind_method("_toggle_tool_script", &VisualScriptEditor::_toggle_tool_script);
- ClassDB::bind_method("_node_selected", &VisualScriptEditor::_node_selected);
- ClassDB::bind_method("_node_moved", &VisualScriptEditor::_node_moved);
ClassDB::bind_method("_move_node", &VisualScriptEditor::_move_node);
- ClassDB::bind_method("_begin_node_move", &VisualScriptEditor::_begin_node_move);
- ClassDB::bind_method("_end_node_move", &VisualScriptEditor::_end_node_move);
- ClassDB::bind_method("_remove_node", &VisualScriptEditor::_remove_node);
ClassDB::bind_method("_update_graph", &VisualScriptEditor::_update_graph, DEFVAL(-1));
- ClassDB::bind_method("_node_ports_changed", &VisualScriptEditor::_node_ports_changed);
-
- ClassDB::bind_method("_create_function_dialog", &VisualScriptEditor::_create_function_dialog);
- ClassDB::bind_method("_create_function", &VisualScriptEditor::_create_function);
- ClassDB::bind_method("_add_node_dialog", &VisualScriptEditor::_add_node_dialog);
- ClassDB::bind_method("_add_func_input", &VisualScriptEditor::_add_func_input);
- ClassDB::bind_method("_remove_func_input", &VisualScriptEditor::_remove_func_input);
- ClassDB::bind_method("_deselect_input_names", &VisualScriptEditor::_deselect_input_names);
-
- ClassDB::bind_method("_default_value_edited", &VisualScriptEditor::_default_value_edited);
- ClassDB::bind_method("_default_value_changed", &VisualScriptEditor::_default_value_changed);
- ClassDB::bind_method("_menu_option", &VisualScriptEditor::_menu_option);
- ClassDB::bind_method("_graph_ofs_changed", &VisualScriptEditor::_graph_ofs_changed);
+
ClassDB::bind_method("_center_on_node", &VisualScriptEditor::_center_on_node);
- ClassDB::bind_method("_comment_node_resized", &VisualScriptEditor::_comment_node_resized);
ClassDB::bind_method("_button_resource_previewed", &VisualScriptEditor::_button_resource_previewed);
ClassDB::bind_method("_port_action_menu", &VisualScriptEditor::_port_action_menu);
- ClassDB::bind_method("_selected_connect_node", &VisualScriptEditor::_selected_connect_node);
- ClassDB::bind_method("_selected_new_virtual_method", &VisualScriptEditor::_selected_new_virtual_method);
- ClassDB::bind_method("_cancel_connect_node", &VisualScriptEditor::_cancel_connect_node);
ClassDB::bind_method("_create_new_node_from_name", &VisualScriptEditor::_create_new_node_from_name);
- ClassDB::bind_method("_expression_text_changed", &VisualScriptEditor::_expression_text_changed);
- ClassDB::bind_method("_add_input_port", &VisualScriptEditor::_add_input_port);
- ClassDB::bind_method("_add_output_port", &VisualScriptEditor::_add_output_port);
- ClassDB::bind_method("_remove_input_port", &VisualScriptEditor::_remove_input_port);
- ClassDB::bind_method("_remove_output_port", &VisualScriptEditor::_remove_output_port);
- ClassDB::bind_method("_change_port_type", &VisualScriptEditor::_change_port_type);
- ClassDB::bind_method("_update_node_size", &VisualScriptEditor::_update_node_size);
- ClassDB::bind_method("_port_name_focus_out", &VisualScriptEditor::_port_name_focus_out);
ClassDB::bind_method("get_drag_data_fw", &VisualScriptEditor::get_drag_data_fw);
ClassDB::bind_method("can_drop_data_fw", &VisualScriptEditor::can_drop_data_fw);
ClassDB::bind_method("drop_data_fw", &VisualScriptEditor::drop_data_fw);
ClassDB::bind_method("_input", &VisualScriptEditor::_input);
- ClassDB::bind_method("_graph_gui_input", &VisualScriptEditor::_graph_gui_input);
-
- ClassDB::bind_method("_on_nodes_delete", &VisualScriptEditor::_on_nodes_delete);
- ClassDB::bind_method("_on_nodes_duplicate", &VisualScriptEditor::_on_nodes_duplicate);
-
- ClassDB::bind_method("_hide_timer", &VisualScriptEditor::_hide_timer);
-
- ClassDB::bind_method("_graph_connected", &VisualScriptEditor::_graph_connected);
- ClassDB::bind_method("_graph_disconnected", &VisualScriptEditor::_graph_disconnected);
- ClassDB::bind_method("_graph_connect_to_empty", &VisualScriptEditor::_graph_connect_to_empty);
ClassDB::bind_method("_update_graph_connections", &VisualScriptEditor::_update_graph_connections);
- ClassDB::bind_method("_selected_method", &VisualScriptEditor::_selected_method);
- ClassDB::bind_method("_draw_color_over_button", &VisualScriptEditor::_draw_color_over_button);
-
ClassDB::bind_method("_generic_search", &VisualScriptEditor::_generic_search);
}
@@ -4709,7 +4655,7 @@ VisualScriptEditor::VisualScriptEditor() {
edit_menu->get_popup()->add_separator();
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/create_function"), EDIT_CREATE_FUNCTION);
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/refresh_nodes"), REFRESH_GRAPH);
- edit_menu->get_popup()->connect_compat("id_pressed", this, "_menu_option");
+ edit_menu->get_popup()->connect("id_pressed", callable_mp(this, &VisualScriptEditor::_menu_option));
members_section = memnew(VBoxContainer);
// Add but wait until done setting up this.
@@ -4719,7 +4665,7 @@ VisualScriptEditor::VisualScriptEditor() {
CheckButton *tool_script_check = memnew(CheckButton);
tool_script_check->set_text(TTR("Make Tool:"));
members_section->add_child(tool_script_check);
- tool_script_check->connect_compat("pressed", this, "_toggle_tool_script");
+ tool_script_check->connect("pressed", callable_mp(this, &VisualScriptEditor::_toggle_tool_script));
/// Members ///
@@ -4727,11 +4673,11 @@ VisualScriptEditor::VisualScriptEditor() {
members_section->add_margin_child(TTR("Members:"), members, true);
members->set_custom_minimum_size(Size2(0, 50 * EDSCALE));
members->set_hide_root(true);
- members->connect_compat("button_pressed", this, "_member_button");
- members->connect_compat("item_edited", this, "_member_edited");
- members->connect_compat("cell_selected", this, "_member_selected", varray(), CONNECT_DEFERRED);
- members->connect_compat("gui_input", this, "_members_gui_input");
- members->connect_compat("item_rmb_selected", this, "_member_rmb_selected");
+ members->connect("button_pressed", callable_mp(this, &VisualScriptEditor::_member_button));
+ members->connect("item_edited", callable_mp(this, &VisualScriptEditor::_member_edited));
+ members->connect("cell_selected", callable_mp(this, &VisualScriptEditor::_member_selected), varray(), CONNECT_DEFERRED);
+ members->connect("gui_input", callable_mp(this, &VisualScriptEditor::_members_gui_input));
+ members->connect("item_rmb_selected", callable_mp(this, &VisualScriptEditor::_member_rmb_selected));
members->set_allow_rmb_select(true);
members->set_allow_reselect(true);
members->set_hide_folding(true);
@@ -4739,13 +4685,13 @@ VisualScriptEditor::VisualScriptEditor() {
member_popup = memnew(PopupMenu);
add_child(member_popup);
- member_popup->connect_compat("id_pressed", this, "_member_option");
+ member_popup->connect("id_pressed", callable_mp(this, &VisualScriptEditor::_member_option));
function_name_edit = memnew(PopupDialog);
function_name_box = memnew(LineEdit);
function_name_edit->add_child(function_name_box);
function_name_edit->set_h_size_flags(SIZE_EXPAND);
- function_name_box->connect_compat("gui_input", this, "_fn_name_box_input");
+ function_name_box->connect("gui_input", callable_mp(this, &VisualScriptEditor::_fn_name_box_input));
function_name_box->set_expand_to_text_length(true);
add_child(function_name_edit);
@@ -4755,15 +4701,15 @@ VisualScriptEditor::VisualScriptEditor() {
add_child(graph);
graph->set_v_size_flags(Control::SIZE_EXPAND_FILL);
graph->set_anchors_and_margins_preset(Control::PRESET_WIDE);
- graph->connect_compat("node_selected", this, "_node_selected");
- graph->connect_compat("_begin_node_move", this, "_begin_node_move");
- graph->connect_compat("_end_node_move", this, "_end_node_move");
- graph->connect_compat("delete_nodes_request", this, "_on_nodes_delete");
- graph->connect_compat("duplicate_nodes_request", this, "_on_nodes_duplicate");
- graph->connect_compat("gui_input", this, "_graph_gui_input");
+ graph->connect("node_selected", callable_mp(this, &VisualScriptEditor::_node_selected));
+ graph->connect("_begin_node_move", callable_mp(this, &VisualScriptEditor::_begin_node_move));
+ graph->connect("_end_node_move", callable_mp(this, &VisualScriptEditor::_end_node_move));
+ graph->connect("delete_nodes_request", callable_mp(this, &VisualScriptEditor::_on_nodes_delete));
+ graph->connect("duplicate_nodes_request", callable_mp(this, &VisualScriptEditor::_on_nodes_duplicate));
+ graph->connect("gui_input", callable_mp(this, &VisualScriptEditor::_graph_gui_input));
graph->set_drag_forwarding(this);
graph->hide();
- graph->connect_compat("scroll_offset_changed", this, "_graph_ofs_changed");
+ graph->connect("scroll_offset_changed", callable_mp(this, &VisualScriptEditor::_graph_ofs_changed));
/// Add Buttons to Top Bar/Zoom bar.
HBoxContainer *graph_hbc = graph->get_zoom_hbox();
@@ -4773,18 +4719,18 @@ VisualScriptEditor::VisualScriptEditor() {
graph_hbc->add_child(base_lbl);
base_type_select = memnew(Button);
- base_type_select->connect_compat("pressed", this, "_change_base_type");
+ base_type_select->connect("pressed", callable_mp(this, &VisualScriptEditor::_change_base_type));
graph_hbc->add_child(base_type_select);
Button *add_nds = memnew(Button);
add_nds->set_text(TTR("Add Nodes..."));
graph_hbc->add_child(add_nds);
- add_nds->connect_compat("pressed", this, "_add_node_dialog");
+ add_nds->connect("pressed", callable_mp(this, &VisualScriptEditor::_add_node_dialog));
Button *fn_btn = memnew(Button);
fn_btn->set_text(TTR("Add Function..."));
graph_hbc->add_child(fn_btn);
- fn_btn->connect_compat("pressed", this, "_create_function_dialog");
+ fn_btn->connect("pressed", callable_mp(this, &VisualScriptEditor::_create_function_dialog));
// Add Function Dialog.
VBoxContainer *function_vb = memnew(VBoxContainer);
@@ -4802,7 +4748,7 @@ VisualScriptEditor::VisualScriptEditor() {
func_name_box->set_h_size_flags(SIZE_EXPAND_FILL);
func_name_box->set_placeholder(TTR("function_name"));
func_name_box->set_text("");
- func_name_box->connect_compat("focus_entered", this, "_deselect_input_names");
+ func_name_box->connect("focus_entered", callable_mp(this, &VisualScriptEditor::_deselect_input_names));
func_name_hbox->add_child(func_name_box);
// Add minor setting for function if needed, here!
@@ -4812,7 +4758,7 @@ VisualScriptEditor::VisualScriptEditor() {
Button *add_input_button = memnew(Button);
add_input_button->set_h_size_flags(SIZE_EXPAND_FILL);
add_input_button->set_text(TTR("Add Input"));
- add_input_button->connect_compat("pressed", this, "_add_func_input");
+ add_input_button->connect("pressed", callable_mp(this, &VisualScriptEditor::_add_func_input));
function_vb->add_child(add_input_button);
func_input_scroll = memnew(ScrollContainer);
@@ -4828,7 +4774,7 @@ VisualScriptEditor::VisualScriptEditor() {
function_create_dialog->set_title(TTR("Create Function"));
function_create_dialog->add_child(function_vb);
function_create_dialog->get_ok()->set_text(TTR("Create"));
- function_create_dialog->get_ok()->connect_compat("pressed", this, "_create_function");
+ function_create_dialog->get_ok()->connect("pressed", callable_mp(this, &VisualScriptEditor::_create_function));
add_child(function_create_dialog);
select_func_text = memnew(Label);
@@ -4848,7 +4794,7 @@ VisualScriptEditor::VisualScriptEditor() {
hint_text_timer = memnew(Timer);
hint_text_timer->set_wait_time(4);
- hint_text_timer->connect_compat("timeout", this, "_hide_timer");
+ hint_text_timer->connect("timeout", callable_mp(this, &VisualScriptEditor::_hide_timer));
add_child(hint_text_timer);
// Allowed casts (connections).
@@ -4866,9 +4812,9 @@ VisualScriptEditor::VisualScriptEditor() {
graph->add_valid_left_disconnect_type(TYPE_SEQUENCE);
- graph->connect_compat("connection_request", this, "_graph_connected");
- graph->connect_compat("disconnection_request", this, "_graph_disconnected");
- graph->connect_compat("connection_to_empty", this, "_graph_connect_to_empty");
+ graph->connect("connection_request", callable_mp(this, &VisualScriptEditor::_graph_connected));
+ graph->connect("disconnection_request", callable_mp(this, &VisualScriptEditor::_graph_disconnected));
+ graph->connect("connection_to_empty", callable_mp(this, &VisualScriptEditor::_graph_connect_to_empty));
edit_signal_dialog = memnew(AcceptDialog);
edit_signal_dialog->get_ok()->set_text(TTR("Close"));
@@ -4892,7 +4838,7 @@ VisualScriptEditor::VisualScriptEditor() {
select_base_type = memnew(CreateDialog);
select_base_type->set_base_type("Object"); // Anything goes.
- select_base_type->connect_compat("create", this, "_change_base_type_callback");
+ select_base_type->connect("create", callable_mp(this, &VisualScriptEditor::_change_base_type_callback));
add_child(select_base_type);
undo_redo = EditorNode::get_singleton()->get_undo_redo();
@@ -4904,22 +4850,22 @@ VisualScriptEditor::VisualScriptEditor() {
default_value_edit = memnew(CustomPropertyEditor);
add_child(default_value_edit);
- default_value_edit->connect_compat("variant_changed", this, "_default_value_changed");
+ default_value_edit->connect("variant_changed", callable_mp(this, &VisualScriptEditor::_default_value_changed));
method_select = memnew(VisualScriptPropertySelector);
add_child(method_select);
- method_select->connect_compat("selected", this, "_selected_method");
+ method_select->connect("selected", callable_mp(this, &VisualScriptEditor::_selected_method));
error_line = -1;
new_connect_node_select = memnew(VisualScriptPropertySelector);
add_child(new_connect_node_select);
new_connect_node_select->set_resizable(true);
- new_connect_node_select->connect_compat("selected", this, "_selected_connect_node");
- new_connect_node_select->get_cancel()->connect_compat("pressed", this, "_cancel_connect_node");
+ new_connect_node_select->connect("selected", callable_mp(this, &VisualScriptEditor::_selected_connect_node));
+ new_connect_node_select->get_cancel()->connect("pressed", callable_mp(this, &VisualScriptEditor::_cancel_connect_node));
new_virtual_method_select = memnew(VisualScriptPropertySelector);
add_child(new_virtual_method_select);
- new_virtual_method_select->connect_compat("selected", this, "_selected_new_virtual_method");
+ new_virtual_method_select->connect("selected", callable_mp(this, &VisualScriptEditor::_selected_new_virtual_method));
}
VisualScriptEditor::~VisualScriptEditor() {
diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp
index 4a9a6bdb1d..ea09c82013 100644
--- a/modules/visual_script/visual_script_nodes.cpp
+++ b/modules/visual_script/visual_script_nodes.cpp
@@ -3130,8 +3130,6 @@ void VisualScriptCustomNode::_bind_methods() {
stepmi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
BIND_VMETHOD(stepmi);
- ClassDB::bind_method(D_METHOD("_script_changed"), &VisualScriptCustomNode::_script_changed);
-
BIND_ENUM_CONSTANT(START_MODE_BEGIN_SEQUENCE);
BIND_ENUM_CONSTANT(START_MODE_CONTINUE_SEQUENCE);
BIND_ENUM_CONSTANT(START_MODE_RESUME_YIELD);
@@ -3144,7 +3142,7 @@ void VisualScriptCustomNode::_bind_methods() {
}
VisualScriptCustomNode::VisualScriptCustomNode() {
- connect_compat("script_changed", this, "_script_changed");
+ connect("script_changed", callable_mp(this, &VisualScriptCustomNode::_script_changed));
}
//////////////////////////////////////////
diff --git a/modules/visual_script/visual_script_property_selector.cpp b/modules/visual_script/visual_script_property_selector.cpp
index 20bad364dc..d799f19143 100644
--- a/modules/visual_script/visual_script_property_selector.cpp
+++ b/modules/visual_script/visual_script_property_selector.cpp
@@ -51,7 +51,7 @@ void VisualScriptPropertySelector::_sbox_input(const Ref<InputEvent> &p_ie) {
if (k.is_valid()) {
- switch (k->get_scancode()) {
+ switch (k->get_keycode()) {
case KEY_UP:
case KEY_DOWN:
case KEY_PAGEUP:
@@ -518,11 +518,15 @@ void VisualScriptPropertySelector::_item_selected() {
help_bit->set_text(text);
}
+void VisualScriptPropertySelector::_hide_requested() {
+ _closed(); // From WindowDialog.
+}
+
void VisualScriptPropertySelector::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
- connect_compat("confirmed", this, "_confirmed");
+ connect("confirmed", callable_mp(this, &VisualScriptPropertySelector::_confirmed));
}
}
@@ -690,11 +694,6 @@ void VisualScriptPropertySelector::show_window(float p_screen_ratio) {
void VisualScriptPropertySelector::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_text_changed"), &VisualScriptPropertySelector::_text_changed);
- ClassDB::bind_method(D_METHOD("_confirmed"), &VisualScriptPropertySelector::_confirmed);
- ClassDB::bind_method(D_METHOD("_sbox_input"), &VisualScriptPropertySelector::_sbox_input);
- ClassDB::bind_method(D_METHOD("_item_selected"), &VisualScriptPropertySelector::_item_selected);
-
ADD_SIGNAL(MethodInfo("selected", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "category"), PropertyInfo(Variant::BOOL, "connecting")));
}
@@ -705,23 +704,23 @@ VisualScriptPropertySelector::VisualScriptPropertySelector() {
//set_child_rect(vbc);
search_box = memnew(LineEdit);
vbc->add_margin_child(TTR("Search:"), search_box);
- search_box->connect_compat("text_changed", this, "_text_changed");
- search_box->connect_compat("gui_input", this, "_sbox_input");
+ search_box->connect("text_changed", callable_mp(this, &VisualScriptPropertySelector::_text_changed));
+ search_box->connect("gui_input", callable_mp(this, &VisualScriptPropertySelector::_sbox_input));
search_options = memnew(Tree);
vbc->add_margin_child(TTR("Matches:"), search_options, true);
get_ok()->set_text(TTR("Open"));
get_ok()->set_disabled(true);
register_text_enter(search_box);
set_hide_on_ok(false);
- search_options->connect_compat("item_activated", this, "_confirmed");
- search_options->connect_compat("cell_selected", this, "_item_selected");
+ search_options->connect("item_activated", callable_mp(this, &VisualScriptPropertySelector::_confirmed));
+ search_options->connect("cell_selected", callable_mp(this, &VisualScriptPropertySelector::_item_selected));
search_options->set_hide_root(true);
search_options->set_hide_folding(true);
virtuals_only = false;
seq_connect = false;
help_bit = memnew(EditorHelpBit);
vbc->add_margin_child(TTR("Description:"), help_bit);
- help_bit->connect_compat("request_hide", this, "_closed");
+ help_bit->connect("request_hide", callable_mp(this, &VisualScriptPropertySelector::_hide_requested));
search_options->set_columns(3);
search_options->set_column_expand(1, false);
search_options->set_column_expand(2, false);
diff --git a/modules/visual_script/visual_script_property_selector.h b/modules/visual_script/visual_script_property_selector.h
index a1eb0b842c..f438ca1f5b 100644
--- a/modules/visual_script/visual_script_property_selector.h
+++ b/modules/visual_script/visual_script_property_selector.h
@@ -41,16 +41,16 @@ class VisualScriptPropertySelector : public ConfirmationDialog {
LineEdit *search_box;
Tree *search_options;
+ void _text_changed(const String &p_newtext);
+ void _sbox_input(const Ref<InputEvent> &p_ie);
void _update_search();
void create_visualscript_item(const String &name, TreeItem *const root, const String &search_input, const String &text);
-
void get_visual_node_names(const String &root_filter, const Set<String> &p_modifiers, bool &found, TreeItem *const root, LineEdit *const search_box);
- void _sbox_input(const Ref<InputEvent> &p_ie);
-
void _confirmed();
- void _text_changed(const String &p_newtext);
+ void _item_selected();
+ void _hide_requested();
EditorHelpBit *help_bit;
@@ -65,8 +65,6 @@ class VisualScriptPropertySelector : public ConfirmationDialog {
bool virtuals_only;
bool seq_connect;
- void _item_selected();
-
Vector<Variant::Type> type_filter;
protected:
diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp
index 87067faf8e..1e8ee9b17e 100644
--- a/modules/vorbis/audio_stream_ogg_vorbis.cpp
+++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp
@@ -381,7 +381,7 @@ AudioStreamPlaybackOGGVorbis::~AudioStreamPlaybackOGGVorbis() {
_clear_stream();
}
-RES ResourceFormatLoaderAudioStreamOGGVorbis::load(const String &p_path, const String &p_original_path, Error *r_error) {
+RES ResourceFormatLoaderAudioStreamOGGVorbis::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress) {
if (r_error)
*r_error = OK;
diff --git a/modules/vorbis/audio_stream_ogg_vorbis.h b/modules/vorbis/audio_stream_ogg_vorbis.h
index 739765a12f..db621f88d2 100644
--- a/modules/vorbis/audio_stream_ogg_vorbis.h
+++ b/modules/vorbis/audio_stream_ogg_vorbis.h
@@ -128,7 +128,7 @@ public:
class ResourceFormatLoaderAudioStreamOGGVorbis : public ResourceFormatLoader {
public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL);
+ virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL, bool p_use_sub_threads = false, float *r_progress = nullptr);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
diff --git a/modules/webm/video_stream_webm.cpp b/modules/webm/video_stream_webm.cpp
index 5768392fe5..265383831e 100644
--- a/modules/webm/video_stream_webm.cpp
+++ b/modules/webm/video_stream_webm.cpp
@@ -474,7 +474,7 @@ void VideoStreamWebm::set_audio_track(int p_track) {
////////////
-RES ResourceFormatLoaderWebm::load(const String &p_path, const String &p_original_path, Error *r_error) {
+RES ResourceFormatLoaderWebm::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress) {
FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
if (!f) {
diff --git a/modules/webm/video_stream_webm.h b/modules/webm/video_stream_webm.h
index a3d3c173f4..3feaa1278f 100644
--- a/modules/webm/video_stream_webm.h
+++ b/modules/webm/video_stream_webm.h
@@ -128,7 +128,7 @@ public:
class ResourceFormatLoaderWebm : public ResourceFormatLoader {
public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL);
+ virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL, bool p_use_sub_threads = false, float *r_progress = nullptr);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;