Compare commits

...
Author SHA1 Message Date
ActualHorse 4fd24f03a1 Slicing function now has multiple deferred stages. 2026-10-01 11:42:21 -04:00
ActualHorse 223eaf2f32 Reworked slicing code in various ways:
- slice_mesh takes an input struct, and returns all data in an output struct.
- Separated loops to ease the possibility of making them deferred functions.
2026-09-29 04:19:39 -04:00
ActualHorse 6e508ccde2 - slice_mesh now supports interpolating custom Float32 data arrays.
- get_skinned_vertex_positions now uses Ref<ArrayMesh> instead of a pointer.
2026-09-27 17:36:49 -04:00
ActualHorse e35adc23b2 Removed fix_polygon_winding's ability to delete edges, since it caused problems
with polygons that have only three vertices and also that isn't really its job
to do anyway.
2026-09-26 14:04:32 -04:00
ActualHorse b5fe9f7c8d HurtboxBase now sends hit updates on a timer instead of on a new frame. 2026-09-26 01:42:38 -04:00
ActualHorse 64421d8ff7 Added a macro to easily call functions on a timer. 2026-09-26 01:34:33 -04:00
ActualHorse b6315f3076 Sliced mesh caps now generate proper normals, thanks to SurfaceTool. 2026-09-26 01:34:01 -04:00
11 changed files with 799 additions and 391 deletions
+53 -1
View File
@@ -1,8 +1,60 @@
/*
* ©2023 Batty Bovine Productions, LLC. All Rights Reserved.
* ©2026 Batty Bovine Productions, LLC. All Rights Reserved.
*
* If this source code makes it to the public internet, this software can be
* considered to be protected by the MIT licence. Have fun with it.
*/
#include "i_damageable.h"
#include "orng_macros.h"
#include "godot_cpp/classes/scene_tree.hpp"
#include "godot_cpp/classes/scene_tree_timer.hpp"
void IDamageable::_slice_meshes(Ref<MeshEditSliceInput> data)
{
this->_is_slicing = true;
this->_input_queue.emplace_back(data);
if (this->_slice_state_queue.empty())
this->_new_slice_loop();
}
void IDamageable::_new_slice_loop()
{
this->_slice_state_queue.emplace_back(MeshEditingLibrary::slice_mesh(this->_input_queue.front()));
this->_slice_loop();
}
void IDamageable::_slice_loop()
{
Ref<MeshSliceState> current_state = this->_slice_state_queue.front();
switch(current_state->current_stage)
{
case MeshEditingLibrary::MeshEditSliceStage::SLICE_STAGE_INIT:
MeshEditingLibrary::slice_mesh_init(current_state);
break;
case MeshEditingLibrary::MeshEditSliceStage::SLICE_STAGE_PLANE_SPLIT:
MeshEditingLibrary::slice_mesh_plane_split(current_state);
break;
case MeshEditingLibrary::MeshEditSliceStage::SLICE_STAGE_EDGE_SPLIT:
MeshEditingLibrary::slice_mesh_edge_split(current_state);
break;
case MeshEditingLibrary::MeshEditSliceStage::SLICE_STAGE_APPLY_CAP:
MeshEditingLibrary::slice_mesh_apply_cap(current_state);
break;
case MeshEditingLibrary::MeshEditSliceStage::SLICE_STAGE_COMPLETE:
this->_slice_complete(current_state->output);
this->_input_queue.erase(this->_input_queue.begin());
this->_slice_state_queue.erase(this->_slice_state_queue.begin());
if (!this->_input_queue.empty())
return this->_new_slice_loop();
break;
}
if (!this->_input_queue.empty())
CALL_NEXT_FRAME(callable_mp(this, &IDamageable::_slice_loop));
else
this->_is_slicing = false;
}
+25 -2
View File
@@ -1,5 +1,5 @@
/*
* ©2023 Batty Bovine Productions, LLC. All Rights Reserved.
* ©2026 Batty Bovine Productions, LLC. All Rights Reserved.
*
* If this source code makes it to the public internet, this software can be
* considered to be protected by the MIT licence. Have fun with it.
@@ -7,9 +7,32 @@
#pragma once
#include "nodes/mesh_editing_library.h"
class IDamageable
#include "godot_cpp/classes/node.hpp"
using namespace godot;
class IDamageable : public Node3D
{
public:
virtual void take_damage(const struct HurtboxDamage &hit_data) = 0;
protected:
void _slice_meshes(Ref<MeshEditSliceInput> data);
virtual void _slice_complete(Ref<MeshEditSliceOutput> data) = 0;
private:
void _new_slice_loop();
void _slice_loop();
bool _is_slicing = false;
std::vector<Ref<MeshSliceState>> _slice_state_queue;
std::vector<Ref<MeshEditSliceInput>> _input_queue;
// Godot boilerplate below
protected:
static void _bind_methods() {}
};
@@ -42,8 +42,17 @@ void Character3D::set_meshes(TypedArray<ArrayMesh> m)
void Character3D::set_character_skeleton(CharacterSkeleton3D *cs)
{
this->character_skeleton = cs;
this->_rebuild_mesh_instances();
if (cs)
{
if (this->character_skeleton)
{
this->skeleton_set.erase(this->character_skeleton);
this->character_skeleton->queue_free();
}
this->character_skeleton = cs;
this->skeleton_set.emplace(this->character_skeleton);
this->_rebuild_mesh_instances();
}
}
@@ -66,54 +75,19 @@ void Character3D::_next_hurtbox_in_queue()
Skeleton3D *skeleton = character_skeleton->get_skeleton();
const Plane &local_slice_plane = skeleton->get_global_transform().inverse().xform(Plane(hit_data.global_hit_transform.get_basis().get_column(1), hit_data.global_hit_transform.origin));
std::vector<MeshInstance3D*> sliced_mesh_instances = character_skeleton->get_mesh_instances();
TypedArray<MeshInstance3D> sliced_mesh_instances = character_skeleton->get_mesh_instances();
const uint32_t num_mesh_instances = sliced_mesh_instances.size();
TypedArray<ArrayMesh> first_half_meshes_to_add;
TypedArray<ArrayMesh> other_half_meshes_to_add;
for (uint32_t i = 0; i < num_mesh_instances; i++)
{
MeshInstance3D *original_mesh_instance = sliced_mesh_instances[i];
Ref<ArrayMesh> first_half; first_half.instantiate();
Ref<ArrayMesh> other_half; other_half.instantiate();
PackedVector3Array &impact_points = MeshEditingLibrary::slice_mesh(original_mesh_instance, skeleton, this->_full_aabb, local_slice_plane, first_half, other_half, MeshEditingLibrary::CAP_UV_FILL_MESH_BOUNDS, this->inner_materials);
if (first_half->get_surface_count() <= 0 && other_half->get_surface_count() > 0)
{
Ref<ArrayMesh> temp = first_half;
first_half = other_half;
other_half = temp;
}
if (first_half->get_surface_count() > 0)
{
first_half_meshes_to_add.append(first_half);
}
if (other_half->get_surface_count() > 0)
{
other_half_meshes_to_add.append(other_half);
}
}
character_skeleton->set_meshes(first_half_meshes_to_add);
if (other_half_meshes_to_add.size() > 0)
{
RandomNumberGenerator *rng = memnew(RandomNumberGenerator);
const float random_x = rng->randf_range(-0.05f, 0.05f);
const float random_z = rng->randf_range(-0.05f, 0.05f);
CharacterSkeleton3D *new_character_skeleton = cast_to<CharacterSkeleton3D>(this->character_skeleton->duplicate());
this->character_skeleton->add_sibling(new_character_skeleton);
new_character_skeleton->set_position(Vector3(random_x, 0.0f, random_z));
PRINT_LOG(CHARACTER3D_TAG, "Adding new meshes:");
for (uint16_t i = 0; i < other_half_meshes_to_add.size(); i++)
{
const Ref<ArrayMesh> &new_array_mesh = other_half_meshes_to_add[i];
PRINT_LOG(CHARACTER3D_TAG, " Mesh ", i+1, " size: ", new_array_mesh->get_surface_count());
}
new_character_skeleton->set_meshes(other_half_meshes_to_add);
}
Ref<MeshEditSliceInput> input; input.instantiate();
input->mesh_instance = cast_to<MeshInstance3D>(sliced_mesh_instances[0]);
input->skeleton = skeleton;
input->full_aabb = this->_full_aabb;
input->local_plane = local_slice_plane;
input->cap_option = MeshEditingLibrary::CAP_UV_FILL_MESH_BOUNDS;
input->cap_materials = this->inner_materials;
this->_slice_meshes(input);
}
else
{
@@ -124,6 +98,59 @@ void Character3D::_next_hurtbox_in_queue()
CALL_NEXT_FRAME(callable_mp(this, &Character3D::_next_hurtbox_in_queue));
}
void Character3D::_slice_complete(Ref<MeshEditSliceOutput> data)
{
if (data->main_half->get_surface_count() <= 0 && data->secondary_half->get_surface_count() > 0)
{
Ref<ArrayMesh> temp = data->main_half;
data->main_half = data->secondary_half;
data->secondary_half = temp;
}
TypedArray<ArrayMesh> first_half_meshes_to_add;
TypedArray<ArrayMesh> other_half_meshes_to_add;
if (data->main_half->get_surface_count() > 0)
{
first_half_meshes_to_add.append(data->main_half);
}
if (data->secondary_half->get_surface_count() > 0)
{
other_half_meshes_to_add.append(data->secondary_half);
}
for (CharacterSkeleton3D *skeleton : this->skeleton_set)
{
if (skeleton->get_skeleton() == data->original_skeleton)
{
skeleton->set_meshes(first_half_meshes_to_add);
break;
}
}
if (other_half_meshes_to_add.size() > 0)
{
RandomNumberGenerator *rng = memnew(RandomNumberGenerator);
const float random_x = rng->randf_range(-0.05f, 0.05f);
const float random_z = rng->randf_range(-0.05f, 0.05f);
CharacterSkeleton3D *new_character_skeleton = cast_to<CharacterSkeleton3D>(this->character_skeleton->duplicate());
this->character_skeleton->add_sibling(new_character_skeleton);
new_character_skeleton->set_position(Vector3(random_x, 0.0f, random_z));
PRINT_LOG(CHARACTER3D_TAG, "Adding new meshes:");
for (uint16_t i = 0; i < other_half_meshes_to_add.size(); i++)
{
const Ref<ArrayMesh> &new_array_mesh = other_half_meshes_to_add[i];
PRINT_LOG(CHARACTER3D_TAG, " Mesh ", i+1, " size: ", new_array_mesh->get_surface_count());
}
new_character_skeleton->set_meshes(other_half_meshes_to_add);
this->skeleton_set.emplace(new_character_skeleton);
}
}
void Character3D::_on_fell_out_of_world()
{
@@ -136,11 +163,13 @@ void Character3D::_rebuild_mesh_instances()
if (!this->character_skeleton || !this->is_node_ready())
return;
std::vector<MeshInstance3D*> mesh_instances = this->character_skeleton->set_meshes(this->meshes);
TypedArray<MeshInstance3D> mesh_instances = this->character_skeleton->set_meshes(this->meshes);
const uint32_t num_meshes = mesh_instances.size();
this->_full_aabb = AABB();
for (const MeshInstance3D *mesh_instance : mesh_instances)
for (uint32_t i = 0; i < num_meshes; i++)
{
const MeshInstance3D *mesh_instance = cast_to<MeshInstance3D>(mesh_instances[i]);
const Vector3 &full_aabb_position = this->_full_aabb.get_position();
const Vector3 &full_aabb_size = this->_full_aabb.get_size();
@@ -13,6 +13,8 @@
#include "nodes/character_nodes/character_skeleton3d.h"
#include <map>
#include <unordered_set>
#include <godot_cpp/classes/character_body3d.hpp>
#include <godot_cpp/classes/material.hpp>
#include <godot_cpp/classes/mesh_instance3d.hpp>
@@ -23,9 +25,9 @@ using namespace godot;
#define CHARACTER3D_TAG "Character3D"
class Character3D : public CharacterBody3D, public IDamageable
class Character3D : public IDamageable
{
GDCLASS(Character3D, CharacterBody3D);
GDCLASS(Character3D, IDamageable);
public:
virtual void _ready() override;
@@ -49,9 +51,13 @@ private:
void _rebuild_mesh_instances();
void _next_hurtbox_in_queue();
virtual void _slice_complete(Ref<MeshEditSliceOutput> data) override;
TypedArray<ArrayMesh> meshes;
CharacterSkeleton3D *character_skeleton = nullptr;
std::unordered_set<CharacterSkeleton3D*> skeleton_set;
AABB _full_aabb;
TypedArray<Material> inner_materials;
@@ -16,21 +16,22 @@
using namespace godot;
std::vector<MeshInstance3D*> CharacterSkeleton3D::set_meshes(TypedArray<ArrayMesh> meshes)
TypedArray<MeshInstance3D> CharacterSkeleton3D::set_meshes(TypedArray<ArrayMesh> meshes)
{
if (this->skeleton)
{
this->_old_mesh_instances = this->_mesh_instances;
this->_old_mesh_instances = this->_mesh_instances.duplicate(true);
this->_mesh_instances.clear();
uint16_t num_new_meshes = meshes.size();
this->_mesh_instances.resize(num_new_meshes);
for (uint16_t i = 0; i < num_new_meshes; i++)
{
MeshInstance3D *mesh_instance = memnew(MeshInstance3D);
mesh_instance->set_mesh(meshes[i]);
mesh_instance->set_skeleton_path(this->skeleton->get_path());
this->skeleton->add_child(mesh_instance, false, Node::INTERNAL_MODE_BACK);
this->_mesh_instances.emplace_back(mesh_instance);
this->_mesh_instances[i] = mesh_instance;
}
CALL_NEXT_FRAME(callable_mp(this, &CharacterSkeleton3D::_delete_old_meshes));
@@ -40,8 +41,8 @@ std::vector<MeshInstance3D*> CharacterSkeleton3D::set_meshes(TypedArray<ArrayMes
}
void CharacterSkeleton3D::_delete_old_meshes()
{
for (MeshInstance3D *old_mesh : this->_old_mesh_instances)
old_mesh->queue_free();
for (uint16_t i = 0; i < this->_old_mesh_instances.size(); i++)
cast_to<MeshInstance3D>(this->_old_mesh_instances[i])->queue_free();
this->_old_mesh_instances.clear();
}
@@ -26,15 +26,15 @@ class CharacterSkeleton3D : public Node3D
GDCLASS(CharacterSkeleton3D, Node3D);
public:
std::vector<MeshInstance3D*> set_meshes(TypedArray<ArrayMesh> meshes);
TypedArray<MeshInstance3D> set_meshes(TypedArray<ArrayMesh> meshes);
Skeleton3D *get_skeleton() const { return this->skeleton; }
PhysicalBoneSimulator3D *get_physical_bone_simulator() const { return this->physical_bone_simulator; }
AnimationPlayer *get_animation_player() const { return this->animation_player; }
AnimationTree *get_animation_tree() const { return this->animation_tree; }
void replace_mesh_instance(const uint16_t index, Ref<ArrayMesh> new_mesh) { this->_mesh_instances[index]->set_mesh(new_mesh); }
std::vector<MeshInstance3D*> get_mesh_instances() const { return this->_mesh_instances; }
void replace_mesh_instance(const uint16_t index, Ref<ArrayMesh> new_mesh) { cast_to<MeshInstance3D>(this->_mesh_instances[index])->set_mesh(new_mesh); }
TypedArray<MeshInstance3D> get_mesh_instances() const { return this->_mesh_instances; }
protected:
void set_skeleton(Skeleton3D *s) { this->skeleton = s; }
@@ -53,8 +53,8 @@ private:
AnimationPlayer *animation_player = nullptr;
AnimationTree *animation_tree = nullptr;
std::vector<MeshInstance3D*> _mesh_instances;
std::vector<MeshInstance3D*> _old_mesh_instances;
TypedArray<MeshInstance3D> _mesh_instances;
TypedArray<MeshInstance3D> _old_mesh_instances;
// Godot boilerplate below
protected:
+2 -2
View File
@@ -52,7 +52,7 @@ void HurtboxBase::_ready()
this->set_process(false);
this->remaining_life = this->lifetime;
CALL_NEXT_FRAME(callable_mp(this, &HurtboxBase::_send_next_in_queue));
this->_send_next_in_queue();
}
void HurtboxBase::_physics_process(double delta)
@@ -146,6 +146,6 @@ void HurtboxBase::_send_next_in_queue()
}
else
{
CALL_NEXT_FRAME(callable_mp(this, &HurtboxBase::_send_next_in_queue));
CALL_ON_TIMEOUT(1.0f / 8.0f, callable_mp(this, &HurtboxBase::_send_next_in_queue));
}
}
File diff suppressed because it is too large Load Diff
+144 -4
View File
@@ -15,11 +15,12 @@
#include <godot_cpp/variant/vector3.hpp>
using namespace godot;
#include <map>
#include <vector>
#define MESH_EDITING_LIBRARY_TAG "MeshEditingLibrary"
typedef std::vector<Vector3> SkinnedVertices;
typedef PackedVector3Array SkinnedVertices;
typedef std::vector<SkinnedVertices> SkinnedSurfaces;
@@ -55,6 +56,11 @@ struct MeshEditPolygon2D
};
class MeshEditSliceInput;
class MeshEditSliceOutput;
class MeshSliceState;
class MeshEditingLibrary : public Node
{
GDCLASS(MeshEditingLibrary, Node);
@@ -63,6 +69,15 @@ public:
MeshEditingLibrary(){}
~MeshEditingLibrary(){}
enum MeshEditSliceStage
{
SLICE_STAGE_INIT,
SLICE_STAGE_PLANE_SPLIT,
SLICE_STAGE_EDGE_SPLIT,
SLICE_STAGE_APPLY_CAP,
SLICE_STAGE_COMPLETE
};
enum MeshEditCapUV
{
CAP_UV_FILL_MESH_BOUNDS,
@@ -70,7 +85,12 @@ public:
CAP_UV_TILED
};
static PackedVector3Array slice_mesh(MeshInstance3D *original_mesh_instance, const Skeleton3D *original_skeleton, const AABB &original_aabb, const Plane &local_plane, Ref<ArrayMesh> out_first_half, Ref<ArrayMesh> out_other_half, const MeshEditCapUV cap_option, const TypedArray<Material> &cap_materials);
static Ref<class MeshSliceState> slice_mesh(const Ref<class MeshEditSliceInput> input);
static void slice_mesh_init(Ref<class MeshSliceState> state);
static void slice_mesh_plane_split(Ref<class MeshSliceState> state);
static void slice_mesh_edge_split(Ref<class MeshSliceState> state);
static void slice_mesh_apply_cap(Ref<class MeshSliceState> state);
protected:
enum MeshSlicePlaneOrientation
@@ -83,7 +103,9 @@ protected:
private:
static const Vector2 calculate_planar_uv(const Vector3 &vertex, const Vector3 &mesh_bounds, const MeshSlicePlaneOrientation &axis);
static SkinnedVertices get_skinned_vertex_positions(const uint8_t surface, const Mesh *mesh, const Skeleton3D *skeleton = nullptr, std::vector<Transform3D> &bone_transforms = std::vector<Transform3D>());
static SkinnedVertices get_skinned_vertex_positions(const uint8_t surface, const Ref<ArrayMesh> mesh, const Skeleton3D *skeleton = nullptr, std::vector<Transform3D> &bone_transforms = std::vector<Transform3D>());
static constexpr const uint8_t uv_to_body_part_shift(const float x, const float y) { return (uint8_t(y * 5.0f) * 5) + uint8_t(x * 5.0f); }
static const Transform3D project_edges(std::vector<MeshEditEdge2D> &out_2d_edges, const std::vector<MeshEditEdge3D> &in_3d_edges, const Plane &plane);
static void build_2d_polygons_from_edges(std::vector<MeshEditPolygon2D> &out_polygons, const std::vector<MeshEditEdge2D> &in_edges, std::vector<MeshEditPolygon2D> &error_polygons);
@@ -96,7 +118,13 @@ private:
protected:
static void _bind_methods()
{
ClassDB::bind_static_method("MeshEditingLibrary", D_METHOD("slice_mesh", "original_mesh_instance", "original_skeleton", "local_plane", "out_first_half", "out_other_half", "cap_option", "cap_material"), &MeshEditingLibrary::slice_mesh);
ClassDB::bind_static_method("MeshEditingLibrary", D_METHOD("slice_mesh", "input"), &MeshEditingLibrary::slice_mesh);
BIND_ENUM_CONSTANT(SLICE_STAGE_INIT);
BIND_ENUM_CONSTANT(SLICE_STAGE_PLANE_SPLIT);
BIND_ENUM_CONSTANT(SLICE_STAGE_EDGE_SPLIT);
BIND_ENUM_CONSTANT(SLICE_STAGE_APPLY_CAP);
BIND_ENUM_CONSTANT(SLICE_STAGE_COMPLETE);
BIND_ENUM_CONSTANT(CAP_UV_FILL_MESH_BOUNDS);
BIND_ENUM_CONSTANT(CAP_UV_FILL_CAP_BOUNDS);
@@ -104,4 +132,116 @@ protected:
}
};
VARIANT_ENUM_CAST(MeshEditingLibrary::MeshEditSliceStage);
VARIANT_ENUM_CAST(MeshEditingLibrary::MeshEditCapUV);
class MeshEditSliceInput : public RefCounted
{
GDCLASS(MeshEditSliceInput, RefCounted);
public:
MeshInstance3D *mesh_instance = nullptr;
const Skeleton3D *skeleton = nullptr;
AABB full_aabb;
Plane local_plane;
uint32_t hit_mask = 0b1111111111111111111111111;
MeshEditingLibrary::MeshEditCapUV cap_option;
TypedArray<Material> cap_materials;
protected:
static void _bind_methods(){}
};
class MeshEditSliceOutput : public RefCounted
{
GDCLASS(MeshEditSliceOutput, RefCounted);
public:
const Skeleton3D *original_skeleton = nullptr;
Ref<ArrayMesh> main_half;
Ref<ArrayMesh> secondary_half;
PackedVector3Array impact_points;
uint32_t hit_map = 0b0000000000000000000000000;
protected:
static void _bind_methods(){}
};
class MeshSliceState : public RefCounted
{
GDCLASS(MeshSliceState, RefCounted);
public:
MeshEditingLibrary::MeshEditSliceStage current_stage = MeshEditingLibrary::MeshEditSliceStage::SLICE_STAGE_INIT;
MeshInstance3D *current_mesh = nullptr;
Ref<MeshEditSliceInput> input;
uint8_t surface = 0;
uint32_t element = 0;
Ref<ArrayMesh> first_half;
Ref<ArrayMesh> other_half;
Ref<ArrayMesh> original_mesh;
uint8_t num_surfaces;
std::vector<Array> surface_arrays_list;
std::vector<SkinnedVertices> skinned_vertex_positions;
std::vector<std::vector<Transform3D>> skinned_vertex_transforms;
std::vector<bool> has_normal;
std::vector<bool> has_tangent;
std::vector<bool> has_uv;
std::vector<bool> has_uv2;
std::vector<bool> has_colour;
std::vector<bool> has_bone;
std::vector<bool> has_weight;
std::vector<bool> has_custom0;
std::vector<uint8_t> num_bones_per_vertex;
std::vector<uint8_t> num_custom0_per_vertex;
std::vector<PackedVector3Array> first_half_vertices;
std::vector<PackedVector3Array> first_half_normals;
std::vector<PackedFloat32Array> first_half_tangents;
std::vector<PackedVector2Array> first_half_uvs;
std::vector<PackedVector2Array> first_half_uv2s;
std::vector<PackedColorArray> first_half_colours;
std::vector<PackedInt32Array> first_half_bones;
std::vector<PackedFloat32Array> first_half_weights;
std::vector<PackedFloat32Array> first_half_custom0;
std::vector<PackedVector3Array> other_half_vertices;
std::vector<PackedVector3Array> other_half_normals;
std::vector<PackedFloat32Array> other_half_tangents;
std::vector<PackedVector2Array> other_half_uvs;
std::vector<PackedVector2Array> other_half_uv2s;
std::vector<PackedColorArray> other_half_colours;
std::vector<PackedInt32Array> other_half_bones;
std::vector<PackedFloat32Array> other_half_weights;
std::vector<PackedFloat32Array> other_half_custom0;
std::vector<PackedInt32Array> first_half_indices;
std::vector<PackedInt32Array> other_half_indices;
std::vector<std::vector<float>> vertex_distances;
std::vector<std::map<uint32_t, uint32_t>> base_to_first_sliced_vert_indices;
std::vector<std::map<uint32_t, uint32_t>> base_to_other_sliced_vert_indices;
uint32_t first_half_split_map = 0x00000000;
uint32_t other_half_split_map = 0x00000000;
std::vector<MeshEditEdge3D> clip_edges;
Ref<MeshEditSliceOutput> output;
protected:
static void _bind_methods(){}
};
+3 -1
View File
@@ -34,8 +34,10 @@
/**
* Deferred function helpers
*/
#define CALL_ON_TIMEOUT(T,C) \
this->get_tree()->create_timer(T)->connect("timeout", C)
#define CALL_NEXT_FRAME(C) \
this->get_tree()->create_timer(SMALL_NUMBER)->connect("timeout", C)
CALL_ON_TIMEOUT(SMALL_NUMBER,C)
/**
+3
View File
@@ -78,6 +78,9 @@ void initialize_orng_module(ModuleInitializationLevel p_level) {
ClassDB::register_class<SaveFileData>();
ClassDB::register_class<MeshEditingLibrary>();
ClassDB::register_class<MeshEditSliceInput>();
ClassDB::register_class<MeshEditSliceOutput>();
ClassDB::register_class<MeshSliceState>();
GDSINGLETON_REGISTER_CLASS(InputHandler, _input_handler_singleton);
GDSINGLETON_REGISTER_CLASS(SaveManager, _save_manager_singleton);