mirror of
https://github.com/godotengine/godot.git
synced 2024-11-21 19:42:43 +00:00
Improve support for XR projects
This commit is contained in:
parent
835808ed8f
commit
9dc0543da7
1
.github/workflows/android_builds.yml
vendored
1
.github/workflows/android_builds.yml
vendored
@ -85,6 +85,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cd platform/android/java
|
cd platform/android/java
|
||||||
./gradlew generateGodotEditor
|
./gradlew generateGodotEditor
|
||||||
|
./gradlew generateGodotMetaEditor
|
||||||
cd ../../..
|
cd ../../..
|
||||||
ls -l bin/android_editor_builds/
|
ls -l bin/android_editor_builds/
|
||||||
|
|
||||||
|
@ -1037,7 +1037,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
|
|||||||
if (arg == "--audio-driver" ||
|
if (arg == "--audio-driver" ||
|
||||||
arg == "--display-driver" ||
|
arg == "--display-driver" ||
|
||||||
arg == "--rendering-method" ||
|
arg == "--rendering-method" ||
|
||||||
arg == "--rendering-driver") {
|
arg == "--rendering-driver" ||
|
||||||
|
arg == "--xr-mode") {
|
||||||
if (N) {
|
if (N) {
|
||||||
forwardable_cli_arguments[CLI_SCOPE_TOOL].push_back(arg);
|
forwardable_cli_arguments[CLI_SCOPE_TOOL].push_back(arg);
|
||||||
forwardable_cli_arguments[CLI_SCOPE_TOOL].push_back(N->get());
|
forwardable_cli_arguments[CLI_SCOPE_TOOL].push_back(N->get());
|
||||||
|
@ -271,17 +271,14 @@ OpenXRAPI *OpenXRAPI::singleton = nullptr;
|
|||||||
Vector<OpenXRExtensionWrapper *> OpenXRAPI::registered_extension_wrappers;
|
Vector<OpenXRExtensionWrapper *> OpenXRAPI::registered_extension_wrappers;
|
||||||
|
|
||||||
bool OpenXRAPI::openxr_is_enabled(bool p_check_run_in_editor) {
|
bool OpenXRAPI::openxr_is_enabled(bool p_check_run_in_editor) {
|
||||||
// @TODO we need an overrule switch so we can force enable openxr, i.e run "godot --openxr_enabled"
|
if (XRServer::get_xr_mode() == XRServer::XRMODE_DEFAULT) {
|
||||||
|
if (Engine::get_singleton()->is_editor_hint() && p_check_run_in_editor) {
|
||||||
if (Engine::get_singleton()->is_editor_hint() && p_check_run_in_editor) {
|
return GLOBAL_GET("xr/openxr/enabled.editor");
|
||||||
// Disabled for now, using XR inside of the editor we'll be working on during the coming months.
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
if (XRServer::get_xr_mode() == XRServer::XRMODE_DEFAULT) {
|
|
||||||
return GLOBAL_GET("xr/openxr/enabled");
|
|
||||||
} else {
|
} else {
|
||||||
return XRServer::get_xr_mode() == XRServer::XRMODE_ON;
|
return GLOBAL_GET("xr/openxr/enabled");
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return XRServer::get_xr_mode() == XRServer::XRMODE_ON;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -557,14 +554,11 @@ bool OpenXRAPI::create_instance() {
|
|||||||
extension_ptrs.push_back(enabled_extensions[i].get_data());
|
extension_ptrs.push_back(enabled_extensions[i].get_data());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get our project name
|
|
||||||
String project_name = GLOBAL_GET("application/config/name");
|
|
||||||
|
|
||||||
// Create our OpenXR instance
|
// Create our OpenXR instance
|
||||||
XrApplicationInfo application_info{
|
XrApplicationInfo application_info{
|
||||||
"", // applicationName, we'll set this down below
|
"Godot Engine", // applicationName, if we're running a game we'll update this down below.
|
||||||
1, // applicationVersion, we don't currently have this
|
1, // applicationVersion, we don't currently have this
|
||||||
"Godot Game Engine", // engineName
|
"Godot Engine", // engineName
|
||||||
VERSION_MAJOR * 10000 + VERSION_MINOR * 100 + VERSION_PATCH, // engineVersion 4.0 -> 40000, 4.0.1 -> 40001, 4.1 -> 40100, etc.
|
VERSION_MAJOR * 10000 + VERSION_MINOR * 100 + VERSION_PATCH, // engineVersion 4.0 -> 40000, 4.0.1 -> 40001, 4.1 -> 40100, etc.
|
||||||
XR_API_VERSION_1_0 // apiVersion
|
XR_API_VERSION_1_0 // apiVersion
|
||||||
};
|
};
|
||||||
@ -588,7 +582,11 @@ bool OpenXRAPI::create_instance() {
|
|||||||
extension_ptrs.ptr() // enabledExtensionNames
|
extension_ptrs.ptr() // enabledExtensionNames
|
||||||
};
|
};
|
||||||
|
|
||||||
copy_string_to_char_buffer(project_name, instance_create_info.applicationInfo.applicationName, XR_MAX_APPLICATION_NAME_SIZE);
|
// Get our project name
|
||||||
|
String project_name = GLOBAL_GET("application/config/name");
|
||||||
|
if (!project_name.is_empty()) {
|
||||||
|
copy_string_to_char_buffer(project_name, instance_create_info.applicationInfo.applicationName, XR_MAX_APPLICATION_NAME_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
XrResult result = xrCreateInstance(&instance_create_info, &instance);
|
XrResult result = xrCreateInstance(&instance_create_info, &instance);
|
||||||
ERR_FAIL_COND_V_MSG(XR_FAILED(result), false, "Failed to create XR instance.");
|
ERR_FAIL_COND_V_MSG(XR_FAILED(result), false, "Failed to create XR instance.");
|
||||||
@ -2583,7 +2581,6 @@ OpenXRAPI::OpenXRAPI() {
|
|||||||
|
|
||||||
if (Engine::get_singleton()->is_editor_hint()) {
|
if (Engine::get_singleton()->is_editor_hint()) {
|
||||||
// Enabled OpenXR in the editor? Adjust our settings for the editor
|
// Enabled OpenXR in the editor? Adjust our settings for the editor
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Load settings from project settings
|
// Load settings from project settings
|
||||||
int form_factor_setting = GLOBAL_GET("xr/openxr/form_factor");
|
int form_factor_setting = GLOBAL_GET("xr/openxr/form_factor");
|
||||||
|
@ -12,6 +12,7 @@ allprojects {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
maven { url "https://plugins.gradle.org/m2/" }
|
maven { url "https://plugins.gradle.org/m2/" }
|
||||||
|
maven { url "https://s01.oss.sonatype.org/content/repositories/snapshots/"}
|
||||||
|
|
||||||
// Godot user plugins custom maven repos
|
// Godot user plugins custom maven repos
|
||||||
String[] mavenRepos = getGodotPluginsMavenRepos()
|
String[] mavenRepos = getGodotPluginsMavenRepos()
|
||||||
|
@ -11,6 +11,7 @@ pluginManagement {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
maven { url "https://plugins.gradle.org/m2/" }
|
maven { url "https://plugins.gradle.org/m2/" }
|
||||||
|
maven { url "https://s01.oss.sonatype.org/content/repositories/snapshots/"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,12 +18,14 @@ allprojects {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
maven { url "https://plugins.gradle.org/m2/" }
|
maven { url "https://plugins.gradle.org/m2/" }
|
||||||
|
maven { url "https://s01.oss.sonatype.org/content/repositories/snapshots/"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
supportedAbis = ["arm32", "arm64", "x86_32", "x86_64"]
|
supportedAbis = ["arm32", "arm64", "x86_32", "x86_64"]
|
||||||
supportedFlavors = ["editor", "template"]
|
supportedFlavors = ["editor", "template"]
|
||||||
|
supportedEditorVendors = ["google", "meta"]
|
||||||
supportedFlavorsBuildTypes = [
|
supportedFlavorsBuildTypes = [
|
||||||
"editor": ["dev", "debug", "release"],
|
"editor": ["dev", "debug", "release"],
|
||||||
"template": ["dev", "debug", "release"]
|
"template": ["dev", "debug", "release"]
|
||||||
@ -92,15 +94,20 @@ def templateExcludedBuildTask() {
|
|||||||
/**
|
/**
|
||||||
* Generates the build tasks for the given flavor
|
* Generates the build tasks for the given flavor
|
||||||
* @param flavor Must be one of the supported flavors ('template' / 'editor')
|
* @param flavor Must be one of the supported flavors ('template' / 'editor')
|
||||||
|
* @param editorVendor Must be one of the supported editor vendors ('google' / 'meta')
|
||||||
*/
|
*/
|
||||||
def generateBuildTasks(String flavor = "template") {
|
def generateBuildTasks(String flavor = "template", String editorVendor = "google") {
|
||||||
if (!supportedFlavors.contains(flavor)) {
|
if (!supportedFlavors.contains(flavor)) {
|
||||||
throw new GradleException("Invalid build flavor: $flavor")
|
throw new GradleException("Invalid build flavor: $flavor")
|
||||||
}
|
}
|
||||||
|
if (!supportedEditorVendors.contains(editorVendor)) {
|
||||||
|
throw new GradleException("Invalid editor vendor: $editorVendor")
|
||||||
|
}
|
||||||
|
|
||||||
|
String capitalizedEditorVendor = editorVendor.capitalize()
|
||||||
def buildTasks = []
|
def buildTasks = []
|
||||||
|
|
||||||
// Only build the apks and aar files for which we have native shared libraries unless we intend
|
// Only build the binary files for which we have native shared libraries unless we intend
|
||||||
// to run the scons build tasks.
|
// to run the scons build tasks.
|
||||||
boolean excludeSconsBuildTasks = excludeSconsBuildTasks()
|
boolean excludeSconsBuildTasks = excludeSconsBuildTasks()
|
||||||
boolean isTemplate = flavor == "template"
|
boolean isTemplate = flavor == "template"
|
||||||
@ -163,28 +170,28 @@ def generateBuildTasks(String flavor = "template") {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Copy the generated editor apk to the bin directory.
|
// Copy the generated editor apk to the bin directory.
|
||||||
String copyEditorApkTaskName = "copyEditor${capitalizedTarget}ApkToBin"
|
String copyEditorApkTaskName = "copyEditor${capitalizedEditorVendor}${capitalizedTarget}ApkToBin"
|
||||||
if (tasks.findByName(copyEditorApkTaskName) != null) {
|
if (tasks.findByName(copyEditorApkTaskName) != null) {
|
||||||
buildTasks += tasks.getByName(copyEditorApkTaskName)
|
buildTasks += tasks.getByName(copyEditorApkTaskName)
|
||||||
} else {
|
} else {
|
||||||
buildTasks += tasks.create(name: copyEditorApkTaskName, type: Copy) {
|
buildTasks += tasks.create(name: copyEditorApkTaskName, type: Copy) {
|
||||||
dependsOn ":editor:assemble${capitalizedTarget}"
|
dependsOn ":editor:assemble${capitalizedEditorVendor}${capitalizedTarget}"
|
||||||
from("editor/build/outputs/apk/${target}")
|
from("editor/build/outputs/apk/${editorVendor}/${target}")
|
||||||
into(androidEditorBuildsDir)
|
into(androidEditorBuildsDir)
|
||||||
include("android_editor-${target}*.apk")
|
include("android_editor-${editorVendor}-${target}*.apk")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the generated editor aab to the bin directory.
|
// Copy the generated editor aab to the bin directory.
|
||||||
String copyEditorAabTaskName = "copyEditor${capitalizedTarget}AabToBin"
|
String copyEditorAabTaskName = "copyEditor${capitalizedEditorVendor}${capitalizedTarget}AabToBin"
|
||||||
if (tasks.findByName(copyEditorAabTaskName) != null) {
|
if (tasks.findByName(copyEditorAabTaskName) != null) {
|
||||||
buildTasks += tasks.getByName(copyEditorAabTaskName)
|
buildTasks += tasks.getByName(copyEditorAabTaskName)
|
||||||
} else {
|
} else {
|
||||||
buildTasks += tasks.create(name: copyEditorAabTaskName, type: Copy) {
|
buildTasks += tasks.create(name: copyEditorAabTaskName, type: Copy) {
|
||||||
dependsOn ":editor:bundle${capitalizedTarget}"
|
dependsOn ":editor:bundle${capitalizedEditorVendor}${capitalizedTarget}"
|
||||||
from("editor/build/outputs/bundle/${target}")
|
from("editor/build/outputs/bundle/${editorVendor}${capitalizedTarget}")
|
||||||
into(androidEditorBuildsDir)
|
into(androidEditorBuildsDir)
|
||||||
include("android_editor-${target}*.aab")
|
include("android_editor-${editorVendor}-${target}*.aab")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -197,15 +204,27 @@ def generateBuildTasks(String flavor = "template") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate the Godot Editor Android apk.
|
* Generate the Godot Editor Android binaries.
|
||||||
*
|
*
|
||||||
* Note: Unless the 'generateNativeLibs` argument is specified, the Godot 'tools' shared libraries
|
* Note: Unless the 'generateNativeLibs` argument is specified, the Godot 'tools' shared libraries
|
||||||
* must have been generated (via scons) prior to running this gradle task.
|
* must have been generated (via scons) prior to running this gradle task.
|
||||||
* The task will only build the apk(s) for which the shared libraries is available.
|
* The task will only build the binaries for which the shared libraries is available.
|
||||||
*/
|
*/
|
||||||
task generateGodotEditor {
|
task generateGodotEditor {
|
||||||
gradle.startParameter.excludedTaskNames += templateExcludedBuildTask()
|
gradle.startParameter.excludedTaskNames += templateExcludedBuildTask()
|
||||||
dependsOn = generateBuildTasks("editor")
|
dependsOn = generateBuildTasks("editor", "google")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the Godot Editor Android binaries for Meta devices.
|
||||||
|
*
|
||||||
|
* Note: Unless the 'generateNativeLibs` argument is specified, the Godot 'tools' shared libraries
|
||||||
|
* must have been generated (via scons) prior to running this gradle task.
|
||||||
|
* The task will only build the binaries for which the shared libraries is available.
|
||||||
|
*/
|
||||||
|
task generateGodotMetaEditor {
|
||||||
|
gradle.startParameter.excludedTaskNames += templateExcludedBuildTask()
|
||||||
|
dependsOn = generateBuildTasks("editor", "meta")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -5,16 +5,6 @@ plugins {
|
|||||||
id 'base'
|
id 'base'
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation "androidx.fragment:fragment:$versions.fragmentVersion"
|
|
||||||
implementation project(":lib")
|
|
||||||
|
|
||||||
implementation "androidx.window:window:1.3.0"
|
|
||||||
implementation "androidx.core:core-splashscreen:$versions.splashscreenVersion"
|
|
||||||
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
|
|
||||||
implementation "org.bouncycastle:bcprov-jdk15to18:1.77"
|
|
||||||
}
|
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
// Retrieve the build number from the environment variable; default to 0 if none is specified.
|
// Retrieve the build number from the environment variable; default to 0 if none is specified.
|
||||||
// The build number is added as a suffix to the version code for upload to the Google Play store.
|
// The build number is added as a suffix to the version code for upload to the Google Play store.
|
||||||
@ -154,4 +144,37 @@ android {
|
|||||||
doNotStrip '**/*.so'
|
doNotStrip '**/*.so'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flavorDimensions = ["vendor"]
|
||||||
|
productFlavors {
|
||||||
|
google {
|
||||||
|
dimension "vendor"
|
||||||
|
missingDimensionStrategy 'products', 'editor'
|
||||||
|
}
|
||||||
|
meta {
|
||||||
|
dimension "vendor"
|
||||||
|
missingDimensionStrategy 'products', 'editor'
|
||||||
|
ndk {
|
||||||
|
//noinspection ChromeOsAbiSupport
|
||||||
|
abiFilters "arm64-v8a"
|
||||||
|
}
|
||||||
|
applicationIdSuffix ".meta"
|
||||||
|
versionNameSuffix "-meta"
|
||||||
|
minSdkVersion 23
|
||||||
|
targetSdkVersion 32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation "androidx.fragment:fragment:$versions.fragmentVersion"
|
||||||
|
implementation project(":lib")
|
||||||
|
|
||||||
|
implementation "androidx.window:window:1.3.0"
|
||||||
|
implementation "androidx.core:core-splashscreen:$versions.splashscreenVersion"
|
||||||
|
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
|
||||||
|
implementation "org.bouncycastle:bcprov-jdk15to18:1.77"
|
||||||
|
|
||||||
|
// Meta dependencies
|
||||||
|
metaImplementation "org.godotengine:godot-openxr-vendors-meta:3.0.0-stable"
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
/**************************************************************************/
|
||||||
|
/* GodotEditor.kt */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||||
|
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* */
|
||||||
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
/* a copy of this software and associated documentation files (the */
|
||||||
|
/* "Software"), to deal in the Software without restriction, including */
|
||||||
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
/* the following conditions: */
|
||||||
|
/* */
|
||||||
|
/* The above copyright notice and this permission notice shall be */
|
||||||
|
/* included in all copies or substantial portions of the Software. */
|
||||||
|
/* */
|
||||||
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||||
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||||
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||||
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||||
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||||
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||||
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
/**************************************************************************/
|
||||||
|
|
||||||
|
package org.godotengine.editor
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Primary window of the Godot Editor.
|
||||||
|
*
|
||||||
|
* This is the implementation of the editor used when running on regular Android devices.
|
||||||
|
*/
|
||||||
|
open class GodotEditor : BaseGodotEditor() {
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/**************************************************************************/
|
/**************************************************************************/
|
||||||
/* GodotEditor.kt */
|
/* BaseGodotEditor.kt */
|
||||||
/**************************************************************************/
|
/**************************************************************************/
|
||||||
/* This file is part of: */
|
/* This file is part of: */
|
||||||
/* GODOT ENGINE */
|
/* GODOT ENGINE */
|
||||||
@ -52,6 +52,8 @@ import org.godotengine.godot.GodotLib
|
|||||||
import org.godotengine.godot.error.Error
|
import org.godotengine.godot.error.Error
|
||||||
import org.godotengine.godot.utils.PermissionsUtil
|
import org.godotengine.godot.utils.PermissionsUtil
|
||||||
import org.godotengine.godot.utils.ProcessPhoenix
|
import org.godotengine.godot.utils.ProcessPhoenix
|
||||||
|
import org.godotengine.godot.utils.isHorizonOSDevice
|
||||||
|
import org.godotengine.godot.utils.isNativeXRDevice
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
@ -61,13 +63,11 @@ import kotlin.math.min
|
|||||||
* This provides the basic templates for the activities making up this application.
|
* This provides the basic templates for the activities making up this application.
|
||||||
* Each derived activity runs in its own process, which enable up to have several instances of
|
* Each derived activity runs in its own process, which enable up to have several instances of
|
||||||
* the Godot engine up and running at the same time.
|
* the Godot engine up and running at the same time.
|
||||||
*
|
|
||||||
* It also plays the role of the primary editor window.
|
|
||||||
*/
|
*/
|
||||||
open class GodotEditor : GodotActivity() {
|
abstract class BaseGodotEditor : GodotActivity() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val TAG = GodotEditor::class.java.simpleName
|
private val TAG = BaseGodotEditor::class.java.simpleName
|
||||||
|
|
||||||
private const val WAIT_FOR_DEBUGGER = false
|
private const val WAIT_FOR_DEBUGGER = false
|
||||||
|
|
||||||
@ -81,12 +81,13 @@ open class GodotEditor : GodotActivity() {
|
|||||||
// Command line arguments
|
// Command line arguments
|
||||||
private const val FULLSCREEN_ARG = "--fullscreen"
|
private const val FULLSCREEN_ARG = "--fullscreen"
|
||||||
private const val FULLSCREEN_ARG_SHORT = "-f"
|
private const val FULLSCREEN_ARG_SHORT = "-f"
|
||||||
private const val EDITOR_ARG = "--editor"
|
internal const val EDITOR_ARG = "--editor"
|
||||||
private const val EDITOR_ARG_SHORT = "-e"
|
internal const val EDITOR_ARG_SHORT = "-e"
|
||||||
private const val EDITOR_PROJECT_MANAGER_ARG = "--project-manager"
|
internal const val EDITOR_PROJECT_MANAGER_ARG = "--project-manager"
|
||||||
private const val EDITOR_PROJECT_MANAGER_ARG_SHORT = "-p"
|
internal const val EDITOR_PROJECT_MANAGER_ARG_SHORT = "-p"
|
||||||
private const val BREAKPOINTS_ARG = "--breakpoints"
|
internal const val BREAKPOINTS_ARG = "--breakpoints"
|
||||||
private const val BREAKPOINTS_ARG_SHORT = "-b"
|
internal const val BREAKPOINTS_ARG_SHORT = "-b"
|
||||||
|
internal const val XR_MODE_ARG = "--xr-mode"
|
||||||
|
|
||||||
// Info for the various classes used by the editor
|
// Info for the various classes used by the editor
|
||||||
internal val EDITOR_MAIN_INFO = EditorWindowInfo(GodotEditor::class.java, 777, "")
|
internal val EDITOR_MAIN_INFO = EditorWindowInfo(GodotEditor::class.java, 777, "")
|
||||||
@ -122,6 +123,20 @@ open class GodotEditor : GodotActivity() {
|
|||||||
|
|
||||||
internal open fun getEditorWindowInfo() = EDITOR_MAIN_INFO
|
internal open fun getEditorWindowInfo() = EDITOR_MAIN_INFO
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set of permissions to be excluded when requesting all permissions at startup.
|
||||||
|
*
|
||||||
|
* The permissions in this set will be requested on demand based on use cases.
|
||||||
|
*/
|
||||||
|
@CallSuper
|
||||||
|
protected open fun getExcludedPermissions(): MutableSet<String> {
|
||||||
|
return mutableSetOf(
|
||||||
|
// The RECORD_AUDIO permission is requested when the "audio/driver/enable_input" project
|
||||||
|
// setting is enabled.
|
||||||
|
Manifest.permission.RECORD_AUDIO
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
installSplashScreen()
|
installSplashScreen()
|
||||||
|
|
||||||
@ -131,8 +146,8 @@ open class GodotEditor : GodotActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We exclude certain permissions from the set we request at startup, as they'll be
|
// We exclude certain permissions from the set we request at startup, as they'll be
|
||||||
// requested on demand based on use-cases.
|
// requested on demand based on use cases.
|
||||||
PermissionsUtil.requestManifestPermissions(this, setOf(Manifest.permission.RECORD_AUDIO))
|
PermissionsUtil.requestManifestPermissions(this, getExcludedPermissions())
|
||||||
|
|
||||||
val params = intent.getStringArrayExtra(EXTRA_COMMAND_LINE_PARAMS)
|
val params = intent.getStringArrayExtra(EXTRA_COMMAND_LINE_PARAMS)
|
||||||
Log.d(TAG, "Starting intent $intent with parameters ${params.contentToString()}")
|
Log.d(TAG, "Starting intent $intent with parameters ${params.contentToString()}")
|
||||||
@ -152,8 +167,6 @@ open class GodotEditor : GodotActivity() {
|
|||||||
val longPressEnabled = enableLongPressGestures()
|
val longPressEnabled = enableLongPressGestures()
|
||||||
val panScaleEnabled = enablePanAndScaleGestures()
|
val panScaleEnabled = enablePanAndScaleGestures()
|
||||||
|
|
||||||
checkForProjectPermissionsToEnable()
|
|
||||||
|
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
// Enable long press, panning and scaling gestures
|
// Enable long press, panning and scaling gestures
|
||||||
godotFragment?.godot?.renderView?.inputHandler?.apply {
|
godotFragment?.godot?.renderView?.inputHandler?.apply {
|
||||||
@ -171,17 +184,6 @@ open class GodotEditor : GodotActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check for project permissions to enable
|
|
||||||
*/
|
|
||||||
protected open fun checkForProjectPermissionsToEnable() {
|
|
||||||
// Check for RECORD_AUDIO permission
|
|
||||||
val audioInputEnabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("audio/driver/enable_input"))
|
|
||||||
if (audioInputEnabled) {
|
|
||||||
PermissionsUtil.requestPermission(Manifest.permission.RECORD_AUDIO, this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@CallSuper
|
@CallSuper
|
||||||
protected open fun updateCommandLineParams(args: List<String>) {
|
protected open fun updateCommandLineParams(args: List<String>) {
|
||||||
// Update the list of command line params with the new args
|
// Update the list of command line params with the new args
|
||||||
@ -196,7 +198,7 @@ open class GodotEditor : GodotActivity() {
|
|||||||
|
|
||||||
final override fun getCommandLine() = commandLineParams
|
final override fun getCommandLine() = commandLineParams
|
||||||
|
|
||||||
protected open fun getEditorWindowInfo(args: Array<String>): EditorWindowInfo {
|
protected open fun retrieveEditorWindowInfo(args: Array<String>): EditorWindowInfo {
|
||||||
var hasEditor = false
|
var hasEditor = false
|
||||||
|
|
||||||
var i = 0
|
var i = 0
|
||||||
@ -273,7 +275,7 @@ open class GodotEditor : GodotActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onNewGodotInstanceRequested(args: Array<String>): Int {
|
override fun onNewGodotInstanceRequested(args: Array<String>): Int {
|
||||||
val editorWindowInfo = getEditorWindowInfo(args)
|
val editorWindowInfo = retrieveEditorWindowInfo(args)
|
||||||
|
|
||||||
// Launch a new activity
|
// Launch a new activity
|
||||||
val sourceView = godotFragment?.view
|
val sourceView = godotFragment?.view
|
||||||
@ -405,20 +407,26 @@ open class GodotEditor : GodotActivity() {
|
|||||||
|
|
||||||
return when (policy) {
|
return when (policy) {
|
||||||
LaunchPolicy.AUTO -> {
|
LaunchPolicy.AUTO -> {
|
||||||
try {
|
if (isHorizonOSDevice()) {
|
||||||
when (Integer.parseInt(GodotLib.getEditorSetting("run/window_placement/android_window"))) {
|
// Horizon OS UX is more desktop-like and has support for launching adjacent
|
||||||
ANDROID_WINDOW_SAME_AS_EDITOR -> LaunchPolicy.SAME
|
// windows. So we always want to launch in adjacent mode when auto is selected.
|
||||||
ANDROID_WINDOW_SIDE_BY_SIDE_WITH_EDITOR -> LaunchPolicy.ADJACENT
|
LaunchPolicy.ADJACENT
|
||||||
ANDROID_WINDOW_SAME_AS_EDITOR_AND_LAUNCH_IN_PIP_MODE -> LaunchPolicy.SAME_AND_LAUNCH_IN_PIP_MODE
|
} else {
|
||||||
else -> {
|
try {
|
||||||
// ANDROID_WINDOW_AUTO
|
when (Integer.parseInt(GodotLib.getEditorSetting("run/window_placement/android_window"))) {
|
||||||
defaultLaunchPolicy
|
ANDROID_WINDOW_SAME_AS_EDITOR -> LaunchPolicy.SAME
|
||||||
|
ANDROID_WINDOW_SIDE_BY_SIDE_WITH_EDITOR -> LaunchPolicy.ADJACENT
|
||||||
|
ANDROID_WINDOW_SAME_AS_EDITOR_AND_LAUNCH_IN_PIP_MODE -> LaunchPolicy.SAME_AND_LAUNCH_IN_PIP_MODE
|
||||||
|
else -> {
|
||||||
|
// ANDROID_WINDOW_AUTO
|
||||||
|
defaultLaunchPolicy
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch (e: NumberFormatException) {
|
||||||
|
Log.w(TAG, "Error parsing the Android window placement editor setting", e)
|
||||||
|
// Fall-back to the default launch policy
|
||||||
|
defaultLaunchPolicy
|
||||||
}
|
}
|
||||||
} catch (e: NumberFormatException) {
|
|
||||||
Log.w(TAG, "Error parsing the Android window placement editor setting", e)
|
|
||||||
// Fall-back to the default launch policy
|
|
||||||
defaultLaunchPolicy
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -431,8 +439,16 @@ open class GodotEditor : GodotActivity() {
|
|||||||
/**
|
/**
|
||||||
* Returns true the if the device supports picture-in-picture (PiP)
|
* Returns true the if the device supports picture-in-picture (PiP)
|
||||||
*/
|
*/
|
||||||
protected open fun hasPiPSystemFeature() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N &&
|
protected open fun hasPiPSystemFeature(): Boolean {
|
||||||
packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
|
if (isNativeXRDevice()) {
|
||||||
|
// Known native XR devices do not support PiP.
|
||||||
|
// Will need to revisit as they update their OS.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N &&
|
||||||
|
packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
super.onActivityResult(requestCode, resultCode, data)
|
@ -42,9 +42,9 @@ import android.util.Log
|
|||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used by the [GodotEditor] classes to dispatch messages across processes.
|
* Used by the [BaseGodotEditor] classes to dispatch messages across processes.
|
||||||
*/
|
*/
|
||||||
internal class EditorMessageDispatcher(private val editor: GodotEditor) {
|
internal class EditorMessageDispatcher(private val editor: BaseGodotEditor) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val TAG = EditorMessageDispatcher::class.java.simpleName
|
private val TAG = EditorMessageDispatcher::class.java.simpleName
|
||||||
@ -173,7 +173,11 @@ internal class EditorMessageDispatcher(private val editor: GodotEditor) {
|
|||||||
// to the sender.
|
// to the sender.
|
||||||
val senderId = messengerBundle.getInt(KEY_EDITOR_ID)
|
val senderId = messengerBundle.getInt(KEY_EDITOR_ID)
|
||||||
val senderMessenger: Messenger? = messengerBundle.getParcelable(KEY_EDITOR_MESSENGER)
|
val senderMessenger: Messenger? = messengerBundle.getParcelable(KEY_EDITOR_MESSENGER)
|
||||||
registerMessenger(senderId, senderMessenger)
|
registerMessenger(senderId, senderMessenger) {
|
||||||
|
// Terminate current instance when parent is no longer available.
|
||||||
|
Log.d(TAG, "Terminating current editor instance because parent is no longer available")
|
||||||
|
editor.finish()
|
||||||
|
}
|
||||||
|
|
||||||
// Register ourselves to the sender so that it can communicate with us.
|
// Register ourselves to the sender so that it can communicate with us.
|
||||||
registerSelfTo(pm, senderMessenger, editor.getEditorWindowInfo().windowId)
|
registerSelfTo(pm, senderMessenger, editor.getEditorWindowInfo().windowId)
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
|
|
||||||
package org.godotengine.editor
|
package org.godotengine.editor
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.PictureInPictureParams
|
import android.app.PictureInPictureParams
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@ -38,12 +39,15 @@ import android.os.Build
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.annotation.CallSuper
|
||||||
import org.godotengine.godot.GodotLib
|
import org.godotengine.godot.GodotLib
|
||||||
|
import org.godotengine.godot.utils.PermissionsUtil
|
||||||
|
import org.godotengine.godot.utils.ProcessPhoenix
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Drives the 'run project' window of the Godot Editor.
|
* Drives the 'run project' window of the Godot Editor.
|
||||||
*/
|
*/
|
||||||
class GodotGame : GodotEditor() {
|
open class GodotGame : GodotEditor() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val TAG = GodotGame::class.java.simpleName
|
private val TAG = GodotGame::class.java.simpleName
|
||||||
@ -136,8 +140,53 @@ class GodotGame : GodotEditor() {
|
|||||||
|
|
||||||
override fun enablePanAndScaleGestures() = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_pan_and_scale_gestures"))
|
override fun enablePanAndScaleGestures() = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_pan_and_scale_gestures"))
|
||||||
|
|
||||||
override fun checkForProjectPermissionsToEnable() {
|
override fun onGodotSetupCompleted() {
|
||||||
// Nothing to do.. by the time we get here, the project permissions will have already
|
super.onGodotSetupCompleted()
|
||||||
// been requested by the Editor window.
|
Log.v(TAG, "OnGodotSetupCompleted")
|
||||||
|
|
||||||
|
// Check if we should be running in XR instead (if available) as it's possible we were
|
||||||
|
// launched from the project manager which doesn't have that information.
|
||||||
|
val launchingArgs = intent.getStringArrayExtra(EXTRA_COMMAND_LINE_PARAMS)
|
||||||
|
if (launchingArgs != null) {
|
||||||
|
val editorWindowInfo = retrieveEditorWindowInfo(launchingArgs)
|
||||||
|
if (editorWindowInfo != getEditorWindowInfo()) {
|
||||||
|
val relaunchIntent = getNewGodotInstanceIntent(editorWindowInfo, launchingArgs)
|
||||||
|
relaunchIntent.putExtra(EXTRA_NEW_LAUNCH, true)
|
||||||
|
.putExtra(EditorMessageDispatcher.EXTRA_MSG_DISPATCHER_PAYLOAD, intent.getBundleExtra(EditorMessageDispatcher.EXTRA_MSG_DISPATCHER_PAYLOAD))
|
||||||
|
|
||||||
|
Log.d(TAG, "Relaunching XR project using ${editorWindowInfo.windowClassName} with parameters ${launchingArgs.contentToString()}")
|
||||||
|
val godot = godot
|
||||||
|
if (godot != null) {
|
||||||
|
godot.destroyAndKillProcess {
|
||||||
|
ProcessPhoenix.triggerRebirth(this, relaunchIntent)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ProcessPhoenix.triggerRebirth(this, relaunchIntent)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request project runtime permissions if necessary
|
||||||
|
val permissionsToEnable = getProjectPermissionsToEnable()
|
||||||
|
if (permissionsToEnable.isNotEmpty()) {
|
||||||
|
PermissionsUtil.requestPermissions(this, permissionsToEnable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for project permissions to enable
|
||||||
|
*/
|
||||||
|
@CallSuper
|
||||||
|
protected open fun getProjectPermissionsToEnable(): MutableList<String> {
|
||||||
|
val permissionsToEnable = mutableListOf<String>()
|
||||||
|
|
||||||
|
// Check for RECORD_AUDIO permission
|
||||||
|
val audioInputEnabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("audio/driver/enable_input"))
|
||||||
|
if (audioInputEnabled) {
|
||||||
|
permissionsToEnable.add(Manifest.permission.RECORD_AUDIO)
|
||||||
|
}
|
||||||
|
|
||||||
|
return permissionsToEnable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
99
platform/android/java/editor/src/meta/AndroidManifest.xml
Normal file
99
platform/android/java/editor/src/meta/AndroidManifest.xml
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
xmlns:horizonos="http://schemas.horizonos/sdk">
|
||||||
|
|
||||||
|
<horizonos:uses-horizonos-sdk
|
||||||
|
horizonos:minSdkVersion="69"
|
||||||
|
horizonos:targetSdkVersion="69" />
|
||||||
|
|
||||||
|
<uses-feature
|
||||||
|
android:name="android.hardware.vr.headtracking"
|
||||||
|
android:required="true"
|
||||||
|
android:version="1"/>
|
||||||
|
|
||||||
|
<!-- Oculus Quest hand tracking -->
|
||||||
|
<uses-permission android:name="com.oculus.permission.HAND_TRACKING" />
|
||||||
|
<uses-feature
|
||||||
|
android:name="oculus.software.handtracking"
|
||||||
|
android:required="false" />
|
||||||
|
|
||||||
|
<!-- Passthrough feature flag -->
|
||||||
|
<uses-feature android:name="com.oculus.feature.PASSTHROUGH"
|
||||||
|
android:required="false" />
|
||||||
|
|
||||||
|
<!-- Overlay keyboard support -->
|
||||||
|
<uses-feature android:name="oculus.software.overlay_keyboard" android:required="false"/>
|
||||||
|
|
||||||
|
<!-- Render model -->
|
||||||
|
<uses-permission android:name="com.oculus.permission.RENDER_MODEL" />
|
||||||
|
<uses-feature android:name="com.oculus.feature.RENDER_MODEL" android:required="false" />
|
||||||
|
|
||||||
|
<!-- Anchor api -->
|
||||||
|
<uses-permission android:name="com.oculus.permission.USE_ANCHOR_API" />
|
||||||
|
|
||||||
|
<!-- Scene api -->
|
||||||
|
<uses-permission android:name="com.oculus.permission.USE_SCENE" />
|
||||||
|
|
||||||
|
<application>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".GodotEditor"
|
||||||
|
android:exported="true"
|
||||||
|
android:screenOrientation="landscape"
|
||||||
|
tools:node="merge"
|
||||||
|
tools:replace="android:screenOrientation">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
<category android:name="com.oculus.intent.category.2D" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<meta-data android:name="com.oculus.vrshell.free_resizing_lock_aspect_ratio" android:value="true"/>
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".GodotXRGame"
|
||||||
|
android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
|
||||||
|
android:process=":GodotXRGame"
|
||||||
|
android:launchMode="singleTask"
|
||||||
|
android:icon="@mipmap/ic_play_window"
|
||||||
|
android:label="@string/godot_game_activity_name"
|
||||||
|
android:exported="false"
|
||||||
|
android:screenOrientation="landscape"
|
||||||
|
android:resizeableActivity="false"
|
||||||
|
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="com.oculus.intent.category.VR" />
|
||||||
|
<category android:name="org.khronos.openxr.intent.category.IMMERSIVE_HMD" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<!-- Supported Meta devices -->
|
||||||
|
<meta-data
|
||||||
|
android:name="com.oculus.supportedDevices"
|
||||||
|
android:value="quest3|questpro"
|
||||||
|
tools:replace="android:value" />
|
||||||
|
|
||||||
|
<!--
|
||||||
|
We remove this meta-data originating from the vendors plugin as we only need the loader for
|
||||||
|
now since the project being edited provides its own version of the vendors plugin.
|
||||||
|
|
||||||
|
This needs to be removed once we start implementing the immersive version of the project
|
||||||
|
manager and editor windows.
|
||||||
|
-->
|
||||||
|
<meta-data
|
||||||
|
android:name="org.godotengine.plugin.v2.GodotOpenXRMeta"
|
||||||
|
android:value="org.godotengine.openxr.vendors.meta.GodotOpenXRMeta"
|
||||||
|
tools:node="remove" />
|
||||||
|
|
||||||
|
<!-- Enable system splash screen -->
|
||||||
|
<meta-data android:name="com.oculus.ossplash" android:value="true"/>
|
||||||
|
<!-- Enable passthrough background during the splash screen -->
|
||||||
|
<meta-data android:name="com.oculus.ossplash.background" android:value="passthrough-contextual"/>
|
||||||
|
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
BIN
platform/android/java/editor/src/meta/assets/vr_splash.png
Normal file
BIN
platform/android/java/editor/src/meta/assets/vr_splash.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
@ -0,0 +1,94 @@
|
|||||||
|
/**************************************************************************/
|
||||||
|
/* GodotEditor.kt */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||||
|
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* */
|
||||||
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
/* a copy of this software and associated documentation files (the */
|
||||||
|
/* "Software"), to deal in the Software without restriction, including */
|
||||||
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
/* the following conditions: */
|
||||||
|
/* */
|
||||||
|
/* The above copyright notice and this permission notice shall be */
|
||||||
|
/* included in all copies or substantial portions of the Software. */
|
||||||
|
/* */
|
||||||
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||||
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||||
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||||
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||||
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||||
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||||
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
/**************************************************************************/
|
||||||
|
|
||||||
|
package org.godotengine.editor
|
||||||
|
|
||||||
|
import org.godotengine.godot.GodotLib
|
||||||
|
import org.godotengine.godot.utils.isNativeXRDevice
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Primary window of the Godot Editor.
|
||||||
|
*
|
||||||
|
* This is the implementation of the editor used when running on Meta devices.
|
||||||
|
*/
|
||||||
|
open class GodotEditor : BaseGodotEditor() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val TAG = GodotEditor::class.java.simpleName
|
||||||
|
|
||||||
|
internal val XR_RUN_GAME_INFO = EditorWindowInfo(GodotXRGame::class.java, 1667, ":GodotXRGame")
|
||||||
|
|
||||||
|
internal const val USE_ANCHOR_API_PERMISSION = "com.oculus.permission.USE_ANCHOR_API"
|
||||||
|
internal const val USE_SCENE_PERMISSION = "com.oculus.permission.USE_SCENE"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getExcludedPermissions(): MutableSet<String> {
|
||||||
|
val excludedPermissions = super.getExcludedPermissions()
|
||||||
|
// The USE_ANCHOR_API and USE_SCENE permissions are requested when the "xr/openxr/enabled"
|
||||||
|
// project setting is enabled.
|
||||||
|
excludedPermissions.add(USE_ANCHOR_API_PERMISSION)
|
||||||
|
excludedPermissions.add(USE_SCENE_PERMISSION)
|
||||||
|
return excludedPermissions
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun retrieveEditorWindowInfo(args: Array<String>): EditorWindowInfo {
|
||||||
|
var hasEditor = false
|
||||||
|
var xrModeOn = false
|
||||||
|
|
||||||
|
var i = 0
|
||||||
|
while (i < args.size) {
|
||||||
|
when (args[i++]) {
|
||||||
|
EDITOR_ARG, EDITOR_ARG_SHORT, EDITOR_PROJECT_MANAGER_ARG, EDITOR_PROJECT_MANAGER_ARG_SHORT -> hasEditor = true
|
||||||
|
XR_MODE_ARG -> {
|
||||||
|
val argValue = args[i++]
|
||||||
|
xrModeOn = xrModeOn || ("on" == argValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return if (hasEditor) {
|
||||||
|
EDITOR_MAIN_INFO
|
||||||
|
} else {
|
||||||
|
val openxrEnabled = GodotLib.getGlobal("xr/openxr/enabled").toBoolean()
|
||||||
|
if (openxrEnabled && isNativeXRDevice()) {
|
||||||
|
XR_RUN_GAME_INFO
|
||||||
|
} else {
|
||||||
|
RUN_GAME_INFO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getEditorWindowInfoForInstanceId(instanceId: Int): EditorWindowInfo? {
|
||||||
|
return when (instanceId) {
|
||||||
|
XR_RUN_GAME_INFO.windowId -> XR_RUN_GAME_INFO
|
||||||
|
else -> super.getEditorWindowInfoForInstanceId(instanceId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
/*************************************************************************/
|
||||||
|
/* GodotXRGame.kt */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||||
|
/* */
|
||||||
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
/* a copy of this software and associated documentation files (the */
|
||||||
|
/* "Software"), to deal in the Software without restriction, including */
|
||||||
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
/* the following conditions: */
|
||||||
|
/* */
|
||||||
|
/* The above copyright notice and this permission notice shall be */
|
||||||
|
/* included in all copies or substantial portions of the Software. */
|
||||||
|
/* */
|
||||||
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||||
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||||
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||||
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||||
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||||
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||||
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
/*************************************************************************/
|
||||||
|
|
||||||
|
package org.godotengine.editor
|
||||||
|
|
||||||
|
import org.godotengine.godot.GodotLib
|
||||||
|
import org.godotengine.godot.utils.PermissionsUtil
|
||||||
|
import org.godotengine.godot.xr.XRMode
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide support for running XR apps / games from the editor window.
|
||||||
|
*/
|
||||||
|
open class GodotXRGame: GodotGame() {
|
||||||
|
|
||||||
|
override fun overrideOrientationRequest() = true
|
||||||
|
|
||||||
|
override fun updateCommandLineParams(args: List<String>) {
|
||||||
|
val updatedArgs = ArrayList<String>()
|
||||||
|
if (!args.contains(XRMode.OPENXR.cmdLineArg)) {
|
||||||
|
updatedArgs.add(XRMode.OPENXR.cmdLineArg)
|
||||||
|
}
|
||||||
|
if (!args.contains(XR_MODE_ARG)) {
|
||||||
|
updatedArgs.add(XR_MODE_ARG)
|
||||||
|
updatedArgs.add("on")
|
||||||
|
}
|
||||||
|
updatedArgs.addAll(args)
|
||||||
|
|
||||||
|
super.updateCommandLineParams(updatedArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getEditorWindowInfo() = XR_RUN_GAME_INFO
|
||||||
|
|
||||||
|
override fun getProjectPermissionsToEnable(): MutableList<String> {
|
||||||
|
val permissionsToEnable = super.getProjectPermissionsToEnable()
|
||||||
|
|
||||||
|
val openxrEnabled = GodotLib.getGlobal("xr/openxr/enabled").toBoolean()
|
||||||
|
if (openxrEnabled) {
|
||||||
|
permissionsToEnable.add(USE_ANCHOR_API_PERMISSION)
|
||||||
|
permissionsToEnable.add(USE_SCENE_PERMISSION)
|
||||||
|
}
|
||||||
|
|
||||||
|
return permissionsToEnable
|
||||||
|
}
|
||||||
|
}
|
@ -51,7 +51,7 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flavorDimensions "products"
|
flavorDimensions = ["products"]
|
||||||
productFlavors {
|
productFlavors {
|
||||||
editor {}
|
editor {}
|
||||||
template {}
|
template {}
|
||||||
@ -104,7 +104,7 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
boolean devBuild = buildType == "dev"
|
boolean devBuild = buildType == "dev"
|
||||||
boolean debugSymbols = devBuild || isAndroidStudio()
|
boolean debugSymbols = devBuild
|
||||||
boolean runTests = devBuild
|
boolean runTests = devBuild
|
||||||
boolean productionBuild = !devBuild
|
boolean productionBuild = !devBuild
|
||||||
boolean storeRelease = buildType == "release"
|
boolean storeRelease = buildType == "release"
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
package org.godotengine.godot;
|
package org.godotengine.godot;
|
||||||
|
|
||||||
import org.godotengine.godot.input.GodotInputHandler;
|
import org.godotengine.godot.input.GodotInputHandler;
|
||||||
|
import org.godotengine.godot.utils.DeviceUtils;
|
||||||
|
|
||||||
import android.view.SurfaceView;
|
import android.view.SurfaceView;
|
||||||
|
|
||||||
@ -63,7 +64,11 @@ public interface GodotRenderView {
|
|||||||
|
|
||||||
void setPointerIcon(int pointerType);
|
void setPointerIcon(int pointerType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if pointer capture is supported.
|
||||||
|
*/
|
||||||
default boolean canCapturePointer() {
|
default boolean canCapturePointer() {
|
||||||
return getInputHandler().canCapturePointer();
|
// Pointer capture is not supported on Horizon OS
|
||||||
|
return !DeviceUtils.isHorizonOSDevice() && getInputHandler().canCapturePointer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,6 +68,7 @@ class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView {
|
|||||||
setPointerIcon(PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT));
|
setPointerIcon(PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT));
|
||||||
}
|
}
|
||||||
setFocusableInTouchMode(true);
|
setFocusableInTouchMode(true);
|
||||||
|
setClickable(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -132,17 +133,17 @@ class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onKeyUp(final int keyCode, KeyEvent event) {
|
public boolean onKeyUp(final int keyCode, KeyEvent event) {
|
||||||
return mInputHandler.onKeyUp(keyCode, event);
|
return mInputHandler.onKeyUp(keyCode, event) || super.onKeyUp(keyCode, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onKeyDown(final int keyCode, KeyEvent event) {
|
public boolean onKeyDown(final int keyCode, KeyEvent event) {
|
||||||
return mInputHandler.onKeyDown(keyCode, event);
|
return mInputHandler.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onGenericMotionEvent(MotionEvent event) {
|
public boolean onGenericMotionEvent(MotionEvent event) {
|
||||||
return mInputHandler.onGenericMotionEvent(event);
|
return mInputHandler.onGenericMotionEvent(event) || super.onGenericMotionEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
/**************************************************************************/
|
||||||
|
/* DeviceUtils.kt */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||||
|
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* */
|
||||||
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
/* a copy of this software and associated documentation files (the */
|
||||||
|
/* "Software"), to deal in the Software without restriction, including */
|
||||||
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
/* the following conditions: */
|
||||||
|
/* */
|
||||||
|
/* The above copyright notice and this permission notice shall be */
|
||||||
|
/* included in all copies or substantial portions of the Software. */
|
||||||
|
/* */
|
||||||
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||||
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||||
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||||
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||||
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||||
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||||
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
/**************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains utility methods for detecting specific devices.
|
||||||
|
*/
|
||||||
|
@file:JvmName("DeviceUtils")
|
||||||
|
|
||||||
|
package org.godotengine.godot.utils
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if running on Meta's Horizon OS.
|
||||||
|
*/
|
||||||
|
fun isHorizonOSDevice(): Boolean {
|
||||||
|
return "Oculus".equals(Build.BRAND, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if running on a native Android XR device.
|
||||||
|
*/
|
||||||
|
fun isNativeXRDevice(): Boolean {
|
||||||
|
return isHorizonOSDevice()
|
||||||
|
}
|
@ -8,6 +8,7 @@ set(CMAKE_CXX_EXTENSIONS OFF)
|
|||||||
|
|
||||||
set(GODOT_ROOT_DIR ../../../..)
|
set(GODOT_ROOT_DIR ../../../..)
|
||||||
set(ANDROID_ROOT_DIR "${GODOT_ROOT_DIR}/platform/android" CACHE STRING "")
|
set(ANDROID_ROOT_DIR "${GODOT_ROOT_DIR}/platform/android" CACHE STRING "")
|
||||||
|
set(OPENXR_INCLUDE_DIR "${GODOT_ROOT_DIR}/thirdparty/openxr/include" CACHE STRING "")
|
||||||
|
|
||||||
# Get sources
|
# Get sources
|
||||||
file(GLOB_RECURSE SOURCES ${GODOT_ROOT_DIR}/*.c**)
|
file(GLOB_RECURSE SOURCES ${GODOT_ROOT_DIR}/*.c**)
|
||||||
@ -17,6 +18,7 @@ add_executable(${PROJECT_NAME} ${SOURCES} ${HEADERS})
|
|||||||
target_include_directories(${PROJECT_NAME}
|
target_include_directories(${PROJECT_NAME}
|
||||||
SYSTEM PUBLIC
|
SYSTEM PUBLIC
|
||||||
${GODOT_ROOT_DIR}
|
${GODOT_ROOT_DIR}
|
||||||
${ANDROID_ROOT_DIR})
|
${ANDROID_ROOT_DIR}
|
||||||
|
${OPENXR_INCLUDE_DIR})
|
||||||
|
|
||||||
add_definitions(-DUNIX_ENABLED -DVULKAN_ENABLED -DANDROID_ENABLED -DGLES3_ENABLED -DTOOLS_ENABLED)
|
add_definitions(-DUNIX_ENABLED -DVULKAN_ENABLED -DANDROID_ENABLED -DGLES3_ENABLED -DTOOLS_ENABLED)
|
||||||
|
@ -14,6 +14,7 @@ pluginManagement {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
maven { url "https://plugins.gradle.org/m2/" }
|
maven { url "https://plugins.gradle.org/m2/" }
|
||||||
|
maven { url "https://s01.oss.sonatype.org/content/repositories/snapshots/"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,6 @@
|
|||||||
#include "string_android.h"
|
#include "string_android.h"
|
||||||
|
|
||||||
#include "core/config/engine.h"
|
#include "core/config/engine.h"
|
||||||
#include "core/config/project_settings.h"
|
|
||||||
#include "core/error/error_macros.h"
|
#include "core/error/error_macros.h"
|
||||||
|
|
||||||
static HashMap<String, JNISingleton *> jni_singletons;
|
static HashMap<String, JNISingleton *> jni_singletons;
|
||||||
@ -43,7 +42,6 @@ static HashMap<String, JNISingleton *> jni_singletons;
|
|||||||
void unregister_plugins_singletons() {
|
void unregister_plugins_singletons() {
|
||||||
for (const KeyValue<String, JNISingleton *> &E : jni_singletons) {
|
for (const KeyValue<String, JNISingleton *> &E : jni_singletons) {
|
||||||
Engine::get_singleton()->remove_singleton(E.key);
|
Engine::get_singleton()->remove_singleton(E.key);
|
||||||
ProjectSettings::get_singleton()->set(E.key, Variant());
|
|
||||||
|
|
||||||
if (E.value) {
|
if (E.value) {
|
||||||
memdelete(E.value);
|
memdelete(E.value);
|
||||||
@ -64,7 +62,6 @@ JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeR
|
|||||||
jni_singletons[singname] = s;
|
jni_singletons[singname] = s;
|
||||||
|
|
||||||
Engine::get_singleton()->add_singleton(Engine::Singleton(singname, s));
|
Engine::get_singleton()->add_singleton(Engine::Singleton(singname, s));
|
||||||
ProjectSettings::get_singleton()->set(singname, s);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user