// 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. Interweaved gradient noise adapted from "Next Generation Post Processing in Call of Duty Advanced Warfare"
//    by Jorge Jimenez (Graphics R&D Technical Director - Activision Blizzard)
//    http://advances.realtimerendering.com/s2014/index.html
//
// 2. Copyright (c) 2020 ValgoBoi <https://github.com/ValgoBoi/clover-noise>
//    MIT License (MIT)
//    https://github.com/ValgoBoi/clover-noise/blob/master/LICENSE
//
// xs brush/gradient [Mode] [Direction] [Dither] [Seed]
//
// xs_begin
// author : '@lmcdx.bsky.social'
// arg : { name = 'Mode'  var = 'm_mode'  range = '0 5'  value = '0'  step = '1'  precision = '0' }
// arg : { name = 'Direction'  var = 'm_direction'  range = '0 5'  value = '0'  step = '1'  precision = '0' }
// arg : { name = 'Dither'  var = 'm_noise'  range = '0 50'  value = '0'  step = '1'  precision = '0' }
// arg : { name = 'Seed'  var = 'global_seed'  range = '1 100'  value = '1'  step = '1'  precision = '0' }
// xs_end

int mode = int(m_mode);
int direction = int(m_direction);
float noise = m_noise / 100.0;

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

float hash(vec2 p, float seed) {
	p += seed + global_seed;
	return fract(1e4 * sin(17.0 * p.x + p.y * 0.1) * (0.1 + abs(sin(p.y * 13.0 + p.x))));
}

float hash(vec3 co, float seed) {
	float r = hash(co.xy, seed);
	return hash(vec2(r, co.z), seed);
}

float interleavedGradientNoise(vec2 co) {
	return fract(52.9829189 * fract(dot(co + global_seed, vec2(0.06711056, 0.00583715))));
}

float interleavedGradientNoise(vec3 co) {
	float r = interleavedGradientNoise(co.xy);
	return interleavedGradientNoise(vec2(r, co.z));
}

float scanLines(vec3 co) {
	return mod(co.z, 2.0) / 2.0;
}

float scanLineshash(vec3 co) {
	float r = hash(co.zz, 0.0);
	return mod(co.z, 2.0) / 2.0 + r - 0.5;
}

float wavehash(vec3 co) {
	float r = hash(co.xy, 0.0);
	return mod(co.z, 2.0) / 2.0 + r - 0.5;
}

float orderedDither(vec3 co) {
	co = co - 0.5;
	mat4 m = mat4(0.0,    0.5,    0.125,  0.625,
				  0.75,   0.25,   0.875,  0.375,
				  0.1875, 0.6875, 0.0625, 0.5625,
				  0.9375, 0.4375, 0.8125, 0.3125);

	int x = int(mod(co.x + co.y, 4.0));
	int z = int(mod(co.z, 4.0));
	return m[x][z];
}

float map(vec3 v) {
	if (direction == 1) {
		v.z = i_volume_size.z - v.z;
	} else if (direction == 2) {
		v.xz = v.zx;
	} else if (direction == 3) {
		v.x = i_volume_size.x - v.x;
		v.xz = v.zx;
	} else if (direction == 4) {
		v.yz = v.zy;
	} else if (direction == 5) {
		v.y = i_volume_size.y - v.y;
		v.yz = v.zy;
	}

	vec3 p = floor(v) / i_volume_size;
	float r;

	if (mode == 0) {
		r = interleavedGradientNoise(v);
	} else if (mode == 1) {
		r = hash(v, 0.0);
	} else if (mode == 2) {
		r = scanLineshash(v);
	} else if (mode == 3) {
		r = scanLines(v);
	} else if (mode == 4) {
		r = wavehash(v);
	} else if (mode == 5) {
		r = orderedDither(v);
	}

	vec3 k = p + ((r - 0.5) * noise);
	return pal(k.z);
}