mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-01 17:35:34 -04:00
samples: fixes and improvements to gamepad and mappingGUI sample
This commit is contained in:
parent
85752b06f5
commit
c4b3b558c9
@ -9,21 +9,51 @@ move the camera where the right stick will rotate the camera.
|
|||||||
|
|
||||||
from direct.showbase.ShowBase import ShowBase
|
from direct.showbase.ShowBase import ShowBase
|
||||||
from panda3d.core import TextNode, InputDevice, loadPrcFileData, Vec3
|
from panda3d.core import TextNode, InputDevice, loadPrcFileData, Vec3
|
||||||
|
from panda3d.core import TextPropertiesManager
|
||||||
from direct.gui.OnscreenText import OnscreenText
|
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):
|
class App(ShowBase):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
ShowBase.__init__(self)
|
ShowBase.__init__(self)
|
||||||
# print all events sent through the messenger
|
# Print all events sent through the messenger
|
||||||
self.messenger.toggleVerbose()
|
#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(
|
self.lblWarning = OnscreenText(
|
||||||
text = "No devices found",
|
text = "No devices found",
|
||||||
fg=(1,0,0,1),
|
fg=(1,0,0,1),
|
||||||
scale = .25)
|
scale = .25)
|
||||||
self.lblWarning.hide()
|
|
||||||
|
|
||||||
self.lblAction = OnscreenText(
|
self.lblAction = OnscreenText(
|
||||||
text = "Action",
|
text = "Action",
|
||||||
@ -31,99 +61,133 @@ class App(ShowBase):
|
|||||||
scale = .15)
|
scale = .15)
|
||||||
self.lblAction.hide()
|
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
|
# 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("connect-device", self.connect)
|
||||||
self.accept("disconnect-device", self.disconnect)
|
self.accept("disconnect-device", self.disconnect)
|
||||||
|
|
||||||
self.accept("escape", exit)
|
self.accept("escape", exit)
|
||||||
self.accept("gamepad0-start", exit)
|
|
||||||
self.accept("flight_stick0-start", exit)
|
|
||||||
|
|
||||||
# Accept button events of the first connected gamepad
|
# Accept button events of the first connected gamepad
|
||||||
self.accept("gamepad0-action_a", self.doAction, extraArgs=[True, "Action"])
|
self.accept("gamepad-back", exit)
|
||||||
self.accept("gamepad0-action_a-up", self.doAction, extraArgs=[False, "Release"])
|
self.accept("gamepad-start", exit)
|
||||||
self.accept("gamepad0-action_b", self.doAction, extraArgs=[True, "Action 2"])
|
self.accept("gamepad-face_x", self.reset)
|
||||||
self.accept("gamepad0-action_b-up", self.doAction, extraArgs=[False, "Release"])
|
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 = loader.loadModel("environment")
|
||||||
self.environment.reparentTo(render)
|
self.environment.reparentTo(render)
|
||||||
|
|
||||||
# disable pandas default mouse-camera controls so we can handle the camera
|
# Disable the default mouse-camera controls since we need to handle
|
||||||
# movements by ourself
|
# our own camera controls.
|
||||||
self.disableMouse()
|
self.disableMouse()
|
||||||
|
self.reset()
|
||||||
# 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.taskMgr.add(self.moveTask, "movement update task")
|
self.taskMgr.add(self.moveTask, "movement update task")
|
||||||
|
|
||||||
def connect(self, device):
|
def connect(self, device):
|
||||||
# we need to forward the event to the connectDevice function of showbase
|
"""Event handler that is called when a device is discovered."""
|
||||||
self.connectDevice(device)
|
|
||||||
# Now we can check for ourself
|
# We're only interested if this is a gamepad and we don't have a
|
||||||
self.checkDevices()
|
# 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):
|
def disconnect(self, device):
|
||||||
# we need to forward the event to the disconnectDevice function of showbase
|
"""Event handler that is called when a device is removed."""
|
||||||
self.disconnectDevice(device)
|
|
||||||
# Now we can check for ourself
|
|
||||||
self.checkDevices()
|
|
||||||
|
|
||||||
def checkDevices(self):
|
if self.gamepad != device:
|
||||||
# check if we have gamepad devices connected
|
# We don't care since it's not our gamepad.
|
||||||
if self.devices.get_devices(InputDevice.DC_gamepad):
|
return
|
||||||
# we have at least one gamepad device
|
|
||||||
self.lblWarning.hide()
|
# 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:
|
else:
|
||||||
# no devices connected
|
# No devices. Show the warning.
|
||||||
self.lblWarning.show()
|
self.lblWarning.show()
|
||||||
|
|
||||||
def doAction(self, showText, text):
|
def reset(self):
|
||||||
if showText and self.lblAction.isHidden():
|
"""Reset the camera to the initial position."""
|
||||||
self.lblAction.show()
|
|
||||||
else:
|
self.camera.setPosHpr(0, -200, 10, 0, 0, 0)
|
||||||
self.lblAction.hide()
|
|
||||||
|
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):
|
def moveTask(self, task):
|
||||||
dt = globalClock.getDt()
|
dt = globalClock.getDt()
|
||||||
movementVec = Vec3()
|
|
||||||
|
|
||||||
gamepads = base.devices.getDevices(InputDevice.DC_gamepad)
|
if not self.gamepad:
|
||||||
if len(gamepads) == 0:
|
|
||||||
# savety check
|
|
||||||
return task.cont
|
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
|
# we will use the first found gamepad
|
||||||
# Move the camera left/right
|
# Move the camera left/right
|
||||||
left_x = gamepads[0].findControl(InputDevice.C_left_x)
|
strafe = Vec3(0)
|
||||||
movementVec.setX(left_x.state - self.lxcenter)
|
left_x = self.gamepad.findAxis(InputDevice.Axis.left_x)
|
||||||
# Move the camera forward/backward
|
left_y = self.gamepad.findAxis(InputDevice.Axis.left_y)
|
||||||
left_y = gamepads[0].findControl(InputDevice.C_left_y)
|
strafe.x = left_x.value
|
||||||
movementVec.setY(left_y.state - self.lycenter)
|
strafe.y = left_y.value
|
||||||
# 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))
|
|
||||||
|
|
||||||
# calculate movement
|
# Apply some deadzone, since the sticks don't center exactly at 0
|
||||||
base.camera.setX(base.camera, 100 * dt * movementVec.getX())
|
if strafe.lengthSquared() >= 0.01:
|
||||||
base.camera.setY(base.camera, 100 * dt * movementVec.getY())
|
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
|
return task.cont
|
||||||
|
|
||||||
|
@ -16,62 +16,75 @@ from panda3d.core import (
|
|||||||
TextNode,
|
TextNode,
|
||||||
Vec2,
|
Vec2,
|
||||||
InputDevice,
|
InputDevice,
|
||||||
loadPrcFileData)
|
loadPrcFileData,
|
||||||
|
GamepadButton,
|
||||||
|
KeyboardButton)
|
||||||
|
|
||||||
# Make sure the textures look crisp on every device that supports
|
# Make sure the textures look crisp on every device that supports
|
||||||
# non-power-2 textures
|
# non-power-2 textures
|
||||||
loadPrcFileData("", "textures-auto-power-2 #t")
|
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):
|
def __init__(self):
|
||||||
ShowBase.__init__(self)
|
self.__map = dict.fromkeys(self.actions)
|
||||||
|
|
||||||
self.setBackgroundColor(0, 0, 0)
|
def mapButton(self, action, button):
|
||||||
# make the font look nice at a big scale
|
self.__map[action] = ("button", str(button))
|
||||||
DGG.getDefaultFont().setPixelsPerUnit(100)
|
|
||||||
|
|
||||||
# a dict of actions and button/axis events
|
def mapAxis(self, action, axis):
|
||||||
self.gamepadMapping = {
|
self.__map[action] = ("axis", axis.name)
|
||||||
"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
|
|
||||||
|
|
||||||
# The geometry for our basic buttons
|
def unmap(self):
|
||||||
maps = loader.loadModel("models/button_map")
|
self.__map[action] = None
|
||||||
self.buttonGeom = (
|
|
||||||
maps.find("**/ready"),
|
|
||||||
maps.find("**/click"),
|
|
||||||
maps.find("**/hover"),
|
|
||||||
maps.find("**/disabled"))
|
|
||||||
|
|
||||||
# Create the dialog that asks the user for input on a given
|
def formatMapping(self, action):
|
||||||
# action to map a key to.
|
"""Returns a string label describing the mapping for a given action,
|
||||||
DGG.setDefaultDialogGeom("models/dialog.png")
|
for displaying in a GUI. """
|
||||||
# setup a dialog to ask for device input
|
mapping = self.__map.get(action)
|
||||||
self.dlgInput = OkCancelDialog(
|
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",
|
dialogName="dlg_device_input",
|
||||||
pos=(0, 0, 0.25),
|
pos=(0, 0, 0.25),
|
||||||
text="Hit desired key:",
|
text="Hit desired key:",
|
||||||
@ -82,7 +95,7 @@ class App(ShowBase):
|
|||||||
text_align=TextNode.ACenter,
|
text_align=TextNode.ACenter,
|
||||||
fadeScreen=0.65,
|
fadeScreen=0.65,
|
||||||
frameColor=VBase4(0.3, 0.3, 0.3, 1),
|
frameColor=VBase4(0.3, 0.3, 0.3, 1),
|
||||||
button_geom=self.buttonGeom,
|
button_geom=button_geom,
|
||||||
button_scale=0.15,
|
button_scale=0.15,
|
||||||
button_text_scale=0.35,
|
button_text_scale=0.35,
|
||||||
button_text_align=TextNode.ALeft,
|
button_text_align=TextNode.ALeft,
|
||||||
@ -93,13 +106,73 @@ class App(ShowBase):
|
|||||||
button_frameColor=VBase4(0, 0, 0, 0),
|
button_frameColor=VBase4(0, 0, 0, 0),
|
||||||
button_frameSize=VBase4(-1.0, 1.0, -0.25, 0.25),
|
button_frameSize=VBase4(-1.0, 1.0, -0.25, 0.25),
|
||||||
button_pressEffect=False,
|
button_pressEffect=False,
|
||||||
command=self.closeDialog)
|
command=self.onClose)
|
||||||
self.dlgInput.setTransparency(True)
|
self.dialog.setTransparency(True)
|
||||||
self.dlgInput.configureDialog()
|
self.dialog.configureDialog()
|
||||||
scale = self.dlgInput["image_scale"]
|
scale = self.dialog["image_scale"]
|
||||||
self.dlgInput["image_scale"] = (scale[0]/2.0, scale[1], scale[2]/2.0)
|
self.dialog["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.dialog["text_pos"] = (self.dialog["text_pos"][0], self.dialog["text_pos"][1] + 0.06)
|
||||||
self.dlgInput.hide()
|
|
||||||
|
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
|
# create a sample title
|
||||||
self.textscale = 0.1
|
self.textscale = 0.1
|
||||||
@ -135,6 +208,7 @@ class App(ShowBase):
|
|||||||
decMaps.find("**/dec_click"),
|
decMaps.find("**/dec_click"),
|
||||||
decMaps.find("**/dec_hover"),
|
decMaps.find("**/dec_hover"),
|
||||||
decMaps.find("**/dec_disabled"))
|
decMaps.find("**/dec_disabled"))
|
||||||
|
|
||||||
# create the scrolled frame that will hold our list
|
# create the scrolled frame that will hold our list
|
||||||
self.lstActionMap = DirectScrolledFrame(
|
self.lstActionMap = DirectScrolledFrame(
|
||||||
# make the frame occupy the whole window
|
# make the frame occupy the whole window
|
||||||
@ -167,135 +241,116 @@ class App(ShowBase):
|
|||||||
idx = 0
|
idx = 0
|
||||||
self.listBGEven = base.loader.loadModel("models/list_item_even")
|
self.listBGEven = base.loader.loadModel("models/list_item_even")
|
||||||
self.listBGOdd = base.loader.loadModel("models/list_item_odd")
|
self.listBGOdd = base.loader.loadModel("models/list_item_odd")
|
||||||
for key, value in self.gamepadMapping.items():
|
self.actionLabels = {}
|
||||||
item = self.__makeListItem(key, key, value, idx)
|
for action in self.mapping.actions:
|
||||||
|
mapped = self.mapping.formatMapping(action)
|
||||||
|
item = self.__makeListItem(action, mapped, idx)
|
||||||
item.reparentTo(self.lstActionMap.getCanvas())
|
item.reparentTo(self.lstActionMap.getCanvas())
|
||||||
idx += 1
|
idx += 1
|
||||||
|
|
||||||
# recalculate the canvas size to set scrollbars if necesary
|
# recalculate the canvas size to set scrollbars if necesary
|
||||||
self.lstActionMap["canvasSize"] = (
|
self.lstActionMap["canvasSize"] = (
|
||||||
base.a2dLeft+0.05, base.a2dRight-0.05,
|
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()
|
self.lstActionMap.setCanvasSize()
|
||||||
|
|
||||||
def closeDialog(self, result):
|
def closeDialog(self, action, newInputType, newInput):
|
||||||
self.dlgInput.hide()
|
"""Called in callback when the dialog is closed. newInputType will be
|
||||||
if result == DGG.DIALOG_OK:
|
"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
|
# 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
|
# actualize the label in the list that shows the current
|
||||||
# event for the action
|
# event for the action
|
||||||
self.actualizeLabel["text"] = self.newActionKey
|
self.actionLabels[action]["text"] = self.mapping.formatMapping(action)
|
||||||
|
|
||||||
# cleanup
|
# cleanup
|
||||||
self.dlgInput["text"] ="Hit desired key:"
|
|
||||||
self.actionToMap = ""
|
|
||||||
self.newActionKey = ""
|
|
||||||
self.actualizeLabel = None
|
|
||||||
for bt in base.buttonThrowers:
|
for bt in base.buttonThrowers:
|
||||||
|
bt.node().setSpecificFlag(True)
|
||||||
bt.node().setButtonDownEvent("")
|
bt.node().setButtonDownEvent("")
|
||||||
for bt in base.deviceButtonThrowers:
|
for bt in base.deviceButtonThrowers:
|
||||||
|
bt.node().setSpecificFlag(True)
|
||||||
bt.node().setButtonDownEvent("")
|
bt.node().setButtonDownEvent("")
|
||||||
taskMgr.remove("checkControls")
|
taskMgr.remove("checkControls")
|
||||||
|
|
||||||
def changeMapping(self, action, label):
|
# Now detach all the input devices.
|
||||||
# set the action that we want to map a new key to
|
for device in self.attachedDevices:
|
||||||
self.actionToMap = action
|
base.detachInputDevice(device)
|
||||||
# set the label that needs to be actualized
|
self.attachedDevices.clear()
|
||||||
self.actualizeLabel = label
|
|
||||||
# show our dialog
|
|
||||||
self.dlgInput.show()
|
|
||||||
|
|
||||||
# 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:
|
for bt in base.buttonThrowers:
|
||||||
|
bt.node().setSpecificFlag(False)
|
||||||
bt.node().setButtonDownEvent("keyListenEvent")
|
bt.node().setButtonDownEvent("keyListenEvent")
|
||||||
for bt in base.deviceButtonThrowers:
|
for bt in base.deviceButtonThrowers:
|
||||||
|
bt.node().setSpecificFlag(False)
|
||||||
bt.node().setButtonDownEvent("deviceListenEvent")
|
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
|
self.accept("keyListenEvent", self.dlgInput.buttonPressed)
|
||||||
# a task to check if the controls got moved
|
self.accept("deviceListenEvent", self.dlgInput.buttonPressed)
|
||||||
# This list will help us for checking which controls got moved
|
|
||||||
self.controlStates = {None:{}}
|
# 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
|
# fill it with all available controls
|
||||||
for device in base.devices.get_devices():
|
for device in devices:
|
||||||
for ctrl in device.controls:
|
for axis in device.axes:
|
||||||
if device not in self.controlStates.keys():
|
if device not in self.axisStates.keys():
|
||||||
self.controlStates.update({device: {ctrl.axis: ctrl.state}})
|
self.axisStates.update({device: {axis.axis: axis.value}})
|
||||||
else:
|
else:
|
||||||
self.controlStates[device].update({ctrl.axis: ctrl.state})
|
self.axisStates[device].update({axis.axis: axis.value})
|
||||||
# start the task
|
# start the task
|
||||||
taskMgr.add(self.watchControls, "checkControls")
|
taskMgr.add(self.watchControls, "checkControls")
|
||||||
|
|
||||||
def watchControls(self, task):
|
def watchControls(self, task):
|
||||||
# move through all devices and all it's controls
|
# move through all devices and all it's controls
|
||||||
for device in base.devices.get_devices():
|
for device in self.attachedDevices:
|
||||||
for ctrl in device.controls:
|
if device.device_class == InputDevice.DC_mouse:
|
||||||
# if a control got changed more than the given puffer zone
|
# Ignore mouse axis movement, or the user can't even navigate
|
||||||
if self.controlStates[device][ctrl.axis] + 0.2 < ctrl.state or \
|
# to the OK/Cancel buttons!
|
||||||
self.controlStates[device][ctrl.axis] - 0.2 > ctrl.state:
|
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
|
# set the current state in the dict
|
||||||
self.controlStates[device][ctrl.axis] = ctrl.state
|
self.axisStates[device][axis.axis] = axis.value
|
||||||
# check which axis got moved
|
|
||||||
if ctrl.axis == InputDevice.C_left_x:
|
# Format the axis for being displayed.
|
||||||
self.setKey("Left Stick X")
|
if axis.axis != InputDevice.Axis.none:
|
||||||
elif ctrl.axis == InputDevice.C_left_y:
|
#label = axis.axis.name.replace('_', ' ').title()
|
||||||
self.setKey("Left Stick Y")
|
self.dlgInput.axisMoved(axis.axis)
|
||||||
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")
|
|
||||||
return task.cont
|
return task.cont
|
||||||
|
|
||||||
def setKey(self, args):
|
def __makeListItem(self, action, event, index):
|
||||||
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 dummy(): pass
|
def dummy(): pass
|
||||||
if index % 2 == 0:
|
if index % 2 == 0:
|
||||||
bg = self.listBGEven
|
bg = self.listBGEven
|
||||||
else:
|
else:
|
||||||
bg = self.listBGOdd
|
bg = self.listBGOdd
|
||||||
item = DirectFrame(
|
item = DirectFrame(
|
||||||
text=itemName,
|
text=action,
|
||||||
geom=bg,
|
geom=bg,
|
||||||
geom_scale=(base.a2dRight-0.05, 1, 0.1),
|
geom_scale=(base.a2dRight-0.05, 1, 0.1),
|
||||||
frameSize=VBase4(base.a2dLeft+0.05, base.a2dRight-0.05, -0.05, 0.05),
|
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.reparentTo(item)
|
||||||
lbl.setTransparency(True)
|
lbl.setTransparency(True)
|
||||||
|
self.actionLabels[action] = lbl
|
||||||
|
|
||||||
buttonScale = 0.15
|
buttonScale = 0.15
|
||||||
btn = DirectButton(
|
btn = DirectButton(
|
||||||
text="Change",
|
text="Change",
|
||||||
@ -333,10 +390,10 @@ class App(ShowBase):
|
|||||||
pos=(base.a2dRight-(0.898*buttonScale+0.3), 0, 0),
|
pos=(base.a2dRight-(0.898*buttonScale+0.3), 0, 0),
|
||||||
pressEffect=False,
|
pressEffect=False,
|
||||||
command=self.changeMapping,
|
command=self.changeMapping,
|
||||||
extraArgs=[action, lbl])
|
extraArgs=[action])
|
||||||
btn.setTransparency(True)
|
btn.setTransparency(True)
|
||||||
btn.reparentTo(item)
|
btn.reparentTo(item)
|
||||||
return item
|
return item
|
||||||
|
|
||||||
app = App()
|
app = MappingGUIDemo()
|
||||||
app.run()
|
app.run()
|
||||||
|
384
samples/gamepad/models/xbone-icons.egg
Normal file
384
samples/gamepad/models/xbone-icons.egg
Normal file
@ -0,0 +1,384 @@
|
|||||||
|
<CoordinateSystem> { Y-Up }
|
||||||
|
|
||||||
|
<Texture> xbone-icons {
|
||||||
|
xbone-icons.png
|
||||||
|
<Scalar> format { rgba }
|
||||||
|
<Scalar> alpha { dual }
|
||||||
|
}
|
||||||
|
|
||||||
|
<VertexPool> vpool {
|
||||||
|
<Vertex> 0 {
|
||||||
|
-0.5 0.5 0
|
||||||
|
<UV> { 0.00390625 0.9921875 }
|
||||||
|
}
|
||||||
|
<Vertex> 1 {
|
||||||
|
-0.5 -0.5 0
|
||||||
|
<UV> { 0.00390625 0.7578125 }
|
||||||
|
}
|
||||||
|
<Vertex> 2 {
|
||||||
|
0.5 -0.5 0
|
||||||
|
<UV> { 0.12109375 0.7578125 }
|
||||||
|
}
|
||||||
|
<Vertex> 3 {
|
||||||
|
0.5 0.5 0
|
||||||
|
<UV> { 0.12109375 0.9921875 }
|
||||||
|
}
|
||||||
|
<Vertex> 4 {
|
||||||
|
-0.5 0.5 0
|
||||||
|
<UV> { 0.12890625 0.9921875 }
|
||||||
|
}
|
||||||
|
<Vertex> 5 {
|
||||||
|
-0.5 -0.5 0
|
||||||
|
<UV> { 0.12890625 0.7578125 }
|
||||||
|
}
|
||||||
|
<Vertex> 6 {
|
||||||
|
0.5 -0.5 0
|
||||||
|
<UV> { 0.24609375 0.7578125 }
|
||||||
|
}
|
||||||
|
<Vertex> 7 {
|
||||||
|
0.5 0.5 0
|
||||||
|
<UV> { 0.24609375 0.9921875 }
|
||||||
|
}
|
||||||
|
<Vertex> 8 {
|
||||||
|
-0.5 0.5 0
|
||||||
|
<UV> { 0.25390625 0.9921875 }
|
||||||
|
}
|
||||||
|
<Vertex> 9 {
|
||||||
|
-0.5 -0.5 0
|
||||||
|
<UV> { 0.25390625 0.7578125 }
|
||||||
|
}
|
||||||
|
<Vertex> 10 {
|
||||||
|
0.5 -0.5 0
|
||||||
|
<UV> { 0.37109375 0.7578125 }
|
||||||
|
}
|
||||||
|
<Vertex> 11 {
|
||||||
|
0.5 0.5 0
|
||||||
|
<UV> { 0.37109375 0.9921875 }
|
||||||
|
}
|
||||||
|
<Vertex> 12 {
|
||||||
|
-0.5 0.5 0
|
||||||
|
<UV> { 0.37890625 0.9921875 }
|
||||||
|
}
|
||||||
|
<Vertex> 13 {
|
||||||
|
-0.5 -0.5 0
|
||||||
|
<UV> { 0.37890625 0.7578125 }
|
||||||
|
}
|
||||||
|
<Vertex> 14 {
|
||||||
|
0.5 -0.5 0
|
||||||
|
<UV> { 0.49609375 0.7578125 }
|
||||||
|
}
|
||||||
|
<Vertex> 15 {
|
||||||
|
0.5 0.5 0
|
||||||
|
<UV> { 0.49609375 0.9921875 }
|
||||||
|
}
|
||||||
|
<Vertex> 16 {
|
||||||
|
-0.5 0.5 0
|
||||||
|
<UV> { 0.50390625 0.9921875 }
|
||||||
|
}
|
||||||
|
<Vertex> 17 {
|
||||||
|
-0.5 -0.5 0
|
||||||
|
<UV> { 0.50390625 0.7578125 }
|
||||||
|
}
|
||||||
|
<Vertex> 18 {
|
||||||
|
0.5 -0.5 0
|
||||||
|
<UV> { 0.62109375 0.7578125 }
|
||||||
|
}
|
||||||
|
<Vertex> 19 {
|
||||||
|
0.5 0.5 0
|
||||||
|
<UV> { 0.62109375 0.9921875 }
|
||||||
|
}
|
||||||
|
<Vertex> 20 {
|
||||||
|
-0.5 0.5 0
|
||||||
|
<UV> { 0.62890625 0.9921875 }
|
||||||
|
}
|
||||||
|
<Vertex> 21 {
|
||||||
|
-0.5 -0.5 0
|
||||||
|
<UV> { 0.62890625 0.7578125 }
|
||||||
|
}
|
||||||
|
<Vertex> 22 {
|
||||||
|
0.5 -0.5 0
|
||||||
|
<UV> { 0.74609375 0.7578125 }
|
||||||
|
}
|
||||||
|
<Vertex> 23 {
|
||||||
|
0.5 0.5 0
|
||||||
|
<UV> { 0.74609375 0.9921875 }
|
||||||
|
}
|
||||||
|
<Vertex> 24 {
|
||||||
|
-0.5 0.5 0
|
||||||
|
<UV> { 0.75390625 0.9921875 }
|
||||||
|
}
|
||||||
|
<Vertex> 25 {
|
||||||
|
-0.5 -0.5 0
|
||||||
|
<UV> { 0.75390625 0.7578125 }
|
||||||
|
}
|
||||||
|
<Vertex> 26 {
|
||||||
|
0.5 -0.5 0
|
||||||
|
<UV> { 0.87109375 0.7578125 }
|
||||||
|
}
|
||||||
|
<Vertex> 27 {
|
||||||
|
0.5 0.5 0
|
||||||
|
<UV> { 0.87109375 0.9921875 }
|
||||||
|
}
|
||||||
|
<Vertex> 28 {
|
||||||
|
-0.5 0.5 0
|
||||||
|
<UV> { 0.87890625 0.9921875 }
|
||||||
|
}
|
||||||
|
<Vertex> 29 {
|
||||||
|
-0.5 -0.5 0
|
||||||
|
<UV> { 0.87890625 0.7578125 }
|
||||||
|
}
|
||||||
|
<Vertex> 30 {
|
||||||
|
0.5 -0.5 0
|
||||||
|
<UV> { 0.99609375 0.7578125 }
|
||||||
|
}
|
||||||
|
<Vertex> 31 {
|
||||||
|
0.5 0.5 0
|
||||||
|
<UV> { 0.99609375 0.9921875 }
|
||||||
|
}
|
||||||
|
<Vertex> 32 {
|
||||||
|
-0.5 0.5 0
|
||||||
|
<UV> { 0.12890625 0.7421875 }
|
||||||
|
}
|
||||||
|
<Vertex> 33 {
|
||||||
|
-0.5 -0.5 0
|
||||||
|
<UV> { 0.12890625 0.5078125 }
|
||||||
|
}
|
||||||
|
<Vertex> 34 {
|
||||||
|
0.5 -0.5 0
|
||||||
|
<UV> { 0.24609375 0.5078125 }
|
||||||
|
}
|
||||||
|
<Vertex> 35 {
|
||||||
|
0.5 0.5 0
|
||||||
|
<UV> { 0.24609375 0.7421875 }
|
||||||
|
}
|
||||||
|
<Vertex> 36 {
|
||||||
|
-0.5 0.5 0
|
||||||
|
<UV> { 0.00390625 0.7421875 }
|
||||||
|
}
|
||||||
|
<Vertex> 37 {
|
||||||
|
-0.5 -0.5 0
|
||||||
|
<UV> { 0.00390625 0.5078125 }
|
||||||
|
}
|
||||||
|
<Vertex> 38 {
|
||||||
|
0.5 -0.5 0
|
||||||
|
<UV> { 0.12109375 0.5078125 }
|
||||||
|
}
|
||||||
|
<Vertex> 39 {
|
||||||
|
0.5 0.5 0
|
||||||
|
<UV> { 0.12109375 0.7421875 }
|
||||||
|
}
|
||||||
|
<Vertex> 40 {
|
||||||
|
-0.5 0.5 0
|
||||||
|
<UV> { 0.25390625 0.7421875 }
|
||||||
|
}
|
||||||
|
<Vertex> 41 {
|
||||||
|
-0.5 -0.5 0
|
||||||
|
<UV> { 0.25390625 0.5078125 }
|
||||||
|
}
|
||||||
|
<Vertex> 42 {
|
||||||
|
0.5 -0.5 0
|
||||||
|
<UV> { 0.37109375 0.5078125 }
|
||||||
|
}
|
||||||
|
<Vertex> 43 {
|
||||||
|
0.5 0.5 0
|
||||||
|
<UV> { 0.37109375 0.7421875 }
|
||||||
|
}
|
||||||
|
<Vertex> 44 {
|
||||||
|
-0.5 0.5 0
|
||||||
|
<UV> { 0.37890625 0.7421875 }
|
||||||
|
}
|
||||||
|
<Vertex> 45 {
|
||||||
|
-0.5 -0.5 0
|
||||||
|
<UV> { 0.37890625 0.5078125 }
|
||||||
|
}
|
||||||
|
<Vertex> 46 {
|
||||||
|
0.5 -0.5 0
|
||||||
|
<UV> { 0.49609375 0.5078125 }
|
||||||
|
}
|
||||||
|
<Vertex> 47 {
|
||||||
|
0.5 0.5 0
|
||||||
|
<UV> { 0.49609375 0.7421875 }
|
||||||
|
}
|
||||||
|
<Vertex> 48 {
|
||||||
|
-0.5 0.5 0
|
||||||
|
<UV> { 0.62890625 0.7421875 }
|
||||||
|
}
|
||||||
|
<Vertex> 49 {
|
||||||
|
-0.5 -0.5 0
|
||||||
|
<UV> { 0.62890625 0.5078125 }
|
||||||
|
}
|
||||||
|
<Vertex> 50 {
|
||||||
|
0.5 -0.5 0
|
||||||
|
<UV> { 0.74609375 0.5078125 }
|
||||||
|
}
|
||||||
|
<Vertex> 51 {
|
||||||
|
0.5 0.5 0
|
||||||
|
<UV> { 0.74609375 0.7421875 }
|
||||||
|
}
|
||||||
|
<Vertex> 52 {
|
||||||
|
-0.5 0.5 0
|
||||||
|
<UV> { 0.50390625 0.7421875 }
|
||||||
|
}
|
||||||
|
<Vertex> 53 {
|
||||||
|
-0.5 -0.5 0
|
||||||
|
<UV> { 0.50390625 0.5078125 }
|
||||||
|
}
|
||||||
|
<Vertex> 54 {
|
||||||
|
0.5 -0.5 0
|
||||||
|
<UV> { 0.62109375 0.5078125 }
|
||||||
|
}
|
||||||
|
<Vertex> 55 {
|
||||||
|
0.5 0.5 0
|
||||||
|
<UV> { 0.62109375 0.7421875 }
|
||||||
|
}
|
||||||
|
<Vertex> 56 {
|
||||||
|
-0.5 0.5 0
|
||||||
|
<UV> { 0.75390625 0.7421875 }
|
||||||
|
}
|
||||||
|
<Vertex> 57 {
|
||||||
|
-0.5 -0.5 0
|
||||||
|
<UV> { 0.75390625 0.5078125 }
|
||||||
|
}
|
||||||
|
<Vertex> 58 {
|
||||||
|
0.5 -0.5 0
|
||||||
|
<UV> { 0.87109375 0.5078125 }
|
||||||
|
}
|
||||||
|
<Vertex> 59 {
|
||||||
|
0.5 0.5 0
|
||||||
|
<UV> { 0.87109375 0.7421875 }
|
||||||
|
}
|
||||||
|
<Vertex> 60 {
|
||||||
|
-0.5 0.5 0
|
||||||
|
<UV> { 0.87890625 0.7421875 }
|
||||||
|
}
|
||||||
|
<Vertex> 61 {
|
||||||
|
-0.5 -0.5 0
|
||||||
|
<UV> { 0.87890625 0.5078125 }
|
||||||
|
}
|
||||||
|
<Vertex> 62 {
|
||||||
|
0.5 -0.5 0
|
||||||
|
<UV> { 0.99609375 0.5078125 }
|
||||||
|
}
|
||||||
|
<Vertex> 63 {
|
||||||
|
0.5 0.5 0
|
||||||
|
<UV> { 0.99609375 0.7421875 }
|
||||||
|
}
|
||||||
|
<Vertex> 64 {
|
||||||
|
-0.5 0.5 0
|
||||||
|
<UV> { 0.00390625 0.4921875 }
|
||||||
|
}
|
||||||
|
<Vertex> 65 {
|
||||||
|
-0.5 -0.5 0
|
||||||
|
<UV> { 0.00390625 0.2578125 }
|
||||||
|
}
|
||||||
|
<Vertex> 66 {
|
||||||
|
0.5 -0.5 0
|
||||||
|
<UV> { 0.12109375 0.2578125 }
|
||||||
|
}
|
||||||
|
<Vertex> 67 {
|
||||||
|
0.5 0.5 0
|
||||||
|
<UV> { 0.12109375 0.4921875 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<Group> face_a {
|
||||||
|
<Polygon> {
|
||||||
|
<TRef> { xbone-icons }
|
||||||
|
<VertexRef> { 0 1 2 3 <Ref> { vpool } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<Group> face_b {
|
||||||
|
<Polygon> {
|
||||||
|
<TRef> { xbone-icons }
|
||||||
|
<VertexRef> { 4 5 6 7 <Ref> { vpool } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<Group> dpad {
|
||||||
|
<Polygon> {
|
||||||
|
<TRef> { xbone-icons }
|
||||||
|
<VertexRef> { 8 9 10 11 <Ref> { vpool } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<Group> dpad_down {
|
||||||
|
<Polygon> {
|
||||||
|
<TRef> { xbone-icons }
|
||||||
|
<VertexRef> { 12 13 14 15 <Ref> { vpool } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<Group> dpad_left {
|
||||||
|
<Polygon> {
|
||||||
|
<TRef> { xbone-icons }
|
||||||
|
<VertexRef> { 16 17 18 19 <Ref> { vpool } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<Group> dpad_right {
|
||||||
|
<Polygon> {
|
||||||
|
<TRef> { xbone-icons }
|
||||||
|
<VertexRef> { 20 21 22 23 <Ref> { vpool } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<Group> dpad_up {
|
||||||
|
<Polygon> {
|
||||||
|
<TRef> { xbone-icons }
|
||||||
|
<VertexRef> { 24 25 26 27 <Ref> { vpool } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<Group> XboxOne_LB {
|
||||||
|
<Polygon> {
|
||||||
|
<TRef> { xbone-icons }
|
||||||
|
<VertexRef> { 28 29 30 31 <Ref> { vpool } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<Group> lstick {
|
||||||
|
<Polygon> {
|
||||||
|
<TRef> { xbone-icons }
|
||||||
|
<VertexRef> { 32 33 34 35 <Ref> { vpool } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<Group> ltrigger {
|
||||||
|
<Polygon> {
|
||||||
|
<TRef> { xbone-icons }
|
||||||
|
<VertexRef> { 36 37 38 39 <Ref> { vpool } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<Group> start {
|
||||||
|
<Polygon> {
|
||||||
|
<TRef> { xbone-icons }
|
||||||
|
<VertexRef> { 40 41 42 43 <Ref> { vpool } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<Group> rshoulder {
|
||||||
|
<Polygon> {
|
||||||
|
<TRef> { xbone-icons }
|
||||||
|
<VertexRef> { 44 45 46 47 <Ref> { vpool } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<Group> rstick {
|
||||||
|
<Polygon> {
|
||||||
|
<TRef> { xbone-icons }
|
||||||
|
<VertexRef> { 48 49 50 51 <Ref> { vpool } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<Group> rtrigger {
|
||||||
|
<Polygon> {
|
||||||
|
<TRef> { xbone-icons }
|
||||||
|
<VertexRef> { 52 53 54 55 <Ref> { vpool } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<Group> back {
|
||||||
|
<Polygon> {
|
||||||
|
<TRef> { xbone-icons }
|
||||||
|
<VertexRef> { 56 57 58 59 <Ref> { vpool } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<Group> face_x {
|
||||||
|
<Polygon> {
|
||||||
|
<TRef> { xbone-icons }
|
||||||
|
<VertexRef> { 60 61 62 63 <Ref> { vpool } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<Group> face_y {
|
||||||
|
<Polygon> {
|
||||||
|
<TRef> { xbone-icons }
|
||||||
|
<VertexRef> { 64 65 66 67 <Ref> { vpool } }
|
||||||
|
}
|
||||||
|
}
|
BIN
samples/gamepad/models/xbone-icons.png
Normal file
BIN
samples/gamepad/models/xbone-icons.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
Loading…
x
Reference in New Issue
Block a user