Skip to content

Instantly share code, notes, and snippets.

@AcrylicShrimp
Created August 12, 2021 16:12
Show Gist options
  • Save AcrylicShrimp/7569f1cca8ea1028d6af2525a97e6785 to your computer and use it in GitHub Desktop.
Save AcrylicShrimp/7569f1cca8ea1028d6af2525a97e6785 to your computer and use it in GitHub Desktop.
Rendering system logic
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