baryon_core/
lib.rs

1#![allow(
2    // We use loops for getting early-out of scope without closures.
3    clippy::never_loop,
4    // We don't use syntax sugar where it's not necessary.
5    clippy::match_like_matches_macro,
6    // Redundant matching is more explicit.
7    clippy::redundant_pattern_matching,
8    // Explicit lifetimes are often easier to reason about.
9    clippy::needless_lifetimes,
10    // No need for defaults in the internal types.
11    clippy::new_without_default,
12    // For some reason `rustc` can warn about these in const generics even
13    // though they are required.
14    unused_braces,
15)]
16#![warn(
17    trivial_casts,
18    trivial_numeric_casts,
19    unused_extern_crates,
20    unused_qualifications,
21    // We don't match on a reference, unless required.
22    clippy::pattern_type_mismatch,
23)]
24
25mod color;
26mod mesh;
27mod space;
28
29use raw_window_handle::HasRawWindowHandle;
30use std::ops;
31
32pub use color::Color;
33pub use mesh::{IndexStream, Mesh, MeshBuilder, Prototype, Vertex, VertexStream};
34pub use space::{Camera, Projection, RawSpace};
35
36pub trait HasWindow: HasRawWindowHandle {
37    fn size(&self) -> mint::Vector2<u32>;
38}
39
40struct SurfaceContext {
41    raw: wgpu::Surface,
42    config: wgpu::SurfaceConfiguration,
43}
44
45pub struct Target {
46    pub view: wgpu::TextureView,
47    pub format: wgpu::TextureFormat,
48    pub size: wgpu::Extent3d,
49}
50
51impl Target {
52    pub fn aspect(&self) -> f32 {
53        self.size.width as f32 / self.size.height as f32
54    }
55}
56
57#[derive(Clone, Copy, Debug, PartialEq)]
58pub struct TargetRef(u8);
59
60pub struct Context {
61    #[allow(unused)]
62    instance: wgpu::Instance,
63    surface: Option<SurfaceContext>,
64    device: wgpu::Device,
65    queue: wgpu::Queue,
66    targets: Vec<Target>,
67    meshes: Vec<Mesh>,
68}
69
70#[derive(Default, Debug)]
71pub struct ContextBuilder {
72    power_preference: wgpu::PowerPreference,
73}
74
75impl ContextBuilder {
76    pub fn power_hungry(self, hungry: bool) -> Self {
77        Self {
78            power_preference: if hungry {
79                wgpu::PowerPreference::HighPerformance
80            } else {
81                wgpu::PowerPreference::LowPower
82            },
83            ..self
84        }
85    }
86
87    pub async fn build_offscreen(self) -> Context {
88        let instance = wgpu::Instance::new(wgpu::Backends::PRIMARY);
89        let adapter = instance
90            .request_adapter(&wgpu::RequestAdapterOptions {
91                power_preference: self.power_preference,
92                compatible_surface: None,
93            })
94            .await
95            .unwrap();
96
97        let (device, queue) = adapter
98            .request_device(&wgpu::DeviceDescriptor::default(), None)
99            .await
100            .unwrap();
101
102        Context {
103            instance,
104            surface: None,
105            device,
106            queue,
107            targets: Vec::new(),
108            meshes: Vec::new(),
109        }
110    }
111
112    pub async fn build<W: HasWindow>(self, window: &W) -> Context {
113        let instance = wgpu::Instance::new(wgpu::Backends::PRIMARY);
114
115        let size = window.size();
116        let mut surface = SurfaceContext {
117            raw: unsafe { instance.create_surface(window) },
118            config: wgpu::SurfaceConfiguration {
119                usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
120                // using an erroneous format, it is changed before used
121                format: wgpu::TextureFormat::Depth24Plus,
122                width: size.x,
123                height: size.y,
124                present_mode: wgpu::PresentMode::Mailbox,
125            },
126        };
127
128        let adapter = instance
129            .request_adapter(&wgpu::RequestAdapterOptions {
130                power_preference: self.power_preference,
131                compatible_surface: Some(&surface.raw),
132            })
133            .await
134            .unwrap();
135
136        let (device, queue) = adapter
137            .request_device(&wgpu::DeviceDescriptor::default(), None)
138            .await
139            .unwrap();
140
141        let format = surface.raw.get_preferred_format(&adapter).unwrap();
142        surface.config.format = format;
143        surface.raw.configure(&device, &surface.config);
144
145        Context {
146            instance,
147            surface: Some(surface),
148            device,
149            queue,
150            targets: Vec::new(),
151            meshes: Vec::new(),
152        }
153    }
154}
155
156impl Context {
157    pub fn init() -> ContextBuilder {
158        ContextBuilder::default()
159    }
160
161    pub fn resize(&mut self, width: u32, height: u32) {
162        let surface = match self.surface {
163            Some(ref mut suf) => suf,
164            None => return,
165        };
166        if (surface.config.width, surface.config.height) == (width, height) {
167            return;
168        }
169        surface.config.width = width;
170        surface.config.height = height;
171        surface.raw.configure(&self.device, &surface.config);
172    }
173
174    pub fn present<P: Pass>(&mut self, pass: &mut P, scene: &Scene, camera: &Camera) {
175        let surface = self.surface.as_mut().expect("No screen is configured!");
176        let frame = surface.raw.get_current_frame().unwrap();
177        let view = frame
178            .output
179            .texture
180            .create_view(&wgpu::TextureViewDescriptor::default());
181
182        let tr = TargetRef(self.targets.len() as _);
183        self.targets.push(Target {
184            view,
185            format: surface.config.format,
186            size: wgpu::Extent3d {
187                width: surface.config.width,
188                height: surface.config.height,
189                depth_or_array_layers: 1,
190            },
191        });
192
193        pass.draw(&[tr], scene, camera, self);
194
195        self.targets.pop();
196    }
197
198    pub fn add_mesh(&mut self) -> MeshBuilder {
199        MeshBuilder::new(self)
200    }
201}
202
203impl Drop for Context {
204    fn drop(&mut self) {
205        // Do we need explicit cleanup?
206    }
207}
208
209/// Trait that exposes `Context` details that depend on `wgpu`
210pub trait ContextDetail {
211    fn get_target(&self, tr: TargetRef) -> &Target;
212    fn get_mesh(&self, mr: MeshRef) -> &Mesh;
213    fn device(&self) -> &wgpu::Device;
214    fn queue(&self) -> &wgpu::Queue;
215}
216
217impl ContextDetail for Context {
218    fn get_target(&self, tr: TargetRef) -> &Target {
219        &self.targets[tr.0 as usize]
220    }
221    fn get_mesh(&self, mr: MeshRef) -> &Mesh {
222        &self.meshes[mr.0 as usize]
223    }
224    fn device(&self) -> &wgpu::Device {
225        &self.device
226    }
227    fn queue(&self) -> &wgpu::Queue {
228        &self.queue
229    }
230}
231
232pub trait Pass {
233    fn draw(&mut self, targets: &[TargetRef], scene: &Scene, camera: &Camera, context: &Context);
234}
235
236#[derive(Clone, Copy, Debug, Default, PartialEq)]
237pub struct NodeRef(u32);
238
239#[derive(Default, Debug, PartialEq)]
240pub struct Node {
241    parent: NodeRef,
242    local: space::Space,
243}
244
245pub type EntityRef = hecs::Entity;
246
247pub struct Scene {
248    pub world: hecs::World,
249    nodes: Vec<Node>,
250}
251
252impl ops::Index<NodeRef> for Scene {
253    type Output = Node;
254    fn index(&self, node: NodeRef) -> &Node {
255        &self.nodes[node.0 as usize]
256    }
257}
258
259impl ops::IndexMut<NodeRef> for Scene {
260    fn index_mut(&mut self, node: NodeRef) -> &mut Node {
261        &mut self.nodes[node.0 as usize]
262    }
263}
264
265pub struct BakedScene {
266    spaces: Box<[RawSpace]>,
267}
268
269impl ops::Index<NodeRef> for BakedScene {
270    type Output = RawSpace;
271    fn index(&self, node: NodeRef) -> &RawSpace {
272        &self.spaces[node.0 as usize]
273    }
274}
275
276pub struct EntityKind {
277    raw: hecs::EntityBuilder,
278    mesh: MeshRef,
279}
280
281impl Scene {
282    pub fn new() -> Self {
283        Self {
284            world: Default::default(),
285            nodes: vec![Node::default()],
286        }
287    }
288
289    fn add_node_impl(&mut self, node: Node) -> NodeRef {
290        let index = self.nodes.len();
291        self.nodes.push(node);
292        NodeRef(index as u32)
293    }
294
295    pub fn add_node(&mut self) -> ObjectBuilder<()> {
296        ObjectBuilder {
297            scene: self,
298            node: Node::default(),
299            kind: (),
300        }
301    }
302
303    pub fn add_entity(&mut self, prototype: &Prototype) -> ObjectBuilder<EntityKind> {
304        let mut raw = hecs::EntityBuilder::new();
305        raw.add_bundle(prototype);
306        ObjectBuilder {
307            scene: self,
308            node: Node::default(),
309            kind: EntityKind {
310                raw,
311                mesh: prototype.reference,
312            },
313        }
314    }
315
316    pub fn bake(&self) -> BakedScene {
317        let mut spaces: Vec<RawSpace> = Vec::with_capacity(self.nodes.len());
318        for n in self.nodes.iter() {
319            let space = if n.parent == NodeRef::default() {
320                n.local.clone()
321            } else {
322                let parent_space = spaces[n.parent.0 as usize].to_space();
323                parent_space.combine(&n.local)
324            };
325            spaces.push(space.into());
326        }
327        BakedScene {
328            spaces: spaces.into_boxed_slice(),
329        }
330    }
331}
332
333pub struct Entity {
334    pub node: NodeRef,
335    pub mesh: MeshRef,
336}
337
338pub struct ObjectBuilder<'a, T> {
339    scene: &'a mut Scene,
340    node: Node,
341    kind: T,
342}
343
344impl<T> ObjectBuilder<'_, T> {
345    pub fn parent(mut self, parent: NodeRef) -> Self {
346        self.node.parent = parent;
347        self
348    }
349}
350
351impl ObjectBuilder<'_, ()> {
352    pub fn build(self) -> NodeRef {
353        self.scene.add_node_impl(self.node)
354    }
355}
356
357impl ObjectBuilder<'_, EntityKind> {
358    /// Register a new material component with this entity.
359    ///
360    /// The following components are recognized by the library:
361    ///   - [`Color`]
362    pub fn component<T: hecs::Component>(mut self, component: T) -> Self {
363        self.kind.raw.add(component);
364        self
365    }
366
367    pub fn build(mut self) -> EntityRef {
368        let entity = Entity {
369            node: if self.node.local == space::Space::default() {
370                self.node.parent
371            } else {
372                self.scene.add_node_impl(self.node)
373            },
374            mesh: self.kind.mesh,
375        };
376        let built = self.kind.raw.add(entity).build();
377        self.scene.world.spawn(built)
378    }
379}
380
381#[derive(Clone, Copy, Debug, Default, PartialEq)]
382pub struct MeshRef(u32);