mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Enable pixel art resizing for voxel based ui elements
This commit is contained in:
parent
e5d841e62f
commit
460f5e6f26
@ -1,4 +1,4 @@
|
||||
use crate::ui::img_ids::{BlankGraphic, ImageGraphic, VoxelGraphic, VoxelMs9Graphic};
|
||||
use crate::ui::img_ids::{BlankGraphic, ImageGraphic, VoxelGraphic, VoxelPixArtGraphic};
|
||||
|
||||
// TODO: Combine with image_ids, see macro definition
|
||||
rotation_image_ids! {
|
||||
@ -104,7 +104,7 @@ image_ids! {
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
<VoxelMs9Graphic>
|
||||
<VoxelPixArtGraphic>
|
||||
|
||||
// Skill Icons
|
||||
twohsword_m1: "voxygen.element.icons.2hsword_m1",
|
||||
@ -119,7 +119,6 @@ image_ids! {
|
||||
staff_m2: "voxygen.element.icons.staff_m2",
|
||||
flyingrod_m1: "voxygen.element.icons.debug_wand_m1",
|
||||
flyingrod_m2: "voxygen.element.icons.debug_wand_m2",
|
||||
|
||||
charge: "voxygen.element.icons.skill_charge_3",
|
||||
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::ui::{Graphic, Transform, Ui};
|
||||
use crate::ui::{Graphic, SampleStrat, Transform, Ui};
|
||||
use common::{
|
||||
assets::{self, watch::ReloadIndicator, Asset},
|
||||
comp::item::{Armor, Consumable, Ingredient, Item, ItemKind, Tool},
|
||||
@ -47,7 +47,7 @@ impl ImageSpec {
|
||||
stretch: false,
|
||||
..Default::default()
|
||||
},
|
||||
None,
|
||||
SampleStrat::None,
|
||||
),
|
||||
ImageSpec::VoxTrans(specifier, offset, [rot_x, rot_y, rot_z], zoom) => Graphic::Voxel(
|
||||
graceful_load_vox(&specifier),
|
||||
@ -60,7 +60,7 @@ impl ImageSpec {
|
||||
orth: true, // TODO: Is this what we want here? @Pfau
|
||||
stretch: false,
|
||||
},
|
||||
None,
|
||||
SampleStrat::None,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -1668,7 +1668,7 @@ impl<'a> Widget for SettingsWindow<'a> {
|
||||
Text::new("Fullscreen")
|
||||
.font_size(14)
|
||||
.font_id(self.fonts.cyri)
|
||||
.down_from(state.ids.fluid_mode_list, 125.0)
|
||||
.down_from(state.ids.fluid_mode_list, 8.0)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.fullscreen_label, ui);
|
||||
|
||||
|
@ -4,7 +4,7 @@ use crate::{
|
||||
meta::CharacterData,
|
||||
render::{Consts, Globals, Renderer},
|
||||
ui::{
|
||||
img_ids::{BlankGraphic, ImageGraphic, VoxelGraphic, VoxelMs9Graphic},
|
||||
img_ids::{BlankGraphic, ImageGraphic, VoxelGraphic, VoxelSs9Graphic},
|
||||
ImageFrame, ImageSlider, Tooltip, Tooltipable, Ui,
|
||||
},
|
||||
GlobalState,
|
||||
@ -176,7 +176,7 @@ image_ids! {
|
||||
// Info Window
|
||||
info_frame: "voxygen.element.frames.info_frame",
|
||||
|
||||
<VoxelMs9Graphic>
|
||||
<VoxelSs9Graphic>
|
||||
delete_button: "voxygen.element.buttons.x_red",
|
||||
delete_button_hover: "voxygen.element.buttons.x_red_hover",
|
||||
delete_button_press: "voxygen.element.buttons.x_red_press",
|
||||
|
@ -1,6 +1,8 @@
|
||||
mod pixel_art;
|
||||
mod renderer;
|
||||
|
||||
pub use renderer::{SampleStrat, Transform};
|
||||
|
||||
use crate::render::{Renderer, Texture};
|
||||
use dot_vox::DotVoxData;
|
||||
use guillotiere::{size2, SimpleAtlasAllocator};
|
||||
@ -11,30 +13,10 @@ use pixel_art::resize_pixel_art;
|
||||
use std::sync::Arc;
|
||||
use vek::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Transform {
|
||||
pub ori: Quaternion<f32>,
|
||||
pub offset: Vec3<f32>,
|
||||
pub zoom: f32,
|
||||
pub orth: bool,
|
||||
pub stretch: bool,
|
||||
}
|
||||
impl Default for Transform {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
ori: Quaternion::identity(),
|
||||
offset: Vec3::zero(),
|
||||
zoom: 1.0,
|
||||
orth: true,
|
||||
stretch: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Graphic {
|
||||
Image(Arc<DynamicImage>),
|
||||
Voxel(Arc<DotVoxData>, Transform, Option<u8>),
|
||||
Voxel(Arc<DotVoxData>, Transform, SampleStrat),
|
||||
Blank,
|
||||
}
|
||||
|
||||
@ -287,11 +269,11 @@ fn draw_graphic(graphic_map: &GraphicMap, graphic_id: Id, dims: Vec2<u16>) -> Op
|
||||
u32::from(dims.x),
|
||||
u32::from(dims.y),
|
||||
)),
|
||||
Some(Graphic::Voxel(ref vox, trans, min_samples)) => Some(renderer::draw_vox(
|
||||
Some(Graphic::Voxel(ref vox, trans, sample_strat)) => Some(renderer::draw_vox(
|
||||
&vox.as_ref().into(),
|
||||
dims,
|
||||
trans.clone(),
|
||||
*min_samples,
|
||||
*sample_strat,
|
||||
)),
|
||||
None => {
|
||||
warn!("A graphic was requested via an id which is not in use");
|
||||
|
@ -2,6 +2,7 @@ use common::util::{linear_to_srgba, srgba_to_linear};
|
||||
/// Pixel art scaling
|
||||
/// Note: The current ui is locked to the pixel grid with little animation, if we want smoothly
|
||||
/// moving pixel art this should be done in the shaders
|
||||
/// useful links: https://gitlab.com/veloren/veloren/issues/257
|
||||
use image::RgbaImage;
|
||||
use vek::*;
|
||||
|
||||
@ -17,16 +18,16 @@ const EPSILON: f32 = 0.0001;
|
||||
// where o1 and o2 are the opaque colors of the two areas
|
||||
// now say the areas are actually translucent and these opaque colors are derived by blending with a
|
||||
// common backgound color b
|
||||
// E2: o1 = c1 * a1 + g * (1 - a1)
|
||||
// E3: o2 = c2 * a2 + g * (1 - a2)
|
||||
// E2: o1 = c1 * a1 + b * (1 - a1)
|
||||
// E3: o2 = c2 * a2 + b * (1 - a2)
|
||||
// we want to find the combined color (c3) and combined alpha (a3) such that
|
||||
// E4: o3 = c3 * a3 + g * (1 - a3)
|
||||
// E4: o3 = c3 * a3 + b * (1 - a3)
|
||||
// substitution of E2 and E3 into E1 gives
|
||||
// E5: o3 = A1 * (c1 * a1 + g * (1 - a1)) + A2 * (c2 * a2 + g * (1 - a2))
|
||||
// E5: o3 = A1 * (c1 * a1 + b * (1 - a1)) + A2 * (c2 * a2 + b * (1 - a2))
|
||||
// combining E4 and E5 then separting like terms into separte equations gives
|
||||
// E6: c3 * a3 = A1 * c1 * a1 + A2 * c2 * a2
|
||||
// E7: g * (1 - a3) = A1 * g * (1 - a1) + A2 * g * (1 - a2)
|
||||
// dropping g from E7 and solving for a3
|
||||
// E7: b * (1 - a3) = A1 * b * (1 - a1) + A2 * b * (1 - a2)
|
||||
// dropping b from E7 and solving for a3
|
||||
// E8: a3 = 1 - A1 * (1 - a1) + A2 * (1 - a2)
|
||||
// we can now calculate the combined alpha value
|
||||
// and E6 can then be solved for c3
|
||||
|
@ -1,4 +1,3 @@
|
||||
use super::Transform;
|
||||
use common::{
|
||||
figure::Segment,
|
||||
util::{linear_to_srgba, srgba_to_linear},
|
||||
@ -8,6 +7,33 @@ use euc::{buffer::Buffer2d, rasterizer, Pipeline};
|
||||
use image::{DynamicImage, RgbaImage};
|
||||
use vek::*;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum SampleStrat {
|
||||
None,
|
||||
SuperSampling(u8),
|
||||
PixelCoverage,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Transform {
|
||||
pub ori: Quaternion<f32>,
|
||||
pub offset: Vec3<f32>,
|
||||
pub zoom: f32,
|
||||
pub orth: bool,
|
||||
pub stretch: bool,
|
||||
}
|
||||
impl Default for Transform {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
ori: Quaternion::identity(),
|
||||
offset: Vec3::zero(),
|
||||
zoom: 1.0,
|
||||
orth: true,
|
||||
stretch: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Voxel {
|
||||
mvp: Mat4<f32>,
|
||||
}
|
||||
@ -48,7 +74,7 @@ impl<'a> Pipeline for Voxel {
|
||||
) -> ([f32; 3], Self::VsOut) {
|
||||
let light = Rgba::from_opaque(Rgb::from(*ao_level as f32 / 4.0 + 0.25));
|
||||
let color = light * srgba_to_linear(Rgba::from_opaque(*col));
|
||||
let position = Vec3::from(self.mvp * Vec4::from_point(*pos)).into_array();
|
||||
let position = (self.mvp * Vec4::from_point(*pos)).xyz().into_array();
|
||||
(position, color)
|
||||
}
|
||||
#[inline(always)]
|
||||
@ -63,10 +89,33 @@ pub fn draw_vox(
|
||||
segment: &Segment,
|
||||
output_size: Vec2<u16>,
|
||||
transform: Transform,
|
||||
min_samples: Option<u8>,
|
||||
sample_strat: SampleStrat,
|
||||
) -> RgbaImage {
|
||||
let scale = min_samples.map_or(1.0, |s| s as f32).sqrt().ceil() as usize;
|
||||
let dims = output_size.map(|e| e as usize * scale).into_array();
|
||||
let output_size = output_size.map(|e| e as usize);
|
||||
|
||||
let ori_mat = Mat4::from(transform.ori);
|
||||
let rotated_segment_dims = (ori_mat * Vec4::from_direction(segment.size().map(|e| e as f32)))
|
||||
.xyz()
|
||||
.map(|e| e.abs());
|
||||
|
||||
let dims = match sample_strat {
|
||||
SampleStrat::None => output_size,
|
||||
SampleStrat::SuperSampling(min_samples) => {
|
||||
output_size * (min_samples as f32).sqrt().ceil() as usize
|
||||
}
|
||||
// Assumes
|
||||
// - rotations are multiples of 90 degrees
|
||||
// - the projection is orthographic
|
||||
// - no translation or zooming is performed
|
||||
// - stretch is enabled
|
||||
SampleStrat::PixelCoverage => Vec2::new(
|
||||
rotated_segment_dims.x.round() as usize,
|
||||
rotated_segment_dims.y.round() as usize,
|
||||
),
|
||||
}
|
||||
.into_array();
|
||||
|
||||
// Rendering buffers
|
||||
let mut color = Buffer2d::new(dims, [0; 4]);
|
||||
let mut depth = Buffer2d::new(dims, 1.0);
|
||||
|
||||
@ -92,13 +141,13 @@ pub fn draw_vox(
|
||||
} * Mat4::scaling_3d(
|
||||
// TODO replace with camera-like parameters?
|
||||
if transform.stretch {
|
||||
Vec3::new(2.0 / w, 2.0 / d, 2.0 / h) // Only works with flipped models :(
|
||||
rotated_segment_dims.map(|e| 2.0 / e)
|
||||
} else {
|
||||
let s = w.max(h).max(d);
|
||||
Vec3::new(2.0 / s, 2.0 / s, 2.0 / s)
|
||||
Vec3::from(2.0 / s)
|
||||
} * transform.zoom,
|
||||
) * Mat4::translation_3d(transform.offset)
|
||||
* Mat4::from(transform.ori)
|
||||
* ori_mat
|
||||
* Mat4::translation_3d([-w / 2.0, -h / 2.0, -d / 2.0]);
|
||||
|
||||
Voxel { mvp }.draw::<rasterizer::Triangles<_>, _>(
|
||||
@ -107,29 +156,33 @@ pub fn draw_vox(
|
||||
&mut depth,
|
||||
);
|
||||
|
||||
let image = DynamicImage::ImageRgba8(
|
||||
RgbaImage::from_vec(
|
||||
dims[0] as u32,
|
||||
dims[1] as u32,
|
||||
color
|
||||
.as_ref()
|
||||
.iter()
|
||||
.flatten()
|
||||
.cloned()
|
||||
.collect::<Vec<u8>>(),
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
if scale > 1 {
|
||||
image.resize_exact(
|
||||
let rgba_img = RgbaImage::from_vec(
|
||||
dims[0] as u32,
|
||||
dims[1] as u32,
|
||||
color
|
||||
.as_ref()
|
||||
.iter()
|
||||
.flatten()
|
||||
.copied()
|
||||
.collect::<Vec<u8>>(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
match sample_strat {
|
||||
SampleStrat::None => rgba_img,
|
||||
SampleStrat::SuperSampling(_) => DynamicImage::ImageRgba8(rgba_img)
|
||||
.resize_exact(
|
||||
output_size.x as u32,
|
||||
output_size.y as u32,
|
||||
image::FilterType::Triangle,
|
||||
)
|
||||
.to_rgba(),
|
||||
SampleStrat::PixelCoverage => super::pixel_art::resize_pixel_art(
|
||||
&rgba_img,
|
||||
output_size.x as u32,
|
||||
output_size.y as u32,
|
||||
image::FilterType::Triangle,
|
||||
)
|
||||
} else {
|
||||
image
|
||||
),
|
||||
}
|
||||
.to_rgba()
|
||||
}
|
||||
|
||||
fn ao_level(side1: bool, corner: bool, side2: bool) -> u8 {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::{Graphic, Transform};
|
||||
use super::{Graphic, SampleStrat, Transform};
|
||||
use common::assets::{load, Error};
|
||||
use dot_vox::DotVoxData;
|
||||
use image::DynamicImage;
|
||||
@ -25,9 +25,10 @@ impl<'a> GraphicCreator<'a> for ImageGraphic {
|
||||
}
|
||||
|
||||
pub enum VoxelGraphic {}
|
||||
pub enum VoxelMsGraphic {}
|
||||
pub enum VoxelMs4Graphic {}
|
||||
pub enum VoxelMs9Graphic {}
|
||||
pub enum VoxelSsGraphic {}
|
||||
pub enum VoxelSs4Graphic {}
|
||||
pub enum VoxelSs9Graphic {}
|
||||
pub enum VoxelPixArtGraphic {}
|
||||
|
||||
impl<'a> GraphicCreator<'a> for VoxelGraphic {
|
||||
type Specifier = &'a str;
|
||||
@ -38,11 +39,11 @@ impl<'a> GraphicCreator<'a> for VoxelGraphic {
|
||||
ori: Quaternion::rotation_x(-std::f32::consts::PI / 2.0),
|
||||
..Default::default()
|
||||
},
|
||||
None,
|
||||
SampleStrat::None,
|
||||
))
|
||||
}
|
||||
}
|
||||
impl<'a> GraphicCreator<'a> for VoxelMsGraphic {
|
||||
impl<'a> GraphicCreator<'a> for VoxelSsGraphic {
|
||||
type Specifier = (&'a str, u8);
|
||||
fn new_graphic(specifier: Self::Specifier) -> Result<Graphic, Error> {
|
||||
Ok(Graphic::Voxel(
|
||||
@ -51,11 +52,11 @@ impl<'a> GraphicCreator<'a> for VoxelMsGraphic {
|
||||
ori: Quaternion::rotation_x(-std::f32::consts::PI / 2.0),
|
||||
..Default::default()
|
||||
},
|
||||
Some(specifier.1),
|
||||
SampleStrat::SuperSampling(specifier.1),
|
||||
))
|
||||
}
|
||||
}
|
||||
impl<'a> GraphicCreator<'a> for VoxelMs4Graphic {
|
||||
impl<'a> GraphicCreator<'a> for VoxelSs4Graphic {
|
||||
type Specifier = &'a str;
|
||||
fn new_graphic(specifier: Self::Specifier) -> Result<Graphic, Error> {
|
||||
Ok(Graphic::Voxel(
|
||||
@ -64,11 +65,11 @@ impl<'a> GraphicCreator<'a> for VoxelMs4Graphic {
|
||||
ori: Quaternion::rotation_x(-std::f32::consts::PI / 2.0),
|
||||
..Default::default()
|
||||
},
|
||||
Some(4),
|
||||
SampleStrat::SuperSampling(4),
|
||||
))
|
||||
}
|
||||
}
|
||||
impl<'a> GraphicCreator<'a> for VoxelMs9Graphic {
|
||||
impl<'a> GraphicCreator<'a> for VoxelSs9Graphic {
|
||||
type Specifier = &'a str;
|
||||
fn new_graphic(specifier: Self::Specifier) -> Result<Graphic, Error> {
|
||||
Ok(Graphic::Voxel(
|
||||
@ -77,7 +78,20 @@ impl<'a> GraphicCreator<'a> for VoxelMs9Graphic {
|
||||
ori: Quaternion::rotation_x(-std::f32::consts::PI / 2.0),
|
||||
..Default::default()
|
||||
},
|
||||
Some(9),
|
||||
SampleStrat::SuperSampling(9),
|
||||
))
|
||||
}
|
||||
}
|
||||
impl<'a> GraphicCreator<'a> for VoxelPixArtGraphic {
|
||||
type Specifier = &'a str;
|
||||
fn new_graphic(specifier: Self::Specifier) -> Result<Graphic, Error> {
|
||||
Ok(Graphic::Voxel(
|
||||
load::<DotVoxData>(specifier)?,
|
||||
Transform {
|
||||
ori: Quaternion::rotation_x(-std::f32::consts::PI / 2.0),
|
||||
..Default::default()
|
||||
},
|
||||
SampleStrat::PixelCoverage,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ pub mod img_ids;
|
||||
mod font_ids;
|
||||
|
||||
pub use event::Event;
|
||||
pub use graphic::{Graphic, Transform};
|
||||
pub use graphic::{Graphic, SampleStrat, Transform};
|
||||
pub use scale::{Scale, ScaleMode};
|
||||
pub use widgets::{
|
||||
image_frame::ImageFrame,
|
||||
|
Loading…
Reference in New Issue
Block a user