summaryrefslogtreecommitdiff
path: root/engine/src/ui/view/transform_3d.rs
blob: a426b2a5b2dafd8762557e01f199da9a39083e4b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use dear_imguizmo::GuizmoExt;

use crate::camera::{Active as ActiveCamera, Camera};
use crate::data_types::dimens::Dimens3;
use crate::ecs::pair::ChildOf;
use crate::ecs::query::term::{Traverse, TraverseUp, With, Without};
use crate::ecs::sole::Single;
use crate::ecs::Query;
use crate::projection::ClipVolume as ProjectionClipVolume;
use crate::scene::{Active as ActiveScene, Scene};
use crate::transform::Transform;
use crate::ui::dear_imgui::{
    Context as DearImguiContext,
    TargetWindow as DearImguiTargetWindow,
};
use crate::vector::Vec3;
use crate::windowing::window::{Closed as WindowClosed, Window};

pub fn show(
    subject_query: Query<(
        &mut Transform,
        Traverse<(With<Scene>, With<ActiveScene>), TraverseUp, ChildOf>,
        Without<ActiveCamera>,
    )>,
    camera_query: Query<(
        &Camera,
        &Transform,
        Traverse<(With<Scene>, With<ActiveScene>), TraverseUp, ChildOf>,
        With<ActiveCamera>,
    )>,
    target_window_query: Query<(
        &Window,
        With<DearImguiTargetWindow>,
        Without<WindowClosed>,
    )>,
    mut dear_imgui_context: Single<DearImguiContext>,
) -> Result<(), crate::Error>
{
    let dear_imgui_context = dear_imgui_context.get_mut()?;

    let Some(frame) = dear_imgui_context.frame() else {
        return Ok(());
    };

    let Some((camera, camera_world_pos)) = camera_query.iter().next() else {
        return Ok(());
    };

    let Some((target_window,)) = target_window_query.iter().next() else {
        return Ok(());
    };

    let gizmo = frame.guizmo();

    let [display_width, display_height] = frame.io().display_size();

    gizmo.set_rect(0.0, 0.0, display_width, display_height);

    // The widgets should appear under dear imgui windows
    gizmo.set_drawlist_background();

    let col_major_view = flatten_matrix(
        camera
            .to_view_matrix(camera_world_pos.position)
            .to_column_major(),
    );

    let col_major_projection = flatten_matrix(
        camera
            .projection
            .to_matrix_rh(target_window.inner_size, ProjectionClipVolume::NegOneToOne)
            .to_column_major(),
    );

    for (subject_index, (mut subject_transform,)) in subject_query.iter().enumerate() {
        let subject_index = match i32::try_from(subject_index) {
            Ok(subject_index) => subject_index,
            Err(err) => {
                tracing::error!("Invalid subject index {subject_index}: {err}");
                break;
            }
        };

        // No rotation
        let rotation = [0.0, 0.0, 0.0];

        let mut col_major_model_mat = dear_imguizmo::recompose_matrix(
            &subject_transform.position.into(),
            &rotation,
            &subject_transform.scale.into(),
        );

        let _id_token = gizmo.push_id(subject_index);

        let used = gizmo
            .manipulate_config(
                &col_major_view,
                &col_major_projection,
                &mut col_major_model_mat,
            )
            .operation(
                dear_imguizmo::Operation::TRANSLATE | dear_imguizmo::Operation::SCALE,
            )
            .mode(dear_imguizmo::Mode::World)
            .build();

        if used {
            let (translation, _rotation, scale) =
                dear_imguizmo::decompose_matrix(&col_major_model_mat);

            subject_transform.position = Vec3::from(translation);
            subject_transform.scale = Dimens3::from(scale);
        }
    }

    Ok(())
}

fn flatten_matrix(mat: [[f32; 4]; 4]) -> [f32; 4 * 4]
{
    let flattened = mat.as_flattened();

    let Some(flattened) = flattened.as_array::<{ 4 * 4 }>() else {
        unreachable!();
    };

    flattened.clone()
}