Merge branch 'release/1.10.x'

This commit is contained in:
rdb 2019-02-06 22:52:19 +01:00
commit 36dd8889e7
14 changed files with 543 additions and 201 deletions

View File

@ -12,6 +12,7 @@
*/
#include "configVariableBool.h"
#include "mutexImpl.h"
/**
* Refreshes the cached value.
@ -21,7 +22,10 @@ reload_value() const {
// NB. MSVC doesn't guarantee that this mutex is initialized in a
// thread-safe manner. But chances are that the first time this is called
// is at static init time, when there is no risk of data races.
static MutexImpl lock;
// This needs to be a recursive mutex, because get_bool_word() may end up
// indirectly querying another bool config variable.
static ReMutexImpl lock;
lock.lock();
// We check again for cache validity since another thread may have beaten

View File

@ -288,6 +288,119 @@ test_intersection_from_segment(const CollisionEntry &entry) const {
return new_entry;
}
/**
*
*/
PT(CollisionEntry) CollisionInvSphere::
test_intersection_from_capsule(const CollisionEntry &entry) const {
const CollisionCapsule *capsule;
DCAST_INTO_R(capsule, entry.get_from(), nullptr);
const LMatrix4 &wrt_mat = entry.get_wrt_mat();
LPoint3 from_a = capsule->get_point_a() * wrt_mat;
LPoint3 from_b = capsule->get_point_b() * wrt_mat;
LVector3 from_radius_v =
LVector3(capsule->get_radius(), 0.0f, 0.0f) * wrt_mat;
PN_stdfloat from_radius = from_radius_v.length();
LPoint3 center = get_center();
PN_stdfloat radius = get_radius();
// Check which one of the points lies furthest inside the sphere.
PN_stdfloat dist_a = (from_a - center).length();
PN_stdfloat dist_b = (from_b - center).length();
if (dist_b > dist_a) {
// Store the furthest point into from_a/dist_a.
dist_a = dist_b;
from_a = from_b;
}
// from_a now contains the furthest point. Is it inside?
if (dist_a < radius - from_radius) {
return nullptr;
}
if (collide_cat.is_debug()) {
collide_cat.debug()
<< "intersection detected from " << entry.get_from_node_path()
<< " into " << entry.get_into_node_path() << "\n";
}
PT(CollisionEntry) new_entry = new CollisionEntry(entry);
LVector3 normal = center - from_a;
normal.normalize();
new_entry->set_surface_point(get_center() - normal * radius);
new_entry->set_interior_point(from_a - normal * from_radius);
if (has_effective_normal() && capsule->get_respect_effective_normal()) {
new_entry->set_surface_normal(get_effective_normal());
} else {
new_entry->set_surface_normal(normal);
}
return new_entry;
}
/**
* Double dispatch point for box as a FROM object
*/
PT(CollisionEntry) CollisionInvSphere::
test_intersection_from_box(const CollisionEntry &entry) const {
const CollisionBox *box;
DCAST_INTO_R(box, entry.get_from(), nullptr);
const LMatrix4 &wrt_mat = entry.get_wrt_mat();
LPoint3 center = get_center();
PN_stdfloat radius_sq = get_radius();
radius_sq *= radius_sq;
// Just figure out which box point is furthest from the center. If it
// exceeds the radius, the furthest point wins.
PN_stdfloat max_dist_sq = -1.0;
LPoint3 deepest_vertex;
for (int i = 0; i < 8; ++i) {
LPoint3 point = wrt_mat.xform_point(box->get_point(i));
PN_stdfloat dist_sq = (point - center).length_squared();
if (dist_sq > max_dist_sq) {
deepest_vertex = point;
max_dist_sq = dist_sq;
}
}
if (max_dist_sq < radius_sq) {
// The point furthest away from the center is still inside the sphere.
// Therefore, no collision.
return nullptr;
}
if (collide_cat.is_debug()) {
collide_cat.debug()
<< "intersection detected from " << entry.get_from_node_path()
<< " into " << entry.get_into_node_path() << "\n";
}
PT(CollisionEntry) new_entry = new CollisionEntry(entry);
// The interior point is just the deepest cube vertex.
new_entry->set_interior_point(deepest_vertex);
// Now extrapolate the surface point and normal from that.
LVector3 normal = center - deepest_vertex;
normal.normalize();
new_entry->set_surface_point(center - normal * get_radius());
new_entry->set_surface_normal(
(has_effective_normal() && box->get_respect_effective_normal())
? get_effective_normal() : normal);
return new_entry;
}
/**
* Fills the _viz_geom GeomNode up with Geoms suitable for rendering this
* solid.

View File

@ -55,6 +55,10 @@ protected:
test_intersection_from_ray(const CollisionEntry &entry) const;
virtual PT(CollisionEntry)
test_intersection_from_segment(const CollisionEntry &entry) const;
virtual PT(CollisionEntry)
test_intersection_from_capsule(const CollisionEntry &entry) const;
virtual PT(CollisionEntry)
test_intersection_from_box(const CollisionEntry &entry) const;
virtual void fill_viz_geom();

View File

@ -82,7 +82,7 @@ static const struct DeviceMapping {
{0x28de, 0x1142, InputDevice::DeviceClass::unknown, QB_steam_controller},
// Jess Tech Colour Rumble Pad
{0x0f30, 0x0111, InputDevice::DeviceClass::gamepad, 0},
// Trust GXT 24
// SPEED Link SL-6535-SBK-01
{0x0079, 0x0006, InputDevice::DeviceClass::gamepad, 0},
// 3Dconnexion Space Traveller 3D Mouse
{0x046d, 0xc623, InputDevice::DeviceClass::spatial_mouse, 0},
@ -521,7 +521,10 @@ init_device() {
}
break;
case ABS_THROTTLE:
if (quirks & QB_rudder_from_throttle) {
if (_device_class == DeviceClass::gamepad) {
// Apparently needed for 8bitdo N30 Pro controller
axis = InputDevice::Axis::right_x;
} else if (quirks & QB_rudder_from_throttle) {
axis = InputDevice::Axis::rudder;
} else {
axis = InputDevice::Axis::throttle;

166
panda/src/device/phidsdi.h Normal file
View File

@ -0,0 +1,166 @@
/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file phidsdi.h
* @author rdb
* @date 2019-02-05
*/
#ifndef PHIDSDI_H
#define PHIDSDI_H
#if defined(_WIN32) && !defined(CPPPARSER)
// Copy definitions from hidusage.h, until we can drop support for the 7.1 SDK
typedef USHORT USAGE, *PUSAGE;
#define HID_USAGE_PAGE_UNDEFINED ((USAGE) 0x00)
#define HID_USAGE_PAGE_GENERIC ((USAGE) 0x01)
#define HID_USAGE_PAGE_SIMULATION ((USAGE) 0x02)
#define HID_USAGE_PAGE_VR ((USAGE) 0x03)
#define HID_USAGE_PAGE_SPORT ((USAGE) 0x04)
#define HID_USAGE_PAGE_GAME ((USAGE) 0x05)
#define HID_USAGE_PAGE_KEYBOARD ((USAGE) 0x07)
#define HID_USAGE_PAGE_LED ((USAGE) 0x08)
#define HID_USAGE_PAGE_BUTTON ((USAGE) 0x09)
#define HID_USAGE_GENERIC_POINTER ((USAGE) 0x01)
#define HID_USAGE_GENERIC_MOUSE ((USAGE) 0x02)
#define HID_USAGE_GENERIC_JOYSTICK ((USAGE) 0x04)
#define HID_USAGE_GENERIC_GAMEPAD ((USAGE) 0x05)
#define HID_USAGE_GENERIC_KEYBOARD ((USAGE) 0x06)
#define HID_USAGE_GENERIC_KEYPAD ((USAGE) 0x07)
#define HID_USAGE_GENERIC_SYSTEM_CTL ((USAGE) 0x80)
#define HID_USAGE_GENERIC_X ((USAGE) 0x30)
#define HID_USAGE_GENERIC_Y ((USAGE) 0x31)
#define HID_USAGE_GENERIC_Z ((USAGE) 0x32)
#define HID_USAGE_GENERIC_RX ((USAGE) 0x33)
#define HID_USAGE_GENERIC_RY ((USAGE) 0x34)
#define HID_USAGE_GENERIC_RZ ((USAGE) 0x35)
#define HID_USAGE_GENERIC_SLIDER ((USAGE) 0x36)
#define HID_USAGE_GENERIC_DIAL ((USAGE) 0x37)
#define HID_USAGE_GENERIC_WHEEL ((USAGE) 0x38)
#define HID_USAGE_GENERIC_HATSWITCH ((USAGE) 0x39)
// Copy definitions from hidpi.h, until we can drop support for the 7.1 SDK
#define HIDP_STATUS_SUCCESS ((NTSTATUS)(0x11 << 16))
typedef enum _HIDP_REPORT_TYPE {
HidP_Input,
HidP_Output,
HidP_Feature
} HIDP_REPORT_TYPE;
typedef struct _HIDP_BUTTON_CAPS {
USAGE UsagePage;
UCHAR ReportID;
BOOLEAN IsAlias;
USHORT BitField;
USHORT LinkCollection;
USAGE LinkUsage;
USAGE LinkUsagePage;
BOOLEAN IsRange;
BOOLEAN IsStringRange;
BOOLEAN IsDesignatorRange;
BOOLEAN IsAbsolute;
ULONG Reserved[10];
union {
struct {
USAGE UsageMin, UsageMax;
USHORT StringMin, StringMax;
USHORT DesignatorMin, DesignatorMax;
USHORT DataIndexMin, DataIndexMax;
} Range;
struct {
USAGE Usage, Reserved1;
USHORT StringIndex, Reserved2;
USHORT DesignatorIndex, Reserved3;
USHORT DataIndex, Reserved4;
} NotRange;
};
} HIDP_BUTTON_CAPS, *PHIDP_BUTTON_CAPS;
typedef struct _HIDP_VALUE_CAPS {
USAGE UsagePage;
UCHAR ReportID;
BOOLEAN IsAlias;
USHORT BitField;
USHORT LinkCollection;
USAGE LinkUsage;
USAGE LinkUsagePage;
BOOLEAN IsRange;
BOOLEAN IsStringRange;
BOOLEAN IsDesignatorRange;
BOOLEAN IsAbsolute;
BOOLEAN HasNull;
UCHAR Reserved;
USHORT BitSize;
USHORT ReportCount;
USHORT Reserved2[5];
ULONG UnitsExp;
ULONG Units;
LONG LogicalMin, LogicalMax;
LONG PhysicalMin, PhysicalMax;
union {
struct {
USAGE UsageMin, UsageMax;
USHORT StringMin, StringMax;
USHORT DesignatorMin, DesignatorMax;
USHORT DataIndexMin, DataIndexMax;
} Range;
struct {
USAGE Usage, Reserved1;
USHORT StringIndex, Reserved2;
USHORT DesignatorIndex, Reserved3;
USHORT DataIndex, Reserved4;
} NotRange;
};
} HIDP_VALUE_CAPS, *PHIDP_VALUE_CAPS;
typedef PUCHAR PHIDP_REPORT_DESCRIPTOR;
typedef struct _HIDP_PREPARSED_DATA *PHIDP_PREPARSED_DATA;
typedef struct _HIDP_CAPS {
USAGE Usage;
USAGE UsagePage;
USHORT InputReportByteLength;
USHORT OutputReportByteLength;
USHORT FeatureReportByteLength;
USHORT Reserved[17];
USHORT NumberLinkCollectionNodes;
USHORT NumberInputButtonCaps;
USHORT NumberInputValueCaps;
USHORT NumberInputDataIndices;
USHORT NumberOutputButtonCaps;
USHORT NumberOutputValueCaps;
USHORT NumberOutputDataIndices;
USHORT NumberFeatureButtonCaps;
USHORT NumberFeatureValueCaps;
USHORT NumberFeatureDataIndices;
} HIDP_CAPS, *PHIDP_CAPS;
typedef struct _HIDP_DATA {
USHORT DataIndex;
USHORT Reserved;
union {
ULONG RawValue;
BOOLEAN On;
};
} HIDP_DATA, *PHIDP_DATA;
typedef LONG NTSTATUS;
typedef NTSTATUS (__stdcall *pHidP_GetCaps)(PHIDP_PREPARSED_DATA, PHIDP_CAPS);
typedef NTSTATUS (__stdcall *pHidP_GetButtonCaps)(HIDP_REPORT_TYPE, PHIDP_BUTTON_CAPS, PUSHORT, PHIDP_PREPARSED_DATA);
typedef NTSTATUS (__stdcall *pHidP_GetValueCaps)(HIDP_REPORT_TYPE, PHIDP_VALUE_CAPS, PUSHORT, PHIDP_PREPARSED_DATA);
typedef NTSTATUS (__stdcall *pHidP_GetData)(HIDP_REPORT_TYPE, PHIDP_DATA, PULONG, PHIDP_PREPARSED_DATA, PCHAR, ULONG);
typedef ULONG (__stdcall *pHidP_MaxDataListLength)(HIDP_REPORT_TYPE, PHIDP_PREPARSED_DATA);
#endif
#endif

