Skip to content

Commit

Permalink
Add convex hull CSG node
Browse files Browse the repository at this point in the history
Support a material, add an icon, add simple documentation.

Co-Authored-By: 31 <31eee384@gmail.com>
  • Loading branch information
31 authored and fire committed Jun 16, 2024
1 parent 2fc5f5f commit 58516f7
Show file tree
Hide file tree
Showing 5 changed files with 208 additions and 9 deletions.
22 changes: 22 additions & 0 deletions doc/classes/CSGConvexHull3D.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGConvexHull3D" inherits="CSGPrimitive3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
A CSG convex hull shape.
</brief_description>
<description>
This node creates a mesh based on the convex hull of a set of points 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 [MeshInstance3D] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay.
</description>
<tutorials>
<link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link>
</tutorials>
<members>
<member name="material" type="Material" setter="set_material" getter="get_material">
The [Material] used in drawing the CSG shape.
</member>
<member name="points" type="PackedVector3Array" setter="set_points" getter="get_points" default="PackedVector3Array(0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1)">
The point array that defines the convex hull.
[b]Note:[/b] If fewer than four points are defined or all points are coplanar, no mesh is generated.
</member>
</members>
</class>
107 changes: 98 additions & 9 deletions modules/csg/csg_shape.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,16 @@ struct CSGBrush {
}
manifold::MeshGL mesh;
mesh.runOriginalID.push_back(reserved_id);
mesh.triVerts.resize(current_brush.faces.size() * CSGBrush::MANIFOLD_TRIANGLE, 0);
mesh.vertProperties.resize(current_brush.faces.size() * CSGBrush::MANIFOLD_TRIANGLE * CSGBrush::MANIFOLD_MAX, std::numeric_limits<float>::quiet_NaN());
size_t total_vertices = current_brush.faces.size() * CSGBrush::MANIFOLD_TRIANGLE;
mesh.vertProperties.resize(total_vertices * CSGBrush::MANIFOLD_MAX);
mesh.triVerts.resize(total_vertices);
mesh.numProp = CSGBrush::MANIFOLD_MAX;
constexpr size_t order[CSGBrush::MANIFOLD_TRIANGLE] = { 0, 2, 1 };
for (CowData<CSGBrush::Face>::Size face_i = 0; face_i < current_brush.faces.size(); face_i++) {
const CSGBrush::Face &face = current_brush.faces[face_i];
for (size_t vertex_i = 0; vertex_i < CSGBrush::MANIFOLD_TRIANGLE; vertex_i++) {
int32_t index = face_i * CSGBrush::MANIFOLD_TRIANGLE + vertex_i;
size_t index = face_i * CSGBrush::MANIFOLD_TRIANGLE + vertex_i;
ERR_BREAK(index >= total_vertices);
mesh.triVerts[face_i * CSGBrush::MANIFOLD_TRIANGLE + order[vertex_i]] = index;
Vector3 pos = face.vertices[vertex_i];
Vector2 uv = face.uvs[vertex_i];
Expand Down Expand Up @@ -154,12 +156,34 @@ struct CSGBrush {
size_t material_index = 0;
size_t smooth_index = 0;
size_t invert_index = 0;
for (size_t property_i = 0; property_i < mesh.vertProperties.size(); property_i += CSGBrush::MANIFOLD_MAX) {
manifold_positions[position_index++] = Vector3(mesh.vertProperties[property_i + CSGBrush::MANIFOLD_PROPERTY_POS_X], mesh.vertProperties[property_i + CSGBrush::MANIFOLD_PROPERTY_POS_Y], mesh.vertProperties[property_i + MANIFOLD_PROPERTY_POS_Z]);
manifold_uvs[uv_index++] = Vector2(mesh.vertProperties[property_i + CSGBrush::MANIFOLD_PROPERTY_UV_X], mesh.vertProperties[property_i + CSGBrush::MANIFOLD_PROPERTY_UV_Y]);
manifold_materials[material_index++] = static_cast<int>(Math::round(mesh.vertProperties[property_i + CSGBrush::MANIFOLD_PROPERTY_MATERIAL]));
manifold_smooths[smooth_index++] = mesh.vertProperties[property_i + CSGBrush::MANIFOLD_PROPERTY_SMOOTH] > 0.5f;
manifold_inverts[invert_index++] = mesh.vertProperties[property_i + CSGBrush::MANIFOLD_PROPERTY_INVERT] > 0.5f;
for (size_t property_i = 0; property_i < mesh.vertProperties.size(); property_i += mesh.numProp) {
// Position is guaranteed. Others are not.
manifold_positions[position_index++] = Vector3(
mesh.vertProperties[property_i + CSGBrush::MANIFOLD_PROPERTY_POS_X],
mesh.vertProperties[property_i + CSGBrush::MANIFOLD_PROPERTY_POS_Y],
mesh.vertProperties[property_i + CSGBrush::MANIFOLD_PROPERTY_POS_Z]);
if (mesh.numProp >= CSGBrush::MANIFOLD_PROPERTY_UV_X) {
manifold_uvs[uv_index++] = Vector2(
mesh.vertProperties[property_i + CSGBrush::MANIFOLD_PROPERTY_UV_X],
mesh.vertProperties[property_i + CSGBrush::MANIFOLD_PROPERTY_UV_Y]);
} else {
manifold_uvs[uv_index++] = Vector2(0, 0);
}
if (mesh.numProp >= CSGBrush::MANIFOLD_PROPERTY_MATERIAL) {
manifold_materials[material_index++] = static_cast<int>(Math::round(mesh.vertProperties[property_i + CSGBrush::MANIFOLD_PROPERTY_MATERIAL]));
} else {
manifold_materials[material_index++] = 0;
}
if (mesh.numProp >= CSGBrush::MANIFOLD_PROPERTY_SMOOTH) {
manifold_smooths[smooth_index++] = mesh.vertProperties[property_i + CSGBrush::MANIFOLD_PROPERTY_SMOOTH] > 0.5f;
} else {
manifold_smooths[smooth_index++] = false;
}
if (mesh.numProp >= CSGBrush::MANIFOLD_PROPERTY_INVERT) {
manifold_inverts[invert_index++] = mesh.vertProperties[property_i + CSGBrush::MANIFOLD_PROPERTY_INVERT] > 0.5f;
} else {
manifold_inverts[invert_index++] = false;
}
}
faces.resize(mesh.triVerts.size() / CSGBrush::MANIFOLD_TRIANGLE);
constexpr int32_t order[CSGBrush::MANIFOLD_TRIANGLE] = { 0, 2, 1 };
Expand Down Expand Up @@ -2743,3 +2767,68 @@ CSGPolygon3D::CSGPolygon3D() {
path_joined = false;
path = nullptr;
}

///////////////

CSGBrush *CSGConvexHull3D::_build_brush() {
CSGBrush *new_brush = memnew(CSGBrush);

if (points.size() < 4) {
return new_brush;
}

std::vector<glm::vec3> converted_points;
for (int i = 0; i < points.size(); i++) {
converted_points.push_back(glm::vec3(points[i].x, points[i].y, points[i].z));
}

new_brush->manifold = new_brush->manifold.Hull(converted_points);

HashMap<uint32_t, Ref<Material>> mesh_materials;
mesh_materials[0] = material;
new_brush->unpack_manifold(mesh_materials);

return new_brush;
}

void CSGConvexHull3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_points", "points"), &CSGConvexHull3D::set_points);
ClassDB::bind_method(D_METHOD("get_points"), &CSGConvexHull3D::get_points);

ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGConvexHull3D::set_material);
ClassDB::bind_method(D_METHOD("get_material"), &CSGConvexHull3D::get_material);

ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "points"), "set_points", "get_points");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "Material"), "set_material", "get_material");
}

