bevy_vector_shapes

repository·main·Indexed 19 days ago

https://github.com/james-j-obrien/bevy_vector_shapes

A library for ergonomically creating instanced vector shapes within the Bevy game engine. It supports 2D and 3D environments with features including anti-aliasing, billboarding, a canvas API, and built-in shapes such as lines, rectangles, circles, arcs, and regular polygons. The library provides both immediate and retained rendering modes, supports WASM, and includes a flexible ShapeConfig system for controlling visual and spatial properties.

Tokens
9.7K
Snippets
40
Records
50
Agent score
63%

What's inside bevy_vector_shapes

  1. Core features of Bevy Vector Shapes

    main

    Bevy Vector Shapes provides several capabilities for instanced vector rendering:

    • Built-in Shapes: Lines, rectangles, circles, arcs, and regular polygons.
    • Rendering Modes: Supports both immediate and retained modes.
    • Advanced Rendering: Supports 2D/3D pipelines, transparency, alpha modes, render layers, and bloom.
    • Instancing: Shapes of the same type and rendering configuration are fully instanced together for performance.
    • Visual Quality: Includes local anti-aliasing for smoother edges and optional billboarding to ensure shapes always face the camera.
    • Canvas API: Ability to render shapes to a texture and draw textures onto shapes.
    • Extensibility: Traits are provided to allow implementation of custom shape types.
    • Platform Support: Compiles to WASM for browser execution.
  2. Install and setup Bevy Vector Shapes

    main

    To use Bevy Vector Shapes, add it to your Cargo.toml and ensure you are using a compatible version of Bevy.

    When initializing your Bevy App, you must add one of the following plugins depending on your camera setup:

    • Shape2dPlugin::default(): Use this if you are working with 2D cameras.
    • ShapePlugin::default(): Use this if you need support for both 3D and 2D cameras.

    Note: This library is in early development and may encounter issues.

    use bevy::prelude::*;
    use bevy_vector_shapes::prelude::*;
    
    fn main() {
        App::new()
            .add_plugins(DefaultPlugins)
            // Use Shape2dPlugin for 2D cameras
            // Use ShapePlugin for both 3D and 2D cameras
            .add_plugins(Shape2dPlugin::default())
            .add_systems(Startup, setup)
            .add_systems(Update, draw)
            .run();
    }
    
    fn setup(mut commands: Commands) {
        commands.spawn(Camera2d);
    }
    
    fn draw(mut painter: ShapePainter) {
        painter.circle(100.0);
    }
  3. Configure Canvas rendering behavior with CanvasMode

    main

    The CanvasMode enum determines how and when a canvas is cleared and redrawn. This is useful for optimizing performance by avoiding unnecessary redraws.

    • CanvasMode::Continuous (Default): The canvas is always cleared and redrawn every frame. The camera is always active.
    • CanvasMode::Persistent: The canvas is always drawn, but it is only cleared when Canvas::redraw() is called. If redraw() is not called, the camera's clear color is set to None to prevent clearing the existing content.
    • CanvasMode::OnDemand: The canvas is only drawn or cleared when Canvas::redraw() is called. The camera's activity is tied to the redraw state.
    // Example of using Persistent mode to avoid clearing every frame
    let config = CanvasConfig {
        mode: CanvasMode::Persistent,
        ..CanvasConfig::new(800, 600)
    };
  4. Install and setup the PainterPlugin

    main

    To use bevy_vector_shapes for drawing shapes, add the PainterPlugin to your Bevy App. This plugin initializes the necessary resources (like ShapeStorage) and sets up systems for managing Canvas components and shape rendering. It handles clearing shape storage every frame and updating canvases in the PostUpdate schedule before camera updates.

    use bevy::prelude::*;
    use bevy_vector_shapes::PainterPlugin;
    
    fn main() {
        App::new()
            .add_plugins(DefaultPlugins)
            .add_plugins(PainterPlugin)
            .run();
    }
  5. Initialize the shape rendering pipeline

    main

    To use bevy_vector_shapes, you must initialize the core rendering systems and shaders. This is typically done by adding the ShapeRenderPlugin to your Bevy App and calling its finish() method. This step loads all internal WGSL shaders (core, constants, and specific shape shaders like DISC_HANDLE, LINE_HANDLE, etc.) and sets up the base rendering pipelines.

    Note: The finish() method is a manual step required after adding the plugin to ensure internal assets and pipelines are correctly registered.

    app.add_plugins(ShapeRenderPlugin)
       .finish();
  6. Configure Canvas settings with CanvasConfig

    main

    Use CanvasConfig to define the properties of a new canvas before spawning it.

    Key fields:

    • width / height: Dimensions of the target texture in pixels.
    • mode: The CanvasMode (Continuous, Persistent, or OnDemand).
    • clear_color: The ClearColorConfig used when the canvas is cleared.
    • order: The camera order (analogous to Bevy's Camera order).
    • sampler: The ImageSampler used for the target texture.
    • hdr: Whether to enable High Dynamic Range (HDR) for the texture and camera.
    // Create a default config with specific dimensions
    let config = CanvasConfig::new(1920, 1080);
  7. Spawn shape children from non-shape entities with BuildShapeChildren

    main

    If you need to spawn a hierarchy of shapes under an entity that does not have a ShapeConfig (a standard Bevy entity), use the BuildShapeChildren extension trait.

    By calling with_shape_children on EntityCommands, you can pass in a &ShapeConfig which will then be used by the ShapeChildBuilder to configure all shapes spawned within that closure.

    // 'commands' is standard Bevy Commands
    // 'my_config' is a pre-defined ShapeConfig
    commands.entity(some_standard_entity).with_shape_children(&my_config, |builder| {
        // All shapes spawned here will use 'my_config'
        builder.spawn_shape(MyShapeBundle::default());
    });
  8. Draw shapes with children using ShapePainter::with_children

    main

    While ShapePainter uses an event-based system rather than an entity-component hierarchy, you can simulate parent-child relationships using with_children. This method takes a closure that receives a new ShapePainter instance. The configuration used inside the closure is cloned from the parent, and once the closure finishes, the parent's configuration is restored to its original state.

    painter.with_children(|child_painter| {
        // This shape and its children share the same config context
        child_painter.send(ChildShapeData { .. });
    });
  9. Spawn shape hierarchies with ShapeEntityCommands

    main

    When you have a shape entity and want to spawn child shapes that inherit its configuration, use ShapeEntityCommands::with_children. This method provides a ShapeChildBuilder which allows you to spawn new shapes that automatically inherit the parent's ShapeConfig (minus the transform) and are correctly parented to the entity.

    ShapeEntityCommands implements Deref and DerefMut to EntityCommands, so you can use standard Bevy entity commands directly on the returned object.

    // Assuming 'shape_entity_commands' is a ShapeEntityCommands instance
    shape_entity_commands.with_children(|child_builder| {
        // Use child_builder to spawn more shapes
        child_builder.spawn_shape(MyShapeBundle::default());
    });
  10. Trigger a canvas redraw

    main

    When using CanvasMode::Persistent or CanvasMode::OnDemand, you must manually signal that the canvas needs to be redrawn by calling Canvas::redraw(). This sets an internal flag that the update_canvases system uses to manage camera activity and clear colors.

    fn redraw_system(mut query: Query<&mut Canvas>) {
        for mut canvas in query.iter_mut() {
            // Signal that this canvas needs to be redrawn this frame
            canvas.redraw();
        }
    }