mirror of
https://github.com/panda3d/panda3d.git
synced 2025-09-30 00:32:57 -04:00
Merge branch 'release/1.10.x'
This commit is contained in:
commit
36dd8889e7
@ -12,6 +12,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "configVariableBool.h"
|
#include "configVariableBool.h"
|
||||||
|
#include "mutexImpl.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refreshes the cached value.
|
* Refreshes the cached value.
|
||||||
@ -21,7 +22,10 @@ reload_value() const {
|
|||||||
// NB. MSVC doesn't guarantee that this mutex is initialized in a
|
// 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
|
// 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.
|
// 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();
|
lock.lock();
|
||||||
|
|
||||||
// We check again for cache validity since another thread may have beaten
|
// We check again for cache validity since another thread may have beaten
|
||||||
|
@ -288,6 +288,119 @@ test_intersection_from_segment(const CollisionEntry &entry) const {
|
|||||||
return new_entry;
|
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
|
* Fills the _viz_geom GeomNode up with Geoms suitable for rendering this
|
||||||
* solid.
|
* solid.
|
||||||
|
@ -55,6 +55,10 @@ protected:
|
|||||||
test_intersection_from_ray(const CollisionEntry &entry) const;
|
test_intersection_from_ray(const CollisionEntry &entry) const;
|
||||||
virtual PT(CollisionEntry)
|
virtual PT(CollisionEntry)
|
||||||
test_intersection_from_segment(const CollisionEntry &entry) const;
|
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();
|
virtual void fill_viz_geom();
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ static const struct DeviceMapping {
|
|||||||
{0x28de, 0x1142, InputDevice::DeviceClass::unknown, QB_steam_controller},
|
{0x28de, 0x1142, InputDevice::DeviceClass::unknown, QB_steam_controller},
|
||||||
// Jess Tech Colour Rumble Pad
|
// Jess Tech Colour Rumble Pad
|
||||||
{0x0f30, 0x0111, InputDevice::DeviceClass::gamepad, 0},
|
{0x0f30, 0x0111, InputDevice::DeviceClass::gamepad, 0},
|
||||||
// Trust GXT 24
|
// SPEED Link SL-6535-SBK-01
|
||||||
{0x0079, 0x0006, InputDevice::DeviceClass::gamepad, 0},
|
{0x0079, 0x0006, InputDevice::DeviceClass::gamepad, 0},
|
||||||
// 3Dconnexion Space Traveller 3D Mouse
|
// 3Dconnexion Space Traveller 3D Mouse
|
||||||
{0x046d, 0xc623, InputDevice::DeviceClass::spatial_mouse, 0},
|
{0x046d, 0xc623, InputDevice::DeviceClass::spatial_mouse, 0},
|
||||||
@ -521,7 +521,10 @@ init_device() {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ABS_THROTTLE:
|
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;
|
axis = InputDevice::Axis::rudder;
|
||||||
} else {
|
} else {
|
||||||
axis = InputDevice::Axis::throttle;
|
axis = InputDevice::Axis::throttle;
|
||||||
|
166
panda/src/device/phidsdi.h
Normal file
166
panda/src/device/phidsdi.h
Normal 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
|
@ -289,8 +289,8 @@ on_input_device_arrival(HANDLE handle) {
|
|||||||
|
|
||||||
// Some devices insert quite some trailing space here.
|
// Some devices insert quite some trailing space here.
|
||||||
wchar_t *wbuffer = (wchar_t *)buffer;
|
wchar_t *wbuffer = (wchar_t *)buffer;
|
||||||
size_t wlen = wcslen(wbuffer);
|
size_t wlen = wcsnlen_s(wbuffer, sizeof(buffer) / sizeof(wchar_t));
|
||||||
while (iswspace(wbuffer[wlen - 1])) {
|
while (wlen > 0 && iswspace(wbuffer[wlen - 1])) {
|
||||||
wbuffer[--wlen] = 0;
|
wbuffer[--wlen] = 0;
|
||||||
}
|
}
|
||||||
TextEncoder encoder;
|
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.
|
* Implementation of the message loop.
|
||||||
*/
|
*/
|
||||||
|
@ -52,9 +52,11 @@ private:
|
|||||||
pmap<HANDLE, WinRawInputDevice *> _raw_devices;
|
pmap<HANDLE, WinRawInputDevice *> _raw_devices;
|
||||||
pmap<std::string, WinRawInputDevice *> _raw_devices_by_path;
|
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);
|
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;
|
pCM_Get_DevNode_Property _CM_Get_DevNode_PropertyW;
|
||||||
|
|
||||||
friend class InputDeviceManager;
|
friend class InputDeviceManager;
|
||||||
|
@ -14,156 +14,56 @@
|
|||||||
#include "winRawInputDevice.h"
|
#include "winRawInputDevice.h"
|
||||||
#include "gamepadButton.h"
|
#include "gamepadButton.h"
|
||||||
#include "mouseButton.h"
|
#include "mouseButton.h"
|
||||||
|
#include "buttonRegistry.h"
|
||||||
|
|
||||||
#if defined(_WIN32) && !defined(CPPPARSER)
|
#if defined(_WIN32) && !defined(CPPPARSER)
|
||||||
|
|
||||||
#include <CfgMgr32.h>
|
#include <CfgMgr32.h>
|
||||||
#include <devpkey.h>
|
#include <devpkey.h>
|
||||||
|
#include "phidsdi.h"
|
||||||
|
|
||||||
// Copy definitions from hidusage.h, until we can drop support for the 7.1 SDK
|
enum QuirkBits : int {
|
||||||
typedef USHORT USAGE, *PUSAGE;
|
// Has no trigger axes.
|
||||||
|
QB_no_analog_triggers = 1,
|
||||||
|
|
||||||
#define HID_USAGE_PAGE_UNDEFINED ((USAGE) 0x00)
|
// Throttle goes from -1 to 1 rather than from 0 to 1.
|
||||||
#define HID_USAGE_PAGE_GENERIC ((USAGE) 0x01)
|
QB_centered_throttle = 2,
|
||||||
#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)
|
// Throttle is reversed.
|
||||||
#define HID_USAGE_GENERIC_MOUSE ((USAGE) 0x02)
|
QB_reversed_throttle = 4,
|
||||||
#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)
|
// Some nonstandard gamepads have different button mappings.
|
||||||
#define HID_USAGE_GENERIC_Y ((USAGE) 0x31)
|
static const struct DeviceMapping {
|
||||||
#define HID_USAGE_GENERIC_Z ((USAGE) 0x32)
|
unsigned short vendor;
|
||||||
#define HID_USAGE_GENERIC_RX ((USAGE) 0x33)
|
unsigned short product;
|
||||||
#define HID_USAGE_GENERIC_RY ((USAGE) 0x34)
|
InputDevice::DeviceClass device_class;
|
||||||
#define HID_USAGE_GENERIC_RZ ((USAGE) 0x35)
|
int quirks;
|
||||||
#define HID_USAGE_GENERIC_SLIDER ((USAGE) 0x36)
|
const char *buttons[16];
|
||||||
#define HID_USAGE_GENERIC_DIAL ((USAGE) 0x37)
|
} mapping_presets[] = {
|
||||||
#define HID_USAGE_GENERIC_WHEEL ((USAGE) 0x38)
|
// SNES-style USB gamepad
|
||||||
#define HID_USAGE_GENERIC_HATSWITCH ((USAGE) 0x39)
|
{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
|
// This is our fallback button mapping, used with Xbox 360 and other devices.
|
||||||
#define HIDP_STATUS_SUCCESS ((NTSTATUS)(0x11 << 16))
|
static const char *default_gamepad_mapping[16] = {
|
||||||
|
"face_a", "face_b", "face_x", "face_y", "lshoulder", "rshoulder", "back", "start", "lstick", "rstick"
|
||||||
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);
|
|
||||||
|
|
||||||
static pHidP_GetCaps _HidP_GetCaps = nullptr;
|
static pHidP_GetCaps _HidP_GetCaps = nullptr;
|
||||||
static pHidP_GetButtonCaps _HidP_GetButtonCaps = 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);
|
_name = std::move(name);
|
||||||
|
|
||||||
|
int quirks = 0;
|
||||||
|
const char *const *gamepad_buttons = default_gamepad_mapping;
|
||||||
|
|
||||||
switch (info.dwType) {
|
switch (info.dwType) {
|
||||||
case RIM_TYPEMOUSE:
|
case RIM_TYPEMOUSE:
|
||||||
_device_class = DeviceClass::mouse;
|
_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) {
|
info.hid.usUsage == HID_USAGE_GENERIC_GAMEPAD) {
|
||||||
_device_class = DeviceClass::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 &&
|
} else if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC &&
|
||||||
info.hid.usUsage == HID_USAGE_GENERIC_JOYSTICK) {
|
info.hid.usUsage == HID_USAGE_GENERIC_JOYSTICK) {
|
||||||
_device_class = DeviceClass::flight_stick;
|
_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...
|
// Well, it claims to be a gamepad...
|
||||||
_device_class = DeviceClass::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
|
// Mice
|
||||||
} else if (info.hid.usUsagePage == HID_USAGE_PAGE_GENERIC &&
|
} 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;
|
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.
|
// Initialize hid.dll, which provides the HID parser functions.
|
||||||
static bool hid_initialized = false;
|
static bool hid_initialized = false;
|
||||||
if (!hid_initialized) {
|
if (!hid_initialized) {
|
||||||
@ -342,38 +262,7 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
|
|||||||
<< caps.NumberInputValueCaps << " value caps\n";
|
<< caps.NumberInputValueCaps << " value caps\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do we have a button mapping?
|
ButtonRegistry *registry = ButtonRegistry::ptr();
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare a mapping of data indices to button/axis indices.
|
// Prepare a mapping of data indices to button/axis indices.
|
||||||
_indices.resize(caps.NumberInputDataIndices);
|
_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
|
// 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
|
// 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) {
|
switch (cap.UsagePage) {
|
||||||
case HID_USAGE_PAGE_BUTTON:
|
case HID_USAGE_PAGE_BUTTON:
|
||||||
if (_device_class == DeviceClass::gamepad) {
|
if (_device_class == DeviceClass::gamepad) {
|
||||||
if (usage < sizeof(gamepad_buttons_common) / sizeof(ButtonHandle)) {
|
if (usage > 0 && usage - 1 < _countof(default_gamepad_mapping)) {
|
||||||
handle = gamepad_buttons[usage];
|
if (gamepad_buttons[usage - 1] != nullptr) {
|
||||||
|
handle = registry->find_button(gamepad_buttons[usage - 1]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (_device_class == DeviceClass::flight_stick) {
|
} else if (_device_class == DeviceClass::flight_stick) {
|
||||||
if (usage > 0) {
|
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) {
|
for (int j = 0; j <= upper; ++j) {
|
||||||
USAGE usage = j + cap.Range.UsageMin;
|
USAGE usage = j + cap.Range.UsageMin;
|
||||||
@ -530,9 +421,17 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
|
|||||||
break;
|
break;
|
||||||
case HID_USAGE_GENERIC_Z:
|
case HID_USAGE_GENERIC_Z:
|
||||||
if (_device_class == DeviceClass::gamepad) {
|
if (_device_class == DeviceClass::gamepad) {
|
||||||
axis = Axis::left_trigger;
|
if ((quirks & QB_no_analog_triggers) == 0) {
|
||||||
|
axis = Axis::left_trigger;
|
||||||
|
}
|
||||||
} else if (_device_class == DeviceClass::flight_stick) {
|
} else if (_device_class == DeviceClass::flight_stick) {
|
||||||
axis = Axis::throttle;
|
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 {
|
} else {
|
||||||
axis = Axis::z;
|
axis = Axis::z;
|
||||||
swap(cap.LogicalMin, cap.LogicalMax);
|
swap(cap.LogicalMin, cap.LogicalMax);
|
||||||
@ -555,7 +454,9 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
|
|||||||
break;
|
break;
|
||||||
case HID_USAGE_GENERIC_RZ:
|
case HID_USAGE_GENERIC_RZ:
|
||||||
if (_device_class == DeviceClass::gamepad) {
|
if (_device_class == DeviceClass::gamepad) {
|
||||||
axis = Axis::right_trigger;
|
if ((quirks & QB_no_analog_triggers) == 0) {
|
||||||
|
axis = Axis::right_trigger;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Flip to match Panda's convention for heading.
|
// Flip to match Panda's convention for heading.
|
||||||
axis = Axis::yaw;
|
axis = Axis::yaw;
|
||||||
@ -580,10 +481,7 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int axis_index;
|
int axis_index;
|
||||||
if (_vendor_id == 0x044f && _product_id == 0xb108 && axis == Axis::throttle) {
|
if (!is_signed) {
|
||||||
// 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) {
|
|
||||||
// All axes on the weird XInput-style mappings go from -1 to 1
|
// All axes on the weird XInput-style mappings go from -1 to 1
|
||||||
axis_index = add_axis(axis, cap.LogicalMin, cap.LogicalMax, true);
|
axis_index = add_axis(axis, cap.LogicalMin, cap.LogicalMax, true);
|
||||||
} else {
|
} else {
|
||||||
|
@ -99,12 +99,12 @@ typedef struct _XINPUT_CAPABILITIES_EX {
|
|||||||
WORD Unknown2;
|
WORD Unknown2;
|
||||||
} XINPUT_CAPABILITIES_EX;
|
} XINPUT_CAPABILITIES_EX;
|
||||||
|
|
||||||
typedef DWORD (*pXInputGetState)(DWORD, XINPUT_STATE *);
|
typedef DWORD (WINAPI *pXInputGetState)(DWORD, XINPUT_STATE *);
|
||||||
typedef DWORD (*pXInputSetState)(DWORD, XINPUT_VIBRATION *);
|
typedef DWORD (WINAPI *pXInputSetState)(DWORD, XINPUT_VIBRATION *);
|
||||||
typedef DWORD (*pXInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES *);
|
typedef DWORD (WINAPI *pXInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES *);
|
||||||
typedef DWORD (*pXInputGetCapabilitiesEx)(DWORD, DWORD, DWORD, XINPUT_CAPABILITIES_EX *);
|
typedef DWORD (WINAPI *pXInputGetCapabilitiesEx)(DWORD, DWORD, DWORD, XINPUT_CAPABILITIES_EX *);
|
||||||
typedef DWORD (*pXInputGetBatteryInformation)(DWORD, BYTE, XINPUT_BATTERY_INFORMATION *);
|
typedef DWORD (WINAPI *pXInputGetBatteryInformation)(DWORD, BYTE, XINPUT_BATTERY_INFORMATION *);
|
||||||
typedef DWORD (*pXInputGetBaseBusInformation)(DWORD, XINPUT_BUSINFO *);
|
typedef DWORD (WINAPI *pXInputGetBaseBusInformation)(DWORD, XINPUT_BUSINFO *);
|
||||||
|
|
||||||
static pXInputGetState get_state = nullptr;
|
static pXInputGetState get_state = nullptr;
|
||||||
static pXInputSetState set_state = nullptr;
|
static pXInputSetState set_state = nullptr;
|
||||||
|
@ -97,6 +97,14 @@ has_tangent() const {
|
|||||||
return (_flags & F_has_tangent) != 0;
|
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;
|
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) {
|
set_tangent(const LNormald &tangent) {
|
||||||
_tangent = tangent;
|
_tangent = tangent;
|
||||||
_flags |= F_has_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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -145,7 +145,10 @@ write(std::ostream &out, int indent_level) const {
|
|||||||
} else {
|
} else {
|
||||||
indent(out, indent_level+2) << get_uv() << "\n";
|
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)
|
indent(out, indent_level + 2)
|
||||||
<< "<Tangent> { " << get_tangent() << " }\n";
|
<< "<Tangent> { " << get_tangent() << " }\n";
|
||||||
}
|
}
|
||||||
|
@ -45,8 +45,11 @@ PUBLISHED:
|
|||||||
INLINE void set_uvw(const LTexCoord3d &texCoord);
|
INLINE void set_uvw(const LTexCoord3d &texCoord);
|
||||||
|
|
||||||
INLINE bool has_tangent() const;
|
INLINE bool has_tangent() const;
|
||||||
|
INLINE bool has_tangent4() const;
|
||||||
INLINE const LNormald &get_tangent() const;
|
INLINE const LNormald &get_tangent() const;
|
||||||
|
INLINE LVecBase4d get_tangent4() const;
|
||||||
INLINE void set_tangent(const LNormald &tangent);
|
INLINE void set_tangent(const LNormald &tangent);
|
||||||
|
INLINE void set_tangent4(const LVecBase4d &tangent);
|
||||||
INLINE void clear_tangent();
|
INLINE void clear_tangent();
|
||||||
|
|
||||||
INLINE bool has_binormal() const;
|
INLINE bool has_binormal() const;
|
||||||
@ -69,6 +72,10 @@ private:
|
|||||||
F_has_tangent = 0x001,
|
F_has_tangent = 0x001,
|
||||||
F_has_binormal = 0x002,
|
F_has_binormal = 0x002,
|
||||||
F_has_w = 0x004,
|
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;
|
int _flags;
|
||||||
|
@ -1103,6 +1103,14 @@ vertex_uv_body:
|
|||||||
} else {
|
} else {
|
||||||
DCAST(EggVertexUV, egg_stack.back())->set_tangent(LNormald($4, $5, $6));
|
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 '}'
|
| vertex_uv_body BINORMAL '{' real real real '}'
|
||||||
{
|
{
|
||||||
|
84
tests/egg/test_egg_vertex_uv.py
Normal file
84
tests/egg/test_egg_vertex_uv.py
Normal 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)
|
Loading…
x
Reference in New Issue
Block a user