Skip to content

Instantly share code, notes, and snippets.

@sigmaxipi
Created February 21, 2019 02:52
Show Gist options
  • Save sigmaxipi/b9d4cbfb3410e5be6c5279f56c09b391 to your computer and use it in GitHub Desktop.
Save sigmaxipi/b9d4cbfb3410e5be6c5279f56c09b391 to your computer and use it in GitHub Desktop.
diff --git a/samples/sdk-video360/src/main/java/com/google/vr/sdk/samples/video360/MonoscopicView.java b/samples/sdk-video360/src/main/java/com/google/vr/sdk/samples/video360/MonoscopicView.java
index 19ebd03..cb1b577 100644
--- a/samples/sdk-video360/src/main/java/com/google/vr/sdk/samples/video360/MonoscopicView.java
+++ b/samples/sdk-video360/src/main/java/com/google/vr/sdk/samples/video360/MonoscopicView.java
@@ -30,8 +30,12 @@ import android.support.annotation.AnyThread;
import android.support.annotation.BinderThread;
import android.support.annotation.UiThread;
import android.util.AttributeSet;
+import android.view.Display;
import android.view.MotionEvent;
+import android.view.OrientationEventListener;
+import android.view.Surface;
import android.view.View;
+import android.view.WindowManager;
import com.google.vr.sdk.base.Eye.Type;
import com.google.vr.sdk.samples.video360.rendering.SceneRenderer;
import javax.microedition.khronos.egl.EGLConfig;
@@ -52,6 +56,8 @@ public final class MonoscopicView extends GLSurfaceView {
private SensorManager sensorManager;
private Sensor orientationSensor;
private PhoneOrientationListener phoneOrientationListener;
+ private DisplayOrientationListener displayOrientationListener;
+ private int displayRotationDegrees;
private MediaLoader mediaLoader;
private Renderer renderer;
@@ -88,6 +94,12 @@ public final class MonoscopicView extends GLSurfaceView {
orientationSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GAME_ROTATION_VECTOR);
phoneOrientationListener = new PhoneOrientationListener();
+ // When a phone rotates from portrait <-> landscape or portrait <-> reverse landscape, this flow
+ // is used. However, this flow isn't used for landscape <-> reverse landscape changes. For that
+ // case, displayOrientationListener's onOrientationChanged callback is used.
+ displayOrientationListener = new DisplayOrientationListener(getContext());
+ displayOrientationListener.recomputeDisplayOrientation();
+
touchTracker = new TouchTracker(renderer);
setOnTouchListener(touchTracker);
}
@@ -99,6 +111,7 @@ public final class MonoscopicView extends GLSurfaceView {
// Use the fastest sensor readings.
sensorManager.registerListener(
phoneOrientationListener, orientationSensor, SensorManager.SENSOR_DELAY_FASTEST);
+ displayOrientationListener.enable();
mediaLoader.resume();
}
@@ -107,6 +120,7 @@ public final class MonoscopicView extends GLSurfaceView {
public void onPause() {
mediaLoader.pause();
sensorManager.unregisterListener(phoneOrientationListener);
+ displayOrientationListener.disable();
super.onPause();
}
@@ -121,11 +135,11 @@ public final class MonoscopicView extends GLSurfaceView {
mediaLoader.handleIntent(intent, uiView);
}
- /** Detects sensor events and saves them as a matrix. */
+ /** Detects fine-grained sensor events and saves them as a matrix. */
private class PhoneOrientationListener implements SensorEventListener {
private final float[] phoneInWorldSpaceMatrix = new float[16];
private final float[] remappedPhoneMatrix = new float[16];
- private final float[] angles = new float[3];
+ private final float[] anglesRadians = new float[3];
@Override
@BinderThread
@@ -140,9 +154,9 @@ public final class MonoscopicView extends GLSurfaceView {
phoneInWorldSpaceMatrix,
SensorManager.AXIS_X, SensorManager.AXIS_MINUS_Z,
remappedPhoneMatrix);
- SensorManager.getOrientation(remappedPhoneMatrix, angles);
- float roll = angles[2];
- touchTracker.setRoll(roll);
+ SensorManager.getOrientation(remappedPhoneMatrix, anglesRadians);
+ float roll = anglesRadians[2];
+ touchTracker.setRoll((float) (roll - Math.toRadians(displayRotationDegrees)));
// Rotate from Android coordinates to OpenGL coordinates. Android's coordinate system
// assumes Y points North and Z points to the sky. OpenGL has Y pointing up and Z pointing
@@ -155,6 +169,51 @@ public final class MonoscopicView extends GLSurfaceView {
public void onAccuracyChanged(Sensor sensor, int accuracy) {}
}
+ /** Detects coarse-grained sensor events and saves them as a matrix. */
+ private class DisplayOrientationListener extends OrientationEventListener {
+ public DisplayOrientationListener(Context context) {
+ super(context);
+ }
+
+ /**
+ * This is called when Android's sensors detect rotation of the screen. It is specifically
+ *
+ * @param orientationDegrees the current orientation with a precision of single digit degrees.
+ * This is clockwise with respect to the device's natural orientation.
+ */
+ @Override
+ @BinderThread
+ public void onOrientationChanged(int orientationDegrees) {
+ int counterClockwiseOrientation = 360 - orientationDegrees;
+ int roundedOrientation = (((counterClockwiseOrientation + 45) % 360) / 90) * 90;
+ if (Math.abs(displayRotationDegrees - roundedOrientation) > 90) {
+ recomputeDisplayOrientation();
+ }
+ }
+
+ @AnyThread
+ public void recomputeDisplayOrientation() {
+ WindowManager windowManager =
+ (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
+ Display display = windowManager.getDefaultDisplay();
+ switch (display.getRotation()) {
+ case Surface.ROTATION_90:
+ displayRotationDegrees = 90;
+ break;
+ case Surface.ROTATION_180:
+ displayRotationDegrees = 180;
+ break;
+ case Surface.ROTATION_270:
+ displayRotationDegrees = 270;
+ break;
+ default:
+ displayRotationDegrees = 0;
+ }
+
+ renderer.setDisplayRotation(displayRotationDegrees);
+ }
+ };
+
/**
* Basic touch input system.
*
@@ -189,7 +248,7 @@ public final class MonoscopicView extends GLSurfaceView {
private final PointF accumulatedTouchOffsetDegrees = new PointF();
// The conversion from touch to yaw & pitch requires compensating for device roll. This is set
// on the sensor thread and read on the UI thread.
- private volatile float roll;
+ private volatile float rollRadians;
private final Renderer renderer;
@@ -215,7 +274,7 @@ public final class MonoscopicView extends GLSurfaceView {
float touchY = (event.getY() - previousTouchPointPx.y) / PX_PER_DEGREES;
previousTouchPointPx.set(event.getX(), event.getY());
- float r = roll; // Copy volatile state.
+ float r = rollRadians; // Copy volatile state.
float cr = (float) Math.cos(r);
float sr = (float) Math.sin(r);
// To convert from screen space to the 3D space, we need to adjust the drag vector based
@@ -226,8 +285,8 @@ public final class MonoscopicView extends GLSurfaceView {
// Handle pitch and limit it to 45 degrees.
accumulatedTouchOffsetDegrees.y += sr * touchX + cr * touchY;
accumulatedTouchOffsetDegrees.y =
- Math.max(-MAX_PITCH_DEGREES,
- Math.min(MAX_PITCH_DEGREES, accumulatedTouchOffsetDegrees.y));
+ Math.max(
+ -MAX_PITCH_DEGREES, Math.min(MAX_PITCH_DEGREES, accumulatedTouchOffsetDegrees.y));
renderer.setPitchOffset(accumulatedTouchOffsetDegrees.y);
renderer.setYawOffset(accumulatedTouchOffsetDegrees.x);
@@ -238,9 +297,9 @@ public final class MonoscopicView extends GLSurfaceView {
}
@BinderThread
- public void setRoll(float roll) {
+ public void setRoll(float rollRadians) {
// We compensate for roll by rotating in the opposite direction.
- this.roll = -roll;
+ this.rollRadians = -rollRadians;
}
}
@@ -251,7 +310,7 @@ public final class MonoscopicView extends GLSurfaceView {
static class Renderer implements GLSurfaceView.Renderer {
private final SceneRenderer scene = SceneRenderer.createFor2D();
- // Arbitrary vertical field of view. Adjust as desired.
+ // Arbitrary max field of view. Adjust as desired.
private static final int FIELD_OF_VIEW_DEGREES = 90;
private static final float Z_NEAR = .1f;
private static final float Z_FAR = 100;
@@ -270,6 +329,7 @@ public final class MonoscopicView extends GLSurfaceView {
private final float[] touchYawMatrix = new float[16];
private float touchPitch;
private float deviceRoll;
+ private final float[] displayRotationMatrix = new float[16];
// viewMatrix = touchPitch * deviceOrientation * touchYaw.
private final float[] viewMatrix = new float[16];
@@ -280,6 +340,7 @@ public final class MonoscopicView extends GLSurfaceView {
public Renderer(VideoUiView uiView, MediaLoader mediaLoader) {
Matrix.setIdentityM(deviceOrientationMatrix, 0);
+ Matrix.setIdentityM(displayRotationMatrix, 0);
Matrix.setIdentityM(touchPitchMatrix, 0);
Matrix.setIdentityM(touchYawMatrix, 0);
this.uiView = uiView;
@@ -298,8 +359,25 @@ public final class MonoscopicView extends GLSurfaceView {
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES20.glViewport(0, 0, width, height);
- Matrix.perspectiveM(
- projectionMatrix, 0, FIELD_OF_VIEW_DEGREES, (float) width / height, Z_NEAR, Z_FAR);
+ float aspectRatio = (float) width / height;
+ float verticalFovDegrees;
+ if (aspectRatio < 1) {
+ // For portrait mode, use the max FOV for the vertical FOV.
+ verticalFovDegrees = FIELD_OF_VIEW_DEGREES;
+ } else {
+ // When in landscape mode, we need to compute the vertical FOV to pass into
+ // Matrix.perspectiveM. As a quick calculation we could use
+ // verticalFovDegrees = FIELD_OF_VIEW_DEGREES / aspectRatio. However, this results in an
+ // incorrect FOV for large values of FIELD_OF_VIEW_DEGREES. The correct calculation should
+ // compute the ratios of the tan of the vertical & horizontal FOVs.
+ double horizontalHalfFovRadians = Math.toRadians(FIELD_OF_VIEW_DEGREES / 2);
+ double horizontalHalfFovTanAngle = Math.tan(horizontalHalfFovRadians);
+ double verticalHalfFovTanAngle = horizontalHalfFovTanAngle / aspectRatio;
+ double verticalHalfFovRadians = Math.atan(verticalHalfFovTanAngle);
+ verticalFovDegrees = (float) Math.toDegrees(2 * verticalHalfFovRadians);
+ }
+
+ Matrix.perspectiveM(projectionMatrix, 0, verticalFovDegrees, aspectRatio, Z_NEAR, Z_FAR);
}
@Override
@@ -310,9 +388,11 @@ public final class MonoscopicView extends GLSurfaceView {
synchronized (this) {
Matrix.multiplyMM(tempMatrix, 0, deviceOrientationMatrix, 0, touchYawMatrix, 0);
Matrix.multiplyMM(viewMatrix, 0, touchPitchMatrix, 0, tempMatrix, 0);
+ Matrix.multiplyMM(tempMatrix, 0, displayRotationMatrix, 0, viewMatrix, 0);
}
- Matrix.multiplyMM(viewProjectionMatrix, 0, projectionMatrix, 0, viewMatrix, 0);
+ Matrix.multiplyMM(viewProjectionMatrix, 0, projectionMatrix, 0, tempMatrix, 0);
+
scene.glDrawFrame(viewProjectionMatrix, Type.MONOCULAR);
}
@@ -349,5 +429,14 @@ public final class MonoscopicView extends GLSurfaceView {
public synchronized void setYawOffset(float yawDegrees) {
Matrix.setRotateM(touchYawMatrix, 0, -yawDegrees, 0, 1, 0);
}
+
+ /**
+ * Adjust the rendered scene to handle portrait, landscape, etc display rotations.
+ *
+ * @param displayRotationDegrees should be a multiple of 90 degrees.
+ */
+ public synchronized void setDisplayRotation(int displayRotationDegrees) {
+ Matrix.setRotateM(displayRotationMatrix, 0, displayRotationDegrees, 0, 0, 1);
+ }
}
}
@PhanSon95
Copy link

PhanSon95 commented Dec 18, 2019

Hi @sigmaxipi. I apply your path into my MonoscopicView file
This is my apply: https://gist.github.com/PhanSon95/bbf7e25f700ec8a00b43406debdacb2c
My Vr 360 video worked, but it's not realy worked as smooth, when phone portrait(auto rotate: ON), I've rotate my phone -> LANDSCAPE, application rotate OK , but content of video it's not rotate, may be delay 5s or more time or have to shake my phone , video will rotate normally
I don't know why. If you see this comment, I hope you can reply soon
Thanks so much!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment