aboutsummaryrefslogtreecommitdiff
path: root/src/shader/code/interact.glsl
blob: 141673a043f3b06cd9493945734abec3c4d2061b (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
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[]; };
layout (std430, binding=4) buffer bufferExtra { float extraCells[]; };

uniform uint nX;
uniform uint nY;

/// External influence

uniform bool wall_requested;
uniform bool fluid_requested;

uniform vec2 start;
uniform vec2 end;

/// 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)));
}

bool isNearLine(uint x, uint y, float eps) {
	if ( start == end ) {
		return norm(vec2(x,y) - end) < eps;
	} else {
		return distanceToLineSegment(start, end, vec2(x,y)) < eps;
	}
}

/// 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;
	extraCells[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);
	}
}

void fixWallExterior(uint x, uint y) {
	for ( int i = -1; i <= 1; ++i ) {
		for ( int j = -1; j <= 1; ++j ) {
			if ( getMaterial(x+i,y+j)== 0 ) {
				setMaterial(x+i,y+j,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 ( isNearLine(x, y, 3) ) {
			if ( wall_requested ) {
				setMaterial(x,y,3);
				return;
			}
		}
	}

	if ( material == 0 || material == 3 ) {
		if ( fluid_requested ) {
			if ( isNearLine(x, y, 3) ) {
					setMaterial(x,y,1);
					fixWallExterior(x,y);
					return;
			}
		}
	}


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