summaryrefslogtreecommitdiff
path: root/engine
diff options
context:
space:
mode:
Diffstat (limited to 'engine')
-rw-r--r--engine/Cargo.toml6
-rw-r--r--engine/src/camera/fly.rs4
-rw-r--r--engine/src/file_format/wavefront/mtl.rs11
-rw-r--r--engine/src/input.rs7
-rw-r--r--engine/src/lib.rs2
-rw-r--r--engine/src/opengl/glsl.rs (renamed from engine/src/shader_preprocessor.rs)535
-rw-r--r--engine/src/opengl/mod.rs23
-rw-r--r--engine/src/opengl/shader.rs17
-rw-r--r--engine/src/opengl/texture.rs6
-rw-r--r--engine/src/performance.rs16
-rw-r--r--engine/src/renderer/opengl.rs159
-rw-r--r--engine/src/renderer/opengl/glsl/fragment.glsl (renamed from engine/fragment.glsl)0
-rw-r--r--engine/src/renderer/opengl/glsl/light.glsl (renamed from engine/light.glsl)0
-rw-r--r--engine/src/renderer/opengl/glsl/vertex.glsl (renamed from engine/vertex.glsl)0
-rw-r--r--engine/src/renderer/opengl/glsl/vertex_data.glsl (renamed from engine/vertex_data.glsl)0
-rw-r--r--engine/src/shader.rs186
-rw-r--r--engine/src/texture.rs5
-rw-r--r--engine/src/window.rs402
18 files changed, 791 insertions, 588 deletions
diff --git a/engine/Cargo.toml b/engine/Cargo.toml
index b3868ac..f6cd5cf 100644
--- a/engine/Cargo.toml
+++ b/engine/Cargo.toml
@@ -3,18 +3,16 @@ name = "engine"
version = "0.1.0"
edition = "2021"
-[features]
-debug = ["dep:tracing"]
-
[dependencies]
glfw = { path = "../glfw", features = ["opengl"] }
thiserror = "1.0.49"
gl = "0.14.0"
bitflags = "2.4.0"
-tracing = { version = "0.1.39", optional = true }
+tracing = "0.1.39"
seq-macro = "0.3.5"
paste = "1.0.14"
ecs = { path = "../ecs" }
+util-macros = { path = "../util-macros" }
[dependencies.image]
version = "0.24.7"
diff --git a/engine/src/camera/fly.rs b/engine/src/camera/fly.rs
index b6ba7aa..1333360 100644
--- a/engine/src/camera/fly.rs
+++ b/engine/src/camera/fly.rs
@@ -2,12 +2,11 @@ use ecs::component::local::Local;
use ecs::sole::Single;
use ecs::system::{Into, System};
use ecs::{Component, Query};
-use glfw::window::{Key, KeyState};
use crate::camera::{Active as ActiveCamera, Camera};
use crate::delta_time::DeltaTime;
use crate::event::Update as UpdateEvent;
-use crate::input::{Cursor, CursorFlags, Keys};
+use crate::input::{Cursor, CursorFlags, Key, KeyState, Keys};
use crate::transform::Position;
use crate::util::builder;
use crate::vector::{Vec2, Vec3};
@@ -87,7 +86,6 @@ fn update(
{
for (mut camera, mut camera_pos, mut fly_camera, _) in &camera_query {
if cursor.has_moved && cursor_flags.is_first_move.flag {
- #[cfg(feature = "debug")]
tracing::debug!("First cursor move");
cursor_state.last_pos = cursor.position;
diff --git a/engine/src/file_format/wavefront/mtl.rs b/engine/src/file_format/wavefront/mtl.rs
index ef6e894..d90dbcf 100644
--- a/engine/src/file_format/wavefront/mtl.rs
+++ b/engine/src/file_format/wavefront/mtl.rs
@@ -44,7 +44,6 @@ pub fn parse(obj_content: &str) -> Result<Vec<NamedMaterial>, Error>
.filter(|(_, statement)| matches!(statement.keyword, Keyword::Newmtl))
.count();
- #[cfg(feature = "debug")]
tracing::debug!("Material count: {material_cnt}");
statements_to_materials(statements, material_cnt)
@@ -93,7 +92,7 @@ pub enum Error
},
}
-#[cfg_attr(feature = "debug", tracing::instrument(skip_all))]
+#[tracing::instrument(skip_all)]
fn statements_to_materials(
statements: impl IntoIterator<Item = (usize, Statement<Keyword>)>,
material_cnt: usize,
@@ -110,7 +109,6 @@ fn statements_to_materials(
for (line_no, statement) in statements {
if statement.keyword == Keyword::Newmtl {
if curr_material.ready {
- #[cfg(feature = "debug")]
tracing::debug!("Building material");
let material = curr_material.material_builder.clone().build();
@@ -135,7 +133,6 @@ fn statements_to_materials(
Keyword::Ka => {
let color = get_color_from_statement(&statement, line_no)?;
- #[cfg(feature = "debug")]
tracing::debug!("Adding ambient color");
curr_material.material_builder =
@@ -144,7 +141,6 @@ fn statements_to_materials(
Keyword::Kd => {
let color = get_color_from_statement(&statement, line_no)?;
- #[cfg(feature = "debug")]
tracing::debug!("Adding diffuse color");
curr_material.material_builder =
@@ -153,7 +149,6 @@ fn statements_to_materials(
Keyword::Ks => {
let color = get_color_from_statement(&statement, line_no)?;
- #[cfg(feature = "debug")]
tracing::debug!("Adding specular color");
curr_material.material_builder =
@@ -172,7 +167,6 @@ fn statements_to_materials(
let texture = Texture::open(Path::new(texture_file_path))?;
- #[cfg(feature = "debug")]
tracing::debug!("Adding ambient map");
let texture_id = texture.id();
@@ -185,7 +179,6 @@ fn statements_to_materials(
Keyword::MapKd => {
let texture = get_map_from_texture(&statement, line_no)?;
- #[cfg(feature = "debug")]
tracing::debug!("Adding diffuse map");
let texture_id = texture.id();
@@ -198,7 +191,6 @@ fn statements_to_materials(
Keyword::MapKs => {
let texture = get_map_from_texture(&statement, line_no)?;
- #[cfg(feature = "debug")]
tracing::debug!("Adding specular map");
let texture_id = texture.id();
@@ -213,7 +205,6 @@ fn statements_to_materials(
}
if curr_material.ready {
- #[cfg(feature = "debug")]
tracing::debug!("Building last material");
let material = curr_material.material_builder.build();
diff --git a/engine/src/input.rs b/engine/src/input.rs
index f4166f6..e847702 100644
--- a/engine/src/input.rs
+++ b/engine/src/input.rs
@@ -68,10 +68,6 @@ impl Keys
pub fn set_key_state(&mut self, key: Key, new_key_state: KeyState)
{
- if matches!(new_key_state, KeyState::Repeat) {
- return;
- }
-
let Some(key_data) = self.map.get_mut(&key) else {
unreachable!();
};
@@ -194,7 +190,6 @@ fn initialize(
let cursor_flags_weak_ref = cursor_flags.to_weak_ref();
window.set_focus_callback(move |is_focused| {
- #[cfg(feature = "debug")]
tracing::trace!("Window is focused: {is_focused}");
let cursor_flags_ref = cursor_flags_weak_ref.access().expect("No world");
@@ -209,7 +204,6 @@ fn maybe_clear_cursor_is_first_move(
)
{
if cursor_flags.is_first_move.pending_clear {
- #[cfg(feature = "debug")]
tracing::trace!("Clearing is_first_move");
// This flag was set for the whole previous tick so it can be cleared now
@@ -219,7 +213,6 @@ fn maybe_clear_cursor_is_first_move(
}
if cursor.has_moved && cursor_flags.is_first_move.flag {
- #[cfg(feature = "debug")]
tracing::trace!("Setting flag to clear is_first_move next tick");
// Make this system clear is_first_move the next time it runs
diff --git a/engine/src/lib.rs b/engine/src/lib.rs
index abf26f5..07b2f8d 100644
--- a/engine/src/lib.rs
+++ b/engine/src/lib.rs
@@ -21,7 +21,6 @@ use crate::event::{
};
mod opengl;
-mod shader_preprocessor;
mod util;
pub mod camera;
@@ -38,7 +37,6 @@ pub mod mesh;
pub mod performance;
pub mod projection;
pub mod renderer;
-pub mod shader;
pub mod texture;
pub mod transform;
pub mod vertex;
diff --git a/engine/src/shader_preprocessor.rs b/engine/src/opengl/glsl.rs
index 70696ac..6fd5638 100644
--- a/engine/src/shader_preprocessor.rs
+++ b/engine/src/opengl/glsl.rs
@@ -1,183 +1,181 @@
+use std::borrow::Cow;
+use std::fmt::{Display, Formatter};
use std::path::{Path, PathBuf};
use std::string::FromUtf8Error;
const PREINCLUDE_DIRECTIVE: &str = "#preinclude";
-/// Preprocessor for shaders written in the OpenGL Shading Language.
-pub struct ShaderPreprocessor
+pub fn preprocess<'content>(
+ shader_content: impl Into<Cow<'content, str>>,
+ read_file: &impl Fn(&Path) -> Result<Vec<u8>, std::io::Error>,
+) -> Result<Cow<'content, str>, PreprocessingError>
{
- read_file: fn(&Path) -> Result<Vec<u8>, std::io::Error>,
- base_dir: PathBuf,
+ do_preprocess(shader_content, SpanPath::Original, read_file)
}
-impl ShaderPreprocessor
+fn do_preprocess<'content>(
+ shader_content: impl Into<Cow<'content, str>>,
+ shader_path: SpanPath<'_>,
+ read_file: &impl Fn(&Path) -> Result<Vec<u8>, std::io::Error>,
+) -> Result<Cow<'content, str>, PreprocessingError>
{
- pub fn new(base_dir: PathBuf) -> Self
- {
- Self {
- read_file: |path| std::fs::read(path),
- base_dir,
- }
- }
+ let shader_content = shader_content.into();
- pub fn preprocess(
- &self,
- shader_content: String,
- shader_file_path: &Path,
- ) -> Result<String, Error>
- {
- let mut preincludes = shader_content
- .match_indices(PREINCLUDE_DIRECTIVE)
- .peekable();
+ let mut preincludes = shader_content
+ .match_indices(PREINCLUDE_DIRECTIVE)
+ .peekable();
- if preincludes.peek().is_none() {
- // Shader content contains no preincludes
- return Ok(shader_content);
- };
+ if preincludes.peek().is_none() {
+ // Shader content contains no preincludes
+ return Ok(shader_content.into());
+ };
- let mut preprocessed = shader_content.clone();
+ let mut preprocessed = shader_content.to_string();
- let mut curr = shader_content.find(PREINCLUDE_DIRECTIVE);
+ let mut curr = shader_content.find(PREINCLUDE_DIRECTIVE);
- let mut last_start = 0;
- let mut span_line_offset = 0;
+ let mut last_start = 0;
+ let mut span_line_offset = 0;
- while let Some(preinclude_start) = curr {
- let replacement_job = self.handle_preinclude(
- &preprocessed,
- shader_file_path,
- preinclude_start,
- span_line_offset,
- )?;
+ while let Some(preinclude_start) = curr {
+ let replacement_job = handle_preinclude(
+ &preprocessed,
+ &shader_path,
+ preinclude_start,
+ span_line_offset,
+ )?;
- let path = replacement_job.path.clone();
+ let path = replacement_job.path.clone();
- let mut included = String::from_utf8(
- (self.read_file)(&self.base_dir.join(replacement_job.path.clone()))
- .map_err(|err| Error::ReadIncludedShaderFailed {
- source: err,
- path: replacement_job.path.clone(),
- })?,
- )
- .map_err(|err| Error::IncludedShaderInvalidUtf8 {
- source: err,
- path: path.clone(),
+ let mut included =
+ String::from_utf8(read_file(&replacement_job.path).map_err(|err| {
+ PreprocessingError::ReadIncludedShaderFailed {
+ source: err,
+ path: replacement_job.path.clone(),
+ }
+ })?)
+ .map_err(|err| {
+ PreprocessingError::IncludedShaderInvalidUtf8 {
+ source: err,
+ path: path.clone(),
+ }
})?;
- if let Some(first_line) = included.lines().next() {
- if first_line.starts_with("#version") {
- included = included
- .chars()
- .skip_while(|character| *character != '\n')
- .collect();
- }
+ if let Some(first_line) = included.lines().next() {
+ if first_line.starts_with("#version") {
+ included = included
+ .chars()
+ .skip_while(|character| *character != '\n')
+ .collect();
}
-
- let included_preprocessed =
- self.preprocess(included, &replacement_job.path)?;
-
- let start = replacement_job.start_index;
- let end = replacement_job.end_index;
-
- preprocessed.replace_range(start..end, &included_preprocessed);
-
- curr = preprocessed[last_start + 1..]
- .find(PREINCLUDE_DIRECTIVE)
- .map(|index| index + 1);
-
- last_start = preinclude_start + included_preprocessed.len();
-
- span_line_offset += included_preprocessed.lines().count();
}
- Ok(preprocessed)
- }
+ let included_preprocessed = do_preprocess(
+ &included,
+ SpanPath::Path(replacement_job.path.as_path().into()),
+ read_file,
+ )?;
- fn handle_preinclude(
- &self,
- shader_content: &str,
- shader_file_path: &Path,
- preinclude_start_index: usize,
- span_line_offset: usize,
- ) -> Result<ReplacementJob, Error>
- {
- let expect_token = |token: char, index: usize| {
- let token_found = shader_content.chars().nth(index).ok_or_else(|| {
- Error::ExpectedToken {
- expected: token,
- span: Span::new(
- shader_content,
- &self.base_dir.join(shader_file_path),
- index,
- span_line_offset,
- preinclude_start_index,
- ),
- }
- })?;
+ let start = replacement_job.start_index;
+ let end = replacement_job.end_index;
- if token_found != token {
- return Err(Error::InvalidToken {
- expected: token,
- found: token_found,
- span: Span::new(
- shader_content,
- &self.base_dir.join(shader_file_path),
- index,
- span_line_offset,
- preinclude_start_index,
- ),
- });
- }
+ preprocessed.replace_range(start..end, &included_preprocessed);
- Ok(())
- };
+ curr = preprocessed[last_start + 1..]
+ .find(PREINCLUDE_DIRECTIVE)
+ .map(|index| index + 1);
- let space_index = preinclude_start_index + PREINCLUDE_DIRECTIVE.len();
- let quote_open_index = space_index + 1;
+ last_start = preinclude_start + included_preprocessed.len();
- expect_token(' ', space_index)?;
- expect_token('"', quote_open_index)?;
+ span_line_offset += included_preprocessed.lines().count();
+ }
- let buf = shader_content[quote_open_index + 1..]
- .chars()
- .take_while(|character| *character != '"')
- .map(|character| character as u8)
- .collect::<Vec<_>>();
+ Ok(preprocessed.into())
+}
- if buf.is_empty() {
- return Err(Error::ExpectedToken {
- expected: '"',
+fn handle_preinclude(
+ shader_content: &str,
+ shader_path: &SpanPath<'_>,
+ preinclude_start_index: usize,
+ span_line_offset: usize,
+) -> Result<ReplacementJob, PreprocessingError>
+{
+ let expect_token = |token: char, index: usize| {
+ let token_found = shader_content.chars().nth(index).ok_or_else(|| {
+ PreprocessingError::ExpectedToken {
+ expected: token,
span: Span::new(
shader_content,
- &self.base_dir.join(shader_file_path),
- shader_content.len() - 1,
+ shader_path.to_owned(),
+ index,
span_line_offset,
preinclude_start_index,
),
- });
- }
-
- let path_len = buf.len();
+ }
+ })?;
- let path = PathBuf::from(String::from_utf8(buf).map_err(|err| {
- Error::PreincludePathInvalidUtf8 {
- source: err,
+ if token_found != token {
+ return Err(PreprocessingError::InvalidToken {
+ expected: token,
+ found: token_found,
span: Span::new(
shader_content,
- &self.base_dir.join(shader_file_path),
- quote_open_index + 1,
+ shader_path.to_owned(),
+ index,
span_line_offset,
preinclude_start_index,
),
- }
- })?);
+ });
+ }
- Ok(ReplacementJob {
- start_index: preinclude_start_index,
- end_index: quote_open_index + 1 + path_len + 1,
- path,
- })
+ Ok(())
+ };
+
+ let space_index = preinclude_start_index + PREINCLUDE_DIRECTIVE.len();
+ let quote_open_index = space_index + 1;
+
+ expect_token(' ', space_index)?;
+ expect_token('"', quote_open_index)?;
+
+ let buf = shader_content[quote_open_index + 1..]
+ .chars()
+ .take_while(|character| *character != '"')
+ .map(|character| character as u8)
+ .collect::<Vec<_>>();
+
+ if buf.is_empty() {
+ return Err(PreprocessingError::ExpectedToken {
+ expected: '"',
+ span: Span::new(
+ shader_content,
+ shader_path.to_owned(),
+ shader_content.len() - 1,
+ span_line_offset,
+ preinclude_start_index,
+ ),
+ });
}
+
+ let path_len = buf.len();
+
+ let path = PathBuf::from(String::from_utf8(buf).map_err(|err| {
+ PreprocessingError::PreincludePathInvalidUtf8 {
+ source: err,
+ span: Span::new(
+ shader_content,
+ shader_path.to_owned(),
+ quote_open_index + 1,
+ span_line_offset,
+ preinclude_start_index,
+ ),
+ }
+ })?);
+
+ Ok(ReplacementJob {
+ start_index: preinclude_start_index,
+ end_index: quote_open_index + 1 + path_len + 1,
+ path,
+ })
}
struct ReplacementJob
@@ -187,15 +185,14 @@ struct ReplacementJob
path: PathBuf,
}
-/// Shader preprocessing error.
#[derive(Debug, thiserror::Error)]
-pub enum Error
+pub enum PreprocessingError
{
#[error(
"Invalid token at line {}, column {} of {}. Expected '{}', found '{}'",
span.line,
span.column,
- span.file.display(),
+ span.path,
expected,
found
)]
@@ -211,7 +208,7 @@ pub enum Error
expected,
span.line,
span.column,
- span.file.display(),
+ span.path
)]
ExpectedToken
{
@@ -222,7 +219,7 @@ pub enum Error
"Preinclude path at line {}, column {} of {} is invalid UTF-8",
span.line,
span.column,
- span.file.display(),
+ span.path
)]
PreincludePathInvalidUtf8
{
@@ -249,18 +246,19 @@ pub enum Error
}
#[derive(Debug, Clone, PartialEq, Eq)]
+#[non_exhaustive]
pub struct Span
{
- line: usize,
- column: usize,
- file: PathBuf,
+ pub line: usize,
+ pub column: usize,
+ pub path: SpanPath<'static>,
}
impl Span
{
fn new(
file_content: &str,
- file_path: &Path,
+ path: SpanPath<'static>,
char_index: usize,
line_offset: usize,
line_start_index: usize,
@@ -272,7 +270,49 @@ impl Span
Self {
line,
column: char_index - line_start_index + 1,
- file: file_path.to_path_buf(),
+ path,
+ }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum SpanPath<'a>
+{
+ Original,
+ Path(Cow<'a, Path>),
+}
+
+impl<'a> SpanPath<'a>
+{
+ fn to_owned(&self) -> SpanPath<'static>
+ {
+ match self {
+ Self::Original => SpanPath::Original,
+ Self::Path(path) => SpanPath::Path(Cow::Owned(path.to_path_buf().into())),
+ }
+ }
+}
+
+impl<'a> Display for SpanPath<'a>
+{
+ fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result
+ {
+ match self {
+ Self::Original => write!(formatter, "original file"),
+ Self::Path(path) => write!(formatter, "file {}", path.display()),
+ }
+ }
+}
+
+impl<'a, PathLike> PartialEq<PathLike> for SpanPath<'a>
+where
+ PathLike: AsRef<Path>,
+{
+ fn eq(&self, other: &PathLike) -> bool
+ {
+ match self {
+ Self::Original => false,
+ Self::Path(path) => path == other.as_ref(),
}
}
}
@@ -290,21 +330,17 @@ fn find_line_of_index(text: &str, index: usize) -> usize
mod tests
{
use std::ffi::OsStr;
- use std::path::{Path, PathBuf};
+ use std::path::Path;
- use super::{Error, ShaderPreprocessor};
+ use super::{preprocess, PreprocessingError};
+ use crate::opengl::glsl::SpanPath;
#[test]
fn preprocess_no_directives_is_same()
{
assert_eq!(
- ShaderPreprocessor {
- read_file: |_| { unreachable!() },
- base_dir: PathBuf::new()
- }
- .preprocess("#version 330 core\n".to_string(), Path::new("foo.glsl"),)
- .unwrap(),
- "#version 330 core\n".to_string()
+ preprocess("#version 330 core\n", &|_| { unreachable!() }).unwrap(),
+ "#version 330 core\n"
);
}
@@ -312,20 +348,15 @@ mod tests
fn preprocess_with_directives_works()
{
assert_eq!(
- ShaderPreprocessor {
- read_file: |_| { Ok(b"out vec4 FragColor;".to_vec()) },
- base_dir: PathBuf::new()
- }
- .preprocess(
+ preprocess(
concat!(
"#version 330 core\n",
"\n",
"#preinclude \"foo.glsl\"\n",
"\n",
"void main() {}",
- )
- .to_string(),
- Path::new("foo.glsl"),
+ ),
+ &|_| { Ok(b"out vec4 FragColor;".to_vec()) }
)
.unwrap(),
concat!(
@@ -338,11 +369,7 @@ mod tests
);
assert_eq!(
- ShaderPreprocessor {
- read_file: |_| { Ok(b"out vec4 FragColor;".to_vec()) },
- base_dir: PathBuf::new()
- }
- .preprocess(
+ preprocess(
concat!(
"#version 330 core\n",
"\n",
@@ -351,9 +378,8 @@ mod tests
"in vec3 in_frag_color;\n",
"\n",
"void main() {}",
- )
- .to_string(),
- Path::new("foo.glsl"),
+ ),
+ &|_| { Ok(b"out vec4 FragColor;".to_vec()) }
)
.unwrap(),
concat!(
@@ -368,8 +394,19 @@ mod tests
);
assert_eq!(
- ShaderPreprocessor {
- read_file: |path| {
+ preprocess(
+ concat!(
+ "#version 330 core\n",
+ "\n",
+ "#preinclude \"bar.glsl\"\n",
+ "\n",
+ "in vec3 in_frag_color;\n",
+ "\n",
+ "#preinclude \"foo.glsl\"\n",
+ "\n",
+ "void main() {}",
+ ),
+ &|path| {
if path == OsStr::new("bar.glsl") {
Ok(b"out vec4 FragColor;".to_vec())
} else {
@@ -381,22 +418,6 @@ mod tests
.to_vec())
}
},
- base_dir: PathBuf::new()
- }
- .preprocess(
- concat!(
- "#version 330 core\n",
- "\n",
- "#preinclude \"bar.glsl\"\n",
- "\n",
- "in vec3 in_frag_color;\n",
- "\n",
- "#preinclude \"foo.glsl\"\n",
- "\n",
- "void main() {}",
- )
- .to_string(),
- Path::new("foo.glsl"),
)
.unwrap(),
concat!(
@@ -415,15 +436,9 @@ mod tests
}
#[test]
- fn preprocess_invalid_shader_does_not_work()
+ fn preprocess_invalid_directive_does_not_work()
{
- let path = Path::new("foo.glsl");
-
- let res = ShaderPreprocessor {
- read_file: |_| Ok(b"out vec4 FragColor;".to_vec()),
- base_dir: PathBuf::new(),
- }
- .preprocess(
+ let res = preprocess(
concat!(
"#version 330 core\n",
"\n",
@@ -431,12 +446,11 @@ mod tests
"#preinclude foo.glsl\"\n",
"\n",
"void main() {}",
- )
- .to_string(),
- path,
+ ),
+ &|_| Ok(b"out vec4 FragColor;".to_vec()),
);
- let Err(Error::InvalidToken { expected, found, span }) = res else {
+ let Err(PreprocessingError::InvalidToken { expected, found, span }) = res else {
panic!(
"Expected result to be Err(Error::InvalidToken {{ ... }}), is {res:?}"
);
@@ -446,16 +460,25 @@ mod tests
assert_eq!(found, 'f');
assert_eq!(span.line, 3);
assert_eq!(span.column, 13);
- assert_eq!(span.file, path);
+ assert_eq!(span.path, SpanPath::Original);
}
#[test]
fn preprocess_error_has_correct_span()
{
- let path = Path::new("foo.glsl");
-
- let res = ShaderPreprocessor {
- read_file: |path| {
+ let res = preprocess(
+ concat!(
+ "#version 330 core\n",
+ "\n",
+ "#preinclude \"bar.glsl\"\n",
+ "\n",
+ "#preinclude \"foo.glsl\"\n",
+ "\n",
+ "in vec3 in_frag_color;\n",
+ "\n",
+ "void main() {}",
+ ),
+ &|path| {
if path == OsStr::new("bar.glsl") {
Ok(concat!(
"out vec4 FragColor;\n",
@@ -464,29 +487,25 @@ mod tests
)
.as_bytes()
.to_vec())
+ } else if path == OsStr::new("foo.glsl") {
+ Ok(concat!(
+ "uniform sampler2D input_texture;\n",
+ "\n",
+ // Missing space before first "
+ "#preinclude\"shared_types.glsl\"\n",
+ )
+ .as_bytes()
+ .to_vec())
} else {
- Ok(b"uniform sampler2D input_texture;".to_vec())
+ panic!(concat!(
+ "Expected read function to be called with ",
+ "either path bar.glsl or foo.glsl"
+ ));
}
},
- base_dir: PathBuf::new(),
- }
- .preprocess(
- concat!(
- "#version 330 core\n",
- "\n",
- "#preinclude \"bar.glsl\"\n",
- "\n",
- "in vec3 in_frag_color;\n",
- "\n",
- "#preinclude\"foo.glsl\"\n",
- "\n",
- "void main() {}",
- )
- .to_string(),
- Path::new("foo.glsl"),
);
- let Err(Error::InvalidToken { expected, found, span }) = res else {
+ let Err(PreprocessingError::InvalidToken { expected, found, span }) = res else {
panic!(
"Expected result to be Err(Error::InvalidToken {{ ... }}), is {res:?}"
);
@@ -494,17 +513,26 @@ mod tests
assert_eq!(expected, ' ');
assert_eq!(found, '"');
- assert_eq!(span.line, 7);
+ assert_eq!(span.line, 3);
assert_eq!(span.column, 12);
- assert_eq!(span.file, path);
+ assert_eq!(span.path, SpanPath::Path(Path::new("foo.glsl").into()));
}
#[test]
fn preprocess_included_shader_with_include_works()
{
assert_eq!(
- ShaderPreprocessor {
- read_file: |path| {
+ preprocess(
+ concat!(
+ "#version 330 core\n",
+ "\n",
+ "#preinclude \"bar.glsl\"\n",
+ "\n",
+ "in vec3 in_frag_color;\n",
+ "\n",
+ "void main() {}",
+ ),
+ &|path| {
if path == OsStr::new("bar.glsl") {
Ok(concat!(
"#preinclude \"foo.glsl\"\n",
@@ -521,21 +549,7 @@ mod tests
.as_bytes()
.to_vec())
}
- },
- base_dir: PathBuf::new()
- }
- .preprocess(
- concat!(
- "#version 330 core\n",
- "\n",
- "#preinclude \"bar.glsl\"\n",
- "\n",
- "in vec3 in_frag_color;\n",
- "\n",
- "void main() {}",
- )
- .to_string(),
- Path::new("foo.glsl"),
+ }
)
.unwrap(),
concat!(
@@ -556,8 +570,17 @@ mod tests
#[test]
fn preprocess_included_shader_with_include_error_span_is_correct()
{
- let res = ShaderPreprocessor {
- read_file: |path| {
+ let res = preprocess(
+ concat!(
+ "#version 330 core\n",
+ "\n",
+ "#preinclude \"bar.glsl\"\n",
+ "\n",
+ "in vec3 in_frag_color;\n",
+ "\n",
+ "void main() {}",
+ ),
+ &|path| {
if path == OsStr::new("bar.glsl") {
Ok(concat!(
// ' instead of "
@@ -576,23 +599,9 @@ mod tests
.to_vec())
}
},
- base_dir: PathBuf::new(),
- }
- .preprocess(
- concat!(
- "#version 330 core\n",
- "\n",
- "#preinclude \"bar.glsl\"\n",
- "\n",
- "in vec3 in_frag_color;\n",
- "\n",
- "void main() {}",
- )
- .to_string(),
- Path::new("foo.glsl"),
);
- let Err(Error::InvalidToken { expected, found, span }) = res else {
+ let Err(PreprocessingError::InvalidToken { expected, found, span }) = res else {
panic!(
"Expected result to be Err(Error::InvalidToken {{ ... }}), is {res:?}"
);
@@ -602,6 +611,6 @@ mod tests
assert_eq!(found, '\'');
assert_eq!(span.line, 1);
assert_eq!(span.column, 13);
- assert_eq!(span.file, Path::new("bar.glsl"));
+ assert_eq!(span.path, Path::new("bar.glsl"));
}
}
diff --git a/engine/src/opengl/mod.rs b/engine/src/opengl/mod.rs
index 0b1bb8a..53e0120 100644
--- a/engine/src/opengl/mod.rs
+++ b/engine/src/opengl/mod.rs
@@ -1,16 +1,17 @@
use bitflags::bitflags;
+use gl::types::GLint;
use crate::data_types::dimens::Dimens;
use crate::vector::Vec2;
pub mod buffer;
+pub mod glsl;
pub mod shader;
pub mod texture;
pub mod vertex_array;
mod util;
-#[cfg(feature = "debug")]
pub mod debug;
pub fn set_viewport(position: Vec2<u32>, size: Dimens<u32>)
@@ -47,6 +48,17 @@ pub fn enable(capacity: Capability)
}
}
+pub fn get_context_flags() -> ContextFlags
+{
+ let mut context_flags: GLint = 0;
+
+ unsafe {
+ gl::GetIntegerv(gl::CONTEXT_FLAGS as u32, &mut context_flags);
+ }
+
+ ContextFlags::from_bits_truncate(context_flags as u32)
+}
+
bitflags! {
#[derive(Debug, Clone, Copy)]
pub struct BufferClearMask: u32 {
@@ -105,3 +117,12 @@ impl From<crate::draw_flags::PolygonModeFace> for PolygonModeFace
}
}
}
+
+bitflags! {
+#[derive(Debug, Clone, Copy)]
+pub struct ContextFlags: u32 {
+ const FORWARD_COMPATIBLE = gl::CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT;
+ const DEBUG = gl::CONTEXT_FLAG_DEBUG_BIT;
+ const ROBUST_ACCESS = gl::CONTEXT_FLAG_ROBUST_ACCESS_BIT;
+}
+}
diff --git a/engine/src/opengl/shader.rs b/engine/src/opengl/shader.rs
index 070897e..36dc1a4 100644
--- a/engine/src/opengl/shader.rs
+++ b/engine/src/opengl/shader.rs
@@ -2,7 +2,6 @@ use std::ffi::CStr;
use std::ptr::null_mut;
use crate::matrix::Matrix;
-use crate::shader::Kind;
use crate::vector::Vec3;
#[derive(Debug)]
@@ -20,7 +19,7 @@ impl Shader
Self { shader }
}
- pub fn set_source(&self, source: &str) -> Result<(), Error>
+ pub fn set_source(&mut self, source: &str) -> Result<(), Error>
{
if !source.is_ascii() {
return Err(Error::SourceNotAscii);
@@ -39,7 +38,7 @@ impl Shader
Ok(())
}
- pub fn compile(&self) -> Result<(), Error>
+ pub fn compile(&mut self) -> Result<(), Error>
{
unsafe {
gl::CompileShader(self.shader);
@@ -90,6 +89,14 @@ impl Drop for Shader
}
}
+/// Shader kind.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum Kind
+{
+ Vertex,
+ Fragment,
+}
+
impl Kind
{
fn into_gl(self) -> gl::types::GLenum
@@ -117,14 +124,14 @@ impl Program
Self { program }
}
- pub fn attach(&self, shader: &Shader)
+ pub fn attach(&mut self, shader: &Shader)
{
unsafe {
gl::AttachShader(self.program, shader.shader);
}
}
- pub fn link(&self) -> Result<(), Error>
+ pub fn link(&mut self) -> Result<(), Error>
{
unsafe {
gl::LinkProgram(self.program);
diff --git a/engine/src/opengl/texture.rs b/engine/src/opengl/texture.rs
index 074ade7..52c8554 100644
--- a/engine/src/opengl/texture.rs
+++ b/engine/src/opengl/texture.rs
@@ -1,5 +1,5 @@
use crate::data_types::dimens::Dimens;
-use crate::texture::{Id, Properties};
+use crate::texture::Properties;
#[derive(Debug)]
pub struct Texture
@@ -224,8 +224,8 @@ macro_rules! texture_unit_enum {
}
}
- pub fn from_texture_id(texture_id: Id) -> Option<Self> {
- match texture_id.into_inner() {
+ pub fn from_num(num: usize) -> Option<Self> {
+ match num {
#(
N => Some(Self::No~N),
)*
diff --git a/engine/src/performance.rs b/engine/src/performance.rs
index ffc5c27..3ec8994 100644
--- a/engine/src/performance.rs
+++ b/engine/src/performance.rs
@@ -21,20 +21,6 @@ impl ecs::extension::Extension for Extension
}
}
-#[cfg(feature = "debug")]
-macro_rules! log_perf {
- ($($tt: tt)*) => {
- tracing::info!($($tt)*);
- };
-}
-
-#[cfg(not(feature = "debug"))]
-macro_rules! log_perf {
- ($($tt: tt)*) => {
- println!($($tt)*);
- };
-}
-
fn log_perf(mut state: Local<State>)
{
let Some(last_time) = state.last_time else {
@@ -46,7 +32,7 @@ fn log_perf(mut state: Local<State>)
state.last_time = Some(time_now);
- log_perf!(
+ tracing::info!(
"Frame time: {}us",
time_now.duration_since(last_time).as_micros()
);
diff --git a/engine/src/renderer/opengl.rs b/engine/src/renderer/opengl.rs
index a353c6a..c036cc0 100644
--- a/engine/src/renderer/opengl.rs
+++ b/engine/src/renderer/opengl.rs
@@ -2,7 +2,9 @@
use std::collections::HashMap;
use std::ffi::{c_void, CString};
+use std::io::{Error as IoError, ErrorKind as IoErrorKind};
use std::ops::Deref;
+use std::path::Path;
use std::process::abort;
use ecs::actions::Actions;
@@ -22,10 +24,22 @@ use crate::material::{Flags as MaterialFlags, Material};
use crate::matrix::Matrix;
use crate::mesh::Mesh;
use crate::opengl::buffer::{Buffer, Usage as BufferUsage};
-#[cfg(feature = "debug")]
-use crate::opengl::debug::{MessageSeverity, MessageSource, MessageType};
+use crate::opengl::debug::{
+ enable_debug_output,
+ set_debug_message_callback,
+ set_debug_message_control,
+ MessageIdsAction,
+ MessageSeverity,
+ MessageSource,
+ MessageType,
+};
+use crate::opengl::glsl::{
+ preprocess as glsl_preprocess,
+ PreprocessingError as GlslPreprocessingError,
+};
use crate::opengl::shader::{
Error as GlShaderError,
+ Kind as ShaderKind,
Program as GlShaderProgram,
Shader as GlShader,
};
@@ -39,9 +53,15 @@ use crate::opengl::vertex_array::{
PrimitiveKind,
VertexArray,
};
-use crate::opengl::{clear_buffers, enable, BufferClearMask, Capability};
+use crate::opengl::{
+ clear_buffers,
+ enable,
+ get_context_flags as get_opengl_context_flags,
+ BufferClearMask,
+ Capability,
+ ContextFlags,
+};
use crate::projection::{new_perspective_matrix, Projection};
-use crate::shader::Program as ShaderProgram;
use crate::texture::{Id as TextureId, Texture};
use crate::transform::{Position, Scale};
use crate::util::NeverDrop;
@@ -51,7 +71,6 @@ use crate::window::Window;
type RenderableEntity = (
Mesh,
- ShaderProgram,
Material,
Option<MaterialFlags>,
Option<Position>,
@@ -61,6 +80,7 @@ type RenderableEntity = (
);
#[derive(Debug, Default)]
+#[non_exhaustive]
pub struct Extension {}
impl ecs::extension::Extension for Extension
@@ -95,8 +115,9 @@ fn initialize(window: Single<Window>)
}
});
- #[cfg(feature = "debug")]
- initialize_debug();
+ if get_opengl_context_flags().contains(ContextFlags::DEBUG) {
+ initialize_debug();
+ }
let window_size = window.size().expect("Failed to get window size");
@@ -123,7 +144,6 @@ fn render(
)
{
let Some((camera, camera_pos, _)) = camera_query.iter().next() else {
- #[cfg(feature = "debug")]
tracing::warn!("No current camera. Nothing will be rendered");
return;
};
@@ -136,34 +156,24 @@ fn render(
let directional_lights = directional_lights.iter().collect::<Vec<_>>();
let GlobalGlObjects {
- shader_programs: gl_shader_programs,
+ shader_program,
textures: gl_textures,
} = &mut *gl_objects;
+ let shader_program =
+ shader_program.get_or_insert_with(|| create_default_shader_program().unwrap());
+
clear_buffers(BufferClearMask::COLOR | BufferClearMask::DEPTH);
for (
entity_index,
- (
- mesh,
- shader_program,
- material,
- material_flags,
- position,
- scale,
- draw_flags,
- gl_objects,
- ),
+ (mesh, material, material_flags, position, scale, draw_flags, gl_objects),
) in query.iter().enumerate()
{
let material_flags = material_flags
.map(|material_flags| material_flags.clone())
.unwrap_or_default();
- let shader_program = gl_shader_programs
- .entry(shader_program.u64_hash())
- .or_insert_with(|| create_gl_shader_program(&shader_program).unwrap());
-
let new_gl_objects;
let gl_objects = if let Some(gl_objects) = gl_objects.as_deref() {
@@ -207,15 +217,12 @@ fn render(
&camera_pos,
);
- for texture in &material.textures {
+ for (index, texture) in material.textures.iter().enumerate() {
let gl_texture = gl_textures
.entry(texture.id())
.or_insert_with(|| create_gl_texture(texture));
- let texture_unit =
- TextureUnit::from_texture_id(texture.id()).unwrap_or_else(|| {
- panic!("Texture id {} is a invalid texture unit", texture.id());
- });
+ let texture_unit = TextureUnit::from_num(index).expect("Too many textures");
set_active_texture_unit(texture_unit);
@@ -247,7 +254,7 @@ fn render(
#[derive(Debug, Default, Component)]
struct GlobalGlObjects
{
- shader_programs: HashMap<u64, GlShaderProgram>,
+ shader_program: Option<GlShaderProgram>,
textures: HashMap<TextureId, GlTexture>,
}
@@ -256,20 +263,11 @@ fn set_viewport(position: Vec2<u32>, size: Dimens<u32>)
crate::opengl::set_viewport(position, size);
}
-#[cfg(feature = "debug")]
fn initialize_debug()
{
- use crate::opengl::debug::{
- enable_debug_output,
- set_debug_message_callback,
- set_debug_message_control,
- MessageIdsAction,
- };
-
enable_debug_output();
set_debug_message_callback(opengl_debug_message_cb);
-
set_debug_message_control(None, None, None, &[], MessageIdsAction::Disable);
}
@@ -299,34 +297,68 @@ fn create_gl_texture(texture: &Texture) -> GlTexture
gl_texture
}
-fn create_gl_shader_program(
- shader_program: &ShaderProgram,
-) -> Result<GlShaderProgram, GlShaderError>
+const VERTEX_GLSL_SHADER_SRC: &str = include_str!("opengl/glsl/vertex.glsl");
+const FRAGMENT_GLSL_SHADER_SRC: &str = include_str!("opengl/glsl/fragment.glsl");
+
+const VERTEX_DATA_GLSL_SHADER_SRC: &str = include_str!("opengl/glsl/vertex_data.glsl");
+const LIGHT_GLSL_SHADER_SRC: &str = include_str!("opengl/glsl/light.glsl");
+
+fn create_default_shader_program() -> Result<GlShaderProgram, CreateShaderError>
{
- let gl_shaders = shader_program
- .shaders()
- .iter()
- .map(|shader| {
- let gl_shader = GlShader::new(shader.kind());
+ let mut vertex_shader = GlShader::new(ShaderKind::Vertex);
- gl_shader.set_source(shader.source())?;
- gl_shader.compile()?;
+ vertex_shader.set_source(&*glsl_preprocess(
+ VERTEX_GLSL_SHADER_SRC,
+ &get_glsl_shader_content,
+ )?)?;
- Ok(gl_shader)
- })
- .collect::<Result<Vec<_>, _>>()?;
+ vertex_shader.compile()?;
- let gl_shader_program = GlShaderProgram::new();
+ let mut fragment_shader = GlShader::new(ShaderKind::Fragment);
- for gl_shader in &gl_shaders {
- gl_shader_program.attach(gl_shader);
- }
+ fragment_shader.set_source(&*glsl_preprocess(
+ FRAGMENT_GLSL_SHADER_SRC,
+ &get_glsl_shader_content,
+ )?)?;
+
+ fragment_shader.compile()?;
+
+ let mut gl_shader_program = GlShaderProgram::new();
+
+ gl_shader_program.attach(&vertex_shader);
+ gl_shader_program.attach(&fragment_shader);
gl_shader_program.link()?;
Ok(gl_shader_program)
}
+#[derive(Debug, thiserror::Error)]
+enum CreateShaderError
+{
+ #[error(transparent)]
+ ShaderError(#[from] GlShaderError),
+
+ #[error(transparent)]
+ PreprocessingError(#[from] GlslPreprocessingError),
+}
+
+fn get_glsl_shader_content(path: &Path) -> Result<Vec<u8>, std::io::Error>
+{
+ if path == Path::new("vertex_data.glsl") {
+ return Ok(VERTEX_DATA_GLSL_SHADER_SRC.as_bytes().to_vec());
+ }
+
+ if path == Path::new("light.glsl") {
+ return Ok(LIGHT_GLSL_SHADER_SRC.as_bytes().to_vec());
+ }
+
+ Err(IoError::new(
+ IoErrorKind::NotFound,
+ format!("Content for shader file {} not found", path.display()),
+ ))
+}
+
#[derive(Debug, Component)]
struct GlObjects
{
@@ -340,10 +372,9 @@ struct GlObjects
impl GlObjects
{
- #[cfg_attr(feature = "debug", tracing::instrument(skip_all))]
+ #[tracing::instrument(skip_all)]
fn new(mesh: &Mesh) -> Self
{
- #[cfg(feature = "debug")]
tracing::trace!(
"Creating vertex array, vertex buffer{}",
if mesh.indices().is_some() {
@@ -532,22 +563,29 @@ fn apply_light<PointLightHolder>(
gl_shader_program
.set_uniform_vec_3fv(c"material.specular", &material.specular.clone().into());
+ let texture_map = material
+ .textures
+ .iter()
+ .enumerate()
+ .map(|(index, texture)| (texture.id(), index))
+ .collect::<HashMap<_, _>>();
+
#[allow(clippy::cast_possible_wrap)]
gl_shader_program.set_uniform_1i(
c"material.ambient_map",
- material.ambient_map.into_inner() as i32,
+ *texture_map.get(&material.ambient_map).unwrap() as i32,
);
#[allow(clippy::cast_possible_wrap)]
gl_shader_program.set_uniform_1i(
c"material.diffuse_map",
- material.diffuse_map.into_inner() as i32,
+ *texture_map.get(&material.diffuse_map).unwrap() as i32,
);
#[allow(clippy::cast_possible_wrap)]
gl_shader_program.set_uniform_1i(
c"material.specular_map",
- material.specular_map.into_inner() as i32,
+ *texture_map.get(&material.specular_map).unwrap() as i32,
);
gl_shader_program.set_uniform_1fv(c"material.shininess", material.shininess);
@@ -658,7 +696,6 @@ fn create_view(camera: &Camera, camera_pos: &Position) -> Matrix<f32, 4, 4>
view
}
-#[cfg(feature = "debug")]
#[tracing::instrument(skip_all)]
fn opengl_debug_message_cb(
source: MessageSource,
diff --git a/engine/fragment.glsl b/engine/src/renderer/opengl/glsl/fragment.glsl
index 5bf5ff2..5bf5ff2 100644
--- a/engine/fragment.glsl
+++ b/engine/src/renderer/opengl/glsl/fragment.glsl
diff --git a/engine/light.glsl b/engine/src/renderer/opengl/glsl/light.glsl
index 1bc23a4..1bc23a4 100644
--- a/engine/light.glsl
+++ b/engine/src/renderer/opengl/glsl/light.glsl
diff --git a/engine/vertex.glsl b/engine/src/renderer/opengl/glsl/vertex.glsl
index b57caa6..b57caa6 100644
--- a/engine/vertex.glsl
+++ b/engine/src/renderer/opengl/glsl/vertex.glsl
diff --git a/engine/vertex_data.glsl b/engine/src/renderer/opengl/glsl/vertex_data.glsl
index 486d445..486d445 100644
--- a/engine/vertex_data.glsl
+++ b/engine/src/renderer/opengl/glsl/vertex_data.glsl
diff --git a/engine/src/shader.rs b/engine/src/shader.rs
deleted file mode 100644
index 89f7b7c..0000000
--- a/engine/src/shader.rs
+++ /dev/null
@@ -1,186 +0,0 @@
-use std::collections::hash_map::DefaultHasher;
-use std::fs::read_to_string;
-use std::hash::{Hash, Hasher};
-use std::path::{Path, PathBuf};
-
-use ecs::Component;
-
-use crate::shader_preprocessor::{Error as ShaderPreprocessorError, ShaderPreprocessor};
-
-const VERTEX_SHADER_FILE: &str = "vertex.glsl";
-const FRAGMENT_SHADER_FILE: &str = "fragment.glsl";
-
-const SHADER_DIR: &str = "engine";
-
-/// Shader program
-#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Component)]
-pub struct Program
-{
- shaders: Vec<Shader>,
-}
-
-impl Program
-{
- /// Creates a new shader program with the default shaders.
- ///
- /// # Errors
- /// Returns `Err` if:
- /// - Reading a default shader file Fails
- /// - Preprocessing a shader fails.
- pub fn new() -> Result<Self, Error>
- {
- let mut program = Self { shaders: Vec::new() };
-
- program.push_shader(
- Shader::read_shader_file(
- Kind::Vertex,
- &Path::new(SHADER_DIR).join(VERTEX_SHADER_FILE),
- )?
- .preprocess()?,
- );
-
- program.push_shader(
- Shader::read_shader_file(
- Kind::Fragment,
- &Path::new(SHADER_DIR).join(FRAGMENT_SHADER_FILE),
- )?
- .preprocess()?,
- );
-
- Ok(program)
- }
-
- pub fn push_shader(&mut self, shader: Shader)
- {
- self.shaders.push(shader);
- }
-
- pub fn append_shaders(&mut self, shaders: impl IntoIterator<Item = Shader>)
- {
- self.shaders.extend(shaders);
- }
-
- #[must_use]
- pub fn shaders(&self) -> &[Shader]
- {
- &self.shaders
- }
-
- pub(crate) fn u64_hash(&self) -> u64
- {
- let mut hasher = DefaultHasher::new();
-
- self.hash(&mut hasher);
-
- hasher.finish()
- }
-}
-
-#[derive(Debug, Clone, Hash, PartialEq, Eq)]
-pub struct Shader
-{
- kind: Kind,
- source: String,
- file: PathBuf,
-}
-
-impl Shader
-{
- /// Reads a shader from the specified source file.
- ///
- /// # Errors
- /// Will return `Err` if:
- /// - Reading the file fails
- /// - The shader source is not ASCII
- pub fn read_shader_file(kind: Kind, shader_file: &Path) -> Result<Self, Error>
- {
- let source = read_to_string(shader_file).map_err(|err| Error::ReadFailed {
- source: err,
- shader_file: shader_file.to_path_buf(),
- })?;
-
- if !source.is_ascii() {
- return Err(Error::SourceNotAscii);
- }
-
- Ok(Self {
- kind,
- source,
- file: shader_file.to_path_buf(),
- })
- }
-
- /// Preprocesses the shaders.
- ///
- /// # Errors
- /// Returns `Err` if preprocessing fails.
- pub fn preprocess(self) -> Result<Self, Error>
- {
- let shader_preprocessor = ShaderPreprocessor::new(
- self.file
- .parent()
- .ok_or(Error::SourcePathHasNoParent)?
- .to_path_buf(),
- );
-
- let source_preprocessed = shader_preprocessor
- .preprocess(self.source, &self.file)
- .map_err(|err| Error::PreprocessFailed {
- source: err,
- shader_file: self.file.clone(),
- })?;
-
- Ok(Self {
- kind: self.kind,
- source: source_preprocessed,
- file: self.file.clone(),
- })
- }
-
- #[must_use]
- pub fn kind(&self) -> Kind
- {
- self.kind
- }
-
- #[must_use]
- pub fn source(&self) -> &str
- {
- &self.source
- }
-}
-
-/// Shader kind.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub enum Kind
-{
- Vertex,
- Fragment,
-}
-
-/// Shader error
-#[derive(Debug, thiserror::Error)]
-pub enum Error
-{
- #[error("Failed to read shader {}", shader_file.display())]
- ReadFailed
- {
- #[source]
- source: std::io::Error,
- shader_file: PathBuf,
- },
-
- #[error("Shader source is not ASCII")]
- SourceNotAscii,
-
- #[error("Failed to preprocess shader {}", shader_file.display())]
- PreprocessFailed
- {
- #[source]
- source: ShaderPreprocessorError,
- shader_file: PathBuf,
- },
-
- #[error("Shader source path has no parent")]
- SourcePathHasNoParent,
-}
diff --git a/engine/src/texture.rs b/engine/src/texture.rs
index f82b59d..16c1941 100644
--- a/engine/src/texture.rs
+++ b/engine/src/texture.rs
@@ -181,11 +181,6 @@ impl Id
{
Self { id }
}
-
- pub(crate) fn into_inner(self) -> u32
- {
- self.id
- }
}
impl Display for Id
diff --git a/engine/src/window.rs b/engine/src/window.rs
index ccc1b8d..ad239a1 100644
--- a/engine/src/window.rs
+++ b/engine/src/window.rs
@@ -1,31 +1,19 @@
use std::borrow::Cow;
use std::ffi::{CStr, CString};
+use bitflags::bitflags;
use ecs::actions::Actions;
use ecs::extension::Collector as ExtensionCollector;
use ecs::sole::Single;
use ecs::Sole;
+use glfw::window::{Hint as WindowCreationHint, HintValue as WindowCreationHintValue};
use glfw::WindowSize;
+use util_macros::VariantArr;
use crate::data_types::dimens::Dimens;
use crate::event::{Conclude as ConcludeEvent, Start as StartEvent};
use crate::vector::Vec2;
-mod reexports
-{
- pub use glfw::window::{
- CursorMode,
- Hint as CreationHint,
- HintValue as CreationHintValue,
- InputMode,
- Key,
- KeyModifiers,
- KeyState,
- };
-}
-
-pub use reexports::*;
-
#[derive(Debug, Sole)]
/// Has to be dropped last since it holds the OpenGL context.
#[sole(drop_last)]
@@ -53,7 +41,9 @@ impl Window
enabled: bool,
) -> Result<(), Error>
{
- Ok(self.inner.set_input_mode(input_mode, enabled)?)
+ Ok(self
+ .inner
+ .set_input_mode(input_mode.to_glfw_input_mode(), enabled)?)
}
/// Sets the cursor mode.
@@ -62,7 +52,9 @@ impl Window
/// If a platform error occurs.
pub fn set_cursor_mode(&self, cursor_mode: CursorMode) -> Result<(), Error>
{
- Ok(self.inner.set_cursor_mode(cursor_mode)?)
+ Ok(self
+ .inner
+ .set_cursor_mode(cursor_mode.to_glfw_cursor_mode())?)
}
/// Returns whether or not the window should close. Will return true when the user has
@@ -155,7 +147,19 @@ impl Window
callback: impl Fn(Key, i32, KeyState, KeyModifiers) + 'static,
)
{
- self.inner.set_key_callback(callback);
+ self.inner
+ .set_key_callback(move |key, scancode, key_state, key_modifiers| {
+ let Some(key_state) = KeyState::from_glfw_key_state(key_state) else {
+ return;
+ };
+
+ callback(
+ Key::from_glfw_key(key),
+ scancode,
+ key_state,
+ KeyModifiers::from_bits_truncate(key_modifiers.bits()),
+ )
+ });
}
/// Sets the window's cursor position callback.
@@ -188,10 +192,26 @@ pub struct Builder
impl Builder
{
- #[must_use]
- pub fn creation_hint(mut self, hint: CreationHint, value: CreationHintValue) -> Self
+ /// Sets whether the OpenGL context should be created in debug mode, which may
+ /// provide additional error and diagnostic reporting functionality.
+ pub fn opengl_debug_context(mut self, enabled: bool) -> Self
{
- self.inner = self.inner.hint(hint, value);
+ self.inner = self.inner.hint(
+ WindowCreationHint::OpenGLDebugContext,
+ WindowCreationHintValue::Bool(enabled),
+ );
+
+ self
+ }
+
+ /// Set the desired number of samples to use for multisampling. Zero disables
+ /// multisampling.
+ pub fn multisampling_sample_count(mut self, sample_count: u16) -> Self
+ {
+ self.inner = self.inner.hint(
+ WindowCreationHint::Samples,
+ WindowCreationHintValue::Number(sample_count as i32),
+ );
self
}
@@ -204,8 +224,8 @@ impl Builder
pub fn create(&self, size: Dimens<u32>, title: &str) -> Result<Window, Error>
{
let builder = self.inner.clone().hint(
- CreationHint::OpenGLDebugContext,
- CreationHintValue::Bool(cfg!(feature = "debug")),
+ WindowCreationHint::OpenGLDebugContext,
+ WindowCreationHintValue::Bool(true),
);
let window = builder.create(
@@ -220,6 +240,342 @@ impl Builder
}
}
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, VariantArr)]
+#[variant_arr(name = KEYS)]
+pub enum Key
+{
+ Space,
+ Apostrophe,
+ Comma,
+ Minus,
+ Period,
+ Slash,
+ Digit0,
+ Digit1,
+ Digit2,
+ Digit3,
+ Digit4,
+ Digit5,
+ Digit6,
+ Digit7,
+ Digit8,
+ Digit9,
+ Semicolon,
+ Equal,
+ A,
+ B,
+ C,
+ D,
+ E,
+ F,
+ G,
+ H,
+ I,
+ J,
+ K,
+ L,
+ M,
+ N,
+ O,
+ P,
+ Q,
+ R,
+ S,
+ T,
+ U,
+ V,
+ W,
+ X,
+ Y,
+ Z,
+ LeftBracket,
+ Backslash,
+ RightBracket,
+ GraveAccent,
+ World1,
+ World2,
+ Escape,
+ Enter,
+ Tab,
+ Backspace,
+ Insert,
+ Delete,
+ Right,
+ Left,
+ Down,
+ Up,
+ PageUp,
+ PageDown,
+ Home,
+ End,
+ CapsLock,
+ ScrollLock,
+ NumLock,
+ PrintScreen,
+ Pause,
+ F1,
+ F2,
+ F3,
+ F4,
+ F5,
+ F6,
+ F7,
+ F8,
+ F9,
+ F10,
+ F11,
+ F12,
+ F13,
+ F14,
+ F15,
+ F16,
+ F17,
+ F18,
+ F19,
+ F20,
+ F21,
+ F22,
+ F23,
+ F24,
+ F25,
+ Kp0,
+ Kp1,
+ Kp2,
+ Kp3,
+ Kp4,
+ Kp5,
+ Kp6,
+ Kp7,
+ Kp8,
+ Kp9,
+ KpDecimal,
+ KpDivide,
+ KpMultiply,
+ KpSubtract,
+ KpAdd,
+ KpEnter,
+ KpEqual,
+ LeftShift,
+ LeftControl,
+ LeftAlt,
+ LeftSuper,
+ RightShift,
+ RightControl,
+ RightAlt,
+ RightSuper,
+ Menu,
+}
+
+impl Key
+{
+ fn from_glfw_key(glfw_key: glfw::window::Key) -> Self
+ {
+ match glfw_key {
+ glfw::window::Key::Space => Self::Space,
+ glfw::window::Key::Apostrophe => Self::Apostrophe,
+ glfw::window::Key::Comma => Self::Comma,
+ glfw::window::Key::Minus => Self::Minus,
+ glfw::window::Key::Period => Self::Period,
+ glfw::window::Key::Slash => Self::Slash,
+ glfw::window::Key::Digit0 => Self::Digit0,
+ glfw::window::Key::Digit1 => Self::Digit1,
+ glfw::window::Key::Digit2 => Self::Digit2,
+ glfw::window::Key::Digit3 => Self::Digit3,
+ glfw::window::Key::Digit4 => Self::Digit4,
+ glfw::window::Key::Digit5 => Self::Digit5,
+ glfw::window::Key::Digit6 => Self::Digit6,
+ glfw::window::Key::Digit7 => Self::Digit7,
+ glfw::window::Key::Digit8 => Self::Digit8,
+ glfw::window::Key::Digit9 => Self::Digit9,
+ glfw::window::Key::Semicolon => Self::Semicolon,
+ glfw::window::Key::Equal => Self::Equal,
+ glfw::window::Key::A => Self::A,
+ glfw::window::Key::B => Self::B,
+ glfw::window::Key::C => Self::C,
+ glfw::window::Key::D => Self::D,
+ glfw::window::Key::E => Self::E,
+ glfw::window::Key::F => Self::F,
+ glfw::window::Key::G => Self::G,
+ glfw::window::Key::H => Self::H,
+ glfw::window::Key::I => Self::I,
+ glfw::window::Key::J => Self::J,
+ glfw::window::Key::K => Self::K,
+ glfw::window::Key::L => Self::L,
+ glfw::window::Key::M => Self::M,
+ glfw::window::Key::N => Self::N,
+ glfw::window::Key::O => Self::O,
+ glfw::window::Key::P => Self::P,
+ glfw::window::Key::Q => Self::Q,
+ glfw::window::Key::R => Self::R,
+ glfw::window::Key::S => Self::S,
+ glfw::window::Key::T => Self::T,
+ glfw::window::Key::U => Self::U,
+ glfw::window::Key::V => Self::V,
+ glfw::window::Key::W => Self::W,
+ glfw::window::Key::X => Self::X,
+ glfw::window::Key::Y => Self::Y,
+ glfw::window::Key::Z => Self::Z,
+ glfw::window::Key::LeftBracket => Self::LeftBracket,
+ glfw::window::Key::Backslash => Self::Backslash,
+ glfw::window::Key::RightBracket => Self::RightBracket,
+ glfw::window::Key::GraveAccent => Self::GraveAccent,
+ glfw::window::Key::World1 => Self::World1,
+ glfw::window::Key::World2 => Self::World2,
+ glfw::window::Key::Escape => Self::Escape,
+ glfw::window::Key::Enter => Self::Enter,
+ glfw::window::Key::Tab => Self::Tab,
+ glfw::window::Key::Backspace => Self::Backspace,
+ glfw::window::Key::Insert => Self::Insert,
+ glfw::window::Key::Delete => Self::Delete,
+ glfw::window::Key::Right => Self::Right,
+ glfw::window::Key::Left => Self::Left,
+ glfw::window::Key::Down => Self::Down,
+ glfw::window::Key::Up => Self::Up,
+ glfw::window::Key::PageUp => Self::PageUp,
+ glfw::window::Key::PageDown => Self::PageDown,
+ glfw::window::Key::Home => Self::Home,
+ glfw::window::Key::End => Self::End,
+ glfw::window::Key::CapsLock => Self::CapsLock,
+ glfw::window::Key::ScrollLock => Self::ScrollLock,
+ glfw::window::Key::NumLock => Self::NumLock,
+ glfw::window::Key::PrintScreen => Self::PrintScreen,
+ glfw::window::Key::Pause => Self::Pause,
+ glfw::window::Key::F1 => Self::F1,
+ glfw::window::Key::F2 => Self::F2,
+ glfw::window::Key::F3 => Self::F3,
+ glfw::window::Key::F4 => Self::F4,
+ glfw::window::Key::F5 => Self::F5,
+ glfw::window::Key::F6 => Self::F6,
+ glfw::window::Key::F7 => Self::F7,
+ glfw::window::Key::F8 => Self::F8,
+ glfw::window::Key::F9 => Self::F9,
+ glfw::window::Key::F10 => Self::F10,
+ glfw::window::Key::F11 => Self::F11,
+ glfw::window::Key::F12 => Self::F12,
+ glfw::window::Key::F13 => Self::F13,
+ glfw::window::Key::F14 => Self::F14,
+ glfw::window::Key::F15 => Self::F15,
+ glfw::window::Key::F16 => Self::F16,
+ glfw::window::Key::F17 => Self::F17,
+ glfw::window::Key::F18 => Self::F18,
+ glfw::window::Key::F19 => Self::F19,
+ glfw::window::Key::F20 => Self::F20,
+ glfw::window::Key::F21 => Self::F21,
+ glfw::window::Key::F22 => Self::F22,
+ glfw::window::Key::F23 => Self::F23,
+ glfw::window::Key::F24 => Self::F24,
+ glfw::window::Key::F25 => Self::F25,
+ glfw::window::Key::Kp0 => Self::Kp0,
+ glfw::window::Key::Kp1 => Self::Kp1,
+ glfw::window::Key::Kp2 => Self::Kp2,
+ glfw::window::Key::Kp3 => Self::Kp3,
+ glfw::window::Key::Kp4 => Self::Kp4,
+ glfw::window::Key::Kp5 => Self::Kp5,
+ glfw::window::Key::Kp6 => Self::Kp6,
+ glfw::window::Key::Kp7 => Self::Kp7,
+ glfw::window::Key::Kp8 => Self::Kp8,
+ glfw::window::Key::Kp9 => Self::Kp9,
+ glfw::window::Key::KpDecimal => Self::KpDecimal,
+ glfw::window::Key::KpDivide => Self::KpDivide,
+ glfw::window::Key::KpMultiply => Self::KpMultiply,
+ glfw::window::Key::KpSubtract => Self::KpSubtract,
+ glfw::window::Key::KpAdd => Self::KpAdd,
+ glfw::window::Key::KpEnter => Self::KpEnter,
+ glfw::window::Key::KpEqual => Self::KpEqual,
+ glfw::window::Key::LeftShift => Self::LeftShift,
+ glfw::window::Key::LeftControl => Self::LeftControl,
+ glfw::window::Key::LeftAlt => Self::LeftAlt,
+ glfw::window::Key::LeftSuper => Self::LeftSuper,
+ glfw::window::Key::RightShift => Self::RightShift,
+ glfw::window::Key::RightControl => Self::RightControl,
+ glfw::window::Key::RightAlt => Self::RightAlt,
+ glfw::window::Key::RightSuper => Self::RightSuper,
+ glfw::window::Key::Menu => Self::Menu,
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum KeyState
+{
+ Pressed,
+ Released,
+}
+
+impl KeyState
+{
+ fn from_glfw_key_state(glfw_key_state: glfw::window::KeyState) -> Option<Self>
+ {
+ match glfw_key_state {
+ glfw::window::KeyState::Pressed => Some(Self::Pressed),
+ glfw::window::KeyState::Released => Some(Self::Released),
+ glfw::window::KeyState::Repeat => None,
+ }
+ }
+}
+
+bitflags! {
+ #[derive(Debug, Clone, Copy)]
+ pub struct KeyModifiers: i32 {
+ const SHIFT = glfw::window::KeyModifiers::SHIFT.bits();
+ const CONTROL = glfw::window::KeyModifiers::CONTROL.bits();
+ const ALT = glfw::window::KeyModifiers::ALT.bits();
+ const SUPER = glfw::window::KeyModifiers::SUPER.bits();
+ const CAPS_LOCK = glfw::window::KeyModifiers::CAPS_LOCK.bits();
+ const NUM_LOCK = glfw::window::KeyModifiers::NUM_LOCK.bits();
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum CursorMode
+{
+ /// Hides and grabs the cursor, providing virtual and unlimited cursor movement.
+ Disabled,
+
+ /// Makes the cursor invisible when it is over the content area of the window but
+ /// does not restrict the cursor from leaving.
+ Hidden,
+
+ /// Makes the cursor visible and behaving normally.
+ Normal,
+}
+
+impl CursorMode
+{
+ fn to_glfw_cursor_mode(self) -> glfw::window::CursorMode
+ {
+ match self {
+ Self::Disabled => glfw::window::CursorMode::Disabled,
+ Self::Hidden => glfw::window::CursorMode::Hidden,
+ Self::Normal => glfw::window::CursorMode::Normal,
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum InputMode
+{
+ /// When the cursor is disabled, raw (unscaled and unaccelerated) mouse motion can be
+ /// enabled if available.
+ ///
+ /// Raw mouse motion is closer to the actual motion of the mouse across a surface. It
+ /// is not affected by the scaling and acceleration applied to the motion of the
+ /// desktop cursor. That processing is suitable for a cursor while raw motion is
+ /// better for controlling for example a 3D camera. Because of this, raw mouse motion
+ /// is only provided when the cursor is disabled.
+ RawMouseMotion,
+}
+
+impl InputMode
+{
+ fn to_glfw_input_mode(self) -> glfw::window::InputMode
+ {
+ match self {
+ Self::RawMouseMotion => glfw::window::InputMode::RawMouseMotion,
+ }
+ }
+}
+
#[derive(Debug)]
pub struct Extension
{