diff --git a/inox2d/src/formats/payload.rs b/inox2d/src/formats/payload.rs index cb25138a..dd0cb537 100644 --- a/inox2d/src/formats/payload.rs +++ b/inox2d/src/formats/payload.rs @@ -176,8 +176,8 @@ fn deserialize_simple_physics(obj: JsonObject) -> InoxParseResult } fn deserialize_drawable(obj: JsonObject) -> InoxParseResult { - Ok(Drawable { - blending: Blending { + Ok(Drawable::new( + Blending { mode: match obj.get_str("blend_mode")? { "Normal" => BlendMode::Normal, "Multiply" => BlendMode::Multiply, @@ -192,7 +192,7 @@ fn deserialize_drawable(obj: JsonObject) -> InoxParseResult { screen_tint: obj.get_vec3("screenTint").unwrap_or(vec3(0.0, 0.0, 0.0)), opacity: obj.get_f32("opacity").unwrap_or(1.0), }, - masks: { + { if let Ok(masks) = obj.get_list("masks") { Some(Masks { threshold: obj.get_f32("mask_threshold").unwrap_or(0.5), @@ -209,7 +209,7 @@ fn deserialize_drawable(obj: JsonObject) -> InoxParseResult { None } }, - }) + )) } fn deserialize_mesh(obj: JsonObject) -> InoxParseResult { @@ -446,6 +446,7 @@ fn deserialize_binding_values(param_name: &str, values: &[JsonValue]) -> InoxPar "zSort" => BindingValues::ZSort(deserialize_inner_binding_values(values)?), "transform.t.x" => BindingValues::TransformTX(deserialize_inner_binding_values(values)?), "transform.t.y" => BindingValues::TransformTY(deserialize_inner_binding_values(values)?), + "transform.t.z" => BindingValues::TransformTZ(deserialize_inner_binding_values(values)?), "transform.s.x" => BindingValues::TransformSX(deserialize_inner_binding_values(values)?), "transform.s.y" => BindingValues::TransformSY(deserialize_inner_binding_values(values)?), "transform.r.x" => BindingValues::TransformRX(deserialize_inner_binding_values(values)?), @@ -464,8 +465,13 @@ fn deserialize_binding_values(param_name: &str, values: &[JsonValue]) -> InoxPar BindingValues::Deform(Matrix2d::from_slice_vecs(&parsed, true)?) } - // TODO - "opacity" => BindingValues::Opacity, + "tint.r" => BindingValues::TintR(deserialize_inner_binding_values(values)?), + "tint.g" => BindingValues::TintG(deserialize_inner_binding_values(values)?), + "tint.b" => BindingValues::TintB(deserialize_inner_binding_values(values)?), + "screenTint.r" => BindingValues::ScreenTintR(deserialize_inner_binding_values(values)?), + "screenTint.g" => BindingValues::ScreenTintG(deserialize_inner_binding_values(values)?), + "screenTint.b" => BindingValues::ScreenTintB(deserialize_inner_binding_values(values)?), + "opacity" => BindingValues::Opacity(deserialize_inner_binding_values(values)?), param_name => return Err(InoxParseError::UnknownParamName(param_name.to_owned())), }) } diff --git a/inox2d/src/math/interp.rs b/inox2d/src/math/interp.rs index 85c8e8cc..76823d87 100644 --- a/inox2d/src/math/interp.rs +++ b/inox2d/src/math/interp.rs @@ -67,7 +67,15 @@ fn interpolate_linear(t: f32, range_in: InterpRange, range_out: InterpRange range_in.end, ); - (t - range_in.beg) * (range_out.end - range_out.beg) / (range_in.end - range_in.beg) + range_out.beg + let range_out_delta = range_out.end - range_out.beg; + let range_in_delta = range_in.end - range_in.beg; + if range_out_delta == 0.0 { + // Calculus teachers HATE this ONE WEIRD TRICK to getting rid of + // divide-by-zero errors in your code! + return range_out.beg; + } + + (t - range_in.beg) * range_out_delta / range_in_delta + range_out.beg } #[inline] diff --git a/inox2d/src/node/components.rs b/inox2d/src/node/components.rs index 882df767..c8750ae2 100644 --- a/inox2d/src/node/components.rs +++ b/inox2d/src/node/components.rs @@ -30,10 +30,32 @@ pub struct Composite {} /// If has this as a component, the node should render something pub struct Drawable { pub blending: Blending, + + /// The original parameters for this drawable. + /// Copied over `blending` on reset. + initial_blending: Blending, + /// If Some, the node should consider masking when rendering pub masks: Option, } +impl Drawable { + pub fn new(blending: Blending, masks: Option) -> Self { + Self { + blending, + initial_blending: blending, + masks, + } + } + + /// Reset the drawable back to the initial configuration set when `new` + /// was called. + pub fn reset(&mut self) { + self.blending = self.initial_blending; + } +} + +#[derive(Copy, Clone)] pub struct Blending { pub mode: BlendMode, pub tint: Vec3, diff --git a/inox2d/src/params.rs b/inox2d/src/params.rs index 358af1cb..8b87a23d 100644 --- a/inox2d/src/params.rs +++ b/inox2d/src/params.rs @@ -8,7 +8,7 @@ use crate::math::{ matrix::Matrix2d, }; use crate::node::{ - components::{DeformSource, DeformStack, Mesh, TransformStore, ZSort}, + components::{DeformSource, DeformStack, Drawable, Mesh, TransformStore, ZSort}, InoxNodeUuid, }; use crate::puppet::{InoxNodeTree, Puppet, World}; @@ -26,14 +26,20 @@ pub enum BindingValues { ZSort(Matrix2d), TransformTX(Matrix2d), TransformTY(Matrix2d), + TransformTZ(Matrix2d), TransformSX(Matrix2d), TransformSY(Matrix2d), TransformRX(Matrix2d), TransformRY(Matrix2d), TransformRZ(Matrix2d), Deform(Matrix2d>), - // TODO - Opacity, + TintR(Matrix2d), + TintG(Matrix2d), + TintB(Matrix2d), + ScreenTintR(Matrix2d), + ScreenTintG(Matrix2d), + ScreenTintB(Matrix2d), + Opacity(Matrix2d), } #[derive(Debug, Clone)] @@ -142,6 +148,16 @@ impl Param { .translation .y += bi_interpolate_f32(val_normed, range_in, out_top, out_bottom, binding.interpolate_mode); } + BindingValues::TransformTZ(ref matrix) => { + let (out_top, out_bottom) = ranges_out(matrix, x_mindex, x_maxdex, y_mindex, y_maxdex); + + comps + .get_mut::(binding.node) + .unwrap() + .relative + .translation + .z += bi_interpolate_f32(val_normed, range_in, out_top, out_bottom, binding.interpolate_mode); + } BindingValues::TransformSX(ref matrix) => { let (out_top, out_bottom) = ranges_out(matrix, x_mindex, x_maxdex, y_mindex, y_maxdex); @@ -228,8 +244,48 @@ impl Param { .expect("Nodes being deformed must have a DeformStack component.") .push(DeformSource::Param(self.uuid), Deform::Direct(direct_deform)); } - // TODO - BindingValues::Opacity => {} + BindingValues::TintR(ref matrix) => { + let (out_top, out_bottom) = ranges_out(matrix, x_mindex, x_maxdex, y_mindex, y_maxdex); + + comps.get_mut::(binding.node).unwrap().blending.tint.x *= + bi_interpolate_f32(val_normed, range_in, out_top, out_bottom, binding.interpolate_mode); + } + BindingValues::TintG(ref matrix) => { + let (out_top, out_bottom) = ranges_out(matrix, x_mindex, x_maxdex, y_mindex, y_maxdex); + + comps.get_mut::(binding.node).unwrap().blending.tint.y *= + bi_interpolate_f32(val_normed, range_in, out_top, out_bottom, binding.interpolate_mode); + } + BindingValues::TintB(ref matrix) => { + let (out_top, out_bottom) = ranges_out(matrix, x_mindex, x_maxdex, y_mindex, y_maxdex); + + comps.get_mut::(binding.node).unwrap().blending.tint.z *= + bi_interpolate_f32(val_normed, range_in, out_top, out_bottom, binding.interpolate_mode); + } + BindingValues::ScreenTintR(ref matrix) => { + let (out_top, out_bottom) = ranges_out(matrix, x_mindex, x_maxdex, y_mindex, y_maxdex); + + comps.get_mut::(binding.node).unwrap().blending.screen_tint.x += + bi_interpolate_f32(val_normed, range_in, out_top, out_bottom, binding.interpolate_mode); + } + BindingValues::ScreenTintG(ref matrix) => { + let (out_top, out_bottom) = ranges_out(matrix, x_mindex, x_maxdex, y_mindex, y_maxdex); + + comps.get_mut::(binding.node).unwrap().blending.screen_tint.y += + bi_interpolate_f32(val_normed, range_in, out_top, out_bottom, binding.interpolate_mode); + } + BindingValues::ScreenTintB(ref matrix) => { + let (out_top, out_bottom) = ranges_out(matrix, x_mindex, x_maxdex, y_mindex, y_maxdex); + + comps.get_mut::(binding.node).unwrap().blending.screen_tint.z += + bi_interpolate_f32(val_normed, range_in, out_top, out_bottom, binding.interpolate_mode); + } + BindingValues::Opacity(ref matrix) => { + let (out_top, out_bottom) = ranges_out(matrix, x_mindex, x_maxdex, y_mindex, y_maxdex); + + comps.get_mut::(binding.node).unwrap().blending.opacity *= + bi_interpolate_f32(val_normed, range_in, out_top, out_bottom, binding.interpolate_mode); + } } } } @@ -264,14 +320,19 @@ impl ParamCtx { } } + pub fn get(&self, param_name: &str) -> Result { + if let Some(value) = self.values.get(param_name) { + Ok(*value) + } else { + Err(SetParamError::NoParameterNamed(param_name.to_string())) + } + } + /// Modify components as specified by all params. Must be called ONCE per frame. pub(crate) fn apply(&self, params: &HashMap, nodes: &InoxNodeTree, comps: &mut World) { // a correct implementation should not care about the order of `.apply()` for (param_name, val) in self.values.iter() { - // TODO: a correct implementation should not fail on param value (0, 0) - if *val != Vec2::ZERO { - params.get(param_name).unwrap().apply(*val, nodes, comps); - } + params.get(param_name).unwrap().apply(*val, nodes, comps); } } } diff --git a/inox2d/src/render.rs b/inox2d/src/render.rs index c77fe74d..0cb1826a 100644 --- a/inox2d/src/render.rs +++ b/inox2d/src/render.rs @@ -5,7 +5,7 @@ use std::collections::HashSet; use std::mem::swap; use crate::node::{ - components::{DeformStack, Mask, Masks, ZSort}, + components::{DeformStack, Drawable, Mask, Masks, ZSort}, drawables::{CompositeComponents, DrawableKind, TexturedMeshComponents}, InoxNodeUuid, }; @@ -129,6 +129,10 @@ impl RenderCtx { if let Some(deform_stack) = comps.get_mut::(node.uuid) { deform_stack.reset(); } + + if let Some(drawable) = comps.get_mut::(node.uuid) { + drawable.reset(); + } } }