Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 110 additions & 22 deletions modules/csg/csg_shape.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,26 @@ void CSGShape3D::set_collision_priority(real_t p_priority) {
real_t CSGShape3D::get_collision_priority() const {
return collision_priority;
}

void CSGShape3D::set_autosmooth(bool p_smooth) {
autosmooth = p_smooth;
_make_dirty();
notify_property_list_changed();
}

bool CSGShape3D::is_autosmooth() const {
return autosmooth;
}

void CSGShape3D::set_smoothing_angle(const float p_angle) {
smoothing_angle = p_angle;
_make_dirty();
}

float CSGShape3D::get_smoothing_angle() const {
return smoothing_angle;
}

#endif // PHYSICS_3D_DISABLED

bool CSGShape3D::is_root_shape() const {
Expand Down Expand Up @@ -576,10 +596,14 @@ void CSGShape3D::update_shape() {
CSGBrush *n = _get_brush();
ERR_FAIL_NULL_MSG(n, "Cannot get CSGBrush.");

AHashMap<Vector3, Vector3> vec_map;

Vector<int> face_count;
face_count.resize(n->materials.size() + 1);

Vector<Vector3> smooth_faces;
LocalVector<Vector3> smooth_vertex;
smooth_faces.resize(n->faces.size());
smooth_vertex.resize(n->faces.size() * 3);

for (int i = 0; i < face_count.size(); i++) {
face_count.write[i] = 0;
}
Expand All @@ -589,21 +613,70 @@ void CSGShape3D::update_shape() {
ERR_CONTINUE(mat < -1 || mat >= face_count.size());
int idx = mat == -1 ? face_count.size() - 1 : mat;

if (n->faces[i].smooth) {
Plane p(n->faces[i].vertices[0], n->faces[i].vertices[1], n->faces[i].vertices[2]);
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 *vec = vec_map.getptr(v);
if (vec) {
*vec += p.normal;
} else {
vec_map.insert(v, p.normal);
smooth_faces.write[i] = p.normal;
// Not sure if resize populates the LocalVector.
smooth_vertex[i * 3 + 0] = Vector3(p.normal);
smooth_vertex[i * 3 + 1] = Vector3(p.normal);
smooth_vertex[i * 3 + 2] = Vector3(p.normal);
// We could use a AHashMap Vector3, int to store the number of connections of each vertex position and end the loop earlier. But I'm not sure if the performance gains outweigh the cost.
face_count.write[idx]++;
}

if (autosmooth) {
// We could add a `use_groups` property later to only apply autosmooth on smooth faces or respect smoothing groups in some way.
if (smoothing_angle > 0.1) {
float smooth_angle_rad = Math::cos(Math::deg_to_rad(smoothing_angle));
for (int i = 0; i < smooth_faces.size(); i++) {
for (int k = 0; k < 3; k++) {
int curr_vert = i * 3 + k;
Vector3 vert_a = n->faces[i].vertices[k];
for (int j = i + 1; j < smooth_faces.size(); j++) {
// Compare the angles of faces instead of vertices.
if (smooth_faces[i].dot(smooth_faces[j]) > smooth_angle_rad) {
for (int h = 0; h < 3; h++) {
Vector3 vert_b = n->faces[j].vertices[h];
if (vert_a == vert_b) {
int curr_j = j * 3 + h;
smooth_vertex[curr_vert] += smooth_faces[j];
smooth_vertex[curr_j] += smooth_faces[i];
break;
}
}
}
}
smooth_vertex[curr_vert].normalize();
}
}
}
} else {
for (int i = 0; i < smooth_faces.size(); i++) {
bool face_is_smooth = n->faces[i].smooth;
if (face_is_smooth) {
for (int k = 0; k < 3; k++) {
Vector3 vert_a = n->faces[i].vertices[k];
int curr_vert = i * 3 + k;
// Skip the other vertices of the face as they will never occupy the same position.
for (int j = i + 1; j < smooth_faces.size(); j++) {
// Preparing for when and if we replace Vector of bool for Vector of int smoothing groups. for now, face_is_smooth is always true.
if (face_is_smooth == n->faces[j].smooth) {
for (int h = 0; h < 3; h++) {
Vector3 vert_b = n->faces[j].vertices[h];
if (vert_a == vert_b) {
int curr_j = j * 3 + h;
smooth_vertex[curr_vert] += smooth_faces[j];
smooth_vertex[curr_j] += smooth_faces[i];
// Skip the other 2 vertices as only one vertex of each face can connect with one vertex of other face.
break;
}
}
}
}
smooth_vertex[curr_vert].normalize();
}
}
}

face_count.write[idx]++;
}

Vector<ShapeUpdateSurface> surfaces;
Expand Down Expand Up @@ -647,19 +720,12 @@ void CSGShape3D::update_shape() {

int last = surfaces[idx].last_added;

Plane p(n->faces[i].vertices[0], n->faces[i].vertices[1], n->faces[i].vertices[2]);
int face_pos_i = i * 3;

for (int j = 0; j < 3; j++) {
Vector3 v = n->faces[i].vertices[j];

Vector3 normal = p.normal;

if (n->faces[i].smooth) {
Vector3 *ptr = vec_map.getptr(v);
if (ptr) {
normal = ptr->normalized();
}
}
Vector3 normal = smooth_vertex[face_pos_i + j];

if (n->faces[i].invert) {
normal = -normal;
Expand Down Expand Up @@ -953,6 +1019,19 @@ void CSGShape3D::_validate_property(PropertyInfo &p_property) const {
if (!Engine::get_singleton()->is_editor_hint()) {
return;
}

if (p_property.name == "smoothing_angle") {
if (!autosmooth || (is_inside_tree() && !is_root_shape())) {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
}

if (p_property.name == "autosmooth") {
if (is_inside_tree() && !is_root_shape()) {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
}

bool is_collision_prefixed = p_property.name.begins_with("collision_");
if ((is_collision_prefixed || p_property.name.begins_with("use_collision")) && is_inside_tree() && !is_root_shape()) {
//hide collision if not root
Expand Down Expand Up @@ -1037,6 +1116,15 @@ void CSGShape3D::_bind_methods() {

ClassDB::bind_method(D_METHOD("bake_static_mesh"), &CSGShape3D::bake_static_mesh);

ClassDB::bind_method(D_METHOD("set_autosmooth", "autosmooth"), &CSGShape3D::set_autosmooth);
ClassDB::bind_method(D_METHOD("is_autosmooth"), &CSGShape3D::is_autosmooth);

ClassDB::bind_method(D_METHOD("set_smoothing_angle", "smoothing_angle"), &CSGShape3D::set_smoothing_angle);
ClassDB::bind_method(D_METHOD("get_smoothing_angle"), &CSGShape3D::get_smoothing_angle);

ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autosmooth"), "set_autosmooth", "is_autosmooth");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "smoothing_angle", PROPERTY_HINT_RANGE, "0,180,0.1,degrees"), "set_smoothing_angle", "get_smoothing_angle");

ADD_PROPERTY(PropertyInfo(Variant::INT, "operation", PROPERTY_HINT_ENUM, "Union,Intersection,Subtraction"), "set_operation", "get_operation");
#ifndef DISABLE_DEPRECATED
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "snap", PROPERTY_HINT_RANGE, "0.000001,1,0.000001,suffix:m", PROPERTY_USAGE_NONE), "set_snap", "get_snap");
Expand Down
9 changes: 9 additions & 0 deletions modules/csg/csg_shape.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ class CSGShape3D : public GeometryInstance3D {
bool last_visible = false;
float snap = 0.001;

bool autosmooth = false;
float smoothing_angle = 50.0;

#ifndef PHYSICS_3D_DISABLED
bool use_collision = false;
uint32_t collision_layer = 1;
Expand Down Expand Up @@ -159,6 +162,12 @@ class CSGShape3D : public GeometryInstance3D {
void set_collision_priority(real_t p_priority);
real_t get_collision_priority() const;

void set_autosmooth(bool p_smooth);
bool is_autosmooth() const;

void set_smoothing_angle(const float p_angle);
float get_smoothing_angle() const;

#ifndef DISABLE_DEPRECATED
void set_snap(float p_snap);
float get_snap() const;
Expand Down
8 changes: 8 additions & 0 deletions modules/csg/doc_classes/CSGShape3D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@
</method>
</methods>
<members>
<member name="autosmooth" type="bool" setter="set_autosmooth" getter="is_autosmooth" default="false">
Enables automatic smoothing. This overrides any smoothing on the CSG node and instead uses [member smoothing_angle] to calculate normals based on the angle between faces.
Children of a [CSGCombiner3D] node will be treated as a single mesh.
</member>
<member name="calculate_tangents" type="bool" setter="set_calculate_tangents" getter="is_calculating_tangents" default="true">
Calculate tangents for the CSG shape which allows the use of normal and height maps. This is only applied on the root shape, this setting is ignored on any child. Setting this to [code]false[/code] can speed up shape generation slightly.
</member>
Expand All @@ -91,6 +95,10 @@
<member name="operation" type="int" setter="set_operation" getter="get_operation" enum="CSGShape3D.Operation" default="0">
The operation that is performed on this shape. This is ignored for the first CSG child node as the operation is between this node and the previous child of this nodes parent.
</member>
<member name="smoothing_angle" type="float" setter="set_smoothing_angle" getter="get_smoothing_angle" default="50.0">
When autosmooth is enabled, faces with an angle between them greater than this will be smoothed, while faces with a smaller angle will remain sharp.
Note: An angle lower than 0.1 will cause all smoothing to be disabled, this can be used to increase performance.
</member>
<member name="snap" type="float" setter="set_snap" getter="get_snap" deprecated="The CSG library no longer uses snapping.">
This property does nothing.
</member>
Expand Down