Skip to content

Commit

Permalink
Remove the white border in entity
Browse files Browse the repository at this point in the history
  • Loading branch information
kokosha committed Nov 21, 2024
1 parent cff299f commit 8506d84
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 33 deletions.
1 change: 1 addition & 0 deletions korangar/src/graphics/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ pub struct EntityInstruction {
pub color: Color,
pub mirror: bool,
pub entity_id: EntityId,
pub opaque: bool,
pub texture: Arc<Texture>,
}

Expand Down
5 changes: 4 additions & 1 deletion korangar/src/graphics/passes/forward/entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ pub(crate) struct InstanceData {
angle: f32,
curvature: f32,
mirror: u32,
opaque: u32,
texture_index: i32,
padding: [u32; 2],
padding: u32,
}

pub(crate) struct ForwardEntityDrawer {
Expand Down Expand Up @@ -265,6 +266,7 @@ impl Prepare for ForwardEntityDrawer {
depth_offset: instruction.depth_offset,
curvature: instruction.curvature,
mirror: instruction.mirror as u32,
opaque: instruction.opaque as u32,
texture_index,
padding: Default::default(),
});
Expand All @@ -289,6 +291,7 @@ impl Prepare for ForwardEntityDrawer {
depth_offset: instruction.depth_offset,
curvature: instruction.curvature,
mirror: instruction.mirror as u32,
opaque: instruction.opaque as u32,
texture_index: 0,
padding: Default::default(),
});
Expand Down
6 changes: 5 additions & 1 deletion korangar/src/graphics/passes/forward/shader/entity.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ struct InstanceData {
angle: f32,
curvature: f32,
mirror: u32,
opaque: u32,
texture_index: i32,
}

Expand All @@ -63,6 +64,7 @@ struct VertexOutput {
@location(6) @interpolate(flat) original_curvature: f32,
@location(7) angle: f32,
@location(8) color: vec4<f32>,
@location(9) opaque: u32,
}

