Skip to content

Commit

Permalink
gltf: Fix mesh nodes which are also bones.
Browse files Browse the repository at this point in the history
Fix issue when two skeletons end up directly parented.
Prevent animating TRS for skinned Mesh node.
Fix animating weights on meshes with targets but no weights.
  • Loading branch information
lyuma committed May 26, 2021
1 parent 1da3270 commit f151c64
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 117 deletions.
165 changes: 64 additions & 101 deletions modules/gltf/gltf_document.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2894,6 +2894,8 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
}
blend_weights.write[j] = weights[j];
}
}
if (blend_weights.size() != 0) {
mesh->set_blend_weights(blend_weights);
}
mesh->set_mesh(import_mesh);
Expand Down Expand Up @@ -4196,80 +4198,10 @@ Error GLTFDocument::_reparent_non_joint_skeleton_subtrees(Ref<GLTFState> state,
subtree_set.get_members(subtree_nodes, subtree_root);

for (int subtree_i = 0; subtree_i < subtree_nodes.size(); ++subtree_i) {
ERR_FAIL_COND_V(_reparent_to_fake_joint(state, skeleton, subtree_nodes[subtree_i]), FAILED);

// We modified the tree, recompute all the heights
_compute_node_heights(state);
}
}

return OK;
}

Error GLTFDocument::_reparent_to_fake_joint(Ref<GLTFState> state, Ref<GLTFSkeleton> skeleton, const GLTFNodeIndex node_index) {
Ref<GLTFNode> node = state->nodes[node_index];

// Can we just "steal" this joint if it is just a spatial node?
if (node->skin < 0 && node->mesh < 0 && node->camera < 0) {
node->joint = true;
// Add the joint to the skeletons joints
skeleton->joints.push_back(node_index);
return OK;
}

GLTFNode *fake_joint = memnew(GLTFNode);
const GLTFNodeIndex fake_joint_index = state->nodes.size();
state->nodes.push_back(fake_joint);

// We better not be a joint, or we messed up in our logic
if (node->joint)
return FAILED;

fake_joint->translation = node->translation;
fake_joint->rotation = node->rotation;
fake_joint->scale = node->scale;
fake_joint->xform = node->xform;
fake_joint->joint = true;

// We can use the exact same name here, because the joint will be inside a skeleton and not the scene
fake_joint->set_name(node->get_name());

// Clear the nodes transforms, since it will be parented to the fake joint
node->translation = Vector3(0, 0, 0);
node->rotation = Quat();
node->scale = Vector3(1, 1, 1);
node->xform = Transform();

// Transfer the node children to the fake joint
for (int child_i = 0; child_i < node->children.size(); ++child_i) {
Ref<GLTFNode> child = state->nodes[node->children[child_i]];
child->parent = fake_joint_index;
}

fake_joint->children = node->children;
node->children.clear();

// add the fake joint to the parent and remove the original joint
if (node->parent >= 0) {
Ref<GLTFNode> parent = state->nodes[node->parent];
parent->children.erase(node_index);
parent->children.push_back(fake_joint_index);
fake_joint->parent = node->parent;
}

// Add the node to the fake joint
fake_joint->children.push_back(node_index);
node->parent = fake_joint_index;
node->fake_joint_parent = fake_joint_index;

// Add the fake joint to the skeletons joints
skeleton->joints.push_back(fake_joint_index);

// Replace skin_skeletons with fake joints if we must.
for (GLTFSkinIndex skin_i = 0; skin_i < state->skins.size(); ++skin_i) {
Ref<GLTFSkin> skin = state->skins.write[skin_i];
if (skin->skin_root == node_index) {
skin->skin_root = fake_joint_index;
Ref<GLTFNode> node = state->nodes[subtree_nodes[subtree_i]];
node->joint = true;
// Add the joint to the skeletons joints
skeleton->joints.push_back(subtree_nodes[subtree_i]);
}
}

Expand Down Expand Up @@ -5016,9 +4948,9 @@ void GLTFDocument::_assign_scene_names(Ref<GLTFState> state) {
}
}

