rapidobj

repository·master·Indexed 42 days ago

https://github.com/guybrush77/rapidobj

A high-performance, single-header C++17 library for fast parsing of Wavefront .obj files, optimized for modern hardware and large 3D models. It provides functionality to parse files and streams, handle material libraries (.mtl), and triangulate meshes.

Tokens
5.3K
Snippets
13
Records
19
Agent score
3%

What's inside rapidobj

  1. Configure material handling with MaterialLibrary

    master

    The rapidobj::MaterialLibrary object controls how .mtl files are located and loaded during parsing. It can be used to specify search paths (relative or absolute), provide material definitions as raw strings, or instruct the parser to ignore materials entirely.

    Common Constructors:

    • Default(): For ParseFile, searches the .obj file's directory. For ParseStream, ignores materials but populates Mesh::material_ids.
    • SearchPath(path, policy): Specifies a single search path. Relative paths are relative to the .obj file's parent folder and only work with ParseFile.
    • SearchPaths(paths, policy): Specifies multiple paths to be searched in order. The first match is used.
    • String(text): Provides material definitions directly via a string.
    • Ignore(): Completely ignores all material libraries; Materials and Mesh::material_ids will be empty.
    // Search multiple paths in order
    MaterialLibrary mtllib = MaterialLibrary::SearchPaths({ "materials", "../materials", "." });
    Result result = ParseFile("/home/user/teapot/teapot.obj", mtllib);
    
    // Define materials via string
    static constexpr auto materials = R"(
        newmtl red
        Kd 1 0 0
    )";
    Result result = ParseFile("/home/user/teapot/teapot.obj", MaterialLibrary::String(materials));
  2. Understand the rapidobj Result object

    master
    The Result object is the primary return value from the ParseFile and ParseStream functions. It acts as a container for the parsed data, holding both the .obj and .mtl file data in a binary format. It provides access to the parsed Shape (which can be a Mesh, Lines, or Points) and the Materials array.
  3. Post-processing .obj data for hardware rendering

    master

    Parsed .obj data often requires additional processing before it can be used for GPU/hardware rendering. While rapidobj provides rapidobj::Triangulate to convert polygons to triangles, developers are responsible for implementing other common rendering preparation steps, such as:

    • Attribute Interleaving: Gathering attributes into a single array of interleaved vertex data (optionally including vertex deduplication).
    • Normal Generation: Generating normals if they are missing from the .obj file, potentially using smoothing groups for higher quality.
    • Mesh Optimization: Optimizing meshes based on criteria like material type, mesh size, or minimizing the number of batches submitted to the GPU.
  4. Access vertex data via Attributes

    master

    The Attributes class stores vertex data in four linear arrays of 32-bit floats. Only positions are mandatory; texcoords, normals, and colors may be empty. Data is interleaved as follows:

    • Attributes::positions: { x, y, z } per vertex.
    • Attributes::normals: { x, y, z } per vertex.
    • Attributes::texcoords: { u, v } per vertex.
    • Attributes::colors: { r, g, b } per vertex.
  5. Understand the Lines and Points data structures

    master

    Both Lines and Points use an indices array of Index objects to reference vertex attributes:

    • Lines: Represents a set of polylines. Lines::num_line_vertices stores the number of vertices in each polyline (2 or more). Index::normal_index is always set to an invalid index (-1). Index::texcoord_index is optional (uses -1 if absent).
    • Points: Represents a set of points. Points::indices defines the points. Index::normal_index is always set to an invalid index (-1). Index::texcoord_index is optional (uses -1 if absent).
  6. Understand the Mesh data structure

    master

    A Mesh represents a polyhedral object. Its geometry is defined by two main arrays:

    1. Mesh::indices: A flat array of Index objects. Each Index contains a position_index (mandatory), and optional texcoord_index and normal_index. If an optional attribute is not used, its index is set to -1.
    2. Mesh::num_face_vertices: An array storing the number of vertices for each face (ranging from 3 to 255). This allows the indices array to be flat while still supporting polygons like triangles, quads, or pentagons.

    Additionally, Mesh provides:

    • Mesh::material_ids: Indices into the Materials array for each face.
    • Mesh::smoothing_group_ids: IDs used for normal interpolation.
  7. Install rapidobj via CMake (External/System)

    master

    You can install rapidobj to your system directories using CMake. This allows you to use find_package in your own projects.

    Installation Steps

    1. Configure and generate the build files from the rapidobj directory: cmake -B build .
    2. Install the package (requires administrative access): cd build && sudo make install

    Usage in your CMakeLists.txt

    Once installed, use find_package to link against the rapidobj::rapidobj target. Note that the header is placed in a rapidobj subfolder.

    Note: When using the system installation, the include directive must be #include "rapidobj/rapidobj.hpp".

    # Build and install
    cmake -B build .
    cd build
    sudo make install
    add_executable(my_app my_src.cpp)
    
    find_package(RapidObj REQUIRED)
    
    target_link_libraries(my_app PRIVATE rapidobj::rapidobj)
    // Required include path for system installation
    #include "rapidobj/rapidobj.hpp"
  8. Install rapidobj to a custom directory

    master

    If you want to install rapidobj to a specific folder (e.g., in your home directory) instead of system directories, use the RAPIDOBJ_INCLUDE_DIR and RAPIDOBJ_CMAKE_DIR CMake cache variables.

    Installation Steps

    1. Configure with custom paths: cmake -B build -DRAPIDOBJ_INCLUDE_DIR=${HOME}/local/include -DRAPIDOBJ_CMAKE_DIR=${HOME}/local/cmake .
    2. Install (no sudo required): cd build && make install

    Usage in your CMakeLists.txt

    Since the installation is in a non-standard location, you must provide a hint to find_package using the HINTS argument pointing to your custom CMake directory.

    # Configure for local installation
    cmake -B build -DRAPIDOBJ_INCLUDE_DIR=${HOME}/local/include -DRAPIDOBJ_CMAKE_DIR=${HOME}/local/cmake .
    
    # Install
    cd build
    make install
    add_executable(my_app my_src.cpp)
    
    # Use HINTS to locate the custom installation
    find_package(RapidObj REQUIRED HINTS $ENV{HOME}/local/cmake)
    
    target_link_libraries(my_app PRIVATE rapidobj::rapidobj)
  9. Install rapidobj via Manual Integration

    master

    The simplest way to use rapidobj is to copy the single-header file rapidobj.hpp into your project's include path.

    Requirements

    • A C++ compiler with C++17 support:
      • GCC 8 or higher
      • MSVC 19.14 or higher
      • Clang 7 or higher
    • On Linux, you must link against libpthread. If using GCC 8, you must also link against libstdc++fs because std::filesystem is not part of libstdc++ until GCC 9.
    #include "rapidobj.hpp"
    # Example compilation with g++
    g++ -std=c++17 my_src.cpp -pthread -o my_app
    
    # For GCC 8 specifically:
    g++ -std=c++17 my_src.cpp -pthread -lstdc++fs -o my_app
  10. Embed rapidobj in your project via CMake

    master

    You can include rapidobj directly within your project structure without a system-wide installation. There are two primary methods:

    Method 1: Manual Embedding

    1. Create a thirdparty folder in your project root.
    2. Copy the rapidobj source into that folder.
    3. Use add_subdirectory in your CMakeLists.txt.

    Method 2: FetchContent (Automated)

    Use CMake's FetchContent module to automatically download and integrate rapidobj from its GitHub repository during the configuration step.

    # Method 1: Manual subdirectory
    add_executable(my_app my_src.cpp)
    add_subdirectory(thirdparty/rapidobj)
    target_link_libraries(my_app PRIVATE rapidobj::rapidobj)
    # Method 2: FetchContent
    add_executable(my_app my_src.cpp)
    
    include(FetchContent)
    
    FetchContent_Declare(rapidobj
        GIT_REPOSITORY  https://github.com/guybrush77/rapidobj.git
        GIT_TAG         origin/master)
    
    FetchContent_MakeAvailable(rapidobj)
    
    target_link_libraries(my_app PRIVATE rapidobj::rapidobj)
  11. Count triangles in an .obj file using rapidobj

    master

    To calculate the total number of triangles in an .obj file, use rapidobj::ParseFile to load the data, followed by rapidobj::Triangulate to ensure all faces are converted to triangles. You can then iterate through the result.shapes and sum the size of shape.mesh.num_face_vertices to get the total triangle count.

    #include "rapidobj/rapidobj.hpp"
    
    #include <iostream>
    
    int main()
    {
        rapidobj::Result result = rapidobj::ParseFile("/path/to/my.obj");
    
        if (result.error) {
            std::cout << result.error.code.message() << '\n';
            return EXIT_FAILURE;
        }
    
        bool success = rapidobj::Triangulate(result);
    
        if (!success) {
            std::cout << result.error.code.message() << '\n';
            return EXIT_FAILURE;
        }
    
        size_t num_triangles{};
    
        for (const auto& shape : result.shapes) {
            num_triangles += shape.mesh.num_face_vertices.size();
        }
    
        std::cout << "Shapes:    " << result.shapes.size() << '\n';
        std::cout << "Materials: " << result.materials.size() << '\n';
        std::cout << "Triangles: " << num_triangles << '\n';
    
        return EXIT_SUCCESS;
    }