reflow_components 0.2.1

Standard component catalog for Reflow — procedural, media, GPU, animation, I/O, and stream actors.
Documentation
//! Shader texture nodes — image textures and procedural textures.

use super::{cached_shader_input, update_shader_input_cache};
use crate::{Actor, ActorBehavior, Message, Port};
use anyhow::{Error, Result};
use reflow_actor::{message::EncodableValue, ActorContext};
use reflow_actor_macro::actor;
use serde_json::{json, Value};
use std::collections::HashMap;

#[actor(
    ShaderImageTextureActor,
    inports::<10>(uv),
    outports::<1>(shader),
    state(MemoryState)
)]
pub async fn shader_image_texture_actor(
    ctx: ActorContext,
) -> Result<HashMap<String, Message>, Error> {
    let payload = ctx.get_payload();
    let config = ctx.get_config_hashmap();
    let asset_id = config
        .get("assetId")
        .and_then(|v| v.as_str())
        .unwrap_or("default");
    let uv = match payload.get("uv") {
        Some(Message::Object(obj)) => {
            let v: Value = obj.as_ref().clone().into();
            v
        }
        _ => json!({"type": "texCoord"}),
    };
    let mut out = HashMap::new();
    out.insert(
        "shader".to_string(),
        Message::object(EncodableValue::from(json!({
            "type": "imageTexture",
            "assetId": asset_id,
            "uv": uv,
        }))),
    );
    Ok(out)
}

#[actor(
    ShaderNoiseTextureActor,
    inports::<10>(scale, detail, roughness),
    outports::<1>(shader),
    state(MemoryState)
)]
pub async fn shader_noise_texture_actor(
    ctx: ActorContext,
) -> Result<HashMap<String, Message>, Error> {
    let config = ctx.get_config_hashmap();
    let cache = update_shader_input_cache(&ctx, &["scale", "detail", "roughness"]);
    fn ir_or_default(
        cache: &HashMap<String, Value>,
        config: &HashMap<String, Value>,
        port: &str,
        def: f64,
    ) -> Value {
        if let Some(value) = cached_shader_input(cache, port) {
            value
        } else {
            json!({"type": "constFloat", "c": config.get(port).and_then(|v| v.as_f64()).unwrap_or(def)})
        }
    }
    let mut out = HashMap::new();
    out.insert(
        "shader".to_string(),
        Message::object(EncodableValue::from(json!({
            "type": "noiseTexture",
            "scale": ir_or_default(&cache, &config, "scale", 5.0),
            "detail": ir_or_default(&cache, &config, "detail", 2.0),
            "roughness": ir_or_default(&cache, &config, "roughness", 0.5),
        }))),
    );
    Ok(out)
}

#[actor(
    ShaderCheckerTextureActor,
    inports::<10>(scale, color1, color2),
    outports::<1>(shader),
    state(MemoryState)
)]
pub async fn shader_checker_texture_actor(
    ctx: ActorContext,
) -> Result<HashMap<String, Message>, Error> {
    let config = ctx.get_config_hashmap();
    let cache = update_shader_input_cache(&ctx, &["scale", "color1", "color2"]);
    fn ir_or_float(
        cache: &HashMap<String, Value>,
        config: &HashMap<String, Value>,
        port: &str,
        def: f64,
    ) -> Value {
        if let Some(value) = cached_shader_input(cache, port) {
            value
        } else {
            json!({"type": "constFloat", "c": config.get(port).and_then(|v| v.as_f64()).unwrap_or(def)})
        }
    }
    fn ir_or_color(
        cache: &HashMap<String, Value>,
        config: &HashMap<String, Value>,
        port: &str,
        def: [f64; 3],
    ) -> Value {
        if let Some(value) = cached_shader_input(cache, port) {
            value
        } else {
            json!({"type": "constVec3", "c": config.get(port).and_then(|v| v.as_array()).map(|a| a.iter().filter_map(|v| v.as_f64()).collect::<Vec<_>>()).unwrap_or(def.to_vec())})
        }
    }
    let mut out = HashMap::new();
    out.insert(
        "shader".to_string(),
        Message::object(EncodableValue::from(json!({
            "type": "checkerTexture",
            "scale": ir_or_float(&cache, &config, "scale", 5.0),
            "color1": ir_or_color(&cache, &config, "color1", [0.8, 0.8, 0.8]),
            "color2": ir_or_color(&cache, &config, "color2", [0.2, 0.2, 0.2]),
        }))),
    );
    Ok(out)
}

#[actor(ShaderVoronoiTextureActor, inports::<10>(scale, randomness), outports::<1>(shader), state(MemoryState))]
pub async fn shader_voronoi_texture_actor(
    ctx: ActorContext,
) -> Result<HashMap<String, Message>, Error> {
    let config = ctx.get_config_hashmap();
    let cache = update_shader_input_cache(&ctx, &["scale", "randomness"]);
    fn ir_or_f(
        cache: &HashMap<String, Value>,
        c: &HashMap<String, Value>,
        k: &str,
        d: f64,
    ) -> Value {
        if let Some(value) = cached_shader_input(cache, k) {
            value
        } else {
            json!({"type": "constFloat", "c": c.get(k).and_then(|v| v.as_f64()).unwrap_or(d)})
        }
    }
    let mut out = HashMap::new();
    out.insert(
        "shader".to_string(),
        Message::object(EncodableValue::from(json!({
            "type": "voronoiTexture",
            "scale": ir_or_f(&cache, &config, "scale", 5.0),
            "randomness": ir_or_f(&cache, &config, "randomness", 1.0),
        }))),
    );
    Ok(out)
}

#[actor(ShaderGradientTextureActor, inports::<1>(_trigger), outports::<1>(shader), state(MemoryState))]
pub async fn shader_gradient_texture_actor(
    ctx: ActorContext,
) -> Result<HashMap<String, Message>, Error> {
    let config = ctx.get_config_hashmap();
    let gradient_type = config
        .get("gradientType")
        .and_then(|v| v.as_str())
        .unwrap_or("linear");
    let mut out = HashMap::new();
    out.insert(
        "shader".to_string(),
        Message::object(EncodableValue::from(json!({
            "type": "gradientTexture", "gradientType": gradient_type,
        }))),
    );
    Ok(out)
}

#[actor(ShaderBrickTextureActor, inports::<10>(scale, mortar_size), outports::<1>(shader), state(MemoryState))]
pub async fn shader_brick_texture_actor(
    ctx: ActorContext,
) -> Result<HashMap<String, Message>, Error> {
    let config = ctx.get_config_hashmap();
    let cache = update_shader_input_cache(
        &ctx,
        &["scale", "mortar_size", "color1", "color2", "mortar_color"],
    );
    fn ir_or_f(
        cache: &HashMap<String, Value>,
        c: &HashMap<String, Value>,
        k: &str,
        d: f64,
    ) -> Value {
        if let Some(value) = cached_shader_input(cache, k) {
            value
        } else {
            json!({"type": "constFloat", "c": c.get(k).and_then(|v| v.as_f64()).unwrap_or(d)})
        }
    }
    fn ir_or_c(
        cache: &HashMap<String, Value>,
        c: &HashMap<String, Value>,
        k: &str,
        d: [f64; 3],
    ) -> Value {
        if let Some(value) = cached_shader_input(cache, k) {
            value
        } else {
            json!({"type": "constVec3", "c": d})
        }
    }
    let mut out = HashMap::new();
    out.insert(
        "shader".to_string(),
        Message::object(EncodableValue::from(json!({
            "type": "brickTexture",
            "scale": ir_or_f(&cache, &config, "scale", 5.0),
            "mortarSize": ir_or_f(&cache, &config, "mortar_size", 0.02),
            "color1": ir_or_c(&cache, &config, "color1", [0.8, 0.3, 0.2]),
            "color2": ir_or_c(&cache, &config, "color2", [0.6, 0.2, 0.15]),
            "mortarColor": ir_or_c(&cache, &config, "mortar_color", [0.9, 0.9, 0.85]),
        }))),
    );
    Ok(out)
}

#[actor(ShaderMusgraveTextureActor, inports::<10>(scale, detail, dimension), outports::<1>(shader), state(MemoryState))]
pub async fn shader_musgrave_texture_actor(
    ctx: ActorContext,
) -> Result<HashMap<String, Message>, Error> {
    let config = ctx.get_config_hashmap();
    let cache = update_shader_input_cache(&ctx, &["scale", "detail", "dimension"]);
    fn ir_or_f(
        cache: &HashMap<String, Value>,
        c: &HashMap<String, Value>,
        k: &str,
        d: f64,
    ) -> Value {
        if let Some(value) = cached_shader_input(cache, k) {
            value
        } else {
            json!({"type": "constFloat", "c": c.get(k).and_then(|v| v.as_f64()).unwrap_or(d)})
        }
    }
    let mut out = HashMap::new();
    out.insert(
        "shader".to_string(),
        Message::object(EncodableValue::from(json!({
            "type": "musgraveTexture",
            "scale": ir_or_f(&cache, &config, "scale", 5.0),
            "detail": ir_or_f(&cache, &config, "detail", 2.0),
            "dimension": ir_or_f(&cache, &config, "dimension", 2.0),
        }))),
    );
    Ok(out)
}

#[actor(ShaderWaveTextureActor, inports::<10>(scale, distortion), outports::<1>(shader), state(MemoryState))]
pub async fn shader_wave_texture_actor(
    ctx: ActorContext,
) -> Result<HashMap<String, Message>, Error> {
    let config = ctx.get_config_hashmap();
    let cache = update_shader_input_cache(&ctx, &["scale", "distortion"]);
    let wave_type = config
        .get("waveType")
        .and_then(|v| v.as_str())
        .unwrap_or("bands");
    fn ir_or_f(
        cache: &HashMap<String, Value>,
        c: &HashMap<String, Value>,
        k: &str,
        d: f64,
    ) -> Value {
        if let Some(value) = cached_shader_input(cache, k) {
            value
        } else {
            json!({"type": "constFloat", "c": c.get(k).and_then(|v| v.as_f64()).unwrap_or(d)})
        }
    }
    let mut out = HashMap::new();
    out.insert(
        "shader".to_string(),
        Message::object(EncodableValue::from(json!({
            "type": "waveTexture", "waveType": wave_type,
            "scale": ir_or_f(&cache, &config, "scale", 5.0),
            "distortion": ir_or_f(&cache, &config, "distortion", 0.0),
        }))),
    );
    Ok(out)
}