vec3 VoxelSpaceFromSceneSpace(vec3 position) {
	#if defined SHADOW_MAPPING && !defined VOXELIZE_FULL_COLUMN
	const float radius = VOXELMAP_RESOLUTION / 16;
	#else
	const float radius = VOXELMAP_RESOLUTION / 32;
	#endif

	position.xz += fract(cameraPosition.xz) + radius;
	#if defined SHADOW_MAPPING && !defined VOXELIZE_FULL_COLUMN
	position.y  += cameraPosition.y < 64.0 ? cameraPosition.y : cameraPosition.y > 256.0 - 64.0 ? cameraPosition.y - 128.0 : fract(cameraPosition.y) + 64.0;
	#else
	position.y  += cameraPosition.y;
	#endif

	return position;
}
vec3 SceneSpaceFromVoxelSpace(vec3 position) {
	#if defined SHADOW_MAPPING && !defined VOXELIZE_FULL_COLUMN
	const float radius = VOXELMAP_RESOLUTION / 16;
	#else
	const float radius = VOXELMAP_RESOLUTION / 32;
	#endif

	position.xz -= fract(cameraPosition.xz) + radius;
	#if defined SHADOW_MAPPING && !defined VOXELIZE_FULL_COLUMN
	position.y  -= cameraPosition.y < 64.0 ? cameraPosition.y : cameraPosition.y > 256.0 - 64.0 ? cameraPosition.y - 128.0 : fract(cameraPosition.y) + 64.0;
	#else
	position.y  -= cameraPosition.y;
	#endif

	return position;
}

// Bounds check
bool InsideVoxelmap(uvec3 idx) {
	#if defined SHADOW_MAPPING && !defined VOXELIZE_FULL_COLUMN
		const uvec2 maxBounds = uvec2(VOXELMAP_RESOLUTION /  8, 128) - 1u;
	#else
		const uvec2 maxBounds = uvec2(VOXELMAP_RESOLUTION / 16, 256) - 1u;
	#endif

	return all(lessThanEqual(idx, maxBounds.xyx));
}
bool InsideVoxelmap(ivec3 idx) {
	// Converting to a uvec3 should be free as it preserves the bit pattern.
	return InsideVoxelmap(uvec3(idx));
}
bool InsideVoxelmap(vec3 idx) {
	#if defined SHADOW_MAPPING && !defined VOXELIZE_FULL_COLUMN
		const vec2 maxBounds = vec2(VOXELMAP_RESOLUTION /  8, 128) - 1.0;
	#else
		const vec2 maxBounds = vec2(VOXELMAP_RESOLUTION / 16, 256) - 1.0;
	#endif
	/*
	return clamp(idx, vec3(0.0), maxBounds.xyx) == idx;
	/*/
	return all(greaterThanEqual(idx, vec3(0.0))) && all(lessThanEqual(idx, maxBounds.xyx));
	//*/
}

bool OutsideVoxelmap(uvec3 idx) {
	#if defined SHADOW_MAPPING && !defined VOXELIZE_FULL_COLUMN
		const uvec2 maxBounds = uvec2(VOXELMAP_RESOLUTION /  8, 128) - 1u;
	#else
		const uvec2 maxBounds = uvec2(VOXELMAP_RESOLUTION / 16, 256) - 1u;
	#endif

	return any(greaterThan(idx, maxBounds.xyx));
}
bool OutsideVoxelmap(ivec3 idx) {
	// Converting to a uvec3 should be free as it preserves the bit pattern.
	return OutsideVoxelmap(uvec3(idx));
}
bool OutsideVoxelmap(vec3 idx) {
	#if defined SHADOW_MAPPING && !defined VOXELIZE_FULL_COLUMN
		const vec2 maxBounds = vec2(VOXELMAP_RESOLUTION /  8, 128) - 1.0;
	#else
		const vec2 maxBounds = vec2(VOXELMAP_RESOLUTION / 16, 256) - 1.0;
	#endif
	/*
	return clamp(idx, vec3(0.0), maxBounds.xyx) != idx;
	/*/
	return any(lessThan(idx, vec3(0.0))) || any(greaterThan(idx, maxBounds.xyx));
	//*/
}

