Skip to content

Latest commit

 

History

History
191 lines (139 loc) · 5.68 KB

File metadata and controls

191 lines (139 loc) · 5.68 KB

Contributing to Mtree

This guide covers development setup and the C++ library architecture for contributors.

Prerequisites

  • CMake
  • Python 3.9+
  • C++17 compatible compiler
  • Blender 5.0 or later (for addon development)
  • uv (recommended for fast Python tool management)

Getting Started

  1. Clone the repository recursively to get all submodules:

    git clone --recursive https://github.com/GoodPie/modular_tree
  2. Run the build script for your platform:

    # macOS
    ./build_mtree.osx
    
    # Linux
    ./build_mtree.linux
    
    # Windows
    ./build_mtree.bat
  3. The script bootstraps vcpkg, installs dependencies (Eigen3), and builds via CMake. A cmake project is generated under m_tree/build.

  4. To bundle the Blender addon:

    python .github/scripts/setup_addon.py

    Output goes to tmp/ as a zip file.

  5. Install pre-commit hooks (recommended):

    uvx pre-commit install

    This runs linting and formatting checks automatically on each commit.

Manual Build

cd m_tree/build
cmake ../ -DPYBIND11_PYTHON_VERSION=3.9
cmake --build . --config Release

Running Tests

cd m_tree/build
./binaries/m_tree_tests

Memory Safety Testing

The project uses compiler sanitizers to detect memory errors, undefined behavior, and threading issues. These run automatically in CI on every push and pull request.

CI Workflow

The sanitizer workflow (.github/workflows/sanitizers.yml) runs three sanitizers on Linux:

Sanitizer Detects
ASan (AddressSanitizer) Buffer overflows, use-after-free, memory leaks
UBSan (UndefinedBehaviorSanitizer) Signed overflow, null pointer dereference, misaligned access
TSan (ThreadSanitizer) Data races, deadlocks

A separate macOS job uses xcrun leaks for leak detection on ARM.

Running Sanitizers Locally

Build with sanitizer flags enabled:

cd m_tree/build

# AddressSanitizer (recommended first check)
cmake ../ -DCMAKE_BUILD_TYPE=Debug \
  -DCMAKE_CXX_FLAGS="-fsanitize=address -fno-omit-frame-pointer -g" \
  -DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address"
cmake --build . --config Debug

# Run with enhanced options
ASAN_OPTIONS=detect_stack_use_after_return=1:check_initialization_order=1 \
  ./binaries/m_tree_tests

# UndefinedBehaviorSanitizer
cmake ../ -DCMAKE_BUILD_TYPE=Debug \
  -DCMAKE_CXX_FLAGS="-fsanitize=undefined -fno-sanitize-recover=all -g" \
  -DCMAKE_EXE_LINKER_FLAGS="-fsanitize=undefined"
cmake --build . --config Debug
UBSAN_OPTIONS=print_stacktrace=1 ./binaries/m_tree_tests

macOS Leak Detection

On macOS, use the system leaks tool:

cd m_tree
xcrun leaks --atExit -- ./binaries/m_tree_tests

Look for "0 leaks for 0 total leaked bytes" in the output.

Architecture

The library follows a pipeline pattern: TreeFunctions -> Tree -> Mesher -> Mesh

Tree Functions

TreeFunctions are modular operations that modify tree structure. They form a tree of operations that execute recursively.

  • TreeFunction - Abstract base class with execute() method and child composition via add_child()
  • TrunkFunction - Creates the main trunk
  • BranchFunction - Adds branches to parent nodes

C++ Library Usage

A Tree is generated by executing a succession of TreeFunction objects. When executed, a TreeFunction modifies the structure of the tree, then calls children functions recursively.

Basic tree with trunk and branches:

auto trunk = std::make_shared<TrunkFunction>();
auto branches = std::make_shared<BranchFunction>();
trunk->add_child(branches); // branches are added on top of the trunk
Tree tree(trunk);
tree.execute_functions(); // The tree structure is generated
ManifoldMesher mesher; // Converts a tree into a 3d mesh with smooth topology
mesher.radial_resolution = 32;
Mesh tree_mesh = mesher.mesh_tree(tree); // resulting mesh contains vertices and triangles

Multi-level branching:

auto branches_primary = std::make_shared<BranchFunction>();
auto branches_secondary = std::make_shared<BranchFunction>();
branches_primary.add_child(branches_secondary); // secondary branches grow on primary branches

Multiple branch types (e.g., healthy and dead branches):

auto trunk = std::make_shared<TrunkFunction>();
auto branches_healthy = std::make_shared<BranchFunction>();
auto branches_dead = std::make_shared<BranchFunction>();
branches_dead.length = RandomProperty{.1f, 1f}; // dead branches: 10cm to 1m
branches_dead.start_radius = ConstantProperty{.05f}; // 5% of parent radius

trunk->add_child(branches_healthy); // both sets grow on trunk
trunk->add_child(branches_dead);
Tree tree(trunk);

Project Structure

m_tree/
├── source/           # C++ core library
│   ├── tree_functions/   # TreeFunction implementations
│   ├── tree/             # Tree, Stem, Node data structures
│   ├── meshers/          # Mesh generation (ManifoldMesher, BasicMesher)
│   └── mesh/             # Output geometry
├── python_bindings/  # pybind11 module
└── dependencies/     # vcpkg and pybind11 submodules

python_classes/       # Blender addon code
├── nodes/            # Node editor integration
├── operators.py      # Blender operators
└── resources/        # Node groups and utilities

Dependencies

  • Eigen3 - Linear algebra (installed via vcpkg)
  • pybind11 - Python bindings (git submodule)
  • vcpkg - Package manager (git submodule)

License

  • Blender addon (python_classes/, __init__.py): GPLv3
  • C++ library (m_tree/source/): MIT