1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
use std::marker::PhantomData;

use crate::{render::resource::TrackedRenderPass, tcs::world::World};

/// A draw function which is used to draw a specific [`PhaseItem`].
///
/// They are the the general form of drawing items, whereas [`RenderCommands`](RenderCommand)
/// are more modular.
pub trait Draw<P: PhaseItem>: 'static {
    /// Draws the [`PhaseItem`] by issuing draw calls via the [`TrackedRenderPass`].
    fn draw<'w>(&self, pass: &mut TrackedRenderPass<'w>, wold: &'w World, item: &P);
}

/// An item which will be drawn to the screen. A phase item should be queued up for rendering
/// during the [`RenderStageLabel::Queue`](crate::RenderStageLabel::Queue) stage.
/// Afterwards it will be sorted and rendered automatically  in the
/// [`RenderStageLabel::PhaseSort`](crate::RenderStageLabel::PhaseSort) stage and
/// [`RenderStageLabel::Render`](crate::RenderStageLabel::Render) stage, respectively.
pub trait PhaseItem {
    /// The type used for ordering the items. The smallest values are drawn first.
    type SortKey: Ord;
    /// Determines the order in which the items are drawn during the corresponding [`RenderPhase`](super::RenderPhase).
    fn sort_key(&self) -> Self::SortKey;

    fn draw_function(&self) -> &dyn Draw<Self>;
}

/// [`RenderCommand`] is a trait that runs an ECS query and produces one or more
/// [`TrackedRenderPass`] calls. Types implementing this trait can be composed (as tuples).
///
/// They can be registered as a [`Draw`] function via the
/// [`AddRenderCommand::add_render_command`] method.
///
/// # Example
/// The `DrawPbr` draw function is created from the following render command
/// tuple.  Const generics are used to set specific bind group locations:
///
/// ```ignore
/// pub type DrawPbr = (
///     SetItemPipeline,
///     SetMeshViewBindGroup<0>,
///     SetStandardMaterialBindGroup<1>,
///     SetTransformBindGroup<2>,
///     DrawMesh,
/// );
/// ```
pub trait RenderCommand<P: PhaseItem> {
    /// Renders the [`PhaseItem`] by issuing draw calls via the [`TrackedRenderPass`].
    // TODO: reorder the arguments to match Node and Draw
    fn render<'w>(
        world: &'w World,
        item: &P,
        pass: &mut TrackedRenderPass<'w>,
    ) -> RenderCommandResult;
}

pub enum RenderCommandResult {
    Success,
    Failure,
}

macro_rules! render_command_tuple_impl {
    ($($name: ident),*) => {
        impl<P: PhaseItem, $($name: RenderCommand<P>),*> RenderCommand<P> for ($($name,)*) {
            #[allow(non_snake_case)]
            fn render<'w>(
                world: &'w World,
                item: &P,
                pass: &mut TrackedRenderPass<'w>,
            ) -> RenderCommandResult{
                $(if let RenderCommandResult::Failure = $name::render(world, item, pass) {
                    return RenderCommandResult::Failure;
                })*
                RenderCommandResult::Success
            }
        }
    };
}

render_command_tuple_impl!(C0);
render_command_tuple_impl!(C0, C1);
render_command_tuple_impl!(C0, C1, C2);
render_command_tuple_impl!(C0, C1, C2, C3);
render_command_tuple_impl!(C0, C1, C2, C3, C4);

pub struct DrawState<C, P> {
    phantom_p: PhantomData<P>,
    phantom_c: PhantomData<C>,
}

impl<C, P> DrawState<C, P> {
    pub(crate) fn new() -> Self {
        DrawState {
            phantom_p: Default::default(),
            phantom_c: Default::default(),
        }
    }
}

impl<P: 'static, C: 'static> Draw<P> for DrawState<P, C>
where
    P: PhaseItem,
    C: RenderCommand<P>,
{
    /// Prepares data for the wrapped [`RenderCommand`] and then renders it.
    fn draw<'w>(&self, pass: &mut TrackedRenderPass<'w>, world: &'w World, item: &P) {
        C::render(world, item, pass);
    }
}