SkAR: drawing text, shapes, rotation modes enabled, translating objects

To run this app, you need to create an out directory as such:
bin/gn gen out/arm64 --args='ndk="NDK_PATH" target_cpu="ABI"'

For now, the only supported ABI is arm64

Change-Id: I012f0c6a0550d80a0028f42177d5ca72974d848d
Bug: skia:
Reviewed-on: https://skia-review.googlesource.com/130980
Reviewed-by: Mike Reed <reed@google.com>
Reviewed-by: Mike Klein <mtklein@google.com>
Commit-Queue: Ziad Ben Hadj-Alouane <ziadb@google.com>
This commit is contained in:
ziadb 2018-06-19 13:57:15 -04:00 committed by Ziad Ben Hadj-Alouane
parent 80452bee11
commit bceddbcb72
45 changed files with 5886 additions and 21 deletions

4
.gitignore vendored
View File

@ -29,6 +29,10 @@ platform_tools/android/apps/build
platform_tools/android/apps/*.properties
platform_tools/android/apps/*/build
platform_tools/android/apps/*/src/main/libs
platform_tools/android/apps/*.hprof
platform_tools/android/apps/*/.externalNativeBuild
sampleapp_prefs.txt
/skps
third_party/externals
tools/skp/page_sets/data/*.json

View File

@ -1995,6 +1995,60 @@ if (skia_enable_tools) {
]
}
if (is_android) {
test_app("arcore") {
is_shared_library = true
configs = [
":skia_public",
"gn:default",
]
# For internship expedience, yes, we're rebuilding Skia rather than depending on :skia.
# At the moment there's no way to use Skia and Skottie/SkShaper unless they're in the same .so.
sources = []
sources += skia_core_sources
sources += skia_utils_sources
sources += skia_xps_sources
sources += [
"src/android/SkAndroidFrameworkUtils.cpp",
"src/android/SkAnimatedImage.cpp",
"src/android/SkBitmapRegionCodec.cpp",
"src/android/SkBitmapRegionDecoder.cpp",
"src/codec/SkAndroidCodec.cpp",
"src/codec/SkBmpBaseCodec.cpp",
"src/codec/SkBmpCodec.cpp",
"src/codec/SkBmpMaskCodec.cpp",
"src/codec/SkBmpRLECodec.cpp",
"src/codec/SkBmpStandardCodec.cpp",
"src/codec/SkCodec.cpp",
"src/codec/SkCodecImageGenerator.cpp",
"src/codec/SkColorTable.cpp",
"src/codec/SkGifCodec.cpp",
"src/codec/SkMaskSwizzler.cpp",
"src/codec/SkMasks.cpp",
"src/codec/SkSampledCodec.cpp",
"src/codec/SkSampler.cpp",
"src/codec/SkStreamBuffer.cpp",
"src/codec/SkSwizzler.cpp",
"src/codec/SkWbmpCodec.cpp",
"src/images/SkImageEncoder.cpp",
"src/ports/SkDiscardableMemory_none.cpp",
"src/ports/SkImageGenerator_skia.cpp",
"src/ports/SkMemory_malloc.cpp",
"src/ports/SkOSFile_stdio.cpp",
"src/sfnt/SkOTTable_name.cpp",
"src/sfnt/SkOTUtils.cpp",
"src/utils/mac/SkStream_mac.cpp",
"third_party/gif/SkGifImageReader.cpp",
]
deps = [
":tool_utils",
"modules/skottie",
"modules/skshaper",
]
}
}
if (!skia_use_angle && (is_linux || is_win || is_mac)) {
test_app("HelloWorld") {
sources = [

19
include/utils/Sk3D.h Normal file
View File

@ -0,0 +1,19 @@
/*
* Copyright 2018 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef Sk3D_DEFINED
#define Sk3D_DEFINED
#include "SkPoint3.h"
#include "SkMatrix44.h"
SK_API void Sk3LookAt(SkMatrix44* dst, const SkPoint3& eye, const SkPoint3& center, const SkPoint3& up);
SK_API bool Sk3Perspective(SkMatrix44* dst, float near, float far, float angle);
SK_API void Sk3MapPts(SkPoint dst[], const SkMatrix44& m4, const SkPoint3 src[], int count);
#endif

View File

@ -0,0 +1,74 @@
# Copyright (C) 2018 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.
##
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
# Import the ARCore library.
add_library(arcore SHARED IMPORTED)
set_target_properties(arcore PROPERTIES IMPORTED_LOCATION
"${ARCORE_LIBPATH}/${ANDROID_ABI}/libarcore_sdk_c.so")
add_library(sk_skia SHARED IMPORTED)
set_target_properties(sk_skia PROPERTIES IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/src/main/libs/${ANDROID_ABI}/libarcore.so")
# This is the main app library.
add_library(hello_ar_native SHARED
"src/main/cpp/hello_ar_application.cc"
"src/main/cpp/background_renderer.cc"
"src/main/cpp/jni_interface.cc"
"src/main/cpp/plane_renderer.cc"
"src/main/cpp/point_cloud_renderer.cc"
"src/main/cpp/util.cc"
"src/main/cpp/pending_anchor.cc"
"src/main/cpp/anchor_wrapper.cc")
target_include_directories(hello_ar_native PRIVATE
#BASIC AR NATIVE CODE
"src/main/cpp"
#ARCORE LIBRARY
"${ARCORE_INCLUDE}"
#GLM
"${ANDROID_NDK}/sources/third_party/vulkan/src/libs/glm"
#SKIA INCLUDE DIRECTORIES
"${SKIA_INCLUDE_PATH}/../modules/skshaper/include"
"${SKIA_INCLUDE_PATH}/../modules/skottie/include"
"${SKIA_INCLUDE_PATH}/../tools"
"${SKIA_INCLUDE_PATH}/../gm"
"${SKIA_INCLUDE_PATH}/core"
"${SKIA_INCLUDE_PATH}/config"
"${SKIA_INCLUDE_PATH}/gpu"
"${SKIA_INCLUDE_PATH}/android"
"${SKIA_INCLUDE_PATH}/atlastext"
"${SKIA_INCLUDE_PATH}/c"
"${SKIA_INCLUDE_PATH}/codec"
"${SKIA_INCLUDE_PATH}/effects"
"${SKIA_INCLUDE_PATH}/encode"
"${SKIA_INCLUDE_PATH}/pathops"
"${SKIA_INCLUDE_PATH}/ports"
"${SKIA_INCLUDE_PATH}/private"
"${SKIA_INCLUDE_PATH}/svg"
"${SKIA_INCLUDE_PATH}/utils"
"${SKIA_INCLUDE_PATH}/views")
target_link_libraries(hello_ar_native
android
log
GLESv2
arcore
sk_skia)

View File

@ -0,0 +1,92 @@
/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
apply plugin: 'com.android.application'
/*
The arcore aar library contains the native shared libraries. These are
extracted before building to a temporary directory.
*/
def arcore_libpath = "${buildDir}/arcore-native"
// Create a configuration to mark which aars to extract .so files from
configurations { natives }
android {
sourceSets.main.jni.srcDirs = [] //disable automatic ndk-build call
sourceSets.main.jniLibs.srcDir "src/main/libs"
productFlavors { arm64 {} }
setupSkiaLibraryBuild(project, applicationVariants, "libarcore")
compileSdkVersion 27
defaultConfig {
applicationId "org.skia.viewer"
// 24 is the minimum since ARCore only works with 24 and higher.
minSdkVersion 24
targetSdkVersion 27
versionCode 1
versionName "1.0"
externalNativeBuild {
cmake {
cppFlags "-std=c++11", "-Wall"
arguments "-DANDROID_STL=c++_static",
"-DARCORE_LIBPATH=${arcore_libpath}/jni",
"-DARCORE_INCLUDE=${project.rootDir}/../../libraries/include",
"-DSKIA_INCLUDE_PATH=${project.rootDir}/../../../include"
}
}
ndk {
abiFilters "arm64-v8a"
}
}
flavorDimensions "base"
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
dependencies {
// ARCore library
implementation 'com.google.ar:core:1.2.0'
natives 'com.google.ar:core:1.2.0'
implementation 'com.android.support:appcompat-v7:27.0.2'
implementation 'com.android.support:design:27.0.2'
}
// Extracts the shared libraries from aars in the natives configuration.
// This is done so that NDK builds can access these libraries.
task extractNativeLibraries() {
doFirst {
configurations.natives.files.each { f ->
copy {
from zipTree(f)
into arcore_libpath
include "jni/**/*"
}
}
}
}
tasks.whenTaskAdded {
task-> if (task.name.contains("external") && !task.name.contains("Clean")) {
task.dependsOn(extractNativeLibraries)
//make sure skia lib is built and copied in the correct directory before building arcore
tasks.whenTaskAdded {
t-> if (t.name.contains("CopySkiaLib")) {
task.dependsOn(t)
}
}
}
}

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.skia.arcore">
<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="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat.Light.NoActionBar"
android:usesCleartextTraffic="false">
<!-- 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" />
<activity
android:name=".HelloArActivity"
android:label="@string/app_name"
android:configChanges="orientation|screenSize"
android:exported="true"
android:theme="@style/Theme.AppCompat.DayNight.NoActionBar"
android:screenOrientation="locked">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

View File

@ -0,0 +1,41 @@
/*
* Copyright 2018 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.
*/
#include "hello_ar_application.h"
#include "arcore_c_api.h"
#include "anchor_wrapper.h"
namespace hello_ar {
AnchorWrapper::AnchorWrapper(ArAnchor *anchor) : anchor(anchor) {}
const ArAnchor* AnchorWrapper::GetArAnchor() {
return anchor;
}
DrawableType AnchorWrapper::GetDrawableType() {
return drawableType;
}
void AnchorWrapper::SetArAnchor(ArAnchor* anchor) {
this->anchor = anchor;
}
void AnchorWrapper::SetDrawableType(DrawableType drawableType) {
this->drawableType = drawableType;
}
} // namespace hello_ar

View File

@ -0,0 +1,45 @@
/*
* Copyright 2018 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.
*/
#ifndef C_ARCORE_HELLO_AR_ANCHOR_WRAPPER_H_
#define C_ARCORE_HELLO_AR_ANCHOR_WRAPPER_H_
#include "arcore_c_api.h"
namespace hello_ar {
enum DrawableType {
TEXT = 0, CIRCLE = 1, RECT = 2
};
class AnchorWrapper {
public:
AnchorWrapper(ArAnchor* anchor);
const ArAnchor* GetArAnchor();
DrawableType GetDrawableType();
bool GetInEditMode();
void SetArAnchor(ArAnchor* anchor);
void SetDrawableType(DrawableType drawableType);
void SetInEditMode(bool inEditMode);
private:
ArAnchor* anchor;
DrawableType drawableType;
bool inEditMode = false;
};
} // namespace hello_ar
#endif

View File

@ -0,0 +1,113 @@
/*
* 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.
*/
// This modules handles drawing the passthrough camera image into the OpenGL
// scene.
#include <type_traits>
#include "background_renderer.h"
namespace hello_ar {
namespace {
// Positions of the quad vertices in clip space (X, Y, Z).
const GLfloat kVertices[] = {
-1.0f, -1.0f, 0.0f, +1.0f, -1.0f, 0.0f,
-1.0f, +1.0f, 0.0f, +1.0f, +1.0f, 0.0f,
};
// UVs of the quad vertices (S, T)
const GLfloat kUvs[] = {
0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
};
constexpr char kVertexShader[] = R"(
attribute vec4 vertex;
attribute vec2 textureCoords;
varying vec2 v_textureCoords;
void main() {
v_textureCoords = textureCoords;
gl_Position = vertex;
})";
constexpr char kFragmentShader[] = R"(
#extension GL_OES_EGL_image_external : require
precision mediump float;
uniform samplerExternalOES texture;
varying vec2 v_textureCoords;
void main() {
gl_FragColor = texture2D(texture, v_textureCoords);
})";
} // namespace
void BackgroundRenderer::InitializeGlContent() {
shader_program_ = util::CreateProgram(kVertexShader, kFragmentShader);
if (!shader_program_) {
LOGE("Could not create program.");
}
glGenTextures(1, &texture_id_);
glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture_id_);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
uniform_texture_ = glGetUniformLocation(shader_program_, "texture");
attribute_vertices_ = glGetAttribLocation(shader_program_, "vertex");
attribute_uvs_ = glGetAttribLocation(shader_program_, "textureCoords");
}
void BackgroundRenderer::Draw(const ArSession *session, const ArFrame *frame) {
static_assert(std::extent<decltype(kUvs)>::value == kNumVertices * 2,
"Incorrect kUvs length");
static_assert(std::extent<decltype(kVertices)>::value == kNumVertices * 3,
"Incorrect kVertices length");
// If display rotation changed (also includes view size change), we need to
// re-query the uv coordinates for the on-screen portion of the camera image.
int32_t geometry_changed = 0;
ArFrame_getDisplayGeometryChanged(session, frame, &geometry_changed);
if (geometry_changed != 0 || !uvs_initialized_) {
ArFrame_transformDisplayUvCoords(session, frame, kNumVertices * 2, kUvs,
transformed_uvs_);
uvs_initialized_ = true;
}
glUseProgram(shader_program_);
glDepthMask(GL_FALSE);
glUniform1i(uniform_texture_, 1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture_id_);
glEnableVertexAttribArray(attribute_vertices_);
glVertexAttribPointer(attribute_vertices_, 3, GL_FLOAT, GL_FALSE, 0,
kVertices);
glEnableVertexAttribArray(attribute_uvs_);
glVertexAttribPointer(attribute_uvs_, 2, GL_FLOAT, GL_FALSE, 0,
transformed_uvs_);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glUseProgram(0);
glDepthMask(GL_TRUE);
util::CheckGlError("BackgroundRenderer::Draw() error");
}
GLuint BackgroundRenderer::GetTextureId() const { return texture_id_; }
} // namespace hello_ar

