mirror of
https://github.com/storytold/UE4-OSC.git
synced 2026-10-09 00:09:43 +00:00
can bind OSC message to game inputs
This commit is contained in:
@@ -17,6 +17,9 @@ namespace UnrealBuildTool.Rules
|
|||||||
"CoreUObject",
|
"CoreUObject",
|
||||||
"Engine",
|
"Engine",
|
||||||
"Sockets",
|
"Sockets",
|
||||||
|
"InputCore",
|
||||||
|
"Slate",
|
||||||
|
"SlateCore",
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -121,6 +121,7 @@ struct FOscDataElemStruct
|
|||||||
|
|
||||||
//-------------------------------------------------------------------------
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// Cast the value to T.
|
||||||
template <class T>
|
template <class T>
|
||||||
T GetValue() const
|
T GetValue() const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -61,8 +61,12 @@ public:
|
|||||||
virtual void ShutdownModule( ) override
|
virtual void ShutdownModule( ) override
|
||||||
{
|
{
|
||||||
UE_LOG(LogOSC, Display, TEXT("Shutdown"));
|
UE_LOG(LogOSC, Display, TEXT("Shutdown"));
|
||||||
|
|
||||||
if(_dispatcher.IsValid())
|
if(_dispatcher.IsValid())
|
||||||
{
|
{
|
||||||
|
auto settings = GetMutableDefault<UOscSettings>();
|
||||||
|
settings->ClearKeyInputs(*_dispatcher);
|
||||||
|
|
||||||
_dispatcher->Stop();
|
_dispatcher->Stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,6 +98,9 @@ public:
|
|||||||
// send settings
|
// send settings
|
||||||
settings->UpdateSendAddresses();
|
settings->UpdateSendAddresses();
|
||||||
|
|
||||||
|
// input settings
|
||||||
|
settings->UpdateKeyInputs(*_dispatcher);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
#include "OscPrivatePCH.h"
|
#include "OscPrivatePCH.h"
|
||||||
|
|
||||||
|
#include "OscReceiverInputKey.h"
|
||||||
|
#include "OscDispatcher.h"
|
||||||
|
|
||||||
|
|
||||||
UOscSettings::UOscSettings( const class FPostConstructInitializeProperties& PCIP )
|
UOscSettings::UOscSettings( const class FPostConstructInitializeProperties& PCIP )
|
||||||
: Super(PCIP),
|
: Super(PCIP),
|
||||||
@@ -100,3 +103,24 @@ bool UOscSettings::Parse(const FString & address_port, FIPv4Address * address, u
|
|||||||
*port = portResult;
|
*port = portResult;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UOscSettings::ClearKeyInputs(UOscDispatcher & dispatcher)
|
||||||
|
{
|
||||||
|
for(auto & receiver : _keyReceivers)
|
||||||
|
{
|
||||||
|
// Unregister here, not in the OscReceiverInputKey destructor
|
||||||
|
// because it would crash at the application exit.
|
||||||
|
dispatcher.UnregisterReceiver(&receiver);
|
||||||
|
}
|
||||||
|
_keyReceivers.Reset(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UOscSettings::UpdateKeyInputs(UOscDispatcher & dispatcher)
|
||||||
|
{
|
||||||
|
ClearKeyInputs(dispatcher);
|
||||||
|
for(const auto & address : Inputs)
|
||||||
|
{
|
||||||
|
_keyReceivers.Emplace(address);
|
||||||
|
dispatcher.RegisterReceiver(&_keyReceivers.Last());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include "OscSettings.generated.h"
|
#include "OscSettings.generated.h"
|
||||||
|
|
||||||
|
class OscReceiverInputKey;
|
||||||
|
class UOscDispatcher;
|
||||||
|
|
||||||
|
|
||||||
UCLASS(config=Engine)
|
UCLASS(config=Engine)
|
||||||
class UOscSettings : public UObject
|
class UOscSettings : public UObject
|
||||||
@@ -27,11 +28,24 @@ public:
|
|||||||
UPROPERTY(Config, EditAnywhere, Category=Send)
|
UPROPERTY(Config, EditAnywhere, Category=Send)
|
||||||
TArray<FString> SendTargets;
|
TArray<FString> SendTargets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of the messages treated as inputs.
|
||||||
|
*
|
||||||
|
* Every entry adds a key in the input mapping project setting.
|
||||||
|
* e.g. "/position/x" -> "OSC_position_x"
|
||||||
|
*/
|
||||||
|
UPROPERTY(Config, EditAnywhere, Category=Input)
|
||||||
|
TArray<FString> Inputs;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void UpdateSendAddresses();
|
void UpdateSendAddresses();
|
||||||
|
|
||||||
void Send(const uint8 *buffer, int32 length, int32 targetIndex);
|
void Send(const uint8 *buffer, int32 length, int32 targetIndex);
|
||||||
|
|
||||||
|
void ClearKeyInputs(UOscDispatcher & dispatcher);
|
||||||
|
|
||||||
|
void UpdateKeyInputs(UOscDispatcher & dispatcher);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -43,4 +57,5 @@ public:
|
|||||||
private:
|
private:
|
||||||
TSharedRef<FSocket> _sendSocket;
|
TSharedRef<FSocket> _sendSocket;
|
||||||
TArray<std::pair<FString, TSharedRef<FInternetAddr>>> _sendAddresses;
|
TArray<std::pair<FString, TSharedRef<FInternetAddr>>> _sendAddresses;
|
||||||
|
TArray<OscReceiverInputKey> _keyReceivers;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -70,11 +70,13 @@ void UOscDispatcher::Stop()
|
|||||||
|
|
||||||
void UOscDispatcher::RegisterReceiver(IOscReceiverInterface * receiver)
|
void UOscDispatcher::RegisterReceiver(IOscReceiverInterface * receiver)
|
||||||
{
|
{
|
||||||
|
FScopeLock ScopeLock(&_receiversMutex);
|
||||||
_receivers.AddUnique(receiver);
|
_receivers.AddUnique(receiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UOscDispatcher::UnregisterReceiver(IOscReceiverInterface * receiver)
|
void UOscDispatcher::UnregisterReceiver(IOscReceiverInterface * receiver)
|
||||||
{
|
{
|
||||||
|
FScopeLock ScopeLock(&_receiversMutex);
|
||||||
_receivers.Remove(receiver);
|
_receivers.Remove(receiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,6 +178,8 @@ void UOscDispatcher::Callback(const FArrayReaderPtr& data, const FIPv4Endpoint&)
|
|||||||
|
|
||||||
void UOscDispatcher::CallbackMainThread()
|
void UOscDispatcher::CallbackMainThread()
|
||||||
{
|
{
|
||||||
|
FScopeLock ScopeLock(&_receiversMutex);
|
||||||
|
|
||||||
std::pair<FName, TArray<FOscDataElemStruct>> message;
|
std::pair<FName, TArray<FOscDataElemStruct>> message;
|
||||||
while(_pendingMessages.Dequeue(message))
|
while(_pendingMessages.Dequeue(message))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#include "OscDataElemStruct.h"
|
#include "OscDataElemStruct.h"
|
||||||
#include "OscDispatcher.generated.h"
|
#include "OscDispatcher.generated.h"
|
||||||
|
|
||||||
class IOscReceiverInterface;
|
struct IOscReceiverInterface;
|
||||||
class FSocket;
|
class FSocket;
|
||||||
class FUdpSocketReceiver;
|
class FUdpSocketReceiver;
|
||||||
|
|
||||||
@@ -45,4 +45,7 @@ private:
|
|||||||
FSocket * _socket;
|
FSocket * _socket;
|
||||||
FUdpSocketReceiver * _socketReceiver;
|
FUdpSocketReceiver * _socketReceiver;
|
||||||
TCircularQueue<std::pair<FName, TArray<FOscDataElemStruct>>> _pendingMessages;
|
TCircularQueue<std::pair<FName, TArray<FOscDataElemStruct>>> _pendingMessages;
|
||||||
|
|
||||||
|
/// Protects _receivers
|
||||||
|
FCriticalSection _receiversMutex;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,12 +4,13 @@
|
|||||||
|
|
||||||
|
|
||||||
AOscReceiverActor::AOscReceiverActor(const class FPostConstructInitializeProperties& PCIP)
|
AOscReceiverActor::AOscReceiverActor(const class FPostConstructInitializeProperties& PCIP)
|
||||||
: Super(PCIP)
|
: Super(PCIP),
|
||||||
|
_listener(this)
|
||||||
{
|
{
|
||||||
auto instance = UOscDispatcher::Get();
|
auto instance = UOscDispatcher::Get();
|
||||||
if (instance && !HasAnyFlags(RF_ClassDefaultObject))
|
if (instance && !HasAnyFlags(RF_ClassDefaultObject))
|
||||||
{
|
{
|
||||||
instance->RegisterReceiver(this);
|
instance->RegisterReceiver(&_listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,7 +19,7 @@ void AOscReceiverActor::BeginDestroy()
|
|||||||
auto instance = UOscDispatcher::Get();
|
auto instance = UOscDispatcher::Get();
|
||||||
if (instance && !HasAnyFlags(RF_ClassDefaultObject))
|
if (instance && !HasAnyFlags(RF_ClassDefaultObject))
|
||||||
{
|
{
|
||||||
instance->UnregisterReceiver(this);
|
instance->UnregisterReceiver(&_listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
Super::BeginDestroy();
|
Super::BeginDestroy();
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
|
|
||||||
UCLASS()
|
UCLASS()
|
||||||
class AOscReceiverActor : public AActor, public IOscReceiverInterface
|
class AOscReceiverActor : public AActor
|
||||||
{
|
{
|
||||||
GENERATED_UCLASS_BODY()
|
GENERATED_UCLASS_BODY()
|
||||||
|
|
||||||
@@ -17,17 +17,20 @@ class AOscReceiverActor : public AActor, public IOscReceiverInterface
|
|||||||
UFUNCTION(BlueprintImplementableEvent, Category=OSC)
|
UFUNCTION(BlueprintImplementableEvent, Category=OSC)
|
||||||
void OnOscReceived(const FName & Address, const TArray<FOscDataElemStruct> & Data);
|
void OnOscReceived(const FName & Address, const TArray<FOscDataElemStruct> & Data);
|
||||||
|
|
||||||
protected:
|
public:
|
||||||
|
const FString & GetAddressFilter() const
|
||||||
virtual void BeginDestroy() override;
|
|
||||||
|
|
||||||
virtual const FString & GetAddressFilter() override
|
|
||||||
{
|
{
|
||||||
return AddressFilter;
|
return AddressFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void SendEvent(const FName & Address, const TArray<FOscDataElemStruct> & Data) override
|
void SendEvent(const FName & Address, const TArray<FOscDataElemStruct> & Data)
|
||||||
{
|
{
|
||||||
OnOscReceived(Address, Data);
|
OnOscReceived(Address, Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void BeginDestroy() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
BasicOscReceiver<AOscReceiverActor> _listener;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
|
|
||||||
|
|
||||||
UOscReceiverComponent::UOscReceiverComponent(const class FPostConstructInitializeProperties& PCIP)
|
UOscReceiverComponent::UOscReceiverComponent(const class FPostConstructInitializeProperties& PCIP)
|
||||||
: Super(PCIP)
|
: Super(PCIP),
|
||||||
|
_listener(this)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -16,7 +17,7 @@ void UOscReceiverComponent::OnRegister()
|
|||||||
auto instance = UOscDispatcher::Get();
|
auto instance = UOscDispatcher::Get();
|
||||||
if (instance && !HasAnyFlags(RF_ClassDefaultObject))
|
if (instance && !HasAnyFlags(RF_ClassDefaultObject))
|
||||||
{
|
{
|
||||||
instance->RegisterReceiver(this);
|
instance->RegisterReceiver(&_listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,7 +26,7 @@ void UOscReceiverComponent::OnUnregister()
|
|||||||
auto instance = UOscDispatcher::Get();
|
auto instance = UOscDispatcher::Get();
|
||||||
if (instance && !HasAnyFlags(RF_ClassDefaultObject))
|
if (instance && !HasAnyFlags(RF_ClassDefaultObject))
|
||||||
{
|
{
|
||||||
instance->UnregisterReceiver(this);
|
instance->UnregisterReceiver(&_listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
Super::OnUnregister();
|
Super::OnUnregister();
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FComponentOscReceivedSignature, con
|
|||||||
|
|
||||||
|
|
||||||
UCLASS(meta = (BlueprintSpawnableComponent))
|
UCLASS(meta = (BlueprintSpawnableComponent))
|
||||||
class UOscReceiverComponent : public UActorComponent, public IOscReceiverInterface
|
class UOscReceiverComponent : public UActorComponent
|
||||||
{
|
{
|
||||||
GENERATED_UCLASS_BODY()
|
GENERATED_UCLASS_BODY()
|
||||||
|
|
||||||
@@ -21,18 +21,22 @@ class UOscReceiverComponent : public UActorComponent, public IOscReceiverInterfa
|
|||||||
UPROPERTY(BlueprintAssignable, Category=OSC)
|
UPROPERTY(BlueprintAssignable, Category=OSC)
|
||||||
FComponentOscReceivedSignature OnOscReceived;
|
FComponentOscReceivedSignature OnOscReceived;
|
||||||
|
|
||||||
protected:
|
public:
|
||||||
virtual void OnRegister() override;
|
const FString & GetAddressFilter() const
|
||||||
|
|
||||||
virtual void OnUnregister() override;
|
|
||||||
|
|
||||||
virtual const FString & GetAddressFilter() override
|
|
||||||
{
|
{
|
||||||
return AddressFilter;
|
return AddressFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void SendEvent(const FName & Address, const TArray<FOscDataElemStruct> & Data) override
|
void SendEvent(const FName & Address, const TArray<FOscDataElemStruct> & Data)
|
||||||
{
|
{
|
||||||
OnOscReceived.Broadcast(Address, Data);
|
OnOscReceived.Broadcast(Address, Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void OnRegister() override;
|
||||||
|
|
||||||
|
void OnUnregister() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
BasicOscReceiver<UOscReceiverComponent> _listener;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
#include "OscPrivatePCH.h"
|
||||||
|
#include "OscReceiverInputKey.h"
|
||||||
|
#include "OscDispatcher.h"
|
||||||
|
|
||||||
|
#include "SlateBasics.h"
|
||||||
|
|
||||||
|
|
||||||
|
OscReceiverInputKey::OscReceiverInputKey(const FString &address)
|
||||||
|
: _address(address),
|
||||||
|
_addressName(*address)
|
||||||
|
{
|
||||||
|
char buffer[512]; // address truncated after 500 chars.
|
||||||
|
const auto length = sprintf(buffer, "OSC%.500s", StringCast<ANSICHAR>(*address).Get());
|
||||||
|
|
||||||
|
// replace '/' by '_' because '/' is not a valid FKey character.
|
||||||
|
for(int i=0; i!=length; ++i)
|
||||||
|
{
|
||||||
|
if(buffer[i] == '/')
|
||||||
|
{
|
||||||
|
buffer[i] = '_';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_key = FName(buffer);
|
||||||
|
|
||||||
|
RegisterKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OscReceiverInputKey::RegisterKey() const
|
||||||
|
{
|
||||||
|
if(!_address.IsEmpty() && !EKeys::GetKeyDetails(_key).IsValid())
|
||||||
|
{
|
||||||
|
EKeys::AddKey(FKeyDetails(_key,
|
||||||
|
FText::FromString(_key.ToString()),
|
||||||
|
FKeyDetails::FloatAxis));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OscReceiverInputKey::SendEvent(const FName & Address, const TArray<FOscDataElemStruct> & Data)
|
||||||
|
{
|
||||||
|
if(Address != _addressName)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto & app = FSlateApplication::Get();
|
||||||
|
|
||||||
|
const auto value = Data.Num() ? Data[0].GetValue<float>() : 0.f;
|
||||||
|
|
||||||
|
FKeyEvent keyEvent(_key, app.GetModifierKeys(), 0, false, 0, 0);
|
||||||
|
if(value < 0.5f)
|
||||||
|
{
|
||||||
|
app.ProcessKeyDownEvent(keyEvent);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
app.ProcessKeyUpEvent(keyEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
FAnalogInputEvent axisEvent(_key, app.GetModifierKeys(), 0, false, 0, 0, value);
|
||||||
|
app.ProcessAnalogInputEvent(axisEvent);
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "InputCoreTypes.h"
|
||||||
|
|
||||||
|
#include "OscReceiverInterface.h"
|
||||||
|
#include "OscDataElemStruct.h"
|
||||||
|
|
||||||
|
|
||||||
|
class OscReceiverInputKey : public IOscReceiverInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OscReceiverInputKey(const FString & address);
|
||||||
|
|
||||||
|
OscReceiverInputKey(const OscReceiverInputKey &) = delete;
|
||||||
|
OscReceiverInputKey& operator=(const OscReceiverInputKey &) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/// Adds the custom input key to the Slate application.
|
||||||
|
void RegisterKey() const;
|
||||||
|
|
||||||
|
const FString & GetAddressFilter() const override
|
||||||
|
{
|
||||||
|
return _address;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SendEvent(const FName & Address, const TArray<FOscDataElemStruct> & Data) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
FString _address;
|
||||||
|
FName _addressName;
|
||||||
|
FKey _key;
|
||||||
|
};
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
#include "OscPrivatePCH.h"
|
|
||||||
#include "OscReceiverInterface.h"
|
|
||||||
|
|
||||||
|
|
||||||
UOscReceiverInterface::UOscReceiverInterface(const class FPostConstructInitializeProperties& PCIP)
|
|
||||||
: Super(PCIP)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const FString & IOscReceiverInterface::GetAddressFilter()
|
|
||||||
{
|
|
||||||
static FString defaultValue;
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IOscReceiverInterface::SendEvent(const FName & Address, const TArray<FOscDataElemStruct> & Data)
|
|
||||||
{ }
|
|
||||||
@@ -1,19 +1,33 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "OscDataElemStruct.h"
|
#include "OscDataElemStruct.h"
|
||||||
#include "OscReceiverInterface.generated.h"
|
|
||||||
|
|
||||||
|
|
||||||
UINTERFACE(MinimalAPI)
|
/// Base class to receive OSC messages.
|
||||||
class UOscReceiverInterface : public UInterface
|
struct IOscReceiverInterface
|
||||||
{
|
{
|
||||||
GENERATED_UINTERFACE_BODY()
|
virtual const FString & GetAddressFilter() const = 0;
|
||||||
|
virtual void SendEvent(const FName & Address, const TArray<FOscDataElemStruct> & Data) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class IOscReceiverInterface
|
|
||||||
{
|
|
||||||
GENERATED_IINTERFACE_BODY()
|
|
||||||
|
|
||||||
virtual const FString & GetAddressFilter();
|
/// Forward calls to an impl object.
|
||||||
virtual void SendEvent(const FName & Address, const TArray<FOscDataElemStruct> & Data);
|
template <class T>
|
||||||
|
struct BasicOscReceiver : IOscReceiverInterface
|
||||||
|
{
|
||||||
|
T * const _impl;
|
||||||
|
|
||||||
|
BasicOscReceiver(T * impl) : _impl(impl)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const FString & GetAddressFilter() const final
|
||||||
|
{
|
||||||
|
return _impl->GetAddressFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SendEvent(const FName & Address, const TArray<FOscDataElemStruct> & Data) final
|
||||||
|
{
|
||||||
|
_impl->SendEvent(Address, Data);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user