



// bool IsInVoxelizationVolume(ivec3 voxelIndex) {
// 	const int xzRadiusBlocks = shadowMapResolution / 32;
// 	const ivec3 lo = ivec3(-xzRadiusBlocks    ,   0,-xzRadiusBlocks    );
// 	const ivec3 hi = ivec3( xzRadiusBlocks - 1, 255, xzRadiusBlocks - 1);

// 	return clamp(voxelIndex, lo, hi) == voxelIndex;
// }

#define VOXELMAP_ALTITIUDE 64 // How many blocks the voxelization volume will extend above the camera's head. (Still has 128 fixed total height) [16 32 48 64 80 96 112]
bool IsInVoxelizationVolume(ivec3 voxelIndex) {
	const int xzRadiusBlocks = int(MC_SHADOW_QUALITY * shadowMapResolution) / 32;
    const ivec3 lo = ivec3(-xzRadiusBlocks    , clamp(cameraPosition.y - (128 - VOXELMAP_ALTITIUDE), -64, 271),-xzRadiusBlocks    );
	const ivec3 hi = ivec3( xzRadiusBlocks - 1, clamp(cameraPosition.y +     VOXELMAP_ALTITIUDE - 1,  63, 319), xzRadiusBlocks - 1);

	return clamp(voxelIndex, lo, hi) == voxelIndex;
}

vec3 SceneSpaceToVoxelSpace(vec3 scenePosition) {
	scenePosition.xz += fract(cameraPosition.xz);
	scenePosition.y  += cameraPosition.y;
	return scenePosition;
}

vec3 VoxelSpaceToSceneSpace(vec3 voxelPosition) {
	voxelPosition.xz -= fract(cameraPosition.xz);
	voxelPosition.y  -= cameraPosition.y;
	return voxelPosition;
}
vec3 WorldSpaceToVoxelSpace(vec3 worldPosition) {
	worldPosition.xz -= floor(cameraPosition.xz);
	return worldPosition;
}

vec3 VoxelSpaceToWorldSpace(vec3 voxelPosition) {
	voxelPosition.xz += floor(cameraPosition.xz);
	return voxelPosition;
}

ivec2 GetVoxelStoragePos(ivec3 voxelIndex) { // in pixels/texels
	int offset = (320 - int(cameraPosition.y)) / 2 - VOXELMAP_ALTITIUDE;
	return (voxelIndex.xz + (int(MC_SHADOW_QUALITY * shadowMapResolution) / 32)) * ivec2(8, 16) + ivec2((voxelIndex.y + offset) / 16, (voxelIndex.y + offset) % 16);
}

ivec3 GetVoxelPosition(ivec2 storagePos) { 
	ivec3 voxelIndex = ivec3(0);
	voxelIndex.y = (storagePos.x % 8) * 16 + storagePos.y % 16;
	ivec2 leftSide = storagePos - ivec2(voxelIndex.y / 16, voxelIndex.y % 16);
	voxelIndex.xz = leftSide / ivec2(8, 16) - (int(MC_SHADOW_QUALITY * shadowMapResolution) / 32);
	return voxelIndex;
}

#if !defined VOXELIZATION_PASS
	vec4[2] ReadVoxel(ivec3 voxelPosition) {
		ivec2 storagePos = GetVoxelStoragePos(voxelPosition);

		// return vec4[2](
		// 	texelFetch(shadowcolor0, storagePos, 0),
		// 	texelFetch(shadowcolor1, storagePos, 0)
		// );
		uvec2 data = texelFetch(shadowcolor1, storagePos, 0).xy;
		return vec4[2] (unpackUnorm4x8(data.x), unpackUnorm4x8(data.y));
	}

	// vec4[2] ReadVoxel_manhattan(ivec3 voxelPosition, out float manhattan) {
	// 	ivec2 storagePos = GetVoxelStoragePos(voxelPosition);

	// 	// return vec4[2](
	// 	// 	texelFetch(shadowcolor0, storagePos, 0),
	// 	// 	texelFetch(shadowcolor1, storagePos, 0)
	// 	// );
	// 	uvec2 data = texelFetch(shadowcolor1, storagePos, 0).xy;
	// 	manhattan = floor(255.0f*texelFetch(shadowcolor0, storagePos, 0).x);
	// 	return vec4[2] (unpackUnorm4x8(data.x), unpackUnorm4x8(data.y));
	// }

	// float ReadManhattan(ivec3 voxelPosition) {
	// 	if(IsInVoxelizationVolume(voxelPosition)) {
	// 		ivec2 storagePos = GetVoxelStoragePos(voxelPosition);
	// 		// return texelFetch(shadowcolor0, storagePos, 0).x;
	// 		uint data = texelFetch(shadowcolor1, storagePos, 0).y;
	// 		return unpackUnorm4x8(data).x;
	// 	}

	// 	return 0.0;
	// }
#endif

void SetVoxelTint(inout vec4[2] voxel, vec3 tint) {
	voxel[0].rgb = tint;
}
void SetVoxelId(inout vec4[2] voxel, int id) {
	voxel[0].a = id / 255.0;
}
void SetVoxelTileSize(inout vec4[2] voxel, int tileSize) {
	voxel[1].y = Pack2x4(vec2(Unpack2x4(voxel[1].y).x, clamp(log2(float(tileSize)) / 15.0, 0.0, 1.0)));
}
void SetVoxelTileIndex(inout vec4[2] voxel, ivec2 tileIndex) {
	voxel[1].zw = tileIndex / 255.0;
}

vec3 ExtractVoxelTint(vec4[2] voxel) {
	return voxel[0].rgb;
}
int ExtractVoxelId(vec4[2] voxel) {
	return int(floor(255.0 * voxel[0].a + 0.5));
}
int ExtractVoxelTileSize(vec4[2] voxel) {
	return int(exp2(floor(Unpack2x4(voxel[1].y).y * 15.0 + 0.5)));
}
ivec2 ExtractVoxelTileIndex(vec4[2] voxel) {
	return ivec2(floor(voxel[1].zw * 255.0 + 0.5));
}

float MaxOf(vec2 x) { return max(x.x, x.y); }
float MaxOf(vec3 x) { return max(max(x.x, x.y), x.z); }
float MaxOf(vec4 x) { return max(max(x.x, x.y), max(x.z, x.w)); }