util.py 10 KB
# -*- coding: UTF-8 -*-

import bpy
import math
from bmesh_collision import bmesh_check_intersect_objects

armature = bpy.context.scene.objects.get('Armature.001')

# Vetor com indices de cada bone do lado direito
rightBonesConf = [1, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66]

# Vetor com indices de cada bone do lado esquerdo
leftBonesConf = [0, 43, 44, 45, 46, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82]

# Vetor com indices de cada bone da face
faceBonesConf = [15, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 47]

# Vetor com indices de todos os bones
allBones = list(range(len(armature.pose.bones)))

# define a posição dos keyframes
hands_default_frames = [15]

# define a posição dos keyframes
hands_frames_retilineo = [30, 33]

# Movimento coçar - Índices de poses
cocar_mao_aberta_index = 56
cocar_mao_fechada_index = 24
cocar_orientation_index = 20

# Action expressão facial
facial_expression_id ='07_facial'
facial_expression_action = [facial_expression_id]

# Actions mão direita
conf_direita_id = '01_conf_direita'
pa_direita_id = '03_pa_direita'
orient_direita_id = '05_orient_direita'
right_hand_actions = [conf_direita_id, pa_direita_id, orient_direita_id]

# Actions mão esquerda
conf_esquerda_id = '02_conf_esquerda'
pa_esquerda_id = '04_pa_esquerda'
orient_esquerda_id = '06_orient_esquerda'
left_hand_actions = [conf_esquerda_id, pa_esquerda_id, orient_esquerda_id]

last_keyframe_dict = {}

# Função responsável por selecionar as pose-libs e setar os frames
def setPose(actions, parametesConf, positionFrames, bones, collisionFlag = True):
    bpy.ops.object.mode_set(mode = 'OBJECT')
    bpy.ops.object.select_all(action="DESELECT")
    bpy.ops.object.mode_set(mode = 'POSE')
    
    for x in range(len(positionFrames)):
        for l in range(len(actions)):
            action = actions[l]
            armature.pose_library = bpy.data.actions[action]
            bpy.ops.poselib.apply_pose(pose_index = parametesConf[l])
            for i in range(0, (len(bones))):
                bone = armature.pose.bones[bones[i]]
                validHandConf = action in [conf_direita_id, conf_esquerda_id] and "BnDedo" in bone.name
                validPA = action in [pa_direita_id, pa_esquerda_id] and "ik_FK" in bone.name or "BnPolyV" in bone.name
                validO = action in [orient_direita_id, orient_esquerda_id] and "BnMao" in bone.name
                
                if (validHandConf or validPA or validO):
                    keyframe_insert(bone, 'location', positionFrames[x], collisionFlag and validPA, validO)
                    keyframe_insert(bone, 'rotation_quaternion', positionFrames[x], collisionFlag and validPA, validO)

def keyframe_insert(bone, path, positionFrame, collisionFlag = True, rotationFlag = False):    
    bone.keyframe_insert(data_path = path, index = -1, frame = positionFrame)
    keyframe_id = bone.name + "_" + path
    last_keyframe = last_keyframe_dict[keyframe_id] if keyframe_id in last_keyframe_dict else 0
    last_keyframe_dict[keyframe_id] = positionFrame
        
    if (rotationFlag and path == "rotation_quaternion"):
        checkRotation(bone, positionFrame, last_keyframe)
    
    if (collisionFlag):
        checkCollision(bone, path, positionFrame, last_keyframe)

def resetIKPosition(isRightHand):
    armature.pose_library = bpy.data.actions[pa_direita_id if isRightHand else pa_esquerda_id]
    bpy.ops.poselib.apply_pose(pose_index = 0)

def resetBnMaoPosition(isRightHand):
    armature.pose_library = bpy.data.actions[orient_direita_id if isRightHand else orient_esquerda_id]
    bpy.ops.poselib.apply_pose(pose_index = 0)

def checkRotation(bone, positionFrame, last_keyframe):
    scene = bpy.context.scene
    frame_current = scene.frame_current
    scene.frame_set(positionFrame)
    boneRQ = bone.rotation_quaternion.to_euler()
    scene.frame_set(frame_current)
    isRightHand = ".R" in bone.name
    resetBnMaoPosition(isRightHand) 
    valid_rotation = validate_rotation(bone, positionFrame, last_keyframe)
    
    if (not valid_rotation):
        new_rotation = boneRQ.to_quaternion() * (-1)
        bone.rotation_quaternion = new_rotation
        bone.keyframe_insert(data_path = 'rotation_quaternion', index = -1, frame = positionFrame)

def checkCollision(bone, path, positionFrame, last_keyframe):
    if (last_keyframe == positionFrame):
        return
    
    isRightHand = ".R" in bone.name
    resetIKPosition(isRightHand)
    handCollisionFrame = check_hand_collision(last_keyframe, positionFrame)

    if (handCollisionFrame != -1):
        handle_collision(bone, path, positionFrame, handCollisionFrame)
        return
    
    bodyCollisionFrame = check_body_collision(isRightHand, last_keyframe, positionFrame)
    if (bodyCollisionFrame != -1):
        handle_collision(bone, path, positionFrame, bodyCollisionFrame)
        return

def handle_collision(bone, path, positionFrame, collisionFrame, rollbackFrames = 0):
    scene = bpy.context.scene
    frame_current = scene.frame_current
    scene.frame_set(collisionFrame - rollbackFrames)
    bone.keyframe_insert(data_path = path, index = -1, frame = positionFrame)  
    bpy.context.scene.frame_set(frame_current)

