{"id":23434912,"url":"https://github.com/cxreiff/bevy_ratatui_camera","last_synced_at":"2025-05-16T04:03:44.047Z","repository":{"id":241296023,"uuid":"785633188","full_name":"cxreiff/bevy_ratatui_camera","owner":"cxreiff","description":"A bevy plugin for rendering your bevy app to the terminal using ratatui.","archived":false,"fork":false,"pushed_at":"2025-05-12T23:39:57.000Z","size":399,"stargazers_count":246,"open_issues_count":1,"forks_count":4,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-05-13T00:29:26.517Z","etag":null,"topics":["ascii","bevy","ratatui","rust","tui"],"latest_commit_sha":null,"homepage":"https://crates.io/crates/bevy_ratatui_camera","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cxreiff.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-04-12T09:38:16.000Z","updated_at":"2025-05-12T23:36:18.000Z","dependencies_parsed_at":"2024-06-06T00:29:57.531Z","dependency_job_id":"787bfe44-c839-4dc1-97a2-f020e5807fc6","html_url":"https://github.com/cxreiff/bevy_ratatui_camera","commit_stats":null,"previous_names":["cxreiff/bevy_rat","cxreiff/bevy_ratatui_render","cxreiff/bevy_ratatui_camera"],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cxreiff%2Fbevy_ratatui_camera","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cxreiff%2Fbevy_ratatui_camera/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cxreiff%2Fbevy_ratatui_camera/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cxreiff%2Fbevy_ratatui_camera/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cxreiff","download_url":"https://codeload.github.com/cxreiff/bevy_ratatui_camera/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253851079,"owners_count":21973674,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ascii","bevy","ratatui","rust","tui"],"created_at":"2024-12-23T12:23:45.308Z","updated_at":"2025-05-16T04:03:44.041Z","avatar_url":"https://github.com/cxreiff.png","language":"Rust","funding_links":[],"categories":["Rust","📦 Libraries","Cameras"],"sub_categories":["🏗️ Frameworks"],"readme":"# bevy_ratatui_camera\n\n[![Crate Badge]][Crate]\n[![Docs Badge]][Docs]\n[![Downloads Badge]][Downloads]\n[![License Badge]][License]\n\n\u003cp float=\"left\"\u003e\n\u003cimg src=\"https://assets.cxreiff.com/github/cube.gif\" width=\"30%\" alt=\"cube\"\u003e\n\u003cimg src=\"https://assets.cxreiff.com/github/foxes.gif\" width=\"30%\" alt=\"foxes\"\u003e\n\u003cimg src=\"https://assets.cxreiff.com/github/sponza.gif\" width=\"30%\" alt=\"sponza test scene\"\u003e\n\u003cp\u003e\n\nBevy inside the terminal!\n\nUses bevy headless rendering,\n[ratatui](https://github.com/ratatui-org/ratatui), and\n[bevy_ratatui](https://github.com/cxreiff/bevy_ratatui) to print your bevy\napplication's rendered frames to the terminal.\n\n\u003e [!IMPORTANT]  \n\u003e This crate was renamed from `bevy_ratatui_render` to `bevy_ratatui_camera`.\n\n## getting started\n\n`cargo add bevy_ratatui_camera bevy_ratatui ratatui crossterm`\n\n```rust\nfn main() {\n    App::new()\n        .add_plugins((\n            // disable WinitPlugin as it panics in environments without a display server.\n            // disable LogPlugin as it interferes with terminal output.\n            DefaultPlugins.build()\n                .disable::\u003cWinitPlugin\u003e()\n                .disable::\u003cLogPlugin\u003e(),\n\n            // create windowless loop and set its frame rate.\n            ScheduleRunnerPlugin::run_loop(Duration::from_secs_f64(1. / 60.)),\n\n            // set up the Ratatui context and forward terminal input events.\n            RatatuiPlugins::default(),\n\n            // add the ratatui camera plugin.\n            RatatuiCameraPlugin,\n        ))\n        .add_systems(Startup, setup_scene_system)\n        .add_systems(PostUpdate, draw_scene_system.map(error))\n        .run();\n}\n\n// add RatatuiCamera to your scene's camera.\nfn setup_scene_system(mut commands: Commands) {\n    commands.spawn((\n        Camera3d::default(),\n        RatatuiCamera::default(),\n    ));\n}\n\n// a RatatuiCameraWidget component will be available in your camera entity.\nfn draw_scene_system(\n    mut ratatui: ResMut\u003cRatatuiContext\u003e,\n    mut camera_widget: Single\u003c\u0026mut RatatuiCameraWidget\u003e,\n) -\u003e Result {\n    ratatui.draw(|frame| {\n        camera_widget.render(frame.area(), frame.buffer_mut());\n    })?;\n\n    Ok(())\n}\n```\n\nAs shown above, when `RatatuiCameraPlugin` is added to your application, any\nbevy camera entities that you add a `RatatuiCamera` component to, will have\na `RatatuiCameraWidget` inserted that you can query for. Each\n`RatatuiCameraWidget` is a ratatui widget that when drawn will print the most\nrecent frame rendered by the associated bevy camera, as unicode characters.\n\nUse [bevy_ratatui](https://github.com/cxreiff/bevy_ratatui) for setting ratatui\nup and receiving terminal events (keyboard, focus, mouse, paste, resize) inside\nbevy.\n\n## strategies\n\nThe method by which the rendered image is converted into unicode characters\ndepends on the `RatatuiCameraStrategy` that you choose. Insert a variant of the\ncomponent alongside the `RatatuiCamera` to change the behavior from the\ndefault. Refer to the `RatatuiCameraStrategy` documentation for descriptions of\neach variant.\n\nFor example, to use the \"Luminance\" strategy:\n\n```rust\ncommands.spawn((\n    Camera3d::default(),\n    RatatuiCamera::default(),\n    RatatuiCameraStrategy::Luminance(LuminanceConfig::default()),\n));\n```\n\n## autoresize\n\nBy default, the dimensions of the texture the camera renders to will be resized\neach frame to the dimensions and aspect ratio of the buffer area it was last\nrendered within, to gracefully handle any changes in the window size or your\nratatui layout. If you would like to keep a fixed size and aspect ratio\ninstead, set `dimensions`, set the `autoresize` attribute inside\n`RatatuiCamera` to false (or use `RatatuiCamera::new(width: u32, height: u32)`\nto do both). Your supplied width and height will be used to create the render\ntexture and when rendered to the ratatui buffer with\n`RatatuiCameraWidget::render(...)` it will retain its aspect ratio.\n\n```rust\ncommands.spawn((\n    RatatuiCamera::new(800, 600),\n));\n```\n\n## edge detection\n\nWhen using the a 3d camera, you can optionally insert\na `RatatuiCameraEdgeDetection` component into your camera in order to add an\nedge detection step in the render graph. When printing to the ratatui buffer,\nspecial characters and an override color can be used based on the detected\nedges and their directions. This can be useful for certain visual effects, and\ndistinguishing detail when the text rendering causes edges to blend together.\n\nSet `edge_characters` to `EdgeCharacters::Single(..)` for a single dedicated\nedge character, or set it to `EdgeCharacters::Directional { .. }` to set\ndifferent characters based on the \"direction\" of the edge, for example using\n'―', '|', '/', and '\\\\' characters to draw edge \"lines\". Detecting the correct\nedge direction is a bit fuzzy, so you may need to experiment with\ncolor/depth/normal thresholds for good results.\n\n```rust\nRatatuiCameraEdgeDetection {\n    thickness: 1.4,\n    edge_characters: Self::Directional {\n        vertical: '|',\n        horizontal: '―',\n        forward_diagonal: '/',\n        backward_diagonal: '\\\\',\n    },\n    edge_color: Some(ratatui::style::Color::Magenta),\n    ..default()\n}\n```\n\n## depth detection\n\nSometimes you render using multiple widgets that each represent something in\nthe same world-space, like multiple camera widgets with different render\nstrategies, or a camera widget and text labels that hover over entities in your\nBevy world. However, because each widget is drawn in order, the last widgets to\nbe drawn always cover those previous, even if it seems like they should be\ncovered by some other world object (for example, if you draw a world with\ncharacters, and then draw nametags over the characters, the nametags may be\nvisible even if the character is standing behind a wall).\n\nBy recording a \"closest depth\" associated with each terminal buffer cell, and\nskipping draws when the new cell to be drawn has a more distant depth than the\ndepth already recorded, you can achieve depth occlusion even when mixing\nmultiple kinds of widgets that draw separately. This library contains a couple\nof utilities for this purpose.\n\nFirst, add `RatatuiCameraDepthDetection` to your ratatui camera entity, and the depth\nprepass will be copied back from the GPU alongside your camera's render image:\n\n```rust\n// setup system\ncommands.spawn((\n    RatatuiCamera::default(),\n    RatatuiCameraDepthDetection,\n));\n```\n\nThen, when drawing, use `RatatuiCameraWidget::new_depth_buffer()` to initialize\na `RatatuiCameraDepthBuffer` that you can pass into camera widget render calls\n(or other depth-aware widget render calls) to compare depths against and update\nwhile drawing:\n\n```rust\n// draw system\nlet depth_buffer = \u0026mut widget.new_depth_buffer(area);\n\nwidget.render(area, frame.buffer_mut(), depth_buffer);\n\nwidget.render_overlay_with_depth(area, frame.buffer_mut(), \u0026custom_widget, depth_buffer);\n```\n\nTo achieve this, `RatatuiCameraWidget` implements both ratatui's `Widget` and\n`StatefulWidget` traits, using the stateful version for the depth-aware\nrendering. Because of this, if you have both traits imported, you may need to\nresolve ambiguous trait resolution with fully qualified syntax:\n\n```rust\n\u003cwidget as StatefulWidget\u003e::render(...);\n```\n\n## multiple cameras\n\n`RatatuiCamera` can be added to multiple camera entities. To access the correct\nrender, use marker components on your cameras to use when querying\n`RatatuiCameraWidget`.\n\nIf you need multiple cameras to render to one image, create one `RatatuiCamera`\nmain camera that will define the dimensions, strategy, etcetera, and then create\nadditional `RatatuiSubcamera` cameras that point to the main camera.\n\n## supported terminals\n\nBy default, printing to terminal relies on the terminal supporting 24-bit\ncolor. I've personally tested and confirmed that the following terminals\ndisplay correctly:\n\n- Alacritty\n- Kitty\n- iTerm\n- WezTerm\n- Rio\n- Ghostty\n\n...but any terminal with 24-bit color support should work fine, if its\nperformance is adequate.\n\nYou can also use the `color_support` option in `RatatuiCameraStrategy` to\nconvert colors to a more limited ANSI sets, in order to target terminals with\nmore limited color support, but results may be inconsistent as ANSI colors can\ndepend on the terminal and on user configuration.\n\n## compatibility\n\n| bevy  | bevy_ratatui_camera |\n|-------|---------------------|\n| 0.16  | 0.14                |\n| 0.15  | 0.12                |\n| 0.14  | 0.6                 |\n\n[Crate]: https://crates.io/crates/bevy_ratatui_camera\n[Crate Badge]: https://img.shields.io/crates/v/bevy_ratatui_camera\n[Docs]: https://docs.rs/bevy_ratatui_camera\n[Docs Badge]: https://img.shields.io/badge/docs-bevy_ratatui_camera-886666\n[Downloads]: https://crates.io/crates/bevy_ratatui_camera\n[Downloads Badge]: https://img.shields.io/crates/d/bevy_ratatui_camera.svg\n[License]: ./LICENSE-MIT\n[License Badge]: https://img.shields.io/crates/l/bevy_ratatui_camera\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcxreiff%2Fbevy_ratatui_camera","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcxreiff%2Fbevy_ratatui_camera","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcxreiff%2Fbevy_ratatui_camera/lists"}