From aa317efb5dbbd1990e40046c5fbc5da159b267ea Mon Sep 17 00:00:00 2001 From: Dmitry Stepanov Date: Wed, 17 Sep 2025 19:26:20 +0300 Subject: [PATCH] fixed sprite flipping issues + added ability to flip x/y - fixed incorrect sprite size if used arbitrary uv rect - #838 --- fyrox-impl/src/scene/sprite.rs | 102 ++++++++++++++---- .../shader/standard/standard_sprite.shader | 6 +- 2 files changed, 84 insertions(+), 24 deletions(-) diff --git a/fyrox-impl/src/scene/sprite.rs b/fyrox-impl/src/scene/sprite.rs index 51f273d0a..b925502ec 100644 --- a/fyrox-impl/src/scene/sprite.rs +++ b/fyrox-impl/src/scene/sprite.rs @@ -22,22 +22,21 @@ //! //! For more info see [`Sprite`]. -use crate::scene::node::constructor::NodeConstructor; -use crate::scene::node::RdcControlFlow; use crate::{ core::{ - algebra::{Vector2, Vector3}, + algebra::{Vector2, Vector3, Vector4}, color::Color, math::{aabb::AxisAlignedBoundingBox, Rect, TriangleDefinition}, pool::Handle, reflect::prelude::*, type_traits::prelude::*, uuid::{uuid, Uuid}, + value_as_u8_slice, variable::InheritableVariable, visitor::{Visit, VisitResult, Visitor}, }, - material, - material::{Material, MaterialResource}, + graph::{constructor::ConstructorProvider, BaseSceneGraph}, + material::{self, Material, MaterialResource}, renderer::{self, bundle::RenderContext}, scene::{ base::{Base, BaseBuilder}, @@ -49,13 +48,10 @@ use crate::{ }, RenderPath, }, - node::{Node, NodeTrait}, + node::{constructor::NodeConstructor, Node, NodeTrait, RdcControlFlow}, }, }; use bytemuck::{Pod, Zeroable}; -use fyrox_core::value_as_u8_slice; -use fyrox_graph::constructor::ConstructorProvider; -use fyrox_graph::BaseSceneGraph; use std::ops::{Deref, DerefMut}; /// A vertex for sprites. @@ -66,8 +62,8 @@ pub struct SpriteVertex { pub position: Vector3, /// Texture coordinates. pub tex_coord: Vector2, - /// Sprite parameters: x - size, y - rotation. - pub params: Vector2, + /// Sprite parameters: x - size, y - rotation, z - dx, w - dy. + pub params: Vector4, /// Diffuse color. pub color: Color, } @@ -94,7 +90,7 @@ impl VertexTrait for SpriteVertex { VertexAttributeDescriptor { usage: VertexAttributeUsage::Custom0, data_type: VertexAttributeDataType::F32, - size: 2, + size: 4, divisor: 0, shader_location: 2, normalized: false, @@ -175,6 +171,12 @@ pub struct Sprite { #[reflect(setter = "set_rotation")] rotation: InheritableVariable, + + #[reflect(setter = "set_flip_x")] + flip_x: InheritableVariable, + + #[reflect(setter = "set_flip_y")] + flip_y: InheritableVariable, } impl Visit for Sprite { @@ -199,6 +201,8 @@ impl Visit for Sprite { self.rotation.visit("Rotation", &mut region)?; // Backward compatibility. + let _ = self.flip_x.visit("FlipX", &mut region); + let _ = self.flip_y.visit("FlipY", &mut region); let _ = self.uv_rect.visit("UvRect", &mut region); Ok(()) @@ -293,6 +297,26 @@ impl Sprite { pub fn set_uv_rect(&mut self, uv_rect: Rect) -> Rect { self.uv_rect.set_value_and_mark_modified(uv_rect) } + + /// Enables (`true`) or disables (`false`) horizontal flipping of the sprite. + pub fn set_flip_x(&mut self, flip: bool) -> bool { + self.flip_x.set_value_and_mark_modified(flip) + } + + /// Returns `true` if the sprite is flipped horizontally, `false` - otherwise. + pub fn is_flip_x(&self) -> bool { + *self.flip_x + } + + /// Enables (`true`) or disables (`false`) vertical flipping of the sprite. + pub fn set_flip_y(&mut self, flip: bool) -> bool { + self.flip_y.set_value_and_mark_modified(flip) + } + + /// Returns `true` if the sprite is flipped vertically, `false` - otherwise. + pub fn is_flip_y(&self) -> bool { + *self.flip_y + } } impl ConstructorProvider for Sprite { @@ -328,38 +352,54 @@ impl NodeTrait for Sprite { } let position = self.global_position(); - let params = Vector2::new(*self.size, *self.rotation); type Vertex = SpriteVertex; + let lx = self.uv_rect.position.x; + let rx = self.uv_rect.position.x + self.uv_rect.size.x; + let ty = self.uv_rect.position.y; + let by = self.uv_rect.position.y + self.uv_rect.size.y; + let vertices = [ Vertex { position, - tex_coord: self.uv_rect.right_top_corner(), - params, + tex_coord: Vector2::new( + if *self.flip_x { lx } else { rx }, + if *self.flip_y { by } else { ty }, + ), + params: Vector4::new(*self.size, *self.rotation, 0.5, 0.5), color: *self.color, }, Vertex { position, - tex_coord: self.uv_rect.left_top_corner(), - params, + tex_coord: Vector2::new( + if *self.flip_x { rx } else { lx }, + if *self.flip_y { by } else { ty }, + ), + params: Vector4::new(*self.size, *self.rotation, -0.5, 0.5), color: *self.color, }, Vertex { position, - tex_coord: self.uv_rect.left_bottom_corner(), - params, + tex_coord: Vector2::new( + if *self.flip_x { rx } else { lx }, + if *self.flip_y { ty } else { by }, + ), + params: Vector4::new(*self.size, *self.rotation, -0.5, -0.5), color: *self.color, }, Vertex { position, - tex_coord: self.uv_rect.right_bottom_corner(), - params, + tex_coord: Vector2::new( + if *self.flip_x { lx } else { rx }, + if *self.flip_y { ty } else { by }, + ), + params: Vector4::new(*self.size, *self.rotation, 0.5, -0.5), color: *self.color, }, ]; - let triangles = [TriangleDefinition([0, 1, 2]), TriangleDefinition([2, 3, 0])]; + let triangles = [TriangleDefinition([0, 1, 2]), TriangleDefinition([0, 2, 3])]; let sort_index = ctx.calculate_sorting_index(self.global_position()); @@ -397,6 +437,8 @@ pub struct SpriteBuilder { color: Color, size: f32, rotation: f32, + flip_x: bool, + flip_y: bool, } impl SpriteBuilder { @@ -413,6 +455,8 @@ impl SpriteBuilder { color: Color::WHITE, size: 0.2, rotation: 0.0, + flip_x: false, + flip_y: false, } } @@ -447,6 +491,18 @@ impl SpriteBuilder { self } + /// Flips the sprite horizontally. + pub fn with_flip_x(mut self, flip_x: bool) -> Self { + self.flip_x = flip_x; + self + } + + /// Flips the sprite vertically. + pub fn with_flip_y(mut self, flip_y: bool) -> Self { + self.flip_y = flip_y; + self + } + fn build_sprite(self) -> Sprite { Sprite { base: self.base_builder.build_base(), @@ -455,6 +511,8 @@ impl SpriteBuilder { color: self.color.into(), size: self.size.into(), rotation: self.rotation.into(), + flip_x: self.flip_x.into(), + flip_y: self.flip_y.into(), } } diff --git a/fyrox-material/src/shader/standard/standard_sprite.shader b/fyrox-material/src/shader/standard/standard_sprite.shader index ea8aa38ec..428a5e598 100644 --- a/fyrox-material/src/shader/standard/standard_sprite.shader +++ b/fyrox-material/src/shader/standard/standard_sprite.shader @@ -63,7 +63,7 @@ r#" layout(location = 0) in vec3 vertexPosition; layout(location = 1) in vec2 vertexTexCoord; - layout(location = 2) in vec2 vertexParams; + layout(location = 2) in vec4 vertexParams; layout(location = 3) in vec4 vertexColor; out vec2 texCoord; @@ -73,10 +73,12 @@ { float size = vertexParams.x; float rotation = vertexParams.y; + float dx = vertexParams.z; + float dy = vertexParams.w; texCoord = vertexTexCoord; color = vertexColor; - vec2 vertexOffset = S_RotateVec2(vertexTexCoord * 2.0 - 1.0, rotation); + vec2 vertexOffset = S_RotateVec2(vec2(dx, dy), rotation); vec4 worldPosition = fyrox_instanceData.worldMatrix * vec4(vertexPosition, 1.0); vec3 offset = (vertexOffset.x * fyrox_cameraData.sideVector + vertexOffset.y * fyrox_cameraData.upVector) * size; gl_Position = fyrox_cameraData.viewProjectionMatrix * (worldPosition + vec4(offset.x, offset.y, offset.z, 0.0));