├── .github └── workflows │ └── build-on-push.yml ├── .gitignore ├── .gitmodules ├── .vscode ├── .gitignore └── tasks.json ├── LICENSE ├── README.md ├── SConstruct ├── autoload.png ├── demo ├── .gitignore ├── Hud.gd ├── Main.gd ├── Main.tscn ├── addons │ └── godot_xr_reference │ │ ├── bin │ │ └── .gitignore │ │ ├── godot_xr_reference.gd │ │ ├── godot_xr_reference.gdextension │ │ ├── plugin.cfg │ │ └── plugin.gd ├── assets │ └── wahooney.itch.io │ │ ├── README.md │ │ ├── blue_grid.png │ │ ├── blue_grid.png.import │ │ ├── blue_grid.tres │ │ ├── brown_grid.png │ │ ├── brown_grid.png.import │ │ ├── green_grid.png │ │ ├── green_grid.png.import │ │ ├── green_grid.tres │ │ ├── white_grid.png │ │ └── white_grid.png.import ├── default_env.tres ├── icon.png ├── icon.png.import └── project.godot ├── screenshot.png └── src ├── .gitignore ├── register_type.cpp ├── register_types.h ├── xr_interface_reference.cpp └── xr_interface_reference.h /.github/workflows/build-on-push.yml: -------------------------------------------------------------------------------- 1 | # Workflow to automatically compile a Linux/Windows library on commit/push 2 | name: Build on push 3 | 4 | # Controls when the action will run. Triggers the workflow on push or pull request 5 | # events, but only for the master branch we'll create .zip files 6 | on: 7 | [push, pull_request] 8 | 9 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 10 | jobs: 11 | # This job builds the plugin for our target platforms 12 | build: 13 | name: Building for ${{ matrix.platform }} (${{ matrix.os }}) 14 | runs-on: ${{ matrix.os }} 15 | strategy: 16 | fail-fast: true 17 | matrix: 18 | include: 19 | - name: 🐧 Linux (GCC) 20 | os: ubuntu-20.04 21 | platform: linux 22 | artifact-name: gdxrreference.linux 23 | artifact-path: demo/addons/godot_xr_reference/bin/libgdxrreference.linux.* 24 | 25 | - name: 🏁 Windows (x86_64, MSVC) 26 | os: windows-2019 27 | platform: windows 28 | artifact-name: gdxrreference.windows 29 | artifact-path: demo/addons/godot_xr_reference/bin/libgdxrreference.windows.* 30 | 31 | - name: 🍎 macOS (universal) 32 | os: macos-11 33 | platform: macos 34 | flags: arch=universal 35 | artifact-name: gdxrreference.macos 36 | artifact-path: demo/addons/godot_xr_reference/bin/libgdxrreference.macos.* 37 | 38 | - name: 🤖 Android (arm64) 39 | os: ubuntu-20.04 40 | platform: android 41 | flags: ANDROID_NDK_ROOT=$ANDROID_NDK_LATEST_HOME arch=arm64 42 | artifact-name: gdxrreference.android 43 | artifact-path: demo/addons/godot_xr_reference/bin/libgdxrreference.android.* 44 | 45 | - name: 🍏 iOS (arm64) 46 | os: macos-11 47 | platform: ios 48 | flags: arch=arm64 49 | artifact-name: gdxrreference.ios 50 | artifact-path: demo/addons/godot_xr_reference/bin/libgdxrreference.ios.* 51 | 52 | # Steps represent a sequence of tasks that will be executed as part of the job 53 | steps: 54 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 55 | - name: Setup actions 56 | uses: actions/checkout@v3 57 | with: 58 | submodules: 'recursive' 59 | 60 | - name: Set up Python (for SCons) 61 | uses: actions/setup-python@v4 62 | with: 63 | python-version: '3.x' 64 | 65 | - name: Linux dependencies 66 | if: ${{ matrix.platform == 'linux' }} 67 | run: | 68 | sudo apt-get update -qq 69 | sudo apt-get install -qqq build-essential pkg-config 70 | 71 | - name: Install scons 72 | run: | 73 | python -m pip install scons==4.0.0 74 | 75 | - name: Build debug build 76 | run: | 77 | scons platform=${{ matrix.platform }} target=template_debug ${{ matrix.flags }} 78 | 79 | - name: Build release build 80 | run: | 81 | scons platform=${{ matrix.platform }} target=template_release ${{ matrix.flags }} 82 | 83 | - name: Upload artifact 84 | uses: actions/upload-artifact@v3 85 | with: 86 | name: ${{ matrix.artifact-name }} 87 | path: ${{ matrix.artifact-path }} 88 | if-no-files-found: error 89 | 90 | # This job collects the build output and assembles the final asset (artifact) 91 | asset: 92 | name: Assembling the asset (artifact) 93 | runs-on: ubuntu-20.04 94 | needs: build 95 | if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags')) 96 | 97 | # Steps represent a sequence of tasks that will be executed as part of the job 98 | steps: 99 | - name: Setup actions 100 | uses: actions/checkout@v3 101 | with: 102 | path: source 103 | - name: Download all workflow run artifacts 104 | uses: actions/download-artifact@v3 105 | - name: Copy files to destination 106 | run: | 107 | mkdir plugin 108 | mkdir plugin/addons 109 | cp -r source/demo/addons/godot_xr_reference plugin/addons 110 | cp gdxrreference.linux/*.so plugin/addons/godot_xr_reference/bin/ 111 | cp gdxrreference.windows/*.dll plugin/addons/godot_xr_reference/bin/ 112 | cp gdxrreference.android/*.so plugin/addons/godot_xr_reference/bin/ 113 | cp gdxrreference.ios/*.dylib plugin/addons/godot_xr_reference/bin/ 114 | cp -R gdxrreference.macos/libgdxrreference.macos.* plugin/addons/godot_xr_reference/bin/ 115 | - name: Calculate GIT short ref 116 | run: | 117 | cd source 118 | echo "GITHUB_SHA_SHORT=$(git rev-parse --short ${{ github.sha }})" >> $GITHUB_ENV 119 | cd .. 120 | if: github.ref == 'refs/heads/master' 121 | - name: Get tag name 122 | run: | 123 | echo "GITHUB_SHA_SHORT=$(echo ${GITHUB_REF##*/})" >> $GITHUB_ENV 124 | if: startsWith(github.ref, 'refs/tags') 125 | - name: Clean up extracted files 126 | run: | 127 | rm -rf gdxrreference.linux 128 | rm -rf gdxrreference.windows 129 | rm -rf gdxrreference.android 130 | rm -rf gdxrreference.macos 131 | rm -rf gdxrreference.ios 132 | rm -rf source 133 | rm -rf .git 134 | mv plugin gdxrreference_${{ env.GITHUB_SHA_SHORT }} 135 | - name: Zip asset 136 | run: | 137 | zip -qq -r gdxrreference.zip . 138 | - name: Create and upload asset 139 | uses: ncipollo/release-action@v1 140 | with: 141 | allowUpdates: true 142 | artifacts: "gdxrreference.zip" 143 | omitNameDuringUpdate: true 144 | omitBodyDuringUpdate: true 145 | omitPrereleaseDuringUpdate : true 146 | token: ${{ secrets.GITHUB_TOKEN }} 147 | if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') 148 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # msvc debug files 2 | *.idb 3 | *.pdb 4 | 5 | # scons files 6 | *.sconsign.dblite 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "godot-cpp"] 2 | path = godot-cpp 3 | url = https://github.com/godotengine/godot-cpp 4 | -------------------------------------------------------------------------------- /.vscode/.gitignore: -------------------------------------------------------------------------------- 1 | # these are vscode project files, but we do keep the tasks.json one in that might be useful for people 2 | * 3 | !.gitignore 4 | !tasks.json 5 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "build debug", 8 | "type": "shell", 9 | "options": { 10 | "cwd": "${workspaceFolder}" 11 | }, 12 | "command": "scons", 13 | "group": "build", 14 | "args": [ 15 | "target=template_debug", 16 | "-j8" 17 | ], 18 | "problemMatcher": "$msCompile" 19 | }, 20 | { 21 | "label": "build release", 22 | "type": "shell", 23 | "options": { 24 | "cwd": "${workspaceFolder}" 25 | }, 26 | "command": "scons", 27 | "group": "build", 28 | "args": [ 29 | "target=template_release", 30 | "-j8" 31 | ], 32 | "problemMatcher": "$msCompile" 33 | }, 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Bastiaan Olij and contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Godot XR Reference plugin 2 | 3 | ![GitHub forks](https://img.shields.io/github/forks/godotvr/godot_xr_reference?style=plastic) 4 | ![GitHub Repo stars](https://img.shields.io/github/stars/godotvr/godot_xr_reference?style=plastic) 5 | ![GitHub contributors](https://img.shields.io/github/contributors/godotvr/godot_xr_reference?style=plastic) 6 | ![GitHub](https://img.shields.io/github/license/godotvr/godot_xr_reference?style=plastic) 7 | ![GitHub release (latest by date)](https://img.shields.io/github/v/release/godotvr/godot_xr_reference?style=plastic) 8 | 9 | This is a reference plugin to use as a base to implement XR plugins for Godot 4 and later. 10 | 11 | This example XR interface implements simple side by side output with lens distortion and reacts to WASD and mouse input to move the player around. Click on the screen to capture input, press escape to release input. 12 | ![screenshot](screenshot.png) 13 | 14 | ## Building this plugin 15 | Make sure that when you clone this repository you initialise the submodules recursively. 16 | 17 | Simply compile the plugin with: 18 | ``` 19 | scons target=template_debug 20 | ``` 21 | 22 | > It is important to build debug builds as the editor requires debug builds to run. You can build a release build to distribute alongside your game with `target=template_release`. 23 | 24 | # VSCode 25 | For convencience I've added my `tasks.json` build configuration for `vscode` into this repository. This included the above mentioned build instructions. 26 | 27 | # Demo 28 | 29 | The demo folder contains an example project that implement this interface and is used as the build destination when compiling the plugin. 30 | 31 | # Registering the XRInterface 32 | 33 | In order for your interface to be accessible within Godot it has to be registered with the XRServer. 34 | As the XRServer isn't accessibly from GDExtensions until after registration has been completed, even though it does exist beforehand, we'll need to handle this with a script. 35 | 36 | To ensure the interface is also properly cleaned up we'll create this as an autoload script. 37 | Note that this is marked as a toolscript to ensure the IDE can access meta data provided by the interface. 38 | 39 | ``` 40 | @tool 41 | extends Node 42 | 43 | var xr_interface : XRInterfaceReference 44 | 45 | func get_interface(): 46 | return xr_interface 47 | 48 | 49 | func _enter_tree(): 50 | xr_interface = XRInterfaceReference.new() 51 | if xr_interface: 52 | XRServer.add_interface(xr_interface) 53 | 54 | 55 | func _exit_tree(): 56 | if xr_interface: 57 | XRServer.remove_interface(xr_interface) 58 | xr_interface = null 59 | ``` 60 | 61 | You can register this script on the autoload tab in the project settings like so: 62 | ![autoload](autoload.png "Autoload project settings") 63 | 64 | Note that the demo project also contains a `plugin.cfg` and `plugin.gd` script that will trigger automatic registration of the autoload script. This serves purely as an example and is optional. 65 | 66 | # Initialising the interface 67 | 68 | To use the interface it has to be initialised. This code is similar to that of other XR interfaces in Godot. 69 | 70 | You can do all thats needed in you main script however in our demo a convencience function was added to our autoload script: 71 | ``` 72 | func start_xr(): 73 | if xr_interface: 74 | print("Capabilities " + str(xr_interface.get_capabilities())) 75 | print("Target size " + str(xr_interface.get_render_target_size())) 76 | 77 | if xr_interface.initialize(): 78 | get_viewport().use_xr = true 79 | 80 | print("Initialised") 81 | else: 82 | print("Failed to initialise") 83 | else: 84 | print("Interface was not instantiated") 85 | ``` 86 | 87 | This can now be called from our main script: 88 | 89 | ``` 90 | func _ready(): 91 | XRReferenceInterface.start_xr() 92 | ``` 93 | 94 | # About the author 95 | 96 | This repository is maintained by Bastiaan "Mux212" Olij 97 | Further information to follow. 98 | -------------------------------------------------------------------------------- /SConstruct: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | env = SConscript("godot-cpp/SConstruct") 6 | 7 | # For the reference: 8 | # - CCFLAGS are compilation flags shared between C and C++ 9 | # - CFLAGS are for C-specific compilation flags 10 | # - CXXFLAGS are for C++-specific compilation flags 11 | # - CPPFLAGS are for pre-processor flags 12 | # - CPPDEFINES are for pre-processor defines 13 | # - LINKFLAGS are for linking flags 14 | 15 | target_path = "demo/addons/godot_xr_reference/bin" 16 | target_name = "libgdxrreference" 17 | 18 | # tweak this if you want to use different folders, or more folders, to store your source code in. 19 | env.Append(CPPPATH=["src/"]) 20 | sources = Glob("src/*.cpp") 21 | 22 | if env["platform"] == "macos": 23 | library = env.SharedLibrary( 24 | "{}/{}.{}.{}.framework/libgdexample.{}.{}".format( 25 | target_path, target_name, env["platform"], env["target"], env["platform"], env["target"] 26 | ), 27 | source=sources, 28 | ) 29 | else: 30 | library = env.SharedLibrary( 31 | "{}/{}{}{}".format(target_path, target_name, env["suffix"], env["SHLIBSUFFIX"]), 32 | source=sources, 33 | ) 34 | 35 | Default(library) 36 | -------------------------------------------------------------------------------- /autoload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GodotVR/godot_xr_reference/8f26d23a57f18f0b7a0378820b082f98299aee70/autoload.png -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | # ignore caches 2 | .godot 3 | 4 | -------------------------------------------------------------------------------- /demo/Hud.gd: -------------------------------------------------------------------------------- 1 | extends Node3D 2 | 3 | @export var camera : NodePath 4 | 5 | func _process(_delta): 6 | if camera: 7 | var camera_node : XRCamera3D = get_node(camera) 8 | 9 | if camera_node: 10 | var t = camera_node.transform 11 | var forward : Vector3 = -t.basis.z 12 | forward.y = 0.0 13 | forward.normalized() 14 | 15 | position = t.origin 16 | 17 | look_at(t.origin + forward) 18 | 19 | var fps = Performance.get_monitor(Performance.TIME_FPS) 20 | $FPS.text = "FPS: " + str(fps) 21 | -------------------------------------------------------------------------------- /demo/Main.gd: -------------------------------------------------------------------------------- 1 | extends Node3D 2 | 3 | # Called when the node enters the scene tree for the first time. 4 | func _ready(): 5 | XRReferenceInterface.start_xr() 6 | 7 | func _input(event): 8 | if event is InputEventKey: 9 | if event.pressed and event.keycode == KEY_ESCAPE: 10 | var xr_interface = XRReferenceInterface.get_interface() 11 | if xr_interface: 12 | xr_interface.use_mouse_for_headtracking = false 13 | xr_interface.use_wasd_for_movement = false 14 | 15 | # Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) 16 | elif event is InputEventMouseButton: 17 | if event.pressed: 18 | var xr_interface = XRReferenceInterface.get_interface() 19 | if xr_interface: 20 | xr_interface.use_mouse_for_headtracking = true 21 | xr_interface.use_wasd_for_movement = true 22 | 23 | # until we can receive proper input events in our interface we don't have access to the mouse when captured 24 | # Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) 25 | -------------------------------------------------------------------------------- /demo/Main.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=10 format=3 uid="uid://c4inay0af44y5"] 2 | 3 | [ext_resource type="Script" path="res://Main.gd" id="1_86b4p"] 4 | [ext_resource type="Script" path="res://Hud.gd" id="2_bnd5b"] 5 | [ext_resource type="Material" path="res://assets/wahooney.itch.io/blue_grid.tres" id="2_hf54b"] 6 | [ext_resource type="Material" path="res://assets/wahooney.itch.io/green_grid.tres" id="3_kb4h0"] 7 | 8 | [sub_resource type="PhysicalSkyMaterial" id="PhysicalSkyMaterial_aaq4s"] 9 | 10 | [sub_resource type="Sky" id="Sky_isho0"] 11 | sky_material = SubResource("PhysicalSkyMaterial_aaq4s") 12 | 13 | [sub_resource type="Environment" id="Environment_nkj1m"] 14 | background_mode = 2 15 | sky = SubResource("Sky_isho0") 16 | 17 | [sub_resource type="BoxMesh" id="BoxMesh_8mt8t"] 18 | 19 | [sub_resource type="PlaneMesh" id="PlaneMesh_f4jca"] 20 | size = Vector2(1000, 1000) 21 | 22 | [node name="Main" type="Node3D"] 23 | script = ExtResource("1_86b4p") 24 | 25 | [node name="XROrigin3D" type="XROrigin3D" parent="."] 26 | 27 | [node name="XRCamera3D" type="XRCamera3D" parent="XROrigin3D"] 28 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.8, 0) 29 | 30 | [node name="Hud" type="Node3D" parent="XROrigin3D"] 31 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.8, 0) 32 | script = ExtResource("2_bnd5b") 33 | camera = NodePath("../XRCamera3D") 34 | 35 | [node name="FPS" type="Label3D" parent="XROrigin3D/Hud"] 36 | transform = Transform3D(0.85828, 0, 0.513182, 0, 1, 0, -0.513182, 0, 0.85828, -0.491584, 0.307667, -0.697151) 37 | text = "FPS: 000" 38 | font_size = 16 39 | outline_size = 6 40 | 41 | [node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] 42 | transform = Transform3D(0.760915, -0.456129, 0.461471, 0.0679668, 0.76333, 0.642423, -0.645282, -0.457465, 0.611831, 0.404205, 2.29081, 0) 43 | 44 | [node name="WorldEnvironment" type="WorldEnvironment" parent="."] 45 | environment = SubResource("Environment_nkj1m") 46 | 47 | [node name="Cube" type="MeshInstance3D" parent="."] 48 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1.80584, 1, -5.83298) 49 | mesh = SubResource("BoxMesh_8mt8t") 50 | surface_material_override/0 = ExtResource("2_hf54b") 51 | 52 | [node name="Ground" type="MeshInstance3D" parent="."] 53 | mesh = SubResource("PlaneMesh_f4jca") 54 | surface_material_override/0 = ExtResource("3_kb4h0") 55 | -------------------------------------------------------------------------------- /demo/addons/godot_xr_reference/bin/.gitignore: -------------------------------------------------------------------------------- 1 | *.dll 2 | *.so 3 | *.lib 4 | *.obj 5 | *.o 6 | *.exp 7 | -------------------------------------------------------------------------------- /demo/addons/godot_xr_reference/godot_xr_reference.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Node 3 | 4 | var xr_interface : XRInterfaceReference 5 | 6 | func get_interface(): 7 | return xr_interface 8 | 9 | func start_xr(): 10 | if xr_interface: 11 | print("Capabilities " + str(xr_interface.get_capabilities())) 12 | print("Target size " + str(xr_interface.get_render_target_size())) 13 | 14 | if xr_interface.initialize(): 15 | get_viewport().use_xr = true 16 | 17 | print("Initialised") 18 | else: 19 | print("Failed to initialise") 20 | else: 21 | print("Interface was not instantiated") 22 | 23 | 24 | func _enter_tree(): 25 | xr_interface = XRInterfaceReference.new() 26 | if xr_interface: 27 | XRServer.add_interface(xr_interface) 28 | 29 | 30 | func _exit_tree(): 31 | if xr_interface: 32 | XRServer.remove_interface(xr_interface) 33 | xr_interface = null 34 | -------------------------------------------------------------------------------- /demo/addons/godot_xr_reference/godot_xr_reference.gdextension: -------------------------------------------------------------------------------- 1 | [configuration] 2 | 3 | entry_symbol = "xrreference_library_init" 4 | 5 | [libraries] 6 | 7 | windows.debug.x86_64 = "res://addons/godot_xr_reference/bin/libgdxrreference.windows.template_debug.x86_64.dll" 8 | windows.release.x86_64 = "res://addons/godot_xr_reference/bin/libgdxrreference.windows.template_release.x86_64.dll" 9 | linux.debug.x86_64 = "res://addons/godot_xr_reference/bin/libgdxrreference.linux.template_debug.x86_64.so" 10 | linux.release.x86_64 = "res://addons/godot_xr_reference/bin/libgdxrreference.linux.template_release.x86_64.so" 11 | android.debug.arm64 = "res://addons/godot_xr_reference/bin/libgdxrreference.android.template_debug.arm64.so" 12 | android.release.arm64 = "res://addons/godot_xr_reference/bin/libgdxrreference.android.template_release.arm64.so" 13 | ios.debug.arm64 = "res://addons/godot_xr_reference/bin/libgdxrreference.ios.template_debug.arm64.dylib" 14 | ios.release.arm64 = "res://addons/godot_xr_reference/bin/libgdxrreference.ios.template_release.arm64.dylib" 15 | macos.debug = "res://addons/godot_xr_reference/bin/libgdxrreference.macos.template_debug.framework" 16 | macos.release = "res://addons/godot_xr_reference/bin/libgdxrreference.macos.template_release.framework" 17 | -------------------------------------------------------------------------------- /demo/addons/godot_xr_reference/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="GodotXRReference" 4 | description="Godot XR Reference Interface" 5 | author="Bastiaan Olij and Contributors" 6 | version="1.0.0" 7 | script="plugin.gd" 8 | -------------------------------------------------------------------------------- /demo/addons/godot_xr_reference/plugin.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends EditorPlugin 3 | 4 | func _enter_tree(): 5 | # Register our autoload object 6 | add_autoload_singleton( 7 | "XRReferenceInterface", 8 | "res://addons/godot_xr_reference/godot_xr_reference.gd") 9 | -------------------------------------------------------------------------------- /demo/assets/wahooney.itch.io/README.md: -------------------------------------------------------------------------------- 1 | Textures were created with: https://wahooney.itch.io/texture-grid-generator 2 | -------------------------------------------------------------------------------- /demo/assets/wahooney.itch.io/blue_grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GodotVR/godot_xr_reference/8f26d23a57f18f0b7a0378820b082f98299aee70/demo/assets/wahooney.itch.io/blue_grid.png -------------------------------------------------------------------------------- /demo/assets/wahooney.itch.io/blue_grid.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://0ic7dh51i6m8" 6 | path.s3tc="res://.godot/imported/blue_grid.png-785a01a697c0777376608d81454e4958.s3tc.ctex" 7 | metadata={ 8 | "imported_formats": ["s3tc_bptc"], 9 | "vram_texture": true 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://assets/wahooney.itch.io/blue_grid.png" 15 | dest_files=["res://.godot/imported/blue_grid.png-785a01a697c0777376608d81454e4958.s3tc.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=2 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=true 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=0 36 | -------------------------------------------------------------------------------- /demo/assets/wahooney.itch.io/blue_grid.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="SpatialMaterial" load_steps=2 format=2] 2 | 3 | [ext_resource path="res://assets/wahooney.itch.io/blue_grid.png" type="Texture" id=1] 4 | 5 | [resource] 6 | albedo_texture = ExtResource( 1 ) 7 | uv1_offset = Vector3( 0.5, 0.5, 0.5 ) 8 | uv1_triplanar = true 9 | -------------------------------------------------------------------------------- /demo/assets/wahooney.itch.io/brown_grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GodotVR/godot_xr_reference/8f26d23a57f18f0b7a0378820b082f98299aee70/demo/assets/wahooney.itch.io/brown_grid.png -------------------------------------------------------------------------------- /demo/assets/wahooney.itch.io/brown_grid.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://dpr0lghno60ar" 6 | path="res://.godot/imported/brown_grid.png-5cbd37038e3ea1da588854d56a43bef6.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://assets/wahooney.itch.io/brown_grid.png" 14 | dest_files=["res://.godot/imported/brown_grid.png-5cbd37038e3ea1da588854d56a43bef6.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /demo/assets/wahooney.itch.io/green_grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GodotVR/godot_xr_reference/8f26d23a57f18f0b7a0378820b082f98299aee70/demo/assets/wahooney.itch.io/green_grid.png -------------------------------------------------------------------------------- /demo/assets/wahooney.itch.io/green_grid.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://cr1qtrfotsqhw" 6 | path.s3tc="res://.godot/imported/green_grid.png-e9d81b3d42c428786d9997769a6729a2.s3tc.ctex" 7 | metadata={ 8 | "imported_formats": ["s3tc_bptc"], 9 | "vram_texture": true 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://assets/wahooney.itch.io/green_grid.png" 15 | dest_files=["res://.godot/imported/green_grid.png-e9d81b3d42c428786d9997769a6729a2.s3tc.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=2 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=true 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=0 36 | -------------------------------------------------------------------------------- /demo/assets/wahooney.itch.io/green_grid.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="SpatialMaterial" load_steps=2 format=2] 2 | 3 | [ext_resource path="res://assets/wahooney.itch.io/green_grid.png" type="Texture" id=1] 4 | 5 | [resource] 6 | params_diffuse_mode = 1 7 | albedo_texture = ExtResource( 1 ) 8 | roughness = 0.8 9 | uv1_scale = Vector3( 100, 100, 100 ) 10 | -------------------------------------------------------------------------------- /demo/assets/wahooney.itch.io/white_grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GodotVR/godot_xr_reference/8f26d23a57f18f0b7a0378820b082f98299aee70/demo/assets/wahooney.itch.io/white_grid.png -------------------------------------------------------------------------------- /demo/assets/wahooney.itch.io/white_grid.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://dftg2lxh06qb5" 6 | path.s3tc="res://.godot/imported/white_grid.png-c97900fbca7fa76e931b70c777bf36eb.s3tc.ctex" 7 | metadata={ 8 | "imported_formats": ["s3tc_bptc"], 9 | "vram_texture": true 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://assets/wahooney.itch.io/white_grid.png" 15 | dest_files=["res://.godot/imported/white_grid.png-c97900fbca7fa76e931b70c777bf36eb.s3tc.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=2 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | -------------------------------------------------------------------------------- /demo/default_env.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Environment" load_steps=2 format=3 uid="uid://bd5t3hfr4pxuw"] 2 | 3 | [sub_resource type="Sky" id="1"] 4 | 5 | [resource] 6 | background_mode = 2 7 | sky = SubResource("1") 8 | -------------------------------------------------------------------------------- /demo/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GodotVR/godot_xr_reference/8f26d23a57f18f0b7a0378820b082f98299aee70/demo/icon.png -------------------------------------------------------------------------------- /demo/icon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://d06mfbwqfqhsj" 6 | path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://icon.png" 14 | dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /demo/project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=5 10 | 11 | [application] 12 | 13 | config/name="Godot XR Reference demo" 14 | run/main_scene="res://Main.tscn" 15 | config/features=PackedStringArray("4.1") 16 | config/icon="res://icon.png" 17 | 18 | [autoload] 19 | 20 | XRReferenceInterface="*res://addons/godot_xr_reference/godot_xr_reference.gd" 21 | 22 | [editor_plugins] 23 | 24 | enabled=PackedStringArray("res://addons/godot_xr_reference/plugin.cfg") 25 | 26 | [native_extensions] 27 | 28 | paths=["res://addons/godot_xr_reference/godot_xr_reference.gdextension"] 29 | 30 | [rendering] 31 | 32 | environment/defaults/default_environment="res://default_env.tres" 33 | vulkan/rendering/back_end=1 34 | xr/enabled=true 35 | 36 | [xr] 37 | 38 | shaders/enabled=true 39 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GodotVR/godot_xr_reference/8f26d23a57f18f0b7a0378820b082f98299aee70/screenshot.png -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | # ignore build files 2 | *.o 3 | *.obj 4 | -------------------------------------------------------------------------------- /src/register_type.cpp: -------------------------------------------------------------------------------- 1 | #include "register_types.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "xr_interface_reference.h" 12 | 13 | using namespace godot; 14 | 15 | void initialize_xrreference_module(ModuleInitializationLevel p_level) { 16 | if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { 17 | return; 18 | } 19 | 20 | ClassDB::register_class(); 21 | } 22 | 23 | void uninitialize_xrreference_module(ModuleInitializationLevel p_level) { 24 | if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { 25 | return; 26 | } 27 | 28 | // Note: our class will be unregistered automatically 29 | } 30 | 31 | extern "C" { 32 | 33 | // Initialization. 34 | 35 | GDExtensionBool GDE_EXPORT xrreference_library_init(const GDExtensionInterface *p_interface, const GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) { 36 | GDExtensionBinding::InitObject init_obj(p_interface, p_library, r_initialization); 37 | 38 | init_obj.register_initializer(initialize_xrreference_module); 39 | init_obj.register_terminator(uninitialize_xrreference_module); 40 | init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SERVERS); 41 | 42 | return init_obj.init(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/register_types.h: -------------------------------------------------------------------------------- 1 | #ifndef REGISTER_TYPES_H 2 | #define REGISTER_TYPES_H 3 | 4 | #include 5 | 6 | using namespace godot; 7 | 8 | void initialize_xrreference_module(ModuleInitializationLevel p_level); 9 | void uninitialize_xrreference_module(ModuleInitializationLevel p_level); 10 | 11 | #endif // ! REGISTER_TYPES_H 12 | -------------------------------------------------------------------------------- /src/xr_interface_reference.cpp: -------------------------------------------------------------------------------- 1 | #include "xr_interface_reference.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace godot; 9 | 10 | void XRInterfaceReference::_bind_methods() { 11 | // Methods. 12 | // ClassDB::bind_method(D_METHOD("simple_func"), &Example::simple_func); 13 | 14 | // Properties. 15 | ClassDB::bind_method(D_METHOD("get_eye_height"), &XRInterfaceReference::get_eye_height); 16 | ClassDB::bind_method(D_METHOD("set_eye_height", "eye_height"), &XRInterfaceReference::set_eye_height); 17 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "eye_height"), "set_eye_height", "get_eye_height"); 18 | 19 | ClassDB::bind_method(D_METHOD("get_intraocular_dist"), &XRInterfaceReference::get_intraocular_dist); 20 | ClassDB::bind_method(D_METHOD("set_intraocular_dist", "intraocular_dist"), &XRInterfaceReference::set_intraocular_dist); 21 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "intraocular_dist"), "set_intraocular_dist", "get_intraocular_dist"); 22 | 23 | ClassDB::bind_method(D_METHOD("get_display_width"), &XRInterfaceReference::get_display_width); 24 | ClassDB::bind_method(D_METHOD("set_display_width", "display_width"), &XRInterfaceReference::set_display_width); 25 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "display_width"), "set_display_width", "get_display_width"); 26 | 27 | ClassDB::bind_method(D_METHOD("get_display_to_lens"), &XRInterfaceReference::get_display_to_lens); 28 | ClassDB::bind_method(D_METHOD("set_display_to_lens", "display_to_lens"), &XRInterfaceReference::set_display_to_lens); 29 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "display_to_lens"), "set_display_to_lens", "get_display_to_lens"); 30 | 31 | ClassDB::bind_method(D_METHOD("get_oversample"), &XRInterfaceReference::get_oversample); 32 | ClassDB::bind_method(D_METHOD("set_oversample", "oversample"), &XRInterfaceReference::set_oversample); 33 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversample"), "set_oversample", "get_oversample"); 34 | 35 | ClassDB::bind_method(D_METHOD("get_k1"), &XRInterfaceReference::get_k1); 36 | ClassDB::bind_method(D_METHOD("set_k1", "k1"), &XRInterfaceReference::set_k1); 37 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "k1"), "set_k1", "get_k1"); 38 | 39 | ClassDB::bind_method(D_METHOD("get_k2"), &XRInterfaceReference::get_k2); 40 | ClassDB::bind_method(D_METHOD("set_k2", "k2"), &XRInterfaceReference::set_k2); 41 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "k2"), "set_k2", "get_k2"); 42 | 43 | ClassDB::bind_method(D_METHOD("get_use_mouse_for_headtracking"), &XRInterfaceReference::get_use_mouse_for_headtracking); 44 | ClassDB::bind_method(D_METHOD("set_use_mouse_for_headtracking", "use_mouse_for_headtracking"), &XRInterfaceReference::set_use_mouse_for_headtracking); 45 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_mouse_for_headtracking"), "set_use_mouse_for_headtracking", "get_use_mouse_for_headtracking"); 46 | 47 | ClassDB::bind_method(D_METHOD("get_use_wasd_for_movement"), &XRInterfaceReference::get_use_wasd_for_movement); 48 | ClassDB::bind_method(D_METHOD("set_use_wasd_for_movement", "use_wasd_for_movement"), &XRInterfaceReference::set_use_wasd_for_movement); 49 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_wasd_for_movement"), "set_use_wasd_for_movement", "get_use_wasd_for_movement"); 50 | 51 | // Signals. 52 | // ADD_SIGNAL(MethodInfo("custom_signal", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::INT, "value"))); 53 | // ClassDB::bind_method(D_METHOD("emit_custom_signal", "name", "value"), &Example::emit_custom_signal); 54 | 55 | // Constants. 56 | // BIND_ENUM_CONSTANT(FIRST); 57 | } 58 | 59 | double XRInterfaceReference::get_eye_height() const { 60 | return eye_height; 61 | } 62 | 63 | void XRInterfaceReference::set_eye_height(const double p_eye_height) { 64 | eye_height = p_eye_height; 65 | } 66 | 67 | double XRInterfaceReference::get_intraocular_dist() const { 68 | return intraocular_dist; 69 | } 70 | 71 | void XRInterfaceReference::set_intraocular_dist(const double p_intraocular_dist) { 72 | intraocular_dist = p_intraocular_dist; 73 | } 74 | 75 | double XRInterfaceReference::get_display_width() const { 76 | return display_width; 77 | } 78 | 79 | void XRInterfaceReference::set_display_width(const double p_display_width) { 80 | display_width = p_display_width; 81 | } 82 | 83 | double XRInterfaceReference::get_display_to_lens() const { 84 | return display_to_lens; 85 | } 86 | 87 | void XRInterfaceReference::set_display_to_lens(const double p_display_to_lens) { 88 | display_to_lens = p_display_to_lens; 89 | } 90 | 91 | double XRInterfaceReference::get_oversample() const { 92 | return oversample; 93 | } 94 | 95 | void XRInterfaceReference::set_oversample(const double p_oversample) { 96 | oversample = p_oversample; 97 | } 98 | 99 | double XRInterfaceReference::get_k1() const { 100 | return k1; 101 | } 102 | 103 | void XRInterfaceReference::set_k1(const double p_k1) { 104 | k1 = p_k1; 105 | } 106 | 107 | double XRInterfaceReference::get_k2() const { 108 | return k2; 109 | } 110 | 111 | void XRInterfaceReference::set_k2(const double p_k2) { 112 | k2 = p_k2; 113 | } 114 | 115 | bool XRInterfaceReference::get_use_mouse_for_headtracking() const { 116 | return use_mouse_for_headtracking; 117 | } 118 | 119 | void XRInterfaceReference::set_use_mouse_for_headtracking(bool p_use_mouse_for_headtracking) { 120 | use_mouse_for_headtracking = p_use_mouse_for_headtracking; 121 | } 122 | 123 | bool XRInterfaceReference::get_use_wasd_for_movement() const { 124 | return use_wasd_for_movement; 125 | } 126 | 127 | void XRInterfaceReference::set_use_wasd_for_movement(bool p_use_wasd_for_movement) { 128 | use_wasd_for_movement = p_use_wasd_for_movement; 129 | } 130 | 131 | 132 | StringName XRInterfaceReference::_get_name() const { 133 | // this currently fails to return because we loose our data before it ends up in the callers hands... 134 | StringName name("XR Reference"); 135 | return name; 136 | } 137 | 138 | uint32_t XRInterfaceReference::_get_capabilities() const { 139 | return XR_STEREO; 140 | } 141 | 142 | bool XRInterfaceReference::_is_initialized() const { 143 | return initialised; 144 | } 145 | 146 | bool XRInterfaceReference::_initialize() { 147 | if (!initialised) { 148 | // do any initialisation here.. 149 | xr_server = XRServer::get_singleton(); 150 | if (xr_server == nullptr) { 151 | ERR_FAIL_V_MSG(false, "Couldn't obtain XRServer singleton"); 152 | } 153 | 154 | // we must create a tracker for our head 155 | head.instantiate(); 156 | head->set_tracker_type(XRServer::TRACKER_HEAD); 157 | head->set_tracker_name("head"); 158 | head->set_tracker_desc("Players head"); 159 | xr_server->add_tracker(head); 160 | 161 | // set this as our primary interface 162 | xr_server->set_primary_interface(this); 163 | 164 | initialised = true; 165 | } 166 | return initialised; 167 | } 168 | 169 | void XRInterfaceReference::_uninitialize() { 170 | if (initialised) { 171 | // do any cleanup here... 172 | if (head.is_valid()) { 173 | xr_server->remove_tracker(head); 174 | 175 | head.unref(); 176 | } 177 | 178 | initialised = false; 179 | xr_server = nullptr; 180 | } 181 | } 182 | 183 | XRInterface::TrackingStatus XRInterfaceReference::_get_tracking_status() const { 184 | return XRInterface::XR_UNKNOWN_TRACKING; 185 | } 186 | 187 | Vector2 XRInterfaceReference::_get_render_target_size() { 188 | // TODO get access to display server singleton 189 | 190 | // we use half our window size 191 | DisplayServer *display_server = DisplayServer::get_singleton(); 192 | if (display_server == nullptr) { 193 | return Vector2(); 194 | } 195 | Vector2 target_size = display_server->window_get_size(); 196 | 197 | target_size.x = target_size.x * 0.5 * oversample; 198 | target_size.y = target_size.y * oversample; 199 | 200 | return target_size; 201 | } 202 | 203 | uint32_t XRInterfaceReference::_get_view_count() { 204 | return 2; // stereo 205 | } 206 | 207 | Transform3D XRInterfaceReference::_get_camera_transform() { 208 | if (!initialised) { 209 | return Transform3D(); 210 | } 211 | 212 | Transform3D adj_head_transform = head_transform; 213 | double world_scale = xr_server->get_world_scale(); 214 | 215 | adj_head_transform.origin *= world_scale; 216 | 217 | return xr_server->get_reference_frame() * adj_head_transform; 218 | } 219 | 220 | Transform3D XRInterfaceReference::_get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) { 221 | if (!initialised) { 222 | return Transform3D(); 223 | } 224 | 225 | Transform3D eye_transform; 226 | double world_scale = xr_server->get_world_scale(); 227 | 228 | if (p_view == 0) { 229 | eye_transform.origin.x = -(intraocular_dist * 0.01 * 0.5 * world_scale); 230 | } else if (p_view == 1) { 231 | eye_transform.origin.x = intraocular_dist * 0.01 * 0.5 * world_scale; 232 | } 233 | 234 | Transform3D adj_head_transform = head_transform; 235 | adj_head_transform.origin *= world_scale; 236 | 237 | return p_cam_transform * xr_server->get_reference_frame() * adj_head_transform * eye_transform; 238 | } 239 | 240 | PackedFloat64Array XRInterfaceReference::_get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) { 241 | PackedFloat64Array arr; 242 | arr.resize(16); // 4x4 matrix 243 | 244 | aspect = p_aspect; 245 | 246 | // We don't have access to CameraMatrix here so we'll need to duplicate some code here.. 247 | /* 248 | eye.set_for_hmd(p_view + 1, p_aspect, intraocular_dist, display_width, display_to_lens, oversample, p_z_near, p_z_far); 249 | */ 250 | 251 | // we first calculate our base frustum on our values without taking our lens magnification into account. 252 | double f1 = (intraocular_dist * 0.5) / display_to_lens; 253 | double f2 = ((display_width - intraocular_dist) * 0.5) / display_to_lens; 254 | double f3 = (display_width / 4.0) / display_to_lens; 255 | 256 | // now we apply our oversample factor to increase our FOV. how much we oversample is always a balance we strike between performance and how much 257 | // we're willing to sacrifice in FOV. 258 | double add = ((f1 + f2) * (oversample - 1.0)) / 2.0; 259 | f1 += add; 260 | f2 += add; 261 | f3 *= oversample; 262 | 263 | // always apply KEEP_WIDTH aspect ratio 264 | f3 /= p_aspect; 265 | 266 | double left,right,top,bottom; 267 | if (p_view == 0) { // left eye 268 | left = -f2 * p_z_near; 269 | right = f1 * p_z_near; 270 | bottom = -f3 * p_z_near; 271 | top = f3 * p_z_near; 272 | } else { 273 | left = -f1 * p_z_near; 274 | right = f2 * p_z_near; 275 | bottom = -f3 * p_z_near; 276 | top = f3 * p_z_near; 277 | } 278 | 279 | double x = 2 * p_z_near / (right - left); 280 | double y = 2 * p_z_near / (top - bottom); 281 | 282 | double a = (right + left) / (right - left); 283 | double b = (top + bottom) / (top - bottom); 284 | double c = -(p_z_far + p_z_near) / (p_z_far - p_z_near); 285 | double d = -2 * p_z_far * p_z_near / (p_z_far - p_z_near); 286 | 287 | arr.set(0, x); 288 | arr.set(1, 0); 289 | arr.set(2, 0); 290 | arr.set(3, 0); 291 | arr.set(4, 0); 292 | arr.set(5, y); 293 | arr.set(6, 0); 294 | arr.set(7, 0); 295 | arr.set(8, a); 296 | arr.set(9, b); 297 | arr.set(10, c); 298 | arr.set(11, -1); 299 | arr.set(12, 0); 300 | arr.set(13, 0); 301 | arr.set(14, d); 302 | arr.set(15, 0.0); 303 | 304 | return arr; 305 | } 306 | 307 | void XRInterfaceReference::_post_draw_viewport(const RID &p_render_target, const Rect2 &p_screen_rect) { 308 | Rect2 src_rect(0.0f, 0.0f, 1.0f, 1.0f); 309 | Rect2 dst_rect = p_screen_rect; 310 | 311 | // halve our width 312 | Vector2 size = dst_rect.get_size(); 313 | size.x = size.x * 0.5; 314 | dst_rect.size = size; 315 | 316 | Vector2 eye_center(((-intraocular_dist / 2.0) + (display_width / 4.0)) / (display_width / 2.0), 0.0); 317 | 318 | add_blit(p_render_target, src_rect, dst_rect, true, 0, true, eye_center, k1, k2, oversample, aspect); 319 | 320 | // move rect 321 | Vector2 pos = dst_rect.get_position(); 322 | pos.x = size.x; 323 | dst_rect.position = pos; 324 | 325 | eye_center.x = ((intraocular_dist / 2.0) - (display_width / 4.0)) / (display_width / 2.0); 326 | add_blit(p_render_target, src_rect, dst_rect, true, 1, true, eye_center, k1, k2, oversample, aspect); 327 | } 328 | 329 | void XRInterfaceReference::_process() { 330 | // Emulate a headsets movement through space, we thus update the position and orientation relative to the origin point 331 | 332 | // update our head transform in world space 333 | if (use_mouse_for_headtracking) { 334 | Vector2 mouse_speed = Input::get_singleton()->get_last_mouse_velocity(); 335 | 336 | // we're missing a delta here so frame rate sensative 337 | double fps = 90.0; 338 | angle_x -= mouse_speed.x / fps; 339 | angle_y -= mouse_speed.y / fps; 340 | if (angle_y < -90.0) { 341 | angle_y = -90.0; 342 | } else if (angle_y >= 90.0) { 343 | angle_y = 90.0; 344 | } 345 | 346 | Basis basis; 347 | basis.rotate(Vector3(1.0, 0.0, 0.0), 3.14159265359 * angle_y/ 180); 348 | basis.rotate(Vector3(0.0, 1.0, 0.0), 3.14159265359 * angle_x/ 180); 349 | 350 | head_transform.basis = basis; 351 | } 352 | 353 | // move our head through space 354 | if (use_wasd_for_movement) { 355 | // get our movement vectors 356 | Vector3 forward = -head_transform.basis.get_column(2); // might need to be get_column 357 | Vector3 sideways = head_transform.basis.get_column(0); 358 | 359 | forward.y = 0.0; 360 | forward.normalize(); 361 | 362 | sideways.y = 0.0; 363 | sideways.normalize(); 364 | 365 | // we're missing a delta here so frame rate sensative 366 | double fps = 90.0; 367 | double speed = 5.0; 368 | 369 | Input * input = Input::get_singleton(); 370 | if (input->is_key_pressed(KEY_W)) { 371 | head_transform.origin += speed * forward / fps; 372 | } else if (input->is_key_pressed(KEY_S)) { 373 | head_transform.origin -= speed * forward / fps; 374 | } 375 | if (input->is_key_pressed(KEY_D)) { 376 | head_transform.origin += speed * sideways / fps; 377 | } else if (input->is_key_pressed(KEY_A)) { 378 | head_transform.origin -= speed * sideways / fps; 379 | } 380 | } 381 | 382 | // set height 383 | head_transform.origin.y = eye_height; 384 | 385 | if (head.is_valid()) { 386 | // Set our head position, note in real space, reference frame and world scale is applied later 387 | head->set_pose("default", head_transform, Vector3(), Vector3(), XRPose::XR_TRACKING_CONFIDENCE_HIGH); 388 | } 389 | } 390 | 391 | bool XRInterfaceReference::_get_anchor_detection_is_enabled() const { 392 | return false; 393 | } 394 | 395 | void XRInterfaceReference::_set_anchor_detection_is_enabled(bool enabled) { 396 | 397 | } 398 | 399 | int32_t XRInterfaceReference::_get_camera_feed_id() const { 400 | return 0; 401 | } 402 | 403 | XRInterfaceReference::XRInterfaceReference() { 404 | 405 | } 406 | 407 | XRInterfaceReference::~XRInterfaceReference() { 408 | 409 | } 410 | -------------------------------------------------------------------------------- /src/xr_interface_reference.h: -------------------------------------------------------------------------------- 1 | #ifndef XR_INTERFACE_REFERENCE_H 2 | #define XR_INTERFACE_REFERENCE_H 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | namespace godot { 11 | 12 | class XRInterfaceReference : public XRInterfaceExtension { 13 | GDCLASS(XRInterfaceReference, XRInterfaceExtension); 14 | 15 | protected: 16 | static void _bind_methods(); 17 | 18 | private: 19 | bool initialised = false; 20 | XRServer *xr_server = nullptr; 21 | 22 | double eye_height = 1.85; 23 | 24 | double intraocular_dist = 6.0; 25 | double display_width = 14.5; 26 | double display_to_lens = 4.0; 27 | double oversample = 1.5; 28 | 29 | double k1 = 0.215; 30 | double k2 = 0.215; 31 | double aspect = 1.0; 32 | 33 | Ref head; 34 | Transform3D head_transform; 35 | bool use_mouse_for_headtracking = false; 36 | bool use_wasd_for_movement = false; 37 | double angle_x = 0.0; 38 | double angle_y = 0.0; 39 | 40 | public: 41 | // Constants. 42 | 43 | // Property setters and getters 44 | double get_eye_height() const; 45 | void set_eye_height(const double p_eye_height); 46 | 47 | double get_intraocular_dist() const; 48 | void set_intraocular_dist(const double p_intraocular_dist); 49 | 50 | double get_display_width() const; 51 | void set_display_width(const double p_display_width); 52 | 53 | double get_display_to_lens() const; 54 | void set_display_to_lens(const double p_display_to_lens); 55 | 56 | double get_oversample() const; 57 | void set_oversample(const double p_oversample); 58 | 59 | double get_k1() const; 60 | void set_k1(const double p_k1); 61 | 62 | double get_k2() const; 63 | void set_k2(const double p_k2); 64 | 65 | bool get_use_mouse_for_headtracking() const; 66 | void set_use_mouse_for_headtracking(bool p_use_mouse_for_headtracking); 67 | 68 | bool get_use_wasd_for_movement() const; 69 | void set_use_wasd_for_movement(bool p_use_wasd_for_movement); 70 | 71 | // Functions. 72 | virtual StringName _get_name() const override; 73 | virtual uint32_t _get_capabilities() const override; 74 | 75 | virtual bool _is_initialized() const override; 76 | virtual bool _initialize() override; 77 | virtual void _uninitialize() override; 78 | 79 | virtual XRInterface::TrackingStatus _get_tracking_status() const override; 80 | 81 | virtual Vector2 _get_render_target_size() override; 82 | virtual uint32_t _get_view_count() override; 83 | virtual Transform3D _get_camera_transform() override; 84 | virtual Transform3D _get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override; 85 | virtual PackedFloat64Array _get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) override; 86 | 87 | virtual void _post_draw_viewport(const RID &p_render_target, const Rect2 &p_screen_rect) override; 88 | 89 | virtual void _process() override; 90 | 91 | virtual bool _get_anchor_detection_is_enabled() const override; 92 | virtual void _set_anchor_detection_is_enabled(bool enabled) override; 93 | virtual int32_t _get_camera_feed_id() const override; 94 | 95 | XRInterfaceReference(); 96 | ~XRInterfaceReference(); 97 | }; 98 | } // namespace godot 99 | 100 | #endif // ! XR_INTERFACE_REFERENCE_H 101 | --------------------------------------------------------------------------------