├── .github └── workflows │ └── build.yml ├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── jitpack.yml ├── settings.gradle └── src ├── main ├── java │ └── module-info.java └── kotlin │ └── openvr │ ├── EventListener.kt │ ├── assets │ └── steamVR │ │ ├── input │ │ ├── ActionType.kt │ │ ├── EventClass.kt │ │ ├── ISteamVR_Action_Out.kt │ │ ├── SteamVR_Action.kt │ │ ├── SteamVR_ActionDirections.kt │ │ ├── SteamVR_ActionSet.kt │ │ ├── SteamVR_ActionSet_Manager.kt │ │ ├── SteamVR_Action_Boolean.kt │ │ ├── SteamVR_Action_In.kt │ │ ├── SteamVR_Action_Pose.kt │ │ ├── SteamVR_Action_Single.kt │ │ ├── SteamVR_Action_Skeleton.kt │ │ ├── SteamVR_Action_Vector2.kt │ │ ├── SteamVR_Action_Vector3.kt │ │ ├── SteamVR_Action_Vibration.kt │ │ ├── SteamVR_Behaviour_Pose.kt │ │ ├── SteamVR_Behaviour_Skeleton.kt │ │ ├── SteamVR_Input.kt │ │ ├── SteamVR_Input_ActionFile.kt │ │ ├── SteamVR_Skeleton_Pose.kt │ │ ├── SteamVR_Skeleton_Poser.kt │ │ └── SteamVR_UpdateModes.kt │ │ ├── interactionSystem │ │ └── core │ │ │ └── scripts │ │ │ ├── Interactable.kt │ │ │ └── RenderModel.kt │ │ └── script │ │ ├── SteamVR.kt │ │ ├── SteamVR_Behaviour.kt │ │ ├── SteamVR_Events.kt │ │ ├── SteamVR_HistoryBuffer.kt │ │ ├── SteamVR_Settings.kt │ │ ├── SteamVR_Utils.kt │ │ └── UnityEvent.kt │ ├── controllersListener.kt │ ├── lib │ ├── VrInterface.kt │ ├── calloc.kt │ ├── direct fields.kt │ ├── enums.kt │ ├── ivrApplications.kt │ ├── ivrChaperone.kt │ ├── ivrChaperoneSetup.kt │ ├── ivrCompositor.kt │ ├── ivrDebug.kt │ ├── ivrDriverManager.kt │ ├── ivrExtendedDisplay.kt │ ├── ivrIOBuffer.kt │ ├── ivrInput.kt │ ├── ivrNotifications.kt │ ├── ivrOverlay.kt │ ├── ivrRenderModels.kt │ ├── ivrResources.kt │ ├── ivrScreenshots.kt │ ├── ivrSettings.kt │ ├── ivrSpatialAnchors.kt │ ├── ivrSystem.kt │ ├── ivrTrackedCamera.kt │ ├── openvr.kt │ ├── temp.kt │ └── typealias.kt │ ├── plugin │ ├── Controller.kt │ ├── Render.kt │ └── Utils.kt │ ├── plugin2 │ ├── Camera.kt │ ├── Component.kt │ ├── Events.kt │ ├── GameObject.kt │ ├── Hand.kt │ ├── Material.kt │ ├── Mesh.kt │ ├── RectTransform.kt │ ├── RigidTransform2.kt │ ├── Shader.kt │ ├── SteamVR_Input_Source.kt │ ├── SteamVR_Input_Sources.kt │ ├── SteamVR_RenderModel.kt │ ├── Texture.kt │ ├── TrackedObject.kt │ ├── Transform.kt │ └── Utils.kt │ ├── steamVR_Input │ ├── SteamVR_Actions.kt │ └── actionSetClasses │ │ ├── SteamVR_Input_ActionSet_buggy.kt │ │ ├── SteamVR_Input_ActionSet_default.kt │ │ ├── SteamVR_Input_ActionSet_mixedreality.kt │ │ └── SteamVR_Input_ActionSet_platformer.kt │ └── unity │ └── tmp.kt └── test ├── java ├── main │ └── Test.java ├── module-info.test └── openvr │ └── Test.java ├── kotlin └── openvr │ ├── helloVr_OpenGL │ ├── Application.kt │ ├── CompanionWindow.kt │ ├── Program.kt │ ├── Scene.kt │ ├── components model.kt │ └── helpers.kt │ ├── models.kt │ └── test.kt └── resources ├── actions.json ├── cube_texture.png ├── hellovr_actions.json └── hellovr_bindings_vive_controller.json /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: [push] 4 | 5 | jobs: 6 | linux: 7 | name: 'Linux' 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Set up JDK 1.11 13 | uses: actions/setup-java@v1 14 | with: 15 | java-version: 11 16 | - name: Grant execute permission for gradlew 17 | run: chmod +x gradlew 18 | - name: Build with Gradle 19 | run: ./gradlew build 20 | 21 | windows: 22 | name: 'Windows' 23 | runs-on: windows-latest 24 | 25 | steps: 26 | - uses: actions/checkout@v2 27 | - name: Set up JDK 1.11 28 | uses: actions/setup-java@v1 29 | with: 30 | java-version: 11 31 | - name: Build with Gradle 32 | run: .\gradlew.bat build 33 | 34 | mac: 35 | name: 'Mac OS' 36 | runs-on: macos-latest 37 | 38 | steps: 39 | - uses: actions/checkout@v2 40 | - name: Set up JDK 1.11 41 | uses: actions/setup-java@v1 42 | with: 43 | java-version: 11 44 | - name: Grant execute permission for gradlew 45 | run: chmod +x gradlew 46 | - name: Build with Gradle 47 | run: ./gradlew build 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .nb-gradle/ 2 | .gradle/ 3 | .idea/ 4 | build/ 5 | classes/ 6 | out/ 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - openjdk11 -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Valve Corporation 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its contributors 15 | may be used to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JVM OpenVR Wrapper (synchronized with 1.2.10) 2 | 3 | [![Build Status](https://github.com/kotlin-graphics/openvr/workflows/build/badge.svg)](https://github.com/kotlin-graphics/openvr/actions?workflow=build) 4 | [![license](https://img.shields.io/badge/License-BSD-3-Clause-orange.svg)](https://github.com/kotlin-graphics/openvr/blob/master/LICENSE) 5 | [![Release](https://jitpack.io/v/kotlin-graphics/openvr.svg)](https://jitpack.io/#kotlin-graphics/openvr) 6 | ![Size](https://github-size-badge.herokuapp.com/kotlin-graphics/openvr.svg) 7 | [![Github All Releases](https://img.shields.io/github/downloads/kotlin-graphics/openvr/total.svg)]() 8 | 9 | OpenVR SDK 10 | --- 11 | 12 | This is a kotlin wrapper of [OpenVR](https://github.com/ValveSoftware/openvr), which is an API and runtime that allows access to VR hardware from multiple 13 | vendors without requiring that applications have specific knowledge of the 14 | hardware they are targeting. This repository is an SDK that contains the API 15 | and samples. The runtime is under SteamVR in Tools on Steam. 16 | 17 | ### Documentation 18 | 19 | Documentation for the API is available on the [Github Wiki](https://github.com/ValveSoftware/openvr/wiki/API-Documentation) 20 | 21 | More information on OpenVR and SteamVR can be found on http://steamvr.com 22 | 23 | ## Binding Features: 24 | 25 | - original comments preserved and properly formatted 26 | - direct fields, e.g: `var TrackedDevicePose.poseIsValid: Boolean` 27 | - enumerators for type safety, e.g: `var Texture.type: TextureType` 28 | - every struct method is offered also much more user friendly offering also full interoperability with glm, `getProjectionMatrix` returns, for example, directly a glm `Mat4` 29 | ```kotlin 30 | vrSystem.getProjectionMatrix(eye: VREye, nearZ: Float, farZ: Float, res: Mat4 = Mat4()): Mat4 31 | ``` 32 | that expects an `EVREye` enumerator type for `eEye` and returns directly a [glm](https://github.com/kotlin-graphics/glm) `Mat4`. 33 | instead of: 34 | ```kotlin: 35 | VRSystem.VRSystem_GetProjectionMatrix(eEye: Int, fNearZ: Float, fNear: Float, __result: HmdMatrix44): HmdMatrix44 36 | ``` 37 | Or for example: 38 | ```kotlin 39 | GetStringTrackedDeviceProperty.invoke(..): Int 40 | ``` 41 | where you are supposed to call it one first time to get the right size for the buffer to accommodate the char array for the string and then a second time to actually retrieve the string. 42 | You have the possibility to call directly 43 | ```kotlin 44 | getStringTrackedDeviceProperty(..): String 45 | ``` 46 | that returns directly the resulting string, bringing down a lot of boilerplate code 47 | 48 | - array classes `[]` operator, included `RenderModel_Vertex` 49 | - concise enumerators, e.g. `EVRComponentProperty.VRComponentProperty_IsStatic` is `VRComponentProperty.IsStatic` 50 | ~- `SteamVRListener` for event listener. Instantiate a class extending it, call `.poll()` on it at the begin of each frame and override the corresponding methods you are looking for, such as `buttonPress(left: Boolean, button: EVRButtonId)`~ TO CHECK 51 | 52 | ### Sample: 53 | 54 | You can find the OpenGL sample [here](src/test/kotlin/openvr/helloVr_OpenGL) 55 | 56 | ### Contributions: 57 | 58 | Don't hesitate to contribute to the project by submitting [issues](https://github.com/kotlin-graphics/openvr/issues) or [pull requests](https://github.com/kotlin-graphics/openvr/pulls) for bugs and features. Any feedback is welcome at [elect86@gmail.com](mailto://elect86@gmail.com). 59 | 60 | 61 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | import static org.gradle.internal.os.OperatingSystem.* 2 | 3 | plugins { 4 | id 'java' 5 | id 'java-library' 6 | id "org.jetbrains.kotlin.jvm" version "1.3.70" 7 | id "com.github.johnrengelman.shadow" version '5.2.0' 8 | } 9 | 10 | // jitpack 11 | group = 'com.github.kotlin_graphics' 12 | 13 | ext{ 14 | moduleName = "${group}.openvr" 15 | kotlin = 'org.jetbrains.kotlin:kotlin' 16 | kotlin_version = '1.3.70' 17 | kotlintest_version = '3.4.2' 18 | uno_version = '402f5f495429b7f2178a1d200c32bb5ed2f7e6fa' 19 | vkk_version = 'bc3da1dd67594af6f2d3e49be74b6fc260025c12' 20 | gln_version = '8dea51af29a2a1d6e51ed3b6d7b06ba2ca2f99b9' 21 | gli_version = 'ac3da8180268b5d121ea83866de5bfe0e8998045' 22 | glm_version = '1b4ac18dd1a3c23440d3f33596688aac60bc0141' 23 | unsigned_version = '18131d0fe0b7465a145a4502d31452c5ae0e59a1' 24 | kool_version = 'fcf04b2c03b8949d9d9a8b0a580082e927903510' 25 | lwjgl_version = "3.2.3" 26 | lwjgl_natives = current() == WINDOWS ? "windows" : current() == LINUX ? "linux" : "macos" 27 | } 28 | 29 | dependencies { 30 | 31 | implementation "$kotlin-stdlib-jdk8" 32 | 33 | testImplementation "io.kotlintest:kotlintest-runner-junit5:$kotlintest_version" 34 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3' 35 | 36 | def kx = "com.github.kotlin-graphics" 37 | implementation "$kx:gln:$gln_version" // wtf testImplementation doesnt seem to work 38 | implementation "$kx:gli:$gli_version" 39 | implementation "${kx}.glm:glm:$glm_version" 40 | implementation "$kx:kool:$kool_version" 41 | // implementation "$kx:kotlin-unsigned:$unsigned_version" 42 | 43 | // implementation 'com.github.kotlin-graphics.uno-sdk:uno-core:683e68fbb3' 44 | ["core", "gl"].each { 45 | implementation "${kx}.uno-sdk:uno-$it:$uno_version" 46 | } 47 | 48 | ["", "-glfw", "-jemalloc", "-openal", "-opengl", "-stb", "-openvr", "-vulkan"].each { 49 | implementation "org.lwjgl:lwjgl$it:$lwjgl_version" 50 | if (it != "-vulkan") 51 | runtimeOnly "org.lwjgl:lwjgl$it:$lwjgl_version:natives-$lwjgl_natives" 52 | } 53 | 54 | implementation 'com.beust:klaxon:5.2' 55 | } 56 | 57 | repositories { 58 | mavenCentral() 59 | jcenter() 60 | maven { setUrl("https://oss.sonatype.org/content/repositories/snapshots/") } 61 | maven { setUrl('https://jitpack.io') } 62 | } 63 | 64 | task sourcesJar(type: Jar, dependsOn: classes) { 65 | archiveClassifier = 'sources' 66 | from sourceSets.main.allSource 67 | } 68 | 69 | task javadocJar(type: Jar, dependsOn: javadoc) { 70 | archiveClassifier = 'javadoc' 71 | from javadoc.destinationDir 72 | } 73 | 74 | artifacts { 75 | archives sourcesJar 76 | // archives javadocJar 77 | } 78 | 79 | test.useJUnitPlatform() 80 | 81 | // https://stackoverflow.com/a/47458666/1047713 82 | //test { 83 | // testLogging { 84 | // events "passed", "skipped", "failed" 85 | // exceptionFormat "full" 86 | // outputs.upToDateWhen { false } 87 | // showStandardStreams = true 88 | // } 89 | //} 90 | 91 | shadowJar.archiveClassifier = 'all' 92 | 93 | compileKotlin { 94 | // Enable Kotlin compilation to Java 8 class files with method parameter name metadata 95 | kotlinOptions { 96 | jvmTarget = "11" 97 | // javaParameters = true 98 | } 99 | // As per https://stackoverflow.com/a/47669720 100 | // See also https://discuss.kotlinlang.org/t/kotlin-support-for-java-9-module-system/2499/9 101 | destinationDir = compileJava.destinationDir 102 | } 103 | 104 | compileTestKotlin { 105 | kotlinOptions { 106 | jvmTarget = "11" 107 | // javaParameters = true 108 | } 109 | } 110 | 111 | compileJava { 112 | dependsOn(':compileKotlin') 113 | inputs.property("moduleName", moduleName) 114 | doFirst { 115 | options.compilerArgs = [ 116 | '--module-path', classpath.asPath, 117 | '--patch-module', "$moduleName=${sourceSets["main"].output.asPath}"] 118 | classpath = files() 119 | } 120 | } 121 | 122 | jar { duplicatesStrategy = DuplicatesStrategy.EXCLUDE } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kotlin-graphics/openvr/d3a155e2e3cfe529286e63b33672f10e597246f6/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Sep 12 18:01:50 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.2.2-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save ( ) { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /jitpack.yml: -------------------------------------------------------------------------------- 1 | jdk: 2 | - openjdk11 -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.buildFileName = 'build.gradle' -------------------------------------------------------------------------------- /src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module com.github.kotlin_graphics.openvr { 2 | 3 | requires kotlin.stdlib; 4 | 5 | requires org.lwjgl.openvr; 6 | 7 | requires com.github.kotlin_graphics.kool; 8 | requires com.github.kotlin_graphics.glm; 9 | requires com.github.kotlin_graphics.gli; 10 | // requires com.github.kotlin_graphics.gln; 11 | requires com.github.kotlin_graphics.uno_core; 12 | requires org.lwjgl.vulkan; 13 | requires java.desktop; 14 | requires klaxon; 15 | requires kotlinx.coroutines.core; 16 | requires com.github.kotlin_graphics.gln; 17 | 18 | exports openvr; 19 | exports openvr.lib; 20 | exports openvr.plugin2; 21 | exports openvr.assets.steamVR.input; 22 | exports openvr.assets.steamVR.interactionSystem.core.scripts; 23 | exports openvr.steamVR_Input; 24 | exports openvr.steamVR_Input.actionSetClasses; 25 | exports openvr.unity; 26 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/assets/steamVR/input/ActionType.kt: -------------------------------------------------------------------------------- 1 | package openvr.assets.steamVR.input 2 | 3 | enum class ActionType { 4 | Boolean, Single, Vector2, Vector3, Pose, Skeleton, Vibration; 5 | 6 | fun create(): Action = when (this) { 7 | Boolean -> SteamVR_Action_Boolean() 8 | Single -> SteamVR_Action_Single() 9 | Vector2 -> SteamVR_Action_Vector2() 10 | Vector3 -> SteamVR_Action_Vector3() 11 | Pose -> SteamVR_Action_Pose() 12 | Skeleton -> SteamVR_Action_Skeleton() 13 | Vibration -> SteamVR_Action_Vibration() 14 | } as Action 15 | 16 | fun newSourceMap(): SourceMap = when (this) { 17 | Boolean -> SteamVR_Action_Boolean_Source_Map() 18 | Single -> SteamVR_Action_Single_Source_Map() 19 | Vector2 -> SteamVR_Action_Vector2_Source_Map() 20 | Vector3 -> SteamVR_Action_Vector3_Source_Map() 21 | Pose -> SteamVR_Action_Pose_Source_Map() 22 | Skeleton -> SteamVR_Action_Skeleton_Source_Map() 23 | Vibration -> SteamVR_Action_Vibration_Source_Map() 24 | } as SourceMap 25 | 26 | fun newSourceElement(): SourceElement = when (this) { 27 | Boolean -> SteamVR_Action_Boolean_Source() 28 | Single -> SteamVR_Action_Single_Source() 29 | Vector2 -> SteamVR_Action_Vector2_Source() 30 | Vector3 -> SteamVR_Action_Vector3_Source() 31 | Pose -> SteamVR_Action_Pose_Source() 32 | Skeleton -> SteamVR_Action_Skeleton_Source() 33 | Vibration -> SteamVR_Action_Vibration_Source() 34 | } as SourceElement 35 | } 36 | 37 | fun Action.newCreateType(): Action = when (this) { 38 | is SteamVR_Action_Boolean -> SteamVR_Action_Boolean() 39 | is SteamVR_Action_Single -> SteamVR_Action_Single() 40 | is SteamVR_Action_Vector2 -> SteamVR_Action_Vector2() 41 | is SteamVR_Action_Vector3 -> SteamVR_Action_Vector3() 42 | is SteamVR_Action_Pose -> SteamVR_Action_Pose() 43 | is SteamVR_Action_Skeleton -> SteamVR_Action_Skeleton() 44 | is SteamVR_Action_Vibration -> SteamVR_Action_Vibration() 45 | else -> error("Invalid") 46 | } as Action 47 | 48 | fun Action.newSourceMap(): SourceMap 49 | where Action : SteamVR_Action, SourceMap : SteamVR_Action_Source_Map = when (this) { 50 | is SteamVR_Action_Boolean -> SteamVR_Action_Boolean_Source_Map() 51 | is SteamVR_Action_Single -> SteamVR_Action_Single_Source_Map() 52 | is SteamVR_Action_Vector2 -> SteamVR_Action_Vector2_Source_Map() 53 | is SteamVR_Action_Vector3 -> SteamVR_Action_Vector3_Source_Map() 54 | is SteamVR_Action_Pose -> SteamVR_Action_Pose_Source_Map() 55 | is SteamVR_Action_Skeleton -> SteamVR_Action_Skeleton_Source_Map() 56 | is SteamVR_Action_Vibration -> SteamVR_Action_Vibration_Source_Map() 57 | else -> error("Invalid") 58 | } as SourceMap 59 | 60 | fun SteamVR_Action_Source_Map.newSourceElement(): Source 61 | where Source : SteamVR_Action_Source = when (this) { 62 | is SteamVR_Action_Boolean_Source_Map -> SteamVR_Action_Boolean_Source() 63 | is SteamVR_Action_Single_Source_Map -> SteamVR_Action_Single_Source() 64 | is SteamVR_Action_Vector2_Source_Map -> SteamVR_Action_Vector2_Source() 65 | is SteamVR_Action_Vector3_Source_Map -> SteamVR_Action_Vector3_Source() 66 | is SteamVR_Action_Pose_Source_Map<*> -> SteamVR_Action_Pose_Source() 67 | is SteamVR_Action_Skeleton_Source_Map -> SteamVR_Action_Skeleton_Source() 68 | is SteamVR_Action_Vibration_Source_Map -> SteamVR_Action_Vibration_Source() 69 | else -> error("Invalid") 70 | } as Source -------------------------------------------------------------------------------- /src/main/kotlin/openvr/assets/steamVR/input/EventClass.kt: -------------------------------------------------------------------------------- 1 | package openvr.assets.steamVR.input 2 | 3 | class EventClass(val list: ArrayList) { 4 | 5 | operator fun plusAssign(event: T) { 6 | list += event 7 | } 8 | 9 | operator fun minusAssign(event: T) { 10 | list -= event 11 | } 12 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/assets/steamVR/input/ISteamVR_Action_Out.kt: -------------------------------------------------------------------------------- 1 | package openvr.assets.steamVR.input 2 | 3 | //======= Copyright (c) Valve Corporation, All rights reserved. =============== 4 | 5 | abstract class SteamVR_Action_OutT : SteamVR_ActionT(), ISteamVR_Action_Out 6 | where SourceMap : SteamVR_Action_Source_MapT, SourceElement : SteamVR_Action_Out_Source 7 | 8 | abstract class SteamVR_Action_Out_Source : SteamVR_Action_Source(), ISteamVR_Action_Out_Source 9 | 10 | interface ISteamVR_Action_Out : ISteamVR_Action, ISteamVR_Action_Out_Source 11 | 12 | interface ISteamVR_Action_Out_Source : ISteamVR_Action_Source -------------------------------------------------------------------------------- /src/main/kotlin/openvr/assets/steamVR/input/SteamVR_ActionDirections.kt: -------------------------------------------------------------------------------- 1 | package openvr.assets.steamVR.input 2 | 3 | enum class SteamVR_ActionDirections { In, Out } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/assets/steamVR/input/SteamVR_ActionSet_Manager.kt: -------------------------------------------------------------------------------- 1 | package openvr.assets.steamVR.input 2 | 3 | import kool.set 4 | import openvr.lib.* 5 | import openvr.plugin2.SteamVR_Input_Source 6 | import openvr.plugin2.SteamVR_Input_Sources 7 | import openvr.unity.Application 8 | import openvr.unity.Time 9 | import org.lwjgl.openvr.VRActiveActionSet 10 | import uno.kotlin.plusAssign 11 | 12 | //======= Copyright (c) Valve Corporation, All rights reserved. =============== 13 | 14 | /** Action sets are logical groupings of actions. Multiple sets can be active at one time. */ 15 | object SteamVR_ActionSet_Manager { 16 | 17 | lateinit var rawActiveActionSetArray: VRActiveActionSet.Buffer 18 | 19 | private var changed = false 20 | 21 | fun initialize() {} 22 | 23 | /** Disable all known action sets. */ 24 | fun disableAllActionSets() { 25 | for (actionSet in SteamVR_Input.actionSets) { 26 | actionSet.deactivate(SteamVR_Input_Sources.Any) 27 | actionSet.deactivate(SteamVR_Input_Sources.LeftHand) 28 | actionSet.deactivate(SteamVR_Input_Sources.RightHand) 29 | } 30 | } 31 | 32 | private var lastFrameUpdated = 0 33 | 34 | fun updateActionStates(force: Boolean = false) { 35 | 36 | if (force || Time.frameCount != lastFrameUpdated) { 37 | 38 | lastFrameUpdated = Time.frameCount 39 | 40 | if (changed) 41 | updateActionSetsArray() 42 | 43 | if (::rawActiveActionSetArray.isInitialized && rawActiveActionSetArray.hasRemaining()) { 44 | val err = vrInput updateActionState rawActiveActionSetArray 45 | if (err != vrInput.Error.None) 46 | System.err.println("[SteamVR] UpdateActionState error: ${vrInput.error}") 47 | //else Debug.Log("Action sets activated: " + activeActionSets.Length); 48 | } else { 49 | //Debug.LogWarning("No sets active"); 50 | } 51 | } 52 | } 53 | 54 | fun setChanged() { 55 | changed = true 56 | } 57 | 58 | private fun updateActionSetsArray() { 59 | 60 | val activeActionSetsList = ArrayList() 61 | 62 | val sources = SteamVR_Input_Source.allSources 63 | 64 | for (set in SteamVR_Input.actionSets) { 65 | 66 | for (source in sources) { 67 | 68 | if (set readRawSetActive source) { 69 | 70 | val activeSet = VRActiveActionSet.calloc().apply { 71 | actionSet = set.handle 72 | priority = set readRawSetPriority source 73 | restrictedToDevice = SteamVR_Input_Source getHandle source 74 | } 75 | val insertionIndex = activeActionSetsList.indexOfFirst { it.priority > activeSet.priority } 76 | activeActionSetsList[insertionIndex] = activeSet 77 | } 78 | } 79 | } 80 | 81 | changed = false 82 | 83 | if (::rawActiveActionSetArray.isInitialized) 84 | rawActiveActionSetArray.free() 85 | 86 | rawActiveActionSetArray = VRActiveActionSet.calloc(activeActionSetsList.size) 87 | activeActionSetsList.forEachIndexed { i, set -> rawActiveActionSetArray[i] = set } 88 | 89 | if (Application.isEditor || updateDebugTextInBuilds) 90 | updateDebugText() 91 | } 92 | 93 | fun getSetFromHandle(handle: VRActionHandle): SteamVR_ActionSet? = SteamVR_Input.actionSets.firstOrNull { it.handle == handle } 94 | 95 | lateinit var debugActiveSetListText: String 96 | var updateDebugTextInBuilds = false 97 | 98 | private fun updateDebugText() { 99 | 100 | val stringBuilder = StringBuilder() 101 | 102 | rawActiveActionSetArray.forEach { set -> 103 | stringBuilder += set.priority 104 | stringBuilder += '\t' 105 | stringBuilder += SteamVR_Input_Source.getSource(set.restrictedToDevice) 106 | stringBuilder += '\t' 107 | stringBuilder += getSetFromHandle(set.actionSet)!!.shortName 108 | stringBuilder += '\n' 109 | } 110 | 111 | debugActiveSetListText = stringBuilder.toString() 112 | } 113 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/assets/steamVR/input/SteamVR_Behaviour_Pose.kt: -------------------------------------------------------------------------------- 1 | package openvr.assets.steamVR.input 2 | 3 | import glm_.vec3.Vec3 4 | import openvr.lib.TrackingResult 5 | import openvr.lib.vrInput 6 | import openvr.plugin2.SteamVR_Input_Sources 7 | import openvr.plugin2.Transform 8 | import openvr.assets.steamVR.script.SteamVR_HistoryBuffer 9 | import openvr.unity.Time 10 | 11 | //======= Copyright (c) Valve Corporation, All rights reserved. =============== 12 | 13 | typealias SteamVR_Behaviour_PoseEvent = (SteamVR_Behaviour_Pose, SteamVR_Input_Sources) -> Unit 14 | typealias SteamVR_Behaviour_Pose_ConnectedChangedEvent = (SteamVR_Behaviour_Pose, SteamVR_Input_Sources, Boolean) -> Unit 15 | typealias SteamVR_Behaviour_Pose_TrackingChangedEvent = (SteamVR_Behaviour_Pose, SteamVR_Input_Sources, TrackingResult) -> Unit 16 | typealias SteamVR_Behaviour_Pose_DeviceIndexChangedEvent = (SteamVR_Behaviour_Pose, SteamVR_Input_Sources, Int) -> Unit 17 | 18 | /** This component simplifies the use of Pose actions. Adding it to a gameobject will auto set that transform's position and rotation every update to match the pose. 19 | * Advanced velocity estimation is handled through a buffer of the last 30 updates. */ 20 | class SteamVR_Behaviour_Pose { 21 | 22 | val poseAction = SteamVR_Input.getAction(ActionType.Pose, "Pose") 23 | 24 | /** The device this action should apply to. Any if the action is not device specific. */ 25 | lateinit var inputSource: SteamVR_Input_Sources 26 | 27 | /** If not set, relative to parent */ 28 | var origin: Transform? = null 29 | 30 | /** @Returns whether or not the current pose is in a valid state */ 31 | val isValid: Boolean 32 | get() = poseAction!![inputSource]!!.poseIsValid 33 | 34 | /** @Returns whether or not the pose action is bound and able to be updated */ 35 | val isActive: Boolean 36 | get() = poseAction!![inputSource]!!.active 37 | 38 | 39 | /** This Unity event will fire whenever the position or rotation of this transform is updated. */ 40 | var onTransformUpdated: SteamVR_Behaviour_PoseEvent? = null 41 | 42 | /** This Unity event will fire whenever the position or rotation of this transform is changed. */ 43 | var onTransformChanged: SteamVR_Behaviour_PoseEvent? = null 44 | 45 | /** This Unity event will fire whenever the device is connected or disconnected */ 46 | var onConnectedChanged: SteamVR_Behaviour_Pose_ConnectedChangedEvent? = null 47 | 48 | /** This Unity event will fire whenever the device's tracking state changes */ 49 | var onTrackingChanged: SteamVR_Behaviour_Pose_TrackingChangedEvent? = null 50 | 51 | /** This Unity event will fire whenever the device's deviceIndex changes */ 52 | var onDeviceIndexChanged: SteamVR_Behaviour_Pose_DeviceIndexChangedEvent? = null 53 | 54 | 55 | /** This C# event will fire whenever the position or rotation of this transform is updated. */ 56 | var onTransformUpdatedEvent: SteamVR_Behaviour_Pose_UpdateHandler? = null 57 | 58 | /** This C# event will fire whenever the position or rotation of this transform is changed. */ 59 | var onTransformChangedEvent: SteamVR_Behaviour_Pose_ChangeHandler? = null 60 | 61 | /** This C# event will fire whenever the device is connected or disconnected */ 62 | var onConnectedChangedEvent: SteamVR_Behaviour_Pose_DeviceConnectedChangeHandler? = null 63 | 64 | /** This C# event will fire whenever the device's tracking state changes */ 65 | var onTrackingChangedEvent: SteamVR_Behaviour_Pose_TrackingChangeHandler? = null 66 | 67 | /** This C# event will fire whenever the device's deviceIndex changes */ 68 | var onDeviceIndexChangedEvent: SteamVR_Behaviour_Pose_DeviceIndexChangedHandler? = null 69 | 70 | 71 | /** Can be disabled to stop broadcasting bound device status changes */ 72 | var broadcastDeviceChanges = true 73 | 74 | protected var deviceIndex = -1 75 | 76 | protected val historyBuffer = SteamVR_HistoryBuffer(30) 77 | 78 | 79 | protected fun start() { 80 | if (poseAction == null) { 81 | System.err.println("[SteamVR] No pose action set for this component") 82 | return 83 | } 84 | 85 | checkDeviceIndex() 86 | 87 | // if (origin == null) 88 | // origin = transform.parent TODO 89 | } 90 | 91 | protected fun onEnable() { 92 | // SteamVR.Initialize() 93 | 94 | if (poseAction != null) { 95 | poseAction[inputSource]!!.onUpdate += ::onUpdate 96 | poseAction[inputSource]!!.onDeviceConnectedChanged += ::onDeviceConnectedChanged 97 | poseAction[inputSource]!!.onTrackingChanged += ::onTrackingChanged 98 | poseAction[inputSource]!!.onChange += ::onChange 99 | } 100 | } 101 | 102 | protected fun onDisable() { 103 | poseAction?.get(inputSource)!!.let { 104 | it.onUpdate -= ::onUpdate 105 | it.onDeviceConnectedChanged -= ::onDeviceConnectedChanged 106 | it.onTrackingChanged -= ::onTrackingChanged 107 | it.onChange -= ::onChange 108 | } 109 | 110 | historyBuffer.clear() 111 | } 112 | 113 | private fun onUpdate(fromAction: SteamVR_Action_Pose, fromSource: SteamVR_Input_Sources) { 114 | 115 | updateHistoryBuffer() 116 | 117 | updateTransform() 118 | 119 | onTransformUpdated?.invoke(this, inputSource) 120 | onTransformUpdatedEvent?.invoke(this, inputSource) 121 | } 122 | 123 | protected fun updateTransform() { 124 | 125 | checkDeviceIndex() 126 | TODO() 127 | // if (origin != null) { 128 | // transform.position = origin.transform.TransformPoint(poseAction[inputSource].localPosition) 129 | // transform.rotation = origin.rotation * poseAction[inputSource].localRotation 130 | // } else { 131 | // transform.localPosition = poseAction[inputSource].localPosition 132 | // transform.localRotation = poseAction[inputSource].localRotation 133 | // } 134 | } 135 | 136 | private fun onChange(fromAction: SteamVR_Action_Pose, fromSource: SteamVR_Input_Sources) { 137 | onTransformChanged?.invoke(this, fromSource) 138 | onTransformChangedEvent?.invoke(this, fromSource) 139 | } 140 | 141 | protected fun onDeviceConnectedChanged(changedAction: SteamVR_Action_Pose, changedSource: SteamVR_Input_Sources, connected: Boolean) { 142 | 143 | checkDeviceIndex() 144 | 145 | onConnectedChanged?.invoke(this, inputSource, connected) 146 | onConnectedChangedEvent?.invoke(this, inputSource, connected) 147 | } 148 | 149 | protected fun onTrackingChanged(changedAction: SteamVR_Action_Pose, changedSource: SteamVR_Input_Sources, trackingChanged: TrackingResult) { 150 | onTrackingChanged?.invoke(this, inputSource, trackingChanged) 151 | onTrackingChangedEvent?.invoke(this, inputSource, trackingChanged) 152 | } 153 | 154 | protected fun checkDeviceIndex() { 155 | val action = poseAction!![inputSource]!! 156 | if (action.active && action.deviceIsConnected) { 157 | val currentDeviceIndex = action.trackedDeviceIndex 158 | 159 | if (deviceIndex != currentDeviceIndex) { 160 | deviceIndex = currentDeviceIndex 161 | 162 | if (broadcastDeviceChanges) { 163 | println("SetInputSource $inputSource") 164 | println("SetDeviceIndex $deviceIndex") 165 | } 166 | 167 | onDeviceIndexChanged?.invoke(this, inputSource, deviceIndex) 168 | onDeviceIndexChangedEvent?.invoke(this, inputSource, deviceIndex) 169 | } 170 | } 171 | } 172 | 173 | /** @Returns the device index for the device bound to the pose. */ 174 | fun GetDeviceIndex(): Int { 175 | if (deviceIndex == -1) 176 | checkDeviceIndex() 177 | return deviceIndex 178 | } 179 | 180 | /** @Returns the current velocity of the pose (as of the last update) */ 181 | val velocity: Vec3 182 | get() = poseAction!![inputSource]!!.velocity 183 | 184 | /** @Returns the current angular velocity of the pose (as of the last update) */ 185 | val angularVelocity: Vec3 186 | get() = poseAction!![inputSource]!!.angularVelocity 187 | 188 | /** @Returns the velocities of the pose at the time specified. Can predict in the future or return past values. */ 189 | fun getVelocitiesAtTimeOffset(secondsFromNow: Float, velocity: Vec3, angularVelocity: Vec3): Boolean = 190 | poseAction!![inputSource]!!.getVelocitiesAtTimeOffset(secondsFromNow, velocity, angularVelocity) 191 | 192 | /** Uses previously recorded values to find the peak speed of the pose and returns the corresponding velocity and angular velocity */ 193 | fun getEstimatedPeakVelocities(velocity: Vec3, angularVelocity: Vec3) { 194 | val top = historyBuffer.getTopVelocity(10, 1) 195 | historyBuffer.getAverageVelocities(velocity, angularVelocity, 2, top) 196 | } 197 | 198 | protected var lastFrameUpdated = 0 199 | protected fun updateHistoryBuffer() { 200 | val currentFrame = Time.frameCount 201 | if (lastFrameUpdated != currentFrame) { 202 | val action = poseAction!![inputSource]!! 203 | historyBuffer.update(action.localPosition, action.localRotation, action.velocity, action.angularVelocity) 204 | lastFrameUpdated = currentFrame 205 | } 206 | } 207 | 208 | /** Gets the localized name of the device that the action corresponds to. 209 | * 210 | * @param localizedParts: 211 | * VRInputString.Hand - Which hand the origin is in. E.g. "Left Hand" 212 | * VRInputString.ControllerType - What kind of controller the user has in that hand.E.g. "Vive Controller" 213 | * VRInputString.InputSource - What part of that controller is the origin. E.g. "Trackpad" 214 | * VRInputString.All - All of the above. E.g. "Left Hand Vive Controller Trackpad" */ 215 | fun getLocalizedName(localizedParts: Array): String? = 216 | poseAction?.getLocalizedOriginPart(inputSource, localizedParts) 217 | } 218 | 219 | typealias SteamVR_Behaviour_Pose_ActiveChangeHandler = (fromAction: SteamVR_Behaviour_Pose, fromSource: SteamVR_Input_Sources, active: Boolean) -> Unit 220 | typealias SteamVR_Behaviour_Pose_ChangeHandler = (fromAction: SteamVR_Behaviour_Pose, fromSource: SteamVR_Input_Sources) -> Unit 221 | typealias SteamVR_Behaviour_Pose_UpdateHandler = (fromAction: SteamVR_Behaviour_Pose, fromSource: SteamVR_Input_Sources) -> Unit 222 | typealias SteamVR_Behaviour_Pose_TrackingChangeHandler = (fromAction: SteamVR_Behaviour_Pose, fromSource: SteamVR_Input_Sources, trackingState: TrackingResult) -> Unit 223 | typealias SteamVR_Behaviour_Pose_ValidPoseChangeHandler = (fromAction: SteamVR_Behaviour_Pose, fromSource: SteamVR_Input_Sources, validPose: Boolean) -> Unit 224 | typealias SteamVR_Behaviour_Pose_DeviceConnectedChangeHandler = (fromAction: SteamVR_Behaviour_Pose, fromSource: SteamVR_Input_Sources, deviceConnected: Boolean) -> Unit 225 | typealias SteamVR_Behaviour_Pose_DeviceIndexChangedHandler = (fromAction: SteamVR_Behaviour_Pose, fromSource: SteamVR_Input_Sources, newDeviceIndex: Int) -> Unit -------------------------------------------------------------------------------- /src/main/kotlin/openvr/assets/steamVR/input/SteamVR_Skeleton_Pose.kt: -------------------------------------------------------------------------------- 1 | package openvr.assets.steamVR.input 2 | 3 | import glm_.quat.Quat 4 | import glm_.vec3.Vec3 5 | import openvr.plugin2.SteamVR_Input_Sources 6 | 7 | //======= Copyright (c) Valve Corporation, All rights reserved. =============== 8 | 9 | class SteamVR_Skeleton_Pose { 10 | 11 | val leftHand = SteamVR_Skeleton_Pose_Hand(SteamVR_Input_Sources.LeftHand) 12 | val rightHand = SteamVR_Skeleton_Pose_Hand(SteamVR_Input_Sources.RightHand) 13 | 14 | protected val leftHandInputSource = SteamVR_Input_Sources.LeftHand.ordinal 15 | protected val rightHandInputSource = SteamVR_Input_Sources.RightHand.ordinal 16 | 17 | var applyToSkeletonRoot = true 18 | 19 | fun getHand(hand: Int): SteamVR_Skeleton_Pose_Hand? = when (hand) { 20 | leftHandInputSource -> leftHand 21 | rightHandInputSource -> rightHand 22 | else -> null 23 | } 24 | 25 | fun getHand(hand: SteamVR_Input_Sources): SteamVR_Skeleton_Pose_Hand? = getHand(hand.ordinal) 26 | } 27 | 28 | class SteamVR_Skeleton_Pose_Hand(val inputSource: SteamVR_Input_Sources) { 29 | 30 | val thumbFingerMovementType = SteamVR_Skeleton_FingerExtensionTypes.Static 31 | val indexFingerMovementType = SteamVR_Skeleton_FingerExtensionTypes.Static 32 | val middleFingerMovementType = SteamVR_Skeleton_FingerExtensionTypes.Static 33 | val ringFingerMovementType = SteamVR_Skeleton_FingerExtensionTypes.Static 34 | val pinkyFingerMovementType = SteamVR_Skeleton_FingerExtensionTypes.Static 35 | 36 | /** Get extension type for a particular finger. Thumb is 0, Index is 1, etc. */ 37 | fun getFingerExtensionType(finger: Int): SteamVR_Skeleton_FingerExtensionTypes = when (finger) { 38 | 0 -> thumbFingerMovementType 39 | 1 -> indexFingerMovementType 40 | 2 -> middleFingerMovementType 41 | 3 -> ringFingerMovementType 42 | 4 -> pinkyFingerMovementType 43 | else -> SteamVR_Skeleton_FingerExtensionTypes.Static.also { 44 | System.err.println("Finger not in range!") 45 | } 46 | } 47 | 48 | var ignoreRootPoseData = true 49 | var ignoreWristPoseData = true 50 | 51 | val position = Vec3() 52 | val rotation = Quat() 53 | 54 | var bonePositions = emptyArray() 55 | val boneRotations = emptyArray() 56 | 57 | // public SteamVR_Skeleton_Pose_Hand(SteamVR_Input_Sources source) 58 | // { 59 | // inputSource = source; 60 | // } 61 | 62 | fun getMovementTypeForBone(boneIndex: Int): SteamVR_Skeleton_FingerExtensionTypes = 63 | when (JointIndex.getFingerForBone(boneIndex)) { 64 | FingerIndex.thumb.i -> thumbFingerMovementType 65 | FingerIndex.index.i -> indexFingerMovementType 66 | FingerIndex.middle.i -> middleFingerMovementType 67 | FingerIndex.ring.i -> ringFingerMovementType 68 | FingerIndex.pinky.i -> pinkyFingerMovementType 69 | else -> SteamVR_Skeleton_FingerExtensionTypes.Static 70 | } 71 | } 72 | 73 | enum class SteamVR_Skeleton_FingerExtensionTypes { Static, Free, Extend, Contract } 74 | 75 | class SteamVR_Skeleton_FingerExtensionTypeLists { 76 | val enumList by lazy { SteamVR_Skeleton_FingerExtensionTypes.values() } 77 | val stringList by lazy { enumList.map { it.name } } 78 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/assets/steamVR/input/SteamVR_UpdateModes.kt: -------------------------------------------------------------------------------- 1 | package openvr.assets.steamVR.input 2 | 3 | //======= Copyright (c) Valve Corporation, All rights reserved. =============== 4 | 5 | enum class SteamVR_UpdateModes { 6 | Nothing, OnUpdate, OnFixedUpdate, OnPreCull, OnLateUpdate; 7 | 8 | val i = 1 shl ordinal 9 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/assets/steamVR/script/SteamVR_Behaviour.kt: -------------------------------------------------------------------------------- 1 | package openvr.assets.steamVR.script 2 | 3 | object SteamVR_Behaviour { 4 | 5 | private val openVRDeviceName = "OpenVR" 6 | var forcingInitialization = false 7 | 8 | init { 9 | initialize(false) 10 | } 11 | 12 | var initializeSteamVROnAwake = true 13 | 14 | var doNotDestroy = true 15 | 16 | // [HideInInspector] 17 | // public SteamVR_Render steamvr_render 18 | 19 | internal var isPlaying = false 20 | 21 | private var initializing = false 22 | fun initialize(forceUnityVRToOpenVR: Boolean = false) { 23 | if (initializing == false) { 24 | initializing = true 25 | // GameObject steamVRObject = null 26 | 27 | if (forceUnityVRToOpenVR) 28 | forcingInitialization = true 29 | 30 | // SteamVR_Render renderInstance = GameObject.FindObjectOfType() 31 | // if (renderInstance != null) 32 | // steamVRObject = renderInstance.gameObject 33 | // 34 | // SteamVR_Behaviour behaviourInstance = GameObject.FindObjectOfType() 35 | // if (behaviourInstance != null) 36 | // steamVRObject = behaviourInstance.gameObject 37 | // 38 | // if (steamVRObject == null) 39 | // { 40 | // GameObject objectInstance = new GameObject("[SteamVR]") 41 | // _instance = objectInstance.AddComponent() 42 | // _instance.steamvr_render = objectInstance.AddComponent() 43 | // } 44 | // else 45 | // { 46 | // behaviourInstance = steamVRObject.GetComponent() 47 | // if (behaviourInstance == null) 48 | // behaviourInstance = steamVRObject.AddComponent() 49 | // 50 | // if (renderInstance != null) 51 | // behaviourInstance.steamvr_render = renderInstance 52 | // else 53 | // { 54 | // behaviourInstance.steamvr_render = steamVRObject.GetComponent() 55 | // if (behaviourInstance.steamvr_render == null) 56 | // behaviourInstance.steamvr_render = steamVRObject.AddComponent() 57 | // } 58 | // 59 | // _instance = behaviourInstance 60 | // } 61 | // 62 | // if (_instance != null && _instance .doNotDestroy) 63 | // GameObject.DontDestroyOnLoad(_instance .transform.root.gameObject) 64 | 65 | initializing = false 66 | } 67 | } 68 | 69 | // protected void Awake() 70 | // { 71 | // isPlaying = true; 72 | // 73 | // if (initializeSteamVROnAwake && forcingInitialization == false) 74 | // InitializeSteamVR() 75 | // } 76 | // 77 | // public void InitializeSteamVR(bool forceUnityVRToOpenVR = false) 78 | // { 79 | // if (forceUnityVRToOpenVR) 80 | // { 81 | // forcingInitialization = true 82 | // 83 | // if (initializeCoroutine != null) 84 | // StopCoroutine(initializeCoroutine) 85 | // 86 | // if (XRSettings.loadedDeviceName == openVRDeviceName) 87 | // EnableOpenVR() 88 | // else 89 | // initializeCoroutine = StartCoroutine(DoInitializeSteamVR(forceUnityVRToOpenVR)) 90 | // } 91 | // else 92 | // { 93 | // SteamVR.Initialize(false) 94 | // } 95 | // } 96 | // 97 | // private Coroutine initializeCoroutine 98 | // 99 | // #if UNITY_2018_3_OR_NEWER 100 | // private bool loadedOpenVRDeviceSuccess = false 101 | // private IEnumerator DoInitializeSteamVR(bool forceUnityVRToOpenVR = false) 102 | // { 103 | // XRDevice.deviceLoaded += XRDevice_deviceLoaded 104 | // XRSettings.LoadDeviceByName(openVRDeviceName) 105 | // while (loadedOpenVRDeviceSuccess == false) 106 | // { 107 | // `yield` return null 108 | // } 109 | // XRDevice.deviceLoaded -= XRDevice_deviceLoaded 110 | // EnableOpenVR() 111 | // } 112 | // 113 | // private void XRDevice_deviceLoaded(string deviceName) 114 | // { 115 | // if (deviceName == openVRDeviceName) 116 | // { 117 | // loadedOpenVRDeviceSuccess = true 118 | // } 119 | // else 120 | // { 121 | // Debug.LogError("[SteamVR] Tried to async load: " + openVRDeviceName + ". Loaded: " + deviceName) 122 | // loadedOpenVRDeviceSuccess = true //try anyway 123 | // } 124 | // } 125 | // #else 126 | // private IEnumerator DoInitializeSteamVR(bool forceUnityVRToOpenVR = false) 127 | // { 128 | // XRSettings.LoadDeviceByName(openVRDeviceName) 129 | // `yield` return null 130 | // EnableOpenVR() 131 | // } 132 | // #endif 133 | // 134 | // private void EnableOpenVR() 135 | // { 136 | // XRSettings.enabled = true 137 | // SteamVR.Initialize(false) 138 | // initializeCoroutine = null 139 | // forcingInitialization = false 140 | // } 141 | // 142 | // #if UNITY_EDITOR 143 | // //only stop playing if the unity editor is running 144 | // private void OnDestroy() 145 | // { 146 | // isPlaying = false; 147 | // } 148 | // #endif 149 | // 150 | // #if UNITY_2017_1_OR_NEWER 151 | // protected void OnEnable() 152 | // { 153 | // Application.onBeforeRender += OnBeforeRender 154 | // SteamVR_Events.System(EVREventType.VREvent_Quit).Listen(OnQuit) 155 | // } 156 | // protected void OnDisable() 157 | // { 158 | // Application.onBeforeRender -= OnBeforeRender 159 | // SteamVR_Events.System(EVREventType.VREvent_Quit).Remove(OnQuit) 160 | // } 161 | // protected void OnBeforeRender() 162 | // { 163 | // PreCull() 164 | // } 165 | // #else 166 | // protected void OnEnable() 167 | // { 168 | // Camera.onPreCull += OnCameraPreCull 169 | // SteamVR_Events.System(EVREventType.VREvent_Quit).Listen(OnQuit) 170 | // } 171 | // protected void OnDisable() 172 | // { 173 | // Camera.onPreCull -= OnCameraPreCull 174 | // SteamVR_Events.System(EVREventType.VREvent_Quit).Remove(OnQuit) 175 | // } 176 | // protected void OnCameraPreCull(Camera cam) 177 | // { 178 | // if (!cam.stereoEnabled) 179 | // return 180 | // 181 | // PreCull() 182 | // } 183 | // #endif 184 | // 185 | // protected static int lastFrameCount = -1 186 | // protected void PreCull() 187 | // { 188 | // // Only update poses on the first camera per frame. 189 | // if (Time.frameCount != lastFrameCount) 190 | // { 191 | // lastFrameCount = Time.frameCount 192 | // 193 | // SteamVR_Input.OnPreCull() 194 | // } 195 | // } 196 | // 197 | // protected void FixedUpdate() 198 | // { 199 | // SteamVR_Input.FixedUpdate() 200 | // } 201 | // 202 | // protected void LateUpdate() 203 | // { 204 | // SteamVR_Input.LateUpdate() 205 | // } 206 | // 207 | // protected void Update() 208 | // { 209 | // SteamVR_Input.Update() 210 | // } 211 | // 212 | // protected void OnQuit(VREvent_t vrEvent) 213 | // { 214 | // #if UNITY_EDITOR 215 | // UnityEditor.EditorApplication.isPlaying = false 216 | // #else 217 | // Application.Quit() 218 | // #endif 219 | // } 220 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/assets/steamVR/script/SteamVR_Events.kt: -------------------------------------------------------------------------------- 1 | package openvr.assets.steamVR.script 2 | 3 | import openvr.lib.VREventType 4 | import openvr.plugin2.SteamVR_RenderModel 5 | import org.lwjgl.openvr.TrackedDevicePose 6 | import org.lwjgl.openvr.VREvent 7 | import java.awt.Color 8 | 9 | //======= Copyright (c) Valve Corporation, All rights reserved. =============== 10 | // 11 | // Purpose: Simple event system for SteamVR. 12 | // 13 | // Example usage: 14 | // 15 | // void OnDeviceConnected(int i, bool connected) { ... } 16 | // SteamVR_Events.DeviceConnected.Listen(OnDeviceConnected); // Usually in OnEnable 17 | // SteamVR_Events.DeviceConnected.Remove(OnDeviceConnected); // Usually in OnDisable 18 | // 19 | // Alternatively, if Listening/Removing often these can be cached as follows: 20 | // 21 | // SteamVR_Event.Action deviceConnectedAction; 22 | // void OnAwake() { deviceConnectedAction = SteamVR_Event.DeviceConnectedAction(OnDeviceConnected); } 23 | // void OnEnable() { deviceConnectedAction.enabled = true; } 24 | // void OnDisable() { deviceConnectedAction.enabled = false; } 25 | // 26 | //============================================================================= 27 | 28 | object SteamVR_Events { 29 | 30 | abstract class Action { 31 | abstract fun enable(enabled: Boolean) 32 | // var enabled: Boolean 33 | // set(value) = enable(value) 34 | } 35 | 36 | class Action0(val _event: Event0, val action: UnityAction0) : Action() { 37 | override fun enable(enabled: Boolean) = when { 38 | enabled -> _event.listen(action) 39 | else -> _event.remove(action) 40 | } 41 | } 42 | 43 | class Action1(val _event: Event1, val action: UnityAction1) : Action() { 44 | override fun enable(enabled: Boolean) = when { 45 | enabled -> _event.listen(action) 46 | else -> _event.remove(action) 47 | } 48 | } 49 | 50 | class Action2(val _event: Event2, val action: UnityAction2) : Action() { 51 | override fun enable(enabled: Boolean) = when { 52 | enabled -> _event.listen(action) 53 | else -> _event.remove(action) 54 | } 55 | } 56 | 57 | class Action3(val _event: Event3, val action: UnityAction3) : Action() { 58 | override fun enable(enabled: Boolean) = when { 59 | enabled -> _event.listen(action) 60 | else -> _event.remove(action) 61 | } 62 | } 63 | 64 | class Event0 : UnityEvent0() { 65 | fun listen(action: UnityAction0) { 66 | calls += action 67 | } 68 | 69 | fun remove(action: UnityAction0) { 70 | calls -= action 71 | } 72 | 73 | fun send() = calls.forEach { it() } 74 | } 75 | 76 | class Event1 : UnityEvent1() { 77 | fun listen(action: UnityAction1) { 78 | calls += action 79 | } 80 | 81 | fun remove(action: UnityAction1) { 82 | calls -= action 83 | } 84 | 85 | fun send(arg0: T) = calls.forEach { it(arg0) } 86 | } 87 | 88 | class Event2 : UnityEvent2() { 89 | fun listen(action: UnityAction2) { 90 | calls += action 91 | } 92 | 93 | fun remove(action: UnityAction2) { 94 | calls -= action 95 | } 96 | 97 | fun send(arg0: T0, arg1: T1) = calls.forEach { it(arg0, arg1) } 98 | } 99 | 100 | class Event3 : UnityEvent3() { 101 | fun listen(action: UnityAction3) { 102 | calls += action 103 | } 104 | 105 | fun remove(action: UnityAction3) { 106 | calls -= action 107 | } 108 | 109 | fun send(arg0: T0, arg1: T1, arg2: T2) = calls.forEach { it(arg0, arg1, arg2) } 110 | } 111 | 112 | val calibrating = Event1() 113 | fun calibratingAction(action: UnityAction1): Action = Action1(calibrating, action) 114 | 115 | val deviceConnected = Event2() 116 | fun deviceConnectedAction(action: UnityAction2): Action = Action2(deviceConnected, action) 117 | 118 | val fade = Event3() 119 | fun fadeAction(action: UnityAction3): Action = Action3(fade, action) 120 | 121 | val fadeReady = Event0() 122 | fun fadeReadyAction(action: UnityAction0): Action = Action0(fadeReady, action) 123 | 124 | val hideRenderModels = Event1() 125 | fun hideRenderModelsAction(action: UnityAction1): Action = Action1(hideRenderModels, action) 126 | 127 | val initializing = Event1() 128 | fun initializingAction(action: UnityAction1): Action = Action1(initializing, action) 129 | 130 | val inputFocus = Event1() 131 | fun inputFocusAction(action: UnityAction1): Action = Action1(inputFocus, action) 132 | 133 | val loading = Event1() 134 | fun loadingAction(action: UnityAction1): Action = Action1(loading, action) 135 | 136 | val loadingFadeIn = Event1() 137 | fun loadingFadeInAction(action: UnityAction1): Action = Action1(loadingFadeIn, action) 138 | 139 | val loadingFadeOut = Event1() 140 | fun loadingFadeOutAction(action: UnityAction1): Action = Action1(loadingFadeOut, action) 141 | 142 | val newPoses = Event1>() 143 | fun newPosesAction(action: UnityAction1>): Action = Action1(newPoses, action) 144 | 145 | val newPosesApplied = Event0() 146 | fun newPosesAppliedAction(action: UnityAction0): Action = Action0(newPosesApplied, action) 147 | 148 | val initialized = Event1() 149 | fun initializedAction(action: UnityAction1): Action = Action1(initialized, action) 150 | 151 | val outOfRange = Event1() 152 | fun outOfRangeAction(action: UnityAction1): Action = Action1(outOfRange, action) 153 | 154 | val renderModelLoaded = Event2() 155 | fun renderModelLoadedAction(action: UnityAction2): Action = Action2(renderModelLoaded, action) 156 | 157 | val systemEvents = mutableMapOf>() 158 | fun system(eventType: VREventType): Event1 = systemEvents.getOrPut(eventType) { Event1() } 159 | 160 | fun systemAction(eventType: VREventType, action: UnityAction1): Action = Action1(system(eventType), action) 161 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/assets/steamVR/script/SteamVR_HistoryBuffer.kt: -------------------------------------------------------------------------------- 1 | package openvr.assets.steamVR.script 2 | 3 | import glm_.quat.Quat 4 | import glm_.vec3.Vec3 5 | 6 | //======= Copyright (c) Valve Corporation, All rights reserved. =============== 7 | 8 | open class SteamVR_RingBuffer(val clazz: Class, size: Int) { 9 | 10 | val default: T 11 | get() = clazz.getDeclaredConstructor().newInstance() 12 | 13 | protected val buffer = Array(size) { default } 14 | protected var currentIndex = 0 15 | protected var lastElement: T? = null 16 | 17 | operator fun plus(newElement: T) { 18 | buffer[currentIndex] = newElement 19 | stepForward() 20 | } 21 | 22 | fun stepForward() { 23 | lastElement = buffer[currentIndex] as T? 24 | 25 | currentIndex++ 26 | if (currentIndex >= buffer.size) 27 | currentIndex = 0 28 | 29 | cleared = false 30 | } 31 | 32 | operator fun get(index: Int): T = 33 | buffer[if (index < 0) index + buffer.size else index] as T 34 | 35 | // public virtual T GetLast() 36 | // { 37 | // return lastElement 38 | // } 39 | 40 | val lastIndex: Int 41 | get() { 42 | var lastIndex = currentIndex - 1 43 | if (lastIndex < 0) 44 | lastIndex += buffer.size 45 | 46 | return lastIndex 47 | } 48 | 49 | private var cleared = false 50 | 51 | fun clear() { 52 | if (cleared) 53 | return 54 | 55 | if (buffer == null) 56 | return 57 | 58 | for (index in buffer.indices) 59 | buffer[index] = default 60 | 61 | lastElement = default 62 | 63 | currentIndex = 0 64 | 65 | cleared = true 66 | } 67 | 68 | companion object { 69 | inline operator fun invoke(size: Int): SteamVR_RingBuffer = SteamVR_RingBuffer(T::class.java, size) 70 | } 71 | } 72 | 73 | class SteamVR_HistoryBuffer(size: Int) : SteamVR_RingBuffer(SteamVR_HistoryStep::class.java, size) { 74 | 75 | fun update(position: Vec3, rotation: Quat, velocity: Vec3, angularVelocity: Vec3) { 76 | 77 | if (buffer[currentIndex] == null) 78 | buffer[currentIndex] = SteamVR_HistoryStep() 79 | 80 | (buffer[currentIndex] as SteamVR_HistoryStep).also { 81 | it.position put position 82 | it.rotation put rotation 83 | it.velocity put velocity 84 | it.angularVelocity put angularVelocity 85 | it.timeInTicks = System.currentTimeMillis() 86 | } 87 | stepForward() 88 | } 89 | 90 | fun getVelocityMagnitudeTrend(toIndex_: Int = -1, fromIndex_: Int = -1): Float { 91 | 92 | var toIndex = toIndex_ 93 | if (toIndex == -1) 94 | toIndex = currentIndex - 1 95 | 96 | if (toIndex < 0) 97 | toIndex += buffer.size 98 | 99 | var fromIndex = fromIndex_ 100 | if (fromIndex == -1) 101 | fromIndex = toIndex - 1 102 | 103 | if (fromIndex < 0) 104 | fromIndex += buffer.size 105 | 106 | val toStep = buffer[toIndex] as SteamVR_HistoryStep? 107 | val fromStep = buffer[fromIndex] as SteamVR_HistoryStep? 108 | 109 | return when { 110 | toStep?.isValid == true && fromStep?.isValid == true -> toStep.velocity.length() - fromStep.velocity.length() 111 | else -> 0f 112 | } 113 | } 114 | 115 | val SteamVR_HistoryStep.isValid: Boolean 116 | get() = timeInTicks != -1L 117 | 118 | fun getTopVelocity(forFrames_: Int, addFrames: Int = 0): Int { 119 | 120 | var forFrames = forFrames_ 121 | var topFrame = currentIndex 122 | var topVelocitySqr = 0f 123 | 124 | var currentFrame = currentIndex 125 | 126 | while (forFrames > 0) { 127 | forFrames-- 128 | currentFrame-- 129 | 130 | if (currentFrame < 0) 131 | currentFrame = buffer.lastIndex 132 | 133 | val currentStep = buffer[currentFrame] as SteamVR_HistoryStep? 134 | 135 | if (currentStep?.isValid == false) 136 | break 137 | 138 | val currentSqr = currentStep!!.velocity.length() 139 | if (currentSqr > topVelocitySqr) { 140 | topFrame = currentFrame 141 | topVelocitySqr = currentSqr 142 | } 143 | } 144 | 145 | topFrame += addFrames 146 | 147 | if (topFrame >= buffer.size) 148 | topFrame -= buffer.size 149 | 150 | return topFrame 151 | } 152 | 153 | fun getAverageVelocities(velocity: Vec3, angularVelocity: Vec3, forFrames_: Int, startFrame_: Int = -1) { 154 | 155 | velocity put 0f 156 | angularVelocity put 0f 157 | 158 | var startFrame = startFrame_ 159 | if (startFrame == -1) 160 | startFrame = currentIndex - 1 161 | 162 | if (startFrame < 0) 163 | startFrame = buffer.lastIndex 164 | 165 | var forFrames = forFrames_ 166 | var endFrame = startFrame - forFrames 167 | 168 | if (endFrame < 0) 169 | endFrame += buffer.size 170 | 171 | val totalVelocity = Vec3() 172 | val totalAngularVelocity = Vec3() 173 | var totalFrames = 0f 174 | var currentFrame = startFrame 175 | while (forFrames > 0) { 176 | forFrames-- 177 | currentFrame-- 178 | 179 | if (currentFrame < 0) 180 | currentFrame = buffer.lastIndex 181 | 182 | val currentStep = buffer[currentFrame] as SteamVR_HistoryStep? 183 | 184 | if (currentStep?.isValid == false) 185 | break 186 | 187 | totalFrames++ 188 | 189 | totalVelocity += currentStep!!.velocity 190 | totalAngularVelocity += currentStep!!.angularVelocity 191 | } 192 | 193 | velocity put (totalVelocity / totalFrames) 194 | angularVelocity put (totalAngularVelocity / totalFrames) 195 | } 196 | } 197 | 198 | class SteamVR_HistoryStep { 199 | val position = Vec3() 200 | val rotation = Quat() 201 | 202 | val velocity = Vec3() 203 | 204 | val angularVelocity = Vec3() 205 | 206 | var timeInTicks = -1L 207 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/assets/steamVR/script/SteamVR_Settings.kt: -------------------------------------------------------------------------------- 1 | package openvr.assets.steamVR.script 2 | 3 | import openvr.lib.TrackingUniverseOrigin 4 | import openvr.assets.steamVR.input.SteamVR_Action_Pose 5 | import openvr.assets.steamVR.input.SteamVR_Input 6 | import openvr.assets.steamVR.input.SteamVR_UpdateModes 7 | import openvr.plugin2.SteamVR_Input_Sources 8 | import java.net.URL 9 | 10 | //======= Copyright (c) Valve Corporation, All rights reserved. =============== 11 | 12 | object SteamVR_Settings { 13 | 14 | var pauseGameWhenDashboardVisible = true 15 | var lockPhysicsUpdateRateToRenderFrequency = true 16 | var trackingSpace: TrackingUniverseOrigin 17 | get() = trackingSpaceOrigin 18 | set(newValue) { 19 | trackingSpaceOrigin = newValue 20 | if (SteamVR_Behaviour.isPlaying) 21 | SteamVR_Action_Pose.setTrackingUniverseOrigin(trackingSpaceOrigin) 22 | } 23 | 24 | private var trackingSpaceOrigin = TrackingUniverseOrigin.Standing 25 | 26 | /** Filename local to the project root (or executable, in a build) */ 27 | var actionsFilePath: URL? = ClassLoader.getSystemResource("actions.json") 28 | 29 | /** Path local to the Assets folder */ 30 | var steamVRInputPath = "SteamVR_Input" 31 | 32 | var inputUpdateMode = SteamVR_UpdateModes.OnUpdate.i 33 | var poseUpdateMode = SteamVR_UpdateModes.OnPreCull.i 34 | 35 | var activateFirstActionSetOnStart = true 36 | 37 | /** This is the app key the unity editor will use to identify your application. (can be \"steam.app.[appid]\" to persist bindings between editor steam) */ 38 | var editorAppKey: String? = null 39 | 40 | /** The SteamVR Plugin can automatically make sure VR is enabled in your player settings and if not, enable it. */ 41 | var autoEnableVR = true 42 | 43 | /** [Tooltip("This determines if we use legacy mixed reality mode (3rd controller/tracker device connected) or the new input system mode (pose / input source)")] */ 44 | var legacyMixedRealityCamera = true 45 | 46 | /** [Tooltip("[NON-LEGACY] This is the pose action that will be used for positioning a mixed reality camera if connected")] */ 47 | var mixedRealityCameraPose = SteamVR_Input.getPoseAction("ExternalCamera")!! 48 | 49 | /** [Tooltip("[NON-LEGACY] This is the input source to check on the pose for the mixed reality camera")] */ 50 | var mixedRealityCameraInputSource = SteamVR_Input_Sources.Camera 51 | 52 | /** [Tooltip("[NON-LEGACY] Auto enable mixed reality action set if file exists")] */ 53 | var mixedRealityActionSetAutoEnable = true 54 | 55 | fun isInputUpdateMode(toCheck: SteamVR_UpdateModes) = (inputUpdateMode and toCheck.i) == toCheck.i 56 | 57 | fun isPoseUpdateMode(toCheck: SteamVR_UpdateModes) = (poseUpdateMode and toCheck.i) == toCheck.i 58 | 59 | // public static void VerifyScriptableObject() 60 | // { 61 | // LoadInstance() 62 | // } 63 | // 64 | // private static void LoadInstance() 65 | // { 66 | // if (_instance == null) { 67 | // _instance = Resources.Load("SteamVR_Settings") 68 | // 69 | // if (_instance == null) { 70 | // _instance = SteamVR_Settings.CreateInstance() 71 | // 72 | // #if UNITY_EDITOR 73 | // string folderPath = SteamVR . GetResourcesFolderPath (true) 74 | // string assetPath = System . IO . Path . Combine (folderPath, "SteamVR_Settings.asset") 75 | // 76 | // UnityEditor.AssetDatabase.CreateAsset(_instance, assetPath) 77 | // UnityEditor.AssetDatabase.SaveAssets() 78 | // #endif 79 | // } 80 | // 81 | // if (string.IsNullOrEmpty(_instance.editorAppKey)) { 82 | // _instance.editorAppKey = SteamVR.GenerateAppKey() 83 | // Debug.Log("[SteamVR Setup] Generated you an editor app key of: " + _instance.editorAppKey + ". This lets the editor tell SteamVR what project this is. Has no effect on builds. This can be changed in Assets/SteamVR/Resources/SteamVR_Settings") 84 | // #if UNITY_EDITOR 85 | // UnityEditor.EditorUtility.SetDirty(_instance) 86 | // UnityEditor.AssetDatabase.SaveAssets() 87 | // #endif 88 | // } 89 | // } 90 | // } 91 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/assets/steamVR/script/UnityEvent.kt: -------------------------------------------------------------------------------- 1 | package openvr.assets.steamVR.script 2 | 3 | typealias UnityAction0 = () -> Unit 4 | typealias UnityAction1 = (T) -> Unit 5 | typealias UnityAction2 = (T0, T1) -> Unit 6 | typealias UnityAction3 = (T0, T1, T2) -> Unit 7 | 8 | abstract class UnityEvent0 { 9 | 10 | val calls = ArrayList<() -> Unit>() 11 | } 12 | 13 | abstract class UnityEvent1 { 14 | 15 | val calls = ArrayList<(T) -> Unit>() 16 | } 17 | 18 | abstract class UnityEvent2 { 19 | 20 | val calls = ArrayList<(T0, T1) -> Unit>() 21 | } 22 | 23 | abstract class UnityEvent3 { 24 | 25 | val calls = ArrayList<(T0, T1, T2) -> Unit>() 26 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/controllersListener.kt: -------------------------------------------------------------------------------- 1 | package openvr 2 | 3 | import openvr.lib.* 4 | import org.lwjgl.openvr.VREvent 5 | 6 | /** 7 | * Created by GBarbieri on 14.03.2017. 8 | */ 9 | 10 | abstract class ControllersListener { 11 | 12 | var leftIndex = vr.trackedDeviceIndexInvalid 13 | var rightIndex = vr.trackedDeviceIndexInvalid 14 | 15 | val leftState = VRControllerState() 16 | val rightState = VRControllerState() 17 | 18 | fun init() { 19 | 20 | // Process SteamVR controller state 21 | var firstFound = false 22 | repeat(vr.maxTrackedDeviceCount) { 23 | if (vrSystem.getTrackedDeviceClass(it) == TrackedDeviceClass.Controller) { 24 | val state = 25 | if (firstFound) { 26 | leftIndex = it 27 | leftState 28 | } else { // right first 29 | rightIndex = it 30 | rightState 31 | } 32 | firstFound = true 33 | } 34 | } 35 | updateStates() 36 | } 37 | 38 | fun update(event: VREvent) = when (event.eventType) { 39 | 40 | VREventType.TrackedDeviceActivated -> { 41 | 42 | if (vrSystem.getControllerRoleForTrackedDeviceIndex(event.trackedDeviceIndex) == TrackedControllerRole.LeftHand) 43 | leftIndex = event.trackedDeviceIndex 44 | else 45 | rightIndex = event.trackedDeviceIndex // right first 46 | 47 | trackedDeviceActivated(event) 48 | } 49 | 50 | VREventType.TrackedDeviceDeactivated -> { 51 | 52 | if (event.trackedDeviceIndex == leftIndex) 53 | leftIndex = vr.trackedDeviceIndexInvalid 54 | else if (event.trackedDeviceIndex == rightIndex) 55 | rightIndex = vr.trackedDeviceIndexInvalid 56 | 57 | trackedDeviceDeactivated(event) 58 | } 59 | 60 | VREventType.TrackedDeviceRoleChanged -> { 61 | 62 | if (event.trackedDeviceIndex == leftIndex || event.trackedDeviceIndex == rightIndex) 63 | 64 | if (vrSystem.getControllerRoleForTrackedDeviceIndex(leftIndex) == TrackedControllerRole.RightHand 65 | || vrSystem.getControllerRoleForTrackedDeviceIndex(rightIndex) == TrackedControllerRole.LeftHand) { 66 | 67 | val temp = leftIndex 68 | leftIndex = rightIndex 69 | rightIndex = temp 70 | 71 | updateStates() 72 | } 73 | 74 | trackedDeviceRoleChanged(event) 75 | } 76 | 77 | VREventType.PropertyChanged -> propertyChanged(event) 78 | 79 | else -> { 80 | } 81 | } 82 | 83 | abstract fun trackedDeviceActivated(event: VREvent) 84 | abstract fun trackedDeviceDeactivated(event: VREvent) 85 | 86 | abstract fun trackedDeviceRoleChanged(event: VREvent) 87 | abstract fun propertyChanged(event: VREvent) 88 | 89 | fun updateStates() { 90 | if (leftIndex != vr.trackedDeviceIndexInvalid) 91 | vrSystem.getControllerState(leftIndex, leftState) 92 | if (rightIndex != vr.trackedDeviceIndexInvalid) 93 | vrSystem.getControllerState(rightIndex, rightState) 94 | } 95 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/lib/VrInterface.kt: -------------------------------------------------------------------------------- 1 | package openvr.lib 2 | 3 | import org.lwjgl.openvr.VR 4 | 5 | interface vrInterface { 6 | 7 | val version: String 8 | 9 | /** Returns whether the interface of the specified version exists. */ 10 | val isInterfaceVersionValid: Boolean 11 | get() = stak.asciiAdr("FnTable:$version") { VR.nVR_IsInterfaceVersionValid(it) } 12 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/lib/calloc.kt: -------------------------------------------------------------------------------- 1 | package openvr.lib 2 | 3 | import org.lwjgl.openvr.VRControllerState 4 | import org.lwjgl.openvr.TrackedDevicePose 5 | 6 | fun VRControllerState() = VRControllerState.calloc() 7 | fun TrackedDevicePose() = TrackedDevicePose.calloc() -------------------------------------------------------------------------------- /src/main/kotlin/openvr/lib/ivrChaperone.kt: -------------------------------------------------------------------------------- 1 | package openvr.lib 2 | 3 | import glm_.vec2.Vec2i 4 | import kool.adr 5 | import kool.mInt 6 | import kool.rem 7 | import org.lwjgl.openvr.HmdColor 8 | import org.lwjgl.openvr.HmdQuad 9 | import org.lwjgl.openvr.VRChaperone 10 | import org.lwjgl.openvr.VRChaperone.* 11 | 12 | 13 | object vrChaperone : vrInterface { 14 | 15 | enum class CalibrationState(@JvmField val i: Int) { 16 | 17 | // OK! 18 | /** Chaperone is fully calibrated and working correctly */ 19 | OK(1), 20 | 21 | // Warnings 22 | Warning(100), 23 | /** A base station thinks that it might have moved */ 24 | Warning_BaseStationMayHaveMoved(101), 25 | /** There are less base stations than when calibrated */ 26 | Warning_BaseStationRemoved(102), 27 | /** Seated bounds haven't been calibrated for the current tracking center */ 28 | Warning_SeatedBoundsInvalid(103), 29 | 30 | // Errors 31 | /** The UniverseID is invalid */ 32 | Error(200), 33 | /** Tracking center hasn't be calibrated for at least one of the base stations */ 34 | Error_BaseStationUninitialized(201), 35 | /** Tracking center is calibrated), but base stations disagree on the tracking space */ 36 | Error_BaseStationConflict(202), 37 | /** Play Area hasn't been calibrated for the current tracking center */ 38 | Error_PlayAreaInvalid(203), 39 | /** Collision Bounds haven't been calibrated for the current tracking center */ 40 | Error_CollisionBoundsInvalid(204); 41 | 42 | companion object { 43 | infix fun of(i: Int) = values().first { it.i == i } 44 | } 45 | } 46 | 47 | /** Get the current state of Chaperone calibration. This state can change at any time during a session due to physical base station changes. */ 48 | val calibrationState: CalibrationState 49 | get() = CalibrationState of VRChaperone.VRChaperone_GetCalibrationState() 50 | 51 | /** 52 | * Returns the width and depth of the Play Area (formerly named Soft Bounds) in X and Z. Tracking space center(0, 0, 0) is the center of the Play Area. 53 | * 54 | * @param size 55 | */ 56 | val playAreaSize: Vec2i 57 | get() = stak { 58 | val s = it.mInt(2) 59 | nVRChaperone_GetPlayAreaSize(s.adr, (s + 1).adr) 60 | Vec2i(s[0], s[1]) 61 | } 62 | 63 | /** 64 | * Returns the 4 corner positions of the Play Area (formerly named Soft Bounds). 65 | * 66 | *

Corners are in counter-clockwise order. Standing center (0,0,0) is the center of the Play Area. It's a rectangle. 2 sides are parallel to the X axis 67 | * and 2 sides are parallel to the Z axis. Height of every corner is 0Y (on the floor).

68 | * 69 | * @param rect 70 | */ 71 | infix fun getPlayAreaRect(rect: HmdQuad): Boolean = 72 | nVRChaperone_GetPlayAreaRect(rect.adr) 73 | 74 | /** Reload Chaperone data from the .vrchap file on disk. */ 75 | fun reloadInfo() = VRChaperone_ReloadInfo() 76 | 77 | /** 78 | * Optionally give the chaperone system a hit about the color and brightness in the scene. 79 | * 80 | * @param color 81 | */ 82 | infix fun setSceneColor(color: HmdColor) = nVRChaperone_SetSceneColor(color.adr) // TODO vec3/4? 83 | 84 | /** 85 | * Get the current chaperone bounds draw color and brightness. 86 | * 87 | * @param outputColorArray 88 | * @param collisionBoundsFadeDistance 89 | * @param outputCameraColor 90 | */ 91 | fun getBoundsColor(outputColorArray: HmdColor.Buffer, collisionBoundsFadeDistance: Float, outputCameraColor: HmdColor) = 92 | nVRChaperone_GetBoundsColor(outputColorArray.adr, outputColorArray.rem, collisionBoundsFadeDistance, outputCameraColor.adr) 93 | 94 | /** Determine whether the bounds are showing right now. */ 95 | val areBoundsVisible: Boolean 96 | get() = VRChaperone_AreBoundsVisible() 97 | 98 | /** 99 | * Force the bounds to show, mostly for utilities. 100 | * 101 | * @param force 102 | */ 103 | infix fun forceBoundsVisible(force: Boolean) = VRChaperone_ForceBoundsVisible(force) 104 | 105 | override val version: String 106 | get() = "IVRChaperone_003" 107 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/lib/ivrChaperoneSetup.kt: -------------------------------------------------------------------------------- 1 | package openvr.lib 2 | 3 | import glm_.BYTES 4 | import glm_.mat4x4.Mat4 5 | import glm_.vec2.Vec2 6 | import kool.BYTES 7 | import kool.adr 8 | import kool.mInt 9 | import kool.rem 10 | import org.lwjgl.openvr.HmdMatrix34 11 | import org.lwjgl.openvr.HmdQuad 12 | import org.lwjgl.openvr.HmdVector2 13 | import org.lwjgl.openvr.VRChaperoneSetup 14 | import org.lwjgl.openvr.VRChaperoneSetup.* 15 | import org.lwjgl.system.MemoryUtil.NULL 16 | import org.lwjgl.system.MemoryUtil.memGetFloat 17 | import java.nio.ByteBuffer 18 | import java.nio.IntBuffer 19 | 20 | 21 | object vrChaperoneSetup : vrInterface { 22 | 23 | enum class ConfigFile(@JvmField val i: Int) { 24 | /** The live chaperone config, used by most applications and games */ 25 | Live(1), 26 | /** The temporary chaperone config, used to live-preview collision bounds in room setup */ 27 | Temp(2); 28 | 29 | companion object { 30 | infix fun of(i: Int) = values().first { it.i == i } 31 | } 32 | } 33 | 34 | enum class ImportFlags(@JvmField val i: Int) { 35 | 36 | BoundsOnly(0x0001); 37 | 38 | companion object { 39 | infix fun of(i: Int) = values().first { it.i == i } 40 | } 41 | } 42 | 43 | /** 44 | * Saves the current working copy to disk. 45 | * 46 | * @param configFile one of:
{@link VR#EChaperoneConfigFile_Live}{@link VR#EChaperoneConfigFile_Temp}
47 | */ 48 | infix fun commitWorkingCopy(configFile: ConfigFile): Boolean = VRChaperoneSetup_CommitWorkingCopy(configFile.i) 49 | 50 | /** 51 | * Reverts the working copy to match the live chaperone calibration. 52 | * 53 | *

To modify existing data this MUST be do WHILE getting a non-error {@code ChaperoneCalibrationStatus}. Only after this should you do gets and sets on 54 | * the existing data.

55 | */ 56 | fun revertWorkingCopy() = VRChaperoneSetup_RevertWorkingCopy() 57 | 58 | /** 59 | * Returns the width and depth of the Play Area (formerly named Soft Bounds) in X and Z from the working copy. Tracking space center (0,0,0) is the center 60 | * of the Play Area. 61 | * 62 | * @param size 63 | */ 64 | val workingPlayAreaSize: Vec2 65 | get() = stak { 66 | val s = it.mInt(2) 67 | nVRChaperoneSetup_GetWorkingPlayAreaSize(s.adr, (s + 1).adr) 68 | Vec2(s[0], s[1]) 69 | } 70 | 71 | /** 72 | * Returns the 4 corner positions of the Play Area (formerly named Soft Bounds) from the working copy. 73 | * 74 | *

Corners are in clockwise order. Tracking space center (0,0,0) is the center of the Play Area. It's a rectangle. 2 sides are parallel to the X axis and 75 | * 2 sides are parallel to the Z axis. Height of every corner is 0Y (on the floor).

76 | * 77 | * @param rect 78 | */ 79 | infix fun getWorkingPlayAreaRect(rect: HmdQuad): Boolean = nVRChaperoneSetup_GetWorkingPlayAreaRect(rect.adr) 80 | 81 | /** 82 | * Returns the number of Quads if the buffer points to null. Otherwise it returns Quads into the buffer up to the max specified from the working copy. 83 | * 84 | * @param quadsBuffer 85 | * @param quadsCount 86 | */ 87 | fun getWorkingCollisionBoundsInfo(quadsBuffer: HmdQuad.Buffer?, quadsCount: IntBuffer): Boolean = 88 | nVRChaperoneSetup_GetWorkingCollisionBoundsInfo(quadsBuffer?.adr ?: NULL, quadsCount.adr) 89 | 90 | /** 91 | * Returns the number of Quads if the buffer points to null. Otherwise it returns Quads into the buffer up to the max specified. 92 | * 93 | * @param quadsBuffer 94 | * @param quadsCount 95 | */ 96 | fun getLiveCollisionBoundsInfo(quadsBuffer: HmdQuad.Buffer?, quadsCount: IntBuffer): Boolean = 97 | nVRChaperoneSetup_GetLiveCollisionBoundsInfo(quadsBuffer?.adr ?: NULL, quadsCount.adr) 98 | 99 | /** 100 | * Returns the preferred seated position from the working copy. 101 | * 102 | * @param matSeatedZeroPoseToRawTrackingPose 103 | */ 104 | infix fun getWorkingSeatedZeroPoseToRawTrackingPose(matSeatedZeroPoseToRawTrackingPose: Mat4): Boolean = stak { 105 | val hmdMat34 = it.ncalloc(HmdMatrix34.ALIGNOF, 1, HmdMatrix34.SIZEOF) 106 | nVRChaperoneSetup_GetWorkingSeatedZeroPoseToRawTrackingPose(hmdMat34).also { 107 | hmdMat34 to matSeatedZeroPoseToRawTrackingPose 108 | } 109 | } 110 | 111 | /** 112 | * Returns the standing origin from the working copy. 113 | * 114 | * @param matStandingZeroPoseToRawTrackingPose 115 | */ 116 | infix fun getWorkingStandingZeroPoseToRawTrackingPose(matStandingZeroPoseToRawTrackingPose: Mat4): Boolean = stak { 117 | val hmdMat34 = it.ncalloc(HmdMatrix34.ALIGNOF, 1, HmdMatrix34.SIZEOF) 118 | nVRChaperoneSetup_GetWorkingStandingZeroPoseToRawTrackingPose(hmdMat34).also { 119 | hmdMat34 to matStandingZeroPoseToRawTrackingPose 120 | } 121 | } 122 | 123 | /** 124 | * Sets the Play Area in the working copy. 125 | * 126 | * @param sizeX 127 | * @param sizeZ 128 | */ 129 | infix fun setWorkingPlayAreaSize(size: Vec2) = VRChaperoneSetup_SetWorkingPlayAreaSize(size.x, size.y) 130 | 131 | /** 132 | * Sets the Collision Bounds in the working copy. 133 | * 134 | * @param quadsBuffer 135 | */ 136 | infix fun setWorkingCollisionBoundsInfo(quadsBuffer: HmdQuad.Buffer) = 137 | nVRChaperoneSetup_SetWorkingCollisionBoundsInfo(quadsBuffer.adr, quadsBuffer.rem) 138 | 139 | /** Sets the Collision Bounds in the working copy. */ 140 | infix fun setWorkingPerimeter(pointBuffer: HmdVector2.Buffer) = 141 | nVRChaperoneSetup_SetWorkingPerimeter(pointBuffer.adr, pointBuffer.rem) 142 | 143 | /** 144 | * Sets the preferred seated position in the working copy. 145 | * 146 | * @param matSeatedZeroPoseToRawTrackingPose 147 | */ 148 | infix fun setWorkingSeatedZeroPoseToRawTrackingPose(matSeatedZeroPoseToRawTrackingPose: Mat4) = 149 | nVRChaperoneSetup_SetWorkingSeatedZeroPoseToRawTrackingPose(vr.HmdMatrix34(matSeatedZeroPoseToRawTrackingPose).adr) 150 | 151 | /** 152 | * Sets the preferred standing position in the working copy. 153 | * 154 | * @param matStandingZeroPoseToRawTrackingPose 155 | */ 156 | infix fun setWorkingStandingZeroPoseToRawTrackingPose(matStandingZeroPoseToRawTrackingPose: Mat4) = 157 | nVRChaperoneSetup_SetWorkingStandingZeroPoseToRawTrackingPose(vr.HmdMatrix34(matStandingZeroPoseToRawTrackingPose).adr) 158 | 159 | /** 160 | * Tear everything down and reload it from the file on disk. 161 | * 162 | * @param configFile one of:
{@link VR#EChaperoneConfigFile_Live}{@link VR#EChaperoneConfigFile_Temp}
163 | */ 164 | infix fun reloadFromDisk(configFile: ConfigFile) = VRChaperoneSetup_ReloadFromDisk(configFile.i) 165 | 166 | /** 167 | * Returns the preferred seated position. 168 | * 169 | * @param matSeatedZeroPoseToRawTrackingPose 170 | */ 171 | infix fun getLiveSeatedZeroPoseToRawTrackingPose(matSeatedZeroPoseToRawTrackingPose: Mat4): Boolean = 172 | nVRChaperoneSetup_GetLiveSeatedZeroPoseToRawTrackingPose(vr.HmdMatrix34(matSeatedZeroPoseToRawTrackingPose).adr) 173 | 174 | fun exportLiveToBuffer(buffer: ByteBuffer?, bufferLength: IntBuffer): Boolean = 175 | VRChaperoneSetup.nVRChaperoneSetup_ExportLiveToBuffer(buffer?.adr ?: NULL, bufferLength.adr) 176 | 177 | fun importFromBufferToWorking(buffer: ByteBuffer, importFlags: Int): Boolean = 178 | nVRChaperoneSetup_ImportFromBufferToWorking(buffer.adr, importFlags) 179 | 180 | /** Shows the chaperone data in the working set to preview in the compositor.*/ 181 | fun showWorkingSetPreview() = VRChaperoneSetup_ShowWorkingSetPreview() 182 | 183 | /** Hides the chaperone data in the working set to preview in the compositor (if it was visible).*/ 184 | fun hideWorkingSetPreview() = VRChaperoneSetup_HideWorkingSetPreview() 185 | 186 | /** Fire an event that the tracking system can use to know room setup is about to begin. This lets the tracking 187 | * system make any last minute adjustments that should be incorporated into the new setup. If the user is adjusting 188 | * live in HMD using a tweak tool, keep in mind that calling this might cause the user to see the room jump. */ 189 | fun roomSetupStarting() = VRChaperoneSetup_RoomSetupStarting() 190 | 191 | override val version: String 192 | get() = "IVRChaperoneSetup_006" 193 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/lib/ivrDebug.kt: -------------------------------------------------------------------------------- 1 | package openvr.lib 2 | 3 | import kool.adr 4 | import kool.asciiAdr 5 | import kool.set 6 | import org.lwjgl.openvr.VRDebug 7 | import org.lwjgl.openvr.VRDebug.* 8 | import org.lwjgl.system.MemoryUtil 9 | import org.lwjgl.system.MemoryUtil.NULL 10 | import org.lwjgl.system.MemoryUtil.memASCII 11 | 12 | object vrDebug : vrInterface { 13 | 14 | enum class Error { 15 | Success, BadParameter; 16 | 17 | val i = ordinal 18 | 19 | companion object { 20 | infix fun of(i: Int) = Error.values().first { it.i == i } 21 | } 22 | } 23 | 24 | val pError = MemoryUtil.memCallocInt(1) 25 | 26 | var error: Error 27 | get() = Error of pError[0] 28 | set(value) { 29 | pError[0] = value.i 30 | } 31 | 32 | 33 | /** Create a vr profiler discrete event (point) 34 | * The event will be associated with the message provided in pchMessage, and the current 35 | * time will be used as the event timestamp. */ 36 | fun emitVrProfilerEvent(message: String): Error = 37 | Error of stak.asciiAdr(message) { nVRDebug_EmitVrProfilerEvent(it) } 38 | 39 | /** Create an vr profiler duration event (line) 40 | * The current time will be used as the timestamp for the start of the line. 41 | * On success, pHandleOut will contain a handle valid for terminating this event. */ 42 | fun beginVrProfilerEvent(): VrProfilerEventHandle = 43 | stak.longAdr { pError[0] = nVRDebug_BeginVrProfilerEvent(it) } 44 | 45 | /** Terminate a vr profiler event 46 | * The event associated with hHandle will be considered completed when this method is called. 47 | * The current time will be used assocaited to the termination time of the event, and 48 | * pchMessage will be used as the event title. */ 49 | fun finishVrProfilerEvent(handle: VrProfilerEventHandle, message: String): Error = 50 | Error of stak.asciiAdr(message) { nVRDebug_FinishVrProfilerEvent(handle, it) } 51 | 52 | /** Sends a request to the driver for the specified device and returns the response. The maximum response size is 32k, 53 | * but this method can be called with a smaller buffer. If the response exceeds the size of the buffer, it is truncated. 54 | * The size of the response including its terminating null is returned. */ 55 | fun driverDebugRequest(deviceIndex: TrackedDeviceIndex, request: String): String = 56 | stak { s -> 57 | val pRequest = s.asciiAdr(request) 58 | val responseSize = nVRDebug_DriverDebugRequest(deviceIndex, pRequest, NULL, 0) 59 | val pResponse = s.nmalloc(1, responseSize) 60 | nVRDebug_DriverDebugRequest(deviceIndex, pRequest, pResponse, responseSize) 61 | memASCII(pRequest, responseSize - 1) 62 | } 63 | 64 | override val version: String 65 | get() = "IVRDebug_001" 66 | } 67 | 68 | /** Handle for vr profiler events */ 69 | typealias VrProfilerEventHandle = Long -------------------------------------------------------------------------------- /src/main/kotlin/openvr/lib/ivrDriverManager.kt: -------------------------------------------------------------------------------- 1 | package openvr.lib 2 | 3 | import kool.adr 4 | import kool.asciiAdr 5 | import org.lwjgl.openvr.VRDriverManager.* 6 | import org.lwjgl.system.MemoryUtil.NULL 7 | import org.lwjgl.system.MemoryUtil.memASCII 8 | 9 | object vrDriverManager : vrInterface { 10 | 11 | val driverCount: Int 12 | get() = VRDriverManager_GetDriverCount() 13 | 14 | /** @return the length of the number of bytes necessary to hold this string including the trailing null */ 15 | infix fun getDriverName(driver: DriverId): String = 16 | stak { 17 | val bufferSize = nVRDriverManager_GetDriverName(driver, NULL, 0) 18 | val value = it.malloc(bufferSize) 19 | val result = nVRDriverManager_GetDriverName(driver, value.adr, bufferSize) 20 | memASCII(value, result - 1) 21 | } 22 | 23 | /** 24 | * Returns the property container handle for the specified driver. 25 | * 26 | * @param driverName the driver name 27 | */ 28 | infix fun getDriverHandle(driverName: String): DriverHandle = 29 | stak { nVRDriverManager_GetDriverHandle(it.asciiAdr(driverName)) } 30 | 31 | fun isEnabled(driver: Int): Boolean = 32 | VRDriverManager_IsEnabled(driver) 33 | 34 | override val version: String 35 | get() = "IVRDriverManager_001" 36 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/lib/ivrExtendedDisplay.kt: -------------------------------------------------------------------------------- 1 | package openvr.lib 2 | 3 | import glm_.vec2.Vec2i 4 | import kool.adr 5 | import kool.mInt 6 | import org.lwjgl.openvr.VRExtendedDisplay.* 7 | import org.lwjgl.system.MemoryUtil.memGetInt 8 | import java.nio.IntBuffer 9 | 10 | object vrExtendedDisplay : vrInterface { 11 | 12 | /** Size and position that the window needs to be on the VR display. */ 13 | fun getWindowBounds(pos: Vec2i, size: Vec2i) = 14 | stak { 15 | val b = it.mInt(4) 16 | nVRExtendedDisplay_GetWindowBounds(b.adr, (b + 1).adr, (b + 2).adr, (b + 3).adr) 17 | pos.put(b[0], b[1]) 18 | size.put(b[2], b[3]) 19 | } 20 | 21 | /** Gets the viewport in the frame buffer to draw the output of the distortion into. */ 22 | fun getEyeOutputViewport(eye: VREye, pos: Vec2i, size: Vec2i) = 23 | stak { 24 | val v = it.mInt(4) 25 | nVRExtendedDisplay_GetEyeOutputViewport(eye.i, v.adr, (v + 1).adr, (v + 2).adr, (v + 3).adr) 26 | pos.put(v[0], v[1]) 27 | size.put(v[2], v[3]) 28 | } 29 | 30 | /** 31 | *

D3D10/11 Only

32 | * 33 | *

Returns the adapter index and output index that the user should pass into {@code EnumAdapters} and {@code EnumOutputs} to create the device and swap 34 | * chain in DX10 and DX11. If an error occurs both indices will be set to -1.

35 | */ 36 | fun getDXGIOutputInfo(adapterIndex: IntBuffer, adapterOutputIndex: IntBuffer) = 37 | nVRExtendedDisplay_GetDXGIOutputInfo(adapterIndex.adr, adapterOutputIndex.adr) 38 | 39 | override val version: String 40 | get() = "IVRExtendedDisplay_001" 41 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/lib/ivrIOBuffer.kt: -------------------------------------------------------------------------------- 1 | package openvr.lib 2 | 3 | import kool.adr 4 | import kool.asciiAdr 5 | import kool.rem 6 | import org.lwjgl.openvr.VRIOBuffer.* 7 | import org.lwjgl.system.MemoryUtil.NULL 8 | import java.nio.ByteBuffer 9 | import java.nio.IntBuffer 10 | import java.nio.LongBuffer 11 | 12 | object vrIOBuffer : vrInterface { 13 | 14 | const val invalidHandle = NULL 15 | 16 | enum class Error(@JvmField val i: Int) { 17 | Success(0), 18 | OperationFailed(100), 19 | InvalidHandle(101), 20 | InvalidArgument(102), 21 | PathExists(103), 22 | PathDoesNotExist(104), 23 | Permission(105); 24 | 25 | companion object { 26 | infix fun of(i: Int) = values().first { it.i == i } 27 | } 28 | } 29 | 30 | enum class Mode(@JvmField val i: Int) { 31 | Read(0x0001), 32 | Write(0x0002), 33 | Create(0x0200); 34 | 35 | companion object { 36 | infix fun of(i: Int) = values().first { it.i == i } 37 | } 38 | } 39 | 40 | /** 41 | * Opens an existing or creates a new {@code IOBuffer} of {@code unSize} bytes. 42 | * 43 | * @param mode one of:
{@link VR#EIOBufferMode_IOBufferMode_Read}{@link VR#EIOBufferMode_IOBufferMode_Write}
{@link VR#EIOBufferMode_IOBufferMode_Create}
44 | * @param buffer ~ IOBufferHandle * 45 | */ 46 | fun open(path: String, mode: Mode, elementSize: Int, elements: Int, buffer: LongBuffer): Error = 47 | stak { Error of nVRIOBuffer_Open(it.asciiAdr(path), mode.i, elementSize, elements, buffer.adr) } 48 | 49 | /** Closes a previously opened or created buffer. */ 50 | infix fun close(buffer: IOBufferHandle): Error = Error of VRIOBuffer_Close(buffer) 51 | 52 | /** Reads up to {@code unBytes} from buffer into {@code *dst}, returning number of bytes read in {@code *read} */ 53 | fun read(buffer: IOBufferHandle, dst: ByteBuffer, read: IntBuffer): Error = 54 | Error of nVRIOBuffer_Read(buffer, dst.adr, dst.rem, read.adr) 55 | 56 | /** Writes {@code unBytes} of data from {@code *src} into a buffer. */ 57 | fun write(buffer: IOBufferHandle, src: ByteBuffer): Error = Error of nVRIOBuffer_Write(buffer, src.adr, src.rem) 58 | 59 | /** Retrieves the property container of a buffer. */ 60 | infix fun propertyContainer(buffer: IOBufferHandle): PropertyContainerHandle = VRIOBuffer_PropertyContainer(buffer) 61 | 62 | /** inexpensively checks for readers to allow writers to fast-fail potentially expensive copies and writes. */ 63 | fun hasReaders(buffer: IOBufferHandle): Boolean = VRIOBuffer_HasReaders(buffer) 64 | 65 | override val version: String 66 | get() = "IVRIOBuffer_002" 67 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/lib/ivrNotifications.kt: -------------------------------------------------------------------------------- 1 | package openvr.lib 2 | 3 | import kool.adr 4 | import kool.asciiAdr 5 | import org.lwjgl.openvr.NotificationBitmap 6 | import org.lwjgl.openvr.VRNotifications.VRNotifications_RemoveNotification 7 | import org.lwjgl.openvr.VRNotifications.nVRNotifications_CreateNotification 8 | import org.lwjgl.system.MemoryUtil.NULL 9 | import java.nio.IntBuffer 10 | 11 | object vrNotifications : vrInterface { 12 | 13 | const val textMaxSize = 256 14 | 15 | /** error codes for notifications */ 16 | enum class Error(@JvmField val i: Int) { 17 | 18 | OK(0), 19 | InvalidNotificationId(100), 20 | NotificationQueueFull(101), 21 | InvalidOverlayHandle(102), 22 | SystemWithUserValueAlreadyExists(103); 23 | 24 | companion object { 25 | infix fun of(i: Int) = values().first { it.i == i } 26 | } 27 | } 28 | 29 | /** Be aware that the notification value is used as 'priority' to pick the next notification */ 30 | enum class Type(@JvmField val i: Int) { 31 | 32 | /** Transient notifications are automatically hidden after a period of time set by the user. 33 | * They are used for things like information and chat messages that do not require user interaction. */ 34 | Transient(0), 35 | 36 | /** Persistent notifications are shown to the user until they are hidden by calling RemoveNotification(). 37 | * They are used for things like phone calls and alarms that require user interaction. */ 38 | Persistent(1), 39 | 40 | /** System notifications are shown no matter what. It is expected), that the ::userValue is used as ID. 41 | * If there is already a system notification in the queue with that ID it is not accepted into the queue to 42 | * prevent spamming with system notification */ 43 | Transient_SystemWithUserValue(2); 44 | 45 | companion object { 46 | infix fun of(i: Int) = values().first { it.i == i } 47 | } 48 | } 49 | 50 | enum class Style(@JvmField val i: Int) { 51 | 52 | /** Creates a notification with minimal external styling. */ 53 | None(0), 54 | 55 | /** Used for notifications about overlay-level status. In Steam this is used for events like downloads completing. */ 56 | Application(100), 57 | 58 | /** Used for notifications about contacts that are unknown or not available. In Steam this is used for friend 59 | * invitations and offline friends. */ 60 | Contact_Disabled(200), 61 | 62 | /** Used for notifications about contacts that are available but inactive. In Steam this is used for friends that 63 | * are online but not playing a game. */ 64 | Contact_Enabled(201), 65 | 66 | /** Used for notifications about contacts that are available and active. In Steam this is used for friends that 67 | * are online and currently running a game. */ 68 | Contact_Active(202); 69 | 70 | companion object { 71 | infix fun of(i: Int) = values().first { it.i == i } 72 | } 73 | } 74 | 75 | /** 76 | * Create a notification and enqueue it to be shown to the user. 77 | * 78 | *

An overlay handle is required to create a notification, as otherwise it would be impossible for a user to act on it. To create a two-line notification, 79 | * use a line break ('\n') to split the text into two lines. The {@code image} argument may be {@code NULL}, in which case the specified overlay's icon will be 80 | * used instead.

81 | * 82 | * @param overlayHandle 83 | * @param userValue 84 | * @param type one of:
{@link VR#EVRNotificationType_Transient}{@link VR#EVRNotificationType_Persistent}
{@link VR#EVRNotificationType_Transient_SystemWithUserValue}
85 | * @param text 86 | * @param style one of:
{@link VR#EVRNotificationStyle_None}{@link VR#EVRNotificationStyle_Application}
{@link VR#EVRNotificationStyle_Contact_Disabled}{@link VR#EVRNotificationStyle_Contact_Enabled}
{@link VR#EVRNotificationStyle_Contact_Active}
87 | * @param image 88 | * @param notificationId ~VRNotificationId 89 | */ 90 | fun createNotification(overlayHandle: VROverlayHandle, userValue: Long, type: Type, text: String, 91 | style: Style, image: NotificationBitmap?, notificationId: IntBuffer): Error = 92 | stak { 93 | Error of nVRNotifications_CreateNotification(overlayHandle, userValue, type.i, it.asciiAdr(text), style.i, image?.adr 94 | ?: NULL, notificationId.adr) 95 | } 96 | 97 | /** 98 | * Destroy a notification, hiding it first if it currently shown to the user. 99 | * 100 | * @param notificationId 101 | */ 102 | fun removeNotification(notificationId: VRNotificationId): Error = 103 | Error of VRNotifications_RemoveNotification(notificationId) 104 | 105 | override val version: String 106 | get() = "IVRNotifications_002" 107 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/lib/ivrResources.kt: -------------------------------------------------------------------------------- 1 | package openvr.lib 2 | 3 | import kool.asciiAdr 4 | import org.lwjgl.openvr.VRResources.nVRResources_GetResourceFullPath 5 | import org.lwjgl.openvr.VRResources.nVRResources_LoadSharedResource 6 | import org.lwjgl.system.MemoryUtil.NULL 7 | 8 | 9 | object vrResources : vrInterface { 10 | 11 | /** 12 | * Loads the specified resource into the provided buffer if large enough. 13 | * 14 | * @return the size in bytes of the buffer required to hold the specified resource 15 | */ 16 | infix fun loadSharedResource(resourceName: String): String = 17 | stak { s -> 18 | val resourceNameEncoded = s.asciiAdr(resourceName) 19 | val bufferLen = nVRResources_LoadSharedResource(resourceNameEncoded, NULL, 0) 20 | s.asciiAdr(bufferLen) { 21 | nVRResources_LoadSharedResource(resourceNameEncoded, it, bufferLen) 22 | } 23 | } 24 | 25 | /** 26 | * Provides the full path to the specified resource. Resource names can include named directories for drivers and other things, and this resolves all of 27 | * those and returns the actual physical path. {@code resourceTypeDirectory} is the subdirectory of resources to look in. 28 | */ 29 | fun getResourceFullPath(resourceName: String, resourceTypeDirectory: String): String = 30 | stak { s -> 31 | val resourceNameEncoded = s.asciiAdr(resourceName) 32 | val resourceTypeDirectoryEncoded = s.asciiAdr(resourceTypeDirectory) 33 | val bufferLen = nVRResources_GetResourceFullPath(resourceNameEncoded, resourceTypeDirectoryEncoded, NULL, 0) 34 | s.asciiAdr(bufferLen) { 35 | nVRResources_GetResourceFullPath(resourceNameEncoded, resourceTypeDirectoryEncoded, it, bufferLen) 36 | } 37 | } 38 | 39 | override val version: String 40 | get() = "IVRResources_001" 41 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/lib/ivrScreenshots.kt: -------------------------------------------------------------------------------- 1 | package openvr.lib 2 | 3 | import kool.adr 4 | import kool.asciiAdr 5 | import kool.rem 6 | import kool.set 7 | import org.lwjgl.openvr.VRScreenshots.* 8 | import org.lwjgl.system.MemoryUtil.* 9 | import java.nio.IntBuffer 10 | 11 | 12 | object vrScreenshots : vrInterface { 13 | 14 | /** Errors that can occur with the VR compositor */ 15 | enum class Error(@JvmField val i: Int) { 16 | 17 | None(0), 18 | RequestFailed(1), 19 | IncompatibleVersion(100), 20 | NotFound(101), 21 | BufferTooSmall(102), 22 | ScreenshotAlreadyInProgress(108); 23 | 24 | companion object { 25 | infix fun of(i: Int) = values().first { it.i == i } 26 | } 27 | } 28 | 29 | val pError = memCallocInt(1) 30 | 31 | var error: Error 32 | get() = Error of pError[0] 33 | set(value) { 34 | pError[0] = value.i 35 | } 36 | 37 | enum class Type(@JvmField val i: Int) { 38 | 39 | None(0), 40 | /** left eye only */ 41 | Mono(1), 42 | Stereo(2), 43 | Cubemap(3), 44 | MonoPanorama(4), 45 | StereoPanorama(5); 46 | 47 | companion object { 48 | infix fun of(i: Int) = values().first { it.i == i } 49 | } 50 | } 51 | 52 | enum class PropertyFilenames(@JvmField val i: Int) { 53 | 54 | Preview(0), 55 | VR(1); 56 | 57 | companion object { 58 | infix fun of(i: Int) = values().first { it.i == i } 59 | } 60 | } 61 | 62 | /** 63 | * Request a screenshot of the requested type. 64 | * 65 | *

A request of the {@link VR#EVRScreenshotType_VRScreenshotType_Stereo} type will always work. Other types will depend on the underlying application support.

66 | * 67 | *

The first file name is for the preview image and should be a regular screenshot (ideally from the left eye). The second is the VR screenshot in the 68 | * correct format. They should be in the same aspect ratio.

69 | * 70 | *

Note that the VR dashboard will call this function when the user presses the screenshot binding (currently System Button + Trigger). If Steam is 71 | * running, the destination file names will be in %TEMP% and will be copied into Steam's screenshot library for the running application once 72 | * {@link #VRScreenshots_SubmitScreenshot SubmitScreenshot} is called. If Steam is not running, the paths will be in the user's documents folder under Documents\SteamVR\Screenshots. Other VR 73 | * applications can call this to initate a screenshot outside of user control. The destination file names do not need an extension, will be replaced with 74 | * the correct one for the format which is currently .png.

75 | * 76 | * @param type one of:
{@link VR#EVRScreenshotType_VRScreenshotType_None}
{@link VR#EVRScreenshotType_VRScreenshotType_Mono}
{@link VR#EVRScreenshotType_VRScreenshotType_Stereo}
{@link VR#EVRScreenshotType_VRScreenshotType_Cubemap}
{@link VR#EVRScreenshotType_VRScreenshotType_MonoPanorama}
{@link VR#EVRScreenshotType_VRScreenshotType_StereoPanorama}
77 | * @param outScreenshotHandle ~ScreenshotHandle * 78 | */ 79 | fun requestScreenshot(outScreenshotHandle: VRScreenshotHandleBuffer, type: Type, previewFilename: String, vrFilename: String): Error = 80 | stak { Error of nVRScreenshots_RequestScreenshot(outScreenshotHandle.adr, type.i, it.asciiAdr(previewFilename), it.asciiAdr(vrFilename)) } 81 | 82 | /** 83 | * Called by the running VR application to indicate that it wishes to be in charge of screenshots. If the application does not call this, the Compositor 84 | * will only support {@link VR#EVRScreenshotType_VRScreenshotType_Stereo} screenshots that will be captured without notification to the running app. 85 | * 86 | *

Once hooked your application will receive a {@link VR#EVREventType_VREvent_RequestScreenshot} event when the user presses the buttons to take a screenshot.

87 | * @param supportedTypes ~Type * 88 | */ 89 | infix fun hookScreenshot(supportedTypes: IntBuffer): Error = 90 | Error of nVRScreenshots_HookScreenshot(supportedTypes.adr, supportedTypes.rem) 91 | 92 | /** When your application receives a {@link VR#EVREventType_VREvent_RequestScreenshot} event, call these functions to get the details of the screenshot request. 93 | * Note: Multi-thread unsafe if not passing a pError and reading it from the class property. */ 94 | fun getScreenshotPropertyType(screenshotHandle: ScreenshotHandle, pErr: VRScreenshotErrorBuffer = pError): Type = 95 | Type of nVRScreenshots_GetScreenshotPropertyType(screenshotHandle, pErr.adr) 96 | 97 | /** 98 | * Get the filename for the preview or vr image (see {@code EScreenshotPropertyFilenames}). 99 | * 100 | * @param filenameType one of:
{@link VR#EVRScreenshotPropertyFilenames_VRScreenshotPropertyFilenames_Preview}
{@link VR#EVRScreenshotPropertyFilenames_VRScreenshotPropertyFilenames_VR}
101 | * 102 | * @return the filename 103 | */ 104 | fun getScreenshotPropertyFilename(screenshotHandle: ScreenshotHandle, filenameType: PropertyFilenames, error: IntBuffer): String = 105 | stak { s -> 106 | val filenameLen = nVRScreenshots_GetScreenshotPropertyFilename(screenshotHandle, filenameType.i, NULL, 0, error.adr) 107 | s.asciiAdr(filenameLen) { 108 | nVRScreenshots_GetScreenshotPropertyFilename(screenshotHandle, filenameType.i, it, filenameLen, error.adr) 109 | } 110 | } 111 | 112 | /** 113 | * Call this if the application is taking the screen shot will take more than a few ms processing. This will result in an overlay being presented that 114 | * shows a completion bar. 115 | */ 116 | fun updateScreenshotProgress(screenshotHandle: ScreenshotHandle, progress: Float): Error = 117 | Error of VRScreenshots_UpdateScreenshotProgress(screenshotHandle, progress) 118 | 119 | /** 120 | * Tells the compositor to take an internal screenshot of type {@link VR#EVRScreenshotType_VRScreenshotType_Stereo}. It will take the current submitted scene 121 | * textures of the running application and write them into the preview image and a side-by-side file for the VR image. 122 | * 123 | *

This is similiar to request screenshot, but doesn't ever talk to the application, just takes the shot and submits.

124 | * 125 | * @param outScreenshotHandle ~ScreenshotHandle * 126 | */ 127 | fun takeStereoScreenshot(outScreenshotHandle: IntBuffer, previewFilename: String, vrFilename: String): Error = 128 | stak { Error of nVRScreenshots_TakeStereoScreenshot(outScreenshotHandle.adr, it.asciiAdr(previewFilename), it.asciiAdr(vrFilename)) } 129 | 130 | /** 131 | * Submit the completed screenshot. 132 | * 133 | *

If Steam is running this will call into the Steam client and upload the screenshot to the screenshots section of the library for the running 134 | * application. If Steam is not running, this function will display a notification to the user that the screenshot was taken. The paths should be full 135 | * paths with extensions.

136 | * 137 | *

File paths should be absolute including extensions.

138 | * 139 | *

{@code screenshotHandle} can be {@link VR#k_unScreenshotHandleInvalid} if this was a new shot taking by the app to be saved and not initiated by a user 140 | * (achievement earned or something).

141 | * 142 | * @param type one of:
{@link VR#EVRScreenshotType_VRScreenshotType_None}
{@link VR#EVRScreenshotType_VRScreenshotType_Mono}
{@link VR#EVRScreenshotType_VRScreenshotType_Stereo}
{@link VR#EVRScreenshotType_VRScreenshotType_Cubemap}
{@link VR#EVRScreenshotType_VRScreenshotType_MonoPanorama}
{@link VR#EVRScreenshotType_VRScreenshotType_StereoPanorama}
143 | */ 144 | fun submitScreenshot(screenshotHandle: ScreenshotHandle, type: Type, sourcePreviewFilename: String, sourceVRFilename: String): Error = 145 | stak { Error of nVRScreenshots_SubmitScreenshot(screenshotHandle, type.i, it.asciiAdr(sourcePreviewFilename), it.asciiAdr(sourceVRFilename)) } 146 | 147 | override val version: String 148 | get() = "IVRScreenshots_001" 149 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/lib/ivrSpatialAnchors.kt: -------------------------------------------------------------------------------- 1 | package openvr.lib 2 | 3 | import glm_.BYTES 4 | import kool.adr 5 | import kool.asciiAdr 6 | import kool.intAdr 7 | import kool.set 8 | import org.lwjgl.openvr.SpatialAnchorPose 9 | import org.lwjgl.openvr.VRSpatialAnchors.* 10 | import org.lwjgl.system.MemoryUtil.* 11 | 12 | object vrSpatialAnchors : vrInterface { 13 | 14 | const val invalidHandle = 0 15 | 16 | const val maxDescriptorSize = 32 17 | 18 | enum class Error { 19 | Success, 20 | Internal, 21 | UnknownHandle, 22 | ArrayTooSmall, 23 | InvalidDescriptorChar, 24 | NotYetAvailable, 25 | NotAvailableInThisUniverse, 26 | PermanentlyUnavailable, 27 | WrongDriver, 28 | DescriptorTooLong, 29 | Unknown, 30 | NoRoomCalibration, 31 | InvalidArgument, 32 | UnknownDriver; 33 | 34 | @JvmField 35 | val i = ordinal 36 | 37 | companion object { 38 | infix fun of(i: Int) = values().first { it.i == i } 39 | } 40 | } 41 | 42 | val pError = memCallocInt(1) 43 | 44 | var error: Error 45 | get() = Error of pError[0] 46 | set(value) { 47 | pError[0] = value.i 48 | } 49 | 50 | /** 51 | * JVM custom 52 | * 53 | * Returns a handle for an spatial anchor described by "descriptor". On success, {@code pHandle} will contain a handle valid for this session. Caller can 54 | * wait for an event or occasionally poll {@link #VRSpatialAnchors_GetSpatialAnchorPose GetSpatialAnchorPose} to find the virtual coordinate associated with this anchor. 55 | * 56 | * Note: Multi-thread unsafe if not passing a pError and reading it from the class property. 57 | */ 58 | @JvmOverloads 59 | fun createSpatialAnchorFromDescriptor(descriptor: CharSequence, pErr: VRSpatialAnchorErrorBuffer = pError): SpatialAnchorHandle = 60 | stak { s -> 61 | s.intAdr { 62 | pErr[0] = nVRSpatialAnchors_CreateSpatialAnchorFromDescriptor(s.asciiAdr(descriptor), it) 63 | } 64 | } 65 | 66 | /** 67 | * JVM custom 68 | * 69 | * Returns a handle for an new spatial anchor at {@code pose}. 70 | * 71 | *

On success, {@code pHandle} will contain a handle valid for this session. Caller can wait for an event or occasionally poll 72 | * {@link #VRSpatialAnchors_GetSpatialAnchorDescriptor GetSpatialAnchorDescriptor} to find the permanent descriptor for this pose. The result of {@link #VRSpatialAnchors_GetSpatialAnchorPose GetSpatialAnchorPose} may evolve from this initial 73 | * position if the driver chooses to update it. The anchor will be associated with the driver that provides {@code deviceIndex}, and the driver may use 74 | * that specific device as a hint for how to best create the anchor. The {@code origin} must match whatever tracking origin you are working in 75 | * (seated/standing/raw).

76 | * 77 | *

This should be called when the user is close to (and ideally looking at/interacting with) the target physical location. At that moment, the driver will 78 | * have the most information about how to recover that physical point in the future, and the quality of the anchor (when the descriptor is re-used) will 79 | * be highest. The caller may decide to apply offsets from this initial pose, but is advised to stay relatively close to the original pose location for 80 | * highest fidelity.

81 | * 82 | * Note: Multi-thread unsafe if not passing a pError and reading it from the class property. 83 | */ 84 | @JvmOverloads 85 | fun createSpatialAnchorFromPose(deviceIndex: TrackedDeviceIndex, origin: TrackingUniverseOrigin, pose: SpatialAnchorPose, pErr: VRSpatialAnchorErrorBuffer = pError): SpatialAnchorHandle = 86 | stak { s -> 87 | s.intAdr { 88 | pErr[0] = nVRSpatialAnchors_CreateSpatialAnchorFromPose(deviceIndex, origin.i, pose.adr, it) 89 | } 90 | } 91 | 92 | /** 93 | * Get the pose for a given handle. TODO more convenient? 94 | * 95 | *

This is intended to be cheap enough to call every frame (or fairly often) so that the driver can refine this position when it has more information 96 | * available.

97 | */ 98 | fun getSpatialAnchorPose(handle: SpatialAnchorHandle, origin: TrackingUniverseOrigin, poseOut: SpatialAnchorPose): Error = 99 | Error of nVRSpatialAnchors_GetSpatialAnchorPose(handle, origin.i, poseOut.adr) 100 | 101 | /** 102 | * JVM custom 103 | * 104 | * Get the descriptor for a given handle. 105 | * 106 | *

This will be empty for handles where the driver has not yet built a descriptor. It will be the application-supplied descriptor for previously saved 107 | * anchors that the application is requesting poses for. If the driver has called {@code UpdateSpatialAnchorDescriptor()} already in this session, it 108 | * will be the descriptor provided by the driver.

109 | * 110 | * Note: Multi-thread unsafe if not passing a pError and reading it from the class property. 111 | */ 112 | @JvmOverloads 113 | fun getSpatialAnchorDescriptor(handle: SpatialAnchorHandle, pErr: VRSpatialAnchorErrorBuffer = pError): String = 114 | stak.asciiAdr(maxDescriptorSize) { 115 | pErr[0] = nVRSpatialAnchors_GetSpatialAnchorDescriptor(handle, it, NULL) 116 | } 117 | 118 | override val version: String 119 | get() = "IVRSpatialAnchors_001" 120 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/lib/temp.kt: -------------------------------------------------------------------------------- 1 | package openvr.lib 2 | 3 | import glm_.b 4 | import glm_.i 5 | import kool.* 6 | import org.lwjgl.system.MathUtil 7 | import org.lwjgl.system.MemoryStack 8 | import org.lwjgl.system.MemoryUtil.* 9 | import java.nio.ByteBuffer 10 | 11 | typealias stak = Stack 12 | 13 | //fun bufferOfAscii(string: String, nullTerminated: Boolean = true): ByteBuffer { 14 | // val bytes = stackGet().malloc(string.length + if (nullTerminated) 1 else 0) 15 | // for (i in string.indices) 16 | // bytes[i] = string[i].b 17 | // if (nullTerminated) 18 | // bytes[string.length] = 0 19 | // return bytes 20 | //} 21 | 22 | // TODO remove and use kool's one 23 | fun MemoryStack.asciiAdr(size: Int, nullTerminated: Boolean = true, block: (Adr) -> R): String { 24 | val adr = nmalloc(1, size + nullTerminated.i) 25 | block(adr) 26 | return memASCII(adr, size) 27 | } 28 | 29 | // TODO -> uno 30 | const val NUL = '\u0000' 31 | 32 | /** It mallocs, passes the address and reads the null terminated string 33 | * Getter */ 34 | inline fun Stack.asciiAdr(maxSize: Int, block: (Adr) -> R): String = with { 35 | val adr = it.nmalloc(1, maxSize) 36 | block(adr) 37 | memASCII(adr, strlen64NT1(adr, maxSize)) 38 | } 39 | 40 | /** It malloc the buffer, passes it and reads the null terminated string 41 | * Getter */ 42 | inline fun Stack.asciiBuffer(maxSize: Int, block: (ByteBuffer) -> R): String = with { 43 | val buf = it.malloc(1, maxSize) 44 | block(buf) 45 | memASCII(buf.adr, maxSize) 46 | } 47 | 48 | fun strlen64NT1(address: Long, maxLength: Int): Int { 49 | var i = 0 50 | if (8 <= maxLength) { 51 | val misalignment = address.toInt() and 7 52 | if (misalignment != 0) { // Align to 8 bytes 53 | val len = 8 - misalignment 54 | while (i < len) { 55 | if (UNSAFE.getByte(null, address + i).toInt() == 0) 56 | return i 57 | i++ 58 | } 59 | } 60 | // Aligned longs for performance 61 | while (i <= maxLength - 8) { 62 | if (MathUtil.mathHasZeroByte(UNSAFE.getLong(null, address + i))) 63 | break 64 | i += 8 65 | } 66 | } 67 | // Tail 68 | while (i < maxLength) { 69 | if (UNSAFE.getByte(null, address + i).toInt() == 0) 70 | break 71 | i++ 72 | } 73 | return i 74 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/lib/typealias.kt: -------------------------------------------------------------------------------- 1 | package openvr.lib 2 | 3 | import org.lwjgl.system.MemoryUtil.NULL 4 | import java.nio.IntBuffer 5 | import java.nio.LongBuffer 6 | 7 | 8 | /** A handle for a spatial anchor. This handle is only valid during the session it was created in. 9 | * Anchors that live beyond one session should be saved by their string descriptors. */ 10 | typealias SpatialAnchorHandle = Int 11 | 12 | typealias glSharedTextureHandle = Long 13 | typealias glInt = Int 14 | typealias glUInt = Int 15 | 16 | 17 | // Handle to a shared texture (HANDLE on Windows obtained using OpenSharedResource). 18 | typealias SharedTextureHandle = Long 19 | 20 | const val INVALID_SHARED_TEXTURE_HANDLE: SharedTextureHandle = NULL 21 | 22 | 23 | typealias DriverId = Int 24 | 25 | /** Used to pass device IDs to API calls */ 26 | typealias TrackedDeviceIndex = Int 27 | 28 | 29 | typealias WebConsoleHandle = Long 30 | 31 | const val INVALID_WEB_CONSOLE_HANDLE: WebConsoleHandle = NULL 32 | 33 | 34 | // Refers to a single container of properties 35 | typealias PropertyContainerHandle = Long 36 | 37 | typealias PropertyTypeTag = Int 38 | 39 | 40 | typealias DriverHandle = PropertyContainerHandle 41 | 42 | 43 | /** used to refer to a single VR overlay */ 44 | typealias VROverlayHandle = Long 45 | typealias VROverlayHandleBuffer = LongBuffer 46 | 47 | 48 | /** Type used for referring to bones by their index */ 49 | typealias BoneIndex = Int 50 | 51 | 52 | typealias TrackedCameraHandle = Long 53 | 54 | // Screenshot types 55 | typealias ScreenshotHandle = Int 56 | 57 | 58 | // ivrsystem.h 59 | 60 | // ivrapplications.h 61 | 62 | // ivrsettings.h 63 | 64 | // ivrchaperone.h 65 | 66 | // ivrchaperonesetup.h 67 | 68 | // ivrcompositor.h 69 | 70 | // ivrnotifications.h 71 | 72 | typealias VRNotificationId = Int 73 | 74 | // ivroverlay.h 75 | 76 | // ivrrendermodels.h 77 | 78 | typealias VRComponentProperties = Int 79 | 80 | /** Session unique texture identifier. Rendermodels which share the same texture will have the same id. 81 | IDs <0 denote the texture is not present */ 82 | typealias TextureId = Int 83 | 84 | const val INVALID_TEXTURE_ID: TextureId = -1 85 | 86 | // ivrextendeddisplay.h 87 | 88 | // ivrtrackedcamera.h 89 | 90 | // ivrscreenshots.h 91 | 92 | // ivrresources.h 93 | 94 | // ivrdrivermanager.h 95 | 96 | // ivrinput.h 97 | 98 | typealias VRActionHandle = Long 99 | typealias VRActionSetHandle = Long 100 | typealias VRInputValueHandle = Long 101 | 102 | // ivriobuffer.h 103 | 104 | typealias IOBufferHandle = Long 105 | 106 | // ivrspatialanchors.h 107 | 108 | // Utils 109 | 110 | typealias VRActionHandleBuffer = LongBuffer 111 | typealias VRActionSetHandleBuffer = LongBuffer 112 | typealias VRInputValueHandleBuffer = LongBuffer 113 | typealias VRScreenshotHandleBuffer = LongBuffer 114 | 115 | typealias VRSkeletalTransformSpaceBuffer = IntBuffer 116 | 117 | // JVM custom, Errors 118 | 119 | typealias VRApplicationErrorBuffer = IntBuffer 120 | typealias VROverlayErrorBuffer = IntBuffer 121 | typealias VRVRRenderModelsErrorBuffer = IntBuffer 122 | typealias VRScreenshotErrorBuffer = IntBuffer 123 | typealias VRSettingsErrorBuffer = IntBuffer 124 | typealias VRSpatialAnchorErrorBuffer = IntBuffer 125 | typealias VRInitErrorBuffer = IntBuffer 126 | typealias VRInputErrorBuffer = IntBuffer 127 | typealias TrackedPropertyErrorBuffer = IntBuffer 128 | typealias VRTrackedCameraErrorBuffer = IntBuffer 129 | 130 | typealias TextureTypeBuffer = IntBuffer 131 | typealias ColorSpaceBuffer = IntBuffer 132 | typealias VROverlayInputBuffer = IntBuffer -------------------------------------------------------------------------------- /src/main/kotlin/openvr/plugin/Controller.kt: -------------------------------------------------------------------------------- 1 | package openvr.plugin 2 | 3 | import glm_.glm 4 | import glm_.vec2.Vec2 5 | import glm_.vec3.Vec3 6 | import openvr.lib.* 7 | import org.lwjgl.openvr.TrackedDevicePose 8 | import org.lwjgl.openvr.VRControllerState 9 | 10 | 11 | @Deprecated("Use the new input system https://github.com/ValveSoftware/openvr/wiki/SteamVR-SteamVR_Input") 12 | object Controller { 13 | 14 | enum class ButtonMask(val i: Long) { 15 | /** reserved */ 16 | System(1L shl VRButtonId.System.i), 17 | ApplicationMenu(1L shl VRButtonId.ApplicationMenu.i), 18 | Grip(1L shl VRButtonId.Grip.i), 19 | Axis0(1L shl VRButtonId.Axis0.i), 20 | Axis1(1L shl VRButtonId.Axis1.i), 21 | Axis2(1L shl VRButtonId.Axis2.i), 22 | Axis3(1L shl VRButtonId.Axis3.i), 23 | Axis4(1L shl VRButtonId.Axis4.i), 24 | SteamVR_Touchpad(1L shl VRButtonId.SteamVR_Touchpad.i), 25 | SteamVR_Trigger(1L shl VRButtonId.SteamVR_Trigger.i); 26 | } 27 | 28 | private val _transform = Utils.RigidTransform() 29 | 30 | var touchpadMoveCallback: ((Boolean, Vec2) -> Unit)? = null 31 | var hairTriggerMoveCallback: ((Boolean, Float) -> Unit)? = null 32 | 33 | class Device(val index: Int) { 34 | 35 | var _frameCount = -1 36 | 37 | var valid = false 38 | private set 39 | 40 | var left = true 41 | 42 | val connected get() = update().run { _pose.deviceIsConnected } 43 | val hasTracking get() = update().run { _pose.poseIsValid } 44 | 45 | val outOfRange get() = update().run { _pose.trackingResult == TrackingResult.Running_OutOfRange || _pose.trackingResult == TrackingResult.Calibrating_OutOfRange } 46 | val calibrating get() = update().run { _pose.trackingResult == TrackingResult.Calibrating_InProgress || _pose.trackingResult == TrackingResult.Calibrating_OutOfRange } 47 | val uninitialized get() = update().run { _pose.trackingResult == TrackingResult.Uninitialized } 48 | 49 | /* These values are only accurate for the last controller state change (e.g. trigger release), 50 | and by definition, will always lag behind the predicted visual poses that drive SteamVR_TrackedObjects 51 | since they are sync'd to the input timestamp that caused them to update. */ 52 | val transform get() = update().run { _transform put _pose.deviceToAbsoluteTracking } 53 | val velocity get() = update().run { _velocity.put(_pose.velocity.x, _pose.velocity.y, -_pose.velocity.z) } 54 | val angularVelocity get() = update().run { _angularVelocity(-_pose.angularVelocity.x, -_pose.angularVelocity.y, _pose.angularVelocity.z) } 55 | val state get() = update().run { _state } 56 | val prevState get() = update().run { _prevState } 57 | val pose get() = update().run { _pose } 58 | 59 | private val _velocity = Vec3() 60 | private val _angularVelocity = Vec3() 61 | private val _state = VRControllerState.calloc() 62 | private val _prevState = VRControllerState.calloc() 63 | private val _pose = TrackedDevicePose.calloc() 64 | private var prevFrameCount = -1 65 | private val prevTouchpadPos = Vec2() 66 | /** amount touchpad position must be increased or released on a single axes to change state */ 67 | var touchpadDelta = 0.1f 68 | 69 | fun update(frameCount: Int = _frameCount) { 70 | if (frameCount != prevFrameCount) { 71 | prevFrameCount = frameCount 72 | valid = vrSystem.getControllerStateWithPose(Render.trackingSpace, index, _state, _pose) 73 | if (ButtonMask.SteamVR_Touchpad.touch) 74 | touchpadMoveCallback?.let { 75 | if (prevTouchpadPos.x - _state.axis[0].x >= touchpadDelta || prevTouchpadPos.y - _state.axis[0].y >= touchpadDelta) { 76 | it(left, _state.axis[0].pos) 77 | prevTouchpadPos put _state.axis[0].pos 78 | } 79 | } 80 | updateHairTrigger() 81 | } 82 | } 83 | 84 | val ButtonMask.press get() = update().let { _state.buttonPressed has this } 85 | val ButtonMask.pressDown get() = update().let { _state.buttonPressed has this && _prevState.buttonPressed hasnt this } 86 | val ButtonMask.pressUp get() = update().let { _state.buttonPressed hasnt this && _prevState.buttonPressed has this } 87 | 88 | val VRButtonId.press get() = update().let { _state.buttonPressed has this } 89 | val VRButtonId.pressDown get() = update().let { _state.buttonPressed has this && _prevState.buttonPressed hasnt this } 90 | val VRButtonId.pressUp get() = update().let { _state.buttonPressed hasnt this && _prevState.buttonPressed has this } 91 | 92 | val ButtonMask.touch get() = update().let { _state.buttonTouched has this } 93 | val ButtonMask.touchDown get() = update().let { _state.buttonTouched has this && _prevState.buttonTouched hasnt this } 94 | val ButtonMask.touchUp get() = update().let { _state.buttonTouched hasnt this && _prevState.buttonTouched has this } 95 | 96 | val VRButtonId.touch get() = update().let { _state.buttonTouched has this } 97 | val VRButtonId.touchDown get() = update().let { _state.buttonTouched has this && _prevState.buttonTouched hasnt this } 98 | val VRButtonId.touchUp get() = update().let { _state.buttonTouched hasnt this && _prevState.buttonTouched has this } 99 | 100 | val _axis = Vec2() 101 | fun axis(buttonId: VRButtonId = VRButtonId.SteamVR_Touchpad): Vec2 { 102 | update() 103 | return _axis.apply { 104 | when (buttonId.i - VRButtonId.Axis0.i) { 105 | 0 -> put(_state.axis[0].x, _state.axis[0].y) 106 | 1 -> put(_state.axis[1].x, _state.axis[1].y) 107 | 2 -> put(_state.axis[2].x, _state.axis[2].y) 108 | 3 -> put(_state.axis[3].x, _state.axis[3].y) 109 | 4 -> put(_state.axis[4].x, _state.axis[4].y) 110 | else -> put(0) 111 | } 112 | } 113 | } 114 | 115 | fun triggerHapticPulse(durationMicroSec: Int = 500, buttonId: VRButtonId = VRButtonId.SteamVR_Touchpad) = 116 | vrSystem.triggerHapticPulse(index, buttonId.i - VRButtonId.Axis0.i, durationMicroSec) 117 | 118 | 119 | /** amount trigger must be pulled or released to change state */ 120 | var hairTriggerDelta = 0.1f 121 | private var hairTriggerLimit = 0f 122 | private var hairTriggerState = false 123 | private var hairTriggerPrevState = false 124 | 125 | fun updateHairTrigger() { 126 | hairTriggerPrevState = hairTriggerState 127 | val value = _state.axis[1].x // trigger 128 | if (hairTriggerState) { 129 | if (value < hairTriggerLimit - hairTriggerDelta || value <= 0f) 130 | hairTriggerState = false 131 | } else if (value > hairTriggerLimit + hairTriggerDelta || value >= 1f) 132 | hairTriggerState = true 133 | hairTriggerLimit = if (hairTriggerState) glm.max(hairTriggerLimit, value) else glm.min(hairTriggerLimit, value) 134 | } 135 | 136 | val hairTrigger get() = update().let { hairTriggerState } 137 | val hairTriggerDown get() = update().let { hairTriggerState && !hairTriggerPrevState } 138 | val hairTriggerUp get() = update().let { !hairTriggerState && hairTriggerPrevState } 139 | 140 | private infix fun Long.has(buttonMask: ButtonMask) = (this and buttonMask.i) != 0L 141 | private infix fun Long.hasnt(buttonMask: ButtonMask) = (this and buttonMask.i) == 0L 142 | private infix fun Long.has(buttonId: VRButtonId) = (this and buttonId.mask) != 0L 143 | private infix fun Long.hasnt(buttonId: VRButtonId) = (this and buttonId.mask) == 0L 144 | } 145 | 146 | val devices = Array(vr.maxTrackedDeviceCount) { Device(it) } 147 | 148 | /** updates the controllers scanning [0, maxTrackedDeviceCount) */ 149 | fun update(frameCount: Int) { 150 | for (i in 0 until vr.maxTrackedDeviceCount) 151 | devices[i].update(frameCount) 152 | } 153 | 154 | /** This helper can be used in a variety of ways. Beware that indices may change as new devices are dynamically 155 | * added or removed, controllers are physically swapped between hands, arms crossed, etc. */ 156 | enum class DeviceRelation { 157 | First, 158 | // radially 159 | Leftmost, 160 | Rightmost, 161 | // distance - also see vr.hmd.GetSortedTrackedDeviceIndicesOfClass 162 | FarthestLeft, 163 | FarthestRight 164 | } 165 | 166 | /** @return use -1 for absolute tracking space */ 167 | fun deviceIndex(relation: DeviceRelation, deviceClass: TrackedDeviceClass = TrackedDeviceClass.Controller, 168 | relativeTo: Int = vr.trackedDeviceIndex_Hmd): Int { 169 | 170 | var result = -1 171 | 172 | val invXform = 173 | if (relativeTo < vr.maxTrackedDeviceCount) 174 | devices[relativeTo].transform.inverse_() 175 | else 176 | _transform.apply { pos put 0; rot.put(1f, 0f, 0f, 0f) } 177 | 178 | val system = vrSystem ?: return result 179 | 180 | var best = -Float.MAX_VALUE 181 | for (i in 0 until vr.maxTrackedDeviceCount) { 182 | if (i == relativeTo || system.getTrackedDeviceClass(i) != deviceClass) continue 183 | if (!devices[i].connected) continue 184 | if (relation == DeviceRelation.First) return i 185 | var score: Float 186 | val pos = invXform * devices[i].transform.pos 187 | score = when (relation) { 188 | DeviceRelation.FarthestRight -> pos.x 189 | DeviceRelation.FarthestLeft -> -pos.x 190 | else -> { 191 | val dir = Vec3(pos.x, 0f, pos.z).normalizeAssign() 192 | val dot = dir dot Vec3(0, 0, -1)// TODO check and maybe implement in glm Vector3.forward) 193 | val cross = dir cross Vec3(0, 0, -1) // Vector3.forward) 194 | if (relation == DeviceRelation.Leftmost) 195 | if (cross.y > 0f) 2f - dot else dot 196 | else 197 | if (cross.y < 0f) 2f - dot else dot 198 | } 199 | } 200 | if (score > best) { 201 | result = i 202 | best = score 203 | } 204 | } 205 | return result 206 | } 207 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/plugin/Render.kt: -------------------------------------------------------------------------------- 1 | package openvr.plugin 2 | 3 | import openvr.lib.TrackingUniverseOrigin 4 | 5 | 6 | object Render { 7 | 8 | var trackingSpace = TrackingUniverseOrigin.Standing 9 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/plugin/Utils.kt: -------------------------------------------------------------------------------- 1 | package openvr.plugin 2 | 3 | import glm_.mat4x4.Mat4 4 | import glm_.quat.Quat 5 | import glm_.vec3.Vec3 6 | import openvr.lib.m 7 | import org.lwjgl.openvr.HmdMatrix34 8 | import org.lwjgl.openvr.HmdMatrix44 9 | 10 | object Utils { 11 | 12 | class RigidTransform { 13 | 14 | val pos = Vec3() 15 | val rot = Quat() 16 | 17 | constructor() 18 | 19 | constructor(pos: Vec3, rot: Quat) { 20 | this.pos put pos 21 | this.rot put rot 22 | } 23 | 24 | constructor(pose: HmdMatrix34) { 25 | 26 | val m = Mat4() 27 | 28 | m[0, 0] = +pose.m[0] 29 | m[0, 1] = +pose.m[1] 30 | m[0, 2] = -pose.m[2] 31 | m[0, 3] = +pose.m[3] 32 | 33 | m[1, 0] = +pose.m[4] 34 | m[1, 1] = +pose.m[5] 35 | m[1, 2] = -pose.m[6] 36 | m[1, 3] = +pose.m[7] 37 | 38 | m[2, 0] = -pose.m[8] 39 | m[2, 1] = -pose.m[9] 40 | m[2, 2] = +pose.m[10] 41 | m[2, 3] = -pose.m[11] 42 | 43 | pos put m.position 44 | rot put m.toQuat() 45 | } 46 | 47 | constructor(pose: HmdMatrix44) { 48 | 49 | val m = Mat4() 50 | 51 | m[0, 0] = +pose.m[0] 52 | m[0, 1] = +pose.m[1] 53 | m[0, 2] = -pose.m[2] 54 | m[0, 3] = +pose.m[3] 55 | 56 | m[1, 0] = +pose.m[4] 57 | m[1, 1] = +pose.m[5] 58 | m[1, 2] = -pose.m[6] 59 | m[1, 3] = +pose.m[7] 60 | 61 | m[2, 0] = -pose.m[8] 62 | m[2, 1] = -pose.m[9] 63 | m[2, 2] = +pose.m[10] 64 | m[2, 3] = -pose.m[11] 65 | 66 | m[3, 0] = +pose.m[12] 67 | m[3, 1] = +pose.m[13] 68 | m[3, 2] = -pose.m[14] 69 | m[3, 3] = +pose.m[15] 70 | 71 | pos put m.position 72 | rot put m.toQuat() 73 | } 74 | 75 | fun toHmdMatrix44(): HmdMatrix44 { 76 | TODO() 77 | // val m = Matrix4x4.TRS(pos, rot, Vector3.one) 78 | // var pose = new HmdMat44 () 79 | // 80 | // pose.m0 = m[0, 0] 81 | // pose.m1 = m[0, 1] 82 | // pose.m2 = -m[0, 2] 83 | // pose.m3 = m[0, 3] 84 | // 85 | // pose.m4 = m[1, 0] 86 | // pose.m5 = m[1, 1] 87 | // pose.m6 = -m[1, 2] 88 | // pose.m7 = m[1, 3] 89 | // 90 | // pose.m8 = -m[2, 0] 91 | // pose.m9 = -m[2, 1] 92 | // pose.m10 = m[2, 2] 93 | // pose.m11 = -m[2, 3] 94 | // 95 | // pose.m12 = m[3, 0] 96 | // pose.m13 = m[3, 1] 97 | // pose.m14 = -m[3, 2] 98 | // pose.m15 = m[3, 3] 99 | // 100 | // return pose 101 | } 102 | 103 | fun toHmdMatrix34(): HmdMatrix34 { 104 | TODO() 105 | // var m = Matrix4x4.TRS(pos, rot, Vector3.one) 106 | // var pose = new HmdMat34 () 107 | // 108 | // pose.m0 = m[0, 0] 109 | // pose.m1 = m[0, 1] 110 | // pose.m2 = -m[0, 2] 111 | // pose.m3 = m[0, 3] 112 | // 113 | // pose.m4 = m[1, 0] 114 | // pose.m5 = m[1, 1] 115 | // pose.m6 = -m[1, 2] 116 | // pose.m7 = m[1, 3] 117 | // 118 | // pose.m8 = -m[2, 0] 119 | // pose.m9 = -m[2, 1] 120 | // pose.m10 = m[2, 2] 121 | // pose.m11 = -m[2, 3] 122 | // 123 | // return pose 124 | } 125 | 126 | override fun equals(other: Any?) = if (other is RigidTransform) pos == other.pos && rot == other.rot else false 127 | override fun hashCode() = pos.hashCode() xor rot.hashCode() 128 | operator fun times(b: RigidTransform) = RigidTransform(pos + rot * b.pos, rot * b.rot) 129 | fun inverse_(): RigidTransform { 130 | rot.inverseAssign() 131 | pos put (rot * pos).negateAssign() 132 | return this 133 | } 134 | 135 | fun inverse(res: RigidTransform = RigidTransform()) = res.put(pos, rot).inverse_() 136 | 137 | fun put(pos: Vec3, quat: Quat): RigidTransform { 138 | this.pos put pos 139 | this.rot put rot 140 | return this 141 | } 142 | 143 | infix fun put(pose: HmdMatrix34): RigidTransform { 144 | 145 | val m = Mat4() 146 | 147 | m[0, 0] = +pose.m[0] 148 | m[0, 1] = +pose.m[1] 149 | m[0, 2] = -pose.m[2] 150 | m[0, 3] = +pose.m[3] 151 | 152 | m[1, 0] = +pose.m[4] 153 | m[1, 1] = +pose.m[5] 154 | m[1, 2] = -pose.m[6] 155 | m[1, 3] = +pose.m[7] 156 | 157 | m[2, 0] = -pose.m[8] 158 | m[2, 1] = -pose.m[9] 159 | m[2, 2] = +pose.m[10] 160 | m[2, 3] = -pose.m[11] 161 | 162 | pos put m.position 163 | rot put m.toQuat() 164 | 165 | return this 166 | } 167 | 168 | infix fun put(m: Mat4): RigidTransform { 169 | // infix fun put(pose: HmdMatrix44): RigidTransform { 170 | // 171 | // val m = Mat4() 172 | // 173 | // m[0, 0] = +pose.m[0] 174 | // m[0, 1] = +pose.m[1] 175 | // m[0, 2] = -pose.m[2] 176 | // m[0, 3] = +pose.m[3] 177 | // 178 | // m[1, 0] = +pose.m[4] 179 | // m[1, 1] = +pose.m[5] 180 | // m[1, 2] = -pose.m[6] 181 | // m[1, 3] = +pose.m[7] 182 | // 183 | // m[2, 0] = -pose.m[8] 184 | // m[2, 1] = -pose.m[9] 185 | // m[2, 2] = +pose.m[10] 186 | // m[2, 3] = -pose.m[11] 187 | // 188 | // m[3, 0] = +pose.m[12] 189 | // m[3, 1] = +pose.m[13] 190 | // m[3, 2] = -pose.m[14] 191 | // m[3, 3] = +pose.m[15] 192 | 193 | pos put m.position 194 | rot put m.toQuat() 195 | 196 | return this 197 | } 198 | 199 | fun inverseTransformPoint(point: Vec3) = rot.inverse() * (point - pos) 200 | fun transformPoint(point: Vec3) = pos + (rot * point) 201 | operator fun times(v: Vec3) = transformPoint(v) 202 | 203 | fun interpolate(b: RigidTransform, t: Float): Nothing = TODO()//RigidTransform (Vec3.lerp(a.pos, b.pos, t), Quaternion.Slerp(a.rot, b.rot, t)) 204 | } 205 | } 206 | 207 | val Mat4.position get() = Vec3(this[0, 3], this[1, 3], this[2, 3]) -------------------------------------------------------------------------------- /src/main/kotlin/openvr/plugin2/Camera.kt: -------------------------------------------------------------------------------- 1 | package openvr.plugin2 2 | 3 | class Camera -------------------------------------------------------------------------------- /src/main/kotlin/openvr/plugin2/Component.kt: -------------------------------------------------------------------------------- 1 | package openvr.plugin2 2 | 3 | import java.util.* 4 | 5 | open class Component : Object() { 6 | 7 | lateinit var gameObject: GameObject 8 | val transform = Transform() 9 | } 10 | 11 | open class Object { 12 | 13 | open var name = "" 14 | 15 | val instanceId = UUID.randomUUID() 16 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/plugin2/Events.kt: -------------------------------------------------------------------------------- 1 | package openvr.plugin2 2 | 3 | import openvr.lib.vrRenderModels 4 | import org.lwjgl.openvr.TrackedDevicePose 5 | 6 | class Events { 7 | 8 | abstract class Action { 9 | 10 | abstract fun enable(enabled: Boolean) 11 | var enabled: Boolean = false 12 | set(value) { 13 | enable(true) 14 | field = value 15 | } 16 | } 17 | 18 | // [System.Serializable] 19 | // public class ActionNoArgs : Action 20 | // { 21 | // public ActionNoArgs(Event _event, UnityAction action) 22 | // { 23 | // this._event = _event; 24 | // this.action = action; 25 | // } 26 | // 27 | // public override void Enable(bool enabled) 28 | // { 29 | // if (enabled) 30 | // _event.Listen(action); 31 | // else 32 | // _event.Remove(action); 33 | // } 34 | // 35 | // Event _event; 36 | // UnityAction action; 37 | // } 38 | // 39 | // [System.Serializable] 40 | // public class Action : Action 41 | // { 42 | // public Action(Event _event, UnityAction action) 43 | // { 44 | // this._event = _event; 45 | // this.action = action; 46 | // } 47 | // 48 | // public override void Enable(bool enabled) 49 | // { 50 | // if (enabled) 51 | // _event.Listen(action); 52 | // else 53 | // _event.Remove(action); 54 | // } 55 | // 56 | // Event _event; 57 | // UnityAction action; 58 | // } 59 | // 60 | // [System.Serializable] 61 | // public class Action : Action 62 | // { 63 | // public Action(Event _event, UnityAction action) 64 | // { 65 | // this._event = _event; 66 | // this.action = action; 67 | // } 68 | // 69 | // public override void Enable(bool enabled) 70 | // { 71 | // if (enabled) 72 | // _event.Listen(action); 73 | // else 74 | // _event.Remove(action); 75 | // } 76 | // 77 | // Event _event; 78 | // UnityAction action; 79 | // } 80 | // 81 | // [System.Serializable] 82 | // public class Action : Action 83 | // { 84 | // public Action(Event _event, UnityAction action) 85 | // { 86 | // this._event = _event; 87 | // this.action = action; 88 | // } 89 | // 90 | // public override void Enable(bool enabled) 91 | // { 92 | // if (enabled) 93 | // _event.Listen(action); 94 | // else 95 | // _event.Remove(action); 96 | // } 97 | // 98 | // Event _event; 99 | // UnityAction action; 100 | // } 101 | // 102 | // public class Event : UnityEvent 103 | // { 104 | // public void Listen(UnityAction action) { this.AddListener(action); } 105 | // public void Remove(UnityAction action) { this.RemoveListener(action); } 106 | // public void Send() { this.Invoke(); } 107 | // } 108 | // 109 | class Event0/* : UnityEvent*/ { 110 | // public void Listen(UnityAction action) { this.AddListener(action); } 111 | // public void Remove(UnityAction action) { this.RemoveListener(action); } 112 | // public void Send(T arg0) { this.Invoke(arg0); } 113 | } 114 | // 115 | class Event1/* : UnityEvent*/ { 116 | // public void Listen(UnityAction action) { this.AddListener(action); } 117 | // public void Remove(UnityAction action) { this.RemoveListener(action); } 118 | // fun send(arg0: T0 , arg1: T1 ) { this.Invoke(arg0, arg1); } 119 | } 120 | // 121 | // public class Event : UnityEvent 122 | // { 123 | // public void Listen(UnityAction action) { this.AddListener(action); } 124 | // public void Remove(UnityAction action) { this.RemoveListener(action); } 125 | // public void Send(T0 arg0, T1 arg1, T2 arg2) { this.Invoke(arg0, arg1, arg2); } 126 | // } 127 | 128 | companion object { 129 | // public static Event Calibrating = new Event(); 130 | // public static Action CalibratingAction(UnityAction action) { return new Action(Calibrating, action); } 131 | // 132 | // public static Event DeviceConnected = new Event(); 133 | // public static Action DeviceConnectedAction(UnityAction action) { return new Action(DeviceConnected, action); } 134 | // 135 | // public static Event Fade = new Event(); 136 | // public static Action FadeAction(UnityAction action) { return new Action(Fade, action); } 137 | // 138 | // public static Event FadeReady = new Event(); 139 | // public static Action FadeReadyAction(UnityAction action) { return new ActionNoArgs(FadeReady, action); } 140 | // 141 | // public static Event HideRenderModels = new Event(); 142 | // public static Action HideRenderModelsAction(UnityAction action) { return new Action(HideRenderModels, action); } 143 | // 144 | // public static Event Initializing = new Event(); 145 | // public static Action InitializingAction(UnityAction action) { return new Action(Initializing, action); } 146 | // 147 | // public static Event InputFocus = new Event(); 148 | // public static Action InputFocusAction(UnityAction action) { return new Action(InputFocus, action); } 149 | // 150 | // public static Event Loading = new Event(); 151 | // public static Action LoadingAction(UnityAction action) { return new Action(Loading, action); } 152 | // 153 | // public static Event LoadingFadeIn = new Event(); 154 | // public static Action LoadingFadeInAction(UnityAction action) { return new Action(LoadingFadeIn, action); } 155 | // 156 | // public static Event LoadingFadeOut = new Event(); 157 | // public static Action LoadingFadeOutAction(UnityAction action) { return new Action(LoadingFadeOut, action); } 158 | // 159 | val newPoses = Event0() 160 | 161 | // fun newPosesAction(action: TrackedDevicePose.Buffer): Action = object : Action(newPoses, action) 162 | // 163 | // public static Event NewPosesApplied = new Event(); 164 | // public static Action NewPosesAppliedAction(UnityAction action) { return new ActionNoArgs(NewPosesApplied, action); } 165 | // 166 | // public static Event Initialized = new Event(); 167 | // public static Action InitializedAction(UnityAction action) { return new Action(Initialized, action); } 168 | // 169 | // public static Event OutOfRange = new Event(); 170 | // public static Action OutOfRangeAction(UnityAction action) { return new Action(OutOfRange, action); } 171 | // 172 | val renderModelLoaded = Event1() 173 | // public static Action RenderModelLoadedAction(UnityAction action) { return new Action(RenderModelLoaded, action); } 174 | // 175 | // static System.Collections.Generic.Dictionary> systemEvents = new System.Collections.Generic.Dictionary>(); 176 | // public static Event System(EVREventType eventType) 177 | // { 178 | // Event e; 179 | // if (!systemEvents.TryGetValue(eventType, out e)) 180 | // { 181 | // e = new Event(); 182 | // systemEvents.Add(eventType, e); 183 | // } 184 | // return e; 185 | // } 186 | // 187 | // public static Action SystemAction(EVREventType eventType, UnityAction action) 188 | // { 189 | // return new Action(System(eventType), action); 190 | // } 191 | } 192 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/plugin2/GameObject.kt: -------------------------------------------------------------------------------- 1 | package openvr.plugin2 2 | 3 | 4 | class GameObject(override var name: String) : Component() { 5 | 6 | init { 7 | transform.gameObject = this 8 | } 9 | 10 | // val meshes = ArrayList() 11 | val models = ArrayList() 12 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/plugin2/Material.kt: -------------------------------------------------------------------------------- 1 | package openvr.plugin2 2 | 3 | import gli_.Texture2d 4 | 5 | class Material { 6 | 7 | constructor() 8 | constructor(shader: Shader) { 9 | 10 | } 11 | 12 | 13 | 14 | var mainTexture: Texture2d? = null 15 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/plugin2/RectTransform.kt: -------------------------------------------------------------------------------- 1 | package openvr.plugin2 2 | 3 | class RectTransform : Transform() -------------------------------------------------------------------------------- /src/main/kotlin/openvr/plugin2/RigidTransform2.kt: -------------------------------------------------------------------------------- 1 | package openvr.plugin2 2 | 3 | import glm_.mat4x4.Mat4 4 | import glm_.quat.Quat 5 | import glm_.vec3.Vec3 6 | import org.lwjgl.openvr.HmdMatrix34 7 | import org.lwjgl.openvr.HmdMatrix44 8 | 9 | -------------------------------------------------------------------------------- /src/main/kotlin/openvr/plugin2/Shader.kt: -------------------------------------------------------------------------------- 1 | package openvr.plugin2 2 | 3 | class Shader -------------------------------------------------------------------------------- /src/main/kotlin/openvr/plugin2/SteamVR_Input_Source.kt: -------------------------------------------------------------------------------- 1 | package openvr.plugin2 2 | 3 | import openvr.lib.VRInputValueHandle 4 | import openvr.lib.vrInput 5 | 6 | object SteamVR_Input_Source { 7 | 8 | val numSources = SteamVR_Input_Sources.values().size 9 | 10 | private val inputSourceHandlesBySource: LongArray 11 | private val inputSourceSourcesByHandle: MutableMap 12 | 13 | private var _allSources: Array? = null 14 | 15 | infix fun getHandle(inputSource: SteamVR_Input_Sources): Long = 16 | inputSourceHandlesBySource.getOrElse(inputSource.ordinal) { 0 } 17 | 18 | infix fun getSource(handle: Long): SteamVR_Input_Sources = 19 | inputSourceSourcesByHandle[handle] ?: SteamVR_Input_Sources.Any 20 | 21 | val allSources: Array 22 | get() { 23 | if (_allSources == null) 24 | _allSources = SteamVR_Input_Sources.values() 25 | 26 | return _allSources!! 27 | } 28 | 29 | private fun getPath(inputSourceEnumName: String): String = 30 | SteamVR_Input_Sources.valueOf(inputSourceEnumName).description 31 | 32 | init { 33 | 34 | val allSourcesList = ArrayList() 35 | val enumNames = SteamVR_Input_Sources.values().map { it.name } 36 | inputSourceHandlesBySource = LongArray(enumNames.size) 37 | inputSourceSourcesByHandle = mutableMapOf() 38 | 39 | for (enumIndex in enumNames.indices) { 40 | 41 | val path = getPath(enumNames[enumIndex]) 42 | 43 | val handle = vrInput.getInputSourceHandle(path) 44 | 45 | if (vrInput.error != vrInput.Error.None) 46 | System.err.println("[SteamVR] getInputSourceHandle (" + path + ") error: " + vrInput.error) 47 | 48 | if (enumNames[enumIndex] == SteamVR_Input_Sources.Any.name) { //todo: temporary hack 49 | inputSourceHandlesBySource[enumIndex] = 0 50 | inputSourceSourcesByHandle[0] = SteamVR_Input_Sources.values()[enumIndex] 51 | } else { 52 | inputSourceHandlesBySource[enumIndex] = handle 53 | inputSourceSourcesByHandle[handle] = SteamVR_Input_Sources.values()[enumIndex] 54 | } 55 | 56 | allSourcesList += SteamVR_Input_Sources.values()[enumIndex] 57 | } 58 | 59 | allSourcesList.toArray(_allSources!!) 60 | } 61 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/plugin2/SteamVR_Input_Sources.kt: -------------------------------------------------------------------------------- 1 | package openvr.plugin2 2 | 3 | enum class SteamVR_Input_Sources(val description: String) { 4 | 5 | Any("/unrestricted"), //todo: check to see if this gets exported: k_ulInvalidInputHandle 6 | 7 | LeftHand("/user/hand/left"), 8 | 9 | RightHand("/user/hand/right"), 10 | 11 | LeftFoot("/user/foot/left"), 12 | 13 | RightFoot("/user/foot/right"), 14 | 15 | LeftShoulder("/user/shoulder/left"), 16 | 17 | RightShoulder("/user/shoulder/right"), 18 | 19 | Waist("/user/waist"), 20 | 21 | Chest("/user/chest"), 22 | 23 | Head("/user/head"), 24 | 25 | Gamepad("/user/gamepad"), 26 | 27 | Camera("/user/camera"), 28 | 29 | Keyboard("/user/keyboard") 30 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/plugin2/TrackedObject.kt: -------------------------------------------------------------------------------- 1 | package openvr.plugin2 2 | 3 | import kool.rem 4 | import openvr.lib.* 5 | import org.lwjgl.openvr.TrackedDevicePose 6 | 7 | class TrackedObject { 8 | 9 | enum class Index(val i: TrackedDeviceIndex) { 10 | None(-1), 11 | Hmd(vr.trackedDeviceIndex_Hmd), 12 | Device1(1), 13 | Device2(2), 14 | Device3(3), 15 | Device4(4), 16 | Device5(5), 17 | Device6(6), 18 | Device7(7), 19 | Device8(8), 20 | Device9(9), 21 | Device10(10), 22 | Device11(11), 23 | Device12(12), 24 | Device13(13), 25 | Device14(14), 26 | Device15(15), 27 | Device16(16); 28 | } 29 | 30 | var index = Index.None 31 | 32 | /** If not set, relative to parent */ 33 | // public Transform origin; 34 | 35 | var isValid = false 36 | 37 | fun onNewPoses(poses: TrackedDevicePose.Buffer) { 38 | if (index == Index.None) 39 | return 40 | 41 | val i = index.i 42 | 43 | isValid = false 44 | if (poses.rem <= i) 45 | return 46 | 47 | if (!poses[i].deviceIsConnected) 48 | return 49 | 50 | if (!poses[i].poseIsValid) 51 | return 52 | 53 | isValid = true 54 | 55 | val pose = RigidTransform2(poses[i].mDeviceToAbsoluteTracking()) 56 | 57 | // if (origin != null) { 58 | // transform.position = origin.transform.TransformPoint(pose.pos) 59 | // transform.rotation = origin.rotation * pose.rot 60 | // } else { 61 | // transform.localPosition = pose.pos 62 | // transform.localRotation = pose.rot 63 | // } 64 | } 65 | 66 | // var newPosesAction: Events.Action = Events.NewPosesAction(OnNewPoses) 67 | 68 | fun awake() = onEnable() 69 | 70 | fun onEnable() { 71 | // val render = VR_Render.instance 72 | // if (render == null) { 73 | // enabled = false 74 | // return 75 | // } 76 | // 77 | // newPosesAction.enabled = true 78 | } 79 | 80 | fun onDisable() { 81 | // newPosesAction.enabled = false 82 | isValid = false 83 | } 84 | 85 | fun setDeviceIndex(index: Int) { 86 | Index.values().find { it.i == index }?.let { this.index = it } 87 | } 88 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/steamVR_Input/SteamVR_Actions.kt: -------------------------------------------------------------------------------- 1 | package openvr.steamVR_Input 2 | 3 | import openvr.assets.steamVR.input.* 4 | import openvr.steamVR_Input.actionSetClasses.SteamVR_Input_ActionSet_buggy 5 | import openvr.steamVR_Input.actionSetClasses.SteamVR_Input_ActionSet_default 6 | import openvr.steamVR_Input.actionSetClasses.SteamVR_Input_ActionSet_mixedreality 7 | import openvr.steamVR_Input.actionSetClasses.SteamVR_Input_ActionSet_platformer 8 | 9 | object SteamVR_Actions { 10 | 11 | // SteamVR_Input_Initialization.cs 12 | 13 | fun preInitialize() { 14 | startPreInitActionSets() 15 | SteamVR_Input.preinitializeActionSetDictionaries() 16 | preInitActions() 17 | initializeActionArrays() 18 | SteamVR_Input.preinitializeActionDictionaries() 19 | SteamVR_Input.preinitializeFinishActionSets() 20 | } 21 | 22 | // SteamVR_Input_ActionSets.cs 23 | 24 | private lateinit var p__default: SteamVR_Input_ActionSet_default 25 | 26 | private lateinit var p_platformer: SteamVR_Input_ActionSet_platformer 27 | 28 | private lateinit var p_buggy: SteamVR_Input_ActionSet_buggy 29 | 30 | private lateinit var p_mixedreality: SteamVR_Input_ActionSet_mixedreality 31 | 32 | val _default: SteamVR_Input_ActionSet_default 33 | get() = p__default.getCopy() 34 | 35 | val platformer: SteamVR_Input_ActionSet_platformer 36 | get() = p_platformer.getCopy() 37 | 38 | val buggy: SteamVR_Input_ActionSet_buggy 39 | get() = p_buggy.getCopy() 40 | 41 | val mixedreality: SteamVR_Input_ActionSet_mixedreality 42 | get() = p_mixedreality.getCopy() 43 | 44 | private fun startPreInitActionSets() { 45 | p__default = SteamVR_ActionSet.create(SteamVR_Input_ActionSet_default::class.java, "/actions/default") 46 | p_platformer = SteamVR_ActionSet.create(SteamVR_Input_ActionSet_platformer::class.java, "/actions/platformer") 47 | p_buggy = SteamVR_ActionSet.create(SteamVR_Input_ActionSet_buggy::class.java, "/actions/buggy") 48 | p_mixedreality = SteamVR_ActionSet.create(SteamVR_Input_ActionSet_mixedreality::class.java, "/actions/mixedreality") 49 | SteamVR_Input.actionSets = arrayOf(_default, platformer, buggy, mixedreality) 50 | } 51 | 52 | // SteamVR_Input_Actions.cs 53 | 54 | private lateinit var p_default_InteractUI: SteamVR_Action_Boolean 55 | private lateinit var p_default_Teleport: SteamVR_Action_Boolean 56 | private lateinit var p_default_GrabPinch: SteamVR_Action_Boolean 57 | private lateinit var p_default_GrabGrip: SteamVR_Action_Boolean 58 | private lateinit var p_default_Pose: SteamVR_Action_Pose 59 | private lateinit var p_default_SkeletonLeftHand: SteamVR_Action_Skeleton 60 | private lateinit var p_default_SkeletonRightHand: SteamVR_Action_Skeleton 61 | private lateinit var p_default_Squeeze: SteamVR_Action_Single 62 | private lateinit var p_default_HeadsetOnHead: SteamVR_Action_Boolean 63 | private lateinit var p_default_Haptic: SteamVR_Action_Vibration 64 | private lateinit var p_platformer_Move: SteamVR_Action_Vector2 65 | private lateinit var p_platformer_Jump: SteamVR_Action_Boolean 66 | private lateinit var p_buggy_Steering: SteamVR_Action_Vector2 67 | private lateinit var p_buggy_Throttle: SteamVR_Action_Single 68 | private lateinit var p_buggy_Brake: SteamVR_Action_Boolean 69 | private lateinit var p_buggy_Reset: SteamVR_Action_Boolean 70 | private lateinit var p_mixedreality_ExternalCamera: SteamVR_Action_Pose 71 | 72 | val default_InteractUI: SteamVR_Action_Boolean 73 | get() = p_default_InteractUI.getCopy() 74 | 75 | val default_Teleport: SteamVR_Action_Boolean 76 | get() = p_default_Teleport.getCopy() 77 | 78 | val default_GrabPinch: SteamVR_Action_Boolean 79 | get() = p_default_GrabPinch.getCopy() 80 | 81 | val default_GrabGrip: SteamVR_Action_Boolean 82 | get() = p_default_GrabGrip.getCopy() 83 | 84 | val default_Pose: SteamVR_Action_Pose 85 | get() = p_default_Pose.getCopy() 86 | 87 | val default_SkeletonLeftHand: SteamVR_Action_Skeleton 88 | get() = p_default_SkeletonLeftHand.getCopy() 89 | 90 | val default_SkeletonRightHand: SteamVR_Action_Skeleton 91 | get() = p_default_SkeletonRightHand.getCopy() 92 | 93 | val default_Squeeze: SteamVR_Action_Single 94 | get() = p_default_Squeeze.getCopy() 95 | 96 | val default_HeadsetOnHead: SteamVR_Action_Boolean 97 | get() = p_default_HeadsetOnHead.getCopy() 98 | 99 | val default_Haptic: SteamVR_Action_Vibration 100 | get() = p_default_Haptic.getCopy() 101 | 102 | val platformer_Move: SteamVR_Action_Vector2 103 | get() = p_platformer_Move.getCopy() 104 | 105 | val platformer_Jump: SteamVR_Action_Boolean 106 | get() = p_platformer_Jump.getCopy() 107 | 108 | val buggy_Steering: SteamVR_Action_Vector2 109 | get() = p_buggy_Steering.getCopy() 110 | 111 | val buggy_Throttle: SteamVR_Action_Single 112 | get() = p_buggy_Throttle.getCopy() 113 | 114 | val buggy_Brake: SteamVR_Action_Boolean 115 | get() = p_buggy_Brake.getCopy() 116 | 117 | val buggy_Reset: SteamVR_Action_Boolean 118 | get() = p_buggy_Reset.getCopy() 119 | 120 | val mixedreality_ExternalCamera: SteamVR_Action_Pose 121 | get() = p_mixedreality_ExternalCamera.getCopy() 122 | 123 | private fun initializeActionArrays() { 124 | SteamVR_Input.actions = arrayOf( 125 | default_InteractUI, 126 | default_Teleport, 127 | default_GrabPinch, 128 | default_GrabGrip, 129 | default_Pose, 130 | default_SkeletonLeftHand, 131 | default_SkeletonRightHand, 132 | default_Squeeze, 133 | default_HeadsetOnHead, 134 | default_Haptic, 135 | platformer_Move, 136 | platformer_Jump, 137 | buggy_Steering, 138 | buggy_Throttle, 139 | buggy_Brake, 140 | buggy_Reset, 141 | mixedreality_ExternalCamera) 142 | 143 | SteamVR_Input.actionsIn = arrayOf( 144 | default_InteractUI, 145 | default_Teleport, 146 | default_GrabPinch, 147 | default_GrabGrip, 148 | default_Pose, 149 | default_SkeletonLeftHand, 150 | default_SkeletonRightHand, 151 | default_Squeeze, 152 | default_HeadsetOnHead, 153 | platformer_Move, 154 | platformer_Jump, 155 | buggy_Steering, 156 | buggy_Throttle, 157 | buggy_Brake, 158 | buggy_Reset, 159 | mixedreality_ExternalCamera) 160 | 161 | SteamVR_Input.actionsOut = arrayOf(default_Haptic) 162 | 163 | SteamVR_Input.actionsVibration = arrayOf(default_Haptic) 164 | 165 | SteamVR_Input.actionsPose = arrayOf(default_Pose, mixedreality_ExternalCamera) 166 | 167 | SteamVR_Input.actionsBoolean = arrayOf( 168 | default_InteractUI, 169 | default_Teleport, 170 | default_GrabPinch, 171 | default_GrabGrip, 172 | default_HeadsetOnHead, 173 | platformer_Jump, 174 | buggy_Brake, 175 | buggy_Reset) 176 | 177 | SteamVR_Input.actionsSingle = arrayOf(default_Squeeze, buggy_Throttle) 178 | 179 | SteamVR_Input.actionsVector2 = arrayOf(platformer_Move, buggy_Steering) 180 | 181 | SteamVR_Input.actionsVector3 = emptyArray() 182 | 183 | SteamVR_Input.actionsSkeleton = arrayOf(default_SkeletonLeftHand, default_SkeletonRightHand) 184 | 185 | SteamVR_Input.actionsNonPoseNonSkeletonIn = arrayOf( 186 | default_InteractUI, 187 | default_Teleport, 188 | default_GrabPinch, 189 | default_GrabGrip, 190 | default_Squeeze, 191 | default_HeadsetOnHead, 192 | platformer_Move, 193 | platformer_Jump, 194 | buggy_Steering, 195 | buggy_Throttle, 196 | buggy_Brake, 197 | buggy_Reset) 198 | } 199 | 200 | private fun preInitActions() { 201 | p_default_InteractUI = SteamVR_Action.create(ActionType.Boolean, "/actions/default/in/InteractUI") 202 | p_default_Teleport = SteamVR_Action.create(ActionType.Boolean, "/actions/default/in/Teleport") 203 | p_default_GrabPinch = SteamVR_Action.create(ActionType.Boolean, "/actions/default/in/GrabPinch") 204 | p_default_GrabGrip = SteamVR_Action.create(ActionType.Boolean, "/actions/default/in/GrabGrip") 205 | p_default_Pose = SteamVR_Action.create(ActionType.Pose, "/actions/default/in/Pose") 206 | p_default_SkeletonLeftHand = SteamVR_Action.create(ActionType.Skeleton, "/actions/default/in/SkeletonLeftHand") 207 | p_default_SkeletonRightHand = SteamVR_Action.create(ActionType.Skeleton, "/actions/default/in/SkeletonRightHand") 208 | p_default_Squeeze = SteamVR_Action.create(ActionType.Single, "/actions/default/in/Squeeze") 209 | p_default_HeadsetOnHead = SteamVR_Action.create(ActionType.Boolean, "/actions/default/in/HeadsetOnHead") 210 | p_default_Haptic = SteamVR_Action.create(ActionType.Vibration, "/actions/default/out/Haptic") 211 | p_platformer_Move = SteamVR_Action.create(ActionType.Vector2, "/actions/platformer/in/Move") 212 | p_platformer_Jump = SteamVR_Action.create(ActionType.Boolean, "/actions/platformer/in/Jump") 213 | p_buggy_Steering = SteamVR_Action.create(ActionType.Vector2, "/actions/buggy/in/Steering") 214 | p_buggy_Throttle = SteamVR_Action.create(ActionType.Single, "/actions/buggy/in/Throttle") 215 | p_buggy_Brake = SteamVR_Action.create(ActionType.Boolean, "/actions/buggy/in/Brake") 216 | p_buggy_Reset = SteamVR_Action.create(ActionType.Boolean, "/actions/buggy/in/Reset") 217 | p_mixedreality_ExternalCamera = SteamVR_Action.create(ActionType.Pose, "/actions/mixedreality/in/ExternalCamera") 218 | } 219 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/steamVR_Input/actionSetClasses/SteamVR_Input_ActionSet_buggy.kt: -------------------------------------------------------------------------------- 1 | package openvr.steamVR_Input.actionSetClasses 2 | 3 | import openvr.assets.steamVR.input.SteamVR_ActionSet 4 | import openvr.assets.steamVR.input.SteamVR_Action_Boolean 5 | import openvr.assets.steamVR.input.SteamVR_Action_Single 6 | import openvr.assets.steamVR.input.SteamVR_Action_Vector2 7 | import openvr.steamVR_Input.SteamVR_Actions 8 | 9 | //------------------------------------------------------------------------------ 10 | // 11 | // This code was generated by a tool. 12 | // Runtime Version:4.0.30319.42000 13 | // 14 | // Changes to this file may cause incorrect behavior and will be lost if 15 | // the code is regenerated. 16 | // 17 | //------------------------------------------------------------------------------ 18 | 19 | class SteamVR_Input_ActionSet_buggy : SteamVR_ActionSet() { 20 | 21 | val steering: SteamVR_Action_Vector2 22 | get() = SteamVR_Actions.buggy_Steering 23 | 24 | val throttle: SteamVR_Action_Single 25 | get() = SteamVR_Actions.buggy_Throttle 26 | 27 | val brake: SteamVR_Action_Boolean 28 | get() = SteamVR_Actions.buggy_Brake 29 | 30 | val reset: SteamVR_Action_Boolean 31 | get() = SteamVR_Actions.buggy_Reset 32 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/steamVR_Input/actionSetClasses/SteamVR_Input_ActionSet_default.kt: -------------------------------------------------------------------------------- 1 | package openvr.steamVR_Input.actionSetClasses 2 | 3 | import openvr.assets.steamVR.input.* 4 | import openvr.steamVR_Input.SteamVR_Actions 5 | 6 | //------------------------------------------------------------------------------ 7 | // 8 | // This code was generated by a tool. 9 | // Runtime Version:4.0.30319.42000 10 | // 11 | // Changes to this file may cause incorrect behavior and will be lost if 12 | // the code is regenerated. 13 | // 14 | //------------------------------------------------------------------------------ 15 | 16 | class SteamVR_Input_ActionSet_default : SteamVR_ActionSet() { 17 | 18 | val interactUI: SteamVR_Action_Boolean 19 | get() = SteamVR_Actions.default_InteractUI 20 | 21 | val teleport: SteamVR_Action_Boolean 22 | get() = SteamVR_Actions.default_Teleport 23 | 24 | val grabPinch: SteamVR_Action_Boolean 25 | get() = SteamVR_Actions.default_GrabPinch 26 | 27 | val grabGrip: SteamVR_Action_Boolean 28 | get() = SteamVR_Actions.default_GrabGrip 29 | 30 | val pose: SteamVR_Action_Pose 31 | get() = SteamVR_Actions.default_Pose 32 | 33 | val skeletonLeftHand: SteamVR_Action_Skeleton 34 | get() = SteamVR_Actions.default_SkeletonLeftHand 35 | 36 | val skeletonRightHand: SteamVR_Action_Skeleton 37 | get() = SteamVR_Actions.default_SkeletonRightHand 38 | 39 | val squeeze: SteamVR_Action_Single 40 | get() = SteamVR_Actions.default_Squeeze 41 | 42 | val headsetOnHead: SteamVR_Action_Boolean 43 | get() = SteamVR_Actions.default_HeadsetOnHead 44 | 45 | val haptic: SteamVR_Action_Vibration 46 | get() = SteamVR_Actions.default_Haptic 47 | } -------------------------------------------------------------------------------- /src/main/kotlin/openvr/steamVR_Input/actionSetClasses/SteamVR_Input_ActionSet_mixedreality.kt: -------------------------------------------------------------------------------- 1 | package openvr.steamVR_Input.actionSetClasses 2 | 3 | import openvr.assets.steamVR.input.SteamVR_ActionSet 4 | import openvr.assets.steamVR.input.SteamVR_Action_Pose 5 | import openvr.steamVR_Input.SteamVR_Actions 6 | 7 | //------------------------------------------------------------------------------ 8 | // 9 | // This code was generated by a tool. 10 | // Runtime Version:4.0.30319.42000 11 | // 12 | // Changes to this file may cause incorrect behavior and will be lost if 13 | // the code is regenerated. 14 | // 15 | //------------------------------------------------------------------------------ 16 | 17 | class SteamVR_Input_ActionSet_mixedreality : SteamVR_ActionSet() { 18 | 19 | val externalCamera: SteamVR_Action_Pose 20 | get() = SteamVR_Actions.mixedreality_ExternalCamera 21 | } 22 | -------------------------------------------------------------------------------- /src/main/kotlin/openvr/steamVR_Input/actionSetClasses/SteamVR_Input_ActionSet_platformer.kt: -------------------------------------------------------------------------------- 1 | package openvr.steamVR_Input.actionSetClasses 2 | 3 | import openvr.assets.steamVR.input.SteamVR_ActionSet 4 | import openvr.assets.steamVR.input.SteamVR_Action_Boolean 5 | import openvr.assets.steamVR.input.SteamVR_Action_Vector2 6 | import openvr.steamVR_Input.SteamVR_Actions 7 | 8 | //------------------------------------------------------------------------------ 9 | // 10 | // This code was generated by a tool. 11 | // Runtime Version:4.0.30319.42000 12 | // 13 | // Changes to this file may cause incorrect behavior and will be lost if 14 | // the code is regenerated. 15 | // 16 | //------------------------------------------------------------------------------ 17 | 18 | class SteamVR_Input_ActionSet_platformer : SteamVR_ActionSet() { 19 | 20 | val move: SteamVR_Action_Vector2 21 | get() = SteamVR_Actions.platformer_Move 22 | 23 | val jump: SteamVR_Action_Boolean 24 | get() = SteamVR_Actions.platformer_Jump 25 | } 26 | -------------------------------------------------------------------------------- /src/main/kotlin/openvr/unity/tmp.kt: -------------------------------------------------------------------------------- 1 | package openvr.unity 2 | 3 | import glm_.f 4 | import glm_.min 5 | import glm_.quat.Quat 6 | import openvr.assets.steamVR.script.SteamVR_Settings 7 | import kotlin.math.abs 8 | import kotlin.math.acos 9 | 10 | 11 | object Time { 12 | 13 | private val start = System.currentTimeMillis() 14 | 15 | /** The real time in seconds since the game started (Read Only). */ 16 | val realtimeSinceStartup: Float 17 | get() = (System.currentTimeMillis() - start) / 1_000f 18 | 19 | var renderedFrameCount = 0 20 | 21 | /** The total number of frames that have passed (Read Only). */ 22 | var frameCount = 0 23 | 24 | /** The scale at which the time is passing. This can be used for slow motion effects. */ 25 | var timeScale = 1f 26 | 27 | /** The maximum time a frame can spend on particle updates. If the frame takes longer than this, 28 | * then updates are split into multiple smaller updates. */ 29 | var maximumParticleDeltaTime = 0.03f 30 | 31 | /** A smoothed out Time.deltaTime (Read Only). */ 32 | var smoothDeltaTime = 0f 33 | 34 | /** The maximum time a frame can take. Physics and other fixed frame rate updates 35 | * (like MonoBehaviour's MonoBehaviour.FixedUpdate). */ 36 | var maximumDeltaTime = 1f /3 37 | 38 | /** Slows game playback time to allow screenshots to be saved between frames. */ 39 | var captureFramerate = false 40 | 41 | /** The interval in seconds at which physics and other fixed frame rate updates 42 | * (like MonoBehaviour's MonoBehaviour.FixedUpdate) are performed. */ 43 | var fixedDeltaTime = 0.02f 44 | 45 | /** The timeScale-independent interval in seconds from the last frame to the current one (Read Only). */ 46 | var unscaledDeltaTime = 0.02f 47 | 48 | /** The TimeScale-independant time the latest MonoBehaviour.FixedUpdate has started (Read Only). 49 | * This is the time in seconds since the start of the game. */ 50 | // public static float fixedUnscaledTime { get; } 51 | 52 | /** The timeScale-independant time for this frame (Read Only). This is the time in seconds since the start of the game. */ 53 | // public static float unscaledTime { get; } 54 | 55 | /** The time the latest MonoBehaviour.FixedUpdate has started (Read Only). This is the time in seconds since the start of the game. */ 56 | // public static float fixedTime { get; } 57 | 58 | /** The time in seconds it took to complete the last frame (Read Only). */ 59 | var deltaTime = 0f 60 | 61 | /** The time this frame has started (Read Only). This is the time in seconds since the last level has been loaded. */ 62 | // public static float timeSinceLevelLoad { get; } 63 | 64 | /** The time at the beginning of this frame (Read Only). This is the time in seconds since the start of the game. */ 65 | // [NativeProperty("CurTime")] 66 | // public static float time { get; } 67 | 68 | /** The timeScale-independent interval in seconds from the last fixed frame to the current one (Read Only). */ 69 | // public static float fixedUnscaledDeltaTime { get; } 70 | 71 | /** Returns true if called inside a fixed time step callback (like MonoBehaviour's MonoBehaviour.FixedUpdate), 72 | * otherwise returns false. */ 73 | // public static bool inFixedTimeStep { get; } 74 | } 75 | 76 | 77 | object Application { 78 | var isEditor = false 79 | 80 | var dataPath = SteamVR_Settings.actionsFilePath 81 | } 82 | 83 | 84 | /** @Returns the angle in degrees between two rotations a and b. 85 | * 86 | * https://github.com/jamesjlinden/unity-decompiled/blob/master/UnityEngine/UnityEngine/Quaternion.cs#L401 */ 87 | fun angle(a: Quat, b: Quat): Float = (acos(abs(a dot b) min 1f) * 2.0 * 57.2957801818848).f -------------------------------------------------------------------------------- /src/test/java/main/Test.java: -------------------------------------------------------------------------------- 1 | //package main; 2 | // 3 | //import com.sun.jna.ptr.IntByReference; 4 | //import openvr.*; 5 | // 6 | //import static openvr.vr.vrCompositor; 7 | //import static openvr.vr.vrInit; 8 | //import static openvr.vr.vrShutdown; 9 | ////import static openvr.IvrCompositorKt.getIVRCompositor_Version; 10 | // 11 | ///** 12 | // * Created by GBarbieri on 06.02.2017. 13 | // */ 14 | //public class Test { 15 | // 16 | // public static void main(String[] argvs) { 17 | // 18 | // EVRInitError_ByReference error = new EVRInitError_ByReference(); 19 | // 20 | // IVRSystem hmd = vrInit(error, EVRApplicationType.Scene); 21 | // 22 | // System.out.println(error.value.toString()); 23 | // 24 | // IntByReference w = new IntByReference(); 25 | // IntByReference h = new IntByReference(); 26 | // hmd.getRecommendedRenderTargetSize(w, h); 27 | // System.out.println("resolution: "+w.getValue()+" x "+h.getValue()); 28 | // assert(w.getValue() > 0 && h.getValue() > 0); 29 | // 30 | //// IVRCompositor compositor = new IVRCompositor(vr.VR_GetGenericInterface(getIVRCompositor_Version(), error)); 31 | //// IVRCompositor compositor = new IVRCompositor(vr.VR_GetGenericInterface("ciao", error)); 32 | // assert (vrCompositor() != null); 33 | // 34 | // TrackedDevicePose a = new TrackedDevicePose(); 35 | // 36 | // vrShutdown(); 37 | // } 38 | //} 39 | -------------------------------------------------------------------------------- /src/test/java/module-info.test: -------------------------------------------------------------------------------- 1 | --add-reads 2 | com.github.kotlin_graphics.openvr=com.github.kotlin_graphics.gln -------------------------------------------------------------------------------- /src/test/java/openvr/Test.java: -------------------------------------------------------------------------------- 1 | package openvr; 2 | 3 | /** 4 | * Created by GBarbieri on 06.02.2017. 5 | */ 6 | public class Test { 7 | 8 | public static void main(String[] argvs) { 9 | 10 | // evrin error = EVRInitError_ByReference() 11 | 12 | // val hmd = vrInit(error, EVRApplicationType.Scene)!! 13 | // 14 | // println( 15 | // if(error.value == EVRInitError.None) 16 | // "error: None" 17 | // else 18 | // "${error.value}") 19 | // 20 | // 21 | // val w = IntByReference(0) 22 | // val h = IntByReference(0) 23 | // hmd.getRecommendedRenderTargetSize(w, h) 24 | // println("resolution: ${w.value} x ${h.value}") 25 | openvr.lib.vr vr = openvr.lib.vr.INSTANCE; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/test/kotlin/openvr/helloVr_OpenGL/CompanionWindow.kt: -------------------------------------------------------------------------------- 1 | package openvr.helloVr_OpenGL 2 | 3 | import glm_.L 4 | import glm_.vec2.Vec2i 5 | import gln.buffer.* 6 | import kool.get 7 | import gln.glViewport 8 | import gln.glf.glf 9 | import gln.glf.semantic 10 | import gln.program.glUseProgram 11 | import gln.vertexArray.glBindVertexArray 12 | import gln.vertexArray.glEnableVertexAttribArray 13 | import gln.vertexArray.glVertexAttribPointer 14 | import kool.cap 15 | import kool.free 16 | import kool.Buffer 17 | import kool.IntBuffer 18 | import openvr.lib.VREye 19 | import openvr.lib.stak 20 | import openvr.lib.vr 21 | import org.lwjgl.opengl.GL 22 | import org.lwjgl.opengl.GL11.* 23 | import org.lwjgl.opengl.GL12.GL_CLAMP_TO_EDGE 24 | import org.lwjgl.opengl.GL15.* 25 | import org.lwjgl.opengl.GL15C.GL_ARRAY_BUFFER 26 | import org.lwjgl.opengl.GL15C.glBindBuffer 27 | import org.lwjgl.opengl.GL20 28 | import org.lwjgl.opengl.GL20.glUniform1i 29 | import org.lwjgl.opengl.GL20C 30 | import org.lwjgl.opengl.GL20C.glGetUniformLocation 31 | import org.lwjgl.opengl.GL20C.glUseProgram 32 | import org.lwjgl.opengl.GL30.* 33 | import org.lwjgl.opengl.GLUtil 34 | import uno.glfw.GlfwWindow 35 | import uno.glfw.VSync 36 | import uno.glfw.glfw 37 | import openvr.lib.TrackedDeviceProperty as TDP 38 | 39 | class CompanionWindow { 40 | 41 | val resolution = Vec2i(640, 320) 42 | val position = Vec2i(700, 100) 43 | 44 | val strDriver = hmd.getStringTrackedDeviceProperty(vr.trackedDeviceIndex_Hmd, TDP.TrackingSystemName_String) 45 | 46 | val strDisplay = hmd.getStringTrackedDeviceProperty(vr.trackedDeviceIndex_Hmd, TDP.SerialNumber_String) 47 | 48 | val window = GlfwWindow(resolution, "helloVr - $strDriver $strDisplay").also { 49 | it.pos = position 50 | it.makeContextCurrent() 51 | glfw.swapInterval = if (vBlank) VSync.ON else VSync.OFF 52 | it.show() 53 | GL.createCapabilities() 54 | it.autoSwap = false 55 | } 56 | 57 | enum class Buffer { VERTEX, INDEX } 58 | 59 | var bufferName = IntBuffer() 60 | val vertexArrayName = IntBuffer(1) 61 | var indexSize = 0 62 | val program = ProgramWindow() 63 | 64 | init { 65 | 66 | stak { 67 | val vertices = it.floats( 68 | /* left eye verts 69 | | Pos | TexCoord */ 70 | -1f, -1f, 0f, 0f, 71 | +0f, -1f, 1f, 0f, 72 | -1f, +1f, 0f, 1f, 73 | +0f, +1f, 1f, 1f, 74 | /* right eye verts 75 | | Pos | TexCoord */ 76 | +0f, -1f, 0f, 0f, 77 | +1f, -1f, 1f, 0f, 78 | +0f, +1f, 0f, 1f, 79 | +1f, +1f, 1f, 1f) 80 | 81 | val indices = it.shorts( 82 | 0, 1, 3, 83 | 0, 3, 2, 84 | 4, 5, 7, 85 | 4, 7, 6) 86 | 87 | indexSize = indices.cap 88 | 89 | glGenVertexArrays(vertexArrayName) 90 | glBindVertexArray(vertexArrayName) 91 | 92 | glGenBuffers(bufferName) 93 | 94 | glBindBuffer(GL_ARRAY_BUFFER, bufferName[Buffer.VERTEX]) 95 | glBufferData(GL_ARRAY_BUFFER, vertices, GL_STATIC_DRAW) 96 | 97 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferName[Buffer.INDEX]) 98 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices, GL_STATIC_DRAW) 99 | 100 | glEnableVertexAttribArray(glf.pos2_tc2) 101 | glVertexAttribPointer(glf.pos2_tc2) 102 | 103 | glBindVertexArray() 104 | } 105 | 106 | window.framebufferSizeCB = { size -> resolution put size } 107 | } 108 | 109 | fun render() { 110 | 111 | glDisable(GL_DEPTH_TEST) 112 | glViewport(resolution) 113 | 114 | glBindVertexArray(vertexArrayName) 115 | GL20.glUseProgram(program.name) 116 | 117 | // render left eye with first half of index array and right eye with the second half. 118 | for (eye in VREye.values()) { 119 | glBindTexture(GL_TEXTURE_2D, eyeDesc[eye.i].textureName[FrameBufferDesc.Target.RESOLVE]) 120 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) 121 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) 122 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) 123 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) 124 | // offset is in bytes, so indexSize points to half of indices, because short -> int 125 | glDrawElements(GL_TRIANGLES, indexSize / 2, GL_UNSIGNED_SHORT, if (eye == VREye.Left) 0 else indexSize.L) 126 | } 127 | 128 | glBindVertexArray() 129 | glUseProgram() 130 | } 131 | 132 | fun dispose() { 133 | 134 | glDeleteVertexArrays(vertexArrayName) 135 | glDeleteBuffers(bufferName) 136 | program.delete() 137 | 138 | vertexArrayName.free() 139 | bufferName.free() 140 | } 141 | 142 | class ProgramWindow : Program( 143 | vertSrc = """ 144 | #version 410 core 145 | layout(location = ${semantic.attr.POSITION}) in vec4 position; 146 | layout(location = ${semantic.attr.TEX_COORD}) in vec2 uvCoords; 147 | noperspective out vec2 uv; 148 | void main() { 149 | uv = uvCoords; 150 | gl_Position = position; 151 | }""", 152 | fragSrc = """ 153 | #version 410 core 154 | uniform sampler2D myTexture; 155 | noperspective in vec2 uv; 156 | layout(location = ${semantic.frag.COLOR}) out vec4 outColor; 157 | void main() { 158 | outColor = texture(myTexture, uv); 159 | }""") { 160 | init { 161 | glUseProgram(name) 162 | glUniform1i(glGetUniformLocation(name, "myTexture"), semantic.sampler.DIFFUSE) 163 | } 164 | } 165 | } -------------------------------------------------------------------------------- /src/test/kotlin/openvr/helloVr_OpenGL/Program.kt: -------------------------------------------------------------------------------- 1 | package openvr.helloVr_OpenGL 2 | 3 | import glm_.bool 4 | import openvr.lib.stak 5 | import org.lwjgl.opengl.* 6 | import org.lwjgl.opengl.GL20.GL_FRAGMENT_SHADER 7 | import org.lwjgl.opengl.GL20.GL_VERTEX_SHADER 8 | import org.lwjgl.opengl.GL32C.GL_GEOMETRY_SHADER 9 | import java.io.BufferedReader 10 | import java.io.File 11 | import java.io.InputStreamReader 12 | import java.nio.file.Paths 13 | import java.util.stream.Collectors 14 | 15 | 16 | fun main(args: Array) { 17 | A() 18 | } 19 | 20 | class A { 21 | 22 | init { 23 | 24 | val a = Paths.get(".") 25 | val b = ClassLoader.getSystemResource("cube_texture.png").toURI() 26 | // val c = javaClass.getResource("cube_texture.png").toURI() 27 | val d = javaClass.classLoader.getResource("cube_texture.png").toURI() 28 | println(a) 29 | println(b) 30 | // println(c) 31 | println(d) 32 | } 33 | } 34 | 35 | open class Program( 36 | @JvmField 37 | var name: Int = 0) { 38 | 39 | val uniforms = HashMap() 40 | 41 | /** (root, vertex, fragment) or (root0/vertex, root1/fragment) */ 42 | constructor(context: Class<*>, vararg strings: String) : this(GL20.glCreateProgram()) { 43 | 44 | val root = 45 | if (strings[0].isShaderPath()) 46 | "" 47 | else { 48 | var r = strings[0] 49 | if (r[0] != '/') 50 | r = "/$r" 51 | if (!r.endsWith('/')) 52 | r = "$r/" 53 | r 54 | } 55 | 56 | val (shaders, uniforms) = strings.drop(if (root.isEmpty()) 0 else 1).partition { it.isShaderPath() } 57 | 58 | val shaderNames = shaders.map { createShaderFromPath(context, root + it) }.onEach { GL20.glAttachShader(name, it) } 59 | 60 | GL20.glLinkProgram(name) 61 | 62 | if (GL20.glGetProgrami(name, GL20.GL_LINK_STATUS) == GL11.GL_FALSE) 63 | System.err.println("Linker failure: ${GL20.glGetProgramInfoLog(name)}") 64 | 65 | shaderNames.forEach { 66 | GL20.glDetachShader(name, it) 67 | GL20.glDeleteShader(it) 68 | } 69 | 70 | uniforms.forEach { 71 | val i = GL20.glGetUniformLocation(name, it) 72 | if (i != -1) 73 | this.uniforms[it] = i 74 | else 75 | println("unable to find '$it' uniform location!") 76 | } 77 | } 78 | 79 | 80 | 81 | @JvmOverloads 82 | constructor(vertSrc: String, fragSrc: String, geomSrc: String? = null) : this(GL20.glCreateProgram()){ 83 | 84 | val vert = createShader(vertSrc, GL_VERTEX_SHADER) 85 | val frag = createShader(fragSrc, GL_FRAGMENT_SHADER) 86 | val geom = geomSrc?.let { createShader(it, GL_GEOMETRY_SHADER) } 87 | 88 | attach(vert) 89 | attach(frag) 90 | geom?.let(::attach) 91 | 92 | link() 93 | 94 | if (!linkStatus) 95 | System.err.println("Linker failure: $infoLog") 96 | 97 | detach(vert) 98 | detach(frag) 99 | geom?.let(::detach) 100 | 101 | delete(vert) 102 | delete(frag) 103 | geom?.let(::delete) 104 | } 105 | 106 | operator fun get(s: String): Int = uniforms[s]!! 107 | 108 | private fun String.isShaderPath() = when (substringAfterLast('.')) { 109 | "vert", "tesc", "tese", "geom", "frag", "comp" -> true 110 | else -> false 111 | } 112 | 113 | 114 | fun createShaderFromPath(clazz: Class<*>, path: String): Int { 115 | 116 | val shader = GL20.glCreateShader(path.type) 117 | 118 | val url = clazz.classLoader.getResource(path) 119 | val lines = File(url.toURI()).readLines() 120 | 121 | var source = "" 122 | lines.forEach { 123 | source += when { 124 | it.startsWith("#include ") -> parseInclude(clazz, path.substringBeforeLast('/'), it.substring("#include ".length).trim()) 125 | else -> it 126 | } 127 | source += '\n' 128 | } 129 | 130 | GL20.glShaderSource(shader, source) 131 | 132 | GL20.glCompileShader(shader) 133 | 134 | val status = GL20.glGetShaderi(shader, GL20.GL_COMPILE_STATUS) 135 | if (status == GL11.GL_FALSE) { 136 | 137 | val strInfoLog = GL20.glGetShaderInfoLog(shader) 138 | 139 | System.err.println("Compiler failure in ${path.substringAfterLast('/')} shader: $strInfoLog") 140 | } 141 | 142 | return shader 143 | } 144 | 145 | fun parseInclude(context: Class<*>, root: String, shader: String): String { 146 | if (shader.startsWith('"') && shader.endsWith('"')) 147 | shader.substring(1, shader.length - 1) 148 | val url = context::class.java.getResource("$root/$shader") 149 | return File(url.toURI()).readText() + "\n" 150 | } 151 | 152 | fun createProgram(shaderList: List): Int { 153 | 154 | val program = GL20.glCreateProgram() 155 | 156 | shaderList.forEach { GL20.glAttachShader(program, it) } 157 | 158 | GL20.glLinkProgram(program) 159 | 160 | if (GL20.glGetProgrami(program, GL20.GL_LINK_STATUS) == GL11.GL_FALSE) 161 | System.err.println("Linker failure: ${GL20.glGetProgramInfoLog(program)}") 162 | 163 | shaderList.forEach { 164 | GL20.glDetachShader(program, it) 165 | GL20.glDeleteShader(it) 166 | } 167 | 168 | return program 169 | } 170 | 171 | fun attach(shader: Int) = GL20.glAttachShader(name, shader) 172 | operator fun plusAssign(shader: Int) = GL20.glAttachShader(name, shader) 173 | fun detach(shader: Int) = GL20.glDetachShader(name, shader) 174 | operator fun minusAssign(shader: Int) = GL20.glDetachShader(name, shader) 175 | 176 | fun delete(shader: Int) = GL20.glDeleteShader(shader) 177 | 178 | fun link() = GL20.glLinkProgram(name) 179 | 180 | val linkStatus get() = GL20.glGetProgrami(name, GL20.GL_LINK_STATUS).bool 181 | 182 | val infoLog get() = GL20.glGetProgramInfoLog(name) 183 | 184 | fun delete() = GL20.glDeleteProgram(name) 185 | 186 | companion object { 187 | 188 | fun fromSources(vertSrc: String, fragSrc: String, geomSrc: String? = null): Program { 189 | 190 | val program = Program() 191 | 192 | val shaders = arrayListOf(createShader(vertSrc, GL_VERTEX_SHADER), createShader(fragSrc, GL_FRAGMENT_SHADER)) 193 | geomSrc?.let { shaders += createShader(it, GL32.GL_GEOMETRY_SHADER) } 194 | 195 | shaders.forEach { GL20.glAttachShader(program.name, it) } 196 | 197 | program.link() 198 | 199 | if (GL20.glGetProgrami(program.name, GL20.GL_LINK_STATUS) == GL11.GL_FALSE) 200 | System.err.println("Linker failure: ${GL20.glGetProgramInfoLog(program.name)}") 201 | 202 | shaders.forEach { 203 | GL20.glDetachShader(program.name, it) 204 | GL20.glDeleteShader(it) 205 | } 206 | 207 | return program 208 | } 209 | 210 | @Throws(Error::class) 211 | fun createShader(source: String, type: Int): Int { 212 | 213 | val shader = GL20.glCreateShader(type) 214 | 215 | GL20.glShaderSource(shader, source) 216 | 217 | GL20.glCompileShader(shader) 218 | 219 | if (GL20.glGetShaderi(shader, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) 220 | throw Error(GL20.glGetShaderInfoLog(shader)) 221 | 222 | return shader 223 | } 224 | 225 | fun fromSources(vertSrc: Array, fragSrc: Array, geomSrc: Array? = null): Program { 226 | 227 | val program = Program() 228 | 229 | val shaders = arrayListOf(createShader(vertSrc, GL_VERTEX_SHADER), createShader(fragSrc, GL_FRAGMENT_SHADER)) 230 | geomSrc?.let { shaders += createShader(it, GL32.GL_GEOMETRY_SHADER) } 231 | 232 | shaders.forEach { GL20.glAttachShader(program.name, it) } 233 | 234 | program.link() 235 | 236 | if (GL20.glGetProgrami(program.name, GL20.GL_LINK_STATUS) == GL11.GL_FALSE) 237 | System.err.println("Linker failure: ${GL20.glGetProgramInfoLog(program.name)}") 238 | 239 | shaders.forEach { 240 | GL20.glDetachShader(program.name, it) 241 | GL20.glDeleteShader(it) 242 | } 243 | 244 | return program 245 | } 246 | 247 | @Throws(Error::class) 248 | fun createShader(source: Array, type: Int): Int = stak { 249 | 250 | val shader = GL20.glCreateShader(type) 251 | 252 | GL20.glShaderSource(shader, *source) 253 | 254 | GL20.glCompileShader(shader) 255 | 256 | if (GL20.glGetShaderi(shader, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) 257 | throw Error(GL20.glGetShaderInfoLog(shader)) 258 | 259 | return shader 260 | } 261 | 262 | fun createShaderFromPath(path: String): Int { 263 | 264 | val lines = ClassLoader.getSystemResourceAsStream(path).use { 265 | BufferedReader(InputStreamReader(it)).lines().collect(Collectors.toList()) 266 | } 267 | 268 | var source = "" 269 | lines.forEach { 270 | source += 271 | if (it.startsWith("#include ")) 272 | parseInclude(path.substringBeforeLast('/'), it.substring("#include ".length).trim()) 273 | else it 274 | source += '\n' 275 | } 276 | 277 | try { 278 | return createShader(source, path.type) 279 | } catch (err: Exception) { 280 | throw Error("Compiler failure in ${path.substringAfterLast('/')} shader: ${err.message}") 281 | } 282 | } 283 | 284 | fun parseInclude(root: String, shader: String): String { 285 | if (shader.startsWith('"') && shader.endsWith('"')) 286 | shader.substring(1, shader.length - 1) 287 | val url = ClassLoader.getSystemResource("$root/$shader") 288 | return File(url.toURI()).readText() + "\n" 289 | } 290 | 291 | private val String.type 292 | get() = when (substringAfterLast('.')) { 293 | "vert" -> GL20.GL_VERTEX_SHADER 294 | "tesc" -> GL40.GL_TESS_CONTROL_SHADER 295 | "tese" -> GL40.GL_TESS_EVALUATION_SHADER 296 | "geom" -> GL32.GL_GEOMETRY_SHADER 297 | "frag" -> GL20.GL_FRAGMENT_SHADER 298 | "comp" -> GL43.GL_COMPUTE_SHADER 299 | else -> throw Error("invalid shader extension") 300 | } 301 | } 302 | } -------------------------------------------------------------------------------- /src/test/kotlin/openvr/helloVr_OpenGL/components model.kt: -------------------------------------------------------------------------------- 1 | package openvr.helloVr_OpenGL 2 | 3 | import glm_.vec2.Vec2 4 | import glm_.vec3.Vec3 5 | import gln.glf.semantic 6 | import gln.texture.glBindTexture2d 7 | import gln.texture.glGenerateMipmap2D 8 | import gln.texture.glTex2dParameter 9 | import gln.texture.glTexImage2D 10 | import gln.vertexArray.glBindVertexArray 11 | import gln.vertexArray.glVertexAttribPointer 12 | import kool.IntBuffer 13 | import kool.free 14 | import kool.get 15 | import openvr.lib.* 16 | import openvr.lib.vrRenderModels.freeNative 17 | import org.lwjgl.opengl.EXTTextureFilterAnisotropic.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 18 | import org.lwjgl.opengl.EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT 19 | import org.lwjgl.opengl.GL11 20 | import org.lwjgl.opengl.GL12.GL_CLAMP_TO_EDGE 21 | import org.lwjgl.opengl.GL15 22 | import org.lwjgl.opengl.GL20.glEnableVertexAttribArray 23 | import org.lwjgl.opengl.GL30.* 24 | import org.lwjgl.openvr.RenderModel 25 | import org.lwjgl.openvr.RenderModelComponentState 26 | import org.lwjgl.openvr.RenderModelTextureMap 27 | import org.lwjgl.openvr.RenderModelVertex 28 | 29 | class Model(val name: String) { 30 | 31 | val components = ArrayList() 32 | val componentState = RenderModelComponentState.calloc() 33 | 34 | fun draw(matrixUL: Int) { 35 | 36 | components.forEach { 37 | // vrRenderModels.getComponentStateForDevicePath(name, it.name, ) 38 | } 39 | } 40 | 41 | companion object { 42 | 43 | fun load(name: String): Model? { 44 | 45 | val count = vrRenderModels.getComponentCount(name) 46 | if (count == 0) return null 47 | 48 | val model = Model(name) 49 | 50 | for (i in 0 until count) { 51 | 52 | val componentName = vrRenderModels.getComponentName(name, i) ?: continue 53 | 54 | val componentPath = vrRenderModels.getComponentRenderModelName(name, componentName) ?: continue 55 | 56 | val renderModel = vrRenderModels.loadRenderModel(componentPath) ?: continue 57 | val texture = vrRenderModels.loadTexture(renderModel.diffuseTextureId) 58 | 59 | if (texture == null) { 60 | renderModel.freeNative() 61 | continue 62 | } 63 | 64 | model.components += ModelComponent(componentName, renderModel, texture) 65 | } 66 | 67 | return model 68 | } 69 | } 70 | } 71 | 72 | class ModelComponent(val name: String, vrModel: RenderModel, vrDiffuseTexture: RenderModelTextureMap) { 73 | 74 | enum class Buffer { VERTEX, INDEX } 75 | 76 | var bufferName = IntBuffer() 77 | val vertexArrayName = IntBuffer(1) 78 | val textureName = IntBuffer(1) 79 | var vertexCount = 0 80 | 81 | /** Purpose: Allocates and populates the GL resources for a render model */ 82 | init { 83 | 84 | // create and bind a VAO to hold state for this model 85 | glGenVertexArrays(vertexArrayName) 86 | glBindVertexArray(vertexArrayName) 87 | 88 | // Populate a vertex buffer 89 | glGenBuffers(bufferName) 90 | glBindBuffer(GL15.GL_ARRAY_BUFFER, bufferName[Buffer.VERTEX]) 91 | glBufferData(GL_ARRAY_BUFFER, vrModel.vertices, GL15.GL_STATIC_DRAW) 92 | 93 | // Identify the components in the vertex buffer 94 | glEnableVertexAttribArray(semantic.attr.POSITION) 95 | glVertexAttribPointer(semantic.attr.POSITION, Vec3.length, GL_FLOAT, false, RenderModelVertex.SIZEOF, RenderModelVertex.VPOSITION) 96 | glEnableVertexAttribArray(semantic.attr.TEX_COORD) 97 | glVertexAttribPointer(semantic.attr.TEX_COORD, Vec2.length, GL_FLOAT, false, RenderModelVertex.SIZEOF, RenderModelVertex.RFTEXTURECOORD) 98 | 99 | // Create and populate the index buffer 100 | glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, bufferName[Buffer.INDEX]) 101 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, vrModel.indices, GL15.GL_STATIC_DRAW) 102 | 103 | glBindVertexArray() 104 | 105 | // create and populate the texture 106 | glGenTextures(textureName) 107 | glActiveTexture(GL_TEXTURE0 + semantic.sampler.DIFFUSE) 108 | glBindTexture2d(textureName) 109 | 110 | glTexImage2D(GL_RGBA, vrDiffuseTexture.size, GL_RGBA, GL_UNSIGNED_BYTE, vrDiffuseTexture.textureMapData) 111 | 112 | // If this renders black ask McJohn what's wrong. 113 | glGenerateMipmap2D() 114 | 115 | glTex2dParameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) 116 | glTex2dParameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) 117 | glTex2dParameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR) 118 | glTex2dParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR) 119 | 120 | val largest = glGetFloat(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT) 121 | glTex2dParameter(GL_TEXTURE_MAX_ANISOTROPY_EXT, largest) 122 | 123 | glBindTexture2d() 124 | 125 | vertexCount = vrModel.triangleCount * 3 126 | } 127 | 128 | /** Purpose: Draws the render model */ 129 | fun draw() { 130 | 131 | glBindVertexArray(vertexArrayName) 132 | 133 | glActiveTexture(GL_TEXTURE0 + semantic.sampler.DIFFUSE) 134 | glBindTexture2d(textureName) 135 | 136 | glDrawElements(GL_TRIANGLES, vertexCount, GL_UNSIGNED_SHORT, 0) 137 | 138 | glBindVertexArray(0) 139 | } 140 | 141 | /** Purpose: Frees the GL resources for a render model */ 142 | fun dispose() { 143 | 144 | glDeleteBuffers(bufferName) 145 | glDeleteVertexArrays(vertexArrayName) 146 | GL11.glDeleteTextures(textureName) 147 | 148 | bufferName.free() 149 | vertexArrayName.free() 150 | textureName.free() 151 | } 152 | } -------------------------------------------------------------------------------- /src/test/kotlin/openvr/helloVr_OpenGL/helpers.kt: -------------------------------------------------------------------------------- 1 | package openvr.helloVr_OpenGL 2 | 3 | import glm_.vec2.Vec2 4 | import glm_.vec2.Vec2i 5 | import glm_.vec3.Vec3 6 | import gln.buffer.* 7 | import kool.get 8 | import gln.glf.semantic 9 | import gln.texture.glBindTexture2d 10 | import gln.texture.glGenerateMipmap2D 11 | import gln.texture.glTex2dParameter 12 | import gln.texture.glTexImage2D 13 | import gln.vertexArray.glBindVertexArray 14 | import gln.vertexArray.glVertexAttribPointer 15 | import kool.free 16 | import kool.Buffer 17 | import kool.IntBuffer 18 | import openvr.lib.* 19 | import org.lwjgl.opengl.EXTTextureFilterAnisotropic.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 20 | import org.lwjgl.opengl.EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT 21 | import org.lwjgl.opengl.GL11 22 | import org.lwjgl.opengl.GL12.GL_CLAMP_TO_EDGE 23 | import org.lwjgl.opengl.GL12.GL_TEXTURE_MAX_LEVEL 24 | import org.lwjgl.opengl.GL15 25 | import org.lwjgl.opengl.GL20.glEnableVertexAttribArray 26 | import org.lwjgl.opengl.GL30.* 27 | import org.lwjgl.opengl.GL32.GL_TEXTURE_2D_MULTISAMPLE 28 | import org.lwjgl.opengl.GL32.glTexImage2DMultisample 29 | import org.lwjgl.openvr.RenderModel 30 | import org.lwjgl.openvr.RenderModelTextureMap 31 | import org.lwjgl.openvr.RenderModelVertex 32 | import java.nio.ByteBuffer 33 | 34 | class FrameBufferDesc(val size: Vec2i) { 35 | 36 | object Target { 37 | val RENDER = 0 38 | val RESOLVE = 1 39 | val MAX = 2 40 | } 41 | 42 | val depthRenderbufferName = IntBuffer(1) 43 | val textureName = IntBuffer(Target.MAX) 44 | val frameBufferName = IntBuffer(Target.MAX) 45 | 46 | /** Purpose: Creates a frame buffer. Returns true if the buffer was set up. Throw error if the setup failed. */ 47 | init { 48 | 49 | glGenFramebuffers(frameBufferName) 50 | glGenRenderbuffers(depthRenderbufferName) 51 | glGenTextures(textureName) 52 | 53 | glBindFramebuffer(GL_FRAMEBUFFER, frameBufferName[Target.RENDER]) 54 | 55 | glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbufferName[0]) 56 | glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT, size.x, size.y) 57 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbufferName[0]) 58 | 59 | glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, textureName[Target.RENDER]) 60 | glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, size.x, size.y, true) 61 | glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, textureName[Target.RENDER], 0) 62 | 63 | 64 | glBindFramebuffer(GL_FRAMEBUFFER, frameBufferName[Target.RESOLVE]) 65 | 66 | glBindTexture(GL_TEXTURE_2D, textureName[Target.RESOLVE]) 67 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) 68 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0) 69 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, size.x, size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, null as ByteBuffer?) 70 | glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureName[Target.RESOLVE], 0) 71 | 72 | // check FBO status 73 | if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) 74 | throw Error("framebuffer incomplete!") 75 | 76 | glBindFramebuffer(GL_FRAMEBUFFER, 0) 77 | } 78 | 79 | fun render(eye: VREye) { 80 | 81 | glEnable(GL_MULTISAMPLE) 82 | 83 | glBindFramebuffer(GL_FRAMEBUFFER, frameBufferName[Target.RENDER]) 84 | glViewport(0, 0, size.x, size.y) 85 | scene.render(eye) 86 | glBindFramebuffer(GL_FRAMEBUFFER, 0) 87 | 88 | glDisable(GL_MULTISAMPLE) 89 | 90 | glBindFramebuffer(GL_READ_FRAMEBUFFER, frameBufferName[Target.RENDER]) 91 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBufferName[Target.RESOLVE]) 92 | 93 | glBlitFramebuffer(0, 0, size.x, size.y, 0, 0, size.x, size.y, GL_COLOR_BUFFER_BIT, GL_LINEAR) 94 | 95 | glBindFramebuffer(GL_READ_FRAMEBUFFER, 0) 96 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0) 97 | } 98 | 99 | fun dispose() { 100 | 101 | glDeleteRenderbuffers(depthRenderbufferName) 102 | glDeleteTextures(textureName) 103 | glDeleteFramebuffers(frameBufferName) 104 | 105 | depthRenderbufferName.free() 106 | textureName.free() 107 | frameBufferName.free() 108 | } 109 | } 110 | 111 | class CGLRenderModel(val modelName: String, vrModel: RenderModel, vrDiffuseTexture: RenderModelTextureMap) { 112 | 113 | enum class Buffer { VERTEX, INDEX } 114 | 115 | var bufferName = IntBuffer() 116 | val vertexArrayName = IntBuffer(1) 117 | val textureName = IntBuffer(1) 118 | var vertexCount = 0 119 | 120 | /** Purpose: Allocates and populates the GL resources for a render model */ 121 | init { 122 | 123 | // create and bind a VAO to hold state for this model 124 | glGenVertexArrays(vertexArrayName) 125 | glBindVertexArray(vertexArrayName) 126 | 127 | // Populate a vertex buffer 128 | glGenBuffers(bufferName) 129 | glBindBuffer(GL15.GL_ARRAY_BUFFER, bufferName[Buffer.VERTEX]) 130 | glBufferData(GL_ARRAY_BUFFER, vrModel.vertices, GL15.GL_STATIC_DRAW) 131 | 132 | // Identify the components in the vertex buffer 133 | glEnableVertexAttribArray(semantic.attr.POSITION) 134 | glVertexAttribPointer(semantic.attr.POSITION, Vec3.length, GL_FLOAT, false, RenderModelVertex.SIZEOF, RenderModelVertex.VPOSITION) 135 | glEnableVertexAttribArray(semantic.attr.TEX_COORD) 136 | glVertexAttribPointer(semantic.attr.TEX_COORD, Vec2.length, GL_FLOAT, false, RenderModelVertex.SIZEOF, RenderModelVertex.RFTEXTURECOORD) 137 | 138 | // Create and populate the index buffer 139 | glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, bufferName[Buffer.INDEX]) 140 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, vrModel.indices, GL15.GL_STATIC_DRAW) 141 | 142 | glBindVertexArray() 143 | 144 | // create and populate the texture 145 | glGenTextures(textureName) 146 | glActiveTexture(GL_TEXTURE0 + semantic.sampler.DIFFUSE) 147 | glBindTexture2d(textureName) 148 | 149 | glTexImage2D(GL_RGBA, vrDiffuseTexture.size, GL_RGBA, GL_UNSIGNED_BYTE, vrDiffuseTexture.textureMapData) 150 | 151 | // If this renders black ask McJohn what's wrong. 152 | glGenerateMipmap2D() 153 | 154 | glTex2dParameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) 155 | glTex2dParameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) 156 | glTex2dParameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR) 157 | glTex2dParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR) 158 | 159 | val largest = glGetFloat(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT) 160 | glTex2dParameter(GL_TEXTURE_MAX_ANISOTROPY_EXT, largest) 161 | 162 | glBindTexture2d() 163 | 164 | vertexCount = vrModel.triangleCount * 3 165 | } 166 | 167 | /** Purpose: Draws the render model */ 168 | fun draw() { 169 | 170 | glBindVertexArray(vertexArrayName) 171 | 172 | glActiveTexture(GL_TEXTURE0 + semantic.sampler.DIFFUSE) 173 | glBindTexture2d(textureName) 174 | 175 | glDrawElements(GL_TRIANGLES, vertexCount, GL_UNSIGNED_SHORT, 0) 176 | 177 | glBindVertexArray(0) 178 | } 179 | 180 | /** Purpose: Frees the GL resources for a render model */ 181 | fun dispose() { 182 | 183 | glDeleteBuffers(bufferName) 184 | glDeleteVertexArrays(vertexArrayName) 185 | GL11.glDeleteTextures(textureName) 186 | 187 | bufferName.free() 188 | vertexArrayName.free() 189 | textureName.free() 190 | } 191 | } -------------------------------------------------------------------------------- /src/test/kotlin/openvr/models.kt: -------------------------------------------------------------------------------- 1 | package openvr 2 | 3 | import io.kotlintest.specs.StringSpec 4 | 5 | class models : StringSpec() { 6 | 7 | init { 8 | "models allocation test" { 9 | 10 | if (System.getenv("TRAVIS") != "true") { 11 | 12 | // vrInit(null, EVRApplicationType.Scene) 13 | 14 | val models = listOf( 15 | "arrow", 16 | "dk2_camera") 17 | 18 | for (i in 0..99) 19 | models.forEach(::loadRenderModel) 20 | } 21 | } 22 | } 23 | 24 | fun loadRenderModel(renderModelName: String) { 25 | 26 | // load the model if we didn't find one 27 | // var error: vrRenderModels.Error 28 | // 29 | // val rm = vrRenderModels!! 30 | // 31 | // val pModel = PointerByReference() 32 | // while (true) { 33 | // error = rm.loadRenderModel_Async(modelName, pModel) 34 | // if (error != EVRRenderModelError.Loading) break 35 | // Thread.sleep(1) 36 | // } 37 | // val model = RenderModel.ByReference(pModel.value) 38 | // 39 | // if (error != EVRRenderModelError.None) { 40 | // System.err.println("Unable to load render model $modelName - ${error.getName()}") 41 | // return // move on to the next tracked device 42 | // } 43 | 44 | // val pTexture = PointerByReference() 45 | // while (true) { 46 | // if (rm.loadTexture_Async(model.diffuseTextureId, pTexture) != Error.Loading) 47 | // break 48 | // Thread.sleep(1) 49 | // } 50 | // val texture = RenderModel_TextureMap.ByReference(pTexture.value) 51 | // 52 | // if (error != Error.None) { 53 | // System.err.println("Unable to load render texture id:${model.diffuseTextureId} for render model $modelName") 54 | // rm freeRenderModel model 55 | // return // move on to the next tracked device 56 | // } 57 | 58 | // val v = model.rVertexData!!.toArray(model.vertexCount) 59 | // val i = model.rIndexData!!.pointer.getShortArray(0, model.triangleCount * 3) 60 | 61 | // model.read() 62 | // model.write() 63 | // texture.read() 64 | // texture.write() 65 | // println(model.pointer) 66 | // println(" " + model.rVertexData?.pointer) 67 | // println(" " + model.vertexCount) 68 | // println(" " + model.rIndexData?.pointer) 69 | // println(" " + model.triangleCount) 70 | // println(" " + model.diffuseTextureId) 71 | // println(texture.pointer) 72 | // 73 | // try { 74 | // rm freeRenderModel model 75 | //// rm freeTexture texture 76 | // } catch (e: Error) { 77 | // System.err.println(e) 78 | // } 79 | println() 80 | } 81 | } -------------------------------------------------------------------------------- /src/test/kotlin/openvr/test.kt: -------------------------------------------------------------------------------- 1 | package openvr 2 | 3 | //import com.sun.jna.ptr.FloatByReference 4 | //import glm_.vec2.Vec2 5 | //import io.kotlintest.shouldBe 6 | //import io.kotlintest.shouldNotBe 7 | //import io.kotlintest.specs.StringSpec 8 | //import openvr.EventListener 9 | //import openvr.lib.* 10 | // 11 | // 12 | ///** 13 | // * Created by GBarbieri on 25.10.2016. 14 | // */ 15 | // 16 | // 17 | //class Test : StringSpec() { 18 | // 19 | // init { 20 | // 21 | // "simple test" { 22 | // if (System.getenv("TRAVIS") != "true") 23 | // simpleTest() 24 | // } 25 | // } 26 | // 27 | // fun simpleTest() { 28 | // 29 | // val error = EVRInitError_ByReference() 30 | // 31 | // val hmd = vrInit(error, EVRApplicationType.Scene)!! 32 | // 33 | // vrCompositor shouldNotBe null 34 | // 35 | // error.value shouldBe EVRInitError.None 36 | // 37 | // val (w, h) = hmd.recommendedRenderTargetSize 38 | // (w > 0 && h > 0) shouldBe true 39 | // 40 | // 41 | // val m = hmd.getProjectionMatrix(EVREye.Left, .1f, 10f) 42 | // /** 0.75787073 0 -0.05657852 0 43 | // * 0 0.6820195 -0.0013340205 0 44 | // * 0 0 -1.0101011 -0.10101011 45 | // * 0 0 -1 0 */ 46 | // (m[0, 0] != 0f && m[1, 0] == 0f && m[2, 0] != 0f && m[3, 0] == 0f && 47 | // m[0, 1] == 0f && m[1, 1] != 0f && m[2, 1] != 0f && m[3, 1] == 0f && 48 | // m[0, 2] == 0f && m[1, 2] == 0f && m[2, 2] != 0f && m[3, 2] != 0f && 49 | // m[0, 3] == 0f && m[1, 3] == 0f && m[2, 3] != 0f && m[3, 3] == 0f) shouldBe true 50 | // 51 | // 52 | // val left = FloatByReference() 53 | // val right = FloatByReference() 54 | // val top = FloatByReference() 55 | // val bottom = FloatByReference() 56 | // hmd.getProjectionRaw(EVREye.Left, left, right, top, bottom) 57 | //// -1.3941408, 1.2448317, -1.4681898, 1.4642779 58 | // (left.value < 0 && right.value > 0 && top.value < 0 && bottom.value >= 0) shouldBe true 59 | // 60 | // val overlayHandle = VROverlayHandle_ByReference() 61 | // val overlayThumbnailHandle = VROverlayHandle_ByReference() 62 | // 63 | // vrOverlay?.let { 64 | // val name = "systemOverlay" 65 | // val key = "sample.$name" 66 | // val overlayError = it.createDashboardOverlay(key, name, overlayHandle, overlayThumbnailHandle) 67 | // overlayError shouldBe Error.None 68 | // } 69 | // 70 | // val listener = Listener(hmd) 71 | // 72 | // val start = System.nanoTime() 73 | // while (System.nanoTime() - start < 5e9) { 74 | // listener.poll() 75 | //// hmd.getControllerState(1, state, state.size()) 76 | //// println("(${state.axis[0].x}, ${state.axis[0].y}") 77 | //// val leftMost = Controller.deviceIndex(Controller.DeviceRelation.Leftmost) 78 | //// val rightMost = Controller.deviceIndex(Controller.DeviceRelation.Rightmost) 79 | //// println("most $leftMost, $rightMost") 80 | // } 81 | // 82 | //// if(vr.compositor == null) throw Error() 83 | // 84 | //// val dc = hmd.computeDistortion(EVREye.Left, .5f, .5f) 85 | //// // 86 | //// assert(dc.red[0] in 0..1 && dc.red[1] in 0..1 && dc.green[0] in 0..1 && dc.green[1] in 0..1 && dc.blue[0] in 0..1 && dc.blue[1] in 0..1) 87 | //// 88 | //// 89 | //// val m43 = hmd.getEyeToHeadTransform(EVREye.Left) 90 | //// /** 1 0 0 -0.03045 91 | //// * 0 1 0 0 92 | //// * 0 0 1 0.015 */ 93 | //// assert(m43[0] == 1f && m43[1] == 0f && m43[2] == 0f && m43[3] < 0 94 | //// && m43[4] == 0f && m43[5] == 1f && m43[6] == 0f && m43[7] == 0f 95 | //// && m43[8] == 0f && m43[9] == 0f && m43[10] == 1f && m43[11] > 0) 96 | //// println() 97 | //// println("IsDisplayOnDesktop " + IVRSystem.IsDisplayOnDesktop()) 98 | //// 040 78880 99 | // 100 | // vrShutdown() 101 | // } 102 | //} 103 | // 104 | //class Listener(hmd: IVRSystem) : EventListener(hmd) { 105 | // override fun trackedDeviceActivated(left: Boolean) = println("activated $left, index ${event.trackedDeviceIndex}") 106 | // override fun trackedDeviceDeactivated(left: Boolean) = println("deactivated $left") 107 | // override fun trackedDeviceRoleChanged(left: Boolean) = println("role changed $left") 108 | // override fun trackedDeviceUpdated(left: Boolean) = println("updated $left") 109 | // override fun buttonPress(left: Boolean, button: VRButtonId) { 110 | // println("pressed $button, id ${event.trackedDeviceIndex}") 111 | // } 112 | // 113 | // override fun touchpadMove(left: Boolean, pos: Vec2) { 114 | // println("touchPadMove, left controller: $left, pos $pos") 115 | // } 116 | // 117 | // override fun triggerMove(left: Boolean, state: Boolean, limit: Float, value: Float) { 118 | // println("triggerMove, left $left, state $state, limit $limit, value $value") 119 | // } 120 | // 121 | // override fun mouseMove() { 122 | // println("mouseMove") 123 | // } 124 | // 125 | // override fun scroll() { 126 | // println("scroll") 127 | // } 128 | //} -------------------------------------------------------------------------------- /src/test/resources/actions.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [ 3 | { 4 | "name": "/actions/default/in/InteractUI", 5 | "type": "boolean" 6 | }, 7 | { 8 | "name": "/actions/default/in/Teleport", 9 | "type": "boolean" 10 | }, 11 | { 12 | "name": "/actions/default/in/GrabPinch", 13 | "type": "boolean" 14 | },he 15 | { 16 | "name": "/actions/default/in/GrabGrip", 17 | "type": "boolean" 18 | }, 19 | { 20 | "name": "/actions/default/in/Pose", 21 | "type": "pose" 22 | }, 23 | { 24 | "name": "/actions/default/in/SkeletonLeftHand", 25 | "type": "skeleton", 26 | "skeleton": "/skeleton/hand/left" 27 | }, 28 | { 29 | "name": "/actions/default/in/SkeletonRightHand", 30 | "type": "skeleton", 31 | "skeleton": "/skeleton/hand/right" 32 | }, 33 | { 34 | "name": "/actions/default/in/Squeeze", 35 | "type": "vector1", 36 | "requirement": "optional" 37 | }, 38 | { 39 | "name": "/actions/default/in/HeadsetOnHead", 40 | "type": "boolean", 41 | "requirement": "optional" 42 | }, 43 | { 44 | "name": "/actions/default/out/Haptic", 45 | "type": "vibration" 46 | }, 47 | { 48 | "name": "/actions/platformer/in/Move", 49 | "type": "vector2" 50 | }, 51 | { 52 | "name": "/actions/platformer/in/Jump", 53 | "type": "boolean" 54 | }, 55 | { 56 | "name": "/actions/buggy/in/Steering", 57 | "type": "vector2" 58 | }, 59 | { 60 | "name": "/actions/buggy/in/Throttle", 61 | "type": "vector1" 62 | }, 63 | { 64 | "name": "/actions/buggy/in/Brake", 65 | "type": "boolean" 66 | }, 67 | { 68 | "name": "/actions/buggy/in/Reset", 69 | "type": "boolean" 70 | }, 71 | { 72 | "name": "/actions/mixedreality/in/ExternalCamera", 73 | "type": "pose", 74 | "requirement": "optional" 75 | } 76 | ], 77 | "action_sets": [ 78 | { 79 | "name": "/actions/default", 80 | "usage": "single" 81 | }, 82 | { 83 | "name": "/actions/platformer", 84 | "usage": "single" 85 | }, 86 | { 87 | "name": "/actions/buggy", 88 | "usage": "single" 89 | }, 90 | { 91 | "name": "/actions/mixedreality", 92 | "usage": "single" 93 | } 94 | ], 95 | "default_bindings": [ 96 | { 97 | "controller_type": "vive_controller", 98 | "binding_url": "bindings_vive_controller.json" 99 | }, 100 | { 101 | "controller_type": "oculus_touch", 102 | "binding_url": "bindings_oculus_touch.json" 103 | }, 104 | { 105 | "controller_type": "knuckles", 106 | "binding_url": "bindings_knuckles.json" 107 | }, 108 | { 109 | "controller_type": "holographic_controller", 110 | "binding_url": "bindings_holographic_controller.json" 111 | }, 112 | { 113 | "controller_type": "vive", 114 | "binding_url": "binding_vive.json" 115 | }, 116 | { 117 | "controller_type": "vive_pro", 118 | "binding_url": "binding_vive_pro.json" 119 | }, 120 | { 121 | "controller_type": "rift", 122 | "binding_url": "binding_rift.json" 123 | }, 124 | { 125 | "controller_type": "holographic_hmd", 126 | "binding_url": "binding_holographic_hmd.json" 127 | }, 128 | { 129 | "controller_type": "vive_tracker_camera", 130 | "binding_url": "binding_vive_tracker_camera.json" 131 | } 132 | ], 133 | "localization": [ 134 | { 135 | "language_tag": "en_US", 136 | "/actions/default/in/GrabGrip": "Grab Grip", 137 | "/actions/default/in/GrabPinch": "Grab Pinch", 138 | "/actions/default/in/HeadsetOnHead": "Headset on head (proximity sensor)", 139 | "/actions/default/in/InteractUI": "Interact With UI", 140 | "/actions/default/in/Pose": "Pose", 141 | "/actions/default/in/SkeletonLeftHand": "Skeleton (Left)", 142 | "/actions/default/in/SkeletonRightHand": "Skeleton (Right)", 143 | "/actions/default/in/Teleport": "Teleport", 144 | "/actions/default/out/Haptic": "Haptic", 145 | "/actions/platformer/in/Jump": "Jump" 146 | } 147 | ] 148 | } -------------------------------------------------------------------------------- /src/test/resources/cube_texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kotlin-graphics/openvr/d3a155e2e3cfe529286e63b33672f10e597246f6/src/test/resources/cube_texture.png -------------------------------------------------------------------------------- /src/test/resources/hellovr_actions.json: -------------------------------------------------------------------------------- 1 | { 2 | "default_bindings": [ 3 | { 4 | "controller_type" : "vive_controller", 5 | "binding_url" : "hellovr_bindings_vive_controller.json" 6 | } 7 | ], 8 | "actions": [ 9 | { 10 | "name": "/actions/demo/in/HideCubes", 11 | "type": "boolean" 12 | }, 13 | { 14 | "name": "/actions/demo/in/HideThisController", 15 | "type": "boolean" 16 | }, 17 | { 18 | "name": "/actions/demo/in/triggerhaptic", 19 | "type": "boolean" 20 | }, 21 | { 22 | "name": "/actions/demo/in/AnalogInput", 23 | "type": "vector2" 24 | }, 25 | { 26 | "name": "/actions/demo/in/Hand_Right", 27 | "type": "pose" 28 | }, 29 | { 30 | "name": "/actions/demo/in/Hand_Left", 31 | "type": "pose" 32 | }, 33 | { 34 | "name": "/actions/demo/out/haptic_left", 35 | "type": "vibration" 36 | }, 37 | { 38 | "name": "/actions/demo/out/haptic_right", 39 | "type": "vibration" 40 | } 41 | ], 42 | "localization" : [ 43 | { 44 | "language_tag": "en_US", 45 | "/actions/demo/in/HideCubes" : "Hide Cubes", 46 | "/actions/demo/in/HideThisController" : "Hide this Controller", 47 | "/actions/demo/in/triggerhaptic" : "Trigger Haptic Pulse", 48 | "/actions/demo/in/AnalogInput" : "Analog Input", 49 | "/actions/demo/in/Hand_Right" : "Right Hand Pose", 50 | "/actions/demo/in/Hand_Left" : "Left Hand Pose", 51 | "/actions/demo/out/haptic_left" : "Left Haptic Feedback", 52 | "/actions/demo/out/haptic_right" : "Right Haptic Feedback" 53 | } 54 | ] 55 | } 56 | -------------------------------------------------------------------------------- /src/test/resources/hellovr_bindings_vive_controller.json: -------------------------------------------------------------------------------- 1 | { 2 | "bindings" : { 3 | "/actions/demo" : { 4 | "poses" : [ 5 | { 6 | "output" : "/actions/demo/in/hand_left", 7 | "path" : "/user/hand/left/pose/raw" 8 | }, 9 | { 10 | "output" : "/actions/demo/in/hand_right", 11 | "path" : "/user/hand/right/pose/raw" 12 | } 13 | ], 14 | "haptics" : [ 15 | { 16 | "output" : "/actions/demo/out/haptic_right", 17 | "path" : "/user/hand/right/output/haptic" 18 | }, 19 | { 20 | "output" : "/actions/demo/out/haptic_left", 21 | "path" : "/user/hand/left/output/haptic" 22 | } 23 | ], 24 | "sources" : [ 25 | { 26 | "inputs" : { 27 | "click" : { 28 | "output" : "/actions/demo/in/hidecubes" 29 | } 30 | }, 31 | "mode" : "button", 32 | "path" : "/user/hand/right/input/trigger" 33 | }, 34 | { 35 | "inputs" : { 36 | "position" : { 37 | "output" : "/actions/demo/in/analoginput" 38 | } 39 | }, 40 | "mode" : "trackpad", 41 | "path" : "/user/hand/right/input/trackpad" 42 | }, 43 | { 44 | "inputs" : { 45 | "click" : { 46 | "output" : "/actions/demo/in/triggerhaptic" 47 | } 48 | }, 49 | "mode" : "button", 50 | "path" : "/user/hand/right/input/grip" 51 | }, 52 | { 53 | "inputs" : { 54 | "click" : { 55 | "output" : "/actions/demo/in/hidecubes" 56 | } 57 | }, 58 | "mode" : "button", 59 | "path" : "/user/hand/left/input/trigger" 60 | }, 61 | { 62 | "inputs" : { 63 | "click" : { 64 | "output" : "/actions/demo/in/triggerhaptic" 65 | } 66 | }, 67 | "mode" : "button", 68 | "path" : "/user/hand/left/input/grip" 69 | }, 70 | { 71 | "inputs" : { 72 | "click" : { 73 | "output" : "/actions/demo/in/hidethiscontroller" 74 | } 75 | }, 76 | "mode" : "button", 77 | "path" : "/user/hand/left/input/application_menu" 78 | }, 79 | { 80 | "inputs" : { 81 | "click" : { 82 | "output" : "/actions/demo/in/hidethiscontroller" 83 | } 84 | }, 85 | "mode" : "button", 86 | "path" : "/user/hand/right/input/application_menu" 87 | } 88 | ] 89 | } 90 | }, 91 | "controller_type" : "vive_controller", 92 | "description" : "Bindings for the OpenVR SDK \"hellovr_opengl\" demo for the Vive controller", 93 | "name" : "HelloVR bindings for Vive Controller" 94 | } 95 | --------------------------------------------------------------------------------