google-ar / sceneform-android-sdk

Sceneform SDK for Android
https://developers.google.com/sceneform/develop/
Apache License 2.0
1.23k stars 604 forks source link

Remove Anchor from scene view in android #135

Closed kirankaushal closed 6 years ago

kirankaushal commented 6 years ago

When sceneform render anchor with TransformableNode node = new TransformableNode(arFragment.getTransformationSystem()); node.setRenderable(renderable); node.setParent(anchorNode); arFragment.getArSceneView().getScene().addChild(anchorNode); node.select();

then how to detect and ditach() Anchor from sceneform in android. I have ditached() an Anchor on clear button but after rendering its not ditaching .How to do it?

geraldika commented 6 years ago

@kirankaushal
It works for me: node.getScene().onRemoveChild(node.getParent()) node.setRenderable(null) session.update()

But I'm not sure that it's correct way to remove model from screen

kirankaushal commented 6 years ago

It's working for rendering.But I want to remove rendered Anchor node too.

kirankaushal commented 6 years ago

I have removed added Achor until it's not rendered created node but I was not able to remove rendered node too.

dsternfeld7 commented 6 years ago

Calling parent.removeChild(child) or node.setParent(null) is the correct way to remove a Node from the scene. You can do the same thing with an AnchorNode. Additionally, you can call anchorNode.getAnchor().detach to access the ARCore anchor and tell ARCore to stop tracking that anchor.

kirankaushal commented 6 years ago

I tried to ditach anchorNode but it's not working

dsternfeld7 commented 6 years ago

Can you please share your code with us? Thanks!

kirankaushal commented 6 years ago

package com.example.nandakiran.sceneformoverview;

import android.content.Intent; import android.graphics.Bitmap; import android.icu.text.SimpleDateFormat; import android.net.Uri; import android.os.Build; import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; import android.support.annotation.GuardedBy; import android.support.annotation.Nullable; import android.support.annotation.RequiresApi; import android.support.design.widget.Snackbar; import android.support.v4.content.FileProvider; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.Gravity; import android.view.MotionEvent; import android.view.PixelCopy; import android.view.View; import android.widget.Button; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.Toast;

import com.google.ar.core.Anchor; import com.google.ar.core.Frame; import com.google.ar.core.HitResult; import com.google.ar.core.Plane; import com.google.ar.core.Point; import com.google.ar.core.Trackable; import com.google.ar.core.TrackingState; import com.google.ar.sceneform.AnchorNode; import com.google.ar.sceneform.ArSceneView; import com.google.ar.sceneform.rendering.ModelRenderable; import com.google.ar.sceneform.rendering.Renderable; import com.google.ar.sceneform.rendering.ViewRenderable; import com.google.ar.sceneform.ux.ArFragment; import com.google.ar.sceneform.ux.TransformableNode;

import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.Date; import java.util.List; import java.util.concurrent.CompletableFuture;

public class SceneFormActivity extends AppCompatActivity { private ArFragment arFragment; private ModelRenderable andyRenderable; private PointerDrawable pointer = new PointerDrawable(); private boolean isTracking; private boolean isHitting; private ArSceneView arSceneView; private String TAG="SceneFormActivity"; @Nullable @GuardedBy("singleTapAnchorLock") private Anchor anchor; private Anchor anchor1; ImageButton imageButton; AnchorNode anchorNode;

@RequiresApi(api = Build.VERSION_CODES.N)
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main2);
    arFragment = (ArFragment) getSupportFragmentManager().findFragmentById(R.id.ux_fragment);
    imageButton=findViewById(R.id.ImageButtonClick);
    imageButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            takePhoto();
        }
    });
    // 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().
   /* if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        ModelRenderable.builder()
                .setSource(this, R.raw.andy)
                .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 (plane.getType() != Plane.Type.HORIZONTAL_UPWARD_FACING) {
                    return;
                }
                // Create the Anchor.
                Anchor anchor = hitResult.createAnchor();
                AnchorNode anchorNode = new AnchorNode(anchor);
                anchorNode.setParent(arFragment.getArSceneView().getScene());
                // Create the transformable andy and add it to the anchor.
                TransformableNode andy = new TransformableNode(arFragment.getTransformationSystem());
                andy.setParent(anchorNode);
                andy.setRenderable(andyRenderable);
                andy.select();
            });*/
   /* arFragment.getArSceneView().getScene().setOnUpdateListener(frameTime -> {
        arFragment.onUpdate(frameTime);
        onUpdate();
    });*/
    initializeGallery();

}

