feat: added 2D scene rendering and initialization of the texture atlas

This commit is contained in:
lisk77 2024-10-28 15:17:59 +01:00
parent 878e220249
commit 780365aeb8
12 changed files with 280 additions and 122 deletions

View file

@ -8,6 +8,7 @@ comet_ecs = { path = "../comet_ecs" }
comet_renderer = { path = "../comet_renderer" }
comet_resources = { path = "../comet_resources" }
comet_colors = { path = "../comet_colors" }
comet_log = { path = "../comet_log" }
winit = { version = "0.29", features = ["rwh_05"] }
env_logger = "0.10"
@ -16,6 +17,7 @@ wgpu = { version = "22.0"}
log = "0.4.22"
anyhow = "1.0.89"
bytemuck = "1.18.0"
chrono = "0.4.0"
[dependencies.image]
version = "0.24"

View file

@ -1,6 +1,6 @@
use std::sync::Arc;
use comet_ecs::World;
use comet_resources::ResourceManager;
use comet_ecs::{Component, ComponentSet, Render, Renderer2D, Transform2D, World};
use comet_resources::{ResourceManager, Vertex};
use comet_renderer::{Renderer};
use winit::{
@ -11,7 +11,7 @@ use winit::{
};
use comet_colors::LinearRgba;
use comet_ecs::math::Point3;
use log::warn;
use comet_log::*;
use winit::dpi::{LogicalSize, PhysicalSize};
pub enum ApplicationType {
@ -74,6 +74,51 @@ impl<'a> App<'a> {
Some(Icon::from_rgba(rgba_image.into_raw(), width, height).unwrap())
}
pub fn render_scene_2d(&self, renderer: &mut Renderer) {
let entities = self.world.get_entities_with(ComponentSet::from_ids(vec![Renderer2D::type_id()]));
let mut vertex_buffer: Vec<Vertex> = Vec::new();
let mut index_buffer: Vec<u16> = Vec::new();
for entity in entities {
let renderer_component = self.world().get_component::<Renderer2D>(entity as usize);
let transform_component = self.world().get_component::<Transform2D>(entity as usize);
if renderer_component.is_visible() {
//renderer.draw_texture_at(renderer_component.get_texture(), Point3::new(transform_component.position().x(), transform_component.position().y(), 0.0));
let position = transform_component.position();
let region = renderer.get_texture(renderer_component.get_texture().to_string());
let (dim_x, dim_y) = region.dimensions();
let (bound_x, bound_y) =
((dim_x as f32/ renderer.config().width as f32) * 0.5, (dim_y as f32/ renderer.config().height as f32) * 0.5);
let buffer_size = vertex_buffer.len() as u16;
vertex_buffer.append(&mut vec![
Vertex :: new ( [-bound_x + position.x(), bound_y + position.y(), 0.0], [region.x0(), region.y0()] ),
Vertex :: new ( [-bound_x + position.x(), -bound_y + position.y(), 0.0], [region.x0(), region.y1()] ),
Vertex :: new ( [ bound_x + position.x(), -bound_y + position.y(), 0.0], [region.x1(), region.y1()] ) ,
Vertex :: new ( [ bound_x + position.x(), bound_y + position.y(), 0.0], [region.x1(), region.y0()] )
]);
index_buffer.append(&mut vec![
0 + buffer_size, 1 + buffer_size, 3 + buffer_size,
1 + buffer_size, 2 + buffer_size, 3 + buffer_size
]);
}
}
renderer.set_buffers(vertex_buffer, index_buffer);
/*for entity in entities {
let renderer_component = self.world.get_component::<Renderer2D>(entity as usize);
let position_component = self.world.get_component::<Transform2D>(entity as usize);
if renderer_component.is_visible() {
renderer.draw_texture_at(renderer_component.get_texture(), Point3::new(position_component.position().x(), position_component.position().y(), 0.0));
}
}*/
}
pub fn world(&self) -> &World {
&self.world
}
@ -116,12 +161,14 @@ impl<'a> App<'a> {
window.set_maximized(true);
renderer.initialize_atlas();
event_loop.run(|event, control_flow| {
if self.should_quit {
control_flow.exit()
}
game_manager(&mut self.world, &mut renderer);
self.render_scene_2d(&mut renderer);
match event {
Event::WindowEvent {
@ -161,7 +208,9 @@ impl<'a> App<'a> {
}
}
}
_ => { input_manager(event, &mut self, &mut renderer) }
_ => {
input_manager(event, &mut self, &mut renderer);
}
}
}
_ => {}

View file

@ -1,6 +1,6 @@
[package]
name = "comet_ecs"
version = "0.1.0"
version = "0.2.0"
edition = "2021"
[dependencies]

View file

@ -12,6 +12,7 @@ pub fn my_trait_derive(input: TokenStream) -> TokenStream {
// Get the name of the struct
let name = &input.ident;
let name = &input.ident;
let fields = if let Data::Struct(data) = &input.data {
match &data.fields {

View file

@ -1,4 +1,4 @@
use std::path::Path;
//use comet_resources::Vertex;
use crate::math::{
Vec2,
Vec3
@ -23,15 +23,12 @@ pub trait Component: Send + Sync + PartialEq + Default + 'static {
#[derive(Component)]
pub struct Position2D {
x: f32,
y: f32
position: Vec2
}
#[derive(Component)]
pub struct Position3D {
x: f32,
y: f32,
z: f32
position: Vec3
}
#[derive(Component)]
@ -50,83 +47,111 @@ pub struct Rotation3D {
pub struct Renderer2D {
is_visible: bool,
texture: &'static str,
scale: f32
scale: Vec2
}
impl Position2D {
pub fn from_vec(vec: Vec2) -> Self {
Self {
x: vec.x(),
y: vec.y()
position: vec
}
}
pub fn as_vec(&self) -> Vec2 {
Vec2::new(
self.x,
self.y
)
self.position
}
pub fn x(&self) -> &f32 {
&self.x
pub fn x(&self) -> f32 {
self.position.x()
}
pub fn y(&self) -> &f32 {
&self.y
pub fn y(&self) -> f32 {
self.position.y()
}
pub fn set_x(&mut self, new_x: f32) {
self.x = new_x;
self.position.set_x(new_x);
}
pub fn set_y(&mut self, new_y: f32) {
self.y = new_y;
self.position.set_y(new_y);
}
}
impl Position3D {
pub fn from_vec(vec: Vec3) -> Self {
Self {
x: vec.x(),
y: vec.y(),
z: vec.z()
position: vec
}
}
pub fn as_vec(&self) -> Vec3 {
Vec3::new(
self.x,
self.y,
self.z
)
self.position
}
pub fn x(&self) -> &f32 {
&self.x
pub fn x(&self) -> f32 {
self.position.x()
}
pub fn y(&self) -> &f32 {
&self.y
pub fn y(&self) -> f32 {
self.position.y()
}
pub fn z(&self) -> &f32 {
&self.z
pub fn z(&self) -> f32 {
self.position.z()
}
pub fn set_x(&mut self, new_x: f32) {
self.x = new_x;
self.position.set_x(new_x);
}
pub fn set_y(&mut self, new_y: f32) {
self.y = new_y;
self.position.set_y(new_y);
}
pub fn set_z(&mut self, new_z: f32) {
self.z = new_z
self.position.set_z(new_z);
}
}
pub trait Render {
fn is_visible(&self) -> bool;
fn set_visibility(&mut self, is_visible: bool);
fn get_texture(&self) -> String;
fn set_texture(&mut self, texture: &'static str);
//fn get_vertex_data(&self) -> Vec<Vertex>;
}
impl Render for Renderer2D {
fn is_visible(&self) -> bool {
self.is_visible
}
fn set_visibility(&mut self, is_visible: bool) {
self.is_visible = is_visible;
}
fn get_texture(&self) -> String {
self.texture.clone().parse().unwrap()
}
/// Use the actual file name of the texture instead of the path
/// e.g. "comet_icon.png" instead of "resources/textures/comet_icon.png"
/// The resource manager will already look in the resources/textures folder
fn set_texture(&mut self, texture: &'static str) {
self.texture = texture;
}
/*fn get_vertex_data(&self) -> Vec<Vertex> {
vec![
Vertex::new([0.0, 0.0, 0.0], [0.0, 0.0]),
Vertex::new([1.0, 0.0, 0.0], [1.0, 0.0]),
Vertex::new([1.0, 1.0, 0.0], [1.0, 1.0]),
Vertex::new([0.0, 1.0, 0.0], [0.0, 1.0])
]
}*/
}
// ##################################################
// # BUNDLES #
// ##################################################

View file

@ -411,6 +411,17 @@ impl ComponentStorage {
self.keys.contains(type_id)
}
pub fn contains_components(&self, component_set: ComponentSet) -> bool {
let mut contains = true;
for type_id in component_set.set.iter() {
if !self.keys.contains(type_id) {
contains = false;
break;
}
}
contains
}
pub fn get<T: Component + 'static>(&self) -> Option<&SparseSet> {
self.components.get(*self.index_map.get(&T::type_id()).unwrap())
}

View file

@ -125,6 +125,25 @@ impl World {
self.archetypes.create_archetype(components);
}
fn remove_archetype(&mut self, components: ComponentSet) {
self.archetypes.remove_archetype(&components);
}
fn remove_archetype_subsets(&mut self, components: ComponentSet) {
let component_sets = self.archetypes.component_sets();
let keys: Vec<ComponentSet> = component_sets.iter()
.enumerate()
.filter_map(|(i, &ref elem)| if elem.is_subset(&components) { Some(i) } else { None })
.collect::<Vec<usize>>()
.iter()
.map(|index| component_sets[*index].clone())
.collect::<Vec<ComponentSet>>();
for key in keys {
self.remove_archetype(key.clone());
}
}
fn add_entity_to_archetype(&mut self, entity_id: u32, components: ComponentSet) {
self.archetypes.add_entity_to_archetype(&components, entity_id);
}
@ -159,6 +178,7 @@ impl World {
pub fn register_component<T: Component + 'static>(&mut self) {
self.components.register_component::<T>(self.entities.len());
self.create_archetype(ComponentSet::from_ids(vec![T::type_id()]));
info!(format!("Registered component: {}", T::type_name()));
}
@ -177,8 +197,10 @@ impl World {
if !self.archetypes.contains_archetype(&self.get_component_set(entity_id)) {
self.create_archetype(self.get_component_set(entity_id));
}
self.add_entity_to_archetype(entity_id as u32, self.get_component_set(entity_id));
self.add_entity_to_archetype(entity_id as u32, ComponentSet::from_ids(vec![T::type_id()]));
if self.get_component_set(entity_id) != ComponentSet::from_ids(vec![T::type_id()]) {
self.add_entity_to_archetype(entity_id as u32, self.get_component_set(entity_id));
}
info!(format!("Added component {} to entity {}", T::type_name(), entity_id));
debug!(format!("{:?}", self.archetypes));
}
@ -191,7 +213,7 @@ impl World {
pub fn get_component<T: Component + 'static>(&self, entity_id: usize) -> &T {
assert_ne!(self.entities.get(entity_id), None, "There is no entity with this ID ({}) in the world!", entity_id);
assert!(self.components.get_component::<T>(entity_id) != None, "There is no component {} bound to the entity {} in the world!", T::type_name(), entity_id);
//assert_ne!(self.components.get_component::<T>(entity_id), None, "There is no component {} bound to the entity {} in the world!", T::type_name(), entity_id);
self.components.get_component::<T>(entity_id).unwrap()
}
@ -203,7 +225,7 @@ impl World {
pub fn get_entities_with(&self, components: ComponentSet) -> Vec<u32> {
assert!(self.archetypes.contains_archetype(&components), "The given components {:?} are not registered in the world!", components);
info!(format!("Querying entities with components: {:?}", components));
//debug!(format!("Querying entities with components: {:?}", components));
self.archetypes.get_archetype(&components).unwrap().clone()
}
}

View file

@ -15,7 +15,7 @@ pub trait InnerSpace {
/// Representation of a 2D Vector
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Vec2 {
x: f32,
@ -43,6 +43,14 @@ impl Vec2 {
self.y
}
pub fn set_x(&mut self, new_x: f32) {
self.x = new_x;
}
pub fn set_y(&mut self, new_y: f32) {
self.y = new_y;
}
pub fn length(&self) -> f32 {
(self.x * self.x + self.y * self.y).sqrt()
}
@ -123,7 +131,7 @@ impl Mul<f32> for Vec2 {
/// Representation of a 3D Vector
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Vec3 {
pub x: f32,
@ -161,6 +169,18 @@ impl Vec3 {
self.z
}
pub fn set_x(&mut self, new_x: f32) {
self.x = new_x;
}
pub fn set_y(&mut self, new_y: f32) {
self.y = new_y;
}
pub fn set_z(&mut self, new_z: f32) {
self.z = new_z;
}
pub fn into_quaternion(&self) -> Quat {
Quat {
s: 0.0,
@ -418,7 +438,7 @@ impl Mul<f32> for Vec3 {
/// Representation of a 4D Vector
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Vec4 {
x: f32,
@ -469,6 +489,22 @@ impl Vec4 {
self.w
}
pub fn set_x(&mut self, new_x: f32) {
self.x = new_x;
}
pub fn set_y(&mut self, new_y: f32) {
self.y = new_y;
}
pub fn set_z(&mut self, new_z: f32) {
self.z = new_z;
}
pub fn set_w(&mut self, new_w: f32) {
self.w = new_w;
}
pub fn xxxx(&self) -> Vec4 {
Vec4 {
x: self.x,

View file

@ -20,6 +20,7 @@ tobj = { version = "3.2", default-features = false, features = ["async"]}
wgpu = { version = "22.0"}
winit = { version = "0.29", features = ["rwh_05"] }
instant = "0.1"
chrono = "0.4.0"
[dependencies.image]
version = "0.24"

View file

@ -2,6 +2,7 @@ mod camera;
use core::default::Default;
use std::iter;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::Instant;
use cgmath::num_traits::FloatConst;
@ -13,67 +14,14 @@ use winit::{
dpi::PhysicalSize,
window::Window
};
use winit::dpi::Position;
use comet_colors::LinearRgba;
use comet_log::error;
use comet_math;
use comet_math::{Mat4, Point3, Vec3};
use comet_resources::{ResourceManager, texture, Vertex, Texture};
use comet_resources::texture_atlas::TextureRegion;
use crate::camera::{Camera, CameraUniform};
// RAINBOW TRIANGLE
/*const VERTICES: &[Vertex] = &[
Vertex :: new ( [0.0, 0.5, 0.0], [1.0, 0.0, 0.0] ),
Vertex :: new ( [-0.5, -0.5, 0.0], [0.0, 1.0, 0.0] ),
Vertex :: new ( [0.5, -0.5, 0.0], [0.0, 0.0, 1.0] ),
];*/
// RAW PENTAGON
/*const VERTICES: &[Vertex] = &[
Vertex :: new ( [-0.0868241, 0.49240386, 0.0], [0.5, 0.0, 0.5] ), // A
Vertex :: new ( [-0.49513406, 0.06958647, 0.0], [0.5, 0.0, 0.5] ), // B
Vertex :: new ( [-0.21918549, -0.44939706, 0.0], [0.5, 0.0, 0.5] ), // C
Vertex :: new ( [0.35966998, -0.3473291, 0.0], [0.5, 0.0, 0.5] ), // D
Vertex :: new ( [0.44147372, 0.2347359, 0.0], [0.5, 0.0, 0.5] ), // E
];*/
// RAINBOW PENTAGON
/*const VERTICES: &[Vertex] = &[
Vertex :: new ( [-0.0868241, 0.49240386, 0.0], [1.0, 0.0, 0.0] ), // A
Vertex :: new ( [-0.49513406, 0.06958647, 0.0], [0.0, 0.35, 1.0] ), // B
Vertex :: new ( [-0.21918549, -0.44939706, 0.0], [0.2, 1.0, 0.2] ), // C
Vertex :: new ( [0.35966998, -0.3473291, 0.0], [1.0, 0.85, 0.2] ), // D
Vertex :: new ( [0.44147372, 0.2347359, 0.0], [1.0, 0.2, 0.6] ), // E
];
const INDICES: &[u16] = &[
0, 1, 4,
1, 2, 4,
2, 3, 4,
];*/
// RAW QUAD
/*const VERTICES: &[Vertex] = &[
Vertex :: new ( [-0.5, 0.5, 0.0], [0.0, 0.0, 1.0] ),
Vertex :: new ( [-0.5, -0.5, 0.0], [0.0, 1.0, 0.0] ),
Vertex :: new ( [0.5, -0.5, 0.0], [1.0, 0.0, 0.0] ),
Vertex :: new ( [0.5, 0.5, 0.0], [1.0, 0.0, 1.0] )
];
const INDICES: &[u16] = &[
0, 1, 3,
1, 2, 3
];*/
/*
vec![
Vertex :: new ( [-0.1, 0.1, 0.0], [0.0, 0.0] ),
Vertex :: new ( [-0.1, -0.1, 0.0], [0.0, 1.0] ),
Vertex :: new ( [0.1, -0.1, 0.0], [1.0, 1.0] ),
Vertex :: new ( [0.1, 0.1, 0.0], [1.0, 0.0] ),
], vec![
0, 1, 3,
1, 2, 3
]
*/
pub struct Projection {
aspect: f32,
@ -410,6 +358,10 @@ impl<'a> Renderer<'a> {
self.deltatime
}
pub fn config(&self) -> &wgpu::SurfaceConfiguration {
&self.config
}
fn vertex_data_mut(&mut self) -> &mut Vec<Vertex> {
&mut self.vertex_data
}
@ -418,6 +370,11 @@ impl<'a> Renderer<'a> {
&mut self.index_data
}
pub fn get_texture(&self, texture_path: String) -> &TextureRegion {
assert!(self.resource_manager.texture_atlas().textures().contains_key(&texture_path), "Texture not found in atlas");
self.resource_manager.texture_atlas().textures().get(&texture_path).unwrap()
}
fn create_rectangle(&self, width: f32, height: f32) -> Vec<Vertex> {
let (bound_x, bound_y) =
((width/ self.config.width as f32) * 0.5, (height/ self.config.height as f32) * 0.5);
@ -512,7 +469,37 @@ impl<'a> Renderer<'a> {
self.diffuse_bind_group = diffuse_bind_group;
}
pub(crate) fn set_buffers(&mut self, new_vertex_buffer: Vec<Vertex>, new_index_buffer: Vec<u16>) {
pub fn get_project_root() -> std::io::Result<PathBuf> {
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"))
}
pub fn initialize_atlas(&mut self) {
let texture_path = "resources/textures/".to_string();
let mut paths: Vec<String> = 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());
}
error!(format!("{:?}", paths));
self.set_texture_atlas(paths);
}
pub fn set_buffers(&mut self, new_vertex_buffer: Vec<Vertex>, new_index_buffer: Vec<u16>) {
match new_vertex_buffer == self.vertex_data {
true => return,
false => {
@ -539,7 +526,7 @@ impl<'a> Renderer<'a> {
}
}
pub(crate) fn push_to_buffers(&mut self, new_vertex_buffer: &mut Vec<Vertex>, new_index_buffer: &mut Vec<u16>) {
pub fn push_to_buffers(&mut self, new_vertex_buffer: &mut Vec<Vertex>, new_index_buffer: &mut Vec<u16>) {
self.vertex_data.append(new_vertex_buffer);
self.index_data.append(new_index_buffer);
@ -577,14 +564,12 @@ impl<'a> Renderer<'a> {
self.num_indices = self.index_data.len() as u32;
}
pub fn draw_texture_at(&mut self, texture_path: &str, position: Point3) {
let region = self.resource_manager.texture_locations().get(texture_path).unwrap();
pub fn draw_texture_at(&mut self, texture_path: String, position: Point3) {
let region = self.resource_manager.texture_locations().get(&texture_path).unwrap();
let (dim_x, dim_y) = region.dimensions();
let (bound_x, bound_y) =
((dim_x as f32/ self.config.width as f32) * 0.5, (dim_y as f32/ self.config.height as f32) * 0.5);
/*let bound_x = dim_x as f32/ self.config.width as f32 * 0.5;
let bound_y = bound_x;*/
let vertices: &mut Vec<Vertex> = &mut vec![
Vertex :: new ( [-bound_x + position.x(), bound_y + position.y(), 0.0 + position.z()], [region.x0(), region.y0()] ),

View file

@ -3,7 +3,7 @@ use std::path::Path;
use chrono::Local;
use std::time::Instant;
use image::{DynamicImage, GenericImage, GenericImageView, ImageFormat};
use comet_log::info;
use comet_log::*;
use wgpu::{Device, FilterMode, TextureFormat, TextureUsages};
use crate::Texture;
@ -165,6 +165,7 @@ impl TextureAtlas {
//base.save_with_format(output_path, ImageFormat::Png).expect("Failed to save texture atlas");
info!("Texture atlas created!");
debug!(format!("{:?}", regions));
/*let t1 = Instant::now();
let delta = t1.duration_since(t0);

View file

@ -10,30 +10,55 @@ use comet::{
log::*
};
use winit::event::{WindowEvent};
use comet_ecs::{Component, ComponentSet, Transform2D};
use comet_ecs::{Component, ComponentSet, Render, Renderer2D, Transform2D};
use comet_input::mouse::{mouse_entered, mouse_pressed, Button};
#[derive(Component)]
struct TestComponent {
position: f32
}
fn input(event: &WindowEvent, app: &mut App, renderer: &mut Renderer) {
match event {
_ if key_pressed(event, Key::KeyI) => app.world_mut().register_component::<TestComponent>(),
_ if key_pressed(event, Key::Escape) => app.quit(),
_ if key_pressed(event, Key::KeyC) => { renderer.clear_buffers() }
_ if key_pressed(event, Key::KeyE) => {
let mut renderer2d = Renderer2D::new();
renderer2d.set_texture(r"resources/textures/comet_icon.png");
renderer2d.set_visibility(true);
let id = app.world_mut().new_entity();
app.world_mut().add_component::<TestComponent>(id as usize, TestComponent::new())
app.world_mut().add_component(id as usize, renderer2d.clone());
app.world_mut().add_component(0, renderer2d);
let transform = app.world_mut().get_component_mut::<Transform2D>(id as usize);
transform.position_mut().set_x(0.5);
debug!(format!("{:?}", app.world().components().get_component::<Renderer2D>(0)));
},
_ if key_pressed(event, Key::KeyR) => {
debug!(format!("{:?}", app.world().get_entities_with(ComponentSet::from_ids(vec![Transform2D::type_id(), TestComponent::type_id()]))));
_ if key_pressed(event, Key::KeyW) => {
let transform = app.world_mut().get_component_mut::<Transform2D>(0);
let y = transform.position().y();
transform.position_mut().set_y(y + 0.1);
},
_ if key_pressed(event, Key::KeyA) => {
let transform = app.world_mut().get_component_mut::<Transform2D>(0);
let x = transform.position().x();
transform.position_mut().set_x(x - 0.1);
},
_ if key_pressed(event, Key::KeyS) => {
let transform = app.world_mut().get_component_mut::<Transform2D>(0);
let y = transform.position().y();
transform.position_mut().set_y(y - 0.1);
},
_ if key_pressed(event, Key::KeyD) => {
let transform = app.world_mut().get_component_mut::<Transform2D>(0);
let x = transform.position().x();
transform.position_mut().set_x(x + 0.1);
}
_ => {}
}
}
fn update(world: &mut World, renderer: &mut Renderer) {
if !world.components().contains_components(ComponentSet::from_ids(vec![Transform2D::type_id(), Renderer2D::type_id()])) {
world.register_component::<Renderer2D>();
}
if world.entities().len() == 0 {
let id = world.new_entity();
}