diff options
Diffstat (limited to 'modules')
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 ¤t_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; |