rustglium

How can I use a cgmath::Matrix as a uniform parameter in glium?


I'm trying to integrate the cgmath library into my first experiments with glium, but I can't figure out how to pass my Matrix4 object to the draw() call.

My uniforms object is defined thus:

let uniforms = uniform! {
    matrix: cgmath::Matrix4::from_scale(0.1)
};

and this is my draw call:

target.draw(&vertex_buffer, &index_slice, &program, &uniforms, &Default::default())
      .unwrap();

which fails to compile with the message

error[E0277]: the trait bound `cgmath::Matrix4<{float}>: glium::uniforms::AsUniformValue` is not satisfied

I'm a total beginner with Rust, but I do believe I cannot implement this trait myself, as both it and the Matrix4 type are in a crate separate from mine.

Is there really no better option than to manually convert the matrix into an array of arrays of floats?


Solution

  • I do believe I cannot implement this trait myself, as both it and the Matrix4 type are in a crate separate from mine.

    This is very true.

    Is there really no better option than to manually convert the matrix into an array of arrays of floats?

    Well, you don't have to do a lot manually.

    First, it's useful to note that Matrix4<S> implements Into<[[S; 4]; 4]> (I can't link to that impl directly, so you have to use ctrl+f). That means that you can easily convert a Matrix4 into an array which is accepted by glium. Unfortunately, into() only works when the compiler knows exactly what type to convert to. So here is a non-working and a working version:

    // Not working, the macro accepts many types, so the compiler can't be sure 
    let uniforms = uniform! {
        matrix: cgmath::Matrix4::from_scale(0.1).into()
    };
    
    // Works, because we excplicitly mention the type
    let matrix: [[f64; 4]; 4] = cgmath::Matrix::from_scale(0.1).into();
    let uniforms = uniform! {
        matrix: matrix,  
    };
    

    But this solution might be still too much to write. When I worked with cgmath and glium, I created a helper trait to reduce code size even more. This might not be the best solution, but it works and has no obvious downsides (AFAIK).

    pub trait ToArr {
        type Output;
        fn to_arr(&self) -> Self::Output;
    }
    
    impl<T: BaseNum> ToArr for Matrix4<T> {
        type Output = [[T; 4]; 4];
        fn to_arr(&self) -> Self::Output {
            (*self).into()
        }
    }
    

    I hope this code explains itself. With this trait, you now only have to use the trait near the draw() call and then:

    let uniforms = uniform! {
        matrix: cgmath::Matrix4::from_scale(0.1).to_arr(),
        //                                      ^^^^^^^^^
    };