View File

@ -0,0 +1,61 @@
/*
* 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.
*/
#ifndef C_ARCORE_HELLO_AR_BACKGROUND_RENDERER_H_
#define C_ARCORE_HELLO_AR_BACKGROUND_RENDERER_H_
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <cstdlib>
#include "arcore_c_api.h"
#include "util.h"
namespace hello_ar {
// This class renders the passthrough camera image into the OpenGL frame.
class BackgroundRenderer {
public:
BackgroundRenderer() = default;
~BackgroundRenderer() = default;
// Sets up OpenGL state. Must be called on the OpenGL thread and before any
// other methods below.
void InitializeGlContent();
// Draws the background image. This methods must be called for every ArFrame
// returned by ArSession_update() to catch display geometry change events.
void Draw(const ArSession *session, const ArFrame *frame);
// Returns the generated texture name for the GL_TEXTURE_EXTERNAL_OES target.
GLuint GetTextureId() const;
private:
static constexpr int kNumVertices = 4;
GLuint shader_program_;
GLuint texture_id_;
GLuint attribute_vertices_;
GLuint attribute_uvs_;
GLuint uniform_texture_;
float transformed_uvs_[kNumVertices * 2];
bool uvs_initialized_ = false;
};
} // namespace hello_ar
#endif // C_ARCORE_HELLO_AR_BACKGROUND_RENDERER_H_

View File

@ -0,0 +1,25 @@
/*
* 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.
*/
#ifndef C_ARCORE_HELLOE_AR_GLM_H_
#define C_ARCORE_HELLOE_AR_GLM_H_
#define GLM_FORCE_RADIANS 1
#include "glm.hpp"
#include "gtc/matrix_transform.hpp"
#include "gtc/type_ptr.hpp"
#include "gtx/quaternion.hpp"
#endif

View File

