INIT
This commit is contained in:
53
src/system_param/mesh_aabb.rs
Normal file
53
src/system_param/mesh_aabb.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
use bevy::ecs::system::SystemParam;
|
||||
use bevy::math::Vec3;
|
||||
use bevy::prelude::{Children, Entity, GlobalTransform, Query};
|
||||
use bevy::render::primitives::Aabb;
|
||||
|
||||
#[derive(SystemParam)]
|
||||
pub struct MeshAabb<'w, 's> {
|
||||
meshes: Query<
|
||||
'w,
|
||||
's,
|
||||
(
|
||||
&'static GlobalTransform,
|
||||
Option<&'static Aabb>,
|
||||
Option<&'static Children>,
|
||||
),
|
||||
>,
|
||||
}
|
||||
|
||||
impl MeshAabb<'_, '_> {
|
||||
pub fn calculate(&self, mesh_root: Entity) -> (Vec3, Vec3) {
|
||||
calculate_aabb(&[mesh_root], true, &self.meshes)
|
||||
}
|
||||
}
|
||||
|
||||
fn calculate_aabb(
|
||||
entities: &[Entity],
|
||||
include_children: bool,
|
||||
entities_query: &Query<(&GlobalTransform, Option<&Aabb>, Option<&Children>)>,
|
||||
) -> (Vec3, Vec3) {
|
||||
let combine_bounds = |(a_min, a_max): (Vec3, Vec3), (b_min, b_max): (Vec3, Vec3)| {
|
||||
(a_min.min(b_min), a_max.max(b_max))
|
||||
};
|
||||
let default_bounds = (Vec3::splat(f32::INFINITY), Vec3::splat(f32::NEG_INFINITY));
|
||||
entities
|
||||
.iter()
|
||||
.filter_map(|&entity| {
|
||||
entities_query
|
||||
.get(entity)
|
||||
.map(|(&tf, bounds, children)| {
|
||||
let mut entity_bounds = bounds.map_or(default_bounds, |bounds| {
|
||||
(tf * Vec3::from(bounds.min()), tf * Vec3::from(bounds.max()))
|
||||
});
|
||||
if include_children && let Some(children) = children {
|
||||
let children_bounds =
|
||||
calculate_aabb(children, include_children, entities_query);
|
||||
entity_bounds = combine_bounds(entity_bounds, children_bounds);
|
||||
}
|
||||
entity_bounds
|
||||
})
|
||||
.ok()
|
||||
})
|
||||
.fold(default_bounds, combine_bounds)
|
||||
}
|
||||
84
src/system_param/pointer.rs
Normal file
84
src/system_param/pointer.rs
Normal file
@@ -0,0 +1,84 @@
|
||||
use crate::prelude::{CefWebviewUri, WebviewSize};
|
||||
use crate::system_param::mesh_aabb::MeshAabb;
|
||||
use bevy::ecs::system::SystemParam;
|
||||
use bevy::prelude::*;
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(SystemParam)]
|
||||
pub struct WebviewPointer<'w, 's, C: Component = Camera3d> {
|
||||
aabb: MeshAabb<'w, 's>,
|
||||
cameras: Query<'w, 's, (&'static Camera, &'static GlobalTransform), With<C>>,
|
||||
webviews: Query<
|
||||
'w,
|
||||
's,
|
||||
(&'static GlobalTransform, &'static WebviewSize),
|
||||
(With<CefWebviewUri>, Without<Camera>),
|
||||
>,
|
||||
parents: Query<'w, 's, &'static ChildOf>,
|
||||
}
|
||||
|
||||
impl<C: Component> WebviewPointer<'_, '_, C> {
|
||||
pub fn pos_from_trigger<P>(&self, trigger: &Trigger<Pointer<P>>) -> Option<(Entity, Vec2)>
|
||||
where
|
||||
P: Clone + Reflect + Debug,
|
||||
{
|
||||
let webview = self.parents.root_ancestor(trigger.target);
|
||||
let pos = self.pointer_pos(webview, trigger.pointer_location.position)?;
|
||||
Some((webview, pos))
|
||||
}
|
||||
|
||||
pub fn pointer_pos(&self, webview: Entity, viewport_pos: Vec2) -> Option<Vec2> {
|
||||
let (min, max) = self.aabb.calculate(webview);
|
||||
let aabb_size = Vec2::new(max.x - min.x, max.y - min.y);
|
||||
let (webview_gtf, webview_size) = self.webviews.get(webview).ok()?;
|
||||
self.cameras.iter().find_map(|(camera, camera_gtf)| {
|
||||
pointer_to_webview_uv(
|
||||
viewport_pos,
|
||||
camera,
|
||||
camera_gtf,
|
||||
webview_gtf,
|
||||
aabb_size,
|
||||
webview_size.0,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn pointer_to_webview_uv(
|
||||
cursor_pos: Vec2,
|
||||
camera: &Camera,
|
||||
cam_tf: &GlobalTransform,
|
||||
plane_tf: &GlobalTransform,
|
||||
plane_size: Vec2,
|
||||
tex_size: Vec2,
|
||||
) -> Option<Vec2> {
|
||||
let ray = camera.viewport_to_world(cam_tf, cursor_pos).ok()?;
|
||||
let n = plane_tf.forward().as_vec3();
|
||||
let t = ray.intersect_plane(
|
||||
plane_tf.translation(),
|
||||
InfinitePlane3d::new(plane_tf.forward()),
|
||||
)?;
|
||||
let hit_world = ray.origin + ray.direction * t;
|
||||
let local_hit = plane_tf.affine().inverse().transform_point(hit_world);
|
||||
let local_normal = plane_tf.affine().inverse().transform_vector3(n).normalize();
|
||||
let abs_normal = local_normal.abs();
|
||||
let (u_coord, v_coord) = if abs_normal.z > abs_normal.x && abs_normal.z > abs_normal.y {
|
||||
(local_hit.x, local_hit.y)
|
||||
} else if abs_normal.y > abs_normal.x {
|
||||
(local_hit.x, local_hit.z)
|
||||
} else {
|
||||
(local_hit.y, local_hit.z)
|
||||
};
|
||||
|
||||
let w = plane_size.x;
|
||||
let h = plane_size.y;
|
||||
let u = (u_coord + w * 0.5) / w;
|
||||
let v = (v_coord + h * 0.5) / h;
|
||||
if !(0.0..=1.0).contains(&u) || !(0.0..=1.0).contains(&v) {
|
||||
// outside plane bounds
|
||||
return None;
|
||||
}
|
||||
let px = u * tex_size.x;
|
||||
let py = (1.0 - v) * tex_size.y;
|
||||
Some(Vec2::new(px, py))
|
||||
}
|
||||
Reference in New Issue
Block a user