Java-only version of SkAR: drawing on android Canvas

Bug: skia:
Change-Id: I3b85fac93a2854d1575f71554e2a7da66e8a6a6f
Reviewed-on: https://skia-review.googlesource.com/138920
Reviewed-by: Mike Reed <reed@google.com>
This commit is contained in:
ziadb 2018-07-02 11:12:27 -04:00 committed by Ziad Ben Hadj-Alouane
parent fe76395c4d
commit bb5b7588d6
20 changed files with 1118 additions and 0 deletions

View File

@ -1,3 +1,4 @@
include ':viewer'
include ':skqp'
include ':arcore' //must build out directory first: bin/gn gen out/arm64 --args='ndk="NDKPATH" target_cpu="ABI" is_component_build=true'
include ':skar_java'

View File

@ -0,0 +1,42 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 27
defaultConfig {
applicationId "com.google.ar.core.examples.java.helloar"
// 24 is the minimum since ARCore only works with 24 and higher.
minSdkVersion 26
targetSdkVersion 27
versionCode 1
versionName "1.0"
}
testOptions {
unitTests.returnDefaultValues = false
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
// ARCore library
implementation 'com.google.ar:core:1.2.0'
// Obj - a simple Wavefront OBJ file loader
// https://github.com/javagl/Obj
implementation 'de.javagl:obj:0.2.1'
implementation 'com.android.support:appcompat-v7:27.0.2'
implementation 'com.android.support:design:27.0.2'
// Required -- JUnit 4 framework
testImplementation 'junit:junit:4.12'
// Optional -- Mockito framework
testImplementation 'org.mockito:mockito-core:1.10.19'
}

View File

@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /opt/android-sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2016 Google Inc.
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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.google.ar.core.examples.java.helloskar">
<uses-permission android:name="android.permission.CAMERA"/>
<!-- This tag indicates that this application requires ARCore. This results in the application
only being visible in the Google Play Store on devices that support ARCore. -->
<uses-feature android:name="android.hardware.camera.ar" android:required="true"/>
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
<application
android:allowBackup="false"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:usesCleartextTraffic="false"
tools:ignore="GoogleAppIndexingWarning">
<activity
android:name="com.google.ar.core.examples.java.helloskar.HelloSkARActivity"
android:configChanges="orientation|screenSize"
android:exported="true"
android:theme="@style/Theme.AppCompat.NoActionBar"
android:screenOrientation="locked">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- This tag indicates that this application requires ARCore. This results in the Google Play
Store downloading and installing ARCore along with the application. -->
<meta-data android:name="com.google.ar.core" android:value="required" />
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@ -0,0 +1,31 @@
/*
* 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.
*/
precision highp float;
uniform sampler2D u_Texture;
uniform vec4 u_dotColor;
uniform vec4 u_lineColor;
uniform vec4 u_gridControl; // dotThreshold, lineThreshold, lineFadeShrink, occlusionShrink
varying vec3 v_TexCoordAlpha;
void main() {
vec4 control = texture2D(u_Texture, v_TexCoordAlpha.xy);
float dotScale = v_TexCoordAlpha.z;
float lineFade = max(0.0, u_gridControl.z * v_TexCoordAlpha.z - (u_gridControl.z - 1.0));
vec3 color = (control.r * dotScale > u_gridControl.x) ? u_dotColor.rgb
: (control.g > u_gridControl.y) ? u_lineColor.rgb * lineFade
: (u_lineColor.rgb * 0.25 * lineFade) ;
gl_FragColor = vec4(color, v_TexCoordAlpha.z * u_gridControl.w);
}

View File

