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
129
130
use zaplib::*;

static SHADER: Shader = Shader {
    build_geom: Some(QuadIns::build_geom),
    code_to_concatenate: &[
        Cx::STD_SHADER,
        code_fragment!(
            r#"
            uniform rect_size: vec2;
            uniform use_screen_space: float;
            uniform point_style: float;
            uniform vertex_transform: mat4;

            geometry geom: vec2;

            instance in_pos: vec3;
            instance in_color: vec3;
            instance in_size: float;
            instance in_user_info: vec2;

            // Transforms a vertex to clip space, accounting for aspect ratio
            fn to_clip_space(v: vec4) -> vec4 {
                let w = draw_clip.z - draw_clip.x;
                let h = draw_clip.w - draw_clip.y;
                let aspect = w / h;
                return v / v.w * aspect;
            }

            fn vertex() -> vec4 {
                if use_screen_space == 1. {
                    let projected_pos = camera_projection * camera_view * vertex_transform * vec4(in_pos, 1.0);
                    let point_size = in_size * dpi_factor;
                    let offset = point_size * vec4((geom - vec2(0.5, 0.5))/rect_size, 0, 0);

                    // When rendering screen space points, we convert the projected point to clip space
                    // and then apply the offset.
                    return to_clip_space(projected_pos) + offset;
                } else {
                    let view_pos = camera_view * vertex_transform * vec4(in_pos, 1.0);
                    let point_size = in_size;
                    let offset = point_size * vec4(geom - vec2(0.5, 0.5), 0, 0);

                    // For world space points, we apply the offset in view space so they always
                    // face to the camera.
                    return camera_projection * (view_pos + offset);
                }
            }

            fn pixel() -> vec4 {
                if point_style == 1. {
                    let df = Df::viewport(geom);
                    df.circle(vec2(0.5), 0.5);
                    df.fill(vec4(in_color, 1.));
                    return df.result;
                } else {
                    return vec4(in_color, 1.);
                }
            }"#
        ),
    ],
    ..Shader::DEFAULT
};

#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct DrawPoints3dInstance {
    pub position: Vec3,
    pub color: Vec3,
    pub size: f32,
    /// Not really used for rendering, this extra field can hold context
    /// sensitive information that identifies each instance.
    pub user_info: Vec2,
}

#[repr(C)]
struct DrawPoints3dUniforms {
    rect_size: Vec2,
    use_screen_space: f32,
    point_style: f32,
    vertex_transform: Mat4,
}

#[derive(Debug, Clone, Copy)]
pub enum DrawPoints3dStyle {
    Quad,
    Circle,
}

const POINT_STYLE_QUAD: f32 = 0.0;
const POINT_STYLE_CIRCLE: f32 = 1.0;

pub struct DrawPoints3dOptions {
    pub use_screen_space: bool,
    pub point_style: DrawPoints3dStyle,
    /// Custom transformation to do on all vertices
    pub vertex_transform: Mat4,
}

impl Default for DrawPoints3dOptions {
    fn default() -> Self {
        Self { use_screen_space: false, point_style: DrawPoints3dStyle::Quad, vertex_transform: Mat4::identity() }
    }
}

pub struct DrawPoints3d {}

impl DrawPoints3d {
    /// Draw points markers.
    /// Following Webviz's implementation, points can be rendered in either world or screen space using the `use_screen_space`
    /// flag. Regardless of the render space, all points are rendered as billboards, facing the camera.
    pub fn draw(cx: &mut Cx, data: &[DrawPoints3dInstance], options: DrawPoints3dOptions) -> Area {
        let area = cx.add_instances(&SHADER, data);

        let rect = cx.get_box_rect();
        area.write_user_uniforms(
            cx,
            DrawPoints3dUniforms {
                rect_size: rect.size,
                use_screen_space: if options.use_screen_space { 1. } else { 0. },
                point_style: match options.point_style {
                    DrawPoints3dStyle::Quad => POINT_STYLE_QUAD,
                    DrawPoints3dStyle::Circle => POINT_STYLE_CIRCLE,
                },
                vertex_transform: options.vertex_transform,
            },
        );

        area
    }
}