diff --git a/samples/gamepad/gamepad.py b/samples/gamepad/gamepad.py index 82154838f6..c684c01253 100644 --- a/samples/gamepad/gamepad.py +++ b/samples/gamepad/gamepad.py @@ -9,21 +9,51 @@ move the camera where the right stick will rotate the camera. from direct.showbase.ShowBase import ShowBase from panda3d.core import TextNode, InputDevice, loadPrcFileData, Vec3 +from panda3d.core import TextPropertiesManager from direct.gui.OnscreenText import OnscreenText -loadPrcFileData("", "notify-level-device debug") +loadPrcFileData("", """ + default-fov 60 + notify-level-device debug +""") + +# Informational text in the bottom-left corner. We use the special \5 +# character to embed an image representing the gamepad buttons. +INFO_TEXT = """Move \5lstick\5 to strafe, \5rstick\5 to turn +Press \5ltrigger\5 and \5rtrigger\5 to go down/up +Press \5face_x\5 to reset camera""" + class App(ShowBase): def __init__(self): ShowBase.__init__(self) - # print all events sent through the messenger - self.messenger.toggleVerbose() + # Print all events sent through the messenger + #self.messenger.toggleVerbose() + + # Load the graphics for the gamepad buttons and register them, so that + # we can embed them in our information text. + graphics = loader.loadModel("models/xbone-icons.egg") + mgr = TextPropertiesManager.getGlobalPtr() + for name in ["face_a", "face_b", "face_x", "face_y", "ltrigger", "rtrigger", "lstick", "rstick"]: + graphic = graphics.find("**/" + name) + graphic.setScale(1.5) + mgr.setGraphic(name, graphic) + graphic.setZ(-0.5) + + # Show the informational text in the corner. + self.lblInfo = OnscreenText( + parent = self.a2dBottomLeft, + pos = (0.1, 0.3), + fg = (1, 1, 1, 1), + bg = (0.2, 0.2, 0.2, 0.9), + align = TextNode.A_left, + text = INFO_TEXT) + self.lblInfo.textNode.setCardAsMargin(0.5, 0.5, 0.5, 0.2) self.lblWarning = OnscreenText( text = "No devices found", fg=(1,0,0,1), scale = .25) - self.lblWarning.hide() self.lblAction = OnscreenText( text = "Action", @@ -31,99 +61,133 @@ class App(ShowBase): scale = .15) self.lblAction.hide() - self.checkDevices() + # Is there a gamepad connected? + self.gamepad = None + devices = self.devices.getDevices(InputDevice.DC_gamepad) + if devices: + self.connect(devices[0]) # Accept device dis-/connection events - # NOTE: catching the events here will overwrite the accept in showbase, hence - # we need to forward the event in the functions we set here! self.accept("connect-device", self.connect) self.accept("disconnect-device", self.disconnect) self.accept("escape", exit) - self.accept("gamepad0-start", exit) - self.accept("flight_stick0-start", exit) # Accept button events of the first connected gamepad - self.accept("gamepad0-action_a", self.doAction, extraArgs=[True, "Action"]) - self.accept("gamepad0-action_a-up", self.doAction, extraArgs=[False, "Release"]) - self.accept("gamepad0-action_b", self.doAction, extraArgs=[True, "Action 2"]) - self.accept("gamepad0-action_b-up", self.doAction, extraArgs=[False, "Release"]) + self.accept("gamepad-back", exit) + self.accept("gamepad-start", exit) + self.accept("gamepad-face_x", self.reset) + self.accept("gamepad-face_a", self.action, extraArgs=["face_a"]) + self.accept("gamepad-face_a-up", self.actionUp) + self.accept("gamepad-face_b", self.action, extraArgs=["face_b"]) + self.accept("gamepad-face_b-up", self.actionUp) + self.accept("gamepad-face_y", self.action, extraArgs=["face_y"]) + self.accept("gamepad-face_y-up", self.actionUp) self.environment = loader.loadModel("environment") self.environment.reparentTo(render) - # disable pandas default mouse-camera controls so we can handle the camera - # movements by ourself + # Disable the default mouse-camera controls since we need to handle + # our own camera controls. self.disableMouse() - - # list of connected gamepad devices - gamepads = base.devices.getDevices(InputDevice.DC_gamepad) - - # set the center position of the control sticks - # NOTE: here we assume, that the wheel is centered when the application get started. - # In real world applications, you should notice the user and give him enough time - # to center the wheel until you store the center position of the controler! - self.lxcenter = gamepads[0].findControl(InputDevice.C_left_x).state - self.lycenter = gamepads[0].findControl(InputDevice.C_left_y).state - self.rxcenter = gamepads[0].findControl(InputDevice.C_right_x).state - self.rycenter = gamepads[0].findControl(InputDevice.C_right_y).state - + self.reset() self.taskMgr.add(self.moveTask, "movement update task") def connect(self, device): - # we need to forward the event to the connectDevice function of showbase - self.connectDevice(device) - # Now we can check for ourself - self.checkDevices() + """Event handler that is called when a device is discovered.""" + + # We're only interested if this is a gamepad and we don't have a + # gamepad yet. + if device.device_class == InputDevice.DC_gamepad and not self.gamepad: + print("Found %s" % (device)) + self.gamepad = device + + # Enable this device to ShowBase so that we can receive events. + # We set up the events with a prefix of "gamepad-". + self.attachInputDevice(device, prefix="gamepad") + + # Hide the warning that we have no devices. + self.lblWarning.hide() def disconnect(self, device): - # we need to forward the event to the disconnectDevice function of showbase - self.disconnectDevice(device) - # Now we can check for ourself - self.checkDevices() + """Event handler that is called when a device is removed.""" - def checkDevices(self): - # check if we have gamepad devices connected - if self.devices.get_devices(InputDevice.DC_gamepad): - # we have at least one gamepad device - self.lblWarning.hide() + if self.gamepad != device: + # We don't care since it's not our gamepad. + return + + # Tell ShowBase that the device is no longer needed. + print("Disconnected %s" % (device)) + self.detachInputDevice(device) + self.gamepad = None + + # Do we have any other gamepads? Attach the first other gamepad. + devices = self.devices.getDevices(InputDevice.DC_gamepad) + if devices: + self.connect(devices[0]) else: - # no devices connected + # No devices. Show the warning. self.lblWarning.show() - def doAction(self, showText, text): - if showText and self.lblAction.isHidden(): - self.lblAction.show() - else: - self.lblAction.hide() + def reset(self): + """Reset the camera to the initial position.""" + + self.camera.setPosHpr(0, -200, 10, 0, 0, 0) + + def action(self, button): + # Just show which button has been pressed. + self.lblAction.text = "Pressed \5%s\5" % button + self.lblAction.show() + + def actionUp(self): + # Hide the label showing which button is pressed. + self.lblAction.hide() def moveTask(self, task): dt = globalClock.getDt() - movementVec = Vec3() - gamepads = base.devices.getDevices(InputDevice.DC_gamepad) - if len(gamepads) == 0: - # savety check + if not self.gamepad: return task.cont + strafe_speed = 85 + vert_speed = 50 + turn_speed = 100 + + # If the left stick is pressed, we will go faster. + lstick = self.gamepad.findButton("lstick") + if lstick.pressed: + strafe_speed *= 2.0 + # we will use the first found gamepad # Move the camera left/right - left_x = gamepads[0].findControl(InputDevice.C_left_x) - movementVec.setX(left_x.state - self.lxcenter) - # Move the camera forward/backward - left_y = gamepads[0].findControl(InputDevice.C_left_y) - movementVec.setY(left_y.state - self.lycenter) - # Control the cameras heading - right_x = gamepads[0].findControl(InputDevice.C_right_x) - base.camera.setH(base.camera, 100 * dt * (right_x.state - self.rxcenter)) - # Control the cameras pitch - right_y = gamepads[0].findControl(InputDevice.C_right_y) - base.camera.setP(base.camera, 100 * dt * (right_y.state - self.rycenter)) + strafe = Vec3(0) + left_x = self.gamepad.findAxis(InputDevice.Axis.left_x) + left_y = self.gamepad.findAxis(InputDevice.Axis.left_y) + strafe.x = left_x.value + strafe.y = left_y.value - # calculate movement - base.camera.setX(base.camera, 100 * dt * movementVec.getX()) - base.camera.setY(base.camera, 100 * dt * movementVec.getY()) + # Apply some deadzone, since the sticks don't center exactly at 0 + if strafe.lengthSquared() >= 0.01: + self.camera.setPos(self.camera, strafe * strafe_speed * dt) + + # Use the triggers for the vertical position. + trigger_l = self.gamepad.findAxis(InputDevice.Axis.left_trigger) + trigger_r = self.gamepad.findAxis(InputDevice.Axis.right_trigger) + lift = trigger_r.value - trigger_l.value + self.camera.setZ(self.camera.getZ() + (lift * vert_speed * dt)) + + # Move the camera forward/backward + right_x = self.gamepad.findAxis(InputDevice.Axis.right_x) + right_y = self.gamepad.findAxis(InputDevice.Axis.right_y) + + # Again, some deadzone + if abs(right_x.value) >= 0.1 or abs(right_y.value) >= 0.1: + self.camera.setH(self.camera, turn_speed * dt * -right_x.value) + self.camera.setP(self.camera, turn_speed * dt * right_y.value) + + # Reset the roll so that the camera remains upright. + self.camera.setR(0) return task.cont diff --git a/samples/gamepad/mappingGUI.py b/samples/gamepad/mappingGUI.py index ce9234bd1d..84f26a9f60 100644 --- a/samples/gamepad/mappingGUI.py +++ b/samples/gamepad/mappingGUI.py @@ -16,62 +16,75 @@ from panda3d.core import ( TextNode, Vec2, InputDevice, - loadPrcFileData) + loadPrcFileData, + GamepadButton, + KeyboardButton) # Make sure the textures look crisp on every device that supports # non-power-2 textures loadPrcFileData("", "textures-auto-power-2 #t") -class App(ShowBase): +# How much an axis should have moved for it to register as a movement. +DEAD_ZONE = 0.33 + + +class InputMapping(object): + """A container class for storing a mapping from a string action to either + an axis or a button. You could extend this with additional methods to load + the default mappings from a configuration file. """ + + # Define all the possible actions. + actions = ( + "Move forward", "Move backward", "Move left", "Move right", "Jump", + "Buy", "Use", "Break", "Fix", "Trash", "Change", "Mail", "Upgrade", + ) + def __init__(self): - ShowBase.__init__(self) + self.__map = dict.fromkeys(self.actions) - self.setBackgroundColor(0, 0, 0) - # make the font look nice at a big scale - DGG.getDefaultFont().setPixelsPerUnit(100) + def mapButton(self, action, button): + self.__map[action] = ("button", str(button)) - # a dict of actions and button/axis events - self.gamepadMapping = { - "Move forward":"Left Stick Y", - "Move backward":"Left Stick Y", - "Move left":"Left Stick X", - "Move right":"Left Stick X", - "Jump":"a", - "Action":"b", - "Sprint":"x", - "Map":"y", - "action-1":"c", - "action-2":"d", - "action-3":"e", - "action-4":"f", - "action-5":"g", - "action-6":"h", - "action-7":"i", - "action-8":"j", - "action-9":"k", - "action-10":"l", - "action-11":"m", - } - # this will store the action that we want to remap - self.actionToMap = "" - # this will store the key/axis that we want to asign to an action - self.newActionKey = "" - # this will store the label that needs to be actualized in the list - self.actualizeLabel = None + def mapAxis(self, action, axis): + self.__map[action] = ("axis", axis.name) - # The geometry for our basic buttons - maps = loader.loadModel("models/button_map") - self.buttonGeom = ( - maps.find("**/ready"), - maps.find("**/click"), - maps.find("**/hover"), - maps.find("**/disabled")) + def unmap(self): + self.__map[action] = None - # Create the dialog that asks the user for input on a given - # action to map a key to. - DGG.setDefaultDialogGeom("models/dialog.png") - # setup a dialog to ask for device input - self.dlgInput = OkCancelDialog( + def formatMapping(self, action): + """Returns a string label describing the mapping for a given action, + for displaying in a GUI. """ + mapping = self.__map.get(action) + if not mapping: + return "Unmapped" + + # Format the symbolic string from Panda nicely. In a real-world game, + # you might want to look these up in a translation table, or so. + label = mapping[1].replace('_', ' ').title() + if mapping[0] == "axis": + return "Axis: " + label + else: + return "Button: " + label + + +class ChangeActionDialog(object): + """Encapsulates the UI dialog that opens up when changing a mapping. It + holds the state of which action is being set and which button is pressed + and invokes a callback function when the dialog is exited.""" + + def __init__(self, action, button_geom, command): + # This stores which action we are remapping. + self.action = action + + # This will store the key/axis that we want to asign to an action + self.newInputType = "" + self.newInput = "" + self.setKeyCalled = False + + self.__command = command + + # Initialize the DirectGUI stuff. + self.dialog = OkCancelDialog( dialogName="dlg_device_input", pos=(0, 0, 0.25), text="Hit desired key:", @@ -82,7 +95,7 @@ class App(ShowBase): text_align=TextNode.ACenter, fadeScreen=0.65, frameColor=VBase4(0.3, 0.3, 0.3, 1), - button_geom=self.buttonGeom, + button_geom=button_geom, button_scale=0.15, button_text_scale=0.35, button_text_align=TextNode.ALeft, @@ -93,13 +106,73 @@ class App(ShowBase): button_frameColor=VBase4(0, 0, 0, 0), button_frameSize=VBase4(-1.0, 1.0, -0.25, 0.25), button_pressEffect=False, - command=self.closeDialog) - self.dlgInput.setTransparency(True) - self.dlgInput.configureDialog() - scale = self.dlgInput["image_scale"] - self.dlgInput["image_scale"] = (scale[0]/2.0, scale[1], scale[2]/2.0) - self.dlgInput["text_pos"] = (self.dlgInput["text_pos"][0], self.dlgInput["text_pos"][1] + 0.06) - self.dlgInput.hide() + command=self.onClose) + self.dialog.setTransparency(True) + self.dialog.configureDialog() + scale = self.dialog["image_scale"] + self.dialog["image_scale"] = (scale[0]/2.0, scale[1], scale[2]/2.0) + self.dialog["text_pos"] = (self.dialog["text_pos"][0], self.dialog["text_pos"][1] + 0.06) + + def buttonPressed(self, button): + if any(button.guiItem.getState() == 1 for button in self.dialog.buttonList): + # Ignore events while any of the dialog buttons are active, because + # otherwise we register mouse clicks when the user is trying to + # exit the dialog. + return + + text = str(button).replace('_', ' ').title() + self.dialog["text"] = "New event will be:\n\nButton: " + text + self.newInputType = "button" + self.newInput = button + + def axisMoved(self, axis): + text = axis.name.replace('_', ' ').title() + self.dialog["text"] = "New event will be:\n\nAxis: " + text + self.newInputType = "axis" + self.newInput = axis + + def onClose(self, result): + """Called when the OK or Cancel button is pressed.""" + self.dialog.cleanup() + + # Call the constructor-supplied callback with our new setting, if any. + if self.newInput and result == DGG.DIALOG_OK: + self.__command(self.action, self.newInputType, self.newInput) + else: + # Cancel (or no input was pressed) + self.__command(self.action, None, None) + + +class MappingGUIDemo(ShowBase): + def __init__(self): + ShowBase.__init__(self) + + self.setBackgroundColor(0, 0, 0) + # make the font look nice at a big scale + DGG.getDefaultFont().setPixelsPerUnit(100) + + # Store our mapping, with some sensible defaults. In a real game, you + # will want to load these from a configuration file. + self.mapping = InputMapping() + self.mapping.mapAxis("Move forward", InputDevice.Axis.left_y) + self.mapping.mapAxis("Move backward", InputDevice.Axis.left_y) + self.mapping.mapAxis("Move left", InputDevice.Axis.left_x) + self.mapping.mapAxis("Move right", InputDevice.Axis.left_x) + self.mapping.mapButton("Jump", GamepadButton.face_a()) + self.mapping.mapButton("Use", GamepadButton.face_b()) + self.mapping.mapButton("Break", GamepadButton.face_x()) + self.mapping.mapButton("Fix", GamepadButton.face_y()) + + # The geometry for our basic buttons + maps = loader.loadModel("models/button_map") + self.buttonGeom = ( + maps.find("**/ready"), + maps.find("**/click"), + maps.find("**/hover"), + maps.find("**/disabled")) + + # Change the default dialog skin. + DGG.setDefaultDialogGeom("models/dialog.png") # create a sample title self.textscale = 0.1 @@ -135,6 +208,7 @@ class App(ShowBase): decMaps.find("**/dec_click"), decMaps.find("**/dec_hover"), decMaps.find("**/dec_disabled")) + # create the scrolled frame that will hold our list self.lstActionMap = DirectScrolledFrame( # make the frame occupy the whole window @@ -167,135 +241,116 @@ class App(ShowBase): idx = 0 self.listBGEven = base.loader.loadModel("models/list_item_even") self.listBGOdd = base.loader.loadModel("models/list_item_odd") - for key, value in self.gamepadMapping.items(): - item = self.__makeListItem(key, key, value, idx) + self.actionLabels = {} + for action in self.mapping.actions: + mapped = self.mapping.formatMapping(action) + item = self.__makeListItem(action, mapped, idx) item.reparentTo(self.lstActionMap.getCanvas()) idx += 1 # recalculate the canvas size to set scrollbars if necesary self.lstActionMap["canvasSize"] = ( base.a2dLeft+0.05, base.a2dRight-0.05, - -(len(self.gamepadMapping.keys())*0.1), 0.09) + -(len(self.mapping.actions)*0.1), 0.09) self.lstActionMap.setCanvasSize() - def closeDialog(self, result): - self.dlgInput.hide() - if result == DGG.DIALOG_OK: + def closeDialog(self, action, newInputType, newInput): + """Called in callback when the dialog is closed. newInputType will be + "button" or "axis", or None if the remapping was cancelled.""" + + self.dlgInput = None + + if newInputType is not None: # map the event to the given action - self.gamepadMapping[self.actionToMap] = self.newActionKey + if newInputType == "axis": + self.mapping.mapAxis(action, newInput) + else: + self.mapping.mapButton(action, newInput) + # actualize the label in the list that shows the current # event for the action - self.actualizeLabel["text"] = self.newActionKey + self.actionLabels[action]["text"] = self.mapping.formatMapping(action) # cleanup - self.dlgInput["text"] ="Hit desired key:" - self.actionToMap = "" - self.newActionKey = "" - self.actualizeLabel = None for bt in base.buttonThrowers: + bt.node().setSpecificFlag(True) bt.node().setButtonDownEvent("") for bt in base.deviceButtonThrowers: + bt.node().setSpecificFlag(True) bt.node().setButtonDownEvent("") taskMgr.remove("checkControls") - def changeMapping(self, action, label): - # set the action that we want to map a new key to - self.actionToMap = action - # set the label that needs to be actualized - self.actualizeLabel = label - # show our dialog - self.dlgInput.show() + # Now detach all the input devices. + for device in self.attachedDevices: + base.detachInputDevice(device) + self.attachedDevices.clear() - # catch all button events + def changeMapping(self, action): + # Create the dialog window + self.dlgInput = ChangeActionDialog(action, button_geom=self.buttonGeom, command=self.closeDialog) + + # Attach all input devices. + devices = base.devices.getDevices() + for device in devices: + base.attachInputDevice(device) + self.attachedDevices = devices + + # Disable regular button events on all button event throwers, and + # instead broadcast a generic event. for bt in base.buttonThrowers: + bt.node().setSpecificFlag(False) bt.node().setButtonDownEvent("keyListenEvent") for bt in base.deviceButtonThrowers: + bt.node().setSpecificFlag(False) bt.node().setButtonDownEvent("deviceListenEvent") - self.setKeyCalled = False - self.accept("keyListenEvent", self.setKey) - self.accept("deviceListenEvent", self.setDeviceKey) - # As there are no events thrown for control changes, we set up - # a task to check if the controls got moved - # This list will help us for checking which controls got moved - self.controlStates = {None:{}} + self.accept("keyListenEvent", self.dlgInput.buttonPressed) + self.accept("deviceListenEvent", self.dlgInput.buttonPressed) + + # As there are no events thrown for control changes, we set up a task + # to check if the controls were moved + # This list will help us for checking which controls were moved + self.axisStates = {None: {}} # fill it with all available controls - for device in base.devices.get_devices(): - for ctrl in device.controls: - if device not in self.controlStates.keys(): - self.controlStates.update({device: {ctrl.axis: ctrl.state}}) + for device in devices: + for axis in device.axes: + if device not in self.axisStates.keys(): + self.axisStates.update({device: {axis.axis: axis.value}}) else: - self.controlStates[device].update({ctrl.axis: ctrl.state}) + self.axisStates[device].update({axis.axis: axis.value}) # start the task taskMgr.add(self.watchControls, "checkControls") def watchControls(self, task): # move through all devices and all it's controls - for device in base.devices.get_devices(): - for ctrl in device.controls: - # if a control got changed more than the given puffer zone - if self.controlStates[device][ctrl.axis] + 0.2 < ctrl.state or \ - self.controlStates[device][ctrl.axis] - 0.2 > ctrl.state: + for device in self.attachedDevices: + if device.device_class == InputDevice.DC_mouse: + # Ignore mouse axis movement, or the user can't even navigate + # to the OK/Cancel buttons! + continue + + for axis in device.axes: + # if a control got changed more than the given dead zone + if self.axisStates[device][axis.axis] + DEAD_ZONE < axis.value or \ + self.axisStates[device][axis.axis] - DEAD_ZONE > axis.value: # set the current state in the dict - self.controlStates[device][ctrl.axis] = ctrl.state - # check which axis got moved - if ctrl.axis == InputDevice.C_left_x: - self.setKey("Left Stick X") - elif ctrl.axis == InputDevice.C_left_y: - self.setKey("Left Stick Y") - elif ctrl.axis == InputDevice.C_left_trigger: - self.setKey("Left Trigger") - elif ctrl.axis == InputDevice.C_right_x: - self.setKey("Right Stick X") - elif ctrl.axis == InputDevice.C_right_y: - self.setKey("Right Stick Y") - elif ctrl.axis == InputDevice.C_right_trigger: - self.setKey("Right Trigger") - elif ctrl.axis == InputDevice.C_x: - self.setKey("X") - elif ctrl.axis == InputDevice.C_y: - self.setKey("Y") - elif ctrl.axis == InputDevice.C_trigger: - self.setKey("Trigger") - elif ctrl.axis == InputDevice.C_throttle: - self.setKey("Throttle") - elif ctrl.axis == InputDevice.C_rudder: - self.setKey("Rudder") - elif ctrl.axis == InputDevice.C_hat_x: - self.setKey("Hat X") - elif ctrl.axis == InputDevice.C_hat_y: - self.setKey("Hat Y") - elif ctrl.axis == InputDevice.C_wheel: - self.setKey("Wheel") - elif ctrl.axis == InputDevice.C_accelerator: - self.setKey("Acclerator") - elif ctrl.axis == InputDevice.C_brake: - self.setKey("Break") + self.axisStates[device][axis.axis] = axis.value + + # Format the axis for being displayed. + if axis.axis != InputDevice.Axis.none: + #label = axis.axis.name.replace('_', ' ').title() + self.dlgInput.axisMoved(axis.axis) + return task.cont - def setKey(self, args): - self.setKeyCalled = True - if self.dlgInput.buttonList[0].guiItem.getState() == 1: - # this occurs if the OK button was clicked. To prevent to - # always set the mouse1 event whenever the OK button was - # pressed, we instantly return from this function - return - self.dlgInput["text"] = "New event will be:\n\n" + args - self.newActionKey = args - - def setDeviceKey(self, args): - if not self.setKeyCalled: - self.setKey(args) - self.setKeyCalled = False - - def __makeListItem(self, itemName, action, event, index): + def __makeListItem(self, action, event, index): def dummy(): pass if index % 2 == 0: bg = self.listBGEven else: bg = self.listBGOdd item = DirectFrame( - text=itemName, + text=action, geom=bg, geom_scale=(base.a2dRight-0.05, 1, 0.1), frameSize=VBase4(base.a2dLeft+0.05, base.a2dRight-0.05, -0.05, 0.05), @@ -317,6 +372,8 @@ class App(ShowBase): ) lbl.reparentTo(item) lbl.setTransparency(True) + self.actionLabels[action] = lbl + buttonScale = 0.15 btn = DirectButton( text="Change", @@ -333,10 +390,10 @@ class App(ShowBase): pos=(base.a2dRight-(0.898*buttonScale+0.3), 0, 0), pressEffect=False, command=self.changeMapping, - extraArgs=[action, lbl]) + extraArgs=[action]) btn.setTransparency(True) btn.reparentTo(item) return item -app = App() +app = MappingGUIDemo() app.run() diff --git a/samples/gamepad/models/xbone-icons.egg b/samples/gamepad/models/xbone-icons.egg new file mode 100644 index 0000000000..cc2fb417ad --- /dev/null +++ b/samples/gamepad/models/xbone-icons.egg @@ -0,0 +1,384 @@ + { Y-Up } + + xbone-icons { + xbone-icons.png + format { rgba } + alpha { dual } +} + + vpool { + 0 { + -0.5 0.5 0 + { 0.00390625 0.9921875 } + } + 1 { + -0.5 -0.5 0 + { 0.00390625 0.7578125 } + } + 2 { + 0.5 -0.5 0 + { 0.12109375 0.7578125 } + } + 3 { + 0.5 0.5 0 + { 0.12109375 0.9921875 } + } + 4 { + -0.5 0.5 0 + { 0.12890625 0.9921875 } + } + 5 { + -0.5 -0.5 0 + { 0.12890625 0.7578125 } + } + 6 { + 0.5 -0.5 0 + { 0.24609375 0.7578125 } + } + 7 { + 0.5 0.5 0 + { 0.24609375 0.9921875 } + } + 8 { + -0.5 0.5 0 + { 0.25390625 0.9921875 } + } + 9 { + -0.5 -0.5 0 + { 0.25390625 0.7578125 } + } + 10 { + 0.5 -0.5 0 + { 0.37109375 0.7578125 } + } + 11 { + 0.5 0.5 0 + { 0.37109375 0.9921875 } + } + 12 { + -0.5 0.5 0 + { 0.37890625 0.9921875 } + } + 13 { + -0.5 -0.5 0 + { 0.37890625 0.7578125 } + } + 14 { + 0.5 -0.5 0 + { 0.49609375 0.7578125 } + } + 15 { + 0.5 0.5 0 + { 0.49609375 0.9921875 } + } + 16 { + -0.5 0.5 0 + { 0.50390625 0.9921875 } + } + 17 { + -0.5 -0.5 0 + { 0.50390625 0.7578125 } + } + 18 { + 0.5 -0.5 0 + { 0.62109375 0.7578125 } + } + 19 { + 0.5 0.5 0 + { 0.62109375 0.9921875 } + } + 20 { + -0.5 0.5 0 + { 0.62890625 0.9921875 } + } + 21 { + -0.5 -0.5 0 + { 0.62890625 0.7578125 } + } + 22 { + 0.5 -0.5 0 + { 0.74609375 0.7578125 } + } + 23 { + 0.5 0.5 0 + { 0.74609375 0.9921875 } + } + 24 { + -0.5 0.5 0 + { 0.75390625 0.9921875 } + } + 25 { + -0.5 -0.5 0 + { 0.75390625 0.7578125 } + } + 26 { + 0.5 -0.5 0 + { 0.87109375 0.7578125 } + } + 27 { + 0.5 0.5 0 + { 0.87109375 0.9921875 } + } + 28 { + -0.5 0.5 0 + { 0.87890625 0.9921875 } + } + 29 { + -0.5 -0.5 0 + { 0.87890625 0.7578125 } + } + 30 { + 0.5 -0.5 0 + { 0.99609375 0.7578125 } + } + 31 { + 0.5 0.5 0 + { 0.99609375 0.9921875 } + } + 32 { + -0.5 0.5 0 + { 0.12890625 0.7421875 } + } + 33 { + -0.5 -0.5 0 + { 0.12890625 0.5078125 } + } + 34 { + 0.5 -0.5 0 + { 0.24609375 0.5078125 } + } + 35 { + 0.5 0.5 0 + { 0.24609375 0.7421875 } + } + 36 { + -0.5 0.5 0 + { 0.00390625 0.7421875 } + } + 37 { + -0.5 -0.5 0 + { 0.00390625 0.5078125 } + } + 38 { + 0.5 -0.5 0 + { 0.12109375 0.5078125 } + } + 39 { + 0.5 0.5 0 + { 0.12109375 0.7421875 } + } + 40 { + -0.5 0.5 0 + { 0.25390625 0.7421875 } + } + 41 { + -0.5 -0.5 0 + { 0.25390625 0.5078125 } + } + 42 { + 0.5 -0.5 0 + { 0.37109375 0.5078125 } + } + 43 { + 0.5 0.5 0 + { 0.37109375 0.7421875 } + } + 44 { + -0.5 0.5 0 + { 0.37890625 0.7421875 } + } + 45 { + -0.5 -0.5 0 + { 0.37890625 0.5078125 } + } + 46 { + 0.5 -0.5 0 + { 0.49609375 0.5078125 } + } + 47 { + 0.5 0.5 0 + { 0.49609375 0.7421875 } + } + 48 { + -0.5 0.5 0 + { 0.62890625 0.7421875 } + } + 49 { + -0.5 -0.5 0 + { 0.62890625 0.5078125 } + } + 50 { + 0.5 -0.5 0 + { 0.74609375 0.5078125 } + } + 51 { + 0.5 0.5 0 + { 0.74609375 0.7421875 } + } + 52 { + -0.5 0.5 0 + { 0.50390625 0.7421875 } + } + 53 { + -0.5 -0.5 0 + { 0.50390625 0.5078125 } + } + 54 { + 0.5 -0.5 0 + { 0.62109375 0.5078125 } + } + 55 { + 0.5 0.5 0 + { 0.62109375 0.7421875 } + } + 56 { + -0.5 0.5 0 + { 0.75390625 0.7421875 } + } + 57 { + -0.5 -0.5 0 + { 0.75390625 0.5078125 } + } + 58 { + 0.5 -0.5 0 + { 0.87109375 0.5078125 } + } + 59 { + 0.5 0.5 0 + { 0.87109375 0.7421875 } + } + 60 { + -0.5 0.5 0 + { 0.87890625 0.7421875 } + } + 61 { + -0.5 -0.5 0 + { 0.87890625 0.5078125 } + } + 62 { + 0.5 -0.5 0 + { 0.99609375 0.5078125 } + } + 63 { + 0.5 0.5 0 + { 0.99609375 0.7421875 } + } + 64 { + -0.5 0.5 0 + { 0.00390625 0.4921875 } + } + 65 { + -0.5 -0.5 0 + { 0.00390625 0.2578125 } + } + 66 { + 0.5 -0.5 0 + { 0.12109375 0.2578125 } + } + 67 { + 0.5 0.5 0 + { 0.12109375 0.4921875 } + } +} + face_a { + { + { xbone-icons } + { 0 1 2 3 { vpool } } + } +} + face_b { + { + { xbone-icons } + { 4 5 6 7 { vpool } } + } +} + dpad { + { + { xbone-icons } + { 8 9 10 11 { vpool } } + } +} + dpad_down { + { + { xbone-icons } + { 12 13 14 15 { vpool } } + } +} + dpad_left { + { + { xbone-icons } + { 16 17 18 19 { vpool } } + } +} + dpad_right { + { + { xbone-icons } + { 20 21 22 23 { vpool } } + } +} + dpad_up { + { + { xbone-icons } + { 24 25 26 27 { vpool } } + } +} + XboxOne_LB { + { + { xbone-icons } + { 28 29 30 31 { vpool } } + } +} + lstick { + { + { xbone-icons } + { 32 33 34 35 { vpool } } + } +} + ltrigger { + { + { xbone-icons } + { 36 37 38 39 { vpool } } + } +} + start { + { + { xbone-icons } + { 40 41 42 43 { vpool } } + } +} + rshoulder { + { + { xbone-icons } + { 44 45 46 47 { vpool } } + } +} + rstick { + { + { xbone-icons } + { 48 49 50 51 { vpool } } + } +} + rtrigger { + { + { xbone-icons } + { 52 53 54 55 { vpool } } + } +} + back { + { + { xbone-icons } + { 56 57 58 59 { vpool } } + } +} + face_x { + { + { xbone-icons } + { 60 61 62 63 { vpool } } + } +} + face_y { + { + { xbone-icons } + { 64 65 66 67 { vpool } } + } +} diff --git a/samples/gamepad/models/xbone-icons.png b/samples/gamepad/models/xbone-icons.png new file mode 100644 index 0000000000..e8fd66b487 Binary files /dev/null and b/samples/gamepad/models/xbone-icons.png differ