use crate::{
ffi::graphics as ffi,
graphics::{glsl, Texture},
system::InputStream,
LoadResult, ResourceLoadError,
};
use std::{
ffi::CString,
io::{Read, Seek},
marker::PhantomData,
ptr::{self, NonNull},
};
use super::ShaderType;
#[derive(Debug)]
pub struct Shader<'texture> {
shader: NonNull<ffi::sfShader>,
texture: PhantomData<&'texture Texture>,
}
macro_rules! shader_create {
($shader:ident, $load_block:block) => {{
let $shader =
NonNull::new(unsafe { ffi::sfShader_defaultConstruct() }).ok_or(ResourceLoadError)?;
unsafe {
if !$load_block {
ffi::sfShader_destroy($shader.as_ptr());
return Err(ResourceLoadError);
}
}
Ok(Self {
shader: $shader,
texture: PhantomData,
})
}};
}
fn c_string(source: &str) -> LoadResult<CString> {
CString::new(source).map_err(|_| ResourceLoadError)
}
impl<'texture> Shader<'texture> {
pub fn from_file(path: &str, type_: ShaderType) -> LoadResult<Self> {
shader_create!(shader, {
let path = c_string(path)?;
ffi::sfShader_loadFromFile_1(shader.as_ptr(), path.as_ptr(), type_)
})
}
pub fn from_file_vert_frag(vert: &str, frag: &str) -> LoadResult<Self> {
shader_create!(shader, {
let vert = c_string(vert)?;
let frag = c_string(frag)?;
ffi::sfShader_loadFromFile_vert_frag(shader.as_ptr(), vert.as_ptr(), frag.as_ptr())
})
}
pub fn from_file_all(vert: &str, geom: &str, frag: &str) -> LoadResult<Self> {
shader_create!(shader, {
let vert = c_string(vert)?;
let geom = c_string(geom)?;
let frag = c_string(frag)?;
ffi::sfShader_loadFromFile_all(
shader.as_ptr(),
vert.as_ptr(),
geom.as_ptr(),
frag.as_ptr(),
)
})
}
pub fn from_memory(contents: &str, type_: ShaderType) -> LoadResult<Self> {
shader_create!(shader, {
let contents = c_string(contents)?;
ffi::sfShader_loadFromMemory_1(shader.as_ptr(), contents.as_ptr(), type_)
})
}
pub fn from_memory_vert_frag(vert: &str, frag: &str) -> LoadResult<Self> {
shader_create!(shader, {
let vert = c_string(vert)?;
let frag = c_string(frag)?;
ffi::sfShader_loadFromMemory_vert_frag(shader.as_ptr(), vert.as_ptr(), frag.as_ptr())
})
}
pub fn from_memory_all(vert: &str, geom: &str, frag: &str) -> LoadResult<Self> {
shader_create!(shader, {
let vert = c_string(vert)?;
let geom = c_string(geom)?;
let frag = c_string(frag)?;
ffi::sfShader_loadFromMemory_all(
shader.as_ptr(),
vert.as_ptr(),
geom.as_ptr(),
frag.as_ptr(),
)
})
}
pub fn from_stream<T: Read + Seek>(mut source: T, type_: ShaderType) -> LoadResult<Self> {
shader_create!(shader, {
let source = InputStream::new(&mut source);
ffi::sfShader_loadFromStream_1(shader.as_ptr(), source.stream.0.as_ptr(), type_)
})
}
pub fn from_stream_vert_frag<T, U>(mut vert: T, mut frag: U) -> LoadResult<Self>
where
T: Read + Seek,
U: Read + Seek,
{
shader_create!(shader, {
let vert = InputStream::new(&mut vert);
let frag = InputStream::new(&mut frag);
ffi::sfShader_loadFromStream_vert_frag(
shader.as_ptr(),
vert.stream.0.as_ptr(),
frag.stream.0.as_ptr(),
)
})
}
pub fn from_stream_all<T, U, V>(mut vert: T, mut geom: U, mut frag: V) -> LoadResult<Self>
where
T: Read + Seek,
U: Read + Seek,
V: Read + Seek,
{
shader_create!(shader, {
let vert = InputStream::new(&mut vert);
let geom = InputStream::new(&mut geom);
let frag = InputStream::new(&mut frag);
ffi::sfShader_loadFromStream_all(
shader.as_ptr(),
vert.stream.0.as_ptr(),
geom.stream.0.as_ptr(),
frag.stream.0.as_ptr(),
)
})
}
pub fn bind(shader: Option<&Self>) {
unsafe { ffi::sfShader_bind(shader.map_or(ptr::null_mut(), |s| s.shader.as_ptr())) }
}
#[must_use]
pub fn is_available() -> bool {
unsafe { ffi::sfShader_isAvailable() }
}
#[must_use]
pub fn is_geometry_available() -> bool {
unsafe { ffi::sfShader_isGeometryAvailable() }
}
pub fn set_uniform_float(&mut self, name: &str, value: f32) {
unsafe {
let cstring = CString::new(name).unwrap();
let name = cstring.as_ptr();
ffi::sfShader_setFloatUniform(self.shader.as_ptr(), name, value);
}
}
pub fn set_uniform_vec2(&mut self, name: &str, value: glsl::Vec2) {
unsafe {
let cstring = CString::new(name).unwrap();
let name = cstring.as_ptr();
ffi::sfShader_setVec2Uniform(self.shader.as_ptr(), name, value);
}
}
pub fn set_uniform_vec3(&mut self, name: &str, value: glsl::Vec3) {
unsafe {
let cstring = CString::new(name).unwrap();
let name = cstring.as_ptr();
ffi::sfShader_setVec3Uniform(self.shader.as_ptr(), name, value);
}
}
pub fn set_uniform_vec4<V>(&mut self, name: &str, value: V)
where
V: Into<glsl::Vec4>,
{
unsafe {
let cstring = CString::new(name).unwrap();
let name = cstring.as_ptr();
ffi::sfShader_setVec4Uniform(self.shader.as_ptr(), name, value.into().raw());
}
}
pub fn set_uniform_int(&mut self, name: &str, value: i32) {
unsafe {
let cstring = CString::new(name).unwrap();
let name = cstring.as_ptr();
ffi::sfShader_setIntUniform(self.shader.as_ptr(), name, value);
}
}
pub fn set_uniform_ivec2(&mut self, name: &str, value: glsl::IVec2) {
unsafe {
let cstring = CString::new(name).unwrap();
let name = cstring.as_ptr();
ffi::sfShader_setIvec2Uniform(self.shader.as_ptr(), name, value);
}
}
pub fn set_uniform_ivec3(&mut self, name: &str, value: glsl::IVec3) {
unsafe {
let cstring = CString::new(name).unwrap();
let name = cstring.as_ptr();
ffi::sfShader_setIvec3Uniform(self.shader.as_ptr(), name, value.into());
}
}
pub fn set_uniform_ivec4<V>(&mut self, name: &str, value: V)
where
V: Into<glsl::IVec4>,
{
unsafe {
let cstring = CString::new(name).unwrap();
let name = cstring.as_ptr();
ffi::sfShader_setIvec4Uniform(self.shader.as_ptr(), name, value.into().raw());
}
}
pub fn set_uniform_bool(&mut self, name: &str, value: bool) {
unsafe {
let cstring = CString::new(name).unwrap();
let name = cstring.as_ptr();
ffi::sfShader_setBoolUniform(self.shader.as_ptr(), name, value);
}
}
pub fn set_uniform_bvec2(&mut self, name: &str, value: glsl::BVec2) {
unsafe {
let cstring = CString::new(name).unwrap();
let name = cstring.as_ptr();
ffi::sfShader_setBvec2Uniform(self.shader.as_ptr(), name, value.into());
}
}
pub fn set_uniform_bvec3(&mut self, name: &str, value: glsl::BVec3) {
unsafe {
let cstring = CString::new(name).unwrap();
let name = cstring.as_ptr();
ffi::sfShader_setBvec3Uniform(self.shader.as_ptr(), name, value.into());
}
}
pub fn set_uniform_bvec4(&mut self, name: &str, value: glsl::BVec4) {
unsafe {
let cstring = CString::new(name).unwrap();
let name = cstring.as_ptr();
ffi::sfShader_setBvec4Uniform(self.shader.as_ptr(), name, value.into());
}
}
pub fn set_uniform_mat3<V>(&mut self, name: &str, value: V)
where
V: Into<glsl::Mat3>,
{
unsafe {
let cstring = CString::new(name).unwrap();
let name = cstring.as_ptr();
let value = value.into();
let ptr: *const _ = &value.0;
ffi::sfShader_setMat3Uniform(self.shader.as_ptr(), name, ptr as *const _);
}
}
pub fn set_uniform_mat4<V>(&mut self, name: &str, value: V)
where
V: Into<glsl::Mat4>,
{
unsafe {
let cstring = CString::new(name).unwrap();
let name = cstring.as_ptr();
let value = value.into();
let ptr: *const _ = &value.0;
ffi::sfShader_setMat4Uniform(self.shader.as_ptr(), name, ptr as *const _);
}
}
pub fn set_uniform_texture(&mut self, name: &str, value: &'texture Texture) {
unsafe {
let cstring = CString::new(name).unwrap();
let name = cstring.as_ptr();
ffi::sfShader_setTextureUniform(self.shader.as_ptr(), name, value);
}
}
pub fn set_uniform_current_texture(&mut self, name: &str) {
unsafe {
let cstring = CString::new(name).unwrap();
let name = cstring.as_ptr();
ffi::sfShader_setCurrentTextureUniform(self.shader.as_ptr(), name);
}
}
pub fn set_uniform_array_float(&mut self, name: &str, array: &[f32]) {
unsafe {
let cstring = CString::new(name).unwrap();
let name = cstring.as_ptr();
let len = array.len();
ffi::sfShader_setFloatUniformArray(self.shader.as_ptr(), name, array.as_ptr(), len);
}
}
pub fn set_uniform_array_vec2(&mut self, name: &str, array: &[glsl::Vec2]) {
unsafe {
let cstring = CString::new(name).unwrap();
let name = cstring.as_ptr();
let len = array.len();
let ptr = array.as_ptr();
ffi::sfShader_setVec2UniformArray(self.shader.as_ptr(), name, ptr, len);
}
}
pub fn set_uniform_array_vec3(&mut self, name: &str, array: &[glsl::Vec3]) {
unsafe {
let cstring = CString::new(name).unwrap();
let name = cstring.as_ptr();
let len = array.len();
let ptr = array.as_ptr();
ffi::sfShader_setVec3UniformArray(self.shader.as_ptr(), name, ptr, len);
}
}
pub fn set_uniform_array_vec4(&mut self, name: &str, array: &[glsl::Vec4]) {
unsafe {
let cstring = CString::new(name).unwrap();
let name = cstring.as_ptr();
let len = array.len();
let ptr = array.as_ptr() as *const ffi::sfGlslVec4;
ffi::sfShader_setVec4UniformArray(self.shader.as_ptr(), name, ptr, len);
}
}
pub fn set_uniform_array_mat3(&mut self, name: &str, array: &[glsl::Mat3]) {
unsafe {
let cstring = CString::new(name).unwrap();
let name = cstring.as_ptr();
let len = array.len();
let ptr = array.as_ptr() as *const ffi::sfGlslMat3;
ffi::sfShader_setMat3UniformArray(self.shader.as_ptr(), name, ptr, len);
}
}
pub fn set_uniform_array_mat4(&mut self, name: &str, array: &[glsl::Mat4]) {
unsafe {
let cstring = CString::new(name).unwrap();
let name = cstring.as_ptr();
let len = array.len();
let ptr = array.as_ptr() as *const ffi::sfGlslMat4;
ffi::sfShader_setMat4UniformArray(self.shader.as_ptr(), name, ptr, len);
}
}
#[must_use]
pub fn native_handle(&self) -> u32 {
unsafe { ffi::sfShader_getNativeHandle(self.shader.as_ptr()) }
}
pub(super) fn raw(&self) -> *const ffi::sfShader {
self.shader.as_ptr()
}
}
impl<'texture> Drop for Shader<'texture> {
fn drop(&mut self) {
unsafe { ffi::sfShader_destroy(self.shader.as_ptr()) }
}
}