use fixed::types::I27F5;
use twmap::{Group, Layer, LayerKind};
use vek::{Extent2, Vec2};
use wgpu::Device;
use super::{
GpuGroupData, GpuLayerData, GpuMapDataDyn, GpuMapDataStatic, GpuMapStatic, GpuQuadsRender,
GpuTilemapRender, TilemapCorner,
};
use crate::buffer::GpuBuffer;
use crate::shared::{Clip, ScissorRect};
use crate::textures::Samplers;
use crate::{GpuCamera, TwRenderPass};
pub enum GpuLayerRender {
Tilemap(GpuTilemapRender),
Quads(GpuQuadsRender),
}
pub struct GpuGroupRender {
pub clip: Option<Clip>,
pub parallax: Vec2<f32>,
pub offset: Vec2<f32>,
pub layers: Vec<GpuLayerRender>,
pub game_layer_cutoff: Option<usize>,
}
pub(crate) fn is_gpu_layer(layer: &&Layer) -> bool {
matches!(layer.kind(), LayerKind::Tiles | LayerKind::Quads)
}
fn is_gpu_or_game_layer(layer: &&Layer) -> bool {
matches!(
layer.kind(),
LayerKind::Game | LayerKind::Tiles | LayerKind::Quads
)
}
impl GpuMapStatic {
pub fn prepare_group_render(
&self,
group: &Group,
data: &GpuGroupData,
map_data_static: &GpuMapDataStatic,
map_data_dyn: &GpuMapDataDyn,
camera: &GpuCamera,
tilemap_vertices: &[Option<GpuBuffer<[TilemapCorner; 4]>>],
samplers: &Samplers,
device: &Device,
) -> GpuGroupRender {
let clip = if group.clipping {
let top_left: Vec2<f32> = group.clip.position().az();
let extent: Extent2<f32> =
Extent2::<I27F5>::max(group.clip.extent(), Extent2::zero()).az();
Some(Clip {
top_left,
bottom_right: top_left + extent,
})
} else {
None
};
let parallax = group.parallax.az::<f32>() / 100.;
let offset = group.offset.az::<f32>() * -1.; let mut layers = Vec::new();
for ((layer, tilemap_vertices), layer_data) in group
.layers
.iter()
.filter(is_gpu_layer)
.zip(tilemap_vertices.iter())
.zip(data.layers.iter())
{
match (layer, layer_data) {
(Layer::Tiles(layer), GpuLayerData::Tilemap(layer_data)) => {
layers.push(GpuLayerRender::Tilemap(self.tilemap.prepare_render(
layer,
layer_data,
tilemap_vertices.as_ref().unwrap(),
map_data_static,
samplers,
device,
)))
}
(Layer::Quads(layer), GpuLayerData::Quads(layer_data)) => {
layers.push(GpuLayerRender::Quads(self.quads.prepare_render(
layer,
layer_data,
data,
map_data_static,
map_data_dyn,
camera,
samplers,
device,
)))
}
_ => panic!("Mismatched layers between Group and GroupData"),
}
}
let game_layer_cutoff = group
.layers
.iter()
.filter(is_gpu_or_game_layer)
.position(|layer| layer.kind() == LayerKind::Game);
GpuGroupRender {
clip,
parallax,
offset,
layers,
game_layer_cutoff,
}
}
}
impl GpuLayerRender {
pub fn render<'pass>(&'pass self, render_pass: &mut TwRenderPass<'pass>) {
match self {
GpuLayerRender::Tilemap(tilemap) => tilemap.render(render_pass),
GpuLayerRender::Quads(quads) => quads.render(render_pass),
}
}
}
impl GpuGroupRender {
pub fn render<'pass>(&'pass self, foreground: bool, render_pass: &mut TwRenderPass<'pass>) {
let scissor_rect = match &self.clip {
None => ScissorRect::viewport(render_pass.size), Some(clip) => {
match clip.project(render_pass, Vec2::new(1., 1.)) {
None => return,
Some(scissor_rect) => {
match scissor_rect.intersect(&render_pass.scissor_rect) {
None => return,
Some(scissor_rect) => scissor_rect,
}
}
}
}
};
let layers = match (foreground, self.game_layer_cutoff) {
(_, None) => &self.layers[..],
(true, Some(cutoff)) => &self.layers[cutoff..],
(false, Some(cutoff)) => &self.layers[..cutoff],
};
render_pass.set_scissor_rect(&scissor_rect);
for layer in layers {
layer.render(render_pass);
}
}
}