To build an animation clip event, open the Atoms UI and make sure to have selected the "AnimationClipEvents" tab.
Click on "Add" and you will be prompted with a dialog asking if you want to edit your clip in GUI or script mode.
Widget Connector | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
GUI mode
Give a name to your animation clip (i.e. newManWalk).
Select the FBX, USD or AtomsClip file containing the animation. As soon as you select a file, Atoms displays it on the right side of the GUI. If you are using an Atomsclip, or a Usd that contain only the skelAnimation primitive but not the SkelRoot primitive, you need first to set the compatible agent type and press register.
Most of the GUI options were explained here already.
For this tutorial, we are going to change the "Loop Blend" to 5.
The preview will be very handy now to find the loop start and end frames. Move the mouse over the preview and find two poses that are similar to each other. Generally, the first frame where a foot gets down on the ground will be a good choice.
In our case, the start frame will be 36, and the end frame will be 70.
Set the "Direction Type" to static.
In the preview, you will also see the 3D Axis, by looking at your skeleton moving you can see how skeleton root travels in space from the beginning to the end of your clip. In this case, it's easy to see that the major movement axis for this clip is the X axis.
For this reason, you can set the "Direction" field to (0, 0, 1).
Inside the “Compatible Agent Type” select the agent type you created before.
Click the "Register" button or press CTRL+S.
It is possible your clip might need some cleaning to work correctly in the simulation, like setting correctly the foot down state. In that case, please follow the next steps.
First, we need to compute the ground height data. Check the "Override Feet/Pelvis Height "box. Then you need to set inside the "Ground Height" the y coordinate of the ground. In our case, the value of 0 should be enough.
Then if we want to use the foot lock system, we need to setup the foot down data. Open the "Foot Down "tab. If the data is already inside the clip, we can press "Load from clip" or leave empty, or we can compute them from scratch. Select the "velocity" radio button, set a value of 0.3 on the field on the left, and then press the "compute" button. Atoms will fill the space with an animation curve for each foot. You can edit the curve of each feet moving the keyframe between 0 and 1 to set the footOnGround tag. This tag is used during the simulation to lock the feet. When its value is 1, Atoms locks the feet to that position and then it releases them when the value is 0. Inside the preview, you can see some yellow disk under the foot when it has a keyframe at 1. Check the animation and make sure that the yellow disk appears when a foot is not moving, otherwise tweak the animation curve.
Now we can set up the transition frame. In our case, the clip is relatively short so that we can skip this. But if you have a long clip, and you want setup valid transition frames that are between the start and end of your loop, you can put them here. Use the ',' to separate multiple entries, or just -1 if you want that all the frame are good transition frames. Atoms can help you finding suitable transition frames. Press the "Auto" button on the left. Inside the dialog, select the right agent type, the reference frame that is used to find similar frames, and a tolerance value. The tolerance is expressed in degrees; usually, values of 0.3-0.6 should be sufficient. Press "Compute "and if there are any pose similar to the reference frame the tool adds them automatically to the transition frames field.
If you want to mirror or reverse the animation, you can check the respective checkbox.
The last thing, you can apply clip operators. The clip operators modify in a different way the animation clip. For example, some operators modify specific joints, extend the range or modify the speed. For example, select "retime" from the combo box and press on the plus button. Then select the retime added on the list, and change the multiplier parameter to 0.8. This slow down the whole animation clip.
Click the "Register" button or press CTRL+S to register the clip again.
Your Animation Clip is now ready to be used.
Script mode
In case you selected the script mode, you should edit the below animation clip template script.
Then hit CTRL+S or click on the "Register" button.
Animation Clip
Code Block | ||
---|---|---|
| ||
import os import AtomsMath import AtomsCore import Atoms import AtomsUtils from Atoms import GLOBAL_NAMES class AnimClipEvent17(Atoms.SimulationEvent): eventName = 'animClip17' clipPath = 'path/file.atomsclip' blendFramesAfterFootUp = 4 loop = True loopStart = 0 loopEnd = 10 loopBlend = 5 footLockDisabled = False direction = [1.0,0.0,0.0] directionType = 0 directionFromJoints = [0,1] compatibleAgentType = '' transitionFrames = [] footDownData = {} additiveMode = 0 additiveReferenceClip = '' overridePelvisFeetHeight = False groundHeight = 0.0 reverse = False mirror = False mirrorAnimPlane = 0 mirrorBindPlane = 0 sideJointsSearch = 0 rawMirror = False stringReplacement = ['', ''] clipOperators = [] globalIkData = {} enablePerching = False perchingFactor = [] sanitizeJointNames = False def __init__(self): Atoms.SimulationEvent.__init__(self) self.setName(self.eventName) def load(self): CLIP = GLOBAL_NAMES.CLIP aClips = Atoms.AnimationClips.instance() aClips.addAnimationClip(self.eventName, self.clipPath, True) acPtr = aClips.animationClip(self.eventName) if acPtr is None: AtomsUtils.Logger.warning("Could not read clip file " + str(self.clipPath) + " for clip: " + self.eventName) return if self.sanitizeJointNames: acPtr.sanitizeJointNames() metadataMap = acPtr.metadata() metadataMap[CLIP.BLEND_FRAMES_AFTER_FOOT_UP] = AtomsCore.IntMetadata(self.blendFramesAfterFootUp) metadataMap[CLIP.LOOP] = AtomsCore.BoolMetadata(self.loop) metadataMap[CLIP.LOOP_START] = AtomsCore.IntMetadata(self.loopStart) metadataMap[CLIP.LOOP_END] = AtomsCore.IntMetadata(self.loopEnd) metadataMap[CLIP.LOOP_NUM_BLEND_FRAMES] = AtomsCore.IntMetadata(self.loopBlend) if self.directionType == 1: acPtr.setDirectionType(Atoms.AnimationClip.DirectionType.Static) elif self.directionType == 0: acPtr.setDirectionType(Atoms.AnimationClip.DirectionType.Pelvis) elif self.directionType == 2: acPtr.setDirectionType(Atoms.AnimationClip.DirectionType.Joints) else: acPtr.setDirectionType(Atoms.AnimationClip.DirectionType.Clip) acPtr.setDirection(AtomsMath.V3d(self.direction[0], self.direction[1], self.direction[2])) acPtr.setDirectionFromJoints(self.directionFromJoints[0], self.directionFromJoints[1]) acPtr.disableFootLock(self.footLockDisabled) acPtr.setTransitionFrames(self.transitionFrames) acPtr.setCompatibleAgentType(self.compatibleAgentType) if self.overridePelvisFeetHeight: acPtr.computePelvisAndFeetHeight(self.groundHeight) for foot_name in self.footDownData: foot_down_meta = AtomsCore.Vector2ArrayMetadata() foot_data = [] for frame_data in self.footDownData[foot_name]: foot_data.append(AtomsMath.V2d(frame_data[0], frame_data[1])) foot_down_meta.set(foot_data) acPtr.setFootDownData(foot_name, foot_down_meta) if self.additiveMode == 0: acPtr.setAdditiveMode(Atoms.AnimationClip.AdditiveMode.Disabled) elif self.additiveMode == 1: acPtr.setAdditiveMode(Atoms.AnimationClip.AdditiveMode.BindPose) elif self.additiveMode == 2: acPtr.setAdditiveMode(Atoms.AnimationClip.AdditiveMode.ReferenceClip) acPtr.setAdditiveReferenceClip(self.additiveReferenceClip) if self.reverse: acPtr.reverse() if self.mirror: mirrorAxis = (Atoms.AnimationClip.MirrorPlane.X, Atoms.AnimationClip.MirrorPlane.Y, Atoms.AnimationClip.MirrorPlane.Z) searchMode = (Atoms.AnimationClip.MirrorSideJointsSearch.Auto, Atoms.AnimationClip.MirrorSideJointsSearch.ByName) acPtr.computeMirror(mirrorAxis[self.mirrorAnimPlane], mirrorAxis[self.mirrorBindPlane], searchMode[self.sideJointsSearch], self.stringReplacement[0], self.stringReplacement[1], self.rawMirror) for joint_name in self.globalIkData: ik_meta = AtomsCore.Vector2ArrayMetadata() ik_data = [] for join_ik_data in self.globalIkData[joint_name]: ik_data.append(AtomsMath.V2d(join_ik_data[0], join_ik_data[1])) ik_meta.set(ik_data) acPtr.setGlobalIKData(joint_name, ik_meta) for co in self.clipOperators: name = co[0] values = co[1] type = co[2] enabled = co[3] if not enabled: continue opMeta = AtomsCore.MapMetadata() for k, v in values.iteritems(): if isinstance(v, (tuple, list)): if len(v) == 2: opMeta.addEntry(k, AtomsCore.Vector2Metadata(AtomsMath.V2d(*v))) elif len(v) == 3: opMeta.addEntry(k, AtomsCore.Vector3Metadata(AtomsMath.V3d(*v))) elif isinstance(v, bool): opMeta.addEntry(k, AtomsCore.BoolMetadata(v)) elif isinstance(v, int): opMeta.addEntry(k, AtomsCore.IntMetadata(v)) elif isinstance(v, float): opMeta.addEntry(k, AtomsCore.DoubleMetadata(v)) elif isinstance(v, basestring): opMeta.addEntry(k, AtomsCore.StringMetadata(v)) if not Atoms.AnimationClipOperators.instance().processClip(acPtr, type, opMeta): AtomsUtils.Logger.warning("Could not process operator %s of type %s" % (name, type)) if self.enablePerching: meta = AtomsCore.Vector2ArrayMetadata() data = [] for p_data in self.perchingFactor: data.append(AtomsMath.V2d(p_data[0], p_data[1])) meta.set(data) acPtr.setPerchingData(meta) acPtr.computeAnimatedJoints() def unload(self): Atoms.AnimationClips.instance().removeAnimationClip(self.eventName) |