//

private void initializeGallery() {
    LinearLayout gallery = findViewById(R.id.gallery_layout);

    ImageView andy = new ImageView(this);
    andy.setImageResource(R.drawable.droid_thumb);
    andy.setPadding(10,10,10,10);
    andy.setContentDescription("andy");
    andy.setOnClickListener(view ->{
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {

           // Log.d(TAG, String.valueOf(arFragment.getArSceneView().getArFrame().getUpdatedAnchors()));
            addObject(Uri.parse("andy.sfb"));
        }
    });
    gallery.addView(andy);

    ImageView cabin = new ImageView(this);
    cabin.setImageResource(R.drawable.cabin_thumb);
    cabin.setContentDescription("cabin");
    cabin.setPadding(10,10,10,10);
    cabin.setOnClickListener(view ->{
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {

            addObject(Uri.parse("Cabin.sfb"));
        }
    });
    gallery.addView(cabin);

    ImageView house = new ImageView(this);
    house.setImageResource(R.drawable.house_thumb);
    house.setContentDescription("house");
    house.setPadding(10,10,10,10);
    house.setOnClickListener(view ->{
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {

            addObject(Uri.parse("House.sfb"));
        }
    });
    gallery.addView(house);

    /*ImageView igloo = new ImageView(this);
    igloo.setImageResource(R.drawable.igloo_thumb);
    igloo.setContentDescription("igloo");
    igloo.setPadding(10,10,10,10);
    igloo.setOnClickListener(view ->{
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {

            addObject(Uri.parse("igloo.sfb"));
        }
    });
    gallery.addView(igloo);*/

    Button button=new Button(this);
    button.setText("Clear Anchor");
    button.setPadding(15,15,15,15);
    button.setOnClickListener(view ->{
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            setNewAnchor(null);
            setNewAnchor1(null);
        }
    });
    gallery.addView(button);
}

private void addObject(Uri model) {
    Frame frame = arFragment.getArSceneView().getArFrame();
    android.graphics.Point pt = getScreenCenter();
    List<HitResult> hits;

    if (frame != null ) {
        hits = frame.hitTest(pt.x, pt.y);
        for (HitResult hit : hits) {
            Trackable trackable = hit.getTrackable();
            if ((trackable instanceof Plane &&
                    ((Plane) trackable).isPoseInPolygon(hit.getHitPose()))) {
                placeObject(arFragment, hit.createAnchor(), model);
                break;
            }
        }
    }
}

private void placeObject(ArFragment arFragment, Anchor anchor, Uri model) {
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
        CompletableFuture<Void> renderableFuture =
                ModelRenderable.builder()
                        .setSource(arFragment.getContext(), model)
                        .build()
                        .thenAccept(renderable -> addNodeToScene(arFragment, anchor, renderable))
                        .exceptionally((throwable -> {
                            AlertDialog.Builder builder = new AlertDialog.Builder(this);
                            builder.setMessage(throwable.getMessage())
                                    .setTitle("Codelab error!");
                            AlertDialog dialog = builder.create();
                            dialog.show();
                            return null;
                        }));
    }
}
private void addNodeToScene(ArFragment arFragment, Anchor anchor, Renderable renderable) {

    AnchorNode anchorNode = new AnchorNode(anchor);
    setNewAnchor(anchor);
    TransformableNode node = new TransformableNode(arFragment.getTransformationSystem());
    node.setRenderable(renderable);
    node.setParent(anchorNode);
    Log.d(TAG,"anchor: "+anchor);
    Log.d(TAG,"anchor node: "+anchorNode.getAnchor());
    arFragment.getArSceneView().getScene().addChild(anchorNode);
    //setNewAnchor1(anchorNode.getAnchor());
    node.select();
    }

