Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions inox2d/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ owo-colors = { version = "4.0.0", optional = true }
simple-tga-reader = "0.1.0"
thiserror = "1.0.39"
tracing = "0.1.37"
parking_lot = "0.12.5"
rayon = "1.12.0"

[dev-dependencies]
clap = { version = "4.1.8", features = ["derive"] }
Expand Down
18 changes: 12 additions & 6 deletions inox2d/src/formats/payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,8 @@ fn deserialize_simple_physics(obj: JsonObject) -> InoxParseResult<SimplePhysics>
}

fn deserialize_drawable(obj: JsonObject) -> InoxParseResult<Drawable> {
Ok(Drawable {
blending: Blending {
Ok(Drawable::new(
Blending {
mode: match obj.get_str("blend_mode")? {
"Normal" => BlendMode::Normal,
"Multiply" => BlendMode::Multiply,
Expand All @@ -192,7 +192,7 @@ fn deserialize_drawable(obj: JsonObject) -> InoxParseResult<Drawable> {
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),
Expand All @@ -209,7 +209,7 @@ fn deserialize_drawable(obj: JsonObject) -> InoxParseResult<Drawable> {
None
}
},
})
))
}

fn deserialize_mesh(obj: JsonObject) -> InoxParseResult<Mesh> {
Expand Down Expand Up @@ -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)?),
Expand All @@ -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())),
})
}
Expand Down
10 changes: 9 additions & 1 deletion inox2d/src/math/interp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,15 @@ fn interpolate_linear(t: f32, range_in: InterpRange<f32>, 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]
Expand Down
2 changes: 1 addition & 1 deletion inox2d/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pub mod drawables;

use crate::math::transform::TransformOffset;

#[derive(Clone, Copy, Hash, Eq, PartialEq)]
#[derive(Clone, Copy, Hash, Eq, PartialEq, Debug)]
#[repr(transparent)]
pub struct InoxNodeUuid(pub(crate) u32);

Expand Down
22 changes: 22 additions & 0 deletions inox2d/src/node/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Masks>,
}

impl Drawable {
pub fn new(blending: Blending, masks: Option<Masks>) -> 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,
Expand Down
4 changes: 2 additions & 2 deletions inox2d/src/node/drawables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::node::{
components::{Composite, Drawable, Mesh, TexturedMesh, TransformStore},
InoxNodeUuid,
};
use crate::puppet::World;
use crate::puppet::Query;

/// Possible component combinations of a renderable node.
///
Expand Down Expand Up @@ -38,7 +38,7 @@ impl<'comps> DrawableKind<'comps> {
/// `None` if node not renderable.
///
/// If `check`, will send a warning to `tracing` if component combination non-standard for a supposed-to-be Drawable node.
pub(crate) fn new(id: InoxNodeUuid, comps: &'comps World, check: bool) -> Option<Self> {
pub(crate) fn new(id: InoxNodeUuid, comps: &'comps impl Query, check: bool) -> Option<Self> {
let drawable = match comps.get::<Drawable>(id) {
Some(drawable) => drawable,
None => return None,
Expand Down
106 changes: 93 additions & 13 deletions inox2d/src/params.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};

use glam::{vec2, Vec2};

Expand All @@ -8,10 +8,10 @@ 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};
use crate::puppet::{InoxNodeTree, Partition, Puppet, World};

/// Parameter binding to a node. This allows to animate a node based on the value of the parameter that owns it.
pub struct Binding {
Expand All @@ -26,14 +26,20 @@ pub enum BindingValues {
ZSort(Matrix2d<f32>),
TransformTX(Matrix2d<f32>),
TransformTY(Matrix2d<f32>),
TransformTZ(Matrix2d<f32>),
TransformSX(Matrix2d<f32>),
TransformSY(Matrix2d<f32>),
TransformRX(Matrix2d<f32>),
TransformRY(Matrix2d<f32>),
TransformRZ(Matrix2d<f32>),
Deform(Matrix2d<Vec<Vec2>>),
// TODO
Opacity,
TintR(Matrix2d<f32>),
TintG(Matrix2d<f32>),
TintB(Matrix2d<f32>),
ScreenTintR(Matrix2d<f32>),
ScreenTintG(Matrix2d<f32>),
ScreenTintB(Matrix2d<f32>),
Opacity(Matrix2d<f32>),
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -70,12 +76,17 @@ pub struct Param {
}

impl Param {
/// Iterate all bindings
pub(crate) fn iter_bindings(&self) -> impl Iterator<Item = &Binding> {
self.bindings.iter()
}

/// Internal function that modifies puppet components according to one param set.
/// Must be only called ONCE per frame to ensure correct behavior.
///
/// End users may repeatedly apply a same parameter for multiple times in between frames,
/// but other facilities should be present to make sure this `apply()` is only called once per parameter.
pub(crate) fn apply(&self, val: Vec2, nodes: &InoxNodeTree, comps: &mut World) {
pub(crate) fn apply(&self, val: Vec2, nodes: &InoxNodeTree, comps: &mut Partition<'_>) {
let val = val.clamp(self.min, self.max);
let val_normed = (val - self.min) / (self.max - self.min);

Expand Down Expand Up @@ -108,6 +119,10 @@ impl Param {

// Apply offset on each binding
for binding in &self.bindings {
if !comps.contains(binding.node) {
continue;
}

let range_in = InterpRange::new(
vec2(self.axis_points.x[x_mindex], self.axis_points.y[y_mindex]),
vec2(self.axis_points.x[x_maxdex], self.axis_points.y[y_maxdex]),
Expand Down Expand Up @@ -142,6 +157,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::<TransformStore>(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);

Expand Down Expand Up @@ -228,8 +253,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::<Drawable>(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::<Drawable>(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::<Drawable>(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::<Drawable>(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::<Drawable>(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::<Drawable>(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::<Drawable>(binding.node).unwrap().blending.opacity *=
bi_interpolate_f32(val_normed, range_in, out_top, out_bottom, binding.interpolate_mode);
}
}
}
}
Expand Down Expand Up @@ -264,15 +329,30 @@ impl ParamCtx {
}
}

pub fn get(&self, param_name: &str) -> Result<Vec2, SetParamError> {
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<String, Param>, 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);
let mut bindings_bucket = HashSet::new();

for (param_name, _val) in self.values.iter() {
for node in params.get(param_name).unwrap().iter_bindings().map(|b| b.node) {
bindings_bucket.insert(node);
}
}

let accessed_nodes: Vec<_> = bindings_bucket.iter().copied().collect();
comps.with_node_partitions(accessed_nodes.as_slice(), |mut comps, _| {
for (param_name, val) in self.values.iter() {
params.get(param_name).unwrap().apply(*val, nodes, &mut comps);
}
})
}
}

Expand Down
2 changes: 1 addition & 1 deletion inox2d/src/puppet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::render::RenderCtx;
use meta::PuppetMeta;
use transforms::TransformCtx;
pub use tree::InoxNodeTree;
pub use world::World;
pub use world::{Partition, Query, World};

/// Inochi2D puppet.
pub struct Puppet {
Expand Down
Loading
Loading