-
Notifications
You must be signed in to change notification settings - Fork 0
/
perlin.c
131 lines (112 loc) · 3.28 KB
/
perlin.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include "perlin.h"
#include "util.h"
#include <stdlib.h>
#include <limits.h>
#include <math.h>
// Borowed (with some rework) from https://gist.github.com/nowl/828013
// Thanks nowl!
/*
This retrieves a hash value from the perlin struct given its index;
but this verify the index (as I've had some problems with that).
*/
static int
get_hash (Perlin * perlin, long long index)
{
index %= PERLIN_HASH_SIZE;
if (index < 0 || index >= PERLIN_HASH_SIZE)
message_abort ("Invalid perlin hash index");
return perlin->hash[index];
}
static int
noise2 (Perlin * perlin, long long x, long long y)
{
int tmp = get_hash (perlin, y);
return get_hash (perlin, tmp + x);
}
static double
lin_inter (double x, double y, double s)
{
return x + s * (y - x);
}
static double
smooth_inter (double x, double y, double s)
{
return lin_inter (x, y, s * s * (3 - 2 * s));
}
static double
noise2d (Perlin * perlin, double x, double y)
{
long long x_int = x;
long long y_int = y;
double x_frac = x - x_int;
double y_frac = y - y_int;
int s = noise2 (perlin, x_int, y_int);
int t = noise2 (perlin, x_int + 1, y_int);
int u = noise2 (perlin, x_int, y_int + 1);
int v = noise2 (perlin, x_int + 1, y_int + 1);
double low = smooth_inter (s, t, x_frac);
double high = smooth_inter (u, v, x_frac);
return smooth_inter (low, high, y_frac);
}
/*
Creates a perlin structure. The hash data is randly generated based on
the seed given (so the same seed will always produce the same data).
*/
Perlin
make_perlin (double freq, int depth, int seed)
{
Perlin perlin = { 0 };
perlin.freq = freq;
perlin.depth = depth;
perlin.origin_offset = SHRT_MAX;
srand (seed);
// rand() is crap, but who cares, this is just a wee game.
for (int i = 0; i < PERLIN_HASH_SIZE; ++i)
perlin.hash[i] = rand () & 0xFF;
return perlin;
}
/*
Reads a value from the perlin noise at the co-ordinates given.
This tolerates negative co-ordinates, but just mirrors the nosie
at the origin. To make this less obvious, you can have an origin-offset
that effectively moves this origin out of the way.
*/
double
perlin2d (Perlin * perlin, double x, double y)
{
double xa = fabs (x + perlin->origin_offset) * perlin->freq;
double ya = fabs (y + perlin->origin_offset) * perlin->freq;
double amp = 1.0;
double fin = 0;
double div = 0.0;
int i;
for (i = 0; i < perlin->depth; i++)
{
div += 256 * amp;
fin += noise2d (perlin, xa, ya) * amp;
amp /= 2;
xa *= 2;
ya *= 2;
}
return fin / div;
}
/* Writes the perlin structure out to a text file. */
void
write_perlin (Perlin * perlin, FILE * stream)
{
named_writef (stream, "freq", "%lf", perlin->freq);
named_writef (stream, "depth", "%d", perlin->depth);
named_writef (stream, "origin_offset", "%lf", perlin->origin_offset);
write_bytes ("hash", perlin->hash, PERLIN_HASH_SIZE, stream);
}
/* Reads the perlin structure in from a text file. */
Perlin
read_perlin (FILE * stream)
{
Perlin perlin = { 0 };
readf (stream, "freq", "%lf", &perlin.freq);
readf (stream, "depth", "%d", &perlin.depth);
readf (stream, "origin_offset", "%lf", &perlin.origin_offset);
read_bytes ("hash", perlin.hash, PERLIN_HASH_SIZE, stream);
return perlin;
}