Welcome to Atoms Crowd blog! In this blog we are going to post some interesting articles to show you how you can work with Atoms.
For our first post, we are going to show you a fun hack: how to drive your agents with an XBox controller.
If you are not a technical user you can ignore the last section, download the material provided below and play with them in Maya 2017:
- the Xbox controller module.
- the Xbox module recorder.
- the xbox.pyd file necessary for capturing the XBox controller input. Please download it and copy it somewhere in your python path. The pyd file is compiled for Maya 2017.
- If you want to have a look here is the visual studio project for generating the xbox.pyd
How to run this example.
- Copy the xbox.pyd inside your python path. (i.e. <drive>:\Documents and Settings\<username>\My Documents\maya\<Version>\scripts )
- Open the Atoms UI, create a new behaviour module and copy the content of the Xbox controller module in the editor. Click register.
- Create a new agent group, add a gridLayout module, a stateMachine module and the XBoxControllerModule .
- Set the time line frame range to 1 - 2000 .
- Copy the content of the Xbox module recorder in the script editor and run it.
- Click "Start".
- Use the Xbox controller to drive the agent.
- Click "Stop".
How does it work?
First of all, if you were doing all this by yourself you would need some C++ knowledge to capture the Xbox controller input and make it available in python. Luckily this has already been taken care of and you can access that information with the xbox.pyd module. If you are brave enough you can also have a look at the given Visual Studio project.
There are two other major components we are going to have a look now.
The XBoxControllerModule
This is the Atoms module which does all the work and caches the actions run the by the user with the XBox controller.
import Atoms import AtomsCore import AtomsMath import os import xbox import maya class XBoxControllerModule(Atoms.BehaviourModule): def __init__(self): Atoms.BehaviourModule.__init__(self) # Adding the module metadatas self.addAttribute("cachedAngle", AtomsCore.Vector3Metadata(AtomsMath.V3d(0.0,0.0,0.0))) self.addAttribute("cachedState", AtomsCore.IntMetadata(0)) self.addAttribute("record", AtomsCore.BoolMetadata(False)) self.addAttribute("turnAngle", AtomsCore.DoubleMetadata(3.0)) # initializing the class members self.state = 0 self.old_state = 0 self.old_dir = [0.0,0.0,0.0] # lists for caching the values self.stateKeys=[] self.angleKeys=[] def initSimulation(self, agentGroup): # Emptying the two lists and setting initial values self.stateKeys=[] self.angleKeys=[] self.angleKeys.append([maya.cmds.currentTime(q=1), [0.0,0.0,0.0]]) self.stateKeys.append([maya.cmds.currentTime(q=1), 0]) def initFrame(self, agents, agentGroup): # getting the module parameters turnAngle = self.attributes()["turnAngle"].value() record = self.attributes()["record"].value() # getting the xbox control state state = xbox.getControlState() angle = state["LX"] buttonA = state["A"] buttonB = state["B"] buttonX = state["X"] # finding the right state depending on the pressed button if buttonA: self.state = 0 if buttonB: self.state = 1 if buttonX: self.state = 2 if record: # if we are recording we cache the values if self.old_dir[0] != state["LX"] or self.old_dir[1] != state["LY"]: self.angleKeys.append([maya.cmds.currentTime(q=1), [state["LX"],state["LY"],0.0]]) if self.state != self.old_state: self.stateKeys.append([maya.cmds.currentTime(q=1), self.state]) self.old_dir = [state["LX"],state["LY"],0.0] self.old_state = self.state else: # otherwise we get the default values from the module parameters angle = self.attributes()["cachedAngle"].value().x self.state = self.attributes()["cachedState"].value() # for each agent changing the state and direction for agent in agents: if self.state: agent.metadata()["state"].set(self.state) dir = agent.metadata()["direction"].value() q = AtomsMath.Quatd() q.setAxisAngle(AtomsMath.V3d(0.0,1.0,0.0), -angle * turnAngle * 3.1456 / 180.0) dir = dir * q.toMatrix44() agent.metadata()["direction"].set(dir) def endSimulation(self, agents, agentGroup): # setting all the keyframes for v in self.angleKeys: maya.cmds.setKeyframe(agentGroup.name() + ".atoms_XBoxControllerModule_cachedAngle.atoms_XBoxControllerModule_cachedAngleX",v=v[1][0],t=[v[0]]) maya.cmds.setKeyframe(agentGroup.name() + ".atoms_XBoxControllerModule_cachedAngle.atoms_XBoxControllerModule_cachedAngleY",v=v[1][1],t=[v[0]]) maya.cmds.setKeyframe(agentGroup.name() + ".atoms_XBoxControllerModule_cachedAngle.atoms_XBoxControllerModule_cachedAngleZ",v=v[1][02],t=[v[0]]) for v in self.stateKeys: maya.cmds.setKeyframe(agentGroup.name() + ".atoms_XBoxControllerModule_cachedState",v=v[1],t=[v[0]])
The Xbox module recorder
This is a simple PySide UI for starting/stopping the recording.
import AtomsMaya import maya from PySide2 import QtWidgets, QtCore, QtGui class XboxControllerModuleWidget(QtWidgets.QWidget): def __init__(self, parent=None): super(XboxControllerModuleWidget, self).__init__(parent) self.ag_name = maya.cmds.ls(sl=1,dag=1,lf=1,shapes=1)[0] self.v_layout = QtWidgets.QVBoxLayout() self.start = QtWidgets.QPushButton("Start") self.stop = QtWidgets.QPushButton("Stop") self.v_layout.addWidget(self.start) self.v_layout.addWidget(self.stop) self.setLayout(self.v_layout) self.start.clicked.connect(self._start) self.stop.clicked.connect(self._stop) self.start.setEnabled(True) self.stop.setEnabled(False) def _start(self): ag_name = self.ag_name maya.cmds.setAttr(ag_name + ".atoms_XBoxControllerModule_record",1) maya.mel.eval("playButtonStart;") maya.mel.eval("playButtonForward;") self.start.setEnabled(False) self.stop.setEnabled(True) def _stop(self): ag_name = self.ag_name ag = AtomsMaya.getAgentGroup(ag_name) mod = ag.behaviourModule("XBoxControllerModule") mod.endSimulation(ag.agents(), ag) maya.cmds.setAttr(ag_name + ".atoms_XBoxControllerModule_record",0) maya.mel.eval("playButtonForward;") self.start.setEnabled(True) self.stop.setEnabled(False) w = XboxControllerModuleWidget() w.show()