├── LICENSE ├── MotionCueingInterface ├── MotionCueingInterface.uplugin ├── Resources │ └── Icon128.png └── Source │ ├── MotionCueingInterface │ ├── MotionCueingInterface.Build.cs │ ├── Private │ │ ├── MotionCueingInterface.cpp │ │ └── MotionProbe.cpp │ └── Public │ │ ├── MotionCueingInterface.h │ │ ├── MotionData.h │ │ ├── MotionHost.h │ │ ├── MotionProbe.h │ │ └── MyMotionHost.h │ └── ThirdParty │ └── TheAPI │ ├── BuildTheAPI.cpp │ ├── TheAPI.Build.cs │ ├── TheAPI.sln │ ├── TheAPI.vcxproj │ ├── TheAPI.vcxproj.filters │ ├── TheAPI.vcxproj.user │ ├── include │ └── AddSourceCodeHere.txt │ └── lib │ └── Win64 │ └── AddAPIStaticLibraryHere.txt └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2019, UE4 Plug-ins 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /MotionCueingInterface/MotionCueingInterface.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "Version": 1, 4 | "VersionName": "1.0", 5 | "FriendlyName": "Motion Cueing Interface", 6 | "Description": "Simple interface to connect and send data to Motion Platforms", 7 | "Category": "Motion Cueing", 8 | "CreatedBy": "Francis Maheux", 9 | "CreatedByURL": "", 10 | "DocsURL": "", 11 | "MarketplaceURL": "", 12 | "SupportURL": "", 13 | "CanContainContent": true, 14 | "IsBetaVersion": false, 15 | "Installed": false, 16 | "Modules": [ 17 | { 18 | "Name": "MotionCueingInterface", 19 | "Type": "Runtime", 20 | "LoadingPhase": "Default", 21 | "WhitelistPlatforms": ["Win64"] 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /MotionCueingInterface/Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/MotionCueingInterface/ba7e30bd00422474745fe961a86dee4d49403cf0/MotionCueingInterface/Resources/Icon128.png -------------------------------------------------------------------------------- /MotionCueingInterface/Source/MotionCueingInterface/MotionCueingInterface.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | using System.IO; 5 | 6 | public class MotionCueingInterface : ModuleRules 7 | { 8 | public MotionCueingInterface(ReadOnlyTargetRules Target) : base(Target) 9 | { 10 | PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; 11 | 12 | PublicDependencyModuleNames.AddRange( 13 | new string[] 14 | { 15 | "Core", 16 | "Engine", 17 | "TheAPI" 18 | // ... add other public dependencies that you statically link with here ... 19 | } 20 | ); 21 | 22 | 23 | PrivateDependencyModuleNames.AddRange( 24 | new string[] 25 | { 26 | "CoreUObject", 27 | "Engine", 28 | "Slate", 29 | "SlateCore" 30 | // ... add private dependencies that you statically link with here ... 31 | } 32 | ); 33 | 34 | 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /MotionCueingInterface/Source/MotionCueingInterface/Private/MotionCueingInterface.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "MotionCueingInterface.h" 4 | 5 | 6 | #define LOCTEXT_NAMESPACE "FMotionCueingInterfaceModule" 7 | 8 | void FMotionCueingInterfaceModule::StartupModule() 9 | { 10 | // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module 11 | 12 | } 13 | 14 | void FMotionCueingInterfaceModule::ShutdownModule() 15 | { 16 | // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, 17 | // we call this function before unloading the module. 18 | } 19 | 20 | 21 | #undef LOCTEXT_NAMESPACE 22 | 23 | IMPLEMENT_MODULE(FMotionCueingInterfaceModule, MotionCueingInterface) -------------------------------------------------------------------------------- /MotionCueingInterface/Source/MotionCueingInterface/Private/MotionProbe.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "MotionProbe.h" 4 | 5 | // IMPLEMENTATION NOTE: fmaheux 6 | // The controller needs to get data at high frequency (100-1000Hz) 7 | // Unfortunately, the usual Unreal project will run at a lower frequency, usually between 30-60 Hz 8 | // So we must collect the necessary data from the BaseObject at low frequency (30 Hz). 9 | // Then we interpolate the data and send it at high frequency to the controller. (100-1000 Hz) 10 | 11 | // The low frequency function is TickComponent() 12 | // The high frequency function is TickController() 13 | // We interpolate between PreviousAverageHostData and CurrentAverageHostData 14 | // This solution adds a lag equal to the low frequency interval (33ms -> 30 Hz) * TickControllerAverageCount 15 | // If lag is an issue, then we could lerp between CurrentData and a "PredictionData" 16 | 17 | // Sets default values for this component's properties 18 | UMotionProbe::UMotionProbe() 19 | { 20 | PrimaryComponentTick.bCanEverTick = true; 21 | PrimaryComponentTick.bStartWithTickEnabled = true; 22 | PrimaryComponentTick.bAllowTickOnDedicatedServer = true; 23 | 24 | NextTickTime = 0.033f; // FORCED 30Hz 25 | NextTickDT = 0.0f; 26 | TickControllerCount = 0; 27 | TickControllerAverageCount = 10; 28 | 29 | OutputFrequency = 100; 30 | TickControllerTotal = 0; 31 | ControllerIP = "127.0.0.1"; 32 | ControllerPort = 3000; 33 | HostReceivePort = 3001; 34 | 35 | UseDebugMode = true; 36 | VibrationInput = EVibrationType::Acceleration; 37 | VehicleType = EVehicleType::Car; 38 | InterpolationType = EInterpolationType::Linear; 39 | 40 | TickControllerCounter = 0; 41 | Buffer.Reset(); 42 | } 43 | 44 | // Called when the game starts 45 | void UMotionProbe::BeginPlay() 46 | { 47 | Super::BeginPlay(); 48 | 49 | // Clamp params 50 | OutputFrequency = FMath::Clamp(OutputFrequency, 1, 1000); 51 | 52 | // Reset 53 | TickControllerTotal = 0; 54 | TickControllerCounter = 0; 55 | TickControllerCount = 0; 56 | NextTickDT = 0.0f; 57 | Buffer.Reset(); 58 | CurrentHostData.Reset(); 59 | PreviousHostData.Reset(); 60 | CurrentAverageHostData.Reset(); 61 | PreviousAverageHostData.Reset(); 62 | 63 | // Set low frequency timer for TickComponent() 64 | PrimaryComponentTick.TickInterval = NextTickTime; 65 | 66 | // Set high frequency timer for TickController() 67 | TimerDel.BindUFunction(this, FName("TickController")); 68 | GetWorld()->GetTimerManager().SetTimer(TimerHandle, TimerDel, 1.0f/OutputFrequency, true); 69 | 70 | // Init Host interface 71 | HostInterface->Initialize(); 72 | 73 | // Connect to controller 74 | HostInterface->ConnectToController(ControllerIP, ControllerPort, HostReceivePort); 75 | } 76 | 77 | // Called when the game ends 78 | void UMotionProbe::EndPlay(const EEndPlayReason::Type EndPlayReason) 79 | { 80 | Super::EndPlay(EndPlayReason); 81 | HostInterface->Cleanup(); 82 | GetWorld()->GetTimerManager().ClearTimer(TimerHandle); 83 | } 84 | 85 | void UMotionProbe::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) 86 | { 87 | Super::TickComponent(DeltaTime, TickType, ThisTickFunction); 88 | 89 | NextTickDT += DeltaTime; 90 | if (NextTickDT > NextTickTime) 91 | { 92 | // How many times we tick the controller in that frame 93 | TickControllerCounter += FMath::FloorToInt(NextTickDT * OutputFrequency); 94 | 95 | // Save previous data since we interpolate between "previous" and "current" 96 | PreviousHostData = CurrentHostData; 97 | 98 | // Get current data 99 | CurrentHostData.VehiclePosition = BaseObject->GetComponentLocation(); 100 | CurrentHostData.VehicleRotation = BaseObject->GetComponentRotation(); 101 | 102 | // Create vehicle reference frame 103 | FTransform TM = FTransform(); 104 | TM.SetRotation(CurrentHostData.VehicleRotation.Quaternion()); 105 | 106 | // Calculate forward vector in vehicle space 107 | FVector ForwardVector = CurrentHostData.VehiclePosition - PreviousHostData.VehiclePosition; 108 | ForwardVector = TM.Inverse().TransformVector(ForwardVector); 109 | 110 | // Vehicle Direction and Displacement 111 | ForwardVector.ToDirectionAndLength(CurrentHostData.VehicleDirection, CurrentHostData.VehicleDisplacement); 112 | 113 | // Vehicle Velocity 114 | CurrentHostData.VehicleVelocity = CurrentHostData.VehicleDisplacement / NextTickDT; 115 | 116 | // Vehicle Acceleration 117 | CurrentHostData.VehicleAcceleration = (CurrentHostData.VehicleVelocity - PreviousHostData.VehicleVelocity) / NextTickDT; 118 | 119 | // Kinematic velocity and acceleration per axis 120 | CurrentHostData.VelocityX = (ForwardVector.X) / NextTickDT; // forward speed 121 | CurrentHostData.VelocityY = (ForwardVector.Y) / NextTickDT; // side speed 122 | CurrentHostData.VelocityZ = (ForwardVector.Z) / NextTickDT; // up speed 123 | CurrentHostData.AccelerationX = (CurrentHostData.VelocityX - PreviousHostData.VelocityX) / NextTickDT; // forward acceleration 124 | CurrentHostData.AccelerationY = (CurrentHostData.VelocityY - PreviousHostData.VelocityY) / NextTickDT; // side acceleration 125 | CurrentHostData.AccelerationZ = (CurrentHostData.VelocityZ - PreviousHostData.VelocityZ) / NextTickDT; // up acceleration 126 | 127 | // Rotational motion 128 | CurrentHostData.PitchDisplacement = RotationalDeltaDeg(CurrentHostData.VehicleRotation.Pitch, PreviousHostData.VehicleRotation.Pitch); 129 | CurrentHostData.RollDisplacement = RotationalDeltaDeg(CurrentHostData.VehicleRotation.Roll, PreviousHostData.VehicleRotation.Roll); 130 | CurrentHostData.YawDisplacement = RotationalDeltaDeg(CurrentHostData.VehicleRotation.Yaw, PreviousHostData.VehicleRotation.Yaw); 131 | 132 | CurrentHostData.PitchVelocity = CurrentHostData.PitchDisplacement / NextTickDT; 133 | CurrentHostData.RollVelocity = CurrentHostData.RollDisplacement / NextTickDT; 134 | CurrentHostData.YawVelocity = CurrentHostData.YawDisplacement / NextTickDT; 135 | 136 | CurrentHostData.PitchAcceleration = (CurrentHostData.PitchVelocity - PreviousHostData.PitchVelocity) / NextTickDT; 137 | CurrentHostData.RollAcceleration = (CurrentHostData.RollVelocity - PreviousHostData.RollVelocity) / NextTickDT; 138 | CurrentHostData.YawAcceleration = (CurrentHostData.YawVelocity - PreviousHostData.YawVelocity) / NextTickDT; 139 | 140 | // reset 141 | NextTickDT = 0.0f; 142 | 143 | // accumulate data in buffer to average over time 144 | Buffer.Add(CurrentHostData); 145 | } 146 | 147 | // average over time 148 | if (Buffer.Num() >= TickControllerAverageCount) 149 | { 150 | TickControllerTotal = TickControllerCounter; 151 | TickControllerCounter = 0; 152 | TickControllerCount = 0; 153 | PreviousAverageHostData = CurrentAverageHostData; 154 | CurrentAverageHostData.Reset(); 155 | for (FMotionData& Data : Buffer) 156 | { 157 | CurrentAverageHostData = CurrentAverageHostData + Data; 158 | } 159 | CurrentAverageHostData = CurrentAverageHostData / Buffer.Num(); 160 | Buffer.Empty(); 161 | } 162 | 163 | // Debug print 164 | if (UseDebugMode) 165 | { 166 | DebugPrint(DeltaTime * 2); 167 | } 168 | } 169 | 170 | // Will be called in a burst, not at equal intervals unfortunatly 171 | // Will need to add a delay to avoid burst effect? 172 | void UMotionProbe::TickController() 173 | { 174 | // Interpolate data 175 | float T = float(TickControllerCount) / float(TickControllerTotal - 1); 176 | T = FMath::Clamp(T, 0.0f, 1.0f); 177 | FMotionData InterpolatedData = InterpolateMotionData(PreviousAverageHostData, CurrentAverageHostData, T); 178 | 179 | // receive data from controller 180 | HostInterface->ReceiveDataFromController(); 181 | 182 | // go though the flow of state to control state machine 183 | HostInterface->SetStateFlow(); 184 | 185 | // send data to controller 186 | HostInterface->SendDataToController(InterpolatedData); 187 | 188 | TickControllerCount++; 189 | } 190 | 191 | void UMotionProbe::DebugPrint(float DeltaTime) 192 | { 193 | if (GEngine != nullptr) 194 | { 195 | GEngine->AddOnScreenDebugMessage(15, DeltaTime, FColor::White, TEXT("Vehicle Roll accel (deg/s2): ") + GetFloatAsStringWithPrecision(CurrentAverageHostData.RollAcceleration, 2)); 196 | GEngine->AddOnScreenDebugMessage(14, DeltaTime, FColor::White, TEXT("Vehicle Pitch accel (deg/s2): ") + GetFloatAsStringWithPrecision(CurrentAverageHostData.PitchAcceleration, 2)); 197 | GEngine->AddOnScreenDebugMessage(13, DeltaTime, FColor::White, TEXT("Vehicle Yaw accel (deg/s2): ") + GetFloatAsStringWithPrecision(CurrentAverageHostData.YawAcceleration, 2)); 198 | 199 | GEngine->AddOnScreenDebugMessage(12, DeltaTime, FColor::White, TEXT("Vehicle Roll velocity (deg/s): ") + GetFloatAsStringWithPrecision(CurrentAverageHostData.RollVelocity, 2)); 200 | GEngine->AddOnScreenDebugMessage(11, DeltaTime, FColor::White, TEXT("Vehicle Pitch velocity (deg/s): ") + GetFloatAsStringWithPrecision(CurrentAverageHostData.PitchVelocity, 2)); 201 | GEngine->AddOnScreenDebugMessage(10, DeltaTime, FColor::White, TEXT("Vehicle Yaw velocity (deg/s): ") + GetFloatAsStringWithPrecision(CurrentAverageHostData.YawVelocity, 2)); 202 | 203 | GEngine->AddOnScreenDebugMessage(4, DeltaTime, FColor::White, TEXT("Current Roll (deg): ") + GetFloatAsStringWithPrecision(CurrentAverageHostData.VehicleRotation.Roll, 2)); 204 | GEngine->AddOnScreenDebugMessage(3, DeltaTime, FColor::White, TEXT("Current Pitch (deg): ") + GetFloatAsStringWithPrecision(CurrentAverageHostData.VehicleRotation.Pitch, 2)); 205 | GEngine->AddOnScreenDebugMessage(2, DeltaTime, FColor::White, TEXT("Current Yaw (deg): ") + GetFloatAsStringWithPrecision(CurrentAverageHostData.VehicleRotation.Yaw, 2)); 206 | 207 | GEngine->AddOnScreenDebugMessage(9, DeltaTime, FColor::White, TEXT("Vehicle VelocityZ (m/s): ") + GetFloatAsStringWithPrecision(CurrentAverageHostData.VelocityZ*0.01f, 2)); 208 | GEngine->AddOnScreenDebugMessage(8, DeltaTime, FColor::White, TEXT("Vehicle VelocityY (m/s): ") + GetFloatAsStringWithPrecision(CurrentAverageHostData.VelocityY*0.01f, 2)); 209 | GEngine->AddOnScreenDebugMessage(7, DeltaTime, FColor::White, TEXT("Vehicle VelocityX (m/s): ") + GetFloatAsStringWithPrecision(CurrentAverageHostData.VelocityX*0.01f, 2)); 210 | 211 | GEngine->AddOnScreenDebugMessage(6, DeltaTime, FColor::White, TEXT("Vehicle Acceleration (m/s2): ") + GetFloatAsStringWithPrecision(CurrentAverageHostData.VehicleAcceleration*0.01f, 2)); 212 | GEngine->AddOnScreenDebugMessage(5, DeltaTime, FColor::White, TEXT("Vehicle Velocity (m/s): ") + GetFloatAsStringWithPrecision(CurrentAverageHostData.VehicleVelocity*0.01f, 2)); 213 | 214 | GEngine->AddOnScreenDebugMessage(1, DeltaTime, FColor::Red, TEXT("Current tick: ") + FString::FromInt(TickControllerCount)); 215 | GEngine->AddOnScreenDebugMessage(0, DeltaTime, FColor::Red, TEXT("Total Tick: ") + FString::FromInt(TickControllerTotal)); 216 | } 217 | } 218 | 219 | void UMotionProbe::SetBaseObject(UMeshComponent* Input) 220 | { 221 | BaseObject = Input; 222 | } 223 | -------------------------------------------------------------------------------- /MotionCueingInterface/Source/MotionCueingInterface/Public/MotionCueingInterface.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Modules/ModuleManager.h" 7 | 8 | class UMotionPlate; 9 | 10 | class FMotionCueingInterfaceModule : public IModuleInterface 11 | { 12 | public: 13 | 14 | /** IModuleInterface implementation */ 15 | virtual void StartupModule() override; 16 | virtual void ShutdownModule() override; 17 | }; 18 | -------------------------------------------------------------------------------- /MotionCueingInterface/Source/MotionCueingInterface/Public/MotionData.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | 7 | // Generic data structure for motion system 8 | 9 | struct FMotionData 10 | { 11 | FMotionData() 12 | { 13 | Reset(); 14 | } 15 | 16 | FMotionData& operator=(const FMotionData& A) 17 | { 18 | Counter = A.Counter; 19 | MotionStateCommand = A.MotionStateCommand; 20 | VehicleRotation = A.VehicleRotation; 21 | VehicleDirection = A.VehicleDirection; 22 | VehiclePosition = A.VehiclePosition; 23 | VehicleDisplacement = A.VehicleDisplacement; 24 | VehicleVelocity = A.VehicleVelocity; 25 | VehicleAcceleration = A.VehicleAcceleration; 26 | VelocityX = A.VelocityX; 27 | VelocityY = A.VelocityY; 28 | VelocityZ = A.VelocityZ; 29 | AccelerationX = A.AccelerationX; 30 | AccelerationY = A.AccelerationY; 31 | AccelerationZ = A.AccelerationZ; 32 | PitchDisplacement = A.PitchDisplacement; 33 | YawDisplacement = A.YawDisplacement; 34 | RollDisplacement = A.RollDisplacement; 35 | PitchVelocity = A.PitchVelocity; 36 | YawVelocity = A.YawVelocity; 37 | RollVelocity = A.RollVelocity; 38 | PitchAcceleration = A.PitchAcceleration; 39 | YawAcceleration = A.YawAcceleration; 40 | RollAcceleration = A.RollAcceleration; 41 | return *this; 42 | } 43 | 44 | FMotionData& operator+(const FMotionData& A) 45 | { 46 | // Not additive, assigned it 47 | Counter = A.Counter; 48 | MotionStateCommand = A.MotionStateCommand; 49 | VehicleRotation = A.VehicleRotation; 50 | 51 | // keep direction normalized 52 | VehicleDirection += A.VehicleDirection; 53 | VehicleDirection.Normalize(); 54 | 55 | // additive 56 | VehiclePosition += A.VehiclePosition; 57 | VehicleDisplacement += A.VehicleDisplacement; 58 | VehicleVelocity += A.VehicleVelocity; 59 | VehicleAcceleration += A.VehicleAcceleration; 60 | VelocityX += A.VelocityX; 61 | VelocityY += A.VelocityY; 62 | VelocityZ += A.VelocityZ; 63 | AccelerationX += A.AccelerationX; 64 | AccelerationY += A.AccelerationY; 65 | AccelerationZ += A.AccelerationZ; 66 | PitchDisplacement += A.PitchDisplacement; 67 | YawDisplacement += A.YawDisplacement; 68 | RollDisplacement += A.RollDisplacement; 69 | PitchVelocity += A.PitchVelocity; 70 | YawVelocity += A.YawVelocity; 71 | RollVelocity += A.RollVelocity; 72 | PitchAcceleration += A.PitchAcceleration; 73 | YawAcceleration += A.YawAcceleration; 74 | RollAcceleration += A.RollAcceleration; 75 | return *this; 76 | } 77 | 78 | FMotionData& operator/(float num) 79 | { 80 | //Counter 81 | //MotionStateCommand 82 | //VehicleRotation 83 | //VehicleDirection 84 | VehiclePosition = VehiclePosition / num; 85 | VehicleDisplacement = VehicleDisplacement / num; 86 | VehicleVelocity = VehicleVelocity / num; 87 | VehicleAcceleration = VehicleAcceleration / num; 88 | VelocityX = VelocityX / num; 89 | VelocityY = VelocityY / num; 90 | VelocityZ = VelocityZ / num; 91 | AccelerationX = AccelerationX / num; 92 | AccelerationY = AccelerationY / num; 93 | AccelerationZ = AccelerationZ / num; 94 | PitchDisplacement = PitchDisplacement / num; 95 | YawDisplacement = YawDisplacement / num; 96 | RollDisplacement = RollDisplacement / num; 97 | PitchVelocity = PitchVelocity / num; 98 | YawVelocity = YawVelocity / num; 99 | RollVelocity = RollVelocity / num; 100 | PitchAcceleration = PitchAcceleration / num; 101 | YawAcceleration = YawAcceleration / num; 102 | RollAcceleration = RollAcceleration / num; 103 | return *this; 104 | } 105 | 106 | void SetValue(float Value) 107 | { 108 | VehicleRotation = FRotator(Value, Value, Value); 109 | VehicleDirection = FVector(Value, Value, Value); 110 | VehiclePosition = FVector(Value, Value, Value); 111 | Counter = Value; 112 | MotionStateCommand = Value; 113 | VehicleDisplacement = Value; 114 | VehicleVelocity = Value; 115 | VehicleAcceleration = Value; 116 | VelocityX = Value; 117 | VelocityY = Value; 118 | VelocityZ = Value; 119 | AccelerationX = Value; 120 | AccelerationY = Value; 121 | AccelerationZ = Value; 122 | PitchDisplacement = Value; 123 | YawDisplacement = Value; 124 | RollDisplacement = Value; 125 | PitchVelocity = Value; 126 | YawVelocity = Value; 127 | RollVelocity = Value; 128 | PitchAcceleration = Value; 129 | YawAcceleration = Value; 130 | RollAcceleration = Value; 131 | } 132 | 133 | void Reset() 134 | { 135 | SetValue(0.0f); 136 | } 137 | 138 | // tick counter 139 | int32 Counter; 140 | 141 | // A Integer that represent a state of motion, TBD 142 | int32 MotionStateCommand; 143 | 144 | // Vehicle (ROLL, PITCH, YAW) with respect to horizontal plane (deg) 145 | FRotator VehicleRotation; 146 | 147 | // Vehicle direction in vehicle space (X is forward, Y is side, Z is up) 148 | FVector VehicleDirection; 149 | 150 | // Kinematic motion 151 | FVector VehiclePosition; 152 | float VehicleDisplacement; 153 | float VehicleVelocity; 154 | float VehicleAcceleration; 155 | 156 | // Kinematic velocity per axis (cm/s) 157 | float VelocityX; 158 | float VelocityY; 159 | float VelocityZ; 160 | 161 | // Kinematic acceleraton per axis (cm/s2) 162 | float AccelerationX; 163 | float AccelerationY; 164 | float AccelerationZ; 165 | 166 | // Vehicle rotational displacement (deg) 167 | float PitchDisplacement; 168 | float YawDisplacement; 169 | float RollDisplacement; 170 | 171 | // Vehicle Rotational velocity (deg/s) 172 | float PitchVelocity; 173 | float YawVelocity; 174 | float RollVelocity; 175 | 176 | // Vehicle Rotational acceleration (deg/s2) 177 | float PitchAcceleration; 178 | float YawAcceleration; 179 | float RollAcceleration; 180 | }; 181 | 182 | // linear interpolation 183 | static inline float Lerp(float a, float b, float f) 184 | { 185 | return (a * (1.0 - f)) + (b * f); 186 | } 187 | 188 | // Calculate rotational difference on a circle in deg [-180, 180] 189 | static inline float RotationalDeltaDeg(float CurrentAngle, float PreviousAngle) 190 | { 191 | float PITAU = 360 + 180; 192 | float Delta = FMath::Fmod((CurrentAngle - PreviousAngle + PITAU), 360) - 180; 193 | return Delta; 194 | } 195 | 196 | // add 2 angles and stay in range [-180, 180] 197 | static inline float RotationalAddDeg(float Angle1, float Angle2) 198 | { 199 | float PITAU = 360 + 180; 200 | float Added = FMath::Fmod((Angle1 + Angle2 + PITAU), 360) - 180; 201 | return Added; 202 | } 203 | 204 | 205 | /* 206 | // Calculate rotational difference on a circle in rad (at most PI rad) 207 | static inline float RotationalDifferenceRad(float CurrentAngle, float PreviousAngle) 208 | { 209 | float TAU = 2 * PI; 210 | float PITAU = PI + TAU; 211 | float Delta = FMath::Fmod((CurrentAngle - PreviousAngle + PITAU), TAU) - PI; 212 | return Delta; 213 | } 214 | */ 215 | 216 | // Interpolate from A to B 217 | static inline FMotionData InterpolateMotionData(FMotionData& A, FMotionData& B, float T) 218 | { 219 | FMotionData Out; 220 | 221 | // nlerp 222 | Out.VehicleDirection.X = Lerp(A.VehicleDirection.X, B.VehicleDirection.X, T); 223 | Out.VehicleDirection.Y = Lerp(A.VehicleDirection.Y, B.VehicleDirection.Y, T); 224 | Out.VehicleDirection.Z = Lerp(A.VehicleDirection.Z, B.VehicleDirection.Z, T); 225 | Out.VehicleDirection.Normalize(); 226 | 227 | Out.VehiclePosition.X = Lerp(A.VehiclePosition.X, B.VehiclePosition.X, T); 228 | Out.VehiclePosition.Y = Lerp(A.VehiclePosition.Y, B.VehiclePosition.Y, T); 229 | Out.VehiclePosition.Z = Lerp(A.VehiclePosition.Z, B.VehiclePosition.Z, T); 230 | 231 | float PitchDelta = RotationalDeltaDeg(A.VehicleRotation.Pitch, B.VehicleRotation.Pitch); 232 | Out.VehicleRotation.Pitch = RotationalAddDeg(A.VehicleRotation.Pitch, PitchDelta*T); 233 | 234 | float RollDelta = RotationalDeltaDeg(A.VehicleRotation.Roll, B.VehicleRotation.Roll); 235 | Out.VehicleRotation.Roll = RotationalAddDeg(A.VehicleRotation.Roll, RollDelta*T); 236 | 237 | float YawDelta = RotationalDeltaDeg(A.VehicleRotation.Yaw, B.VehicleRotation.Yaw); 238 | Out.VehicleRotation.Yaw = RotationalAddDeg(A.VehicleRotation.Yaw, YawDelta*T); 239 | 240 | Out.VelocityX = Lerp(A.VelocityX, B.VelocityX, T); 241 | Out.VelocityY = Lerp(A.VelocityY, B.VelocityY, T); 242 | Out.VelocityZ = Lerp(A.VelocityZ, B.VelocityZ, T); 243 | 244 | Out.AccelerationX = Lerp(A.AccelerationX, B.AccelerationX, T); 245 | Out.AccelerationY = Lerp(A.AccelerationY, B.AccelerationY, T); 246 | Out.AccelerationZ = Lerp(A.AccelerationZ, B.AccelerationZ, T); 247 | 248 | Out.PitchDisplacement = Lerp(A.PitchDisplacement, B.PitchDisplacement, T); 249 | Out.RollDisplacement = Lerp(A.RollDisplacement, B.RollDisplacement, T); 250 | Out.YawDisplacement = Lerp(A.YawDisplacement, B.YawDisplacement, T); 251 | 252 | Out.PitchVelocity = Lerp(A.PitchVelocity, B.PitchVelocity, T); 253 | Out.RollVelocity = Lerp(A.RollVelocity, B.RollVelocity, T); 254 | Out.YawVelocity = Lerp(A.YawVelocity, B.YawVelocity, T); 255 | 256 | Out.PitchAcceleration = Lerp(A.PitchAcceleration, B.PitchAcceleration, T); 257 | Out.RollAcceleration = Lerp(A.RollAcceleration, B.RollAcceleration, T); 258 | Out.YawAcceleration = Lerp(A.YawAcceleration, B.YawAcceleration, T); 259 | 260 | Out.VehicleDisplacement = Lerp(A.VehicleDisplacement, B.VehicleDisplacement, T); 261 | Out.VehicleVelocity = Lerp(A.VehicleVelocity, B.VehicleVelocity, T); 262 | Out.VehicleAcceleration = Lerp(A.VehicleAcceleration, B.VehicleAcceleration, T); 263 | 264 | return Out; 265 | } 266 | 267 | static inline FString GetFloatAsStringWithPrecision(float TheFloat, int32 Precision, bool IncludeLeadingZero = true) 268 | { 269 | //Round to integral if have something like 1.9999 within precision 270 | float Rounded = roundf(TheFloat); 271 | if (FMath::Abs(TheFloat - Rounded) < FMath::Pow(10, -1 * Precision)) 272 | { 273 | TheFloat = Rounded; 274 | } 275 | FNumberFormattingOptions NumberFormat; 276 | NumberFormat.MinimumIntegralDigits = (IncludeLeadingZero) ? 1 : 0; 277 | NumberFormat.MaximumIntegralDigits = 10000; 278 | NumberFormat.MinimumFractionalDigits = Precision; 279 | NumberFormat.MaximumFractionalDigits = Precision; 280 | NumberFormat.AlwaysSign = true; 281 | 282 | FString Out; 283 | Out = FText::AsNumber(TheFloat, &NumberFormat).ToString(); 284 | return Out; 285 | } 286 | -------------------------------------------------------------------------------- /MotionCueingInterface/Source/MotionCueingInterface/Public/MotionHost.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "MotionData.h" 6 | #include "CoreMinimal.h" 7 | #include "Engine.h" 8 | 9 | // Generic base class to interface with external Motion System API 10 | class MotionHost 11 | { 12 | private: 13 | 14 | // Remap data from Unreal to your API 15 | virtual void MapData(FMotionData& Data) = 0; 16 | 17 | public: 18 | 19 | // Init API 20 | virtual void Initialize() = 0; 21 | 22 | // Clean up 23 | virtual void Cleanup() = 0; 24 | 25 | // Connect to the controller 26 | virtual void ConnectToController(FString ControllerIP, int ControllerPort, int HostRcvPort) = 0; 27 | 28 | // Send data to the controller 29 | virtual void SendDataToController(FMotionData& Data) = 0; 30 | 31 | // Receive data from the controller 32 | virtual void ReceiveDataFromController() = 0; 33 | 34 | // Sequence of command to control state machine 35 | virtual void SetStateFlow() = 0; 36 | }; 37 | -------------------------------------------------------------------------------- /MotionCueingInterface/Source/MotionCueingInterface/Public/MotionProbe.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Components/ActorComponent.h" 7 | #include "Components/StaticMeshComponent.h" 8 | #include "Components/SkeletalMeshComponent.h" 9 | #include "Engine.h" 10 | #include "MotionData.h" 11 | #include "MyMotionHost.h" 12 | #include "MotionProbe.generated.h" 13 | 14 | 15 | UENUM(BlueprintType) 16 | enum class EVehicleType : uint8 17 | { 18 | Boat, 19 | Car, 20 | Hellicopter, 21 | Bike, 22 | Plane 23 | }; 24 | 25 | UENUM(BlueprintType) 26 | enum class EInterpolationType : uint8 27 | { 28 | Linear, 29 | EaseIn, 30 | EaseOut, 31 | EaseInEaseOut 32 | }; 33 | 34 | UENUM(BlueprintType) 35 | enum class EVibrationType : uint8 36 | { 37 | Speed, 38 | Acceleration, 39 | Surge, 40 | Heave, 41 | Sway, 42 | Roll, 43 | Pitch, 44 | Yaw 45 | }; 46 | 47 | UCLASS( ClassGroup=(MotionCueing), meta=(BlueprintSpawnableComponent) ) 48 | class UMotionProbe : public UActorComponent 49 | { 50 | GENERATED_BODY() 51 | 52 | public: 53 | // Sets default values for this component's properties 54 | UMotionProbe(); 55 | 56 | protected: 57 | 58 | virtual void BeginPlay() override; 59 | virtual void EndPlay(const EEndPlayReason::Type) override; 60 | void DebugPrint(float Deltatime); 61 | 62 | // Low frequency counters 63 | float NextTickTime; 64 | float NextTickDT; 65 | 66 | // Motion data per frame 67 | FMotionData CurrentHostData; 68 | FMotionData PreviousHostData; 69 | 70 | // Average Motion data over time 71 | TArray Buffer; 72 | FMotionData CurrentAverageHostData; 73 | FMotionData PreviousAverageHostData; 74 | 75 | // Object used to get data from 76 | UMeshComponent* BaseObject; 77 | 78 | // High frequency timer and counters 79 | int TickControllerCount; 80 | int TickControllerTotal; 81 | int TickControllerCounter; 82 | int TickControllerAverageCount; 83 | FTimerHandle TimerHandle; 84 | FTimerDelegate TimerDel; 85 | 86 | // Host Interface 87 | MotionHost* HostInterface = new MyMotionHost(); 88 | 89 | public: 90 | 91 | virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; 92 | 93 | // Properties of the component 94 | UPROPERTY(EditAnywhere) 95 | EVehicleType VehicleType; 96 | 97 | UPROPERTY(EditAnywhere) 98 | EInterpolationType InterpolationType; 99 | 100 | UPROPERTY(EditAnywhere) 101 | EVibrationType VibrationInput; 102 | 103 | UPROPERTY(EditAnywhere) 104 | int OutputFrequency; 105 | 106 | UPROPERTY(EditAnywhere) 107 | FString ControllerIP; 108 | 109 | UPROPERTY(EditAnywhere) 110 | int ControllerPort; 111 | 112 | UPROPERTY(EditAnywhere) 113 | int HostReceivePort; 114 | 115 | UPROPERTY(EditAnywhere) 116 | bool UseDebugMode; 117 | 118 | // Blueprint Function to set the base object 119 | UFUNCTION(BlueprintCallable, Category = "MotionCueing") 120 | void SetBaseObject(UMeshComponent* Input); 121 | 122 | // High frequency function that sends data to controller 123 | UFUNCTION() 124 | void TickController(); 125 | 126 | }; 127 | -------------------------------------------------------------------------------- /MotionCueingInterface/Source/MotionCueingInterface/Public/MyMotionHost.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "MotionHost.h" 4 | 5 | #if WITH_THEAPI 6 | // add API include here 7 | // ... 8 | // ... 9 | // ... 10 | // ... 11 | #endif 12 | 13 | 14 | class MyMotionHost : public MotionHost 15 | { 16 | private: 17 | 18 | virtual void MapData(FMotionData& Data) override 19 | { 20 | // Map data from Unreal to your API data structure 21 | // ... 22 | 23 | 24 | } 25 | 26 | public: 27 | 28 | virtual void Initialize() override 29 | { 30 | // Initialize your API object here 31 | // ... 32 | 33 | } 34 | 35 | virtual void Cleanup() override 36 | { 37 | // Perform some cleanup if necessary 38 | // ... 39 | 40 | } 41 | 42 | virtual void ConnectToController(FString ControllerIP, int ControllerPort, int HostRcvPort) override 43 | { 44 | // Connect to your controller using arguments 45 | // ... 46 | 47 | } 48 | 49 | virtual void ReceiveDataFromController() override 50 | { 51 | // Read data from controller 52 | // ... 53 | 54 | } 55 | 56 | 57 | virtual void SetStateFlow() override 58 | { 59 | // If required, go through a state flow in a specific order before sending/receiving data to the controller 60 | // ... 61 | 62 | } 63 | 64 | virtual void SendDataToController(FMotionData& Data) override 65 | { 66 | // Map data from Unreal to Controller 67 | MapData(Data); 68 | 69 | 70 | // Send data 71 | // ... 72 | } 73 | }; 74 | -------------------------------------------------------------------------------- /MotionCueingInterface/Source/ThirdParty/TheAPI/BuildTheAPI.cpp: -------------------------------------------------------------------------------- 1 | // Please build your static library 2 | 3 | #include 4 | #define DLLEXPORT __declspec(dllexport) 5 | 6 | #ifndef DLLEXPORT 7 | #define DLLEXPORT 8 | #endif -------------------------------------------------------------------------------- /MotionCueingInterface/Source/ThirdParty/TheAPI/TheAPI.Build.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using UnrealBuildTool; 3 | 4 | public class TheAPI : ModuleRules 5 | { 6 | public TheAPI(ReadOnlyTargetRules Target) : base(Target) 7 | { 8 | Type = ModuleType.External; 9 | if (Target.Platform == UnrealTargetPlatform.Win64) 10 | { 11 | // add include path 12 | string PlatformDir = Target.Platform.ToString(); 13 | string IncPath = Path.Combine(ModuleDirectory, "include"); 14 | PublicSystemIncludePaths.Add(IncPath); 15 | 16 | // add lib path 17 | string LibPath = Path.Combine(ModuleDirectory, "lib", PlatformDir); 18 | PublicLibraryPaths.Add(LibPath); 19 | 20 | /* 21 | // add static library 22 | string LibName = "TheAPI"; 23 | PublicAdditionalLibraries.Add(LibName + ".lib"); 24 | 25 | // add dynamic library 26 | string DLLName = LibName + ".dll"; 27 | PublicDelayLoadDLLs.Add(DLLName); 28 | string BinaryPath = Path.GetFullPath(Path.Combine(ModuleDirectory, "../../../Binaries/ThirdParty", PlatformDir)); 29 | RuntimeDependencies.Add(Path.Combine(BinaryPath, DLLName)); 30 | */ 31 | 32 | // add macros 33 | PublicDefinitions.Add("WITH_THEAPI=1"); 34 | PublicDefinitions.Add("__WINDOWS__"); 35 | //PublicDefinitions.Add("_WIN32_WINNT_WIN10_TH2"); 36 | //PublicDefinitions.Add("_WIN32_WINNT_WIN10_RS1"); 37 | //PublicDefinitions.Add("_WIN32_WINNT_WIN10_RS2"); 38 | //PublicDefinitions.Add("_WIN32_WINNT_WIN10_RS3"); 39 | //PublicDefinitions.Add("_WIN32_WINNT_WIN10_RS4"); 40 | //PublicDefinitions.Add("_WIN32_WINNT_WIN10_RS5"); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /MotionCueingInterface/Source/ThirdParty/TheAPI/TheAPI.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.136 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TheAPI", "TheAPI.vcxproj", "{FEDDD8A0-D5EF-4B09-B267-B2F2758B2FFC}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {FEDDD8A0-D5EF-4B09-B267-B2F2758B2FFC}.Debug|x64.ActiveCfg = Debug|x64 17 | {FEDDD8A0-D5EF-4B09-B267-B2F2758B2FFC}.Debug|x64.Build.0 = Debug|x64 18 | {FEDDD8A0-D5EF-4B09-B267-B2F2758B2FFC}.Debug|x86.ActiveCfg = Debug|Win32 19 | {FEDDD8A0-D5EF-4B09-B267-B2F2758B2FFC}.Debug|x86.Build.0 = Debug|Win32 20 | {FEDDD8A0-D5EF-4B09-B267-B2F2758B2FFC}.Release|x64.ActiveCfg = Release|x64 21 | {FEDDD8A0-D5EF-4B09-B267-B2F2758B2FFC}.Release|x64.Build.0 = Release|x64 22 | {FEDDD8A0-D5EF-4B09-B267-B2F2758B2FFC}.Release|x86.ActiveCfg = Release|Win32 23 | {FEDDD8A0-D5EF-4B09-B267-B2F2758B2FFC}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {A5F86ADE-2DB8-48F6-A88C-5FFFE8FB40C0} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /MotionCueingInterface/Source/ThirdParty/TheAPI/TheAPI.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {FEDDD8A0-D5EF-4B09-B267-B2F2758B2FFC} 24 | TheAPI 25 | 10.0.17763.0 26 | 27 | 28 | 29 | DynamicLibrary 30 | true 31 | v141 32 | Unicode 33 | 34 | 35 | Application 36 | false 37 | v141 38 | true 39 | MultiByte 40 | 41 | 42 | Application 43 | true 44 | v141 45 | MultiByte 46 | 47 | 48 | StaticLibrary 49 | false 50 | v141 51 | true 52 | Unicode 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | Level3 76 | Disabled 77 | true 78 | true 79 | 80 | 81 | Windows 82 | 83 | 84 | 85 | 86 | Level3 87 | Disabled 88 | true 89 | true 90 | 91 | 92 | 93 | 94 | Level3 95 | MaxSpeed 96 | true 97 | true 98 | true 99 | true 100 | 101 | 102 | true 103 | true 104 | 105 | 106 | 107 | 108 | Level3 109 | MaxSpeed 110 | true 111 | true 112 | true 113 | true 114 | WIN32;__WINDOWS__;NDEBUG;_CONSOLE;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) 115 | 116 | 117 | true 118 | true 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /MotionCueingInterface/Source/ThirdParty/TheAPI/TheAPI.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | 14 | 15 | Source Files 16 | 17 | 18 | -------------------------------------------------------------------------------- /MotionCueingInterface/Source/ThirdParty/TheAPI/TheAPI.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /MotionCueingInterface/Source/ThirdParty/TheAPI/include/AddSourceCodeHere.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/MotionCueingInterface/ba7e30bd00422474745fe961a86dee4d49403cf0/MotionCueingInterface/Source/ThirdParty/TheAPI/include/AddSourceCodeHere.txt -------------------------------------------------------------------------------- /MotionCueingInterface/Source/ThirdParty/TheAPI/lib/Win64/AddAPIStaticLibraryHere.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/MotionCueingInterface/ba7e30bd00422474745fe961a86dee4d49403cf0/MotionCueingInterface/Source/ThirdParty/TheAPI/lib/Win64/AddAPIStaticLibraryHere.txt -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Motion Cueing Interface 2 | 3 | The Motion Cueing Interface plugin is a c++ code sample to help motion system manufacturers with the creation of a plugin that will interface with motion platforms. 4 | 5 | Developer and end user documentation resides in the Wiki. 6 | 7 | 8 | Developed by: 9 | 10 | Sebastien Loze - Industry Manager, Simulations 11 | sebastien.loze@epicgames.com 12 | 13 | Francis Maheux - Technical Artist 14 | francis.maheux@epicgames.com 15 | --------------------------------------------------------------------------------