// Copyright (c) 2023 Lachlan McDonald
// This work is licensed under the MIT License (MIT)
// https://github.com/lachlanmcdonald/magicavoxel-shaders
//
// This script utilises or modifies code from other projects or publications.
// Please see the attributions below for more information:
//
// 1. Copyright (c) 2011 Stefan Gustavson <https://github.com/ashima/webgl-noise>
//    MIT License (MIT)
//    https://github.com/ashima/webgl-noise/blob/master/LICENSE
//
// xs noise/cellular2D [Mode] [Scale] [Jitter] [Noise] [Power] [Steps] [Seed] [Tile X] [Tile Y]
//
// xs_begin
// author : '@lmcdx.bsky.social'
// arg : { name = 'Mode'  var = 'm_mode'  range = '0 5'  value = '0'  step = '1'  precision = '0' }
// arg : { name = 'Scale'  var = 'm_scale'  range = '1 100'  value = '24'  step = '1'  precision = '0' }
// arg : { name = 'Jitter'  var = 'm_jitter'  range = '-100 100'  value = '100'  step = '5'  precision = '0' }
// arg : { name = 'Noise'  var = 'm_noise'  range = '0 100'  value = '0'  step = '1'  precision = '0' }
// arg : { name = 'Power'  var = 'm_power'  range = '0 10'  value = '1'  step = '0.05'  precision = '2' }
// arg : { name = 'Steps'  var = 'm_steps'  range = '0 40'  value = '0'  step = '1'  precision = '0' }
// arg : { name = 'Seed'  var = 'global_seed'  range = '1 100'  value = '1'  step = '1'  precision = '0' }
// arg : { name = 'Tile X'  var = 'tile_x'  range = '0 40'  value = '0'  step = '1'  precision = '0' }
// arg : { name = 'Tile Y'  var = 'tile_y'  range = '0 40'  value = '0'  step = '1'  precision = '0' }
// xs_end

int mode = int(m_mode);
float noise = m_noise / 100.0;
float jitter = m_jitter / 100.0;
float steps = floor(m_steps);
vec2 tile = vec2(tile_x, tile_y) * i_volume_size.xy;

float pal(float p) {
	float f = floor(mix(0.0, float(i_num_color_sels), p));
	return color_sel(f);
}

vec3 mod289(vec3 x) {
	return x - floor(x * (1.0 / 289.0)) * 289.0;
}

vec2 mod289(vec2 x) {
	return x - floor(x * (1.0 / 289.0)) * 289.0;
}

vec3 mod7(vec3 x) {
	return x - floor(x * (1.0 / 7.0)) * 7.0;
}

vec3 permute(vec3 x) {
	return mod289((34.0 * x + 10.0) * x);
}

vec2 cellular(vec2 co, float jitter) {
	float K = 0.142857142857;
	float Ko = 0.428571428571;
	vec2 Pi = mod289(floor(co));
 	vec2 Pf = fract(co);
	vec3 oi = vec3(-1.0, 0.0, 1.0);
	vec3 of = vec3(-0.5, 0.5, 1.5);
	vec3 px = permute(Pi.x + oi);
	vec3 p = permute(px.x + Pi.y + oi);
	vec3 ox = fract(p * K) - Ko;
	vec3 oy = mod7(floor(p * K)) * K - Ko;
	vec3 dx = Pf.x + 0.5 + jitter * ox;
	vec3 dy = Pf.y - of + jitter * oy;
	vec3 d1 = dx * dx + dy * dy;
	p = permute(px.y + Pi.y + oi);
	ox = fract(p * K) - Ko;
	oy = mod7(floor(p * K)) * K - Ko;
	dx = Pf.x - 0.5 + jitter * ox;
	dy = Pf.y - of + jitter * oy;
	vec3 d2 = dx * dx + dy * dy;
	p = permute(px.z + Pi.y + oi);
	ox = fract(p * K) - Ko;
	oy = mod7(floor(p * K)) * K - Ko;
	dx = Pf.x - 1.5 + jitter * ox;
	dy = Pf.y - of + jitter * oy;
	vec3 d3 = dx * dx + dy * dy;
	vec3 d1a = min(d1, d2);
	d2 = max(d1, d2);
	d2 = min(d2, d3);
	d1 = min(d1a, d2);
	d2 = max(d1a, d2);
	d1.xy = (d1.x < d1.y) ? d1.xy : d1.yx;
	d1.xz = (d1.x < d1.z) ? d1.xz : d1.zx;
	d1.yz = min(d1.yz, d2.yz);
	d1.y = min(d1.y, d1.z);
	d1.y = min(d1.y, d2.x);
	return sqrt(d1.xy);
}

float roundf(float f) {
	return f > 0.5 ? ceil(f) : floor(f);
}

float map(vec3 v) {
	vec2 offset = global_seed * i_volume_size.xy + tile;
	vec3 k = floor(v) / i_volume_size;
	vec2 p = cellular((v.xy + offset) / m_scale, jitter);
	vec2 q = cellular((v.xy + offset), 1.0);
	float r;
	float z;

	if (mode == 0 || mode == 1 || mode == 4) {
		r = clamp(pow(p.x + q.x * noise, m_power), 0.0, 1.0);
	} else if (mode == 2 || mode == 3 || mode == 5) {
		r = clamp(pow(p.y + q.y * noise, m_power), 0.0, 1.0);
	}

	if (mode == 1 || mode == 3) {
		z = r;
	} else if (mode == 4 || mode == 5) {
		z = 1.0;
	} else {
		z = 1.0 - r;
	}

	if (steps > 0.0) {
		z = roundf(z * steps) / steps;
	}

	return (k.z <= z) ? pal(r) : 0.0;
}