@ -0,0 +1,987 @@
/*
* 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.
*/
#include "hello_ar_application.h"
#include <gtx/string_cast.hpp>
#include "anchor_wrapper.h"
#include "plane_renderer.h"
#include "pending_anchor.h"
#include "util.h"
#include "SkCanvas.h"
#include "GrContext.h"
#include "gl/GrGLTypes.h"
#include "SkSurface.h"
#include "SkTypeface.h"
#include "SkFontStyle.h"
#include "GrBackendSurface.h"
#include "SkMatrix44.h"
#include "SkMatrix.h"
#include "SkTextBlob.h"
#include "glm.h"
#include "SkPoint3.h"
#include "Sk3D.h"
#include <math.h> /* acos */
#include "SkShaper.h"
#include "Skottie.h"
#include "SkAnimTimer.h"
#include "Resources.h"
#include "SkStream.h"
namespace hello_ar {
namespace {
constexpr size_t kMaxNumberOfAndroidsToRender = 1;
constexpr int32_t kPlaneColorRgbaSize = 16;
const glm::vec3 kWhite = {255, 255, 255};
constexpr std::array<uint32_t, kPlaneColorRgbaSize> kPlaneColorRgba = {
{0xFFFFFFFF, 0xF44336FF, 0xE91E63FF, 0x9C27B0FF, 0x673AB7FF, 0x3F51B5FF,
0x2196F3FF, 0x03A9F4FF, 0x00BCD4FF, 0x009688FF, 0x4CAF50FF, 0x8BC34AFF,
0xCDDC39FF, 0xFFEB3BFF, 0xFFC107FF, 0xFF9800FF}};
inline glm::vec3 GetRandomPlaneColor() {
const int32_t colorRgba = kPlaneColorRgba[std::rand() % kPlaneColorRgbaSize];
return glm::vec3(((colorRgba >> 24) & 0xff) / 255.0f,
((colorRgba >> 16) & 0xff) / 255.0f,
((colorRgba >> 8) & 0xff) / 255.0f);
}
} // namespace
HelloArApplication::HelloArApplication(AAssetManager *asset_manager)
: asset_manager_(asset_manager) {
LOGI("OnCreate()");
}
HelloArApplication::~HelloArApplication() {
if (ar_session_ != nullptr) {
ArSession_destroy(ar_session_);
ArFrame_destroy(ar_frame_);
}
}
void HelloArApplication::OnPause() {
LOGI("OnPause()");
if (ar_session_ != nullptr) {
ArSession_pause(ar_session_);
}
}
void HelloArApplication::OnResume(void *env, void *context, void *activity) {
LOGI("OnResume()");
if (ar_session_ == nullptr) {
ArInstallStatus install_status;
// If install was not yet requested, that means that we are resuming the
// activity first time because of explicit user interaction (such as
// launching the application)
bool user_requested_install = !install_requested_;
// === ATTENTION! ATTENTION! ATTENTION! ===
// This method can and will fail in user-facing situations. Your
// application must handle these cases at least somewhat gracefully. See
// HelloAR Java sample code for reasonable behavior.
CHECK(ArCoreApk_requestInstall(env, activity, user_requested_install,
&install_status) == AR_SUCCESS);
switch (install_status) {
case AR_INSTALL_STATUS_INSTALLED:
break;
case AR_INSTALL_STATUS_INSTALL_REQUESTED:
install_requested_ = true;
return;
}
// === ATTENTION! ATTENTION! ATTENTION! ===
// This method can and will fail in user-facing situations. Your
// application must handle these cases at least somewhat gracefully. See
// HelloAR Java sample code for reasonable behavior.
CHECK(ArSession_create(env, context, &ar_session_) == AR_SUCCESS);
CHECK(ar_session_);
ArFrame_create(ar_session_, &ar_frame_);
CHECK(ar_frame_);
ArSession_setDisplayGeometry(ar_session_, display_rotation_, width_,
height_);
}
const ArStatus status = ArSession_resume(ar_session_);
CHECK(status == AR_SUCCESS);
}
void HelloArApplication::OnSurfaceCreated() {
LOGI("OnSurfaceCreated()");
background_renderer_.InitializeGlContent();
point_cloud_renderer_.InitializeGlContent();
plane_renderer_.InitializeGlContent(asset_manager_);
}
void HelloArApplication::OnDisplayGeometryChanged(int display_rotation,
int width, int height) {
LOGI("OnSurfaceChanged(%d, %d)", width, height);
glViewport(0, 0, width, height);
display_rotation_ = display_rotation;
width_ = width;
height_ = height;
if (ar_session_ != nullptr) {
ArSession_setDisplayGeometry(ar_session_, display_rotation, width, height);;
}
}
void HelloArApplication::OnObjectRotationChanged(int rotation) {
LOGI("OnObjectRotationChanged(%d)", rotation);
currentObjectRotation = rotation;
}
void HelloArApplication::OnAction(float value) {
LOGI("OnAction(%.6f)", value);
currentValue = value;
}
void DrawText(SkCanvas *canvas, SkPaint *paint, const char text[]) {
float spacing = 0.05;
for (int i = 0; i < sizeof(text) / sizeof(text[0]); i++) {
const char letter[] = {text[i]};
size_t byteLength = strlen(static_cast<const char *>(letter));
canvas->drawText(letter, byteLength, spacing * i, 0, *paint);
}
}
void DrawAxes(SkCanvas *canvas, SkMatrix44 m) {
SkPaint p;
p.setStrokeWidth(10);
SkPoint3 src[4] = {
{0, 0, 0},
{0.2, 0, 0},
{0, 0.2, 0},
{0, 0, 0.2},
};
SkPoint dst[4];
Sk3MapPts(dst, m, src, 4);
const char str[] = "XYZ";
p.setColor(SK_ColorRED);
canvas->drawLine(dst[0], dst[1], p);
p.setColor(SK_ColorGREEN);
canvas->drawLine(dst[0], dst[2], p);
p.setColor(SK_ColorBLUE);
canvas->drawLine(dst[0], dst[3], p);
}
void DrawVector(SkCanvas *canvas, SkMatrix44 m, glm::vec3 begin, glm::vec3 end, SkColor c) {
SkPaint p;
p.setStrokeWidth(15);
SkPoint3 src[2] = {
{begin.x, begin.y, begin.z},
{end.x, end.y, end.z}
};
SkPoint dst[2];
Sk3MapPts(dst, m, src, 2);
const char str[] = "XYZ";
p.setColor(c);
canvas->drawLine(dst[0], dst[1], p);
}
void DrawBoundingBox(SkCanvas* canvas) {
SkPaint paint;
paint.setColor(SK_ColorYELLOW);
SkIRect bounds = canvas->getDeviceClipBounds();
SkRect b = SkRect::Make(bounds);
canvas->drawRect(b, paint);
}
void HelloArApplication::OnDrawFrame() {
grContext = GrContext::MakeGL();
GrBackendRenderTarget target;
sk_sp<SkSurface> surface = nullptr;
GrGLFramebufferInfo framebuffer_info;
framebuffer_info.fFBOID = 0;
framebuffer_info.fFormat = 0x8058;
glClearColor(0.9f, 0.9f, 0.9f, 1.0f);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (ar_session_ == nullptr) return;
ArSession_setCameraTextureName(ar_session_,
background_renderer_.GetTextureId());
// Update session to get current frame and render camera background.
if (ArSession_update(ar_session_, ar_frame_) != AR_SUCCESS) {
LOGE("HelloArApplication::OnDrawFrame ArSession_update error");
}
// GET CAMERA INFO
ArCamera *ar_camera;
ArFrame_acquireCamera(ar_session_, ar_frame_, &ar_camera);
glm::mat4 view_mat;
glm::mat4 projection_mat;
ArCamera_getViewMatrix(ar_session_, ar_camera, glm::value_ptr(view_mat));
ArCamera_getProjectionMatrix(ar_session_, ar_camera,
/*near=*/0.1f, /*far=*/100.f,
glm::value_ptr(projection_mat));
ArTrackingState camera_tracking_state;
ArCamera_getTrackingState(ar_session_, ar_camera, &camera_tracking_state);
ArCamera_release(ar_camera);
background_renderer_.Draw(ar_session_, ar_frame_);
// If the camera isn't tracking don't bother rendering other objects.
if (camera_tracking_state != AR_TRACKING_STATE_TRACKING) {
return;
}
// Get light estimation value.
ArLightEstimate *ar_light_estimate;
ArLightEstimateState ar_light_estimate_state;
ArLightEstimate_create(ar_session_, &ar_light_estimate);
ArFrame_getLightEstimate(ar_session_, ar_frame_, ar_light_estimate);
ArLightEstimate_getState(ar_session_, ar_light_estimate,
&ar_light_estimate_state);
// Set light intensity to default. Intensity value ranges from 0.0f to 1.0f.
// The first three components are color scaling factors.
// The last one is the average pixel intensity in gamma space.
float color_correction[4] = {1.f, 1.f, 1.f, 1.f};
if (ar_light_estimate_state == AR_LIGHT_ESTIMATE_STATE_VALID) {
ArLightEstimate_getColorCorrection(ar_session_, ar_light_estimate,
color_correction);
}
ArLightEstimate_destroy(ar_light_estimate);
ar_light_estimate = nullptr;
SkMatrix44 skProj;
SkMatrix44 skView;
SkMatrix skViewport;
skProj = util::GlmMatToSkMat(projection_mat);
skView = util::GlmMatToSkMat(view_mat);
skViewport.setScale(width_ / 2, -height_ / 2);
skViewport.postTranslate(width_ / 2, height_ / 2);
target = GrBackendRenderTarget(width_, height_, 0, 0, framebuffer_info);
surface = SkSurface::MakeFromBackendRenderTarget(grContext.get(),
target,
kBottomLeft_GrSurfaceOrigin,
kRGBA_8888_SkColorType,
nullptr, nullptr);
// Render Andy objects.
std::vector<SkMatrix44> models;
//glm::mat4 model_mat(1.0f);
for (const auto &obj_iter : tracked_obj_set_) {
ArTrackingState tracking_state = AR_TRACKING_STATE_STOPPED;
ArAnchor_getTrackingState(ar_session_, obj_iter, &tracking_state);
if (tracking_state == AR_TRACKING_STATE_TRACKING) {
// Render object only if the tracking state is AR_TRACKING_STATE_TRACKING.
//util::GetTransformMatrixFromAnchor(ar_session_, obj_iter, &model_mat);
//DRAW ANDY
//andy_renderer_.Draw(glm::mat4(1), glm::mat4(1), model_mat, color_correction);
//PREPARE SKIA MATS
SkMatrix44 skModel;
switch (currentObjectRotation) {
case 0: {
auto iter = anchor_skmat4_axis_aligned_map_.find(obj_iter);
if (iter != anchor_skmat4_axis_aligned_map_.end()) {
skModel = iter->second;
models.push_back(skModel);
}
}
break;
case 1: {
auto iter = anchor_skmat4_camera_aligned_map_.find(obj_iter);
if (iter != anchor_skmat4_camera_aligned_map_.end()) {
skModel = iter->second;
models.push_back(skModel);
}
}
break;
case 2: {
auto iter = anchor_skmat4_snap_aligned_map_.find(obj_iter);
if (iter != anchor_skmat4_snap_aligned_map_.end()) {
skModel = iter->second;
models.push_back(skModel);
}
}
break;
default: {
auto iter = anchor_skmat4_axis_aligned_map_.find(obj_iter);
if (iter != anchor_skmat4_axis_aligned_map_.end()) {
skModel = iter->second;
models.push_back(skModel);
}
}
break;
}
}
}
// Update and render planes.
ArTrackableList *plane_list = nullptr;
ArTrackableList_create(ar_session_, &plane_list);
CHECK(plane_list != nullptr);
ArTrackableType plane_tracked_type = AR_TRACKABLE_PLANE;
ArSession_getAllTrackables(ar_session_, plane_tracked_type, plane_list);
int32_t plane_list_size = 0;
ArTrackableList_getSize(ar_session_, plane_list, &plane_list_size);
plane_count_ = plane_list_size;
for (int i = 0; i < plane_list_size; ++i) {
ArTrackable *ar_trackable = nullptr;
ArTrackableList_acquireItem(ar_session_, plane_list, i, &ar_trackable);
ArPlane *ar_plane = ArAsPlane(ar_trackable);
ArTrackingState out_tracking_state;
ArTrackable_getTrackingState(ar_session_, ar_trackable,
&out_tracking_state);
ArPlane *subsume_plane;
ArPlane_acquireSubsumedBy(ar_session_, ar_plane, &subsume_plane);
if (subsume_plane != nullptr) {
ArTrackable_release(ArAsTrackable(subsume_plane));
continue;
}
if (ArTrackingState::AR_TRACKING_STATE_TRACKING != out_tracking_state) {
continue;
}
ArTrackingState plane_tracking_state;
ArTrackable_getTrackingState(ar_session_, ArAsTrackable(ar_plane),
&plane_tracking_state);
if (plane_tracking_state == AR_TRACKING_STATE_TRACKING) {
const auto iter = plane_color_map_.find(ar_plane);
glm::vec3 color;
if (iter != plane_color_map_.end()) {
color = iter->second;
// If this is an already observed trackable release it so it doesn't
// leave aof placing objects on surfaces (n additional reference dangling.
ArTrackable_release(ar_trackable);
} else {
// The first plane is always white.
if (!first_plane_has_been_found_) {
first_plane_has_been_found_ = true;
color = kWhite;
} else {
color = GetRandomPlaneColor();
}
plane_color_map_.insert({ar_plane, color});
}
plane_renderer_.Draw(projection_mat, view_mat, ar_session_, ar_plane,
color);
}
}
ArTrackableList_destroy(plane_list);
plane_list = nullptr;
// Update and render point cloud.
ArPointCloud *ar_point_cloud = nullptr;
ArStatus point_cloud_status =
ArFrame_acquirePointCloud(ar_session_, ar_frame_, &ar_point_cloud);
if (point_cloud_status == AR_SUCCESS) {
point_cloud_renderer_.Draw(projection_mat * view_mat, ar_session_,
ar_point_cloud);
ArPointCloud_release(ar_point_cloud);
}
SkMatrix44 i = SkMatrix44::kIdentity_Constructor;
if (surface != nullptr) {
SkCanvas *canvas = surface->getCanvas();
SkAutoCanvasRestore acr(canvas, true);
SkMatrix44 vpv = skViewport * skProj * skView;
for(SkMatrix44 skModel: models) {
SkMatrix44 i = SkMatrix44::kIdentity_Constructor;
canvas->setMatrix(i);
SkMatrix44 mvpv = skViewport * skProj * skView * skModel;
//Draw XYZ axes
DrawAxes(canvas, mvpv);
//Drawing camera orientation
/* DrawVector(canvas, vpv, begins[0], ends[0], SK_ColorMAGENTA);
DrawVector(canvas, vpv, begins[0], ends[1], SK_ColorYELLOW);
DrawVector(canvas, vpv, begins[0], ends[2], SK_ColorCYAN);*/
canvas->concat(mvpv);
SkPaint paint;
//Draw Circle
paint.setColor(0x80700000);
canvas->drawCircle(0, 0, 0.1, paint);
//Draw Text
paint.setColor(SK_ColorBLUE);
if (currentValue != 0) {
paint.setTextSize(currentValue);
} else {
paint.setTextSize(0.1);
}
paint.setAntiAlias(true);
const char text[] = "SkAR";
size_t byteLength = strlen(static_cast<const char *>(text));
SkShaper shaper(nullptr);
SkTextBlobBuilder builder;
SkPoint p = SkPoint::Make(0, 0);
shaper.shape(&builder, paint, text, byteLength, true, p, 10);
canvas->drawTextBlob(builder.make(), 0, 0, paint);
//DrawBoundingBox(canvas);
}
canvas->flush();
}
}
bool HelloArApplication::OnTouchedFirst(float x, float y, int drawMode) {
LOGI("Entered OnTouchedFirst");
if (pendingAnchor != nullptr) {
delete pendingAnchor;
}
SkPoint p = SkPoint::Make(x,y);
pendingAnchor = new PendingAnchor(p);
bool editAnchor = false;
if (ar_frame_ != nullptr && ar_session_ != nullptr) {
ArHitResultList *hit_result_list = nullptr;
ArHitResultList_create(ar_session_, &hit_result_list);
CHECK(hit_result_list);
ArFrame_hitTest(ar_session_, ar_frame_, x, y, hit_result_list);
int32_t hit_result_list_size = 0;
ArHitResultList_getSize(ar_session_, hit_result_list, &hit_result_list_size);
ArHitResult *ar_hit_result = nullptr;
ArPose *out_pose = nullptr;
ArPlane* hitPlane = nullptr;
for (int32_t i = 0; i < hit_result_list_size; ++i) {
ArHitResult *ar_hit = nullptr;
ArPose *created_out_pose = nullptr;
ArHitResult_create(ar_session_, &ar_hit);
ArHitResultList_getItem(ar_session_, hit_result_list, i, ar_hit);
if (ar_hit == nullptr) {
LOGE("HelloArApplication::OnTouched ArHitResultList_getItem error");
return editAnchor;
}
ArTrackable *ar_trackable = nullptr;
ArHitResult_acquireTrackable(ar_session_, ar_hit, &ar_trackable);
ArTrackableType ar_trackable_type = AR_TRACKABLE_NOT_VALID;
ArTrackable_getType(ar_session_, ar_trackable, &ar_trackable_type);
// Creates an anchor if a plane or an oriented point was hit.
if (AR_TRACKABLE_PLANE == ar_trackable_type) {
ArPose *hit_pose = nullptr;
ArPose_create(ar_session_, nullptr, &hit_pose);
ArHitResult_getHitPose(ar_session_, ar_hit, hit_pose);
int32_t in_polygon = 0;
ArPlane *ar_plane = ArAsPlane(ar_trackable);
ArPlane_isPoseInPolygon(ar_session_, ar_plane, hit_pose, &in_polygon);
{
// Use hit pose and camera pose to check if hittest is from the
// back of the plane, if it is, no need to create the anchor.
ArPose *camera_pose = nullptr;
ArPose_create(ar_session_, nullptr, &camera_pose);
ArCamera *ar_camera;
ArFrame_acquireCamera(ar_session_, ar_frame_, &ar_camera);
ArCamera_getPose(ar_session_, ar_camera, camera_pose);
float normal_distance_to_plane = util::CalculateDistanceToPlane(
ar_session_, *hit_pose, *camera_pose);
if (!in_polygon || normal_distance_to_plane < 0) {
ArPose_destroy(camera_pose);
continue;
}
ArPose_destroy(camera_pose);
ArCamera_release(ar_camera);
}
//Raw pose of hit location
float out_hit_raw[] = {0, 0, 0, 0, 0, 0, 0};
ArPose_getPoseRaw(ar_session_, hit_pose, out_hit_raw);
ArPose_destroy(hit_pose);
//Position of anchor
glm::vec4 pendingAnchorPos(out_hit_raw[4], out_hit_raw[5], out_hit_raw[6], 1);
pendingAnchor->SetContainingPlane(ar_plane);
//Check if plane contains approx the same anchor
auto planeAnchors = plane_anchors_map_.find(ar_plane);
if (planeAnchors != plane_anchors_map_.end()) {
//other anchors existed on this plane
std::vector<ArAnchor*> anchors = planeAnchors->second;
int i = 0;
LOGI("Size of anchor list: %d", (int) anchors.size());
for(ArAnchor* const& anchor: anchors) {
//Get anchor's pose
i++;
LOGI("CHECKING: Anchor #%d", i);
ArPose *anchor_pose = nullptr;
ArPose_create(ar_session_, nullptr, &anchor_pose);
ArAnchor_getPose(ar_session_, anchor, anchor_pose);
float out_anchor_raw[] = {0, 0, 0, 0, 0, 0, 0};
ArPose_getPoseRaw(ar_session_, anchor_pose, out_anchor_raw);
ArPose_destroy(anchor_pose);
glm::vec4 oldAnchorPos(out_anchor_raw[4], out_anchor_raw[5], out_anchor_raw[6], 1);
oldAnchorPos = oldAnchorPos - pendingAnchorPos;
float distance = util::Magnitude(glm::vec3(oldAnchorPos));
if (distance < 0.1f) {
LOGI("TouchFirst: Editing old anchor!");
editAnchor = true;
pendingAnchor->SetArAnchor(anchor);
pendingAnchor->SetEditMode(true);
ArHitResult_destroy(ar_hit);
ArHitResultList_destroy(hit_result_list);
LOGI("TouchFirst: Edit %d", editAnchor);
return editAnchor;
}
}
}
//actual hit result, and containing plane
ar_hit_result = ar_hit;
hitPlane = ar_plane;
//new anchor pos
float wanted_raw_pose[] = {0, 0, 0, 0, out_hit_raw[4], out_hit_raw[5], out_hit_raw[6]};
ArPose_create(ar_session_, wanted_raw_pose, &created_out_pose);
out_pose = created_out_pose;
break;
}
}
if (ar_hit_result) {
LOGI("TouchFirst: Adding new anchor!");
ArAnchor *anchor = nullptr;
pendingAnchor->SetEditMode(false);
if (ArSession_acquireNewAnchor(ar_session_, out_pose, &anchor) != AR_SUCCESS) {
LOGE("HelloArApplication::OnTouched ArHitResult_acquireNewAnchor error");
LOGI("TouchFirst: Failed to acquire new anchor");
delete hitPlane;
delete pendingAnchor;
pendingAnchor = nullptr;
LOGI("TouchFirst: Edit %d", editAnchor);
return editAnchor;
}
pendingAnchor->SetArAnchor(anchor);
ArHitResult_destroy(ar_hit_result);
ArHitResultList_destroy(hit_result_list);
ArPose_destroy(out_pose);
hit_result_list = nullptr;
LOGI("TouchFirst: Edit %d", editAnchor);
return editAnchor;
}
LOGI("TouchFirst: didn't hit anything");
delete hitPlane;
delete pendingAnchor;
pendingAnchor = nullptr;
LOGI("TouchFirst: Edit %d", editAnchor);
return editAnchor;
}
}
void HelloArApplication::AddAnchor(ArAnchor* anchor, ArPlane* containingPlane) {
//delete anchor from matrices maps
//releasing the anchor if it is not tracking anymore
ArTrackingState tracking_state = AR_TRACKING_STATE_STOPPED;
ArAnchor_getTrackingState(ar_session_, anchor, &tracking_state);
if (tracking_state != AR_TRACKING_STATE_TRACKING) {
RemoveAnchor(anchor);
return;
}
//releasing the first anchor if we exceeded maximum number of objects to be rendered
if (tracked_obj_set_.size() >= kMaxNumberOfAndroidsToRender) {
RemoveAnchor(tracked_obj_set_[0]);
}
//updating the containing plane with a new anchor
auto planeAnchors = plane_anchors_map_.find(containingPlane);
if (planeAnchors != plane_anchors_map_.end()) {
//other anchors existed on this plane
LOGI("TouchFinal: ADDING TO OLD ANCHORS");
std::vector<ArAnchor*> anchors = planeAnchors->second;
anchors.push_back(anchor);
plane_anchors_map_[containingPlane] = anchors;
anchor_plane_map_.insert({anchor, containingPlane});
} else {
LOGI("TouchFinal: NEW SET OF ANCHORS");
std::vector<ArAnchor*> anchors;
anchors.push_back(anchor);
plane_anchors_map_.insert({containingPlane, anchors});
anchor_plane_map_.insert({anchor, containingPlane});
}
tracked_obj_set_.push_back(anchor);
}
void HelloArApplication::OnTouchTranslate(float x, float y) {
LOGI("Entered On Edit Touched");
ArAnchor *anchor = pendingAnchor->GetArAnchor();
glm::mat4 matrix = util::SkMatToGlmMat(
anchor_skmat4_axis_aligned_map_.find(anchor)->second);
if (ar_frame_ != nullptr && ar_session_ != nullptr) {
ArHitResultList *hit_result_list = nullptr;
ArHitResultList_create(ar_session_, &hit_result_list);
CHECK(hit_result_list);
ArFrame_hitTest(ar_session_, ar_frame_, x, y, hit_result_list);
int32_t hit_result_list_size = 0;
ArHitResultList_getSize(ar_session_, hit_result_list, &hit_result_list_size);
ArHitResult *ar_hit_result = nullptr;
ArPose *out_pose = nullptr;
ArPlane *hitPlane = nullptr;
for (int32_t i = 0; i < hit_result_list_size; ++i) {
ArHitResult *ar_hit = nullptr;
ArPose *created_out_pose = nullptr;
ArHitResult_create(ar_session_, &ar_hit);
ArHitResultList_getItem(ar_session_, hit_result_list, i, ar_hit);
if (ar_hit == nullptr) {
LOGE("HelloArApplication::OnTouched ArHitResultList_getItem error");
return;
}
ArTrackable *ar_trackable = nullptr;
ArHitResult_acquireTrackable(ar_session_, ar_hit, &ar_trackable);
ArTrackableType ar_trackable_type = AR_TRACKABLE_NOT_VALID;
ArTrackable_getType(ar_session_, ar_trackable, &ar_trackable_type);
// Creates an anchor if a plane or an oriented point was hit.
if (AR_TRACKABLE_PLANE == ar_trackable_type) {
ArPose *hit_pose = nullptr;
ArPose_create(ar_session_, nullptr, &hit_pose);
ArHitResult_getHitPose(ar_session_, ar_hit, hit_pose);
int32_t in_polygon = 0;
ArPlane *ar_plane = ArAsPlane(ar_trackable);
ArPlane_isPoseInPolygon(ar_session_, ar_plane, hit_pose, &in_polygon);
{
// Use hit pose and camera pose to check if hittest is from the
// back of the plane, if it is, no need to create the anchor.
ArPose *camera_pose = nullptr;
ArPose_create(ar_session_, nullptr, &camera_pose);
ArCamera *ar_camera;
ArFrame_acquireCamera(ar_session_, ar_frame_, &ar_camera);
ArCamera_getPose(ar_session_, ar_camera, camera_pose);
float normal_distance_to_plane = util::CalculateDistanceToPlane(
ar_session_, *hit_pose, *camera_pose);
if (!in_polygon || normal_distance_to_plane < 0) {
ArPose_destroy(camera_pose);
continue;
}
ArPose_destroy(camera_pose);
ArCamera_release(ar_camera);
}
//Raw pose of hit location
float out_hit_raw[] = {0, 0, 0, 0, 0, 0, 0};
ArPose_getPoseRaw(ar_session_, hit_pose, out_hit_raw);
ArPose_destroy(hit_pose);
//Translate by new amount
glm::vec4 newPos(out_hit_raw[4], out_hit_raw[5], out_hit_raw[6], 1);
glm::vec4 oldPos = pendingAnchor->GetAnchorPos(ar_session_);
glm::vec3 movement = glm::vec3(newPos - oldPos);
//CAMERA SETTINGS
glm::mat4 backToOrigin(1);
backToOrigin = glm::translate(backToOrigin, -glm::vec3(oldPos));
glm::mat4 backToPlane(1);
backToPlane = glm::translate(backToPlane, glm::vec3(oldPos));
//Axes of Skia object: start with XYZ, totate to get X(-Z)Y, paste on plane, go back to origin --> plane orientation but on origin
glm::vec3 objX = glm::normalize(glm::vec3(
backToOrigin * matrix *
glm::vec4(1, 0, 0, 1))); //X still X
glm::vec3 objY = glm::normalize(glm::vec3(
backToOrigin * matrix *
glm::vec4(0, 1, 0, 1))); //Y is now Z
glm::vec3 objZ = glm::normalize(glm::vec3(
backToOrigin * matrix *
glm::vec4(0, 0, 1, 1))); //Z is now Y
glm::mat4 translate(1);
translate = glm::translate(translate, movement);
matrix = translate * matrix;
RemoveAnchor(anchor);
//new anchor pos
float wanted_raw_pose[] = {0, 0, 0, 0, out_hit_raw[4], out_hit_raw[5],
out_hit_raw[6]};
ArPose_create(ar_session_, wanted_raw_pose, &created_out_pose);
out_pose = created_out_pose;
ar_hit_result = ar_hit;
break;
}
}
if (ar_hit_result) {
LOGI("TouchFirst: Adding new anchor!");
ArAnchor *anchor = nullptr;
pendingAnchor->SetEditMode(false);
if (ArSession_acquireNewAnchor(ar_session_, out_pose, &anchor) != AR_SUCCESS) {
LOGE("HelloArApplication::OnTouched ArHitResult_acquireNewAnchor error");
LOGI("TouchFirst: Failed to acquire new anchor");
delete hitPlane;
delete pendingAnchor;
pendingAnchor = nullptr;
return;
}
pendingAnchor->SetArAnchor(anchor);
anchor_skmat4_axis_aligned_map_[anchor] = util::GlmMatToSkMat(matrix);
//Add anchor
AddAnchor(anchor, pendingAnchor->GetContainingPlane());
ArHitResult_destroy(ar_hit_result);
ArHitResultList_destroy(hit_result_list);
ArPose_destroy(out_pose);
hit_result_list = nullptr;
return;
}
}
}
void HelloArApplication::RemoveAnchor(ArAnchor* anchor) {
//delete anchor from matrices maps
anchor_skmat4_axis_aligned_map_.erase(anchor);
anchor_skmat4_camera_aligned_map_.erase(anchor);
anchor_skmat4_snap_aligned_map_.erase(anchor);
auto containingPlaneIter = anchor_plane_map_.find(anchor);
if (containingPlaneIter != anchor_plane_map_.end()) {
ArPlane* containingPlane = containingPlaneIter->second;
auto planeAnchors = plane_anchors_map_.find(containingPlane);
if (planeAnchors != plane_anchors_map_.end()) {
//delete this anchor from the list of anchors associated with its plane
std::vector<ArAnchor*> anchors = planeAnchors->second;
anchors.erase(std::remove(anchors.begin(), anchors.end(), anchor), anchors.end());
plane_anchors_map_[planeAnchors->first] = anchors;
//delete anchor from map of anchor to plane
anchor_plane_map_.erase(anchor);
}
}
//delete anchor from list of tracked objects
tracked_obj_set_.erase(std::remove(tracked_obj_set_.begin(), tracked_obj_set_.end(), anchor), tracked_obj_set_.end());
ArAnchor_release(anchor);
}
void HelloArApplication::UpdateMatrixMaps(ArAnchor* anchorKey, glm::mat4 aaMat, glm::mat4 caMat, glm::mat4 snapMat) {
anchor_skmat4_axis_aligned_map_.insert({anchorKey, util::GlmMatToSkMat(aaMat)});
anchor_skmat4_camera_aligned_map_.insert({anchorKey, util::GlmMatToSkMat(caMat)});
anchor_skmat4_snap_aligned_map_.insert({anchorKey, util::GlmMatToSkMat(snapMat)});
}
void SetSkiaInitialRotation(glm::mat4& initRotation) {
initRotation = glm::rotate(initRotation, SK_ScalarPI / 2, glm::vec3(1, 0, 0));
}
void SetSkiaObjectAxes(glm::vec3& x, glm::vec3& y, glm::vec3& z, glm::mat4 transform) {
x = glm::normalize(glm::vec3(transform * glm::vec4(1, 0, 0, 1))); //X still X
y = glm::normalize(glm::vec3(transform * glm::vec4(0, 1, 0, 1))); //Y is now Z
z = glm::normalize(glm::vec3(transform * glm::vec4(0, 0, 1, 1))); //Z is now Y
}
void SetCameraAlignedRotation(glm::mat4& rotateTowardsCamera, float& rotationDirection, const glm::vec3& toProject, const glm::vec3& skiaY, const glm::vec3& skiaZ) {
glm::vec3 hitLookProj = -util::ProjectOntoPlane(toProject, skiaZ);
float angleRad = util::AngleRad(skiaY, hitLookProj);
glm::vec3 cross = glm::normalize(glm::cross(skiaY, hitLookProj));
//outs
rotationDirection = util::Dot(cross, skiaZ);
rotateTowardsCamera = glm::rotate(rotateTowardsCamera, angleRad, rotationDirection * skiaZ);
}
struct CameraAlignmentInfo {
glm::vec3& skiaY, skiaZ;
glm::mat4& preRot, postRot;
CameraAlignmentInfo(glm::vec3& skiaY, glm::vec3& skiaZ, glm::mat4 preRot, glm::mat4 postRot)
: skiaY(skiaY), skiaZ(skiaZ), preRot(preRot), postRot(postRot) {}
};
void SetCameraAlignedVertical(glm::mat4& caMat, const glm::mat4& camRot, const CameraAlignmentInfo& camAlignInfo) {
//Camera axes
glm::vec3 xCamera = glm::vec3(glm::vec4(1, 0, 0, 1) * camRot);
glm::vec3 yCamera = glm::vec3(glm::vec4(0, 1, 0, 1) * camRot);
glm::vec3 zCamera = glm::vec3(glm::vec4(0, 0, -1, 1) * camRot);
//Get matrix that rotates object from plane towards the wanted angle
glm::mat4 rotateTowardsCamera(1);
float rotationDirection = 1;
SetCameraAlignedRotation(rotateTowardsCamera, rotationDirection, yCamera, camAlignInfo.skiaY, camAlignInfo.skiaZ);
//LogOrientation(dot, angleRad, "Vertical/Wall");
glm::mat4 flip(1);
flip = glm::rotate(flip, SK_ScalarPI, rotationDirection * camAlignInfo.skiaZ);
caMat = camAlignInfo.postRot * flip * rotateTowardsCamera * camAlignInfo.preRot;
}
void SetCameraAlignedHorizontal(glm::mat4& caMat, ArPlaneType planeType, const glm::vec3 hitLook, const CameraAlignmentInfo& camAlignInfo) {
//Ceiling or Floor: follow hit location
//Get matrix that rotates object from plane towards the wanted angle
glm::mat4 rotateTowardsCamera(1);
float rotationDirection = 1;
SetCameraAlignedRotation(rotateTowardsCamera, rotationDirection, hitLook, camAlignInfo.skiaY, camAlignInfo.skiaZ);
if (planeType == ArPlaneType::AR_PLANE_HORIZONTAL_DOWNWARD_FACING) {
//ceiling
//LogOrientation(dot, angleRad, "Ceiling");
glm::mat4 flip(1);
flip = glm::rotate(flip, SK_ScalarPI, rotationDirection * camAlignInfo.skiaZ);
caMat = camAlignInfo.postRot * flip * rotateTowardsCamera * camAlignInfo.preRot;
} else {
//floor or tabletop
//LogOrientation(dot, angleRad, "Floor");
caMat = camAlignInfo.postRot * rotateTowardsCamera * camAlignInfo.preRot;
}
}
void HelloArApplication::SetCameraAlignedMatrix(glm::mat4& caMat, glm::vec3 hitPos, glm::mat4& planeModel, const glm::mat4& initRotation) {
//Translation matrices: from plane to origin, and from origin to plane
glm::mat4 backToOrigin(1);
backToOrigin = glm::translate(backToOrigin, -hitPos);
glm::mat4 backToPlane(1);
backToPlane = glm::translate(backToPlane, hitPos);
//Axes of Skia object: start with XYZ, totate to get X(-Z)Y, paste on plane, go back to origin --> plane orientation but on origin
glm::vec3 skiaX, skiaY, skiaZ;
SetSkiaObjectAxes(skiaX, skiaY, skiaZ, backToOrigin * planeModel * initRotation);
//Get camera position & rotation
glm::vec3 cameraPos;
glm::mat4 cameraRotationMatrix;
util::GetCameraInfo(ar_session_, ar_frame_, cameraPos, cameraRotationMatrix);
//Set matrix depending on type of surface
ArPlaneType planeType = AR_PLANE_VERTICAL;
ArPlane_getType(ar_session_, pendingAnchor->GetContainingPlane(), &planeType);
//Set CamerAlignmentInfo
CameraAlignmentInfo camAlignInfo(skiaY, skiaZ, backToOrigin * planeModel * initRotation, backToPlane);
if (planeType == ArPlaneType::AR_PLANE_VERTICAL) {
//Wall: follow phone orientation
SetCameraAlignedVertical(caMat, cameraRotationMatrix, camAlignInfo);
} else {
//Ceiling or Floor: follow hit location
glm::vec3 hitLook(hitPos - cameraPos);
SetCameraAlignedHorizontal(caMat, planeType, hitLook, camAlignInfo);
}
}
void HelloArApplication::SetModelMatrices(glm::mat4& aaMat, glm::mat4& caMat, glm::mat4& snapMat, const glm::mat4& planeModel) {
//Brings Skia world to ARCore world
glm::mat4 initRotation(1);
SetSkiaInitialRotation(initRotation);
//Copy plane model for editing
glm::mat4 copyPlaneModel(planeModel);
//Set snap matrix
//snapMat = copyPlaneModel * initRotation;
//Set axis-aligned matrix
glm::vec4 anchorPos = pendingAnchor->GetAnchorPos(ar_session_);
copyPlaneModel[3] = anchorPos;
aaMat = planeModel * initRotation;
//Set camera-aligned matrix
//SetCameraAlignedMatrix(caMat, glm::vec3(anchorPos), copyPlaneModel, initRotation);
}
void GetPlaneModelMatrix(glm::mat4& planeModel, ArSession* arSession, ArPlane* arPlane) {
ArPose *plane_pose = nullptr;
ArPose_create(arSession, nullptr, &plane_pose);
ArPlane_getCenterPose(arSession, arPlane, plane_pose);
util::GetTransformMatrixFromPose(arSession, plane_pose, &planeModel);
ArPose_destroy(plane_pose);
}
void HelloArApplication::OnTouchedFinal(int type) {
LOGI("Entered OnTouchedFinal");
if (pendingAnchor == nullptr) {
LOGI("WARNING: Entered OnTouchedFinal but no pending anchor..");
return;
}
if (pendingAnchor->GetEditMode()) {
LOGI("WARNING: Editing old anchor in OnTouchedFinal!");
}
//Get necessary pending anchor info
ArPlane* containingPlane = pendingAnchor->GetContainingPlane();
glm::vec4 pendingAnchorPos = pendingAnchor->GetAnchorPos(ar_session_);
ArAnchor* actualAnchor = pendingAnchor->GetArAnchor();
//Plane model matrix
glm::mat4 planeModel(1);
GetPlaneModelMatrix(planeModel, ar_session_, containingPlane);
//Setup skia object model matrices
glm::mat4 matrixAxisAligned(1);
glm::mat4 matrixCameraAligned(1);
glm::mat4 matrixSnapAligned(1);
SetModelMatrices(matrixAxisAligned, matrixCameraAligned, matrixSnapAligned, planeModel);
//Update anchor -> model matrix datastructures
UpdateMatrixMaps(actualAnchor, matrixAxisAligned, matrixCameraAligned, matrixSnapAligned);
//Add anchor to aux datastructures
AddAnchor(actualAnchor, containingPlane);
}
} // namespace hello_ar

