How to move object from Anchor to Anchor?
Asked Answered
O

2

8

My use case is:

  1. tap on the screen and save the "point" as starting anchor
  2. tap on the screen second time and save the "point" as end anchor
  3. push the button that will move the object from starting to end anchor

I've built my own node that is using ObjectAnimator similar like in the solar system example. My only problem is that I do not know how to determine start and end point for the the evaluator. My first thought was to take the x,y,z from Pose of start and end anchor

Vector3 start = new Vector3(startAnchor.getPose().tx(), startAnchor.getPose().ty(), startAnchor.getPose().tz());
Vector3 end = new Vector3(endAnchor.getPose().tx(), endAnchor.getPose().ty(), endAnchor.getPose().tz());

movingAnimation.setObjectValues(startingPoint, endPoint);
movingAnimation.setPropertyName("localPosition");
movingAnimation.setEvaluator(new Vector3Evaluator());

but when I do that animation is done from completely different places.

I haven't found any reference to built-in tools for such operation. I'm using Sceneform.

So the question is: How to make a fluent animation (a simple slide is enough) from anchor A to anchor B?

Ossieossietzky answered 14/7, 2018 at 10:19 Comment(0)
S
15

I did this in the HelloSceneform sample. I created the first AnchorNode and added the "andy" node as a child. On the next tap, I created the endPosition AnchorNode and started the animation to move to that position.

The thing to remember is that if you are using the positions of objects with a different parent, you want to use worldPosition vs. localPosition.

  private void onPlaneTap(HitResult hitResult, Plane plane, MotionEvent motionEvent) {
      if (andyRenderable == null) {
        return;
      }
      // Create the Anchor.
      Anchor anchor = hitResult.createAnchor();

      // Create the starting position.
      if (startNode == null) {
        startNode = new AnchorNode(anchor);
        startNode.setParent(arFragment.getArSceneView().getScene());

        // Create the transformable andy and add it to the anchor.
        andy = new Node();
        andy.setParent(startNode);
        andy.setRenderable(andyRenderable);
      } else {
        // Create the end position and start the animation.
        endNode = new AnchorNode(anchor);
        endNode.setParent(arFragment.getArSceneView().getScene());
        startWalking();
      }
  }

  private void startWalking() {
    objectAnimation = new ObjectAnimator();
    objectAnimation.setAutoCancel(true);
    objectAnimation.setTarget(andy);

    // All the positions should be world positions
    // The first position is the start, and the second is the end.
    objectAnimation.setObjectValues(andy.getWorldPosition(), endNode.getWorldPosition());

    // Use setWorldPosition to position andy.
    objectAnimation.setPropertyName("worldPosition");

    // The Vector3Evaluator is used to evaluator 2 vector3 and return the next
    // vector3.  The default is to use lerp. 
    objectAnimation.setEvaluator(new Vector3Evaluator());
    // This makes the animation linear (smooth and uniform).
    objectAnimation.setInterpolator(new LinearInterpolator());
    // Duration in ms of the animation.
    objectAnimation.setDuration(500);
    objectAnimation.start();
  }
Subglacial answered 16/7, 2018 at 19:30 Comment(0)
T
0
/**
 * This is an example activity that uses the Sceneform UX package to make common AR tasks easier.
 */
public class MainActivity extends AppCompatActivity {
    private static final String TAG = MainActivity.class.getSimpleName();
    private static final double MIN_OPENGL_VERSION = 3.1;
    Session mSession;
    private ArFragment arFragment;
    private ArSceneView arSceneView;
    private ModelRenderable andyRenderable;
    private boolean shouldConfigureSession = false;
    private boolean modelAdded = false;
    private ObjectAnimator objectAnimation;
    private TransformableNode andy;
    private AnchorNode endNode;
    private GestureDetector trackableGestureDetector;

    /**
     * Returns false and displays an error message if Sceneform can not run, true if Sceneform can run
     * on this device.
     * <p>
     * <p>Sceneform requires Android N on the device as well as OpenGL 3.1 capabilities.
     * <p>
     * <p>Finishes the activity if Sceneform can not run
     */
    public static boolean checkIsSupportedDeviceOrFinish(final Activity activity) {
        if (Build.VERSION.SDK_INT < VERSION_CODES.N) {
            Log.e(TAG, "Sceneform requires Android N or later");
            Toast.makeText(activity, "Sceneform requires Android N or later", Toast.LENGTH_LONG).show();
            activity.finish();
            return false;
        }

        String openGlVersionString =
                ((ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE))
                        .getDeviceConfigurationInfo()
                        .getGlEsVersion();
        if (Double.parseDouble(openGlVersionString) < MIN_OPENGL_VERSION) {
            Log.e(TAG, "Sceneform requires OpenGL ES 3.1 later");
            Toast.makeText(activity, "Sceneform requires OpenGL ES 3.1 or later", Toast.LENGTH_LONG)
                    .show();
            activity.finish();
            return false;
        }
        return true;
    }

