Atoms has its own internal format for agent caches, please check the reference to know more about what files is made of.
Cache header
The cache header is a MapMetadata cointaining the following key-value pairs:
- agentIds: an IntArrayMetadata containing all the agents ids
- cacheType: a StringMetadata,for internal use only and it must store the value "dynamic"
- endFrame: an IntMetadata containing the end frame
- startFrame: an IntMetadata containing the start frame
This file must have the following name extension: atoms (i.e. test_cache.atoms)
Agent types
To make the caches more portable, all the agent type files are saved in a "agentTypes" subfolder next to the cache files.
The agent type are serialized in a binary format:
agentType->serialize(agentTypeArchive);
agentTypeArchive->writeToFile("myAgentType.agentType")</pre>
Archive class.
Definition: Serialiser.h:29
AtomsPtr< AgentType > AgentTypePtr
Agent type pointer.
Definition: AgentType.h:17
To make an agent type visible inside the Atoms UI, an event simulation python wrapper script is exported in the same folder.
std::string cachePath = "/atomsDemo/cache/";
std::string agentType = agent->agentType().name();
agentTypePtr->serialize(agentTypeArchive);
agentTypeArchive->writeToFile(cachePath + "/agentTypes/" + agentType + ".agentType")
const std::string pythonEvent =
"import AtomsCore\n"
"import Atoms\n"
"import imath\n"
"\n"
"class "+ agentType +"Cached(Atoms.SimulationEvent):\n"
"\tagentTypeFile = r'" + agentTypePath + "'\n"
"\teventName = '" + agentType + "Cached'\n"
"\tagentTypeName = '+ agentType + '\n"
"\tgeoPath = ''\n"
"\tskinPath = ''\n"
"\tstateMachine = ''\n"
"\n"
"\tdef __init__(self):\n"
"\t\tAtoms.SimulationEvent.__init__(self)\n"
"\t\tself.setName(self.eventName)\n"
"\n"
"\tdef load(self):\n"
"\t\taType = Atoms.AgentType()\n"
"\t\taTypeArchive = AtomsCore.Archive()\n"
"\t\tif aTypeArchive.readFromFile(self.agentTypeFile):\n"
"\t\t\taType.deserialise(aTypeArchive)\n"
"\t\telse:\n"
"\t\t\treturn\n"
"\n"
"\t\tif self.geoPath:\n"
"\t\t\tmeshMap = AtomsCore.MapMetadata()\n"
"\t\t\ttypeArchive = AtomsCore.Archive()\n"
"\t\t\tif typeArchive.readFromFile(self.geoPath):\n"
"\t\t\t\tmeshMap.deserialise(typeArchive)\n"
"\t\t\t\taType.metadata()["lowGeo"] = meshMap\n"
"\n"
"\t\tif self.skinPath:\n"
"\t\t\tskinMap = AtomsCore.MapMetadata()\n"
"\t\t\tskinArchive = AtomsCore.Archive()\n"
"\t\t\tif skinArchive.readFromFile(self.skinPath):\n"
"\t\t\t\tskinMap.deserialise(skinArchive)\n"
"\t\t\t\taType.metadata()["skinGeo"] = skinMap\n"
"\n"
"\t\taType.metadata()["stateMachine"] = AtomsCore.StringMetadata(self.stateMachine)\n"
"\n"
"\t\tAtoms.AgentTypes.instance().addAgentType(self.agentTypeName, aType)\n";
std::string agentTypeWrapperPath = cachePath + "/agenTypes/" + agentType + ".py";
std::ofstream out(agentTypeWrapperPath);
out << pythonEvent;
out.close();</pre>
CACHED_AGENT_TYPE_SCRIPT = '''
import AtomsCore
import Atoms
class AgentTypeEvent$(Atoms.SimulationEvent):
\tagentTypeFile = r'agentTypeFile/pre>
\teventName = 'agentType/pre>
\tagentTypeName = 'agentTypeName/pre>
\tgeoPath = ''
\tskinPath = ''
\tstateMachine = ''
\tdef __init__(self):
\t\tAtoms.SimulationEvent.__init__(self)
\t\tself.setName(self.eventName)
\tdef load(self):
\t\taType = Atoms.AgentType()
\t\taTypeArchive = AtomsCore.Archive()
\t\tif aTypeArchive.readFromFile(self.agentTypeFile):
\t\t\taType.deserialise(aTypeArchive)
\t\telse:
\t\t\treturn
\t\tif self.geoPath:
\t\t\tmeshMap = AtomsCore.MapMetadata()
\t\t\ttypeArchive = AtomsCore.Archive()
\t\t\tif typeArchive.readFromFile(self.geoPath):
\t\t\t\tmeshMap.deserialise(typeArchive)
\t\t\t\taType.metadata()["lowGeo"] = meshMap
\t\tif self.skinPath:
\t\t\tskinMap = AtomsCore.MapMetadata()
\t\t\tskinArchive = AtomsCore.Archive()
\t\t\tif skinArchive.readFromFile(self.skinPath):
\t\t\t\tskinMap.deserialise(skinArchive)
\t\t\t\taType.metadata()["skinGeo"] = skinMap
\t\taType.metadata()["stateMachine"] = AtomsCore.StringMetadata(self.stateMachine)
\t\tAtoms.AgentTypes.instance().addAgentType(self.agentTypeName, aType)
'''
agentType = "atomsRobot"
if aType:
aType.serialise(atArchive)
atArchive.writeToFile(os.path.join(agentTypesPath, "%s.agentType" %
(agentType)))
agentTypesPath = "/atomsDemo/cache/test_cache/agentTypes/"
agentTypeScript = CACHED_AGENT_TYPE_SCRIPT
af = os.path.normpath(os.path.join(agentTypesPath, "%s.agentType" %
(agentType)))
agentTypeScript = agentTypeScript.replace("agentTypeFile$", af)
agentTypeScript = agentTypeScript.replace("agentType$","%sCached" %
agentType)
agentTypeScript = agentTypeScript.replace("agentTypeName$",
agentType)
agentTypeScript = agentTypeScript.replace("AgentTypeEvent$",
"%sCached" % (agentType))
with open(os.path.join(agentTypesPath, "%s.py" % (agentType)), "w") as pyFile:
pyFile.write(agentTypeScript)
pyFile.close()</pre>
static AgentTypes & instance()
Singleton access for global data.
Frame files
The cache has these four files per frame:
- header: basic info such as number of agents at the current frame, number of created agents and number of deleted agents
- frame: agent data such as agent type, position, velocity, bbox and variation
- meta: agents metadata
- pose: agent skeleton data
Header file
The cache frame header is a MapMetadata cointaining the following key-value pairs:
- agents: an IntArrayMetadata containing all the agents ids
- agentsCreated: an IntArrayMetadata containing the ids of the created agents at this frame.
- agentsDeleted: an IntArrayMetadata containing the ids of the deleted agents at this frame.
- box: an BoxMetadata containing the bounding box at the current frame
This file has the following extension: .$FRAME.header.atoms (i.e. test_cache.0010.header.atoms)
Frame file
The cache frame is a MapMetadata. Each entry in this map uses the agent id as key while the value is another MapMetadata containing the following key-value pairs:
- agentType: StringMetadata containing the agent type name
- box: BoxMetadata containing the agent bounding box
- position: Vector3Metadata containing the agent position
- variation: StringMetadata containing the agent variation
- velocity: Vector3Metadata containing the agent velocity
This is an example of frame data:
- "0":
- "agentType": StringMetadata("atomsRobot")
- "box": BoxMetadata(AtomsCore::Box(AtomsCore::Vector3(-10,-10,-10),AtomsCore::Vector3( 10, 10, 10)))
- "position": Vector3Metadata(AtomsCore::Vector3(0,0,0))
- "velocity": Vector3Metadata(AtomsCore::Vector3(1.4354,0.234,0.54433))
- "1":
- "5":
- "agentType": StringMetadata("atomsRobot")
- "box": BoxMetadata(...)
- "position": Vector3Metadata(...)
- "velocity": Vector3Metadata(...)
- ...
This file has the following extension: .$FRAME.frame.atoms (i.e. test_cache.0010.frame.atoms)
Metafile
The cache meta file is a MapMetadata. Each entry in this map uses the agent id as key while the value is another MapMetadata containing all the metadatas of the agent
This is an example:
- "0":
- "position": Vector3Metadata(...)
- "direction": Vector3Metadata(...)
- "state": IntMetadata(...)
- ...
- "1":
- "position": Vector3Metadata(...)
- "direction": Vector3Metadata(...)
- "state": IntMetadata(...)
- ...
- "1":
- "position": Vector3Metadata(...)
- "direction": Vector3Metadata(...)
- "state": IntMetadata(...)
- ...
- ...
This file must have the following extension: .$FRAME.meta.atoms (i.e. test_cache.0010.meta.atoms)
Pose file
The cache pose file is a MapMetadata. Each entry in this map uses the agent id as key while the value is a PoseMetadata that store the agent pose.
This file has the following extension: .$FRAME.pose.atoms (i.e. test_cache.0010.pose.atoms)
Writing an Atoms cache
An atoms cache can be exported using the AtomsCache::exportCacheFrame() function. This function exports only the frame files, the header and the agents type must be exported
#include <Atoms/AtomsCache
std::string cachePath = "/atomsDemo/cache/";
std::string cacheName = "cache_test";
int startFrame = 1;
int endFrame = 2;
// list of agents groups to export
std::vector<AtomsPtr<Atoms::AgentGroup>> agentGroups;
// if you are in maya or houdini you can get the agent groups from the agents simulation
auto sims = Atoms::AgentsSimulations::instance()
auto sim = sims.begin().second;
for(unsigned int i=0; i < sim.numAgentGroups(); i++)
{
agentGroups.push_back(sim.agentGroup(i));
}
// This maps the agent global id with the cache id
std::unordered_map<size_t, size_t> cacheIdMap;
std::set<std::string> agentTypes;
// Add compression and use multithread
size_t tags = AtomsCore::Archive::kRandomAccessCompress | AtomsCore::Archive::kMultithread;
for (unsigned int frame = startFrame; frame < endFrame; frame++)
{
AtomsCache::exportCacheFrame(cachePath, cacheName, frame, agentGroups, cacheIdMap, agentTypes, tags);
}
// save the agent types
Atoms::AgentTypes& agentTypes = Atoms::AgentTypes::instance();
for(const std::string agentType: agentTypes)
{
auto agentTypePtr = agentTypes->agentType(agentType);
if (!agentTypePtr)
continue;
AtomsCore::Archive agentTypeArchive(agentTypePtr->memSize());
agentType->serialize(agentTypeArchive);
std::string agentTypePath = cachePath + "/agenTypes/" + agentType + ".agentType";
agentTypeArchive->writeToFile(agentTypePath);
//store the python event wrapper
const std::string pythonEvent =
"import AtomsCore\n"
"import Atoms\n"
"import imath\n"
"\n"
"class "+ agentType +"Cached(
Atoms.SimulationEvent):\n"
"\tagentTypeFile = r'" + agentTypePath + "'\n"
"\teventName = '" + agentType + "Cached'\n"
"\tagentTypeName = '+ agentType + '\n"
"\tgeoPath = ''\n"
"\tskinPath = ''\n"
"\tstateMachine = ''\n"
"\n"
"\tdef __init__(self):\n"
"\t\tAtoms.SimulationEvent.__init__(self)\n"
"\t\tself.setName(self.eventName)\n"
"\n"
"\tdef load(self):\n"
"\t\taType =
Atoms.AgentType()\n"
"\t\tif aTypeArchive.readFromFile(self.agentTypeFile):\n"
"\t\t\taType.deserialise(aTypeArchive)\n"
"\t\telse:\n"
"\t\t\treturn\n"
"\n"
"\t\tif self.geoPath:\n"
"\t\t\tif typeArchive.readFromFile(self.geoPath):\n"
"\t\t\t\tmeshMap.deserialise(typeArchive)\n"
"\t\t\t\taType.metadata()["lowGeo"] = meshMap\n"
"\n"
"\t\tif self.skinPath:\n"
"\t\t\tif skinArchive.readFromFile(self.skinPath):\n"
"\t\t\t\tskinMap.deserialise(skinArchive)\n"
"\t\t\t\taType.metadata()["skinGeo"] = skinMap\n"
"\n"
"\t\taType.metadata()["stateMachine"] =
AtomsCore.StringMetadata(self.stateMachine)\n"
"\n"
"\t\tAtoms.AgentTypes.instance().addAgentType(self.agentTypeName, aType)\n";
std::string agentTypeWrapperPath = cachePath + "/agenTypes/" + agentType + ".py";
std::ofstream out(agentTypeWrapperPath);
out << pythonEvent;
out.close();
}
AtomsCore::IntArrayMetadata agentIdsMeta;
std::vector<int>& agentIdsVec = agentIdsMeta.get();
agentIdsVec.reserve(cacheIdMap.size());
for (auto it=cacheIdMap.begin(); it != cacheIdMap.end(); it++)
{
agentIdsVec.pushBack(it->second);
}
std::sort(agentIdsVec.begin(), agentIdsVec.end());
AtomsCore::MapMetadata cacheHeader;
cacheHeader.addEntry("startFrame", &AtomsCore::IntMetadata(startFrame));
cacheHeader.addEntry("endFrame", &AtomsCore::IntMetadata(endFrame));
cacheHeader.addEntry("cacheType", &AtomsCore::StringMetadata("dynamic"));
cacheHeader.addEntry("agentIds", &agentIdsMeta);
AtomsCore namespace.
Definition: Agent.h:344
Atoms namespace.
Definition: Agent.h:29
Reading an Atoms cache
Since the Atoms cache is made of different serialized MapMetadata you can read them using the atoms serialization system.
If you are going to query multiple times different cache frames during a simulation, you can use the cache manager to increase the performance.