Search code examples
graphics3dmeshassimp

Improve mesh geometry serialization performance


I want to improve the serialization time of my 3D application. I use the following scene for testing: https://www.dropbox.com/scl/fi/j8ays9phm2xs45icla4bo/TestScene.zip?rlkey=qy8jpwgz3s8b95mz62l8axcug&dl=0

I did a performance comparison with Blender and 3DS Max.
Blender: 1 second
3DS max: 2 seconds
My application: 15 seconds !!

To serialize meshes I use the OBJ file format and Assimp.
The Assimp serialization time is too high. Should I write my own format? Is there any tricks I should know about?

Thank you. For convenience here my serialization code:

    aiScene* scene = new aiScene();

    // Allocate materials memory
    scene->mNumMaterials = 1;
    scene->mMaterials = new aiMaterial*[scene->mNumMaterials];
    
    // Allocate mesh memory
    scene->mNumMeshes = (unsigned)m_flatMeshContainer.size();
    scene->mMeshes = new aiMesh*[m_flatMeshContainer.size()];

    scene->mRootNode = new aiNode();
    scene->mRootNode->mNumMeshes = 0;

    // Create materials
    for (Uint32 i = 0u; i < scene->mNumMaterials; ++i)
    {
        scene->mMaterials[i] = new aiMaterial();
    }

    // Create meshes
    std::mutex mtx;
    tbb::parallel_for(size_t(0), m_meshGroupIdentifiers.size(), [&](size_t idx) {

        const auto& groupId = m_meshGroupIdentifiers[idx];
        const auto group = getMeshGroupPtr_FromEntity(groupId);

        // Create meshes
        const auto nbMeshes = (unsigned)group->m_meshes.size();
        aiNode* node = new aiNode();
        node->mMeshes = new unsigned int[nbMeshes];
        node->mNumMeshes = nbMeshes;

        for (Uint32 i = 0u; i < nbMeshes; ++i)
        {
            // Init mesh
            const Mesh* nativeMesh = group->m_meshes[i];
            const auto flatMeshIdx = nativeMesh->getFlatId();
            scene->mMeshes[flatMeshIdx] = new aiMesh();
            node->mMeshes[i] = flatMeshIdx;

            aiMesh* mesh = scene->mMeshes[flatMeshIdx];
            mesh->mName = group->getName();
            mesh->mMaterialIndex = 0;

            // Build vertices
            const auto& vertices = nativeMesh->getRealVertices();
            const auto nbVertex = (unsigned)vertices.size();

            mesh->mVertices = new aiVector3D[nbVertex];
            mesh->mNormals = new aiVector3D[nbVertex];
            mesh->mNumVertices = nbVertex;

            mesh->mTextureCoords[0] = new aiVector3D[nbVertex];
            mesh->mNumUVComponents[0] = nbVertex;

            for (Uint32 j = 0u; j < nbVertex; ++j)
            {
                const auto& vtx = vertices[j];

                mesh->mVertices[j] = aiVector3D(vtx.position.x, vtx.position.y, vtx.position.z);
                mesh->mNormals[j] = aiVector3D(vtx.normal.x, vtx.normal.y, vtx.normal.z);
                mesh->mTextureCoords[0][j] = aiVector3D(vtx.texCoord.x, vtx.texCoord.y, 0);
            }

            // Build faces
            const auto& indices = nativeMesh->getRealIndices();
            mesh->mNumFaces = (unsigned)indices.size() / PRIMITIVE_NB_VTX;
            mesh->mFaces = new aiFace[mesh->mNumFaces];

            for (Uint32 j = 0u; j < indices.size(); j += PRIMITIVE_NB_VTX)
            {
                aiFace &face = mesh->mFaces[j / PRIMITIVE_NB_VTX];
                face.mIndices = new unsigned int[PRIMITIVE_NB_VTX];
                face.mNumIndices = PRIMITIVE_NB_VTX;

                for (Uint32 k = 0; k < PRIMITIVE_NB_VTX; ++k)
                {
                    face.mIndices[k] = indices[k + j];
                }
            }
        }

        std::lock_guard<std::mutex> lock(mtx);
        scene->mRootNode->addChildren(1, &node);
    });


    const auto objPath = m_serializationFullPath.string();
    Assimp::Exporter exporter;
    // HERE : SLOW  --------------------------------------------------------------------------------------------
    exporter.Export(scene, "obj", objPath);
    // ---------------------------------------------------------------------------------------------------------

    delete scene;

Solution

  • I solved it by implementing my own binary file format.
    For writing to the file i use:

        std::ofstream fileStream(filePath, std::ios::out | std::ios::binary)
    

    for reading:

        std::ifstream fileStream(filePath, std::ios::out | std::ios::binary);
    

    Here good examples: https://www.tutorialspoint.com/reading-and-writing-binary-file-in-c-cplusplus#:~:text=To%20write%20a%20binary%20file,the%20end%20of%20the%20file.