1#![allow(
2 clippy::never_loop,
4 clippy::match_like_matches_macro,
6 clippy::redundant_pattern_matching,
8 clippy::needless_lifetimes,
10 clippy::new_without_default,
12 unused_braces,
15)]
16#![warn(
17 trivial_casts,
18 trivial_numeric_casts,
19 unused_extern_crates,
20 unused_qualifications,
21 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 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 }
207}
208
209pub 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 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);