Created
August 12, 2021 16:12
-
-
Save AcrylicShrimp/7569f1cca8ea1028d6af2525a97e6785 to your computer and use it in GitHub Desktop.
Rendering system logic
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use crate::component::*; | |
use crate::render::*; | |
use crate::system::System; | |
use crate::EngineContextWithoutSystemManager; | |
use legion::*; | |
use std::cmp::{max, min, Ordering}; | |
use std::mem::size_of; | |
use std::sync::Arc; | |
enum RendererRef<'r> { | |
GlyphRenderer(&'r GlyphRenderer, (f32, f32), (Arc<Texture>, TexelMapping)), | |
SpriteRenderer(&'r SpriteRenderer, &'r Arc<Sprite>), | |
TilemapRenderer(&'r TilemapRenderer), | |
} | |
impl<'r> RendererRef<'r> { | |
pub fn shader(&self) -> &Arc<Shader> { | |
match self { | |
&Self::GlyphRenderer(glyph_renderer, ..) => &glyph_renderer.shader, | |
&Self::SpriteRenderer(sprite_renderer, ..) => &sprite_renderer.shader, | |
&Self::TilemapRenderer(tilemap_renderer, ..) => &tilemap_renderer.shader, | |
} | |
} | |
pub fn texture(&self) -> &Arc<Texture> { | |
match self { | |
Self::GlyphRenderer(.., (texture, ..)) => &texture, | |
&Self::SpriteRenderer(.., sprite) => sprite.texture(), | |
&Self::TilemapRenderer(tilemap_renderer) => tilemap_renderer.tilemap.palette.texture(), | |
} | |
} | |
pub fn texture_uniform_name(&self) -> &'static str { | |
match self { | |
Self::GlyphRenderer(..) => "glyph", | |
Self::SpriteRenderer(..) => "sprite", | |
Self::TilemapRenderer(..) => "sprite", | |
} | |
} | |
} | |
pub struct RendererSystem { | |
glyph_buffer: Arc<Buffer>, | |
sprite_buffer: Arc<Buffer>, | |
tilemap_sprite_buffer: Arc<Buffer>, | |
} | |
impl RendererSystem { | |
pub fn new() -> Self { | |
let glyph_buffer = Buffer::from(&[ | |
1f32, 1f32, // | |
1f32, 0f32, // | |
// | |
0f32, 0f32, // | |
0f32, 1f32, // | |
// | |
1f32, 0f32, // | |
1f32, 1f32, // | |
// | |
// | |
// | |
1f32, 1f32, // | |
1f32, 0f32, // | |
// | |
0f32, 1f32, // | |
0f32, 0f32, // | |
// | |
0f32, 0f32, // | |
0f32, 1f32, // | |
]); | |
let sprite_buffer = Buffer::from(&[ | |
1f32, 1f32, // | |
1f32, 0f32, // | |
// | |
0f32, 0f32, // | |
0f32, 1f32, // | |
// | |
1f32, 0f32, // | |
1f32, 1f32, // | |
// | |
// | |
// | |
1f32, 1f32, // | |
1f32, 0f32, // | |
// | |
0f32, 1f32, // | |
0f32, 0f32, // | |
// | |
0f32, 0f32, // | |
0f32, 1f32, // | |
]); | |
let tilemap_sprite_buffer = Buffer::from(&[ | |
1f32, 1f32, // | |
1f32, 0f32, // | |
// | |
0f32, 0f32, // | |
0f32, 1f32, // | |
// | |
1f32, 0f32, // | |
1f32, 1f32, // | |
// | |
// | |
// | |
1f32, 1f32, // | |
1f32, 0f32, // | |
// | |
0f32, 1f32, // | |
0f32, 0f32, // | |
// | |
0f32, 0f32, // | |
0f32, 1f32, // | |
]); | |
Self { | |
glyph_buffer: glyph_buffer.into(), | |
sprite_buffer: sprite_buffer.into(), | |
tilemap_sprite_buffer: tilemap_sprite_buffer.into(), | |
} | |
} | |
} | |
impl System for RendererSystem { | |
// TODO: Clean and simplify the rendering pipelines. | |
fn run(&mut self, context: &EngineContextWithoutSystemManager) { | |
let mut world = context.world_mut(); | |
let mut glyph_mgr = context.glyph_mgr_mut(); | |
let mut camera_query = <(&Transform, &Camera)>::query(); | |
let (world, rest_world) = world.split_for_query(&camera_query); | |
let mut renderers = Vec::with_capacity(4096); | |
// | |
// Glyph renderers. | |
// | |
for (transform, layer, glyph_renderer) in unsafe { | |
<(&Transform, &Layer, &mut GlyphRenderer)>::query().iter_unchecked(&rest_world) | |
} { | |
let glyphs = { | |
let (font, layout) = glyph_renderer.font_and_layout(); | |
layout | |
.glyphs() | |
.into_iter() | |
.map(|glyph| { | |
( | |
(glyph.x, glyph.y), | |
(glyph_mgr.glyph(font, glyph.key).clone()), | |
) | |
}) | |
.collect::<Vec<_>>() | |
}; | |
for (offset, (texture, mapping)) in glyphs { | |
renderers.push(( | |
glyph_renderer.order, | |
&self.glyph_buffer, | |
glyph_renderer.shader.handle(), | |
texture.handle(), | |
u32::from(*transform), | |
layer, | |
RendererRef::GlyphRenderer(glyph_renderer, offset, (texture, mapping)), | |
)) | |
} | |
} | |
// | |
// Sprite renderers. | |
// | |
for (transform, layer, sprite_renderer) in | |
<(&Transform, &Layer, &SpriteRenderer)>::query().iter(&rest_world) | |
{ | |
let sprite = match &sprite_renderer.sprite { | |
SpriteType::Animated(animation) => animation.sprite(), | |
SpriteType::Sprite(sprite) => sprite, | |
}; | |
renderers.push(( | |
sprite_renderer.order, | |
&self.sprite_buffer, | |
sprite_renderer.shader.handle(), | |
sprite.texture().handle(), | |
u32::from(*transform), | |
layer, | |
RendererRef::SpriteRenderer(sprite_renderer, sprite), | |
)); | |
} | |
// | |
// Tilemap renderers. | |
// | |
for (transform, layer, tilemap_renderer) in | |
<(&Transform, &Layer, &TilemapRenderer)>::query().iter(&rest_world) | |
{ | |
renderers.push(( | |
tilemap_renderer.order, | |
&self.tilemap_sprite_buffer, | |
tilemap_renderer.shader.handle(), | |
tilemap_renderer.tilemap.palette.texture().handle(), | |
u32::from(*transform), | |
layer, | |
RendererRef::TilemapRenderer(tilemap_renderer), | |
)); | |
} | |
renderers.sort_unstable_by(|lhs, rhs| match lhs.0.cmp(&rhs.0) { | |
Ordering::Equal => match Arc::as_ptr(lhs.1).cmp(&Arc::as_ptr(rhs.1)) { | |
Ordering::Equal => match lhs.2.cmp(&rhs.2) { | |
Ordering::Equal => lhs.3.cmp(&rhs.3), | |
ordering => ordering, | |
}, | |
ordering => ordering, | |
}, | |
ordering => ordering, | |
}); | |
let mut renderer_chunk: Vec<( | |
(isize, *const Buffer, u32, u32), | |
(Arc<Shader>, Arc<Texture>, &'static str, &Arc<Buffer>), | |
Vec<_>, | |
)> = Vec::new(); | |
for renderer in renderers { | |
let key = (renderer.0, Arc::as_ptr(renderer.1), renderer.2, renderer.3); | |
let value = (renderer.4, renderer.5, renderer.6); | |
match renderer_chunk.last_mut() { | |
Some(last) if last.0 == key => last.2.push(value), | |
_ => renderer_chunk.push(( | |
key, | |
( | |
value.2.shader().clone(), | |
value.2.texture().clone(), | |
value.2.texture_uniform_name(), | |
renderer.1, | |
), | |
vec![value], | |
)), | |
} | |
} | |
let mut cameras = camera_query.iter(&world).collect::<Vec<_>>(); | |
cameras.sort_unstable_by(|lhs, rhs| lhs.1.order.cmp(&rhs.1.order)); | |
let mut renderer = Renderer::new(); | |
let screen_mgr = context.screen_mgr(); | |
let rendering_mgr = context.render_mgr(); | |
let transform_mgr = context.transform_mgr(); | |
let width_half = (screen_mgr.width() * 0.5) as f32; | |
let height_half = (screen_mgr.height() * 0.5) as f32; | |
for (camera_transform, camera) in cameras { | |
let camera_transform_index = u32::from(*camera_transform); | |
let camera_transform = transform_mgr.transform(camera_transform_index); | |
let mut ndc_to_world = transform_mgr | |
.transform_world_matrix(camera_transform_index) | |
.clone(); | |
ndc_to_world[0] *= width_half; | |
ndc_to_world[1] *= width_half; | |
ndc_to_world[3] *= height_half; | |
ndc_to_world[4] *= height_half; | |
let mut camera_matrix_inverse = [0f32; 9]; | |
camera_transform.to_matrix_inverse_with_scale( | |
width_half, | |
height_half, | |
&mut camera_matrix_inverse, | |
); | |
for (.., (shader, texture, texture_uniform_name, buffer), renderers) in &renderer_chunk | |
{ | |
let mut instance_count = 0; | |
let mut per_instance_buffer = Vec::<f32>::with_capacity(4096); | |
let renderers = renderers | |
.iter() | |
.filter(|&(_, &layer, ..)| camera.layer & u64::from(layer) != 0); | |
for (transform, _, renderer) in renderers { | |
let matrix = transform_mgr.transform_world_matrix(*transform); | |
match renderer { | |
RendererRef::GlyphRenderer( | |
glyph_renderer, | |
(offset_x, offset_y), | |
(texture, mapping), | |
) => { | |
per_instance_buffer.extend(&[ | |
matrix[0], | |
matrix[1], | |
matrix[2], | |
matrix[3], | |
matrix[4], | |
matrix[5], | |
matrix[6] + matrix[0] * offset_x + matrix[3] * offset_y, | |
matrix[7] + matrix[1] * offset_x + matrix[4] * offset_y, | |
matrix[8], | |
mapping.width() as f32, | |
mapping.height() as f32, | |
glyph_renderer.color.r, | |
glyph_renderer.color.g, | |
glyph_renderer.color.b, | |
glyph_renderer.color.a, | |
(mapping.min().0 as f32 + 0.5f32) / texture.width() as f32, | |
(mapping.min().1 as f32 + 0.5f32) / texture.height() as f32, | |
(mapping.max().0 as f32 - 0.5f32) / texture.width() as f32, | |
(mapping.max().1 as f32 - 0.5f32) / texture.height() as f32, | |
]); | |
instance_count += 1; | |
} | |
&RendererRef::SpriteRenderer(sprite_renderer, sprite) => { | |
let texel_mapping = sprite.texel_mapping(); | |
per_instance_buffer.extend(&[ | |
matrix[0], | |
matrix[1], | |
matrix[2], | |
matrix[3], | |
matrix[4], | |
matrix[5], | |
matrix[6], | |
matrix[7], | |
matrix[8], | |
sprite.width() as f32, | |
sprite.height() as f32, | |
sprite_renderer.color.r, | |
sprite_renderer.color.g, | |
sprite_renderer.color.b, | |
sprite_renderer.color.a, | |
(texel_mapping.min().0 as f32 + 0.5f32) / texture.width() as f32, | |
(texel_mapping.min().1 as f32 + 0.5f32) / texture.height() as f32, | |
(texel_mapping.max().0 as f32 - 0.5f32) / texture.width() as f32, | |
(texel_mapping.max().1 as f32 - 0.5f32) / texture.height() as f32, | |
]); | |
instance_count += 1; | |
} | |
&RendererRef::TilemapRenderer(tilemap_renderer) => { | |
let transform = transform_mgr.transform(*transform); | |
let mut world_to_local = [0f32; 9]; | |
let mut ndc_to_local = [0f32; 6]; | |
transform.to_matrix_inverse(&mut world_to_local); | |
ndc_to_local[0] = ndc_to_world[0] * world_to_local[0] | |
+ ndc_to_world[1] * world_to_local[3] | |
+ ndc_to_world[2] * world_to_local[6]; | |
ndc_to_local[1] = ndc_to_world[0] * world_to_local[1] | |
+ ndc_to_world[1] * world_to_local[4] | |
+ ndc_to_world[2] * world_to_local[7]; | |
ndc_to_local[2] = ndc_to_world[3] * world_to_local[0] | |
+ ndc_to_world[4] * world_to_local[3] | |
+ ndc_to_world[5] * world_to_local[6]; | |
ndc_to_local[3] = ndc_to_world[3] * world_to_local[1] | |
+ ndc_to_world[4] * world_to_local[4] | |
+ ndc_to_world[5] * world_to_local[7]; | |
ndc_to_local[4] = ndc_to_world[6] * world_to_local[0] | |
+ ndc_to_world[7] * world_to_local[3] | |
+ ndc_to_world[8] * world_to_local[6]; | |
ndc_to_local[5] = ndc_to_world[6] * world_to_local[1] | |
+ ndc_to_world[7] * world_to_local[4] | |
+ ndc_to_world[8] * world_to_local[7]; | |
let aabb_x_lt = -ndc_to_local[0] + ndc_to_local[2] + ndc_to_local[4]; | |
let aabb_x_lb = -ndc_to_local[0] - ndc_to_local[2] + ndc_to_local[4]; | |
let aabb_x_rt = ndc_to_local[0] + ndc_to_local[2] + ndc_to_local[4]; | |
let aabb_x_rb = ndc_to_local[0] - ndc_to_local[2] + ndc_to_local[4]; | |
let aabb_y_lt = -ndc_to_local[1] + ndc_to_local[3] + ndc_to_local[5]; | |
let aabb_y_lb = -ndc_to_local[1] + -ndc_to_local[3] + ndc_to_local[5]; | |
let aabb_y_rt = ndc_to_local[1] + ndc_to_local[3] + ndc_to_local[5]; | |
let aabb_y_rb = ndc_to_local[1] + -ndc_to_local[3] + ndc_to_local[5]; | |
let aabb_min_x = [aabb_x_lt, aabb_x_lb, aabb_x_rt, aabb_x_rb] | |
.iter() | |
.cloned() | |
.reduce(f32::min) | |
.unwrap(); | |
let aabb_max_x = [aabb_x_lt, aabb_x_lb, aabb_x_rt, aabb_x_rb] | |
.iter() | |
.cloned() | |
.reduce(f32::max) | |
.unwrap(); | |
let aabb_min_y = [aabb_y_lt, aabb_y_lb, aabb_y_rt, aabb_y_rb] | |
.iter() | |
.cloned() | |
.reduce(f32::min) | |
.unwrap(); | |
let aabb_max_y = [aabb_y_lt, aabb_y_lb, aabb_y_rt, aabb_y_rb] | |
.iter() | |
.cloned() | |
.reduce(f32::max) | |
.unwrap(); | |
let tile_width = tilemap_renderer.tilemap.tile_width; | |
let tile_height = tilemap_renderer.tilemap.tile_height; | |
let inv_tile_width = 1f32 / tile_width; | |
let inv_tile_height = 1f32 / tile_height; | |
let range_min_x = min( | |
tilemap_renderer.tilemap.tile_count_x, | |
max(0, (aabb_min_x * inv_tile_width) as isize) as usize, | |
); | |
let range_max_x = min( | |
tilemap_renderer.tilemap.tile_count_x, | |
max(0, (aabb_max_x * inv_tile_width).ceil() as isize) as usize, | |
); | |
let range_min_y = min( | |
tilemap_renderer.tilemap.tile_count_y, | |
max(0, (aabb_min_y * inv_tile_height) as isize) as usize, | |
); | |
let range_max_y = min( | |
tilemap_renderer.tilemap.tile_count_y, | |
max(0, (aabb_max_y * inv_tile_height).ceil() as isize) as usize, | |
); | |
let sprites = tilemap_renderer.tilemap.palette.sprites(); | |
for layer in &tilemap_renderer.tilemap.layers { | |
for y in range_min_y..range_max_y { | |
let base_index = | |
(tilemap_renderer.tilemap.tile_count_y - 1 - y) | |
* tilemap_renderer.tilemap.tile_count_x; | |
for x in range_min_x..range_max_x { | |
let sprite = match layer[base_index + x] { | |
0 => continue, | |
index => &sprites[index - 1], | |
}; | |
let texel_mapping = sprite.texel_mapping(); | |
let offset_x = x as f32 * tile_width; | |
let offset_y = y as f32 * tile_height; | |
per_instance_buffer.extend(&[ | |
matrix[0], | |
matrix[1], | |
matrix[2], | |
matrix[3], | |
matrix[4], | |
matrix[5], | |
matrix[6] + matrix[0] * offset_x + matrix[3] * offset_y, | |
matrix[7] + matrix[1] * offset_x + matrix[4] * offset_y, | |
matrix[8], | |
tile_width, | |
tile_height, | |
tilemap_renderer.color.r, | |
tilemap_renderer.color.g, | |
tilemap_renderer.color.b, | |
tilemap_renderer.color.a, | |
(texel_mapping.min().0 as f32 + 0.5f32) | |
/ texture.width() as f32, | |
(texel_mapping.min().1 as f32 + 0.5f32) | |
/ texture.height() as f32, | |
(texel_mapping.max().0 as f32 - 0.5f32) | |
/ texture.width() as f32, | |
(texel_mapping.max().1 as f32 - 0.5f32) | |
/ texture.height() as f32, | |
]); | |
instance_count += 1; | |
} | |
} | |
} | |
} | |
} | |
} | |
let per_instance_buffer = Arc::new(Buffer::from(per_instance_buffer.as_slice())); | |
renderer.enqueue_render_request( | |
instance_count, | |
2, | |
RenderMode::Trangles, | |
shader.clone(), | |
&|req| { | |
rendering_mgr.apply_common_shader_input(req); | |
req.uniform_f33("camera", camera_matrix_inverse); | |
req.uniform_texture(*texture_uniform_name, texture.clone()); | |
req.attribute("pos", (*buffer).clone(), 0); | |
req.attribute("uv", (*buffer).clone(), (size_of::<f32>() * 2) as _); | |
req.attribute_per_instance("transform", per_instance_buffer.clone(), 0); | |
req.attribute_per_instance( | |
"size", | |
per_instance_buffer.clone(), | |
(size_of::<f32>() * 9) as _, | |
); | |
req.attribute_per_instance( | |
"color", | |
per_instance_buffer.clone(), | |
(size_of::<f32>() * 11) as _, | |
); | |
req.attribute_per_instance( | |
"uv_rect", | |
per_instance_buffer.clone(), | |
(size_of::<f32>() * 15) as _, | |
); | |
}, | |
); | |
} | |
} | |
renderer.flush(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment