How to save Meshes from ML2 Meshing sample OR the Spaces app?

Hi everyone,

I am currently working on a project where I need to save the meshing results as an OBJ file after scanning a space or object using the Magic Leap 2 SDK. The meshing sample and/or the spaces app are the good examples. I am using meshing sample from the C-API provided in the mlsdk (don't need to use Unity). However, I’m encountering an issue where the application fails to retrieve the mesh result, specifically returning the error "Failed to get mesh result."
Here is the SaveMesh function where I attempt to save the mesh data to an OBJ file:

void SaveMesh(MLHandle meshing_client) {
    std::ofstream file("/data/data/com.magicleap.capi.sample.meshing/files/MeshOutput.obj");
    if (!file.is_open()) {
        status_message_ = "Failed to open file for writing";
        return;
    }
    status_message_ = "File opened successfully for writing";

    MLMeshingExtents extents = {{{0.f, 0.f, 0.f}}, {{0.f, 0.f, 0.f, 1.f}}, {{10.f, 10.f, 10.f}}};  // Define the extents
    MLHandle request_handle = ML_INVALID_HANDLE;
    MLResult result = MLMeshingRequestMeshInfo(meshing_client, &extents, &request_handle);
    if (result != MLResult_Ok) {
        status_message_ = "Failed to request mesh info";
        return;
    }
    status_message_ = "Mesh info requested successfully";

    MLMeshingMeshInfo mesh_info = {};
    result = MLMeshingGetMeshInfoResult(meshing_client, request_handle, &mesh_info);
    const int max_retries = 10;
    int retry_count = 0;
    while (result == MLResult_Pending && retry_count < max_retries) {
        std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Sleep for 100 ms
        result = MLMeshingGetMeshInfoResult(meshing_client, request_handle, &mesh_info);
        retry_count++;
    }

    if (result != MLResult_Ok) {
        status_message_ = "Failed to get mesh info result after retries. Error: " + std::to_string(result);
        return;
    }

    status_message_ = "Mesh info retrieved successfully";

    for (uint32_t i = 0; i < mesh_info.data_count; ++i) {
        MLMeshingBlockRequest block_request = {mesh_info.data[i].id, MLMeshingLOD_Medium};
        MLMeshingMeshRequest mesh_request = {1, &block_request};

        result = MLMeshingRequestMesh(meshing_client, &mesh_request, &request_handle);
        if (result != MLResult_Ok) {
            status_message_ = "Failed to request mesh";
            return;
        }

        MLMeshingMesh mesh = {};
        result = MLMeshingGetMeshResult(meshing_client, request_handle, &mesh);
        if (result != MLResult_Ok) {
            status_message_ = "Failed to get mesh result";
            return;
        }

        if (mesh.data_count > 0) {
            status_message_ = "Mesh data retrieved with " + std::to_string(mesh.data_count) + " blocks";
            for (uint32_t j = 0; j < mesh.data_count; ++j) {
                MLMeshingBlockMesh &block_mesh = mesh.data[j];
                if (block_mesh.vertex_count > 0 && block_mesh.index_count > 0) {
                    status_message_ = "Processing block " + std::to_string(j) + " with " + std::to_string(block_mesh.vertex_count) + " vertices.";
                }

                for (uint32_t v = 0; v < block_mesh.vertex_count; ++v) {
                    file << "v " << block_mesh.vertex[v].x << " " << block_mesh.vertex[v].y << " " << block_mesh.vertex[v].z << "\n";
                }

                for (uint32_t idx = 0; idx < block_mesh.index_count; idx += 3) {
                    file << "f " << block_mesh.index[idx] + 1 << " " << block_mesh.index[idx + 1] + 1 << " " << block_mesh.index[idx + 2] + 1 << "\n";
                }
            }
        } else {
            status_message_ = "No mesh data available.";
        }

        result = MLMeshingFreeResource(meshing_client, &request_handle);
        if (result != MLResult_Ok) {
            status_message_ = "Failed to free resources";
        }
    }

    file.close();
    status_message_ = "Mesh saved to /data/data/com.magicleap.capi.sample.meshing/files/MeshOutput.obj";
}

Hi @dawar.khan977 Do you mind providing more logs from the application when you run into this issue? Does the MLSDK Meshing sample work when you run it without the custom logic?

Hi @kbabilinski ,
Thank you for the response.

The app runs smoothly, but the mesh results were not being retrieved. Fortunately, I got some meshes now BUT I am not sure if this is the good way or is there any other smart way to do this. Because my current mesh is not same as I see in scanning. Probably the points are same but there are also some edged from the end. Here is the details of the app I created:

I created a GUI in the meshing sample using the mlsdk C API. There are three buttons:

  1. Start Scan: Starts scanning the surface with triangular meshes.
  2. Stop Scan: Stops the scanning.
  3. Save Mesh as OBJ: Saves the mesh as an OBJ file.

The first two functions work, but when I click "Save OBJ," it fails to retrieve the mesh results.

Below is the code for the saveMesh function, which is called when the user clicks the "Save Mesh" button:
thanks

void SaveMesh(MLHandle meshing_client) {
    // Open the file for writing
    std::ofstream file("/data/data/com.magicleap.capi.sample.meshing/files/MeshOutput.obj");
    if (!file.is_open()) {
        status_message_ = "Failed to open file for writing";
        return;
    }
    status_message_ = "File opened successfully for writing";

    // Define the extents for the mesh request
    MLMeshingExtents extents = {{{0.f, 0.f, 0.f}}, {{0.f, 0.f, 0.f, 1.f}}, {{10.f, 10.f, 10.f}}};
    MLHandle request_handle = ML_INVALID_HANDLE;

    // Request mesh information
    MLResult result = MLMeshingRequestMeshInfo(meshing_client, &extents, &request_handle);
    if (result != MLResult_Ok) {
        status_message_ = "Failed to request mesh info";
        //file << status_message_ << std::endl;
        return;
    }

    status_message_ = "Mesh info requested successfully";
    //file << status_message_ << std::endl;

    // Retrieve the mesh info
    MLMeshingMeshInfo mesh_info = {};
    const int max_retries = 15;
    int retry_count = 0;
    while (retry_count < max_retries) {
        result = MLMeshingGetMeshInfoResult(meshing_client, request_handle, &mesh_info);
        if (result == MLResult_Ok) {
            break;  // Successfully retrieved mesh info
        } else if (result != MLResult_Pending) {
            status_message_ = "Failed to get mesh info result after retries. Error: " + std::to_string(result);
            //file << status_message_ << std::endl;
            return;
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(100));  // Wait before retrying
        retry_count++;
    }

    if (result != MLResult_Ok) {
        status_message_ = "Failed to retrieve mesh info. Error: " + std::to_string(result);
        //file << status_message_ << std::endl;
        return;
    }

    status_message_ = "Mesh info retrieved successfully";
    //file << status_message_ << std::endl;

    // Process each block in the mesh info
    for (uint32_t i = 0; i < mesh_info.data_count; ++i) {
        MLMeshingBlockRequest block_request = {mesh_info.data[i].id, MLMeshingLOD_Medium};
        MLMeshingMeshRequest mesh_request = {1, &block_request};

        result = MLMeshingRequestMesh(meshing_client, &mesh_request, &request_handle);
        if (result != MLResult_Ok) {
            status_message_ = "Failed to request mesh for block " + std::to_string(i);
            //file << status_message_ << std::endl;
            continue;
        }

        MLMeshingMesh mesh = {};
        retry_count = 0;
        while (retry_count < max_retries) {
            result = MLMeshingGetMeshResult(meshing_client, request_handle, &mesh);
            if (result == MLResult_Ok) {
                break;  // Successfully retrieved mesh result
            } else if (result != MLResult_Pending) {
                status_message_ = "Failed to get mesh result after retries. Error: " + std::to_string(result);
                //file << status_message_ << std::endl;
                break;
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(100));  // Wait before retrying
            retry_count++;
        }

        if (result != MLResult_Ok) {
            continue;  // Skip to the next block if mesh result retrieval failed
        }

        // Write mesh data to file
        for (uint32_t j = 0; j < mesh.data_count; ++j) {
            MLMeshingBlockMesh &block_mesh = mesh.data[j];
            if (block_mesh.vertex_count > 0 && block_mesh.index_count > 0) {
                for (uint32_t v = 0; v < block_mesh.vertex_count; ++v) {
                    file << "v " << block_mesh.vertex[v].x << " " << block_mesh.vertex[v].y << " " << block_mesh.vertex[v].z << "\n";
                }
                for (uint32_t idx = 0; idx < block_mesh.index_count; idx += 3) {
                    file << "f " << block_mesh.index[idx] + 1 << " " << block_mesh.index[idx + 1] + 1 << " " << block_mesh.index[idx + 2] + 1 << "\n";
                }
            }
        }

        // Free the resources for the current mesh request
        MLMeshingFreeResource(meshing_client, &request_handle);
    }

    // Close the file and update the status message
    file.close();
    status_message_ = "Mesh saved to /data/data/com.magicleap.capi.sample.meshing/files/MeshOutput.obj";
}


Here is a mesh saved..

I wont be able to debug your code directly as serializing mesh files falls outside the scope of our support but based on the images The issue with your function appears to be related to how the vertex indices are written to the file in the face definitions. OBJ files are 1-indexed, meaning the vertex indices should start from 1, but your code might be incorrectly handling indices or the vertex order in a way that causes incorrect connections between vertices.

Thank you. Yeah there was an indexing issue which is fixed now. BTW Magic leap misses black objects which is a big limitation of its scanning.
thanks

This topic was automatically closed 15 days after the last reply. New replies are no longer allowed.