pandemonium_engine/modules/entity_spell_system/skeleton/character_skeleton_2d.cpp

616 lines
20 KiB
C++

/*************************************************************************/
/* 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<PackedScene> &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<PackedScene> &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<PackedScene> &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<AnimationPlayer>(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<AnimationTree>(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<ModelVisual> vis) {
ERR_FAIL_COND(!vis.is_valid());
for (int i = 0; i < vis->get_visual_entry_count(); ++i) {
Ref<ModelVisualEntry> 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<ModelVisual> 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<ModelVisualEntry> 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<ModelVisual> CharacterSkeleton2D::get_model_visual(int index) {
ERR_FAIL_INDEX_V(index, _model_visuals.size(), Ref<ModelVisual>());
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<ModelVisual> vis, Ref<ModelVisualEntry> 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<EntityEnums::CommonCharacterSkeletonPoints>(ive->get_bone());
for (int i = 0; i < ive->get_size(); ++i) {
Ref<PackedScene> 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<Ref<SkeletonModelEntry>> &entries = _entries.write[target_bone_idx];
for (int i = 0; i < entries.size(); ++i) {
Ref<SkeletonModelEntry> e = entries.get(i);
if (e->get_entry() == ive) {
e->set_count(e->get_count() + 1);
return;
}
}
Ref<SkeletonModelEntry> 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<ModelVisual> vis, Ref<ModelVisualEntry> 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<EntityEnums::CommonCharacterSkeletonPoints>(ive->get_bone());
for (int i = 0; i < ive->get_size(); ++i) {
Ref<PackedScene> 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<Ref<SkeletonModelEntry>> &entries = _entries.write[target_bone_idx];
for (int i = 0; i < entries.size(); ++i) {
Ref<SkeletonModelEntry> 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<SkeletonModelEntry> CharacterSkeleton2D::get_model_entry(const int bone_index, const int index) {
ERR_FAIL_INDEX_V(bone_index, _entries.size(), Ref<SkeletonModelEntry>());
ERR_FAIL_INDEX_V(index, _entries[bone_index].size(), Ref<SkeletonModelEntry>());
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<Ref<SkeletonModelEntry>> &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<Texture> tex, float mul_color) const {
ERR_FAIL_COND_V(arr.size() != RenderingServer::ARRAY_MAX, arr);
ERR_FAIL_COND_V(!tex.is_valid(), arr);
Ref<Image> 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<PropertyInfo> *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);
}