@ -0,0 +1,40 @@
/*
* 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.
*/
uniform mat4 u_Model;
uniform mat4 u_ModelViewProjection;
uniform mat2 u_PlaneUvMatrix;
uniform vec3 u_Normal;
attribute vec3 a_XZPositionAlpha; // (x, z, alpha)
varying vec3 v_TexCoordAlpha;
void main() {
vec4 local_pos = vec4(a_XZPositionAlpha.x, 0.0, a_XZPositionAlpha.y, 1.0);
vec4 world_pos = u_Model * local_pos;
// Construct two vectors that are orthogonal to the normal.
// This arbitrary choice is not co-linear with either horizontal
// or vertical plane normals.
const vec3 arbitrary = vec3(1.0, 1.0, 0.0);
vec3 vec_u = normalize(cross(u_Normal, arbitrary));
vec3 vec_v = normalize(cross(u_Normal, vec_u));
// Project vertices in world frame onto vec_u and vec_v.
vec2 uv = vec2(dot(world_pos.xyz, vec_u), dot(world_pos.xyz, vec_v));
v_TexCoordAlpha = vec3(u_PlaneUvMatrix * uv, a_XZPositionAlpha.z);
gl_Position = u_ModelViewProjection * local_pos;
}

View File

@ -0,0 +1,21 @@
/*
* 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.
*/
precision mediump float;
varying vec4 v_Color;
void main() {
gl_FragColor = v_Color;
}

View File

@ -0,0 +1,28 @@
/*
* 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.
*/
uniform mat4 u_ModelViewProjection;
uniform vec4 u_Color;
uniform float u_PointSize;
attribute vec4 a_Position;
varying vec4 v_Color;
void main() {
v_Color = u_Color;
gl_Position = u_ModelViewProjection * vec4(a_Position.xyz, 1.0);
gl_PointSize = u_PointSize;
}

View File

@ -0,0 +1,24 @@
/*
* 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.
*/
#extension GL_OES_EGL_image_external : require
precision mediump float;
varying vec2 v_TexCoord;
uniform samplerExternalOES sTexture;
void main() {
gl_FragColor = texture2D(sTexture, v_TexCoord);
}

View File

@ -0,0 +1,24 @@
/*
* 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.
*/
attribute vec4 a_Position;
attribute vec2 a_TexCoord;
varying vec2 v_TexCoord;
void main() {
gl_Position = a_Position;
v_TexCoord = a_TexCoord;
}

View File

@ -0,0 +1,45 @@
package com.google.ar.core.examples.java.helloskar;
import android.content.Context;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
/**
* SurfaceView that is overlayed on top of a GLSurfaceView. All 2D drawings can be done on this
* surface.
*/
public class ARSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
boolean running;
public ARSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
SurfaceHolder holder = getHolder();
this.setBackgroundColor(Color.TRANSPARENT);
this.setZOrderOnTop(true);
holder.setFormat(PixelFormat.TRANSPARENT);
holder.addCallback(this);
}
public boolean isRunning() {
return running;
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
running = true;
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
running = false;
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
}

View File