View File

@ -0,0 +1,158 @@
/*
* 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.
*/
#ifndef C_ARCORE_HELLOE_AR_HELLO_AR_APPLICATION_H_
#define C_ARCORE_HELLOE_AR_HELLO_AR_APPLICATION_H_
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <android/asset_manager.h>
#include <jni.h>
#include <memory>
#include <set>
#include <string>
#include <unordered_map>
#include <GrContext.h>
#include <gl/GrGLTypes.h>
#include <GrBackendSurface.h>
#include <SkSurface.h>
#include <Skottie.h>
#include "arcore_c_api.h"
#include "background_renderer.h"
#include "glm.h"
#include "plane_renderer.h"
#include "point_cloud_renderer.h"
#include "util.h"
#include "pending_anchor.h"
namespace hello_ar {
// HelloArApplication handles all application logics.
class HelloArApplication {
public:
// Constructor and deconstructor.
HelloArApplication() = default;
HelloArApplication(AAssetManager *asset_manager);
~HelloArApplication();
SkMatrix SkiaRenderer(const glm::mat4 &proj, const glm::mat4 &view, const glm::mat4 &model);
// OnPause is called on the UI thread from the Activity's onPause method.
void OnPause();
// OnResume is called on the UI thread from the Activity's onResume method.
void OnResume(void *env, void *context, void *activity);
// OnSurfaceCreated is called on the OpenGL thread when GLSurfaceView
// is created.
void OnSurfaceCreated();
// OnDisplayGeometryChanged is called on the OpenGL thread when the
// render surface size or display rotation changes.
//
// @param display_rotation: current display rotation.
// @param width: width of the changed surface view.
// @param height: height of the changed surface view.
void OnDisplayGeometryChanged(int display_rotation, int width, int height);
void OnObjectRotationChanged(int rotation);
void OnAction(float value);
// OnDrawFrame is called on the OpenGL thread to render the next frame.
void OnDrawFrame();
bool OnTouchedFirst(float x, float y, int drawMode);
void OnTouchTranslate(float x, float y);
void OnEditTouched(float x, float y);
void OnTouchedFinal(int type);
void RemoveAnchor(ArAnchor* anchor);
void AddAnchor(ArAnchor* anchor, ArPlane* containingPlane);
void UpdateMatrixMaps(ArAnchor* anchorKey, glm::mat4 aaMat, glm::mat4 caMat, glm::mat4 snapMat);
void SetModelMatrices(glm::mat4& aaMat, glm::mat4& caMat, glm::mat4& snapMat, const glm::mat4& planeModel);
void SetCameraAlignedMatrix(glm::mat4& caMat, glm::vec3 hitPos, glm::mat4& planeModel, const glm::mat4& initRotation);
// Returns true if any planes have been detected. Used for hiding the
// "searching for planes" snackbar.
bool HasDetectedPlanes() const { return plane_count_ > 0; }
glm::mat4
ComputeCameraAlignedMatrix(ArPlane *arPlane, glm::mat4 planeModel, glm::mat4 initRotation,
glm::vec4 anchorPos,
glm::vec3 cameraPos, glm::vec3 hitPos,
float cameraDisplayOutRaw[]);
private:
ArSession *ar_session_ = nullptr;
ArFrame *ar_frame_ = nullptr;
PendingAnchor* pendingAnchor = nullptr;
//SKIA VARS
sk_sp<GrContext> grContext;
sk_sp<skottie::Animation> fAnim;
SkScalar fAnimT = 0;
bool install_requested_ = false;
int width_ = 1;
int height_ = 1;
int display_rotation_ = 0;
int currentObjectRotation = 0;
float currentValue = 0;
std::vector<glm::vec3> begins;
std::vector<glm::vec3> ends;
AAssetManager *const asset_manager_;
// The anchors at which we are drawing android models
std::vector<ArAnchor *> tracked_obj_set_;
// Stores the randomly-selected color each plane is drawn with
std::unordered_map<ArPlane *, glm::vec3> plane_color_map_;
std::unordered_map<ArAnchor *, SkMatrix44> anchor_skmat4_axis_aligned_map_;
std::unordered_map<ArAnchor *, SkMatrix44> anchor_skmat4_camera_aligned_map_;
std::unordered_map<ArAnchor *, SkMatrix44> anchor_skmat4_snap_aligned_map_;
std::unordered_map<ArPlane *, std::vector<ArAnchor*>> plane_anchors_map_;
std::unordered_map<ArAnchor *, ArPlane*> anchor_plane_map_;
// The first plane is always rendered in white, if this is true then a plane
// at some point has been found.
bool first_plane_has_been_found_ = false;
PointCloudRenderer point_cloud_renderer_;
BackgroundRenderer background_renderer_;
PlaneRenderer plane_renderer_;
int32_t plane_count_ = 0;
};
} // namespace hello_ar
#endif // C_ARCORE_HELLOE_AR_HELLO_AR_APPLICATION_H_

