Rust-SDL2 Transparent Background for Texture - rust

Anybody have experience with https://rust-sdl2.github.io/rust-sdl2/sdl2/index.html? I can't seem to make a texture's background be transparent. My code looks something like the following:
let mut canvas = window.into_canvas().build().unwrap();
canvas.set_blend_mode(sdl2::render::BlendMode::Blend);
let texture_creator = canvas.texture_creator();
let mut texture = texture_creator.load_texture(std::path::Path::new(
"path_to_png",
))?;
texture.set_blend_mode(sdl2::render::BlendMode::Blend);
canvas.copy_ex(&texture, None, None, 0., None, false, false)?;
canvas.present();
I've tried different permutations of blendmode without success. Help.
Edit: a reproducible example
extern crate sdl2;
use sdl2::event::Event;
use sdl2::image::LoadTexture;
use sdl2::keyboard::Keycode;
use std::time::Duration;
fn main() -> Result<(), String> {
let sdl_context = sdl2::init().unwrap();
let video_subsystem = sdl_context.video().unwrap();
let window = video_subsystem
.window("rust-sdl2 demo", 800, 600)
.position_centered()
.build()
.unwrap();
let mut canvas = window.into_canvas().build().unwrap();
canvas.default_pixel_format();
canvas.set_blend_mode(sdl2::render::BlendMode::Blend);
let texture_creator = canvas.texture_creator();
texture_creator.default_pixel_format();
let mut texture = texture_creator.load_texture(std::path::Path::new(
"someimage.png",
))?;
texture.set_blend_mode(sdl2::render::BlendMode::None);
let mut event_pump = sdl_context.event_pump().unwrap();
'running: loop {
for event in event_pump.poll_iter() {
match event {
Event::KeyDown {
keycode: Some(Keycode::Escape),
..
} => break 'running,
_ => {}
}
}
canvas.clear();
canvas.copy_ex(&texture, None, None, 0., None, false, false)?;
canvas.present();
std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 60));
}
Ok(())
}

Related

RUST + FLTK: accessing another widget from widget on_push() function

