Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Unreal 5 #9

Merged
merged 11 commits into from
Sep 22, 2023
2 changes: 2 additions & 0 deletions BrainFlowPlugin/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Binaries/
Intermediate/
64 changes: 28 additions & 36 deletions BrainFlowPlugin/Source/BrainFlowPlugin/BrainFlowPlugin.Build.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,44 +5,30 @@

public class BrainFlowPlugin : ModuleRules
{
public BrainFlowPlugin(ReadOnlyTargetRules Target) : base(Target)
{
public BrainFlowPlugin(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;

PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });

PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "Public"));

if(Target.Platform == UnrealTargetPlatform.Win32)
Andrey1994 marked this conversation as resolved.
Show resolved Hide resolved
{
PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "Compiled", "Win32_dynamic", "inc"));
PublicLibraryPaths.Add(Path.Combine(ModuleDirectory, "Compiled", "Win32_dynamic", "lib"));
PublicAdditionalLibraries.Add("Brainflow32.lib");
PublicAdditionalLibraries.Add("DataHandler32.lib");
PublicAdditionalLibraries.Add("BoardController32.lib");
PublicAdditionalLibraries.Add("MLModule32.lib");
RuntimeDependencies.Add("$(TargetOutputDir)/BoardController32.dll", Path.Combine(ModuleDirectory, "Compiled", "Win32_dynamic", "lib", "BoardController32.dll"));
RuntimeDependencies.Add("$(TargetOutputDir)/DataHandler32.dll", Path.Combine(ModuleDirectory, "Compiled", "Win32_dynamic", "lib", "DataHandler32.dll"));
RuntimeDependencies.Add("$(TargetOutputDir)/GanglionLib32.dll", Path.Combine(ModuleDirectory, "Compiled", "Win32_dynamic", "lib", "GanglionLib32.dll"));
RuntimeDependencies.Add("$(TargetOutputDir)/gforce32.dll", Path.Combine(ModuleDirectory, "Compiled", "Win32_dynamic", "lib", "gforce32.dll"));
RuntimeDependencies.Add("$(TargetOutputDir)/gForceSDKWrapper32.dll", Path.Combine(ModuleDirectory, "Compiled", "Win32_dynamic", "lib", "gForceSDKWrapper32.dll"));
RuntimeDependencies.Add("$(TargetOutputDir)/neurosdk-x86.dll", Path.Combine(ModuleDirectory, "Compiled", "Win32_dynamic", "lib", "neurosdk-x86.dll"));
RuntimeDependencies.Add("$(TargetOutputDir)/BrainBitLib32.dll", Path.Combine(ModuleDirectory, "Compiled", "Win32_dynamic", "lib", "BrainBitLib32.dll"));
RuntimeDependencies.Add("$(TargetOutputDir)/eego-SDK32.dll", Path.Combine(ModuleDirectory, "Compiled", "Win32_dynamic", "lib", "eego-SDK32.dll"));
}
if(Target.Platform == UnrealTargetPlatform.Win64)
{
PublicLibraryPaths.Add(Path.Combine(ModuleDirectory, "Compiled", "x64_dynamic", "lib"));
PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "Compiled", "x64_dynamic", "inc"));
PublicAdditionalLibraries.Add("Brainflow.lib");
PublicAdditionalLibraries.Add("DataHandler.lib");
PublicAdditionalLibraries.Add("BoardController.lib");
PublicAdditionalLibraries.Add("MLModule.lib");

string Folder = Path.Combine(ModuleDirectory, "Compiled", "x64_dynamic", "lib");
PublicAdditionalLibraries.Add(Folder + "/Brainflow.lib");

PublicAdditionalLibraries.Add(Folder + "/DataHandler.lib");
PublicAdditionalLibraries.Add(Folder + "/BoardController.lib");
PublicAdditionalLibraries.Add(Folder + "/MLModule.lib");

