The situation
I am using a boid physics particle system in Blender 2.82. I am rendering the particles using an object that has armature animation. Following a process similar to what is described in this tutorial. This works fine within Blender.
The problem
I would like to export the generated 'flock' to a model (glb to be specific) and in order to do so I need to create real bits of geometry for each particle and copy over the animation per frame of each particles to the newly created geometry.
I saw the answer and script here which works perfecty in terms of copying static geometries, but it does not allow for creating copies of models that have armature included (unless I am mistaken?)
So I adapted the script to the following
import bpy
# Set these to False if you don't want to key that property.
KEYFRAME_LOCATION = True
KEYFRAME_ROTATION = True
KEYFRAME_SCALE = False
MATCHPARTICLE_SCALE = True
KEYFRAME_VISIBILITY = False # Viewport and render visibility. set this to false otherwise animations don't export to gltf
def create_objects_for_particles(ps, ps_name):
#deselect source
bpy.data.objects[ps_name].select_set(False)
# store a ref for every object to match every particle
obj_list = []
for i, _ in enumerate(ps.particles):
#need to rename duplicate bones for gltf export
boneName = "bone_{}".format(i)
#duplicate objects and store the armature to animate
dupli = bpy.ops.object.duplicate()
#select the recent duplicate objects
activeObjects = bpy.context.selected_objects
#loop through the objects
for ob in activeObjects:
#just get the armature
if ob.type == 'ARMATURE':
#rename bones
for bone in ob.data.bones:
bone.name = boneName
#add the new object to the list
obj_list.append(ob)
return obj_list
def match_and_keyframe_objects(ps, obj_list, start_frame, end_frame):
# Match and keyframe the objects to the particles for every frame in the
# given range.
for frame in range(start_frame, end_frame + 1):
print("frame {} processed".format(frame))
bpy.context.scene.frame_set(frame)
for p, obj in zip(ps.particles, obj_list):
match_object_to_particle(p, obj)
keyframe_obj(obj)
def match_object_to_particle(p, obj):
# Match the location, rotation, scale and visibility of the object to
# the particle.
loc = p.location
rot = p.rotation
size = p.size
if p.alive_state == 'ALIVE':
vis = True
else:
vis = False
obj.location = loc
# Set rotation mode to quaternion to match particle rotation.
obj.rotation_mode = 'QUATERNION'
obj.rotation_quaternion = rot
if MATCHPARTICLE_SCALE:
obj.scale = (size, size, size)
#obj.hide_viewport = not(vis) # <<<-- this was called "hide" in <= 2.79
#obj.hide_render = not(vis)
def keyframe_obj(obj):
# Keyframe location, rotation, scale and visibility if specified.
if KEYFRAME_LOCATION:
obj.keyframe_insert("location")
if KEYFRAME_ROTATION:
obj.keyframe_insert("rotation_quaternion")
if KEYFRAME_SCALE:
obj.keyframe_insert("scale")
if KEYFRAME_VISIBILITY:
obj.keyframe_insert("hide_viewport") # <<<-- this was called "hide" in <= 2.79
obj.keyframe_insert("hide_render")
def main():
#in 2.8 you need to evaluate the Dependency graph in order to get data from animation, modifiers, etc
depsgraph = bpy.context.evaluated_depsgraph_get()
# Assume only 2 objects are selected.
# The active object should be the one with the particle system.
ps_obj = bpy.context.object
ps_obj_evaluated = depsgraph.objects[ ps_obj.name ]
ps_name = ps_obj.name
ps = ps_obj_evaluated.particle_systems[0] # Assume only 1 particle system is present.
start_frame = bpy.context.scene.frame_start
end_frame = bpy.context.scene.frame_end
obj_list = create_objects_for_particles(ps, ps_name)
match_and_keyframe_objects(ps, obj_list, start_frame, end_frame)
if __name__ == '__main__':
main()
Which works... ish, the problem is that it is very inconsistent.
Sometimes it works perfectly,
sometimes it only duplicates the objects but does not produce any keyframes,
sometimes it just crashes blender altogether. etc. I can't see any logic to when it does or doesn't work.
I think my script is logical and since it does work sometimes I think it must be going in right direction but I am a bit of a novice when it comes to scripting so I may be making basic errors.
I wonder if anyone would be willing to have a go with that script and see if it works for them?
The instructions are similar to the original,
first you need to select your armatured object (both the mesh and the bones)
then you select the object that has the particle system attached to it
so you should have 3 objects selected in total. Then run that script.
See below a link to example file, you will find an object with animated armature, a particle system set up and the script is in the scripting tab called 'convert'.
https://cdn.glitch.com/50a342dd-f2eb-4ff5-81ed-e7dff1e4e4cc%2Fparticletogeom_example.blend.zip
Any advice is much appreciated, there might just be some basic issues with my python, too many loops or something.