initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled

This commit is contained in:
2025-09-16 20:46:46 -04:00
commit 9d30169a8d
13378 changed files with 7050105 additions and 0 deletions

54
modules/csg/SCsub Normal file
View File

@@ -0,0 +1,54 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
env_csg = env_modules.Clone()
env_csg.Append(CPPDEFINES=("MANIFOLD_PAR", "-1"))
# Thirdparty source files
thirdparty_obj = []
thirdparty_dir = "#thirdparty/manifold/"
thirdparty_sources = [
"src/boolean_result.cpp",
"src/boolean3.cpp",
"src/constructors.cpp",
"src/csg_tree.cpp",
"src/edge_op.cpp",
"src/face_op.cpp",
"src/impl.cpp",
"src/manifold.cpp",
"src/polygon.cpp",
"src/properties.cpp",
"src/quickhull.cpp",
"src/sdf.cpp",
"src/smoothing.cpp",
"src/sort.cpp",
"src/subdivision.cpp",
"src/tree2d.cpp",
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
env_csg.Prepend(CPPEXTPATH=[thirdparty_dir + "include"])
env_thirdparty = env_csg.Clone()
env_thirdparty.disable_warnings()
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
env.modules_sources += thirdparty_obj
# Godot source files
module_obj = []
env_csg.add_source_files(module_obj, "*.cpp")
if env.editor_build:
env_csg.add_source_files(module_obj, "editor/*.cpp")
env.modules_sources += module_obj
# Needed to force rebuilding the module files when the thirdparty library is updated.
env.Depends(module_obj, thirdparty_obj)

24
modules/csg/config.py Normal file
View File

@@ -0,0 +1,24 @@
def can_build(env, platform):
return not env["disable_3d"]
def configure(env):
pass
def get_doc_classes():
return [
"CSGBox3D",
"CSGCombiner3D",
"CSGCylinder3D",
"CSGMesh3D",
"CSGPolygon3D",
"CSGPrimitive3D",
"CSGShape3D",
"CSGSphere3D",
"CSGTorus3D",
]
def get_doc_path():
return "doc_classes"

117
modules/csg/csg.cpp Normal file
View File

@@ -0,0 +1,117 @@
/**************************************************************************/
/* csg.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "csg.h"
// CSGBrush
void CSGBrush::build_from_faces(const Vector<Vector3> &p_vertices, const Vector<Vector2> &p_uvs, const Vector<bool> &p_smooth, const Vector<Ref<Material>> &p_materials, const Vector<bool> &p_flip_faces) {
faces.clear();
int vc = p_vertices.size();
ERR_FAIL_COND((vc % 3) != 0);
const Vector3 *rv = p_vertices.ptr();
int uvc = p_uvs.size();
const Vector2 *ruv = p_uvs.ptr();
int sc = p_smooth.size();
const bool *rs = p_smooth.ptr();
int mc = p_materials.size();
const Ref<Material> *rm = p_materials.ptr();
int ic = p_flip_faces.size();
const bool *ri = p_flip_faces.ptr();
HashMap<Ref<Material>, int> material_map;
faces.resize(p_vertices.size() / 3);
for (int i = 0; i < faces.size(); i++) {
Face &f = faces.write[i];
f.vertices[0] = rv[i * 3 + 0];
f.vertices[1] = rv[i * 3 + 1];
f.vertices[2] = rv[i * 3 + 2];
if (uvc == vc) {
f.uvs[0] = ruv[i * 3 + 0];
f.uvs[1] = ruv[i * 3 + 1];
f.uvs[2] = ruv[i * 3 + 2];
}
if (sc == vc / 3) {
f.smooth = rs[i];
} else {
f.smooth = false;
}
if (ic == vc / 3) {
f.invert = ri[i];
} else {
f.invert = false;
}
if (mc == vc / 3) {
Ref<Material> mat = rm[i];
if (mat.is_valid()) {
HashMap<Ref<Material>, int>::ConstIterator E = material_map.find(mat);
if (E) {
f.material = E->value;
} else {
f.material = material_map.size();
material_map[mat] = f.material;
}
} else {
f.material = -1;
}
}
}
materials.resize(material_map.size());
for (const KeyValue<Ref<Material>, int> &E : material_map) {
materials.write[E.value] = E.key;
}
_regen_face_aabbs();
}
void CSGBrush::copy_from(const CSGBrush &p_brush, const Transform3D &p_xform) {
faces = p_brush.faces;
materials = p_brush.materials;
for (int i = 0; i < faces.size(); i++) {
for (int j = 0; j < 3; j++) {
faces.write[i].vertices[j] = p_xform.xform(p_brush.faces[i].vertices[j]);
}
}
_regen_face_aabbs();
}

66
modules/csg/csg.h Normal file
View File

@@ -0,0 +1,66 @@
/**************************************************************************/
/* csg.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "core/math/aabb.h"
#include "core/math/transform_3d.h"
#include "core/math/vector2.h"
#include "core/math/vector3.h"
#include "core/object/ref_counted.h"
#include "core/templates/vector.h"
#include "scene/resources/material.h"
struct CSGBrush {
struct Face {
Vector3 vertices[3];
Vector2 uvs[3];
AABB aabb;
bool smooth = false;
bool invert = false;
int material = 0;
};
Vector<Face> faces;
Vector<Ref<Material>> materials;
inline void _regen_face_aabbs() {
for (int i = 0; i < faces.size(); i++) {
faces.write[i].aabb = AABB();
faces.write[i].aabb.position = faces[i].vertices[0];
faces.write[i].aabb.expand_to(faces[i].vertices[1]);
faces.write[i].aabb.expand_to(faces[i].vertices[2]);
}
}
// Create a brush from faces.
void build_from_faces(const Vector<Vector3> &p_vertices, const Vector<Vector2> &p_uvs, const Vector<bool> &p_smooth, const Vector<Ref<Material>> &p_materials, const Vector<bool> &p_invert_faces);
void copy_from(const CSGBrush &p_brush, const Transform3D &p_xform);
};

2829
modules/csg/csg_shape.cpp Normal file

File diff suppressed because it is too large Load Diff

494
modules/csg/csg_shape.h Normal file
View File

@@ -0,0 +1,494 @@
/**************************************************************************/
/* csg_shape.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "csg.h"
#include "scene/3d/path_3d.h"
#include "scene/3d/visual_instance_3d.h"
#ifndef PHYSICS_3D_DISABLED
#include "scene/resources/3d/concave_polygon_shape_3d.h"
#endif // PHYSICS_3D_DISABLED
#include "thirdparty/misc/mikktspace.h"
class NavigationMesh;
class NavigationMeshSourceGeometryData3D;
class CSGShape3D : public GeometryInstance3D {
GDCLASS(CSGShape3D, GeometryInstance3D);
public:
enum Operation {
OPERATION_UNION,
OPERATION_INTERSECTION,
OPERATION_SUBTRACTION,
};
private:
Operation operation = OPERATION_UNION;
CSGShape3D *parent_shape = nullptr;
CSGBrush *brush = nullptr;
AABB node_aabb;
bool dirty = false;
bool last_visible = false;
float snap = 0.001;
#ifndef PHYSICS_3D_DISABLED
bool use_collision = false;
uint32_t collision_layer = 1;
uint32_t collision_mask = 1;
real_t collision_priority = 1.0;
Ref<ConcavePolygonShape3D> root_collision_shape;
RID root_collision_instance;
RID root_collision_debug_instance;
Transform3D debug_shape_old_transform;
#endif // PHYSICS_3D_DISABLED
bool calculate_tangents = true;
Ref<ArrayMesh> root_mesh;
struct Vector3Hasher {
_ALWAYS_INLINE_ uint32_t hash(const Vector3 &p_vec3) const {
uint32_t h = hash_murmur3_one_float(p_vec3.x);
h = hash_murmur3_one_float(p_vec3.y, h);
h = hash_murmur3_one_float(p_vec3.z, h);
return h;
}
};
struct ShapeUpdateSurface {
Vector<Vector3> vertices;
Vector<Vector3> normals;
Vector<Vector2> uvs;
Vector<real_t> tans;
Ref<Material> 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);
#ifndef PHYSICS_3D_DISABLED
void _update_collision_faces();
bool _is_debug_collision_shape_visible();
void _update_debug_collision_shape();
void _clear_debug_collision_shape();
void _on_transform_changed();
Vector<Vector3> _get_brush_collision_faces();
#endif // PHYSICS_3D_DISABLED
protected:
void _notification(int p_what);
virtual CSGBrush *_build_brush() = 0;
void _make_dirty(bool p_parent_removing = false);
PackedStringArray get_configuration_warnings() const override;
static void _bind_methods();
friend class CSGCombiner3D;
CSGBrush *_get_brush();
void _validate_property(PropertyInfo &p_property) const;
public:
Array get_meshes() const;
void update_shape();
void set_operation(Operation p_operation);
Operation get_operation() const;
virtual Vector<Vector3> get_brush_faces();
virtual AABB get_aabb() const override;
void set_use_collision(bool p_enable);
bool is_using_collision() const;
void set_collision_layer(uint32_t p_layer);
uint32_t get_collision_layer() const;
void set_collision_mask(uint32_t p_mask);
uint32_t get_collision_mask() const;
void set_collision_layer_value(int p_layer_number, bool p_value);
bool get_collision_layer_value(int p_layer_number) const;
void set_collision_mask_value(int p_layer_number, bool p_value);
bool get_collision_mask_value(int p_layer_number) const;
RID _get_root_collision_instance() const;
void set_collision_priority(real_t p_priority);
real_t get_collision_priority() const;
#ifndef DISABLE_DEPRECATED
void set_snap(float p_snap);
float get_snap() const;
#endif // DISABLE_DEPRECATED
void set_calculate_tangents(bool p_calculate_tangents);
bool is_calculating_tangents() const;
bool is_root_shape() const;
Ref<ArrayMesh> bake_static_mesh();
#ifndef PHYSICS_3D_DISABLED
Ref<ConcavePolygonShape3D> bake_collision_shape();
#endif // PHYSICS_3D_DISABLED
virtual Ref<TriangleMesh> generate_triangle_mesh() const override;
#ifndef NAVIGATION_3D_DISABLED
private:
static Callable _navmesh_source_geometry_parsing_callback;
static RID _navmesh_source_geometry_parser;
public:
static void navmesh_parse_init();
static void navmesh_parse_source_geometry(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node);
#endif // NAVIGATION_3D_DISABLED
CSGShape3D();
~CSGShape3D();
};
VARIANT_ENUM_CAST(CSGShape3D::Operation)
class CSGCombiner3D : public CSGShape3D {
GDCLASS(CSGCombiner3D, CSGShape3D);
private:
virtual CSGBrush *_build_brush() override;
public:
CSGCombiner3D();
};
class CSGPrimitive3D : public CSGShape3D {
GDCLASS(CSGPrimitive3D, CSGShape3D);
protected:
bool flip_faces;
CSGBrush *_create_brush_from_arrays(const Vector<Vector3> &p_vertices, const Vector<Vector2> &p_uv, const Vector<bool> &p_smooth, const Vector<Ref<Material>> &p_materials);
static void _bind_methods();
public:
void set_flip_faces(bool p_invert);
bool get_flip_faces();
CSGPrimitive3D();
};
class CSGMesh3D : public CSGPrimitive3D {
GDCLASS(CSGMesh3D, CSGPrimitive3D);
virtual CSGBrush *_build_brush() override;
Ref<Mesh> mesh;
Ref<Material> material;
void _mesh_changed();
protected:
static void _bind_methods();
public:
void set_mesh(const Ref<Mesh> &p_mesh);
Ref<Mesh> get_mesh();
void set_material(const Ref<Material> &p_material);
Ref<Material> get_material() const;
};
class CSGSphere3D : public CSGPrimitive3D {
GDCLASS(CSGSphere3D, CSGPrimitive3D);
virtual CSGBrush *_build_brush() override;
Ref<Material> material;
bool smooth_faces;
float radius;
int radial_segments;
int rings;
protected:
static void _bind_methods();
public:
void set_radius(const float p_radius);
float get_radius() const;
void set_radial_segments(const int p_radial_segments);
int get_radial_segments() const;
void set_rings(const int p_rings);
int get_rings() const;
void set_material(const Ref<Material> &p_material);
Ref<Material> 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> material;
Vector3 size = Vector3(1, 1, 1);
protected:
static void _bind_methods();
#ifndef DISABLE_DEPRECATED
// Kept for compatibility from 3.x to 4.0.
bool _set(const StringName &p_name, const Variant &p_value);
#endif
public:
void set_size(const Vector3 &p_size);
Vector3 get_size() const;
void set_material(const Ref<Material> &p_material);
Ref<Material> get_material() const;
CSGBox3D() {}
};
class CSGCylinder3D : public CSGPrimitive3D {
GDCLASS(CSGCylinder3D, CSGPrimitive3D);
virtual CSGBrush *_build_brush() override;
Ref<Material> material;
float radius;
float height;
int sides;
bool cone;
bool smooth_faces;
protected:
static void _bind_methods();
public:
void set_radius(const float p_radius);
float get_radius() const;
void set_height(const float p_height);
float get_height() const;
void set_sides(const int p_sides);
int get_sides() const;
void set_cone(const bool p_cone);
bool is_cone() const;
void set_smooth_faces(bool p_smooth_faces);
bool get_smooth_faces() const;
void set_material(const Ref<Material> &p_material);
Ref<Material> get_material() const;
CSGCylinder3D();
};
class CSGTorus3D : public CSGPrimitive3D {
GDCLASS(CSGTorus3D, CSGPrimitive3D);
virtual CSGBrush *_build_brush() override;
Ref<Material> material;
float inner_radius;
float outer_radius;
int sides;
int ring_sides;
bool smooth_faces;
protected:
static void _bind_methods();
public:
void set_inner_radius(const float p_inner_radius);
float get_inner_radius() const;
void set_outer_radius(const float p_outer_radius);
float get_outer_radius() const;
void set_sides(const int p_sides);
int get_sides() const;
void set_ring_sides(const int p_ring_sides);
int get_ring_sides() const;
void set_smooth_faces(bool p_smooth_faces);
bool get_smooth_faces() const;
void set_material(const Ref<Material> &p_material);
Ref<Material> get_material() const;
CSGTorus3D();
};
class CSGPolygon3D : public CSGPrimitive3D {
GDCLASS(CSGPolygon3D, CSGPrimitive3D);
public:
enum Mode {
MODE_DEPTH,
MODE_SPIN,
MODE_PATH
};
enum PathIntervalType {
PATH_INTERVAL_DISTANCE,
PATH_INTERVAL_SUBDIVIDE
};
enum PathRotation {
PATH_ROTATION_POLYGON,
PATH_ROTATION_PATH,
PATH_ROTATION_PATH_FOLLOW,
};
private:
virtual CSGBrush *_build_brush() override;
Vector<Vector2> polygon;
Ref<Material> material;
Mode mode;
float depth;
float spin_degrees;
int spin_sides;
NodePath path_node;
PathIntervalType path_interval_type;
float path_interval;
float path_simplify_angle;
PathRotation path_rotation;
bool path_rotation_accurate;
bool path_local;
Path3D *path = nullptr;
bool smooth_faces;
bool path_continuous_u;
real_t path_u_distance;
bool path_joined;
bool _is_editable_3d_polygon() const;
bool _has_editable_3d_polygon_no_depth() const;
void _path_changed();
void _path_exited();
protected:
static void _bind_methods();
void _validate_property(PropertyInfo &p_property) const;
void _notification(int p_what);
public:
void set_polygon(const Vector<Vector2> &p_polygon);
Vector<Vector2> get_polygon() const;
void set_mode(Mode p_mode);
Mode get_mode() const;
void set_depth(float p_depth);
float get_depth() const;
void set_spin_degrees(float p_spin_degrees);
float get_spin_degrees() const;
void set_spin_sides(int p_spin_sides);
int get_spin_sides() const;
void set_path_node(const NodePath &p_path);
NodePath get_path_node() const;
void set_path_interval_type(PathIntervalType p_interval_type);
PathIntervalType get_path_interval_type() const;
void set_path_interval(float p_interval);
float get_path_interval() const;
void set_path_simplify_angle(float p_angle);
float get_path_simplify_angle() const;
void set_path_rotation(PathRotation p_rotation);
PathRotation get_path_rotation() const;
void set_path_rotation_accurate(bool p_enable);
bool get_path_rotation_accurate() const;
void set_path_local(bool p_enable);
bool is_path_local() const;
void set_path_continuous_u(bool p_enable);
bool is_path_continuous_u() const;
void set_path_u_distance(real_t p_path_u_distance);
real_t get_path_u_distance() const;
void set_path_joined(bool p_enable);
bool is_path_joined() const;
void set_smooth_faces(bool p_smooth_faces);
bool get_smooth_faces() const;
void set_material(const Ref<Material> &p_material);
Ref<Material> get_material() const;
CSGPolygon3D();
};
VARIANT_ENUM_CAST(CSGPolygon3D::Mode)
VARIANT_ENUM_CAST(CSGPolygon3D::PathRotation)
VARIANT_ENUM_CAST(CSGPolygon3D::PathIntervalType)

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGBox3D" inherits="CSGPrimitive3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
A CSG Box shape.
</brief_description>
<description>
This node allows you to create a box for use with the CSG system.
[b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance3D] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay.
</description>
<tutorials>
<link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link>
</tutorials>
<members>
<member name="material" type="Material" setter="set_material" getter="get_material">
The material used to render the box.
</member>
<member name="size" type="Vector3" setter="set_size" getter="get_size" default="Vector3(1, 1, 1)">
The box's width, height and depth.
</member>
</members>
</class>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGCombiner3D" inherits="CSGShape3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
A CSG node that allows you to combine other CSG modifiers.
</brief_description>
<description>
For complex arrangements of shapes, it is sometimes needed to add structure to your CSG nodes. The CSGCombiner3D node allows you to create this structure. The node encapsulates the result of the CSG operations of its children. In this way, it is possible to do operations on one set of shapes that are children of one CSGCombiner3D node, and a set of separate operations on a second set of shapes that are children of a second CSGCombiner3D node, and then do an operation that takes the two end results as its input to create the final shape.
[b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance3D] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay.
</description>
<tutorials>
<link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link>
</tutorials>
</class>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGCylinder3D" inherits="CSGPrimitive3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
A CSG Cylinder shape.
</brief_description>
<description>
This node allows you to create a cylinder (or cone) for use with the CSG system.
[b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance3D] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay.
</description>
<tutorials>
<link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link>
</tutorials>
<members>
<member name="cone" type="bool" setter="set_cone" getter="is_cone" default="false">
If [code]true[/code] a cone is created, the [member radius] will only apply to one side.
</member>
<member name="height" type="float" setter="set_height" getter="get_height" default="2.0">
The height of the cylinder.
</member>
<member name="material" type="Material" setter="set_material" getter="get_material">
The material used to render the cylinder.
</member>
<member name="radius" type="float" setter="set_radius" getter="get_radius" default="0.5">
The radius of the cylinder.
</member>
<member name="sides" type="int" setter="set_sides" getter="get_sides" default="8">
The number of sides of the cylinder, the higher this number the more detail there will be in the cylinder.
</member>
<member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces" default="true">
If [code]true[/code] the normals of the cylinder are set to give a smooth effect making the cylinder seem rounded. If [code]false[/code] the cylinder will have a flat shaded look.
</member>
</members>
</class>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGMesh3D" inherits="CSGPrimitive3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
A CSG Mesh shape that uses a mesh resource.
</brief_description>
<description>
This CSG node allows you to use any mesh resource as a CSG shape, provided it is [i]manifold[/i]. A manifold shape is closed, does not self-intersect, does not contain internal faces and has no edges that connect to more than two faces. See also [CSGPolygon3D] for drawing 2D extruded polygons to be used as CSG nodes.
[b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance3D] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay.
</description>
<tutorials>
<link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link>
</tutorials>
<members>
<member name="material" type="Material" setter="set_material" getter="get_material">
The [Material] used in drawing the CSG shape.
</member>
<member name="mesh" type="Mesh" setter="set_mesh" getter="get_mesh">
The [Mesh] resource to use as a CSG shape.
[b]Note:[/b] Some [Mesh] types such as [PlaneMesh], [PointMesh], [QuadMesh], and [RibbonTrailMesh] are excluded from the type hint for this property, as these primitives are non-[i]manifold[/i] and thus not compatible with the CSG algorithm.
[b]Note:[/b] When using an [ArrayMesh], all vertex attributes except [constant Mesh.ARRAY_VERTEX], [constant Mesh.ARRAY_NORMAL] and [constant Mesh.ARRAY_TEX_UV] are left unused. Only [constant Mesh.ARRAY_VERTEX] and [constant Mesh.ARRAY_TEX_UV] will be passed to the GPU.
[constant Mesh.ARRAY_NORMAL] is only used to determine which faces require the use of flat shading. By default, CSGMesh will ignore the mesh's vertex normals, recalculate them for each vertex and use a smooth shader. If a flat shader is required for a face, ensure that all vertex normals of the face are approximately equal.
</member>
</members>
</class>

View File

@@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGPolygon3D" inherits="CSGPrimitive3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Extrudes a 2D polygon shape to create a 3D mesh.
</brief_description>
<description>
An array of 2D points is extruded to quickly and easily create a variety of 3D meshes. See also [CSGMesh3D] for using 3D meshes as CSG nodes.
[b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance3D] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay.
</description>
<tutorials>
<link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link>
</tutorials>
<members>
<member name="depth" type="float" setter="set_depth" getter="get_depth" default="1.0">
When [member mode] is [constant MODE_DEPTH], the depth of the extrusion.
</member>
<member name="material" type="Material" setter="set_material" getter="get_material">
Material to use for the resulting mesh. The UV maps the top half of the material to the extruded shape (U along the length of the extrusions and V around the outline of the [member polygon]), the bottom-left quarter to the front end face, and the bottom-right quarter to the back end face.
</member>
<member name="mode" type="int" setter="set_mode" getter="get_mode" enum="CSGPolygon3D.Mode" default="0">
The [member mode] used to extrude the [member polygon].
</member>
<member name="path_continuous_u" type="bool" setter="set_path_continuous_u" getter="is_path_continuous_u">
When [member mode] is [constant MODE_PATH], by default, the top half of the [member material] is stretched along the entire length of the extruded shape. If [code]false[/code] the top half of the material is repeated every step of the extrusion.
</member>
<member name="path_interval" type="float" setter="set_path_interval" getter="get_path_interval">
When [member mode] is [constant MODE_PATH], the path interval or ratio of path points to extrusions.
</member>
<member name="path_interval_type" type="int" setter="set_path_interval_type" getter="get_path_interval_type" enum="CSGPolygon3D.PathIntervalType">
When [member mode] is [constant MODE_PATH], this will determine if the interval should be by distance ([constant PATH_INTERVAL_DISTANCE]) or subdivision fractions ([constant PATH_INTERVAL_SUBDIVIDE]).
</member>
<member name="path_joined" type="bool" setter="set_path_joined" getter="is_path_joined">
When [member mode] is [constant MODE_PATH], if [code]true[/code] the ends of the path are joined, by adding an extrusion between the last and first points of the path.
</member>
<member name="path_local" type="bool" setter="set_path_local" getter="is_path_local">
When [member mode] is [constant MODE_PATH], if [code]true[/code] the [Transform3D] of the [CSGPolygon3D] is used as the starting point for the extrusions, not the [Transform3D] of the [member path_node].
</member>
<member name="path_node" type="NodePath" setter="set_path_node" getter="get_path_node">
When [member mode] is [constant MODE_PATH], the location of the [Path3D] object used to extrude the [member polygon].
</member>
<member name="path_rotation" type="int" setter="set_path_rotation" getter="get_path_rotation" enum="CSGPolygon3D.PathRotation">
When [member mode] is [constant MODE_PATH], the path rotation method used to rotate the [member polygon] as it is extruded.
</member>
<member name="path_rotation_accurate" type="bool" setter="set_path_rotation_accurate" getter="get_path_rotation_accurate">
When [member mode] is [constant MODE_PATH], if [code]true[/code] the polygon will be rotated according to the proper tangent of the path at the sampled points. If [code]false[/code] an approximation is used, which decreases in accuracy as the number of subdivisions decreases.
</member>
<member name="path_simplify_angle" type="float" setter="set_path_simplify_angle" getter="get_path_simplify_angle">
When [member mode] is [constant MODE_PATH], extrusions that are less than this angle, will be merged together to reduce polygon count.
</member>
<member name="path_u_distance" type="float" setter="set_path_u_distance" getter="get_path_u_distance">
When [member mode] is [constant MODE_PATH], this is the distance along the path, in meters, the texture coordinates will tile. When set to 0, texture coordinates will match geometry exactly with no tiling.
</member>
<member name="polygon" type="PackedVector2Array" setter="set_polygon" getter="get_polygon" default="PackedVector2Array(0, 0, 0, 1, 1, 1, 1, 0)">
The point array that defines the 2D polygon that is extruded. This can be a convex or concave polygon with 3 or more points. The polygon must [i]not[/i] have any intersecting edges. Otherwise, triangulation will fail and no mesh will be generated.
[b]Note:[/b] If only 1 or 2 points are defined in [member polygon], no mesh will be generated.
</member>
<member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces" default="false">
If [code]true[/code], applies smooth shading to the extrusions.
</member>
<member name="spin_degrees" type="float" setter="set_spin_degrees" getter="get_spin_degrees">
When [member mode] is [constant MODE_SPIN], the total number of degrees the [member polygon] is rotated when extruding.
</member>
<member name="spin_sides" type="int" setter="set_spin_sides" getter="get_spin_sides">
When [member mode] is [constant MODE_SPIN], the number of extrusions made.
</member>
</members>
<constants>
<constant name="MODE_DEPTH" value="0" enum="Mode">
The [member polygon] shape is extruded along the negative Z axis.
</constant>
<constant name="MODE_SPIN" value="1" enum="Mode">
The [member polygon] shape is extruded by rotating it around the Y axis.
</constant>
<constant name="MODE_PATH" value="2" enum="Mode">
The [member polygon] shape is extruded along the [Path3D] specified in [member path_node].
</constant>
<constant name="PATH_ROTATION_POLYGON" value="0" enum="PathRotation">
The [member polygon] shape is not rotated.
[b]Note:[/b] Requires the path Z coordinates to continually decrease to ensure viable shapes.
</constant>
<constant name="PATH_ROTATION_PATH" value="1" enum="PathRotation">
The [member polygon] shape is rotated along the path, but it is not rotated around the path axis.
[b]Note:[/b] Requires the path Z coordinates to continually decrease to ensure viable shapes.
</constant>
<constant name="PATH_ROTATION_PATH_FOLLOW" value="2" enum="PathRotation">
The [member polygon] shape follows the path and its rotations around the path axis.
</constant>
<constant name="PATH_INTERVAL_DISTANCE" value="0" enum="PathIntervalType">
When [member mode] is set to [constant MODE_PATH], [member path_interval] will determine the distance, in meters, each interval of the path will extrude.
</constant>
<constant name="PATH_INTERVAL_SUBDIVIDE" value="1" enum="PathIntervalType">
When [member mode] is set to [constant MODE_PATH], [member path_interval] will subdivide the polygons along the path.
</constant>
</constants>
</class>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGPrimitive3D" inherits="CSGShape3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Base class for CSG primitives.
</brief_description>
<description>
Parent class for various CSG primitives. It contains code and functionality that is common between them. It cannot be used directly. Instead use one of the various classes that inherit from it.
[b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance3D] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay.
</description>
<tutorials>
<link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link>
</tutorials>
<members>
<member name="flip_faces" type="bool" setter="set_flip_faces" getter="get_flip_faces" default="false">
If set, the order of the vertices in each triangle are reversed resulting in the backside of the mesh being drawn.
</member>
</members>
</class>

View File

@@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGShape3D" inherits="GeometryInstance3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
The CSG base class.
</brief_description>
<description>
This is the CSG base class that provides CSG operation support to the various CSG nodes in Godot.
[b]Performance:[/b] CSG nodes are only intended for prototyping as they have a significant CPU performance cost. Consider baking final CSG operation results into static geometry that replaces the CSG nodes.
Individual CSG root node results can be baked to nodes with static resources with the editor menu that appears when a CSG root node is selected.
Individual CSG root nodes can also be baked to static resources with scripts by calling [method bake_static_mesh] for the visual mesh or [method bake_collision_shape] for the physics collision.
Entire scenes of CSG nodes can be baked to static geometry and exported with the editor glTF scene exporter: [b]Scene &gt; Export As... &gt; glTF 2.0 Scene...[/b]
</description>
<tutorials>
<link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link>
</tutorials>
<methods>
<method name="bake_collision_shape">
<return type="ConcavePolygonShape3D" />
<description>
Returns a baked physics [ConcavePolygonShape3D] of this node's CSG operation result. Returns an empty shape if the node is not a CSG root node or has no valid geometry.
[b]Performance:[/b] If the CSG operation results in a very detailed geometry with many faces physics performance will be very slow. Concave shapes should in general only be used for static level geometry and not with dynamic objects that are moving.
[b]Note:[/b] CSG mesh data updates are deferred, which means they are updated with a delay of one rendered frame. To avoid getting an empty shape or outdated mesh data, make sure to call [code]await get_tree().process_frame[/code] before using [method bake_collision_shape] in [method Node._ready] or after changing properties on the [CSGShape3D].
</description>
</method>
<method name="bake_static_mesh">
<return type="ArrayMesh" />
<description>
Returns a baked static [ArrayMesh] of this node's CSG operation result. Materials from involved CSG nodes are added as extra mesh surfaces. Returns an empty mesh if the node is not a CSG root node or has no valid geometry.
[b]Note:[/b] CSG mesh data updates are deferred, which means they are updated with a delay of one rendered frame. To avoid getting an empty mesh or outdated mesh data, make sure to call [code]await get_tree().process_frame[/code] before using [method bake_static_mesh] in [method Node._ready] or after changing properties on the [CSGShape3D].
</description>
</method>
<method name="get_collision_layer_value" qualifiers="const">
<return type="bool" />
<param index="0" name="layer_number" type="int" />
<description>
Returns whether or not the specified layer of the [member collision_layer] is enabled, given a [param layer_number] between 1 and 32.
</description>
</method>
<method name="get_collision_mask_value" qualifiers="const">
<return type="bool" />
<param index="0" name="layer_number" type="int" />
<description>
Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [param layer_number] between 1 and 32.
</description>
</method>
<method name="get_meshes" qualifiers="const">
<return type="Array" />
<description>
Returns an [Array] with two elements, the first is the [Transform3D] of this node and the second is the root [Mesh] of this node. Only works when this node is the root shape.
[b]Note:[/b] CSG mesh data updates are deferred, which means they are updated with a delay of one rendered frame. To avoid getting an empty shape or outdated mesh data, make sure to call [code]await get_tree().process_frame[/code] before using [method get_meshes] in [method Node._ready] or after changing properties on the [CSGShape3D].
</description>
</method>
<method name="is_root_shape" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if this is a root shape and is thus the object that is rendered.
</description>
</method>
<method name="set_collision_layer_value">
<return type="void" />
<param index="0" name="layer_number" type="int" />
<param index="1" name="value" type="bool" />
<description>
Based on [param value], enables or disables the specified layer in the [member collision_layer], given a [param layer_number] between 1 and 32.
</description>
</method>
<method name="set_collision_mask_value">
<return type="void" />
<param index="0" name="layer_number" type="int" />
<param index="1" name="value" type="bool" />
<description>
Based on [param value], enables or disables the specified layer in the [member collision_mask], given a [param layer_number] between 1 and 32.
</description>
</method>
</methods>
<members>
<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>
<member name="collision_layer" type="int" setter="set_collision_layer" getter="get_collision_layer" default="1">
The physics layers this area is in.
Collidable objects can exist in any of 32 different layers. These layers work like a tagging system, and are not visual. A collidable can use these layers to select with which objects it can collide, using the collision_mask property.
A contact is detected if object A is in any of the layers that object B scans, or object B is in any layer scanned by object A. See [url=$DOCS_URL/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
</member>
<member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask" default="1">
The physics layers this CSG shape scans for collisions. Only effective if [member use_collision] is [code]true[/code]. See [url=$DOCS_URL/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
</member>
<member name="collision_priority" type="float" setter="set_collision_priority" getter="get_collision_priority" default="1.0">
The priority used to solve colliding when occurring penetration. Only effective if [member use_collision] is [code]true[/code]. The higher the priority is, the lower the penetration into the object will be. This can for example be used to prevent the player from breaking through the boundaries of a level.
</member>
<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="snap" type="float" setter="set_snap" getter="get_snap" deprecated="The CSG library no longer uses snapping.">
This property does nothing.
</member>
<member name="use_collision" type="bool" setter="set_use_collision" getter="is_using_collision" default="false">
Adds a collision shape to the physics engine for our CSG shape. This will always act like a static body. Note that the collision shape is still active even if the CSG shape itself is hidden. See also [member collision_mask] and [member collision_priority].
</member>
</members>
<constants>
<constant name="OPERATION_UNION" value="0" enum="Operation">
Geometry of both primitives is merged, intersecting geometry is removed.
</constant>
<constant name="OPERATION_INTERSECTION" value="1" enum="Operation">
Only intersecting geometry remains, the rest is removed.
</constant>
<constant name="OPERATION_SUBTRACTION" value="2" enum="Operation">
The second shape is subtracted from the first, leaving a dent with its shape.
</constant>
</constants>
</class>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGSphere3D" inherits="CSGPrimitive3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
A CSG Sphere shape.
</brief_description>
<description>
This node allows you to create a sphere for use with the CSG system.
[b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance3D] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay.
</description>
<tutorials>
<link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link>
</tutorials>
<members>
<member name="material" type="Material" setter="set_material" getter="get_material">
The material used to render the sphere.
</member>
<member name="radial_segments" type="int" setter="set_radial_segments" getter="get_radial_segments" default="12">
Number of vertical slices for the sphere.
</member>
<member name="radius" type="float" setter="set_radius" getter="get_radius" default="0.5">
Radius of the sphere.
</member>
<member name="rings" type="int" setter="set_rings" getter="get_rings" default="6">
Number of horizontal slices for the sphere.
</member>
<member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces" default="true">
If [code]true[/code] the normals of the sphere are set to give a smooth effect making the sphere seem rounded. If [code]false[/code] the sphere will have a flat shaded look.
</member>
</members>
</class>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGTorus3D" inherits="CSGPrimitive3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
A CSG Torus shape.
</brief_description>
<description>
This node allows you to create a torus for use with the CSG system.
[b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance3D] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay.
</description>
<tutorials>
<link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link>
</tutorials>
<members>
<member name="inner_radius" type="float" setter="set_inner_radius" getter="get_inner_radius" default="0.5">
The inner radius of the torus.
</member>
<member name="material" type="Material" setter="set_material" getter="get_material">
The material used to render the torus.
</member>
<member name="outer_radius" type="float" setter="set_outer_radius" getter="get_outer_radius" default="1.0">
The outer radius of the torus.
</member>
<member name="ring_sides" type="int" setter="set_ring_sides" getter="get_ring_sides" default="6">
The number of edges each ring of the torus is constructed of.
</member>
<member name="sides" type="int" setter="set_sides" getter="get_sides" default="8">
The number of slices the torus is constructed of.
</member>
<member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces" default="true">
If [code]true[/code] the normals of the torus are set to give a smooth effect making the torus seem rounded. If [code]false[/code] the torus will have a flat shaded look.
</member>
</members>
</class>

View File

@@ -0,0 +1,507 @@
/**************************************************************************/
/* csg_gizmos.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "csg_gizmos.h"
#include "editor/editor_node.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/scene/3d/gizmos/gizmo_3d_helper.h"
#include "editor/scene/3d/node_3d_editor_plugin.h"
#include "editor/settings/editor_settings.h"
#include "scene/3d/camera_3d.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/physics/collision_shape_3d.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/menu_button.h"
void CSGShapeEditor::_node_removed(Node *p_node) {
if (p_node == node) {
node = nullptr;
options->hide();
}
}
void CSGShapeEditor::edit(CSGShape3D *p_csg_shape) {
node = p_csg_shape;
if (node) {
options->show();
} else {
options->hide();
}
}
void CSGShapeEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
options->set_button_icon(get_editor_theme_icon(SNAME("CSGCombiner3D")));
} break;
}
}
void CSGShapeEditor::_menu_option(int p_option) {
Array meshes = node->get_meshes();
if (meshes.is_empty()) {
err_dialog->set_text(TTR("CSG operation returned an empty array."));
err_dialog->popup_centered();
return;
}
switch (p_option) {
case MENU_OPTION_BAKE_MESH_INSTANCE: {
_create_baked_mesh_instance();
} break;
case MENU_OPTION_BAKE_COLLISION_SHAPE: {
_create_baked_collision_shape();
} break;
}
}
void CSGShapeEditor::_create_baked_mesh_instance() {
if (node == get_tree()->get_edited_scene_root()) {
err_dialog->set_text(TTR("Can not add a baked mesh as sibling for the scene root.\nMove the CSG root node below a parent node."));
err_dialog->popup_centered();
return;
}
Ref<ArrayMesh> mesh = node->bake_static_mesh();
if (mesh.is_null()) {
err_dialog->set_text(TTR("CSG operation returned an empty mesh."));
err_dialog->popup_centered();
return;
}
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Create baked CSGShape3D Mesh Instance"));
Node *owner = get_tree()->get_edited_scene_root();
MeshInstance3D *mi = memnew(MeshInstance3D);
mi->set_mesh(mesh);
mi->set_name("CSGBakedMeshInstance3D");
mi->set_transform(node->get_transform());
ur->add_do_method(node, "add_sibling", mi, true);
ur->add_do_method(mi, "set_owner", owner);
ur->add_do_method(Node3DEditor::get_singleton(), SceneStringName(_request_gizmo), mi);
ur->add_do_reference(mi);
ur->add_undo_method(node->get_parent(), "remove_child", mi);
ur->commit_action();
}
void CSGShapeEditor::_create_baked_collision_shape() {
if (node == get_tree()->get_edited_scene_root()) {
err_dialog->set_text(TTR("Can not add a baked collision shape as sibling for the scene root.\nMove the CSG root node below a parent node."));
err_dialog->popup_centered();
return;
}
Ref<Shape3D> shape = node->bake_collision_shape();
if (shape.is_null()) {
err_dialog->set_text(TTR("CSG operation returned an empty shape."));
err_dialog->popup_centered();
return;
}
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Create baked CSGShape3D Collision Shape"));
Node *owner = get_tree()->get_edited_scene_root();
CollisionShape3D *cshape = memnew(CollisionShape3D);
cshape->set_shape(shape);
cshape->set_name("CSGBakedCollisionShape3D");
cshape->set_transform(node->get_transform());
ur->add_do_method(node, "add_sibling", cshape, true);
ur->add_do_method(cshape, "set_owner", owner);
ur->add_do_method(Node3DEditor::get_singleton(), SceneStringName(_request_gizmo), cshape);
ur->add_do_reference(cshape);
ur->add_undo_method(node->get_parent(), "remove_child", cshape);
ur->commit_action();
}
CSGShapeEditor::CSGShapeEditor() {
options = memnew(MenuButton);
options->hide();
options->set_text(TTR("CSG"));
options->set_switch_on_hover(true);
options->set_flat(false);
options->set_theme_type_variation("FlatMenuButton");
Node3DEditor::get_singleton()->add_control_to_menu_panel(options);
options->get_popup()->add_item(TTR("Bake Mesh Instance"), MENU_OPTION_BAKE_MESH_INSTANCE);
options->get_popup()->add_item(TTR("Bake Collision Shape"), MENU_OPTION_BAKE_COLLISION_SHAPE);
options->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &CSGShapeEditor::_menu_option));
err_dialog = memnew(AcceptDialog);
add_child(err_dialog);
}
///////////
CSGShape3DGizmoPlugin::CSGShape3DGizmoPlugin() {
helper.instantiate();
Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/csg");
create_material("shape_union_material", gizmo_color);
create_material("shape_union_solid_material", gizmo_color);
gizmo_color.invert();
create_material("shape_subtraction_material", gizmo_color);
create_material("shape_subtraction_solid_material", gizmo_color);
gizmo_color.r = 0.95;
gizmo_color.g = 0.95;
gizmo_color.b = 0.95;
create_material("shape_intersection_material", gizmo_color);
create_material("shape_intersection_solid_material", gizmo_color);
create_handle_material("handles");
}
String CSGShape3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
CSGShape3D *cs = Object::cast_to<CSGShape3D>(p_gizmo->get_node_3d());
if (Object::cast_to<CSGSphere3D>(cs)) {
return "Radius";
}
if (Object::cast_to<CSGBox3D>(cs)) {
return helper->box_get_handle_name(p_id);
}
if (Object::cast_to<CSGCylinder3D>(cs)) {
return p_id == 0 ? "Radius" : "Height";
}
if (Object::cast_to<CSGTorus3D>(cs)) {
return p_id == 0 ? "InnerRadius" : "OuterRadius";
}
return "";
}
Variant CSGShape3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
CSGShape3D *cs = Object::cast_to<CSGShape3D>(p_gizmo->get_node_3d());
if (Object::cast_to<CSGSphere3D>(cs)) {
CSGSphere3D *s = Object::cast_to<CSGSphere3D>(cs);
return s->get_radius();
}
if (Object::cast_to<CSGBox3D>(cs)) {
CSGBox3D *s = Object::cast_to<CSGBox3D>(cs);
return s->get_size();
}
if (Object::cast_to<CSGCylinder3D>(cs)) {
CSGCylinder3D *s = Object::cast_to<CSGCylinder3D>(cs);
return Vector2(s->get_radius(), s->get_height());
}
if (Object::cast_to<CSGTorus3D>(cs)) {
CSGTorus3D *s = Object::cast_to<CSGTorus3D>(cs);
return p_id == 0 ? s->get_inner_radius() : s->get_outer_radius();
}
return Variant();
}
void CSGShape3DGizmoPlugin::begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) {
helper->initialize_handle_action(get_handle_value(p_gizmo, p_id, p_secondary), p_gizmo->get_node_3d()->get_global_transform());
}
void CSGShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) {
CSGShape3D *cs = Object::cast_to<CSGShape3D>(p_gizmo->get_node_3d());
Vector3 sg[2];
helper->get_segment(p_camera, p_point, sg);
if (Object::cast_to<CSGSphere3D>(cs)) {
CSGSphere3D *s = Object::cast_to<CSGSphere3D>(cs);
Vector3 ra, rb;
Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb);
float d = ra.x;
if (Node3DEditor::get_singleton()->is_snap_enabled()) {
d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
}
if (d < 0.001) {
d = 0.001;
}
s->set_radius(d);
}
if (Object::cast_to<CSGBox3D>(cs)) {
CSGBox3D *s = Object::cast_to<CSGBox3D>(cs);
Vector3 size = s->get_size();
Vector3 position;
helper->box_set_handle(sg, p_id, size, position);
s->set_size(size);
s->set_global_position(position);
}
if (Object::cast_to<CSGCylinder3D>(cs)) {
CSGCylinder3D *s = Object::cast_to<CSGCylinder3D>(cs);
real_t height = s->get_height();
real_t radius = s->get_radius();
Vector3 position;
helper->cylinder_set_handle(sg, p_id, height, radius, position);
s->set_height(height);
s->set_radius(radius);
s->set_global_position(position);
}
if (Object::cast_to<CSGTorus3D>(cs)) {
CSGTorus3D *s = Object::cast_to<CSGTorus3D>(cs);
Vector3 axis;
axis[0] = 1.0;
Vector3 ra, rb;
Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
float d = axis.dot(ra);
if (Node3DEditor::get_singleton()->is_snap_enabled()) {
d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
}
if (d < 0.001) {
d = 0.001;
}
if (p_id == 0) {
s->set_inner_radius(d);
} else if (p_id == 1) {
s->set_outer_radius(d);
}
}
}
void CSGShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) {
CSGShape3D *cs = Object::cast_to<CSGShape3D>(p_gizmo->get_node_3d());
if (Object::cast_to<CSGSphere3D>(cs)) {
CSGSphere3D *s = Object::cast_to<CSGSphere3D>(cs);
if (p_cancel) {
s->set_radius(p_restore);
return;
}
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Sphere Shape Radius"));
ur->add_do_method(s, "set_radius", s->get_radius());
ur->add_undo_method(s, "set_radius", p_restore);
ur->commit_action();
}
if (Object::cast_to<CSGBox3D>(cs)) {
helper->box_commit_handle(TTR("Change CSG Box Size"), p_cancel, cs);
}
if (Object::cast_to<CSGCylinder3D>(cs)) {
helper->cylinder_commit_handle(p_id, TTR("Change CSG Cylinder Radius"), TTR("Change CSG Cylinder Height"), p_cancel, cs);
}
if (Object::cast_to<CSGTorus3D>(cs)) {
CSGTorus3D *s = Object::cast_to<CSGTorus3D>(cs);
if (p_cancel) {
if (p_id == 0) {
s->set_inner_radius(p_restore);
} else {
s->set_outer_radius(p_restore);
}
return;
}
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
if (p_id == 0) {
ur->create_action(TTR("Change Torus Inner Radius"));
ur->add_do_method(s, "set_inner_radius", s->get_inner_radius());
ur->add_undo_method(s, "set_inner_radius", p_restore);
} else {
ur->create_action(TTR("Change Torus Outer Radius"));
ur->add_do_method(s, "set_outer_radius", s->get_outer_radius());
ur->add_undo_method(s, "set_outer_radius", p_restore);
}
ur->commit_action();
}
}
bool CSGShape3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<CSGSphere3D>(p_spatial) || Object::cast_to<CSGBox3D>(p_spatial) || Object::cast_to<CSGCylinder3D>(p_spatial) || Object::cast_to<CSGTorus3D>(p_spatial) || Object::cast_to<CSGMesh3D>(p_spatial) || Object::cast_to<CSGPolygon3D>(p_spatial);
}
String CSGShape3DGizmoPlugin::get_gizmo_name() const {
return "CSGShape3D";
}
int CSGShape3DGizmoPlugin::get_priority() const {
return -1;
}
bool CSGShape3DGizmoPlugin::is_selectable_when_hidden() const {
return true;
}
void CSGShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
p_gizmo->clear();
CSGShape3D *cs = Object::cast_to<CSGShape3D>(p_gizmo->get_node_3d());
Vector<Vector3> faces = cs->get_brush_faces();
if (faces.is_empty()) {
return;
}
Vector<Vector3> lines;
lines.resize(faces.size() * 2);
{
const Vector3 *r = faces.ptr();
for (int i = 0; i < lines.size(); i += 6) {
int f = i / 6;
for (int j = 0; j < 3; j++) {
int j_n = (j + 1) % 3;
lines.write[i + j * 2 + 0] = r[f * 3 + j];
lines.write[i + j * 2 + 1] = r[f * 3 + j_n];
}
}
}
Ref<Material> material;
switch (cs->get_operation()) {
case CSGShape3D::OPERATION_UNION:
material = get_material("shape_union_material", p_gizmo);
break;
case CSGShape3D::OPERATION_INTERSECTION:
material = get_material("shape_intersection_material", p_gizmo);
break;
case CSGShape3D::OPERATION_SUBTRACTION:
material = get_material("shape_subtraction_material", p_gizmo);
break;
}
Ref<Material> handles_material = get_material("handles");
p_gizmo->add_lines(lines, material);
p_gizmo->add_collision_segments(lines);
if (cs->is_root_shape()) {
Array csg_meshes = cs->get_meshes();
if (csg_meshes.size() == 2) {
Ref<Mesh> csg_mesh = csg_meshes[1];
if (csg_mesh.is_valid()) {
p_gizmo->add_collision_triangles(csg_mesh->generate_triangle_mesh());
}
}
}
if (p_gizmo->is_selected()) {
// Draw a translucent representation of the CSG node
Ref<ArrayMesh> mesh = memnew(ArrayMesh);
Array array;
array.resize(Mesh::ARRAY_MAX);
array[Mesh::ARRAY_VERTEX] = faces;
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, array);
Ref<Material> solid_material;
switch (cs->get_operation()) {
case CSGShape3D::OPERATION_UNION:
solid_material = get_material("shape_union_solid_material", p_gizmo);
break;
case CSGShape3D::OPERATION_INTERSECTION:
solid_material = get_material("shape_intersection_solid_material", p_gizmo);
break;
case CSGShape3D::OPERATION_SUBTRACTION:
solid_material = get_material("shape_subtraction_solid_material", p_gizmo);
break;
}
p_gizmo->add_mesh(mesh, solid_material);
}
if (Object::cast_to<CSGSphere3D>(cs)) {
CSGSphere3D *s = Object::cast_to<CSGSphere3D>(cs);
float r = s->get_radius();
Vector<Vector3> handles;
handles.push_back(Vector3(r, 0, 0));
p_gizmo->add_handles(handles, handles_material);
}
if (Object::cast_to<CSGBox3D>(cs)) {
CSGBox3D *s = Object::cast_to<CSGBox3D>(cs);
Vector<Vector3> handles = helper->box_get_handles(s->get_size());
p_gizmo->add_handles(handles, handles_material);
}
if (Object::cast_to<CSGCylinder3D>(cs)) {
CSGCylinder3D *s = Object::cast_to<CSGCylinder3D>(cs);
Vector<Vector3> handles = helper->cylinder_get_handles(s->get_height(), s->get_radius());
p_gizmo->add_handles(handles, handles_material);
}
if (Object::cast_to<CSGTorus3D>(cs)) {
CSGTorus3D *s = Object::cast_to<CSGTorus3D>(cs);
Vector<Vector3> handles;
handles.push_back(Vector3(s->get_inner_radius(), 0, 0));
handles.push_back(Vector3(s->get_outer_radius(), 0, 0));
p_gizmo->add_handles(handles, handles_material);
}
}
void EditorPluginCSG::edit(Object *p_object) {
CSGShape3D *csg_shape = Object::cast_to<CSGShape3D>(p_object);
if (csg_shape && csg_shape->is_root_shape()) {
csg_shape_editor->edit(csg_shape);
} else {
csg_shape_editor->edit(nullptr);
}
}
bool EditorPluginCSG::handles(Object *p_object) const {
CSGShape3D *csg_shape = Object::cast_to<CSGShape3D>(p_object);
return csg_shape && csg_shape->is_root_shape();
}
EditorPluginCSG::EditorPluginCSG() {
Ref<CSGShape3DGizmoPlugin> gizmo_plugin = Ref<CSGShape3DGizmoPlugin>(memnew(CSGShape3DGizmoPlugin));
Node3DEditor::get_singleton()->add_gizmo_plugin(gizmo_plugin);
csg_shape_editor = memnew(CSGShapeEditor);
EditorNode::get_singleton()->get_gui_base()->add_child(csg_shape_editor);
}

View File

@@ -0,0 +1,102 @@
/**************************************************************************/
/* csg_gizmos.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "../csg_shape.h"
#include "editor/plugins/editor_plugin.h"
#include "editor/scene/3d/node_3d_editor_gizmos.h"
#include "scene/gui/control.h"
class AcceptDialog;
class Gizmo3DHelper;
class MenuButton;
class CSGShape3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(CSGShape3DGizmoPlugin, EditorNode3DGizmoPlugin);
Ref<Gizmo3DHelper> helper;
public:
virtual bool has_gizmo(Node3D *p_spatial) override;
virtual String get_gizmo_name() const override;
virtual int get_priority() const override;
virtual bool is_selectable_when_hidden() const override;
virtual void redraw(EditorNode3DGizmo *p_gizmo) override;
virtual String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override;
virtual Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override;
void begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) override;
virtual void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) override;
virtual void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) override;
CSGShape3DGizmoPlugin();
};
class CSGShapeEditor : public Control {
GDCLASS(CSGShapeEditor, Control);
enum Menu {
MENU_OPTION_BAKE_MESH_INSTANCE,
MENU_OPTION_BAKE_COLLISION_SHAPE,
};
CSGShape3D *node = nullptr;
MenuButton *options = nullptr;
AcceptDialog *err_dialog = nullptr;
void _menu_option(int p_option);
void _create_baked_mesh_instance();
void _create_baked_collision_shape();
protected:
void _node_removed(Node *p_node);
void _notification(int p_what);
public:
void edit(CSGShape3D *p_csg_shape);
CSGShapeEditor();
};
class EditorPluginCSG : public EditorPlugin {
GDCLASS(EditorPluginCSG, EditorPlugin);
CSGShapeEditor *csg_shape_editor = nullptr;
public:
virtual String get_plugin_name() const override { return "CSGShape3D"; }
virtual void edit(Object *p_object) override;
virtual bool handles(Object *p_object) const override;
EditorPluginCSG();
};

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><mask id="a"><path fill="#fefefe" d="M0 0h16v10a2 2 0 0 0-2-2h-2a2 2 0 0 0-2 2 2 2 0 0 0-2 2v2a2 2 0 0 0 2 2H0z"/></mask><path fill="#5fb2ff" d="M12 9a1 1 0 0 0-1 1v1h2v2h1a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1zm1 4h-2v-2h-1a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1z"/><path fill="none" stroke="#fc7f7f" stroke-width="2" d="m8 2 6 3v6l-6 3-6-3V5zm0 12V8l6-3M8 8 2 5" mask="url(#a)"/></svg>

After

Width:  |  Height:  |  Size: 451 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><mask id="a"><path fill="#fefefe" d="M0 0h16v10a2 2 0 0 0-2-2h-2a2 2 0 0 0-2 2 2 2 0 0 0-2 2v2a2 2 0 0 0 2 2H0z"/></mask><path fill="#5fb2ff" d="M12 9a1 1 0 0 0-1 1v1h2v2h1a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1zm1 4h-2v-2h-1a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1z"/><path fill="none" stroke="#fc7f7f" stroke-width="2" d="M4 6a4 4 0 0 1 8 0v4a4 4 0 0 1-8 0zm0 1.25a2.5 1 0 0 0 8 0m-4-5v12" mask="url(#a)"/></svg>

After

Width:  |  Height:  |  Size: 476 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#5fb2ff" d="M12 9a1 1 0 0 0-1 1v1h2v2h1a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1zm1 4h-2v-2h-1a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1z"/><path fill="#fc7f7f" d="M3 1a2 2 0 0 0-2 2h2zm2 0v2h2V1zm4 0v2h2V1zm4 0v2h2a2 2 0 0 0-2-2zM1 5v2h2V5zm12 0v2h2V5zM1 9v2h2V9zm0 4a2 2 0 0 0 2 2v-2zm4 0v2h2v-2z"/></svg>

After

Width:  |  Height:  |  Size: 377 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><mask id="a"><path fill="#fefefe" d="M0 0h16v10a2 2 0 0 0-2-2h-2a2 2 0 0 0-2 2 2 2 0 0 0-2 2v2a2 2 0 0 0 2 2H0z"/></mask><path fill="#5fb2ff" d="M12 9a1 1 0 0 0-1 1v1h2v2h1a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1zm1 4h-2v-2h-1a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1z"/><path fill="none" stroke="#fc7f7f" stroke-width="2" d="M2 4v8a6 2 0 0 0 12 0V4A6 2 0 0 0 2 4a6 2 0 0 0 12 0" mask="url(#a)"/></svg>

After

Width:  |  Height:  |  Size: 462 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#fc7f7f" d="M4.73 2A2 2 0 1 0 2 4.73v6.541A2 2 0 1 0 4.729 14H8v-2H4.729A2 2 0 0 0 4 11.271V5.415l4.914 4.916A2 2 0 0 1 9.998 10a2 2 0 0 1 .33-1.084L5.414 4h5.856a2 2 0 0 0 .73.729V8h2V4.729A2 2 0 1 0 11.27 2z"/><path fill="#5fb2ff" d="M12 9a1 1 0 0 0-1 1v1h2v2h1a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1zm1 4h-2v-2h-1a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1z"/></svg>

After

Width:  |  Height:  |  Size: 440 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><mask id="a"><path fill="#fefefe" d="M0 0h16v10a2 2 0 0 0-2-2h-2a2 2 0 0 0-2 2 2 2 0 0 0-2 2v2a2 2 0 0 0 2 2H0z"/></mask><path fill="#5fb2ff" d="M12 9a1 1 0 0 0-1 1v1h2v2h1a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1zm1 4h-2v-2h-1a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1z"/><path fill="none" stroke="#fc7f7f" stroke-linejoin="round" stroke-width="2" d="m8 2 6 3.5v5L8 14l-6-3.5v-5h6zm6 3.5L8 9 2 5.5M8 9v5" mask="url(#a)"/></svg>

After

Width:  |  Height:  |  Size: 486 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><mask id="a"><path fill="#fefefe" d="M0 0h16v10a2 2 0 0 0-2-2h-2a2 2 0 0 0-2 2 2 2 0 0 0-2 2v2a2 2 0 0 0 2 2H0z"/></mask><path fill="#5fb2ff" d="M12 9a1 1 0 0 0-1 1v1h2v2h1a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1zm1 4h-2v-2h-1a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1z"/><path fill="none" stroke="#fc7f7f" stroke-width="2" d="M8 2a6 6 0 0 0 0 12A6 6 0 0 0 8 2v12M2.05 7.4a6 2 0 0 0 11.9 0" mask="url(#a)"/></svg>

After

Width:  |  Height:  |  Size: 472 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><mask id="a"><path fill="#fefefe" d="M0 0h16v10a2 2 0 0 0-2-2h-2a2 2 0 0 0-2 2 2 2 0 0 0-2 2v2a2 2 0 0 0 2 2H0z"/></mask><path fill="#5fb2ff" d="M12 9a1 1 0 0 0-1 1v1h2v2h1a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1zm1 4h-2v-2h-1a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1z"/><g fill="none" stroke="#fc7f7f" mask="url(#a)"><path stroke-width="2" d="M2.5 10a6 4 0 0 0 11 0 4 4 0 0 0 0-4 6 4 0 0 0-11 0 4 4 0 0 0 0 4z"/><path stroke-linecap="round" stroke-width="1.75" d="M6.2 7.2a2 1 0 1 0 3.6 0"/></g></svg>

After

Width:  |  Height:  |  Size: 562 B

View File

@@ -0,0 +1,65 @@
/**************************************************************************/
/* register_types.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "register_types.h"
#include "csg_shape.h"
#ifdef TOOLS_ENABLED
#include "editor/csg_gizmos.h"
#endif
void initialize_csg_module(ModuleInitializationLevel p_level) {
if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) {
GDREGISTER_ABSTRACT_CLASS(CSGShape3D);
GDREGISTER_ABSTRACT_CLASS(CSGPrimitive3D);
GDREGISTER_CLASS(CSGMesh3D);
GDREGISTER_CLASS(CSGSphere3D);
GDREGISTER_CLASS(CSGBox3D);
GDREGISTER_CLASS(CSGCylinder3D);
GDREGISTER_CLASS(CSGTorus3D);
GDREGISTER_CLASS(CSGPolygon3D);
GDREGISTER_CLASS(CSGCombiner3D);
#ifndef NAVIGATION_3D_DISABLED
CSGShape3D::navmesh_parse_init();
#endif // NAVIGATION_3D_DISABLED
}
#ifdef TOOLS_ENABLED
if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) {
EditorPlugins::add_by_type<EditorPluginCSG>();
}
#endif
}
void uninitialize_csg_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
}

View File

@@ -0,0 +1,36 @@
/**************************************************************************/
/* register_types.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "modules/register_module_types.h"
void initialize_csg_module(ModuleInitializationLevel p_level);
void uninitialize_csg_module(ModuleInitializationLevel p_level);

View File

@@ -0,0 +1,111 @@
/**************************************************************************/
/* test_csg.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "../csg.h"
#include "../csg_shape.h"
#include "tests/test_macros.h"
namespace TestCSG {
TEST_CASE("[SceneTree][CSG] CSGPolygon3D") {
SUBCASE("[SceneTree][CSG] CSGPolygon3D: using accurate path tangent for polygon rotation") {
const float polygon_radius = 10.0f;
const Vector3 expected_min_bounds = Vector3(-polygon_radius, -polygon_radius, 0);
const Vector3 expected_max_bounds = Vector3(100 + polygon_radius, polygon_radius, 100);
const AABB expected_aabb = AABB(expected_min_bounds, expected_max_bounds - expected_min_bounds);
Ref<Curve3D> curve;
curve.instantiate();
curve->add_point(
// p_position
Vector3(0, 0, 0),
// p_in
Vector3(),
// p_out
Vector3(0, 0, 60));
curve->add_point(
// p_position
Vector3(100, 0, 100),
// p_in
Vector3(0, 0, -60),
// p_out
Vector3());
Path3D *path = memnew(Path3D);
path->set_curve(curve);
CSGPolygon3D *csg_polygon_3d = memnew(CSGPolygon3D);
SceneTree::get_singleton()->get_root()->add_child(csg_polygon_3d);
csg_polygon_3d->add_child(path);
csg_polygon_3d->set_path_node(csg_polygon_3d->get_path_to(path));
csg_polygon_3d->set_mode(CSGPolygon3D::Mode::MODE_PATH);
PackedVector2Array polygon;
polygon.append(Vector2(-polygon_radius, 0));
polygon.append(Vector2(0, polygon_radius));
polygon.append(Vector2(polygon_radius, 0));
polygon.append(Vector2(0, -polygon_radius));
csg_polygon_3d->set_polygon(polygon);
csg_polygon_3d->set_path_rotation(CSGPolygon3D::PathRotation::PATH_ROTATION_PATH);
csg_polygon_3d->set_path_rotation_accurate(true);
// Minimize the number of extrusions.
// This decreases the number of samples taken from the curve.
// Having fewer samples increases the inaccuracy of the line between samples as an approximation of the tangent of the curve.
// With correct polygon orientation, the bounding box for the given curve should be independent of the number of extrusions.
csg_polygon_3d->set_path_interval_type(CSGPolygon3D::PathIntervalType::PATH_INTERVAL_DISTANCE);
csg_polygon_3d->set_path_interval(1000.0f);
// Call get_brush_faces to force the bounding box to update.
csg_polygon_3d->get_brush_faces();
CHECK(csg_polygon_3d->get_aabb().is_equal_approx(expected_aabb));
// Perform the bounding box check again with a greater number of extrusions.
csg_polygon_3d->set_path_interval(1.0f);
csg_polygon_3d->get_brush_faces();
CHECK(csg_polygon_3d->get_aabb().is_equal_approx(expected_aabb));
csg_polygon_3d->remove_child(path);
SceneTree::get_singleton()->get_root()->remove_child(csg_polygon_3d);
memdelete(csg_polygon_3d);
memdelete(path);
}
}
} // namespace TestCSG