| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310 | /*************************************************************************//*  skeleton_2d.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 "skeleton_2d.h"void Bone2D::_notification(int p_what) {	if (p_what == NOTIFICATION_ENTER_TREE) {		Node *parent = get_parent();		parent_bone = Object::cast_to<Bone2D>(parent);		skeleton = NULL;		while (parent) {			skeleton = Object::cast_to<Skeleton2D>(parent);			if (skeleton)				break;			if (!Object::cast_to<Bone2D>(parent))				break; //skeletons must be chained to Bone2Ds.			parent = parent->get_parent();		}		if (skeleton) {			Skeleton2D::Bone bone;			bone.bone = this;			skeleton->bones.push_back(bone);			skeleton->_make_bone_setup_dirty();		}	}	if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) {		if (skeleton) {			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) {			for (int i = 0; i < skeleton->bones.size(); i++) {				if (skeleton->bones[i].bone == this) {					skeleton->bones.remove(i);					break;				}			}			skeleton->_make_bone_setup_dirty();			skeleton = NULL;		}		parent_bone = NULL;	}}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);	ClassDB::bind_method(D_METHOD("set_default_length", "default_length"), &Bone2D::set_default_length);	ClassDB::bind_method(D_METHOD("get_default_length"), &Bone2D::get_default_length);	ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "rest"), "set_rest", "get_rest");	ADD_PROPERTY(PropertyInfo(Variant::REAL, "default_length", PROPERTY_HINT_RANGE, "1,1024,1"), "set_default_length", "get_default_length");}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 {	return rest;}Transform2D Bone2D::get_skeleton_rest() const {	if (parent_bone) {		return parent_bone->get_skeleton_rest() * rest;	} else {		return rest;	}}void Bone2D::apply_rest() {	set_transform(rest);}void Bone2D::set_default_length(float p_length) {	default_length = p_length;}float Bone2D::get_default_length() const {	return default_length;}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) {			warning += TTR("This Bone2D chain should end at a Skeleton2D node.");		} else {			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 warning;}Bone2D::Bone2D() {	skeleton = NULL;	parent_bone = NULL;	skeleton_index = -1;	default_length = 16;	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);	}}//////////////////////////////////////void Skeleton2D::_make_bone_setup_dirty() {	if (bone_setup_dirty)		return;	bone_setup_dirty = true;	if (is_inside_tree()) {		call_deferred("_update_bone_setup");	}}void Skeleton2D::_update_bone_setup() {	if (!bone_setup_dirty)		return;	bone_setup_dirty = false;	VS::get_singleton()->skeleton_allocate(skeleton, bones.size(), true);	bones.sort(); //sorty so they are always in the same order/index	for (int i = 0; i < bones.size(); i++) {		bones.write[i].rest_inverse = bones[i].bone->get_skeleton_rest().affine_inverse(); //bind pose		bones.write[i].bone->skeleton_index = i;		Bone2D *parent_bone = Object::cast_to<Bone2D>(bones[i].bone->get_parent());		if (parent_bone) {			bones.write[i].parent_index = parent_bone->skeleton_index;		} else {			bones.write[i].parent_index = -1;		}	}	transform_dirty = true;	_update_transform();	emit_signal("bone_setup_changed");}void Skeleton2D::_make_transform_dirty() {	if (transform_dirty)		return;	transform_dirty = true;	if (is_inside_tree()) {		call_deferred("_update_transform");	}}void Skeleton2D::_update_transform() {	if (bone_setup_dirty) {		_update_bone_setup();		return; //above will update transform anyway	}	if (!transform_dirty)		return;	transform_dirty = false;	for (int i = 0; i < bones.size(); i++) {		ERR_CONTINUE(bones[i].parent_index >= i);		if (bones[i].parent_index >= 0) {			bones.write[i].accum_transform = bones[bones[i].parent_index].accum_transform * bones[i].bone->get_transform();		} else {			bones.write[i].accum_transform = bones[i].bone->get_transform();		}	}	for (int i = 0; i < bones.size(); i++) {		Transform2D final_xform = bones[i].accum_transform * bones[i].rest_inverse;		VS::get_singleton()->skeleton_bone_set_transform_2d(skeleton, i, final_xform);	}}int Skeleton2D::get_bone_count() const {	ERR_FAIL_COND_V(!is_inside_tree(), 0);	if (bone_setup_dirty) {		const_cast<Skeleton2D *>(this)->_update_bone_setup();	}	return bones.size();}Bone2D *Skeleton2D::get_bone(int p_idx) {	ERR_FAIL_COND_V(!is_inside_tree(), NULL);	ERR_FAIL_INDEX_V(p_idx, bones.size(), NULL);	return bones[p_idx].bone;}void Skeleton2D::_notification(int p_what) {	if (p_what == NOTIFICATION_READY) {		if (bone_setup_dirty)			_update_bone_setup();		if (transform_dirty)			_update_transform();		request_ready();	}	if (p_what == NOTIFICATION_TRANSFORM_CHANGED) {		VS::get_singleton()->skeleton_set_base_transform_2d(skeleton, get_global_transform());	}}RID Skeleton2D::get_skeleton() const {	return skeleton;}void Skeleton2D::_bind_methods() {	ClassDB::bind_method(D_METHOD("_update_bone_setup"), &Skeleton2D::_update_bone_setup);	ClassDB::bind_method(D_METHOD("_update_transform"), &Skeleton2D::_update_transform);	ClassDB::bind_method(D_METHOD("get_bone_count"), &Skeleton2D::get_bone_count);	ClassDB::bind_method(D_METHOD("get_bone", "idx"), &Skeleton2D::get_bone);	ClassDB::bind_method(D_METHOD("get_skeleton"), &Skeleton2D::get_skeleton);	ADD_SIGNAL(MethodInfo("bone_setup_changed"));}Skeleton2D::Skeleton2D() {	bone_setup_dirty = true;	transform_dirty = true;	skeleton = VS::get_singleton()->skeleton_create();	set_notify_transform(true);}Skeleton2D::~Skeleton2D() {	VS::get_singleton()->free(skeleton);}
 |