Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
198 changes: 197 additions & 1 deletion crates/processing_ffi/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use bevy::{
math::{Vec2, Vec3, Vec4},
math::{Affine3A, Mat4, Vec2, Vec3, Vec4},
prelude::Entity,
render::render_resource::{Extent3d, TextureFormat},
};
Expand Down Expand Up @@ -1887,6 +1887,202 @@ pub extern "C" fn processing_transform_reset(entity_id: u64) {
error::check(|| transform_reset(entity));
}

/// Attach an orbit camera controller.
#[unsafe(no_mangle)]
pub extern "C" fn processing_orbit_camera(graphics_id: u64) {
error::clear_error();
let graphics_entity = Entity::from_bits(graphics_id);
error::check(|| graphics_orbit_camera(graphics_entity));
}

/// Attach a free-flight camera controller.
#[unsafe(no_mangle)]
pub extern "C" fn processing_free_camera(graphics_id: u64) {
error::clear_error();
let graphics_entity = Entity::from_bits(graphics_id);
error::check(|| graphics_free_camera(graphics_entity));
}

/// Attach a pan/zoom camera controller.
#[unsafe(no_mangle)]
pub extern "C" fn processing_pan_camera(graphics_id: u64) {
error::clear_error();
let graphics_entity = Entity::from_bits(graphics_id);
error::check(|| graphics_pan_camera(graphics_entity));
}

/// Remove the active camera controller.
#[unsafe(no_mangle)]
pub extern "C" fn processing_disable_camera_controller(graphics_id: u64) {
error::clear_error();
let graphics_entity = Entity::from_bits(graphics_id);
error::check(|| graphics_disable_camera_controller(graphics_entity));
}

/// Set the camera distance from its center (zoom).
#[unsafe(no_mangle)]
pub extern "C" fn processing_camera_set_distance(graphics_id: u64, distance: f32) {
error::clear_error();
let graphics_entity = Entity::from_bits(graphics_id);
error::check(|| camera_set_distance(graphics_entity, distance));
}

/// Set the orbit camera's look-at center.
#[unsafe(no_mangle)]
pub extern "C" fn processing_camera_set_center(graphics_id: u64, x: f32, y: f32, z: f32) {
error::clear_error();
let graphics_entity = Entity::from_bits(graphics_id);
error::check(|| camera_set_center(graphics_entity, Vec3::new(x, y, z)));
}

/// Set the minimum camera distance.
#[unsafe(no_mangle)]
pub extern "C" fn processing_camera_set_min_distance(graphics_id: u64, min: f32) {
error::clear_error();
let graphics_entity = Entity::from_bits(graphics_id);
error::check(|| camera_set_min_distance(graphics_entity, min));
}

/// Set the maximum camera distance.
#[unsafe(no_mangle)]
pub extern "C" fn processing_camera_set_max_distance(graphics_id: u64, max: f32) {
error::clear_error();
let graphics_entity = Entity::from_bits(graphics_id);
error::check(|| camera_set_max_distance(graphics_entity, max));
}

/// Set the camera controller's sensitivity.
#[unsafe(no_mangle)]
pub extern "C" fn processing_camera_set_speed(graphics_id: u64, speed: f32) {
error::clear_error();
let graphics_entity = Entity::from_bits(graphics_id);
error::check(|| camera_set_speed(graphics_entity, speed));
}

/// Reset the camera controller to its initial pose.
#[unsafe(no_mangle)]
pub extern "C" fn processing_camera_reset(graphics_id: u64) {
error::clear_error();
let graphics_entity = Entity::from_bits(graphics_id);
error::check(|| camera_reset(graphics_entity));
}

/// Position the camera at eye, looking at center with the given up.
///
/// An active camera controller overrides this each frame.
#[unsafe(no_mangle)]
pub extern "C" fn processing_camera(
graphics_id: u64,
eye_x: f32,
eye_y: f32,
eye_z: f32,
center_x: f32,
center_y: f32,
center_z: f32,
up_x: f32,
up_y: f32,
up_z: f32,
) {
error::clear_error();
let graphics_entity = Entity::from_bits(graphics_id);
error::check(|| {
graphics_camera(
graphics_entity,
Vec3::new(eye_x, eye_y, eye_z),
Vec3::new(center_x, center_y, center_z),
Vec3::new(up_x, up_y, up_z),
)
});
}

/// A column-major 4x4 matrix.
#[repr(C)]
pub struct Matrix {
pub m: [f32; 16],
}