void CSGConvexHull3D::set_points(const Vector<Vector3> &p_points) {
points = p_points;
_make_dirty();
update_gizmos();
}

Vector<Vector3> CSGConvexHull3D::get_points() const {
return points;
}

void CSGConvexHull3D::set_material(const Ref<Material> &p_material) {
material = p_material;
_make_dirty();
}

Ref<Material> CSGConvexHull3D::get_material() const {
return material;
}

CSGConvexHull3D::CSGConvexHull3D() {
// defaults
points.push_back(Vector3(0, 0, 0));
points.push_back(Vector3(0, 1, 0));
points.push_back(Vector3(1, 1, 0));
points.push_back(Vector3(1, 0, 0));
points.push_back(Vector3(0, 0, 1));
points.push_back(Vector3(0, 1, 1));
points.push_back(Vector3(1, 1, 1));
points.push_back(Vector3(1, 0, 1));
}
22 changes: 22 additions & 0 deletions modules/csg/csg_shape.h
Original file line number Diff line number Diff line change
Expand Up @@ -455,4 +455,26 @@ VARIANT_ENUM_CAST(CSGPolygon3D::Mode)
VARIANT_ENUM_CAST(CSGPolygon3D::PathRotation)
VARIANT_ENUM_CAST(CSGPolygon3D::PathIntervalType)

class CSGConvexHull3D : public CSGPrimitive3D {
GDCLASS(CSGConvexHull3D, CSGPrimitive3D);

private:
virtual CSGBrush *_build_brush() override;

Vector<Vector3> points;
Ref<Material> material;

protected:
static void _bind_methods();

public:
void set_points(const Vector<Vector3> &p_points);
Vector<Vector3> get_points() const;

void set_material(const Ref<Material> &p_material);
Ref<Material> get_material() const;

CSGConvexHull3D();
};

#endif // CSG_SHAPE_H
65 changes: 65 additions & 0 deletions modules/csg/icons/CSGConvexHull3D.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions modules/csg/register_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ void initialize_csg_module(ModuleInitializationLevel p_level) {
GDREGISTER_CLASS(CSGCylinder3D);
GDREGISTER_CLASS(CSGTorus3D);
GDREGISTER_CLASS(CSGPolygon3D);
GDREGISTER_CLASS(CSGConvexHull3D);
GDREGISTER_CLASS(CSGCombiner3D);
}
#ifdef TOOLS_ENABLED
Expand Down

0 comments on commit 58516f7

Please sign in to comment.