diff --git a/kayak_font/examples/bevy.rs b/kayak_font/examples/bevy.rs deleted file mode 100644 index ac6413002da01dd77fef9096436c433bd93b8026..0000000000000000000000000000000000000000 --- a/kayak_font/examples/bevy.rs +++ /dev/null @@ -1,137 +0,0 @@ -use bevy::{ - math::Vec2, - prelude::{ - App as BevyApp, AssetServer, Camera2dBundle, Commands, Component, Handle, Input, KeyCode, - PluginGroup, Query, Res, ResMut, Sprite, SpriteBundle, Transform, With, Without, - }, - render::color::Color, - window::{WindowDescriptor, WindowPlugin}, - DefaultPlugins, -}; - -use kayak_font::{bevy::KayakFontPlugin, Alignment, KayakFont}; -use renderer::{FontRenderPlugin, Text}; - -mod renderer; - -const FONT_SIZE: f32 = 24.0; -const INITIAL_SIZE: Vec2 = Vec2::from_array([400.0, 300.0]); -const INITIAL_POS: Vec2 = Vec2::from_array([-200.0, 0.0]); -const INSTRUCTIONS: &str = - "Press 'A' and 'D' to shrink and grow the text box.\nPress \"Space\" to cycle text alignment."; - -#[derive(Component)] -struct Instructions; - -fn startup(mut commands: Commands, asset_server: Res<AssetServer>) { - commands.spawn(Camera2dBundle::default()); - - let font_handle: Handle<KayakFont> = asset_server.load("roboto.kttf"); - // let font_handle: Handle<KayakFont> = asset_server.load("roboto.kayak_font"); - - commands - .spawn(Text { - horz_alignment: Alignment::Start, - color: Color::WHITE, - content: "Hello World! This text should wrap because it's kinda-super-long. How cool is that?!\nHere's a new line.\n\tHere's a tab.".into(), - font_size: FONT_SIZE, - line_height: FONT_SIZE * 1.2, // Firefox method of calculating default line heights see: https://developer.mozilla.org/en-US/docs/Web/CSS/line-height - position: INITIAL_POS, - size: INITIAL_SIZE, - }) - .insert(font_handle.clone()); - - commands.spawn(SpriteBundle { - sprite: Sprite { - color: Color::DARK_GRAY, - custom_size: Some(INITIAL_SIZE), - ..Default::default() - }, - transform: Transform::from_xyz( - (INITIAL_SIZE.x / 2.0) + INITIAL_POS.x, - (-INITIAL_SIZE.y / 4.0) - 20.0, - -0.05, - ), - ..Default::default() - }); - - commands.spawn(( - Text { - horz_alignment: Alignment::Middle, - color: Color::WHITE, - content: INSTRUCTIONS.into(), - font_size: 32.0, - line_height: 32.0 * 1.2, // Firefox method of calculating default line heights see: https://developer.mozilla.org/en-US/docs/Web/CSS/line-height - position: Vec2::new(-360.0, 250.0), - size: Vec2::new(720.0, 200.0), - }, - Instructions, - font_handle.clone(), - )); -} - -fn control_text( - keyboard_input: ResMut<Input<KeyCode>>, - mut text_box: Query<&mut Text, Without<Instructions>>, - mut instructions: Query<&mut Text, With<Instructions>>, - mut bg: Query<&mut Sprite>, -) { - let speed = - if keyboard_input.pressed(KeyCode::LShift) || keyboard_input.pressed(KeyCode::RShift) { - 2.5 - } else { - 1.0 - }; - - if keyboard_input.just_pressed(KeyCode::Space) { - for mut text in text_box.iter_mut() { - let next = match text.horz_alignment { - Alignment::Start => Alignment::Middle, - Alignment::Middle => Alignment::End, - Alignment::End => Alignment::Start, - }; - text.horz_alignment = next; - } - } - - let speed = if keyboard_input.pressed(KeyCode::D) { - speed - } else if keyboard_input.pressed(KeyCode::A) { - -speed - } else { - return; - }; - - for mut text in text_box.iter_mut() { - text.size.x += speed; - text.position.x -= speed / 2.0; - - let mut instructions = instructions.single_mut(); - instructions.content = String::from(INSTRUCTIONS); - instructions - .content - .push_str(&format!("\nSize: {}", text.size.x)); - } - - for mut sprite in bg.iter_mut() { - sprite.custom_size.as_mut().unwrap().x += speed; - } -} - -fn main() { - BevyApp::new() - .add_plugins(DefaultPlugins.set(WindowPlugin { - window: WindowDescriptor { - width: 1270.0, - height: 720.0, - title: String::from("UI Example"), - ..Default::default() - }, - ..Default::default() - })) - .add_plugin(KayakFontPlugin) - .add_plugin(FontRenderPlugin) - .add_startup_system(startup) - .add_system(control_text) - .run(); -} diff --git a/kayak_font/examples/renderer/extract.rs b/kayak_font/examples/renderer/extract.rs deleted file mode 100644 index 8c1667c56215f3da416c231fe8775b053ea50595..0000000000000000000000000000000000000000 --- a/kayak_font/examples/renderer/extract.rs +++ /dev/null @@ -1,57 +0,0 @@ -use bevy::{ - math::Vec2, - prelude::{Assets, Commands, Handle, Query, Rect, Res}, - render::Extract, -}; -use kayak_font::{KayakFont, TextProperties}; - -use super::{ - pipeline::{ExtractCharBundle, ExtractedChar}, - Text, -}; - -pub fn extract( - mut commands: Commands, - fonts: Extract<Res<Assets<KayakFont>>>, - texts: Extract<Query<(&Text, &Handle<KayakFont>)>>, -) { - let mut extracted_texts = Vec::new(); - - for (text, font_handle) in texts.iter() { - if let Some(font) = fonts.get(font_handle) { - let properties = TextProperties { - font_size: text.font_size, - line_height: text.line_height, - max_size: (text.size.x, text.size.y), - alignment: text.horz_alignment, - ..Default::default() - }; - - let text_layout = font.measure(&text.content, properties); - - for glyph_rect in text_layout.glyphs() { - let mut position = Vec2::from(glyph_rect.position); - position.y *= -1.0; - position += text.position; - - let size = Vec2::from(glyph_rect.size); - - extracted_texts.push(ExtractCharBundle { - extracted_quad: ExtractedChar { - font_handle: Some(font_handle.clone()), - rect: Rect { - min: position, - max: position + size, - }, - color: text.color, - vertex_index: 0, - char_id: font.get_char_id(glyph_rect.content).unwrap(), - z_index: 0.0, - }, - }); - } - } - } - - commands.spawn_batch(extracted_texts); -} diff --git a/kayak_font/examples/renderer/mod.rs b/kayak_font/examples/renderer/mod.rs deleted file mode 100644 index 10ccaff55844b4b5635db7eb7c2703f4f40c676c..0000000000000000000000000000000000000000 --- a/kayak_font/examples/renderer/mod.rs +++ /dev/null @@ -1,63 +0,0 @@ -use bevy::{ - core_pipeline::core_2d::Transparent2d, - prelude::{Assets, HandleUntyped, Plugin, Res, ResMut}, - reflect::TypeUuid, - render::{ - render_asset::RenderAssets, - render_phase::DrawFunctions, - render_resource::Shader, - renderer::{RenderDevice, RenderQueue}, - texture::Image, - RenderApp, RenderStage, - }, -}; -use kayak_font::bevy::FontTextureCache; - -use self::pipeline::{DrawUI, FontPipeline, QuadMeta}; - -mod extract; -pub mod pipeline; -mod text; - -pub use text::*; - -pub const FONT_SHADER_HANDLE: HandleUntyped = - HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 7604018236855288450); - -pub struct FontRenderPlugin; - -impl Plugin for FontRenderPlugin { - fn build(&self, app: &mut bevy::prelude::App) { - let mut shaders = app.world.get_resource_mut::<Assets<Shader>>().unwrap(); - let unified_shader = Shader::from_wgsl(include_str!("shader.wgsl")); - shaders.set_untracked(FONT_SHADER_HANDLE, unified_shader); - - let render_app = app.sub_app_mut(RenderApp); - render_app - .init_resource::<QuadMeta>() - .init_resource::<FontPipeline>() - .add_system_to_stage(RenderStage::Extract, extract::extract) - .add_system_to_stage(RenderStage::Prepare, pipeline::prepare_quads) - .add_system_to_stage(RenderStage::Queue, pipeline::queue_quads) - .add_system_to_stage(RenderStage::Queue, create_and_update_font_cache_texture); - - let draw_quad = DrawUI::new(&mut render_app.world); - - render_app - .world - .get_resource::<DrawFunctions<Transparent2d>>() - .unwrap() - .write() - .add(draw_quad); - } -} - -fn create_and_update_font_cache_texture( - device: Res<RenderDevice>, - queue: Res<RenderQueue>, - pipeline: Res<FontPipeline>, - mut font_texture_cache: ResMut<FontTextureCache>, - images: Res<RenderAssets<Image>>, -) { - font_texture_cache.process_new(&device, &queue, pipeline.into_inner(), &images); -} diff --git a/kayak_font/examples/renderer/pipeline.rs b/kayak_font/examples/renderer/pipeline.rs deleted file mode 100644 index f2738c4c436fe7a9a83a2c9edf15f1353693d548..0000000000000000000000000000000000000000 --- a/kayak_font/examples/renderer/pipeline.rs +++ /dev/null @@ -1,396 +0,0 @@ -use bevy::{ - core_pipeline::core_2d::Transparent2d, - ecs::system::{ - lifetimeless::{Read, SQuery, SRes}, - SystemState, - }, - math::{Mat4, Quat, Vec2, Vec3, Vec4}, - prelude::{ - Bundle, Component, Entity, FromWorld, Handle, Query, Rect, Res, ResMut, Resource, World, - }, - render::{ - color::Color, - render_phase::{Draw, DrawFunctions, RenderPhase, TrackedRenderPass}, - render_resource::{ - BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, - BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType, BlendComponent, - BlendFactor, BlendOperation, BlendState, BufferBindingType, BufferSize, BufferUsages, - BufferVec, CachedRenderPipelineId, ColorTargetState, ColorWrites, FragmentState, - FrontFace, MultisampleState, PipelineCache, PolygonMode, PrimitiveState, - PrimitiveTopology, RenderPipelineDescriptor, SamplerBindingType, Shader, ShaderStages, - ShaderType, TextureFormat, TextureSampleType, TextureViewDimension, VertexAttribute, - VertexBufferLayout, VertexFormat, VertexState, VertexStepMode, - }, - renderer::{RenderDevice, RenderQueue}, - texture::{BevyDefault, GpuImage}, - view::{ViewUniformOffset, ViewUniforms}, - }, - utils::FloatOrd, -}; -use bytemuck::{Pod, Zeroable}; -use kayak_font::{ - bevy::{FontRenderingPipeline, FontTextureCache}, - KayakFont, -}; - -use super::FONT_SHADER_HANDLE; - -#[derive(Resource)] -pub struct FontPipeline { - view_layout: BindGroupLayout, - pub(crate) font_image_layout: BindGroupLayout, - pipeline: CachedRenderPipelineId, - empty_font_texture: (GpuImage, BindGroup), -} - -const QUAD_VERTEX_POSITIONS: &[Vec3] = &[ - Vec3::from_array([0.0, 0.0, 0.0]), - Vec3::from_array([1.0, 1.0, 0.0]), - Vec3::from_array([0.0, 1.0, 0.0]), - Vec3::from_array([0.0, 0.0, 0.0]), - Vec3::from_array([1.0, 0.0, 0.0]), - Vec3::from_array([1.0, 1.0, 0.0]), -]; - -impl FontRenderingPipeline for FontPipeline { - fn get_font_image_layout(&self) -> &BindGroupLayout { - &self.font_image_layout - } -} - -impl FromWorld for FontPipeline { - fn from_world(world: &mut World) -> Self { - let world = world.cell(); - let render_device = world.get_resource::<RenderDevice>().unwrap(); - let mut pipeline_cache = world.get_resource_mut::<PipelineCache>().unwrap(); - - let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { - entries: &[BindGroupLayoutEntry { - binding: 0, - visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, - ty: BindingType::Buffer { - ty: BufferBindingType::Uniform, - has_dynamic_offset: true, - min_binding_size: BufferSize::new(144), - }, - count: None, - }], - label: Some("ui_view_layout"), - }); - - // Used by fonts - let font_image_layout = - render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { - entries: &[ - BindGroupLayoutEntry { - binding: 0, - visibility: ShaderStages::FRAGMENT, - ty: BindingType::Texture { - multisampled: false, - sample_type: TextureSampleType::Float { filterable: true }, - view_dimension: TextureViewDimension::D2Array, - }, - count: None, - }, - BindGroupLayoutEntry { - binding: 1, - visibility: ShaderStages::FRAGMENT, - ty: BindingType::Sampler(SamplerBindingType::Filtering), - count: None, - }, - ], - label: Some("text_image_layout"), - }); - - let vertex_buffer_layout = VertexBufferLayout { - array_stride: 60, - step_mode: VertexStepMode::Vertex, - attributes: vec![ - VertexAttribute { - format: VertexFormat::Float32x3, - offset: 0, - shader_location: 0, - }, - VertexAttribute { - format: VertexFormat::Float32x4, - offset: 12, - shader_location: 1, - }, - VertexAttribute { - format: VertexFormat::Float32x4, - offset: 28, - shader_location: 2, - }, - VertexAttribute { - format: VertexFormat::Float32x4, - offset: 44, - shader_location: 3, - }, - ], - }; - - let empty_font_texture = FontTextureCache::get_empty(&render_device, &font_image_layout); - - let pipeline_desc = RenderPipelineDescriptor { - vertex: VertexState { - shader: FONT_SHADER_HANDLE.typed::<Shader>(), - entry_point: "vertex".into(), - shader_defs: vec![], - buffers: vec![vertex_buffer_layout], - }, - fragment: Some(FragmentState { - shader: FONT_SHADER_HANDLE.typed::<Shader>(), - shader_defs: vec![], - entry_point: "fragment".into(), - targets: vec![Some(ColorTargetState { - format: TextureFormat::bevy_default(), - blend: Some(BlendState { - color: BlendComponent { - src_factor: BlendFactor::SrcAlpha, - dst_factor: BlendFactor::OneMinusSrcAlpha, - operation: BlendOperation::Add, - }, - alpha: BlendComponent { - src_factor: BlendFactor::One, - dst_factor: BlendFactor::One, - operation: BlendOperation::Add, - }, - }), - write_mask: ColorWrites::ALL, - })], - }), - layout: Some(vec![view_layout.clone(), font_image_layout.clone()]), - primitive: PrimitiveState { - front_face: FrontFace::Ccw, - cull_mode: None, - polygon_mode: PolygonMode::Fill, - conservative: false, - topology: PrimitiveTopology::TriangleList, - strip_index_format: None, - unclipped_depth: false, - }, - depth_stencil: None, - multisample: MultisampleState { - count: 4, - mask: !0, - alpha_to_coverage_enabled: false, - }, - label: Some("font_pipeline".into()), - }; - - FontPipeline { - pipeline: pipeline_cache.queue_render_pipeline(pipeline_desc), - view_layout, - font_image_layout, - empty_font_texture, - } - } -} - -#[derive(Debug, Bundle)] -pub struct ExtractCharBundle { - pub(crate) extracted_quad: ExtractedChar, -} - -#[derive(Debug, Component, Clone)] -pub struct ExtractedChar { - pub rect: Rect, - pub color: Color, - pub vertex_index: usize, - pub char_id: u32, - pub z_index: f32, - pub font_handle: Option<Handle<KayakFont>>, -} - -#[repr(C)] -#[derive(Copy, Clone, Pod, Zeroable)] -struct QuadVertex { - pub position: [f32; 3], - pub color: [f32; 4], - pub uv: [f32; 4], - pub pos_size: [f32; 4], -} - -#[repr(C)] -#[derive(Copy, Clone, ShaderType)] -struct QuadType { - pub t: i32, -} - -#[derive(Resource)] -pub struct QuadMeta { - vertices: BufferVec<QuadVertex>, - view_bind_group: Option<BindGroup>, -} - -impl Default for QuadMeta { - fn default() -> Self { - Self { - vertices: BufferVec::new(BufferUsages::VERTEX), - view_bind_group: None, - } - } -} - -pub fn prepare_quads( - render_device: Res<RenderDevice>, - render_queue: Res<RenderQueue>, - mut sprite_meta: ResMut<QuadMeta>, - mut extracted_quads: Query<&mut ExtractedChar>, -) { - let extracted_sprite_len = extracted_quads.iter_mut().len(); - // don't create buffers when there are no quads - if extracted_sprite_len == 0 { - return; - } - - sprite_meta.vertices.clear(); - sprite_meta.vertices.reserve( - extracted_sprite_len * QUAD_VERTEX_POSITIONS.len(), - &render_device, - ); - - for (i, mut extracted_sprite) in extracted_quads.iter_mut().enumerate() { - let sprite_rect = extracted_sprite.rect; - let color = extracted_sprite.color.as_linear_rgba_f32(); - - let uv_min = Vec2::ZERO; - let uv_max = Vec2::ONE; - - let bottom_left = Vec4::new(uv_min.x, uv_max.y, extracted_sprite.char_id as f32, 0.0); - let top_left = Vec4::new(uv_min.x, uv_min.y, extracted_sprite.char_id as f32, 0.0); - let top_right = Vec4::new(uv_max.x, uv_min.y, extracted_sprite.char_id as f32, 0.0); - let bottom_right = Vec4::new(uv_max.x, uv_max.y, extracted_sprite.char_id as f32, 0.0); - - let uvs: [[f32; 4]; 6] = [ - bottom_left.into(), - top_right.into(), - top_left.into(), - bottom_left.into(), - bottom_right.into(), - top_right.into(), - ]; - - extracted_sprite.vertex_index = i; - for (index, vertex_position) in QUAD_VERTEX_POSITIONS.iter().enumerate() { - let world = Mat4::from_scale_rotation_translation( - sprite_rect.size().extend(1.0), - Quat::default(), - sprite_rect.min.extend(0.0), - ); - let final_position = (world * Vec3::from(*vertex_position).extend(1.0)).truncate(); - sprite_meta.vertices.push(QuadVertex { - position: final_position.into(), - color, - uv: uvs[index], - pos_size: [ - sprite_rect.min.x, - sprite_rect.min.y, - sprite_rect.size().x, - sprite_rect.size().y, - ], - }); - } - } - sprite_meta - .vertices - .write_buffer(&render_device, &render_queue); -} - -pub fn queue_quads( - draw_functions: Res<DrawFunctions<Transparent2d>>, - render_device: Res<RenderDevice>, - mut sprite_meta: ResMut<QuadMeta>, - view_uniforms: Res<ViewUniforms>, - quad_pipeline: Res<FontPipeline>, - mut extracted_sprites: Query<(Entity, &ExtractedChar)>, - mut views: Query<&mut RenderPhase<Transparent2d>>, -) { - if let Some(view_binding) = view_uniforms.uniforms.binding() { - sprite_meta.view_bind_group = Some(render_device.create_bind_group(&BindGroupDescriptor { - entries: &[BindGroupEntry { - binding: 0, - resource: view_binding, - }], - label: Some("quad_view_bind_group"), - layout: &quad_pipeline.view_layout, - })); - - let draw_quad = draw_functions.read().get_id::<DrawUI>().unwrap(); - for mut transparent_phase in views.iter_mut() { - for (entity, quad) in extracted_sprites.iter_mut() { - transparent_phase.add(Transparent2d { - draw_function: draw_quad, - pipeline: quad_pipeline.pipeline, - entity, - sort_key: FloatOrd(quad.z_index), - batch_range: None, - }); - } - } - } -} - -pub struct DrawUI { - params: SystemState<( - SRes<QuadMeta>, - SRes<FontPipeline>, - SRes<PipelineCache>, - SRes<FontTextureCache>, - SQuery<Read<ViewUniformOffset>>, - SQuery<Read<ExtractedChar>>, - )>, -} - -impl DrawUI { - pub fn new(world: &mut World) -> Self { - Self { - params: SystemState::new(world), - } - } -} - -impl Draw<Transparent2d> for DrawUI { - fn draw<'w>( - &mut self, - world: &'w World, - pass: &mut TrackedRenderPass<'w>, - view: Entity, - item: &Transparent2d, - ) { - let (quad_meta, unified_pipeline, pipelines, font_texture_cache, views, quads) = - self.params.get(world); - - let view_uniform = views.get(view).unwrap(); - let quad_meta = quad_meta.into_inner(); - let extracted_quad = quads.get(item.entity).unwrap(); - if let Some(pipeline) = pipelines.into_inner().get_render_pipeline(item.pipeline) { - pass.set_render_pipeline(pipeline); - pass.set_vertex_buffer(0, quad_meta.vertices.buffer().unwrap().slice(..)); - pass.set_bind_group( - 0, - quad_meta.view_bind_group.as_ref().unwrap(), - &[view_uniform.offset], - ); - - let unified_pipeline = unified_pipeline.into_inner(); - if let Some(font_handle) = extracted_quad.font_handle.as_ref() { - if let Some(image_bindings) = - font_texture_cache.into_inner().get_binding(font_handle) - { - pass.set_bind_group(1, image_bindings, &[]); - } else { - pass.set_bind_group(1, &unified_pipeline.empty_font_texture.1, &[]); - } - } else { - pass.set_bind_group(1, &unified_pipeline.empty_font_texture.1, &[]); - } - - pass.draw( - (extracted_quad.vertex_index * QUAD_VERTEX_POSITIONS.len()) as u32 - ..((extracted_quad.vertex_index + 1) * QUAD_VERTEX_POSITIONS.len()) as u32, - 0..1, - ); - } - } -} diff --git a/kayak_font/examples/renderer/shader.wgsl b/kayak_font/examples/renderer/shader.wgsl deleted file mode 100644 index 36af3a3045da9d17a6dfe361f221bdf6d84517c4..0000000000000000000000000000000000000000 --- a/kayak_font/examples/renderer/shader.wgsl +++ /dev/null @@ -1,52 +0,0 @@ -struct View { - view_proj: mat4x4<f32>, - world_position: vec3<f32>, -} - -@group(0) @binding(0) -var<uniform> view: View; - -struct VertexOutput { - @builtin(position) position: vec4<f32>, - @location(0) color: vec4<f32>, - @location(1) uv: vec3<f32>, - @location(2) pos: vec2<f32>, - @location(3) size: vec2<f32>, - @location(4) screen_position: vec2<f32>, - @location(5) border_radius: f32, -}; - -@vertex -fn vertex( - @location(0) vertex_position: vec3<f32>, - @location(1) vertex_color: vec4<f32>, - @location(2) vertex_uv: vec4<f32>, - @location(3) vertex_pos_size: vec4<f32>, -) -> VertexOutput { - var out: VertexOutput; - out.color = vertex_color; - out.pos = vertex_pos_size.xy; - out.position = view.view_proj * vec4<f32>(vertex_position, 1.0); - out.screen_position = (view.view_proj * vec4<f32>(vertex_position, 1.0)).xy; - out.uv = vertex_uv.xyz; - out.size = vertex_pos_size.zw; - out.border_radius = vertex_uv.w; - return out; -} - -@group(1) @binding(0) -var font_texture: texture_2d_array<f32>; -@group(1) @binding(1) -var font_sampler: sampler; - -@fragment -fn fragment(in: VertexOutput) -> @location(0) vec4<f32> { - var px_range = 4.5; - var tex_dimensions = textureDimensions(font_texture); - var msdf_unit = vec2<f32>(px_range, px_range) / vec2<f32>(f32(tex_dimensions.x), f32(tex_dimensions.y)); - var x = textureSample(font_texture, font_sampler, vec2<f32>(in.uv.x, in.uv.y), i32(in.uv.z)); - var v = max(min(x.r, x.g), min(max(x.r, x.g), x.b)); - var sig_dist = (v - 0.5) * dot(msdf_unit, 0.5 / fwidth(in.uv.xy)); - var a = clamp(sig_dist + 0.5, 0.0, 1.0); - return vec4<f32>(in.color.rgb, a); -} \ No newline at end of file diff --git a/kayak_font/examples/renderer/text.rs b/kayak_font/examples/renderer/text.rs deleted file mode 100644 index 21832d9bb7a4bfe4c2be2f4f9f85acd969a77674..0000000000000000000000000000000000000000 --- a/kayak_font/examples/renderer/text.rs +++ /dev/null @@ -1,13 +0,0 @@ -use bevy::{math::Vec2, prelude::Component, render::color::Color}; -use kayak_font::Alignment; - -#[derive(Component)] -pub struct Text { - pub horz_alignment: Alignment, - pub content: String, - pub position: Vec2, - pub size: Vec2, - pub font_size: f32, - pub line_height: f32, - pub color: Color, -}