def check_hand_collision(initFrame, endFrame):
    return check_collision('right_hand_box', 'left_hand_box', initFrame, endFrame)

def check_collision(objName, otherObjName, initFrame, endFrame):
    scene = bpy.context.scene
    frame_current = scene.frame_current
    startFrame = initFrame + int(math.fabs((endFrame - initFrame)/2))  
    collisionFrame = -1
    for i in range(startFrame, endFrame + 1, 1):
            scene.frame_set(i)
            obj = scene.objects.get(objName)
            otherObj = scene.objects.get(otherObjName)

            if (bmesh_check_intersect_objects(obj, otherObj)):
                collisionFrame = i
                break
    scene.frame_set(frame_current)
    return collisionFrame

def check_body_collision(isRightHand, initFrame, endFrame):
    hand_box = 'right_hand_box' if isRightHand else 'left_hand_box'
    body_box = 'body_box'
    result = check_collision(hand_box, body_box, initFrame, endFrame)
    return result

# Função que limpa todos os keyframes e define a quantidade de frames
def erase_all_keyframes():
    for i in bpy.data.objects:
        i.animation_data_clear()
    bpy.context.scene.frame_start = 1
    bpy.context.scene.frame_current = bpy.context.scene.frame_start
    bpy.context.scene.frame_end = bpy.context.scene.frame_start

# Função que define as configurações de saida
def outconf():
    erase_all_keyframes()
    bpy.context.scene.render.resolution_x = 640
    bpy.context.scene.render.resolution_y = 480
    bpy.context.scene.render.resolution_percentage = 100
    bpy.context.scene.render.image_settings.file_format = 'H264'
    bpy.context.scene.render.ffmpeg.format = 'MPEG4'
    bpy.context.scene.render.ffmpeg.codec = 'H264'
    # bpy.context.scene.render.filepath = '/tmp/'

    # Otimização da renderização
    bpy.context.scene.render.use_shadows = False
    bpy.context.scene.render.use_raytrace = False
    bpy.context.scene.render.use_envmaps = False
    bpy.context.scene.render.use_motion_blur = False
    bpy.context.scene.render.use_shadows = False
    bpy.context.scene.render.tile_x = 320
    bpy.context.scene.render.tile_y = 240

def render_sign(userId, signName, beginFrame, endFrame):
    from sys import argv, path
    from os.path import abspath, dirname
    from pyutil import log, file_rename
    getcwd = dirname(abspath(__file__))
    bpy.context.scene.render.filepath = getcwd + "/users/"+ str(userId)+ "/"+ signName + "_"
    bpy.context.scene.frame_start = beginFrame
    bpy.context.scene.frame_end = endFrame
    outFilename = ("%s%0.4i-%0.4i.mp4" % (bpy.context.scene.render.filepath, bpy.context.scene.frame_start, bpy.context.scene.frame_end))
    log("All frames: %i" % (endFrame))
    bpy.ops.render.render(animation = True, write_still = False, layer = "", scene = "")
    file_rename(outFilename)
    bpy.ops.wm.quit_blender()

# Função que recupera o frame final do movimento
def get_endFrame(json_input, hands_frames_retilineo):
    endsFrame = [18]
    if(json_input["rightHand"] != []):
        if(json_input["rightHand"][0] == "circular"):
            # Sugestao: json_input["rightHand"][4] eh o número de voltas/repeticoes? colocar numa variavel
            endsFrame.append(int(json_input["rightHand"][4]*8*5+18))
        elif(json_input["rightHand"][0] == "semicircular"):
            endsFrame.append(int(json_input["rightHand"][4]*5*5+18))
        elif(json_input["rightHand"][0] == "retilineo"):
            endsFrame.append(max(hands_frames_retilineo))
    if(json_input["leftHand"] != []):
        if(json_input["leftHand"][0] == "circular"):
            endsFrame.append(int(json_input["leftHand"][4]*8*5+18))
        elif(json_input["leftHand"][0] == "semicircular"):
            endsFrame.append(int(json_input["rightHand"][4]*5*5+18))
        elif(json_input["rightHand"][0] == "retilineo"):
            endsFrame.append(max(hands_frames_retilineo))
    return(max(endsFrame))

def validate_rotation(bone, endFrame, startFrame = 0):
    if (endFrame - startFrame == 1):
        return True
    
    rotFrames = [[]]
    scene = bpy.context.scene
    frame_current = scene.frame_current
    
    for i in range(startFrame+1, endFrame+1, 1):
            scene.frame_set(i)
            rotFrames[-1] = bone.rotation_quaternion.to_euler()
            rotFrames.append([])
    
    rotFrames.remove([])
    scene.frame_set(frame_current)
     
    for k in range(1, len(rotFrames), 1):
            for i in range(0, 3, 1):
                    if (math.fabs(rotFrames[k][i] - rotFrames[k-1][i])) > math.pi/2:
                        return False
    return True

# Axis: "X", "Y" e "Z"
def apply_rotation(bone, axis, currentFrame, endFrame, degree):
    new_rotation = bone.rotation_quaternion.to_euler()
    new_rotation.rotate_axis(axis, math.radians(degree))
    new_rotation = new_rotation.to_quaternion()
      
    bone.rotation_quaternion = new_rotation
    keyframe_insert(bone, 'rotation_quaternion', currentFrame, False, True)