/// Alligator Noise is provided by Side Effects Software Inc. and is licensed
/// under a Creative Commons Attribution-ShareAlike 4.0 International License.
///
/// Author:  "Side Effects Software Inc"
/// Source:  "http://www.sidefx.com/docs/hdk15.0/alligator_2alligator_8_c-example.html"
/// License: "http://creativecommons.org/licenses/by-sa/4.0/"
///
/// Translated and modified by Ivan Mavrov, Chaos Group Ltd. 2016
/// Contact: ivan.mavrov@chaosgroup.com
 
/// 3D Alligator noise implementation.
/// Returned values are in the [0, 1] range.
float alligatorNoise3D(point position) {
    vector cellOffsets[27] = {
        vector( 0,  0,  0),
        vector( 1,  0,  0),
        vector( 1,  1,  0),
        vector( 0,  1,  0),
        vector(-1,  1,  0),
        vector(-1,  0,  0),
        vector(-1, -1,  0),
        vector( 0, -1,  0),
        vector( 1, -1,  0),
         
        vector( 0,  0, -1),
        vector( 1,  0, -1),
        vector( 1,  1, -1),
        vector( 0,  1, -1),
        vector(-1,  1, -1),
        vector(-1,  0, -1),
        vector(-1, -1, -1),
        vector( 0, -1, -1),
        vector( 1, -1, -1),
 
        vector( 0,  0,  1),
        vector( 1,  0,  1),
        vector( 1,  1,  1),
        vector( 0,  1,  1),
        vector(-1,  1,  1),
        vector(-1,  0,  1),
        vector(-1, -1,  1),
        vector( 0, -1,  1),
        vector( 1, -1,  1)
    };
 
    point iPosition = floor(position);
 
    float firstReverseSmoothPointDistance = 0.0;
    float secondReverseSmoothPointDistance = 0.0;
     
    for (int cellIndex = 0; cellIndex < 27; ++cellIndex) {
        point cellOrigin = iPosition + cellOffsets[cellIndex];
        vector cellPointOffset = cellnoise(cellOrigin, 0.0);
        point cellPointPosition = cellOrigin + cellPointOffset;
 
        float cellPointDistance = distance(position, cellPointPosition);
         
        if (cellPointDistance < 1.0) {
            float reverseSmoothDistance = smoothstep(0.0, 1.0, 1.0 - cellPointDistance);
             
            float distanceMultiplier = float(cellnoise(cellOrigin, 1.0));
            reverseSmoothDistance *= distanceMultiplier;
             
            if (firstReverseSmoothPointDistance < reverseSmoothDistance) {
                secondReverseSmoothPointDistance = firstReverseSmoothPointDistance;
                firstReverseSmoothPointDistance = reverseSmoothDistance;
            } else {
                if (secondReverseSmoothPointDistance < reverseSmoothDistance) {
                    secondReverseSmoothPointDistance = reverseSmoothDistance;
                }
            }
        }
    }
 
    return firstReverseSmoothPointDistance - secondReverseSmoothPointDistance;
}
 
/// 3D Fractal Alligator noise implementation.
/// Returned values are in the [-1, 1] range.
float fractalAlligatorNoise3D(
    point position,
    float lacunarity, // Houdini 2.0
    float gain,       // Houdini rough
    int octaveCount   // Houdini turbulence - 1
) {
    float noiseValue = 0.0;
     
    float amplitude = 1.0;
 
    for (int octave = 0; octave < octaveCount; ++octave) {
        noiseValue += amplitude * (alligatorNoise3D(position) - 0.5);      
        position *= lacunarity;
        amplitude *= gain;
    }
     
    return noiseValue;
}
 
shader FractalAlligatorNoise
    [[ string description = "Fractal Alligator noise" ]]
(
    float start_frequency = 1.0
        [[ string description = "Initial sampling position multiplier that affects the overall granularity." ]],
    vector start_offset = vector(0.0)
        [[ string description = "Offsets the initial sampling position effectively shifting the pattern in the specified direction." ]],
         
    float lacunarity = 2.0
        [[ string description = "Position (frequency) multiplier per iteration." ]],
    float gain = 0.5
        [[ string description = "Amplitude multiplier per iteration." ]],
    int octaves = 8
        [[ string description = "Number of fractal iterations." ]],
     
    float attenuation = 1.0
        [[ string description = "The power of the falloff applied to the final result." ]],
    string spacename = "object",
    output color result = 0.0
) {
    point objectPosition = transform(spacename, P);
    point startPosition = start_frequency * objectPosition - start_offset;
     
    float noiseValue = fractalAlligatorNoise3D(startPosition, lacunarity, gain, octaves);
     
    noiseValue = 0.5 * noiseValue + 0.5;
    noiseValue = pow(noiseValue, attenuation);
     
    result = color(noiseValue);
}