From f0b536ac93b3a9a49dfff8a7637f09b153a3b955 Mon Sep 17 00:00:00 2001
From: Adrian Kummerlaender
Date: Fri, 22 Feb 2019 21:49:07 +0100
Subject: Improvise interactive wall drawing

Internal wall cells need to be disabled to prevent delayed propagation
of the reflected populations.

This is just quickly thrown together - both the visual drawing and the backend's
material handling remain to be improved.
---
 CMakeLists.txt                       |  1 +
 src/buffer/vertex/material_buffer.cc | 51 ++++++++++++++++++++++++
 src/buffer/vertex/material_buffer.h  | 18 +++++++++
 src/glfw/window.cc                   | 11 ++++--
 src/glfw/window.h                    |  2 +-
 src/main.cc                          |  7 +++-
 src/shader/code/collide.glsl         | 75 +++++++++++++++++++++++++-----------
 src/shader/code/stream.glsl          | 15 ++++++--
 src/shader/code/vertex.glsl          | 16 +++++---
 9 files changed, 158 insertions(+), 38 deletions(-)
 create mode 100644 src/buffer/vertex/material_buffer.cc
 create mode 100644 src/buffer/vertex/material_buffer.h

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5b8b141..70f0107 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -17,6 +17,7 @@ add_executable(
 		src/shader/wrap/graphic_shader.cc
 		src/buffer/vertex/lattice_cell_buffer.cc
 		src/buffer/vertex/fluid_cell_buffer.cc
+		src/buffer/vertex/material_buffer.cc
 )
 
 target_link_libraries(
diff --git a/src/buffer/vertex/material_buffer.cc b/src/buffer/vertex/material_buffer.cc
new file mode 100644
index 0000000..82ccea2
--- /dev/null
+++ b/src/buffer/vertex/material_buffer.cc
@@ -0,0 +1,51 @@
+#include "material_buffer.h"
+
+#include <vector>
+
+MaterialBuffer::MaterialBuffer(GLuint nX, GLuint nY):
+	_nX(nX), _nY(nY) {
+	glGenVertexArrays(1, &_array);
+	glGenBuffers(1, &_buffer);
+
+	glBindVertexArray(_array);
+	glBindBuffer(GL_ARRAY_BUFFER, _buffer);
+
+	std::vector<GLint> data(nX*nY, GLint{1});
+
+	for ( int x = 0; x < nX; ++x ) {
+		data[     0*nX + x] = 0;
+		data[(nY-1)*nX + x] = 0;
+	}
+	for ( int y = 0; y < nY; ++y ) {
+		data[y*nX +    0] = 0;
+		data[y*nX + nX-1] = 0;
+	}
+
+	for ( int x = 1; x < nX-1; ++x ) {
+		data[     1*nX + x] = 2;
+		data[(nY-2)*nX + x] = 2;
+	}
+	for ( int y = 1; y < nY-1; ++y ) {
+		data[y*nX +    1] = 2;
+		data[y*nX + nX-2] = 2;
+	}
+
+	glBufferData(
+		GL_ARRAY_BUFFER,
+		data.size() * sizeof(GLint),
+		data.data(),
+		GL_STATIC_DRAW
+	);
+
+	glEnableVertexAttribArray(0);
+	glVertexAttribPointer(0, 1, GL_INT, GL_FALSE, 0, nullptr);
+}
+
+MaterialBuffer::~MaterialBuffer() {
+	glDeleteBuffers(1, &_buffer);
+	glDeleteVertexArrays(1, &_array);
+}
+
+GLuint MaterialBuffer::getBuffer() const {
+	return _buffer;
+}
diff --git a/src/buffer/vertex/material_buffer.h b/src/buffer/vertex/material_buffer.h
new file mode 100644
index 0000000..eccf008
--- /dev/null
+++ b/src/buffer/vertex/material_buffer.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <GL/glew.h>
+
+class MaterialBuffer {
+private:
+	const GLuint _nX;
+	const GLuint _nY;
+
+	GLuint _array;
+	GLuint _buffer;
+
+public:
+	MaterialBuffer(GLuint nX, GLuint nY);
+	~MaterialBuffer();
+
+	GLuint getBuffer() const;
+};
diff --git a/src/glfw/window.cc b/src/glfw/window.cc
index 98890b7..4f14501 100644
--- a/src/glfw/window.cc
+++ b/src/glfw/window.cc
@@ -37,14 +37,19 @@ int Window::getHeight() const {
 	return _height;
 }
 
-std::tuple<bool,int,int> Window::getMouse() const {
-	const bool clicked = glfwGetMouseButton(_handle, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS;
+std::tuple<int,int,int> Window::getMouse() const {
+	int state = 0;
+	if ( glfwGetMouseButton(_handle, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS ) {
+		state = 1;
+	} else if ( glfwGetMouseButton(_handle, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS ) {
+		state = 2;
+	}
 
 	double x, y;
 	glfwGetCursorPos(_handle, &x, &y);
 
 	return std::make_tuple(
-		clicked,
+		state,
 		x - int(getWidth()/2),
 		int(getHeight()/2 - y)
 	);
diff --git a/src/glfw/window.h b/src/glfw/window.h
index af7ba4d..143d457 100644
--- a/src/glfw/window.h
+++ b/src/glfw/window.h
@@ -27,7 +27,7 @@ public:
 	int getWidth() const;
 	int getHeight() const;
 
-	std::tuple<bool,int,int> getMouse() const;
+	std::tuple<int,int,int> getMouse() const;
 
 	KeyWatcher getKeyWatcher(int key) const;
 
diff --git a/src/main.cc b/src/main.cc
index 3048979..0a400a5 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -10,6 +10,7 @@
 
 #include "buffer/vertex/fluid_cell_buffer.h"
 #include "buffer/vertex/lattice_cell_buffer.h"
+#include "buffer/vertex/material_buffer.h"
 
 #include "shader/wrap/graphic_shader.h"
 #include "shader/wrap/compute_shader.h"
@@ -66,6 +67,7 @@ int renderWindow() {
 	std::unique_ptr<LatticeCellBuffer> lattice_a;
 	std::unique_ptr<LatticeCellBuffer> lattice_b;
 	std::unique_ptr<FluidCellBuffer>   fluid;
+	std::unique_ptr<MaterialBuffer>    geometry;
 
 	std::unique_ptr<ComputeShader> collide_shader;
 	std::unique_ptr<ComputeShader> stream_shader;
@@ -77,6 +79,7 @@ int renderWindow() {
 		lattice_a = std::make_unique<LatticeCellBuffer>(nX, nY);
 		lattice_b = std::make_unique<LatticeCellBuffer>(nX, nY);
 		fluid     = std::make_unique<  FluidCellBuffer>(nX, nY);
+		geometry  = std::make_unique<   MaterialBuffer>(nX, nY);
 
 		collide_shader = std::make_unique<ComputeShader>(COLLIDE_SHADER_CODE);
 		stream_shader  = std::make_unique<ComputeShader>(STREAM_SHADER_CODE);
@@ -94,8 +97,8 @@ int renderWindow() {
 
 	auto pause_key = window.getKeyWatcher(GLFW_KEY_SPACE);
 
-	auto tick_buffers = { lattice_a->getBuffer(), lattice_b->getBuffer(), fluid->getBuffer() };
-	auto tock_buffers = { lattice_b->getBuffer(), lattice_a->getBuffer(), fluid->getBuffer() };
+	auto tick_buffers = { lattice_a->getBuffer(), lattice_b->getBuffer(), fluid->getBuffer(), geometry->getBuffer() };
+	auto tock_buffers = { lattice_b->getBuffer(), lattice_a->getBuffer(), fluid->getBuffer(), geometry->getBuffer() };
 
 	window.render([&](bool window_size_changed) {
 		if ( pause_key.wasClicked() ) {
diff --git a/src/shader/code/collide.glsl b/src/shader/code/collide.glsl
index cb950cb..b58bec3 100644
--- a/src/shader/code/collide.glsl
+++ b/src/shader/code/collide.glsl
@@ -3,9 +3,10 @@ static const std::string COLLIDE_SHADER_CODE = R"(
 
 layout (local_size_x = 1, local_size_y = 1) in;
 
-layout (std430, binding=1) buffer bufferCollide{ float collideCells[]; };
-layout (std430, binding=2) buffer bufferStream{  float streamCells[]; };
-layout (std430, binding=3) buffer bufferFluid{   float fluidCells[]; };
+layout (std430, binding=1) buffer bufferCollide  { float collideCells[];  };
+layout (std430, binding=2) buffer bufferStream   { float streamCells[];   };
+layout (std430, binding=3) buffer bufferFluid    { float fluidCells[];    };
+layout (std430, binding=4) buffer bufferGeometry { int   materialCells[]; };
 
 /// external influence
 
@@ -76,6 +77,22 @@ void setFluid(uint x, uint y, vec2 v, float d) {
 	fluidCells[idx + 2] = d;
 }
 
+int getMaterial(uint x, uint y) {
+	return materialCells[nX*y + x];
+}
+
+void setMaterial(uint x, uint y, int m) {
+	materialCells[nX*y + x] = m;
+}
+
+void disableFluid(uint x, uint y) {
+	const uint idx = indexOfFluidVertex(x, y);
+	fluidCells[idx + 0] = 0.0;
+	fluidCells[idx + 1] = 0.0;
+	fluidCells[idx + 2] = -2.0;
+}
+
+
 /// Moments
 
 float density(uint x, uint y) {
@@ -103,21 +120,19 @@ float equilibrium(float d, vec2 v, int i, int j) {
 /// Determine external influence
 
 float getExternalPressureInflux(uint x, uint y) {
-	if ( mouseState == 1 && norm(vec2(x,y) - mousePos) < 4 ) {
+	if ( mouseState == 1 && norm(vec2(x,y) - mousePos) < 2 ) {
 		return 1.5;
 	} else {
 		return 0.0;
 	}
 }
 
-/// Domain description
-
-bool isEndOfWorld(uint x, uint y) {
-	return x == 0 || x == nX-1 || y == 0 || y == nY-1;
-}
-
-bool isOuterWall(uint x, uint y) {
-	return x == 1 || x == nX-2 || y == 1 || y == nY-2;
+bool getExternalWallRequest(uint x, uint y) {
+	if ( mouseState == 2 && norm(vec2(x,y) - mousePos) < 4 ) {
+		return true;
+	} else {
+		return false;
+	}
 }
 
 /// Actual collide kernel
@@ -130,22 +145,38 @@ void main() {
 		return;
 	}
 
-	if ( isEndOfWorld(x,y) ) {
-		return;
+	if ( getExternalWallRequest(x,y) ) {
+		setMaterial(x,y,2);
+		disableFluid(x,y);
 	}
 
-	if ( isOuterWall(x,y) ) {
-		return;
+	const int material = getMaterial(x,y);
+
+	if ( material == 2 ) {
+		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 ) {
+					++wallNeighbors;
+				}
+			}
+		}
+		if ( wallNeighbors == 9 ) {
+			setMaterial(x,y,0);
+		}
 	}
 
-	const float d = max(getExternalPressureInflux(x,y), density(x,y));
-	const vec2  v = velocity(x,y,d);
+	if ( material == 1 ) { // fluid
+		const float d = max(getExternalPressureInflux(x,y), density(x,y));
+		const vec2  v = velocity(x,y,d);
 
-	setFluid(x,y,v,d);
+		setFluid(x,y,v,d);
 
-	for ( int i = -1; i <= 1; ++i ) {
-		for ( int j = -1; j <= 1; ++j ) {
-			set(x,y,i,j, get(x,y,i,j) + omega * (equilibrium(d,v,i,j) - get(x,y,i,j)));
+		for ( int i = -1; i <= 1; ++i ) {
+			for ( int j = -1; j <= 1; ++j ) {
+				set(x,y,i,j, get(x,y,i,j) + omega * (equilibrium(d,v,i,j) - get(x,y,i,j)));
+			}
 		}
 	}
 }
diff --git a/src/shader/code/stream.glsl b/src/shader/code/stream.glsl
index 345127d..a8aec1d 100644
--- a/src/shader/code/stream.glsl
+++ b/src/shader/code/stream.glsl
@@ -3,8 +3,9 @@ static const std::string STREAM_SHADER_CODE = R"(
 
 layout (local_size_x = 1, local_size_y = 1) in;
 
-layout (std430, binding=1) buffer bufferCollide{ float collideCells[]; };
-layout (std430, binding=2) buffer bufferStream{  float streamCells[]; };
+layout (std430, binding=1) buffer bufferCollide  { float collideCells[];  };
+layout (std430, binding=2) buffer bufferStream   { float streamCells[];   };
+layout (std430, binding=4) buffer bufferGeometry { int   materialCells[]; };
 
 /// LBM constants
 
@@ -33,6 +34,10 @@ void set(uint x, uint y, int i, int j, float v) {
 	streamCells[indexOfLatticeCell(x,y) + indexOfDirection(i,j)] = v;
 }
 
+int getMaterial(uint x, uint y) {
+	return materialCells[nX*y + x];
+}
+
 /// Domain description
 
 bool isEndOfWorld(uint x, uint y) {
@@ -63,11 +68,13 @@ void main() {
 		return;
 	}
 
-	if ( isEndOfWorld(x,y) ) {
+	const int material = getMaterial(x,y);
+
+	if ( material == 0 ) {
 		return;
 	}
 
-	if ( isOuterWall(x,y) ) {
+	if ( material == 2 ) {
 		bounceBack(x,y);
 	}
 
diff --git a/src/shader/code/vertex.glsl b/src/shader/code/vertex.glsl
index 700a9b7..52d9403 100644
--- a/src/shader/code/vertex.glsl
+++ b/src/shader/code/vertex.glsl
@@ -10,7 +10,7 @@ out VS_OUT {
 uniform uint nX;
 uniform uint nY;
 
-const float displayAmplifier = 5.0;
+const float displayAmplifier = 50.0;
 
 float unit(float x) {
 	return 1.0/(1.0+exp(-x));
@@ -42,10 +42,14 @@ void main() {
 		1.
 	);
 
-	vs_out.color = mix(
-		vec3(-0.5, 0.0, 1.0),
-		vec3( 1.0, 0.0, 0.0),
-		displayAmplifier * VertexPosition.z * norm(VertexPosition.xy)
-	);
+	if ( VertexPosition.z < -1.0 ) {
+		vs_out.color = vec3(0.0, 0.0, 0.0);
+	} else {
+		vs_out.color = mix(
+			vec3(-0.5, 0.0, 1.0),
+			vec3( 1.0, 0.0, 0.0),
+			displayAmplifier * VertexPosition.z * norm(VertexPosition.xy)
+		);
+	}
 }
 )";
-- 
cgit v1.2.3