aboutsummaryrefslogtreecommitdiff
path: root/src/shader/code/interact.glsl
blob: a65d96a0e7f77abc2114139ca823e42004c148c4 (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
static const std::string INTERACT_SHADER_CODE = R"(
#version 430

layout (local_size_x = 1, local_size_y = 1) in;

layout (std430, binding=3) buffer bufferFluid { float fluidCells[]; };

uniform uint nX;
uniform uint nY;

/// External influence

uniform int  prevMouseState;
uniform vec2 prevMousePos;
uniform int  currMouseState;
uniform vec2 currMousePos;

/// Vector utilities

float norm(vec2 v) {
	return sqrt(dot(v,v));
}

float distanceToLineSegment(vec2 a, vec2 b, vec2 p) {
	const vec2 ab = b - a;

	const vec2 pa = a - p;
	if ( dot(ab, pa) > 0.0 ) {
		return norm(pa);
	}

	const vec2 bp = p - b;
	if ( dot(ab, bp) > 0.0 ) {
		return norm(bp);
	}

	return norm(pa - ab * (dot(ab, pa) / dot(ab, ab)));
}

/// Array indexing

uint indexOfFluidVertex(uint x, uint y) {
	return 3*nX*y + 3*x;
}

/// Data access

int getMaterial(uint x, uint y) {
	const uint idx = indexOfFluidVertex(x, y);
	return int(fluidCells[idx + 2]);
}

void setMaterial(uint x, uint y, int m) {
	const uint idx = indexOfFluidVertex(x, y);
	fluidCells[idx + 2] = m;
}

/// Geometry cleanup

void disableWallInterior(uint x, uint y) {
	int wallNeighbors = 0;

	for ( int i = -1; i <= 1; ++i ) {
		for ( int j = -1; j <= 1; ++j ) {
			const int material = getMaterial(x+i,y+j);
			if ( material  == 0 || material == 2 || material == 3 ) {
				++wallNeighbors;
			}
		}
	}

	if ( wallNeighbors == 9 ) {
		setMaterial(x,y,0);
	}
}

/// Determine external influence

bool isNearMouse(uint x, uint y, float eps) {
	if ( prevMouseState == currMouseState ) {
		return distanceToLineSegment(prevMousePos, currMousePos, vec2(x,y)) < eps;
	} else {
		return norm(vec2(x,y) - currMousePos) < eps;
	}
}

bool isInfluxRequestedAt(uint x, uint y) {
	return currMouseState == 1 && isNearMouse(x, y, 3);
}

bool isWallRequestedAt(uint x, uint y) {
	return currMouseState == 2 && isNearMouse(x, y, 3);
}

/// Actual interaction kernel

void main() {
	const uint x = gl_GlobalInvocationID.x;
	const uint y = gl_GlobalInvocationID.y;

	if ( !(x < nX && y < nY) ) {
		return;
	}

	const int material = getMaterial(x,y);

	if ( material == 1 ) {
		if ( isInfluxRequestedAt(x,y) ) {
			setMaterial(x,y,4);
			return;
		}
		if ( isWallRequestedAt(x,y) ) {
			setMaterial(x,y,3);
			return;
		}
	}

	if ( material == 3 ) {
		disableWallInterior(x,y);
	}

	if ( material == 4 ) {
		// reset influx material after execution
		setMaterial(x,y,1);
	}
}
)";