piston_window icon indicating copy to clipboard operation
piston_window copied to clipboard

How to modify the view matrix? (ie correctly resize the window)

Open real-felix opened this issue 8 years ago • 3 comments
trafficstars

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?

real-felix avatar May 20 '17 07:05 real-felix

Using Piston-Graphics?

bvssvni avatar May 20 '17 16:05 bvssvni

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 Context data is disturbing. What are we supposed to do with the view field, for example?
  • There is only one matrix, whereas in graphics development (in openGL for example) there are at least 2 matrices: view and transform.

real-felix avatar May 24 '17 14:05 real-felix

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],
                    _ => (),
                },
                _ => (),
            },
        }
    }

kaikalii avatar Jul 16 '18 16:07 kaikalii