Merge pull request #94321 from fire/vsk-csg-manifold-update-4.3
Fix mesh corruption of CSG by using elalish/manifold
This commit is contained in:
commit
c814493e95
45 changed files with 20005 additions and 1628 deletions
|
|
@ -6,7 +6,42 @@ Import("env_modules")
|
|||
|
||||
env_csg = env_modules.Clone()
|
||||
|
||||
# Godot source files
|
||||
env_csg.Append(CPPDEFINES=("MANIFOLD_PAR", "-1"))
|
||||
|
||||
# Thirdparty source files
|
||||
|
||||
thirdparty_obj = []
|
||||
|
||||
thirdparty_dir = "#thirdparty/manifold/"
|
||||
thirdparty_sources = [
|
||||
"src/boolean_result.cpp",
|
||||
"src/boolean3.cpp",
|
||||
"src/constructors.cpp",
|
||||
"src/csg_tree.cpp",
|
||||
"src/edge_op.cpp",
|
||||
"src/face_op.cpp",
|
||||
"src/impl.cpp",
|
||||
"src/manifold.cpp",
|
||||
"src/polygon.cpp",
|
||||
"src/properties.cpp",
|
||||
"src/quickhull.cpp",
|
||||
"src/smoothing.cpp",
|
||||
"src/sort.cpp",
|
||||
"src/subdivision.cpp",
|
||||
]
|
||||
|
||||
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
|
||||
env_csg.Prepend(
|
||||
CPPPATH=[
|
||||
thirdparty_dir + "include",
|
||||
]
|
||||
)
|
||||
env_thirdparty = env_csg.Clone()
|
||||
env_thirdparty.disable_warnings()
|
||||
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
|
||||
env.modules_sources += thirdparty_obj
|
||||
|
||||
# Godot's own source files
|
||||
env_csg.add_source_files(env.modules_sources, "*.cpp")
|
||||
if env.editor_build:
|
||||
env_csg.add_source_files(env.modules_sources, "editor/*.cpp")
|
||||
|
|
|
|||
1428
modules/csg/csg.cpp
1428
modules/csg/csg.cpp
File diff suppressed because it is too large
Load diff
|
|
@ -55,150 +55,18 @@ struct CSGBrush {
|
|||
Vector<Face> faces;
|
||||
Vector<Ref<Material>> materials;
|
||||
|
||||
inline void _regen_face_aabbs();
|
||||
inline void _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]);
|
||||
}
|
||||
}
|
||||
|
||||
// Create a brush from faces.
|
||||
void build_from_faces(const Vector<Vector3> &p_vertices, const Vector<Vector2> &p_uvs, const Vector<bool> &p_smooth, const Vector<Ref<Material>> &p_materials, const Vector<bool> &p_invert_faces);
|
||||
void copy_from(const CSGBrush &p_brush, const Transform3D &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 = false;
|
||||
bool inside = false;
|
||||
int points[3] = {};
|
||||
Vector2 uvs[3];
|
||||
bool smooth = false;
|
||||
bool invert = false;
|
||||
int material_idx = 0;
|
||||
};
|
||||
|
||||
struct FaceBVH {
|
||||
int face = 0;
|
||||
int left = 0;
|
||||
int right = 0;
|
||||
int next = 0;
|
||||
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_murmur3_one_32(p_vk.x);
|
||||
h = hash_murmur3_one_32(p_vk.y, h);
|
||||
h = hash_murmur3_one_32(p_vk.z, h);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
struct Intersection {
|
||||
bool found = false;
|
||||
real_t conormal = FLT_MAX;
|
||||
real_t distance_squared = FLT_MAX;
|
||||
real_t origin_angle = FLT_MAX;
|
||||
};
|
||||
|
||||
struct IntersectionDistance {
|
||||
bool is_conormal;
|
||||
real_t distance_squared;
|
||||
};
|
||||
|
||||
Vector<Vector3> points;
|
||||
Vector<Face> faces;
|
||||
HashMap<Ref<Material>, int> materials;
|
||||
HashMap<Vector3, int> vertex_map;
|
||||
OAHashMap<VertexKey, int, VertexKeyHash> snap_cache;
|
||||
float vertex_snap = 0.0;
|
||||
|
||||
inline void _add_distance(List<IntersectionDistance> &r_intersectionsA, List<IntersectionDistance> &r_intersectionsB, bool p_from_B, real_t p_distance, bool p_is_conormal) const;
|
||||
inline bool _bvh_inside(FaceBVH *r_facebvhptr, int p_max_depth, int p_bvh_first, int p_face_idx) const;
|
||||
inline int _create_bvh(FaceBVH *r_facebvhptr, FaceBVH **r_facebvhptrptr, int p_from, int p_size, int p_depth, int &r_max_depth, int &r_max_alloc);
|
||||
|
||||
void add_face(const Vector3 p_points[3], const Vector2 p_uvs[3], bool p_smooth, bool p_invert, const Ref<Material> &p_material, bool p_from_b);
|
||||
void mark_inside_faces();
|
||||
};
|
||||
|
||||
struct Build2DFaces {
|
||||
struct Vertex2D {
|
||||
Vector2 point;
|
||||
Vector2 uv;
|
||||
};
|
||||
|
||||
struct Face2D {
|
||||
int vertex_idx[3] = {};
|
||||
};
|
||||
|
||||
Vector<Vertex2D> vertices;
|
||||
Vector<Face2D> faces;
|
||||
Plane plane;
|
||||
Transform3D to_2D;
|
||||
Transform3D to_3D;
|
||||
float vertex_snap2 = 0.0;
|
||||
|
||||
inline int _get_point_idx(const Vector2 &p_point);
|
||||
inline int _add_vertex(const Vertex2D &p_vertex);
|
||||
inline void _add_vertex_idx_sorted(Vector<int> &r_vertex_indices, int p_new_vertex_index);
|
||||
inline void _merge_faces(const Vector<int> &p_segment_indices);
|
||||
inline void _find_edge_intersections(const Vector2 p_segment_points[2], Vector<int> &r_segment_indices);
|
||||
inline int _insert_point(const Vector2 &p_point);
|
||||
|
||||
void insert(const CSGBrush &p_brush, int p_brush_face);
|
||||
void addFacesToMesh(MeshMerge &r_mesh_merge, bool p_smooth, bool p_invert, const Ref<Material> &p_material, bool p_from_b);
|
||||
|
||||
Build2DFaces() {}
|
||||
Build2DFaces(const CSGBrush &p_brush, int p_brush_face, float p_vertex_snap2);
|
||||
};
|
||||
|
||||
struct Build2DFaceCollection {
|
||||
HashMap<int, Build2DFaces> build2DFacesA;
|
||||
HashMap<int, Build2DFaces> 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
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@
|
|||
|
||||
#include "core/math/geometry_2d.h"
|
||||
|
||||
#include <manifold/manifold.h>
|
||||
|
||||
void CSGShape3D::set_use_collision(bool p_enable) {
|
||||
if (use_collision == p_enable) {
|
||||
return;
|
||||
|
|
@ -167,78 +169,213 @@ void CSGShape3D::_make_dirty(bool p_parent_removing) {
|
|||
dirty = true;
|
||||
}
|
||||
|
||||
CSGBrush *CSGShape3D::_get_brush() {
|
||||
if (dirty) {
|
||||
if (brush) {
|
||||
memdelete(brush);
|
||||
}
|
||||
brush = nullptr;
|
||||
enum ManifoldProperty {
|
||||
MANIFOLD_PROPERTY_POSITION_X = 0,
|
||||
MANIFOLD_PROPERTY_POSITION_Y,
|
||||
MANIFOLD_PROPERTY_POSITION_Z,
|
||||
MANIFOLD_PROPERTY_INVERT,
|
||||
MANIFOLD_PROPERTY_SMOOTH_GROUP,
|
||||
MANIFOLD_PROPERTY_UV_X_0,
|
||||
MANIFOLD_PROPERTY_UV_Y_0,
|
||||
MANIFOLD_PROPERTY_MAX
|
||||
};
|
||||
|
||||
CSGBrush *n = _build_brush();
|
||||
static void _unpack_manifold(
|
||||
const manifold::Manifold &p_manifold,
|
||||
const HashMap<int32_t, Ref<Material>> &p_mesh_materials,
|
||||
CSGBrush *r_mesh_merge) {
|
||||
manifold::MeshGL64 mesh = p_manifold.GetMeshGL64();
|
||||
|
||||
for (int i = 0; i < get_child_count(); i++) {
|
||||
CSGShape3D *child = Object::cast_to<CSGShape3D>(get_child(i));
|
||||
if (!child) {
|
||||
continue;
|
||||
}
|
||||
if (!child->is_visible()) {
|
||||
continue;
|
||||
}
|
||||
constexpr int32_t order[3] = { 0, 2, 1 };
|
||||
|
||||
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 CSGShape3D::OPERATION_UNION:
|
||||
bop.merge_brushes(CSGBrushOperation::OPERATION_UNION, *n, *nn2, *nn, snap);
|
||||
break;
|
||||
case CSGShape3D::OPERATION_INTERSECTION:
|
||||
bop.merge_brushes(CSGBrushOperation::OPERATION_INTERSECTION, *n, *nn2, *nn, snap);
|
||||
break;
|
||||
case CSGShape3D::OPERATION_SUBTRACTION:
|
||||
bop.merge_brushes(CSGBrushOperation::OPERATION_SUBTRACTION, *n, *nn2, *nn, snap);
|
||||
break;
|
||||
}
|
||||
memdelete(n);
|
||||
memdelete(nn2);
|
||||
n = nn;
|
||||
}
|
||||
for (size_t run_i = 0; run_i < mesh.runIndex.size() - 1; run_i++) {
|
||||
uint32_t original_id = -1;
|
||||
if (run_i < mesh.runOriginalID.size()) {
|
||||
original_id = mesh.runOriginalID[run_i];
|
||||
}
|
||||
|
||||
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();
|
||||
Ref<Material> material;
|
||||
if (p_mesh_materials.has(original_id)) {
|
||||
material = p_mesh_materials[original_id];
|
||||
}
|
||||
// Find or reserve a material ID in the brush.
|
||||
int32_t material_id = r_mesh_merge->materials.find(material);
|
||||
if (material_id == -1) {
|
||||
material_id = r_mesh_merge->materials.size();
|
||||
r_mesh_merge->materials.push_back(material);
|
||||
}
|
||||
|
||||
brush = n;
|
||||
size_t begin = mesh.runIndex[run_i];
|
||||
size_t end = mesh.runIndex[run_i + 1];
|
||||
for (size_t vert_i = begin; vert_i < end; vert_i += 3) {
|
||||
CSGBrush::Face face;
|
||||
face.material = material_id;
|
||||
int32_t first_property_index = mesh.triVerts[vert_i + order[0]];
|
||||
face.smooth = mesh.vertProperties[first_property_index * mesh.numProp + MANIFOLD_PROPERTY_SMOOTH_GROUP] > 0.5f;
|
||||
face.invert = mesh.vertProperties[first_property_index * mesh.numProp + MANIFOLD_PROPERTY_INVERT] > 0.5f;
|
||||
|
||||
dirty = false;
|
||||
for (int32_t tri_order_i = 0; tri_order_i < 3; tri_order_i++) {
|
||||
int32_t property_i = mesh.triVerts[vert_i + order[tri_order_i]];
|
||||
ERR_FAIL_COND_MSG(property_i * mesh.numProp >= mesh.vertProperties.size(), "Invalid index into vertex properties");
|
||||
face.vertices[tri_order_i] = Vector3(
|
||||
mesh.vertProperties[property_i * mesh.numProp + MANIFOLD_PROPERTY_POSITION_X],
|
||||
mesh.vertProperties[property_i * mesh.numProp + MANIFOLD_PROPERTY_POSITION_Y],
|
||||
mesh.vertProperties[property_i * mesh.numProp + MANIFOLD_PROPERTY_POSITION_Z]);
|
||||
face.uvs[tri_order_i] = Vector2(
|
||||
mesh.vertProperties[property_i * mesh.numProp + MANIFOLD_PROPERTY_UV_X_0],
|
||||
mesh.vertProperties[property_i * mesh.numProp + MANIFOLD_PROPERTY_UV_Y_0]);
|
||||
}
|
||||
r_mesh_merge->faces.push_back(face);
|
||||
}
|
||||
}
|
||||
|
||||
r_mesh_merge->_regen_face_aabbs();
|
||||
}
|
||||
|
||||
static void _pack_manifold(
|
||||
const CSGBrush *const p_mesh_merge,
|
||||
manifold::Manifold &r_manifold,
|
||||
HashMap<int32_t, Ref<Material>> &p_mesh_materials,
|
||||
float p_snap) {
|
||||
ERR_FAIL_NULL_MSG(p_mesh_merge, "p_mesh_merge is null");
|
||||
|
||||
HashMap<uint32_t, Vector<CSGBrush::Face>> faces_by_material;
|
||||
for (int face_i = 0; face_i < p_mesh_merge->faces.size(); face_i++) {
|
||||
const CSGBrush::Face &face = p_mesh_merge->faces[face_i];
|
||||
faces_by_material[face.material].push_back(face);
|
||||
}
|
||||
|
||||
manifold::MeshGL64 mesh;
|
||||
mesh.tolerance = p_snap;
|
||||
mesh.numProp = MANIFOLD_PROPERTY_MAX;
|
||||
mesh.runOriginalID.reserve(faces_by_material.size());
|
||||
mesh.runIndex.reserve(faces_by_material.size() + 1);
|
||||
mesh.vertProperties.reserve(p_mesh_merge->faces.size() * 3 * MANIFOLD_PROPERTY_MAX);
|
||||
|
||||
// Make a run of triangles for each material.
|
||||
for (const KeyValue<uint32_t, Vector<CSGBrush::Face>> &E : faces_by_material) {
|
||||
const uint32_t material_id = E.key;
|
||||
const Vector<CSGBrush::Face> &faces = E.value;
|
||||
mesh.runIndex.push_back(mesh.triVerts.size());
|
||||
|
||||
// Associate the material with an ID.
|
||||
uint32_t reserved_id = r_manifold.ReserveIDs(1);
|
||||
mesh.runOriginalID.push_back(reserved_id);
|
||||
Ref<Material> material;
|
||||
if (material_id < p_mesh_merge->materials.size()) {
|
||||
material = p_mesh_merge->materials[material_id];
|
||||
}
|
||||
|
||||
p_mesh_materials.insert(reserved_id, material);
|
||||
for (const CSGBrush::Face &face : faces) {
|
||||
for (int32_t tri_order_i = 0; tri_order_i < 3; tri_order_i++) {
|
||||
constexpr int32_t order[3] = { 0, 2, 1 };
|
||||
int i = order[tri_order_i];
|
||||
|
||||
mesh.triVerts.push_back(mesh.vertProperties.size() / MANIFOLD_PROPERTY_MAX);
|
||||
|
||||
size_t begin = mesh.vertProperties.size();
|
||||
mesh.vertProperties.resize(mesh.vertProperties.size() + MANIFOLD_PROPERTY_MAX);
|
||||
// Add the vertex properties.
|
||||
// Use CSGBrush constants rather than push_back for clarity.
|
||||
double *vert = &mesh.vertProperties[begin];
|
||||
vert[MANIFOLD_PROPERTY_POSITION_X] = face.vertices[i].x;
|
||||
vert[MANIFOLD_PROPERTY_POSITION_Y] = face.vertices[i].y;
|
||||
vert[MANIFOLD_PROPERTY_POSITION_Z] = face.vertices[i].z;
|
||||
vert[MANIFOLD_PROPERTY_UV_X_0] = face.uvs[i].x;
|
||||
vert[MANIFOLD_PROPERTY_UV_Y_0] = face.uvs[i].y;
|
||||
vert[MANIFOLD_PROPERTY_SMOOTH_GROUP] = face.smooth ? 1.0f : 0.0f;
|
||||
vert[MANIFOLD_PROPERTY_INVERT] = face.invert ? 1.0f : 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
// runIndex needs an explicit end value.
|
||||
mesh.runIndex.push_back(mesh.triVerts.size());
|
||||
ERR_FAIL_COND_MSG(mesh.vertProperties.size() % mesh.numProp != 0, "Invalid vertex properties size.");
|
||||
mesh.Merge();
|
||||
r_manifold = manifold::Manifold(mesh);
|
||||
manifold::Manifold::Error err = r_manifold.Status();
|
||||
if (err != manifold::Manifold::Error::NoError) {
|
||||
print_error(String("Manifold creation from mesh failed:" + itos((int)err)));
|
||||
}
|
||||
}
|
||||
|
||||
struct ManifoldOperation {
|
||||
manifold::Manifold manifold;
|
||||
manifold::OpType operation;
|
||||
static manifold::OpType convert_csg_op(CSGShape3D::Operation op) {
|
||||
switch (op) {
|
||||
case CSGShape3D::OPERATION_SUBTRACTION:
|
||||
return manifold::OpType::Subtract;
|
||||
case CSGShape3D::OPERATION_INTERSECTION:
|
||||
return manifold::OpType::Intersect;
|
||||
default:
|
||||
return manifold::OpType::Add;
|
||||
}
|
||||
}
|
||||
ManifoldOperation() :
|
||||
operation(manifold::OpType::Add) {}
|
||||
ManifoldOperation(const manifold::Manifold &m, manifold::OpType op) :
|
||||
manifold(m), operation(op) {}
|
||||
};
|
||||
|
||||
CSGBrush *CSGShape3D::_get_brush() {
|
||||
if (!dirty) {
|
||||
return brush;
|
||||
}
|
||||
if (brush) {
|
||||
memdelete(brush);
|
||||
}
|
||||
brush = nullptr;
|
||||
CSGBrush *n = _build_brush();
|
||||
HashMap<int32_t, Ref<Material>> mesh_materials;
|
||||
manifold::Manifold root_manifold;
|
||||
_pack_manifold(n, root_manifold, mesh_materials, get_snap());
|
||||
manifold::OpType current_op = ManifoldOperation::convert_csg_op(get_operation());
|
||||
std::vector<manifold::Manifold> manifolds;
|
||||
manifolds.push_back(root_manifold);
|
||||
for (int i = 0; i < get_child_count(); i++) {
|
||||
CSGShape3D *child = Object::cast_to<CSGShape3D>(get_child(i));
|
||||
if (!child || !child->is_visible()) {
|
||||
continue;
|
||||
}
|
||||
CSGBrush *child_brush = child->_get_brush();
|
||||
if (!child_brush) {
|
||||
continue;
|
||||
}
|
||||
CSGBrush transformed_brush;
|
||||
transformed_brush.copy_from(*child_brush, child->get_transform());
|
||||
manifold::Manifold child_manifold;
|
||||
_pack_manifold(&transformed_brush, child_manifold, mesh_materials, get_snap());
|
||||
manifold::OpType child_operation = ManifoldOperation::convert_csg_op(child->get_operation());
|
||||
if (child_operation != current_op) {
|
||||
manifold::Manifold result = manifold::Manifold::BatchBoolean(manifolds, current_op);
|
||||
manifolds.clear();
|
||||
manifolds.push_back(result);
|
||||
current_op = child_operation;
|
||||
}
|
||||
manifolds.push_back(child_manifold);
|
||||
}
|
||||
if (!manifolds.empty()) {
|
||||
manifold::Manifold manifold_result = manifold::Manifold::BatchBoolean(manifolds, current_op);
|
||||
if (n) {
|
||||
memdelete(n);
|
||||
}
|
||||
n = memnew(CSGBrush);
|
||||
_unpack_manifold(manifold_result, mesh_materials, n);
|
||||
}
|
||||
AABB aabb;
|
||||
if (n && !n->faces.is_empty()) {
|
||||
aabb.position = n->faces[0].vertices[0];
|
||||
for (const CSGBrush::Face &face : n->faces) {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
aabb.expand_to(face.vertices[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
node_aabb = aabb;
|
||||
brush = n;
|
||||
dirty = false;
|
||||
return brush;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue