aboutsummaryrefslogtreecommitdiffhomepage
path: root/platform_tools/android/apps/arcore/src/main/cpp/hello_ar_application.cc
diff options
context:
space:
mode:
Diffstat (limited to 'platform_tools/android/apps/arcore/src/main/cpp/hello_ar_application.cc')
-rw-r--r--platform_tools/android/apps/arcore/src/main/cpp/hello_ar_application.cc987
1 files changed, 987 insertions, 0 deletions
diff --git a/platform_tools/android/apps/arcore/src/main/cpp/hello_ar_application.cc b/platform_tools/android/apps/arcore/src/main/cpp/hello_ar_application.cc
new file mode 100644
index 0000000000..6e050f373c
--- /dev/null
+++ b/platform_tools/android/apps/arcore/src/main/cpp/hello_ar_application.cc
@@ -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