fyrox_impl/scene/dim2/
rectangle.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Rectangle is the simplest "2D" node, it can be used to create "2D" graphics. 2D is in quotes
22//! here because the node is actually a 3D node, like everything else in the engine.
23//!
24//! See [`Rectangle`] docs for more info.
25
26use crate::scene::node::constructor::NodeConstructor;
27use crate::{
28    core::{
29        algebra::{Point3, Vector2, Vector3},
30        color::Color,
31        math::{aabb::AxisAlignedBoundingBox, Rect, TriangleDefinition},
32        pool::Handle,
33        reflect::prelude::*,
34        type_traits::prelude::*,
35        uuid::{uuid, Uuid},
36        variable::InheritableVariable,
37        visitor::prelude::*,
38    },
39    material::{self, Material, MaterialResource},
40    renderer::{self, bundle::RenderContext},
41    scene::{
42        base::{Base, BaseBuilder},
43        graph::Graph,
44        mesh::buffer::{
45            VertexAttributeDataType, VertexAttributeDescriptor, VertexAttributeUsage, VertexTrait,
46        },
47        mesh::RenderPath,
48        node::{Node, NodeTrait, RdcControlFlow},
49    },
50};
51use bytemuck::{Pod, Zeroable};
52use fyrox_core::value_as_u8_slice;
53use fyrox_graph::constructor::ConstructorProvider;
54use fyrox_graph::BaseSceneGraph;
55use std::{
56    hash::{Hash, Hasher},
57    ops::{Deref, DerefMut},
58};
59
60/// A vertex for static meshes.
61#[derive(Copy, Clone, Debug, Default, Pod, Zeroable)]
62#[repr(C)] // OpenGL expects this structure packed as in C
63pub struct RectangleVertex {
64    /// Position of vertex in local coordinates.
65    pub position: Vector3<f32>,
66    /// Texture coordinates.
67    pub tex_coord: Vector2<f32>,
68    /// Diffuse color.
69    pub color: Color,
70}
71
72impl VertexTrait for RectangleVertex {
73    fn layout() -> &'static [VertexAttributeDescriptor] {
74        &[
75            VertexAttributeDescriptor {
76                usage: VertexAttributeUsage::Position,
77                data_type: VertexAttributeDataType::F32,
78                size: 3,
79                divisor: 0,
80                shader_location: 0,
81                normalized: false,
82            },
83            VertexAttributeDescriptor {
84                usage: VertexAttributeUsage::TexCoord0,
85                data_type: VertexAttributeDataType::F32,
86                size: 2,
87                divisor: 0,
88                shader_location: 1,
89                normalized: false,
90            },
91            VertexAttributeDescriptor {
92                usage: VertexAttributeUsage::Color,
93                data_type: VertexAttributeDataType::U8,
94                size: 4,
95                divisor: 0,
96                shader_location: 2,
97                normalized: true,
98            },
99        ]
100    }
101}
102
103impl PartialEq for RectangleVertex {
104    fn eq(&self, other: &Self) -> bool {
105        self.position == other.position
106            && self.tex_coord == other.tex_coord
107            && self.color == other.color
108    }
109}
110
111// This is safe because Vertex is tightly packed struct with C representation
112// there is no padding bytes which may contain garbage data. This is strictly
113// required because vertices will be directly passed on GPU.
114impl Hash for RectangleVertex {
115    fn hash<H: Hasher>(&self, state: &mut H) {
116        #[allow(unsafe_code)]
117        unsafe {
118            let bytes = self as *const Self as *const u8;
119            state.write(std::slice::from_raw_parts(
120                bytes,
121                std::mem::size_of::<Self>(),
122            ))
123        }
124    }
125}
126
127/// Rectangle is the simplest "2D" node, it can be used to create "2D" graphics. 2D is in quotes
128/// here because the node is actually a 3D node, like everything else in the engine.
129///
130/// ## Material
131///
132/// Rectangles could use an arbitrary material for rendering, which means that you have full control
133/// on how the rectangle will be rendered on screen.
134///
135/// By default, the rectangle uses standard 2D material which has only one property - `diffuseTexture`.
136/// You could use it to set a texture for your rectangle:
137///
138/// ```rust
139/// # use fyrox_impl::{
140/// #     core::sstorage::ImmutableString,
141/// #     material::{shader::SamplerFallback, MaterialProperty},
142/// #     resource::texture::TextureResource,
143/// #     scene::dim2::rectangle::Rectangle,
144/// # };
145/// #
146/// fn set_texture(rect: &mut Rectangle, texture: Option<TextureResource>) {
147///     rect.material()
148///         .data_ref()
149///         .bind("diffuseTexture", texture);
150/// }
151/// ```
152///
153/// The same property could also be changed in the editor using the Material Editor invoked from
154/// the `Material` property in the Inspector.
155///
156/// ## Performance
157///
158/// Rectangles use batching to let you draw tons of rectangles with high performance.
159///
160/// ## Specifying region for rendering
161///
162/// You can specify a portion of the texture that will be used for rendering using [`Self::set_uv_rect`]
163/// method. This is especially useful if you need to create sprite sheet animation, you use the single
164/// image, but just changing portion for rendering. Keep in mind that the coordinates are normalized
165/// which means `[0; 0]` corresponds to top-left corner of the texture and `[1; 1]` corresponds to
166/// right-bottom corner.
167#[derive(Reflect, Debug, Clone, ComponentProvider)]
168pub struct Rectangle {
169    base: Base,
170
171    #[reflect(setter = "set_color")]
172    color: InheritableVariable<Color>,
173
174    #[reflect(setter = "set_uv_rect")]
175    uv_rect: InheritableVariable<Rect<f32>>,
176
177    material: InheritableVariable<MaterialResource>,
178}
179
180impl Visit for Rectangle {
181    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
182        let mut region = visitor.enter_region(name)?;
183
184        if region.is_reading() {
185            if let Some(material) =
186                material::visit_old_texture_as_material(&mut region, Material::standard_2d)
187            {
188                self.material = material.into();
189            } else {
190                self.material.visit("Material", &mut region)?;
191            }
192        } else {
193            self.material.visit("Material", &mut region)?;
194        }
195
196        self.base.visit("Base", &mut region)?;
197        self.color.visit("Color", &mut region)?;
198        let _ = self.uv_rect.visit("UvRect", &mut region);
199
200        Ok(())
201    }
202}
203
204impl Default for Rectangle {
205    fn default() -> Self {
206        Self {
207            base: Default::default(),
208            color: Default::default(),
209            uv_rect: InheritableVariable::new_modified(Rect::new(0.0, 0.0, 1.0, 1.0)),
210            material: InheritableVariable::new_modified(MaterialResource::new_ok(
211                Default::default(),
212                Material::standard_2d(),
213            )),
214        }
215    }
216}
217
218impl Deref for Rectangle {
219    type Target = Base;
220
221    fn deref(&self) -> &Self::Target {
222        &self.base
223    }
224}
225
226impl DerefMut for Rectangle {
227    fn deref_mut(&mut self) -> &mut Self::Target {
228        &mut self.base
229    }
230}
231
232impl TypeUuidProvider for Rectangle {
233    fn type_uuid() -> Uuid {
234        uuid!("bb57b5e0-367a-4490-bf30-7f547407d5b5")
235    }
236}
237
238impl Rectangle {
239    /// Returns current color of the rectangle.
240    pub fn color(&self) -> Color {
241        *self.color
242    }
243
244    /// Returns a reference to the current material used by the rectangle.
245    pub fn material(&self) -> &InheritableVariable<MaterialResource> {
246        &self.material
247    }
248
249    /// Returns a reference to the current material used by the rectangle.
250    pub fn material_mut(&mut self) -> &mut InheritableVariable<MaterialResource> {
251        &mut self.material
252    }
253
254    /// Sets color of the rectangle.
255    pub fn set_color(&mut self, color: Color) -> Color {
256        self.color.set_value_and_mark_modified(color)
257    }
258
259    /// Returns a rectangle that defines the region in texture which will be rendered. The coordinates are normalized
260    /// which means `[0; 0]` corresponds to top-left corner of the texture and `[1; 1]` corresponds to right-bottom
261    /// corner.
262    pub fn uv_rect(&self) -> Rect<f32> {
263        *self.uv_rect
264    }
265
266    /// Sets a rectangle that defines the region in texture which will be rendered. The coordinates are normalized
267    /// which means `[0; 0]` corresponds to top-left corner of the texture and `[1; 1]` corresponds to right-bottom
268    /// corner.
269    ///
270    /// The coordinates can exceed `[1; 1]` boundary to create tiling effect (keep in mind that tiling should be
271    /// enabled in texture options).
272    ///
273    /// The default value is `(0, 0, 1, 1)` rectangle which corresponds to entire texture.
274    pub fn set_uv_rect(&mut self, uv_rect: Rect<f32>) -> Rect<f32> {
275        self.uv_rect.set_value_and_mark_modified(uv_rect)
276    }
277}
278
279impl ConstructorProvider<Node, Graph> for Rectangle {
280    fn constructor() -> NodeConstructor {
281        NodeConstructor::new::<Self>()
282            .with_variant("Rectangle (2D Sprite)", |_| {
283                RectangleBuilder::new(BaseBuilder::new().with_name("Sprite (2D)"))
284                    .build_node()
285                    .into()
286            })
287            .with_group("2D")
288    }
289}
290
291impl NodeTrait for Rectangle {
292    fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
293        AxisAlignedBoundingBox::unit()
294    }
295
296    fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
297        self.local_bounding_box()
298            .transform(&self.global_transform())
299    }
300
301    fn id(&self) -> Uuid {
302        Self::type_uuid()
303    }
304
305    fn collect_render_data(&self, ctx: &mut RenderContext) -> RdcControlFlow {
306        if !self.should_be_rendered(ctx.frustum) {
307            return RdcControlFlow::Continue;
308        }
309
310        if renderer::is_shadow_pass(ctx.render_pass_name) {
311            return RdcControlFlow::Continue;
312        }
313
314        let global_transform = self.global_transform();
315
316        type Vertex = RectangleVertex;
317
318        let vertices = [
319            Vertex {
320                position: global_transform
321                    .transform_point(&Point3::new(-0.5, 0.5, 0.0))
322                    .coords,
323                tex_coord: self.uv_rect.right_top_corner(),
324                color: *self.color,
325            },
326            Vertex {
327                position: global_transform
328                    .transform_point(&Point3::new(0.5, 0.5, 0.0))
329                    .coords,
330                tex_coord: self.uv_rect.left_top_corner(),
331                color: *self.color,
332            },
333            Vertex {
334                position: global_transform
335                    .transform_point(&Point3::new(0.5, -0.5, 0.0))
336                    .coords,
337                tex_coord: self.uv_rect.left_bottom_corner(),
338                color: *self.color,
339            },
340            Vertex {
341                position: global_transform
342                    .transform_point(&Point3::new(-0.5, -0.5, 0.0))
343                    .coords,
344                tex_coord: self.uv_rect.right_bottom_corner(),
345                color: *self.color,
346            },
347        ];
348
349        let triangles = [TriangleDefinition([0, 1, 2]), TriangleDefinition([2, 3, 0])];
350
351        let sort_index = ctx.calculate_sorting_index(self.global_position());
352
353        ctx.storage.push_triangles(
354            Vertex::layout(),
355            &self.material,
356            RenderPath::Forward,
357            sort_index,
358            self.handle(),
359            &mut move |mut vertex_buffer, mut triangle_buffer| {
360                let start_vertex_index = vertex_buffer.vertex_count();
361
362                for vertex in vertices.iter() {
363                    vertex_buffer
364                        .push_vertex_raw(value_as_u8_slice(vertex))
365                        .unwrap();
366                }
367
368                triangle_buffer
369                    .push_triangles_iter_with_offset(start_vertex_index, triangles.into_iter());
370            },
371        );
372
373        RdcControlFlow::Continue
374    }
375}
376
377/// Allows you to create rectangle in declarative manner.
378pub struct RectangleBuilder {
379    base_builder: BaseBuilder,
380    color: Color,
381    uv_rect: Rect<f32>,
382    material: MaterialResource,
383}
384
385impl RectangleBuilder {
386    /// Creates new rectangle builder.
387    pub fn new(base_builder: BaseBuilder) -> Self {
388        Self {
389            base_builder,
390            color: Color::WHITE,
391            uv_rect: Rect::new(0.0, 0.0, 1.0, 1.0),
392            material: MaterialResource::new_ok(Default::default(), Material::standard_2d()),
393        }
394    }
395
396    /// Sets desired color of the rectangle.
397    pub fn with_color(mut self, color: Color) -> Self {
398        self.color = color;
399        self
400    }
401
402    /// Sets desired portion of the texture for the rectangle. See [`Rectangle::set_uv_rect`]
403    /// for more info.
404    pub fn with_uv_rect(mut self, uv_rect: Rect<f32>) -> Self {
405        self.uv_rect = uv_rect;
406        self
407    }
408
409    /// Sets the desired material of the rectangle.
410    pub fn with_material(mut self, material: MaterialResource) -> Self {
411        self.material = material;
412        self
413    }
414
415    /// Creates new [`Rectangle`] instance.
416    pub fn build_rectangle(self) -> Rectangle {
417        Rectangle {
418            base: self.base_builder.build_base(),
419            color: self.color.into(),
420            uv_rect: self.uv_rect.into(),
421            material: self.material.into(),
422        }
423    }
424
425    /// Creates new [`Rectangle`] instance.
426    pub fn build_node(self) -> Node {
427        Node::new(self.build_rectangle())
428    }
429
430    /// Creates new [`Rectangle`] instance and adds it to the graph.
431    pub fn build(self, graph: &mut Graph) -> Handle<Node> {
432        graph.add_node(self.build_node())
433    }
434}