1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
//! Mesh vertices.
use core::fmt;
use all_is_cubes::euclid::Point3D;
use all_is_cubes::math::{Cube, Face6, FreeCoordinate, FreePoint, FreeVector, Rgba};
use all_is_cubes::util::{ConciseDebug, Fmt, Refmt as _};
use crate::MeshTypes;
/// Basic vertex data type for a [`BlockMesh`].
/// Implement <code>[`From`]<[`BlockVertex`]></code> (and usually [`GfxVertex`])
/// to provide a specialized version fit for the target graphics API.
///
/// `T` is the type of texture-coordinate points being used. That is, one `T` value
/// should identify one point in the block's 3D texture, such as `T = Point3<f32>`).
///
/// [`BlockMesh`]: super::BlockMesh
#[allow(clippy::exhaustive_structs)]
#[derive(Clone, Copy, PartialEq)]
pub struct BlockVertex<T> {
/// Vertex position.
pub position: FreePoint,
/// Vertex normal, always axis-aligned.
pub face: Face6,
/// Surface color or texture coordinate.
pub coloring: Coloring<T>,
}
impl<T: Clone> BlockVertex<T> {
/// Remove the clamp information for the sake of tidier tests of one thing at a time.
#[cfg(test)]
pub(crate) fn remove_clamps(mut self) -> Self {
self.coloring = match self.coloring {
Coloring::Texture {
pos,
clamp_min: _,
clamp_max: _,
} => Coloring::Texture {
clamp_min: pos.clone(),
clamp_max: pos.clone(),
pos,
},
other @ Coloring::Solid(_) => other,
};
self
}
}
/// Describes the two ways a [`BlockVertex`] may be colored; by a solid color or by a texture.
///
/// `T` is the type of texture-coordinate points being used. That is, one `T` value
/// should identify one point in the block's 3D texture, such as `T = Point3<f32>`).
#[allow(clippy::exhaustive_enums)]
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum Coloring<T> {
/// Solid color.
Solid(Rgba),
/// Texture coordinates provided by the [`Allocator`](super::texture::Allocator)
/// for this vertex.
Texture {
/// Texture coordinates for this vertex.
pos: T,
/// Lower bounds for clamping the entire surface's texture coordinates.
/// Used to avoid texture bleed.
clamp_min: T,
/// Upper bounds for clamping the entire surface's texture coordinates.
/// Used to avoid texture bleed.
clamp_max: T,
},
}
impl<T> fmt::Debug for BlockVertex<T>
where
Coloring<T>: fmt::Debug,
{
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
// Print compactly on single line even if the formatter is in prettyprint mode.
write!(
fmt,
"{{ p: {:?} n: {:?} c: {:?} }}",
self.position.refmt(&ConciseDebug),
self.face,
self.coloring
)
}
}
impl<T> fmt::Debug for Coloring<T>
where
T: Fmt<ConciseDebug>, // TODO: inelegant
{
// TODO: test formatting of this
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Coloring::Solid(color) => write!(fmt, "Solid({color:?})"),
Coloring::Texture { pos, .. } => {
write!(fmt, "Texture({:?})", pos.refmt(&ConciseDebug))
}
}
}
}
/// A custom representation of [`BlockVertex`] suitable for a specific graphics system.
///
/// The life cycle of a [`GfxVertex`]:
///
/// * First, it is constructed by [`BlockMesh::new()`]
/// for a particular [`Block`] value, and stored in a [`BlockMesh`].
/// * Then, wherever that block appears in a [`Space`], the block vertices are copied
/// to become the [`SpaceMesh`]’s vertices, and [`GfxVertex::instantiate_vertex`] is
/// called on each copy to position it at the particular block's location.
///
/// [`Block`]: all_is_cubes::block::Block
/// [`BlockMesh`]: crate::BlockMesh
/// [`BlockMesh::new()`]: crate::BlockMesh::new()
/// [`Space`]: all_is_cubes::space::Space
/// [`SpaceMesh`]: crate::SpaceMesh
pub trait GfxVertex: From<BlockVertex<Self::TexPoint>> + Copy + Sized + 'static {
/// Whether [`SpaceMesh`]es should provide pre-sorted vertex index slices to allow
/// back-to-front drawing order based on viewing ranges.
///
/// Design note: Strictly speaking, this doesn't need to be static and could be part
/// of [`MeshOptions`]. However, we currently have no reason to complicate run-time
/// data flow that way.
///
/// [`SpaceMesh`]: crate::SpaceMesh
/// [`MeshOptions`]: crate::MeshOptions
const WANTS_DEPTH_SORTING: bool;
/// Number type for the vertex position coordinates.
type Coordinate: num_traits::float::FloatCore;
/// Point type identifying a point in the block's texture.
type TexPoint: Copy;
/// Type of the data carried from [`Self::instantiate_block()`] to
/// [`Self::instantiate_vertex()`].
type BlockInst: Copy;
/// Prepare the information needed by [`Self::instantiate_vertex()`] for one block.
/// Currently, this constitutes the location of that block, and hence this function
/// is responsible for any necessary numeric conversion.
fn instantiate_block(cube: Cube) -> Self::BlockInst;
/// Transforms a vertex belonging to a general model of a block to its instantiation
/// in a specific location in space.
///
/// The `block` value should be obtained by calling [`Self::instantiate_block()`].
fn instantiate_vertex(&mut self, block: Self::BlockInst);
/// Returns the position of this vertex.
///
/// Note: This is used to perform depth sorting for transparent vertices.
fn position(&self) -> Point3D<Self::Coordinate, Cube>;
}
/// Trivial implementation of [`GfxVertex`] for testing purposes. Discards lighting.
impl<T: Copy + 'static> GfxVertex for BlockVertex<T> {
const WANTS_DEPTH_SORTING: bool = true;
type Coordinate = FreeCoordinate;
type TexPoint = T;
type BlockInst = FreeVector;
fn position(&self) -> FreePoint {
self.position
}
#[inline]
fn instantiate_block(cube: Cube) -> Self::BlockInst {
cube.lower_bounds().to_f64().to_vector()
}
#[inline]
fn instantiate_vertex(&mut self, offset: Self::BlockInst) {
self.position += offset;
}
}
/// A vertex type's position using its coordinate type.
pub(crate) type VPos<M> = Point3D<<<M as MeshTypes>::Vertex as GfxVertex>::Coordinate, Cube>;