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

How to prevent download same gltf file from server every time? #367

Open mistrydarshan99 opened 6 years ago

mistrydarshan99 commented 6 years ago

In ARCore 1.5 documentation provide snippest to load 3D model runtime.

private static final String GLTF_ASSET =
      "https://github.com/KhronosGroup/glTF-Sample-Models/raw/master/2.0/Duck/glTF/Duck.gltf";

ModelRenderable.builder()
        .setSource(activity, RenderableSource.builder()
            .setSource(activity, Uri.parse(GLTF_ASSET), RenderableSource.SourceType.GLTF2)
            .setScale(0.05f)  // Scale the original model to 50%.
            .setRecenterMode(RenderableSource.RecenterMode.ROOT)
            .build())
        .setRegistryId(GLTF_ASSET)
        .build()
        .thenAccept(renderable -> {
          duckRenderable = renderable;
          Toast.makeText(activity, "Download done.", Toast.LENGTH_LONG).show();
        })
        .exceptionally(throwable -> {
          Toast toast = Toast.makeText(activity, "Unable to load renderable " + GLTF_ASSET,
              Toast.LENGTH_LONG);
          toast.setGravity(Gravity.CENTER, 0, 0);
          toast.show();
          return null;
        });

Instead of loading the 3D model at runtime, Can we download (.gltf) file in storage? Is there any method available in sceneform assets library to dowload .gltf file in storage?

Load 3D model every time is time consuming usecase for end user. User will not wait for long time in such usecase.

romainguy commented 6 years ago

A URI can point to a file or a content provider. It doesn't have to be an http URL.

malik-at-work commented 6 years ago

Looking at the code here, file, android resource and http URI's are supported. We don't do content providers yet. I'm adding support for that for the next release.

mistrydarshan99 commented 6 years ago

@malik-at-work I think at least provide one method in sceneform assets library that accepts two parameters whether user download 3D model or not and download location of 3D model in file storage. So that we will not retrieve 3D model from a server every time.

artemtkachenko commented 5 years ago

@malik-at-work Hello If you don't mind could you clarify is it possible to get downloaded .sfb file url from ModelRenderable? E.g.

  ModelRenderable.builder()
          .setSource(this, Uri.parse("http://blabla/3dmodels/android/my_custom_model.sfb"))
          .build()
            .thenAccept(renderable -> {
                this.renderable = renderable;
            })

In accept callback I want to store fetched .sfb file into the local device storage and use this local stored .sfb file later instead of downloading it again from the server. Please advise.

jonas-kgomo commented 5 years ago

I tried to load this model at runtime from Azure But it doesn't load. I followed the documentation.

How can we download to device then render in Sceneform?

artemtkachenko commented 5 years ago

@jonas-kgomo You should download a file itself into device storage and use Callable<InputStream> inputStreamCreator as a source for ModelRenderable: https://developers.google.com/ar/reference/java/sceneform/reference/com/google/ar/sceneform/rendering/ModelRenderable.Builder e.g. (kotlin)

ModelRenderable.builder()
            .setSource(context, Callable<InputStream> {
                return@Callable FileInputStream(%downloaded_file_absolute_ path_string%)
            })
            .build()
jonas-kgomo commented 5 years ago

I am using Java, this doesn't render the model. It's not able to fetch the location from runtime :

            File file = new File("file:///storage/emulated/0/Download/andy.sfb");
            Callable callable = () -> {
            InputStream inputStream = new FileInputStream(file);
            return inputStream;
        };
        FutureTask task = new FutureTask<>(callable);
        new Thread(task).start();

        ModelRenderable.builder()
            .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;
            }

            // 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();
        });
    }

I have a button that downloads the model to device (internal storage),

public void addListenerOnButton() {

        button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View view) {

                downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
                Uri uri = Uri.parse("https://companyname.blob.core.windows.net/models/andy.sfb");

                DownloadManager.Request request = new DownloadManager.Request(uri);
                request.setTitle("model");
                request.setDescription("downloading...");
                request.allowScanningByMediaScanner();// if you want to be available from media players

                request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
                Long reference = downloadManager.enqueue(request);
                File filepath = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "andy.sfb");

                Toast.makeText(getApplicationContext(), "FilePath : " + filepath, Toast.LENGTH_LONG).show();
              // this file is saved at : /storage/emulated/0/Download/andy.png

            }

        });
    }
artemtkachenko commented 5 years ago

@jonas-kgomo I sent you a 100% working solution. I don't know what is wrong with your code; basically, there are plenty of reasons why your code does not render a model:

Moreover you don't need creatingFutureTask and start it in a thread, just pass the callable object into setSource(...). Also I recommend logging the exception message. Display toast with the hardcoded text message won't help you;

manojghorela69 commented 5 years ago

@artemtkachenko can you share with me the working solution thank you.

chnouman commented 4 years ago

@artemtkachenko when we can expect running code?