// VoxelStructure.glsl - Voxel Grid Structure and Utilities
// Based on 0fps.net/2018/02/21/voxel-lighting/ and reference implementation

#ifndef VOXEL_STRUCTURE_GLSL
#define VOXEL_STRUCTURE_GLSL

//============================================================================//
// CONFIGURATION
//============================================================================//

#ifndef VOXEL_RADIUS
#define VOXEL_RADIUS 64 // [32 64 128 256]
#endif

#define VOXEL_AREA (VOXEL_RADIUS * 2)
#define VOXEL_AREA_SQR (VOXEL_AREA * VOXEL_AREA)
#define VOXEL_CENTER vec3(VOXEL_RADIUS)
#define VOXEL_SIZE (1.0 / float(VOXEL_AREA))

//============================================================================//
// COORDINATE CONVERSIONS
//============================================================================//

// World position to voxel grid coordinates (integer)
ivec3 WorldToVoxel(vec3 worldPos) {
    // Camera-relative position
    vec3 relativePos = worldPos - floor(cameraPosition);
    
    // Center around voxel grid
    vec3 voxelPos = relativePos + VOXEL_CENTER;
    
    return ivec3(floor(voxelPos));
}

// Voxel coordinates to world position (center of voxel)
vec3 VoxelToWorld(ivec3 voxelCoord) {
    vec3 relativePos = vec3(voxelCoord) - VOXEL_CENTER + vec3(0.5);
    return relativePos + floor(cameraPosition);
}

// Voxel coordinates to texture coordinates [0,1]
vec3 VoxelToTexCoord(ivec3 voxelCoord) {
    return (vec3(voxelCoord) + vec3(0.5)) / float(VOXEL_AREA);
}

// Texture coordinates to voxel coordinates
ivec3 TexCoordToVoxel(vec3 texCoord) {
    return ivec3(floor(texCoord * float(VOXEL_AREA)));
}

// World position to texture coordinates (with fractional camera position)
vec3 WorldToTexCoord(vec3 worldPos) {
    vec3 relativePos = worldPos - cameraPosition;
    vec3 voxelPos = relativePos + VOXEL_CENTER;
    return voxelPos / float(VOXEL_AREA);
}

//============================================================================//
// BOUNDS CHECKING
//============================================================================//

// Check if voxel coordinates are within grid bounds
bool IsVoxelInBounds(ivec3 voxelCoord) {
    return all(greaterThanEqual(voxelCoord, ivec3(0))) && 
           all(lessThan(voxelCoord, ivec3(VOXEL_AREA)));
}

// Check if world position is within voxel grid
bool IsWorldPosInGrid(vec3 worldPos) {
    ivec3 voxel = WorldToVoxel(worldPos);
    return IsVoxelInBounds(voxel);
}

//============================================================================//
// VOXEL DATA PACKING/UNPACKING
//============================================================================//

// Pack voxel data: RGB albedo + A opacity
vec4 PackVoxelData(vec3 albedo, float opacity) {
    return vec4(albedo, opacity);
}

// Unpack voxel data
void UnpackVoxelData(vec4 data, out vec3 albedo, out float opacity) {
    albedo = data.rgb;
    opacity = data.a;
}

//============================================================================//
// NEIGHBOR OFFSETS
//============================================================================//



//============================================================================//
// DISTANCE CALCULATIONS
//============================================================================//

// Manhattan distance between two voxels
int VoxelManhattanDistance(ivec3 a, ivec3 b) {
    ivec3 diff = abs(a - b);
    return diff.x + diff.y + diff.z;
}

// Euclidean distance between two voxels
float VoxelDistance(ivec3 a, ivec3 b) {
    return length(vec3(a - b));
}

// Distance from voxel to camera (in voxels)
float VoxelDistanceToCamera(ivec3 voxelCoord) {
    return length(vec3(voxelCoord) - VOXEL_CENTER);
}

//============================================================================//
// ATTENUATION
//============================================================================//

// Light attenuation based on distance traveled
float LightAttenuation(float distance, float maxDistance) {
    float ratio = distance / maxDistance;
    return max(0.0, 1.0 - ratio * ratio);
}

// Attenuation with inverse square falloff
float LightAttenuationInvSqr(float distance) {
    return 1.0 / (1.0 + distance * distance);
}

//============================================================================//
// CAMERA MOVEMENT DETECTION
//============================================================================//

// Check if camera moved significantly (requires voxel grid update)
bool DidCameraMove() {
    vec3 deltaPos = floor(cameraPosition) - floor(previousCameraPosition);
    return dot(deltaPos, deltaPos) > 0.5;
}

// Get camera movement offset in voxel space
ivec3 GetCameraMovementOffset() {
    return ivec3(floor(cameraPosition) - floor(previousCameraPosition));
}

//============================================================================//
// TRILINEAR SAMPLING
//============================================================================//

// Trilinear interpolation helper
vec4 SampleVoxelGridTrilinear(sampler3D voxelTex, vec3 worldPos) {
    vec3 texCoord = WorldToTexCoord(worldPos);
    
    // Clamp to valid range
    texCoord = clamp(texCoord, vec3(0.5 / float(VOXEL_AREA)), 
                               vec3(1.0 - 0.5 / float(VOXEL_AREA)));
    
    return texture(voxelTex, texCoord);
}

//============================================================================//
// DEBUG HELPERS
//============================================================================//

// Convert voxel coordinate to debug color
vec3 VoxelCoordToDebugColor(ivec3 voxel) {
    return vec3(voxel) / float(VOXEL_AREA);
}

// Visualize voxel grid as wireframe
bool IsVoxelGridEdge(ivec3 voxel, int thickness) {
    ivec3 edge = min(voxel, ivec3(VOXEL_AREA - 1) - voxel);
    return any(lessThan(edge, ivec3(thickness)));
}
#endif // VOXEL_STRUCTURE_GLSL