View File

@ -0,0 +1,130 @@
/*
* 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.
*/
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
#include <jni.h>
#include "hello_ar_application.h"
#define JNI_METHOD(return_type, method_name) \
JNIEXPORT return_type JNICALL \
Java_org_skia_arcore_JniInterface_##method_name
extern "C" {
namespace {
// maintain a reference to the JVM so we can use it later.
static JavaVM *g_vm = nullptr;
inline jlong jptr(hello_ar::HelloArApplication *native_hello_ar_application) {
return reinterpret_cast<intptr_t>(native_hello_ar_application);
}
inline hello_ar::HelloArApplication *native(jlong ptr) {
return reinterpret_cast<hello_ar::HelloArApplication *>(ptr);
}
} // namespace
jint JNI_OnLoad(JavaVM *vm, void *) {
g_vm = vm;
return JNI_VERSION_1_6;
}
JNI_METHOD(jlong, createNativeApplication)
(JNIEnv *env, jclass, jobject j_asset_manager) {
AAssetManager *asset_manager = AAssetManager_fromJava(env, j_asset_manager);
return jptr(new hello_ar::HelloArApplication(asset_manager));
}
JNI_METHOD(void, destroyNativeApplication)
(JNIEnv *, jclass, jlong native_application) {
delete native(native_application);
}
JNI_METHOD(void, onPause)
(JNIEnv *, jclass, jlong native_application) {
native(native_application)->OnPause();
}
JNI_METHOD(void, onResume)
(JNIEnv *env, jclass, jlong native_application, jobject context,
jobject activity) {
native(native_application)->OnResume(env, context, activity);
}
JNI_METHOD(void, onGlSurfaceCreated)
(JNIEnv *, jclass, jlong native_application) {
native(native_application)->OnSurfaceCreated();
}
JNI_METHOD(void, onDisplayGeometryChanged)
(JNIEnv *, jobject, jlong native_application, int display_rotation, int width,
int height) {
native(native_application)
->OnDisplayGeometryChanged(display_rotation, width, height);
}
JNI_METHOD(void, onObjectRotationChanged)
(JNIEnv *, jobject, jlong native_application, int rotation) {
native(native_application)
->OnObjectRotationChanged(rotation);
}
JNI_METHOD(void, onAction)
(JNIEnv *, jobject, jlong native_application, jfloat value) {
native(native_application)->OnAction(value);
}
JNI_METHOD(void, onGlSurfaceDrawFrame)
(JNIEnv *, jclass, jlong native_application) {
native(native_application)->OnDrawFrame();
}
JNI_METHOD(void, onTouchTranslate)
(JNIEnv *, jclass, jlong native_application, jfloat x, jfloat y) {
return native(native_application)->OnTouchTranslate(x, y);
}
JNI_METHOD(bool, onTouchedFirst)
(JNIEnv *, jclass, jlong native_application, jfloat x, jfloat y, int drawMode) {
return native(native_application)->OnTouchedFirst(x, y, drawMode);
}
JNI_METHOD(void, onTouchedFinal)
(JNIEnv *, jclass, jlong native_application, int type) {
native(native_application)->OnTouchedFinal(type);
}
JNI_METHOD(jboolean, hasDetectedPlanes)
(JNIEnv *, jclass, jlong native_application) {
return static_cast<jboolean>(
native(native_application)->HasDetectedPlanes() ? JNI_TRUE : JNI_FALSE);
}
JNIEnv *GetJniEnv() {
JNIEnv *env;
jint result = g_vm->AttachCurrentThread(&env, nullptr);
return result == JNI_OK ? env : nullptr;
}
jclass FindClass(const char *classname) {
JNIEnv *env = GetJniEnv();
return env->FindClass(classname);
}
} // extern "C"

View File

@ -0,0 +1,34 @@
/*
* 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.
*/
#ifndef C_ARCORE_HELLOE_AR_JNI_INTERFACE_H_
#define C_ARCORE_HELLOE_AR_JNI_INTERFACE_H_
#include <jni.h>
/**
* Helper functions to provide access to Java from C via JNI.
*/
extern "C" {
// Helper function used to access the jni environment on the current thread.
// In this sample, no consideration is made for detaching the thread when the
// thread exits. This can cause memory leaks, so production applications should
// detach when the thread no longer needs access to the JVM.
JNIEnv *GetJniEnv();
jclass FindClass(const char *classname);
} // extern "C"
#endif

View File

@ -0,0 +1,68 @@
/*
* Copyright 2018 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.
*/
#include "hello_ar_application.h"
#include "plane_renderer.h"
#include "util.h"
#include "SkCanvas.h"
namespace hello_ar {
PendingAnchor::PendingAnchor(SkPoint touchLocation) : touchLocation(touchLocation) {}
PendingAnchor::~PendingAnchor() {}
SkPoint PendingAnchor::GetTouchLocation() {
return touchLocation;
}
bool PendingAnchor::GetEditMode() {
return editMode;
}
ArPlane* PendingAnchor::GetContainingPlane() {
return containingPlane;
}
glm::vec4 PendingAnchor::GetAnchorPos(ArSession* arSession) {
float poseRaw[] = {0, 0, 0, 0, 0, 0, 0};
ArPose* anchorPose = nullptr;
ArPose_create(arSession, poseRaw, &anchorPose);
ArAnchor_getPose(arSession, this->anchor, anchorPose);
ArPose_getPoseRaw(arSession, anchorPose, poseRaw);
ArPose_destroy(anchorPose);
glm::vec4 anchorPos = glm::vec4(poseRaw[4], poseRaw[5], poseRaw[6], 1);
return anchorPos;
}
ArAnchor* PendingAnchor::GetArAnchor() {
return anchor;
}
void PendingAnchor::SetArAnchor(ArAnchor* anchor) {
this->anchor = anchor;
}
void PendingAnchor::SetEditMode(bool editMode) {
this->editMode = editMode;
}
void PendingAnchor::SetContainingPlane(ArPlane* plane) {
this->containingPlane = plane;
}
} // namespace hello_ar

View File

@ -0,0 +1,50 @@
/*
* Copyright 2018 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.
*/
#ifndef C_ARCORE_HELLO_AR_PENDING_ANCHOR_H_
#define C_ARCORE_HELLO_AR_PENDING_ANCHOR_H_
#include <gl/GrGLTypes.h>
#include <GrBackendSurface.h>
#include "arcore_c_api.h"
#include "glm.h"
namespace hello_ar {
class PendingAnchor {
public:
PendingAnchor(SkPoint touchLocation);
~PendingAnchor();
SkPoint GetTouchLocation();
bool GetEditMode();
ArPlane* GetContainingPlane();
glm::vec4 GetAnchorPos(ArSession* arSession);
ArAnchor* GetArAnchor();
void SetArAnchor(ArAnchor* anchor);
void SetEditMode(bool editMode);
void SetContainingPlane(ArPlane* plane);
private:
SkPoint touchLocation;
bool editMode = false;
ArAnchor* anchor;
ArPlane* containingPlane;
};
} // namespace hello_ar
#endif

View File

