OpenSimplex 2D noise
This commit is contained in:
parent
20b405c28d
commit
9163942d94
3 changed files with 245 additions and 130 deletions
27
include/OpenSimplex2F.h
Normal file
27
include/OpenSimplex2F.h
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int xsv, ysv;
|
||||||
|
double dx, dy;
|
||||||
|
} LatticePoint2D;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
double dx, dy;
|
||||||
|
} Grad2;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
short *perm;
|
||||||
|
Grad2 *permGrad2;
|
||||||
|
} OpenSimplexGradients;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
Grad2 *GRADIENTS_2D;
|
||||||
|
LatticePoint2D **LOOKUP_2D;
|
||||||
|
} OpenSimplexEnv;
|
||||||
|
|
||||||
|
OpenSimplexEnv *initOpenSimplex();
|
||||||
|
OpenSimplexGradients *newOpenSimplexGradients(OpenSimplexEnv *ose, long seed);
|
||||||
|
double noise2(OpenSimplexEnv *ose, OpenSimplexGradients *osg, double x, double y);
|
||||||
|
double noise2_XBeforeY(OpenSimplexEnv *ose, OpenSimplexGradients *osg, double x, double y);
|
203
src/OpenSimplex2F.c
Normal file
203
src/OpenSimplex2F.c
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "OpenSimplex2F.h"
|
||||||
|
|
||||||
|
#define PSIZE 2048
|
||||||
|
#define PMASK 2047
|
||||||
|
#define N2 0.01001634121365712
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Utility
|
||||||
|
*/
|
||||||
|
|
||||||
|
int inline _fastFloor(double x)
|
||||||
|
{
|
||||||
|
int xi = (int)x;
|
||||||
|
return x < xi ? xi - 1 : xi;
|
||||||
|
}
|
||||||
|
|
||||||
|
Grad2 *_newGrad2Arr(unsigned int size)
|
||||||
|
{
|
||||||
|
return (Grad2 *)malloc(sizeof(Grad2) * size);
|
||||||
|
}
|
||||||
|
|
||||||
|
short *_newShortArr(unsigned int size)
|
||||||
|
{
|
||||||
|
return (short *)malloc(sizeof(short) * size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Grad2 _newGrad2(double dx, double dy)
|
||||||
|
{
|
||||||
|
Grad2 grad2;
|
||||||
|
grad2.dx = dx;
|
||||||
|
grad2.dy = dy;
|
||||||
|
return grad2;
|
||||||
|
}
|
||||||
|
|
||||||
|
Grad2 *_newGrad2ConstArray()
|
||||||
|
{
|
||||||
|
Grad2 *arr = (Grad2 *)malloc(sizeof(Grad2) * 24);
|
||||||
|
int i = 0;
|
||||||
|
arr[i++] = _newGrad2(0.130526192220052, 0.99144486137381);
|
||||||
|
arr[i++] = _newGrad2(0.38268343236509, 0.923879532511287);
|
||||||
|
arr[i++] = _newGrad2(0.608761429008721, 0.793353340291235);
|
||||||
|
arr[i++] = _newGrad2(0.793353340291235, 0.608761429008721);
|
||||||
|
arr[i++] = _newGrad2(0.923879532511287, 0.38268343236509);
|
||||||
|
arr[i++] = _newGrad2(0.99144486137381, 0.130526192220051);
|
||||||
|
arr[i++] = _newGrad2(0.99144486137381, -0.130526192220051);
|
||||||
|
arr[i++] = _newGrad2(0.923879532511287, -0.38268343236509);
|
||||||
|
arr[i++] = _newGrad2(0.793353340291235, -0.60876142900872);
|
||||||
|
arr[i++] = _newGrad2(0.608761429008721, -0.793353340291235);
|
||||||
|
arr[i++] = _newGrad2(0.38268343236509, -0.923879532511287);
|
||||||
|
arr[i++] = _newGrad2(0.130526192220052, -0.99144486137381);
|
||||||
|
arr[i++] = _newGrad2(-0.130526192220052, -0.99144486137381);
|
||||||
|
arr[i++] = _newGrad2(-0.38268343236509, -0.923879532511287);
|
||||||
|
arr[i++] = _newGrad2(-0.608761429008721, -0.793353340291235);
|
||||||
|
arr[i++] = _newGrad2(-0.793353340291235, -0.608761429008721);
|
||||||
|
arr[i++] = _newGrad2(-0.923879532511287, -0.38268343236509);
|
||||||
|
arr[i++] = _newGrad2(-0.99144486137381, -0.130526192220052);
|
||||||
|
arr[i++] = _newGrad2(-0.99144486137381, 0.130526192220051);
|
||||||
|
arr[i++] = _newGrad2(-0.923879532511287, 0.38268343236509);
|
||||||
|
arr[i++] = _newGrad2(-0.793353340291235, 0.608761429008721);
|
||||||
|
arr[i++] = _newGrad2(-0.608761429008721, 0.793353340291235);
|
||||||
|
arr[i++] = _newGrad2(-0.38268343236509, 0.923879532511287);
|
||||||
|
arr[i++] = _newGrad2(-0.130526192220052, 0.99144486137381);
|
||||||
|
Grad2 *gradients2D = _newGrad2Arr(PSIZE);
|
||||||
|
for (int i = 0; i < 24; i++)
|
||||||
|
{
|
||||||
|
arr[i].dx /= N2;
|
||||||
|
arr[i].dy /= N2;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < PSIZE; i++)
|
||||||
|
{
|
||||||
|
gradients2D[i] = arr[i % 24];
|
||||||
|
}
|
||||||
|
return gradients2D;
|
||||||
|
}
|
||||||
|
|
||||||
|
LatticePoint2D *_newLatticePoint2D(int xsv, int ysv)
|
||||||
|
{
|
||||||
|
LatticePoint2D *plp2D = (LatticePoint2D *)malloc(sizeof(LatticePoint2D));
|
||||||
|
plp2D->xsv = xsv;
|
||||||
|
plp2D->ysv = ysv;
|
||||||
|
double ssv = (xsv + ysv) * -0.211324865405187;
|
||||||
|
plp2D->dx = -xsv - ssv;
|
||||||
|
plp2D->dy = -ysv - ssv;
|
||||||
|
return plp2D;
|
||||||
|
}
|
||||||
|
|
||||||
|
LatticePoint2D **_newLatticePoint2DConstArray()
|
||||||
|
{
|
||||||
|
LatticePoint2D **plp2DArr = (LatticePoint2D **)malloc(sizeof(LatticePoint2D *) * 4);
|
||||||
|
plp2DArr[0] = _newLatticePoint2D(1, 0);
|
||||||
|
plp2DArr[1] = _newLatticePoint2D(0, 0);
|
||||||
|
plp2DArr[2] = _newLatticePoint2D(1, 1);
|
||||||
|
plp2DArr[3] = _newLatticePoint2D(0, 1);
|
||||||
|
return plp2DArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Noise Evaluators
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 2D Simplex noise base.
|
||||||
|
* Lookup table implementation inspired by DigitalShadow.
|
||||||
|
*/
|
||||||
|
double _noise2_Base(OpenSimplexEnv *ose, OpenSimplexGradients *osg, double xs, double ys)
|
||||||
|
{
|
||||||
|
double value = 0;
|
||||||
|
|
||||||
|
// Get base points and offsets
|
||||||
|
int xsb = _fastFloor(xs), ysb = _fastFloor(ys);
|
||||||
|
double xsi = xs - xsb, ysi = ys - ysb;
|
||||||
|
|
||||||
|
// Index to point list
|
||||||
|
int index = (int)((ysi - xsi) / 2 + 1);
|
||||||
|
|
||||||
|
double ssi = (xsi + ysi) * -0.211324865405187;
|
||||||
|
double xi = xsi + ssi, yi = ysi + ssi;
|
||||||
|
|
||||||
|
// Point contributions
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
LatticePoint2D *c = ose->LOOKUP_2D[index + i];
|
||||||
|
|
||||||
|
double dx = xi + c->dx, dy = yi + c->dy;
|
||||||
|
double attn = 0.5 - dx * dx - dy * dy;
|
||||||
|
if (attn <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int pxm = (xsb + c->xsv) & PMASK, pym = (ysb + c->ysv) & PMASK;
|
||||||
|
Grad2 grad = osg->permGrad2[osg->perm[pxm] ^ pym];
|
||||||
|
double extrapolation = grad.dx * dx + grad.dy * dy;
|
||||||
|
|
||||||
|
attn *= attn;
|
||||||
|
value += attn * attn * extrapolation;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 2D Simplex noise, standard lattice orientation.
|
||||||
|
*/
|
||||||
|
double noise2(OpenSimplexEnv *ose, OpenSimplexGradients *osg, double x, double y)
|
||||||
|
{
|
||||||
|
|
||||||
|
// Get points for A2* lattice
|
||||||
|
double s = 0.366025403784439 * (x + y);
|
||||||
|
double xs = x + s, ys = y + s;
|
||||||
|
|
||||||
|
return _noise2_Base(ose, osg, xs, ys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 2D Simplex noise, with Y pointing down the main diagonal.
|
||||||
|
* Might be better for a 2D sandbox style game, where Y is vertical.
|
||||||
|
* Probably slightly less optimal for heightmaps or continent maps.
|
||||||
|
*/
|
||||||
|
double noise2_XBeforeY(OpenSimplexEnv *ose, OpenSimplexGradients *osg, double x, double y)
|
||||||
|
{
|
||||||
|
|
||||||
|
// Skew transform and rotation baked into one.
|
||||||
|
double xx = x * 0.7071067811865476;
|
||||||
|
double yy = y * 1.224744871380249;
|
||||||
|
|
||||||
|
return _noise2_Base(ose, osg, yy + xx, yy - xx);
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenSimplexEnv *initOpenSimplex()
|
||||||
|
{
|
||||||
|
OpenSimplexEnv *ose = (OpenSimplexEnv *)malloc(sizeof(OpenSimplexEnv));
|
||||||
|
ose->GRADIENTS_2D = _newGrad2ConstArray();
|
||||||
|
ose->LOOKUP_2D = _newLatticePoint2DConstArray();
|
||||||
|
return ose;
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenSimplexGradients *newOpenSimplexGradients(OpenSimplexEnv *ose, long seed)
|
||||||
|
{
|
||||||
|
OpenSimplexGradients *osg = (OpenSimplexGradients *)malloc(sizeof(OpenSimplexGradients));
|
||||||
|
osg->perm = _newShortArr(PSIZE);
|
||||||
|
osg->permGrad2 = _newGrad2Arr(PSIZE);
|
||||||
|
short *source = _newShortArr(PSIZE);
|
||||||
|
for (short i = 0; i < PSIZE; i++)
|
||||||
|
{
|
||||||
|
source[i] = i;
|
||||||
|
}
|
||||||
|
for (int i = PSIZE - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
seed = seed * 6364136223846793005L + 1442695040888963407L;
|
||||||
|
int r = (int)((seed + 31) % (i + 1));
|
||||||
|
if (r < 0)
|
||||||
|
{
|
||||||
|
r += (i + 1);
|
||||||
|
}
|
||||||
|
osg->perm[i] = source[r];
|
||||||
|
osg->permGrad2[i] = ose->GRADIENTS_2D[osg->perm[i]];
|
||||||
|
source[r] = source[i];
|
||||||
|
}
|
||||||
|
return osg;
|
||||||
|
}
|
145
src/main.c
145
src/main.c
|
@ -1,117 +1,30 @@
|
||||||
/*******************************************************************************************
|
|
||||||
*
|
|
||||||
* raylib [text] example - Input Box
|
|
||||||
*
|
|
||||||
* This example has been created using raylib 3.5 (www.raylib.com)
|
|
||||||
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2017 Ramon Santamaria (@raysan5)
|
|
||||||
*
|
|
||||||
********************************************************************************************/
|
|
||||||
|
|
||||||
#include "raylib.h"
|
#include "raylib.h"
|
||||||
|
#include "OpenSimplex2F.h"
|
||||||
|
|
||||||
#define MAX_INPUT_CHARS 21
|
#define initialScreenWidth 900
|
||||||
#define DEBUG 1
|
#define initialScreenHeight 900
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
// Initialization
|
// Initialization
|
||||||
//--------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------
|
||||||
const int screenWidth = 800;
|
|
||||||
const int screenHeight = 450;
|
|
||||||
|
|
||||||
const float holdBackspace = 0.5f;
|
SetConfigFlags(FLAG_WINDOW_RESIZABLE);
|
||||||
const float repeatBackspace = 0.05f;
|
InitWindow(initialScreenWidth, initialScreenHeight, "raylib [core] example - basic window");
|
||||||
|
|
||||||
SetConfigFlags(FLAG_VSYNC_HINT);
|
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||||
InitWindow(screenWidth, screenHeight, "raylib [text] example - input box");
|
|
||||||
|
|
||||||
char name[MAX_INPUT_CHARS + 1] = "\0"; // NOTE: One extra space required for null terminator char '\0'
|
OpenSimplexEnv *simplexEnv = initOpenSimplex();
|
||||||
int letterCount = 0;
|
OpenSimplexGradients *simplexGradiants = newOpenSimplexGradients(simplexEnv, 0);
|
||||||
|
|
||||||
Rectangle textBox = {screenWidth / 2.0f - ((MAX_INPUT_CHARS + 1) * 10.0f), 180, 25.0f * MAX_INPUT_CHARS, 50};
|
|
||||||
bool mouseOnText = false;
|
|
||||||
|
|
||||||
int framesCounter = 0;
|
|
||||||
float backspaceTimer = holdBackspace;
|
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
SetTraceLogLevel(LOG_ALL);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// SetTargetFPS(60); // Set our desired framerate.
|
|
||||||
//--------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TraceLog(LOG_INFO, TextFormat("%i", GetScreenWidth()));
|
||||||
|
|
||||||
// Main game loop
|
// Main game loop
|
||||||
while (!WindowShouldClose()) // Detect window close button or ESC key
|
while (!WindowShouldClose()) // Detect window close button or ESC key
|
||||||
{
|
{
|
||||||
// Update
|
// Update
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
if (CheckCollisionPointRec(GetMousePosition(), textBox))
|
|
||||||
mouseOnText = true;
|
|
||||||
else
|
|
||||||
mouseOnText = false;
|
|
||||||
|
|
||||||
if (mouseOnText)
|
|
||||||
{
|
|
||||||
// Set the window's cursor to the I-Beam
|
|
||||||
SetMouseCursor(MOUSE_CURSOR_IBEAM);
|
|
||||||
|
|
||||||
// Get char pressed (unicode character) on the queue
|
|
||||||
int key = GetCharPressed();
|
|
||||||
|
|
||||||
// Check if more characters have been pressed on the same frame
|
|
||||||
while (key > 0)
|
|
||||||
{
|
|
||||||
// NOTE: Only allow keys in range [32..125]
|
|
||||||
if ((key >= 32) && (key <= 125) && (letterCount < MAX_INPUT_CHARS))
|
|
||||||
{
|
|
||||||
name[letterCount] = (char)key;
|
|
||||||
name[letterCount + 1] = '\0'; // Add null terminator at the end of the string.
|
|
||||||
letterCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
key = GetCharPressed(); // Check next character in the queue
|
|
||||||
}
|
|
||||||
|
|
||||||
bool backspace = false;
|
|
||||||
|
|
||||||
if (IsKeyPressed(KEY_BACKSPACE))
|
|
||||||
{
|
|
||||||
backspace = true;
|
|
||||||
backspaceTimer = holdBackspace;
|
|
||||||
}
|
|
||||||
else if (IsKeyDown(KEY_BACKSPACE))
|
|
||||||
{
|
|
||||||
backspaceTimer -= GetFrameTime();
|
|
||||||
if (backspaceTimer <= 0.0f)
|
|
||||||
{
|
|
||||||
backspace = true;
|
|
||||||
backspaceTimer = repeatBackspace;
|
|
||||||
}
|
|
||||||
TraceLog(LOG_INFO, TextFormat("backspaceTimer: %f", backspaceTimer));
|
|
||||||
}
|
|
||||||
else if (IsKeyReleased(KEY_BACKSPACE))
|
|
||||||
{
|
|
||||||
backspaceTimer = holdBackspace;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (backspace)
|
|
||||||
{
|
|
||||||
letterCount--;
|
|
||||||
if (letterCount < 0)
|
|
||||||
letterCount = 0;
|
|
||||||
name[letterCount] = '\0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
SetMouseCursor(MOUSE_CURSOR_DEFAULT);
|
|
||||||
|
|
||||||
if (mouseOnText)
|
|
||||||
framesCounter++;
|
|
||||||
else
|
|
||||||
framesCounter = 0;
|
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
// Draw
|
// Draw
|
||||||
|
@ -120,29 +33,14 @@ int main(void)
|
||||||
|
|
||||||
ClearBackground(RAYWHITE);
|
ClearBackground(RAYWHITE);
|
||||||
|
|
||||||
DrawText("PLACE MOUSE OVER INPUT BOX!", 240, 140, 20, GRAY);
|
for (int x = 0; x < GetScreenWidth(); x++)
|
||||||
|
|
||||||
DrawRectangleRec(textBox, LIGHTGRAY);
|
|
||||||
if (mouseOnText)
|
|
||||||
DrawRectangleLines((int)textBox.x, (int)textBox.y, (int)textBox.width, (int)textBox.height, RED);
|
|
||||||
else
|
|
||||||
DrawRectangleLines((int)textBox.x, (int)textBox.y, (int)textBox.width, (int)textBox.height, DARKGRAY);
|
|
||||||
|
|
||||||
DrawText(name, (int)textBox.x + 5, (int)textBox.y + 8, 40, MAROON);
|
|
||||||
|
|
||||||
const char charCounter[sizeof("INPUT CHARS: /") + 6] = TextFormat("INPUT CHARS: %i/%i", letterCount, MAX_INPUT_CHARS);
|
|
||||||
DrawText(charCounter, (screenWidth - MeasureText(charCounter, 20)) / 2, 250, 20, DARKGRAY);
|
|
||||||
|
|
||||||
if (mouseOnText)
|
|
||||||
{
|
{
|
||||||
if (letterCount < MAX_INPUT_CHARS)
|
for (int y = 0; y < GetScreenHeight(); y++)
|
||||||
{
|
{
|
||||||
// Draw blinking underscore char
|
int noise = (noise2(simplexEnv, simplexGradiants, x / 64.0, y / 64.0) + 1.0) / 2 * 255;
|
||||||
if (((framesCounter / 20) % 2) == 0)
|
|
||||||
DrawText("_", (int)textBox.x + 8 + MeasureText(name, 40), (int)textBox.y + 12, 40, MAROON);
|
DrawPixel(x, y, (Color){noise, noise, noise, 255});
|
||||||
}
|
}
|
||||||
else
|
|
||||||
DrawText("Press BACKSPACE to delete chars...", 230, 300, 20, GRAY);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EndDrawing();
|
EndDrawing();
|
||||||
|
@ -156,16 +54,3 @@ int main(void)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if any key is pressed
|
|
||||||
// NOTE: We limit keys check to keys between 32 (KEY_SPACE) and 126
|
|
||||||
bool IsAnyKeyPressed()
|
|
||||||
{
|
|
||||||
bool keyPressed = false;
|
|
||||||
int key = GetKeyPressed();
|
|
||||||
|
|
||||||
if ((key >= 32) && (key <= 126))
|
|
||||||
keyPressed = true;
|
|
||||||
|
|
||||||
return keyPressed;
|
|
||||||
}
|
|
Reference in a new issue