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::*;