use crate::ecs::{Entity, World};
use crate::gfx::draw_preview::{self, DrawMaterial};
use concinnity_cook::authoring::registry::RegisteredType;
use serde_json::{Map, Value};
use super::Apply;
const KEYS: [&str; 2] = ["material", "cull_distance"];
pub(crate) struct DrawChange {
entity: Entity,
material: Option<DrawMaterial>,
cull_distance: Option<f32>,
}
pub(super) fn plan(
world: &World,
ct: RegisteredType,
name: &str,
args: &Map<String, Value>,
keys: &[String],
) -> Option<Apply> {
if ct != RegisteredType::Prop || !keys.iter().all(|k| KEYS.contains(&k.as_str())) {
return None;
}
if !draw_preview::is_available(world) {
return None;
}
let id = crate::ecs::asset_id::lookup(name)?;
let entity = world
.resource::<concinnity_core::ecs::EntityByName>()?
.get(id)?;
let mut change = DrawChange {
entity,
material: None,
cull_distance: None,
};
for key in keys {
match key.as_str() {
"material" => change.material = Some(swap(world, entity, args)?),
_ => change.cull_distance = Some(args.get(key)?.as_f64()? as f32),
}
}
Some(Apply::Draw(change))
}
pub(super) fn commit(world: &mut World, change: DrawChange) {
if let Some(material) = change.material {
draw_preview::apply_material(world, change.entity, material);
}
if let Some(cull_distance) = change.cull_distance {
draw_preview::apply_cull_distance(world, change.entity, cull_distance);
}
}
fn swap(world: &World, entity: Entity, args: &Map<String, Value>) -> Option<DrawMaterial> {
let name = args.get("material")?.as_str()?;
let next = draw_preview::material(world, crate::ecs::asset_id::lookup(name)?)?;
let current = draw_preview::drawn_material(world, entity)?;
current.swappable_with(&next).then_some(next)
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
fn args(v: Value) -> Map<String, Value> {
v.as_object().cloned().unwrap()
}
fn keys(names: &[&str]) -> Vec<String> {
names.iter().map(|k| k.to_string()).collect()
}
#[test]
fn a_non_draw_edit_is_not_claimed() {
let world = World::new();
for (ty, changed) in [
("Prop", "position"),
("Prop", "texture"),
("Model", "material"),
("Material", "roughness"),
] {
let ct = RegisteredType::parse(ty).expect("a registered type");
assert!(
plan(
&world,
ct,
"crate_a",
&args(json!({ changed: "x" })),
&keys(&[changed]),
)
.is_none(),
"{ty}.{changed} is not this path's"
);
}
}
#[test]
fn a_world_without_a_renderer_declines() {
let world = World::new();
assert!(!draw_preview::is_available(&world));
assert!(
plan(
&world,
RegisteredType::Prop,
"crate_a",
&args(json!({ "material": "steel" })),
&keys(&["material"]),
)
.is_none()
);
assert!(
plan(
&world,
RegisteredType::Prop,
"crate_a",
&args(json!({ "cull_distance": 40.0 })),
&keys(&["cull_distance"]),
)
.is_none()
);
}
}