Initial Oculus Quest support added.

This commit is contained in:
XsparkieX 2020-09-06 13:40:32 +02:00
parent c0b15329e3
commit 791e90b22a
64 changed files with 11622 additions and 72 deletions

17
.gitignore vendored
View file

@ -1,8 +1,9 @@
build
Makefile.local
*.swp
*tags
*~
*.ccls-cache/
compile_commands.json
# OS X
####################
@ -39,3 +40,17 @@ profile
*.sdf
*.opensdf
*.suo
# Android
####################
.gradle/
.cxx/
.settings/
assets/
jniLibs/
Debug/
Release/
*.iml
.classpath
.project
local.properties

View file

@ -111,6 +111,10 @@ ifndef CLIENTBIN
CLIENTBIN=ioquake3
endif
ifndef LIBPREFIX
LIBPREFIX=
endif
ifndef SERVERBIN
SERVERBIN=ioq3ded
endif
@ -250,6 +254,7 @@ RGL1DIR=$(MOUNT_DIR)/renderergl1
RGL2DIR=$(MOUNT_DIR)/renderergl2
CMDIR=$(MOUNT_DIR)/qcommon
SDLDIR=$(MOUNT_DIR)/sdl
VRDIR=$(MOUNT_DIR)/vr
ASMDIR=$(MOUNT_DIR)/asm
SYSDIR=$(MOUNT_DIR)/sys
GDIR=$(MOUNT_DIR)/game
@ -276,6 +281,7 @@ LOKISETUPDIR=misc/setup
NSISDIR=misc/nsis
SDLHDIR=$(MOUNT_DIR)/SDL2
LIBSDIR=$(MOUNT_DIR)/libs
VRAPIDIR=$(MOUNT_DIR)/VrApi
bin_path=$(shell which $(1) 2> /dev/null)
@ -422,6 +428,51 @@ ifneq (,$(findstring "$(PLATFORM)", "linux" "gnu_kfreebsd" "kfreebsd-gnu" "gnu")
endif
else # ifeq Linux
#############################################################################
# SETUP AND BUILD -- Android
#############################################################################
ifeq ($(PLATFORM),android)
ANDROID_NDK = ~/Android/Sdk/ndk/21.1.6352462
ARCH = aarch64
CC = $(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android26-clang
RANLIB = $(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android-ranlib
TOOLS_CFLAGS += -DARCH_STRING=\"$(COMPILE_ARCH)\"
BASE_CFLAGS = -Wall -fno-strict-aliasing -Wimplicit -Wstrict-prototypes -pipe \
-fno-builtin-cos -fno-builtin-sin -fPIC -DARCH_STRING=\\\"$(ARCH)\\\"
CLIENT_CFLAGS += $(SDL_CFLAGS) -DSDL_DISABLE_IMMINTRIN_H -fno-builtin-cos -fno-builtin-sin
OPTIMIZEVM = -O3 -funroll-loops -fomit-frame-pointer
OPTIMIZE = $(OPTIMIZEVM) -ffast-math
HAVE_VM_COMPILED = false
LIBPREFIX = lib
CLIENTBIN = $(LIBPREFIX)ioquake3
FULLBINEXT = .so
SHLIBEXT = so
SHLIBCFLAGS =
SHLIBLDFLAGS = -shared -lm $(LDFLAGS)
THREAD_LIBS =
LIBS = -ldl -lm -Wl,--no-undefined -shared
CLIENT_LIBS = -lGLESv3
RENDERER_LIBS = -lGLESv3 -lEGL
# SDL
BASE_CFLAGS += -I$(SDLHDIR)/include
CLIENT_LIBS += $(LIBSDIR)/android/arm64-v8a/libSDL2.so
RENDERER_LIBS += $(LIBSDIR)/android/arm64-v8a/libSDL2.so
CLIENT_EXTRA_FILES += $(LIBSDIR)/android/arm64-v8a/libSDL2.so
# VrApi
BASE_CFLAGS += -I$(VRAPIDIR)/Include
CLIENT_LIBS += $(VRAPIDIR)/Libs/Android/arm64-v8a/Release/libvrapi.so
RENDERER_LIBS += $(VRAPIDIR)/Libs/Android/arm64-v8a/Release/libvrapi.so
CLIENT_EXTRA_FILES += $(VRAPIDIR)/Libs/Android/arm64-v8a/Release/libvrapi.so
else # ifeq Android
#############################################################################
# SETUP AND BUILD -- MAC OS X
#############################################################################
@ -954,6 +1005,7 @@ else # ifeq sunos
SHLIBLDFLAGS=-shared
endif #Linux
endif #Android
endif #darwin
endif #MINGW
endif #FreeBSD
@ -991,9 +1043,9 @@ endif
ifneq ($(BUILD_CLIENT),0)
ifneq ($(USE_RENDERER_DLOPEN),0)
TARGETS += $(B)/$(CLIENTBIN)$(FULLBINEXT) $(B)/renderer_opengl1_$(SHLIBNAME)
TARGETS += $(B)/$(CLIENTBIN)$(FULLBINEXT) $(B)/$(LIBPREFIX)renderer_opengl1_$(SHLIBNAME)
ifneq ($(BUILD_RENDERER_OPENGL2),0)
TARGETS += $(B)/renderer_opengl2_$(SHLIBNAME)
TARGETS += $(B)/$(LIBPREFIX)renderer_opengl2_$(SHLIBNAME)
endif
else
TARGETS += $(B)/$(CLIENTBIN)$(FULLBINEXT)
@ -1223,7 +1275,7 @@ define DO_REF_STR
$(echo_cmd) "REF_STR $<"
$(Q)rm -f $@
$(Q)echo "const char *fallbackShader_$(notdir $(basename $<)) =" >> $@
$(Q)cat $< | sed -e 's/^\(.*\)$$/\"\1\"/' >> $@
$(Q)cat $< | sed -e 's/^/\"/;s/$$/\\n\"/' | tr -d '\r' >> $@
$(Q)echo ";" >> $@
endef
@ -1759,6 +1811,10 @@ Q3OBJ = \
$(B)/client/sdl_input.o \
$(B)/client/sdl_snd.o \
\
$(B)/client/vr_base.o \
$(B)/client/vr_input.o \
$(B)/client/vr_renderer.o \
\
$(B)/client/con_log.o \
$(B)/client/sys_autoupdater.o \
$(B)/client/sys_main.o
@ -2191,12 +2247,12 @@ $(B)/$(CLIENTBIN)$(FULLBINEXT): $(Q3OBJ) $(LIBSDLMAIN)
-o $@ $(Q3OBJ) \
$(LIBSDLMAIN) $(CLIENT_LIBS) $(LIBS)
$(B)/renderer_opengl1_$(SHLIBNAME): $(Q3ROBJ) $(JPGOBJ)
$(B)/$(LIBPREFIX)renderer_opengl1_$(SHLIBNAME): $(Q3ROBJ) $(JPGOBJ)
$(echo_cmd) "LD $@"
$(Q)$(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(Q3ROBJ) $(JPGOBJ) \
$(THREAD_LIBS) $(LIBSDLMAIN) $(RENDERER_LIBS) $(LIBS)
$(B)/renderer_opengl2_$(SHLIBNAME): $(Q3R2OBJ) $(Q3R2STRINGOBJ) $(JPGOBJ)
$(B)/$(LIBPREFIX)renderer_opengl2_$(SHLIBNAME): $(Q3R2OBJ) $(Q3R2STRINGOBJ) $(JPGOBJ)
$(echo_cmd) "LD $@"
$(Q)$(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(Q3R2OBJ) $(Q3R2STRINGOBJ) $(JPGOBJ) \
$(THREAD_LIBS) $(LIBSDLMAIN) $(RENDERER_LIBS) $(LIBS)
@ -2697,6 +2753,9 @@ $(B)/client/%.o: $(ZDIR)/%.c
$(B)/client/%.o: $(SDLDIR)/%.c
$(DO_CC)
$(B)/client/%.o: $(VRDIR)/%.c
$(DO_CC)
$(B)/client/%.o: $(SYSDIR)/%.c
$(DO_CC)
@ -2882,9 +2941,9 @@ endif
ifneq ($(BUILD_CLIENT),0)
$(INSTALL) $(STRIP_FLAG) -m 0755 $(BR)/$(CLIENTBIN)$(FULLBINEXT) $(COPYBINDIR)/$(CLIENTBIN)$(FULLBINEXT)
ifneq ($(USE_RENDERER_DLOPEN),0)
$(INSTALL) $(STRIP_FLAG) -m 0755 $(BR)/renderer_opengl1_$(SHLIBNAME) $(COPYBINDIR)/renderer_opengl1_$(SHLIBNAME)
$(INSTALL) $(STRIP_FLAG) -m 0755 $(BR)/$(LIBPREFIX)renderer_opengl1_$(SHLIBNAME) $(COPYBINDIR)/$(LIBPREFIX)renderer_opengl1_$(SHLIBNAME)
ifneq ($(BUILD_RENDERER_OPENGL2),0)
$(INSTALL) $(STRIP_FLAG) -m 0755 $(BR)/renderer_opengl2_$(SHLIBNAME) $(COPYBINDIR)/renderer_opengl2_$(SHLIBNAME)
$(INSTALL) $(STRIP_FLAG) -m 0755 $(BR)/$(LIBPREFIX)renderer_opengl2_$(SHLIBNAME) $(COPYBINDIR)/$(LIBPREFIX)renderer_opengl2_$(SHLIBNAME)
endif
else
ifneq ($(BUILD_RENDERER_OPENGL2),0)

20
Makefile.local Normal file
View file

@ -0,0 +1,20 @@
PLATFORM=android
BUILD_CLIENT=1
BUILD_CLIENT_SMP=0
BUILD_GAME_QVM=0
BUILD_GAME_SO=1
BUILD_MISSIONPACK=0
BUILD_SERVER=0
BUILD_STANDALONE=1
GENERATE_DEPENDENCIES=0
USE_CODEC_VORBIS=0
USE_CURL=0
USE_CURL_DLOPEN=0
USE_INTERNAL_SPEEX=0
USE_LOCAL_HEADERS=0
USE_MUMBLE=0
USE_OPENAL=0
USE_OPENAL_DLOPEN=0
USE_RENDERER_DLOPEN=0
USE_SVN=0
USE_VOIP=0

44
android/app/build.gradle Normal file
View file

@ -0,0 +1,44 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
applicationId = 'com.spark.ioq3quest'
minSdkVersion 25
targetSdkVersion 26
versionCode 1
versionName "1.0"
externalNativeBuild {
cmake {
arguments '-DANDROID_STL=c++_static'
}
}
ndk {
abiFilters 'arm64-v8a'
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
path 'src/main/cpp/CMakeLists.txt'
version '3.10.2'
}
}
sourceSets {
main {
jniLibs.srcDirs 'src/main/jniLibs'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
}

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.spark.ioq3quest"
android:installLocation="preferExternal"
android:versionCode="1"
android:versionName="1.0">
<uses-feature android:name="android.hardware.vr.headtracking" android:version="1" android:required="true" />
<uses-permission android:name="oculus.permission.handtracking" />
<uses-feature android:name="oculus.software.handtracking" android:required="false" />
<uses-feature android:glEsVersion="0x00030001" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:allowBackup="false"
android:fullBackupContent="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:hasCode="true">
<meta-data android:name="com.samsung.android.vr.application.mode" android:value="vr_only"/>
<activity android:name="com.spark.ioq3quest.MainActivity"
android:label="@string/app_name"
android:screenOrientation="landscape"
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
android:configChanges="density|keyboard|keyboardHidden|navigation|orientation|screenLayout|screenSize|uiMode"
android:launchMode="singleTask"
android:resizeableActivity="false">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View file

@ -0,0 +1,41 @@
cmake_minimum_required(VERSION 3.4.1)
set(${CMAKE_C_FLAGS}, "${CMAKE_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11 -Wall -Werror")
link_directories(../jniLibs/arm64-v8a)
add_library(main SHARED main.cpp)
SET(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "Configs" FORCE)
SET(BUILD_FOLDER release-android-aarch64)
if(CMAKE_BUILD_TYPE MATCHES "Debug")
SET(BUILD_FOLDER debug-android-aarch64)
endif()
add_custom_target(copy_libs
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/../../../../../build/${BUILD_FOLDER}/libioquake3_opengl2.so ${CMAKE_SOURCE_DIR}/../jniLibs/arm64-v8a/libioquake3_opengl2.so
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/../../../../../build/${BUILD_FOLDER}/libSDL2.so ${CMAKE_SOURCE_DIR}/../jniLibs/arm64-v8a/libSDL2.so
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/../../../../../build/${BUILD_FOLDER}/libvrapi.so ${CMAKE_SOURCE_DIR}/../jniLibs/arm64-v8a/libvrapi.so
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/../../../../../build/${BUILD_FOLDER}/baseq3/cgameaarch64.so ${CMAKE_SOURCE_DIR}/../jniLibs/arm64-v8a/libcgameaarch64.so
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/../../../../../build/${BUILD_FOLDER}/baseq3/qagameaarch64.so ${CMAKE_SOURCE_DIR}/../jniLibs/arm64-v8a/libqagameaarch64.so
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/../../../../../build/${BUILD_FOLDER}/baseq3/uiaarch64.so ${CMAKE_SOURCE_DIR}/../jniLibs/arm64-v8a/libuiaarch64.so
)
add_dependencies(main copy_libs)
target_include_directories(main PRIVATE
${CMAKE_SOURCE_DIR}/../../../../../code/VrApi/Include
${CMAKE_SOURCE_DIR}/../../../../../code/SDL2/include
${CMAKE_SOURCE_DIR}/../../../../../code)
target_compile_definitions(main PRIVATE
ARCH_STRING=android)
find_library(log-lib log)
target_link_libraries(main
android
ioquake3_opengl2
SDL2
vrapi
${log-lib})

View file

@ -0,0 +1,127 @@
#include <jni.h>
#include <memory.h>
#include <string.h>
#include <android/log.h>
#include <VrApi.h>
#include <VrApi_Helpers.h>
#include <string>
extern "C" {
#include <qcommon/q_shared.h>
#include <qcommon/qcommon.h>
#include <vr/vr_base.h>
#include <vr/vr_renderer.h>
#include <SDL.h>
extern void CON_LogcatFn( void (*LogcatFn)( const char* message ) );
}
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "Quake3", __VA_ARGS__))
#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "Quake3", __VA_ARGS__))
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "Quake3", __VA_ARGS__))
#define LOGF(...) ((void)__android_log_print(ANDROID_LOG_FATAL, "Quake3", __VA_ARGS__))
static JNIEnv* g_Env = NULL;
static JavaVM* g_JavaVM = NULL;
static jobject g_ActivityObject = NULL;
extern "C"
{
JNIEXPORT void JNICALL Java_com_spark_ioq3quest_MainActivity_nativeCreate(JNIEnv* env, jclass cls, jobject thisObject)
{
g_ActivityObject = env->NewGlobalRef(thisObject);
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
g_JavaVM = vm;
if (g_JavaVM->GetEnv((void**) &g_Env, JNI_VERSION_1_4) != JNI_OK) {
return -1;
}
return JNI_VERSION_1_4;
}
}
static void ioq3_logfn(const char* msg)
{
LOGI("%s", msg);
}
static ovrJava engine_get_ovrJava() {
ovrJava java;
java.Vm = g_JavaVM;
java.ActivityObject = g_ActivityObject;
java.Vm->AttachCurrentThread(&java.Env, NULL);
return java;
}
int main(int argc, char* argv[]) {
CON_LogcatFn(&ioq3_logfn);
std::string defaultArgs("+set fs_basepath ");
defaultArgs += SDL_AndroidGetExternalStoragePath();
defaultArgs += " +set fs_game baseq3 +map q3dm6";
char* args = new char[defaultArgs.length() + 1];
args[defaultArgs.length()] = '\0';
memcpy(args, defaultArgs.c_str(), defaultArgs.length());
Com_Init(args);
ovrJava java = engine_get_ovrJava();
constexpr auto vrEnabled = true;
engine_t* engine = nullptr;
if (vrEnabled)
{
engine = VR_Init(java);
VR_InitRenderer(engine);
VR_EnterVR(engine, java);
}
while (1) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
LOGI("Received SDL Event: %d", event.type);
if (vrEnabled)
{
switch (event.type)
{
case SDL_WINDOWEVENT_FOCUS_GAINED:
VR_EnterVR(engine, engine_get_ovrJava());
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
VR_LeaveVR(engine);
break;
}
}
}
if (vrEnabled)
{
VR_DrawFrame(engine);
}
else
{
Com_Frame();
}
}
if (vrEnabled)
{
VR_LeaveVR(engine);
VR_DestroyRenderer(engine);
}
Com_Shutdown();
if (vrEnabled)
{
VR_Destroy(engine);
}
delete [] args;
return 0;
}

