def build_skeleton(self, pkg_name, obj_name): pkg = ue.get_or_create_package('{0}_Skeleton'.format(pkg_name)) skel = Skeleton('{0}_Skeleton'.format(obj_name), pkg) # add a root bone from which all of the others will descend # (this trick will avoid generating an invalid skeleton [and a crash], as in UE4 only one root can exist) skel.skeleton_add_bone('root', -1, FTransform()) # iterate bones in the json file, note that we move from opengl axis to UE4 # (y on top, x right, z forward right-handed) to (y right, x forward left-handed, z on top) for bone in self.model['bones']: # assume no rotation quat = FQuat() # give priority to quaternions # remember to negate x and y axis, as we invert z on position if 'rotq' in bone: quat = FQuat(bone['rotq'][2], bone['rotq'][0] * -1, bone['rotq'][1] * -1, bone['rotq'][3]) elif 'rot' in bone: quat = FRotator(bone['rot'][2], bone['rot'][0] - 180, bone['rot'][1] - 180).quaternion() pos = FVector(bone['pos'][2] * -1, bone['pos'][0], bone['pos'][1]) * self.scale # always set parent+1 as we added the root bone before skel.skeleton_add_bone(bone['name'], bone['parent'] + 1, FTransform(pos, quat)) skel.save_package() return skel
def build_animation(self, pkg_name, obj_name): factory = AnimSequenceFactory() factory.TargetSkeleton = self.skeleton new_anim = factory.factory_create_new('{0}_Animation'.format(pkg_name)) new_anim.NumFrames = self.model['animation']['length'] * \ self.model['animation']['fps'] new_anim.SequenceLength = self.model['animation']['length'] # each bone maps to a track in UE4 animations for bone_index, track in enumerate(self.model['animation']['hierarchy']): # retrieve the bone/track name from the index (remember to add 1 as we have the additional root bone) bone_name = self.skeleton.skeleton_get_bone_name(bone_index + 1) positions = [] rotations = [] scales = [] for key in track['keys']: t = key['time'] if 'pos' in key: positions.append( (t, FVector(key['pos'][2] * -1, key['pos'][0], key['pos'][1]) * 100)) if 'rotq' in key: rotations.append((t, FQuat( key['rotq'][2], key['rotq'][0] * -1, key['rotq'][1] * -1, key['rotq'][3]))) elif 'rot' in key: # is it a quaternion ? if len(key['rot']) == 4: rotations.append( (t, FQuat(key['rot'][2], key['rot'][0] * -1, key['rot'][1] * -1, key['rot'][3]))) else: rotations.append( (t, FRotator(key['rot'][2], key['rot'][0] - 180, key['rot'][1] - 180).quaternion())) pos_keys = [] rot_keys = [] # generate the right number of frames for t in numpy.arange(0, self.model['animation']['length'], 1.0 / self.model['animation']['fps']): pos_keys.append(self.interpolate_vector(positions, t)) rot_keys.append(self.interpolate_quaternion( rotations, t).get_normalized()) track_data = FRawAnimSequenceTrack() track_data.pos_keys = pos_keys track_data.rot_keys = rot_keys new_anim.add_new_raw_track(bone_name, track_data) # if we have curves, just add them to the animation if self.curves: new_anim.RawCurveData = RawCurveTracks(FloatCurves=self.curves) new_anim.save_package() return new_anim
def split_hips(self, animation, bone='Hips'): self.choosen_skeleton = None # first ask for which skeleton to use: self.window = SWindow(title='Choose your new Skeleton', modal=True, sizing_rule=1)( SObjectPropertyEntryBox(allowed_class=Skeleton, on_object_changed=self.set_skeleton) ) self.window.add_modal() if not self.choosen_skeleton: raise DialogException('Please specify a Skeleton for retargeting') factory = AnimSequenceFactory() factory.TargetSkeleton = self.choosen_skeleton base_path = animation.get_path_name() package_name = ue.get_path(base_path) object_name = ue.get_base_filename(base_path) new_anim = factory.factory_create_new(package_name + '/' + object_name + '_rooted') new_anim.NumFrames = animation.NumFrames new_anim.SequenceLength = animation.SequenceLength # first step is generatin the 'root' track # we need to do it before anything else, as the 'root' track must be the 0 one for index, name in enumerate(animation.AnimationTrackNames): if name == bone: data = animation.get_raw_animation_track(index) # extract root motion root_motion = [(position - data.pos_keys[0]) for position in data.pos_keys] # create a new track (the root motion one) root_data = FRawAnimSequenceTrack() root_data.pos_keys = root_motion # ensure empty rotations ! root_data.rot_keys = [FQuat()] # add the track new_anim.add_new_raw_track('root', root_data) break else: raise DialogException('Unable to find bone {0}'.format(bone)) # now append the original tracks, but removes the position keys # from the original root bone for index, name in enumerate(animation.AnimationTrackNames): data = animation.get_raw_animation_track(index) if name == bone: # remove root motion from original track data.pos_keys = [data.pos_keys[0]] new_anim.add_new_raw_track(name, data) else: new_anim.add_new_raw_track(name, data) new_anim.save_package()
def interpolate_quaternion(self, timeline, t): keys = [] x_values = [] y_values = [] z_values = [] w_values = [] for key, value in timeline: keys.append(key) x_values.append(value[0]) y_values.append(value[1]) z_values.append(value[2]) w_values.append(value[3]) x = numpy.interp(t, keys, x_values) y = numpy.interp(t, keys, y_values) z = numpy.interp(t, keys, z_values) w = numpy.interp(t, keys, w_values) return FQuat(x, y, z, w)