//code to create pointer
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
private void onUpdate() {
    boolean trackingChanged = updateTracking();
    View contentView = findViewById(android.R.id.content);
    if (trackingChanged) {
        if (isTracking) {
            contentView.getOverlay().add(pointer);
        } else {
            contentView.getOverlay().remove(pointer);
        }
        contentView.invalidate();
    }

    if (isTracking) {
        boolean hitTestChanged = updateHitTest();
        if (hitTestChanged) {
            pointer.setEnabled(isHitting);
            contentView.invalidate();
        }
    }
}

private boolean updateTracking() {
    Frame frame = arFragment.getArSceneView().getArFrame();
    boolean wasTracking = isTracking;
    isTracking = frame.getCamera().getTrackingState() == TrackingState.TRACKING;
    return isTracking != wasTracking;
}

private boolean updateHitTest() {
    Frame frame = arFragment.getArSceneView().getArFrame();
    android.graphics.Point pt = getScreenCenter();
    List<HitResult> hits;
    boolean wasHitting = isHitting;
    isHitting = false;
    if (frame != null) {
        hits = frame.hitTest(pt.x, pt.y);
        for (HitResult hit : hits) {
            Trackable trackable = hit.getTrackable();
            if ((trackable instanceof Plane &&
                    ((Plane) trackable).isPoseInPolygon(hit.getHitPose()))) {
                isHitting = true;
                break;
            }
        }
    }
    return wasHitting != isHitting;
}

private android.graphics.Point getScreenCenter() {
    View vw = findViewById(android.R.id.content);
    return new android.graphics.Point(vw.getWidth()/2, vw.getHeight()/2);
}

@GuardedBy("singleTapAnchorLock")
private void setNewAnchor(@Nullable Anchor newAnchor) {
    if (anchor != null) {
        anchor.detach();
    }
    anchor = newAnchor;
}
@GuardedBy("singleTapAnchorLock")
private void setNewAnchor1(AnchorNode anchorNode1) {
    if(anchorNode!=null){
        anchorNode.setParent(null);
    }
    anchorNode = anchorNode1;
}
private void takePhoto() {
    final String filename = generateFilename();
    ArSceneView view = arFragment.getArSceneView();

    // Create a bitmap the size of the scene view.
    final Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),
            Bitmap.Config.ARGB_8888);

    // Create a handler thread to offload the processing of the image.
    final HandlerThread handlerThread = new HandlerThread("PixelCopier");
    handlerThread.start();
    // Make the request to copy.
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        PixelCopy.request(view, bitmap, (copyResult) -> {
            if (copyResult == PixelCopy.SUCCESS) {
                try {
                    saveBitmapToDisk(bitmap, filename);
                } catch (IOException e) {
                    Toast toast = Toast.makeText(SceneFormActivity.this, e.toString(),
                            Toast.LENGTH_LONG);
                    toast.show();
                    return;
                }
                Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content),
                        "Photo saved", Snackbar.LENGTH_LONG);
                snackbar.setAction("Open in Photos", v -> {
                    File photoFile = new File(filename);

                    Uri photoURI = FileProvider.getUriForFile(SceneFormActivity.this,
                            SceneFormActivity.this.getPackageName() + ".ar.name.provider",
                            photoFile);
                    Intent intent = new Intent(Intent.ACTION_VIEW, photoURI);
                    intent.setDataAndType(photoURI, "image/*");
                    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    startActivity(intent);

                });
                snackbar.show();
            } else {
                Toast toast = Toast.makeText(SceneFormActivity.this,
                        "Failed to copyPixels: " + copyResult, Toast.LENGTH_LONG);
                toast.show();
            }
            handlerThread.quitSafely();
        }, new Handler(handlerThread.getLooper()));
    }
}
private void saveBitmapToDisk(Bitmap bitmap, String filename) throws IOException {

    File out = new File(filename);
    if (!out.getParentFile().exists()) {
        out.getParentFile().mkdirs();
    }
    try (FileOutputStream outputStream = new FileOutputStream(filename);
         ByteArrayOutputStream outputData = new ByteArrayOutputStream()) {
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputData);
        outputData.writeTo(outputStream);
        outputStream.flush();
        outputStream.close();
    } catch (IOException ex) {
        throw new IOException("Failed to save bitmap to disk", ex);
    }
}
private String generateFilename() {
    String date =
            null;
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
        date = new SimpleDateFormat("yyyyMMddHHmmss", java.util.Locale.getDefault()).format(new Date());
    }
    return Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES) + File.separator + "Sceneform/" + date + "_screenshot.jpg";
}

}

kirankaushal commented 6 years ago

I have made a setNewAnchor() method and called that method while A anchor hit on screen and also when I want to clear Anchor on Clear button click.

kirankaushal commented 6 years ago

I had made changes in CodeLab SceneForm Project by Google

claywilkinson commented 6 years ago

If I understand you correctly, you want to clear the anchor if an existing anchor is already in the scene? I do this here. This also moves the renderable nodes to be children of the new anchor.

It is good practice to not have a renderable set on the anchor node, but rather add a child node of the anchor node which has the renderable set.

 /** Sets the new value of the current anchor. Detaches the old anchor, if it was non-null. */
  private void setNewAnchor(Anchor newAnchor) {
    AnchorNode newAnchorNode = null;

    if (anchorNode != null && newAnchor != null) {
      // Create a new anchor node and move the children over.
      newAnchorNode = new AnchorNode(newAnchor);
      newAnchorNode.setParent(arFragment.getArSceneView().getScene());
      List<Node> children = new ArrayList<>(anchorNode.getChildren());
      for (Node child : children) {
        child.setParent(newAnchorNode);
      }
    } else if (anchorNode == null && newAnchor != null) {
      // First anchor node created, add Andy as a child.
      newAnchorNode = new AnchorNode(newAnchor);
      newAnchorNode.setParent(arFragment.getArSceneView().getScene());

      Node andy = new Node();
      andy.setRenderable(andyRenderable);
      andy.setParent(newAnchorNode);
    } else {
      // Just clean up the anchor node.
      if (anchorNode != null && anchorNode.getAnchor() != null) {
        anchorNode.getAnchor().detach();
        anchorNode.setParent(null);
        anchorNode = null;
      }
    }
    anchorNode = newAnchorNode;
  }

If you simply want to clear the scene you can do this by:

    private void onClear() {
        List<Node> children = new ArrayList<>(fragment.getArSceneView().getScene().getChildren());
        for (Node node : children) {
            if (node instanceof AnchorNode) {
                if (((AnchorNode) node).getAnchor() != null) {
                    ((AnchorNode) node).getAnchor().detach();
                }
            }
            if (!(node instanceof Camera) && !(node instanceof Sun)) {
                node.setParent(null);
            }
        }
    }
malik-at-work commented 6 years ago

Closing due to inactivity

ashif-ismail commented 6 years ago

this works for me

private void removePreviousAnchors() { List<Node> nodeList = new ArrayList<>(arFragment.getArSceneView().getScene().getChildren()); for (Node childNode : nodeList) { if (childNode instanceof AnchorNode) { if (((AnchorNode) childNode).getAnchor() != null) { ((AnchorNode) childNode).getAnchor().detach(); ((AnchorNode) childNode).setParent(null); } } } }

basitsaleemmb commented 6 years ago

andyRenderable = finalAndy.get(); node= new Node(); node.setRenderable(andyRenderable);

node.setOnTapListener((v, event) -> { Toast.makeText( c, "I want to remove node from scene", Toast.LENGTH_LONG) .show(); });

i want to remove node from scene on tap of node

ashif-ismail commented 5 years ago

It clearly tells that,you miss a '{' . Close the parentheses wherever you are missing it.

On Tue, Apr 30, 2019, 10:47 PM Nana-asiamah98 notifications@github.com wrote:

@SheikhZayed https://github.com/SheikhZayed I'm getting an error with your code. '}' expected How do I resolve it?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/google-ar/sceneform-android-sdk/issues/135#issuecomment-488069639, or mute the thread https://github.com/notifications/unsubscribe-auth/ACC6FFPBKSRIDWBNI73H5MTPTCH23ANCNFSM4FGHMEKA .

Nana-asiamah98 commented 5 years ago

@SheikhZayed I fixed it but it deletes all the nodes created. I want to delete a single node. Please,help me out.

vishvendu commented 5 years ago

I want to clear the node without any button click , I want to stop rendering a video when image has been removed from the place while we are rendering the video on that image. The problem is I still see the video rendering on that part even the image has been removed.