From f89e6e67fea15bf070933c0675990dac8e38e593 Mon Sep 17 00:00:00 2001 From: Adabriand Furtado Date: Thu, 16 Jul 2015 13:32:15 -0300 Subject: [PATCH] Adicionado o tratamento de colisão das mãos com bounding boxes --- avatar_cartoon_v2.70.blend | Bin 6965072 -> 0 bytes avatar_cartoon_v2.74.blend | Bin 0 -> 6973752 bytes bmesh_collision.py | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ libras.py | 6 +++--- util.py | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------- 5 files changed, 187 insertions(+), 17 deletions(-) delete mode 100755 avatar_cartoon_v2.70.blend create mode 100755 avatar_cartoon_v2.74.blend create mode 100644 bmesh_collision.py diff --git a/avatar_cartoon_v2.70.blend b/avatar_cartoon_v2.70.blend deleted file mode 100755 index fdf1b7a..0000000 Binary files a/avatar_cartoon_v2.70.blend and /dev/null differ diff --git a/avatar_cartoon_v2.74.blend b/avatar_cartoon_v2.74.blend new file mode 100755 index 0000000..4e6cbc8 Binary files /dev/null and b/avatar_cartoon_v2.74.blend differ diff --git a/bmesh_collision.py b/bmesh_collision.py new file mode 100644 index 0000000..d385b9d --- /dev/null +++ b/bmesh_collision.py @@ -0,0 +1,88 @@ +import bpy +import bmesh + +def bmesh_copy_from_object(obj, transform=True, triangulate=True, apply_modifiers=False): + + assert(obj.type == 'MESH') + + if apply_modifiers and obj.modifiers: + me = obj.to_mesh(bpy.context.scene, True, 'PREVIEW', calc_tessface=False) + bm = bmesh.new() + bm.from_mesh(me) + bpy.data.meshes.remove(me) + else: + me = obj.data + if obj.mode == 'EDIT': + bm_orig = bmesh.from_edit_mesh(me) + bm = bm_orig.copy() + else: + bm = bmesh.new() + bm.from_mesh(me) + + # Remove custom data layers to save memory + for elem in (bm.faces, bm.edges, bm.verts, bm.loops): + for layers_name in dir(elem.layers): + if not layers_name.startswith("_"): + layers = getattr(elem.layers, layers_name) + for layer_name, layer in layers.items(): + layers.remove(layer) + + if transform: + bm.transform(obj.matrix_world) + + if triangulate: + bmesh.ops.triangulate(bm, faces=bm.faces) + + return bm + +def bmesh_check_intersect_objects(obj, obj2): + assert(obj != obj2) + + # Triangulate + bm = bmesh_copy_from_object(obj, transform=True, triangulate=True) + bm2 = bmesh_copy_from_object(obj2, transform=True, triangulate=True) + + # If bm has more edges, use bm2 instead for looping over its edges + # (so we cast less rays from the simpler object to the more complex object) + if len(bm.edges) > len(bm2.edges): + bm2, bm = bm, bm2 + + # Create a real mesh (lame!) + scene = bpy.context.scene + me_tmp = bpy.data.meshes.new(name="~temp~") + bm2.to_mesh(me_tmp) + bm2.free() + obj_tmp = bpy.data.objects.new(name=me_tmp.name, object_data=me_tmp) + scene.objects.link(obj_tmp) + scene.update() + ray_cast = obj_tmp.ray_cast + + intersect = False + + EPS_NORMAL = 0.000001 + EPS_CENTER = 0.01 # should always be bigger + + #for ed in me_tmp.edges: + for ed in bm.edges: + v1, v2 = ed.verts + + # setup the edge with an offset + co_1 = v1.co.copy() + co_2 = v2.co.copy() + co_mid = (co_1 + co_2) * 0.5 + no_mid = (v1.normal + v2.normal).normalized() * EPS_NORMAL + co_1 = co_1.lerp(co_mid, EPS_CENTER) + no_mid + co_2 = co_2.lerp(co_mid, EPS_CENTER) + no_mid + + co, no, index = ray_cast(co_1, co_2) + if index != -1: + intersect = True + break + + scene.objects.unlink(obj_tmp) + bpy.data.objects.remove(obj_tmp) + bpy.data.meshes.remove(me_tmp) + + scene.update() + + return intersect \ No newline at end of file diff --git a/libras.py b/libras.py index d0f2e56..517e5fb 100644 --- a/libras.py +++ b/libras.py @@ -36,7 +36,7 @@ def poseDefault(positionFrames, collisionFlag = False): handDefaultParam = [0, 0, 0] util.setPose(util.right_hand_actions, handDefaultParam, positionFrames, util.rightBonesConf, collisionFlag) util.setPose(util.left_hand_actions, handDefaultParam, positionFrames, util.leftBonesConf, collisionFlag) - # Setar a expressão facial padrão + #setFaceConfiguration([0], positionFrames, util.faceBonesConf) # Função responsável por setar as configurações das mãos def setHandConfiguration(actions, handParam, positionFrames, bones): @@ -44,7 +44,7 @@ def setHandConfiguration(actions, handParam, positionFrames, bones): # Função responsável por setar a configuração da face def setFaceConfiguration(handParam, positionFrames, bones): - util.setPose(['007_Facial'], handParam, positionFrames, bones) + util.setPose(util.facial_expression_action, handParam, positionFrames, bones) # Sugestao: Alguma forma de uniformizar o calculo do endFrame (atualizado aqui e no movimento circular) initialFrame, endFrame = 15, util.get_endFrame(json_input, util.hands_frames_retilineo) @@ -57,7 +57,7 @@ def configureHands(): hands = ["rightHand", "leftHand"] iks = ["ik_FK.R", "ik_FK.L"] bones_ = [util.rightBonesConf, util.leftBonesConf] - #Array com as actions FAKES que seram selecionadas no Blender para cada lado do corpo + # Array com as actions FAKES que seram selecionadas no Blender para cada lado do corpo actions = [util.right_hand_actions, util.left_hand_actions] global endFrame for i in range(len(hands)): diff --git a/util.py b/util.py index 182299d..88ff1e1 100644 --- a/util.py +++ b/util.py @@ -2,8 +2,7 @@ import bpy import math -#from bmesh_collision import bmesh_check_intersect_objects -#from pyutil import log +from bmesh_collision import bmesh_check_intersect_objects armature = bpy.context.scene.objects.get('Armature.001') @@ -20,7 +19,7 @@ faceBonesConf = [15, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 47] allBones = list(range(len(armature.pose.bones))) # define a posição dos keyframes -hands_default_frames = [15, 18] +hands_default_frames = [15] # define a posição dos keyframes hands_frames_retilineo = [30, 33] @@ -30,11 +29,21 @@ 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 -right_hand_actions = [0, 2, 4] +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 -left_hand_actions = [1, 3, 5] +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 = {} @@ -46,14 +55,84 @@ def setPose(actions, parametesConf, positionFrames, bones, collisionFlag = True) for x in range(len(positionFrames)): for l in range(len(actions)): - armature.pose_library = bpy.data.actions[actions[l]] + 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))): - keyframe_insert(armature.pose.bones[bones[i]], 'location', positionFrames[x], collisionFlag) - keyframe_insert(armature.pose.bones[bones[i]], 'rotation_quaternion', positionFrames[x], collisionFlag) - -def keyframe_insert(bone, path, positionFrame, collisionFlag = True): + 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 = bpy.context.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): + isRightHand = ".R" in bone.name + resetIKPosition(isRightHand) + collisionFrame = check_collision(last_keyframe, positionFrame) + + if (last_keyframe != positionFrame and collisionFrame != -1): + handle_collision(bone, path, positionFrame, collisionFrame) + +def handle_collision(bone, path, positionFrame, collisionFrame, rollbackFrames = 0): + scene = bpy.context.scene + frame_current = bpy.context.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_collision(initFrame, endFrame): + scene = bpy.context.scene + frame_current = bpy.context.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) + right_cube = bpy.context.scene.objects.get('right_hand_box') + left_cube = bpy.context.scene.objects.get('left_hand_box') + + if (bmesh_check_intersect_objects(right_cube, left_cube)): + collisionFrame = i + break + scene.frame_set(frame_current) + return collisionFrame # Função que limpa todos os keyframes e define a quantidade de frames def erase_all_keyframes(): @@ -117,12 +196,15 @@ def get_endFrame(json_input, hands_frames_retilineo): endsFrame.append(max(hands_frames_retilineo)) return(max(endsFrame)) -def validate_rotation(bone, endFrame): +def validate_rotation(bone, endFrame, startFrame = 0): + if (endFrame - startFrame == 1): + return True + rotFrames = [[]] scene = bpy.context.scene frame_current = bpy.context.scene.frame_current - for i in range(0, endFrame + 1,1): + for i in range(startFrame+1, endFrame+1, 1): scene.frame_set(i) rotFrames[-1] = bone.rotation_quaternion.to_euler() rotFrames.append( [] ) @@ -130,9 +212,9 @@ def validate_rotation(bone, endFrame): rotFrames.remove([]) scene.frame_set(frame_current) - for k in range(1, endFrame + 1, 1): + 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 : + if (math.fabs(rotFrames[k][i] - rotFrames[k-1][i])) > math.pi/2: return False return True -- libgit2 0.21.2