def rigRecon(input, context, timeOffset, rot, transl): for name in input.keys(): entry = input[name] bpy.ops.object.select_pattern(pattern=name + "*", extend=False) print("pattern: %s" % name + '*') obj = context.selected_objects[0] print("Keyframing %s" % obj.name) prevPose = None prevEuler = None offset = Vector((0, 0, 0)) for sFrameId in entry['log'].keys(): state = entry['log'][sFrameId] frameId = int(sFrameId) #print( "Adding keyFrame %d" % frameId ) context.scene.frame_set(frameId) obj.location = state['pos'] obj.rotation_mode = 'QUATERNION' q = Quaternion(state['pose']) if prevPose: if q.dot(prevPose) < 0: q.negate() #print('newq:',q) # else: # print('dotok:', prevPose, q, q.dot(prevPose)) obj.rotation_quaternion = q #obj.rotation_euler = q.to_euler('XYZ') #obj.rotation_euler.x += offset.x #obj.rotation_euler.y += offset.y #obj.rotation_euler.z += offset.z #if prevEuler: # d = Vector(obj.rotation_euler) - Vector(prevEuler) # if d.length > 1: # print("[",sFrameId,"] d: ", d ) # print('prev: ', prevEuler, 'new: ', obj.rotation_euler) # if abs(d.x) > math.pi/2: # offset.x += 2*math.pi # obj.rotation_euler.x += 2*math.pi # print( "offset.x:" , offset.x, 'newx: ', obj.rotation_euler.x ) # if abs(d.y) > math.pi/2: # offset.y += 2*math.pi # obj.rotation_euler.y += 2*math.pi # print( "offset.y:" , offset.y, 'newy: ', obj.rotation_euler.y ) # if abs(d.z) > math.pi/2: # offset.z += 2*math.pi # obj.rotation_euler.z += 2*math.pi # print( "offset.z:" , offset.z, 'newz: ', obj.rotation_euler.z ) #obj.rotation_euler.x -= math.pi / 2. #bpy.ops.anim.keyframe_insert() #obj.keyframe_insert( data_path="LocRot", index=-1,frame=frameId) bpy.ops.anim.keying_set_active_set(type='BUILTIN_KSI_LocRot') bpy.ops.anim.keyframe_insert() #obj.keyframe_insert(index=-1) #obj.keyframe_insert(data_path="location", index=-1, frame=frameId) #obj.keyframe_insert(data_path="rotation", index=-1, frame=frameId) prevPose = q.copy() prevEuler = obj.rotation_euler.copy()
def shorten_quaternion_paths(qs): """ Given a list of quaternions, return a list of quaternions which produce the same rotations but where each element is always the closest quaternion to its predecessor. Applying this to the ordinates of a curve ensure rotation always takes the "shortest path". See glTF issue #1395. """ # Also note: it does not matter if you apply this before or after coordinate # conversion :) res = [] if qs: res.append(qs[0]) for i in range(1, len(qs)): q = Quaternion(qs[i]) res.append(-q if q.dot(res[-1]) < 0 else q) return res
def sequence_items_from_action(action, sequence_items, action_data, action_type, frame_count, animation_type): if animation_type == "REGULAR": locations_map = {} rotations_map = {} scales_map = {} bones_map = action_data p_bone: PoseBone for parent_tag, p_bone in bones_map.items(): pos_vector_path = p_bone.path_from_id("location") rot_quaternion_path = p_bone.path_from_id("rotation_quaternion") rot_euler_path = p_bone.path_from_id("rotation_euler") scale_vector_path = p_bone.path_from_id("scale") # Get list of per-frame data for every path b_locations = evaluate_vector( action.fcurves, pos_vector_path, frame_count) b_quaternions = evaluate_quaternion( action.fcurves, rot_quaternion_path, frame_count) b_euler_quaternions = evaluate_euler_to_quaternion( action.fcurves, rot_euler_path, frame_count) b_scales = evaluate_vector( action.fcurves, scale_vector_path, frame_count) # Link them with Bone ID if len(b_locations) > 0: locations_map[parent_tag] = b_locations # Its a bit of a edge case scenario because blender uses either # euler or quaternion (I cant really understand why quaternion doesnt update with euler) # So we will prefer quaternion over euler for now # TODO: Theres also third rotation in blender, angles or something... if len(b_quaternions) > 0: rotations_map[parent_tag] = b_quaternions elif len(b_euler_quaternions) > 0: rotations_map[parent_tag] = b_euler_quaternions if len(b_scales) > 0: scales_map[parent_tag] = b_scales # Transform position from local to armature space for parent_tag, positions in locations_map.items(): p_bone = bones_map[parent_tag] bone = p_bone.bone for frame_id, position in enumerate(positions): mat = bone.matrix_local if bone.parent is not None: mat = bone.parent.matrix_local.inverted() @ bone.matrix_local mat_decomposed = mat.decompose() bone_location = mat_decomposed[0] bone_rotation = mat_decomposed[1] position.rotate(bone_rotation) diff_location = Vector(( (position.x + bone_location.x), (position.y + bone_location.y), (position.z + bone_location.z), )) positions[frame_id] = diff_location # Transform rotation from local to armature space for parent_tag, quaternions in rotations_map.items(): p_bone = bones_map[parent_tag] bone = p_bone.bone prev_quaternion = None for quaternion in quaternions: if p_bone.parent is not None: pose_rot = Matrix.to_quaternion(bone.matrix) quaternion.rotate(pose_rot) if prev_quaternion is not None: # "Flickering bug" fix - killso: # This bug is caused by interpolation algorithm used in GTA # which is not slerp, but straight interpolation of every value # and this leads to incorrect results in cases if dot(this, next) < 0 # This is correct "Quaternion Lerp" algorithm: # if (Dot(start, end) >= 0f) # { # result.X = (1 - amount) * start.X + amount * end.X # ... # } # else # { # result.X = (1 - amount) * start.X - amount * end.X # ... # } # (Statement difference is only substracting instead of adding) # But GTA algorithm doesn't have Dot check, # resulting all values that are not passing this statement to "lag" in game. # (because of incorrect interpolation direction) # So what we do is make all values to pass Dot(start, end) >= 0f statement if Quaternion.dot(prev_quaternion, quaternion) < 0: quaternion *= -1 prev_quaternion = quaternion # WARNING: ANY OPERATION WITH ROTATION WILL CAUSE SIGN CHANGE. PROCEED ANYTHING BEFORE FIX. if len(locations_map) > 0: sequence_items[ensure_action_track( TrackType.BonePosition, action_type)] = locations_map if len(rotations_map) > 0: sequence_items[ensure_action_track( TrackType.BoneRotation, action_type)] = rotations_map if len(scales_map) > 0: sequence_items[ensure_action_track( TrackType.BoneScale, action_type)] = scales_map elif animation_type == "UV": u_locations_map = {} v_locations_map = {} uv_mat = action uv_nodetree = uv_mat.node_tree mat_nodes = uv_nodetree.nodes vector_math_node = None # Verify if material has required node(Vector Math) to get animation data for node in mat_nodes: if node.type == 'VECT_MATH': vector_math_node = node break if vector_math_node is None: raise Exception("Unable to find Vector Math node in material to get UV animation data!") else: pos_vector_path = vector_math_node.inputs[1].path_from_id("default_value") uv_fcurve = uv_nodetree.animation_data.action.fcurves uv_locations = evaluate_vector( uv_fcurve, pos_vector_path, frame_count) if len(uv_locations) > 0: u_channel_loc = [] v_channel_loc = [] for vector in uv_locations: u_channel_loc.append(round(vector.x, 4)) v_channel_loc.append(-round(vector.y, 4)) if len(u_channel_loc) > 0: u_locations_map[0] = u_channel_loc if len(v_channel_loc) > 0: v_locations_map[0] = v_channel_loc if len(u_locations_map) > 0: sequence_items[ensure_action_track( TrackType.UV0, action_type)] = u_locations_map if len(v_locations_map) > 0: sequence_items[ensure_action_track( TrackType.UV1, action_type)] = v_locations_map
def execute(self, context): data = [] objId = 0 for ob in bpy.data.objects: if (ob.physacq.is_actor): print(ob.name) o = {} o['name'] = ob.name o['objId'] = objId o['shape'] = ob.physacq.shape o['size'] = { "x": ob.dimensions[0], "y": ob.dimensions[1], "z": ob.dimensions[2] } o['mass'] = ob.physacq.mass log = {} if not ob.animation_data: continue action = ob.animation_data.action for fcu in action.fcurves: #print( "fcu: ", fcu ) #print( "prop: ", fcu.data_path ) #print( "propId: ", fcu.array_index ) if fcu.data_path == 'location': logKey = 'pos' elif fcu.data_path == 'rotation_euler': logKey = 'pose' elif fcu.data_path == 'rotation_quaternion': logKey = 'pose' elif fcu.data_path == 'scale': continue else: print("Can't interpret keyframe type, attention!!!", fcu.data_path) continue if fcu.array_index == 0: subKey = 'x' elif fcu.array_index == 1: subKey = 'y' elif fcu.array_index == 2: subKey = 'z' elif fcu.array_index == 3: subKey = 'w' else: print("Unprepared!") keyframe_points = fcu.keyframe_points for entry in keyframe_points: #print( entry.co ) #print( "key:", str(int(entry.co[0])) ) frameId = str(int(entry.co[0])) if frameId not in log: log[frameId] = {} if logKey not in log[frameId]: log[frameId][logKey] = {} log[frameId][logKey][subKey] = entry.co[1] prevKey = None prevQ = None for key, frame in log.items(): if 'pos' in log[key]: pos = Vector( coordinatesUtil.jsonVector3ToTuple( log[key]['pos'])) log[key]['pos'] = coordinatesUtil.vector3ToJson( self.coordChangeM * pos) if 'pose' not in log[key]: continue #print( 'ob.rotation_mode:', ob.rotation_mode) if ob.rotation_mode == 'XYZ': v = Vector( coordinatesUtil.jsonVector3ToTuple( log[key]['pose'])) eul = Euler(v, ob.rotation_mode) q = eul.to_quaternion() elif ob.rotation_mode == 'QUATERNION': entry = log[key]['pose'] print(entry) q = Quaternion(( entry['x'], entry['y'], entry['z'], entry['w'])) # yes, array_index==3 was parsed to w if prevQ: dotProd = q.dot(prevQ) if dotProd < 0: print("dot: ", dotProd) #q.negate() #print( "q: %s" % (q.__repr__()) ) #log[ key ] ['pose'] = { 'x' : q[1], 'y' : -q[3], 'z': q[2], 'w' : q[0] } log[key]['pose'] = coordinatesUtil.quatToJson( coordinatesUtil.quatFromBlender(q)) if prevKey: #print( "check: ", log[prevKey]['pose'], "\n",log[key]['pose'] ) q2 = Quaternion(q) q2.negate() #print( "q:", q, "negative: ", q2 ) prevKey = key prevQ = q o['log'] = log data.append(o) objId += 1 if not len(data): self.report({'WARNING'}, "No keyframes, not saving") return {'FINISHED'} fp = context.scene.physacq.savePath if len(bpy.path.basename(fp)) == 0: fp = "//cuboids.json" if fp.find("//") >= 0: fp = bpy.path.abspath(fp) self.report({'INFO'}, "Saving poses to %s" % (fp)) f = open(fp, 'w') json.dump(data, f) f.close() return {'FINISHED'}