diff --git a/app/build.gradle b/app/build.gradle index f09e9c9..f592b96 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,8 +7,8 @@ android { applicationId "com.drbeef.dvr" minSdkVersion 16 targetSdkVersion 19 - versionCode 1 - versionName '1.0.0' + versionCode 2 + versionName '1.0.1' } buildTypes { release { diff --git a/app/jni/prboom/r_fps.c b/app/jni/prboom/r_fps.c index 10a9d07..25be360 100644 --- a/app/jni/prboom/r_fps.c +++ b/app/jni/prboom/r_fps.c @@ -63,7 +63,7 @@ tic_vars_t tic_vars; view_vars_t original_view_vars; -fixed_t r_stereo_offset = 0x30000; +fixed_t r_stereo_offset = 0x38000; extern int realtic_clock_rate; void D_Display(void); @@ -121,7 +121,7 @@ void R_InterpolateView (player_t *player, fixed_t frac, int eye) //More we roll, less we stereo float mult = 1.0f; - mult -= fabs(hmdRoll)/22.5f; + mult -= fabs(hmdRoll)/30.0f; if (mult < 0.0f) mult = 0.0f; diff --git a/app/src/main/java/com/drbeef/dvr/MainActivity.java b/app/src/main/java/com/drbeef/dvr/MainActivity.java index 92e7168..e3898dd 100644 --- a/app/src/main/java/com/drbeef/dvr/MainActivity.java +++ b/app/src/main/java/com/drbeef/dvr/MainActivity.java @@ -12,7 +12,9 @@ import android.opengl.GLES20; import android.opengl.Matrix; import android.os.Bundle; import android.os.Vibrator; +import android.util.DisplayMetrics; import android.util.Log; +import android.view.Display; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.InputDevice; @@ -20,9 +22,11 @@ import android.view.Window; import android.view.WindowManager; import com.google.vrtoolkit.cardboard.CardboardActivity; +import com.google.vrtoolkit.cardboard.CardboardDeviceParams; import com.google.vrtoolkit.cardboard.CardboardView; import com.google.vrtoolkit.cardboard.Eye; import com.google.vrtoolkit.cardboard.HeadTransform; +import com.google.vrtoolkit.cardboard.ScreenParams; import com.google.vrtoolkit.cardboard.Viewport; import java.io.BufferedReader; @@ -48,10 +52,7 @@ public class MainActivity { private static final String TAG = "DVR"; - //Head orientation - private float[] eulerAngles = new float[3]; - - OpenGL openGL = null; + OpenGL openGL = null; // Audio Cache Manager private AudioManager mAudioMgr; @@ -62,6 +63,12 @@ public class MainActivity // height of mBitmap private int mDoomHeight; + //Head orientation + private float[] eulerAngles = new float[3]; + private float hmdYaw; + private float hmdPitch; + private float hmdRoll; + //-1 means start button isn't pressed private long startButtonDownCounter = -1; //Don't allow the trigger to fire more than once per 200ms @@ -83,9 +90,12 @@ public class MainActivity //Can't rebuild eye buffers until surface changed flag recorded public static boolean mSurfaceChanged = false; + float lensCentreOffset = -1.0f; + private boolean mShowingSpashScreen = true; private int[] splashTexture = new int[1]; private MediaPlayer mPlayer; + private int mPlayerVolume = 100; static { @@ -286,9 +296,9 @@ public class MainActivity public void onNewFrame(HeadTransform headTransform) { headTransform.getEulerAngles(eulerAngles, 0); - float yaw = eulerAngles[1] / (M_PI / 180.0f); - float pitch = -eulerAngles[0] / (M_PI / 180.0f); - float roll = -eulerAngles[2] / (M_PI / 180.0f); + hmdYaw = eulerAngles[1] / (M_PI / 180.0f); + hmdPitch = -eulerAngles[0] / (M_PI / 180.0f); + hmdRoll = -eulerAngles[2] / (M_PI / 180.0f); if (!mShowingSpashScreen && mWADChooser.choosingWAD()) { @@ -306,20 +316,32 @@ public class MainActivity argv = args.split(" "); String dvr= DoomTools.GetDVRFolder(); Natives.DoomInit(argv, dvr); + mDVRInitialised = true; - if (mPlayer != null) { - mPlayer.stop(); - mPlayer.release(); - mPlayer = null; - } } } if (mDVRInitialised) { + //Fade out intro music + if (mPlayer != null) { + mPlayerVolume--; + if (mPlayerVolume == 0) { + mPlayer.stop(); + mPlayer.release(); + mPlayer = null; + } + else + { + float log1 = AudioManager.getLogVolume(mPlayerVolume); + mPlayer.setVolume(log1, log1); + } + } + + long newState = Natives.gameState(); if (newState == 0) - Natives.DoomStartFrame(pitch, yaw, roll); + Natives.DoomStartFrame(hmdPitch, hmdYaw, hmdRoll); else Natives.DoomStartFrame(0, 0, 0); @@ -332,29 +354,42 @@ public class MainActivity cardboardView.resetHeadTracker(); } } + + if (mDVRInitialised) { + //Get all the DOOM drawing done here, minimise time between eye calls + Natives.DoomDrawEye(0); + openGL.CopyBitmapToTexture(mDoomBitmap, openGL.fbo[0].ColorTexture[0]); + Natives.DoomDrawEye(1); + openGL.CopyBitmapToTexture(mDoomBitmap, openGL.fbo[1].ColorTexture[0]); + } } @Override public void onDrawEye(Eye eye) { + + if (lensCentreOffset == -1.0f) { + //Now calculate the auto lens centre correction + CardboardDeviceParams device = cardboardView.getHeadMountedDisplay().getCardboardDeviceParams(); + ScreenParams scr = cardboardView.getScreenParams(); + Display display = getWindowManager().getDefaultDisplay(); + DisplayMetrics met = new DisplayMetrics(); + display.getMetrics(met); + float dpmil = (met.xdpi / 25.4f); + float qscreen = (scr.getWidthMeters() * 1000.0f) / 4.0f; + float halflens = (device.getInterLensDistance() * 1000.0f) / 2.0f; + //Multiply by small fudge factor (25%) + lensCentreOffset = ((halflens - qscreen) * dpmil) * 1.25f; + //Viewport size is not the same as screen resolution, so convert + lensCentreOffset = (lensCentreOffset / (scr.getWidth() / 2.0f)) * eye.getViewport().width; + } + + if (!mShowingSpashScreen && mWADChooser.choosingWAD()) { mWADChooser.onDrawEye(eye, this); } else if (mDVRInitialised || mShowingSpashScreen) { - if (mShowingSpashScreen) { - GLES20.glEnable(GLES20.GL_SCISSOR_TEST); - GLES20.glScissor(eye.getViewport().x, eye.getViewport().y, - eye.getViewport().width, eye.getViewport().height); - GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); - } else { - Natives.DoomDrawEye(eye.getType() - 1); - - //Now we'll have a populated bitmap, copy to the fbo colour buffer - openGL.CopyBitmapToTexture(mDoomBitmap, openGL.fbo.ColorTexture[0]); - } - GLES20.glViewport(eye.getViewport().x, eye.getViewport().y, eye.getViewport().width, @@ -367,7 +402,7 @@ public class MainActivity eye.getViewport().y, eye.getViewport().width, eye.getViewport().height); - GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); GLES20.glUseProgram(openGL.sp_Image); @@ -397,37 +432,40 @@ public class MainActivity } else { - // Create the triangles for orthographic projection (if required) - int w = (int) (eye.getViewport().width * 0.82f); - int h = (int) (eye.getViewport().height * 0.7f); - int x = (int) ((eye.getType() == Eye.Type.LEFT) ? 0 : (eye.getViewport().width - w)); - int y = (int) (eye.getViewport().height * 0.15f); + float widthScaler = 0.76f; + float heightScaler = 0.64f; + + // Create the triangles for orthographic projection + int w = (int) (eye.getViewport().width * widthScaler); + int h = (int) (eye.getViewport().height * heightScaler); + int x = (int) (eye.getViewport().width * ((1.0f-widthScaler)/2.0f));; + int y = (int) (eye.getViewport().height * ((1.0f-heightScaler)/2.0f)); int pitchOffset = (int)(-(eulerAngles[0]/M_PI)*(eye.getViewport().height)); - int widthScaler = 0; + int pitchWidthScaler = 0; float f = -(eulerAngles[0]/M_PI); if (f > 0.125f) - widthScaler = (int)(((f - 0.125f)/2.0f) * eye.getViewport().width); + pitchWidthScaler = (int)(((f - 0.125f)/2.0f) * eye.getViewport().width); - openGL.SetupTriangle(x+widthScaler, y, w-widthScaler*2, h); + int l = (int)lensCentreOffset; + if (eye.getType() == Eye.Type.LEFT) + l = -l; + openGL.SetupTriangle(x + pitchWidthScaler, y, w - pitchWidthScaler * 2, h); // Calculate the projection and view transformation Matrix.orthoM(openGL.view, 0, 0, eye.getViewport().width, 0, eye.getViewport().height, 0, 50); //Translate so origin is centre of image - if (eye.getType() == Eye.Type.LEFT) - Matrix.translateM(openGL.view, 0, w / 2, eye.getViewport().height / 2, 0); - else - Matrix.translateM(openGL.view, 0, eye.getViewport().width - w / 2, eye.getViewport().height / 2, 0); + Matrix.translateM(openGL.view, 0, eye.getViewport().width / 2, eye.getViewport().height / 2, 0); //rotate for head roll - Matrix.rotateM(openGL.view, 0, (int) (-(eulerAngles[2] / M_PI) * 180.f), 0, 0, 1); + Matrix.rotateM(openGL.view, 0, (int) hmdRoll, 0, 0, 1); //translate back to where it was before - if (eye.getType() == Eye.Type.LEFT) - Matrix.translateM(openGL.view, 0, -w / 2, -eye.getViewport().height / 2, 0); - else - Matrix.translateM(openGL.view, 0, w / 2 - eye.getViewport().width, -eye.getViewport().height / 2, 0); - //Now apply head pitch transformation + Matrix.translateM(openGL.view, 0, (float)(Math.cos(eulerAngles[2]) * l) - eye.getViewport().width / 2, + (float)(Math.sin(eulerAngles[2]) * l) - eye.getViewport().height / 2, 0); + + //Now apply head hmdPitch transformation Matrix.translateM(openGL.view, 0, 0, pitchOffset, 0); + //Matrix.translateM(openGL.view, 0, l, 0, 0); Matrix.multiplyMM(openGL.modelViewProjection, 0, openGL.view, 0, openGL.camera, 0); // Prepare the triangle coordinate data @@ -449,7 +487,7 @@ public class MainActivity GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, splashTexture[0]); } else { - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, openGL.fbo.ColorTexture[0]); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, openGL.fbo[eye.getType()-1].ColorTexture[0]); } // Set the sampler texture unit to our fbo's color texture @@ -477,9 +515,9 @@ public class MainActivity if (!mShowingSpashScreen && mWADChooser.choosingWAD()) { - if (eulerAngles[1] / (M_PI / 180.0f) > 15.0f) + if (hmdYaw > 15.0f) mWADChooser.MoveNext(); - else if (eulerAngles[1] / (M_PI / 180.0f) < -15.0f) + else if (hmdYaw < -15.0f) mWADChooser.MovePrev(); else mWADChooser.SelectWAD(); @@ -524,9 +562,9 @@ public class MainActivity { if (action == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BUTTON_A) { - if (eulerAngles[1] / (M_PI / 180.0f) > 10.0f) + if (hmdYaw > 15.0f) mWADChooser.MoveNext(); - else if (eulerAngles[1] / (M_PI / 180.0f) < -10.0f) + else if (hmdYaw < -15.0f) mWADChooser.MovePrev(); else mWADChooser.SelectWAD(); @@ -612,6 +650,8 @@ public class MainActivity // 1 - Generic BT gamepad // 2 - Samsung gamepad that uses different axes for right stick int gamepadType = 0; + int lTrigAction = KeyEvent.ACTION_UP; + int rTrigAction = KeyEvent.ACTION_UP; @Override public boolean onGenericMotionEvent(MotionEvent event) { @@ -647,6 +687,26 @@ public class MainActivity Natives.motionEvent(0, (int)(rx * 30), 0); break; } + + //Fire weapon using shoulder trigger + float axisRTrigger = max(event.getAxisValue(MotionEvent.AXIS_RTRIGGER), + event.getAxisValue(MotionEvent.AXIS_GAS)); + int newRTrig = axisRTrigger > 0.6 ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP; + if (rTrigAction != newRTrig) + { + Natives.keyEvent(newRTrig, DoomTools.KEY_RCTRL); + rTrigAction = newRTrig; + } + + //Run using L shoulder + float axisLTrigger = max(event.getAxisValue(MotionEvent.AXIS_LTRIGGER), + event.getAxisValue(MotionEvent.AXIS_BRAKE)); + int newLTrig = axisLTrigger > 0.6 ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP; + if (lTrigAction != newLTrig) + { + Natives.keyEvent(newLTrig, DoomTools.KEY_RSHIFT); + lTrigAction = newLTrig; + } } } return false; @@ -744,7 +804,8 @@ public class MainActivity mDoomBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.RGB_565); openGL.SetBitmap(mDoomBitmap); - openGL.CreateFBO(mDoomWidth, mDoomHeight); + openGL.CreateFBO(openGL.fbo[0], mDoomWidth, mDoomHeight); + openGL.CreateFBO(openGL.fbo[1], mDoomWidth, mDoomHeight); } @Override diff --git a/app/src/main/java/com/drbeef/dvr/OpenGL.java b/app/src/main/java/com/drbeef/dvr/OpenGL.java index 7c07fa7..4be858c 100644 --- a/app/src/main/java/com/drbeef/dvr/OpenGL.java +++ b/app/src/main/java/com/drbeef/dvr/OpenGL.java @@ -43,7 +43,8 @@ public class OpenGL { modelView = new float[16]; //Create the FBOs - fbo = new DVRFBO(); + fbo[0] = new DVRFBO(); + fbo[1] = new DVRFBO(); } public void onSurfaceCreated(EGLConfig config) { @@ -176,10 +177,10 @@ public class OpenGL { return shader; } //FBO render eye buffer - public DVRFBO fbo; + public DVRFBO fbo[] = new DVRFBO[2]; - boolean CreateFBO( int width, int height) + boolean CreateFBO(DVRFBO fbo, int width, int height) { Log.d("DVR", "CreateFBO"); // Create the color buffer texture. @@ -217,7 +218,7 @@ public class OpenGL { return true; } - void DestroyFBO( ) + void DestroyFBO(DVRFBO fbo) { GLES20.glDeleteFramebuffers( 1, fbo.FrameBuffer, 0 ); fbo.FrameBuffer[0] = 0; diff --git a/app/src/main/java/com/drbeef/dvr/WADChooser.java b/app/src/main/java/com/drbeef/dvr/WADChooser.java index 68e2f35..a53dc09 100644 --- a/app/src/main/java/com/drbeef/dvr/WADChooser.java +++ b/app/src/main/java/com/drbeef/dvr/WADChooser.java @@ -151,7 +151,7 @@ public class WADChooser { canvas.drawText(">", 228, 116, paint); } - openGL.CopyBitmapToTexture(bitmap, openGL.fbo.ColorTexture[0]); + openGL.CopyBitmapToTexture(bitmap, openGL.fbo[0].ColorTexture[0]); } public void onDrawEye(Eye eye, Context ctx) { @@ -240,7 +240,7 @@ public class WADChooser { IntBuffer activeTex0 = IntBuffer.allocate(2); GLES20.glGetIntegerv(GLES20.GL_TEXTURE_BINDING_2D, activeTex0); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, openGL.fbo.ColorTexture[0]); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, openGL.fbo[0].ColorTexture[0]); // Set the sampler texture unit to our fbo's color texture GLES20.glUniform1i(openGL.samplerParam, 0); diff --git a/app/src/main/java/doom/audio/AudioClip.java b/app/src/main/java/doom/audio/AudioClip.java index 1333d6a..c356b93 100644 --- a/app/src/main/java/doom/audio/AudioClip.java +++ b/app/src/main/java/doom/audio/AudioClip.java @@ -124,7 +124,7 @@ public class AudioClip if ( mPlayer != null) { if (vol > 100) vol = 100; - float log1=1.0f - (float)(Math.log(101-vol)/Math.log(101)); + float log1 = AudioManager.getLogVolume(vol); mPlayer.setVolume(log1, log1); } } diff --git a/app/src/main/java/doom/audio/AudioManager.java b/app/src/main/java/doom/audio/AudioManager.java index 9aa9e8f..65b8ddd 100644 --- a/app/src/main/java/doom/audio/AudioManager.java +++ b/app/src/main/java/doom/audio/AudioManager.java @@ -46,7 +46,11 @@ public class AudioManager private AudioManager(Context ctx) { mContext = ctx; } - + + static public float getLogVolume(int volume) { + return 1.0f - (float)(Math.log(101-volume)/Math.log(101)); + } + /** * Start a sound by name & volume * @param name example "pistol" when firing the gun