@ -0,0 +1,220 @@
/*
* 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.
*/
#include "plane_renderer.h"
#include "util.h"
namespace hello_ar {
namespace {
constexpr char kVertexShader[] = R"(
precision highp float;
precision highp int;
attribute vec3 vertex;
varying vec2 v_textureCoords;
varying float v_alpha;
uniform mat4 mvp;
uniform mat4 model_mat;
uniform vec3 normal;
void main() {
// Vertex Z value is used as the alpha in this shader.
v_alpha = vertex.z;
vec4 local_pos = vec4(vertex.x, 0.0, vertex.y, 1.0);
gl_Position = mvp * local_pos;
vec4 world_pos = model_mat * 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(normal, arbitrary));
vec3 vec_v = normalize(cross(normal, vec_u));
// Project vertices in world frame onto vec_u and vec_v.
v_textureCoords = vec2(
dot(world_pos.xyz, vec_u), dot(world_pos.xyz, vec_v));
})";
constexpr char kFragmentShader[] = R"(
precision highp float;
precision highp int;
uniform sampler2D texture;
uniform vec3 color;
varying vec2 v_textureCoords;
varying float v_alpha;
void main() {
float r = texture2D(texture, v_textureCoords).r;
gl_FragColor = vec4(color.xyz, r * v_alpha);
})";
} // namespace
void PlaneRenderer::InitializeGlContent(AAssetManager *asset_manager) {
shader_program_ = util::CreateProgram(kVertexShader, kFragmentShader);
if (!shader_program_) {
LOGE("Could not create program.");
}
uniform_mvp_mat_ = glGetUniformLocation(shader_program_, "mvp");
uniform_texture_ = glGetUniformLocation(shader_program_, "texture");
uniform_model_mat_ = glGetUniformLocation(shader_program_, "model_mat");
uniform_normal_vec_ = glGetUniformLocation(shader_program_, "normal");
uniform_color_ = glGetUniformLocation(shader_program_, "color");
attri_vertices_ = glGetAttribLocation(shader_program_, "vertex");
glGenTextures(1, &texture_id_);
glBindTexture(GL_TEXTURE_2D, texture_id_);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
if (!util::LoadPngFromAssetManager(GL_TEXTURE_2D, "models/trigrid.png")) {
LOGE("Could not load png texture for planes.");
}
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
util::CheckGlError("plane_renderer::InitializeGlContent()");
}
void PlaneRenderer::Draw(const glm::mat4 &projection_mat,
const glm::mat4 &view_mat, const ArSession *ar_session,
const ArPlane *ar_plane, const glm::vec3 &color) {
if (!shader_program_) {
LOGE("shader_program is null.");
return;
}
UpdateForPlane(ar_session, ar_plane);
glUseProgram(shader_program_);
glDepthMask(GL_FALSE);
glActiveTexture(GL_TEXTURE0);
glUniform1i(uniform_texture_, 0);
glBindTexture(GL_TEXTURE_2D, texture_id_);
// Compose final mvp matrix for this plane renderer.
glUniformMatrix4fv(uniform_mvp_mat_, 1, GL_FALSE,
glm::value_ptr(projection_mat * view_mat * model_mat_));
glUniformMatrix4fv(uniform_model_mat_, 1, GL_FALSE,
glm::value_ptr(model_mat_));
glUniform3f(uniform_normal_vec_, normal_vec_.x, normal_vec_.y, normal_vec_.z);
glUniform3f(uniform_color_, color.x, color.y, color.z);
glEnableVertexAttribArray(attri_vertices_);
glVertexAttribPointer(attri_vertices_, 3, GL_FLOAT, GL_FALSE, 0,
vertices_.data());
glDrawElements(GL_TRIANGLES, triangles_.size(), GL_UNSIGNED_SHORT,
triangles_.data());
glUseProgram(0);
glDepthMask(GL_TRUE);
util::CheckGlError("plane_renderer::Draw()");
}
void PlaneRenderer::UpdateForPlane(const ArSession *ar_session,
const ArPlane *ar_plane) {
// The following code generates a triangle mesh filling a convex polygon,
// including a feathered edge for blending.
//
// The indices shown in the diagram are used in comments below.
// _______________ 0_______________1
// | | |4___________5|
// | | | | | |
// | | => | | | |
// | | | | | |
// | | |7-----------6|
// --------------- 3---------------2
vertices_.clear();
triangles_.clear();
int32_t polygon_length;
ArPlane_getPolygonSize(ar_session, ar_plane, &polygon_length);
if (polygon_length == 0) {
LOGE("PlaneRenderer::UpdatePlane, no valid plane polygon is found");
return;
}
const int32_t vertices_size = polygon_length / 2;
std::vector<glm::vec2> raw_vertices(vertices_size);
ArPlane_getPolygon(ar_session, ar_plane,
glm::value_ptr(raw_vertices.front()));
// Fill vertex 0 to 3. Note that the vertex.xy are used for x and z
// position. vertex.z is used for alpha. The outter polygon's alpha
// is 0.
for (int32_t i = 0; i < vertices_size; ++i) {
vertices_.push_back(glm::vec3(raw_vertices[i].x, raw_vertices[i].y, 0.0f));
}
util::ScopedArPose scopedArPose(ar_session);
ArPlane_getCenterPose(ar_session, ar_plane, scopedArPose.GetArPose());
ArPose_getMatrix(ar_session, scopedArPose.GetArPose(),
glm::value_ptr(model_mat_));
normal_vec_ = util::GetPlaneNormal(ar_session, *scopedArPose.GetArPose());
// Feather distance 0.2 meters.
const float kFeatherLength = 0.2f;
// Feather scale over the distance between plane center and vertices.
const float kFeatherScale = 0.2f;
// Fill vertex 4 to 7, with alpha set to 1.
for (int32_t i = 0; i < vertices_size; ++i) {
// Vector from plane center to current point.
glm::vec2 v = raw_vertices[i];
const float scale =
1.0f - std::min((kFeatherLength / glm::length(v)), kFeatherScale);
const glm::vec2 result_v = scale * v;
vertices_.push_back(glm::vec3(result_v.x, result_v.y, 1.0f));
}
const int32_t vertices_length = vertices_.size();
const int32_t half_vertices_length = vertices_length / 2;
// Generate triangle (4, 5, 6) and (4, 6, 7).
for (int i = half_vertices_length + 1; i < vertices_length - 1; ++i) {
triangles_.push_back(half_vertices_length);
triangles_.push_back(i);
triangles_.push_back(i + 1);
}
// Generate triangle (0, 1, 4), (4, 1, 5), (5, 1, 2), (5, 2, 6),
// (6, 2, 3), (6, 3, 7), (7, 3, 0), (7, 0, 4)
for (int i = 0; i < half_vertices_length; ++i) {
triangles_.push_back(i);
triangles_.push_back((i + 1) % half_vertices_length);
triangles_.push_back(i + half_vertices_length);
triangles_.push_back(i + half_vertices_length);
triangles_.push_back((i + 1) % half_vertices_length);
triangles_.push_back((i + half_vertices_length + 1) % half_vertices_length +
half_vertices_length);
}
}
} // namespace hello_ar

View File

@ -0,0 +1,70 @@
/*
* 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.
*/
#ifndef C_ARCORE_HELLOE_AR_PLANE_RENDERER_H_
#define C_ARCORE_HELLOE_AR_PLANE_RENDERER_H_
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <android/asset_manager.h>
#include <array>
#include <cstdint>
#include <cstdlib>
#include <string>
#include <vector>
#include "arcore_c_api.h"
#include "glm.h"
namespace hello_ar {
// PlaneRenderer renders ARCore plane type.
class PlaneRenderer {
public:
PlaneRenderer() = default;
~PlaneRenderer() = default;
// Sets up OpenGL state used by the plane renderer. Must be called on the
// OpenGL thread.
void InitializeGlContent(AAssetManager *asset_manager);
// Draws the provided plane.
void Draw(const glm::mat4 &projection_mat, const glm::mat4 &view_mat,
const ArSession *ar_session, const ArPlane *ar_plane,
const glm::vec3 &color);
private:
void UpdateForPlane(const ArSession *ar_session, const ArPlane *ar_plane);
std::vector<glm::vec3> vertices_;
std::vector<GLushort> triangles_;
glm::mat4 model_mat_ = glm::mat4(1.0f);
glm::vec3 normal_vec_ = glm::vec3(0.0f);
GLuint texture_id_;
GLuint shader_program_;
GLint attri_vertices_;
GLint uniform_mvp_mat_;
GLint uniform_texture_;
GLint uniform_model_mat_;
GLint uniform_normal_vec_;
GLint uniform_color_;
};
} // namespace hello_ar
#endif // C_ARCORE_HELLOE_AR_PLANE_RENDERER_H_

View File

@ -0,0 +1,77 @@
/*
* 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.
*/
#include "point_cloud_renderer.h"
#include "util.h"
namespace hello_ar {
namespace {
constexpr char kVertexShader[] = R"(
attribute vec4 vertex;
uniform mat4 mvp;
void main() {
gl_PointSize = 5.0;
// Pointcloud vertex's w component is confidence value.
// Not used in renderer.
gl_Position = mvp * vec4(vertex.xyz, 1.0);
})";
constexpr char kFragmentShader[] = R"(
precision lowp float;
void main() {
gl_FragColor = vec4(0.1215, 0.7372, 0.8235, 1.0);
})";
} // namespace
void PointCloudRenderer::InitializeGlContent() {
shader_program_ = util::CreateProgram(kVertexShader, kFragmentShader);
CHECK(shader_program_);
attribute_vertices_ = glGetAttribLocation(shader_program_, "vertex");
uniform_mvp_mat_ = glGetUniformLocation(shader_program_, "mvp");
util::CheckGlError("point_cloud_renderer::InitializeGlContent()");
}
void PointCloudRenderer::Draw(glm::mat4 mvp_matrix, ArSession *ar_session,
ArPointCloud *ar_point_cloud) const {
CHECK(shader_program_);
glUseProgram(shader_program_);
int32_t number_of_points = 0;
ArPointCloud_getNumberOfPoints(ar_session, ar_point_cloud, &number_of_points);
if (number_of_points <= 0) {
return;
}
const float *point_cloud_data;
ArPointCloud_getData(ar_session, ar_point_cloud, &point_cloud_data);
glUniformMatrix4fv(uniform_mvp_mat_, 1, GL_FALSE, glm::value_ptr(mvp_matrix));
glEnableVertexAttribArray(attribute_vertices_);
glVertexAttribPointer(attribute_vertices_, 4, GL_FLOAT, GL_FALSE, 0,
point_cloud_data);
glDrawArrays(GL_POINTS, 0, number_of_points);
glUseProgram(0);
util::CheckGlError("PointCloudRenderer::Draw");
}
} // namespace hello_ar

View File

@ -0,0 +1,57 @@
/*
* 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.
*/
#ifndef C_ARCORE_HELLOE_AR_POINT_CLOUD_RENDERER_H_
#define C_ARCORE_HELLOE_AR_POINT_CLOUD_RENDERER_H_
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <cstdlib>
#include <vector>
#include "arcore_c_api.h"
#include "glm.h"
namespace hello_ar {
class PointCloudRenderer {
public:
// Default constructor of PointCloudRenderer.
PointCloudRenderer() = default;
// Default deconstructor of PointCloudRenderer.
~PointCloudRenderer() = default;
// Initialize the GL content, needs to be called on GL thread.
void InitializeGlContent();
// Render the AR point cloud.
//
// @param mvp_matrix, the model view projection matrix of point cloud.
// @param ar_session, the session that is used to query point cloud points
// from ar_point_cloud.
// @param ar_point_cloud, point cloud data to for rendering.
void Draw(glm::mat4 mvp_matrix, ArSession *ar_session,
ArPointCloud *ar_point_cloud) const;
private:
GLuint shader_program_;
GLint attribute_vertices_;
GLint uniform_mvp_mat_;
};
} // namespace hello_ar
#endif // C_ARCORE_HELLOE_AR_POINT_CLOUD_RENDERER_H_

View File

