scena 1.7.2

A Rust-native scene-graph renderer with typed scene state, glTF assets, and explicit prepare/render lifecycles.
Documentation
use crate::assets::{
    AssetMaterialFallbackV1, AssetMaterialSource, AssetMaterialSourceKind, TextureSourceFormat,
};
use crate::material::{AlphaMode, MaterialKind, TextureColorSpace};

use super::{
    SceneMaterialInspectionV1, SceneMaterialSlotInspectionV1, SceneMaterialSourceInspectionV1,
};
use crate::scene::inspection::{SceneMaterialInspection, SceneTextureInspection};

pub(super) fn material_inspection_v1(
    material: &SceneMaterialInspection,
) -> SceneMaterialInspectionV1 {
    let fallbacks = material
        .fallbacks()
        .iter()
        .map(AssetMaterialFallbackV1::from)
        .collect::<Vec<_>>();
    SceneMaterialInspectionV1 {
        kind: material_kind_name(material.kind()).to_string(),
        source: material
            .source()
            .map(material_source_inspection_v1)
            .unwrap_or_else(unknown_material_source),
        base_color: material.base_color(),
        alpha_mode: alpha_mode_name(material.alpha_mode()).to_string(),
        textures: material_texture_slot_rows(material, &fallbacks),
        fallbacks,
    }
}

fn material_source_inspection_v1(source: &AssetMaterialSource) -> SceneMaterialSourceInspectionV1 {
    SceneMaterialSourceInspectionV1 {
        kind: material_source_kind_name(source.kind()).to_string(),
        asset_path: source.asset_path().map(|path| path.as_str().to_owned()),
        material_index: source.material_index(),
        reason: source.reason().map(str::to_owned),
    }
}

fn unknown_material_source() -> SceneMaterialSourceInspectionV1 {
    SceneMaterialSourceInspectionV1 {
        kind: "unknown".to_string(),
        asset_path: None,
        material_index: None,
        reason: Some("material source metadata was not recorded".to_string()),
    }
}

fn material_texture_slot_rows(
    material: &SceneMaterialInspection,
    fallbacks: &[AssetMaterialFallbackV1],
) -> Vec<SceneMaterialSlotInspectionV1> {
    [
        ("baseColorTexture", material.base_color_texture()),
        ("normalTexture", material.normal_texture()),
        (
            "metallicRoughnessTexture",
            material.metallic_roughness_texture(),
        ),
        ("occlusionTexture", material.occlusion_texture()),
        ("emissiveTexture", material.emissive_texture()),
        ("clearcoatTexture", material.clearcoat_texture()),
        (
            "clearcoatRoughnessTexture",
            material.clearcoat_roughness_texture(),
        ),
        (
            "clearcoatNormalTexture",
            material.clearcoat_normal_texture(),
        ),
        ("sheenColorTexture", material.sheen_color_texture()),
        ("sheenRoughnessTexture", material.sheen_roughness_texture()),
        ("anisotropyTexture", material.anisotropy_texture()),
        ("iridescenceTexture", material.iridescence_texture()),
        (
            "iridescenceThicknessTexture",
            material.iridescence_thickness_texture(),
        ),
        ("transmissionTexture", material.transmission_texture()),
        ("thicknessTexture", material.thickness_texture()),
    ]
    .into_iter()
    .filter_map(|(slot, texture)| {
        let texture = texture?;
        Some(SceneMaterialSlotInspectionV1 {
            slot: slot.to_string(),
            source_path: texture.source_path().as_str().to_owned(),
            source_format: texture_source_format_name(texture.source_format()).to_string(),
            color_space: texture_color_space_name(texture.color_space()).to_string(),
            decoded_dimensions: texture
                .decoded_dimensions()
                .map(|(width, height)| [width, height]),
            has_decoded_pixels: texture.has_decoded_pixels(),
            provenance: texture.provenance().clone(),
            fallback: matching_fallback(slot, &texture, fallbacks),
        })
    })
    .collect()
}

fn matching_fallback(
    slot: &str,
    texture: &SceneTextureInspection,
    fallbacks: &[AssetMaterialFallbackV1],
) -> Option<AssetMaterialFallbackV1> {
    fallbacks
        .iter()
        .find(|fallback| {
            fallback.material_slot == slot
                && fallback.fallback_path == texture.source_path().as_str()
        })
        .cloned()
}

fn material_source_kind_name(kind: AssetMaterialSourceKind) -> &'static str {
    match kind {
        AssetMaterialSourceKind::UserCreated => "user_created",
        AssetMaterialSourceKind::SourceMaterial => "source_material",
        AssetMaterialSourceKind::GeneratedDefault => "generated_default",
    }
}

fn material_kind_name(kind: MaterialKind) -> &'static str {
    match kind {
        MaterialKind::Unlit => "unlit",
        MaterialKind::PbrMetallicRoughness => "pbr_metallic_roughness",
        MaterialKind::Line => "line",
        MaterialKind::Wireframe => "wireframe",
        MaterialKind::Edge => "edge",
    }
}

fn alpha_mode_name(alpha_mode: AlphaMode) -> &'static str {
    match alpha_mode {
        AlphaMode::Opaque => "opaque",
        AlphaMode::Mask { .. } => "mask",
        AlphaMode::Blend => "blend",
    }
}

fn texture_source_format_name(format: TextureSourceFormat) -> &'static str {
    match format {
        TextureSourceFormat::Png => "png",
        TextureSourceFormat::Jpeg => "jpeg",
        TextureSourceFormat::Webp => "webp",
        TextureSourceFormat::Ktx2Basisu => "ktx2_basisu",
    }
}

fn texture_color_space_name(color_space: TextureColorSpace) -> &'static str {
    match color_space {
        TextureColorSpace::Linear => "linear",
        TextureColorSpace::Srgb => "srgb",
    }
}