diff --git a/Editor/Editor.xcodeproj/project.pbxproj b/Editor/Editor.xcodeproj/project.pbxproj
index 0d2a844b3..118ad03c3 100644
--- a/Editor/Editor.xcodeproj/project.pbxproj
+++ b/Editor/Editor.xcodeproj/project.pbxproj
@@ -65,6 +65,7 @@
1E73E7142F0510C10090D1DA /* main_MacOS.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1E73E7132F0510B40090D1DA /* main_MacOS.mm */; };
1E73E7392F0513710090D1DA /* libWickedEngine.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1E73E7382F0513620090D1DA /* libWickedEngine.a */; };
1E73E7552F053DBE0090D1DA /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1E73E7532F053DBE0090D1DA /* Metal.framework */; };
+ 1EBD1C072F251B7E00C29B34 /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1EBD1C052F251B7E00C29B34 /* GameController.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -225,6 +226,7 @@
1E73E74D2F053DAB0090D1DA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
1E73E7512F053DB80090D1DA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
1E73E7532F053DBE0090D1DA /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; };
+ 1EBD1C052F251B7E00C29B34 /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = System/Library/Frameworks/GameController.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -232,6 +234,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ 1EBD1C072F251B7E00C29B34 /* GameController.framework in Frameworks */,
1E640FDB2F0AE648003D9FC7 /* AppKit.framework in Frameworks */,
1E73E7392F0513710090D1DA /* libWickedEngine.a in Frameworks */,
1E73E7552F053DBE0090D1DA /* Metal.framework in Frameworks */,
@@ -406,6 +409,7 @@
1E73E7162F0511130090D1DA /* Frameworks */ = {
isa = PBXGroup;
children = (
+ 1EBD1C052F251B7E00C29B34 /* GameController.framework */,
1E73E7532F053DBE0090D1DA /* Metal.framework */,
1E73E7512F053DB80090D1DA /* Foundation.framework */,
1E73E74D2F053DAB0090D1DA /* AppKit.framework */,
diff --git a/README.md b/README.md
index 66c09b2ff..cd42695ec 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,6 @@
Wicked Engine is an open-source 3D engine with modern graphics. Use this as a C++ framework for your graphics projects, a standalone 3D editor, LUA scripting or just for learning.
- [Website](https://wickedengine.net/)
-- [Forum](https://wickedengine.net/forum/)
- [Features](features.txt)
- [Editor Manual](Content/Documentation/WickedEditor-Manual.pdf)
- [C++ Documentation](Content/Documentation/WickedEngine-Documentation.md)
@@ -83,7 +82,7 @@ To build the engine for Mac OS, use the provided .xcodeproj files with the Xcod
- Editor/Editor.xcodeproj to build the Wicked Editor
- Samples/Template_MacOS/Template_MacOS.xcodeproj to build a minimal template application for MacOS
-If you want to develop an application that uses Wicked Engine, you will have to link to libWickedEngine.a and `#include "WickedEngine.h"` into the source code. You also need to add references to the following Apple SDKs: `Metal`, `AppKit`. I also recommend to set up a custom working directory in the Product->Scheme->Edit Scheme->Options->custom working directory = `$(PROJECT_DIR)`, this will better match the development experience in Visual Studio. For examples, look at the Template_MacOS and Editor projects.
+If you want to develop an application that uses Wicked Engine, you will have to link to libWickedEngine.a and `#include "WickedEngine.h"` into the source code. You also need to add references to the following Apple SDKs: `Metal`, `AppKit`, `GameController`. I also recommend to set up a custom working directory in the Product->Scheme->Edit Scheme->Options->custom working directory = `$(PROJECT_DIR)`, this will better match the development experience in Visual Studio. For examples, look at the Template_MacOS and Editor projects.
#### Xbox Series X|S
Xbox Series specific extension files required for building are currently private.
diff --git a/Samples/Template_MacOS/Template_MacOS.xcodeproj/project.pbxproj b/Samples/Template_MacOS/Template_MacOS.xcodeproj/project.pbxproj
index 77c2fed02..50d0dd9cd 100644
--- a/Samples/Template_MacOS/Template_MacOS.xcodeproj/project.pbxproj
+++ b/Samples/Template_MacOS/Template_MacOS.xcodeproj/project.pbxproj
@@ -9,6 +9,7 @@
/* Begin PBXBuildFile section */
1E79298D2EFE77ED00B9E35C /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1E79298C2EFE77ED00B9E35C /* main.mm */; };
1E79298F2EFE783B00B9E35C /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1E79298E2EFE783B00B9E35C /* AppKit.framework */; };
+ 1EBD1C112F2525E900C29B34 /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1EBD1C0F2F2525E900C29B34 /* GameController.framework */; };
1EDA80B52EE2C9BD00210D41 /* libWickedEngine.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1EDA80B42EE2C9BD00210D41 /* libWickedEngine.a */; };
1EDA82202EE3802C00210D41 /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1EDA821F2EE3802C00210D41 /* Metal.framework */; };
/* End PBXBuildFile section */
@@ -26,6 +27,7 @@
/* Begin PBXFileReference section */
1E79298C2EFE77ED00B9E35C /* main.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = ""; };
1E79298E2EFE783B00B9E35C /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
+ 1EBD1C0F2F2525E900C29B34 /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = System/Library/Frameworks/GameController.framework; sourceTree = SDKROOT; };
1EDA807B2EE2C1DB00210D41 /* Template_MacOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Template_MacOS.app; sourceTree = BUILT_PRODUCTS_DIR; };
1EDA809E2EE2C5AD00210D41 /* WickedEngine.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = WickedEngine.xcodeproj; path = ../../WickedEngine/WickedEngine.xcodeproj; sourceTree = ""; };
1EDA821F2EE3802C00210D41 /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; };
@@ -38,6 +40,7 @@
files = (
1E79298F2EFE783B00B9E35C /* AppKit.framework in Frameworks */,
1EDA82202EE3802C00210D41 /* Metal.framework in Frameworks */,
+ 1EBD1C112F2525E900C29B34 /* GameController.framework in Frameworks */,
1EDA80B52EE2C9BD00210D41 /* libWickedEngine.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -65,6 +68,7 @@
1EDA809D2EE2C5AD00210D41 /* Frameworks */ = {
isa = PBXGroup;
children = (
+ 1EBD1C0F2F2525E900C29B34 /* GameController.framework */,
1E79298E2EFE783B00B9E35C /* AppKit.framework */,
1EDA821F2EE3802C00210D41 /* Metal.framework */,
1EDA809E2EE2C5AD00210D41 /* WickedEngine.xcodeproj */,
diff --git a/WickedEngine/WickedEngine.xcodeproj/project.pbxproj b/WickedEngine/WickedEngine.xcodeproj/project.pbxproj
index 85f4b5647..87bc72628 100644
--- a/WickedEngine/WickedEngine.xcodeproj/project.pbxproj
+++ b/WickedEngine/WickedEngine.xcodeproj/project.pbxproj
@@ -11,6 +11,7 @@
1E640FEA2F0B0565003D9FC7 /* libdxcompiler.dylib in Embed Libraries */ = {isa = PBXBuildFile; fileRef = 1EDA8A692EE5D68100210D41 /* libdxcompiler.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
1E640FEC2F0B0567003D9FC7 /* libmetalirconverter.dylib in Embed Libraries */ = {isa = PBXBuildFile; fileRef = 1E640FE12F0AEC0B003D9FC7 /* libmetalirconverter.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
1E79298A2EFE75E400B9E35C /* wiAppleHelper.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1E7929892EFE75BA00B9E35C /* wiAppleHelper.mm */; };
+ 1EBD1C012F2514EC00C29B34 /* wiInput_Apple.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1EBD1C002F2514E700C29B34 /* wiInput_Apple.mm */; };
1EDA7A562EE1DF2100210D41 /* wiArchive.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1EDA7A552EE1DF2100210D41 /* wiArchive.cpp */; };
1EDA7B042EE1DFE300210D41 /* wiNetwork_Linux.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1EDA7AA82EE1DFE300210D41 /* wiNetwork_Linux.cpp */; };
1EDA7B062EE1DFE300210D41 /* wiScene_Components.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1EDA7AD92EE1DFE300210D41 /* wiScene_Components.cpp */; };
@@ -299,6 +300,9 @@
1E640FE12F0AEC0B003D9FC7 /* libmetalirconverter.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libmetalirconverter.dylib; sourceTree = ""; };
1E7929892EFE75BA00B9E35C /* wiAppleHelper.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = wiAppleHelper.mm; sourceTree = ""; };
1E79298B2EFE76C500B9E35C /* wiAppleHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = wiAppleHelper.h; sourceTree = ""; };
+ 1EBD1BFF2F2514D700C29B34 /* wiInput_Apple.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = wiInput_Apple.h; sourceTree = ""; };
+ 1EBD1C002F2514E700C29B34 /* wiInput_Apple.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = wiInput_Apple.mm; sourceTree = ""; };
+ 1EBD1C032F25166100C29B34 /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = System/Library/Frameworks/GameController.framework; sourceTree = SDKROOT; };
1ED576B82F0115B40032621C /* bcn_common_api.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = bcn_common_api.h; sourceTree = ""; };
1ED576B92F0115B40032621C /* bcn_common_kernel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = bcn_common_kernel.h; sourceTree = ""; };
1ED576BA2F0115B40032621C /* common_def.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = common_def.h; sourceTree = ""; };
@@ -1624,6 +1628,14 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
+ 1EBD1C022F25166100C29B34 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 1EBD1C032F25166100C29B34 /* GameController.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
1ED576BB2F0115B40032621C /* compressonator */ = {
isa = PBXGroup;
children = (
@@ -2085,6 +2097,8 @@
1EDA71632EE0978C00210D41 = {
isa = PBXGroup;
children = (
+ 1EBD1C002F2514E700C29B34 /* wiInput_Apple.mm */,
+ 1EBD1BFF2F2514D700C29B34 /* wiInput_Apple.h */,
1E640FE12F0AEC0B003D9FC7 /* libmetalirconverter.dylib */,
1ED5785F2F0115B40032621C /* shaders */,
1E79298B2EFE76C500B9E35C /* wiAppleHelper.h */,
@@ -2271,6 +2285,7 @@
1EDA7B032EE1DFE300210D41 /* wiVoxelGrid_BindLua.cpp */,
1EDA7A552EE1DF2100210D41 /* wiArchive.cpp */,
1EDA7BB12EE1E60500210D41 /* libWickedEngine.a */,
+ 1EBD1C022F25166100C29B34 /* Frameworks */,
);
sourceTree = "";
};
@@ -3400,6 +3415,7 @@
1EDA7DB62EE1EC9500210D41 /* SkeletonMapper.cpp in Sources */,
1EDA7DB72EE1EC9500210D41 /* SkeletonPose.cpp in Sources */,
1EDA7DB82EE1EC9500210D41 /* CylinderShape.cpp in Sources */,
+ 1EBD1C012F2514EC00C29B34 /* wiInput_Apple.mm in Sources */,
1EDA7DB92EE1EC9500210D41 /* PhysicsSystem.cpp in Sources */,
1EDA7DBA2EE1EC9500210D41 /* TriangleSplitter.cpp in Sources */,
1EDA7DBB2EE1EC9500210D41 /* DebugRendererPlayback.cpp in Sources */,
diff --git a/WickedEngine/offlineshadercompiler.xcodeproj/project.pbxproj b/WickedEngine/offlineshadercompiler.xcodeproj/project.pbxproj
index c2c6ab432..54715c6cb 100644
--- a/WickedEngine/offlineshadercompiler.xcodeproj/project.pbxproj
+++ b/WickedEngine/offlineshadercompiler.xcodeproj/project.pbxproj
@@ -11,6 +11,7 @@
1E640FC92F0AE1C3003D9FC7 /* libWickedEngine.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1E640FC22F0AE07B003D9FC7 /* libWickedEngine.a */; };
1E640FD02F0AE275003D9FC7 /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1E640FCF2F0AE275003D9FC7 /* Metal.framework */; };
1E640FD42F0AE282003D9FC7 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1E640FD32F0AE282003D9FC7 /* AppKit.framework */; };
+ 1EBD1C0D2F2525BD00C29B34 /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1EBD1C0B2F2525BD00C29B34 /* GameController.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -41,6 +42,7 @@
1E640FBD2F0AE07B003D9FC7 /* WickedEngine.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = WickedEngine.xcodeproj; sourceTree = ""; };
1E640FCF2F0AE275003D9FC7 /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; };
1E640FD32F0AE282003D9FC7 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
+ 1EBD1C0B2F2525BD00C29B34 /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = System/Library/Frameworks/GameController.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -50,6 +52,7 @@
files = (
1E640FD02F0AE275003D9FC7 /* Metal.framework in Frameworks */,
1E640FD42F0AE282003D9FC7 /* AppKit.framework in Frameworks */,
+ 1EBD1C0D2F2525BD00C29B34 /* GameController.framework in Frameworks */,
1E640FC92F0AE1C3003D9FC7 /* libWickedEngine.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -77,6 +80,7 @@
1E640FBC2F0AE07B003D9FC7 /* Frameworks */ = {
isa = PBXGroup;
children = (
+ 1EBD1C0B2F2525BD00C29B34 /* GameController.framework */,
1E640FD32F0AE282003D9FC7 /* AppKit.framework */,
1E640FCF2F0AE275003D9FC7 /* Metal.framework */,
1E640FBD2F0AE07B003D9FC7 /* WickedEngine.xcodeproj */,
diff --git a/WickedEngine/wiInput.cpp b/WickedEngine/wiInput.cpp
index d2704af2c..333491548 100644
--- a/WickedEngine/wiInput.cpp
+++ b/WickedEngine/wiInput.cpp
@@ -26,6 +26,7 @@
#endif // PLATFORM_PS5
#ifdef __APPLE__
+#include "wiInput_Apple.h"
#include
#include
namespace wi::input::apple
@@ -141,6 +142,7 @@ namespace wi::input
RAWINPUT,
SDLINPUT,
PS5,
+ APPLE,
};
DeviceType deviceType;
int deviceIndex;
@@ -161,6 +163,7 @@ namespace wi::input
#endif // PLATFORM_PS5
#ifdef __APPLE__
+ wi::input::apple::Initialize();
wi::apple::CursorInit(cursor_table_original);
#endif // __APPLE__
@@ -336,6 +339,39 @@ namespace wi::input
}
}
+#ifdef __APPLE__
+ // Check if low-level Apple controller is not registered for playerindex slot and register:
+ for (int i = 0; i < wi::input::apple::GetMaxControllerCount(); ++i)
+ {
+ if (wi::input::apple::GetControllerState(nullptr, i))
+ {
+ int slot = -1;
+ for (int j = 0; j < (int)controllers.size(); ++j)
+ {
+ if (slot < 0 && controllers[j].deviceType == Controller::DISCONNECTED)
+ {
+ // take the first disconnected slot
+ slot = j;
+ }
+ if (controllers[j].deviceType == Controller::APPLE && controllers[j].deviceIndex == i)
+ {
+ // it is already registered to this slot
+ slot = j;
+ break;
+ }
+ }
+ if (slot == -1)
+ {
+ // no disconnected slot was found, and it was not registered
+ slot = (int)controllers.size();
+ controllers.emplace_back();
+ }
+ controllers[slot].deviceType = Controller::APPLE;
+ controllers[slot].deviceIndex = i;
+ }
+ }
+#endif // __APPLE__
+
#ifdef PLATFORM_PS5
// Check if low-level PS5 controller is not registered for playerindex slot and register:
for (int i = 0; i < wi::input::ps5::GetMaxControllerCount(); ++i)
@@ -389,6 +425,11 @@ namespace wi::input
connected = wi::input::ps5::GetControllerState(&controller.state, controller.deviceIndex);
#endif // PLATFORM_PS5
break;
+ case Controller::APPLE:
+#ifdef __APPLE__
+ connected = wi::input::apple::GetControllerState(&controller.state, controller.deviceIndex);
+#endif // __APPLE__
+ break;
case Controller::DISCONNECTED:
connected = false;
break;
@@ -1132,6 +1173,12 @@ namespace wi::input
{
wi::input::sdlinput::SetControllerFeedback(data, controller.deviceIndex);
}
+#ifdef __APPLE__
+ else if (controller.deviceType == Controller::APPLE)
+ {
+ wi::input::apple::SetControllerFeedback(data, controller.deviceIndex);
+ }
+#endif // __APPLE__
#ifdef PLATFORM_PS5
else if (controller.deviceType == Controller::PS5)
{
diff --git a/WickedEngine/wiInput_Apple.h b/WickedEngine/wiInput_Apple.h
new file mode 100644
index 000000000..5f095e47e
--- /dev/null
+++ b/WickedEngine/wiInput_Apple.h
@@ -0,0 +1,20 @@
+#pragma once
+#include "CommonInclude.h"
+#include "wiInput.h"
+
+namespace wi::input::apple
+{
+// Initialize once
+void Initialize();
+
+// Returns how many gamepads can Xinput handle
+int GetMaxControllerCount();
+
+// Returns whether the controller identified by index parameter is available or not.
+// Id state parameter is not nullptr, and the controller is available, the state will be written into it
+bool GetControllerState(wi::input::ControllerState* state, int index);
+
+// Sends feedback data for the controller identified by index parameter to output
+void SetControllerFeedback(const wi::input::ControllerFeedback& data, int index);
+}
+
diff --git a/WickedEngine/wiInput_Apple.mm b/WickedEngine/wiInput_Apple.mm
new file mode 100644
index 000000000..ef3ca46a5
--- /dev/null
+++ b/WickedEngine/wiInput_Apple.mm
@@ -0,0 +1,151 @@
+#include "wiInput_Apple.h"
+
+#import
+#import
+
+#include
+
+namespace wi::input::apple
+{
+static std::mutex locker;
+static GCController* controllers[8] = {};
+
+void Initialize()
+{
+ @autoreleasepool {
+
+ // Register new controller event:
+ [[NSNotificationCenter defaultCenter] addObserverForName:GCControllerDidConnectNotification
+ object:nil
+ queue:[NSOperationQueue mainQueue]
+ usingBlock:^(NSNotification *note) {
+ GCController *controller = note.object;
+ std::scoped_lock lck(locker);
+ for (int i = 0; i < arraysize(controllers); ++i)
+ {
+ if (controllers[i] == nullptr)
+ {
+ controllers[i] = controller;
+ break;
+ }
+ }
+ }];
+
+ // Remove controller event:
+ [[NSNotificationCenter defaultCenter] addObserverForName:GCControllerDidDisconnectNotification
+ object:nil
+ queue:[NSOperationQueue mainQueue]
+ usingBlock:^(NSNotification *note) {
+ GCController *controller = note.object;
+ std::scoped_lock lck(locker);
+ for (int i = 0; i < arraysize(controllers); ++i)
+ {
+ if (controllers[i] == controller)
+ {
+ controllers[i] = nullptr;
+ break;
+ }
+ }
+ }];
+
+ // Register already connected controllers:
+ for (GCController *controller in [GCController controllers])
+ {
+ std::scoped_lock lck(locker);
+ for (int i = 0; i < arraysize(controllers); ++i)
+ {
+ if (controllers[i] == nullptr)
+ {
+ controllers[i] = controller;
+ break;
+ }
+ }
+ }
+ }
+}
+
+int GetMaxControllerCount()
+{
+ return arraysize(controllers);
+}
+
+constexpr float deadzone(float x)
+{
+ if (x > -0.24f && x < 0.24f)
+ return 0.f;
+ return clamp(x, -1.0f, 1.0f);
+}
+
+
+constexpr uint64_t buttonToBit(int button)
+{
+ return 1ull << (button - GAMEPAD_RANGE_START - 1);
+}
+bool GetControllerState(wi::input::ControllerState* state, int index)
+{
+ std::scoped_lock lck(locker);
+ if (index < 0 || index >= arraysize(controllers))
+ return false;
+
+ GCController* controller = controllers[index];
+ if (controller == nullptr)
+ return false;
+
+ GCExtendedGamepad* pad = controller.extendedGamepad;
+ if (pad == nullptr)
+ return false;
+
+ if (state)
+ {
+ *state = wi::input::ControllerState();
+
+ if (pad.dpad.up.pressed) state->buttons |= buttonToBit(GAMEPAD_BUTTON_UP);
+ if (pad.dpad.left.pressed) state->buttons |= buttonToBit(GAMEPAD_BUTTON_LEFT);
+ if (pad.dpad.down.pressed) state->buttons |= buttonToBit(GAMEPAD_BUTTON_DOWN);
+ if (pad.dpad.right.pressed) state->buttons |= buttonToBit(GAMEPAD_BUTTON_RIGHT);
+
+ // It looks like this exactly matches the xbox layout:
+ if (pad.buttonX.pressed) state->buttons |= buttonToBit(GAMEPAD_BUTTON_XBOX_X);
+ if (pad.buttonA.pressed) state->buttons |= buttonToBit(GAMEPAD_BUTTON_XBOX_A);
+ if (pad.buttonB.pressed) state->buttons |= buttonToBit(GAMEPAD_BUTTON_XBOX_B);
+ if (pad.buttonY.pressed) state->buttons |= buttonToBit(GAMEPAD_BUTTON_XBOX_Y);
+
+ if (pad.leftShoulder.pressed) state->buttons |= buttonToBit(GAMEPAD_BUTTON_XBOX_L1);
+ if (pad.rightShoulder.pressed) state->buttons |= buttonToBit(GAMEPAD_BUTTON_XBOX_R1);
+
+ if (pad.leftThumbstickButton.pressed) state->buttons |= buttonToBit(GAMEPAD_BUTTON_XBOX_L3);
+ if (pad.rightThumbstickButton.pressed) state->buttons |= buttonToBit(GAMEPAD_BUTTON_XBOX_R3);
+
+ if (pad.buttonMenu.pressed) state->buttons |= buttonToBit(GAMEPAD_BUTTON_XBOX_START);
+ if (pad.buttonOptions.pressed) state->buttons |= buttonToBit(GAMEPAD_BUTTON_XBOX_BACK);
+
+ state->thumbstick_L = XMFLOAT2(deadzone(pad.leftThumbstick.xAxis.value), deadzone(pad.leftThumbstick.yAxis.value));
+ state->thumbstick_R = XMFLOAT2(deadzone(pad.rightThumbstick.xAxis.value), -deadzone(pad.rightThumbstick.yAxis.value));
+
+ state->trigger_L = pad.leftTrigger.value;
+ state->trigger_R = pad.rightTrigger.value;
+ }
+
+ return true;
+}
+
+void SetControllerFeedback(const wi::input::ControllerFeedback& data, int index)
+{
+ std::scoped_lock lck(locker);
+ if (index < 0 || index >= arraysize(controllers))
+ return;
+
+ GCController* controller = controllers[index];
+ if (controller == nullptr)
+ return;
+
+ if (controller.light)
+ {
+ XMFLOAT3 color = data.led_color.toFloat3();
+ controller.light.color = [[GCColor alloc] initWithRed:color.x green:color.y blue:color.z];
+ }
+
+ // TODO: vibration
+}
+
+}
diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp
index 86adad5f3..742061907 100644
--- a/WickedEngine/wiVersion.cpp
+++ b/WickedEngine/wiVersion.cpp
@@ -9,7 +9,7 @@ namespace wi::version
// minor features, major updates, breaking compatibility changes
const int minor = 72;
// minor bug fixes, alterations, refactors, updates
- const int revision = 17;
+ const int revision = 18;
const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);