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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//! 3D scenes for fframes, with an API after three.js. fframes re-exports this crate as
//! `fframes::three_d` with its `3d` feature: `fframes = { features = ["3d", "wgpu"] }`.
//!
//! A [`Scene`] holds [`Node`]s in a hierarchy: meshes, lights, particles and groups. A
//! [`Camera`] looks at the scene. A [`Renderer`] makes an image of both for an `<image>`
//! element in the `svgr!` tree of a frame. Regular SVG can go below and above it.
//!
//! The renderer has 4x multisampling and physically based materials: metal and roughness,
//! clearcoat and specular antialiasing. A procedural studio or an HDR panorama lights the
//! scene. The renderer draws soft shadows, planar reflections ([`Reflector`]), transparency,
//! additive glows and particles. It also has fog, depth of field, motion blur, bloom and
//! ACES tone mapping.
//!
//! The default renderer is a multithreaded software rasterizer. It runs on all backends:
//! CPU, Skia and the wasm editor. The `wgpu` feature of fframes (the `gpu` feature of this
//! crate) renders with wgpu on Vulkan, Metal or DX12, see [`Renderer::gpu`]. The two paths
//! use the same shading. On average, a GPU frame differs from the CPU frame by less than
//! one 8-bit step. Without a usable GPU, the CPU renders.
//!
//! A backend can share its GPU, see `fframes::shared_gpu`. The Skia Vulkan backend does
//! this with its `wgpu` feature. [`Renderer::render`] then renders on that device into a
//! texture that Skia draws. The 3D image stays on the GPU until the encoder.
//!
//! A texture can show fframes content: [`Texture::from_image`] of
//! `ctx.render_svgr(svgr, width, height)` puts an svgr tree on a surface, and a 3D render is
//! an image for the next svgr tree. So svgr and 3D nest to any depth. On the shared GPU the
//! svgr image stays a texture too, and the GPU makes its mipmap levels.
//!
//! Units are meters and radians. Colors as hex numbers or `&str` are sRGB.
//!
//! # Example
//!
//! ```rust,ignore
//! use fframes::three_d::*;
//! use fframes::{FFramesContext, Frame, Svgr, Video, svgr};
//!
//! struct Spin {
//! scene: Scene,
//! cube: NodeId,
//! renderer: Renderer,
//! }
//!
//! impl Spin {
//! fn new() -> Self {
//! let mut scene = Scene::new().environment(Environment::studio());
//! let cube = scene.add(Node::mesh(
//! Geometry::rounded_box(1.0, 1.0, 1.0, 0.08, 4),
//! Material::metal(0xd4d8e0, 0.25),
//! ).at(0.0, 0.5, 0.0));
//! scene.add(Node::mesh(Geometry::floor(20.0, 20.0), Material::shadow_catcher(0.6)));
//! scene.add(Node::light(Light::sun(Rgb::WHITE, 3.0, vec3(2.0, 5.0, 3.0))));
//! Self { scene, cube, renderer: Renderer::new(1920, 1080).bloom(Bloom::default()) }
//! }
//! }
//!
//! // in render_frame
//! let t = frame.seconds();
//! let mut scene = self.scene.clone();
//! scene[self.cube].rotation = euler(0.0, t, 0.0);
//! let camera = Camera::perspective(35.0).orbit(vec3(0.0, 0.5, 0.0), 5.0, 30.0, 15.0);
//! let image = self.renderer.render(&scene, &camera);
//! svgr!(<svg ...><image href={image.href()} x="0" y="0" width="1920" height="1080" /></svg>)
//! ```
//!
//! # Coming from three.js
//!
//! | three.js | `fframes_three_d` |
//! | --- | --- |
//! | `new Mesh(new BoxGeometry(1, 2, 3), new MeshStandardMaterial({ color: 0xff0000 }))` | `Node::mesh(Geometry::cuboid(1.0, 2.0, 3.0), Material::standard(0xff0000))` |
//! | `mesh.position.set(x, y, z)`, `mesh.rotation.set(x, y, z)` | `.at(x, y, z)`, `.euler(x, y, z)` or `scene[id].position = ...` |
//! | `new Group()`, `group.add(mesh)` | [`Node::group`], `scene.add_to(group, node)` |
//! | `metalness`, `roughness`, `emissive`, `emissiveIntensity`, `clearcoat`, `map`, `normalMap`, `normalScale`, `aoMap` | the same names on [`Material`] |
//! | `MeshBasicMaterial`, `ShadowMaterial` | [`Material::unlit`], [`Material::shadow_catcher`] |
//! | `AmbientLight`, `HemisphereLight`, `DirectionalLight`, `PointLight`, `SpotLight` | [`Light`] |
//! | `light.castShadow = true` | [`Light::sun`] or [`Light::with_shadow`] |
//! | `scene.environment = pmrem(RoomEnvironment)` | [`Environment::studio`] |
//! | `RGBELoader` panorama | [`Environment::map`] with [`EnvironmentMap::decode`] |
//! | `scene.fog = new Fog(color, near, far)` | [`Scene::fog`] with [`Fog::Linear`] |
//! | `Reflector` | [`Scene::reflector`] with [`Material::reflection`] |
//! | `camera.position.set(..); camera.lookAt(..)` | `Camera::perspective(fov).at(..).look_at(..)` |
//! | `vector.project(camera)` | [`Camera::project`] |
//! | `GLTFLoader` + `AnimationMixer` | [`Model::from_gltf`] + [`ModelInstance::play`] |
//! | `STLLoader`, `toCreasedNormals` | [`Geometry::from_stl`], [`Geometry::smooth`] |
//! | `VideoTexture` | [`Texture::from_image`] of a decoded video frame, for each frame |
//! | `CanvasTexture` | [`Texture::from_image`] of `ctx.render_svgr(svgr, width, height)` |
//! | `UnrealBloomPass`, `ACESFilmicToneMapping` | [`Bloom`], [`ToneMapping`] |
//! | `Points` | [`Node::particles`] |
//! | `InstancedMesh`, `instanceColor` | [`Node::instanced`] with [`Instances`] |
// Colors are hex numbers as in three.js: `0xff8800`.
// A texture can hold a wgpu texture. Proving that a texture is `Sync` goes deep into the
// types of wgpu in release builds.
// Rasterization and mesh generation index several parallel arrays in one loop.
pub use ;
pub use ;
pub use Rgb;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use Texture;