1use cgmath::{Matrix4, SquareMatrix};
6use std::collections::HashMap;
7use stretch::{geometry::Size, node::Stretch};
8
9use crate::layer::EventHandler;
10use crate::layer::Key;
11use crate::layer::LayoutMode;
12use crate::layer::Layer;
13use crate::wgpu_context::WgpuContext;
14
15const SHADER_SOURCE: &str = r#"
17struct VertexInput {
18 @location(0) position: vec3<f32>,
19 @location(1) tex_coords: vec2<f32>,
20}
21
22struct VertexOutput {
23 @builtin(position) clip_position: vec4<f32>,
24 @location(0) tex_coords: vec2<f32>,
25}
26
27struct Uniforms {
28 transform: mat4x4<f32>,
29 projection: mat4x4<f32>,
30 color: vec4<f32>,
31 use_texture: u32,
32}
33
34@group(0) @binding(0)
35var<uniform> uniforms: Uniforms;
36
37@group(1) @binding(0)
38var t_texture: texture_2d<f32>;
39@group(1) @binding(1)
40var t_sampler: sampler;
41
42@vertex
43fn vs_main(vertex: VertexInput) -> VertexOutput {
44 var out: VertexOutput;
45 out.clip_position = uniforms.projection * uniforms.transform * vec4<f32>(vertex.position, 1.0);
46 out.tex_coords = vertex.tex_coords;
47 return out;
48}
49
50@fragment
51fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
52 if (uniforms.use_texture > 0u) {
53 return textureSample(t_texture, t_sampler, in.tex_coords);
54 } else {
55 return uniforms.color;
56 }
57}
58"#;
59
60pub fn render(name: String) {
61 println!("Render {}", name);
62}
63
64pub struct Play {
65 _name: String,
66 stage_list: Vec<Layer>,
67 stage_map: HashMap<String, usize>,
68 projection: Matrix4<f32>,
69 pub stretch: Option<Stretch>,
70 pub wgpu_context: Option<WgpuContext>,
71 render_pipeline: Option<wgpu::RenderPipeline>,
72 bind_group_layout: Option<wgpu::BindGroupLayout>,
73 texture_bind_group_layout: Option<wgpu::BindGroupLayout>,
74 default_texture: Option<wgpu::Texture>,
75 default_texture_view: Option<wgpu::TextureView>,
76 sampler: Option<wgpu::Sampler>,
77}
78
79impl Play {
80 pub fn new(
81 name: String,
82 viewport_width: i32,
83 viewport_height: i32,
84 layout_mode: LayoutMode,
85 ) -> Self {
86 let mut stretch = None;
87 match layout_mode {
88 LayoutMode::Flex => {
89 stretch = Some(Stretch::new());
90 }
91 LayoutMode::UserDefine => {
92 print!("UserDefine");
93 }
94 }
95
96 let mut play = Play {
97 _name: name,
98 stage_list: Vec::new(),
99 stage_map: HashMap::new(),
100 projection: Matrix4::identity(),
101 stretch: stretch,
102 wgpu_context: None,
103 render_pipeline: None,
104 bind_group_layout: None,
105 texture_bind_group_layout: None,
106 default_texture: None,
107 default_texture_view: None,
108 sampler: None,
109 };
110
111 let orth_matrix = cgmath::ortho(
113 0.0,
114 viewport_width as f32,
115 viewport_height as f32,
116 0.0,
117 1.0,
118 -1.0,
119 );
120 play.projection = orth_matrix;
121
122 play
123 }
124
125 pub fn init_wgpu(&mut self) {
126 self.wgpu_context = Some(pollster::block_on(WgpuContext::new_offscreen()));
128 }
129
130 pub fn init_wgpu_with_surface(
132 &mut self,
133 window: impl Into<wgpu::SurfaceTarget<'static>>,
134 width: u32,
135 height: u32,
136 ) {
137 self.wgpu_context = Some(pollster::block_on(WgpuContext::new_with_surface(
138 window, width, height,
139 )));
140
141 self.setup_render_pipeline();
143 self.create_default_texture();
144 }
145
146 fn setup_render_pipeline(&mut self) {
148 let Some(ref context) = self.wgpu_context else {
149 return;
150 };
151
152 let device = &context.device;
153
154 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
156 label: Some("Shader"),
157 source: wgpu::ShaderSource::Wgsl(SHADER_SOURCE.into()),
158 });
159
160 let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
162 label: Some("Uniform Bind Group Layout"),
163 entries: &[wgpu::BindGroupLayoutEntry {
164 binding: 0,
165 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
166 ty: wgpu::BindingType::Buffer {
167 ty: wgpu::BufferBindingType::Uniform,
168 has_dynamic_offset: false,
169 min_binding_size: None,
170 },
171 count: None,
172 }],
173 });
174
175 let texture_bind_group_layout =
177 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
178 label: Some("Texture Bind Group Layout"),
179 entries: &[
180 wgpu::BindGroupLayoutEntry {
181 binding: 0,
182 visibility: wgpu::ShaderStages::FRAGMENT,
183 ty: wgpu::BindingType::Texture {
184 multisampled: false,
185 view_dimension: wgpu::TextureViewDimension::D2,
186 sample_type: wgpu::TextureSampleType::Float { filterable: true },
187 },
188 count: None,
189 },
190 wgpu::BindGroupLayoutEntry {
191 binding: 1,
192 visibility: wgpu::ShaderStages::FRAGMENT,
193 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
194 count: None,
195 },
196 ],
197 });
198
199 let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
201 label: Some("Render Pipeline Layout"),
202 bind_group_layouts: &[&bind_group_layout, &texture_bind_group_layout],
203 push_constant_ranges: &[],
204 });
205
206 let surface_format = context
208 .surface_config
209 .as_ref()
210 .map(|c| c.format)
211 .unwrap_or(wgpu::TextureFormat::Bgra8UnormSrgb);
212
213 let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
215 label: Some("Render Pipeline"),
216 layout: Some(&render_pipeline_layout),
217 vertex: wgpu::VertexState {
218 module: &shader,
219 entry_point: "vs_main",
220 buffers: &[crate::layer::Vertex::desc()],
221 compilation_options: wgpu::PipelineCompilationOptions::default(),
222 },
223 fragment: Some(wgpu::FragmentState {
224 module: &shader,
225 entry_point: "fs_main",
226 targets: &[Some(wgpu::ColorTargetState {
227 format: surface_format,
228 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
229 write_mask: wgpu::ColorWrites::ALL,
230 })],
231 compilation_options: wgpu::PipelineCompilationOptions::default(),
232 }),
233 primitive: wgpu::PrimitiveState {
234 topology: wgpu::PrimitiveTopology::TriangleList,
235 strip_index_format: None,
236 front_face: wgpu::FrontFace::Ccw,
237 cull_mode: Some(wgpu::Face::Back),
238 polygon_mode: wgpu::PolygonMode::Fill,
239 unclipped_depth: false,
240 conservative: false,
241 },
242 depth_stencil: None,
243 multisample: wgpu::MultisampleState {
244 count: 1,
245 mask: !0,
246 alpha_to_coverage_enabled: false,
247 },
248 multiview: None,
249 cache: None,
250 });
251
252 self.render_pipeline = Some(render_pipeline);
253 self.bind_group_layout = Some(bind_group_layout);
254 self.texture_bind_group_layout = Some(texture_bind_group_layout);
255
256 self.sampler = Some(device.create_sampler(&wgpu::SamplerDescriptor {
258 address_mode_u: wgpu::AddressMode::ClampToEdge,
259 address_mode_v: wgpu::AddressMode::ClampToEdge,
260 address_mode_w: wgpu::AddressMode::ClampToEdge,
261 mag_filter: wgpu::FilterMode::Linear,
262 min_filter: wgpu::FilterMode::Linear,
263 mipmap_filter: wgpu::FilterMode::Nearest,
264 ..Default::default()
265 }));
266 }
267
268 fn create_default_texture(&mut self) {
270 let Some(ref context) = self.wgpu_context else {
271 return;
272 };
273
274 let device = &context.device;
275 let queue = &context.queue;
276
277 let texture_size = wgpu::Extent3d {
278 width: 1,
279 height: 1,
280 depth_or_array_layers: 1,
281 };
282
283 let texture = device.create_texture(&wgpu::TextureDescriptor {
284 label: Some("Default White Texture"),
285 size: texture_size,
286 mip_level_count: 1,
287 sample_count: 1,
288 dimension: wgpu::TextureDimension::D2,
289 format: wgpu::TextureFormat::Rgba8UnormSrgb,
290 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
291 view_formats: &[],
292 });
293
294 queue.write_texture(
296 wgpu::ImageCopyTexture {
297 texture: &texture,
298 mip_level: 0,
299 origin: wgpu::Origin3d::ZERO,
300 aspect: wgpu::TextureAspect::All,
301 },
302 &[255, 255, 255, 255],
303 wgpu::ImageDataLayout {
304 offset: 0,
305 bytes_per_row: Some(4),
306 rows_per_image: Some(1),
307 },
308 texture_size,
309 );
310
311 let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
312 self.default_texture = Some(texture);
313 self.default_texture_view = Some(texture_view);
314 }
315
316 pub fn resize(&mut self, width: u32, height: u32) {
318 if let Some(ref mut context) = self.wgpu_context {
319 context.resize(width, height);
320 }
321
322 let orth_matrix = cgmath::ortho(
324 0.0,
325 width as f32,
326 height as f32,
327 0.0,
328 1.0,
329 -1.0,
330 );
331 self.projection = orth_matrix;
332 }
333
334 fn render_layer(
336 layer: &mut Layer,
337 render_pass: &mut wgpu::RenderPass,
338 parent_transform: Option<&Matrix4<f32>>,
339 context: &WgpuContext,
340 projection: &Matrix4<f32>,
341 render_pipeline: &wgpu::RenderPipeline,
342 bind_group_layout: &wgpu::BindGroupLayout,
343 texture_bind_group_layout: &wgpu::BindGroupLayout,
344 sampler: &wgpu::Sampler,
345 default_texture_view: &wgpu::TextureView,
346 ) {
347 if !layer.visible {
348 return;
349 }
350
351 if layer.vertex_buffer.is_none() || layer.index_buffer.is_none() {
353 layer.init_buffers(&context.device);
354 }
355
356 if !layer.image_path.is_empty() && layer.texture.is_none() {
358 layer.load_image_texture(&context.device, &context.queue);
359 }
360
361 if layer.vertex_buffer.is_none() || layer.index_buffer.is_none() {
363 return;
364 }
365
366 let mut transform = layer.model_matrix();
368 if let Some(parent) = parent_transform {
369 transform = transform * parent;
370 }
371
372 let uniform_buffer = layer.create_uniform_buffer(&context.device, &transform, projection);
374
375 let uniform_bind_group = context.device.create_bind_group(&wgpu::BindGroupDescriptor {
377 label: Some("Uniform Bind Group"),
378 layout: bind_group_layout,
379 entries: &[wgpu::BindGroupEntry {
380 binding: 0,
381 resource: uniform_buffer.as_entire_binding(),
382 }],
383 });
384
385 let texture_bind_group = layer.get_or_create_bind_group(
387 &context.device,
388 texture_bind_group_layout,
389 sampler,
390 default_texture_view,
391 );
392
393 render_pass.set_pipeline(render_pipeline);
395 render_pass.set_bind_group(0, &uniform_bind_group, &[]);
396 render_pass.set_bind_group(1, texture_bind_group, &[]);
397 render_pass.set_vertex_buffer(0, layer.vertex_buffer.as_ref().unwrap().slice(..));
398 render_pass.set_index_buffer(
399 layer.index_buffer.as_ref().unwrap().slice(..),
400 wgpu::IndexFormat::Uint16,
401 );
402 render_pass.draw_indexed(0..6, 0, 0..1);
403
404 for (i, sub_layer) in layer.sub_layer_list.iter_mut().enumerate() {
406 if !sub_layer.focused && i != layer.focused_sub_layer {
407 Self::render_layer(
408 sub_layer,
409 render_pass,
410 Some(&transform),
411 context,
412 projection,
413 render_pipeline,
414 bind_group_layout,
415 texture_bind_group_layout,
416 sampler,
417 default_texture_view,
418 );
419 }
420 }
421
422 if !layer.sub_layer_list.is_empty() && layer.focused_sub_layer < layer.sub_layer_list.len() {
424 Self::render_layer(
425 &mut layer.sub_layer_list[layer.focused_sub_layer],
426 render_pass,
427 Some(&transform),
428 context,
429 projection,
430 render_pipeline,
431 bind_group_layout,
432 texture_bind_group_layout,
433 sampler,
434 default_texture_view,
435 );
436 }
437 }
438
439 pub fn new_layer(
440 name: String,
441 w: u32,
442 h: u32,
443 event_handler: Option<Box<dyn EventHandler>>,
444 ) -> Layer {
445 Layer::new(name, w, h, event_handler)
446 }
447
448 pub fn add_new_layer_to_stage(&mut self, stage_name: &String, layer: Layer) {
449 match self.stage_map.get(stage_name) {
450 Some(&index) => {
451 self.stage_list[index].add_sub_layer(layer);
452 }
453 _ => println!("Can't find the stage with the given name: {}", stage_name),
454 }
455 }
456
457 pub fn set_visible_stage(&mut self, name: &String, visible: bool) {
458 match self.stage_map.get(name) {
459 Some(&index) => {
460 self.stage_list[index].set_visible(visible);
461 self.stage_list[index].needs_update = true;
462 }
463 _ => println!("Can't find the stage with the given name: {}", name),
464 }
465 }
466
467 pub fn add_stage(&mut self, stage: Layer) -> String {
468 let stage_name = stage.name.to_string();
469 self.stage_list.push(stage);
470 self
471 .stage_map
472 .insert(stage_name.to_string(), self.stage_list.len() - 1);
473
474 stage_name
475 }
476
477 pub fn handle_input(&mut self, key: Key) {
478 for stage in self.stage_list.iter_mut() {
480 stage.handle_input(key);
481 }
482 }
483
484 pub fn render(&mut self) {
485 for stage in self.stage_list.iter_mut() {
487 if stage.needs_update {
488 stage.layout_sub_layers(None, &mut self.stretch);
489
490 if let Some(stretch_obj) = &mut self.stretch {
491 stretch_obj
492 .compute_layout(stage.node.unwrap(), Size::undefined())
493 .unwrap();
494 }
495
496 stage.update_layout(&mut self.stretch);
497 stage.needs_update = false;
498 }
499
500 stage.animate();
501 stage.render(None, &self.projection);
502 }
503
504 if let Some(ref context) = self.wgpu_context {
506 if let Some(ref surface) = context.surface {
507 let output = match surface.get_current_texture() {
508 Ok(output) => output,
509 Err(e) => {
510 eprintln!("Failed to get current texture: {:?}", e);
511 return;
512 }
513 };
514
515 let view = output
516 .texture
517 .create_view(&wgpu::TextureViewDescriptor::default());
518
519 let mut encoder = context
520 .device
521 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
522 label: Some("Render Encoder"),
523 });
524
525 {
526 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
527 label: Some("Render Pass"),
528 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
529 view: &view,
530 resolve_target: None,
531 ops: wgpu::Operations {
532 load: wgpu::LoadOp::Clear(wgpu::Color {
533 r: 0.1,
534 g: 0.2,
535 b: 0.3,
536 a: 1.0,
537 }),
538 store: wgpu::StoreOp::Store,
539 },
540 })],
541 depth_stencil_attachment: None,
542 timestamp_writes: None,
543 occlusion_query_set: None,
544 });
545
546 for stage in self.stage_list.iter_mut() {
548 Self::render_layer(
549 stage,
550 &mut render_pass,
551 None,
552 context,
553 &self.projection,
554 self.render_pipeline.as_ref().unwrap(),
555 self.bind_group_layout.as_ref().unwrap(),
556 self.texture_bind_group_layout.as_ref().unwrap(),
557 self.sampler.as_ref().unwrap(),
558 self.default_texture_view.as_ref().unwrap(),
559 );
560 }
561 }
562
563 context.queue.submit(std::iter::once(encoder.finish()));
564 output.present();
565 }
566 }
567 }
568}