/// Right-multiply the model matrix by a column-major 4x4 matrix.
///
/// # Safety
/// - matrix points to at least 16 f32.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn processing_apply_matrix(graphics_id: u64, matrix: *const f32) {
error::clear_error();
let graphics_entity = Entity::from_bits(graphics_id);
error::check(|| {
// SAFETY: Caller guarantees matrix points to 16 valid f32 elements
let cols: [f32; 16] = unsafe { std::slice::from_raw_parts(matrix, 16) }
.try_into()
.unwrap();
let affine = Affine3A::from_mat4(Mat4::from_cols_array(&cols));
graphics_record_command(graphics_entity, DrawCommand::ApplyMatrix(affine))
});
}

/// The current model matrix, column-major. Flushes pending draws; identity on error.
#[unsafe(no_mangle)]
pub extern "C" fn processing_get_matrix(graphics_id: u64) -> Matrix {
error::clear_error();
let graphics_entity = Entity::from_bits(graphics_id);
let m = error::check(|| graphics_get_matrix(graphics_entity).map(|mat| mat.to_cols_array()))
.unwrap_or_else(|| Mat4::IDENTITY.to_cols_array());
Matrix { m }
}

/// Model-space point to world-space X (modelX).
#[unsafe(no_mangle)]
pub extern "C" fn processing_model_x(graphics_id: u64, x: f32, y: f32, z: f32) -> f32 {
error::clear_error();
let graphics_entity = Entity::from_bits(graphics_id);
error::check(|| graphics_model_point(graphics_entity, Vec3::new(x, y, z)).map(|p| p.x))
.unwrap_or(0.0)
}

/// Model-space point to world-space Y (modelY).
#[unsafe(no_mangle)]
pub extern "C" fn processing_model_y(graphics_id: u64, x: f32, y: f32, z: f32) -> f32 {
error::clear_error();
let graphics_entity = Entity::from_bits(graphics_id);
error::check(|| graphics_model_point(graphics_entity, Vec3::new(x, y, z)).map(|p| p.y))
.unwrap_or(0.0)
}

/// Model-space point to world-space Z (modelZ).
#[unsafe(no_mangle)]
pub extern "C" fn processing_model_z(graphics_id: u64, x: f32, y: f32, z: f32) -> f32 {
error::clear_error();
let graphics_entity = Entity::from_bits(graphics_id);
error::check(|| graphics_model_point(graphics_entity, Vec3::new(x, y, z)).map(|p| p.z))
.unwrap_or(0.0)
}

/// Model-space point to screen X in pixels (screenX).
#[unsafe(no_mangle)]
pub extern "C" fn processing_screen_x(graphics_id: u64, x: f32, y: f32, z: f32) -> f32 {
error::clear_error();
let graphics_entity = Entity::from_bits(graphics_id);
error::check(|| graphics_screen_point(graphics_entity, Vec3::new(x, y, z)).map(|p| p.x))
.unwrap_or(0.0)
}

/// Model-space point to screen Y in pixels (screenY).
#[unsafe(no_mangle)]
pub extern "C" fn processing_screen_y(graphics_id: u64, x: f32, y: f32, z: f32) -> f32 {
error::clear_error();
let graphics_entity = Entity::from_bits(graphics_id);
error::check(|| graphics_screen_point(graphics_entity, Vec3::new(x, y, z)).map(|p| p.y))
.unwrap_or(0.0)
}

/// Model-space point to screen depth in [0,1] (screenZ).
#[unsafe(no_mangle)]
pub extern "C" fn processing_screen_z(graphics_id: u64, x: f32, y: f32, z: f32) -> f32 {
error::clear_error();
let graphics_entity = Entity::from_bits(graphics_id);
error::check(|| graphics_screen_point(graphics_entity, Vec3::new(x, y, z)).map(|p| p.z))
.unwrap_or(0.0)
}

pub const PROCESSING_ATTR_FORMAT_FLOAT: u8 = 1;
pub const PROCESSING_ATTR_FORMAT_FLOAT2: u8 = 2;
pub const PROCESSING_ATTR_FORMAT_FLOAT3: u8 = 3;
Expand Down
40 changes: 40 additions & 0 deletions crates/processing_render/src/graphics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,46 @@ pub fn begin_draw(In(entity): In<Entity>, mut state_query: Query<&mut RenderStat
Ok(())
}

pub fn get_matrix(In(entity): In<Entity>, states: Query<&RenderState>) -> Result<Mat4> {
let state = states
.get(entity)
.map_err(|_| ProcessingError::GraphicsNotFound)?;
Ok(Mat4::from(state.transform.current()))
}

pub fn model_point(
In((entity, point)): In<(Entity, Vec3)>,
states: Query<&RenderState>,
) -> Result<Vec3> {
let state = states
.get(entity)
.map_err(|_| ProcessingError::GraphicsNotFound)?;
Ok(state.transform.transform_point(point))
}

