summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
authorJuan Linietsky <reduzio@gmail.com>2018-05-03 17:29:15 -0300
committerJuan Linietsky <reduzio@gmail.com>2018-05-03 17:29:56 -0300
commitb3e4bc562c07142d48907278f9cc496791ff7b33 (patch)
tree1dd28db8c97d288934268c908b15f1ff4f1e59f8 /scene
parente9c1957a3ee8087dd3d6381f39ca0eaaf90e9289 (diff)
Skeleton for 2D WIP
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/line_2d.cpp3
-rw-r--r--scene/2d/polygon_2d.cpp86
-rw-r--r--scene/2d/skeleton_2d.cpp69
-rw-r--r--scene/2d/skeleton_2d.h7
4 files changed, 152 insertions, 13 deletions
diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp
index 229c2c6fe8..3e61dd05f4 100644
--- a/scene/2d/line_2d.cpp
+++ b/scene/2d/line_2d.cpp
@@ -270,7 +270,8 @@ void Line2D::_draw() {
lb.indices,
lb.vertices,
lb.colors,
- lb.uvs,
+ lb.uvs, Vector<int>(), Vector<float>(),
+
texture_rid);
// DEBUG
diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp
index eed26f5399..4d6ebc81c3 100644
--- a/scene/2d/polygon_2d.cpp
+++ b/scene/2d/polygon_2d.cpp
@@ -30,7 +30,7 @@
#include "polygon_2d.h"
#include "core/math/geometry.h"
-
+#include "skeleton_2d.h"
Dictionary Polygon2D::_edit_get_state() const {
Dictionary state = Node2D::_edit_get_state();
state["offset"] = offset;
@@ -91,8 +91,20 @@ void Polygon2D::_notification(int p_what) {
if (polygon.size() < 3)
return;
+ Skeleton2D *skeleton_node = NULL;
+ if (has_node(skeleton)) {
+ skeleton_node = Object::cast_to<Skeleton2D>(get_node(skeleton));
+ }
+
+ if (skeleton_node)
+ VS::get_singleton()->canvas_item_attach_skeleton(get_canvas_item(), skeleton_node->get_skeleton());
+ else
+ VS::get_singleton()->canvas_item_attach_skeleton(get_canvas_item(), RID());
+
Vector<Vector2> points;
Vector<Vector2> uvs;
+ Vector<int> bones;
+ Vector<float> weights;
points.resize(polygon.size());
@@ -180,6 +192,70 @@ void Polygon2D::_notification(int p_what) {
}
}
+ if (!invert && bone_weights.size()) {
+ //a skeleton is set! fill indices and weights
+ int vc = points.size();
+ bones.resize(vc * 4);
+ weights.resize(vc * 4);
+
+ int *bonesw = bones.ptrw();
+ float *weightsw = weights.ptrw();
+
+ for (int i = 0; i < vc * 4; i++) {
+ bonesw[i] = 0;
+ weightsw[i] = 0;
+ }
+
+ for (int i = 0; i < bone_weights.size(); i++) {
+ if (bone_weights[i].weights.size() != points.size()) {
+ continue; //different number of vertices, sorry not using.
+ }
+ if (!skeleton_node->has_node(bone_weights[i].path)) {
+ continue; //node does not exist
+ }
+ Bone2D *bone = Object::cast_to<Bone2D>(skeleton_node->get_node(bone_weights[i].path));
+ if (!bone) {
+ continue;
+ }
+
+ int bone_index = bone->get_index_in_skeleton();
+ PoolVector<float>::Read r = bone_weights[i].weights.read();
+ for (int j = 0; j < vc; j++) {
+ if (r[j] == 0.0)
+ continue; //weight is unpainted, skip
+ //find an index with a weight
+ for (int k = 0; k < 4; k++) {
+ if (weightsw[j * 4 + k] < r[j]) {
+ //this is less than this weight, insert weight!
+ for (int l = 3; l > k; l--) {
+ weightsw[j * 4 + l] = weightsw[j * 4 + l - 1];
+ bonesw[j * 4 + l] = bonesw[j * 4 + l - 1];
+ }
+ weightsw[j * 4 + k] = r[j];
+ bonesw[j * 4 + k] = bone_index;
+ break;
+ }
+ }
+ }
+ }
+
+ //normalize the weights
+ for (int i = 0; i < vc; i++) {
+ float tw = 0;
+ for (int j = 0; j < 4; j++) {
+ tw += weightsw[i * 4 + j];
+ }
+ if (tw == 0)
+ continue; //unpainted, do nothing
+
+ //normalize
+ for (int j = 0; j < 4; j++) {
+ weightsw[i * 4 + j] /= tw;
+ // print_line("point " + itos(i) + " idx " + itos(j) + " index: " + itos(bonesw[i * 4 + j]) + " weight: " + rtos(weightsw[i * 4 + j]));
+ }
+ }
+ }
+
Vector<Color> colors;
int color_len = vertex_colors.size();
colors.resize(len);
@@ -197,7 +273,8 @@ void Polygon2D::_notification(int p_what) {
// VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, points, colors, uvs, texture.is_valid() ? texture->get_rid() : RID());
if (invert || splits.size() == 0) {
- VS::get_singleton()->canvas_item_add_polygon(get_canvas_item(), points, colors, uvs, texture.is_valid() ? texture->get_rid() : RID(), RID(), antialiased);
+ Vector<int> indices = Geometry::triangulate_polygon(points);
+ VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, points, colors, uvs, bones, weights, texture.is_valid() ? texture->get_rid() : RID());
} else {
//use splits
Vector<int> loop;
@@ -268,7 +345,7 @@ void Polygon2D::_notification(int p_what) {
//print_line("loops: " + itos(loops.size()) + " indices: " + itos(indices.size()));
- VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, points, colors, uvs, texture.is_valid() ? texture->get_rid() : RID());
+ VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, points, colors, uvs, bones, weights, texture.is_valid() ? texture->get_rid() : RID());
}
} break;
@@ -488,9 +565,12 @@ void Polygon2D::_set_bones(const Array &p_bones) {
}
void Polygon2D::set_skeleton(const NodePath &p_skeleton) {
+ if (skeleton == p_skeleton)
+ return;
skeleton = p_skeleton;
update();
}
+
NodePath Polygon2D::get_skeleton() const {
return skeleton;
}
diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp
index 3b3561bc1c..9197127235 100644
--- a/scene/2d/skeleton_2d.cpp
+++ b/scene/2d/skeleton_2d.cpp
@@ -28,6 +28,11 @@ void Bone2D::_notification(int p_what) {
skeleton->_make_transform_dirty();
}
}
+ if (p_what == NOTIFICATION_MOVED_IN_PARENT) {
+ if (skeleton) {
+ skeleton->_make_bone_setup_dirty();
+ }
+ }
if (p_what == NOTIFICATION_EXIT_TREE) {
if (skeleton) {
@@ -48,12 +53,18 @@ void Bone2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_rest", "rest"), &Bone2D::set_rest);
ClassDB::bind_method(D_METHOD("get_rest"), &Bone2D::get_rest);
ClassDB::bind_method(D_METHOD("apply_rest"), &Bone2D::apply_rest);
+ ClassDB::bind_method(D_METHOD("get_skeleton_rest"), &Bone2D::get_skeleton_rest);
+ ClassDB::bind_method(D_METHOD("get_index_in_skeleton"), &Bone2D::get_index_in_skeleton);
+
+ ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D,"rest"),"set_rest","get_rest");
}
void Bone2D::set_rest(const Transform2D &p_rest) {
rest = p_rest;
if (skeleton)
skeleton->_make_bone_setup_dirty();
+
+ update_configuration_warning();
}
Transform2D Bone2D::get_rest() const {
@@ -73,22 +84,45 @@ void Bone2D::apply_rest() {
set_transform(rest);
}
+int Bone2D::get_index_in_skeleton() const {
+ ERR_FAIL_COND_V(!skeleton,-1);
+ skeleton->_update_bone_setup();
+ return skeleton_index;
+}
String Bone2D::get_configuration_warning() const {
+
+ String warning = Node2D::get_configuration_warning();
if (!skeleton) {
+ if (warning!=String()) {
+ warning+="\n";
+ }
if (parent_bone) {
- return TTR("This Bone2D chain should end at a Skeleton2D node.");
+ warning+=TTR("This Bone2D chain should end at a Skeleton2D node.");
} else {
- return TTR("A Bone2D only works with a Skeleton2D or another Bone2D as parent node.");
+ warning+=TTR("A Bone2D only works with a Skeleton2D or another Bone2D as parent node.");
+ }
+ }
+
+ if (rest==Transform2D(0,0,0,0,0,0)) {
+ if (warning!=String()) {
+ warning+="\n";
}
+ warning+=TTR("This bone lacks a proper REST pose. Go to the Skeleton2D node and set one.");
+
}
- return Node2D::get_configuration_warning();
+ return warning;
}
Bone2D::Bone2D() {
skeleton = NULL;
parent_bone = NULL;
+ skeleton_index=-1;
set_notify_local_transform(true);
+ //this is a clever hack so the bone knows no rest has been set yet, allowing to show an error.
+ for(int i=0;i<3;i++) {
+ rest[i]=Vector2(0,0);
+ }
}
//////////////////////////////////////
@@ -114,7 +148,14 @@ void Skeleton2D::_update_bone_setup() {
bones.sort(); //sorty so they are always in the same order/index
for (int i = 0; i < bones.size(); i++) {
- bones[i].rest_inverse = bones[i].bone->get_skeleton_rest(); //bind pose
+ bones[i].rest_inverse = bones[i].bone->get_skeleton_rest().affine_inverse(); //bind pose
+ bones[i].bone->skeleton_index=i;
+ Bone2D *parent_bone = Object::cast_to<Bone2D>(bones[i].bone->get_parent());
+ if (parent_bone) {
+ bones[i].parent_index=parent_bone->skeleton_index;
+ } else {
+ bones[i].parent_index=-1;
+ }
}
transform_dirty = true;
@@ -142,13 +183,20 @@ void Skeleton2D::_update_transform() {
transform_dirty = false;
- Transform2D global_xform = get_global_transform();
- Transform2D global_xform_inverse = global_xform.affine_inverse();
+ for (int i = 0; i < bones.size(); i++) {
+
+ ERR_CONTINUE(bones[i].parent_index>=i);
+ if (bones[i].parent_index>=0) {
+ bones[i].accum_transform = bones[bones[i].parent_index].accum_transform * bones[i].bone->get_transform();
+ } else {
+ bones[i].accum_transform = bones[i].bone->get_transform();
+ }
+ }
for (int i = 0; i < bones.size(); i++) {
- Transform2D final_xform = bones[i].rest_inverse * bones[i].bone->get_relative_transform_to_parent(this);
- VS::get_singleton()->skeleton_bone_set_transform_2d(skeleton, i, global_xform * (final_xform * global_xform_inverse));
+ Transform2D final_xform = bones[i].accum_transform * bones[i].rest_inverse;
+ VS::get_singleton()->skeleton_bone_set_transform_2d(skeleton, i, final_xform);
}
}
@@ -179,10 +227,12 @@ void Skeleton2D::_notification(int p_what) {
_update_bone_setup();
if (transform_dirty)
_update_transform();
+
+ request_ready();
}
if (p_what == NOTIFICATION_TRANSFORM_CHANGED) {
- _make_transform_dirty();
+ VS::get_singleton()->skeleton_set_base_transform_2d(skeleton,get_global_transform());
}
}
@@ -203,6 +253,7 @@ void Skeleton2D::_bind_methods() {
Skeleton2D::Skeleton2D() {
bone_setup_dirty = true;
transform_dirty = true;
+
skeleton = VS::get_singleton()->skeleton_create();
}
diff --git a/scene/2d/skeleton_2d.h b/scene/2d/skeleton_2d.h
index 49199f684f..9ae74b56d3 100644
--- a/scene/2d/skeleton_2d.h
+++ b/scene/2d/skeleton_2d.h
@@ -12,6 +12,9 @@ class Bone2D : public Node2D {
Skeleton2D *skeleton;
Transform2D rest;
+friend class Skeleton2D;
+ int skeleton_index;
+
protected:
void _notification(int p_what);
static void _bind_methods();
@@ -24,6 +27,8 @@ public:
String get_configuration_warning() const;
+ int get_index_in_skeleton() const;
+
Bone2D();
};
@@ -37,6 +42,8 @@ class Skeleton2D : public Node2D {
return p_bone.bone->is_greater_than(bone);
}
Bone2D *bone;
+ int parent_index;
+ Transform2D accum_transform;
Transform2D rest_inverse;
};