oxygengine_composite_renderer/system/
mesh.rs

1use crate::{
2    component::{CompositeMesh, CompositeRenderable, CompositeSurfaceCache},
3    composite_renderer::{Command, Mask, PathElement, Renderable, Triangles},
4    math::Vec2,
5    mesh_asset_protocol::{Mesh, MeshAsset, MeshVertex},
6};
7use core::{
8    assets::{asset::AssetId, database::AssetsDatabase},
9    ecs::{Comp, Universe, WorldRef},
10};
11use std::collections::HashMap;
12
13#[derive(Debug, Default)]
14pub struct CompositeMeshSystemCache {
15    meshes_cache: HashMap<String, Mesh>,
16    meshes_table: HashMap<AssetId, String>,
17}
18
19pub type CompositeMeshSystemResources<'a> = (
20    WorldRef,
21    &'a AssetsDatabase,
22    &'a mut CompositeMeshSystemCache,
23    Comp<&'a mut CompositeMesh>,
24    Comp<&'a mut CompositeRenderable>,
25    Comp<&'a mut CompositeSurfaceCache>,
26);
27
28pub fn composite_mesh_system(universe: &mut Universe) {
29    let (world, assets, mut cache, ..) = universe.query_resources::<CompositeMeshSystemResources>();
30
31    for id in assets.lately_loaded_protocol("mesh") {
32        let id = *id;
33        let asset = assets
34            .asset_by_id(id)
35            .expect("trying to use not loaded mesh asset");
36        let path = asset.path().to_owned();
37        let asset = asset
38            .get::<MeshAsset>()
39            .expect("trying to use non-mesh asset");
40        let mesh = asset.mesh().clone();
41        cache.meshes_cache.insert(path.clone(), mesh);
42        cache.meshes_table.insert(id, path);
43    }
44    for id in assets.lately_unloaded_protocol("mesh") {
45        if let Some(path) = cache.meshes_table.remove(id) {
46            cache.meshes_cache.remove(&path);
47        }
48    }
49
50    for (_, (mesh, renderable, surface)) in world
51        .query::<(
52            &mut CompositeMesh,
53            &mut CompositeRenderable,
54            Option<&mut CompositeSurfaceCache>,
55        )>()
56        .iter()
57    {
58        if mesh.dirty_mesh || mesh.dirty_visuals {
59            if let Some(r) = build_renderable(mesh, &cache.meshes_cache) {
60                renderable.0 = r;
61                if let Some(surface) = surface {
62                    surface.rebuild();
63                }
64                mesh.dirty_mesh = false;
65                mesh.dirty_visuals = false;
66            }
67        }
68    }
69}
70
71fn build_renderable<'a>(
72    mesh: &mut CompositeMesh,
73    meshes: &HashMap<String, Mesh>,
74) -> Option<Renderable<'a>> {
75    if let Some(asset) = meshes.get(mesh.mesh()) {
76        if mesh.dirty_mesh {
77            if let Some(root) = &asset.rig {
78                mesh.setup_bones_from_rig(root);
79            }
80        }
81        if mesh.dirty_visuals {
82            let vertices = if let Some(root) = &asset.rig {
83                mesh.rebuild_model_space(root);
84                build_skined_vertices(&asset.vertices, mesh)
85            } else {
86                build_vertices(&asset.vertices)
87            };
88            let masks = asset
89                .masks
90                .iter()
91                .map(|indices| build_mask(&vertices, &indices.indices))
92                .collect::<Vec<_>>();
93            let mut meta = asset
94                .submeshes
95                .iter()
96                .zip(mesh.materials().iter())
97                .filter_map(|(submesh, material)| {
98                    if material.alpha > 0.0 {
99                        let triangles = Triangles {
100                            image: material.image.to_string().into(),
101                            color: Default::default(),
102                            vertices: vertices.to_vec(),
103                            faces: submesh.cached_faces().to_vec(),
104                        };
105                        let masks = submesh
106                            .masks
107                            .iter()
108                            .map(|i| masks[*i].to_vec())
109                            .collect::<Vec<_>>();
110                        Some((triangles, material.alpha, material.order, masks))
111                    } else {
112                        None
113                    }
114                })
115                .collect::<Vec<_>>();
116            meta.sort_by(|a, b| a.2.partial_cmp(&b.2).unwrap());
117            let count = meta.len() * 4 + meta.iter().fold(0, |a, v| a + v.3.len());
118            let mut commands = Vec::with_capacity(count);
119            for (triangles, alpha, _, masks) in meta {
120                commands.push(Command::Store);
121                for mask in masks {
122                    let mask = Mask { elements: mask };
123                    commands.push(Command::Draw(mask.into()));
124                }
125                commands.push(Command::Alpha(alpha));
126                commands.push(Command::Draw(triangles.into()));
127                commands.push(Command::Restore);
128            }
129            return Some(Renderable::Commands(commands));
130        }
131    }
132    None
133}
134
135fn build_skined_vertices(vertices: &[MeshVertex], mesh: &CompositeMesh) -> Vec<(Vec2, Vec2)> {
136    vertices
137        .iter()
138        .map(|v| {
139            let p = v.bone_info.iter().fold(Vec2::default(), |a, i| {
140                let p = if let Some(m) = mesh.bones_model_space.get(&i.name) {
141                    *m * v.position
142                } else {
143                    v.position
144                };
145                a + p * i.weight
146            });
147            (p, v.tex_coord)
148        })
149        .collect::<Vec<_>>()
150}
151
152fn build_vertices(vertices: &[MeshVertex]) -> Vec<(Vec2, Vec2)> {
153    vertices
154        .iter()
155        .map(|v| (v.position, v.tex_coord))
156        .collect::<Vec<_>>()
157}
158
159fn build_mask(vertices: &[(Vec2, Vec2)], indices: &[usize]) -> Vec<PathElement> {
160    let mut result = Vec::with_capacity(indices.len());
161    for index in indices {
162        if *index == 0 {
163            result.push(PathElement::MoveTo(vertices[*index].0));
164        } else {
165            result.push(PathElement::LineTo(vertices[*index].0));
166        }
167    }
168    result
169}