From d3afe8f3a5f3a02f275b754705296ede439115ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mikrut?= Date: Tue, 11 Nov 2025 14:23:13 +0100 Subject: [PATCH 1/8] Improve rust version performance --- Cargo.toml | 15 ++++++ examples/seed_2d.rs | 22 ++++++++ rust/fast.rs | 119 +++++++++++++++++++++++++++++++++++++++---- rust/smooth.rs | 120 ++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 254 insertions(+), 22 deletions(-) create mode 100644 examples/seed_2d.rs diff --git a/Cargo.toml b/Cargo.toml index d826e22..1517e84 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ name = "opensimplex2" version = "1.1.0" edition = "2021" +rust-version = "1.70.0" description = "Port of OpenSimplex2" authors = ["KdotJPG"] @@ -19,3 +20,17 @@ include = [ [lib] path = "rust/lib.rs" crate-type = ["rlib", "staticlib", "cdylib"] + +[profile.release] +opt-level = 3 +lto = "fat" +codegen-units = 1 +panic = "abort" + +[lints] +clippy.excessive_precision = "allow" +clippy.identity_op = "allow" +clippy.too_many_arguments = "allow" +clippy.approx_constant = "allow" +clippy.suboptimal_flops = "allow" + diff --git a/examples/seed_2d.rs b/examples/seed_2d.rs new file mode 100644 index 0000000..4a5fae0 --- /dev/null +++ b/examples/seed_2d.rs @@ -0,0 +1,22 @@ +use std::time::Instant; + +fn main() { + const SEED: i64 = 0; + + let _ = opensimplex2::fast::noise2(SEED, 0.0, 0.0); + + for _ in 0..10 { + let iteration_time = Instant::now(); + + for x in 0..8000 { + for y in 0..8000 { + let _ = opensimplex2::fast::noise2(SEED, x as f64, y as f64); + } + } + + println!( + "Rust Impl 2D: {} msec", + iteration_time.elapsed().as_millis() + ); + } +} diff --git a/rust/fast.rs b/rust/fast.rs index ae34ce9..670e468 100644 --- a/rust/fast.rs +++ b/rust/fast.rs @@ -2,7 +2,7 @@ K.jpg's OpenSimplex 2, faster variant */ -use std::{num::Wrapping, sync::Once}; +use std::{num::Wrapping, sync::OnceLock}; const PRIME_X: i64 = 0x5205402B9270C86F; const PRIME_Y: i64 = 0x598CD327003817B5; @@ -585,20 +585,15 @@ struct Gradients { gradients4D: Vec, } -static mut GRADIENTS: (Once, Option) = (Once::new(), None); +static GRADIENTS: OnceLock = OnceLock::new(); fn getGradients() -> &'static Gradients { - unsafe { - GRADIENTS.0.call_once(|| { - GRADIENTS.1 = Some(initGradients()); - }); - GRADIENTS.1.as_ref().unwrap() - } + GRADIENTS.get_or_init(initGradients) } fn initGradients() -> Gradients { let gradients2D: Vec<_> = GRAD2_SRC - .into_iter() + .iter() .map(|v| (v / NORMALIZER_2D) as f32) .collect::>() // cache divisions .into_iter() @@ -607,7 +602,7 @@ fn initGradients() -> Gradients { .collect(); let gradients3D: Vec<_> = GRAD3_SRC - .into_iter() + .iter() .map(|v| (v / NORMALIZER_3D) as f32) .collect::>() // cache divisions .into_iter() @@ -616,7 +611,7 @@ fn initGradients() -> Gradients { .collect(); let gradients4D: Vec<_> = GRAD4_SRC - .into_iter() + .iter() .map(|v| (v / NORMALIZER_4D) as f32) .collect::>() // cache divisions .into_iter() @@ -877,3 +872,105 @@ const GRAD4_SRC: &[f64] = &[ 0.7821684431180708, 0.4321472685365301, 0.4321472685365301, -0.12128480194602098, 0.753341017856078, 0.37968289875261624, 0.37968289875261624, 0.37968289875261624, ]; +#[cfg(test)] +mod tests { + use super::*; + + const PRECISION: f32 = 1e-6; + + #[test] + fn noise2_deterministic() { + let a = noise2(42, 1.2345, -6.789); + let b = noise2(42, 1.2345, -6.789); + assert!( + (a - -0.5491986).abs() < PRECISION, + "noise2 returned unexpected value - {a}" + ); + assert!( + (a - b).abs() < PRECISION, + "noise2 should be deterministic for same seed and inputs" + ); + } + + #[test] + fn noise2_different_seed() { + let a = noise2(42, 0.5, 0.5); + let b = noise2(43, 0.5, 0.5); + assert!( + (a - b).abs() > PRECISION, + "noise2 should return different values for different seeds - {a} vs {b}" + ); + assert!( + (a - -0.08086589).abs() < PRECISION, + "noise2 returned unexpected value - {a}" + ); + assert!( + (b - 0.6142375).abs() < PRECISION, + "noise2 returned unexpected value - {b}" + ); + } + + #[test] + fn noise3_deterministic() { + let a = noise3_ImproveXY(321, -1.0, 2.0, 3.0); + let b = noise3_ImproveXY(321, -1.0, 2.0, 3.0); + assert!( + (a - b).abs() < PRECISION, + "noise3 should be deterministic for same seed and inputs, got {a} and {b}" + ); + assert!( + (a - 0.67525643).abs() < PRECISION, + "noise3 returned unexpected value - {a}" + ); + } + + #[test] + fn noise3_different_seed() { + let a = noise3_ImproveXY(1, 0.1, 0.2, 0.3); + let b = noise3_ImproveXY(2, 0.1, 0.2, 0.3); + assert!( + (a - b).abs() > PRECISION, + "noise3 should return different values for different seeds - {a} vs {b}" + ); + assert!( + (a - 0.06824334).abs() < PRECISION, + "noise3 returned unexpected value - {a}" + ); + assert!( + (b - 0.22377315).abs() < PRECISION, + "noise3 returned unexpected value - {b}" + ); + } + + #[test] + fn noise4_deterministic() { + let a = noise4_ImproveXYZ(9, 2.0, 3.0, 5.0, 6.0); + let b = noise4_ImproveXYZ(9, 2.0, 3.0, 5.0, 6.0); + assert!( + (a - b).abs() < PRECISION, + "noise4 should be deterministic for same seed and inputs, got {a} and {b}" + ); + assert!( + (a - -0.1311275).abs() < PRECISION, + "noise4 returned unexpected value - {a}" + ); + } + + #[test] + fn noise4_different_seed() { + let a = noise4_ImproveXYZ(1, 0.1, 0.2, 0.3, 0.4); + let b = noise4_ImproveXYZ(2, 0.1, 0.2, 0.3, 0.4); + assert!( + (a - b).abs() > PRECISION, + "noise4 should return different values for different seeds - {a} vs {b}" + ); + assert!( + (a - -0.07606829).abs() < PRECISION, + "noise4 returned unexpected value - {a}" + ); + assert!( + (b - 0.21148828).abs() < PRECISION, + "noise4 returned unexpected value - {b}" + ); + } +} diff --git a/rust/smooth.rs b/rust/smooth.rs index 79ca1f9..fb942d2 100644 --- a/rust/smooth.rs +++ b/rust/smooth.rs @@ -2,7 +2,7 @@ K.jpg's OpenSimplex 2, smooth variant ("SuperSimplex") */ -use std::{num::Wrapping, sync::Once}; +use std::{num::Wrapping, sync::OnceLock}; const PRIME_X: i64 = 0x5205402B9270C86F; const PRIME_Y: i64 = 0x598CD327003817B5; @@ -824,20 +824,15 @@ struct StaticData { lookup4DB: Vec, } -static mut STATIC_DATA: (Once, Option) = (Once::new(), None); +static STATIC_DATA: OnceLock = OnceLock::new(); fn getStaticData() -> &'static StaticData { - unsafe { - STATIC_DATA.0.call_once(|| { - STATIC_DATA.1 = Some(initStaticData()); - }); - STATIC_DATA.1.as_ref().unwrap() - } + STATIC_DATA.get_or_init(initStaticData) } fn initStaticData() -> StaticData { let gradients2D: Vec<_> = GRAD2_SRC - .into_iter() + .iter() .map(|v| (v / NORMALIZER_2D) as f32) .collect::>() // cache divisions .into_iter() @@ -846,7 +841,7 @@ fn initStaticData() -> StaticData { .collect(); let gradients3D: Vec<_> = GRAD3_SRC - .into_iter() + .iter() .map(|v| (v / NORMALIZER_3D) as f32) .collect::>() // cache divisions .into_iter() @@ -855,7 +850,7 @@ fn initStaticData() -> StaticData { .collect(); let gradients4D: Vec<_> = GRAD4_SRC - .into_iter() + .iter() .map(|v| (v / NORMALIZER_4D) as f32) .collect::>() // cache divisions .into_iter() @@ -1399,3 +1394,106 @@ const LOOKUP_4D_VERTEX_CODES: &[&[u8]] = &[ &[0x55, 0x59, 0x65, 0x69, 0x6A, 0x95, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xAE, 0xBA, 0xEA], &[0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xAB, 0xAE, 0xBA, 0xEA], ]; + +#[cfg(test)] +mod tests { + use super::*; + + const PRECISION: f32 = 1e-6; + + #[test] + fn noise2_deterministic() { + let a = noise2(42, 1.2345, -6.789); + let b = noise2(42, 1.2345, -6.789); + assert!( + (a - -0.32238963).abs() < PRECISION, + "noise2 returned unexpected value - {a}" + ); + assert!( + (a - b).abs() < PRECISION, + "noise2 should be deterministic for same seed and inputs" + ); + } + + #[test] + fn noise2_different_seed() { + let a = noise2(42, 0.5, 0.5); + let b = noise2(43, 0.5, 0.5); + assert!( + (a - b).abs() > PRECISION, + "noise2 should return different values for different seeds - {a} vs {b}" + ); + assert!( + (a - -0.04904862).abs() < PRECISION, + "noise2 returned unexpected value - {a}" + ); + assert!( + (b - 0.3536184).abs() < PRECISION, + "noise2 returned unexpected value - {b}" + ); + } + + #[test] + fn noise3_deterministic() { + let a = noise3_ImproveXY(321, -1.0, 2.0, 3.0); + let b = noise3_ImproveXY(321, -1.0, 2.0, 3.0); + assert!( + (a - b).abs() < PRECISION, + "noise3 should be deterministic for same seed and inputs, got {a} and {b}" + ); + assert!( + (a - 0.53415114).abs() < PRECISION, + "noise3 returned unexpected value - {a}" + ); + } + + #[test] + fn noise3_different_seed() { + let a = noise3_ImproveXY(1, 0.1, 0.2, 0.3); + let b = noise3_ImproveXY(2, 0.1, 0.2, 0.3); + assert!( + (a - b).abs() > PRECISION, + "noise3 should return different values for different seeds - {a} vs {b}" + ); + assert!( + (a - 0.045763396).abs() < PRECISION, + "noise3 returned unexpected value - {a}" + ); + assert!( + (b - 0.13154802).abs() < PRECISION, + "noise3 returned unexpected value - {b}" + ); + } + + #[test] + fn noise4_deterministic() { + let a = noise4_ImproveXYZ(9, 2.0, 3.0, 5.0, 6.0); + let b = noise4_ImproveXYZ(9, 2.0, 3.0, 5.0, 6.0); + assert!( + (a - b).abs() < PRECISION, + "noise4 should be deterministic for same seed and inputs, got {a} and {b}" + ); + assert!( + (a - -0.14588071).abs() < PRECISION, + "noise4 returned unexpected value - {a}" + ); + } + + #[test] + fn noise4_different_seed() { + let a = noise4_ImproveXYZ(1, 0.1, 0.2, 0.3, 0.4); + let b = noise4_ImproveXYZ(2, 0.1, 0.2, 0.3, 0.4); + assert!( + (a - b).abs() > PRECISION, + "noise4 should return different values for different seeds - {a} vs {b}" + ); + assert!( + (a - 0.06384007).abs() < PRECISION, + "noise4 returned unexpected value - {a}" + ); + assert!( + (b - 0.29178715).abs() < PRECISION, + "noise4 returned unexpected value - {b}" + ); + } +} From 56c9b2b0a0398b8b1f4fc38e091908e62e416460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mikrut?= Date: Tue, 11 Nov 2025 15:02:43 +0100 Subject: [PATCH 2/8] Remove unnecessary collect --- rust/fast.rs | 6 ------ rust/smooth.rs | 6 ------ 2 files changed, 12 deletions(-) diff --git a/rust/fast.rs b/rust/fast.rs index 670e468..f108bc5 100644 --- a/rust/fast.rs +++ b/rust/fast.rs @@ -595,8 +595,6 @@ fn initGradients() -> Gradients { let gradients2D: Vec<_> = GRAD2_SRC .iter() .map(|v| (v / NORMALIZER_2D) as f32) - .collect::>() // cache divisions - .into_iter() .cycle() .take((N_GRADS_2D * 2) as usize) .collect(); @@ -604,8 +602,6 @@ fn initGradients() -> Gradients { let gradients3D: Vec<_> = GRAD3_SRC .iter() .map(|v| (v / NORMALIZER_3D) as f32) - .collect::>() // cache divisions - .into_iter() .cycle() .take((N_GRADS_3D * 4) as usize) .collect(); @@ -613,8 +609,6 @@ fn initGradients() -> Gradients { let gradients4D: Vec<_> = GRAD4_SRC .iter() .map(|v| (v / NORMALIZER_4D) as f32) - .collect::>() // cache divisions - .into_iter() .cycle() .take((N_GRADS_4D * 4) as usize) .collect(); diff --git a/rust/smooth.rs b/rust/smooth.rs index fb942d2..c235b00 100644 --- a/rust/smooth.rs +++ b/rust/smooth.rs @@ -834,8 +834,6 @@ fn initStaticData() -> StaticData { let gradients2D: Vec<_> = GRAD2_SRC .iter() .map(|v| (v / NORMALIZER_2D) as f32) - .collect::>() // cache divisions - .into_iter() .cycle() .take((N_GRADS_2D * 2) as usize) .collect(); @@ -843,8 +841,6 @@ fn initStaticData() -> StaticData { let gradients3D: Vec<_> = GRAD3_SRC .iter() .map(|v| (v / NORMALIZER_3D) as f32) - .collect::>() // cache divisions - .into_iter() .cycle() .take((N_GRADS_3D * 4) as usize) .collect(); @@ -852,8 +848,6 @@ fn initStaticData() -> StaticData { let gradients4D: Vec<_> = GRAD4_SRC .iter() .map(|v| (v / NORMALIZER_4D) as f32) - .collect::>() // cache divisions - .into_iter() .cycle() .take((N_GRADS_4D * 4) as usize) .collect(); From 2fc0f5510a34e6cf5bf56d2a6efaa16f5051aacc Mon Sep 17 00:00:00 2001 From: Christopher Serr Date: Tue, 11 Nov 2025 16:27:51 +0100 Subject: [PATCH 3/8] Precompute all Lookup Tables at Compile Time This precomputes all lookup tables at compile time, so there's no runtime performance hit anymore at all. As a side effect, this also means the library is now entirely `no_std` compatible. --- rust/fast.rs | 84 ++++++++++-------------- rust/ffi.rs | 2 +- rust/lib.rs | 1 + rust/smooth.rs | 172 ++++++++++++++++++++++++++----------------------- 4 files changed, 128 insertions(+), 131 deletions(-) diff --git a/rust/fast.rs b/rust/fast.rs index f108bc5..65889c1 100644 --- a/rust/fast.rs +++ b/rust/fast.rs @@ -2,7 +2,7 @@ K.jpg's OpenSimplex 2, faster variant */ -use std::{num::Wrapping, sync::OnceLock}; +use core::num::Wrapping; const PRIME_X: i64 = 0x5205402B9270C86F; const PRIME_Y: i64 = 0x598CD327003817B5; @@ -518,8 +518,7 @@ fn grad2(seed: Wrapping, xsvp: Wrapping, ysvp: Wrapping, dx: f32, hash *= HASH_MULTIPLIER; hash ^= hash.0 >> (64 - N_GRADS_2D_EXPONENT + 1); let gi = (hash.0 as i32 & ((N_GRADS_2D - 1) << 1)) as usize; - let grads = &getGradients().gradients2D; - grads[gi | 0] * dx + grads[gi | 1] * dy + GRAD2[gi | 0] * dx + GRAD2[gi | 1] * dy } fn grad3( @@ -535,8 +534,7 @@ fn grad3( hash *= HASH_MULTIPLIER; hash ^= hash.0 >> (64 - N_GRADS_3D_EXPONENT + 2); let gi = (hash.0 as i32 & ((N_GRADS_3D - 1) << 2)) as usize; - let grads = &getGradients().gradients3D; - grads[gi | 0] * dx + grads[gi | 1] * dy + grads[gi | 2] * dz + GRAD3[gi | 0] * dx + GRAD3[gi | 1] * dy + GRAD3[gi | 2] * dz } fn grad4( @@ -554,8 +552,7 @@ fn grad4( hash *= HASH_MULTIPLIER; hash ^= hash.0 >> (64 - N_GRADS_4D_EXPONENT + 2); let gi = (hash.0 as i32 & ((N_GRADS_4D - 1) << 2)) as usize; - let grads = &getGradients().gradients4D; - (grads[gi | 0] * dx + grads[gi | 1] * dy) + (grads[gi | 2] * dz + grads[gi | 3] * dw) + (GRAD4[gi | 0] * dx + GRAD4[gi | 1] * dy) + (GRAD4[gi | 2] * dz + GRAD4[gi | 3] * dw) } fn fastFloor(x: f64) -> i32 { @@ -579,49 +576,38 @@ fn fastRound(x: f64) -> i32 { gradients */ -struct Gradients { - gradients2D: Vec, - gradients3D: Vec, - gradients4D: Vec, -} - -static GRADIENTS: OnceLock = OnceLock::new(); - -fn getGradients() -> &'static Gradients { - GRADIENTS.get_or_init(initGradients) -} - -fn initGradients() -> Gradients { - let gradients2D: Vec<_> = GRAD2_SRC - .iter() - .map(|v| (v / NORMALIZER_2D) as f32) - .cycle() - .take((N_GRADS_2D * 2) as usize) - .collect(); - - let gradients3D: Vec<_> = GRAD3_SRC - .iter() - .map(|v| (v / NORMALIZER_3D) as f32) - .cycle() - .take((N_GRADS_3D * 4) as usize) - .collect(); - - let gradients4D: Vec<_> = GRAD4_SRC - .iter() - .map(|v| (v / NORMALIZER_4D) as f32) - .cycle() - .take((N_GRADS_4D * 4) as usize) - .collect(); - - Gradients { - gradients2D, - gradients3D, - gradients4D, +static GRAD2: [f32; N_GRADS_2D as usize * 2] = { + let mut data = [0.0; N_GRADS_2D as usize * 2]; + let mut i = 0; + while i < data.len() { + data[i] = (GRAD2_SRC[i % GRAD2_SRC.len()] / NORMALIZER_2D) as f32; + i += 1; } -} + data +}; + +static GRAD3: [f32; N_GRADS_3D as usize * 4] = { + let mut data = [0.0; N_GRADS_3D as usize * 4]; + let mut i = 0; + while i < data.len() { + data[i] = (GRAD3_SRC[i % GRAD3_SRC.len()] / NORMALIZER_3D) as f32; + i += 1; + } + data +}; + +static GRAD4: [f32; N_GRADS_4D as usize * 4] = { + let mut data = [0.0; N_GRADS_4D as usize * 4]; + let mut i = 0; + while i < data.len() { + data[i] = (GRAD4_SRC[i % GRAD4_SRC.len()] / NORMALIZER_4D) as f32; + i += 1; + } + data +}; #[rustfmt::skip] -const GRAD2_SRC: &[f64] = &[ +static GRAD2_SRC: [f64; 48] = [ 0.38268343236509, 0.923879532511287, 0.923879532511287, 0.38268343236509, 0.923879532511287, -0.38268343236509, @@ -650,7 +636,7 @@ const GRAD2_SRC: &[f64] = &[ ]; #[rustfmt::skip] -const GRAD3_SRC: &[f64] = &[ +static GRAD3_SRC: [f64; 192] = [ 2.22474487139, 2.22474487139, -1.0, 0.0, 2.22474487139, 2.22474487139, 1.0, 0.0, 3.0862664687972017, 1.1721513422464978, 0.0, 0.0, @@ -703,7 +689,7 @@ const GRAD3_SRC: &[f64] = &[ ]; #[rustfmt::skip] -const GRAD4_SRC: &[f64] = &[ +static GRAD4_SRC: [f64; 640] = [ -0.6740059517812944, -0.3239847771997537, -0.3239847771997537, 0.5794684678643381, -0.7504883828755602, -0.4004672082940195, 0.15296486218853164, 0.5029860367700724, -0.7504883828755602, 0.15296486218853164, -0.4004672082940195, 0.5029860367700724, diff --git a/rust/ffi.rs b/rust/ffi.rs index abc0ab4..630a998 100644 --- a/rust/ffi.rs +++ b/rust/ffi.rs @@ -1,4 +1,4 @@ -use std::ffi::{c_double, c_float, c_longlong}; +use core::ffi::{c_double, c_float, c_longlong}; use crate::{fast, smooth}; diff --git a/rust/lib.rs b/rust/lib.rs index d627507..8f3f660 100644 --- a/rust/lib.rs +++ b/rust/lib.rs @@ -1,3 +1,4 @@ +#![no_std] #![allow(non_snake_case)] pub mod fast; diff --git a/rust/smooth.rs b/rust/smooth.rs index c235b00..25e66d9 100644 --- a/rust/smooth.rs +++ b/rust/smooth.rs @@ -2,7 +2,7 @@ K.jpg's OpenSimplex 2, smooth variant ("SuperSimplex") */ -use std::{num::Wrapping, sync::OnceLock}; +use core::num::Wrapping; const PRIME_X: i64 = 0x5205402B9270C86F; const PRIME_Y: i64 = 0x598CD327003817B5; @@ -692,13 +692,11 @@ fn noise4_UnskewedBase(seed: i64, xs: f64, ys: f64, zs: f64, ws: f64) -> f32 { | ((fastFloor(ws * 4.0) & 3) << 6); // Point contributions - let staticData = getStaticData(); let mut value = 0.0; - let secondaryIndexStartAndStop = staticData.lookup4DA[index as usize]; + let secondaryIndexStartAndStop = LOOKUP4DA[index as usize]; let secondaryIndexStart = secondaryIndexStartAndStop & 0xFFFF; let secondaryIndexStop = secondaryIndexStartAndStop >> 16; - for i in secondaryIndexStart..secondaryIndexStop { - let c = &staticData.lookup4DB[i]; + for c in &LOOKUP4DB[secondaryIndexStart..secondaryIndexStop] { let dx = xi + c.dx; let dy = yi + c.dy; let dz = zi + c.dz; @@ -734,8 +732,7 @@ fn grad2(seed: Wrapping, xsvp: Wrapping, ysvp: Wrapping, dx: f32, hash *= HASH_MULTIPLIER; hash ^= hash.0 >> (64 - N_GRADS_2D_EXPONENT + 1); let gi = (hash.0 as i32 & ((N_GRADS_2D - 1) << 1)) as usize; - let grads = &getStaticData().gradients2D; - grads[gi | 0] * dx + grads[gi | 1] * dy + GRAD2[gi | 0] * dx + GRAD2[gi | 1] * dy } fn grad3( @@ -751,8 +748,7 @@ fn grad3( hash *= HASH_MULTIPLIER; hash ^= hash.0 >> (64 - N_GRADS_3D_EXPONENT + 2); let gi = (hash.0 as i32 & ((N_GRADS_3D - 1) << 2)) as usize; - let grads = &getStaticData().gradients3D; - grads[gi | 0] * dx + grads[gi | 1] * dy + grads[gi | 2] * dz + GRAD3[gi | 0] * dx + GRAD3[gi | 1] * dy + GRAD3[gi | 2] * dz } fn grad4( @@ -770,8 +766,7 @@ fn grad4( hash *= HASH_MULTIPLIER; hash ^= hash.0 >> (64 - N_GRADS_4D_EXPONENT + 2); let gi = (hash.0 as i32 & ((N_GRADS_4D - 1) << 2)) as usize; - let grads = &getStaticData().gradients4D; - (grads[gi | 0] * dx + grads[gi | 1] * dy) + (grads[gi | 2] * dz + grads[gi | 3] * dw) + (GRAD4[gi | 0] * dx + GRAD4[gi | 1] * dy) + (GRAD4[gi | 2] * dz + GRAD4[gi | 3] * dw) } fn fastFloor(x: f64) -> i32 { @@ -787,7 +782,7 @@ fn fastFloor(x: f64) -> i32 { Lookup Tables & Gradients */ -#[derive(Clone, Default)] +#[derive(Copy, Clone, Default)] struct LatticeVertex4D { pub dx: f32, pub dy: f32, @@ -800,13 +795,13 @@ struct LatticeVertex4D { } impl LatticeVertex4D { - pub fn new(xsv: i32, ysv: i32, zsv: i32, wsv: i32) -> Self { + pub const fn new(xsv: i32, ysv: i32, zsv: i32, wsv: i32) -> Self { let ssv = (xsv + ysv + zsv + wsv) as f32 * UNSKEW_4D; Self { - xsvp: (Wrapping(xsv as i64) * Wrapping(PRIME_X)).0, - ysvp: (Wrapping(ysv as i64) * Wrapping(PRIME_Y)).0, - zsvp: (Wrapping(zsv as i64) * Wrapping(PRIME_Z)).0, - wsvp: (Wrapping(wsv as i64) * Wrapping(PRIME_W)).0, + xsvp: (xsv as i64).wrapping_mul(PRIME_X), + ysvp: (ysv as i64).wrapping_mul(PRIME_Y), + zsvp: (zsv as i64).wrapping_mul(PRIME_Z), + wsvp: (wsv as i64).wrapping_mul(PRIME_W), dx: -xsv as f32 - ssv, dy: -ysv as f32 - ssv, dz: -zsv as f32 - ssv, @@ -815,75 +810,90 @@ impl LatticeVertex4D { } } -struct StaticData { - gradients2D: Vec, - gradients3D: Vec, - gradients4D: Vec, - - lookup4DA: Vec, - lookup4DB: Vec, -} - -static STATIC_DATA: OnceLock = OnceLock::new(); +static GRAD2: [f32; N_GRADS_2D as usize * 2] = { + let mut data = [0.0; N_GRADS_2D as usize * 2]; + let mut i = 0; + while i < data.len() { + data[i] = (GRAD2_SRC[i % GRAD2_SRC.len()] / NORMALIZER_2D) as f32; + i += 1; + } + data +}; + +static GRAD3: [f32; N_GRADS_3D as usize * 4] = { + let mut data = [0.0; N_GRADS_3D as usize * 4]; + let mut i = 0; + while i < data.len() { + data[i] = (GRAD3_SRC[i % GRAD3_SRC.len()] / NORMALIZER_3D) as f32; + i += 1; + } + data +}; + +static GRAD4: [f32; N_GRADS_4D as usize * 4] = { + let mut data = [0.0; N_GRADS_4D as usize * 4]; + let mut i = 0; + while i < data.len() { + data[i] = (GRAD4_SRC[i % GRAD4_SRC.len()] / NORMALIZER_4D) as f32; + i += 1; + } + data +}; + +const N_LATTICE_VERTICES_TOTAL: usize = { + let mut i = 0; + let mut value = 0; + while i < LOOKUP_4D_VERTEX_CODES.len() { + value += LOOKUP_4D_VERTEX_CODES[i].len(); + i += 1; + } + value +}; + +static LATTICE_VERTICES_BY_CODE: [LatticeVertex4D; 256] = { + let mut data = [LatticeVertex4D::new(0, 0, 0, 0); 256]; + let mut i = 0; + while i < data.len() { + let cx = ((i as i32 >> 0) & 3) - 1; + let cy = ((i as i32 >> 2) & 3) - 1; + let cz = ((i as i32 >> 4) & 3) - 1; + let cw = ((i as i32 >> 6) & 3) - 1; + data[i] = LatticeVertex4D::new(cx, cy, cz, cw); + i += 1; + } + data +}; -fn getStaticData() -> &'static StaticData { - STATIC_DATA.get_or_init(initStaticData) -} +static LOOKUP4DA: [usize; 256] = { + let mut data = [0; 256]; + let mut i = 0; + let mut j = 0; + while i < 256 { + data[i] = j | ((j + LOOKUP_4D_VERTEX_CODES[i].len()) << 16); + j += LOOKUP_4D_VERTEX_CODES[i].len(); + i += 1; + } + data +}; -fn initStaticData() -> StaticData { - let gradients2D: Vec<_> = GRAD2_SRC - .iter() - .map(|v| (v / NORMALIZER_2D) as f32) - .cycle() - .take((N_GRADS_2D * 2) as usize) - .collect(); - - let gradients3D: Vec<_> = GRAD3_SRC - .iter() - .map(|v| (v / NORMALIZER_3D) as f32) - .cycle() - .take((N_GRADS_3D * 4) as usize) - .collect(); - - let gradients4D: Vec<_> = GRAD4_SRC - .iter() - .map(|v| (v / NORMALIZER_4D) as f32) - .cycle() - .take((N_GRADS_4D * 4) as usize) - .collect(); - - let nLatticeVerticesTotal = LOOKUP_4D_VERTEX_CODES.iter().map(|v| v.len()).sum(); - let latticeVerticesByCode: Vec<_> = (0..256) - .map(|i| { - let cx = ((i >> 0) & 3) - 1; - let cy = ((i >> 2) & 3) - 1; - let cz = ((i >> 4) & 3) - 1; - let cw = ((i >> 6) & 3) - 1; - LatticeVertex4D::new(cx, cy, cz, cw) - }) - .collect(); - let mut lookup4DA = vec![0; 256]; - let mut lookup4DB = vec![Default::default(); nLatticeVerticesTotal]; +static LOOKUP4DB: [LatticeVertex4D; N_LATTICE_VERTICES_TOTAL] = { + let mut data = [LatticeVertex4D::new(0, 0, 0, 0); N_LATTICE_VERTICES_TOTAL]; + let mut i = 0; let mut j = 0; - for i in 0..256 { - lookup4DA[i] = j | ((j + LOOKUP_4D_VERTEX_CODES[i].len()) << 16); - for k in 0..LOOKUP_4D_VERTEX_CODES[i].len() { - lookup4DB[j] = latticeVerticesByCode[LOOKUP_4D_VERTEX_CODES[i][k] as usize].clone(); + while i < 256 { + let mut k = 0; + while k < LOOKUP_4D_VERTEX_CODES[i].len() { + data[j] = LATTICE_VERTICES_BY_CODE[LOOKUP_4D_VERTEX_CODES[i][k] as usize]; j += 1; + k += 1; } + i += 1; } - - StaticData { - gradients2D, - gradients3D, - gradients4D, - lookup4DA, - lookup4DB, - } -} + data +}; #[rustfmt::skip] -const GRAD2_SRC: &[f64] = &[ +static GRAD2_SRC: [f64; 48] = [ 0.38268343236509, 0.923879532511287, 0.923879532511287, 0.38268343236509, 0.923879532511287, -0.38268343236509, @@ -912,7 +922,7 @@ const GRAD2_SRC: &[f64] = &[ ]; #[rustfmt::skip] -const GRAD3_SRC: &[f64] = &[ +static GRAD3_SRC: [f64; 192] = [ 2.22474487139, 2.22474487139, -1.0, 0.0, 2.22474487139, 2.22474487139, 1.0, 0.0, 3.0862664687972017, 1.1721513422464978, 0.0, 0.0, @@ -965,7 +975,7 @@ const GRAD3_SRC: &[f64] = &[ ]; #[rustfmt::skip] -const GRAD4_SRC: &[f64] = &[ +static GRAD4_SRC: [f64; 640] = [ -0.6740059517812944, -0.3239847771997537, -0.3239847771997537, 0.5794684678643381, -0.7504883828755602, -0.4004672082940195, 0.15296486218853164, 0.5029860367700724, -0.7504883828755602, 0.15296486218853164, -0.4004672082940195, 0.5029860367700724, @@ -1130,7 +1140,7 @@ const GRAD4_SRC: &[f64] = &[ ]; #[rustfmt::skip] -const LOOKUP_4D_VERTEX_CODES: &[&[u8]] = &[ +static LOOKUP_4D_VERTEX_CODES: [&[u8]; 256] = [ &[0x15, 0x45, 0x51, 0x54, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA], &[0x15, 0x45, 0x51, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x6A, 0x95, 0x96, 0x9A, 0xA6, 0xAA], &[0x01, 0x05, 0x11, 0x15, 0x41, 0x45, 0x51, 0x55, 0x56, 0x5A, 0x66, 0x6A, 0x96, 0x9A, 0xA6, 0xAA], From 89929a51ef42a3b1f420adb7ec5363b58f2ad579 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mikrut?= Date: Tue, 11 Nov 2025 18:57:36 +0100 Subject: [PATCH 4/8] Make std optional, but disabled by default --- Cargo.toml | 4 ++++ examples/seed_2d.rs | 5 +++-- rust/lib.rs | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1517e84..5ad8b25 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,10 @@ lto = "fat" codegen-units = 1 panic = "abort" +[features] +default = [] +std = [] + [lints] clippy.excessive_precision = "allow" clippy.identity_op = "allow" diff --git a/examples/seed_2d.rs b/examples/seed_2d.rs index 4a5fae0..afa4c85 100644 --- a/examples/seed_2d.rs +++ b/examples/seed_2d.rs @@ -7,15 +7,16 @@ fn main() { for _ in 0..10 { let iteration_time = Instant::now(); + let mut res = 0.0; // To not optimize away the loop for x in 0..8000 { for y in 0..8000 { - let _ = opensimplex2::fast::noise2(SEED, x as f64, y as f64); + res += opensimplex2::fast::noise2(SEED, x as f64, y as f64); } } println!( - "Rust Impl 2D: {} msec", + "Rust Impl 2D: {} msec (Res: {res})", iteration_time.elapsed().as_millis() ); } diff --git a/rust/lib.rs b/rust/lib.rs index 8f3f660..710911e 100644 --- a/rust/lib.rs +++ b/rust/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(feature = "std"), no_std)] #![allow(non_snake_case)] pub mod fast; From 47dd58b89604b566fee697a9ac7f4c299639b932 Mon Sep 17 00:00:00 2001 From: EndrII Date: Wed, 12 Nov 2025 00:46:16 +0100 Subject: [PATCH 5/8] added cmake support --- CMakeLists.txt | 109 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..a9434f9 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,109 @@ +cmake_minimum_required(VERSION 3.19) + +set(CURRENT_PROJECT "OpenSimplex2") + +option(OPENSIMPLEX_OLD "Enable build old C implementation instead Rust" OFF) + +if (OPENSIMPLEX_OLD) + + set(SOURCE_CPP + "${CMAKE_CURRENT_SOURCE_DIR}/_old/c/OpenSimplex2F.c" + "${CMAKE_CURRENT_SOURCE_DIR}/_old/c/OpenSimplex2F.h" + ) + + add_library(${CURRENT_PROJECT} ${SOURCE_CPP}) + target_include_directories(${CURRENT_PROJECT} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/_old/c/") + target_compile_options(${CURRENT_PROJECT} PRIVATE -Wall -Wextra --pedantic) + +else() + include(ExternalProject) + + ExternalProject_Add( + ${CURRENT_PROJECT}Rust + DOWNLOAD_COMMAND "" + CONFIGURE_COMMAND "" + BUILD_COMMAND cargo build COMMAND cargo build --release --features std --target-dir "${CMAKE_CURRENT_BINARY_DIR}" + BINARY_DIR "${CMAKE_CURRENT_SOURCE_DIR}" + INSTALL_COMMAND "" + LOG_BUILD ON) + + + # ADD_CUSTOM_TARGET( + # ${CURRENT_PROJECT}Rust + # COMMAND cargo build --release --features std --target-dir "${CMAKE_CURRENT_BINARY_DIR}" + # COMMENT "cargo build --release --features std --target-dir ${CMAKE_CURRENT_BINARY_DIR}" + # WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + # ) + + if(WIN32) + if (BUILD_SHARED_LIBS) + add_library( ${CURRENT_PROJECT} SHARED IMPORTED ) + + # You can define two import-locations: one for debug and one for release. + set_target_properties( ${CURRENT_PROJECT} + PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/release/lib${CURRENT_PROJECT}.dll + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}" + IMPORTED_IMPLIB ${CMAKE_CURRENT_BINARY_DIR}/release/lib${CURRENT_PROJECT}.lib + ) + else() + add_library( ${CURRENT_PROJECT} STATIC IMPORTED ) + + # You can define two import-locations: one for debug and one for release. + set_target_properties( ${CURRENT_PROJECT} + PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/release/libopensimplex.lib + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}" + + ) + endif() + + elseif(APPLE) + + if (BUILD_SHARED_LIBS) + add_library( ${CURRENT_PROJECT} SHARED IMPORTED ) + + # You can define two import-locations: one for debug and one for release. + set_target_properties( ${CURRENT_PROJECT} + PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/release/libopensimplex.dylib + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}" + + ) + else() + add_library( ${CURRENT_PROJECT} STATIC IMPORTED ) + + # You can define two import-locations: one for debug and one for release. + set_target_properties( ${CURRENT_PROJECT} + PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/release/libopensimplex.lib + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}" + + ) + endif() + + elseif(UNIX) + if (BUILD_SHARED_LIBS) + add_library( ${CURRENT_PROJECT} SHARED IMPORTED ) + + # You can define two import-locations: one for debug and one for release. + set_target_properties( ${CURRENT_PROJECT} + PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/release/libopensimplex.so + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}" + IMPORTED_SONAME "lib${CURRENT_PROJECT}.so" + + ) + else() + add_library( ${CURRENT_PROJECT} STATIC IMPORTED ) + + # You can define two import-locations: one for debug and one for release. + set_target_properties( ${CURRENT_PROJECT} + PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/release/libopensimplex.a + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}" + IMPORTED_SONAME "lib${CURRENT_PROJECT}.a" + + ) + endif() + + endif() + + add_dependencies(${CURRENT_PROJECT} ${CURRENT_PROJECT}Rust) + + +endif() From 07dfcc6fd982b2c4aea74391237173a824c0e12d Mon Sep 17 00:00:00 2001 From: EndrII Date: Wed, 12 Nov 2025 13:40:12 +0100 Subject: [PATCH 6/8] added CMake Inteface implementation --- .gitignore | 1 + CMakeLists.txt | 119 ++++++++++---------------------------------- README.md | 39 ++++++++++++++- rust/OpenSimplex2.h | 10 +++- 4 files changed, 73 insertions(+), 96 deletions(-) diff --git a/.gitignore b/.gitignore index eb5a316..a9d37c5 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ target +Cargo.lock diff --git a/CMakeLists.txt b/CMakeLists.txt index a9434f9..21bb9e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,109 +1,40 @@ cmake_minimum_required(VERSION 3.19) -set(CURRENT_PROJECT "OpenSimplex2") +set(CURRENT_PROJECT "OpenSimplex2Interface") -option(OPENSIMPLEX_OLD "Enable build old C implementation instead Rust" OFF) +option(OPENSIMPLEX_C "Enable build old C implementation instead Rust" OFF) +option(OPENSIMPLEX_RUST "Enable build old C implementation instead Rust" ON) -if (OPENSIMPLEX_OLD) - set(SOURCE_CPP - "${CMAKE_CURRENT_SOURCE_DIR}/_old/c/OpenSimplex2F.c" - "${CMAKE_CURRENT_SOURCE_DIR}/_old/c/OpenSimplex2F.h" - ) - - add_library(${CURRENT_PROJECT} ${SOURCE_CPP}) - target_include_directories(${CURRENT_PROJECT} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/_old/c/") - target_compile_options(${CURRENT_PROJECT} PRIVATE -Wall -Wextra --pedantic) - -else() - include(ExternalProject) - - ExternalProject_Add( - ${CURRENT_PROJECT}Rust - DOWNLOAD_COMMAND "" - CONFIGURE_COMMAND "" - BUILD_COMMAND cargo build COMMAND cargo build --release --features std --target-dir "${CMAKE_CURRENT_BINARY_DIR}" - BINARY_DIR "${CMAKE_CURRENT_SOURCE_DIR}" - INSTALL_COMMAND "" - LOG_BUILD ON) - - - # ADD_CUSTOM_TARGET( - # ${CURRENT_PROJECT}Rust - # COMMAND cargo build --release --features std --target-dir "${CMAKE_CURRENT_BINARY_DIR}" - # COMMENT "cargo build --release --features std --target-dir ${CMAKE_CURRENT_BINARY_DIR}" - # WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - # ) - - if(WIN32) - if (BUILD_SHARED_LIBS) - add_library( ${CURRENT_PROJECT} SHARED IMPORTED ) - - # You can define two import-locations: one for debug and one for release. - set_target_properties( ${CURRENT_PROJECT} - PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/release/lib${CURRENT_PROJECT}.dll - INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}" - IMPORTED_IMPLIB ${CMAKE_CURRENT_BINARY_DIR}/release/lib${CURRENT_PROJECT}.lib - ) - else() - add_library( ${CURRENT_PROJECT} STATIC IMPORTED ) - - # You can define two import-locations: one for debug and one for release. - set_target_properties( ${CURRENT_PROJECT} - PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/release/libopensimplex.lib - INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}" - - ) - endif() +add_library(${CURRENT_PROJECT} INTERFACE) - elseif(APPLE) +if (OPENSIMPLEX_RUST) - if (BUILD_SHARED_LIBS) - add_library( ${CURRENT_PROJECT} SHARED IMPORTED ) + include(FetchContent) - # You can define two import-locations: one for debug and one for release. - set_target_properties( ${CURRENT_PROJECT} - PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/release/libopensimplex.dylib - INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}" - - ) - else() - add_library( ${CURRENT_PROJECT} STATIC IMPORTED ) - - # You can define two import-locations: one for debug and one for release. - set_target_properties( ${CURRENT_PROJECT} - PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/release/libopensimplex.lib - INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}" - - ) - endif() - - elseif(UNIX) - if (BUILD_SHARED_LIBS) - add_library( ${CURRENT_PROJECT} SHARED IMPORTED ) - - # You can define two import-locations: one for debug and one for release. - set_target_properties( ${CURRENT_PROJECT} - PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/release/libopensimplex.so - INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}" - IMPORTED_SONAME "lib${CURRENT_PROJECT}.so" - - ) - else() - add_library( ${CURRENT_PROJECT} STATIC IMPORTED ) + FetchContent_Declare( + Corrosion + GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git + GIT_TAG v0.5 + ) + FetchContent_MakeAvailable(Corrosion) + corrosion_import_crate(MANIFEST_PATH ${CMAKE_CURRENT_SOURCE_DIR}/Cargo.toml ALL_FEATURES) - # You can define two import-locations: one for debug and one for release. - set_target_properties( ${CURRENT_PROJECT} - PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/release/libopensimplex.a - INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}" - IMPORTED_SONAME "lib${CURRENT_PROJECT}.a" + target_link_libraries(${CURRENT_PROJECT} INTERFACE "opensimplex2") + target_include_directories(${CURRENT_PROJECT} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) - ) - endif() +endif() - endif() +if (OPENSIMPLEX_C) - add_dependencies(${CURRENT_PROJECT} ${CURRENT_PROJECT}Rust) + set(SOURCE_CPP + "${CMAKE_CURRENT_SOURCE_DIR}/_old/c/OpenSimplex2F.c" + "${CMAKE_CURRENT_SOURCE_DIR}/_old/c/OpenSimplex2F.h" + ) + add_library(${CURRENT_PROJECT}C ${SOURCE_CPP}) + target_include_directories(${CURRENT_PROJECT}C PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/_old/") + target_compile_options(${CURRENT_PROJECT}C PRIVATE -Wall -Wextra --pedantic) + target_link_libraries(${CURRENT_PROJECT} INTERFACE ${CURRENT_PROJECT}C) endif() diff --git a/README.md b/README.md index 63e21c5..1a4212c 100644 --- a/README.md +++ b/README.md @@ -38,18 +38,55 @@ Gradient vector tables were also revisited to improve probability symmetry in bo Note: area-generators have been moved to [their original repository](https://github.com/KdotJPG/Noise-VertexQueue-AreaGen). ## C FFI usage + +### Monual usage Install a [Rust toolchain](https://www.rust-lang.org/tools/install) if needed. Build debug or release artifacts with: ``` cargo build # or -cargo build --release +cargo build --release --features std ``` Library files will be in `./target/{debug,release}/`. Copy header from [`./rust/OpenSimplex2.h`](./rust/OpenSimplex2.h). +### CMake + +To include the library in your own CMake project, simply add the subdirectory and link the OpenSimplex2 interface target: + +```cmake +add_subdirectory(submodules/OpenSimplex2) +target_link_libraries(${CURRENT_PROJECT} PUBLIC OpenSimplex2Interface) +``` + +The OpenSimplex2 interface provides two implementation backends (C and Rust). +By default, only the Rust backend is compiled. +To control which backend should be used, configure the following options: + +* OPENSIMPLEX_C — OFF by default +* OPENSIMPLEX_RUST — ON by default + +You can enable both backends simultaneously if needed. +To override the default options, use: + +```cmake +option(OPENSIMPLEX_C "Enable build old C implementation instead Rust" ON) +option(OPENSIMPLEX_RUST "Enable build old C implementation instead Rust" ON) +``` + +### Including in C/C++ Projects + +``` cpp +#include // if the OPENSIMPLEX_C option enabled +#include // if the OPENSIMPLEX_RUST option enabled +``` + +**Note:** +The C and Rust implementations use different function names to call the noise generators. + ## Changelog +* Rust implementation optimisations and CMake Inteface implementation (Nov 12, 2025) * Tuned up this `README.md`. (Mar 26, 2022) * Re-wrote functions to be instancelessly seedable and less dependent on lookup tables. Re-organized repository. Renamed `OpenSimplex2F` to just `OpenSimplex2` in file/class names. (Jan 16, 2022) * Shortened lookup table for Simplex/OpenSimplex2(F) 4D (July 5, 2020) diff --git a/rust/OpenSimplex2.h b/rust/OpenSimplex2.h index c0cc957..67da9d8 100644 --- a/rust/OpenSimplex2.h +++ b/rust/OpenSimplex2.h @@ -1,6 +1,10 @@ -#pragma once #ifndef OPENSIMPLEX2_H + +#ifdef __cplusplus +extern "C" { +#endif + extern float opensimplex2_fast_noise2(long long seed, double x, double y); extern float opensimplex2_fast_noise2_ImproveX(long long seed, double x, double y); extern float opensimplex2_fast_noise3_ImproveXY(long long seed, double x, double y, double z); @@ -22,4 +26,8 @@ extern float opensimplex2_smooth_noise4_ImproveXYZ(long long seed, double x, dou extern float opensimplex2_smooth_noise4_ImproveXY_ImproveZW(long long seed, double x, double y, double z, double w); extern float opensimplex2_smooth_noise4_Fallback(long long seed, double x, double y, double z, double w); +#ifdef __cplusplus +} +#endif + #endif From 48cf0c25a9e4f24dec30f25e93497f9a162d5abd Mon Sep 17 00:00:00 2001 From: Andrei Yankovich Date: Wed, 12 Nov 2025 15:51:56 +0300 Subject: [PATCH 7/8] Apply suggestions from code review --- CMakeLists.txt | 4 ++-- README.md | 4 ++-- rust/OpenSimplex2.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 21bb9e0..16e0ac4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,8 +2,8 @@ cmake_minimum_required(VERSION 3.19) set(CURRENT_PROJECT "OpenSimplex2Interface") -option(OPENSIMPLEX_C "Enable build old C implementation instead Rust" OFF) -option(OPENSIMPLEX_RUST "Enable build old C implementation instead Rust" ON) +option(OPENSIMPLEX_C "Enable/Disable build of the old C implementation" OFF) +option(OPENSIMPLEX_RUST "Enable/Disable build of the main Rust implementation" ON) add_library(${CURRENT_PROJECT} INTERFACE) diff --git a/README.md b/README.md index 1a4212c..21e0af0 100644 --- a/README.md +++ b/README.md @@ -70,8 +70,8 @@ You can enable both backends simultaneously if needed. To override the default options, use: ```cmake -option(OPENSIMPLEX_C "Enable build old C implementation instead Rust" ON) -option(OPENSIMPLEX_RUST "Enable build old C implementation instead Rust" ON) +option(OPENSIMPLEX_C "Enable/Disable build of the old C implementation" OFF) +option(OPENSIMPLEX_RUST "Enable/Disable build of the main Rust implementation" ON) ``` ### Including in C/C++ Projects diff --git a/rust/OpenSimplex2.h b/rust/OpenSimplex2.h index 67da9d8..f371300 100644 --- a/rust/OpenSimplex2.h +++ b/rust/OpenSimplex2.h @@ -1,5 +1,5 @@ #ifndef OPENSIMPLEX2_H - +#define OPENSIMPLEX2_H #ifdef __cplusplus extern "C" { From f321f1ee567ae6f2380e0f922e9d97060a952d6a Mon Sep 17 00:00:00 2001 From: EndrII Date: Thu, 13 Nov 2025 10:48:36 +0100 Subject: [PATCH 8/8] update Rust configure tool --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 16e0ac4..236a1ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,8 @@ if (OPENSIMPLEX_RUST) FetchContent_Declare( Corrosion GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git - GIT_TAG v0.5 + GIT_TAG v0.5.2 + ) FetchContent_MakeAvailable(Corrosion) corrosion_import_crate(MANIFEST_PATH ${CMAKE_CURRENT_SOURCE_DIR}/Cargo.toml ALL_FEATURES)