use super::positioning::{ImageOrigin, MaskFacePart, MaskFaceParts};
use glam::{vec2, Mat4, Quat, Vec2, Vec4};
pub const FACE_OUTPUT_SIZE: u16 = 512;
use crate::model::{Model2d, Vertex};
use crate::{TEX_SCALE_X, TEX_SCALE_Y};
pub use bytemuck::cast_slice;
use vee_parse::NxCharInfo;
use vee_resources::color::nx::{modulate, ColorModulated};
use vee_resources::packing::Float16;
use vee_resources::tex::{ResourceTexture, TextureElement};
const NON_REPLACEMENT: [f32; 4] = [f32::NAN, f32::NAN, f32::NAN, f32::NAN];
pub struct MaskModels {
pub left_eye: Model2d,
pub right_eye: Model2d,
pub left_brow: Option<Model2d>,
pub right_brow: Option<Model2d>,
pub left_mustache: Option<Model2d>,
pub right_mustache: Option<Model2d>,
pub mouth: Model2d,
pub mole: Option<Model2d>,
}
impl MaskModels {
pub fn all(self) -> Vec<Model2d> {
[
Some(self.left_eye),
Some(self.right_eye),
self.left_brow,
self.right_brow,
Some(self.mouth),
self.left_mustache,
self.right_mustache,
self.mole,
]
.into_iter()
.flatten()
.collect()
}
pub fn brows(self) -> Vec<Model2d> {
[self.left_brow, self.right_brow]
.into_iter()
.flatten()
.collect()
}
}
pub fn mask_texture_meshes(
char: &NxCharInfo,
res_texture: &ResourceTexture,
file_texture: &[u8],
) -> MaskModels {
let mask = MaskFaceParts::init(char, 256.0);
let make_shape = |part: MaskFacePart, modulated: ColorModulated, tex_data: TextureElement| {
let (vertices, indices, mtx) = quad(
part.x,
part.y,
part.width,
part.height,
part.angle_deg,
part.origin,
256.0,
);
if part.width <= 0.0 || part.height <= 0.0 {
return None;
};
let tex = tex_data.get_image(file_texture).unwrap()?;
Some(Model2d {
vertices,
indices,
tex: image::DynamicImage::ImageRgba8(tex).flipv(),
mvp_matrix: mtx,
modulation: modulate(modulated, char),
opaque: None,
label: Some(format!("{modulated:?}")),
})
};
let left_eye = make_shape(
mask.eye[0],
ColorModulated::Eye,
res_texture.eye[char.eye_type as usize],
);
let right_eye = make_shape(
mask.eye[1],
ColorModulated::Eye,
res_texture.eye[char.eye_type as usize],
);
let left_brow = make_shape(
mask.eyebrow[0],
ColorModulated::Eyebrow,
res_texture.eyebrow[char.eyebrow_type as usize],
);
let right_brow = make_shape(
mask.eyebrow[1],
ColorModulated::Eyebrow,
res_texture.eyebrow[char.eyebrow_type as usize],
);
let mouth = make_shape(
mask.mouth,
ColorModulated::Mouth,
res_texture.mouth[char.mouth_type as usize],
);
let left_mustache = make_shape(
mask.mustache[0],
ColorModulated::Mustache,
res_texture.mustache[char.mustache_type as usize],
);
let right_mustache = make_shape(
mask.mustache[1],
ColorModulated::Mustache,
res_texture.mustache[char.mustache_type as usize],
);
let mole = make_shape(
mask.mole,
ColorModulated::Mole,
res_texture.mole[if char.mole_type == 0 { 0 } else { 1 }],
);
MaskModels {
left_eye: left_eye.unwrap(),
right_eye: right_eye.unwrap(),
left_brow,
right_brow,
left_mustache,
right_mustache,
mouth: mouth.unwrap(),
mole,
}
}
pub fn model_view_matrix(translation: Vec2, scale: Vec2, rot_z: f32) -> Mat4 {
Mat4::from_scale_rotation_translation(
(scale * vec2(TEX_SCALE_X, TEX_SCALE_Y)).extend(1.0),
Quat::from_rotation_z(-rot_z.to_radians()),
translation.extend(0.0),
)
}
fn v2(x: f32, y: f32) -> [f32; 3] {
[x, y, 0.0]
}
const OPENGL_TO_WEBGPU_Y_FLIP: Mat4 = Mat4::from_cols(Vec4::X, Vec4::NEG_Y, Vec4::Z, Vec4::W);
pub fn quad(
x: f32,
y: f32,
width: f32,
height: f32,
rot_z: f32,
origin: ImageOrigin,
resolution: f32,
) -> (Vec<Vertex>, Vec<u32>, Mat4) {
let base_x: f32;
let s0: f32;
let s1: f32;
let mv_mtx = model_view_matrix(vec2(x, resolution - y), vec2(width, height), rot_z);
let p_mtx = Mat4::orthographic_rh(0.0, resolution, 0.0, resolution, 200.0, -200.0);
let mvp_mtx = p_mtx * mv_mtx;
match origin {
ImageOrigin::Center => {
base_x = -0.5;
s0 = 1.0;
s1 = 0.0;
}
ImageOrigin::Right => {
base_x = -1.0;
s0 = 1.0;
s1 = 0.0;
}
ImageOrigin::Left | ImageOrigin::Ignore => {
base_x = 0.0;
s0 = 0.0;
s1 = 1.0;
}
}
(
vec![
Vertex {
position: v2(1.0 + base_x, -0.5).map(Float16::from_f32),
_pad: 0,
tex_coords: [s0, 0.0].map(Float16::from_f32),
normal: [0.0, 0.0, 0.0],
},
Vertex {
position: v2(1.0 + base_x, 0.5).map(Float16::from_f32),
_pad: 0,
tex_coords: [s0, 1.0].map(Float16::from_f32),
normal: [0.0, 0.0, 0.0],
},
Vertex {
position: v2(base_x, 0.5).map(Float16::from_f32),
_pad: 0,
tex_coords: [s1, 1.0].map(Float16::from_f32),
normal: [0.0, 0.0, 0.0],
},
Vertex {
position: v2(base_x, -0.5).map(Float16::from_f32),
_pad: 0,
tex_coords: [s1, 0.0].map(Float16::from_f32),
normal: [0.0, 0.0, 0.0],
},
],
vec![0, 1, 2, 0, 2, 3],
mvp_mtx,
)
}
pub fn trivial_quad() -> (Vec<Vertex>, Vec<u32>) {
(
vec![
Vertex {
position: [0.5, -0.5, 0.0].map(Float16::from_f32),
_pad: 0,
tex_coords: [0.0, 0.0].map(Float16::from_f32),
normal: [0.0, 0.0, 0.0],
},
Vertex {
position: [0.5, 0.5, 0.0].map(Float16::from_f32),
_pad: 0,
tex_coords: [0.0, 1.0].map(Float16::from_f32),
normal: [0.0, 0.0, 0.0],
},
Vertex {
position: [-0.5, 0.5, 0.0].map(Float16::from_f32),
_pad: 0,
tex_coords: [1.0, 1.0].map(Float16::from_f32),
normal: [0.0, 0.0, 0.0],
},
Vertex {
position: [-0.5, -0.5, 0.0].map(Float16::from_f32),
_pad: 0,
tex_coords: [1.0, 0.0].map(Float16::from_f32),
normal: [0.0, 0.0, 0.0],
},
],
vec![0, 1, 2, 0, 2, 3],
)
}
pub fn bgr_to_rgb([b, g, r, a]: [f32; 4]) -> [f32; 4] {
[r, g, b, a]
}
#[cfg(test)]
mod tests {
}