RuntimeDependencies.Add("$(TargetOutputDir)/BoardController.dll", Path.Combine(ModuleDirectory, "Compiled", "x64_dynamic", "lib", "BoardController.dll"));
RuntimeDependencies.Add("$(TargetOutputDir)/DataHandler.dll", Path.Combine(ModuleDirectory, "Compiled", "x64_dynamic", "lib", "DataHandler.dll"));
RuntimeDependencies.Add("$(TargetOutputDir)/GanglionLib.dll", Path.Combine(ModuleDirectory, "Compiled", "x64_dynamic", "lib", "GanglionLib.dll"));
RuntimeDependencies.Add("$(TargetOutputDir)/gforce64.dll", Path.Combine(ModuleDirectory, "Compiled", "x64_dynamic", "lib", "gforce64.dll"));
RuntimeDependencies.Add("$(TargetOutputDir)/gForceSDKWrapper.dll", Path.Combine(ModuleDirectory, "Compiled", "x64_dynamic", "lib", "gForceSDKWrapper.dll"));
//RuntimeDependencies.Add("$(TargetOutputDir)/gforce64.dll", Path.Combine(ModuleDirectory, "Compiled", "x64_dynamic", "lib", "gforce64.dll"));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those dlls seem to be missing (they are built by the python script but not copied over).

Are they still relevant should I look into copying them over or should I remove them from here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are not missed, they are copied if you run python script with --oymotion option and it seems like I forgot to add this option to readme.
I also considered an option to take dlls from this job which stored all possible libs as github artifacts https://github.com/brainflow-dev/brainflow/actions/workflows/deploy_cpp_libs.yml but ended up using submodules because it was easier for me but maybe not for users

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw in fact there are some missing libraries inside this unreal engine build script, for example simpleble, muse_bglib, etc. Need to add them to support all possible devices. You can set all options in build.py to true to see the full list of libs

Copy link
Member

@Andrey1994 Andrey1994 Sep 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also maybe it makes sense to write a method which will go through files in lib folder after compilation and add all of them ro runtimedependencies. It will look better than hardcoding names, especially including the fact that there are different build options

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also considered an option to take dlls from this job which stored all possible libs as github artifacts https://github.com/brainflow-dev/brainflow/actions/workflows/deploy_cpp_libs.yml but ended up using submodules because it was easier for me but maybe not for users

My suggestion, especially if you want to publish this to the maketplace sometime soon, is to push the precompiled binaries into this repo. I know it's not ideal to have binary files in git, but it really makes the publishing process to marketplace easier + you are sure if someone clones they are using a compatible version (UE SDK - C++ SDK). The job you have there seems perfect we just need to make sure it supports all the platforms you care about. I also wrote something similar here: https://github.com/outoftheboxplugins/configcat-unreal/actions/runs/5833331101

It's still very important to give the users some instructions about compiling locally because they might need to change the C++ SDK.

also maybe it makes sense to write a method which will go through files in lib folder after compilation and add all of them ro runtimedependencies. It will look better than hardcoding names, especially including the fact that there are different build options

Yes, it makes total sense. I actually did something similar a few weeks ago for this plugin: https://github.com/outoftheboxplugins/configcat-unreal/blob/main/Source/ThirdParty/ConfigCatCppSDK/ConfigCatCppSdk.Build.cs

I wanted to keep the changes of the PR to a minimum, but I am happy to implement it here for this PR or next ones.

btw in fact there are some missing libraries inside this unreal engine build script, for example simpleble, muse_bglib, etc. Need to add them to support all possible devices. You can set all options in build.py to true to see the full list of libs

I think if we deliver pre-built binaries we can just include all in the repo and we can adjust the Build.cs accordingly.

Copy link
Member

@Andrey1994 Andrey1994 Sep 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can setup jobs to compile everything and push to marketplace wo pushing binaries to git, but can add binaries to the repo also if needed. The main requirement - it should be simple to update and dont take too much time to maintain, so probably smth like cron job with personal access token will be ok.

Lets fix this ugly hardcode for dyn libs and iterate over files in libs dir as a part of this PR

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, I've adjust the DLLs and the libs discovery.

