summaryrefslogtreecommitdiff
path: root/src/libs/din/din.c
blob: aa71a2f8e0f9702de5bcbc820abc008dadb5c8d7 (plain)
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/* implements a classical perlin noise function */
/* https://en.wikipedia.org/wiki/Perlin_noise */

#include <assert.h>
#include <stdlib.h>

#include "din.h"
#include "v3f.h"

typedef struct din_t {
	int		width, height, depth;
	unsigned	seed;
	int		W_x_H;
	v3f_t		grid[];
} din_t;


/* return random number between -1 and +1 */
static inline float randf(unsigned *seed)
{
	return 2.f / ((float)RAND_MAX) * rand_r(seed) - 1.f;
}


void din_randomize(din_t *din)
{
	int	x, y, z;

	for (z = 0; z < din->depth; z++) {
		for (y = 0; y < din->height; y++) {
			for (x = 0; x < din->width; x++) {
				v3f_t	r;

				r.x = randf(&din->seed);
				r.y = randf(&din->seed);
				r.z = randf(&din->seed);

				din->grid[z * din->W_x_H + y * din->width + x] = v3f_normalize(&r);
			}
		}
	}
}


din_t * din_new(int width, int height, int depth, unsigned seed)
{
	din_t	*din;

	assert(width > 1);
	assert(height > 1);
	assert(depth > 1);

	din = calloc(1, sizeof(din_t) + (sizeof(v3f_t) * (width * height * depth)));
	if (!din)
		return NULL;

	din->width = width;
	din->height = height;
	din->depth = depth;
	din->seed = seed;

	/* premultiply this since we do it a lot in addressing din->grid[] */
	din->W_x_H = width * height;

	din_randomize(din);

	return din;
}


void din_free(din_t *din)
{
	free(din);
}


static inline float dotgradient(const din_t *din, int x, int y, int z, const v3f_t *coordinate)
{
	v3f_t	distance = v3f_sub(coordinate, &(v3f_t){.x = x, .y = y, .z = z});

	return v3f_dot(&din->grid[z * din->W_x_H + y * din->width + x], &distance);
}


static inline float lerp(float a, float b, float t)
{
	return (1.0f - t) * a + t * b;
}


static inline float smootherstep(float x) {
	return x * x * x * (x * (x * 6.f - 15.f) + 10.f);
}


/* coordinate is in a unit cube of -1...+1 */
float din(din_t *din, v3f_t *coordinate)
{
	int	x0, y0, z0, x1, y1, z1;
	float	i1, i2, ii1, ii2;
	float	tx, ty, tz;
	float	n0, n1;

#if 0
	assert(din);
	assert(coordinate);
	assert(coordinate->x >= -1.f && coordinate->x <= 1.f);
	assert(coordinate->y >= -1.f && coordinate->y <= 1.f);
	assert(coordinate->z >= -1.f && coordinate->z <= 1.f);
#endif

	coordinate->x = .5f + (coordinate->x * .5f + .5f) * (float)(din->width - 2);
	coordinate->y = .5f + (coordinate->y * .5f + .5f) * (float)(din->height - 2);
	coordinate->z = .5f + (coordinate->z * .5f + .5f) * (float)(din->depth - 2);

	x0 = floorf(coordinate->x);
	y0 = floorf(coordinate->y);
	z0 = floorf(coordinate->z);

	x1 = x0 + 1;
	y1 = y0 + 1;
	z1 = z0 + 1;

	tx = coordinate->x - (float)x0;
	ty = coordinate->y - (float)y0;
	tz = coordinate->z - (float)z0;

	n0 = dotgradient(din, x0, y0, z0, coordinate);
	n1 = dotgradient(din, x1, y0, z0, coordinate);
	tx = smootherstep(tx);
	i1 = lerp(n0, n1, tx);

	n0 = dotgradient(din, x0, y1, z0, coordinate);
	n1 = dotgradient(din, x1, y1, z0, coordinate);
	i2 = lerp(n0, n1, tx);

	ty = smootherstep(ty);
	ii1 = lerp(i1, i2, ty);

	n0 = dotgradient(din, x0, y0, z1, coordinate);
	n1 = dotgradient(din, x1, y0, z1, coordinate);
	i1 = lerp(n0, n1, tx);

	n0 = dotgradient(din, x0, y1, z1, coordinate);
	n1 = dotgradient(din, x1, y1, z1, coordinate);
	i2 = lerp(n0, n1, tx);

	ii2 = lerp(i1, i2, ty);

	tz = smootherstep(tz);

	return lerp(ii1, ii2, tz) * 1.1547005383792515290182975610039f;
}
© All Rights Reserved