From 28f2cc1f38612934a7bbd81dd30787215d74bbe5 Mon Sep 17 00:00:00 2001 From: Relintai Date: Thu, 9 Jul 2020 00:26:20 +0200 Subject: [PATCH] Moved the 3D skeleton implementation into the 2D one. --- skeleton/character_skeleton_2d.cpp | 561 ++++++++++++++++++++++++++++- skeleton/character_skeleton_2d.h | 119 +++++- 2 files changed, 658 insertions(+), 22 deletions(-) diff --git a/skeleton/character_skeleton_2d.cpp b/skeleton/character_skeleton_2d.cpp index 18990b4..b0c5766 100644 --- a/skeleton/character_skeleton_2d.cpp +++ b/skeleton/character_skeleton_2d.cpp @@ -22,19 +22,23 @@ SOFTWARE. #include "character_skeleton_2d.h" -void CharacterSkeleton2D::add_model_visual(Ref vis) { +#include "../singletons/ess.h" + +#include "../data/items/model_visual.h" + +#include "../defines.h" + +int CharacterSkeleton2D::get_entity_type() const { + return _entity_type; } -void CharacterSkeleton2D::remove_model_visual(Ref vis) { -} -void CharacterSkeleton2D::remove_model_visual_index(int index) { -} -Ref CharacterSkeleton2D::get_model_visual(int index) { - return Ref(); -} -int CharacterSkeleton2D::get_model_visual_count() { - return 0; -} -void CharacterSkeleton2D::clear_model_visuals() { +void CharacterSkeleton2D::set_entity_type(const int value) { + _entity_type = value; + + int bones_size = ESS::get_singleton()->skeletons_bones_index_get(_entity_type).get_slice_count(","); + int attachment_size = ESS::get_singleton()->skeletons_bone_attachment_index_get(_entity_type).get_slice_count(","); + + _attach_point_nodes.resize(attachment_size); + _entries.resize(bones_size); } int CharacterSkeleton2D::get_model_index() { @@ -44,14 +48,498 @@ void CharacterSkeleton2D::set_model_index(int value) { _model_index = value; } +bool CharacterSkeleton2D::get_model_dirty() const { + return _model_dirty; +} +void CharacterSkeleton2D::set_model_dirty(bool value) { + _model_dirty = value; +} + +NodePath CharacterSkeleton2D::attach_point_path_get(const int index) const { + ERR_FAIL_INDEX_V(index, _attach_point_nodes.size(), NodePath()); + + return _attach_point_nodes[index].path; +} +void CharacterSkeleton2D::attach_point_path_set(const int index, const NodePath &path) { + ERR_FAIL_INDEX(index, _attach_point_nodes.size()); + + _attach_point_nodes.write[index].path = path; + _attach_point_nodes.write[index].node = get_node_or_null(path); +} + +Node *CharacterSkeleton2D::attach_point_node_get(const int index) { + ERR_FAIL_INDEX_V(index, _attach_point_nodes.size(), NULL); + + return _attach_point_nodes[index].node; +} + +int CharacterSkeleton2D::attach_point_count() const { + return _attach_point_nodes.size(); +} + +Node *CharacterSkeleton2D::common_attach_point_node_get(const EntityEnums::CommonCharacterSkeletonPoints index) { + ERR_FAIL_INDEX_V(common_attach_point_index_get(index), _attach_point_nodes.size(), NULL); + + return _attach_point_nodes[common_attach_point_index_get(index)].node; +} +void CharacterSkeleton2D::common_attach_point_add(const EntityEnums::CommonCharacterSkeletonPoints point, const Ref &scene) { + int index = common_attach_point_index_get(point); + + ERR_FAIL_INDEX(index, _attach_point_nodes.size()); + + Node *n = _attach_point_nodes[index].node; + + if (INSTANCE_VALIDATE(n) && n->has_method("add")) { + n->call("add", scene); + } +} +void CharacterSkeleton2D::common_attach_point_add_timed(const EntityEnums::CommonCharacterSkeletonPoints point, const Ref &scene, const float time) { + int index = common_attach_point_index_get(point); + + ERR_FAIL_INDEX(index, _attach_point_nodes.size()); + + Node *n = _attach_point_nodes[index].node; + + if (INSTANCE_VALIDATE(n) && n->has_method("add_timed")) { + n->call("add_timed", scene, time); + } +} +void CharacterSkeleton2D::common_attach_point_remove(const EntityEnums::CommonCharacterSkeletonPoints point, const Ref &scene) { + int index = common_attach_point_index_get(point); + + ERR_FAIL_INDEX(index, _attach_point_nodes.size()); + + Node *n = _attach_point_nodes[index].node; + + if (INSTANCE_VALIDATE(n) && n->has_method("remove")) { + n->call("remove", scene); + } +} +int CharacterSkeleton2D::common_attach_point_index_get(const EntityEnums::CommonCharacterSkeletonPoints point) { + return call("_common_attach_point_index_get", point); +} +int CharacterSkeleton2D::_common_attach_point_index_get(const EntityEnums::CommonCharacterSkeletonPoints point) { + return 0; +} + +NodePath CharacterSkeleton2D::get_animation_player_path() { + return _animation_player_path; +} + +void CharacterSkeleton2D::set_animation_player_path(NodePath path) { + _animation_player_path = path; + + Node *node = get_node_or_null(_animation_player_path); + + if (node != NULL) { + _animation_player = Object::cast_to(node); + } else { + _animation_player = NULL; + } +} + +AnimationPlayer *CharacterSkeleton2D::get_animation_player() { + return _animation_player; +} + +NodePath CharacterSkeleton2D::get_animation_tree_path() { + return _animation_tree_path; +} + +void CharacterSkeleton2D::set_animation_tree_path(NodePath path) { + _animation_tree_path = path; + + Node *node = get_node_or_null(_animation_tree_path); + + if (node != NULL) { + _animation_tree = Object::cast_to(node); + } else { + _animation_tree = NULL; + } +} + +AnimationTree *CharacterSkeleton2D::get_animation_tree() { + return _animation_tree; +} + +void CharacterSkeleton2D::update_nodes() { + for (int i = 0; i < _attach_point_nodes.size(); ++i) { + _attach_point_nodes.write[i].node = get_node_or_null(_attach_point_nodes[i].path); + } + + set_animation_player_path(_animation_player_path); + set_animation_tree_path(_animation_tree_path); +} + +void CharacterSkeleton2D::add_model_visual(Ref vis) { + ERR_FAIL_COND(!vis.is_valid()); + + for (int i = 0; i < vis->get_visual_entry_count(); ++i) { + Ref e = vis->get_visual_entry(i); + + if (e.is_valid()) + add_model_visual_entry(vis, e); + } + + _model_visuals.push_back(vis); + + set_process(true); + _model_dirty = true; +} +void CharacterSkeleton2D::remove_model_visual(Ref vis) { + ERR_FAIL_COND(!vis.is_valid()); + + int index = _model_visuals.find(vis); + + if (index == -1) + return; + + for (int i = 0; i < _entries.size(); ++i) { + Ref e = vis->get_visual_entry(i); + + if (e.is_valid()) + remove_model_visual_entry(vis, e); + } + + _model_visuals.remove(index); + + set_process(true); + _model_dirty = true; +} +void CharacterSkeleton2D::remove_model_visual_index(int index) { + ERR_FAIL_INDEX(index, _model_visuals.size()); + + set_process(true); + _model_dirty = true; + + _model_visuals.remove(index); +} +Ref CharacterSkeleton2D::get_model_visual(int index) { + ERR_FAIL_INDEX_V(index, _model_visuals.size(), Ref()); + + set_process(true); + _model_dirty = true; + + return _model_visuals.get(index); +} +int CharacterSkeleton2D::get_model_visual_count() { + return _model_visuals.size(); +} +void CharacterSkeleton2D::clear_model_visuals() { + _model_visuals.clear(); + + for (int i = 0; i < _entries.size(); ++i) { + _entries.write[i].clear(); + } + + _model_dirty = true; + set_process(true); +} + +void CharacterSkeleton2D::add_model_visual_entry(Ref vis, Ref ive) { + ERR_FAIL_COND(!vis.is_valid()); + ERR_FAIL_COND(!ive.is_valid()); + + if (ive->get_type() == ModelVisualEntry::MODEL_VISUAL_ENTRY_TYPE_ATTACHMENT) { + EntityEnums::CommonCharacterSkeletonPoints target_bone = static_cast(ive->get_bone()); + + for (int i = 0; i < ive->get_size(); ++i) { + Ref ps = ive->get_attachment(i); + + if (ps.is_valid()) { + common_attach_point_add(target_bone, ps); + } + } + + return; + } + + int target_bone_idx = ive->get_bone(); + + Vector > &entries = _entries.write[target_bone_idx]; + + for (int i = 0; i < entries.size(); ++i) { + Ref e = entries.get(i); + + if (e->get_entry() == ive) { + e->set_count(e->get_count() + 1); + return; + } + } + + Ref e; + e.instance(); + + e->set_priority(vis->get_layer()); + //e->set_color(ive->get_color()); + e->set_entry(ive); + + entries.push_back(e); + _model_dirty = true; + set_process(true); +} +void CharacterSkeleton2D::remove_model_visual_entry(Ref vis, Ref ive) { + ERR_FAIL_COND(!vis.is_valid()); + ERR_FAIL_COND(!ive.is_valid()); + + if (ive->get_type() == ModelVisualEntry::MODEL_VISUAL_ENTRY_TYPE_ATTACHMENT) { + EntityEnums::CommonCharacterSkeletonPoints target_bone = static_cast(ive->get_bone()); + + for (int i = 0; i < ive->get_size(); ++i) { + Ref ps = ive->get_attachment(i); + + if (ps.is_valid()) { + common_attach_point_remove(target_bone, ps); + } + } + + return; + } + + int target_bone_idx = ive->get_bone(); + + Vector > &entries = _entries.write[target_bone_idx]; + + for (int i = 0; i < entries.size(); ++i) { + Ref e = entries.get(i); + + if (e->get_entry() == ive) { + e->set_count(e->get_count() - 1); + + if (e->get_count() <= 0) { + entries.remove(i); + + _model_dirty = true; + set_process(true); + } + + return; + } + } +} + +Ref CharacterSkeleton2D::get_model_entry(const int bone_index, const int index) { + ERR_FAIL_INDEX_V(bone_index, _entries.size(), Ref()); + ERR_FAIL_INDEX_V(index, _entries[bone_index].size(), Ref()); + + return _entries[bone_index].get(index); +} +int CharacterSkeleton2D::get_model_entry_count(const int bone_index) { + ERR_FAIL_INDEX_V(bone_index, _entries.size(), 0); + + return _entries[bone_index].size(); +} + +void CharacterSkeleton2D::sort_layers() { + for (int i = 0; i < _entries.size(); ++i) { + Vector > &entries = _entries.write[i]; + + entries.sort_custom<_ModelEntryComparator>(); + } +} + +void CharacterSkeleton2D::build_model() { + call("_build_model"); +} + +void CharacterSkeleton2D::_build_model() { + set_process(false); +} + +Array CharacterSkeleton2D::merge_mesh_array(Array arr) const { + ERR_FAIL_COND_V(arr.size() != VisualServer::ARRAY_MAX, arr); + + PoolVector3Array verts = arr[VisualServer::ARRAY_VERTEX]; + PoolVector3Array normals = arr[VisualServer::ARRAY_NORMAL]; + PoolVector2Array uvs = arr[VisualServer::ARRAY_TEX_UV]; + PoolColorArray colors = arr[VisualServer::ARRAY_COLOR]; + PoolIntArray indices = arr[VisualServer::ARRAY_INDEX]; + PoolIntArray bones = arr[VisualServer::ARRAY_BONES]; + PoolRealArray weights = arr[VisualServer::ARRAY_WEIGHTS]; + + int i = 0; + while (i < verts.size()) { + Vector3 v = verts[i]; + + Array equals; + for (int j = i + 1; j < verts.size(); ++j) { + Vector3 vc = verts[j]; + + if (Math::is_equal_approx(v.x, vc.x) && Math::is_equal_approx(v.y, vc.y) && Math::is_equal_approx(v.z, vc.z)) + equals.push_back(j); + } + + for (int k = 0; k < equals.size(); ++k) { + int rem = equals[k]; + int remk = rem - k; + + verts.remove(remk); + normals.remove(remk); + uvs.remove(remk); + colors.remove(remk); + + int bindex = remk * 4; + for (int l = 0; l < 4; ++l) { + bones.remove(bindex); + weights.remove(bindex); + } + + for (int j = 0; j < indices.size(); ++j) { + int indx = indices[j]; + + if (indx == remk) + indices.set(j, i); + else if (indx > remk) + indices.set(j, indx - 1); + } + } + + ++i; + } + + arr[VisualServer::ARRAY_VERTEX] = verts; + arr[VisualServer::ARRAY_NORMAL] = normals; + arr[VisualServer::ARRAY_TEX_UV] = uvs; + arr[VisualServer::ARRAY_COLOR] = colors; + arr[VisualServer::ARRAY_INDEX] = indices; + arr[VisualServer::ARRAY_BONES] = bones; + arr[VisualServer::ARRAY_WEIGHTS] = weights; + + return arr; +} +Array CharacterSkeleton2D::bake_mesh_array_uv(Array arr, Ref tex, float mul_color) const { + ERR_FAIL_COND_V(arr.size() != VisualServer::ARRAY_MAX, arr); + ERR_FAIL_COND_V(!tex.is_valid(), arr); + + Ref img = tex->get_data(); + + ERR_FAIL_COND_V(!img.is_valid(), arr); + + Vector2 imgsize = img->get_size(); + + PoolVector2Array uvs = arr[VisualServer::ARRAY_TEX_UV]; + PoolColorArray colors = arr[VisualServer::ARRAY_COLOR]; + +#if !GODOT4 + img->lock(); +#endif + + for (int i = 0; i < uvs.size(); ++i) { + Vector2 uv = uvs[i]; + uv *= imgsize; + + Color c = img->get_pixelv(uv); + + colors.set(i, colors[i] * c * mul_color); + } + +#if !GODOT4 + img->unlock(); +#endif + + arr[VisualServer::ARRAY_COLOR] = colors; + + return arr; +} + CharacterSkeleton2D::CharacterSkeleton2D() { + _model_dirty = false; _model_index = 0; + _entity_type = 0; + + _animation_player = NULL; + _animation_tree = NULL; } CharacterSkeleton2D::~CharacterSkeleton2D() { + _attach_point_nodes.clear(); + + for (int i = 0; i < _entries.size(); ++i) { + _entries.write[i].clear(); + } + + _entries.clear(); + + _model_visuals.clear(); +} + +void CharacterSkeleton2D::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + update_nodes(); + } break; + case NOTIFICATION_PROCESS: { + if (_model_dirty) + build_model(); + } break; + case NOTIFICATION_EXIT_TREE: { + } break; + } +} + +bool CharacterSkeleton2D::_set(const StringName &p_name, const Variant &p_value) { + String name = p_name; + + if (name.begins_with("attach_point_paths")) { + int index = name.get_slicec('/', 1).get_slicec('_', 0).to_int(); + + if (index >= _attach_point_nodes.size()) { + _attach_point_nodes.resize(index + 1); + } + + NodePath np = p_value; + + _attach_point_nodes.write[index].path = np; + + if (is_inside_tree()) + _attach_point_nodes.write[index].node = get_node_or_null(p_value); + + return true; + } + + return false; +} +bool CharacterSkeleton2D::_get(const StringName &p_name, Variant &r_ret) const { + String name = p_name; + + if (name.begins_with("attach_point_paths")) { + int index = name.get_slicec('/', 1).get_slicec('_', 0).to_int(); + + if (index >= _attach_point_nodes.size()) { + return false; + } + + r_ret = _attach_point_nodes[index].path; + + return true; + } + + return false; +} +void CharacterSkeleton2D::_get_property_list(List *p_list) const { + if (ESS::get_singleton()->skeletons_bone_attachments_count() == 0) { + return; + } + + String bones = ESS::get_singleton()->skeletons_bone_attachment_index_get(_entity_type); + int slicec = bones.get_slice_count(","); + + for (int i = 0; i < slicec; ++i) { + p_list->push_back(PropertyInfo(Variant::NODE_PATH, "attach_point_paths/" + itos(i) + "_" + bones.get_slicec(',', i))); + } +} + +void CharacterSkeleton2D::_validate_property(PropertyInfo &property) const { + if (property.name == "entity_type") { + property.hint_string = ESS::get_singleton()->entity_types_get(); + } } void CharacterSkeleton2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_entity_type"), &CharacterSkeleton2D::get_entity_type); + ClassDB::bind_method(D_METHOD("set_entity_type", "value"), &CharacterSkeleton2D::set_entity_type); + ADD_PROPERTY(PropertyInfo(Variant::INT, "entity_type", PROPERTY_HINT_ENUM, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_entity_type", "get_entity_type"); + ClassDB::bind_method(D_METHOD("get_model_index"), &CharacterSkeleton2D::get_model_index); ClassDB::bind_method(D_METHOD("set_model_index", "value"), &CharacterSkeleton2D::set_model_index); ADD_PROPERTY(PropertyInfo(Variant::INT, "model_index"), "set_model_index", "get_model_index"); @@ -62,4 +550,53 @@ void CharacterSkeleton2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_model_visual", "index"), &CharacterSkeleton2D::get_model_visual); ClassDB::bind_method(D_METHOD("get_model_visual_count"), &CharacterSkeleton2D::get_model_visual_count); ClassDB::bind_method(D_METHOD("clear_model_visuals"), &CharacterSkeleton2D::clear_model_visuals); + + BIND_VMETHOD(MethodInfo("_build_model")); + + ClassDB::bind_method(D_METHOD("get_model_dirty"), &CharacterSkeleton2D::get_model_dirty); + ClassDB::bind_method(D_METHOD("set_model_dirty", "value"), &CharacterSkeleton2D::set_model_dirty); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "model_dirty"), "set_model_dirty", "get_model_dirty"); + + ClassDB::bind_method(D_METHOD("get_animation_player_path"), &CharacterSkeleton2D::get_animation_player_path); + ClassDB::bind_method(D_METHOD("set_animation_player_path", "path"), &CharacterSkeleton2D::set_animation_player_path); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "animation_player_path"), "set_animation_player_path", "get_animation_player_path"); + + ClassDB::bind_method(D_METHOD("get_animation_tree_path"), &CharacterSkeleton2D::get_animation_tree_path); + ClassDB::bind_method(D_METHOD("set_animation_tree_path", "path"), &CharacterSkeleton2D::set_animation_tree_path); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "animation_tree_path"), "set_animation_tree_path", "get_animation_tree_path"); + + ClassDB::bind_method(D_METHOD("add_model_visual_entry", "vis", "ive"), &CharacterSkeleton2D::add_model_visual_entry); + ClassDB::bind_method(D_METHOD("remove_model_visual_entry", "vis", "ive"), &CharacterSkeleton2D::remove_model_visual_entry); + ClassDB::bind_method(D_METHOD("get_model_entry", "bone_index", "index"), &CharacterSkeleton2D::get_model_entry); + ClassDB::bind_method(D_METHOD("get_model_entry_count", "bone_index"), &CharacterSkeleton2D::get_model_entry_count); + + ClassDB::bind_method(D_METHOD("sort_layers"), &CharacterSkeleton2D::sort_layers); + + ClassDB::bind_method(D_METHOD("build_model"), &CharacterSkeleton2D::build_model); + ClassDB::bind_method(D_METHOD("_build_model"), &CharacterSkeleton2D::_build_model); + + ClassDB::bind_method(D_METHOD("merge_mesh_array", "arr"), &CharacterSkeleton2D::merge_mesh_array); + ClassDB::bind_method(D_METHOD("bake_mesh_array_uv", "arr", "tex", "mul_color"), &CharacterSkeleton2D::bake_mesh_array_uv, DEFVAL(0.7)); + + //Bone Paths + ClassDB::bind_method(D_METHOD("attach_point_path_get", "index"), &CharacterSkeleton2D::attach_point_path_get); + ClassDB::bind_method(D_METHOD("attach_point_path_set", "index", "path"), &CharacterSkeleton2D::attach_point_path_set); + + ClassDB::bind_method(D_METHOD("attach_point_node_get", "index"), &CharacterSkeleton2D::attach_point_node_get); + + ClassDB::bind_method(D_METHOD("attach_point_count"), &CharacterSkeleton2D::attach_point_count); + + BIND_VMETHOD(MethodInfo("_common_attach_point_index_get", PropertyInfo(Variant::INT, "point", PROPERTY_HINT_NONE, EntityEnums::BINDING_STRING_COMMON_CHARCATER_SKELETON_POINTS))); + + ClassDB::bind_method(D_METHOD("common_attach_point_node_get", "point"), &CharacterSkeleton2D::common_attach_point_node_get); + ClassDB::bind_method(D_METHOD("common_attach_point_add", "point", "scene"), &CharacterSkeleton2D::common_attach_point_add); + ClassDB::bind_method(D_METHOD("common_attach_point_add_timed", "point", "scene", "time"), &CharacterSkeleton2D::common_attach_point_add_timed); + ClassDB::bind_method(D_METHOD("common_attach_point_remove", "point", "scene"), &CharacterSkeleton2D::common_attach_point_remove); + ClassDB::bind_method(D_METHOD("common_attach_point_index_get", "point"), &CharacterSkeleton2D::common_attach_point_index_get); + ClassDB::bind_method(D_METHOD("_common_attach_point_index_get", "point"), &CharacterSkeleton2D::_common_attach_point_index_get); + + ClassDB::bind_method(D_METHOD("get_animation_player"), &CharacterSkeleton2D::get_animation_player); + ClassDB::bind_method(D_METHOD("get_animation_tree"), &CharacterSkeleton2D::get_animation_tree); + + ClassDB::bind_method(D_METHOD("update_nodes"), &CharacterSkeleton2D::update_nodes); } diff --git a/skeleton/character_skeleton_2d.h b/skeleton/character_skeleton_2d.h index 5af4258..b3cb204 100644 --- a/skeleton/character_skeleton_2d.h +++ b/skeleton/character_skeleton_2d.h @@ -25,36 +25,135 @@ SOFTWARE. #include "scene/2d/node_2d.h" -#include "core/node_path.h" -#include "core/ustring.h" +#include "scene/main/node.h" + #include "core/vector.h" -#include "../entity_enums.h" +#include "core/node_path.h" +#include "core/ustring.h" +#include "scene/animation/animation_player.h" +#include "scene/animation/animation_tree.h" -#include "../data/items/model_visual.h" +#include "../entity_enums.h" +#include "skeleton_model_entry.h" + +#include "../data/items/model_visual_entry.h" + +#include "../defines.h" + +#include spatial_h_path + +#if GODOT4 +#define Texture Texture2D +#endif + +class ModelVisual; class CharacterSkeleton2D : public Node2D { GDCLASS(CharacterSkeleton2D, Node2D); public: - virtual void add_model_visual(Ref vis); - virtual void remove_model_visual(Ref vis); - virtual void remove_model_visual_index(int index); - virtual Ref get_model_visual(int index); - virtual int get_model_visual_count(); - virtual void clear_model_visuals(); + int get_entity_type() const; + void set_entity_type(const int value); int get_model_index(); void set_model_index(int value); + bool get_model_dirty() const; + void set_model_dirty(bool value); + + NodePath attach_point_path_get(const int index) const; + void attach_point_path_set(const int index, const NodePath &path); + + Node *attach_point_node_get(const int index); + + int attach_point_count() const; + + Node *common_attach_point_node_get(const EntityEnums::CommonCharacterSkeletonPoints point); + void common_attach_point_add(const EntityEnums::CommonCharacterSkeletonPoints point, const Ref &scene); + void common_attach_point_add_timed(const EntityEnums::CommonCharacterSkeletonPoints point, const Ref &scene, const float time); + void common_attach_point_remove(const EntityEnums::CommonCharacterSkeletonPoints point, const Ref &scene); + int common_attach_point_index_get(const EntityEnums::CommonCharacterSkeletonPoints point); + virtual int _common_attach_point_index_get(const EntityEnums::CommonCharacterSkeletonPoints point); + + NodePath get_animation_player_path(); + void set_animation_player_path(NodePath path); + + AnimationPlayer *get_animation_player(); + + NodePath get_animation_tree_path(); + void set_animation_tree_path(NodePath path); + + AnimationTree *get_animation_tree(); + + void update_nodes(); + + void add_model_visual(Ref vis); + void remove_model_visual(Ref vis); + void remove_model_visual_index(int index); + Ref get_model_visual(int index); + int get_model_visual_count(); + void clear_model_visuals(); + + void add_model_visual_entry(Ref vis, Ref ive); + void remove_model_visual_entry(Ref vis, Ref ive); + + Ref get_model_entry(const int bone_index, const int index); + int get_model_entry_count(const int bone_index); + + void sort_layers(); + + void build_model(); + void _build_model(); + + Array merge_mesh_array(Array arr) const; + Array bake_mesh_array_uv(Array arr, Ref tex, float mul_color = 0.7) const; + CharacterSkeleton2D(); ~CharacterSkeleton2D(); protected: + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List *p_list) const; + void _validate_property(PropertyInfo &property) const; static void _bind_methods(); + virtual void _notification(int p_notification); + +protected: + struct _ModelEntryComparator { + _FORCE_INLINE_ bool operator()(const Ref &a, const Ref &b) const { + if (!a.is_valid() || !b.is_valid()) { + return false; + } + + return (a->get_priority() > b->get_priority()); + } + }; + + struct AttachPointNode { + NodePath path; + Node *node; + + AttachPointNode() { + node = NULL; + } + }; private: + int _entity_type; int _model_index; + NodePath _animation_player_path; + NodePath _animation_tree_path; + + AnimationPlayer *_animation_player; + AnimationTree *_animation_tree; + + Vector _attach_point_nodes; + + bool _model_dirty; + Vector > _model_visuals; + Vector > > _entries; }; #endif