use std::path::PathBuf; use regex::Regex; pub(crate) fn merge_shader<'a>(shader: &'a str) -> String { const TOOLS: &'static str = r#"// This is a tool that merges the shader code with the shader code from the shader module. struct UniformCommonTools { model_matrix: mat4x4f, view_matrix: mat4x4f, proj_matrix: mat4x4f, camera_x: f32, camera_y: f32, camera_z: f32, camera_target_x: f32, camera_target_y: f32, camera_target_z: f32, camera_up_x: f32, camera_up_y: f32, camera_up_z: f32, // camera_position: vec3f, // camera_front: vec3f, // camera_up: vec3f, } @group(0) @binding(0) var common_tools: UniformCommonTools; "#; let mut visited_files = std::collections::HashSet::new(); let path: PathBuf = PathBuf::from(shader); let base = match path.canonicalize() { Ok(path) => path.parent().unwrap().to_owned(), Err(e) => { // panic!("Failed to canonicalize path: {}", e); PathBuf::from(r#"C:\Users\qwin7\projects\radarmp\mp_elements\shaders"#) } }; let mut result = process_file(&path, &mut visited_files, &base); // 将工具代码插入到 shader 代码的开头 result.insert_str(0, TOOLS); return result; } fn process_file<'a, P: AsRef + 'a, B: AsRef>( file_path: P, visited_files: &mut std::collections::HashSet, base_path: B, ) -> String { let base_path = base_path.as_ref(); let file_path = file_path.as_ref(); // 如果该文件已经处理过,则避免死循环 if visited_files.contains(file_path) { return String::new(); // 返回空字符串表示已经处理过 } visited_files.insert(file_path.to_path_buf()); let content = std::fs::read_to_string(file_path).unwrap_or_else(|_| { panic!("Failed to read file: {}", file_path.display()); }); let re = Regex::new(r#"#include\s+\"([^\"]+\.wgsl)\";"#).unwrap(); let result = re.replace_all(&content, |caps: ®ex::Captures| { let included_file = &caps[1]; // 获取文件名 let included_path = resolve_included_file(included_file, base_path.as_ref()); let included_content = process_file( &included_path, visited_files, base_path.parent().unwrap_or(base_path), ); included_content }); result.to_string() } fn resolve_included_file(included_file: &str, base_path: &std::path::Path) -> std::path::PathBuf { let included_path = std::path::Path::new(included_file); if included_path.is_relative() { base_path.join(included_path) } else { included_path.to_path_buf() } } mod test { use super::merge_shader; #[test] fn test() { let merged = merge_shader("/Users/tsuki/projects/mp/mp_elements/shaders/ppi.wgsl"); println!("{}", merged); } }