aboutsummaryrefslogtreecommitdiffhomepage
path: root/platform_tools/android/apps/skar_java/src/main/java/com/google/ar/core/examples/java/helloskar/HelloCanvasAR.java
diff options
context:
space:
mode:
Diffstat (limited to 'platform_tools/android/apps/skar_java/src/main/java/com/google/ar/core/examples/java/helloskar/HelloCanvasAR.java')
-rw-r--r--platform_tools/android/apps/skar_java/src/main/java/com/google/ar/core/examples/java/helloskar/HelloCanvasAR.java553
1 files changed, 553 insertions, 0 deletions
diff --git a/platform_tools/android/apps/skar_java/src/main/java/com/google/ar/core/examples/java/helloskar/HelloCanvasAR.java b/platform_tools/android/apps/skar_java/src/main/java/com/google/ar/core/examples/java/helloskar/HelloCanvasAR.java
new file mode 100644
index 0000000000..cc4ad93b99
--- /dev/null
+++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/ar/core/examples/java/helloskar/HelloCanvasAR.java
@@ -0,0 +1,553 @@
+/*
+ * Copyright 2017 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.google.ar.core.examples.java.helloskar;
+
+import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PointF;
+import android.graphics.PorterDuff;
+import android.opengl.GLES20;
+import android.opengl.GLSurfaceView;
+import android.opengl.Matrix;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.design.internal.BottomNavigationMenuView;
+import android.support.design.widget.BottomNavigationView;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.SurfaceHolder;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Toast;
+
+import com.google.ar.core.Anchor;
+import com.google.ar.core.ArCoreApk;
+import com.google.ar.core.Camera;
+import com.google.ar.core.Frame;
+import com.google.ar.core.HitResult;
+import com.google.ar.core.Plane;
+import com.google.ar.core.Point;
+import com.google.ar.core.Point.OrientationMode;
+import com.google.ar.core.PointCloud;
+import com.google.ar.core.Session;
+import com.google.ar.core.Trackable;
+import com.google.ar.core.TrackingState;
+import com.google.ar.core.examples.java.common.helpers.CameraPermissionHelper;
+import com.google.ar.core.examples.java.common.helpers.DisplayRotationHelper;
+import com.google.ar.core.examples.java.common.helpers.FullScreenHelper;
+import com.google.ar.core.examples.java.common.helpers.SnackbarHelper;
+import com.google.ar.core.examples.java.common.helpers.TapHelper;
+import com.google.ar.core.examples.java.common.rendering.BackgroundRenderer;
+import com.google.ar.core.exceptions.CameraNotAvailableException;
+import com.google.ar.core.exceptions.UnavailableApkTooOldException;
+import com.google.ar.core.exceptions.UnavailableArcoreNotInstalledException;
+import com.google.ar.core.exceptions.UnavailableDeviceNotCompatibleException;
+import com.google.ar.core.exceptions.UnavailableSdkTooOldException;
+import com.google.ar.core.exceptions.UnavailableUserDeclinedInstallationException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+/**
+ * This is a simple example that shows how to create an augmented reality (AR) application using the
+ * ARCore API. The application will display any detected planes and will allow the user to tap on a
+ * plane to place 2D objects
+ */
+
+public class HelloCanvasAR extends AppCompatActivity implements GLSurfaceView.Renderer {
+ public enum DrawingType {
+ circle, rect, text, animation
+ }
+
+ private static final String TAG = HelloCanvasAR.class.getSimpleName();
+
+ //Simple SurfaceView used to draw 2D objects on top of the GLSurfaceView
+ private ARSurfaceView arSurfaceView;
+ private Canvas canvas;
+ private SurfaceHolder holder;
+
+ //GLSurfaceView used to draw 3D objects & camera input
+ private GLSurfaceView glSurfaceView;
+
+ //ARSession
+ private Session session;
+
+ private boolean installRequested;
+ private final SnackbarHelper messageSnackbarHelper = new SnackbarHelper();
+ private DisplayRotationHelper displayRotationHelper;
+ private TapHelper tapHelper;
+
+ // OpenGL background renderer
+ private final BackgroundRenderer backgroundRenderer = new BackgroundRenderer();
+
+ // 2D Renderer
+ private DrawManager drawManager = new DrawManager();
+ private DrawingType currentDrawabletype = DrawingType.circle;
+ private boolean drawSmoothPainting = true;
+
+ // Temporary matrix allocated here to reduce number of allocations for each frame.
+ private final float[] anchorMatrix = new float[16];
+
+ PointF previousEvent;
+
+ // Anchors created from taps used for object placing.
+ private final ArrayList<Anchor> anchors = new ArrayList<>();
+
+ // Animation fields
+ float radius;
+ String PROPERTY_RADIUS = "radius";
+ ValueAnimator animator;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ Toolbar myToolbar = (Toolbar) findViewById(R.id.my_toolbar);
+ setSupportActionBar(myToolbar);
+
+
+ //hide notifications bar
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+ WindowManager.LayoutParams.FLAG_FULLSCREEN);
+
+ arSurfaceView = findViewById(R.id.arsurfaceview);
+ glSurfaceView = findViewById(R.id.glsurfaceview);
+ arSurfaceView.bringToFront();
+ arSurfaceView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ displayRotationHelper = new DisplayRotationHelper(/*context=*/ this);
+
+ // Set up tap listener.
+ tapHelper = new TapHelper(/*context=*/ this);
+ glSurfaceView.setOnTouchListener(tapHelper);
+
+ // Set up renderer.
+ glSurfaceView.setPreserveEGLContextOnPause(true);
+ glSurfaceView.setEGLContextClientVersion(2);
+ glSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0); // Alpha used for plane blending.
+ glSurfaceView.setRenderer(this);
+ glSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
+
+ installRequested = false;
+
+ BottomNavigationView bottomNav = findViewById(R.id.palette);
+ bottomNav.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
+ @Override
+ public boolean onNavigationItemSelected(@NonNull MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.palette_green:
+ drawManager.fingerPainting.setColor(Color.GREEN);
+ return true;
+ case R.id.palette_red:
+ drawManager.fingerPainting.setColor(Color.RED);
+ return true;
+ case R.id.palette_reset:
+ drawManager.fingerPainting.reset();
+ return true;
+ default:
+ return true;
+ }
+ }
+ });
+
+ // Animator set up
+ PropertyValuesHolder propertyRadius = PropertyValuesHolder.ofFloat(PROPERTY_RADIUS, 0, 0.5f);
+ animator = new ValueAnimator();
+ animator.setValues(propertyRadius);
+ animator.setDuration(1000);
+ animator.setRepeatCount(ValueAnimator.INFINITE);
+ animator.setRepeatMode(ValueAnimator.REVERSE);
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ radius = (float) animation.getAnimatedValue(PROPERTY_RADIUS);
+ }
+ });
+ animator.start();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ if (session == null) {
+ Exception exception = null;
+ String message = null;
+ try {
+ switch (ArCoreApk.getInstance().requestInstall(this, !installRequested)) {
+ case INSTALL_REQUESTED:
+ installRequested = true;
+ return;
+ case INSTALLED:
+ break;
+ }
+
+ // ARCore requires camera permissions to operate. If we did not yet obtain runtime
+ // permission on Android M and above, now is a good time to ask the user for it.
+ if (!CameraPermissionHelper.hasCameraPermission(this)) {
+ CameraPermissionHelper.requestCameraPermission(this);
+ return;
+ }
+
+ // Create the session.
+ session = new Session(/* context= */ this);
+
+ } catch (UnavailableArcoreNotInstalledException
+ | UnavailableUserDeclinedInstallationException e) {
+ message = "Please install ARCore";
+ exception = e;
+ } catch (UnavailableApkTooOldException e) {
+ message = "Please update ARCore";
+ exception = e;
+ } catch (UnavailableSdkTooOldException e) {
+ message = "Please update this app";
+ exception = e;
+ } catch (UnavailableDeviceNotCompatibleException e) {
+ message = "This device does not support AR";
+ exception = e;
+ } catch (Exception e) {
+ message = "Failed to create AR session";
+ exception = e;
+ }
+
+ if (message != null) {
+ messageSnackbarHelper.showError(this, message);
+ Log.e(TAG, "Exception creating session", exception);
+ return;
+ }
+ }
+
+ // Note that order matters - see the note in onPause(), the reverse applies here.
+ try {
+ session.resume();
+ } catch (CameraNotAvailableException e) {
+ messageSnackbarHelper.showError(this, "Camera not available. Please restart the app.");
+ session = null;
+ return;
+ }
+
+ glSurfaceView.onResume();
+ displayRotationHelper.onResume();
+ messageSnackbarHelper.showMessage(this, "Searching for surfaces...");
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ if (session != null) {
+ displayRotationHelper.onPause();
+ glSurfaceView.onPause();
+ session.pause();
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] results) {
+ if (!CameraPermissionHelper.hasCameraPermission(this)) {
+ Toast.makeText(this, "Camera permission is needed to run this application", Toast.LENGTH_LONG)
+ .show();
+ if (!CameraPermissionHelper.shouldShowRequestPermissionRationale(this)) {
+ // Permission denied with checking "Do not ask again".
+ CameraPermissionHelper.launchPermissionSettings(this);
+ }
+ finish();
+ }
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ super.onWindowFocusChanged(hasFocus);
+ FullScreenHelper.setFullScreenOnWindowFocusChanged(this, hasFocus);
+ }
+
+ /************** GLSurfaceView Methods ****************************/
+ @Override
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ GLES20.glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
+
+ // Prepare the rendering objects. This involves reading shaders, so may throw an IOException.
+ try {
+ // Create the texture and pass it to ARCore session to be filled during update().
+ backgroundRenderer.createOnGlThread( this);
+ drawManager.initializePlaneShader(this, "models/trigrid.png");
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to read an asset file", e);
+ }
+ }
+
+ @Override
+ public void onSurfaceChanged(GL10 gl, int width, int height) {
+ displayRotationHelper.onSurfaceChanged(width, height);
+ GLES20.glViewport(0, 0, width, height);
+
+ // Send viewport information to 2D AR drawing manager
+ drawManager.updateViewport(width, height);
+ }
+
+ @Override
+ public void onDrawFrame(GL10 gl) {
+ canvas = null;
+ holder = null;
+
+ // Clear screen to notify driver it should not load any pixels from previous frame.
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
+
+ if (session == null) {
+ return;
+ }
+ // Notify ARCore session that the view size changed so that the perspective matrix and
+ // the video background can be properly adjusted.
+ displayRotationHelper.updateSessionIfNeeded(session);
+
+
+ try {
+ session.setCameraTextureName(backgroundRenderer.getTextureId());
+ Frame frame = session.update();
+ Camera camera = frame.getCamera();
+
+ MotionEvent tap = tapHelper.poll();
+ if (tap != null && camera.getTrackingState() == TrackingState.TRACKING) {
+ for (HitResult hit : frame.hitTest(tap)) {
+ // Check if any plane was hit, and if it was hit inside the plane polygon
+ Trackable trackable = hit.getTrackable();
+ // Creates an anchor if a plane or an oriented point was hit.
+ if ((trackable instanceof Plane
+ && ((Plane) trackable).isPoseInPolygon(hit.getHitPose())
+ && (DrawManager.calculateDistanceToPlane(hit.getHitPose(), camera.getPose())
+ > 0))
+ || (trackable instanceof Point
+ && ((Point) trackable).getOrientationMode()
+ == OrientationMode.ESTIMATED_SURFACE_NORMAL)) {
+ if (anchors.size() >= 20) {
+ anchors.get(0).detach();
+ anchors.remove(0);
+ }
+ anchors.add(hit.createAnchor());
+ break;
+ }
+ }
+ }
+
+ // Draw background with OpenGL.
+ // TODO: possibly find a way to extract texture and draw on Canvas
+ backgroundRenderer.draw(frame);
+
+ // If not tracking, don't draw objects
+ if (camera.getTrackingState() == TrackingState.PAUSED) {
+ return;
+ }
+
+ // Get projection matrix.
+ float[] projmtx = new float[16];
+ camera.getProjectionMatrix(projmtx, 0, 0.1f, 100.0f);
+ drawManager.updateProjectionMatrix(projmtx);
+
+ // Get camera matrix and draw.
+ float[] viewmtx = new float[16];
+ camera.getViewMatrix(viewmtx, 0);
+ drawManager.updateViewMatrix(viewmtx);
+
+ final float[] colorCorrectionRgba = new float[4];
+ frame.getLightEstimate().getColorCorrection(colorCorrectionRgba, 0);
+ drawManager.updateLightColorFilter(colorCorrectionRgba);
+
+ // Building finger painting
+ TapHelper.ScrollEvent holdTap = tapHelper.holdPoll();
+ if (holdTap != null && camera.getTrackingState() == TrackingState.TRACKING) {
+ for (HitResult hit : frame.hitTest(holdTap.e)) {
+ // Check if any plane was hit, and if it was hit inside the plane polygon
+ Trackable trackable = hit.getTrackable();
+ // Creates an anchor if a plane or an oriented point was hit.
+ if ((trackable instanceof Plane
+ && ((Plane) trackable).isPoseInPolygon(hit.getHitPose())
+ && (DrawManager.calculateDistanceToPlane(hit.getHitPose(), camera.getPose())
+ > 0))
+ || (trackable instanceof Point
+ && ((Point) trackable).getOrientationMode()
+ == OrientationMode.ESTIMATED_SURFACE_NORMAL)) {
+
+ // Get hit point transform, apply it to the origin
+ float[] gm = new float[16];
+ hit.getHitPose().toMatrix(gm, 0);
+ float[] point = {0, 0, 0, 1};
+ Matrix.multiplyMV(point, 0, gm, 0, point, 0);
+
+ if (drawManager.fingerPainting.isEmpty()) {
+ drawManager.fingerPainting.addPoint(new PointF(0, 0), true);
+
+ // Get model matrix of first point
+ float[] m = new float[16];
+ hit.getHitPose().toMatrix(m, 0);
+ drawManager.fingerPainting.setModelMatrix(m);
+ } else {
+ float localDistanceScale = 1000;
+ PointF distance = new PointF(point[0] - previousEvent.x,
+ point[2] - previousEvent.y);
+
+ if (distance.length() < 0.05f) {
+ continue;
+ }
+
+ // New point is distance + old point
+ PointF p = new PointF(distance.x * localDistanceScale
+ + drawManager.fingerPainting.previousPoint.x,
+ distance.y * localDistanceScale
+ + drawManager.fingerPainting.previousPoint.y);
+
+ drawManager.fingerPainting.addPoint(p, holdTap.isStartOfScroll);
+ }
+
+ previousEvent = new PointF(point[0], point[2]);
+ break;
+ }
+ }
+ }
+
+ // Drawing on Canvas (SurfaceView)
+ if (arSurfaceView.isRunning()) {
+ // Lock canvas
+ SurfaceHolder holder = arSurfaceView.getHolder();
+ Canvas canvas = holder.lockHardwareCanvas();
+ canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
+
+ // Draw point cloud
+ PointCloud pointCloud = frame.acquirePointCloud();
+ drawPointCloud(canvas, pointCloud);
+ pointCloud.release();
+
+ // Draw planes
+ // Check if we detected at least one plane. If so, hide the loading message.
+ if (messageSnackbarHelper.isShowing()) {
+ for (Plane plane : session.getAllTrackables(Plane.class)) {
+ if (plane.getType() == com.google.ar.core.Plane.Type.HORIZONTAL_UPWARD_FACING
+ && plane.getTrackingState() == TrackingState.TRACKING) {
+ messageSnackbarHelper.hide(this);
+ break;
+ }
+ }
+ }
+
+ // Draw planes
+ drawPlanes(canvas, camera);
+
+ // Draw models
+ drawModels(canvas);
+
+ // Draw finger painting
+ drawFingerPainting(canvas);
+
+ // Unlock canvas
+ holder.unlockCanvasAndPost(canvas);
+ }
+ } catch (Throwable t) {
+ // Avoid crashing the application due to unhandled exceptions.
+ if (holder != null && canvas != null) {
+ holder.unlockCanvasAndPost(canvas);
+ }
+ Log.e(TAG, "Exception on the OpenGL thread", t);
+ }
+ }
+
+ // Helper drawing functions that invoke drawManager
+ private void drawPlanes(Canvas canvas, Camera camera) {
+ drawManager.drawPlanes(canvas, camera.getPose(), session.getAllTrackables(Plane.class));
+ }
+
+ private void drawPointCloud(Canvas canvas, PointCloud cloud) {
+ drawManager.drawPointCloud(canvas, cloud);
+ }
+
+ private void drawModels(Canvas canvas) {
+ for (Anchor anchor : anchors) {
+ if (anchor.getTrackingState() != TrackingState.TRACKING) {
+ continue;
+ }
+ // Get the current pose of an Anchor in world space. The Anchor pose is updated
+ // during calls to session.update() as ARCore refines its estimate of the world.
+ anchor.getPose().toMatrix(anchorMatrix, 0);
+ drawManager.modelMatrices.add(0, anchorMatrix);
+
+ switch (currentDrawabletype) {
+ case circle:
+ drawManager.drawCircle(canvas);
+ break;
+ case rect:
+ drawManager.drawRect(canvas);
+ break;
+ case animation:
+ drawManager.drawAnimatedRoundRect(canvas, radius);
+ break;
+ case text:
+ drawManager.drawText(canvas, "Android");
+ break;
+ default:
+ drawManager.drawCircle(canvas);
+ break;
+ }
+ }
+ }
+
+ private void drawFingerPainting(Canvas canvas) {
+ drawManager.fingerPainting.setSmoothness(drawSmoothPainting);
+ drawManager.drawFingerPainting(canvas);
+ }
+
+ // Menu functions
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.main_menu, menu);
+ return true;
+ }
+
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.reset_paint:
+ drawManager.fingerPainting.reset();
+ return true;
+ case R.id.smooth_paint:
+ drawSmoothPainting = true;
+ return true;
+ case R.id.rough_paint:
+ drawSmoothPainting = false;
+ return true;
+ case R.id.draw_circle:
+ currentDrawabletype = DrawingType.circle;
+ return true;
+ case R.id.draw_rect:
+ currentDrawabletype = DrawingType.rect;
+ return true;
+ case R.id.draw_animation:
+ currentDrawabletype = DrawingType.animation;
+ return true;
+ case R.id.draw_text:
+ currentDrawabletype = DrawingType.text;
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+}