суббота, 10 декабря 2016 г.

Зря жаловался.

    Внезапно всё пошло, причем почти сразу же после написания предыдущего поста. Логику добавления системы частиц я поменял, еще кое-какие изменения есть. По сути эта версия уже Бета, основной функционал готов, осталось только напильником пройтись, попереименовывать некоторые переменные, определиться с диапазоном их изменения, возможно добавить еще каких настроек, или наоборот, какие-то настройки убрать. Еще добавить всякие подсказки, короче красиво оформить. Да, проверку типа объекта надо сделать, чтобы оно не пыталось взорвать лампочку, к примеру.
 
    Долго провозился с системой частиц, сразу всем хочу сказать, не надо для изменения текущего кадра в сцене использовать scene.frame_current, оно так не работает, система частиц плевать хотела на такое изменение, юзайте scene.frame_set(), если бы я сразу им воспользовался, то пару дней не провозился бы.


    Ниже код, какой он есть сейчас. ДЛИННОТА!






bl_info = {
    "name" : "ExplodeToShape",
    "author" : "denis8424",
    "category" : "Animation",
    "version" : (0, 7),
    }

import bpy

def main(animationProps, solidifyProps, dead_Explode, particleProps):
 
    print('<<<<<<<>>>>>>>') # это чтобы в консоли отличать сообщения между
                            # разными запусками оператора
 
    ########################
    # VAR 
    ########################
 
    # выковыриваем из списков переменные. Списки, чтобы вызов функции 
    # был короче, оно в принципе не обязательно, просто я лентяй
 
    # количество кадров, приходящееся на один ключ
    frameWithAKey = animationProps[0]
    # стартовый кадр анимации
    startFr = animationProps[1]
    # конечный кадр анимации
    end_Fr = animationProps[2]
    # использовать ли модификатор Твердости
    useSolidify = solidifyProps[0]
    # параметр толщины вышеупомянутого модификатора Твердости
    thickness = solidifyProps[1]
 
    # Теперь пошли настройки системы частиц
 
    # количество "кусков"
    count = particleProps[0]
    # время жизни частиц
    lifetime = particleProps[1]
    # Эти два параметра управляют индивидуальной скоростью разлета осколков    
    # направление задается случайным образом
    factor_random = particleProps[2]
    # направление задается от нормали
    factor_normal = particleProps[3]
 
    # А эти три - задают общий для всех обломков вектор полета
    object_align_factor_X = particleProps[4]
    object_align_factor_Y = particleProps[5]
    object_align_factor_Z = particleProps[6]
                             
    # выковыриваем из контекста нужные данные
    
    # текущая сцена
    scene = bpy.context.scene
    # объект, для которого вызывается оператор
    object = bpy.context.object
    # имя объекта, нужно для задания имен ключам
    name = object.name
 
    # создаем список имен используемых объектов
    # формат - [имя, соответствующий кадр анимации, индекс в списке]
 
    listNames = []
    # сразу создаем переменную индекса, надо будет посмотреть, может её выкинуть
    index = 0
 
    # Вот теперь пошла работа
 
    # Создаем клон нашего объекта
    bpy.ops.object.duplicate()
    # Получаем его, он будет клонироваться, на нем висят все модификаторы и он
    # же отвечает за форму базового ключа
    basisOb = scene.objects.active
    # переименовываем нашу основу
    basisOb.name = name + ' BasisExplode'
    # добавляем модификатор системы частиц    
    bpy.ops.object.modifier_add(type = 'PARTICLE_SYSTEM')
    # получаем добавленную систему частиц
    part_S = basisOb.particle_systems['ParticleSystem']
    # и настраиваем её, переменные мы знаем
    # начало и конец испускания частиц должны быть одинаковы!
    part_S.settings.frame_start = startFr
    part_S.settings.frame_end = startFr  
    part_S.settings.count = count
    part_S.settings.lifetime = lifetime
    part_S.settings.factor_random = factor_random
    part_S.settings.normal_factor = factor_normal
    part_S.settings.object_align_factor[0] = object_align_factor_X
    part_S.settings.object_align_factor[1] = object_align_factor_Y
    part_S.settings.object_align_factor[2] = object_align_factor_Z
    part_S.settings.integrator = 'EULER'
    part_S.settings.use_modifier_stack = True
    # Надо наверное сделать возможность смены типа физики частиц
    part_S.settings.physics_type = 'NEWTON'
 
    # Теперь добавляем модификатор взрыва  
    bpy.ops.object.modifier_add(type = 'EXPLODE')
 
    # И модификатор "Твердости", если таковая галочка установлена
    if useSolidify == True:
        bpy.ops.object.modifier_add(type = 'SOLIDIFY')
        basisOb.modifiers['Solidify'].thickness = 0.1
    # добавляем базовый объект в список имен
    listNames.append((basisOb.name, startFr, index))
 
    # Теперь будем клонировать некоторое количество раз наш базовый объект          
    for frame in range(startFr + frameWithAKey, end_Fr + frameWithAKey, frameWithAKey):
        # в процессе рассчитываем ключевые кадры
        # поскольку это цикл, то после копирования активным станет новый клон,
        # поэтому снова делаем активным базовый объект
        scene.objects.active = basisOb
        # выбираем базовый объект
        basisOb.select = True
        # клонируем его
        bpy.ops.object.duplicate()
        # получаем новую копию
        newOb = scene.objects.active
        # и переименовываем
        newOb.name = name + ' Explode' + str(frame)
        # увеличиваем индекс на 1
        index = index + 1
        # добавляем имя клона, кадр анимации и индекс в список имен
        listNames.append((newOb.name, frame, index))
        # снимаем выбор со всех объектов        
        bpy.ops.object.select_all(action = 'DESELECT')
             
    # Теперь достаем из списка имя клона и связанный с ним номер кадра
    for name, frame, index in listNames:
        # устанавливаем текущий кадр в сцене
        scene.frame_set(frame, 0.0)
        # получаем текущий объект из сцены по имени
        currOb = scene.objects[name]
        # делаем его активным и выбираем
        scene.objects.active = currOb
        currOb.select = True
        # а теперь применяем модификаторы
        # сначала модификатор взрыва
        bpy.ops.object.modifier_apply(modifier = 'Explode', apply_as = 'DATA')
        # потом удаляем систему частиц
        bpy.ops.object.particle_system_remove()
        # и если мы выбрали "твердость" обломков
        if useSolidify == True:
            # то применяем и этот модификатор
            bpy.ops.object.modifier_apply(modifier = 'Solidify')
        # снимаем выделение
        bpy.ops.object.select_all(action = 'DESELECT')

 
 
    #print(listNames)
    # Здесь будем добавлять клонов как ключи формы, причем самому первому клону,
    # потому что нельзя добавить объект в ключи другому объекту, если у них 
    # разное число вершин.
 
    for name, frame, index in listNames:
        #print(name)
        # пропускаем первого клона, он же базовый, на него будем писать ключи
        # формы
        if index == 0:
            continue
        else:
            # понятно, выковыриваем клон(не первый) по имени, он пойдет
            # в ключ формы
            currObj = bpy.context.scene.objects[name]
            # выбираем его
            currObj.select = True 
            # получаем первого клона
            explObject = bpy.context.scene.objects[listNames[0][0]]
            # выбираем его и делаем его активным
            explObject.select = True
            bpy.context.scene.objects.active = explObject
            # пишем текущий клон в ключ формы первого клона
            bpy.ops.object.join_shapes()
            # снимем выделение со всех объектов сцены
            bpy.ops.object.select_all(action = 'DESELECT')
            # снова выбираем текущего клона и в этот раз делаем его активным
            bpy.context.scene.objects.active = currObj
            currObj.select = True
            # удаляем текущий клон    
            bpy.ops.object.delete()
         
    # Здесь будем анимировать ключи формы 
         
    # снимаем выделение со всех объектов, на всякий пожарный  
    bpy.ops.object.select_all(action = 'DESELECT')
    # выделяем первый и теперь уже единственный клон, он же наш базовый
    objExpl = bpy.context.scene.objects[listNames[0][0]]
    bpy.context.scene.objects.active = objExpl
    objExpl.select = True
    # получаем его список ключей форм
    shapeKeys = objExpl.data.shape_keys

    for name, frame, index in listNames:
        # достаем ключ формы по имени, на всякий случай проверяя, есть ли такой
        if name in shapeKeys.key_blocks:
            # текущий ключ формы
            currBlock = shapeKeys.key_blocks[name]
            # генерируем список из кортежей "значение - кадр"
            frameList = [(0.0, frame-frameWithAKey),
                        (1.0, frame),
                        (0.0, frame+frameWithAKey)]
            # И по этому списку задаем ключи анимации ключам формы
            for J in frameList:
#                print(J) #debug
                currBlock.value = J[0]
                currBlock.interpolation = 'KEY_LINEAR'
                currBlock.keyframe_insert("value", frame = J[1])
 
    # изменяем тип ключевых точек для кривых анимации 
 
    # выковыриваем циклом по одной ФКурву из списка данных анимации  
    for f_curve in shapeKeys.animation_data.action.fcurves:
        # из ФКурвы достаем список ключевых точек и проходим по одной
        for key_point in f_curve.keyframe_points:
            #изменяем тип
            key_point.interpolation = 'LINEAR'

    # возвращаем сцену в первый кадр  
    bpy.context.scene.frame_set(1, 0.0)

class ExplodeOperator(bpy.types.Operator):
    """Explode object"""
    bl_idname = "object.explode_operator"
    bl_label = "Explode Object Operator"
    bl_options = {'REGISTER', 'UNDO'}
 
    frameWithAKey = bpy.props.IntProperty(
        name = "frameWithAKey",
        default = 1,
        min = 1,
        max = 8,
        step = 1,
        description = "animation frame with a shape key"
        )
     
    startFr = bpy.props.IntProperty(
        name = "start frame",
        default = 1,
        min = 1,
        max = 255,
        step = 1,
        description = "start animation frame"
        )
 
    end_Fr = bpy.props.IntProperty(
        name = "end_frame",
        default = 8,
        min = 1,
        max = 255,
        step = 1,
        description = "end animation frame"
        )

     
    thickness_Solidify = bpy.props.FloatProperty(
        name = "thickness_Solidify",
        default = 0.05,
        min = 0.01,
        max = 1.0,
        step = 1,
        description = "Solidify thickness"
        )
     
    use_Solidify = bpy.props.BoolProperty(
        name = 'use_Solidify',
        default = False,
        description = 'Solidify usy or not'
        )
 
    # а вот это не сделанная насторойка модификатора взрыва  
    dead_Explode = bpy.props.BoolProperty(
        name = 'Explode Dead',
        default = False,
        description = 'dead_Explode'
        )
     
    count = bpy.props.IntProperty(
        name = "count",
        default = 32,
        min = 1,
        max = 255,
        step = 1,
        description = "Particle Count"
        )
             
    lifetime = bpy.props.IntProperty(
        name = "lifetime",
        default = 50,
        min = 1,
        max = 255,
        step = 1,
        description = "Particle lifetime"
        )
     
    factor_random = bpy.props.FloatProperty(
        name = "factor_random",
        default = 1.0,
        min = 0.0,
        max = 255.0,
        step = 1.0,
        description = "Particle random factor"
        )
     
    factor_normal = bpy.props.FloatProperty(
        name = "factor_normal",
        default = 1.0,
        min = 0.0,
        max = 255.0,
        step = 1.0,
        description = "Particle normal factor"
        )
   
    object_align_factor_X = bpy.props.FloatProperty(
        name = "object_align_factor_X",
        default = 0.0,
        min = -255.0,
        max = 255.0,
        step = 1.0,
        description = "Particle speed factor"
        )
    object_align_factor_Y = bpy.props.FloatProperty(
        name = "object_align_factor_Y",
        default = 0.0,
        min = -255.0,
        max = 255.0,
        step = 1.0,
        description = "Particle speed factor"
        )
    object_align_factor_Z = bpy.props.FloatProperty(
        name = "object_align_factor_Z",
        default = 0.0,
        min = -255.0,
        max = 255.0,
        step = 1.0,
        description = "Particle speed factor"
        )
     
    @classmethod
    def poll(cls, context):
        return context.active_object is not None

    def execute(self, context):
        animationProps = [
            self.frameWithAKey,
            self.startFr,
            self.end_Fr
            ]  
        solidifyProps = [
            self.use_Solidify,
            self.thickness_Solidify
            ]
        particleProps = [
            self.count,
            self.lifetime,
            self.factor_random,
            self.factor_normal,
            self.object_align_factor_X,
            self.object_align_factor_Y,
            self.object_align_factor_Z
            ]
     
        main(
        animationProps,
        solidifyProps,
        self.dead_Explode,
        particleProps
        )
        return {'FINISHED'}
 
    def draw(self, context):
        layout = self.layout
     
        layout.label(text = 'Animation:')
        box = layout.box()
        box.prop(self, 'frameWithAKey')
        row = box.row(align = True)
        row.prop(self, 'startFr')
        row.prop(self, 'end_Fr')
     
        layout.label(text = 'Solidify Modifier:')
        box = layout.box()
        row = box.row(align = True)
        #row.label(text = 'Solidify:')
        row.prop(self, 'use_Solidify')
        if self.use_Solidify == True:
            box.prop(self, 'thickness_Solidify')
     
#        layout.label(text = 'Explode Modifier:')    
#        box = layout.box()
#        box.prop(self, 'dead_Explode')
     
        layout.label(text = 'Particle System:')  
        box = layout.box()
        box.prop(self, 'count')
        box.prop(self, 'lifetime')
        box.prop(self, 'factor_random')
        box.prop(self, 'factor_normal')
        col = box.column(align = True)
        col.prop(self, 'object_align_factor_X')
        col.prop(self, 'object_align_factor_Y')
        col.prop(self, 'object_align_factor_Z')
     


def add_object_menu(self, context):
    self.layout.separator()
    self.layout.operator(
        ExplodeOperator.bl_idname,
        text = ExplodeOperator.__doc__)
    self.layout.separator()
 
def register():
    bpy.utils.register_class(ExplodeOperator)
    bpy.types.VIEW3D_MT_object.append(add_object_menu)
 
def unregister():
    bpy.utils.unregister_class(ExplodeOperator)
    bpy.types.VIEW3D_MT_object.remove(add_object_menu)


 
if __name__ == '__main__':
    register()
         

Комментариев нет:

Отправить комментарий