2023年2月23日 星期四

將粒子復製到剛體

 將粒子復製到剛體

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()