Can you port this fluid simulation to roblox. Make sure there are no simplifications to anything in the code i just want it to be ported to lua in roblox studio and somehow visualized.

Gmflux_lib:
enum FluxCell {
	Height,		// Height.rgba
	Depth,		// Depth.rgba
	FluxLD,		// FluxLeft.rg,		FluxDown.ba (This assignment is a little bit on the stupid side)
	FluxRU		// FluxRight.rg,	FluxUp.ba
}

function GMFlux_container(_tsize = 4096) constructor {
/// @func			GMFlux_container([texsize = 4096])
/// @description	constructs a flux simulation container object
	fluxStrength = 0.250;
	fluxAtten = 0.975;
	fluxSpill = 0.0;
	
	texSize = _tsize;
	texSubCount = 2;
	texTotalCount = texSubCount * texSubCount;
	cellSize = texSize div texSubCount; 
	
	fluxSurface = -1;
	pongSurface = -1;
	stitchSurface = -1;
	
	fluxSnapshot = -1;
	pongSnapshot = -1;
	stitchSnapshot = -1;
	
	surfaceSafeTimer = 0;
	snapshotSafeTimer = 0;
	
	cellActive = -1;
	cellActiveX = 0;
	cellActiveY = 0;
	
	static cellGetX = function(_fluxCell)	{	return (_fluxCell mod texSubCount) * cellSize	}
	static cellGetY = function(_fluxCell)	{	return (_fluxCell div texSubCount) * cellSize	}
	static cellGetX2 = function(_fluxCell)	{	return (_fluxCell mod texSubCount) * cellSize + cellSize	}
	static cellGetY2 = function(_fluxCell)	{	return (_fluxCell div texSubCount) * cellSize + cellSize	}
	
	static surfaceEnsure = function() {
	/// @func			surfaceEnsure()
	/// @description	ensure render targets exist
		if (!surface_exists(fluxSurface)) {
			fluxSurface = surface_create(texSize, texSize);
			surfaceSafeTimer = 0;
		}
		if (!surface_exists(pongSurface)) {
			pongSurface = surface_create(texSize, texSize);
			surfaceSafeTimer = 0;
		}
		if (!surface_exists(stitchSurface)) {
			stitchSurface = surface_create(texSize, 8 * texTotalCount);
			surfaceSafeTimer = 0;
		}
	}
	static surfacePing = function() {
	/// @func			surfacePing()
	/// @description	copy to pong
		gpu_push_state();
		gpu_set_blendmode_ext_sepalpha(bm_one, bm_zero, bm_one, bm_zero);
		surface_set_target(pongSurface);
		draw_surface(fluxSurface, 0, 0);
		surface_reset_target();
		gpu_pop_state();
	}
	static surfaceFree = function() {
	/// @func			surfaceFree()
	/// @description	free surfaces
		if (surface_exists(fluxSurface)) {
			surface_free(fluxSurface);
			fluxSurface = -1;
		}
		if (surface_exists(pongSurface)) {
			surface_free(pongSurface);
			pongSurface = -1;
		}
		if (surface_exists(stitchSurface)) {
			surface_free(stitchSurface);
			stitchSurface = -1;
		}
	}
	
	static surfaceSnapshotEnsure = function() {
	/// @func			surfaceEnsure()
	/// @description	ensure ram snapshots exist
		if (fluxSnapshot == -1) {
			fluxSnapshot = buffer_create(texSize * texSize * 4, buffer_fast, 1);
			snapshotSafeTimer = 0;
		}
		if (pongSnapshot == -1) {
			pongSnapshot = buffer_create(texSize * texSize * 4, buffer_fast, 1);
			snapshotSafeTimer = 0;
		}
		if (stitchSnapshot == -1) {
			stitchSnapshot = buffer_create(texSize * texSize * 4, buffer_fast, 1);
			snapshotSafeTimer = 0;
		}
	}
	static surfaceSnapshotWrite = function() {
	/// @func			surfaceSnapshotWrite()
	/// @description	write vram snapshot to ram
		surfaceEnsure();
		surfaceSnapshotEnsure();
		if (surfaceSafeTimer <= 1) return false;
		if (snapshotSafeTimer <= 1) return false;
		buffer_get_surface(fluxSnapshot, fluxSurface, 0);
		buffer_get_surface(pongSnapshot, pongSurface, 0);
		buffer_get_surface(stitchSnapshot, stitchSurface, 0);
		return true;
	}
	static surfaceSnapshotRead = function() {
	/// @func			surfaceSnapshotWrite()
	/// @description	write vram snapshot to ram
		surfaceEnsure();
		surfaceSnapshotEnsure();
		if (surfaceSafeTimer <= 1) return false;
		if (snapshotSafeTimer <= 1) return false;
		buffer_set_surface(fluxSnapshot, fluxSurface, 0);
		buffer_set_surface(pongSnapshot, pongSurface, 0);
		buffer_set_surface(stitchSnapshot, stitchSurface, 0);
		return true;
	}
	static surfaceSnapshotFree = function() {
	/// @func			surfaceSnapshotFree()
	/// @description	free snapshots
		if (fluxSnapshot != -1) {
			fluxSnapshot = buffer_delete(fluxSnapshot);
			snapshotSafeTimer = 0;
		}
		if (pongSnapshot != -1) {
			pongSnapshot = buffer_delete(pongSnapshot);
			snapshotSafeTimer = 0;
		}
		if (stitchSnapshot != -1) {
			stitchSnapshot = buffer_delete(stitchSnapshot);
			snapshotSafeTimer = 0;
		}
	}
	
	static cellEncodeHeightSprite = function(_fluxCell, _spr, _img, _factor) {
	/// @func			cellEncodeHeightSprite(FluxCell, sprite_index, image_index, height_max)
	/// @description	encodes a sprite resource into a cell
		var _x = (_fluxCell mod texSubCount) * cellSize,
			_y = (_fluxCell div texSubCount) * cellSize;
		gpu_push_state();
		gpu_set_blendmode_ext_sepalpha(bm_one, bm_zero, bm_one, bm_zero);
		surface_set_target(fluxSurface);
		var _shader = GMFluxShd_encodeHeightfield;
		shader_set(_shader);
		shader_set_uniform_f(shader_get_uniform(_shader, "u_uHeightFactor"), _factor);
		shader_set_uniform_f(shader_get_uniform(_shader, "u_uTexSize"), sprite_get_width(_spr), sprite_get_height(_spr));
		draw_sprite_stretched(_spr, _img, _x, _y, cellSize, cellSize);
		shader_reset();
		surface_reset_target();
		gpu_pop_state();
	}
	static cellEncodeHeightSurface = function(_fluxCell, _surf, _factor) {
	/// @func			cellEncodeHeightSurface(FluxCell, surface_index, height_max)
	/// @description	encodes a surface into a cell
		var _x = (_fluxCell mod texSubCount) * cellSize,
			_y = (_fluxCell div texSubCount) * cellSize;
		gpu_push_state();
		gpu_set_blendmode_ext_sepalpha(bm_one, bm_zero, bm_one, bm_zero);
		surface_set_target(fluxSurface);
		var _shader = GMFluxShd_encodeHeightfield;
		shader_set(_shader);
		shader_set_uniform_f(shader_get_uniform(_shader, "u_uHeightFactor"), _factor);
		shader_set_uniform_f(shader_get_uniform(_shader, "u_uTexSize"), surface_get_width(_surf), surface_get_height(_surf));
		draw_surface_stretched(_surf, _x, _y, cellSize, cellSize);
		shader_reset();
		surface_reset_target();
		gpu_pop_state();
	}
	static cellEncodeFill = function(_fluxCell, _value) {
	/// @func			cellEncodeFill(FluxCell, value)
	/// @description	fills a cell with a value
		var _x = (_fluxCell mod texSubCount) * cellSize,
			_y = (_fluxCell div texSubCount) * cellSize;
		gpu_push_state();
		gpu_set_blendmode_ext_sepalpha(bm_one, bm_zero, bm_one, bm_zero);
		surface_set_target(fluxSurface);
		var _shader = GMFluxShd_encodeFill;
		shader_set(_shader);
		shader_set_uniform_f(shader_get_uniform(_shader, "u_uValue"), _value);
		draw_sprite_stretched(GMFluxSpr_2x2, 0, _x, _y, cellSize, cellSize);
		shader_reset();
		surface_reset_target();
		gpu_pop_state();
	}
	static cellEncodeFillXY = function(_fluxCell, _valueX, _valueY) {
	/// @func			cellEncodeFill(FluxCell, value_x, value_y)
	/// @description	fills a cell with a 2 component vector value
		var _x = (_fluxCell mod texSubCount) * cellSize,
			_y = (_fluxCell div texSubCount) * cellSize;
		gpu_push_state();
		gpu_set_blendmode_ext_sepalpha(bm_one, bm_zero, bm_one, bm_zero);
		surface_set_target(fluxSurface);
		var _shader = GMFluxShd_encodeFillXY;
		shader_set(_shader);
		shader_set_uniform_f(shader_get_uniform(_shader, "u_uValue"), _valueX, _valueY);
		draw_sprite_stretched(GMFluxSpr_2x2, 0, _x, _y, cellSize, cellSize);
		shader_reset();
		surface_reset_target();
		gpu_pop_state();
	}
	
	static cellAddBegin = function(_fluxCell, _shader = GMFluxShd_additive) {
	/// @func			cellAddBegin(FluxCell, [shader_index = GMFluxShd_additive])
	/// @description	begin writing to a cell
		surfacePing();
		cellActive = _fluxCell;
		cellActiveX = (_fluxCell mod texSubCount) * cellSize;
		cellActiveY = (_fluxCell div texSubCount) * cellSize
		gpu_push_state();
		gpu_set_blendmode_ext_sepalpha(bm_one, bm_zero, bm_one, bm_zero);
		surface_set_target(fluxSurface);
		shader_set(_shader);
		shader_set_uniform_f(shader_get_uniform(_shader, "u_uSize"), texSubCount, cellSize, texSize);
		texture_set_stage(shader_get_sampler_index(_shader, "u_uSampler"), surface_get_texture(pongSurface));
		shader_set_uniform_f(shader_get_uniform(_shader, "u_uStencil"), 0, 0, cellSize, cellSize);
		shader_set_uniform_f(shader_get_uniform(_shader, "u_uStencilOffset"), cellActiveX, cellActiveY);
		shader_set_uniform_f(shader_get_uniform(_shader, "u_uRange"), 0.0, 1.0);
		
		matrix_set(matrix_world, matrix_build(cellActiveX, cellActiveY, 0, 0, 0, 0, 1, 1, 1));
	}
	static cellAddXYBegin = function(_fluxCell, _component) {
	/// @func			cellAddXYBegin(FluxCell, component)
	/// @description	begin writing single component of a vec2 to a cell
		cellAddBegin(_fluxCell, GMFluxShd_additiveXY);
		shader_set_uniform_i(shader_get_uniform(GMFluxShd_additiveXY, "u_uComponent"), _component);
	}
	static cellAddSetRange = function(_min, _max) {
	/// @func			cellAddSetRange(value_min, value_max)
	/// @description	set the range of current cell write operation
		shader_set_uniform_f(shader_get_uniform(shader_current(), "u_uRange"), _min, _max);
	}
	static cellAddEnd = function() {
	/// @func			cellAddEnd()
	/// @description	finish writing to a cell
		cellActive = -1;
		shader_reset();
		surface_reset_target();
		gpu_pop_state();
		matrix_set(matrix_world, matrix_stack_top());
	}
	
	static cellOutputSurface = function(_fluxCell) {
	/// @func			cellOutputSurface(FluxCell)
	/// @description	output cell to a surface
		var _surf = surface_create(cellSize, cellSize);
		var _cx = cellGetX(_fluxCell),
			_cy = cellGetY(_fluxCell);
		gpu_set_blendmode_ext_sepalpha(bm_one, bm_zero, bm_one, bm_zero);
		surface_set_target(_surf);
		draw_surface(fluxSurface, -_cx, -_cy);
		surface_reset_target();
		gpu_set_blendmode(bm_normal);
		return _surf;
	}
	static cellOutputBuffer = function(_fluxCell) {
	/// @func			cellOutputBuffer(FluxCell)
	/// @description	output cell to a buffer
		var _surf = cellOutputSurface(_fluxCell);
		var _buff = buffer_create(cellSize * cellSize * 4, buffer_fast, 1);
		buffer_get_surface(_buff, _surf, 0);
		surface_free(_surf);
		return _buff;
	}
	
	static stepFlux = function() {
	/// @func			stepFlux()
	/// @description	perform a step of flux simulation
		gpu_push_state();
		gpu_set_blendmode_ext_sepalpha(bm_one, bm_zero, bm_one, bm_zero);
		surface_set_target(pongSurface);
		var _shader = GMFluxShd_stepFlux;
		shader_set(_shader);
		shader_set_uniform_f(shader_get_uniform(_shader, "u_uSize"), texSubCount, cellSize, texSize);
		shader_set_uniform_f(shader_get_uniform(_shader, "u_uStrength"), fluxStrength);
		shader_set_uniform_f(shader_get_uniform(_shader, "u_uAttenuation"), fluxAtten);
		shader_set_uniform_f(shader_get_uniform(_shader, "u_uSpill"), fluxSpill);
		draw_surface(fluxSurface, 0, 0);
		shader_reset();
		surface_reset_target();
		var _temp = pongSurface;
		pongSurface = fluxSurface;
		fluxSurface = _temp;
		gpu_pop_state();
	}
	static stepDepth = function() {
	/// @func			stepDepth(FluxCell)
	/// @description	perform a step of depth simulation
		gpu_push_state();
		gpu_set_blendmode_ext_sepalpha(bm_one, bm_zero, bm_one, bm_zero);
		surface_set_target(pongSurface);
		var _shader = GMFluxShd_stepDepth;
		shader_set(_shader);
		shader_set_uniform_f(shader_get_uniform(_shader, "u_uSize"), texSubCount, cellSize, texSize);
		draw_surface(fluxSurface, 0, 0);
		shader_reset();
		surface_reset_target();
		var _temp = pongSurface;
		pongSurface = fluxSurface;
		fluxSurface = _temp;
		gpu_pop_state();
	}
	
	//------------------------------------------------------------//
	// Lookups
	static __lookupVFormatCreate = function() {
		vertex_format_begin();
		vertex_format_add_position_3d();	// lookup_id, none
		return vertex_format_end();
	}
	
	static lookupVFormat = __lookupVFormatCreate();
	lookupBuffer = -1;
	lookupSurface = -1;
	lookupModel = -1;
	lookupSize = 0;
	
	static lookupAlloc = function(_size) {
	/// @func			lookupAlloc(size)
	/// @description	allocate n lookup fragments
		lookupFree();
		var _sa = power(2, ceil(log2(sqrt(_size))) ),
			_sb = power(2, ceil(log2(_size)) );
		lookupSurface = (_sb > 4) ? surface_create(_sa, _sa) : surface_create(_sb, 1);
		lookupBuffer = buffer_create(_size * 4, buffer_fast, 1);
		lookupModel = vertex_create_buffer();
		vertex_begin(lookupModel, lookupVFormat);
		for(var i = 0; i < _size; i ++) {
			vertex_position_3d(lookupModel, i, 0, 0);
		}
		vertex_end(lookupModel);
		vertex_freeze(lookupModel);
		lookupSize = _size;
	}
	static lookupFree = function() {
	/// @func			lookupFree()
	/// @description	free allocated lookup mem
		if surface_exists(lookupSurface) {
			surface_free(lookupSurface);
			lookupSurface = -1;
		}
		if buffer_exists(lookupBuffer) {
			buffer_delete(lookupBuffer);
			lookupBuffer = -1;
		}
		if (lookupModel != -1) {
			vertex_delete_buffer(lookupModel);
			lookupModel = -1;
		}
	}
	static lookupIdFromPosition = function(_x, _y) {
	/// @func			lookupIdFromPosition(x, y)
	/// @description	get lookup id from flux coordinates
		return (_x + _y * cellSize);
	}
	static lookupWrite = function(_cells, _ids, _offset = 0) {
	/// @func			lookupWrite(array_cells, array_ids, [offset = 0])
	/// @description	write fragments to allocated lookup mem
		var _shader = GMFluxShd_lookup;
		gpu_push_state();
		gpu_set_blendmode_ext_sepalpha(bm_one, bm_zero, bm_one, bm_zero);
		gpu_set_tex_filter(false);
		shader_set(_shader);
		surface_set_target(lookupSurface);
		shader_set_uniform_f(shader_get_uniform(_shader, "u_uSize"), texSubCount, cellSize, texSize);
		shader_set_uniform_f(shader_get_uniform(_shader, "u_uSizePass"), surface_get_width(lookupSurface), surface_get_height(lookupSurface));
		shader_set_uniform_f_array(shader_get_uniform(_shader, "u_uListCells"), _cells);
		shader_set_uniform_f_array(shader_get_uniform(_shader, "u_uListIds"), _ids);
		shader_set_uniform_f(shader_get_uniform(_shader, "u_uListOffset"), _offset);
		vertex_submit(lookupModel, pr_pointlist, surface_get_texture(fluxSurface) );
		shader_reset();
		surface_reset_target();
		gpu_pop_state();
	}
	static lookupCopy = function() {
	/// @func			lookupCopy()
	/// @description	copy lookup mem to ram
		buffer_get_surface(lookupBuffer, lookupSurface, 0);
	}
	static lookupReadIndex = function(_index) {
	/// @func			lookupReadIndex(index)
	/// @description	output RGBA array of a frag at index from lookup mem
		var _r, _g, _b, _a;
		buffer_seek(lookupBuffer, buffer_seek_start, _index * 4);
		_r = buffer_read(lookupBuffer, buffer_u8);
		_g = buffer_read(lookupBuffer, buffer_u8);
		_b = buffer_read(lookupBuffer, buffer_u8);
		_a = buffer_read(lookupBuffer, buffer_u8);
		return [_r, _g, _b, _a];
	}
	
	//------------------------------------------------------------//
	// Encoding
	static encodeBound = 256;
	
	static encodeRGBA = function(_f, _bound = encodeBound) {
	/// @func encodeRGBA(float, [bound])
		var _half = 8388607,
			_val = floor(_f) + _half + 256;
		var _a = 255-round(frac(abs(_f)) * 255),
			_b = _val div 65536,
			_g = floor(_val - _b * 65536) div 256,
			_r = floor(_val) % 256;
		return [_r, _g, _b, _a];
	}
	static decodeRGBA = function(_r, _g, _b, _a, _bound = encodeBound) {
	/// @func decodeRGBA(r, g, b, a, [bound])
		var _val = _r + _g * 256 + _b * 65536,
			_half = 8388607,
			_int = (_val - _half - 256),
			_frac = _int < 0 ? (_a / 255) : 1 - (_a / 255);
		return _int + (_frac % 1.0);
	}
	static encodeXY = function(_f, _bound = encodeBound) {
	/// @func encodeXY(float, [bound])
		var _half = 32767,
			_val = _f * _bound + _half;
		var _y = _val div 256,
			_x = floor(_val) % 256;
		return [_x, _y];
	}
	static decodeXY = function(_x, _y, _bound = encodeBound) {
	/// @func decodeXY(x, y, [bound])
		var _val = _x + _y * 256,
			_half = 32767;
		return (_val - _half - 256.) / _bound;
	}
	
	//------------------------------------------------------------//
	// Destructor
	static free = function() {
	/// @func			free()
	/// @description	free everything
		surfaceFree();
		surfaceSnapshotFree();
		lookupFree();
	}
}

