Search code examples
javaandroidpolygonarcoresceneform

How to draw a polygon with Sceneform, ARCore?


Let's say I have three anchors from hit result from ArFragment.

Anchor anchor = hitResult.createAnchor();

How can I draw a triangle and apply custom texture using Sceneform?


Solution

  • First step is to create a list of AnchorNodes to be able to get the coordinates of the Anchors. We will add all of them in a list:

    private final List<AnchorNode> anchorsList = new ArrayList<>();
    

    then in OnTapArPlaneListener, we can create our triangle if we reach three anchors (or three coordinates). We will generate our triangle as a ModelRenderable:

    final Anchor anchor = hitResult.createAnchor();
    final AnchorNode anchorNode = new AnchorNode(anchor);
    
    anchorNode.setParent(arFragment.getArSceneView().getScene());
    anchorsList.add(anchorNode);
    
    if (anchorsList.size() == 3) {
        final Texture.Sampler sampler = Texture.Sampler.builder()
                .setMinFilter(Texture.Sampler.MinFilter.LINEAR_MIPMAP_LINEAR)
                .setMagFilter(Texture.Sampler.MagFilter.LINEAR)
                .setWrapModeR(Texture.Sampler.WrapMode.REPEAT)
                .setWrapModeS(Texture.Sampler.WrapMode.REPEAT)
                .setWrapModeT(Texture.Sampler.WrapMode.REPEAT)
                .build();
    
        Texture.builder()
                .setSource(() -> getAssets().open("wall.jpg"))
                .setSampler(sampler)
                .build()
                .thenAccept(texture -> MaterialFactory.makeOpaqueWithTexture(this, texture)
                        .thenAccept(material -> {
                            final Node node = new Node();
                            final ModelRenderable triangle = makeTriangleWithAnchors(anchorsList, material);
    
                            node.setParent(arFragment.getArSceneView().getScene());
                            node.setRenderable(triangle);
                        })
                );
    }
    

    and here is the detail of the method makeTriangleWithAnchors():

    private ModelRenderable makeTriangleWithAnchors(@NonNull final List<AnchorNode> anchorNodes, @NonNull final Material material) {
        if (anchorNodes.size() != 3) throw new IllegalStateException("Different count of anchorsList than 3");
    
        final Vector3 p0 = anchorNodes.get(0).getLocalPosition();
        final Vector3 p1 = anchorNodes.get(1).getLocalPosition();
        final Vector3 p2 = anchorNodes.get(2).getLocalPosition();
        final Vector3 up = Vector3.up();
        final UvCoordinate uvTop = new UvCoordinate(0.5f, 1.0f);
        final UvCoordinate uvBotLeft = new UvCoordinate(0.0f, 0.0f);
        final UvCoordinate uvBotRight = new UvCoordinate(1.0f, 0.0f);
        final List<Vertex> vertices = new ArrayList<>(Arrays.asList(
                Vertex.builder().setPosition(p0).setNormal(up).setUvCoordinate(uvTop).build(),
                Vertex.builder().setPosition(p1).setNormal(up).setUvCoordinate(uvBotRight).build(),
                Vertex.builder().setPosition(p2).setNormal(up).setUvCoordinate(uvBotLeft).build()
        ));
    
        final List<Integer> triangleIndices = new ArrayList<>(3);
        triangleIndices.add(0);
        triangleIndices.add(2);
        triangleIndices.add(1);
        triangleIndices.add(0);
        triangleIndices.add(1);
        triangleIndices.add(2);
    
        final RenderableDefinition.Submesh submesh = RenderableDefinition.Submesh.builder()
                .setTriangleIndices(triangleIndices)
                .setMaterial(material)
                .build();
        final RenderableDefinition renderableDefinition = RenderableDefinition.builder()
                .setVertices(vertices)
                .setSubmeshes(Arrays.asList(submesh))
                .build();
        final CompletableFuture future = ModelRenderable.builder()
                .setSource(renderableDefinition)
                .build();
    
        final ModelRenderable result;
        try {
            result = (ModelRenderable) future.get();
        } catch (InterruptedException | ExecutionException e) {
            throw new AssertionError("Error creating renderable.", e);
        }
    
        if (result == null) {
            throw new AssertionError("Error creating renderable.");
        } else {
            return result;
        }
    }
    

    And this is the result I got with the code shown before (I added some Bugdroids to show where the anchors are): enter image description here