summaryrefslogtreecommitdiff
path: root/engine/src/opengl
diff options
context:
space:
mode:
Diffstat (limited to 'engine/src/opengl')
-rw-r--r--engine/src/opengl/buffer.rs92
-rw-r--r--engine/src/opengl/debug.rs145
-rw-r--r--engine/src/opengl/glsl.rs616
-rw-r--r--engine/src/opengl/mod.rs108
-rw-r--r--engine/src/opengl/shader.rs240
-rw-r--r--engine/src/opengl/texture.rs240
-rw-r--r--engine/src/opengl/util.rs30
-rw-r--r--engine/src/opengl/vertex_array.rs183
8 files changed, 617 insertions, 1037 deletions
diff --git a/engine/src/opengl/buffer.rs b/engine/src/opengl/buffer.rs
deleted file mode 100644
index 2be7f12..0000000
--- a/engine/src/opengl/buffer.rs
+++ /dev/null
@@ -1,92 +0,0 @@
-use std::marker::PhantomData;
-use std::mem::size_of_val;
-
-#[derive(Debug)]
-pub struct Buffer<Item>
-{
- buf: gl::types::GLuint,
- _pd: PhantomData<Item>,
-}
-
-impl<Item> Buffer<Item>
-{
- pub fn new() -> Self
- {
- let mut buffer = gl::types::GLuint::default();
-
- unsafe {
- gl::CreateBuffers(1, &mut buffer);
- };
-
- Self { buf: buffer, _pd: PhantomData }
- }
-
- /// Stores items in the currently bound buffer.
- pub fn store(&mut self, items: &[Item], usage: Usage)
- {
- unsafe {
- #[allow(clippy::cast_possible_wrap)]
- gl::NamedBufferData(
- self.buf,
- size_of_val(items) as gl::types::GLsizeiptr,
- items.as_ptr().cast(),
- usage.into_gl(),
- );
- }
- }
-
- pub fn object(&self) -> gl::types::GLuint
- {
- self.buf
- }
-
- /// Does a weak clone of this buffer. The buffer itself is NOT copied in any way this
- /// function only copies the internal buffer ID.
- ///
- /// # Safety
- /// The returned `Buffer` must not be dropped if another `Buffer` referencing the
- /// same buffer ID is used later or if a [`VertexArray`] is used later.
- ///
- /// [`VertexArray`]: crate::opengl::vertex_array::VertexArray
- pub unsafe fn clone_weak(&self) -> Self
- {
- Self { buf: self.buf, _pd: PhantomData }
- }
-}
-
-impl<Item> Drop for Buffer<Item>
-{
- fn drop(&mut self)
- {
- unsafe {
- gl::DeleteBuffers(1, &self.buf);
- }
- }
-}
-
-/// Buffer usage.
-#[derive(Debug)]
-#[allow(dead_code)]
-pub enum Usage
-{
- /// The buffer data is set only once and used by the GPU at most a few times.
- Stream,
-
- /// The buffer data is set only once and used many times.
- Static,
-
- /// The buffer data is changed a lot and used many times.
- Dynamic,
-}
-
-impl Usage
-{
- fn into_gl(self) -> gl::types::GLenum
- {
- match self {
- Self::Stream => gl::STREAM_DRAW,
- Self::Static => gl::STATIC_DRAW,
- Self::Dynamic => gl::DYNAMIC_DRAW,
- }
- }
-}
diff --git a/engine/src/opengl/debug.rs b/engine/src/opengl/debug.rs
deleted file mode 100644
index 203590a..0000000
--- a/engine/src/opengl/debug.rs
+++ /dev/null
@@ -1,145 +0,0 @@
-use std::ffi::c_void;
-use std::io::{stderr, Write};
-use std::panic::catch_unwind;
-use std::ptr::null_mut;
-use std::sync::Mutex;
-
-use crate::opengl::util::gl_enum;
-
-pub type MessageCallback = fn(
- source: MessageSource,
- ty: MessageType,
- id: u32,
- severity: MessageSeverity,
- message: &str,
-);
-
-pub fn enable_debug_output()
-{
- unsafe {
- gl::Enable(gl::DEBUG_OUTPUT);
- gl::Enable(gl::DEBUG_OUTPUT_SYNCHRONOUS);
- }
-}
-
-pub fn set_debug_message_callback(cb: MessageCallback)
-{
- *DEBUG_MESSAGE_CB.lock().unwrap() = Some(cb);
-
- unsafe {
- gl::DebugMessageCallback(Some(debug_message_cb), null_mut());
- }
-}
-
-pub fn set_debug_message_control(
- source: Option<MessageSource>,
- ty: Option<MessageType>,
- severity: Option<MessageSeverity>,
- ids: &[u32],
- ids_action: MessageIdsAction,
-)
-{
- // Ids shouldn't realistically be large enough to cause a panic here
- let ids_len: i32 = ids.len().try_into().unwrap();
-
- unsafe {
- gl::DebugMessageControl(
- source.map_or(gl::DONT_CARE, |source| source as u32),
- ty.map_or(gl::DONT_CARE, |ty| ty as u32),
- severity.map_or(gl::DONT_CARE, |severity| severity as u32),
- ids_len,
- ids.as_ptr(),
- ids_action as u8,
- );
- }
-}
-
-#[derive(Debug, Clone, Copy)]
-#[allow(dead_code)]
-pub enum MessageIdsAction
-{
- Enable = 1,
- Disable = 0,
-}
-
-gl_enum! {
-pub enum MessageSource
-{
- Api = gl::DEBUG_SOURCE_API,
- WindowSystem = gl::DEBUG_SOURCE_WINDOW_SYSTEM,
- ShaderCompiler = gl::DEBUG_SOURCE_SHADER_COMPILER,
- ThirdParty = gl::DEBUG_SOURCE_THIRD_PARTY,
- Application = gl::DEBUG_SOURCE_APPLICATION,
- Other = gl::DEBUG_SOURCE_OTHER,
-}
-}
-
-gl_enum! {
-pub enum MessageType
-{
- DeprecatedBehavior = gl::DEBUG_TYPE_DEPRECATED_BEHAVIOR,
- Error = gl::DEBUG_TYPE_ERROR,
- Marker = gl::DEBUG_TYPE_MARKER,
- Other = gl::DEBUG_TYPE_OTHER,
- Performance = gl::DEBUG_TYPE_PERFORMANCE,
- PopGroup = gl::DEBUG_TYPE_POP_GROUP,
- PushGroup = gl::DEBUG_TYPE_PUSH_GROUP,
- Portability = gl::DEBUG_TYPE_PORTABILITY,
- UndefinedBehavior = gl::DEBUG_TYPE_UNDEFINED_BEHAVIOR,
-}
-}
-
-gl_enum! {
-pub enum MessageSeverity
-{
- High = gl::DEBUG_SEVERITY_HIGH,
- Medium = gl::DEBUG_SEVERITY_MEDIUM,
- Low = gl::DEBUG_SEVERITY_LOW,
- Notification = gl::DEBUG_SEVERITY_NOTIFICATION,
-}
-}
-
-static DEBUG_MESSAGE_CB: Mutex<Option<MessageCallback>> = Mutex::new(None);
-
-extern "system" fn debug_message_cb(
- source: gl::types::GLenum,
- ty: gl::types::GLenum,
- id: gl::types::GLuint,
- severity: gl::types::GLenum,
- message_length: gl::types::GLsizei,
- message: *const gl::types::GLchar,
- _user_param: *mut c_void,
-)
-{
- // Unwinds are catched because unwinding from Rust code into foreign code is UB.
- let res = catch_unwind(|| {
- let cb_lock = DEBUG_MESSAGE_CB.lock().unwrap();
-
- if let Some(cb) = *cb_lock {
- let msg_source = MessageSource::from_gl(source).unwrap();
- let msg_type = MessageType::from_gl(ty).unwrap();
- let msg_severity = MessageSeverity::from_gl(severity).unwrap();
-
- let msg_length = usize::try_from(message_length).unwrap();
-
- // SAFETY: The received message should be a valid ASCII string
- let message = unsafe {
- std::str::from_utf8_unchecked(std::slice::from_raw_parts(
- message.cast(),
- msg_length,
- ))
- };
-
- cb(msg_source, msg_type, id, msg_severity, message);
- }
- });
-
- if res.is_err() {
- // eprintln is not used since it can panic and unwinds are unwanted because
- // unwinding from Rust code into foreign code is UB.
- stderr()
- .write_all(b"ERROR: Panic in debug message callback")
- .ok();
- println!();
- }
-}
diff --git a/engine/src/opengl/glsl.rs b/engine/src/opengl/glsl.rs
new file mode 100644
index 0000000..6fd5638
--- /dev/null
+++ b/engine/src/opengl/glsl.rs
@@ -0,0 +1,616 @@
+use std::borrow::Cow;
+use std::fmt::{Display, Formatter};
+use std::path::{Path, PathBuf};
+use std::string::FromUtf8Error;
+
+const PREINCLUDE_DIRECTIVE: &str = "#preinclude";
+
+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>
+{
+ do_preprocess(shader_content, SpanPath::Original, read_file)
+}
+
+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>
+{
+ let shader_content = shader_content.into();
+
+ let mut preincludes = shader_content
+ .match_indices(PREINCLUDE_DIRECTIVE)
+ .peekable();
+
+ if preincludes.peek().is_none() {
+ // Shader content contains no preincludes
+ return Ok(shader_content.into());
+ };
+
+ let mut preprocessed = shader_content.to_string();
+
+ let mut curr = shader_content.find(PREINCLUDE_DIRECTIVE);
+
+ let mut last_start = 0;
+ let mut span_line_offset = 0;
+
+ 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 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();
+ }
+ }
+
+ let included_preprocessed = do_preprocess(
+ &included,
+ SpanPath::Path(replacement_job.path.as_path().into()),
+ read_file,
+ )?;
+
+ 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.into())
+}
+
+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,
+ shader_path.to_owned(),
+ index,
+ span_line_offset,
+ preinclude_start_index,
+ ),
+ }
+ })?;
+
+ if token_found != token {
+ return Err(PreprocessingError::InvalidToken {
+ expected: token,
+ found: token_found,
+ span: Span::new(
+ shader_content,
+ shader_path.to_owned(),
+ index,
+ span_line_offset,
+ preinclude_start_index,
+ ),
+ });
+ }
+
+ 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
+{
+ start_index: usize,
+ end_index: usize,
+ path: PathBuf,
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum PreprocessingError
+{
+ #[error(
+ "Invalid token at line {}, column {} of {}. Expected '{}', found '{}'",
+ span.line,
+ span.column,
+ span.path,
+ expected,
+ found
+ )]
+ InvalidToken
+ {
+ expected: char,
+ found: char,
+ span: Span,
+ },
+
+ #[error(
+ "Expected token '{}' at line {}, column {} of {}. Found eof",
+ expected,
+ span.line,
+ span.column,
+ span.path
+ )]
+ ExpectedToken
+ {
+ expected: char, span: Span
+ },
+
+ #[error(
+ "Preinclude path at line {}, column {} of {} is invalid UTF-8",
+ span.line,
+ span.column,
+ span.path
+ )]
+ PreincludePathInvalidUtf8
+ {
+ #[source]
+ source: FromUtf8Error,
+ span: Span,
+ },
+
+ #[error("Failed to read included shader")]
+ ReadIncludedShaderFailed
+ {
+ #[source]
+ source: std::io::Error,
+ path: PathBuf,
+ },
+
+ #[error("Included shader is not valid UTF-8")]
+ IncludedShaderInvalidUtf8
+ {
+ #[source]
+ source: FromUtf8Error,
+ path: PathBuf,
+ },
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+#[non_exhaustive]
+pub struct Span
+{
+ pub line: usize,
+ pub column: usize,
+ pub path: SpanPath<'static>,
+}
+
+impl Span
+{
+ fn new(
+ file_content: &str,
+ path: SpanPath<'static>,
+ char_index: usize,
+ line_offset: usize,
+ line_start_index: usize,
+ ) -> Self
+ {
+ let line = find_line_of_index(file_content, char_index) + 1
+ - line_offset.saturating_sub(1);
+
+ Self {
+ line,
+ column: char_index - line_start_index + 1,
+ 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(),
+ }
+ }
+}
+
+fn find_line_of_index(text: &str, index: usize) -> usize
+{
+ text.chars()
+ .take(index + 1)
+ .enumerate()
+ .filter(|(_, character)| *character == '\n')
+ .count()
+}
+
+#[cfg(test)]
+mod tests
+{
+ use std::ffi::OsStr;
+ use std::path::Path;
+
+ use super::{preprocess, PreprocessingError};
+ use crate::opengl::glsl::SpanPath;
+
+ #[test]
+ fn preprocess_no_directives_is_same()
+ {
+ assert_eq!(
+ preprocess("#version 330 core\n", &|_| { unreachable!() }).unwrap(),
+ "#version 330 core\n"
+ );
+ }
+
+ #[test]
+ fn preprocess_with_directives_works()
+ {
+ assert_eq!(
+ preprocess(
+ concat!(
+ "#version 330 core\n",
+ "\n",
+ "#preinclude \"foo.glsl\"\n",
+ "\n",
+ "void main() {}",
+ ),
+ &|_| { Ok(b"out vec4 FragColor;".to_vec()) }
+ )
+ .unwrap(),
+ concat!(
+ "#version 330 core\n",
+ "\n",
+ "out vec4 FragColor;\n",
+ "\n",
+ "void main() {}",
+ )
+ );
+
+ assert_eq!(
+ preprocess(
+ concat!(
+ "#version 330 core\n",
+ "\n",
+ "#preinclude \"bar.glsl\"\n",
+ "\n",
+ "in vec3 in_frag_color;\n",
+ "\n",
+ "void main() {}",
+ ),
+ &|_| { Ok(b"out vec4 FragColor;".to_vec()) }
+ )
+ .unwrap(),
+ concat!(
+ "#version 330 core\n",
+ "\n",
+ "out vec4 FragColor;\n",
+ "\n",
+ "in vec3 in_frag_color;\n",
+ "\n",
+ "void main() {}",
+ )
+ );
+
+ assert_eq!(
+ 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 {
+ Ok(concat!(
+ "uniform sampler2D input_texture;\n",
+ "in vec2 in_texture_coords;"
+ )
+ .as_bytes()
+ .to_vec())
+ }
+ },
+ )
+ .unwrap(),
+ concat!(
+ "#version 330 core\n",
+ "\n",
+ "out vec4 FragColor;\n",
+ "\n",
+ "in vec3 in_frag_color;\n",
+ "\n",
+ "uniform sampler2D input_texture;\n",
+ "in vec2 in_texture_coords;\n",
+ "\n",
+ "void main() {}",
+ )
+ );
+ }
+
+ #[test]
+ fn preprocess_invalid_directive_does_not_work()
+ {
+ let res = preprocess(
+ concat!(
+ "#version 330 core\n",
+ "\n",
+ // Missing "
+ "#preinclude foo.glsl\"\n",
+ "\n",
+ "void main() {}",
+ ),
+ &|_| Ok(b"out vec4 FragColor;".to_vec()),
+ );
+
+ let Err(PreprocessingError::InvalidToken { expected, found, span }) = res else {
+ panic!(
+ "Expected result to be Err(Error::InvalidToken {{ ... }}), is {res:?}"
+ );
+ };
+
+ assert_eq!(expected, '"');
+ assert_eq!(found, 'f');
+ assert_eq!(span.line, 3);
+ assert_eq!(span.column, 13);
+ assert_eq!(span.path, SpanPath::Original);
+ }
+
+ #[test]
+ fn preprocess_error_has_correct_span()
+ {
+ 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",
+ "in vec2 in_texture_coords;\n",
+ "in float foo;"
+ )
+ .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 {
+ panic!(concat!(
+ "Expected read function to be called with ",
+ "either path bar.glsl or foo.glsl"
+ ));
+ }
+ },
+ );
+
+ let Err(PreprocessingError::InvalidToken { expected, found, span }) = res else {
+ panic!(
+ "Expected result to be Err(Error::InvalidToken {{ ... }}), is {res:?}"
+ );
+ };
+
+ assert_eq!(expected, ' ');
+ assert_eq!(found, '"');
+ assert_eq!(span.line, 3);
+ assert_eq!(span.column, 12);
+ assert_eq!(span.path, SpanPath::Path(Path::new("foo.glsl").into()));
+ }
+
+ #[test]
+ fn preprocess_included_shader_with_include_works()
+ {
+ assert_eq!(
+ 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",
+ "\n",
+ "out vec4 FragColor;"
+ )
+ .as_bytes()
+ .to_vec())
+ } else {
+ Ok(concat!(
+ "uniform sampler2D input_texture;\n",
+ "in vec2 in_texture_coords;"
+ )
+ .as_bytes()
+ .to_vec())
+ }
+ }
+ )
+ .unwrap(),
+ concat!(
+ "#version 330 core\n",
+ "\n",
+ "uniform sampler2D input_texture;\n",
+ "in vec2 in_texture_coords;\n",
+ "\n",
+ "out vec4 FragColor;\n",
+ "\n",
+ "in vec3 in_frag_color;\n",
+ "\n",
+ "void main() {}",
+ )
+ );
+ }
+
+ #[test]
+ fn preprocess_included_shader_with_include_error_span_is_correct()
+ {
+ 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 "
+ "#preinclude 'foo.glsl\"\n",
+ "\n",
+ "out vec4 FragColor;"
+ )
+ .as_bytes()
+ .to_vec())
+ } else {
+ Ok(concat!(
+ "uniform sampler2D input_texture;\n",
+ "in vec2 in_texture_coords;"
+ )
+ .as_bytes()
+ .to_vec())
+ }
+ },
+ );
+
+ let Err(PreprocessingError::InvalidToken { expected, found, span }) = res else {
+ panic!(
+ "Expected result to be Err(Error::InvalidToken {{ ... }}), is {res:?}"
+ );
+ };
+
+ assert_eq!(expected, '"');
+ assert_eq!(found, '\'');
+ assert_eq!(span.line, 1);
+ assert_eq!(span.column, 13);
+ assert_eq!(span.path, Path::new("bar.glsl"));
+ }
+}
diff --git a/engine/src/opengl/mod.rs b/engine/src/opengl/mod.rs
index 0b1bb8a..2208ac6 100644
--- a/engine/src/opengl/mod.rs
+++ b/engine/src/opengl/mod.rs
@@ -1,107 +1 @@
-use bitflags::bitflags;
-
-use crate::data_types::dimens::Dimens;
-use crate::vector::Vec2;
-
-pub mod buffer;
-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>)
-{
- unsafe {
- #[allow(clippy::cast_possible_wrap)]
- gl::Viewport(
- position.x as i32,
- position.y as i32,
- size.width as i32,
- size.height as i32,
- );
- }
-}
-
-pub fn clear_buffers(mask: BufferClearMask)
-{
- unsafe {
- gl::Clear(mask.bits());
- }
-}
-
-pub fn set_polygon_mode(face: impl Into<PolygonModeFace>, mode: impl Into<PolygonMode>)
-{
- unsafe {
- gl::PolygonMode(face.into() as u32, mode.into() as u32);
- }
-}
-
-pub fn enable(capacity: Capability)
-{
- unsafe {
- gl::Enable(capacity as u32);
- }
-}
-
-bitflags! {
- #[derive(Debug, Clone, Copy)]
- pub struct BufferClearMask: u32 {
- const COLOR = gl::COLOR_BUFFER_BIT;
- const DEPTH = gl::DEPTH_BUFFER_BIT;
- const STENCIL = gl::STENCIL_BUFFER_BIT;
- }
-}
-
-#[derive(Debug)]
-#[repr(u32)]
-pub enum Capability
-{
- DepthTest = gl::DEPTH_TEST,
- MultiSample = gl::MULTISAMPLE,
-}
-
-#[derive(Debug)]
-#[repr(u32)]
-pub enum PolygonMode
-{
- Point = gl::POINT,
- Line = gl::LINE,
- Fill = gl::FILL,
-}
-
-impl From<crate::draw_flags::PolygonMode> for PolygonMode
-{
- fn from(mode: crate::draw_flags::PolygonMode) -> Self
- {
- match mode {
- crate::draw_flags::PolygonMode::Point => Self::Point,
- crate::draw_flags::PolygonMode::Fill => Self::Fill,
- crate::draw_flags::PolygonMode::Line => Self::Line,
- }
- }
-}
-
-#[derive(Debug)]
-#[repr(u32)]
-pub enum PolygonModeFace
-{
- Front = gl::FRONT,
- Back = gl::BACK,
- FrontAndBack = gl::FRONT_AND_BACK,
-}
-
-impl From<crate::draw_flags::PolygonModeFace> for PolygonModeFace
-{
- fn from(face: crate::draw_flags::PolygonModeFace) -> Self
- {
- match face {
- crate::draw_flags::PolygonModeFace::Front => Self::Front,
- crate::draw_flags::PolygonModeFace::Back => Self::Back,
- crate::draw_flags::PolygonModeFace::FrontAndBack => Self::FrontAndBack,
- }
- }
-}
+pub mod glsl;
diff --git a/engine/src/opengl/shader.rs b/engine/src/opengl/shader.rs
deleted file mode 100644
index 070897e..0000000
--- a/engine/src/opengl/shader.rs
+++ /dev/null
@@ -1,240 +0,0 @@
-use std::ffi::CStr;
-use std::ptr::null_mut;
-
-use crate::matrix::Matrix;
-use crate::shader::Kind;
-use crate::vector::Vec3;
-
-#[derive(Debug)]
-pub struct Shader
-{
- shader: gl::types::GLuint,
-}
-
-impl Shader
-{
- pub fn new(kind: Kind) -> Self
- {
- let shader = unsafe { gl::CreateShader(kind.into_gl()) };
-
- Self { shader }
- }
-
- pub fn set_source(&self, source: &str) -> Result<(), Error>
- {
- if !source.is_ascii() {
- return Err(Error::SourceNotAscii);
- }
-
- unsafe {
- #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)]
- gl::ShaderSource(
- self.shader,
- 1,
- &source.as_ptr().cast(),
- &(source.len() as gl::types::GLint),
- );
- }
-
- Ok(())
- }
-
- pub fn compile(&self) -> Result<(), Error>
- {
- unsafe {
- gl::CompileShader(self.shader);
- }
-
- let mut compile_success = gl::types::GLint::default();
-
- unsafe {
- gl::GetShaderiv(self.shader, gl::COMPILE_STATUS, &mut compile_success);
- }
-
- if compile_success == 0 {
- let info_log = self.get_info_log();
-
- return Err(Error::CompileFailed(info_log));
- }
-
- Ok(())
- }
-
- fn get_info_log(&self) -> String
- {
- let mut buf = vec![gl::types::GLchar::default(); 512];
-
- unsafe {
- #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)]
- gl::GetShaderInfoLog(
- self.shader,
- buf.len() as gl::types::GLsizei,
- null_mut(),
- buf.as_mut_ptr(),
- );
- }
-
- let info_log = unsafe { CStr::from_ptr(buf.as_ptr()) };
-
- unsafe { String::from_utf8_unchecked(info_log.to_bytes().to_vec()) }
- }
-}
-
-impl Drop for Shader
-{
- fn drop(&mut self)
- {
- unsafe {
- gl::DeleteShader(self.shader);
- }
- }
-}
-
-impl Kind
-{
- fn into_gl(self) -> gl::types::GLenum
- {
- match self {
- Self::Vertex => gl::VERTEX_SHADER,
- Self::Fragment => gl::FRAGMENT_SHADER,
- }
- }
-}
-
-/// Shader program
-#[derive(Debug, PartialEq, Eq, Hash)]
-pub struct Program
-{
- program: gl::types::GLuint,
-}
-
-impl Program
-{
- pub fn new() -> Self
- {
- let program = unsafe { gl::CreateProgram() };
-
- Self { program }
- }
-
- pub fn attach(&self, shader: &Shader)
- {
- unsafe {
- gl::AttachShader(self.program, shader.shader);
- }
- }
-
- pub fn link(&self) -> Result<(), Error>
- {
- unsafe {
- gl::LinkProgram(self.program);
- }
-
- let mut link_success = gl::types::GLint::default();
-
- unsafe {
- gl::GetProgramiv(self.program, gl::LINK_STATUS, &mut link_success);
- }
-
- if link_success == 0 {
- let info_log = self.get_info_log();
-
- return Err(Error::CompileFailed(info_log));
- }
-
- Ok(())
- }
-
- pub fn activate(&self)
- {
- unsafe {
- gl::UseProgram(self.program);
- }
- }
-
- pub fn set_uniform_matrix_4fv(&mut self, name: &CStr, matrix: &Matrix<f32, 4, 4>)
- {
- let uniform_location =
- unsafe { gl::GetUniformLocation(self.program, name.as_ptr().cast()) };
-
- unsafe {
- gl::ProgramUniformMatrix4fv(
- self.program,
- uniform_location,
- 1,
- gl::FALSE,
- matrix.as_ptr(),
- );
- }
- }
-
- pub fn set_uniform_vec_3fv(&mut self, name: &CStr, vec: &Vec3<f32>)
- {
- let uniform_location =
- unsafe { gl::GetUniformLocation(self.program, name.as_ptr().cast()) };
-
- unsafe {
- gl::ProgramUniform3fv(self.program, uniform_location, 1, vec.as_ptr());
- }
- }
-
- pub fn set_uniform_1fv(&mut self, name: &CStr, num: f32)
- {
- let uniform_location =
- unsafe { gl::GetUniformLocation(self.program, name.as_ptr().cast()) };
-
- unsafe {
- gl::ProgramUniform1fv(self.program, uniform_location, 1, &num);
- }
- }
-
- pub fn set_uniform_1i(&mut self, name: &CStr, num: i32)
- {
- let uniform_location =
- unsafe { gl::GetUniformLocation(self.program, name.as_ptr().cast()) };
-
- unsafe {
- gl::ProgramUniform1i(self.program, uniform_location, num);
- }
- }
-
- fn get_info_log(&self) -> String
- {
- let mut buf = vec![gl::types::GLchar::default(); 512];
-
- unsafe {
- #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)]
- gl::GetProgramInfoLog(
- self.program,
- buf.len() as gl::types::GLsizei,
- null_mut(),
- buf.as_mut_ptr(),
- );
- }
-
- let info_log = unsafe { CStr::from_ptr(buf.as_ptr()) };
-
- unsafe { String::from_utf8_unchecked(info_log.to_bytes().to_vec()) }
- }
-}
-
-impl Drop for Program
-{
- fn drop(&mut self)
- {
- unsafe {
- gl::DeleteProgram(self.program);
- }
- }
-}
-
-/// Shader error.
-#[derive(Debug, thiserror::Error)]
-pub enum Error
-{
- #[error("All characters in source are not within the ASCII range")]
- SourceNotAscii,
-
- #[error("Failed to compile: {0}")]
- CompileFailed(String),
-}
diff --git a/engine/src/opengl/texture.rs b/engine/src/opengl/texture.rs
deleted file mode 100644
index 074ade7..0000000
--- a/engine/src/opengl/texture.rs
+++ /dev/null
@@ -1,240 +0,0 @@
-use crate::data_types::dimens::Dimens;
-use crate::texture::{Id, Properties};
-
-#[derive(Debug)]
-pub struct Texture
-{
- texture: gl::types::GLuint,
-}
-
-impl Texture
-{
- pub fn new() -> Self
- {
- let mut texture = gl::types::GLuint::default();
-
- unsafe {
- gl::CreateTextures(gl::TEXTURE_2D, 1, &mut texture);
- };
-
- Self { texture }
- }
-
- pub fn bind(&self)
- {
- unsafe {
- gl::BindTexture(gl::TEXTURE_2D, self.texture);
- }
- }
-
- pub fn generate(
- &mut self,
- dimens: Dimens<u32>,
- data: &[u8],
- pixel_data_format: PixelDataFormat,
- )
- {
- self.alloc_image(pixel_data_format, dimens, data);
-
- unsafe {
- gl::GenerateTextureMipmap(self.texture);
- }
- }
-
- pub fn apply_properties(&mut self, properties: &Properties)
- {
- self.set_wrap(properties.wrap);
- self.set_magnifying_filter(properties.magnifying_filter);
- self.set_minifying_filter(properties.minifying_filter);
- }
-
- pub fn set_wrap(&mut self, wrapping: Wrapping)
- {
- let wrapping_gl = wrapping.to_gl();
-
- #[allow(clippy::cast_possible_wrap)]
- unsafe {
- gl::TextureParameteri(self.texture, gl::TEXTURE_WRAP_S, wrapping_gl as i32);
- gl::TextureParameteri(self.texture, gl::TEXTURE_WRAP_T, wrapping_gl as i32);
- }
- }
-
- pub fn set_magnifying_filter(&mut self, filtering: Filtering)
- {
- let filtering_gl = filtering.to_gl();
-
- #[allow(clippy::cast_possible_wrap)]
- unsafe {
- gl::TextureParameteri(
- self.texture,
- gl::TEXTURE_MAG_FILTER,
- filtering_gl as i32,
- );
- }
- }
-
- pub fn set_minifying_filter(&mut self, filtering: Filtering)
- {
- let filtering_gl = filtering.to_gl();
-
- #[allow(clippy::cast_possible_wrap)]
- unsafe {
- gl::TextureParameteri(
- self.texture,
- gl::TEXTURE_MIN_FILTER,
- filtering_gl as i32,
- );
- }
- }
-
- fn alloc_image(
- &mut self,
- pixel_data_format: PixelDataFormat,
- dimens: Dimens<u32>,
- data: &[u8],
- )
- {
- unsafe {
- #[allow(clippy::cast_possible_wrap)]
- gl::TextureStorage2D(
- self.texture,
- 1,
- pixel_data_format.to_sized_internal_format(),
- dimens.width as i32,
- dimens.height as i32,
- );
-
- #[allow(clippy::cast_possible_wrap)]
- gl::TextureSubImage2D(
- self.texture,
- 0,
- 0,
- 0,
- dimens.width as i32,
- dimens.height as i32,
- pixel_data_format.to_format(),
- gl::UNSIGNED_BYTE,
- data.as_ptr().cast(),
- );
- }
- }
-}
-
-impl Drop for Texture
-{
- fn drop(&mut self)
- {
- unsafe {
- gl::DeleteTextures(1, &self.texture);
- }
- }
-}
-
-/// Texture wrapping.
-#[derive(Debug, Clone, Copy)]
-pub enum Wrapping
-{
- Repeat,
- MirroredRepeat,
- ClampToEdge,
- ClampToBorder,
-}
-
-impl Wrapping
-{
- fn to_gl(self) -> gl::types::GLenum
- {
- match self {
- Self::Repeat => gl::REPEAT,
- Self::MirroredRepeat => gl::MIRRORED_REPEAT,
- Self::ClampToEdge => gl::CLAMP_TO_EDGE,
- Self::ClampToBorder => gl::CLAMP_TO_BORDER,
- }
- }
-}
-
-#[derive(Debug, Clone, Copy)]
-pub enum Filtering
-{
- Nearest,
- Linear,
-}
-
-impl Filtering
-{
- fn to_gl(self) -> gl::types::GLenum
- {
- match self {
- Self::Linear => gl::LINEAR,
- Self::Nearest => gl::NEAREST,
- }
- }
-}
-
-/// Texture pixel data format.
-#[derive(Debug, Clone, Copy)]
-pub enum PixelDataFormat
-{
- Rgb8,
- Rgba8,
-}
-
-impl PixelDataFormat
-{
- fn to_sized_internal_format(self) -> gl::types::GLenum
- {
- match self {
- Self::Rgb8 => gl::RGB8,
- Self::Rgba8 => gl::RGBA8,
- }
- }
-
- fn to_format(self) -> gl::types::GLenum
- {
- match self {
- Self::Rgb8 => gl::RGB,
- Self::Rgba8 => gl::RGBA,
- }
- }
-}
-
-pub fn set_active_texture_unit(texture_unit: TextureUnit)
-{
- unsafe {
- gl::ActiveTexture(texture_unit.into_gl());
- }
-}
-
-macro_rules! texture_unit_enum {
- (cnt=$cnt: literal) => {
- seq_macro::seq!(N in 0..$cnt {
- #[derive(Debug, Clone, Copy)]
- pub enum TextureUnit {
- #(
- No~N,
- )*
- }
-
- impl TextureUnit {
- fn into_gl(self) -> gl::types::GLenum {
- match self {
- #(
- Self::No~N => gl::TEXTURE~N,
- )*
- }
- }
-
- pub fn from_texture_id(texture_id: Id) -> Option<Self> {
- match texture_id.into_inner() {
- #(
- N => Some(Self::No~N),
- )*
- _ => None
- }
- }
- }
- });
- };
-}
-
-texture_unit_enum!(cnt = 31);
diff --git a/engine/src/opengl/util.rs b/engine/src/opengl/util.rs
deleted file mode 100644
index e60778f..0000000
--- a/engine/src/opengl/util.rs
+++ /dev/null
@@ -1,30 +0,0 @@
-// May only be used when certain crate features are enabled
-#![allow(unused_macros, unused_imports)]
-
-macro_rules! gl_enum {
- (
- $visibility: vis enum $name: ident
- {$(
- $variant: ident = gl::$gl_enum: ident,
- )+}
- ) => {
- #[derive(Debug, Clone, Copy)]
- #[repr(u32)]
- $visibility enum $name
- {$(
- $variant = gl::$gl_enum,
- )+}
-
- impl $name {
- fn from_gl(num: gl::types::GLenum) -> Option<Self>
- {
- match num {
- $(gl::$gl_enum => Some(Self::$variant),)+
- _ => None
- }
- }
- }
- };
-}
-
-pub(crate) use gl_enum;
diff --git a/engine/src/opengl/vertex_array.rs b/engine/src/opengl/vertex_array.rs
deleted file mode 100644
index da5d91e..0000000
--- a/engine/src/opengl/vertex_array.rs
+++ /dev/null
@@ -1,183 +0,0 @@
-use std::mem::size_of;
-
-use crate::opengl::buffer::Buffer;
-use crate::vertex::Vertex;
-
-#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
-const VERTEX_STRIDE: i32 = size_of::<Vertex>() as i32;
-
-#[derive(Debug)]
-pub struct VertexArray
-{
- array: gl::types::GLuint,
-}
-
-impl VertexArray
-{
- pub fn new() -> Self
- {
- let mut array = 0;
-
- unsafe {
- gl::CreateVertexArrays(1, &mut array);
- }
-
- Self { array }
- }
-
- /// Draws the currently bound vertex array.
- pub fn draw_arrays(primitive_kind: PrimitiveKind, start_index: u32, cnt: u32)
- {
- unsafe {
- #[allow(clippy::cast_possible_wrap)]
- gl::DrawArrays(
- primitive_kind.into_gl(),
- start_index as gl::types::GLint,
- cnt as gl::types::GLsizei,
- );
- }
- }
-
- /// Draws the currently bound vertex array.
- pub fn draw_elements(primitive_kind: PrimitiveKind, offset: u32, cnt: u32)
- {
- unsafe {
- #[allow(clippy::cast_possible_wrap)]
- gl::DrawElements(
- primitive_kind.into_gl(),
- cnt as gl::types::GLsizei,
- gl::UNSIGNED_INT,
- (offset as gl::types::GLint) as *const _,
- );
- }
- }
-
- pub fn bind_element_buffer(&mut self, element_buffer: &Buffer<u32>)
- {
- unsafe {
- gl::VertexArrayElementBuffer(self.array, element_buffer.object());
- }
- }
-
- pub fn bind_vertex_buffer(
- &mut self,
- binding_index: u32,
- vertex_buffer: &Buffer<Vertex>,
- offset: isize,
- )
- {
- unsafe {
- gl::VertexArrayVertexBuffer(
- self.array,
- binding_index,
- vertex_buffer.object(),
- offset,
- VERTEX_STRIDE,
- );
- }
- }
-
- pub fn enable_attrib(&mut self, attrib_index: u32)
- {
- unsafe {
- gl::EnableVertexArrayAttrib(self.array, attrib_index as gl::types::GLuint);
- }
- }
-
- pub fn set_attrib_format(
- &mut self,
- attrib_index: u32,
- data_type: DataType,
- normalized: bool,
- offset: u32,
- )
- {
- unsafe {
- #[allow(clippy::cast_possible_wrap)]
- gl::VertexArrayAttribFormat(
- self.array,
- attrib_index,
- data_type.size() as gl::types::GLint,
- data_type as u32,
- if normalized { gl::TRUE } else { gl::FALSE },
- offset,
- );
- }
- }
-
- /// Associate a vertex attribute and a vertex buffer binding.
- pub fn set_attrib_vertex_buf_binding(
- &mut self,
- attrib_index: u32,
- vertex_buf_binding_index: u32,
- )
- {
- unsafe {
- gl::VertexArrayAttribBinding(
- self.array,
- attrib_index,
- vertex_buf_binding_index,
- );
- }
- }
-
- pub fn bind(&self)
- {
- unsafe { gl::BindVertexArray(self.array) }
- }
-
- /// Does a weak clone of this vertex array. The vertex array itself is NOT copied in
- /// any way this function only copies the internal vertex array ID.
- ///
- /// # Safety
- /// The returned `VertexArray` must not be dropped if another `VertexArray`
- /// referencing the same vertex array ID is used later.
- pub unsafe fn clone_unsafe(&self) -> Self
- {
- Self { array: self.array }
- }
-}
-
-impl Drop for VertexArray
-{
- fn drop(&mut self)
- {
- unsafe {
- gl::DeleteVertexArrays(1, &self.array);
- }
- }
-}
-
-#[derive(Debug)]
-pub enum PrimitiveKind
-{
- Triangles,
-}
-
-impl PrimitiveKind
-{
- fn into_gl(self) -> gl::types::GLenum
- {
- match self {
- Self::Triangles => gl::TRIANGLES,
- }
- }
-}
-
-#[derive(Debug, Clone, Copy)]
-#[repr(u32)]
-pub enum DataType
-{
- Float = gl::FLOAT,
-}
-
-impl DataType
-{
- pub fn size(self) -> u32
- {
- #[allow(clippy::cast_possible_truncation)]
- match self {
- Self::Float => size_of::<gl::types::GLfloat>() as u32,
- }
- }
-}