# Game Engine Integrations

MosisService provides a virtual phone that game engines can display and interact with.

## Integration Architecture

```
┌─────────────────────────────────────────────────────────────────┐
│                        MosisService                              │
│              (OpenGL ES rendering → AHardwareBuffer)             │
└───────────────────────────┬─────────────────────────────────────┘
                            │ Binder IPC + Shared Memory
          ┌─────────────────┴─────────────────┐
          ▼                                   ▼
┌─────────────────────┐             ┌─────────────────────┐
│    MosisUnreal      │             │      MosisVR        │
│   (UE5.5 Plugin)    │             │  (Unity Package)    │
│   Vulkan Import     │             │  Vulkan/OpenGL      │
└─────────────────────┘             └─────────────────────┘
```

## MosisUnreal (Unreal Engine 5.5)

**Location**: `D:\Dev\Mosis\MosisUnreal\Plugins\MosisSDK\`

### Build Script (Recommended)

Use the build script for one-command builds and deployment:

```batch
cd D:\Dev\Mosis\MosisUnreal

:: Build, install, and launch (default)
build-unreal.bat

:: Build APK only
build-unreal.bat build

:: Install to device only (requires prior build)
build-unreal.bat install

:: Launch app (starts MosisService first, then MosisUnreal)
build-unreal.bat launch

:: Clean build artifacts
build-unreal.bat clean
```

The script auto-detects connected Android devices, handles APK and OBB installation, and properly sequences app launches (MosisService must start before MosisUnreal).

### Manual Build Commands

```batch
:: Windows Editor Build
"D:\Epic\UE_5.5\Engine\Build\BatchFiles\Build.bat" ^
    MosisUnrealEditor Win64 Development ^
    -Project="D:\Dev\Mosis\MosisUnreal\MosisUnreal.uproject"

:: Android APK Build
"D:\Epic\UE_5.5\Engine\Build\BatchFiles\RunUAT.bat" ^
    BuildCookRun ^
    -project="D:\Dev\Mosis\MosisUnreal\MosisUnreal.uproject" ^
    -platform=Android -clientconfig=Development ^
    -build -cook -stage -pak -package -noP4

:: Clean Build (delete these folders first)
rmdir /s /q "Intermediate\Build"
rmdir /s /q "Binaries"
```

### Output Files

| Build | Output |
|-------|--------|
| Windows Editor | `Plugins/MosisSDK/Binaries/Win64/UnrealEditor-MosisSDK.dll` |
| Android APK | `Binaries/Android/MosisUnreal-arm64.apk` |
| Android OBB | `Binaries/Android/main.1.com.omixlab.MosisUnreal.obb` |

### Requirements

- Android SDK Platform 36 (for AIDL binder headers)
- Android Build Tools 36.1.0 (for AIDL compiler)
- `ANDROID_HOME` environment variable set

### Quest Deployment

**IMPORTANT**: Git Bash on Windows converts Unix paths to Windows paths. Use `MSYS_NO_PATHCONV=1` prefix for all adb push commands.

```bash
# Set Quest device ID (check with: adb devices -l)
QUEST=2G0YC5ZF7W01T0

# Install APK
adb -s $QUEST install -r D:/Dev/Mosis/MosisUnreal/Binaries/Android/MosisUnreal-arm64.apk

# Create OBB temp directory
adb -s $QUEST shell "mkdir -p /data/local/tmp/obb/com.omixlab.MosisUnreal"

# Push OBB to temp location (MUST use MSYS_NO_PATHCONV=1)
MSYS_NO_PATHCONV=1 adb -s $QUEST push D:/Dev/Mosis/MosisUnreal/Binaries/Android/main.1.com.omixlab.MosisUnreal.obb /data/local/tmp/obb/com.omixlab.MosisUnreal/

# Move OBB to final location
adb -s $QUEST shell "rm -rf /sdcard/Android/obb/com.omixlab.MosisUnreal"
adb -s $QUEST shell "mv /data/local/tmp/obb/com.omixlab.MosisUnreal /sdcard/Android/obb/"