Since this is a bigger change, I would love to know if you can suggest some tests to confirm everything is working well. (I've tried building some bits from https://brainflow.readthedocs.io/en/stable/Examples.html#id2). Or if you know someone who's using this with Unreal, I would be happy to get in touch with them and confirm it works for them.

Screenshot 2023-09-11 at 08 30 27

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this test is ok you can use it with synthetic board and see if you can read some data

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did you manage to get some data using this sample?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Thanks again for all the help both here and on slack. I was able to get the sample data both for at editortime (Play in Editor) and runtime (packaged game).

I've modified the file as discussed, the brainflow.lib is statically linked in the .exe and the 3 DLL files (board controller, ml module, data handler) are dynamically linked. All other DLL are copied over to the final build so they can loaded if needed.

//RuntimeDependencies.Add("$(TargetOutputDir)/gForceSDKWrapper.dll", Path.Combine(ModuleDirectory, "Compiled", "x64_dynamic", "lib", "gForceSDKWrapper.dll"));
RuntimeDependencies.Add("$(TargetOutputDir)/neurosdk-x64.dll", Path.Combine(ModuleDirectory, "Compiled", "x64_dynamic", "lib", "neurosdk-x64.dll"));
RuntimeDependencies.Add("$(TargetOutputDir)/Unicorn.dll", Path.Combine(ModuleDirectory, "Compiled", "x64_dynamic", "lib", "Unicorn.dll"));
RuntimeDependencies.Add("$(TargetOutputDir)/BrainBitLib.dll", Path.Combine(ModuleDirectory, "Compiled", "x64_dynamic", "lib", "BrainBitLib.dll"));
Expand All @@ -51,11 +37,14 @@ public BrainFlowPlugin(ReadOnlyTargetRules Target) : base(Target)
if(Target.Platform == UnrealTargetPlatform.Mac)
{
PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "Compiled", "macos", "inc"));
PublicLibraryPaths.Add(Path.Combine(ModuleDirectory, "Compiled", "macos", "lib"));
PublicAdditionalLibraries.Add("libBrainflow.a");
PublicDelayLoadDLLs.Add("libDataHandler.dylib");
PublicDelayLoadDLLs.Add("libBoardController.dylib");
PublicDelayLoadDLLs.Add("libMLModule.dylib");

string Folder = Path.Combine(ModuleDirectory, "Compiled", "macos", "lib");
PublicAdditionalLibraries.Add(Folder + "/libBrainflow.a");

PublicDelayLoadDLLs.Add(Folder + "/libDataHandler.dylib");
PublicDelayLoadDLLs.Add(Folder + "/libBoardController.dylib");
PublicDelayLoadDLLs.Add(Folder + "/libMLModule.dylib");

RuntimeDependencies.Add("$(TargetOutputDir)/libBoardController.dylib", Path.Combine(ModuleDirectory, "Compiled", "macos", "lib", "libBoardController.dylib"));
RuntimeDependencies.Add("$(TargetOutputDir)/libDataHandler.dylib", Path.Combine(ModuleDirectory, "Compiled", "macos", "lib", "libDataHandler.dylib"));
RuntimeDependencies.Add("$(TargetOutputDir)/libGanglionLib.dylib", Path.Combine(ModuleDirectory, "Compiled", "macos", "lib", "libGanglionLib.dylib"));
Expand All @@ -66,11 +55,14 @@ public BrainFlowPlugin(ReadOnlyTargetRules Target) : base(Target)
if(Target.Platform == UnrealTargetPlatform.Linux)
{
PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "Compiled", "linux", "inc"));
PublicLibraryPaths.Add(Path.Combine(ModuleDirectory, "Compiled", "linux", "lib"));
PublicAdditionalLibraries.Add("libBrainflow.a");
PublicDelayLoadDLLs.Add("libDataHandler.so");
PublicDelayLoadDLLs.Add("libBoardController.so");
PublicDelayLoadDLLs.Add("libMLModule.so");

string Folder = Path.Combine(ModuleDirectory, "Compiled", "linux", "lib");
PublicAdditionalLibraries.Add(Folder + "/libBrainflow.a");

PublicDelayLoadDLLs.Add(Folder + "/libDataHandler.so");
PublicDelayLoadDLLs.Add(Folder + "/libBoardController.so");
PublicDelayLoadDLLs.Add(Folder + "/libMLModule.so");

RuntimeDependencies.Add("$(TargetOutputDir)/libBoardController.so", Path.Combine(ModuleDirectory, "Compiled", "linux", "lib", "libBoardController.so"));
RuntimeDependencies.Add("$(TargetOutputDir)/libDataHandler.so", Path.Combine(ModuleDirectory, "Compiled", "linux", "lib", "libDataHandler.so"));
RuntimeDependencies.Add("$(TargetOutputDir)/libGanglionLib.so", Path.Combine(ModuleDirectory, "Compiled", "linux", "lib", "libGanglionLib.so"));
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ python tools/build.py --clear-build-dir --cmake-install-prefix %FULL_PATH_TO_CLO
# to use debug version you need to add --debug flag
```

##### MacOS ARM64

Compile brainflow using:
```
cd brainflow
python tools/build.py --clear-build-dir --cmake-install-prefix %FULL_PATH_TO_CLONED_FOLDER%/BrainFlowPlugin/Source/BrainFlowPlugin/Compiled/macos --cmake-osx-architectures=arm64
# to use debug version you need to add --debug flag
```

##### Win32

Compile brainflow using:
Expand Down