diff --git a/TangoReleaseLibs/aar/tango-ux-support-library.aar b/TangoReleaseLibs/aar/tango-ux-support-library.aar deleted file mode 100644 index 74caa23b..00000000 Binary files a/TangoReleaseLibs/aar/tango-ux-support-library.aar and /dev/null differ diff --git a/TangoReleaseLibs/aar/tango_support_java_lib.aar b/TangoReleaseLibs/aar/tango_support_java_lib.aar index ddd6f1d1..a55ac1e9 100644 Binary files a/TangoReleaseLibs/aar/tango_support_java_lib.aar and b/TangoReleaseLibs/aar/tango_support_java_lib.aar differ diff --git a/TangoReleaseLibs/aar/tango_ux_support_library.aar b/TangoReleaseLibs/aar/tango_ux_support_library.aar new file mode 100644 index 00000000..c9928dba Binary files /dev/null and b/TangoReleaseLibs/aar/tango_ux_support_library.aar differ diff --git a/TangoReleaseLibs/jar/tango_java_lib.jar b/TangoReleaseLibs/jar/tango_java_lib.jar index ba3dc19a..77a2c68a 100644 Binary files a/TangoReleaseLibs/jar/tango_java_lib.jar and b/TangoReleaseLibs/jar/tango_java_lib.jar differ diff --git a/java_augmented_reality_example/app/src/main/java/com/projecttango/examples/java/augmentedreality/AugmentedRealityActivity.java b/java_augmented_reality_example/app/src/main/java/com/projecttango/examples/java/augmentedreality/AugmentedRealityActivity.java index 66ee7aac..b250d642 100644 --- a/java_augmented_reality_example/app/src/main/java/com/projecttango/examples/java/augmentedreality/AugmentedRealityActivity.java +++ b/java_augmented_reality_example/app/src/main/java/com/projecttango/examples/java/augmentedreality/AugmentedRealityActivity.java @@ -34,7 +34,6 @@ import android.app.AlertDialog; import android.content.DialogInterface; import android.content.pm.PackageManager; -import android.hardware.Camera; import android.hardware.display.DisplayManager; import android.opengl.GLSurfaceView; import android.opengl.Matrix; @@ -43,7 +42,6 @@ import android.support.v4.content.ContextCompat; import android.util.Log; import android.view.Display; -import android.view.Surface; import android.widget.Toast; import org.rajawali3d.scene.ASceneFrameCallback; @@ -79,15 +77,11 @@ public class AugmentedRealityActivity extends Activity { private static final String TAG = AugmentedRealityActivity.class.getSimpleName(); private static final int INVALID_TEXTURE_ID = 0; - // For all current Tango devices, color camera is in the camera id 0. - private static final int COLOR_CAMERA_ID = 0; - private static final String CAMERA_PERMISSION = Manifest.permission.CAMERA; private static final int CAMERA_PERMISSION_CODE = 0; private SurfaceView mSurfaceView; private AugmentedRealityRenderer mRenderer; - private TangoCameraIntrinsics mIntrinsics; private Tango mTango; private TangoConfig mConfig; private boolean mIsConnected = false; @@ -99,7 +93,7 @@ public class AugmentedRealityActivity extends Activity { private AtomicBoolean mIsFrameAvailableTangoThread = new AtomicBoolean(false); private double mRgbTimestampGlThread; - private int mColorCameraToDisplayAndroidRotation = 0; + private int mDisplayRotation = 0; @Override protected void onCreate(Bundle savedInstanceState) { @@ -118,7 +112,7 @@ public void onDisplayAdded(int displayId) { @Override public void onDisplayChanged(int displayId) { synchronized (this) { - setAndroidOrientation(); + setDisplayRotation(); } } @@ -136,16 +130,12 @@ protected void onStart() { super.onStart(); mSurfaceView.onResume(); - setAndroidOrientation(); - // Set render mode to RENDERMODE_CONTINUOUSLY to force getting onDraw callbacks until // the Tango service is properly set-up and we start getting onFrameAvailable callbacks. mSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); // Check and request camera permission at run time. - if (hasCameraPermission()) { + if (checkAndRequestPermissions()) { bindTangoService(); - } else { - requestCameraPermission(); } } @@ -197,12 +187,16 @@ public void run() { mTango.connect(mConfig); startupTango(); mIsConnected = true; + setDisplayRotation(); } catch (TangoOutOfDateException e) { Log.e(TAG, getString(R.string.exception_out_of_date), e); + showsToastAndFinishOnUiThread(R.string.exception_out_of_date); } catch (TangoErrorException e) { Log.e(TAG, getString(R.string.exception_tango_error), e); + showsToastAndFinishOnUiThread(R.string.exception_tango_error); } catch (TangoInvalidException e) { Log.e(TAG, getString(R.string.exception_tango_invalid), e); + showsToastAndFinishOnUiThread(R.string.exception_tango_invalid); } } } @@ -281,9 +275,6 @@ public void onFrameAvailable(int cameraId) { } } }); - - // Obtain the intrinsic parameters of the color camera. - mIntrinsics = mTango.getCameraIntrinsics(TangoCameraIntrinsics.TANGO_CAMERA_COLOR); } /** @@ -311,9 +302,12 @@ public void onPreFrame(long sceneTime, double deltaTime) { // Set-up scene camera projection to match RGB camera intrinsics. if (!mRenderer.isSceneCameraConfigured()) { + TangoCameraIntrinsics intrinsics = + TangoSupport.getCameraIntrinsicsBasedOnDisplayRotation( + TangoCameraIntrinsics.TANGO_CAMERA_COLOR, + mDisplayRotation); mRenderer.setProjectionMatrix( - projectionMatrixFromCameraIntrinsics(mIntrinsics, - mColorCameraToDisplayAndroidRotation)); + projectionMatrixFromCameraIntrinsics(intrinsics)); } // Connect the camera texture to the OpenGL Texture if necessary // NOTE: When the OpenGL context is recycled, Rajawali may re-generate the @@ -347,7 +341,7 @@ public void onPreFrame(long sceneTime, double deltaTime) { TangoPoseData.COORDINATE_FRAME_AREA_DESCRIPTION, TangoPoseData.COORDINATE_FRAME_CAMERA_COLOR, TangoSupport.TANGO_SUPPORT_ENGINE_OPENGL, - mColorCameraToDisplayAndroidRotation); + mDisplayRotation); if (lastFramePose.statusCode == TangoPoseData.POSE_VALID) { // Update the camera pose from the renderer mRenderer.updateRenderCameraPose(lastFramePose); @@ -391,39 +385,12 @@ public boolean callPreFrame() { mSurfaceView.setSurfaceRenderer(mRenderer); } - private static int getColorCameraToDisplayAndroidRotation(int displayRotation, - int cameraRotation) { - int cameraRotationNormalized = 0; - switch (cameraRotation) { - case 90: - cameraRotationNormalized = 1; - break; - case 180: - cameraRotationNormalized = 2; - break; - case 270: - cameraRotationNormalized = 3; - break; - default: - cameraRotationNormalized = 0; - break; - } - int ret = displayRotation - cameraRotationNormalized; - if (ret < 0) { - ret += 4; - } - return ret; - } - /** * Use Tango camera intrinsics to calculate the projection Matrix for the Rajawali scene. * * @param intrinsics camera instrinsics for computing the project matrix. - * @param rotation the relative rotation between the camera intrinsics and display glContext. */ - private static float[] projectionMatrixFromCameraIntrinsics(TangoCameraIntrinsics intrinsics, - int rotation) { - // Adjust camera intrinsics according to rotation + private static float[] projectionMatrixFromCameraIntrinsics(TangoCameraIntrinsics intrinsics) { float cx = (float) intrinsics.cx; float cy = (float) intrinsics.cy; float width = (float) intrinsics.width; @@ -431,31 +398,6 @@ private static float[] projectionMatrixFromCameraIntrinsics(TangoCameraIntrinsic float fx = (float) intrinsics.fx; float fy = (float) intrinsics.fy; - switch (rotation) { - case Surface.ROTATION_90: - cx = (float) intrinsics.cy; - cy = (float) intrinsics.width - (float) intrinsics.cx; - width = (float) intrinsics.height; - height = (float) intrinsics.width; - fx = (float) intrinsics.fy; - fy = (float) intrinsics.fx; - break; - case Surface.ROTATION_180: - cx = (float) intrinsics.width - cx; - cy = (float) intrinsics.height - cy; - break; - case Surface.ROTATION_270: - cx = (float) intrinsics.height - (float) intrinsics.cy; - cy = (float) intrinsics.cx; - width = (float) intrinsics.height; - height = (float) intrinsics.width; - fx = (float) intrinsics.fy; - fy = (float) intrinsics.fx; - break; - default: - break; - } - // Uses frustumM to create a projection matrix taking into account calibrated camera // intrinsic parameter. // Reference: http://ksimek.github.io/2013/06/03/calibrated_cameras_in_opengl/ @@ -481,23 +423,35 @@ private static float[] projectionMatrixFromCameraIntrinsics(TangoCameraIntrinsic /** * Set the color camera background texture rotation and save the camera to display rotation. */ - private void setAndroidOrientation() { + private void setDisplayRotation() { Display display = getWindowManager().getDefaultDisplay(); - Camera.CameraInfo colorCameraInfo = new Camera.CameraInfo(); - Camera.getCameraInfo(COLOR_CAMERA_ID, colorCameraInfo); + mDisplayRotation = display.getRotation(); - mColorCameraToDisplayAndroidRotation = - getColorCameraToDisplayAndroidRotation(display.getRotation(), - colorCameraInfo.orientation); - // Run this in OpenGL thread. + // We also need to update the camera texture UV coordinates. This must be run in the OpenGL + // thread. mSurfaceView.queueEvent(new Runnable() { @Override public void run() { - mRenderer.updateColorCameraTextureUvGlThread(mColorCameraToDisplayAndroidRotation); + if (mIsConnected) { + mRenderer.updateColorCameraTextureUvGlThread(mDisplayRotation); + } } }); } + /** + * Check we have the necessary permissions for this app, and ask for them if we haven't. + * + * @return True if we have the necessary permissions, false if we haven't. + */ + private boolean checkAndRequestPermissions() { + if (!hasCameraPermission()) { + requestCameraPermission(); + return false; + } + return true; + } + /** * Check we have the necessary permissions for this app. */ @@ -536,6 +490,9 @@ public void onClick(DialogInterface dialogInterface, int i) { dialog.show(); } + /** + * Result for requesting camera permission. + */ @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { @@ -546,4 +503,20 @@ public void onRequestPermissionsResult(int requestCode, String[] permissions, Toast.LENGTH_LONG).show(); } } + + /** + * Display toast on UI thread. + * + * @param resId The resource id of the string resource to use. Can be formatted text. + */ + private void showsToastAndFinishOnUiThread(final int resId) { + runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(AugmentedRealityActivity.this, + getString(resId), Toast.LENGTH_LONG).show(); + finish(); + } + }); + } } diff --git a/java_augmented_reality_example/app/src/main/java/com/projecttango/examples/java/augmentedreality/AugmentedRealityRenderer.java b/java_augmented_reality_example/app/src/main/java/com/projecttango/examples/java/augmentedreality/AugmentedRealityRenderer.java index 0a27ac4b..6e09379c 100644 --- a/java_augmented_reality_example/app/src/main/java/com/projecttango/examples/java/augmentedreality/AugmentedRealityRenderer.java +++ b/java_augmented_reality_example/app/src/main/java/com/projecttango/examples/java/augmentedreality/AugmentedRealityRenderer.java @@ -44,6 +44,8 @@ import javax.microedition.khronos.opengles.GL10; +import com.projecttango.tangosupport.TangoSupport; + /** * Renderer that implements a basic augmented reality scene using Rajawali. * It creates a scene with a background quad taking the whole screen, where the color camera is @@ -54,9 +56,6 @@ public class AugmentedRealityRenderer extends Renderer { private static final String TAG = AugmentedRealityRenderer.class.getSimpleName(); private float[] textureCoords0 = new float[]{0.0F, 1.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F, 0.0F}; - private float[] textureCoords270 = new float[]{1.0F, 1.0F, 0.0F, 1.0F, 1.0F, 0.0F, 0.0F, 0.0F}; - private float[] textureCoords180 = new float[]{1.0F, 0.0F, 1.0F, 1.0F, 0.0F, 0.0F, 0.0F, 1.0F}; - private float[] textureCoords90 = new float[]{0.0F, 0.0F, 1.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F}; // Rajawali texture used to render the Tango color camera. private ATexture mTangoCameraTexture; @@ -172,20 +171,9 @@ public void updateColorCameraTextureUvGlThread(int rotation) { mBackgroundQuad = new ScreenQuad(); } - switch (rotation) { - case Surface.ROTATION_90: - mBackgroundQuad.getGeometry().setTextureCoords(textureCoords90, true); - break; - case Surface.ROTATION_180: - mBackgroundQuad.getGeometry().setTextureCoords(textureCoords180, true); - break; - case Surface.ROTATION_270: - mBackgroundQuad.getGeometry().setTextureCoords(textureCoords270, true); - break; - default: - mBackgroundQuad.getGeometry().setTextureCoords(textureCoords0, true); - break; - } + float[] textureCoords = + TangoSupport.getVideoOverlayUVBasedOnDisplayRotation(textureCoords0, rotation); + mBackgroundQuad.getGeometry().setTextureCoords(textureCoords, true); mBackgroundQuad.getGeometry().reload(); } diff --git a/java_basic_examples/hello_area_description/src/main/java/com/projecttango/examples/java/helloareadescription/HelloAreaDescriptionActivity.java b/java_basic_examples/hello_area_description/src/main/java/com/projecttango/examples/java/helloareadescription/HelloAreaDescriptionActivity.java index 810d4c63..1968da90 100644 --- a/java_basic_examples/hello_area_description/src/main/java/com/projecttango/examples/java/helloareadescription/HelloAreaDescriptionActivity.java +++ b/java_basic_examples/hello_area_description/src/main/java/com/projecttango/examples/java/helloareadescription/HelloAreaDescriptionActivity.java @@ -103,14 +103,18 @@ public void run() { startupTango(); } catch (TangoOutOfDateException e) { Log.e(TAG, getString(R.string.tango_out_of_date_exception), e); + showsToastAndFinishOnUiThread(R.string.tango_out_of_date_exception); } catch (TangoErrorException e) { Log.e(TAG, getString(R.string.tango_error), e); + showsToastAndFinishOnUiThread(R.string.tango_error); } catch (TangoInvalidException e) { Log.e(TAG, getString(R.string.tango_invalid), e); + showsToastAndFinishOnUiThread(R.string.tango_invalid); } catch (SecurityException e) { // Area Learning permissions are required. If they are not available, // SecurityException is thrown. Log.e(TAG, getString(R.string.no_permissions), e); + showsToastAndFinishOnUiThread(R.string.no_permissions); } } @@ -358,4 +362,20 @@ private void showSetAdfNameDialog() { setAdfNameDialog.setArguments(bundle); setAdfNameDialog.show(manager, "ADFNameDialog"); } + + /** + * Display toast on UI thread. + * + * @param resId The resource id of the string resource to use. Can be formatted text. + */ + private void showsToastAndFinishOnUiThread(final int resId) { + runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(HelloAreaDescriptionActivity.this, + getString(resId), Toast.LENGTH_LONG).show(); + finish(); + } + }); + } } diff --git a/java_basic_examples/hello_depth_perception/src/main/java/com/projecttango/examples/java/hellodepthperception/HelloDepthPerceptionActivity.java b/java_basic_examples/hello_depth_perception/src/main/java/com/projecttango/examples/java/hellodepthperception/HelloDepthPerceptionActivity.java index 1eae6ae0..5c61e5d7 100644 --- a/java_basic_examples/hello_depth_perception/src/main/java/com/projecttango/examples/java/hellodepthperception/HelloDepthPerceptionActivity.java +++ b/java_basic_examples/hello_depth_perception/src/main/java/com/projecttango/examples/java/hellodepthperception/HelloDepthPerceptionActivity.java @@ -31,6 +31,7 @@ import android.app.Activity; import android.os.Bundle; import android.util.Log; +import android.widget.Toast; import java.nio.FloatBuffer; import java.util.ArrayList; @@ -73,10 +74,13 @@ public void run() { startupTango(); } catch (TangoOutOfDateException e) { Log.e(TAG, getString(R.string.exception_out_of_date), e); + showsToastAndFinishOnUiThread(R.string.exception_out_of_date); } catch (TangoErrorException e) { Log.e(TAG, getString(R.string.exception_tango_error), e); + showsToastAndFinishOnUiThread(R.string.exception_tango_error); } catch (TangoInvalidException e) { Log.e(TAG, getString(R.string.exception_tango_invalid), e); + showsToastAndFinishOnUiThread(R.string.exception_tango_invalid); } } } @@ -177,4 +181,20 @@ private float calculateAveragedDepth(FloatBuffer pointCloudBuffer, int numPoints } return averageZ; } + + /** + * Display toast on UI thread. + * + * @param resId The resource id of the string resource to use. Can be formatted text. + */ + private void showsToastAndFinishOnUiThread(final int resId) { + runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(HelloDepthPerceptionActivity.this, + getString(resId), Toast.LENGTH_LONG).show(); + finish(); + } + }); + } } diff --git a/java_basic_examples/hello_motion_tracking/src/main/java/com/projecttango/examples/java/hellomotiontracking/HelloMotionTrackingActivity.java b/java_basic_examples/hello_motion_tracking/src/main/java/com/projecttango/examples/java/hellomotiontracking/HelloMotionTrackingActivity.java index c4c7280d..ab24a1d0 100644 --- a/java_basic_examples/hello_motion_tracking/src/main/java/com/projecttango/examples/java/hellomotiontracking/HelloMotionTrackingActivity.java +++ b/java_basic_examples/hello_motion_tracking/src/main/java/com/projecttango/examples/java/hellomotiontracking/HelloMotionTrackingActivity.java @@ -31,6 +31,7 @@ import android.app.Activity; import android.os.Bundle; import android.util.Log; +import android.widget.Toast; import java.util.ArrayList; @@ -72,10 +73,13 @@ public void run() { startupTango(); } catch (TangoOutOfDateException e) { Log.e(TAG, getString(R.string.exception_out_of_date), e); + showsToastAndFinishOnUiThread(R.string.exception_out_of_date); } catch (TangoErrorException e) { Log.e(TAG, getString(R.string.exception_tango_error), e); + showsToastAndFinishOnUiThread(R.string.exception_tango_error); } catch (TangoInvalidException e) { Log.e(TAG, getString(R.string.exception_tango_invalid), e); + showsToastAndFinishOnUiThread(R.string.exception_tango_invalid); } } } @@ -170,4 +174,20 @@ private void logPose(TangoPoseData pose) { Log.i(TAG, stringBuilder.toString()); } + + /** + * Display toast on UI thread. + * + * @param resId The resource id of the string resource to use. Can be formatted text. + */ + private void showsToastAndFinishOnUiThread(final int resId) { + runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(HelloMotionTrackingActivity.this, + getString(resId), Toast.LENGTH_LONG).show(); + finish(); + } + }); + } } diff --git a/java_basic_examples/hello_video/build.gradle b/java_basic_examples/hello_video/build.gradle index 3fc5b332..2279a50c 100644 --- a/java_basic_examples/hello_video/build.gradle +++ b/java_basic_examples/hello_video/build.gradle @@ -31,6 +31,13 @@ if (project.hasProperty("Tango.catkin_devel_prefix")) { external_lib_prefix = "../../TangoReleaseLibs" } +repositories { + flatDir { + dirs external_lib_prefix + '/aar' + } +} + dependencies { compile fileTree(dir: external_lib_prefix + '/jar', include: ['**/*.jar']) + compile (name: 'tango_support_java_lib', ext: 'aar') } diff --git a/java_basic_examples/hello_video/src/main/java/com/projecttango/examples/java/hellovideo/HelloVideoActivity.java b/java_basic_examples/hello_video/src/main/java/com/projecttango/examples/java/hellovideo/HelloVideoActivity.java index 32ed1f6d..0987a329 100644 --- a/java_basic_examples/hello_video/src/main/java/com/projecttango/examples/java/hellovideo/HelloVideoActivity.java +++ b/java_basic_examples/hello_video/src/main/java/com/projecttango/examples/java/hellovideo/HelloVideoActivity.java @@ -30,17 +30,19 @@ import com.google.atap.tangoservice.TangoXyzIjData; import android.app.Activity; -import android.hardware.Camera; import android.hardware.display.DisplayManager; import android.opengl.GLSurfaceView; import android.os.Bundle; import android.util.Log; import android.view.Display; import android.widget.TextView; +import android.widget.Toast; import java.util.ArrayList; import java.util.concurrent.atomic.AtomicBoolean; +import com.projecttango.tangosupport.TangoSupport; + /** * This is a stripped down simple example that shows how to use the Tango APIs to render the Tango * RGB camera into an OpenGL texture. @@ -58,8 +60,6 @@ public class HelloVideoActivity extends Activity { private static final String TAG = HelloVideoActivity.class.getSimpleName(); private static final int INVALID_TEXTURE_ID = 0; - // For all current Tango devices, color camera is in the camera id 0. - private static final int COLOR_CAMERA_ID = 0; private static final String sTimestampFormat = "Timestamp: %f"; private GLSurfaceView mSurfaceView; @@ -74,7 +74,7 @@ public class HelloVideoActivity extends Activity { private int mConnectedTextureIdGlThread = INVALID_TEXTURE_ID; private AtomicBoolean mIsFrameAvailableTangoThread = new AtomicBoolean(false); - private int mColorCameraToDisplayAndroidRotation = 0; + private int mDisplayRotation = 0; @Override protected void onCreate(Bundle savedInstanceState) { @@ -87,17 +87,19 @@ protected void onCreate(Bundle savedInstanceState) { if (displayManager != null) { displayManager.registerDisplayListener(new DisplayManager.DisplayListener() { @Override - public void onDisplayAdded(int displayId) {} + public void onDisplayAdded(int displayId) { + } @Override public void onDisplayChanged(int displayId) { synchronized (this) { - setAndroidOrientation(); + setDisplayRotation(); } } @Override - public void onDisplayRemoved(int displayId) {} + public void onDisplayRemoved(int displayId) { + } }, null); } // Set-up a dummy OpenGL renderer associated with this surface view @@ -109,8 +111,6 @@ protected void onResume() { super.onResume(); mSurfaceView.onResume(); - setAndroidOrientation(); - // Set render mode to RENDERMODE_CONTINUOUSLY to force getting onDraw callbacks until the // Tango service is properly set-up and we start getting onFrameAvailable callbacks. mSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); @@ -129,16 +129,21 @@ public void run() { // the OpenGL thread or in the UI thread. synchronized (HelloVideoActivity.this) { try { + TangoSupport.initialize(); mConfig = setupTangoConfig(mTango); mTango.connect(mConfig); startupTango(); mIsConnected = true; + setDisplayRotation(); } catch (TangoOutOfDateException e) { Log.e(TAG, getString(R.string.exception_out_of_date), e); + showsToastAndFinishOnUiThread(R.string.exception_out_of_date); } catch (TangoErrorException e) { Log.e(TAG, getString(R.string.exception_tango_error), e); + showsToastAndFinishOnUiThread(R.string.exception_tango_error); } catch (TangoInvalidException e) { Log.e(TAG, getString(R.string.exception_tango_invalid), e); + showsToastAndFinishOnUiThread(R.string.exception_tango_invalid); } } } @@ -318,46 +323,37 @@ public void run() { mSurfaceView.setRenderer(mRenderer); } - private static int getColorCameraToDisplayAndroidRotation(int displayRotation, - int cameraRotation) { - int cameraRotationNormalized = 0; - switch (cameraRotation) { - case 90: - cameraRotationNormalized = 1; - break; - case 180: - cameraRotationNormalized = 2; - break; - case 270: - cameraRotationNormalized = 3; - break; - default: - cameraRotationNormalized = 0; - break; - } - int ret = displayRotation - cameraRotationNormalized; - if (ret < 0) { - ret += 4; - } - return ret; - } - /** * Set the color camera background texture rotation and save the camera to display rotation. */ - private void setAndroidOrientation() { + private void setDisplayRotation() { Display display = getWindowManager().getDefaultDisplay(); - Camera.CameraInfo colorCameraInfo = new Camera.CameraInfo(); - Camera.getCameraInfo(COLOR_CAMERA_ID, colorCameraInfo); + mDisplayRotation = display.getRotation(); - mColorCameraToDisplayAndroidRotation = - getColorCameraToDisplayAndroidRotation(display.getRotation(), - colorCameraInfo.orientation); - // Run this in the OpenGL thread. + // We also need to update the camera texture UV coordinates. This must be run in the OpenGL + // thread. mSurfaceView.queueEvent(new Runnable() { @Override public void run() { - mRenderer.updateColorCameraTextureUv(mColorCameraToDisplayAndroidRotation); + if (mIsConnected) { + mRenderer.updateColorCameraTextureUv(mDisplayRotation); + } + } + }); + } + + /** + * Display toast on UI thread. + * + * @param resId The resource id of the string resource to use. Can be formatted text. + */ + private void showsToastAndFinishOnUiThread(final int resId) { + runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(HelloVideoActivity.this, + getString(resId), Toast.LENGTH_LONG).show(); + finish(); } }); } diff --git a/java_basic_examples/hello_video/src/main/java/com/projecttango/examples/java/hellovideo/HelloVideoRenderer.java b/java_basic_examples/hello_video/src/main/java/com/projecttango/examples/java/hellovideo/HelloVideoRenderer.java index 70b97282..f3ab051c 100644 --- a/java_basic_examples/hello_video/src/main/java/com/projecttango/examples/java/hellovideo/HelloVideoRenderer.java +++ b/java_basic_examples/hello_video/src/main/java/com/projecttango/examples/java/hellovideo/HelloVideoRenderer.java @@ -19,7 +19,6 @@ import android.opengl.GLES20; import android.opengl.GLSurfaceView; import android.util.Log; -import android.view.Surface; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -29,6 +28,8 @@ import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; +import com.projecttango.tangosupport.TangoSupport; + /** * A simple OpenGL renderer that renders the Tango RGB camera texture on a full-screen background. */ @@ -55,12 +56,6 @@ public class HelloVideoRenderer implements GLSurfaceView.Renderer { private final float[] textureCoords0 = new float[]{1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f}; - private final float[] textureCoords270 = - new float[]{1.0F, 0.0F, 1.0F, 1.0F, 0.0F, 0.0F, 0.0F, 1.0F}; - private final float[] textureCoords180 = - new float[]{0.0F, 0.0F, 1.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F}; - private final float[] textureCoords90 = - new float[]{0.0F, 1.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F, 0.0F}; /** * A small callback to allow the caller to introduce application-specific code to be executed @@ -102,20 +97,9 @@ public HelloVideoRenderer(RenderCallback callback) { } public void updateColorCameraTextureUv(int rotation){ - switch (rotation) { - case Surface.ROTATION_90: - setTextureCoords(textureCoords90); - break; - case Surface.ROTATION_180: - setTextureCoords(textureCoords180); - break; - case Surface.ROTATION_270: - setTextureCoords(textureCoords270); - break; - default: - setTextureCoords(textureCoords0); - break; - } + float[] textureCoords = + TangoSupport.getVideoOverlayUVBasedOnDisplayRotation(textureCoords0, rotation); + setTextureCoords(textureCoords); } private void setTextureCoords(float[] textureCoords) { diff --git a/java_floor_plan_example/app/src/main/java/com/projecttango/examples/java/floorplan/Floorplan.java b/java_floor_plan_example/app/src/main/java/com/projecttango/examples/java/floorplan/Floorplan.java deleted file mode 100644 index 0739271a..00000000 --- a/java_floor_plan_example/app/src/main/java/com/projecttango/examples/java/floorplan/Floorplan.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright 2016 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.projecttango.examples.java.floorplan; - -import android.graphics.Canvas; -import android.graphics.Paint; - -import org.rajawali3d.math.vector.Vector3; - -import java.util.ArrayList; -import java.util.List; - -/** - * Domain class that represents a floor plan. - * It is represented as a set of points that are joined consecutively by straight lines. - */ -public class Floorplan { - - /** - * Padding factor only used for the representation in 2D in a canvas. - */ - private static final float RENDER_PADDING_SCALE_FACTOR = 0.8f; - - /** - * The list of points of the plan. Each point is a float[3] with x, y and z coordinates. - */ - private List mPlanPoints = new ArrayList(); - - public Floorplan(List planPoints) { - mPlanPoints.addAll(planPoints); - } - - /** - * Draw a 2D representation of the plan with text labels for the length of each wall. - * - * @param canvas The Canvas to draw the plan on. - * @param paint The Paint object describing the colors and styles for the plan. - */ - public void drawOnCanvas(Canvas canvas, Paint paint) { - draw2dlines(canvas, paint); - drawTexts(canvas, paint); - } - - /** - * Draws a 2D representation of the floor plan on a canvas. The plan is scaled to fill - * the whole canvas. The {@code RENDER_PADDING_SCALE_FACTOR} is used to leave some blank - * space around the borders of the canvas. - */ - private void draw2dlines(Canvas canvas, Paint paint) { - // Get center of the canvas. - int centerX = canvas.getWidth() / 2; - int centerY = canvas.getHeight() / 2; - float[] lines = new float[4 * mPlanPoints.size()]; - int i = 0; - if (!mPlanPoints.isEmpty()) { - float[] lastPoint = null; - Floorplan scaledFloorplan = translateAndScalePlan(canvas); - // For every point add a line to the last point. Start from the center of the canvas. - for (float[] nextPoint : scaledFloorplan.mPlanPoints) { - if (lastPoint != null) { - lines[i++] = centerX + lastPoint[0]; - lines[i++] = centerY + lastPoint[2]; - lines[i++] = centerX + nextPoint[0]; - lines[i++] = centerY + nextPoint[2]; - } - lastPoint = nextPoint; - } - lines[i++] = centerX + lastPoint[0]; - lines[i++] = centerY + lastPoint[2]; - lines[i++] = centerX + scaledFloorplan.mPlanPoints.get(0)[0]; - lines[i++] = centerY + scaledFloorplan.mPlanPoints.get(0)[2]; - } - canvas.drawLines(lines, paint); - } - - /** - * Draw text labels for the length of each wall. - */ - private void drawTexts(Canvas canvas, Paint paint) { - int centerX = canvas.getWidth() / 2; - int centerY = canvas.getHeight() / 2; - if (!mPlanPoints.isEmpty()) { - float[] lastPoint = null; - float[] nextPoint; - float[] lastScaledPoint = null; - float[] nextScaledPoint; - Floorplan scaledFloorplan = translateAndScalePlan(canvas); - for (int i = 0; i < mPlanPoints.size(); i++) { - nextPoint = mPlanPoints.get(i); - nextScaledPoint = scaledFloorplan.mPlanPoints.get(i); - if (lastPoint != null) { - // Get the length of the original unscaled plan. - double length = Math.sqrt( - (lastPoint[0] - nextPoint[0]) * (lastPoint[0] - nextPoint[0]) + - (lastPoint[2] - nextPoint[2]) * (lastPoint[2] - nextPoint[2])); - // Draw the label in the middle of each wall. - double posX = centerX + (lastScaledPoint[0] + nextScaledPoint[0]) / 2; - double posY = centerY + (lastScaledPoint[2] + nextScaledPoint[2]) / 2; - canvas.drawText(String.format("%.2f", length) + "m", (float) posX, (float) - posY, paint); - } - lastPoint = nextPoint; - lastScaledPoint = nextScaledPoint; - } - // Get the length of the original unscaled plan. - double length = Math.sqrt( - (lastPoint[0] - mPlanPoints.get(0)[0]) * (lastPoint[0] - mPlanPoints.get(0)[0]) - + (lastPoint[2] - mPlanPoints.get(0)[2]) * - (lastPoint[2] - mPlanPoints.get(0)[2])); - // Draw the label in the middle of each wall. - double posX = centerX + (lastScaledPoint[0] + scaledFloorplan.mPlanPoints.get(0)[0]) - / 2; - double posY = centerY + (lastScaledPoint[2] + scaledFloorplan.mPlanPoints.get(0)[2]) - / 2; - canvas.drawText(String.format("%.2f", length) + "m", (float) posX, (float) posY, paint); - } - } - - /** - * Translate the whole plan to be centered at the origin and scale it to fill the canvas. - */ - private Floorplan translateAndScalePlan(Canvas canvas) { - // Get center of the plan. - float[] planCenter = getPlanCenter(); - // Get scale factor of the plan. - float scale = getPlanScale(canvas.getHeight(), canvas.getWidth()); - List scaledPoints = new ArrayList(); - for (float[] nextPoint : mPlanPoints) { - float[] newPoint = new float[3]; - for (int i = 0; i < 3; i++) { - newPoint[i] = (nextPoint[i] - planCenter[i]) * scale; - } - scaledPoints.add(newPoint); - } - - return new Floorplan(scaledPoints); - } - - /** - * Get the center point of the plan. - */ - private float[] getPlanCenter() { - float[] bounds = getPlanBounds(); - return new float[]{(bounds[0] + bounds[1]) / 2, (bounds[2] + bounds[3]) / 2, - (bounds[4] + bounds[5]) / 2}; - } - - /** - * Get the scale of the plan. - * The scale is the factor the plan should be multiplied by to fill the whole canvas. - */ - private float getPlanScale(int height, int width) { - float[] bounds = getPlanBounds(); - float xScale = RENDER_PADDING_SCALE_FACTOR * width / (bounds[1] - bounds[0]); - float zScale = RENDER_PADDING_SCALE_FACTOR * height / (bounds[5] - bounds[4]); - return xScale < zScale ? xScale : zScale; - } - - /** - * Get the bounds of the plan. - */ - private float[] getPlanBounds() { - float xStart = Float.MAX_VALUE; - float yStart = Float.MAX_VALUE; - float zStart = Float.MAX_VALUE; - float xEnd = Float.MIN_VALUE; - float yEnd = Float.MIN_VALUE; - float zEnd = Float.MIN_VALUE; - for (float[] point : mPlanPoints) { - if (point[0] < xStart) { - xStart = point[0]; - } - if (point[0] > xEnd) { - xEnd = point[0]; - } - if (point[1] < yStart) { - yStart = point[1]; - } - if (point[1] > yEnd) { - yEnd = point[1]; - } - if (point[2] < zStart) { - zStart = point[2]; - } - if (point[2] > zEnd) { - zEnd = point[2]; - } - } - return new float[]{xStart, xEnd, yStart, yEnd, zStart, zEnd}; - } - - public List getPlanPoints() { - return new ArrayList(mPlanPoints); - } -} diff --git a/java_floor_plan_example/app/src/main/java/com/projecttango/examples/java/floorplan/FloorplanActivity.java b/java_floor_plan_example/app/src/main/java/com/projecttango/examples/java/floorplan/FloorplanActivity.java deleted file mode 100644 index 3abaa65b..00000000 --- a/java_floor_plan_example/app/src/main/java/com/projecttango/examples/java/floorplan/FloorplanActivity.java +++ /dev/null @@ -1,859 +0,0 @@ -/* - * Copyright 2016 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.projecttango.examples.java.floorplan; - -import com.google.atap.tangoservice.Tango; -import com.google.atap.tangoservice.Tango.OnTangoUpdateListener; -import com.google.atap.tangoservice.TangoCameraIntrinsics; -import com.google.atap.tangoservice.TangoConfig; -import com.google.atap.tangoservice.TangoCoordinateFramePair; -import com.google.atap.tangoservice.TangoErrorException; -import com.google.atap.tangoservice.TangoEvent; -import com.google.atap.tangoservice.TangoException; -import com.google.atap.tangoservice.TangoInvalidException; -import com.google.atap.tangoservice.TangoOutOfDateException; -import com.google.atap.tangoservice.TangoPointCloudData; -import com.google.atap.tangoservice.TangoPoseData; -import com.google.atap.tangoservice.TangoXyzIjData; - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.hardware.Camera; -import android.hardware.display.DisplayManager; -import android.opengl.Matrix; -import android.os.AsyncTask; -import android.os.Bundle; -import android.util.Log; -import android.view.Display; -import android.view.MotionEvent; -import android.view.Surface; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.RelativeLayout; -import android.widget.Toast; - -import org.rajawali3d.scene.ASceneFrameCallback; -import org.rajawali3d.view.SurfaceView; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; - -import com.projecttango.tangosupport.TangoPointCloudManager; -import com.projecttango.tangosupport.TangoSupport; -import com.projecttango.tangosupport.TangoSupport.IntersectionPointPlaneModelPair; - -/** - * An example showing how to build a very simple application that allows the user to create a floor - * plan in Java. It uses TangoSupportLibrary to do plane fitting using the point cloud data. - * When the user clicks on the display, plane detection is done on the surface at the location of - * the click and a 3D object will be placed in the scene anchored at that location. A - * {@code WallMeasurement} will be recorded for that plane. - *

- * You need to take exactly one measurement per wall in clockwise order. As you take measurements, - * the perimeter of the floor plan will be displayed as lines in AR. After you have taken all the - * measurements you can press the 'Done' button and the final result will be drawn in 2D as seen - * from above along with labels showing the sizes of the walls. - *

- * You are going to be building an ADF as you take the measurements. After pressing the 'Done' - * button the ADF will be saved and an optimization will be run on it. After that, all the recorded - * measurements are re-queried and the floor plan will be rebuilt in order to have better precision. - *

- * Note that it is important to include the KEY_BOOLEAN_LOWLATENCYIMUINTEGRATION configuration - * parameter in order to achieve the best results synchronizing the Rajawali virtual world with the - * RGB camera. - *

- * For more details on the augmented reality effects, including color camera texture rendering, - * see java_augmented_reality_example or java_hello_video_example. - */ -public class FloorplanActivity extends Activity implements View.OnTouchListener { - private static final String TAG = FloorplanActivity.class.getSimpleName(); - private static final int INVALID_TEXTURE_ID = 0; - // For all current Tango devices, color camera is in the camera id 0. - private static final int COLOR_CAMERA_ID = 0; - - private SurfaceView mSurfaceView; - private FloorplanRenderer mRenderer; - private TangoCameraIntrinsics mIntrinsics; - private TangoPointCloudManager mPointCloudManager; - private Tango mTango; - private TangoConfig mConfig; - private boolean mIsConnected = false; - private double mCameraPoseTimestamp = 0; - private List mWallMeasurementList; - private Floorplan mFloorplan; - private FinishPlanTask mFinishPlanTask; - private Button mDoneButton; - private ViewGroup mProgressGroup; - - // Texture rendering related fields - // NOTE: Naming indicates which thread is in charge of updating this variable - private int mConnectedTextureIdGlThread = INVALID_TEXTURE_ID; - private AtomicBoolean mIsFrameAvailableTangoThread = new AtomicBoolean(false); - private double mRgbTimestampGlThread; - - private int mColorCameraToDisplayAndroidRotation = 0; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - - mSurfaceView = (SurfaceView) findViewById(R.id.ar_view); - mRenderer = new FloorplanRenderer(this); - mSurfaceView.setSurfaceRenderer(mRenderer); - mSurfaceView.setOnTouchListener(this); - // Set ZOrderOnTop to false so the other views don't get hidden by the SurfaceView. - mSurfaceView.setZOrderOnTop(false); - mProgressGroup = (ViewGroup) findViewById(R.id.progress_group); - mPointCloudManager = new TangoPointCloudManager(); - mDoneButton = (Button) findViewById(R.id.done_button); - - DisplayManager displayManager = (DisplayManager) getSystemService(DISPLAY_SERVICE); - if (displayManager != null) { - displayManager.registerDisplayListener(new DisplayManager.DisplayListener() { - @Override - public void onDisplayAdded(int displayId) {} - - @Override - public void onDisplayChanged(int displayId) { - synchronized (this) { - setAndroidOrientation(); - } - } - - @Override - public void onDisplayRemoved(int displayId) {} - }, null); - } - } - - @Override - protected void onResume() { - super.onResume(); - - setAndroidOrientation(); - - // Check if it has permissions. - // Area learning permissions are needed in order to save the adf. - if (Tango.hasPermission(this, Tango.PERMISSIONTYPE_ADF_LOAD_SAVE)) { - // Initialize Tango Service as a normal Android Service, since we call - // mTango.disconnect() in onPause, this will unbind Tango Service, so - // every time when onResume get called, we should create a new Tango object. - mTango = new Tango(FloorplanActivity.this, new Runnable() { - // Pass in a Runnable to be called from UI thread when Tango is ready, - // this Runnable will be running on a new thread. - // When Tango is ready, we can call Tango functions safely here only - // when there is no UI thread changes involved. - @Override - public void run() { - synchronized (FloorplanActivity.this) { - try { - TangoSupport.initialize(); - mConfig = setupTangoConfig(mTango); - mTango.connect(mConfig); - startupTango(); - connectRenderer(); - mIsConnected = true; - } catch (TangoOutOfDateException e) { - Log.e(TAG, getString(R.string.exception_out_of_date), e); - } catch (TangoErrorException e) { - Log.e(TAG, getString(R.string.exception_tango_error), e); - } catch (TangoInvalidException e) { - Log.e(TAG, getString(R.string.exception_tango_invalid), e); - } catch (SecurityException e) { - // Area Learning permissions are required. If they are not available, - // SecurityException is thrown. - Log.e(TAG, getString(R.string.failed_permissions), e); - } - } - } - }); - - // When connecting, reset the plan. The old measurements don't make sense. - resetRenderer(); - } else { - startActivityForResult( - Tango.getRequestPermissionIntent(Tango.PERMISSIONTYPE_ADF_LOAD_SAVE), - Tango.TANGO_INTENT_ACTIVITYCODE); - } - } - - @Override - protected void onPause() { - super.onPause(); - - // Synchronize against disconnecting while the service is being used in the OpenGL thread or - // in the UI thread. - synchronized (this) { - try { - if (mIsConnected) { - mRenderer.getCurrentScene().clearFrameCallbacks(); - mTango.disconnectCamera(TangoCameraIntrinsics.TANGO_CAMERA_COLOR); - // We need to invalidate the connected texture ID so that we cause a - // re-connection - // in the OpenGL thread after resume - mConnectedTextureIdGlThread = INVALID_TEXTURE_ID; - mTango.disconnect(); - mIsConnected = false; - } - } catch (TangoErrorException e) { - Log.e(TAG, getString(R.string.exception_tango_error), e); - } - } - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - // Check which request we're responding to - if (requestCode == Tango.TANGO_INTENT_ACTIVITYCODE) { - // Make sure the request was successful - if (resultCode == RESULT_CANCELED) { - Toast.makeText(this, "Area Learning Permissions Required!", - Toast.LENGTH_SHORT).show(); - finish(); - } - } - } - - /** - * Sets up the tango configuration object. Make sure mTango object is initialized before - * making this call. - */ - private TangoConfig setupTangoConfig(Tango tango) { - // Use default configuration for Tango Service, plus low latency - // IMU integration, depth, color camera and area learning. - TangoConfig config = mTango.getConfig(TangoConfig.CONFIG_TYPE_DEFAULT); - // NOTE: Low latency integration is necessary to achieve a precise alignment of virtual - // objects with the RBG image and produce a good AR effect. - config.putBoolean(TangoConfig.KEY_BOOLEAN_LOWLATENCYIMUINTEGRATION, true); - config.putBoolean(TangoConfig.KEY_BOOLEAN_DEPTH, true); - config.putInt(TangoConfig.KEY_INT_DEPTH_MODE, TangoConfig.TANGO_DEPTH_MODE_POINT_CLOUD); - // NOTE: Area learning is necessary to achieve better precision is pose estimation - config.putBoolean(TangoConfig.KEY_BOOLEAN_LEARNINGMODE, true); - config.putBoolean(TangoConfig.KEY_BOOLEAN_COLORCAMERA, true); - return config; - } - - /** - * Set up the callback listeners for the Tango service and obtain other parameters required - * after Tango connection. - * Listen to updates from the RGB camera and Point Cloud. - */ - private void startupTango() { - // No need to add any coordinate frame pairs since we are not - // using pose data. So just initialize. - ArrayList framePairs = new ArrayList(); - mTango.connectListener(framePairs, new OnTangoUpdateListener() { - @Override - public void onPoseAvailable(TangoPoseData pose) { - // We are not using OnPoseAvailable for this app. - } - - @Override - public void onFrameAvailable(int cameraId) { - // Check if the frame available is for the camera we want and update its frame - // on the view. - if (cameraId == TangoCameraIntrinsics.TANGO_CAMERA_COLOR) { - // Mark a camera frame is available for rendering in the OpenGL thread - mIsFrameAvailableTangoThread.set(true); - mSurfaceView.requestRender(); - } - } - - @Override - public void onXyzIjAvailable(TangoXyzIjData xyzIj) { - // We are not using onXyzIjAvailable for this app. - } - - @Override - public void onPointCloudAvailable(TangoPointCloudData pointCloud) { - // Save the cloud and point data for later use. - mPointCloudManager.updatePointCloud(pointCloud); - } - - @Override - public void onTangoEvent(TangoEvent event) { - // We are not using OnTangoEvent for this app. - } - }); - - // Obtain the intrinsic parameters of the color camera. - mIntrinsics = mTango.getCameraIntrinsics(TangoCameraIntrinsics.TANGO_CAMERA_COLOR); - } - - /** - * Resets the status every time we connect to the service. The old measurements - * don't make sense. - */ - private synchronized void resetRenderer() { - mWallMeasurementList = new ArrayList(); - mRenderer.removeMeasurements(); - mRenderer.updatePlan(new Floorplan(new ArrayList())); - } - - /** - * Connects the view and renderer to the color camara and callbacks. - */ - private void connectRenderer() { - // Register a Rajawali Scene Frame Callback to update the scene camera pose whenever a new - // RGB frame is rendered. - // (@see https://github.com/Rajawali/Rajawali/wiki/Scene-Frame-Callbacks) - mRenderer.getCurrentScene().registerFrameCallback(new ASceneFrameCallback() { - @Override - public void onPreFrame(long sceneTime, double deltaTime) { - // NOTE: This is called from the OpenGL render thread, after all the renderer - // onRender callbacks had a chance to run and before scene objects are rendered - // into the scene. - - // Prevent concurrent access to {@code mIsFrameAvailableTangoThread} from the Tango - // callback thread and service disconnection from an onPause event. - try { - synchronized (FloorplanActivity.this) { - // Don't execute any tango API actions if we're not connected to the service - if (!mIsConnected) { - return; - } - - // Set-up scene camera projection to match RGB camera intrinsics - if (!mRenderer.isSceneCameraConfigured()) { - mRenderer.setProjectionMatrix( - projectionMatrixFromCameraIntrinsics(mIntrinsics, - mColorCameraToDisplayAndroidRotation)); - } - - // Connect the camera texture to the OpenGL Texture if necessary - // NOTE: When the OpenGL context is recycled, Rajawali may re-generate the - // texture with a different ID. - if (mConnectedTextureIdGlThread != mRenderer.getTextureId()) { - mTango.connectTextureId(TangoCameraIntrinsics.TANGO_CAMERA_COLOR, - mRenderer.getTextureId()); - mConnectedTextureIdGlThread = mRenderer.getTextureId(); - Log.d(TAG, "connected to texture id: " + mRenderer.getTextureId()); - } - - // If there is a new RGB camera frame available, update the texture with it - if (mIsFrameAvailableTangoThread.compareAndSet(true, false)) { - mRgbTimestampGlThread = - mTango.updateTexture(TangoCameraIntrinsics.TANGO_CAMERA_COLOR); - } - - // If a new RGB frame has been rendered, update the camera pose to match. - if (mRgbTimestampGlThread > mCameraPoseTimestamp) { - // Calculate the camera color pose at the camera frame update time in - // OpenGL engine. - TangoPoseData lastFramePose = TangoSupport.getPoseAtTime( - mRgbTimestampGlThread, - TangoPoseData.COORDINATE_FRAME_AREA_DESCRIPTION, - TangoPoseData.COORDINATE_FRAME_CAMERA_COLOR, - TangoSupport.TANGO_SUPPORT_ENGINE_OPENGL, - mColorCameraToDisplayAndroidRotation); - if (lastFramePose.statusCode == TangoPoseData.POSE_VALID) { - // Update the camera pose from the renderer - mRenderer.updateRenderCameraPose(lastFramePose); - mCameraPoseTimestamp = lastFramePose.timestamp; - } else { - Log.w(TAG, "Can't get device pose at time: " + - mRgbTimestampGlThread); - } - } - } - // Avoid crashing the application due to unhandled exceptions - } catch (TangoErrorException e) { - Log.e(TAG, "Tango API call error within the OpenGL render thread", e); - } catch (Throwable t) { - Log.e(TAG, "Exception on the OpenGL thread", t); - } - } - - @Override - public void onPreDraw(long sceneTime, double deltaTime) { - - } - - @Override - public void onPostFrame(long sceneTime, double deltaTime) { - - } - - @Override - public boolean callPreFrame() { - return true; - } - }); - } - - /** - * Use Tango camera intrinsics to calculate the projection Matrix for the Rajawali scene. - * The function also rotates the intrinsics based on current rotation from color camera to - * display. - */ - private static float[] projectionMatrixFromCameraIntrinsics(TangoCameraIntrinsics intrinsics, - int rotation) { - // Uses frustumM to create a projection matrix taking into account calibrated camera - // intrinsic parameter. - // Reference: http://ksimek.github.io/2013/06/03/calibrated_cameras_in_opengl/ - float near = 0.1f; - float far = 100; - - // Adjust camera intrinsics according to rotation from color camera to display. - double cx = intrinsics.cx; - double cy = intrinsics.cy; - double width = intrinsics.width; - double height = intrinsics.height; - double fx = intrinsics.fx; - double fy = intrinsics.fy; - - switch (rotation) { - case Surface.ROTATION_90: - cx = intrinsics.cy; - cy = intrinsics.width - intrinsics.cx; - width = intrinsics.height; - height = intrinsics.width; - fx = intrinsics.fy; - fy = intrinsics.fx; - break; - case Surface.ROTATION_180: - cx = intrinsics.width - cx; - cy = intrinsics.height - cy; - break; - case Surface.ROTATION_270: - cx = intrinsics.height - intrinsics.cy; - cy = intrinsics.cx; - width = intrinsics.height; - height = intrinsics.width; - fx = intrinsics.fy; - fy = intrinsics.fx; - break; - default: - break; - } - - double xscale = near / fx; - double yscale = near / fy; - - double xoffset = (cx - (width / 2.0)) * xscale; - // Color camera's coordinates has y pointing downwards so we negate this term. - double yoffset = -(cy - (height / 2.0)) * yscale; - - float m[] = new float[16]; - Matrix.frustumM(m, 0, - (float) (xscale * -width / 2.0 - xoffset), - (float) (xscale * width / 2.0 - xoffset), - (float) (yscale * -height / 2.0 - yoffset), - (float) (yscale * height / 2.0 - yoffset), near, far); - return m; - } - - /** - * This method handles when the user clicks the screen. It will try to fit a plane to the - * clicked point using depth data. The floor plan will be rebuilt and the result will be shown - * in AR. - */ - @Override - public boolean onTouch(View view, MotionEvent motionEvent) { - if (motionEvent.getAction() == MotionEvent.ACTION_UP) { - // Calculate click location in u,v (0;1) coordinates. - float u = motionEvent.getX() / view.getWidth(); - float v = motionEvent.getY() / view.getHeight(); - - try { - // Take a wall measurement by fitting a plane on the clicked point using the latest - // point cloud data. - // Synchronize against concurrent access to the RGB timestamp in the OpenGL thread - // and a possible service disconnection due to an onPause event. - WallMeasurement wallMeasurement; - synchronized (this) { - wallMeasurement = doWallMeasurement(u, v, mRgbTimestampGlThread); - } - - // If the measurement was successful add it and run the floor plan building - // algorithm. - if (wallMeasurement != null) { - mWallMeasurementList.add(wallMeasurement); - mRenderer.addWallMeasurement(wallMeasurement); - buildPlan(false); - } - } catch (TangoException t) { - Toast.makeText(getApplicationContext(), - R.string.failed_measurement, - Toast.LENGTH_SHORT).show(); - Log.e(TAG, getString(R.string.failed_measurement), t); - } catch (SecurityException t) { - Toast.makeText(getApplicationContext(), - R.string.failed_permissions, - Toast.LENGTH_SHORT).show(); - Log.e(TAG, getString(R.string.failed_permissions), t); - } - } - return true; - } - - /** - * Use the TangoSupport library and point cloud data to calculate the plane at the specified - * location in the color camera frame. - * It returns the pose of the fitted plane in a TangoPoseData structure. - */ - private WallMeasurement doWallMeasurement(float u, float v, double rgbTimestamp) { - TangoPointCloudData pointCloud = mPointCloudManager.getLatestPointCloud(); - - if (pointCloud == null) { - return null; - } - - // We need to calculate the transform between the color camera at the - // time the user clicked and the depth camera at the time the depth - // cloud was acquired. - TangoPoseData depthTcolorPose = TangoSupport.calculateRelativePose( - pointCloud.timestamp, TangoPoseData.COORDINATE_FRAME_CAMERA_DEPTH, - rgbTimestamp, TangoPoseData.COORDINATE_FRAME_CAMERA_COLOR); - - // Perform plane fitting with the latest available point cloud data. - try { - float[] uv = getColorCameraUVFromDisplay(u, v, mColorCameraToDisplayAndroidRotation); - - double[] identityTranslation = {0.0, 0.0, 0.0}; - double[] identityRotation = {0.0, 0.0, 0.0, 1.0}; - IntersectionPointPlaneModelPair intersectionPointPlaneModelPair = - TangoSupport.fitPlaneModelNearPoint(pointCloud, - identityTranslation, identityRotation, uv[0], uv[1], - depthTcolorPose.translation, depthTcolorPose.rotation); - - // Get the depth camera transform at the time the plane data was acquired. - TangoSupport.TangoMatrixTransformData transform = - TangoSupport.getMatrixTransformAtTime(pointCloud.timestamp, - TangoPoseData.COORDINATE_FRAME_AREA_DESCRIPTION, - TangoPoseData.COORDINATE_FRAME_CAMERA_DEPTH, - TangoSupport.TANGO_SUPPORT_ENGINE_OPENGL, - TangoSupport.TANGO_SUPPORT_ENGINE_TANGO, - Surface.ROTATION_0); - if (transform.statusCode == TangoPoseData.POSE_VALID) { - // Update the AR object location. - float[] planeFitTransform = calculatePlaneTransform( - intersectionPointPlaneModelPair.intersectionPoint, - intersectionPointPlaneModelPair.planeModel, transform.matrix); - - return new WallMeasurement(planeFitTransform, - transform.matrix, - pointCloud.timestamp); - } else { - Log.d(TAG, "Could not get a valid transform from depth to area description at time " - + pointCloud.timestamp); - } - } catch (TangoException e) { - Log.d(TAG, "Failed to fit plane"); - } - return null; - } - - /** - * Calculate the pose of the plane based on the position and normal orientation of the plane - * and align it with gravity. - */ - private float[] calculatePlaneTransform(double[] point, double normal[], - float[] openGlTdepth) { - // Vector aligned to gravity. - float[] openGlUp = new float[]{0, 1, 0, 0}; - float[] depthTOpenGl = new float[16]; - Matrix.invertM(depthTOpenGl, 0, openGlTdepth, 0); - float[] depthUp = new float[4]; - Matrix.multiplyMV(depthUp, 0, depthTOpenGl, 0, openGlUp, 0); - // Create the plane matrix transform in depth frame from a point, the plane normal and the - // up vector. - float[] depthTplane = matrixFromPointNormalUp(point, normal, depthUp); - float[] openGlTplane = new float[16]; - Matrix.multiplyMM(openGlTplane, 0, openGlTdepth, 0, depthTplane, 0); - return openGlTplane; - } - - /** - * Builds the plan from the set of measurements and updates the rendering in AR. - * - * @param closed If true, close the floor plan; if false, continue the floor plan. - */ - public void buildPlan(boolean closed) { - mFloorplan = PlanBuilder.buildPlan(mWallMeasurementList, closed); - mRenderer.updatePlan(mFloorplan); - } - - /** - * Updates every saved measurement. It re-queries the device pose at the time the measurement - * was taken. - */ - public void updateMeasurements() { - for (WallMeasurement wallMeasurement : mWallMeasurementList) { - // We need to re query the depth transform when the measurements were taken. - TangoSupport.TangoMatrixTransformData transform = - TangoSupport.getMatrixTransformAtTime(wallMeasurement - .getDepthTransformTimeStamp(), - TangoPoseData.COORDINATE_FRAME_AREA_DESCRIPTION, - TangoPoseData.COORDINATE_FRAME_CAMERA_DEPTH, - TangoSupport.TANGO_SUPPORT_ENGINE_OPENGL, - TangoSupport.TANGO_SUPPORT_ENGINE_TANGO, - Surface.ROTATION_0); - if (transform.statusCode == TangoPoseData.POSE_VALID) { - wallMeasurement.update(transform.matrix); - mRenderer.addWallMeasurement(wallMeasurement); - } else { - Log.d(TAG, "Could not get a valid transform from depth to area description at time " - + wallMeasurement - .getDepthTransformTimeStamp()); - } - } - } - - /** - * Finish plan, save the adf, and show the final result. - * Executed as an AsyncTask because saving the adf could be an expensive operation. - */ - public void finishPlan(View view) { - // Don't attempt to save if the service is not ready. - if (!canSaveAdf()) { - Toast.makeText(this, "Tango service not ready to save ADF", Toast.LENGTH_LONG).show(); - return; - } - - // Only finish the plan if we have enough measurements. - if (mWallMeasurementList.size() < 3) { - Toast.makeText(this, "At least 3 measurements are needed to close the room", - Toast.LENGTH_LONG).show(); - return; - } - - if (mFinishPlanTask != null) { - Log.w(TAG, "Finish task already executing"); - return; - } - - mFinishPlanTask = new FinishPlanTask(); - mFinishPlanTask.execute(); - } - - /** - * Verifies whether the Tango service is in a state where the ADF can be saved or not. - */ - private boolean canSaveAdf() { - boolean canSaveAdf = false; - try { - synchronized (this) { - TangoPoseData poseData = mTango.getPoseAtTime(0, new TangoCoordinateFramePair( - TangoPoseData.COORDINATE_FRAME_AREA_DESCRIPTION, - TangoPoseData.COORDINATE_FRAME_START_OF_SERVICE)); - if (poseData.statusCode == TangoPoseData.POSE_VALID) { - canSaveAdf = true; - } else { - Log.w(TAG, "ADF pose unavailable"); - } - } - } catch (TangoException e) { - Log.e(TAG, "Exception query Tango service before saving ADF.", e); - } - return canSaveAdf; - } - - /** - * Calculates a transformation matrix based on a point, a normal and the up gravity vector. - * The coordinate frame of the target transformation will a right handed system with Z+ in - * the direction of the normal and Y+ up. - */ - private float[] matrixFromPointNormalUp(double[] point, double[] normal, float[] up) { - float[] zAxis = new float[]{(float) normal[0], (float) normal[1], (float) normal[2]}; - normalize(zAxis); - float[] xAxis = crossProduct(up, zAxis); - normalize(xAxis); - float[] yAxis = crossProduct(zAxis, xAxis); - normalize(yAxis); - float[] m = new float[16]; - Matrix.setIdentityM(m, 0); - m[0] = xAxis[0]; - m[1] = xAxis[1]; - m[2] = xAxis[2]; - m[4] = yAxis[0]; - m[5] = yAxis[1]; - m[6] = yAxis[2]; - m[8] = zAxis[0]; - m[9] = zAxis[1]; - m[10] = zAxis[2]; - m[12] = (float) point[0]; - m[13] = (float) point[1]; - m[14] = (float) point[2]; - return m; - } - - /** - * Normalize a vector. - */ - private void normalize(float[] v) { - double norm = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); - v[0] /= norm; - v[1] /= norm; - v[2] /= norm; - } - - /** - * Cross product between two vectors following the right hand rule. - */ - private float[] crossProduct(float[] v1, float[] v2) { - float[] result = new float[3]; - result[0] = v1[1] * v2[2] - v2[1] * v1[2]; - result[1] = v1[2] * v2[0] - v2[2] * v1[0]; - result[2] = v1[0] * v2[1] - v2[0] * v1[1]; - return result; - } - - /** - * Given an UV coordinate in display(screen) space, returns UV coordinate in color camera space. - */ - float[] getColorCameraUVFromDisplay(float u, float v, int colorToDisplayRotation) { - switch (colorToDisplayRotation) { - case 1: - return new float[]{1.0f - v, u}; - case 2: - return new float[]{1.0f - u, 1.0f - v}; - case 3: - return new float[]{v, 1.0f - u}; - default: - return new float[]{u, v}; - } - } - - private static int getColorCameraToDisplayAndroidRotation(int displayRotation, - int cameraRotation) { - int cameraRotationNormalized = 0; - switch (cameraRotation) { - case 90: - cameraRotationNormalized = 1; - break; - case 180: - cameraRotationNormalized = 2; - break; - case 270: - cameraRotationNormalized = 3; - break; - default: - cameraRotationNormalized = 0; - break; - } - int ret = displayRotation - cameraRotationNormalized; - if (ret < 0) { - ret += 4; - } - return ret; - } - - /** - * Set the color camera background texture rotation and save the camera to display rotation. - */ - private void setAndroidOrientation() { - Display display = getWindowManager().getDefaultDisplay(); - Camera.CameraInfo colorCameraInfo = new Camera.CameraInfo(); - Camera.getCameraInfo(COLOR_CAMERA_ID, colorCameraInfo); - - mColorCameraToDisplayAndroidRotation = - getColorCameraToDisplayAndroidRotation(display.getRotation(), - colorCameraInfo.orientation); - // Run this in OpenGL thread. - mSurfaceView.queueEvent(new Runnable() { - @Override - public void run() { - mRenderer.updateColorCameraTextureUvGlThread(mColorCameraToDisplayAndroidRotation); - } - }); - } - - /** - * Finish plan AsyncTask. - * Shows a spinner while it's saving the adf and updating the measurements. - * Draws the final result on a canvas and shows it. - */ - private class FinishPlanTask extends AsyncTask { - - @Override - protected void onPreExecute() { - mProgressGroup.setVisibility(View.VISIBLE); - } - - @Override - protected Void doInBackground(Void... params) { - // Save and optimize ADF. - mTango.saveAreaDescription(); - // Update poses after optimization and re build plan. - mRenderer.removeMeasurements(); - updateMeasurements(); - buildPlan(true); - - return null; - } - - @Override - protected void onPostExecute(Void v) { - mProgressGroup.setVisibility(View.GONE); - RelativeLayout frameLayout = new RelativeLayout(FloorplanActivity.this); - // Draw final result on Canvas. - PlanView planView = new PlanView(FloorplanActivity.this); - planView.setLayoutParams(new RelativeLayout.LayoutParams( - RelativeLayout.LayoutParams.MATCH_PARENT, - RelativeLayout.LayoutParams.MATCH_PARENT)); - frameLayout.addView(planView); - // Add 'Back' button. - Button backButton = new Button(FloorplanActivity.this); - backButton.setText("Back"); - RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout - .LayoutParams.WRAP_CONTENT, RelativeLayout - .LayoutParams.WRAP_CONTENT); - params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE); - params.addRule(RelativeLayout.ALIGN_PARENT_END, RelativeLayout.TRUE); - backButton.setLayoutParams(params); - backButton.setPadding(100, 100, 100, 100); - backButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - // Recreate the Activity to start over. - recreate(); - } - }); - frameLayout.addView(backButton); - setContentView(frameLayout); - planView.invalidate(); - mFinishPlanTask = null; - } - } - - /** - * Custom View that draws the plan in 2D. - */ - private class PlanView extends View { - - private Paint mPaint; - - public PlanView(Context context) { - super(context); - mPaint = new Paint(); - mPaint.setStrokeWidth(10); - mPaint.setTextSize(50); - } - - @Override - public void onDraw(Canvas canvas) { - mFloorplan.drawOnCanvas(canvas, mPaint); - } - } -} diff --git a/java_floor_plan_example/app/src/main/java/com/projecttango/examples/java/floorplan/FloorplanRenderer.java b/java_floor_plan_example/app/src/main/java/com/projecttango/examples/java/floorplan/FloorplanRenderer.java deleted file mode 100644 index 498021ad..00000000 --- a/java_floor_plan_example/app/src/main/java/com/projecttango/examples/java/floorplan/FloorplanRenderer.java +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright 2016 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.projecttango.examples.java.floorplan; - -import com.google.atap.tangoservice.TangoCameraIntrinsics; -import com.google.atap.tangoservice.TangoPoseData; - -import android.content.Context; -import android.graphics.Color; -import android.util.Log; -import android.view.MotionEvent; -import android.view.Surface; - -import org.rajawali3d.Object3D; -import org.rajawali3d.lights.DirectionalLight; -import org.rajawali3d.materials.Material; -import org.rajawali3d.materials.methods.DiffuseMethod; -import org.rajawali3d.materials.methods.SpecularMethod; -import org.rajawali3d.materials.textures.ATexture; -import org.rajawali3d.materials.textures.StreamingTexture; -import org.rajawali3d.materials.textures.Texture; -import org.rajawali3d.math.Matrix4; -import org.rajawali3d.math.Quaternion; -import org.rajawali3d.math.vector.Vector3; -import org.rajawali3d.primitives.Line3D; -import org.rajawali3d.primitives.Plane; -import org.rajawali3d.primitives.ScreenQuad; -import org.rajawali3d.renderer.Renderer; - -import java.nio.FloatBuffer; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Stack; - -import javax.microedition.khronos.opengles.GL10; - -/** - * Very simple augmented reality example which displays cubes fixed in place for every - * WallMeasurement and a continuous line for the perimeter of the floor plan. - * Each time the user clicks on the screen, a cube is placed flush with the surface detected - * using the point cloud data at the position clicked. - */ -public class FloorplanRenderer extends Renderer { - private static final float CUBE_SIDE_LENGTH = 0.3f; - private static final String TAG = FloorplanRenderer.class.getSimpleName(); - - private float[] textureCoords0 = new float[]{0.0F, 1.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F, 0.0F}; - private float[] textureCoords270 = new float[]{1.0F, 1.0F, 0.0F, 1.0F, 1.0F, 0.0F, 0.0F, 0.0F}; - private float[] textureCoords180 = new float[]{1.0F, 0.0F, 1.0F, 1.0F, 0.0F, 0.0F, 0.0F, 1.0F}; - private float[] textureCoords90 = new float[]{0.0F, 0.0F, 1.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F}; - - private List mNewPoseList = new ArrayList(); - private boolean mObjectPoseUpdated = false; - private boolean mPlanUpdated = false; - private Material mPlaneMaterial; - private Object3D mPlanLine = null; - private Stack mPlanPoints; - private List mMeasurementObjectList = new ArrayList(); - - // Augmented reality related fields - private ATexture mTangoCameraTexture; - private boolean mSceneCameraConfigured; - - private ScreenQuad mBackgroundQuad; - - /** - * Small utility class to hold a position and orientation pair. - */ - class Pose { - public Pose(Vector3 p, Quaternion q) { - position = p; - orientation = q; - } - - public Quaternion orientation; - public Vector3 position; - } - - public FloorplanRenderer(Context context) { - super(context); - } - - @Override - protected void initScene() { - // Create a quad covering the whole background and assign a texture to it where the - // Tango color camera contents will be rendered. - if (mBackgroundQuad == null) { - mBackgroundQuad = new ScreenQuad(); - mBackgroundQuad.getGeometry().setTextureCoords(textureCoords0); - } - Material tangoCameraMaterial = new Material(); - tangoCameraMaterial.setColorInfluence(0); - // We need to use Rajawali's {@code StreamingTexture} since it sets up the texture - // for GL_TEXTURE_EXTERNAL_OES rendering - mTangoCameraTexture = - new StreamingTexture("camera", (StreamingTexture.ISurfaceListener) null); - try { - tangoCameraMaterial.addTexture(mTangoCameraTexture); - mBackgroundQuad.setMaterial(tangoCameraMaterial); - } catch (ATexture.TextureException e) { - Log.e(TAG, "Exception creating texture for RGB camera contents", e); - } - getCurrentScene().addChildAt(mBackgroundQuad, 0); - - // Add a directional light in an arbitrary direction. - DirectionalLight light = new DirectionalLight(1, 0.2, -1); - light.setColor(1, 1, 1); - light.setPower(0.8f); - light.setPosition(3, 2, 4); - getCurrentScene().addLight(light); - - // Set-up a material. - mPlaneMaterial = new Material(); - mPlaneMaterial.enableLighting(true); - mPlaneMaterial.setDiffuseMethod(new DiffuseMethod.Lambert()); - mPlaneMaterial.setSpecularMethod(new SpecularMethod.Phong()); - mPlaneMaterial.setColor(0xff009900); - mPlaneMaterial.setColorInfluence(0.5f); - try { - Texture t = new Texture("wall", R.drawable.wall); - mPlaneMaterial.addTexture(t); - } catch (ATexture.TextureException e) { - e.printStackTrace(); - } - } - - /** - * Update background texture's UV coordinates when device orientation is changed. i.e change - * between landscape and portrait mode. - * This must be run in the OpenGL thread. - */ - public void updateColorCameraTextureUvGlThread(int rotation) { - if (mBackgroundQuad == null) { - mBackgroundQuad = new ScreenQuad(); - } - - switch (rotation) { - case Surface.ROTATION_90: - mBackgroundQuad.getGeometry().setTextureCoords(textureCoords90, true); - break; - case Surface.ROTATION_180: - mBackgroundQuad.getGeometry().setTextureCoords(textureCoords180, true); - break; - case Surface.ROTATION_270: - mBackgroundQuad.getGeometry().setTextureCoords(textureCoords270, true); - break; - default: - mBackgroundQuad.getGeometry().setTextureCoords(textureCoords0, true); - break; - } - mBackgroundQuad.getGeometry().reload(); - } - - @Override - protected void onRender(long elapsedRealTime, double deltaTime) { - // Update the AR object if necessary - // Synchronize against concurrent access with the setter below. - synchronized (this) { - if (mObjectPoseUpdated) { - Iterator poseIterator = mNewPoseList.iterator(); - Object3D object3D; - while (poseIterator.hasNext()) { - Pose pose = poseIterator.next(); - object3D = new Plane(CUBE_SIDE_LENGTH, CUBE_SIDE_LENGTH, 2, 2); - object3D.setMaterial(mPlaneMaterial); - // Place the 3D object in the location of the detected plane. - object3D.setPosition(pose.position); - object3D.rotate(pose.orientation); - - getCurrentScene().addChild(object3D); - mMeasurementObjectList.add(object3D); - poseIterator.remove(); - } - mObjectPoseUpdated = false; - } - - if (mPlanUpdated) { - if (mPlanLine != null) { - // Remove the old line. - getCurrentScene().removeChild(mPlanLine); - } - if (mPlanPoints.size() > 1) { - // Create a line with the points of the plan perimeter. - mPlanLine = new Line3D(mPlanPoints, 20, Color.RED); - Material m = new Material(); - m.setColor(Color.RED); - mPlanLine.setMaterial(m); - getCurrentScene().addChild(mPlanLine); - } - mPlanUpdated = false; - } - } - - super.onRender(elapsedRealTime, deltaTime); - } - - /** - * Update the scene camera based on the provided pose in Tango start of service frame. - * The camera pose should match the pose of the camera color at the time the last rendered RGB - * frame, which can be retrieved with this.getTimestamp(); - *

- * NOTE: This must be called from the OpenGL render thread - it is not thread safe. - */ - public void updateRenderCameraPose(TangoPoseData cameraPose) { - float[] rotation = cameraPose.getRotationAsFloats(); - float[] translation = cameraPose.getTranslationAsFloats(); - Quaternion quaternion = new Quaternion(rotation[3], rotation[0], rotation[1], rotation[2]); - // Conjugating the Quaternion is need because Rajawali uses left handed convention for - // quaternions. - getCurrentCamera().setRotation(quaternion.conjugate()); - getCurrentCamera().setPosition(translation[0], translation[1], translation[2]); - } - - /** - * It returns the ID currently assigned to the texture where the Tango color camera contents - * should be rendered. - * NOTE: This must be called from the OpenGL render thread - it is not thread safe. - */ - public int getTextureId() { - return mTangoCameraTexture == null ? -1 : mTangoCameraTexture.getTextureId(); - } - - /** - * We need to override this method to mark the camera for re-configuration (set proper - * projection matrix) since it will be reset by Rajawali on surface changes. - */ - @Override - public void onRenderSurfaceSizeChanged(GL10 gl, int width, int height) { - super.onRenderSurfaceSizeChanged(gl, width, height); - mSceneCameraConfigured = false; - } - - public boolean isSceneCameraConfigured() { - return mSceneCameraConfigured; - } - - /** - * Sets the projection matrix for the scene camera to match the parameters of the color camera, - * provided by the {@code TangoCameraIntrinsics}. - */ - public void setProjectionMatrix(float[] matrix) { - getCurrentCamera().setProjectionMatrix(new Matrix4(matrix)); - } - - @Override - public void onOffsetsChanged(float xOffset, float yOffset, - float xOffsetStep, float yOffsetStep, - int xPixelOffset, int yPixelOffset) { - } - - @Override - public void onTouchEvent(MotionEvent event) { - - } - - /** - * Add a new WallMeasurement. - * A new cube will be added at the plane position and orientation to represent the measurement. - */ - public synchronized void addWallMeasurement(WallMeasurement wallMeasurement) { - float[] openGlTWall = wallMeasurement.getPlaneTransform(); - Matrix4 openGlTWallMatrix = new Matrix4(openGlTWall); - mNewPoseList.add(new Pose(openGlTWallMatrix.getTranslation(), - new Quaternion().fromMatrix(openGlTWallMatrix))); - mObjectPoseUpdated = true; - } - - /** - * Update the perimeter line with the new floor plan. - */ - public synchronized void updatePlan(Floorplan plan) { - Stack points = new Stack(); - for (float[] point : plan.getPlanPoints()) { - points.add(new Vector3(point[0], 0, point[2])); - } - mPlanPoints = points; - mPlanUpdated = true; - } - - /** - * Remove all the measurements from the Scene. - */ - public synchronized void removeMeasurements() { - for (Object3D object3D : mMeasurementObjectList) { - getCurrentScene().removeChild(object3D); - } - } -} diff --git a/java_floor_plan_example/app/src/main/java/com/projecttango/examples/java/floorplan/PlanBuilder.java b/java_floor_plan_example/app/src/main/java/com/projecttango/examples/java/floorplan/PlanBuilder.java deleted file mode 100644 index 70fcda39..00000000 --- a/java_floor_plan_example/app/src/main/java/com/projecttango/examples/java/floorplan/PlanBuilder.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2016 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.projecttango.examples.java.floorplan; - -import org.rajawali3d.math.vector.Vector3; - -import java.util.ArrayList; -import java.util.List; - -/** - * Builder that knows how to build a Floorplan given a list of WallMeasurements. - */ -public class PlanBuilder { - - /** - * Creates a new Floorplan object beased on the measurements that we have so far. - * - * @param wallMeasurementList List of WallMeasurements to use as input to build the plan. - * It must have only one measurement per wall. - * @param closed If true, close the floor plan and intersect the first and last - * measurements. If false, continue the floor plan. - */ - public static Floorplan buildPlan(List wallMeasurementList, boolean closed) { - List planPoints = new ArrayList(); - WallMeasurement lastWallMeasurement = null; - // Intersect every measurement with the previous one and add the result to the plan. - if (!wallMeasurementList.isEmpty()) { - boolean first = true; - float[] lastAddedPoint = null; - for (WallMeasurement wallMeasurement : wallMeasurementList) { - if (lastWallMeasurement != null) { - if (!first) { - planPoints.remove(lastAddedPoint); - } - planPoints.add(wallMeasurement.intersect(lastWallMeasurement)); - first = false; - } - float[] openGlWall = wallMeasurement.getPlaneTransform(); - float[] measurementPoint = new float[]{openGlWall[12], openGlWall[13], - openGlWall[14]}; - planPoints.add(measurementPoint); - lastWallMeasurement = wallMeasurement; - lastAddedPoint = measurementPoint; - } - - // If closing the floor plan, intersect the first and last measurements. - if (closed) { - planPoints.remove(lastAddedPoint); - planPoints.add(lastWallMeasurement.intersect(wallMeasurementList.get(0))); - planPoints.remove(planPoints.get(0)); - } - } - return new Floorplan(planPoints); - } - -} diff --git a/java_floor_plan_example/app/src/main/java/com/projecttango/examples/java/floorplan/WallMeasurement.java b/java_floor_plan_example/app/src/main/java/com/projecttango/examples/java/floorplan/WallMeasurement.java deleted file mode 100644 index 7aadd14d..00000000 --- a/java_floor_plan_example/app/src/main/java/com/projecttango/examples/java/floorplan/WallMeasurement.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2016 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.projecttango.examples.java.floorplan; - -import android.opengl.Matrix; - -/** - * Representation of wall as a measured plane. - */ -public class WallMeasurement { - /** - * The pose of the plane in OpenGl frame. - */ - private float[] mOpenGlTPlaneTransform; - /** - * The pose of the depth camera when the measurement was taken in OpenGl frame. - */ - private float[] mOpenGlTDepthTransform; - /** - * The mTimestamp of the measurement. - */ - private double mTimestamp; - - public WallMeasurement(float[] openGlTPlaneTransform, float[] openGlTDepthTransform, double - timestamp) { - mOpenGlTPlaneTransform = openGlTPlaneTransform; - mOpenGlTDepthTransform = openGlTDepthTransform; - mTimestamp = timestamp; - } - - /** - * Update the plane pose of the measurement given an updated device pose at the timestamp of - * the measurement. - */ - public void update(float[] newOpenGlTDepthTransform) { - float[] depthTOpenGl = new float[16]; - Matrix.invertM(depthTOpenGl, 0, mOpenGlTDepthTransform, 0); - float[] newOpenGlTOldOpenGl = new float[16]; - Matrix.multiplyMM(newOpenGlTOldOpenGl, 0, newOpenGlTDepthTransform, 0, depthTOpenGl, 0); - float[] newOpenGlTPlane = new float[16]; - Matrix.multiplyMM(newOpenGlTPlane, 0, newOpenGlTOldOpenGl, 0, mOpenGlTPlaneTransform, 0); - mOpenGlTPlaneTransform = newOpenGlTPlane; - mOpenGlTDepthTransform = newOpenGlTDepthTransform; - } - - /** - * Intersect this measurement with another WallMeasurement. - * - * @param otherWallMeasurement The other WallMeasurement to intersect with. - * @return The point of intersection in world frame. - */ - public float[] intersect(WallMeasurement otherWallMeasurement) { - float[] openGlTPlane = getPlaneTransform(); - float[] openGlTOtherPlane = otherWallMeasurement.getPlaneTransform(); - // We will calculate the intersection in the frame of the first transformation. - // Transform the second wall measurement to the first measurement frame - float[] planeTOpenGl = new float[16]; - Matrix.invertM(planeTOpenGl, 0, openGlTPlane, 0); - float[] firstPlaneTsecondPlane = new float[16]; - Matrix.multiplyMM(firstPlaneTsecondPlane, 0, planeTOpenGl, 0, openGlTOtherPlane, 0); - - // The translation of the second transform origin, in the first one's frame - float[] wallPsecond = new float[]{firstPlaneTsecondPlane[12], firstPlaneTsecondPlane[13], - firstPlaneTsecondPlane[14]}; - // The vector representing the X axis of the second transform, in the first's frame - float[] wallTXsecond = new float[]{firstPlaneTsecondPlane[0], firstPlaneTsecondPlane[1], - firstPlaneTsecondPlane[2]}; - - float[] wallPintersection = - new float[]{wallPsecond[0] - wallTXsecond[0] / wallTXsecond[2] * wallPsecond[2], - 0, 0, 1}; - - float[] worldPIntersection = new float[4]; - Matrix.multiplyMV(worldPIntersection, 0, openGlTPlane, 0, wallPintersection, 0); - - return worldPIntersection; - } - - public float[] getPlaneTransform() { - return mOpenGlTPlaneTransform; - } - - public double getDepthTransformTimeStamp() { - return mTimestamp; - } - -} diff --git a/java_floor_plan_example/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/java_floor_plan_example/app/src/main/res/drawable-xxhdpi/ic_launcher.png deleted file mode 100644 index 80a8adf4..00000000 Binary files a/java_floor_plan_example/app/src/main/res/drawable-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/java_floor_plan_example/app/src/main/res/drawable-xxhdpi/wall.png b/java_floor_plan_example/app/src/main/res/drawable-xxhdpi/wall.png deleted file mode 100644 index 76d2053f..00000000 Binary files a/java_floor_plan_example/app/src/main/res/drawable-xxhdpi/wall.png and /dev/null differ diff --git a/java_floor_plan_example/app/src/main/res/layout/activity_main.xml b/java_floor_plan_example/app/src/main/res/layout/activity_main.xml deleted file mode 100644 index 00c339c2..00000000 --- a/java_floor_plan_example/app/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - -