added astar pathfinder, started adding styles for ui

This commit is contained in:
Dmitry
2019-11-13 23:02:58 +03:00
parent da0b554d30
commit 698c2c7174
22 changed files with 757 additions and 162 deletions
+20 -2
View File
@@ -5,7 +5,25 @@
</component>
<component name="ChangeListManager">
<list default="true" id="1932b2c4-d6ff-413c-9558-19959667326b" name="Default Changelist" comment="scene graph">
<change beforePath="$PROJECT_DIR$/Cargo.lock" beforeDir="false" afterPath="$PROJECT_DIR$/Cargo.lock" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/engine/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/engine/mod.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/engine/resource_manager.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/engine/resource_manager.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/gui/border.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/gui/border.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/gui/button.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/gui/button.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/gui/check_box.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/gui/check_box.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/gui/grid.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/gui/grid.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/gui/image.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/gui/image.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/gui/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/gui/mod.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/gui/scroll_bar.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/gui/scroll_bar.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/gui/text.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/gui/text.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/gui/text_box.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/gui/text_box.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/gui/widget.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/gui/widget.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/gui/window.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/gui/window.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/renderer/deferred_light_renderer.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/renderer/deferred_light_renderer.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/resource/ttf.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/resource/ttf.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/scene/animation.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/scene/animation.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/scene/graph.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/scene/graph.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/utils/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/utils/mod.rs" afterDir="false" />
</list>
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="SHOW_DIALOG" value="false" />
@@ -60,7 +78,7 @@
</list>
</option>
</component>
<component name="RunManager" selected="Cargo Command.Clippy">
<component name="RunManager" selected="Cargo Command.Release">
<configuration name="Clippy" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
<option name="channel" value="DEFAULT" />
<option name="command" value="clippy" />
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "rg3d"
version = "0.3.2"
version = "0.3.3"
authors = ["Dmitry Stepanov <[email protected]>"]
edition = "2018"
license = "MIT"
+12 -10
View File
@@ -13,10 +13,12 @@ use crate::{
renderer::{Renderer, error::RendererError, gl},
window::{WindowBuilder, Window},
scene::SceneContainer,
};
use crate::{
PossiblyCurrent, GlRequest, GlProfile,
WindowedContext, NotCurrent, Api,
PossiblyCurrent,
GlRequest,
GlProfile,
WindowedContext,
NotCurrent,
Api,
event_loop::EventLoop
};
@@ -55,9 +57,9 @@ impl Engine {
/// ```
/// use rg3d::engine::Engine;
/// use rg3d::window::WindowBuilder;
/// use rg3d::event::EventsLoop;
/// use rg3d::event_loop::EventLoop;
///
/// let evt = EventsLoop::new();
/// let evt = EventLoop::new();
/// let window_builder = WindowBuilder::new()
/// .with_title("Test")
/// .with_fullscreen(None);
@@ -105,9 +107,9 @@ impl Engine {
/// ```no_run
/// use rg3d::engine::{Engine, EngineInterfaceMut};
/// use rg3d::window::WindowBuilder;
/// use rg3d::EventsLoop;
/// use rg3d::event_loop::EventLoop;
///
/// let evt = EventsLoop::new();
/// let evt = EventLoop::new();
/// let mut engine = Engine::new(WindowBuilder::new(), &evt).unwrap();
/// let EngineInterfaceMut{scenes, ui, renderer, sound_context, resource_manager} = engine.interface_mut();
/// ```
@@ -130,9 +132,9 @@ impl Engine {
/// ```no_run
/// use rg3d::engine::{EngineInterface, Engine};
/// use rg3d::window::WindowBuilder;
/// use rg3d::EventsLoop;
/// use rg3d::event_loop::EventLoop;
///
/// let evt = EventsLoop::new();
/// let evt = EventLoop::new();
/// let engine = Engine::new(WindowBuilder::new(), &evt).unwrap();
/// let EngineInterface{scenes, ui, renderer, sound_context, resource_manager} = engine.interface();
/// ```
+2 -2
View File
@@ -5,12 +5,12 @@ use std::{
use crate::resource::{
texture::Texture,
model::Model,
texture::TextureKind,
};
use rg3d_core::{
visitor::{Visitor, VisitResult, Visit}
};
use rg3d_sound::buffer::{Buffer, BufferKind};
use crate::resource::texture::TextureKind;
pub struct ResourceManager {
textures: Vec<Arc<Mutex<Texture>>>,
@@ -195,7 +195,7 @@ impl ResourceManager {
for old_model in self.get_models().to_vec() {
let mut old_model = old_model.lock().unwrap();
let new_model = match Model::load(old_model.path.as_path(),self) {
let new_model = match Model::load(old_model.path.as_path(), self) {
Ok(new_model) => new_model,
Err(e) => {
println!("Unable to reload {:?} model! Reason: {}", old_model.path, e);
+1 -1
View File
@@ -84,7 +84,7 @@ impl BorderBuilder {
impl Draw for Border {
fn draw(&mut self, drawing_context: &mut DrawingContext) {
let bounds= self.widget.get_screen_bounds();
drawing_context.push_rect_filled(&bounds, None, self.widget.color);
drawing_context.push_rect_filled(&bounds, None, self.widget.color());
drawing_context.push_rect_vary(&bounds, self.stroke_thickness, self.stroke_color);
drawing_context.commit(CommandKind::Geometry, CommandTexture::None);
}
+30 -26
View File
@@ -1,30 +1,34 @@
use crate::gui::{
node::UINode,
widget::{
Widget,
WidgetBuilder,
AsWidget
use crate::{
gui::{
node::UINode,
widget::{
Widget,
WidgetBuilder,
AsWidget,
},
UserInterface,
HorizontalAlignment,
VerticalAlignment,
Thickness,
text::TextBuilder,
border::BorderBuilder,
event::{UIEvent, UIEventKind},
Layout,
Draw,
draw::DrawingContext,
Update,
},
UserInterface,
HorizontalAlignment,
VerticalAlignment,
Thickness,
text::TextBuilder,
border::BorderBuilder,
event::{UIEvent, UIEventKind},
Layout,
Draw,
draw::DrawingContext,
Update,
resource::ttf::Font,
};
use rg3d_core::{
color::Color,
pool::Handle,
math::vec2::Vec2,
};
use std::rc::Rc;
use std::cell::RefCell;
use crate::resource::ttf::Font;
use std::{
rc::Rc,
cell::RefCell,
};
/// Button
///
@@ -75,7 +79,7 @@ pub enum ButtonContent {
pub struct ButtonBuilder {
widget_builder: WidgetBuilder,
content: Option<ButtonContent>,
font: Option<Rc<RefCell<Font>>>
font: Option<Rc<RefCell<Font>>>,
}
impl ButtonBuilder {
@@ -130,16 +134,16 @@ impl ButtonBuilder {
if evt.source == handle || ui.is_node_child_of(evt.source, handle) {
let back = ui.nodes.borrow_mut(handle).widget_mut();
match evt.kind {
UIEventKind::MouseDown { .. } => back.color = pressed_color,
UIEventKind::MouseDown { .. } => back.set_color(pressed_color),
UIEventKind::MouseUp { .. } => {
if back.is_mouse_over {
back.color = hover_color;
back.set_color(hover_color);
} else {
back.color = normal_color;
back.set_color(normal_color);
}
}
UIEventKind::MouseLeave => back.color = normal_color,
UIEventKind::MouseEnter => back.color = hover_color,
UIEventKind::MouseLeave => back.set_color(normal_color),
UIEventKind::MouseEnter => back.set_color(hover_color),
_ => ()
}
}
+27 -3
View File
@@ -1,10 +1,24 @@
use crate::gui::{widget::{Widget, AsWidget, WidgetBuilder}, Draw, Layout, UserInterface, Update, draw::DrawingContext, node::UINode, event::{UIEvent, UIEventKind}, Thickness, Visibility, bool_to_visibility};
use crate::gui::{
widget::{Widget, AsWidget, WidgetBuilder},
Draw,
Layout,
UserInterface,
Update,
draw::DrawingContext,
node::UINode,
event::{UIEvent, UIEventKind},
Thickness,
Visibility,
bool_to_visibility,
Styleable,
border::BorderBuilder,
};
use rg3d_core::{
math::vec2::Vec2,
pool::Handle,
color::Color
};
use crate::gui::border::BorderBuilder;
use rg3d_core::color::Color;
use std::any::Any;
pub struct CheckBox {
widget: Widget,
@@ -139,4 +153,14 @@ impl CheckBoxBuilder {
ui.add_node(check_box)
}
}
impl Styleable for CheckBox {
fn set_property(&mut self, name: &str, value: &dyn Any) {
}
fn get_property(&self, name: &str) -> Option<&dyn Any> {
None
}
}
+8 -8
View File
@@ -171,8 +171,8 @@ impl Layout for Grid {
let size_for_child = {
let child = ui.nodes.borrow(*child_handle).widget();
Vec2 {
x: self.columns.borrow()[child.column].actual_width,
y: self.rows.borrow()[child.row].actual_height,
x: self.columns.borrow()[child.column()].actual_width,
y: self.rows.borrow()[child.row()].actual_height,
}
};
ui.measure(*child_handle, size_for_child);
@@ -202,8 +202,8 @@ impl Layout for Grid {
let mut final_rect = None;
let child = ui.nodes.borrow(*child_handle).widget();
if let Some(column) = self.columns.borrow().get(child.column) {
if let Some(row) = self.rows.borrow().get(child.row) {
if let Some(column) = self.columns.borrow().get(child.column()) {
if let Some(row) = self.rows.borrow().get(child.row()) {
final_rect = Some(Rect::new(
column.x,
row.y,
@@ -290,7 +290,7 @@ impl Grid {
col.actual_width = col.desired_width;
for child_handle in self.widget.children.iter() {
let child = ui.nodes.borrow(*child_handle).widget();
if child.column == i && child.visibility == Visibility::Visible && child.desired_size.get().x > col.actual_width {
if child.column() == i && child.visibility == Visibility::Visible && child.desired_size.get().x > col.actual_width {
col.actual_width = child.desired_size.get().x;
}
}
@@ -313,7 +313,7 @@ impl Grid {
row.actual_height = row.desired_height;
for child_handle in self.widget.children.iter() {
let child = ui.nodes.borrow(*child_handle).widget();
if child.row == i && child.visibility == Visibility::Visible && child.desired_size.get().y > row.actual_height {
if child.row() == i && child.visibility == Visibility::Visible && child.desired_size.get().y > row.actual_height {
row.actual_height = child.desired_size.get().y;
}
}
@@ -329,7 +329,7 @@ impl Grid {
if available_size.x.is_infinite() {
for child_handle in self.widget.children.iter() {
let child = ui.nodes.borrow(*child_handle).widget();
if let Some(column) = self.columns.borrow().get(child.column) {
if let Some(column) = self.columns.borrow().get(child.column()) {
if column.size_mode == SizeMode::Stretch {
rest_width += child.desired_size.get().x;
}
@@ -362,7 +362,7 @@ impl Grid {
if available_size.y.is_infinite() {
for child_handle in self.widget.children.iter() {
let child = ui.nodes.borrow(*child_handle).widget();
if let Some(row) = self.rows.borrow().get(child.row) {
if let Some(row) = self.rows.borrow().get(child.row()) {
if row.size_mode == SizeMode::Stretch {
rest_height += child.desired_size.get().y;
}
+8 -6
View File
@@ -1,19 +1,21 @@
use std::sync::{Mutex, Arc};
use rg3d_core::{
math::vec2::Vec2,
pool::Handle
};
use crate::{
resource::texture::Texture,
gui::{
node::UINode,
Draw,
draw::{DrawingContext, CommandKind, CommandTexture},
widget::{Widget, AsWidget},
Layout,
UserInterface,
Update,
widget::WidgetBuilder
},
resource::texture::Texture,
};
use rg3d_core::math::vec2::Vec2;
use crate::gui::node::UINode;
use rg3d_core::pool::Handle;
use crate::gui::widget::WidgetBuilder;
pub struct Image {
widget: Widget,
@@ -33,7 +35,7 @@ impl AsWidget for Image {
impl Draw for Image {
fn draw(&mut self, drawing_context: &mut DrawingContext) {
let bounds = self.widget.get_screen_bounds();
drawing_context.push_rect_filled(&bounds, None, self.widget.color);
drawing_context.push_rect_filled(&bounds, None, self.widget.color());
let texture = self.texture.as_ref().map_or(CommandTexture::None,
|t| { CommandTexture::Texture(t.clone()) });
drawing_context.commit(CommandKind::Geometry, texture);
+27 -5
View File
@@ -17,11 +17,13 @@ pub mod list_box;
pub mod stack_panel;
pub mod text_box;
pub mod check_box;
pub mod style;
use std::{
collections::VecDeque,
rc::Rc,
cell::RefCell,
any::Any
};
use crate::{
gui::{
@@ -34,6 +36,7 @@ use crate::{
UIEventKind,
UIEventHandler,
},
style::Style
},
resource::{ttf::Font},
utils::UnsafeCollectionView,
@@ -104,19 +107,39 @@ pub enum Visibility {
Hidden,
}
trait Layout {
pub trait Layout {
fn measure_override(&self, ui: &UserInterface, available_size: Vec2) -> Vec2;
fn arrange_override(&self, ui: &UserInterface, final_size: Vec2) -> Vec2;
}
trait Draw {
pub trait Draw {
fn draw(&mut self, drawing_context: &mut DrawingContext);
}
trait Update {
pub trait Update {
fn update(&mut self, dt: f32);
}
pub trait Styleable: AsWidget {
fn set_property(&mut self, name: &str, value: &dyn Any);
fn get_property(&self, name: &str) -> Option<&'_ dyn Any>;
fn apply_style(&mut self, style: Rc<Style>) {
// Apply base style first.
if let Some(base_style) = style.base_style() {
self.apply_style(base_style);
}
// Remember last applied style.
self.widget_mut().set_style(style.clone());
// Then apply current.
for setter in style.setters() {
self.set_property(setter.name(), setter.value());
}
}
}
pub struct UserInterface {
nodes: Pool<UINode>,
drawing_context: DrawingContext,
@@ -691,7 +714,7 @@ impl UserInterface {
/// to some specific node. Lets take a window for example, we can close or open
/// it by just sending an appropriate event - something like this:
///
/// ```
/// ```no_run
/// use rg3d::gui::event::{UIEvent, UIEventKind};
///
/// ui.send_event(UIEvent::targeted(options_window, UIEventKind::Opened));
@@ -700,7 +723,6 @@ impl UserInterface {
self.events.push_back(event);
}
/// Extracts UI event one-by-one from common queue. Each extracted event will go to *all*
/// available nodes first and only then will be moved outside of this method. This is one
/// of most important methods which must be called each frame of your game loop, otherwise
+55 -54
View File
@@ -13,7 +13,7 @@ use crate::gui::{
widget::{Widget, WidgetBuilder, AsWidget},
Draw,
draw::DrawingContext,
Update
Update,
};
use rg3d_core::{
color::Color, math,
@@ -262,61 +262,62 @@ impl ScrollBarBuilder {
.with_width(30.0)
.with_height(30.0)
.with_event_handler(Box::new(move |ui, handle, evt| {
if evt.source == handle {
match evt.kind {
UIEventKind::MouseDown { pos, .. } => {
let indicator_pos = ui.nodes.borrow(handle).widget().screen_position;
let scroll_bar_node = ui.borrow_by_criteria_up_mut(handle, |node| node.is_scroll_bar());
let scroll_bar = scroll_bar_node.as_scroll_bar_mut();
scroll_bar.is_dragging = true;
scroll_bar.offset = indicator_pos - pos;
if evt.source != handle {
return;
}
match evt.kind {
UIEventKind::MouseDown { pos, .. } => {
let indicator_pos = ui.nodes.borrow(handle).widget().screen_position;
let scroll_bar_node = ui.borrow_by_criteria_up_mut(handle, |node| node.is_scroll_bar());
let scroll_bar = scroll_bar_node.as_scroll_bar_mut();
scroll_bar.is_dragging = true;
scroll_bar.offset = indicator_pos - pos;
ui.capture_mouse(handle);
evt.handled = true;
}
UIEventKind::MouseUp { .. } => {
let scroll_bar_node = ui.borrow_by_criteria_up_mut(handle, |node| node.is_scroll_bar());
scroll_bar_node.as_scroll_bar_mut().is_dragging = false;
ui.release_mouse_capture();
evt.handled = true;
}
UIEventKind::MouseMove { pos, .. } => {
let (field_pos, field_size) = {
let canvas = ui.borrow_by_name_up(handle, ScrollBar::PART_CANVAS).widget();
(canvas.screen_position, canvas.actual_size.get())
};
let bar_size = ui.nodes.borrow(handle).widget().actual_size.get();
let scroll_bar_node = ui.borrow_by_criteria_up_mut(handle, |node| node.is_scroll_bar());
let scroll_bar = scroll_bar_node.as_scroll_bar_mut();
let orientation = scroll_bar.orientation;
if scroll_bar.is_dragging {
let percent = match orientation {
Orientation::Horizontal => {
let span = field_size.x - bar_size.x;
let offset = pos.x - field_pos.x + scroll_bar.offset.x;
if span > 0.0 {
math::clampf(offset / span, 0.0, 1.0)
} else {
0.0
}
}
Orientation::Vertical => {
let span = field_size.y - bar_size.y;
let offset = pos.y - field_pos.y + scroll_bar.offset.y;
if span > 0.0 {
math::clampf(offset / span, 0.0, 1.0)
} else {
0.0
}
}
};
scroll_bar.set_value(percent * (scroll_bar.max - scroll_bar.min));
evt.handled = true;
}
}
_ => ()
ui.capture_mouse(handle);
evt.handled = true;
}
UIEventKind::MouseUp { .. } => {
let scroll_bar_node = ui.borrow_by_criteria_up_mut(handle, |node| node.is_scroll_bar());
scroll_bar_node.as_scroll_bar_mut().is_dragging = false;
ui.release_mouse_capture();
evt.handled = true;
}
UIEventKind::MouseMove { pos, .. } => {
let (field_pos, field_size) = {
let canvas = ui.borrow_by_name_up(handle, ScrollBar::PART_CANVAS).widget();
(canvas.screen_position, canvas.actual_size.get())
};
let bar_size = ui.nodes.borrow(handle).widget().actual_size.get();
let scroll_bar_node = ui.borrow_by_criteria_up_mut(handle, |node| node.is_scroll_bar());
let scroll_bar = scroll_bar_node.as_scroll_bar_mut();
let orientation = scroll_bar.orientation;
if scroll_bar.is_dragging {
let percent = match orientation {
Orientation::Horizontal => {
let span = field_size.x - bar_size.x;
let offset = pos.x - field_pos.x + scroll_bar.offset.x;
if span > 0.0 {
math::clampf(offset / span, 0.0, 1.0)
} else {
0.0
}
}
Orientation::Vertical => {
let span = field_size.y - bar_size.y;
let offset = pos.y - field_pos.y + scroll_bar.offset.y;
if span > 0.0 {
math::clampf(offset / span, 0.0, 1.0)
} else {
0.0
}
}
};
scroll_bar.set_value(percent * (scroll_bar.max - scroll_bar.min));
evt.handled = true;
}
}
_ => ()
}
})))
.with_stroke_color(Color::opaque(50, 50, 50))
+76
View File
@@ -0,0 +1,76 @@
use std::{
any::Any,
rc::Rc
};
pub struct PropertySetter {
name: String,
value: Box<dyn Any>,
}
impl PropertySetter {
pub fn name(&self) -> &str {
self.name.as_str()
}
pub fn value(&self) -> &dyn Any {
self.value.as_ref()
}
}
#[derive(Default)]
pub struct Style {
base_style: Option<Rc<Style>>,
setters: Vec<PropertySetter>,
}
impl Style {
pub fn new() -> Self {
Self::default()
}
pub fn setters(&self) -> &[PropertySetter] {
self.setters.as_slice()
}
pub fn base_style(&self) -> Option<Rc<Style>> {
self.base_style.clone()
}
}
pub struct StyleBuilder {
base_style: Option<Rc<Style>>,
setters: Vec<PropertySetter>,
}
impl Default for StyleBuilder {
fn default() -> Self {
Self::new()
}
}
impl StyleBuilder {
pub fn new() -> Self {
Self {
base_style: None,
setters: Default::default(),
}
}
pub fn with_base_style(mut self, style: Rc<Style>) -> Self {
self.base_style = Some(style);
self
}
pub fn with_setter(mut self, name: &str, value: Box<dyn Any>) -> Self {
self.setters.push(PropertySetter { name: name.to_owned(), value });
self
}
pub fn build(self) -> Style {
Style {
base_style: self.base_style,
setters: self.setters
}
}
}
+1 -1
View File
@@ -62,7 +62,7 @@ impl Draw for Text {
if self.need_update {
self.formatted_text.set_size(Vec2::new(bounds.w, bounds.h));
self.formatted_text.set_text(self.text.as_str());
self.formatted_text.set_color(self.widget.color);
self.formatted_text.set_color(self.widget.color());
self.formatted_text.set_horizontal_alignment(self.horizontal_alignment);
self.formatted_text.set_vertical_alignment(self.vertical_alignment);
self.formatted_text.build();
+1 -1
View File
@@ -247,7 +247,7 @@ impl Draw for TextBox {
drawing_context.commit(CommandKind::Geometry, CommandTexture::None);
self.formatted_text.set_size(Vec2::new(bounds.w, bounds.h));
self.formatted_text.set_color(self.widget.color);
self.formatted_text.set_color(self.widget.color());
self.formatted_text.build();
if let Some(ref selection_range) = self.selection_range {
+127 -16
View File
@@ -1,12 +1,10 @@
use std::{
cell::Cell,
collections::VecDeque,
};
use rg3d_core::{
color::Color,
math::vec2::Vec2,
math::{
vec2::Vec2,
Rect,
},
pool::Handle,
math::Rect,
};
use crate::gui::{
VerticalAlignment,
@@ -19,9 +17,19 @@ use crate::gui::{
node::UINode,
event::{UIEventHandler, UIEvent},
draw::DrawingContext,
Update
Update,
Styleable,
style::Style,
};
use std::{
cell::{
RefCell,
Cell,
},
collections::VecDeque,
any::Any,
rc::Rc
};
use std::cell::RefCell;
pub trait AsWidget {
fn widget(&self) -> &Widget;
@@ -49,11 +57,11 @@ pub struct Widget {
/// Maximum width and height
pub(in crate::gui) max_size: Vec2,
/// Overlay color of the node
pub(in crate::gui) color: Color,
color: Color,
/// Index of row to which this node belongs
pub(in crate::gui) row: usize,
row: usize,
/// Index of column to which this node belongs
pub(in crate::gui) column: usize,
column: usize,
/// Vertical alignment
pub(in crate::gui) vertical_alignment: VerticalAlignment,
/// Horizontal alignment
@@ -73,6 +81,7 @@ pub struct Widget {
pub(in crate::gui) event_handlers: Vec<Box<UIEventHandler>>,
pub(in crate::gui) events: RefCell<VecDeque<UIEvent>>,
pub(in crate::gui) is_hit_test_visible: bool,
pub(in crate::gui) style: Option<Rc<Style>>,
}
impl Default for Widget {
@@ -82,9 +91,7 @@ impl Default for Widget {
}
impl Update for Widget {
fn update(&mut self, _dt: f32) {
}
fn update(&mut self, _dt: f32) {}
}
impl Layout for Widget {
@@ -135,11 +142,28 @@ impl Draw for Widget {
}
impl Widget {
pub const WIDTH: &'static str = "Width";
pub const HEIGHT: &'static str = "Height";
pub const VERTICAL_ALIGNMENT: &'static str = "VerticalAlignment";
pub const HORIZONTAL_ALIGNMENT: &'static str = "HorizontalAlignment";
pub const MARGIN: &'static str = "Margin";
pub const ROW: &'static str = "Row";
pub const COLUMN: &'static str = "Column";
pub const COLOR: &'static str = "Color";
pub const VISIBILITY: &'static str = "Visibility";
pub const MIN_SIZE: &'static str = "MinSize";
pub const MAX_SIZE: &'static str = "MaxSize";
#[inline]
pub fn set_color(&mut self, color: Color) {
self.color = color;
}
#[inline]
pub fn color(&self) -> Color {
self.color
}
#[inline]
pub fn set_width(&mut self, width: f32) {
self.width.set(width);
@@ -165,6 +189,16 @@ impl Widget {
self.horizontal_alignment = halign;
}
#[inline]
pub fn column(&self) -> usize {
self.column
}
#[inline]
pub fn row(&self) -> usize {
self.row
}
#[inline]
pub fn get_screen_bounds(&self) -> Rect<f32> {
Rect::new(self.screen_position.x, self.screen_position.y, self.actual_size.get().x, self.actual_size.get().y)
@@ -179,6 +213,29 @@ impl Widget {
pub fn get_visibility(&self) -> Visibility {
self.visibility
}
#[inline]
pub fn set_style(&mut self, style: Rc<Style>) {
self.style = Some(style);
}
fn raw_copy(&self) -> Self {
Self {
name: self.name.clone(),
width: self.width.clone(),
height: self.height.clone(),
min_size: self.min_size,
max_size: self.max_size,
color: self.color,
row: self.row,
column: self.column,
vertical_alignment: self.vertical_alignment,
horizontal_alignment: self.horizontal_alignment,
margin: self.margin,
visibility: self.visibility,
..Default::default()
}
}
}
pub struct WidgetBuilder {
@@ -198,6 +255,7 @@ pub struct WidgetBuilder {
event_handlers: Vec<Box<UIEventHandler>>,
is_hit_test_visible: bool,
visibility: Visibility,
style: Option<Rc<Style>>,
}
impl Default for WidgetBuilder {
@@ -224,7 +282,8 @@ impl WidgetBuilder {
children: Vec::new(),
event_handlers: Vec::new(),
is_hit_test_visible: true,
visibility: Visibility::Visible
visibility: Visibility::Visible,
style: None,
}
}
@@ -283,6 +342,11 @@ impl WidgetBuilder {
self
}
pub fn with_style(mut self, style: Rc<Style>) -> Self {
self.style = Some(style);
self
}
pub fn with_child(mut self, handle: Handle<UINode>) -> Self {
if handle.is_some() {
self.children.push(handle);
@@ -318,7 +382,7 @@ impl WidgetBuilder {
}
pub fn build(self) -> Widget {
Widget {
let mut widget = Widget {
name: self.name.unwrap_or_default(),
desired_local_position: Cell::new(self.desired_position.unwrap_or(Vec2::ZERO)),
width: Cell::new(self.width.unwrap_or(std::f32::NAN)),
@@ -346,6 +410,53 @@ impl WidgetBuilder {
event_handlers: self.event_handlers,
events: RefCell::new(VecDeque::new()),
is_hit_test_visible: self.is_hit_test_visible,
style: None,
};
if let Some(style) = self.style {
widget.apply_style(style);
}
widget
}
}
impl Styleable for Widget {
fn set_property(&mut self, name: &str, value: &dyn Any) {
match name {
Self::HORIZONTAL_ALIGNMENT => if let Some(value) = value.downcast_ref() { self.horizontal_alignment = *value },
Self::VERTICAL_ALIGNMENT => if let Some(value) = value.downcast_ref() { self.vertical_alignment = *value },
Self::WIDTH => {
if let Some(value) = value.downcast_ref() { self.width.set(*value) } else if let Some(value) = value.downcast_ref::<f64>() { self.width.set(*value as f32) }
}
Self::HEIGHT => {
if let Some(value) = value.downcast_ref() { self.height.set(*value) } else if let Some(value) = value.downcast_ref::<f64>() { self.height.set(*value as f32) }
}
Self::MARGIN => if let Some(value) = value.downcast_ref() { self.margin = *value },
Self::ROW => if let Some(value) = value.downcast_ref() { self.row = *value },
Self::COLUMN => if let Some(value) = value.downcast_ref() { self.column = *value },
Self::COLOR => if let Some(value) = value.downcast_ref() { self.color = *value },
Self::VISIBILITY => if let Some(value) = value.downcast_ref() { self.visibility = *value },
Self::MIN_SIZE => if let Some(value) = value.downcast_ref() { self.min_size = *value },
Self::MAX_SIZE => if let Some(value) = value.downcast_ref() { self.max_size = *value },
_ => ()
}
}
fn get_property(&self, name: &str) -> Option<&'_ dyn Any> {
match name {
Self::HORIZONTAL_ALIGNMENT => Some(&self.horizontal_alignment),
Self::VERTICAL_ALIGNMENT => Some(&self.vertical_alignment),
Self::WIDTH => Some(&self.width),
Self::HEIGHT => Some(&self.height),
Self::MARGIN => Some(&self.margin),
Self::ROW => Some(&self.row),
Self::COLUMN => Some(&self.column),
Self::VISIBILITY => Some(&self.visibility),
Self::COLOR => Some(&self.color),
Self::MIN_SIZE => Some(&self.min_size),
Self::MAX_SIZE => Some(&self.max_size),
_ => None,
}
}
}
+15 -6
View File
@@ -5,12 +5,21 @@ use rg3d_core::{
};
use crate::gui::{
event::UIEventKind,
border::BorderBuilder, node::UINode,
UserInterface, grid::{GridBuilder, Column, Row},
HorizontalAlignment, text::TextBuilder, Thickness,
button::ButtonBuilder, scroll_viewer::ScrollViewerBuilder,
Layout, widget::{Widget, WidgetBuilder, AsWidget}, Draw,
draw::DrawingContext, Update, event::UIEvent, Visibility,
border::BorderBuilder,
node::UINode,
UserInterface,
grid::{GridBuilder, Column, Row},
HorizontalAlignment,
text::TextBuilder,
Thickness,
button::ButtonBuilder,
scroll_viewer::ScrollViewerBuilder,
Layout,
widget::{Widget, WidgetBuilder, AsWidget},
Draw,
draw::DrawingContext,
Update, event::UIEvent,
Visibility,
bool_to_visibility,
};
+8 -4
View File
@@ -19,7 +19,8 @@ use crate::{
gpu_texture::GpuTexture,
shadow_map_renderer::PointShadowMapRenderer,
QualitySettings,
},
RenderPassStatistics
}
};
use rg3d_core::{
math::{
@@ -30,7 +31,6 @@ use rg3d_core::{
},
color::Color,
};
use crate::renderer::RenderPassStatistics;
struct AmbientLightShader {
program: GpuProgram,
@@ -224,8 +224,12 @@ impl DeferredLightRenderer {
}
pub fn set_quality_settings(&mut self, settings: &QualitySettings) -> Result<(), RendererError> {
self.spot_shadow_map_renderer = SpotShadowMapRenderer::new(settings.spot_shadow_map_size)?;
self.point_shadow_map_renderer = PointShadowMapRenderer::new(settings.point_shadow_map_size)?;
if settings.spot_shadow_map_size != self.spot_shadow_map_renderer.size {
self.spot_shadow_map_renderer = SpotShadowMapRenderer::new(settings.spot_shadow_map_size)?;
}
if settings.point_shadow_map_size != self.point_shadow_map_renderer.size {
self.point_shadow_map_renderer = PointShadowMapRenderer::new(settings.point_shadow_map_size)?;
}
Ok(())
}
+1 -1
View File
@@ -1054,7 +1054,7 @@ mod test {
#[test]
fn font_test() {
let font_bytes = std::include_bytes!("../built_in_font.ttf").to_vec();
let font = Font::from_memory(font_bytes, 20.0, (0..255).collect()).unwrap();
let font = Font::from_memory(font_bytes, 20.0, Font::default_char_set()).unwrap();
let font = Rc::new(RefCell::new(font));
let raster_path = Path::new("./raster");
if !raster_path.exists() {
+3 -2
View File
@@ -16,7 +16,9 @@ use rg3d_core::{
Handle,
PoolIterator,
PoolIteratorMut,
},
PoolPairIterator,
PoolPairIteratorMut
}
};
use std::sync::{Mutex, Arc};
use crate::{
@@ -28,7 +30,6 @@ use crate::{
},
resource::model::Model,
};
use rg3d_core::pool::{PoolPairIterator, PoolPairIteratorMut};
#[derive(Copy, Clone)]
pub struct KeyFrame {
+14 -11
View File
@@ -397,7 +397,7 @@ impl Graph {
pub fn traverse_iter(&self, from: Handle<Node>) -> GraphTraverseIterator {
GraphTraverseIterator {
graph: self,
stack: vec![from]
stack: vec![from],
}
}
@@ -410,14 +410,14 @@ impl Graph {
pub fn traverse_handle_iter(&self, from: Handle<Node>) -> GraphHandleTraverseIterator {
GraphHandleTraverseIterator {
graph: self,
stack: vec![from]
stack: vec![from],
}
}
}
pub struct GraphTraverseIterator<'a> {
graph: &'a Graph,
stack: Vec<Handle<Node>>
stack: Vec<Handle<Node>>,
}
impl<'a> Iterator for GraphTraverseIterator<'a> {
@@ -431,7 +431,7 @@ impl<'a> Iterator for GraphTraverseIterator<'a> {
self.stack.push(*child_handle);
}
return Some(node)
return Some(node);
}
None
@@ -440,7 +440,7 @@ impl<'a> Iterator for GraphTraverseIterator<'a> {
pub struct GraphHandleTraverseIterator<'a> {
graph: &'a Graph,
stack: Vec<Handle<Node>>
stack: Vec<Handle<Node>>,
}
impl<'a> Iterator for GraphHandleTraverseIterator<'a> {
@@ -452,7 +452,7 @@ impl<'a> Iterator for GraphHandleTraverseIterator<'a> {
self.stack.push(*child_handle);
}
return Some(handle)
return Some(handle);
}
None
}
@@ -476,9 +476,12 @@ impl Visit for Graph {
#[cfg(test)]
mod test {
use crate::scene::graph::Graph;
use crate::scene::{
graph::Graph,
node::Node,
base::Base,
};
use rg3d_core::pool::Handle;
use crate::scene::node::{Node, NodeKind};
#[test]
fn graph_init_test() {
@@ -490,9 +493,9 @@ mod test {
#[test]
fn graph_node_test() {
let mut graph = Graph::new();
let a = graph.add_node(Node::new(NodeKind::Base));
let b = graph.add_node(Node::new(NodeKind::Base));
let c = graph.add_node(Node::new(NodeKind::Base));
let a = graph.add_node(Node::Base(Base::default()));
let b = graph.add_node(Node::Base(Base::default()));
let c = graph.add_node(Node::Base(Base::default()));
assert_eq!(graph.pool.alive_count(), 4);
}
}
+314
View File
@@ -0,0 +1,314 @@
use rg3d_core::math::vec3::Vec3;
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum PathVertexState {
NonVisited,
Open,
Closed,
}
pub struct PathVertex {
/// Position in world.
position: Vec3,
state: PathVertexState,
g_score: f32,
f_score: f32,
parent: Option<usize>,
neighbours: Vec<usize>,
}
impl PathVertex {
pub fn new(position: Vec3) -> Self {
Self {
position,
parent: None,
g_score: std::f32::MAX,
f_score: std::f32::MAX,
state: PathVertexState::NonVisited,
neighbours: Default::default(),
}
}
fn clear(&mut self) {
self.g_score = std::f32::MAX;
self.f_score = std::f32::MAX;
self.state = PathVertexState::NonVisited;
self.parent = None;
}
}
pub struct PathFinder {
vertices: Vec<PathVertex>
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum PathKind {
Full,
Partial,
Empty,
}
fn heuristic(a: Vec3, b: Vec3) -> f32 {
a.sqr_distance(&b)
}
impl Default for PathFinder {
fn default() -> Self {
Self::new()
}
}
pub enum PathError {
InvalidIndex(usize),
CyclicReferenceFound,
}
impl PathFinder {
pub fn new() -> Self {
Self {
vertices: Default::default()
}
}
/// Sets active set of vertices. Links between vertices must contain
/// valid indices (which are not out-of-bounds), otherwise path from/to
/// such vertices won't be built.
pub fn set_vertices(&mut self, vertices: Vec<PathVertex>) {
self.vertices = vertices;
}
/// Tries to find a vertex closest to given point.
///
/// # Notes
///
/// O(n) complexity.
pub fn get_closest_vertex_to(&self, point: Vec3) -> Option<usize> {
let mut closest_sqr_distance = std::f32::MAX;
let mut closest_index = None;
for (i, vertex) in self.vertices.iter().enumerate() {
let sqr_distance = (vertex.position - point).sqr_len();
if sqr_distance < closest_sqr_distance {
closest_sqr_distance = sqr_distance;
closest_index = Some(i);
}
}
closest_index
}
/// Creates bidirectional link between two vertices. Bidirectional means
/// that point `a` can be reached from point `b` as well as point `b`
/// can be reached from point `a`.
pub fn link_bidirect(&mut self, a: usize, b: usize) {
self.link_unidirect(a, b);
self.link_unidirect(b, a);
}
/// Creates unidirectional link between vertex `a` and vertex `b`. Unidirectional
/// means that there is no direct link between `b` to `a`, only from `a` to `b`.
pub fn link_unidirect(&mut self, a: usize, b: usize) {
if let Some(vertex_a) = self.vertices.get_mut(a) {
vertex_a.neighbours.push(b);
}
}
/// Returns shared reference to path vertex at given index.
pub fn get_vertex(&self, index: usize) -> Option<&PathVertex> {
self.vertices.get(index)
}
/// Tries to build path from begin point to end point. Returns path kind:
/// - Full: there are direct path from begin to end.
/// - Partial: there are not direct path from begin to end, but it is closest.
/// - Empty: no path available - in most cases indicates some error in input
/// params.
///
/// # Notes
///
/// This is more or less naive implementation, it most certainly will be slower
/// than specialized solutions. I haven't benchmarked this algorithm against any
/// other library!
pub fn build(&mut self, from: usize, to: usize, path: &mut Vec<Vec3>) -> Result<PathKind, PathError> {
if self.vertices.is_empty() {
return Ok(PathKind::Empty);
}
path.clear();
for vertex in self.vertices.iter_mut() {
vertex.clear();
}
let end_pos = self.vertices.get(to)
.ok_or(PathError::InvalidIndex(to))?.position;
// Put start vertex in open set.
let start = self.vertices.get_mut(from).ok_or(PathError::InvalidIndex(from))?;
start.state = PathVertexState::Open;
start.g_score = 0.0;
start.f_score = heuristic(start.position, end_pos);
let mut open_set_size = 1;
while open_set_size > 0 {
let mut current_index = 0;
for (i, vertex) in self.vertices.iter().enumerate() {
let current_vertex = self.vertices.get(current_index)
.ok_or(PathError::InvalidIndex(current_index))?;
if vertex.state == PathVertexState::Open && vertex.f_score < current_vertex.f_score {
current_index = i;
}
}
if current_index == to {
self.reconstruct_path(current_index, path);
return Ok(PathKind::Full);
}
open_set_size -= 1;
// Take second mutable reference to vertices array, we'll enforce borrowing rules
// at runtime. It will *never* give two mutable references to same path vertex.
let unsafe_vertices: &mut Vec<PathVertex> = unsafe {
&mut *(&mut self.vertices as *mut _)
};
let current_vertex = self.vertices.get_mut(current_index)
.ok_or(PathError::InvalidIndex(current_index))?;
current_vertex.state = PathVertexState::Closed;
for neighbour_index in current_vertex.neighbours.iter() {
// Make sure that borrowing rules are not violated.
if *neighbour_index == current_index {
return Err(PathError::CyclicReferenceFound);
}
// Safely get mutable reference to neighbour
let neighbour = unsafe_vertices.get_mut(*neighbour_index)
.ok_or(PathError::InvalidIndex(*neighbour_index))?;
if neighbour.state == PathVertexState::Closed {
continue;
}
neighbour.state = PathVertexState::Open;
open_set_size += 1;
let g_score = current_vertex.g_score + heuristic(current_vertex.position, neighbour.position);
if g_score >= neighbour.g_score {
continue;
}
neighbour.parent = Some(current_index);
neighbour.g_score = g_score;
neighbour.f_score = g_score + heuristic(neighbour.position, end_pos);
}
}
// No direct path found, then there is probably partial path exists.
// Look for vertex with least f_score and use it as starting point to
// reconstruct partial path.
let mut closest_index = 0;
for (i, vertex) in self.vertices.iter().enumerate() {
let closest_vertex = self.vertices.get(closest_index)
.ok_or(PathError::InvalidIndex(closest_index))?;
if vertex.f_score < closest_vertex.f_score {
closest_index = i;
}
}
self.reconstruct_path(closest_index, path);
if path.is_empty() {
Ok(PathKind::Empty)
} else {
Ok(PathKind::Partial)
}
}
fn reconstruct_path(&self, mut current: usize, path: &mut Vec<Vec3>) {
while let Some(vertex) = self.vertices.get(current) {
path.push(vertex.position);
if let Some(parent) = vertex.parent {
current = parent;
} else {
break;
}
}
}
}
#[cfg(test)]
mod test {
use crate::utils::astar::{PathFinder, PathVertex};
use rg3d_core::math::vec3::Vec3;
use rand::Rng;
#[test]
fn astar_random_points() {
let mut pathfinder = PathFinder::new();
let mut path = Vec::new();
assert!(pathfinder.build(0, 0, &mut path).is_ok());
assert!(path.is_empty());
let size = 40;
// Create vertices.
let mut vertices = Vec::new();
for y in 0..size {
for x in 0..size {
vertices.push(PathVertex::new(Vec3::new(x as f32, y as f32, 0.0)));
}
}
pathfinder.set_vertices(vertices);
assert!(pathfinder.build(100000, 99999, &mut path).is_err());
// Link vertices as grid.
for y in 0..(size - 1) {
for x in 0..(size - 1) {
pathfinder.link_bidirect(y * size + x, y * size + x + 1);
pathfinder.link_bidirect(y * size + x, (y + 1) * size + x);
}
}
let mut paths_count = 0;
for _ in 0..1000 {
let sx = rand::thread_rng().gen_range(0, size - 1);
let sy = rand::thread_rng().gen_range(0, size - 1);
let tx = rand::thread_rng().gen_range(0, size - 1);
let ty = rand::thread_rng().gen_range(0, size - 1);
let from = sy * size + sx;
let to = ty * size + tx;
assert!(pathfinder.build(from, to, &mut path).is_ok());
assert!(!path.is_empty());
if path.len() > 1 {
paths_count += 1;
assert_eq!(*path.first().unwrap(), pathfinder.get_vertex(to).unwrap().position);
assert_eq!(*path.last().unwrap(), pathfinder.get_vertex(from).unwrap().position);
} else {
let point = *path.first().unwrap();
assert_eq!(point, pathfinder.get_vertex(to).unwrap().position);
assert_eq!(point, pathfinder.get_vertex(from).unwrap().position);
}
for pair in path.chunks(2) {
if pair.len() == 2 {
let a = pair[0];
let b = pair[1];
assert!(a.distance(&b) <= 2.0f32.sqrt());
}
}
}
assert!(paths_count > 0);
}
}
+6 -2
View File
@@ -1,7 +1,11 @@
pub mod astar;
use std::marker::PhantomData;
use crate::scene::mesh::Mesh;
use crate::{
scene::mesh::Mesh,
scene::base::AsBase
};
use rg3d_physics::static_geometry::{StaticGeometry, StaticTriangle};
use crate::scene::base::AsBase;
pub struct UnsafeCollectionView<T> {
items: *const T,