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 | ... | ... |