diff options
Diffstat (limited to 'platform/android')
23 files changed, 1381 insertions, 698 deletions
diff --git a/platform/android/SCsub b/platform/android/SCsub index 22ed476c6f..cf6752fa54 100644 --- a/platform/android/SCsub +++ b/platform/android/SCsub @@ -23,8 +23,6 @@ android_files = [ ] env_android = env.Clone() -if env['target'] == "profile": - env_android.Append(CPPFLAGS=['-DPROFILER_ENABLED']) android_objects = [] for x in android_files: @@ -37,9 +35,7 @@ android_objects.append(env_thirdparty.SharedObject('#thirdparty/misc/ifaddrs-and lib = env_android.add_shared_library("#bin/libgodot", [android_objects], SHLIBSUFFIX=env["SHLIBSUFFIX"]) lib_arch_dir = '' -if env['android_arch'] == 'armv6': - lib_arch_dir = 'armeabi' -elif env['android_arch'] == 'armv7': +if env['android_arch'] == 'armv7': lib_arch_dir = 'armeabi-v7a' elif env['android_arch'] == 'arm64v8': lib_arch_dir = 'arm64-v8a' diff --git a/platform/android/detect.py b/platform/android/detect.py index b7641172e4..3f179e3a65 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -26,7 +26,7 @@ def get_opts(): return [ ('ANDROID_NDK_ROOT', 'Path to the Android NDK', os.environ.get("ANDROID_NDK_ROOT", 0)), ('ndk_platform', 'Target platform (android-<api>, e.g. "android-18")', "android-18"), - EnumVariable('android_arch', 'Target architecture', "armv7", ('armv7', 'armv6', 'arm64v8', 'x86', 'x86_64')), + EnumVariable('android_arch', 'Target architecture', "armv7", ('armv7', 'arm64v8', 'x86', 'x86_64')), BoolVariable('android_neon', 'Enable NEON support (armv7 only)', True), BoolVariable('android_stl', 'Enable Android STL support (for modules)', True) ] @@ -93,7 +93,7 @@ def configure(env): ## Architecture - if env['android_arch'] not in ['armv7', 'armv6', 'arm64v8', 'x86', 'x86_64']: + if env['android_arch'] not in ['armv7', 'arm64v8', 'x86', 'x86_64']: env['android_arch'] = 'armv7' neon_text = "" @@ -119,13 +119,6 @@ def configure(env): abi_subpath = "x86_64-linux-android" arch_subpath = "x86_64" env["x86_libtheora_opt_gcc"] = True - elif env['android_arch'] == 'armv6': - env['ARCH'] = 'arch-arm' - env.extra_suffix = ".armv6" + env.extra_suffix - target_subpath = "arm-linux-androideabi-4.9" - abi_subpath = "arm-linux-androideabi" - arch_subpath = "armeabi" - can_vectorize = False elif env["android_arch"] == "armv7": env['ARCH'] = 'arch-arm' target_subpath = "arm-linux-androideabi-4.9" @@ -151,20 +144,21 @@ def configure(env): if (env["optimize"] == "speed"): #optimize for speed (default) env.Append(LINKFLAGS=['-O2']) env.Append(CCFLAGS=['-O2', '-fomit-frame-pointer']) - env.Append(CPPFLAGS=['-DNDEBUG']) + env.Append(CPPDEFINES=['NDEBUG']) else: #optimize for size env.Append(CCFLAGS=['-Os']) - env.Append(CPPFLAGS=['-DNDEBUG']) + env.Append(CPPDEFINES=['NDEBUG']) env.Append(LINKFLAGS=['-Os']) if (can_vectorize): env.Append(CCFLAGS=['-ftree-vectorize']) if (env["target"] == "release_debug"): - env.Append(CPPFLAGS=['-DDEBUG_ENABLED']) + env.Append(CPPDEFINES=['DEBUG_ENABLED']) elif (env["target"] == "debug"): env.Append(LINKFLAGS=['-O0']) env.Append(CCFLAGS=['-O0', '-g', '-fno-limit-debug-info']) - env.Append(CPPFLAGS=['-D_DEBUG', '-UNDEBUG', '-DDEBUG_ENABLED', '-DDEBUG_MEMORY_ENABLED']) + env.Append(CPPDEFINES=['_DEBUG', 'DEBUG_ENABLED', 'DEBUG_MEMORY_ENABLED']) + env.Append(CPPFLAGS=['-UNDEBUG']) ## Compiler configuration @@ -222,7 +216,7 @@ def configure(env): else: env.Append(CXXFLAGS=['-fno-rtti', '-fno-exceptions']) # Don't use dynamic_cast, necessary with no-rtti. - env.Append(CPPFLAGS=['-DNO_SAFE_CAST']) + env.Append(CPPDEFINES=['NO_SAFE_CAST']) ndk_version = get_ndk_version(env["ANDROID_NDK_ROOT"]) if ndk_version != None and LooseVersion(ndk_version) >= LooseVersion("15.0.4075724"): @@ -232,13 +226,13 @@ def configure(env): env.Append(CPPFLAGS=["-isystem", sysroot + "/usr/include/" + abi_subpath]) env.Append(CPPFLAGS=["-isystem", env["ANDROID_NDK_ROOT"] + "/sources/android/support/include"]) # For unified headers this define has to be set manually - env.Append(CPPFLAGS=["-D__ANDROID_API__=" + str(get_platform(env['ndk_platform']))]) + env.Append(CPPDEFINES=[('__ANDROID_API__', str(get_platform(env['ndk_platform'])))]) else: print("Using NDK deprecated headers") env.Append(CPPFLAGS=["-isystem", lib_sysroot + "/usr/include"]) env.Append(CCFLAGS='-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -fvisibility=hidden -fno-strict-aliasing'.split()) - env.Append(CPPFLAGS='-DNO_STATVFS -DGLES_ENABLED'.split()) + env.Append(CPPDEFINES=['NO_STATVFS', 'GLES_ENABLED']) env['neon_enabled'] = False if env['android_arch'] == 'x86': @@ -249,26 +243,21 @@ def configure(env): elif env['android_arch'] == 'x86_64': target_opts = ['-target', 'x86_64-none-linux-android'] - elif env["android_arch"] == "armv6": - target_opts = ['-target', 'armv6-none-linux-androideabi'] - env.Append(CCFLAGS='-march=armv6 -mfpu=vfp -mfloat-abi=softfp'.split()) - env.Append(CPPFLAGS=['-D__ARM_ARCH_6__']) - elif env["android_arch"] == "armv7": target_opts = ['-target', 'armv7-none-linux-androideabi'] env.Append(CCFLAGS='-march=armv7-a -mfloat-abi=softfp'.split()) - env.Append(CPPFLAGS='-D__ARM_ARCH_7__ -D__ARM_ARCH_7A__'.split()) + env.Append(CPPDEFINES=['__ARM_ARCH_7__', '__ARM_ARCH_7A__']) if env['android_neon']: env['neon_enabled'] = True env.Append(CCFLAGS=['-mfpu=neon']) - env.Append(CPPFLAGS=['-D__ARM_NEON__']) + env.Append(CPPDEFINES=['__ARM_NEON__']) else: env.Append(CCFLAGS=['-mfpu=vfpv3-d16']) elif env["android_arch"] == "arm64v8": target_opts = ['-target', 'aarch64-none-linux-android'] env.Append(CCFLAGS=['-mfix-cortex-a53-835769']) - env.Append(CPPFLAGS=['-D__ARM_ARCH_8A__']) + env.Append(CPPDEFINES=['__ARM_ARCH_8A__']) env.Append(CCFLAGS=target_opts) env.Append(CCFLAGS=common_opts) @@ -301,7 +290,7 @@ def configure(env): '/toolchains/' + target_subpath + '/prebuilt/' + host_subpath + '/' + abi_subpath + '/lib']) env.Prepend(CPPPATH=['#platform/android']) - env.Append(CPPFLAGS=['-DANDROID_ENABLED', '-DUNIX_ENABLED', '-DNO_FCNTL']) + env.Append(CPPDEFINES=['ANDROID_ENABLED', 'UNIX_ENABLED', 'NO_FCNTL']) env.Append(LIBS=['OpenSLES', 'EGL', 'GLESv3', 'GLESv2', 'android', 'log', 'z', 'dl']) # Return NDK version string in source.properties (adapted from the Chromium project). diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index b987b3aebd..1b9d31d752 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -208,7 +208,7 @@ static const LauncherIcon launcher_icons[] = { class EditorExportPlatformAndroid : public EditorExportPlatform { - GDCLASS(EditorExportPlatformAndroid, EditorExportPlatform) + GDCLASS(EditorExportPlatformAndroid, EditorExportPlatform); Ref<ImageTexture> logo; Ref<ImageTexture> run_icon; @@ -553,9 +553,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { static Vector<String> get_abis() { Vector<String> abis; - // We can still build armv6 in theory, but it doesn't make much - // sense for games, so disabling for now. - //abis.push_back("armeabi"); abis.push_back("armeabi-v7a"); abis.push_back("arm64-v8a"); abis.push_back("x86"); @@ -597,7 +594,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { if (abi_index != -1) { exported = true; String abi = abis[abi_index]; - String dst_path = "lib/" + abi + "/" + p_so.path.get_file(); + String dst_path = String("lib").plus_file(abi).plus_file(p_so.path.get_file()); Vector<uint8_t> array = FileAccess::get_file_as_array(p_so.path); Error store_err = store_in_apk(ed, dst_path, array); ERR_FAIL_COND_V(store_err, store_err); @@ -668,6 +665,8 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { bool screen_support_large = p_preset->get("screen/support_large"); bool screen_support_xlarge = p_preset->get("screen/support_xlarge"); + int xr_mode_index = p_preset->get("graphics/xr_mode"); + Vector<String> perms; const char **aperms = android_perms; @@ -790,7 +789,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { if (tname == "manifest" && attrname == "versionName") { if (attr_value == 0xFFFFFFFF) { - WARN_PRINT("Version name in a resource, should be plaintext") + WARN_PRINT("Version name in a resource, should be plain text"); } else string_table.write[attr_value] = version_name; } @@ -825,6 +824,20 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { encode_uint32(min_gles3 ? 0x00030000 : 0x00020000, &p_manifest.write[iofs + 16]); } + if (tname == "meta-data" && attrname == "name" && string_table[attr_value] == "xr_mode_metadata_name") { + // Update the meta-data 'android:name' attribute based on the selected XR mode. + if (xr_mode_index == 1 /* XRMode.OVR */) { + string_table.write[attr_value] = "com.samsung.android.vr.application.mode"; + } + } + + if (tname == "meta-data" && attrname == "value" && string_table[attr_value] == "xr_mode_metadata_value") { + // Update the meta-data 'android:value' attribute based on the selected XR mode. + if (xr_mode_index == 1 /* XRMode.OVR */) { + string_table.write[attr_value] = "vr_only"; + } + } + iofs += 20; } @@ -1142,6 +1155,7 @@ public: virtual void get_export_options(List<ExportOption> *r_options) { + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "graphics/xr_mode", PROPERTY_HINT_ENUM, "Regular,Oculus Mobile VR"), 0)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/32_bits_framebuffer"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "one_click_deploy/clear_previous_install"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_package/debug", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), "")); @@ -2074,6 +2088,14 @@ public: } } + int xr_mode_index = p_preset->get("graphics/xr_mode"); + if (xr_mode_index == 1 /* XRMode.OVR */) { + cl.push_back("--xr_mode_ovr"); + } else { + // XRMode.REGULAR is the default. + cl.push_back("--xr_mode_regular"); + } + if (use_32_fb) cl.push_back("--use_depth_32"); diff --git a/platform/android/file_access_jandroid.cpp b/platform/android/file_access_jandroid.cpp index 63bc5f69d1..5b8cf01138 100644 --- a/platform/android/file_access_jandroid.cpp +++ b/platform/android/file_access_jandroid.cpp @@ -62,7 +62,7 @@ Error FileAccessJAndroid::_open(const String &p_path, int p_mode_flags) { JNIEnv *env = ThreadAndroid::get_env(); jstring js = env->NewStringUTF(path.utf8().get_data()); - int res = env->CallIntMethod(io, _file_open, js, p_mode_flags & WRITE ? true : false); + int res = env->CallIntMethod(io, _file_open, js, (p_mode_flags & WRITE) ? true : false); env->DeleteLocalRef(js); OS::get_singleton()->print("fopen: '%s' ret %i\n", path.utf8().get_data(), res); diff --git a/platform/android/java/AndroidManifest.xml b/platform/android/java/AndroidManifest.xml index 9997950137..3152ef12cd 100644 --- a/platform/android/java/AndroidManifest.xml +++ b/platform/android/java/AndroidManifest.xml @@ -13,7 +13,7 @@ <!--glEsVersion is modified by the exporter, changing this value here has no effect--> <uses-feature android:glEsVersion="0x00020000" android:required="true" /> -<!--Adding custom text to manifest is fine, but do it outside the custom user and application BEGIN/ENDregions, as that gets rewritten--> +<!--Adding custom text to manifest is fine, but do it outside the custom user and application BEGIN/END regions, as that gets rewritten--> <!--Custom permissions XML added by add-ons. It's recommended to add them from the export preset, though--> <!--CHUNK_USER_PERMISSIONS_BEGIN--> @@ -25,12 +25,15 @@ <!--The following values are replaced when Godot exports, modifying them here has no effect. Do these changes in the--> <!--export preset. Adding new ones is fine.--> +<!-- XR mode metadata. This is modified by the exporter based on the selected xr mode. DO NOT CHANGE the values here. --> + <meta-data android:name="xr_mode_metadata_name" android:value="xr_mode_metadata_value"/> + <activity android:name="org.godotengine.godot.Godot" android:label="@string/godot_project_name_string" - android:theme="@android:style/Theme.NoTitleBar.Fullscreen" + android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen" android:launchMode="singleTask" android:screenOrientation="landscape" - android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize" + android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode" android:resizeableActivity="false" tools:ignore="UnusedAttribute"> diff --git a/platform/android/java/src/org/godotengine/godot/Godot.java b/platform/android/java/src/org/godotengine/godot/Godot.java index 0eeaf0701c..6e1841fa8b 100644 --- a/platform/android/java/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/src/org/godotengine/godot/Godot.java @@ -30,8 +30,6 @@ package org.godotengine.godot; -//import android.R; - import android.Manifest; import android.app.Activity; import android.app.ActivityManager; @@ -60,10 +58,10 @@ import android.os.Environment; import android.os.Messenger; import android.provider.Settings.Secure; import android.support.v4.content.ContextCompat; -import android.util.Log; import android.view.Display; import android.view.KeyEvent; import android.view.MotionEvent; +import android.view.Surface; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; @@ -73,7 +71,6 @@ import android.view.WindowManager; import android.widget.Button; import android.widget.FrameLayout; import android.widget.ProgressBar; -import android.widget.RelativeLayout; import android.widget.TextView; import com.google.android.vending.expansion.downloader.DownloadProgressInfo; import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller; @@ -94,6 +91,7 @@ import java.util.Locale; import javax.microedition.khronos.opengles.GL10; import org.godotengine.godot.input.GodotEditText; import org.godotengine.godot.payments.PaymentsManager; +import org.godotengine.godot.xr.XRMode; public class Godot extends Activity implements SensorEventListener, IDownloaderClient { @@ -116,10 +114,12 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC private Button mPauseButton; private Button mWiFiSettingsButton; + private XRMode xrMode = XRMode.REGULAR; private boolean use_32_bits = false; private boolean use_immersive = false; private boolean use_debug_opengl = false; private boolean mStatePaused; + private boolean activityResumed; private int mState; static private Intent mCurrentIntent; @@ -282,7 +282,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC // ...add to FrameLayout layout.addView(edittext); - mView = new GodotView(getApplication(), io, use_gl3, use_32_bits, use_debug_opengl, this); + mView = new GodotView(this, xrMode, use_gl3, use_32_bits, use_debug_opengl); layout.addView(mView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); edittext.setView(mView); io.setEdit(edittext); @@ -402,6 +402,20 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC } } + /** + * Used by the native code (java_godot_wrapper.h) to check whether the activity is resumed or paused. + */ + private boolean isActivityResumed() { + return activityResumed; + } + + /** + * Used by the native code (java_godot_wrapper.h) to access the Android surface. + */ + private Surface getSurface() { + return mView.getHolder().getSurface(); + } + String expansion_pack_path; private void initializeGodot() { @@ -474,7 +488,11 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC for (int i = 0; i < command_line.length; i++) { boolean has_extra = i < command_line.length - 1; - if (command_line[i].equals("--use_depth_32")) { + if (command_line[i].equals(XRMode.REGULAR.cmdLineArg)) { + xrMode = XRMode.REGULAR; + } else if (command_line[i].equals(XRMode.OVR.cmdLineArg)) { + xrMode = XRMode.OVR; + } else if (command_line[i].equals("--use_depth_32")) { use_32_bits = true; } else if (command_line[i].equals("--debug_opengl")) { use_debug_opengl = true; @@ -612,6 +630,8 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC @Override protected void onPause() { super.onPause(); + activityResumed = false; + if (!godot_initialized) { if (null != mDownloaderClientStub) { mDownloaderClientStub.disconnect(this); @@ -687,6 +707,8 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC singletons[i].onMainResume(); } + + activityResumed = true; } public void UiChangeListener() { diff --git a/platform/android/java/src/org/godotengine/godot/GodotRenderer.java b/platform/android/java/src/org/godotengine/godot/GodotRenderer.java new file mode 100644 index 0000000000..8e3775c2a9 --- /dev/null +++ b/platform/android/java/src/org/godotengine/godot/GodotRenderer.java @@ -0,0 +1,61 @@ +/*************************************************************************/ +/* GodotRenderer.java */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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.godot; + +import android.opengl.GLSurfaceView; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; +import org.godotengine.godot.utils.GLUtils; + +/** + * Godot's renderer implementation. + */ +class GodotRenderer implements GLSurfaceView.Renderer { + + public void onDrawFrame(GL10 gl) { + GodotLib.step(); + for (int i = 0; i < Godot.singleton_count; i++) { + Godot.singletons[i].onGLDrawFrame(gl); + } + } + + public void onSurfaceChanged(GL10 gl, int width, int height) { + + GodotLib.resize(width, height); + for (int i = 0; i < Godot.singleton_count; i++) { + Godot.singletons[i].onGLSurfaceChanged(gl, width, height); + } + } + + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + GodotLib.newcontext(GLUtils.use_32); + } +} diff --git a/platform/android/java/src/org/godotengine/godot/GodotView.java b/platform/android/java/src/org/godotengine/godot/GodotView.java index ab28d9ec33..fc3e47e69d 100644 --- a/platform/android/java/src/org/godotengine/godot/GodotView.java +++ b/platform/android/java/src/org/godotengine/godot/GodotView.java @@ -30,28 +30,20 @@ package org.godotengine.godot; import android.annotation.SuppressLint; -import android.content.Context; -import android.content.ContextWrapper; import android.graphics.PixelFormat; -import android.hardware.input.InputManager; import android.opengl.GLSurfaceView; -import android.util.AttributeSet; -import android.util.Log; -import android.view.InputDevice; import android.view.KeyEvent; import android.view.MotionEvent; -import java.io.File; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import javax.microedition.khronos.egl.EGL10; -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.egl.EGLContext; -import javax.microedition.khronos.egl.EGLDisplay; -import javax.microedition.khronos.opengles.GL10; -import org.godotengine.godot.input.InputManagerCompat; -import org.godotengine.godot.input.InputManagerCompat.InputDeviceListener; +import org.godotengine.godot.input.GodotInputHandler; +import org.godotengine.godot.utils.GLUtils; +import org.godotengine.godot.xr.XRMode; +import org.godotengine.godot.xr.ovr.OvrConfigChooser; +import org.godotengine.godot.xr.ovr.OvrContextFactory; +import org.godotengine.godot.xr.ovr.OvrWindowSurfaceFactory; +import org.godotengine.godot.xr.regular.RegularConfigChooser; +import org.godotengine.godot.xr.regular.RegularContextFactory; +import org.godotengine.godot.xr.regular.RegularFallbackConfigChooser; + /** * A simple GLSurfaceView sub-class that demonstrate how to perform * OpenGL ES 2.0 rendering into a GL Surface. Note the following important @@ -70,57 +62,26 @@ import org.godotengine.godot.input.InputManagerCompat.InputDeviceListener; * that matches it exactly (with regards to red/green/blue/alpha channels * bit depths). Failure to do so would result in an EGL_BAD_MATCH error. */ -public class GodotView extends GLSurfaceView implements InputDeviceListener { - - private static String TAG = "GodotView"; - private static final boolean DEBUG = false; - private Context ctx; - - private GodotIO io; - private static boolean use_gl3 = false; - private static boolean use_32 = false; - private static boolean use_debug_opengl = false; +public class GodotView extends GLSurfaceView { - private Godot activity; + private static String TAG = GodotView.class.getSimpleName(); - private InputManagerCompat mInputManager; - public GodotView(Context context, GodotIO p_io, boolean p_use_gl3, boolean p_use_32_bits, boolean p_use_debug_opengl, Godot p_activity) { - super(context); - ctx = context; - io = p_io; - use_gl3 = p_use_gl3; - use_32 = p_use_32_bits; - use_debug_opengl = p_use_debug_opengl; + private final Godot activity; + private final GodotInputHandler inputHandler; - activity = p_activity; - - setPreserveEGLContextOnPause(true); - - mInputManager = InputManagerCompat.Factory.getInputManager(this.getContext()); - mInputManager.registerInputDeviceListener(this, null); - init(false, 16, 0); - } - - public GodotView(Context context) { - super(context); - ctx = context; - } + public GodotView(Godot activity, XRMode xrMode, boolean p_use_gl3, boolean p_use_32_bits, boolean p_use_debug_opengl) { + super(activity); + GLUtils.use_gl3 = p_use_gl3; + GLUtils.use_32 = p_use_32_bits; + GLUtils.use_debug_opengl = p_use_debug_opengl; - public GodotView(Context context, boolean translucent, int depth, int stencil) { - super(context); - init(translucent, depth, stencil); + this.activity = activity; + this.inputHandler = new GodotInputHandler(this); + init(xrMode, false, 16, 0); } public void initInputDevices() { - /* initially add input devices*/ - int[] deviceIds = mInputManager.getInputDeviceIds(); - for (int deviceId : deviceIds) { - InputDevice device = mInputManager.getInputDevice(deviceId); - if (DEBUG) { - Log.v("GodotView", String.format("init() deviceId:%d, Name:%s\n", deviceId, device.getName())); - } - onInputDeviceAdded(deviceId); - } + this.inputHandler.initInputDevices(); } @SuppressLint("ClickableViewAccessibility") @@ -130,617 +91,80 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { return activity.gotTouchEvent(event); } - public int get_godot_button(int keyCode) { - - int button; - switch (keyCode) { - case KeyEvent.KEYCODE_BUTTON_A: // Android A is SNES B - button = 0; - break; - case KeyEvent.KEYCODE_BUTTON_B: - button = 1; - break; - case KeyEvent.KEYCODE_BUTTON_X: // Android X is SNES Y - button = 2; - break; - case KeyEvent.KEYCODE_BUTTON_Y: - button = 3; - break; - case KeyEvent.KEYCODE_BUTTON_L1: - button = 9; - break; - case KeyEvent.KEYCODE_BUTTON_L2: - button = 15; - break; - case KeyEvent.KEYCODE_BUTTON_R1: - button = 10; - break; - case KeyEvent.KEYCODE_BUTTON_R2: - button = 16; - break; - case KeyEvent.KEYCODE_BUTTON_SELECT: - button = 4; - break; - case KeyEvent.KEYCODE_BUTTON_START: - button = 6; - break; - case KeyEvent.KEYCODE_BUTTON_THUMBL: - button = 7; - break; - case KeyEvent.KEYCODE_BUTTON_THUMBR: - button = 8; - break; - case KeyEvent.KEYCODE_DPAD_UP: - button = 11; - break; - case KeyEvent.KEYCODE_DPAD_DOWN: - button = 12; - break; - case KeyEvent.KEYCODE_DPAD_LEFT: - button = 13; - break; - case KeyEvent.KEYCODE_DPAD_RIGHT: - button = 14; - break; - case KeyEvent.KEYCODE_BUTTON_C: - button = 17; - break; - case KeyEvent.KEYCODE_BUTTON_Z: - button = 18; - break; - - default: - button = keyCode - KeyEvent.KEYCODE_BUTTON_1 + 20; - break; - } - return button; - }; - - private static class joystick { - public int device_id; - public String name; - public ArrayList<InputDevice.MotionRange> axes; - public ArrayList<InputDevice.MotionRange> hats; - } - - private static class RangeComparator implements Comparator<InputDevice.MotionRange> { - @Override - public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) { - return arg0.getAxis() - arg1.getAxis(); - } - } - - ArrayList<joystick> joy_devices = new ArrayList<joystick>(); - - private int find_joy_device(int device_id) { - for (int i = 0; i < joy_devices.size(); i++) { - if (joy_devices.get(i).device_id == device_id) { - return i; - } - } - - return -1; - } - - @Override - public void onInputDeviceAdded(int deviceId) { - int id = find_joy_device(deviceId); - - // Check if the device has not been already added - if (id < 0) { - InputDevice device = mInputManager.getInputDevice(deviceId); - //device can be null if deviceId is not found - if (device != null) { - int sources = device.getSources(); - if (((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) || - ((sources & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK)) { - id = joy_devices.size(); - - joystick joy = new joystick(); - joy.device_id = deviceId; - joy.name = device.getName(); - joy.axes = new ArrayList<InputDevice.MotionRange>(); - joy.hats = new ArrayList<InputDevice.MotionRange>(); - - List<InputDevice.MotionRange> ranges = device.getMotionRanges(); - Collections.sort(ranges, new RangeComparator()); - - for (InputDevice.MotionRange range : ranges) { - if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) { - joy.hats.add(range); - } else { - joy.axes.add(range); - } - } - - joy_devices.add(joy); - - final int device_id = id; - final String name = joy.name; - queueEvent(new Runnable() { - @Override - public void run() { - GodotLib.joyconnectionchanged(device_id, true, name); - } - }); - } - } - } - } - - @Override - public void onInputDeviceRemoved(int deviceId) { - final int device_id = find_joy_device(deviceId); - - // Check if the evice has not been already removed - if (device_id > -1) { - joy_devices.remove(device_id); - - queueEvent(new Runnable() { - @Override - public void run() { - GodotLib.joyconnectionchanged(device_id, false, ""); - } - }); - } - } - - @Override - public void onInputDeviceChanged(int deviceId) { - onInputDeviceRemoved(deviceId); - onInputDeviceAdded(deviceId); - } @Override public boolean onKeyUp(final int keyCode, KeyEvent event) { - - if (keyCode == KeyEvent.KEYCODE_BACK) { - return true; - } - - if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { - return super.onKeyUp(keyCode, event); - }; - - int source = event.getSource(); - if ((source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK || (source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD || (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) { - - final int button = get_godot_button(keyCode); - final int device_id = find_joy_device(event.getDeviceId()); - - // Check if the device exists - if (device_id > -1) { - queueEvent(new Runnable() { - @Override - public void run() { - GodotLib.joybutton(device_id, button, false); - } - }); - return true; - } - } else { - final int chr = event.getUnicodeChar(0); - queueEvent(new Runnable() { - @Override - public void run() { - GodotLib.key(keyCode, chr, false); - } - }); - }; - - return super.onKeyUp(keyCode, event); - }; + return inputHandler.onKeyUp(keyCode, event) || super.onKeyUp(keyCode, event); + } @Override public boolean onKeyDown(final int keyCode, KeyEvent event) { - - if (keyCode == KeyEvent.KEYCODE_BACK) { - activity.onBackPressed(); - // press 'back' button should not terminate program - //normal handle 'back' event in game logic - return true; - } - - if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { - return super.onKeyDown(keyCode, event); - }; - - int source = event.getSource(); - //Log.e(TAG, String.format("Key down! source %d, device %d, joystick %d, %d, %d", event.getDeviceId(), source, (source & InputDevice.SOURCE_JOYSTICK), (source & InputDevice.SOURCE_DPAD), (source & InputDevice.SOURCE_GAMEPAD))); - - if ((source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK || (source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD || (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) { - - if (event.getRepeatCount() > 0) // ignore key echo - return true; - - final int button = get_godot_button(keyCode); - final int device_id = find_joy_device(event.getDeviceId()); - - // Check if the device exists - if (device_id > -1) { - queueEvent(new Runnable() { - @Override - public void run() { - GodotLib.joybutton(device_id, button, true); - } - }); - return true; - } - } else { - final int chr = event.getUnicodeChar(0); - queueEvent(new Runnable() { - @Override - public void run() { - GodotLib.key(keyCode, chr, true); - } - }); - }; - - return super.onKeyDown(keyCode, event); + return inputHandler.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event); } @Override public boolean onGenericMotionEvent(MotionEvent event) { - - if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK && event.getAction() == MotionEvent.ACTION_MOVE) { - - final int device_id = find_joy_device(event.getDeviceId()); - - // Check if the device exists - if (device_id > -1) { - joystick joy = joy_devices.get(device_id); - - for (int i = 0; i < joy.axes.size(); i++) { - InputDevice.MotionRange range = joy.axes.get(i); - final float value = (event.getAxisValue(range.getAxis()) - range.getMin()) / range.getRange() * 2.0f - 1.0f; - final int idx = i; - queueEvent(new Runnable() { - @Override - public void run() { - GodotLib.joyaxis(device_id, idx, value); - } - }); - } - - for (int i = 0; i < joy.hats.size(); i += 2) { - final int hatX = Math.round(event.getAxisValue(joy.hats.get(i).getAxis())); - final int hatY = Math.round(event.getAxisValue(joy.hats.get(i + 1).getAxis())); - queueEvent(new Runnable() { - @Override - public void run() { - GodotLib.joyhat(device_id, hatX, hatY); - } - }); - } - return true; - } - }; - - return super.onGenericMotionEvent(event); - }; - - private void init(boolean translucent, int depth, int stencil) { - - this.setFocusableInTouchMode(true); - /* By default, GLSurfaceView() creates a RGB_565 opaque surface. - * If we want a translucent one, we should change the surface's - * format here, using PixelFormat.TRANSLUCENT for GL Surfaces - * is interpreted as any 32-bit surface with alpha by SurfaceFlinger. - */ - if (translucent) { - this.getHolder().setFormat(PixelFormat.TRANSLUCENT); - } - - /* Setup the context factory for 2.0 rendering. - * See ContextFactory class definition below - */ - setEGLContextFactory(new ContextFactory()); - - /* We need to choose an EGLConfig that matches the format of - * our surface exactly. This is going to be done in our - * custom config chooser. See ConfigChooser class definition - * below. - */ - - if (use_32) { - setEGLConfigChooser(translucent ? - new FallbackConfigChooser(8, 8, 8, 8, 24, stencil, new ConfigChooser(8, 8, 8, 8, 16, stencil)) : - new FallbackConfigChooser(8, 8, 8, 8, 24, stencil, new ConfigChooser(5, 6, 5, 0, 16, stencil))); - - } else { - setEGLConfigChooser(translucent ? - new ConfigChooser(8, 8, 8, 8, 16, stencil) : - new ConfigChooser(5, 6, 5, 0, 16, stencil)); - } - - /* Set the renderer responsible for frame rendering */ - setRenderer(new Renderer()); - } - - private static final int _EGL_CONTEXT_FLAGS_KHR = 0x30FC; - private static final int _EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR = 0x00000001; - - private static class ContextFactory implements GLSurfaceView.EGLContextFactory { - private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098; - public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) { - String driver_name = GodotLib.getGlobal("rendering/quality/driver/driver_name"); - if (use_gl3 && !driver_name.equals("GLES3")) { - use_gl3 = false; - } - if (use_gl3) - Log.w(TAG, "creating OpenGL ES 3.0 context :"); - else - Log.w(TAG, "creating OpenGL ES 2.0 context :"); - - checkEglError("Before eglCreateContext", egl); - EGLContext context; - if (use_debug_opengl) { - int[] attrib_list2 = { EGL_CONTEXT_CLIENT_VERSION, 2, _EGL_CONTEXT_FLAGS_KHR, _EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, EGL10.EGL_NONE }; - int[] attrib_list3 = { EGL_CONTEXT_CLIENT_VERSION, 3, _EGL_CONTEXT_FLAGS_KHR, _EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, EGL10.EGL_NONE }; - context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, use_gl3 ? attrib_list3 : attrib_list2); - } else { - int[] attrib_list2 = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; - int[] attrib_list3 = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL10.EGL_NONE }; - context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, use_gl3 ? attrib_list3 : attrib_list2); - } - checkEglError("After eglCreateContext", egl); - return context; - } - - public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) { - egl.eglDestroyContext(display, context); - } - } - - private static void checkEglError(String prompt, EGL10 egl) { - int error; - while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) { - Log.e(TAG, String.format("%s: EGL error: 0x%x", prompt, error)); - } - } - /* Fallback if 32bit View is not supported*/ - private static class FallbackConfigChooser extends ConfigChooser { - private ConfigChooser fallback; - - public FallbackConfigChooser(int r, int g, int b, int a, int depth, int stencil, ConfigChooser fallback) { - super(r, g, b, a, depth, stencil); - this.fallback = fallback; - } - - @Override - public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) { - EGLConfig ec = super.chooseConfig(egl, display, configs); - if (ec == null) { - Log.w(TAG, "Trying ConfigChooser fallback"); - ec = fallback.chooseConfig(egl, display, configs); - use_32 = false; - } - return ec; - } + return inputHandler.onGenericMotionEvent(event) || super.onGenericMotionEvent(event); } - private static class ConfigChooser implements GLSurfaceView.EGLConfigChooser { - - public ConfigChooser(int r, int g, int b, int a, int depth, int stencil) { - mRedSize = r; - mGreenSize = g; - mBlueSize = b; - mAlphaSize = a; - mDepthSize = depth; - mStencilSize = stencil; - } - - /* This EGL config specification is used to specify 2.0 rendering. - * We use a minimum size of 4 bits for red/green/blue, but will - * perform actual matching in chooseConfig() below. - */ - private static int EGL_OPENGL_ES2_BIT = 4; - private static int[] s_configAttribs2 = { - EGL10.EGL_RED_SIZE, 4, - EGL10.EGL_GREEN_SIZE, 4, - EGL10.EGL_BLUE_SIZE, 4, - // EGL10.EGL_DEPTH_SIZE, 16, - // EGL10.EGL_STENCIL_SIZE, EGL10.EGL_DONT_CARE, - EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL10.EGL_NONE - }; - private static int[] s_configAttribs3 = { - EGL10.EGL_RED_SIZE, 4, - EGL10.EGL_GREEN_SIZE, 4, - EGL10.EGL_BLUE_SIZE, 4, - // EGL10.EGL_DEPTH_SIZE, 16, - // EGL10.EGL_STENCIL_SIZE, EGL10.EGL_DONT_CARE, - EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, //apparently there is no EGL_OPENGL_ES3_BIT - EGL10.EGL_NONE - }; - - public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { - - /* Get the number of minimally matching EGL configurations - */ - int[] num_config = new int[1]; - egl.eglChooseConfig(display, use_gl3 ? s_configAttribs3 : s_configAttribs2, null, 0, num_config); - - int numConfigs = num_config[0]; - - if (numConfigs <= 0) { - throw new IllegalArgumentException("No configs match configSpec"); - } - - /* Allocate then read the array of minimally matching EGL configs - */ - EGLConfig[] configs = new EGLConfig[numConfigs]; - egl.eglChooseConfig(display, use_gl3 ? s_configAttribs3 : s_configAttribs2, configs, numConfigs, num_config); + private void init(XRMode xrMode, boolean translucent, int depth, int stencil) { - if (DEBUG) { - printConfigs(egl, display, configs); - } - /* Now return the "best" one - */ - return chooseConfig(egl, display, configs); - } + setPreserveEGLContextOnPause(true); + setFocusableInTouchMode(true); + switch (xrMode) { - public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, - EGLConfig[] configs) { - for (EGLConfig config : configs) { - int d = findConfigAttrib(egl, display, config, - EGL10.EGL_DEPTH_SIZE, 0); - int s = findConfigAttrib(egl, display, config, - EGL10.EGL_STENCIL_SIZE, 0); + case OVR: + // Replace the default egl config chooser. + setEGLConfigChooser(new OvrConfigChooser()); - // We need at least mDepthSize and mStencilSize bits - if (d < mDepthSize || s < mStencilSize) - continue; + // Replace the default context factory. + setEGLContextFactory(new OvrContextFactory()); - // We want an *exact* match for red/green/blue/alpha - int r = findConfigAttrib(egl, display, config, - EGL10.EGL_RED_SIZE, 0); - int g = findConfigAttrib(egl, display, config, - EGL10.EGL_GREEN_SIZE, 0); - int b = findConfigAttrib(egl, display, config, - EGL10.EGL_BLUE_SIZE, 0); - int a = findConfigAttrib(egl, display, config, - EGL10.EGL_ALPHA_SIZE, 0); + // Replace the default window surface factory. + setEGLWindowSurfaceFactory(new OvrWindowSurfaceFactory()); + break; - if (r == mRedSize && g == mGreenSize && b == mBlueSize && a == mAlphaSize) - return config; - } - return null; - } + case REGULAR: + default: + /* By default, GLSurfaceView() creates a RGB_565 opaque surface. + * If we want a translucent one, we should change the surface's + * format here, using PixelFormat.TRANSLUCENT for GL Surfaces + * is interpreted as any 32-bit surface with alpha by SurfaceFlinger. + */ + if (translucent) { + this.getHolder().setFormat(PixelFormat.TRANSLUCENT); + } - private int findConfigAttrib(EGL10 egl, EGLDisplay display, - EGLConfig config, int attribute, int defaultValue) { + /* Setup the context factory for 2.0 rendering. + * See ContextFactory class definition below + */ + setEGLContextFactory(new RegularContextFactory()); - if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) { - return mValue[0]; - } - return defaultValue; - } + /* We need to choose an EGLConfig that matches the format of + * our surface exactly. This is going to be done in our + * custom config chooser. See ConfigChooser class definition + * below. + */ - private void printConfigs(EGL10 egl, EGLDisplay display, - EGLConfig[] configs) { - int numConfigs = configs.length; - Log.w(TAG, String.format("%d configurations", numConfigs)); - for (int i = 0; i < numConfigs; i++) { - Log.w(TAG, String.format("Configuration %d:\n", i)); - printConfig(egl, display, configs[i]); - } - } + if (GLUtils.use_32) { + setEGLConfigChooser(translucent ? + new RegularFallbackConfigChooser(8, 8, 8, 8, 24, stencil, + new RegularConfigChooser(8, 8, 8, 8, 16, stencil)) : + new RegularFallbackConfigChooser(8, 8, 8, 8, 24, stencil, + new RegularConfigChooser(5, 6, 5, 0, 16, stencil))); - private void printConfig(EGL10 egl, EGLDisplay display, - EGLConfig config) { - int[] attributes = { - EGL10.EGL_BUFFER_SIZE, - EGL10.EGL_ALPHA_SIZE, - EGL10.EGL_BLUE_SIZE, - EGL10.EGL_GREEN_SIZE, - EGL10.EGL_RED_SIZE, - EGL10.EGL_DEPTH_SIZE, - EGL10.EGL_STENCIL_SIZE, - EGL10.EGL_CONFIG_CAVEAT, - EGL10.EGL_CONFIG_ID, - EGL10.EGL_LEVEL, - EGL10.EGL_MAX_PBUFFER_HEIGHT, - EGL10.EGL_MAX_PBUFFER_PIXELS, - EGL10.EGL_MAX_PBUFFER_WIDTH, - EGL10.EGL_NATIVE_RENDERABLE, - EGL10.EGL_NATIVE_VISUAL_ID, - EGL10.EGL_NATIVE_VISUAL_TYPE, - 0x3030, // EGL10.EGL_PRESERVED_RESOURCES, - EGL10.EGL_SAMPLES, - EGL10.EGL_SAMPLE_BUFFERS, - EGL10.EGL_SURFACE_TYPE, - EGL10.EGL_TRANSPARENT_TYPE, - EGL10.EGL_TRANSPARENT_RED_VALUE, - EGL10.EGL_TRANSPARENT_GREEN_VALUE, - EGL10.EGL_TRANSPARENT_BLUE_VALUE, - 0x3039, // EGL10.EGL_BIND_TO_TEXTURE_RGB, - 0x303A, // EGL10.EGL_BIND_TO_TEXTURE_RGBA, - 0x303B, // EGL10.EGL_MIN_SWAP_INTERVAL, - 0x303C, // EGL10.EGL_MAX_SWAP_INTERVAL, - EGL10.EGL_LUMINANCE_SIZE, - EGL10.EGL_ALPHA_MASK_SIZE, - EGL10.EGL_COLOR_BUFFER_TYPE, - EGL10.EGL_RENDERABLE_TYPE, - 0x3042 // EGL10.EGL_CONFORMANT - }; - String[] names = { - "EGL_BUFFER_SIZE", - "EGL_ALPHA_SIZE", - "EGL_BLUE_SIZE", - "EGL_GREEN_SIZE", - "EGL_RED_SIZE", - "EGL_DEPTH_SIZE", - "EGL_STENCIL_SIZE", - "EGL_CONFIG_CAVEAT", - "EGL_CONFIG_ID", - "EGL_LEVEL", - "EGL_MAX_PBUFFER_HEIGHT", - "EGL_MAX_PBUFFER_PIXELS", - "EGL_MAX_PBUFFER_WIDTH", - "EGL_NATIVE_RENDERABLE", - "EGL_NATIVE_VISUAL_ID", - "EGL_NATIVE_VISUAL_TYPE", - "EGL_PRESERVED_RESOURCES", - "EGL_SAMPLES", - "EGL_SAMPLE_BUFFERS", - "EGL_SURFACE_TYPE", - "EGL_TRANSPARENT_TYPE", - "EGL_TRANSPARENT_RED_VALUE", - "EGL_TRANSPARENT_GREEN_VALUE", - "EGL_TRANSPARENT_BLUE_VALUE", - "EGL_BIND_TO_TEXTURE_RGB", - "EGL_BIND_TO_TEXTURE_RGBA", - "EGL_MIN_SWAP_INTERVAL", - "EGL_MAX_SWAP_INTERVAL", - "EGL_LUMINANCE_SIZE", - "EGL_ALPHA_MASK_SIZE", - "EGL_COLOR_BUFFER_TYPE", - "EGL_RENDERABLE_TYPE", - "EGL_CONFORMANT" - }; - int[] value = new int[1]; - for (int i = 0; i < attributes.length; i++) { - int attribute = attributes[i]; - String name = names[i]; - if (egl.eglGetConfigAttrib(display, config, attribute, value)) { - Log.w(TAG, String.format(" %s: %d\n", name, value[0])); } else { - // Log.w(TAG, String.format(" %s: failed\n", name)); - while (egl.eglGetError() != EGL10.EGL_SUCCESS) - ; + setEGLConfigChooser(translucent ? + new RegularConfigChooser(8, 8, 8, 8, 16, stencil) : + new RegularConfigChooser(5, 6, 5, 0, 16, stencil)); } - } + break; } - // Subclasses can adjust these values: - protected int mRedSize; - protected int mGreenSize; - protected int mBlueSize; - protected int mAlphaSize; - protected int mDepthSize; - protected int mStencilSize; - private int[] mValue = new int[1]; + /* Set the renderer responsible for frame rendering */ + setRenderer(new GodotRenderer()); } - private static class Renderer implements GLSurfaceView.Renderer { - - public void onDrawFrame(GL10 gl) { - GodotLib.step(); - for (int i = 0; i < Godot.singleton_count; i++) { - Godot.singletons[i].onGLDrawFrame(gl); - } - } - - public void onSurfaceChanged(GL10 gl, int width, int height) { - - GodotLib.resize(width, height); - for (int i = 0; i < Godot.singleton_count; i++) { - Godot.singletons[i].onGLSurfaceChanged(gl, width, height); - } - } - - public void onSurfaceCreated(GL10 gl, EGLConfig config) { - GodotLib.newcontext(use_32); - } + public void onBackPressed() { + activity.onBackPressed(); } } diff --git a/platform/android/java/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/src/org/godotengine/godot/input/GodotInputHandler.java new file mode 100644 index 0000000000..a443a0ad90 --- /dev/null +++ b/platform/android/java/src/org/godotengine/godot/input/GodotInputHandler.java @@ -0,0 +1,360 @@ +/*************************************************************************/ +/* GodotInputHandler.java */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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.godot.input; + +import static org.godotengine.godot.utils.GLUtils.DEBUG; + +import android.util.Log; +import android.view.InputDevice; +import android.view.InputDevice.MotionRange; +import android.view.KeyEvent; +import android.view.MotionEvent; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import org.godotengine.godot.GodotLib; +import org.godotengine.godot.GodotView; +import org.godotengine.godot.input.InputManagerCompat.InputDeviceListener; + +/** + * Handles input related events for the {@link GodotView} view. + */ +public class GodotInputHandler implements InputDeviceListener { + + private final ArrayList<Joystick> joysticksDevices = new ArrayList<Joystick>(); + + private final GodotView godotView; + private final InputManagerCompat inputManager; + + public GodotInputHandler(GodotView godotView) { + this.godotView = godotView; + this.inputManager = InputManagerCompat.Factory.getInputManager(godotView.getContext()); + this.inputManager.registerInputDeviceListener(this, null); + } + + private void queueEvent(Runnable task) { + godotView.queueEvent(task); + } + + private boolean isKeyEvent_GameDevice(int source) { + // Note that keyboards are often (SOURCE_KEYBOARD | SOURCE_DPAD) + if (source == (InputDevice.SOURCE_KEYBOARD | InputDevice.SOURCE_DPAD)) + return false; + + return (source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK || (source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD || (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD; + } + + public boolean onKeyUp(final int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + return true; + } + + if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { + return false; + }; + + int source = event.getSource(); + if (isKeyEvent_GameDevice(source)) { + + final int button = getGodotButton(keyCode); + final int device_id = findJoystickDevice(event.getDeviceId()); + + // Check if the device exists + if (device_id > -1) { + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.joybutton(device_id, button, false); + } + }); + return true; + } + } else { + final int chr = event.getUnicodeChar(0); + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.key(keyCode, chr, false); + } + }); + }; + + return false; + } + + public boolean onKeyDown(final int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + godotView.onBackPressed(); + // press 'back' button should not terminate program + //normal handle 'back' event in game logic + return true; + } + + if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { + return false; + }; + + int source = event.getSource(); + //Log.e(TAG, String.format("Key down! source %d, device %d, joystick %d, %d, %d", event.getDeviceId(), source, (source & InputDevice.SOURCE_JOYSTICK), (source & InputDevice.SOURCE_DPAD), (source & InputDevice.SOURCE_GAMEPAD))); + + if (isKeyEvent_GameDevice(source)) { + + if (event.getRepeatCount() > 0) // ignore key echo + return true; + + final int button = getGodotButton(keyCode); + final int device_id = findJoystickDevice(event.getDeviceId()); + + // Check if the device exists + if (device_id > -1) { + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.joybutton(device_id, button, true); + } + }); + return true; + } + } else { + final int chr = event.getUnicodeChar(0); + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.key(keyCode, chr, true); + } + }); + }; + + return false; + } + + public boolean onGenericMotionEvent(MotionEvent event) { + if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK && event.getAction() == MotionEvent.ACTION_MOVE) { + + final int device_id = findJoystickDevice(event.getDeviceId()); + + // Check if the device exists + if (device_id > -1) { + Joystick joy = joysticksDevices.get(device_id); + + for (int i = 0; i < joy.axes.size(); i++) { + InputDevice.MotionRange range = joy.axes.get(i); + final float value = (event.getAxisValue(range.getAxis()) - range.getMin()) / range.getRange() * 2.0f - 1.0f; + final int idx = i; + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.joyaxis(device_id, idx, value); + } + }); + } + + for (int i = 0; i < joy.hats.size(); i += 2) { + final int hatX = Math.round(event.getAxisValue(joy.hats.get(i).getAxis())); + final int hatY = Math.round(event.getAxisValue(joy.hats.get(i + 1).getAxis())); + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.joyhat(device_id, hatX, hatY); + } + }); + } + return true; + } + }; + + return false; + } + + public void initInputDevices() { + /* initially add input devices*/ + int[] deviceIds = inputManager.getInputDeviceIds(); + for (int deviceId : deviceIds) { + InputDevice device = inputManager.getInputDevice(deviceId); + if (DEBUG) { + Log.v("GodotView", String.format("init() deviceId:%d, Name:%s\n", deviceId, device.getName())); + } + onInputDeviceAdded(deviceId); + } + } + + @Override + public void onInputDeviceAdded(int deviceId) { + int id = findJoystickDevice(deviceId); + + // Check if the device has not been already added + if (id < 0) { + InputDevice device = inputManager.getInputDevice(deviceId); + //device can be null if deviceId is not found + if (device != null) { + int sources = device.getSources(); + if (((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) || + ((sources & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK)) { + id = joysticksDevices.size(); + + Joystick joy = new Joystick(); + joy.device_id = deviceId; + joy.name = device.getName(); + joy.axes = new ArrayList<InputDevice.MotionRange>(); + joy.hats = new ArrayList<InputDevice.MotionRange>(); + + List<InputDevice.MotionRange> ranges = device.getMotionRanges(); + Collections.sort(ranges, new RangeComparator()); + + for (InputDevice.MotionRange range : ranges) { + if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) { + joy.hats.add(range); + } else { + joy.axes.add(range); + } + } + + joysticksDevices.add(joy); + + final int device_id = id; + final String name = joy.name; + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.joyconnectionchanged(device_id, true, name); + } + }); + } + } + } + } + + @Override + public void onInputDeviceRemoved(int deviceId) { + final int device_id = findJoystickDevice(deviceId); + + // Check if the evice has not been already removed + if (device_id > -1) { + joysticksDevices.remove(device_id); + + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.joyconnectionchanged(device_id, false, ""); + } + }); + } + } + + @Override + public void onInputDeviceChanged(int deviceId) { + onInputDeviceRemoved(deviceId); + onInputDeviceAdded(deviceId); + } + + private static class RangeComparator implements Comparator<MotionRange> { + @Override + public int compare(MotionRange arg0, MotionRange arg1) { + return arg0.getAxis() - arg1.getAxis(); + } + } + + public static int getGodotButton(int keyCode) { + int button; + switch (keyCode) { + case KeyEvent.KEYCODE_BUTTON_A: // Android A is SNES B + button = 0; + break; + case KeyEvent.KEYCODE_BUTTON_B: + button = 1; + break; + case KeyEvent.KEYCODE_BUTTON_X: // Android X is SNES Y + button = 2; + break; + case KeyEvent.KEYCODE_BUTTON_Y: + button = 3; + break; + case KeyEvent.KEYCODE_BUTTON_L1: + button = 9; + break; + case KeyEvent.KEYCODE_BUTTON_L2: + button = 15; + break; + case KeyEvent.KEYCODE_BUTTON_R1: + button = 10; + break; + case KeyEvent.KEYCODE_BUTTON_R2: + button = 16; + break; + case KeyEvent.KEYCODE_BUTTON_SELECT: + button = 4; + break; + case KeyEvent.KEYCODE_BUTTON_START: + button = 6; + break; + case KeyEvent.KEYCODE_BUTTON_THUMBL: + button = 7; + break; + case KeyEvent.KEYCODE_BUTTON_THUMBR: + button = 8; + break; + case KeyEvent.KEYCODE_DPAD_UP: + button = 11; + break; + case KeyEvent.KEYCODE_DPAD_DOWN: + button = 12; + break; + case KeyEvent.KEYCODE_DPAD_LEFT: + button = 13; + break; + case KeyEvent.KEYCODE_DPAD_RIGHT: + button = 14; + break; + case KeyEvent.KEYCODE_BUTTON_C: + button = 17; + break; + case KeyEvent.KEYCODE_BUTTON_Z: + button = 18; + break; + + default: + button = keyCode - KeyEvent.KEYCODE_BUTTON_1 + 20; + break; + } + return button; + } + + private int findJoystickDevice(int device_id) { + for (int i = 0; i < joysticksDevices.size(); i++) { + if (joysticksDevices.get(i).device_id == device_id) { + return i; + } + } + + return -1; + } +} diff --git a/platform/android/java/src/org/godotengine/godot/input/Joystick.java b/platform/android/java/src/org/godotengine/godot/input/Joystick.java new file mode 100644 index 0000000000..ff95bfb0c5 --- /dev/null +++ b/platform/android/java/src/org/godotengine/godot/input/Joystick.java @@ -0,0 +1,44 @@ +/*************************************************************************/ +/* Joystick.java */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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.godot.input; + +import android.view.InputDevice.MotionRange; +import java.util.ArrayList; + +/** + * POJO class to represent a Joystick input device. + */ +class Joystick { + int device_id; + String name; + ArrayList<MotionRange> axes; + ArrayList<MotionRange> hats; +} diff --git a/platform/android/java/src/org/godotengine/godot/utils/GLUtils.java b/platform/android/java/src/org/godotengine/godot/utils/GLUtils.java new file mode 100644 index 0000000000..6c95494f8b --- /dev/null +++ b/platform/android/java/src/org/godotengine/godot/utils/GLUtils.java @@ -0,0 +1,157 @@ +/*************************************************************************/ +/* GLUtils.java */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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.godot.utils; + +import android.util.Log; +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.egl.EGLDisplay; + +/** + * Contains GL utilities methods. + */ +public class GLUtils { + + private static final String TAG = GLUtils.class.getSimpleName(); + + public static final boolean DEBUG = false; + + public static boolean use_gl3 = false; + public static boolean use_32 = false; + public static boolean use_debug_opengl = false; + + private static final String[] ATTRIBUTES_NAMES = new String[] { + "EGL_BUFFER_SIZE", + "EGL_ALPHA_SIZE", + "EGL_BLUE_SIZE", + "EGL_GREEN_SIZE", + "EGL_RED_SIZE", + "EGL_DEPTH_SIZE", + "EGL_STENCIL_SIZE", + "EGL_CONFIG_CAVEAT", + "EGL_CONFIG_ID", + "EGL_LEVEL", + "EGL_MAX_PBUFFER_HEIGHT", + "EGL_MAX_PBUFFER_PIXELS", + "EGL_MAX_PBUFFER_WIDTH", + "EGL_NATIVE_RENDERABLE", + "EGL_NATIVE_VISUAL_ID", + "EGL_NATIVE_VISUAL_TYPE", + "EGL_PRESERVED_RESOURCES", + "EGL_SAMPLES", + "EGL_SAMPLE_BUFFERS", + "EGL_SURFACE_TYPE", + "EGL_TRANSPARENT_TYPE", + "EGL_TRANSPARENT_RED_VALUE", + "EGL_TRANSPARENT_GREEN_VALUE", + "EGL_TRANSPARENT_BLUE_VALUE", + "EGL_BIND_TO_TEXTURE_RGB", + "EGL_BIND_TO_TEXTURE_RGBA", + "EGL_MIN_SWAP_INTERVAL", + "EGL_MAX_SWAP_INTERVAL", + "EGL_LUMINANCE_SIZE", + "EGL_ALPHA_MASK_SIZE", + "EGL_COLOR_BUFFER_TYPE", + "EGL_RENDERABLE_TYPE", + "EGL_CONFORMANT" + }; + + private static final int[] ATTRIBUTES = new int[] { + EGL10.EGL_BUFFER_SIZE, + EGL10.EGL_ALPHA_SIZE, + EGL10.EGL_BLUE_SIZE, + EGL10.EGL_GREEN_SIZE, + EGL10.EGL_RED_SIZE, + EGL10.EGL_DEPTH_SIZE, + EGL10.EGL_STENCIL_SIZE, + EGL10.EGL_CONFIG_CAVEAT, + EGL10.EGL_CONFIG_ID, + EGL10.EGL_LEVEL, + EGL10.EGL_MAX_PBUFFER_HEIGHT, + EGL10.EGL_MAX_PBUFFER_PIXELS, + EGL10.EGL_MAX_PBUFFER_WIDTH, + EGL10.EGL_NATIVE_RENDERABLE, + EGL10.EGL_NATIVE_VISUAL_ID, + EGL10.EGL_NATIVE_VISUAL_TYPE, + 0x3030, // EGL10.EGL_PRESERVED_RESOURCES, + EGL10.EGL_SAMPLES, + EGL10.EGL_SAMPLE_BUFFERS, + EGL10.EGL_SURFACE_TYPE, + EGL10.EGL_TRANSPARENT_TYPE, + EGL10.EGL_TRANSPARENT_RED_VALUE, + EGL10.EGL_TRANSPARENT_GREEN_VALUE, + EGL10.EGL_TRANSPARENT_BLUE_VALUE, + 0x3039, // EGL10.EGL_BIND_TO_TEXTURE_RGB, + 0x303A, // EGL10.EGL_BIND_TO_TEXTURE_RGBA, + 0x303B, // EGL10.EGL_MIN_SWAP_INTERVAL, + 0x303C, // EGL10.EGL_MAX_SWAP_INTERVAL, + EGL10.EGL_LUMINANCE_SIZE, + EGL10.EGL_ALPHA_MASK_SIZE, + EGL10.EGL_COLOR_BUFFER_TYPE, + EGL10.EGL_RENDERABLE_TYPE, + 0x3042 // EGL10.EGL_CONFORMANT + }; + + private GLUtils() {} + + public static void checkEglError(String tag, String prompt, EGL10 egl) { + int error; + while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) { + Log.e(tag, String.format("%s: EGL error: 0x%x", prompt, error)); + } + } + + public static void printConfigs(EGL10 egl, EGLDisplay display, + EGLConfig[] configs) { + int numConfigs = configs.length; + Log.v(TAG, String.format("%d configurations", numConfigs)); + for (int i = 0; i < numConfigs; i++) { + Log.v(TAG, String.format("Configuration %d:\n", i)); + printConfig(egl, display, configs[i]); + } + } + + private static void printConfig(EGL10 egl, EGLDisplay display, + EGLConfig config) { + int[] value = new int[1]; + for (int i = 0; i < ATTRIBUTES.length; i++) { + int attribute = ATTRIBUTES[i]; + String name = ATTRIBUTES_NAMES[i]; + if (egl.eglGetConfigAttrib(display, config, attribute, value)) { + Log.i(TAG, String.format(" %s: %d\n", name, value[0])); + } else { + // Log.w(TAG, String.format(" %s: failed\n", name)); + while (egl.eglGetError() != EGL10.EGL_SUCCESS) + ; + } + } + } +} diff --git a/platform/android/java/src/org/godotengine/godot/xr/XRMode.java b/platform/android/java/src/org/godotengine/godot/xr/XRMode.java new file mode 100644 index 0000000000..dd5701af7d --- /dev/null +++ b/platform/android/java/src/org/godotengine/godot/xr/XRMode.java @@ -0,0 +1,49 @@ +/*************************************************************************/ +/* XRMode.java */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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.godot.xr; + +/** + * Godot available XR modes. + */ +public enum XRMode { + REGULAR(0, "Regular", "--xr_mode_regular"), // Regular/flatscreen + OVR(1, "Oculus Mobile VR", "--xr_mode_ovr"); + + final int index; + final String label; + public final String cmdLineArg; + + XRMode(int index, String label, String cmdLineArg) { + this.index = index; + this.label = label; + this.cmdLineArg = cmdLineArg; + } +} diff --git a/platform/android/java/src/org/godotengine/godot/xr/ovr/OvrConfigChooser.java b/platform/android/java/src/org/godotengine/godot/xr/ovr/OvrConfigChooser.java new file mode 100644 index 0000000000..ff836a31ca --- /dev/null +++ b/platform/android/java/src/org/godotengine/godot/xr/ovr/OvrConfigChooser.java @@ -0,0 +1,112 @@ +/*************************************************************************/ +/* OvrConfigChooser.java */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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.godot.xr.ovr; + +import android.opengl.EGLExt; +import android.opengl.GLSurfaceView; +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.egl.EGLDisplay; + +/** + * EGL config chooser for the Oculus Mobile VR SDK. + */ +public class OvrConfigChooser implements GLSurfaceView.EGLConfigChooser { + + private static final int[] CONFIG_ATTRIBS = { + EGL10.EGL_RED_SIZE, 8, + EGL10.EGL_GREEN_SIZE, 8, + EGL10.EGL_BLUE_SIZE, 8, + EGL10.EGL_ALPHA_SIZE, 8, // Need alpha for the multi-pass timewarp compositor + EGL10.EGL_DEPTH_SIZE, 0, + EGL10.EGL_STENCIL_SIZE, 0, + EGL10.EGL_SAMPLES, 0, + EGL10.EGL_NONE + }; + + @Override + public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { + // Do NOT use eglChooseConfig, because the Android EGL code pushes in + // multisample flags in eglChooseConfig if the user has selected the "force 4x + // MSAA" option in settings, and that is completely wasted for our warp + // target. + int[] numConfig = new int[1]; + if (!egl.eglGetConfigs(display, null, 0, numConfig)) { + throw new IllegalArgumentException("eglGetConfigs failed."); + } + + int configsCount = numConfig[0]; + if (configsCount <= 0) { + throw new IllegalArgumentException("No configs match configSpec"); + } + + EGLConfig[] configs = new EGLConfig[configsCount]; + if (!egl.eglGetConfigs(display, configs, configsCount, numConfig)) { + throw new IllegalArgumentException("eglGetConfigs #2 failed."); + } + + int[] value = new int[1]; + for (EGLConfig config : configs) { + egl.eglGetConfigAttrib(display, config, EGL10.EGL_RENDERABLE_TYPE, value); + if ((value[0] & EGLExt.EGL_OPENGL_ES3_BIT_KHR) != EGLExt.EGL_OPENGL_ES3_BIT_KHR) { + continue; + } + + // The pbuffer config also needs to be compatible with normal window rendering + // so it can share textures with the window context. + egl.eglGetConfigAttrib(display, config, EGL10.EGL_SURFACE_TYPE, value); + if ((value[0] & (EGL10.EGL_WINDOW_BIT | EGL10.EGL_PBUFFER_BIT)) != (EGL10.EGL_WINDOW_BIT | EGL10.EGL_PBUFFER_BIT)) { + continue; + } + + // Check each attribute in CONFIG_ATTRIBS (which are the attributes we care about) + // and ensure the value in config matches. + int attribIndex = 0; + while (CONFIG_ATTRIBS[attribIndex] != EGL10.EGL_NONE) { + egl.eglGetConfigAttrib(display, config, CONFIG_ATTRIBS[attribIndex], value); + if (value[0] != CONFIG_ATTRIBS[attribIndex + 1]) { + // Attribute key's value does not match the configs value. + // Start checking next config. + break; + } + + // Step by two because CONFIG_ATTRIBS is in key/value pairs. + attribIndex += 2; + } + + if (CONFIG_ATTRIBS[attribIndex] == EGL10.EGL_NONE) { + // All relevant attributes match, set the config and stop checking the rest. + return config; + } + } + return null; + } +} diff --git a/platform/android/java/src/org/godotengine/godot/xr/ovr/OvrContextFactory.java b/platform/android/java/src/org/godotengine/godot/xr/ovr/OvrContextFactory.java new file mode 100644 index 0000000000..5f6da8c672 --- /dev/null +++ b/platform/android/java/src/org/godotengine/godot/xr/ovr/OvrContextFactory.java @@ -0,0 +1,58 @@ +/*************************************************************************/ +/* OvrContextFactory.java */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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.godot.xr.ovr; + +import android.opengl.EGL14; +import android.opengl.GLSurfaceView; +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.egl.EGLContext; +import javax.microedition.khronos.egl.EGLDisplay; + +/** + * EGL Context factory for the Oculus mobile VR SDK. + */ +public class OvrContextFactory implements GLSurfaceView.EGLContextFactory { + + private static final int[] CONTEXT_ATTRIBS = { + EGL14.EGL_CONTEXT_CLIENT_VERSION, 3, EGL10.EGL_NONE + }; + + @Override + public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) { + return egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, CONTEXT_ATTRIBS); + } + + @Override + public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) { + egl.eglDestroyContext(display, context); + } +} diff --git a/platform/android/java/src/org/godotengine/godot/xr/ovr/OvrWindowSurfaceFactory.java b/platform/android/java/src/org/godotengine/godot/xr/ovr/OvrWindowSurfaceFactory.java new file mode 100644 index 0000000000..f1e38c35d8 --- /dev/null +++ b/platform/android/java/src/org/godotengine/godot/xr/ovr/OvrWindowSurfaceFactory.java @@ -0,0 +1,60 @@ +/*************************************************************************/ +/* OvrWindowSurfaceFactory.java */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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.godot.xr.ovr; + +import android.opengl.GLSurfaceView; +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.egl.EGLDisplay; +import javax.microedition.khronos.egl.EGLSurface; + +/** + * EGL window surface factory for the Oculus mobile VR SDK. + */ +public class OvrWindowSurfaceFactory implements GLSurfaceView.EGLWindowSurfaceFactory { + + private final static int[] SURFACE_ATTRIBS = { + EGL10.EGL_WIDTH, 16, + EGL10.EGL_HEIGHT, 16, + EGL10.EGL_NONE + }; + + @Override + public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config, + Object nativeWindow) { + return egl.eglCreatePbufferSurface(display, config, SURFACE_ATTRIBS); + } + + @Override + public void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface) { + egl.eglDestroySurface(display, surface); + } +} diff --git a/platform/android/java/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java b/platform/android/java/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java new file mode 100644 index 0000000000..3836967f86 --- /dev/null +++ b/platform/android/java/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java @@ -0,0 +1,151 @@ +/*************************************************************************/ +/* RegularConfigChooser.java */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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.godot.xr.regular; + +import android.opengl.GLSurfaceView; +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.egl.EGLDisplay; +import org.godotengine.godot.utils.GLUtils; + +/** + * Used to select the egl config for pancake games. + */ +public class RegularConfigChooser implements GLSurfaceView.EGLConfigChooser { + + private static final String TAG = RegularConfigChooser.class.getSimpleName(); + + private int[] mValue = new int[1]; + + /* This EGL config specification is used to specify 2.0 rendering. + * We use a minimum size of 4 bits for red/green/blue, but will + * perform actual matching in chooseConfig() below. + */ + private static int EGL_OPENGL_ES2_BIT = 4; + private static int[] s_configAttribs2 = { + EGL10.EGL_RED_SIZE, 4, + EGL10.EGL_GREEN_SIZE, 4, + EGL10.EGL_BLUE_SIZE, 4, + // EGL10.EGL_DEPTH_SIZE, 16, + // EGL10.EGL_STENCIL_SIZE, EGL10.EGL_DONT_CARE, + EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL10.EGL_NONE + }; + private static int[] s_configAttribs3 = { + EGL10.EGL_RED_SIZE, 4, + EGL10.EGL_GREEN_SIZE, 4, + EGL10.EGL_BLUE_SIZE, 4, + // EGL10.EGL_DEPTH_SIZE, 16, + // EGL10.EGL_STENCIL_SIZE, EGL10.EGL_DONT_CARE, + EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, //apparently there is no EGL_OPENGL_ES3_BIT + EGL10.EGL_NONE + }; + + public RegularConfigChooser(int r, int g, int b, int a, int depth, int stencil) { + mRedSize = r; + mGreenSize = g; + mBlueSize = b; + mAlphaSize = a; + mDepthSize = depth; + mStencilSize = stencil; + } + + public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { + + /* Get the number of minimally matching EGL configurations + */ + int[] num_config = new int[1]; + egl.eglChooseConfig(display, GLUtils.use_gl3 ? s_configAttribs3 : s_configAttribs2, null, 0, num_config); + + int numConfigs = num_config[0]; + + if (numConfigs <= 0) { + throw new IllegalArgumentException("No configs match configSpec"); + } + + /* Allocate then read the array of minimally matching EGL configs + */ + EGLConfig[] configs = new EGLConfig[numConfigs]; + egl.eglChooseConfig(display, GLUtils.use_gl3 ? s_configAttribs3 : s_configAttribs2, configs, numConfigs, num_config); + + if (GLUtils.DEBUG) { + GLUtils.printConfigs(egl, display, configs); + } + /* Now return the "best" one + */ + return chooseConfig(egl, display, configs); + } + + public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, + EGLConfig[] configs) { + for (EGLConfig config : configs) { + int d = findConfigAttrib(egl, display, config, + EGL10.EGL_DEPTH_SIZE, 0); + int s = findConfigAttrib(egl, display, config, + EGL10.EGL_STENCIL_SIZE, 0); + + // We need at least mDepthSize and mStencilSize bits + if (d < mDepthSize || s < mStencilSize) + continue; + + // We want an *exact* match for red/green/blue/alpha + int r = findConfigAttrib(egl, display, config, + EGL10.EGL_RED_SIZE, 0); + int g = findConfigAttrib(egl, display, config, + EGL10.EGL_GREEN_SIZE, 0); + int b = findConfigAttrib(egl, display, config, + EGL10.EGL_BLUE_SIZE, 0); + int a = findConfigAttrib(egl, display, config, + EGL10.EGL_ALPHA_SIZE, 0); + + if (r == mRedSize && g == mGreenSize && b == mBlueSize && a == mAlphaSize) + return config; + } + return null; + } + + private int findConfigAttrib(EGL10 egl, EGLDisplay display, + EGLConfig config, int attribute, int defaultValue) { + + if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) { + return mValue[0]; + } + return defaultValue; + } + + // Subclasses can adjust these values: + protected int mRedSize; + protected int mGreenSize; + protected int mBlueSize; + protected int mAlphaSize; + protected int mDepthSize; + protected int mStencilSize; +} diff --git a/platform/android/java/src/org/godotengine/godot/xr/regular/RegularContextFactory.java b/platform/android/java/src/org/godotengine/godot/xr/regular/RegularContextFactory.java new file mode 100644 index 0000000000..4f1e9a696b --- /dev/null +++ b/platform/android/java/src/org/godotengine/godot/xr/regular/RegularContextFactory.java @@ -0,0 +1,81 @@ +/*************************************************************************/ +/* RegularContextFactory.java */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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.godot.xr.regular; + +import android.opengl.GLSurfaceView; +import android.util.Log; +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.egl.EGLContext; +import javax.microedition.khronos.egl.EGLDisplay; +import org.godotengine.godot.GodotLib; +import org.godotengine.godot.utils.GLUtils; + +/** + * Factory used to setup the opengl context for pancake games. + */ +public class RegularContextFactory implements GLSurfaceView.EGLContextFactory { + private static final String TAG = RegularContextFactory.class.getSimpleName(); + + private static final int _EGL_CONTEXT_FLAGS_KHR = 0x30FC; + private static final int _EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR = 0x00000001; + + private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098; + + public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) { + String driver_name = GodotLib.getGlobal("rendering/quality/driver/driver_name"); + if (GLUtils.use_gl3 && !driver_name.equals("GLES3")) { + GLUtils.use_gl3 = false; + } + if (GLUtils.use_gl3) + Log.w(TAG, "creating OpenGL ES 3.0 context :"); + else + Log.w(TAG, "creating OpenGL ES 2.0 context :"); + + GLUtils.checkEglError(TAG, "Before eglCreateContext", egl); + EGLContext context; + if (GLUtils.use_debug_opengl) { + int[] attrib_list2 = { EGL_CONTEXT_CLIENT_VERSION, 2, _EGL_CONTEXT_FLAGS_KHR, _EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, EGL10.EGL_NONE }; + int[] attrib_list3 = { EGL_CONTEXT_CLIENT_VERSION, 3, _EGL_CONTEXT_FLAGS_KHR, _EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, EGL10.EGL_NONE }; + context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, GLUtils.use_gl3 ? attrib_list3 : attrib_list2); + } else { + int[] attrib_list2 = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; + int[] attrib_list3 = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL10.EGL_NONE }; + context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, GLUtils.use_gl3 ? attrib_list3 : attrib_list2); + } + GLUtils.checkEglError(TAG, "After eglCreateContext", egl); + return context; + } + + public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) { + egl.eglDestroyContext(display, context); + } +} diff --git a/platform/android/java/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java b/platform/android/java/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java new file mode 100644 index 0000000000..f5718ef2b3 --- /dev/null +++ b/platform/android/java/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java @@ -0,0 +1,61 @@ +/*************************************************************************/ +/* RegularFallbackConfigChooser.java */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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.godot.xr.regular; + +import android.util.Log; +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.egl.EGLDisplay; +import org.godotengine.godot.utils.GLUtils; + +/* Fallback if 32bit View is not supported*/ +public class RegularFallbackConfigChooser extends RegularConfigChooser { + + private static final String TAG = RegularFallbackConfigChooser.class.getSimpleName(); + + private RegularConfigChooser fallback; + + public RegularFallbackConfigChooser(int r, int g, int b, int a, int depth, int stencil, RegularConfigChooser fallback) { + super(r, g, b, a, depth, stencil); + this.fallback = fallback; + } + + @Override + public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) { + EGLConfig ec = super.chooseConfig(egl, display, configs); + if (ec == null) { + Log.w(TAG, "Trying ConfigChooser fallback"); + ec = fallback.chooseConfig(egl, display, configs); + GLUtils.use_32 = false; + } + return ec; + } +} diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index 466f79c215..77f077456e 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -289,7 +289,7 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) { PoolVector<int>::Write w = sarr.write(); env->GetIntArrayRegion(arr, 0, fCount, w.ptr()); - w = PoolVector<int>::Write(); + w.release(); return sarr; }; @@ -302,7 +302,7 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) { PoolVector<uint8_t>::Write w = sarr.write(); env->GetByteArrayRegion(arr, 0, fCount, reinterpret_cast<signed char *>(w.ptr())); - w = PoolVector<uint8_t>::Write(); + w.release(); return sarr; }; @@ -514,7 +514,7 @@ public: PoolVector<int>::Write w = sarr.write(); env->GetIntArrayRegion(arr, 0, fCount, w.ptr()); - w = PoolVector<int>::Write(); + w.release(); ret = sarr; env->DeleteLocalRef(arr); } break; @@ -528,7 +528,7 @@ public: PoolVector<float>::Write w = sarr.write(); env->GetFloatArrayRegion(arr, 0, fCount, w.ptr()); - w = PoolVector<float>::Write(); + w.release(); ret = sarr; env->DeleteLocalRef(arr); } break; diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp index e92d4437b1..339b14974c 100644 --- a/platform/android/java_godot_wrapper.cpp +++ b/platform/android/java_godot_wrapper.cpp @@ -60,6 +60,8 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance) { _set_clipboard = p_env->GetMethodID(cls, "setClipboard", "(Ljava/lang/String;)V"); _request_permission = p_env->GetMethodID(cls, "requestPermission", "(Ljava/lang/String;)Z"); _init_input_devices = p_env->GetMethodID(cls, "initInputDevices", "()V"); + _get_surface = p_env->GetMethodID(cls, "getSurface", "()Landroid/view/Surface;"); + _is_activity_resumed = p_env->GetMethodID(cls, "isActivityResumed", "()Z"); } GodotJavaWrapper::~GodotJavaWrapper() { @@ -191,3 +193,21 @@ void GodotJavaWrapper::init_input_devices() { env->CallVoidMethod(godot_instance, _init_input_devices); } } + +jobject GodotJavaWrapper::get_surface() { + if (_get_surface) { + JNIEnv *env = ThreadAndroid::get_env(); + return env->CallObjectMethod(godot_instance, _get_surface); + } else { + return NULL; + } +} + +bool GodotJavaWrapper::is_activity_resumed() { + if (_is_activity_resumed) { + JNIEnv *env = ThreadAndroid::get_env(); + return env->CallBooleanMethod(godot_instance, _is_activity_resumed); + } else { + return false; + } +} diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h index be4f109d8c..82c2a5d122 100644 --- a/platform/android/java_godot_wrapper.h +++ b/platform/android/java_godot_wrapper.h @@ -55,6 +55,8 @@ private: jmethodID _set_clipboard = 0; jmethodID _request_permission = 0; jmethodID _init_input_devices = 0; + jmethodID _get_surface = 0; + jmethodID _is_activity_resumed = 0; public: GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance); @@ -78,6 +80,8 @@ public: void set_clipboard(const String &p_text); bool request_permission(const String &p_name); void init_input_devices(); + jobject get_surface(); + bool is_activity_resumed(); }; #endif /* !JAVA_GODOT_WRAPPER_H */ diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index f8076dfd2d..ebc319e57d 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -176,6 +176,9 @@ Error OS_Android::initialize(const VideoMode &p_desired, int p_video_driver, int input = memnew(InputDefault); input->set_fallback_mapping("Default Android Gamepad"); + ///@TODO implement a subclass for Android and instantiate that instead + camera_server = memnew(CameraServer); + //power_manager = memnew(PowerAndroid); return OK; @@ -193,6 +196,9 @@ void OS_Android::delete_main_loop() { } void OS_Android::finalize() { + + memdelete(camera_server); + memdelete(input); } diff --git a/platform/android/os_android.h b/platform/android/os_android.h index 4dbc96f4da..e74d4cfd43 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -39,6 +39,7 @@ #include "main/input_default.h" //#include "power_android.h" #include "servers/audio_server.h" +#include "servers/camera_server.h" #include "servers/visual/rasterizer.h" class GodotJavaWrapper; @@ -77,6 +78,8 @@ private: VisualServer *visual_server; + CameraServer *camera_server; + mutable String data_dir_cache; //AudioDriverAndroid audio_driver_android; |