1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
use rask_engine::math::Mat3;
use rask_wasm_shared::error::ClientError;
use web_sys::WebGl2RenderingContext as Gl2;
use web_sys::WebGlShader;
use web_sys::{WebGlProgram, WebGlUniformLocation};

const VERTEX_SHADER: &str = include_str!("shader/vertex.glsl");
const FRAGMENT_SHADER: &str = include_str!("shader/fragment.glsl");

enum ShaderType {
    Vertex,
    Fragment,
}

#[derive(Debug)]
pub struct Program {
    id: WebGlProgram,
    transformation: WebGlUniformLocation,
    texture: WebGlUniformLocation,
}

impl Program {
    fn get_uniform_location(
        gl: &Gl2,
        prog: &WebGlProgram,
        name: &str,
    ) -> Result<WebGlUniformLocation, ClientError> {
        gl.get_uniform_location(&prog, name)
            .ok_or(ClientError::WebGlError(format!(
                "cannot find uniform location \"{}\"",
                name
            )))
    }

    pub fn new(gl: &Gl2) -> Result<Self, ClientError> {
        let prog = gl.create_program().ok_or(ClientError::WebGlError(
            "cannot create a webgl shader program".to_owned(),
        ))?;
        let vs = Shader::create_from_source(gl, VERTEX_SHADER, ShaderType::Vertex)?;
        let fs = Shader::create_from_source(gl, FRAGMENT_SHADER, ShaderType::Fragment)?;
        vs.attach(&gl, &prog);
        fs.attach(&gl, &prog);
        gl.link_program(&prog);

        const TRANSFORMATION: &str = "transformation";
        const TEXTURE: &str = "g_texture";

        if gl.get_program_parameter(&prog, Gl2::LINK_STATUS).as_bool() == Some(true) {
            Ok(Self {
                transformation: Self::get_uniform_location(&gl, &prog, TRANSFORMATION)?,
                texture: Self::get_uniform_location(&gl, &prog, TEXTURE)?,
                id: prog,
            })
        } else {
            let info = gl
                .get_program_info_log(&prog)
                .unwrap_or("<undefined>".to_owned());
            Err(ClientError::WebGlError(format!("link error: {}", info)))
        }
    }

    pub fn use_program(&self, gl: &Gl2) {
        gl.use_program(Some(&self.id))
    }

    pub fn upload_fransformation(&self, gl: &Gl2, mat: &Mat3) {
        self.use_program(gl);
        gl.uniform_matrix3fv_with_f32_array(Some(&self.transformation), true, &mat.as_ref().clone())
    }

    pub fn upload_texture_id(&self, gl: &Gl2, id: i32) {
        self.use_program(gl);
        gl.uniform1i(Some(&self.texture), id)
    }
}

struct Shader(WebGlShader);

impl Shader {
    pub fn create_from_source(
        gl: &Gl2,
        source: &str,
        shader_type: ShaderType,
    ) -> Result<Self, ClientError> {
        let type_ = match shader_type {
            ShaderType::Vertex => Gl2::VERTEX_SHADER,
            ShaderType::Fragment => Gl2::FRAGMENT_SHADER,
        };
        let shader = gl.create_shader(type_).ok_or(ClientError::WebGlError(
            "cannot create a webgl shader".to_owned(),
        ))?;
        gl.shader_source(&shader, source);
        gl.compile_shader(&shader);
        if gl
            .get_shader_parameter(&shader, Gl2::COMPILE_STATUS)
            .as_bool()
            == Some(true)
        {
            Ok(Self(shader))
        } else {
            let info = gl
                .get_shader_info_log(&shader)
                .unwrap_or("<undefined>".to_owned());
            Err(ClientError::WebGlError(format!("comile error: {}", info)))
        }
    }

    pub fn attach(&self, gl: &Gl2, prog: &WebGlProgram) {
        gl.attach_shader(prog, &self.0)
    }
}