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.

339 lines
8.6 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. Uniform::TextureVec(i) => gl::uniform_1iv(location, i.len() as _, i.as_ptr()),
  73. }
  74. Ok(())
  75. }
  76. pub fn uniform_location<T>(&self, name: T) -> Result<gl::GLint, Error>
  77. where
  78. T: Into<String>,
  79. {
  80. let name = Into::<String>::into(name);
  81. let name = CString::new(name)?;
  82. let location = Error::checked(|| gl::get_uniform_location(self.id, name.as_ptr()))?;
  83. if location >= 0 {
  84. Ok(location)
  85. } else {
  86. Err(Error::ShaderUnknownUniform(name.into_string().unwrap()))
  87. }
  88. }
  89. pub fn uniform_block_index<T>(&self, name: T) -> Result<gl::GLuint, Error>
  90. where
  91. T: Into<String>,
  92. {
  93. let name = Into::<String>::into(name);
  94. let name = CString::new(name)?;
  95. let location = Error::checked(|| gl::get_uniform_block_index(self.id, name.as_ptr()))?;
  96. if location != gl::INVALID_INDEX {
  97. Ok(location)
  98. } else {
  99. Err(Error::ShaderUnknownUniform(name.into_string().unwrap()))
  100. }
  101. }
  102. pub fn uniform_block_binding<T>(&self, index: T, binding: gl::GLuint) -> Result<(), Error>
  103. where
  104. T: IntoUniformBlockIndex,
  105. {
  106. let index = IntoUniformBlockIndex::into_index(index, &self)?;
  107. gl::uniform_block_binding(self.id, index, binding);
  108. Ok(())
  109. }
  110. }
  111. impl Drop for Program {
  112. fn drop(&mut self) {
  113. gl::delete_program(self.id);
  114. }
  115. }
  116. impl Bindable for Program {
  117. fn bind(&self) {
  118. gl::use_program(self.id);
  119. }
  120. fn unbind(&self) {
  121. gl::use_program(0);
  122. }
  123. }
  124. /* Shader */
  125. pub struct Shader {
  126. id: gl::GLuint,
  127. }
  128. impl Shader {
  129. pub fn from_string<F>(type_: Type, mut source: String, f: F) -> Result<Self, Error>
  130. where
  131. F: Fn(&str) -> Result<String, Error>,
  132. {
  133. while let Some(p) = source.find("#pragma include ") {
  134. let s = &source[p..];
  135. let e = s.find('\n').unwrap_or_else(|| s.len());
  136. let s = &s[..e];
  137. let c = f(&s[16..])?;
  138. source = source.replace(s, &c);
  139. }
  140. let id = gl::create_shader(type_.as_enum());
  141. let id = Error::err_if(&0, id)?;
  142. let source = CString::new(source)?;
  143. let ptr = source.as_ptr();
  144. let ptr: *const *const _ = &ptr;
  145. gl::shader_source(id, 1, ptr as *const *const _, null());
  146. gl::compile_shader(id);
  147. let mut success = 1;
  148. gl::get_shader_iv(id, gl::COMPILE_STATUS, &mut success);
  149. if success != 1 {
  150. let mut len = 0;
  151. gl::get_shader_iv(id, gl::INFO_LOG_LENGTH, &mut len);
  152. let mut buffer = Vec::<u8>::with_capacity(len as usize + 1);
  153. buffer.resize(len as usize, 0);
  154. gl::get_shader_info_log(
  155. id,
  156. len,
  157. null_mut(),
  158. buffer.as_mut_ptr() as *mut gl::types::GLchar,
  159. );
  160. let msg = from_utf8(&buffer)?;
  161. let code = source.into_string().unwrap();
  162. let code = code
  163. .lines()
  164. .enumerate()
  165. .map(|(i, s)| format!("{:03}: {}\n", i + 1, s))
  166. .collect();
  167. return Err(Error::ShaderCompile {
  168. code,
  169. error: msg.into(),
  170. });
  171. }
  172. Ok(Self { id })
  173. }
  174. pub fn from_reader<R, F>(type_: Type, reader: &mut R, f: F) -> Result<Self, Error>
  175. where
  176. R: Read,
  177. F: Fn(&str) -> Result<String, Error>,
  178. {
  179. let mut source = String::new();
  180. reader.read_to_string(&mut source)?;
  181. Self::from_string(type_, source, f)
  182. }
  183. pub fn from_file<P, F>(type_: Type, path: P, f: F) -> Result<Self, Error>
  184. where
  185. P: AsRef<Path>,
  186. F: Fn(&str) -> Result<String, Error>,
  187. {
  188. let mut file = File::open(&path)?;
  189. match Self::from_reader(type_, &mut file, f) {
  190. Ok(v) => Ok(v),
  191. Err(Error::ShaderCompile { code, error }) => Err(Error::ShaderCompile {
  192. code: format!("{}\n{}", path.as_ref().display(), code),
  193. error,
  194. }),
  195. Err(err) => Err(err),
  196. }
  197. }
  198. pub fn id(&self) -> gl::GLuint {
  199. self.id
  200. }
  201. }
  202. impl Drop for Shader {
  203. fn drop(&mut self) {
  204. gl::delete_shader(self.id);
  205. }
  206. }
  207. /* Type */
  208. #[derive(Debug, Copy, Clone, Eq, PartialEq)]
  209. pub enum Type {
  210. Vertex,
  211. Compute,
  212. Fragment,
  213. Geometry,
  214. TesslationControl,
  215. TesslationEvaluation,
  216. }
  217. impl AsEnum for Type {
  218. fn as_enum(&self) -> gl::GLenum {
  219. match self {
  220. Self::Vertex => gl::VERTEX_SHADER,
  221. Self::Compute => gl::COMPUTE_SHADER,
  222. Self::Fragment => gl::FRAGMENT_SHADER,
  223. Self::Geometry => gl::GEOMETRY_SHADER,
  224. Self::TesslationControl => gl::TESS_CONTROL_SHADER,
  225. Self::TesslationEvaluation => gl::TESS_EVALUATION_SHADER,
  226. }
  227. }
  228. }
  229. /* Uniform */
  230. pub enum Uniform<'a> {
  231. Matrix4f(&'a Matrix4f),
  232. Vector4f(&'a Vector4f),
  233. Vector2f(&'a Vector2f),
  234. Float(f32),
  235. Texture(gl::GLint),
  236. TextureVec(&'a [gl::GLint]),
  237. }
  238. /* IntoUniformLocation */
  239. pub trait IntoUniformLocation {
  240. fn into_location(self, program: &Program) -> Result<gl::GLint, Error>;
  241. }
  242. impl IntoUniformLocation for gl::GLint {
  243. fn into_location(self, _program: &Program) -> Result<gl::GLint, Error> {
  244. Ok(self)
  245. }
  246. }
  247. impl IntoUniformLocation for &str {
  248. fn into_location(self, program: &Program) -> Result<gl::GLint, Error> {
  249. program.uniform_location(self)
  250. }
  251. }
  252. impl IntoUniformLocation for String {
  253. fn into_location(self, program: &Program) -> Result<gl::GLint, Error> {
  254. program.uniform_location(self)
  255. }
  256. }
  257. /* IntoUniformBlockIndex */
  258. pub trait IntoUniformBlockIndex {
  259. fn into_index(self, program: &Program) -> Result<gl::GLuint, Error>;
  260. }
  261. impl IntoUniformBlockIndex for gl::GLuint {
  262. fn into_index(self, _program: &Program) -> Result<gl::GLuint, Error> {
  263. Ok(self)
  264. }
  265. }
  266. impl IntoUniformBlockIndex for &str {
  267. fn into_index(self, program: &Program) -> Result<gl::GLuint, Error> {
  268. program.uniform_block_index(self)
  269. }
  270. }
  271. impl IntoUniformBlockIndex for String {
  272. fn into_index(self, program: &Program) -> Result<gl::GLuint, Error> {
  273. program.uniform_block_index(self)
  274. }
  275. }