summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engine/src/rendering.rs22
-rw-r--r--engine/src/rendering/main_render_pass.rs16
-rw-r--r--engine/src/rendering/shader.rs52
-rw-r--r--engine/src/rendering/shader/cursor.rs116
-rw-r--r--engine/src/rendering/shader/default.rs305
-rw-r--r--engine/src/windowing.rs9
-rw-r--r--src/main.rs12
7 files changed, 344 insertions, 188 deletions
diff --git a/engine/src/rendering.rs b/engine/src/rendering.rs
index 87c3b6f..4871f08 100644
--- a/engine/src/rendering.rs
+++ b/engine/src/rendering.rs
@@ -24,9 +24,9 @@ use crate::mesh::Mesh;
use crate::rendering::blending::Config as BlendingConfig;
use crate::rendering::object::{Id as ObjectId, Store as ObjectStore};
use crate::rendering::shader::cursor::{
+ Binding as ShaderBinding,
BindingLocation as ShaderBindingLocation,
BindingValue as ShaderBindingValue,
- Cursor as ShaderCursor,
};
use crate::rendering::shader::Program as ShaderProgram;
use crate::texture::Texture;
@@ -548,24 +548,8 @@ fn handle_window_removed(
#[derive(Default, Clone, Component)]
pub struct PendingShaderBindings
{
- pub bindings: Vec<(ShaderBindingLocation, ShaderBindingValue)>,
- pub surface_specific_bindings:
- Vec<(SurfaceId, ShaderBindingLocation, ShaderBindingValue)>,
-}
-
-impl<'a> Extend<(ShaderCursor<'a>, ShaderBindingValue)> for PendingShaderBindings
-{
- fn extend<Iter: IntoIterator<Item = (ShaderCursor<'a>, ShaderBindingValue)>>(
- &mut self,
- iter: Iter,
- )
- {
- self.bindings.extend(iter.into_iter().map(
- |(shader_cursor, shader_binding_val)| {
- (shader_cursor.into_binding_location(), shader_binding_val)
- },
- ))
- }
+ pub bindings: Vec<ShaderBinding>,
+ pub surface_specific_bindings: Vec<(SurfaceId, ShaderBinding)>,
}
#[derive(Debug, Default, Clone, Component)]
diff --git a/engine/src/rendering/main_render_pass.rs b/engine/src/rendering/main_render_pass.rs
index 7d06bf2..d83fad4 100644
--- a/engine/src/rendering/main_render_pass.rs
+++ b/engine/src/rendering/main_render_pass.rs
@@ -6,6 +6,7 @@ use crate::ecs::Query;
use crate::error;
use crate::model::{MaterialSearchResult, Model};
use crate::rendering::object::{Id as ObjectId, Store as ObjectStore};
+use crate::rendering::shader::cursor::Binding as ShaderBinding;
use crate::rendering::shader::default::ASSET_LABEL as DEFAULT_SHADER_ASSET_LABEL;
use crate::rendering::shader::{
Context as ShaderContext,
@@ -177,8 +178,10 @@ pub fn add_main_render_passes(
}
}
- for (shader_binding_loc, shader_binding_val) in
- &pending_shader_bindings.bindings
+ for ShaderBinding {
+ location: shader_binding_loc,
+ value: shader_binding_val,
+ } in &pending_shader_bindings.bindings
{
render_pass.commands.push(Command::SetShaderBinding(
shader_binding_loc.clone(),
@@ -186,8 +189,13 @@ pub fn add_main_render_passes(
));
}
- for (shader_binding_surface_id, shader_binding_loc, shader_binding_val) in
- &pending_shader_bindings.surface_specific_bindings
+ for (
+ shader_binding_surface_id,
+ ShaderBinding {
+ location: shader_binding_loc,
+ value: shader_binding_val,
+ },
+ ) in &pending_shader_bindings.surface_specific_bindings
{
if *shader_binding_surface_id != surface_spec.id {
continue;
diff --git a/engine/src/rendering/shader.rs b/engine/src/rendering/shader.rs
index 6b68e1e..74109a7 100644
--- a/engine/src/rendering/shader.rs
+++ b/engine/src/rendering/shader.rs
@@ -340,6 +340,13 @@ impl<'a> TypeLayout<'a>
))
}
+ pub fn resource_shape(&self) -> Option<ResourceShape>
+ {
+ Some(ResourceShape::from_bits_retain(
+ self.inner.resource_shape()? as u32,
+ ))
+ }
+
pub fn get_field_by_name(&self, name: &str) -> Option<VariableLayout<'a>>
{
let index = self.inner.find_field_index_by_name(name);
@@ -400,6 +407,21 @@ impl<'a> TypeLayout<'a>
self.inner.field_count()
}
+ pub fn element_cnt(&self) -> Option<usize>
+ {
+ self.inner.element_count()
+ }
+
+ pub fn row_cnt(&self) -> Option<u32>
+ {
+ self.inner.row_count()
+ }
+
+ pub fn column_cnt(&self) -> Option<u32>
+ {
+ self.inner.column_count()
+ }
+
pub fn element_type_layout(&self) -> Option<TypeLayout<'a>>
{
self.inner
@@ -582,6 +604,36 @@ impl ScalarType
}
}
+bitflags! {
+#[derive(Debug, Copy, Clone)]
+pub struct ResourceShape: u32 {
+ const BASE = shader_slang::ResourceShape::SlangResourceBaseShapeMask as u32;
+ const NONE = shader_slang::ResourceShape::SlangResourceNone as u32;
+ const TEXTURE_1D = shader_slang::ResourceShape::SlangTexture1d as u32;
+ const TEXTURE_2D = shader_slang::ResourceShape::SlangTexture2d as u32;
+ const TEXTURE_3D = shader_slang::ResourceShape::SlangTexture3d as u32;
+ const TEXTURE_CUBE = shader_slang::ResourceShape::SlangTextureCube as u32;
+ const TEXTURE_BUFFER = shader_slang::ResourceShape::SlangTextureBuffer as u32;
+ const STRUCTURED_BUFFER = shader_slang::ResourceShape::SlangStructuredBuffer as u32;
+ const BYTE_ADDRESS_BUFFER = shader_slang::ResourceShape::SlangByteAddressBuffer as u32;
+ const UNKNOWN = shader_slang::ResourceShape::SlangResourceUnknown as u32;
+ const ACCELERATION_STRUCTURE = shader_slang::ResourceShape::SlangAccelerationStructure as u32;
+ const TEXTURE_SUBPASS = shader_slang::ResourceShape::SlangTextureSubpass as u32;
+ const EXT_SHAPE = shader_slang::ResourceShape::SlangResourceExtShapeMask as u32;
+ const TEXTURE_FEEDBACK_FLAG = shader_slang::ResourceShape::SlangTextureFeedbackFlag as u32;
+ const TEXTURE_SHADOW_FLAG = shader_slang::ResourceShape::SlangTextureShadowFlag as u32;
+ const TEXTURE_ARRAY_FLAG = shader_slang::ResourceShape::SlangTextureArrayFlag as u32;
+ const TEXTURE_MULTISAMPLE_FLAG = shader_slang::ResourceShape::SlangTextureMultisampleFlag as u32;
+ const TEXTURE_COMBINED_FLAG = shader_slang::ResourceShape::SlangTextureCombinedFlag as u32;
+ const TEXTURE_1D_ARRAY = shader_slang::ResourceShape::SlangTexture1dArray as u32;
+ const TEXTURE_2D_ARRAY = shader_slang::ResourceShape::SlangTexture2dArray as u32;
+ const TEXTURE_CUBE_ARRAY = shader_slang::ResourceShape::SlangTextureCubeArray as u32;
+ const TEXTURE_2D_MULTISAMPLE = shader_slang::ResourceShape::SlangTexture2dMultisample as u32;
+ const TEXTURE_2D_MULTISAMPLE_ARRAY = shader_slang::ResourceShape::SlangTexture2dMultisampleArray as u32;
+ const TEXTURE_SUBPASS_MULTISAMPLE = shader_slang::ResourceShape::SlangTextureSubpassMultisample as u32;
+}
+}
+
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum ParameterCategory
{
diff --git a/engine/src/rendering/shader/cursor.rs b/engine/src/rendering/shader/cursor.rs
index 8aab4f2..32dee00 100644
--- a/engine/src/rendering/shader/cursor.rs
+++ b/engine/src/rendering/shader/cursor.rs
@@ -1,8 +1,16 @@
+use std::hint::cold_path;
+
use crate::color::Color;
use crate::data_types::matrix::Matrix;
use crate::data_types::vector::Vec3;
use crate::rendering::object::Id as RenderingObjectId;
-use crate::rendering::shader::{TypeKind, TypeLayout, VariableLayout};
+use crate::rendering::shader::{
+ ResourceShape,
+ ScalarType,
+ TypeKind,
+ TypeLayout,
+ VariableLayout,
+};
/// Shader cursor
#[derive(Clone)]
@@ -89,9 +97,14 @@ impl<'a> Cursor<'a>
self
}
- pub fn binding_location(&self) -> &BindingLocation
+ pub fn binding(self, value: BindingValue) -> Result<Binding, BindingError>
{
- &self.binding_location
+ value.validate_for_shader_type(self.type_layout)?;
+
+ Ok(Binding {
+ location: self.binding_location,
+ value,
+ })
}
pub fn into_binding_location(self) -> BindingLocation
@@ -120,6 +133,84 @@ pub enum BindingValue
Texture(RenderingObjectId),
}
+impl BindingValue
+{
+ fn validate_for_shader_type(&self, ty: TypeLayout<'_>) -> Result<(), BindingError>
+ {
+ match (
+ self,
+ ty.kind(),
+ ty.scalar_type(),
+ ty.element_type_layout()
+ .map(|elem_ty| elem_ty.scalar_type()),
+ ty.element_cnt(),
+ ty.resource_shape(),
+ ) {
+ (Self::Uint(_), TypeKind::Scalar, Some(ScalarType::Uint32), _, _, _)
+ | (Self::Int(_), TypeKind::Scalar, Some(ScalarType::Int32), _, _, _)
+ | (Self::Float(_), TypeKind::Scalar, Some(ScalarType::Float32), _, _, _)
+ | (
+ Self::FVec3(_),
+ TypeKind::Vector,
+ _,
+ Some(Some(ScalarType::Float32)),
+ Some(3),
+ _,
+ )
+ | (
+ Self::Color(Color::Rgb(_)),
+ TypeKind::Vector,
+ _,
+ Some(Some(ScalarType::Float32)),
+ Some(3),
+ _,
+ )
+ | (
+ Self::Color(Color::Rgba(_)),
+ TypeKind::Vector,
+ _,
+ Some(Some(ScalarType::Float32)),
+ Some(4),
+ _,
+ )
+ | (
+ Self::Color(Color::Luma(_)),
+ TypeKind::Vector,
+ _,
+ Some(Some(ScalarType::Float32)),
+ Some(1),
+ _,
+ )
+ | (
+ Self::Color(Color::LumaA(_)),
+ TypeKind::Vector,
+ _,
+ Some(Some(ScalarType::Float32)),
+ Some(2),
+ _,
+ ) => Ok(()),
+ (
+ Self::FMat4x4(_),
+ TypeKind::Matrix,
+ _,
+ Some(Some(ScalarType::Float32)),
+ _,
+ _,
+ ) if ty.row_cnt() == Some(4) && ty.column_cnt() == Some(4) => Ok(()),
+ (Self::Texture(_), TypeKind::Resource, _, _, _, Some(res_shape))
+ if (res_shape & ResourceShape::BASE)
+ .contains(ResourceShape::TEXTURE_2D) =>
+ {
+ Ok(())
+ }
+ _ => {
+ cold_path();
+ Err(BindingError::IncorrectValueType { value: self.clone() })
+ }
+ }
+ }
+}
+
impl From<u32> for BindingValue
{
fn from(value: u32) -> Self
@@ -167,3 +258,22 @@ impl From<Matrix<f32, 4, 4>> for BindingValue
BindingValue::FMat4x4(matrix)
}
}
+
+#[derive(Debug, Clone)]
+#[non_exhaustive]
+pub struct Binding
+{
+ pub location: BindingLocation,
+ pub value: BindingValue,
+}
+
+#[derive(Debug, thiserror::Error)]
+#[non_exhaustive]
+pub enum BindingError
+{
+ #[error("Value {value:?} has incorrect type for shader binding location")]
+ IncorrectValueType
+ {
+ value: BindingValue
+ },
+}
diff --git a/engine/src/rendering/shader/default.rs b/engine/src/rendering/shader/default.rs
index bb5c366..6621d06 100644
--- a/engine/src/rendering/shader/default.rs
+++ b/engine/src/rendering/shader/default.rs
@@ -165,22 +165,20 @@ pub fn enqueue_set_shader_bindings(
let lighting_shader_cursor = shader_cursor.field("Uniforms").field("lighting");
let material_shader_cursor = lighting_shader_cursor.field("material");
- shader_bindings.extend([
- (model_3d_shader_cursor.field("model"), model_matrix.into()),
- (
- model_3d_shader_cursor.field("model_inverted"),
- inverted_model_matrix.into(),
- ),
- (
- model_3d_shader_cursor.field("view"),
- create_view_matrix(&camera, camera_world_pos.position).into(),
- ),
- (
- lighting_shader_cursor.field("view_pos"),
- camera_world_pos.position.into(),
- ),
- (
- material_shader_cursor.field("ambient"),
+ shader_bindings.bindings.extend([
+ model_3d_shader_cursor
+ .field("model")
+ .binding(model_matrix.into())?,
+ model_3d_shader_cursor
+ .field("model_inverted")
+ .binding(inverted_model_matrix.into())?,
+ model_3d_shader_cursor
+ .field("view")
+ .binding(create_view_matrix(&camera, camera_world_pos.position).into())?,
+ lighting_shader_cursor
+ .field("view_pos")
+ .binding(camera_world_pos.position.into())?,
+ material_shader_cursor.field("ambient").binding(
(if material_flags.use_ambient_color {
&model_material.ambient
} else {
@@ -188,161 +186,164 @@ pub fn enqueue_set_shader_bindings(
}
.clone())
.into(),
- ),
- (
- material_shader_cursor.field("diffuse"),
- model_material.diffuse.clone().into(),
- ),
- (
- material_shader_cursor.field("specular"),
- model_material.specular.clone().into(),
- ),
- (
- material_shader_cursor.field("shininess"),
- model_material.shininess.into(),
- ),
- (
- lighting_shader_cursor.field("directional_light_cnt"),
+ )?,
+ material_shader_cursor
+ .field("diffuse")
+ .binding(model_material.diffuse.clone().into())?,
+ material_shader_cursor
+ .field("specular")
+ .binding(model_material.specular.clone().into())?,
+ material_shader_cursor
+ .field("shininess")
+ .binding(model_material.shininess.into())?,
+ lighting_shader_cursor
+ .field("directional_light_cnt")
+ .binding(
u32::try_from(directional_light_query.iter().count())
.expect(
"Directional light count does not fit in 32-bit unsigned integer",
)
.into(),
- ),
- (
- lighting_shader_cursor.field("point_light_cnt"),
+ )?,
+ lighting_shader_cursor.field("point_light_cnt").binding(
u32::try_from(point_light_query.iter().count())
.expect("Point light count does not fit in 32-bit unsigned integer")
.into(),
- ),
+ )?,
]);
for (window, window_surface_spec) in &window_query {
shader_bindings.surface_specific_bindings.push((
window_surface_spec.id,
- model_3d_shader_cursor
- .field("projection")
- .into_binding_location(),
- create_projection_matrix(&camera, &window.inner_size).into(),
+ model_3d_shader_cursor.field("projection").binding(
+ create_projection_matrix(&camera, &window.inner_size).into(),
+ )?,
));
}
- shader_bindings.extend(point_light_query.iter().enumerate().flat_map(
- |(point_light_index, (point_light, point_light_world_pos))| {
- let point_light_shader_cursor = lighting_shader_cursor
- .field("point_lights")
- .element(point_light_index);
-
- let phong_shader_cursor = point_light_shader_cursor.field("phong");
-
- let attenuation_props_shader_cursor =
- point_light_shader_cursor.field("attenuation_props");
-
- [
- (
- phong_shader_cursor.field("diffuse"),
- point_light.diffuse.clone().into(),
- ),
- (
- phong_shader_cursor.field("specular"),
- point_light.specular.clone().into(),
- ),
- (
- point_light_shader_cursor.field("position"),
- (point_light_world_pos.position + point_light.local_position)
- .into(),
- ),
- (
- attenuation_props_shader_cursor.field("constant"),
- point_light.attenuation_params.constant.into(),
- ),
- (
- attenuation_props_shader_cursor.field("linear"),
- point_light.attenuation_params.linear.into(),
- ),
- (
- attenuation_props_shader_cursor.field("quadratic"),
- point_light.attenuation_params.quadratic.into(),
- ),
- ]
- },
- ));
-
- shader_bindings.extend(directional_light_query.iter().enumerate().flat_map(
- |(directional_light_index, (directional_light,))| {
- let directional_light_shader_cursor = lighting_shader_cursor
- .field("directional_lights")
- .element(directional_light_index);
-
- let phong_shader_cursor = directional_light_shader_cursor.field("phong");
-
- [
- (
- phong_shader_cursor.field("diffuse"),
- directional_light.diffuse.clone().into(),
- ),
- (
- phong_shader_cursor.field("specular"),
- directional_light.specular.clone().into(),
- ),
- (
- directional_light_shader_cursor.field("direction"),
- directional_light.direction.into(),
- ),
- ]
- },
- ));
+ shader_bindings
+ .bindings
+ .reserve(point_light_query.iter().count() * 6);
- shader_bindings.bindings.push((
- shader_cursor.field("ambient_map").into_binding_location(),
- ShaderBindingValue::Texture(
- model_material
- .ambient_map
- .as_ref()
- .map(|ambient_map| ambient_map.clone())
- .unwrap_or_else(|| {
- assets
- .get_handle_to_loaded(TEXTURE_WHITE_1X1_ASSET_LABEL.clone())
- .expect("Not possible")
- })
- .id()
- .into(),
- ),
- ));
+ for (point_light_index, (point_light, point_light_world_pos)) in
+ point_light_query.iter().enumerate()
+ {
+ let point_light_shader_cursor = lighting_shader_cursor
+ .field("point_lights")
+ .element(point_light_index);
+
+ let phong_shader_cursor = point_light_shader_cursor.field("phong");
+
+ let attenuation_props_shader_cursor =
+ point_light_shader_cursor.field("attenuation_props");
+
+ shader_bindings.bindings.extend([
+ phong_shader_cursor
+ .field("diffuse")
+ .binding(point_light.diffuse.clone().into())?,
+ phong_shader_cursor
+ .field("specular")
+ .binding(point_light.specular.clone().into())?,
+ point_light_shader_cursor.field("position").binding(
+ (point_light_world_pos.position + point_light.local_position).into(),
+ )?,
+ attenuation_props_shader_cursor
+ .field("constant")
+ .binding(point_light.attenuation_params.constant.into())?,
+ attenuation_props_shader_cursor
+ .field("linear")
+ .binding(point_light.attenuation_params.linear.into())?,
+ attenuation_props_shader_cursor
+ .field("quadratic")
+ .binding(point_light.attenuation_params.quadratic.into())?,
+ ]);
+ }
- shader_bindings.bindings.push((
- shader_cursor.field("diffuse_map").into_binding_location(),
- ShaderBindingValue::Texture(
- model_material
- .diffuse_map
- .as_ref()
- .map(|diffuse_map| diffuse_map.clone())
- .unwrap_or_else(|| {
- assets
- .get_handle_to_loaded(TEXTURE_WHITE_1X1_ASSET_LABEL.clone())
- .expect("Not possible")
- })
- .id()
- .into(),
- ),
- ));
+ shader_bindings
+ .bindings
+ .reserve(directional_light_query.iter().count() * 3);
- shader_bindings.bindings.push((
- shader_cursor.field("specular_map").into_binding_location(),
- ShaderBindingValue::Texture(
- model_material
- .specular_map
- .as_ref()
- .map(|specular_map| specular_map.clone())
- .unwrap_or_else(|| {
- assets
- .get_handle_to_loaded(TEXTURE_WHITE_1X1_ASSET_LABEL.clone())
- .expect("Not possible")
- })
- .id()
- .into(),
- ),
- ));
+ for (directional_light_index, (directional_light,)) in
+ directional_light_query.iter().enumerate()
+ {
+ let directional_light_shader_cursor = lighting_shader_cursor
+ .field("directional_lights")
+ .element(directional_light_index);
+
+ let phong_shader_cursor = directional_light_shader_cursor.field("phong");
+
+ shader_bindings.bindings.extend([
+ phong_shader_cursor
+ .field("diffuse")
+ .binding(directional_light.diffuse.clone().into())?,
+ phong_shader_cursor
+ .field("specular")
+ .binding(directional_light.specular.clone().into())?,
+ directional_light_shader_cursor
+ .field("direction")
+ .binding(directional_light.direction.into())?,
+ ]);
+ }
+
+ shader_bindings.bindings.push(
+ shader_cursor
+ .field("ambient_map")
+ .binding(ShaderBindingValue::Texture(
+ model_material
+ .ambient_map
+ .as_ref()
+ .map(|ambient_map| ambient_map.clone())
+ .unwrap_or_else(|| {
+ assets
+ .get_handle_to_loaded(
+ TEXTURE_WHITE_1X1_ASSET_LABEL.clone(),
+ )
+ .expect("Not possible")
+ })
+ .id()
+ .into(),
+ ))?,
+ );
+
+ shader_bindings.bindings.push(
+ shader_cursor
+ .field("diffuse_map")
+ .binding(ShaderBindingValue::Texture(
+ model_material
+ .diffuse_map
+ .as_ref()
+ .map(|diffuse_map| diffuse_map.clone())
+ .unwrap_or_else(|| {
+ assets
+ .get_handle_to_loaded(
+ TEXTURE_WHITE_1X1_ASSET_LABEL.clone(),
+ )
+ .expect("Not possible")
+ })
+ .id()
+ .into(),
+ ))?,
+ );
+
+ shader_bindings.bindings.push(
+ shader_cursor
+ .field("specular_map")
+ .binding(ShaderBindingValue::Texture(
+ model_material
+ .specular_map
+ .as_ref()
+ .map(|specular_map| specular_map.clone())
+ .unwrap_or_else(|| {
+ assets
+ .get_handle_to_loaded(
+ TEXTURE_WHITE_1X1_ASSET_LABEL.clone(),
+ )
+ .expect("Not possible")
+ })
+ .id()
+ .into(),
+ ))?,
+ );
if !has_pending_shader_bindings_comp {
actions.add_components(entity_id, (shader_bindings.clone(),));
diff --git a/engine/src/windowing.rs b/engine/src/windowing.rs
index adc5132..6997f73 100644
--- a/engine/src/windowing.rs
+++ b/engine/src/windowing.rs
@@ -518,7 +518,7 @@ impl Context
};
})
{
- tracing::error!("Failed to create windowing app thread: {err}");
+ tracing::error!("Failed to create windowing app thread: {:#}", crate::Error::new(err));
return Self {
shared_state,
@@ -569,7 +569,7 @@ impl Context
unreachable!();
};
- tracing::error!("Error in windowing app thread: {err}");
+ tracing::error!("Error in windowing app thread: {:#}", crate::Error::new(err));
} else {
tracing::error!("Windowing app initialization aborted unexpectedly");
}
@@ -728,7 +728,7 @@ impl App
) {
Ok(window) => window,
Err(err) => {
- tracing::error!("Failed to create window: {err}");
+ tracing::error!("Failed to create window: {:#}", crate::Error::new(err));
continue;
}
},
@@ -953,7 +953,8 @@ impl ApplicationHandler for App
cold_path();
tracing::error!(
window_id=?window_id,
- "Failed to lock cursor position: {err}"
+ "Failed to lock cursor position: {:#}",
+ crate::Error::new(err)
);
};
}
diff --git a/src/main.rs b/src/main.rs
index e6ba5b6..51d50d1 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -13,7 +13,7 @@ use engine::camera::{
Camera,
Controllable as ControllableCamera,
};
-use engine::color::Color;
+use engine::color::{Color, Rgb};
use engine::data_types::dimens::Dimens3;
use engine::ecs::actions::Actions;
use engine::ecs::error::Context as _;
@@ -58,11 +58,11 @@ use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::EnvFilter;
-const YELLOW: Color<f32> = Color {
- red: 0.988235294118,
- green: 0.941176470588,
- blue: 0.0274509803922,
-};
+const YELLOW: Color<f32> = Color::Rgb(Rgb {
+ r: 0.988235294118,
+ g: 0.941176470588,
+ b: 0.0274509803922,
+});
const RESOURCE_DIR: &str = "res";