├── .github └── workflows │ ├── build.yml │ └── publish.yml ├── .gitignore ├── LICENSE ├── README.md ├── controls ├── Index.md ├── Legacy Mixed Reality.md └── Oculus, HP Mixed Reallity, Vive Cosmos.md ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── mcxr-core ├── build.gradle.kts └── src │ └── main │ ├── java │ └── net │ │ └── sorenon │ │ └── mcxr │ │ └── core │ │ ├── JOMLUtil.java │ │ ├── MCXRCore.java │ │ ├── MCXRScale.java │ │ ├── Pose.java │ │ ├── Teleport.java │ │ ├── accessor │ │ └── PlayerExt.java │ │ ├── client │ │ └── MCXRCoreClient.java │ │ ├── config │ │ ├── MCXRCoreConfig.java │ │ └── MCXRCoreConfigImpl.java │ │ └── mixin │ │ ├── LivingEntityAcc.java │ │ ├── LivingEntityMixin.java │ │ ├── PlayerEntityMixin.java │ │ ├── ServerLoginNetworkHandlerAcc.java │ │ └── hands │ │ ├── EntityMixin.java │ │ ├── LivingEntityMixin.java │ │ ├── ServerPlayerGameModeMixin.java │ │ └── client │ │ └── MultiPlayerGameModeMixin.java │ └── resources │ ├── assets │ └── mcxr-core │ │ └── icon.png │ ├── fabric.mod.json │ └── mcxr-core.mixins.json ├── mcxr-play ├── build.gradle.kts ├── loader │ └── openxr_loader.dll └── src │ └── main │ ├── java │ ├── net │ │ ├── sorenon │ │ │ └── mcxr │ │ │ │ └── play │ │ │ │ ├── MCXRGuiManager.java │ │ │ │ ├── MCXROptionsScreen.java │ │ │ │ ├── MCXRPlayClient.java │ │ │ │ ├── MoveDirectionPose.java │ │ │ │ ├── PlayOptions.java │ │ │ │ ├── accessor │ │ │ │ ├── Matrix4fExt.java │ │ │ │ └── MinecraftExt.java │ │ │ │ ├── compat │ │ │ │ └── svc │ │ │ │ │ └── SimpleVoiceChatCompat.java │ │ │ │ ├── gui │ │ │ │ ├── AddServerScreen.java │ │ │ │ ├── QuickChat.java │ │ │ │ ├── QuickMenu.java │ │ │ │ ├── XrChatScreen.java │ │ │ │ ├── XrEditBoxScreen.java │ │ │ │ ├── XrSignEditScreen.java │ │ │ │ └── keyboard │ │ │ │ │ ├── XrAbstract2DKeyboard.java │ │ │ │ │ ├── XrChatKeyboard.java │ │ │ │ │ ├── XrEditBoxKeyboard.java │ │ │ │ │ └── XrSignKeyboard.java │ │ │ │ ├── input │ │ │ │ ├── ControllerPoses.java │ │ │ │ ├── XrInput.java │ │ │ │ ├── actions │ │ │ │ │ ├── Action.java │ │ │ │ │ ├── BoolAction.java │ │ │ │ │ ├── FloatAction.java │ │ │ │ │ ├── InputAction.java │ │ │ │ │ ├── MultiPoseAction.java │ │ │ │ │ ├── SessionAwareAction.java │ │ │ │ │ ├── SingleInputAction.java │ │ │ │ │ └── Vec2fAction.java │ │ │ │ └── actionsets │ │ │ │ │ ├── ActionSet.java │ │ │ │ │ ├── GuiActionSet.java │ │ │ │ │ ├── HandsActionSet.java │ │ │ │ │ └── VanillaGameplayActionSet.java │ │ │ │ ├── mixin │ │ │ │ ├── ChatScreenMixin.java │ │ │ │ ├── EditBoxMixin.java │ │ │ │ ├── FramebufferMixin.java │ │ │ │ ├── KeyboardInputMixin.java │ │ │ │ ├── MultiPlayerScreenMixin.java │ │ │ │ ├── PauseScreenMixin.java │ │ │ │ ├── SystemDetailsMixin.java │ │ │ │ ├── TitleScreenMixin.java │ │ │ │ ├── TutorialToastMixin.java │ │ │ │ ├── accessor │ │ │ │ │ ├── CameraAcc.java │ │ │ │ │ ├── MouseHandlerAcc.java │ │ │ │ │ ├── RenderTargetAcc.java │ │ │ │ │ └── WindowAcc.java │ │ │ │ ├── gui │ │ │ │ │ ├── InputConstantsMixin.java │ │ │ │ │ ├── MinecraftMixin.java │ │ │ │ │ └── ScreenMixin.java │ │ │ │ ├── hands │ │ │ │ │ ├── FireworkRocketEntityMixin.java │ │ │ │ │ ├── GameRendererMixin.java │ │ │ │ │ ├── LivingEntityMixin.java │ │ │ │ │ ├── PlayerMixin.java │ │ │ │ │ └── ThrownItemRendererMixin.java │ │ │ │ ├── rendering │ │ │ │ │ ├── GameRendererMixin.java │ │ │ │ │ ├── GuiMixin.java │ │ │ │ │ ├── LevelRendererMixin.java │ │ │ │ │ ├── Matrix4fMixin.java │ │ │ │ │ ├── MinecraftMixin.java │ │ │ │ │ └── WindowMixin.java │ │ │ │ ├── resources │ │ │ │ │ ├── GameRendererMixin.java │ │ │ │ │ └── ResourceManagerReloadListenerMixin.java │ │ │ │ └── roomscale │ │ │ │ │ └── ClientPlayerEntityMixin.java │ │ │ │ ├── openxr │ │ │ │ ├── MCXRGameRenderer.java │ │ │ │ ├── OpenXRInstance.java │ │ │ │ ├── OpenXRSession.java │ │ │ │ ├── OpenXRState.java │ │ │ │ ├── OpenXRSwapchain.java │ │ │ │ ├── OpenXRSystem.java │ │ │ │ ├── XrException.java │ │ │ │ └── XrRuntimeException.java │ │ │ │ └── rendering │ │ │ │ ├── MCXRCamera.java │ │ │ │ ├── RenderPass.java │ │ │ │ ├── RenderTypeBuilder.java │ │ │ │ ├── UnownedTexture.java │ │ │ │ ├── VrFirstPersonRenderer.java │ │ │ │ └── XrRenderTarget.java │ │ └── tr7zw │ │ │ ├── LICENCE-tr7zw │ │ │ └── MapRenderer.java │ └── org │ │ └── lwjgl │ │ └── openxr │ │ ├── KHRAndroidCreateInstance.java │ │ └── XrInstanceCreateInfoAndroidKHR.java │ └── resources │ ├── assets │ ├── mcxr-play │ │ ├── icon.png │ │ ├── lang │ │ │ ├── de_de.json │ │ │ ├── en_us.json │ │ │ ├── fr_ca.json │ │ │ ├── fr_fr.json │ │ │ ├── it_it.json │ │ │ └── pt_br.json │ │ └── textures │ │ │ └── misc │ │ │ ├── hurt_vr.png │ │ │ ├── vignette_vr.png │ │ │ └── vignette_vr.png.mcmeta │ └── minecraft │ │ └── shaders │ │ ├── core │ │ ├── blit_screen_mcxr.fsh │ │ ├── blit_screen_mcxr.json │ │ ├── blit_screen_mcxr.vsh │ │ ├── blit_screen_mcxr_srgb.fsh │ │ ├── blit_screen_mcxr_srgb.json │ │ ├── gui_blit_screen_mcxr.fsh │ │ ├── gui_blit_screen_mcxr.json │ │ └── gui_blit_screen_mcxr.vsh │ │ └── include │ │ └── mcxr_fxaa.glsl │ ├── fabric.mod.json │ ├── mcxr-play.accesswidener │ └── mcxr-play.mixins.json └── settings.gradle.kts /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: gradle-ci 2 | 3 | on: [ push, pull_request ] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - name: checkout repository 11 | uses: actions/checkout@v3 12 | - name: setup JDK 17 13 | uses: actions/setup-java@v3 14 | with: 15 | java-version: 17 16 | distribution: adopt 17 | - name: update cache 18 | uses: actions/cache@v3 19 | with: 20 | path: | 21 | ~/.gradle/caches 22 | ~/.gradle/loom-cache 23 | ~/.gradle/wrapper 24 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/gradle-wrapper.properties') }} 25 | restore-keys: ${{ runner.os }}-gradle- 26 | 27 | - name: make gradle wrapper executable 28 | run: chmod +x ./gradlew 29 | 30 | - name: build 31 | run: ./gradlew build 32 | 33 | - name: capture build artifacts 34 | uses: actions/upload-artifact@v2 35 | with: 36 | name: Artifacts 37 | path: | 38 | mcxr-core/build/libs 39 | mcxr-play/build/libs 40 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | ## TODO this 2 | #name: gradle-ci 3 | # 4 | #on: 5 | # release: 6 | # types: 7 | # - published 8 | # 9 | #jobs: 10 | # build: 11 | # runs-on: ubuntu-latest 12 | # 13 | # steps: 14 | # - name: checkout repository 15 | # uses: actions/checkout@v3 16 | # - name: setup JDK 17 17 | # uses: actions/setup-java@v3 18 | # with: 19 | # java-version: 17 20 | # distribution: adopt 21 | # - name: update cache 22 | # uses: actions/cache@v3 23 | # with: 24 | # path: | 25 | # ~/.gradle/caches 26 | # ~/.gradle/loom-cache 27 | # ~/.gradle/wrapper 28 | # key: ${{ runner.os }}-gradle-${{ hashFiles('**/gradle-wrapper.properties') }} 29 | # restore-keys: ${{ runner.os }}-gradle- 30 | # 31 | # - name: make gradle wrapper executable 32 | # run: chmod +x ./gradlew 33 | # 34 | # - name: build 35 | # run: ./gradlew build 36 | # 37 | # - if: ${{ contains(github.ref, 'core') }} 38 | # name: Upload MCXR-Core artifacts to Modrinth, Curseforge and GitHub 39 | # uses: Kir-Antipov/mc-publish@v2.0 40 | # with: 41 | # name: MCXR Core 42 | # 43 | # modrinth-id: hcEWWGik 44 | # modrinth-token: ${{ secrets.MODRINTH_TOKEN }} 45 | # 46 | # curseforge-id: 631772 47 | # curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }} 48 | # 49 | # github-token: ${{ secrets.GITHUB_TOKEN }} 50 | # 51 | # files: mcxr-core/build/libs/!(*-@(dev|sources|all)).jar 52 | # 53 | # version-type: beta 54 | # 55 | # loaders: fabric 56 | # 57 | # version-resolver: latest 58 | # 59 | # - if: ${{ contains(github.ref, 'play') }} 60 | # name: Upload MCXR-Play artifacts to Modrinth, Curseforge and GitHub 61 | # uses: Kir-Antipov/mc-publish@v2.0 62 | # with: 63 | # name: MCXR Play 64 | # 65 | # modrinth-id: hcEWWGik 66 | # modrinth-token: ${{ secrets.MODRINTH_TOKEN }} 67 | # 68 | # curseforge-id: 9jVyqpHR 69 | # curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }} 70 | # 71 | # github-token: ${{ secrets.GITHUB_TOKEN }} 72 | # 73 | # files: mcxr-play/build/libs/!(*-@(dev|sources|all)).jar 74 | # 75 | # version-type: beta 76 | # 77 | # loaders: fabric 78 | # 79 | # version-resolver: latest 80 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # gradle 2 | 3 | .gradle/ 4 | build/ 5 | out/ 6 | classes/ 7 | 8 | # eclipse 9 | 10 | *.launch 11 | 12 | # idea 13 | 14 | .idea/ 15 | *.iml 16 | *.ipr 17 | *.iws 18 | 19 | # vscode 20 | 21 | .settings/ 22 | .vscode/ 23 | bin/ 24 | .classpath 25 | .project 26 | 27 | # macos 28 | 29 | *.DS_Store 30 | 31 | # fabric 32 | 33 | run/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Sorenon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /controls/Index.md: -------------------------------------------------------------------------------- 1 | # Controls for The Index Controllers 2 | 3 | ## Gameplay 4 | - Use: Left trigger 5 | - Attack: Right trigger 6 | - Move: Left thumbstick 7 | - Turn: Right thumbstick left + right 8 | - Hotbar: Right thumbstick up + down 9 | - Inventory: Tap left B 10 | - Pause: Hold left B 11 | - Jump: Right A 12 | - Sprint: Right grip 13 | - Sneak: Left grip 14 | - Stand: Left thumbstick click 15 | 16 | ## GUI 17 | - Pickup (Left Click): Right A 18 | - Split (Right Click): Right B 19 | - Quick Move (Shift Click): Left A 20 | - Exit: Left B 21 | - Reset GUI: Left / Right thumbstick click 22 | - Scroll: Right thumbstick -------------------------------------------------------------------------------- /controls/Legacy Mixed Reality.md: -------------------------------------------------------------------------------- 1 | # Controls for Oculus Touch, Index, HP Mixed Reality and Vive Cosmos Controllers 2 | 3 | ## Gameplay 4 | - Use: Left trigger 5 | - Attack: Right trigger 6 | - Move: Left thumbstick 7 | - Turn: Right thumbstick left + right 8 | - Hotbar: Right thumbstick up + down 9 | - Inventory: Tap 'Menu' 10 | - Pause: Hold 'Menu' 11 | - Jump: Right trackpad click 12 | - Sprint: Right grip 13 | - Sneak: Left grip 14 | - Stand: Left thumbstick click 15 | 16 | ## GUI 17 | - Pickup (Left Click): Right trigger click 18 | - Split (Right Click): Right trackpad click 19 | - Quick Move (Shift Click): Left trigger click 20 | - Exit: 'Menu' / Left trackpad click 21 | - Reset GUI: Left / Right thumbstick click 22 | - Scroll: Right thumbstick -------------------------------------------------------------------------------- /controls/Oculus, HP Mixed Reallity, Vive Cosmos.md: -------------------------------------------------------------------------------- 1 | # Controls for Oculus Touch, HP Mixed Reality and Vive Cosmos Controllers 2 | 3 | ## Gameplay 4 | - Use: Left trigger 5 | - Attack: Right trigger 6 | - Move: Left thumbstick 7 | - Turn: Right thumbstick left + right 8 | - Hotbar: Right thumbstick up + down 9 | - Inventory: Tap Y 10 | - Pause: Hold Y 11 | - Jump: A 12 | - Sprint: Right grip 13 | - Sneak: Left grip 14 | - Stand: Left thumbstick click 15 | 16 | ## GUI 17 | - Pickup (Left Click): A 18 | - Split (Right Click): B 19 | - Quick Move (Shift Click): X 20 | - Exit: Y 21 | - Reset GUI: Left / Right thumbstick click 22 | - Scroll: Right thumbstick -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Done to increase the memory available to gradle. 2 | org.gradle.jvmargs=-Xmx1G 3 | 4 | # Fabric Properties 5 | # check these on https://fabricmc.net/use 6 | minecraft_version=1.19 7 | loader_version=0.14.6 8 | 9 | # Mod Properties 10 | core_version = 0.3.0 11 | play_version = 0.3.2 12 | maven_group = net.sorenon 13 | 14 | # Dependencies 15 | fabric_version=0.55.1+1.19 16 | pehkui_version=3.3.2 17 | joml_version=1.10.4 18 | night_config_version=3.6.5 19 | quilt_mappings=1 20 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcxr-org/MCXR/19219f1c11ddd37c5704ea8b3da8d5776c96e06c/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /mcxr-core/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("fabric-loom") version "0.11-SNAPSHOT" 3 | id("io.github.juuxel.loom-quiltflower") version "+" 4 | id("maven-publish") 5 | id("org.quiltmc.quilt-mappings-on-loom") version "4.0.0" 6 | id("org.ajoberstar.grgit") version "5.0.0-rc.3" 7 | } 8 | 9 | base { 10 | archivesBaseName = "mcxr-core" 11 | } 12 | 13 | version = "${properties["core_version"].toString()}+mc${properties["minecraft_version"].toString()}" 14 | group = properties["maven_group"].toString() 15 | 16 | repositories { 17 | maven { 18 | name = "Jitpack" 19 | url = uri("https://jitpack.io") 20 | } 21 | } 22 | 23 | dependencies { 24 | minecraft("com.mojang:minecraft:${properties["minecraft_version"].toString()}") 25 | mappings(loom.layered { 26 | this.addLayer(quiltMappings.mappings("org.quiltmc:quilt-mappings:${properties["minecraft_version"].toString()}+build.${properties["quilt_mappings"].toString()}:v2")) 27 | officialMojangMappings() 28 | }) 29 | modImplementation("net.fabricmc:fabric-loader:${properties["loader_version"].toString()}") 30 | 31 | modImplementation("net.fabricmc.fabric-api:fabric-api:${properties["fabric_version"].toString()}") 32 | 33 | modImplementation("com.github.Virtuoel:Pehkui:${properties["pehkui_version"].toString()}") { 34 | exclude(group = "net.fabricmc.fabric-api") 35 | } 36 | 37 | include(implementation("org.joml:joml:${properties["joml_version"].toString()}")!!) 38 | include(implementation("com.electronwill.night-config:core:${properties["night_config_version"].toString()}")!!) 39 | include(implementation("com.electronwill.night-config:toml:${properties["night_config_version"].toString()}")!!) 40 | } 41 | 42 | tasks { 43 | processResources { 44 | val playVersion = project.properties["play_version"].toString(); 45 | val coreVersion = project.properties["core_version"].toString(); 46 | inputs.property("play_version", playVersion) 47 | inputs.property("core_version", coreVersion) 48 | 49 | filesMatching("fabric.mod.json") { 50 | expand("play_version" to playVersion, "core_version" to coreVersion) 51 | } 52 | } 53 | 54 | withType { 55 | options.release.set(17) 56 | } 57 | 58 | jar { 59 | from("LICENSE") { 60 | rename { "${it}_${project.properties["archivesBaseName"].toString()}" } 61 | } 62 | } 63 | } 64 | 65 | java { 66 | sourceCompatibility = JavaVersion.VERSION_17 67 | targetCompatibility = JavaVersion.VERSION_17 68 | withSourcesJar() 69 | } 70 | 71 | // configure the maven publication 72 | publishing { 73 | publications { 74 | create("mod") { 75 | from(components["java"]) 76 | } 77 | } 78 | 79 | // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. 80 | repositories { 81 | // Add repositories to publish to here. 82 | // Notice: This block does NOT have the same function as the block in the top level. 83 | // The repositories here will be used for publishing your artifact, not for 84 | // retrieving dependencies. 85 | } 86 | } 87 | 88 | loom { 89 | runs { 90 | create("coreClient") { 91 | client() 92 | configName = "MCXR Core Client" 93 | ideConfigGenerated(true) 94 | } 95 | 96 | create("coreServer") { 97 | server() 98 | configName = "MCXR Core Server" 99 | ideConfigGenerated(true) 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /mcxr-core/src/main/java/net/sorenon/mcxr/core/JOMLUtil.java: -------------------------------------------------------------------------------- 1 | package net.sorenon.mcxr.core; 2 | 3 | import com.mojang.math.Quaternion; 4 | import net.minecraft.world.phys.Vec3; 5 | import org.joml.*; 6 | 7 | public class JOMLUtil { 8 | 9 | /** 10 | * Vector 3 start 11 | */ 12 | 13 | public static Vec3 convert(Vector3dc vec) { 14 | return new Vec3(vec.x(), vec.y(), vec.z()); 15 | } 16 | 17 | public static Vec3 convert(Vector3fc vec) { 18 | return new Vec3(vec.x(), vec.y(), vec.z()); 19 | } 20 | 21 | public static Vector3d convert(Vec3 vec) { 22 | return new Vector3d(vec.x, vec.y, vec.z); 23 | } 24 | 25 | /** 26 | * Quaternion start 27 | */ 28 | 29 | public Quaternion convert(Quaterniondc quat) { 30 | return new Quaternion((float) quat.x(), (float) quat.y(), (float) quat.z(), (float) quat.w()); 31 | } 32 | 33 | public static Quaternion convert(Quaternionfc quat) { 34 | return new Quaternion(quat.x(), quat.y(), quat.z(), quat.w()); 35 | } 36 | 37 | public static Quaternionf convert(Quaternion quat) { 38 | return new Quaternionf(quat.i(), quat.j(), quat.k(), quat.r()); 39 | } 40 | 41 | public static Quaterniond convertd(Quaternion quat) { 42 | return new Quaterniond(quat.i(), quat.j(), quat.k(), quat.r()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /mcxr-core/src/main/java/net/sorenon/mcxr/core/MCXRScale.java: -------------------------------------------------------------------------------- 1 | package net.sorenon.mcxr.core; 2 | 3 | import net.fabricmc.loader.api.FabricLoader; 4 | import net.minecraft.world.entity.Entity; 5 | import virtuoel.pehkui.api.ScaleTypes; 6 | import virtuoel.pehkui.util.ScaleUtils; 7 | 8 | public class MCXRScale { 9 | public static float getScale(Entity entity) { 10 | return getScale(entity, 1.0f); 11 | } 12 | 13 | public static float getScale(Entity entity, float delta) { 14 | if (FabricLoader.getInstance().isModLoaded("pehkui")) { 15 | var scaleData = ScaleTypes.BASE.getScaleData(entity); 16 | return scaleData.getScale(delta); 17 | } else { 18 | return 1; 19 | } 20 | } 21 | 22 | public static float getMotionScale(Entity entity) { 23 | if (FabricLoader.getInstance().isModLoaded("pehkui")) { 24 | return ScaleUtils.getMotionScale(entity); 25 | } else { 26 | return 1; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /mcxr-core/src/main/java/net/sorenon/mcxr/core/Pose.java: -------------------------------------------------------------------------------- 1 | package net.sorenon.mcxr.core; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import net.minecraft.util.Mth; 5 | import org.joml.Math; 6 | import org.joml.Quaternionf; 7 | import org.joml.Vector3f; 8 | 9 | public class Pose { 10 | 11 | public final Quaternionf orientation = new Quaternionf(); 12 | public final Vector3f pos = new Vector3f(); 13 | 14 | public void set(Pose pose) { 15 | pos.set(pose.pos); 16 | orientation.set(pose.orientation); 17 | } 18 | 19 | public Vector3f getPos() { 20 | return pos; 21 | } 22 | 23 | public Quaternionf getOrientation() { 24 | return orientation; 25 | } 26 | 27 | public float getMCYaw() { 28 | return getMCYaw(orientation); 29 | } 30 | 31 | public float getMCPitch() { 32 | return getMCPitch(orientation); 33 | } 34 | 35 | public void write(ByteBuf buf) { 36 | buf.writeFloat(pos.x).writeFloat(pos.y).writeFloat(pos.z); 37 | buf.writeFloat(orientation.x).writeFloat(orientation.y).writeFloat(orientation.z).writeFloat(orientation.w); 38 | } 39 | 40 | public void read(ByteBuf buf) { 41 | this.pos.set(buf.readFloat(), buf.readFloat(), buf.readFloat()); 42 | this.orientation.set(buf.readFloat(), buf.readFloat(), buf.readFloat(), buf.readFloat()); 43 | } 44 | 45 | public static float getMCYaw(Quaternionf orientation) { 46 | return getMCYaw(orientation, new Vector3f(0, 0, -1)); 47 | } 48 | 49 | public static float getMCYaw(Quaternionf orientation, Vector3f normal) { 50 | orientation.transform(normal); 51 | float yaw = getYawFromNormal(normal); 52 | return (float) -Math.toDegrees(yaw) + 180; 53 | } 54 | 55 | public static float getMCPitch(Quaternionf orientation) { 56 | return getMCPitch(orientation, new Vector3f(0, 0, -1)); 57 | } 58 | 59 | public static float getMCPitch(Quaternionf orientation, Vector3f normal) { 60 | orientation.transform(normal); 61 | float pitch = (float) Math.asin(Mth.clamp(normal.y, -0.999999999, 0.999999999)); 62 | return (float) -Math.toDegrees(pitch); 63 | } 64 | 65 | public static float getYawFromNormal(Vector3f normal) { 66 | if (normal.z < 0) { 67 | return (float) java.lang.Math.atan(normal.x / normal.z); 68 | } 69 | if (normal.z == 0) { 70 | return (float) (Math.PI / 2 * -Mth.sign(normal.x)); 71 | } 72 | if (normal.z > 0) { 73 | return (float) (java.lang.Math.atan(normal.x / normal.z) + Math.PI); 74 | } 75 | return 0; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /mcxr-core/src/main/java/net/sorenon/mcxr/core/Teleport.java: -------------------------------------------------------------------------------- 1 | package net.sorenon.mcxr.core; 2 | 3 | import net.minecraft.core.Direction; 4 | import net.minecraft.world.entity.player.Player; 5 | import net.minecraft.world.level.ClipContext; 6 | import net.minecraft.world.phys.AABB; 7 | import net.minecraft.world.phys.HitResult; 8 | import net.minecraft.world.phys.Vec3; 9 | import net.minecraft.world.phys.shapes.CollisionContext; 10 | import net.minecraft.world.phys.shapes.Shapes; 11 | import net.minecraft.world.phys.shapes.VoxelShape; 12 | import org.jetbrains.annotations.Nullable; 13 | import oshi.util.tuples.Pair; 14 | 15 | public class Teleport { 16 | 17 | public static Pair fireRayFromHand(Player player, Vec3 start, Vec3 direction) { 18 | var level = player.level; 19 | 20 | var hitResult = level.clip(new ClipContext(start, start.add(direction.scale(7)), ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, player)); 21 | var hitPos = hitResult.getLocation(); 22 | 23 | if (hitResult.getType() != HitResult.Type.MISS) { 24 | var blockPos = hitResult.getBlockPos(); 25 | 26 | var dims = player.getDimensions(player.getPose()); 27 | if (hitResult.getDirection() == Direction.UP) { 28 | VoxelShape shape = Shapes.create(AABB.ofSize(hitPos, dims.width + 1.0E-6, dims.height + 1.0E-6, dims.width + 1.0E-6)); 29 | var optional = level.findFreePosition(player, shape, hitPos.add(0, dims.height / 2, 0), dims.width, dims.height, dims.width); 30 | if (optional.isPresent()) { 31 | return new Pair<>(null, optional.get().subtract(0, dims.height / 2, 0)); 32 | } 33 | } else if (level.getBlockState(blockPos.above()).getCollisionShape(level, blockPos.above(), CollisionContext.of(player)).isEmpty()) { 34 | var blockState = level.getBlockState(blockPos).getCollisionShape(level, blockPos, CollisionContext.of(player)); 35 | var pos = new Vec3(blockPos.getX() + 0.5f, blockPos.getY() + blockState.max(Direction.Axis.Y), blockPos.getZ() + 0.5f); 36 | if (level.noCollision(dims.makeBoundingBox(pos))) { 37 | return new Pair<>(hitPos, pos); 38 | } 39 | } 40 | } 41 | 42 | return new Pair<>(hitPos, null); 43 | } 44 | 45 | public static Pair fireFallRay(Player player, Vec3 hitPos1) { 46 | var level = player.level; 47 | 48 | var hitResult = level.clip(new ClipContext(hitPos1, hitPos1.subtract(0, 5, 0), ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, player)); 49 | // For some reason doing a large raycast skips the first few centimeters 50 | if (hitResult.getType() == HitResult.Type.MISS) { 51 | hitResult = level.clip(new ClipContext(hitPos1, hitPos1.subtract(0, 1000, 0), ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, player)); 52 | } 53 | 54 | var hitPos2 = hitResult.getLocation(); 55 | 56 | var dims = player.getDimensions(player.getPose()); 57 | VoxelShape shape = Shapes.create(AABB.ofSize(hitPos2, dims.width + 1.0E-6, dims.height + 1.0E-6, dims.width + 1.0E-6)); 58 | return level 59 | .findFreePosition(player, shape, hitPos2.add(0, dims.height / 2, 0), dims.width, dims.height, dims.width) 60 | .map(vec3 -> new Pair<>(vec3.subtract(0, dims.height / 2, 0), true)) 61 | .orElseGet(() -> new Pair<>(hitPos2, false)); 62 | } 63 | 64 | @Nullable 65 | public static Vec3 tp(Player player, Vec3 start, Vec3 direction) { 66 | var stage1 = fireRayFromHand(player, start, direction); 67 | var hitPos1 = stage1.getA(); 68 | var finalPos1 = stage1.getB(); 69 | 70 | if (finalPos1 != null) { 71 | return finalPos1; 72 | } 73 | 74 | hitPos1 = hitPos1.subtract(direction.scale(0.05)); 75 | 76 | var stage2 = fireFallRay(player, hitPos1); 77 | var hitPos2 = stage2.getA(); 78 | 79 | if (stage2.getB()) { 80 | return hitPos2; 81 | } 82 | 83 | return null; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /mcxr-core/src/main/java/net/sorenon/mcxr/core/accessor/PlayerExt.java: -------------------------------------------------------------------------------- 1 | package net.sorenon.mcxr.core.accessor; 2 | 3 | import net.minecraft.world.entity.HumanoidArm; 4 | import net.sorenon.mcxr.core.Pose; 5 | 6 | public interface PlayerExt { 7 | 8 | Pose getHeadPose(); 9 | 10 | Pose getLeftHandPose(); 11 | 12 | Pose getRightHandPose(); 13 | 14 | default Pose getPoseForArm(HumanoidArm arm) { 15 | if (arm == HumanoidArm.LEFT) { 16 | return getLeftHandPose(); 17 | } else { 18 | return getRightHandPose(); 19 | } 20 | } 21 | 22 | void setIsXr(boolean isXr); 23 | 24 | boolean isXR(); 25 | 26 | ThreadLocal getOverrideTransform(); 27 | } 28 | -------------------------------------------------------------------------------- /mcxr-core/src/main/java/net/sorenon/mcxr/core/client/MCXRCoreClient.java: -------------------------------------------------------------------------------- 1 | package net.sorenon.mcxr.core.client; 2 | 3 | import net.fabricmc.api.ClientModInitializer; 4 | import net.fabricmc.fabric.api.client.networking.v1.ClientLoginNetworking; 5 | import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; 6 | import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; 7 | import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; 8 | import net.fabricmc.loader.api.FabricLoader; 9 | import net.minecraft.network.FriendlyByteBuf; 10 | import net.minecraft.world.entity.player.Player; 11 | import net.sorenon.mcxr.core.MCXRCore; 12 | import net.sorenon.mcxr.core.Pose; 13 | import net.sorenon.mcxr.core.accessor.PlayerExt; 14 | import net.sorenon.mcxr.core.config.MCXRCoreConfigImpl; 15 | import org.apache.logging.log4j.LogManager; 16 | import org.apache.logging.log4j.Logger; 17 | 18 | import java.util.concurrent.CompletableFuture; 19 | 20 | import static net.sorenon.mcxr.core.MCXRCore.POSES; 21 | 22 | public class MCXRCoreClient implements ClientModInitializer { 23 | 24 | public static MCXRCoreClient INSTANCE; 25 | 26 | private static final Logger LOGGER = LogManager.getLogger("MCXR Core"); 27 | 28 | public boolean playInstalled = false; 29 | 30 | @Override 31 | public void onInitializeClient() { 32 | INSTANCE = this; 33 | 34 | playInstalled = FabricLoader.getInstance().isModLoaded("mcxr-play"); 35 | 36 | ClientLoginNetworking.registerGlobalReceiver(MCXRCore.S2C_CONFIG, (client, handler, bufIn, listenerAdder) -> { 37 | var buf = PacketByteBufs.create(); 38 | LOGGER.info("Received login packet"); 39 | buf.writeBoolean(playInstalled); 40 | ((MCXRCoreConfigImpl) MCXRCore.getCoreConfig()).xrEnabled = true; 41 | return CompletableFuture.completedFuture(buf); 42 | }); 43 | 44 | ClientPlayConnectionEvents.DISCONNECT.register((handler, client) -> 45 | ((MCXRCoreConfigImpl) MCXRCore.getCoreConfig()).xrEnabled = false 46 | ); 47 | } 48 | 49 | public void setPlayerPoses( 50 | Player player, 51 | Pose headPose, 52 | Pose leftHandPose, 53 | Pose rightHandPose, 54 | // float height, 55 | float handAngleAdjust 56 | ) { 57 | PlayerExt acc = (PlayerExt) player; 58 | acc.getHeadPose().set(headPose); 59 | acc.getLeftHandPose().set(leftHandPose); 60 | acc.getRightHandPose().set(rightHandPose); 61 | // acc.setHeight(height); 62 | 63 | acc.getHeadPose().pos.sub((float) player.getX(), (float) player.getY(), (float) player.getZ()); 64 | acc.getLeftHandPose().pos.sub((float) player.getX(), (float) player.getY(), (float) player.getZ()); 65 | acc.getRightHandPose().pos.sub((float) player.getX(), (float) player.getY(), (float) player.getZ()); 66 | 67 | acc.getLeftHandPose().orientation.rotateX(handAngleAdjust); 68 | acc.getRightHandPose().orientation.rotateX(handAngleAdjust); 69 | 70 | FriendlyByteBuf buf = PacketByteBufs.create(); 71 | acc.getHeadPose().write(buf); 72 | acc.getLeftHandPose().write(buf); 73 | acc.getRightHandPose().write(buf); 74 | // buf.writeFloat(height); 75 | 76 | ClientPlayNetworking.send(POSES, buf); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /mcxr-core/src/main/java/net/sorenon/mcxr/core/config/MCXRCoreConfig.java: -------------------------------------------------------------------------------- 1 | package net.sorenon.mcxr.core.config; 2 | 3 | public interface MCXRCoreConfig { 4 | boolean supportsMCXR(); 5 | 6 | boolean dynamicPlayerHeight(); 7 | 8 | boolean dynamicPlayerEyeHeight(); 9 | 10 | boolean thinnerPlayerBoundingBox(); 11 | 12 | boolean controllerRaytracing(); 13 | 14 | boolean roomscaleMovement(); 15 | 16 | boolean handBasedItemUsage(); 17 | } 18 | -------------------------------------------------------------------------------- /mcxr-core/src/main/java/net/sorenon/mcxr/core/config/MCXRCoreConfigImpl.java: -------------------------------------------------------------------------------- 1 | package net.sorenon.mcxr.core.config; 2 | 3 | public class MCXRCoreConfigImpl implements MCXRCoreConfig { 4 | 5 | public boolean xrEnabled; 6 | 7 | @Override 8 | public boolean supportsMCXR() { 9 | return xrEnabled; 10 | } 11 | 12 | @Override 13 | public boolean dynamicPlayerHeight() { 14 | return xrEnabled; 15 | } 16 | 17 | @Override 18 | public boolean dynamicPlayerEyeHeight() { 19 | return xrEnabled; 20 | } 21 | 22 | @Override 23 | public boolean thinnerPlayerBoundingBox() { 24 | return xrEnabled; 25 | } 26 | 27 | @Override 28 | public boolean controllerRaytracing() { 29 | return true; 30 | } 31 | 32 | @Override 33 | public boolean roomscaleMovement() { 34 | return true; 35 | } 36 | 37 | @Override 38 | public boolean handBasedItemUsage() { 39 | return xrEnabled; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /mcxr-core/src/main/java/net/sorenon/mcxr/core/mixin/LivingEntityAcc.java: -------------------------------------------------------------------------------- 1 | package net.sorenon.mcxr.core.mixin; 2 | 3 | import net.minecraft.world.entity.EntityDimensions; 4 | import net.minecraft.world.entity.LivingEntity; 5 | import net.minecraft.world.entity.Pose; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.gen.Invoker; 8 | 9 | @Mixin(LivingEntity.class) 10 | public interface LivingEntityAcc { 11 | 12 | @Invoker 13 | float callGetStandingEyeHeight(Pose pose, EntityDimensions entityDimensions); 14 | } 15 | -------------------------------------------------------------------------------- /mcxr-core/src/main/java/net/sorenon/mcxr/core/mixin/LivingEntityMixin.java: -------------------------------------------------------------------------------- 1 | package net.sorenon.mcxr.core.mixin; 2 | 3 | import net.minecraft.world.entity.Entity; 4 | import net.minecraft.world.entity.EntityDimensions; 5 | import net.minecraft.world.entity.EntityType; 6 | import net.minecraft.world.entity.LivingEntity; 7 | import net.minecraft.world.entity.Pose; 8 | import net.minecraft.world.level.Level; 9 | import net.sorenon.mcxr.core.MCXRCore; 10 | import net.sorenon.mcxr.core.accessor.PlayerExt; 11 | import org.spongepowered.asm.mixin.Mixin; 12 | import org.spongepowered.asm.mixin.injection.At; 13 | import org.spongepowered.asm.mixin.injection.Inject; 14 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 15 | 16 | @Mixin(LivingEntity.class) 17 | public abstract class LivingEntityMixin extends Entity { 18 | 19 | public LivingEntityMixin(EntityType type, Level world) { 20 | super(type, world); 21 | } 22 | 23 | @Inject(method = "getEyeHeight", at = @At("HEAD"), cancellable = true) 24 | void overrideEyeHeight(Pose pose, EntityDimensions dimensions, CallbackInfoReturnable cir) { 25 | if (!MCXRCore.getCoreConfig().dynamicPlayerEyeHeight()) { 26 | return; 27 | } 28 | 29 | if (this instanceof PlayerExt acc && acc.isXR()) { 30 | cir.setReturnValue(acc.getHeadPose().pos.y); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /mcxr-core/src/main/java/net/sorenon/mcxr/core/mixin/PlayerEntityMixin.java: -------------------------------------------------------------------------------- 1 | package net.sorenon.mcxr.core.mixin; 2 | 3 | import net.minecraft.world.damagesource.DamageSource; 4 | import net.minecraft.world.entity.EntityDimensions; 5 | import net.minecraft.world.entity.EntityType; 6 | import net.minecraft.world.entity.HumanoidArm; 7 | import net.minecraft.world.entity.LivingEntity; 8 | import net.minecraft.world.entity.player.Player; 9 | import net.minecraft.world.level.Level; 10 | import net.minecraft.world.phys.AABB; 11 | import net.minecraft.world.phys.Vec3; 12 | import net.minecraft.world.phys.shapes.VoxelShape; 13 | import net.sorenon.mcxr.core.MCXRCore; 14 | import net.sorenon.mcxr.core.MCXRScale; 15 | import net.sorenon.mcxr.core.Pose; 16 | import net.sorenon.mcxr.core.accessor.PlayerExt; 17 | import org.spongepowered.asm.mixin.Final; 18 | import org.spongepowered.asm.mixin.Mixin; 19 | import org.spongepowered.asm.mixin.Shadow; 20 | import org.spongepowered.asm.mixin.Unique; 21 | import org.spongepowered.asm.mixin.injection.At; 22 | import org.spongepowered.asm.mixin.injection.Inject; 23 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 24 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 25 | 26 | import java.util.List; 27 | import java.util.Map; 28 | 29 | import static net.minecraft.world.entity.player.Player.STANDING_DIMENSIONS; 30 | 31 | @Mixin(value = Player.class, priority = 10_000 /*Pehuki*/) 32 | public abstract class PlayerEntityMixin extends LivingEntity implements PlayerExt { 33 | 34 | @Shadow 35 | @Final 36 | private static Map POSES; 37 | 38 | @Shadow 39 | public abstract boolean hurt(DamageSource source, float amount); 40 | 41 | @Unique 42 | public boolean isXr = false; 43 | 44 | @Unique 45 | public Pose headPose = new Pose(); 46 | 47 | @Unique 48 | public Pose leftHandPose = new Pose(); 49 | 50 | @Unique 51 | public Pose rightHandPose = new Pose(); 52 | 53 | @Unique 54 | public float height = 0; 55 | 56 | @Unique 57 | public ThreadLocal overrideTransform = ThreadLocal.withInitial(() -> null); 58 | 59 | protected PlayerEntityMixin(EntityType entityType, Level world) { 60 | super(entityType, world); 61 | } 62 | 63 | @Inject(method = "tick", at = @At("HEAD")) 64 | void preTick(CallbackInfo ci) { 65 | if (this.isXR()) { 66 | this.refreshDimensions(); 67 | } 68 | } 69 | 70 | @Inject(method = "getDimensions", at = @At("HEAD"), cancellable = true) 71 | void overrideDims(net.minecraft.world.entity.Pose pose, CallbackInfoReturnable cir) { 72 | boolean dynamicHeight = MCXRCore.getCoreConfig().dynamicPlayerHeight(); 73 | boolean thinnerBB = MCXRCore.getCoreConfig().thinnerPlayerBoundingBox(); 74 | if (!dynamicHeight && !thinnerBB) { 75 | return; 76 | } 77 | 78 | EntityDimensions vanilla = POSES.getOrDefault(pose, STANDING_DIMENSIONS); 79 | 80 | if (this.isXR()) { 81 | final float scale = MCXRScale.getScale(this); 82 | 83 | float width = vanilla.width; 84 | if (thinnerBB) { 85 | width = 0.5f; 86 | } 87 | 88 | if (dynamicHeight) { 89 | final float minHeight = 0.5f * scale; 90 | final float currentHeight = this.getBbHeight(); 91 | // final float wantedHeight = (headPose.pos.y - (float) this.position().y + 0.125f * scale); 92 | final float wantedHeight = height + 0.125f * scale; 93 | final float deltaHeight = wantedHeight - currentHeight; 94 | 95 | if (deltaHeight <= 0) { 96 | cir.setReturnValue( 97 | EntityDimensions.scalable(width * scale, Math.max(wantedHeight, minHeight)) 98 | ); 99 | return; 100 | } 101 | 102 | AABB currentSize = this.getBoundingBox(); 103 | List list = this.level.getEntityCollisions(this, currentSize.expandTowards(0, deltaHeight, 0)); 104 | final double maxDeltaHeight = collideBoundingBox(this, new Vec3(0, deltaHeight, 0), currentSize, this.level, list).y; 105 | 106 | cir.setReturnValue( 107 | EntityDimensions.scalable(width * scale, Math.max(currentHeight + (float) maxDeltaHeight, minHeight)) 108 | ); 109 | } else { 110 | cir.setReturnValue( 111 | EntityDimensions.scalable(width * scale, vanilla.height) 112 | ); 113 | } 114 | } 115 | } 116 | 117 | @Override 118 | public Pose getHeadPose() { 119 | return headPose; 120 | } 121 | 122 | @Override 123 | public Pose getLeftHandPose() { 124 | return leftHandPose; 125 | } 126 | 127 | @Override 128 | public Pose getRightHandPose() { 129 | return rightHandPose; 130 | } 131 | 132 | @Override 133 | public void setIsXr(boolean isXr) { 134 | this.isXr = isXr; 135 | } 136 | 137 | @Override 138 | public boolean isXR() { 139 | return isXr; 140 | } 141 | 142 | @Override 143 | public ThreadLocal getOverrideTransform() { 144 | return this.overrideTransform; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /mcxr-core/src/main/java/net/sorenon/mcxr/core/mixin/ServerLoginNetworkHandlerAcc.java: -------------------------------------------------------------------------------- 1 | package net.sorenon.mcxr.core.mixin; 2 | 3 | import com.mojang.authlib.GameProfile; 4 | import net.minecraft.server.network.ServerLoginPacketListenerImpl; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | @Mixin(ServerLoginPacketListenerImpl.class) 9 | public interface ServerLoginNetworkHandlerAcc { 10 | 11 | @Accessor 12 | GameProfile getGameProfile(); 13 | } 14 | -------------------------------------------------------------------------------- /mcxr-core/src/main/java/net/sorenon/mcxr/core/mixin/hands/EntityMixin.java: -------------------------------------------------------------------------------- 1 | package net.sorenon.mcxr.core.mixin.hands; 2 | 3 | import net.minecraft.world.entity.Entity; 4 | import net.minecraft.world.entity.player.Player; 5 | import net.minecraft.world.phys.Vec3; 6 | import net.sorenon.mcxr.core.JOMLUtil; 7 | import net.sorenon.mcxr.core.Pose; 8 | import net.sorenon.mcxr.core.accessor.PlayerExt; 9 | import org.joml.Quaternionf; 10 | import org.joml.Vector3f; 11 | import org.spongepowered.asm.mixin.Mixin; 12 | import org.spongepowered.asm.mixin.Shadow; 13 | import org.spongepowered.asm.mixin.injection.At; 14 | import org.spongepowered.asm.mixin.injection.Inject; 15 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 16 | 17 | @Mixin(Entity.class) 18 | public abstract class EntityMixin { 19 | 20 | @Shadow 21 | public abstract Vec3 position(); 22 | 23 | @Shadow 24 | public abstract float getEyeHeight(); 25 | 26 | @Shadow private Vec3 position; 27 | 28 | @Inject(method = "getEyeY", at = @At("HEAD"), cancellable = true) 29 | void overrideEyeY(CallbackInfoReturnable cir) { 30 | if (this instanceof PlayerExt playerExt && playerExt.getOverrideTransform().get() != null) { 31 | cir.setReturnValue(this.position().y + this.getEyeHeight()); 32 | } 33 | } 34 | 35 | @Inject(method = "getEyeHeight()F", at = @At("HEAD"), cancellable = true) 36 | void overrideEyeHeight(CallbackInfoReturnable cir) { 37 | if (this instanceof PlayerExt playerExt && playerExt.getOverrideTransform().get() != null) { 38 | cir.setReturnValue(0.1F); 39 | } 40 | } 41 | 42 | @Inject(method = "position", at = @At("HEAD"), cancellable = true) 43 | void overridePosition(CallbackInfoReturnable cir) { 44 | if (this instanceof PlayerExt playerExt && playerExt.getOverrideTransform().get() != null) { 45 | cir.setReturnValue(JOMLUtil.convert(playerExt.getPoseForArm(playerExt.getOverrideTransform().get()).getPos()).add(this.position)); 46 | } 47 | } 48 | 49 | @Inject(method = "getX()D", at = @At("HEAD"), cancellable = true) 50 | void overrideXPosition(CallbackInfoReturnable cir) { 51 | if (this instanceof PlayerExt playerExt && playerExt.getOverrideTransform().get() != null) { 52 | cir.setReturnValue(this.position().x); 53 | } 54 | } 55 | 56 | @Inject(method = "getY()D", at = @At("HEAD"), cancellable = true) 57 | void overrideYPosition(CallbackInfoReturnable cir) { 58 | if (this instanceof PlayerExt playerExt && playerExt.getOverrideTransform().get() != null) { 59 | cir.setReturnValue(this.position().y); 60 | } 61 | } 62 | 63 | @Inject(method = "getZ()D", at = @At("HEAD"), cancellable = true) 64 | void overrideZPosition(CallbackInfoReturnable cir) { 65 | if (this instanceof PlayerExt playerExt && playerExt.getOverrideTransform().get() != null) { 66 | cir.setReturnValue(this.position().z); 67 | } 68 | } 69 | 70 | @Inject(method = "getXRot", at = @At("HEAD"), cancellable = true) 71 | void overrideXRot(CallbackInfoReturnable cir) { 72 | if (this instanceof PlayerExt playerExt && playerExt.getOverrideTransform().get() != null) { 73 | cir.setReturnValue(Pose.getMCPitch(playerExt.getPoseForArm(playerExt.getOverrideTransform().get()).getOrientation(), new Vector3f(0, -1, 0))); 74 | } 75 | } 76 | 77 | @Inject(method = "getYRot", at = @At("HEAD"), cancellable = true) 78 | void overrideYRot(CallbackInfoReturnable cir) { 79 | if (this instanceof PlayerExt playerExt && playerExt.getOverrideTransform() != null && playerExt.getOverrideTransform().get() != null) { 80 | cir.setReturnValue(Pose.getMCYaw(playerExt.getPoseForArm(playerExt.getOverrideTransform().get()).getOrientation(), new Vector3f(0, -1, 0))); 81 | } 82 | } 83 | 84 | @Inject(method = "getUpVector", at = @At("HEAD"), cancellable = true) 85 | void overrideUpVector(float f, CallbackInfoReturnable cir) { 86 | if (this instanceof PlayerExt playerExt && playerExt.getOverrideTransform().get() != null) { 87 | cir.setReturnValue(JOMLUtil.convert(playerExt.getPoseForArm(playerExt.getOverrideTransform().get()).getOrientation().transform(new Vector3f(0, 0, 1)))); 88 | } 89 | } 90 | 91 | @Inject(method = "getViewVector", at = @At("HEAD"), cancellable = true) 92 | void overrideViewVector(float f, CallbackInfoReturnable cir) { 93 | if (this instanceof PlayerExt playerExt && playerExt.getOverrideTransform().get() != null) { 94 | cir.setReturnValue(JOMLUtil.convert(playerExt.getPoseForArm(playerExt.getOverrideTransform().get()).getOrientation().transform(new Vector3f(0, -1, 0)))); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /mcxr-core/src/main/java/net/sorenon/mcxr/core/mixin/hands/LivingEntityMixin.java: -------------------------------------------------------------------------------- 1 | package net.sorenon.mcxr.core.mixin.hands; 2 | 3 | import net.minecraft.world.InteractionHand; 4 | import net.minecraft.world.entity.Entity; 5 | import net.minecraft.world.entity.EntityType; 6 | import net.minecraft.world.entity.LivingEntity; 7 | import net.minecraft.world.level.Level; 8 | import net.sorenon.mcxr.core.MCXRCore; 9 | import net.sorenon.mcxr.core.accessor.PlayerExt; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.Shadow; 12 | import org.spongepowered.asm.mixin.injection.At; 13 | import org.spongepowered.asm.mixin.injection.Inject; 14 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 15 | 16 | @Mixin(LivingEntity.class) 17 | public abstract class LivingEntityMixin extends Entity { 18 | 19 | @Shadow public abstract InteractionHand getUsedItemHand(); 20 | 21 | public LivingEntityMixin(EntityType entityType, Level level) { 22 | super(entityType, level); 23 | } 24 | 25 | @Inject(method = "releaseUsingItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;releaseUsing(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/entity/LivingEntity;I)V")) 26 | void preReleaseUsing(CallbackInfo ci) { 27 | if (this instanceof PlayerExt playerExt && playerExt.isXR() && MCXRCore.getCoreConfig().handBasedItemUsage()) { 28 | playerExt.getOverrideTransform().set(MCXRCore.handToArm((LivingEntity)(Object)this, this.getUsedItemHand())); 29 | } 30 | } 31 | 32 | @Inject(method = "releaseUsingItem", at = @At(value = "INVOKE", shift = At.Shift.AFTER, target = "Lnet/minecraft/world/item/ItemStack;releaseUsing(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/entity/LivingEntity;I)V")) 33 | void postReleaseUsing(CallbackInfo ci) { 34 | if (this instanceof PlayerExt playerExt && playerExt.isXR()) { 35 | playerExt.getOverrideTransform().set(null); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /mcxr-core/src/main/java/net/sorenon/mcxr/core/mixin/hands/ServerPlayerGameModeMixin.java: -------------------------------------------------------------------------------- 1 | package net.sorenon.mcxr.core.mixin.hands; 2 | 3 | import net.minecraft.server.level.ServerPlayer; 4 | import net.minecraft.server.level.ServerPlayerGameMode; 5 | import net.minecraft.world.InteractionHand; 6 | import net.minecraft.world.InteractionResult; 7 | import net.minecraft.world.item.ItemStack; 8 | import net.minecraft.world.level.Level; 9 | import net.sorenon.mcxr.core.MCXRCore; 10 | import net.sorenon.mcxr.core.accessor.PlayerExt; 11 | import org.spongepowered.asm.mixin.Mixin; 12 | import org.spongepowered.asm.mixin.injection.At; 13 | import org.spongepowered.asm.mixin.injection.Inject; 14 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 15 | 16 | @Mixin(ServerPlayerGameMode.class) 17 | public class ServerPlayerGameModeMixin { 18 | 19 | @Inject(method = "useItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;use(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/InteractionHand;)Lnet/minecraft/world/InteractionResultHolder;")) 20 | void preUse(ServerPlayer serverPlayer, 21 | Level level, 22 | ItemStack itemStack, 23 | InteractionHand interactionHand, 24 | CallbackInfoReturnable cir) { 25 | PlayerExt playerExt = ((PlayerExt) serverPlayer); 26 | if (playerExt.isXR() && MCXRCore.getCoreConfig().handBasedItemUsage()) { 27 | playerExt.getOverrideTransform().set(MCXRCore.handToArm(serverPlayer, interactionHand)); 28 | } 29 | } 30 | 31 | @Inject(method = "useItem", at = @At(value = "INVOKE", shift = At.Shift.AFTER, target = "Lnet/minecraft/world/item/ItemStack;use(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/InteractionHand;)Lnet/minecraft/world/InteractionResultHolder;")) 32 | void postUse(ServerPlayer serverPlayer, 33 | Level level, 34 | ItemStack itemStack, 35 | InteractionHand interactionHand, 36 | CallbackInfoReturnable cir) { 37 | PlayerExt playerExt = ((PlayerExt) serverPlayer); 38 | if (playerExt.isXR()) { 39 | playerExt.getOverrideTransform().set(null); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /mcxr-core/src/main/java/net/sorenon/mcxr/core/mixin/hands/client/MultiPlayerGameModeMixin.java: -------------------------------------------------------------------------------- 1 | package net.sorenon.mcxr.core.mixin.hands.client; 2 | 3 | import net.minecraft.client.multiplayer.MultiPlayerGameMode; 4 | import net.minecraft.client.player.LocalPlayer; 5 | import net.minecraft.world.InteractionHand; 6 | import net.minecraft.world.InteractionResult; 7 | import net.minecraft.world.phys.BlockHitResult; 8 | import net.sorenon.mcxr.core.MCXRCore; 9 | import net.sorenon.mcxr.core.accessor.PlayerExt; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.Inject; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 14 | 15 | @Mixin(MultiPlayerGameMode.class) 16 | public class MultiPlayerGameModeMixin { 17 | 18 | @Inject(method = "performUseItemOn", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;useOn(Lnet/minecraft/world/item/context/UseOnContext;)Lnet/minecraft/world/InteractionResult;")) 19 | void preUse(LocalPlayer localPlayer, InteractionHand interactionHand, BlockHitResult blockHitResult, CallbackInfoReturnable cir) { 20 | PlayerExt playerExt = ((PlayerExt) localPlayer); 21 | if (playerExt.isXR() && MCXRCore.getCoreConfig().handBasedItemUsage()) { 22 | playerExt.getOverrideTransform().set(MCXRCore.handToArm(localPlayer, interactionHand)); 23 | } 24 | } 25 | 26 | @Inject(method = "performUseItemOn", at = @At(value = "INVOKE", shift = At.Shift.AFTER, target = "Lnet/minecraft/world/item/ItemStack;useOn(Lnet/minecraft/world/item/context/UseOnContext;)Lnet/minecraft/world/InteractionResult;")) 27 | void postUse(LocalPlayer localPlayer, InteractionHand interactionHand, BlockHitResult blockHitResult, CallbackInfoReturnable cir) { 28 | PlayerExt playerExt = ((PlayerExt) localPlayer); 29 | if (playerExt.isXR()) { 30 | playerExt.getOverrideTransform().set(null); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /mcxr-core/src/main/resources/assets/mcxr-core/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcxr-org/MCXR/19219f1c11ddd37c5704ea8b3da8d5776c96e06c/mcxr-core/src/main/resources/assets/mcxr-core/icon.png -------------------------------------------------------------------------------- /mcxr-core/src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "mcxr-core", 4 | "version": "${core_version}", 5 | "name": "[MCXR] Core", 6 | "description": "Lightweight XR friendly framework. Allows VR-players to use all vr features.\nThis mod does NOT let you play on vr by itself [MCXR] Play is needed too.", 7 | "authors": [ 8 | "Sorenon" 9 | ], 10 | "contact": { 11 | "homepage": "https://github.com/Sorenon/MCXR", 12 | "sources": "https://github.com/Sorenon/MCXR", 13 | "issues": "https://github.com/Sorenon/MCXR/issues" 14 | }, 15 | "custom": { 16 | "modmenu": { 17 | "links": { 18 | "modmenu.discord": "https://discord.gg/CqEZbJwWx4" 19 | }, 20 | "parent": "mcxr-play" 21 | } 22 | }, 23 | "license": "MIT", 24 | "icon": "assets/mcxr-core/icon.png", 25 | "environment": "*", 26 | "entrypoints": { 27 | "main": [ 28 | "net.sorenon.mcxr.core.MCXRCore" 29 | ], 30 | "client": [ 31 | "net.sorenon.mcxr.core.client.MCXRCoreClient" 32 | ] 33 | }, 34 | "mixins": [ 35 | "mcxr-core.mixins.json" 36 | ], 37 | "depends": { 38 | "fabricloader": ">=0.14.6", 39 | "fabric": "*", 40 | "minecraft": "1.19.x", 41 | "java": ">=17" 42 | }, 43 | "suggests": { 44 | "mcxr-play": "${play_version}" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /mcxr-core/src/main/resources/mcxr-core.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "net.sorenon.mcxr.core.mixin", 5 | "compatibilityLevel": "JAVA_17", 6 | "mixins": [ 7 | "LivingEntityAcc", 8 | "LivingEntityMixin", 9 | "PlayerEntityMixin", 10 | "ServerLoginNetworkHandlerAcc", 11 | "hands.EntityMixin", 12 | "hands.LivingEntityMixin", 13 | "hands.ServerPlayerGameModeMixin" 14 | ], 15 | "client": [ 16 | "hands.client.MultiPlayerGameModeMixin" 17 | ], 18 | "injectors": { 19 | "defaultRequire": 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /mcxr-play/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("fabric-loom") version "0.11-SNAPSHOT" 3 | id("io.github.juuxel.loom-quiltflower") version "+" 4 | id("maven-publish") 5 | id("org.quiltmc.quilt-mappings-on-loom") version "4.0.0" 6 | id("org.ajoberstar.grgit") version "5.0.0-rc.3" 7 | } 8 | 9 | base { 10 | archivesBaseName = "mcxr-play" 11 | } 12 | 13 | version = "${properties["play_version"].toString()}+mc${properties["minecraft_version"].toString()}" 14 | group = properties["maven_group"].toString() 15 | 16 | repositories { 17 | maven { 18 | name = "Jitpack" 19 | url = uri("https://jitpack.io") 20 | } 21 | maven { 22 | name = "Modrinth" 23 | url = uri("https://api.modrinth.com/maven") 24 | content { 25 | includeGroup("maven.modrinth") 26 | } 27 | } 28 | } 29 | 30 | dependencies { 31 | implementation(project(path = ":mcxr-core", configuration = "namedElements")) 32 | 33 | minecraft("com.mojang:minecraft:${properties["minecraft_version"].toString()}") 34 | mappings(loom.layered { 35 | this.addLayer(quiltMappings.mappings("org.quiltmc:quilt-mappings:${properties["minecraft_version"].toString()}+build.${properties["quilt_mappings"].toString()}:v2")) 36 | officialMojangMappings() 37 | }) 38 | modImplementation("net.fabricmc:fabric-loader:${properties["loader_version"].toString()}") 39 | 40 | modImplementation("net.fabricmc.fabric-api:fabric-api:${properties["fabric_version"].toString()}") 41 | 42 | // modImplementation("maven.modrinth:simple-voice-chat:fabric-1.18.2-2.2.26") 43 | 44 | modCompileOnly("com.github.Virtuoel:Pehkui:${properties["pehkui_version"].toString()}") { 45 | exclude(group = "net.fabricmc.fabric-api") 46 | } 47 | 48 | include(implementation("org.lwjgl:lwjgl-openxr:3.3.1")!!) 49 | implementation("org.joml:joml:${properties["joml_version"].toString()}") 50 | implementation("com.electronwill.night-config:core:${properties["night_config_version"].toString()}") 51 | implementation("com.electronwill.night-config:toml:${properties["night_config_version"].toString()}") 52 | } 53 | 54 | loom { 55 | accessWidenerPath.set(file("src/main/resources/mcxr-play.accesswidener")) 56 | } 57 | 58 | sourceSets { 59 | main { 60 | resources { 61 | srcDir("loader") 62 | } 63 | } 64 | } 65 | 66 | tasks { 67 | processResources { 68 | val playVersion = project.properties["play_version"].toString(); 69 | val coreVersion = project.properties["core_version"].toString(); 70 | inputs.property("play_version", playVersion) 71 | inputs.property("core_version", coreVersion) 72 | 73 | filesMatching("fabric.mod.json") { 74 | expand("play_version" to playVersion, "core_version" to coreVersion) 75 | } 76 | } 77 | 78 | withType { 79 | options.release.set(17) 80 | } 81 | 82 | jar { 83 | from("LICENSE") { 84 | rename { "${it}_${project.properties["archivesBaseName"].toString()}" } 85 | } 86 | } 87 | } 88 | 89 | java { 90 | sourceCompatibility = JavaVersion.VERSION_17 91 | targetCompatibility = JavaVersion.VERSION_17 92 | withSourcesJar() 93 | } 94 | 95 | // configure the maven publication 96 | publishing { 97 | publications { 98 | create("mod") { 99 | from(components["java"]) 100 | } 101 | } 102 | 103 | // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. 104 | repositories { 105 | // Add repositories to publish to here. 106 | // Notice: This block does NOT have the same function as the block in the top level. 107 | // The repositories here will be used for publishing your artifact, not for 108 | // retrieving dependencies. 109 | } 110 | } 111 | 112 | loom { 113 | runs { 114 | create("playClient") { 115 | client() 116 | configName = "MCXR Play Client" 117 | ideConfigGenerated(true) 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /mcxr-play/loader/openxr_loader.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcxr-org/MCXR/19219f1c11ddd37c5704ea8b3da8d5776c96e06c/mcxr-play/loader/openxr_loader.dll -------------------------------------------------------------------------------- /mcxr-play/src/main/java/net/sorenon/mcxr/play/MCXRGuiManager.java: -------------------------------------------------------------------------------- 1 | package net.sorenon.mcxr.play; 2 | 3 | import com.mojang.blaze3d.pipeline.RenderTarget; 4 | import com.mojang.blaze3d.pipeline.TextureTarget; 5 | import net.minecraft.client.Minecraft; 6 | import net.minecraft.client.gui.screens.Screen; 7 | import net.minecraft.resources.ResourceLocation; 8 | import net.minecraft.world.phys.Vec3; 9 | import net.sorenon.mcxr.core.JOMLUtil; 10 | import net.sorenon.mcxr.play.rendering.UnownedTexture; 11 | import net.sorenon.mcxr.play.rendering.MCXRCamera; 12 | import org.jetbrains.annotations.Nullable; 13 | import org.joml.*; 14 | 15 | public class MCXRGuiManager { 16 | 17 | public final int guiFramebufferWidth = 1980; 18 | public final int guiFramebufferHeight = 1080; 19 | 20 | public final ResourceLocation guiRenderTexture = new ResourceLocation("mcxr", "net/sorenon/mcxr/play/gui"); 21 | 22 | public RenderTarget guiRenderTarget; 23 | public RenderTarget guiPostProcessRenderTarget; 24 | 25 | public double guiScale; 26 | 27 | public int scaledWidth; 28 | public int scaledHeight; 29 | 30 | public boolean needsReset = true; 31 | 32 | public float size = 1.5f; 33 | 34 | /** 35 | * The transform of the GUI in physical space 36 | */ 37 | public Vec3 position = null; 38 | public Quaterniond orientation = new Quaterniond(0, 0, 0, 1); 39 | 40 | public void init() { 41 | guiScale = calcGuiScale(); 42 | 43 | int widthFloor = (int) (guiFramebufferWidth / guiScale); 44 | scaledWidth = guiFramebufferWidth / guiScale > widthFloor ? widthFloor + 1 : widthFloor; 45 | 46 | int heightFloor = (int) (guiFramebufferHeight / guiScale); 47 | scaledHeight = guiFramebufferHeight / guiScale > heightFloor ? heightFloor + 1 : heightFloor; 48 | 49 | guiRenderTarget = new TextureTarget(guiFramebufferWidth, guiFramebufferHeight, true, Minecraft.ON_OSX); 50 | guiRenderTarget.setClearColor(0, 0, 0, 0); 51 | guiPostProcessRenderTarget = new TextureTarget(guiFramebufferWidth, guiFramebufferHeight, false, Minecraft.ON_OSX); 52 | Minecraft.getInstance().getTextureManager().register(guiRenderTexture, new UnownedTexture(guiPostProcessRenderTarget.getColorTextureId())); 53 | } 54 | 55 | @SuppressWarnings("ConditionCoveredByFurtherCondition") 56 | public double calcGuiScale() { 57 | int guiScale = 4; 58 | boolean forceUnicodeFont = Minecraft.getInstance().isEnforceUnicode(); 59 | 60 | int scale; 61 | scale = 1; 62 | while (scale != guiScale && scale < guiFramebufferWidth && scale < guiFramebufferHeight && guiFramebufferWidth / (scale + 1) >= 320 && guiFramebufferHeight / (scale + 1) >= 240) { 63 | ++scale; 64 | } 65 | 66 | if (forceUnicodeFont && scale % 2 != 0) { 67 | ++scale; 68 | } 69 | return scale; 70 | } 71 | 72 | public boolean isScreenOpen() { 73 | return Minecraft.getInstance().screen != null; 74 | } 75 | 76 | public void handleOpenScreen(@Nullable Screen screen) { 77 | if (screen == null) { 78 | position = null; 79 | orientation.set(0, 0, 0, 1); 80 | needsReset = false; 81 | } else if (position == null) { 82 | resetTransform(); 83 | } 84 | } 85 | 86 | public void resetTransform() { 87 | MCXRCamera camera = (MCXRCamera) Minecraft.getInstance().gameRenderer.getMainCamera(); 88 | if (camera.isInitialized()) { 89 | orientation = JOMLUtil.convertd(camera.rotation()); 90 | position = JOMLUtil.convert(MCXRPlayClient.viewSpacePoses.getUnscaledPhysicalPose().getPos().add(orientation.transform(new Vector3f(0, -0.5f, 1)))); 91 | needsReset = false; 92 | } else { 93 | needsReset = true; 94 | } 95 | } 96 | 97 | @Nullable 98 | public Vector3d guiRaycast(Vector3d rayPos, Vector3d rayDir) { 99 | if (position == null) { 100 | return null; 101 | } 102 | double distance = Intersectiond.intersectRayPlane( 103 | rayPos, 104 | rayDir, 105 | JOMLUtil.convert(position), 106 | orientation.transform(new Vector3d(0, 0, -1)), 107 | 0.1f 108 | ); 109 | if (distance >= 0) { 110 | return rayDir.mul(distance, new Vector3d()).add(rayPos); 111 | } 112 | return null; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /mcxr-play/src/main/java/net/sorenon/mcxr/play/MoveDirectionPose.java: -------------------------------------------------------------------------------- 1 | package net.sorenon.mcxr.play; 2 | 3 | import com.mojang.blaze3d.platform.InputConstants; 4 | import net.minecraft.client.Minecraft; 5 | import net.minecraft.network.chat.Component; 6 | import net.minecraft.world.phys.Vec3; 7 | import net.sorenon.mcxr.core.JOMLUtil; 8 | import net.sorenon.mcxr.core.Pose; 9 | import net.sorenon.mcxr.play.input.XrInput; 10 | import org.jetbrains.annotations.Nullable; 11 | import org.joml.Math; 12 | import org.joml.Quaternionf; 13 | import org.joml.Vector3d; 14 | import org.joml.Vector3f; 15 | import org.lwjgl.glfw.GLFW; 16 | 17 | import java.util.Optional; 18 | 19 | public enum MoveDirectionPose { 20 | Head, 21 | RightHand, 22 | LeftHand; 23 | 24 | public Component toComponent() { 25 | switch (this) { 26 | case Head -> { 27 | return Component.translatable("mcxr.move_direction.head"); 28 | } 29 | case RightHand -> { 30 | return Component.translatable("mcxr.move_direction.right_hand"); 31 | } 32 | case LeftHand -> { 33 | return Component.translatable("mcxr.move_direction.left_hand"); 34 | } 35 | default -> throw new IllegalStateException("Unexpected value: " + this); 36 | } 37 | } 38 | 39 | public MoveDirectionPose iterate() { 40 | boolean next = !InputConstants.isKeyDown(Minecraft.getInstance().getWindow().getWindow(), GLFW.GLFW_KEY_LEFT_SHIFT); 41 | switch (this) { 42 | case Head -> { 43 | return next ? RightHand : LeftHand; 44 | } 45 | case RightHand -> { 46 | return next ? LeftHand : Head; 47 | } 48 | case LeftHand -> { 49 | return next ? Head : RightHand; 50 | } 51 | default -> throw new IllegalStateException("Unexpected value: " + this); 52 | } 53 | } 54 | 55 | @Nullable 56 | public Vec3 getLookDirection() { 57 | Quaternionf orientation = new Quaternionf(); 58 | switch (this) { 59 | case Head -> { 60 | return null; 61 | } 62 | case RightHand -> XrInput.handsActionSet.gripPoses[1].getMinecraftPose().getOrientation().get(orientation); 63 | case LeftHand -> XrInput.handsActionSet.gripPoses[0].getMinecraftPose().getOrientation().get(orientation); 64 | } 65 | orientation = orientation.rotateX(Math.toRadians(PlayOptions.handPitchAdjust)); 66 | return JOMLUtil.convert(orientation.transform(new Vector3d(0, -1, 0))); 67 | } 68 | 69 | public Optional getMCYaw() { 70 | Quaternionf orientation = new Quaternionf(); 71 | switch (this) { 72 | case Head -> { 73 | return Optional.empty(); 74 | } 75 | case RightHand -> XrInput.handsActionSet.gripPoses[1].getMinecraftPose().getOrientation().get(orientation); 76 | case LeftHand -> XrInput.handsActionSet.gripPoses[0].getMinecraftPose().getOrientation().get(orientation); 77 | } 78 | orientation = orientation.rotateX(Math.toRadians(PlayOptions.handPitchAdjust)); 79 | return Optional.of(Pose.getMCYaw(orientation, new Vector3f(0, -1, 0))); 80 | } 81 | 82 | public Optional getMCPitch() { 83 | Quaternionf orientation = new Quaternionf(); 84 | switch (this) { 85 | case Head -> { 86 | return Optional.empty(); 87 | } 88 | case RightHand -> XrInput.handsActionSet.gripPoses[1].getMinecraftPose().getOrientation().get(orientation); 89 | case LeftHand -> XrInput.handsActionSet.gripPoses[0].getMinecraftPose().getOrientation().get(orientation); 90 | } 91 | orientation = orientation.rotateX(Math.toRadians(PlayOptions.handPitchAdjust)); 92 | return Optional.of(Pose.getMCPitch(orientation, new Vector3f(0, -1, 0))); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /mcxr-play/src/main/java/net/sorenon/mcxr/play/PlayOptions.java: -------------------------------------------------------------------------------- 1 | package net.sorenon.mcxr.play; 2 | 3 | import com.electronwill.nightconfig.core.file.FileConfig; 4 | import com.mojang.blaze3d.platform.InputConstants; 5 | import net.fabricmc.loader.api.FabricLoader; 6 | import net.minecraft.client.Minecraft; 7 | import net.minecraft.network.chat.Component; 8 | import org.lwjgl.glfw.GLFW; 9 | 10 | public class PlayOptions { 11 | 12 | private static FileConfig fileConfig; 13 | 14 | public static boolean xrUninitialized = false; 15 | public static boolean xrPaused = false; 16 | 17 | public static MoveDirectionPose walkDirection = MoveDirectionPose.LeftHand; 18 | public static MoveDirectionPose swimDirection = MoveDirectionPose.RightHand; 19 | public static MoveDirectionPose flyDirection = MoveDirectionPose.RightHand; 20 | 21 | public static boolean smoothTurning = false; 22 | public static float snapTurnAmount = 22f; 23 | public static float smoothTurnRate = 120f; 24 | 25 | /** 26 | * The angle to rotate the player's in-game hand for a more comfortable experience 27 | * May be different for different controllers -> needs testing 28 | */ 29 | public static float handPitchAdjust = 30; 30 | 31 | public static float SSAA = 1; 32 | 33 | public static IndexTouchpad indexTouchpadState = IndexTouchpad.Off; 34 | 35 | public static void init() { 36 | fileConfig = FileConfig.of(FabricLoader.getInstance().getConfigDir().resolve(MCXRPlayClient.MODID + ".toml")); 37 | } 38 | 39 | public static void save() { 40 | fileConfig.set("disableMCXR", xrUninitialized); 41 | fileConfig.set("pauseMCXR", xrPaused); 42 | 43 | fileConfig.set("walkDirection", walkDirection); 44 | fileConfig.set("swimDirection", swimDirection); 45 | fileConfig.set("flyDirection", flyDirection); 46 | 47 | fileConfig.set("handPitchAdjust", handPitchAdjust); 48 | 49 | fileConfig.set("smoothTurning", smoothTurning); 50 | fileConfig.set("snapTurnAmount", snapTurnAmount); 51 | fileConfig.set("smoothTurnRate", smoothTurnRate); 52 | fileConfig.set("indexTouchpadState", indexTouchpadState); 53 | 54 | fileConfig.set("SSAA", SSAA); 55 | fileConfig.save(); 56 | } 57 | 58 | public static void load() { 59 | fileConfig.load(); 60 | xrUninitialized = fileConfig.getOrElse("disableMCXR", false); 61 | xrPaused = fileConfig.getOrElse("pauseMCXR", false); 62 | 63 | walkDirection = fileConfig.getEnumOrElse("walkDirection", MoveDirectionPose.LeftHand); 64 | swimDirection = fileConfig.getEnumOrElse("swimDirection", MoveDirectionPose.RightHand); 65 | flyDirection = fileConfig.getEnumOrElse("flyDirection", MoveDirectionPose.RightHand); 66 | 67 | handPitchAdjust = fileConfig.getOrElse("handPitchAdjust", 30f).floatValue(); 68 | 69 | smoothTurning = fileConfig.getOrElse("smoothTurning", false); 70 | snapTurnAmount = fileConfig.getOrElse("snapTurnAmount", 22f).floatValue(); 71 | smoothTurnRate = fileConfig.getOrElse("smoothTurnRate", 120f).floatValue(); 72 | indexTouchpadState = fileConfig.getEnumOrElse("indexTouchpadState", IndexTouchpad.Off); 73 | 74 | SSAA = fileConfig.getOrElse("SSAA", 1).floatValue(); 75 | } 76 | 77 | public enum IndexTouchpad { 78 | Off, 79 | RightForward, 80 | LeftForward; 81 | 82 | 83 | public Component toComponent() { 84 | switch (this) { 85 | case Off -> { 86 | return Component.translatable("mcxr.index_touchpad.off"); 87 | } 88 | case RightForward -> { 89 | return Component.translatable("mcxr.index_touchpad.right_hand"); 90 | } 91 | case LeftForward -> { 92 | return Component.translatable("mcxr.index_touchpad.left_hand"); 93 | } 94 | default -> throw new IllegalStateException("Unexpected value: " + this); 95 | } 96 | } 97 | 98 | public IndexTouchpad iterate() { 99 | boolean next = !InputConstants.isKeyDown(Minecraft.getInstance().getWindow().getWindow(), GLFW.GLFW_KEY_LEFT_SHIFT); 100 | switch (this) { 101 | case Off -> { 102 | return next ? RightForward : LeftForward; 103 | } 104 | case RightForward -> { 105 | return next ? LeftForward : Off; 106 | } 107 | case LeftForward -> { 108 | return next ? Off : RightForward; 109 | } 110 | default -> throw new IllegalStateException("Unexpected value: " + this); 111 | } 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /mcxr-play/src/main/java/net/sorenon/mcxr/play/accessor/Matrix4fExt.java: -------------------------------------------------------------------------------- 1 | package net.sorenon.mcxr.play.accessor; 2 | 3 | import org.lwjgl.openxr.XrFovf; 4 | 5 | public interface Matrix4fExt { 6 | void setXrProjection(XrFovf fov, float nearZ, float farZ); 7 | } 8 | -------------------------------------------------------------------------------- /mcxr-play/src/main/java/net/sorenon/mcxr/play/accessor/MinecraftExt.java: -------------------------------------------------------------------------------- 1 | package net.sorenon.mcxr.play.accessor; 2 | 3 | import net.sorenon.mcxr.play.rendering.RenderPass; 4 | 5 | public interface MinecraftExt { 6 | 7 | void preRender(boolean tick, Runnable preTick); 8 | 9 | void doRender(boolean tick, long frameStartTime, RenderPass renderPass); 10 | 11 | void postRender(); 12 | } 13 | -------------------------------------------------------------------------------- /mcxr-play/src/main/java/net/sorenon/mcxr/play/compat/svc/SimpleVoiceChatCompat.java: -------------------------------------------------------------------------------- 1 | package net.sorenon.mcxr.play.compat.svc; 2 | 3 | // import de.maxhenkel.voicechat.gui.VoiceChatScreen; 4 | import net.minecraft.client.Minecraft; 5 | import net.minecraft.client.gui.components.Button; 6 | import net.minecraft.network.chat.Component; 7 | 8 | import java.util.ArrayList; 9 | 10 | public class SimpleVoiceChatCompat { 11 | public static void createButton(ArrayList