encodeheightfield.fsh:	
varying vec2 v_vTexcoord;
varying vec4 v_vColour;

uniform float u_uHeightFactor;
uniform vec2 u_uTexSize;

#define BDEF 256.0

vec2 px = vec2(1.) / u_uTexSize;

vec4 encodeRGBA(in float _f, float _bound) {
	float _half = 8388607.;
	float _val = floor(_f + 256.) + _half;
	vec4 _rgba = vec4(vec3(0.), 255.-floor(fract(abs(_f)) * 255. + .5) );
	_rgba.b = floor(_val / 65536.);
	_rgba.g = floor((_val - _rgba.b * 65536.) / 256.);
	_rgba.r = mod(_val, 256.);
	return _rgba / 255.;
}
float diffuse(sampler2D _tex, vec2 _uvs) {
	float _total = 0.0;
	_total += texture2D(_tex, _uvs + (px.x, 0.) ).r;
	_total += texture2D(_tex, _uvs - (px.x, 0.) ).r;
	_total += texture2D(_tex, _uvs + (0., px.y) ).r;
	_total += texture2D(_tex, _uvs - (0., px.y) ).r;
	float _my = texture2D(_tex, _uvs).r;
	return mix(_my, _total / 4.0, 0.5);
}

void main() {
    float height = diffuse(gm_BaseTexture, v_vTexcoord);
	
	gl_FragColor = encodeRGBA(height * u_uHeightFactor, BDEF);
}

Stepdepth.fsh:
varying vec2 v_vTexcoord;
varying vec4 v_vColour;

uniform vec3 u_uSize;	// (cellCount, cellSize, texSize)

#define BDEF 256.0

#define SLOT_HEIGHT		0.0
#define SLOT_DEPTH		1.0
#define SLOT_FLUX_LD	2.0
#define SLOT_FLUX_RU	3.0

precision highp float;

vec2 px = vec2(1.0) / u_uSize.z;

vec4 encodeRGBA(in float _f, float _bound) {
	float _half = 8388607.;
	float _val = floor(_f + 256.) + _half;
	vec4 _rgba = vec4(vec3(0.), 255.-floor(fract(abs(_f)) * 255. + .5) );
	_rgba.b = floor(_val / 65536.);
	_rgba.g = floor((_val - _rgba.b * 65536.) / 256.);
	_rgba.r = mod(_val, 256.);
	return _rgba / 255.;
}
float decodeRGBA(in vec4 _rgba, in float _bound) {
	vec4 _v = _rgba * 255.;
	float _half = 8388607.;
	float _val = _v.r + _v.g * 256. + _v.b * 65536.;
	float _int = (_val - _half - 256.);
	float _frac = (_int < 0.) ? (_v.a / 255.) : 1. - (_v.a / 255.);
	return _int + mod(_frac, 1.0);
}
vec2 encodeXY(in float _f, in float _bound) {
	float _half = 32767.;
	float _val = _f * _bound + _half + 256.;
	return vec2(mod(floor(_val), 256.), floor(_val / 256.)) / 255.;
}
float decodeXY(in vec2 _xy, in float _bound) {
	vec2 _v = _xy * 255.;
	float _val = _v.x + _v.y * 256.;
	float _half = 32767.;
	return (_val - _half - 256.) / _bound;
}

