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

1

6

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?

Vivle answered 13/10, 2016 at 17:49 Comment(2)
Duplicate of How do I implement a trait I don't own for a type I don't own?Epicotyl
@shepmaster: it's not really a duplicate, because I don't really want to implement that trait unless there's no better way. The link is informative though - thanks.Vivle
S
9

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(),
    //                                      ^^^^^^^^^
};
Sweater answered 13/10, 2016 at 18:7 Comment(4)
Thanks Lukas, that did the trick! I'm so new to Rust (started on Monday 3 days ago) that it didn't occur to me that there was nothing stopping me from defining and implementing my own trait. (Even so, I couldn't have come up with this on my own, too much left to learn still.) Out of curiosity, why did you stop doing stuff with cgmath and glium?Vivle
sorry @LukasKalbertodt forgot to correctly include your name in the above comment.Vivle
@Vivle The name is not a problem, when you are commenting on my answer, since I get notified anyway ;-) Glad I could help. I did not stop working with cgmath or glium; sorry if my answer seem to imply it. I really like cgmath and think its design is terrific. While generally being pretty nice to use, glium has some design flaws, which tomaka itself acknowledges. A rewrite is being considered. But glium is still OK, so don't stop using it ;-)Sweater
Thanks! That's the answer I was hoping for :-)Vivle

© 2022 - 2024 — McMap. All rights reserved.