use crate::{
asset::{
io::ResourceIo,
loader::{BoxedLoaderFuture, LoaderPayload, ResourceLoader},
state::LoadError,
untyped::ResourceKind,
Resource, ResourceData,
},
core::{
algebra::{Matrix4, Point3, Vector2, Vector3, Vector4},
hash_combine,
log::Log,
math::TriangleDefinition,
pool::{ErasedHandle, Handle},
reflect::prelude::*,
sparse::AtomicIndex,
type_traits::prelude::*,
uuid_provider,
variable::InheritableVariable,
visitor::{Visit, VisitResult, Visitor},
Uuid,
},
material::{Material, MaterialResource, MaterialResourceExtension},
resource::texture::{TextureKind, TexturePixelKind, TextureResource, TextureResourceExtension},
scene::{
mesh::{
buffer::{
TriangleBuffer, VertexAttributeUsage, VertexBuffer, VertexFetchError,
VertexReadTrait, VertexTrait, VertexWriteTrait,
},
vertex::StaticVertex,
},
node::Node,
},
utils::raw_mesh::{RawMesh, RawMeshBuilder},
};
use bytemuck::{Pod, Zeroable};
use fxhash::{FxHashMap, FxHasher};
use fyrox_resource::manager::BuiltInResource;
use half::f16;
use std::{
error::Error,
hash::Hasher,
path::{Path, PathBuf},
sync::{Arc, LazyLock},
};
#[derive(Debug, Clone, Visit, Reflect, PartialEq)]
pub struct BlendShape {
#[reflect(min_value = 0.0, max_value = 100.0, step = 1.0)]
pub weight: f32,
#[reflect(read_only)]
pub name: String,
}
uuid_provider!(BlendShape = "fea08418-58fe-4fde-991b-36be235432bd");
impl Default for BlendShape {
fn default() -> Self {
Self {
weight: 100.0,
name: Default::default(),
}
}
}
#[derive(Reflect, Debug, Clone, Default)]
pub struct BlendShapesContainer {
pub blend_shapes: Vec<BlendShape>,
pub blend_shape_storage: Option<TextureResource>,
}
#[derive(Clone)]
pub struct InputBlendShapeData {
pub default_weight: f32,
pub name: String,
pub positions: FxHashMap<u32, Vector3<f16>>,
pub normals: FxHashMap<u32, Vector3<f16>>,
pub tangents: FxHashMap<u32, Vector3<f16>>,
}
impl BlendShapesContainer {
pub fn from_lists(
base_shape: &VertexBuffer,
input_blend_shapes: &[InputBlendShapeData],
) -> Self {
#[repr(C)]
#[derive(Default, Clone, Copy, Pod, Zeroable)]
struct VertexData {
position: Vector3<f16>,
normal: Vector3<f16>,
tangent: Vector3<f16>,
}
#[inline]
fn coord_to_index(x: usize, y: usize, z: usize, width: usize, height: usize) -> usize {
z * width * height + y * width + x
}
#[inline]
fn index_to_2d_coord(index: usize, width: usize) -> Vector2<usize> {
let y = index / width;
let x = index - width * y; Vector2::new(x, y)
}
#[inline]
fn fetch(
vertices: &mut [VertexData],
vertex_index: usize,
width: u32,
height: u32,
layer: usize,
) -> Option<&mut VertexData> {
let coord = index_to_2d_coord(vertex_index, width as usize);
vertices.get_mut(coord_to_index(
coord.x,
coord.y,
layer,
width as usize,
height as usize,
))
}
let width = base_shape.vertex_count().min(512);
let height = (base_shape.vertex_count() as f32 / width as f32).ceil() as u32;
let depth = input_blend_shapes.len() as u32;
let mut vertex_data = vec![VertexData::default(); (width * height * depth) as usize];
for (layer, blend_shape) in input_blend_shapes.iter().enumerate() {
for (index, position) in blend_shape.positions.iter() {
if let Some(vertex) = fetch(&mut vertex_data, *index as usize, width, height, layer)
{
vertex.position = *position;
}
}
for (index, normal) in blend_shape.normals.iter() {
if let Some(vertex) = fetch(&mut vertex_data, *index as usize, width, height, layer)
{
vertex.normal = *normal;
}
}
for (index, tangent) in blend_shape.tangents.iter() {
if let Some(vertex) = fetch(&mut vertex_data, *index as usize, width, height, layer)
{
vertex.tangent = *tangent;
}
}
}
let bytes = crate::core::transmute_vec_as_bytes::<VertexData>(vertex_data);
assert_eq!(
bytes.len(),
(width * height * depth) as usize * std::mem::size_of::<VertexData>()
);
Self {
blend_shapes: input_blend_shapes
.iter()
.map(|bs| BlendShape {
weight: bs.default_weight,
name: bs.name.clone(),
})
.collect(),
blend_shape_storage: Some(
TextureResource::from_bytes(
Uuid::new_v4(),
TextureKind::Volume {
width: width * 3,
height,
depth,
},
TexturePixelKind::RGB16F,
bytes,
ResourceKind::Embedded,
)
.unwrap(),
),
}
}
}
#[derive(Debug, Clone, Default, Reflect, TypeUuidProvider)]
#[type_uuid(id = "8a23a414-e66d-4e12-9628-92c6ab49c2f0")]
pub struct SurfaceData {
pub vertex_buffer: VertexBuffer,
pub geometry_buffer: TriangleBuffer,
pub blend_shapes_container: Option<BlendShapesContainer>,
#[reflect(hidden)]
pub(crate) cache_index: Arc<AtomicIndex>,
}
impl ResourceData for SurfaceData {
fn type_uuid(&self) -> Uuid {
<SurfaceData as fyrox_core::TypeUuidProvider>::type_uuid()
}
fn save(&mut self, path: &Path) -> Result<(), Box<dyn Error>> {
let mut visitor = Visitor::new();
self.visit("SurfaceData", &mut visitor)?;
visitor.save_ascii_to_file(path)?;
Ok(())
}
fn can_be_saved(&self) -> bool {
true
}
fn try_clone_box(&self) -> Option<Box<dyn ResourceData>> {
Some(Box::new(self.clone()))
}
}
impl SurfaceData {
pub fn new(vertex_buffer: VertexBuffer, triangles: TriangleBuffer) -> Self {
Self {
vertex_buffer,
geometry_buffer: triangles,
blend_shapes_container: None,
cache_index: Arc::new(AtomicIndex::unassigned()),
}
}
pub fn transform_geometry(&mut self, transform: &Matrix4<f32>) -> Result<(), VertexFetchError> {
let normal_matrix = transform.try_inverse().unwrap_or_default().transpose();
let mut vertex_buffer_mut = self.vertex_buffer.modify();
for mut view in vertex_buffer_mut.iter_mut() {
let position = view.read_3_f32(VertexAttributeUsage::Position)?;
view.write_3_f32(
VertexAttributeUsage::Position,
transform.transform_point(&Point3::from(position)).coords,
)?;
let normal = view.read_3_f32(VertexAttributeUsage::Normal)?;
view.write_3_f32(
VertexAttributeUsage::Normal,
normal_matrix.transform_vector(&normal),
)?;
let tangent = view.read_4_f32(VertexAttributeUsage::Tangent)?;
let new_tangent = normal_matrix.transform_vector(&tangent.xyz());
view.write_4_f32(
VertexAttributeUsage::Tangent,
Vector4::new(new_tangent.x, new_tangent.y, new_tangent.z, tangent.w),
)?;
}
Ok(())
}
pub fn from_raw_mesh<T>(raw: RawMesh<T>) -> Self
where
T: VertexTrait,
{
Self {
vertex_buffer: VertexBuffer::new(raw.vertices.len(), raw.vertices).unwrap(),
geometry_buffer: TriangleBuffer::new(raw.triangles),
blend_shapes_container: Default::default(),
cache_index: Arc::new(AtomicIndex::unassigned()),
}
}
pub fn calculate_tangents(&mut self) -> Result<(), VertexFetchError> {
let mut tan1 = vec![Vector3::default(); self.vertex_buffer.vertex_count() as usize];
let mut tan2 = vec![Vector3::default(); self.vertex_buffer.vertex_count() as usize];
for triangle in self.geometry_buffer.iter() {
let i1 = triangle[0] as usize;
let i2 = triangle[1] as usize;
let i3 = triangle[2] as usize;
let view1 = &self.vertex_buffer.get(i1).unwrap();
let view2 = &self.vertex_buffer.get(i2).unwrap();
let view3 = &self.vertex_buffer.get(i3).unwrap();
let v1 = view1.read_3_f32(VertexAttributeUsage::Position)?;
let v2 = view2.read_3_f32(VertexAttributeUsage::Position)?;
let v3 = view3.read_3_f32(VertexAttributeUsage::Position)?;
if v1 == v2 || v1 == v3 || v2 == v3 {
Log::warn(format!(
"Degenerated triangle found when calculating tangents. Lighting may be \
incorrect! Triangle indices: {triangle:?}. Triangle vertices: {v1:?} {v2:?} {v3:?}",
));
}
let w1 = view1.read_3_f32(VertexAttributeUsage::TexCoord0)?;
let w2 = view2.read_3_f32(VertexAttributeUsage::TexCoord0)?;
let w3 = view3.read_3_f32(VertexAttributeUsage::TexCoord0)?;
let x1 = v2.x - v1.x;
let x2 = v3.x - v1.x;
let y1 = v2.y - v1.y;
let y2 = v3.y - v1.y;
let z1 = v2.z - v1.z;
let z2 = v3.z - v1.z;
let s1 = w2.x - w1.x;
let s2 = w3.x - w1.x;
let t1 = w2.y - w1.y;
let t2 = w3.y - w1.y;
let r = 1.0 / (s1 * t2 - s2 * t1);
let sdir = Vector3::new(
(t2 * x1 - t1 * x2) * r,
(t2 * y1 - t1 * y2) * r,
(t2 * z1 - t1 * z2) * r,
);
tan1[i1] += sdir;
tan1[i2] += sdir;
tan1[i3] += sdir;
let tdir = Vector3::new(
(s1 * x2 - s2 * x1) * r,
(s1 * y2 - s2 * y1) * r,
(s1 * z2 - s2 * z1) * r,
);
tan2[i1] += tdir;
tan2[i2] += tdir;
tan2[i3] += tdir;
}
let mut vertex_buffer_mut = self.vertex_buffer.modify();
for (mut view, (t1, t2)) in vertex_buffer_mut.iter_mut().zip(tan1.into_iter().zip(tan2)) {
let normal = view.read_3_f32(VertexAttributeUsage::Normal)?;
let tangent = (t1 - normal.scale(normal.dot(&t1)))
.try_normalize(f32::EPSILON)
.unwrap_or_else(|| Vector3::new(0.0, 1.0, 0.0));
let handedness = normal.cross(&t1).dot(&t2).signum();
view.write_4_f32(
VertexAttributeUsage::Tangent,
Vector4::new(tangent.x, tangent.y, tangent.z, handedness),
)?;
}
Ok(())
}
pub fn make_unit_xy_quad() -> Self {
let vertices = vec![
StaticVertex {
position: Vector3::default(),
normal: Vector3::z(),
tex_coord: Vector2::y(),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::x(),
normal: Vector3::z(),
tex_coord: Vector2::new(1.0, 1.0),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::new(1.0, 1.0, 0.0),
normal: Vector3::z(),
tex_coord: Vector2::x(),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::y(),
normal: Vector3::z(),
tex_coord: Vector2::default(),
tangent: Vector4::default(),
},
];
let triangles = vec![TriangleDefinition([0, 1, 2]), TriangleDefinition([0, 2, 3])];
Self::new(
VertexBuffer::new(vertices.len(), vertices).unwrap(),
TriangleBuffer::new(triangles),
)
}
pub fn make_collapsed_xy_quad() -> Self {
let vertices = vec![
StaticVertex {
position: Vector3::default(),
normal: Vector3::z(),
tex_coord: Vector2::default(),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::default(),
normal: Vector3::z(),
tex_coord: Vector2::x(),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::default(),
normal: Vector3::z(),
tex_coord: Vector2::new(1.0, 1.0),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::default(),
normal: Vector3::z(),
tex_coord: Vector2::y(),
tangent: Vector4::default(),
},
];
let triangles = vec![TriangleDefinition([0, 1, 2]), TriangleDefinition([0, 2, 3])];
Self::new(
VertexBuffer::new(vertices.len(), vertices).unwrap(),
TriangleBuffer::new(triangles),
)
}
pub fn make_quad(transform: &Matrix4<f32>) -> Self {
let vertices = vec![
StaticVertex {
position: Vector3::new(-0.5, 0.5, 0.0),
normal: -Vector3::z(),
tex_coord: Vector2::new(1.0, 1.0),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::new(0.5, 0.5, 0.0),
normal: -Vector3::z(),
tex_coord: Vector2::new(0.0, 1.0),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::new(0.5, -0.5, 0.0),
normal: -Vector3::z(),
tex_coord: Vector2::new(0.0, 0.0),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::new(-0.5, -0.5, 0.0),
normal: -Vector3::z(),
tex_coord: Vector2::new(1.0, 0.0),
tangent: Vector4::default(),
},
];
let mut data = Self::new(
VertexBuffer::new(vertices.len(), vertices).unwrap(),
TriangleBuffer::new(vec![
TriangleDefinition([0, 1, 2]),
TriangleDefinition([0, 2, 3]),
]),
);
data.calculate_tangents().unwrap();
data.transform_geometry(transform).unwrap();
data
}
pub fn calculate_normals(&mut self) -> Result<(), VertexFetchError> {
let mut vertex_buffer_mut = self.vertex_buffer.modify();
for triangle in self.geometry_buffer.iter() {
let ia = triangle[0] as usize;
let ib = triangle[1] as usize;
let ic = triangle[2] as usize;
let a = vertex_buffer_mut
.get(ia)
.unwrap()
.read_3_f32(VertexAttributeUsage::Position)?;
let b = vertex_buffer_mut
.get(ib)
.unwrap()
.read_3_f32(VertexAttributeUsage::Position)?;
let c = vertex_buffer_mut
.get(ic)
.unwrap()
.read_3_f32(VertexAttributeUsage::Position)?;
let normal = (b - a).cross(&(c - a)).normalize();
vertex_buffer_mut
.get_mut(ia)
.unwrap()
.write_3_f32(VertexAttributeUsage::Normal, normal)?;
vertex_buffer_mut
.get_mut(ib)
.unwrap()
.write_3_f32(VertexAttributeUsage::Normal, normal)?;
vertex_buffer_mut
.get_mut(ic)
.unwrap()
.write_3_f32(VertexAttributeUsage::Normal, normal)?;
}
Ok(())
}
pub fn make_sphere(slices: usize, stacks: usize, r: f32, transform: &Matrix4<f32>) -> Self {
let mut builder = RawMeshBuilder::<StaticVertex>::new(stacks * slices, stacks * slices * 3);
let d_theta = std::f32::consts::PI / slices as f32;
let d_phi = 2.0 * std::f32::consts::PI / stacks as f32;
let d_tc_y = 1.0 / stacks as f32;
let d_tc_x = 1.0 / slices as f32;
for i in 0..stacks {
for j in 0..slices {
let nj = j + 1;
let ni = i + 1;
let k0 = r * (d_theta * i as f32).sin();
let k1 = (d_phi * j as f32).cos();
let k2 = (d_phi * j as f32).sin();
let k3 = r * (d_theta * i as f32).cos();
let k4 = r * (d_theta * ni as f32).sin();
let k5 = (d_phi * nj as f32).cos();
let k6 = (d_phi * nj as f32).sin();
let k7 = r * (d_theta * ni as f32).cos();
if i != (stacks - 1) {
let v0 = Vector3::new(k0 * k1, k0 * k2, k3);
let t0 = Vector2::new(d_tc_x * j as f32, d_tc_y * i as f32);
let v1 = Vector3::new(k4 * k1, k4 * k2, k7);
let t1 = Vector2::new(d_tc_x * j as f32, d_tc_y * ni as f32);
let v2 = Vector3::new(k4 * k5, k4 * k6, k7);
let t2 = Vector2::new(d_tc_x * nj as f32, d_tc_y * ni as f32);
builder.insert(StaticVertex::from_pos_uv_normal(v0, t0, v0));
builder.insert(StaticVertex::from_pos_uv_normal(v1, t1, v1));
builder.insert(StaticVertex::from_pos_uv_normal(v2, t2, v2));
}
if i != 0 {
let v0 = Vector3::new(k4 * k5, k4 * k6, k7);
let t0 = Vector2::new(d_tc_x * nj as f32, d_tc_y * ni as f32);
let v1 = Vector3::new(k0 * k5, k0 * k6, k3);
let t1 = Vector2::new(d_tc_x * nj as f32, d_tc_y * i as f32);
let v2 = Vector3::new(k0 * k1, k0 * k2, k3);
let t2 = Vector2::new(d_tc_x * j as f32, d_tc_y * i as f32);
builder.insert(StaticVertex::from_pos_uv_normal(v0, t0, v0));
builder.insert(StaticVertex::from_pos_uv_normal(v1, t1, v1));
builder.insert(StaticVertex::from_pos_uv_normal(v2, t2, v2));
}
}
}
let mut data = Self::from_raw_mesh(builder.build());
data.calculate_tangents().unwrap();
data.transform_geometry(transform).unwrap();
data
}
pub fn make_cone(sides: usize, r: f32, h: f32, transform: &Matrix4<f32>) -> Self {
let mut builder = RawMeshBuilder::<StaticVertex>::new(3 * sides, 3 * sides);
let d_phi = 2.0 * std::f32::consts::PI / sides as f32;
let d_theta = 1.0 / sides as f32;
for i in 0..sides {
let nx0 = (d_phi * i as f32).cos();
let ny0 = (d_phi * i as f32).sin();
let nx1 = (d_phi * (i + 1) as f32).cos();
let ny1 = (d_phi * (i + 1) as f32).sin();
let x0 = r * nx0;
let z0 = r * ny0;
let x1 = r * nx1;
let z1 = r * ny1;
let tx0 = d_theta * i as f32;
let tx1 = d_theta * (i + 1) as f32;
let (t_cap_y_curr, t_cap_x_curr) = (d_phi * i as f32).sin_cos();
let (t_cap_y_next, t_cap_x_next) = (d_phi * (i + 1) as f32).sin_cos();
let t_cap_x_curr = t_cap_x_curr * 0.5 + 0.5;
let t_cap_y_curr = t_cap_y_curr * 0.5 + 0.5;
let t_cap_x_next = t_cap_x_next * 0.5 + 0.5;
let t_cap_y_next = t_cap_y_next * 0.5 + 0.5;
builder.insert(StaticVertex::from_pos_uv_normal(
Vector3::new(0.0, 0.0, 0.0),
Vector2::new(0.5, 0.5),
Vector3::new(0.0, -1.0, 0.0),
));
builder.insert(StaticVertex::from_pos_uv_normal(
Vector3::new(x0, 0.0, z0),
Vector2::new(t_cap_x_curr, t_cap_y_curr),
Vector3::new(0.0, -1.0, 0.0),
));
builder.insert(StaticVertex::from_pos_uv_normal(
Vector3::new(x1, 0.0, z1),
Vector2::new(t_cap_x_next, t_cap_y_next),
Vector3::new(0.0, -1.0, 0.0),
));
let tip = Vector3::new(0.0, h, 0.0);
let v_curr = Vector3::new(x0, 0.0, z0);
let v_next = Vector3::new(x1, 0.0, z1);
let n_next = (tip - v_next).cross(&(v_next - v_curr));
let n_curr = (tip - v_curr).cross(&(v_next - v_curr));
builder.insert(StaticVertex::from_pos_uv_normal(
tip,
Vector2::new(0.5, 0.0),
n_curr,
));
builder.insert(StaticVertex::from_pos_uv_normal(
v_next,
Vector2::new(tx1, 1.0),
n_next,
));
builder.insert(StaticVertex::from_pos_uv_normal(
v_curr,
Vector2::new(tx0, 1.0),
n_curr,
));
}
let mut data = Self::from_raw_mesh(builder.build());
data.calculate_tangents().unwrap();
data.transform_geometry(transform).unwrap();
data
}
pub fn make_torus(
inner_radius: f32,
outer_radius: f32,
num_rings: usize,
num_segments: usize,
transform: &Matrix4<f32>,
) -> Self {
let mut vertices = Vec::new();
for j in 0..=num_rings {
for i in 0..=num_segments {
let u = i as f32 / num_segments as f32 * std::f32::consts::TAU;
let v = j as f32 / num_rings as f32 * std::f32::consts::TAU;
let center = Vector3::new(inner_radius * u.cos(), inner_radius * u.sin(), 0.0);
let position = Vector3::new(
(inner_radius + outer_radius * v.cos()) * u.cos(),
outer_radius * v.sin(),
(inner_radius + outer_radius * v.cos()) * u.sin(),
);
let uv = Vector2::new(i as f32 / num_segments as f32, j as f32 / num_rings as f32);
let normal = (position - center)
.try_normalize(f32::EPSILON)
.unwrap_or_default();
vertices.push(StaticVertex::from_pos_uv_normal(position, uv, normal));
}
}
let mut triangles = Vec::new();
for j in 1..=num_rings {
for i in 1..=num_segments {
let a = ((num_segments + 1) * j + i - 1) as u32;
let b = ((num_segments + 1) * (j - 1) + i - 1) as u32;
let c = ((num_segments + 1) * (j - 1) + i) as u32;
let d = ((num_segments + 1) * j + i) as u32;
triangles.push(TriangleDefinition([d, b, a]));
triangles.push(TriangleDefinition([d, c, b]));
}
}
let mut data = Self::new(
VertexBuffer::new(vertices.len(), vertices).unwrap(),
TriangleBuffer::new(triangles),
);
data.calculate_tangents().unwrap();
data.transform_geometry(transform).unwrap();
data
}
pub fn make_cylinder(
sides: usize,
r: f32,
h: f32,
caps: bool,
transform: &Matrix4<f32>,
) -> Self {
let mut builder = RawMeshBuilder::<StaticVertex>::new(3 * sides, 3 * sides);
let d_phi = 2.0 * std::f32::consts::PI / sides as f32;
let d_theta = 1.0 / sides as f32;
for i in 0..sides {
let nx0 = (d_phi * i as f32).cos();
let ny0 = (d_phi * i as f32).sin();
let nx1 = (d_phi * (i + 1) as f32).cos();
let ny1 = (d_phi * (i + 1) as f32).sin();
let x0 = r * nx0;
let z0 = r * ny0;
let x1 = r * nx1;
let z1 = r * ny1;
if caps {
let (t_cap_y_curr, t_cap_x_curr) = (d_phi * i as f32).sin_cos();
let (t_cap_y_next, t_cap_x_next) = (d_phi * (i + 1) as f32).sin_cos();
let t_cap_x_curr = t_cap_x_curr * 0.5 + 0.5;
let t_cap_y_curr = t_cap_y_curr * 0.5 + 0.5;
let t_cap_x_next = t_cap_x_next * 0.5 + 0.5;
let t_cap_y_next = t_cap_y_next * 0.5 + 0.5;
builder.insert(StaticVertex::from_pos_uv_normal(
Vector3::new(x1, h, z1),
Vector2::new(t_cap_x_next, t_cap_y_next),
Vector3::new(0.0, 1.0, 0.0),
));
builder.insert(StaticVertex::from_pos_uv_normal(
Vector3::new(x0, h, z0),
Vector2::new(t_cap_x_curr, t_cap_y_curr),
Vector3::new(0.0, 1.0, 0.0),
));
builder.insert(StaticVertex::from_pos_uv_normal(
Vector3::new(0.0, h, 0.0),
Vector2::new(0.5, 0.5),
Vector3::new(0.0, 1.0, 0.0),
));
builder.insert(StaticVertex::from_pos_uv_normal(
Vector3::new(x0, 0.0, z0),
Vector2::new(t_cap_x_curr, t_cap_y_curr),
Vector3::new(0.0, -1.0, 0.0),
));
builder.insert(StaticVertex::from_pos_uv_normal(
Vector3::new(x1, 0.0, z1),
Vector2::new(t_cap_x_next, t_cap_y_next),
Vector3::new(0.0, -1.0, 0.0),
));
builder.insert(StaticVertex::from_pos_uv_normal(
Vector3::new(0.0, 0.0, 0.0),
Vector2::new(0.5, 0.5),
Vector3::new(0.0, -1.0, 0.0),
));
}
let t_side_curr = d_theta * i as f32;
let t_side_next = d_theta * (i + 1) as f32;
builder.insert(StaticVertex::from_pos_uv_normal(
Vector3::new(x0, 0.0, z0),
Vector2::new(t_side_curr, 0.0),
Vector3::new(x0, 0.0, z0),
));
builder.insert(StaticVertex::from_pos_uv_normal(
Vector3::new(x0, h, z0),
Vector2::new(t_side_curr, 1.0),
Vector3::new(x0, 0.0, z0),
));
builder.insert(StaticVertex::from_pos_uv_normal(
Vector3::new(x1, 0.0, z1),
Vector2::new(t_side_next, 0.0),
Vector3::new(x1, 0.0, z1),
));
builder.insert(StaticVertex::from_pos_uv_normal(
Vector3::new(x1, 0.0, z1),
Vector2::new(t_side_next, 0.0),
Vector3::new(x1, 0.0, z1),
));
builder.insert(StaticVertex::from_pos_uv_normal(
Vector3::new(x0, h, z0),
Vector2::new(t_side_curr, 1.0),
Vector3::new(x0, 0.0, z0),
));
builder.insert(StaticVertex::from_pos_uv_normal(
Vector3::new(x1, h, z1),
Vector2::new(t_side_next, 1.0),
Vector3::new(x1, 0.0, z1),
));
}
let mut data = Self::from_raw_mesh(builder.build());
data.calculate_tangents().unwrap();
data.transform_geometry(transform).unwrap();
data
}
pub fn make_cube(transform: Matrix4<f32>) -> Self {
let vertices = vec![
StaticVertex {
position: Vector3::new(-0.5, -0.5, 0.5),
normal: Vector3::z(),
tex_coord: Vector2::default(),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::new(-0.5, 0.5, 0.5),
normal: Vector3::z(),
tex_coord: Vector2::y(),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::new(0.5, 0.5, 0.5),
normal: Vector3::z(),
tex_coord: Vector2::new(1.0, 1.0),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::new(0.5, -0.5, 0.5),
normal: Vector3::z(),
tex_coord: Vector2::x(),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::new(-0.5, -0.5, -0.5),
normal: -Vector3::z(),
tex_coord: Vector2::default(),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::new(-0.5, 0.5, -0.5),
normal: -Vector3::z(),
tex_coord: Vector2::y(),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::new(0.5, 0.5, -0.5),
normal: -Vector3::z(),
tex_coord: Vector2::new(1.0, 1.0),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::new(0.5, -0.5, -0.5),
normal: -Vector3::z(),
tex_coord: Vector2::x(),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::new(-0.5, -0.5, -0.5),
normal: -Vector3::x(),
tex_coord: Vector2::default(),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::new(-0.5, 0.5, -0.5),
normal: -Vector3::x(),
tex_coord: Vector2::y(),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::new(-0.5, 0.5, 0.5),
normal: -Vector3::x(),
tex_coord: Vector2::new(1.0, 1.0),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::new(-0.5, -0.5, 0.5),
normal: -Vector3::x(),
tex_coord: Vector2::x(),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::new(0.5, -0.5, -0.5),
normal: Vector3::x(),
tex_coord: Vector2::default(),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::new(0.5, 0.5, -0.5),
normal: Vector3::x(),
tex_coord: Vector2::y(),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::new(0.5, 0.5, 0.5),
normal: Vector3::x(),
tex_coord: Vector2::new(1.0, 1.0),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::new(0.5, -0.5, 0.5),
normal: Vector3::x(),
tex_coord: Vector2::x(),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::new(-0.5, 0.5, 0.5),
normal: Vector3::y(),
tex_coord: Vector2::default(),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::new(-0.5, 0.5, -0.5),
normal: Vector3::y(),
tex_coord: Vector2::y(),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::new(0.5, 0.5, -0.5),
normal: Vector3::y(),
tex_coord: Vector2::new(1.0, 1.0),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::new(0.5, 0.5, 0.5),
normal: Vector3::y(),
tex_coord: Vector2::x(),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::new(-0.5, -0.5, 0.5),
normal: -Vector3::y(),
tex_coord: Vector2::default(),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::new(-0.5, -0.5, -0.5),
normal: -Vector3::y(),
tex_coord: Vector2::y(),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::new(0.5, -0.5, -0.5),
normal: -Vector3::y(),
tex_coord: Vector2::new(1.0, 1.0),
tangent: Vector4::default(),
},
StaticVertex {
position: Vector3::new(0.5, -0.5, 0.5),
normal: -Vector3::y(),
tex_coord: Vector2::x(),
tangent: Vector4::default(),
},
];
let triangles = vec![
TriangleDefinition([2, 1, 0]),
TriangleDefinition([3, 2, 0]),
TriangleDefinition([4, 5, 6]),
TriangleDefinition([4, 6, 7]),
TriangleDefinition([10, 9, 8]),
TriangleDefinition([11, 10, 8]),
TriangleDefinition([12, 13, 14]),
TriangleDefinition([12, 14, 15]),
TriangleDefinition([18, 17, 16]),
TriangleDefinition([19, 18, 16]),
TriangleDefinition([20, 21, 22]),
TriangleDefinition([20, 22, 23]),
];
let mut data = Self::new(
VertexBuffer::new(vertices.len(), vertices).unwrap(),
TriangleBuffer::new(triangles),
);
data.calculate_tangents().unwrap();
data.transform_geometry(&transform).unwrap();
data
}
pub fn content_hash(&self) -> u64 {
hash_combine(
self.geometry_buffer.content_hash(),
self.vertex_buffer.content_hash(),
)
}
pub fn clear(&mut self) {
self.geometry_buffer.modify().clear();
self.vertex_buffer.modify().clear();
}
}
impl Visit for SurfaceData {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
let mut region = visitor.enter_region(name)?;
self.vertex_buffer.visit("VertexBuffer", &mut region)?;
self.geometry_buffer.visit("GeometryBuffer", &mut region)?;
Ok(())
}
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct VertexWeight {
pub value: f32,
pub effector: ErasedHandle,
}
impl Default for VertexWeight {
fn default() -> Self {
Self {
value: 0.0,
effector: ErasedHandle::none(),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Default)]
pub struct VertexWeightSet {
weights: [VertexWeight; 4],
count: usize,
}
impl VertexWeightSet {
pub fn push(&mut self, weight: VertexWeight) -> bool {
if self.count < self.weights.len() {
self.weights[self.count] = weight;
self.count += 1;
true
} else {
false
}
}
pub fn len(&self) -> usize {
self.count
}
pub fn is_empty(&self) -> bool {
self.count == 0
}
pub fn iter(&self) -> std::slice::Iter<VertexWeight> {
self.weights[0..self.count].iter()
}
pub fn iter_mut(&mut self) -> std::slice::IterMut<VertexWeight> {
self.weights[0..self.count].iter_mut()
}
pub fn normalize(&mut self) {
let len = self.iter().fold(0.0, |qs, w| qs + w.value * w.value).sqrt();
if len >= f32::EPSILON {
let k = 1.0 / len;
for w in self.iter_mut() {
w.value *= k;
}
}
}
}
pub type SurfaceResource = Resource<SurfaceData>;
pub trait SurfaceResourceExtension {
fn deep_clone(&self) -> Self;
}
impl SurfaceResourceExtension for SurfaceResource {
fn deep_clone(&self) -> Self {
Self::new_ok(
Uuid::new_v4(),
ResourceKind::Embedded,
self.data_ref().clone(),
)
}
}
#[derive(Debug, Reflect, Visit, PartialEq)]
pub struct Surface {
pub(crate) data: InheritableVariable<SurfaceResource>,
pub(crate) material: InheritableVariable<MaterialResource>,
pub bones: InheritableVariable<Vec<Handle<Node>>>,
unique_material: InheritableVariable<bool>,
#[reflect(hidden)]
#[visit(skip)]
pub(crate) vertex_weights: Vec<VertexWeightSet>,
}
uuid_provider!(Surface = "485caf12-4e7d-4b1a-b6bd-0681fd92f789");
impl Clone for Surface {
fn clone(&self) -> Self {
Self {
data: self.data.clone(),
material: if *self.unique_material {
self.material.deep_copy_as_embedded().into()
} else {
self.material.clone()
},
bones: self.bones.clone(),
unique_material: self.unique_material.clone(),
vertex_weights: self.vertex_weights.clone(),
}
}
}
impl Default for Surface {
fn default() -> Self {
Self {
data: SurfaceResource::new_ok(
Uuid::new_v4(),
ResourceKind::Embedded,
SurfaceData::make_cube(Matrix4::identity()),
)
.into(),
material: MaterialResource::new_ok(
Uuid::new_v4(),
Default::default(),
Material::standard(),
)
.into(),
vertex_weights: Default::default(),
bones: Default::default(),
unique_material: Default::default(),
}
}
}
impl Surface {
#[inline]
pub fn new(data: SurfaceResource) -> Self {
Self {
data: data.into(),
..Default::default()
}
}
pub fn material_id(&self) -> u64 {
self.material.key()
}
pub fn batch_id(&self) -> u64 {
let mut hasher = FxHasher::default();
hasher.write_u64(self.material_id());
hasher.write_u64(self.data.key());
hasher.finish()
}
#[inline]
pub fn data(&self) -> SurfaceResource {
(*self.data).clone()
}
#[inline]
pub fn data_ref(&self) -> &SurfaceResource {
&self.data
}
pub fn material(&self) -> &MaterialResource {
&self.material
}
pub fn set_material(&mut self, material: MaterialResource) {
self.material.set_value_and_mark_modified(material);
}
#[inline]
pub fn bones(&self) -> &[Handle<Node>] {
&self.bones
}
pub fn is_unique_material(&self) -> bool {
*self.unique_material
}
pub fn set_unique_material(&mut self, unique: bool) {
self.unique_material.set_value_and_mark_modified(unique);
}
}
pub struct SurfaceBuilder {
data: SurfaceResource,
material: Option<MaterialResource>,
bones: Vec<Handle<Node>>,
unique_material: bool,
}
impl SurfaceBuilder {
pub fn new(data: SurfaceResource) -> Self {
Self {
data,
material: None,
bones: Default::default(),
unique_material: false,
}
}
pub fn with_material(mut self, material: MaterialResource) -> Self {
self.material = Some(material);
self
}
pub fn with_bones(mut self, bones: Vec<Handle<Node>>) -> Self {
self.bones = bones;
self
}
pub fn with_unique_material(mut self, unique: bool) -> Self {
self.unique_material = unique;
self
}
pub fn build(self) -> Surface {
Surface {
data: self.data.into(),
material: self
.material
.unwrap_or_else(|| {
MaterialResource::new_ok(
Uuid::new_v4(),
Default::default(),
Material::standard(),
)
})
.into(),
vertex_weights: Default::default(),
bones: self.bones.into(),
unique_material: self.unique_material.into(),
}
}
}
pub struct SurfaceDataLoader {}
impl ResourceLoader for SurfaceDataLoader {
fn extensions(&self) -> &[&str] {
&["surface"]
}
fn is_native_extension(&self, ext: &str) -> bool {
fyrox_core::cmp_strings_case_insensitive(ext, "surface")
}
fn data_type_uuid(&self) -> Uuid {
<SurfaceData as TypeUuidProvider>::type_uuid()
}
fn load(&self, path: PathBuf, io: Arc<dyn ResourceIo>) -> BoxedLoaderFuture {
Box::pin(async move {
let io = io.as_ref();
let data = io.load_file(&path).await.map_err(LoadError::new)?;
let mut visitor = Visitor::load_from_memory(&data).map_err(LoadError::new)?;
let mut surface_data = SurfaceData::default();
surface_data
.visit("SurfaceData", &mut visitor)
.map_err(LoadError::new)?;
Ok(LoaderPayload::new(surface_data))
})
}
}
pub static CUBE: LazyLock<BuiltInResource<SurfaceData>> = LazyLock::new(|| {
BuiltInResource::new_no_source(
"Cube Surface",
SurfaceResource::new_ok(
uuid!("d3a4604a-e1c6-430b-b524-8d3213723952"),
ResourceKind::External,
SurfaceData::make_cube(Matrix4::identity()),
),
)
});
pub static QUAD: LazyLock<BuiltInResource<SurfaceData>> = LazyLock::new(|| {
BuiltInResource::new_no_source(
"Quad Surface",
SurfaceResource::new_ok(
uuid!("a124317f-640b-4c1b-9fdc-af62f745eeba"),
ResourceKind::External,
SurfaceData::make_quad(&Matrix4::identity()),
),
)
});
pub static CYLINDER: LazyLock<BuiltInResource<SurfaceData>> = LazyLock::new(|| {
BuiltInResource::new_no_source(
"Cylinder Surface",
SurfaceResource::new_ok(
uuid!("16300ec8-4446-41a7-8ad6-9b45428d0b1b"),
ResourceKind::External,
SurfaceData::make_cylinder(32, 1.0, 1.0, true, &Matrix4::identity()),
),
)
});
pub static SPHERE: LazyLock<BuiltInResource<SurfaceData>> = LazyLock::new(|| {
BuiltInResource::new_no_source(
"Sphere Surface",
SurfaceResource::new_ok(
uuid!("ff1811ba-b9ad-4c37-89b8-503f79aaa4bd"),
ResourceKind::External,
SurfaceData::make_sphere(32, 32, 1.0, &Matrix4::identity()),
),
)
});
pub static CONE: LazyLock<BuiltInResource<SurfaceData>> = LazyLock::new(|| {
BuiltInResource::new_no_source(
"Cone Surface",
SurfaceResource::new_ok(
uuid!("e4e79405-39c5-4fe4-ba3e-c961f3d7379e"),
ResourceKind::External,
SurfaceData::make_cone(32, 1.0, 1.0, &Matrix4::identity()),
),
)
});
pub static TORUS: LazyLock<BuiltInResource<SurfaceData>> = LazyLock::new(|| {
BuiltInResource::new_no_source(
"Torus Surface",
SurfaceResource::new_ok(
uuid!("d2bb5455-c72e-475d-90da-e3a7bd5b7d07"),
ResourceKind::External,
SurfaceData::make_torus(1.0, 0.25, 32, 32, &Matrix4::identity()),
),
)
});