@ -0,0 +1,106 @@
package com.google.ar.core.examples.java.helloskar;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.RectF;
import com.google.skar.SkARMatrix;
import com.google.skar.SkARUtil;
import java.util.ArrayList;
public class DrawManager {
private float[] projectionMatrix = new float[16];
private float[] viewMatrix = new float[16];
private float[] viewportMatrix = new float[16];
private ColorFilter lightFilter;
public ArrayList<float[]> modelMatrices = new ArrayList<>();
public void updateViewportMatrix(float width, float height) {
viewportMatrix = SkARMatrix.createViewportMatrix(width, height);
}
public void updateProjectionMatrix(float[] projectionMatrix) {
this.projectionMatrix = projectionMatrix;
}
public void updateViewMatrix(float[] viewMatrix) {
this.viewMatrix = viewMatrix;
}
public void updateLightColorFilter(float[] colorCorr) {
lightFilter = SkARUtil.createLightCorrectionColorFilter(colorCorr);
}
public void drawCircle(Canvas canvas) {
if (modelMatrices.isEmpty()) {
return;
}
Paint p = new Paint();
p.setColorFilter(lightFilter);
p.setARGB(180, 100, 0, 0);
canvas.save();
canvas.setMatrix(SkARMatrix.createPerspectiveMatrix(modelMatrices.get(0),
viewMatrix, projectionMatrix, viewportMatrix));
canvas.drawCircle(0, 0, 0.1f, p);
canvas.restore();
}
public void drawRect(Canvas canvas) {
if (modelMatrices.isEmpty()) {
return;
}
Paint p = new Paint();
p.setColorFilter(lightFilter);
p.setARGB(180, 0, 0, 255);
canvas.save();
canvas.setMatrix(SkARMatrix.createPerspectiveMatrix(modelMatrices.get(0),
viewMatrix, projectionMatrix, viewportMatrix));
RectF rect = new RectF(0, 0, 0.2f, 0.2f);
canvas.drawRect(rect, p);
canvas.restore();
}
public void drawText(Canvas canvas, String text) {
if (modelMatrices.isEmpty()) {
return;
}
Paint p = new Paint();
float textSize = 100;
p.setColorFilter(lightFilter);
p.setARGB(255, 0, 255, 0);
p.setTextSize(textSize);
float[] scaleMatrix = getTextScaleMatrix(textSize);
float[] rotateMatrix = createXYtoXZRotationMatrix();
float[] actualModel = new float[16];
android.opengl.Matrix.setIdentityM(actualModel, 0);
android.opengl.Matrix.multiplyMM(actualModel, 0, scaleMatrix, 0, rotateMatrix, 0);
android.opengl.Matrix.multiplyMM(actualModel, 0, modelMatrices.get(0), 0, actualModel, 0);
canvas.save();
canvas.setMatrix(SkARMatrix.createPerspectiveMatrix(actualModel,
viewMatrix, projectionMatrix, viewportMatrix, false));
canvas.drawText(text, 0, 0, p);
canvas.restore();
}
private float[] getTextScaleMatrix(float size) {
float scaleFactor = 1 / (size * 10);
float[] initScale = new float[16];
android.opengl.Matrix.setIdentityM(initScale, 0);
android.opengl.Matrix.scaleM(initScale, 0, scaleFactor, scaleFactor, scaleFactor);
return initScale;
}
private float[] createXYtoXZRotationMatrix() {
float[] skiaRotation = new float[16];
android.opengl.Matrix.setIdentityM(skiaRotation, 0);
android.opengl.Matrix.rotateM(skiaRotation, 0, 90, 1, 0, 0);
return skiaRotation;
}
}

View File

@ -0,0 +1,355 @@
/*
* 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.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
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.examples.java.common.rendering.PlaneRenderer;
import com.google.ar.core.examples.java.common.rendering.PointCloudRenderer;
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 HelloSkARActivity extends AppCompatActivity implements GLSurfaceView.Renderer {
private static final String TAG = HelloSkARActivity.class.getSimpleName();
//Simple SurfaceView used to draw 2D objects on top of the GLSurfaceView
private ARSurfaceView arSurfaceView;
//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;
//Renderers
private final BackgroundRenderer backgroundRenderer = new BackgroundRenderer();
private final PlaneRenderer planeRenderer = new PlaneRenderer();
private final PointCloudRenderer pointCloudRenderer = new PointCloudRenderer();
//2D Renderer
private DrawManager drawManager = new DrawManager();
// Temporary matrix allocated here to reduce number of allocations for each frame.
private final float[] anchorMatrix = new float[16];
// Anchors created from taps used for object placing.
private final ArrayList<Anchor> anchors = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
arSurfaceView = findViewById(R.id.arsurfaceview);
glSurfaceView = findViewById(R.id.glsurfaceview);
arSurfaceView.bringToFront();
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;
}
@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(/*context=*/ this);
planeRenderer.createOnGlThread(/*context=*/ this, "models/trigrid.png");
pointCloudRenderer.createOnGlThread(/*context=*/ this);
} 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.updateViewportMatrix(width, height);
}
@Override
public void onDrawFrame(GL10 gl) {
// 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())
&& (PlaneRenderer.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.
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);
PointCloud pointCloud = frame.acquirePointCloud();
pointCloudRenderer.update(pointCloud);
pointCloudRenderer.draw(viewmtx, projmtx);
pointCloud.release();
// 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;
}
}
}
// Visualize planes.
planeRenderer.drawPlanes(
session.getAllTrackables(Plane.class), camera.getDisplayOrientedPose(), projmtx);
// Draw models using Canvas
if (arSurfaceView.isRunning()) {
drawModels();
}
} catch (Throwable t) {
// Avoid crashing the application due to unhandled exceptions.
Log.e(TAG, "Exception on the OpenGL thread", t);
}
}
private void drawModels() {
SurfaceHolder holder = arSurfaceView.getHolder();
Canvas canvas = holder.lockHardwareCanvas();
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
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);
drawManager.drawRect(canvas);
drawManager.drawCircle(canvas);
drawManager.drawText(canvas, "HelloSkAR");
}
holder.unlockCanvasAndPost(canvas);
}
}