View file

@ -0,0 +1,19 @@
package com.spark.ioq3quest;
import android.os.Bundle;
import org.libsdl.app.SDLActivity;
public class MainActivity extends SDLActivity
{
@Override
protected void onCreate(Bundle savedInstanceState) {
nativeCreate(this);
super.onCreate(savedInstanceState);
}
public static native void nativeCreate(MainActivity thisObject);
static {
System.loadLibrary("main");
}
}

View file

@ -0,0 +1,37 @@
package org.libsdl.app;
import android.content.Context;
/**
SDL library initialization
*/
public class SDL {
// This function should be called first and sets up the native code
// so it can call into the Java classes
public static void setupJNI() {
SDLActivity.nativeSetupJNI();
SDLAudioManager.nativeSetupJNI();
SDLControllerManager.nativeSetupJNI();
}
// This function should be called each time the activity is started
public static void initialize() {
setContext(null);
SDLActivity.initialize();
SDLAudioManager.initialize();
SDLControllerManager.initialize();
}
// This function stores the current activity (SDL or not)
public static void setContext(Context context) {
mContext = context;
}
public static Context getContext() {
return mContext;
}
protected static Context mContext;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,178 @@
package org.libsdl.app;
import android.media.*;
import android.util.Log;
public class SDLAudioManager
{
protected static final String TAG = "SDLAudio";
protected static AudioTrack mAudioTrack;
protected static AudioRecord mAudioRecord;
public static void initialize() {
mAudioTrack = null;
mAudioRecord = null;
}
// Audio
/**
* This method is called by SDL using JNI.
*/
public static int audioOpen(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO;
int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
Log.v(TAG, "SDL audio: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + (sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer");
// Let the user pick a larger buffer if they really want -- but ye
// gods they probably shouldn't, the minimums are horrifyingly high
// latency already
desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
if (mAudioTrack == null) {
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
// Instantiating AudioTrack can "succeed" without an exception and the track may still be invalid
// Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java
// Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState()
if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
Log.e(TAG, "Failed during initialization of Audio Track");
mAudioTrack = null;
return -1;
}
mAudioTrack.play();
}
Log.v(TAG, "SDL audio: got " + ((mAudioTrack.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioTrack.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + (mAudioTrack.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer");
return 0;
}
/**
* This method is called by SDL using JNI.
*/
public static void audioWriteShortBuffer(short[] buffer) {
if (mAudioTrack == null) {
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
return;
}
for (int i = 0; i < buffer.length; ) {
int result = mAudioTrack.write(buffer, i, buffer.length - i);
if (result > 0) {
i += result;
} else if (result == 0) {
try {
Thread.sleep(1);
} catch(InterruptedException e) {
// Nom nom
}
} else {
Log.w(TAG, "SDL audio: error return from write(short)");
return;
}
}
}
/**
* This method is called by SDL using JNI.
*/
public static void audioWriteByteBuffer(byte[] buffer) {
if (mAudioTrack == null) {
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
return;
}
for (int i = 0; i < buffer.length; ) {
int result = mAudioTrack.write(buffer, i, buffer.length - i);
if (result > 0) {
i += result;
} else if (result == 0) {
try {
Thread.sleep(1);
} catch(InterruptedException e) {
// Nom nom
}
} else {
Log.w(TAG, "SDL audio: error return from write(byte)");
return;
}
}
}
/**
* This method is called by SDL using JNI.
*/
public static int captureOpen(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO;
int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
Log.v(TAG, "SDL capture: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + (sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer");
// Let the user pick a larger buffer if they really want -- but ye
// gods they probably shouldn't, the minimums are horrifyingly high
// latency already
desiredFrames = Math.max(desiredFrames, (AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
if (mAudioRecord == null) {
mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate,
channelConfig, audioFormat, desiredFrames * frameSize);
// see notes about AudioTrack state in audioOpen(), above. Probably also applies here.
if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
Log.e(TAG, "Failed during initialization of AudioRecord");
mAudioRecord.release();
mAudioRecord = null;
return -1;
}
mAudioRecord.startRecording();
}
Log.v(TAG, "SDL capture: got " + ((mAudioRecord.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioRecord.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + (mAudioRecord.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer");
return 0;
}
/** This method is called by SDL using JNI. */
public static int captureReadShortBuffer(short[] buffer, boolean blocking) {
// !!! FIXME: this is available in API Level 23. Until then, we always block. :(
//return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
return mAudioRecord.read(buffer, 0, buffer.length);
}
/** This method is called by SDL using JNI. */
public static int captureReadByteBuffer(byte[] buffer, boolean blocking) {
// !!! FIXME: this is available in API Level 23. Until then, we always block. :(
//return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
return mAudioRecord.read(buffer, 0, buffer.length);
}
/** This method is called by SDL using JNI. */
public static void audioClose() {
if (mAudioTrack != null) {
mAudioTrack.stop();
mAudioTrack.release();
mAudioTrack = null;
}
}
/** This method is called by SDL using JNI. */
public static void captureClose() {
if (mAudioRecord != null) {
mAudioRecord.stop();
mAudioRecord.release();
mAudioRecord = null;
}
}
public static native int nativeSetupJNI();
}

View file

@ -0,0 +1,435 @@
package org.libsdl.app;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import android.content.Context;
import android.os.*;
import android.view.*;
import android.util.Log;
public class SDLControllerManager
{
public static native int nativeSetupJNI();
public static native int nativeAddJoystick(int device_id, String name, String desc,
int is_accelerometer, int nbuttons,
int naxes, int nhats, int nballs);
public static native int nativeRemoveJoystick(int device_id);
public static native int nativeAddHaptic(int device_id, String name);
public static native int nativeRemoveHaptic(int device_id);
public static native int onNativePadDown(int device_id, int keycode);
public static native int onNativePadUp(int device_id, int keycode);
public static native void onNativeJoy(int device_id, int axis,
float value);
public static native void onNativeHat(int device_id, int hat_id,
int x, int y);
protected static SDLJoystickHandler mJoystickHandler;
protected static SDLHapticHandler mHapticHandler;
private static final String TAG = "SDLControllerManager";
public static void initialize() {
mJoystickHandler = null;
mHapticHandler = null;
SDLControllerManager.setup();
}
public static void setup() {
if (Build.VERSION.SDK_INT >= 16) {
mJoystickHandler = new SDLJoystickHandler_API16();
} else if (Build.VERSION.SDK_INT >= 12) {
mJoystickHandler = new SDLJoystickHandler_API12();
} else {
mJoystickHandler = new SDLJoystickHandler();
}
mHapticHandler = new SDLHapticHandler();
}
// Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance
public static boolean handleJoystickMotionEvent(MotionEvent event) {
return mJoystickHandler.handleMotionEvent(event);
}
/**
* This method is called by SDL using JNI.
*/
public static void pollInputDevices() {
mJoystickHandler.pollInputDevices();
}
/**
* This method is called by SDL using JNI.
*/
public static void pollHapticDevices() {
mHapticHandler.pollHapticDevices();
}
/**
* This method is called by SDL using JNI.
*/
public static void hapticRun(int device_id, int length) {
mHapticHandler.run(device_id, length);
}
// Check if a given device is considered a possible SDL joystick
public static boolean isDeviceSDLJoystick(int deviceId) {
InputDevice device = InputDevice.getDevice(deviceId);
// We cannot use InputDevice.isVirtual before API 16, so let's accept
// only nonnegative device ids (VIRTUAL_KEYBOARD equals -1)
if ((device == null) || (deviceId < 0)) {
return false;
}
int sources = device.getSources();
/* This is called for every button press, so let's not spam the logs */
/**
if ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) == InputDevice.SOURCE_CLASS_JOYSTICK) {
Log.v(TAG, "Input device " + device.getName() + " is a joystick.");
}
if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) {
Log.v(TAG, "Input device " + device.getName() + " is a dpad.");
}
if ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
Log.v(TAG, "Input device " + device.getName() + " is a gamepad.");
}
**/
return (((sources & InputDevice.SOURCE_CLASS_JOYSTICK) == InputDevice.SOURCE_CLASS_JOYSTICK) ||
((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) ||
((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
);
}
}
/* A null joystick handler for API level < 12 devices (the accelerometer is handled separately) */
class SDLJoystickHandler {
/**
* Handles given MotionEvent.
* @param event the event to be handled.
* @return if given event was processed.
*/
public boolean handleMotionEvent(MotionEvent event) {
return false;
}
/**
* Handles adding and removing of input devices.
*/
public void pollInputDevices() {
}
}
/* Actual joystick functionality available for API >= 12 devices */
class SDLJoystickHandler_API12 extends SDLJoystickHandler {
static class SDLJoystick {
public int device_id;
public String name;
public String desc;
public ArrayList<InputDevice.MotionRange> axes;
public ArrayList<InputDevice.MotionRange> hats;
}
static class RangeComparator implements Comparator<InputDevice.MotionRange> {
@Override
public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) {
return arg0.getAxis() - arg1.getAxis();
}
}
private ArrayList<SDLJoystick> mJoysticks;
public SDLJoystickHandler_API12() {
mJoysticks = new ArrayList<SDLJoystick>();
}
@Override
public void pollInputDevices() {
int[] deviceIds = InputDevice.getDeviceIds();
// It helps processing the device ids in reverse order
// For example, in the case of the XBox 360 wireless dongle,
// so the first controller seen by SDL matches what the receiver
// considers to be the first controller
for(int i=deviceIds.length-1; i>-1; i--) {
SDLJoystick joystick = getJoystick(deviceIds[i]);
if (joystick == null) {
joystick = new SDLJoystick();
InputDevice joystickDevice = InputDevice.getDevice(deviceIds[i]);
if (SDLControllerManager.isDeviceSDLJoystick(deviceIds[i])) {
joystick.device_id = deviceIds[i];
joystick.name = joystickDevice.getName();
joystick.desc = getJoystickDescriptor(joystickDevice);
joystick.axes = new ArrayList<InputDevice.MotionRange>();
joystick.hats = new ArrayList<InputDevice.MotionRange>();
List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
Collections.sort(ranges, new RangeComparator());
for (InputDevice.MotionRange range : ranges ) {
if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
if (range.getAxis() == MotionEvent.AXIS_HAT_X ||
range.getAxis() == MotionEvent.AXIS_HAT_Y) {
joystick.hats.add(range);
}
else {
joystick.axes.add(range);
}
}
}
mJoysticks.add(joystick);
SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc, 0, -1,
joystick.axes.size(), joystick.hats.size()/2, 0);
}
}
}
/* Check removed devices */
ArrayList<Integer> removedDevices = new ArrayList<Integer>();
for(int i=0; i < mJoysticks.size(); i++) {
int device_id = mJoysticks.get(i).device_id;
int j;
for (j=0; j < deviceIds.length; j++) {
if (device_id == deviceIds[j]) break;
}
if (j == deviceIds.length) {
removedDevices.add(Integer.valueOf(device_id));
}
}
for(int i=0; i < removedDevices.size(); i++) {
int device_id = removedDevices.get(i).intValue();
SDLControllerManager.nativeRemoveJoystick(device_id);
for (int j=0; j < mJoysticks.size(); j++) {
if (mJoysticks.get(j).device_id == device_id) {
mJoysticks.remove(j);
break;
}
}
}
}
protected SDLJoystick getJoystick(int device_id) {
for(int i=0; i < mJoysticks.size(); i++) {
if (mJoysticks.get(i).device_id == device_id) {
return mJoysticks.get(i);
}
}
return null;
}
@Override
public boolean handleMotionEvent(MotionEvent event) {
if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) {
int actionPointerIndex = event.getActionIndex();
int action = event.getActionMasked();
switch(action) {
case MotionEvent.ACTION_MOVE:
SDLJoystick joystick = getJoystick(event.getDeviceId());
if ( joystick != null ) {
for (int i = 0; i < joystick.axes.size(); i++) {
InputDevice.MotionRange range = joystick.axes.get(i);
/* Normalize the value to -1...1 */
float value = ( event.getAxisValue( range.getAxis(), actionPointerIndex) - range.getMin() ) / range.getRange() * 2.0f - 1.0f;
SDLControllerManager.onNativeJoy(joystick.device_id, i, value );
}
for (int i = 0; i < joystick.hats.size(); i+=2) {
int hatX = Math.round(event.getAxisValue( joystick.hats.get(i).getAxis(), actionPointerIndex ) );
int hatY = Math.round(event.getAxisValue( joystick.hats.get(i+1).getAxis(), actionPointerIndex ) );
SDLControllerManager.onNativeHat(joystick.device_id, i/2, hatX, hatY );
}
}
break;
default:
break;
}
}
return true;
}
public String getJoystickDescriptor(InputDevice joystickDevice) {
return joystickDevice.getName();
}
}
class SDLJoystickHandler_API16 extends SDLJoystickHandler_API12 {
@Override
public String getJoystickDescriptor(InputDevice joystickDevice) {
String desc = joystickDevice.getDescriptor();
if (desc != null && !desc.isEmpty()) {
return desc;
}
return super.getJoystickDescriptor(joystickDevice);
}
}
class SDLHapticHandler {
class SDLHaptic {
public int device_id;
public String name;
public Vibrator vib;
}
private ArrayList<SDLHaptic> mHaptics;
public SDLHapticHandler() {
mHaptics = new ArrayList<SDLHaptic>();
}
public void run(int device_id, int length) {
SDLHaptic haptic = getHaptic(device_id);
if (haptic != null) {
haptic.vib.vibrate (length);
}
}
public void pollHapticDevices() {
final int deviceId_VIBRATOR_SERVICE = 999999;
boolean hasVibratorService = false;
int[] deviceIds = InputDevice.getDeviceIds();
// It helps processing the device ids in reverse order
// For example, in the case of the XBox 360 wireless dongle,
// so the first controller seen by SDL matches what the receiver
// considers to be the first controller
if (Build.VERSION.SDK_INT >= 16)
{
for (int i = deviceIds.length - 1; i > -1; i--) {
SDLHaptic haptic = getHaptic(deviceIds[i]);
if (haptic == null) {
InputDevice device = InputDevice.getDevice(deviceIds[i]);
Vibrator vib = device.getVibrator();
if (vib.hasVibrator()) {
haptic = new SDLHaptic();
haptic.device_id = deviceIds[i];
haptic.name = device.getName();
haptic.vib = vib;
mHaptics.add(haptic);
SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
}
}
}
}
/* Check VIBRATOR_SERVICE */
Vibrator vib = (Vibrator) SDL.getContext().getSystemService(Context.VIBRATOR_SERVICE);
if (vib != null) {
if (Build.VERSION.SDK_INT >= 11) {
hasVibratorService = vib.hasVibrator();
} else {
hasVibratorService = true;
}
if (hasVibratorService) {
SDLHaptic haptic = getHaptic(deviceId_VIBRATOR_SERVICE);
if (haptic == null) {
haptic = new SDLHaptic();
haptic.device_id = deviceId_VIBRATOR_SERVICE;
haptic.name = "VIBRATOR_SERVICE";
haptic.vib = vib;
mHaptics.add(haptic);
SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
}
}
}
/* Check removed devices */
ArrayList<Integer> removedDevices = new ArrayList<Integer>();
for(int i=0; i < mHaptics.size(); i++) {
int device_id = mHaptics.get(i).device_id;
int j;
for (j=0; j < deviceIds.length; j++) {
if (device_id == deviceIds[j]) break;
}
if (device_id == deviceId_VIBRATOR_SERVICE && hasVibratorService) {
// don't remove the vibrator if it is still present
} else if (j == deviceIds.length) {
removedDevices.add(device_id);
}
}
for(int i=0; i < removedDevices.size(); i++) {
int device_id = removedDevices.get(i);
SDLControllerManager.nativeRemoveHaptic(device_id);
for (int j=0; j < mHaptics.size(); j++) {
if (mHaptics.get(j).device_id == device_id) {
mHaptics.remove(j);
break;
}
}
}
}
protected SDLHaptic getHaptic(int device_id) {
for(int i=0; i < mHaptics.size(); i++) {
if (mHaptics.get(i).device_id == device_id) {
return mHaptics.get(i);
}
}
return null;
}
}
class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener {
// Generic Motion (mouse hover, joystick...) events go here
@Override
public boolean onGenericMotion(View v, MotionEvent event) {
float x, y;
int action;
switch ( event.getSource() ) {
case InputDevice.SOURCE_JOYSTICK:
case InputDevice.SOURCE_GAMEPAD:
case InputDevice.SOURCE_DPAD:
return SDLControllerManager.handleJoystickMotionEvent(event);
case InputDevice.SOURCE_MOUSE:
if (!SDLActivity.mSeparateMouseAndTouch) {
break;
}
action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_SCROLL:
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
SDLActivity.onNativeMouse(0, action, x, y);
return true;
case MotionEvent.ACTION_HOVER_MOVE:
x = event.getX(0);
y = event.getY(0);
SDLActivity.onNativeMouse(0, action, x, y);
return true;
default:
break;
}
break;
default:
break;
}
// Event was not managed
return false;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">NativeActivity</string>
</resources>

14
android/autoexec.cfg Normal file
View file

@ -0,0 +1,14 @@
bind LEFTARROW +moveleft
bind RIGHTARROW +moveright
sensitivity 100
set in_joystick 1
set r_externalGLSL 1
set r_stereoEnabled 1
set r_mode -2
set cg_runpitch 0
set cg_runroll 0
set cg_bobup 0
set cg_bobpitch 0
set cg_bobroll 0
set cg_weaponbob 0
set sv_pure 0

21
android/build.gradle Normal file
View file

@ -0,0 +1,21 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.3'
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

20
android/gradle.properties Normal file
View file

@ -0,0 +1,20 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
android.enableJetifier=true
android.useAndroidX=true
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true

Binary file not shown.

View file

@ -0,0 +1,6 @@
#Fri May 22 15:31:25 CEST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip

164
android/gradlew vendored Executable file
View file

@ -0,0 +1,164 @@
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

90
android/gradlew.bat vendored Normal file
View file

@ -0,0 +1,90 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

3
android/make.sh Executable file
View file

@ -0,0 +1,3 @@
#!/bin/sh
./gradlew :app:assembleDebug

58
android/run.sh Executable file
View file

@ -0,0 +1,58 @@
#!/bin/sh
cd ..
TARGET=debug
make -j24 $TARGET
if [ $? -ne 0 ]; then
echo "Failed to build ioq3"
exit 1
fi
cd android
./make.sh
if [ $? -ne 0 ]; then
echo "Failed to build android project"
exit 1
fi
PACKAGE_NAME=com.spark.ioq3quest
ANDROID_STORAGE_LOCATION=/sdcard/Android/data/$PACKAGE_NAME/files/
APK_LOCATION=./app/build/outputs/apk/debug/app-debug.apk
adb install -r $APK_LOCATION
if [ $? -ne 0 ]; then
adb uninstall $PACKAGE_NAME
adb install $APK_LOCATION
if [ $? -ne 0 ]; then
echo "Failed to install apk."
exit 1
fi
fi
adb shell mkdir -p $ANDROID_STORAGE_LOCATION
adb push --sync ~/.local/share/Steam/steamapps/common/Quake\ 3\ Arena/baseq3 $ANDROID_STORAGE_LOCATION
if [ $? -ne 0 ]; then
echo "Failed to transfer files."
exit 1
fi
adb push --sync ../code/renderergl2/glsl $ANDROID_STORAGE_LOCATION/baseq3/
if [ $? -ne 0 ]; then
echo "Failed to transfer shaders."
exit 1
fi
adb push --sync autoexec.cfg $ANDROID_STORAGE_LOCATION/baseq3/
if [ $? -ne 0 ]; then
echo "Failed to transfer autoexec."
exit 1
fi
adb logcat -c
adb shell am start -n $PACKAGE_NAME/.MainActivity
if [ $? -ne 0 ]; then
echo "Failed to start application."
exit 1
fi
adb logcat *:S Quake3:V SDL:V DEBUG:V

2
android/settings.gradle Normal file
View file

@ -0,0 +1,2 @@
include ':app'

View file

@ -1089,6 +1089,7 @@ extern vmCvar_t cg_runroll;
extern vmCvar_t cg_bobup;
extern vmCvar_t cg_bobpitch;
extern vmCvar_t cg_bobroll;
extern vmCvar_t cg_weaponbob;
extern vmCvar_t cg_swingSpeed;
extern vmCvar_t cg_shadows;
extern vmCvar_t cg_gibs;

View file

@ -96,6 +96,7 @@ vmCvar_t cg_runroll;
vmCvar_t cg_bobup;
vmCvar_t cg_bobpitch;
vmCvar_t cg_bobroll;
vmCvar_t cg_weaponbob;
vmCvar_t cg_swingSpeed;
vmCvar_t cg_shadows;
vmCvar_t cg_gibs;
@ -246,6 +247,7 @@ static cvarTable_t cvarTable[] = {
{ &cg_bobup , "cg_bobup", "0.005", CVAR_CHEAT },
{ &cg_bobpitch, "cg_bobpitch", "0.002", CVAR_ARCHIVE },
{ &cg_bobroll, "cg_bobroll", "0.002", CVAR_ARCHIVE },
{ &cg_weaponbob, "cg_weaponbob", "1", CVAR_ARCHIVE },
{ &cg_swingSpeed, "cg_swingSpeed", "0.3", CVAR_CHEAT },
{ &cg_animSpeed, "cg_animspeed", "1", CVAR_CHEAT },
{ &cg_debugAnim, "cg_debuganim", "0", CVAR_CHEAT },

View file

@ -921,9 +921,12 @@ static void CG_CalculateWeaponPosition( vec3_t origin, vec3_t angles ) {
}
// gun angles from bobbing
angles[ROLL] += scale * cg.bobfracsin * 0.005;
angles[YAW] += scale * cg.bobfracsin * 0.01;
angles[PITCH] += cg.xyspeed * cg.bobfracsin * 0.005;
if (cg_weaponbob.value != 0)
{
angles[ROLL] += scale * cg.bobfracsin * 0.005;
angles[YAW] += scale * cg.bobfracsin * 0.01;
angles[PITCH] += cg.xyspeed * cg.bobfracsin * 0.005;
}
// drop the weapon when landing
delta = cg.time - cg.landTime;

Binary file not shown.

Binary file not shown.

View file

@ -1473,6 +1473,10 @@ int FS_FindVM(void **startSearch, char *found, int foundlen, const char *name, i
if(enableDll)
{
#ifdef __ANDROID__
Com_sprintf(found, foundlen, "lib%s", dllName);
return VMI_NATIVE;
#else
netpath = FS_BuildOSPath(dir->path, dir->gamedir, dllName);
if(FS_FileInPathExists(netpath))
@ -1482,6 +1486,7 @@ int FS_FindVM(void **startSearch, char *found, int foundlen, const char *name, i
return VMI_NATIVE;
}
#endif
}
if(FS_FOpenFileReadDir(qvmName, search, NULL, qfalse, qfalse) > 0)

View file

@ -575,6 +575,10 @@ it will attempt to load as a system dll
*/
vm_t *VM_Create( const char *module, intptr_t (*systemCalls)(intptr_t *),
vmInterpret_t interpret ) {
#if __ANDROID__
interpret = VMI_NATIVE;
#endif
vm_t *vm;
vmHeader_t *header;
int i, remaining, retval;

View file

@ -28,8 +28,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#ifdef USE_LOCAL_HEADERS
# include "SDL_opengl.h"
# include "SDL_opengles2.h"
#else
# include <SDL_opengl.h>
# include <SDL_opengles2.h>
#endif
extern void (APIENTRYP qglActiveTextureARB) (GLenum texture);
@ -80,7 +82,6 @@ extern void (APIENTRYP qglUnlockArraysEXT) (void);
GLE(void, TexParameterf, GLenum target, GLenum pname, GLfloat param) \
GLE(void, TexParameteri, GLenum target, GLenum pname, GLint param) \
GLE(void, TexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels) \
GLE(void, Translatef, GLfloat x, GLfloat y, GLfloat z) \
GLE(void, Viewport, GLint x, GLint y, GLsizei width, GLsizei height) \
// OpenGL 1.0/1.1 and OpenGL ES 1.x but not OpenGL 3.2 core profile
@ -167,6 +168,7 @@ extern void (APIENTRYP qglUnlockArraysEXT) (void);
GLE(void, DeleteProgram, GLuint program) \
GLE(void, DeleteShader, GLuint shader) \
GLE(void, DetachShader, GLuint program, GLuint shader) \
GLE(void, DrawBuffersEXT, GLsizei n, const GLenum *bufs) \
GLE(void, DisableVertexAttribArray, GLuint index) \
GLE(void, EnableVertexAttribArray, GLuint index) \
GLE(void, GetActiveUniform, GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name) \

File diff suppressed because it is too large Load diff

View file

@ -24,6 +24,18 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "tr_common.h"
static void* stb_malloc_impl(size_t size) { return ri.Malloc(size); }
static void* stb_realloc_impl(void* p, size_t newsize) { free(p); return ri.Malloc(newsize); }
static void stb_free_impl(void* p) { ri.Free(p); }
#define STB_IMAGE_IMPLEMENTATION
#define STBI_NO_STDIO
#define STBI_MALLOC(sz) stb_malloc_impl(sz)
#define STBI_REALLOC(p,newsz) stb_realloc_impl(p, newsz)
#define STBI_FREE(p) stb_free_impl(p)
#include "stb_image.h"
/*
* Include file for users of JPEG library.
* You will need to have included system headers that define at least
@ -80,6 +92,46 @@ static void R_JPGOutputMessage(j_common_ptr cinfo)
void R_LoadJPG(const char *filename, unsigned char **pic, int *width, int *height)
{
int len;
int channels;
union {
byte *b;
void *v;
} fbuffer;
len = ri.FS_ReadFile ( ( char * ) filename, &fbuffer.v);
if (!fbuffer.b || len < 0) {
return;
}
channels = 3;
unsigned char* loaded_data = stbi_load_from_memory(fbuffer.b, len, width, height, &channels, 0);
ri.FS_FreeFile (fbuffer.v);
if (loaded_data)
{
const size_t img_size = *width * *height;
*pic = ri.Malloc(img_size * 4);
size_t src_index = 0;
size_t tgt_index = 0;
for (size_t i = 0; i < img_size; ++i)
{
(*pic)[tgt_index++] = loaded_data[src_index++];
(*pic)[tgt_index++] = loaded_data[src_index++];
(*pic)[tgt_index++] = loaded_data[src_index++];
(*pic)[tgt_index++] = 255;
}
ri.Free(loaded_data);
}
else
{
*pic = NULL;
}
// NOTE (SB): Need to figure out what is wrong with the JPEG implementation on Android.
#if 0
/* This struct contains the JPEG decompression parameters and pointers to
* working space (which is allocated as needed by the JPEG library).
*/
@ -264,6 +316,7 @@ void R_LoadJPG(const char *filename, unsigned char **pic, int *width, int *heigh
*/
/* And we're done! */
#endif
}

View file

@ -24,6 +24,13 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "tr_types.h"
#if __ANDROID__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wstrict-prototypes"
#include <VrApi.h>
#pragma clang diagnostic pop
#endif
#define REF_API_VERSION 8
//
@ -82,6 +89,9 @@ typedef struct {
// if the pointers are not NULL, timing info will be returned
void (*EndFrame)( int *frontEndMsec, int *backEndMsec );
#if __ANDROID__
void (*SetVRHeadsetParms)( const ovrTracking2* ovrTracking, int renderBufferL, int renderBufferR );
#endif
int (*MarkFragments)( int numPoints, const vec3_t *points, const vec3_t projection,
int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer );

View file

@ -707,7 +707,9 @@ void RB_DrawSun( float scale, shader_t *shader ) {
}
qglLoadMatrixf( backEnd.viewParms.world.modelMatrix );
#ifndef __ANDROID__
qglTranslatef (backEnd.viewParms.or.origin[0], backEnd.viewParms.or.origin[1], backEnd.viewParms.or.origin[2]);
#endif
dist = backEnd.viewParms.zFar / 1.75; // div sqrt(3)
size = dist * scale;
@ -770,7 +772,9 @@ void RB_StageIteratorSky( void ) {
qglPushMatrix ();
GL_State( 0 );
GL_Cull( CT_FRONT_SIDED );
#ifndef __ANDROID__
qglTranslatef (backEnd.viewParms.or.origin[0], backEnd.viewParms.or.origin[1], backEnd.viewParms.or.origin[2]);
#endif
DrawSkyBox( tess.shader );

View file

@ -1,3 +1,5 @@
precision mediump sampler2D;
uniform sampler2D u_DiffuseMap;
#if defined(USE_LIGHTMAP)

View file

@ -1,3 +1,4 @@
precision mediump sampler2D;
uniform sampler2D u_ShadowMap;
uniform vec3 u_LightForward;

View file

@ -1,3 +1,6 @@
precision mediump sampler2D;
precision mediump sampler2DShadow;
uniform sampler2D u_ScreenDepthMap;
uniform sampler2DShadow u_ShadowMap;

View file

@ -274,9 +274,21 @@ void GL_SetProjectionMatrix(mat4_t matrix)
}
void GL_SetModelviewMatrix(mat4_t matrix)
void GL_SetModelviewMatrix(mat4_t matrix, qboolean applyStereoView)
{
Mat4Copy(matrix, glState.modelview);
if (applyStereoView)
{
if (tr.refdef.stereoFrame == STEREO_LEFT) {
Mat4Multiply(tr.vrParms.viewL, matrix, glState.modelview);
} else if (tr.refdef.stereoFrame == STEREO_RIGHT) {
Mat4Multiply(tr.vrParms.viewR, matrix, glState.modelview);
} else {
Mat4Copy(matrix, glState.modelview);
}
} else {
Mat4Copy(matrix, glState.modelview);
}
Mat4Multiply(glState.projection, glState.modelview, glState.modelviewProjection);
}
@ -412,7 +424,7 @@ void RB_BeginDrawingView (void) {
plane2[2] = DotProduct (backEnd.viewParms.or.axis[2], plane);
plane2[3] = DotProduct (plane, backEnd.viewParms.or.origin) - plane[3];
#endif
GL_SetModelviewMatrix( s_flipMatrix );
GL_SetModelviewMatrix( s_flipMatrix, qtrue );
}
}
@ -531,7 +543,7 @@ void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) {
R_TransformDlights( backEnd.refdef.num_dlights, backEnd.refdef.dlights, &backEnd.or );
}
GL_SetModelviewMatrix( backEnd.or.modelMatrix );
GL_SetModelviewMatrix( backEnd.or.modelMatrix, qtrue );
//
// change depthrange. Also change projection matrix so first person weapon does not look like coming
@ -561,8 +573,13 @@ void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) {
}
}
#ifdef __ANDROID__
if(!oldDepthRange)
glDepthRangef(0.0f, 0.3f);
#else
if(!oldDepthRange)
qglDepthRange (0, 0.3);
#endif
}
else
{
@ -571,7 +588,11 @@ void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) {
GL_SetProjectionMatrix( backEnd.viewParms.projectionMatrix );
}
#ifdef __ANDROID__
glDepthRangef(0.0f, 1.0f);
#else
qglDepthRange (0, 1);
#endif
}
oldDepthRange = depthRange;
@ -597,9 +618,13 @@ void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) {
// go back to the world modelview matrix
GL_SetModelviewMatrix( backEnd.viewParms.world.modelMatrix );
GL_SetModelviewMatrix( backEnd.viewParms.world.modelMatrix, qtrue );
#ifdef __ANDROID__
glDepthRangef(0, 1);
#else
qglDepthRange (0, 1);
#endif
}
@ -645,7 +670,7 @@ void RB_SetGL2D (void) {
Mat4Ortho(0, width, height, 0, 0, 1, matrix);
GL_SetProjectionMatrix(matrix);
Mat4Identity(matrix);
GL_SetModelviewMatrix(matrix);
GL_SetModelviewMatrix(matrix, false);
GL_State( GLS_DEPTHTEST_DISABLE |
GLS_SRCBLEND_SRC_ALPHA |
@ -1176,7 +1201,6 @@ const void *RB_DrawSurfs( const void *data ) {
return (const void *)(cmd + 1);
}
/*
=============
RB_DrawBuffer
@ -1195,7 +1219,11 @@ const void *RB_DrawBuffer( const void *data ) {
if (glRefConfig.framebufferObject)
FBO_Bind(NULL);
#ifdef __ANDROID__
qglDrawBuffersEXT( 1, (const GLenum*)&cmd->buffer );
#else
qglDrawBuffer( cmd->buffer );
#endif
// clear screen for debugging
if ( r_clear->integer ) {
@ -1460,7 +1488,7 @@ const void *RB_PostProcess(const void *data)
if(tess.numIndexes)
RB_EndSurface();
if (!glRefConfig.framebufferObject || !r_postProcess->integer)
if (__ANDROID__ || !glRefConfig.framebufferObject || !r_postProcess->integer)
{
// do nothing
return (const void *)(cmd + 1);
@ -1728,6 +1756,28 @@ const void *RB_ExportCubemaps(const void *data)
return (const void *)(cmd + 1);
}
/*
====================
RB_SwitchEye
====================
*/
const void* RB_SwitchEye( const void* data ) {
const switchEyeCommand_t *cmd = data;
tr.renderFbo->frameBuffer = cmd->eye;
// Not calling FBO_Bind explicitly, since we just update the frameBuffer
// within the struct.
if (tr.renderFbo == glState.currentFBO)
{
GL_BindFramebuffer(GL_FRAMEBUFFER, tr.renderFbo->frameBuffer);
glState.currentFBO = tr.renderFbo;
}
tr.refdef.stereoFrame = cmd->stereoFrame;
return (const void*)(cmd + 1);
}
/*
====================
@ -1779,6 +1829,9 @@ void RB_ExecuteRenderCommands( const void *data ) {
case RC_EXPORT_CUBEMAPS:
data = RB_ExportCubemaps(data);
break;
case RC_SWITCH_EYE:
data = RB_SwitchEye(data);
break;
case RC_END_OF_LIST:
default:
// finish any 2D drawing if needed

View file

@ -411,15 +411,32 @@ void RE_BeginFrame( stereoFrame_t stereoFrame ) {
}
if (glConfig.stereoEnabled) {
if( !(cmd = R_GetCommandBuffer(sizeof(*cmd))) )
return;
cmd->commandId = RC_DRAW_BUFFER;
if (tr.renderFbo && tr.vrParms.renderBufferOriginal == 0) {
tr.vrParms.renderBufferOriginal = tr.renderFbo->frameBuffer;
}
if ( stereoFrame == STEREO_LEFT ) {
cmd->buffer = (int)GL_BACK_LEFT;
if (tr.vrParms.valid == qtrue) {
if (tr.renderFbo) {
switchEyeCommand_t* sec;
if (!(sec = R_GetCommandBuffer(sizeof(*sec))))
return;
sec->commandId = RC_SWITCH_EYE;
sec->eye = tr.vrParms.renderBufferL;
sec->stereoFrame = stereoFrame;
}
}
} else if ( stereoFrame == STEREO_RIGHT ) {
cmd->buffer = (int)GL_BACK_RIGHT;
if (tr.vrParms.valid == qtrue) {
if (tr.renderFbo) {
switchEyeCommand_t* sec;
if (!(sec = R_GetCommandBuffer(sizeof(*sec))))
return;
sec->commandId = RC_SWITCH_EYE;
sec->eye = tr.vrParms.renderBufferR;
sec->stereoFrame = stereoFrame;
}
}
} else {
ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is enabled, but stereoFrame was %i", stereoFrame );
}
@ -556,6 +573,41 @@ void RE_EndFrame( int *frontEndMsec, int *backEndMsec ) {
backEnd.pc.msec = 0;
}
#if __ANDROID__
void R_Mat4Transpose( const float in[4][4], float* out ) {
int i, j;
for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j) {
out[i * 4 + j] = in[j][i];
}
}
}
void RE_SetVRHeadsetParms( const ovrTracking2* tracking, int renderBufferL, int renderBufferR ) {
if (tracking) {
R_Mat4Transpose(tracking->Eye[0].ProjectionMatrix.M, tr.vrParms.projectionL);
R_Mat4Transpose(tracking->Eye[1].ProjectionMatrix.M, tr.vrParms.projectionR);
R_Mat4Transpose(tracking->Eye[0].ViewMatrix.M, tr.vrParms.viewL);
R_Mat4Transpose(tracking->Eye[1].ViewMatrix.M, tr.vrParms.viewR);
const float worldToMeter = 25.0f; // https://quakewiki.org/wiki/unit, assume 25 units is 1 meter.
tr.vrParms.viewL[12] *= worldToMeter;
tr.vrParms.viewL[13] *= worldToMeter;
tr.vrParms.viewL[14] *= worldToMeter;
tr.vrParms.viewR[12] *= worldToMeter;
tr.vrParms.viewR[13] *= worldToMeter;
tr.vrParms.viewR[14] *= worldToMeter;
tr.vrParms.renderBufferL = renderBufferL;
tr.vrParms.renderBufferR = renderBufferR;
tr.vrParms.valid = qtrue;
} else {
tr.vrParms.valid = qfalse;
}
}
#endif
/*
=============
RE_TakeVideoFrame

View file

@ -58,7 +58,11 @@ void GLimp_InitExtraExtensions(void)
QGL_ARB_occlusion_query_PROCS;
// OpenGL 3.0 - GL_ARB_framebuffer_object
#ifdef __ANDROID__
extension = "GL_OES_framebuffer_object";
#else
extension = "GL_ARB_framebuffer_object";
#endif
glRefConfig.framebufferObject = qfalse;
glRefConfig.framebufferBlit = qfalse;
glRefConfig.framebufferMultisample = qfalse;
@ -81,7 +85,11 @@ void GLimp_InitExtraExtensions(void)
}
// OpenGL 3.0 - GL_ARB_vertex_array_object
#ifdef __ANDROID__
extension = "GL_OES_vertex_array_object";
#else
extension = "GL_ARB_vertex_array_object";
#endif
glRefConfig.vertexArrayObject = qfalse;
if (q_gl_version_at_least_3_0 || SDL_GL_ExtensionSupported(extension))
{
@ -105,7 +113,11 @@ void GLimp_InitExtraExtensions(void)
}
// OpenGL 3.0 - GL_ARB_texture_float
#ifdef __ANDROID
extension = "GL_OES_texture_float";
#else
extension = "GL_ARB_texture_float";
#endif
glRefConfig.textureFloat = qfalse;
if (q_gl_version_at_least_3_0 || SDL_GL_ExtensionSupported(extension))
{

View file

@ -529,7 +529,7 @@ void RB_RenderFlares (void) {
Mat4Copy(glState.projection, oldprojection);
Mat4Copy(glState.modelview, oldmodelview);
Mat4Identity(matrix);
GL_SetModelviewMatrix(matrix);
GL_SetModelviewMatrix(matrix, qtrue);
Mat4Ortho( backEnd.viewParms.viewportX, backEnd.viewParms.viewportX + backEnd.viewParms.viewportWidth,
backEnd.viewParms.viewportY, backEnd.viewParms.viewportY + backEnd.viewParms.viewportHeight,
-99999, 99999, matrix );
@ -544,7 +544,7 @@ void RB_RenderFlares (void) {
}
GL_SetProjectionMatrix(oldprojection);
GL_SetModelviewMatrix(oldmodelview);
GL_SetModelviewMatrix(oldmodelview, qtrue);
}

View file

@ -241,6 +241,25 @@ static void GLSL_GetShaderHeader( GLenum shaderType, const GLchar *extra, char *
dest[0] = '\0';
// HACK: abuse the GLSL preprocessor to turn GLSL 1.20 shaders into 1.30 ones
#ifdef __ANDROID__
Q_strcat(dest, size, "#version 300 es\n");
Q_strcat(dest, size, "precision mediump float;\n");
if(shaderType == GL_VERTEX_SHADER)
{
Q_strcat(dest, size, "#define attribute in\n");
Q_strcat(dest, size, "#define varying out\n");
}
else
{
Q_strcat(dest, size, "#define varying in\n");
Q_strcat(dest, size, "out vec4 out_Color;\n");
Q_strcat(dest, size, "#define gl_FragColor out_Color\n");
Q_strcat(dest, size, "#define texture2D texture\n");
Q_strcat(dest, size, "#define textureCubeLod textureLod\n");
Q_strcat(dest, size, "#define shadow2D texture\n");
}
#else
if(glRefConfig.glslMajorVersion > 1 || (glRefConfig.glslMajorVersion == 1 && glRefConfig.glslMinorVersion >= 30))
{
if (glRefConfig.glslMajorVersion > 1 || (glRefConfig.glslMajorVersion == 1 && glRefConfig.glslMinorVersion >= 50))
@ -269,6 +288,7 @@ static void GLSL_GetShaderHeader( GLenum shaderType, const GLchar *extra, char *
Q_strcat(dest, size, "#version 120\n");
Q_strcat(dest, size, "#define shadow2D(a,b) shadow2D(a,b).r \n");
}
#endif
// HACK: add some macros to avoid extra uniforms and save speed and code maintenance
//Q_strcat(dest, size,

View file

@ -1624,6 +1624,9 @@ static qboolean RawImage_ScaleToPower2( byte **data, int *inout_width, int *inou
static qboolean RawImage_HasAlpha(const byte *scan, int numPixels)
{
#if __ANDROID__
return qtrue; // Using RGB as internalFormat is not supported if data format is RGBA.
#else
int i;
if (!scan)
@ -1638,6 +1641,7 @@ static qboolean RawImage_HasAlpha(const byte *scan, int numPixels)
}
return qfalse;
#endif
}
static GLenum RawImage_GetFormat(const byte *data, int numPixels, GLenum picFormat, qboolean lightMap, imgType_t type, imgFlags_t flags)
@ -1942,6 +1946,21 @@ static GLenum PixelDataFormatFromInternalFormat(GLenum internalFormat)
}
}
static GLenum PixelTypeFromInternalFormat(GLenum internalFormat)
{
switch (internalFormat)
{
case GL_DEPTH_COMPONENT:
return GL_UNSIGNED_SHORT;
case GL_DEPTH_COMPONENT24:
return GL_UNSIGNED_INT;
case GL_DEPTH_COMPONENT32F:
return GL_FLOAT;
default:
return GL_UNSIGNED_BYTE;
}
}
static void RawImage_UploadTexture(GLuint texture, byte *data, int x, int y, int width, int height, GLenum target, GLenum picFormat, int numMips, GLenum internalFormat, imgType_t type, imgFlags_t flags, qboolean subtexture )
{
GLenum dataFormat, dataType;
@ -2106,7 +2125,7 @@ image_t *R_CreateImage2( const char *name, byte *pic, int width, int height, GLe
qboolean picmip = !!(flags & IMGFLAG_PICMIP);
qboolean lastMip;
GLenum textureTarget = cubemap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D;
GLenum dataFormat;
GLenum dataFormat, pixelType;
if (strlen(name) >= MAX_QPATH ) {
ri.Error (ERR_DROP, "R_CreateImage: \"%s\" is too long", name);
@ -2163,6 +2182,7 @@ image_t *R_CreateImage2( const char *name, byte *pic, int width, int height, GLe
// Allocate texture storage so we don't have to worry about it later.
dataFormat = PixelDataFormatFromInternalFormat(internalFormat);
pixelType = PixelTypeFromInternalFormat(internalFormat);
mipWidth = width;
mipHeight = height;
miplevel = 0;
@ -2178,7 +2198,7 @@ image_t *R_CreateImage2( const char *name, byte *pic, int width, int height, GLe
}
else
{
qglTextureImage2DEXT(image->texnum, GL_TEXTURE_2D, miplevel, internalFormat, mipWidth, mipHeight, 0, dataFormat, GL_UNSIGNED_BYTE, NULL);
qglTextureImage2DEXT(image->texnum, GL_TEXTURE_2D, miplevel, internalFormat, mipWidth, mipHeight, 0, dataFormat, pixelType, NULL);
}
mipWidth = MAX(1, mipWidth >> 1);
@ -2213,7 +2233,9 @@ image_t *R_CreateImage2( const char *name, byte *pic, int width, int height, GLe
case GL_DEPTH_COMPONENT32_ARB:
// Fix for sampling depth buffer on old nVidia cards.
// from http://www.idevgames.com/forums/thread-4141-post-34844.html#pid34844
#ifndef __ANDROID__
qglTextureParameterfEXT(image->texnum, textureTarget, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE);
#endif
qglTextureParameterfEXT(image->texnum, textureTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
qglTextureParameterfEXT(image->texnum, textureTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
break;

View file

@ -955,7 +955,11 @@ const void *RB_TakeVideoFrameCmd( const void *data )
*/
void GL_SetDefaultState( void )
{
#ifdef __ANDROID__
glClearDepthf( 1.0f );
#else
qglClearDepth( 1.0f );
#endif
qglCullFace(GL_FRONT);
@ -987,7 +991,9 @@ void GL_SetDefaultState( void )
glState.currentVao = NULL;
glState.vertexAttribsEnabled = 0;
#ifndef __ANDROID__
qglPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
#endif
qglDepthMask( GL_TRUE );
qglDisable( GL_DEPTH_TEST );
qglEnable( GL_SCISSOR_TEST );
@ -1345,7 +1351,7 @@ void R_Register( void )
r_norefresh = ri.Cvar_Get ("r_norefresh", "0", CVAR_CHEAT);
r_drawentities = ri.Cvar_Get ("r_drawentities", "1", CVAR_CHEAT );
r_ignore = ri.Cvar_Get( "r_ignore", "1", CVAR_CHEAT );
r_nocull = ri.Cvar_Get ("r_nocull", "0", CVAR_CHEAT);
r_nocull = ri.Cvar_Get ("r_nocull", "1", CVAR_CHEAT);
r_novis = ri.Cvar_Get ("r_novis", "0", CVAR_CHEAT);
r_showcluster = ri.Cvar_Get ("r_showcluster", "0", CVAR_CHEAT);
r_speeds = ri.Cvar_Get ("r_speeds", "0", CVAR_CHEAT);
@ -1536,7 +1542,13 @@ void RE_Shutdown( qboolean destroyWindow ) {
R_IssuePendingRenderCommands();
R_ShutDownQueries();
if (glRefConfig.framebufferObject)
{
if (tr.vrParms.renderBufferOriginal != 0)
{
tr.renderFbo->frameBuffer = tr.vrParms.renderBufferOriginal;
}
FBO_Shutdown();
}
R_DeleteTextures();
R_ShutdownVaos();
GLSL_ShutdownGPUShaders();
@ -1617,6 +1629,10 @@ refexport_t *GetRefAPI ( int apiVersion, refimport_t *rimp ) {
re.BeginFrame = RE_BeginFrame;
re.EndFrame = RE_EndFrame;
#if __ANDROID__
re.SetVRHeadsetParms = RE_SetVRHeadsetParms;
#endif
re.MarkFragments = R_MarkFragments;
re.LerpTag = R_LerpTag;
re.ModelBounds = R_ModelBounds;

View file

@ -839,6 +839,17 @@ typedef struct {
stereoFrame_t stereoFrame;
} viewParms_t;
typedef struct {
qboolean valid;
float projectionL[16];
float projectionR[16];
float viewL[16];
float viewR[16];
int renderBufferL;
int renderBufferR;
int renderBufferOriginal;
} vrParms_t;
/*
==============================================================================
@ -1599,6 +1610,7 @@ typedef struct {
// -----------------------------------------
viewParms_t viewParms;
vrParms_t vrParms;
float identityLight; // 1.0 / ( 1 << overbrightBits )
int identityLightByte; // identityLight * 255
@ -1906,7 +1918,7 @@ void GL_CheckErrs( char *file, int line );
#define GL_CheckErrors(...) GL_CheckErrs(__FILE__, __LINE__)
void GL_State( unsigned long stateVector );
void GL_SetProjectionMatrix(mat4_t matrix);
void GL_SetModelviewMatrix(mat4_t matrix);
void GL_SetModelviewMatrix(mat4_t matrix, qboolean applyStereoView);
void GL_Cull( int cullType );
#define GLS_SRCBLEND_ZERO 0x00000001
@ -2433,6 +2445,12 @@ typedef struct {
int commandId;
} exportCubemapsCommand_t;
typedef struct {
int commandId;
int eye;
stereoFrame_t stereoFrame;
} switchEyeCommand_t;
typedef enum {
RC_END_OF_LIST,
RC_SET_COLOR,
@ -2446,7 +2464,8 @@ typedef enum {
RC_CLEARDEPTH,
RC_CAPSHADOWMAP,
RC_POSTPROCESS,
RC_EXPORT_CUBEMAPS
RC_EXPORT_CUBEMAPS,
RC_SWITCH_EYE
} renderCommand_t;
@ -2488,6 +2507,9 @@ void RE_StretchPic ( float x, float y, float w, float h,
float s1, float t1, float s2, float t2, qhandle_t hShader );
void RE_BeginFrame( stereoFrame_t stereoFrame );
void RE_EndFrame( int *frontEndMsec, int *backEndMsec );
#if __ANDROID__
void RE_SetVRHeadsetParms( const ovrTracking2* ovrTracking, int renderBufferL, int renderBufferR );
#endif
void RE_SaveJPG(char * filename, int quality, int image_width, int image_height,
unsigned char *image_buffer, int padding);
size_t RE_SaveJPGToBuffer(byte *buffer, size_t bufSize, int quality,

View file

@ -755,21 +755,6 @@ void R_SetupProjection(viewParms_t *dest, float zProj, float zFar, qboolean comp
float xmin, xmax, ymin, ymax;
float width, height, stereoSep = r_stereoSeparation->value;
/*
* offset the view origin of the viewer for stereo rendering
* by setting the projection matrix appropriately.
*/
if(stereoSep != 0)
{
if(dest->stereoFrame == STEREO_LEFT)
stereoSep = zProj / stereoSep;
else if(dest->stereoFrame == STEREO_RIGHT)
stereoSep = zProj / -stereoSep;
else
stereoSep = 0;
}
ymax = zProj * tan(dest->fovY * M_PI / 360.0f);
ymin = -ymax;
@ -778,21 +763,44 @@ void R_SetupProjection(viewParms_t *dest, float zProj, float zFar, qboolean comp
width = xmax - xmin;
height = ymax - ymin;
dest->projectionMatrix[0] = 2 * zProj / width;
dest->projectionMatrix[4] = 0;
dest->projectionMatrix[8] = (xmax + xmin + 2 * stereoSep) / width;
dest->projectionMatrix[12] = 2 * zProj * stereoSep / width;
dest->projectionMatrix[1] = 0;
dest->projectionMatrix[5] = 2 * zProj / height;
dest->projectionMatrix[9] = ( ymax + ymin ) / height; // normally 0
dest->projectionMatrix[13] = 0;
if (tr.vrParms.valid) {
if (dest->stereoFrame == STEREO_LEFT) {
memcpy(&dest->projectionMatrix, &tr.vrParms.projectionL, sizeof(dest->projectionMatrix));
}
else {
memcpy(&dest->projectionMatrix, &tr.vrParms.projectionR, sizeof(dest->projectionMatrix));
}
} else {
/*
* offset the view origin of the viewer for stereo rendering
* by setting the projection matrix appropriately.
*/
if(stereoSep != 0)
{
if(dest->stereoFrame == STEREO_LEFT)
stereoSep = zProj / stereoSep;
else if(dest->stereoFrame == STEREO_RIGHT)
stereoSep = zProj / -stereoSep;
else
stereoSep = 0;
}
dest->projectionMatrix[3] = 0;
dest->projectionMatrix[7] = 0;
dest->projectionMatrix[11] = -1;
dest->projectionMatrix[15] = 0;
dest->projectionMatrix[0] = 2 * zProj / width;
dest->projectionMatrix[4] = 0;
dest->projectionMatrix[8] = (xmax + xmin + 2 * stereoSep) / width;
dest->projectionMatrix[12] = 2 * zProj * stereoSep / width;
dest->projectionMatrix[1] = 0;
dest->projectionMatrix[5] = 2 * zProj / height;
dest->projectionMatrix[9] = ( ymax + ymin ) / height; // normally 0
dest->projectionMatrix[13] = 0;
dest->projectionMatrix[3] = 0;
dest->projectionMatrix[7] = 0;
dest->projectionMatrix[11] = -1;
dest->projectionMatrix[15] = 0;
}
// Now that we have all the data for the projection matrix we can also setup the view frustum.
if(computeFrustum)
@ -815,10 +823,12 @@ void R_SetupProjectionZ(viewParms_t *dest)
depth = zFar - zNear;
dest->projectionMatrix[2] = 0;
dest->projectionMatrix[6] = 0;
dest->projectionMatrix[10] = -( zFar + zNear ) / depth;
dest->projectionMatrix[14] = -2 * zFar * zNear / depth;
if (!tr.vrParms.valid) {
dest->projectionMatrix[2] = 0;
dest->projectionMatrix[6] = 0;
dest->projectionMatrix[10] = -( zFar + zNear ) / depth;
dest->projectionMatrix[14] = -2 * zFar * zNear / depth;
}
if (dest->isPortal)
{

View file

@ -804,7 +804,7 @@ void RB_DrawSun( float scale, shader_t *shader ) {
Mat4Translation( backEnd.viewParms.or.origin, translation );
Mat4Multiply( backEnd.viewParms.world.modelMatrix, translation, modelview );
GL_SetModelviewMatrix( modelview );
GL_SetModelviewMatrix( modelview, qtrue );
}
dist = backEnd.viewParms.zFar / 1.75; // div sqrt(3)
@ -855,11 +855,19 @@ void RB_StageIteratorSky( void ) {
// r_showsky will let all the sky blocks be drawn in
// front of everything to allow developers to see how
// much sky is getting sucked in
#ifdef __ANDROID__
if ( r_showsky->integer ) {
glDepthRangef( 0.0, 0.0 );
} else {
glDepthRangef( 1.0, 1.0 );
}
#else
if ( r_showsky->integer ) {
qglDepthRange( 0.0, 0.0 );
} else {
qglDepthRange( 1.0, 1.0 );
}
#endif
// draw the outer skybox
if ( tess.shader->sky.outerbox[0] && tess.shader->sky.outerbox[0] != tr.defaultImage ) {
@ -876,13 +884,13 @@ void RB_StageIteratorSky( void ) {
Mat4Copy( glState.modelview, oldmodelview );
Mat4Translation( backEnd.viewParms.or.origin, trans );
Mat4Multiply( glState.modelview, trans, product );
GL_SetModelviewMatrix( product );
GL_SetModelviewMatrix( product, qtrue );
}
DrawSkyBox( tess.shader );
GL_SetModelviewMatrix( oldmodelview );
GL_SetModelviewMatrix( oldmodelview, qtrue );
}
// generate the vertexes for all the clouds, which will be drawn
@ -893,9 +901,12 @@ void RB_StageIteratorSky( void ) {
// draw the inner skybox
#ifdef __ANDROID__
glDepthRangef( 0.0, 1.0 );
#else
// back to normal depth range
qglDepthRange( 0.0, 1.0 );
#endif
// note that sky was drawn so we will draw a sun later
backEnd.skyRenderedThisView = qtrue;

View file

@ -112,6 +112,7 @@ GLimp_LogComment
*/
void GLimp_LogComment( char *comment )
{
// CON_Print(comment);
}
/*
@ -307,7 +308,9 @@ static qboolean GLimp_GetProcAddresses( qboolean fixedFunction ) {
QGL_1_5_PROCS;
QGL_2_0_PROCS;
// error so this doesn't segfault due to NULL desktop GL functions being used
#ifndef __ANDROID__
Com_Error( ERR_FATAL, "Unsupported OpenGL Version: %s", version );
#endif
} else {
Com_Error( ERR_FATAL, "Unsupported OpenGL Version (%s), OpenGL 2.0 is required", version );
}
@ -431,7 +434,7 @@ static int GLimp_SetMode(int mode, qboolean fullscreen, qboolean noborder, qbool
// use desktop video resolution
if( desktopMode.h > 0 )
{
glConfig.vidWidth = desktopMode.w;
glConfig.vidWidth = r_stereoEnabled->integer ? desktopMode.w * 0.5f : desktopMode.w;
glConfig.vidHeight = desktopMode.h;
}
else

View file

@ -33,6 +33,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "../client/client.h"
#include "../sys/sys_local.h"
#if __ANDROID__
#include "../vr/vr_input.h"
#endif
static cvar_t *in_keyboardDebug = NULL;
static SDL_GameController *gamepad = NULL;
@ -1197,6 +1201,10 @@ void IN_Frame( void )
IN_ProcessEvents( );
#if __ANDROID__
IN_VRInputFrame( );
#endif
// Set event time for next frame to earliest possible time an event could happen
in_eventTime = Sys_Milliseconds( );
@ -1206,6 +1214,8 @@ void IN_Frame( void )
vidRestartTime = 0;
Cbuf_AddText( "vid_restart\n" );
}
}
/*

View file

@ -501,6 +501,8 @@ char *CON_Input( void )
CON_Print
==================
*/
void (*GLogcatFn) ( const char *msg );
void CON_Print( const char *msg )
{
if (!msg[0])
@ -513,6 +515,12 @@ void CON_Print( const char *msg )
else
fputs( msg, stderr );
#if __ANDROID__
if (GLogcatFn) {
(*GLogcatFn)(msg);
}
#endif
if (!ttycon_on) {
// CON_Hide didn't do anything.
return;
@ -535,3 +543,15 @@ void CON_Print( const char *msg )
ttycon_show_overdue++;
}
}
#if __ANDROID__
/*
==================
CON_LogcatFn
==================
*/
void CON_LogcatFn( void (*LogcatFn)( const char* message ) )
{
GLogcatFn = LogcatFn;
}
#endif

View file

@ -45,6 +45,9 @@ void CON_Shutdown( void );
void CON_Init( void );
char *CON_Input( void );
void CON_Print( const char *message );
#if __ANDROID__
void CON_LogcatFn( void (*LogcatFn)( const char* message ) );
#endif
unsigned int CON_LogSize( void );
unsigned int CON_LogWrite( const char *in );

View file

@ -255,7 +255,7 @@ Sys_InitPIDFile
*/
void Sys_InitPIDFile( const char *gamedir ) {
if( Sys_WritePIDFile( gamedir ) ) {
#ifndef DEDICATED
#if !defined(DEDICATED) && !defined(__ANDROID__)
char message[1024];
char modName[MAX_OSPATH];

62
code/vr/vr_base.c Normal file
View file

@ -0,0 +1,62 @@
#include "vr_base.h"
#include "VrApi_Types.h"
#if __ANDROID__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wstrict-prototypes"
#include <VrApi_Helpers.h>
#pragma clang diagnostic pop
#include <EGL/egl.h>
#include <assert.h>
static engine_t vr_engine;
engine_t* VR_Init( ovrJava java )
{
ovrInitParms initParams;
ovrResult initResult;
memset(&vr_engine, 0, sizeof(vr_engine));
initParams = vrapi_DefaultInitParms(&java);
initResult = vrapi_Initialize(&initParams);
assert(initResult == VRAPI_INITIALIZE_SUCCESS);
return &vr_engine;
}
void VR_Destroy( engine_t* engine )
{
if (engine == &vr_engine) {
vrapi_Shutdown();
}
}
void VR_EnterVR( engine_t* engine, ovrJava java ) {
if (!engine->ovr) {
ovrModeParms modeParams = vrapi_DefaultModeParms(&java);
modeParams.Display = (size_t)eglGetCurrentDisplay();
modeParams.WindowSurface = (size_t)eglGetCurrentSurface(EGL_DRAW);
modeParams.ShareContext = (size_t)eglGetCurrentContext();
engine->ovr = vrapi_EnterVrMode(&modeParams);
engine->frameIndex = 0;
vrapi_SetTrackingSpace(engine->ovr, VRAPI_TRACKING_SPACE_LOCAL);
}
}
void VR_LeaveVR( engine_t* engine ) {
if (engine->ovr) {
vrapi_LeaveVrMode(engine->ovr);
engine->ovr = NULL;
}
}
engine_t* VR_GetEngine( void ) {
return &vr_engine;
}
#endif

17
code/vr/vr_base.h Normal file
View file

@ -0,0 +1,17 @@
#ifndef __VR_BASE
#define __VR_BASE
#if __ANDROID__
#include "vr_types.h"
engine_t* VR_Init( ovrJava java );
void VR_Destroy( engine_t* engine );
void VR_EnterVR( engine_t* engine, ovrJava java );
void VR_LeaveVR( engine_t* engine );
engine_t* VR_GetEngine( void );
#endif
#endif

197
code/vr/vr_input.c Normal file
View file

@ -0,0 +1,197 @@
#include "vr_input.h"
#if __ANDROID__
#include "../qcommon/q_shared.h"
#include "../qcommon/qcommon.h"
#include "../client/keycodes.h"
#include "vr_base.h"
#include "VrApi_Input.h"
#ifdef USE_LOCAL_HEADERS
# include "SDL.h"
#else
# include <SDL.h>
#endif
enum {
VR_TOUCH_AXIS_UP = 1 << 0,
VR_TOUCH_AXIS_DOWN = 1 << 1,
VR_TOUCH_AXIS_LEFT = 1 << 2,
VR_TOUCH_AXIS_RIGHT = 1 << 3,
VR_TOUCH_AXIS_TRIGGER_INDEX = 1 << 4,
};
typedef struct {
uint32_t buttons;
uint32_t axisButtons;
} vrController_t;
static qboolean controllerInit = qfalse;
static vrController_t leftController;
static vrController_t rightController;
static int in_vrEventTime = 0;
static float pressedThreshold = 0.75f;
static float releasedThreshold = 0.5f;
extern cvar_t *cl_sensitivity;
extern cvar_t *m_pitch;
extern cvar_t *m_yaw;
static void IN_VRJoystick( qboolean isRightController, float joystickX, float joystickY )
{
vrController_t* controller = isRightController == qtrue ? &rightController : &leftController;
// Menu controls mapped to keyboard codes...
if (isRightController == qfalse) {
if (!(controller->axisButtons & VR_TOUCH_AXIS_UP) && joystickY > pressedThreshold) {
controller->axisButtons |= VR_TOUCH_AXIS_UP;
Com_QueueEvent(in_vrEventTime, SE_KEY, K_UPARROW, qtrue, 0, NULL);
} else if ((controller->axisButtons & VR_TOUCH_AXIS_UP) && joystickY < releasedThreshold) {
controller->axisButtons &= ~VR_TOUCH_AXIS_UP;
Com_QueueEvent(in_vrEventTime, SE_KEY, K_UPARROW, qfalse, 0, NULL);
}
if (!(controller->axisButtons & VR_TOUCH_AXIS_DOWN) && joystickY < -pressedThreshold) {
controller->axisButtons |= VR_TOUCH_AXIS_DOWN;
Com_QueueEvent(in_vrEventTime, SE_KEY, K_DOWNARROW, qtrue, 0, NULL);
} else if ((controller->axisButtons & VR_TOUCH_AXIS_DOWN) && joystickY > -releasedThreshold) {
controller->axisButtons &= ~VR_TOUCH_AXIS_DOWN;
Com_QueueEvent(in_vrEventTime, SE_KEY, K_DOWNARROW, qfalse, 0, NULL);
}
if (!(controller->axisButtons & VR_TOUCH_AXIS_LEFT) && joystickX < -pressedThreshold) {
controller->axisButtons |= VR_TOUCH_AXIS_LEFT;
Com_QueueEvent(in_vrEventTime, SE_KEY, K_LEFTARROW, qtrue, 0, NULL);
} else if ((controller->axisButtons & VR_TOUCH_AXIS_LEFT) && joystickX > -releasedThreshold) {
controller->axisButtons &= ~VR_TOUCH_AXIS_LEFT;
Com_QueueEvent(in_vrEventTime, SE_KEY, K_LEFTARROW, qfalse, 0, NULL);
}
if (!(controller->axisButtons & VR_TOUCH_AXIS_RIGHT) && joystickX > pressedThreshold) {
controller->axisButtons |= VR_TOUCH_AXIS_RIGHT;
Com_QueueEvent(in_vrEventTime, SE_KEY, K_RIGHTARROW, qtrue, 0, NULL);
} else if ((controller->axisButtons & VR_TOUCH_AXIS_RIGHT) && joystickX < releasedThreshold) {
controller->axisButtons &= ~VR_TOUCH_AXIS_RIGHT;
Com_QueueEvent(in_vrEventTime, SE_KEY, K_RIGHTARROW, qfalse, 0, NULL);
}
} else {
const float sensitivity = cl_sensitivity->value;
const float x = joystickX * sensitivity * m_yaw->value;
const float y = joystickY * sensitivity * -m_pitch->value;
Com_QueueEvent(in_vrEventTime, SE_MOUSE, x, y, 0, NULL);
}
}
static void IN_VRTriggers( qboolean isRightController, float index ) {
vrController_t* controller = isRightController == qtrue ? &rightController : &leftController;
if (isRightController == qtrue) {
if (!(controller->axisButtons & VR_TOUCH_AXIS_TRIGGER_INDEX) && index > pressedThreshold) {
controller->axisButtons |= VR_TOUCH_AXIS_TRIGGER_INDEX;
Com_QueueEvent(in_vrEventTime, SE_KEY, K_MOUSE1, qtrue, 0, NULL);
} else if ((controller->axisButtons & VR_TOUCH_AXIS_TRIGGER_INDEX) && index < releasedThreshold) {
controller->axisButtons &= ~VR_TOUCH_AXIS_TRIGGER_INDEX;
Com_QueueEvent(in_vrEventTime, SE_KEY, K_MOUSE1, qfalse, 0, NULL);
}
}
}
static void IN_VRButtonsChanged( qboolean isRightController, uint32_t buttons )
{
vrController_t* controller = isRightController == qtrue ? &rightController : &leftController;
if ((buttons & ovrButton_A) && !(controller->buttons & ovrButton_A)) {
Com_QueueEvent(in_vrEventTime, SE_KEY, K_PAD0_A, qtrue, 0, NULL);
} else if (!(buttons & ovrButton_A) && (controller->buttons & ovrButton_A)) {
Com_QueueEvent(in_vrEventTime, SE_KEY, K_PAD0_A, qfalse, 0, NULL);
}
if ((buttons & ovrButton_B) && !(controller->buttons & ovrButton_B)) {
Com_QueueEvent(in_vrEventTime, SE_KEY, K_PAD0_B, qtrue, 0, NULL);
} else if (!(buttons & ovrButton_B) && (controller->buttons & ovrButton_B)) {
Com_QueueEvent(in_vrEventTime, SE_KEY, K_PAD0_B, qfalse, 0, NULL);
}
if ((buttons & ovrButton_X) && !(controller->buttons & ovrButton_B)) {
Com_QueueEvent(in_vrEventTime, SE_KEY, K_PAD0_X, qtrue, 0, NULL);
} else if (!(buttons & ovrButton_B) && (controller->buttons & ovrButton_B)) {
Com_QueueEvent(in_vrEventTime, SE_KEY, K_PAD0_X, qfalse, 0, NULL);
}
if ((buttons & ovrButton_Y) && !(controller->buttons & ovrButton_B)) {
Com_QueueEvent(in_vrEventTime, SE_KEY, K_PAD0_Y, qtrue, 0, NULL);
} else if (!(buttons & ovrButton_B) && (controller->buttons & ovrButton_B)) {
Com_QueueEvent(in_vrEventTime, SE_KEY, K_PAD0_Y, qfalse, 0, NULL);
}
controller->buttons = buttons;
}
void IN_VRInputFrame( void )
{
if (controllerInit == qfalse) {
memset(&leftController, 0, sizeof(leftController));
memset(&rightController, 0, sizeof(rightController));
controllerInit = qtrue;
}
ovrMobile* ovr = VR_GetEngine()->ovr;
if (!ovr) {
return;
}
ovrInputCapabilityHeader capsHeader;
uint32_t index = 0;
for (;;) {
ovrResult enumResult = vrapi_EnumerateInputDevices(ovr, index, &capsHeader);
if (enumResult < 0) {
break;
}
++index;
if (capsHeader.Type != ovrControllerType_TrackedRemote) {
continue;
}
ovrInputTrackedRemoteCapabilities caps;
caps.Header = capsHeader;
ovrResult capsResult = vrapi_GetInputDeviceCapabilities(ovr, &caps.Header);
if (capsResult < 0) {
continue;
}
ovrInputStateTrackedRemote state;
state.Header.ControllerType = ovrControllerType_TrackedRemote;
ovrResult stateResult = vrapi_GetCurrentInputState(ovr, capsHeader.DeviceID, &state.Header);
if (stateResult < 0) {
continue;
}
qboolean isRight;
vrController_t* controller;
if (caps.ControllerCapabilities & ovrControllerCaps_LeftHand) {
isRight = qfalse;
controller = &leftController;
} else if (caps.ControllerCapabilities & ovrControllerCaps_RightHand) {
isRight = qtrue;
controller = &rightController;
}
else {
continue;
}
IN_VRJoystick(isRight, state.JoystickNoDeadZone.x, state.JoystickNoDeadZone.y);
IN_VRTriggers(isRight, state.IndexTrigger);
if (controller->buttons ^ state.Buttons) {
IN_VRButtonsChanged(isRight, state.Buttons);
}
}
in_vrEventTime = Sys_Milliseconds( );
}
#endif

10
code/vr/vr_input.h Normal file
View file

@ -0,0 +1,10 @@
#ifndef __VR_INPUT_H
#define __VR_INPUT_H
#if __ANDROID__
void IN_VRInputFrame( void );
#endif
#endif

141
code/vr/vr_renderer.c Normal file
View file

@ -0,0 +1,141 @@
#include "vr_renderer.h"
#include "../qcommon/q_shared.h"
#include "../qcommon/qcommon.h"
#include "../client/client.h"
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wstrict-prototypes"
#include <VrApi_Helpers.h>
#pragma clang diagnostic pop
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#define ENABLE_GL_DEBUG 1
#define ENABLE_GL_DEBUG_VERBOSE 0
#if ENABLE_GL_DEBUG
#include <GLES3/gl32.h>
#endif
void APIENTRY VR_GLDebugLog(GLenum source, GLenum type, GLuint id,
GLenum severity, GLsizei length, const GLchar* message, const void* userParam)
{
if (type == GL_DEBUG_TYPE_ERROR || type == GL_DEBUG_TYPE_PERFORMANCE || ENABLE_GL_DEBUG_VERBOSE)
{
Com_Printf("GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
(type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""), type, severity, message);
}
}
void VR_InitRenderer( engine_t* engine ) {
#if ENABLE_GL_DEBUG
glEnable(GL_DEBUG_OUTPUT);
glDebugMessageCallback(VR_GLDebugLog, 0);
#endif
int eyeW, eyeH;
eyeW = 1440; // vrapi_GetSystemPropertyInt(&java, VRAPI_SYS_PROP_SUGGESTED_EYE_TEXTURE_WIDTH);
eyeH = 1600; // vrapi_GetSystemPropertyInt(&java, VRAPI_SYS_PROP_SUGGESTED_EYE_TEXTURE_HEIGHT);
for (int eye = 0; eye < VRAPI_FRAME_LAYER_EYE_MAX; ++eye) {
framebuffer_t* framebuffer = &engine->framebuffers[eye];
framebuffer->colorTexture = vrapi_CreateTextureSwapChain3(VRAPI_TEXTURE_TYPE_2D, GL_RGBA8,
eyeW, eyeH, 1, 3);
framebuffer->swapchainLength = vrapi_GetTextureSwapChainLength(framebuffer->colorTexture);
framebuffer->depthBuffers = (GLuint*)malloc(framebuffer->swapchainLength * sizeof(GLuint));
framebuffer->framebuffers = (GLuint*)malloc(framebuffer->swapchainLength * sizeof(GLuint));
for (int index = 0; index < framebuffer->swapchainLength; ++index) {
GLuint colorTexture;
GLenum framebufferStatus;
colorTexture = vrapi_GetTextureSwapChainHandle(framebuffer->colorTexture, index);
glBindTexture(GL_TEXTURE_2D, colorTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
glGenRenderbuffers(1, &framebuffer->depthBuffers[index]);
glBindRenderbuffer(GL_RENDERBUFFER, framebuffer->depthBuffers[index]);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, eyeW, eyeH);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glGenFramebuffers(1, &framebuffer->framebuffers[index]);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer->framebuffers[index]);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
framebuffer->depthBuffers[index]);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0);
framebufferStatus = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
assert(framebufferStatus == GL_FRAMEBUFFER_COMPLETE);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
}
}
}
void VR_DestroyRenderer( engine_t* engine ) {
for (int eye = 0; eye < VRAPI_FRAME_LAYER_EYE_MAX; ++eye)
{
if (engine->framebuffers[eye].swapchainLength > 0) {
glDeleteFramebuffers(engine->framebuffers[eye].swapchainLength,
engine->framebuffers[eye].depthBuffers);
free(engine->framebuffers[eye].depthBuffers);
free(engine->framebuffers[eye].framebuffers);
vrapi_DestroyTextureSwapChain(engine->framebuffers[eye].colorTexture);
memset(&engine->framebuffers[eye], 0, sizeof(engine->framebuffers[eye]));
}
}
}
void VR_DrawFrame( engine_t* engine ) {
double predictedDisplayTime;
ovrTracking2 tracking;
ovrLayerProjection2 layer;
if (!engine->ovr)
{
return;
}
++engine->frameIndex;
predictedDisplayTime = vrapi_GetPredictedDisplayTime(engine->ovr, engine->frameIndex);
tracking = vrapi_GetPredictedTracking2(engine->ovr, predictedDisplayTime);
layer = vrapi_DefaultLayerProjection2();
layer.HeadPose = tracking.HeadPose;
for (int eye = 0; eye < VRAPI_FRAME_LAYER_EYE_MAX; ++eye) {
layer.Textures[eye].ColorSwapChain = engine->framebuffers[eye].colorTexture;
layer.Textures[eye].SwapChainIndex = engine->framebuffers[eye].swapchainIndex;
layer.Textures[eye].TexCoordsFromTanAngles = ovrMatrix4f_TanAngleMatrixFromProjection(&tracking.Eye[eye].ProjectionMatrix);
}
const framebuffer_t* framebuffers = engine->framebuffers;
re.SetVRHeadsetParms(&tracking,
framebuffers[0].framebuffers[framebuffers[0].swapchainIndex],
framebuffers[1].framebuffers[framebuffers[1].swapchainIndex]);
Com_Frame();
for (int eye = 0; eye < VRAPI_FRAME_LAYER_EYE_MAX; ++eye) {
engine->framebuffers[eye].swapchainIndex = (engine->framebuffers[eye].swapchainIndex + 1) %
engine->framebuffers[eye].swapchainLength;
}
const ovrLayerHeader2* layers[] = {
&layer.Header
};
ovrSubmitFrameDescription2 frameDesc = { 0 };
frameDesc.Flags = 0;
frameDesc.SwapInterval = 1;
frameDesc.FrameIndex = engine->frameIndex;
frameDesc.DisplayTime = predictedDisplayTime;
frameDesc.LayerCount = 1;
frameDesc.Layers = layers;
vrapi_SubmitFrame2(engine->ovr, &frameDesc);
}

14
code/vr/vr_renderer.h Normal file
View file

@ -0,0 +1,14 @@
#ifndef __VR_RENDERER
#define __VR_RENDERER
#if __ANDROID__
#include "vr_types.h"
void VR_InitRenderer( engine_t* engine );
void VR_DestroyRenderer( engine_t* engine );
void VR_DrawFrame( engine_t* engine );
#endif
#endif

31
code/vr/vr_types.h Normal file
View file

@ -0,0 +1,31 @@
#ifndef __VR_TYPES
#define __VR_TYPES
#ifdef USE_LOCAL_HEADERS
# include "SDL_opengl.h"
# include "SDL_opengles2.h"
#else
# include <SDL_opengl.h>
# include <SDL_opengles2.h>
#endif
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wstrict-prototypes"
#include <VrApi.h>
#pragma clang diagnostic pop
typedef struct {
int swapchainLength;
int swapchainIndex;
ovrTextureSwapChain* colorTexture;
GLuint* depthBuffers;
GLuint* framebuffers;
} framebuffer_t;
typedef struct {
uint64_t frameIndex;
ovrMobile* ovr;
framebuffer_t framebuffers[VRAPI_FRAME_LAYER_EYE_MAX];
} engine_t;
#endif