You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

337 lines
8.5 KiB

  1. use std::ffi::CString;
  2. use std::fs::File;
  3. use std::io::Read;
  4. use std::path::Path;
  5. use std::ptr::{null, null_mut};
  6. use std::str::from_utf8;
  7. use crate::{
  8. error::Error,
  9. matrix::Matrix4f,
  10. misc::{AsEnum, Bindable},
  11. vector::{Vector2f, Vector4f},
  12. };
  13. /* Programm */
  14. #[derive(Default)]
  15. pub struct Program {
  16. id: gl::GLuint,
  17. }
  18. impl Program {
  19. pub fn from_shaders<I>(iter: I) -> Result<Self, Error>
  20. where
  21. I: IntoIterator<Item = Shader>,
  22. {
  23. Self::from_shaders_result(iter.into_iter().map(Ok))
  24. }
  25. pub fn from_shaders_result<I, E>(iter: I) -> Result<Self, E>
  26. where
  27. I: IntoIterator<Item = Result<Shader, E>>,
  28. E: From<Error>,
  29. {
  30. let id = gl::create_program();
  31. let id = Error::err_if(&0, id)?;
  32. let shaders = iter.into_iter().collect::<Result<Vec<_>, _>>()?;
  33. for shader in &shaders {
  34. gl::attach_shader(id, shader.id());
  35. }
  36. gl::link_program(id);
  37. for shader in &shaders {
  38. gl::detach_shader(id, shader.id());
  39. }
  40. let mut success = 1;
  41. gl::get_program_iv(id, gl::LINK_STATUS, &mut success);
  42. if success != 1 {
  43. let mut len = 0;
  44. gl::get_program_iv(id, gl::INFO_LOG_LENGTH, &mut len);
  45. let mut buffer = Vec::<u8>::with_capacity(len as usize + 1);
  46. buffer.resize(len as usize, 0);
  47. gl::get_program_info_log(
  48. id,
  49. len,
  50. null_mut(),
  51. buffer.as_mut_ptr() as *mut gl::types::GLchar,
  52. );
  53. let msg = from_utf8(&buffer).map_err(Error::Utf8Error)?;
  54. return Err(Error::ShaderLink(msg.into()).into());
  55. }
  56. Ok(Program { id })
  57. }
  58. pub fn id(&self) -> gl::GLuint {
  59. self.id
  60. }
  61. pub fn uniform<T>(&self, location: T, uniform: Uniform<'_>) -> Result<(), Error>
  62. where
  63. T: IntoUniformLocation,
  64. {
  65. let location = IntoUniformLocation::into_location(location, &self)?;
  66. match uniform {
  67. Uniform::Matrix4f(v) => gl::uniform_matrix_4fv(location, 1, gl::FALSE, v.as_ptr()),
  68. Uniform::Vector4f(v) => gl::uniform_4fv(location, 1, v.as_ptr()),
  69. Uniform::Vector2f(v) => gl::uniform_2fv(location, 1, v.as_ptr()),
  70. Uniform::Float(v) => gl::uniform_1f(location, v),
  71. Uniform::Texture(i) => gl::uniform_1i(location, i),
  72. }
  73. Ok(())
  74. }
  75. pub fn uniform_location<T>(&self, name: T) -> Result<gl::GLint, Error>
  76. where
  77. T: Into<String>,
  78. {
  79. let name = Into::<String>::into(name);
  80. let name = CString::new(name)?;
  81. let location = Error::checked(|| gl::get_uniform_location(self.id, name.as_ptr()))?;
  82. if location >= 0 {
  83. Ok(location)
  84. } else {
  85. Err(Error::ShaderUnknownUniform(name.into_string().unwrap()))
  86. }
  87. }
  88. pub fn uniform_block_index<T>(&self, name: T) -> Result<gl::GLuint, Error>
  89. where
  90. T: Into<String>,
  91. {
  92. let name = Into::<String>::into(name);
  93. let name = CString::new(name)?;
  94. let location = Error::checked(|| gl::get_uniform_block_index(self.id, name.as_ptr()))?;
  95. if location != gl::INVALID_INDEX {
  96. Ok(location)
  97. } else {
  98. Err(Error::ShaderUnknownUniform(name.into_string().unwrap()))
  99. }
  100. }
  101. pub fn uniform_block_binding<T>(&self, index: T, binding: gl::GLuint) -> Result<(), Error>
  102. where
  103. T: IntoUniformBlockIndex,
  104. {
  105. let index = IntoUniformBlockIndex::into_index(index, &self)?;
  106. gl::uniform_block_binding(self.id, index, binding);
  107. Ok(())
  108. }
  109. }
  110. impl Drop for Program {
  111. fn drop(&mut self) {
  112. gl::delete_program(self.id);
  113. }
  114. }
  115. impl Bindable for Program {
  116. fn bind(&self) {
  117. gl::use_program(self.id);
  118. }
  119. fn unbind(&self) {
  120. gl::use_program(0);
  121. }
  122. }
  123. /* Shader */
  124. pub struct Shader {
  125. id: gl::GLuint,
  126. }
  127. impl Shader {
  128. pub fn from_string<F>(type_: Type, mut source: String, f: F) -> Result<Self, Error>
  129. where
  130. F: Fn(&str) -> Result<String, Error>,
  131. {
  132. while let Some(p) = source.find("#pragma include ") {
  133. let s = &source[p..];
  134. let e = s.find('\n').unwrap_or_else(|| s.len());
  135. let s = &s[..e];
  136. let c = f(&s[16..])?;
  137. source = source.replace(s, &c);
  138. }
  139. let id = gl::create_shader(type_.as_enum());
  140. let id = Error::err_if(&0, id)?;
  141. let source = CString::new(source)?;
  142. let ptr = source.as_ptr();
  143. let ptr: *const *const _ = &ptr;
  144. gl::shader_source(id, 1, ptr as *const *const _, null());
  145. gl::compile_shader(id);
  146. let mut success = 1;
  147. gl::get_shader_iv(id, gl::COMPILE_STATUS, &mut success);
  148. if success != 1 {
  149. let mut len = 0;
  150. gl::get_shader_iv(id, gl::INFO_LOG_LENGTH, &mut len);
  151. let mut buffer = Vec::<u8>::with_capacity(len as usize + 1);
  152. buffer.resize(len as usize, 0);
  153. gl::get_shader_info_log(
  154. id,
  155. len,
  156. null_mut(),
  157. buffer.as_mut_ptr() as *mut gl::types::GLchar,
  158. );
  159. let msg = from_utf8(&buffer)?;
  160. let code = source.into_string().unwrap();
  161. let code = code
  162. .lines()
  163. .enumerate()
  164. .map(|(i, s)| format!("{:03}: {}\n", i + 1, s))
  165. .collect();
  166. return Err(Error::ShaderCompile {
  167. code,
  168. error: msg.into(),
  169. });
  170. }
  171. Ok(Self { id })
  172. }
  173. pub fn from_reader<R, F>(type_: Type, reader: &mut R, f: F) -> Result<Self, Error>
  174. where
  175. R: Read,
  176. F: Fn(&str) -> Result<String, Error>,
  177. {
  178. let mut source = String::new();
  179. reader.read_to_string(&mut source)?;
  180. Self::from_string(type_, source, f)
  181. }
  182. pub fn from_file<P, F>(type_: Type, path: P, f: F) -> Result<Self, Error>
  183. where
  184. P: AsRef<Path>,
  185. F: Fn(&str) -> Result<String, Error>,
  186. {
  187. let mut file = File::open(&path)?;
  188. match Self::from_reader(type_, &mut file, f) {
  189. Ok(v) => Ok(v),
  190. Err(Error::ShaderCompile { code, error }) => Err(Error::ShaderCompile {
  191. code: format!("{}\n{}", path.as_ref().display(), code),
  192. error,
  193. }),
  194. Err(err) => Err(err),
  195. }
  196. }
  197. pub fn id(&self) -> gl::GLuint {
  198. self.id
  199. }
  200. }
  201. impl Drop for Shader {
  202. fn drop(&mut self) {
  203. gl::delete_shader(self.id);
  204. }
  205. }
  206. /* Type */
  207. #[derive(Debug, Copy, Clone, Eq, PartialEq)]
  208. pub enum Type {
  209. Vertex,
  210. Compute,
  211. Fragment,
  212. Geometry,
  213. TesslationControl,
  214. TesslationEvaluation,
  215. }
  216. impl AsEnum for Type {
  217. fn as_enum(&self) -> gl::GLenum {
  218. match self {
  219. Self::Vertex => gl::VERTEX_SHADER,
  220. Self::Compute => gl::COMPUTE_SHADER,
  221. Self::Fragment => gl::FRAGMENT_SHADER,
  222. Self::Geometry => gl::GEOMETRY_SHADER,
  223. Self::TesslationControl => gl::TESS_CONTROL_SHADER,
  224. Self::TesslationEvaluation => gl::TESS_EVALUATION_SHADER,
  225. }
  226. }
  227. }
  228. /* Uniform */
  229. pub enum Uniform<'a> {
  230. Matrix4f(&'a Matrix4f),
  231. Vector4f(&'a Vector4f),
  232. Vector2f(&'a Vector2f),
  233. Float(f32),
  234. Texture(gl::GLint),
  235. }
  236. /* IntoUniformLocation */
  237. pub trait IntoUniformLocation {
  238. fn into_location(self, program: &Program) -> Result<gl::GLint, Error>;
  239. }
  240. impl IntoUniformLocation for gl::GLint {
  241. fn into_location(self, _program: &Program) -> Result<gl::GLint, Error> {
  242. Ok(self)
  243. }
  244. }
  245. impl IntoUniformLocation for &str {
  246. fn into_location(self, program: &Program) -> Result<gl::GLint, Error> {
  247. program.uniform_location(self)
  248. }
  249. }
  250. impl IntoUniformLocation for String {
  251. fn into_location(self, program: &Program) -> Result<gl::GLint, Error> {
  252. program.uniform_location(self)
  253. }
  254. }
  255. /* IntoUniformBlockIndex */
  256. pub trait IntoUniformBlockIndex {
  257. fn into_index(self, program: &Program) -> Result<gl::GLuint, Error>;
  258. }
  259. impl IntoUniformBlockIndex for gl::GLuint {
  260. fn into_index(self, _program: &Program) -> Result<gl::GLuint, Error> {
  261. Ok(self)
  262. }
  263. }
  264. impl IntoUniformBlockIndex for &str {
  265. fn into_index(self, program: &Program) -> Result<gl::GLuint, Error> {
  266. program.uniform_block_index(self)
  267. }
  268. }
  269. impl IntoUniformBlockIndex for String {
  270. fn into_index(self, program: &Program) -> Result<gl::GLuint, Error> {
  271. program.uniform_block_index(self)
  272. }
  273. }