diff --git a/modules/csg/SCsub b/modules/csg/SCsub deleted file mode 100644 index 641a42c18..000000000 --- a/modules/csg/SCsub +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env python - -Import("env") -Import("env_modules") - -env_csg = env_modules.Clone() - -# Godot's own source files -env_csg.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/csg/config.py b/modules/csg/config.py deleted file mode 100644 index 0f267872d..000000000 --- a/modules/csg/config.py +++ /dev/null @@ -1,24 +0,0 @@ -def can_build(env, platform): - return True - - -def configure(env): - pass - - -def get_doc_classes(): - return [ - "CSGBox", - "CSGCombiner", - "CSGCylinder", - "CSGMesh", - "CSGPolygon", - "CSGPrimitive", - "CSGShape", - "CSGSphere", - "CSGTorus", - ] - - -def get_doc_path(): - return "doc_classes" diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp deleted file mode 100644 index 25ae92eb0..000000000 --- a/modules/csg/csg.cpp +++ /dev/null @@ -1,1479 +0,0 @@ -/*************************************************************************/ -/* csg.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "csg.h" - -#include "core/math/geometry.h" -#include "core/math/math_funcs.h" -#include "core/sort_array.h" - -// 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 parallel 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 is_triangle_degenerate(const Vector2 p_vertices[3], real_t p_vertex_snap2) { - real_t det = p_vertices[0].x * p_vertices[1].y - p_vertices[0].x * p_vertices[2].y + - p_vertices[0].y * p_vertices[2].x - p_vertices[0].y * p_vertices[1].x + - p_vertices[1].x * p_vertices[2].y - p_vertices[1].y * p_vertices[2].x; - - return det < p_vertex_snap2; -} - -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 PoolVector &p_vertices, const PoolVector &p_uvs, const PoolVector &p_smooth, const PoolVector> &p_materials, const PoolVector &p_invert_faces) { - faces.clear(); - - int vc = p_vertices.size(); - - ERR_FAIL_COND((vc % 3) != 0); - - PoolVector::Read rv = p_vertices.read(); - int uvc = p_uvs.size(); - PoolVector::Read ruv = p_uvs.read(); - int sc = p_smooth.size(); - PoolVector::Read rs = p_smooth.read(); - int mc = p_materials.size(); - PoolVector>::Read rm = p_materials.read(); - int ic = p_invert_faces.size(); - PoolVector::Read ri = p_invert_faces.read(); - - Map, int> material_map; - - 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) { - f.smooth = rs[i]; - } else { - f.smooth = false; - } - - if (ic == vc / 3) { - f.invert = ri[i]; - } else { - f.invert = false; - } - - if (mc == vc / 3) { - Ref mat = rm[i]; - if (mat.is_valid()) { - const Map, 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; - } - } - } - - materials.resize(material_map.size()); - for (Map, int>::Element *E = material_map.front(); E; E = E->next()) { - materials.write[E->get()] = E->key(); - } - - _regen_face_aabbs(); -} - -void CSGBrush::copy_from(const CSGBrush &p_brush, const Transform &p_xform) { - faces = p_brush.faces; - materials = p_brush.materials; - - for (int i = 0; i < faces.size(); i++) { - for (int j = 0; j < 3; j++) { - faces.write[i].vertices[j] = p_xform.xform(p_brush.faces[i].vertices[j]); - } - } - - _regen_face_aabbs(); -} - -// CSGBrushOperation - -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) { - // 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); - } - } - } - - // Add faces to MeshMerge. - MeshMerge mesh_merge; - mesh_merge.vertex_snap = p_vertex_snap; - - for (int i = 0; i < p_brush_a.faces.size(); i++) { - Ref material; - if (p_brush_a.faces[i].material != -1) { - material = p_brush_a.materials[p_brush_a.faces[i].material]; - } - - 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]; - } - mesh_merge.add_face(points, uvs, p_brush_a.faces[i].smooth, p_brush_a.faces[i].invert, material, false); - } - } - - for (int i = 0; i < p_brush_b.faces.size(); i++) { - Ref material; - if (p_brush_b.faces[i].material != -1) { - material = p_brush_b.materials[p_brush_b.faces[i].material]; - } - - 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 points[3]; - Vector2 uvs[3]; - for (int j = 0; j < 3; j++) { - points[j] = p_brush_b.faces[i].vertices[j]; - uvs[j] = p_brush_b.faces[i].uvs[j]; - } - mesh_merge.add_face(points, uvs, p_brush_b.faces[i].smooth, p_brush_b.faces[i].invert, material, true); - } - } - - // Mark faces that ended up inside the intersection. - mesh_merge.mark_inside_faces(); - - // Create new brush and fill with new faces. - r_merged_brush.faces.clear(); - - switch (p_operation) { - case OPERATION_UNION: { - int outside_count = 0; - - for (int i = 0; i < mesh_merge.faces.size(); i++) { - if (mesh_merge.faces[i].inside) { - continue; - } - outside_count++; - } - - r_merged_brush.faces.resize(outside_count); - - outside_count = 0; - - for (int i = 0; i < mesh_merge.faces.size(); i++) { - if (mesh_merge.faces[i].inside) { - continue; - } - - 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]; - } - - 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++; - } - - r_merged_brush._regen_face_aabbs(); - - } break; - - case OPERATION_INTERSECTION: { - int inside_count = 0; - - for (int i = 0; i < mesh_merge.faces.size(); i++) { - if (!mesh_merge.faces[i].inside) { - continue; - } - inside_count++; - } - - r_merged_brush.faces.resize(inside_count); - - inside_count = 0; - - for (int i = 0; i < mesh_merge.faces.size(); i++) { - if (!mesh_merge.faces[i].inside) { - continue; - } - - 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]; - } - - 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++; - } - - r_merged_brush._regen_face_aabbs(); - - } break; - - case OPERATION_SUBTRACTION: { - int face_count = 0; - - 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++; - } - - r_merged_brush.faces.resize(face_count); - - face_count = 0; - - 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; - } - - 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 (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]); - } - - 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++; - } - - r_merged_brush._regen_face_aabbs(); - - } break; - } - - // Update the list of materials. - r_merged_brush.materials.resize(mesh_merge.materials.size()); - for (const Map, int>::Element *E = mesh_merge.materials.front(); E; E = E->next()) { - r_merged_brush.materials.write[E->get()] = E->key(); - } -} - -// CSGBrushOperation::MeshMerge - -// Use a limit to speed up bvh and limit the depth. -#define BVH_LIMIT 8 - -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 > r_max_depth) { - r_max_depth = p_depth; - } - - if (p_size == 0) { - return -1; - } - - if (p_size <= BVH_LIMIT) { - for (int i = 0; i < p_size - 1; i++) { - facebvhptrptr[p_from + i]->next = facebvhptrptr[p_from + i + 1] - facebvhptr; - } - return facebvhptrptr[p_from] - facebvhptr; - } - - AABB aabb; - aabb = facebvhptrptr[p_from]->aabb; - for (int i = 1; i < p_size; i++) { - aabb.merge_with(facebvhptrptr[p_from + i]->aabb); - } - - int li = aabb.get_longest_axis_index(); - - switch (li) { - case Vector3::AXIS_X: { - SortArray 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 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 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(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 = r_max_alloc++; - FaceBVH *_new = &facebvhptr[index]; - _new->aabb = aabb; - _new->center = aabb.position + aabb.size * 0.5; - _new->face = -1; - _new->left = left; - _new->right = right; - _new->next = -1; - - return index; -} - -void CSGBrushOperation::MeshMerge::_add_distance(List &r_intersectionsA, List &r_intersectionsB, bool p_from_B, real_t p_distance) const { - List &intersections = p_from_B ? r_intersectionsB : r_intersectionsA; - - // Check if distance exists. - for (const List::Element *E = intersections.front(); E; E = E->next()) { - if (Math::is_equal_approx(**E, p_distance)) { - 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); - - enum { - TEST_AABB_BIT = 0, - VISIT_LEFT_BIT = 1, - VISIT_RIGHT_BIT = 2, - VISIT_DONE_BIT = 3, - VISITED_BIT_SHIFT = 29, - NODE_IDX_MASK = (1 << VISITED_BIT_SHIFT) - 1, - VISITED_BIT_MASK = ~NODE_IDX_MASK - }; - - List intersectionsA; - List intersectionsB; - - int level = 0; - int pos = p_bvh_first; - stack[0] = pos; - - while (true) { - uint32_t node = stack[level] & NODE_IDX_MASK; - const FaceBVH *current_facebvhptr = &(facebvhptr[node]); - bool done = false; - - switch (stack[level] >> VISITED_BIT_SHIFT) { - case TEST_AABB_BIT: { - 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 not 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 (current_facebvhptr->next != -1) { - current_facebvhptr = &facebvhptr[current_facebvhptr->next]; - } else { - current_facebvhptr = nullptr; - } - } - - stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; - - } else { - 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] = current_facebvhptr->left | TEST_AABB_BIT; - level++; - continue; - } - - case VISIT_RIGHT_BIT: { - stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; - stack[level + 1] = current_facebvhptr->right | TEST_AABB_BIT; - level++; - continue; - } - - case VISIT_DONE_BIT: { - if (level == 0) { - done = true; - break; - } else { - level--; - } - continue; - } - } - - if (done) { - break; - } - } - - // 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 optimizations, - // such as BVH and pre AABB intersection test. - - Vector bvhvec; - bvhvec.resize(faces.size() * 3); // Will never be larger than this (TODO: Make better) - FaceBVH *facebvh = bvhvec.ptrw(); - - AABB aabb_a; - AABB aabb_b; - - bool first_a = true; - bool first_b = true; - - for (int i = 0; i < faces.size(); i++) { - 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) { - aabb_b = facebvh[i].aabb; - first_b = false; - } else { - aabb_b.merge_with(facebvh[i].aabb); - } - } else { - if (first_a) { - aabb_a = facebvh[i].aabb; - first_a = false; - } else { - aabb_a.merge_with(facebvh[i].aabb); - } - } - } - - AABB intersection_aabb = aabb_a.intersection(aabb_b); - - // Check if shape AABBs intersect. - if (intersection_aabb.size == Vector3()) { - return; - } - - Vector bvhtrvec; - bvhtrvec.resize(faces.size()); - FaceBVH **bvhptr = bvhtrvec.ptrw(); - for (int i = 0; i < faces.size(); i++) { - bvhptr[i] = &facebvh[i]; - } - - int max_depth = 0; - int max_alloc = faces.size(); - _create_bvh(facebvh, bvhptr, 0, faces.size(), 1, max_depth, max_alloc); - - for (int i = 0; i < faces.size(); i++) { - // Check if face AABB intersects the intersection AABB. - if (!intersection_aabb.intersects_inclusive(facebvh[i].aabb)) { - continue; - } - - if (_bvh_inside(facebvh, max_depth, max_alloc - 1, i)) { - faces.write[i].inside = true; - } - } -} - -void CSGBrushOperation::MeshMerge::add_face(const Vector3 p_points[], const Vector2 p_uvs[], bool p_smooth, bool p_invert, const Ref &p_material, bool p_from_b) { - int indices[3]; - for (int i = 0; i < 3; i++) { - VertexKey vk; - 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(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; - } - - 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(); - materials[p_material] = face.material_idx; - } else { - face.material_idx = materials[p_material]; - } - } else { - face.material_idx = -1; - } - - for (int k = 0; k < 3; k++) { - face.points[k] = indices[k]; - face.uvs[k] = p_uvs[k]; - } - - faces.push_back(face); -} - -// CSGBrushOperation::Build2DFaces - -int CSGBrushOperation::Build2DFaces::_get_point_idx(const Vector2 &p_point) { - 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; -} - -int CSGBrushOperation::Build2DFaces::_add_vertex(const Vertex2D &p_vertex) { - // Check if vertex exists. - int vertex_id = _get_point_idx(p_vertex.point); - if (vertex_id != -1) { - return vertex_id; - } - - vertices.push_back(p_vertex); - return vertices.size() - 1; -} - -void CSGBrushOperation::Build2DFaces::_add_vertex_idx_sorted(Vector &r_vertex_indices, int p_new_vertex_index) { - 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."); - - // The first vertex. - if (r_vertex_indices.size() == 0) { - // Simply add it. - r_vertex_indices.push_back(p_new_vertex_index); - return; - } - - // The second vertex. - if (r_vertex_indices.size() == 1) { - Vector2 first_point = vertices[r_vertex_indices[0]].point; - Vector2 new_point = vertices[p_new_vertex_index].point; - - // 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 beginning 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; - } - - // 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; - } - } - - // New largest, add it to the end. - r_vertex_indices.push_back(p_new_vertex_index); - } -} - -void CSGBrushOperation::Build2DFaces::_merge_faces(const Vector &p_segment_indices) { - int segments = p_segment_indices.size() - 1; - if (segments < 2) { - return; - } - - // 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) { - int closest_idx = 0; - int inner_idx = p_segment_indices[sorted_idx]; - - 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]; - } - - // Find the mergeable faces. - Vector merge_faces_idx; - Vector merge_faces; - Vector 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); - } - } - } - - Vector 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)) { - if (!degenerate_points.find(outer_edge_idx[0])) { - degenerate_points.push_back(outer_edge_idx[0]); - } - if (!degenerate_points.find(outer_edge_idx[1])) { - degenerate_points.push_back(outer_edge_idx[1]); - } - continue; - } - - // 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); - } - - // 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; - } - - // 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; - } - } - } - } - } -} - -void CSGBrushOperation::Build2DFaces::_find_edge_intersections(const Vector2 p_segment_points[2], Vector &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; - } - } - - // 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; - } - - // 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; - } - } - } -} - -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 - }; - - // Skip degenerate triangles. - if (is_triangle_degenerate(points, vertex_snap2)) { - continue; - } - - // 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]; - } - } - - // 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; - } - - // 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; - } - - // 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; - } - } - - // 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 new_points[3] = { points[i], points[(i + 1) % 3], vertices[new_vertex_idx].point }; - if (is_triangle_degenerate(new_points, vertex_snap2)) { - continue; - } - - 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); - - // No need to check other faces. - break; - } - } - - return new_vertex_idx; -} - -void CSGBrushOperation::Build2DFaces::insert(const CSGBrush &p_brush, int p_face_idx) { - // 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. - - 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); - } - } - } - - Vector segment_indices; - Vector2 segment[2]; - int inserted_index[3] = { -1, -1, -1 }; - - // Insert points. - for (int i = 0; i < points_count; ++i) { - inserted_index[i] = _insert_point(points_2D[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 (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(); - } - } -} - -void CSGBrushOperation::Build2DFaces::addFacesToMesh(MeshMerge &r_mesh_merge, bool p_smooth, bool p_invert, const Ref &p_material, bool p_from_b) { - 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); - } - - 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. - } - } - } - } - - // 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 deleted file mode 100644 index f51e8f113..000000000 --- a/modules/csg/csg.h +++ /dev/null @@ -1,195 +0,0 @@ -/*************************************************************************/ -/* csg.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef 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/transform.h" -#include "core/math/vector2.h" -#include "core/math/vector3.h" -#include "core/oa_hash_map.h" -#include "core/pool_vector.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; - bool smooth; - bool invert; - int material; - }; - - Vector faces; - Vector> materials; - - inline void _regen_face_aabbs(); - - // Create a brush from faces. - void build_from_faces(const PoolVector &p_vertices, const PoolVector &p_uvs, const PoolVector &p_smooth, const PoolVector> &p_materials, const PoolVector &p_invert_faces); - void copy_from(const CSGBrush &p_brush, const Transform &p_xform); -}; - -struct CSGBrushOperation { - enum Operation { - OPERATION_UNION, - OPERATION_INTERSECTION, - OPERATION_SUBTRACTION, - }; - - 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 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; - int next; - Vector3 center; - AABB aabb; - }; - - 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 FaceBVHCmpY { - _FORCE_INLINE_ bool operator()(const FaceBVH *p_left, const FaceBVH *p_right) const { - return p_left->center.y < p_right->center.y; - } - }; - struct FaceBVHCmpZ { - _FORCE_INLINE_ bool operator()(const FaceBVH *p_left, const FaceBVH *p_right) const { - return p_left->center.z < p_right->center.z; - } - }; - - struct VertexKey { - int32_t x, y, z; - _FORCE_INLINE_ bool operator<(const VertexKey &p_key) const { - if (x == p_key.x) { - if (y == p_key.y) { - return z < p_key.z; - } else { - return y < p_key.y; - } - } else { - return x < p_key.x; - } - } - - _FORCE_INLINE_ bool operator==(const VertexKey &p_key) const { - return (x == p_key.x && y == p_key.y && z == p_key.z); - } - }; - - struct VertexKeyHash { - static _FORCE_INLINE_ uint32_t hash(const VertexKey &p_vk) { - uint32_t h = hash_djb2_one_32(p_vk.x); - h = hash_djb2_one_32(p_vk.y, h); - h = hash_djb2_one_32(p_vk.z, h); - return h; - } - }; - - Vector points; - Vector faces; - Map, int> materials; - Map vertex_map; - OAHashMap snap_cache; - float vertex_snap; - - inline void _add_distance(List &r_intersectionsA, List &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 &p_material, bool p_from_b); - void mark_inside_faces(); - }; - - struct Build2DFaces { - struct Vertex2D { - Vector2 point; - Vector2 uv; - }; - - struct Face2D { - int vertex_idx[3]; - }; - - Vector vertices; - Vector 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 &r_vertex_indices, int p_new_vertex_index); - inline void _merge_faces(const Vector &p_segment_indices); - inline void _find_edge_intersections(const Vector2 p_segment_points[2], Vector &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 &p_material, bool p_from_b); - - Build2DFaces() {} - Build2DFaces(const CSGBrush &p_brush, int p_brush_face, float p_vertex_snap2); - }; - - struct Build2DFaceCollection { - Map build2DFacesA; - Map build2DFacesB; - }; - - 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_gizmos.cpp b/modules/csg/csg_gizmos.cpp deleted file mode 100644 index dfae12833..000000000 --- a/modules/csg/csg_gizmos.cpp +++ /dev/null @@ -1,455 +0,0 @@ -/*************************************************************************/ -/* csg_gizmos.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "csg_gizmos.h" - -/////////// - -CSGShapeSpatialGizmoPlugin::CSGShapeSpatialGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/csg", Color(0.0, 0.4, 1, 0.15)); - create_material("shape_union_material", gizmo_color); - create_material("shape_union_solid_material", gizmo_color); - gizmo_color.invert(); - create_material("shape_subtraction_material", gizmo_color); - create_material("shape_subtraction_solid_material", gizmo_color); - gizmo_color.r = 0.95; - gizmo_color.g = 0.95; - gizmo_color.b = 0.95; - create_material("shape_intersection_material", gizmo_color); - create_material("shape_intersection_solid_material", gizmo_color); - - create_handle_material("handles"); -} - -String CSGShapeSpatialGizmoPlugin::get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_idx) const { - CSGShape *cs = Object::cast_to(p_gizmo->get_spatial_node()); - - if (Object::cast_to(cs)) { - return "Radius"; - } - - if (Object::cast_to(cs)) { - static const char *hname[3] = { "Width", "Height", "Depth" }; - return hname[p_idx]; - } - - if (Object::cast_to(cs)) { - return p_idx == 0 ? "Radius" : "Height"; - } - - if (Object::cast_to(cs)) { - return p_idx == 0 ? "InnerRadius" : "OuterRadius"; - } - - return ""; -} -Variant CSGShapeSpatialGizmoPlugin::get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const { - CSGShape *cs = Object::cast_to(p_gizmo->get_spatial_node()); - - if (Object::cast_to(cs)) { - CSGSphere *s = Object::cast_to(cs); - return s->get_radius(); - } - - if (Object::cast_to(cs)) { - CSGBox *s = Object::cast_to(cs); - switch (p_idx) { - case 0: - return s->get_width(); - case 1: - return s->get_height(); - case 2: - return s->get_depth(); - } - } - - if (Object::cast_to(cs)) { - CSGCylinder *s = Object::cast_to(cs); - return p_idx == 0 ? s->get_radius() : s->get_height(); - } - - if (Object::cast_to(cs)) { - CSGTorus *s = Object::cast_to(cs); - return p_idx == 0 ? s->get_inner_radius() : s->get_outer_radius(); - } - - return Variant(); -} -void CSGShapeSpatialGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point) { - CSGShape *cs = Object::cast_to(p_gizmo->get_spatial_node()); - - Transform gt = cs->get_global_transform(); - //gt.orthonormalize(); - Transform gi = gt.affine_inverse(); - - Vector3 ray_from = p_camera->project_ray_origin(p_point); - Vector3 ray_dir = p_camera->project_ray_normal(p_point); - - Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 16384) }; - - if (Object::cast_to(cs)) { - CSGSphere *s = Object::cast_to(cs); - - Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb); - float d = ra.x; - if (SpatialEditor::get_singleton()->is_snap_enabled()) { - d = Math::stepify(d, SpatialEditor::get_singleton()->get_translate_snap()); - } - - if (d < 0.001) { - d = 0.001; - } - - s->set_radius(d); - } - - if (Object::cast_to(cs)) { - CSGBox *s = Object::cast_to(cs); - - Vector3 axis; - axis[p_idx] = 1.0; - Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); - float d = ra[p_idx]; - - if (Math::is_nan(d)) { - // The handle is perpendicular to the camera. - return; - } - - if (SpatialEditor::get_singleton()->is_snap_enabled()) { - d = Math::stepify(d, SpatialEditor::get_singleton()->get_translate_snap()); - } - - if (d < 0.001) { - d = 0.001; - } - - switch (p_idx) { - case 0: - s->set_width(d * 2); - break; - case 1: - s->set_height(d * 2); - break; - case 2: - s->set_depth(d * 2); - break; - } - } - - if (Object::cast_to(cs)) { - CSGCylinder *s = Object::cast_to(cs); - - Vector3 axis; - axis[p_idx == 0 ? 0 : 1] = 1.0; - Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); - float d = axis.dot(ra); - if (SpatialEditor::get_singleton()->is_snap_enabled()) { - d = Math::stepify(d, SpatialEditor::get_singleton()->get_translate_snap()); - } - - if (d < 0.001) { - d = 0.001; - } - - if (p_idx == 0) { - s->set_radius(d); - } else if (p_idx == 1) { - s->set_height(d * 2.0); - } - } - - if (Object::cast_to(cs)) { - CSGTorus *s = Object::cast_to(cs); - - Vector3 axis; - axis[0] = 1.0; - Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); - float d = axis.dot(ra); - if (SpatialEditor::get_singleton()->is_snap_enabled()) { - d = Math::stepify(d, SpatialEditor::get_singleton()->get_translate_snap()); - } - - if (d < 0.001) { - d = 0.001; - } - - if (p_idx == 0) { - s->set_inner_radius(d); - } else if (p_idx == 1) { - s->set_outer_radius(d); - } - } -} -void CSGShapeSpatialGizmoPlugin::commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { - CSGShape *cs = Object::cast_to(p_gizmo->get_spatial_node()); - - if (Object::cast_to(cs)) { - CSGSphere *s = Object::cast_to(cs); - if (p_cancel) { - s->set_radius(p_restore); - return; - } - - UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change Sphere Shape Radius")); - ur->add_do_method(s, "set_radius", s->get_radius()); - ur->add_undo_method(s, "set_radius", p_restore); - ur->commit_action(); - } - - if (Object::cast_to(cs)) { - CSGBox *s = Object::cast_to(cs); - if (p_cancel) { - switch (p_idx) { - case 0: - s->set_width(p_restore); - break; - case 1: - s->set_height(p_restore); - break; - case 2: - s->set_depth(p_restore); - break; - } - return; - } - - UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change Box Shape Extents")); - static const char *method[3] = { "set_width", "set_height", "set_depth" }; - float current = 0; - switch (p_idx) { - case 0: - current = s->get_width(); - break; - case 1: - current = s->get_height(); - break; - case 2: - current = s->get_depth(); - break; - } - - ur->add_do_method(s, method[p_idx], current); - ur->add_undo_method(s, method[p_idx], p_restore); - ur->commit_action(); - } - - if (Object::cast_to(cs)) { - CSGCylinder *s = Object::cast_to(cs); - if (p_cancel) { - if (p_idx == 0) { - s->set_radius(p_restore); - } else { - s->set_height(p_restore); - } - return; - } - - UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); - if (p_idx == 0) { - ur->create_action(TTR("Change Cylinder Radius")); - ur->add_do_method(s, "set_radius", s->get_radius()); - ur->add_undo_method(s, "set_radius", p_restore); - } else { - ur->create_action(TTR("Change Cylinder Height")); - ur->add_do_method(s, "set_height", s->get_height()); - ur->add_undo_method(s, "set_height", p_restore); - } - - ur->commit_action(); - } - - if (Object::cast_to(cs)) { - CSGTorus *s = Object::cast_to(cs); - if (p_cancel) { - if (p_idx == 0) { - s->set_inner_radius(p_restore); - } else { - s->set_outer_radius(p_restore); - } - return; - } - - UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); - if (p_idx == 0) { - ur->create_action(TTR("Change Torus Inner Radius")); - ur->add_do_method(s, "set_inner_radius", s->get_inner_radius()); - ur->add_undo_method(s, "set_inner_radius", p_restore); - } else { - ur->create_action(TTR("Change Torus Outer Radius")); - ur->add_do_method(s, "set_outer_radius", s->get_outer_radius()); - ur->add_undo_method(s, "set_outer_radius", p_restore); - } - - ur->commit_action(); - } -} -bool CSGShapeSpatialGizmoPlugin::has_gizmo(Spatial *p_spatial) { - return Object::cast_to(p_spatial) || Object::cast_to(p_spatial) || Object::cast_to(p_spatial) || Object::cast_to(p_spatial) || Object::cast_to(p_spatial) || Object::cast_to(p_spatial); -} - -String CSGShapeSpatialGizmoPlugin::get_name() const { - return "CSGShapes"; -} - -int CSGShapeSpatialGizmoPlugin::get_priority() const { - return -1; -} - -bool CSGShapeSpatialGizmoPlugin::is_selectable_when_hidden() const { - return true; -} - -void CSGShapeSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { - p_gizmo->clear(); - - CSGShape *cs = Object::cast_to(p_gizmo->get_spatial_node()); - - PoolVector faces = cs->get_brush_faces(); - - if (faces.size() == 0) { - return; - } - - Vector lines; - lines.resize(faces.size() * 2); - { - PoolVector::Read r = faces.read(); - - for (int i = 0; i < lines.size(); i += 6) { - int f = i / 6; - for (int j = 0; j < 3; j++) { - int j_n = (j + 1) % 3; - lines.write[i + j * 2 + 0] = r[f * 3 + j]; - lines.write[i + j * 2 + 1] = r[f * 3 + j_n]; - } - } - } - - Ref material; - switch (cs->get_operation()) { - case CSGShape::OPERATION_UNION: - material = get_material("shape_union_material", p_gizmo); - break; - case CSGShape::OPERATION_INTERSECTION: - material = get_material("shape_intersection_material", p_gizmo); - break; - case CSGShape::OPERATION_SUBTRACTION: - material = get_material("shape_subtraction_material", p_gizmo); - break; - } - - Ref handles_material = get_material("handles"); - - p_gizmo->add_lines(lines, material); - p_gizmo->add_collision_segments(lines); - - Array csg_meshes = cs->get_meshes(); - if (csg_meshes.size() != 2) { - return; - } - - Ref csg_mesh = csg_meshes[1]; - if (csg_mesh.is_valid()) { - p_gizmo->add_collision_triangles(csg_mesh->generate_triangle_mesh()); - } - - if (p_gizmo->is_selected()) { - // Draw a translucent representation of the CSG node - Ref mesh = memnew(ArrayMesh); - Array array; - array.resize(Mesh::ARRAY_MAX); - array[Mesh::ARRAY_VERTEX] = faces; - mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, array); - - Ref solid_material; - switch (cs->get_operation()) { - case CSGShape::OPERATION_UNION: - solid_material = get_material("shape_union_solid_material", p_gizmo); - break; - case CSGShape::OPERATION_INTERSECTION: - solid_material = get_material("shape_intersection_solid_material", p_gizmo); - break; - case CSGShape::OPERATION_SUBTRACTION: - solid_material = get_material("shape_subtraction_solid_material", p_gizmo); - break; - } - - p_gizmo->add_mesh(mesh, false, Ref(), solid_material); - } - - if (Object::cast_to(cs)) { - CSGSphere *s = Object::cast_to(cs); - - float r = s->get_radius(); - Vector handles; - handles.push_back(Vector3(r, 0, 0)); - p_gizmo->add_handles(handles, handles_material); - } - - if (Object::cast_to(cs)) { - CSGBox *s = Object::cast_to(cs); - - Vector handles; - handles.push_back(Vector3(s->get_width() * 0.5, 0, 0)); - handles.push_back(Vector3(0, s->get_height() * 0.5, 0)); - handles.push_back(Vector3(0, 0, s->get_depth() * 0.5)); - p_gizmo->add_handles(handles, handles_material); - } - - if (Object::cast_to(cs)) { - CSGCylinder *s = Object::cast_to(cs); - - Vector handles; - handles.push_back(Vector3(s->get_radius(), 0, 0)); - handles.push_back(Vector3(0, s->get_height() * 0.5, 0)); - p_gizmo->add_handles(handles, handles_material); - } - - if (Object::cast_to(cs)) { - CSGTorus *s = Object::cast_to(cs); - - Vector handles; - handles.push_back(Vector3(s->get_inner_radius(), 0, 0)); - handles.push_back(Vector3(s->get_outer_radius(), 0, 0)); - p_gizmo->add_handles(handles, handles_material); - } -} - -EditorPluginCSG::EditorPluginCSG(EditorNode *p_editor) { - Ref gizmo_plugin = Ref(memnew(CSGShapeSpatialGizmoPlugin)); - SpatialEditor::get_singleton()->add_gizmo_plugin(gizmo_plugin); -} diff --git a/modules/csg/csg_gizmos.h b/modules/csg/csg_gizmos.h deleted file mode 100644 index 3b2f66a48..000000000 --- a/modules/csg/csg_gizmos.h +++ /dev/null @@ -1,63 +0,0 @@ -/*************************************************************************/ -/* csg_gizmos.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef CSG_GIZMOS_H -#define CSG_GIZMOS_H - -#include "csg_shape.h" -#include "editor/editor_plugin.h" -#include "editor/spatial_editor_gizmos.h" - -class CSGShapeSpatialGizmoPlugin : public EditorSpatialGizmoPlugin { - GDCLASS(CSGShapeSpatialGizmoPlugin, EditorSpatialGizmoPlugin); - -public: - bool has_gizmo(Spatial *p_spatial); - String get_name() const; - int get_priority() const; - bool is_selectable_when_hidden() const; - void redraw(EditorSpatialGizmo *p_gizmo); - - String get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_idx) const; - Variant get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const; - void set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point); - void commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel); - - CSGShapeSpatialGizmoPlugin(); -}; - -class EditorPluginCSG : public EditorPlugin { - GDCLASS(EditorPluginCSG, EditorPlugin); - -public: - EditorPluginCSG(EditorNode *p_editor); -}; - -#endif // CSG_GIZMOS_H diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp deleted file mode 100644 index 48275e111..000000000 --- a/modules/csg/csg_shape.cpp +++ /dev/null @@ -1,2426 +0,0 @@ -/*************************************************************************/ -/* csg_shape.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "csg_shape.h" - -void CSGShape::set_use_collision(bool p_enable) { - if (use_collision == p_enable) { - return; - } - - use_collision = p_enable; - - if (!is_inside_tree() || !is_root_shape()) { - return; - } - - if (use_collision) { - root_collision_shape.instance(); - root_collision_instance = RID_PRIME(PhysicsServer::get_singleton()->body_create(PhysicsServer::BODY_MODE_STATIC)); - PhysicsServer::get_singleton()->body_set_state(root_collision_instance, PhysicsServer::BODY_STATE_TRANSFORM, get_global_transform()); - PhysicsServer::get_singleton()->body_add_shape(root_collision_instance, root_collision_shape->get_rid()); - PhysicsServer::get_singleton()->body_set_space(root_collision_instance, get_world()->get_space()); - PhysicsServer::get_singleton()->body_attach_object_instance_id(root_collision_instance, get_instance_id()); - set_collision_layer(collision_layer); - set_collision_mask(collision_mask); - _make_dirty(); //force update - } else { - PhysicsServer::get_singleton()->free(root_collision_instance); - root_collision_instance = RID(); - root_collision_shape.unref(); - } - _change_notify(); -} - -bool CSGShape::is_using_collision() const { - return use_collision; -} - -void CSGShape::set_collision_layer(uint32_t p_layer) { - collision_layer = p_layer; - if (root_collision_instance.is_valid()) { - PhysicsServer::get_singleton()->body_set_collision_layer(root_collision_instance, p_layer); - } -} - -uint32_t CSGShape::get_collision_layer() const { - return collision_layer; -} - -void CSGShape::set_collision_mask(uint32_t p_mask) { - collision_mask = p_mask; - if (root_collision_instance.is_valid()) { - PhysicsServer::get_singleton()->body_set_collision_mask(root_collision_instance, p_mask); - } -} - -uint32_t CSGShape::get_collision_mask() const { - return collision_mask; -} - -void CSGShape::set_collision_mask_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); - uint32_t mask = get_collision_mask(); - if (p_value) { - mask |= 1 << p_bit; - } else { - mask &= ~(1 << p_bit); - } - set_collision_mask(mask); -} - -bool CSGShape::get_collision_mask_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); - return get_collision_mask() & (1 << p_bit); -} - -void CSGShape::set_collision_layer_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision layer bit must be between 0 and 31 inclusive."); - uint32_t layer = get_collision_layer(); - if (p_value) { - layer |= 1 << p_bit; - } else { - layer &= ~(1 << p_bit); - } - set_collision_layer(layer); -} - -bool CSGShape::get_collision_layer_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision layer bit must be between 0 and 31 inclusive."); - return get_collision_layer() & (1 << p_bit); -} - -bool CSGShape::is_root_shape() const { - return !parent_shape; -} - -void CSGShape::set_snap(float p_snap) { - snap = p_snap; -} - -float CSGShape::get_snap() const { - return snap; -} - -void CSGShape::_make_dirty(bool p_parent_removing) { - if ((p_parent_removing || is_root_shape()) && !dirty) { - call_deferred("_update_shape"); // Must be deferred; otherwise, is_root_shape() will use the previous parent - } - - if (!is_root_shape()) { - parent_shape->_make_dirty(); - } else if (!dirty) { - call_deferred("_update_shape"); - } - - dirty = true; -} - -CSGBrush *CSGShape::_get_brush() { - if (dirty) { - if (brush) { - memdelete(brush); - } - brush = nullptr; - - CSGBrush *n = _build_brush(); - - for (int i = 0; i < get_child_count(); i++) { - CSGShape *child = Object::cast_to(get_child(i)); - if (!child) { - continue; - } - if (!child->is_visible()) { - continue; - } - - CSGBrush *n2 = child->_get_brush(); - if (!n2) { - continue; - } - if (!n) { - n = memnew(CSGBrush); - - n->copy_from(*n2, child->get_transform()); - - } else { - CSGBrush *nn = memnew(CSGBrush); - CSGBrush *nn2 = memnew(CSGBrush); - nn2->copy_from(*n2, child->get_transform()); - - CSGBrushOperation bop; - - switch (child->get_operation()) { - case CSGShape::OPERATION_UNION: - bop.merge_brushes(CSGBrushOperation::OPERATION_UNION, *n, *nn2, *nn, snap); - break; - case CSGShape::OPERATION_INTERSECTION: - bop.merge_brushes(CSGBrushOperation::OPERATION_INTERSECTION, *n, *nn2, *nn, snap); - break; - case CSGShape::OPERATION_SUBTRACTION: - bop.merge_brushes(CSGBrushOperation::OPERATION_SUBTRACTION, *n, *nn2, *nn, snap); - break; - } - memdelete(n); - memdelete(nn2); - n = nn; - } - } - - if (n) { - AABB aabb; - for (int i = 0; i < n->faces.size(); i++) { - for (int j = 0; j < 3; j++) { - if (i == 0 && j == 0) { - aabb.position = n->faces[i].vertices[j]; - } else { - aabb.expand_to(n->faces[i].vertices[j]); - } - } - } - node_aabb = aabb; - } else { - node_aabb = AABB(); - } - - brush = n; - - dirty = false; - } - - return brush; -} - -int CSGShape::mikktGetNumFaces(const SMikkTSpaceContext *pContext) { - ShapeUpdateSurface &surface = *((ShapeUpdateSurface *)pContext->m_pUserData); - - return surface.vertices.size() / 3; -} - -int CSGShape::mikktGetNumVerticesOfFace(const SMikkTSpaceContext *pContext, const int iFace) { - // always 3 - return 3; -} - -void CSGShape::mikktGetPosition(const SMikkTSpaceContext *pContext, float fvPosOut[], const int iFace, const int iVert) { - ShapeUpdateSurface &surface = *((ShapeUpdateSurface *)pContext->m_pUserData); - - Vector3 v = surface.verticesw[iFace * 3 + iVert]; - fvPosOut[0] = v.x; - fvPosOut[1] = v.y; - fvPosOut[2] = v.z; -} - -void CSGShape::mikktGetNormal(const SMikkTSpaceContext *pContext, float fvNormOut[], const int iFace, const int iVert) { - ShapeUpdateSurface &surface = *((ShapeUpdateSurface *)pContext->m_pUserData); - - Vector3 n = surface.normalsw[iFace * 3 + iVert]; - fvNormOut[0] = n.x; - fvNormOut[1] = n.y; - fvNormOut[2] = n.z; -} - -void CSGShape::mikktGetTexCoord(const SMikkTSpaceContext *pContext, float fvTexcOut[], const int iFace, const int iVert) { - ShapeUpdateSurface &surface = *((ShapeUpdateSurface *)pContext->m_pUserData); - - Vector2 t = surface.uvsw[iFace * 3 + iVert]; - fvTexcOut[0] = t.x; - fvTexcOut[1] = t.y; -} - -void CSGShape::mikktSetTSpaceDefault(const SMikkTSpaceContext *pContext, const float fvTangent[], const float fvBiTangent[], const float fMagS, const float fMagT, - const tbool bIsOrientationPreserving, const int iFace, const int iVert) { - ShapeUpdateSurface &surface = *((ShapeUpdateSurface *)pContext->m_pUserData); - - int i = iFace * 3 + iVert; - Vector3 normal = surface.normalsw[i]; - Vector3 tangent = Vector3(fvTangent[0], fvTangent[1], fvTangent[2]); - Vector3 bitangent = Vector3(-fvBiTangent[0], -fvBiTangent[1], -fvBiTangent[2]); // for some reason these are reversed, something with the coordinate system in Godot - float d = bitangent.dot(normal.cross(tangent)); - - i *= 4; - surface.tansw[i++] = tangent.x; - surface.tansw[i++] = tangent.y; - surface.tansw[i++] = tangent.z; - surface.tansw[i++] = d < 0 ? -1 : 1; -} - -void CSGShape::_update_shape() { - if (!is_root_shape()) { - return; - } - - set_base(RID()); - root_mesh.unref(); //byebye root mesh - - CSGBrush *n = _get_brush(); - ERR_FAIL_COND_MSG(!n, "Cannot get CSGBrush."); - - OAHashMap vec_map; - - Vector face_count; - face_count.resize(n->materials.size() + 1); - for (int i = 0; i < face_count.size(); i++) { - face_count.write[i] = 0; - } - - for (int i = 0; i < n->faces.size(); i++) { - int mat = n->faces[i].material; - ERR_CONTINUE(mat < -1 || mat >= face_count.size()); - int idx = mat == -1 ? face_count.size() - 1 : mat; - - if (n->faces[i].smooth) { - Plane p(n->faces[i].vertices[0], n->faces[i].vertices[1], n->faces[i].vertices[2]); - - for (int j = 0; j < 3; j++) { - Vector3 v = n->faces[i].vertices[j]; - Vector3 add; - if (vec_map.lookup(v, add)) { - add += p.normal; - } else { - add = p.normal; - } - vec_map.set(v, add); - } - } - - face_count.write[idx]++; - } - - Vector surfaces; - - surfaces.resize(face_count.size()); - - //create arrays - for (int i = 0; i < surfaces.size(); i++) { - surfaces.write[i].vertices.resize(face_count[i] * 3); - surfaces.write[i].normals.resize(face_count[i] * 3); - surfaces.write[i].uvs.resize(face_count[i] * 3); - if (calculate_tangents) { - surfaces.write[i].tans.resize(face_count[i] * 3 * 4); - } - surfaces.write[i].last_added = 0; - - if (i != surfaces.size() - 1) { - surfaces.write[i].material = n->materials[i]; - } - - surfaces.write[i].verticesw = surfaces.write[i].vertices.write(); - surfaces.write[i].normalsw = surfaces.write[i].normals.write(); - surfaces.write[i].uvsw = surfaces.write[i].uvs.write(); - if (calculate_tangents) { - surfaces.write[i].tansw = surfaces.write[i].tans.write(); - } - } - - //fill arrays - { - for (int i = 0; i < n->faces.size(); i++) { - int order[3] = { 0, 1, 2 }; - - if (n->faces[i].invert) { - SWAP(order[1], order[2]); - } - - int mat = n->faces[i].material; - ERR_CONTINUE(mat < -1 || mat >= face_count.size()); - int idx = mat == -1 ? face_count.size() - 1 : mat; - - int last = surfaces[idx].last_added; - - Plane p(n->faces[i].vertices[0], n->faces[i].vertices[1], n->faces[i].vertices[2]); - - for (int j = 0; j < 3; j++) { - Vector3 v = n->faces[i].vertices[j]; - - Vector3 normal = p.normal; - - if (n->faces[i].smooth && vec_map.lookup(v, normal)) { - normal.normalize(); - } - - if (n->faces[i].invert) { - normal = -normal; - } - - int k = last + order[j]; - surfaces[idx].verticesw[k] = v; - surfaces[idx].uvsw[k] = n->faces[i].uvs[j]; - surfaces[idx].normalsw[k] = normal; - - if (calculate_tangents) { - // zero out our tangents for now - k *= 4; - surfaces[idx].tansw[k++] = 0.0; - surfaces[idx].tansw[k++] = 0.0; - surfaces[idx].tansw[k++] = 0.0; - surfaces[idx].tansw[k++] = 0.0; - } - } - - surfaces.write[idx].last_added += 3; - } - } - - root_mesh.instance(); - //create surfaces - - for (int i = 0; i < surfaces.size(); i++) { - // calculate tangents for this surface - bool have_tangents = calculate_tangents; - if (have_tangents) { - SMikkTSpaceInterface mkif; - mkif.m_getNormal = mikktGetNormal; - mkif.m_getNumFaces = mikktGetNumFaces; - mkif.m_getNumVerticesOfFace = mikktGetNumVerticesOfFace; - mkif.m_getPosition = mikktGetPosition; - mkif.m_getTexCoord = mikktGetTexCoord; - mkif.m_setTSpace = mikktSetTSpaceDefault; - mkif.m_setTSpaceBasic = nullptr; - - SMikkTSpaceContext msc; - msc.m_pInterface = &mkif; - msc.m_pUserData = &surfaces.write[i]; - have_tangents = genTangSpaceDefault(&msc); - } - - // unset write access - surfaces.write[i].verticesw.release(); - surfaces.write[i].normalsw.release(); - surfaces.write[i].uvsw.release(); - surfaces.write[i].tansw.release(); - - if (surfaces[i].last_added == 0) { - continue; - } - - // and convert to surface array - Array array; - array.resize(Mesh::ARRAY_MAX); - - array[Mesh::ARRAY_VERTEX] = surfaces[i].vertices; - array[Mesh::ARRAY_NORMAL] = surfaces[i].normals; - array[Mesh::ARRAY_TEX_UV] = surfaces[i].uvs; - if (have_tangents) { - array[Mesh::ARRAY_TANGENT] = surfaces[i].tans; - } - - int idx = root_mesh->get_surface_count(); - root_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, array); - root_mesh->surface_set_material(idx, surfaces[i].material); - } - - set_base(root_mesh->get_rid()); - - _update_collision_faces(); -} - -void CSGShape::_update_collision_faces() { - if (use_collision && is_root_shape() && root_collision_shape.is_valid()) { - CSGBrush *n = _get_brush(); - ERR_FAIL_COND_MSG(!n, "Cannot get CSGBrush."); - PoolVector physics_faces; - physics_faces.resize(n->faces.size() * 3); - PoolVector::Write physicsw = physics_faces.write(); - - for (int i = 0; i < n->faces.size(); i++) { - int order[3] = { 0, 1, 2 }; - - if (n->faces[i].invert) { - SWAP(order[1], order[2]); - } - - physicsw[i * 3 + 0] = n->faces[i].vertices[order[0]]; - physicsw[i * 3 + 1] = n->faces[i].vertices[order[1]]; - physicsw[i * 3 + 2] = n->faces[i].vertices[order[2]]; - } - - root_collision_shape->set_faces(physics_faces); - } -} - -AABB CSGShape::get_aabb() const { - return node_aabb; -} - -PoolVector CSGShape::get_brush_faces() { - ERR_FAIL_COND_V(!is_inside_tree(), PoolVector()); - CSGBrush *b = _get_brush(); - if (!b) { - return PoolVector(); - } - - PoolVector faces; - int fc = b->faces.size(); - faces.resize(fc * 3); - { - PoolVector::Write w = faces.write(); - for (int i = 0; i < fc; i++) { - w[i * 3 + 0] = b->faces[i].vertices[0]; - w[i * 3 + 1] = b->faces[i].vertices[1]; - w[i * 3 + 2] = b->faces[i].vertices[2]; - } - } - - return faces; -} - -PoolVector CSGShape::get_faces(uint32_t p_usage_flags) const { - return PoolVector(); -} - -void CSGShape::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_PARENTED: { - Node *parentn = get_parent(); - if (parentn) { - parent_shape = Object::cast_to(parentn); - if (parent_shape) { - set_base(RID()); - root_mesh.unref(); - } - } - if (!brush || parent_shape) { - // Update this node if uninitialized, or both this node and its new parent if it gets added to another CSG shape - _make_dirty(); - } - last_visible = is_visible(); - } break; - - case NOTIFICATION_UNPARENTED: { - if (!is_root_shape()) { - // Update this node and its previous parent only if it's currently being removed from another CSG shape - _make_dirty(true); // Must be forced since is_root_shape() uses the previous parent - } - parent_shape = nullptr; - } break; - - case NOTIFICATION_VISIBILITY_CHANGED: { - if (!is_root_shape() && last_visible != is_visible()) { - // Update this node's parent only if its own visibility has changed, not the visibility of parent nodes - parent_shape->_make_dirty(); - } - last_visible = is_visible(); - } break; - - case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { - if (!is_root_shape()) { - // Update this node's parent only if its own transformation has changed, not the transformation of parent nodes - parent_shape->_make_dirty(); - } - } break; - - case NOTIFICATION_ENTER_TREE: { - if (use_collision && is_root_shape()) { - root_collision_shape.instance(); - root_collision_instance = PhysicsServer::get_singleton()->body_create(); - PhysicsServer::get_singleton()->body_set_mode(root_collision_instance, PhysicsServer::BODY_MODE_STATIC); - PhysicsServer::get_singleton()->body_set_state(root_collision_instance, PhysicsServer::BODY_STATE_TRANSFORM, get_global_transform()); - PhysicsServer::get_singleton()->body_add_shape(root_collision_instance, root_collision_shape->get_rid()); - PhysicsServer::get_singleton()->body_set_space(root_collision_instance, get_world()->get_space()); - PhysicsServer::get_singleton()->body_attach_object_instance_id(root_collision_instance, get_instance_id()); - set_collision_layer(collision_layer); - set_collision_mask(collision_mask); - _update_collision_faces(); - } - } break; - - case NOTIFICATION_EXIT_TREE: { - if (use_collision && is_root_shape() && root_collision_instance.is_valid()) { - PhysicsServer::get_singleton()->free(root_collision_instance); - root_collision_instance = RID(); - root_collision_shape.unref(); - } - } break; - - case NOTIFICATION_TRANSFORM_CHANGED: { - if (use_collision && is_root_shape() && root_collision_instance.is_valid()) { - PhysicsServer::get_singleton()->body_set_state(root_collision_instance, PhysicsServer::BODY_STATE_TRANSFORM, get_global_transform()); - } - } break; - } -} - -void CSGShape::set_operation(Operation p_operation) { - operation = p_operation; - _make_dirty(); - update_gizmo(); -} - -CSGShape::Operation CSGShape::get_operation() const { - return operation; -} - -void CSGShape::set_calculate_tangents(bool p_calculate_tangents) { - calculate_tangents = p_calculate_tangents; - _make_dirty(); -} - -bool CSGShape::is_calculating_tangents() const { - return calculate_tangents; -} - -void CSGShape::_validate_property(PropertyInfo &property) const { - bool is_collision_prefixed = property.name.begins_with("collision_"); - if ((is_collision_prefixed || property.name.begins_with("use_collision")) && is_inside_tree() && !is_root_shape()) { - //hide collision if not root - property.usage = PROPERTY_USAGE_NOEDITOR; - } else if (is_collision_prefixed && !bool(get("use_collision"))) { - property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; - } -} - -// Calling _make_dirty() normally calls a deferred _update_shape. -// This is problematic if we need to read the geometry immediately. -// This function provides a means to make sure the shape is updated -// immediately. It should only be used where necessary to prevent -// updating CSGs multiple times per frame. Use _make_dirty in preference. -void CSGShape::force_update_shape() { - if (dirty) { - _update_shape(); - } -} - -Array CSGShape::get_meshes() const { - if (root_mesh.is_valid()) { - Array arr; - arr.resize(2); - arr[0] = Transform(); - arr[1] = root_mesh; - return arr; - } - - return Array(); -} -void CSGShape::_bind_methods() { - ClassDB::bind_method(D_METHOD("_update_shape"), &CSGShape::_update_shape); - ClassDB::bind_method(D_METHOD("is_root_shape"), &CSGShape::is_root_shape); - - ClassDB::bind_method(D_METHOD("set_operation", "operation"), &CSGShape::set_operation); - ClassDB::bind_method(D_METHOD("get_operation"), &CSGShape::get_operation); - - ClassDB::bind_method(D_METHOD("set_snap", "snap"), &CSGShape::set_snap); - ClassDB::bind_method(D_METHOD("get_snap"), &CSGShape::get_snap); - - ClassDB::bind_method(D_METHOD("set_use_collision", "operation"), &CSGShape::set_use_collision); - ClassDB::bind_method(D_METHOD("is_using_collision"), &CSGShape::is_using_collision); - - ClassDB::bind_method(D_METHOD("set_collision_layer", "layer"), &CSGShape::set_collision_layer); - ClassDB::bind_method(D_METHOD("get_collision_layer"), &CSGShape::get_collision_layer); - - ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &CSGShape::set_collision_mask); - ClassDB::bind_method(D_METHOD("get_collision_mask"), &CSGShape::get_collision_mask); - - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &CSGShape::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &CSGShape::get_collision_mask_bit); - - ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &CSGShape::set_collision_layer_bit); - ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &CSGShape::get_collision_layer_bit); - - ClassDB::bind_method(D_METHOD("set_calculate_tangents", "enabled"), &CSGShape::set_calculate_tangents); - ClassDB::bind_method(D_METHOD("is_calculating_tangents"), &CSGShape::is_calculating_tangents); - - ClassDB::bind_method(D_METHOD("get_meshes"), &CSGShape::get_meshes); - - ADD_PROPERTY(PropertyInfo(Variant::INT, "operation", PROPERTY_HINT_ENUM, "Union,Intersection,Subtraction"), "set_operation", "get_operation"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "snap", PROPERTY_HINT_RANGE, "0.0001,1,0.001"), "set_snap", "get_snap"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "calculate_tangents"), "set_calculate_tangents", "is_calculating_tangents"); - - ADD_GROUP("Collision", "collision_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_collision"), "set_use_collision", "is_using_collision"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); - - BIND_ENUM_CONSTANT(OPERATION_UNION); - BIND_ENUM_CONSTANT(OPERATION_INTERSECTION); - BIND_ENUM_CONSTANT(OPERATION_SUBTRACTION); -} - -CSGShape::CSGShape() { - operation = OPERATION_UNION; - parent_shape = nullptr; - brush = nullptr; - dirty = false; - snap = 0.001; - use_collision = false; - collision_layer = 1; - collision_mask = 1; - calculate_tangents = true; - set_notify_local_transform(true); -} - -CSGShape::~CSGShape() { - if (brush) { - memdelete(brush); - brush = nullptr; - } -} -////////////////////////////////// - -CSGBrush *CSGCombiner::_build_brush() { - return memnew(CSGBrush); //does not build anything -} - -CSGCombiner::CSGCombiner() { -} - -///////////////////// - -CSGBrush *CSGPrimitive::_create_brush_from_arrays(const PoolVector &p_vertices, const PoolVector &p_uv, const PoolVector &p_smooth, const PoolVector> &p_materials) { - CSGBrush *brush = memnew(CSGBrush); - - PoolVector invert; - invert.resize(p_vertices.size() / 3); - { - int ic = invert.size(); - PoolVector::Write w = invert.write(); - for (int i = 0; i < ic; i++) { - w[i] = invert_faces; - } - } - brush->build_from_faces(p_vertices, p_uv, p_smooth, p_materials, invert); - - return brush; -} - -void CSGPrimitive::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_invert_faces", "invert_faces"), &CSGPrimitive::set_invert_faces); - ClassDB::bind_method(D_METHOD("is_inverting_faces"), &CSGPrimitive::is_inverting_faces); - - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "invert_faces"), "set_invert_faces", "is_inverting_faces"); -} - -void CSGPrimitive::set_invert_faces(bool p_invert) { - if (invert_faces == p_invert) { - return; - } - - invert_faces = p_invert; - - _make_dirty(); -} - -bool CSGPrimitive::is_inverting_faces() { - return invert_faces; -} - -CSGPrimitive::CSGPrimitive() { - invert_faces = false; -} - -///////////////////// - -CSGBrush *CSGMesh::_build_brush() { - if (!mesh.is_valid()) { - return memnew(CSGBrush); - } - - PoolVector vertices; - PoolVector smooth; - PoolVector> materials; - PoolVector uvs; - Ref material = get_material(); - - for (int i = 0; i < mesh->get_surface_count(); i++) { - if (mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) { - continue; - } - - Array arrays = mesh->surface_get_arrays(i); - - if (arrays.size() == 0) { - _make_dirty(); - ERR_FAIL_COND_V(arrays.size() == 0, memnew(CSGBrush)); - } - - PoolVector avertices = arrays[Mesh::ARRAY_VERTEX]; - if (avertices.size() == 0) { - continue; - } - - PoolVector::Read vr = avertices.read(); - - PoolVector anormals = arrays[Mesh::ARRAY_NORMAL]; - PoolVector::Read nr; - bool nr_used = false; - if (anormals.size()) { - nr = anormals.read(); - nr_used = true; - } - - PoolVector auvs = arrays[Mesh::ARRAY_TEX_UV]; - PoolVector::Read uvr; - bool uvr_used = false; - if (auvs.size()) { - uvr = auvs.read(); - uvr_used = true; - } - - Ref mat; - if (material.is_valid()) { - mat = material; - } else { - mat = mesh->surface_get_material(i); - } - - PoolVector aindices = arrays[Mesh::ARRAY_INDEX]; - if (aindices.size()) { - int as = vertices.size(); - int is = aindices.size(); - - vertices.resize(as + is); - smooth.resize((as + is) / 3); - materials.resize((as + is) / 3); - uvs.resize(as + is); - - PoolVector::Write vw = vertices.write(); - PoolVector::Write sw = smooth.write(); - PoolVector::Write uvw = uvs.write(); - PoolVector>::Write mw = materials.write(); - - PoolVector::Read ir = aindices.read(); - - for (int j = 0; j < is; j += 3) { - Vector3 vertex[3]; - Vector3 normal[3]; - Vector2 uv[3]; - - for (int k = 0; k < 3; k++) { - int idx = ir[j + k]; - vertex[k] = vr[idx]; - if (nr_used) { - normal[k] = nr[idx]; - } - if (uvr_used) { - uv[k] = uvr[idx]; - } - } - - bool flat = normal[0].distance_to(normal[1]) < CMP_EPSILON && normal[0].distance_to(normal[2]) < CMP_EPSILON; - - vw[as + j + 0] = vertex[0]; - vw[as + j + 1] = vertex[1]; - vw[as + j + 2] = vertex[2]; - - uvw[as + j + 0] = uv[0]; - uvw[as + j + 1] = uv[1]; - uvw[as + j + 2] = uv[2]; - - sw[(as + j) / 3] = !flat; - mw[(as + j) / 3] = mat; - } - } else { - int as = vertices.size(); - int is = avertices.size(); - - vertices.resize(as + is); - smooth.resize((as + is) / 3); - uvs.resize(as + is); - materials.resize((as + is) / 3); - - PoolVector::Write vw = vertices.write(); - PoolVector::Write sw = smooth.write(); - PoolVector::Write uvw = uvs.write(); - PoolVector>::Write mw = materials.write(); - - for (int j = 0; j < is; j += 3) { - Vector3 vertex[3]; - Vector3 normal[3]; - Vector2 uv[3]; - - for (int k = 0; k < 3; k++) { - vertex[k] = vr[j + k]; - if (nr_used) { - normal[k] = nr[j + k]; - } - if (uvr_used) { - uv[k] = uvr[j + k]; - } - } - - bool flat = normal[0].distance_to(normal[1]) < CMP_EPSILON && normal[0].distance_to(normal[2]) < CMP_EPSILON; - - vw[as + j + 0] = vertex[0]; - vw[as + j + 1] = vertex[1]; - vw[as + j + 2] = vertex[2]; - - uvw[as + j + 0] = uv[0]; - uvw[as + j + 1] = uv[1]; - uvw[as + j + 2] = uv[2]; - - sw[(as + j) / 3] = !flat; - mw[(as + j) / 3] = mat; - } - } - } - - if (vertices.size() == 0) { - return memnew(CSGBrush); - } - - return _create_brush_from_arrays(vertices, uvs, smooth, materials); -} - -void CSGMesh::_mesh_changed() { - _make_dirty(); - update_gizmo(); -} - -void CSGMesh::set_material(const Ref &p_material) { - if (material == p_material) { - return; - } - material = p_material; - _make_dirty(); -} - -Ref CSGMesh::get_material() const { - return material; -} - -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); - - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material"); -} - -void CSGMesh::set_mesh(const Ref &p_mesh) { - if (mesh == p_mesh) { - return; - } - if (mesh.is_valid()) { - mesh->disconnect("changed", this, "_mesh_changed"); - } - mesh = p_mesh; - - if (mesh.is_valid()) { - mesh->connect("changed", this, "_mesh_changed"); - } - - _mesh_changed(); -} - -Ref CSGMesh::get_mesh() { - return mesh; -} - -//////////////////////////////// - -CSGBrush *CSGSphere::_build_brush() { - // set our bounding box - - CSGBrush *brush = memnew(CSGBrush); - - int face_count = rings * radial_segments * 2 - radial_segments * 2; - - bool invert_val = is_inverting_faces(); - Ref material = get_material(); - - PoolVector faces; - PoolVector uvs; - PoolVector smooth; - PoolVector> materials; - PoolVector invert; - - faces.resize(face_count * 3); - uvs.resize(face_count * 3); - - smooth.resize(face_count); - materials.resize(face_count); - invert.resize(face_count); - - { - PoolVector::Write facesw = faces.write(); - PoolVector::Write uvsw = uvs.write(); - PoolVector::Write smoothw = smooth.write(); - PoolVector>::Write materialsw = materials.write(); - PoolVector::Write invertw = invert.write(); - - // We want to follow an order that's convenient for UVs. - // For latitude step we start at the top and move down like in an image. - const double latitude_step = -Math_PI / rings; - const double longitude_step = Math_TAU / radial_segments; - int face = 0; - for (int i = 0; i < rings; i++) { - double latitude0 = latitude_step * i + Math_TAU / 4; - double cos0 = Math::cos(latitude0); - double sin0 = Math::sin(latitude0); - double v0 = double(i) / rings; - - double latitude1 = latitude_step * (i + 1) + Math_TAU / 4; - double cos1 = Math::cos(latitude1); - double sin1 = Math::sin(latitude1); - double v1 = double(i + 1) / rings; - - for (int j = 0; j < radial_segments; j++) { - double longitude0 = longitude_step * j; - // We give sin to X and cos to Z on purpose. - // This allows UVs to be CCW on +X so it maps to images well. - double x0 = Math::sin(longitude0); - double z0 = Math::cos(longitude0); - double u0 = double(j) / radial_segments; - - double longitude1 = longitude_step * (j + 1); - if (j == radial_segments - 1) { - longitude1 = 0; - } - - double x1 = Math::sin(longitude1); - double z1 = Math::cos(longitude1); - double u1 = double(j + 1) / radial_segments; - - Vector3 v[4] = { - Vector3(x0 * cos0, sin0, z0 * cos0) * radius, - Vector3(x1 * cos0, sin0, z1 * cos0) * radius, - Vector3(x1 * cos1, sin1, z1 * cos1) * radius, - Vector3(x0 * cos1, sin1, z0 * cos1) * radius, - }; - - Vector2 u[4] = { - Vector2(u0, v0), - Vector2(u1, v0), - Vector2(u1, v1), - Vector2(u0, v1), - }; - - // Draw the first face, but skip this at the north pole (i == 0). - if (i > 0) { - facesw[face * 3 + 0] = v[0]; - facesw[face * 3 + 1] = v[1]; - facesw[face * 3 + 2] = v[2]; - - uvsw[face * 3 + 0] = u[0]; - uvsw[face * 3 + 1] = u[1]; - uvsw[face * 3 + 2] = u[2]; - - smoothw[face] = smooth_faces; - invertw[face] = invert_val; - materialsw[face] = material; - - face++; - } - - // Draw the second face, but skip this at the south pole (i == rings - 1). - if (i < rings - 1) { - facesw[face * 3 + 0] = v[2]; - facesw[face * 3 + 1] = v[3]; - facesw[face * 3 + 2] = v[0]; - - uvsw[face * 3 + 0] = u[2]; - uvsw[face * 3 + 1] = u[3]; - uvsw[face * 3 + 2] = u[0]; - - smoothw[face] = smooth_faces; - invertw[face] = invert_val; - materialsw[face] = material; - - face++; - } - } - } - - if (face != face_count) { - ERR_PRINT("Face mismatch bug! fix code"); - } - } - - brush->build_from_faces(faces, uvs, smooth, materials, invert); - - return brush; -} - -void CSGSphere::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_radius", "radius"), &CSGSphere::set_radius); - ClassDB::bind_method(D_METHOD("get_radius"), &CSGSphere::get_radius); - - ClassDB::bind_method(D_METHOD("set_radial_segments", "radial_segments"), &CSGSphere::set_radial_segments); - ClassDB::bind_method(D_METHOD("get_radial_segments"), &CSGSphere::get_radial_segments); - ClassDB::bind_method(D_METHOD("set_rings", "rings"), &CSGSphere::set_rings); - ClassDB::bind_method(D_METHOD("get_rings"), &CSGSphere::get_rings); - - ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGSphere::set_smooth_faces); - ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGSphere::get_smooth_faces); - - ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGSphere::set_material); - ClassDB::bind_method(D_METHOD("get_material"), &CSGSphere::get_material); - - ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001"), "set_radius", "get_radius"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1"), "set_radial_segments", "get_radial_segments"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1"), "set_rings", "get_rings"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material"); -} - -void CSGSphere::set_radius(const float p_radius) { - ERR_FAIL_COND(p_radius <= 0); - radius = p_radius; - _make_dirty(); - update_gizmo(); - _change_notify("radius"); -} - -float CSGSphere::get_radius() const { - return radius; -} - -void CSGSphere::set_radial_segments(const int p_radial_segments) { - radial_segments = p_radial_segments > 4 ? p_radial_segments : 4; - _make_dirty(); - update_gizmo(); -} - -int CSGSphere::get_radial_segments() const { - return radial_segments; -} - -void CSGSphere::set_rings(const int p_rings) { - rings = p_rings > 1 ? p_rings : 1; - _make_dirty(); - update_gizmo(); -} - -int CSGSphere::get_rings() const { - return rings; -} - -void CSGSphere::set_smooth_faces(const bool p_smooth_faces) { - smooth_faces = p_smooth_faces; - _make_dirty(); -} - -bool CSGSphere::get_smooth_faces() const { - return smooth_faces; -} - -void CSGSphere::set_material(const Ref &p_material) { - material = p_material; - _make_dirty(); -} - -Ref CSGSphere::get_material() const { - return material; -} - -CSGSphere::CSGSphere() { - // defaults - radius = 1.0; - radial_segments = 12; - rings = 6; - smooth_faces = true; -} - -/////////////// - -CSGBrush *CSGBox::_build_brush() { - // set our bounding box - - CSGBrush *brush = memnew(CSGBrush); - - int face_count = 12; //it's a cube.. - - bool invert_val = is_inverting_faces(); - Ref material = get_material(); - - PoolVector faces; - PoolVector uvs; - PoolVector smooth; - PoolVector> materials; - PoolVector invert; - - faces.resize(face_count * 3); - uvs.resize(face_count * 3); - - smooth.resize(face_count); - materials.resize(face_count); - invert.resize(face_count); - - { - PoolVector::Write facesw = faces.write(); - PoolVector::Write uvsw = uvs.write(); - PoolVector::Write smoothw = smooth.write(); - PoolVector>::Write materialsw = materials.write(); - PoolVector::Write invertw = invert.write(); - - int face = 0; - - Vector3 vertex_mul(width * 0.5, height * 0.5, depth * 0.5); - - { - for (int i = 0; i < 6; i++) { - Vector3 face_points[4]; - float uv_points[8] = { 0, 0, 0, 1, 1, 1, 1, 0 }; - - for (int j = 0; j < 4; j++) { - float v[3]; - v[0] = 1.0; - v[1] = 1 - 2 * ((j >> 1) & 1); - v[2] = v[1] * (1 - 2 * (j & 1)); - - for (int k = 0; k < 3; k++) { - if (i < 3) { - face_points[j][(i + k) % 3] = v[k]; - } else { - face_points[3 - j][(i + k) % 3] = -v[k]; - } - } - } - - Vector2 u[4]; - for (int j = 0; j < 4; j++) { - u[j] = Vector2(uv_points[j * 2 + 0], uv_points[j * 2 + 1]); - } - - //face 1 - facesw[face * 3 + 0] = face_points[0] * vertex_mul; - facesw[face * 3 + 1] = face_points[1] * vertex_mul; - facesw[face * 3 + 2] = face_points[2] * vertex_mul; - - uvsw[face * 3 + 0] = u[0]; - uvsw[face * 3 + 1] = u[1]; - uvsw[face * 3 + 2] = u[2]; - - smoothw[face] = false; - invertw[face] = invert_val; - materialsw[face] = material; - - face++; - //face 1 - facesw[face * 3 + 0] = face_points[2] * vertex_mul; - facesw[face * 3 + 1] = face_points[3] * vertex_mul; - facesw[face * 3 + 2] = face_points[0] * vertex_mul; - - uvsw[face * 3 + 0] = u[2]; - uvsw[face * 3 + 1] = u[3]; - uvsw[face * 3 + 2] = u[0]; - - smoothw[face] = false; - invertw[face] = invert_val; - materialsw[face] = material; - - face++; - } - } - - if (face != face_count) { - ERR_PRINT("Face mismatch bug! fix code"); - } - } - - brush->build_from_faces(faces, uvs, smooth, materials, invert); - - return brush; -} - -void CSGBox::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_width", "width"), &CSGBox::set_width); - ClassDB::bind_method(D_METHOD("get_width"), &CSGBox::get_width); - - ClassDB::bind_method(D_METHOD("set_height", "height"), &CSGBox::set_height); - ClassDB::bind_method(D_METHOD("get_height"), &CSGBox::get_height); - - ClassDB::bind_method(D_METHOD("set_depth", "depth"), &CSGBox::set_depth); - ClassDB::bind_method(D_METHOD("get_depth"), &CSGBox::get_depth); - - ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGBox::set_material); - ClassDB::bind_method(D_METHOD("get_material"), &CSGBox::get_material); - - ADD_PROPERTY(PropertyInfo(Variant::REAL, "width", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_width", "get_width"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_height", "get_height"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "depth", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_depth", "get_depth"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material"); -} - -void CSGBox::set_width(const float p_width) { - width = p_width; - _make_dirty(); - update_gizmo(); - _change_notify("width"); -} - -float CSGBox::get_width() const { - return width; -} - -void CSGBox::set_height(const float p_height) { - height = p_height; - _make_dirty(); - update_gizmo(); - _change_notify("height"); -} - -float CSGBox::get_height() const { - return height; -} - -void CSGBox::set_depth(const float p_depth) { - depth = p_depth; - _make_dirty(); - update_gizmo(); - _change_notify("depth"); -} - -float CSGBox::get_depth() const { - return depth; -} - -void CSGBox::set_material(const Ref &p_material) { - material = p_material; - _make_dirty(); - update_gizmo(); -} - -Ref CSGBox::get_material() const { - return material; -} - -CSGBox::CSGBox() { - // defaults - width = 2.0; - height = 2.0; - depth = 2.0; -} - -/////////////// - -CSGBrush *CSGCylinder::_build_brush() { - // set our bounding box - - CSGBrush *brush = memnew(CSGBrush); - - int face_count = sides * (cone ? 1 : 2) + sides + (cone ? 0 : sides); - - bool invert_val = is_inverting_faces(); - Ref material = get_material(); - - PoolVector faces; - PoolVector uvs; - PoolVector smooth; - PoolVector> materials; - PoolVector invert; - - faces.resize(face_count * 3); - uvs.resize(face_count * 3); - - smooth.resize(face_count); - materials.resize(face_count); - invert.resize(face_count); - - { - PoolVector::Write facesw = faces.write(); - PoolVector::Write uvsw = uvs.write(); - PoolVector::Write smoothw = smooth.write(); - PoolVector>::Write materialsw = materials.write(); - PoolVector::Write invertw = invert.write(); - - int face = 0; - - Vector3 vertex_mul(radius, height * 0.5, radius); - - { - for (int i = 0; i < sides; i++) { - float inc = float(i) / sides; - float inc_n = float((i + 1)) / sides; - if (i == sides - 1) { - inc_n = 0; - } - - float ang = inc * Math_PI * 2.0; - float ang_n = inc_n * Math_PI * 2.0; - - Vector3 base(Math::cos(ang), 0, Math::sin(ang)); - Vector3 base_n(Math::cos(ang_n), 0, Math::sin(ang_n)); - - Vector3 face_points[4] = { - base + Vector3(0, -1, 0), - base_n + Vector3(0, -1, 0), - base_n * (cone ? 0.0 : 1.0) + Vector3(0, 1, 0), - base * (cone ? 0.0 : 1.0) + Vector3(0, 1, 0), - }; - - Vector2 u[4] = { - Vector2(inc, 0), - Vector2(inc_n, 0), - Vector2(inc_n, 1), - Vector2(inc, 1), - }; - - //side face 1 - facesw[face * 3 + 0] = face_points[0] * vertex_mul; - facesw[face * 3 + 1] = face_points[1] * vertex_mul; - facesw[face * 3 + 2] = face_points[2] * vertex_mul; - - uvsw[face * 3 + 0] = u[0]; - uvsw[face * 3 + 1] = u[1]; - uvsw[face * 3 + 2] = u[2]; - - smoothw[face] = smooth_faces; - invertw[face] = invert_val; - materialsw[face] = material; - - face++; - - if (!cone) { - //side face 2 - facesw[face * 3 + 0] = face_points[2] * vertex_mul; - facesw[face * 3 + 1] = face_points[3] * vertex_mul; - facesw[face * 3 + 2] = face_points[0] * vertex_mul; - - uvsw[face * 3 + 0] = u[2]; - uvsw[face * 3 + 1] = u[3]; - uvsw[face * 3 + 2] = u[0]; - - smoothw[face] = smooth_faces; - invertw[face] = invert_val; - materialsw[face] = material; - face++; - } - - //bottom face 1 - facesw[face * 3 + 0] = face_points[1] * vertex_mul; - facesw[face * 3 + 1] = face_points[0] * vertex_mul; - facesw[face * 3 + 2] = Vector3(0, -1, 0) * vertex_mul; - - uvsw[face * 3 + 0] = Vector2(face_points[1].x, face_points[1].y) * 0.5 + Vector2(0.5, 0.5); - uvsw[face * 3 + 1] = Vector2(face_points[0].x, face_points[0].y) * 0.5 + Vector2(0.5, 0.5); - uvsw[face * 3 + 2] = Vector2(0.5, 0.5); - - smoothw[face] = false; - invertw[face] = invert_val; - materialsw[face] = material; - face++; - - if (!cone) { - //top face 1 - facesw[face * 3 + 0] = face_points[3] * vertex_mul; - facesw[face * 3 + 1] = face_points[2] * vertex_mul; - facesw[face * 3 + 2] = Vector3(0, 1, 0) * vertex_mul; - - uvsw[face * 3 + 0] = Vector2(face_points[1].x, face_points[1].y) * 0.5 + Vector2(0.5, 0.5); - uvsw[face * 3 + 1] = Vector2(face_points[0].x, face_points[0].y) * 0.5 + Vector2(0.5, 0.5); - uvsw[face * 3 + 2] = Vector2(0.5, 0.5); - - smoothw[face] = false; - invertw[face] = invert_val; - materialsw[face] = material; - face++; - } - } - } - - if (face != face_count) { - ERR_PRINT("Face mismatch bug! fix code"); - } - } - - brush->build_from_faces(faces, uvs, smooth, materials, invert); - - return brush; -} - -void CSGCylinder::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_radius", "radius"), &CSGCylinder::set_radius); - ClassDB::bind_method(D_METHOD("get_radius"), &CSGCylinder::get_radius); - - ClassDB::bind_method(D_METHOD("set_height", "height"), &CSGCylinder::set_height); - ClassDB::bind_method(D_METHOD("get_height"), &CSGCylinder::get_height); - - ClassDB::bind_method(D_METHOD("set_sides", "sides"), &CSGCylinder::set_sides); - ClassDB::bind_method(D_METHOD("get_sides"), &CSGCylinder::get_sides); - - ClassDB::bind_method(D_METHOD("set_cone", "cone"), &CSGCylinder::set_cone); - ClassDB::bind_method(D_METHOD("is_cone"), &CSGCylinder::is_cone); - - ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGCylinder::set_material); - ClassDB::bind_method(D_METHOD("get_material"), &CSGCylinder::get_material); - - ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGCylinder::set_smooth_faces); - ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGCylinder::get_smooth_faces); - - ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_radius", "get_radius"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_height", "get_height"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_sides", "get_sides"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cone"), "set_cone", "is_cone"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material"); -} - -void CSGCylinder::set_radius(const float p_radius) { - radius = p_radius; - _make_dirty(); - update_gizmo(); - _change_notify("radius"); -} - -float CSGCylinder::get_radius() const { - return radius; -} - -void CSGCylinder::set_height(const float p_height) { - height = p_height; - _make_dirty(); - update_gizmo(); - _change_notify("height"); -} - -float CSGCylinder::get_height() const { - return height; -} - -void CSGCylinder::set_sides(const int p_sides) { - ERR_FAIL_COND(p_sides < 3); - sides = p_sides; - _make_dirty(); - update_gizmo(); -} - -int CSGCylinder::get_sides() const { - return sides; -} - -void CSGCylinder::set_cone(const bool p_cone) { - cone = p_cone; - _make_dirty(); - update_gizmo(); -} - -bool CSGCylinder::is_cone() const { - return cone; -} - -void CSGCylinder::set_smooth_faces(const bool p_smooth_faces) { - smooth_faces = p_smooth_faces; - _make_dirty(); -} - -bool CSGCylinder::get_smooth_faces() const { - return smooth_faces; -} - -void CSGCylinder::set_material(const Ref &p_material) { - material = p_material; - _make_dirty(); -} - -Ref CSGCylinder::get_material() const { - return material; -} - -CSGCylinder::CSGCylinder() { - // defaults - radius = 1.0; - height = 1.0; - sides = 8; - cone = false; - smooth_faces = true; -} - -/////////////// - -CSGBrush *CSGTorus::_build_brush() { - // set our bounding box - - float min_radius = inner_radius; - float max_radius = outer_radius; - - if (min_radius == max_radius) { - return memnew(CSGBrush); //sorry, can't - } - - if (min_radius > max_radius) { - SWAP(min_radius, max_radius); - } - - float radius = (max_radius - min_radius) * 0.5; - - CSGBrush *brush = memnew(CSGBrush); - - int face_count = ring_sides * sides * 2; - - bool invert_val = is_inverting_faces(); - Ref material = get_material(); - - PoolVector faces; - PoolVector uvs; - PoolVector smooth; - PoolVector> materials; - PoolVector invert; - - faces.resize(face_count * 3); - uvs.resize(face_count * 3); - - smooth.resize(face_count); - materials.resize(face_count); - invert.resize(face_count); - - { - PoolVector::Write facesw = faces.write(); - PoolVector::Write uvsw = uvs.write(); - PoolVector::Write smoothw = smooth.write(); - PoolVector>::Write materialsw = materials.write(); - PoolVector::Write invertw = invert.write(); - - int face = 0; - - { - for (int i = 0; i < sides; i++) { - float inci = float(i) / sides; - float inci_n = float((i + 1)) / sides; - if (i == sides - 1) { - inci_n = 0; - } - - float angi = inci * Math_PI * 2.0; - float angi_n = inci_n * Math_PI * 2.0; - - Vector3 normali = Vector3(Math::cos(angi), 0, Math::sin(angi)); - Vector3 normali_n = Vector3(Math::cos(angi_n), 0, Math::sin(angi_n)); - - for (int j = 0; j < ring_sides; j++) { - float incj = float(j) / ring_sides; - float incj_n = float((j + 1)) / ring_sides; - if (j == ring_sides - 1) { - incj_n = 0; - } - - float angj = incj * Math_PI * 2.0; - float angj_n = incj_n * Math_PI * 2.0; - - Vector2 normalj = Vector2(Math::cos(angj), Math::sin(angj)) * radius + Vector2(min_radius + radius, 0); - Vector2 normalj_n = Vector2(Math::cos(angj_n), Math::sin(angj_n)) * radius + Vector2(min_radius + radius, 0); - - Vector3 face_points[4] = { - Vector3(normali.x * normalj.x, normalj.y, normali.z * normalj.x), - Vector3(normali.x * normalj_n.x, normalj_n.y, normali.z * normalj_n.x), - Vector3(normali_n.x * normalj_n.x, normalj_n.y, normali_n.z * normalj_n.x), - Vector3(normali_n.x * normalj.x, normalj.y, normali_n.z * normalj.x) - }; - - Vector2 u[4] = { - Vector2(inci, incj), - Vector2(inci, incj_n), - Vector2(inci_n, incj_n), - Vector2(inci_n, incj), - }; - - // face 1 - facesw[face * 3 + 0] = face_points[0]; - facesw[face * 3 + 1] = face_points[2]; - facesw[face * 3 + 2] = face_points[1]; - - uvsw[face * 3 + 0] = u[0]; - uvsw[face * 3 + 1] = u[2]; - uvsw[face * 3 + 2] = u[1]; - - smoothw[face] = smooth_faces; - invertw[face] = invert_val; - materialsw[face] = material; - - face++; - - //face 2 - facesw[face * 3 + 0] = face_points[3]; - facesw[face * 3 + 1] = face_points[2]; - facesw[face * 3 + 2] = face_points[0]; - - uvsw[face * 3 + 0] = u[3]; - uvsw[face * 3 + 1] = u[2]; - uvsw[face * 3 + 2] = u[0]; - - smoothw[face] = smooth_faces; - invertw[face] = invert_val; - materialsw[face] = material; - face++; - } - } - } - - if (face != face_count) { - ERR_PRINT("Face mismatch bug! fix code"); - } - } - - brush->build_from_faces(faces, uvs, smooth, materials, invert); - - return brush; -} - -void CSGTorus::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_inner_radius", "radius"), &CSGTorus::set_inner_radius); - ClassDB::bind_method(D_METHOD("get_inner_radius"), &CSGTorus::get_inner_radius); - - ClassDB::bind_method(D_METHOD("set_outer_radius", "radius"), &CSGTorus::set_outer_radius); - ClassDB::bind_method(D_METHOD("get_outer_radius"), &CSGTorus::get_outer_radius); - - ClassDB::bind_method(D_METHOD("set_sides", "sides"), &CSGTorus::set_sides); - ClassDB::bind_method(D_METHOD("get_sides"), &CSGTorus::get_sides); - - ClassDB::bind_method(D_METHOD("set_ring_sides", "sides"), &CSGTorus::set_ring_sides); - ClassDB::bind_method(D_METHOD("get_ring_sides"), &CSGTorus::get_ring_sides); - - ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGTorus::set_material); - ClassDB::bind_method(D_METHOD("get_material"), &CSGTorus::get_material); - - ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGTorus::set_smooth_faces); - ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGTorus::get_smooth_faces); - - ADD_PROPERTY(PropertyInfo(Variant::REAL, "inner_radius", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_inner_radius", "get_inner_radius"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "outer_radius", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_outer_radius", "get_outer_radius"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_sides", "get_sides"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "ring_sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_ring_sides", "get_ring_sides"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material"); -} - -void CSGTorus::set_inner_radius(const float p_inner_radius) { - inner_radius = p_inner_radius; - _make_dirty(); - update_gizmo(); - _change_notify("inner_radius"); -} - -float CSGTorus::get_inner_radius() const { - return inner_radius; -} - -void CSGTorus::set_outer_radius(const float p_outer_radius) { - outer_radius = p_outer_radius; - _make_dirty(); - update_gizmo(); - _change_notify("outer_radius"); -} - -float CSGTorus::get_outer_radius() const { - return outer_radius; -} - -void CSGTorus::set_sides(const int p_sides) { - ERR_FAIL_COND(p_sides < 3); - sides = p_sides; - _make_dirty(); - update_gizmo(); -} - -int CSGTorus::get_sides() const { - return sides; -} - -void CSGTorus::set_ring_sides(const int p_ring_sides) { - ERR_FAIL_COND(p_ring_sides < 3); - ring_sides = p_ring_sides; - _make_dirty(); - update_gizmo(); -} - -int CSGTorus::get_ring_sides() const { - return ring_sides; -} - -void CSGTorus::set_smooth_faces(const bool p_smooth_faces) { - smooth_faces = p_smooth_faces; - _make_dirty(); -} - -bool CSGTorus::get_smooth_faces() const { - return smooth_faces; -} - -void CSGTorus::set_material(const Ref &p_material) { - material = p_material; - _make_dirty(); -} - -Ref CSGTorus::get_material() const { - return material; -} - -CSGTorus::CSGTorus() { - // defaults - inner_radius = 2.0; - outer_radius = 3.0; - sides = 8; - ring_sides = 6; - smooth_faces = true; -} - -/////////////// - -CSGBrush *CSGPolygon::_build_brush() { - CSGBrush *brush = memnew(CSGBrush); - - if (polygon.size() < 3) { - return brush; - } - - // Triangulate polygon shape. - Vector shape_polygon = polygon; - if (Triangulate::get_area(shape_polygon) > 0) { - shape_polygon.invert(); - } - int shape_sides = shape_polygon.size(); - Vector shape_faces = Geometry::triangulate_polygon(shape_polygon); - ERR_FAIL_COND_V_MSG(shape_faces.size() < 3, brush, "Failed to triangulate CSGPolygon. Make sure the polygon doesn't have any intersecting edges."); - - // Get polygon enclosing Rect2. - Rect2 shape_rect(shape_polygon[0], Vector2()); - for (int i = 1; i < shape_sides; i++) { - shape_rect.expand_to(shape_polygon[i]); - } - - // If MODE_PATH, check if curve has changed. - Ref curve; - if (mode == MODE_PATH) { - Path *current_path = Object::cast_to(get_node_or_null(path_node)); - if (path != current_path) { - if (path) { - path->disconnect("tree_exited", this, "_path_exited"); - path->disconnect("curve_changed", this, "_path_changed"); - } - path = current_path; - if (path) { - path->connect("tree_exited", this, "_path_exited"); - path->connect("curve_changed", this, "_path_changed"); - } - } - - if (!path) { - return brush; - } - - curve = path->get_curve(); - if (curve.is_null() || curve->get_point_count() < 2) { - return brush; - } - } - - // Calculate the number of extrusions, ends and faces. - int extrusions = 0; - int extrusion_face_count = shape_sides * 2; - int end_count = 0; - int shape_face_count = shape_faces.size() / 3; - real_t curve_length = 1.0; - switch (mode) { - case MODE_DEPTH: - extrusions = 1; - end_count = 2; - break; - case MODE_SPIN: - extrusions = spin_sides; - if (spin_degrees < 360) { - end_count = 2; - } - break; - case MODE_PATH: { - curve_length = curve->get_baked_length(); - if (path_interval_type == PATH_INTERVAL_DISTANCE) { - extrusions = MAX(1, Math::ceil(curve_length / path_interval)) + 1; - } else { - extrusions = Math::ceil(1.0 * curve->get_point_count() / path_interval); - } - if (!path_joined) { - end_count = 2; - extrusions -= 1; - } - } break; - } - int face_count = extrusions * extrusion_face_count + end_count * shape_face_count; - - // Initialize variables used to create the mesh. - Ref material = get_material(); - - PoolVector faces; - PoolVector uvs; - PoolVector smooth; - PoolVector> materials; - PoolVector invert; - - faces.resize(face_count * 3); - uvs.resize(face_count * 3); - smooth.resize(face_count); - materials.resize(face_count); - invert.resize(face_count); - int faces_removed = 0; - - { - PoolVector::Write facesw = faces.write(); - PoolVector::Write uvsw = uvs.write(); - PoolVector::Write smoothw = smooth.write(); - PoolVector>::Write materialsw = materials.write(); - PoolVector::Write invertw = invert.write(); - - int face = 0; - Transform base_xform; - Transform current_xform; - Transform previous_xform; - Transform previous_previous_xform; - double u_step = 1.0 / extrusions; - if (path_u_distance > 0.0) { - u_step *= curve_length / path_u_distance; - } - double v_step = 1.0 / shape_sides; - double spin_step = Math::deg2rad(spin_degrees / spin_sides); - double extrusion_step = 1.0 / extrusions; - if (mode == MODE_PATH) { - if (path_joined) { - extrusion_step = 1.0 / (extrusions - 1); - } - extrusion_step *= curve_length; - } - - if (mode == MODE_PATH) { - if (!path_local) { - base_xform = path->get_global_transform(); - } - - Vector3 current_point = curve->interpolate_baked(0); - Vector3 next_point = curve->interpolate_baked(extrusion_step); - Vector3 current_up = Vector3(0, 1, 0); - Vector3 direction = next_point - current_point; - - if (path_joined) { - Vector3 last_point = curve->interpolate_baked(curve->get_baked_length()); - direction = next_point - last_point; - } - - switch (path_rotation) { - case PATH_ROTATION_POLYGON: - direction = Vector3(0, 0, -1); - break; - case PATH_ROTATION_PATH: - break; - case PATH_ROTATION_PATH_FOLLOW: - current_up = curve->interpolate_baked_up_vector(0); - break; - } - - Transform facing = Transform().looking_at(direction, current_up); - current_xform = base_xform.translated(current_point) * facing; - } - - // Create the mesh. - if (end_count > 0) { - // Add front end face. - for (int face_idx = 0; face_idx < shape_face_count; face_idx++) { - for (int face_vertex_idx = 0; face_vertex_idx < 3; face_vertex_idx++) { - // We need to reverse the rotation of the shape face vertices. - int index = shape_faces[face_idx * 3 + 2 - face_vertex_idx]; - Point2 p = shape_polygon[index]; - Point2 uv = (p - shape_rect.position) / shape_rect.size; - - // Use the left side of the bottom half of the y-inverted texture. - uv.x = uv.x / 2; - uv.y = 1 - (uv.y / 2); - - facesw[face * 3 + face_vertex_idx] = current_xform.xform(Vector3(p.x, p.y, 0)); - uvsw[face * 3 + face_vertex_idx] = uv; - } - - smoothw[face] = false; - materialsw[face] = material; - invertw[face] = invert_faces; - face++; - } - } - - real_t angle_simplify_dot = Math::cos(Math::deg2rad(path_simplify_angle)); - Vector3 previous_simplify_dir = Vector3(0, 0, 0); - int faces_combined = 0; - - // Add extrusion faces. - for (int x0 = 0; x0 < extrusions; x0++) { - previous_previous_xform = previous_xform; - previous_xform = current_xform; - - switch (mode) { - case MODE_DEPTH: { - current_xform.translate(Vector3(0, 0, -depth)); - } break; - case MODE_SPIN: { - current_xform.rotate(Vector3(0, 1, 0), spin_step); - } break; - case MODE_PATH: { - double previous_offset = x0 * extrusion_step; - double current_offset = (x0 + 1) * extrusion_step; - double next_offset = (x0 + 2) * extrusion_step; - if (x0 == extrusions - 1) { - if (path_joined) { - current_offset = 0; - next_offset = extrusion_step; - } else { - next_offset = current_offset; - } - } - - Vector3 previous_point = curve->interpolate_baked(previous_offset); - Vector3 current_point = curve->interpolate_baked(current_offset); - Vector3 next_point = curve->interpolate_baked(next_offset); - Vector3 current_up = Vector3(0, 1, 0); - Vector3 direction = next_point - previous_point; - Vector3 current_dir = (current_point - previous_point).normalized(); - - // If the angles are similar, remove the previous face and replace it with this one. - if (path_simplify_angle > 0.0 && x0 > 0 && previous_simplify_dir.dot(current_dir) > angle_simplify_dot) { - faces_combined += 1; - previous_xform = previous_previous_xform; - face -= extrusion_face_count; - faces_removed += extrusion_face_count; - } else { - faces_combined = 0; - previous_simplify_dir = current_dir; - } - - switch (path_rotation) { - case PATH_ROTATION_POLYGON: - direction = Vector3(0, 0, -1); - break; - case PATH_ROTATION_PATH: - break; - case PATH_ROTATION_PATH_FOLLOW: - current_up = curve->interpolate_baked_up_vector(current_offset); - break; - } - - Transform facing = Transform().looking_at(direction, current_up); - current_xform = base_xform.translated(current_point) * facing; - } break; - } - - double u0 = (x0 - faces_combined) * u_step; - double u1 = ((x0 + 1) * u_step); - if (mode == MODE_PATH && !path_continuous_u) { - u0 = 0.0; - u1 = 1.0; - } - - for (int y0 = 0; y0 < shape_sides; y0++) { - int y1 = (y0 + 1) % shape_sides; - // Use the top half of the texture. - double v0 = (y0 * v_step) / 2; - double v1 = ((y0 + 1) * v_step) / 2; - - Vector3 v[4] = { - previous_xform.xform(Vector3(shape_polygon[y0].x, shape_polygon[y0].y, 0)), - current_xform.xform(Vector3(shape_polygon[y0].x, shape_polygon[y0].y, 0)), - current_xform.xform(Vector3(shape_polygon[y1].x, shape_polygon[y1].y, 0)), - previous_xform.xform(Vector3(shape_polygon[y1].x, shape_polygon[y1].y, 0)), - }; - - Vector2 u[4] = { - Vector2(u0, v0), - Vector2(u1, v0), - Vector2(u1, v1), - Vector2(u0, v1), - }; - - // Face 1 - facesw[face * 3 + 0] = v[0]; - facesw[face * 3 + 1] = v[1]; - facesw[face * 3 + 2] = v[2]; - - uvsw[face * 3 + 0] = u[0]; - uvsw[face * 3 + 1] = u[1]; - uvsw[face * 3 + 2] = u[2]; - - smoothw[face] = smooth_faces; - invertw[face] = invert_faces; - materialsw[face] = material; - - face++; - - // Face 2 - facesw[face * 3 + 0] = v[2]; - facesw[face * 3 + 1] = v[3]; - facesw[face * 3 + 2] = v[0]; - - uvsw[face * 3 + 0] = u[2]; - uvsw[face * 3 + 1] = u[3]; - uvsw[face * 3 + 2] = u[0]; - - smoothw[face] = smooth_faces; - invertw[face] = invert_faces; - materialsw[face] = material; - - face++; - } - } - - if (end_count > 1) { - // Add back end face. - for (int face_idx = 0; face_idx < shape_face_count; face_idx++) { - for (int face_vertex_idx = 0; face_vertex_idx < 3; face_vertex_idx++) { - int index = shape_faces[face_idx * 3 + face_vertex_idx]; - Point2 p = shape_polygon[index]; - Point2 uv = (p - shape_rect.position) / shape_rect.size; - - // Use the x-inverted ride side of the bottom half of the y-inverted texture. - uv.x = 1 - uv.x / 2; - uv.y = 1 - (uv.y / 2); - - facesw[face * 3 + face_vertex_idx] = current_xform.xform(Vector3(p.x, p.y, 0)); - uvsw[face * 3 + face_vertex_idx] = uv; - } - - smoothw[face] = false; - materialsw[face] = material; - invertw[face] = invert_faces; - face++; - } - } - - face_count -= faces_removed; - ERR_FAIL_COND_V_MSG(face != face_count, brush, "Bug: Failed to create the CSGPolygon mesh correctly."); - } - - if (faces_removed > 0) { - faces.resize(face_count * 3); - uvs.resize(face_count * 3); - smooth.resize(face_count); - materials.resize(face_count); - invert.resize(face_count); - } - - brush->build_from_faces(faces, uvs, smooth, materials, invert); - - return brush; -} - -void CSGPolygon::_notification(int p_what) { - if (p_what == NOTIFICATION_EXIT_TREE) { - if (path) { - path->disconnect("tree_exited", this, "_path_exited"); - path->disconnect("curve_changed", this, "_path_changed"); - path = nullptr; - } - } -} - -void CSGPolygon::_validate_property(PropertyInfo &property) const { - if (property.name.begins_with("spin") && mode != MODE_SPIN) { - property.usage = 0; - } - if (property.name.begins_with("path") && mode != MODE_PATH) { - property.usage = 0; - } - if (property.name == "depth" && mode != MODE_DEPTH) { - property.usage = 0; - } - - CSGShape::_validate_property(property); -} - -void CSGPolygon::_path_changed() { - _make_dirty(); - update_gizmo(); -} - -void CSGPolygon::_path_exited() { - path = nullptr; -} - -void CSGPolygon::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_polygon", "polygon"), &CSGPolygon::set_polygon); - ClassDB::bind_method(D_METHOD("get_polygon"), &CSGPolygon::get_polygon); - - ClassDB::bind_method(D_METHOD("set_mode", "mode"), &CSGPolygon::set_mode); - ClassDB::bind_method(D_METHOD("get_mode"), &CSGPolygon::get_mode); - - ClassDB::bind_method(D_METHOD("set_depth", "depth"), &CSGPolygon::set_depth); - ClassDB::bind_method(D_METHOD("get_depth"), &CSGPolygon::get_depth); - - ClassDB::bind_method(D_METHOD("set_spin_degrees", "degrees"), &CSGPolygon::set_spin_degrees); - ClassDB::bind_method(D_METHOD("get_spin_degrees"), &CSGPolygon::get_spin_degrees); - - ClassDB::bind_method(D_METHOD("set_spin_sides", "spin_sides"), &CSGPolygon::set_spin_sides); - ClassDB::bind_method(D_METHOD("get_spin_sides"), &CSGPolygon::get_spin_sides); - - ClassDB::bind_method(D_METHOD("set_path_node", "path"), &CSGPolygon::set_path_node); - ClassDB::bind_method(D_METHOD("get_path_node"), &CSGPolygon::get_path_node); - - ClassDB::bind_method(D_METHOD("set_path_interval_type", "interval_type"), &CSGPolygon::set_path_interval_type); - ClassDB::bind_method(D_METHOD("get_path_interval_type"), &CSGPolygon::get_path_interval_type); - - ClassDB::bind_method(D_METHOD("set_path_interval", "path_interval"), &CSGPolygon::set_path_interval); - ClassDB::bind_method(D_METHOD("get_path_interval"), &CSGPolygon::get_path_interval); - - ClassDB::bind_method(D_METHOD("set_path_simplify_angle", "degrees"), &CSGPolygon::set_path_simplify_angle); - ClassDB::bind_method(D_METHOD("get_path_simplify_angle"), &CSGPolygon::get_path_simplify_angle); - - ClassDB::bind_method(D_METHOD("set_path_rotation", "path_rotation"), &CSGPolygon::set_path_rotation); - ClassDB::bind_method(D_METHOD("get_path_rotation"), &CSGPolygon::get_path_rotation); - - ClassDB::bind_method(D_METHOD("set_path_local", "enable"), &CSGPolygon::set_path_local); - ClassDB::bind_method(D_METHOD("is_path_local"), &CSGPolygon::is_path_local); - - ClassDB::bind_method(D_METHOD("set_path_continuous_u", "enable"), &CSGPolygon::set_path_continuous_u); - ClassDB::bind_method(D_METHOD("is_path_continuous_u"), &CSGPolygon::is_path_continuous_u); - - ClassDB::bind_method(D_METHOD("set_path_u_distance", "distance"), &CSGPolygon::set_path_u_distance); - ClassDB::bind_method(D_METHOD("get_path_u_distance"), &CSGPolygon::get_path_u_distance); - - ClassDB::bind_method(D_METHOD("set_path_joined", "enable"), &CSGPolygon::set_path_joined); - ClassDB::bind_method(D_METHOD("is_path_joined"), &CSGPolygon::is_path_joined); - - ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGPolygon::set_material); - ClassDB::bind_method(D_METHOD("get_material"), &CSGPolygon::get_material); - - ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGPolygon::set_smooth_faces); - ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGPolygon::get_smooth_faces); - - 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::POOL_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::REAL, "depth", PROPERTY_HINT_EXP_RANGE, "0.01,100.0,0.01,or_greater"), "set_depth", "get_depth"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "spin_degrees", PROPERTY_HINT_RANGE, "1,360,0.1"), "set_spin_degrees", "get_spin_degrees"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "spin_sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_spin_sides", "get_spin_sides"); - ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "path_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Path"), "set_path_node", "get_path_node"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "path_interval_type", PROPERTY_HINT_ENUM, "Distance,Subdivide"), "set_path_interval_type", "get_path_interval_type"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "path_interval", PROPERTY_HINT_RANGE, "0.01,1.0,0.01,exp,or_greater"), "set_path_interval", "get_path_interval"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "path_simplify_angle", PROPERTY_HINT_EXP_RANGE, "0.0,180.0,0.1,or_greater"), "set_path_simplify_angle", "get_path_simplify_angle"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "path_rotation", PROPERTY_HINT_ENUM, "Polygon,Path,PathFollow"), "set_path_rotation", "get_path_rotation"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_local"), "set_path_local", "is_path_local"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_continuous_u"), "set_path_continuous_u", "is_path_continuous_u"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "path_u_distance", PROPERTY_HINT_RANGE, "0.0,10.0,0.01,or_greater"), "set_path_u_distance", "get_path_u_distance"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_joined"), "set_path_joined", "is_path_joined"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material"); - - BIND_ENUM_CONSTANT(MODE_DEPTH); - BIND_ENUM_CONSTANT(MODE_SPIN); - BIND_ENUM_CONSTANT(MODE_PATH); - - BIND_ENUM_CONSTANT(PATH_ROTATION_POLYGON); - BIND_ENUM_CONSTANT(PATH_ROTATION_PATH); - BIND_ENUM_CONSTANT(PATH_ROTATION_PATH_FOLLOW); - - BIND_ENUM_CONSTANT(PATH_INTERVAL_DISTANCE); - BIND_ENUM_CONSTANT(PATH_INTERVAL_SUBDIVIDE); -} - -void CSGPolygon::set_polygon(const Vector &p_polygon) { - polygon = p_polygon; - _make_dirty(); - update_gizmo(); -} - -Vector CSGPolygon::get_polygon() const { - return polygon; -} - -void CSGPolygon::set_mode(Mode p_mode) { - mode = p_mode; - _make_dirty(); - update_gizmo(); - _change_notify(); -} - -CSGPolygon::Mode CSGPolygon::get_mode() const { - return mode; -} - -void CSGPolygon::set_depth(const float p_depth) { - ERR_FAIL_COND(p_depth < 0.001); - depth = p_depth; - _make_dirty(); - update_gizmo(); -} - -float CSGPolygon::get_depth() const { - return depth; -} - -void CSGPolygon::set_path_continuous_u(bool p_enable) { - path_continuous_u = p_enable; - _make_dirty(); -} - -bool CSGPolygon::is_path_continuous_u() const { - return path_continuous_u; -} - -void CSGPolygon::set_path_u_distance(real_t p_path_u_distance) { - path_u_distance = p_path_u_distance; - _make_dirty(); - update_gizmo(); -} - -real_t CSGPolygon::get_path_u_distance() const { - return path_u_distance; -} - -void CSGPolygon::set_spin_degrees(const float p_spin_degrees) { - ERR_FAIL_COND(p_spin_degrees < 0.01 || p_spin_degrees > 360); - spin_degrees = p_spin_degrees; - _make_dirty(); - update_gizmo(); -} - -float CSGPolygon::get_spin_degrees() const { - return spin_degrees; -} - -void CSGPolygon::set_spin_sides(int p_spin_sides) { - ERR_FAIL_COND(p_spin_sides < 3); - spin_sides = p_spin_sides; - _make_dirty(); - update_gizmo(); -} - -int CSGPolygon::get_spin_sides() const { - return spin_sides; -} - -void CSGPolygon::set_path_node(const NodePath &p_path) { - path_node = p_path; - _make_dirty(); - update_gizmo(); -} - -NodePath CSGPolygon::get_path_node() const { - return path_node; -} - -void CSGPolygon::set_path_interval_type(PathIntervalType p_interval_type) { - path_interval_type = p_interval_type; - _make_dirty(); - update_gizmo(); -} - -CSGPolygon::PathIntervalType CSGPolygon::get_path_interval_type() const { - return path_interval_type; -} - -void CSGPolygon::set_path_interval(float p_interval) { - path_interval = p_interval; - _make_dirty(); - update_gizmo(); -} - -float CSGPolygon::get_path_interval() const { - return path_interval; -} - -void CSGPolygon::set_path_simplify_angle(float angle) { - path_simplify_angle = angle; - _make_dirty(); - update_gizmo(); -} - -float CSGPolygon::get_path_simplify_angle() const { - return path_simplify_angle; -} - -void CSGPolygon::set_path_rotation(PathRotation p_rotation) { - path_rotation = p_rotation; - _make_dirty(); - update_gizmo(); -} - -CSGPolygon::PathRotation CSGPolygon::get_path_rotation() const { - return path_rotation; -} - -void CSGPolygon::set_path_local(bool p_enable) { - path_local = p_enable; - _make_dirty(); - update_gizmo(); -} - -bool CSGPolygon::is_path_local() const { - return path_local; -} - -void CSGPolygon::set_path_joined(bool p_enable) { - path_joined = p_enable; - _make_dirty(); - update_gizmo(); -} - -bool CSGPolygon::is_path_joined() const { - return path_joined; -} - -void CSGPolygon::set_smooth_faces(const bool p_smooth_faces) { - smooth_faces = p_smooth_faces; - _make_dirty(); -} - -bool CSGPolygon::get_smooth_faces() const { - return smooth_faces; -} - -void CSGPolygon::set_material(const Ref &p_material) { - material = p_material; - _make_dirty(); -} - -Ref CSGPolygon::get_material() const { - return material; -} - -bool CSGPolygon::_is_editable_3d_polygon() const { - return true; -} - -bool CSGPolygon::_has_editable_3d_polygon_no_depth() const { - return true; -} - -CSGPolygon::CSGPolygon() { - // defaults - mode = MODE_DEPTH; - polygon.push_back(Vector2(0, 0)); - polygon.push_back(Vector2(0, 1)); - polygon.push_back(Vector2(1, 1)); - polygon.push_back(Vector2(1, 0)); - depth = 1.0; - spin_degrees = 360; - spin_sides = 8; - smooth_faces = false; - path_interval_type = PATH_INTERVAL_DISTANCE; - path_interval = 1.0; - path_simplify_angle = 0.0; - path_rotation = PATH_ROTATION_PATH_FOLLOW; - path_local = false; - path_continuous_u = true; - path_u_distance = 1.0; - path_joined = false; - path = nullptr; -} diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h deleted file mode 100644 index 4a5efb05f..000000000 --- a/modules/csg/csg_shape.h +++ /dev/null @@ -1,456 +0,0 @@ -/*************************************************************************/ -/* csg_shape.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef CSG_SHAPE_H -#define CSG_SHAPE_H - -#define CSGJS_HEADER_ONLY - -#include "csg.h" -#include "scene/3d/path.h" -#include "scene/3d/visual_instance.h" -#include "scene/resources/concave_polygon_shape.h" -#include "thirdparty/misc/mikktspace.h" - -class CSGShape : public GeometryInstance { - GDCLASS(CSGShape, GeometryInstance); - -public: - enum Operation { - OPERATION_UNION, - OPERATION_INTERSECTION, - OPERATION_SUBTRACTION, - - }; - -private: - Operation operation; - CSGShape *parent_shape; - - CSGBrush *brush; - - AABB node_aabb; - - bool dirty; - bool last_visible = false; - float snap; - - bool use_collision; - uint32_t collision_layer; - uint32_t collision_mask; - Ref root_collision_shape; - RID root_collision_instance; - - bool calculate_tangents; - - Ref root_mesh; - - struct Vector3Hasher { - _ALWAYS_INLINE_ uint32_t hash(const Vector3 &p_vec3) const { - uint32_t h = hash_djb2_one_float(p_vec3.x); - h = hash_djb2_one_float(p_vec3.y, h); - h = hash_djb2_one_float(p_vec3.z, h); - return h; - } - }; - - struct ShapeUpdateSurface { - PoolVector vertices; - PoolVector normals; - PoolVector uvs; - PoolVector tans; - Ref material; - int last_added; - - PoolVector::Write verticesw; - PoolVector::Write normalsw; - PoolVector::Write uvsw; - PoolVector::Write tansw; - }; - - //mikktspace callbacks - static int mikktGetNumFaces(const SMikkTSpaceContext *pContext); - static int mikktGetNumVerticesOfFace(const SMikkTSpaceContext *pContext, const int iFace); - static void mikktGetPosition(const SMikkTSpaceContext *pContext, float fvPosOut[], const int iFace, const int iVert); - static void mikktGetNormal(const SMikkTSpaceContext *pContext, float fvNormOut[], const int iFace, const int iVert); - static void mikktGetTexCoord(const SMikkTSpaceContext *pContext, float fvTexcOut[], const int iFace, const int iVert); - static void mikktSetTSpaceDefault(const SMikkTSpaceContext *pContext, const float fvTangent[], const float fvBiTangent[], const float fMagS, const float fMagT, - const tbool bIsOrientationPreserving, const int iFace, const int iVert); - - void _update_shape(); - void _update_collision_faces(); - -protected: - void _notification(int p_what); - virtual CSGBrush *_build_brush() = 0; - void _make_dirty(bool p_parent_removing = false); - - static void _bind_methods(); - - friend class CSGCombiner; - CSGBrush *_get_brush(); - - virtual void _validate_property(PropertyInfo &property) const; - -public: - Array get_meshes() const; - - void force_update_shape(); - - void set_operation(Operation p_operation); - Operation get_operation() const; - - virtual PoolVector get_brush_faces(); - - virtual AABB get_aabb() const; - virtual PoolVector get_faces(uint32_t p_usage_flags) const; - - void set_use_collision(bool p_enable); - bool is_using_collision() const; - - void set_collision_layer(uint32_t p_layer); - uint32_t get_collision_layer() const; - - void set_collision_mask(uint32_t p_mask); - uint32_t get_collision_mask() const; - - void set_collision_layer_bit(int p_bit, bool p_value); - bool get_collision_layer_bit(int p_bit) const; - - void set_collision_mask_bit(int p_bit, bool p_value); - bool get_collision_mask_bit(int p_bit) const; - - void set_snap(float p_snap); - float get_snap() const; - - void set_calculate_tangents(bool p_calculate_tangents); - bool is_calculating_tangents() const; - - bool is_root_shape() const; - CSGShape(); - ~CSGShape(); -}; - -VARIANT_ENUM_CAST(CSGShape::Operation) - -class CSGCombiner : public CSGShape { - GDCLASS(CSGCombiner, CSGShape); - -private: - virtual CSGBrush *_build_brush(); - -public: - CSGCombiner(); -}; - -class CSGPrimitive : public CSGShape { - GDCLASS(CSGPrimitive, CSGShape); - -protected: - bool invert_faces; - CSGBrush *_create_brush_from_arrays(const PoolVector &p_vertices, const PoolVector &p_uv, const PoolVector &p_smooth, const PoolVector> &p_materials); - static void _bind_methods(); - -public: - void set_invert_faces(bool p_invert); - bool is_inverting_faces(); - - CSGPrimitive(); -}; - -class CSGMesh : public CSGPrimitive { - GDCLASS(CSGMesh, CSGPrimitive); - - virtual CSGBrush *_build_brush(); - - Ref mesh; - Ref material; - - void _mesh_changed(); - -protected: - static void _bind_methods(); - -public: - void set_mesh(const Ref &p_mesh); - Ref get_mesh(); - - void set_material(const Ref &p_material); - Ref get_material() const; -}; - -class CSGSphere : public CSGPrimitive { - GDCLASS(CSGSphere, CSGPrimitive); - virtual CSGBrush *_build_brush(); - - Ref material; - bool smooth_faces; - float radius; - int radial_segments; - int rings; - -protected: - static void _bind_methods(); - -public: - void set_radius(const float p_radius); - float get_radius() const; - - void set_radial_segments(const int p_radial_segments); - int get_radial_segments() const; - - void set_rings(const int p_rings); - int get_rings() const; - - void set_material(const Ref &p_material); - Ref get_material() const; - - void set_smooth_faces(bool p_smooth_faces); - bool get_smooth_faces() const; - - CSGSphere(); -}; - -class CSGBox : public CSGPrimitive { - GDCLASS(CSGBox, CSGPrimitive); - virtual CSGBrush *_build_brush(); - - Ref material; - float width; - float height; - float depth; - -protected: - static void _bind_methods(); - -public: - void set_width(const float p_width); - float get_width() const; - - void set_height(const float p_height); - float get_height() const; - - void set_depth(const float p_depth); - float get_depth() const; - - void set_material(const Ref &p_material); - Ref get_material() const; - - CSGBox(); -}; - -class CSGCylinder : public CSGPrimitive { - GDCLASS(CSGCylinder, CSGPrimitive); - virtual CSGBrush *_build_brush(); - - Ref material; - float radius; - float height; - int sides; - bool cone; - bool smooth_faces; - -protected: - static void _bind_methods(); - -public: - void set_radius(const float p_radius); - float get_radius() const; - - void set_height(const float p_height); - float get_height() const; - - void set_sides(const int p_sides); - int get_sides() const; - - void set_cone(const bool p_cone); - bool is_cone() const; - - void set_smooth_faces(bool p_smooth_faces); - bool get_smooth_faces() const; - - void set_material(const Ref &p_material); - Ref get_material() const; - - CSGCylinder(); -}; - -class CSGTorus : public CSGPrimitive { - GDCLASS(CSGTorus, CSGPrimitive); - virtual CSGBrush *_build_brush(); - - Ref material; - float inner_radius; - float outer_radius; - int sides; - int ring_sides; - bool smooth_faces; - -protected: - static void _bind_methods(); - -public: - void set_inner_radius(const float p_inner_radius); - float get_inner_radius() const; - - void set_outer_radius(const float p_outer_radius); - float get_outer_radius() const; - - void set_sides(const int p_sides); - int get_sides() const; - - void set_ring_sides(const int p_ring_sides); - int get_ring_sides() const; - - void set_smooth_faces(bool p_smooth_faces); - bool get_smooth_faces() const; - - void set_material(const Ref &p_material); - Ref get_material() const; - - CSGTorus(); -}; - -class CSGPolygon : public CSGPrimitive { - GDCLASS(CSGPolygon, CSGPrimitive); - -public: - enum Mode { - MODE_DEPTH, - MODE_SPIN, - MODE_PATH - }; - - enum PathIntervalType { - PATH_INTERVAL_DISTANCE, - PATH_INTERVAL_SUBDIVIDE - }; - - enum PathRotation { - PATH_ROTATION_POLYGON, - PATH_ROTATION_PATH, - PATH_ROTATION_PATH_FOLLOW, - }; - -private: - virtual CSGBrush *_build_brush(); - - Vector polygon; - Ref material; - - Mode mode; - - float depth; - - float spin_degrees; - int spin_sides; - - NodePath path_node; - PathIntervalType path_interval_type; - float path_interval; - float path_simplify_angle; - PathRotation path_rotation; - bool path_local; - - Path *path; - - bool smooth_faces; - bool path_continuous_u; - real_t path_u_distance; - bool path_joined; - - bool _is_editable_3d_polygon() const; - bool _has_editable_3d_polygon_no_depth() const; - - void _path_changed(); - void _path_exited(); - -protected: - static void _bind_methods(); - virtual void _validate_property(PropertyInfo &property) const; - void _notification(int p_what); - -public: - void set_polygon(const Vector &p_polygon); - Vector get_polygon() const; - - void set_mode(Mode p_mode); - Mode get_mode() const; - - void set_depth(float p_depth); - float get_depth() const; - - void set_spin_degrees(float p_spin_degrees); - float get_spin_degrees() const; - - void set_spin_sides(int p_spin_sides); - int get_spin_sides() const; - - void set_path_node(const NodePath &p_path); - NodePath get_path_node() const; - - void set_path_interval_type(PathIntervalType p_interval_type); - PathIntervalType get_path_interval_type() const; - - void set_path_interval(float p_interval); - float get_path_interval() const; - - void set_path_simplify_angle(float p_angle); - float get_path_simplify_angle() const; - - void set_path_rotation(PathRotation p_rotation); - PathRotation get_path_rotation() const; - - void set_path_local(bool p_enable); - bool is_path_local() const; - - void set_path_continuous_u(bool p_enable); - bool is_path_continuous_u() const; - - void set_path_u_distance(real_t p_path_u_distance); - real_t get_path_u_distance() const; - - void set_path_joined(bool p_enable); - bool is_path_joined() const; - - void set_smooth_faces(bool p_smooth_faces); - bool get_smooth_faces() const; - - void set_material(const Ref &p_material); - Ref get_material() const; - - CSGPolygon(); -}; - -VARIANT_ENUM_CAST(CSGPolygon::Mode) -VARIANT_ENUM_CAST(CSGPolygon::PathRotation) -VARIANT_ENUM_CAST(CSGPolygon::PathIntervalType) - -#endif // CSG_SHAPE_H diff --git a/modules/csg/doc_classes/CSGBox.xml b/modules/csg/doc_classes/CSGBox.xml deleted file mode 100644 index 1f081c964..000000000 --- a/modules/csg/doc_classes/CSGBox.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - A CSG Box shape. - - - This node allows you to create a box for use with the CSG system. - [b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay. - - - $DOCS_URL/tutorials/3d/csg_tools.html - - - - - - Depth of the box measured from the center of the box. - - - Height of the box measured from the center of the box. - - - The material used to render the box. - - - Width of the box measured from the center of the box. - - - - - diff --git a/modules/csg/doc_classes/CSGCombiner.xml b/modules/csg/doc_classes/CSGCombiner.xml deleted file mode 100644 index 43f6d3880..000000000 --- a/modules/csg/doc_classes/CSGCombiner.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - A CSG node that allows you to combine other CSG modifiers. - - - For complex arrangements of shapes, it is sometimes needed to add structure to your CSG nodes. The CSGCombiner node allows you to create this structure. The node encapsulates the result of the CSG operations of its children. In this way, it is possible to do operations on one set of shapes that are children of one CSGCombiner node, and a set of separate operations on a second set of shapes that are children of a second CSGCombiner node, and then do an operation that takes the two end results as its input to create the final shape. - [b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay. - - - $DOCS_URL/tutorials/3d/csg_tools.html - - - - - - diff --git a/modules/csg/doc_classes/CSGCylinder.xml b/modules/csg/doc_classes/CSGCylinder.xml deleted file mode 100644 index cee90475c..000000000 --- a/modules/csg/doc_classes/CSGCylinder.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - A CSG Cylinder shape. - - - This node allows you to create a cylinder (or cone) for use with the CSG system. - [b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay. - - - $DOCS_URL/tutorials/3d/csg_tools.html - - - - - - If [code]true[/code] a cone is created, the [member radius] will only apply to one side. - - - The height of the cylinder. - - - The material used to render the cylinder. - - - The radius of the cylinder. - - - The number of sides of the cylinder, the higher this number the more detail there will be in the cylinder. - - - If [code]true[/code] the normals of the cylinder are set to give a smooth effect making the cylinder seem rounded. If [code]false[/code] the cylinder will have a flat shaded look. - - - - - diff --git a/modules/csg/doc_classes/CSGMesh.xml b/modules/csg/doc_classes/CSGMesh.xml deleted file mode 100644 index 14bc9bb02..000000000 --- a/modules/csg/doc_classes/CSGMesh.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - A CSG Mesh shape that uses a mesh resource. - - - This CSG node allows you to use any mesh resource as a CSG shape, provided it is closed, does not self-intersect, does not contain internal faces and has no edges that connect to more than two faces. See also [CSGPolygon] for drawing 2D extruded polygons to be used as CSG nodes. - [b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay. - - - $DOCS_URL/tutorials/3d/csg_tools.html - - - - - - The [Material] used in drawing the CSG shape. - - - The [Mesh] resource to use as a CSG shape. - [b]Note:[/b] When using an [ArrayMesh], avoid meshes with vertex normals unless a flat shader is required. By default, CSGMesh will ignore the mesh's vertex normals and use a smooth shader calculated using the faces' normals. If a flat shader is required, ensure that all faces' vertex normals are parallel. - - - - - diff --git a/modules/csg/doc_classes/CSGPolygon.xml b/modules/csg/doc_classes/CSGPolygon.xml deleted file mode 100644 index 4fa874cf9..000000000 --- a/modules/csg/doc_classes/CSGPolygon.xml +++ /dev/null @@ -1,94 +0,0 @@ - - - - Extrudes a 2D polygon shape to create a 3D mesh. - - - An array of 2D points is extruded to quickly and easily create a variety of 3D meshes. See also [CSGMesh] for using 3D meshes as CSG nodes. - [b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay. - - - $DOCS_URL/tutorials/3d/csg_tools.html - - - - - - When [member mode] is [constant MODE_DEPTH], the depth of the extrusion. - - - Material to use for the resulting mesh. The UV maps the top half of the material to the extruded shape (U along the the length of the extrusions and V around the outline of the [member polygon]), the bottom-left quarter to the front end face, and the bottom-right quarter to the back end face. - - - The [member mode] used to extrude the [member polygon]. - - - When [member mode] is [constant MODE_PATH], by default, the top half of the [member material] is stretched along the entire length of the extruded shape. If [code]false[/code] the top half of the material is repeated every step of the extrusion. - - - When [member mode] is [constant MODE_PATH], the path interval or ratio of path points to extrusions. - - - When [member mode] is [constant MODE_PATH], this will determine if the interval should be by distance ([constant PATH_INTERVAL_DISTANCE]) or subdivision fractions ([constant PATH_INTERVAL_SUBDIVIDE]). - - - When [member mode] is [constant MODE_PATH], if [code]true[/code] the ends of the path are joined, by adding an extrusion between the last and first points of the path. - - - When [member mode] is [constant MODE_PATH], if [code]true[/code] the [Transform] of the [CSGPolygon] is used as the starting point for the extrusions, not the [Transform] of the [member path_node]. - - - When [member mode] is [constant MODE_PATH], the location of the [Path] object used to extrude the [member polygon]. - - - When [member mode] is [constant MODE_PATH], the [enum PathRotation] method used to rotate the [member polygon] as it is extruded. - - - When [member mode] is [constant MODE_PATH], extrusions that are less than this angle, will be merged together to reduce polygon count. - - - When [member mode] is [constant MODE_PATH], this is the distance along the path, in meters, the texture coordinates will tile. When set to 0, texture coordinates will match geometry exactly with no tiling. - - - The point array that defines the 2D polygon that is extruded. This can be a convex or concave polygon with 3 or more points. The polygon must [i]not[/i] have any intersecting edges. Otherwise, triangulation will fail and no mesh will be generated. - [b]Note:[/b] If only 1 or 2 points are defined in [member polygon], no mesh will be generated. - - - If [code]true[/code], applies smooth shading to the extrusions. - - - When [member mode] is [constant MODE_SPIN], the total number of degrees the [member polygon] is rotated when extruding. - - - When [member mode] is [constant MODE_SPIN], the number of extrusions made. - - - - - The [member polygon] shape is extruded along the negative Z axis. - - - The [member polygon] shape is extruded by rotating it around the Y axis. - - - The [member polygon] shape is extruded along the [Path] specified in [member path_node]. - - - The [member polygon] shape is not rotated. - [b]Note:[/b] Requires the path's Z coordinates to continually decrease to ensure viable shapes. - - - The [member polygon] shape is rotated along the path, but it is not rotated around the path axis. - [b]Note:[/b] Requires the path's Z coordinates to continually decrease to ensure viable shapes. - - - The [member polygon] shape follows the path and its rotations around the path axis. - - - When [member mode] is set to [constant MODE_PATH], [member path_interval] will determine the distance, in meters, each interval of the path will extrude. - - - When [member mode] is set to [constant MODE_PATH], [member path_interval] will subdivide the polygons along the path. - - - diff --git a/modules/csg/doc_classes/CSGPrimitive.xml b/modules/csg/doc_classes/CSGPrimitive.xml deleted file mode 100644 index 0c8646874..000000000 --- a/modules/csg/doc_classes/CSGPrimitive.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - Base class for CSG primitives. - - - Parent class for various CSG primitives. It contains code and functionality that is common between them. It cannot be used directly. Instead use one of the various classes that inherit from it. - [b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay. - - - $DOCS_URL/tutorials/3d/csg_tools.html - - - - - - Invert the faces of the mesh. - - - - - diff --git a/modules/csg/doc_classes/CSGShape.xml b/modules/csg/doc_classes/CSGShape.xml deleted file mode 100644 index f4d7ff50b..000000000 --- a/modules/csg/doc_classes/CSGShape.xml +++ /dev/null @@ -1,90 +0,0 @@ - - - - The CSG base class. - - - This is the CSG base class that provides CSG operation support to the various CSG nodes in Godot. - [b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay. - - - $DOCS_URL/tutorials/3d/csg_tools.html - - - - - - - Returns an individual bit on the collision mask. - - - - - - - Returns an individual bit on the collision mask. - - - - - - Returns an [Array] with two elements, the first is the [Transform] of this node and the second is the root [Mesh] of this node. Only works when this node is the root shape. - - - - - - Returns [code]true[/code] if this is a root shape and is thus the object that is rendered. - - - - - - - - Sets individual bits on the layer mask. Use this if you only need to change one layer's value. - - - - - - - - Sets individual bits on the collision mask. Use this if you only need to change one layer's value. - - - - - - Calculate tangents for the CSG shape which allows the use of normal maps. This is only applied on the root shape, this setting is ignored on any child. - - - The physics layers this area is in. - Collidable objects can exist in any of 32 different layers. These layers work like a tagging system, and are not visual. A collidable can use these layers to select with which objects it can collide, using the collision_mask property. - A contact is detected if object A is in any of the layers that object B scans, or object B is in any layer scanned by object A. See [url=$DOCS_URL/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information. - - - The physics layers this CSG shape scans for collisions. See [url=$DOCS_URL/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information. - - - The operation that is performed on this shape. This is ignored for the first CSG child node as the operation is between this node and the previous child of this nodes parent. - - - Snap makes the mesh snap to a given distance so that the faces of two meshes can be perfectly aligned. A lower value results in greater precision but may be harder to adjust. - - - Adds a collision shape to the physics engine for our CSG shape. This will always act like a static body. Note that the collision shape is still active even if the CSG shape itself is hidden. - - - - - Geometry of both primitives is merged, intersecting geometry is removed. - - - Only intersecting geometry remains, the rest is removed. - - - The second shape is subtracted from the first, leaving a dent with its shape. - - - diff --git a/modules/csg/doc_classes/CSGSphere.xml b/modules/csg/doc_classes/CSGSphere.xml deleted file mode 100644 index 4c0c284e9..000000000 --- a/modules/csg/doc_classes/CSGSphere.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - A CSG Sphere shape. - - - This node allows you to create a sphere for use with the CSG system. - [b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay. - - - $DOCS_URL/tutorials/3d/csg_tools.html - - - - - - The material used to render the sphere. - - - Number of vertical slices for the sphere. - - - Radius of the sphere. - - - Number of horizontal slices for the sphere. - - - If [code]true[/code] the normals of the sphere are set to give a smooth effect making the sphere seem rounded. If [code]false[/code] the sphere will have a flat shaded look. - - - - - diff --git a/modules/csg/doc_classes/CSGTorus.xml b/modules/csg/doc_classes/CSGTorus.xml deleted file mode 100644 index 7a83a2826..000000000 --- a/modules/csg/doc_classes/CSGTorus.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - A CSG Torus shape. - - - This node allows you to create a torus for use with the CSG system. - [b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay. - - - $DOCS_URL/tutorials/3d/csg_tools.html - - - - - - The inner radius of the torus. - - - The material used to render the torus. - - - The outer radius of the torus. - - - The number of edges each ring of the torus is constructed of. - - - The number of slices the torus is constructed of. - - - If [code]true[/code] the normals of the torus are set to give a smooth effect making the torus seem rounded. If [code]false[/code] the torus will have a flat shaded look. - - - - - diff --git a/modules/csg/icons/icon_c_s_g_box.svg b/modules/csg/icons/icon_c_s_g_box.svg deleted file mode 100644 index 67e34df44..000000000 --- a/modules/csg/icons/icon_c_s_g_box.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/modules/csg/icons/icon_c_s_g_capsule.svg b/modules/csg/icons/icon_c_s_g_capsule.svg deleted file mode 100644 index 92a7b5a87..000000000 --- a/modules/csg/icons/icon_c_s_g_capsule.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/modules/csg/icons/icon_c_s_g_combiner.svg b/modules/csg/icons/icon_c_s_g_combiner.svg deleted file mode 100644 index cce2902e2..000000000 --- a/modules/csg/icons/icon_c_s_g_combiner.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/modules/csg/icons/icon_c_s_g_cylinder.svg b/modules/csg/icons/icon_c_s_g_cylinder.svg deleted file mode 100644 index 645a74c79..000000000 --- a/modules/csg/icons/icon_c_s_g_cylinder.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/modules/csg/icons/icon_c_s_g_mesh.svg b/modules/csg/icons/icon_c_s_g_mesh.svg deleted file mode 100644 index 6e940a4aa..000000000 --- a/modules/csg/icons/icon_c_s_g_mesh.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/modules/csg/icons/icon_c_s_g_polygon.svg b/modules/csg/icons/icon_c_s_g_polygon.svg deleted file mode 100644 index 71b03cb8e..000000000 --- a/modules/csg/icons/icon_c_s_g_polygon.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/modules/csg/icons/icon_c_s_g_sphere.svg b/modules/csg/icons/icon_c_s_g_sphere.svg deleted file mode 100644 index f81b56699..000000000 --- a/modules/csg/icons/icon_c_s_g_sphere.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/modules/csg/icons/icon_c_s_g_torus.svg b/modules/csg/icons/icon_c_s_g_torus.svg deleted file mode 100644 index 3d30aa47b..000000000 --- a/modules/csg/icons/icon_c_s_g_torus.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/modules/csg/register_types.cpp b/modules/csg/register_types.cpp deleted file mode 100644 index b6b28c9d8..000000000 --- a/modules/csg/register_types.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/*************************************************************************/ -/* register_types.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "register_types.h" - -#include "csg_gizmos.h" -#include "csg_shape.h" - -void register_csg_types() { -#ifndef _3D_DISABLED - - ClassDB::register_virtual_class(); - ClassDB::register_virtual_class(); - ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); - -#ifdef TOOLS_ENABLED - EditorPlugins::add_by_type(); -#endif -#endif -} - -void unregister_csg_types() { -} diff --git a/modules/csg/register_types.h b/modules/csg/register_types.h deleted file mode 100644 index 59d84dd52..000000000 --- a/modules/csg/register_types.h +++ /dev/null @@ -1,37 +0,0 @@ -/*************************************************************************/ -/* register_types.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef CSG_REGISTER_TYPES_H -#define CSG_REGISTER_TYPES_H - -void register_csg_types(); -void unregister_csg_types(); - -#endif // CSG_REGISTER_TYPES_H