From 23c87e78353f74b36e63d06459f91d351b4d1479 Mon Sep 17 00:00:00 2001 From: iiMidknightii <89699177+iiMidknightii@users.noreply.github.com> Date: Fri, 12 Dec 2025 21:27:16 -0800 Subject: [PATCH 1/5] Refactored macros into mixin templates for readability --- .clangd | 5 + addons/PathMesh3D/path_mesh_3d.gdextension | 2 +- addons/PathMesh3D/src/path_area_3d.cpp | 21 +- addons/PathMesh3D/src/path_area_3d.hpp | 19 +- addons/PathMesh3D/src/path_collision_tool.hpp | 624 ------------------ .../PathMesh3D/src/path_collision_tool_3d.hpp | 306 +++++++++ addons/PathMesh3D/src/path_extrude_3d.cpp | 50 +- addons/PathMesh3D/src/path_extrude_3d.hpp | 15 +- addons/PathMesh3D/src/path_mesh_3d.cpp | 54 +- addons/PathMesh3D/src/path_mesh_3d.hpp | 13 +- addons/PathMesh3D/src/path_modifier_3d.cpp | 2 +- addons/PathMesh3D/src/path_multimesh_3d.cpp | 24 +- addons/PathMesh3D/src/path_multimesh_3d.hpp | 8 +- .../PathMesh3D/src/path_physics_tool_3d.hpp | 362 ++++++++++ addons/PathMesh3D/src/path_scene_3d.cpp | 22 +- addons/PathMesh3D/src/path_scene_3d.hpp | 8 +- addons/PathMesh3D/src/path_static_body_3d.cpp | 21 +- addons/PathMesh3D/src/path_static_body_3d.hpp | 19 +- addons/PathMesh3D/src/path_tool.hpp | 199 ------ addons/PathMesh3D/src/path_tool_3d.hpp | 249 +++++++ doc_classes/PathArea3D.xml | 22 +- doc_classes/PathExtrude3D.xml | 15 +- doc_classes/PathMesh3D.xml | 15 +- doc_classes/PathMultiMesh3D.xml | 15 +- doc_classes/PathScene3D.xml | 15 +- doc_classes/PathStaticBody3D.xml | 22 +- 26 files changed, 1123 insertions(+), 1004 deletions(-) create mode 100644 .clangd delete mode 100644 addons/PathMesh3D/src/path_collision_tool.hpp create mode 100644 addons/PathMesh3D/src/path_collision_tool_3d.hpp create mode 100644 addons/PathMesh3D/src/path_physics_tool_3d.hpp delete mode 100644 addons/PathMesh3D/src/path_tool.hpp create mode 100644 addons/PathMesh3D/src/path_tool_3d.hpp diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..853c253 --- /dev/null +++ b/.clangd @@ -0,0 +1,5 @@ +CompileFlags: + Remove: + - "-fno-gnu-unique" + Add: + - "-std=c++17" diff --git a/addons/PathMesh3D/path_mesh_3d.gdextension b/addons/PathMesh3D/path_mesh_3d.gdextension index 846e94c..106dcf7 100644 --- a/addons/PathMesh3D/path_mesh_3d.gdextension +++ b/addons/PathMesh3D/path_mesh_3d.gdextension @@ -14,7 +14,7 @@ windows.debug.x86_32 = "res://addons/PathMesh3D/bin/path_mesh_3d.windows.templat windows.release.x86_32 = "res://addons/PathMesh3D/bin/path_mesh_3d.windows.template_release.x86_32.dll" windows.debug.x86_64 = "res://addons/PathMesh3D/bin/path_mesh_3d.windows.template_debug.x86_64.dll" windows.release.x86_64 = "res://addons/PathMesh3D/bin/path_mesh_3d.windows.template_release.x86_64.dll" -linux.debug.x86_64 = "res://addons/PathMesh3D/bin/libpath_mesh_3d.linux.template_debug.x86_64.so" +linux.debug.x86_64 = "res://addons/PathMesh3D/bin/libpath_mesh_3d.linux.template_debug.dev.x86_64.so" linux.release.x86_64 = "res://addons/PathMesh3D/bin/libpath_mesh_3d.linux.template_release.x86_64.so" linux.debug.arm64 = "res://addons/PathMesh3D/bin/libpath_mesh_3d.linux.template_debug.arm64.so" linux.release.arm64 = "res://addons/PathMesh3D/bin/libpath_mesh_3d.linux.template_release.arm64.so" diff --git a/addons/PathMesh3D/src/path_area_3d.cpp b/addons/PathMesh3D/src/path_area_3d.cpp index 6375927..e47912c 100644 --- a/addons/PathMesh3D/src/path_area_3d.cpp +++ b/addons/PathMesh3D/src/path_area_3d.cpp @@ -9,5 +9,24 @@ #include #include "path_area_3d.hpp" +#include "path_physics_tool_3d.hpp" +#include "path_tool_3d.hpp" -PATH_COLLISION_TOOL_IMPLEMENTATION(PathArea3D, Area3D, area) \ No newline at end of file +using namespace godot; + +void PathArea3D::_bind_methods() { + PathTool3D::_bind_path_tool_3d_methods(); + PathPhysicsTool3D::_bind_path_physics_tool_3d_methods(); +} + +void PathArea3D::_notification(int p_what) { + PathTool3D::_notification_path_tool_3d(p_what); +} + +void PathArea3D::_validate_property(PropertyInfo &p_property) const { + PathPhysicsTool3D::_validate_path_physics_tool_3d_property(p_property); +} + +void PathArea3D::_rebuild_mesh() { + PathPhysicsTool3D::_rebuild_shape(); +} \ No newline at end of file diff --git a/addons/PathMesh3D/src/path_area_3d.hpp b/addons/PathMesh3D/src/path_area_3d.hpp index 3917f47..805cdc0 100644 --- a/addons/PathMesh3D/src/path_area_3d.hpp +++ b/addons/PathMesh3D/src/path_area_3d.hpp @@ -2,17 +2,26 @@ #include -#include "path_tool.hpp" -#include "path_collision_tool.hpp" +#include "path_tool_3d.hpp" +#include "path_physics_tool_3d.hpp" namespace godot { class MultiMeshInstance3D; -class PathArea3D : public Area3D { +class PathArea3D : public Area3D, public PathPhysicsTool3D { GDCLASS(PathArea3D, Area3D) - PATH_TOOL(PathArea3D, SHAPE) - PATH_COLLISION_TOOL_HEADER(PathArea3D, Area3D, area) + +public: + PathArea3D *_bake_area() const { + return _bake(); + } + +protected: + static void _bind_methods(); + void _notification(int p_what); + void _validate_property(PropertyInfo &p_property) const; + virtual void _rebuild_mesh() override final; }; } // namespace godot diff --git a/addons/PathMesh3D/src/path_collision_tool.hpp b/addons/PathMesh3D/src/path_collision_tool.hpp deleted file mode 100644 index 4a3cc6d..0000000 --- a/addons/PathMesh3D/src/path_collision_tool.hpp +++ /dev/null @@ -1,624 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PATH_COLLISION_TOOL_HEADER(m_class, m_super, m_bake_name) \ - \ -public: \ - enum Distribution { \ - DISTRIBUTE_BY_COUNT, \ - DISTRIBUTE_BY_DISTANCE, \ - DISTRIBUTE_MAX, \ - }; \ - enum Rotation { \ - ROTATE_FIXED, \ - ROTATE_PATH, \ - ROTATE_MAX, \ - }; \ - enum Alignment { \ - ALIGN_FROM_START, \ - ALIGN_CENTERED, \ - ALIGN_FROM_END, \ - ALIGN_MAX, \ - }; \ - \ - ~m_class(); \ - \ - void set_shape(const Ref &p_shape); \ - Ref get_shape() const; \ - \ - void set_distribution(Distribution p_distribution); \ - Distribution get_distribution() const; \ - \ - void set_alignment(Alignment p_alignment); \ - Alignment get_alignment() const; \ - \ - void set_count(uint64_t p_count); \ - uint64_t get_count() const; \ - \ - void set_distance(double p_distance); \ - double get_distance() const; \ - \ - void set_rotation_mode(Rotation p_rotation_mode); \ - Rotation get_rotation_mode() const; \ - \ - void set_rotation(const Vector3 &p_rotation); \ - Vector3 get_rotation() const; \ - \ - void set_sample_cubic(bool p_cubic); \ - bool get_sample_cubic() const; \ - \ - m_super *bake_##m_bake_name(); \ - \ -protected: \ - static void _bind_methods(); \ - void _validate_property(PropertyInfo &p_property) const; \ - \ -private: \ - Ref shape; \ - Distribution distribution = DISTRIBUTE_BY_COUNT; \ - Alignment alignment = ALIGN_FROM_START; \ - uint64_t count = 1; \ - double distance = 1.0; \ - Rotation rotation_mode = ROTATE_FIXED; \ - Vector3 rotation = Vector3(); \ - bool sample_cubic = false; \ - \ - LocalVector owners; \ - MultiMeshInstance3D *collision_debug = nullptr; \ - \ - void _on_shape_changed(); \ - - -#define PATH_COLLISION_TOOL_IMPLEMENTATION(m_class, m_super, m_bake_name) \ -using namespace godot; \ -\ -m_class::~m_class() { \ - PATH_TOOL_DESTRUCTOR(m_class) \ - \ - if (shape.is_valid()) { \ - if (shape->is_connected("changed", callable_mp(this, &m_class::_on_shape_changed))) { \ - shape->disconnect("changed", callable_mp(this, &m_class::_on_shape_changed)); \ - } \ - shape.unref(); \ - } \ -} \ -\ -void m_class::set_shape(const Ref &p_shape) { \ - if (shape != p_shape) { \ - if (shape.is_valid()) { \ - if (shape->is_connected("changed", callable_mp(this, &m_class::_on_shape_changed))) { \ - shape->disconnect("changed", callable_mp(this, &m_class::_on_shape_changed)); \ - } \ - } \ - \ - shape = p_shape; \ - \ - if (shape.is_valid()) { \ - shape->connect("changed", callable_mp(this, &m_class::_on_shape_changed)); \ - } \ - \ - _on_shape_changed(); \ - } \ -} \ -\ -Ref m_class::get_shape() const { \ - return shape; \ -} \ -\ -void m_class::set_distribution(Distribution p_distribution) { \ - if (distribution != p_distribution) { \ - distribution = p_distribution; \ - queue_rebuild(); \ - notify_property_list_changed(); \ - } \ -} \ -\ -auto m_class::get_distribution() const -> Distribution { \ - return distribution; \ -} \ -\ -void m_class::set_alignment(Alignment p_alignment) { \ - if (alignment != p_alignment) { \ - alignment = p_alignment; \ - queue_rebuild(); \ - } \ -} \ -\ -auto m_class::get_alignment() const -> Alignment{ \ - return alignment; \ -} \ -\ -void m_class::set_count(uint64_t p_count) { \ - if (count != p_count) { \ - count = p_count; \ - queue_rebuild(); \ - if (distribution == DISTRIBUTE_BY_COUNT) { \ - queue_rebuild(); \ - } \ - } \ -} \ -\ -uint64_t m_class::get_count() const { \ - return count; \ -} \ -\ -void m_class::set_distance(double p_distance) { \ - if (distance != p_distance) { \ - distance = p_distance; \ - queue_rebuild(); \ - if (distribution == DISTRIBUTE_BY_DISTANCE) { \ - queue_rebuild(); \ - } \ - } \ -} \ -\ -double m_class::get_distance() const { \ - return distance; \ -} \ -\ -void m_class::set_rotation_mode(Rotation p_rotation_mode) { \ - if (rotation_mode != p_rotation_mode) { \ - rotation_mode = p_rotation_mode; \ - queue_rebuild(); \ - } \ -} \ -\ -auto m_class::get_rotation_mode() const -> Rotation { \ - return rotation_mode; \ -} \ -\ -void m_class::set_rotation(const Vector3 &p_rotation) { \ - if (rotation != p_rotation) { \ - rotation = p_rotation; \ - queue_rebuild(); \ - } \ -} \ -\ -Vector3 m_class::get_rotation() const { \ - return rotation; \ -} \ -\ -void m_class::set_sample_cubic(bool p_cubic) { \ - if (sample_cubic != p_cubic) { \ - sample_cubic = p_cubic; \ - queue_rebuild(); \ - } \ -} \ -\ -bool m_class::get_sample_cubic() const { \ - return sample_cubic; \ -} \ -\ -m_super *m_class::bake_##m_bake_name() { \ - m_super *out = memnew(m_super); \ - Ref new_shape = shape->duplicate(); \ - \ - for (const uint32_t owner_id : owners) { \ - CollisionShape3D *new_obj = memnew(CollisionShape3D); \ - new_obj->set_shape(new_shape); \ - out->add_child(new_obj); \ - new_obj->set_transform(shape_owner_get_transform(owner_id)); \ - if (out->get_owner() != nullptr) { \ - new_obj->set_owner(out->get_owner()); \ - } \ - } \ - \ - return out; \ -} \ -\ -void m_class::_bind_methods() { \ - PATH_TOOL_BINDS(m_class, shape, SHAPE) \ - \ - ClassDB::bind_method(D_METHOD("bake_" #m_bake_name), &m_class::bake_##m_bake_name); \ - \ - ClassDB::bind_method(D_METHOD("set_shape", "shape"), &m_class::set_shape); \ - ClassDB::bind_method(D_METHOD("get_shape"), &m_class::get_shape); \ - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape3D"), "set_shape", "get_shape"); \ - \ - ClassDB::bind_method(D_METHOD("set_distribution", "distribution"), &m_class::set_distribution); \ - ClassDB::bind_method(D_METHOD("get_distribution"), &m_class::get_distribution); \ - ADD_PROPERTY(PropertyInfo(Variant::INT, "distribution", PROPERTY_HINT_ENUM, "By Count,By Distance"), "set_distribution", "get_distribution"); \ - \ - ClassDB::bind_method(D_METHOD("set_alignment", "alignment"), &m_class::set_alignment); \ - ClassDB::bind_method(D_METHOD("get_alignment"), &m_class::get_alignment); \ - ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "From Start,Centered,From End"), "set_alignment", "get_alignment"); \ - \ - ClassDB::bind_method(D_METHOD("set_count", "count"), &m_class::set_count); \ - ClassDB::bind_method(D_METHOD("get_count"), &m_class::get_count); \ - ADD_PROPERTY(PropertyInfo(Variant::INT, "count", PROPERTY_HINT_RANGE, "0,1000,1,or_greater"), "set_count", "get_count"); \ - \ - ClassDB::bind_method(D_METHOD("set_distance", "distance"), &m_class::set_distance); \ - ClassDB::bind_method(D_METHOD("get_distance"), &m_class::get_distance); \ - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance", PROPERTY_HINT_RANGE, "0.01,1000.0,0.01,or_greater"), "set_distance", "get_distance"); \ - \ - ClassDB::bind_method(D_METHOD("set_rotation_mode", "rotation_mode"), &m_class::set_rotation_mode); \ - ClassDB::bind_method(D_METHOD("get_rotation_mode"), &m_class::get_rotation_mode); \ - ADD_PROPERTY(PropertyInfo(Variant::INT, "rotation_mode", PROPERTY_HINT_ENUM, "Fixed,Path"), "set_rotation_mode", "get_rotation_mode"); \ - \ - ClassDB::bind_method(D_METHOD("set_rotation", "rotation"), &m_class::set_rotation); \ - ClassDB::bind_method(D_METHOD("get_rotation"), &m_class::get_rotation); \ - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation"), "set_rotation", "get_rotation"); \ - \ - ClassDB::bind_method(D_METHOD("set_sample_cubic", "cubic"), &m_class::set_sample_cubic); \ - ClassDB::bind_method(D_METHOD("get_sample_cubic"), &m_class::get_sample_cubic); \ - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sample_cubic"), "set_sample_cubic", "get_sample_cubic"); \ - \ - ADD_SIGNAL(MethodInfo("shape_changed")); \ - \ - BIND_ENUM_CONSTANT(DISTRIBUTE_BY_COUNT); \ - BIND_ENUM_CONSTANT(DISTRIBUTE_BY_DISTANCE); \ - BIND_ENUM_CONSTANT(ALIGN_FROM_START); \ - BIND_ENUM_CONSTANT(ALIGN_CENTERED); \ - BIND_ENUM_CONSTANT(ALIGN_FROM_END); \ - BIND_ENUM_CONSTANT(ROTATE_FIXED); \ - BIND_ENUM_CONSTANT(ROTATE_PATH); \ -} \ -\ -void m_class::_notification(int p_what) { \ - switch (p_what) { \ - case NOTIFICATION_READY: { \ - set_process_internal(true); \ - _rebuild_mesh(); \ - } break; \ - \ - case NOTIFICATION_INTERNAL_PROCESS: { \ - if (_pop_is_dirty()) { \ - _rebuild_mesh(); \ - } \ - } break; \ - } \ -} \ -\ -void m_class::_validate_property(PropertyInfo &p_property) const { \ - if (p_property.name == StringName("count") && distribution == DISTRIBUTE_BY_DISTANCE) { \ - p_property.usage = PROPERTY_USAGE_NONE; \ - } else if (p_property.name == StringName("distance") && distribution == DISTRIBUTE_BY_COUNT) { \ - p_property.usage = PROPERTY_USAGE_NONE; \ - } \ -} \ -\ -void m_class::_rebuild_mesh() { \ - for (const uint32_t owner_id : owners) { \ - shape_owner_clear_shapes(owner_id); \ - remove_shape_owner(owner_id); \ - } \ - owners.clear(); \ - \ - if (collision_debug != nullptr) { \ - remove_child(collision_debug); \ - collision_debug->queue_free(); \ - collision_debug = nullptr; \ - } \ - \ - if (path3d == nullptr || path3d->get_curve().is_null() || !path3d->is_inside_tree() || !shape.is_valid()) { \ - return; \ - } \ - \ - Transform3D mod_transform = _get_relative_transform(); \ - \ - Ref curve = path3d->get_curve(); \ - if (curve->get_point_count() < 2) { \ - return; \ - } \ - \ - double baked_l = curve->get_baked_length(); \ - if (baked_l == 0.0) { \ - return; \ - } \ - \ - uint64_t n_instances = 0; \ - double separation = 0.0; \ - switch (distribution) { \ - case DISTRIBUTE_BY_COUNT: { \ - n_instances = count; \ - separation = baked_l / (count - 1); \ - } break; \ - case DISTRIBUTE_BY_DISTANCE: { \ - separation = distance; \ - n_instances = Math::floor(baked_l / separation) + 1; \ - } break; \ - default: \ - ERR_FAIL(); \ - } \ - \ - double offset = 0.0; \ - if (distribution != DISTRIBUTE_BY_COUNT) { \ - switch (alignment) { \ - case ALIGN_FROM_START: { \ - offset = 0.0; \ - } break; \ - case ALIGN_CENTERED: { \ - offset = (baked_l - separation * (n_instances - 1)) * 0.5; \ - } break; \ - case ALIGN_FROM_END: { \ - offset = baked_l - separation * (n_instances - 1); \ - } break; \ - default: \ - ERR_FAIL(); \ - } \ - } \ - \ - bool debugging = Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_collisions_hint(); \ - \ - if (debugging) { \ - collision_debug = memnew(MultiMeshInstance3D); \ - collision_debug->set_multimesh(memnew(MultiMesh)); \ - collision_debug->get_multimesh()->set_transform_format(MultiMesh::TRANSFORM_3D); \ - collision_debug->get_multimesh()->set_instance_count(n_instances); \ - collision_debug->get_multimesh()->set_mesh(shape->get_debug_mesh()); \ - add_child(collision_debug, false, INTERNAL_MODE_BACK); \ - } \ - \ - owners.resize(n_instances); \ - for (uint64_t i = 0; i < n_instances; ++i) { \ - Transform3D transform; \ - switch (rotation_mode) { \ - case ROTATE_FIXED: { \ - transform.origin = curve->sample_baked(offset, sample_cubic); \ - transform.basis = Basis::from_euler(rotation); \ - } break; \ - case ROTATE_PATH: { \ - transform = curve->sample_baked_with_rotation(offset, sample_cubic, true); \ - transform.basis.rotate(rotation); \ - } break; \ - default: \ - ERR_FAIL(); \ - } \ - \ - transform *= _sample_3d_modifiers_at(offset / baked_l); \ - \ - if (relative_transform == TRANSFORM_SHAPE_PATH_NODE) { \ - transform = mod_transform * transform; \ - } \ - \ - if (debugging) { \ - collision_debug->get_multimesh()->set_instance_transform(i, transform); \ - } \ - \ - uint32_t owner_id = create_shape_owner(this); \ - shape_owner_add_shape(owner_id, shape); \ - shape_owner_set_transform(owner_id, transform); \ - owners[i] = owner_id; \ - offset += separation; \ - } \ -} \ -\ -void m_class::_on_shape_changed() { \ - queue_rebuild(); \ - emit_signal("shape_changed"); \ -} - - -#define PATH_MESH_WITH_COLLISION(m_generated_mesh) \ -public: \ - enum CollisionMode { \ - COLLISION_MODE_NONE = 0, \ - COLLISION_MODE_TRIMESH, \ - COLLISION_MODE_CONVEX, \ - COLLISION_MODE_MULTIPLE_CONVEX, \ - }; \ - \ - _FORCE_INLINE_ void set_generate_collision(const CollisionMode p_mode) { \ - if (collision_mode != p_mode) { \ - collision_mode = p_mode; \ - queue_rebuild_collision(); \ - notify_property_list_changed(); \ - } \ - } \ - \ - _FORCE_INLINE_ CollisionMode get_generate_collision() const { return collision_mode; } \ - \ - _FORCE_INLINE_ void set_convex_collision_clean(bool p_clean) { \ - if (convex_collision_clean != p_clean) { \ - convex_collision_clean = p_clean; \ - queue_rebuild_collision(); \ - } \ - } \ - \ - _FORCE_INLINE_ bool get_convex_collision_clean() const { return convex_collision_clean; } \ - \ - _FORCE_INLINE_ void set_convex_collision_simplify(bool p_simplify) { \ - if (convex_collision_simplify != p_simplify) { \ - convex_collision_simplify = p_simplify; \ - queue_rebuild_collision(); \ - } \ - } \ - \ - _FORCE_INLINE_ bool get_convex_collision_simplify() const { return convex_collision_simplify; } \ - \ - _FORCE_INLINE_ Node *get_collision_node() const { return collision_node; } \ - \ - _FORCE_INLINE_ void queue_rebuild_collision() { collision_dirty = true; } \ - \ - _FORCE_INLINE_ void set_collision_layer(uint32_t p_layer) { \ - if (collision_layer != p_layer && collision_node != nullptr) { \ - PhysicsBody3D *body = cast_to(collision_node); \ - if (body != nullptr) { \ - collision_layer = p_layer; \ - body->set_collision_layer(collision_layer); \ - } \ - } \ - } \ - \ - _FORCE_INLINE_ uint32_t get_collision_layer() const { return collision_layer; } \ - \ - _FORCE_INLINE_ void set_collision_mask(uint32_t p_mask) { \ - if (collision_mask != p_mask && collision_node != nullptr) { \ - PhysicsBody3D *body = cast_to(collision_node); \ - if (body != nullptr) { \ - collision_mask = p_mask; \ - body->set_collision_mask(collision_mask); \ - } \ - } \ - } \ - \ - _FORCE_INLINE_ uint32_t get_collision_mask() const { return collision_mask; } \ - \ - _FORCE_INLINE_ Node *create_trimesh_collision_node() { \ - return _setup_collision_node(m_generated_mesh->create_trimesh_shape()); \ - } \ - \ - _FORCE_INLINE_ void create_trimesh_collision() { \ - _add_child_collision_node(create_trimesh_collision_node()); \ - } \ - \ - _FORCE_INLINE_ Node *create_convex_collision_node(bool p_clean = true, bool p_simplify = false) { \ - return _setup_collision_node(m_generated_mesh->create_convex_shape(p_clean, p_simplify)); \ - } \ - \ - _FORCE_INLINE_ void create_convex_collision(bool p_clean = true, bool p_simplify = false) { \ - _add_child_collision_node(create_convex_collision_node(p_clean, p_simplify)); \ - } \ - \ - _FORCE_INLINE_ Node *create_multiple_convex_collision_node(const Ref &p_settings = nullptr) { \ - Ref settings = p_settings; \ - if (!settings.is_valid()) { \ - settings.instantiate(); \ - } \ - \ - /* TODO: GDExtension doesn't have API parity here... */ \ - LocalVector> shapes; /* = m_generated_mesh->convex_decompose(settings); */ \ - if (shapes.is_empty()) { \ - return nullptr; \ - } \ - \ - StaticBody3D *static_body = memnew(StaticBody3D); \ - for (int i = 0; i < shapes.size(); ++i) { \ - CollisionShape3D *cshape = memnew(CollisionShape3D); \ - cshape->set_shape(shapes[i]); \ - static_body->add_child(cshape, true); \ - } \ - return static_body; \ - } \ - \ - _FORCE_INLINE_ void create_multiple_convex_collision(const Ref &p_settings = nullptr) { \ - _add_child_collision_node(create_multiple_convex_collision_node(p_settings)); \ - } \ - \ -protected: \ - void _validate_property(PropertyInfo &p_property) const { \ - if (p_property.name == StringName("convex_collision_clean") || p_property.name == StringName("convex_collision_simplify")) { \ - p_property.usage = collision_mode == COLLISION_MODE_CONVEX ? PROPERTY_USAGE_DEFAULT : PROPERTY_USAGE_NONE; \ - } else if (p_property.name == StringName("collision_layer") || p_property.name == StringName("collision_mask")) { \ - p_property.usage = collision_mode == COLLISION_MODE_NONE ? PROPERTY_USAGE_NONE : PROPERTY_USAGE_DEFAULT; \ - } \ - } \ - \ -private: \ - CollisionMode collision_mode = COLLISION_MODE_NONE; \ - bool convex_collision_clean = true; \ - bool convex_collision_simplify = false; \ - bool collision_dirty = false; \ - Node *collision_node = nullptr; \ - MeshInstance3D *collision_debug = nullptr; \ - uint32_t collision_layer = 1; \ - uint32_t collision_mask = 1; \ - \ - _FORCE_INLINE_ Node *_setup_collision_node(const Ref &shape) { \ - StaticBody3D *static_body = memnew(StaticBody3D); \ - CollisionShape3D *cshape = memnew(CollisionShape3D); \ - cshape->set_shape(shape); \ - static_body->add_child(cshape, true); \ - return static_body; \ - } \ - \ - _FORCE_INLINE_ void _add_child_collision_node(Node *p_node) { \ - if (p_node != nullptr) { \ - add_child(p_node, true); \ - PhysicsBody3D *body = cast_to(p_node); \ - if (body != nullptr) { \ - body->set_collision_layer(collision_layer); \ - body->set_collision_mask(collision_mask); \ - } \ - if (get_owner() != nullptr) { \ - p_node->set_owner(get_owner()); \ - for (int i = 0; i < p_node->get_child_count(); ++i) { \ - Node *c = p_node->get_child(i); \ - c->set_owner(get_owner()); \ - } \ - } \ - } \ - } \ - \ - _FORCE_INLINE_ void _rebuild_collision_node() { \ - collision_dirty = false; \ - \ - if (collision_node != nullptr) { \ - remove_child(collision_node); \ - collision_node->queue_free(); \ - collision_node = nullptr; \ - } \ - \ - if (collision_debug != nullptr) { \ - remove_child(collision_debug); \ - collision_debug->queue_free(); \ - collision_debug = nullptr; \ - } \ - \ - switch (collision_mode) { \ - case COLLISION_MODE_TRIMESH: \ - collision_node = create_trimesh_collision_node(); \ - break; \ - case COLLISION_MODE_CONVEX: \ - collision_node = create_convex_collision_node(convex_collision_clean, convex_collision_simplify); \ - break; \ - case COLLISION_MODE_MULTIPLE_CONVEX: \ - collision_node = create_multiple_convex_collision_node(); \ - break; \ - default: \ - break; \ - } \ - \ - if (collision_node != nullptr) { \ - add_child(collision_node, false, INTERNAL_MODE_BACK); \ - \ - if (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_collisions_hint()) { \ - collision_debug = memnew(MeshInstance3D); \ - collision_debug->set_mesh(cast_to(collision_node->get_child(0))->get_shape()->get_debug_mesh()); \ - add_child(collision_debug, false, INTERNAL_MODE_BACK); \ - } \ - } \ - } - - -#define PATH_MESH_WITH_COLLISION_BINDS(m_class) \ - ADD_GROUP("Collision", ""); \ - ClassDB::bind_method(D_METHOD("set_generate_collision", "mode"), &m_class::set_generate_collision); \ - ClassDB::bind_method(D_METHOD("get_generate_collision"), &m_class::get_generate_collision); \ - ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mode", PROPERTY_HINT_ENUM, "None,Trimesh,Convex,Multiple Convex"), "set_generate_collision", "get_generate_collision"); \ - \ - ClassDB::bind_method(D_METHOD("set_convex_collision_clean", "clean"), &m_class::set_convex_collision_clean); \ - ClassDB::bind_method(D_METHOD("get_convex_collision_clean"), &m_class::get_convex_collision_clean); \ - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "convex_collision_clean"), "set_convex_collision_clean", "get_convex_collision_clean"); \ - \ - ClassDB::bind_method(D_METHOD("set_convex_collision_simplify", "simplify"), &m_class::set_convex_collision_simplify); \ - ClassDB::bind_method(D_METHOD("get_convex_collision_simplify"), &m_class::get_convex_collision_simplify); \ - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "convex_collision_simplify"), "set_convex_collision_simplify", "get_convex_collision_simplify"); \ - \ - ClassDB::bind_method(D_METHOD("set_collision_layer", "layer"), &m_class::set_collision_layer); \ - ClassDB::bind_method(D_METHOD("get_collision_layer"), &m_class::get_collision_layer); \ - ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer"); \ - \ - ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &m_class::set_collision_mask); \ - ClassDB::bind_method(D_METHOD("get_collision_mask"), &m_class::get_collision_mask); \ - ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); \ - \ - ClassDB::bind_method(D_METHOD("create_trimesh_collision"), &m_class::create_trimesh_collision); \ - ClassDB::bind_method(D_METHOD("create_convex_collision", "clean", "simplify"), &m_class::create_convex_collision, DEFVAL(true), DEFVAL(false)); \ - ClassDB::bind_method(D_METHOD("create_multiple_convex_collision", "settings"), &m_class::create_multiple_convex_collision, DEFVAL(nullptr)); \ - \ - BIND_ENUM_CONSTANT(COLLISION_MODE_NONE); \ - BIND_ENUM_CONSTANT(COLLISION_MODE_TRIMESH); \ - BIND_ENUM_CONSTANT(COLLISION_MODE_CONVEX); \ - BIND_ENUM_CONSTANT(COLLISION_MODE_MULTIPLE_CONVEX); - \ No newline at end of file diff --git a/addons/PathMesh3D/src/path_collision_tool_3d.hpp b/addons/PathMesh3D/src/path_collision_tool_3d.hpp new file mode 100644 index 0000000..3880052 --- /dev/null +++ b/addons/PathMesh3D/src/path_collision_tool_3d.hpp @@ -0,0 +1,306 @@ +#pragma once + +#include "godot_cpp/classes/object.hpp" +#include "godot_cpp/core/defs.hpp" +#include +#include "path_tool_3d.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace godot { + +template +class PathCollisionTool3D : public PathTool3D { +public: + enum CollisionMode { + COLLISION_MODE_NONE = 0, + COLLISION_MODE_TRIMESH, + COLLISION_MODE_CONVEX, + COLLISION_MODE_MULTIPLE_CONVEX, + }; + + _FORCE_INLINE_ void set_generate_collision(const CollisionMode p_mode) { + if (collision_mode != p_mode) { + collision_mode = p_mode; + queue_rebuild_collision(); + _self()->notify_property_list_changed(); + } + } + + _FORCE_INLINE_ CollisionMode get_generate_collision() const { + return collision_mode; + } + + _FORCE_INLINE_ void set_convex_collision_clean(bool p_clean) { + if (convex_collision_clean != p_clean) { + convex_collision_clean = p_clean; + queue_rebuild_collision(); + } + } + + _FORCE_INLINE_ bool get_convex_collision_clean() const { + return convex_collision_clean; + } + + _FORCE_INLINE_ void set_convex_collision_simplify(bool p_simplify) { + if (convex_collision_simplify != p_simplify) { + convex_collision_simplify = p_simplify; + queue_rebuild_collision(); + } + } + + _FORCE_INLINE_ bool get_convex_collision_simplify() const { + return convex_collision_simplify; + } + + _FORCE_INLINE_ Node *get_collision_node() const { + return Object::cast_to(ObjectDB::get_instance(collision_node_id)); + } + + _FORCE_INLINE_ void queue_rebuild_collision() { + collision_dirty = true; + } + + _FORCE_INLINE_ void set_collision_layer(uint32_t p_layer) { + Node *collision_node = get_collision_node(); + if (collision_layer != p_layer && collision_node != nullptr) { + PhysicsBody3D *body = Object::cast_to(collision_node); + if (body != nullptr) { + collision_layer = p_layer; + body->set_collision_layer(collision_layer); + } + } + } + + _FORCE_INLINE_ uint32_t get_collision_layer() const { + return collision_layer; + } + + _FORCE_INLINE_ void set_collision_mask(uint32_t p_mask) { + Node *collision_node = get_collision_node(); + if (collision_mask != p_mask && collision_node != nullptr) { + PhysicsBody3D *body = Object::cast_to(collision_node); + if (body != nullptr) { + collision_mask = p_mask; + body->set_collision_mask(collision_mask); + } + } + } + + _FORCE_INLINE_ uint32_t get_collision_mask() const { + return collision_mask; + } + + _FORCE_INLINE_ Node *create_trimesh_collision_node() { + return _setup_collision_node(_get_mesh()->create_trimesh_shape()); + } + + _FORCE_INLINE_ void create_trimesh_collision() { + _add_child_collision_node(create_trimesh_collision_node()); + } + + _FORCE_INLINE_ Node *create_convex_collision_node(bool p_clean = true, bool p_simplify = false) { + return _setup_collision_node(_get_mesh()->create_convex_shape(p_clean, p_simplify)); + } + + _FORCE_INLINE_ void create_convex_collision(bool p_clean = true, bool p_simplify = false) { + _add_child_collision_node(create_convex_collision_node(p_clean, p_simplify)); + } + + _FORCE_INLINE_ Node *create_multiple_convex_collision_node(const Ref &p_settings = nullptr) { + Ref settings = p_settings; + if (!settings.is_valid()) { + settings.instantiate(); + } + + /* TODO: GDExtension doesn't have API parity here... */ + LocalVector> shapes; // = _self()->_get_mesh()->convex_decompose(settings); + if (shapes.is_empty()) { + return nullptr; + } + + StaticBody3D *static_body = memnew(StaticBody3D); + for (int i = 0; i < shapes.size(); ++i) { + CollisionShape3D *cshape = memnew(CollisionShape3D); + cshape->set_shape(shapes[i]); + static_body->add_child(cshape, true); + } + return static_body; + } + + _FORCE_INLINE_ void create_multiple_convex_collision(const Ref &p_settings = nullptr) { + _add_child_collision_node(create_multiple_convex_collision_node(p_settings)); + } + +protected: + static void _bind_path_collision_tool_3d_methods() { + ClassDB::add_property_group(T::get_class_static(), "Collision", ""); + ClassDB::bind_method(D_METHOD("set_generate_collision", "mode"), static_cast(&PathCollisionTool3D::set_generate_collision)); + ClassDB::bind_method(D_METHOD("get_generate_collision"), static_cast(&PathCollisionTool3D::get_generate_collision)); + ClassDB::add_property(T::get_class_static(), PropertyInfo(Variant::INT, "collision_mode", PROPERTY_HINT_ENUM, "None,Trimesh,Convex,Multiple Convex"), "set_generate_collision", "get_generate_collision"); + + ClassDB::bind_method(D_METHOD("set_convex_collision_clean", "clean"), static_cast(&PathCollisionTool3D::set_convex_collision_clean)); + ClassDB::bind_method(D_METHOD("get_convex_collision_clean"), static_cast(&PathCollisionTool3D::get_convex_collision_clean)); + ClassDB::add_property(T::get_class_static(), PropertyInfo(Variant::BOOL, "convex_collision_clean"), "set_convex_collision_clean", "get_convex_collision_clean"); + + ClassDB::bind_method(D_METHOD("set_convex_collision_simplify", "simplify"), static_cast(&PathCollisionTool3D::set_convex_collision_simplify)); + ClassDB::bind_method(D_METHOD("get_convex_collision_simplify"), static_cast(&PathCollisionTool3D::get_convex_collision_simplify)); + ClassDB::add_property(T::get_class_static(), PropertyInfo(Variant::BOOL, "convex_collision_simplify"), "set_convex_collision_simplify", "get_convex_collision_simplify"); + + ClassDB::bind_method(D_METHOD("set_collision_layer", "layer"), static_cast(&PathCollisionTool3D::set_collision_layer)); + ClassDB::bind_method(D_METHOD("get_collision_layer"), static_cast(&PathCollisionTool3D::get_collision_layer)); + ClassDB::add_property(T::get_class_static(), PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer"); + + ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), static_cast(&PathCollisionTool3D::set_collision_mask)); + ClassDB::bind_method(D_METHOD("get_collision_mask"), static_cast(&PathCollisionTool3D::get_collision_mask)); + ClassDB::add_property(T::get_class_static(), PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); + + ClassDB::bind_method(D_METHOD("create_trimesh_collision"), static_cast(&PathCollisionTool3D::create_trimesh_collision)); + ClassDB::bind_method(D_METHOD("create_convex_collision", "clean", "simplify"), static_cast(&PathCollisionTool3D::create_convex_collision), DEFVAL(true), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("create_multiple_convex_collision", "settings"), static_cast&)>(&PathCollisionTool3D::create_multiple_convex_collision), DEFVAL(nullptr)); + + ClassDB::bind_integer_constant(T::get_class_static(), "CollisionMode", "COLLISION_MODE_NONE", COLLISION_MODE_NONE); + ClassDB::bind_integer_constant(T::get_class_static(), "CollisionMode", "COLLISION_MODE_TRIMESH", COLLISION_MODE_TRIMESH); + ClassDB::bind_integer_constant(T::get_class_static(), "CollisionMode", "COLLISION_MODE_CONVEX", COLLISION_MODE_CONVEX); + ClassDB::bind_integer_constant(T::get_class_static(), "CollisionMode", "COLLISION_MODE_MULTIPLE_CONVEX", COLLISION_MODE_MULTIPLE_CONVEX); + } + + void _notification_path_collision_tool_3d(int p_what) { + switch (p_what) { + case Node::NOTIFICATION_READY: { + _self()->set_process_internal(true); + _rebuild_collision_node(); + } break; + + case Node::NOTIFICATION_INTERNAL_PROCESS: { + if (_self()->_pop_is_collision_dirty()) { + _rebuild_collision_node(); + } + } break; + } + } + + void _validate_path_collision_tool_3d_property(PropertyInfo &p_property) const { + if (p_property.name == StringName("convex_collision_clean") || p_property.name == StringName("convex_collision_simplify")) { + p_property.usage = get_generate_collision() == COLLISION_MODE_CONVEX ? PROPERTY_USAGE_DEFAULT : PROPERTY_USAGE_NONE; + } else if (p_property.name == StringName("collision_layer") || p_property.name == StringName("collision_mask")) { + p_property.usage = get_generate_collision() == COLLISION_MODE_NONE ? PROPERTY_USAGE_NONE : PROPERTY_USAGE_DEFAULT; + } + } + + virtual Ref _get_mesh() const = 0; + + _FORCE_INLINE_ bool _pop_is_collision_dirty() { + bool is_dirty = collision_dirty; + collision_dirty = false; + return is_dirty; + } + + _FORCE_INLINE_ void _clear_collision_node() { + Node *collision_node = get_collision_node(); + + if (collision_node != nullptr) { + _self()->remove_child(collision_node); + collision_node->queue_free(); + collision_node = nullptr; + } + } + + _FORCE_INLINE_ void _rebuild_collision_node() { + _clear_collision_node(); + + if (collision_debug_id != 0) { + MeshInstance3D *collision_debug = Object::cast_to(ObjectDB::get_instance(collision_debug_id)); + if (collision_debug != nullptr) { + _self()->remove_child(collision_debug); + collision_debug->queue_free(); + } + collision_debug_id = 0; + } + + Node *collision_node = nullptr; + + switch (collision_mode) { + case COLLISION_MODE_TRIMESH: + collision_node = create_trimesh_collision_node(); + break; + case COLLISION_MODE_CONVEX: + collision_node = create_convex_collision_node(convex_collision_clean, convex_collision_simplify); + break; + case COLLISION_MODE_MULTIPLE_CONVEX: + collision_node = create_multiple_convex_collision_node(); + break; + default: + break; + } + + if (collision_node != nullptr) { + collision_node_id = collision_node->get_instance_id(); + + _self()->add_child(collision_node, false, _self()->INTERNAL_MODE_BACK); + + if (Engine::get_singleton()->is_editor_hint() || _self()->get_tree()->is_debugging_collisions_hint()) { + MeshInstance3D *collision_debug = memnew(MeshInstance3D); + collision_debug->set_mesh(Object::cast_to(collision_node->get_child(0))->get_shape()->get_debug_mesh()); + _self()->add_child(collision_debug, false, _self()->INTERNAL_MODE_BACK); + collision_debug_id = collision_debug->get_instance_id(); + } + } + } + +private: + CollisionMode collision_mode = COLLISION_MODE_NONE; + bool convex_collision_clean = true; + bool convex_collision_simplify = false; + bool collision_dirty = false; + uint32_t collision_node_id = 0; + uint64_t collision_debug_id = 0; + uint32_t collision_layer = 1; + uint32_t collision_mask = 1; + + _FORCE_INLINE_ Node *_setup_collision_node(const Ref &shape) { + StaticBody3D *static_body = memnew(StaticBody3D); + CollisionShape3D *cshape = memnew(CollisionShape3D); + cshape->set_shape(shape); + static_body->add_child(cshape, true); + return static_body; + } + + _FORCE_INLINE_ void _add_child_collision_node(Node *p_node) { + if (p_node != nullptr) { + _self()->add_child(p_node, true); + PhysicsBody3D *body = Object::cast_to(p_node); + if (body != nullptr) { + body->set_collision_layer(collision_layer); + body->set_collision_mask(collision_mask); + } + if (_self()->get_owner() != nullptr) { + p_node->set_owner(_self()->get_owner()); + for (int i = 0; i < p_node->get_child_count(); ++i) { + Node *c = p_node->get_child(i); + c->set_owner(_self()->get_owner()); + } + } + } + } + + _FORCE_INLINE_ T *_self() { + return static_cast(this); + } + _FORCE_INLINE_ const T *_self() const { + return static_cast(this); + } +}; + +} \ No newline at end of file diff --git a/addons/PathMesh3D/src/path_extrude_3d.cpp b/addons/PathMesh3D/src/path_extrude_3d.cpp index 7549ff1..0482f41 100644 --- a/addons/PathMesh3D/src/path_extrude_3d.cpp +++ b/addons/PathMesh3D/src/path_extrude_3d.cpp @@ -2,6 +2,9 @@ #include #include "path_extrude_3d.hpp" +#include "godot_cpp/classes/array_mesh.hpp" +#include "path_collision_tool_3d.hpp" +#include "path_tool_3d.hpp" using namespace godot; @@ -126,8 +129,6 @@ PathExtrude3D::PathExtrude3D() { } PathExtrude3D::~PathExtrude3D() { - PATH_TOOL_DESTRUCTOR(PathExtrude3D) - if (profile.is_valid()) { if (profile->is_connected("changed", callable_mp(this, &PathExtrude3D::_on_profile_changed))) { profile->disconnect("changed", callable_mp(this, &PathExtrude3D::_on_profile_changed)); @@ -141,7 +142,8 @@ PathExtrude3D::~PathExtrude3D() { } void PathExtrude3D::_bind_methods() { - PATH_TOOL_BINDS(PathExtrude3D, mesh, MESH) + PathTool3D::_bind_path_tool_3d_methods(); + PathCollisionTool3D::_bind_path_collision_tool_3d_methods(); ClassDB::bind_method(D_METHOD("get_baked_mesh"), &PathExtrude3D::get_baked_mesh); @@ -184,8 +186,6 @@ void PathExtrude3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_triangle_count"), &PathExtrude3D::get_triangle_count); ADD_PROPERTY(PropertyInfo(Variant::INT, "triangle_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY), "", "get_triangle_count"); - PATH_MESH_WITH_COLLISION_BINDS(PathExtrude3D) - ADD_SIGNAL(MethodInfo("profile_changed")); BIND_BITFIELD_FLAG(END_CAPS_NONE); @@ -195,31 +195,25 @@ void PathExtrude3D::_bind_methods() { } void PathExtrude3D::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_READY: { - set_process_internal(true); - _rebuild_mesh(); - } break; - - case NOTIFICATION_INTERNAL_PROCESS: { - if ((profile.is_valid() && profile->regen_if_dirty()) || _pop_is_dirty()) { - _rebuild_mesh(); - } else if (collision_dirty) { - _rebuild_collision_node(); - } - } break; - } + PathTool3D::_notification_path_tool_3d(p_what); + PathCollisionTool3D::_notification_path_collision_tool_3d(p_what); +} + +void PathExtrude3D::_validate_property(PropertyInfo &p_property) const { + PathCollisionTool3D::_validate_path_collision_tool_3d_property(p_property); +} + +bool PathExtrude3D::_pop_should_rebuild() { + return (profile.is_valid() && profile->regen_if_dirty()) || _pop_is_dirty(); } void PathExtrude3D::_rebuild_mesh() { generated_mesh->clear_surfaces(); - if (nullptr != collision_node) { - remove_child(collision_node); - collision_node->queue_free(); - collision_node = nullptr; - } n_tris = 0; + _clear_collision_node(); + + Path3D *path3d = get_path_3d(); if (profile.is_null() || path3d == nullptr || path3d->get_curve().is_null() || !path3d->is_inside_tree()) { return; } @@ -291,8 +285,8 @@ void PathExtrude3D::_rebuild_mesh() { uv_transforms[idx_slice] = _sample_uv_modifiers_at(offset / baked_l); } - if (relative_transform == TRANSFORM_MESH_PATH_NODE) { - Transform3D transform = _get_relative_transform(); + if (get_relative_transform() == TRANSFORM_PATH_NODE) { + Transform3D transform = _get_final_transform(); for (uint64_t idx_slice = 0; idx_slice < n_slices; ++idx_slice) { transforms[idx_slice] = transform * transforms[idx_slice]; } @@ -646,4 +640,8 @@ void PathExtrude3D::_rebuild_mesh() { void PathExtrude3D::_on_profile_changed() { emit_signal("profile_changed"); +} + +Ref PathExtrude3D::_get_mesh() const { + return generated_mesh; } \ No newline at end of file diff --git a/addons/PathMesh3D/src/path_extrude_3d.hpp b/addons/PathMesh3D/src/path_extrude_3d.hpp index 494312b..0f88974 100644 --- a/addons/PathMesh3D/src/path_extrude_3d.hpp +++ b/addons/PathMesh3D/src/path_extrude_3d.hpp @@ -4,16 +4,14 @@ #include #include -#include "path_tool.hpp" -#include "path_collision_tool.hpp" +#include "path_tool_3d.hpp" +#include "path_collision_tool_3d.hpp" #include "path_extrude_profile_base.hpp" namespace godot { -class PathExtrude3D : public GeometryInstance3D { +class PathExtrude3D : public GeometryInstance3D, public PathCollisionTool3D { GDCLASS(PathExtrude3D, GeometryInstance3D) - PATH_TOOL(PathExtrude3D, MESH) - PATH_MESH_WITH_COLLISION(generated_mesh) public: enum EndCaps { @@ -58,6 +56,12 @@ class PathExtrude3D : public GeometryInstance3D { protected: static void _bind_methods(); + void _notification(int p_what); + void _validate_property(PropertyInfo &p_property) const; + + virtual void _rebuild_mesh() override final; + virtual bool _pop_should_rebuild() override final; + virtual Ref _get_mesh() const override final; private: Ref profile; @@ -75,6 +79,7 @@ class PathExtrude3D : public GeometryInstance3D { void _on_profile_changed(); }; + } VARIANT_ENUM_CAST(PathExtrude3D::RelativeTransform) diff --git a/addons/PathMesh3D/src/path_mesh_3d.cpp b/addons/PathMesh3D/src/path_mesh_3d.cpp index 10ede13..109fdc2 100644 --- a/addons/PathMesh3D/src/path_mesh_3d.cpp +++ b/addons/PathMesh3D/src/path_mesh_3d.cpp @@ -2,7 +2,8 @@ #include #include "path_mesh_3d.hpp" - +#include "path_collision_tool_3d.hpp" +#include "path_tool_3d.hpp" using namespace godot; @@ -206,7 +207,8 @@ uint64_t PathMesh3D::get_total_triangle_count() const { } void PathMesh3D::_bind_methods() { - PATH_TOOL_BINDS(PathMesh3D, mesh, MESH) + PathTool3D::_bind_path_tool_3d_methods(); + PathCollisionTool3D::_bind_path_collision_tool_3d_methods(); ClassDB::bind_method(D_METHOD("get_baked_mesh"), &PathMesh3D::get_baked_mesh); @@ -237,8 +239,6 @@ void PathMesh3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_total_triangle_count"), &PathMesh3D::get_total_triangle_count); ADD_PROPERTY(PropertyInfo(Variant::INT, "total_triangle_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY), "", "get_total_triangle_count"); - - PATH_MESH_WITH_COLLISION_BINDS(PathMesh3D) ADD_SIGNAL(MethodInfo("mesh_changed")); @@ -251,20 +251,8 @@ void PathMesh3D::_bind_methods() { } void PathMesh3D::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_READY: { - set_process_internal(true); - _rebuild_mesh(); - } break; - - case NOTIFICATION_INTERNAL_PROCESS: { - if (_pop_is_dirty()) { - _rebuild_mesh(); - } else if (collision_dirty) { - _rebuild_collision_node(); - } - } break; - } + PathTool3D::_notification_path_tool_3d(p_what); + PathCollisionTool3D::_notification_path_collision_tool_3d(p_what); } void PathMesh3D::_get_property_list(List *p_list) const { @@ -419,22 +407,24 @@ bool PathMesh3D::_get(const StringName &p_name, Variant &r_property) const { return false; } +void PathMesh3D::_validate_property(PropertyInfo &p_property) const { + PathCollisionTool3D::_validate_path_collision_tool_3d_property(p_property); +} + void PathMesh3D::_rebuild_mesh() { generated_mesh->clear_surfaces(); for (SurfaceData &surf : surfaces) { surf.n_tris = 0; } - if (collision_node != nullptr) { - remove_child(collision_node); - collision_node->queue_free(); - collision_node = nullptr; - } + _clear_collision_node(); + + Path3D *path3d = get_path_3d(); if (path3d == nullptr || path3d->get_curve().is_null() || !path3d->is_inside_tree() || source_mesh.is_null()) { return; } - Transform3D mod_transform = _get_relative_transform(); + Transform3D mod_transform = _get_final_transform(); Ref curve = path3d->get_curve(); ERR_FAIL_COND_MSG(curve->get_point_count() < 2, "Curve has < 2 points, cannot tesselate."); @@ -591,7 +581,7 @@ void PathMesh3D::_rebuild_mesh() { vertex = old_verts[idx_vert]; } - Transform3D final_transform = relative_transform == TRANSFORM_MESH_PATH_NODE ? mod_transform * transform : transform; + Transform3D final_transform = get_relative_transform() == TRANSFORM_PATH_NODE ? mod_transform * transform : transform; new_verts[k] = final_transform.xform(vertex); if (has_column[Mesh::ARRAY_NORMAL]) { @@ -699,6 +689,7 @@ double PathMesh3D::_get_mesh_length() const { } uint64_t PathMesh3D::_get_max_count() const { + Path3D *path3d = get_path_3d(); if (source_mesh.is_valid() && path3d != nullptr && path3d->get_curve().is_valid()) { return path3d->get_curve()->get_baked_length() / _get_mesh_length(); } else { @@ -723,24 +714,19 @@ PathMesh3D::PathMesh3D() { } PathMesh3D::~PathMesh3D() { - PATH_TOOL_DESTRUCTOR(PathMesh3D) - if (source_mesh.is_valid()) { if (source_mesh->is_connected("changed", callable_mp(this, &PathMesh3D::_on_mesh_changed))) { source_mesh->disconnect("changed", callable_mp(this, &PathMesh3D::_on_mesh_changed)); } source_mesh.unref(); } - if (path3d != nullptr) { - if (UtilityFunctions::is_instance_id_valid(path3d->get_instance_id()) && - path3d->is_connected("curve_changed", callable_mp(this, &PathMesh3D::_on_curve_changed))) { - path3d->disconnect("curve_changed", callable_mp(this, &PathMesh3D::_on_curve_changed)); - } - path3d = nullptr; - } if (generated_mesh.is_valid()) { generated_mesh->clear_surfaces(); generated_mesh.unref(); } surfaces.clear(); +} + +Ref PathMesh3D::_get_mesh() const { + return generated_mesh; } \ No newline at end of file diff --git a/addons/PathMesh3D/src/path_mesh_3d.hpp b/addons/PathMesh3D/src/path_mesh_3d.hpp index b2a657b..562072b 100644 --- a/addons/PathMesh3D/src/path_mesh_3d.hpp +++ b/addons/PathMesh3D/src/path_mesh_3d.hpp @@ -4,14 +4,12 @@ #include #include -#include "path_tool.hpp" -#include "path_collision_tool.hpp" +#include "path_tool_3d.hpp" +#include "path_collision_tool_3d.hpp" namespace godot { -class PathMesh3D : public GeometryInstance3D { +class PathMesh3D : public GeometryInstance3D, public PathCollisionTool3D { GDCLASS(PathMesh3D, GeometryInstance3D) - PATH_TOOL(PathMesh3D, MESH) - PATH_MESH_WITH_COLLISION(generated_mesh) public: enum Distribution { @@ -75,6 +73,11 @@ class PathMesh3D : public GeometryInstance3D { bool _property_get_revert(const StringName &p_name, Variant &r_property) const; bool _set(const StringName &p_name, const Variant &p_property); bool _get(const StringName &p_name, Variant &r_property) const; + void _notification(int p_what); + void _validate_property(PropertyInfo &p_property) const; + + virtual void _rebuild_mesh() override final; + virtual Ref _get_mesh() const override final; private: Ref source_mesh; diff --git a/addons/PathMesh3D/src/path_modifier_3d.cpp b/addons/PathMesh3D/src/path_modifier_3d.cpp index a6c7c24..a78287c 100644 --- a/addons/PathMesh3D/src/path_modifier_3d.cpp +++ b/addons/PathMesh3D/src/path_modifier_3d.cpp @@ -1,6 +1,6 @@ #include -#include "path_tool.hpp" +#include "path_tool_3d.hpp" #include "path_modifier_3d.hpp" #include "path_mesh_3d.hpp" #include "path_extrude_3d.hpp" diff --git a/addons/PathMesh3D/src/path_multimesh_3d.cpp b/addons/PathMesh3D/src/path_multimesh_3d.cpp index 7269346..5956c21 100644 --- a/addons/PathMesh3D/src/path_multimesh_3d.cpp +++ b/addons/PathMesh3D/src/path_multimesh_3d.cpp @@ -115,7 +115,7 @@ bool PathMultiMesh3D::get_sample_cubic() const { } void PathMultiMesh3D::_bind_methods() { - PATH_TOOL_BINDS(PathMultiMesh3D, mesh, MESH) + PathTool3D::_bind_path_tool_3d_methods(); ClassDB::bind_method(D_METHOD("set_multi_mesh", "multi_mesh"), &PathMultiMesh3D::set_multi_mesh); ClassDB::bind_method(D_METHOD("get_multi_mesh"), &PathMultiMesh3D::get_multi_mesh); @@ -165,18 +165,7 @@ void PathMultiMesh3D::_bind_methods() { } void PathMultiMesh3D::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_READY: { - set_process_internal(true); - _rebuild_mesh(); - } break; - - case NOTIFICATION_INTERNAL_PROCESS: { - if (_pop_is_dirty()) { - _rebuild_mesh(); - } - } break; - } + PathTool3D::_notification_path_tool_3d(p_what); } void PathMultiMesh3D::_validate_property(PropertyInfo &property) const { @@ -202,11 +191,12 @@ void PathMultiMesh3D::_rebuild_mesh() { } multi_mesh->set_instance_count(0); + Path3D *path3d = get_path_3d(); if (path3d == nullptr || path3d->get_curve().is_null() || !path3d->is_inside_tree()) { return; } - Transform3D mod_transform = _get_relative_transform(); + Transform3D mod_transform = _get_final_transform(); Ref curve = path3d->get_curve(); if (curve->get_point_count() < 2) { @@ -275,7 +265,7 @@ void PathMultiMesh3D::_rebuild_mesh() { transform *= _sample_3d_modifiers_at(offset / baked_l); - if (relative_transform == TRANSFORM_MESH_PATH_NODE) { + if (get_relative_transform() == TRANSFORM_PATH_NODE) { transform = mod_transform * transform; } @@ -284,9 +274,7 @@ void PathMultiMesh3D::_rebuild_mesh() { } } -PathMultiMesh3D::~PathMultiMesh3D() { - PATH_TOOL_DESTRUCTOR(PathMultiMesh3D) - +PathMultiMesh3D::~PathMultiMesh3D() { if (multi_mesh.is_valid()) { if (multi_mesh->is_connected("changed", callable_mp(this, &PathMultiMesh3D::_on_mesh_changed))) { multi_mesh->disconnect("changed", callable_mp(this, &PathMultiMesh3D::_on_mesh_changed)); diff --git a/addons/PathMesh3D/src/path_multimesh_3d.hpp b/addons/PathMesh3D/src/path_multimesh_3d.hpp index d280fc5..99b0ad7 100644 --- a/addons/PathMesh3D/src/path_multimesh_3d.hpp +++ b/addons/PathMesh3D/src/path_multimesh_3d.hpp @@ -3,13 +3,12 @@ #include #include -#include "path_tool.hpp" +#include "path_tool_3d.hpp" namespace godot { -class PathMultiMesh3D : public GeometryInstance3D { +class PathMultiMesh3D : public GeometryInstance3D, public PathTool3D { GDCLASS(PathMultiMesh3D, GeometryInstance3D) - PATH_TOOL(PathMultiMesh3D, MESH) public: enum Distribution { @@ -59,6 +58,9 @@ class PathMultiMesh3D : public GeometryInstance3D { protected: static void _bind_methods(); void _validate_property(PropertyInfo &property) const; + void _notification(int p_what); + + virtual void _rebuild_mesh() override final; private: Ref multi_mesh; diff --git a/addons/PathMesh3D/src/path_physics_tool_3d.hpp b/addons/PathMesh3D/src/path_physics_tool_3d.hpp new file mode 100644 index 0000000..54a9eb4 --- /dev/null +++ b/addons/PathMesh3D/src/path_physics_tool_3d.hpp @@ -0,0 +1,362 @@ +#pragma once + +#include "godot_cpp/core/class_db.hpp" +#include "godot_cpp/core/defs.hpp" +#include +#include "path_tool_3d.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace godot { + +template +class PathPhysicsTool3D : public PathTool3D { +public: + enum Distribution { + DISTRIBUTE_BY_COUNT, + DISTRIBUTE_BY_DISTANCE, + DISTRIBUTE_MAX, + }; + enum Rotation { + ROTATE_FIXED, + ROTATE_PATH, + ROTATE_MAX, + }; + enum Alignment { + ALIGN_FROM_START, + ALIGN_CENTERED, + ALIGN_FROM_END, + ALIGN_MAX, + }; + + virtual ~PathPhysicsTool3D() { + if (shape.is_valid()) { + if (shape->is_connected("changed", callable_mp(_self(), static_cast(&PathPhysicsTool3D::_on_shape_changed)))) { + shape->disconnect("changed", callable_mp(_self(), static_cast(&PathPhysicsTool3D::_on_shape_changed))); + } + shape.unref(); + } + } + + void set_shape(const Ref &p_shape) { + if (shape != p_shape) { + if (shape.is_valid()) { + if (shape->is_connected("changed", callable_mp(_self(), static_cast(&PathPhysicsTool3D::_on_shape_changed)))) { + shape->disconnect("changed", callable_mp(_self(), static_cast(&PathPhysicsTool3D::_on_shape_changed))); + } + } + + shape = p_shape; + + if (shape.is_valid()) { + shape->connect("changed", callable_mp(_self(), static_cast(&PathPhysicsTool3D::_on_shape_changed))); + } + + _on_shape_changed(); + } + } + + Ref get_shape() const { + return shape; + } + + void set_distribution(Distribution p_distribution) { + if (distribution != p_distribution) { + distribution = p_distribution; + _self()->queue_rebuild(); + _self()->notify_property_list_changed(); + } + } + + auto get_distribution() const -> Distribution { + return distribution; + } + + void set_alignment(Alignment p_alignment) { + if (alignment != p_alignment) { + alignment = p_alignment; + _self()->queue_rebuild(); + } + } + + auto get_alignment() const -> Alignment{ + return alignment; + } + + void set_count(uint64_t p_count) { + if (count != p_count) { + count = p_count; + if (distribution == DISTRIBUTE_BY_COUNT) { + _self()->queue_rebuild(); + } + } + } + + uint64_t get_count() const { + return count; + } + + void set_distance(double p_distance) { + if (distance != p_distance) { + distance = p_distance; + if (distribution == DISTRIBUTE_BY_DISTANCE) { + _self()->queue_rebuild(); + } + } + } + + double get_distance() const { + return distance; + } + + void set_rotation_mode(Rotation p_rotation_mode) { + if (rotation_mode != p_rotation_mode) { + rotation_mode = p_rotation_mode; + _self()->queue_rebuild(); + } + } + + auto get_rotation_mode() const -> Rotation { + return rotation_mode; + } + + void set_rotation(const Vector3 &p_rotation) { + if (rotation != p_rotation) { + rotation = p_rotation; + _self()->queue_rebuild(); + } + } + + Vector3 get_rotation() const { + return rotation; + } + + void set_sample_cubic(bool p_cubic) { + if (sample_cubic != p_cubic) { + sample_cubic = p_cubic; + _self()->queue_rebuild(); + } + } + + bool get_sample_cubic() const { + return sample_cubic; + } + +protected: + static void _bind_path_physics_tool_3d_methods() { + ClassDB::bind_method(D_METHOD("set_shape", "shape"), static_cast&)>(&PathPhysicsTool3D::set_shape)); + ClassDB::bind_method(D_METHOD("get_shape"), static_cast (T::*)() const>(&PathPhysicsTool3D::get_shape)); + ClassDB::add_property(T::get_class_static(), PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape3D"), "set_shape", "get_shape"); + + ClassDB::bind_method(D_METHOD("set_distribution", "distribution"), static_cast(&PathPhysicsTool3D::set_distribution)); + ClassDB::bind_method(D_METHOD("get_distribution"), static_cast(&PathPhysicsTool3D::get_distribution)); + ClassDB::add_property(T::get_class_static(), PropertyInfo(Variant::INT, "distribution", PROPERTY_HINT_ENUM, "By Count,By Distance"), "set_distribution", "get_distribution"); + + ClassDB::bind_method(D_METHOD("set_alignment", "alignment"), static_cast(&PathPhysicsTool3D::set_alignment)); + ClassDB::bind_method(D_METHOD("get_alignment"), static_cast(&PathPhysicsTool3D::get_alignment)); + ClassDB::add_property(T::get_class_static(), PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "From Start,Centered,From End"), "set_alignment", "get_alignment"); + + ClassDB::bind_method(D_METHOD("set_count", "count"), static_cast(&PathPhysicsTool3D::set_count)); + ClassDB::bind_method(D_METHOD("get_count"), static_cast(&PathPhysicsTool3D::get_count)); + ClassDB::add_property(T::get_class_static(), PropertyInfo(Variant::INT, "count", PROPERTY_HINT_RANGE, "0,1000,1,or_greater"), "set_count", "get_count"); + + ClassDB::bind_method(D_METHOD("set_distance", "distance"), static_cast(&PathPhysicsTool3D::set_distance)); + ClassDB::bind_method(D_METHOD("get_distance"), static_cast(&PathPhysicsTool3D::get_distance)); + ClassDB::add_property(T::get_class_static(), PropertyInfo(Variant::FLOAT, "distance", PROPERTY_HINT_RANGE, "0.01,1000.0,0.01,or_greater"), "set_distance", "get_distance"); + + ClassDB::bind_method(D_METHOD("set_rotation_mode", "rotation_mode"), static_cast(&PathPhysicsTool3D::set_rotation_mode)); + ClassDB::bind_method(D_METHOD("get_rotation_mode"), static_cast(&PathPhysicsTool3D::get_rotation_mode)); + ClassDB::add_property(T::get_class_static(), PropertyInfo(Variant::INT, "rotation_mode", PROPERTY_HINT_ENUM, "Fixed,Path"), "set_rotation_mode", "get_rotation_mode"); + + ClassDB::bind_method(D_METHOD("set_rotation", "rotation"), static_cast(&PathPhysicsTool3D::set_rotation)); + ClassDB::bind_method(D_METHOD("get_rotation"), static_cast(&PathPhysicsTool3D::get_rotation)); + ClassDB::add_property(T::get_class_static(), PropertyInfo(Variant::VECTOR3, "rotation"), "set_rotation", "get_rotation"); + + ClassDB::bind_method(D_METHOD("set_sample_cubic", "cubic"), static_cast(&PathPhysicsTool3D::set_sample_cubic)); + ClassDB::bind_method(D_METHOD("get_sample_cubic"), static_cast(&PathPhysicsTool3D::get_sample_cubic)); + ClassDB::add_property(T::get_class_static(), PropertyInfo(Variant::BOOL, "sample_cubic"), "set_sample_cubic", "get_sample_cubic"); + + ClassDB::add_signal(T::get_class_static(), MethodInfo("shape_changed")); + + ClassDB::bind_integer_constant(T::get_class_static(), "Distribution", "DISTRIBUTE_BY_COUNT", DISTRIBUTE_BY_COUNT); + ClassDB::bind_integer_constant(T::get_class_static(), "Distribution", "DISTRIBUTE_BY_DISTANCE", DISTRIBUTE_BY_DISTANCE); + ClassDB::bind_integer_constant(T::get_class_static(), "Alignment", "ALIGN_FROM_START", ALIGN_FROM_START); + ClassDB::bind_integer_constant(T::get_class_static(), "Alignment", "ALIGN_CENTERED", ALIGN_CENTERED); + ClassDB::bind_integer_constant(T::get_class_static(), "Alignment", "ALIGN_FROM_END", ALIGN_FROM_END); + ClassDB::bind_integer_constant(T::get_class_static(), "Rotation", "ROTATE_FIXED", ROTATE_FIXED); + ClassDB::bind_integer_constant(T::get_class_static(), "Rotation", "ROTATE_PATH", ROTATE_PATH); + } + + void _validate_path_physics_tool_3d_property(PropertyInfo &p_property) const { + if (p_property.name == StringName("count") && get_distribution() == DISTRIBUTE_BY_DISTANCE) { + p_property.usage = PROPERTY_USAGE_NONE; + } else if (p_property.name == StringName("distance") && get_distribution() == DISTRIBUTE_BY_COUNT) { + p_property.usage = PROPERTY_USAGE_NONE; + } + } + + T *_bake() const { + T *out = memnew(T); + Ref new_shape = shape->duplicate(); + + for (const uint32_t owner_id : owners) { + CollisionShape3D *new_obj = memnew(CollisionShape3D); + new_obj->set_shape(new_shape); + out->add_child(new_obj); + new_obj->set_transform(_self()->shape_owner_get_transform(owner_id)); + if (out->get_owner() != nullptr) { + new_obj->set_owner(out->get_owner()); + } + } + + return out; + } + + void _rebuild_shape() { + for (const uint32_t owner_id : owners) { + _self()->shape_owner_clear_shapes(owner_id); + _self()->remove_shape_owner(owner_id); + } + owners.clear(); + + MultiMeshInstance3D *collision_debug = _get_collision_debug(); + if (collision_debug != nullptr) { + _self()->remove_child(collision_debug); + collision_debug->queue_free(); + collision_debug_id = 0; + } + + Path3D *path3d = _self()->get_path_3d(); + if (path3d == nullptr || path3d->get_curve().is_null() || !path3d->is_inside_tree() || !shape.is_valid()) { + return; + } + + Transform3D mod_transform = _self()->_get_final_transform(); + + Ref curve = path3d->get_curve(); + if (curve->get_point_count() < 2) { + return; + } + + double baked_l = curve->get_baked_length(); + if (baked_l == 0.0) { + return; + } + + uint64_t n_instances = 0; + double separation = 0.0; + switch (distribution) { + case DISTRIBUTE_BY_COUNT: { + n_instances = count; + separation = baked_l / (count - 1); + } break; + case DISTRIBUTE_BY_DISTANCE: { + separation = distance; + n_instances = Math::floor(baked_l / separation) + 1; + } break; + default: + ERR_FAIL(); + } + + double offset = 0.0; + if (distribution != DISTRIBUTE_BY_COUNT) { + switch (alignment) { + case ALIGN_FROM_START: { + offset = 0.0; + } break; + case ALIGN_CENTERED: { + offset = (baked_l - separation * (n_instances - 1)) * 0.5; + } break; + case ALIGN_FROM_END: { + offset = baked_l - separation * (n_instances - 1); + } break; + default: + ERR_FAIL(); + } + } + + bool debugging = Engine::get_singleton()->is_editor_hint() || _self()->get_tree()->is_debugging_collisions_hint(); + + if (debugging) { + MultiMeshInstance3D *collision_debug = memnew(MultiMeshInstance3D); + collision_debug_id = collision_debug->get_instance_id(); + collision_debug->set_multimesh(memnew(MultiMesh)); + collision_debug->get_multimesh()->set_transform_format(MultiMesh::TRANSFORM_3D); + collision_debug->get_multimesh()->set_instance_count(n_instances); + collision_debug->get_multimesh()->set_mesh(shape->get_debug_mesh()); + _self()->add_child(collision_debug, false, _self()->INTERNAL_MODE_BACK); + } + + owners.resize(n_instances); + for (uint64_t i = 0; i < n_instances; ++i) { + Transform3D transform; + switch (rotation_mode) { + case ROTATE_FIXED: { + transform.origin = curve->sample_baked(offset, sample_cubic); + transform.basis = Basis::from_euler(rotation); + } break; + case ROTATE_PATH: { + transform = curve->sample_baked_with_rotation(offset, sample_cubic, true); + transform.basis.rotate(rotation); + } break; + default: + ERR_FAIL(); + } + + transform *= _self()->_sample_3d_modifiers_at(offset / baked_l); + + if (_self()->_get_relative_transform() == _self()->TRANSFORM_PATH_NODE) { + transform = mod_transform * transform; + } + + if (debugging) { + MultiMeshInstance3D *collision_debug = _get_collision_debug(); + if (collision_debug != nullptr) { + collision_debug->get_multimesh()->set_instance_transform(i, transform); + } + } + + uint32_t owner_id = _self()->create_shape_owner(_self()); + _self()->shape_owner_add_shape(owner_id, shape); + _self()->shape_owner_set_transform(owner_id, transform); + owners[i] = owner_id; + offset += separation; + } + } + + void _on_shape_changed() { + _self()->queue_rebuild(); + _self()->emit_signal("shape_changed"); + } + +private: + Ref shape; + Distribution distribution = DISTRIBUTE_BY_COUNT; + Alignment alignment = ALIGN_FROM_START; + uint64_t count = 1; + double distance = 1.0; + Rotation rotation_mode = ROTATE_FIXED; + Vector3 rotation = Vector3(); + bool sample_cubic = false; + + LocalVector owners; + uint64_t collision_debug_id = 0; + + _FORCE_INLINE_ T *_self() { + return static_cast(this); + } + _FORCE_INLINE_ const T *_self() const { + return static_cast(this); + } + + _FORCE_INLINE_ MultiMeshInstance3D *_get_collision_debug() const { + return Object::cast_to(ObjectDB::get_instance(collision_debug_id)); + } +}; + +} \ No newline at end of file diff --git a/addons/PathMesh3D/src/path_scene_3d.cpp b/addons/PathMesh3D/src/path_scene_3d.cpp index 8ab3225..5fdd91f 100644 --- a/addons/PathMesh3D/src/path_scene_3d.cpp +++ b/addons/PathMesh3D/src/path_scene_3d.cpp @@ -116,7 +116,7 @@ TypedArray PathScene3D::bake_instances() { } void PathScene3D::_bind_methods() { - PATH_TOOL_BINDS(PathScene3D, scene, SCENE) + PathTool3D::_bind_path_tool_3d_methods(); ClassDB::bind_method(D_METHOD("bake_instances"), &PathScene3D::bake_instances); @@ -168,18 +168,7 @@ void PathScene3D::_bind_methods() { } void PathScene3D::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_READY: { - set_process_internal(true); - _rebuild_mesh(); - } break; - - case NOTIFICATION_INTERNAL_PROCESS: { - if (_pop_is_dirty()) { - _rebuild_mesh(); - } - } break; - } + PathTool3D::_notification_path_tool_3d(p_what); } PackedStringArray PathScene3D::_get_configuration_warnings() const { @@ -226,6 +215,7 @@ void PathScene3D::_rebuild_mesh() { } instances.clear(); + Path3D *path3d = get_path_3d(); if (path3d == nullptr || path3d->get_curve().is_null() || !path3d->is_inside_tree() || scene.is_null()) { return; } @@ -239,7 +229,7 @@ void PathScene3D::_rebuild_mesh() { } tmp->queue_free(); - Transform3D mod_transform = _get_relative_transform(); + Transform3D mod_transform = _get_final_transform(); Ref curve = path3d->get_curve(); if (curve->get_point_count() < 2) { @@ -305,7 +295,7 @@ void PathScene3D::_rebuild_mesh() { transform *= _sample_3d_modifiers_at(offset / baked_l); - if (relative_transform == TRANSFORM_SCENE_PATH_NODE) { + if (get_relative_transform() == TRANSFORM_PATH_NODE) { transform = mod_transform * transform; } @@ -328,8 +318,6 @@ PathScene3D::~PathScene3D() { } scene.unref(); } - - PATH_TOOL_DESTRUCTOR(PathScene3D); instances.clear(); } \ No newline at end of file diff --git a/addons/PathMesh3D/src/path_scene_3d.hpp b/addons/PathMesh3D/src/path_scene_3d.hpp index c5128bc..dbccebf 100644 --- a/addons/PathMesh3D/src/path_scene_3d.hpp +++ b/addons/PathMesh3D/src/path_scene_3d.hpp @@ -3,13 +3,12 @@ #include #include -#include "path_tool.hpp" +#include "path_tool_3d.hpp" namespace godot { -class PathScene3D : public Node3D { +class PathScene3D : public Node3D, public PathTool3D { GDCLASS(PathScene3D, Node3D) - PATH_TOOL(PathScene3D, SCENE) public: enum Distribution { @@ -63,6 +62,9 @@ class PathScene3D : public Node3D { protected: static void _bind_methods(); void _validate_property(PropertyInfo &property) const; + void _notification(int p_what); + + virtual void _rebuild_mesh() override final; private: Ref scene; diff --git a/addons/PathMesh3D/src/path_static_body_3d.cpp b/addons/PathMesh3D/src/path_static_body_3d.cpp index 5a50113..24f4fa6 100644 --- a/addons/PathMesh3D/src/path_static_body_3d.cpp +++ b/addons/PathMesh3D/src/path_static_body_3d.cpp @@ -9,5 +9,24 @@ #include #include "path_static_body_3d.hpp" +#include "path_physics_tool_3d.hpp" +#include "path_tool_3d.hpp" -PATH_COLLISION_TOOL_IMPLEMENTATION(PathStaticBody3D, StaticBody3D, static_body) \ No newline at end of file +using namespace godot; + +void PathStaticBody3D::_bind_methods() { + PathTool3D::_bind_path_tool_3d_methods(); + PathPhysicsTool3D::_bind_path_physics_tool_3d_methods(); +} + +void PathStaticBody3D::_notification(int p_what) { + PathTool3D::_notification_path_tool_3d(p_what); +} + +void PathStaticBody3D::_validate_property(PropertyInfo &p_property) const { + PathPhysicsTool3D::_validate_path_physics_tool_3d_property(p_property); +} + +void PathStaticBody3D::_rebuild_mesh() { + PathPhysicsTool3D::_rebuild_shape(); +} \ No newline at end of file diff --git a/addons/PathMesh3D/src/path_static_body_3d.hpp b/addons/PathMesh3D/src/path_static_body_3d.hpp index 5017078..9aefc67 100644 --- a/addons/PathMesh3D/src/path_static_body_3d.hpp +++ b/addons/PathMesh3D/src/path_static_body_3d.hpp @@ -2,17 +2,26 @@ #include -#include "path_tool.hpp" -#include "path_collision_tool.hpp" +#include "path_tool_3d.hpp" +#include "path_physics_tool_3d.hpp" namespace godot { class MultiMeshInstance3D; -class PathStaticBody3D : public StaticBody3D { +class PathStaticBody3D : public StaticBody3D, public PathPhysicsTool3D { GDCLASS(PathStaticBody3D, StaticBody3D) - PATH_TOOL(PathStaticBody3D, SHAPE) - PATH_COLLISION_TOOL_HEADER(PathStaticBody3D, StaticBody3D, static_body) + +public: + PathStaticBody3D *_bake_static_body() const { + return _bake(); + } + +protected: + static void _bind_methods(); + void _notification(int p_what); + void _validate_property(PropertyInfo &p_property) const; + virtual void _rebuild_mesh() override final; }; } diff --git a/addons/PathMesh3D/src/path_tool.hpp b/addons/PathMesh3D/src/path_tool.hpp deleted file mode 100644 index b6c7598..0000000 --- a/addons/PathMesh3D/src/path_tool.hpp +++ /dev/null @@ -1,199 +0,0 @@ -#pragma once - -#include -#include - -#include "path_modifier_3d.hpp" - -#define PATH_TOOL(m_class, m_type) \ -public: \ - enum RelativeTransform { \ - TRANSFORM_##m_type##_LOCAL, \ - TRANSFORM_##m_type##_PATH_NODE, \ - TRANSFOMR_##m_type##_MAX, \ - }; \ - \ - _FORCE_INLINE_ void set_path_3d(Path3D *p_path) { \ - if (p_path != path3d) { \ - if (path3d != nullptr && path3d->is_connected("curve_changed", callable_mp(this, &m_class::_on_curve_changed))) { \ - path3d->disconnect("curve_changed", callable_mp(this, &m_class::_on_curve_changed)); \ - } \ - \ - path3d = p_path; \ - \ - if (path3d != nullptr && !path3d->is_connected("curve_changed", callable_mp(this, &m_class::_on_curve_changed))) { \ - path3d->connect("curve_changed", callable_mp(this, &m_class::_on_curve_changed)); \ - } \ - \ - _on_curve_changed(); \ - } \ - } \ - \ - _FORCE_INLINE_ Path3D *get_path_3d() const { return path3d; } \ - \ - _FORCE_INLINE_ void set_relative_transform(const RelativeTransform p_transform) { \ - if (relative_transform != p_transform) { \ - relative_transform = p_transform; \ - queue_rebuild(); \ - } \ - } \ - \ - _FORCE_INLINE_ RelativeTransform get_relative_transform() const { return relative_transform; } \ - \ - _FORCE_INLINE_ void register_modifier(PathModifier3D *p_modifier) { \ - if (!modifiers.has(p_modifier)) { \ - modifiers.push_back(p_modifier); \ - queue_rebuild(); \ - } \ - } \ - \ - _FORCE_INLINE_ void unregister_modifier(PathModifier3D *p_modifier) { \ - if (modifiers.has(p_modifier)) { \ - modifiers.erase(p_modifier); \ - queue_rebuild(); \ - } \ - } \ - \ - _FORCE_INLINE_ void queue_rebuild() { dirty = true; } \ - \ -protected: \ - void _notification(int p_what); \ - \ -private: \ - Path3D *path3d = nullptr; \ - RelativeTransform relative_transform = TRANSFORM_##m_type##_LOCAL; \ - Transform3D self_transform; \ - Transform3D path_transform; \ - LocalVector modifiers; \ - bool dirty = true; \ - \ - _FORCE_INLINE_ bool _pop_is_dirty() { \ - bool is_dirty = dirty; \ - if (!is_dirty && relative_transform == TRANSFORM_##m_type##_PATH_NODE) { \ - Transform3D tmp = get_global_transform(); \ - is_dirty = is_dirty || self_transform != tmp; \ - self_transform = tmp; \ - if (path3d != nullptr) { \ - tmp = path3d->get_global_transform(); \ - is_dirty = is_dirty || path_transform != tmp; \ - path_transform = tmp; \ - } \ - } \ - \ - for (PathModifier3D *modifier : modifiers) { \ - is_dirty = is_dirty || modifier->pop_is_dirty(); \ - } \ - \ - dirty = false; \ - return is_dirty; \ - } \ - \ - _FORCE_INLINE_ Transform3D _get_relative_transform() const { \ - return self_transform.affine_inverse() * path_transform; \ - } \ - \ - _FORCE_INLINE_ Transform3D _sample_3d_modifiers_at(real_t p_offset_ratio) const { \ - Transform3D out; \ - for (PathModifier3D *modifier : modifiers) { \ - out *= modifier->sample_3d_modifier_at(p_offset_ratio); \ - } \ - return out; \ - }\ - \ - _FORCE_INLINE_ Transform2D _sample_uv_modifiers_at(real_t p_offset_ratio) const { \ - Transform2D out; \ - for (PathModifier3D *modifier : modifiers) { \ - out *= modifier->sample_uv_modifier_at(p_offset_ratio); \ - } \ - return out; \ - } \ - \ - void _rebuild_mesh(); \ - \ - _FORCE_INLINE_ void _on_curve_changed() { \ - queue_rebuild(); \ - emit_signal("curve_changed"); \ - } - -#define PATH_TOOL_BINDS(m_class, m_type_lower, m_type_upper) \ - ClassDB::bind_method(D_METHOD("queue_rebuild"), &m_class::queue_rebuild); \ - \ - ClassDB::bind_method(D_METHOD("set_path_3d", "path"), &m_class::set_path_3d); \ - ClassDB::bind_method(D_METHOD("get_path_3d"), &m_class::get_path_3d); \ - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "path_3d", PROPERTY_HINT_NODE_TYPE, "Path3D"), "set_path_3d", "get_path_3d"); \ - \ - ClassDB::bind_method(D_METHOD("set_" #m_type_lower "_transform", "transform"), &m_class::set_relative_transform); \ - ClassDB::bind_method(D_METHOD("get_" #m_type_lower "_transform"), &m_class::get_relative_transform); \ - ADD_PROPERTY(PropertyInfo(Variant::INT, #m_type_lower "_transform", PROPERTY_HINT_ENUM, "Transform to Self,Transform to Path Node"), "set_" #m_type_lower "_transform", "get_" #m_type_lower "_transform"); \ - \ - ADD_SIGNAL(MethodInfo("curve_changed")); \ - \ - BIND_ENUM_CONSTANT(TRANSFORM_##m_type_upper##_LOCAL); \ - BIND_ENUM_CONSTANT(TRANSFORM_##m_type_upper##_PATH_NODE); - -#define PATH_TOOL_DESTRUCTOR(m_class) \ - if (path3d != nullptr) { \ - if (UtilityFunctions::is_instance_id_valid(path3d->get_instance_id()) && \ - path3d->is_connected("curve_changed", callable_mp(this, &m_class::_on_curve_changed))) { \ - path3d->disconnect("curve_changed", callable_mp(this, &m_class::_on_curve_changed)); \ - } \ - } \ - path3d = nullptr; - - - -namespace godot { - - /* HELPER CLASSES */ -class PathToolInterface { -public: - virtual Path3D *get_path_3d() const = 0; - virtual Ref get_curve_3d() const = 0; - - virtual void register_modifier(PathModifier3D *p_modifier) = 0; - virtual void unregister_modifier(PathModifier3D *p_modifier) = 0; -}; - -template -class PathToolWrapper : public PathToolInterface { - -public: - PathToolWrapper(T* p_tool) : tool(p_tool) {} - ~PathToolWrapper() { - tool = nullptr; - } - - Path3D *get_path_3d() const override { - if (tool != nullptr) { - return tool->get_path_3d(); - } else { - return nullptr; - } - } - - Ref get_curve_3d() const override { - Path3D *path3d = get_path_3d(); - if (path3d != nullptr) { - return path3d->get_curve(); - } else { - return nullptr; - } - } - - void register_modifier(PathModifier3D *p_modifier) override { - if (tool != nullptr) { - tool->register_modifier(p_modifier); - } - } - - void unregister_modifier(PathModifier3D *p_modifier) override { - if (tool != nullptr) { - tool->unregister_modifier(p_modifier); - } - } - -private: - T* tool = nullptr; -}; - -} \ No newline at end of file diff --git a/addons/PathMesh3D/src/path_tool_3d.hpp b/addons/PathMesh3D/src/path_tool_3d.hpp new file mode 100644 index 0000000..3dd9351 --- /dev/null +++ b/addons/PathMesh3D/src/path_tool_3d.hpp @@ -0,0 +1,249 @@ +#pragma once + +#include +#include + +#include "godot_cpp/core/class_db.hpp" +#include "godot_cpp/core/defs.hpp" +#include "godot_cpp/core/error_macros.hpp" +#include "path_modifier_3d.hpp" + +namespace godot { + +template +class PathTool3D { + +public: + enum RelativeTransform { + TRANSFORM_LOCAL, + TRANSFORM_PATH_NODE, + TRANSFORM_MAX, + }; + + virtual ~PathTool3D() { + Path3D *path3d_node = get_path_3d(); + if (path3d_node != nullptr && path3d_node->is_connected("curve_changed", callable_mp(_self(), static_cast(&PathTool3D::_on_curve_changed)))) { + path3d_node->disconnect("curve_changed", callable_mp(_self(), static_cast(&PathTool3D::_on_curve_changed))); + } + modifiers.clear(); + } + + _FORCE_INLINE_ void set_path_3d(Path3D *p_path) { + ERR_FAIL_NULL(p_path); + + if (p_path->get_instance_id() != path3d_id) { + Path3D *old_node = get_path_3d(); + if (old_node != nullptr && old_node->is_connected("curve_changed", callable_mp(_self(), static_cast(&PathTool3D::_on_curve_changed)))) { + old_node->disconnect("curve_changed", callable_mp(_self(), static_cast(&PathTool3D::_on_curve_changed))); + } + + path3d_id = p_path->get_instance_id(); + + Path3D *new_node = get_path_3d(); + if (new_node != nullptr && !new_node->is_connected("curve_changed", callable_mp(_self(), static_cast(&PathTool3D::_on_curve_changed)))) { + new_node->connect("curve_changed", callable_mp(_self(), static_cast(&PathTool3D::_on_curve_changed))); + } + + _on_curve_changed(); + } + } + + _FORCE_INLINE_ Path3D *get_path_3d() const { + return Object::cast_to(ObjectDB::get_instance(path3d_id)); + } + + _FORCE_INLINE_ void set_relative_transform(const RelativeTransform p_transform) { + if (relative_transform != p_transform) { + relative_transform = p_transform; + queue_rebuild(); + } + } + + _FORCE_INLINE_ RelativeTransform get_relative_transform() const { + return relative_transform; + } + + _FORCE_INLINE_ void register_modifier(PathModifier3D *p_modifier) { + if (!modifiers.has(p_modifier)) { + modifiers.push_back(p_modifier); + queue_rebuild(); + } + } + + _FORCE_INLINE_ void unregister_modifier(PathModifier3D *p_modifier) { + if (modifiers.has(p_modifier)) { + modifiers.erase(p_modifier); + queue_rebuild(); + } + } + + _FORCE_INLINE_ void queue_rebuild() { + dirty = true; + } + +protected: + static void _bind_path_tool_3d_methods() { + ClassDB::bind_method(D_METHOD("queue_rebuild"), static_cast(&PathTool3D::queue_rebuild)); + + ClassDB::bind_method(D_METHOD("set_path_3d", "path"), static_cast(&PathTool3D::set_path_3d)); + ClassDB::bind_method(D_METHOD("get_path_3d"), static_cast(&PathTool3D::get_path_3d)); + ClassDB::add_property(T::get_class_static(), PropertyInfo(Variant::NODE_PATH, "path_3d", PROPERTY_HINT_NODE_TYPE, "Path3D"), "set_path_3d", "get_path_3d"); + + ClassDB::bind_method(D_METHOD("set_relative_transform", "transform"), static_cast(&PathTool3D::set_relative_transform)); + ClassDB::bind_method(D_METHOD("get_relative_transform"), static_cast(&PathTool3D::get_relative_transform)); + ClassDB::add_property(T::get_class_static(), PropertyInfo(Variant::INT, "relative_transform", PROPERTY_HINT_ENUM, "Transform to Self,Transform to Path Node"), "set_relative_transform", "get_relative_transform"); + + ClassDB::add_signal(T::get_class_static(), MethodInfo("curve_changed")); + + ClassDB::bind_integer_constant(T::get_class_static(), "RelativeTransform", "TRANSFORM_LOCAL", TRANSFORM_LOCAL); + ClassDB::bind_integer_constant(T::get_class_static(), "RelativeTransform", "TRANSFORM_PATH_NODE", TRANSFORM_PATH_NODE); + } + + void _notification_path_tool_3d(int p_what) { + switch (p_what) { + case Node::NOTIFICATION_READY: { + _self()->set_process_internal(true); + _rebuild_mesh(); + } break; + + case Node::NOTIFICATION_INTERNAL_PROCESS: { + if (_pop_should_rebuild()) { + _rebuild_mesh(); + } + } break; + } + } + + virtual void _rebuild_mesh() = 0; + + virtual bool _pop_should_rebuild() { + return _pop_is_dirty(); + } + + _FORCE_INLINE_ bool _pop_is_dirty() { + bool is_dirty = dirty; + if (!is_dirty && relative_transform == TRANSFORM_PATH_NODE) { + Transform3D tmp = _self()->get_global_transform(); + is_dirty = is_dirty || self_transform != tmp; + self_transform = tmp; + Path3D *path3d_node = get_path_3d(); + if (path3d_node != nullptr) { + tmp = path3d_node->get_global_transform(); + is_dirty = is_dirty || path_transform != tmp; + path_transform = tmp; + } + } + + for (PathModifier3D *modifier : modifiers) { + is_dirty = is_dirty || modifier->pop_is_dirty(); + } + + dirty = false; + return is_dirty; + } + + _FORCE_INLINE_ RelativeTransform _get_relative_transform() const { + return relative_transform; + } + + _FORCE_INLINE_ Transform3D _get_final_transform() const { + return self_transform.affine_inverse() * path_transform; + } + + _FORCE_INLINE_ Transform3D _sample_3d_modifiers_at(real_t p_offset_ratio) const { + Transform3D out; + for (PathModifier3D *modifier : modifiers) { + out *= modifier->sample_3d_modifier_at(p_offset_ratio); + } + return out; + } + + _FORCE_INLINE_ Transform2D _sample_uv_modifiers_at(real_t p_offset_ratio) const { + Transform2D out; + for (PathModifier3D *modifier : modifiers) { + out *= modifier->sample_uv_modifier_at(p_offset_ratio); + } + return out; + } + +private: + uint32_t path3d_id = 0; + RelativeTransform relative_transform = TRANSFORM_LOCAL; + Transform3D self_transform; + Transform3D path_transform; + LocalVector modifiers; + bool dirty = true; + +private: + _FORCE_INLINE_ void _on_curve_changed() { + queue_rebuild(); + _self()->emit_signal("curve_changed"); + } + + _FORCE_INLINE_ T *_self() { + return static_cast(this); + } + _FORCE_INLINE_ const T *_self() const { + return static_cast(this); + } +}; + +} + + +namespace godot { + + /* HELPER CLASSES */ +class PathToolInterface { +public: + virtual Path3D *get_path_3d() const = 0; + virtual Ref get_curve_3d() const = 0; + + virtual void register_modifier(PathModifier3D *p_modifier) = 0; + virtual void unregister_modifier(PathModifier3D *p_modifier) = 0; +}; + +template +class PathToolWrapper : public PathToolInterface { + +public: + PathToolWrapper(T* p_tool) : tool(p_tool) { + } + ~PathToolWrapper() { + tool = nullptr; + } + + Path3D *get_path_3d() const override { + if (tool != nullptr) { + return tool->get_path_3d(); + } else { + return nullptr; + } + } + + Ref get_curve_3d() const override { + Path3D *path3d_node = get_path_3d(); + if (path3d_node != nullptr) { + return path3d_node->get_curve(); + } else { + return nullptr; + } + } + + void register_modifier(PathModifier3D *p_modifier) override { + if (tool != nullptr) { + tool->register_modifier(p_modifier); + } + } + + void unregister_modifier(PathModifier3D *p_modifier) override { + if (tool != nullptr) { + tool->unregister_modifier(p_modifier); + } + } + +private: + T* tool = nullptr; +}; + +} \ No newline at end of file diff --git a/doc_classes/PathArea3D.xml b/doc_classes/PathArea3D.xml index 4895e60..1906a93 100644 --- a/doc_classes/PathArea3D.xml +++ b/doc_classes/PathArea3D.xml @@ -9,12 +9,6 @@ - - - - Returns an [Area3D] with the same geometry generated by this node. - - @@ -40,6 +34,10 @@ The path to duplicate the shape along. + + Determines how the shapes will be transformed relative to this node or the path node. + See [enum RelativeTransform] for more information. + Determines the rotation of the shape along the path. @@ -53,10 +51,6 @@ The collision shape resource to duplicate along the path. - - Determines how the shapes will be transformed relative to this node or the path node. - See [enum RelativeTransform] for more information. - @@ -71,11 +65,11 @@ - - When [member shape_transform] is set to this value, the shape will be transformed to match to this node's transform. + + The shapes will be transformed relative to the PathArea3D node. - - When [member shape_transform] is set to this value, the shape will be transformed to match to the path node's transform. + + The shapes will be transformed relative to the Path3D node. When [member distribution] is set to this value, the shapes will be spaced evenly based on the [member count]. diff --git a/doc_classes/PathExtrude3D.xml b/doc_classes/PathExtrude3D.xml index 0e6087a..cc94522 100644 --- a/doc_classes/PathExtrude3D.xml +++ b/doc_classes/PathExtrude3D.xml @@ -75,9 +75,6 @@ The material used for the extrusion. Similar to [CSGPolygon3D], the bttom left of the texture is applied to the starting end cap, and the bottom right of the texture is applied to the end cap. The top half of the texture is applied to the extruded profile, with U increasing along the path length. - - Sets the overall transform of the generated mesh to either follow the PathExtrude3D node's transform or the transform of the Path3D node. - A spatial offset in the X any Y direction along the path. @@ -90,6 +87,10 @@ The 2D profile to extrude along the path. + + Determines how the cross-section is transformed relative to this node or the path node. + See [enum RelativeTransform] for more information. + If [code]true[/code], the cross-section is sampled using cubic interpolation. If [code]false[/code], the cross-section is sampled using linear interpolation. @@ -119,11 +120,11 @@ - - When [member mesh_transform] is set to this value, the mesh is transformed using the PathExtrude3D node's transform. + + The cross-section is transformed relative to the PathExtrude3D node. - - When [member mesh_transform] is set to this value, the mesh is transformed using the Path3D node's transform. + + The cross-section is transformed relative to the Path3D node. When [member collision_mode] is set to this value, no collision is automatically generated. [method create_trimesh_collision], [method create_convex_collision], or [method create_multiple_convex_collision] can be used to manually generate collision. diff --git a/doc_classes/PathMesh3D.xml b/doc_classes/PathMesh3D.xml index b5517c5..7f45581 100644 --- a/doc_classes/PathMesh3D.xml +++ b/doc_classes/PathMesh3D.xml @@ -221,12 +221,13 @@ The input mesh whose surfaces will be tiled along the path. - - Sets the overall transform of the generated mesh to either follow the PathMesh3D node's transform or the transform of the Path3D node. - The Path3D node that the input mesh will be tiled along. + + Determines how the mesh is transformed relative to this node or the path node. + See [enum RelativeTransform] for more information. + A read-only value that stores the triangle count of the generated mesh. @@ -244,11 +245,11 @@ - - When the [member mesh_transform] property is set to this value, the mesh is transformed using the PathMesh3D node's transform. + + The mesh will be transformed relative to the PathMesh3D node. - - When the [member mesh_transform] property is set to this value, the mesh is transformed using the Path3D node's transform. + + The mesh will be transformed relative to the Path3D node. If [member collision_mode] is set to this value, no collision is generated. diff --git a/doc_classes/PathMultiMesh3D.xml b/doc_classes/PathMultiMesh3D.xml index 91c759f..6ac3300 100644 --- a/doc_classes/PathMultiMesh3D.xml +++ b/doc_classes/PathMultiMesh3D.xml @@ -29,15 +29,16 @@ Sets the method in which instances are spaced along the path. - - Sets the transform of the generated mesh to be relative to the Path3D node or the transform of the PathMultiMesh3D node. - The MultiMesh resource to instance along the path. The Path3D node used to instance the MultiMesh. + + Determines how the instances are transformed relative to this node or the path node. + See [enum RelativeTransform] for more information. + The rotation applied to each instance along the path. If [member rotation_mode] is set to [constant ROTATE_FIXED], this property is the Euler angle of all the instances relative to the PathMesh3D transform. If [member rotation_mode] is set to [constant ROTATE_PATH], this property is the Euler angle of each instance relative to the path. If [member rotation_mode] is set to [constant ROTATE_RANDOM], this property is ignored. @@ -61,11 +62,11 @@ - - When [member mesh_transform] is set to this value, the generated mesh is transformed relative to the PathMultiMesh3D node. + + The instances will be transformed relative to the PathMultiMesh3D node. - - When [member mesh_transform] is set to this value, the generated mesh is transformed relative to the Path3D node. + + The instances will be transformed relative to the Path3D node. When [member distribution] is set to this value, the instances are distributed by count. diff --git a/doc_classes/PathScene3D.xml b/doc_classes/PathScene3D.xml index 30ad488..52d4a7d 100644 --- a/doc_classes/PathScene3D.xml +++ b/doc_classes/PathScene3D.xml @@ -38,6 +38,10 @@ The Path3D node used to instance the MultiMesh. + + Determines how the instances are transformed relative to this node or the path node. + See [enum RelativeTransform] for more information. + The rotation applied to each instance along the path. If [member rotation_mode] is set to [constant ROTATE_FIXED], this property is the Euler angle of all the instances relative to the PathMesh3D transform. If [member rotation_mode] is set to [constant ROTATE_PATH], this property is the Euler angle of each instance relative to the path. If [member rotation_mode] is set to [constant ROTATE_RANDOM], this property is ignored. @@ -50,9 +54,6 @@ A [PackedScene] resource used to instance scenes along the path. - - Sets the transform relative to the path for the generated scenes. - @@ -67,11 +68,11 @@ - - When [member scene_transform] is set to this value, the generated scenes are transformed relative to the PathScene3D node. + + The instances will be transformed relative to the PathScene3D node. - - When [member scene_transform] is set to this value, the generated scenes are transformed relative to the Path3D node. + + The instances will be transformed relative to the Path3D node. When [member distribution] is set to this value, the instances are distributed by count. diff --git a/doc_classes/PathStaticBody3D.xml b/doc_classes/PathStaticBody3D.xml index 96726ca..53f30a4 100644 --- a/doc_classes/PathStaticBody3D.xml +++ b/doc_classes/PathStaticBody3D.xml @@ -9,12 +9,6 @@ - - - - Returns a [StaticBody3D] with the same geometry generated by this node. - - @@ -40,6 +34,10 @@ The path to duplicate the shape along. + + Determines how the shapes will be transformed relative to this node or the path node. + See [enum RelativeTransform] for more information. + Determines the rotation of the shape along the path. @@ -53,10 +51,6 @@ The collision shape resource to duplicate along the path. - - Determines how the shapes will be transformed relative to this node or the path node. - See [enum RelativeTransform] for more information. - @@ -71,11 +65,11 @@ - - When [member shape_transform] is set to this value, the shape will be transformed to match to this node's transform. + + The shapes will be transformed relative to the PathStaticBody3D node. - - When [member shape_transform] is set to this value, the shape will be transformed to match to the path node's transform. + + The shapes will be transformed relative to the Path3D node. When [member distribution] is set to this value, the shapes will be spaced evenly based on the [member count]. From fc98c09e2f85c107cee78248ac2bfd310daa1dc9 Mon Sep 17 00:00:00 2001 From: iiMidknightii <89699177+iiMidknightii@users.noreply.github.com> Date: Sat, 13 Dec 2025 19:12:51 -0800 Subject: [PATCH 2/5] Fixed various crashes and error messages --- .../PathMesh3D/src/path_collision_tool_3d.hpp | 48 ++++++++++++------- addons/PathMesh3D/src/path_extrude_3d.cpp | 17 +++++-- addons/PathMesh3D/src/path_mesh_3d.cpp | 13 +++-- addons/PathMesh3D/src/path_multimesh_3d.cpp | 1 - addons/PathMesh3D/src/path_tool_3d.hpp | 9 ++-- tests/test_pathextrude3d.tscn | 16 +++---- tests/test_pathmesh3d.tscn | 6 +-- 7 files changed, 67 insertions(+), 43 deletions(-) diff --git a/addons/PathMesh3D/src/path_collision_tool_3d.hpp b/addons/PathMesh3D/src/path_collision_tool_3d.hpp index 3880052..799384e 100644 --- a/addons/PathMesh3D/src/path_collision_tool_3d.hpp +++ b/addons/PathMesh3D/src/path_collision_tool_3d.hpp @@ -68,6 +68,10 @@ class PathCollisionTool3D : public PathTool3D { return Object::cast_to(ObjectDB::get_instance(collision_node_id)); } + _FORCE_INLINE_ Node *get_collision_debug_node() const { + return Object::cast_to(ObjectDB::get_instance(collision_debug_id)); + } + _FORCE_INLINE_ void queue_rebuild_collision() { collision_dirty = true; } @@ -146,8 +150,8 @@ class PathCollisionTool3D : public PathTool3D { protected: static void _bind_path_collision_tool_3d_methods() { ClassDB::add_property_group(T::get_class_static(), "Collision", ""); - ClassDB::bind_method(D_METHOD("set_generate_collision", "mode"), static_cast(&PathCollisionTool3D::set_generate_collision)); - ClassDB::bind_method(D_METHOD("get_generate_collision"), static_cast(&PathCollisionTool3D::get_generate_collision)); + ClassDB::bind_method(D_METHOD("set_generate_collision", "mode"), static_cast(&PathCollisionTool3D::set_generate_collision)); + ClassDB::bind_method(D_METHOD("get_generate_collision"), static_cast(&PathCollisionTool3D::get_generate_collision)); ClassDB::add_property(T::get_class_static(), PropertyInfo(Variant::INT, "collision_mode", PROPERTY_HINT_ENUM, "None,Trimesh,Convex,Multiple Convex"), "set_generate_collision", "get_generate_collision"); ClassDB::bind_method(D_METHOD("set_convex_collision_clean", "clean"), static_cast(&PathCollisionTool3D::set_convex_collision_clean)); @@ -209,26 +213,25 @@ class PathCollisionTool3D : public PathTool3D { _FORCE_INLINE_ void _clear_collision_node() { Node *collision_node = get_collision_node(); - if (collision_node != nullptr) { _self()->remove_child(collision_node); collision_node->queue_free(); collision_node = nullptr; + collision_node_id = 0; + } + + Node *collision_debug = get_collision_debug_node(); + if (collision_debug != nullptr) { + _self()->remove_child(collision_debug); + collision_debug->queue_free(); + collision_debug = nullptr; + collision_debug_id = 0; } } _FORCE_INLINE_ void _rebuild_collision_node() { _clear_collision_node(); - if (collision_debug_id != 0) { - MeshInstance3D *collision_debug = Object::cast_to(ObjectDB::get_instance(collision_debug_id)); - if (collision_debug != nullptr) { - _self()->remove_child(collision_debug); - collision_debug->queue_free(); - } - collision_debug_id = 0; - } - Node *collision_node = nullptr; switch (collision_mode) { @@ -250,11 +253,24 @@ class PathCollisionTool3D : public PathTool3D { _self()->add_child(collision_node, false, _self()->INTERNAL_MODE_BACK); - if (Engine::get_singleton()->is_editor_hint() || _self()->get_tree()->is_debugging_collisions_hint()) { + if (_self()->get_tree()->is_debugging_collisions_hint()) { MeshInstance3D *collision_debug = memnew(MeshInstance3D); - collision_debug->set_mesh(Object::cast_to(collision_node->get_child(0))->get_shape()->get_debug_mesh()); - _self()->add_child(collision_debug, false, _self()->INTERNAL_MODE_BACK); collision_debug_id = collision_debug->get_instance_id(); + + CollisionShape3D *cshape = Object::cast_to(collision_node->get_child(0)); + if (cshape != nullptr) { + Ref shape = cshape->get_shape(); + if (shape.is_valid()) { + collision_debug->set_mesh(shape->get_debug_mesh()); + _self()->add_child(collision_debug, false, _self()->INTERNAL_MODE_BACK); + } else { + collision_debug->queue_free(); + collision_debug_id = 0; + } + } else { + collision_debug->queue_free(); + collision_debug_id = 0; + } } } } @@ -264,7 +280,7 @@ class PathCollisionTool3D : public PathTool3D { bool convex_collision_clean = true; bool convex_collision_simplify = false; bool collision_dirty = false; - uint32_t collision_node_id = 0; + uint64_t collision_node_id = 0; uint64_t collision_debug_id = 0; uint32_t collision_layer = 1; uint32_t collision_mask = 1; diff --git a/addons/PathMesh3D/src/path_extrude_3d.cpp b/addons/PathMesh3D/src/path_extrude_3d.cpp index 0482f41..af31bd7 100644 --- a/addons/PathMesh3D/src/path_extrude_3d.cpp +++ b/addons/PathMesh3D/src/path_extrude_3d.cpp @@ -225,18 +225,25 @@ void PathExtrude3D::_rebuild_mesh() { PackedVector3Array tessellated_points = curve->tessellate(tessellation_max_stages, tessellation_tolerance_degrees); uint64_t n_slices = tessellated_points.size(); - ERR_FAIL_COND_MSG(n_slices < 2, "Not enough points on curve to tesselate."); + if (n_slices < 2) { + return; + } double baked_l = curve->get_baked_length(); - ERR_FAIL_COND_MSG(baked_l == 0.0, "Curve has no length."); + if (baked_l == 0.0) { + return; + } Array arrays = profile->get_mesh_arrays(); - ERR_FAIL_COND_MSG(arrays.size() == 0, "Mesh has no array data."); - ERR_FAIL_COND_MSG(arrays[0].get_type() != Variant::PACKED_VECTOR2_ARRAY, "First (and required) element of mesh array must be a PackedVector2Array."); + if (arrays.size() == 0 || arrays[0].get_type() != Variant::PACKED_VECTOR2_ARRAY) { + return; + } PackedVector2Array cross_section = arrays[0]; uint64_t n_vertices = cross_section.size(); - ERR_FAIL_COND_MSG(n_vertices < 2, "Number of vertices provided in cross section < 2."); + if (n_vertices < 2) { + return; + } LocalVector has_column; has_column.resize(Mesh::ARRAY_MAX); diff --git a/addons/PathMesh3D/src/path_mesh_3d.cpp b/addons/PathMesh3D/src/path_mesh_3d.cpp index 109fdc2..85110ae 100644 --- a/addons/PathMesh3D/src/path_mesh_3d.cpp +++ b/addons/PathMesh3D/src/path_mesh_3d.cpp @@ -427,14 +427,19 @@ void PathMesh3D::_rebuild_mesh() { Transform3D mod_transform = _get_final_transform(); Ref curve = path3d->get_curve(); - ERR_FAIL_COND_MSG(curve->get_point_count() < 2, "Curve has < 2 points, cannot tesselate."); + if (curve->get_point_count() < 2) { + return; + } double baked_l = curve->get_baked_length(); - ERR_FAIL_COND_MSG(baked_l == 0.0, "Curve has no length."); + if (baked_l == 0.0) { + return; + } double mesh_l = _get_mesh_length(); - ERR_FAIL_COND_MSG(mesh_l == 0.0, "Provided mesh has no length in Z dimension. Try rotating on X or Y to gain length."); - ERR_FAIL_COND_MSG(baked_l < mesh_l, "Curve length < mesh length, cannot tile."); + if (mesh_l == 0.0 || baked_l < mesh_l) { + return; + } for (uint64_t idx_surf = 0; idx_surf < source_mesh->get_surface_count(); ++idx_surf) { SurfaceData &surf = surfaces[idx_surf]; diff --git a/addons/PathMesh3D/src/path_multimesh_3d.cpp b/addons/PathMesh3D/src/path_multimesh_3d.cpp index 5956c21..88f4779 100644 --- a/addons/PathMesh3D/src/path_multimesh_3d.cpp +++ b/addons/PathMesh3D/src/path_multimesh_3d.cpp @@ -204,7 +204,6 @@ void PathMultiMesh3D::_rebuild_mesh() { } double baked_l = curve->get_baked_length(); - if (baked_l == 0.0) { return; } diff --git a/addons/PathMesh3D/src/path_tool_3d.hpp b/addons/PathMesh3D/src/path_tool_3d.hpp index 3dd9351..9b99301 100644 --- a/addons/PathMesh3D/src/path_tool_3d.hpp +++ b/addons/PathMesh3D/src/path_tool_3d.hpp @@ -39,9 +39,8 @@ class PathTool3D { path3d_id = p_path->get_instance_id(); - Path3D *new_node = get_path_3d(); - if (new_node != nullptr && !new_node->is_connected("curve_changed", callable_mp(_self(), static_cast(&PathTool3D::_on_curve_changed)))) { - new_node->connect("curve_changed", callable_mp(_self(), static_cast(&PathTool3D::_on_curve_changed))); + if (p_path != nullptr && !p_path->is_connected("curve_changed", callable_mp(_self(), static_cast(&PathTool3D::_on_curve_changed)))) { + p_path->connect("curve_changed", callable_mp(_self(), static_cast(&PathTool3D::_on_curve_changed))); } _on_curve_changed(); @@ -87,7 +86,7 @@ class PathTool3D { ClassDB::bind_method(D_METHOD("set_path_3d", "path"), static_cast(&PathTool3D::set_path_3d)); ClassDB::bind_method(D_METHOD("get_path_3d"), static_cast(&PathTool3D::get_path_3d)); - ClassDB::add_property(T::get_class_static(), PropertyInfo(Variant::NODE_PATH, "path_3d", PROPERTY_HINT_NODE_TYPE, "Path3D"), "set_path_3d", "get_path_3d"); + ClassDB::add_property(T::get_class_static(), PropertyInfo(Variant::OBJECT, "path_3d", PROPERTY_HINT_NODE_TYPE, "Path3D"), "set_path_3d", "get_path_3d"); ClassDB::bind_method(D_METHOD("set_relative_transform", "transform"), static_cast(&PathTool3D::set_relative_transform)); ClassDB::bind_method(D_METHOD("get_relative_transform"), static_cast(&PathTool3D::get_relative_transform)); @@ -167,7 +166,7 @@ class PathTool3D { } private: - uint32_t path3d_id = 0; + uint64_t path3d_id = 0; RelativeTransform relative_transform = TRANSFORM_LOCAL; Transform3D self_transform; Transform3D path_transform; diff --git a/tests/test_pathextrude3d.tscn b/tests/test_pathextrude3d.tscn index 0599f85..b56039f 100644 --- a/tests/test_pathextrude3d.tscn +++ b/tests/test_pathextrude3d.tscn @@ -2,9 +2,8 @@ [ext_resource type="Texture2D" uid="uid://bxldv7ij0s6uw" path="res://tests/test_uv.png" id="1_jtvri"] -[sub_resource type="PathExtrudeProfileCircle" id="PathExtrudeProfileCircle_yx5hd"] -radius = 0.52 -segments = 12 +[sub_resource type="PathExtrudeProfileCircle" id="PathExtrudeProfileCircle_2g0eo"] +radius = 0.5 [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_eywb8"] albedo_texture = ExtResource("1_jtvri") @@ -27,7 +26,7 @@ func _process(delta: float) -> void: " [sub_resource type="Curve" id="Curve_2g0eo"] -_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(0.753859, 0), -0.0378524, -0.0378524, 0, 0, Vector2(0.9494, 0.442167), 5.8086, 5.8086, 0, 0, Vector2(1, 1), 14.9377, 0.0, 0, 0] +_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(0.753859, 0), -0.0378524, -0.0378524, 0, 0, Vector2(0.90853137, 0.17918497), 2.600726, 2.600726, 0, 0, Vector2(1, 1), 14.9377, 0.0, 0, 0] point_count = 4 [sub_resource type="GDScript" id="GDScript_2g0eo"] @@ -48,21 +47,20 @@ point_count = 3 [node name="PathExtrude3D" type="PathExtrude3D" parent="." node_paths=PackedStringArray("path_3d")] path_3d = NodePath("../Path3D") -mesh_transform = 1 -profile = SubResource("PathExtrudeProfileCircle_yx5hd") +profile = SubResource("PathExtrudeProfileCircle_2g0eo") tessellation_tolerance_degrees = 0.5 material = SubResource("StandardMaterial3D_eywb8") transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.15287, -0.660449) script = SubResource("GDScript_x3lis") [node name="PathModifier3D" type="PathModifier3D" parent="PathExtrude3D"] -curve_offset_end = 11.4352 +curve_offset_end = 11.435217 influence = SubResource("Curve_2g0eo") scale_modifier = Vector3(0, 0, 0) [node name="PathModifier3D2" type="PathModifier3D" parent="PathExtrude3D"] -curve_offset_end = 11.4352 -uv_offset_modifier = Vector2(50.1095, 0) +curve_offset_end = 11.435217 +uv_offset_modifier = Vector2(66.195526, 0) script = SubResource("GDScript_2g0eo") [node name="Path3D" type="Path3D" parent="."] diff --git a/tests/test_pathmesh3d.tscn b/tests/test_pathmesh3d.tscn index a7a6521..9ca7d7a 100644 --- a/tests/test_pathmesh3d.tscn +++ b/tests/test_pathmesh3d.tscn @@ -23,7 +23,7 @@ point_count = 3 [sub_resource type="Curve3D" id="Curve3D_jw5u5"] _data = { -"points": PackedVector3Array(-0.992874, -0.672518, 2.01513, 0.993, 0, -2.015, 0, 0, 3, 0.481, 0, 0.862, -0.481, 0, -0.862, 0, 0, -3, 1.268, 0, -0.227, -1.268, 0, 0.227, -2.636, 0, -3.924, -3.83053, 1.01841, -2.45066, 3.83053, -1.01841, 2.45066, -2.84019, 0.0907836, 2.8903), +"points": PackedVector3Array(-0.992874, -0.672518, 2.01513, 0.993, 0, -2.015, 0, 0, 3, 0.481, 0, 0.862, -0.481, 0, -0.862, 0, 0, -3, 1.268, 0, -0.227, -1.268, 0, 0.227, -2.636, 0, -3.924, -3.83053, 1.01841, -2.45066, 3.83053, -1.01841, 2.45066, -2.9785013, 0.023592472, 3.1116865), "tilts": PackedFloat32Array(0, 0, 0, -2.19921) } point_count = 4 @@ -33,10 +33,10 @@ script = SubResource("GDScript_bfc6h") [node name="PathMesh3D" type="PathMesh3D" parent="." node_paths=PackedStringArray("path_3d")] path_3d = NodePath("../Path3D") -mesh = ExtResource("1_wgxgm") collision_mode = 1 collision_layer = 1 collision_mask = 1 +mesh = ExtResource("1_wgxgm") surface_0/tile_rotation = Vector3(0, 0, 0) surface_0/tile_rotation_order = 2 surface_0/distribution = 0 @@ -49,7 +49,7 @@ surface_0/offset = Vector2(0, 0) surface_0/triangle_count = 853 [node name="PathModifier3D" type="PathModifier3D" parent="PathMesh3D"] -curve_offset_end = 17.59416 +curve_offset_end = 17.808922 influence = SubResource("Curve_eedmf") [node name="Path3D" type="Path3D" parent="."] From faa4ad2d347700db1d7cb0ea63bb1cc3324a46b3 Mon Sep 17 00:00:00 2001 From: iiMidknightii <89699177+iiMidknightii@users.noreply.github.com> Date: Sat, 13 Dec 2025 19:13:09 -0800 Subject: [PATCH 3/5] Refactored path modifier and tool relationship --- addons/PathMesh3D/src/path_modifier_3d.cpp | 45 ++++-------- addons/PathMesh3D/src/path_modifier_3d.hpp | 3 +- addons/PathMesh3D/src/path_tool_3d.hpp | 80 ++++++---------------- 3 files changed, 38 insertions(+), 90 deletions(-) diff --git a/addons/PathMesh3D/src/path_modifier_3d.cpp b/addons/PathMesh3D/src/path_modifier_3d.cpp index a78287c..b7f9430 100644 --- a/addons/PathMesh3D/src/path_modifier_3d.cpp +++ b/addons/PathMesh3D/src/path_modifier_3d.cpp @@ -1,15 +1,11 @@ #include #include "path_tool_3d.hpp" -#include "path_modifier_3d.hpp" -#include "path_mesh_3d.hpp" -#include "path_extrude_3d.hpp" -#include "path_multimesh_3d.hpp" -#include "path_scene_3d.hpp" using namespace godot; void PathModifier3D::set_curve_offset_start(real_t p_offset) { + PathToolInterface* tool = _get_tool(); if (tool != nullptr) { const Ref &path_curve = tool->get_curve_3d(); if (path_curve.is_valid() && path_curve->get_baked_length() > 0.0) { @@ -21,6 +17,7 @@ void PathModifier3D::set_curve_offset_start(real_t p_offset) { real_t PathModifier3D::get_curve_offset_start() const { real_t r_value = 0.0; + PathToolInterface* tool = _get_tool(); if (tool != nullptr) { const Ref &path_curve = tool->get_curve_3d(); if (path_curve.is_valid()) { @@ -32,6 +29,7 @@ real_t PathModifier3D::get_curve_offset_start() const { } void PathModifier3D::set_curve_offset_end(real_t p_offset) { + PathToolInterface* tool = _get_tool(); if (tool != nullptr) { const Ref &path_curve = tool->get_curve_3d(); if (path_curve.is_valid() && path_curve->get_baked_length() > 0.0) { @@ -43,6 +41,7 @@ void PathModifier3D::set_curve_offset_end(real_t p_offset) { real_t PathModifier3D::get_curve_offset_end() const { real_t r_value = 0.0; + PathToolInterface* tool = _get_tool(); if (tool != nullptr) { const Ref &path_curve = tool->get_curve_3d(); if (path_curve.is_valid()) { @@ -373,35 +372,16 @@ void PathModifier3D::_bind_methods() { void PathModifier3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_POST_ENTER_TREE: { - if (tool != nullptr) { - memfree(tool); - tool = nullptr; - } - - #define WRAP_TOOL(m_type) \ - m_type *tmp_##m_type = Object::cast_to(parent); \ - if (tmp_##m_type != nullptr) { \ - tool = memnew(PathToolWrapper(tmp_##m_type)); \ - tool->register_modifier(this); \ - break; \ - } - - Node *parent = get_parent(); - while (parent != nullptr) { - WRAP_TOOL(PathMesh3D) - WRAP_TOOL(PathExtrude3D) - WRAP_TOOL(PathMultiMesh3D) - WRAP_TOOL(PathScene3D) - - parent = parent->get_parent(); + if (dynamic_cast(get_parent()) != nullptr) { + tool_interface_id = get_parent()->get_instance_id(); + _get_tool()->register_modifier(this); } } break; case NOTIFICATION_EXIT_TREE: { - if (tool != nullptr) { - tool->unregister_modifier(this); - memfree(tool); - tool = nullptr; + if (_get_tool() != nullptr) { + _get_tool()->unregister_modifier(this); + tool_interface_id = 0; } } } @@ -430,6 +410,7 @@ bool PathModifier3D::_property_get_revert(const StringName &p_name, Variant &r_p return false; } } else if (p_name == StringName("curve_offset_end")) { + PathToolInterface* tool = _get_tool(); if (tool != nullptr) { const Ref &path_curve = tool->get_curve_3d(); if (path_curve.is_valid() && path_curve->get_baked_length() > 0.0) { @@ -465,6 +446,10 @@ void PathModifier3D::_validate_property(PropertyInfo &p_property) const { } } +PathToolInterface* PathModifier3D::_get_tool() const { + return dynamic_cast(ObjectDB::get_instance(tool_interface_id)); +} + real_t PathModifier3D::_sample_influence(real_t p_offset_ratio) const { if (influence.is_valid()) { if (p_offset_ratio < curve_offset_ratio_start || p_offset_ratio > curve_offset_ratio_end) { diff --git a/addons/PathMesh3D/src/path_modifier_3d.hpp b/addons/PathMesh3D/src/path_modifier_3d.hpp index 53e5d34..bbdb0cc 100644 --- a/addons/PathMesh3D/src/path_modifier_3d.hpp +++ b/addons/PathMesh3D/src/path_modifier_3d.hpp @@ -99,8 +99,9 @@ class PathModifier3D : public Node3D { bool dirty = true; - PathToolInterface *tool = nullptr; + uint64_t tool_interface_id = 0; + PathToolInterface* _get_tool() const; real_t _sample_influence(real_t p_offset_ratio) const; void _on_influence_changed(); }; diff --git a/addons/PathMesh3D/src/path_tool_3d.hpp b/addons/PathMesh3D/src/path_tool_3d.hpp index 9b99301..cfaa6dd 100644 --- a/addons/PathMesh3D/src/path_tool_3d.hpp +++ b/addons/PathMesh3D/src/path_tool_3d.hpp @@ -3,6 +3,7 @@ #include #include +#include "godot_cpp/classes/curve3d.hpp" #include "godot_cpp/core/class_db.hpp" #include "godot_cpp/core/defs.hpp" #include "godot_cpp/core/error_macros.hpp" @@ -10,8 +11,27 @@ namespace godot { +class PathToolInterface { + +public: + virtual void set_path_3d(Path3D *p_path) = 0; + virtual Path3D *get_path_3d() const = 0; + + Ref get_curve_3d() const { + Path3D *path3d_node = get_path_3d(); + if (path3d_node != nullptr) { + return path3d_node->get_curve(); + } else { + return Ref(); + } + } + + virtual void register_modifier(PathModifier3D *p_modifier) = 0; + virtual void unregister_modifier(PathModifier3D *p_modifier) = 0; +}; + template -class PathTool3D { +class PathTool3D : public PathToolInterface { public: enum RelativeTransform { @@ -187,62 +207,4 @@ class PathTool3D { } }; -} - - -namespace godot { - - /* HELPER CLASSES */ -class PathToolInterface { -public: - virtual Path3D *get_path_3d() const = 0; - virtual Ref get_curve_3d() const = 0; - - virtual void register_modifier(PathModifier3D *p_modifier) = 0; - virtual void unregister_modifier(PathModifier3D *p_modifier) = 0; -}; - -template -class PathToolWrapper : public PathToolInterface { - -public: - PathToolWrapper(T* p_tool) : tool(p_tool) { - } - ~PathToolWrapper() { - tool = nullptr; - } - - Path3D *get_path_3d() const override { - if (tool != nullptr) { - return tool->get_path_3d(); - } else { - return nullptr; - } - } - - Ref get_curve_3d() const override { - Path3D *path3d_node = get_path_3d(); - if (path3d_node != nullptr) { - return path3d_node->get_curve(); - } else { - return nullptr; - } - } - - void register_modifier(PathModifier3D *p_modifier) override { - if (tool != nullptr) { - tool->register_modifier(p_modifier); - } - } - - void unregister_modifier(PathModifier3D *p_modifier) override { - if (tool != nullptr) { - tool->unregister_modifier(p_modifier); - } - } - -private: - T* tool = nullptr; -}; - } \ No newline at end of file From 5f10bfe9219c86c456b4b320574e2bfbe58337e5 Mon Sep 17 00:00:00 2001 From: iiMidknightii <89699177+iiMidknightii@users.noreply.github.com> Date: Sat, 13 Dec 2025 19:30:12 -0800 Subject: [PATCH 4/5] Cleaned up some includes --- addons/PathMesh3D/src/path_area_3d.cpp | 11 ----------- addons/PathMesh3D/src/path_physics_tool_3d.hpp | 6 +++++- addons/PathMesh3D/src/path_static_body_3d.cpp | 12 ------------ 3 files changed, 5 insertions(+), 24 deletions(-) diff --git a/addons/PathMesh3D/src/path_area_3d.cpp b/addons/PathMesh3D/src/path_area_3d.cpp index e47912c..ddd37ef 100644 --- a/addons/PathMesh3D/src/path_area_3d.cpp +++ b/addons/PathMesh3D/src/path_area_3d.cpp @@ -1,16 +1,5 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include #include "path_area_3d.hpp" -#include "path_physics_tool_3d.hpp" -#include "path_tool_3d.hpp" using namespace godot; diff --git a/addons/PathMesh3D/src/path_physics_tool_3d.hpp b/addons/PathMesh3D/src/path_physics_tool_3d.hpp index 54a9eb4..be6773e 100644 --- a/addons/PathMesh3D/src/path_physics_tool_3d.hpp +++ b/addons/PathMesh3D/src/path_physics_tool_3d.hpp @@ -1,16 +1,20 @@ #pragma once +#include "path_tool_3d.hpp" #include "godot_cpp/core/class_db.hpp" #include "godot_cpp/core/defs.hpp" #include -#include "path_tool_3d.hpp" #include +#include #include #include #include #include #include #include +#include +#include +#include namespace godot { diff --git a/addons/PathMesh3D/src/path_static_body_3d.cpp b/addons/PathMesh3D/src/path_static_body_3d.cpp index 24f4fa6..da9baed 100644 --- a/addons/PathMesh3D/src/path_static_body_3d.cpp +++ b/addons/PathMesh3D/src/path_static_body_3d.cpp @@ -1,16 +1,4 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include "path_static_body_3d.hpp" -#include "path_physics_tool_3d.hpp" -#include "path_tool_3d.hpp" using namespace godot; From 6e889c29545e4ce9460fed3877a53c30e9108e4a Mon Sep 17 00:00:00 2001 From: iiMidknightii <89699177+iiMidknightii@users.noreply.github.com> Date: Sat, 13 Dec 2025 19:33:11 -0800 Subject: [PATCH 5/5] Moved path tools src files to subfolder --- addons/PathMesh3D/SConstruct | 2 +- .../PathMesh3D/src/{ => path_tools}/path_area_3d.cpp | 2 +- .../PathMesh3D/src/{ => path_tools}/path_area_3d.hpp | 0 .../src/{ => path_tools}/path_extrude_3d.cpp | 2 +- .../src/{ => path_tools}/path_extrude_3d.hpp | 0 .../PathMesh3D/src/{ => path_tools}/path_mesh_3d.cpp | 2 +- .../PathMesh3D/src/{ => path_tools}/path_mesh_3d.hpp | 0 .../src/{ => path_tools}/path_multimesh_3d.cpp | 2 +- .../src/{ => path_tools}/path_multimesh_3d.hpp | 0 .../src/{ => path_tools}/path_scene_3d.cpp | 2 +- .../src/{ => path_tools}/path_scene_3d.hpp | 0 .../src/{ => path_tools}/path_static_body_3d.cpp | 2 +- .../src/{ => path_tools}/path_static_body_3d.hpp | 0 addons/PathMesh3D/src/register_types.cpp | 12 ++++++------ 14 files changed, 13 insertions(+), 13 deletions(-) rename addons/PathMesh3D/src/{ => path_tools}/path_area_3d.cpp (92%) rename addons/PathMesh3D/src/{ => path_tools}/path_area_3d.hpp (100%) rename addons/PathMesh3D/src/{ => path_tools}/path_extrude_3d.cpp (99%) rename addons/PathMesh3D/src/{ => path_tools}/path_extrude_3d.hpp (100%) rename addons/PathMesh3D/src/{ => path_tools}/path_mesh_3d.cpp (99%) rename addons/PathMesh3D/src/{ => path_tools}/path_mesh_3d.hpp (100%) rename addons/PathMesh3D/src/{ => path_tools}/path_multimesh_3d.cpp (99%) rename addons/PathMesh3D/src/{ => path_tools}/path_multimesh_3d.hpp (100%) rename addons/PathMesh3D/src/{ => path_tools}/path_scene_3d.cpp (99%) rename addons/PathMesh3D/src/{ => path_tools}/path_scene_3d.hpp (100%) rename addons/PathMesh3D/src/{ => path_tools}/path_static_body_3d.cpp (91%) rename addons/PathMesh3D/src/{ => path_tools}/path_static_body_3d.hpp (100%) diff --git a/addons/PathMesh3D/SConstruct b/addons/PathMesh3D/SConstruct index 25ec6b2..8d13c0c 100644 --- a/addons/PathMesh3D/SConstruct +++ b/addons/PathMesh3D/SConstruct @@ -23,7 +23,7 @@ env.CompilationDatabase('compile_database.json') # tweak this if you want to use different folders, or more folders, to store your source code in. env.Append(CPPPATH=["src/"]) -sources = Glob("src/*.cpp") + Glob("src/extrude_profiles/*.cpp") +sources = Glob("src/*.cpp") + Glob("src/extrude_profiles/*.cpp") + Glob("src/path_tools/*.cpp") if env["target"] in ["editor", "template_debug"]: try: diff --git a/addons/PathMesh3D/src/path_area_3d.cpp b/addons/PathMesh3D/src/path_tools/path_area_3d.cpp similarity index 92% rename from addons/PathMesh3D/src/path_area_3d.cpp rename to addons/PathMesh3D/src/path_tools/path_area_3d.cpp index ddd37ef..adb3a84 100644 --- a/addons/PathMesh3D/src/path_area_3d.cpp +++ b/addons/PathMesh3D/src/path_tools/path_area_3d.cpp @@ -1,5 +1,5 @@ -#include "path_area_3d.hpp" +#include "path_tools/path_area_3d.hpp" using namespace godot; diff --git a/addons/PathMesh3D/src/path_area_3d.hpp b/addons/PathMesh3D/src/path_tools/path_area_3d.hpp similarity index 100% rename from addons/PathMesh3D/src/path_area_3d.hpp rename to addons/PathMesh3D/src/path_tools/path_area_3d.hpp diff --git a/addons/PathMesh3D/src/path_extrude_3d.cpp b/addons/PathMesh3D/src/path_tools/path_extrude_3d.cpp similarity index 99% rename from addons/PathMesh3D/src/path_extrude_3d.cpp rename to addons/PathMesh3D/src/path_tools/path_extrude_3d.cpp index af31bd7..bdcb665 100644 --- a/addons/PathMesh3D/src/path_extrude_3d.cpp +++ b/addons/PathMesh3D/src/path_tools/path_extrude_3d.cpp @@ -1,7 +1,7 @@ #include #include -#include "path_extrude_3d.hpp" +#include "path_tools/path_extrude_3d.hpp" #include "godot_cpp/classes/array_mesh.hpp" #include "path_collision_tool_3d.hpp" #include "path_tool_3d.hpp" diff --git a/addons/PathMesh3D/src/path_extrude_3d.hpp b/addons/PathMesh3D/src/path_tools/path_extrude_3d.hpp similarity index 100% rename from addons/PathMesh3D/src/path_extrude_3d.hpp rename to addons/PathMesh3D/src/path_tools/path_extrude_3d.hpp diff --git a/addons/PathMesh3D/src/path_mesh_3d.cpp b/addons/PathMesh3D/src/path_tools/path_mesh_3d.cpp similarity index 99% rename from addons/PathMesh3D/src/path_mesh_3d.cpp rename to addons/PathMesh3D/src/path_tools/path_mesh_3d.cpp index 85110ae..f56460c 100644 --- a/addons/PathMesh3D/src/path_mesh_3d.cpp +++ b/addons/PathMesh3D/src/path_tools/path_mesh_3d.cpp @@ -1,7 +1,7 @@ #include #include -#include "path_mesh_3d.hpp" +#include "path_tools/path_mesh_3d.hpp" #include "path_collision_tool_3d.hpp" #include "path_tool_3d.hpp" diff --git a/addons/PathMesh3D/src/path_mesh_3d.hpp b/addons/PathMesh3D/src/path_tools/path_mesh_3d.hpp similarity index 100% rename from addons/PathMesh3D/src/path_mesh_3d.hpp rename to addons/PathMesh3D/src/path_tools/path_mesh_3d.hpp diff --git a/addons/PathMesh3D/src/path_multimesh_3d.cpp b/addons/PathMesh3D/src/path_tools/path_multimesh_3d.cpp similarity index 99% rename from addons/PathMesh3D/src/path_multimesh_3d.cpp rename to addons/PathMesh3D/src/path_tools/path_multimesh_3d.cpp index 88f4779..8ee19d4 100644 --- a/addons/PathMesh3D/src/path_multimesh_3d.cpp +++ b/addons/PathMesh3D/src/path_tools/path_multimesh_3d.cpp @@ -1,6 +1,6 @@ #include -#include "path_multimesh_3d.hpp" +#include "path_tools/path_multimesh_3d.hpp" using namespace godot; diff --git a/addons/PathMesh3D/src/path_multimesh_3d.hpp b/addons/PathMesh3D/src/path_tools/path_multimesh_3d.hpp similarity index 100% rename from addons/PathMesh3D/src/path_multimesh_3d.hpp rename to addons/PathMesh3D/src/path_tools/path_multimesh_3d.hpp diff --git a/addons/PathMesh3D/src/path_scene_3d.cpp b/addons/PathMesh3D/src/path_tools/path_scene_3d.cpp similarity index 99% rename from addons/PathMesh3D/src/path_scene_3d.cpp rename to addons/PathMesh3D/src/path_tools/path_scene_3d.cpp index 5fdd91f..86de64a 100644 --- a/addons/PathMesh3D/src/path_scene_3d.cpp +++ b/addons/PathMesh3D/src/path_tools/path_scene_3d.cpp @@ -1,6 +1,6 @@ #include -#include "path_scene_3d.hpp" +#include "path_tools/path_scene_3d.hpp" using namespace godot; diff --git a/addons/PathMesh3D/src/path_scene_3d.hpp b/addons/PathMesh3D/src/path_tools/path_scene_3d.hpp similarity index 100% rename from addons/PathMesh3D/src/path_scene_3d.hpp rename to addons/PathMesh3D/src/path_tools/path_scene_3d.hpp diff --git a/addons/PathMesh3D/src/path_static_body_3d.cpp b/addons/PathMesh3D/src/path_tools/path_static_body_3d.cpp similarity index 91% rename from addons/PathMesh3D/src/path_static_body_3d.cpp rename to addons/PathMesh3D/src/path_tools/path_static_body_3d.cpp index da9baed..f9a879f 100644 --- a/addons/PathMesh3D/src/path_static_body_3d.cpp +++ b/addons/PathMesh3D/src/path_tools/path_static_body_3d.cpp @@ -1,4 +1,4 @@ -#include "path_static_body_3d.hpp" +#include "path_tools/path_static_body_3d.hpp" using namespace godot; diff --git a/addons/PathMesh3D/src/path_static_body_3d.hpp b/addons/PathMesh3D/src/path_tools/path_static_body_3d.hpp similarity index 100% rename from addons/PathMesh3D/src/path_static_body_3d.hpp rename to addons/PathMesh3D/src/path_tools/path_static_body_3d.hpp diff --git a/addons/PathMesh3D/src/register_types.cpp b/addons/PathMesh3D/src/register_types.cpp index e011432..e9a5712 100644 --- a/addons/PathMesh3D/src/register_types.cpp +++ b/addons/PathMesh3D/src/register_types.cpp @@ -1,14 +1,14 @@ -#include "path_mesh_3d.hpp" -#include "path_extrude_3d.hpp" -#include "path_multimesh_3d.hpp" -#include "path_scene_3d.hpp" +#include "path_tools/path_mesh_3d.hpp" +#include "path_tools/path_extrude_3d.hpp" +#include "path_tools/path_multimesh_3d.hpp" +#include "path_tools/path_scene_3d.hpp" #include "path_modifier_3d.hpp" #include "path_extrude_profile_base.hpp" #include "extrude_profiles/path_extrude_profile_manual.hpp" #include "extrude_profiles/path_extrude_profile_rect.hpp" #include "extrude_profiles/path_extrude_profile_circle.hpp" -#include "path_static_body_3d.hpp" -#include "path_area_3d.hpp" +#include "path_tools/path_static_body_3d.hpp" +#include "path_tools/path_area_3d.hpp" #include "register_types.hpp" void initialize_path_mesh_3d(ModuleInitializationLevel p_level) {