asny / three-d

2D/3D renderer - makes it simple to draw stuff across platforms (including web)
MIT License
1.24k stars 105 forks source link

Pan Camera Control #369

Closed ThundR67 closed 1 year ago

ThundR67 commented 1 year ago

Is there a way to implement a panning based camera. I am trying to render an image, and what to pan across in XY directions.

asny commented 1 year ago

Yeah, you probably want something like in the Mandelbrot example?

ThundR67 commented 1 year ago

I tried the Mandelbrot example, but It does not work properly. When I run it, instead of a pan movement, the effect I get is the image is orbiting around a point somewhere behind it.

struct PageImage {
    width: u32,
    height: u32,
    data: Vec<u8>,
}

async fn run(img: PageImage) {
    let window = Window::new(WindowSettings {
        title: "Sprites!".to_string(),
        max_size: Some((1280, 720)),
        ..Default::default()
    })
    .unwrap();
    let context = window.gl();

    let mut camera = Camera::new_orthographic(
        window.viewport(),
        vec3(0.0, 0.0, 1.0),
        vec3(0.0, 0.0, 0.0),
        vec3(0.0, 1.0, 0.0),
        2.5,
        0.0,
        10.0,
    );

    let cpu_texture = CpuTexture {
        data: TextureData::RgbU8(img.data.chunks(3).map(|c| [c[0], c[1], c[2]]).collect()),
        width: img.width,
        height: img.height,
        ..Default::default()    
    };

    let material = ColorMaterial {
        color: Color::WHITE,
        texture: Some(std::sync::Arc::new(Texture2D::new(&context, &cpu_texture)).into()),
        ..Default::default()
    };

    let geometry = Sprites::new(
        &context,
        &[
            vec3(0.0, 0.0, 0.0),
        ],
        None,
    );

    window.render_loop(move |mut frame_input| {
        let mut redraw = frame_input.first_frame;
        redraw |= camera.set_viewport(frame_input.viewport);

        for event in frame_input.events.iter() {
            match event {
                Event::MouseMotion { delta, button, .. } => {
                    if *button == Some(MouseButton::Left) {
                        let speed = 0.003 * camera.position().z.abs();
                        let right = camera.right_direction();
                        let up = right.cross(camera.view_direction());
                        let delta = -right * speed * delta.0 as f32 + up * speed * delta.1 as f32;
                        camera.translate(&delta);
                        redraw = true;
                    }
                }
                Event::MouseWheel {
                    delta, position, ..
                } => {
                    let distance = camera.position().z.abs();
                    let mut target = camera.position_at_pixel((position.0 as f32, position.1 as f32));
                    target.z = 0.0;
                    camera.zoom_towards(&target, distance * 0.05 * delta.1 as f32, 0.00001, 10.0);
                    redraw = true;
                }
                _ => {}
            }
        }

        if redraw {
            frame_input
                .screen()
                .clear(ClearState::color_and_depth(0.0, 0.0, 0.0, 1.0, 1.0))
                .render(
                    &camera, 
                    [ Gm {geometry: &geometry, material: &material} ], 
                    &[]
            );
        }

        FrameOutput {
            swap_buffers: redraw,
            wait_next_event: true,
            ..Default::default()
        }
    });
}
asny commented 1 year ago

Mmm. Ok. Probably because you're using Sprites which always turns towards the camera. If you only want to draw an image, you could look at the image example.

ThundR67 commented 1 year ago

Yah, that one works. Thanks.