wrend/
lib.rs

1#![warn(missing_docs)]
2
3//! Wrend is a ***W***ebGL2 ***Rend***ering library for making Rust/JavaScript + WebGL development easier and safer.
4//!
5//! Wrend provides an organized system for building custom rendering pipelines: you tell Wrend *how* all of your graphical resources
6//! relate to one another through user-specified ids and callbacks, and it does all the work of actually putting things together.
7//! It also comes with some out-of-the-box nice-to-have abstractions like recording canvas output and animation frame scheduling,
8//! as well as automatic clean-up.
9//!
10//! Though most of the demo app examples are built using Yew, `wrend` itself is framework agnostic  and is designed
11//! to be used in a variety of settings with diverse rendering pipelines, including contexts like React and
12//! raw HTML & JavaScript.
13//!
14//! ## Overview
15//!
16//! ### Links
17//!
18//! The fundamental organizing components of Wrend are `links`, such as [`ProgramLink`], [`AttributeLink`],
19//! and [`UniformLink`],  which get appended to a [RendererDataBuilder]. These links tell `wrend` how your data
20//! should be created and how each resource relates to all the other resources in your pipeline.
21//!
22//! ### Callbacks
23//!
24//! Many links accept some sort of callback, which is used to create a particular resource in your  build pipeline.
25//!
26//! For example, [`BufferLink`] accepts a [`BufferCreateCallback`], which is called during the build process to
27//! acquire a [`web_sys::WebGlBuffer`]. In this callback, you are free to initialize your [`web_sys::WebGlBuffer`]
28//! however you like.
29//!
30//! ### Ids
31//!
32//! Most resources such as shaders, [`Uniform`]s and [`Attribute`]s retrieve resources using unique [`Id`]s, which can be
33//! any data type that implements the [`Id`] trait. These Ids help Wrend understand how your data fits together.
34//!
35//! For example, you can load shaders into the build pipeline using [`RendererDataBuilder::add_vertex_shader_src`].
36//! Then, when creating a [`ProgramLink`], you can refer to that shader using its `VertexShaderId` to link that shader
37//! to any number of programs you create.
38//!
39//! ### Build
40//!
41//! Once all resources and `links` have been added to the [RendererDataBuilder], the pipeline can be built
42//! on using [RendererDataBuilder::build_renderer].
43//!
44//! # Panics
45//!
46//! There are very few locations in which Rust code can panic in `wrend`, and those that exist are being slimmed down.
47//!
48//! The primary locations that *can* are where JavaScript types such as arrays are passed into Rust--because these types must be converted
49//! to WebAssembly, there is currently the chance for panic if the wrong type is supplied. If using TypeScript, you should see TypeScript errors
50//! for any incorrect types supplied, as the library as a whole is strongly typed.
51//!
52//! A long term goal of `wrend` is to provide matchable errors (or `catch`able errors in JavaScript) for all fallible operations.
53//!
54//! # Example
55//!
56//! The following is a "Hello, triangle!" example (the equivalent of "Hello, world!" for WebGL)
57//!
58//! ```no_run
59//! use js_sys::Float32Array;
60//! use wasm_bindgen::{prelude::*, JsCast};
61//! use web_sys::{window, HtmlCanvasElement, WebGl2RenderingContext};
62//! use wrend::{
63//!     AttributeCreateContext, AttributeLink, BufferCreateContext, BufferLink, Id, IdDefault, IdName,
64//!     ProgramLink, Renderer, RendererData,
65//! };
66//!
67//! #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default)]
68//! pub struct ProgramId;
69//!
70//! impl Id for ProgramId {}
71//!
72//! #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default)]
73//! pub struct VaoId;
74//!
75//! impl Id for VaoId {}
76//!
77//! #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
78//! pub enum BufferId {
79//!     VertexBuffer,
80//! }
81//!
82//! impl Id for BufferId {}
83//!
84//! impl Default for BufferId {
85//!     fn default() -> Self {
86//!         Self::VertexBuffer
87//!     }
88//! }
89//!
90//! impl IdName for BufferId {
91//!     fn name(&self) -> String {
92//!         match self {
93//!             BufferId::VertexBuffer => "a_position".to_string(),
94//!         }
95//!     }
96//! }
97//!
98//! #[derive(Clone, Default, Copy, PartialEq, Eq, Hash, Debug)]
99//! pub struct VertexShaderId;
100//!
101//! impl Id for VertexShaderId {}
102//!
103//! #[derive(Clone, Default, Copy, PartialEq, Eq, Hash, Debug)]
104//! pub struct FragmentShaderId;
105//!
106//! impl Id for FragmentShaderId {}
107//!
108//! #[derive(Clone, Default, Copy, PartialEq, Eq, Hash, Debug)]
109//! pub struct PositionAttributeId;
110//!
111//! impl Id for PositionAttributeId {}
112//!
113//! impl IdName for PositionAttributeId {
114//!     fn name(&self) -> String {
115//!         String::from("a_position")
116//!     }
117//! }
118//!
119//! #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
120//! struct AppState {
121//!     count: u32,
122//! }
123//!
124//! const VERTEX_SHADER: &str = r#"#version 300 es
125//! in vec2 a_position;
126//! out vec2 v_position;
127//!
128//! void main() {
129//!     gl_Position = vec4(a_position, 0, 1);
130//!     vec2 zero_to_two = a_position + 1.0;
131//!     vec2 zero_to_one = zero_to_two * 0.5;
132//!     v_position = zero_to_one;
133//! }"#;
134//!
135//!
136//! const FRAGMENT_SHADER: &str = r#"#version 300 es
137//! precision mediump float;
138//! in vec2 v_position;
139//! out vec4 out_color;
140//!
141//! void main() {
142//!   out_color = vec4(v_position.x, v_position.y, v_position.x * 0.5 + v_position.y * 0.5, 1);
143//! }"#;
144//!
145//! pub fn main() -> Result<(), JsValue> {
146//!     let canvas: HtmlCanvasElement = window()
147//!         .unwrap()
148//!         .document()
149//!         .unwrap()
150//!         .query_selector("canvas")
151//!         .unwrap()
152//!         .unwrap()
153//!         .dyn_into()
154//!         .unwrap();
155//!
156//!     let app_state = AppState::default();
157//!
158//!     let program_link = ProgramLink::new(ProgramId, VertexShaderId, FragmentShaderId);
159//!
160//!     let vertex_buffer_link =
161//!         BufferLink::new(BufferId::VertexBuffer, |ctx: &BufferCreateContext| {
162//!             let gl = ctx.gl();
163//!             let buffer = gl.create_buffer().unwrap();
164//!             gl.bind_buffer(WebGl2RenderingContext::ARRAY_BUFFER, Some(&buffer));
165//!             let vertex_array = unsafe { Float32Array::view(&[-0.0, 1.0, 1.0, -1.0, -1.0, -1.0]) };
166//!             gl.buffer_data_with_array_buffer_view(
167//!                 WebGl2RenderingContext::ARRAY_BUFFER,
168//!                 &vertex_array,
169//!                 WebGl2RenderingContext::STATIC_DRAW,
170//!             );
171//!
172//!             buffer
173//!         });
174//!
175//!     let a_position_link = AttributeLink::new(
176//!         VaoId,
177//!         BufferId::VertexBuffer,
178//!         PositionAttributeId,
179//!         |ctx: &AttributeCreateContext| {
180//!             let gl = ctx.gl();
181//!             let attribute_location = ctx.attribute_location();
182//!             let webgl_buffer = ctx.webgl_buffer();
183//!             gl.bind_buffer(WebGl2RenderingContext::ARRAY_BUFFER, Some(webgl_buffer));
184//!             gl.vertex_attrib_pointer_with_i32(
185//!                 attribute_location.into(),
186//!                 2,
187//!                 WebGl2RenderingContext::FLOAT,
188//!                 false,
189//!                 0,
190//!                 0,
191//!             );
192//!         },
193//!     );
194//!
195//!     let render_callback = |renderer_data: &RendererData<
196//!         VertexShaderId,
197//!         FragmentShaderId,
198//!         ProgramId,
199//!         IdDefault,
200//!         BufferId,
201//!         PositionAttributeId,
202//!         IdDefault,
203//!         IdDefault,
204//!         IdDefault,
205//!         VaoId,
206//!         AppState,
207//!     >| {
208//!         let gl = renderer_data.gl();
209//!         let canvas: HtmlCanvasElement = gl.canvas().unwrap().dyn_into().unwrap();
210//!
211//!         renderer_data.use_program(&ProgramId);
212//!         renderer_data.use_vao(&VaoId);
213//!
214//!         gl.viewport(0, 0, canvas.width() as i32, canvas.height() as i32);
215//!         gl.clear_color(0.0, 0.0, 0.0, 0.0);
216//!         gl.clear(WebGl2RenderingContext::COLOR_BUFFER_BIT);
217//!         gl.draw_arrays(WebGl2RenderingContext::TRIANGLES, 0, 3);
218//!     };
219//!
220//!     let mut render_builder = Renderer::builder();
221//!
222//!     render_builder
223//!         .set_canvas(canvas)
224//!         .set_user_ctx(app_state)
225//!         .add_vertex_shader_src(VertexShaderId, VERTEX_SHADER.to_string())
226//!         .add_fragment_shader_src(FragmentShaderId, FRAGMENT_SHADER.to_string())
227//!         .add_program_link(program_link)
228//!         .add_buffer_link(vertex_buffer_link)
229//!         .add_attribute_link(a_position_link)
230//!         .add_vao_link(VaoId)
231//!         .set_render_callback(render_callback);
232//!
233//!     let renderer = render_builder
234//!         .build_renderer()
235//!         .expect("Renderer should successfully build");
236//!
237//!     renderer.render();
238//!
239//!     Ok(())
240//! }
241//! ```
242//!
243//! # Demos
244//!
245//! todo
246//!
247//! ## Future Work
248//!
249//! Currently, wrend only supports build pipelines where all resources are initialized up front.
250//! That is, no *new* textures, buffers, uniforms can be added after the pipeline has been initialized.
251
252mod animation;
253mod attributes;
254mod buffers;
255mod callbacks;
256mod constants;
257mod framebuffers;
258mod ids;
259mod math;
260mod programs;
261mod recording;
262mod renderer_data;
263mod renderers;
264mod shaders;
265mod textures;
266mod transform_feedback;
267mod types;
268mod uniforms;
269mod utils;
270
271pub(crate) use recording::*;
272
273pub use animation::*;
274pub use attributes::*;
275pub use buffers::*;
276pub use callbacks::*;
277pub use constants::*;
278pub use framebuffers::*;
279pub use ids::*;
280pub use math::*;
281pub use programs::*;
282pub use renderer_data::*;
283pub use renderers::*;
284pub use shaders::*;
285pub use textures::*;
286pub use transform_feedback::*;
287pub use types::*;
288pub use uniforms::*;
289pub use utils::*;