Repository: godotengine/godot
File: scene/3d/spring_bone_simulator_3d.cpp (C++)

Code before:

SpringBoneSimulator3D::BoneDirection SpringBoneSimulator3D::get_end_bone_direction(int p_index) const {
	ERR_FAIL_INDEX_V(p_index, settings.size(), BONE_DIRECTION_FROM_PARENT);
	return settings[p_index]->end_bone_direction;
}

void SpringBoneSimulator3D::set_end_bone_length(int p_index, float p_length) {
	ERR_FAIL_INDEX(p_index, settings.size());
	settings[p_index]->end_bone_length = p_length;
	_make_joints_dirty(p_index);
}

float SpringBoneSimulator3D::get_end_bone_length(int p_index) const {
	ERR_FAIL_INDEX_V(p_index, settings.size(), 0);
	return settings[p_index]->end_bone_length;
}

Vector3 SpringBoneSimulator3D::get_end_bone_axis(int p_end_bone, BoneDirection p_direction) const {
	Vector3 axis;
	if (p_direction == BONE_DIRECTION_FROM_PARENT) {
		Skeleton3D *sk = get_skeleton();
		if (sk) {
			axis = sk->get_bone_rest(p_end_bone).origin; // Parent to end bone, on the parent space.
			int parent = sk->get_bone_parent(p_end_bone);
			if (parent >= 0) {
				axis = sk->get_bone_rest(parent).basis.xform_inv(axis);
			}
			axis = sk->get_bone_rest(p_end_bone).basis.xform_inv(axis);
			axis.normalize();
		}
	} else {
		axis = get_vector_from_bone_axis(static_cast<BoneAxis>((int)p_direction));
	}
	return axis;
}

void SpringBoneSimulator3D::set_center_from(int p_index, CenterFrom p_center_from) {
	ERR_FAIL_INDEX(p_index, settings.size());
	bool center_changed = settings[p_index]->center_from != p_center_from;
	settings[p_index]->center_from = p_center_from;
	if (center_changed) {
		reset();
	}
	notify_property_list_changed();
}

SpringBoneSimulator3D::CenterFrom SpringBoneSimulator3D::get_center_from(int p_index) const {
	ERR_FAIL_INDEX_V(p_index, settings.size(), CENTER_FROM_WORLD_ORIGIN);
	return settings[p_index]->center_from;
}


Review comment (suggestion):
I do not believe this change is correct.

The correct way to transform a vector from parent space to child bone (end bone) space is sk->get_bone_rest(p_end_bone).basis.xform_inv(axis), as the code was written before.

Code after:

SpringBoneSimulator3D::BoneDirection SpringBoneSimulator3D::get_end_bone_direction(int p_index) const {
	ERR_FAIL_INDEX_V(p_index, settings.size(), BONE_DIRECTION_FROM_PARENT);
	return settings[p_index]->end_bone_direction;
}

void SpringBoneSimulator3D::set_end_bone_length(int p_index, float p_length) {
	ERR_FAIL_INDEX(p_index, settings.size());
	settings[p_index]->end_bone_length = p_length;
	_make_joints_dirty(p_index);
}

float SpringBoneSimulator3D::get_end_bone_length(int p_index) const {
	ERR_FAIL_INDEX_V(p_index, settings.size(), 0);
	return settings[p_index]->end_bone_length;
}

Vector3 SpringBoneSimulator3D::get_end_bone_axis(int p_end_bone, BoneDirection p_direction) const {
	Vector3 axis;
	if (p_direction == BONE_DIRECTION_FROM_PARENT) {
		Skeleton3D *sk = get_skeleton();
		if (sk) {
			axis = sk->get_bone_rest(p_end_bone).basis.xform_inv(sk->get_bone_rest(p_end_bone).origin);
			axis.normalize();
		}
	} else {
		axis = get_vector_from_bone_axis(static_cast<BoneAxis>((int)p_direction));
	}
	return axis;
}

void SpringBoneSimulator3D::set_center_from(int p_index, CenterFrom p_center_from) {
	ERR_FAIL_INDEX(p_index, settings.size());
	bool center_changed = settings[p_index]->center_from != p_center_from;
	settings[p_index]->center_from = p_center_from;
	if (center_changed) {
		reset();
	}
	notify_property_list_changed();
}

SpringBoneSimulator3D::CenterFrom SpringBoneSimulator3D::get_center_from(int p_index) const {
	ERR_FAIL_INDEX_V(p_index, settings.size(), CENTER_FROM_WORLD_ORIGIN);
	return settings[p_index]->center_from;
}

void SpringBoneSimulator3D::set_center_node(int p_index, const NodePath &p_node_path) {
	ERR_FAIL_INDEX(p_index, settings.size());
	bool center_changed = settings[p_index]->center_node != p_node_path;
	settings[p_index]->center_node = p_node_path;
	if (center_changed) {