EZ-Tree

repository·main·Indexed 23 days ago

https://github.com/dgreenheck/ez-tree

A procedural tree generator for Three.js applications and a standalone web app for creating and exporting trees as .PNG or .GLB files. It features a Tree class for dynamic generation, support for Levels of Detail (LODs) to optimize performance in large scenes, and predefined configurations via TreePreset. Users can customize bark, branch, and leaf parameters, or extract raw BufferGeometry for custom instancing systems.

Tokens
4.9K
Snippets
14
Records
35
Agent score
80%

What's inside @dgreenheck/ez-tree

  1. Texture Attribution and Licensing

    main

    The textures used in EZ-Tree are subject to different licenses depending on their type:

    • Bark Textures (located in bark/): Sourced from ambientcg.com and released under the Creative Commons CC0 1.0 Universal license (public domain). While attribution is not legally required, it is provided as a courtesy.
    • Leaf Textures (located in leaves/): Bundled with the EZ-Tree demo app and licensed under the project's own license (refer to the repository's main LICENSE file).
  2. How Levels of Detail (LODs) work in EZ-Tree

    main

    For performance in scenes with many trees, use generateLODs() instead of generate(). This builds the tree at multiple levels of detail hosted in a THREE.LOD object inside the tree group. The renderer automatically switches levels based on camera distance.

    All levels share the same skeleton (maintaining silhouette consistency) and use the same bark and leaf materials, allowing tree.update(time) to animate wind across all levels simultaneously.

    Note: Calling generate() after generateLODs() will tear down the LOD system and restore a single full-detail mesh pair. If exporting to GLB, a tree generated with generateLODs() will include all LOD levels.

    const tree = new Tree();
    tree.loadPreset('Ash Medium');
    tree.generateLODs(); // instead of generate()
    scene.add(tree);
  3. How Tree LODs and geometry generation work

    main

    The Tree class uses a two-step process to create its visual representation:

    1. Skeleton Generation: The #generateSkeleton method uses an internal RNG (seeded by options.seed) to grow a hierarchical structure of Branch objects. This step determines the exact position, orientation, and radius of every branch section and leaf placement. Because this is done once, you can run multiple meshing passes (for different LODs) against the same skeleton without changing the tree's shape.

    2. Meshing: The #meshSkeleton method converts the mathematical skeleton into THREE.BufferGeometry. It uses the LODDetail parameters to skip sections or segments, reducing the triangle count for distant views.

    LOD Switching: When generateLODs is used, the tree is wrapped in a THREE.LOD object. The renderer automatically selects the appropriate mesh group based on the camera's distance. All levels share the same bark and leaf materials, allowing the update() method to apply wind animation uniformly across all levels.

  4. Run the EZ-Tree app using Docker Compose

    main

    You can run the EZ-Tree application in a containerized environment using Docker Compose. The tree-gen-app service maps port 5173 to your host, allowing you to access the application locally.

    To enable live development, the configuration mounts the local ./src and ./public directories into the container. The application is started with NODE_ENV=development and configured to listen on 0.0.0.0 to ensure the host can reach the service.

  5. Basic usage of the Tree class

    main

    To generate a tree, instantiate the Tree class, configure its options object, and call generate(). Note that you must call generate() every time you change parameters to update the geometry. The resulting tree object can be added directly to a Three.js scene.

    // Create new instance
    const tree = new Tree();
    
    // Set parameters
    tree.options.seed = 12345;
    tree.options.trunk.length = 20;
    tree.options.branch.levels = 3;
    
    // Generate tree and add to your Three.js scene
    tree.generate();
    scene.add(tree);
  6. Configure custom LOD levels

    main

    You can pass an array of custom LOD configurations to generateLODs(). Each object defines a distance at which the level triggers and a detail object to control mesh reduction.

    tree.generateLODs([
      { distance: 0, detail: {} }, // full detail
      {
        distance: 80,
        hysteresis: 0.05,
        detail: {
          sectionStride: 3,    // sample every 3rd ring along each branch
          segmentFactor: 0.75, // reduce radial segments to 75% (min 3)
          leafStride: 2,       // keep every 2nd leaf...
          leafScale: 1.4,      // ...enlarged to preserve canopy coverage
          billboard: 'single', // drop the second crossed leaf quad
        },
      },
    ]);
  7. Generate raw geometry for custom LOD or instancing

    main
    If you are implementing your own LOD or instancing system, use tree.createGeometry(detail). This returns raw { branches, leaves } BufferGeometry pairs at the specified detail level without modifying the tree's existing meshes.
  8. Configure the Trellis class

    main

    The Trellis constructor accepts an options object to define the grid's physical properties and appearance.

    Required/Supported keys:

    • color: The color of the trellis cylinders (e.g., hex value).
    • cylinderRadius: The radius of the cylinders forming the grid.
    • width: The total width of the trellis grid along the X axis.
    • height: The total height of the trellis grid along the Y axis.
    • spacing: The distance between horizontal and vertical lines.
    • position: An object { x, y, z } defining the base position of the trellis.
  9. Reference: Branch Parameters

    main

    The branch object defines trunk and branch structure:

    • levels: Number of recursive branch levels (0 is just a trunk).
    • angle: Array of angles (in degrees) for child branches relative to parents per level.
    • children: Number of child branches at each level (indexed by level).
    • force: Directional growth force: { direction: { x, y, z }, strength: number }.
    • gnarliness: Array of twist/curl values per level.
    • length: Object mapping levels to branch lengths.
    • radius: Array of thickness values per level.
    • sections: Number of segments along the length (resolution).
    • segments: Number of radial segments (smoothness).
    • start: Fraction (0 to 1) along parent branch where children start.
    • taper: Reduction in radius from base to tip (0 to 1).
    • twist: Amount of twisting per level.