Skip to content

Commit be934e4

Browse files
committed
Make noise generation resolution aware
1 parent 06a409f commit be934e4

File tree

5 files changed

+63
-80
lines changed

5 files changed

+63
-80
lines changed

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: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -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,32 @@ 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+
let image_bounds = Bbox::from_transform(DAffine2::IDENTITY).to_axis_aligned_bbox();
648+
let intersection = viewport_bounds.intersect(&image_bounds);
649+
650+
offset = (intersection.start - image_bounds.start).max(DVec2::ZERO);
651+
size = intersection.size();
652+
}
653+
654+
// If the image would not be visible, return an empty image
655+
if size.x <= 0. || size.y <= 0. {
656+
return ImageFrame::empty();
657+
}
658+
659+
let footprint_scale = footprint.scale();
660+
let width = (size.x * footprint_scale.x) as u32;
661+
let height = (size.y * footprint_scale.y) as u32;
662+
642663
// All
643-
let [width, height] = dimensions.to_array();
664+
// let [width, height] = dimensions.to_array();
644665
let mut image = Image::new(width, height, Color::from_luminance(0.5));
645666
let mut noise = fastnoise_lite::FastNoiseLite::with_seed(seed as i32);
646-
noise.set_frequency(Some(scale as f32 / 1000.));
667+
noise.set_frequency(Some(scale as f32));
647668

648669
// Domain Warp
649670
let domain_warp_type = match domain_warp_type {
@@ -677,7 +698,7 @@ fn noise_pattern(
677698

678699
return ImageFrame::<Color> {
679700
image,
680-
transform: DAffine2::from_scale(DVec2::new(width as f64, height as f64)),
701+
transform: DAffine2::from_translation(offset) * DAffine2::from_scale(size),
681702
alpha_blending: AlphaBlending::default(),
682703
};
683704
}
@@ -718,12 +739,16 @@ fn noise_pattern(
718739
noise.set_cellular_return_type(Some(cellular_return_type));
719740
noise.set_cellular_jitter(Some(cellular_jitter as f32));
720741

742+
let coordinate_offset = offset.as_vec2();
743+
let scale = size.as_vec2() / Vec2::new(width as f32, height as f32);
721744
// Calculate the noise for every pixel
722745
for y in 0..height {
723746
for x in 0..width {
724747
let pixel = image.get_pixel_mut(x, y).unwrap();
748+
let pos = Vec2::new(x as f32, y as f32);
749+
let vec = pos * scale + coordinate_offset;
725750

726-
let (mut x, mut y) = (x as f32, y as f32);
751+
let (mut x, mut y) = (vec.x, vec.y);
727752
if domain_warp_active && domain_warp_amplitude > 0. {
728753
(x, y) = noise.domain_warp_2d(x, y);
729754
}
@@ -736,7 +761,7 @@ fn noise_pattern(
736761
// Return the coherent noise image
737762
ImageFrame::<Color> {
738763
image,
739-
transform: DAffine2::from_scale(DVec2::new(width as f64, height as f64)),
764+
transform: DAffine2::from_translation(offset) * DAffine2::from_scale(size),
740765
alpha_blending: AlphaBlending::default(),
741766
}
742767
}
@@ -748,20 +773,17 @@ pub struct MandelbrotNode;
748773
fn mandelbrot_node(footprint: Footprint) -> ImageFrame<Color> {
749774
let viewport_bounds = footprint.viewport_bounds_in_local_space();
750775

751-
let width = footprint.resolution.x;
752-
let height = footprint.resolution.y;
753-
754776
let image_bounds = Bbox::from_transform(DAffine2::IDENTITY).to_axis_aligned_bbox();
755777
let intersection = viewport_bounds.intersect(&image_bounds);
756778
let size = intersection.size();
757779

780+
let offset = (intersection.start - image_bounds.start).max(DVec2::ZERO);
781+
758782
// If the image would not be visible, return an empty image
759783
if size.x <= 0. || size.y <= 0. {
760784
return ImageFrame::empty();
761785
}
762786

763-
let offset = (intersection.start - image_bounds.start).max(DVec2::ZERO);
764-
765787
let scale = footprint.scale();
766788
let width = (size.x * scale.x) as u32;
767789
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)