View File

@ -0,0 +1,211 @@
package com.google.skar;
import android.graphics.Matrix;
/**
* Provides static methods for matrix manipulation. Input matrices are assumed to be 4x4
* android.opengl.Matrix types. Output matrices are 3x3 android.graphics.Matrix types.
* The main use of this class is to be able to get a Matrix for a Canvas that applies perspective
* to 2D objects
*/
public class SkARMatrix {
/**
* Returns an android.graphics.Matrix that can be used on a Canvas to draw a 2D object in
* perspective. Objects will be rotated towards the XZ plane. Undefined behavior when any of
* the matrices are not of size 16, or are null.
*
* @param model 4x4 model matrix of the object to be drawn (global/world)
* @param view 4x4 camera view matrix (brings objects to camera origin and orientation)
* @param projection 4x4 projection matrix
* @param viewport 4x4 viewport matrix
* @return 3x3 matrix that puts a 2D objects in perspective on a Canvas
*/
public static Matrix createPerspectiveMatrix(float[] model, float[] view, float[] projection,
float[] viewport) {
float[] skiaRotation = createXYtoXZRotationMatrix();
float[][] matrices = {skiaRotation, model, view, projection, viewport};
return createMatrixFrom4x4Array(matrices);
}
/**
* Returns an android.graphics.Matrix that can be used on a Canvas to draw a 2D object in
* perspective. Undefined behavior when any of the matrices are not of size 16, or are null.
*
* @param model 4x4 model matrix of the object to be drawn (global/world)
* @param view 4x4 camera view matrix (brings objects to camera origin and orientation)
* @param projection 4x4 projection matrix
* @param viewport 4x4 viewport matrix
* @param rotatePlane specifies if object should be from the XY plane to the XZ plane
* @return 3x3 matrix that puts a 2D objects in perspective on a Canvas
*/
public static Matrix createPerspectiveMatrix(float[] model, float[] view, float[] projection,
float[] viewport, boolean rotatePlane) {
if (rotatePlane) {
return createPerspectiveMatrix(model, view, projection, viewport);
} else {
float[][] matrices = {model, view, projection, viewport};
return createMatrixFrom4x4Array(matrices);
}
}
/**
* Returns an android.graphics.Matrix that can be used on a Canvas to draw a 2D object in
* perspective. Undefined behavior when any of the matrices are not of size 16, or are null.
*
* @param model 4x4 model matrix of the object to be drawn (global/world)
* @param view 4x4 camera view matrix (brings objects to camera origin and orientation)
* @param projection 4x4 projection matrix
* @param viewPortWidth width of viewport of GLSurfaceView
* @param viewPortHeight height of viewport of GLSurfaceView
* @param rotatePlane specifies if object should be from the XY plane to the XZ plane
* @return 3x3 matrix that puts a 2D objects in perspective on a Canvas
*/
public static Matrix createPerspectiveMatrix(float[] model, float[] view, float[] projection,
float viewPortWidth, float viewPortHeight, boolean rotatePlane) {
if (rotatePlane) {
return createPerspectiveMatrix(model, view, projection, viewPortWidth, viewPortHeight);
} else {
float[] viewPort = createViewportMatrix(viewPortWidth, viewPortHeight);
float[][] matrices = {model, view, projection, viewPort};
return createMatrixFrom4x4Array(matrices);
}
}
/**
* Returns an android.graphics.Matrix that can be used on a Canvas to draw a 2D object in
* perspective. Object will be rotated towards the XZ plane. Undefined behavior when any of
* the matrices are not of size 16, or are null.
*
* @param model 4x4 model matrix of the object to be drawn (global/world)
* @param view 4x4 camera view matrix (brings objects to camera origin and orientation)
* @param projection 4x4 projection matrix
* @param viewPortWidth width of viewport of GLSurfaceView
* @param viewPortHeight height of viewport of GLSurfaceView
* @return 3x3 matrix that puts a 2D objects in perspective on a Canvas
*/
public static Matrix createPerspectiveMatrix(float[] model, float[] view, float[] projection,
float viewPortWidth, float viewPortHeight) {
float[] viewPort = createViewportMatrix(viewPortWidth, viewPortHeight);
float[] skiaRotation = createXYtoXZRotationMatrix();
float[][] matrices = {skiaRotation, model, view, projection, viewPort};
return createMatrixFrom4x4Array(matrices);
}
/**
* Returns a 16-float matrix in column-major order that represents a viewport matrix given
* the width and height of the viewport.
*
* @param width width of viewport
* @param height height of viewport
*/
public static float[] createViewportMatrix(float width, float height) {
float[] viewPort = new float[16];
android.opengl.Matrix.setIdentityM(viewPort, 0);
android.opengl.Matrix.translateM(viewPort, 0, width / 2, height / 2, 0);
android.opengl.Matrix.scaleM(viewPort, 0, width / 2, -height / 2, 0);
return viewPort;
}
/**
* Returns a 16-float matrix in column-major order that is used to rotate objects from the XY plane
* to the XZ plane. This is useful given that objects drawn on the Canvas are on the XY plane.
* In order to get objects to appear as if they are sticking on planes/ceilings/walls, we need
* to rotate them from the XY plane to the XZ plane.
*/
private static float[] createXYtoXZRotationMatrix() {
float[] rotation = new float[16];
android.opengl.Matrix.setIdentityM(rotation, 0);
android.opengl.Matrix.rotateM(rotation, 0, 90, 1, 0, 0);
return rotation;
}
/**
* Returns an android.graphics.Matrix resulting from a 9-float matrix array in row-major order.
* Undefined behavior when the array is not of size 9 or is null.
*
* @param m3 9-float matrix array in row-major order
*/
public static Matrix createMatrixFrom3x3(float[] m3) {
Matrix m = new Matrix();
m.setValues(m3);
return m;
}
/**
* Returns an android.graphics.Matrix resulting from a 16-float matrix array in column-major order
* Undefined behavior when the array is not of size 16 or is null.
*
* @param m4
*/
public static Matrix createMatrixFrom4x4(float[] m4) {
float[] m3 = matrix4x4ToMatrix3x3(m4);
return createMatrixFrom3x3(m3);
}
/**
* Returns an android.graphics.Matrix resulting from the concatenation of 16-float matrices
* in column-major order from left to right.
* e.g: m4Array = {m1, m2, m3} --> returns m = m3 * m2 * m1
* Undefined behavior when the array is empty, null, or contains arrays not of size 9 (or null)
*
* @param m4Array array of 16-float matrices in column-major order
*/
public static Matrix createMatrixFrom4x4Array(float[][] m4Array) {
float[] result = multiplyMatrices4x4(m4Array);
return createMatrixFrom4x4(result);
}
/**
* Returns a 9-float matrix in row-major order given a 16-float matrix in column-major order.
* This will drop the Z column and row.
* Undefined behavior when the array is not of size 9 or is null.
*
* @param m4 16-float matrix in column-major order
*/
private static float[] matrix4x4ToMatrix3x3(float[] m4) {
float[] m3 = new float[9];
int j = 0;
for (int i = 0; i < 7; i = i + 3) {
if (j == 2) {
j++; //skip row #3
}
m3[i] = m4[j];
m3[i + 1] = m4[j + 4];
m3[i + 2] = m4[j + 12];
j++;
}
return m3;
}
/**
* Returns a 16-float matrix in column-major order resulting from the multiplication of matrices.
* e.g: m4Array = {m1, m2, m3} --> returns m = m3 * m2 * m1
* Undefined behavior when the array is empty, null, or contains arrays not of size 9 (or null)
*
* @param m4Array array of 16-float matrices in column-major order
*/
private static float[] multiplyMatrices4x4(float[][] m4Array) {
float[] result = new float[16];
android.opengl.Matrix.setIdentityM(result, 0);
float[] rhs = result;
for (int i = 0; i < m4Array.length; i++) {
float[] lhs = m4Array[i];
android.opengl.Matrix.multiplyMM(result, 0, lhs, 0, rhs, 0);
rhs = result;
}
return result;
}
}