BoneAttachment *GLTFDocument::_generate_bone_attachment(Ref<GLTFState> state, Skeleton *skeleton, const GLTFNodeIndex node_index) {
BoneAttachment *GLTFDocument::_generate_bone_attachment(Ref<GLTFState> state, Skeleton *skeleton, const GLTFNodeIndex node_index, const GLTFNodeIndex bone_index) {
Ref<GLTFNode> gltf_node = state->nodes[node_index];
Ref<GLTFNode> bone_node = state->nodes[gltf_node->parent];
Ref<GLTFNode> bone_node = state->nodes[bone_index];

BoneAttachment *bone_attachment = memnew(BoneAttachment);
print_verbose("glTF: Creating bone attachment for: " + gltf_node->get_name());
Expand Down Expand Up @@ -5535,7 +5467,20 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> state, Node *scene_parent
Skeleton *skeleton = state->skeletons[gltf_node->skeleton]->godot_skeleton;

if (active_skeleton != skeleton) {
ERR_FAIL_COND_MSG(active_skeleton != nullptr, "glTF: Generating scene detected direct parented Skeletons");
if (active_skeleton != nullptr) {
BoneAttachment *bone_attachment = _generate_bone_attachment(state, active_skeleton, node_index, gltf_node->parent);

scene_parent->add_child(bone_attachment);
bone_attachment->set_owner(scene_root);

// There is no gltf_node that represent this, so just directly create a unique name
bone_attachment->set_name(_gen_unique_name(state, "BoneAttachment"));

// We change the scene_parent to our bone attachment now. We do not set current_node because we want to make the node
// and attach it to the bone_attachment
scene_parent = bone_attachment;
WARN_PRINT(vformat("glTF: Generating scene detected direct parented Skeletons at node %d", node_index));
}

// Add it to the scene if it has not already been added
if (skeleton->get_parent() == nullptr) {
Expand All @@ -5548,23 +5493,39 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> state, Node *scene_parent
current_node = skeleton;
}

bool use_xform = true;
// If we have an active skeleton, and the node is node skinned, we need to create a bone attachment
if (current_node == nullptr || gltf_node->mesh >= 0 || gltf_node->camera >= 0 || gltf_node->light >= 0) {
if (active_skeleton != nullptr && gltf_node->skin < 0) {
BoneAttachment *bone_attachment = _generate_bone_attachment(state, active_skeleton, node_index);
if (current_node == nullptr && active_skeleton != nullptr && gltf_node->skin < 0) {
BoneAttachment *bone_attachment = _generate_bone_attachment(state, active_skeleton, node_index, gltf_node->parent);

scene_parent->add_child(bone_attachment);
bone_attachment->set_owner(scene_root);
scene_parent->add_child(bone_attachment);
bone_attachment->set_owner(scene_root);

// There is no gltf_node that represent this, so just directly create a unique name
bone_attachment->set_name(_gen_unique_name(state, "BoneAttachment"));
// There is no gltf_node that represent this, so just directly create a unique name
bone_attachment->set_name(_gen_unique_name(state, "BoneAttachment"));

// We change the scene_parent to our bone attachment now. We do not set current_node because we want to make the node
// and attach it to the bone_attachment
scene_parent = bone_attachment;
}
// We change the scene_parent to our bone attachment now. We do not set current_node because we want to make the node
// and attach it to the bone_attachment
scene_parent = bone_attachment;
} else if (current_node != nullptr && active_skeleton != nullptr &&
((gltf_node->skin < 0 && gltf_node->mesh >= 0) || gltf_node->camera >= 0 || gltf_node->light >= 0)) {
BoneAttachment *bone_attachment = _generate_bone_attachment(state, active_skeleton, node_index, node_index);

scene_parent->add_child(bone_attachment);
bone_attachment->set_owner(scene_root);

// There is no gltf_node that represent this, so just directly create a unique name
bone_attachment->set_name(_gen_unique_name(state, "BoneAttachment"));

// We still have not managed to make a node
// We change the scene_parent to our bone attachment now. We do not set current_node because we want to make the node
// and attach it to the bone_attachment
scene_parent = bone_attachment;
current_node = nullptr;
use_xform = false;
}

// We still have not managed to make a node
if (current_node == nullptr || (gltf_node->skin >= 0 && gltf_node->mesh >= 0)) {
if (gltf_node->mesh >= 0) {
current_node = _generate_mesh_instance(state, scene_parent, node_index);
} else if (gltf_node->camera >= 0) {
Expand Down Expand Up @@ -5594,7 +5555,7 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> state, Node *scene_parent
state->scene_nodes.insert(node_index, current_node);

for (int i = 0; i < gltf_node->children.size(); ++i) {
_generate_scene_node(state, current_node, scene_root, gltf_node->children[i]);
_generate_scene_node(state, gltf_node->skeleton >= 0 ? active_skeleton : current_node, scene_root, gltf_node->children[i]);
}
}

Expand Down Expand Up @@ -5735,29 +5696,31 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,

for (Map<int, GLTFAnimation::Track>::Element *track_i = anim->get_tracks().front(); track_i; track_i = track_i->next()) {
const GLTFAnimation::Track &track = track_i->get();
//need to find the path
//need to find the path: for skeletons, weight tracks will affect the mesh
NodePath node_path;
//for skeletons, transform tracks always affect bones
NodePath transform_node_path;

GLTFNodeIndex node_index = track_i->key();
if (state->nodes[node_index]->fake_joint_parent >= 0) {
// Should be same as parent
node_index = state->nodes[node_index]->fake_joint_parent;
}

const Ref<GLTFNode> gltf_node = state->nodes[track_i->key()];

Node *root = ap->get_parent();
ERR_FAIL_COND(root == nullptr);
Node *godot_node = state->scene_nodes.find(node_index)->get();
node_path = root->get_path_to(godot_node);

if (gltf_node->skeleton >= 0) {
const Skeleton *sk = state->skeletons[gltf_node->skeleton]->godot_skeleton;
ERR_FAIL_COND(sk == nullptr);

const String path = ap->get_parent()->get_path_to(sk);
const String bone = gltf_node->get_name();
node_path = path + ":" + bone;
transform_node_path = path + ":" + bone;
} else {
Node *root = ap->get_parent();
Node *godot_node = state->scene_nodes.find(node_index)->get();
node_path = root->get_path_to(godot_node);
transform_node_path = node_path;
}
bool transform_affects_skinned_mesh_instance = gltf_node->skeleton < 0 && gltf_node->skin >= 0;

for (int i = 0; i < track.rotation_track.times.size(); i++) {
length = MAX(length, track.rotation_track.times[i]);
Expand All @@ -5775,11 +5738,11 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
}
}

if (track.rotation_track.values.size() || track.translation_track.values.size() || track.scale_track.values.size()) {
if ((track.rotation_track.values.size() || track.translation_track.values.size() || track.scale_track.values.size()) && !transform_affects_skinned_mesh_instance) {
//make transform track
int track_idx = animation->get_track_count();
animation->add_track(Animation::TYPE_TRANSFORM);
animation->track_set_path(track_idx, node_path);
animation->track_set_path(track_idx, transform_node_path);
//first determine animation length

const double increment = 1.0 / bake_fps;
Expand Down
3 changes: 2 additions & 1 deletion modules/gltf/gltf_document.h
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,8 @@ class GLTFDocument : public Resource {
Error _serialize_animations(Ref<GLTFState> state);
BoneAttachment *_generate_bone_attachment(Ref<GLTFState> state,
Skeleton *skeleton,
const GLTFNodeIndex node_index);
const GLTFNodeIndex node_index,
const GLTFNodeIndex bone_index);
Spatial *_generate_mesh_instance(Ref<GLTFState> state, Node *scene_parent, const GLTFNodeIndex node_index);
Camera *_generate_camera(Ref<GLTFState> state, Node *scene_parent,
const GLTFNodeIndex node_index);
Expand Down
11 changes: 0 additions & 11 deletions modules/gltf/gltf_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,6 @@ void GLTFNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_scale", "scale"), &GLTFNode::set_scale);
ClassDB::bind_method(D_METHOD("get_children"), &GLTFNode::get_children);
ClassDB::bind_method(D_METHOD("set_children", "children"), &GLTFNode::set_children);
ClassDB::bind_method(D_METHOD("get_fake_joint_parent"), &GLTFNode::get_fake_joint_parent);
ClassDB::bind_method(D_METHOD("set_fake_joint_parent", "fake_joint_parent"), &GLTFNode::set_fake_joint_parent);
ClassDB::bind_method(D_METHOD("get_light"), &GLTFNode::get_light);
ClassDB::bind_method(D_METHOD("set_light", "light"), &GLTFNode::set_light);

Expand All @@ -72,7 +70,6 @@ void GLTFNode::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::QUAT, "rotation"), "set_rotation", "get_rotation"); // Quat
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "scale"), "set_scale", "get_scale"); // Vector3
ADD_PROPERTY(PropertyInfo(Variant::POOL_INT_ARRAY, "children"), "set_children", "get_children"); // Vector<int>
ADD_PROPERTY(PropertyInfo(Variant::INT, "fake_joint_parent"), "set_fake_joint_parent", "get_fake_joint_parent"); // GLTFNodeIndex
ADD_PROPERTY(PropertyInfo(Variant::INT, "light"), "set_light", "get_light"); // GLTFLightIndex
}

Expand Down Expand Up @@ -172,14 +169,6 @@ void GLTFNode::set_children(Vector<int> p_children) {
children = p_children;
}

GLTFNodeIndex GLTFNode::get_fake_joint_parent() {
return fake_joint_parent;
}

void GLTFNode::set_fake_joint_parent(GLTFNodeIndex p_fake_joint_parent) {
fake_joint_parent = p_fake_joint_parent;
}

GLTFLightIndex GLTFNode::get_light() {
return light;
}
Expand Down
4 changes: 0 additions & 4 deletions modules/gltf/gltf_node.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ class GLTFNode : public Resource {
Quat rotation;
Vector3 scale = Vector3(1, 1, 1);
Vector<int> children;
GLTFNodeIndex fake_joint_parent = -1;
GLTFLightIndex light = -1;

protected:
Expand Down Expand Up @@ -96,9 +95,6 @@ class GLTFNode : public Resource {
Vector<int> get_children();
void set_children(Vector<int> p_children);

GLTFNodeIndex get_fake_joint_parent();
void set_fake_joint_parent(GLTFNodeIndex p_fake_joint_parent);

GLTFLightIndex get_light();
void set_light(GLTFLightIndex p_light);
};
Expand Down

0 comments on commit f151c64

Please sign in to comment.