/*************************************************************************/ /* character_skeleton_2d.cpp */ /*************************************************************************/ /* This file is part of: */ /* PANDEMONIUM ENGINE */ /* https://github.com/Relintai/pandemonium_engine */ /*************************************************************************/ /* Copyright (c) 2022-present Péter Magyar. */ /* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ /* "Software"), to deal in the Software without restriction, including */ /* without limitation the rights to use, copy, modify, merge, publish, */ /* distribute, sublicense, and/or sell copies of the Software, and to */ /* permit persons to whom the Software is furnished to do so, subject to */ /* the following conditions: */ /* */ /* The above copyright notice and this permission notice shall be */ /* included in all copies or substantial portions of the Software. */ /* */ /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "character_skeleton_2d.h" #include "../singletons/ess.h" #include "../data/items/model_visual.h" #include "../defines.h" int CharacterSkeleton2D::get_entity_type() const { return _entity_type; } void CharacterSkeleton2D::set_entity_type(const int value) { _entity_type = value; if (ESS::get_singleton()) { 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() { return _model_index; } 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 (ObjectDB::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 (ObjectDB::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 (ObjectDB::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(); ERR_FAIL_INDEX(target_bone_idx, _entries.size()); 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(); ERR_FAIL_INDEX(target_bone_idx, _entries.size()); 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() != RenderingServer::ARRAY_MAX, arr); PoolVector3Array verts = arr[RenderingServer::ARRAY_VERTEX]; PoolVector3Array normals = arr[RenderingServer::ARRAY_NORMAL]; PoolVector2Array uvs = arr[RenderingServer::ARRAY_TEX_UV]; PoolColorArray colors = arr[RenderingServer::ARRAY_COLOR]; PoolIntArray indices = arr[RenderingServer::ARRAY_INDEX]; PoolIntArray bones = arr[RenderingServer::ARRAY_BONES]; PoolRealArray weights = arr[RenderingServer::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[RenderingServer::ARRAY_VERTEX] = verts; arr[RenderingServer::ARRAY_NORMAL] = normals; arr[RenderingServer::ARRAY_TEX_UV] = uvs; arr[RenderingServer::ARRAY_COLOR] = colors; arr[RenderingServer::ARRAY_INDEX] = indices; arr[RenderingServer::ARRAY_BONES] = bones; arr[RenderingServer::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() != RenderingServer::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[RenderingServer::ARRAY_TEX_UV]; PoolColorArray colors = arr[RenderingServer::ARRAY_COLOR]; img->lock(); 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); } img->unlock(); arr[RenderingServer::ARRAY_COLOR] = colors; return arr; } CharacterSkeleton2D::CharacterSkeleton2D() { set_sort_enabled(false); _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"); ClassDB::bind_method(D_METHOD("add_model_visual", "vis"), &CharacterSkeleton2D::add_model_visual); ClassDB::bind_method(D_METHOD("remove_model_visual", "vis"), &CharacterSkeleton2D::remove_model_visual); ClassDB::bind_method(D_METHOD("remove_model_visual_index", "index"), &CharacterSkeleton2D::remove_model_visual_index); 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); }