struct FragmentOutput {
Expand Down Expand Up @@ -118,6 +120,7 @@ fn vs_main(
output.original_curvature = instance.curvature;
output.angle = instance.angle;
output.color = instance.color;
output.opaque = instance.opaque;
return output;
}

Expand All @@ -129,13 +132,14 @@ fn fs_main(input: VertexOutput) -> FragmentOutput {
let rotate = vec2(input.texture_coordinates.x - 0.5, input.texture_coordinates.y - 0.5) * mat2x2(cos_factor, sin_factor, -sin_factor, cos_factor);
let texture_coordinates = vec2(clamp(rotate.x + 0.5, 0.0, 1.0), clamp(rotate.y + 0.5, 0.0, 1.0));

let diffuse_color = textureSample(texture, texture_sampler, texture_coordinates);
var diffuse_color = textureSample(texture, texture_sampler, texture_coordinates);
let alpha_channel = textureSample(texture, nearest_sampler, texture_coordinates).a;

if (alpha_channel == 0.0) {
discard;
}

diffuse_color.a = select(diffuse_color.a, 1.0, input.opaque != 0u);
// Calculate which tile this fragment belongs to
let pixel_position = vec2<u32>(floor(input.position.xy));
let tile_x = pixel_position.x / TILE_SIZE;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ struct InstanceData {
angle: f32,
curvature: f32,
mirror: u32,
opaque: u32,
texture_index: i32,
}

Expand All @@ -64,6 +65,7 @@ struct VertexOutput {
@location(7) texture_index: i32,
@location(8) angle: f32,
@location(9) color: vec4<f32>,
@location(10) opaque: u32,
}

struct FragmentOutput {
Expand Down Expand Up @@ -121,6 +123,7 @@ fn vs_main(
output.texture_index = instance.texture_index;
output.angle = instance.angle;
output.color = instance.color;
output.opaque = instance.opaque;
return output;
}

Expand All @@ -132,13 +135,14 @@ fn fs_main(input: VertexOutput) -> FragmentOutput {
let rotate = vec2(input.texture_coordinates.x - 0.5, input.texture_coordinates.y - 0.5) * mat2x2(cos_factor, sin_factor, -sin_factor, cos_factor);
let texture_coordinates = vec2(clamp(rotate.x + 0.5, 0.0, 1.0), clamp(rotate.y + 0.5, 0.0, 1.0));

let diffuse_color = textureSample(textures[input.texture_index], texture_sampler, texture_coordinates);
var diffuse_color = textureSample(textures[input.texture_index], texture_sampler, texture_coordinates);
let alpha_channel = textureSample(textures[input.texture_index], nearest_sampler, texture_coordinates).a;

if (alpha_channel == 0.0) {
discard;
}

diffuse_color.a = select(diffuse_color.a, 1.0, input.opaque != 0u);
// Calculate which tile this fragment belongs to
let pixel_position = vec2<u32>(floor(input.position.xy));
let tile_x = pixel_position.x / TILE_SIZE;
Expand Down
155 changes: 125 additions & 30 deletions korangar/src/loaders/sprite/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::num::{NonZeroU32, NonZeroUsize};
use std::sync::Arc;

use image::{Pixel, Rgba, RgbaImage};
#[cfg(feature = "debug")]
use korangar_debug::logging::{print_debug, Colorize, Timer};
use korangar_interface::elements::PrototypeElement;
Expand All @@ -24,6 +25,8 @@ pub struct Sprite {
pub palette_size: usize,
#[hidden_element]
pub textures: Vec<Arc<Texture>>,
#[hidden_element]
pub vec_opaque: Vec<Arc<bool>>,
#[cfg(feature = "debug")]
sprite_data: SpriteData,
}
Expand Down Expand Up @@ -89,33 +92,7 @@ impl SpriteLoader {
let cloned_sprite_data = sprite_data.clone();

let palette = sprite_data.palette.unwrap(); // unwrap_or_default() as soon as i know what

let rgba_images: Vec<RgbaImageData> = sprite_data
.rgba_image_data
.iter()
.map(|image_data| {
// Revert the rows, the image is flipped upside down
// Convert the pixel from ABGR format to RGBA format
let width = image_data.width;
let data = image_data
.data
.chunks_exact(4 * width as usize)
.rev()
.flat_map(|pixels| {
pixels
.chunks_exact(4)
.flat_map(|pixel| [pixel[3], pixel[2], pixel[1], pixel[0]])
.collect::<Vec<u8>>()
})
.collect();

RgbaImageData {
width: image_data.width,
height: image_data.height,
data,
}
})
.collect();
let mut vec_opaque = Vec::new();

// TODO: Move this to an extension trait in `korangar_loaders`.
pub fn color_bytes(palette: &PaletteColor, index: u8) -> [u8; 4] {
Expand All @@ -136,17 +113,49 @@ impl SpriteLoader {
.flat_map(|palette_index| color_bytes(&palette.colors[*palette_index as usize], *palette_index))
.collect();

RgbaImageData {
let rgba_image_data = RgbaImageData {
width: image_data.width,
height: image_data.height,
data,
}
};
let is_opaque = SpriteLoader::is_opaque(&rgba_image_data);
(SpriteLoader::recolor_border(rgba_image_data), is_opaque)
});
let palette_size = palette_images.len();

let rgba_images: Vec<(RgbaImageData, bool)> = sprite_data
.rgba_image_data
.iter()
.map(|image_data| {
// Revert the rows, the image is flipped upside down
// Convert the pixel from ABGR format to RGBA format
let width = image_data.width;
let data = image_data
.data
.chunks_exact(4 * width as usize)
.rev()
.flat_map(|pixels| {
pixels
.chunks_exact(4)
.flat_map(|pixel| [pixel[3], pixel[2], pixel[1], pixel[0]])
.collect::<Vec<u8>>()
})
.collect();

let rgba_image_data = RgbaImageData {
width: image_data.width,
height: image_data.height,
data,
};
let is_opaque = SpriteLoader::is_opaque(&rgba_image_data);
(SpriteLoader::recolor_border(rgba_image_data), is_opaque)
})
.collect();

let textures = palette_images
.chain(rgba_images)
.map(|image_data| {
.map(|(image_data, is_opaque)| {
vec_opaque.push(Arc::new(is_opaque));
let texture = Texture::new_with_data(
&self.device,
&self.queue,
Expand Down Expand Up @@ -175,6 +184,7 @@ impl SpriteLoader {
textures,
#[cfg(feature = "debug")]
sprite_data: cloned_sprite_data,
vec_opaque,
});
let _ = self.cache.insert(path.to_string(), sprite.clone());

Expand All @@ -190,4 +200,89 @@ impl SpriteLoader {
None => self.load(path),
}
}

// Recolor the transparent border with the mean of adjacent color.
fn recolor_border(rgba_image_data: RgbaImageData) -> RgbaImageData {
let rgba_image = RgbaImage::from_raw(
rgba_image_data.width as u32,
rgba_image_data.height as u32,
rgba_image_data.data,
)
.unwrap();

let mut rgba_image_result = rgba_image.clone();

let pixel_width = rgba_image_data.width as u32;
let pixel_height = rgba_image_data.height as u32;
let width = rgba_image_data.width as i32;
let height = rgba_image_data.height as i32;

for x in 0..pixel_width {
let x_i32 = x as i32;
for y in 0..pixel_height {
let pixel = rgba_image.get_pixel(x, y).channels();
if pixel[3] != 0 {
continue;
}
let y_i32 = y as i32;
let mut total_r = 0;
let mut total_g = 0;
let mut total_b = 0;
let mut pixel_count = 0;
for dx in -1..=1 {
for dy in -1..=1 {
if dx == 0 && dy == 0 {
continue;
}
let nx = x_i32 + dx;
let ny = y_i32 + dy;
if 0 <= nx && nx < width && 0 <= ny && ny < height {
let close_pixel = rgba_image.get_pixel(nx as u32, ny as u32).channels();
if close_pixel[3] != 0 {
total_r += close_pixel[0] as u32;
total_g += close_pixel[1] as u32;
total_b += close_pixel[2] as u32;
pixel_count += 1;
}
}
}
}
if pixel_count != 0 {
let mean_r = total_r / pixel_count;
let mean_g = total_g / pixel_count;
let mean_b = total_b / pixel_count;
let color = Rgba([mean_r as u8, mean_g as u8, mean_b as u8, 0]);
rgba_image_result.put_pixel(x, y, color);
}
}
}

RgbaImageData {
width: rgba_image_data.width,
height: rgba_image_data.height,
data: rgba_image_result.into_raw(),
}
}

fn is_opaque(rgba_image_data: &RgbaImageData) -> bool {
let rgba_image = RgbaImage::from_raw(
rgba_image_data.width as u32,
rgba_image_data.height as u32,
rgba_image_data.data.clone(),
)
.unwrap();

let pixel_width = rgba_image_data.width as u32;
let pixel_height = rgba_image_data.height as u32;

for x in 0..pixel_width {
for y in 0..pixel_height {
let pixel = rgba_image.get_pixel(x, y).channels();
if pixel[3] != 0 && pixel[3] != 255 {
return false;
}
}
}
return true;
}
}
2 changes: 2 additions & 0 deletions korangar/src/world/animation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ impl AnimationData {
let animation_index = frame_part.animation_index;
let sprite_number = frame_part.sprite_number;
let texture = &self.animation_pair[animation_index].sprites.textures[sprite_number];
let opaque = &self.animation_pair[animation_index].sprites.vec_opaque[sprite_number];

// The constant 10.0 is a magic scale factor of an image.
// The vertex position is calculated from the center of image, so we need to
Expand Down Expand Up @@ -145,6 +146,7 @@ impl AnimationData {
angle: frame_part.angle,
color: frame_part.color,
mirror: frame_part.mirror,
opaque: **opaque && (frame_part.color.alpha == 1.0),
entity_id,
texture: texture.clone(),
});
Expand Down

0 comments on commit 8506d84

Please sign in to comment.