use nalgebra_glm::{Vec3, vec3};
use crate::ecs::generational_registry::{registry_entry_by_name, registry_entry_by_name_mut};
use crate::ecs::lattice::components::{Lattice, LatticeInfluenced};
use crate::ecs::mesh::components::{MorphTarget, MorphTargetData};
use crate::ecs::morph::components::MorphWeights;
use crate::ecs::prefab::resources::mesh_cache_mark_dirty;
use crate::ecs::world::{
Entity, GLOBAL_TRANSFORM, LATTICE, LATTICE_INFLUENCED, MORPH_WEIGHTS, RENDER_MESH, World,
};
pub fn lattice_deformation_system(world: &mut World) {
let influenced_entities: Vec<Entity> = world
.query_entities(LATTICE_INFLUENCED | RENDER_MESH | MORPH_WEIGHTS | GLOBAL_TRANSFORM)
.collect();
if influenced_entities.is_empty() {
return;
}
let lattice_entities: Vec<(Entity, Lattice)> = world
.query_entities(LATTICE)
.filter_map(|entity| world.get_lattice(entity).cloned().map(|l| (entity, l)))
.collect();
if lattice_entities.is_empty() {
return;
}
for entity in influenced_entities {
let Some(influenced) = world.get_lattice_influenced(entity).cloned() else {
continue;
};
let Some((_, lattice)) = lattice_entities
.iter()
.find(|(e, _)| *e == influenced.lattice_entity)
else {
continue;
};
let Some(global_transform) = world.get_global_transform(entity) else {
continue;
};
let current_position = vec3(
global_transform.0[(0, 3)],
global_transform.0[(1, 3)],
global_transform.0[(2, 3)],
);
if !influenced.needs_update(lattice.version, current_position) {
continue;
}
let Some(render_mesh) = world.get_render_mesh(entity) else {
continue;
};
let mesh_name = render_mesh.name.clone();
let Some(mesh) = registry_entry_by_name(&world.resources.mesh_cache.registry, &mesh_name)
else {
continue;
};
let vertex_count = mesh.vertices.len();
let model_matrix = global_transform.0;
let _normal_matrix = nalgebra_glm::transpose(&nalgebra_glm::inverse(
&nalgebra_glm::mat4_to_mat3(&model_matrix),
));
let mut position_displacements = Vec::with_capacity(vertex_count);
let mut normal_displacements = Vec::with_capacity(vertex_count);
let base_positions: Vec<[f32; 3]> = mesh.vertices.iter().map(|v| v.position).collect();
let base_normals: Vec<[f32; 3]> = mesh.vertices.iter().map(|v| v.normal).collect();
for vertex in &mesh.vertices {
let local_pos = vec3(vertex.position[0], vertex.position[1], vertex.position[2]);
let world_pos = nalgebra_glm::vec4_to_vec3(
&(model_matrix * nalgebra_glm::vec4(local_pos.x, local_pos.y, local_pos.z, 1.0)),
);
let displacement = lattice.sample(world_pos);
let inverse_model = nalgebra_glm::inverse(&model_matrix);
let local_displacement = nalgebra_glm::vec4_to_vec3(
&(inverse_model
* nalgebra_glm::vec4(displacement.x, displacement.y, displacement.z, 0.0)),
);
position_displacements.push([
local_displacement.x,
local_displacement.y,
local_displacement.z,
]);
normal_displacements.push([0.0, 0.0, 0.0]);
}
let morph_target =
MorphTarget::new(position_displacements).with_normals(normal_displacements);
let morph_data = MorphTargetData::new(vec![morph_target])
.with_base_data(base_positions, base_normals)
.with_default_weights(vec![1.0]);
let _ = mesh;
if let Some(mesh) =
registry_entry_by_name_mut(&mut world.resources.mesh_cache.registry, &mesh_name)
{
mesh.morph_targets = Some(morph_data);
}
mesh_cache_mark_dirty(&mut world.resources.mesh_cache, mesh_name.clone());
if let Some(morph_weights) = world.get_morph_weights_mut(entity) {
morph_weights.weights = vec![1.0];
}
if let Some(influenced) = world.get_lattice_influenced_mut(entity) {
influenced.mark_updated(lattice.version, current_position);
}
}
}
pub fn create_lattice_entity(
world: &mut World,
bounds_min: Vec3,
bounds_max: Vec3,
dimensions: [usize; 3],
) -> Entity {
let entity = world.spawn_entities(LATTICE, 1)[0];
let lattice = Lattice::new(bounds_min, bounds_max, dimensions);
world.set_lattice(entity, lattice);
entity
}
pub fn register_entity_for_lattice_deformation(
world: &mut World,
entity: Entity,
lattice_entity: Entity,
) {
world.add_components(entity, LATTICE_INFLUENCED | MORPH_WEIGHTS);
world.set_lattice_influenced(entity, LatticeInfluenced::new(lattice_entity));
if let Some(render_mesh) = world.get_render_mesh(entity) {
let mesh_name = render_mesh.name.clone();
world.set_morph_weights(entity, MorphWeights::new(vec![1.0], mesh_name));
}
}