CSG Support for Godot!

-Missing Icons
-Missing freezing option (for baking light and faster load)
-Missing a way to export from Godot (GLTF2?)
-Probably buggy (may freeze editor, can be worked around easily, but let me know if this happens so it's easier to catch bugs)
Happy testing!
This commit is contained in:
Juan Linietsky 2018-04-27 21:52:15 -03:00
parent b22f048700
commit 8d199a9b2c
18 changed files with 4715 additions and 42 deletions

View file

@ -5858,7 +5858,7 @@ EditorNode::EditorNode() {
add_editor_plugin(memnew(ParticlesEditorPlugin(this)));
add_editor_plugin(memnew(ResourcePreloaderEditorPlugin(this)));
add_editor_plugin(memnew(ItemListEditorPlugin(this)));
add_editor_plugin(memnew(CollisionPolygonEditorPlugin(this)));
add_editor_plugin(memnew(Polygon3DEditorPlugin(this)));
add_editor_plugin(memnew(CollisionPolygon2DEditorPlugin(this)));
add_editor_plugin(memnew(TileSetEditorPlugin(this)));
add_editor_plugin(memnew(TileMapEditorPlugin(this)));

View file

@ -36,7 +36,7 @@
#include "scene/3d/camera.h"
#include "spatial_editor_plugin.h"
void CollisionPolygonEditor::_notification(int p_what) {
void Polygon3DEditor::_notification(int p_what) {
switch (p_what) {
@ -53,15 +53,15 @@ void CollisionPolygonEditor::_notification(int p_what) {
return;
}
if (node->get_depth() != prev_depth) {
if (_get_depth() != prev_depth) {
_polygon_draw();
prev_depth = node->get_depth();
prev_depth = _get_depth();
}
} break;
}
}
void CollisionPolygonEditor::_node_removed(Node *p_node) {
void Polygon3DEditor::_node_removed(Node *p_node) {
if (p_node == node) {
node = NULL;
@ -72,7 +72,7 @@ void CollisionPolygonEditor::_node_removed(Node *p_node) {
}
}
void CollisionPolygonEditor::_menu_option(int p_option) {
void Polygon3DEditor::_menu_option(int p_option) {
switch (p_option) {
@ -91,10 +91,10 @@ void CollisionPolygonEditor::_menu_option(int p_option) {
}
}
void CollisionPolygonEditor::_wip_close() {
void Polygon3DEditor::_wip_close() {
undo_redo->create_action(TTR("Create Poly3D"));
undo_redo->add_undo_method(node, "set_polygon", node->get_polygon());
undo_redo->add_undo_method(node, "set_polygon", node->call("get_polygon"));
undo_redo->add_do_method(node, "set_polygon", wip);
undo_redo->add_do_method(this, "_polygon_draw");
undo_redo->add_undo_method(this, "_polygon_draw");
@ -107,14 +107,14 @@ void CollisionPolygonEditor::_wip_close() {
undo_redo->commit_action();
}
bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event) {
bool Polygon3DEditor::forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event) {
if (!node)
return false;
Transform gt = node->get_global_transform();
Transform gi = gt.affine_inverse();
float depth = node->get_depth() * 0.5;
float depth = _get_depth() * 0.5;
Vector3 n = gt.basis.get_axis(2).normalized();
Plane p(gt.origin + n * depth, n);
@ -137,7 +137,7 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R
cpoint = CanvasItemEditor::get_singleton()->snap_point(cpoint);
Vector<Vector2> poly = node->get_polygon();
Vector<Vector2> poly = node->call("get_polygon");
//first check if a point is to be added (segment split)
real_t grab_threshold = EDITOR_DEF("editors/poly_editor/point_grab_radius", 8);
@ -226,7 +226,7 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R
poly.insert(closest_idx + 1, cpoint);
edited_point = closest_idx + 1;
edited_point_pos = cpoint;
node->set_polygon(poly);
node->call("set_polygon", poly);
_polygon_draw();
return true;
}
@ -341,7 +341,16 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R
return false;
}
void CollisionPolygonEditor::_polygon_draw() {
float Polygon3DEditor::_get_depth() {
if (bool(node->call("_has_editable_3d_polygon_no_depth")))
return 0;
return float(node->call("get_depth"));
}
void Polygon3DEditor::_polygon_draw() {
if (!node)
return;
@ -351,9 +360,9 @@ void CollisionPolygonEditor::_polygon_draw() {
if (wip_active)
poly = wip;
else
poly = node->get_polygon();
poly = node->call("get_polygon");
float depth = node->get_depth() * 0.5;
float depth = _get_depth() * 0.5;
imgeom->clear();
imgeom->set_material_override(line_material);
@ -464,13 +473,13 @@ void CollisionPolygonEditor::_polygon_draw() {
m->surface_set_material(0, handle_material);
}
void CollisionPolygonEditor::edit(Node *p_collision_polygon) {
void Polygon3DEditor::edit(Node *p_collision_polygon) {
if (p_collision_polygon) {
node = Object::cast_to<CollisionPolygon>(p_collision_polygon);
node = Object::cast_to<Spatial>(p_collision_polygon);
//Enable the pencil tool if the polygon is empty
if (node->get_polygon().size() == 0) {
if (Vector<Vector2>(node->call("get_polygon")).size() == 0) {
_menu_option(MODE_CREATE);
}
wip.clear();
@ -491,14 +500,14 @@ void CollisionPolygonEditor::edit(Node *p_collision_polygon) {
}
}
void CollisionPolygonEditor::_bind_methods() {
void Polygon3DEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_menu_option"), &CollisionPolygonEditor::_menu_option);
ClassDB::bind_method(D_METHOD("_polygon_draw"), &CollisionPolygonEditor::_polygon_draw);
ClassDB::bind_method(D_METHOD("_node_removed"), &CollisionPolygonEditor::_node_removed);
ClassDB::bind_method(D_METHOD("_menu_option"), &Polygon3DEditor::_menu_option);
ClassDB::bind_method(D_METHOD("_polygon_draw"), &Polygon3DEditor::_polygon_draw);
ClassDB::bind_method(D_METHOD("_node_removed"), &Polygon3DEditor::_node_removed);
}
CollisionPolygonEditor::CollisionPolygonEditor(EditorNode *p_editor) {
Polygon3DEditor::Polygon3DEditor(EditorNode *p_editor) {
node = NULL;
editor = p_editor;
@ -545,22 +554,22 @@ CollisionPolygonEditor::CollisionPolygonEditor(EditorNode *p_editor) {
pointsm->set_transform(Transform(Basis(), Vector3(0, 0, 0.00001)));
}
CollisionPolygonEditor::~CollisionPolygonEditor() {
Polygon3DEditor::~Polygon3DEditor() {
memdelete(imgeom);
}
void CollisionPolygonEditorPlugin::edit(Object *p_object) {
void Polygon3DEditorPlugin::edit(Object *p_object) {
collision_polygon_editor->edit(Object::cast_to<Node>(p_object));
}
bool CollisionPolygonEditorPlugin::handles(Object *p_object) const {
bool Polygon3DEditorPlugin::handles(Object *p_object) const {
return p_object->is_class("CollisionPolygon");
return Object::cast_to<Spatial>(p_object) && bool(p_object->call("_is_editable_3d_polygon"));
}
void CollisionPolygonEditorPlugin::make_visible(bool p_visible) {
void Polygon3DEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
collision_polygon_editor->show();
@ -571,14 +580,14 @@ void CollisionPolygonEditorPlugin::make_visible(bool p_visible) {
}
}
CollisionPolygonEditorPlugin::CollisionPolygonEditorPlugin(EditorNode *p_node) {
Polygon3DEditorPlugin::Polygon3DEditorPlugin(EditorNode *p_node) {
editor = p_node;
collision_polygon_editor = memnew(CollisionPolygonEditor(p_node));
collision_polygon_editor = memnew(Polygon3DEditor(p_node));
SpatialEditor::get_singleton()->add_control_to_menu_panel(collision_polygon_editor);
collision_polygon_editor->hide();
}
CollisionPolygonEditorPlugin::~CollisionPolygonEditorPlugin() {
Polygon3DEditorPlugin::~Polygon3DEditorPlugin() {
}

View file

@ -44,9 +44,9 @@
class CanvasItemEditor;
class CollisionPolygonEditor : public HBoxContainer {
class Polygon3DEditor : public HBoxContainer {
GDCLASS(CollisionPolygonEditor, HBoxContainer);
GDCLASS(Polygon3DEditor, HBoxContainer);
UndoRedo *undo_redo;
enum Mode {
@ -66,7 +66,7 @@ class CollisionPolygonEditor : public HBoxContainer {
EditorNode *editor;
Panel *panel;
CollisionPolygon *node;
Spatial *node;
ImmediateGeometry *imgeom;
MeshInstance *pointsm;
Ref<ArrayMesh> m;
@ -85,6 +85,8 @@ class CollisionPolygonEditor : public HBoxContainer {
void _polygon_draw();
void _menu_option(int p_option);
float _get_depth();
protected:
void _notification(int p_what);
void _node_removed(Node *p_node);
@ -93,28 +95,28 @@ protected:
public:
virtual bool forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event);
void edit(Node *p_collision_polygon);
CollisionPolygonEditor(EditorNode *p_editor);
~CollisionPolygonEditor();
Polygon3DEditor(EditorNode *p_editor);
~Polygon3DEditor();
};
class CollisionPolygonEditorPlugin : public EditorPlugin {
class Polygon3DEditorPlugin : public EditorPlugin {
GDCLASS(CollisionPolygonEditorPlugin, EditorPlugin);
GDCLASS(Polygon3DEditorPlugin, EditorPlugin);
CollisionPolygonEditor *collision_polygon_editor;
Polygon3DEditor *collision_polygon_editor;
EditorNode *editor;
public:
virtual bool forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event) { return collision_polygon_editor->forward_spatial_gui_input(p_camera, p_event); }
virtual String get_name() const { return "CollisionPolygon"; }
virtual String get_name() const { return "Polygon3DEditor"; }
bool has_main_screen() const { return false; }
virtual void edit(Object *p_object);
virtual bool handles(Object *p_object) const;
virtual void make_visible(bool p_visible);
CollisionPolygonEditorPlugin(EditorNode *p_node);
~CollisionPolygonEditorPlugin();
Polygon3DEditorPlugin(EditorNode *p_node);
~Polygon3DEditorPlugin();
};
#endif // COLLISION_POLYGON_EDITOR_PLUGIN_H

9
modules/csg/SCsub Normal file
View file

@ -0,0 +1,9 @@
#!/usr/bin/env python
Import('env')
Import('env_modules')
env_csg = env_modules.Clone()
# Godot's own source files
env_csg.add_source_files(env.modules_sources, "*.cpp")

5
modules/csg/config.py Normal file
View file

@ -0,0 +1,5 @@
def can_build(platform):
return True
def configure(env):
pass

1472
modules/csg/csg.cpp Normal file

File diff suppressed because it is too large Load diff

206
modules/csg/csg.h Normal file
View file

@ -0,0 +1,206 @@
#ifndef CSG_H
#define CSG_H
#include "aabb.h"
#include "dvector.h"
#include "map.h"
#include "math_2d.h"
#include "oa_hash_map.h"
#include "plane.h"
#include "scene/resources/material.h"
#include "transform.h"
#include "vector3.h"
struct CSGBrush {
struct Face {
Vector3 vertices[3];
Vector2 uvs[3];
AABB aabb;
bool smooth;
bool invert;
int material;
};
Vector<Face> faces;
Vector<Ref<Material> > materials;
void _regen_face_aabbs();
//create a brush from faces
void build_from_faces(const PoolVector<Vector3> &p_vertices, const PoolVector<Vector2> &p_uvs, const PoolVector<bool> &p_smooth, const PoolVector<Ref<Material> > &p_materials, const PoolVector<bool> &p_invert_faces);
void copy_from(const CSGBrush &p_brush, const Transform &p_xform);
void clear();
};
struct CSGBrushOperation {
enum Operation {
OPERATION_UNION,
OPERATION_INTERSECTION,
OPERATION_SUBSTRACTION,
};
struct MeshMerge {
struct BVH {
int face;
int left;
int right;
int next;
Vector3 center;
AABB aabb;
};
struct BVHCmpX {
bool operator()(const BVH *p_left, const BVH *p_right) const {
return p_left->center.x < p_right->center.x;
}
};
struct BVHCmpY {
bool operator()(const BVH *p_left, const BVH *p_right) const {
return p_left->center.y < p_right->center.y;
}
};
struct BVHCmpZ {
bool operator()(const BVH *p_left, const BVH *p_right) const {
return p_left->center.z < p_right->center.z;
}
};
int _bvh_count_intersections(BVH *bvhptr, int p_max_depth, int p_bvh_first, const Vector3 &p_begin, const Vector3 &p_end, int p_exclude) const;
int _create_bvh(BVH *p_bvh, BVH **p_bb, int p_from, int p_size, int p_depth, int &max_depth, int &max_alloc);
struct VertexKey {
int32_t x, y, z;
_FORCE_INLINE_ bool operator<(const VertexKey &p_key) const {
if (x == p_key.x) {
if (y == p_key.y) {
return z < p_key.z;
} else {
return y < p_key.y;
}
} else {
return x < p_key.x;
}
}
_FORCE_INLINE_ bool operator==(const VertexKey &p_key) const {
return (x == p_key.x && y == p_key.y && z == p_key.z);
}
};
struct VertexKeyHash {
static _FORCE_INLINE_ uint32_t hash(const VertexKey &p_vk) {
uint32_t h = hash_djb2_one_32(p_vk.x);
h = hash_djb2_one_32(p_vk.y, h);
h = hash_djb2_one_32(p_vk.z, h);
return h;
}
};
OAHashMap<VertexKey, int, 64, VertexKeyHash> snap_cache;
Vector<Vector3> points;
struct Face {
bool from_b;
bool inside;
int points[3];
Vector2 uvs[3];
bool smooth;
bool invert;
int material_idx;
};
Vector<Face> faces;
Map<Ref<Material>, int> materials;
Map<Vector3, int> vertex_map;
void add_face(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, const Vector2 &p_uv_a, const Vector2 &p_uv_b, const Vector2 &p_uv_c, bool p_smooth, bool p_invert, const Ref<Material> &p_material, bool p_from_b);
// void add_face(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, bool p_from_b);
float vertex_snap;
void mark_inside_faces();
};
struct BuildPoly {
Plane plane;
Transform to_poly;
Transform to_world;
int face_index;
struct Point {
Vector2 point;
Vector2 uv;
};
Vector<Point> points;
struct Edge {
bool outer;
int points[2];
Edge() {
outer = false;
}
};
Vector<Edge> edges;
Ref<Material> material;
bool smooth;
bool invert;
int base_edges; //edges from original triangle, even if split
void _clip_segment(const CSGBrush *p_brush, int p_face, const Vector2 *segment, MeshMerge &mesh_merge, bool p_for_B);
void create(const CSGBrush *p_brush, int p_face, MeshMerge &mesh_merge, bool p_for_B);
void clip(const CSGBrush *p_brush, int p_face, MeshMerge &mesh_merge, bool p_for_B);
};
struct PolyPoints {
Vector<int> points;
Vector<Vector<int> > holes;
};
struct EdgeSort {
int edge;
int prev_point;
int edge_point;
float angle;
bool operator<(const EdgeSort &p_edge) const { return angle < p_edge.angle; }
};
struct CallbackData {
const CSGBrush *A;
const CSGBrush *B;
int face_a;
CSGBrushOperation *self;
Map<int, BuildPoly> build_polys_A;
Map<int, BuildPoly> build_polys_B;
};
void _add_poly_points(const BuildPoly &p_poly, int p_edge, int p_from_point, int p_to_point, const Vector<Vector<int> > &vertex_process, Vector<bool> &edge_process, Vector<PolyPoints> &r_poly);
void _add_poly_outline(const BuildPoly &p_poly, int p_from_point, int p_to_point, const Vector<Vector<int> > &vertex_process, Vector<int> &r_outline);
void _merge_poly(MeshMerge &mesh, int p_face_idx, const BuildPoly &p_poly, bool p_from_b);
void _collision_callback(const CSGBrush *A, int p_face_a, Map<int, BuildPoly> &build_polys_a, const CSGBrush *B, int p_face_b, Map<int, BuildPoly> &build_polys_b, MeshMerge &mesh_merge);
static void _collision_callbacks(void *ud, int p_face_b);
void merge_brushes(Operation p_operation, const CSGBrush &p_A, const CSGBrush &p_B, CSGBrush &result, float p_snap = 0.001);
};
#endif // CSG_H

315
modules/csg/csg_gizmos.cpp Normal file
View file

@ -0,0 +1,315 @@
#include "csg_gizmos.h"
///////////
String CSGShapeSpatialGizmo::get_handle_name(int p_idx) const {
if (Object::cast_to<CSGSphere>(cs)) {
return "Radius";
}
if (Object::cast_to<CSGBox>(cs)) {
static const char *hname[3] = { "Width", "Height", "Depth" };
return hname[p_idx];
}
if (Object::cast_to<CSGCylinder>(cs)) {
return p_idx == 0 ? "Radius" : "Height";
}
if (Object::cast_to<CSGTorus>(cs)) {
return p_idx == 0 ? "InnerRadius" : "OuterRadius";
}
return "";
}
Variant CSGShapeSpatialGizmo::get_handle_value(int p_idx) const {
if (Object::cast_to<CSGSphere>(cs)) {
CSGSphere *s = Object::cast_to<CSGSphere>(cs);
return s->get_radius();
}
if (Object::cast_to<CSGBox>(cs)) {
CSGBox *s = Object::cast_to<CSGBox>(cs);
switch (p_idx) {
case 0: return s->get_width();
case 1: return s->get_height();
case 2: return s->get_depth();
}
}
if (Object::cast_to<CSGCylinder>(cs)) {
CSGCylinder *s = Object::cast_to<CSGCylinder>(cs);
return p_idx == 0 ? s->get_radius() : s->get_height();
}
if (Object::cast_to<CSGTorus>(cs)) {
CSGTorus *s = Object::cast_to<CSGTorus>(cs);
return p_idx == 0 ? s->get_inner_radius() : s->get_outer_radius();
}
return Variant();
}
void CSGShapeSpatialGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_point) {
Transform gt = cs->get_global_transform();
gt.orthonormalize();
Transform gi = gt.affine_inverse();
Vector3 ray_from = p_camera->project_ray_origin(p_point);
Vector3 ray_dir = p_camera->project_ray_normal(p_point);
Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 16384) };
if (Object::cast_to<CSGSphere>(cs)) {
CSGSphere *s = Object::cast_to<CSGSphere>(cs);
Vector3 ra, rb;
Geometry::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb);
float d = ra.x;
if (d < 0.001)
d = 0.001;
s->set_radius(d);
}
if (Object::cast_to<CSGBox>(cs)) {
CSGBox *s = Object::cast_to<CSGBox>(cs);
Vector3 axis;
axis[p_idx] = 1.0;
Vector3 ra, rb;
Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
float d = ra[p_idx];
if (d < 0.001)
d = 0.001;
switch (p_idx) {
case 0: s->set_width(d); break;
case 1: s->set_height(d); break;
case 2: s->set_depth(d); break;
}
}
if (Object::cast_to<CSGCylinder>(cs)) {
CSGCylinder *s = Object::cast_to<CSGCylinder>(cs);
Vector3 axis;
axis[p_idx == 0 ? 0 : 1] = 1.0;
Vector3 ra, rb;
Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
float d = axis.dot(ra);
if (d < 0.001)
d = 0.001;
if (p_idx == 0)
s->set_radius(d);
else if (p_idx == 1)
s->set_height(d * 2.0);
}
if (Object::cast_to<CSGTorus>(cs)) {
CSGTorus *s = Object::cast_to<CSGTorus>(cs);
Vector3 axis;
axis[0] = 1.0;
Vector3 ra, rb;
Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
float d = axis.dot(ra);
if (d < 0.001)
d = 0.001;
if (p_idx == 0)
s->set_inner_radius(d);
else if (p_idx == 1)
s->set_outer_radius(d);
}
}
void CSGShapeSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) {
if (Object::cast_to<CSGSphere>(cs)) {
CSGSphere *s = Object::cast_to<CSGSphere>(cs);
if (p_cancel) {
s->set_radius(p_restore);
return;
}
UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
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<CSGBox>(cs)) {
CSGBox *s = Object::cast_to<CSGBox>(cs);
if (p_cancel) {
switch (p_idx) {
case 0: s->set_width(p_restore); break;
case 1: s->set_height(p_restore); break;
case 2: s->set_depth(p_restore); break;
}
return;
}
UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
ur->create_action(TTR("Change Box Shape Extents"));
static const char *method[3] = { "set_width", "set_height", "set_depth" };
float current;
switch (p_idx) {
case 0: current = s->get_width(); break;
case 1: current = s->get_height(); break;
case 2: current = s->get_depth(); break;
}
ur->add_do_method(s, method[p_idx], current);
ur->add_undo_method(s, method[p_idx], p_restore);
ur->commit_action();
}
if (Object::cast_to<CSGCylinder>(cs)) {
CSGCylinder *s = Object::cast_to<CSGCylinder>(cs);
if (p_cancel) {
if (p_idx == 0)
s->set_radius(p_restore);
else
s->set_height(p_restore);
return;
}
UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
if (p_idx == 0) {
ur->create_action(TTR("Change Cylinder Radius"));
ur->add_do_method(s, "set_radius", s->get_radius());
ur->add_undo_method(s, "set_radius", p_restore);
} else {
ur->create_action(TTR("Change Cylinder Height"));
ur->add_do_method(s, "set_height", s->get_height());
ur->add_undo_method(s, "set_height", p_restore);
}
ur->commit_action();
}
if (Object::cast_to<CSGTorus>(cs)) {
CSGTorus *s = Object::cast_to<CSGTorus>(cs);
if (p_cancel) {
if (p_idx == 0)
s->set_inner_radius(p_restore);
else
s->set_outer_radius(p_restore);
return;
}
UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
if (p_idx == 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();
}
}
void CSGShapeSpatialGizmo::redraw() {
clear();
Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/csg");
Ref<Material> material = create_material("shape_material", gizmo_color);
PoolVector<Vector3> faces = cs->get_brush_faces();
Vector<Vector3> lines;
lines.resize(faces.size() * 2);
{
PoolVector<Vector3>::Read r = faces.read();
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[i + j * 2 + 0] = r[f * 3 + j];
lines[i + j * 2 + 1] = r[f * 3 + j_n];
}
}
}
add_lines(lines, material);
add_collision_segments(lines);
if (Object::cast_to<CSGSphere>(cs)) {
CSGSphere *s = Object::cast_to<CSGSphere>(cs);
float r = s->get_radius();
Vector<Vector3> handles;
handles.push_back(Vector3(r, 0, 0));
add_handles(handles);
}
if (Object::cast_to<CSGBox>(cs)) {
CSGBox *s = Object::cast_to<CSGBox>(cs);
Vector<Vector3> handles;
handles.push_back(Vector3(s->get_width(), 0, 0));
handles.push_back(Vector3(0, s->get_height(), 0));
handles.push_back(Vector3(0, 0, s->get_depth()));
add_handles(handles);
}
if (Object::cast_to<CSGCylinder>(cs)) {
CSGCylinder *s = Object::cast_to<CSGCylinder>(cs);
Vector<Vector3> handles;
handles.push_back(Vector3(s->get_radius(), 0, 0));
handles.push_back(Vector3(0, s->get_height() * 0.5, 0));
add_handles(handles);
}
if (Object::cast_to<CSGTorus>(cs)) {
CSGTorus *s = Object::cast_to<CSGTorus>(cs);
Vector<Vector3> handles;
handles.push_back(Vector3(s->get_inner_radius(), 0, 0));
handles.push_back(Vector3(s->get_outer_radius(), 0, 0));
add_handles(handles);
}
}
CSGShapeSpatialGizmo::CSGShapeSpatialGizmo(CSGShape *p_cs) {
cs = p_cs;
set_spatial_node(p_cs);
}
Ref<SpatialEditorGizmo> EditorPluginCSG::create_spatial_gizmo(Spatial *p_spatial) {
if (Object::cast_to<CSGSphere>(p_spatial) || Object::cast_to<CSGBox>(p_spatial) || Object::cast_to<CSGCylinder>(p_spatial) || Object::cast_to<CSGTorus>(p_spatial) || Object::cast_to<CSGMesh>(p_spatial) || Object::cast_to<CSGPolygon>(p_spatial)) {
Ref<CSGShapeSpatialGizmo> csg = memnew(CSGShapeSpatialGizmo(Object::cast_to<CSGShape>(p_spatial)));
return csg;
}
return Ref<SpatialEditorGizmo>();
}
EditorPluginCSG::EditorPluginCSG(EditorNode *p_editor) {
EDITOR_DEF("editors/3d_gizmos/gizmo_colors/csg", Color(0.2, 0.5, 1, 0.1));
}

30
modules/csg/csg_gizmos.h Normal file
View file

@ -0,0 +1,30 @@
#ifndef CSG_GIZMOS_H
#define CSG_GIZMOS_H
#include "csg_shape.h"
#include "editor/editor_plugin.h"
#include "editor/spatial_editor_gizmos.h"
class CSGShapeSpatialGizmo : public EditorSpatialGizmo {
GDCLASS(CSGShapeSpatialGizmo, EditorSpatialGizmo);
CSGShape *cs;
public:
virtual String get_handle_name(int p_idx) const;
virtual Variant get_handle_value(int p_idx) const;
virtual void set_handle(int p_idx, Camera *p_camera, const Point2 &p_point);
virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false);
void redraw();
CSGShapeSpatialGizmo(CSGShape *p_cs = NULL);
};
class EditorPluginCSG : public EditorPlugin {
GDCLASS(EditorPluginCSG, EditorPlugin)
public:
virtual Ref<SpatialEditorGizmo> create_spatial_gizmo(Spatial *p_spatial);
EditorPluginCSG(EditorNode *p_editor);
};
#endif // CSG_GIZMOS_H

2152
modules/csg/csg_shape.cpp Normal file

File diff suppressed because it is too large Load diff

363
modules/csg/csg_shape.h Normal file
View file

@ -0,0 +1,363 @@
#ifndef CSG_SHAPE_H
#define CSG_SHAPE_H
#define CSGJS_HEADER_ONLY
#include "csg.h"
#include "scene/3d/visual_instance.h"
#include "scene/resources/concave_polygon_shape.h"
class CSGShape : public VisualInstance {
GDCLASS(CSGShape, VisualInstance);
public:
enum Operation {
OPERATION_UNION,
OPERATION_INTERSECTION,
OPERATION_SUBTRACTION,
};
private:
Operation operation;
CSGShape *parent;
CSGBrush *brush;
AABB node_aabb;
bool dirty;
bool use_collision;
Ref<ConcavePolygonShape> root_collision_shape;
RID root_collision_instance;
Ref<ArrayMesh> 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 {
PoolVector<Vector3> vertices;
PoolVector<Vector3> normals;
PoolVector<Vector2> uvs;
Ref<Material> material;
int last_added;
PoolVector<Vector3>::Write verticesw;
PoolVector<Vector3>::Write normalsw;
PoolVector<Vector2>::Write uvsw;
};
void _update_shape();
protected:
void _notification(int p_what);
virtual CSGBrush *_build_brush(AABB *r_aabb) = 0;
void _make_dirty();
static void _bind_methods();
friend class CSGCombiner;
CSGBrush *_get_brush();
virtual void _validate_property(PropertyInfo &property) const;
public:
void set_operation(Operation p_operation);
Operation get_operation() const;
virtual PoolVector<Vector3> get_brush_faces();
virtual AABB get_aabb() const;
virtual PoolVector<Face3> get_faces(uint32_t p_usage_flags) const;
void set_use_collision(bool p_enable);
bool is_using_collision() const;
bool is_root_shape() const;
CSGShape();
~CSGShape();
};
VARIANT_ENUM_CAST(CSGShape::Operation)
class CSGCombiner : public CSGShape {
GDCLASS(CSGCombiner, CSGShape)
private:
float snap;
virtual CSGBrush *_build_brush(AABB *r_aabb);
protected:
static void _bind_methods();
public:
void set_snap(float p_snap);
float get_snap() const;
CSGCombiner();
};
class CSGPrimitive : public CSGShape {
GDCLASS(CSGPrimitive, CSGShape)
private:
bool invert_faces;
protected:
CSGBrush *_create_brush_from_arrays(const PoolVector<Vector3> &p_vertices, const PoolVector<Vector2> &p_uv, const PoolVector<bool> &p_smooth, const PoolVector<Ref<Material> > &p_materials);
static void _bind_methods();
public:
void set_invert_faces(bool p_invert);
bool is_inverting_faces();
CSGPrimitive();
};
class CSGMesh : public CSGPrimitive {
GDCLASS(CSGMesh, CSGPrimitive)
virtual CSGBrush *_build_brush(AABB *r_aabb);
Ref<Mesh> mesh;
void _mesh_changed();
protected:
static void _bind_methods();
public:
void set_mesh(const Ref<Mesh> &p_mesh);
Ref<Mesh> get_mesh();
};
class CSGSphere : public CSGPrimitive {
GDCLASS(CSGSphere, CSGPrimitive)
virtual CSGBrush *_build_brush(AABB *r_aabb);
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;
CSGSphere();
};
class CSGBox : public CSGPrimitive {
GDCLASS(CSGBox, CSGPrimitive)
virtual CSGBrush *_build_brush(AABB *r_aabb);
Ref<Material> material;
float width;
float height;
float depth;
protected:
static void _bind_methods();
public:
void set_width(const float p_width);
float get_width() const;
void set_height(const float p_height);
float get_height() const;
void set_depth(const float p_depth);
float get_depth() const;
void set_material(const Ref<Material> &p_material);
Ref<Material> get_material() const;
CSGBox();
};
class CSGCylinder : public CSGPrimitive {
GDCLASS(CSGCylinder, CSGPrimitive)
virtual CSGBrush *_build_brush(AABB *r_aabb);
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;
CSGCylinder();
};
class CSGTorus : public CSGPrimitive {
GDCLASS(CSGTorus, CSGPrimitive)
virtual CSGBrush *_build_brush(AABB *r_aabb);
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;
CSGTorus();
};
class CSGPolygon : public CSGPrimitive {
GDCLASS(CSGPolygon, CSGPrimitive)
public:
enum Mode {
MODE_DEPTH,
MODE_SPIN,
MODE_PATH
};
enum PathRotation {
PATH_ROTATION_POLYGON,
PATH_ROTATION_PATH,
PATH_ROTATION_PATH_FOLLOW,
};
private:
virtual CSGBrush *_build_brush(AABB *r_aabb);
Vector<Vector2> polygon;
Ref<Material> material;
Mode mode;
float depth;
float spin_degrees;
int spin_sides;
NodePath path_node;
float path_interval;
PathRotation path_rotation;
Node *path_cache;
bool smooth_faces;
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();
virtual void _validate_property(PropertyInfo &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_sides);
int get_spin_sides() const;
void set_path_node(const NodePath &p_path);
NodePath get_path_node() 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_smooth_faces(bool p_smooth_faces);
bool get_smooth_faces() const;
void set_material(const Ref<Material> &p_material);
Ref<Material> get_material() const;
CSGPolygon();
};
VARIANT_ENUM_CAST(CSGPolygon::Mode)
VARIANT_ENUM_CAST(CSGPolygon::PathRotation)
#endif // CSG_SHAPE_H

View file

@ -0,0 +1,59 @@
/*************************************************************************/
/* register_types.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2018 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 "register_types.h"
#include "csg_shape.h"
#include "csg_gizmos.h"
void register_csg_types() {
#ifndef _3D_DISABLED
ClassDB::register_virtual_class<CSGShape>();
ClassDB::register_virtual_class<CSGPrimitive>();
ClassDB::register_class<CSGMesh>();
ClassDB::register_class<CSGSphere>();
ClassDB::register_class<CSGBox>();
ClassDB::register_class<CSGCylinder>();
ClassDB::register_class<CSGTorus>();
ClassDB::register_class<CSGPolygon>();
ClassDB::register_class<CSGCombiner>();
#ifdef TOOLS_ENABLED
EditorPlugins::add_by_type<EditorPluginCSG>();
#endif
#endif
}
void unregister_csg_types() {
}

View file

@ -0,0 +1,32 @@
/*************************************************************************/
/* register_types.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2018 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. */
/*************************************************************************/
void register_csg_types();
void unregister_csg_types();

View file

@ -173,6 +173,9 @@ String CollisionPolygon::get_configuration_warning() const {
return String();
}
bool CollisionPolygon::_is_editable_3d_polygon() const {
return true;
}
void CollisionPolygon::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_depth", "depth"), &CollisionPolygon::set_depth);
@ -184,6 +187,8 @@ void CollisionPolygon::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_disabled", "disabled"), &CollisionPolygon::set_disabled);
ClassDB::bind_method(D_METHOD("is_disabled"), &CollisionPolygon::is_disabled);
ClassDB::bind_method(D_METHOD("_is_editable_3d_polygon"), &CollisionPolygon::_is_editable_3d_polygon);
ADD_PROPERTY(PropertyInfo(Variant::REAL, "depth"), "set_depth", "get_depth");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon");

View file

@ -53,6 +53,8 @@ protected:
void _update_in_shape_owner(bool p_xform_only = false);
bool _is_editable_3d_polygon() const;
protected:
void _notification(int p_what);
static void _bind_methods();

View file

@ -40,6 +40,9 @@ void Path::_curve_changed() {
if (is_inside_tree() && Engine::get_singleton()->is_editor_hint())
update_gizmo();
if (is_inside_tree()) {
emit_signal("curve_changed");
}
}
void Path::set_curve(const Ref<Curve3D> &p_curve) {
@ -68,6 +71,8 @@ void Path::_bind_methods() {
ClassDB::bind_method(D_METHOD("_curve_changed"), &Path::_curve_changed);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve3D"), "set_curve", "get_curve");
ADD_SIGNAL(MethodInfo("curve_changed"));
}
Path::Path() {

View file

@ -912,6 +912,7 @@ void ArrayMesh::surface_set_material(int p_idx, const Ref<Material> &p_material)
VisualServer::get_singleton()->mesh_surface_set_material(mesh, p_idx, p_material.is_null() ? RID() : p_material->get_rid());
_change_notify("material");
emit_changed();
}
void ArrayMesh::surface_set_name(int p_idx, const String &p_name) {
@ -919,6 +920,7 @@ void ArrayMesh::surface_set_name(int p_idx, const String &p_name) {
ERR_FAIL_INDEX(p_idx, surfaces.size());
surfaces[p_idx].name = p_name;
emit_changed();
}
String ArrayMesh::surface_get_name(int p_idx) const {
@ -931,6 +933,7 @@ void ArrayMesh::surface_update_region(int p_surface, int p_offset, const PoolVec
ERR_FAIL_INDEX(p_surface, surfaces.size());
VS::get_singleton()->mesh_surface_update_region(mesh, p_surface, p_offset, p_data);
emit_changed();
}
void ArrayMesh::surface_set_custom_aabb(int p_idx, const AABB &p_aabb) {
@ -938,6 +941,7 @@ void ArrayMesh::surface_set_custom_aabb(int p_idx, const AABB &p_aabb) {
ERR_FAIL_INDEX(p_idx, surfaces.size());
surfaces[p_idx].aabb = p_aabb;
// set custom aabb too?
emit_changed();
}
Ref<Material> ArrayMesh::surface_get_material(int p_idx) const {
@ -986,6 +990,7 @@ void ArrayMesh::set_custom_aabb(const AABB &p_custom) {
custom_aabb = p_custom;
VS::get_singleton()->mesh_set_custom_aabb(mesh, custom_aabb);
emit_changed();
}
AABB ArrayMesh::get_custom_aabb() const {

View file

@ -65,6 +65,8 @@ void PrimitiveMesh::_update() const {
pending_request = false;
_clear_triangle_mesh();
const_cast<PrimitiveMesh *>(this)->emit_changed();
}
void PrimitiveMesh::_request_update() {