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
323 fn render_layer(
325 layer: &mut Layer,
326 render_pass: &mut wgpu::RenderPass,
327 parent_transform: Option<&Matrix4<f32>>,
328 context: &WgpuContext,
329 projection: &Matrix4<f32>,
330 render_pipeline: &wgpu::RenderPipeline,
331 bind_group_layout: &wgpu::BindGroupLayout,
332 texture_bind_group_layout: &wgpu::BindGroupLayout,
333 sampler: &wgpu::Sampler,
334 default_texture_view: &wgpu::TextureView,
335 ) {
336 if !layer.visible {
337 return;
338 }
339
340 if layer.vertex_buffer.is_none() || layer.index_buffer.is_none() {
342 layer.init_buffers(&context.device);
343 }
344
345 if !layer.image_path.is_empty() && layer.texture.is_none() {
347 layer.load_image_texture(&context.device, &context.queue);
348 }
349
350 if layer.vertex_buffer.is_none() || layer.index_buffer.is_none() {
352 return;
353 }
354
355 let mut transform = layer.model_matrix();
357 if let Some(parent) = parent_transform {
358 transform = transform * parent;
359 }
360
361 let uniform_buffer = layer.create_uniform_buffer(&context.device, &transform, projection);
363
364 let uniform_bind_group = context.device.create_bind_group(&wgpu::BindGroupDescriptor {
366 label: Some("Uniform Bind Group"),
367 layout: bind_group_layout,
368 entries: &[wgpu::BindGroupEntry {
369 binding: 0,
370 resource: uniform_buffer.as_entire_binding(),
371 }],
372 });
373
374 let texture_bind_group = layer.get_or_create_bind_group(
376 &context.device,
377 texture_bind_group_layout,
378 sampler,
379 default_texture_view,
380 );
381
382 render_pass.set_pipeline(render_pipeline);
384 render_pass.set_bind_group(0, &uniform_bind_group, &[]);
385 render_pass.set_bind_group(1, texture_bind_group, &[]);
386 render_pass.set_vertex_buffer(0, layer.vertex_buffer.as_ref().unwrap().slice(..));
387 render_pass.set_index_buffer(
388 layer.index_buffer.as_ref().unwrap().slice(..),
389 wgpu::IndexFormat::Uint16,
390 );
391 render_pass.draw_indexed(0..6, 0, 0..1);
392
393 for (i, sub_layer) in layer.sub_layer_list.iter_mut().enumerate() {
395 if !sub_layer.focused && i != layer.focused_sub_layer {
396 Self::render_layer(
397 sub_layer,
398 render_pass,
399 Some(&transform),
400 context,
401 projection,
402 render_pipeline,
403 bind_group_layout,
404 texture_bind_group_layout,
405 sampler,
406 default_texture_view,
407 );
408 }
409 }
410
411 if !layer.sub_layer_list.is_empty() && layer.focused_sub_layer < layer.sub_layer_list.len() {
413 Self::render_layer(
414 &mut layer.sub_layer_list[layer.focused_sub_layer],
415 render_pass,
416 Some(&transform),
417 context,
418 projection,
419 render_pipeline,
420 bind_group_layout,
421 texture_bind_group_layout,
422 sampler,
423 default_texture_view,
424 );
425 }
426 }
427
428 pub fn new_layer(
429 name: String,
430 w: u32,
431 h: u32,
432 event_handler: Option<Box<dyn EventHandler>>,
433 ) -> Layer {
434 Layer::new(name, w, h, event_handler)
435 }
436
437 pub fn add_new_layer_to_stage(&mut self, stage_name: &String, layer: Layer) {
438 match self.stage_map.get(stage_name) {
439 Some(&index) => {
440 self.stage_list[index].add_sub_layer(layer);
441 }
442 _ => println!("Can't find the stage with the given name: {}", stage_name),
443 }
444 }
445
446 pub fn set_visible_stage(&mut self, name: &String, visible: bool) {
447 match self.stage_map.get(name) {
448 Some(&index) => {
449 self.stage_list[index].set_visible(visible);
450 self.stage_list[index].needs_update = true;
451 }
452 _ => println!("Can't find the stage with the given name: {}", name),
453 }
454 }
455
456 pub fn add_stage(&mut self, stage: Layer) -> String {
457 let stage_name = stage.name.to_string();
458 self.stage_list.push(stage);
459 self
460 .stage_map
461 .insert(stage_name.to_string(), self.stage_list.len() - 1);
462
463 stage_name
464 }
465
466 pub fn handle_input(&mut self, key: Key) {
467 for stage in self.stage_list.iter_mut() {
469 stage.handle_input(key);
470 }
471 }
472
473 pub fn render(&mut self) {
474 for stage in self.stage_list.iter_mut() {
476 if stage.needs_update {
477 stage.layout_sub_layers(None, &mut self.stretch);
478
479 if let Some(stretch_obj) = &mut self.stretch {
480 stretch_obj
481 .compute_layout(stage.node.unwrap(), Size::undefined())
482 .unwrap();
483 }
484
485 stage.update_layout(&mut self.stretch);
486 stage.needs_update = false;
487 }
488
489 stage.animate();
490 stage.render(None, &self.projection);
491 }
492
493 if let Some(ref context) = self.wgpu_context {
495 if let Some(ref surface) = context.surface {
496 let output = match surface.get_current_texture() {
497 Ok(output) => output,
498 Err(e) => {
499 eprintln!("Failed to get current texture: {:?}", e);
500 return;
501 }
502 };
503
504 let view = output
505 .texture
506 .create_view(&wgpu::TextureViewDescriptor::default());
507
508 let mut encoder = context
509 .device
510 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
511 label: Some("Render Encoder"),
512 });
513
514 {
515 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
516 label: Some("Render Pass"),
517 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
518 view: &view,
519 resolve_target: None,
520 ops: wgpu::Operations {
521 load: wgpu::LoadOp::Clear(wgpu::Color {
522 r: 0.1,
523 g: 0.2,
524 b: 0.3,
525 a: 1.0,
526 }),
527 store: wgpu::StoreOp::Store,
528 },
529 })],
530 depth_stencil_attachment: None,
531 timestamp_writes: None,
532 occlusion_query_set: None,
533 });
534
535 for stage in self.stage_list.iter_mut() {
537 Self::render_layer(
538 stage,
539 &mut render_pass,
540 None,
541 context,
542 &self.projection,
543 self.render_pipeline.as_ref().unwrap(),
544 self.bind_group_layout.as_ref().unwrap(),
545 self.texture_bind_group_layout.as_ref().unwrap(),
546 self.sampler.as_ref().unwrap(),
547 self.default_texture_view.as_ref().unwrap(),
548 );
549 }
550 }
551
552 context.queue.submit(std::iter::once(encoder.finish()));
553 output.present();
554 }
555 }
556 }
557}