View File

@ -0,0 +1,31 @@
package com.google.skar;
import android.graphics.ColorFilter;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import java.util.Arrays;
public class SkARUtil {
private static final float MIDDLE_GRAY_GAMMA = 0.466f;
/**
* Returns a ColorFilter that can be used on a Paint to apply color correction effects
* as documented by ARCore in
* <a href="https://developers.google.com/ar/reference/java/com/google/ar/core/LightEstimate">LightEstimate</a>
*
* @param colorCorr output array of
* <a href="https://developers.google.com/ar/reference/java/com/google/ar/core/LightEstimate.html#getColorCorrection(float[],%20int)">getColorCorrection()</a>
* @return ColorFilter with effects applied
*/
public static ColorFilter createLightCorrectionColorFilter(float[] colorCorr) {
float[] colorCorrCopy = Arrays.copyOf(colorCorr, 4);
for (int i = 0; i < 3; i++) {
colorCorrCopy[i] *= colorCorrCopy[3] / MIDDLE_GRAY_GAMMA;
}
ColorMatrix m = new ColorMatrix();
m.setScale(colorCorrCopy[0], colorCorrCopy[1], colorCorrCopy[2], 1);
return new ColorMatrixColorFilter(m);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -0,0 +1,39 @@
<!--
Copyright 2016 Google Inc.
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.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.google.ar.core.examples.java.helloskar.HelloSkARActivity">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.ar.core.examples.java.helloskar.ARSurfaceView
android:id="@+id/arsurfaceview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<android.opengl.GLSurfaceView
android:id="@+id/glsurfaceview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
</RelativeLayout>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?><!--
Copyright 2016 Google Inc.
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.
-->
<resources>
<string name="app_name">HelloSkAR Java</string>
</resources>

View File

@ -0,0 +1,35 @@
<!--
Copyright 2016 Google Inc.
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.
-->
<resources>
<!--
Base application theme, dependent on API level. This theme is replaced
by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
-->
<style name="AppBaseTheme" parent="android:Theme.Light">
<!--
Theme customizations available in newer API levels can go in
res/values-vXX/styles.xml, while customizations related to
backward-compatibility can go here.
-->
</style>
<!-- Application theme. -->
<style name="AppTheme" parent="AppBaseTheme">
<!-- All customizations that are NOT specific to a particular API-level can go here. -->
</style>
</resources>