initial setup for .vox rendering

Former-commit-id: bc3d2432c41d9dc3e1f57e569f3aecb9be3fe79a
This commit is contained in:
Imbris 2019-04-15 02:07:56 -04:00
parent 48b4d3cae2
commit 2e69526069
8 changed files with 474 additions and 69 deletions

42
Cargo.lock generated
View File

@ -461,6 +461,33 @@ dependencies = [
"termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "euc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"vek 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "euclid"
version = "0.19.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"euclid_macros 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "euclid_macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.31 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "failure"
version = "0.1.5"
@ -668,6 +695,14 @@ dependencies = [
"x11-dl 2.18.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "guillotiere"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"euclid 0.19.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "hibitset"
version = "0.5.4"
@ -1943,12 +1978,15 @@ dependencies = [
"conrod_core 0.63.0 (git+https://gitlab.com/veloren/conrod.git)",
"conrod_winit 0.63.0 (git+https://gitlab.com/veloren/conrod.git)",
"dot_vox 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"euc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"gfx 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gfx_device_gl 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)",
"gfx_window_glutin 0.28.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glsl-include 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"glutin 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
"guillotiere 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"image 0.21.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2215,6 +2253,9 @@ dependencies = [
"checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b"
"checksum enum_primitive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "be4551092f4d519593039259a9ed8daedf0da12e5109c5280338073eaeb81180"
"checksum env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b61fa891024a945da30a9581546e8cfaf5602c7b3f4c137a2805cf388f92075a"
"checksum euc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0151594c4feeeb99ff35ac1b467383a46fcb2705275615bed0a47f25ffe2ccf8"
"checksum euclid 0.19.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7a4719a544a67ed3fc33784c2bd2c6581663dfe83b719a6ae05c6dabc3b51c73"
"checksum euclid_macros 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fdcb84c18ea5037a1c5a23039b4ff29403abce2e0d6b1daa11cf0bde2b30be15"
"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2"
"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1"
"checksum fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33"
@ -2238,6 +2279,7 @@ dependencies = [
"checksum gleam 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "39bb69499005e11b7b7cc0af38404a1bc0f53d954bffa8adcdb6e8d5b14f75d5"
"checksum glsl-include 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "31c109a006ad24fd612da10d185b51000ef502155578f3634416f102f0d63b6c"
"checksum glutin 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "535c6eda58adbb227604b2db10a022ffd6339d7ea3e970f338e7d98aeb24fcc3"
"checksum guillotiere 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "46e965c66630b3a0369feafb06d945f15a4f59aaecc209eb1c4a2b57bb48ee06"
"checksum hibitset 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6527bc88f32e0d3926c7572874b2bf17a19b36978aacd0aacf75f7d27a5992d0"
"checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114"
"checksum image 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "545f000e8aa4e569e93f49c446987133452e0091c2494ac3efd3606aa3d309f2"

View File

@ -22,6 +22,7 @@ glutin = "0.19"
winit = {version = "0.18", features = ["serde"]}
conrod_core = { git = "https://gitlab.com/veloren/conrod.git" }
conrod_winit = { git = "https://gitlab.com/veloren/conrod.git" }
euc = "0.2"
# ECS
specs = "0.14"
@ -41,3 +42,5 @@ config = "0.9"
serde = "1.0"
serde_derive = "1.0"
toml = "0.4"
guillotiere = "0.4"
fnv = "1.0"

View File

@ -2,7 +2,7 @@ mod chat;
use crate::{
render::Renderer,
ui::{ScaleMode, ToggleButton, Ui},
ui::{self, ScaleMode, ToggleButton, Ui},
window::{Event as WinEvent, Key, Window},
};
use common::assets;
@ -257,7 +257,7 @@ impl Imgs {
.as_slice(),
)
.unwrap();
ui.new_image(renderer, &image).unwrap()
ui.new_graphic(ui::Graphic::Image(image))
};
Imgs {
// Bag

View File

@ -235,7 +235,7 @@ impl Imgs {
.as_slice(),
)
.unwrap();
ui.new_image(renderer, &image).unwrap()
ui.new_graphic(ui::Graphic::Image(image))
};
Imgs {
v_logo: load("element/v_logo.png"),

View File

@ -74,7 +74,7 @@ impl Imgs {
.as_slice(),
)
.unwrap();
ui.new_image(renderer, &image).unwrap()
ui.new_graphic(ui::Graphic::Image(image))
};
Imgs {
bg: load("background/bg_main.png"),

92
voxygen/src/ui/graphic.rs Normal file
View File

@ -0,0 +1,92 @@
use common::figure::Segment;
use image::DynamicImage;
use guillotiere::{
AtlasAllocator,
Allocation,
size2,
};
use fnv::FnvHashMap;
use vek::*;
pub enum Graphic {
Image(DynamicImage),
Voxel(Segment),
}
#[derive(PartialEq, Eq, Hash, Copy, Clone)]
pub struct Id(u32);
type Parameters = (Id, Vec2<u16>, Aabr<u64>);
pub struct GraphicCache {
atlas: AtlasAllocator,
graphic_map: FnvHashMap<Id, Graphic>,
rect_map: FnvHashMap<Parameters, Aabr<u16>>,
next_id: u32,
}
impl GraphicCache {
pub fn new(size: Vec2<u16>) -> Self {
Self {
atlas: AtlasAllocator::new(size2(i32::from(size.x), i32::from(size.y))),
graphic_map: FnvHashMap::default(),
rect_map: FnvHashMap::default(),
next_id: 0,
}
}
pub fn new_graphic(&mut self, graphic: Graphic) -> Id {
let id = self.next_id;
self.next_id = id.wrapping_add(1);
let id = Id(id);
self.graphic_map.insert(id, graphic);
id
}
pub fn cache_res<F>(&mut self, graphic_id: Id, dims: Vec2<u16>, source: Aabr<f64>, mut cacher: F) -> Option<Aabr<u16>> where F: FnMut(Aabr<u16>, Vec<[u8; 4]>) {
match self.rect_map.get(&(graphic_id, dims, source.map(|e| e.to_bits()))) { //<-------- TODO: Replace this with rounded representation of source
Some(aabr) => Some(*aabr),
None => match self.graphic_map.get(&graphic_id) {
Some(graphic) => {
// Allocate rectangle
let aabr = match self.atlas.allocate(size2(i32::from(dims.x + 2), i32::from(dims.y + 2))) {
Some(Allocation{id, rectangle}) => {
let (min, max) = (rectangle.min, rectangle.max);
Aabr {
min: Vec2::new(min.x as u16 + 1, min.y as u16 + 1),
max: Vec2::new(max.x as u16 - 1, max.y as u16 - 1),
}
}
// Out of room
// TODO: make more room by 1. expanding cache size, 2. removing unused allocations, 3. rearranging rectangles
None => return None,
};
// Render image
// TODO: use source
let data = match graphic {
Graphic::Image(ref image) => {
image
.resize_exact(u32::from(aabr.size().w), u32::from(aabr.size().h), image::FilterType::Nearest)
.to_rgba()
.pixels()
.map(|p| p.data)
.collect::<Vec<[u8; 4]>>()
}
Graphic::Voxel(segment) => {
super::veuc::draw_vox(&segment, aabr.size().into())
}
};
// Draw to allocated area
cacher(aabr, data);
// Insert area into map for retrieval
self.rect_map.insert((graphic_id, dims, source.map(|e| e.to_bits())), aabr);
// Return area
Some(aabr)
}
None => None,
}
}
}
}

View File

@ -1,7 +1,14 @@
mod widgets;
mod graphic;
mod veuc;
pub use widgets::toggle_button::ToggleButton;
pub use graphic::Graphic;
use graphic::{
GraphicCache,
Id as GraphicId,
};
use image::DynamicImage;
use conrod_core::{
Ui as CrUi,
@ -81,9 +88,10 @@ impl Event {
}
pub struct Cache {
blank_texture: Texture<UiPipeline>,
glyph_cache: GlyphCache<'static>,
glyph_cache_tex: Texture<UiPipeline>,
graphic_cache: graphic::GraphicCache,
graphic_cache_tex: Texture<UiPipeline>,
}
// TODO: Should functions be returning UiError instead of Error?
@ -93,23 +101,27 @@ impl Cache {
const SCALE_TOLERANCE: f32 = 0.1;
const POSITION_TOLERANCE: f32 = 0.1;
let graphic_cache_dims = Vec2::new(w * 4, h * 4);
Ok(Self {
blank_texture: renderer.create_texture(&DynamicImage::new_rgba8(1, 1))?,
glyph_cache: GlyphCache::builder()
.dimensions(w as u32, h as u32)
.scale_tolerance(SCALE_TOLERANCE)
.position_tolerance(POSITION_TOLERANCE)
.build(),
glyph_cache_tex: renderer.create_dynamic_texture((w, h).into())?,
graphic_cache: GraphicCache::new(graphic_cache_dims),
graphic_cache_tex: renderer.create_dynamic_texture(graphic_cache_dims)?,
})
}
pub fn blank_texture(&self) -> &Texture<UiPipeline> { &self.blank_texture }
pub fn glyph_cache_tex(&self) -> &Texture<UiPipeline> { &self.glyph_cache_tex }
pub fn glyph_cache_mut_and_tex(&mut self) -> (&mut GlyphCache<'static>, &Texture<UiPipeline>) { (&mut self.glyph_cache, &self.glyph_cache_tex) }
pub fn graphic_cache_tex(&self) -> &Texture<UiPipeline> { &self.graphic_cache_tex }
pub fn graphic_cache_mut_and_tex(&mut self) -> (&mut GraphicCache, &Texture<UiPipeline>) { (&mut self.graphic_cache, &self.graphic_cache_tex) }
pub fn new_graphic(&mut self, graphic: Graphic) -> GraphicId { self.graphic_cache.new_graphic(graphic) }
}
enum DrawKind {
Image(ImgId),
Image,
// Text and non-textured geometry
Plain,
}
@ -121,9 +133,9 @@ enum DrawCommand {
Scissor(Aabr<u16>),
}
impl DrawCommand {
fn image(model: Model<UiPipeline>, img_id: ImgId) -> DrawCommand {
fn image(model: Model<UiPipeline>) -> DrawCommand {
DrawCommand::Draw {
kind: DrawKind::Image(img_id),
kind: DrawKind::Image,
model,
}
}
@ -198,7 +210,7 @@ impl Scale {
pub struct Ui {
ui: CrUi,
image_map: Map<Texture<UiPipeline>>,
image_map: Map<GraphicId>,
cache: Cache,
// Draw commands for the next render
draw_commands: Vec<DrawCommand>,
@ -230,10 +242,12 @@ impl Ui {
self.ui.handle_event(Input::Resize(w, h));
}
pub fn new_image(&mut self, renderer: &mut Renderer, image: &DynamicImage) -> Result<ImgId, Error> {
Ok(self.image_map.insert(renderer.create_texture(image)?))
pub fn new_graphic(&mut self, graphic: Graphic) -> ImgId {
self.image_map.insert(self.cache.new_graphic(graphic))
}
// TODO: add function that creates a blank graphic
pub fn new_font(&mut self, font: Font) -> FontId {
self.ui.fonts.insert(font)
}
@ -246,7 +260,7 @@ impl Ui {
self.ui.set_widgets()
}
// Accepts option so widget can be unfocused
// Accepts Option so widget can be unfocused
pub fn focus_widget(&mut self, id: Option<WidgId>) {
self.ui.keyboard_capture(match id {
Some(id) => id,
@ -259,7 +273,7 @@ impl Ui {
self.ui.global_input().current.widget_capturing_keyboard
}
// Get whether the a widget besides the window is capturing the mouse
// Get whether a widget besides the window is capturing the mouse
pub fn no_widget_capturing_mouse(&self) -> bool {
self.ui.global_input().current.widget_capturing_mouse.filter(|id| id != &self.ui.window ).is_none()
}
@ -305,7 +319,13 @@ impl Ui {
self.draw_commands.clear();
let mut mesh = Mesh::new();
let mut current_img = None;
// TODO: this could be removed entirely if the draw call just used both textures
enum State {
Image,
Plain,
};
let mut current_state = State::Plain;
let window_scizzor = default_scissor(renderer);
let mut current_scizzor = window_scizzor;
@ -314,9 +334,10 @@ impl Ui {
// `Plain` state.
macro_rules! switch_to_plain_state {
() => {
if let Some(image_id) = current_img.take() {
self.draw_commands.push(DrawCommand::image(renderer.create_model(&mesh).unwrap(), image_id));
if let State::Image = current_state {
self.draw_commands.push(DrawCommand::image(renderer.create_model(&mesh).unwrap()));
mesh.clear();
current_state = State::Plain;
}
};
}
@ -324,7 +345,7 @@ impl Ui {
let p_scale_factor = self.scale.scale_factor_physical();
while let Some(prim) = primitives.next() {
let Primitive {kind, scizzor, id, rect} = prim;
let Primitive {kind, scizzor, id: _id, rect} = prim;
// Check for a change in the scizzor
let new_scizzor = {
@ -348,12 +369,12 @@ impl Ui {
};
if new_scizzor != current_scizzor {
// Finish the current command
match current_img.take() {
None =>
self.draw_commands.push(DrawCommand::plain(renderer.create_model(&mesh).unwrap())),
Some(image_id) =>
self.draw_commands.push(DrawCommand::image(renderer.create_model(&mesh).unwrap(), image_id)),
}
self.draw_commands.push(match current_state {
State::Plain =>
DrawCommand::plain(renderer.create_model(&mesh).unwrap()),
State::Image =>
DrawCommand::image(renderer.create_model(&mesh).unwrap()),
});
mesh.clear();
// Update the scizzor and produce a command.
@ -375,52 +396,58 @@ impl Ui {
use conrod_core::render::PrimitiveKind;
match kind {
PrimitiveKind::Image { image_id, color, source_rect } => {
// Switch to the `Image` state for this image if we're not in it already.
let new_image_id = image_id;
match current_img {
// If we're already in the drawing mode for this image, we're done.
Some(image_id) if image_id == new_image_id => (),
// If we were in the `Plain` drawing state, switch to Image drawing state.
None => {
self.draw_commands.push(DrawCommand::plain(renderer.create_model(&mesh).unwrap()));
mesh.clear();
current_img = Some(new_image_id);
}
// If we were drawing a different image, switch state to draw *this* image.
Some(image_id) => {
self.draw_commands.push(DrawCommand::image(renderer.create_model(&mesh).unwrap(), image_id));
mesh.clear();
current_img = Some(new_image_id);
}
if let State::Plain = current_state {
self.draw_commands.push(DrawCommand::plain(renderer.create_model(&mesh).unwrap()));
mesh.clear();
current_state = State::Image;
}
let color = srgb_to_linear(color.unwrap_or(conrod_core::color::WHITE).to_fsa());
// Transform the source rectangle into uv coordinates
let (image_w, image_h) = self.image_map
.get(&image_id)
.expect("Image does not exist in image map")
.get_dimensions()
.map(|e| e as f64)
.into_tuple();
let (uv_l, uv_r, uv_t, uv_b) = match source_rect {
Some(src_rect) => {
let (l, r, b, t) = src_rect.l_r_b_t();
((l / image_w) as f32,
(r / image_w) as f32,
(b / image_h) as f32,
(t / image_h) as f32)
let (graphic_cache, cache_tex) = self.cache.graphic_cache_mut_and_tex();
let resolution = Vec2::new(
(rect.w() * p_scale_factor) as u16,
(rect.h() * p_scale_factor) as u16,
);
// Transform the source rectangle into uv coordinate
// TODO: make sure this is right
let source_aabr = {
let (uv_l, uv_r, uv_b, uv_t) = (0.0, 1.0, 0.0, 1.0);/*match source_rect {
Some(src_rect) => {
let (l, r, b, t) = src_rect.l_r_b_t();
((l / image_w) as f32,
(r / image_w) as f32,
(b / image_h) as f32,
(t / image_h) as f32)
}
None => (0.0, 1.0, 0.0, 1.0),
};*/
Aabr {
min: Vec2::new(uv_l, uv_b),
max: Vec2::new(uv_r, uv_t),
}
None => (0.0, 1.0, 0.0, 1.0),
};
let uv = Aabr {
min: Vec2::new(uv_l, uv_b),
max: Vec2::new(uv_r, uv_t),
let graphic_id = self.image_map.get(&image_id).expect("Image does not exist in image map");
let (cache_w, cache_h) = cache_tex.get_dimensions().map(|e| e as f32).into_tuple();
// Cache graphic at particular resolution
let uv_aabr = match graphic_cache.cache_res(*graphic_id, resolution, source_aabr, |aabr, data| {
let offset = aabr.min.into_array();
let size = aabr.size().into_array();
renderer.update_texture(cache_tex, offset, size, &data);
}) {
Some(aabr) => Aabr {
min: Vec2::new(aabr.min.x as f32 / cache_w, aabr.max.y as f32 / cache_h),
max: Vec2::new(aabr.max.x as f32 / cache_w, aabr.min.y as f32 / cache_h),
},
None => continue,
};
mesh.push_quad(create_ui_quad(
gl_aabr(rect),
uv,
uv_aabr,
color,
UiMode::Image,
));
@ -535,12 +562,12 @@ impl Ui {
}
}
// Enter the final command
match current_img {
None =>
self.draw_commands.push(DrawCommand::plain(renderer.create_model(&mesh).unwrap())),
Some(image_id) =>
self.draw_commands.push(DrawCommand::image(renderer.create_model(&mesh).unwrap(), image_id)),
}
self.draw_commands.push(match current_state {
State::Plain =>
DrawCommand::plain(renderer.create_model(&mesh).unwrap()),
State::Image =>
DrawCommand::image(renderer.create_model(&mesh).unwrap()),
});
// Handle window resizing
if let Some(new_dims) = self.window_resized.take() {
@ -560,8 +587,8 @@ impl Ui {
}
DrawCommand::Draw { kind, model } => {
let tex = match kind {
DrawKind::Image(image_id) => {
self.image_map.get(&image_id).expect("Image does not exist in image map")
DrawKind::Image => {
self.cache.graphic_cache_tex()
}
DrawKind::Plain => {
self.cache.glyph_cache_tex()

241
voxygen/src/ui/veuc.rs Normal file
View File

@ -0,0 +1,241 @@
use euc::{
Pipeline,
rasterizer,
buffer::Buffer2d,
Interpolate,
};
use common::{
figure::Segment,
vol::{
Vox,
SizedVol,
ReadVol,
},
};
use vek::*;
trait Shader {
type VertExtra;
type VsOut: Clone + Interpolate;
fn vert(&self, v_color: Rgba<f32>, v_pos: Vec3<f32>, vert_extra: &Self::VertExtra) -> (Vec3<f32>, Self::VsOut);
fn frag(&self, vs_out: &Self::VsOut) -> Rgba<f32>;
}
struct Voxel<S> where S: Shader {
mvp: Mat4<f32>,
shader: S,
}
struct SimpleShader;
impl Shader for SimpleShader {
type VertExtra = ();
type VsOut = Rgba<f32>;
fn vert(&self, v_color: Rgba<f32>, v_pos: Vec3<f32>, _: &Self::VertExtra) -> (Vec3<f32>, Self::VsOut) {
(
v_pos,
v_color,
)
}
fn frag(&self, vs_out: &Self::VsOut) -> Rgba<f32> {
*vs_out
}
}
impl<'a, S> Pipeline for Voxel<S> where S: Shader {
type Vertex = (Vec3<f32>, Rgb<f32>, S::VertExtra);
type VsOut = S::VsOut;
type Pixel = [u8; 4];
#[inline(always)]
fn vert(&self, (v_pos, v_color, v_extra): &Self::Vertex) -> ([f32; 3], Self::VsOut) {
let (pos, out) = self.shader.vert(
srgb_to_linear(Rgba::from_opaque(*v_color)),
Vec3::from(self.mvp * Vec4::from_point(*v_pos)),
v_extra,
);
(
pos.into_array(),
out,
)
}
#[inline(always)]
fn frag(&self, vs_out: &Self::VsOut) -> Self::Pixel {
let color = self.shader.frag(vs_out);
linear_to_srgb(color).map(|e| (e * 255.0) as u8).into_array()
}
}
pub fn draw_vox(segment: &Segment, output_size: Vec2<u16>) -> Vec<[u8; 4]> {
let dims = output_size.map(|e| e as usize).into_array();
let mut color = Buffer2d::new(dims, [50; 4]);
let mut depth = Buffer2d::new(dims, 1.0);
let mvp =
Mat4::rotation_y(0.6) *
Mat4::<f32>::scaling_3d(1.0 / 14.0) *
Mat4::translation_2d([-14.0, -14.0]) *
Mat4::rotation_x(-std::f32::consts::PI / 2.0 );
Voxel {
mvp,
shader: SimpleShader,
}
.draw::<rasterizer::Triangles<_>, _>(
&generate_mesh(segment, Vec3::from(0.0)),
&mut color,
&mut depth,
);
// TODO: remove this clone
color.as_ref().to_vec()
}
type Vert = <Voxel<SimpleShader> as Pipeline>::Vertex;
// TODO: generalise meshing code
fn create_quad(
origin: Vec3<f32>,
unit_x: Vec3<f32>,
unit_y: Vec3<f32>,
//norm: Vec3<f32>,
col: Rgb<f32>,
) -> [Vert; 6] {
let a = (origin, col, ());
let b = (origin + unit_x, col, ());
let c = (origin + unit_x + unit_y, col, ());
let d = (origin + unit_y, col, ());
[
a, b, c, // Tri 1
c, d, a, // Tri 2
]
}
fn generate_mesh(segment: &Segment, offs: Vec3<f32>) -> Vec<Vert> {
let mut vertices = Vec::new();
for pos in segment.iter_positions() {
if let Some(col) = segment
.get(pos)
.ok()
.and_then(|vox| vox.get_color())
{
let col = col.map(|e| e as f32 / 255.0);
// -x
if segment.get(pos - Vec3::unit_x())
.map(|v| v.is_empty())
.unwrap_or(true)
{
vertices.extend_from_slice(&create_quad(
offs + pos.map(|e| e as f32) + Vec3::unit_y(),
-Vec3::unit_y(),
Vec3::unit_z(),
//-Vec3::unit_x(),
col,
));
}
// +x
if segment.get(pos + Vec3::unit_x())
.map(|v| v.is_empty())
.unwrap_or(true)
{
vertices.extend_from_slice(&create_quad(
offs + pos.map(|e| e as f32) + Vec3::unit_x(),
Vec3::unit_y(),
Vec3::unit_z(),
//Vec3::unit_x(),
col,
));
}
// -y
if segment.get(pos - Vec3::unit_y())
.map(|v| v.is_empty())
.unwrap_or(true)
{
vertices.extend_from_slice(&create_quad(
offs + pos.map(|e| e as f32),
Vec3::unit_x(),
Vec3::unit_z(),
//-Vec3::unit_y(),
col,
));
}
// +y
if segment.get(pos + Vec3::unit_y())
.map(|v| v.is_empty())
.unwrap_or(true)
{
vertices.extend_from_slice(&create_quad(
offs + pos.map(|e| e as f32) + Vec3::unit_y(),
Vec3::unit_z(),
Vec3::unit_x(),
//Vec3::unit_y(),
col,
));
}
// -z
if segment.get(pos - Vec3::unit_z())
.map(|v| v.is_empty())
.unwrap_or(true)
{
vertices.extend_from_slice(&create_quad(
offs + pos.map(|e| e as f32),
Vec3::unit_y(),
Vec3::unit_x(),
//-Vec3::unit_z(),
col,
));
}
// +z
if segment.get(pos + Vec3::unit_z())
.map(|v| v.is_empty())
.unwrap_or(true)
{
vertices.extend_from_slice(&create_quad(
offs + pos.map(|e| e as f32) + Vec3::unit_z(),
Vec3::unit_x(),
Vec3::unit_y(),
//Vec3::unit_z(),
col,
));
}
}
}
vertices
}
// TODO: put these in utility a module
#[inline(always)]
fn to_linear(x: f32) -> f32 {
if x <= 0.04045 {
x / 12.92
} else {
((x + 0.055) / 1.055).powf(2.4)
}
}
#[inline(always)]
fn to_srgb(x: f32) -> f32 {
if x <= 0.0031308 {
x * 12.92
} else {
x.powf(1.0 / 2.4) * 1.055 - 0.055
}
}
#[inline(always)]
fn srgb_to_linear(c: Rgba<f32>) -> Rgba<f32> {
Rgba {
r: to_linear(c.r),
g: to_linear(c.g),
b: to_linear(c.b),
a: c.a,
}
}
#[inline(always)]
fn linear_to_srgb(c: Rgba<f32>) -> Rgba<f32> {
Rgba {
r: to_srgb(c.r),
g: to_srgb(c.g),
b: to_srgb(c.b),
a: c.a,
}
}