@ -0,0 +1,329 @@
/*
* 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.
*/
#include "util.h"
#include <unistd.h>
#include <sstream>
#include <string>
#include <SkMatrix44.h>
#include <gtx/string_cast.inl>
#include "jni_interface.h"
namespace hello_ar {
namespace util {
void CheckGlError(const char *operation) {
bool anyError = false;
for (GLint error = glGetError(); error; error = glGetError()) {
LOGE("after %s() glError (0x%x)\n", operation, error);
anyError = true;
}
if (anyError) {
abort();
}
}
// Convenience function used in CreateProgram below.
static GLuint LoadShader(GLenum shader_type, const char *shader_source) {
GLuint shader = glCreateShader(shader_type);
if (!shader) {
return shader;
}
glShaderSource(shader, 1, &shader_source, nullptr);
glCompileShader(shader);
GLint compiled = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
if (!compiled) {
GLint info_len = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_len);
if (!info_len) {
return shader;
}
char *buf = reinterpret_cast<char *>(malloc(info_len));
if (!buf) {
return shader;
}
glGetShaderInfoLog(shader, info_len, nullptr, buf);
LOGE("hello_ar::util::Could not compile shader %d:\n%s\n", shader_type,
buf);
free(buf);
glDeleteShader(shader);
shader = 0;
}
return shader;
}
GLuint CreateProgram(const char *vertex_source, const char *fragment_source) {
GLuint vertexShader = LoadShader(GL_VERTEX_SHADER, vertex_source);
if (!vertexShader) {
return 0;
}
GLuint fragment_shader = LoadShader(GL_FRAGMENT_SHADER, fragment_source);
if (!fragment_shader) {
return 0;
}
GLuint program = glCreateProgram();
if (program) {
glAttachShader(program, vertexShader);
CheckGlError("hello_ar::util::glAttachShader");
glAttachShader(program, fragment_shader);
CheckGlError("hello_ar::util::glAttachShader");
glLinkProgram(program);
GLint link_status = GL_FALSE;
glGetProgramiv(program, GL_LINK_STATUS, &link_status);
if (link_status != GL_TRUE) {
GLint buf_length = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &buf_length);
if (buf_length) {
char *buf = reinterpret_cast<char *>(malloc(buf_length));
if (buf) {
glGetProgramInfoLog(program, buf_length, nullptr, buf);
LOGE("hello_ar::util::Could not link program:\n%s\n", buf);
free(buf);
}
}
glDeleteProgram(program);
program = 0;
}
}
return program;
}
bool LoadPngFromAssetManager(int target, const std::string &path) {
JNIEnv *env = GetJniEnv();
// Put all the JNI values in a structure that is statically initalized on the
// first call to this method. This makes it thread safe in the unlikely case
// of multiple threads calling this method.
static struct JNIData {
jclass helper_class;
jmethodID load_image_method;
jmethodID load_texture_method;
} jniIds = [env]() -> JNIData {
constexpr char kHelperClassName[] =
"org/skia/arcore/JniInterface";
constexpr char kLoadImageMethodName[] = "loadImage";
constexpr char kLoadImageMethodSignature[] =
"(Ljava/lang/String;)Landroid/graphics/Bitmap;";
constexpr char kLoadTextureMethodName[] = "loadTexture";
constexpr char kLoadTextureMethodSignature[] =
"(ILandroid/graphics/Bitmap;)V";
jclass helper_class = FindClass(kHelperClassName);
if (helper_class) {
helper_class = static_cast<jclass>(env->NewGlobalRef(helper_class));
jmethodID load_image_method = env->GetStaticMethodID(
helper_class, kLoadImageMethodName, kLoadImageMethodSignature);
jmethodID load_texture_method = env->GetStaticMethodID(
helper_class, kLoadTextureMethodName, kLoadTextureMethodSignature);
return {helper_class, load_image_method, load_texture_method};
}
LOGE("hello_ar::util::Could not find Java helper class %s",
kHelperClassName);
return {};
}();
if (!jniIds.helper_class) {
return false;
}
jstring j_path = env->NewStringUTF(path.c_str());
jobject image_obj = env->CallStaticObjectMethod(
jniIds.helper_class, jniIds.load_image_method, j_path);
if (j_path) {
env->DeleteLocalRef(j_path);
}
env->CallStaticVoidMethod(jniIds.helper_class, jniIds.load_texture_method,
target, image_obj);
return true;
}
void GetTransformMatrixFromPose(ArSession *ar_session,
const ArPose *ar_pose,
glm::mat4 *out_model_mat) {
if (out_model_mat == nullptr) {
LOGE("util::GetTransformMatrixFromPose model_mat is null.");
return;
}
ArPose_getMatrix(ar_session, ar_pose,
glm::value_ptr(*out_model_mat));
}
glm::vec3 GetPlaneNormal(const ArSession *ar_session,
const ArPose &plane_pose) {
float plane_pose_raw[7] = {0.f};
ArPose_getPoseRaw(ar_session, &plane_pose, plane_pose_raw);
glm::quat plane_quaternion(plane_pose_raw[3], plane_pose_raw[0],
plane_pose_raw[1], plane_pose_raw[2]);
// Get normal vector, normal is defined to be positive Y-position in local
// frame.
return glm::rotate(plane_quaternion, glm::vec3(0., 1.f, 0.));
}
float CalculateDistanceToPlane(const ArSession *ar_session,
const ArPose &plane_pose,
const ArPose &camera_pose) {
float plane_pose_raw[7] = {0.f};
ArPose_getPoseRaw(ar_session, &plane_pose, plane_pose_raw);
glm::vec3 plane_position(plane_pose_raw[4], plane_pose_raw[5],
plane_pose_raw[6]);
glm::vec3 normal = GetPlaneNormal(ar_session, plane_pose);
float camera_pose_raw[7] = {0.f};
ArPose_getPoseRaw(ar_session, &camera_pose, camera_pose_raw);
glm::vec3 camera_P_plane(camera_pose_raw[4] - plane_position.x,
camera_pose_raw[5] - plane_position.y,
camera_pose_raw[6] - plane_position.z);
return glm::dot(normal, camera_P_plane);
}
glm::mat4 GetCameraRotationMatrix(float cameraOutRaw[]) {
glm::mat4 cameraRotation(1);
glm::quat cameraQuat = glm::quat(cameraOutRaw[0], cameraOutRaw[1], cameraOutRaw[2],
cameraOutRaw[3]);
cameraRotation = glm::toMat4(cameraQuat);
glm::vec4 temp = cameraRotation[0];
cameraRotation[0] = cameraRotation[2];
cameraRotation[2] = temp;
return cameraRotation;
}
void GetCameraInfo(ArSession* arSession, ArFrame* arFrame, glm::vec3& cameraPos, glm::mat4& cameraRotation) {
//Acquire camera
ArCamera *ar_camera;
ArFrame_acquireCamera(arSession, arFrame, &ar_camera);
//Get camera pose
ArPose *camera_pose = nullptr;
ArPose_create(arSession, nullptr, &camera_pose);
ArCamera_getDisplayOrientedPose(arSession, ar_camera, camera_pose);
//Get camera raw info
float outCameraRaw[] = {0, 0, 0, 0, 0, 0, 0};
ArPose_getPoseRaw(arSession, camera_pose, outCameraRaw);
ArPose_destroy(camera_pose);
//Write to out variables
cameraPos = glm::vec3(outCameraRaw[4], outCameraRaw[5], outCameraRaw[6]);
cameraRotation = util::GetCameraRotationMatrix(outCameraRaw);
//Release camera
ArCamera_release(ar_camera);
}
SkMatrix44 GlmMatToSkMat(const glm::mat4 m) {
SkMatrix44 skMat = SkMatrix44::kIdentity_Constructor;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
skMat.set(j, i, m[i][j]);
}
}
return skMat;
}
glm::mat4 SkMatToGlmMat(const SkMatrix44 m) {
glm::mat4 glmMat(1);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
glmMat[i][j] = m.get(j, i);
}
}
return glmMat;
}
void Log4x4Matrix(float raw_matrix[16]) {
LOGI(
"%f, %f, %f, %f\n"
"%f, %f, %f, %f\n"
"%f, %f, %f, %f\n"
"%f, %f, %f, %f\n",
raw_matrix[0], raw_matrix[1], raw_matrix[2], raw_matrix[3], raw_matrix[4],
raw_matrix[5], raw_matrix[6], raw_matrix[7], raw_matrix[8], raw_matrix[9],
raw_matrix[10], raw_matrix[11], raw_matrix[12], raw_matrix[13],
raw_matrix[14], raw_matrix[15]);
}
void LogGlmMat(glm::mat4 m, char *type) {
std::string str = glm::to_string(m);
LOGE("glm Matrix - %s: %s\n", type, str.c_str());
}
void LogSkMat44(SkMatrix44 m, char *type) {
LOGE("SkMatrix - %s: [%g, %g, %g, %g] || [%g, %g, %g, %g] || [%g, %g, %g, %g] || [%g, %g, %g, %g] \n",
type,
m.get(0, 0), m.get(1, 0), m.get(2, 0), m.get(3, 0),
m.get(0, 1), m.get(1, 1), m.get(2, 1), m.get(3, 1),
m.get(0, 2), m.get(1, 2), m.get(2, 2), m.get(3, 2),
m.get(0, 3), m.get(1, 3), m.get(2, 3), m.get(3, 3)
);
}
void LogSkMat(SkMatrix m, char *type) {
LOGE("SkMatrix - %s: [%g, %g, %g] || [%g, %g, %g] || [%g, %g, %g] \n", type,
m.get(0), m.get(3), m.get(6),
m.get(1), m.get(4), m.get(7),
m.get(2), m.get(5), m.get(8)
);
}
void LogOrientation(float rotationDirection, float angleRad, char *type) {
LOGI("Plane orientation: %s", type);
LOGI("Cross dotted with zDir:", rotationDirection);
if (rotationDirection == -1) {
LOGI("Counter Clockwise %.6f degrees rotation: ", glm::degrees(angleRad));
} else {
LOGI("Clockwise %.6f degrees rotation: ", glm::degrees(angleRad));
}
}
float Dot(glm::vec3 u, glm::vec3 v) {
float result = u.x * v.x + u.y * v.y + u.z * v.z;
return result;
}
float Magnitude(glm::vec3 u) {
float result = u.x * u.x + u.y * u.y + u.z * u.z;
return sqrt(result);
}
float AngleRad(glm::vec3 u, glm::vec3 v) {
float dot = util::Dot(u, v);
float scale = (util::Magnitude(u) * util::Magnitude(v));
float cosine = dot / scale;
float acosine = acos(cosine);
return acosine;
}
glm::vec3 ProjectOntoPlane(glm::vec3 in, glm::vec3 normal) {
float dot = util::Dot(in, normal);
float multiplier = dot / (util::Magnitude(normal) * util::Magnitude(normal));
glm::vec3 out = in - multiplier * normal;
return out;
}
} // namespace util
} // namespace hello_ar

View File

@ -0,0 +1,139 @@
/*
* 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.
*/
#ifndef C_ARCORE_HELLOE_AR_UTIL_H_
#define C_ARCORE_HELLOE_AR_UTIL_H_
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <android/asset_manager.h>
#include <android/log.h>
#include <errno.h>
#include <jni.h>
#include <cstdint>
#include <cstdlib>
#include <vector>
#include <SkMatrix44.h>
#include "arcore_c_api.h"
#include "glm.h"
#ifndef LOGI
#define LOGI(...) \
__android_log_print(ANDROID_LOG_INFO, "hello_ar_example_c", __VA_ARGS__)
#endif // LOGI
#ifndef LOGE
#define LOGE(...) \
__android_log_print(ANDROID_LOG_ERROR, "hello_ar_example_c", __VA_ARGS__)
#endif // LOGE
#ifndef CHECK
#define CHECK(condition) \
if (!(condition)) { \
LOGE("*** CHECK FAILED at %s:%d: %s", __FILE__, __LINE__, #condition); \
abort(); \
}
#endif // CHECK
namespace hello_ar {
// Utilities
namespace util {
// Provides a scoped allocated instance of Anchor.
// Can be treated as an ArAnchor*.
class ScopedArPose {
public:
explicit ScopedArPose(const ArSession *session) {
ArPose_create(session, nullptr, &pose_);
}
~ScopedArPose() { ArPose_destroy(pose_); }
ArPose *GetArPose() { return pose_; }
// Delete copy constructors.
ScopedArPose(const ScopedArPose &) = delete;
void operator=(const ScopedArPose &) = delete;
private:
ArPose *pose_;
};
/* GL Utils */
// Check GL error, and abort if an error is encountered.
//
// @param operation, the name of the GL function call.
void CheckGlError(const char *operation);
// Create a shader program ID.
//
// @param vertex_source, the vertex shader source.
// @param fragment_source, the fragment shader source.
// @return
GLuint CreateProgram(const char *vertex_source, const char *fragment_source);
// Load png file from assets folder and then assign it to the OpenGL target.
// This method must be called from the renderer thread since it will result in
// OpenGL calls to assign the image to the texture target.
//
// @param target, openGL texture target to load the image into.
// @param path, path to the file, relative to the assets folder.
// @return true if png is loaded correctly, otherwise false.
bool LoadPngFromAssetManager(int target, const std::string &path);
/* ARCore utils */
void GetTransformMatrixFromPose(ArSession *ar_session, const ArPose *ar_pose, glm::mat4 *out_model_mat);
// Get the plane's normal from center pose.
glm::vec3 GetPlaneNormal(const ArSession *ar_session, const ArPose &plane_pose);
// Calculate the normal distance to plane from cameraPose, the given planePose
// should have y axis parallel to plane's normal, for example plane's center
// pose or hit test pose.
float CalculateDistanceToPlane(const ArSession *ar_session, const ArPose &plane_pose, const ArPose &camera_pose);
// Outputs the camera rotation using display orientation
glm::mat4 GetCameraRotationMatrix(float cameraOutRaw[]);
// Computes camera position and orientation (using GetCameraRotationMatrix)
void GetCameraInfo(ArSession* arSession, ArFrame* arFrame, glm::vec3& cameraPos, glm::mat4& cameraRotation);
/* Matrix conversion */
SkMatrix44 GlmMatToSkMat(const glm::mat4 m);
glm::mat4 SkMatToGlmMat(const SkMatrix44 m);
/* Logging utils */
//Row major output
void Log4x4Matrix(float raw_matrix[16]);
//Column major output
void LogGlmMat(glm::mat4 m, char *type);
void LogSkMat44(SkMatrix44 m, char *type);
void LogSkMat(SkMatrix m, char *type);
void LogOrientation(float rotationDirection, float angleRad, char *type);
/* Vector ops */
float Dot(glm::vec3 u, glm::vec3 v);
float Magnitude(glm::vec3 u);
float AngleRad(glm::vec3 u, glm::vec3 v);
glm::vec3 ProjectOntoPlane(glm::vec3 in, glm::vec3 normal);
} // namespace util
} // namespace hello_ar
#endif // C_ARCORE_HELLOE_AR_UTIL_H_

View File

@ -0,0 +1,65 @@
/*
* 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 org.skia.arcore;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.provider.Settings;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
/**
* Helper to ask camera permission.
*/
public class CameraPermissionHelper {
private static final String CAMERA_PERMISSION = Manifest.permission.CAMERA;
private static final int CAMERA_PERMISSION_CODE = 0;
/**
* Check to see we have the necessary permissions for this app.
*/
public static boolean hasCameraPermission(Activity activity) {
return ContextCompat.checkSelfPermission(activity, CAMERA_PERMISSION)
== PackageManager.PERMISSION_GRANTED;
}
/**
* Check to see we have the necessary permissions for this app, and ask for them if we don't.
*/
public static void requestCameraPermission(Activity activity) {
ActivityCompat.requestPermissions(
activity, new String[]{CAMERA_PERMISSION}, CAMERA_PERMISSION_CODE);
}
/**
* Check to see if we need to show the rationale for this permission.
*/
public static boolean shouldShowRequestPermissionRationale(Activity activity) {
return ActivityCompat.shouldShowRequestPermissionRationale(activity, CAMERA_PERMISSION);
}
/**
* Launch Application Setting to grant permission.
*/
public static void launchPermissionSettings(Activity activity) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.fromParts("package", activity.getPackageName(), null));
activity.startActivity(intent);
}
}