float nodeSlot(vec2 _pos) {
	return floor(_pos.x / u_uSize.y) + floor(_pos.y / u_uSize.y) * u_uSize.x;
}
vec2 positionSlot(float _slot) {
	return vec2(mod(_slot, u_uSize.x) * u_uSize.y, floor(_slot / u_uSize.x) * u_uSize.y);
}
vec4 lookupSlot(in vec2 _uv, in float _slot) {
	vec2 _pos = _uv + positionSlot(_slot) / u_uSize.z;
	return texture2D(gm_BaseTexture, _pos);
}
vec2 offsetSlot(in vec2 _uv) {
	vec2 _bounds = vec2(u_uSize.y / u_uSize.z);// - px;
	float _x;
	float _y;
	_x = ( (_uv.x < 0.0) ? - _uv.x : ( (_uv.x > _bounds.x) ? _uv.x - _bounds.x : 0.0) );
	_y = ( (_uv.y < 0.0) ? - _uv.y : ( (_uv.y > _bounds.y) ? _uv.y - _bounds.y : 0.0) );
	return vec2(_x, _y);
}

float readHeight(in vec2 _uv) {
	_uv = clamp(_uv, 0.0, u_uSize.y / u_uSize.z);
	return max(decodeRGBA(lookupSlot(_uv, 0.0), BDEF), 0.0);
}
float readDepth(in vec2 _uv) {
	_uv = clamp(_uv, 0.0, u_uSize.y / u_uSize.z);
	return max(decodeRGBA(lookupSlot(_uv, 1.0), BDEF), 0.0);
}
vec4 readFlux(in vec2 _uv) {
	//if (!inboundSlot(_uv)) return vec4(0.0);
	vec2 _offset = offsetSlot(_uv);
	if (length(_offset) > 0.0) return vec4(0.0);
	vec4 _flux;
	vec4 _embed;
	_embed = lookupSlot(_uv, 2.0);
	_flux.xy = vec2(decodeXY(_embed.xy, BDEF), decodeXY(_embed.zw, BDEF));
	_embed = lookupSlot(_uv, 3.0);
	_flux.zw = vec2(decodeXY(_embed.xy, BDEF), decodeXY(_embed.zw, BDEF));
	// protect from overflux during separate passes
	float _wdepth = decodeRGBA(lookupSlot(_uv, 1.0), BDEF);
	float _ftotal = _flux.x + _flux.y + _flux.z + _flux.w;
	return _flux * clamp(_wdepth / _ftotal, 0.0, 1.0);
	return _flux;
}

