Skip to content

Instantly share code, notes, and snippets.

@jessestricker
Created July 13, 2016 18:41
Show Gist options
  • Save jessestricker/0688dbc21bd398a8369c22f34922e29a to your computer and use it in GitHub Desktop.
Save jessestricker/0688dbc21bd398a8369c22f34922e29a to your computer and use it in GitHub Desktop.
[C++] OpenSimplexNoise
#include "OpenSimplexNoise.hpp"
#include <iostream>
#include <string>
int main()
{
OpenSimplexNoise noise(1234);
// check for persistance
std::cout << noise(0, 0) << std::endl;
std::cout << noise(0, 0) << std::endl;
// print other values
std::cout << noise(0, 1) << std::endl;
std::cout << noise(1, 0) << std::endl;
std::cout << noise(1, 1) << std::endl;
// wait for enter key
std::string s;
std::getline(std::cin, s);
return 0;
}
#include "OpenSimplexNoise.hpp"
#include <cmath> // sqrt
#include <numeric> // std::iota
#include <algorithm> // std::shuffle
const OpenSimplexNoise::value_t
OpenSimplexNoise::STRETCH_CONST = (sqrt(3) - 3) / 6,
OpenSimplexNoise::SQUISH_CONST = (sqrt(3) - 1) / 2;
const std::array<OpenSimplexNoise::gradient, 8> OpenSimplexNoise::GRADIENTS =
{
gradient{ 5,2 },{ 2,5 },
{ -5,2 },{ -2,5 },
{ 5, -2 },{ 2, -5 },
{ -5, -2 },{ -2, -5 },
};
OpenSimplexNoise::OpenSimplexNoise(seed_t seed) :
_seed(seed), _perm{}
{
// fill perm with numbers from 0 to UINT8_MAX
std::iota(_perm.begin(), _perm.end(), 0);
// shuffle all values using a seeded mersenne twister
std::mt19937 randEngine(seed);
std::shuffle(_perm.begin(), _perm.end(), randEngine);
}
OpenSimplexNoise::seed_t OpenSimplexNoise::seed() const { return _seed; }
OpenSimplexNoise::value_t OpenSimplexNoise::operator()(value_t x, value_t y) const
{
value_t xsb, ysb;
value_t xins, yins;
{
// place input coordinates onto grid.
auto stretchOffset = (x + y) * STRETCH_CONST;
auto xs = x + stretchOffset;
auto ys = y + stretchOffset;
// floor to get grid coordinates of rhombus (stretched square) super-cell origin.
xsb = static_cast<value_t>(fast_floor(xs));
ysb = static_cast<value_t>(fast_floor(ys));
// compute grid coordinates relative to rhombus origin.
xins = xs - xsb;
yins = ys - ysb;
}
auto inSum = xins + yins;
value_t dx0, dy0;
{
// skew out to get actual coordinates of rhombus origin
auto squishOffset = (xsb + ysb) * SQUISH_CONST;
auto xb = xsb + squishOffset;
auto yb = ysb + squishOffset;
// positions relative to origin point.
dx0 = x - xb;
dy0 = y - yb;
}
value_t value = 0;
// contribution (1,0)
{
auto dx1 = dx0 - 1 - SQUISH_CONST;
auto dy1 = dy0 - 0 - SQUISH_CONST;
auto attn1 = 2 - dx1 * dx1 - dy1 * dy1;
if (attn1 > 0) {
attn1 *= attn1;
value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, dx1, dy1);
}
}
// contribution (0,1)
{
auto dx2 = dx0 - 0 - SQUISH_CONST;
auto dy2 = dy0 - 1 - SQUISH_CONST;
auto attn2 = 2 - dx2 * dx2 - dy2 * dy2;
if (attn2 > 0) {
attn2 *= attn2;
value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, dx2, dy2);
}
}
value_t dx_ext, dy_ext;
int xsv_ext, ysv_ext;
{
if (inSum <= 1) { // we're inside the triangle (2-simplex) at (0,0)
auto zins = 1 - inSum;
if (zins > xins || zins > yins) { // (0,0) is one of the closest two triangular vertices
if (xins > yins) {
xsv_ext = xsb + 1;
ysv_ext = ysb - 1;
dx_ext = dx0 - 1;
dy_ext = dy0 + 1;
}
else {
xsv_ext = xsb - 1;
ysv_ext = ysb + 1;
dx_ext = dx0 + 1;
dy_ext = dy0 - 1;
}
}
else { // (1,0) and (0,1) are the closest two vertices.
xsv_ext = xsb + 1;
ysv_ext = ysb + 1;
dx_ext = dx0 - 1 - 2 * SQUISH_CONST;
dy_ext = dy0 - 1 - 2 * SQUISH_CONST;
}
}
else { // we're inside the triangle (2-simplex) at (1,1)
auto zins = 2 - inSum;
if (zins < xins || zins < yins) { // (0,0) is one of the closest two triangular vertices
if (xins > yins) {
xsv_ext = xsb + 2;
ysv_ext = ysb + 0;
dx_ext = dx0 - 2 - 2 * SQUISH_CONST;
dy_ext = dy0 + 0 - 2 * SQUISH_CONST;
}
else {
xsv_ext = xsb + 0;
ysv_ext = ysb + 2;
dx_ext = dx0 + 0 - 2 * SQUISH_CONST;
dy_ext = dy0 - 2 - 2 * SQUISH_CONST;
}
}
else { // (1,0) and (0,1) are the closest two vertices.
dx_ext = dx0;
dy_ext = dy0;
xsv_ext = xsb;
ysv_ext = ysb;
}
xsb += 1;
ysb += 1;
dx0 = dx0 - 1 - 2 * SQUISH_CONST;
dy0 = dy0 - 1 - 2 * SQUISH_CONST;
}
}
// contribution (0,0) or (1,1)
{
auto attn0 = 2 - dx0 * dx0 - dy0 * dy0;
if (attn0 > 0) {
attn0 *= attn0;
value += attn0 * attn0 * extrapolate(xsb, ysb, dx0, dy0);
}
}
// extra vertex
{
auto attn_ext = 2 - dx_ext * dx_ext - dy_ext * dy_ext;
if (attn_ext > 0) {
attn_ext *= attn_ext;
value += attn_ext * attn_ext * extrapolate(xsv_ext, ysv_ext, dx_ext, dy_ext);
}
}
return value / NORM_CONST;
}
OpenSimplexNoise::value_t OpenSimplexNoise::extrapolate(uint8_t xsb, uint8_t ysb, value_t dx, value_t dy) const
{
uint8_t index = _perm[(_perm[xsb & 0xFF] + ysb) & 0xFF];
index &= 0x0E;
index /= 2;
auto& gradient = GRADIENTS[index];
return gradient.x * dx + gradient.y * dy;
}
#pragma once
#include <array> // std::array
#include <cstdint> // int8_t, ...
#include <limits> // std::numeric_limits<>
#include <random> // std::mt19937
class OpenSimplexNoise
{
public:
typedef std::mt19937::result_type seed_t;
typedef double value_t;
static constexpr seed_t DEFAULT_SEED = 0;
static constexpr value_t NORM_CONST = 47;
static const value_t STRETCH_CONST, SQUISH_CONST;
OpenSimplexNoise(seed_t seed = DEFAULT_SEED);
seed_t seed() const;
value_t operator()(value_t x, value_t y) const;
private:
struct gradient
{
int8_t x, y;
gradient(int8_t x, int8_t y) : x(x), y(y) {}
};
static const std::array<gradient, 8> GRADIENTS;
seed_t _seed;
std::array<uint8_t, std::numeric_limits<uint8_t>::max() + 1> _perm;
value_t extrapolate(uint8_t xsb, uint8_t ysb, value_t dx, value_t dy) const;
};
//
// utility functions
inline int32_t fast_floor(float v)
{
auto vi = static_cast<int32_t>(v);
return v < vi ? vi - 1 : vi;
}
inline int64_t fast_floor(double v)
{
auto vi = static_cast<int64_t>(v);
return v < vi ? vi - 1 : vi;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment