This guide covers development setup and the C++ library architecture for contributors.
- CMake
- Python 3.9+
- C++17 compatible compiler
- Blender 5.0 or later (for addon development)
- uv (recommended for fast Python tool management)
-
Clone the repository recursively to get all submodules:
git clone --recursive https://github.com/GoodPie/modular_tree
-
Run the build script for your platform:
# macOS ./build_mtree.osx # Linux ./build_mtree.linux # Windows ./build_mtree.bat
-
The script bootstraps vcpkg, installs dependencies (Eigen3), and builds via CMake. A cmake project is generated under
m_tree/build. -
To bundle the Blender addon:
python .github/scripts/setup_addon.py
Output goes to
tmp/as a zip file. -
Install pre-commit hooks (recommended):
uvx pre-commit install
This runs linting and formatting checks automatically on each commit.
cd m_tree/build
cmake ../ -DPYBIND11_PYTHON_VERSION=3.9
cmake --build . --config Releasecd m_tree/build
./binaries/m_tree_testsThe project uses compiler sanitizers to detect memory errors, undefined behavior, and threading issues. These run automatically in CI on every push and pull request.
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.
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_testsOn macOS, use the system leaks tool:
cd m_tree
xcrun leaks --atExit -- ./binaries/m_tree_testsLook for "0 leaks for 0 total leaked bytes" in the output.
The library follows a pipeline pattern: TreeFunctions -> Tree -> Mesher -> Mesh
TreeFunctions are modular operations that modify tree structure. They form a tree of operations that execute recursively.
TreeFunction- Abstract base class withexecute()method and child composition viaadd_child()TrunkFunction- Creates the main trunkBranchFunction- Adds branches to parent nodes
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 trianglesMulti-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 branchesMultiple 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);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
- Eigen3 - Linear algebra (installed via vcpkg)
- pybind11 - Python bindings (git submodule)
- vcpkg - Package manager (git submodule)