diff --git a/.github/workflows/build-on-push.yml b/.github/workflows/build-on-push.yml index 9781fd1..dd330f1 100644 --- a/.github/workflows/build-on-push.yml +++ b/.github/workflows/build-on-push.yml @@ -17,20 +17,23 @@ jobs: matrix: include: # Unsupported platforms are commented out. - #- name: 🐧 Linux (GCC) - # os: ubuntu-20.04 - # platform: linux - # artifact-name: gdtiltfive.linux - # artifact-path: build/bin/libgdtiltfive.linux.* + - name: 🐧 Linux (GCC) + os: ubuntu-20.04 + platform: linux + artifact-name: gdtiltfive.linux + artifact-path: | + build/bin/linux/x86_64/libgdtiltfive.linux.template_debug.x86_64.so + build/bin/linux/x86_64/libgdtiltfive.linux.template_release.x86_64.so + extension/TiltFiveNDK/lib/linux/x86_64/libTiltFiveNative.so - name: 🏁 Windows (x86_64, MSVC) os: windows-2019 platform: windows artifact-name: gdtiltfive.windows artifact-path: | - build/bin/libgdtiltfive.windows.template_debug.x86_64.dll - build/bin/libgdtiltfive.windows.template_release.x86_64.dll - extension/TiltFiveNDK/lib/win/x86_64/TiltFiveNative.dll + build/bin/windows/x86_64/libgdtiltfive.windows.template_debug.x86_64.dll + build/bin/windows/x86_64/libgdtiltfive.windows.template_release.x86_64.dll + extension/TiltFiveNDK/lib/win/x86_64/TiltFiveNative.dll #- name: 🍎 macOS (universal) # os: macos-11 @@ -39,12 +42,14 @@ jobs: # artifact-name: gdtiltfive.macos # artifact-path: build/bin/libgdtiltfive.macos.* - #- name: πŸ€– Android (arm64) - # os: ubuntu-20.04 - # platform: android - # flags: ANDROID_NDK_ROOT=$ANDROID_NDK_LATEST_HOME arch=arm64 - # artifact-name: gdtiltfive.android - # artifact-path: build/bin/libgdtiltfive.android.* + - name: πŸ€– Android (arm64) + os: ubuntu-20.04 + platform: android + #flags: ANDROID_NDK_ROOT=$ANDROID_NDK_LATEST_HOME arch=arm64 + artifact-name: gdtiltfive.android + artifact-path: | + build/bin/android/arm64/libgdtiltfive.android.* + androidplugin/plugin/build/outputs/aar/gdtiltfive* #- name: 🍏 iOS (arm64) # os: macos-11 @@ -72,17 +77,52 @@ jobs: sudo apt-get update -qq sudo apt-get install -qqq build-essential pkg-config + - name: Set up GCC + if: ${{ matrix.platform == 'linux' }} + uses: egor-tensin/setup-gcc@v1 + with: + version: 11 + platform: x64 + + - name: Android dependencies + if: ${{ matrix.platform == 'android' }} + uses: nttld/setup-ndk@v1 + id: setup-ndk + with: + ndk-version: r26c + add-to-path: false + - name: Install scons run: | - python -m pip install scons==4.0.0 + python -m pip install scons==4.0.0 - name: Build debug build run: | - scons platform=${{ matrix.platform }} target=template_debug ${{ matrix.flags }} + scons platform=${{ matrix.platform }} target=template_debug ${{ matrix.flags }} --no-gradle + env: + ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }} - name: Build release build run: | - scons platform=${{ matrix.platform }} target=template_release ${{ matrix.flags }} + scons platform=${{ matrix.platform }} target=template_release ${{ matrix.flags }} --no-gradle + env: + ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }} + + - name: Set up Java + if: ${{ matrix.platform == 'android' }} + uses: actions/setup-java@v4 + with: + distribution: 'zulu' # See 'Supported distributions' for available options + java-version: '17' + + - name: Gradle build + if: ${{ matrix.platform == 'android' }} + run: | + mkdir plugin/libs + cp ../extension/TiltFiveNDK/lib/android/TiltFiveAndroidClient.jar plugin/libs/TiltFiveAndroidClient.jar + chmod +x ./gradlew + ./gradlew build + working-directory: ./androidplugin - name: Upload artifact uses: actions/upload-artifact@v3 @@ -117,12 +157,20 @@ jobs: cp source/CONTRIBUTORS.md plugin/addons/tiltfive cp source/CHANGES.md plugin/addons/tiltfive mkdir plugin/addons/tiltfive/bin - # cp gdtiltfive.linux/*.so plugin/addons/tiltfive/bin/ - cp gdtiltfive.windows/build/bin/*.dll plugin/addons/tiltfive/bin/ - cp gdtiltfive.windows/extension/TiltFiveNDK/lib/win/x86_64/*.dll plugin/addons/tiltfive/bin/ - # cp gdtiltfive.android/*.so plugin/addons/tiltfive/bin/ - # cp gdtiltfive.ios/*.dylib plugin/addons/tiltfive/bin/ - # cp -R gdtiltfive.macos/libgdtiltfive.macos.* plugin/addons/tiltfive/bin/ + mkdir plugin/addons/tiltfive/bin/linux + mkdir plugin/addons/tiltfive/bin/linux/x86_64 + mkdir plugin/addons/tiltfive/bin/windows + mkdir plugin/addons/tiltfive/bin/windows/x86_64 + mkdir plugin/addons/tiltfive/bin/android/ + mkdir plugin/addons/tiltfive/bin/android/arm64 + cp gdtiltfive.linux/build/bin/linux/x86_64/*.so plugin/addons/tiltfive/bin/linux/x86_64/ + cp gdtiltfive.linux/extension/TiltFiveNDK/lib/linux/x86_64/*.so plugin/addons/tiltfive/bin/linux/x86_64/ + cp gdtiltfive.windows/build/bin/windows/x86_64/*.dll plugin/addons/tiltfive/bin/windows/x86_64/ + cp gdtiltfive.windows/extension/TiltFiveNDK/lib/win/x86_64/*.dll plugin/addons/tiltfive/bin/windows/x86_64/ + cp gdtiltfive.android/build/bin/android/arm64/*.so plugin/addons/tiltfive/bin/android/arm64/ + cp source/extension/TiltFiveNDK/lib/android/arm64-v8a/*.so plugin/addons/tiltfive/bin/android/arm64/ + cp gdtiltfive.android/androidplugin/plugin/build/outputs/aar/gdtiltfive-debug.aar plugin/addons/tiltfive/bin/android + cp gdtiltfive.android/androidplugin/plugin/build/outputs/aar/gdtiltfive-release.aar plugin/addons/tiltfive/bin/android - name: Copy files to destination for csharp run: | mkdir plugin_cs @@ -132,12 +180,20 @@ jobs: cp source/CONTRIBUTORS.md plugin_cs/addons/tiltfive cp source/CHANGES.md plugin_cs/addons/tiltfive mkdir plugin_cs/addons/tiltfive/bin - # cp gdtiltfive.linux/*.so plugin_cs/addons/tiltfive/bin/ - cp gdtiltfive.windows/build/bin/*.dll plugin_cs/addons/tiltfive/bin/ - cp gdtiltfive.windows/extension/TiltFiveNDK/lib/win/x86_64/*.dll plugin_cs/addons/tiltfive/bin/ - # cp gdtiltfive.android/*.so plugin_cs/addons/tiltfive/bin/ - # cp gdtiltfive.ios/*.dylib plugin_cs/addons/tiltfive/bin/ - # cp -R gdtiltfive.macos/libgdtiltfive.macos.* plugin_cs/addons/tiltfive/bin/ + mkdir plugin_cs/addons/tiltfive/bin/linux + mkdir plugin_cs/addons/tiltfive/bin/linux/x86_64 + mkdir plugin_cs/addons/tiltfive/bin/windows + mkdir plugin_cs/addons/tiltfive/bin/windows/x86_64 + mkdir plugin_cs/addons/tiltfive/bin/android/ + mkdir plugin_cs/addons/tiltfive/bin/android/arm64 + cp gdtiltfive.linux/build/bin/linux/x86_64/*.so plugin_cs/addons/tiltfive/bin/linux/x86_64/ + cp gdtiltfive.linux/extension/TiltFiveNDK/lib/linux/x86_64/*.so plugin_cs/addons/tiltfive/bin/linux/x86_64/ + cp gdtiltfive.windows/build/bin/windows/x86_64/*.dll plugin_cs/addons/tiltfive/bin/windows/x86_64/ + cp gdtiltfive.windows/extension/TiltFiveNDK/lib/win/x86_64/*.dll plugin_cs/addons/tiltfive/bin/windows/x86_64/ + cp gdtiltfive.android/build/bin/android/arm64/*.so plugin_cs/addons/tiltfive/bin/android/arm64/ + cp source/extension/TiltFiveNDK/lib/android/arm64-v8a/*.so plugin_cs/addons/tiltfive/bin/android/arm64/ + cp gdtiltfive.android/androidplugin/plugin/build/outputs/aar/gdtiltfive-debug.aar plugin_cs/addons/tiltfive/bin/android + cp gdtiltfive.android/androidplugin/plugin/build/outputs/aar/gdtiltfive-release.aar plugin_cs/addons/tiltfive/bin/android - name: Calculate GIT short ref run: | cd source @@ -172,4 +228,4 @@ jobs: omitBodyDuringUpdate: true omitPrereleaseDuringUpdate : true token: ${{ secrets.GITHUB_TOKEN }} - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') \ No newline at end of file diff --git a/.gitignore b/.gitignore index 51f4e69..d9e4c62 100644 --- a/.gitignore +++ b/.gitignore @@ -26,5 +26,12 @@ build *.includes *.idb *.exp +*.aar +# project specific ignores !extension/TiltFiveNDK/lib/*/*/* + +example.gd/android/ +example.gd/export_presets.cfg +example.csharp/android/ +example.csharp/export_presets.cfg diff --git a/README.md b/README.md index fe027eb..94abedf 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,11 @@ TiltFiveGodot4 isΒ **GDExtension**Β for the Godot 4 engine to connect to theΒ [T | | Renderer
Forward+ | Renderer
Mobile | Renderer
Compatibility | | - | - | - | - | -| Windows |βœ…| βœ… | βœ… | -| Linux1 | ✓ |✓ | ✓ | -| Android | | ❌ | | +| Windows | βœ… | βœ… | βœ… | +| Linux1 | βœ… |βœ… | βœ… | +| Android1 |❌| ❌ | βœ… | -1. [Experimental Version](https://github.com/patrickdown/TiltFiveGodot4/releases/tag/1.1.0-linux-experimental3) + 1. Experimental Support ## Usage @@ -26,15 +26,16 @@ Please refer to this [documentation](https://patrickdown.github.io/godot/tilt-fi ### Prerequisites -This extension requires a C++20 capable compiler. +This extension requires a C++20 capable compiler. To build for Android you will need Java 17 and the Android SDK along with the r26c NDK. Things you will need to know how to do. * Use [scons](https://scons.org/) * [Build GDExtensions](https://docs.godotengine.org/en/stable/tutorials/scripting/gdextension/gdextension_cpp_example.html) +* Install the Android SDK and NDK (For android builds) ### Building the extension -To build the plugin invoke `scons` from the root directory of the project. The build product will in `build\bin`. Invoking `scons example` will build the product and copy the binaries to the `example.gd\addons\tilt-five\bin` and `example.csharp\addons\tilt-five\bin` directories. +To build the plugin invoke `scons` from the root directory of the project. The build product will in `build\bin`. Invoking `scons --assemble` will build the product and copy the binaries to the `example.gd\addons\tilt-five\bin` and `example.csharp\addons\tilt-five\bin` directories. ## Using the build products diff --git a/SConstruct b/SConstruct index fb8e654..07ab724 100644 --- a/SConstruct +++ b/SConstruct @@ -1,16 +1,56 @@ #!/usr/bin/env python import os import sys +from pprint import pprint +env = SConscript('godot-cpp/SConstruct') + +AddOption( + '--no-gradle', + dest='no_gradle', + action='store_true', + help='no not spawn gradle to build the android plugin') + +AddOption( + '--assemble', + dest='assemble_example', + action='store_true', + help='assemble the plugin files into the example project') + +AddOption( + '--gd-install-dir', + dest='gd_install_dir', + type='string', + nargs=1, + action='store', + metavar='DIR', + help='godot project directory to copy the gdscript plugin to') +AddOption( + '--cs-install-dir', + dest='cs_install_dir', + type='string', + nargs=1, + action='store', + metavar='DIR', + help='godot project directory to copy the c# plugin to') + +gd_install_dir = GetOption('gd_install_dir') +cs_install_dir = GetOption('cs_install_dir') + +assemble_example = GetOption('assemble_example') or gd_install_dir or cs_install_dir + +build_aar_library = not GetOption('no_gradle') and env['platform'] == 'android' VariantDir('build/src','extension/src', duplicate=False) VariantDir('build/T5Integration','extension/T5Integration', duplicate=False) -env = SConscript('godot-cpp/SConstruct') tilt_five_headers_path = 'extension/TiltFiveNDK/include' tilt_five_library_path = 'extension/TiltFiveNDK/lib/' + { 'windows' : 'win/x86_64', 'linux' : 'linux/x86_64', 'android' : 'android/arm64-v8a'}[env["platform"]] -tilt_five_library = {'windows' : 'TiltFiveNative.dll.if', 'linux' : 'libTiltFiveNative.so', 'android' : 'libTiltFiveNative.so'}[env["platform"]] +tilt_five_library = {'windows' : 'TiltFiveNative.dll.if', 'linux' : 'libTiltFiveNative.so', 'android' : 'TiltFiveNative'}[env["platform"]] +tilt_five_jar = 'extension/TiltFiveNDK/lib/android/TiltFiveAndroidClient.jar' + +bin_path = "{}/{}".format(env['platform'], env['arch']) # For the reference: # - CCFLAGS are compilation flags shared between C and C++ @@ -25,6 +65,7 @@ env.Append(CPPPATH=['extension/src/','extension/T5Integration/',tilt_five_header sources = Glob('build/src/*.cpp') sources += Glob('build/T5Integration/*.cpp') + env.Append(LIBPATH=[tilt_five_library_path]) env.Append(LIBS=[tilt_five_library]) @@ -33,36 +74,50 @@ if env['platform'] == 'windows': env['CXXFLAGS'].remove('/std:c++17') env.Append(CXXFLAGS=['/std:c++20']) env.Append(CXXFLAGS=['/Zc:__cplusplus']) - library = env.SharedLibrary( - 'build/bin/libgdtiltfive{}{}'.format(env['suffix'], env['SHLIBSUFFIX']), - source=sources, - ) elif env['platform'] == 'linux': env['t5_shared_lib'] = 'libTiltFiveNative.so' env['CXXFLAGS'].remove('-std=c++17') env.Append(CXXFLAGS=['-std=c++20']) env.Append(RPATH=env.Literal('\\$$ORIGIN' )) - library = env.SharedLibrary( - 'build/bin/libgdtiltfive{}{}'.format(env['suffix'], env['SHLIBSUFFIX']), - source=sources, - ) elif env['platform'] == 'android': - env['t5_shared_lib'] = 'libTiltFiveNative.so' + env['t5_shared_lib'] = 'lib{}.so'.format(tilt_five_library) env['CXXFLAGS'].remove('-std=c++17') env.Append(CXXFLAGS=['-std=c++20']) - env.Append(CXXFLAGS=['-stdlib=libc++']) - env.Append(CCFLAGS=['-fPIC']) + #env.Append(CPPDEFINES = ['ANDROID_CPP']) env.Append(RPATH=env.Literal('\\$$ORIGIN' )) - library = env.SharedLibrary( - 'build/bin/libgdtiltfive{}{}'.format(env['suffix'], env['SHLIBSUFFIX']), - source=sources, - ) -f1 = env.Command('example.gd/addons/tiltfive/bin/libgdtiltfive{}{}'.format(env['suffix'], env['SHLIBSUFFIX']), library, Copy('$TARGET', '$SOURCE') ) -f2 = env.Command('example.gd/addons/tiltfive/bin/{}'.format(env['t5_shared_lib']), tilt_five_library_path + '/{}'.format(env['t5_shared_lib']), Copy('$TARGET', '$SOURCE') ) -f3 = env.Command('example.csharp/addons/tiltfive/bin/libgdtiltfive{}{}'.format(env['suffix'], env['SHLIBSUFFIX']), library, Copy('$TARGET', '$SOURCE') ) -f4 = env.Command('example.csharp/addons/tiltfive/bin/{}'.format(env['t5_shared_lib']), tilt_five_library_path + '/{}'.format(env['t5_shared_lib']), Copy('$TARGET', '$SOURCE') ) +library = env.SharedLibrary( + 'build/bin/{}/libgdtiltfive{}{}'.format(bin_path,env['suffix'], env['SHLIBSUFFIX']), + source=sources, +) + +things_to_build = [library] + +if build_aar_library: + print("Building Android AAR library...") + Execute(Copy('androidplugin/plugin/libs/TiltFiveAndroidClient.jar', tilt_five_jar)) + SConscript('androidplugin/SConstruct', exports="env") + +if assemble_example: + f1 = env.Command('example.gd/addons/tiltfive/bin/{}/libgdtiltfive{}{}'.format(bin_path,env['suffix'], env['SHLIBSUFFIX']), library, Copy('$TARGET', '$SOURCE') ) + f2 = env.Command('example.gd/addons/tiltfive/bin/{}/{}'.format(bin_path,env['t5_shared_lib']), tilt_five_library_path + '/{}'.format(env['t5_shared_lib']), Copy('$TARGET', '$SOURCE') ) + f3 = env.Command('example.csharp/addons/tiltfive/bin/{}/libgdtiltfive{}{}'.format(bin_path,env['suffix'], env['SHLIBSUFFIX']), library, Copy('$TARGET', '$SOURCE') ) + f4 = env.Command('example.csharp/addons/tiltfive/bin/{}/{}'.format(bin_path,env['t5_shared_lib']), tilt_five_library_path + '/{}'.format(env['t5_shared_lib']), Copy('$TARGET', '$SOURCE') ) + things_to_build += [f1, f2, f3, f4] + if build_aar_library: + f5 = env.Command('example.gd/addons/tiltfive/bin/android/gdtiltfive-debug.aar', 'androidplugin/plugin/build/outputs/aar/gdtiltfive-debug.aar', Copy('$TARGET', '$SOURCE') ) + f6 = env.Command('example.gd/addons/tiltfive/bin/android/gdtiltfive-release.aar', 'androidplugin/plugin/build/outputs/aar/gdtiltfive-release.aar', Copy('$TARGET', '$SOURCE') ) + f7 = env.Command('example.csharp/addons/tiltfive/bin/android/gdtiltfive-debug.aar', 'androidplugin/plugin/build/outputs/aar/gdtiltfive-debug.aar', Copy('$TARGET', '$SOURCE') ) + f8 = env.Command('example.csharp/addons/tiltfive/bin/android/gdtiltfive-release.aar', 'androidplugin/plugin/build/outputs/aar/gdtiltfive-release.aar', Copy('$TARGET', '$SOURCE') ) + things_to_build += [f5, f6, f7, f8] + +if gd_install_dir: + inst1 = env.Install("{}/addons".format(gd_install_dir) , "example.gd/addons/tiltfive") + things_to_build += [inst1] + +if cs_install_dir: + inst2 = env.Install("{}/addons".format(cs_install_dir) , "example.csharp/addons/tiltfive") + things_to_build += [inst2] -env.Alias('example', [f1, f2, f3, f4]) +Default(things_to_build) -Default(library) diff --git a/androidplugin/.gitignore b/androidplugin/.gitignore new file mode 100644 index 0000000..ad2d0e9 --- /dev/null +++ b/androidplugin/.gitignore @@ -0,0 +1,34 @@ +# Gradle files +.gradle/ +build/ +libs/ + +# Local configuration file (sdk path, etc) +local.properties + +# Log/OS Files +*.log + +# Android Studio generated files and folders +captures/ +.externalNativeBuild/ +.cxx/ +*.apk +output.json + +# IntelliJ +*.iml +.idea/ +misc.xml +deploymentTargetDropDown.xml +render.experimental.xml + +# Keystore files +*.jks +*.keystore + +# Google Services (e.g. APIs or Firebase) +google-services.json + +# Android Profiling +*.hprof diff --git a/androidplugin/README.md b/androidplugin/README.md new file mode 100644 index 0000000..04fd16e --- /dev/null +++ b/androidplugin/README.md @@ -0,0 +1,5 @@ +# Tilt Five Godot Android ARR + +This is a small project that builds the Java parts that are +required by the Tile Five client. It's only callable functions +are used by the c++ parts of the plugin. diff --git a/androidplugin/SConstruct b/androidplugin/SConstruct new file mode 100644 index 0000000..c2731da --- /dev/null +++ b/androidplugin/SConstruct @@ -0,0 +1,13 @@ +#!/usr/bin/env python +import os +import sys + +Import("env") + +def gradle_build(target, source, env): + os.system('gradlew build --no-daemon') + +android_outputs = ['plugin/build/outputs/aar/gdtiltfive-debug.aar', 'plugin/build/outputs/aar/gdtiltfive-release.aar'] +android_inputs = ['plugin/libs/TiltFiveAndroidClient.jar', 'plugin/src/main/java/org/tiltfivegodot/plugin/GodotAndroidPlugin.java'] +Default(env.Command(android_outputs, android_inputs, gradle_build)) + diff --git a/androidplugin/build.gradle.kts b/androidplugin/build.gradle.kts new file mode 100644 index 0000000..83658ce --- /dev/null +++ b/androidplugin/build.gradle.kts @@ -0,0 +1,5 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + id("com.android.library") version "7.4.1" apply false + id("org.jetbrains.kotlin.android") version "1.8.0" apply false +} diff --git a/androidplugin/gradle.properties b/androidplugin/gradle.properties new file mode 100644 index 0000000..2cbd6d1 --- /dev/null +++ b/androidplugin/gradle.properties @@ -0,0 +1,23 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true diff --git a/androidplugin/gradle/wrapper/gradle-wrapper.jar b/androidplugin/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e708b1c Binary files /dev/null and b/androidplugin/gradle/wrapper/gradle-wrapper.jar differ diff --git a/androidplugin/gradle/wrapper/gradle-wrapper.properties b/androidplugin/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..0017edf --- /dev/null +++ b/androidplugin/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Aug 25 21:04:51 PDT 2023 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/androidplugin/gradlew b/androidplugin/gradlew new file mode 100644 index 0000000..4f906e0 --- /dev/null +++ b/androidplugin/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/androidplugin/gradlew.bat b/androidplugin/gradlew.bat new file mode 100644 index 0000000..dbc0684 --- /dev/null +++ b/androidplugin/gradlew.bat @@ -0,0 +1,90 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + + +if exist "%JAVA_EXE%" goto execute +echo %JAVA_HOME% +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/androidplugin/plugin/.gitignore b/androidplugin/plugin/.gitignore new file mode 100644 index 0000000..273b322 --- /dev/null +++ b/androidplugin/plugin/.gitignore @@ -0,0 +1,2 @@ +/build +/demo \ No newline at end of file diff --git a/androidplugin/plugin/build.gradle.kts b/androidplugin/plugin/build.gradle.kts new file mode 100644 index 0000000..347b597 --- /dev/null +++ b/androidplugin/plugin/build.gradle.kts @@ -0,0 +1,44 @@ +import com.android.build.gradle.internal.tasks.factory.dependsOn + +plugins { + id("com.android.library") + id("org.jetbrains.kotlin.android") +} + +// TODO: Update value to your plugin's name. +val pluginName = "gdtiltfive" + +// TODO: Update value to match your plugin's package name. +val pluginPackageName = "org.tiltfivegodot.plugin" + +android { + namespace = pluginPackageName + compileSdk = 33 + + buildFeatures { + buildConfig = true + } + + defaultConfig { + minSdk = 21 + + manifestPlaceholders["godotPluginName"] = pluginName + manifestPlaceholders["godotPluginPackageName"] = pluginPackageName + buildConfigField("String", "GODOT_PLUGIN_NAME", "\"${pluginName}\"") + setProperty("archivesBaseName", pluginName) + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + kotlinOptions { + jvmTarget = "17" + } +} + +dependencies { + implementation("org.godotengine:godot:4.2.0.stable") + implementation(files("libs/TiltFiveAndroidClient.jar")) +} + diff --git a/androidplugin/plugin/src/main/AndroidManifest.xml b/androidplugin/plugin/src/main/AndroidManifest.xml new file mode 100644 index 0000000..765c0a7 --- /dev/null +++ b/androidplugin/plugin/src/main/AndroidManifest.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + diff --git a/androidplugin/plugin/src/main/java/org/tiltfivegodot/plugin/GodotAndroidPlugin.java b/androidplugin/plugin/src/main/java/org/tiltfivegodot/plugin/GodotAndroidPlugin.java new file mode 100644 index 0000000..72f977e --- /dev/null +++ b/androidplugin/plugin/src/main/java/org/tiltfivegodot/plugin/GodotAndroidPlugin.java @@ -0,0 +1,41 @@ +package org.tiltfivegodot.plugin; + +import com.tiltfive.client.TiltFiveClient; +import org.godotengine.godot.Godot; +import org.godotengine.godot.plugin.GodotPlugin; +import org.godotengine.godot.plugin.UsedByGodot; +import android.util.Log; + +public class GodotAndroidPlugin extends GodotPlugin { + + private TiltFiveClient tiltFiveClient; + private long platformContext = 0; + + public GodotAndroidPlugin(Godot godot) { + super(godot); + } + + @Override + public String getPluginName() { + return BuildConfig.GODOT_PLUGIN_NAME; + } + + @Override + public void onGodotSetupCompleted() { + super.onGodotSetupCompleted(); + + tiltFiveClient = new TiltFiveClient(getActivity().getApplicationContext()); + platformContext = tiltFiveClient.newPlatformContext(); + } + + @UsedByGodot + private String getPlatformContextString() { + return Long.toString(platformContext); + } + + // Using this from gdscript will cause a crash + @UsedByGodot + private long getPlatformContext() { + return platformContext; + } +} \ No newline at end of file diff --git a/androidplugin/settings.gradle.kts b/androidplugin/settings.gradle.kts new file mode 100644 index 0000000..9debf0d --- /dev/null +++ b/androidplugin/settings.gradle.kts @@ -0,0 +1,20 @@ +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + maven("https://plugins.gradle.org/m2/") + maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") + } +} + +// TODO: Update project's name. +rootProject.name = "gdtiltfive" +include(":plugin") diff --git a/example.csharp/addons/tiltfive/T5Interface.cs b/example.csharp/addons/tiltfive/T5Interface.cs index 30e1527..f717712 100644 --- a/example.csharp/addons/tiltfive/T5Interface.cs +++ b/example.csharp/addons/tiltfive/T5Interface.cs @@ -45,7 +45,38 @@ class XRRigState { public override void _EnterTree() { base._EnterTree(); + CreateXRInterface(); + } + + public override void _ExitTree() + { + DestroyXRInterface(); + base._ExitTree(); + } + + public override void _Notification(int what) + { + if (what == NotificationApplicationPaused) + { + DestroyXRInterface(); + } + else if (what == NotificationApplicationResumed) + { + CreateXRInterface(); + + if(!xrInterface.IsInitialized()) + { + xrInterface.Initialize(); + } + } + } + private void CreateXRInterface() + { + if (xrInterface != null) + { + return; + } xrInterface = ClassDB.Instantiate("TiltFiveXRInterface").As(); xrInterface.Set("application_id", T5ProjectSettings.ApplicationID); @@ -59,8 +90,12 @@ public override void _EnterTree() xrInterface.Connect("service_event", Callable.From(_OnServiceEvent)); } - public override void _ExitTree() + private void DestroyXRInterface() { + if (xrInterface == null) + { + return; + } xrInterface.Disconnect("service_event", Callable.From(_OnServiceEvent)); xrInterface.Disconnect("glasses_event", Callable.From(_OnGlassesEvent)); @@ -69,8 +104,8 @@ public override void _ExitTree() xrInterface.Call("uninitialize"); } XRServer.RemoveInterface(xrInterface as XRInterface); - - base._ExitTree(); + xrInterface = null; + glassesDictionary.Clear(); } public override void _Ready() diff --git a/example.csharp/addons/tiltfive/plugin.cs b/example.csharp/addons/tiltfive/plugin.cs index 8839ab2..2ca8d3e 100644 --- a/example.csharp/addons/tiltfive/plugin.cs +++ b/example.csharp/addons/tiltfive/plugin.cs @@ -1,5 +1,6 @@ #if TOOLS using Godot; +using Godot.Collections; using System; [Tool] @@ -7,6 +8,8 @@ public partial class plugin : EditorPlugin { static bool isInitialized = false; + AndroidExportPlugin _androidExportPlugin; + private void Setup() { if (!isInitialized) @@ -20,8 +23,15 @@ private void Setup() public override void _EnterTree() { T5ProjectSettings.setup_properties(); + _androidExportPlugin = new AndroidExportPlugin(); + AddExportPlugin(_androidExportPlugin); Setup(); + } + public override void _ExitTree() + { + RemoveExportPlugin(_androidExportPlugin); + _androidExportPlugin = null; } public override void _EnablePlugin() @@ -40,4 +50,32 @@ public override void _DisablePlugin() } } } + +[Tool] +public partial class AndroidExportPlugin : EditorExportPlugin +{ + static string _plugin_name = "gdtiltfive"; + + public override bool _SupportsPlatform(EditorExportPlatform platform) { + if(platform is EditorExportPlatformAndroid) { + return true; + } + return false; + } + + public override string[] _GetAndroidLibraries(EditorExportPlatform platform, bool debug) { + if(platform is EditorExportPlatformAndroid) { + if(debug) { + return new string[] {"tiltfive/bin/android/" + _plugin_name + "-debug.aar"}; + } else { + return new string[] {"tiltfive/bin/android/" + _plugin_name + "-release.aar"}; + } + } + return null; + } + + public override string _GetName() { + return _plugin_name; + } +} #endif diff --git a/example.csharp/addons/tiltfive/tiltfive.gdextension b/example.csharp/addons/tiltfive/tiltfive.gdextension index 96362ae..850895b 100644 --- a/example.csharp/addons/tiltfive/tiltfive.gdextension +++ b/example.csharp/addons/tiltfive/tiltfive.gdextension @@ -8,15 +8,32 @@ T5Gameboard = "res://addons/tiltfive/assets/board.svg" [libraries] -windows.x86_64.debug = "res://addons/tiltfive/bin/libgdtiltfive.windows.template_debug.x86_64.dll" -windows.x86_64.release = "res://addons/tiltfive/bin/libgdtiltfive.windows.template_release.x86_64.dll" +windows.x86_64.debug = "res://addons/tiltfive/bin/windows/x86_64/libgdtiltfive.windows.template_debug.x86_64.dll" +windows.x86_64.release = "res://addons/tiltfive/bin/windows/x86_64/libgdtiltfive.windows.template_release.x86_64.dll" +linux.x86_64.debug = "res://addons/tiltfive/bin/linux/x86_64/libgdtiltfive.linux.template_debug.x86_64.so" +linux.x86_64.release = "res://addons/tiltfive/bin/linux/x86_64/libgdtiltfive.linux.template_release.x86_64.so" +android.debug.arm64 = "res://addons/tiltfive/bin/android/arm64/libgdtiltfive.android.template_debug.arm64.so" +android.release.arm64 = "res://addons/tiltfive/bin/android/arm64/libgdtiltfive.android.template_release.arm64.so" + [dependencies] windows.x86_64.debug = { - "res://addons/tiltfive/bin/TiltFiveNative.dll" : "" + "res://addons/tiltfive/bin/windows/x86_64/TiltFiveNative.dll" : "" } windows.x86_64.release = { - "res://addons/tiltfive/bin/TiltFiveNative.dll" : "" + "res://addons/tiltfive/bin/windows/x86_64/TiltFiveNative.dll" : "" +} +linux.x86_64.debug = { + "res://addons/tiltfive/bin/linux/x86_64/libTiltFiveNative.so" : "" +} +linux.x86_64.release = { + "res://addons/tiltfive/bin/linux/x86_64/libTiltFiveNative.so" : "" +} +android.arm64.debug = { + "res://addons/tiltfive/bin/android/arm64/libTiltFiveNative.so" : "" +} +android.arm64.release = { + "res://addons/tiltfive/bin/android/arm64/libTiltFiveNative.so" : "" } diff --git a/example.gd/addons/tiltfive/T5Interface.gd b/example.gd/addons/tiltfive/T5Interface.gd index 3f7e5df..704aa1c 100644 --- a/example.gd/addons/tiltfive/T5Interface.gd +++ b/example.gd/addons/tiltfive/T5Interface.gd @@ -28,6 +28,23 @@ func get_tilt_five_xr_interface() -> TiltFiveXRInterface: return tilt_five_xr_interface func _enter_tree(): + process_mode = Node.PROCESS_MODE_ALWAYS + _create_xr_interface() + +func _exit_tree(): + _destroy_xr_interface() + +func _notification(what): + match what: + NOTIFICATION_APPLICATION_PAUSED: + _destroy_xr_interface() + NOTIFICATION_APPLICATION_RESUMED: + _create_xr_interface() + if !tilt_five_xr_interface.is_initialized(): + tilt_five_xr_interface.initialize() + +func _create_xr_interface(): + if (tilt_five_xr_interface): return tilt_five_xr_interface = TiltFiveXRInterface.new() if tilt_five_xr_interface: tilt_five_xr_interface.application_id = T5ProjectSettings.application_id @@ -39,7 +56,7 @@ func _enter_tree(): tilt_five_xr_interface.glasses_event.connect(_on_glasses_event) tilt_five_xr_interface.service_event.connect(_on_service_event) -func _exit_tree(): +func _destroy_xr_interface(): if tilt_five_xr_interface: tilt_five_xr_interface.service_event.disconnect(_on_service_event) tilt_five_xr_interface.glasses_event.disconnect(_on_glasses_event) @@ -48,6 +65,7 @@ func _exit_tree(): XRServer.remove_interface(tilt_five_xr_interface) tilt_five_xr_interface = null + id_to_state.clear() func _ready(): if not t5_manager: diff --git a/example.gd/addons/tiltfive/plugin.gd b/example.gd/addons/tiltfive/plugin.gd index 942637c..bdf8921 100644 --- a/example.gd/addons/tiltfive/plugin.gd +++ b/example.gd/addons/tiltfive/plugin.gd @@ -3,21 +3,43 @@ extends EditorPlugin static var _initialized := false +var export_plugin: AndroidExportPlugin + func _setup(): if not _initialized: - #add_custom_type("T5Manager", "Node", preload("res://addons/tiltfive/T5Manager.gd"), preload("res://addons/tiltfive/assets/glasses.svg")) add_autoload_singleton("T5Interface", "res://addons/tiltfive/T5Interface.gd") _initialized = true func _enter_tree(): + export_plugin = AndroidExportPlugin.new() + add_export_plugin(export_plugin) T5ProjectSettings.setup_properties() _setup() +func _exit_tree(): + remove_export_plugin(export_plugin) + export_plugin = null + func _enable_plugin(): _setup() func _disable_plugin(): if _initialized: - #remove_custom_type("T5Manager") remove_autoload_singleton("T5Interface") +class AndroidExportPlugin extends EditorExportPlugin: + var _plugin_name = "gdtiltfive" + + func _supports_platform(platform): + if platform is EditorExportPlatformAndroid: + return true + return false + + func _get_android_libraries(platform, debug): + if debug: + return PackedStringArray(["tiltfive/bin/android/" + _plugin_name + "-debug.aar"]) + else: + return PackedStringArray(["tiltfive/bin/android/" + _plugin_name + "-release.aar"]) + + func _get_name(): + return _plugin_name diff --git a/example.gd/addons/tiltfive/tiltfive.gdextension b/example.gd/addons/tiltfive/tiltfive.gdextension index aa4e9e7..850895b 100644 --- a/example.gd/addons/tiltfive/tiltfive.gdextension +++ b/example.gd/addons/tiltfive/tiltfive.gdextension @@ -8,24 +8,32 @@ T5Gameboard = "res://addons/tiltfive/assets/board.svg" [libraries] -windows.x86_64.debug = "res://addons/tiltfive/bin/libgdtiltfive.windows.template_debug.x86_64.dll" -windows.x86_64.release = "res://addons/tiltfive/bin/libgdtiltfive.windows.template_release.x86_64.dll" -linux.x86_64.debug = "res://addons/tiltfive/bin/libgdtiltfive.linux.template_debug.x86_64.so" -linux.x86_64.release = "res://addons/tiltfive/bin/libgdtiltfive.linux.template_release.x86_64.so" +windows.x86_64.debug = "res://addons/tiltfive/bin/windows/x86_64/libgdtiltfive.windows.template_debug.x86_64.dll" +windows.x86_64.release = "res://addons/tiltfive/bin/windows/x86_64/libgdtiltfive.windows.template_release.x86_64.dll" +linux.x86_64.debug = "res://addons/tiltfive/bin/linux/x86_64/libgdtiltfive.linux.template_debug.x86_64.so" +linux.x86_64.release = "res://addons/tiltfive/bin/linux/x86_64/libgdtiltfive.linux.template_release.x86_64.so" +android.debug.arm64 = "res://addons/tiltfive/bin/android/arm64/libgdtiltfive.android.template_debug.arm64.so" +android.release.arm64 = "res://addons/tiltfive/bin/android/arm64/libgdtiltfive.android.template_release.arm64.so" [dependencies] windows.x86_64.debug = { - "res://addons/tiltfive/bin/TiltFiveNative.dll" : "" + "res://addons/tiltfive/bin/windows/x86_64/TiltFiveNative.dll" : "" } windows.x86_64.release = { - "res://addons/tiltfive/bin/TiltFiveNative.dll" : "" + "res://addons/tiltfive/bin/windows/x86_64/TiltFiveNative.dll" : "" } linux.x86_64.debug = { - "res://addons/tiltfive/bin/libTiltFiveNative.so" : "" + "res://addons/tiltfive/bin/linux/x86_64/libTiltFiveNative.so" : "" } linux.x86_64.release = { - "res://addons/tiltfive/bin/libTiltFiveNative.so" : "" + "res://addons/tiltfive/bin/linux/x86_64/libTiltFiveNative.so" : "" +} +android.arm64.debug = { + "res://addons/tiltfive/bin/android/arm64/libTiltFiveNative.so" : "" +} +android.arm64.release = { + "res://addons/tiltfive/bin/android/arm64/libTiltFiveNative.so" : "" } diff --git a/extension/T5Integration/T5Service.cpp b/extension/T5Integration/T5Service.cpp index 2cb8874..4045794 100644 --- a/extension/T5Integration/T5Service.cpp +++ b/extension/T5Integration/T5Service.cpp @@ -20,7 +20,7 @@ T5Service::~T5Service() { stop_service(); } -bool T5Service::start_service(const std::string_view application_id, std::string_view application_version, uint8_t sdk_type) { +bool T5Service::start_service(const std::string_view application_id, std::string_view application_version, uint8_t sdk_type, void* platform_context) { if (_state.is_current(T5ServiceState::RUNNING)) return true; if (_state.set_and_was_toggled(T5ServiceState::STARTING)) { @@ -34,7 +34,7 @@ bool T5Service::start_service(const std::string_view application_id, std::string clientInfo.applicationVersion = application_version.data(); clientInfo.sdkType = sdk_type; - auto result = t5CreateContext(&_context, &clientInfo, nullptr); + auto result = t5CreateContext(&_context, &clientInfo, platform_context); if (result != T5_SUCCESS) { LOG_T5_ERROR(result); diff --git a/extension/T5Integration/T5Service.h b/extension/T5Integration/T5Service.h index e570d88..84c3a58 100644 --- a/extension/T5Integration/T5Service.h +++ b/extension/T5Integration/T5Service.h @@ -59,7 +59,7 @@ class T5Service { T5Service(); virtual ~T5Service(); - bool start_service(const std::string_view applicationID, std::string_view applicationVersion, uint8_t sdk_type = 0); + bool start_service(const std::string_view applicationID, std::string_view applicationVersion, uint8_t sdk_type = 0, void* platform_context = nullptr); void stop_service(); bool is_service_started(); diff --git a/extension/T5Integration/Wand.cpp b/extension/T5Integration/Wand.cpp index 343c1aa..05f470d 100644 --- a/extension/T5Integration/Wand.cpp +++ b/extension/T5Integration/Wand.cpp @@ -75,7 +75,7 @@ bool WandService::start(T5_Glasses handle) { _glasses_handle = handle; _last_wand_error = T5_SUCCESS; _running = true; - _thread = std::jthread([this](std::stop_token s_token) { monitor_wands(s_token); }); + _thread = jthrd::jthread([this](jthrd::stop_token s_token) { monitor_wands(s_token); }); return _running; } @@ -122,7 +122,7 @@ T5_Result WandService::get_last_error() { return tmp; } -void WandService::monitor_wands(std::stop_token s_token) { +void WandService::monitor_wands(jthrd::stop_token s_token) { if (!configure_wand_tracking(true)) return; diff --git a/extension/T5Integration/Wand.h b/extension/T5Integration/Wand.h index dc59afd..a3e773a 100644 --- a/extension/T5Integration/Wand.h +++ b/extension/T5Integration/Wand.h @@ -1,9 +1,12 @@ #pragma once #include #include +#include #include #include +#include + using namespace std::chrono_literals; namespace T5Integration { @@ -66,12 +69,12 @@ class WandService { private: bool configure_wand_tracking(bool enable); - void monitor_wands(std::stop_token s_token); + void monitor_wands(jthrd::stop_token s_token); T5_Glasses _glasses_handle; WandList _wand_list; - std::jthread _thread; + jthrd::jthread _thread; std::mutex _list_access; std::atomic_bool _running; diff --git a/extension/T5Integration/jthread.cpp b/extension/T5Integration/jthread.cpp new file mode 100644 index 0000000..712c44f --- /dev/null +++ b/extension/T5Integration/jthread.cpp @@ -0,0 +1,169 @@ +#include "jthread.h" + +#ifndef __cpp_lib_jthread +#include +#include +#include +#include + +namespace jthrd { +struct StopState { + std::atomic request_stop = false; + + // this is not efficient, but it only needs to be correct + std::mutex callbacks_mutex; + std::vector callbacks; + + void add_callback(stop_callback* callback) { + auto lk = std::unique_lock(callbacks_mutex); + if (!request_stop) { + callbacks.push_back(callback); + } else { + callback->callback(); + } + } + + void remove_callback(stop_callback* callback) { + auto lk = std::unique_lock(callbacks_mutex); + auto i = std::find(begin(callbacks), end(callbacks), callback); + if (i != end(callbacks)) { + callbacks.erase(i); + } + } + + void exec_callbacks() { + auto lk = std::unique_lock(callbacks_mutex); + for (const auto& cb : callbacks) { + assert(cb->callback); + cb->callback(); + } + callbacks.clear(); + } +}; + +stop_token::stop_token() = default; + +stop_token::stop_token(std::shared_ptr s) : + state(s) {} + +stop_token::stop_token(const stop_token& other) noexcept = default; +stop_token::stop_token(stop_token&& other) noexcept = default; +stop_token::~stop_token() = default; + +stop_token& stop_token::operator=(const stop_token& other) noexcept = default; +stop_token& stop_token::operator=(stop_token&& other) noexcept = default; + +bool stop_token::stop_requested() noexcept { + if (state) { + return state->request_stop; + } + return false; +} + +bool stop_token::stop_possible() const noexcept { + return static_cast(state); +} + +void stop_token::swap(stop_token& other) noexcept { + state.swap(other.state); +} + +stop_source::stop_source() : + state(std::make_shared()) {} + +stop_source::stop_source(nostopstate_t nss) noexcept {} + +stop_source::stop_source(const stop_source& other) noexcept = default; +stop_source::stop_source(stop_source&& other) noexcept = default; +stop_source::~stop_source() = default; + +stop_source& stop_source::operator=(const stop_source& other) noexcept = default; +stop_source& stop_source::operator=(stop_source&& other) noexcept = default; + +stop_token stop_source::get_token() const noexcept { + return stop_token{ state }; +} + +bool stop_source::request_stop() noexcept { + if (state && (state->request_stop.exchange(true) == false)) { + state->exec_callbacks(); + return true; + } + return false; +} + +bool stop_source::stop_possible() const noexcept { + return static_cast(state); +} + +void stop_source::swap(stop_source& other) noexcept { + state.swap(other.state); +} + +stop_callback::~stop_callback() { + if (token.state) { + token.state->remove_callback(this); + } +} + +void stop_callback::self_register() { + if (token.state) { + token.state->add_callback(this); + } else { + callback(); + } +} + +jthread::jthread() noexcept + : + stop{ nostopstate } {} + +jthread::jthread(jthread&& other) noexcept = default; + +jthread::~jthread() { + if (joinable()) { + request_stop(); + join(); + } +} + +jthread& jthread::operator=(jthread&& other) noexcept = default; + +jthread::id jthread::get_id() const noexcept { + return impl.get_id(); +} + +bool jthread::joinable() const noexcept { + return impl.joinable(); +} + +void jthread::join() { + impl.join(); +} + +void jthread::detach() { + impl.detach(); +} + +stop_source jthread::get_stop_source() noexcept { + if (joinable()) { + return stop; + } else { + return stop_source{ nostopstate }; + } +} + +stop_token jthread::get_stop_token() noexcept { + return get_stop_source().get_token(); +} + +bool jthread::request_stop() noexcept { + return stop.request_stop(); +} + +void jthread::swap(jthread& other) noexcept { + stop.swap(other.stop); + impl.swap(other.impl); +} +} //namespace jthrd +#endif \ No newline at end of file diff --git a/extension/T5Integration/jthread.h b/extension/T5Integration/jthread.h new file mode 100644 index 0000000..219b780 --- /dev/null +++ b/extension/T5Integration/jthread.h @@ -0,0 +1,158 @@ +/* This provides a drop in replacement for std::jthread, std::stop_token, + * std::stop_source, and std::stop_callback for compilers that do not yet + * support them. This was pulled from a reference implementation of the + * jthread proposal + */ +#ifndef _JTHREAD_H_ +#define _JTHREAD_H_ + +#include +#include +#include + +namespace jthrd { +#ifdef __cpp_lib_jthread +// If jthread is available, pull it into this namespace +using std::jthread; +using std::nostopstate; +using std::stop_callback; +using std::stop_source; +using std::stop_token; +#else +// otherwise, implement a drop in replacement in this namespace +struct StopState; + +struct nostopstate_t {}; +constexpr auto nostopstate = nostopstate_t{}; + +class stop_token { +public: + stop_token(); + explicit stop_token(std::shared_ptr state); + stop_token(const stop_token& other) noexcept; + stop_token(stop_token&& other) noexcept; + ~stop_token(); + + stop_token& operator=(const stop_token& other) noexcept; + stop_token& operator=(stop_token&& other) noexcept; + + [[nodiscard]] bool stop_requested() noexcept; + [[nodiscard]] bool stop_possible() const noexcept; + + void swap(stop_token& other) noexcept; + +private: + std::shared_ptr state; + + friend class stop_callback; +}; + +inline void swap(stop_token& lhs, stop_token& rhs) noexcept { + lhs.swap(rhs); +} + +class stop_source { +public: + stop_source(); + explicit stop_source(nostopstate_t nss) noexcept; + stop_source(const stop_source& other) noexcept; + stop_source(stop_source&& other) noexcept; + ~stop_source(); + + stop_source& operator=(const stop_source& other) noexcept; + stop_source& operator=(stop_source&& other) noexcept; + + [[nodiscard]] stop_token get_token() const noexcept; + + bool request_stop() noexcept; + [[nodiscard]] bool stop_possible() const noexcept; + + void swap(stop_source& other) noexcept; + +private: + std::shared_ptr state; +}; + +inline void swap(stop_source& lhs, stop_source& rhs) noexcept { + lhs.swap(rhs); +} + +class stop_callback { +public: + template + explicit stop_callback(const stop_token& st, C&& cb) : + token(st), callback(cb) { + self_register(); + } + + template + explicit stop_callback(stop_token&& st, C&& cb) : + token(std::forward(st)), callback(cb) { + self_register(); + } + + ~stop_callback(); + +private: + stop_token token; + std::function callback; + + void self_register(); + + stop_callback(const stop_callback&) = delete; + stop_callback(stop_callback&&) = delete; + stop_callback& operator=(const stop_callback& other) noexcept = delete; + stop_callback& operator=(stop_callback&& other) noexcept = delete; + + friend struct StopState; +}; + +class jthread { +public: + using id = std::thread::id; + + jthread() noexcept; + jthread(jthread&& other) noexcept; + + template + requires std::is_invocable_v, std::decay_t...> + explicit jthread(Function&& f, Args&&... args) : + impl(std::forward(f), std::forward(args)...) {} + + template + requires std::is_invocable_v, stop_token, std::decay_t...> + explicit jthread(Function&& f, Args&&... args) : + stop(), + impl(std::forward(f), stop.get_token(), std::forward(args)...) {} + + ~jthread(); + + jthread& operator=(jthread&& other) noexcept; + + [[nodiscard]] id get_id() const noexcept; + [[nodiscard]] bool joinable() const noexcept; + + void join(); + void detach(); + + [[nodiscard]] stop_source get_stop_source() noexcept; + [[nodiscard]] stop_token get_stop_token() noexcept; + bool request_stop() noexcept; + + void swap(jthread& other) noexcept; + +private: + stop_source stop = {}; + std::thread impl; + + jthread(const jthread&) = delete; + jthread& operator=(const jthread& other) noexcept = delete; +}; + +inline void swap(jthread& lhs, jthread& rhs) noexcept { + lhs.swap(rhs); +} +#endif +} //namespace jthrd + +#endif //_JTHREAD_H_ \ No newline at end of file diff --git a/extension/src/TiltFiveXRInterface.cpp b/extension/src/TiltFiveXRInterface.cpp index f40d36b..5a7e523 100644 --- a/extension/src/TiltFiveXRInterface.cpp +++ b/extension/src/TiltFiveXRInterface.cpp @@ -1,4 +1,5 @@ #include "TiltFiveXRInterface.h" +#include #include #include #include @@ -139,6 +140,23 @@ TiltFiveXRInterface::GlassesIndexEntry* TiltFiveXRInterface::lookup_glasses_by_v return nullptr; } +bool TiltFiveXRInterface::setup_android() { + if (OS::get_singleton()->get_name() == "Android") { + if (Engine::get_singleton()->has_singleton("gdtiltfive")) { + auto gdtiltfive = Engine::get_singleton()->get_singleton("gdtiltfive"); + String pc_string = gdtiltfive->call("getPlatformContextString"); + _platform_context = pc_string.to_int(); + LOG_MESSAGE("Platform context: ", _platform_context); + } else { + LOG_ERROR("gdtiltfive plugin was not found and is required for Tilt Five to work on Android"); + return false; + } + } else { + _platform_context = 0; + } + return true; +} + bool TiltFiveXRInterface::_is_initialized() const { return _initialised; } @@ -155,6 +173,8 @@ bool TiltFiveXRInterface::_initialize() { t5_service = GodotT5ObjectRegistry::service(); ERR_FAIL_COND_V_MSG(!t5_service, false, "Couldn't obtain GodotT5Service singleton"); + ERR_FAIL_COND_V_MSG(!setup_android(), false, "Couldn't setup Android platform context"); + RenderingServer* rendering_server = RenderingServer::get_singleton(); ERR_FAIL_NULL_V(rendering_server, false); RenderingDevice* rendering_device = rendering_server->get_rendering_device(); @@ -167,7 +187,11 @@ bool TiltFiveXRInterface::_initialize() { auto ai = application_id.ascii(); auto av = application_version.ascii(); - bool is_started = t5_service->start_service(ai.get_data(), av.get_data(), kSdkTypeCommunityGodot); + // I don't like this but long is how it is returned from the T5 Java API. + auto platform_context = reinterpret_cast(_platform_context); + LOG_MESSAGE("Platform context: ", _platform_context); + + bool is_started = t5_service->start_service(ai.get_data(), av.get_data(), kSdkTypeCommunityGodot, platform_context); ERR_FAIL_COND_V_MSG(!is_started, false, "Couldn't start T5 Service"); _initialised = true; diff --git a/extension/src/TiltFiveXRInterface.h b/extension/src/TiltFiveXRInterface.h index 98b9ef8..ad03d42 100644 --- a/extension/src/TiltFiveXRInterface.h +++ b/extension/src/TiltFiveXRInterface.h @@ -146,6 +146,8 @@ class TiltFiveXRInterface : public XRInterfaceExtension { ~TiltFiveXRInterface(); protected: + bool setup_android(); + static void _bind_methods(); void _start_display(GlassesIndexEntry &entry, SubViewport *viewport, T5Origin3D *xr_origin); @@ -166,6 +168,7 @@ class TiltFiveXRInterface : public XRInterfaceExtension { String application_version; float _trigger_click_threshold = 0.5; bool _is_debug_logging = false; + long _platform_context = 0; std::vector _glasses_index; std::vector _glasses_events;