diff options
Diffstat (limited to 'engine/src/opengl')
| -rw-r--r-- | engine/src/opengl/buffer.rs | 92 | ||||
| -rw-r--r-- | engine/src/opengl/debug.rs | 145 | ||||
| -rw-r--r-- | engine/src/opengl/glsl.rs | 616 | ||||
| -rw-r--r-- | engine/src/opengl/mod.rs | 108 | ||||
| -rw-r--r-- | engine/src/opengl/shader.rs | 240 | ||||
| -rw-r--r-- | engine/src/opengl/texture.rs | 240 | ||||
| -rw-r--r-- | engine/src/opengl/util.rs | 30 | ||||
| -rw-r--r-- | engine/src/opengl/vertex_array.rs | 183 |
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, - } - } -} |
