I have a vehicle I’ve modeled in Blender. The 4 engines and 4 landing legs could benefit from shared meshes and animations. I could of course export a single engine and landing leg with animation, then instantiate in Godot.
But instead of maintaining multiple part files, is there any way I can set this up in Blender such that on export/import, Godot will recognize these as scene instances (both mesh and animation)?
Blender certainly agrees with you, there is multiple ways to share mesh data within blender. You can instantiate “collections” which behave somewhat similar to scenes, and you can link mesh data between objects which behaves similar to resources in Godot. The latter may already be happening on export and Godot may be picking up on that shared mesh data.
I believe the advanced imported (opened by double-clicking a .glb file) will show the scene, individual mesh data object, and materials. If you only see two or three meshes listed in the Mesh tab then it’s probably being imported as shared data already, otherwise you may have to change some of your blender data.
I think this is partially working. Using a quick test scene, I see multiple meshes, and while the mesh instance nodes have unique names, the actual mesh blocks share the same name, so I’m assuming this means they are using the same data block, yeah?
However, this doesn’t seem to also instance the animation. They are instead all linked to a single animation action. I’m looking to be able to instance the animation as well, so that the engines/landing gear can articulate independently.
Animating objects in blender is clunky on export sadly. The objects are always unique, even though they share mesh data it’s the Objects being animated not the mesh. I think your best bet is to animate one object, in the advanced imported your can save that animation to a file then apply it to many AnimationPlayers with different Root Node properties, hopefully the animation path can line up (or be overridden in the importer) in a way that is easy to assign Root Nodes within this imported scene; if not you may have to re-organize and re-name your collections and objects.
Unless I’m missing some steps, this is looking like a bust. The animation player wont accept the collection as the root. It wants the entire scene’s root. I’m guessing this would work if I gave each engine/landing gear an armature, and used the skeleton as the root, but skinning a bunch of mechanical parts seems like unnecessary overhead just for the convenience of having everything in one import file.
Exporting a single engine and landing gear separately is starting to sound like an attractive option. What do you think?
Adding the Weights may not be that hard, select all set to 100% in this case. But yeah it is a ~strange~ step editing the model, may have more complications anyways. On the other side may be good practice to animate the machines, more articulate animations are always nice right?
By overhead, I mean that with using an armature, Godot has to evaluate and translate each vertex bound to a bone, vs just animating each part’s origin. I’m sure the performance difference is negligible, but that’s why I made the choice to just skip out on the skeleton.
In the case of the landing gear, there is a more complex rig involved so that the shocks articulate correctly, but it’s using object constraints that get baked down to keyframes on export. Then I plan to use a bit of code to seek their compression animations so they conform to uneven terrain.
You can add smarts to your import process with import scripts.
Suppose you have exported some set of meshes with 4 engines under an “instances_engine” Node3D parent.
You can have the script nuke and rebuild the MeshInstance3D nodes of 3 of those 4 engines with shared data:
var engine_collection = $"airship/instances_engine"
var source_of_instances = engine_collection.get_children[0]
var children_list = engine_collection.get_children
var transforms = []
for instance in children_list.slice(1):
transforms.append(instance.transform)
for i in transforms.length():
# remove child that is not first instance.
engine_collection.remove_child(children_list[1 + i])
# replace old child with new child that shares mesh with first instance.
var new_simplified_instance = MeshInstance3D.new()
new_simplified_instance.mesh = source_of_instances.mesh
# new child inherits transform from old separate-mesh child.
new_simplified_instance.transform = transforms[i]
# add a dedicated animation player for this instance.
new_simplified_instance.add_child(AnimationPlayer.new())
# ....
# do some code here to copypaste animations from the old
# animation player and redirect their tracks' nodepaths
# to the new instance.
copy_animations(["tilt_forward", "tilt_backward", "angle_out", "angle_in", "expose", "dock"],
$"Animation Player", new_simplified_instance.get_children()[0])
# ....
# finally add the reconstructed instance back in.
engine_collection.add_child(new_simplified_instance)
Though i’m not sure if blender or the GLTF spec support exporting animations of transforms of nested objects without skinning their vertices to some bone on a skeleton.
keeping the engine separate and reassembling instances on a scene after import does sound like less hassle.
Just in case you someday end up having to share the same animation resource on different players for other reasons:
AnimationPlayer extends AnimationMixer.
AnimationMixer has AnimationLibrary.
AnimationLibrary is a resource indexing animation name strings to Animation resources. animation_player.get_animation_library("") should give you the shared collection of all animations stored on that player’s global space.
you can iterate on the library and copy references to its animations to the global animation library of another player with a differerent node set as root. That way, changing an animation reflects on all players that use it without manually propagating the changed keyframes.
Yeah I may end up going that route, but I still think your idea is worth exploring if I can get instancing to work with it. If nothing else, I’ll learn a bit more about import scripts, which is something I haven’t used much.