pub mod parser;
pub mod processor;
pub mod types;
pub use parser::MeshParser;
pub use processor::{MeshProcessor, MeshStats};
pub use types::{
AABB,
BlendShape,
BlendShapeChannel,
BlendShapeData,
BlendShapeVertex,
ChannelInfo,
CompressedMesh,
Mesh,
MeshConfig,
MeshInfo,
MeshResult,
PackedFloatVector,
PackedIntVector,
StreamingInfo,
SubMesh,
VertexData,
};
pub struct MeshManager {
processor: MeshProcessor,
}
impl MeshManager {
pub fn new(version: crate::unity_version::UnityVersion) -> Self {
Self {
processor: MeshProcessor::new(version),
}
}
pub fn with_config(version: crate::unity_version::UnityVersion, config: MeshConfig) -> Self {
Self {
processor: MeshProcessor::with_config(version, config),
}
}
pub fn process_mesh(
&self,
object: &crate::object::UnityObject,
) -> crate::error::Result<MeshResult> {
self.processor.parse_mesh(object)
}
pub fn export_to_obj(&self, mesh: &Mesh) -> crate::error::Result<String> {
self.processor.export_to_obj(mesh)
}
pub fn get_statistics(&self, meshes: &[&Mesh]) -> MeshStats {
self.processor.get_mesh_stats(meshes)
}
pub fn validate_mesh(&self, mesh: &Mesh) -> crate::error::Result<()> {
self.processor.validate_mesh(mesh)
}
pub fn get_supported_features(&self) -> Vec<&'static str> {
self.processor.get_supported_features()
}
pub fn is_feature_supported(&self, feature: &str) -> bool {
self.processor.is_feature_supported(feature)
}
pub fn config(&self) -> &MeshConfig {
self.processor.config()
}
pub fn set_config(&mut self, config: MeshConfig) {
self.processor.set_config(config);
}
pub fn version(&self) -> &crate::unity_version::UnityVersion {
self.processor.version()
}
pub fn set_version(&mut self, version: crate::unity_version::UnityVersion) {
self.processor.set_version(version);
}
pub fn extract_vertices(&self, mesh: &Mesh) -> crate::error::Result<Vec<[f32; 3]>> {
self.processor.extract_vertex_positions(mesh)
}
pub fn extract_normals(&self, mesh: &Mesh) -> crate::error::Result<Vec<[f32; 3]>> {
self.processor.extract_vertex_normals(mesh)
}
pub fn extract_uvs(&self, mesh: &Mesh) -> crate::error::Result<Vec<[f32; 2]>> {
self.processor.extract_uv_coordinates(mesh)
}
pub fn extract_indices(&self, mesh: &Mesh) -> crate::error::Result<Vec<u32>> {
self.processor.extract_triangle_indices(mesh)
}
}
impl Default for MeshManager {
fn default() -> Self {
Self::new(crate::unity_version::UnityVersion::default())
}
}
pub fn create_manager(version: crate::unity_version::UnityVersion) -> MeshManager {
MeshManager::new(version)
}
pub fn create_performance_manager(version: crate::unity_version::UnityVersion) -> MeshManager {
let config = MeshConfig {
extract_vertices: false,
extract_indices: false,
process_blend_shapes: false,
decompress_meshes: false,
max_vertex_count: Some(10000),
};
MeshManager::with_config(version, config)
}
pub fn create_full_manager(version: crate::unity_version::UnityVersion) -> MeshManager {
let config = MeshConfig {
extract_vertices: true,
extract_indices: true,
process_blend_shapes: true,
decompress_meshes: true,
max_vertex_count: None,
};
MeshManager::with_config(version, config)
}
pub fn parse_mesh(
object: &crate::object::UnityObject,
version: &crate::unity_version::UnityVersion,
) -> crate::error::Result<Mesh> {
let parser = MeshParser::new(version.clone());
let result = parser.parse_from_unity_object(object)?;
Ok(result.mesh)
}
pub fn export_mesh_to_obj(
mesh: &Mesh,
version: &crate::unity_version::UnityVersion,
) -> crate::error::Result<String> {
let processor = MeshProcessor::new(version.clone());
processor.export_to_obj(mesh)
}
pub fn validate_mesh(mesh: &Mesh) -> crate::error::Result<()> {
let processor = MeshProcessor::default();
processor.validate_mesh(mesh)
}
pub fn get_vertex_count(mesh: &Mesh) -> u32 {
mesh.vertex_count()
}
pub fn get_triangle_count(mesh: &Mesh) -> u32 {
mesh.triangle_count()
}
pub fn has_blend_shapes(mesh: &Mesh) -> bool {
mesh.has_blend_shapes()
}
pub fn is_compressed_mesh(mesh: &Mesh) -> bool {
mesh.is_compressed()
}
pub fn has_streaming_data(mesh: &Mesh) -> bool {
mesh.has_streaming_data()
}
pub fn get_mesh_bounds(mesh: &Mesh) -> &AABB {
mesh.bounds()
}
pub fn is_mesh_feature_supported(
version: &crate::unity_version::UnityVersion,
feature: &str,
) -> bool {
match feature {
"basic_mesh" | "sub_meshes" | "vertex_data" => true,
"blend_shapes" | "compressed_mesh" => version.major >= 5,
"mesh_optimization" | "streaming_info" => version.major >= 2017,
"mesh_usage_flags" => version.major >= 2018,
"mesh_topology" | "vertex_attributes" => version.major >= 2019,
_ => false,
}
}
pub fn get_recommended_config(version: &crate::unity_version::UnityVersion) -> MeshConfig {
if version.major >= 2019 {
MeshConfig {
extract_vertices: true,
extract_indices: true,
process_blend_shapes: true,
decompress_meshes: true,
max_vertex_count: None,
}
} else if version.major >= 2017 {
MeshConfig {
extract_vertices: true,
extract_indices: true,
process_blend_shapes: true,
decompress_meshes: true,
max_vertex_count: Some(100000),
}
} else if version.major >= 5 {
MeshConfig {
extract_vertices: true,
extract_indices: true,
process_blend_shapes: false,
decompress_meshes: false,
max_vertex_count: Some(50000),
}
} else {
MeshConfig {
extract_vertices: false,
extract_indices: false,
process_blend_shapes: false,
decompress_meshes: false,
max_vertex_count: Some(10000),
}
}
}
#[derive(Debug, Clone)]
pub struct ProcessingOptions {
pub parallel_processing: bool,
pub cache_results: bool,
pub validate_meshes: bool,
pub generate_lods: bool,
}
impl Default for ProcessingOptions {
fn default() -> Self {
Self {
parallel_processing: false,
cache_results: true,
validate_meshes: true,
generate_lods: false,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_manager_creation() {
let version = crate::unity_version::UnityVersion::default();
let manager = create_manager(version);
assert!(manager.get_supported_features().contains(&"basic_mesh"));
}
#[test]
fn test_performance_manager() {
let version = crate::unity_version::UnityVersion::default();
let manager = create_performance_manager(version);
assert!(!manager.config().extract_vertices);
assert!(!manager.config().process_blend_shapes);
}
#[test]
fn test_full_manager() {
let version = crate::unity_version::UnityVersion::default();
let manager = create_full_manager(version);
assert!(manager.config().extract_vertices);
assert!(manager.config().process_blend_shapes);
}
#[test]
fn test_feature_support() {
let version_2020 =
crate::unity_version::UnityVersion::parse_version("2020.3.12f1").unwrap();
assert!(is_mesh_feature_supported(&version_2020, "basic_mesh"));
assert!(is_mesh_feature_supported(&version_2020, "blend_shapes"));
assert!(is_mesh_feature_supported(
&version_2020,
"vertex_attributes"
));
let version_2017 =
crate::unity_version::UnityVersion::parse_version("2017.4.40f1").unwrap();
assert!(is_mesh_feature_supported(&version_2017, "streaming_info"));
assert!(!is_mesh_feature_supported(
&version_2017,
"vertex_attributes"
));
}
#[test]
fn test_recommended_config() {
let version_2020 =
crate::unity_version::UnityVersion::parse_version("2020.3.12f1").unwrap();
let config = get_recommended_config(&version_2020);
assert!(config.extract_vertices);
assert!(config.process_blend_shapes);
assert!(config.decompress_meshes);
let version_5 = crate::unity_version::UnityVersion::parse_version("5.6.7f1").unwrap();
let config = get_recommended_config(&version_5);
assert!(config.extract_vertices);
assert!(!config.process_blend_shapes);
}
}