Nazariglez / notan

Cross-platform multimedia layer
https://nazariglez.github.io/notan-web/
Apache License 2.0
802 stars 55 forks source link

WIP: next #299

Open Nazariglez opened 10 months ago

Nazariglez commented 10 months ago

This is the next version of notan. We can consider it a rework, addressing some of the flaws, but also leaving behind some bad design decisions.

To merge this we need to reach a feature pair status, the API may change in some cases:

Then we can aim to get some extras, but they are not required:

Nazariglez commented 10 months ago

Each example must be made on its own commit, and that commit must be referenced on this PR. Allowing users to check the diff easily as a guide for migrating.


Edit: I am thinking that this can be tricky if it's an iterative change, because the API can evolve a bit while this PR lands, but I'll try to do my best.

Nazariglez commented 8 months ago

I added a checklist with things that must be done. Some of them are going to change quite a bit, so I am going to create separate PRs for those features or just leave comments to discuss the changes.

I am trying to keep the soul of the project without changes. However, some things must go, like graphics extensions, they are handy but too limiting (ie: we cannot use draw2d on EGUI because that design does not work with the borrow checker, etc...). This will make some things more verbose for the user, but not that much. We will see.

I want to do this in a way that works for me and for others as well, so I am not going to click the merge button until we're sure this is what we want to do with Notan.

Nazariglez commented 4 weeks ago

Quick update on this, I am experimenting almost full-time in another repo with a new API for Notan. I really like the API made here, I do think it's elegant and functional, however it has still some issues and is not very ergonomic when dealing with plugins (egui paint callback, etc...). I hope to have something working and translated this repo soon. A sneak peak (this can still change):

gfx_clear.rs

use rkit::gfx::{self, Color, Renderer};
use rkit::time;

fn main() {
    rkit::init().on_update(update).run().unwrap()
}

fn update() {
    let t = time::elapsed_f32();
    let color = Color::rgb(t.cos(), t.sin(), 1.0);

    let mut renderer = Renderer::new();
    renderer.begin_pass().clear_color(color);

    gfx::render_to_frame(&renderer).unwrap();
}

gfx_triangle.rs

use rkit::gfx::{self, Buffer, Color, RenderPipeline, Renderer, VertexFormat, VertexLayout};

// language=wgsl
const SHADER: &str = r#"
struct VertexInput {
    @location(0) position: vec2<f32>,
    @location(1) color: vec3<f32>,
};

struct VertexOutput {
    @builtin(position) position: vec4<f32>,
    @location(0) color: vec3<f32>,
};

@vertex
fn vs_main(
    model: VertexInput,
) -> VertexOutput {
    var out: VertexOutput;
    out.color = model.color;
    out.position = vec4<f32>(model.position - 0.5, 0.0, 1.0);
    return out;
}

@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
    return vec4<f32>(in.color, 1.0);
}
"#;

struct State {
    pip: RenderPipeline,
    vbo: Buffer,
}

impl State {
    fn new() -> Result<Self, String> {
        let pip = gfx::create_render_pipeline(SHADER)
            .with_vertex_layout(
                VertexLayout::new()
                    .with_attr(0, VertexFormat::Float32x2)
                    .with_attr(1, VertexFormat::Float32x3),
            )
            .build()?;

        #[rustfmt::skip]
        let vertices: &[f32] = &[
            0.5, 1.0,   1.0, 0.0, 0.0,
            0.0, 0.0,   0.0, 1.0, 0.0,
            1.0, 0.0,   0.0, 0.0, 1.0,
        ];

        let vbo = gfx::create_vertex_buffer(vertices).build()?;
        Ok(Self { pip, vbo })
    }
}

fn main() {
    rkit::init_with(|| State::new().unwrap())
        .on_update(update)
        .run()
        .unwrap()
}

fn update(s: &mut State) {
    let mut renderer = Renderer::new();
    renderer
        .begin_pass()
        .clear_color(Color::rgb(0.1, 0.2, 0.3))
        .pipeline(&s.pip)
        .buffers(&[&s.vbo])
        .draw(0..3);

    gfx::render_to_frame(&renderer).unwrap();
}
Nazariglez commented 2 weeks ago

Working on Draw2D API:

draw_triangle.rs

use rkit::draw::draw_2d;
use rkit::gfx::{self, Color};
use rkit::math::vec2;

fn main() -> Result<(), String> {
    rkit::init().on_update(update).run()
}

fn update(s: &mut ()) {
    let mut draw = draw_2d();
    draw.clear(Color::rgb(0.1, 0.2, 0.3));
    draw.triangle(vec2(400.0, 100.0), vec2(100.0, 500.0), vec2(700.0, 500.0));
    gfx::render_to_frame(&draw).unwrap();
}

draw_sprite.rs

use rkit::app::window_size;
use rkit::draw::{draw_2d, Sprite};
use rkit::gfx::{self, Color};

struct State {
    sprite: Sprite,
}

impl State {
    fn new() -> Result<Self, String> {
        let sprite = draw::create_sprite()
            .from_image(include_bytes!("assets/ferris.png"))
            .build()?;

        Ok(Self { sprite })
    }
}

fn main() -> Result<(), String> {
    rkit::init_with(|| State::new().unwrap())
        .on_update(update)
        .run()
}

fn update(s: &mut State) {
    let pos = window_size() * 0.5 - s.sprite.size() * 0.5;

    let mut draw = draw_2d();
    draw.clear(Color::rgb(0.1, 0.2, 0.3));
    draw.image(&s.sprite).position(pos);
    gfx::render_to_frame(&draw).unwrap();
}

I am trying to keep the API as similar as possible but improving (or trying) some parts.

Related to this, WGPU is a different beast, and I am not sure yet how to manage blending and masking in a easy way, so I am still experimenting a lot with this.