將粒子復製到剛體
https://blenderartists.org/t/copy-particles-to-rigid-bodies/563269
bl_info = {
"name": "Copy Particles to Rigid Bodies",
"version": (0, 0, 21),
"blender": (2, 80, 0),
"location": "View3D > Sidebar",
"description": "Transfers dupliobjects from a PS to a Rigid Bodies simulation",
"category": "Animation",
}
import bpy, random
class Particles_to_Sim(bpy.types.Operator):
bl_idname = 'object.particles_to_simulation'
bl_label = 'Copy Particles'
bl_description = 'Transfers dupliobjects from a PS to a Rigid Bodies simulation'
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
obj = bpy.context.object
return(obj and obj.particle_systems)
def execute(self, context):
wm = bpy.context.window_manager
scn = bpy.context.scene
fps = scn.render.fps
obj = bpy.context.object
edg = bpy.context.evaluated_depsgraph_get()
sys = obj.evaluated_get(edg).particle_systems
set = sys.active.settings
par = sys.active.particles
txt = 'Set Particle System dupliobject to a Rigid Body object / group'
# disable simulation
scn.rigidbody_world.enabled = False
# to avoid PS cache troubles
# obj.particle_systems.active.seed += 1
# get dupliobject from particles system
if set.render_type == 'OBJECT':
duplist = [set.instance_object]
elif set.render_type == 'COLLECTION':
duplist = set.instance_collection.objects[:]
else:
self.report({'ERROR'}, txt)
return{'FINISHED'}
# check if dupliobjects are valid
for d in duplist:
if not d.rigid_body:
self.report({'ERROR'}, txt)
return{'FINISHED'}
# an Empty as parent allows to move / rotate later
bpy.ops.object.add(type='EMPTY')
bpy.context.object.name = 'Bullet Particles'
bpy.ops.object.select_all(action='DESELECT')
root = context.view_layer.objects.active
delta = obj.location * wm.use_loc
root.location = delta
for p in par:
dup = random.choice(duplist)
btime = int(round(p.birth_time))
scn.frame_set(btime)
phy = bpy.data.objects.new('particle.000', dup.data.copy()) ###
scn.collection.objects.link(phy)
context.view_layer.objects.active = phy #..?
phy.select_set(True)
phy.rotation_euler = p.rotation.to_euler()
bpy.ops.rigidbody.objects_add(type='ACTIVE')
scn.frame_set(scn.frame_current) #..?
phy.parent = root
phy.select_set(False)
sca = [p.size * s for s in dup.scale]
# copy some rigid body settings
phy.rigid_body.collision_shape = dup.rigid_body.collision_shape
phy.rigid_body.restitution = dup.rigid_body.restitution
phy.rigid_body.linear_damping = dup.rigid_body.linear_damping
phy.rigid_body.angular_damping = dup.rigid_body.angular_damping
phy.rigid_body.friction = dup.rigid_body.friction
phy.rigid_body.mass = dup.rigid_body.mass
# keyframe unborn particle
phy.scale = sca
phy.location = p.location - delta
phy.rigid_body.kinematic = True
phy.keyframe_insert('location', frame = btime)
phy.rigid_body.keyframe_insert('kinematic', frame = btime)
# keyframe particle pop up
if not set.show_unborn:
phy.scale = [0,0,0]
phy.keyframe_insert('scale', frame = btime - wm.grow_frames)
phy.scale = sca
phy.keyframe_insert('scale', frame = btime)
# keyframe alive particle
phy.location += p.velocity / fps * wm.vel_mult
phy.keyframe_insert('location', frame = btime + 1)
phy.rigid_body.kinematic = False
phy.rigid_body.keyframe_insert('kinematic', frame = btime + 2)
# hide emmitter
obj.hide_viewport = obj.hide_render = True
scn.frame_set(scn.frame_start)
bpy.ops.object.select_all(action='DESELECT')
context.view_layer.objects.active = root
root.select_set(True)
# enable simulation
scn.rigidbody_world.enabled = True
return{'FINISHED'}
class PanelP2RB(bpy.types.Panel):
bl_label = 'Particles to Simulation'
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = "SIM"
@classmethod
def poll(cls, context):
obj = bpy.context.object
return(obj and obj.particle_systems)
def draw(self, context):
wm = bpy.context.window_manager
scn = bpy.context.scene
obj = bpy.context.object
set = obj.particle_systems[0].settings
layout = self.layout
layout.operator('object.particles_to_simulation')
layout.prop(wm, 'use_loc')
column = layout.column(align=True)
column.prop(wm, 'vel_mult')
if not set.show_unborn:
column.prop(wm, 'grow_frames')
column.separator()
column.prop(scn.rigidbody_world, "use_split_impulse")
bpy.types.WindowManager.vel_mult=bpy.props.FloatProperty(name='Speed',
min=0.01, max=50, default=1, description='Particle speed multiplier')
bpy.types.WindowManager.use_loc=bpy.props.BoolProperty(name='Origin at emiter',
default=True, description='Place simulation root object at emiter start position '
', maybe disable for animated emitters')
bpy.types.WindowManager.grow_frames=bpy.props.IntProperty(name='Grow time',
min=1, max=50, default=1, description='Frames to scale particles before simulating')
classes = (
Particles_to_Sim,
PanelP2RB
)
register, unregister = bpy.utils.register_classes_factory(classes)
if __name__ == '__main__':
register()