    @Override
    @SuppressWarnings({"AndroidApiChecker", "FutureReturnValueIgnored"})
    // CompletableFuture requires api level 24
    // FutureReturnValueIgnored is not valid
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (!checkIsSupportedDeviceOrFinish(this)) {
            return;
        }

        setContentView(R.layout.activity_main);
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 105);

        arFragment = (ArFragment) getSupportFragmentManager().findFragmentById(R.id.ux_fragment);
        if (arFragment != null) {
            arFragment.getPlaneDiscoveryController().hide();
            arFragment.getPlaneDiscoveryController().setInstructionView(null);

        }
        arSceneView = arFragment.getArSceneView();
        arSceneView.getScene().addOnUpdateListener((this::onUpdateFrame));

        arFragment.getArSceneView().getScene().addOnPeekTouchListener(this::handleOnTouch);
        this.trackableGestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {
            public boolean onSingleTapUp(MotionEvent e) {
                onSingleTap(e);
                return true;
            }

            public boolean onDown(MotionEvent e) {
                return true;
            }
        });

        // When you build a Renderable, Sceneform loads its resources in the background while returning
        // a CompletableFuture. Call thenAccept(), handle(), or check isDone() before calling get().

        File file = new File(Environment.getExternalStorageDirectory(), "model.sfb");
        Uri photoURI = Uri.fromFile(file);
        Callable callable = () -> (InputStream) new FileInputStream(file);
        FutureTask task = new FutureTask<>(callable);
        new Thread(task).start();
        ModelRenderable.builder()
                .setSource(this, R.raw.model) //.setSource(this, callable)
                .build()
                .thenAccept(renderable -> andyRenderable = renderable)
                .exceptionally(
                        throwable -> {
                            Toast toast =
                                    Toast.makeText(this, "Unable to load andy renderable", Toast.LENGTH_LONG);
                            toast.setGravity(Gravity.CENTER, 0, 0);
                            toast.show();
                            return null;
                        });
        arFragment.setOnTapArPlaneListener(
                (HitResult hitResult, Plane plane, MotionEvent motionEvent) -> {
                    if (andyRenderable == null) {
                        return;
                    }

                    if (modelAdded) {
                        endNode = new AnchorNode(hitResult.createAnchor());
                        endNode.setParent(arFragment.getArSceneView().getScene());
                        startWalking();
                    }
                });

    }

    private void handleOnTouch(HitTestResult hitTestResult, MotionEvent motionEvent) {
        // First call ArFragment's listener to handle TransformableNodes.
        arFragment.onPeekTouch(hitTestResult, motionEvent);

        // Check for touching a Sceneform node
        if (hitTestResult.getNode() != null) {
            return;
        }

        // Otherwise call gesture detector.
        trackableGestureDetector.onTouchEvent(motionEvent);
    }

    private void onSingleTap(MotionEvent motionEvent) {
        Frame frame = arFragment.getArSceneView().getArFrame();
        if (frame != null && motionEvent != null && frame.getCamera().getTrackingState() == TrackingState.TRACKING) {
            for (HitResult hit : frame.hitTest(motionEvent)) {
                Trackable trackable = hit.getTrackable();
                if (trackable instanceof Plane && ((Plane) trackable).isPoseInPolygon(hit.getHitPose())) {
                    Plane plane = (Plane) trackable;
                    endNode = new AnchorNode(plane.createAnchor(plane.getCenterPose()));
                    endNode.setParent(arFragment.getArSceneView().getScene());
                    startWalking();
                    // Handle plane hits.
                    break;
                } else if (trackable instanceof Point) {
                    // Handle point hits
                    Point point = (Point) trackable;
                    endNode = new AnchorNode(point.createAnchor(hit.getHitPose()));
                    endNode.setParent(arFragment.getArSceneView().getScene());
                    startWalking();
                } else if (trackable instanceof AugmentedImage) {
                    // Handle image hits.
                    AugmentedImage image = (AugmentedImage) trackable;
                    endNode = new AnchorNode(image.createAnchor(image.getCenterPose()));
                    endNode.setParent(arFragment.getArSceneView().getScene());
                    startWalking();
                }
            }
        }
    }

    private void startWalking() {
        objectAnimation = new ObjectAnimator();
        objectAnimation.setAutoCancel(true);
        objectAnimation.setTarget(andy);

        // All the positions should be world positions
        // The first position is the start, and the second is the end.
        objectAnimation.setObjectValues(andy.getWorldPosition(), endNode.getWorldPosition());

        // Use setWorldPosition to position andy.
        objectAnimation.setPropertyName("worldPosition");

        // The Vector3Evaluator is used to evaluator 2 vector3 and return the next
        // vector3.  The default is to use lerp.
        objectAnimation.setEvaluator(new Vector3Evaluator());

        // This makes the animation linear (smooth and uniform).
        objectAnimation.setInterpolator(new LinearInterpolator());

        // Duration in ms of the animation.
        objectAnimation.setDuration(500);
        objectAnimation.start();
    }

    private void configureSession() {
        Config config = new Config(mSession);
        if (!setupAugmentedImageDb(config)) {
            Toast.makeText(this, "Could not setup augmented", Toast.LENGTH_SHORT).show();
        }
        config.setUpdateMode(Config.UpdateMode.LATEST_CAMERA_IMAGE);
        mSession.configure(config);
    }

    @Override
    public void onPause() {
        super.onPause();
        if (mSession != null) {
            // Note that the order matters - GLSurfaceView is paused first so that it does not try
            // to query the session. If Session is paused before GLSurfaceView, GLSurfaceView may
            // still call session.update() and get a SessionPausedException.
            arSceneView.pause();
            mSession.pause();
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (mSession == null) {
            String message = null;
            Exception exception = null;
            try {
                mSession = new Session(this);
            } catch (UnavailableArcoreNotInstalledException
                    e) {
                message = "Please install ARCore";
                exception = e;
            } catch (UnavailableApkTooOldException e) {
                message = "Please update ARCore";
                exception = e;
            } catch (UnavailableSdkTooOldException e) {
                message = "Please update this app";
                exception = e;
            } catch (Exception e) {
                message = "This device does not support AR";
                exception = e;
            }

            if (message != null) {
                Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
                Log.e(TAG, "Exception creating session", exception);
                return;
            }
            shouldConfigureSession = true;

        }
        if (shouldConfigureSession) {
            configureSession();
            shouldConfigureSession = false;

            arSceneView.setupSession(mSession);
        }


    }

    private void onUpdateFrame(FrameTime frameTime) {
        Frame frame = arSceneView.getArFrame();


        Collection<AugmentedImage> updatedAugmentedImages =
                frame.getUpdatedTrackables(AugmentedImage.class);
        Log.d("size----", String.valueOf(updatedAugmentedImages.size()));

        for (AugmentedImage augmentedImage : updatedAugmentedImages) {
            if (augmentedImage.getTrackingState() == TrackingState.TRACKING) {
                // Check camera image matches our reference image
                if (augmentedImage.getName().contains("car")) {

                    if (!modelAdded) {
                        modelAdded = true;
                        Anchor anchor = augmentedImage.createAnchor(augmentedImage.getCenterPose());
                        AnchorNode anchorNode = new AnchorNode(anchor);
                        anchorNode.setParent(arFragment.getArSceneView().getScene());

                        // Create the transformable andy and add it to the anchor.
                        andy = new TransformableNode(arFragment.getTransformationSystem());
                        andy.setParent(anchorNode);
                        andy.setRenderable(andyRenderable);
                        andy.select();

                    }
                }

            }
        }

    }

    private boolean setupAugmentedImageDb(Config config) {
        AugmentedImageDatabase augmentedImageDatabase;

        Bitmap augmentedImageBitmap = loadAugmentedImage();
        if (augmentedImageBitmap == null) {
            return false;
        }

        augmentedImageDatabase = new AugmentedImageDatabase(mSession);
        augmentedImageDatabase.addImage("car", augmentedImageBitmap);

        config.setAugmentedImageDatabase(augmentedImageDatabase);
        return true;
    }

    private Bitmap loadAugmentedImage() {
        try (InputStream is = getAssets().open("car.jpeg")) {
            return BitmapFactory.decodeStream(is);
        } catch (IOException e) {
            Log.e(TAG, "IO exception loading augmented image bitmap.", e);
        }
        return null;
    }
}
Tamikotamil answered 14/8, 2018 at 13:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.