-
Notifications
You must be signed in to change notification settings - Fork 59
/
Copy pathinteractive_triangle.rs
212 lines (181 loc) · 6.06 KB
/
interactive_triangle.rs
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
//! This example demonstrates how to _map_ GPU tessellations to alter their attributes. It consists
//! of a triangle that you can move around by grabbing one of its three vertices and moving them
//! around.
//!
//! Press down the left click of your mouse / trackpad when your mouse is close to a vertex to grab
//! it, move it around and release the left click to put it at the place your cursor is.
//! Press <escape> to quit or close the window.
//!
//! <https://docs.rs/luminance>
use crate::{
shared::{FragSlot, Vertex},
Example, InputAction, LoopFeedback, PlatformServices,
};
use luminance::{
backend::Backend,
context::Context,
dim::{Dim2, Size2},
framebuffer::{Back, Framebuffer},
pipeline::PipelineState,
primitive::Triangle,
render_state::RenderState,
shader::{Program, ProgramBuilder},
vertex_entity::{VertexEntity, VertexEntityBuilder, View},
vertex_storage::{Interleaved, Interleaving},
};
use mint::{Vector2, Vector3};
const VS: &str = include_str!("simple-vs.glsl");
const FS: &str = include_str!("simple-fs.glsl");
const TRI_VERTICES: [Vertex; 3] = [
Vertex::new(
Vector2 { x: 0.5, y: -0.5 },
Vector3 {
x: 0.,
y: 1.,
z: 0.,
},
),
Vertex::new(
Vector2 { x: 0.0, y: 0.5 },
Vector3 {
x: 0.,
y: 0.,
z: 1.,
},
),
Vertex::new(
Vector2 { x: -0.5, y: -0.5 },
Vector3 {
x: 1.,
y: 0.,
z: 0.,
},
),
];
// when selecting a vertex, the vertex is “snapped” only if the distance (in pixels) between the
// position of the click and the vertex is minimal to this value (expressed in pixels, since it’s a
// distance)
const CLICK_RADIUS_PX: f32 = 20.;
// a simple convenient function to compute a distance between two [f32; 2]
fn distance(a: &Vector2<f32>, b: &Vector2<f32>) -> f32 {
let x = b.x - a.x;
let y = b.y - b.y;
(x * x + y * y).sqrt()
}
// convert from screen space to window space
fn screen_to_window(a: &Vector2<f32>, w: f32, h: f32) -> Vector2<f32> {
Vector2 {
x: (1. + a.x) * 0.5 * w as f32,
y: (1. - a.y) * 0.5 * h as f32,
}
}
// convert from window space to screen space
fn window_to_screen(a: &Vector2<f32>, w: f32, h: f32) -> Vector2<f32> {
Vector2 {
x: a.x / w * 2. - 1.,
y: 1. - a.y / h * 2.,
}
}
pub struct LocalExample {
program: Program<Vertex, (), Triangle, FragSlot, ()>,
triangle: VertexEntity<Vertex, Triangle, Interleaving>,
// current cursor position
cursor_pos: Option<Vector2<f32>>,
// when we press down a button, if we are to select a vertex, we need to know which one; this
// variable contains its index (0, 1 or 2)
selected: Option<usize>,
// used to perform window-space / screen-space coordinates conversion
window_dim: [f32; 2],
back_buffer: Framebuffer<Dim2, Back<FragSlot>, Back<()>>,
}
impl Example for LocalExample {
type Err = luminance::backend::Error;
const TITLE: &'static str = "Interactive Triangle";
fn bootstrap(
[width, height]: [u32; 2],
_: &mut impl PlatformServices,
ctx: &mut Context<impl Backend>,
) -> Result<Self, Self::Err> {
let program = ctx.new_program(
ProgramBuilder::new()
.add_vertex_stage(VS)
.no_primitive_stage()
.add_shading_stage(FS),
)?;
let triangle = ctx.new_vertex_entity(
VertexEntityBuilder::new().add_vertices(Interleaved::new().set_vertices(TRI_VERTICES)),
)?;
let cursor_pos = None;
let selected = None;
let back_buffer = ctx.back_buffer(Size2::new(width, height))?;
Ok(Self {
program,
triangle,
cursor_pos,
selected,
window_dim: [width as f32, height as f32],
back_buffer,
})
}
fn render_frame(
mut self,
_: f32,
actions: impl Iterator<Item = InputAction>,
ctx: &mut Context<impl Backend>,
) -> Result<LoopFeedback<Self>, Self::Err> {
for action in actions {
match action {
InputAction::Quit => return Ok(LoopFeedback::Exit),
// if we press down the primary action, we want to check whether a vertex is nearby; to do so,
// we map the triangle’s vertices and look for one; we take the one with the minimal
// distance that satisfies the distance rule defined at the top of this file
// (CLICK_RADIUS_PX)
InputAction::PrimaryPressed => {
if let Some(ref cursor_pos) = self.cursor_pos {
let vertices = self.triangle.vertices().vertices();
for i in 0..3 {
let [w, h] = self.window_dim;
// convert the vertex position from screen space into window space
let ws_pos = screen_to_window(&vertices[i].co, w, h);
if distance(&ws_pos, cursor_pos) <= CLICK_RADIUS_PX {
println!("selecting vertex i={}", i);
self.selected = Some(i);
break;
}
}
}
}
InputAction::PrimaryReleased => {
self.selected = None;
}
// whenever the cursor moves, if we have a selection, we set the position of that vertex to
// the position of the cursor, and synchronize the GPU vertex entity
InputAction::CursorMoved { x, y } => {
let pos = Vector2 { x, y };
self.cursor_pos = Some(pos);
if let Some(selected) = self.selected {
let vertices = self.triangle.vertices().vertices_mut();
let [w, h] = self.window_dim;
vertices[selected].co = window_to_screen(&pos, w, h);
// update the vertices on the GPU
ctx.update_vertices(&mut self.triangle)?;
}
}
InputAction::Resized { width, height } => {
self.window_dim = [width as _, height as _];
}
_ => (),
}
}
let program = &self.program;
let triangle = &self.triangle;
ctx.with_framebuffer(&self.back_buffer, &PipelineState::default(), |mut frame| {
frame.with_program(program, |mut frame| {
frame.with_render_state(&RenderState::default(), |mut frame| {
frame.render_vertex_entity(triangle.view(..))
})
})
})?;
Ok(LoopFeedback::Continue(self))
}
}