# -*- coding: UTF-8 -*- import bpy import copy import json import math import os import pyutil import shutil import sys import subprocess import tempfile from bmesh_collision import bmesh_check_intersect_objects armature = bpy.context.scene.objects.get('Armature.001') dict_bones = { "01_conf_direita": ['BnDedo.1.R', 'BnDedo.1.R.006', 'BnDedo.1.R.005', 'BnDedo.1.R.001', 'BnDedo.1.R.008', 'BnDedo.1.R.007', 'BnDedo.1.R.002', 'BnDedo.1.R.010', 'BnDedo.1.R.009', 'BnDedo.1.R.003', 'BnDedo.1.R.012', 'BnDedo.1.R.011', 'BnDedo.1.R.004', 'BnDedo.1.R.014', 'BnDedo.1.R.013'], "02_conf_esquerda": ['BnDedo.1.L', 'BnDedo.1.L.006', 'BnDedo.1.L.005', 'BnDedo.1.L.001', 'BnDedo.1.L.008', 'BnDedo.1.L.007', 'BnDedo.1.L.002', 'BnDedo.1.L.010', 'BnDedo.1.L.009', 'BnDedo.1.L.003', 'BnDedo.1.L.012', 'BnDedo.1.L.011', 'BnDedo.1.L.004', 'BnDedo.1.L.014', 'BnDedo.1.L.013'], "03_pa_direita": ['BnPolyV.R', 'ik_FK.R'], "04_pa_esquerda": ['BnPolyV.L', 'ik_FK.L'], "05_orient_direita": ['BnMao.R'], "06_orient_esquerda": ['BnMao.L'], "07_facial": ['BnPescoco', 'BnCabeca', 'BnSobrancCentro.L', 'BnSobrancCentro.R', 'BnSobrancLateral.L', 'BnSobrancLateral.R', 'BnPalpebSuper.L', 'BnPalpebInfe.L', 'BnSobrancCentro', 'BnLabioCentroSuper', 'BnBochecha.L', 'BnBochecha.R', 'BnLabioCentroInfer', 'BnBocaCanto.L', 'BnBocaCanto.R', 'BnMandibula', 'BnLingua', 'BnLingua.003', 'BnLingua.001', 'BnLingua.002', 'BnPalpebSuper.R', 'BnPalpebInfe.R', 'BnOlhosMira', 'BnOlhoMira.L', 'BnOlhoMira.R', 'BnOlho.L', 'BnOlho.R'] } dict_interpolation = { "lento": 15, "normal": 10, "rapido": 5, } # Movimento coçar - Índices de poses cocar_mao_aberta_index = 55 cocar_mao_fechada_index = 24 cocar_orientation_index = 11 # 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] all_actions = [conf_direita_id, conf_esquerda_id, pa_direita_id, pa_esquerda_id, orient_direita_id, orient_esquerda_id, facial_expression_id] all_bones = dict_bones[conf_direita_id] + dict_bones[conf_esquerda_id] + dict_bones[pa_direita_id] + dict_bones[pa_esquerda_id] + dict_bones[orient_direita_id] + dict_bones[orient_esquerda_id] + dict_bones[facial_expression_id] right_bones_conf = dict_bones[conf_direita_id] + dict_bones[pa_direita_id] + dict_bones[orient_direita_id] left_bones_conf = dict_bones[conf_esquerda_id] + dict_bones[pa_esquerda_id] + dict_bones[orient_esquerda_id] last_keyframe_dict = {} # Invalid rotation lower bound - 100 degree invalid_rotation_lower_bound = (5*math.pi)/9 # Invalid rotation upper bound - 330 degree invalid_rotation_upper_bound = (11*math.pi)/6 def pose_default(current_frame, frame_jump): setPose(all_actions, [0, 0, 0, 0, 0, 0, 0], [current_frame], all_bones, False) return current_frame + frame_jump def select_bones(bones = bpy.context.object.pose.bones): if (isinstance(bones, int) or isinstance(bones, str)): bpy.context.object.pose.bones[bones].bone.select = True elif (isinstance(bones, list)): for bone in bones: bpy.context.object.pose.bones[bone].bone.select = True elif (isinstance(bones, type(bpy.context.object.pose.bones))): for pose_bone in bones: pose_bone.bone.select = True return def deselect_bones(bones = bpy.context.object.pose.bones): if (isinstance(bones, int) or isinstance(bones, str)): bpy.context.object.pose.bones[bones].bone.select = False elif (isinstance(bones, list)): for bone in bones: bpy.context.object.pose.bones[bone].bone.select = False elif (isinstance(bones, type(bpy.context.object.pose.bones))): for pose_bone in bones: pose_bone.bone.select = False return def set_pose_mode(): bpy.ops.object.mode_set(mode = 'OBJECT') bpy.ops.object.select_all(action = "DESELECT") bpy.ops.object.mode_set(mode = 'POSE') def delete_all_keyframes(): bpy.context.active_object.animation_data_clear() for obj in bpy.data.objects: obj.animation_data_clear() return def setPose(actions, parametesConf, positionFrames, bones, collisionFlag = True): for x in range(len(positionFrames)): for l in range(len(actions)): action = actions[l] select_bones(bones) armature.pose_library = bpy.data.actions[action] bpy.ops.poselib.apply_pose(pose_index = parametesConf[l]) deselect_bones(bones) bpy.context.object.pose_library = None 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 internal_keyframe_insert(bone, path, positionFrame, collisionFlag = True, rotationFlag = False): # Limit hand depth if ("ik_FK" in bone.name and bone.location.z < 1): bone.location.z = 1 bone.bone.select = True bone.keyframe_insert(data_path = path, index = -1, group = bone.name, frame = positionFrame) bone.bone.select = False 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 and "ik_FK" in bone.name): checkCollision(bone, path, positionFrame, last_keyframe) def keyframe_insert(bones, path, positionFrame, collisionFlag = True, rotationFlag = False): if (isinstance(bones, list)): for bone in bones: if (isinstance(bone, int) or isinstance(bone, str)): internal_keyframe_insert(bpy.context.object.pose.bones[bone], path, positionFrame, collisionFlag, rotationFlag) else: internal_keyframe_insert(bone, path, positionFrame, collisionFlag, rotationFlag) else: internal_keyframe_insert(bones, path, positionFrame, collisionFlag, rotationFlag) 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 get_bone_data_from_frame(bone, positionFrame, path): scene = bpy.context.scene frame_current = scene.frame_current scene.frame_set(positionFrame) result = copy.copy(bone.location) if path == 'location' else copy.copy(bone.rotation_quaternion) scene.frame_set(frame_current) return result def checkRotation(bone, positionFrame, last_keyframe): boneRQ = get_bone_data_from_frame(bone, positionFrame, 'rotation_quaternion') isRightHand = ".R" in bone.name resetBnMaoPosition(isRightHand) valid_rotation = validate_rotation(bone, positionFrame, last_keyframe) if (not valid_rotation): new_rotation = boneRQ * (-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 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(endFrame) 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 define as configurações de saida def configure_output(): bpy.context.scene.frame_start = 0 bpy.context.scene.frame_current = bpy.context.scene.frame_start bpy.context.scene.frame_end = bpy.context.scene.frame_start 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' # 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 return def render_sign(user_id = "", sinal = "", frame_final = bpy.context.scene.frame_end): bpy.context.scene.frame_end = frame_final #frame_final bpy.context.scene.render.filepath = tempfile.NamedTemporaryFile().name pyutil.log("Render Video Frames: %i" % (frame_final)) temp_filename = ("%s%0.4i-%0.4i" % (bpy.context.scene.render.filepath, bpy.context.scene.frame_start, bpy.context.scene.frame_end)) temp_filename_mp4 = temp_filename + ".mp4" temp_filename_webm = temp_filename + ".webm" pyutil.log("Temporary File: %s" % (temp_filename_mp4)) bpy.ops.render.render(animation = True, write_still = False, layer = "", scene = "") if pyutil.file_exists(temp_filename_mp4): getcwd = os.path.dirname(os.path.abspath(__file__)) user_directory = os.path.join(getcwd, "users", user_id) user_full_path_webm = os.path.join(user_directory, sinal + ".webm") user_full_path_mp4 = os.path.join(user_directory, sinal + ".mp4") if (make_dir(user_directory)): pyutil.log("user dir created: %s" % (user_directory)) subprocess.call(["avconv", "-loglevel", "0", "-y", "-i", temp_filename_mp4, "-r", "24", "-vcodec", "libvpx", temp_filename_webm]) subprocess.call(["avconv", "-loglevel", "0", "-y", "-i", temp_filename_mp4, "-r", "24", "-vcodec", "libx264", user_full_path_mp4]) subprocess.call(["rm", "-f", temp_filename_mp4]) if pyutil.file_exists(temp_filename_webm): try: shutil.copy(temp_filename_webm, user_full_path_webm) subprocess.call(["rm", "-f", temp_filename_webm]) except: pyutil.log("error while copy the video '%s' to ''" % (temp_filename_webm, user_full_path_webm)) bpy.ops.wm.quit_blender() """ getcwd = os.path.dirname(os.path.abspath(__file__)) base_path = os.path.join(getcwd, "users", user_id, sinal) bpy.context.scene.render.filepath = base_path + "_" bpy.context.scene.frame_end = frame_final pyutil.log("Gerando Video... Frames: %i" % (frame_final)) bpy.ops.render.render(animation = True, write_still = False, layer = "", scene = "") renamed_video = pyutil.file_rename("%s%0.4i-%0.4i.mp4" % (bpy.context.scene.render.filepath, bpy.context.scene.frame_start, bpy.context.scene.frame_end)) # Converte arquivo .mp4 para .webm subprocess.call(["avconv", "-loglevel", "0", "-y", "-i", renamed_video, "-r", "24", "-vcodec", "libvpx", base_path + ".webm"]) subprocess.call(["rm", renamed_video]) bpy.ops.wm.quit_blender() """ return def make_dir(directory): if not os.path.exists(directory): os.makedirs(directory) return True return False def export_blend(directory, filename, frame_start, frame_current, frame_end): tmp_frame_start = bpy.context.scene.frame_start tmp_frame_current = bpy.context.scene.frame_current tmp_frame_end = bpy.context.scene.frame_end bpy.context.scene.frame_start = frame_start bpy.context.scene.frame_current = frame_current bpy.context.scene.frame_end = frame_end make_dir(directory) bpy.ops.wm.save_as_mainfile(filepath = os.path.join(directory, filename)) bpy.context.scene.frame_start = tmp_frame_start bpy.context.scene.frame_current = tmp_frame_current bpy.context.scene.frame_end = tmp_frame_end def export_json(obj, directory, filename, inline = True): make_dir(directory) fp = open(os.path.join(directory, filename), 'w', encoding = "utf-8") fp.write( json.dumps( obj, ensure_ascii = False, indent = None if (inline) else 4, separators = (",", ":") if (inline) else None ) ) fp.close return def validate_rotation(bone, endFrame, startFrame = 0): if (endFrame - startFrame == 1): return True rotFrames = [[]] scene = bpy.context.scene 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(endFrame) for k in range(1, len(rotFrames), 1): for i in range(0, 3, 1): delta_rotation = math.fabs(rotFrames[k][i] - rotFrames[k-1][i]) if (delta_rotation > invalid_rotation_lower_bound and delta_rotation < invalid_rotation_upper_bound): return False return True # Axis: "X", "Y" e "Z" def apply_rotation(bone, axis, currentFrame, 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)