piston_window
piston_window copied to clipboard
How to modify the view matrix? (ie correctly resize the window)
Hi!
I am currently writing a game with piston_window, but I am stuck with a thing. My window is resizable, so I want to write my model in a [0., 1.] frame and then draw everything in my view using the current window dimension (ie [800, 1200] for example).
I think this is possible with the view matrix, as I can remember, but I found no way to do this with piston_window. Can someone help me?
Using Piston-Graphics?
Sure, with Piston-Graphics, but how? That's the question.
After hours of search, I think there is no view matrix I can access. My solution is to use the base matrix as a view matrix:
fn render(&mut self, args: &RenderArgs) {
use piston_window::Transformed;
let square = rectangle::centered_square(0., 0., 0.2); // radius is 1/5 of the window
let (w, h) = (args.width as f64, args.height as f64);
self.gl.draw(args.viewport(), |c, gl| {
let view = c.transform.scale(w, h);
clear([1., 1., 1., 1.], gl);
let transform = view.trans(0.5, 0.5); // move at the center of the window
rectangle([1., 0., 0., 1.], square, transform, gl);
});
}
I use this https://github.com/PistonDevelopers/opengl_graphics/blob/master/src/back_end.rs#L405 as shown in the example.
With this solution, everything is drawn in a [0., 1.] frame and then resized to the size of the window. Is this the intended way to do this? Few things are confusing for a newcomer here:
- The
Contextdata is disturbing. What are we supposed to do with theviewfield, for example? - There is only one matrix, whereas in graphics development (in openGL for example) there are at least 2 matrices: view and transform.
I struggled with this too, and it took me a long time to determine a good solution. I did figure out a way to define a view center and view zoom which, along with the window context's transform, can be used to construct a view transform which can be used for drawing things.
I've put an example below. If you hook up some keys to change the view_centerand view_zoom variables, you should find that the view moves as you would expect. I also threw in the calculation for the view-relative mouse position, as it's often needed in games.
// Initialize a view
let mut view_center = [0.0, 0.0];
let mut view_zoom = 1.0;
// Initialize a window-relative mouse position
let mut mouse_pos = [0.0, 0.0];
// Game loop
while let Some(event) = window.next() {
// Get the size of the window in case it was resized
let window_size = [window.size().width as f64, window.size().height as f64];
// The mouse coordinates taking the view position and zoom into account
let mouse_coords: Vec<f64> = mouse_pos
.iter()
.enumerate()
.map(|(i, &x)| (x - window_size[i] / 2.0) / view_zoom - view_center[i])
.collect();
// Draw
window.draw_2d(&e, |c, g| {
// Create a view transform from the context's
let view = c.transform
// Move the view to its position
.append_transform(translate([
view_center[0] * view_zoom,
view_center[1] * view_zoom,
]))
// Scale the view
.append_transform(scale(view_zoom, view_zoom))
// Move the transform over a bit so that the coordinate at view_center
// actually lies in the center of the screen
.append_transform(translate([
window_size[0] / 2.0 / view_zoom,
window_size[1] / 2.0 / view_zoom,
]));
// Draw some stuff
clear([0.5, 0.7, 0.5, 1.0], g);
rectangle([0.0, 0.0, 1.0, 1.0], [0.0, 0.0, 100.0, 100.0], view, g);
});
// Get the window-relative mouse position
match event {
Event::Input(input) => match input {
Input::Move(motion) => match motion {
Motion::MouseCursor(x, y) => mouse_pos = [x, y],
_ => (),
},
_ => (),
},
}
}