# Start MosisService first, then MosisUnreal
adb -s $QUEST shell am start -n com.omixlab.mosis/.MainActivity
sleep 3
adb -s $QUEST shell am start -n com.omixlab.MosisUnreal/com.epicgames.unreal.GameActivity

# Monitor logs
adb -s $QUEST logcat -s MosisSDK MosisOS MosisTest
```

**Full rebuild and deploy sequence**:
```bash
# 1. Build Android APK+OBB
"D:\Epic\UE_5.5\Engine\Build\BatchFiles\RunUAT.bat" BuildCookRun \
    -project="D:\Dev\Mosis\MosisUnreal\MosisUnreal.uproject" \
    -platform=Android -clientconfig=Development \
    -build -cook -stage -pak -package -noP4

# 2. Stop app, push APK and OBB, restart
QUEST=2G0YC5ZF7W01T0
adb -s $QUEST shell am force-stop com.omixlab.MosisUnreal
adb -s $QUEST install -r D:/Dev/Mosis/MosisUnreal/Binaries/Android/MosisUnreal-arm64.apk
adb -s $QUEST shell "mkdir -p /data/local/tmp/obb/com.omixlab.MosisUnreal"
MSYS_NO_PATHCONV=1 adb -s $QUEST push D:/Dev/Mosis/MosisUnreal/Binaries/Android/main.1.com.omixlab.MosisUnreal.obb /data/local/tmp/obb/com.omixlab.MosisUnreal/
adb -s $QUEST shell "rm -rf /sdcard/Android/obb/com.omixlab.MosisUnreal && mv /data/local/tmp/obb/com.omixlab.MosisUnreal /sdcard/Android/obb/"
adb -s $QUEST shell am start -n com.omixlab.MosisUnreal/com.epicgames.unreal.GameActivity
```

**Common issues**:
- App stuck on loading screen: OBB not pushed or wrong version
- `adb push` path errors: Missing `MSYS_NO_PATHCONV=1` prefix
- Service not connecting: Start `com.omixlab.mosis` before the game

### VR Pointer Interaction

The MosisSDK includes `UMosisPointerComponent` for VR ray-based touch interaction.

**Key Files**:
- `Public/MosisPointerComponent.h` - Component header
- `Private/MosisPointerComponent.cpp` - Implementation

**Features**:
- Raycast from component transform (attach as child of motion controller)
- Automatic detection of `AMosisPhoneActor` hits
- Touch state machine: Down/Move/Up events
- Optional Enhanced Input binding via `TriggerAction` property
- Manual control via `SetTriggerPressed(bool)`
- Debug ray visualization

**Blueprint Setup**:
1. Add `MosisPointerComponent` as child of `MotionControllerComponent`
2. Set `TriggerAction` to your trigger input action (optional)
3. Touch events are sent automatically when pointing at phone and triggering

**C++ Setup**:
```cpp
// In VR Pawn header
UPROPERTY(VisibleAnywhere)
UMosisPointerComponent* RightPointer;

// In constructor
RightPointer = CreateDefaultSubobject<UMosisPointerComponent>(TEXT("RightPointer"));
RightPointer->SetupAttachment(RightMotionController);

// In BeginPlay (if using Enhanced Input)
RightPointer->TriggerAction = TriggerInputAction;
```

**Manual Control (without Enhanced Input)**:
```cpp
// In your input handling code
void AMyVRPawn::OnTriggerPressed()
{
    RightPointer->SetTriggerPressed(true);
}