// Lookup
uvec2 CalculateVoxelmapIdx(uvec3 idx) {
	#if defined SHADOW_MAPPING && !defined VOXELIZE_FULL_COLUMN
	const uint range = uint(VOXELMAP_RESOLUTION /  8);
	#else
	const uint range = uint(VOXELMAP_RESOLUTION / 16);
	#endif

	#if   VOXELMAP_LAYOUT == 2
		#if defined SHADOW_MAPPING && !defined VOXELIZE_FULL_COLUMN
		uint linear = idx.x + range * (idx.y + 128u * idx.z);
		uvec2 pos = uvec2(linear % uint(SHADOWTEX_RESOLUTION), linear / uint(SHADOWTEX_RESOLUTION));
		#else
		uint linear = idx.x + range * (idx.y + 256u * idx.z);
		uvec2 pos = uvec2(linear % uint(VOXELMAP_RESOLUTION), linear / uint(VOXELMAP_RESOLUTION));
		#endif
	#elif VOXELMAP_LAYOUT == 1
		uvec2 tileIdx = uvec2(idx.y & 15u, idx.y >> 4u);
		uvec2 pos = idx.xz + range * tileIdx;
	#else //VOXELMAP_LAYOUT == 0
		uvec2 tile = uvec2(idx.y & 15u, idx.y >> 4u);
		uvec2 pos = tile + idx.xz * tileSize;
	#endif

	return pos;
}
ivec2 CalculateVoxelmapIdx(ivec3 idx) {
	#if defined SHADOW_MAPPING && !defined VOXELIZE_FULL_COLUMN
	const int range = VOXELMAP_RESOLUTION /  8;
	#else
	const int range = VOXELMAP_RESOLUTION / 16;
	#endif

	#if   VOXELMAP_LAYOUT == 2
		#if defined SHADOW_MAPPING && !defined VOXELIZE_FULL_COLUMN
		int linear = idx.x + range * (idx.y + 128 * idx.z);
		ivec2 pos = ivec2(linear % SHADOWTEX_RESOLUTION, linear / SHADOWTEX_RESOLUTION);
		#else
		int linear = idx.x + range * (idx.y + 256 * idx.z);
		ivec2 pos = ivec2(linear % VOXELMAP_RESOLUTION, linear / VOXELMAP_RESOLUTION);
		#endif
	#elif VOXELMAP_LAYOUT == 1
		ivec2 tileIdx = ivec2(idx.y & 15, idx.y >> 4);
		ivec2 pos = idx.xz + range * tileIdx;
	#else //VOXELMAP_LAYOUT == 0
		ivec2 tile = ivec2(idx.y & 15, idx.y >> 4);
		ivec2 pos = tile + idx.xz * tileSize;
	#endif

	return pos;
}
vec2 CalculateVoxelmapCoord(vec3 idx) {
	#if defined SHADOW_MAPPING && !defined VOXELIZE_FULL_COLUMN
	const float range = float(VOXELMAP_RESOLUTION /  8);
	#else
	const float range = float(VOXELMAP_RESOLUTION / 16);
	#endif

	#if   VOXELMAP_LAYOUT == 2
		#if defined SHADOW_MAPPING && !defined VOXELIZE_FULL_COLUMN
		float linear = idx.x + range * (idx.y + 128.0 * idx.z);
		vec2 pos; pos.x = modf(linear / float(SHADOWTEX_RESOLUTION), pos.y) * float(SHADOWTEX_RESOLUTION);
		#else
		float linear = idx.x + range * (idx.y + 256.0 * idx.z);
		vec2 pos; pos.x = modf(linear / float(VOXELMAP_RESOLUTION), pos.y) * float(VOXELMAP_RESOLUTION);
		#endif
	#elif VOXELMAP_LAYOUT == 1
		#if defined SHADOW_MAPPING && !defined VOXELIZE_FULL_COLUMN
		const vec2 tiles = vec2(16.0,  8.0);
		#else
		const vec2 tiles = vec2(16.0, 16.0);
		#endif

		vec2 tileIdx; tileIdx.x = modf(idx.y / tiles.x, tileIdx.y) * tiles.x;
		vec2 pos = idx.xz + range * tileIdx;
	#else //VOXELMAP_LAYOUT == 0
		#if defined SHADOW_MAPPING && !defined VOXELIZE_FULL_COLUMN
		const vec2 tileSize = vec2(16.0,  8.0);
		#else
		const vec2 tileSize = vec2(16.0, 16.0);
		#endif

		vec2 tile; tile.x = modf(idx.y / tileSize.x, tile.y) * tileSize.x;
		vec2 pos = tile + idx.xz * tileSize;
	#endif

	pos /= SHADOWTEX_RESOLUTION;
	pos += 0.5 / SHADOWTEX_RESOLUTION;

	return pos;
}

#ifdef DEV_VIS
uint[8] reads = uint[8](0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u);
#endif
#if OCTREE_LEVELS > 1 && defined STAGE_FRAGMENT
bool VoxelIsOccupied(uvec3 idx, uint level) {
	#ifdef DEV_VIS
	++reads[level];
	#endif

	float cmp = (255.5 - OCTREE_LEVELS) / 255.0 + float(level) / 255.0;
	uvec2 voxelmapIdx = CalculateVoxelmapIdx((idx >> level) << level);
	return texelFetch(shadowcolor0, ivec2(voxelmapIdx), 0).a < cmp;
}
bool VoxelIsOccupied(ivec3 idx, int level) {
	#ifdef DEV_VIS
	++reads[level];
	#endif

	float cmp = (255.5 - OCTREE_LEVELS) / 255.0 + float(level) / 255.0;
	ivec2 voxelmapIdx = CalculateVoxelmapIdx((idx >> level) << level);
	return texelFetch(shadowcolor0, voxelmapIdx, 0).a < cmp;
}
bool VoxelIsOccupied(vec3 idx, int level) {
	#ifdef DEV_VIS
	++reads[level];
	#endif

	float cmp = (255.5 - OCTREE_LEVELS) / 255.0 + float(level) / 255.0;
	//vec2 voxelmapCoord = CalculateVoxelmapCoord(floor(idx / float(1 << level)) * float(1 << level));
	//vec2 voxelmapCoord = CalculateVoxelmapCoord((ivec3(idx) >> level) << level);
	//return texture(shadowcolor0, voxelmapCoord).a < cmp;
	ivec2 voxelmapIdx = CalculateVoxelmapIdx((ivec3(idx) >> level) << level);
	return texelFetch(shadowcolor0, voxelmapIdx, 0).a < cmp;
}
#endif
