Skip to main content

siglus_scene_vm/
layer.rs

1use crate::image_manager::ImageId;
2
3pub type LayerId = usize;
4pub type SpriteId = usize;
5
6#[derive(Debug, Copy, Clone, Eq, PartialEq)]
7pub enum SpriteFit {
8    /// Stretch to the entire framebuffer.
9    FullScreen,
10    /// Positioned in pixel coordinates with size controlled by `size_mode`.
11    PixelRect,
12}
13
14#[derive(Debug, Copy, Clone, Eq, PartialEq)]
15pub struct ClipRect {
16    pub left: i32,
17    pub top: i32,
18    pub right: i32,
19    pub bottom: i32,
20}
21
22#[derive(Debug, Copy, Clone, Eq, PartialEq)]
23pub enum SpriteSizeMode {
24    /// Use the intrinsic image size.
25    Intrinsic,
26    /// Use an explicit size.
27    Explicit { width: u32, height: u32 },
28}
29
30#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
31pub enum SpriteBlend {
32    Normal,
33    Add,
34    Sub,
35    Mul,
36    Screen,
37    Overlay,
38}
39
40impl Default for SpriteBlend {
41    fn default() -> Self {
42        Self::Normal
43    }
44}
45
46#[derive(Debug, Clone, Copy, PartialEq)]
47pub struct SpriteRuntimeLight {
48    pub id: i32,
49    pub kind: i32,
50    pub diffuse: [f32; 4],
51    pub ambient: [f32; 4],
52    pub specular: [f32; 4],
53    pub pos: [f32; 4],
54    pub dir: [f32; 4],
55    pub atten: [f32; 4],
56    pub cone: [f32; 4],
57}
58
59impl Default for SpriteRuntimeLight {
60    fn default() -> Self {
61        Self {
62            id: -1,
63            kind: -1,
64            diffuse: [0.0, 0.0, 0.0, 1.0],
65            ambient: [0.0, 0.0, 0.0, 1.0],
66            specular: [0.0, 0.0, 0.0, 1.0],
67            pos: [0.0, 0.0, 0.0, 1.0],
68            dir: [0.0, 0.0, -1.0, 0.0],
69            atten: [1.0, 0.0, 0.0, 5000.0],
70            cone: [0.0, 0.0, 1.0, 0.0],
71        }
72    }
73}
74impl SpriteBlend {
75    pub fn from_i64(v: i64) -> Self {
76        match v {
77            1 => SpriteBlend::Add,
78            2 => SpriteBlend::Sub,
79            3 => SpriteBlend::Mul,
80            4 => SpriteBlend::Screen,
81            5 => SpriteBlend::Overlay,
82            _ => SpriteBlend::Normal,
83        }
84    }
85}
86
87#[derive(Debug, Clone)]
88pub struct Sprite {
89    pub image_id: Option<ImageId>,
90    pub mask_image_id: Option<ImageId>,
91    pub mask_offset_x: i32,
92    pub mask_offset_y: i32,
93    pub tonecurve_image_id: Option<ImageId>,
94    pub tonecurve_row: f32,
95    pub tonecurve_sat: f32,
96    pub fit: SpriteFit,
97    pub size_mode: SpriteSizeMode,
98    pub visible: bool,
99    pub alpha: u8,
100    pub x: i32,
101    pub y: i32,
102    pub z: f32,
103    pub scale_x: f32,
104    pub scale_y: f32,
105    pub scale_z: f32,
106    pub rotate: f32,
107    pub rotate_x: f32,
108    pub rotate_y: f32,
109    pub pivot_x: f32,
110    pub pivot_y: f32,
111    pub pivot_z: f32,
112    /// Siglus object sprites use OBJECT.X/Y as the render anchor. For G00/PCT
113    /// object resources, vertices are offset by OBJECT.CENTER plus the texture
114    /// center stored in the cut metadata. Runtime helper sprites keep this off
115    /// and continue to use x/y as a top-left pixel rectangle.
116    pub object_anchor: bool,
117    pub texture_center_x: f32,
118    pub texture_center_y: f32,
119    pub camera_enabled: bool,
120    pub camera_eye: [f32; 3],
121    pub camera_target: [f32; 3],
122    pub camera_up: [f32; 3],
123    pub camera_view_angle_deg: f32,
124    pub culling: bool,
125    pub alpha_test: bool,
126    pub alpha_blend: bool,
127    pub fog_use: bool,
128    pub light_no: i32,
129    pub light_enabled: bool,
130    pub light_diffuse: [f32; 4],
131    pub light_ambient: [f32; 4],
132    pub light_specular: [f32; 4],
133    pub light_factor: f32,
134    pub light_kind: i32,
135    pub light_pos: [f32; 4],
136    pub light_dir: [f32; 4],
137    pub light_atten: [f32; 4],
138    pub light_cone: [f32; 4],
139    pub mesh_runtime_lights: Vec<SpriteRuntimeLight>,
140    pub fog_enabled: bool,
141    pub fog_color: [f32; 4],
142    pub fog_near: f32,
143    pub fog_far: f32,
144    pub fog_scroll_x: f32,
145    pub fog_texture_image_id: Option<ImageId>,
146    pub world_no: i32,
147    pub billboard: bool,
148    pub mesh_file_name: Option<String>,
149    pub mesh_animation: crate::mesh3d::MeshAnimationState,
150    /// 0 = regular sprite or 2D camera quad, 1 = static mesh, 2 = billboard mesh, 3 = skinned mesh.
151    pub mesh_kind: u8,
152    pub shadow_cast: bool,
153    pub shadow_receive: bool,
154    /// Runtime wipe/effect family applied by the renderer.
155    /// 0=none, 1=mosaic, 2=raster_h, 3=raster_v, 4=explosion_blur,
156    /// 5=shimi, 6=shimi_inv, 10=cross_mosaic, 11=cross_raster_h,
157    /// 12=cross_raster_v, 13=cross_explosion_blur.
158    pub wipe_fx_mode: u8,
159    pub wipe_fx_params: [f32; 4],
160    /// Optional secondary source texture for dual-source wipe/effect composition.
161    pub wipe_src_image_id: Option<ImageId>,
162    pub tr: u8,
163    pub mono: u8,
164    pub reverse: u8,
165    pub bright: u8,
166    pub dark: u8,
167    pub color_rate: u8,
168    pub color_add_r: u8,
169    pub color_add_g: u8,
170    pub color_add_b: u8,
171    pub color_r: u8,
172    pub color_g: u8,
173    pub color_b: u8,
174    /// 0 = off, 1 = use luminance as alpha, 2 = use texture alpha.
175    pub mask_mode: u8,
176    pub blend: SpriteBlend,
177    pub dst_clip: Option<ClipRect>,
178    pub src_clip: Option<ClipRect>,
179    /// Render order within a layer (ascending).
180    pub order: i32,
181}
182
183impl Default for Sprite {
184    fn default() -> Self {
185        Self {
186            image_id: None,
187            mask_image_id: None,
188            mask_offset_x: 0,
189            mask_offset_y: 0,
190            tonecurve_image_id: None,
191            tonecurve_row: 0.0,
192            tonecurve_sat: 0.0,
193            fit: SpriteFit::PixelRect,
194            size_mode: SpriteSizeMode::Intrinsic,
195            visible: false,
196            alpha: 255,
197            x: 0,
198            y: 0,
199            z: 0.0,
200            scale_x: 1.0,
201            scale_y: 1.0,
202            scale_z: 1.0,
203            rotate: 0.0,
204            rotate_x: 0.0,
205            rotate_y: 0.0,
206            pivot_x: 0.0,
207            pivot_y: 0.0,
208            pivot_z: 0.0,
209            object_anchor: false,
210            texture_center_x: 0.0,
211            texture_center_y: 0.0,
212            camera_enabled: false,
213            camera_eye: [0.0, 0.0, -1000.0],
214            camera_target: [0.0, 0.0, 0.0],
215            camera_up: [0.0, 1.0, 0.0],
216            camera_view_angle_deg: 45.0,
217            culling: false,
218            alpha_test: false,
219            alpha_blend: true,
220            fog_use: false,
221            light_no: -1,
222            light_enabled: false,
223            light_diffuse: [1.0, 1.0, 1.0, 1.0],
224            light_ambient: [0.0, 0.0, 0.0, 1.0],
225            light_specular: [0.0, 0.0, 0.0, 1.0],
226            light_factor: 0.0,
227            light_kind: -1,
228            light_pos: [0.0, 0.0, 0.0, 0.0],
229            light_dir: [0.0, 0.0, -1.0, 0.0],
230            light_atten: [1.0, 0.0, 0.0, 5000.0],
231            light_cone: [0.0, 0.0, 1.0, 0.0],
232            mesh_runtime_lights: Vec::new(),
233            fog_enabled: false,
234            fog_color: [0.0, 0.0, 0.0, 1.0],
235            fog_near: 0.0,
236            fog_far: 0.0,
237            fog_scroll_x: 0.0,
238            fog_texture_image_id: None,
239            world_no: -1,
240            billboard: false,
241            mesh_file_name: None,
242            mesh_animation: crate::mesh3d::MeshAnimationState::default(),
243            mesh_kind: 0,
244            shadow_cast: false,
245            shadow_receive: false,
246            wipe_fx_mode: 0,
247            wipe_fx_params: [0.0; 4],
248            wipe_src_image_id: None,
249            tr: 255,
250            mono: 0,
251            reverse: 0,
252            bright: 0,
253            dark: 0,
254            color_rate: 0,
255            color_add_r: 0,
256            color_add_g: 0,
257            color_add_b: 0,
258            color_r: 0,
259            color_g: 0,
260            color_b: 0,
261            mask_mode: 0,
262            blend: SpriteBlend::Normal,
263            dst_clip: None,
264            src_clip: None,
265            order: 0,
266        }
267    }
268}
269
270#[derive(Debug, Clone)]
271pub struct Layer {
272    sprites: Vec<Sprite>,
273}
274
275impl Layer {
276    pub fn new() -> Self {
277        Self {
278            sprites: Vec::new(),
279        }
280    }
281
282    pub fn create_sprite(&mut self) -> SpriteId {
283        let id = self.sprites.len();
284        self.sprites.push(Sprite::default());
285        id
286    }
287
288    pub fn sprite(&self, id: SpriteId) -> Option<&Sprite> {
289        self.sprites.get(id)
290    }
291
292    pub fn sprite_mut(&mut self, id: SpriteId) -> Option<&mut Sprite> {
293        self.sprites.get_mut(id)
294    }
295
296    pub fn clear_all_sprites(&mut self) {
297        // Keep sprite IDs stable: do not shrink the sprite vector.
298        for s in &mut self.sprites {
299            s.image_id = None;
300            s.mask_image_id = None;
301            s.mask_offset_x = 0;
302            s.mask_offset_y = 0;
303            s.tonecurve_image_id = None;
304            s.tonecurve_row = 0.0;
305            s.tonecurve_sat = 0.0;
306            s.visible = false;
307            s.alpha = 255;
308            s.x = 0;
309            s.y = 0;
310            s.z = 0.0;
311            s.order = 0;
312            s.fit = SpriteFit::PixelRect;
313            s.size_mode = SpriteSizeMode::Intrinsic;
314            s.scale_x = 1.0;
315            s.scale_y = 1.0;
316            s.scale_z = 1.0;
317            s.rotate = 0.0;
318            s.rotate_x = 0.0;
319            s.rotate_y = 0.0;
320            s.pivot_x = 0.0;
321            s.pivot_y = 0.0;
322            s.pivot_z = 0.0;
323            s.camera_enabled = false;
324            s.camera_eye = [0.0, 0.0, -1000.0];
325            s.camera_target = [0.0, 0.0, 0.0];
326            s.camera_up = [0.0, 1.0, 0.0];
327            s.camera_view_angle_deg = 45.0;
328            s.culling = false;
329            s.alpha_test = false;
330            s.alpha_blend = true;
331            s.fog_use = false;
332            s.light_no = -1;
333            s.light_enabled = false;
334            s.light_diffuse = [1.0, 1.0, 1.0, 1.0];
335            s.light_ambient = [0.0, 0.0, 0.0, 1.0];
336            s.light_specular = [0.0, 0.0, 0.0, 1.0];
337            s.light_factor = 0.0;
338            s.light_kind = -1;
339            s.light_pos = [0.0, 0.0, 0.0, 0.0];
340            s.light_dir = [0.0, 0.0, -1.0, 0.0];
341            s.light_atten = [1.0, 0.0, 0.0, 5000.0];
342            s.light_cone = [0.0, 0.0, 1.0, 0.0];
343            s.mesh_runtime_lights.clear();
344            s.fog_enabled = false;
345            s.fog_color = [0.0, 0.0, 0.0, 1.0];
346            s.fog_near = 0.0;
347            s.fog_far = 0.0;
348            s.fog_scroll_x = 0.0;
349            s.fog_texture_image_id = None;
350            s.world_no = -1;
351            s.billboard = false;
352            s.mesh_kind = 0;
353            s.mesh_file_name = None;
354            s.mesh_animation = crate::mesh3d::MeshAnimationState::default();
355            s.shadow_cast = false;
356            s.shadow_receive = false;
357            s.wipe_fx_mode = 0;
358            s.wipe_fx_params = [0.0; 4];
359            s.wipe_src_image_id = None;
360            s.tr = 255;
361            s.mono = 0;
362            s.reverse = 0;
363            s.bright = 0;
364            s.dark = 0;
365            s.color_rate = 0;
366            s.color_add_r = 0;
367            s.color_add_g = 0;
368            s.color_add_b = 0;
369            s.color_r = 0;
370            s.color_g = 0;
371            s.color_b = 0;
372            s.blend = SpriteBlend::Normal;
373            s.dst_clip = None;
374            s.src_clip = None;
375        }
376    }
377
378    fn sprite_ids_sorted(&self) -> Vec<SpriteId> {
379        let mut ids: Vec<SpriteId> = (0..self.sprites.len()).collect();
380        ids.sort_by(|&a, &b| {
381            let oa = self.sprites[a].order;
382            let ob = self.sprites[b].order;
383            oa.cmp(&ob).then(a.cmp(&b))
384        });
385        ids
386    }
387}
388
389#[derive(Debug, Clone)]
390pub struct RenderSprite {
391    pub layer_id: Option<LayerId>,
392    pub sprite_id: Option<SpriteId>,
393    /// Original Siglus C++ S_tnm_sorter.order. This is separate from
394    /// Sprite::order because Sprite::order is still used by older backend
395    /// storage paths and debug output. Final scene ordering, wipe ranges,
396    /// effects, and quake ranges must use this pair.
397    pub sorter_order: i32,
398    /// Original Siglus C++ S_tnm_sorter.layer. This can be much larger than
399    /// 1023, so it must not be packed into Sprite::order.
400    pub sorter_layer: i32,
401    pub sprite: Sprite,
402}
403
404impl RenderSprite {
405    pub fn new(layer_id: Option<LayerId>, sprite_id: Option<SpriteId>, sprite: Sprite) -> Self {
406        let (sorter_order, sorter_layer) = unpack_sprite_order(sprite.order);
407        Self {
408            layer_id,
409            sprite_id,
410            sorter_order,
411            sorter_layer,
412            sprite,
413        }
414    }
415
416    pub fn with_sorter(
417        layer_id: Option<LayerId>,
418        sprite_id: Option<SpriteId>,
419        sorter_order: i32,
420        sorter_layer: i32,
421        sprite: Sprite,
422    ) -> Self {
423        Self {
424            layer_id,
425            sprite_id,
426            sorter_order,
427            sorter_layer,
428            sprite,
429        }
430    }
431
432    pub fn set_sorter(&mut self, order: i64, layer: i64) {
433        self.sorter_order = order.clamp(i32::MIN as i64, i32::MAX as i64) as i32;
434        self.sorter_layer = layer.clamp(i32::MIN as i64, i32::MAX as i64) as i32;
435    }
436}
437
438fn unpack_sprite_order(order: i32) -> (i32, i32) {
439    if order.abs() >= 1024 {
440        (order.div_euclid(1024), order.rem_euclid(1024))
441    } else {
442        (0, order)
443    }
444}
445
446#[derive(Debug, Default)]
447pub struct LayerManager {
448    bg: Sprite,
449    layers: Vec<Layer>,
450}
451
452impl LayerManager {
453    pub fn new() -> Self {
454        Self::default()
455    }
456
457    pub fn bg_mut(&mut self) -> &mut Sprite {
458        &mut self.bg
459    }
460
461    pub fn set_bg_image(&mut self, image_id: ImageId) {
462        self.bg.image_id = Some(image_id);
463        self.bg.mask_image_id = None;
464        self.bg.mask_offset_x = 0;
465        self.bg.mask_offset_y = 0;
466        self.bg.tonecurve_image_id = None;
467        self.bg.tonecurve_row = 0.0;
468        self.bg.tonecurve_sat = 0.0;
469        self.bg.wipe_fx_mode = 0;
470        self.bg.wipe_fx_params = [0.0; 4];
471        self.bg.wipe_src_image_id = None;
472        self.bg.fit = SpriteFit::FullScreen;
473        self.bg.size_mode = SpriteSizeMode::Intrinsic;
474        self.bg.visible = true;
475        self.bg.order = i32::MIN;
476        self.bg.scale_x = 1.0;
477        self.bg.scale_y = 1.0;
478        self.bg.scale_z = 1.0;
479        self.bg.rotate = 0.0;
480        self.bg.rotate_x = 0.0;
481        self.bg.rotate_y = 0.0;
482        self.bg.pivot_x = 0.0;
483        self.bg.pivot_y = 0.0;
484        self.bg.pivot_z = 0.0;
485        self.bg.z = 0.0;
486        self.bg.camera_enabled = false;
487        self.bg.camera_eye = [0.0, 0.0, -1000.0];
488        self.bg.camera_target = [0.0, 0.0, 0.0];
489        self.bg.camera_up = [0.0, 1.0, 0.0];
490        self.bg.camera_view_angle_deg = 45.0;
491        self.bg.culling = false;
492        self.bg.alpha_test = false;
493        self.bg.alpha_blend = true;
494        self.bg.fog_use = false;
495        self.bg.light_no = -1;
496        self.bg.light_enabled = false;
497        self.bg.light_diffuse = [1.0, 1.0, 1.0, 1.0];
498        self.bg.light_ambient = [0.0, 0.0, 0.0, 1.0];
499        self.bg.light_specular = [0.0, 0.0, 0.0, 1.0];
500        self.bg.light_factor = 0.0;
501        self.bg.light_kind = -1;
502        self.bg.light_pos = [0.0, 0.0, 0.0, 0.0];
503        self.bg.light_dir = [0.0, 0.0, -1.0, 0.0];
504        self.bg.light_atten = [1.0, 0.0, 0.0, 5000.0];
505        self.bg.light_cone = [0.0, 0.0, 1.0, 0.0];
506        self.bg.mesh_runtime_lights.clear();
507        self.bg.fog_enabled = false;
508        self.bg.fog_color = [0.0, 0.0, 0.0, 1.0];
509        self.bg.fog_near = 0.0;
510        self.bg.fog_far = 0.0;
511        self.bg.fog_scroll_x = 0.0;
512        self.bg.fog_texture_image_id = None;
513        self.bg.world_no = -1;
514        self.bg.billboard = false;
515        self.bg.mesh_kind = 0;
516        self.bg.mesh_file_name = None;
517        self.bg.mesh_animation = crate::mesh3d::MeshAnimationState::default();
518        self.bg.shadow_cast = false;
519        self.bg.shadow_receive = false;
520        self.bg.wipe_fx_mode = 0;
521        self.bg.wipe_fx_params = [0.0; 4];
522        self.bg.wipe_src_image_id = None;
523        self.bg.tr = 255;
524        self.bg.mono = 0;
525        self.bg.reverse = 0;
526        self.bg.bright = 0;
527        self.bg.dark = 0;
528        self.bg.color_rate = 0;
529        self.bg.color_add_r = 0;
530        self.bg.color_add_g = 0;
531        self.bg.color_add_b = 0;
532        self.bg.color_r = 0;
533        self.bg.color_g = 0;
534        self.bg.color_b = 0;
535        self.bg.mask_mode = 0;
536        self.bg.blend = SpriteBlend::Normal;
537        self.bg.dst_clip = None;
538        self.bg.src_clip = None;
539    }
540
541    pub fn clear_bg(&mut self) {
542        self.bg.image_id = None;
543        self.bg.mask_image_id = None;
544        self.bg.mask_offset_x = 0;
545        self.bg.mask_offset_y = 0;
546        self.bg.tonecurve_image_id = None;
547        self.bg.tonecurve_row = 0.0;
548        self.bg.tonecurve_sat = 0.0;
549        self.bg.wipe_fx_mode = 0;
550        self.bg.wipe_fx_params = [0.0; 4];
551        self.bg.wipe_src_image_id = None;
552        self.bg.light_enabled = false;
553        self.bg.light_diffuse = [1.0, 1.0, 1.0, 1.0];
554        self.bg.light_ambient = [0.0, 0.0, 0.0, 1.0];
555        self.bg.light_specular = [0.0, 0.0, 0.0, 1.0];
556        self.bg.light_factor = 0.0;
557        self.bg.light_kind = -1;
558        self.bg.light_pos = [0.0, 0.0, 0.0, 0.0];
559        self.bg.light_dir = [0.0, 0.0, -1.0, 0.0];
560        self.bg.light_atten = [1.0, 0.0, 0.0, 5000.0];
561        self.bg.light_cone = [0.0, 0.0, 1.0, 0.0];
562        self.bg.mesh_runtime_lights.clear();
563        self.bg.fog_enabled = false;
564        self.bg.fog_color = [0.0, 0.0, 0.0, 1.0];
565        self.bg.fog_near = 0.0;
566        self.bg.fog_far = 0.0;
567        self.bg.fog_scroll_x = 0.0;
568        self.bg.fog_texture_image_id = None;
569        self.bg.mesh_kind = 0;
570        self.bg.mesh_file_name = None;
571        self.bg.mesh_animation = crate::mesh3d::MeshAnimationState::default();
572        self.bg.shadow_cast = false;
573        self.bg.shadow_receive = false;
574        self.bg.visible = false;
575    }
576
577    pub fn create_layer(&mut self) -> LayerId {
578        let id = self.layers.len();
579        self.layers.push(Layer::new());
580        id
581    }
582
583    pub fn layer(&self, id: LayerId) -> Option<&Layer> {
584        self.layers.get(id)
585    }
586
587    pub fn layer_mut(&mut self, id: LayerId) -> Option<&mut Layer> {
588        self.layers.get_mut(id)
589    }
590
591    pub fn clear_layer(&mut self, id: LayerId) {
592        if let Some(layer) = self.layers.get_mut(id) {
593            layer.clear_all_sprites();
594        }
595    }
596
597    pub fn clear_all(&mut self) {
598        self.clear_bg();
599        for layer in &mut self.layers {
600            layer.clear_all_sprites();
601        }
602    }
603
604    pub fn reset_runtime_effects(&mut self) {
605        self.bg.mask_image_id = None;
606        self.bg.mask_offset_x = 0;
607        self.bg.mask_offset_y = 0;
608        self.bg.tonecurve_image_id = None;
609        self.bg.tonecurve_row = 0.0;
610        self.bg.tonecurve_sat = 0.0;
611        self.bg.wipe_fx_mode = 0;
612        self.bg.wipe_fx_params = [0.0; 4];
613        self.bg.wipe_src_image_id = None;
614        self.bg.light_enabled = false;
615        self.bg.light_diffuse = [1.0, 1.0, 1.0, 1.0];
616        self.bg.light_ambient = [0.0, 0.0, 0.0, 1.0];
617        self.bg.light_specular = [0.0, 0.0, 0.0, 1.0];
618        self.bg.light_factor = 0.0;
619        self.bg.light_kind = -1;
620        self.bg.light_pos = [0.0, 0.0, 0.0, 0.0];
621        self.bg.light_dir = [0.0, 0.0, -1.0, 0.0];
622        self.bg.light_atten = [1.0, 0.0, 0.0, 5000.0];
623        self.bg.light_cone = [0.0, 0.0, 1.0, 0.0];
624        self.bg.mesh_runtime_lights.clear();
625        self.bg.fog_enabled = false;
626        self.bg.fog_color = [0.0, 0.0, 0.0, 1.0];
627        self.bg.fog_near = 0.0;
628        self.bg.fog_far = 0.0;
629        self.bg.fog_scroll_x = 0.0;
630        self.bg.fog_texture_image_id = None;
631        for layer in &mut self.layers {
632            for s in &mut layer.sprites {
633                s.mask_image_id = None;
634                s.mask_offset_x = 0;
635                s.mask_offset_y = 0;
636                s.tonecurve_image_id = None;
637                s.tonecurve_row = 0.0;
638                s.tonecurve_sat = 0.0;
639                s.wipe_fx_mode = 0;
640                s.wipe_fx_params = [0.0; 4];
641                s.wipe_src_image_id = None;
642                s.light_enabled = false;
643                s.light_diffuse = [1.0, 1.0, 1.0, 1.0];
644                s.light_ambient = [0.0, 0.0, 0.0, 1.0];
645                s.light_factor = 0.0;
646                s.fog_enabled = false;
647                s.fog_color = [0.0, 0.0, 0.0, 1.0];
648                s.fog_near = 0.0;
649                s.fog_far = 0.0;
650                s.fog_scroll_x = 0.0;
651                s.fog_texture_image_id = None;
652                s.mesh_kind = 0;
653                s.mesh_file_name = None;
654                s.shadow_cast = false;
655                s.shadow_receive = false;
656            }
657        }
658    }
659
660    pub fn render_list(&self) -> Vec<RenderSprite> {
661        let mut out = Vec::new();
662
663        if self.bg.visible && self.bg.alpha > 0 && self.bg.tr > 0 {
664            if let Some(img) = self.bg.image_id {
665                let mut bg = self.bg.clone();
666                bg.image_id = Some(img);
667                out.push(RenderSprite::new(None, None, bg));
668            }
669        }
670
671        for (layer_id, layer) in self.layers.iter().enumerate() {
672            for sprite_id in layer.sprite_ids_sorted() {
673                let s = &layer.sprites[sprite_id];
674                if !s.visible {
675                    continue;
676                }
677                if s.image_id.is_none() || s.alpha == 0 || s.tr == 0 {
678                    continue;
679                }
680                out.push(RenderSprite::new(Some(layer_id), Some(sprite_id), s.clone()));
681            }
682        }
683
684        out
685    }
686}