void AMyVRPawn::OnTriggerReleased()
{
    RightPointer->SetTriggerPressed(false);
}
```

**Component Properties**:

| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `RayLength` | float | 500.0 | Maximum ray length in cm |
| `bShowDebugRay` | bool | false | Draw debug visualization |
| `DebugRayColor` | FLinearColor | Red | Ray color when not hitting |
| `DebugRayHitColor` | FLinearColor | Green | Ray color when hitting phone |
| `TraceChannel` | ECollisionChannel | Visibility | Collision channel for raycast |
| `TriggerAction` | UInputAction* | nullptr | Enhanced Input action for trigger |

**Blueprint Functions**:

| Function | Returns | Description |
|----------|---------|-------------|
| `IsPointingAtPhone()` | bool | True if ray hits a phone actor |
| `GetTargetPhone()` | AMosisPhoneActor* | Currently targeted phone (or nullptr) |
| `GetHitLocation()` | FVector | World location of ray hit |
| `GetRayOrigin()` | FVector | Ray start position |
| `GetRayDirection()` | FVector | Ray direction vector |
| `IsTriggerPressed()` | bool | Current trigger state |
| `SetTriggerPressed(bool)` | void | Manual trigger control |

**Touch State Machine**:
```
Idle ──[raycast hit]──► Hover ──[trigger]──► Pressed
  ▲                       │                      │
  │                       │ raycast miss         │ trigger release
  │                       ▼                      ▼
  └──────────────────── Idle ◄────────────────────

Events:
- Idle → Pressed: SendTouch(Down)
- Pressed + moving: SendTouch(Move)
- Pressed → Idle: SendTouch(Up)
```

## MosisVR (Unity 6000.3.2f1)

**Location**: `D:\Dev\Mosis\MosisVR\Packages\com.omixlab.mosis_sdk\`

### Direct APK Build (Recommended)

```batch
"C:\Program Files\Unity\Hub\Editor\6000.3.2f1\Editor\Unity.exe" ^
    -batchmode -quit -nographics ^
    -projectPath "D:\Dev\Mosis\MosisVR" ^
    -executeMethod BuildScript.BuildAndroidDirectCI ^
    -outputPath "D:\Dev\Mosis\Builds\Unity\Android\MosisVR.apk"
```

### Export + Gradle Build

For more control, export a Gradle project then build separately:

```batch
:: Step 1: Export from Unity
"C:\Program Files\Unity\Hub\Editor\6000.3.2f1\Editor\Unity.exe" ^
    -batchmode -quit -nographics ^
    -projectPath "D:\Dev\Mosis\MosisVR" ^
    -executeMethod BuildScript.BuildAndroidCI ^
    -export true ^
    -outputPath "D:\Dev\Mosis\Builds\Unity\Android\MosisVR"

:: Step 2: Build with Gradle
cd D:\Dev\Mosis\Builds\Unity\Android\MosisVR
gradle assembleRelease
:: APK at: launcher\build\outputs\apk\release\launcher-release.apk
```

### Unity Editor Manual Build

1. File > Build Settings > Android
2. Player Settings: IL2CPP, ARM64, Vulkan + OpenGLES3
3. For direct APK: Uncheck "Export Project", click Build
4. For export: Check "Export Project", click Export

### Native Plugin Build (Manual)

The native plugin builds automatically via CMake during Unity's build. To rebuild manually:

```batch
cd Packages/com.omixlab.mosis_sdk/Plugins/Android/cpp
cmake -B build -DCMAKE_TOOLCHAIN_FILE=%ANDROID_NDK_HOME%/build/cmake/android.toolchain.cmake ^
    -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=android-29
cmake --build build
```

## Device Testing (Both Engines)

```bash
# Install MosisService first
adb install -r MosisService-debug.apk

# Install game client
adb install -r MosisUnreal-arm64.apk  # or MosisVR.apk

# Launch service
adb shell am start -n com.omixlab.mosis/.MainActivity

# Launch client
adb shell am start -n com.omixlab.MosisUnreal/com.epicgames.unreal.GameActivity
# or for Unity:
adb shell am start -n com.omixlab.mosisvr/com.unity3d.player.UnityPlayerActivity