pub fn screen_point(
In((entity, point)): In<(Entity, Vec3)>,
query: Query<(&RenderState, &Projection, &Transform, &SurfaceSize)>,
) -> Result<Vec3> {
let (state, projection, camera_transform, size) = query
.get(entity)
.map_err(|_| ProcessingError::GraphicsNotFound)?;
let world = state.transform.transform_point(point);
let clip_from_view = projection.get_clip_from_view();
let view_from_world = camera_transform.to_matrix().inverse();
let clip = clip_from_view * view_from_world * world.extend(1.0);
if clip.w == 0.0 {
return Ok(Vec3::ZERO);
}
let ndc = clip.truncate() / clip.w;
let SurfaceSize(width, height) = *size;
Ok(Vec3::new(
(ndc.x + 1.0) * 0.5 * width as f32,
(1.0 - ndc.y) * 0.5 * height as f32,
ndc.z,
))
}

pub fn flush(app: &mut App, entity: Entity) -> Result<()> {
graphics_mut!(app, entity).insert(Flush);
app.update();
Expand Down
44 changes: 44 additions & 0 deletions crates/processing_render/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,50 @@ pub fn transform_look_at(entity: Entity, target: Vec3) -> error::Result<()> {
})
}

/// Position the camera at `eye`, looking at `center` with the given `up`.
pub fn graphics_camera(
entity: Entity,
eye: Vec3,
center: Vec3,
up: Vec3,
) -> error::Result<()> {
app_mut(|app| {
app.world_mut()
.run_system_cached_with(transform::camera, (entity, eye, center, up))
.unwrap()
})
}

/// The current model matrix. Flushes pending draws first.
pub fn graphics_get_matrix(entity: Entity) -> error::Result<Mat4> {
app_mut(|app| {
graphics::flush(app, entity)?;
app.world_mut()
.run_system_cached_with(graphics::get_matrix, entity)
.unwrap()
})
}

/// Map a point from the current model space to world space (modelX/Y/Z).
pub fn graphics_model_point(entity: Entity, point: Vec3) -> error::Result<Vec3> {
app_mut(|app| {
graphics::flush(app, entity)?;
app.world_mut()
.run_system_cached_with(graphics::model_point, (entity, point))
.unwrap()
})
}

/// Map a point from the current model space to screen space (screenX/Y/Z).
pub fn graphics_screen_point(entity: Entity, point: Vec3) -> error::Result<Vec3> {
app_mut(|app| {
graphics::flush(app, entity)?;
app.world_mut()
.run_system_cached_with(graphics::screen_point, (entity, point))
.unwrap()
})
}

pub fn transform_reset(entity: Entity) -> error::Result<()> {
app_mut(|app| {
app.world_mut()
Expand Down
2 changes: 2 additions & 0 deletions crates/processing_render/src/render/command.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use bevy::math::Affine3A;
use bevy::prelude::*;
use bevy::render::render_resource::{BlendComponent, BlendFactor, BlendOperation, BlendState};
use processing_core::constants as consts;
Expand Down Expand Up @@ -573,6 +574,7 @@ pub enum DrawCommand {
PushMatrix,
PopMatrix,
ResetMatrix,
ApplyMatrix(Affine3A),
PushStyle,
PopStyle,
Translate(Vec3),
Expand Down
1 change: 1 addition & 0 deletions crates/processing_render/src/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -862,6 +862,7 @@ pub fn flush_draw_commands(
DrawCommand::PushMatrix => state.transform.push(),
DrawCommand::PopMatrix => state.transform.pop(),
DrawCommand::ResetMatrix => state.transform.reset(),
DrawCommand::ApplyMatrix(m) => state.transform.apply(m),
DrawCommand::PushStyle => state.style.push(),
DrawCommand::PopStyle => state.style.pop(),
DrawCommand::Translate(v) => state.transform.apply(Affine3A::from_translation(v)),
Expand Down
11 changes: 11 additions & 0 deletions crates/processing_render/src/transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,17 @@ pub fn look_at(
Ok(())
}

pub fn camera(
In((entity, eye, center, up)): In<(Entity, Vec3, Vec3, Vec3)>,
mut transforms: Query<&mut Transform>,
) -> Result<()> {
let mut transform = transforms
.get_mut(entity)
.map_err(|_| ProcessingError::TransformNotFound)?;
*transform = Transform::from_translation(eye).looking_at(center, up);
Ok(())
}

pub fn reset(In(entity): In<Entity>, mut transforms: Query<&mut Transform>) -> Result<()> {
let mut transform = transforms
.get_mut(entity)
Expand Down
Loading