opensimplex2.odin 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. /*
  2. OpenSimplex2 noise implementation.
  3. Ported from https://github.com/KdotJPG/OpenSimplex2.
  4. Copyright 2022 Yuki2 (https://github.com/NoahR02)
  5. */
  6. package math_noise
  7. /*
  8. Input coordinate vectors
  9. */
  10. Vec2 :: [2]f64
  11. Vec3 :: [3]f64
  12. Vec4 :: [4]f64
  13. /*
  14. Noise Evaluators
  15. */
  16. /*
  17. 2D Simplex noise, standard lattice orientation.
  18. */
  19. @(require_results)
  20. noise_2d :: proc(seed: i64, coord: Vec2) -> (value: f32) {
  21. // Get points for A2* lattice
  22. skew := SKEW_2D * (coord.x + coord.y)
  23. skewed := coord + skew
  24. return _internal_noise_2d_unskewed_base(seed, skewed)
  25. }
  26. /*
  27. 2D Simplex noise, with Y pointing down the main diagonal.
  28. Might be better for a 2D sandbox style game, where Y is vertical.
  29. Probably slightly less optimal for heightmaps or continent maps,
  30. unless your map is centered around an equator. It's a subtle
  31. difference, but the option is here to make it an easy choice.
  32. */
  33. @(require_results)
  34. noise_2d_improve_x :: proc(seed: i64, coord: Vec2) -> (value: f32) {
  35. // Skew transform and rotation baked into one.
  36. xx := coord.x * ROOT_2_OVER_2
  37. yy := coord.y * (ROOT_2_OVER_2 * (1 + 2 * SKEW_2D))
  38. return _internal_noise_2d_unskewed_base(seed, Vec2{yy + xx, yy - xx})
  39. }
  40. /*
  41. 3D OpenSimplex2 noise, with better visual isotropy in (X, Y).
  42. Recommended for 3D terrain and time-varied animations.
  43. The Z coordinate should always be the "different" coordinate in whatever your use case is.
  44. If Y is vertical in world coordinates, call `noise_3d_improve_xz(x, z, Y)` or use `noise_3d_xz_before_y`.
  45. If Z is vertical in world coordinates, call `noise_3d_improve_xz(x, y, Z)`.
  46. For a time varied animation, call `noise_3d_improve_xz(x, y, T)`.
  47. */
  48. @(require_results)
  49. noise_3d_improve_xy :: proc(seed: i64, coord: Vec3) -> (value: f32) {
  50. /*
  51. Re-orient the cubic lattices without skewing, so Z points up the main lattice diagonal,
  52. and the planes formed by XY are moved far out of alignment with the cube faces.
  53. Orthonormal rotation. Not a skew transform.
  54. */
  55. xy := coord.x + coord.y
  56. s2 := xy * ROTATE_3D_ORTHOGONALIZER
  57. zz := coord.z * ROOT_3_OVER_3
  58. r := Vec3{coord.x + s2 + zz, coord.y + s2 + zz, xy * -ROOT_3_OVER_3 + zz}
  59. // Evaluate both lattices to form a BCC lattice.
  60. return _internal_noise_3d_unrotated_base(seed, r)
  61. }
  62. /*
  63. 3D OpenSimplex2 noise, with better visual isotropy in (X, Z).
  64. Recommended for 3D terrain and time-varied animations.
  65. The Y coordinate should always be the "different" coordinate in whatever your use case is.
  66. If Y is vertical in world coordinates, call `noise_3d_improve_xz(x, Y, z)`.
  67. If Z is vertical in world coordinates, call `noise_3d_improve_xz(x, Z, y)` or use `noise_3d_improve_xy`.
  68. For a time varied animation, call `noise_3d_improve_xz(x, T, y)` or use `noise_3d_improve_xy`.
  69. */
  70. @(require_results)
  71. noise_3d_improve_xz :: proc(seed: i64, coord: Vec3) -> (value: f32) {
  72. /*
  73. Re-orient the cubic lattices without skewing, so Y points up the main lattice diagonal,
  74. and the planes formed by XZ are moved far out of alignment with the cube faces.
  75. Orthonormal rotation. Not a skew transform.
  76. */
  77. xz := coord.x + coord.z
  78. s2 := xz * ROTATE_3D_ORTHOGONALIZER
  79. yy := coord.y * ROOT_3_OVER_3
  80. r := Vec3{coord.x + s2 + yy, xz * -ROOT_3_OVER_3 + yy, coord.z + s2 + yy}
  81. // Evaluate both lattices to form a BCC lattice.
  82. return _internal_noise_3d_unrotated_base(seed, r)
  83. }
  84. /*
  85. 3D OpenSimplex2 noise, fallback rotation option
  86. Use `noise_3d_improve_xy` or `noise_3d_improve_xz` instead, wherever appropriate.
  87. They have less diagonal bias. This function's best use is as a fallback.
  88. */
  89. @(require_results)
  90. noise_3d_fallback :: proc(seed: i64, coord: Vec3) -> (value: f32) {
  91. /*
  92. Re-orient the cubic lattices via rotation, to produce a familiar look.
  93. Orthonormal rotation. Not a skew transform.
  94. */
  95. bias := FALLBACK_ROTATE_3D * (coord.x + coord.y + coord.z)
  96. biased := bias - coord
  97. // Evaluate both lattices to form a BCC lattice.
  98. return _internal_noise_3d_unrotated_base(seed, biased)
  99. }
  100. /*
  101. 4D OpenSimplex2 noise, with XYZ oriented like `noise_3d_improve_xy`
  102. and W for an extra degree of freedom. W repeats eventually.
  103. Recommended for time-varied animations which texture a 3D object (W=time)
  104. in a space where Z is vertical.
  105. */
  106. @(require_results)
  107. noise_4d_improve_xyz_improve_xy :: proc(seed: i64, coord: Vec4) -> (value: f32) {
  108. xy := coord.x + coord.y
  109. s2 := xy * -0.21132486540518699998
  110. zz := coord.z * 0.28867513459481294226
  111. ww := coord.w * 0.2236067977499788
  112. xr, yr : f64 = coord.x + (zz + ww + s2), coord.y + (zz + ww + s2)
  113. zr : f64 = xy * -0.57735026918962599998 + (zz + ww)
  114. wr : f64 = coord.z * -0.866025403784439 + ww
  115. return _internal_noise_4d_unskewed_base(seed, Vec4{xr, yr, zr, wr})
  116. }
  117. /*
  118. 4D OpenSimplex2 noise, with XYZ oriented like `noise_3d_improve_xz`
  119. and W for an extra degree of freedom. W repeats eventually.
  120. Recommended for time-varied animations which texture a 3D object (W=time)
  121. in a space where Y is vertical.
  122. */
  123. @(require_results)
  124. noise_4d_improve_xyz_improve_xz :: proc(seed: i64, coord: Vec4) -> (value: f32) {
  125. xz := coord.x + coord.z
  126. s2 := xz * -0.21132486540518699998
  127. yy := coord.y * 0.28867513459481294226
  128. ww := coord.w * 0.2236067977499788
  129. xr, zr : f64 = coord.x + (yy + ww + s2), coord.z + (yy + ww + s2)
  130. yr := xz * -0.57735026918962599998 + (yy + ww)
  131. wr := coord.y * -0.866025403784439 + ww
  132. return _internal_noise_4d_unskewed_base(seed, Vec4{xr, yr, zr, wr})
  133. }
  134. /*
  135. 4D OpenSimplex2 noise, with XYZ oriented like `noise_3d_fallback`
  136. and W for an extra degree of freedom. W repeats eventually.
  137. Recommended for time-varied animations which texture a 3D object (W=time)
  138. where there isn't a clear distinction between horizontal and vertical
  139. */
  140. @(require_results)
  141. noise_4d_improve_xyz :: proc(seed: i64, coord: Vec4) -> (value: f32) {
  142. xyz := coord.x + coord.y + coord.z
  143. ww := coord.w * 0.2236067977499788
  144. s2 := xyz * -0.16666666666666666 + ww
  145. skewed := Vec4{coord.x + s2, coord.y + s2, coord.z + s2, -0.5 * xyz + ww}
  146. return _internal_noise_4d_unskewed_base(seed, skewed)
  147. }
  148. /*
  149. 4D OpenSimplex2 noise, fallback lattice orientation.
  150. */
  151. @(require_results)
  152. noise_4d_fallback :: proc(seed: i64, coord: Vec4) -> (value: f32) {
  153. // Get points for A4 lattice
  154. skew := f64(SKEW_4D) * (coord.x + coord.y + coord.z + coord.w)
  155. return _internal_noise_4d_unskewed_base(seed, coord + skew)
  156. }