From 9163942d9473380a4063f493f47ab4fdb914cb44 Mon Sep 17 00:00:00 2001 From: Tobias Berger Date: Mon, 15 Nov 2021 22:43:44 +0100 Subject: [PATCH] OpenSimplex 2D noise --- include/OpenSimplex2F.h | 27 ++++++ src/OpenSimplex2F.c | 203 ++++++++++++++++++++++++++++++++++++++++ src/main.c | 145 +++------------------------- 3 files changed, 245 insertions(+), 130 deletions(-) create mode 100644 include/OpenSimplex2F.h create mode 100644 src/OpenSimplex2F.c diff --git a/include/OpenSimplex2F.h b/include/OpenSimplex2F.h new file mode 100644 index 0000000..11fc10f --- /dev/null +++ b/include/OpenSimplex2F.h @@ -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); \ No newline at end of file diff --git a/src/OpenSimplex2F.c b/src/OpenSimplex2F.c new file mode 100644 index 0000000..38d60d3 --- /dev/null +++ b/src/OpenSimplex2F.c @@ -0,0 +1,203 @@ +#include +#include +#include +#include +#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; +} \ No newline at end of file diff --git a/src/main.c b/src/main.c index 5e54a60..48fac59 100644 --- a/src/main.c +++ b/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 "OpenSimplex2F.h" -#define MAX_INPUT_CHARS 21 -#define DEBUG 1 +#define initialScreenWidth 900 +#define initialScreenHeight 900 int main(void) { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - const float holdBackspace = 0.5f; - const float repeatBackspace = 0.05f; + SetConfigFlags(FLAG_WINDOW_RESIZABLE); + InitWindow(initialScreenWidth, initialScreenHeight, "raylib [core] example - basic window"); - SetConfigFlags(FLAG_VSYNC_HINT); - InitWindow(screenWidth, screenHeight, "raylib [text] example - input box"); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second - char name[MAX_INPUT_CHARS + 1] = "\0"; // NOTE: One extra space required for null terminator char '\0' - int letterCount = 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. + OpenSimplexEnv *simplexEnv = initOpenSimplex(); + OpenSimplexGradients *simplexGradiants = newOpenSimplexGradients(simplexEnv, 0); //-------------------------------------------------------------------------------------- + TraceLog(LOG_INFO, TextFormat("%i", GetScreenWidth())); + // Main game loop while (!WindowShouldClose()) // Detect window close button or ESC key { // 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 @@ -120,29 +33,14 @@ int main(void) ClearBackground(RAYWHITE); - DrawText("PLACE MOUSE OVER INPUT BOX!", 240, 140, 20, GRAY); - - 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) + for (int x = 0; x < GetScreenWidth(); x++) { - if (letterCount < MAX_INPUT_CHARS) + for (int y = 0; y < GetScreenHeight(); y++) { - // Draw blinking underscore char - if (((framesCounter / 20) % 2) == 0) - DrawText("_", (int)textBox.x + 8 + MeasureText(name, 40), (int)textBox.y + 12, 40, MAROON); + int noise = (noise2(simplexEnv, simplexGradiants, x / 64.0, y / 64.0) + 1.0) / 2 * 255; + + DrawPixel(x, y, (Color){noise, noise, noise, 255}); } - else - DrawText("Press BACKSPACE to delete chars...", 230, 300, 20, GRAY); } EndDrawing(); @@ -155,17 +53,4 @@ int main(void) //-------------------------------------------------------------------------------------- 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; } \ No newline at end of file