I used FLTK to create a window and two buttons inside, the btn_A has a callback and should change the btn_B label, but I dont see any non-monstrous approach do to this, ples halp? =''[
fn main() {
showMainWindow();
}
pub fn showMainWindow() {
//WINDOW
let application=app::App::default();
let mut win = window::Window::default().with_size(500,300);
//BTN_A
let mut btn_A:Listener<_> = button::Button::new(100,100,100,50,"btn_A").into();
//BTN_B
let mut btn_B:Listener<_> = button::Button::new(300,100,100,50,"btn_B").into();
//BTN_A_CALLBACK
btn_A.handle(|elem,evt| match evt {
enums::Event::Push => { btn_A(elem); true }
_ => { false }
});
win.end();
win.show();
application.run().unwrap();
}
pub fn btn_A(elem:&mut button::Button) {
elem.deactivate(); //deactivate itself
//but how do I access btn_B here?
}
In principle all that is needed is to pass a mutable reference to btn_B to your handler function:
pub fn btn_A(elem:&mut button::Button, btn_B: &mut button::Button) {
...
}
However there is one slight problem with your code: You named the function the same as the variable that holds your button.
Apart from that in the most recent version of the fltk crate (v.1.2.23, that I used because you did not specify which version you used in your question) there does not seem to be a Listener<_> type.
Here is an example based on the snippet you posted for changing the label of btn_B:
use fltk::{prelude::{WidgetExt, GroupExt, WidgetBase}, window, app, button, enums};
fn main() {
showMainWindow();
}
pub fn showMainWindow() {
//WINDOW
let application = app::App::default();
let mut win = window::Window::default().with_size(500, 300);
//BTN_A
let mut btn_A = button::Button::new(100, 100, 100, 50, "btn_A");
//BTN_B
let mut btn_B = button::Button::new(300, 100, 100, 50, "btn_B");
//BTN_A_CALLBACK
btn_A.handle(move |elem, evt| match evt {
enums::Event::Push => {
btn_A_click(elem, &mut btn_B);
true
}
_ => false,
});
win.end();
win.show();
application.run().unwrap();
}
pub fn btn_A_click(elem: &mut button::Button, btn_B: &mut button::Button) {
elem.deactivate(); //deactivate itself
//but how do I access btn_B here?
btn_B.set_label("New title.")
}
Also note, that the handle closure now takes ownership of btn_B because of the move keyword.

Segmentation fault in Vulkan on vkMapMemory call / (device.map_memory in Erupt)

I'm using Rust and the Rust-based Vulkan wrapper Erupt.
Everything in this code runs fine until the device.map_memory call. No VulkanValidation warnings or errors are raised. Just a segmentation fault. I have offsets set to zero, but this is the only memory usage of this buffer, so I'm not sure if the issue is offsets or something else.
use memoffset::offset_of;
use simple_logger::SimpleLogger;
use winit::{
event::{
Event, KeyboardInput, WindowEvent,
ElementState, StartCause, VirtualKeyCode
},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
window::Window
};
// use winit::event::{ElementState, StartCause, VirtualKeyCode};
use structopt::StructOpt;
use erupt::{
cstr,
utils::{self, surface},
vk, DeviceLoader, EntryLoader, InstanceLoader,
};
use std::{
ffi::{c_void, CStr, CString},
mem::*,
os::raw::c_char,
ptr,
result::Result,
result::Result::*,
string::String,
thread,
time,
};
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
const TITLE: &str = "Peregrine Ray-Trace";
const FRAMES_IN_FLIGHT: usize = 2;
const LAYER_KHRONOS_VALIDATION: *const c_char = cstr!("VK_LAYER_KHRONOS_validation");
#[derive(Debug, StructOpt)]
struct Opt {
/// Use validation layers
#[structopt(short, long)]
validation_layers: bool,
}
unsafe extern "system" fn debug_callback(
_message_severity: vk::DebugUtilsMessageSeverityFlagBitsEXT,
_message_types: vk::DebugUtilsMessageTypeFlagsEXT,
p_callback_data: *const vk::DebugUtilsMessengerCallbackDataEXT,
_p_user_data: *mut c_void,
) -> vk::Bool32 {
eprintln!(
"Vulkan: {}",
CStr::from_ptr((*p_callback_data).p_message).to_string_lossy()
);
vk::FALSE
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
struct VertexV3 {
pos: [f32; 4],
color: [f32; 4],
}
impl VertexV3 {
fn get_binding_descriptions() -> [vk::VertexInputBindingDescription; 1] {
[vk::VertexInputBindingDescription {
binding: 0,
stride: std::mem::size_of::<Self>() as u32,
input_rate: vk::VertexInputRate::VERTEX,
}]
}
pub fn get_attribute_descriptions() -> [vk::VertexInputAttributeDescription; 2] {
[
vk::VertexInputAttributeDescription {
binding: 0,
location: 0,
format: vk::Format::R32G32B32A32_SFLOAT,
offset: offset_of!(Self, pos) as u32,
},
vk::VertexInputAttributeDescription {
binding: 0,
location: 1,
format: vk::Format::R32G32B32A32_SFLOAT,
offset: offset_of!(Self, color) as u32,
},
]
}
}
fn main() {
println!("Ray-Peregrine Lab 24::::!!!!");
let mut vulkan_output = String::from("");
println!("Ray-EE-Oct-12");
let event_loop = EventLoop::new();
let window = WindowBuilder::new()
.with_title("Peregrine Ray-Trace")
.build(&event_loop)
.unwrap();
let entry = EntryLoader::new().unwrap();
// https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Instance
let application_name = CString::new("Peregrine Ray-Trace").unwrap();
let engine_name = CString::new("Vulkan Engine").unwrap();
let app_info = vk::ApplicationInfoBuilder::new()
.application_name(&application_name)
.application_version(vk::make_api_version(0, 1, 0, 0))
.engine_name(&engine_name)
.engine_version(vk::make_api_version(0, 1, 0, 0))
.api_version(vk::make_api_version(0, 1, 0, 0));
let mut instance_extensions = surface::enumerate_required_extensions(&window).unwrap();
instance_extensions.push(vk::EXT_DEBUG_UTILS_EXTENSION_NAME);
let mut instance_layers = Vec::new();
instance_layers.push(LAYER_KHRONOS_VALIDATION);
let device_extensions = vec![
vk::KHR_SWAPCHAIN_EXTENSION_NAME,
vk::KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME,
vk::KHR_RAY_QUERY_EXTENSION_NAME,
vk::KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME,
vk::KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME,
vk::KHR_SPIRV_1_4_EXTENSION_NAME,
];
let mut device_layers = Vec::new();
device_layers.push(LAYER_KHRONOS_VALIDATION);
let instance_info = vk::InstanceCreateInfoBuilder::new()
.application_info(&app_info)
.enabled_extension_names(&instance_extensions)
.enabled_layer_names(&instance_layers);
let instance = unsafe { InstanceLoader::new(&entry, &instance_info, None) }.unwrap();
// https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Validation_layers
// if opt.validation_layers ...
let messenger = {
let messenger_info = vk::DebugUtilsMessengerCreateInfoEXTBuilder::new()
.message_severity(
vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE_EXT
| vk::DebugUtilsMessageSeverityFlagsEXT::WARNING_EXT
| vk::DebugUtilsMessageSeverityFlagsEXT::ERROR_EXT,
)
.message_type(
vk::DebugUtilsMessageTypeFlagsEXT::GENERAL_EXT
| vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION_EXT
| vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE_EXT,
)
.pfn_user_callback(Some(debug_callback));
unsafe { instance.create_debug_utils_messenger_ext(&messenger_info, None) }.unwrap()
};
let surface = unsafe { surface::create_surface(&instance, &window, None) }.unwrap();
let (physical_device, queue_family, format, present_mode, device_properties) =
unsafe { instance.enumerate_physical_devices(None) }
.unwrap()
.into_iter()
.filter_map(|physical_device| unsafe {
// println!("Physical Device: {:?}", physical_device);
// println!("Phyisical Device Queue Family Properties: {:?}", instance.get_physical_device_properties(physical_device));
let queue_family = match instance
.get_physical_device_queue_family_properties(physical_device, None)
.into_iter()
.enumerate()
.position(|(i, queue_family_properties)| {
queue_family_properties
.queue_flags
.contains(vk::QueueFlags::GRAPHICS)
&& instance
.get_physical_device_surface_support_khr(
physical_device,
i as u32,
surface,
)
.unwrap()
}) {
Some(queue_family) => queue_family as u32,
None => return None,
};
let formats = instance
.get_physical_device_surface_formats_khr(physical_device, surface, None)
.unwrap();
let format = match formats
.iter()
.find(|surface_format| {
surface_format.format == vk::Format::B8G8R8A8_SRGB
&& surface_format.color_space == vk::ColorSpaceKHR::SRGB_NONLINEAR_KHR
})
.or_else(|| formats.get(0))
{
Some(surface_format) => *surface_format,
None => return None,
};
let present_mode = instance
.get_physical_device_surface_present_modes_khr(physical_device, surface, None)
.unwrap()
.into_iter()
.find(|present_mode| present_mode == &vk::PresentModeKHR::MAILBOX_KHR)
.unwrap_or(vk::PresentModeKHR::FIFO_KHR);
let supported_device_extensions = instance
.enumerate_device_extension_properties(physical_device, None, None)
.unwrap();
let device_extensions_supported =
device_extensions.iter().all(|device_extension| {
let device_extension = CStr::from_ptr(*device_extension);
supported_device_extensions.iter().any(|properties| {
CStr::from_ptr(properties.extension_name.as_ptr()) == device_extension
})
});
if !device_extensions_supported {
return None;
}
let device_properties = instance.get_physical_device_properties(physical_device);
Some((
physical_device,
queue_family,
format,
present_mode,
device_properties,
))
})
.max_by_key(|(_, _, _, _, properties)| match properties.device_type {
vk::PhysicalDeviceType::DISCRETE_GPU => 2,
vk::PhysicalDeviceType::INTEGRATED_GPU => 1,
_ => 0,
})
.expect("No suitable physical device found");
//end of declaration of enum (physical_device, queue_family, format, present_mode, device_properties)
// https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Logical_device_and_queues
let queue_info = vec![vk::DeviceQueueCreateInfoBuilder::new()
.queue_family_index(queue_family)
.queue_priorities(&[1.0])];
let features = vk::PhysicalDeviceFeaturesBuilder::new();
let device_info = vk::DeviceCreateInfoBuilder::new()
.queue_create_infos(&queue_info)
.enabled_features(&features)
.enabled_extension_names(&device_extensions)
.enabled_layer_names(&device_layers);
let device =
unsafe { DeviceLoader::new(&instance, physical_device, &device_info, None) }.unwrap();
// let queue2 = unsafe { device2.get_device_queue(queue_family, 0) };
let queue = unsafe { device.get_device_queue(queue_family, 0) };
println!("\n \n");
let model_path: &'static str = "assets/terrain__002__.obj";
let (models, materials) = tobj::load_obj(&model_path, &tobj::LoadOptions::default()).expect("Failed to load model object!");
let model = models[0].clone();
let materials = materials.unwrap();
let material = materials.clone().into_iter().nth(0).unwrap();
let mut vertices = vec![];
let mut indices = vec![];
let mesh = model.mesh;
let total_vertices_count = mesh.positions.len() / 3;
for i in 0..total_vertices_count {
let vertex = VertexV3 {
pos: [
mesh.positions[i * 3],
mesh.positions[i * 3 + 1],
mesh.positions[i * 3 + 2],
1.0,
],
color: [1.0, 1.0, 1.0, 1.0],
};
vertices.push(vertex);
};
indices = mesh.indices.clone();
println!("Starting buffer and memory allocation/mapping processes... \n");
let vertex_buffer_size = ::std::mem::size_of_val(&vertices) as vk::DeviceSize;
println!("vertex_buffer_size: {:?}", vertex_buffer_size);
let physical_device_memory_properties = unsafe { instance.get_physical_device_memory_properties(physical_device) };
println!("\n physical_device_memory_properties: {:?}", physical_device_memory_properties);
pretty_print(physical_device_memory_properties);
let vertex_buffer_create_info = vk::BufferCreateInfoBuilder::new()
.size(vertex_buffer_size * 8)
.usage(vk::BufferUsageFlags::VERTEX_BUFFER)
.sharing_mode(vk::SharingMode::EXCLUSIVE);
println!("\n vertex_buffer_create_info: {:?}", vertex_buffer_create_info);
let vertex_buffer = unsafe {
device
.create_buffer(&vertex_buffer_create_info, None)
.expect("Failed to create vertex buffer.")
};
let vertex_buffer_memory_reqs = unsafe {
device
.get_buffer_memory_requirements(vertex_buffer)
};
println!("\n vertex_buffer_memory_reqs: {:?}", vertex_buffer_memory_reqs);
let vertex_buffer_memory_allocate_info = unsafe {
vk::MemoryAllocateInfoBuilder::new()
.allocation_size(vertex_buffer_memory_reqs.size)
.memory_type_index(2)
.build()
};
println!("\n vertex_buffer_memory_allocate_info, {:?} \n", vertex_buffer_memory_allocate_info);
let vertex_buffer_memory = unsafe {
device
.allocate_memory(&vertex_buffer_memory_allocate_info, None)
.expect("Failed to allocate vertex buffer memory.")
};
println!("\n vertex_buffer_memory: {:?} \n", &vertex_buffer_memory);
unsafe { device.bind_buffer_memory(vertex_buffer, vertex_buffer_memory, 0) }
.expect("Error on bind buffer memory");
unsafe {
let mut pointer: *mut *mut std::ffi::c_void = std::ptr::null_mut();
device
.map_memory(
vertex_buffer_memory,
0,
vertex_buffer_memory_reqs.size,
None,
pointer,
)
.expect("failed to map 333memory.");
}
}
fn pretty_print(stuff: vk::PhysicalDeviceMemoryProperties) {
println!("\n pretty_print physical_device_memory_properties: \n");
for memory_type in stuff.memory_types {
println!("memory type: {:?}", memory_type);
}
for heap in stuff.memory_heaps {
println!("memory heap: {:?}", heap);
}
}
With help from friz64 on the Erupt GameDev board:
I was derefencing a null pointer.
The pointer needed to be *mut rather than *mut *mut, and then take a mutable reference from it.
The block around map_memory should look like this:
unsafe {
let mut pointer: *mut std::ffi::c_void = std::ptr::null_mut();
let mut ref1 = &mut pointer;
device
.map_memory(
vertex_buffer_memory,
256,
vk::WHOLE_SIZE,
None,
ref1,
)
.expect("failed to map 333memory.");
}

Rendering text in a separate function using piston2d in rust

I am trying to render text in a separate function using piston2d / piston_window. I am able to draw text just fine, but I can't figure out how to pass the appropriate parameters into a separate function.
I have studied What is GlyphCache type in a function to render text in Piston2d and adjusted my code accordingly, but I can't make sense of the error I am getting.
use piston_window::*;
fn main() {
let font = include_bytes!("IBMPlexSans-Regular.ttf");
let opengl = OpenGL::V3_2;
let settings = WindowSettings::new("test", [500, 500])
.graphics_api(opengl)
.fullscreen(false)
.vsync(true)
.exit_on_esc(true);
let mut window: PistonWindow = settings.build().unwrap();
let mut glyphs = Glyphs::from_bytes(
font,
window.create_texture_context(),
TextureSettings::new(),
)
.unwrap();
while let Some(e) = window.next() {
window.draw_2d(&e, |c, gfx, device| {
clear([0.2; 4], gfx);
text::Text::new_color([1.0, 1.0, 1.0, 0.7], 30)
.draw(
"Hi!",
&mut glyphs,
&c.draw_state,
c.transform
.trans(100., 100.),
gfx,
)
.unwrap();
glyphs.factory.encoder.flush(device);
});
}
}
fn render_text(
x: f64,
y: f64,
text: &str,
size: u32,
c: Context,
g: &mut G2d,
glyphs: &mut glyph_cache::rusttype::GlyphCache<GfxFactory, G2dTexture>,
) {
text::Text::new(size)
.draw(text, glyphs, &c.draw_state, c.transform.trans(x, y), g)
.unwrap();
}
I am receiving the following error:
error[E0277]: the trait bound `Texture<gfx_device_gl::Resources>: UpdateTexture<gfx_device_gl::factory::Factory>` is not satisfied
--> src/main.rs:54:10
|
54 | .draw(text, glyphs, &c.draw_state, c.transform.trans(x, y), g)
| ^^^^ the trait `UpdateTexture<gfx_device_gl::factory::Factory>` is not implemented for `Texture<gfx_device_gl::Resources>`
|
= help: the following implementations were found:
<Texture<R> as UpdateTexture<TextureContext<F, R, C>>>
= note: required because of the requirements on the impl of `CharacterCache` for `GlyphCache<'_, gfx_device_gl::factory::Factory, Texture<gfx_device_gl::Resources>>`
I am aware this is probably very piston-specific, but I would be very happy about any pointers.
Just had the same problem. It's almost a year after but documentation for piston_window isn't the best so maybe others will need it.
This worked for me
use piston_window::types::Color;
use piston_window::{text, Context, G2d, Glyphs, Transformed};
pub const text_color: Color = [1.0, 1.0, 1.0, 1.0];
pub fn draw_text(
ctx: &Context,
graphics: &mut G2d,
glyphs: &mut Glyphs,
color: Color,
pos: Position,
text: &str,
) {
text::Text::new_color(color, 20)
.draw(
text,
glyphs,
&ctx.draw_state,
ctx.transform.trans(pos.x as f64, pos.y as f64),
graphics,
)
.unwrap();
}
pub struct Pos {
pub x: f64,
pub y: f64,
}
pub fn main() {
let assets = find_folder::Search::ParentsThenKids(3, 3)
.for_folder("assets")
.unwrap();
let ref font = assets.join("retro-gaming.ttf");
let mut glyphs = window.load_font(font).unwrap();
let size = [500., 500.];
let mut window: PistonWindow = WindowSettings::new("Test", size)
.resizable(false)
.exit_on_esc(true)
.build()
.unwrap();
while let Some(event) = window.next() {
window.draw_2d(&event, |ctx, g, _| {
draw_text(&ctx, g, &mut glyphs, text_color, Pos { x: 0, y: 10 }, "20")
}
}
}
Note that I'm using different "Glyphs" than you are. There's additionally one more dependency in "Cargo.toml" namely find_folder = "0.3.0".
I'm not entirely sure whether the snippet above compiles and works. It is a quick refactor from this commit https://github.com/laszukdawid/rsnake/commit/e6e23563ebdd9c7b972ee17b5d0299e2202358cf.

What is an acceptable approach to dragging sprites with Bevy 0.4?

While trying out Bevy, I had the need for dragging and dropping sprites.
Unfortunately, this does not seem to come ready made or I did not find it in the documentation.
What would be the most idiomatic way to achieve this goal?
What I have tried so far is in my answer, but I'll gladly accept another solution that is better/faster/more idiomatic.
I'm not experienced enough to know what's idiomatic unfortunately, however, here's an overview of how I've implemented sprite dragging in my application, and it feels like a good way to me:
I have a "cursor location" entity with a transform component (and a Cursor component for identification) that I update in a system each frame to the location of the cursor.
Every draggable object has a Hoverable and Draggable component. I iterate over those objects in one system each where I add/remove a Hovered and Dragged component to the entities to indicate if they are hovered or dragged.
I have a system that checks if an object is getting dropped, and if so gives it a Dropped component.
I have a system that runs when an entity gets the 'Dragged' component (using the Added<C> filter), which sets the objects parent to the "cursor location" entity.
And another system for when an entity gets the 'Dropped' component, which clears the parent.
To me, having many systems with small areas of responsibility feels good. I would be interested to hear opposing views as I lack experience.
There are of course many things I've left out in this overview, so here's my code for reference. There are some oddities and unnecessary code for a minimal example since this is adapted from my actual code:
#![allow(clippy::type_complexity)]
use bevy::{prelude::*, render::camera::Camera};
fn main() {
App::build()
.init_resource::<State>()
.add_resource(WindowDescriptor {
title: "Bevy".to_string(),
width: 1024.0,
height: 768.0,
vsync: true,
..Default::default()
})
.add_plugins(DefaultPlugins)
.add_plugin(MyPlugin)
.run();
}
pub struct MyPlugin;
impl Plugin for MyPlugin {
fn build(&self, app: &mut AppBuilder) {
app.add_startup_system(setup.system())
.add_system_to_stage(stage::PRE_UPDATE, cursor_state.system())
.add_system_to_stage(stage::UPDATE, cursor_transform.system())
.add_system_to_stage(stage::UPDATE, draggable.system())
.add_system_to_stage(stage::UPDATE, hoverable.system())
.add_system_to_stage(stage::POST_UPDATE, drag.system())
.add_system_to_stage(stage::POST_UPDATE, drop.system())
.add_system_to_stage(stage::POST_UPDATE, material.system());
}
}
const SPRITE_SIZE: f32 = 55.0;
fn setup(
commands: &mut Commands,
asset_server: Res<AssetServer>,
mut materials: ResMut<Assets<ColorMaterial>>,
) {
let bevy_texture = asset_server.load("sprites/bevy-icon.png");
commands
.spawn(Camera2dBundle::default())
.spawn(())
.with(CursorState::default())
.spawn((Transform::default(), GlobalTransform::default(), Cursor));
for _ in 0..4 {
commands
.spawn(SpriteBundle {
material: materials.add(bevy_texture.clone().into()),
sprite: Sprite::new(Vec2::new(SPRITE_SIZE, SPRITE_SIZE)),
..Default::default()
})
.with(Hoverable)
.with(Draggable);
}
}
#[derive(Default)]
struct CursorState {
cursor_world: Vec2,
cursor_moved: bool,
}
struct Cursor;
struct Draggable;
struct Dragged;
struct Dropped;
struct Hoverable;
struct Hovered;
fn cursor_state(
mut state: ResMut<State>,
e_cursor_moved: Res<Events<CursorMoved>>,
windows: Res<Windows>,
mut q_cursor_state: Query<&mut CursorState>,
q_camera: Query<&Transform, With<Camera>>,
) {
let event_cursor_screen = state.er_cursor_moved.latest(&e_cursor_moved);
for mut cursor_state in q_cursor_state.iter_mut() {
if let Some(event_cursor_screen) = event_cursor_screen {
let window = windows.get_primary().unwrap();
let cam_transform = q_camera.iter().last().unwrap();
cursor_state.cursor_world =
cursor_to_world(window, cam_transform, event_cursor_screen.position);
cursor_state.cursor_moved = true;
} else {
cursor_state.cursor_moved = false;
}
}
}
fn cursor_transform(
commands: &mut Commands,
q_cursor_state: Query<&CursorState>,
mut q_cursor: Query<(Entity, &mut Transform), With<Cursor>>,
) {
let cursor_state = q_cursor_state.iter().next().unwrap();
for (cursor_e, mut transform) in q_cursor.iter_mut() {
transform.translation.x = cursor_state.cursor_world.x;
transform.translation.y = cursor_state.cursor_world.y;
commands.remove_one::<Parent>(cursor_e);
}
}
fn hoverable(
commands: &mut Commands,
q_cursor_state: Query<&CursorState>,
q_hoverable: Query<(Entity, &Transform, &Sprite), (With<Hoverable>, Without<Dragged>)>,
) {
let cursor_state = q_cursor_state.iter().next().unwrap();
if cursor_state.cursor_moved {
for (entity, transform, sprite) in q_hoverable.iter() {
let half_width = sprite.size.x / 2.0;
let half_height = sprite.size.y / 2.0;
if transform.translation.x - half_width < cursor_state.cursor_world.x
&& transform.translation.x + half_width > cursor_state.cursor_world.x
&& transform.translation.y - half_height < cursor_state.cursor_world.y
&& transform.translation.y + half_height > cursor_state.cursor_world.y
{
commands.insert_one(entity, Hovered);
} else {
commands.remove_one::<Hovered>(entity);
}
}
}
}
fn material(
mut materials: ResMut<Assets<ColorMaterial>>,
q_hoverable: Query<
(&Handle<ColorMaterial>, Option<&Hovered>, Option<&Dragged>),
With<Hoverable>,
>,
) {
let mut first = true;
for (material, hovered, dragged) in q_hoverable.iter() {
let (red, green, alpha) = if dragged.is_some() {
(0.0, 1.0, 1.0)
} else if first && hovered.is_some() {
first = false;
(1.0, 0.0, 1.0)
} else if hovered.is_some() {
(1.0, 1.0, 0.5)
} else {
(1.0, 1.0, 1.0)
};
materials.get_mut(material).unwrap().color.set_r(red);
materials.get_mut(material).unwrap().color.set_g(green);
materials.get_mut(material).unwrap().color.set_a(alpha);
}
}
fn cursor_to_world(window: &Window, cam_transform: &Transform, cursor_pos: Vec2) -> Vec2 {
// get the size of the window
let size = Vec2::new(window.width() as f32, window.height() as f32);
// the default orthographic projection is in pixels from the center;
// just undo the translation
let screen_pos = cursor_pos - size / 2.0;
// apply the camera transform
let out = cam_transform.compute_matrix() * screen_pos.extend(0.0).extend(1.0);
Vec2::new(out.x, out.y)
}
fn draggable(
commands: &mut Commands,
i_mouse_button: Res<Input<MouseButton>>,
q_pressed: Query<Entity, (With<Hovered>, With<Draggable>)>,
q_released: Query<Entity, With<Dragged>>,
) {
if i_mouse_button.just_pressed(MouseButton::Left) {
if let Some(entity) = q_pressed.iter().next() {
commands.insert_one(entity, Dragged);
}
} else if i_mouse_button.just_released(MouseButton::Left) {
for entity in q_released.iter() {
commands.remove_one::<Dragged>(entity);
commands.insert_one(entity, Dropped);
}
}
}
fn drag(
commands: &mut Commands,
mut q_dragged: Query<(Entity, &mut Transform, &GlobalTransform), Added<Dragged>>,
q_cursor: Query<(Entity, &GlobalTransform), With<Cursor>>,
) {
if let Some((cursor_e, cursor_transform)) = q_cursor.iter().next() {
for (entity, mut transform, global_transform) in q_dragged.iter_mut() {
let global_pos = global_transform.translation - cursor_transform.translation;
commands.insert_one(entity, Parent(cursor_e));
transform.translation.x = global_pos.x;
transform.translation.y = global_pos.y;
}
}
}
fn drop(
commands: &mut Commands,
mut q_dropped: Query<(Entity, &mut Transform, &GlobalTransform), Added<Dropped>>,
) {
for (entity, mut transform, global_transform) in q_dropped.iter_mut() {
let global_pos = global_transform.translation;
transform.translation.x = global_pos.x;
transform.translation.y = global_pos.y;
commands.remove_one::<Parent>(entity);
commands.remove_one::<Dropped>(entity);
}
}
#[derive(Default)]
struct State {
er_cursor_moved: EventReader<CursorMoved>,
}
This code is for bevy 0.4.
This is the solution I came up with. Complete example
main.rs
use bevy::prelude::*;
use bevy::render::pass::ClearColor;
use bevy::window::CursorMoved;
const SPRITE_SIZE: f32 = 55.0;
fn main() {
App::build()
.add_resource(WindowDescriptor {
width: 1000.0,
height: 1000.0,
resizable: false,
title: "Bevy: drag sprite".to_string(),
..Default::default()
})
.add_resource(Msaa { samples: 4 })
.add_resource(ClearColor(Color::rgb(0.9, 0.9, 0.9)))
.add_plugins(DefaultPlugins)
.add_startup_system(setup.system())
.add_system(sprite_system.system())
.add_system(bevy::input::system::exit_on_esc_system.system())
.run();
}
fn setup(
commands: &mut Commands,
asset_server: Res<AssetServer>,
mut materials: ResMut<Assets<ColorMaterial>>,
) {
commands.spawn(Camera2dBundle::default());
// show sprite in the middle of the screen
let bevy_texture = asset_server.load("sprites/bevy-icon.png");
commands.spawn(SpriteBundle {
sprite: Sprite::new(Vec2::new(SPRITE_SIZE, SPRITE_SIZE)),
material: materials.add(bevy_texture.clone().into()),
..Default::default()
});
}
#[derive(Default)]
struct State {
cursor_moved_event_reader: EventReader<CursorMoved>,
// store current cursor/mouse position
cursor_pos: Vec2,
// store entity ID and the difference between sprite center and mouse click location
sprite: Option<(Entity, Vec3)>,
}
fn sprite_system(
mut state: Local<State>,
windows: Res<Windows>,
mouse_button_input: Res<Input<MouseButton>>,
cursor_moved_events: Res<Events<CursorMoved>>,
mut sprites: Query<(Entity, &Sprite)>,
mut transforms: Query<&mut Transform>,
) {
let window = windows.get_primary().unwrap();
let half_window = Vec2::new(window.width() / 2.0, window.height() / 2.0);
// if cursor has moved, transform to graphics coordinates and store in state.curser_pos
if let Some(cursor_event) = state.cursor_moved_event_reader.latest(&cursor_moved_events) {
state.cursor_pos = cursor_event.position - half_window;
state.cursor_pos.x = state.cursor_pos.x;
};
// stop dragging if mouse button was released
if mouse_button_input.just_released(MouseButton::Left) {
state.sprite = None;
return;
}
// set new sprite position, if mouse button is pressed and a sprite was clicked on
// take previous click difference into account, to avoid sprite jumps on first move
if mouse_button_input.pressed(MouseButton::Left) && state.sprite.is_some() {
let sprite = state.sprite.unwrap();
let mut sprite_pos = transforms.get_mut(sprite.0).unwrap();
trace!("Sprite position old: {:?}", sprite_pos.translation);
sprite_pos.translation.x = state.cursor_pos.x + sprite.1.x;
sprite_pos.translation.y = state.cursor_pos.y + sprite.1.y;
trace!("Sprite position new: {:?}", sprite_pos.translation);
// position clamping was left out intentionally
}
// store sprite ID and mouse distance from sprite center, if sprite was clicked
if mouse_button_input.just_pressed(MouseButton::Left) {
for (entity, sprite) in sprites.iter_mut() {
let sprite_pos = transforms.get_mut(entity).unwrap().translation;
let diff = cursor_to_sprite_diff(&state.cursor_pos, &sprite_pos);
// sprite is a circle, so check distance from center < sprite radius
if diff.length() < (sprite.size.x / 2.0) {
state.sprite = Some((entity, diff));
}
}
}
}
fn cursor_to_sprite_diff(cursor_pos: &Vec2, sprite_pos: &Vec3) -> Vec3 {
Vec3::new(
sprite_pos.x - cursor_pos.x,
sprite_pos.y - cursor_pos.y,
0.0,
)
}
Cargo.toml
[package]
name = "bevy-drag-sprite"
version = "0.1.0"
authors = ["Me"]
edition = "2018"
[dependencies]
bevy = "0.4"

How to end the borrowing of an object without introducing a new scope?

I want to access an object as read-only after it has been passed to another object which changes its state. I know there is a problem with accessing an object when its still under control by being borrowed.
extern crate renderay_rs;
use renderay_rs::*;
fn draw_canvas(canvas: &Canvas) {
let max_height = canvas.height;
let max_width = canvas.width;
for iterator_y in 0..max_height {
for iterator_x in 0..max_width {
print!("{}", canvas.array[iterator_y * iterator_x]);
}
print!("\n");
}
}
fn main() {
let point = Point { x: 5, y: 5 };
let mut canvas = Canvas {
width: 10,
height: 10,
array: vec!['o'; 10*10],
};
let mut renderer = CanvasRenderer::new(&mut canvas);
renderer.render_point('A', point);
draw_canvas(canvas);
}
I feel I should end the borrowing after the renderer.render_point(..., ...) method. Is there a way to do so? The object has no need to be borrowed by it or the CanvasRenderer after its state has been changed.
I've used block scope before, but I don't feel happy with this approach. I hope you can give me a hint for a better alternative!
You can introduce a block to reduce the scope of renderer:
fn main() {
let point = Point { x: 5, y: 5 };
let mut canvas = Canvas {
width: 10,
height: 10,
array: vec!['o'; 10*10],
};
{
let mut renderer = CanvasRenderer::new(&mut canvas);
renderer.render_point('A', point);
}
draw_canvas(canvas);
}
Another way if you don't like blocks is to use functions. Rust will be able to figure out where the borrow ends from the lifetimes (here the function does not return anything, so the borrow ends after the function call):
fn render_things(canvas: &mut Canvas) {
let mut renderer = CanvasRenderer::new(canvas);
renderer.render_point('A', point);
}
fn main() {
let point = Point { x: 5, y: 5 };
let mut canvas = Canvas {
width: 10,
height: 10,
array: vec!['o'; 10*10],
};
render_things(&mut canvas);
draw_canvas(canvas);
}

Resources