use crate::scene::animation::spritesheet::SpriteSheetAnimation;
use crate::{
core::{
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},
},
graph::{constructor::ConstructorProvider, SceneGraph},
material::{Material, MaterialResource},
renderer::{self, bundle::RenderContext},
scene::{
base::{Base, BaseBuilder},
graph::Graph,
mesh::{
buffer::{
VertexAttributeDataType, VertexAttributeDescriptor, VertexAttributeUsage,
VertexTrait,
},
RenderPath,
},
node::{constructor::NodeConstructor, Node, NodeTrait, RdcControlFlow},
},
};
use bytemuck::{Pod, Zeroable};
use std::ops::{Deref, DerefMut};
#[derive(Copy, Clone, Debug, Default, Pod, Zeroable)]
#[repr(C)] pub struct SpriteVertex {
pub position: Vector3<f32>,
pub tex_coord: Vector2<f32>,
pub params: Vector4<f32>,
pub color: Color,
}
impl VertexTrait for SpriteVertex {
fn layout() -> &'static [VertexAttributeDescriptor] {
&[
VertexAttributeDescriptor {
usage: VertexAttributeUsage::Position,
data_type: VertexAttributeDataType::F32,
size: 3,
divisor: 0,
shader_location: 0,
normalized: false,
},
VertexAttributeDescriptor {
usage: VertexAttributeUsage::TexCoord0,
data_type: VertexAttributeDataType::F32,
size: 2,
divisor: 0,
shader_location: 1,
normalized: false,
},
VertexAttributeDescriptor {
usage: VertexAttributeUsage::Custom0,
data_type: VertexAttributeDataType::F32,
size: 4,
divisor: 0,
shader_location: 2,
normalized: false,
},
VertexAttributeDescriptor {
usage: VertexAttributeUsage::Color,
data_type: VertexAttributeDataType::U8,
size: 4,
divisor: 0,
shader_location: 3,
normalized: true,
},
]
}
}
#[derive(Debug, Reflect, Clone, ComponentProvider, Visit)]
#[reflect(derived_type = "Node")]
pub struct Sprite {
base: Base,
#[reflect(setter = "set_uv_rect")]
uv_rect: InheritableVariable<Rect<f32>>,
material: InheritableVariable<MaterialResource>,
#[reflect(setter = "set_color")]
color: InheritableVariable<Color>,
#[reflect(min_value = 0.0, step = 0.1)]
#[reflect(setter = "set_size")]
size: InheritableVariable<f32>,
#[reflect(setter = "set_rotation")]
rotation: InheritableVariable<f32>,
#[reflect(setter = "set_flip_x")]
flip_x: InheritableVariable<bool>,
#[reflect(setter = "set_flip_y")]
flip_y: InheritableVariable<bool>,
}
impl Deref for Sprite {
type Target = Base;
fn deref(&self) -> &Self::Target {
&self.base
}
}
impl DerefMut for Sprite {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.base
}
}
impl Default for Sprite {
fn default() -> Self {
SpriteBuilder::new(BaseBuilder::new()).build_sprite()
}
}
impl TypeUuidProvider for Sprite {
fn type_uuid() -> Uuid {
uuid!("60fd7e34-46c1-4ae9-8803-1f5f4c341518")
}
}
impl Sprite {
pub fn set_size(&mut self, size: f32) -> f32 {
self.size.set_value_and_mark_modified(size)
}
pub fn size(&self) -> f32 {
*self.size
}
pub fn set_color(&mut self, color: Color) -> Color {
self.color.set_value_and_mark_modified(color)
}
pub fn color(&self) -> Color {
*self.color
}
pub fn set_rotation(&mut self, rotation: f32) -> f32 {
self.rotation.set_value_and_mark_modified(rotation)
}
pub fn rotation(&self) -> f32 {
*self.rotation
}
pub fn material(&self) -> &InheritableVariable<MaterialResource> {
&self.material
}
pub fn material_mut(&mut self) -> &mut InheritableVariable<MaterialResource> {
&mut self.material
}
pub fn uv_rect(&self) -> Rect<f32> {
*self.uv_rect
}
pub fn set_uv_rect(&mut self, uv_rect: Rect<f32>) -> Rect<f32> {
self.uv_rect.set_value_and_mark_modified(uv_rect)
}
pub fn set_flip_x(&mut self, flip: bool) -> bool {
self.flip_x.set_value_and_mark_modified(flip)
}
pub fn is_flip_x(&self) -> bool {
*self.flip_x
}
pub fn set_flip_y(&mut self, flip: bool) -> bool {
self.flip_y.set_value_and_mark_modified(flip)
}
pub fn is_flip_y(&self) -> bool {
*self.flip_y
}
pub fn apply_animation(&mut self, animation: &SpriteSheetAnimation) {
self.material()
.data_ref()
.bind("diffuseTexture", animation.texture());
self.set_uv_rect(animation.current_frame_uv_rect().unwrap_or_default());
}
}
impl ConstructorProvider<Node, Graph> for Sprite {
fn constructor() -> NodeConstructor {
NodeConstructor::new::<Self>().with_variant("Sprite (3D)", |_| {
SpriteBuilder::new(BaseBuilder::new().with_name("Sprite"))
.build_node()
.into()
})
}
}
impl NodeTrait for Sprite {
fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
AxisAlignedBoundingBox::from_radius(*self.size)
}
fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
self.base.world_bounding_box()
}
fn id(&self) -> Uuid {
Self::type_uuid()
}
fn collect_render_data(&self, ctx: &mut RenderContext) -> RdcControlFlow {
if !self.should_be_rendered(ctx.frustum, ctx.render_mask) {
return RdcControlFlow::Continue;
}
if renderer::is_shadow_pass(ctx.render_pass_name) || !self.cast_shadows() {
return RdcControlFlow::Continue;
}
let position = self.global_position();
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: 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: 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: 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: 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([0, 2, 3])];
let sort_index = ctx.calculate_sorting_index(self.global_position());
ctx.storage.push_triangles(
ctx.dynamic_surface_cache,
Vertex::layout(),
&self.material,
RenderPath::Forward,
sort_index,
self.handle(),
&mut move |mut vertex_buffer, mut triangle_buffer| {
let start_vertex_index = vertex_buffer.vertex_count();
for vertex in vertices.iter() {
vertex_buffer
.push_vertex_raw(value_as_u8_slice(vertex))
.unwrap();
}
triangle_buffer
.push_triangles_iter_with_offset(start_vertex_index, triangles.into_iter());
},
);
RdcControlFlow::Continue
}
}
pub struct SpriteBuilder {
base_builder: BaseBuilder,
uv_rect: Rect<f32>,
material: MaterialResource,
color: Color,
size: f32,
rotation: f32,
flip_x: bool,
flip_y: bool,
}
impl SpriteBuilder {
pub fn new(base_builder: BaseBuilder) -> Self {
Self {
base_builder,
material: MaterialResource::new_ok(
Uuid::new_v4(),
Default::default(),
Material::standard_sprite(),
),
uv_rect: Rect::new(0.0, 0.0, 1.0, 1.0),
color: Color::WHITE,
size: 0.2,
rotation: 0.0,
flip_x: false,
flip_y: false,
}
}
pub fn with_uv_rect(mut self, uv_rect: Rect<f32>) -> Self {
self.uv_rect = uv_rect;
self
}
pub fn with_material(mut self, material: MaterialResource) -> Self {
self.material = material;
self
}
pub fn with_color(mut self, color: Color) -> Self {
self.color = color;
self
}
pub fn with_size(mut self, size: f32) -> Self {
self.size = size;
self
}
pub fn with_rotation(mut self, rotation: f32) -> Self {
self.rotation = rotation;
self
}
pub fn with_flip_x(mut self, flip_x: bool) -> Self {
self.flip_x = flip_x;
self
}
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(),
material: self.material.into(),
uv_rect: self.uv_rect.into(),
color: self.color.into(),
size: self.size.into(),
rotation: self.rotation.into(),
flip_x: self.flip_x.into(),
flip_y: self.flip_y.into(),
}
}
pub fn build_node(self) -> Node {
Node::new(self.build_sprite())
}
pub fn build(self, graph: &mut Graph) -> Handle<Sprite> {
graph.add_node(self.build_node()).to_variant()
}
}