Merge branch 'FyroxEngine:master' into tile-anim

This commit is contained in:
b-guild
2025-01-10 14:24:19 -08:00
committed by GitHub
18 changed files with 485 additions and 124 deletions
+1
View File
@@ -19,6 +19,7 @@ lazy_static = "1.4.0"
ron = "0.8.0"
serde = "^1.0.0"
toml = { version = "0.8.10", default-features = false, features = ["parse"] }
toml_edit = "0.22.6"
strum = "0.26.1"
strum_macros = "0.26.1"
open = "5"
+29 -5
View File
@@ -169,21 +169,39 @@ use plugins::inspector::InspectorPlugin;
use std::{
cell::RefCell,
collections::VecDeque,
io::{BufRead, BufReader},
io::{BufRead, BufReader, Cursor, Read},
path::{Path, PathBuf},
process::Stdio,
rc::Rc,
sync::{
atomic::{AtomicBool, Ordering},
mpsc::{self, channel, Receiver},
Arc,
Arc, LazyLock,
},
time::{Duration, Instant},
};
use toml_edit::DocumentMut;
pub const FIXED_TIMESTEP: f32 = 1.0 / 60.0;
pub const MSG_SYNC_FLAG: u64 = 1;
static EDITOR_VERSION: LazyLock<String> = LazyLock::new(|| {
let manifest = include_bytes!("../Cargo.toml");
let mut file = Cursor::new(&manifest);
let mut toml = String::new();
if file.read_to_string(&mut toml).is_ok() {
if let Ok(document) = toml.parse::<DocumentMut>() {
if let Some(package) = document.get("package").and_then(|i| i.as_table()) {
if let Some(version) = package.get("version") {
return version.to_string().replace('\"', "");
}
}
}
}
"<unknown>".to_string()
});
pub fn send_sync_message(ui: &UserInterface, mut msg: UiMessage) {
msg.flags = MSG_SYNC_FLAG;
ui.send_message(msg);
@@ -559,6 +577,10 @@ pub struct Editor {
impl Editor {
pub fn new(startup_data: Option<StartupData>) -> Self {
// Useful for debugging purposes when users don't bother to mention editor version
// they're using.
Log::info(format!("Editor version: {}", &*EDITOR_VERSION));
let (log_message_sender, log_message_receiver) = channel();
Log::add_listener(log_message_sender);
@@ -2065,9 +2087,11 @@ impl Editor {
let graphics_context = engine.graphics_context.as_initialized_mut();
graphics_context
.window
.set_title(&format!("Fyroxed: {}", working_directory.to_string_lossy()));
graphics_context.window.set_title(&format!(
"FyroxEd {}: {}",
*EDITOR_VERSION,
working_directory.to_string_lossy()
));
match FileSystemWatcher::new(&working_directory, Duration::from_secs(1)) {
Ok(watcher) => {
+9 -3
View File
@@ -86,6 +86,7 @@ impl BuildWindow {
let window = WindowBuilder::new(WidgetBuilder::new().with_width(420.0).with_height(200.0))
.can_minimize(false)
.can_close(false)
.can_maximize(false)
.open(false)
.with_content(
GridBuilder::new(
@@ -96,12 +97,17 @@ impl BuildWindow {
.with_child(
TextBuilder::new(
WidgetBuilder::new()
.with_margin(Thickness::uniform(1.0))
.with_margin(Thickness {
left: 5.0,
top: 1.0,
right: 1.0,
bottom: 1.0,
})
.on_column(0),
)
.with_text(format!(
"Please wait while {project_name} is building... Build Log:"
))
"Please wait while {project_name} is building..."
))
.build(ctx),
)
.with_child({
+21
View File
@@ -249,3 +249,24 @@ impl Log {
}
}
}
#[macro_export]
macro_rules! info {
($($arg:tt)*) => {
$crate::log::Log::info(format!($($arg)*))
};
}
#[macro_export]
macro_rules! warn {
($($arg:tt)*) => {
$crate::log::Log::warn(format!($($arg)*))
};
}
#[macro_export]
macro_rules! err {
($($arg:tt)*) => {
$crate::log::Log::err(format!($($arg)*))
};
}
+15 -11
View File
@@ -40,6 +40,7 @@ use crate::{
Complex, Const, Matrix, Matrix2, Matrix3, Matrix4, Quaternion, RawStorage, RawStorageMut,
SVector, Scalar, UnitComplex, UnitQuaternion, Vector2, Vector3, Vector4, U1,
},
array_as_u8_slice_mut,
io::{self, FileLoadError},
pool::{Handle, Pool},
replace_slashes,
@@ -540,24 +541,27 @@ impl_field_data!(Vector4<u64>, FieldKind::Vector4U64);
impl<T> Visit for BinaryBlob<'_, T>
where
T: Copy,
T: Copy + bytemuck::Pod,
{
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
if visitor.reading {
if let Some(field) = visitor.find_field(name) {
match &field.kind {
FieldKind::BinaryBlob(data) => {
let mut bytes = std::mem::ManuallyDrop::new(data.clone());
let len = data.len() / size_of::<T>();
let mut vec = Vec::<T>::with_capacity(len);
// SAFETY: This is kinda safe, but may cause portability issues because of various byte order.
// However it seems to be fine, since big-endian is pretty much dead and unused nowadays.
*self.vec = unsafe {
Vec::from_raw_parts(
bytes.as_mut_ptr() as *mut T,
bytes.len() / std::mem::size_of::<T>(),
bytes.capacity() / std::mem::size_of::<T>(),
)
};
unsafe {
std::ptr::copy_nonoverlapping(
data.as_ptr(),
array_as_u8_slice_mut(&mut vec).as_mut_ptr(),
data.len(),
);
vec.set_len(len);
}
*self.vec = vec;
Ok(())
}
+59
View File
@@ -18,49 +18,108 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//! Buffer is a type-agnostic data storage located directly in GPU memory. It could be considered
//! as a data block which content is a pile of bytes, whose meaning is defined externally.
use crate::error::FrameworkError;
use bytemuck::Pod;
use fyrox_core::{array_as_u8_slice, array_as_u8_slice_mut};
use std::any::Any;
/// GPU buffer kind.
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum BufferKind {
/// Vertex buffer. It is used to supply vertex data (such as positions, normals, texture
/// coordinates, etc.) to GPU.
Vertex,
/// Index buffer. It is used to describe how vertices forms specific types of primitives.
/// For example a quad could be described by a set of 4 vertices, but 2 triangles which in their
/// turn consists of 6 indices (0,1,2,0,2,3 vertex indices).
Index,
/// Uniform buffer. It is used to supply context-specific data that is needed for rendering.
/// Usually it contains data that changes from frame-to-frame (world transform, color, etc.).
Uniform,
/// Pixel read buffer. It is a special buffer that is used to asynchronously read-back data
/// from GPU.
PixelRead,
/// Pixel write buffer. It is a special buffer that is used to write some data to GPU
/// asynchronously.
PixelWrite,
}
/// A hint for video driver that allows it to optimize buffer's content for more efficient use.
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum BufferUsage {
/// The buffer contents will be modified once and used at most a few times.
/// The buffer contents are modified by the application, and used as the source for
/// drawing and image specification commands.
StreamDraw,
/// The buffer contents will be modified once and used at most a few times.
/// The buffer contents are modified by reading data from the GPU, and used to return that
/// data when queried by the application.
StreamRead,
/// The buffer contents will be modified once and used at most a few times.
/// The buffer contents are modified by the application, and used as the source for
/// drawing and image specification commands.
StreamCopy,
/// The buffer contents will be modified once and used many times.
/// The buffer contents are modified by the application, and used as the source for
/// drawing and image specification commands.
StaticDraw,
/// The buffer contents will be modified once and used many times.
/// The buffer contents are modified by reading data from the GPU, and used to return that
/// data when queried by the application.
StaticRead,
/// The buffer contents will be modified once and used many times.
/// The buffer contents are modified by reading data from the GPU, and used as the source for
/// drawing and image specification commands.
StaticCopy,
/// The buffer contents will be modified repeatedly and used many times.
/// The buffer contents are modified by the application, and used as the source for
/// drawing and image specification commands.
DynamicDraw,
/// The buffer contents will be modified repeatedly and used many times.
/// The buffer contents are modified by reading data from the GPU, and used to return that
/// data when queried by the application.
DynamicRead,
/// The buffer contents will be modified repeatedly and used many times.
/// The buffer contents are modified by reading data from the GPU, and used as the source for
/// drawing and image specification commands.
DynamicCopy,
}
/// Buffer is a type-agnostic data storage located directly in GPU memory. It could be considered
/// as a data block which content is a pile of bytes, whose meaning is defined externally.
pub trait Buffer: Any {
/// Downcasts self reference as reference to [`Any`].
fn as_any(&self) -> &dyn Any;
/// Downcasts self reference as reference to [`Any`].
fn as_any_mut(&mut self) -> &mut dyn Any;
/// Returns usage kind of the buffer.
fn usage(&self) -> BufferUsage;
/// Returns buffer kind.
fn kind(&self) -> BufferKind;
/// Returns total size of the buffer in bytes.
fn size(&self) -> usize;
/// Writes an arbitrary number of bytes from the given slice.
fn write_data(&self, data: &[u8]) -> Result<(), FrameworkError>;
/// Read an arbitrary number of bytes from the buffer (GPU memory) to the given slice. The
/// amount of the data that will be attempted to read is defined by the length of the given
/// slice.
fn read_data(&self, data: &mut [u8]) -> Result<(), FrameworkError>;
}
impl dyn Buffer {
/// Tries to write typed data to the buffer. The data type must implement [`Pod`] trait for
/// safe usage.
pub fn write_data_of_type<T: Pod>(&self, data: &[T]) -> Result<(), FrameworkError> {
let data = array_as_u8_slice(data);
Buffer::write_data(self, data)
}
/// Tries to read data from the buffer and convert them to the given type. The data type must
/// implement [`Pod`] trait for safe usage. The amount of the data that will be attempted to
/// read is defined by the length of the given slice.
pub fn read_data_of_type<T: Pod>(&self, data: &mut [T]) -> Result<(), FrameworkError> {
let data = array_as_u8_slice_mut(data);
Buffer::read_data(self, data)
+1
View File
@@ -83,6 +83,7 @@ pub struct VertexBufferDescriptor<'a> {
pub struct GeometryBufferDescriptor<'a> {
pub element_kind: ElementKind,
pub buffers: &'a [VertexBufferDescriptor<'a>],
pub usage: BufferUsage,
}
pub trait GeometryBuffer {
+4 -5
View File
@@ -18,17 +18,16 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
use crate::geometry_buffer::{AttributeKind, GeometryBuffer, GeometryBufferDescriptor};
use crate::{
buffer::{Buffer, BufferKind, BufferUsage},
buffer::{Buffer, BufferKind},
core::{array_as_u8_slice, math::TriangleDefinition},
error::FrameworkError,
geometry_buffer::{AttributeKind, GeometryBuffer, GeometryBufferDescriptor},
gl::{buffer::GlBuffer, server::GlGraphicsServer, ToGlConstant},
ElementKind,
};
use glow::HasContext;
use std::any::Any;
use std::{cell::Cell, marker::PhantomData, rc::Weak};
use std::{any::Any, cell::Cell, marker::PhantomData, rc::Weak};
impl AttributeKind {
fn gl_type(self) -> u32 {
@@ -61,7 +60,7 @@ impl GlGeometryBuffer {
server.set_vertex_array_object(Some(vao));
let element_buffer = GlBuffer::new(server, 0, BufferKind::Index, BufferUsage::StaticDraw)?;
let element_buffer = GlBuffer::new(server, 0, BufferKind::Index, desc.usage)?;
let mut buffers = Vec::new();
for buffer in desc.buffers {
+130 -53
View File
@@ -40,6 +40,7 @@ pub mod server;
pub mod stats;
pub mod uniform;
/// A set of possible polygon filling modes.
#[derive(
Copy,
Clone,
@@ -55,20 +56,21 @@ pub mod uniform;
EnumString,
VariantNames,
TypeUuidProvider,
Default,
)]
#[type_uuid(id = "47aff01a-7daa-427c-874c-87464a7ffe28")]
pub enum PolygonFillMode {
/// Only vertices of polygons are rendered. Their size is 1px by default.
Point,
/// Only edges of polygons are rendered using 1px lines. This mode is useful for wireframe
/// drawing.
Line,
/// The entire polygon surface is rendered. This is default rendering mode.
#[default]
Fill,
}
impl Default for PolygonFillMode {
fn default() -> Self {
Self::Fill
}
}
/// A function used to compare two values. Usually it is used for depth and stencil testing.
#[derive(
Copy,
Clone,
@@ -85,6 +87,7 @@ impl Default for PolygonFillMode {
AsRefStr,
EnumString,
VariantNames,
Default,
)]
pub enum CompareFunc {
/// Never passes.
@@ -97,6 +100,7 @@ pub enum CompareFunc {
Equal,
/// Passes if the incoming value is less than or equal to the stored value.
#[default]
LessOrEqual,
/// Passes if the incoming value is greater than the stored value.
@@ -112,12 +116,7 @@ pub enum CompareFunc {
Always,
}
impl Default for CompareFunc {
fn default() -> Self {
Self::LessOrEqual
}
}
/// Defines a set values (per color) for blending operation.
#[derive(
Copy,
Clone,
@@ -134,8 +133,10 @@ impl Default for CompareFunc {
AsRefStr,
EnumString,
VariantNames,
Default,
)]
pub enum BlendFactor {
#[default]
Zero,
One,
SrcColor,
@@ -157,29 +158,38 @@ pub enum BlendFactor {
OneMinusSrc1Alpha,
}
impl Default for BlendFactor {
fn default() -> Self {
Self::Zero
}
}
/// Defines an operation used in blending equation.
#[derive(
Copy, Clone, Hash, PartialOrd, PartialEq, Eq, Ord, Serialize, Deserialize, Visit, Debug, Reflect,
Copy,
Clone,
Hash,
PartialOrd,
PartialEq,
Eq,
Ord,
Serialize,
Deserialize,
Visit,
Debug,
Reflect,
Default,
)]
pub enum BlendMode {
/// Addition of two operands (`Source + Dest`). This is default operation.
#[default]
Add,
/// Subtraction of two operands (`Source - Dest`).
Subtract,
/// Reverse subtraction of two operands (`Dest - Source`).
ReverseSubtract,
/// Per-component min function of two operands (`min(Source, Dest)`).
Min,
/// Per-component max function of two operands (`max(Source, Dest)`).
Max,
}
impl Default for BlendMode {
fn default() -> Self {
Self::Add
}
}
/// An equation used for blending a source pixel color with the destination color (the one that is
/// already in a frame buffer). This equation has different modes for rgb/alpha parts.
#[derive(
Copy,
Clone,
@@ -196,21 +206,33 @@ impl Default for BlendMode {
Reflect,
)]
pub struct BlendEquation {
/// An operation for RGB part.
pub rgb: BlendMode,
/// An operation for alpha part.
pub alpha: BlendMode,
}
/// Blending function defines sources of data for both operands in blending equation (separately
/// for RGB and Alpha parts). Default blending function is replacing destination values with the
/// source ones.
#[derive(
Copy, Clone, PartialOrd, PartialEq, Ord, Eq, Hash, Serialize, Deserialize, Visit, Debug, Reflect,
)]
pub struct BlendFunc {
/// Data for source (the value that is produced by a shader) in the blending equation (RGB part).
pub sfactor: BlendFactor,
/// Data for destination (the value that is already in a frame buffer) in the blending equation
/// (RGB part).
pub dfactor: BlendFactor,
/// Data for source (the value that is produced by a shader) in the blending equation (alpha part).
pub alpha_sfactor: BlendFactor,
/// Data for destination (the value that is already in a frame buffer) in the blending equation
/// (alpha part).
pub alpha_dfactor: BlendFactor,
}
impl BlendFunc {
/// Creates a new blending function where both RGB and Alpha parts have the same blending factor.
pub fn new(sfactor: BlendFactor, dfactor: BlendFactor) -> Self {
Self {
sfactor,
@@ -220,6 +242,7 @@ impl BlendFunc {
}
}
/// Creates a new blending function where RGB and Alpha parts have different blending factor.
pub fn new_separate(
sfactor: BlendFactor,
dfactor: BlendFactor,
@@ -246,13 +269,19 @@ impl Default for BlendFunc {
}
}
/// A mask that defines which colors will be stored in a frame buffer during rendering operation.
/// By default, all colors are stored (every field is set to `true`).
#[derive(
Copy, Clone, PartialOrd, PartialEq, Hash, Debug, Serialize, Deserialize, Visit, Eq, Reflect,
)]
pub struct ColorMask {
/// A flag, that defines whether the red channel is written or not in a frame buffer.
pub red: bool,
/// A flag, that defines whether the green channel is written or not in a frame buffer.
pub green: bool,
/// A flag, that defines whether the blue channel is written or not in a frame buffer.
pub blue: bool,
/// A flag, that defines whether the alpha channel is written or not in a frame buffer.
pub alpha: bool,
}
@@ -268,6 +297,7 @@ impl Default for ColorMask {
}
impl ColorMask {
/// Creates a new color mask where all the components will have the specified value.
pub fn all(value: bool) -> Self {
Self {
red: value,
@@ -278,6 +308,8 @@ impl ColorMask {
}
}
/// Defines a polygon face that will be rendered. This is usually used for back face culling.
/// Default value is [`PolygonFace::FrontAndBack`].
#[derive(
Copy,
Clone,
@@ -292,25 +324,32 @@ impl ColorMask {
AsRefStr,
EnumString,
VariantNames,
Default,
)]
pub enum PolygonFace {
/// Only front faces will be rendered.
Front,
/// Only back faces will be rendered.
Back,
/// Both, back and front faces will be rendered.
#[default]
FrontAndBack,
}
impl Default for PolygonFace {
fn default() -> Self {
Self::FrontAndBack
}
}
/// Defines a function that used in a stencil test.
#[derive(
Copy, Clone, PartialOrd, PartialEq, Hash, Debug, Serialize, Deserialize, Visit, Eq, Reflect,
)]
pub struct StencilFunc {
/// A function that is used to compare two values. Default value is [`CompareFunc::Always`].
pub func: CompareFunc,
/// Reference value that is used to compare against the current value in the stencil buffer.
/// Default value is 0.
pub ref_value: u32,
/// A mask value that is used to filter out some bits (using logical AND operation) of a value
/// in the stencil buffer and the ref value (for example if a [`CompareFunc::Less`] is used
/// then the entire equation will look like `(ref & mask) < (stencil & mask)`). Default value
/// is `0xFFFFFFFF`.
pub mask: u32,
}
@@ -324,6 +363,7 @@ impl Default for StencilFunc {
}
}
/// An action with the stencil value in the stencil buffer is the stencil test passed.
#[derive(
Copy,
Clone,
@@ -339,9 +379,11 @@ impl Default for StencilFunc {
AsRefStr,
EnumString,
VariantNames,
Default,
)]
pub enum StencilAction {
/// Keeps the current value.
/// Keeps the current value. This is the default variant.
#[default]
Keep,
/// Sets the stencil buffer value to 0.
@@ -372,19 +414,19 @@ pub enum StencilAction {
Invert,
}
impl Default for StencilAction {
fn default() -> Self {
Self::Keep
}
}
/// A set of actions that will be performed with the stencil buffer during various testing stages.
#[derive(
Copy, Clone, PartialOrd, PartialEq, Hash, Debug, Serialize, Deserialize, Visit, Eq, Reflect,
)]
pub struct StencilOp {
/// An action that happens when the stencil test has failed.
pub fail: StencilAction,
/// An action that happens when the depth test has failed.
pub zfail: StencilAction,
/// An action that happens when the depth test has passed.
pub zpass: StencilAction,
/// A mask that is used to filter out some bits (using `AND` logical operation) from the source
/// value before writing it to the stencil buffer.
pub write_mask: u32,
}
@@ -399,43 +441,70 @@ impl Default for StencilOp {
}
}
/// A face side to cull.
#[derive(
Copy, Clone, PartialOrd, PartialEq, Hash, Debug, Serialize, Deserialize, Visit, Eq, Reflect,
Copy,
Clone,
PartialOrd,
PartialEq,
Hash,
Debug,
Serialize,
Deserialize,
Visit,
Eq,
Reflect,
Default,
)]
pub enum CullFace {
/// Cull only back faces.
#[default]
Back,
/// Cull only front faces.
Front,
}
impl Default for CullFace {
fn default() -> Self {
Self::Back
}
}
/// Blending parameters (such as blending function and its equation).
#[derive(Serialize, Deserialize, Default, Visit, Debug, PartialEq, Clone, Eq, Reflect)]
pub struct BlendParameters {
/// Blending function, see [`BlendFunc`] for more info.
pub func: BlendFunc,
/// Blending equation, see [`BlendEquation`] for more info.
pub equation: BlendEquation,
}
/// A rectangular area that defines which pixels will be rendered in a frame buffer or not.
#[derive(Serialize, Deserialize, Default, Visit, Debug, PartialEq, Clone, Copy, Eq, Reflect)]
pub struct ScissorBox {
/// X coordinate of the box's origin.
pub x: i32,
/// Y coordinate of the box's origin. Located at the bottom of the rectangle.
pub y: i32,
/// Width of the box.
pub width: i32,
/// Height of the box.
pub height: i32,
}
/// A set of drawing parameters, that are used during draw call. It defines pretty much all pipeline
/// settings all at once.
#[derive(Serialize, Deserialize, Visit, Debug, PartialEq, Clone, Eq, Reflect)]
pub struct DrawParameters {
/// An optional cull face. If [`None`], then the culling is disabled.
pub cull_face: Option<CullFace>,
/// Color write mask.
pub color_write: ColorMask,
/// A flag, that defines whether the depth values should be written to the depth buffer or not.
pub depth_write: bool,
/// Stencil test options. If [`None`], then the stencil test is disabled.
pub stencil_test: Option<StencilFunc>,
/// Depth test options. If [`None`], then the depth test is disabled.
pub depth_test: Option<CompareFunc>,
/// Blending options. If [`None`], then the blending is disabled.
pub blend: Option<BlendParameters>,
/// Stencil operation.
pub stencil_op: StencilOp,
/// Optional scissor box. If [`None`], then the scissor test is disabled.
pub scissor_box: Option<ScissorBox>,
}
@@ -454,22 +523,30 @@ impl Default for DrawParameters {
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
/// A range of elements (usually it's triangles) to draw in a draw call.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
pub enum ElementRange {
/// All available elements. This is the default option.
#[default]
Full,
Specific { offset: usize, count: usize },
}
impl Default for ElementRange {
fn default() -> Self {
Self::Full
}
/// Specific range of elements. Useful if you have a large buffer that contains multiple smaller
/// elements at once.
Specific {
/// Offset (in elements) from the beginning of the buffer.
offset: usize,
/// Total count of elements to draw.
count: usize,
},
}
/// Element kind of geometry.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum ElementKind {
/// Triangles.
Triangle,
/// Lines.
Line,
/// Points.
Point,
}
+73 -2
View File
@@ -18,6 +18,11 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#![warn(missing_docs)]
//! Graphics server is an abstraction layer over various graphics APIs used on different platforms
//! supported by the engine.
use crate::gpu_texture::{
GpuTextureKind, MagnificationFilter, MinificationFilter, PixelKind, WrapMode,
};
@@ -39,6 +44,7 @@ use std::{
rc::{Rc, Weak},
};
/// Graphics server capabilities.
#[derive(Debug)]
pub struct ServerCapabilities {
/// The maximum size in basic machine units of a uniform block, which must be at least 16384.
@@ -50,32 +56,65 @@ pub struct ServerCapabilities {
pub max_lod_bias: f32,
}
/// A shared reference to a graphics server.
pub type SharedGraphicsServer = Rc<dyn GraphicsServer>;
/// Graphics server is an abstraction layer over various graphics APIs used on different platforms
/// supported by the engine. Such abstraction layer tries to provide more or less high-level and
/// unified interface, that can be used to build graphics pipelines quickly and more or less efficiently.
///
/// Low-level GAPI-specific optimizations could be performed using direct access to the underlying API,
/// by downcasting to a specific type.
pub trait GraphicsServer: Any {
/// Downcasts self reference as reference to [`Any`].
fn as_any(&self) -> &dyn Any;
/// Downcasts self reference as reference to [`Any`].
fn as_any_mut(&mut self) -> &mut dyn Any;
/// Creates a GPU buffer with the given size and kind. Usage is a hint to the video driver
/// that allows to perform some potential performance optimizations.
fn create_buffer(
&self,
size: usize,
buffer_kind: BufferKind,
buffer_usage: BufferUsage,
) -> Result<Box<dyn Buffer>, FrameworkError>;
/// Creates a new GPU texture using the given descriptor.
fn create_texture(
&self,
desc: GpuTextureDescriptor,
) -> Result<Rc<RefCell<dyn GpuTexture>>, FrameworkError>;
/// Creates a new frame buffer using the given depth and color attachments. Depth attachment
/// not exist, but there must be at least one color attachment of a format that supports rendering.
fn create_frame_buffer(
&self,
depth_attachment: Option<Attachment>,
color_attachments: Vec<Attachment>,
) -> Result<Box<dyn FrameBuffer>, FrameworkError>;
/// Creates a frame buffer that "connected" to the final image that will be displayed to the
/// screen.
fn back_buffer(&self) -> Box<dyn FrameBuffer>;
/// Creates a new GPU query, that can perform asynchronous data fetching from GPU. Usually it
/// is used to create occlusion queries.
fn create_query(&self) -> Result<Box<dyn Query>, FrameworkError>;
/// Creates a new named GPU program using a pair of vertex and fragment shaders. The name could
/// be used for debugging purposes.
fn create_program(
&self,
name: &str,
vertex_source: &str,
fragment_source: &str,
) -> Result<Box<dyn GpuProgram>, FrameworkError>;
/// Almost the same as [`Self::create_program`], but accepts additional array of resource
/// definitions. The implementation of graphics server will generate proper resource bindings
/// in the shader code for you.
fn create_program_with_properties(
&self,
name: &str,
@@ -83,26 +122,58 @@ pub trait GraphicsServer: Any {
fragment_source: &str,
properties: &[ShaderResourceDefinition],
) -> Result<Box<dyn GpuProgram>, FrameworkError>;
/// Creates a new read-back buffer, that can be used to obtain texture data from GPU. It can be
/// used to read rendering result from GPU to CPU memory and save the result to disk.
fn create_async_read_buffer(
&self,
pixel_size: usize,
pixel_count: usize,
) -> Result<Box<dyn AsyncReadBuffer>, FrameworkError>;
/// Creates a new geometry buffer, which consists of one or more vertex buffers and only one
/// element buffer. Geometry buffer could be considered as a complex mesh storage allocated on
/// GPU.
fn create_geometry_buffer(
&self,
desc: GeometryBufferDescriptor,
) -> Result<Box<dyn GeometryBuffer>, FrameworkError>;
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
/// Creates a weak reference to the shared graphics server.
fn weak(self: Rc<Self>) -> Weak<dyn GraphicsServer>;
/// Sends all scheduled GPU command buffers for execution on GPU without waiting for a certain
/// threshold.
fn flush(&self);
/// Waits until all the scheduled GPU commands are fully executed. This is blocking operation, and
/// it blocks the current thread until all the commands are fully executed.
fn finish(&self);
/// Unbinds the all bound resources from the graphics pipeline.
fn invalidate_resource_bindings_cache(&self);
/// Returns GPU pipeline statistics. See [`PipelineStatistics`] for more info.
fn pipeline_statistics(&self) -> PipelineStatistics;
/// Swaps the front and back buffers and thus presenting the final image on screen. There could
/// be more than two buffers, and it is up to the graphics server implementation to choose the
/// right amount, but it can't be less than two.
fn swap_buffers(&self) -> Result<(), FrameworkError>;
/// Notifies the graphics server that the size of the back buffer has changed. It has very limited
/// use and there are very few platforms (Linux with Wayland mostly) that needs this function to
/// be called.
fn set_frame_size(&self, new_size: (u32, u32));
/// Returns current capabilities of the graphics server. See [`ServerCapabilities`] for more info.
fn capabilities(&self) -> ServerCapabilities;
/// Sets current polygon fill mode. See [`PolygonFace`] and [`PolygonFillMode`] docs for more info.
fn set_polygon_fill_mode(&self, polygon_face: PolygonFace, polygon_fill_mode: PolygonFillMode);
/// A shortcut for [`Self::create_texture`], that creates a rectangular texture with the given
/// size and pixel kind.
fn create_2d_render_target(
&self,
pixel_kind: PixelKind,
+5 -1
View File
@@ -1467,7 +1467,11 @@ impl Engine {
params: params.clone(),
});
self.sound_engine.initialize_audio_output_device()?;
if let Err(err) = self.sound_engine.initialize_audio_output_device() {
Log::err(format!(
"Unable to initialize audio output device! Reason: {err:?}"
));
}
Ok(())
} else {
@@ -125,6 +125,7 @@ impl DebugRenderer {
],
data: VertexBufferData::new::<Vertex>(None),
}],
usage: BufferUsage::DynamicDraw,
};
Ok(Self {
+1
View File
@@ -78,6 +78,7 @@ impl GeometryBufferExt for dyn GeometryBuffer {
bytes: Some(data.vertex_buffer.raw_data()),
},
}],
usage,
};
let geometry_buffer = server.create_geometry_buffer(geometry_buffer_desc)?;
+2
View File
@@ -137,6 +137,7 @@ impl UiRenderer {
],
data: VertexBufferData::new::<crate::gui::draw::Vertex>(None),
}],
usage: BufferUsage::DynamicDraw,
};
let clipping_geometry_buffer_desc = GeometryBufferDescriptor {
@@ -155,6 +156,7 @@ impl UiRenderer {
],
data: VertexBufferData::new::<crate::gui::draw::Vertex>(None),
}],
usage: BufferUsage::DynamicDraw,
};
Ok(Self {
+38 -16
View File
@@ -26,18 +26,18 @@
use crate::{
brush::Brush,
color::draw_checker_board,
core::{algebra::Vector2, color::Color, math::Rect, pool::Handle},
core::{reflect::prelude::*, type_traits::prelude::*, visitor::prelude::*},
core::{
algebra::Vector2, color::Color, math::Rect, pool::Handle, reflect::prelude::*,
type_traits::prelude::*, variable::InheritableVariable, visitor::prelude::*,
},
define_constructor,
draw::{CommandTexture, Draw, DrawingContext},
message::{MessageDirection, UiMessage},
widget::{Widget, WidgetBuilder},
BuildContext, Control, UiNode, UserInterface,
};
use fyrox_core::uuid_provider;
use fyrox_core::variable::InheritableVariable;
use fyrox_graph::constructor::{ConstructorProvider, GraphNodeConstructor};
use fyrox_texture::TextureResource;
use fyrox_texture::{TextureKind, TextureResource};
use std::ops::{Deref, DerefMut};
/// A set of messages that could be used to alter [`Image`] widget state at runtime.
@@ -91,19 +91,20 @@ impl ImageMessage {
/// # };
///
/// fn create_image(ctx: &mut BuildContext, texture: TextureResource) -> Handle<UiNode> {
/// // You must explicitly set width and height of the image, otherwise it will collapse to a
/// // point and you won't see anything.
/// let width = 100.0;
/// let height = 100.0;
/// ImageBuilder::new(WidgetBuilder::new().with_width(width).with_height(height))
/// ImageBuilder::new(WidgetBuilder::new())
/// .with_texture(texture)
/// .build(ctx)
/// }
/// ```
///
/// There are one common pitfall when using Image widget - you must explicitly set width and height of the image if it is
/// not placed to some panel, that will stretch it automatically. In other words if you created an image with undefined
/// width and height, then putting it to some container like Grid' cell will stretch the image to fit cell bounds.
/// By default, the Image widget will try to use the size of the texture as its desired size for layout
/// process. This means that the widget will be as large as the texture if the outer bounds allows
/// that. You can specify the desired width and height manually and the image will shrink/expand
/// automatically.
///
/// Keep in mind, that texture is a resource, and it could be loaded asynchronously, and during that
/// process, the UI can't fetch texture's size, and it will be collapsed into a point. After it fully
/// loaded, the widget will take texture's size as normal.
///
/// ## Vertical Flip
///
@@ -177,7 +178,8 @@ impl ImageMessage {
/// It is useful if you have many custom UI elements packed in a single texture atlas. Drawing using atlases is much more
/// efficient and faster. This could also be used for animations, when you have multiple frames packed in a single atlas
/// and changing texture coordinates over the time.
#[derive(Default, Clone, Visit, Reflect, Debug, ComponentProvider)]
#[derive(Default, Clone, Visit, Reflect, Debug, ComponentProvider, TypeUuidProvider)]
#[type_uuid(id = "18e18d0f-cb84-4ac1-8050-3480a2ec3de5")]
pub struct Image {
/// Base widget of the image.
pub widget: Widget,
@@ -210,9 +212,29 @@ impl ConstructorProvider<UiNode, UserInterface> for Image {
crate::define_widget_deref!(Image);
uuid_provider!(Image = "18e18d0f-cb84-4ac1-8050-3480a2ec3de5");
impl Control for Image {
fn measure_override(&self, ui: &UserInterface, available_size: Vector2<f32>) -> Vector2<f32> {
let mut size: Vector2<f32> = self.widget.measure_override(ui, available_size);
if let Some(texture) = self.texture.as_ref() {
let state = texture.state();
if let Some(data) = state.data_ref() {
if let TextureKind::Rectangle { width, height } = data.kind() {
let width = width as f32;
let height = height as f32;
if size.x < width {
size.x = width;
}
if size.y < height {
size.y = height;
}
}
}
}
size
}
fn draw(&self, drawing_context: &mut DrawingContext) {
let bounds = self.widget.bounding_rect();
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

+70 -17
View File
@@ -55,8 +55,7 @@ use fyrox::{
VerticalAlignment,
},
};
use fyrox_build_tools::build::BuildWindow;
use fyrox_build_tools::{BuildProfile, CommandDescriptor};
use fyrox_build_tools::{build::BuildWindow, BuildProfile, CommandDescriptor};
use std::{
collections::VecDeque,
path::{Path, PathBuf},
@@ -65,7 +64,7 @@ use std::{
pub enum Mode {
Normal,
Build {
CommandExecution {
queue: VecDeque<CommandDescriptor>,
process: Option<std::process::Child>,
current_dir: PathBuf,
@@ -74,7 +73,7 @@ pub enum Mode {
impl Mode {
pub fn is_build(&self) -> bool {
matches!(self, Mode::Build { .. })
matches!(self, Mode::CommandExecution { .. })
}
}
@@ -144,6 +143,7 @@ pub struct ProjectManager {
settings_window: Option<SettingsWindow>,
no_projects_warning: Handle<UiNode>,
exclude_project: Handle<UiNode>,
clean_project: Handle<UiNode>,
pub focused: bool,
pub update_loop_state: UpdateLoopState,
}
@@ -454,6 +454,7 @@ impl ProjectManager {
(can be changed in settings).\nHotkey: Ctrl+O";
let exclude_project_tooltip = "Removes the project from the project manager, \
but does NOT delete it.\nHotkey: Ctrl+E";
let clean_project_tooltip = "Removes project's build artifacts.\nHotkey: Ctrl+N";
let edit = make_text_and_image_button_with_tooltip(
ctx,
@@ -546,6 +547,19 @@ impl ProjectManager {
Color::ORANGE,
font_size,
);
let clean_project = make_text_and_image_button_with_tooltip(
ctx,
"Clean",
22.0,
22.0,
load_image(include_bytes!("../resources/clean.png")),
clean_project_tooltip,
0,
0,
Some(11),
Color::LIGHT_STEEL_BLUE,
font_size,
);
let hot_reload = CheckBoxBuilder::new(
WidgetBuilder::new()
.with_tab_index(Some(4))
@@ -568,6 +582,7 @@ impl ProjectManager {
.with_child(run)
.with_child(open_ide)
.with_child(upgrade)
.with_child(clean_project)
.with_child(locate)
.with_child(delete)
.with_child(exclude_project),
@@ -672,6 +687,7 @@ impl ProjectManager {
settings_window: None,
no_projects_warning,
exclude_project,
clean_project,
focused: true,
update_loop_state: Default::default(),
}
@@ -698,7 +714,7 @@ impl ProjectManager {
}
fn handle_modes(&mut self, ui: &mut UserInterface) {
let Mode::Build {
let Mode::CommandExecution {
ref mut process,
ref mut queue,
ref current_dir,
@@ -916,6 +932,19 @@ impl ProjectManager {
self.refresh(ui);
}
fn on_clean_clicked(&mut self, ui: &mut UserInterface) {
self.run_selected_project_command(
"clean",
vec![CommandDescriptor {
command: "cargo".to_string(),
args: vec!["clean".to_string()],
environment_variables: vec![],
}]
.into(),
ui,
)
}
fn on_button_click(&mut self, button: Handle<UiNode>, ui: &mut UserInterface) {
if button == self.create {
self.on_create_clicked(ui);
@@ -936,11 +965,13 @@ impl ProjectManager {
} else if button == self.locate {
self.on_locate_click();
} else if button == self.open_ide {
self.on_open_ide_click();
self.on_open_ide_click(ui);
} else if button == self.open_settings {
self.on_open_settings_click(ui);
} else if button == self.exclude_project {
self.on_exclude_project_clicked(ui);
} else if button == self.clean_project {
self.on_clean_clicked(ui);
}
}
@@ -950,7 +981,7 @@ impl ProjectManager {
Log::verify(open::that_detached(folder));
}
fn on_open_ide_click(&mut self) {
fn on_open_ide_click(&mut self, ui: &mut UserInterface) {
let project = some_or_return!(self.selection.and_then(|i| self.settings.projects.get(i)));
let mut open_ide_command = self.settings.open_ide_command.clone();
if let Some(manifest_path_arg) = open_ide_command
@@ -968,6 +999,8 @@ impl ProjectManager {
"Unable to open the IDE using {} command. Reason: {:?}",
open_ide_command, err
));
self.on_open_settings_click(ui);
}
}
@@ -976,21 +1009,40 @@ impl ProjectManager {
self.settings_window = Some(SettingsWindow::new(&self.settings, ctx));
}
fn run_selected_project_command(
&mut self,
action_name: &str,
queue: VecDeque<CommandDescriptor>,
ui: &mut UserInterface,
) {
let index = some_or_return!(self.selection);
let project = some_or_return!(self.settings.projects.get(index));
self.mode = Mode::CommandExecution {
queue,
process: None,
current_dir: project.manifest_path.parent().unwrap().into(),
};
self.build_window = Some(BuildWindow::new(action_name, &mut ui.build_ctx()));
}
fn run_build_profile(
&mut self,
name: &str,
build_profile: &BuildProfile,
ui: &mut UserInterface,
) {
let index = some_or_return!(self.selection);
let project = some_or_return!(self.settings.projects.get(index));
self.mode = Mode::Build {
queue: build_profile.build_and_run_queue(),
process: None,
current_dir: project.manifest_path.parent().unwrap().into(),
};
self.build_window = Some(BuildWindow::new(name, &mut ui.build_ctx()));
let mut build_profile = build_profile.clone();
// Force run `cargo update` before running the project to prevent various issues with
// dependency versions incompatibility.
build_profile.build_commands.insert(
0,
CommandDescriptor {
command: "cargo".to_string(),
args: vec!["update".to_string()],
environment_variables: vec![],
},
);
self.run_selected_project_command(name, build_profile.build_and_run_queue(), ui);
}
fn on_hot_reload_changed(&mut self, value: bool, ui: &mut UserInterface) {
@@ -1019,9 +1071,10 @@ impl ProjectManager {
KeyCode::KeyC if modifiers.control => self.on_create_clicked(ui),
KeyCode::KeyH if modifiers.control => self.on_hot_reload_changed(true, ui),
KeyCode::KeyL if modifiers.control => self.on_locate_click(),
KeyCode::KeyO if modifiers.control => self.on_open_ide_click(),
KeyCode::KeyO if modifiers.control => self.on_open_ide_click(ui),
KeyCode::KeyS if modifiers.control => self.on_open_settings_click(ui),
KeyCode::KeyE if modifiers.control => self.on_exclude_project_clicked(ui),
KeyCode::KeyN if modifiers.control => self.on_clean_clicked(ui),
_ => (),
}
}
+26 -11
View File
@@ -26,18 +26,32 @@ use std::{
collections::HashMap,
fmt::Display,
fs::{create_dir_all, read_dir, remove_dir_all, File},
io::{Read, Write},
io::{Cursor, Read, Write},
path::Path,
process::Command,
sync::LazyLock,
};
use toml_edit::{table, value, DocumentMut};
use uuid::Uuid;
// Ideally, this should be taken from respective Cargo.toml of the engine and the editor.
// However, it does not seem to work with builds published to crates.io, because when
// the template generator is published, it does not have these Cargo.toml's available
// and to solve this we just hard code these values and pray for the best.
pub const CURRENT_VERSION: &str = "0.36.0";
// Template generator version must match the engine version. This is checked by the CI.
pub static CURRENT_VERSION: LazyLock<String> = LazyLock::new(|| {
let manifest = include_bytes!("../Cargo.toml");
let mut file = Cursor::new(&manifest);
let mut toml = String::new();
if file.read_to_string(&mut toml).is_ok() {
if let Ok(document) = toml.parse::<DocumentMut>() {
if let Some(package) = document.get("package").and_then(|i| i.as_table()) {
if let Some(version) = package.get("version") {
return version.to_string().replace('\"', "");
}
}
}
}
panic!("Project manager's Cargo.toml is malformed!");
});
fn write_file<P: AsRef<Path>, S: AsRef<str>>(path: P, content: S) -> Result<(), String> {
let mut file = File::create(path.as_ref()).map_err(|e| e.to_string())?;
@@ -582,6 +596,7 @@ fn init_workspace(base_path: &Path, vcs: &str) -> Result<(), String> {
}
// Write Cargo.toml
let version = &*CURRENT_VERSION;
write_file(
base_path.join("Cargo.toml"),
format!(
@@ -591,10 +606,10 @@ members = ["editor", "executor", "executor-wasm", "executor-android", "game", "g
resolver = "2"
[workspace.dependencies.fyrox]
version = "{CURRENT_VERSION}"
version = "{version}"
default-features = false
[workspace.dependencies.fyroxed_base]
version = "{CURRENT_VERSION}"
version = "{version}"
default-features = false
# Separate build profiles for hot reloading. These profiles ensures that build artifacts for
@@ -821,10 +836,10 @@ pub fn upgrade_project(root_path: &Path, version: &str, local: bool) -> Result<(
dependencies["fyrox_scripts"] = scripts_table;
}
} else {
dependencies["fyrox"] = value(CURRENT_VERSION);
dependencies["fyroxed_base"] = value(CURRENT_VERSION);
dependencies["fyrox"] = value(&*CURRENT_VERSION);
dependencies["fyroxed_base"] = value(&*CURRENT_VERSION);
if dependencies.contains_key("fyrox_scripts") {
dependencies["fyrox_scripts"] = value(CURRENT_VERSION);
dependencies["fyrox_scripts"] = value(&*CURRENT_VERSION);
}
}
} else if version == "nightly" {