В примере Java ARCore Hello AR
мы можем разместить объект Android на плоскости, нажав на экран, как мы можем использовать эту информацию HitResult
, чтобы провести линию между этими объектами?
Спасибо за помощь!
В примере Java ARCore Hello AR
мы можем разместить объект Android на плоскости, нажав на экран, как мы можем использовать эту информацию HitResult
, чтобы провести линию между этими объектами?
Спасибо за помощь!
В разделе кода, где вы захватываете якорь, чтобы разместить объект, вы должны проверить, есть ли у вас уже предыдущий якорь. Если у вас есть предыдущая привязка, возьмите worldPosition (как объект Vector3) из предыдущих и текущих привязок, затем вычислите разницу между ними, создайте линию такой длины и прикрепите ее к сцене в средней точке между двумя точки. Наконец, установите previousAnchor на текущий.
Вот код, который я использовал для решения этой проблемы:
// Create the Anchor.
Anchor anchor = hitResult.createAnchor();
AnchorNode anchorNode = new AnchorNode(anchor);
// Code to insert object probably happens here
if (lastAnchorNode != null) {
Vector3 point1, point2;
point1 = lastAnchorNode.getWorldPosition();
point2 = anchorNode.getWorldPosition();
Node line = new Node();
/* First, find the vector extending between the two points and define a look rotation in terms of this
Vector. */
final Vector3 difference = Vector3.subtract(point1, point2);
final Vector3 directionFromTopToBottom = difference.normalized();
final Quaternion rotationFromAToB =
Quaternion.lookRotation(directionFromTopToBottom, Vector3.up());
final Renderable[] lineRenderable = new Renderable[1];
/* Then, create a rectangular prism, using ShapeFactory.makeCube() and use the difference vector
to extend to the necessary length. */
MaterialFactory.makeOpaqueWithColor(this, color)
.thenAccept(
material -> {
lineRenderable[0] = ShapeFactory.makeCube(new Vector3(.01f, .01f, difference.length()),
Vector3.zero(), material);
});
/* Last, set the world rotation of the node to the rotation calculated earlier and set the world position to
the midpoint between the given points . */
line.setParent(anchorNode);
line.setRenderable(lineRenderable[0]);
line.setWorldPosition(Vector3.add(point1, point2).scaled(.5f));
line.setWorldRotation(rotationFromAToB);
}
lastAnchorNode = anchorNode;
lastAnchorNode
. Как спасти это setOnTapArPlaneListener
. Помогите, пожалуйста
- person Shubham Agrawal; 15.10.2018
Anchor
будет вам полезен. благодаря этому вы можете отслеживать точки касания и использовать эти координаты между точками. Я сделал что-то подобное, чтобы подсчитать расстояние между двумя точками, которые я коснулся
Решение от @Arthulia у меня не работает = (
Но я поступил немного иначе. Оно работает:
в моем onCreate:
ArFragment arFragment = (ArFragment) getSupportFragmentManager().findFragmentById(R.id.vr_fragment);
arFragment.setOnTapArPlaneListener(
(HitResult hitResult, Plane plane, MotionEvent motionEvent) -> {
addLineBetweenHits(hitResult, plane, motionEvent);
});
и позже:
private void addLineBetweenHits(HitResult hitResult, Plane plane, MotionEvent motionEvent) {
Anchor anchor = hitResult.createAnchor();
AnchorNode anchorNode = new AnchorNode(anchor);
if (lastAnchorNode != null) {
anchorNode.setParent(arFragment.getArSceneView().getScene());
Vector3 point1, point2;
point1 = lastAnchorNode.getWorldPosition();
point2 = anchorNode.getWorldPosition();
/*
First, find the vector extending between the two points and define a look rotation
in terms of this Vector.
*/
final Vector3 difference = Vector3.subtract(point1, point2);
final Vector3 directionFromTopToBottom = difference.normalized();
final Quaternion rotationFromAToB =
Quaternion.lookRotation(directionFromTopToBottom, Vector3.up());
MaterialFactory.makeOpaqueWithColor(getApplicationContext(), color)
.thenAccept(
material -> {
/* Then, create a rectangular prism, using ShapeFactory.makeCube() and use the difference vector
to extend to the necessary length. */
ModelRenderable model = ShapeFactory.makeCube(
new Vector3(.01f, .01f, difference.length()),
Vector3.zero(), material);
/* Last, set the world rotation of the node to the rotation calculated earlier and set the world position to
the midpoint between the given points . */
Node node = new Node();
node.setParent(anchorNode);
node.setRenderable(model);
node.setWorldPosition(Vector3.add(point1, point2).scaled(.5f));
node.setWorldRotation(rotationFromAToB);
}
);
lastAnchorNode = anchorNode;
}
lastAnchorNode
. Как спасти это setOnTapArPlaneListener
. Помогите, пожалуйста
- person Shubham Agrawal; 15.10.2018
Я думаю, что вам следует сохранить всю информацию HitResults, особенно для позиции Pose.
Вы должны создать класс как PlaneRenderer, чтобы он мог рисовать линию между начальной и конечной позицией.
using System;
namespace GoogleARCore.Examples.HelloAR
{
using System.Collections.Generic;
using GoogleARCore;
using GoogleARCore.Examples.Common;
using UnityEngine;
#if UNITY_EDITOR
// Set up touch input propagation while using Instant Preview in the editor.
using Input = InstantPreviewInput;
#endif
/// <summary>
/// Controls the HelloAR example.
/// </summary>
public class HelloARController : MonoBehaviour
{
/// <summary>
/// The first-person camera being used to render the passthrough camera image (i.e. AR background).
/// </summary>
public Camera FirstPersonCamera;
/// <summary>
/// A prefab for tracking and visualizing detected planes.
/// </summary>
public GameObject DetectedPlanePrefab;
/// <summary>
/// A model to place when a raycast from a user touch hits a plane.
/// </summary>
public GameObject AndyPlanePrefab;
/// <summary>
/// A model to place when a raycast from a user touch hits a feature point.
/// </summary>
public GameObject AndyPointPrefab;
/// <summary>
/// A gameobject parenting UI for displaying the "searching for planes" snackbar.
/// </summary>
public GameObject SearchingForPlaneUI;
/// <summary>
/// The rotation in degrees need to apply to model when the Andy model is placed.
/// </summary>
private const float k_ModelRotation = 180.0f;
/// <summary>
/// A list to hold all planes ARCore is tracking in the current frame. This object is used across
/// the application to avoid per-frame allocations.
/// </summary>
private List<DetectedPlane> m_AllPlanes = new List<DetectedPlane> ();
/// <summary>
/// True if the app is in the process of quitting due to an ARCore connection error, otherwise false.
/// </summary>
private bool m_IsQuitting = false;
private int count = 0;
private Vector3[] pos = new Vector3[4];
private LineRenderer lineRenderer;
/// <summary>
/// The Unity Update() method.
/// </summary>
public void Update ()
{
_UpdateApplicationLifecycle ();
// Hide snackbar when currently tracking at least one plane.
Session.GetTrackables<DetectedPlane> (m_AllPlanes);
bool showSearchingUI = true;
for (int i = 0; i < m_AllPlanes.Count; i++) {
if (m_AllPlanes [i].TrackingState == TrackingState.Tracking) {
showSearchingUI = false;
break;
}
}
SearchingForPlaneUI.SetActive (showSearchingUI);
// If the player has not touched the screen, we are done with this update.
Touch touch;
if (Input.touchCount < 1 || (touch = Input.GetTouch (0)).phase != TouchPhase.Began) {
return;
}
// Raycast against the location the player touched to search for planes.
TrackableHit hit;
TrackableHitFlags raycastFilter = TrackableHitFlags.PlaneWithinPolygon |
TrackableHitFlags.FeaturePointWithSurfaceNormal;
if (Frame.Raycast (touch.position.x, touch.position.y, raycastFilter, out hit)) {
// 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.
if ((hit.Trackable is DetectedPlane) &&
Vector3.Dot (FirstPersonCamera.transform.position - hit.Pose.position,
hit.Pose.rotation * Vector3.up) < 0) {
Debug.Log ("Hit at back of the current DetectedPlane");
} else {
// Choose the Andy model for the Trackable that got hit.
GameObject prefab;
if (hit.Trackable is FeaturePoint) {
prefab = AndyPointPrefab;
} else {
prefab = AndyPlanePrefab;
}
if (count < 2) {
pos [count] = hit.Pose.position;
// Instantiate Andy model at the hit pose.
var andyObject = Instantiate (prefab, hit.Pose.position, hit.Pose.rotation);
// Compensate for the hitPose rotation facing away from the raycast (i.e. camera).
andyObject.transform.Rotate (0, k_ModelRotation, 0, Space.Self);
// Create an anchor to allow ARCore to track the hitpoint as understanding of the physical
// world evolves.
var anchor = hit.Trackable.CreateAnchor (hit.Pose);
// Make Andy model a child of the anchor.
andyObject.transform.parent = anchor.transform;
count++;
}
// Рисование линий из точек касания с помощью средства рендеринга линий
if (count == 2) { //Creating lineRenderer object lineRenderer = new GameObject("Line").AddComponent<LineRenderer>(); lineRenderer.startColor = Color.black; lineRenderer.endColor = Color.black; lineRenderer.startWidth = 0.01f; lineRenderer.endWidth = 0.01f; lineRenderer.positionCount = 2; lineRenderer.useWorldSpace = true; //Drawing line from first touched point to the second one lineRenderer.SetPosition(0,pos[0]); lineRenderer.SetPosition(1,pos[1]); }
}
}
}
/// <summary>
/// Check and update the application lifecycle.
/// </summary>
private void _UpdateApplicationLifecycle ()
{
// Exit the app when the 'back' button is pressed.
if (Input.GetKey (KeyCode.Escape)) {
Application.Quit ();
}
// Only allow the screen to sleep when not tracking.
if (Session.Status != SessionStatus.Tracking) {
const int lostTrackingSleepTimeout = 15;
Screen.sleepTimeout = lostTrackingSleepTimeout;
} else {
Screen.sleepTimeout = SleepTimeout.NeverSleep;
}
if (m_IsQuitting) {
return;
}
// Quit if ARCore was unable to connect and give Unity some time for the toast to appear.
if (Session.Status == SessionStatus.ErrorPermissionNotGranted) {
_ShowAndroidToastMessage ("Camera permission is needed to run this application.");
m_IsQuitting = true;
Invoke ("_DoQuit", 0.5f);
} else if (Session.Status.IsError ()) {
_ShowAndroidToastMessage ("ARCore encountered a problem connecting. Please start the app again.");
m_IsQuitting = true;
Invoke ("_DoQuit", 0.5f);
}
}
/// <summary>
/// Actually quit the application.
/// </summary>
private void _DoQuit ()
{
Application.Quit ();
}
/// <summary>
/// Show an Android toast message.
/// </summary>
/// <param name="message">Message string to show in the toast.</param>
private void _ShowAndroidToastMessage (string message)
{
AndroidJavaClass unityPlayer = new AndroidJavaClass ("com.unity3d.player.UnityPlayer");
AndroidJavaObject unityActivity = unityPlayer.GetStatic<AndroidJavaObject> ("currentActivity");
if (unityActivity != null) {
AndroidJavaClass toastClass = new AndroidJavaClass ("android.widget.Toast");
unityActivity.Call ("runOnUiThread", new AndroidJavaRunnable (() => {
AndroidJavaObject toastObject = toastClass.CallStatic<AndroidJavaObject> ("makeText", unityActivity,
message, 0);
toastObject.Call ("show");
}));
}
}
}
}