diff --git a/.gitignore b/.gitignore index d5bd8b9..605799d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ +/Cargo.lock /target -/build.rs \ No newline at end of file +/build.rs +/src/main.rs diff --git a/README.md b/README.md index 89ecd23..e1049ba 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,9 @@ a free and open source games framework > [!WARNING] > This project is in early development and is not yet ready for use. -> +> > It could be potentially used to make something very basic in a very hacky way, but it is not a good experience. -> +> > Installation is manual as of right now but if it reaches an acceptable state I will publish the crate on crates. > > There is a plan for a project creation tool that will automate the project setup process. @@ -20,7 +20,7 @@ project │ build.rs │ crates │ └── comet -│ resources +│ res │ └── shaders │ └── textures │ └── sounds @@ -29,15 +29,15 @@ project ``` ```toml -# Cargo.toml +# Cargo.toml # ... [dependencies] -comet = { path = "path/of/the/comet/crate" } +comet = { path = "path/of/the/comet/crate" } # ... ``` ```rust -// main.rs example +// main.rs example use comet::prelude::*; struct GameState {} @@ -50,17 +50,17 @@ impl GameState { // This function will be called once before the event loop starts fn setup(app: &mut App, renderer: &mut Renderer2D) {} -// This function will be called every tick +// This function will be called every tick fn update(app: &mut App, renderer: &mut Renderer2D, dt: f32) {} -fn main() { +fn main() { App::new() // Generate a basic 2D app .with_preset(App2D) // Pre-registers the `Transform2D` component in the scene .with_title("Comet App") // Sets the window title - .with_icon(r"resources/textures/comet_icon.png") // Sets the window icon + .with_icon(r"res/textures/comet_icon.png") // Sets the window icon .with_size(1920, 1080) // Sets the window size .with_game_state(GameState::new()) // Adds a custom game state struct - .run::(setup, update) // Starts app with the given + .run::(setup, update) // Starts app with the given } ``` @@ -75,41 +75,41 @@ use std::path::PathBuf; fn main() -> Result<()> { // Watch resource directories for changes - println!("cargo:rerun-if-changed=resources/materials/*"); - println!("cargo:rerun-if-changed=resources/objects/*"); - println!("cargo:rerun-if-changed=resources/textures/*"); - println!("cargo:rerun-if-changed=resources/shaders/*"); - println!("cargo:rerun-if-changed=resources/data/*"); - println!("cargo:rerun-if-changed=resources/sounds/*"); - println!("cargo:rerun-if-changed=resources/fonts/*"); - + println!("cargo:rerun-if-changed=res/materials/*"); + println!("cargo:rerun-if-changed=res/objects/*"); + println!("cargo:rerun-if-changed=res/textures/*"); + println!("cargo:rerun-if-changed=res/shaders/*"); + println!("cargo:rerun-if-changed=res/data/*"); + println!("cargo:rerun-if-changed=res/sounds/*"); + println!("cargo:rerun-if-changed=res/fonts/*"); + let profile = env::var("PROFILE")?; let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR")?); let target_dir = manifest_dir.join("target").join(&profile); - - let dest_resources_dir = target_dir.join("resources"); - + + let dest_resources_dir = target_dir.join("res"); + std::fs::create_dir_all(&dest_resources_dir)?; - + let mut copy_options = CopyOptions::new(); copy_options.overwrite = true; copy_options.copy_inside = true; - + let resource_folders = vec![ - "resources/materials/", - "resources/objects/", - "resources/textures/", - "resources/shaders/", - "resources/data/", - "resources/sounds/", - "resources/fonts/", + "res/materials/", + "res/objects/", + "res/textures/", + "res/shaders/", + "res/data/", + "res/sounds/", + "res/fonts/", ]; - + let resource_paths: Vec = resource_folders .iter() .map(|p| manifest_dir.join(p)) .collect(); - + copy_items(&resource_paths, &dest_resources_dir, ©_options)?; Ok(()) diff --git a/crates/comet_renderer/src/render2d.rs b/crates/comet_renderer/src/render2d.rs index c62ceff..11988e4 100644 --- a/crates/comet_renderer/src/render2d.rs +++ b/crates/comet_renderer/src/render2d.rs @@ -1,313 +1,313 @@ +use crate::camera::{CameraUniform, RenderCamera}; +use crate::draw_info::DrawInfo; +use crate::render_pass::{RenderPassInfo, RenderPassType}; +use crate::renderer::Renderer; +use comet_colors::Color; +use comet_ecs::{Camera2D, Component, Position2D, Render, Render2D, Scene, Text, Transform2D}; +use comet_log::*; +use comet_math::{p2, p3, v2, v3}; +use comet_resources::texture_atlas::TextureRegion; +use comet_resources::{graphic_resource_manager::GraphicResourceManager, Texture, Vertex}; +use comet_structs::ComponentSet; use std::iter; use std::path::PathBuf; use std::sync::Arc; use std::time::Instant; -use wgpu::BufferUsages; use wgpu::core::command::DrawKind::Draw; use wgpu::naga::ShaderStage; use wgpu::util::DeviceExt; +use wgpu::BufferUsages; use winit::dpi::PhysicalSize; use winit::window::Window; -use comet_colors::Color; -use comet_ecs::{Camera2D, Component, Position2D, Render, Render2D, Transform2D, Scene, Text}; -use comet_log::*; -use comet_math::{p2, p3, v2, v3}; -use comet_resources::{graphic_resource_manager::GraphicResourceManager, Texture, Vertex}; -use comet_resources::texture_atlas::TextureRegion; -use comet_structs::ComponentSet; -use crate::camera::{RenderCamera, CameraUniform}; -use crate::draw_info::DrawInfo; -use crate::render_pass::{RenderPassInfo, RenderPassType}; -use crate::renderer::Renderer; pub struct Renderer2D<'a> { - surface: wgpu::Surface<'a>, - device: wgpu::Device, - queue: wgpu::Queue, - config: wgpu::SurfaceConfiguration, - size: PhysicalSize, - render_pipeline_layout: wgpu::PipelineLayout, - universal_render_pipeline: wgpu::RenderPipeline, - texture_bind_group_layout: wgpu::BindGroupLayout, - dummy_texture_bind_group: wgpu::BindGroup, - texture_sampler: wgpu::Sampler, - camera: RenderCamera, - camera_uniform: CameraUniform, - camera_buffer: wgpu::Buffer, - camera_bind_group: wgpu::BindGroup, - render_pass: Vec, - draw_info: Vec, - graphic_resource_manager: GraphicResourceManager, - delta_time: f32, - last_frame_time: Instant, - clear_color: wgpu::Color, + surface: wgpu::Surface<'a>, + device: wgpu::Device, + queue: wgpu::Queue, + config: wgpu::SurfaceConfiguration, + size: PhysicalSize, + render_pipeline_layout: wgpu::PipelineLayout, + universal_render_pipeline: wgpu::RenderPipeline, + texture_bind_group_layout: wgpu::BindGroupLayout, + dummy_texture_bind_group: wgpu::BindGroup, + texture_sampler: wgpu::Sampler, + camera: RenderCamera, + camera_uniform: CameraUniform, + camera_buffer: wgpu::Buffer, + camera_bind_group: wgpu::BindGroup, + render_pass: Vec, + draw_info: Vec, + graphic_resource_manager: GraphicResourceManager, + delta_time: f32, + last_frame_time: Instant, + clear_color: wgpu::Color, } impl<'a> Renderer2D<'a> { pub fn new(window: Arc, clear_color: Option) -> Renderer2D<'a> { - let size = PhysicalSize::::new(1920, 1080); + let size = PhysicalSize::::new(1920, 1080); - let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { - backends: wgpu::Backends::PRIMARY, - ..Default::default() - }); + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { + backends: wgpu::Backends::PRIMARY, + ..Default::default() + }); - let surface = instance.create_surface(window).unwrap(); + let surface = instance.create_surface(window).unwrap(); - let adapter = pollster::block_on(instance - .request_adapter(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::default(), - compatible_surface: Some(&surface), - force_fallback_adapter: false, - })) - .unwrap(); + let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::default(), + compatible_surface: Some(&surface), + force_fallback_adapter: false, + })) + .unwrap(); - let (device, queue) = pollster::block_on(adapter - .request_device( - &wgpu::DeviceDescriptor { - label: None, - required_features: wgpu::Features::empty(), - required_limits: wgpu::Limits::default(), - memory_hints: Default::default(), - }, - None, // Trace path - )) - .unwrap(); + let (device, queue) = pollster::block_on(adapter.request_device( + &wgpu::DeviceDescriptor { + label: None, + required_features: wgpu::Features::empty(), + required_limits: wgpu::Limits::default(), + memory_hints: Default::default(), + }, + None, // Trace path + )) + .unwrap(); - let surface_caps = surface.get_capabilities(&adapter); - let surface_format = surface_caps - .formats - .iter() - .copied() - .find(|f| f.is_srgb()) - .unwrap_or(surface_caps.formats[0]); - let config = wgpu::SurfaceConfiguration { - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - format: surface_format, - width: size.width, - height: size.height, - present_mode: surface_caps.present_modes[0], - alpha_mode: surface_caps.alpha_modes[0], - view_formats: vec![], - desired_maximum_frame_latency: 2, - }; + let surface_caps = surface.get_capabilities(&adapter); + let surface_format = surface_caps + .formats + .iter() + .copied() + .find(|f| f.is_srgb()) + .unwrap_or(surface_caps.formats[0]); + let config = wgpu::SurfaceConfiguration { + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + format: surface_format, + width: size.width, + height: size.height, + present_mode: surface_caps.present_modes[0], + alpha_mode: surface_caps.alpha_modes[0], + view_formats: vec![], + desired_maximum_frame_latency: 2, + }; - let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("Shader"), - source: wgpu::ShaderSource::Wgsl(include_str!("base2d.wgsl").into()), - }); + let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("Shader"), + source: wgpu::ShaderSource::Wgsl(include_str!("base2d.wgsl").into()), + }); - let graphic_resource_manager = GraphicResourceManager::new(); + let graphic_resource_manager = GraphicResourceManager::new(); - let diffuse_bytes = include_bytes!(r"../../../resources/textures/comet_icon.png"); - let diffuse_texture = - Texture::from_bytes(&device, &queue, diffuse_bytes, "comet_icon.png", false).unwrap(); + let diffuse_bytes = include_bytes!(r"../../../res/textures/comet_icon.png"); + let diffuse_texture = + Texture::from_bytes(&device, &queue, diffuse_bytes, "comet_icon.png", false).unwrap(); - let texture_bind_group_layout = - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - multisampled: false, - view_dimension: wgpu::TextureViewDimension::D2, - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), - count: None, - }, - ], - label: Some("texture_bind_group_layout"), - }); + let texture_bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + multisampled: false, + view_dimension: wgpu::TextureViewDimension::D2, + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, + }, + ], + label: Some("texture_bind_group_layout"), + }); - let camera = RenderCamera::new(1.0, v2::new(2.0, 2.0), v3::new(0.0, 0.0, 0.0)); + let camera = RenderCamera::new(1.0, v2::new(2.0, 2.0), v3::new(0.0, 0.0, 0.0)); - let mut camera_uniform = CameraUniform::new(); - camera_uniform.update_view_proj(&camera); + let mut camera_uniform = CameraUniform::new(); + camera_uniform.update_view_proj(&camera); - let camera_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Camera Buffer"), - contents: bytemuck::cast_slice(&[camera_uniform]), - usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, - }); + let camera_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Camera Buffer"), + contents: bytemuck::cast_slice(&[camera_uniform]), + usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, + }); - let camera_bind_group_layout = - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - entries: &[wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }], - label: Some("camera_bind_group_layout"), - }); + let camera_bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }], + label: Some("camera_bind_group_layout"), + }); - let camera_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &camera_bind_group_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: camera_buffer.as_entire_binding(), - }], - label: Some("camera_bind_group"), - }); + let camera_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &camera_bind_group_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: camera_buffer.as_entire_binding(), + }], + label: Some("camera_bind_group"), + }); - let render_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Render Pipeline Layout"), - bind_group_layouts: &[ - &texture_bind_group_layout, - &camera_bind_group_layout, - ], - push_constant_ranges: &[], - }); + let render_pipeline_layout = + device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Render Pipeline Layout"), + bind_group_layouts: &[&texture_bind_group_layout, &camera_bind_group_layout], + push_constant_ranges: &[], + }); - let universal_render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Render Pipeline"), - layout: Some(&render_pipeline_layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: "vs_main", - buffers: &[Vertex::desc()], - compilation_options: Default::default(), - }, - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: "fs_main", - targets: &[Some(wgpu::ColorTargetState { - format: config.format, - blend: Some(wgpu::BlendState { - color: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::SrcAlpha, - dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, - operation: wgpu::BlendOperation::Add, - }, - alpha: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::One, - dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, - operation: wgpu::BlendOperation::Add, - }, - }), - write_mask: wgpu::ColorWrites::ALL, - })], - compilation_options: Default::default(), - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: Some(wgpu::Face::Back), - polygon_mode: wgpu::PolygonMode::Fill, - unclipped_depth: false, - conservative: false, - }, - depth_stencil: None, - multisample: wgpu::MultisampleState { - count: 1, - mask: !0, - alpha_to_coverage_enabled: false, - }, - multiview: None, - cache: None, - }); + let universal_render_pipeline = + device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Render Pipeline"), + layout: Some(&render_pipeline_layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "vs_main", + buffers: &[Vertex::desc()], + compilation_options: Default::default(), + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "fs_main", + targets: &[Some(wgpu::ColorTargetState { + format: config.format, + blend: Some(wgpu::BlendState { + color: wgpu::BlendComponent { + src_factor: wgpu::BlendFactor::SrcAlpha, + dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, + operation: wgpu::BlendOperation::Add, + }, + alpha: wgpu::BlendComponent { + src_factor: wgpu::BlendFactor::One, + dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, + operation: wgpu::BlendOperation::Add, + }, + }), + write_mask: wgpu::ColorWrites::ALL, + })], + compilation_options: Default::default(), + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + strip_index_format: None, + front_face: wgpu::FrontFace::Ccw, + cull_mode: Some(wgpu::Face::Back), + polygon_mode: wgpu::PolygonMode::Fill, + unclipped_depth: false, + conservative: false, + }, + depth_stencil: None, + multisample: wgpu::MultisampleState { + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, + }, + multiview: None, + cache: None, + }); - let mut render_pass: Vec = Vec::new(); - /*render_pass.push(RenderPassInfo::new_engine_pass( - &device, - "Standard Render Pass".to_string(), - &texture_bind_group_layout, - &diffuse_texture, - vec![], - vec![], - ));*/ + let mut render_pass: Vec = Vec::new(); + /*render_pass.push(RenderPassInfo::new_engine_pass( + &device, + "Standard Render Pass".to_string(), + &texture_bind_group_layout, + &diffuse_texture, + vec![], + vec![], + ));*/ - let clear_color = match clear_color { - Some(color) => color.to_wgpu(), - None => wgpu::Color { - r: 0.0, - g: 0.0, - b: 0.0, - a: 1.0, - } - }; + let clear_color = match clear_color { + Some(color) => color.to_wgpu(), + None => wgpu::Color { + r: 0.0, + g: 0.0, + b: 0.0, + a: 1.0, + }, + }; - let texture_sampler = device.create_sampler(&wgpu::SamplerDescriptor { - address_mode_u: wgpu::AddressMode::ClampToEdge, - address_mode_v: wgpu::AddressMode::ClampToEdge, - address_mode_w: wgpu::AddressMode::ClampToEdge, - mag_filter: wgpu::FilterMode::Linear, - min_filter: wgpu::FilterMode::Linear, - mipmap_filter: wgpu::FilterMode::Linear, - lod_min_clamp: 0.0, - lod_max_clamp: 100.0, - compare: None, - anisotropy_clamp: 16, - border_color: None, - ..Default::default() - }); + let texture_sampler = device.create_sampler(&wgpu::SamplerDescriptor { + address_mode_u: wgpu::AddressMode::ClampToEdge, + address_mode_v: wgpu::AddressMode::ClampToEdge, + address_mode_w: wgpu::AddressMode::ClampToEdge, + mag_filter: wgpu::FilterMode::Linear, + min_filter: wgpu::FilterMode::Linear, + mipmap_filter: wgpu::FilterMode::Linear, + lod_min_clamp: 0.0, + lod_max_clamp: 100.0, + compare: None, + anisotropy_clamp: 16, + border_color: None, + ..Default::default() + }); - let empty_texture = device.create_texture(&wgpu::TextureDescriptor { - label: Some("Empty Texture"), - size: wgpu::Extent3d { - width: config.width, - height: config.height, - depth_or_array_layers: 1, - }, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Bgra8UnormSrgb, - usage: wgpu::TextureUsages::COPY_SRC | wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::TEXTURE_BINDING, - view_formats: &[wgpu::TextureFormat::Bgra8UnormSrgb], - }); + let empty_texture = device.create_texture(&wgpu::TextureDescriptor { + label: Some("Empty Texture"), + size: wgpu::Extent3d { + width: config.width, + height: config.height, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Bgra8UnormSrgb, + usage: wgpu::TextureUsages::COPY_SRC + | wgpu::TextureUsages::COPY_DST + | wgpu::TextureUsages::TEXTURE_BINDING, + view_formats: &[wgpu::TextureFormat::Bgra8UnormSrgb], + }); - let dummy_texture_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &texture_bind_group_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(&empty_texture.create_view(&wgpu::TextureViewDescriptor::default())), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Sampler(&texture_sampler), - }, - ], - label: Some("dummy_texture_bind_group"), - }); - - let mut draw_info: Vec = Vec::new(); + let dummy_texture_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &texture_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView( + &empty_texture.create_view(&wgpu::TextureViewDescriptor::default()), + ), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Sampler(&texture_sampler), + }, + ], + label: Some("dummy_texture_bind_group"), + }); - Self { - surface, - device, - queue, - config, - size, - render_pipeline_layout, - universal_render_pipeline, - texture_bind_group_layout, - dummy_texture_bind_group, - texture_sampler, - camera, - camera_uniform, - camera_buffer, - camera_bind_group, - render_pass, - draw_info, - graphic_resource_manager, - delta_time: 0.0, - last_frame_time: Instant::now(), - clear_color, - } - } -} \ No newline at end of file + let mut draw_info: Vec = Vec::new(); + + Self { + surface, + device, + queue, + config, + size, + render_pipeline_layout, + universal_render_pipeline, + texture_bind_group_layout, + dummy_texture_bind_group, + texture_sampler, + camera, + camera_uniform, + camera_buffer, + camera_bind_group, + render_pass, + draw_info, + graphic_resource_manager, + delta_time: 0.0, + last_frame_time: Instant::now(), + clear_color, + } + } +} diff --git a/crates/comet_renderer/src/renderer2d.rs b/crates/comet_renderer/src/renderer2d.rs index b2aaaf2..2450d74 100755 --- a/crates/comet_renderer/src/renderer2d.rs +++ b/crates/comet_renderer/src/renderer2d.rs @@ -1,732 +1,895 @@ +use crate::{ + camera::{CameraUniform, RenderCamera}, + draw_info::DrawInfo, + renderer::Renderer, +}; +use comet_colors::Color; +use comet_ecs::{Camera2D, Component, Position2D, Render, Render2D, Scene, Text, Transform2D}; +use comet_log::*; +use comet_math::{p2, v2, v3}; +use comet_resources::texture_atlas::TextureRegion; +use comet_resources::{graphic_resource_manager::GraphicResourceManager, Texture, Vertex}; +use comet_structs::ComponentSet; use std::iter; use std::path::PathBuf; use std::sync::Arc; use std::time::Instant; -use wgpu::BufferUsages; use wgpu::naga::ShaderStage; use wgpu::util::DeviceExt; +use wgpu::BufferUsages; use winit::dpi::PhysicalSize; use winit::window::Window; -use comet_colors::Color; -use comet_ecs::{Camera2D, Component, Position2D, Render, Render2D, Transform2D, Scene, Text}; -use comet_log::*; -use comet_math::{p2, v2, v3}; -use comet_resources::{graphic_resource_manager::GraphicResourceManager, Texture, Vertex}; -use comet_resources::texture_atlas::TextureRegion; -use comet_structs::ComponentSet; -use crate::{renderer::Renderer, draw_info::DrawInfo, camera::{RenderCamera, CameraUniform}}; pub struct Renderer2D<'a> { - surface: wgpu::Surface<'a>, - device: wgpu::Device, - queue: wgpu::Queue, - config: wgpu::SurfaceConfiguration, - size: PhysicalSize, - universal_render_pipeline: wgpu::RenderPipeline, - texture_bind_group_layout: wgpu::BindGroupLayout, - texture_sampler: wgpu::Sampler, - camera: RenderCamera, - camera_uniform: CameraUniform, - camera_buffer: wgpu::Buffer, - camera_bind_group: wgpu::BindGroup, - draw_info: Vec, - graphic_resource_manager: GraphicResourceManager, - delta_time: f32, - last_frame_time: Instant, - clear_color: wgpu::Color, + surface: wgpu::Surface<'a>, + device: wgpu::Device, + queue: wgpu::Queue, + config: wgpu::SurfaceConfiguration, + size: PhysicalSize, + universal_render_pipeline: wgpu::RenderPipeline, + texture_bind_group_layout: wgpu::BindGroupLayout, + texture_sampler: wgpu::Sampler, + camera: RenderCamera, + camera_uniform: CameraUniform, + camera_buffer: wgpu::Buffer, + camera_bind_group: wgpu::BindGroup, + draw_info: Vec, + graphic_resource_manager: GraphicResourceManager, + delta_time: f32, + last_frame_time: Instant, + clear_color: wgpu::Color, } impl<'a> Renderer2D<'a> { - pub fn new(window: Arc, clear_color: Option) -> Renderer2D<'a> { - let size = window.inner_size();//PhysicalSize::::new(1920, 1080); - - let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { - backends: wgpu::Backends::PRIMARY, - ..Default::default() - }); - - let surface = instance.create_surface(window).unwrap(); - - let adapter = pollster::block_on(instance - .request_adapter(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::default(), - compatible_surface: Some(&surface), - force_fallback_adapter: false, - })) - .unwrap(); - - let (device, queue) = pollster::block_on(adapter - .request_device( - &wgpu::DeviceDescriptor { - label: None, - required_features: wgpu::Features::empty(), - required_limits: wgpu::Limits::default(), - memory_hints: Default::default(), - }, - None, // Trace path - )) - .unwrap(); - - let surface_caps = surface.get_capabilities(&adapter); - let surface_format = surface_caps - .formats - .iter() - .copied() - .find(|f| f.is_srgb()) - .unwrap_or(surface_caps.formats[0]); - let config = wgpu::SurfaceConfiguration { - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - format: surface_format, - width: size.width, - height: size.height, - present_mode: surface_caps.present_modes[0], - alpha_mode: surface_caps.alpha_modes[0], - view_formats: vec![], - desired_maximum_frame_latency: 2, - }; - - let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("Universal Shader"), - source: wgpu::ShaderSource::Wgsl(include_str!("base2d.wgsl").into()), - }); - - let graphic_resource_manager = GraphicResourceManager::new(); - - let texture_bind_group_layout = - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - multisampled: false, - view_dimension: wgpu::TextureViewDimension::D2, - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), - count: None, - }, - ], - label: Some("Universal Texture Bind Group Layout"), - }); - - let camera = RenderCamera::new(1.0, v2::new(2.0, 2.0), v3::new(0.0, 0.0, 0.0)); - - let mut camera_uniform = CameraUniform::new(); - camera_uniform.update_view_proj(&camera); - - let camera_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Camera Buffer"), - contents: bytemuck::cast_slice(&[camera_uniform]), - usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, - }); - - let camera_bind_group_layout = - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - entries: &[wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }], - label: Some("Universal Camera Bind Group Layout"), - }); - - let camera_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &camera_bind_group_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: camera_buffer.as_entire_binding(), - }], - label: Some("Universal Camera Bind Group"), - }); - - let render_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Universal Render Pipeline Layout"), - bind_group_layouts: &[ - &texture_bind_group_layout, - &camera_bind_group_layout, - ], - push_constant_ranges: &[], - }); - - let universal_render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Universal Render Pipeline"), - layout: Some(&render_pipeline_layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: "vs_main", - buffers: &[Vertex::desc()], - compilation_options: Default::default(), - }, - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: "fs_main", - targets: &[Some(wgpu::ColorTargetState { - format: config.format, - blend: Some(wgpu::BlendState { - color: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::SrcAlpha, - dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, - operation: wgpu::BlendOperation::Add, - }, - alpha: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::One, - dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, - operation: wgpu::BlendOperation::Add, - }, - }), - write_mask: wgpu::ColorWrites::ALL, - })], - compilation_options: Default::default(), - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: Some(wgpu::Face::Back), - polygon_mode: wgpu::PolygonMode::Fill, - unclipped_depth: false, - conservative: false, - }, - depth_stencil: None, - multisample: wgpu::MultisampleState { - count: 1, - mask: !0, - alpha_to_coverage_enabled: false, - }, - multiview: None, - cache: None, - }); - - let clear_color = match clear_color { - Some(color) => color.to_wgpu(), - None => wgpu::Color { - r: 0.0, - g: 0.0, - b: 0.0, - a: 1.0, - } - }; - - let texture_sampler = device.create_sampler(&wgpu::SamplerDescriptor { - address_mode_u: wgpu::AddressMode::ClampToEdge, - address_mode_v: wgpu::AddressMode::ClampToEdge, - address_mode_w: wgpu::AddressMode::ClampToEdge, - mag_filter: wgpu::FilterMode::Linear, - min_filter: wgpu::FilterMode::Linear, - mipmap_filter: wgpu::FilterMode::Linear, - lod_min_clamp: 0.0, - lod_max_clamp: 100.0, - compare: None, - anisotropy_clamp: 16, - border_color: None, - ..Default::default() - }); - - let mut draw_info: Vec = Vec::new(); - draw_info.push( - DrawInfo::new( - "Universal Draw".to_string(), - &device, - &Texture::from_image(&device, &queue, &image::DynamicImage::new(1, 1, image::ColorType::Rgba8), None, false).unwrap(), - &texture_bind_group_layout, - &texture_sampler, - vec![], - vec![], - ) - ); - - Self { - surface, - device, - queue, - config, - size, - universal_render_pipeline, - texture_bind_group_layout, - texture_sampler, - camera, - camera_uniform, - camera_buffer, - camera_bind_group, - draw_info, - graphic_resource_manager, - delta_time: 0.0, - last_frame_time: Instant::now(), - clear_color, - } - } - - pub fn dt(&self) -> f32 { - self.delta_time - } - - pub fn config(&self) -> &wgpu::SurfaceConfiguration { - &self.config - } - - pub fn size(&self) -> PhysicalSize { - self.size - } - - pub fn resize(&mut self, new_size: PhysicalSize) { - if new_size.width > 0 && new_size.height > 0 { - self.size = new_size; - self.config.width = new_size.width; - self.config.height = new_size.height; - self.surface.configure(&self.device, &self.config); - } - } - - pub fn add_draw_call(&mut self, draw_call: String, texture: Texture) { - let draw_info = DrawInfo::new( - draw_call, - &self.device, - &texture, - &self.texture_bind_group_layout, - &self.texture_sampler, - vec![], - vec![], - ); - self.draw_info.push(draw_info); - } - - /// A function that loads a shader from the resources/shaders dir given the full name of the shader file. - pub fn load_shader(&mut self, file_name: &str, shader_stage: Option) { - self.graphic_resource_manager.load_shader(shader_stage, ((Self::get_project_root().unwrap().as_os_str().to_str().unwrap().to_string() + "/resources/shaders/").as_str().to_string() + file_name).as_str(), &self.device).unwrap(); - info!("Shader ({}) loaded successfully", file_name); - } - - /// A function that loads a list of shaders from the given filenames out of the resources/shaders dir - pub fn load_shaders(&mut self, shader_stages: Vec>, file_names: Vec<&str>) { - for (i, file_name) in file_names.iter().enumerate() { - self.load_shader(file_name, shader_stages[i].clone()); - info!("Shader ({}) loaded successfully", file_name); - } - } - - /// A function that applies a shader to the entire surface of the `Renderer2D` if the shader is loaded. - pub fn apply_shader(&mut self, shader: &str) { - let module = match self.graphic_resource_manager.get_shader(shader) { - Some(module) => module, - None => { - error!("Shader not found"); - return; - } - }; - } - - /// A function to revert back to the base shader of the `Renderer2D` - pub fn apply_base_shader(&mut self) { - todo!() - } - - /// A function to load a TTF font from the specified path - pub fn load_font(&mut self, path: &str, size: f32) { - self.graphic_resource_manager.load_font(path, size); - let atlas = self.graphic_resource_manager.fonts().iter().find(|f| f.name() == path).unwrap().glyphs().atlas(); - let font_info = DrawInfo::new( - format!("{}", path), - &self.device, - &Texture::from_image(&self.device, &self.queue, atlas, None, false).unwrap(), - &self.texture_bind_group_layout, - &self.texture_sampler, - vec![], - vec![], - ); - - self.draw_info.push(font_info); - } - - /// An interface for getting the location of the texture in the texture atlas. - pub fn get_texture_region(&self, texture_path: String) -> Option<&TextureRegion> { - if !self.graphic_resource_manager.texture_atlas().textures().contains_key(&texture_path) { - error!("Texture {} not found in atlas", &texture_path); - } - self.graphic_resource_manager.texture_atlas().textures().get(&texture_path) - } - - /// A function to get the `TextureRegion` of a specified glyph - pub fn get_glyph_region(&self, glyph: char, font: String) -> &TextureRegion { - let font_atlas = self.graphic_resource_manager.fonts().iter().find(|f| f.name() == font).unwrap(); - font_atlas.get_glyph(glyph).unwrap() - } - - /// A function that allows you to set the texture atlas with a list of paths to the textures. - /// The old texture atlas will be replaced with the new one. - pub fn set_texture_atlas_by_paths(&mut self, paths: Vec) { - self.graphic_resource_manager.create_texture_atlas(paths); - self.draw_info[0].set_texture(&self.device, &self.texture_bind_group_layout, &Texture::from_image(&self.device, &self.queue, self.graphic_resource_manager.texture_atlas().atlas(), None, false).unwrap()); - } - - fn set_texture_atlas(&mut self, texture_atlas: Texture) { - self.draw_info[0].set_texture( - &self.device, - &self.texture_bind_group_layout, - &texture_atlas, - ); - } - - fn get_project_root() -> std::io::Result { - let path = std::env::current_dir()?; - let mut path_ancestors = path.as_path().ancestors(); - - while let Some(p) = path_ancestors.next() { - let has_cargo = - std::fs::read_dir(p)? - .into_iter() - .any(|p| p.unwrap().file_name() == std::ffi::OsString::from("Cargo.lock")); - if has_cargo { - return Ok(PathBuf::from(p)) - } - } - Err(std::io::Error::new(std::io::ErrorKind::NotFound, "Ran out of places to find Cargo.toml")) - } - - /// A function that takes all the textures inside the resources/textures folder and creates a texture atlas from them. - pub fn initialize_atlas(&mut self) { - let texture_path = "resources/textures/".to_string(); - let mut paths: Vec = Vec::new(); - - for path in std::fs::read_dir(Self::get_project_root().unwrap().as_os_str().to_str().unwrap().to_string() + "/resources/textures").unwrap() { - paths.push(texture_path.clone() + path.unwrap().file_name().to_str().unwrap()); - } - - self.set_texture_atlas_by_paths(paths); - } - - /// A function that writes on the buffers and sets the geometry and index buffer of the `Renderer2D` with the given data. - fn set_buffers(&mut self, new_geometry_buffer: Vec, new_index_buffer: Vec) { - self.draw_info[0].update_vertex_buffer(&self.device, &self.queue, new_geometry_buffer); - self.draw_info[0].update_index_buffer(&self.device, &self.queue, new_index_buffer); - } - - fn add_text_to_buffers(&self, text: String, font: String, size: f32, position: p2, color: wgpu::Color) -> (Vec, Vec) { - let vert_color = [color.r as f32, color.g as f32, color.b as f32, color.a as f32]; - - let screen_position = p2::new(position.x()/self.config.width as f32, position.y()/self.config.height as f32); - let scale_factor = size / self.graphic_resource_manager.fonts().iter().find(|f| f.name() == font).unwrap().size(); - - let line_height = (self.graphic_resource_manager.fonts().iter().find(|f| f.name() == font).unwrap().line_height() / self.config.height as f32) * scale_factor; - let lines = text - .split("\n") - .map(|s| { - s.split("").map(|escape| { - match escape { - _ if escape == "\t" => { - " " - } - _ => escape - } - }).collect::() - }) - .collect::>(); - - let mut x_offset = 0.0; - let mut y_offset = 0.0; - - let mut vertex_data = Vec::new(); - let mut index_data = Vec::new(); - - for line in lines { - for c in line.chars() { - let region = self.get_glyph_region(c, font.clone()); - let (dim_x, dim_y) = region.dimensions(); - - let w = (dim_x as f32 / self.config.width as f32) * scale_factor; - let h = (dim_y as f32 / self.config.height as f32) * scale_factor; - - let offset_x_px = (region.offset_x() / self.config.width as f32) * scale_factor; - let offset_y_px = (region.offset_y() / self.config.height as f32) * scale_factor; - - let glyph_left = screen_position.x() + x_offset + offset_x_px; - let glyph_top = screen_position.y() - offset_y_px - y_offset; - let glyph_right = glyph_left + w; - let glyph_bottom = glyph_top - h; - - let vertices: &mut Vec = &mut vec![ - Vertex::new([ glyph_left, glyph_top, 0.0 ], [region.u0(), region.v0()], vert_color), - Vertex::new([ glyph_left, glyph_bottom, 0.0 ], [region.u0(), region.v1()], vert_color), - Vertex::new([ glyph_right, glyph_bottom, 0.0 ], [region.u1(), region.v1()], vert_color), - Vertex::new([ glyph_right, glyph_top, 0.0 ], [region.u1(), region.v0()], vert_color), - ]; - - let buffer_size = vertex_data.len() as u16; - let indices: &mut Vec = &mut vec![ - buffer_size, buffer_size + 1, buffer_size + 3, - buffer_size + 1, buffer_size + 2, buffer_size + 3, - ]; - - x_offset += (region.advance() / self.config.width as f32) * scale_factor; - - vertex_data.append(vertices); - index_data.append(indices); - } - - y_offset += line_height; - x_offset = 0.0; - } - - (vertex_data, index_data) - } - - fn find_priority_camera(&self, cameras: Vec) -> usize { - let mut priority = 0; - let mut position = 0; - for (i, camera) in cameras.iter().enumerate() { - if camera.priority() < priority { - priority = camera.priority(); - position = i; - } - } - position - } - - fn setup_camera<'b>(&mut self, cameras: Vec, scene: &'b Scene) -> (&'b Position2D, &'b Camera2D){ - let cam = cameras.get( - self.find_priority_camera( - cameras.iter().map(|e| *scene.get_component::(*e).unwrap() - ).collect::>()) - ).unwrap(); - - let camera_component = scene.get_component::(*cam).unwrap(); - let camera_position = scene.get_component::(*cam).unwrap().position(); - - let camera = RenderCamera::new( - camera_component.zoom(), - camera_component.dimensions(), - v3::new(camera_position.as_vec().x(), - camera_position.as_vec().y(), - 0.0)); - let mut camera_uniform = CameraUniform::new(); - camera_uniform.update_view_proj(&camera); - - let camera_buffer = self.device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Universal Camera Buffer"), - contents: bytemuck::cast_slice(&[camera_uniform]), - usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, - }); - - let camera_bind_group_layout = - self.device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - entries: &[wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }], - label: Some("Universal Camera Bind Group Layout"), - }); - - let camera_bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &camera_bind_group_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: camera_buffer.as_entire_binding(), - }], - label: Some("Universal Camera Bind Group"), - }); - - self.camera = camera; - self.camera_buffer = camera_buffer; - self.camera_uniform = camera_uniform; - self.camera_bind_group = camera_bind_group; - - (camera_position, camera_component) - } - - /// A function to automatically render all the entities of the `Scene` struct. - /// The entities must have the `Render2D` and `Transform2D` components to be rendered as well as set visible. - pub fn render_scene_2d(&mut self, scene: &Scene) { - let cameras = scene.get_entities_with(vec![Transform2D::type_id(), Camera2D::type_id()]); - - if cameras.is_empty() { - return; - } - - let entities = scene.get_entities_with(vec![Transform2D::type_id(), Render2D::type_id()]); - let texts = scene.get_entities_with(vec![Transform2D::type_id(), comet_ecs::Text::type_id()]); - - self.setup_camera(cameras, scene); - - let mut vertex_buffer: Vec = Vec::new(); - let mut index_buffer: Vec = Vec::new(); - - for entity in entities { - let renderer_component = scene.get_component::(entity).unwrap(); - let transform_component = scene.get_component::(entity).unwrap(); - - if renderer_component.is_visible() { - let world_position = transform_component.position().clone(); - let rotation_angle = transform_component.rotation().to_radians(); - - let mut t_region: Option<&TextureRegion> = None; - match self.get_texture_region(renderer_component.get_texture().to_string()) { - Some(texture_region) => { - t_region = Some(texture_region); - }, - None => continue, - } - let region = t_region.unwrap(); - let (dim_x, dim_y) = region.dimensions(); - - let half_width = dim_x as f32 * 0.5; - let half_height = dim_y as f32 * 0.5; - - let buffer_size = vertex_buffer.len() as u16; - - let world_corners = [ - (-half_width, half_height), - (-half_width, -half_height), - (half_width, -half_height), - (half_width, half_height), - ]; - - let cos_angle = rotation_angle.cos(); - let sin_angle = rotation_angle.sin(); - - let mut rotated_world_corners = [(0.0f32, 0.0f32); 4]; - for i in 0..4 { - let (x, y) = world_corners[i]; - rotated_world_corners[i] = ( - x * cos_angle - y * sin_angle + world_position.x(), - x * sin_angle + y * cos_angle + world_position.y() - ); - } - - let mut screen_corners = [(0.0f32, 0.0f32); 4]; - for i in 0..4 { - screen_corners[i] = ( - rotated_world_corners[i].0 / self.config().width as f32, - rotated_world_corners[i].1 / self.config().height as f32 - ); - } - - vertex_buffer.append(&mut vec![ - Vertex :: new ( [screen_corners[0].0, screen_corners[0].1, 0.0], [region.u0(), region.v0()], [1.0, 1.0, 1.0, 1.0] ), - Vertex :: new ( [screen_corners[1].0, screen_corners[1].1, 0.0], [region.u0(), region.v1()], [1.0, 1.0, 1.0, 1.0] ), - Vertex :: new ( [screen_corners[2].0, screen_corners[2].1, 0.0], [region.u1(), region.v1()], [1.0, 1.0, 1.0, 1.0] ) , - Vertex :: new ( [screen_corners[3].0, screen_corners[3].1, 0.0], [region.u1(), region.v0()], [1.0, 1.0, 1.0, 1.0] ) - ]); - - index_buffer.append(&mut vec![ - 0 + buffer_size, 1 + buffer_size, 3 + buffer_size, - 1 + buffer_size, 2 + buffer_size, 3 + buffer_size - ]); - } - } - - for text in texts { - let component = scene.get_component::(text).unwrap(); - let transform = scene.get_component::(text).unwrap(); - - if component.is_visible() { - let (vertices, indices) = self.add_text_to_buffers(component.content().to_string(), component.font().to_string(), component.font_size(), p2::from_vec(transform.position().as_vec()), component.color().to_wgpu()); - let draw = self.draw_info.iter_mut().find(|d| d.name() == &format!("{}", component.font())).unwrap(); - draw.update_vertex_buffer(&self.device, &self.queue, vertices); - draw.update_index_buffer(&self.device, &self.queue, indices); - } - } - - self.set_buffers(vertex_buffer, index_buffer); - } - - fn sort_entities_by_position(&self, entity_data: Vec<(usize, Position2D)>) -> Vec { - let mut sorted_entities: Vec = vec![]; - - let mut entity_data = entity_data.clone(); - entity_data.sort_by(|a, b| a.1.x().partial_cmp(&b.1.x()).unwrap()); - - for (i, _) in entity_data { - sorted_entities.push(i); - } - - sorted_entities - } - - pub fn update(&mut self) -> f32 { - let now = Instant::now(); - self.delta_time = now.duration_since(self.last_frame_time).as_secs_f32(); // Time delta in seconds - self.last_frame_time = now; - self.delta_time - } - - pub fn render(&mut self) -> Result<(), wgpu::SurfaceError> { - let output = self.surface.get_current_texture()?; - let output_view = output.texture.create_view(&wgpu::TextureViewDescriptor::default()); - - let mut encoder = self - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("Render Encoder"), - }); - - { - let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("Universal Render Pass"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &output_view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(self.clear_color), - store: wgpu::StoreOp::Store, - }, - })], - depth_stencil_attachment: None, - occlusion_query_set: None, - timestamp_writes: None, - }); - - render_pass.set_pipeline(&self.universal_render_pipeline); - - for i in 0..self.draw_info.len() { - render_pass.set_bind_group(0, self.draw_info[i].texture(), &[]); - render_pass.set_bind_group(1, &self.camera_bind_group, &[]); - render_pass.set_vertex_buffer(0, self.draw_info[i].vertex_buffer().slice(..)); - render_pass.set_index_buffer(self.draw_info[i].index_buffer().slice(..), wgpu::IndexFormat::Uint16); - render_pass.draw_indexed(0..self.draw_info[i].num_indices(), 0, 0..1); - } - } - - self.queue.submit(iter::once(encoder.finish())); - output.present(); - Ok(()) - } + pub fn new(window: Arc, clear_color: Option) -> Renderer2D<'a> { + let size = window.inner_size(); //PhysicalSize::::new(1920, 1080); + + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { + backends: wgpu::Backends::PRIMARY, + ..Default::default() + }); + + let surface = instance.create_surface(window).unwrap(); + + let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::default(), + compatible_surface: Some(&surface), + force_fallback_adapter: false, + })) + .unwrap(); + + let (device, queue) = pollster::block_on(adapter.request_device( + &wgpu::DeviceDescriptor { + label: None, + required_features: wgpu::Features::empty(), + required_limits: wgpu::Limits::default(), + memory_hints: Default::default(), + }, + None, // Trace path + )) + .unwrap(); + + let surface_caps = surface.get_capabilities(&adapter); + let surface_format = surface_caps + .formats + .iter() + .copied() + .find(|f| f.is_srgb()) + .unwrap_or(surface_caps.formats[0]); + let config = wgpu::SurfaceConfiguration { + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + format: surface_format, + width: size.width, + height: size.height, + present_mode: surface_caps.present_modes[0], + alpha_mode: surface_caps.alpha_modes[0], + view_formats: vec![], + desired_maximum_frame_latency: 2, + }; + + let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("Universal Shader"), + source: wgpu::ShaderSource::Wgsl(include_str!("base2d.wgsl").into()), + }); + + let graphic_resource_manager = GraphicResourceManager::new(); + + let texture_bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + multisampled: false, + view_dimension: wgpu::TextureViewDimension::D2, + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, + }, + ], + label: Some("Universal Texture Bind Group Layout"), + }); + + let camera = RenderCamera::new(1.0, v2::new(2.0, 2.0), v3::new(0.0, 0.0, 0.0)); + + let mut camera_uniform = CameraUniform::new(); + camera_uniform.update_view_proj(&camera); + + let camera_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Camera Buffer"), + contents: bytemuck::cast_slice(&[camera_uniform]), + usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, + }); + + let camera_bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }], + label: Some("Universal Camera Bind Group Layout"), + }); + + let camera_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &camera_bind_group_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: camera_buffer.as_entire_binding(), + }], + label: Some("Universal Camera Bind Group"), + }); + + let render_pipeline_layout = + device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Universal Render Pipeline Layout"), + bind_group_layouts: &[&texture_bind_group_layout, &camera_bind_group_layout], + push_constant_ranges: &[], + }); + + let universal_render_pipeline = + device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Universal Render Pipeline"), + layout: Some(&render_pipeline_layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "vs_main", + buffers: &[Vertex::desc()], + compilation_options: Default::default(), + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "fs_main", + targets: &[Some(wgpu::ColorTargetState { + format: config.format, + blend: Some(wgpu::BlendState { + color: wgpu::BlendComponent { + src_factor: wgpu::BlendFactor::SrcAlpha, + dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, + operation: wgpu::BlendOperation::Add, + }, + alpha: wgpu::BlendComponent { + src_factor: wgpu::BlendFactor::One, + dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, + operation: wgpu::BlendOperation::Add, + }, + }), + write_mask: wgpu::ColorWrites::ALL, + })], + compilation_options: Default::default(), + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + strip_index_format: None, + front_face: wgpu::FrontFace::Ccw, + cull_mode: Some(wgpu::Face::Back), + polygon_mode: wgpu::PolygonMode::Fill, + unclipped_depth: false, + conservative: false, + }, + depth_stencil: None, + multisample: wgpu::MultisampleState { + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, + }, + multiview: None, + cache: None, + }); + + let clear_color = match clear_color { + Some(color) => color.to_wgpu(), + None => wgpu::Color { + r: 0.0, + g: 0.0, + b: 0.0, + a: 1.0, + }, + }; + + let texture_sampler = device.create_sampler(&wgpu::SamplerDescriptor { + address_mode_u: wgpu::AddressMode::ClampToEdge, + address_mode_v: wgpu::AddressMode::ClampToEdge, + address_mode_w: wgpu::AddressMode::ClampToEdge, + mag_filter: wgpu::FilterMode::Linear, + min_filter: wgpu::FilterMode::Linear, + mipmap_filter: wgpu::FilterMode::Linear, + lod_min_clamp: 0.0, + lod_max_clamp: 100.0, + compare: None, + anisotropy_clamp: 16, + border_color: None, + ..Default::default() + }); + + let mut draw_info: Vec = Vec::new(); + draw_info.push(DrawInfo::new( + "Universal Draw".to_string(), + &device, + &Texture::from_image( + &device, + &queue, + &image::DynamicImage::new(1, 1, image::ColorType::Rgba8), + None, + false, + ) + .unwrap(), + &texture_bind_group_layout, + &texture_sampler, + vec![], + vec![], + )); + + Self { + surface, + device, + queue, + config, + size, + universal_render_pipeline, + texture_bind_group_layout, + texture_sampler, + camera, + camera_uniform, + camera_buffer, + camera_bind_group, + draw_info, + graphic_resource_manager, + delta_time: 0.0, + last_frame_time: Instant::now(), + clear_color, + } + } + + pub fn dt(&self) -> f32 { + self.delta_time + } + + pub fn config(&self) -> &wgpu::SurfaceConfiguration { + &self.config + } + + pub fn size(&self) -> PhysicalSize { + self.size + } + + pub fn resize(&mut self, new_size: PhysicalSize) { + if new_size.width > 0 && new_size.height > 0 { + self.size = new_size; + self.config.width = new_size.width; + self.config.height = new_size.height; + self.surface.configure(&self.device, &self.config); + } + } + + pub fn add_draw_call(&mut self, draw_call: String, texture: Texture) { + let draw_info = DrawInfo::new( + draw_call, + &self.device, + &texture, + &self.texture_bind_group_layout, + &self.texture_sampler, + vec![], + vec![], + ); + self.draw_info.push(draw_info); + } + + /// A function that loads a shader from the resources/shaders dir given the full name of the shader file. + pub fn load_shader(&mut self, file_name: &str, shader_stage: Option) { + self.graphic_resource_manager + .load_shader( + shader_stage, + ((Self::get_project_root() + .unwrap() + .as_os_str() + .to_str() + .unwrap() + .to_string() + + "/res/shaders/") + .as_str() + .to_string() + + file_name) + .as_str(), + &self.device, + ) + .unwrap(); + info!("Shader ({}) loaded successfully", file_name); + } + + /// A function that loads a list of shaders from the given filenames out of the resources/shaders dir + pub fn load_shaders(&mut self, shader_stages: Vec>, file_names: Vec<&str>) { + for (i, file_name) in file_names.iter().enumerate() { + self.load_shader(file_name, shader_stages[i].clone()); + info!("Shader ({}) loaded successfully", file_name); + } + } + + /// A function that applies a shader to the entire surface of the `Renderer2D` if the shader is loaded. + pub fn apply_shader(&mut self, shader: &str) { + let module = match self.graphic_resource_manager.get_shader(shader) { + Some(module) => module, + None => { + error!("Shader not found"); + return; + } + }; + } + + /// A function to revert back to the base shader of the `Renderer2D` + pub fn apply_base_shader(&mut self) { + todo!() + } + + /// A function to load a TTF font from the specified path + pub fn load_font(&mut self, path: &str, size: f32) { + self.graphic_resource_manager.load_font(path, size); + let atlas = self + .graphic_resource_manager + .fonts() + .iter() + .find(|f| f.name() == path) + .unwrap() + .glyphs() + .atlas(); + let font_info = DrawInfo::new( + format!("{}", path), + &self.device, + &Texture::from_image(&self.device, &self.queue, atlas, None, false).unwrap(), + &self.texture_bind_group_layout, + &self.texture_sampler, + vec![], + vec![], + ); + + self.draw_info.push(font_info); + } + + /// An interface for getting the location of the texture in the texture atlas. + pub fn get_texture_region(&self, texture_path: String) -> Option<&TextureRegion> { + if !self + .graphic_resource_manager + .texture_atlas() + .textures() + .contains_key(&texture_path) + { + error!("Texture {} not found in atlas", &texture_path); + } + self.graphic_resource_manager + .texture_atlas() + .textures() + .get(&texture_path) + } + + /// A function to get the `TextureRegion` of a specified glyph + pub fn get_glyph_region(&self, glyph: char, font: String) -> &TextureRegion { + let font_atlas = self + .graphic_resource_manager + .fonts() + .iter() + .find(|f| f.name() == font) + .unwrap(); + font_atlas.get_glyph(glyph).unwrap() + } + + /// A function that allows you to set the texture atlas with a list of paths to the textures. + /// The old texture atlas will be replaced with the new one. + pub fn set_texture_atlas_by_paths(&mut self, paths: Vec) { + self.graphic_resource_manager.create_texture_atlas(paths); + self.draw_info[0].set_texture( + &self.device, + &self.texture_bind_group_layout, + &Texture::from_image( + &self.device, + &self.queue, + self.graphic_resource_manager.texture_atlas().atlas(), + None, + false, + ) + .unwrap(), + ); + } + + fn set_texture_atlas(&mut self, texture_atlas: Texture) { + self.draw_info[0].set_texture( + &self.device, + &self.texture_bind_group_layout, + &texture_atlas, + ); + } + + fn get_project_root() -> std::io::Result { + let path = std::env::current_dir()?; + let mut path_ancestors = path.as_path().ancestors(); + + while let Some(p) = path_ancestors.next() { + let has_cargo = std::fs::read_dir(p)? + .into_iter() + .any(|p| p.unwrap().file_name() == std::ffi::OsString::from("Cargo.lock")); + if has_cargo { + return Ok(PathBuf::from(p)); + } + } + Err(std::io::Error::new( + std::io::ErrorKind::NotFound, + "Ran out of places to find Cargo.toml", + )) + } + + /// A function that takes all the textures inside the resources/textures folder and creates a texture atlas from them. + pub fn initialize_atlas(&mut self) { + let texture_path = "res/textures/".to_string(); + let mut paths: Vec = Vec::new(); + + for path in std::fs::read_dir( + Self::get_project_root() + .unwrap() + .as_os_str() + .to_str() + .unwrap() + .to_string() + + "/res/textures", + ) + .unwrap() + { + paths.push(texture_path.clone() + path.unwrap().file_name().to_str().unwrap()); + } + + self.set_texture_atlas_by_paths(paths); + } + + /// A function that writes on the buffers and sets the geometry and index buffer of the `Renderer2D` with the given data. + fn set_buffers(&mut self, new_geometry_buffer: Vec, new_index_buffer: Vec) { + self.draw_info[0].update_vertex_buffer(&self.device, &self.queue, new_geometry_buffer); + self.draw_info[0].update_index_buffer(&self.device, &self.queue, new_index_buffer); + } + + fn add_text_to_buffers( + &self, + text: String, + font: String, + size: f32, + position: p2, + color: wgpu::Color, + ) -> (Vec, Vec) { + let vert_color = [ + color.r as f32, + color.g as f32, + color.b as f32, + color.a as f32, + ]; + + let screen_position = p2::new( + position.x() / self.config.width as f32, + position.y() / self.config.height as f32, + ); + let scale_factor = size + / self + .graphic_resource_manager + .fonts() + .iter() + .find(|f| f.name() == font) + .unwrap() + .size(); + + let line_height = (self + .graphic_resource_manager + .fonts() + .iter() + .find(|f| f.name() == font) + .unwrap() + .line_height() + / self.config.height as f32) + * scale_factor; + let lines = text + .split("\n") + .map(|s| { + s.split("") + .map(|escape| match escape { + _ if escape == "\t" => " ", + _ => escape, + }) + .collect::() + }) + .collect::>(); + + let mut x_offset = 0.0; + let mut y_offset = 0.0; + + let mut vertex_data = Vec::new(); + let mut index_data = Vec::new(); + + for line in lines { + for c in line.chars() { + let region = self.get_glyph_region(c, font.clone()); + let (dim_x, dim_y) = region.dimensions(); + + let w = (dim_x as f32 / self.config.width as f32) * scale_factor; + let h = (dim_y as f32 / self.config.height as f32) * scale_factor; + + let offset_x_px = (region.offset_x() / self.config.width as f32) * scale_factor; + let offset_y_px = (region.offset_y() / self.config.height as f32) * scale_factor; + + let glyph_left = screen_position.x() + x_offset + offset_x_px; + let glyph_top = screen_position.y() - offset_y_px - y_offset; + let glyph_right = glyph_left + w; + let glyph_bottom = glyph_top - h; + + let vertices: &mut Vec = &mut vec![ + Vertex::new( + [glyph_left, glyph_top, 0.0], + [region.u0(), region.v0()], + vert_color, + ), + Vertex::new( + [glyph_left, glyph_bottom, 0.0], + [region.u0(), region.v1()], + vert_color, + ), + Vertex::new( + [glyph_right, glyph_bottom, 0.0], + [region.u1(), region.v1()], + vert_color, + ), + Vertex::new( + [glyph_right, glyph_top, 0.0], + [region.u1(), region.v0()], + vert_color, + ), + ]; + + let buffer_size = vertex_data.len() as u16; + let indices: &mut Vec = &mut vec![ + buffer_size, + buffer_size + 1, + buffer_size + 3, + buffer_size + 1, + buffer_size + 2, + buffer_size + 3, + ]; + + x_offset += (region.advance() / self.config.width as f32) * scale_factor; + + vertex_data.append(vertices); + index_data.append(indices); + } + + y_offset += line_height; + x_offset = 0.0; + } + + (vertex_data, index_data) + } + + fn find_priority_camera(&self, cameras: Vec) -> usize { + let mut priority = 0; + let mut position = 0; + for (i, camera) in cameras.iter().enumerate() { + if camera.priority() < priority { + priority = camera.priority(); + position = i; + } + } + position + } + + fn setup_camera<'b>( + &mut self, + cameras: Vec, + scene: &'b Scene, + ) -> (&'b Position2D, &'b Camera2D) { + let cam = cameras + .get( + self.find_priority_camera( + cameras + .iter() + .map(|e| *scene.get_component::(*e).unwrap()) + .collect::>(), + ), + ) + .unwrap(); + + let camera_component = scene.get_component::(*cam).unwrap(); + let camera_position = scene.get_component::(*cam).unwrap().position(); + + let camera = RenderCamera::new( + camera_component.zoom(), + camera_component.dimensions(), + v3::new( + camera_position.as_vec().x(), + camera_position.as_vec().y(), + 0.0, + ), + ); + let mut camera_uniform = CameraUniform::new(); + camera_uniform.update_view_proj(&camera); + + let camera_buffer = self + .device + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Universal Camera Buffer"), + contents: bytemuck::cast_slice(&[camera_uniform]), + usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, + }); + + let camera_bind_group_layout = + self.device + .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }], + label: Some("Universal Camera Bind Group Layout"), + }); + + let camera_bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &camera_bind_group_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: camera_buffer.as_entire_binding(), + }], + label: Some("Universal Camera Bind Group"), + }); + + self.camera = camera; + self.camera_buffer = camera_buffer; + self.camera_uniform = camera_uniform; + self.camera_bind_group = camera_bind_group; + + (camera_position, camera_component) + } + + /// A function to automatically render all the entities of the `Scene` struct. + /// The entities must have the `Render2D` and `Transform2D` components to be rendered as well as set visible. + pub fn render_scene_2d(&mut self, scene: &Scene) { + let cameras = scene.get_entities_with(vec![Transform2D::type_id(), Camera2D::type_id()]); + + if cameras.is_empty() { + return; + } + + let entities = scene.get_entities_with(vec![Transform2D::type_id(), Render2D::type_id()]); + let texts = + scene.get_entities_with(vec![Transform2D::type_id(), comet_ecs::Text::type_id()]); + + self.setup_camera(cameras, scene); + + let mut vertex_buffer: Vec = Vec::new(); + let mut index_buffer: Vec = Vec::new(); + + for entity in entities { + let renderer_component = scene.get_component::(entity).unwrap(); + let transform_component = scene.get_component::(entity).unwrap(); + + if renderer_component.is_visible() { + let world_position = transform_component.position().clone(); + let rotation_angle = transform_component.rotation().to_radians(); + + let mut t_region: Option<&TextureRegion> = None; + match self.get_texture_region(renderer_component.get_texture().to_string()) { + Some(texture_region) => { + t_region = Some(texture_region); + } + None => continue, + } + let region = t_region.unwrap(); + let (dim_x, dim_y) = region.dimensions(); + + let half_width = dim_x as f32 * 0.5; + let half_height = dim_y as f32 * 0.5; + + let buffer_size = vertex_buffer.len() as u16; + + let world_corners = [ + (-half_width, half_height), + (-half_width, -half_height), + (half_width, -half_height), + (half_width, half_height), + ]; + + let cos_angle = rotation_angle.cos(); + let sin_angle = rotation_angle.sin(); + + let mut rotated_world_corners = [(0.0f32, 0.0f32); 4]; + for i in 0..4 { + let (x, y) = world_corners[i]; + rotated_world_corners[i] = ( + x * cos_angle - y * sin_angle + world_position.x(), + x * sin_angle + y * cos_angle + world_position.y(), + ); + } + + let mut screen_corners = [(0.0f32, 0.0f32); 4]; + for i in 0..4 { + screen_corners[i] = ( + rotated_world_corners[i].0 / self.config().width as f32, + rotated_world_corners[i].1 / self.config().height as f32, + ); + } + + vertex_buffer.append(&mut vec![ + Vertex::new( + [screen_corners[0].0, screen_corners[0].1, 0.0], + [region.u0(), region.v0()], + [1.0, 1.0, 1.0, 1.0], + ), + Vertex::new( + [screen_corners[1].0, screen_corners[1].1, 0.0], + [region.u0(), region.v1()], + [1.0, 1.0, 1.0, 1.0], + ), + Vertex::new( + [screen_corners[2].0, screen_corners[2].1, 0.0], + [region.u1(), region.v1()], + [1.0, 1.0, 1.0, 1.0], + ), + Vertex::new( + [screen_corners[3].0, screen_corners[3].1, 0.0], + [region.u1(), region.v0()], + [1.0, 1.0, 1.0, 1.0], + ), + ]); + + index_buffer.append(&mut vec![ + 0 + buffer_size, + 1 + buffer_size, + 3 + buffer_size, + 1 + buffer_size, + 2 + buffer_size, + 3 + buffer_size, + ]); + } + } + + for text in texts { + let component = scene.get_component::(text).unwrap(); + let transform = scene.get_component::(text).unwrap(); + + if component.is_visible() { + let (vertices, indices) = self.add_text_to_buffers( + component.content().to_string(), + component.font().to_string(), + component.font_size(), + p2::from_vec(transform.position().as_vec()), + component.color().to_wgpu(), + ); + let draw = self + .draw_info + .iter_mut() + .find(|d| d.name() == &format!("{}", component.font())) + .unwrap(); + draw.update_vertex_buffer(&self.device, &self.queue, vertices); + draw.update_index_buffer(&self.device, &self.queue, indices); + } + } + + self.set_buffers(vertex_buffer, index_buffer); + } + + fn sort_entities_by_position(&self, entity_data: Vec<(usize, Position2D)>) -> Vec { + let mut sorted_entities: Vec = vec![]; + + let mut entity_data = entity_data.clone(); + entity_data.sort_by(|a, b| a.1.x().partial_cmp(&b.1.x()).unwrap()); + + for (i, _) in entity_data { + sorted_entities.push(i); + } + + sorted_entities + } + + pub fn update(&mut self) -> f32 { + let now = Instant::now(); + self.delta_time = now.duration_since(self.last_frame_time).as_secs_f32(); // Time delta in seconds + self.last_frame_time = now; + self.delta_time + } + + pub fn render(&mut self) -> Result<(), wgpu::SurfaceError> { + let output = self.surface.get_current_texture()?; + let output_view = output + .texture + .create_view(&wgpu::TextureViewDescriptor::default()); + + let mut encoder = self + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("Render Encoder"), + }); + + { + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("Universal Render Pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &output_view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(self.clear_color), + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + occlusion_query_set: None, + timestamp_writes: None, + }); + + render_pass.set_pipeline(&self.universal_render_pipeline); + + for i in 0..self.draw_info.len() { + render_pass.set_bind_group(0, self.draw_info[i].texture(), &[]); + render_pass.set_bind_group(1, &self.camera_bind_group, &[]); + render_pass.set_vertex_buffer(0, self.draw_info[i].vertex_buffer().slice(..)); + render_pass.set_index_buffer( + self.draw_info[i].index_buffer().slice(..), + wgpu::IndexFormat::Uint16, + ); + render_pass.draw_indexed(0..self.draw_info[i].num_indices(), 0, 0..1); + } + } + + self.queue.submit(iter::once(encoder.finish())); + output.present(); + Ok(()) + } } impl<'a> Renderer for Renderer2D<'a> { - fn new(window: Arc, clear_color: Option) -> Renderer2D<'a> { - Self::new(window, clear_color) - } + fn new(window: Arc, clear_color: Option) -> Renderer2D<'a> { + Self::new(window, clear_color) + } - fn size(&self) -> PhysicalSize { - self.size() - } + fn size(&self) -> PhysicalSize { + self.size() + } - fn resize(&mut self, new_size: PhysicalSize) { - self.resize(new_size) - } + fn resize(&mut self, new_size: PhysicalSize) { + self.resize(new_size) + } - fn update(&mut self) -> f32 { - self.update() - } + fn update(&mut self) -> f32 { + self.update() + } - fn render(&mut self) -> Result<(), wgpu::SurfaceError> { - self.render() - } + fn render(&mut self) -> Result<(), wgpu::SurfaceError> { + self.render() + } } diff --git a/examples/simple_move_2d.rs b/examples/simple_move_2d.rs index f7ea151..9aaa49b 100644 --- a/examples/simple_move_2d.rs +++ b/examples/simple_move_2d.rs @@ -1,73 +1,73 @@ use comet::prelude::*; -use winit_input_helper::WinitInputHelper; use comet_input::keyboard::Key; +use winit_input_helper::WinitInputHelper; fn setup(app: &mut App, renderer: &mut Renderer2D) { - // Takes all the textures from resources/textures and puts them into a texture atlas - renderer.initialize_atlas(); + // Takes all the textures from res/textures and puts them into a texture atlas + renderer.initialize_atlas(); - let camera = app.new_entity(); - app.add_component(camera, Transform2D::new()); - app.add_component(camera, Camera2D::new(v2::new(2.0, 2.0), 1.0, 1)); + let camera = app.new_entity(); + app.add_component(camera, Transform2D::new()); + app.add_component(camera, Camera2D::new(v2::new(2.0, 2.0), 1.0, 1)); - let e1 = app.new_entity(); + let e1 = app.new_entity(); - app.add_component(e1, Transform2D::new()); + app.add_component(e1, Transform2D::new()); - let mut renderer2d = Render2D::new(); - renderer2d.set_texture(r"resources/textures/comet_icon.png"); - renderer2d.set_visibility(true); + let mut renderer2d = Render2D::new(); + renderer2d.set_texture(r"res/textures/comet_icon.png"); + renderer2d.set_visibility(true); - app.add_component(e1, renderer2d); + app.add_component(e1, renderer2d); } fn update(app: &mut App, renderer: &mut Renderer2D, dt: f32) { - handle_input(app, dt); + handle_input(app, dt); - renderer.render_scene_2d(app.scene()); + renderer.render_scene_2d(app.scene()); } fn handle_input(app: &mut App, dt: f32) { - if app.key_held(Key::KeyW) - || app.key_held(Key::KeyA) - || app.key_held(Key::KeyS) - || app.key_held(Key::KeyD) - { - update_position( - app.input_manager().clone(), - app.get_component_mut::(1).unwrap(), - dt - ); - } + if app.key_held(Key::KeyW) + || app.key_held(Key::KeyA) + || app.key_held(Key::KeyS) + || app.key_held(Key::KeyD) + { + update_position( + app.input_manager().clone(), + app.get_component_mut::(1).unwrap(), + dt, + ); + } } fn update_position(input: WinitInputHelper, transform: &mut Transform2D, dt: f32) { - let mut direction = v2::ZERO; + let mut direction = v2::ZERO; - if input.key_held(Key::KeyW) { - direction += v2::Y; - } - if input.key_held(Key::KeyA) { - direction -= v2::X; - } - if input.key_held(Key::KeyS) { - direction -= v2::Y; - } - if input.key_held(Key::KeyD) { - direction += v2::X; - } + if input.key_held(Key::KeyW) { + direction += v2::Y; + } + if input.key_held(Key::KeyA) { + direction -= v2::X; + } + if input.key_held(Key::KeyS) { + direction -= v2::Y; + } + if input.key_held(Key::KeyD) { + direction += v2::X; + } - // If check to prevent division by zero and the comet to fly off into infinity... - if direction != v2::ZERO { - let normalized_dir = direction.normalize(); - let displacement = normalized_dir * 777.7 * dt; - transform.translate(displacement); - } + // If check to prevent division by zero and the comet to fly off into infinity... + if direction != v2::ZERO { + let normalized_dir = direction.normalize(); + let displacement = normalized_dir * 777.7 * dt; + transform.translate(displacement); + } } fn main() { - App::new() - .with_title("Simple Move 2D") - .with_preset(App2D) - .run::(setup, update); -} \ No newline at end of file + App::new() + .with_title("Simple Move 2D") + .with_preset(App2D) + .run::(setup, update); +} diff --git a/examples/simple_text.rs b/examples/simple_text.rs index b4fe829..fd88244 100644 --- a/examples/simple_text.rs +++ b/examples/simple_text.rs @@ -1,42 +1,45 @@ use comet::prelude::*; fn setup(app: &mut App, renderer: &mut Renderer2D) { - // Loading the font from the resources/fonts directory with a rendered size of 77px - renderer.load_font("./resources/fonts/PressStart2P-Regular.ttf", 77.0); + // Loading the font from the res/fonts directory with a rendered size of 77px + renderer.load_font("./res/fonts/PressStart2P-Regular.ttf", 77.0); // Setting up camera let camera = app.new_entity(); - + app.add_component(camera, Transform2D::new()); app.add_component(camera, Camera2D::new(v2::new(2.0, 2.0), 1.0, 1)); // Creating the text entity let text = app.new_entity(); app.add_component(text, Transform2D::new()); - app.add_component(text, Text::new( - "comet", // The content of the text - "./resources/fonts/PressStart2P-Regular.ttf", // The used font (right now exact to the font path) - 77.0, // Pixel size at which the font will be drawn - true, // Should the text be visible - sRgba::::from_hex("#abb2bfff") // Color of the text - )); + app.add_component( + text, + Text::new( + "comet", // The content of the text + "./res/fonts/PressStart2P-Regular.ttf", // The used font (right now exact to the font path) + 77.0, // Pixel size at which the font will be drawn + true, // Should the text be visible + sRgba::::from_hex("#abb2bfff"), // Color of the text + ), + ); } fn update(app: &mut App, renderer: &mut Renderer2D, dt: f32) { // Getting the windows size let size = renderer.size(); - + // Recalculating the position of the text every frame to ensure the same relative position let transform = app.get_component_mut::(1).unwrap(); - transform.position_mut().set_x(-((size.width-50) as f32)); - transform.position_mut().set_y((size.height-100) as f32); + transform.position_mut().set_x(-((size.width - 50) as f32)); + transform.position_mut().set_y((size.height - 100) as f32); renderer.render_scene_2d(app.scene()); } fn main() { - App::new() + App::new() .with_preset(App2D) - .with_title("Simple Text") - .run::(setup, update); -} \ No newline at end of file + .with_title("Simple Text") + .run::(setup, update); +} diff --git a/examples/textured_entity.rs b/examples/textured_entity.rs index 68e634c..633ebe2 100644 --- a/examples/textured_entity.rs +++ b/examples/textured_entity.rs @@ -1,32 +1,32 @@ use comet::prelude::*; fn setup(app: &mut App, renderer: &mut Renderer2D) { - // Creating a texture atlas from the provided textures in the vector - renderer.set_texture_atlas_by_paths(vec!["./resources/textures/comet_icon.png".to_string()]); + // Creating a texture atlas from the provided textures in the vector + renderer.set_texture_atlas_by_paths(vec!["./res/textures/comet_icon.png".to_string()]); - // Creating a camera entity - let cam = app.new_entity(); - app.add_component(cam, Transform2D::new()); - app.add_component(cam, Camera2D::new(v2::new(2.0, 2.0), 1.0, 1)); + // Creating a camera entity + let cam = app.new_entity(); + app.add_component(cam, Transform2D::new()); + app.add_component(cam, Camera2D::new(v2::new(2.0, 2.0), 1.0, 1)); - // Creating a textured entity - let e0 = app.new_entity(); - app.add_component(e0, Transform2D::new()); + // Creating a textured entity + let e0 = app.new_entity(); + app.add_component(e0, Transform2D::new()); - let mut render = Render2D::new(); - render.set_visibility(true); - render.set_texture("./resources/textures/comet_icon.png"); + let mut render = Render2D::new(); + render.set_visibility(true); + render.set_texture("./res/textures/comet_icon.png"); - app.add_component(e0, render); + app.add_component(e0, render); } fn update(app: &mut App, renderer: &mut Renderer2D, dt: f32) { - renderer.render_scene_2d(app.scene()) + renderer.render_scene_2d(app.scene()) } fn main() { - App::new() - .with_title("Textured Entity") - .with_preset(App2D) - .run::(setup, update); -} \ No newline at end of file + App::new() + .with_title("Textured Entity") + .with_preset(App2D) + .run::(setup, update); +} diff --git a/resources/fonts/PressStart2P-Regular.ttf b/res/fonts/PressStart2P-Regular.ttf similarity index 100% rename from resources/fonts/PressStart2P-Regular.ttf rename to res/fonts/PressStart2P-Regular.ttf diff --git a/resources/shaders/blacknwhite.wgsl b/res/shaders/blacknwhite.wgsl similarity index 100% rename from resources/shaders/blacknwhite.wgsl rename to res/shaders/blacknwhite.wgsl diff --git a/resources/shaders/crt.wgsl b/res/shaders/crt.wgsl similarity index 100% rename from resources/shaders/crt.wgsl rename to res/shaders/crt.wgsl diff --git a/resources/shaders/glitch.wgsl b/res/shaders/glitch.wgsl similarity index 100% rename from resources/shaders/glitch.wgsl rename to res/shaders/glitch.wgsl diff --git a/resources/textures/comet-1024.png b/res/textures/comet-1024.png similarity index 100% rename from resources/textures/comet-1024.png rename to res/textures/comet-1024.png diff --git a/resources/textures/comet-128.png b/res/textures/comet-128.png similarity index 100% rename from resources/textures/comet-128.png rename to res/textures/comet-128.png diff --git a/resources/textures/comet-16.png b/res/textures/comet-16.png similarity index 100% rename from resources/textures/comet-16.png rename to res/textures/comet-16.png diff --git a/resources/textures/comet-256.png b/res/textures/comet-256.png similarity index 100% rename from resources/textures/comet-256.png rename to res/textures/comet-256.png diff --git a/resources/textures/comet-32.png b/res/textures/comet-32.png similarity index 100% rename from resources/textures/comet-32.png rename to res/textures/comet-32.png diff --git a/resources/textures/comet-512.png b/res/textures/comet-512.png similarity index 100% rename from resources/textures/comet-512.png rename to res/textures/comet-512.png diff --git a/resources/textures/comet-64.png b/res/textures/comet-64.png similarity index 100% rename from resources/textures/comet-64.png rename to res/textures/comet-64.png diff --git a/resources/textures/comet-green-16.png b/res/textures/comet-green-16.png similarity index 100% rename from resources/textures/comet-green-16.png rename to res/textures/comet-green-16.png diff --git a/resources/textures/comet-green-32.png b/res/textures/comet-green-32.png similarity index 100% rename from resources/textures/comet-green-32.png rename to res/textures/comet-green-32.png diff --git a/resources/textures/comet-green-64.png b/res/textures/comet-green-64.png similarity index 100% rename from resources/textures/comet-green-64.png rename to res/textures/comet-green-64.png diff --git a/resources/textures/comet_icon.png b/res/textures/comet_icon.png similarity index 100% rename from resources/textures/comet_icon.png rename to res/textures/comet_icon.png