GUI mode
Give a name for your agent type (i.e. testMan).
Select the skeleton and geo files. The "Skeleton File" will point to your skeleton definition file (.atomsskel, .fbx or .usd), the "Skin Path" to your skinned geo file (.geos, .fbx or .usd) and the "Ragdoll Path" to your ragdoll file (if you have one).
Set the state machine to "manStateMachine" (you created this in the state machine tutorial) or select it from the drop down menu on the right side of the field.
You can edit the Agent Type scale multiplier if you want, but it's not necessary for this tutorial.
Joint Tags
Atoms need some information on some joints to be able to place an agent correctly on a ground.
If you exported the atomsskel file following our tutorials the file will contain already the tag information, so there is not need to follow the next steps. You can see straight away if your joints were tagged properly as they will be colored inside the preview. Please refer to the Agent Type section for more info about colors.
In case you are trying to set up your own character and your joints are not tagged yet, please do the following.
First, you need to tag all the pelvis. The first pelvis must the first joint for the hierarchy. In the man case, the first joint is named "Hips". Expand the pelvises tab, then write "Hips" inside the joint field and press the "add pelvis" button. It adds the pelvis tag to the root joint. If your skeleton is a quadruped, you need to tag also the second pelvis that is the joint that connect the front legs or if a creature with more legs tag any other pelvis.
Now we need to tag the legs. Expand the legs tab. You need to set here 4 input to define a leg. Inside the root field, write the start joint of a leg. In this case, for the man left leg, I added LeftUpLeg. Then I need to fill the Ik field. It will be the joint of the ankle where Atoms adds the ik affector. In my case is the joint LeftFoot. Then we need to set the tip joint. It is the joint on the foot that checks the contact with the ground during the simulation. In my case, it's the LeftToeBase joint. The last parameter is the pole vector. Here you need to set the world position of the pole vector effector. For my left leg the root joint is around position (7.45, -7.74, 1.54) so a pole vector of (7.45, -7.74, 12.0) should be enough (to see the world space position for your joints inside the viewport, right click on the viewport and enable “Draw Joint World Pos“). If you don't want to pass a pole vector but let atoms compute automatically just set (0,0,0).
Please be aware Atoms won’t be able to compute a valid pole vector in case your leg joints are on a straight line. At this point press the "Add leg "button to tag the leg joints. Do the same for the right leg.
The last thing to do is to add the skipIk tag. This tag is used to exclude joints from the computation of the ik. For example, if you have a joint between the leg root and the knee, or the knee and the ankle you usually want to exclude this joint from the ik computation, otherwise Atoms rotates as well this joint. So write the name of the joint that you want to exclude inside the SkipIk tab and press the "Add SkipIk "button to tag this joint.
Click on the "Register" button or press CTRL+S.
Your agent type is now ready to be used.
The Agent Type scale multiplier will affect all agents of this type. If you want to change the scale of a specific agent you should use the agent scale Behaviour Module.
Script mode
In case you selected the script mode, you should edit your script, below you can find the default template for an agent type.
Then hit CTRL+S or click on the "Register" button.
AgentType
import os import AtomsMath import AtomsCore import Atoms import AtomsUtils from Atoms import GLOBAL_NAMES class AgentTypeEvent1(Atoms.SimulationEvent): eventName = 'agentType1' skelFile = '' geoPath = '' skinPath = '' ragdollSetupPath = '' characterSetupPath = '' stateMachine = '' scaleMultiplier = 1.0 radius = 1.0 drawOptimization = {} pelvises = [] skipIks = [] legs = [] sanitizeJointNames = False def __init__(self): Atoms.SimulationEvent.__init__(self) self.setName(self.eventName) @staticmethod def check_file(file_path, extensions): # we have to consider users might be use database paths# # so only check if the file is valid in case a valid extension is found if file_path == "": return True p = file_path.split("@")[0] if "." not in file_path: return True ext = p.split(".")[-1] if ext in extensions: return os.path.exists(AtomsUtils.solvePath(p)) return True def load(self): AGENT_TYPE = GLOBAL_NAMES.AGENT_TYPE if not self.check_file(self.skelFile, Atoms.SkeletonLoaderFactory.instance().getRegisteredSkeletonLoaders()): AtomsUtils.Logger.warning("Invalid skeleton file " + str(self.skelFile) + " for agent type " + self.eventName) return if not self.check_file(self.ragdollSetupPath, ["atomsragdoll"]): AtomsUtils.Logger.warning("Invalid ragdoll file " + str(self.ragdollSetupPath) + " for agent type " + self.eventName) return if not self.check_file(self.characterSetupPath, ["atomscharacter"]): AtomsUtils.Logger.warning("Invalid characterization file " + str(self.self.characterSetupPath) + " for agent type " + self.eventName) return if not self.check_file(self.geoPath, Atoms.MeshLoaderFactory.instance().getRegisteredMeshLoaders()): AtomsUtils.Logger.warning("Invalid proxy geo file " + str(self.geoPath) + " for agent type " + self.eventName) return if not self.check_file(self.skinPath, Atoms.MeshLoaderFactory.instance().getRegisteredMeshLoaders()): AtomsUtils.Logger.warning("Invalid skin geo file " + str(self.skinPath) + " for agent type " + self.eventName) return skel = Atoms.loadSkeleton(self.skelFile) if self.sanitizeJointNames: skel.sanitizeJointNames() for p in self.pelvises: id = skel.jointId(p) if id != -1: skel.addPelvis(id) for si in self.skipIks: id = skel.jointId(si) if id != -1: skipIkMeta = AtomsCore.BoolMetadata(True) skel.addJointMetadata(id, "skipIk", skipIkMeta) for f in self.legs: if not isinstance(f, (tuple, list)) or not len(f) == 4: continue root = skel.jointId(f[0]) ik = skel.jointId(f[1]) tip = skel.jointId(f[2]) if root == -1 or tip == -1 or ik == -1: continue skel.addFoot(ik, root, tip) poleVector = AtomsMath.V3d(f[3][0], f[3][1], f[3][2]) poleVectorMeta = AtomsCore.Vector3Metadata(poleVector) skel.addJointMetadata(root, "poleVector", poleVectorMeta) if self.legs or self.pelvises: skel.buildIkData() aType = Atoms.AgentType() aType.setSkeleton(skel) meshMap = Atoms.loadMesh(self.geoPath) if meshMap: aType.metadata()[AGENT_TYPE.LOW_GEO] = meshMap elif self.geoPath != "": AtomsUtils.Logger.warning("Could not read geo file: " + str(self.geoPath)) skinMap = Atoms.loadMesh(self.skinPath) if skinMap: aType.metadata()[AGENT_TYPE.SKIN_GEO] = skinMap elif self.skinPath != "": AtomsUtils.Logger.warning("Could not read skin geo file: " + str(self.skinPath)) ragdoll_setup = AtomsCore.MapMetadata() if self.ragdollSetupPath: ark = AtomsCore.Archive() if ark.readFromFile(AtomsUtils.solvePath(self.ragdollSetupPath)): ragdoll_setup.deserialise(ark) aType.metadata()[AGENT_TYPE.RAGDOLL] = ragdoll_setup character_setup = AtomsCore.MapMetadata() if self.characterSetupPath: ark = AtomsCore.Archive() if ark.readFromFile(AtomsUtils.solvePath(self.characterSetupPath)): character_setup.deserialise(ark) aType.metadata()[AGENT_TYPE.CHARACTER] = character_setup else: character_setup = Atoms.createCharacterizationFromSkeleton(self.skelFile) if character_setup: aType.metadata()[AGENT_TYPE.CHARACTER] = character_setup aType.metadata()[AGENT_TYPE.STATE_MACHINE] = AtomsCore.StringMetadata(self.stateMachine) aType.metadata()[AGENT_TYPE.SCALE_MULTIPLIER] = AtomsCore.DoubleMetadata(self.scaleMultiplier) aType.metadata()[AGENT_TYPE.RADIUS] = AtomsCore.DoubleMetadata(self.radius) lod_levels = [0] lod_values = [] for lod_key in ['B', 'C', 'D']: if lod_key in self.drawOptimization: lod_levels.append(self.drawOptimization[lod_key][0]) lod_values.append(self.drawOptimization[lod_key][1]) lod_levels_meta = AtomsCore.IntArrayMetadata() lod_levels_meta.set(lod_levels) aType.metadata()[AGENT_TYPE.LOD_LEVELS] = lod_levels_meta lod_values_meta = AtomsCore.DoubleArrayMetadata() lod_values_meta.set(lod_values) aType.metadata()[AGENT_TYPE.LOD_DISTANCES] = lod_values_meta lod_mode_meta = AtomsCore.IntMetadata(0) if 'mode' in self.drawOptimization: lod_mode_meta.set(self.drawOptimization['mode']) aType.metadata()[AGENT_TYPE.LOD_MODE] = lod_mode_meta Atoms.AgentTypes.instance().addAgentType(self.eventName, aType) def unload(self): Atoms.AgentTypes.instance().removeAgentType(self.eventName)