void main() {
	vec2 texpos = floor(v_vTexcoord * u_uSize.z);
	float slot = nodeSlot(texpos);
	vec2 uvs = v_vTexcoord - positionSlot(slot)/u_uSize.z;
	
	vec4 pack = lookupSlot(uvs, slot);
	
	// update depth
	if (slot == SLOT_DEPTH) {
		float depth = readDepth(uvs);
		vec4 myflux = readFlux(uvs);
		float myfluxtotal = myflux.x + myflux.y + myflux.z + myflux.w;
		float influx = 0.0;
		influx += readFlux(uvs + vec2(px.x, 0.0)).x;
		influx += readFlux(uvs + vec2(0.0, px.y)).w;
		influx += readFlux(uvs + vec2(-px.x, 0.0)).z;
		influx += readFlux(uvs + vec2(0.0, -px.y)).y;
		depth = depth - myfluxtotal + influx;
		pack = encodeRGBA(max(depth, 0.0), BDEF);
	}
	
    gl_FragColor = pack;
}

Stepflux.fsh:
varying vec2 v_vTexcoord;
varying vec4 v_vColour;

uniform vec3 u_uSize;	// (cellCount, cellSize, texSize)

uniform float u_uStrength;
uniform float u_uAttenuation;
uniform float u_uSpill;

#define BDEF 256.0

#define SLOT_HEIGHT		0.0
#define SLOT_DEPTH		1.0
#define SLOT_FLUX_LD	2.0
#define SLOT_FLUX_RU	3.0

precision highp float;

vec2 px = vec2(1.0) / u_uSize.z;

float hash(vec2 co) {
	return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}

vec4 encodeRGBA(in float _f, float _bound) {
	float _half = 8388607.;
	float _val = floor(_f + 256.) + _half;
	vec4 _rgba = vec4(vec3(0.), 255.-floor(fract(abs(_f)) * 255. + .5) );
	_rgba.b = floor(_val / 65536.);
	_rgba.g = floor((_val - _rgba.b * 65536.) / 256.);
	_rgba.r = mod(_val, 256.);
	return _rgba / 255.;
}
float decodeRGBA(in vec4 _rgba, in float _bound) {
	vec4 _v = _rgba * 255.;
	float _half = 8388607.;
	float _val = _v.r + _v.g * 256. + _v.b * 65536.;
	float _int = (_val - _half - 256.);
	float _frac = (_int < 0.) ? (_v.a / 255.) : 1. - (_v.a / 255.);
	return _int + mod(_frac, 1.0);
}
vec2 encodeXY(in float _f, in float _bound) {
	float _half = 32767.;
	float _val = _f * _bound + _half + 256.;
	return vec2(mod(floor(_val), 256.), floor(_val / 256.)) / 255.;
}
float decodeXY(in vec2 _xy, in float _bound) {
	vec2 _v = _xy * 255.;
	float _val = _v.x + _v.y * 256.;
	float _half = 32767.;
	return (_val - _half - 256.) / _bound;
}