# Monitor all Mosis logs
adb logcat -s MosisSDK MosisTest RMLUI Vulkan
```

## Vulkan HardwareBuffer Import

Both game engines use Vulkan to import AHardwareBuffer from MosisService.

### UE5 Implementation

MosisUnreal uses UE5's built-in Vulkan RHI API for hardware buffer import:

```cpp
#include "IVulkanDynamicRHI.h"

// Import AHardwareBuffer as Vulkan texture (zero-copy)
IVulkanDynamicRHI* VulkanRHI = GetIVulkanDynamicRHI();
FTextureRHIRef ImportedTexture = VulkanRHI->RHICreateTexture2DFromAndroidHardwareBuffer(Buffer);

// GPU-to-GPU copy to destination texture each frame
ENQUEUE_RENDER_COMMAND(CopyMosisTexture)(
    [SrcTexture, DstTexture, Width, Height](FRHICommandListImmediate& RHICmdList)
    {
        RHICmdList.Transition(FRHITransitionInfo(SrcTexture, ERHIAccess::Unknown, ERHIAccess::CopySrc));
        RHICmdList.Transition(FRHITransitionInfo(DstTexture, ERHIAccess::Unknown, ERHIAccess::CopyDest));

        FRHICopyTextureInfo CopyInfo;
        CopyInfo.Size = FIntVector(Width, Height, 1);
        RHICmdList.CopyTexture(SrcTexture, DstTexture, CopyInfo);

        RHICmdList.Transition(FRHITransitionInfo(DstTexture, ERHIAccess::CopyDest, ERHIAccess::SRVMask));
    }
);
```

**Key files:**
- `MosisPhoneTexture.cpp` - Vulkan import and GPU copy
- `MosisPhoneComponent.cpp` - Frame update coordination

### Required Vulkan Extensions

UE5's VulkanRHI enables these automatically on Android:

```
VK_ANDROID_external_memory_android_hardware_buffer
VK_KHR_external_memory
VK_KHR_dedicated_allocation
```

### Raw Vulkan Import Pattern (Reference)

For custom implementations outside UE5:

```cpp
// 1. Query buffer properties
VkAndroidHardwareBufferPropertiesANDROID props = {
    .sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID
};
VkAndroidHardwareBufferFormatPropertiesANDROID formatProps = {
    .sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID
};
props.pNext = &formatProps;
vkGetAndroidHardwareBufferPropertiesANDROID(device, buffer, &props);

// 2. Create image with external memory
VkExternalMemoryImageCreateInfo extInfo = {
    .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
    .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID
};

VkImageCreateInfo imageInfo = {
    .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
    .pNext = &extInfo,
    .imageType = VK_IMAGE_TYPE_2D,
    .format = formatProps.format,
    .extent = {desc.width, desc.height, 1},
    .mipLevels = 1,
    .arrayLayers = 1,
    .samples = VK_SAMPLE_COUNT_1_BIT,
    .tiling = VK_IMAGE_TILING_OPTIMAL,
    .usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
    .sharingMode = VK_SHARING_MODE_EXCLUSIVE
};
vkCreateImage(device, &imageInfo, nullptr, &image);

// 3. Import memory from HardwareBuffer
VkImportAndroidHardwareBufferInfoANDROID importInfo = {
    .sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID,
    .buffer = buffer
};

VkMemoryDedicatedAllocateInfo dedicatedInfo = {
    .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
    .pNext = &importInfo,
    .image = image
};

VkMemoryAllocateInfo allocInfo = {
    .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
    .pNext = &dedicatedInfo,
    .allocationSize = props.allocationSize,
    .memoryTypeIndex = FindMemoryType(props.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
};
vkAllocateMemory(device, &allocInfo, nullptr, &memory);
vkBindImageMemory(device, image, memory, 0);
```

### Double Buffering

The imported image is copied to a local texture each frame via GPU copy to prevent data races with MosisService rendering. This is a fast GPU-to-GPU blit operation.
