google / draco

Draco is a library for compressing and decompressing 3D geometric meshes and point clouds. It is intended to improve the storage and transmission of 3D graphics.
https://google.github.io/draco/
Apache License 2.0
6.52k stars 966 forks source link

Android Integration #371

Closed farzaa closed 6 years ago

farzaa commented 6 years ago

Hey there everyone! I'm having a hard time getting Draco to work on an Android device. The documentation says:

To include Draco in an existing or new Android Studio project, reference it from the cmake file of an existing native project that has a minimum SDK version of 18 or higher. The project must support C++11. To add Draco to your project:

I'm not sure what it means to reference Draco from an "existing native project. If someone could please explain the steps on how to get this going that'd be awesome :).

tomfinegan commented 6 years ago

Can you clarify where you're having the problem? Are you starting with a new project, or adding Draco to one which already exists?

For either case the steps on https://github.com/google/draco#android-studio-project-integration still appear to be correct, but I haven't had time to test them lately.

farzaa commented 6 years ago

Hiya. So, I've made a bit of progress. I managed to create a static library (.a) using Android Studio for x86_84 which should work with an x86_84 emulator. \

Here's how I'm doing my decode from within JNI. The line LOGI("%s", "❌ Bad decode in triangular mesh\n"); always runs whenever I try and decode. This means I'm able to load a mesh, but once I begin to decode it it runs into a bit of a snag. This is happening with every mesh I try, including the bunny.ply given in the original readme.

If you could perhaps test out Android and see if its properly decoding a triangular mesh on your end as a sanity check, that'd be awesome. Also, I should mention I get this same behavior when trying to decode a point cloud as well.

But please do let me know if you see anything wrong with the code. Though, its almost a direct copy paste of code from draco_decoder.cc.

Any tips would be greatly appreciated.


Java_com_example_hellolibs_MainActivity_stringFromJNI(JNIEnv *env, jobject thiz) {

    LOGI("%s", "✅ Welcome to Draco by Flynn.");

    std::ifstream input_file("/storage/emulated/0/FLYNN/bunny.drc", std::ios::binary);

    if (!input_file) {
        LOGI("%s", "❌ Input file not found.");
        return;
    } else {
        LOGI("%s", "✅ Opened input file.");
    }

    // Read the file stream into a buffer.
    std::streampos file_size = 0;
    input_file.seekg(0, std::ios::end);
    file_size = input_file.tellg() - file_size;
    input_file.seekg(0, std::ios::beg);
    std::vector<char> data(file_size);
    input_file.read(data.data(), file_size);

    if (data.empty()) {
        LOGI("%s", "❌ Input file was empty");
        return;
    } else {
        LOGI("%s", "✅ Input file was not empty!");
    }

    draco::DecoderBuffer buffer;
    buffer.Init(data.data(), data.size());

    // Decode the input data into a geometry.
    std::unique_ptr<draco::PointCloud> pc;
    draco::Mesh *mesh = nullptr;
    auto type_statusor = draco::Decoder::GetEncodedGeometryType(&buffer);
    if (!type_statusor.ok()) {
        LOGI("%s", "❌ Decoded geometry type is messed up!");
        return;
    }

    LOGI("ANOTHER... %s", type_statusor.status().error_msg());
    const draco::EncodedGeometryType geom_type = type_statusor.value();
    if (geom_type == draco::TRIANGULAR_MESH) {

        draco::Decoder decoder;
        auto statusor = decoder.DecodeMeshFromBuffer(&buffer);
        LOGI("%s", statusor.status().error_msg());
        if (!statusor.ok()) {
            LOGI("%s", "❌ Bad decode in triangular mesh\n");
            return;
        }

        std::unique_ptr<draco::Mesh> in_mesh = std::move(statusor).value();

        if (in_mesh) {
            mesh = in_mesh.get();
            pc = std::move(in_mesh);
        }
    } else if (geom_type == draco::POINT_CLOUD) {
        // Failed to decode it as mesh, so let's try to decode it as a point cloud.
        draco::Decoder decoder;
        auto statusor = decoder.DecodePointCloudFromBuffer(&buffer);
        if (!statusor.ok()) {
            LOGI("%s", "❌ Bad decode in P.C mesh \n");
            return;
        }
        pc = std::move(statusor).value();

    }

    if (pc == nullptr) {
        LOGI("%s", "❌ Failed to decode the input file.\n");
    } else {
        LOGI("%s", "✅ Decoded!");
    }

}
farzaa commented 6 years ago

I've got it working! Here is my project ->

https://github.com/farzaa/DracoPortedToAndroid

Think I should make a PR to throw this in as an example for Android?

tomfinegan commented 6 years ago

I've got it working!

What was the fix?

Think I should make a PR to throw this in as an example for Android?

It depends on what the issue actually was behind the problem. At this point, no, I don't think that's necessary, but I could be convinced otherwise is the underlying issue is a complex issue that reproduces easily and impacts all developers attempting native integration with Draco.

farzaa commented 6 years ago

So for me, the fix was to actually just use the generated static libraries (.a) files and have the JNI call that. To generate the library, I followed the instructions on the repo readme. But, is this how the devs want us to do it? I much rather be able to call the Draco code directly from the JNI and not have to generate the static libraries and than link them to my app project.

tomfinegan commented 6 years ago

You should not need to build the static libraries separately. I think we need some more info in Draco's README.md. In addition to the steps noted for updating the CMakelists.txt currently listed in the README.md, I believe you need to add the following the app's build.gradle (within the cmake child of externalNativeBuild):

cppFlags "-DDRACO_POINT_CLOUD_COMPRESSION_SUPPORTED"
cppFlags "-DDRACO_MESH_COMPRESSION_SUPPORTED"
cppFlags "-DDRACO_STANDARD_EDGEBREAKER_SUPPORTED"
cppFlags "-DDRACO_PREDICTIVE_EDGEBREAKER_SUPPORTED"
arguments "-DANDROID_STL=c++_shared"

After adding the above you should be able to build Draco as part of your project instead of using the libs directly.

tomfinegan commented 6 years ago

https://github.com/google/draco/pull/377 updates README.md.

These aren't needed:

cppFlags "-DDRACO_POINT_CLOUD_COMPRESSION_SUPPORTED"
cppFlags "-DDRACO_MESH_COMPRESSION_SUPPORTED"
cppFlags "-DDRACO_STANDARD_EDGEBREAKER_SUPPORTED"
cppFlags "-DDRACO_PREDICTIVE_EDGEBREAKER_SUPPORTED"
arguments "-DANDROID_STL=c++_shared"

The existing README was nearly correct. It was only missing the ANDROID_STL setting from the cmake child of externalNativeBuild:

arguments "-DANDROID_STL=c++_shared"

With these updates I'm able to build Draco from within Android Studio and use it from a basic test application on my Android device by simply following the instructions in README.md. My test application is essentially a copy/paste of the code from draco_decoder.cc into native-lib.cpp. The only additional steps were related to file system access permissions.

Please feel free to comment or reopen. Closing this for now.