float nodeSlot(vec2 _pos) {
	return floor(_pos.x / u_uSize.y) + floor(_pos.y / u_uSize.y) * u_uSize.x;
}
vec2 positionSlot(float _slot) {
	return vec2(mod(_slot, u_uSize.x) * u_uSize.y, floor(_slot / u_uSize.x) * u_uSize.y);
}
vec4 lookupSlot(in vec2 _uv, in float _slot) {
	vec2 _pos = _uv + positionSlot(_slot) / u_uSize.z;
	return texture2D(gm_BaseTexture, _pos);
}
vec2 offsetSlot(in vec2 _uv) {
	vec2 _bounds = vec2(u_uSize.y / u_uSize.z);// - px;
	float _x;
	float _y;
	_x = ( (_uv.x < 0.0) ? - _uv.x : ( (_uv.x > _bounds.x) ? _uv.x - _bounds.x : 0.0) );
	_y = ( (_uv.y < 0.0) ? - _uv.y : ( (_uv.y > _bounds.y) ? _uv.y - _bounds.y : 0.0) );
	return vec2(_x, _y);
}

float readHeight(in vec2 _uv) {
	_uv = clamp(_uv, 0.0, u_uSize.y / u_uSize.z);
	return max(decodeRGBA(lookupSlot(_uv, 0.0), BDEF), 0.0);
}
float readDepth(in vec2 _uv) {
	_uv = clamp(_uv, 0.0, u_uSize.y / u_uSize.z);
	return max(decodeRGBA(lookupSlot(_uv, 1.0), BDEF), 0.0);
}
vec4 readFlux(in vec2 _uv) {
	vec2 _offset = offsetSlot(_uv);
	if (length(_offset) > 0.0) return vec4(0.0);
	vec4 _flux;
	vec4 _embed;
	_embed = lookupSlot(_uv, 2.0);
	_flux.xy = vec2(decodeXY(_embed.xy, BDEF), decodeXY(_embed.zw, BDEF));
	_embed = lookupSlot(_uv, 3.0);
	_flux.zw = vec2(decodeXY(_embed.xy, BDEF), decodeXY(_embed.zw, BDEF));
	// protect from overflux during separate passes
	float _wdepth = decodeRGBA(lookupSlot(_uv, 1.0), BDEF);
	float _ftotal = _flux.x + _flux.y + _flux.z + _flux.w;
	return _flux * clamp(_wdepth / _ftotal, 0.0, 1.0);
}
float computeFluxSingle(vec2 _heightdepth, vec2 _uv) {
	float _dheight = readHeight(_uv);
	float _ddepth = readDepth(_uv);
	return max(0.0, (_heightdepth.x + _heightdepth.y) - (_dheight + _ddepth));
}

void main() {
	vec2 texpos = floor(v_vTexcoord * u_uSize.z);
	float slot = nodeSlot(texpos);
	vec2 uvs = v_vTexcoord - positionSlot(slot)/u_uSize.z;
	
	vec4 pack = lookupSlot(uvs, slot);
	
	// update flux (left, down)
	if (slot == SLOT_FLUX_LD) {
		float height = readHeight(uvs);
		float depth = readDepth(uvs);
		vec2 heightdepth = vec2(height, depth);
		vec4 myflux = readFlux(uvs);
		vec4 outflux;
		outflux.z = computeFluxSingle(heightdepth, uvs + vec2(px.x, 0.0));
		outflux.w = computeFluxSingle(heightdepth, uvs + vec2(0.0, -px.y));
		outflux.x = computeFluxSingle(heightdepth, uvs + vec2(-px.x, 0.0));
		outflux.y = computeFluxSingle(heightdepth, uvs + vec2(0.0, px.y));
		outflux = u_uAttenuation * myflux + u_uStrength * outflux;
		float outfluxtotal = outflux.x + outflux.y + outflux.z + outflux.w;
		outflux = ( outfluxtotal <= u_uSpill ? vec4(0.0) : ( depth < outfluxtotal ? outflux * (depth / outfluxtotal) : outflux) );
		pack.xy = encodeXY(outflux.x, BDEF);
		pack.zw = encodeXY(outflux.y, BDEF);
	}
	// update flux (right, up)
	else if (slot == SLOT_FLUX_RU) {
		float height = readHeight(uvs);
		float depth = readDepth(uvs);
		vec2 heightdepth = vec2(height, depth);
		vec4 myflux = readFlux(uvs);
		vec4 outflux;
		outflux.z = computeFluxSingle(heightdepth, uvs + vec2(px.x, 0.0));
		outflux.w = computeFluxSingle(heightdepth, uvs + vec2(0.0, -px.y));
		outflux.x = computeFluxSingle(heightdepth, uvs + vec2(-px.x, 0.0));
		outflux.y = computeFluxSingle(heightdepth, uvs + vec2(0.0, px.y));
		outflux = u_uAttenuation * myflux + u_uStrength * outflux;
		float outfluxtotal = outflux.x + outflux.y + outflux.z + outflux.w;
		outflux = ( outfluxtotal <= u_uSpill ? vec4(0.0) : ( depth < outfluxtotal ? outflux * (depth / outfluxtotal) : outflux) );
		pack.xy = encodeXY(outflux.z, BDEF);
		pack.zw = encodeXY(outflux.w, BDEF);
	}
	
    gl_FragColor = pack;
}