View File

@ -0,0 +1,364 @@
/*
* 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 org.skia.arcore;
import android.app.Activity;
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.os.Handler;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.ActionMode;
import android.view.ContextMenu;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.PopupMenu;
import android.widget.Toast;
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 C API.
*/
public class HelloArActivity extends AppCompatActivity
implements GLSurfaceView.Renderer, DisplayManager.DisplayListener {
private static final String TAG = HelloArActivity.class.getSimpleName();
private static final int SNACKBAR_UPDATE_INTERVAL_MILLIS = 1000; // In milliseconds.
private GLSurfaceView mSurfaceView;
private Activity activity = null;
private boolean mViewportChanged = false;
private int mViewportWidth;
private int mViewportHeight;
private View contextView = null;
private int mCurrentObjectRotation = 0;
private float mCurrentValue = 0;
private float X = 0;
private float Y = 0;
private boolean toEdit = false;
// Opaque native pointer to the native application instance.
private long mNativeApplication;
private GestureDetector mGestureDetector;
private Snackbar mLoadingMessageSnackbar;
private Handler mPlaneStatusCheckingHandler;
private final Runnable mPlaneStatusCheckingRunnable =
new Runnable() {
@Override
public void run() {
// The runnable is executed on main UI thread.
try {
if (JniInterface.hasDetectedPlanes(mNativeApplication)) {
if (mLoadingMessageSnackbar != null) {
mLoadingMessageSnackbar.dismiss();
}
mLoadingMessageSnackbar = null;
} else {
mPlaneStatusCheckingHandler.postDelayed(
mPlaneStatusCheckingRunnable, SNACKBAR_UPDATE_INTERVAL_MILLIS);
}
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
}
};
private int mDrawMode = -1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar myToolbar = (Toolbar) findViewById(R.id.my_toolbar);
setSupportActionBar(myToolbar);
activity = this;
//hide notifications bar
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
mSurfaceView = (GLSurfaceView) findViewById(R.id.surfaceview);
mGestureDetector =
new GestureDetector(
this,
new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(final MotionEvent e) {
toEdit = JniInterface.onTouchedFirst(mNativeApplication, e.getX(), e.getY(), mDrawMode);
Log.i(TAG, "toEdit: " + toEdit);
X = e.getX();
Y = e.getY();
contextView.showContextMenu(e.getX(), e.getY());
return true;
}
@Override
public boolean onScroll (MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
Log.i(TAG, "Scrolling!");
JniInterface.onTouchTranslate(mNativeApplication, e2.getX(), e2.getY());
return true;
}
@Override
public boolean onDown(MotionEvent e) {
return true;
}
});
mSurfaceView.setOnTouchListener(
new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return mGestureDetector.onTouchEvent(event);
}
});
// Set up renderer.
mSurfaceView.setPreserveEGLContextOnPause(true);
mSurfaceView.setEGLContextClientVersion(2);
mSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0); // Alpha used for plane blending.
mSurfaceView.setRenderer(this);
mSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
JniInterface.assetManager = getAssets();
mNativeApplication = JniInterface.createNativeApplication(getAssets());
mPlaneStatusCheckingHandler = new Handler();
//Floating context menu
contextView = findViewById(R.id.menuView);
this.registerForContextMenu(contextView);
View.OnLongClickListener listener = new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
activity.closeContextMenu();
return false;
}
};
contextView.setOnLongClickListener(listener);
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo)
{
super.onCreateContextMenu(menu, v, menuInfo);
if (!toEdit) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.draw_menu, menu);
menu.setHeaderTitle("Draw Options");
}
v.setClickable(false);
v.setFocusable(false);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
switch (item.getItemId()) {
case R.id.draw_text:
JniInterface.onTouchedFinal(mNativeApplication, 0);
return true;
case R.id.draw_circle:
JniInterface.onTouchedFinal(mNativeApplication, 1);
return true;
case R.id.draw_rect:
JniInterface.onTouchedFinal(mNativeApplication, 2);
return true;
case R.id.edit_size:
return true;
case R.id.edit_text:
return true;
default:
return super.onContextItemSelected(item);
}
}
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.rotation_mode, menu);
return true;
}
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.rotation_axis_aligned:
mCurrentObjectRotation = 0;
break;
case R.id.rotation_camera_aligned:
mCurrentObjectRotation = 1;
break;
case R.id.rotation_snap_aligned:
mCurrentObjectRotation = 2;
break;
case R.id.action:
mCurrentValue = 180;
JniInterface.onAction(mNativeApplication, mCurrentValue);
return true;
default:
return true;
}
JniInterface.onObjectRotationChanged(mNativeApplication, mCurrentObjectRotation);
return true;
}
@Override
protected void onResume() {
super.onResume();
// 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;
}
JniInterface.onResume(mNativeApplication, getApplicationContext(), this);
mSurfaceView.onResume();
mLoadingMessageSnackbar =
Snackbar.make(
HelloArActivity.this.findViewById(android.R.id.content),
"Searching for surfaces...",
Snackbar.LENGTH_INDEFINITE);
// Set the snackbar background to light transparent black color.
mLoadingMessageSnackbar.getView().setBackgroundColor(0xbf323232);
mLoadingMessageSnackbar.show();
mPlaneStatusCheckingHandler.postDelayed(
mPlaneStatusCheckingRunnable, SNACKBAR_UPDATE_INTERVAL_MILLIS);
// Listen to display changed events to detect 180° rotation, which does not cause a config
// change or view resize.
getSystemService(DisplayManager.class).registerDisplayListener(this, null);
}
@Override
public void onPause() {
super.onPause();
mSurfaceView.onPause();
JniInterface.onPause(mNativeApplication);
mPlaneStatusCheckingHandler.removeCallbacks(mPlaneStatusCheckingRunnable);
getSystemService(DisplayManager.class).unregisterDisplayListener(this);
}
@Override
public void onDestroy() {
super.onDestroy();
// Synchronized to avoid racing onDrawFrame.
synchronized (this) {
JniInterface.destroyNativeApplication(mNativeApplication);
mNativeApplication = 0;
}
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
// Standard Android full-screen functionality.
getWindow()
.getDecorView()
.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
GLES20.glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
JniInterface.onGlSurfaceCreated(mNativeApplication);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
mViewportWidth = width;
mViewportHeight = height;
mViewportChanged = true;
}
@Override
public void onDrawFrame(GL10 gl) {
// Synchronized to avoid racing onDestroy.
synchronized (this) {
if (mNativeApplication == 0) {
return;
}
if (mViewportChanged) {
int displayRotation = getWindowManager().getDefaultDisplay().getRotation();
JniInterface.onDisplayGeometryChanged(
mNativeApplication, displayRotation, mViewportWidth, mViewportHeight);
mViewportChanged = false;
}
JniInterface.onGlSurfaceDrawFrame(mNativeApplication);
}
}
@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();
}
}
// DisplayListener methods
@Override
public void onDisplayAdded(int displayId) {
}
@Override
public void onDisplayRemoved(int displayId) {
}
@Override
public void onDisplayChanged(int displayId) {
mViewportChanged = true;
}
}

View File

@ -0,0 +1,82 @@
package org.skia.arcore;
import android.app.Activity;
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLUtils;
import android.util.Log;
import java.io.IOException;
/**
* JNI interface to native layer.
*/
public class JniInterface {
static {
System.loadLibrary("hello_ar_native");
}
private static final String TAG = "JniInterface";
static AssetManager assetManager;
public static native long createNativeApplication(AssetManager assetManager);
public static native void destroyNativeApplication(long nativeApplication);
public static native void onPause(long nativeApplication);
public static native void onResume(long nativeApplication, Context context, Activity activity);
/**
* Allocate OpenGL resources for rendering.
*/
public static native void onGlSurfaceCreated(long nativeApplication);
/**
* Called on the OpenGL thread before onGlSurfaceDrawFrame when the view port width, height, or
* display rotation may have changed.
*/
public static native void onDisplayGeometryChanged(
long nativeApplication, int displayRotation, int width, int height);
public static native void onObjectRotationChanged(long nativeApplication, int rotation);
public static native void onAction(long nativeApplication, float value);
/**
* Main render loop, called on the OpenGL thread.
*/
public static native void onGlSurfaceDrawFrame(long nativeApplication);
/**
* OnTouch event, called on the OpenGL thread.
*/
public static native void onTouchTranslate(long nativeApplication, float x, float y);
public static native boolean onTouchedFirst(long nativeApplication, float x, float y, int drawMode);
public static native void onTouchedFinal(long nativeApplication, int type);
/**
* Get plane count in current session. Used to disable the "searching for surfaces" snackbar.
*/
public static native boolean hasDetectedPlanes(long nativeApplication);
public static Bitmap loadImage(String imageName) {
try {
return BitmapFactory.decodeStream(assetManager.open(imageName));
} catch (IOException e) {
Log.e(TAG, "Cannot open image " + imageName);
return null;
}
}
public static void loadTexture(int target, Bitmap bitmap) {
GLUtils.texImage2D(target, 0, bitmap, 0);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:background="@android:color/darker_gray"
android:layout_height="match_parent">
<!-- The navigation drawer that's always open -->
<ListView android:id="@+id/leftDrawer"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:choiceMode="singleChoice"
android:divider="@android:color/transparent"
android:dividerHeight="0dp"
android:layout_marginRight="5dp"
android:background="@android:color/background_light"/>
<!-- The main content view -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- We use mainLayout for recreating SurfaceView -->
<LinearLayout
android:id="@+id/mainLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<SurfaceView
android:id="@+id/surfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
</LinearLayout>
</RelativeLayout>
</LinearLayout>

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- The main content view -->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:layout_gravity="center"
android:id="@+id/menuView"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/my_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:elevation="4dp"
android:theme="@style/ThemeOverlay.AppCompat.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
<EditText
android:id="@+id/text_box"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="false"
android:clickable="false"
android:visibility="gone"/>
<android.opengl.GLSurfaceView
android:id="@+id/surfaceview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="bottom" />
</LinearLayout>
</FrameLayout>
</android.support.v4.widget.DrawerLayout>

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:weightSum="1">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginLeft="10dp"
android:layout_marginBottom="0dp"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Name:"
android:id="@+id/nameText" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="0dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="Value"
android:id="@+id/valueText" />
<Spinner
android:id="@+id/optionSpinner"
android:paddingTop="0dp"
android:paddingBottom="0dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</Spinner>
</LinearLayout>

View File

@ -0,0 +1,15 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/draw_text"
android:title="Draw Text" />
<item
android:id="@+id/draw_shape"
android:title="Draw Shape" >
<menu>
<item android:id="@+id/draw_circle"
android:title="Circle" />
<item android:id="@+id/draw_rect"
android:title="Rectangle" />
</menu>
</item>
</menu>

View File

@ -0,0 +1,8 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/edit_size"
android:title="Size" />
<item
android:id="@+id/edit_text"
android:title="Text" />
</menu>

View File

@ -0,0 +1,11 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/rotation_axis_aligned"
android:title="Plane Axis-Aligned"/>
<item android:id="@+id/rotation_camera_aligned"
android:title="Camera-Aligned"/>
<item android:id="@+id/rotation_snap_aligned"
android:title="Snap-Aligned"/>
<item android:id="@+id/action"
android:title="Do Action"/>
</menu>

View File

@ -0,0 +1,12 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@+id/action_left"
android:icon="@android:drawable/ic_media_previous"
android:showAsAction="always"/>
<item android:id="@+id/action_right"
android:icon="@android:drawable/ic_media_next"
android:showAsAction="always"/>
</menu>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer name="value_tag_key">1</integer>
</resources>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2017 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">HelloAR C</string>
</resources>

View File

@ -0,0 +1,35 @@
<!--
Copyright 2017 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>

View File

@ -3,10 +3,11 @@
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.3'
classpath 'com.android.tools.build:gradle:3.0.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@ -15,6 +16,7 @@ buildscript {
allprojects {
repositories {
google()
jcenter()
}
}
@ -105,5 +107,6 @@ def constructBuildCommand(project, variant, appName) {
}
String out_dir = getVariantOutDir(project, variant).skiaOut
return "${depotToolsDir}/ninja -C $out_dir $appName"
}

View File

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip

View File

@ -1,2 +1,3 @@
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'

View File

@ -22,6 +22,7 @@ android {
versionName "1.0"
signingConfig signingConfigs.debug
}
flavorDimensions "base"
sourceSets.main.jni.srcDirs = []
sourceSets.main.jniLibs.srcDir "src/main/libs"
productFlavors { universal{}; arm {}; arm64 {}; x86 {}; x64 {}; arm64vulkan{}; }

View File

@ -22,6 +22,7 @@ android {
versionName "1.0"
signingConfig signingConfigs.debug
}
flavorDimensions "base"
sourceSets.main.jni.srcDirs = [] //disable automatic ndk-build call
sourceSets.main.jniLibs.srcDir "src/main/libs"
productFlavors { universal{}; arm {}; arm64 {}; x86 {}; x64 {}; arm64vulkan{}; }

File diff suppressed because it is too large Load Diff

View File

@ -1,19 +0,0 @@
/*
* Copyright 2018 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef Sk3D_DEFINED
#define Sk3D_DEFINED
#include "SkPoint3.h"
#include "SkMatrix44.h"
void Sk3LookAt(SkMatrix44* dst, const SkPoint3& eye, const SkPoint3& center, const SkPoint3& up);
bool Sk3Perspective(SkMatrix44* dst, float near, float far, float angle);
void Sk3MapPts(SkPoint dst[], const SkMatrix44& m4, const SkPoint3 src[], int count);
#endif