procedural_animations/procedural_animation.cpp

607 lines
21 KiB
C++
Raw Normal View History

#include "procedural_animation.h"
2020-01-27 03:04:53 +01:00
//Categories
PoolVector<int> ProceduralAnimation::get_category_indices() const {
PoolVector<int> idxr;
idxr.resize(_categories.size());
int i = 0;
for (Map<int, Category *>::Element *E = _categories.front(); E; E = E->next()) {
idxr.set(i, E->key());
++i;
}
2020-01-27 03:04:53 +01:00
return idxr;
}
2020-01-27 03:04:53 +01:00
int ProceduralAnimation::add_category(const String &name) {
Category *cat = memnew(Category);
cat->name = name;
int key = -1;
for (Map<int, Category *>::Element *E = _categories.front(); E; E = E->next()) {
if (E->key() > key) {
key = E->key();
}
}
2020-01-27 03:04:53 +01:00
++key;
_categories[key] = cat;
return key;
}
void ProceduralAnimation::remove_category(const int index) {
ERR_FAIL_COND(!_categories.has(index));
Category *category = _categories[index];
_categories.erase(index);
memdelete(category);
}
String ProceduralAnimation::get_category_name(const int category_index) const {
ERR_FAIL_COND_V(!_categories.has(category_index), "");
return _categories[category_index]->name;
}
void ProceduralAnimation::set_category_name(const int category_index, const String &value) {
ERR_FAIL_COND(!_categories.has(category_index));
_categories[category_index]->name = value;
}
//Animations
PoolVector<int> ProceduralAnimation::get_animation_indices(const int category_index) const {
ERR_FAIL_COND_V(!_categories.has(category_index), PoolVector<int>());
Category *cat = _categories[category_index];
2020-01-27 03:04:53 +01:00
PoolVector<int> idxr;
idxr.resize(cat->animations.size());
int i = 0;
for (Map<int, AnimationEntry *>::Element *E = cat->animations.front(); E; E = E->next()) {
idxr.set(i, E->key());
++i;
}
return idxr;
}
2020-01-27 03:04:53 +01:00
int ProceduralAnimation::add_animation(const int category_index) {
ERR_FAIL_COND_V(!_categories.has(category_index), 0);
2020-01-27 03:04:53 +01:00
Category *cat = _categories[category_index];
2020-01-27 03:04:53 +01:00
int key = -1;
for (Map<int, AnimationEntry *>::Element *E = cat->animations.front(); E; E = E->next()) {
if (E->key() > key) {
key = E->key();
}
}
2020-01-27 03:04:53 +01:00
++key;
AnimationEntry *entry = memnew(AnimationEntry);
cat->animations[key] = entry;
2020-01-27 03:04:53 +01:00
return key;
}
2020-01-27 03:04:53 +01:00
void ProceduralAnimation::remove_animation(const int category_index, const int animation_index) {
ERR_FAIL_COND(!_categories.has(category_index));
2020-01-27 03:04:53 +01:00
Category *cat = _categories[category_index];
ERR_FAIL_COND(!cat->animations.has(animation_index));
AnimationEntry *entry = cat->animations[animation_index];
cat->animations.erase(animation_index);
memdelete(entry);
}
String ProceduralAnimation::get_animation_name(const int category_index, const int animation_index) {
ERR_FAIL_COND_V(!_categories.has(category_index), "");
Category *cat = _categories[category_index];
ERR_FAIL_COND_V(!cat->animations.has(animation_index), "");
return cat->animations[animation_index]->name;
}
void ProceduralAnimation::set_animation_name(const int category_index, const int animation_index, const String &value) {
ERR_FAIL_COND(!_categories.has(category_index));
Category *cat = _categories[category_index];
ERR_FAIL_COND(!cat->animations.has(animation_index));
cat->animations[animation_index]->name = value;
}
Vector2 ProceduralAnimation::get_animation_node_position(const int category_index, int animation_index) const {
ERR_FAIL_COND_V(!_categories.has(category_index), Vector2());
Category *cat = _categories[category_index];
ERR_FAIL_COND_V(!cat->animations.has(animation_index), Vector2());
return cat->animations[animation_index]->position;
}
void ProceduralAnimation::set_animation_node_position(const int category_index, const int animation_index, const Vector2 &value) {
ERR_FAIL_COND(!_categories.has(category_index));
Category *cat = _categories[category_index];
ERR_FAIL_COND(!cat->animations.has(animation_index));
cat->animations[animation_index]->position = value;
}
int ProceduralAnimation::get_start_frame_index(const int category_index, const int animation_index) const {
ERR_FAIL_COND_V(!_categories.has(category_index), 0);
Category *cat = _categories[category_index];
ERR_FAIL_COND_V(!cat->animations.has(animation_index), 0);
return cat->animations[animation_index]->start_frame_index;
}
void ProceduralAnimation::set_start_frame_index(const int category_index, const int animation_index, const int value) {
ERR_FAIL_COND(!_categories.has(category_index));
Category *cat = _categories[category_index];
ERR_FAIL_COND(!cat->animations.has(animation_index));
cat->animations[animation_index]->start_frame_index = value;
}
//Keyframes
PoolVector<int> ProceduralAnimation::get_keyframe_indices(const int category_index, const int animation_index) const {
ERR_FAIL_COND_V(!_categories.has(category_index), PoolVector<int>());
Category *cat = _categories[category_index];
ERR_FAIL_COND_V(!cat->animations.has(animation_index), PoolVector<int>());
AnimationEntry *ae = cat->animations[animation_index];
2020-01-27 03:04:53 +01:00
PoolVector<int> idxr;
idxr.resize(ae->keyframes.size());
int i = 0;
for (Map<int, AnimationKeyFrame *>::Element *E = ae->keyframes.front(); E; E = E->next()) {
idxr.set(i, E->key());
++i;
}
return idxr;
}
int ProceduralAnimation::add_keyframe(const int category_index, const int animation_index) {
ERR_FAIL_COND_V(!_categories.has(category_index), 0);
Category *cat = _categories[category_index];
ERR_FAIL_COND_V(!cat->animations.has(animation_index), 0);
AnimationEntry *ae = cat->animations[animation_index];
2020-01-27 03:04:53 +01:00
int key = -1;
for (Map<int, AnimationKeyFrame *>::Element *E = ae->keyframes.front(); E; E = E->next()) {
if (E->key() > key) {
key = E->key();
}
}
2020-01-27 03:04:53 +01:00
++key;
AnimationKeyFrame *entry = memnew(AnimationKeyFrame);
2020-01-27 03:04:53 +01:00
ae->keyframes[key] = entry;
return key;
}
2020-01-27 03:04:53 +01:00
void ProceduralAnimation::remove_keyframe(const int category_index, const int animation_index, const int keyframe_index) {
ERR_FAIL_COND(!_categories.has(category_index));
Category *cat = _categories[category_index];
ERR_FAIL_COND(!cat->animations.has(animation_index));
AnimationEntry *ae = cat->animations[animation_index];
2020-01-27 03:04:53 +01:00
ERR_FAIL_COND(!ae->keyframes.has(keyframe_index));
AnimationKeyFrame *entry = ae->keyframes[keyframe_index];
2020-01-27 03:04:53 +01:00
cat->animations.erase(keyframe_index);
memdelete(entry);
}
2020-01-27 03:04:53 +01:00
String ProceduralAnimation::get_keyframe_name(const int category_index, const int animation_index, const int keyframe_index) {
ERR_FAIL_COND_V(!_categories.has(category_index), "");
Category *cat = _categories[category_index];
ERR_FAIL_COND_V(!cat->animations.has(animation_index), "");
AnimationEntry *ae = cat->animations[animation_index];
ERR_FAIL_COND_V(!ae->keyframes.has(keyframe_index), "");
return ae->keyframes[keyframe_index]->name;
}
2020-01-27 03:04:53 +01:00
void ProceduralAnimation::set_keyframe_name(const int category_index, const int animation_index, const int keyframe_index, const String &value) {
ERR_FAIL_COND(!_categories.has(category_index));
Category *cat = _categories[category_index];
ERR_FAIL_COND(!cat->animations.has(animation_index));
AnimationEntry *ae = cat->animations[animation_index];
ERR_FAIL_COND(!ae->keyframes.has(keyframe_index));
ae->keyframes[keyframe_index]->name = value;
}
2020-01-27 03:04:53 +01:00
int ProceduralAnimation::get_keyframe_animation_keyframe_index(const int category_index, int animation_index, const int keyframe_index) const {
ERR_FAIL_COND_V(!_categories.has(category_index), 0);
Category *cat = _categories[category_index];
ERR_FAIL_COND_V(!cat->animations.has(animation_index), 0);
AnimationEntry *ae = cat->animations[animation_index];
ERR_FAIL_COND_V(!ae->keyframes.has(keyframe_index), 0);
return ae->keyframes[keyframe_index]->animation_keyframe_index;
}
2020-01-27 03:04:53 +01:00
void ProceduralAnimation::set_keyframe_animation_keyframe_index(const int category_index, int animation_index, const int keyframe_index, int value) {
ERR_FAIL_COND(!_categories.has(category_index));
Category *cat = _categories[category_index];
ERR_FAIL_COND(!cat->animations.has(animation_index));
AnimationEntry *ae = cat->animations[animation_index];
ERR_FAIL_COND(!ae->keyframes.has(keyframe_index));
ae->keyframes[keyframe_index]->animation_keyframe_index = value;
}
2020-01-27 03:04:53 +01:00
int ProceduralAnimation::get_keyframe_next_keyframe_index(const int category_index, const int animation_index, const int keyframe_index) const {
ERR_FAIL_COND_V(!_categories.has(category_index), 0);
Category *cat = _categories[category_index];
ERR_FAIL_COND_V(!cat->animations.has(animation_index), 0);
AnimationEntry *ae = cat->animations[animation_index];
ERR_FAIL_COND_V(!ae->keyframes.has(keyframe_index), 0);
return ae->keyframes[keyframe_index]->next_keyframe;
}
2020-01-27 03:04:53 +01:00
void ProceduralAnimation::set_keyframe_next_keyframe_index(const int category_index, const int animation_index, const int keyframe_index, const int value) {
ERR_FAIL_COND(!_categories.has(category_index));
Category *cat = _categories[category_index];
ERR_FAIL_COND(!cat->animations.has(animation_index));
AnimationEntry *ae = cat->animations[animation_index];
ERR_FAIL_COND(!ae->keyframes.has(keyframe_index));
ae->keyframes[keyframe_index]->next_keyframe = value;
}
2020-01-27 03:04:53 +01:00
Ref<Curve> ProceduralAnimation::get_keyframe_in_curve(const int category_index, const int animation_index, const int keyframe_index) const {
ERR_FAIL_COND_V(!_categories.has(category_index), Ref<Curve>());
Category *cat = _categories[category_index];
ERR_FAIL_COND_V(!cat->animations.has(animation_index), Ref<Curve>());
AnimationEntry *ae = cat->animations[animation_index];
ERR_FAIL_COND_V(!ae->keyframes.has(keyframe_index), Ref<Curve>());
return ae->keyframes[keyframe_index]->in_curve;
}
2020-01-27 03:04:53 +01:00
void ProceduralAnimation::set_keyframe_in_curve(const int category_index, const int animation_index, const int keyframe_index, const Ref<Curve> &value) {
ERR_FAIL_COND(!_categories.has(category_index));
Category *cat = _categories[category_index];
ERR_FAIL_COND(!cat->animations.has(animation_index));
AnimationEntry *ae = cat->animations[animation_index];
ERR_FAIL_COND(!ae->keyframes.has(keyframe_index));
ae->keyframes[keyframe_index]->in_curve = value;
}
2020-01-27 03:04:53 +01:00
Vector2 ProceduralAnimation::get_keyframe_node_position(const int category_index, int animation_index, const int keyframe_index) const {
ERR_FAIL_COND_V(!_categories.has(category_index), Vector2());
Category *cat = _categories[category_index];
ERR_FAIL_COND_V(!cat->animations.has(animation_index), Vector2());
AnimationEntry *ae = cat->animations[animation_index];
ERR_FAIL_COND_V(!ae->keyframes.has(keyframe_index), Vector2());
return ae->keyframes[keyframe_index]->position;
2020-01-27 03:04:53 +01:00
}
void ProceduralAnimation::set_keyframe_node_position(const int category_index, const int animation_index, const int keyframe_index, const Vector2 &value) {
ERR_FAIL_COND(!_categories.has(category_index));
Category *cat = _categories[category_index];
ERR_FAIL_COND(!cat->animations.has(animation_index));
AnimationEntry *ae = cat->animations[animation_index];
ERR_FAIL_COND(!ae->keyframes.has(keyframe_index));
ae->keyframes[keyframe_index]->position = value;
2020-01-27 03:04:53 +01:00
}
ProceduralAnimation::ProceduralAnimation() {
}
2020-01-27 03:04:53 +01:00
ProceduralAnimation::~ProceduralAnimation() {
for (Map<int, Category *>::Element *E = _categories.front(); E; E = E->next()) {
memdelete(E->get());
}
_categories.clear();
_animation.unref();
}
bool ProceduralAnimation::_set(const StringName &p_name, const Variant &p_value) {
String name = p_name;
2020-01-27 03:04:53 +01:00
if (name.begins_with("categories/")) {
int category_index = name.get_slicec('/', 1).to_int();
String what = name.get_slicec('/', 2);
2020-01-27 03:04:53 +01:00
if (!_categories.has(category_index)) {
Category *cat = memnew(Category);
2020-01-27 03:04:53 +01:00
_categories[category_index] = cat;
}
2020-01-27 03:04:53 +01:00
Category *cat = _categories[category_index];
if (what == "name") {
cat->name = p_value;
return true;
} else if (what == "animation") {
2020-01-27 03:04:53 +01:00
int animation_index = name.get_slicec('/', 3).to_int();
String anim_prop_name = name.get_slicec('/', 4);
if (!cat->animations.has(animation_index)) {
2020-01-27 03:04:53 +01:00
AnimationEntry *ae = memnew(AnimationEntry);
2020-01-27 03:04:53 +01:00
cat->animations[animation_index] = ae;
}
2020-01-27 03:04:53 +01:00
AnimationEntry *ae = cat->animations[animation_index];
if (anim_prop_name == "name") {
2020-01-27 03:04:53 +01:00
ae->name = p_value;
return true;
2020-01-27 03:04:53 +01:00
} else if (anim_prop_name == "position") {
ae->position = p_value;
return true;
2020-01-27 03:04:53 +01:00
} else if (anim_prop_name == "start_frame_index") {
ae->start_frame_index = p_value;
return true;
2020-01-27 03:04:53 +01:00
} else if (anim_prop_name == "keyframe") {
int keyframe_index = name.get_slicec('/', 5).to_int();
String keyframe_name = name.get_slicec('/', 6);
if (!ae->keyframes.has(keyframe_index)) {
2020-01-27 03:04:53 +01:00
AnimationKeyFrame *keyframe = memnew(AnimationKeyFrame);
2020-01-27 03:04:53 +01:00
ae->keyframes[keyframe_index] = keyframe;
}
AnimationKeyFrame *keyframe = ae->keyframes[keyframe_index];
if (keyframe_name == "name") {
keyframe->name = p_value;
return true;
} else if (keyframe_name == "animation_keyframe_index") {
keyframe->animation_keyframe_index = p_value;
return true;
} else if (keyframe_name == "next_keyframe") {
keyframe->next_keyframe = p_value;
return true;
} else if (keyframe_name == "in_curve") {
keyframe->in_curve = p_value;
return true;
} else if (keyframe_name == "position") {
keyframe->position = p_value;
return true;
} else {
return false;
}
} else {
return false;
}
}
} else {
return false;
}
return true;
}
bool ProceduralAnimation::_get(const StringName &p_name, Variant &r_ret) const {
String name = p_name;
2020-01-27 03:04:53 +01:00
if (name.begins_with("categories/")) {
2020-01-27 03:04:53 +01:00
int category_index = name.get_slicec('/', 1).to_int();
String what = name.get_slicec('/', 2);
2020-01-27 03:04:53 +01:00
Category *category = _categories[category_index];
if (what == "name") {
2020-01-27 03:04:53 +01:00
r_ret = category->name;
return true;
} else if (what == "animation") {
2020-01-27 03:04:53 +01:00
int animation_index = name.get_slicec('/', 3).to_int();
String anim_prop_name = name.get_slicec('/', 4);
2020-01-27 03:04:53 +01:00
AnimationEntry *anim = category->animations[animation_index];
if (anim_prop_name == "name") {
2020-01-27 03:04:53 +01:00
r_ret = anim->name;
return true;
2020-01-27 03:04:53 +01:00
} else if (anim_prop_name == "position") {
r_ret = anim->position;
return true;
2020-01-27 03:04:53 +01:00
} else if (anim_prop_name == "start_frame_index") {
r_ret = anim->start_frame_index;
return true;
2020-01-27 03:04:53 +01:00
} else if (anim_prop_name == "keyframe") {
int keyframe_index = name.get_slicec('/', 5).to_int();
String keyframe_prop_name = name.get_slicec('/', 6);
2020-01-27 03:04:53 +01:00
AnimationKeyFrame *keyframe = anim->keyframes[keyframe_index];
2020-01-27 03:04:53 +01:00
if (keyframe_prop_name == "name") {
r_ret = keyframe->name;
return true;
} else if (keyframe_prop_name == "animation_keyframe_index") {
r_ret = keyframe->animation_keyframe_index;
return true;
} else if (keyframe_prop_name == "next_keyframe") {
r_ret = keyframe->next_keyframe;
return true;
} else if (keyframe_prop_name == "in_curve") {
r_ret = keyframe->in_curve;
return true;
} else if (keyframe_prop_name == "position") {
r_ret = keyframe->position;
return true;
} else {
return false;
}
} else {
return false;
}
2020-01-27 03:04:53 +01:00
} else {
return false;
}
} else {
return false;
}
return true;
}
2020-01-27 03:04:53 +01:00
void ProceduralAnimation::_get_property_list(List<PropertyInfo> *p_list) const {
//int property_usange = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL;
int property_usange = PROPERTY_USAGE_DEFAULT;
2020-01-27 03:04:53 +01:00
for (Map<int, Category *>::Element *E = _categories.front(); E; E = E->next()) {
Category *category = E->get();
2020-01-27 03:04:53 +01:00
p_list->push_back(PropertyInfo(Variant::STRING, "categories/" + itos(E->key()) + "/name", PROPERTY_HINT_NONE, "", property_usange));
2020-01-27 03:04:53 +01:00
for (Map<int, AnimationEntry *>::Element *A = category->animations.front(); A; A = A->next()) {
AnimationEntry *animation = A->get();
2020-01-27 03:04:53 +01:00
p_list->push_back(PropertyInfo(Variant::STRING, "categories/" + itos(E->key()) + "/animation/" + itos(A->key()) + "/name", PROPERTY_HINT_NONE, "", property_usange));
p_list->push_back(PropertyInfo(Variant::VECTOR2, "categories/" + itos(E->key()) + "/animation/" + itos(A->key()) + "/position", PROPERTY_HINT_NONE, "", property_usange));
p_list->push_back(PropertyInfo(Variant::INT, "categories/" + itos(E->key()) + "/animation/" + itos(A->key()) + "/start_frame_index", PROPERTY_HINT_NONE, "", property_usange));
2020-01-27 03:04:53 +01:00
for (Map<int, AnimationKeyFrame *>::Element *K = animation->keyframes.front(); K; K = K->next()) {
p_list->push_back(PropertyInfo(Variant::STRING, "categories/" + itos(E->key()) + "/animation/" + itos(A->key()) + "/keyframe/" + itos(K->key()) + "/name", PROPERTY_HINT_NONE, "", property_usange));
p_list->push_back(PropertyInfo(Variant::INT, "categories/" + itos(E->key()) + "/animation/" + itos(A->key()) + "/keyframe/" + itos(K->key()) + "/animation_keyframe_index", PROPERTY_HINT_NONE, "", property_usange));
p_list->push_back(PropertyInfo(Variant::INT, "categories/" + itos(E->key()) + "/animation/" + itos(A->key()) + "/keyframe/" + itos(K->key()) + "/next_keyframe", PROPERTY_HINT_NONE, "", property_usange));
p_list->push_back(PropertyInfo(Variant::INT, "categories/" + itos(E->key()) + "/animation/" + itos(A->key()) + "/keyframe/" + itos(K->key()) + "/next_animation", PROPERTY_HINT_NONE, "", property_usange));
p_list->push_back(PropertyInfo(Variant::OBJECT, "categories/" + itos(E->key()) + "/animation/" + itos(A->key()) + "/keyframe/" + itos(K->key()) + "/in_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve", property_usange));
p_list->push_back(PropertyInfo(Variant::VECTOR2, "categories/" + itos(E->key()) + "/animation/" + itos(A->key()) + "/keyframe/" + itos(K->key()) + "/position", PROPERTY_HINT_NONE, "", property_usange));
}
}
}
}
void ProceduralAnimation::_bind_methods() {
//Categories
ClassDB::bind_method(D_METHOD("get_category_indices"), &ProceduralAnimation::get_category_indices);
ClassDB::bind_method(D_METHOD("add_category", "name"), &ProceduralAnimation::add_category);
ClassDB::bind_method(D_METHOD("remove_category", "index"), &ProceduralAnimation::remove_category);
ClassDB::bind_method(D_METHOD("get_category_name", "category_index"), &ProceduralAnimation::get_category_name);
ClassDB::bind_method(D_METHOD("set_category_name", "category_index"), &ProceduralAnimation::set_category_name);
//Animations
ClassDB::bind_method(D_METHOD("get_animation_indices", "category_index"), &ProceduralAnimation::get_animation_indices);
ClassDB::bind_method(D_METHOD("add_animation", "category_index"), &ProceduralAnimation::add_animation);
ClassDB::bind_method(D_METHOD("remove_animation", "category_index", "animation_index"), &ProceduralAnimation::remove_animation);
ClassDB::bind_method(D_METHOD("get_animation_name", "category_index", "animation_index"), &ProceduralAnimation::get_animation_name);
ClassDB::bind_method(D_METHOD("set_animation_name", "category_index", "animation_index", "value"), &ProceduralAnimation::set_animation_name);
ClassDB::bind_method(D_METHOD("get_animation_node_position", "category_index", "animation_index"), &ProceduralAnimation::get_animation_node_position);
ClassDB::bind_method(D_METHOD("set_animation_node_position", "category_index", "animation_index", "value"), &ProceduralAnimation::set_animation_node_position);
ClassDB::bind_method(D_METHOD("get_start_frame_index", "category_index", "animation_index"), &ProceduralAnimation::get_start_frame_index);
ClassDB::bind_method(D_METHOD("set_start_frame_index", "category_index", "animation_index", "value"), &ProceduralAnimation::set_start_frame_index);
//Keyframes
ClassDB::bind_method(D_METHOD("get_keyframe_indices", "category_index", "animation_index"), &ProceduralAnimation::get_keyframe_indices);
ClassDB::bind_method(D_METHOD("add_keyframe", "category_index", "animation_index"), &ProceduralAnimation::add_keyframe);
ClassDB::bind_method(D_METHOD("remove_keyframe", "category_index", "animation_index", "keyframe_index"), &ProceduralAnimation::remove_keyframe);
ClassDB::bind_method(D_METHOD("get_keyframe_name", "category_index", "animation_index", "keyframe_index"), &ProceduralAnimation::get_keyframe_name);
ClassDB::bind_method(D_METHOD("set_keyframe_name", "category_index", "animation_index", "keyframe_index", "value"), &ProceduralAnimation::set_keyframe_name);
ClassDB::bind_method(D_METHOD("get_keyframe_animation_keyframe_index", "category_index", "animation_index", "keyframe_index"), &ProceduralAnimation::get_keyframe_animation_keyframe_index);
ClassDB::bind_method(D_METHOD("set_keyframe_animation_keyframe_index", "category_index", "animation_index", "keyframe_index", "value"), &ProceduralAnimation::set_keyframe_animation_keyframe_index);
ClassDB::bind_method(D_METHOD("get_keyframe_next_keyframe_index", "category_index", "animation_index", "keyframe_index"), &ProceduralAnimation::get_keyframe_next_keyframe_index);
ClassDB::bind_method(D_METHOD("set_keyframe_next_keyframe_index", "category_index", "animation_index", "keyframe_index", "value"), &ProceduralAnimation::set_keyframe_next_keyframe_index);
ClassDB::bind_method(D_METHOD("get_keyframe_in_curve", "category_index", "animation_index", "keyframe_index"), &ProceduralAnimation::get_keyframe_in_curve);
ClassDB::bind_method(D_METHOD("set_keyframe_in_curve", "category_index", "animation_index", "keyframe_index", "value"), &ProceduralAnimation::set_keyframe_in_curve);
ClassDB::bind_method(D_METHOD("get_keyframe_node_position", "category_index", "animation_index", "keyframe_index"), &ProceduralAnimation::get_keyframe_node_position);
ClassDB::bind_method(D_METHOD("set_keyframe_node_position", "category_index", "animation_index", "keyframe_index", "value"), &ProceduralAnimation::set_keyframe_node_position);
}