worldgen: add Perlin noise test

This commit is contained in:
Lephenixnoir 2022-07-17 01:22:21 +01:00
parent fa68e83260
commit 252ff19f2a
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
3 changed files with 162 additions and 6 deletions

View File

@ -21,7 +21,7 @@ fxconv_declare_converters(converters.py)
add_executable(addin ${SOURCES} ${ASSETS})
target_compile_options(addin PRIVATE -Wall -Wextra -Wno-narrowing -Os -std=c++20)
target_link_libraries(addin LibProf::LibProf Gint::Gint -lsupc++)
target_link_libraries(addin LibProf::LibProf Gint::Gint -lnum -lsupc++)
if("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G)
message(FATAL_ERROR "This game is not supported on fx-9860G!")

View File

@ -1,3 +1,4 @@
#include <num/num.h>
#include "render.h"
#include "world.h"
#include "graphics.h"
@ -33,16 +34,115 @@ static void renderGame(Camera *camera)
renderText(2, hudY+3, "1 2 3 4 5 6 7 8 9");
}
// --== Perlin noise ==--
#include <math.h>
using namespace libnum;
//using num = float;
struct vec2 {
num x, y;
};
static vec2 perlinGradients[16];
GCONSTRUCTOR
static void precomputeGradients(void)
{
for(int i = 0; i < 16; i++) {
float alpha = 2 * 3.14159 * i / 16;
perlinGradients[i].x = num(cosf(alpha));
perlinGradients[i].y = num(sinf(alpha));
}
}
static num smooth(num t)
{
return t * t * t * (t * (6 * t - num(15)) + num(10));
}
static num lerp(num x, num y, num t)
{
return x + t * (y - x);
}
static num dot(vec2 *grid, int GRID_N, int ix, int iy, num fx, num fy)
{
vec2 g = grid[iy * GRID_N + ix];
return (fx - num(ix)) * g.x + (fy - num(iy)) * g.y;
}
static uint8_t *perlinNoise(int N, int PERLIN_CELL_SIZE)
{
uint8_t *noise = new uint8_t[N * N];
if(!noise)
return nullptr;
int GRID_N = (N / PERLIN_CELL_SIZE) + 1;
vec2 *grid = new vec2[GRID_N * GRID_N];
if(!grid) {
delete[] noise;
return nullptr;
}
for(int i = 0; i < GRID_N * GRID_N; i++)
grid[i] = perlinGradients[rand() % 16];
for(int y = 0; y < N; y++)
for(int x = 0; x < N; x++) {
num fx = num(x) / PERLIN_CELL_SIZE;
num fy = num(y) / PERLIN_CELL_SIZE;
int ix = (int)fx;
int iy = (int)fy;
num d0 = dot(grid, GRID_N, ix, iy, fx, fy);
num d1 = dot(grid, GRID_N, ix+1, iy, fx, fy);
num d2 = dot(grid, GRID_N, ix, iy+1, fx, fy);
num d3 = dot(grid, GRID_N, ix+1, iy+1, fx, fy);
num rx = smooth(fx - num(ix));
num ry = smooth(fy - num(iy));
num v = lerp(lerp(d0, d1, rx), lerp(d2, d3, rx), ry);
noise[y * N + x] = 128 + (int)(v * num(127));
}
delete[] grid;
return noise;
}
#include <gint/display.h>
void perlinTest(void)
{
uint8_t *noise;
int time = prof_exec({ noise = perlinNoise(128, 16); });
dclear(C_RED);
if(noise)
for(int y = 0; y < 128; y++)
for(int x = 0; x < 128; x++) {
int gray = noise[y * 128 + x] >> 3;
dpixel(x, y, C_RGB(gray, gray, gray));
}
dprint(2, DHEIGHT-20, C_WHITE, "time = %d ms", time / 1000);
dupdate();
getkey();
}
// -
int main(void)
{
prof_init();
srand(0xc0ffee);
perlinTest();
World *world = Nooncraft::mkWorld(128, 128);
if(!world)
return 0;
srand(0xc0ffee);
renderClear();
renderText(0, 0, "GENERATING WORLD...");
renderUpdate();

View File

@ -2,12 +2,13 @@
from PIL import Image
import random
import math
WIDTH = 128
HEIGHT = 128
BIOMES = 16
PERLIN_CELL_SIZE = 8
PERLIN_CELL_SIZE = 32
# Voronoi diagram by jump flooding
def voronoi(seeds):
@ -38,7 +39,7 @@ def voronoi(seeds):
return world
# Generate a biome center map but avoid the Voronoi algorithm
# --== Generate a biome center map with the jump-flood algorithm ==--
def random_color():
r = random.randint(0, 256)
@ -64,4 +65,59 @@ def show_biomes(biomes):
img.show()
show_biomes(biomes)
# show_biomes(biomes)
# --== Generate some Perlin noise ==--
pregradients = []
for i in range(16):
alpha = 2 * math.pi * i / 16
pregradients.append((math.cos(alpha), math.sin(alpha)))
def random_grid(N):
N = WIDTH // PERLIN_CELL_SIZE + 1
return [[random.choice(pregradients) for x in range(N)] for y in range(N)]
def smooth(t):
return t * t * t * (t * (6 * t - 15) + 10)
def interp(x, y, t):
return y * t + x * (1.0 - t)
def dot(grid, ix, iy, fx, fy):
gx, gy = grid[iy][ix]
return (fx - ix) * gx + (fy - iy) * gy
def perlin_at(grid, fx, fy):
ix = int(fx)
iy = int(fy)
d0 = dot(grid, ix, iy, fx, fy)
d1 = dot(grid, ix+1, iy, fx, fy)
d2 = dot(grid, ix, iy+1, fx, fy)
d3 = dot(grid, ix+1, iy+1, fx, fy)
rx = smooth(fx - ix)
ry = smooth(fy - iy)
return interp(interp(d0, d1, rx), interp(d2, d3, rx), ry)
PERLIN_N = WIDTH // PERLIN_CELL_SIZE
grid = random_grid(PERLIN_N)
def show_perlin(grid):
img = Image.new("RGBA", (WIDTH, HEIGHT))
px = img.load()
for y in range(HEIGHT):
for x in range(WIDTH):
fx, fy = x / PERLIN_CELL_SIZE, y / PERLIN_CELL_SIZE
noise = perlin_at(grid, fx, fy)
gray = 128 + round(noise * 128)
px[x, y] = (gray, gray, gray)
img.show()
show_perlin(grid)