Skip to content

Commit c5dde18

Browse files
TrueDoctorKeavon
andauthored
Make noise generation resolution aware (#1909)
* Make noise generation resolution aware * Invert "Scale" so it's not "Frequency"; make "Clipping" 100x100 not 1x1 --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
1 parent 0dfddd5 commit c5dde18

5 files changed

Lines changed: 67 additions & 81 deletions

File tree

editor/src/messages/portfolio/document/node_graph/document_node_types.rs

Lines changed: 4 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -776,48 +776,10 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
776776
identifier: "Noise Pattern",
777777
node_template: NodeTemplate {
778778
document_node: DocumentNode {
779-
implementation: DocumentNodeImplementation::Network(NodeNetwork {
780-
exports: vec![NodeInput::node(NodeId(1), 0)],
781-
nodes: vec![
782-
DocumentNode {
783-
inputs: vec![
784-
NodeInput::network(concrete!(()), 0),
785-
NodeInput::network(concrete!(UVec2), 1),
786-
NodeInput::network(concrete!(u32), 2),
787-
NodeInput::network(concrete!(f64), 3),
788-
NodeInput::network(concrete!(graphene_core::raster::NoiseType), 4),
789-
NodeInput::network(concrete!(graphene_core::raster::FractalType), 5),
790-
NodeInput::network(concrete!(f64), 6),
791-
NodeInput::network(concrete!(graphene_core::raster::FractalType), 7),
792-
NodeInput::network(concrete!(u32), 8),
793-
NodeInput::network(concrete!(f64), 9),
794-
NodeInput::network(concrete!(f64), 10),
795-
NodeInput::network(concrete!(f64), 11),
796-
NodeInput::network(concrete!(f64), 12),
797-
NodeInput::network(concrete!(graphene_core::raster::CellularDistanceFunction), 13),
798-
NodeInput::network(concrete!(graphene_core::raster::CellularReturnType), 14),
799-
NodeInput::network(concrete!(f64), 15),
800-
],
801-
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_std::raster::NoisePatternNode<_, _, _, _, _, _, _, _, _, _, _, _, _, _, _>")),
802-
..Default::default()
803-
},
804-
// TODO: Make noise pattern node resolution aware and remove the cull node
805-
DocumentNode {
806-
inputs: vec![NodeInput::node(NodeId(0), 0)],
807-
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::transform::CullNode<_>")),
808-
manual_composition: Some(concrete!(Footprint)),
809-
..Default::default()
810-
},
811-
]
812-
.into_iter()
813-
.enumerate()
814-
.map(|(id, node)| (NodeId(id as u64), node))
815-
.collect(),
816-
..Default::default()
817-
}),
779+
manual_composition: Some(concrete!(Footprint)),
780+
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_std::raster::NoisePatternNode<_, _, _, _, _, _, _, _, _, _, _, _, _, _, _>")),
818781
inputs: vec![
819-
NodeInput::value(TaggedValue::None, false),
820-
NodeInput::value(TaggedValue::UVec2((512, 512).into()), false),
782+
NodeInput::value(TaggedValue::Bool(true), false),
821783
NodeInput::value(TaggedValue::U32(0), false),
822784
NodeInput::value(TaggedValue::F64(10.), false),
823785
NodeInput::value(TaggedValue::NoiseType(NoiseType::default()), false),
@@ -837,8 +799,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
837799
},
838800
persistent_node_metadata: DocumentNodePersistentMetadata {
839801
input_names: vec![
840-
"None".to_string(),
841-
"Dimensions".to_string(),
802+
"Clip".to_string(),
842803
"Seed".to_string(),
843804
"Scale".to_string(),
844805
"Noise Type".to_string(),

editor/src/messages/portfolio/document/node_graph/node_properties.rs

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -945,15 +945,15 @@ pub fn extract_channel_properties(document_node: &DocumentNode, node_id: NodeId,
945945
// As soon as there are more types of noise, this should be uncommented.
946946
pub fn noise_pattern_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
947947
// Get the current values of the inputs of interest so they can set whether certain inputs are disabled based on various conditions.
948-
let current_noise_type = match &document_node.inputs[4].as_value() {
948+
let current_noise_type = match &document_node.inputs[3].as_value() {
949949
Some(&TaggedValue::NoiseType(noise_type)) => Some(noise_type),
950950
_ => None,
951951
};
952-
let current_domain_warp_type = match &document_node.inputs[5].as_value() {
952+
let current_domain_warp_type = match &document_node.inputs[4].as_value() {
953953
Some(&TaggedValue::DomainWarpType(domain_warp_type)) => Some(domain_warp_type),
954954
_ => None,
955955
};
956-
let current_fractal_type = match &document_node.inputs[7].as_value() {
956+
let current_fractal_type = match &document_node.inputs[6].as_value() {
957957
Some(&TaggedValue::FractalType(fractal_type)) => Some(fractal_type),
958958
_ => None,
959959
};
@@ -966,28 +966,30 @@ pub fn noise_pattern_properties(document_node: &DocumentNode, node_id: NodeId, _
966966
!domain_warp_active && (current_fractal_type == Some(FractalType::DomainWarpIndependent) || current_fractal_type == Some(FractalType::DomainWarpProgressive));
967967

968968
// All
969-
let dimensions = vec2_widget(document_node, node_id, 1, "Dimensions", "W", "H", "px", Some(1.), add_blank_assist);
970-
let seed = number_widget(document_node, node_id, 2, "Seed", NumberInput::default().min(0.).is_integer(true), true);
971-
let scale = number_widget(document_node, node_id, 3, "Scale", NumberInput::default().min(0.).disabled(!coherent_noise_active), true);
972-
let noise_type_row = noise_type(document_node, node_id, 4, "Noise Type", true);
969+
let clip = LayoutGroup::Row {
970+
widgets: bool_widget(document_node, node_id, 0, "Clip", true),
971+
};
972+
let seed = number_widget(document_node, node_id, 1, "Seed", NumberInput::default().min(0.).is_integer(true), true);
973+
let scale = number_widget(document_node, node_id, 2, "Scale", NumberInput::default().min(0.).disabled(!coherent_noise_active), true);
974+
let noise_type_row = noise_type(document_node, node_id, 3, "Noise Type", true);
973975

974976
// Domain Warp
975-
let domain_warp_type_row = domain_warp_type(document_node, node_id, 5, "Domain Warp Type", true, !coherent_noise_active);
977+
let domain_warp_type_row = domain_warp_type(document_node, node_id, 4, "Domain Warp Type", true, !coherent_noise_active);
976978
let domain_warp_amplitude = number_widget(
977979
document_node,
978980
node_id,
979-
6,
981+
5,
980982
"Domain Warp Amplitude",
981983
NumberInput::default().min(0.).disabled(!coherent_noise_active || !domain_warp_active),
982984
true,
983985
);
984986

985987
// Fractal
986-
let fractal_type_row = fractal_type(document_node, node_id, 7, "Fractal Type", true, !coherent_noise_active);
988+
let fractal_type_row = fractal_type(document_node, node_id, 6, "Fractal Type", true, !coherent_noise_active);
987989
let fractal_octaves = number_widget(
988990
document_node,
989991
node_id,
990-
8,
992+
7,
991993
"Fractal Octaves",
992994
NumberInput::default()
993995
.mode_range()
@@ -1001,7 +1003,7 @@ pub fn noise_pattern_properties(document_node: &DocumentNode, node_id: NodeId, _
10011003
let fractal_lacunarity = number_widget(
10021004
document_node,
10031005
node_id,
1004-
9,
1006+
8,
10051007
"Fractal Lacunarity",
10061008
NumberInput::default()
10071009
.mode_range()
@@ -1013,7 +1015,7 @@ pub fn noise_pattern_properties(document_node: &DocumentNode, node_id: NodeId, _
10131015
let fractal_gain = number_widget(
10141016
document_node,
10151017
node_id,
1016-
10,
1018+
9,
10171019
"Fractal Gain",
10181020
NumberInput::default()
10191021
.mode_range()
@@ -1025,7 +1027,7 @@ pub fn noise_pattern_properties(document_node: &DocumentNode, node_id: NodeId, _
10251027
let fractal_weighted_strength = number_widget(
10261028
document_node,
10271029
node_id,
1028-
11,
1030+
10,
10291031
"Fractal Weighted Strength",
10301032
NumberInput::default()
10311033
.mode_range()
@@ -1037,7 +1039,7 @@ pub fn noise_pattern_properties(document_node: &DocumentNode, node_id: NodeId, _
10371039
let fractal_ping_pong_strength = number_widget(
10381040
document_node,
10391041
node_id,
1040-
12,
1042+
11,
10411043
"Fractal Ping Pong Strength",
10421044
NumberInput::default()
10431045
.mode_range()
@@ -1048,12 +1050,12 @@ pub fn noise_pattern_properties(document_node: &DocumentNode, node_id: NodeId, _
10481050
);
10491051

10501052
// Cellular
1051-
let cellular_distance_function_row = cellular_distance_function(document_node, node_id, 13, "Cellular Distance Function", true, !coherent_noise_active || !cellular_noise_active);
1052-
let cellular_return_type = cellular_return_type(document_node, node_id, 14, "Cellular Return Type", true, !coherent_noise_active || !cellular_noise_active);
1053+
let cellular_distance_function_row = cellular_distance_function(document_node, node_id, 12, "Cellular Distance Function", true, !coherent_noise_active || !cellular_noise_active);
1054+
let cellular_return_type = cellular_return_type(document_node, node_id, 13, "Cellular Return Type", true, !coherent_noise_active || !cellular_noise_active);
10531055
let cellular_jitter = number_widget(
10541056
document_node,
10551057
node_id,
1056-
15,
1058+
14,
10571059
"Cellular Jitter",
10581060
NumberInput::default()
10591061
.mode_range()
@@ -1065,7 +1067,7 @@ pub fn noise_pattern_properties(document_node: &DocumentNode, node_id: NodeId, _
10651067

10661068
vec![
10671069
// All
1068-
dimensions,
1070+
clip,
10691071
LayoutGroup::Row { widgets: seed },
10701072
LayoutGroup::Row { widgets: scale },
10711073
noise_type_row,

node-graph/gcore/src/transform.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,7 @@ impl Footprint {
168168
}
169169

170170
pub fn scale(&self) -> DVec2 {
171-
let x = self.transform.transform_vector2((1., 0.).into()).length();
172-
let y = self.transform.transform_vector2((0., 1.).into()).length();
173-
DVec2::new(x, y)
171+
self.transform.decompose_scale()
174172
}
175173

176174
pub fn offset(&self) -> DVec2 {

node-graph/gstd/src/raster.rs

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use graphene_core::value::CopiedNode;
1313
use graphene_core::{AlphaBlending, Color, Node, WasmNotSend};
1414

1515
use fastnoise_lite;
16-
use glam::{DAffine2, DVec2, UVec2, Vec2};
16+
use glam::{DAffine2, DVec2, Vec2};
1717
use rand::prelude::*;
1818
use rand_chacha::ChaCha8Rng;
1919
use std::collections::HashMap;
@@ -586,7 +586,7 @@ fn image_frame<_P: Pixel>(image: Image<_P>, transform: DAffine2) -> graphene_cor
586586

587587
#[derive(Debug, Clone, Copy)]
588588
pub struct NoisePatternNode<
589-
Dimensions,
589+
Clip,
590590
Seed,
591591
Scale,
592592
NoiseType,
@@ -602,7 +602,7 @@ pub struct NoisePatternNode<
602602
CellularReturnType,
603603
CellularJitter,
604604
> {
605-
dimensions: Dimensions,
605+
clip: Clip,
606606
seed: Seed,
607607
scale: Scale,
608608
noise_type: NoiseType,
@@ -622,8 +622,8 @@ pub struct NoisePatternNode<
622622
#[node_macro::node_fn(NoisePatternNode)]
623623
#[allow(clippy::too_many_arguments)]
624624
fn noise_pattern(
625-
_no_primary_input: (),
626-
dimensions: UVec2,
625+
footprint: Footprint,
626+
clip: bool,
627627
seed: u32,
628628
scale: f64,
629629
noise_type: NoiseType,
@@ -639,11 +639,33 @@ fn noise_pattern(
639639
cellular_return_type: CellularReturnType,
640640
cellular_jitter: f64,
641641
) -> graphene_core::raster::ImageFrame<Color> {
642+
let viewport_bounds = footprint.viewport_bounds_in_local_space();
643+
644+
let mut size = viewport_bounds.size();
645+
let mut offset = viewport_bounds.start;
646+
if clip {
647+
// TODO: Remove "clip" entirely (and its arbitrary 100x100 clipping square) once we have proper resolution-aware layer clipping
648+
const CLIPPING_SQUARE_SIZE: f64 = 100.;
649+
let image_bounds = Bbox::from_transform(DAffine2::from_scale(DVec2::splat(CLIPPING_SQUARE_SIZE))).to_axis_aligned_bbox();
650+
let intersection = viewport_bounds.intersect(&image_bounds);
651+
652+
offset = (intersection.start - image_bounds.start).max(DVec2::ZERO);
653+
size = intersection.size();
654+
}
655+
656+
// If the image would not be visible, return an empty image
657+
if size.x <= 0. || size.y <= 0. {
658+
return ImageFrame::empty();
659+
}
660+
661+
let footprint_scale = footprint.scale();
662+
let width = (size.x * footprint_scale.x) as u32;
663+
let height = (size.y * footprint_scale.y) as u32;
664+
642665
// All
643-
let [width, height] = dimensions.to_array();
644666
let mut image = Image::new(width, height, Color::from_luminance(0.5));
645667
let mut noise = fastnoise_lite::FastNoiseLite::with_seed(seed as i32);
646-
noise.set_frequency(Some(scale as f32 / 1000.));
668+
noise.set_frequency(Some(1. / (scale as f32).max(f32::EPSILON)));
647669

648670
// Domain Warp
649671
let domain_warp_type = match domain_warp_type {
@@ -665,6 +687,8 @@ fn noise_pattern(
665687
NoiseType::ValueCubic => fastnoise_lite::NoiseType::ValueCubic,
666688
NoiseType::Value => fastnoise_lite::NoiseType::Value,
667689
NoiseType::WhiteNoise => {
690+
// TODO: Generate in layer space, not viewport space
691+
668692
let mut rng = ChaCha8Rng::seed_from_u64(seed as u64);
669693

670694
for y in 0..height {
@@ -677,7 +701,7 @@ fn noise_pattern(
677701

678702
return ImageFrame::<Color> {
679703
image,
680-
transform: DAffine2::from_scale(DVec2::new(width as f64, height as f64)),
704+
transform: DAffine2::from_translation(offset) * DAffine2::from_scale(size),
681705
alpha_blending: AlphaBlending::default(),
682706
};
683707
}
@@ -718,12 +742,16 @@ fn noise_pattern(
718742
noise.set_cellular_return_type(Some(cellular_return_type));
719743
noise.set_cellular_jitter(Some(cellular_jitter as f32));
720744

745+
let coordinate_offset = offset.as_vec2();
746+
let scale = size.as_vec2() / Vec2::new(width as f32, height as f32);
721747
// Calculate the noise for every pixel
722748
for y in 0..height {
723749
for x in 0..width {
724750
let pixel = image.get_pixel_mut(x, y).unwrap();
751+
let pos = Vec2::new(x as f32, y as f32);
752+
let vec = pos * scale + coordinate_offset;
725753

726-
let (mut x, mut y) = (x as f32, y as f32);
754+
let (mut x, mut y) = (vec.x, vec.y);
727755
if domain_warp_active && domain_warp_amplitude > 0. {
728756
(x, y) = noise.domain_warp_2d(x, y);
729757
}
@@ -736,7 +764,7 @@ fn noise_pattern(
736764
// Return the coherent noise image
737765
ImageFrame::<Color> {
738766
image,
739-
transform: DAffine2::from_scale(DVec2::new(width as f64, height as f64)),
767+
transform: DAffine2::from_translation(offset) * DAffine2::from_scale(size),
740768
alpha_blending: AlphaBlending::default(),
741769
}
742770
}
@@ -748,20 +776,17 @@ pub struct MandelbrotNode;
748776
fn mandelbrot_node(footprint: Footprint) -> ImageFrame<Color> {
749777
let viewport_bounds = footprint.viewport_bounds_in_local_space();
750778

751-
let width = footprint.resolution.x;
752-
let height = footprint.resolution.y;
753-
754779
let image_bounds = Bbox::from_transform(DAffine2::IDENTITY).to_axis_aligned_bbox();
755780
let intersection = viewport_bounds.intersect(&image_bounds);
756781
let size = intersection.size();
757782

783+
let offset = (intersection.start - image_bounds.start).max(DVec2::ZERO);
784+
758785
// If the image would not be visible, return an empty image
759786
if size.x <= 0. || size.y <= 0. {
760787
return ImageFrame::empty();
761788
}
762789

763-
let offset = (intersection.start - image_bounds.start).max(DVec2::ZERO);
764-
765790
let scale = footprint.scale();
766791
let width = (size.x * scale.x) as u32;
767792
let height = (size.y * scale.y) as u32;

node-graph/interpreted-executor/src/node_registry.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -644,7 +644,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
644644
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Footprint, output: TextureFrame, fn_params: [Footprint => TextureFrame]),
645645
register_node!(graphene_core::structural::ConsNode<_, _>, input: Image<Color>, params: [&str]),
646646
register_node!(graphene_std::raster::ImageFrameNode<_, _>, input: Image<Color>, params: [DAffine2]),
647-
register_node!(graphene_std::raster::NoisePatternNode<_, _, _, _, _, _, _, _, _, _, _, _, _, _, _>, input: (), params: [UVec2, u32, f64, NoiseType, DomainWarpType, f64, FractalType, u32, f64, f64, f64, f64, CellularDistanceFunction, CellularReturnType, f64]),
647+
register_node!(graphene_std::raster::NoisePatternNode<_, _, _, _, _, _, _, _, _, _, _, _, _, _, _>, input: Footprint, params: [bool, u32, f64, NoiseType, DomainWarpType, f64, FractalType, u32, f64, f64, f64, f64, CellularDistanceFunction, CellularReturnType, f64]),
648648
#[cfg(feature = "quantization")]
649649
register_node!(graphene_std::quantization::GenerateQuantizationNode<_, _>, input: ImageFrame<Color>, params: [u32, u32]),
650650
register_node!(graphene_core::quantization::QuantizeNode<_>, input: Color, params: [QuantizationChannels]),

0 commit comments

Comments
 (0)