Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposed new API #31

Closed
Cifram opened this issue Dec 19, 2014 · 8 comments
Closed

Proposed new API #31

Cifram opened this issue Dec 19, 2014 · 8 comments
Labels

Comments

@Cifram
Copy link
Collaborator

Cifram commented Dec 19, 2014

Based on a discussion on IRC #rust-gamedev, I want to propose a new API, as so:

struct Seed { ... }
Seed {
    fn new(seed: u32) -> Seed;
}

fn perlin2d(seed: Seed, point: (f32, f32)) -> f32;
fn perlin3d(seed: Seed, point: (f32, f32, f32)) -> f32;
fn perlin3d(seed: Seed, point: (f32, f32, f32, f32)) -> f32;

fn open_simplex2d(seed: Seed, point: (f32, f32)) -> f32;
fn open_simplex3d(seed: Seed, point: (f32, f32, f32)) -> f32;
fn open_simplex4d(seed: Seed, point: (f32, f32, f32, f32)) -> f32;

fn worley2d_points(seed: Seed, point: (f32, f32)) -> [(f32, f32), ..9];
fn worley3d_points(seed: Seed, point: (f32, f32, f32)) -> [(f32, f32, f32), ..27];
fn worley4d_points(seed: Seed, point: (f32, f32, f32, f32)) -> [(f32, f32, f32, f32), ..81];

fn worley2d_nearest_point(seed: Seed, point: (f32, f32)) -> f32;
fn worley3d_nearest_point(seed: Seed, point: (f32, f32, f32)) -> f32;
fn worley4d_nearest_point(seed: Seed, point: (f32, f32, f32, f32)) -> f32;

fn worley2d_nearest_edge(seed: Seed, point: (f32, f32)) -> f32;
fn worley3d_nearest_edge(seed: Seed, point: (f32, f32, f32)) -> f32;
fn worley4d_nearest_edge(seed: Seed, point: (f32, f32, f32, f32)) -> f32;

fn worley2d_manhattan_point(seed: Seed, point: (f32, f32)) -> f32;
fn worley3d_manhattan_point(seed: Seed, point: (f32, f32, f32)) -> f32;
fn worley4d_manhattan_point(seed: Seed, point: (f32, f32, f32, f32)) -> f32;

fn worley2d_manhattan_edge(seed: Seed, point: (f32, f32)) -> f32;
fn worley3d_manhattan_edge(seed: Seed, point: (f32, f32, f32)) -> f32;
fn worley4d_manhattan_edge(seed: Seed, point: (f32, f32, f32, f32)) -> f32;

fn brownian2d<F>(seed: Seed, point: (f32, f32), noise_func: F, wavelength: f32, octaves: u32) -> f32
    where F: Fn(Seed, (f32, f32)) -> f32;
fn brownian3d<F>(seed: Seed, point: (f32, f32, f32), noise_func: F, wavelength: f32, octaves: u32) -> f32
    where F: Fn(Seed, (f32, f32, f32)) -> f32;
fn brownian4d<F>(seed: Seed, point: (f32, f32, f32, f32), noise_func: F, wavelength: f32, octaves: u32) -> f32
    where F: Fn(Seed, (f32, f32, f32, f32)) -> f32;

This API is simple and composable. To explain the different pieces of it:

The Seed struct just contains a shuffled array of bytes, useful for generating consistent, location-based random numbers.

Perlin noise is one of the most standard noise equations around.

Simplex noise is an improved version of Perlin noise, and also very standard. However, it was pointed out to me by bjz that Simplex noise is under patent, but OpenSimplex is almost as good.

Worley noise, also called Cell noise or Voronoi noise, is unusual. It involves dividing space into a set of cells by placing a point randomly in each hypercubic region of space, and using proximity to that point, and the points in all neighboring hypercubic regions, to determine which cell the current point occupies. There are a lot of ways of calculating this proximity, which produce a variety of different effects. To support any kind of Worley noise the user may want, the basic Worley noise function just returns the complete set of points from this and all neighboring regions. Then we have an additional set of functions built on top that for the most common types of Worley noise:

  • Nearest returns the distance to the nearest point.
  • Manhattan returns the distance to the nearest point as measured in Manhattan distance. Manhattan distance is just the largest dimension of the offset. This is useful for producing squared off cells.
  • Nearest edge returns the distance to the second nearest point minus the distance to the nearest point, which gives you a number that approximates distance from the nearest edge.
  • Manhattan edge is like nearest edge, but using manhattan distance.

The brownian functions do fractal brownian motion on whatever noise function you hand it, with the specified wavelength (the size of the first iteration) and octaves (the number of iterations). Each iteration has half the wavelength and half the amplitude of the previous iteration.

@brendanzab
Copy link
Collaborator

I like this. Any reason why you are using functions, not methods on Seed?

@mpowell-imvu
Copy link

Because it hadn't occurred to me. :-)

My first draft didn't include Seed. Then I realized I needed a seed structure, so added it to all the functions. So I didn't think of turning them into member functions.

That said, I think making them into member functions would make them less composable. If I want to call brownian2d on perlin noise now, this is how I'd do it:

let noise = brownian2d(seed, point, perlin2d, 16, 3);

But if we made these member functions of Seed, it would have to be:

let noise = seed.brownian2d(point, |seed: Seed, point: (f32, f32)| seed.perlin2d(point), 16, 3);

So I think I'd argue to keep them as free functions. That said, Seed should really be passed by borrow. I'll go through and fix that.

Also, I left of lacunarity and persistence in the brownian motion, even though your current implementation includes them. They should probably be added back in. However, I also feel they should have defaults of 2.0 and 0.5, as that's what you want like 99% of the time.

@mpowell-imvu
Copy link

Oh, apparently I can't edit the issue once it's been responded to. Oh well. The proposed changes are still captured in my comment.

Also thinking about a way to better generalize the worley noise into two parts. One generates a set of nearby cell points, and the other analyzes those points to produce a value. Sort of like what's already in this proposal, except with the ability to swap out for a different point generation algorithm.

I don't think I'd include any other point generation algorithms in the noise-rs library, for now... But I could see consumers of this library wanting to implement some alternate algorithms.

@brendanzab
Copy link
Collaborator

Regarding the composability concerns, note that UFCS you could do:

seed.brownian2d((x, y), Seed::perlin2d, ..)

Have you seen http://libnoise.sourceforge.net/ by the way? Here are some Haskell libraries based on it:

Might be abit complex though, I like the simplicity of your approach.

@Cifram
Copy link
Collaborator Author

Cifram commented Dec 20, 2014

I had not seen this... I'll dig into it's structure a bit, and see if there's anything that seems worth pulling out.

@Razaekel
Copy link
Owner

Razaekel commented Mar 2, 2017

@Cifram do you feel a need to expand on this any further?

@Cifram
Copy link
Collaborator Author

Cifram commented Mar 2, 2017

No, not really... This post was made before I started contributing to this library. This API was pretty much implemented, and refined, before my attention turned elsewhere. I haven't been actively involved in any development here for close to 2 years.

@Razaekel
Copy link
Owner

Razaekel commented Mar 2, 2017

Thanks for the followup.

@Razaekel Razaekel closed this as completed Mar 2, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants