Skip to main content

gizmo_engine/
asset_server.rs

1use crate::core::asset::Handle;
2use crate::renderer::async_assets::AsyncAssetLoader;
3use crate::renderer::components::{Material, Mesh};
4use wgpu::util::DeviceExt;
5
6pub struct AssetServer {
7    pub loader: AsyncAssetLoader,
8    mesh_paths: std::collections::HashMap<String, Handle<Mesh>>,
9    _material_paths: std::collections::HashMap<String, Handle<Material>>,
10    pub completed_gltfs: Vec<crate::renderer::async_assets::GltfImportCompletion>,
11    pub completed_gltf_errors: Vec<crate::renderer::async_assets::GltfImportError>,
12    /// Arka planda decode'u tamamlanan streaming texture'ları. `asset_server_update_system`
13    /// bunları `drain_completed`'dan buraya biriktirir; `TextureStreamingSystem` her frame
14    /// tüketip GPU'ya yükler ve ilgili entity'lerin `Material.bind_group`'unu günceller.
15    /// (Eskiden `completed.textures` sessizce ATILIYORDU → streaming görsel olarak no-op'tu.)
16    pub completed_textures: Vec<crate::renderer::async_assets::TextureReloadCompletion>,
17    #[cfg(all(feature = "render", not(target_arch = "wasm32")))]
18    pub watcher: Option<crate::renderer::hot_reload::AssetWatcher>,
19}
20
21impl Default for AssetServer {
22    fn default() -> Self {
23        Self::new()
24    }
25}
26
27impl AssetServer {
28    pub fn new() -> Self {
29        #[cfg(all(feature = "render", not(target_arch = "wasm32")))]
30        let watcher = crate::renderer::hot_reload::AssetWatcher::new(&["assets", "demo/assets"]);
31
32        Self {
33            loader: AsyncAssetLoader::new(),
34            mesh_paths: std::collections::HashMap::new(),
35            _material_paths: std::collections::HashMap::new(),
36            completed_gltfs: Vec::new(),
37            completed_gltf_errors: Vec::new(),
38            completed_textures: Vec::new(),
39            #[cfg(all(feature = "render", not(target_arch = "wasm32")))]
40            watcher,
41        }
42    }
43
44    pub fn load_mesh(&mut self, path: &str) -> Handle<Mesh> {
45        if let Some(handle) = self.mesh_paths.get(path) {
46            return handle.clone();
47        }
48        let handle = crate::core::asset::Handle::weak(crate::core::asset::HandleId::new());
49        self.loader.request_obj_load(path.to_string(), handle.id.0);
50        self.mesh_paths.insert(path.to_string(), handle.clone());
51        handle
52    }
53}
54
55pub fn asset_server_update_system(
56    mut server: crate::core::system::ResMut<AssetServer>,
57    renderer: crate::core::system::ResMut<crate::renderer::Renderer>,
58    mut meshes: crate::core::system::ResMut<crate::core::asset::Assets<Mesh>>,
59) {
60    // Process Hot Reloading
61    #[cfg(all(feature = "render", not(target_arch = "wasm32")))]
62    if let Some(watcher) = &server.watcher {
63        let changed = watcher.poll_changes();
64        for path in changed {
65            let path_str = path.to_string_lossy().to_string();
66            // Check if mesh needs reloading
67            if let Some(handle) = server.mesh_paths.get(&path_str) {
68                tracing::info!("AssetWatcher: Reloading mesh {:?}", path_str);
69                server.loader.request_obj_load(path_str.clone(), handle.id.0);
70            }
71        }
72    }
73
74    // Process garbage collection
75    meshes.process_drops();
76
77    let completed = server.loader.drain_completed();
78
79    server.completed_gltfs.extend(completed.gltfs);
80    server.completed_gltf_errors.extend(completed.gltf_errors);
81    // Decode'u biten streaming texture'ları SAKLA (eskiden burada atılıyordu → no-op).
82    // `TextureStreamingSystem` bunları GPU'ya yükleyip materyallere uygular.
83    server.completed_textures.extend(completed.textures);
84
85    if completed.objs.is_empty() {
86        return;
87    }
88
89    for obj in completed.objs {
90        let mesh_source = format!("obj:{}", obj.path);
91        // Create wgpu buffer
92        let vbuf = renderer
93            .device
94            .create_buffer_init(&wgpu::util::BufferInitDescriptor {
95                label: Some(&format!("Obj VBuf: {}", obj.path)),
96                contents: bytemuck::cast_slice(&obj.vertices),
97                usage: wgpu::BufferUsages::VERTEX,
98            });
99        let mesh = Mesh::new(
100            &renderer.device,
101            std::sync::Arc::new(vbuf),
102            &obj.vertices,
103            gizmo_math::Vec3::ZERO,
104            mesh_source,
105        );
106        for handle_id in obj.handle_ids {
107            let handle = crate::core::asset::Handle::weak(crate::core::asset::HandleId(handle_id));
108            meshes.insert(&handle, mesh.clone());
109        }
110    }
111}
112
113pub struct AssetServerPlugin;
114
115impl<State: 'static> crate::app::Plugin<State> for AssetServerPlugin {
116    fn build(&self, app: &mut crate::app::App<State>) {
117        app.world.insert_resource(AssetServer::new());
118        app.schedule.add_di_system(asset_server_update_system);
119        // Distance-based texture streaming: request nearby high-res textures and
120        // upload+apply the ones the worker finished decoding. Runs after the drain
121        // above populated `AssetServer::completed_textures` (a one-frame lag if it
122        // happens to run first is harmless).
123        app.schedule.add_di_system(
124            gizmo_core::system::SystemConfig::new(Box::new(
125                crate::systems::streaming::TextureStreamingSystem,
126            ))
127            .label("texture_streaming"),
128        );
129    }
130}