Merge 090f7340aa
into 7211012c4f
This commit is contained in:
commit
933c626094
9
modules/motionmatch/SCsub
Normal file
9
modules/motionmatch/SCsub
Normal file
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import('env')
|
||||
Import('env_modules')
|
||||
|
||||
env_mm = env_modules.Clone()
|
||||
|
||||
# Godot source files
|
||||
env_mm.add_source_files(env.modules_sources, "*.cpp")
|
419
modules/motionmatch/animation_motion_match_editor.cpp
Normal file
419
modules/motionmatch/animation_motion_match_editor.cpp
Normal file
|
@ -0,0 +1,419 @@
|
|||
#include "animation_motion_match_editor.h"
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "core/io/resource_loader.h"
|
||||
#include "editor/editor_scale.h"
|
||||
|
||||
bool AnimationNodeMotionMatchEditor::can_edit(
|
||||
const Ref<AnimationNode> &p_node) {
|
||||
Ref<AnimationNodeMotionMatch> anmm = p_node;
|
||||
return anmm.is_valid();
|
||||
}
|
||||
|
||||
void AnimationNodeMotionMatchEditor::edit(const Ref<AnimationNode> &p_node) {
|
||||
motion_match = p_node;
|
||||
|
||||
if (motion_match.is_valid()) {
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationNodeMotionMatchEditor::_match_tracks_edited() {
|
||||
if (updating) {
|
||||
return;
|
||||
}
|
||||
|
||||
TreeItem *edited = match_tracks->get_edited();
|
||||
ERR_FAIL_COND(!edited);
|
||||
|
||||
NodePath edited_path = edited->get_metadata(0);
|
||||
bool matched = edited->is_checked(0);
|
||||
|
||||
UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
|
||||
|
||||
updating = true;
|
||||
undo_redo->create_action(TTR("Change Match Track"));
|
||||
if (matched) {
|
||||
undo_redo->add_do_method(motion_match.ptr(), "add_matching_track",
|
||||
edited_path);
|
||||
undo_redo->add_undo_method(motion_match.ptr(), "remove_matching_track",
|
||||
edited_path);
|
||||
} else {
|
||||
undo_redo->add_do_method(motion_match.ptr(), "remove_matching_track",
|
||||
edited_path);
|
||||
undo_redo->add_undo_method(motion_match.ptr(), "add_matching_track",
|
||||
edited_path);
|
||||
}
|
||||
undo_redo->add_do_method(this, "_update_match_tracks");
|
||||
undo_redo->add_undo_method(this, "_update_match_tracks");
|
||||
undo_redo->commit_action();
|
||||
updating = false;
|
||||
}
|
||||
|
||||
void AnimationNodeMotionMatchEditor::_edit_match_tracks() {
|
||||
_update_match_tracks();
|
||||
motion_match->editing = true;
|
||||
match_tracks_dialog->popup_centered_clamped(Size2(500, 500) * EDSCALE);
|
||||
}
|
||||
|
||||
void AnimationNodeMotionMatchEditor::_clear_tree() {
|
||||
motion_match->clear_keys();
|
||||
}
|
||||
|
||||
void AnimationNodeMotionMatchEditor::_update_match_tracks() {
|
||||
if (!is_visible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (updating) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*Checking for errors*/
|
||||
|
||||
NodePath player_path =
|
||||
AnimationTreeEditor::get_singleton()->get_tree()->get_animation_player();
|
||||
if (!AnimationTreeEditor::get_singleton()->get_tree()->has_node(
|
||||
player_path)) {
|
||||
EditorNode::get_singleton()->show_warning(
|
||||
TTR("No animation player set, so unable to retrieve track names."));
|
||||
return;
|
||||
}
|
||||
|
||||
AnimationPlayer *player = Object::cast_to<AnimationPlayer>(
|
||||
AnimationTreeEditor::get_singleton()->get_tree()->get_node(player_path));
|
||||
if (!player) {
|
||||
EditorNode::get_singleton()->show_warning(
|
||||
TTR("Player path set is invalid, so unable to retrieve track names."));
|
||||
return;
|
||||
}
|
||||
|
||||
Node *base = player->get_node(player->get_root());
|
||||
if (!base) {
|
||||
EditorNode::get_singleton()->show_warning(
|
||||
TTR("Animation player has no valid root node path, so unable to "
|
||||
"retrieve track names."));
|
||||
return;
|
||||
}
|
||||
/**/
|
||||
|
||||
/*Get the list of all bones and display it*/
|
||||
updating = true;
|
||||
|
||||
Set<String> paths;
|
||||
List<StringName> animations;
|
||||
player->get_animation_list(&animations);
|
||||
|
||||
for (List<StringName>::Element *E = animations.front(); E; E = E->next()) {
|
||||
Ref<Animation> anim = player->get_animation(E->get());
|
||||
for (int i = 0; i < anim->get_track_count(); i++) {
|
||||
paths.insert(anim->track_get_path(i));
|
||||
}
|
||||
}
|
||||
|
||||
match_tracks->clear();
|
||||
TreeItem *root = match_tracks->create_item();
|
||||
|
||||
Map<String, TreeItem *> parenthood;
|
||||
|
||||
for (Set<String>::Element *E = paths.front(); E; E = E->next()) {
|
||||
NodePath path = E->get();
|
||||
TreeItem *ti = NULL;
|
||||
String accum;
|
||||
for (int i = 0; i < path.get_name_count(); i++) {
|
||||
String name = path.get_name(i);
|
||||
if (accum != String()) {
|
||||
accum += "/";
|
||||
}
|
||||
accum += name;
|
||||
if (!parenthood.has(accum)) {
|
||||
if (ti) {
|
||||
ti = match_tracks->create_item(ti);
|
||||
} else {
|
||||
ti = match_tracks->create_item(root);
|
||||
}
|
||||
parenthood[accum] = ti;
|
||||
ti->set_text(0, name);
|
||||
ti->set_selectable(0, false);
|
||||
ti->set_editable(0, false);
|
||||
if (base->has_node(accum)) {
|
||||
Node *node = base->get_node(accum);
|
||||
ti->set_icon(
|
||||
0, EditorNode::get_singleton()->get_object_icon(node, "Node"));
|
||||
}
|
||||
|
||||
} else {
|
||||
ti = parenthood[accum];
|
||||
}
|
||||
}
|
||||
Node *node = NULL;
|
||||
if (base->has_node(accum)) {
|
||||
node = base->get_node(accum);
|
||||
}
|
||||
if (!node)
|
||||
continue; // no node, can't edit
|
||||
|
||||
if (path.get_subname_count()) {
|
||||
String concat = path.get_concatenated_subnames();
|
||||
this->skeleton = Object::cast_to<Skeleton3D>(node);
|
||||
if (skeleton && skeleton->find_bone(concat) != -1) {
|
||||
// path in skeleton
|
||||
String bone = concat;
|
||||
int idx = skeleton->find_bone(bone);
|
||||
List<String> bone_path;
|
||||
while (idx != -1) {
|
||||
bone_path.push_front(skeleton->get_bone_name(idx));
|
||||
idx = skeleton->get_bone_parent(idx);
|
||||
}
|
||||
|
||||
accum += ":";
|
||||
for (List<String>::Element *F = bone_path.front(); F; F = F->next()) {
|
||||
if (F != bone_path.front()) {
|
||||
accum += "/";
|
||||
}
|
||||
|
||||
accum += F->get();
|
||||
if (!parenthood.has(accum)) {
|
||||
ti = match_tracks->create_item(ti);
|
||||
parenthood[accum] = ti;
|
||||
ti->set_text(0, F->get());
|
||||
ti->set_selectable(0, false);
|
||||
ti->set_editable(0, false);
|
||||
ti->set_icon(0, get_theme_icon("BoneAttachment", "EditorIcons"));
|
||||
} else {
|
||||
ti = parenthood[accum];
|
||||
}
|
||||
}
|
||||
|
||||
ti->set_editable(0, true);
|
||||
ti->set_selectable(0, true);
|
||||
ti->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
|
||||
ti->set_text(0, concat);
|
||||
ti->set_checked(0, motion_match->is_matching_track(path));
|
||||
ti->set_icon(0, get_theme_icon("BoneAttachment", "EditorIcons"));
|
||||
ti->set_metadata(0, path);
|
||||
|
||||
} else {
|
||||
// just a property
|
||||
ti = match_tracks->create_item(ti);
|
||||
ti->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
|
||||
ti->set_text(0, concat);
|
||||
ti->set_editable(0, true);
|
||||
ti->set_selectable(0, true);
|
||||
ti->set_checked(0, motion_match->is_matching_track(path));
|
||||
ti->set_metadata(0, path);
|
||||
}
|
||||
} else {
|
||||
if (ti) {
|
||||
// just a node, likely call or animation track
|
||||
ti->set_editable(0, true);
|
||||
ti->set_selectable(0, true);
|
||||
ti->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
|
||||
ti->set_checked(0, motion_match->is_matching_track(path));
|
||||
ti->set_metadata(0, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**/
|
||||
updating = false;
|
||||
}
|
||||
|
||||
void AnimationNodeMotionMatchEditor::_update_tracks() {
|
||||
print_line(itos(motion_match->get_matching_tracks().size()));
|
||||
if (motion_match->get_matching_tracks().size() == 0) {
|
||||
EditorNode::get_singleton()->show_warning(
|
||||
TTR("Please select tracks to match!"));
|
||||
}
|
||||
NodePath player_path =
|
||||
AnimationTreeEditor::get_singleton()->get_tree()->get_animation_player();
|
||||
/*Checking for errors*/
|
||||
if (!AnimationTreeEditor::get_singleton()->get_tree()->has_node(
|
||||
player_path)) {
|
||||
EditorNode::get_singleton()->show_warning(
|
||||
TTR("No animation player set, so unable to retrieve track names."));
|
||||
return;
|
||||
}
|
||||
|
||||
AnimationPlayer *player = Object::cast_to<AnimationPlayer>(
|
||||
AnimationTreeEditor::get_singleton()->get_tree()->get_node(player_path));
|
||||
if (!player) {
|
||||
EditorNode::get_singleton()->show_warning(
|
||||
TTR("Player path set is invalid, so unable to retrieve track names."));
|
||||
return;
|
||||
}
|
||||
|
||||
if ((AnimationTreeEditor::get_singleton()
|
||||
->get_tree()
|
||||
->get_root_motion_track() == NodePath())) {
|
||||
EditorNode::get_singleton()->show_warning(
|
||||
TTR("No root motion track was set, unable to build database."));
|
||||
return;
|
||||
}
|
||||
|
||||
/**/
|
||||
/*UPDATING DATABASE*/
|
||||
|
||||
motion_match->clear_keys();
|
||||
motion_match->set_dim_len(9);
|
||||
List<StringName> Animations;
|
||||
player->get_animation_list(&Animations);
|
||||
const StringName t = "Tracker";
|
||||
for (int i = 0; i < Animations.size(); i++) {
|
||||
if (Animations[i] != t) {
|
||||
Ref<Animation> anim = player->get_animation(Animations[i]);
|
||||
int root = anim->find_track(AnimationTreeEditor::get_singleton()
|
||||
->get_tree()
|
||||
->get_root_motion_track());
|
||||
NodePath root_motion_track = AnimationTreeEditor::get_singleton()->get_tree()->get_root_motion_track();
|
||||
int max_count =
|
||||
fill_tracks(player, anim.ptr(), root_motion_track);
|
||||
|
||||
motion_match->delta_time =
|
||||
anim->track_get_key_time(max_key_track, max_count - 1) / max_count;
|
||||
|
||||
for (int j = 0; j < max_count - snap_x->get_value(); j++) {
|
||||
float x = 0;
|
||||
float z = 0;
|
||||
for (int p = 0; p < 2; p++) {
|
||||
x += Math::pow(-1.0, double(p + 1)) *
|
||||
Vector3(Dictionary(anim->track_get_key_value(root, j + p))
|
||||
.get("location", Variant()))[0];
|
||||
z += Math::pow(-1.0, double(p + 1)) *
|
||||
Vector3(Dictionary(anim->track_get_key_value(root, j + p))
|
||||
.get("location", Variant()))[2];
|
||||
}
|
||||
|
||||
frame_model *key = new frame_model;
|
||||
for (int y = 0; y < motion_match->get_matching_tracks().size(); y++) {
|
||||
int track = anim->find_track(motion_match->get_matching_tracks()[y]);
|
||||
Vector3 loc = Vector3(Dictionary(anim->track_get_key_value(track, j))
|
||||
.get("location", Variant()));
|
||||
Vector<float> arr = {};
|
||||
for (int l = 0; l < snap_x->get_value(); l++) {
|
||||
if (l != 1) {
|
||||
arr.append(loc[l]);
|
||||
}
|
||||
}
|
||||
key->bone_data->append(arr);
|
||||
}
|
||||
Vector3 r_loc = Vector3(Dictionary(anim->track_get_key_value(root, j))
|
||||
.get("location", Variant()));
|
||||
for (int k = 0; k < snap_x->get_value(); k++) {
|
||||
Vector3 loc =
|
||||
Vector3(Dictionary(anim->track_get_key_value(root, j + k))
|
||||
.get("location", Variant()));
|
||||
for (int l = 0; l < 3; l++) {
|
||||
if (l != 1) {
|
||||
key->traj.append((loc[l] - r_loc[l]) * 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
key->time = anim->track_get_key_time(root, j + 1);
|
||||
key->anim_num = i;
|
||||
keys->append(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
motion_match->editing = false;
|
||||
motion_match->set_keys_data(keys);
|
||||
motion_match->skeleton = skeleton;
|
||||
if (motion_match->get_matching_tracks().size() != 0) {
|
||||
motion_match->done = true;
|
||||
}
|
||||
print_line("DONE");
|
||||
/**/
|
||||
}
|
||||
|
||||
void AnimationNodeMotionMatchEditor::_bind_methods() {
|
||||
ClassDB::bind_method("_match_tracks_edited",
|
||||
&AnimationNodeMotionMatchEditor::_match_tracks_edited);
|
||||
ClassDB::bind_method("_update_match_tracks",
|
||||
&AnimationNodeMotionMatchEditor::_update_match_tracks);
|
||||
ClassDB::bind_method("_edit_match_tracks",
|
||||
&AnimationNodeMotionMatchEditor::_edit_match_tracks);
|
||||
ClassDB::bind_method("_update_tracks",
|
||||
&AnimationNodeMotionMatchEditor::_update_tracks);
|
||||
ClassDB::bind_method("_clear_tree",
|
||||
&AnimationNodeMotionMatchEditor::_clear_tree);
|
||||
}
|
||||
AnimationNodeMotionMatchEditor::AnimationNodeMotionMatchEditor() {
|
||||
match_tracks_dialog = memnew(AcceptDialog);
|
||||
add_child(match_tracks_dialog);
|
||||
match_tracks_dialog->set_title(TTR("Tracks to Match:"));
|
||||
|
||||
VBoxContainer *match_tracks_vbox = memnew(VBoxContainer);
|
||||
match_tracks_dialog->add_child(match_tracks_vbox);
|
||||
|
||||
match_tracks = memnew(Tree);
|
||||
match_tracks_vbox->add_child(match_tracks);
|
||||
match_tracks->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
match_tracks->set_hide_root(true);
|
||||
match_tracks->connect_compat("item_edited", this, "_match_tracks_edited");
|
||||
|
||||
edit_match_tracks = memnew(Button("Edit Matching Tracks"));
|
||||
add_child(edit_match_tracks);
|
||||
edit_match_tracks->connect_compat("pressed", this, "_edit_match_tracks");
|
||||
|
||||
snap_x = memnew(SpinBox);
|
||||
add_child(snap_x);
|
||||
snap_x->set_prefix("Samples:");
|
||||
snap_x->set_min(1);
|
||||
snap_x->set_step(1);
|
||||
snap_x->set_max(100);
|
||||
|
||||
update_tracks = memnew(Button("Update DataBase"));
|
||||
add_child(update_tracks);
|
||||
update_tracks->connect_compat("pressed", this, "_update_tracks");
|
||||
|
||||
HBoxContainer *velocity_vbox = memnew(HBoxContainer);
|
||||
Label *l = memnew(Label);
|
||||
|
||||
l->set_text("Velocity");
|
||||
velocity_vbox->add_child(l);
|
||||
|
||||
updating = false;
|
||||
}
|
||||
|
||||
int AnimationNodeMotionMatchEditor::fill_tracks(AnimationPlayer *player,
|
||||
Animation *anim,
|
||||
NodePath &root) {
|
||||
int max_keys = 0;
|
||||
Vector<NodePath> tracks_tf = motion_match->get_matching_tracks();
|
||||
tracks_tf.push_back(root);
|
||||
for (int i = 0; i < tracks_tf.size(); i++) {
|
||||
if (anim->track_get_key_count(anim->find_track(tracks_tf[i])) >
|
||||
anim->track_get_key_count(anim->find_track(tracks_tf[max_keys]))) {
|
||||
max_keys = i;
|
||||
}
|
||||
}
|
||||
for (int i = 0;
|
||||
i < anim->track_get_key_count(anim->find_track(tracks_tf[max_keys]));
|
||||
i++) {
|
||||
float min_time =
|
||||
anim->track_get_key_time(anim->find_track(tracks_tf[0]), i);
|
||||
for (int p = 0; p < tracks_tf.size(); p++) {
|
||||
if (anim->track_get_key_time(anim->find_track(tracks_tf[p]), i) <
|
||||
min_time) {
|
||||
min_time = anim->track_get_key_time(anim->find_track(tracks_tf[p]), i);
|
||||
}
|
||||
}
|
||||
for (int p = 0; p < tracks_tf.size(); p++) {
|
||||
if (anim->track_get_key_time(anim->find_track(tracks_tf[p]), i) >
|
||||
min_time) {
|
||||
Vector3 t1;
|
||||
Quat t2;
|
||||
Vector3 t3;
|
||||
anim->transform_track_interpolate(anim->find_track(tracks_tf[p]),
|
||||
min_time, &t1, &t2, &t3);
|
||||
anim->transform_track_insert_key(anim->find_track(tracks_tf[p]),
|
||||
min_time, t1, t2, t3);
|
||||
}
|
||||
}
|
||||
}
|
||||
max_key_track = anim->find_track(tracks_tf[max_keys]);
|
||||
return anim->track_get_key_count(anim->find_track(tracks_tf[max_keys]));
|
||||
}
|
||||
|
||||
#endif
|
52
modules/motionmatch/animation_motion_match_editor.h
Normal file
52
modules/motionmatch/animation_motion_match_editor.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
#ifndef ANIMATION_MOTION_MATCH_EDITOR_H
|
||||
#define ANIMATION_MOTION_MATCH_EDITOR_H
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
||||
#include "frame_model.h"
|
||||
|
||||
#include "core/reference.h"
|
||||
#include "editor/plugins/animation_tree_editor_plugin.h"
|
||||
#include "modules/motionmatch/animation_node_motion_match.h"
|
||||
|
||||
class AnimationNodeMotionMatchEditor : public AnimationTreeNodeEditorPlugin {
|
||||
GDCLASS(AnimationNodeMotionMatchEditor, AnimationTreeNodeEditorPlugin)
|
||||
|
||||
Ref<AnimationNodeMotionMatch> motion_match;
|
||||
|
||||
AcceptDialog *match_tracks_dialog;
|
||||
Tree *match_tracks;
|
||||
|
||||
Button *edit_match_tracks;
|
||||
Button *update_tracks;
|
||||
Button *clear_tree;
|
||||
|
||||
SpinBox *snap_x;
|
||||
|
||||
Skeleton3D *skeleton;
|
||||
|
||||
bool updating;
|
||||
Vector<frame_model *> *keys = new Vector<frame_model *>();
|
||||
|
||||
void _match_tracks_edited();
|
||||
|
||||
void _edit_match_tracks();
|
||||
void _update_match_tracks();
|
||||
void _update_tracks();
|
||||
void _clear_tree();
|
||||
int max_key_track;
|
||||
void _update_vel();
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
bool can_edit(const Ref<AnimationNode> &p_node) override;
|
||||
virtual void edit(const Ref<AnimationNode> &p_node) override;
|
||||
|
||||
int fill_tracks(AnimationPlayer *player, Animation *anim, NodePath &root);
|
||||
|
||||
AnimationNodeMotionMatchEditor();
|
||||
};
|
||||
#endif // ANIMATION_MOTION_MATCH_EDITOR_H
|
||||
#endif
|
577
modules/motionmatch/animation_node_motion_match.cpp
Normal file
577
modules/motionmatch/animation_node_motion_match.cpp
Normal file
|
@ -0,0 +1,577 @@
|
|||
#include "animation_node_motion_match.h"
|
||||
#include "scene/main/node.h"
|
||||
|
||||
void AnimationNodeMotionMatch::get_parameter_list(
|
||||
List<PropertyInfo> *r_list) const {
|
||||
r_list->push_back(PropertyInfo(Variant::INT, min, PROPERTY_HINT_NONE, ""));
|
||||
r_list->push_back(PropertyInfo(Variant::INT, samples, PROPERTY_HINT_RANGE,
|
||||
"5,40,1,or_greater"));
|
||||
r_list->push_back(PropertyInfo(Variant::FLOAT, pvst, PROPERTY_HINT_RANGE,
|
||||
"0,1,0.01,or_greater"));
|
||||
r_list->push_back(PropertyInfo(Variant::FLOAT, f_time, PROPERTY_HINT_RANGE,
|
||||
"0,2,0.01,or_greater"));
|
||||
}
|
||||
|
||||
Variant AnimationNodeMotionMatch::get_parameter_default_value(
|
||||
const StringName &p_parameter) const {
|
||||
if (p_parameter == min) {
|
||||
return 0;
|
||||
} else if (p_parameter == samples) {
|
||||
return 10;
|
||||
} else if (p_parameter == pvst) {
|
||||
return 0.5;
|
||||
} else if (p_parameter == f_time) {
|
||||
return 0.25;
|
||||
} else {
|
||||
return Variant();
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationNodeMotionMatch::add_matching_track(
|
||||
const NodePath &p_track_path) {
|
||||
matching_tracks.push_back(p_track_path);
|
||||
} // Adds tracks to matching_tracks
|
||||
|
||||
void AnimationNodeMotionMatch::remove_matching_track(
|
||||
const NodePath &p_track_path) {
|
||||
matching_tracks.erase(p_track_path);
|
||||
} // removes tracks
|
||||
|
||||
bool AnimationNodeMotionMatch::is_matching_track(
|
||||
const NodePath &p_track_path) const {
|
||||
return matching_tracks.find(p_track_path) != -1;
|
||||
}
|
||||
|
||||
void AnimationNodeMotionMatch::update_motion_database(
|
||||
AnimationPlayer *p_animation_player) {
|
||||
for (int i = 0; i < matching_tracks.size(); i++) {
|
||||
print_line("track " + itos(i) + ": " + String(matching_tracks[i]));
|
||||
}
|
||||
}
|
||||
|
||||
Vector<NodePath> AnimationNodeMotionMatch::get_matching_tracks() {
|
||||
return matching_tracks;
|
||||
}
|
||||
|
||||
void AnimationNodeMotionMatch::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("add_matching_track", "track"),
|
||||
&AnimationNodeMotionMatch::add_matching_track);
|
||||
ClassDB::bind_method(D_METHOD("remove_matching_track", "track"),
|
||||
&AnimationNodeMotionMatch::remove_matching_track);
|
||||
ClassDB::bind_method(D_METHOD("is_matching_track", "track"),
|
||||
&AnimationNodeMotionMatch::is_matching_track);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("add_coordinates", "point"),
|
||||
&AnimationNodeMotionMatch::add_coordinates);
|
||||
ClassDB::bind_method(D_METHOD("load_coordinates", "points"),
|
||||
&AnimationNodeMotionMatch::load_coordinates);
|
||||
ClassDB::bind_method(D_METHOD("clear_coordinates"),
|
||||
&AnimationNodeMotionMatch::clear_coordinates);
|
||||
ClassDB::bind_method(D_METHOD("get_coordinates"),
|
||||
&AnimationNodeMotionMatch::get_coordinates);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_dim_len", "dim_len"),
|
||||
&AnimationNodeMotionMatch::set_dim_len);
|
||||
ClassDB::bind_method(D_METHOD("get_dim_len"),
|
||||
&AnimationNodeMotionMatch::get_dim_len);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_min_leaves", "min_leaves"),
|
||||
&AnimationNodeMotionMatch::set_min_leaves);
|
||||
ClassDB::bind_method(D_METHOD("get_min_leaves"),
|
||||
&AnimationNodeMotionMatch::get_min_leaves);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_root"),
|
||||
&AnimationNodeMotionMatch::get_root);
|
||||
ClassDB::bind_method(D_METHOD("build_tree"),
|
||||
&AnimationNodeMotionMatch::build_tree);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_start_index", "si"),
|
||||
&AnimationNodeMotionMatch::set_start_index);
|
||||
ClassDB::bind_method(D_METHOD("get_start_index"),
|
||||
&AnimationNodeMotionMatch::get_start_index);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("calc_root_threshold"),
|
||||
&AnimationNodeMotionMatch::calc_root_threshold);
|
||||
ClassDB::bind_method(D_METHOD("KNNSearch", "point", "k"),
|
||||
&AnimationNodeMotionMatch::KNNSearch);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("calculate_threshold", "point_coordinates"),
|
||||
&AnimationNodeMotionMatch::KDNode::calculate_threshold);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("add_index", "ind"),
|
||||
&AnimationNodeMotionMatch::KDNode::add_index);
|
||||
ClassDB::bind_method(D_METHOD("clear_indices"),
|
||||
&AnimationNodeMotionMatch::KDNode::clear_indices);
|
||||
ClassDB::bind_method(D_METHOD("get_indices"),
|
||||
&AnimationNodeMotionMatch::KDNode::get_indices);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("leaf_split", "point_coordinates_data",
|
||||
"no_of_dims", "start_dim"),
|
||||
&AnimationNodeMotionMatch::KDNode::leaf_split);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_left"),
|
||||
&AnimationNodeMotionMatch::KDNode::get_left);
|
||||
ClassDB::bind_method(D_METHOD("get_right"),
|
||||
&AnimationNodeMotionMatch::KDNode::get_right);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_prev"),
|
||||
&AnimationNodeMotionMatch::KDNode::get_prev);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_th", "th"),
|
||||
&AnimationNodeMotionMatch::KDNode::set_th);
|
||||
ClassDB::bind_method(D_METHOD("get_th"),
|
||||
&AnimationNodeMotionMatch::KDNode::get_th);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("Predict_traj", "vel", "samples"),
|
||||
&AnimationNodeMotionMatch::Predict_traj);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_velocity", "vel"),
|
||||
&AnimationNodeMotionMatch::set_velocity);
|
||||
ClassDB::bind_method(D_METHOD("get_velocity"),
|
||||
&AnimationNodeMotionMatch::get_velocity);
|
||||
|
||||
// for trajectory drawing
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_keys_size"),
|
||||
&AnimationNodeMotionMatch::get_key_size);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_key_traj", "key number"),
|
||||
&AnimationNodeMotionMatch::get_key_traj);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_future_traj"),
|
||||
&AnimationNodeMotionMatch::get_future_traj);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "velocity"), "set_velocity",
|
||||
"get_velocity");
|
||||
}
|
||||
|
||||
AnimationNodeMotionMatch::AnimationNodeMotionMatch() {
|
||||
root.instance();
|
||||
dim_len = 2;
|
||||
start_index = 0;
|
||||
point_coordinates = {};
|
||||
min_leaves = 1;
|
||||
|
||||
this->pos = "position";
|
||||
this->min = "min_key";
|
||||
this->pvst = "Pose vs Trajectory";
|
||||
this->f_time = "Check rate";
|
||||
this->samples = "Future Traj Samples";
|
||||
}
|
||||
|
||||
AnimationNodeMotionMatch::~AnimationNodeMotionMatch() {}
|
||||
|
||||
void AnimationNodeMotionMatch::add_coordinates(Vector<float> point) {
|
||||
if (point.size() != dim_len) {
|
||||
print_line("ERROR: Point is of wrong size.");
|
||||
error = true;
|
||||
err = LOAD_POINT_ERROR;
|
||||
} else {
|
||||
for (int i = 0; i < point.size(); i++) {
|
||||
this->point_coordinates.append(point[i]);
|
||||
}
|
||||
root->add_index(root->get_indices().size());
|
||||
}
|
||||
} // adds point to KD-tree
|
||||
|
||||
void AnimationNodeMotionMatch::load_coordinates(Vector<float> points) {
|
||||
if (dim_len < 0) {
|
||||
print_line("ERROR: Length is negative");
|
||||
error = true;
|
||||
err = LOAD_POINT_ERROR;
|
||||
} else if (points.size() % dim_len != 0) {
|
||||
print_line("ERROR: Point is of wrong size.");
|
||||
error = true;
|
||||
err = LOAD_POINT_ERROR;
|
||||
} else {
|
||||
for (int i = 0; i < points.size() / dim_len; i++) {
|
||||
Vector<float> point;
|
||||
for (int j = 0; j < dim_len; j++) {
|
||||
point.append(points[i * dim_len + j]);
|
||||
}
|
||||
add_coordinates(point);
|
||||
}
|
||||
}
|
||||
} // load multiple points at a go to KD-Tree
|
||||
|
||||
Vector<float> AnimationNodeMotionMatch::get_coordinates() {
|
||||
return this->point_coordinates;
|
||||
}
|
||||
|
||||
void AnimationNodeMotionMatch::clear_coordinates() {
|
||||
this->point_coordinates.resize(0);
|
||||
}
|
||||
|
||||
void AnimationNodeMotionMatch::set_dim_len(int32_t dim_len) {
|
||||
this->dim_len = dim_len;
|
||||
} // set dimension length
|
||||
|
||||
int32_t AnimationNodeMotionMatch::get_dim_len() {
|
||||
return this->dim_len;
|
||||
}
|
||||
|
||||
AnimationNodeMotionMatch::KDNode *AnimationNodeMotionMatch::get_root() {
|
||||
return root.ptr();
|
||||
} // returns root of the KDTree
|
||||
|
||||
void AnimationNodeMotionMatch::clear_root() {
|
||||
root->point_indices = {};
|
||||
root->split_th = 0;
|
||||
root->split_axis = 0;
|
||||
root->left = Ref<KDNode>();
|
||||
root->right = Ref<KDNode>();
|
||||
} // resets KDtree
|
||||
|
||||
void AnimationNodeMotionMatch::set_start_index(int32_t si) {
|
||||
this->start_index = si;
|
||||
} // set from which point you want to start evaluation
|
||||
|
||||
int32_t AnimationNodeMotionMatch::get_start_index() {
|
||||
return this->start_index;
|
||||
}
|
||||
|
||||
void AnimationNodeMotionMatch::calc_root_threshold() {
|
||||
if (this->point_coordinates.size() == 0) {
|
||||
print_line("ERROR:Load Points first!");
|
||||
} else {
|
||||
root->calculate_threshold(point_coordinates, dim_len);
|
||||
}
|
||||
} // Calculating root threshold TODO : Add options for threshold
|
||||
|
||||
void AnimationNodeMotionMatch::set_min_leaves(int32_t min_l) {
|
||||
min_leaves = min_l;
|
||||
} // min_leaves per node
|
||||
|
||||
int32_t AnimationNodeMotionMatch::get_min_leaves() {
|
||||
return min_leaves;
|
||||
}
|
||||
|
||||
void AnimationNodeMotionMatch::build_tree() {
|
||||
if (error == true) {
|
||||
if (err == LOAD_POINT_ERROR)
|
||||
print_line("ERROR: Check the input points");
|
||||
else if (err == QUERY_POINT_ERROR)
|
||||
print_line("ERROR: Check your query point");
|
||||
else if (err == K_ERROR)
|
||||
print_line("ERROR: Invalid value for number of neighbors");
|
||||
|
||||
} else {
|
||||
print_line("PROCESS : Building KD-Tree..");
|
||||
root->leaf_split(point_coordinates, dim_len, start_index, min_leaves);
|
||||
print_line("KD-Tree built SUCCESSFULLY");
|
||||
}
|
||||
} // Builds the tree with all the given parameters
|
||||
|
||||
float dist_between(Vector<float> point_coordinates, Vector<float> p1,
|
||||
uint32_t index) {
|
||||
float n = 0;
|
||||
for (int i = 0; i < p1.size(); i++) {
|
||||
n += (p1[i] - point_coordinates[p1.size() * index + i]) *
|
||||
(p1[i] - point_coordinates[p1.size() * index + i]);
|
||||
}
|
||||
return sqrt(n);
|
||||
}
|
||||
|
||||
bool in_array(Vector<float> points, uint32_t query) {
|
||||
for (int i = 0; i < points.size(); i++) {
|
||||
if (points[i] == query)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector<float> AnimationNodeMotionMatch::KNNSearch(Vector<float> point,
|
||||
int32_t k) {
|
||||
if (error == false && point.size() % dim_len != 0) {
|
||||
error = true;
|
||||
err = QUERY_POINT_ERROR;
|
||||
} else if (error == false && k > point_coordinates.size() / dim_len) {
|
||||
error = true;
|
||||
err = K_ERROR;
|
||||
}
|
||||
|
||||
if (error == true) {
|
||||
if (err == LOAD_POINT_ERROR)
|
||||
print_line("ERROR: Check the input points");
|
||||
else if (err == QUERY_POINT_ERROR)
|
||||
print_line("ERROR: Check your query point");
|
||||
else if (err == K_ERROR)
|
||||
print_line("ERROR: Invalid value for number of neighbors");
|
||||
|
||||
return {};
|
||||
|
||||
} else {
|
||||
Vector<float> Knn = {};
|
||||
KDNode *m_node; /*node where the query point can be placed*/
|
||||
KDNode *node = root.ptr();
|
||||
|
||||
while (node->get_indices().size() > min_leaves) {
|
||||
if (point[node->get_split_axis()] > node->get_th()) {
|
||||
node = node->get_left();
|
||||
} else {
|
||||
node = node->get_right();
|
||||
}
|
||||
}
|
||||
m_node = node;
|
||||
for (int j = 0; j < k; j++) {
|
||||
uint32_t nn = 0; /*nearest neighbour*/
|
||||
float nd = std::numeric_limits<float>::max(); /*nearest distance*/
|
||||
for (int i = 0; i < node->get_indices().size(); i++) {
|
||||
if (j == 0) {
|
||||
float dist =
|
||||
dist_between(point_coordinates, point, node->get_indices()[i]);
|
||||
if (nd > dist || nd == 0) {
|
||||
nd = dist;
|
||||
nn = node->get_indices()[i];
|
||||
}
|
||||
} else if (!in_array(Knn, node->get_indices()[i])) {
|
||||
float dist =
|
||||
dist_between(point_coordinates, point, node->get_indices()[i]);
|
||||
if (nd > dist || nd == 0) {
|
||||
nd = dist;
|
||||
nn = node->get_indices()[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
while (node->get_prev() != NULL) {
|
||||
node = node->get_prev();
|
||||
if ((point[node->get_split_axis()] - node->get_th()) > nd) {
|
||||
break;
|
||||
} else {
|
||||
for (int i = 0; i < node->get_indices().size(); i++) {
|
||||
if (j == 0) {
|
||||
float dist = dist_between(point_coordinates, point,
|
||||
node->get_indices()[i]);
|
||||
if (nd > dist || nd == 0) {
|
||||
nd = dist;
|
||||
nn = node->get_indices()[i];
|
||||
}
|
||||
} else if (!in_array(Knn, node->get_indices()[i])) {
|
||||
float dist = dist_between(point_coordinates, point,
|
||||
node->get_indices()[i]);
|
||||
if (nd > dist || nd == 0) {
|
||||
nd = dist;
|
||||
nn = node->get_indices()[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Knn.append(nn);
|
||||
node = m_node;
|
||||
print_line("Round" + itos(j) + ":" + itos(nn));
|
||||
}
|
||||
return Knn;
|
||||
}
|
||||
} /*returns K nearest neighbours in a Vector<float>*/
|
||||
|
||||
void AnimationNodeMotionMatch::KDNode::calculate_threshold(
|
||||
Vector<float> point_coordinates, int32_t dim_len) {
|
||||
if (point_coordinates.size() % dim_len != 0) {
|
||||
print_line("ERROR: Point coordinates array is of wrong size.");
|
||||
} else {
|
||||
this->split_th = 0;
|
||||
for (int i = 0; i < this->point_indices.size(); i++) {
|
||||
this->split_th +=
|
||||
point_coordinates[(point_indices[i] * dim_len) + split_axis];
|
||||
}
|
||||
this->split_th = this->split_th / this->point_indices.size();
|
||||
}
|
||||
} // threshold calculating function
|
||||
|
||||
bool AnimationNodeMotionMatch::KDNode::are_all_points_same(
|
||||
Vector<float> point_coordinates, int32_t dim_len) {
|
||||
for (int i = 0; i < dim_len; i++) {
|
||||
for (int j = 0; j < point_indices.size(); j++) {
|
||||
float p = point_coordinates[(point_indices[j] * dim_len) + i];
|
||||
for (int k = 0; k < point_indices.size(); k++) {
|
||||
if (point_coordinates[(point_indices[k] * dim_len) + i] != p) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AnimationNodeMotionMatch::KDNode::set_th(float t) {
|
||||
this->split_th = t;
|
||||
}
|
||||
|
||||
float AnimationNodeMotionMatch::KDNode::get_th() {
|
||||
return this->split_th;
|
||||
}
|
||||
|
||||
void AnimationNodeMotionMatch::KDNode::add_index(uint32_t i) {
|
||||
point_indices.append(i);
|
||||
}
|
||||
|
||||
void AnimationNodeMotionMatch::KDNode::clear_indices() {
|
||||
point_indices.resize(0);
|
||||
}
|
||||
|
||||
Vector<float> AnimationNodeMotionMatch::KDNode::get_indices() {
|
||||
return point_indices;
|
||||
}
|
||||
|
||||
AnimationNodeMotionMatch::KDNode::KDNode() {
|
||||
this->point_indices = {};
|
||||
this->split_th = 0;
|
||||
this->split_axis = 0;
|
||||
}
|
||||
|
||||
void AnimationNodeMotionMatch::KDNode::leaf_split(
|
||||
Vector<float> point_coordinates, int32_t dim_len, int32_t dim,
|
||||
int32_t min_leaves) {
|
||||
if (point_coordinates.size() % dim_len != 0) {
|
||||
print_line("ERROR: Point coordinates array is of wrong size.");
|
||||
} else {
|
||||
split_axis = dim % dim_len;
|
||||
calculate_threshold(point_coordinates, dim_len);
|
||||
|
||||
left.instance();
|
||||
right.instance();
|
||||
|
||||
left->prev = this;
|
||||
right->prev = this;
|
||||
for (int j = 0; j < this->point_indices.size(); j++) {
|
||||
if (point_coordinates[(point_indices[j] * dim_len) + split_axis] >
|
||||
split_th) {
|
||||
left->point_indices.append(point_indices[j]);
|
||||
} else {
|
||||
right->point_indices.append(point_indices[j]);
|
||||
}
|
||||
}
|
||||
|
||||
if (left->point_indices.size() > min_leaves &&
|
||||
!are_all_points_same(point_coordinates, dim_len)) {
|
||||
left->leaf_split(point_coordinates, dim_len, dim + 1, min_leaves);
|
||||
}
|
||||
if (right->point_indices.size() > min_leaves &&
|
||||
!are_all_points_same(point_coordinates, dim_len)) {
|
||||
right->leaf_split(point_coordinates, dim_len, dim + 1, min_leaves);
|
||||
}
|
||||
}
|
||||
} // update tree with given point
|
||||
|
||||
AnimationNodeMotionMatch::KDNode *AnimationNodeMotionMatch::KDNode::get_left() {
|
||||
return left.ptr();
|
||||
}
|
||||
|
||||
AnimationNodeMotionMatch::KDNode *
|
||||
AnimationNodeMotionMatch::KDNode::get_right() {
|
||||
return right.ptr();
|
||||
}
|
||||
|
||||
float AnimationNodeMotionMatch::process(float p_time, bool p_seek) {
|
||||
AnimationPlayer *player = state->player;
|
||||
|
||||
List<StringName> a_nam;
|
||||
player->get_animation_list(&a_nam);
|
||||
// Tracker->Dummy track for modifications
|
||||
if (!player->has_animation("Tracker")) {
|
||||
main = a_nam[0];
|
||||
Animation *a = player->get_animation(a_nam[0]).ptr();
|
||||
|
||||
r_index = player->get_animation(a_nam[0]).ptr()->find_track(
|
||||
state->tree->get_root_motion_track());
|
||||
a->track_set_enabled(r_index, false);
|
||||
|
||||
player->add_animation("Tracker", a);
|
||||
a_nam.clear();
|
||||
player->get_animation_list(&a_nam);
|
||||
}
|
||||
|
||||
if (!timeout && keys->size() != 0 && !editing) {
|
||||
Vector3 l_v = Vector3();
|
||||
l_v = get("velocity");
|
||||
future_traj = Predict_traj(l_v, get_parameter(samples));
|
||||
float min_cost = std::numeric_limits<float>::max();
|
||||
float min_cost_time = 0;
|
||||
int dup;
|
||||
|
||||
if (first_time) {
|
||||
dup = -1;
|
||||
first_time = false;
|
||||
player->play("Tracker");
|
||||
} else {
|
||||
dup = get_parameter(min);
|
||||
}
|
||||
|
||||
int p = 0;
|
||||
print_line(itos(get_instance_id()));
|
||||
for (p = 0; p < keys->size(); p++) {
|
||||
if (p != dup) {
|
||||
float pos_cost = 0.0f;
|
||||
float traj_cost = 0.0f;
|
||||
float tot_cost = 0.0f;
|
||||
|
||||
for (int i = 0; i < matching_tracks.size(); i++) {
|
||||
Vector<String> s = String(matching_tracks[i]).split(":");
|
||||
Vector3 pos =
|
||||
skeleton->get_bone_global_pose(skeleton->find_bone(s[1]))
|
||||
.get_origin();
|
||||
|
||||
for (int po = 0; po < 2; po++) {
|
||||
pos_cost += (pos[po] - (*(*keys)[p]->bone_data)[i][po]) *
|
||||
(pos[po] - (*(*keys)[p]->bone_data)[i][po]);
|
||||
}
|
||||
} // calculating pose costs
|
||||
|
||||
for (int t = 0; t < int(get_parameter(samples)) * 2; t++) {
|
||||
traj_cost += ((*keys)[p]->traj[t] - future_traj[t]) *
|
||||
((*keys)[p]->traj[t] - future_traj[t]);
|
||||
} // calculating traj costs
|
||||
|
||||
real_t lbd = get_parameter(pvst); // Pose vs Trajectory cost
|
||||
tot_cost = lbd * pos_cost + (1 - lbd) * traj_cost;
|
||||
|
||||
if (tot_cost < min_cost) {
|
||||
min_cost = tot_cost;
|
||||
min_cost_time = (*keys)[p]->time;
|
||||
set_parameter(min, p);
|
||||
} // set min
|
||||
}
|
||||
}
|
||||
player->seek(min_cost_time); // play min for every frame
|
||||
timeout = true;
|
||||
}
|
||||
c_time += p_time;
|
||||
// f_time parameter decides the check rate
|
||||
if (c_time > real_t(get_parameter(f_time))) {
|
||||
timeout = false;
|
||||
c_time = 0;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
Vector<float> AnimationNodeMotionMatch::Predict_traj(Vector3 L_Velocity,
|
||||
int samples) {
|
||||
// used exponential decay here
|
||||
// TODO : Add multiple options to pick for the user
|
||||
// Can use interpolation functions
|
||||
Vector<float> futurepath = {};
|
||||
Vector3 c_pos = Vector3();
|
||||
|
||||
float time = 0;
|
||||
|
||||
for (int i = 0; i < samples; i++) {
|
||||
c_pos[0] = c_pos[0] + L_Velocity[0] * (1 - Math::exp(-time));
|
||||
futurepath.append(c_pos[0]);
|
||||
c_pos[2] = c_pos[2] + L_Velocity[2] * (1 - Math::exp(-time));
|
||||
futurepath.append(c_pos[2]);
|
||||
|
||||
time += delta_time;
|
||||
}
|
||||
return futurepath;
|
||||
}
|
||||
|
||||
void AnimationNodeMotionMatch::print_array(Vector<float> ar) {
|
||||
String s = "";
|
||||
for (int k = 0; k < ar.size(); k++) {
|
||||
s += itos(ar[k]) + ",";
|
||||
}
|
||||
print_line(s);
|
||||
}
|
152
modules/motionmatch/animation_node_motion_match.h
Normal file
152
modules/motionmatch/animation_node_motion_match.h
Normal file
|
@ -0,0 +1,152 @@
|
|||
#ifndef ANIMATION_NODE_MOTION_MATCH_H
|
||||
#define ANIMATION_NODE_MOTION_MATCH_H
|
||||
|
||||
#include "frame_model.h"
|
||||
|
||||
#include "core/reference.h"
|
||||
#include "editor/plugins/animation_tree_editor_plugin.h"
|
||||
#include "scene/3d/physics_body_3d.h"
|
||||
#include "scene/animation/animation_tree.h"
|
||||
#include <limits>
|
||||
|
||||
class AnimationNodeMotionMatch : public AnimationRootNode {
|
||||
GDCLASS(AnimationNodeMotionMatch, AnimationRootNode)
|
||||
|
||||
Vector<NodePath> matching_tracks;
|
||||
// parameters
|
||||
StringName vel;
|
||||
StringName pos;
|
||||
StringName min;
|
||||
StringName pvst;
|
||||
StringName samples;
|
||||
StringName f_time;
|
||||
|
||||
StringName main;
|
||||
// variables used during matching
|
||||
bool first_time = true;
|
||||
float c_time = 0;
|
||||
bool timeout = false;
|
||||
Vector3 v = Vector3();
|
||||
// KDNode Struct
|
||||
struct KDNode : public Reference {
|
||||
/*th -> Threshold*/
|
||||
// Variables
|
||||
Vector<float> point_indices;
|
||||
Ref<KDNode> left;
|
||||
Ref<KDNode> right;
|
||||
float split_th;
|
||||
int32_t split_axis;
|
||||
// Methods
|
||||
bool are_all_points_same(Vector<float> point_coordinates, int32_t dim_len);
|
||||
KDNode *prev;
|
||||
void calculate_threshold(Vector<float> points, int32_t dim_len);
|
||||
|
||||
void add_index(uint32_t i);
|
||||
void clear_indices();
|
||||
Vector<float> get_indices();
|
||||
|
||||
void leaf_split(Vector<float> point_coordinates, int32_t dim_len,
|
||||
int32_t dim, int32_t min_leaves);
|
||||
|
||||
KDNode *get_left();
|
||||
KDNode *get_right();
|
||||
KDNode *get_prev() { return prev; }
|
||||
|
||||
int32_t get_split_axis() { return split_axis; }
|
||||
|
||||
void set_th(float th);
|
||||
float get_th();
|
||||
KDNode();
|
||||
};
|
||||
|
||||
Vector<frame_model *> *keys = new Vector<frame_model *>();
|
||||
Vector<float> future_traj;
|
||||
Vector<float> point_coordinates;
|
||||
Ref<KDNode> root;
|
||||
int dim_len; /*no of dimensions*/
|
||||
int32_t start_index; /*Axis relative to which the first split occurs*/
|
||||
int32_t min_leaves; /*Minimum leafs in nodes at the end level*/
|
||||
bool error = false;
|
||||
|
||||
enum errortype { LOAD_POINT_ERROR,
|
||||
QUERY_POINT_ERROR,
|
||||
K_ERROR };
|
||||
|
||||
Vector3 velocity;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
Variant get_parameter_default_value(
|
||||
const StringName &p_parameter) const override;
|
||||
|
||||
public:
|
||||
Skeleton3D *skeleton;
|
||||
NodePath root_track = NodePath();
|
||||
int r_index;
|
||||
bool done = false;
|
||||
bool editing = false;
|
||||
float delta_time;
|
||||
void get_parameter_list(List<PropertyInfo> *r_list) const override;
|
||||
|
||||
float process(float p_time, bool p_seek) override;
|
||||
void add_matching_track(const NodePath &p_track_path);
|
||||
void remove_matching_track(const NodePath &p_track_path);
|
||||
bool is_matching_track(const NodePath &p_track_path) const;
|
||||
Vector<NodePath> get_matching_tracks();
|
||||
void update_motion_database(AnimationPlayer *p_animation_player);
|
||||
|
||||
errortype err;
|
||||
void set_start_index(int32_t si);
|
||||
int32_t get_start_index();
|
||||
|
||||
void set_min_leaves(int32_t min_l);
|
||||
int32_t get_min_leaves();
|
||||
|
||||
void add_coordinates(Vector<float> point);
|
||||
void load_coordinates(Vector<float> points);
|
||||
Vector<float> get_coordinates();
|
||||
void clear_coordinates();
|
||||
|
||||
void set_dim_len(int32_t dim_len);
|
||||
int32_t get_dim_len();
|
||||
|
||||
void calc_root_threshold();
|
||||
|
||||
Vector<float> KNNSearch(Vector<float> point, int32_t k);
|
||||
|
||||
void build_tree();
|
||||
KDNode *get_root();
|
||||
void clear_root();
|
||||
|
||||
Vector<frame_model *> *get_keys_data() { return keys; }
|
||||
|
||||
void set_keys_data(Vector<frame_model *> *kys) { keys = kys; }
|
||||
void clear_keys() {
|
||||
while (keys->size() != 0) {
|
||||
keys->remove(0);
|
||||
}
|
||||
c_time = 0;
|
||||
timeout = false;
|
||||
}
|
||||
|
||||
Vector<float> Predict_traj(Vector3 L_Velocity, int samples);
|
||||
|
||||
int get_traj_samples() { return get_parameter(samples); }
|
||||
void set_traj_samples(int sa) { set_parameter(samples, sa); }
|
||||
|
||||
Vector3 get_velocity() { return velocity; }
|
||||
|
||||
void set_velocity(Vector3 v) { velocity = v; }
|
||||
|
||||
// for trajectory drawing
|
||||
|
||||
int get_key_size() { return keys->size(); }
|
||||
Vector<float> get_key_traj(int k_n) { return (*keys)[k_n]->traj; }
|
||||
Vector<float> get_future_traj() { return future_traj; }
|
||||
|
||||
void print_array(Vector<float> ar);
|
||||
AnimationNodeMotionMatch();
|
||||
~AnimationNodeMotionMatch();
|
||||
};
|
||||
|
||||
#endif // ANIMATION_NODE_MOTION_MATCH_H
|
5
modules/motionmatch/config.py
Normal file
5
modules/motionmatch/config.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
def can_build(env, platform):
|
||||
return True
|
||||
|
||||
def configure(env):
|
||||
pass
|
13
modules/motionmatch/frame_model.h
Normal file
13
modules/motionmatch/frame_model.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#ifndef FRAME_MODEL_H
|
||||
#define FRAME_MODEL_H
|
||||
|
||||
#include "scene/main/node.h"
|
||||
|
||||
struct frame_model {
|
||||
Vector<Vector<float>> *bone_data = new Vector<Vector<float>>();
|
||||
Vector<float> traj;
|
||||
float time = 0.0f;
|
||||
int anim_num = 0;
|
||||
};
|
||||
|
||||
#endif
|
54
modules/motionmatch/register_types.cpp
Normal file
54
modules/motionmatch/register_types.cpp
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*************************************************************************/
|
||||
/* register_types.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
|
||||
/* */
|
||||
/* 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 "register_types.h"
|
||||
|
||||
#include "animation_node_motion_match.h"
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "animation_motion_match_editor.h"
|
||||
|
||||
static void _editor_init() {
|
||||
AnimationNodeMotionMatchEditor *motion_match_editor =
|
||||
memnew(AnimationNodeMotionMatchEditor);
|
||||
AnimationTreeEditor::get_singleton()->add_plugin(motion_match_editor);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void register_motionmatch_types() {
|
||||
ClassDB::register_class<AnimationNodeMotionMatch>();
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
EditorNode::add_init_callback(_editor_init);
|
||||
#endif
|
||||
}
|
||||
|
||||
void unregister_motionmatch_types() {}
|
32
modules/motionmatch/register_types.h
Normal file
32
modules/motionmatch/register_types.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*************************************************************************/
|
||||
/* register_types.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
|
||||
/* */
|
||||
/* 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
void register_motionmatch_types();
|
||||
void unregister_motionmatch_types();
|
Loading…
Reference in a new issue