From c7925972b0dd8fbaf63aaebf8f996e0fb9b00989 Mon Sep 17 00:00:00 2001
From: trollodel <33117082+trollodel@users.noreply.github.com>
Date: Sat, 18 Sep 2021 09:26:34 +0200
Subject: [PATCH] Allow to use CSG without nodes
Add CSG shape resources.
Add CSGTool for CSG operations.
Make CSG nodes using CSG resources and CSGTool.
---
doc/classes/CSGTool.xml | 48 +
modules/csg/config.py | 7 +
modules/csg/csg_gizmos.cpp | 18 +-
modules/csg/csg_shape.cpp | 1600 ++-------------
modules/csg/csg_shape.h | 184 +-
modules/csg/csg_tool.cpp | 1720 +++++++++++++++++
modules/csg/csg_tool.h | 322 +++
modules/csg/doc_classes/CSGBox3D.xml | 3 -
modules/csg/doc_classes/CSGBoxShape3D.xml | 19 +
modules/csg/doc_classes/CSGCylinder3D.xml | 6 -
.../csg/doc_classes/CSGCylinderShape3D.xml | 28 +
modules/csg/doc_classes/CSGMesh3D.xml | 3 -
modules/csg/doc_classes/CSGMeshShape3D.xml | 19 +
modules/csg/doc_classes/CSGPolygon3D.xml | 8 +-
modules/csg/doc_classes/CSGPolygonShape3D.xml | 65 +
modules/csg/doc_classes/CSGPrimitive3D.xml | 4 +
.../csg/doc_classes/CSGPrimitiveShape3D.xml | 23 +
modules/csg/doc_classes/CSGSphere3D.xml | 6 -
modules/csg/doc_classes/CSGSphereShape3D.xml | 25 +
modules/csg/doc_classes/CSGTorus3D.xml | 6 -
modules/csg/doc_classes/CSGTorusShape3D.xml | 28 +
modules/csg/icons/CSGBoxShape3D.svg | 1 +
modules/csg/icons/CSGCapsuleShape3D.svg | 1 +
modules/csg/icons/CSGCylinderShape3D.svg | 1 +
modules/csg/icons/CSGMeshShape3D.svg | 1 +
modules/csg/icons/CSGPolygonShape3D.svg | 1 +
modules/csg/icons/CSGSphereShape3D.svg | 1 +
modules/csg/icons/CSGTorusShape3D.svg | 1 +
modules/csg/register_types.cpp | 9 +
29 files changed, 2540 insertions(+), 1618 deletions(-)
create mode 100644 doc/classes/CSGTool.xml
create mode 100644 modules/csg/csg_tool.cpp
create mode 100644 modules/csg/csg_tool.h
create mode 100644 modules/csg/doc_classes/CSGBoxShape3D.xml
create mode 100644 modules/csg/doc_classes/CSGCylinderShape3D.xml
create mode 100644 modules/csg/doc_classes/CSGMeshShape3D.xml
create mode 100644 modules/csg/doc_classes/CSGPolygonShape3D.xml
create mode 100644 modules/csg/doc_classes/CSGPrimitiveShape3D.xml
create mode 100644 modules/csg/doc_classes/CSGSphereShape3D.xml
create mode 100644 modules/csg/doc_classes/CSGTorusShape3D.xml
create mode 100644 modules/csg/icons/CSGBoxShape3D.svg
create mode 100644 modules/csg/icons/CSGCapsuleShape3D.svg
create mode 100644 modules/csg/icons/CSGCylinderShape3D.svg
create mode 100644 modules/csg/icons/CSGMeshShape3D.svg
create mode 100644 modules/csg/icons/CSGPolygonShape3D.svg
create mode 100644 modules/csg/icons/CSGSphereShape3D.svg
create mode 100644 modules/csg/icons/CSGTorusShape3D.svg
diff --git a/doc/classes/CSGTool.xml b/doc/classes/CSGTool.xml
new file mode 100644
index 000000000000..c809ff99128d
--- /dev/null
+++ b/doc/classes/CSGTool.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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/config.py b/modules/csg/config.py
index 3991b846f96f..1f74b6bbb54c 100644
--- a/modules/csg/config.py
+++ b/modules/csg/config.py
@@ -17,6 +17,13 @@ def get_doc_classes():
"CSGShape3D",
"CSGSphere3D",
"CSGTorus3D",
+ "CSGBoxShape3D",
+ "CSGCylinderShape3D",
+ "CSGMeshShape3D",
+ "CSGPolygonShape3D",
+ "CSGPrimitiveShape3D",
+ "CSGSphereShape3D",
+ "CSGTorusShape3D"
]
diff --git a/modules/csg/csg_gizmos.cpp b/modules/csg/csg_gizmos.cpp
index 2f8b354bb717..22bea8da3a18 100644
--- a/modules/csg/csg_gizmos.cpp
+++ b/modules/csg/csg_gizmos.cpp
@@ -324,16 +324,16 @@ void CSGShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
}
}
}
-
+ CSGTool::Operation op = static_cast(cs->get_operation());
Ref material;
- switch (cs->get_operation()) {
- case CSGShape3D::OPERATION_UNION:
+ switch (op) {
+ case CSGTool::OPERATION_UNION:
material = get_material("shape_union_material", p_gizmo);
break;
- case CSGShape3D::OPERATION_INTERSECTION:
+ case CSGTool::OPERATION_INTERSECTION:
material = get_material("shape_intersection_material", p_gizmo);
break;
- case CSGShape3D::OPERATION_SUBTRACTION:
+ case CSGTool::OPERATION_SUBTRACTION:
material = get_material("shape_subtraction_material", p_gizmo);
break;
}
@@ -352,14 +352,14 @@ void CSGShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, array);
Ref solid_material;
- switch (cs->get_operation()) {
- case CSGShape3D::OPERATION_UNION:
+ switch (op) {
+ case CSGTool::OPERATION_UNION:
solid_material = get_material("shape_union_solid_material", p_gizmo);
break;
- case CSGShape3D::OPERATION_INTERSECTION:
+ case CSGTool::OPERATION_INTERSECTION:
solid_material = get_material("shape_intersection_solid_material", p_gizmo);
break;
- case CSGShape3D::OPERATION_SUBTRACTION:
+ case CSGTool::OPERATION_SUBTRACTION:
solid_material = get_material("shape_subtraction_solid_material", p_gizmo);
break;
}
diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp
index bf11cc7f68ae..d75f8ca07e42 100644
--- a/modules/csg/csg_shape.cpp
+++ b/modules/csg/csg_shape.cpp
@@ -150,14 +150,12 @@ void CSGShape3D::_make_dirty() {
dirty = true;
}
-CSGBrush *CSGShape3D::_get_brush() {
+CSGBrush CSGShape3D::_get_brush() {
if (dirty) {
- if (brush) {
- memdelete(brush);
- }
- brush = nullptr;
+ csg_tool.instantiate();
- CSGBrush *n = _build_brush();
+ CSGBrush self_brush = _build_brush();
+ csg_tool->add_brush(self_brush, static_cast(operation), snap);
for (int i = 0; i < get_child_count(); i++) {
CSGShape3D *child = Object::cast_to(get_child(i));
@@ -168,115 +166,16 @@ CSGBrush *CSGShape3D::_get_brush() {
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 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_SUBSTRACTION, *n, *nn2, *nn, snap);
- break;
- }
- memdelete(n);
- memdelete(nn2);
- n = nn;
- }
+ CSGBrush child_brush = child->_get_brush();
+ CSGBrush n;
+ n.copy_from(child_brush, child->get_transform());
+ csg_tool->add_brush(n, static_cast(child->get_operation()), snap);
}
-
- 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;
-
+ node_aabb = csg_tool->get_aabb();
dirty = false;
}
- return brush;
-}
-
-int CSGShape3D::mikktGetNumFaces(const SMikkTSpaceContext *pContext) {
- ShapeUpdateSurface &surface = *((ShapeUpdateSurface *)pContext->m_pUserData);
-
- return surface.vertices.size() / 3;
-}
-
-int CSGShape3D::mikktGetNumVerticesOfFace(const SMikkTSpaceContext *pContext, const int iFace) {
- // always 3
- return 3;
-}
-
-void CSGShape3D::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 CSGShape3D::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 CSGShape3D::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 CSGShape3D::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;
+ return csg_tool->get_brush();
}
void CSGShape3D::_update_shape() {
@@ -287,174 +186,12 @@ void CSGShape3D::_update_shape() {
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;
-
- 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;
+ _get_brush();
+ root_mesh = csg_tool->commit(Variant(), calculate_tangents);
- if (i != surfaces.size() - 1) {
- surfaces.write[i].material = n->materials[i];
- }
-
- surfaces.write[i].verticesw = surfaces.write[i].vertices.ptrw();
- surfaces.write[i].normalsw = surfaces.write[i].normals.ptrw();
- surfaces.write[i].uvsw = surfaces.write[i].uvs.ptrw();
- if (calculate_tangents) {
- surfaces.write[i].tansw = surfaces.write[i].tans.ptrw();
- }
- }
-
- // Update collision faces.
if (root_collision_shape.is_valid()) {
- Vector physics_faces;
- physics_faces.resize(n->faces.size() * 3);
- Vector3 *physicsw = physics_faces.ptrw();
-
- 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);
- }
-
- //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.instantiate();
- //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);
- }
-
- 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);
+ Ref shape = csg_tool->create_trimesh_shape();
+ root_collision_shape->set_faces(shape->get_faces());
}
set_base(root_mesh->get_rid());
@@ -464,26 +201,9 @@ AABB CSGShape3D::get_aabb() const {
return node_aabb;
}
-Vector CSGShape3D::get_brush_faces() {
- ERR_FAIL_COND_V(!is_inside_tree(), Vector());
- CSGBrush *b = _get_brush();
- if (!b) {
- return Vector();
- }
-
- Vector faces;
- int fc = b->faces.size();
- faces.resize(fc * 3);
- {
- Vector3 *w = faces.ptrw();
- 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;
+Vector CSGShape3D::get_brush_faces() const {
+ ERR_FAIL_COND_V(!is_inside_tree() || csg_tool.is_null(), Vector());
+ return csg_tool->get_brush_faces();
}
Vector CSGShape3D::get_faces(uint32_t p_usage_flags) const {
@@ -635,20 +355,14 @@ void CSGShape3D::_bind_methods() {
}
CSGShape3D::CSGShape3D() {
+ csg_tool.instantiate();
set_notify_local_transform(true);
}
-CSGShape3D::~CSGShape3D() {
- if (brush) {
- memdelete(brush);
- brush = nullptr;
- }
-}
-
//////////////////////////////////
-CSGBrush *CSGCombiner3D::_build_brush() {
- return memnew(CSGBrush); //does not build anything
+CSGBrush CSGCombiner3D::_build_brush() {
+ return CSGBrush(); //does not build anything
}
CSGCombiner3D::CSGCombiner3D() {
@@ -656,233 +370,86 @@ CSGCombiner3D::CSGCombiner3D() {
/////////////////////
-CSGBrush *CSGPrimitive3D::_create_brush_from_arrays(const Vector &p_vertices, const Vector &p_uv, const Vector &p_smooth, const Vector[> &p_materials) {
- CSGBrush *brush = memnew(CSGBrush);
-
- Vector invert;
- invert.resize(p_vertices.size() / 3);
- {
- int ic = invert.size();
- bool *w = invert.ptrw();
- 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 CSGPrimitive3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_invert_faces", "invert_faces"), &CSGPrimitive3D::set_invert_faces);
ClassDB::bind_method(D_METHOD("is_inverting_faces"), &CSGPrimitive3D::is_inverting_faces);
+ ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGPrimitive3D::set_smooth_faces);
+ ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGPrimitive3D::get_smooth_faces);
+ ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGPrimitive3D::set_material);
+ ClassDB::bind_method(D_METHOD("get_material"), &CSGPrimitive3D::get_material);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "invert_faces"), "set_invert_faces", "is_inverting_faces");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material");
}
void CSGPrimitive3D::set_invert_faces(bool p_invert) {
- if (invert_faces == p_invert) {
- return;
- }
-
- invert_faces = p_invert;
-
+ Ref csg_primitive = get_csg_primitive();
+ csg_primitive->set_invert_faces(p_invert);
_make_dirty();
}
-bool CSGPrimitive3D::is_inverting_faces() {
- return invert_faces;
+bool CSGPrimitive3D::is_inverting_faces() const {
+ const Ref csg_primitive = get_csg_primitive();
+ return csg_primitive->is_inverting_faces();
}
-CSGPrimitive3D::CSGPrimitive3D() {
- invert_faces = false;
+void CSGPrimitive3D::set_smooth_faces(bool p_smooth_faces) {
+ Ref csg_primitive = get_csg_primitive();
+ csg_primitive->set_smooth_faces(p_smooth_faces);
+ _make_dirty();
}
-/////////////////////
-
-CSGBrush *CSGMesh3D::_build_brush() {
- if (!mesh.is_valid()) {
- return memnew(CSGBrush);
- }
-
- Vector vertices;
- Vector smooth;
- Vector][> materials;
- Vector 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));
- }
-
- Vector avertices = arrays[Mesh::ARRAY_VERTEX];
- if (avertices.size() == 0) {
- continue;
- }
-
- const Vector3 *vr = avertices.ptr();
-
- Vector anormals = arrays[Mesh::ARRAY_NORMAL];
- const Vector3 *nr = nullptr;
- if (anormals.size()) {
- nr = anormals.ptr();
- }
-
- Vector auvs = arrays[Mesh::ARRAY_TEX_UV];
- const Vector2 *uvr = nullptr;
- if (auvs.size()) {
- uvr = auvs.ptr();
- }
+bool CSGPrimitive3D::get_smooth_faces() const {
+ const Ref csg_primitive = get_csg_primitive();
+ return csg_primitive->get_smooth_faces();
+}
- Ref mat;
- if (material.is_valid()) {
- mat = material;
- } else {
- mat = mesh->surface_get_material(i);
- }
+void CSGPrimitive3D::set_material(const Ref &p_material) {
+ Ref csg_primitive = get_csg_primitive();
+ csg_primitive->set_material(p_material);
+ _make_dirty();
+}
- Vector 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);
-
- Vector3 *vw = vertices.ptrw();
- bool *sw = smooth.ptrw();
- Vector2 *uvw = uvs.ptrw();
- Ref *mw = materials.ptrw();
-
- const int *ir = aindices.ptr();
-
- 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) {
- normal[k] = nr[idx];
- }
- if (uvr) {
- uv[k] = uvr[idx];
- }
- }
-
- bool flat = normal[0].is_equal_approx(normal[1]) && normal[0].is_equal_approx(normal[2]);
-
- 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);
-
- Vector3 *vw = vertices.ptrw();
- bool *sw = smooth.ptrw();
- Vector2 *uvw = uvs.ptrw();
- Ref *mw = materials.ptrw();
-
- 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) {
- normal[k] = nr[j + k];
- }
- if (uvr) {
- uv[k] = uvr[j + k];
- }
- }
-
- bool flat = normal[0].is_equal_approx(normal[1]) && normal[0].is_equal_approx(normal[2]);
-
- 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;
- }
- }
- }
+Ref CSGPrimitive3D::get_material() const {
+ const Ref csg_primitive = get_csg_primitive();
+ return csg_primitive->get_material();
+}
- if (vertices.size() == 0) {
- return memnew(CSGBrush);
+CSGBrush CSGPrimitive3D::_build_brush() {
+ Ref csg_primitive = get_csg_primitive();
+ if (csg_primitive.is_null()) {
+ return CSGBrush();
+ } else {
+ CSGBrush brush = csg_primitive->get_brush();
+ return brush;
}
-
- return _create_brush_from_arrays(vertices, uvs, smooth, materials);
}
+/////////////////////
+
void CSGMesh3D::_mesh_changed() {
_make_dirty();
update_gizmos();
}
-void CSGMesh3D::set_material(const Ref &p_material) {
- if (material == p_material) {
- return;
- }
- material = p_material;
- _make_dirty();
-}
-
-Ref CSGMesh3D::get_material() const {
- return material;
-}
-
void CSGMesh3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &CSGMesh3D::set_mesh);
ClassDB::bind_method(D_METHOD("get_mesh"), &CSGMesh3D::get_mesh);
- ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGMesh3D::set_material);
- ClassDB::bind_method(D_METHOD("get_material"), &CSGMesh3D::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, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material");
}
void CSGMesh3D::set_mesh(const Ref &p_mesh) {
+ Ref mesh = csg_primitive->get_mesh();
if (mesh == p_mesh) {
return;
}
if (mesh.is_valid()) {
mesh->disconnect("changed", callable_mp(this, &CSGMesh3D::_mesh_changed));
}
- mesh = p_mesh;
+ csg_primitive->set_mesh(p_mesh);
+ mesh = csg_primitive->get_mesh();
if (mesh.is_valid()) {
mesh->connect("changed", callable_mp(this, &CSGMesh3D::_mesh_changed));
@@ -891,131 +458,16 @@ void CSGMesh3D::set_mesh(const Ref &p_mesh) {
_mesh_changed();
}
-Ref CSGMesh3D::get_mesh() {
- return mesh;
+Ref CSGMesh3D::get_mesh() const {
+ return csg_primitive->get_mesh();
}
-////////////////////////////////
-
-CSGBrush *CSGSphere3D::_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();
-
- Vector faces;
- Vector uvs;
- Vector smooth;
- Vector][> materials;
- Vector invert;
-
- faces.resize(face_count * 3);
- uvs.resize(face_count * 3);
-
- smooth.resize(face_count);
- materials.resize(face_count);
- invert.resize(face_count);
-
- {
- Vector3 *facesw = faces.ptrw();
- Vector2 *uvsw = uvs.ptrw();
- bool *smoothw = smooth.ptrw();
- Ref *materialsw = materials.ptrw();
- bool *invertw = invert.ptrw();
-
- // 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);
- 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;
+CSGMesh3D::CSGMesh3D() {
+ csg_primitive.instantiate();
}
+////////////////////////////////
+
void CSGSphere3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_radius", "radius"), &CSGSphere3D::set_radius);
ClassDB::bind_method(D_METHOD("get_radius"), &CSGSphere3D::get_radius);
@@ -1025,343 +477,70 @@ void CSGSphere3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_rings", "rings"), &CSGSphere3D::set_rings);
ClassDB::bind_method(D_METHOD("get_rings"), &CSGSphere3D::get_rings);
- ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGSphere3D::set_smooth_faces);
- ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGSphere3D::get_smooth_faces);
-
- ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGSphere3D::set_material);
- ClassDB::bind_method(D_METHOD("get_material"), &CSGSphere3D::get_material);
-
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "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, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material");
}
void CSGSphere3D::set_radius(const float p_radius) {
- ERR_FAIL_COND(p_radius <= 0);
- radius = p_radius;
+ csg_primitive->set_radius(p_radius);
_make_dirty();
update_gizmos();
}
float CSGSphere3D::get_radius() const {
- return radius;
+ return csg_primitive->get_radius();
}
void CSGSphere3D::set_radial_segments(const int p_radial_segments) {
- radial_segments = p_radial_segments > 4 ? p_radial_segments : 4;
+ csg_primitive->set_radial_segments(p_radial_segments);
_make_dirty();
update_gizmos();
}
int CSGSphere3D::get_radial_segments() const {
- return radial_segments;
+ return csg_primitive->get_radial_segments();
}
void CSGSphere3D::set_rings(const int p_rings) {
- rings = p_rings > 1 ? p_rings : 1;
+ csg_primitive->set_rings(p_rings);
_make_dirty();
update_gizmos();
}
int CSGSphere3D::get_rings() const {
- return rings;
-}
-
-void CSGSphere3D::set_smooth_faces(const bool p_smooth_faces) {
- smooth_faces = p_smooth_faces;
- _make_dirty();
-}
-
-bool CSGSphere3D::get_smooth_faces() const {
- return smooth_faces;
-}
-
-void CSGSphere3D::set_material(const Ref &p_material) {
- material = p_material;
- _make_dirty();
-}
-
-Ref CSGSphere3D::get_material() const {
- return material;
+ return csg_primitive->get_rings();
}
CSGSphere3D::CSGSphere3D() {
- // defaults
- radius = 1.0;
- radial_segments = 12;
- rings = 6;
- smooth_faces = true;
+ csg_primitive.instantiate();
}
///////////////
-CSGBrush *CSGBox3D::_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();
-
- Vector faces;
- Vector uvs;
- Vector smooth;
- Vector][> materials;
- Vector invert;
-
- faces.resize(face_count * 3);
- uvs.resize(face_count * 3);
-
- smooth.resize(face_count);
- materials.resize(face_count);
- invert.resize(face_count);
-
- {
- Vector3 *facesw = faces.ptrw();
- Vector2 *uvsw = uvs.ptrw();
- bool *smoothw = smooth.ptrw();
- Ref *materialsw = materials.ptrw();
- bool *invertw = invert.ptrw();
-
- int face = 0;
-
- Vector3 vertex_mul = size / 2;
-
- {
- 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 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] = 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 CSGBox3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_size", "size"), &CSGBox3D::set_size);
ClassDB::bind_method(D_METHOD("get_size"), &CSGBox3D::get_size);
- ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGBox3D::set_material);
- ClassDB::bind_method(D_METHOD("get_material"), &CSGBox3D::get_material);
-
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size"), "set_size", "get_size");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material");
}
void CSGBox3D::set_size(const Vector3 &p_size) {
- size = p_size;
+ csg_primitive->set_size(p_size);
_make_dirty();
update_gizmos();
}
Vector3 CSGBox3D::get_size() const {
- return size;
+ return csg_primitive->get_size();
}
-void CSGBox3D::set_material(const Ref &p_material) {
- material = p_material;
- _make_dirty();
- update_gizmos();
-}
-
-Ref CSGBox3D::get_material() const {
- return material;
+CSGBox3D::CSGBox3D() {
+ csg_primitive.instantiate();
}
///////////////
-CSGBrush *CSGCylinder3D::_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();
-
- Vector faces;
- Vector uvs;
- Vector smooth;
- Vector][> materials;
- Vector invert;
-
- faces.resize(face_count * 3);
- uvs.resize(face_count * 3);
-
- smooth.resize(face_count);
- materials.resize(face_count);
- invert.resize(face_count);
-
- {
- Vector3 *facesw = faces.ptrw();
- Vector2 *uvsw = uvs.ptrw();
- bool *smoothw = smooth.ptrw();
- Ref *materialsw = materials.ptrw();
- bool *invertw = invert.ptrw();
-
- 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;
-
- float ang = inc * Math_TAU;
- float ang_n = inc_n * Math_TAU;
-
- 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 CSGCylinder3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_radius", "radius"), &CSGCylinder3D::set_radius);
ClassDB::bind_method(D_METHOD("get_radius"), &CSGCylinder3D::get_radius);
@@ -1375,211 +554,57 @@ void CSGCylinder3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_cone", "cone"), &CSGCylinder3D::set_cone);
ClassDB::bind_method(D_METHOD("is_cone"), &CSGCylinder3D::is_cone);
- ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGCylinder3D::set_material);
- ClassDB::bind_method(D_METHOD("get_material"), &CSGCylinder3D::get_material);
-
- ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGCylinder3D::set_smooth_faces);
- ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGCylinder3D::get_smooth_faces);
-
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_radius", "get_radius");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "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, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material");
}
void CSGCylinder3D::set_radius(const float p_radius) {
- radius = p_radius;
+ csg_primitive->set_radius(p_radius);
_make_dirty();
update_gizmos();
}
float CSGCylinder3D::get_radius() const {
- return radius;
+ return csg_primitive->get_radius();
}
void CSGCylinder3D::set_height(const float p_height) {
- height = p_height;
+ csg_primitive->set_height(p_height);
_make_dirty();
update_gizmos();
}
float CSGCylinder3D::get_height() const {
- return height;
+ return csg_primitive->get_height();
}
void CSGCylinder3D::set_sides(const int p_sides) {
- ERR_FAIL_COND(p_sides < 3);
- sides = p_sides;
+ csg_primitive->set_sides(p_sides);
_make_dirty();
update_gizmos();
}
int CSGCylinder3D::get_sides() const {
- return sides;
+ return csg_primitive->get_sides();
}
void CSGCylinder3D::set_cone(const bool p_cone) {
- cone = p_cone;
+ csg_primitive->set_cone(p_cone);
_make_dirty();
update_gizmos();
}
bool CSGCylinder3D::is_cone() const {
- return cone;
-}
-
-void CSGCylinder3D::set_smooth_faces(const bool p_smooth_faces) {
- smooth_faces = p_smooth_faces;
- _make_dirty();
-}
-
-bool CSGCylinder3D::get_smooth_faces() const {
- return smooth_faces;
-}
-
-void CSGCylinder3D::set_material(const Ref &p_material) {
- material = p_material;
- _make_dirty();
-}
-
-Ref CSGCylinder3D::get_material() const {
- return material;
+ return csg_primitive->is_cone();
}
CSGCylinder3D::CSGCylinder3D() {
- // defaults
- radius = 1.0;
- height = 1.0;
- sides = 8;
- cone = false;
- smooth_faces = true;
+ csg_primitive.instantiate();
}
-///////////////
-
-CSGBrush *CSGTorus3D::_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();
-
- Vector faces;
- Vector uvs;
- Vector smooth;
- Vector][> materials;
- Vector invert;
-
- faces.resize(face_count * 3);
- uvs.resize(face_count * 3);
-
- smooth.resize(face_count);
- materials.resize(face_count);
- invert.resize(face_count);
-
- {
- Vector3 *facesw = faces.ptrw();
- Vector2 *uvsw = uvs.ptrw();
- bool *smoothw = smooth.ptrw();
- Ref *materialsw = materials.ptrw();
- bool *invertw = invert.ptrw();
-
- int face = 0;
-
- {
- for (int i = 0; i < sides; i++) {
- float inci = float(i) / sides;
- float inci_n = float((i + 1)) / sides;
-
- float angi = inci * Math_TAU;
- float angi_n = inci_n * Math_TAU;
-
- 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;
-
- float angj = incj * Math_TAU;
- float angj_n = incj_n * Math_TAU;
-
- 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 CSGTorus3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_inner_radius", "radius"), &CSGTorus3D::set_inner_radius);
@@ -1590,393 +615,96 @@ void CSGTorus3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_sides", "sides"), &CSGTorus3D::set_sides);
ClassDB::bind_method(D_METHOD("get_sides"), &CSGTorus3D::get_sides);
-
- ClassDB::bind_method(D_METHOD("set_ring_sides", "sides"), &CSGTorus3D::set_ring_sides);
+ ClassDB::bind_method(D_METHOD("set_ring_sides", "ring_sides"), &CSGTorus3D::set_ring_sides);
ClassDB::bind_method(D_METHOD("get_ring_sides"), &CSGTorus3D::get_ring_sides);
- ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGTorus3D::set_material);
- ClassDB::bind_method(D_METHOD("get_material"), &CSGTorus3D::get_material);
-
- ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGTorus3D::set_smooth_faces);
- ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGTorus3D::get_smooth_faces);
-
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inner_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_inner_radius", "get_inner_radius");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "outer_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "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, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material");
}
void CSGTorus3D::set_inner_radius(const float p_inner_radius) {
- inner_radius = p_inner_radius;
+ csg_primitive->set_inner_radius(p_inner_radius);
_make_dirty();
update_gizmos();
}
float CSGTorus3D::get_inner_radius() const {
- return inner_radius;
+ return csg_primitive->get_inner_radius();
}
void CSGTorus3D::set_outer_radius(const float p_outer_radius) {
- outer_radius = p_outer_radius;
+ csg_primitive->set_outer_radius(p_outer_radius);
_make_dirty();
update_gizmos();
}
float CSGTorus3D::get_outer_radius() const {
- return outer_radius;
+ return csg_primitive->get_outer_radius();
}
void CSGTorus3D::set_sides(const int p_sides) {
- ERR_FAIL_COND(p_sides < 3);
- sides = p_sides;
+ csg_primitive->set_sides(p_sides);
_make_dirty();
update_gizmos();
}
int CSGTorus3D::get_sides() const {
- return sides;
+ return csg_primitive->get_sides();
}
void CSGTorus3D::set_ring_sides(const int p_ring_sides) {
- ERR_FAIL_COND(p_ring_sides < 3);
- ring_sides = p_ring_sides;
+ csg_primitive->set_ring_sides(p_ring_sides);
_make_dirty();
update_gizmos();
}
int CSGTorus3D::get_ring_sides() const {
- return ring_sides;
-}
-
-void CSGTorus3D::set_smooth_faces(const bool p_smooth_faces) {
- smooth_faces = p_smooth_faces;
- _make_dirty();
-}
-
-bool CSGTorus3D::get_smooth_faces() const {
- return smooth_faces;
-}
-
-void CSGTorus3D::set_material(const Ref &p_material) {
- material = p_material;
- _make_dirty();
-}
-
-Ref CSGTorus3D::get_material() const {
- return material;
+ return csg_primitive->get_ring_sides();
}
CSGTorus3D::CSGTorus3D() {
// defaults
- inner_radius = 2.0;
- outer_radius = 3.0;
- sides = 8;
- ring_sides = 6;
- smooth_faces = true;
+ csg_primitive.instantiate();
}
///////////////
-CSGBrush *CSGPolygon3D::_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.reverse();
- }
- int shape_sides = shape_polygon.size();
- Vector shape_faces = Geometry2D::triangulate_polygon(shape_polygon);
- ERR_FAIL_COND_V_MSG(shape_faces.size() < 3, brush, "Failed to triangulate CSGPolygon");
-
- // 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]);
- }
+CSGBrush CSGPolygon3D::_build_brush() {
+ // set our bounding box
- // If MODE_PATH, check if curve has changed.
- Ref curve;
- if (mode == MODE_PATH) {
- Path3D *current_path = Object::cast_to(get_node_or_null(path_node));
- if (path != current_path) {
- if (path) {
- path->disconnect("tree_exited", callable_mp(this, &CSGPolygon3D::_path_exited));
- path->disconnect("curve_changed", callable_mp(this, &CSGPolygon3D::_path_changed));
- }
- path = current_path;
- if (path) {
- path->connect("tree_exited", callable_mp(this, &CSGPolygon3D::_path_exited));
- path->connect("curve_changed", callable_mp(this, &CSGPolygon3D::_path_changed));
- }
+ if (csg_primitive->get_mode() == CSGPolygonShape3D::MODE_PATH) {
+ if (!has_node(path_node)) {
+ return CSGBrush();
}
-
+ Node *n = get_node(path_node);
+ if (!n) {
+ return CSGBrush();
+ }
+ Path3D *path = Object::cast_to(n);
if (!path) {
- return brush;
+ return CSGBrush();
}
- curve = path->get_curve();
- if (curve.is_null() || curve->get_point_count() < 2) {
- return brush;
+ Ref curve = path->get_curve();
+ if (curve.is_null()) {
+ return CSGBrush();
}
- }
-
- // Calculate the number 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;
- 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: {
- 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;
-
- // Intialize variables used to create the mesh.
- Ref material = get_material();
-
- Vector faces;
- Vector uvs;
- Vector smooth;
- Vector][> materials;
- Vector invert;
-
- faces.resize(face_count * 3);
- uvs.resize(face_count * 3);
- smooth.resize(face_count);
- materials.resize(face_count);
- invert.resize(face_count);
-
- Vector3 *facesw = faces.ptrw();
- Vector2 *uvsw = uvs.ptrw();
- bool *smoothw = smooth.ptrw();
- Ref *materialsw = materials.ptrw();
- bool *invertw = invert.ptrw();
-
- int face = 0;
- Transform3D base_xform;
- Transform3D current_xform;
- Transform3D previous_xform;
- double u_step = 1.0 / extrusions;
- 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);
+ if (curve->get_baked_length() <= 0) {
+ return CSGBrush();
}
- extrusion_step *= curve->get_baked_length();
- }
+ csg_primitive->set_path_curve(curve);
- if (mode == MODE_PATH) {
+ Transform3D path_to_this;
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;
- }
-
- Transform3D facing = Transform3D().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++;
- }
- }
-
- // Add extrusion faces.
- for (int x0 = 0; x0 < extrusions; x0++) {
- 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;
-
- 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;
- }
-
- Transform3D facing = Transform3D().looking_at(direction, current_up);
- current_xform = base_xform.translated(current_point) * facing;
- } break;
- }
-
- double u0 = x0 * 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++;
+ // center on paths origin
+ path_to_this = get_global_transform().affine_inverse() * path->get_global_transform();
}
+ csg_primitive->set_path_transform(path_to_this);
}
- ERR_FAIL_COND_V_MSG(face != face_count, brush, "Bug: Failed to create the CSGPolygon mesh correctly.");
-
- brush->build_from_faces(faces, uvs, smooth, materials, invert);
-
- return brush;
+ return CSGPrimitive3D::_build_brush();
}
void CSGPolygon3D::_notification(int p_what) {
@@ -1990,13 +718,14 @@ void CSGPolygon3D::_notification(int p_what) {
}
void CSGPolygon3D::_validate_property(PropertyInfo &property) const {
- if (property.name.begins_with("spin") && mode != MODE_SPIN) {
+ CSGPolygonShape3D::Mode mode = csg_primitive->get_mode();
+ if (property.name.begins_with("spin") && mode != CSGPolygonShape3D::MODE_SPIN) {
property.usage = PROPERTY_USAGE_NONE;
}
- if (property.name.begins_with("path") && mode != MODE_PATH) {
+ if (property.name.begins_with("path") && mode != CSGPolygonShape3D::MODE_PATH) {
property.usage = PROPERTY_USAGE_NONE;
}
- if (property.name == "depth" && mode != MODE_DEPTH) {
+ if (property.name == "depth" && mode != CSGPolygonShape3D::MODE_DEPTH) {
property.usage = PROPERTY_USAGE_NONE;
}
@@ -2046,12 +775,6 @@ void CSGPolygon3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_path_joined", "enable"), &CSGPolygon3D::set_path_joined);
ClassDB::bind_method(D_METHOD("is_path_joined"), &CSGPolygon3D::is_path_joined);
- ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGPolygon3D::set_material);
- ClassDB::bind_method(D_METHOD("get_material"), &CSGPolygon3D::get_material);
-
- ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGPolygon3D::set_smooth_faces);
- ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGPolygon3D::get_smooth_faces);
-
ClassDB::bind_method(D_METHOD("_is_editable_3d_polygon"), &CSGPolygon3D::_is_editable_3d_polygon);
ClassDB::bind_method(D_METHOD("_has_editable_3d_polygon_no_depth"), &CSGPolygon3D::_has_editable_3d_polygon_no_depth);
@@ -2066,8 +789,6 @@ void CSGPolygon3D::_bind_methods() {
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::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, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material");
BIND_ENUM_CONSTANT(MODE_DEPTH);
BIND_ENUM_CONSTANT(MODE_SPIN);
@@ -2079,66 +800,63 @@ void CSGPolygon3D::_bind_methods() {
}
void CSGPolygon3D::set_polygon(const Vector &p_polygon) {
- polygon = p_polygon;
+ csg_primitive->set_polygon(p_polygon);
_make_dirty();
update_gizmos();
}
Vector CSGPolygon3D::get_polygon() const {
- return polygon;
+ return csg_primitive->get_polygon();
}
void CSGPolygon3D::set_mode(Mode p_mode) {
- mode = p_mode;
+ csg_primitive->set_mode(static_cast(p_mode));
_make_dirty();
update_gizmos();
notify_property_list_changed();
}
CSGPolygon3D::Mode CSGPolygon3D::get_mode() const {
- return mode;
+ return static_cast(csg_primitive->get_mode());
}
void CSGPolygon3D::set_depth(const float p_depth) {
- ERR_FAIL_COND(p_depth < 0.001);
- depth = p_depth;
+ csg_primitive->set_depth(p_depth);
_make_dirty();
update_gizmos();
}
float CSGPolygon3D::get_depth() const {
- return depth;
+ return csg_primitive->get_depth();
}
void CSGPolygon3D::set_path_continuous_u(bool p_enable) {
- path_continuous_u = p_enable;
+ csg_primitive->set_path_continuous_u(p_enable);
_make_dirty();
}
bool CSGPolygon3D::is_path_continuous_u() const {
- return path_continuous_u;
+ return csg_primitive->is_path_continuous_u();
}
void CSGPolygon3D::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;
+ csg_primitive->set_spin_degrees(p_spin_degrees);
_make_dirty();
update_gizmos();
}
float CSGPolygon3D::get_spin_degrees() const {
- return spin_degrees;
+ return csg_primitive->get_spin_degrees();
}
void CSGPolygon3D::set_spin_sides(int p_spin_sides) {
- ERR_FAIL_COND(p_spin_sides < 3);
- spin_sides = p_spin_sides;
+ csg_primitive->set_spin_sides(p_spin_sides);
_make_dirty();
update_gizmos();
}
int CSGPolygon3D::get_spin_sides() const {
- return spin_sides;
+ return csg_primitive->get_spin_sides();
}
void CSGPolygon3D::set_path_node(const NodePath &p_path) {
@@ -2152,24 +870,23 @@ NodePath CSGPolygon3D::get_path_node() const {
}
void CSGPolygon3D::set_path_interval(float p_interval) {
- ERR_FAIL_COND_MSG(p_interval <= 0 || p_interval > 1, "Path interval must be greater than 0 and less than or equal to 1.0.");
- path_interval = p_interval;
+ csg_primitive->set_path_interval(p_interval);
_make_dirty();
update_gizmos();
}
float CSGPolygon3D::get_path_interval() const {
- return path_interval;
+ return csg_primitive->get_path_interval();
}
-void CSGPolygon3D::set_path_rotation(PathRotation p_rotation) {
- path_rotation = p_rotation;
+void CSGPolygon3D::set_path_rotation(CSGPolygon3D::PathRotation p_rotation) {
+ csg_primitive->set_path_rotation(static_cast(p_rotation));
_make_dirty();
update_gizmos();
}
CSGPolygon3D::PathRotation CSGPolygon3D::get_path_rotation() const {
- return path_rotation;
+ return static_cast(csg_primitive->get_path_rotation());
}
void CSGPolygon3D::set_path_local(bool p_enable) {
@@ -2183,31 +900,13 @@ bool CSGPolygon3D::is_path_local() const {
}
void CSGPolygon3D::set_path_joined(bool p_enable) {
- path_joined = p_enable;
+ csg_primitive->set_path_joined(p_enable);
_make_dirty();
update_gizmos();
}
bool CSGPolygon3D::is_path_joined() const {
- return path_joined;
-}
-
-void CSGPolygon3D::set_smooth_faces(const bool p_smooth_faces) {
- smooth_faces = p_smooth_faces;
- _make_dirty();
-}
-
-bool CSGPolygon3D::get_smooth_faces() const {
- return smooth_faces;
-}
-
-void CSGPolygon3D::set_material(const Ref &p_material) {
- material = p_material;
- _make_dirty();
-}
-
-Ref CSGPolygon3D::get_material() const {
- return material;
+ return csg_primitive->is_path_joined();
}
bool CSGPolygon3D::_is_editable_3d_polygon() const {
@@ -2219,20 +918,11 @@ bool CSGPolygon3D::_has_editable_3d_polygon_no_depth() const {
}
CSGPolygon3D::CSGPolygon3D() {
- // defaults
- mode = MODE_DEPTH;
+ csg_primitive.instantiate();
+ Vector polygon;
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 = 1.0;
- path_rotation = PATH_ROTATION_PATH_FOLLOW;
- path_local = false;
- path_continuous_u = true;
- path_joined = false;
- path = nullptr;
+ set_polygon(polygon);
}
diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h
index 5cf371665e67..84a0362c355f 100644
--- a/modules/csg/csg_shape.h
+++ b/modules/csg/csg_shape.h
@@ -33,28 +33,26 @@
#define CSGJS_HEADER_ONLY
-#include "csg.h"
+#include "csg_tool.h"
#include "scene/3d/path_3d.h"
#include "scene/3d/visual_instance_3d.h"
-#include "scene/resources/concave_polygon_shape_3d.h"
-#include "thirdparty/misc/mikktspace.h"
class CSGShape3D : public GeometryInstance3D {
GDCLASS(CSGShape3D, GeometryInstance3D);
public:
+ // This is very ugly
enum Operation {
- OPERATION_UNION,
- OPERATION_INTERSECTION,
- OPERATION_SUBTRACTION,
-
+ OPERATION_UNION = CSGTool::OPERATION_UNION,
+ OPERATION_INTERSECTION = CSGTool::OPERATION_INTERSECTION,
+ OPERATION_SUBTRACTION = CSGTool::OPERATION_SUBTRACTION,
};
private:
Operation operation = OPERATION_UNION;
CSGShape3D *parent = nullptr;
- CSGBrush *brush = nullptr;
+ Ref csg_tool;
AABB node_aabb;
@@ -71,49 +69,17 @@ class CSGShape3D : public GeometryInstance3D {
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 {
- Vector vertices;
- Vector normals;
- Vector uvs;
- Vector tans;
- Ref material;
- int last_added = 0;
-
- Vector3 *verticesw = nullptr;
- Vector3 *normalsw = nullptr;
- Vector2 *uvsw = nullptr;
- real_t *tansw = nullptr;
- };
-
- //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();
protected:
void _notification(int p_what);
- virtual CSGBrush *_build_brush() = 0;
+ virtual CSGBrush _build_brush() = 0;
void _make_dirty();
static void _bind_methods();
friend class CSGCombiner3D;
- CSGBrush *_get_brush();
+ CSGBrush _get_brush();
virtual void _validate_property(PropertyInfo &property) const override;
@@ -123,7 +89,7 @@ class CSGShape3D : public GeometryInstance3D {
void set_operation(Operation p_operation);
Operation get_operation() const;
- virtual Vector get_brush_faces();
+ virtual Vector get_brush_faces() const;
virtual AABB get_aabb() const override;
virtual Vector get_faces(uint32_t p_usage_flags) const override;
@@ -151,16 +117,14 @@ class CSGShape3D : public GeometryInstance3D {
bool is_root_shape() const;
CSGShape3D();
- ~CSGShape3D();
};
-VARIANT_ENUM_CAST(CSGShape3D::Operation)
+VARIANT_ENUM_CAST(CSGShape3D::Operation);
class CSGCombiner3D : public CSGShape3D {
GDCLASS(CSGCombiner3D, CSGShape3D);
-private:
- virtual CSGBrush *_build_brush() override;
+ virtual CSGBrush _build_brush() override;
public:
CSGCombiner3D();
@@ -170,24 +134,31 @@ class CSGPrimitive3D : public CSGShape3D {
GDCLASS(CSGPrimitive3D, CSGShape3D);
protected:
- bool invert_faces;
- CSGBrush *_create_brush_from_arrays(const Vector &p_vertices, const Vector &p_uv, const Vector &p_smooth, const Vector][> &p_materials);
+ virtual CSGBrush _build_brush() override;
+ virtual Ref get_csg_primitive() const = 0;
static void _bind_methods();
public:
void set_invert_faces(bool p_invert);
- bool is_inverting_faces();
+ bool is_inverting_faces() 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;
- CSGPrimitive3D();
+ CSGPrimitive3D() {}
};
class CSGMesh3D : public CSGPrimitive3D {
GDCLASS(CSGMesh3D, CSGPrimitive3D);
- virtual CSGBrush *_build_brush() override;
+ virtual Ref get_csg_primitive() const override {
+ return csg_primitive;
+ }
- Ref mesh;
- Ref material;
+ Ref csg_primitive;
void _mesh_changed();
@@ -196,21 +167,19 @@ class CSGMesh3D : public CSGPrimitive3D {
public:
void set_mesh(const Ref &p_mesh);
- Ref get_mesh();
+ Ref get_mesh() const;
- void set_material(const Ref &p_material);
- Ref get_material() const;
+ CSGMesh3D();
};
class CSGSphere3D : public CSGPrimitive3D {
GDCLASS(CSGSphere3D, CSGPrimitive3D);
- virtual CSGBrush *_build_brush() override;
- Ref material;
- bool smooth_faces;
- float radius;
- int radial_segments;
- int rings;
+ virtual Ref get_csg_primitive() const override {
+ return csg_primitive;
+ }
+
+ Ref csg_primitive;
protected:
static void _bind_methods();
@@ -225,21 +194,17 @@ class CSGSphere3D : public CSGPrimitive3D {
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;
-
CSGSphere3D();
};
class CSGBox3D : public CSGPrimitive3D {
GDCLASS(CSGBox3D, CSGPrimitive3D);
- virtual CSGBrush *_build_brush() override;
- Ref material;
- Vector3 size = Vector3(2, 2, 2);
+ virtual Ref get_csg_primitive() const override {
+ return csg_primitive;
+ }
+
+ Ref csg_primitive;
protected:
static void _bind_methods();
@@ -248,22 +213,17 @@ class CSGBox3D : public CSGPrimitive3D {
void set_size(const Vector3 &p_size);
Vector3 get_size() const;
- void set_material(const Ref &p_material);
- Ref get_material() const;
-
- CSGBox3D() {}
+ CSGBox3D();
};
class CSGCylinder3D : public CSGPrimitive3D {
GDCLASS(CSGCylinder3D, CSGPrimitive3D);
- virtual CSGBrush *_build_brush() override;
- Ref material;
- float radius;
- float height;
- int sides;
- bool cone;
- bool smooth_faces;
+ virtual Ref get_csg_primitive() const override {
+ return csg_primitive;
+ }
+
+ Ref csg_primitive;
protected:
static void _bind_methods();
@@ -281,25 +241,17 @@ class CSGCylinder3D : public CSGPrimitive3D {
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;
-
CSGCylinder3D();
};
class CSGTorus3D : public CSGPrimitive3D {
GDCLASS(CSGTorus3D, CSGPrimitive3D);
- virtual CSGBrush *_build_brush() override;
- Ref material;
- float inner_radius;
- float outer_radius;
- int sides;
- int ring_sides;
- bool smooth_faces;
+ virtual Ref get_csg_primitive() const override {
+ return csg_primitive;
+ }
+
+ Ref csg_primitive;
protected:
static void _bind_methods();
@@ -317,12 +269,6 @@ class CSGTorus3D : public CSGPrimitive3D {
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;
-
CSGTorus3D();
};
@@ -330,30 +276,28 @@ class CSGPolygon3D : public CSGPrimitive3D {
GDCLASS(CSGPolygon3D, CSGPrimitive3D);
public:
+ // This is very ugly
enum Mode {
- MODE_DEPTH,
- MODE_SPIN,
- MODE_PATH
+ MODE_DEPTH = CSGPolygonShape3D::MODE_DEPTH,
+ MODE_SPIN = CSGPolygonShape3D::MODE_SPIN,
+ MODE_PATH = CSGPolygonShape3D::MODE_PATH
};
+ // and this too
enum PathRotation {
- PATH_ROTATION_POLYGON,
- PATH_ROTATION_PATH,
- PATH_ROTATION_PATH_FOLLOW,
+ PATH_ROTATION_POLYGON = CSGPolygonShape3D::PATH_ROTATION_POLYGON,
+ PATH_ROTATION_PATH = CSGPolygonShape3D::PATH_ROTATION_PATH,
+ PATH_ROTATION_PATH_FOLLOW = CSGPolygonShape3D::PATH_ROTATION_PATH_FOLLOW,
};
private:
- virtual CSGBrush *_build_brush() override;
-
- Vector polygon;
- Ref material;
-
- Mode mode;
+ virtual Ref get_csg_primitive() const override {
+ return csg_primitive;
+ }
- float depth;
+ Ref csg_primitive;
- float spin_degrees;
- int spin_sides;
+ virtual CSGBrush _build_brush() override;
NodePath path_node;
float path_interval;
@@ -411,12 +355,6 @@ class CSGPolygon3D : public CSGPrimitive3D {
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;
-
CSGPolygon3D();
};
diff --git a/modules/csg/csg_tool.cpp b/modules/csg/csg_tool.cpp
new file mode 100644
index 000000000000..b55f8d9227f8
--- /dev/null
+++ b/modules/csg/csg_tool.cpp
@@ -0,0 +1,1720 @@
+/*************************************************************************/
+/* csg_tool.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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_tool.h"
+
+#include "scene/3d/path_3d.h"
+
+// CSGPrimitive3D
+void CSGPrimitiveShape3D::_create_brush_from_arrays(const Vector &p_vertices, const Vector &p_uv, const Vector &p_smooth, const Vector][> &p_materials) {
+ brush = CSGBrush();
+
+ Vector invert;
+ invert.resize(p_vertices.size() / 3);
+ {
+ int ic = invert.size();
+ bool *w = invert.ptrw();
+ for (int i = 0; i < ic; i++) {
+ w[i] = invert_faces;
+ }
+ }
+ brush.build_from_faces(p_vertices, p_uv, p_smooth, p_materials, invert);
+}
+
+CSGBrush CSGPrimitiveShape3D::get_brush() {
+ _update_brush();
+ return brush;
+}
+
+void CSGPrimitiveShape3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_invert_faces", "invert_faces"), &CSGPrimitiveShape3D::set_invert_faces);
+ ClassDB::bind_method(D_METHOD("is_inverting_faces"), &CSGPrimitiveShape3D::is_inverting_faces);
+ ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGPrimitiveShape3D::set_smooth_faces);
+ ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGPrimitiveShape3D::get_smooth_faces);
+ ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGPrimitiveShape3D::set_material);
+ ClassDB::bind_method(D_METHOD("get_material"), &CSGPrimitiveShape3D::get_material);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "invert_faces"), "set_invert_faces", "is_inverting_faces");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "StandardMaterial3D,ShaderMaterial"), "set_material", "get_material");
+}
+
+bool CSGPrimitiveShape3D::is_inverting_faces() const {
+ return invert_faces;
+}
+
+void CSGPrimitiveShape3D::set_invert_faces(bool p_invert) {
+ if (invert_faces == p_invert) {
+ return;
+ }
+ dirty = true;
+ emit_changed();
+ invert_faces = p_invert;
+}
+
+bool CSGPrimitiveShape3D::get_smooth_faces() const {
+ return smooth_faces;
+}
+
+void CSGPrimitiveShape3D::set_smooth_faces(bool p_smooth_faces) {
+ if (smooth_faces == p_smooth_faces) {
+ return;
+ }
+ dirty = true;
+ emit_changed();
+ smooth_faces = p_smooth_faces;
+}
+
+Ref CSGPrimitiveShape3D::get_material() const {
+ return material;
+}
+
+void CSGPrimitiveShape3D::set_material(const Ref &p_material) {
+ if (material == p_material) {
+ return;
+ }
+ dirty = true;
+ emit_changed();
+ material = p_material;
+}
+
+// CSGMesh3D
+void CSGMeshShape3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &CSGMeshShape3D::set_mesh);
+ ClassDB::bind_method(D_METHOD("get_mesh"), &CSGMeshShape3D::get_mesh);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
+}
+
+Ref CSGMeshShape3D::get_mesh() const {
+ return mesh;
+}
+
+void CSGMeshShape3D::set_mesh(const Ref &p_mesh) {
+ if (mesh == p_mesh) {
+ return;
+ }
+ dirty = true;
+ emit_changed();
+ mesh = p_mesh;
+}
+
+void CSGMeshShape3D::_update_brush() {
+ if (dirty) {
+ if (!mesh.is_valid()) {
+ return;
+ }
+
+ Vector vertices;
+ Vector smooth;
+ Vector][> materials;
+ Vector 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) {
+ dirty = true;
+ ERR_FAIL_COND(arrays.size() == 0);
+ }
+
+ Vector avertices = arrays[Mesh::ARRAY_VERTEX];
+ if (avertices.size() == 0) {
+ continue;
+ }
+
+ const Vector3 *vr = avertices.ptr();
+
+ Vector anormals = arrays[Mesh::ARRAY_NORMAL];
+ const Vector3 *nr = nullptr;
+ if (anormals.size()) {
+ nr = anormals.ptr();
+ }
+
+ Vector auvs = arrays[Mesh::ARRAY_TEX_UV];
+ const Vector2 *uvr = nullptr;
+ if (auvs.size()) {
+ uvr = auvs.ptr();
+ }
+
+ Ref mat;
+ if (material.is_valid()) {
+ mat = material;
+ } else {
+ mat = mesh->surface_get_material(i);
+ }
+
+ Vector 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);
+
+ Vector3 *vw = vertices.ptrw();
+ bool *sw = smooth.ptrw();
+ Vector2 *uvw = uvs.ptrw();
+ Ref *mw = materials.ptrw();
+
+ const int *ir = aindices.ptr();
+
+ 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) {
+ normal[k] = nr[idx];
+ }
+ if (uvr) {
+ uv[k] = uvr[idx];
+ }
+ }
+
+ bool flat = normal[0].is_equal_approx(normal[1]) && normal[0].is_equal_approx(normal[2]);
+
+ 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);
+
+ Vector3 *vw = vertices.ptrw();
+ bool *sw = smooth.ptrw();
+ Vector2 *uvw = uvs.ptrw();
+ Ref *mw = materials.ptrw();
+
+ 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) {
+ normal[k] = nr[j + k];
+ }
+ if (uvr) {
+ uv[k] = uvr[j + k];
+ }
+ }
+
+ bool flat = normal[0].is_equal_approx(normal[1]) && normal[0].is_equal_approx(normal[2]);
+
+ 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;
+ }
+
+ _create_brush_from_arrays(vertices, uvs, smooth, materials);
+
+ dirty = false;
+ }
+}
+
+// CSGBox3D
+void CSGBoxShape3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_size", "size"), &CSGBoxShape3D::set_size);
+ ClassDB::bind_method(D_METHOD("get_size"), &CSGBoxShape3D::get_size);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size"), "set_size", "get_size");
+}
+
+Vector3 CSGBoxShape3D::get_size() const {
+ return size;
+}
+
+void CSGBoxShape3D::set_size(const Vector3 &p_size) {
+ if (size == p_size) {
+ return;
+ }
+ dirty = true;
+ emit_changed();
+ size = p_size;
+}
+
+void CSGBoxShape3D::_update_brush() {
+ if (dirty) {
+ brush = CSGBrush();
+
+ int face_count = 12; //it's a cube..
+
+ bool invert_val = is_inverting_faces();
+ Ref material = get_material();
+
+ Vector faces;
+ Vector uvs;
+ Vector smooth;
+ Vector][> materials;
+ Vector invert;
+
+ faces.resize(face_count * 3);
+ uvs.resize(face_count * 3);
+
+ smooth.resize(face_count);
+ materials.resize(face_count);
+ invert.resize(face_count);
+
+ {
+ Vector3 *facesw = faces.ptrw();
+ Vector2 *uvsw = uvs.ptrw();
+ bool *smoothw = smooth.ptrw();
+ Ref *materialsw = materials.ptrw();
+ bool *invertw = invert.ptrw();
+
+ int face = 0;
+
+ Vector3 vertex_mul = size / 2;
+
+ {
+ 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 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] = 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);
+ dirty = false;
+ }
+}
+
+// CSGCylinder3D
+void CSGCylinderShape3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_radius", "radius"), &CSGCylinderShape3D::set_radius);
+ ClassDB::bind_method(D_METHOD("get_radius"), &CSGCylinderShape3D::get_radius);
+ ClassDB::bind_method(D_METHOD("set_height", "height"), &CSGCylinderShape3D::set_height);
+ ClassDB::bind_method(D_METHOD("get_height"), &CSGCylinderShape3D::get_height);
+
+ ClassDB::bind_method(D_METHOD("set_sides", "sides"), &CSGCylinderShape3D::set_sides);
+ ClassDB::bind_method(D_METHOD("get_sides"), &CSGCylinderShape3D::get_sides);
+ ClassDB::bind_method(D_METHOD("set_cone", "cone"), &CSGCylinderShape3D::set_cone);
+ ClassDB::bind_method(D_METHOD("is_cone"), &CSGCylinderShape3D::is_cone);
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "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");
+}
+
+float CSGCylinderShape3D::get_radius() const {
+ return radius;
+}
+
+void CSGCylinderShape3D::set_radius(float p_radius) {
+ ERR_FAIL_COND(p_radius <= 0);
+ if (radius == p_radius) {
+ return;
+ }
+ dirty = true;
+ emit_changed();
+ radius = p_radius;
+}
+
+float CSGCylinderShape3D::get_height() const {
+ return height;
+}
+
+void CSGCylinderShape3D::set_height(float p_height) {
+ if (height == p_height) {
+ return;
+ }
+ dirty = true;
+ emit_changed();
+ height = p_height;
+}
+
+int CSGCylinderShape3D::get_sides() const {
+ return sides;
+}
+
+void CSGCylinderShape3D::set_sides(int p_sides) {
+ ERR_FAIL_COND(p_sides < 3);
+ if (sides == p_sides) {
+ return;
+ }
+ dirty = true;
+ emit_changed();
+ sides = p_sides;
+}
+
+bool CSGCylinderShape3D::is_cone() const {
+ return cone;
+}
+
+void CSGCylinderShape3D::set_cone(bool p_cone) {
+ if (cone == p_cone) {
+ return;
+ }
+ dirty = true;
+ emit_changed();
+ cone = p_cone;
+}
+
+void CSGCylinderShape3D::_update_brush() {
+ if (dirty) {
+ brush = CSGBrush();
+
+ // set our bounding box
+
+ int face_count = sides * (cone ? 1 : 2) + sides + (cone ? 0 : sides);
+
+ bool invert_val = is_inverting_faces();
+ Ref material = get_material();
+
+ Vector faces;
+ Vector uvs;
+ Vector smooth;
+ Vector][> materials;
+ Vector invert;
+
+ faces.resize(face_count * 3);
+ uvs.resize(face_count * 3);
+
+ smooth.resize(face_count);
+ materials.resize(face_count);
+ invert.resize(face_count);
+
+ {
+ Vector3 *facesw = faces.ptrw();
+ Vector2 *uvsw = uvs.ptrw();
+ bool *smoothw = smooth.ptrw();
+ Ref *materialsw = materials.ptrw();
+ bool *invertw = invert.ptrw();
+
+ 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;
+
+ float ang = inc * Math_TAU;
+ float ang_n = inc_n * Math_TAU;
+
+ 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] = get_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] = get_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);
+ dirty = false;
+ }
+}
+
+// CSGSphere3D
+void CSGSphereShape3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_radius", "radius"), &CSGSphereShape3D::set_radius);
+ ClassDB::bind_method(D_METHOD("get_radius"), &CSGSphereShape3D::get_radius);
+ ClassDB::bind_method(D_METHOD("set_radial_segments", "radial_segments"), &CSGSphereShape3D::set_radial_segments);
+ ClassDB::bind_method(D_METHOD("get_radial_segments"), &CSGSphereShape3D::get_radial_segments);
+ ClassDB::bind_method(D_METHOD("set_rings", "rings"), &CSGSphereShape3D::set_rings);
+ ClassDB::bind_method(D_METHOD("get_rings"), &CSGSphereShape3D::get_rings);
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "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");
+}
+
+float CSGSphereShape3D::get_radius() const {
+ return radius;
+}
+
+void CSGSphereShape3D::set_radius(float p_radius) {
+ if (radius == p_radius) {
+ return;
+ }
+ dirty = true;
+ emit_changed();
+ radius = p_radius;
+}
+
+int CSGSphereShape3D::get_radial_segments() const {
+ return radial_segments;
+}
+
+void CSGSphereShape3D::set_radial_segments(int p_radial_segments) {
+ p_radial_segments = p_radial_segments > 4 ? p_radial_segments : 4;
+ if (radial_segments == p_radial_segments) {
+ return;
+ }
+
+ dirty = true;
+ emit_changed();
+ radial_segments = p_radial_segments;
+}
+
+int CSGSphereShape3D::get_rings() const {
+ return rings;
+}
+
+void CSGSphereShape3D::set_rings(int p_rings) {
+ p_rings = p_rings > 1 ? p_rings : 1;
+ if (rings == p_rings) {
+ return;
+ }
+ dirty = true;
+ emit_changed();
+ rings = p_rings;
+}
+
+void CSGSphereShape3D::_update_brush() {
+ // set our bounding box
+ if (dirty) {
+ brush = CSGBrush();
+
+ int face_count = rings * radial_segments * 2 - radial_segments * 2;
+
+ bool invert_val = is_inverting_faces();
+ Ref material = get_material();
+
+ Vector faces;
+ Vector uvs;
+ Vector smooth;
+ Vector][> materials;
+ Vector invert;
+
+ faces.resize(face_count * 3);
+ uvs.resize(face_count * 3);
+
+ smooth.resize(face_count);
+ materials.resize(face_count);
+ invert.resize(face_count);
+
+ {
+ Vector3 *facesw = faces.ptrw();
+ Vector2 *uvsw = uvs.ptrw();
+ bool *smoothw = smooth.ptrw();
+ Ref *materialsw = materials.ptrw();
+ bool *invertw = invert.ptrw();
+
+ int face = 0;
+ const double lat_step = Math_TAU / rings;
+ const double lon_step = Math_TAU / radial_segments;
+
+ for (int i = 1; i <= rings; i++) {
+ double lat0 = lat_step * (i - 1) - Math_TAU / 4;
+ double z0 = Math::sin(lat0);
+ double zr0 = Math::cos(lat0);
+ double u0 = double(i - 1) / rings;
+
+ double lat1 = lat_step * i - Math_TAU / 4;
+ double z1 = Math::sin(lat1);
+ double zr1 = Math::cos(lat1);
+ double u1 = double(i) / rings;
+
+ for (int j = radial_segments; j >= 1; j--) {
+ double lng0 = lon_step * (j - 1);
+ double x0 = Math::cos(lng0);
+ double y0 = Math::sin(lng0);
+ double v0 = double(i - 1) / radial_segments;
+
+ double lng1 = lon_step * j;
+ double x1 = Math::cos(lng1);
+ double y1 = Math::sin(lng1);
+ double v1 = double(i) / radial_segments;
+
+ Vector3 v[4] = {
+ Vector3(x1 * zr0, z0, y1 * zr0) * radius,
+ Vector3(x1 * zr1, z1, y1 * zr1) * radius,
+ Vector3(x0 * zr1, z1, y0 * zr1) * radius,
+ Vector3(x0 * zr0, z0, y0 * zr0) * radius
+ };
+
+ Vector2 u[4] = {
+ Vector2(v1, u0),
+ Vector2(v1, u1),
+ Vector2(v0, u1),
+ Vector2(v0, u0),
+
+ };
+
+ if (i < rings) {
+ //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] = get_smooth_faces();
+ invertw[face] = invert_val;
+ materialsw[face] = material;
+
+ face++;
+ }
+
+ if (i > 1) {
+ //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] = get_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);
+ }
+}
+
+// CSGTorus3D
+void CSGTorusShape3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_inner_radius", "radius"), &CSGTorusShape3D::set_inner_radius);
+ ClassDB::bind_method(D_METHOD("get_inner_radius"), &CSGTorusShape3D::get_inner_radius);
+ ClassDB::bind_method(D_METHOD("set_outer_radius", "radius"), &CSGTorusShape3D::set_outer_radius);
+ ClassDB::bind_method(D_METHOD("get_outer_radius"), &CSGTorusShape3D::get_outer_radius);
+
+ ClassDB::bind_method(D_METHOD("set_sides", "sides"), &CSGTorusShape3D::set_sides);
+ ClassDB::bind_method(D_METHOD("get_sides"), &CSGTorusShape3D::get_sides);
+ ClassDB::bind_method(D_METHOD("set_ring_sides", "sides"), &CSGTorusShape3D::set_ring_sides);
+ ClassDB::bind_method(D_METHOD("get_ring_sides"), &CSGTorusShape3D::get_ring_sides);
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inner_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_inner_radius", "get_inner_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "outer_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "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");
+}
+
+float CSGTorusShape3D::get_inner_radius() const {
+ return inner_radius;
+}
+
+void CSGTorusShape3D::set_inner_radius(float p_inner_radius) {
+ if (inner_radius == p_inner_radius) {
+ return;
+ }
+ dirty = true;
+ emit_changed();
+ inner_radius = p_inner_radius;
+}
+
+float CSGTorusShape3D::get_outer_radius() const {
+ return outer_radius;
+}
+
+void CSGTorusShape3D::set_outer_radius(float p_outer_radius) {
+ if (outer_radius == p_outer_radius) {
+ return;
+ }
+
+ dirty = true;
+ emit_changed();
+ outer_radius = p_outer_radius;
+}
+
+int CSGTorusShape3D::get_sides() const {
+ return sides;
+}
+
+void CSGTorusShape3D::set_sides(int p_sides) {
+ ERR_FAIL_COND(p_sides < 3);
+ if (sides == p_sides) {
+ return;
+ }
+ dirty = true;
+ emit_changed();
+ sides = p_sides;
+}
+
+int CSGTorusShape3D::get_ring_sides() const {
+ return ring_sides;
+}
+
+void CSGTorusShape3D::set_ring_sides(int p_ring_sides) {
+ ERR_FAIL_COND(p_ring_sides < 3);
+ if (ring_sides == p_ring_sides) {
+ return;
+ }
+ dirty = true;
+ emit_changed();
+ ring_sides = p_ring_sides;
+}
+
+void CSGTorusShape3D::_update_brush() {
+ if (dirty) {
+ brush = CSGBrush();
+
+ // set our bounding box
+
+ float min_radius = inner_radius;
+ float max_radius = outer_radius;
+
+ ERR_FAIL_COND_MSG(min_radius == max_radius, "Condition \"inner_radius == outer_radius\" is true");
+
+ if (min_radius > max_radius) {
+ SWAP(min_radius, max_radius);
+ }
+
+ float radius = (max_radius - min_radius) * 0.5;
+
+ int face_count = ring_sides * sides * 2;
+
+ bool invert_val = is_inverting_faces();
+ Ref material = get_material();
+
+ Vector faces;
+ Vector uvs;
+ Vector smooth;
+ Vector][> materials;
+ Vector invert;
+
+ faces.resize(face_count * 3);
+ uvs.resize(face_count * 3);
+
+ smooth.resize(face_count);
+ materials.resize(face_count);
+ invert.resize(face_count);
+
+ {
+ Vector3 *facesw = faces.ptrw();
+ Vector2 *uvsw = uvs.ptrw();
+ bool *smoothw = smooth.ptrw();
+ Ref *materialsw = materials.ptrw();
+ bool *invertw = invert.ptrw();
+
+ int face = 0;
+
+ {
+ for (int i = 0; i < sides; i++) {
+ float inci = float(i) / sides;
+ float inci_n = float((i + 1)) / sides;
+
+ float angi = inci * Math_TAU;
+ float angi_n = inci_n * Math_TAU;
+
+ 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;
+
+ float angj = incj * Math_TAU;
+ float angj_n = incj_n * Math_TAU;
+
+ 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] = get_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] = get_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);
+ dirty = false;
+ }
+}
+
+// CSGPolygonShape3D
+void CSGPolygonShape3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_polygon", "polygon"), &CSGPolygonShape3D::set_polygon);
+ ClassDB::bind_method(D_METHOD("get_polygon"), &CSGPolygonShape3D::get_polygon);
+
+ ClassDB::bind_method(D_METHOD("set_mode", "mode"), &CSGPolygonShape3D::set_mode);
+ ClassDB::bind_method(D_METHOD("get_mode"), &CSGPolygonShape3D::get_mode);
+
+ ClassDB::bind_method(D_METHOD("set_depth", "depth"), &CSGPolygonShape3D::set_depth);
+ ClassDB::bind_method(D_METHOD("get_depth"), &CSGPolygonShape3D::get_depth);
+
+ ClassDB::bind_method(D_METHOD("set_spin_degrees", "degrees"), &CSGPolygonShape3D::set_spin_degrees);
+ ClassDB::bind_method(D_METHOD("get_spin_degrees"), &CSGPolygonShape3D::get_spin_degrees);
+
+ ClassDB::bind_method(D_METHOD("set_spin_sides", "spin_sides"), &CSGPolygonShape3D::set_spin_sides);
+ ClassDB::bind_method(D_METHOD("get_spin_sides"), &CSGPolygonShape3D::get_spin_sides);
+
+ ClassDB::bind_method(D_METHOD("set_path_curve", "curve"), &CSGPolygonShape3D::set_path_curve);
+ ClassDB::bind_method(D_METHOD("get_path_curve"), &CSGPolygonShape3D::get_path_curve);
+
+ ClassDB::bind_method(D_METHOD("set_path_transform", "xform"), &CSGPolygonShape3D::set_path_transform);
+ ClassDB::bind_method(D_METHOD("get_path_transform"), &CSGPolygonShape3D::get_path_transform);
+
+ ClassDB::bind_method(D_METHOD("set_path_interval", "distance"), &CSGPolygonShape3D::set_path_interval);
+ ClassDB::bind_method(D_METHOD("get_path_interval"), &CSGPolygonShape3D::get_path_interval);
+
+ ClassDB::bind_method(D_METHOD("set_path_rotation", "mode"), &CSGPolygonShape3D::set_path_rotation);
+ ClassDB::bind_method(D_METHOD("get_path_rotation"), &CSGPolygonShape3D::get_path_rotation);
+
+ ClassDB::bind_method(D_METHOD("set_path_continuous_u", "enable"), &CSGPolygonShape3D::set_path_continuous_u);
+ ClassDB::bind_method(D_METHOD("is_path_continuous_u"), &CSGPolygonShape3D::is_path_continuous_u);
+
+ ClassDB::bind_method(D_METHOD("set_path_joined", "enable"), &CSGPolygonShape3D::set_path_joined);
+ ClassDB::bind_method(D_METHOD("is_path_joined"), &CSGPolygonShape3D::is_path_joined);
+
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Depth,Spin,Path"), "set_mode", "get_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth", PROPERTY_HINT_RANGE, "0.01,100.0,0.01,or_greater,exp"), "set_depth", "get_depth");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "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::OBJECT, "path_curve", PROPERTY_HINT_RESOURCE_TYPE), "set_path_curve", "get_path_curve");
+ ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "path_transform"), "set_path_transform", "get_path_transform");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_interval", PROPERTY_HINT_RANGE, "0.1,1.0,0.05,exp"), "set_path_interval", "get_path_interval");
+ 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_continuous_u"), "set_path_continuous_u", "is_path_continuous_u");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_joined"), "set_path_joined", "is_path_joined");
+
+ 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);
+}
+
+void CSGPolygonShape3D::_validate_property(PropertyInfo &property) const {
+ if (property.name.begins_with("spin") && mode != CSGPolygonShape3D::MODE_SPIN) {
+ property.usage = PROPERTY_USAGE_NONE;
+ }
+ if (property.name.begins_with("path") && mode != CSGPolygonShape3D::MODE_PATH) {
+ property.usage = PROPERTY_USAGE_NONE;
+ }
+ if (property.name == "depth" && mode != CSGPolygonShape3D::MODE_DEPTH) {
+ property.usage = PROPERTY_USAGE_NONE;
+ }
+
+ CSGPrimitiveShape3D::_validate_property(property);
+}
+
+void CSGPolygonShape3D::set_polygon(const Vector &p_polygon) {
+ dirty = true;
+ emit_changed();
+ polygon = p_polygon;
+}
+
+Vector CSGPolygonShape3D::get_polygon() const {
+ return polygon;
+}
+
+void CSGPolygonShape3D::set_mode(Mode p_mode) {
+ dirty = true;
+ emit_changed();
+ mode = p_mode;
+ notify_property_list_changed();
+}
+
+CSGPolygonShape3D::Mode CSGPolygonShape3D::get_mode() const {
+ return mode;
+}
+
+void CSGPolygonShape3D::set_depth(const float p_depth) {
+ ERR_FAIL_COND(p_depth < 0.001);
+ dirty = true;
+ emit_changed();
+ depth = p_depth;
+}
+
+float CSGPolygonShape3D::get_depth() const {
+ return depth;
+}
+
+void CSGPolygonShape3D::set_path_curve(const Ref &p_curve) {
+ dirty = true;
+ emit_changed();
+ path_curve = p_curve;
+}
+
+Ref CSGPolygonShape3D::get_path_curve() const {
+ return path_curve;
+}
+
+void CSGPolygonShape3D::set_path_transform(Transform3D p_xform) {
+ dirty = true;
+ emit_changed();
+ path_transform = p_xform;
+}
+
+Transform3D CSGPolygonShape3D::get_path_transform() const {
+ return path_transform;
+}
+
+void CSGPolygonShape3D::set_path_continuous_u(bool p_enable) {
+ dirty = true;
+ emit_changed();
+ path_continuous_u = p_enable;
+}
+
+bool CSGPolygonShape3D::is_path_continuous_u() const {
+ return path_continuous_u;
+}
+
+void CSGPolygonShape3D::set_spin_degrees(const float p_spin_degrees) {
+ ERR_FAIL_COND(p_spin_degrees < 0.01 || p_spin_degrees > 360);
+ dirty = true;
+ emit_changed();
+ spin_degrees = p_spin_degrees;
+}
+
+float CSGPolygonShape3D::get_spin_degrees() const {
+ return spin_degrees;
+}
+
+void CSGPolygonShape3D::set_spin_sides(const int p_spin_sides) {
+ ERR_FAIL_COND(p_spin_sides < 3);
+ dirty = true;
+ emit_changed();
+ spin_sides = p_spin_sides;
+}
+
+int CSGPolygonShape3D::get_spin_sides() const {
+ return spin_sides;
+}
+
+void CSGPolygonShape3D::set_path_interval(float p_interval) {
+ ERR_FAIL_COND_MSG(p_interval < 0.001, "Path interval cannot be smaller than 0.001.");
+ dirty = true;
+ emit_changed();
+ path_interval = p_interval;
+}
+
+float CSGPolygonShape3D::get_path_interval() const {
+ return path_interval;
+}
+
+void CSGPolygonShape3D::set_path_rotation(PathRotation p_rotation) {
+ dirty = true;
+ emit_changed();
+ path_rotation = p_rotation;
+}
+
+CSGPolygonShape3D::PathRotation CSGPolygonShape3D::get_path_rotation() const {
+ return path_rotation;
+}
+
+void CSGPolygonShape3D::set_path_joined(bool p_enable) {
+ dirty = true;
+ emit_changed();
+ path_joined = p_enable;
+}
+
+bool CSGPolygonShape3D::is_path_joined() const {
+ return path_joined;
+}
+
+void CSGPolygonShape3D::_update_brush() {
+ if (dirty) {
+ brush = CSGBrush();
+
+ if (polygon.size() < 3) {
+ dirty = false;
+ return;
+ }
+
+ // Triangulate polygon shape.
+ Vector shape_polygon = polygon;
+ if (Triangulate::get_area(shape_polygon) > 0) {
+ shape_polygon.reverse();
+ }
+ int shape_sides = shape_polygon.size();
+ Vector shape_faces = Geometry2D::triangulate_polygon(shape_polygon);
+ ERR_FAIL_COND_MSG(shape_faces.size() < 3, "Failed to triangulate CSGPolygon");
+
+ // 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.
+ if (mode == MODE_PATH) {
+ ERR_FAIL_COND(path_curve.is_null());
+ }
+
+ // Calculate the number 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;
+ 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: {
+ extrusions = Math::ceil(1.0 * path_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;
+
+ // Intialize variables used to create the mesh.
+ Ref material = get_material();
+
+ Vector faces;
+ Vector uvs;
+ Vector smooth;
+ Vector][> materials;
+ Vector invert;
+
+ faces.resize(face_count * 3);
+ uvs.resize(face_count * 3);
+ smooth.resize(face_count);
+ materials.resize(face_count);
+ invert.resize(face_count);
+
+ Vector3 *facesw = faces.ptrw();
+ Vector2 *uvsw = uvs.ptrw();
+ bool *smoothw = smooth.ptrw();
+ Ref *materialsw = materials.ptrw();
+ bool *invertw = invert.ptrw();
+
+ int face = 0;
+ Transform3D current_xform;
+ Transform3D previous_xform;
+ double u_step = 1.0 / extrusions;
+ 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 *= path_curve->get_baked_length();
+ }
+
+ if (mode == MODE_PATH) {
+ Vector3 current_point = path_curve->interpolate_baked(0);
+ Vector3 next_point = path_curve->interpolate_baked(extrusion_step);
+ Vector3 current_up = Vector3(0, 1, 0);
+ Vector3 direction = next_point - current_point;
+
+ if (path_joined) {
+ Vector3 last_point = path_curve->interpolate_baked(path_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 = path_curve->interpolate_baked_up_vector(0);
+ break;
+ }
+
+ Transform3D facing = Transform3D().looking_at(direction, current_up);
+ current_xform = path_transform.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] = is_inverting_faces();
+ face++;
+ }
+ }
+
+ // Add extrusion faces.
+ for (int x0 = 0; x0 < extrusions; x0++) {
+ 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 = path_curve->interpolate_baked(previous_offset);
+ Vector3 current_point = path_curve->interpolate_baked(current_offset);
+ Vector3 next_point = path_curve->interpolate_baked(next_offset);
+ Vector3 current_up = Vector3(0, 1, 0);
+ Vector3 direction = next_point - previous_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 = path_curve->interpolate_baked_up_vector(current_offset);
+ break;
+ }
+
+ Transform3D facing = Transform3D().looking_at(direction, current_up);
+ current_xform = path_transform.translated(current_point) * facing;
+ } break;
+ }
+
+ double u0 = x0 * 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] = get_smooth_faces();
+ invertw[face] = is_inverting_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] = get_smooth_faces();
+ invertw[face] = is_inverting_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] = is_inverting_faces();
+ face++;
+ }
+ }
+
+ ERR_FAIL_COND_MSG(face != face_count, "Bug: Failed to create the CSGPolygon mesh correctly.");
+
+ brush.build_from_faces(faces, uvs, smooth, materials, invert);
+ }
+}
+
+// CSGTool
+
+int CSGTool::mikktGetNumFaces(const SMikkTSpaceContext *pContext) {
+ ShapeUpdateSurface &surface = *((ShapeUpdateSurface *)pContext->m_pUserData);
+
+ return surface.vertices.size() / 3;
+}
+
+int CSGTool::mikktGetNumVerticesOfFace(const SMikkTSpaceContext *pContext, const int iFace) {
+ // always 3
+ return 3;
+}
+
+void CSGTool::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 CSGTool::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 CSGTool::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 CSGTool::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 CSGTool::add_brush(CSGBrush &p_brush, Operation p_operation, float p_vertex_snap) {
+ if (brush.faces.is_empty()) {
+ brush = p_brush;
+ } else {
+ CSGBrush newbrush;
+ CSGBrushOperation brush_operation;
+ brush_operation.merge_brushes(static_cast(p_operation), brush, p_brush, newbrush, p_vertex_snap);
+ brush = newbrush;
+ }
+}
+
+Vector CSGTool::get_brush_faces() const {
+ Vector faces;
+ int fc = brush.faces.size();
+ faces.resize(fc * 3);
+ {
+ Vector3 *w = faces.ptrw();
+ for (int i = 0; i < fc; i++) {
+ w[i * 3 + 0] = brush.faces[i].vertices[0];
+ w[i * 3 + 1] = brush.faces[i].vertices[1];
+ w[i * 3 + 2] = brush.faces[i].vertices[2];
+ }
+ }
+
+ return faces;
+}
+
+void CSGTool::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("add_primitive", "primitive", "operation", "xform", "vertex_snap"), &CSGTool::add_primitive, DEFVAL(OPERATION_UNION), DEFVAL(Transform3D()), DEFVAL(0.001));
+ ClassDB::bind_method(D_METHOD("create_trimesh_shape"), &CSGTool::create_trimesh_shape);
+ ClassDB::bind_method(D_METHOD("commit", "existing", "generate_tangents"), &CSGTool::commit, DEFVAL(Variant()), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_aabb"), &CSGTool::get_aabb);
+
+ BIND_ENUM_CONSTANT(OPERATION_UNION);
+ BIND_ENUM_CONSTANT(OPERATION_INTERSECTION);
+ BIND_ENUM_CONSTANT(OPERATION_SUBTRACTION);
+}
+
+void CSGTool::add_primitive(Ref p_primitive, Operation p_operation, Transform3D p_xform, float p_vertex_snap) {
+ CSGBrush newbrush;
+ newbrush.copy_from(p_primitive->get_brush(), p_xform);
+ add_brush(newbrush, p_operation, p_vertex_snap);
+}
+
+Ref CSGTool::create_trimesh_shape() const {
+ Ref shape;
+ shape.instantiate();
+
+ Vector physics_faces;
+ physics_faces.resize(brush.faces.size() * 3);
+ Vector3 *physicsw = physics_faces.ptrw();
+
+ for (int i = 0; i < brush.faces.size(); i++) {
+ int order[3] = { 0, 1, 2 };
+
+ if (brush.faces[i].invert) {
+ SWAP(order[1], order[2]);
+ }
+
+ physicsw[i * 3 + 0] = brush.faces[i].vertices[order[0]];
+ physicsw[i * 3 + 1] = brush.faces[i].vertices[order[1]];
+ physicsw[i * 3 + 2] = brush.faces[i].vertices[order[2]];
+ }
+ return shape;
+}
+
+Ref CSGTool::commit(const Ref &p_existing, bool p_generate_tangents) {
+ Ref mesh;
+ if (p_existing.is_valid()) {
+ mesh = p_existing;
+ } else {
+ mesh.instantiate();
+ }
+
+ OAHashMap vec_map;
+
+ Vector face_count;
+ face_count.resize(brush.materials.size() + 1);
+ for (int i = 0; i < face_count.size(); i++) {
+ face_count.write[i] = 0;
+ }
+
+ for (int i = 0; i < brush.faces.size(); i++) {
+ int mat = brush.faces[i].material;
+ ERR_CONTINUE(mat < -1 || mat >= face_count.size());
+ int idx = mat == -1 ? face_count.size() - 1 : mat;
+
+ Plane p(brush.faces[i].vertices[0], brush.faces[i].vertices[1], brush.faces[i].vertices[2]);
+
+ for (int j = 0; j < 3; j++) {
+ Vector3 v = brush.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 (p_generate_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 = brush.materials[i];
+ }
+
+ surfaces.write[i].verticesw = surfaces.write[i].vertices.ptrw();
+ surfaces.write[i].normalsw = surfaces.write[i].normals.ptrw();
+ surfaces.write[i].uvsw = surfaces.write[i].uvs.ptrw();
+ if (p_generate_tangents) {
+ surfaces.write[i].tansw = surfaces.write[i].tans.ptrw();
+ }
+ }
+
+ //fill arrays
+ {
+ for (int i = 0; i < brush.faces.size(); i++) {
+ int order[3] = { 0, 1, 2 };
+
+ if (brush.faces[i].invert) {
+ SWAP(order[1], order[2]);
+ }
+
+ int mat = brush.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(brush.faces[i].vertices[0], brush.faces[i].vertices[1], brush.faces[i].vertices[2]);
+
+ for (int j = 0; j < 3; j++) {
+ Vector3 v = brush.faces[i].vertices[j];
+
+ Vector3 normal = p.normal;
+
+ if (brush.faces[i].smooth && vec_map.lookup(v, normal)) {
+ normal.normalize();
+ }
+
+ if (brush.faces[i].invert) {
+ normal = -normal;
+ }
+
+ int k = last + order[j];
+ surfaces[idx].verticesw[k] = v;
+ surfaces[idx].uvsw[k] = brush.faces[i].uvs[j];
+ surfaces[idx].normalsw[k] = normal;
+
+ if (p_generate_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;
+ }
+ }
+
+ //create surfaces
+
+ for (int i = 0; i < surfaces.size(); i++) {
+ // generate tangents for this surface
+ bool have_tangents = p_generate_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);
+ }
+
+ 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 = mesh->get_surface_count();
+ mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, array);
+ mesh->surface_set_material(idx, surfaces[i].material);
+ }
+ return mesh;
+}
+
+AABB CSGTool::get_aabb() const {
+ AABB aabb;
+ for (int i = 0; i < brush.faces.size(); i++) {
+ for (int j = 0; j < 3; j++) {
+ if (i == 0 && j == 0) {
+ aabb.position = brush.faces[i].vertices[j];
+ } else {
+ aabb.expand_to(brush.faces[i].vertices[j]);
+ }
+ }
+ }
+ return aabb;
+}
diff --git a/modules/csg/csg_tool.h b/modules/csg/csg_tool.h
new file mode 100644
index 000000000000..3956eaf64d4d
--- /dev/null
+++ b/modules/csg/csg_tool.h
@@ -0,0 +1,322 @@
+/*************************************************************************/
+/* csg_tool.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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_TOOL_H
+#define CSG_TOOL_H
+
+#include "csg.h"
+
+#include "core/math/geometry_2d.h"
+#include "scene/resources/concave_polygon_shape_3d.h"
+#include "scene/resources/material.h"
+#include "scene/resources/mesh.h"
+#include "thirdparty/misc/mikktspace.h"
+
+class CSGPrimitiveShape3D : public Resource {
+ GDCLASS(CSGPrimitiveShape3D, Resource);
+
+ bool invert_faces = false;
+ bool smooth_faces = true;
+ Ref material;
+
+protected:
+ CSGBrush brush;
+ bool dirty = true;
+
+ virtual void _update_brush() = 0;
+
+ void _create_brush_from_arrays(const Vector &p_vertices, const Vector &p_uv, const Vector &p_smooth, const Vector][> &p_materials);
+
+ _ALWAYS_INLINE_ void _recreate_brush() {
+ brush = CSGBrush();
+ }
+
+ static void _bind_methods();
+
+public:
+ CSGBrush get_brush();
+
+ bool is_inverting_faces() const;
+ void set_invert_faces(bool p_invert);
+
+ bool get_smooth_faces() const;
+ void set_smooth_faces(bool p_smooth_faces);
+
+ Ref get_material() const;
+ void set_material(const Ref &p_material);
+};
+
+class CSGMeshShape3D : public CSGPrimitiveShape3D {
+ GDCLASS(CSGMeshShape3D, CSGPrimitiveShape3D);
+
+ Ref mesh;
+
+ virtual void _update_brush() override;
+
+protected:
+ static void _bind_methods();
+
+public:
+ Ref get_mesh() const;
+ void set_mesh(const Ref &p_mesh);
+};
+
+class CSGBoxShape3D : public CSGPrimitiveShape3D {
+ GDCLASS(CSGBoxShape3D, CSGPrimitiveShape3D);
+
+ Vector3 size = Vector3(2, 2, 2);
+
+ virtual void _update_brush() override;
+
+protected:
+ static void _bind_methods();
+
+public:
+ Vector3 get_size() const;
+ void set_size(const Vector3 &p_size);
+};
+
+class CSGCylinderShape3D : public CSGPrimitiveShape3D {
+ GDCLASS(CSGCylinderShape3D, CSGPrimitiveShape3D);
+
+ float radius = 1.0;
+ float height = 1.0;
+ int sides = 8;
+ bool cone = false;
+
+ virtual void _update_brush() override;
+
+protected:
+ static void _bind_methods();
+
+public:
+ float get_radius() const;
+ void set_radius(float p_radius);
+
+ float get_height() const;
+ void set_height(float p_height);
+
+ int get_sides() const;
+ void set_sides(int p_sides);
+
+ bool is_cone() const;
+ void set_cone(bool p_cone);
+};
+
+class CSGSphereShape3D : public CSGPrimitiveShape3D {
+ GDCLASS(CSGSphereShape3D, CSGPrimitiveShape3D);
+
+ float radius = 1.0;
+ // Should they match SphereMesh3D?
+ int radial_segments = 12;
+ int rings = 6;
+
+ virtual void _update_brush() override;
+
+protected:
+ static void _bind_methods();
+
+public:
+ float get_radius() const;
+ void set_radius(float p_radius);
+
+ int get_radial_segments() const;
+ void set_radial_segments(int p_radial_segments);
+
+ int get_rings() const;
+ void set_rings(int p_rings);
+};
+
+class CSGTorusShape3D : public CSGPrimitiveShape3D {
+ GDCLASS(CSGTorusShape3D, CSGPrimitiveShape3D);
+
+ float inner_radius = 2.0;
+ float outer_radius = 3.0;
+ int sides = 8;
+ int ring_sides = 6;
+
+ virtual void _update_brush() override;
+
+protected:
+ static void _bind_methods();
+
+public:
+ float get_inner_radius() const;
+ void set_inner_radius(float p_inner_radius);
+ float get_outer_radius() const;
+ void set_outer_radius(float p_outer_radius);
+ int get_sides() const;
+ void set_sides(int p_sides);
+ int get_ring_sides() const;
+ void set_ring_sides(int p_ring_sides);
+};
+
+// TODO: migrate this
+
+class CSGPolygonShape3D : public CSGPrimitiveShape3D {
+ GDCLASS(CSGPolygonShape3D, CSGPrimitiveShape3D);
+
+public:
+ enum Mode {
+ MODE_DEPTH,
+ MODE_SPIN,
+ MODE_PATH
+ };
+
+ enum PathRotation {
+ PATH_ROTATION_POLYGON,
+ PATH_ROTATION_PATH,
+ PATH_ROTATION_PATH_FOLLOW,
+ };
+
+private:
+ Vector polygon;
+
+ Mode mode = MODE_DEPTH;
+
+ float depth = 1.0;
+
+ float spin_degrees = 360.0;
+ int spin_sides = 8;
+
+ Ref path_curve;
+ float path_interval = 1.0;
+ PathRotation path_rotation = PATH_ROTATION_PATH;
+ Transform3D path_transform;
+
+ bool path_continuous_u = false;
+ bool path_joined = false;
+
+ void _path_changed();
+ void _path_exited();
+
+ virtual void _update_brush() override;
+
+protected:
+ static void _bind_methods();
+ virtual void _validate_property(PropertyInfo &property) const override;
+
+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_curve(const Ref &p_curve);
+ Ref get_path_curve() const;
+
+ void set_path_interval(float p_interval);
+ float get_path_interval() const;
+
+ void set_path_rotation(PathRotation p_rotation);
+ PathRotation get_path_rotation() const;
+
+ void set_path_transform(Transform3D p_xform);
+ Transform3D get_path_transform() const;
+
+ void set_path_continuous_u(bool p_enable);
+ bool is_path_continuous_u() const;
+
+ void set_path_joined(bool p_enable);
+ bool is_path_joined() const;
+};
+
+VARIANT_ENUM_CAST(CSGPolygonShape3D::Mode)
+VARIANT_ENUM_CAST(CSGPolygonShape3D::PathRotation)
+
+class CSGTool : public RefCounted {
+ GDCLASS(CSGTool, RefCounted);
+
+public:
+ enum Operation {
+ OPERATION_UNION = CSGBrushOperation::OPERATION_UNION,
+ OPERATION_INTERSECTION = CSGBrushOperation::OPERATION_INTERSECTION,
+ OPERATION_SUBTRACTION = CSGBrushOperation::OPERATION_SUBSTRACTION,
+ };
+
+private:
+ CSGBrush brush;
+
+ struct ShapeUpdateSurface {
+ Vector vertices;
+ Vector normals;
+ Vector]