mirror of
https://github.com/griffi-gh/hUI.git
synced 2024-12-03 20:28:42 -06:00
hui-painter stuff i forgor to commit :p
This commit is contained in:
parent
61989c7a79
commit
e0d370844a
|
@ -164,4 +164,5 @@
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h2>MSRV</h2>
|
<h2>MSRV</h2>
|
||||||
1.75
|
1.80 (or latest stable at the time of the last major release)<br>
|
||||||
|
bumps to MSRV are considered a breaking change
|
||||||
|
|
|
@ -4,7 +4,7 @@ description = "Derive macros for hUI"
|
||||||
repository = "https://github.com/griffi-gh/hui"
|
repository = "https://github.com/griffi-gh/hui"
|
||||||
readme = "../README.md"
|
readme = "../README.md"
|
||||||
authors = ["griffi-gh <prasol258@gmail.com>"]
|
authors = ["griffi-gh <prasol258@gmail.com>"]
|
||||||
rust-version = "1.75"
|
rust-version = "1.80"
|
||||||
version = "0.1.0-alpha.5"
|
version = "0.1.0-alpha.5"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
|
|
|
@ -5,7 +5,7 @@ repository = "https://github.com/griffi-gh/hui"
|
||||||
readme = "../README.md"
|
readme = "../README.md"
|
||||||
authors = ["griffi-gh <prasol258@gmail.com>"]
|
authors = ["griffi-gh <prasol258@gmail.com>"]
|
||||||
version = "0.1.0-alpha.5"
|
version = "0.1.0-alpha.5"
|
||||||
rust-version = "1.75"
|
rust-version = "1.80"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
publish = true
|
publish = true
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "hui-painter"
|
name = "hui-painter"
|
||||||
description = "UI rendering middleware for hUI, abstracts away triangulation, text rendering and all the other hard stuff."
|
description = "UI rendering middleware for hUI, abstracts away triangulation, text layout and rendering and all the other hard stuff."
|
||||||
repository = "https://github.com/griffi-gh/hui"
|
repository = "https://github.com/griffi-gh/hui"
|
||||||
readme = "../README.md"
|
readme = "../README.md"
|
||||||
authors = ["griffi-gh <prasol258@gmail.com>"]
|
authors = ["griffi-gh <prasol258@gmail.com>"]
|
||||||
|
@ -10,7 +10,6 @@ edition = "2021"
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
publish = true
|
publish = true
|
||||||
include = [
|
include = [
|
||||||
"assets/**/*",
|
|
||||||
"src/**/*.rs",
|
"src/**/*.rs",
|
||||||
"Cargo.toml",
|
"Cargo.toml",
|
||||||
]
|
]
|
||||||
|
@ -18,3 +17,7 @@ include = [
|
||||||
[dependencies]
|
[dependencies]
|
||||||
hui-shared = { version = "0.1.0-alpha.5", path = "../hui-shared" }
|
hui-shared = { version = "0.1.0-alpha.5", path = "../hui-shared" }
|
||||||
glam = "0.28"
|
glam = "0.28"
|
||||||
|
log = "0.4"
|
||||||
|
rect_packer = "0.2" # TODO: use sth else like `crunch` instead?
|
||||||
|
hashbrown = "0.14"
|
||||||
|
nohash-hasher = "0.2"
|
||||||
|
|
|
@ -1,16 +1,5 @@
|
||||||
//TODO painter rewrite
|
pub mod paint;
|
||||||
|
pub mod texture;
|
||||||
mod rect;
|
|
||||||
pub use rect::PaintRectParams;
|
|
||||||
|
|
||||||
pub struct PaintTransformParams {
|
|
||||||
transform: glam::Affine2,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum PaintCommand {
|
|
||||||
Rect(PaintRectParams),
|
|
||||||
Transform(PaintTransformParams, Box<PaintCommand>),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Painter {
|
pub struct Painter {
|
||||||
|
|
||||||
|
|
4
hui-painter/src/paint.rs
Normal file
4
hui-painter/src/paint.rs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
//TODO painter rewrite
|
||||||
|
|
||||||
|
pub mod command;
|
||||||
|
pub mod buffer;
|
27
hui-painter/src/paint/buffer.rs
Normal file
27
hui-painter/src/paint/buffer.rs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
use glam::{Vec2, Vec4};
|
||||||
|
|
||||||
|
pub struct Vertex {
|
||||||
|
pub position: Vec2, //Vec3,
|
||||||
|
pub uv: Vec2,
|
||||||
|
pub color: Vec4,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PaintBuffer {
|
||||||
|
pub vertices: Vec<Vertex>,
|
||||||
|
pub indices: Vec<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PaintBuffer {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
vertices: Vec::new(),
|
||||||
|
indices: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PaintBuffer {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
14
hui-painter/src/paint/command.rs
Normal file
14
hui-painter/src/paint/command.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
use crate::paint::buffer::PaintBuffer;
|
||||||
|
|
||||||
|
mod transform;
|
||||||
|
pub use transform::PaintTransform;
|
||||||
|
|
||||||
|
mod rectangle;
|
||||||
|
pub use rectangle::PaintRectangle;
|
||||||
|
|
||||||
|
mod text;
|
||||||
|
pub use text::PaintText;
|
||||||
|
|
||||||
|
pub trait PaintCommand {
|
||||||
|
fn paint(&self, into: &mut PaintBuffer);
|
||||||
|
}
|
46
hui-painter/src/paint/command/rectangle.rs
Normal file
46
hui-painter/src/paint/command/rectangle.rs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
use glam::{Vec2, vec2};
|
||||||
|
use hui_shared::{color, rect::{Corners, FillColor}};
|
||||||
|
use crate::paint::{
|
||||||
|
buffer::PaintBuffer,
|
||||||
|
command::PaintCommand,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct PaintRectangle {
|
||||||
|
/// Color of the rectangle.
|
||||||
|
pub color: FillColor,
|
||||||
|
|
||||||
|
/// Texture to use for the rectangle.
|
||||||
|
pub texture: Option<u32>,
|
||||||
|
|
||||||
|
/// UV coords inside the texture
|
||||||
|
pub texture_uv: Corners<Vec2>,
|
||||||
|
|
||||||
|
/// Border width.
|
||||||
|
pub border_radius: Corners<f32>,
|
||||||
|
|
||||||
|
/// Border color.
|
||||||
|
pub border_radius_points_override: Option<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PaintRectangle {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
color: color::WHITE.into(),
|
||||||
|
texture: None,
|
||||||
|
texture_uv: Corners {
|
||||||
|
top_left: vec2(0., 0.),
|
||||||
|
top_right: vec2(1., 0.),
|
||||||
|
bottom_left: vec2(0., 1.),
|
||||||
|
bottom_right: vec2(1., 1.),
|
||||||
|
},
|
||||||
|
border_radius: Corners::all(0.0),
|
||||||
|
border_radius_points_override: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PaintCommand for PaintRectangle {
|
||||||
|
fn paint(&self, into: &mut PaintBuffer) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
14
hui-painter/src/paint/command/text.rs
Normal file
14
hui-painter/src/paint/command/text.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
use crate::paint::{
|
||||||
|
buffer::PaintBuffer,
|
||||||
|
command::PaintCommand,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct PaintText {
|
||||||
|
//TODO: PaintText command
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PaintCommand for PaintText {
|
||||||
|
fn paint(&self, into: &mut PaintBuffer) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
29
hui-painter/src/paint/command/transform.rs
Normal file
29
hui-painter/src/paint/command/transform.rs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
use crate::paint::{
|
||||||
|
buffer::PaintBuffer,
|
||||||
|
command::PaintCommand,
|
||||||
|
};
|
||||||
|
|
||||||
|
//TODO: use generics instead
|
||||||
|
|
||||||
|
pub struct PaintTransform {
|
||||||
|
pub transform: glam::Affine2,
|
||||||
|
pub children: Vec<Box<dyn PaintCommand>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PaintCommand for PaintTransform {
|
||||||
|
fn paint(&self, into: &mut PaintBuffer) {
|
||||||
|
// remember the starting index
|
||||||
|
let starting_index = into.vertices.len();
|
||||||
|
|
||||||
|
// paint children nodes
|
||||||
|
for child in &self.children {
|
||||||
|
child.paint(into);
|
||||||
|
}
|
||||||
|
|
||||||
|
// trans the children in-place
|
||||||
|
for vtx in &mut into.vertices[starting_index..] {
|
||||||
|
//TODO fix for rotation around the center of the object
|
||||||
|
vtx.position = self.transform.transform_point2(vtx.position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,13 +0,0 @@
|
||||||
use glam::Vec2;
|
|
||||||
use hui_shared::rect::{Corners, FillColor};
|
|
||||||
|
|
||||||
pub struct PaintRectParams {
|
|
||||||
/// Color of the rectangle.
|
|
||||||
pub color: FillColor,
|
|
||||||
|
|
||||||
/// Border width.
|
|
||||||
pub border_radius: Corners<f32>,
|
|
||||||
|
|
||||||
/// Border color.
|
|
||||||
pub border_radius_points_override: Option<f32>,
|
|
||||||
}
|
|
2
hui-painter/src/texture.rs
Normal file
2
hui-painter/src/texture.rs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
pub(crate) mod atlas;
|
||||||
|
pub use atlas::TextureHandle;
|
192
hui-painter/src/texture/atlas.rs
Normal file
192
hui-painter/src/texture/atlas.rs
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
use glam::{UVec2, uvec2, ivec2};
|
||||||
|
use rect_packer::DensePacker;
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
use nohash_hasher::BuildNoHashHasher;
|
||||||
|
|
||||||
|
//TODO support rotation
|
||||||
|
const ALLOW_ROTATION: bool = false;
|
||||||
|
|
||||||
|
const RGBA_BYTES_PER_PIXEL: usize = 4;
|
||||||
|
|
||||||
|
fn assert_size(size: UVec2) {
|
||||||
|
assert!(
|
||||||
|
size.x > 0 &&
|
||||||
|
size.y > 0,
|
||||||
|
"size must be greater than 0"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
size.x <= i32::MAX as u32 &&
|
||||||
|
size.y <= i32::MAX as u32,
|
||||||
|
"size must be less than i32::MAX"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub enum SourceTextureFormat {
|
||||||
|
/// RGBA, 8-bit per channel\
|
||||||
|
/// (Default and preferred format)
|
||||||
|
#[default]
|
||||||
|
RGBA8,
|
||||||
|
|
||||||
|
/// RGB, 8-bit per channel
|
||||||
|
/// (Alpha channel is assumed to be 255)
|
||||||
|
RGB8,
|
||||||
|
|
||||||
|
/// Alpha only, 8-bit per channel
|
||||||
|
/// (All other channels are assumed to be 255 (white))
|
||||||
|
A8,
|
||||||
|
|
||||||
|
//TODO ARGB, BGRA, etc.
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SourceTextureFormat {
|
||||||
|
pub const fn bytes_per_pixel(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
SourceTextureFormat::RGBA8 => 4,
|
||||||
|
SourceTextureFormat::RGB8 => 3,
|
||||||
|
SourceTextureFormat::A8 => 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type TextureId = u32;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct TextureHandle {
|
||||||
|
pub(crate) id: TextureId,
|
||||||
|
pub(crate) size: UVec2,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TextureAllocation {
|
||||||
|
handle: TextureHandle,
|
||||||
|
offset: UVec2,
|
||||||
|
size: UVec2,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TextureAtlas {
|
||||||
|
size: UVec2,
|
||||||
|
data: Vec<u8>,
|
||||||
|
packer: DensePacker,
|
||||||
|
next_id: TextureId,
|
||||||
|
allocations: HashMap<TextureId, TextureAllocation, BuildNoHashHasher<TextureId>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TextureAtlas {
|
||||||
|
pub fn new(size: UVec2) -> Self {
|
||||||
|
assert_size(size);
|
||||||
|
let data_bytes = (size.x * size.y) as usize * RGBA_BYTES_PER_PIXEL;
|
||||||
|
Self {
|
||||||
|
size,
|
||||||
|
data: vec![0; data_bytes],
|
||||||
|
packer: DensePacker::new(
|
||||||
|
size.x as i32,
|
||||||
|
size.y as i32,
|
||||||
|
),
|
||||||
|
next_id: 0,
|
||||||
|
allocations: HashMap::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_handle(&mut self, size: UVec2) -> TextureHandle {
|
||||||
|
let handle = TextureHandle {
|
||||||
|
id: self.next_id,
|
||||||
|
size,
|
||||||
|
};
|
||||||
|
self.next_id += 1;
|
||||||
|
handle
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Allocate a texture in the atlas, returning a handle to it.\
|
||||||
|
/// The data present in the texture is undefined, and may include garbage data.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
/// - If any of the dimensions of the texture are zero or exceed `i32::MAX`.
|
||||||
|
pub fn allocate(&mut self, size: UVec2) -> TextureHandle {
|
||||||
|
assert_size(size);
|
||||||
|
|
||||||
|
// Pack the texture
|
||||||
|
let pack = self.packer.pack(
|
||||||
|
size.x as i32,
|
||||||
|
size.y as i32,
|
||||||
|
ALLOW_ROTATION
|
||||||
|
);
|
||||||
|
|
||||||
|
//TODO: handle pack failure by either resizing the atlas or returning an error
|
||||||
|
let pack = pack.unwrap();
|
||||||
|
let offset = ivec2(pack.x, pack.y).as_uvec2();
|
||||||
|
|
||||||
|
// Allocate the texture
|
||||||
|
let handle = self.next_handle(size);
|
||||||
|
let allocation = TextureAllocation { handle, offset, size };
|
||||||
|
self.allocations.insert_unique_unchecked(handle.id, allocation);
|
||||||
|
|
||||||
|
handle
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate a texture in the atlas, returning a handle to it.\
|
||||||
|
/// The texture is initialized with the provided data.
|
||||||
|
///
|
||||||
|
/// The source data must be in the format specified by the `format` parameter.\
|
||||||
|
/// (Please note that the internal format of the texture is always RGBA8, regardless of the source format.)
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
/// - If any of the dimensions of the texture are zero or exceed `i32::MAX`.
|
||||||
|
/// - The length of the data array is zero or not a multiple of the stride (stride = width * bytes per pixel).
|
||||||
|
pub fn allocate_with_data(&mut self, format: SourceTextureFormat, data: &[u8], width: usize) -> TextureHandle {
|
||||||
|
assert!(
|
||||||
|
!data.is_empty(),
|
||||||
|
"texture data must not be empty"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Calculate the stride of the texture
|
||||||
|
let bytes_per_pixel = format.bytes_per_pixel();
|
||||||
|
let stride = bytes_per_pixel * width;
|
||||||
|
assert_eq!(
|
||||||
|
data.len() % stride, 0,
|
||||||
|
"texture data must be a multiple of the stride",
|
||||||
|
);
|
||||||
|
|
||||||
|
// Calculate the size of the texture
|
||||||
|
let size = uvec2(
|
||||||
|
width as u32,
|
||||||
|
(data.len() / stride) as u32,
|
||||||
|
);
|
||||||
|
assert_size(size);
|
||||||
|
|
||||||
|
// Allocate the texture
|
||||||
|
let handle = self.allocate(size);
|
||||||
|
let allocation = self.allocations.get(&handle.id).unwrap();
|
||||||
|
|
||||||
|
for y in 0..size.y {
|
||||||
|
for x in 0..size.x {
|
||||||
|
let src_idx = (y * size.x + x) as usize * bytes_per_pixel;
|
||||||
|
let dst_idx: usize = (
|
||||||
|
(allocation.offset.y + y) * size.x +
|
||||||
|
(allocation.offset.x + x)
|
||||||
|
) as usize * RGBA_BYTES_PER_PIXEL;
|
||||||
|
|
||||||
|
let src = &data[src_idx..src_idx + bytes_per_pixel];
|
||||||
|
let dst = &mut self.data[dst_idx..dst_idx + RGBA_BYTES_PER_PIXEL];
|
||||||
|
|
||||||
|
match format {
|
||||||
|
SourceTextureFormat::RGBA8 => {
|
||||||
|
dst.copy_from_slice(src);
|
||||||
|
}
|
||||||
|
SourceTextureFormat::RGB8 => {
|
||||||
|
dst[..3].copy_from_slice(src);
|
||||||
|
dst[3] = 255;
|
||||||
|
}
|
||||||
|
SourceTextureFormat::A8 => {
|
||||||
|
dst[0] = src[0];
|
||||||
|
dst[1] = src[0];
|
||||||
|
dst[2] = src[0];
|
||||||
|
dst[3] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handle
|
||||||
|
}
|
||||||
|
}
|
|
@ -1 +1,2 @@
|
||||||
pub mod rect;
|
pub mod rect;
|
||||||
|
pub mod color;
|
||||||
|
|
|
@ -5,7 +5,7 @@ repository = "https://github.com/griffi-gh/hui"
|
||||||
readme = "../README.md"
|
readme = "../README.md"
|
||||||
authors = ["griffi-gh <prasol258@gmail.com>"]
|
authors = ["griffi-gh <prasol258@gmail.com>"]
|
||||||
version = "0.1.0-alpha.5"
|
version = "0.1.0-alpha.5"
|
||||||
rust-version = "1.75"
|
rust-version = "1.80"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
publish = true
|
publish = true
|
||||||
|
|
|
@ -4,7 +4,7 @@ description = "Simple UI library for games and other interactive applications"
|
||||||
repository = "https://github.com/griffi-gh/hui"
|
repository = "https://github.com/griffi-gh/hui"
|
||||||
readme = "../README.md"
|
readme = "../README.md"
|
||||||
authors = ["griffi-gh <prasol258@gmail.com>"]
|
authors = ["griffi-gh <prasol258@gmail.com>"]
|
||||||
rust-version = "1.75"
|
rust-version = "1.80"
|
||||||
version = "0.1.0-alpha.5"
|
version = "0.1.0-alpha.5"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
|
|
Loading…
Reference in a new issue