Advect.fsh:
varying vec2 v_vTexcoord;
varying vec4 v_vColour;

uniform sampler2D u_uSampler;
uniform float u_uFragTime;

#define BDEF 256.0

float decodeXY(in vec2 _xy, in float _bound) {
	vec2 _v = _xy * 255.;
	float _val = _v.x + _v.y * 256.;
	float _half = 32767.;
	return (_val - _half - 256.) / _bound;
}
float rand(vec2 co) {
	return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}

void main() {
	// get flux
	vec4 flux;
    vec4 sample = texture2D(u_uSampler, v_vTexcoord * 0.5 + vec2(0.0, 0.5) );
	flux.x = decodeXY(sample.xy, BDEF);	// left
	flux.y = decodeXY(sample.zw, BDEF);	// down
	sample = texture2D(u_uSampler, v_vTexcoord * 0.5 + vec2(0.5, 0.5) );
	flux.z = decodeXY(sample.xy, BDEF);	// right
	flux.w = decodeXY(sample.zw, BDEF);	// up
	vec2 flow = (flux.zy - flux.xw) * 0.01;
	
	// tiny rotator offset
	vec2 frot = vec2(sin(length(flow)), cos(length(flow))) * 0.19 * rand(v_vTexcoord * u_uFragTime * vec2(451.23, 94.526));
	// velocity
	float veloc = (0.25 + rand(u_uFragTime * vec2(160.81, 380.34) * v_vTexcoord) * 1.25 ) * 0.001 * 0.6;
	sample = texture2D(gm_BaseTexture, v_vTexcoord - (flow - frot) * veloc);
	
	// mandatory falloff
	float foam = sample.a - 1.0 / 255.0;
	
	// flow falloff
	foam -= (flux.x + flux.y + flux.z + flux.w) / 4500.0;
	
	gl_FragColor = vec4(sample.rgb, foam);
}

Waterlevel.fsh:
varying vec2 v_vTexcoord;
varying vec3 v_vWorldPos;
varying float v_vWaterLevel;
varying vec3 v_vNormal;
varying vec2 v_vScreenpos;
varying float v_vDepth;
varying vec3 v_vLookat;
varying float v_vOOB;

uniform vec4 u_uWaterColBegin;
uniform vec4 u_uWaterColEnd;
uniform vec3 u_uReflectionRange;	// (start_alpha, end_alpha, z_falloff)

uniform float u_uWaterColFalloff;

uniform float u_uDepthFalloff;

uniform vec3 u_uSunray;

float decodeRGBA(in vec4 _rgba, in float _bound) {
	vec4 _v = _rgba * 255.;
	float _half = 8388607.;
	float _val = _v.r + _v.g * 256. + _v.b * 65536.;
	float _int = (_val - _half - 256.);
	float _frac = (_int < 0.) ? (_v.a / 255.) : 1. - (_v.a / 255.);
	return _int + mod(_frac, 1.0);
}

// credit @XorDev
vec3 sky(vec3 pos) {
	#define PI 3.14159265358979
	vec4 uvs = vec4(0.0, 0.67, 1.0, 0.33);
	vec2 u = vec2(atan(pos.x,pos.y)/2.,-asin( abs(pos.z) ))/PI+.5;
	vec3 t = texture2D(gm_BaseTexture, u * uvs.zw + uvs.xy).rgb;
	
	return t;
}

float lum(vec3 col) {
	return (0.2126*col.r + 0.7152*col.g + 0.0722*col.b);
}

void main() {
	if (v_vOOB < 0.1) discard;
	if (floor(v_vWaterLevel) < 1.0) discard;
	
	vec3 norm = normalize(v_vNormal);
	vec3 sunray = normalize(u_uSunray);
	
	float light = abs(dot(sunray, norm));
	light = pow(light, 4.0);
	
	float fog = min(v_vWorldPos.z/32000., 1.0);
	fog = 1.0 - pow(fog, 8.);
	
	norm = normalize(vec3(norm.xy, norm.z * 2.5));
	vec2 flookup = v_vScreenpos.xy * vec2(1.0, 0.334) + norm.xy*0.02;
	flookup.x = clamp(flookup.x, 0.0, 1.0);
	flookup.y = clamp(flookup.y, 0.0, 0.333);
	gl_FragColor.xyz = texture2D(gm_BaseTexture, flookup ).xyz;
	
	flookup = vec2(0.0, 0.334) + v_vScreenpos.xy * vec2(1.0, 0.333);
	flookup.x = clamp(flookup.x, 0.0, 1.0);
	flookup.y = clamp(flookup.y, 0.334, 0.666);
	float bdepth = decodeRGBA(texture2D(gm_BaseTexture, flookup ), 256.0);
	bdepth = max(bdepth - v_vDepth - 1.0, 0.0);
	
	vec4 waterColor = mix(u_uWaterColBegin, u_uWaterColEnd, min(bdepth * u_uWaterColFalloff, 1.0) );
	
	float alp = waterColor.a;
	
	alp = min(bdepth / 80.0, 1.0);
	
	gl_FragColor.xyz = mix(gl_FragColor.xyz, waterColor.xyz, waterColor.a);
	gl_FragColor.xyz *= mix(vec3(1.0), waterColor.xyz, min(bdepth * u_uDepthFalloff, 1.0) );
	
	gl_FragColor.xyz += light * 0.25; 
	
	// apply reflection
	vec3 vray = normalize(v_vLookat);
	vec3 vnor = normalize(vec3(norm.xy, norm.z));
	vec3 refl = sky(reflect(vray, vnor));
	gl_FragColor.xyz = mix(gl_FragColor.xyz, refl, clamp(v_vDepth / u_uReflectionRange.z * (1.+lum(refl)*2.0), u_uReflectionRange.x, u_uReflectionRange.y));
	
	// apply shallow foam/wetness
	float shore = min(bdepth / 80.0, 1.0);
	gl_FragColor.xyz = mix(vec3(mix(0.0, 1.0, min(bdepth / 80.0, 1.0))), gl_FragColor.xyz, shore * shore );
	
	gl_FragColor.a = alp * fog;
}

