Rust 3D with Fyrox - Select an element

by

Once we have the basics done, and I assume you also have an official tutorial done, time to dive deep into some complex stuff.

I'm making an RTS/RPG game with a perspective camera, so everything the official Fyrox source can give me is far from what I need. Even documentation is not a helper here.

After messing around and reading, I assumed I could have mouse position from engine.user_interface.cursor_position(). It clearly states the cursor position of the user interface. Documentation doesn't exist here despite being a public method, so I tried it.

Unfortunately, it was always (0,0), so this wasn't what I was looking for. The correct way to handle mouse movement is via Event.

event_loop.run(move |event, _, control_flow| {
        match event {
            Event::WindowEvent { event: WindowEvent::CursorMoved { position, .. } , .. } => {
	      game.mouse.x = position.x as f32;
	      game.mouse.y = position.y as f32;
	    }
        }
});

To simplify everything and easily use it later on, it's good to store the mouse position as Vector2.

struct Game {
  // Current Scene handle
  pub scene: Handle< Scene>,
  // Camera handle
  pub camera: Handle<Node>,
  pub mouse: Vector2<f32>,
}

Once I have the cursor position, I can create a ray projected from Camera to the mouse position. We can find how to create such a ray using the mouse position in the official Fyrox book in the section camera Camera.

fn make_picking_ray(camera: &Camera, point: Vector2<f32>, renderer: &Renderer) -> Ray {
    camera.make_ray(point, renderer.get_frame_bounds())
}

The Camera makes the Ray with its own position as the start position and the end position as a mouse position in 3D space where depth is the farthest Camera can see. Now time to use the Ray.

1// Take reference to elements graph
2let graph = &engine.scenes[self.scene].graph;
3
4// Create the Ray
5let ray = Self::make_picking_ray(
6 graph[self.player.camera].as_camera(),
7 self.player.controller.mouse,
8 &engine.renderer,
9);
10
11// Allocate the list of elements which Ray hit
12let mut buffer = Vec::default();
13
14// Cast the ray in the target position
15graph.physics.cast_ray(
16 RayCastOptions {
17 // Ray start position
18 ray_origin: Point3::from(ray.origin),
19 // Ray direction
20 ray_direction: ray.dir,
21 // Ray maximum length
22 max_len: f32::MAX,
23 // Default collision
24 groups: InteractionGroups::default(),
25 // Put elements in order they were hit
26 sort_results: true,
27 },
28 &mut buffer,
29);
30
31if let Some(hit) = buffer.into_iter().next() {
32 // The Ray can hit only the Collider element
33 let collider = &graph[hit.collider];
34 // We want Rigid Body to add, remove and reference clicked element
35 let parent = collider.parent();
36 if parent.is_some() {
37 return;
38 }
39 let parent = &graph[parent];
40 if !parent.is_rigid_body() {
41 return;
42 }
43 // Rigid Body
44 let parent = parent.as_rigid_body();
45
46 println!("clicked {:?}", parent.name());
47
48 // DO SOMETHING
49}

There are a couple of things which need to be highlighted here. Variable hit is Intersection which has nothing to do with Result. This structure references collider and distance from the Camera to the element (toi). We will use it to find the component of the graph. In line 33, we found the Collider for the element we want to modify. Then from lines 35 to 44, we retrieve Rigid Body from the graph.