At the moment I am trying to write new nodes for GeometryNodes.
My tasks are to write a node that will accept only the Armature Type. (I think this is not a problem, I have already written some node that checks whether it is armature).
But I ran into a problem, and I do not really see any documentation about it.
Let’s imagine that there is armature - it has bones.
I want to create dynamic outputs from the node with the name of each bone from the armature.
You have to choose, or armature input socket or dynamic output sockets. And still quite hard to implement for beginner and not sure this is mean to be commited into blender so this is right place to discuse this.
For a node that dynamically adds sockets you could check out the Index Switch node:
However, the design as described doesn’t work very well, because the Object is itself a runtime value. At the time when the node sockets are defined the actual object is unknown, so you can’t actually tell what (if any) bones it has and which sockets would need to be added. The node can be evaluated in different context with a different armature.
If you make the armature object a fixed property of the node itself you could get the actual pointer in the node_declare function - but then it cannot be a socket and the node is not a re-usable building block any more. It gets tied to one specific object and users would have to change the setting inside the node tree.
The above aside, I’m also not sure its a usable/workable design option. I mean the Armature could easily have 100’s of bones. Remember, not only are the deform bones actual bones, but so are all the animation control bones, so average human character, 1000+ bones, easy.
Just the fingers/thumb would be 15 deform bones, likely double for FK/IK/Tweak control bones, then add the rest of the hand, then double again for other hand, I think you get the picture.
A massive long Armature node.
What you’d need is some sort of method/nodes to select/extract the bones or bone chain that you then want the rest of the node tree to work on.
But now of course we are starting to talk about a whole rigging nodes system and the dev’s aren’t up to that stage yet.
Since I have a rather small understanding of writing nodes, by default I will output the vector-position of the bone (To begin with). I think I will somehow select the bone in the menu.
Yeah. Probably good idea. I trying to understand how to get pointer of Armature. In python it was pretty easy. In C++ there almost no documentation about it. If you can send some link - would be great.
You will likely need to make changes in source/blender/nodes/intern/geometry_nodes_dependencies.cc and source/blender/modifiers/intern/MOD_nodes.cc.
Basically you need to add a dependency graph relation, so that Blender knows to update the geometry nodes when the armature is modified. update_depsgraph in MOD_armature.cc shows how to add such relations.
Simplest would be to start with adding a DEG_OB_COMP_EVAL_POSE relation for the whole pose, and then maybe later refine it so it’s only for the individual bones.
Hell yeah! Its working. Im not sure if its optimized (probably not, need to code review).
But! yeah! I did changes with source/blender/nodes/intern/geometry_nodes_dependencies.cc
and source/blender/modifiers/intern/MOD_nodes.cc.
Basically what I did:
I added few function to dependecies.cc
static void add_object_socket_pose_dependency(const bNodeSocket &socket,
GeometryNodesEvalDependencies &deps)
{
if (socket.is_input()) {
if (socket.is_logically_linked()) {
return;
}
}
if (socket.type == SOCK_OBJECT) {
if (Object *object = static_cast<bNodeSocketValueObject *>(socket.default_value)->value) {
if (object->type == OB_ARMATURE) {
deps.add_object(object);
int session_uid = object->id.session_uid;
if (deps.objects_info.contains(session_uid)) {
auto &info = deps.objects_info.lookup(session_uid);
info.armature_pose = true;
}
}
}
}
}
static void add_armature_pose_dependencies(const bNodeTree &tree,
GeometryNodesEvalDependencies &deps)
{
for (const bNode *node : tree.nodes_by_type("GeometryNodeArmatureInfo")) {
if (node->is_muted()) {
continue;
}
for (const bNodeSocket *socket : node->input_sockets()) {
if (STREQ(socket->name, "Object")) {
add_object_socket_pose_dependency(*socket, deps);
break;
}
}
}
}
and some changes to MOD_nodes.cc
if (object.type == OB_ARMATURE) {
if (info.armature_pose) {
DEG_add_object_relation(ctx->node, &object, DEG_OB_COMP_EVAL_POSE, "Nodes Modifier");
}
}
}
Thank you very much! Also, the code on half done with ChatGPT (Yeah im suck in C++, but started to understand how it works) now i can add more nodes, and make more optimized stuff.
Also, here is example:
The main good thing - I understand how it works finally.
Use Parent - bool value. When the parameter is enabled, the position of the bone is taken taking into account the parent. When disabled, the position is taken without taking into account the parent, if the bone as a whole would not have a parent.
Local Axis - bool value. If disabled, the world orientation of the bone is taken. If enabled, the local orientation of the bone is taken.
This looks super cool !
Do you think a transform matrix could be outputed to be alligned with the object info ? I’m guessing that a lot of easy operations would be done only using the transform information.