This mod adds Virtual Reality support to Might and Magic VII using OpenXR.
If you just want to play the game in VR, follow these simple steps:
- Download: Get the latest
OpenEnrothVR.exefrom the bin/ folder. - Install: Copy
OpenEnrothVR.exeinto your Might and Magic VII installation folder (the one containing theDATA,ANIMS, andSOUNDSfolders). - VR Setup:
- Ensure you have a VR headset connected (Meta Quest, Valve Index, etc.).
- Start SteamVR (or your preferred OpenXR runtime).
- Play: Run
OpenEnrothVR.exefrom your game folder.
- 6DOF tracking: HMD orientation controls camera.
- Stereoscopic rendering: full 3D world for both eyes.
- Input: VR controllers or keyboard/mouse.
- Engine: OpenEnroth (modern C++ reimplementation of MM7).
- Backend: OpenXR (SteamVR, Oculus/Meta, WMR).
Note: Playable, but developing quality of life features.
- Game data: MM7 assets (
ANIMS,DATA,MUSIC,SOUNDS). - VR runtime: SteamVR (recommended) or any OpenXR runtime.
- Build tools (only for building):
- CMake 3.20+ (VR docs) / CMake 3.27+ (OpenEnroth docs).
- C++ compiler: VS 2019/2022 recommended for VR; minimums: VS 2022, GCC 13, AppleClang 15/Clang 15.
- SDL3 (window/context).
Main dependencies (OpenEnroth):
- SDL3, FFmpeg, OpenAL Soft, Zlib.
Optional:
- Python 3.x for style checks.
IDEs tested: VS 2022+, VS Code 2022+, CLion 2022+.
Left controller (locomotion & UI)
- Stick up/down: move forward/back.
- Stick left/right: strafe.
- X Button: Toggle World-Locked Overlay (GUI Billboard) while in game.
- Y Button / ESC: Open Main Menu (activates World-Locked Overlay).
- Raycast: Point at 2D screens to move the mouse cursor.
Right controller (view/action)
- Stick left/right: snap/smooth turn.
- Stick up: jump.
- Trigger: Interact / Select / Mouse Left Click.
- Grip: Combat / Cast / Quick Cast.
- Clone:
git clone https://github.com/vallewillian-source/open-enroth-vr.git cd open-enroth-vr - Configure:
FetchContent downloads OpenXR SDK + deps.
mkdir build cd build cmake .. - Compile:
cmake --build . --config Release - Install: copy
OpenEnrothVR.exe(found inbuild/src/Bin/OpenEnroth/Release/) to your MM7 folder (whereDATAis).
- Start SteamVR.
- Run
OpenEnrothVR.exe. - If headset is black, set SteamVR as default OpenXR runtime:
SteamVR Settings > Developer > Set SteamVR as OpenXR Runtime.
Keyboard/mouse still works for standard MM7 in VR view.
VRManagersingleton for OpenXR (XrInstance,XrSession,XrSpace).- Stereo swapchains (double-buffered).
- Converts OpenXR Y-Up (RH) to OpenEnroth Z-Up.
- Uses SDL3 to get
HWND/HGLRCreliably on Windows 11.
Engine loop (src/Engine/Engine.cpp)
- If VR initialized, begin OpenXR frame.
- For each eye:
- Acquire swapchain image.
- Bind VR FBO.
drawWorld()with VR flags.- Release swapchain image.
xrEndFrame.- Render desktop view (debug/spectator).
OpenGL renderer (src/Engine/Graphics/Renderer/OpenGLRenderer.cpp)
_set_3d_modelview_matrixand_set_3d_projection_matrixuseVRManagermatrices whenIsRenderingVREye().BeginScene3Drespects currently bound VR FBO (does not reset to backbuffer).
The mod uses a unified system for all 2D interfaces (Main Menu, Houses, Shops, Guilds, Dungeons, and GUI Billboards).
When a 2D interface is active (e.g., m_isGameMenuOpen or m_showGuiBillboard), the 2D screen is projected onto a virtual plane in the 3D world.
- Capture: The game's 2D output is captured using
glBlitFramebufferinto an overlay texture. - Positioning: On activation, the overlay is placed 2.5m in front of the player's current HMD position and oriented towards them. This "world-locks" the screen, providing a stable "virtual monitor" experience.
- Rendering: The overlay is rendered as a separate layer in the VR composition, ensuring high clarity and comfort.
Interaction with 2D screens is handled via a raycast system from the left controller, replacing the traditional stick-based cursor movement.
- Ray Generation: A laser beam is projected from the left controller's position and orientation.
- Intersection: The system calculates the intersection between the ray and the virtual 2D plane.
- Cursor Mapping: The intersection point is mapped to normalized (0.0 to 1.0) coordinates, which are then converted to the game's native resolution (e.g., 640x480).
- Input Injection: These coordinates are injected into the game's mouse logic via
VRManager::GetMenuMouseState, allowing seamless interaction with all existing UI elements. - Clicks: The right controller's trigger acts as the Left Mouse Button.
- To push objects farther and improve comfort, move each eye image away from the nose (more parallel).
- VR lenses focus at fixed distance (~2m). Convergence should simulate this to avoid nausea.
- World-locked full-screen UIs are more comfortable than head-locked.
FetchContentdownloads OpenXR SDK.- Static library target
engine_vrencapsulates VR logic.
- Never compile automatically generated code.
- Always use explicit casts (no implicit casts).
- Never use
dynamic_cast. - Always use
static_castfor type conversion. - Never use
reinterpret_castfor type conversion.
This section describes the development process and is required reading for contributors.
Main: SDL3, FFmpeg, OpenAL Soft, Zlib.
Prebuilt dependencies are used by default and resolved during CMake.
- Install Git and Visual Studio 2022.
- Windows SDK v10.0.20348.0+.
- Clone/fork
https://github.com/vallewillian-source/open-enroth-vr. - CMake: standalone or VS-provided; add to PATH if needed.
- Open folder in VS, pick x32/x64, wait for CMake, set startup
OpenEnrothVR.exe, run.
Based on Google C++ Style Guide. PRs fail on style issues.
- Style checks: build
check_styletarget. - Documentation: Doxygen format (
/**and@). - Naming:
CamelCasefor types,camelCasefor methods/variables. Private members start with_.
Policy: add tests for fixable bugs when possible.
- Unit tests: Google Test (
OpenEnroth_UnitTest). - Game tests: Instrumented engine; events injected between frames.
Scripts in resources/scripts.
- Lua Language Server (LuaLS) recommended for development.
In-game debug console:
- Launch game.
- Load/create game.
- Press
~.
The unified 2D system handles the rendering, but the game logic follows this flow:
An event in EvtInterpreter.cpp calls enterHouse(houseId).
- Validates opening hours and bans in
src/GUI/UI/UIHouses.cpp. createHouseUI(houseId)creates theGUIWindow_House.current_screen_typebecomesSCREEN_HOUSE.
houseDialogPressEscape():
- Clears NPC state and releases the dialogue window.
current_screen_typereturns toSCREEN_GAME, resuming game time.