View File

@ -289,8 +289,8 @@ on_input_device_arrival(HANDLE handle) {
// Some devices insert quite some trailing space here.
wchar_t *wbuffer = (wchar_t *)buffer;
size_t wlen = wcslen(wbuffer);
while (iswspace(wbuffer[wlen - 1])) {
size_t wlen = wcsnlen_s(wbuffer, sizeof(buffer) / sizeof(wchar_t));
while (wlen > 0 && iswspace(wbuffer[wlen - 1])) {
wbuffer[--wlen] = 0;
}
TextEncoder encoder;
@ -391,6 +391,19 @@ on_input_device_removal(HANDLE handle) {
}
}
/**
* Polls the system to see if there are any new devices. In some
* implementations this is a no-op.
*/
void WinInputDeviceManager::
update() {
MSG msg;
while (PeekMessage(&msg, _message_hwnd, WM_INPUT_DEVICE_CHANGE, WM_INPUT, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
/**
* Implementation of the message loop.
*/

View File

@ -52,9 +52,11 @@ private:
pmap<HANDLE, WinRawInputDevice *> _raw_devices;
pmap<std::string, WinRawInputDevice *> _raw_devices_by_path;
virtual void update() override;
static LRESULT WINAPI window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
typedef CONFIGRET (*pCM_Get_DevNode_Property)(DEVINST, const DEVPROPKEY *, DEVPROPTYPE *, PBYTE, PULONG, ULONG);
typedef CONFIGRET (WINAPI *pCM_Get_DevNode_Property)(DEVINST, const DEVPROPKEY *, DEVPROPTYPE *, PBYTE, PULONG, ULONG);
pCM_Get_DevNode_Property _CM_Get_DevNode_PropertyW;
friend class InputDeviceManager;

View File

@ -14,156 +14,56 @@
#include "winRawInputDevice.h"
#include "gamepadButton.h"
#include "mouseButton.h"
#include "buttonRegistry.h"
#if defined(_WIN32) && !defined(CPPPARSER)
#include <CfgMgr32.h>
#include <devpkey.h>
#include "phidsdi.h"
// Copy definitions from hidusage.h, until we can drop support for the 7.1 SDK
typedef USHORT USAGE, *PUSAGE;
enum QuirkBits : int {
// Has no trigger axes.
QB_no_analog_triggers = 1,
#define HID_USAGE_PAGE_UNDEFINED ((USAGE) 0x00)
#define HID_USAGE_PAGE_GENERIC ((USAGE) 0x01)
#define HID_USAGE_PAGE_SIMULATION ((USAGE) 0x02)
#define HID_USAGE_PAGE_VR ((USAGE) 0x03)
#define HID_USAGE_PAGE_SPORT ((USAGE) 0x04)
#define HID_USAGE_PAGE_GAME ((USAGE) 0x05)
#define HID_USAGE_PAGE_KEYBOARD ((USAGE) 0x07)
#define HID_USAGE_PAGE_LED ((USAGE) 0x08)
#define HID_USAGE_PAGE_BUTTON ((USAGE) 0x09)
// Throttle goes from -1 to 1 rather than from 0 to 1.
QB_centered_throttle = 2,
#define HID_USAGE_GENERIC_POINTER ((USAGE) 0x01)
#define HID_USAGE_GENERIC_MOUSE ((USAGE) 0x02)
#define HID_USAGE_GENERIC_JOYSTICK ((USAGE) 0x04)
#define HID_USAGE_GENERIC_GAMEPAD ((USAGE) 0x05)
#define HID_USAGE_GENERIC_KEYBOARD ((USAGE) 0x06)
#define HID_USAGE_GENERIC_KEYPAD ((USAGE) 0x07)
#define HID_USAGE_GENERIC_SYSTEM_CTL ((USAGE) 0x80)
// Throttle is reversed.
QB_reversed_throttle = 4,
};
#define HID_USAGE_GENERIC_X ((USAGE) 0x30)
#define HID_USAGE_GENERIC_Y ((USAGE) 0x31)
#define HID_USAGE_GENERIC_Z ((USAGE) 0x32)
#define HID_USAGE_GENERIC_RX ((USAGE) 0x33)
#define HID_USAGE_GENERIC_RY ((USAGE) 0x34)
#define HID_USAGE_GENERIC_RZ ((USAGE) 0x35)
#define HID_USAGE_GENERIC_SLIDER ((USAGE) 0x36)
#define HID_USAGE_GENERIC_DIAL ((USAGE) 0x37)
#define HID_USAGE_GENERIC_WHEEL ((USAGE) 0x38)
#define HID_USAGE_GENERIC_HATSWITCH ((USAGE) 0x39)
// Some nonstandard gamepads have different button mappings.
static const struct DeviceMapping {
unsigned short vendor;
unsigned short product;
InputDevice::DeviceClass device_class;
int quirks;
const char *buttons[16];
} mapping_presets[] = {
// SNES-style USB gamepad
{0x0810, 0xe501, InputDevice::DeviceClass::gamepad, QB_no_analog_triggers,
{"face_x", "face_a", "face_b", "face_y", "lshoulder", "rshoulder", "none", "none", "back", "start"}
},
// SPEED Link SL-6535-SBK-01
{0x0079, 0x0006, InputDevice::DeviceClass::gamepad, QB_no_analog_triggers,
{"face_y", "face_b", "face_a", "face_x", "lshoulder", "rshoulder", "ltrigger", "rtrigger", "back", "start", "lstick", "rstick"}
},
// T.Flight Hotas X
{0x044f, 0xb108, InputDevice::DeviceClass::flight_stick, QB_centered_throttle | QB_reversed_throttle,
{0}
},
// NVIDIA Shield Controller
{0x0955, 0x7214, InputDevice::DeviceClass::gamepad, 0,
{"face_a", "face_b", "n", "face_x", "face_y", "rshoulder", "lshoulder", "rshoulder", "e", "f", "g", "start", "h", "lstick", "rstick", "i"}
},
{0},
};
// Copy definitions from hidpi.h, until we can drop support for the 7.1 SDK
#define HIDP_STATUS_SUCCESS ((NTSTATUS)(0x11 << 16))
typedef enum _HIDP_REPORT_TYPE {
HidP_Input,
HidP_Output,
HidP_Feature
} HIDP_REPORT_TYPE;
typedef struct _HIDP_BUTTON_CAPS {
USAGE UsagePage;
UCHAR ReportID;
BOOLEAN IsAlias;
USHORT BitField;
USHORT LinkCollection;
USAGE LinkUsage;
USAGE LinkUsagePage;
BOOLEAN IsRange;
BOOLEAN IsStringRange;
BOOLEAN IsDesignatorRange;
BOOLEAN IsAbsolute;
ULONG Reserved[10];
union {
struct {
USAGE UsageMin, UsageMax;
USHORT StringMin, StringMax;
USHORT DesignatorMin, DesignatorMax;
USHORT DataIndexMin, DataIndexMax;
} Range;
struct {
USAGE Usage, Reserved1;
USHORT StringIndex, Reserved2;
USHORT DesignatorIndex, Reserved3;
USHORT DataIndex, Reserved4;
} NotRange;
};
} HIDP_BUTTON_CAPS, *PHIDP_BUTTON_CAPS;
typedef struct _HIDP_VALUE_CAPS {
USAGE UsagePage;
UCHAR ReportID;
BOOLEAN IsAlias;
USHORT BitField;
USHORT LinkCollection;
USAGE LinkUsage;
USAGE LinkUsagePage;
BOOLEAN IsRange;
BOOLEAN IsStringRange;
BOOLEAN IsDesignatorRange;
BOOLEAN IsAbsolute;
BOOLEAN HasNull;
UCHAR Reserved;
USHORT BitSize;
USHORT ReportCount;
USHORT Reserved2[5];
ULONG UnitsExp;
ULONG Units;
LONG LogicalMin, LogicalMax;
LONG PhysicalMin, PhysicalMax;
union {
struct {
USAGE UsageMin, UsageMax;
USHORT StringMin, StringMax;
USHORT DesignatorMin, DesignatorMax;
USHORT DataIndexMin, DataIndexMax;
} Range;
struct {
USAGE Usage, Reserved1;
USHORT StringIndex, Reserved2;
USHORT DesignatorIndex, Reserved3;
USHORT DataIndex, Reserved4;
} NotRange;
};
} HIDP_VALUE_CAPS, *PHIDP_VALUE_CAPS;
typedef PUCHAR PHIDP_REPORT_DESCRIPTOR;
typedef struct _HIDP_PREPARSED_DATA *PHIDP_PREPARSED_DATA;
typedef struct _HIDP_CAPS {
USAGE Usage;
USAGE UsagePage;
USHORT InputReportByteLength;
USHORT OutputReportByteLength;
USHORT FeatureReportByteLength;
USHORT Reserved[17];
USHORT NumberLinkCollectionNodes;
USHORT NumberInputButtonCaps;
USHORT NumberInputValueCaps;
USHORT NumberInputDataIndices;
USHORT NumberOutputButtonCaps;
USHORT NumberOutputValueCaps;
USHORT NumberOutputDataIndices;
USHORT NumberFeatureButtonCaps;
USHORT NumberFeatureValueCaps;
USHORT NumberFeatureDataIndices;
} HIDP_CAPS, *PHIDP_CAPS;
typedef struct _HIDP_DATA {
USHORT DataIndex;
USHORT Reserved;
union {
ULONG RawValue;
BOOLEAN On;
};
} HIDP_DATA, *PHIDP_DATA;
typedef LONG NTSTATUS;
typedef NTSTATUS (*pHidP_GetCaps)(PHIDP_PREPARSED_DATA, PHIDP_CAPS);
typedef NTSTATUS (*pHidP_GetButtonCaps)(HIDP_REPORT_TYPE, PHIDP_BUTTON_CAPS, PUSHORT, PHIDP_PREPARSED_DATA);
typedef NTSTATUS (*pHidP_GetValueCaps)(HIDP_REPORT_TYPE, PHIDP_VALUE_CAPS, PUSHORT, PHIDP_PREPARSED_DATA);
typedef NTSTATUS (*pHidP_GetData)(HIDP_REPORT_TYPE, PHIDP_DATA, PULONG, PHIDP_PREPARSED_DATA, PCHAR, ULONG);
typedef ULONG (*pHidP_MaxDataListLength)(HIDP_REPORT_TYPE, PHIDP_PREPARSED_DATA);
// This is our fallback button mapping, used with Xbox 360 and other devices.
static const char *default_gamepad_mapping[16] = {
"face_a", "face_b", "face_x", "face_y", "lshoulder", "rshoulder", "back", "start", "lstick", "rstick"
};
static pHidP_GetCaps _HidP_GetCaps = nullptr;
static pHidP_GetButtonCaps _HidP_GetButtonCaps = nullptr;
@ -246,6 +146,9 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
_name = std::move(name);
int quirks = 0;
const char *const *gamepad_buttons = default_gamepad_mapping;
switch (info.dwType) {
case RIM_TYPEMOUSE:
_device_class = DeviceClass::mouse;
@ -264,7 +167,7 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
info.hid.usUsage == HID_USAGE_GENERIC_GAMEPAD) {
_device_class = DeviceClass::gamepad;
// Flight sticks
// Various game controllers, incl. flight sticks and some gamepads
} else if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC &&
info.hid.usUsage == HID_USAGE_GENERIC_JOYSTICK) {
_device_class = DeviceClass::flight_stick;
@ -273,11 +176,6 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
// Well, it claims to be a gamepad...
_device_class = DeviceClass::gamepad;
}
//TODO: better solution for this
if (_vendor_id == 0x0079 && _product_id == 0x0006) {
// Trust GXT 24
_device_class = DeviceClass::gamepad;
}
// Mice
} else if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC &&
@ -306,6 +204,28 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
return false;
}
if (_device_class == DeviceClass::gamepad ||
_device_class == DeviceClass::flight_stick) {
// Do we have a built-in mapping?
const DeviceMapping *mapping = mapping_presets;
while (mapping->vendor != 0) {
if (info.hid.dwVendorId == mapping->vendor &&
info.hid.dwProductId == mapping->product) {
_device_class = mapping->device_class;
gamepad_buttons = mapping->buttons;
quirks = mapping->quirks;
if (device_cat.is_debug()) {
device_cat.debug()
<< "Using preset mapping for " << mapping->device_class
<< " with VID=" << std::hex << mapping->vendor
<< " PID=" << mapping->product << std::dec << "\n";
}
break;
}
++mapping;
}
}
// Initialize hid.dll, which provides the HID parser functions.
static bool hid_initialized = false;
if (!hid_initialized) {
@ -342,38 +262,7 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
<< caps.NumberInputValueCaps << " value caps\n";
}
// Do we have a button mapping?
static const ButtonHandle gamepad_buttons_common[] = {
ButtonHandle::none(),
GamepadButton::face_a(),
GamepadButton::face_b(),
GamepadButton::face_x(),
GamepadButton::face_y(),
GamepadButton::lshoulder(),
GamepadButton::rshoulder(),
GamepadButton::start(),
GamepadButton::back(),
GamepadButton::lstick(),
GamepadButton::rstick(),
};
const ButtonHandle *gamepad_buttons = gamepad_buttons_common;
if (_vendor_id == 0x0810 && _product_id == 0xe501) {
// SNES-style USB gamepad
static const ButtonHandle gamepad_buttons_snes[] = {
ButtonHandle::none(),
GamepadButton::face_x(),
GamepadButton::face_a(),
GamepadButton::face_b(),
GamepadButton::face_y(),
GamepadButton::lshoulder(),
GamepadButton::rshoulder(),
ButtonHandle::none(),
ButtonHandle::none(),
GamepadButton::back(),
GamepadButton::start(),
};
gamepad_buttons = gamepad_buttons_snes;
}
ButtonRegistry *registry = ButtonRegistry::ptr();
// Prepare a mapping of data indices to button/axis indices.
_indices.resize(caps.NumberInputDataIndices);
@ -414,7 +303,7 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
}
}
nassertd(cap.Range.DataIndexMin + upper < _indices.size()) continue;
nassertd(cap.Range.DataIndexMin + upper < (int)_indices.size()) continue;
// Windows will only tell us which buttons in a report are "on", so we
// need to keep track of which buttons exist in which report so that we
@ -429,8 +318,10 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
switch (cap.UsagePage) {
case HID_USAGE_PAGE_BUTTON:
if (_device_class == DeviceClass::gamepad) {
if (usage < sizeof(gamepad_buttons_common) / sizeof(ButtonHandle)) {
handle = gamepad_buttons[usage];
if (usage > 0 && usage - 1 < _countof(default_gamepad_mapping)) {
if (gamepad_buttons[usage - 1] != nullptr) {
handle = registry->find_button(gamepad_buttons[usage - 1]);
}
}
} else if (_device_class == DeviceClass::flight_stick) {
if (usage > 0) {
@ -491,7 +382,7 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
}
}
nassertd(cap.Range.DataIndexMin + upper < _indices.size()) continue;
nassertd(cap.Range.DataIndexMin + upper < (int)_indices.size()) continue;
for (int j = 0; j <= upper; ++j) {
USAGE usage = j + cap.Range.UsageMin;
@ -530,9 +421,17 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
break;
case HID_USAGE_GENERIC_Z:
if (_device_class == DeviceClass::gamepad) {
if ((quirks & QB_no_analog_triggers) == 0) {
axis = Axis::left_trigger;
}
} else if (_device_class == DeviceClass::flight_stick) {
axis = Axis::throttle;
if ((quirks & QB_reversed_throttle) != 0) {
std::swap(cap.LogicalMin, cap.LogicalMax);
}
if ((quirks & QB_centered_throttle) != 0) {
is_signed = false;
}
} else {
axis = Axis::z;
swap(cap.LogicalMin, cap.LogicalMax);
@ -555,7 +454,9 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
break;
case HID_USAGE_GENERIC_RZ:
if (_device_class == DeviceClass::gamepad) {
if ((quirks & QB_no_analog_triggers) == 0) {
axis = Axis::right_trigger;
}
} else {
// Flip to match Panda's convention for heading.
axis = Axis::yaw;
@ -580,10 +481,7 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
}
int axis_index;
if (_vendor_id == 0x044f && _product_id == 0xb108 && axis == Axis::throttle) {
// T.Flight Hotas X throttle is reversed and can go backwards.
axis_index = add_axis(axis, cap.LogicalMax, cap.LogicalMin, true);
} else if (!is_signed) {
if (!is_signed) {
// All axes on the weird XInput-style mappings go from -1 to 1
axis_index = add_axis(axis, cap.LogicalMin, cap.LogicalMax, true);
} else {

View File

@ -99,12 +99,12 @@ typedef struct _XINPUT_CAPABILITIES_EX {
WORD Unknown2;
} XINPUT_CAPABILITIES_EX;
typedef DWORD (*pXInputGetState)(DWORD, XINPUT_STATE *);
typedef DWORD (*pXInputSetState)(DWORD, XINPUT_VIBRATION *);
typedef DWORD (*pXInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES *);
typedef DWORD (*pXInputGetCapabilitiesEx)(DWORD, DWORD, DWORD, XINPUT_CAPABILITIES_EX *);
typedef DWORD (*pXInputGetBatteryInformation)(DWORD, BYTE, XINPUT_BATTERY_INFORMATION *);
typedef DWORD (*pXInputGetBaseBusInformation)(DWORD, XINPUT_BUSINFO *);
typedef DWORD (WINAPI *pXInputGetState)(DWORD, XINPUT_STATE *);
typedef DWORD (WINAPI *pXInputSetState)(DWORD, XINPUT_VIBRATION *);
typedef DWORD (WINAPI *pXInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES *);
typedef DWORD (WINAPI *pXInputGetCapabilitiesEx)(DWORD, DWORD, DWORD, XINPUT_CAPABILITIES_EX *);
typedef DWORD (WINAPI *pXInputGetBatteryInformation)(DWORD, BYTE, XINPUT_BATTERY_INFORMATION *);
typedef DWORD (WINAPI *pXInputGetBaseBusInformation)(DWORD, XINPUT_BUSINFO *);
static pXInputGetState get_state = nullptr;
static pXInputSetState set_state = nullptr;

View File

@ -97,6 +97,14 @@ has_tangent() const {
return (_flags & F_has_tangent) != 0;
}
/**
*
*/
INLINE bool EggVertexUV::
has_tangent4() const {
return (_flags & F_has_tangent4) != 0;
}
/**
*
*/
@ -106,6 +114,19 @@ get_tangent() const {
return _tangent;
}
/**
*
*/
INLINE LVecBase4d EggVertexUV::
get_tangent4() const {
LVecBase4d tangent4(_tangent, 1.0);
nassertr_always(has_tangent(), tangent4);
if (_flags & F_flip_computed_binormal) {
tangent4[3] = -1.0;
}
return tangent4;
}
/**
*
*/
@ -113,6 +134,22 @@ INLINE void EggVertexUV::
set_tangent(const LNormald &tangent) {
_tangent = tangent;
_flags |= F_has_tangent;
_flags &= ~(F_has_tangent4 | F_flip_computed_binormal);
}
/**
* Sets the tangent vector, along with a fourth parameter that is multiplied
* with the result of cross(normal, tangent) when computing the binormal.
*/
INLINE void EggVertexUV::
set_tangent4(const LVecBase4d &tangent) {
_tangent = tangent.get_xyz();
_flags |= F_has_tangent4 | F_has_tangent;
if (tangent[3] < 0.0) {
_flags |= F_flip_computed_binormal;
} else {
_flags &= ~F_flip_computed_binormal;
}
}
/**

View File

@ -145,7 +145,10 @@ write(std::ostream &out, int indent_level) const {
} else {
indent(out, indent_level+2) << get_uv() << "\n";
}
if (has_tangent()) {
if (has_tangent4()) {
indent(out, indent_level + 2)
<< "<Tangent> { " << get_tangent4() << " }\n";
} else if (has_tangent()) {
indent(out, indent_level + 2)
<< "<Tangent> { " << get_tangent() << " }\n";
}

View File

@ -45,8 +45,11 @@ PUBLISHED:
INLINE void set_uvw(const LTexCoord3d &texCoord);
INLINE bool has_tangent() const;
INLINE bool has_tangent4() const;
INLINE const LNormald &get_tangent() const;
INLINE LVecBase4d get_tangent4() const;
INLINE void set_tangent(const LNormald &tangent);
INLINE void set_tangent4(const LVecBase4d &tangent);
INLINE void clear_tangent();
INLINE bool has_binormal() const;
@ -69,6 +72,10 @@ private:
F_has_tangent = 0x001,
F_has_binormal = 0x002,
F_has_w = 0x004,
F_has_tangent4 = 0x008,
// Only defined temporarily as we can't add a float to this class in 1.10.
F_flip_computed_binormal = 0x010,
};
int _flags;

View File

@ -1103,6 +1103,14 @@ vertex_uv_body:
} else {
DCAST(EggVertexUV, egg_stack.back())->set_tangent(LNormald($4, $5, $6));
}
}
| vertex_uv_body TANGENT '{' real real real real '}'
{
if (DCAST(EggVertexUV, egg_stack.back())->has_tangent()) {
eggyywarning("Ignoring repeated tangent");
} else {
DCAST(EggVertexUV, egg_stack.back())->set_tangent4(LVecBase4d($4, $5, $6, $7));
}
}
| vertex_uv_body BINORMAL '{' real real real '}'
{

View File

@ -0,0 +1,84 @@
import pytest
from panda3d import core
# Skip these tests if we can't import egg.
egg = pytest.importorskip("panda3d.egg")
def read_egg_vertex(string):
"""Reads an EggVertex from a string."""
data = "<VertexPool> pool { <Vertex> 1 { %s } }" % (string)
stream = core.StringStream(data.encode('utf-8'))
data = egg.EggData()
assert data.read(stream)
pool, = data.get_children()
return pool.get_vertex(1)
def test_egg_vertex_uv_empty():
vertex = read_egg_vertex("""
0 0 0
<UV> {
0 0
}
""")
obj = vertex.get_uv_obj("")
assert not obj.has_tangent()
assert not obj.has_tangent4()
assert '<Tangent>' not in str(obj)
def test_egg_vertex_tangent():
vertex = read_egg_vertex("""
0 0 0
<UV> {
0 0
<Tangent> { 2 3 4 }
}
""")
obj = vertex.get_uv_obj("")
assert obj.has_tangent()
assert not obj.has_tangent4()
assert obj.get_tangent() == (2, 3, 4)
assert obj.get_tangent4() == (2, 3, 4, 1)
assert '{ 2 3 4 }' in str(obj)
def test_egg_vertex_tangent4_pos():
vertex = read_egg_vertex("""
0 0 0
<UV> {
0 0
<Tangent> { 2 3 4 1 }
}
""")
obj = vertex.get_uv_obj("")
assert obj.has_tangent()
assert obj.has_tangent4()
assert obj.get_tangent() == (2, 3, 4)
assert obj.get_tangent4() == (2, 3, 4, 1)
assert '{ 2 3 4 1 }' in str(obj)
def test_egg_vertex_tangent4_neg():
vertex = read_egg_vertex("""
0 0 0
<UV> {
0 0
<Tangent> { 2 3 4 -1 }
}
""")
obj = vertex.get_uv_obj("")
assert obj.has_tangent()
assert obj.has_tangent4()
assert obj.get_tangent() == (2, 3, 4)
assert obj.get_tangent4() == (2, 3, 4, -1)
assert '{ 2 3 4 -1 }' in str(obj)