Commit f89e6e67fea15bf070933c0675990dac8e38e593
1 parent
cef27bb8
Exists in
master
Adicionado o tratamento de colisão das mãos com bounding boxes
Showing
5 changed files
with
187 additions
and
17 deletions
Show diff stats
avatar_cartoon_v2.70.blend
No preview for this file type
No preview for this file type
| ... | ... | @@ -0,0 +1,88 @@ |
| 1 | +import bpy | |
| 2 | +import bmesh | |
| 3 | + | |
| 4 | +def bmesh_copy_from_object(obj, transform=True, triangulate=True, apply_modifiers=False): | |
| 5 | + | |
| 6 | + assert(obj.type == 'MESH') | |
| 7 | + | |
| 8 | + if apply_modifiers and obj.modifiers: | |
| 9 | + me = obj.to_mesh(bpy.context.scene, True, 'PREVIEW', calc_tessface=False) | |
| 10 | + bm = bmesh.new() | |
| 11 | + bm.from_mesh(me) | |
| 12 | + bpy.data.meshes.remove(me) | |
| 13 | + else: | |
| 14 | + me = obj.data | |
| 15 | + if obj.mode == 'EDIT': | |
| 16 | + bm_orig = bmesh.from_edit_mesh(me) | |
| 17 | + bm = bm_orig.copy() | |
| 18 | + else: | |
| 19 | + bm = bmesh.new() | |
| 20 | + bm.from_mesh(me) | |
| 21 | + | |
| 22 | + # Remove custom data layers to save memory | |
| 23 | + for elem in (bm.faces, bm.edges, bm.verts, bm.loops): | |
| 24 | + for layers_name in dir(elem.layers): | |
| 25 | + if not layers_name.startswith("_"): | |
| 26 | + layers = getattr(elem.layers, layers_name) | |
| 27 | + for layer_name, layer in layers.items(): | |
| 28 | + layers.remove(layer) | |
| 29 | + | |
| 30 | + if transform: | |
| 31 | + bm.transform(obj.matrix_world) | |
| 32 | + | |
| 33 | + if triangulate: | |
| 34 | + bmesh.ops.triangulate(bm, faces=bm.faces) | |
| 35 | + | |
| 36 | + return bm | |
| 37 | + | |
| 38 | +def bmesh_check_intersect_objects(obj, obj2): | |
| 39 | + assert(obj != obj2) | |
| 40 | + | |
| 41 | + # Triangulate | |
| 42 | + bm = bmesh_copy_from_object(obj, transform=True, triangulate=True) | |
| 43 | + bm2 = bmesh_copy_from_object(obj2, transform=True, triangulate=True) | |
| 44 | + | |
| 45 | + # If bm has more edges, use bm2 instead for looping over its edges | |
| 46 | + # (so we cast less rays from the simpler object to the more complex object) | |
| 47 | + if len(bm.edges) > len(bm2.edges): | |
| 48 | + bm2, bm = bm, bm2 | |
| 49 | + | |
| 50 | + # Create a real mesh (lame!) | |
| 51 | + scene = bpy.context.scene | |
| 52 | + me_tmp = bpy.data.meshes.new(name="~temp~") | |
| 53 | + bm2.to_mesh(me_tmp) | |
| 54 | + bm2.free() | |
| 55 | + obj_tmp = bpy.data.objects.new(name=me_tmp.name, object_data=me_tmp) | |
| 56 | + scene.objects.link(obj_tmp) | |
| 57 | + scene.update() | |
| 58 | + ray_cast = obj_tmp.ray_cast | |
| 59 | + | |
| 60 | + intersect = False | |
| 61 | + | |
| 62 | + EPS_NORMAL = 0.000001 | |
| 63 | + EPS_CENTER = 0.01 # should always be bigger | |
| 64 | + | |
| 65 | + #for ed in me_tmp.edges: | |
| 66 | + for ed in bm.edges: | |
| 67 | + v1, v2 = ed.verts | |
| 68 | + | |
| 69 | + # setup the edge with an offset | |
| 70 | + co_1 = v1.co.copy() | |
| 71 | + co_2 = v2.co.copy() | |
| 72 | + co_mid = (co_1 + co_2) * 0.5 | |
| 73 | + no_mid = (v1.normal + v2.normal).normalized() * EPS_NORMAL | |
| 74 | + co_1 = co_1.lerp(co_mid, EPS_CENTER) + no_mid | |
| 75 | + co_2 = co_2.lerp(co_mid, EPS_CENTER) + no_mid | |
| 76 | + | |
| 77 | + co, no, index = ray_cast(co_1, co_2) | |
| 78 | + if index != -1: | |
| 79 | + intersect = True | |
| 80 | + break | |
| 81 | + | |
| 82 | + scene.objects.unlink(obj_tmp) | |
| 83 | + bpy.data.objects.remove(obj_tmp) | |
| 84 | + bpy.data.meshes.remove(me_tmp) | |
| 85 | + | |
| 86 | + scene.update() | |
| 87 | + | |
| 88 | + return intersect | |
| 0 | 89 | \ No newline at end of file | ... | ... |
libras.py
| ... | ... | @@ -36,7 +36,7 @@ def poseDefault(positionFrames, collisionFlag = False): |
| 36 | 36 | handDefaultParam = [0, 0, 0] |
| 37 | 37 | util.setPose(util.right_hand_actions, handDefaultParam, positionFrames, util.rightBonesConf, collisionFlag) |
| 38 | 38 | util.setPose(util.left_hand_actions, handDefaultParam, positionFrames, util.leftBonesConf, collisionFlag) |
| 39 | - # Setar a expressão facial padrão | |
| 39 | + #setFaceConfiguration([0], positionFrames, util.faceBonesConf) | |
| 40 | 40 | |
| 41 | 41 | # Função responsável por setar as configurações das mãos |
| 42 | 42 | def setHandConfiguration(actions, handParam, positionFrames, bones): |
| ... | ... | @@ -44,7 +44,7 @@ def setHandConfiguration(actions, handParam, positionFrames, bones): |
| 44 | 44 | |
| 45 | 45 | # Função responsável por setar a configuração da face |
| 46 | 46 | def setFaceConfiguration(handParam, positionFrames, bones): |
| 47 | - util.setPose(['007_Facial'], handParam, positionFrames, bones) | |
| 47 | + util.setPose(util.facial_expression_action, handParam, positionFrames, bones) | |
| 48 | 48 | |
| 49 | 49 | # Sugestao: Alguma forma de uniformizar o calculo do endFrame (atualizado aqui e no movimento circular) |
| 50 | 50 | initialFrame, endFrame = 15, util.get_endFrame(json_input, util.hands_frames_retilineo) |
| ... | ... | @@ -57,7 +57,7 @@ def configureHands(): |
| 57 | 57 | hands = ["rightHand", "leftHand"] |
| 58 | 58 | iks = ["ik_FK.R", "ik_FK.L"] |
| 59 | 59 | bones_ = [util.rightBonesConf, util.leftBonesConf] |
| 60 | - #Array com as actions FAKES que seram selecionadas no Blender para cada lado do corpo | |
| 60 | + # Array com as actions FAKES que seram selecionadas no Blender para cada lado do corpo | |
| 61 | 61 | actions = [util.right_hand_actions, util.left_hand_actions] |
| 62 | 62 | global endFrame |
| 63 | 63 | for i in range(len(hands)): | ... | ... |
util.py
| ... | ... | @@ -2,8 +2,7 @@ |
| 2 | 2 | |
| 3 | 3 | import bpy |
| 4 | 4 | import math |
| 5 | -#from bmesh_collision import bmesh_check_intersect_objects | |
| 6 | -#from pyutil import log | |
| 5 | +from bmesh_collision import bmesh_check_intersect_objects | |
| 7 | 6 | |
| 8 | 7 | armature = bpy.context.scene.objects.get('Armature.001') |
| 9 | 8 | |
| ... | ... | @@ -20,7 +19,7 @@ faceBonesConf = [15, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 47] |
| 20 | 19 | allBones = list(range(len(armature.pose.bones))) |
| 21 | 20 | |
| 22 | 21 | # define a posição dos keyframes |
| 23 | -hands_default_frames = [15, 18] | |
| 22 | +hands_default_frames = [15] | |
| 24 | 23 | |
| 25 | 24 | # define a posição dos keyframes |
| 26 | 25 | hands_frames_retilineo = [30, 33] |
| ... | ... | @@ -30,11 +29,21 @@ cocar_mao_aberta_index = 56 |
| 30 | 29 | cocar_mao_fechada_index = 24 |
| 31 | 30 | cocar_orientation_index = 20 |
| 32 | 31 | |
| 32 | +# Action expressão facial | |
| 33 | +facial_expression_id ='07_facial' | |
| 34 | +facial_expression_action = [facial_expression_id] | |
| 35 | + | |
| 33 | 36 | # Actions mão direita |
| 34 | -right_hand_actions = [0, 2, 4] | |
| 37 | +conf_direita_id = '01_conf_direita' | |
| 38 | +pa_direita_id = '03_pa_direita' | |
| 39 | +orient_direita_id = '05_orient_direita' | |
| 40 | +right_hand_actions = [conf_direita_id, pa_direita_id, orient_direita_id] | |
| 35 | 41 | |
| 36 | 42 | # Actions mão esquerda |
| 37 | -left_hand_actions = [1, 3, 5] | |
| 43 | +conf_esquerda_id = '02_conf_esquerda' | |
| 44 | +pa_esquerda_id = '04_pa_esquerda' | |
| 45 | +orient_esquerda_id = '06_orient_esquerda' | |
| 46 | +left_hand_actions = [conf_esquerda_id, pa_esquerda_id, orient_esquerda_id] | |
| 38 | 47 | |
| 39 | 48 | last_keyframe_dict = {} |
| 40 | 49 | |
| ... | ... | @@ -46,14 +55,84 @@ def setPose(actions, parametesConf, positionFrames, bones, collisionFlag = True) |
| 46 | 55 | |
| 47 | 56 | for x in range(len(positionFrames)): |
| 48 | 57 | for l in range(len(actions)): |
| 49 | - armature.pose_library = bpy.data.actions[actions[l]] | |
| 58 | + action = actions[l] | |
| 59 | + armature.pose_library = bpy.data.actions[action] | |
| 50 | 60 | bpy.ops.poselib.apply_pose(pose_index = parametesConf[l]) |
| 51 | 61 | for i in range(0, (len(bones))): |
| 52 | - keyframe_insert(armature.pose.bones[bones[i]], 'location', positionFrames[x], collisionFlag) | |
| 53 | - keyframe_insert(armature.pose.bones[bones[i]], 'rotation_quaternion', positionFrames[x], collisionFlag) | |
| 54 | - | |
| 55 | -def keyframe_insert(bone, path, positionFrame, collisionFlag = True): | |
| 62 | + bone = armature.pose.bones[bones[i]] | |
| 63 | + validHandConf = action in [conf_direita_id, conf_esquerda_id] and "BnDedo" in bone.name | |
| 64 | + validPA = action in [pa_direita_id, pa_esquerda_id] and "ik_FK" in bone.name or "BnPolyV" in bone.name | |
| 65 | + validO = action in [orient_direita_id, orient_esquerda_id] and "BnMao" in bone.name | |
| 66 | + | |
| 67 | + if (validHandConf or validPA or validO): | |
| 68 | + keyframe_insert(bone, 'location', positionFrames[x], collisionFlag and validPA, validO) | |
| 69 | + keyframe_insert(bone, 'rotation_quaternion', positionFrames[x], collisionFlag and validPA, validO) | |
| 70 | + | |
| 71 | +def keyframe_insert(bone, path, positionFrame, collisionFlag = True, rotationFlag = False): | |
| 56 | 72 | bone.keyframe_insert(data_path = path, index = -1, frame = positionFrame) |
| 73 | + keyframe_id = bone.name + "_" + path | |
| 74 | + last_keyframe = last_keyframe_dict[keyframe_id] if keyframe_id in last_keyframe_dict else 0 | |
| 75 | + last_keyframe_dict[keyframe_id] = positionFrame | |
| 76 | + | |
| 77 | + if (rotationFlag and path == "rotation_quaternion"): | |
| 78 | + checkRotation(bone, positionFrame, last_keyframe) | |
| 79 | + | |
| 80 | + if (collisionFlag): | |
| 81 | + checkCollision(bone, path, positionFrame, last_keyframe) | |
| 82 | + | |
| 83 | +def resetIKPosition(isRightHand): | |
| 84 | + armature.pose_library = bpy.data.actions[pa_direita_id if isRightHand else pa_esquerda_id] | |
| 85 | + bpy.ops.poselib.apply_pose(pose_index = 0) | |
| 86 | + | |
| 87 | +def resetBnMaoPosition(isRightHand): | |
| 88 | + armature.pose_library = bpy.data.actions[orient_direita_id if isRightHand else orient_esquerda_id] | |
| 89 | + bpy.ops.poselib.apply_pose(pose_index = 0) | |
| 90 | + | |
| 91 | +def checkRotation(bone, positionFrame, last_keyframe): | |
| 92 | + scene = bpy.context.scene | |
| 93 | + frame_current = bpy.context.scene.frame_current | |
| 94 | + scene.frame_set(positionFrame) | |
| 95 | + boneRQ = bone.rotation_quaternion.to_euler() | |
| 96 | + scene.frame_set(frame_current) | |
| 97 | + isRightHand = ".R" in bone.name | |
| 98 | + resetBnMaoPosition(isRightHand) | |
| 99 | + valid_rotation = validate_rotation(bone, positionFrame, last_keyframe) | |
| 100 | + | |
| 101 | + if (not valid_rotation): | |
| 102 | + new_rotation = boneRQ.to_quaternion() * (-1) | |
| 103 | + bone.rotation_quaternion = new_rotation | |
| 104 | + bone.keyframe_insert(data_path = 'rotation_quaternion', index = -1, frame = positionFrame) | |
| 105 | + | |
| 106 | +def checkCollision(bone, path, positionFrame, last_keyframe): | |
| 107 | + isRightHand = ".R" in bone.name | |
| 108 | + resetIKPosition(isRightHand) | |
| 109 | + collisionFrame = check_collision(last_keyframe, positionFrame) | |
| 110 | + | |
| 111 | + if (last_keyframe != positionFrame and collisionFrame != -1): | |
| 112 | + handle_collision(bone, path, positionFrame, collisionFrame) | |
| 113 | + | |
| 114 | +def handle_collision(bone, path, positionFrame, collisionFrame, rollbackFrames = 0): | |
| 115 | + scene = bpy.context.scene | |
| 116 | + frame_current = bpy.context.scene.frame_current | |
| 117 | + scene.frame_set(collisionFrame - rollbackFrames) | |
| 118 | + bone.keyframe_insert(data_path = path, index = -1, frame = positionFrame) | |
| 119 | + bpy.context.scene.frame_set(frame_current) | |
| 120 | + | |
| 121 | +def check_collision(initFrame, endFrame): | |
| 122 | + scene = bpy.context.scene | |
| 123 | + frame_current = bpy.context.scene.frame_current | |
| 124 | + startFrame = initFrame + int(math.fabs((endFrame - initFrame)/2)) | |
| 125 | + collisionFrame = -1 | |
| 126 | + for i in range(startFrame, endFrame + 1, 1): | |
| 127 | + scene.frame_set(i) | |
| 128 | + right_cube = bpy.context.scene.objects.get('right_hand_box') | |
| 129 | + left_cube = bpy.context.scene.objects.get('left_hand_box') | |
| 130 | + | |
| 131 | + if (bmesh_check_intersect_objects(right_cube, left_cube)): | |
| 132 | + collisionFrame = i | |
| 133 | + break | |
| 134 | + scene.frame_set(frame_current) | |
| 135 | + return collisionFrame | |
| 57 | 136 | |
| 58 | 137 | # Função que limpa todos os keyframes e define a quantidade de frames |
| 59 | 138 | def erase_all_keyframes(): |
| ... | ... | @@ -117,12 +196,15 @@ def get_endFrame(json_input, hands_frames_retilineo): |
| 117 | 196 | endsFrame.append(max(hands_frames_retilineo)) |
| 118 | 197 | return(max(endsFrame)) |
| 119 | 198 | |
| 120 | -def validate_rotation(bone, endFrame): | |
| 199 | +def validate_rotation(bone, endFrame, startFrame = 0): | |
| 200 | + if (endFrame - startFrame == 1): | |
| 201 | + return True | |
| 202 | + | |
| 121 | 203 | rotFrames = [[]] |
| 122 | 204 | scene = bpy.context.scene |
| 123 | 205 | frame_current = bpy.context.scene.frame_current |
| 124 | 206 | |
| 125 | - for i in range(0, endFrame + 1,1): | |
| 207 | + for i in range(startFrame+1, endFrame+1, 1): | |
| 126 | 208 | scene.frame_set(i) |
| 127 | 209 | rotFrames[-1] = bone.rotation_quaternion.to_euler() |
| 128 | 210 | rotFrames.append( [] ) |
| ... | ... | @@ -130,9 +212,9 @@ def validate_rotation(bone, endFrame): |
| 130 | 212 | rotFrames.remove([]) |
| 131 | 213 | scene.frame_set(frame_current) |
| 132 | 214 | |
| 133 | - for k in range(1, endFrame + 1, 1): | |
| 215 | + for k in range(1, len(rotFrames), 1): | |
| 134 | 216 | for i in range(0, 3, 1): |
| 135 | - if (math.fabs(rotFrames[k][i] - rotFrames[k-1][i])) > math.pi : | |
| 217 | + if (math.fabs(rotFrames[k][i] - rotFrames[k-1][i])) > math.pi/2: | |
| 136 | 218 | return False |
| 137 | 219 | return True |
| 138 | 220 | ... | ... |