reverie_engine_opengl/
shader.rs

1//! シェーダプログラム
2
3use crate::gl;
4use crate::gl::types::*;
5use crate::gl::Gl;
6
7use reverie_util::math::nalgebra;
8use std::collections::HashMap;
9use std::ffi::{CStr, CString};
10use std::fs::File;
11use std::io::Read;
12use std::str;
13
14/// プログラム
15#[derive(Debug)]
16pub struct Program {
17    gl: Gl,
18    id: GLuint,
19}
20
21impl Program {
22    pub fn default_uv(gl: Gl) -> Result<Self, String> {
23        let vert = Shader::from_vert_code(
24            Gl::clone(&gl),
25            &CString::new(include_str!("../resources/uv.vert")).unwrap(),
26        )?;
27        let frag = Shader::from_frag_code(
28            Gl::clone(&gl),
29            &CString::new(include_str!("../resources/uv.frag")).unwrap(),
30        )?;
31        Self::from_shaders(gl, &[vert, frag])
32    }
33
34    /// 頂点シェーダーとフラグメントシェーダーをリンクしてプログラムを作る
35    ///
36    /// # Returns
37    ///
38    /// `Ok`のときは`Program`、`Err`のときはエラーメッセージ
39    pub fn from_shaders(gl: Gl, shaders: &[Shader]) -> Result<Self, String> {
40        let program_id = unsafe { gl.CreateProgram() };
41        for shader in shaders {
42            unsafe {
43                gl.AttachShader(program_id, shader.raw_id());
44            }
45        }
46
47        unsafe {
48            gl.LinkProgram(program_id);
49        }
50
51        let mut success: GLint = 1;
52        unsafe {
53            gl.GetProgramiv(program_id, gl::LINK_STATUS, &mut success);
54        }
55        if success == 0 {
56            let mut len: GLint = 0;
57            unsafe { gl.GetProgramiv(program_id, gl::INFO_LOG_LENGTH, &mut len) }
58            let error = create_whitespace_cstring_with_len(len as usize);
59
60            unsafe {
61                // as_ptr() の戻り値へ書き込むのは未定義動作である (as_mut_ptr()は用意されていない)
62                //
63                // > The returned pointer is read-only; writing to it (including passing it to C code that writes to it) causes undefined behavior.
64                // https://doc.rust-lang.org/std/ffi/struct.CString.html#method.as_ptr
65                //
66                // でも動いているのでとりあえずそのまま使う。
67                // glow にもそういうコードがある。
68                // https://github.com/grovesNL/glow/blob/eb44a878a756d5ddce8505158690ec9bd272be8f/src/native.rs#L422
69                #[allow(clippy::as_ptr_cast_mut)]
70                gl.GetProgramInfoLog(
71                    program_id,
72                    len,
73                    std::ptr::null_mut(),
74                    error.as_ptr() as *mut GLchar,
75                )
76            }
77
78            return Err(error.to_string_lossy().into_owned());
79        }
80
81        for shader in shaders {
82            unsafe { gl.DetachShader(program_id, shader.raw_id()) }
83        }
84        Ok(Self { gl, id: program_id })
85    }
86
87    /// OpenGLの関数に渡すためのプログラムID
88    ///
89    /// # Safety
90    /// glDeleteProgramされていない限り安全
91    pub const unsafe fn raw_id(&self) -> GLuint {
92        self.id
93    }
94
95    /// このプログラムをOpenGLで使うように設定する(glUseProgram)
96    pub fn set_used(&self) {
97        unsafe {
98            self.gl.UseProgram(self.id);
99        }
100    }
101
102    #[allow(clippy::missing_safety_doc)]
103    // TODO: Safetyの説明を書く
104    /// ユニフォーム変数を送る
105    pub unsafe fn set_uniforms(&self, uniforms: &UniformVariables<'_>) {
106        for (name, uniform) in uniforms.map.iter() {
107            self.set_uniform(name, uniform);
108        }
109    }
110
111    #[allow(clippy::missing_safety_doc)]
112    // TODO: Safetyの説明を書く
113    /// ユニフォーム変数を送る
114    pub unsafe fn set_uniform(&self, name: &CStr, value: &Uniform<'_>) {
115        match *value {
116            Uniform::Bool(b) => self.set_bool(name, b),
117            Uniform::Int(i) => self.set_int(name, i),
118            Uniform::Float(f) => self.set_float(name, f),
119            Uniform::Vector3(v) => self.set_vector3(name, v),
120            Uniform::TripleFloat(f1, f2, f3) => self.set_vec3(name, f1, f2, f3),
121            Uniform::Matrix4(m) => self.set_mat4(name, m),
122        }
123    }
124
125    #[allow(clippy::missing_safety_doc)]
126    // TODO: Safetyの説明を書く
127    /// bool型のユニフォーム変数を送る
128    pub unsafe fn set_bool(&self, name: &CStr, value: bool) {
129        self.gl.Uniform1i(
130            self.gl.GetUniformLocation(self.id, name.as_ptr()),
131            value as i32,
132        );
133    }
134
135    #[allow(clippy::missing_safety_doc)]
136    // TODO: Safetyの説明を書く
137    /// int型のユニフォーム変数を送る
138    pub unsafe fn set_int(&self, name: &CStr, value: i32) {
139        self.gl
140            .Uniform1i(self.gl.GetUniformLocation(self.id, name.as_ptr()), value);
141    }
142
143    #[allow(clippy::missing_safety_doc)]
144    // TODO: Safetyの説明を書く
145    /// float型のユニフォーム変数を送る
146    pub unsafe fn set_float(&self, name: &CStr, value: f32) {
147        self.gl
148            .Uniform1f(self.gl.GetUniformLocation(self.id, name.as_ptr()), value);
149    }
150
151    #[allow(clippy::missing_safety_doc)]
152    // TODO: Safetyの説明を書く
153    /// 3次元ベクトル型(float)のユニフォーム変数を送る
154    pub unsafe fn set_vector3(&self, name: &CStr, value: &nalgebra::Vector3<f32>) {
155        self.gl.Uniform3fv(
156            self.gl.GetUniformLocation(self.id, name.as_ptr()),
157            1,
158            value.as_ptr(),
159        );
160    }
161
162    #[allow(clippy::missing_safety_doc)]
163    // TODO: Safetyの説明を書く
164    /// float型のユニフォーム変数3つを送る
165    pub unsafe fn set_vec3(&self, name: &CStr, x: f32, y: f32, z: f32) {
166        self.gl
167            .Uniform3f(self.gl.GetUniformLocation(self.id, name.as_ptr()), x, y, z);
168    }
169
170    #[allow(clippy::missing_safety_doc)]
171    // TODO: Safetyの説明を書く
172    /// 4次行列型(float)のユニフォーム変数を送る
173    pub unsafe fn set_mat4(&self, name: &CStr, mat: &nalgebra::Matrix4<f32>) {
174        self.gl.UniformMatrix4fv(
175            self.gl.GetUniformLocation(self.id, name.as_ptr()),
176            1,
177            gl::FALSE,
178            mat.as_ptr(),
179        );
180    }
181}
182
183impl Drop for Program {
184    /// OpenGLが保持しているプログラムの実体も削除される(glDeleteProgram)
185    fn drop(&mut self) {
186        unsafe {
187            self.gl.DeleteProgram(self.id);
188        }
189    }
190}
191
192/// 頂点シェーダーまたはフラグメントシェーダー
193#[derive(Debug)]
194pub struct Shader {
195    gl: Gl,
196    id: GLuint,
197}
198
199impl Shader {
200    /// シェーダのソースコードをコンパイルする
201    pub fn from_code(gl: Gl, code: &CStr, kind: GLenum) -> Result<Self, String> {
202        let id = unsafe { gl.CreateShader(kind) };
203
204        unsafe {
205            gl.ShaderSource(id, 1, &code.as_ptr(), std::ptr::null());
206            gl.CompileShader(id);
207        }
208        let mut success: GLint = 1;
209        unsafe {
210            gl.GetShaderiv(id, gl::COMPILE_STATUS, &mut success);
211        }
212
213        /* コンパイル失敗の時 */
214        if success == 0 {
215            let mut len: GLint = 0;
216            unsafe {
217                gl.GetShaderiv(id, gl::INFO_LOG_LENGTH, &mut len);
218            }
219
220            let error = create_whitespace_cstring_with_len(len as usize);
221            unsafe {
222                // as_ptr() の戻り値へ書き込むのは未定義動作である (as_mut_ptr()は用意されていない)
223                //
224                // > The returned pointer is read-only; writing to it (including passing it to C code that writes to it) causes undefined behavior.
225                // https://doc.rust-lang.org/std/ffi/struct.CString.html#method.as_ptr
226                //
227                // でも動いているのでとりあえずそのまま使う。
228                // glow にもそういうコードがある。
229                // https://github.com/grovesNL/glow/blob/eb44a878a756d5ddce8505158690ec9bd272be8f/src/native.rs#L333
230                #[allow(clippy::as_ptr_cast_mut)]
231                gl.GetShaderInfoLog(id, len, std::ptr::null_mut(), error.as_ptr() as *mut GLchar);
232            }
233
234            return Err(error.to_string_lossy().into_owned());
235        }
236        Ok(Self { gl, id })
237    }
238
239    /// ファイルからシェーダーのコードを読み込み、コンパイルする
240    ///
241    /// # Panics
242    ///
243    /// ファイルが開けなかった時にパニックする
244    pub fn from_file(gl: Gl, path: &str, kind: GLenum) -> Result<Self, String> {
245        let mut file = File::open(path)
246            .unwrap_or_else(|err| panic!("ERROR: {} : Failed to open file '{}'", err, path));
247        let mut code = String::new();
248        file.read_to_string(&mut code)
249            .unwrap_or_else(|err| panic!("ERROR: {} : Failed to read file '{}'", err, path));
250
251        let code = CString::new(code.as_bytes()).unwrap();
252
253        Self::from_code(gl, &code, kind)
254    }
255
256    /// 頂点シェーダーをファイルから作る
257    pub fn from_vert_file(gl: Gl, path: &str) -> Result<Self, String> {
258        Self::from_file(gl, path, gl::VERTEX_SHADER)
259    }
260
261    /// フラグメントシェーダーをファイルから作る
262    pub fn from_frag_file(gl: Gl, path: &str) -> Result<Self, String> {
263        Self::from_file(gl, path, gl::FRAGMENT_SHADER)
264    }
265
266    /// 頂点シェーダーをソースコードから作る
267    pub fn from_vert_code(gl: Gl, code: &CStr) -> Result<Self, String> {
268        Self::from_code(gl, code, gl::VERTEX_SHADER)
269    }
270
271    /// フラグメントシェーダーをソースコードから作る
272    pub fn from_frag_code(gl: Gl, code: &CStr) -> Result<Self, String> {
273        Self::from_code(gl, code, gl::FRAGMENT_SHADER)
274    }
275
276    /// OpenGLの関数に渡すためのシェーダーID
277    ///
278    /// # Safety
279    /// glDeleteShaderされていない限り安全
280    pub const unsafe fn raw_id(&self) -> GLuint {
281        self.id
282    }
283}
284
285impl Drop for Shader {
286    /// OpenGLが保持しているシェーダーの実体も削除される(glDeleteProgram)
287    fn drop(&mut self) {
288        unsafe {
289            self.gl.DeleteShader(self.id);
290        }
291    }
292}
293
294fn create_whitespace_cstring_with_len(len: usize) -> CString {
295    let mut buffer: Vec<u8> = Vec::with_capacity(len + 1);
296    buffer.extend(std::iter::once(b' ').cycle().take(len));
297    unsafe { CString::from_vec_unchecked(buffer) }
298}
299
300/// ユニフォーム変数
301#[derive(Debug)]
302pub enum Uniform<'a> {
303    Bool(bool),
304    Int(i32),
305    Float(f32),
306    Vector3(&'a nalgebra::Vector3<f32>),
307    TripleFloat(f32, f32, f32),
308    Matrix4(&'a nalgebra::Matrix4<f32>),
309}
310
311/// ユニフォーム変数のセット
312pub struct UniformVariables<'a> {
313    map: HashMap<&'a CStr, Uniform<'a>>,
314}
315
316impl<'a> Default for UniformVariables<'a> {
317    fn default() -> Self {
318        Self::new()
319    }
320}
321
322impl<'a> UniformVariables<'a> {
323    /// 空のセットを作る
324    pub fn new() -> Self {
325        Self {
326            map: HashMap::new(),
327        }
328    }
329
330    /// 名前を指定してユニフォーム変数を追加する
331    pub fn add(&mut self, name: &'a CStr, value: Uniform<'a>) -> &mut Self {
332        self.map.insert(name, value);
333        self
334    }
335}