Additive.fsh:
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
varying vec2 v_vPosition;

uniform vec3 u_uSize;	// (cellCount, cellSize, texSize)

uniform sampler2D u_uSampler;
uniform vec4 u_uStencil;
uniform vec2 u_uStencilOffset;
uniform vec2 u_uRange;

#define BDEF 256.0

vec4 encodeRGBA(in float _f, float _bound) {
	float _half = 8388607.;
	float _val = floor(_f + 256.) + _half;
	vec4 _rgba = vec4(vec3(0.), 255.-floor(fract(abs(_f)) * 255. + .5) );
	_rgba.b = floor(_val / 65536.);
	_rgba.g = floor((_val - _rgba.b * 65536.) / 256.);
	_rgba.r = mod(_val, 256.);
	return _rgba / 255.;
}
float decodeRGBA(in vec4 _rgba, in float _bound) {
	vec4 _v = _rgba * 255.;
	float _half = 8388607.;
	float _val = _v.r + _v.g * 256. + _v.b * 65536.;
	float _int = (_val - _half - 256.);
	float _frac = (_int < 0.) ? (_v.a / 255.) : 1. - (_v.a / 255.);
	return _int + mod(_frac, 1.0);
}

void main() {
	if (v_vPosition != clamp(v_vPosition, u_uStencil.xy, u_uStencil.zw)) {
		discard;
	}
    float height = decodeRGBA(texture2D(u_uSampler, (u_uStencilOffset + v_vPosition) / u_uSize.z), BDEF);
	vec2 sample = texture2D(gm_BaseTexture, v_vTexcoord).xw;
	float value = mix(u_uRange.x, u_uRange.y, sample.x) * sample.y;
	gl_FragColor = encodeRGBA(max(height + value, 0.0), BDEF);
}

Additivexy.fsh:
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
varying vec2 v_vPosition;

uniform vec3 u_uSize;	// (cellCount, cellSize, texSize)

uniform sampler2D u_uSampler;
uniform vec4 u_uStencil;
uniform vec2 u_uStencilOffset;
uniform vec2 u_uRange;

uniform int u_uComponent;

#define BDEF 256.0

vec4 encodeRGBA(in float _f, float _bound) {
	float _half = 8388607.;
	float _val = floor(_f + 256.) + _half;
	vec4 _rgba = vec4(vec3(0.), 255.-floor(fract(abs(_f)) * 255. + .5) );
	_rgba.b = floor(_val / 65536.);
	_rgba.g = floor((_val - _rgba.b * 65536.) / 256.);
	_rgba.r = mod(_val, 256.);
	return _rgba / 255.;
}
vec2 encodeXY(in float _f, in float _bound) {
	float _half = 32767.;
	float _val = _f * _bound + _half + 256.;
	return vec2(mod(floor(_val), 256.), floor(_val / 256.)) / 255.;
}
float decodeXY(in vec2 _xy, in float _bound) {
	vec2 _v = _xy * 255.;
	float _val = _v.x + _v.y * 256.;
	float _half = 32767.;
	return (_val - _half - 256.) / _bound;
}

void main() {
	if (v_vPosition != clamp(v_vPosition, u_uStencil.xy, u_uStencil.zw)) {
		discard;
	}
	
	vec4 texsamp = texture2D(u_uSampler, (u_uStencilOffset + v_vPosition) / u_uSize.z);
	float height;
	float value;
	vec2 sample;
	
	gl_FragColor = texsamp;
	
	if (u_uComponent <= 0) {
		height = decodeXY(texsamp.xy, BDEF);
		sample = texture2D(gm_BaseTexture, v_vTexcoord).xw;
		value = mix(u_uRange.x, u_uRange.y, sample.x) * sample.y;
		gl_FragColor = vec4(encodeXY(height + value, BDEF), texsamp.zw);
	}
	else if (u_uComponent == 1) {
		height = decodeXY(texsamp.zw, BDEF);
		sample = texture2D(gm_BaseTexture, v_vTexcoord).yw;
		value = mix(u_uRange.x, u_uRange.y, sample.x) * sample.y;
		gl_FragColor = vec4(texsamp.xy, encodeXY(height + value, BDEF));
	}
	
}
Encodefill.fsh:
uniform float u_uValue;

#define BDEF 256.0

vec4 encodeRGBA(in float _f, float _bound) {
	float _half = 8388607.;
	float _val = floor(_f + 256.) + _half;
	vec4 _rgba = vec4(vec3(0.), 255.-floor(fract(abs(_f)) * 255. + .5) );
	_rgba.b = floor(_val / 65536.);
	_rgba.g = floor((_val - _rgba.b * 65536.) / 256.);
	_rgba.r = mod(_val, 256.);
	return _rgba / 255.;
}

void main() {
	gl_FragColor = encodeRGBA(u_uValue, BDEF);
}