-- glslfx version 0.1

//
// Copyright 2019 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
//    names, trademarks, service marks, or product names of the Licensor
//    and its affiliates, except as required to comply with Section 4(c) of
//    the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//

--- This is what an import might look like.
--- #import $TOOLS/hdx/shaders/renderPass.glslfx

--- --------------------------------------------------------------------------

-- layout HdxRenderPass.RenderOitOpaquePixels

[
    ["out", "vec4", "colorOut"]
]

-- glsl HdxRenderPass.RenderOitOpaquePixels

void RenderOutput(vec4 Peye, vec3 Neye, vec4 color, vec4 patchCoord)
{
    if (color.a >= 1.0) {
        colorOut = vec4(color.rgb, 1);
    } else {
        discard;
    }
}

-- layout HdxRenderPass.WriteOitLayersToBufferCommon

[
    ["in", "early_fragment_tests"],
    ["buffer readWrite", "CounterBuffer", "hdxOitCounterBuffer",
        ["atomic_int", "hdxOitCounterBuffer"]
    ]
]

-- glsl HdxRenderPass.WriteOitLayersToBufferCommon

void RenderOutputImpl(vec4 Peye, vec3 Neye, vec4 color, vec4 patchCoord)
{
    #if defined(HD_HAS_hdxOitDataBuffer)

    const int screenWidth = int(HdGet_oitScreenSize().x);
    const int screenHeight = int(HdGet_oitScreenSize().y);
    // Must match the per-pixel sample count used when creating the OIT buffers.
    // (See HdxOitResolveTask::_PrepareOitBuffers)
    const int numSamples = 8;
        
    const int dataBufferSize = screenWidth * screenHeight * numSamples;
    const int counterBufferSize = screenWidth * screenHeight + 1;

    // +1 because the counter buffer is cleared with -1, but we want the
    // first index to start at 0.
    int writeIndex = ATOMIC_ADD(hdxOitCounterBuffer[0], 1) + 1;
    
    if (writeIndex < dataBufferSize) {
        int screenIndex =
            int(gl_FragCoord.x) + int(gl_FragCoord.y) * screenWidth;

        if (screenIndex < counterBufferSize) {
            int prevIndex =
                ATOMIC_EXCHANGE(hdxOitCounterBuffer[screenIndex+1], writeIndex);
            hdxOitDataBuffer[writeIndex] = color;

            // Note that we have a choice here to either pick gl_FragCoord.z or
            // the depth value from Peye. The former is obtained by applying
            // the perspective transform and cannot be changed by a shader.
            //
            // We pick Peye here so that a shader has an opportunity to change
            // the depth of the sample inserted into the OIT list.
            //
            // This is used by volumes. However, non-volume translucent
            // geometry should never modify Peye value and call RenderOutput
            // with the Peye value from the vertex shader so that it is
            // consistent with gl_FragCoord. This is because such geometry is
            // subject to a opaque pixel render pass performing a z-test
            // against gl_FragCoord.z.
            //
            // Note there are implications of using the depth value from Peye
            // instead of gl_FragCoord.z here for the subsequent OIT resolve
            // shader: the depth sorting order needs to be flipped and the
            // OIT resolve shader cannot compare depths in the OIT list
            // against the depth buffer unless it takes the perspective
            // transform into account.
            hdxOitDepthBuffer[writeIndex] = Peye.z / Peye.w;
            hdxOitIndexBuffer[writeIndex] = prevIndex;
        }
    } else {
        // We may overrun the counter buffer integer and wrap back to 0 if
        // we have a lot of OIT samples.
        ATOMIC_ADD(hdxOitCounterBuffer[0], -1);
    }

    #endif
}

-- glsl HdxRenderPass.WriteOitLayersToBufferTranslucent

void RenderOutput(vec4 Peye, vec3 Neye, vec4 color, vec4 patchCoord)
{
    // There are two render passes for ordinary OIT geometry.
    // Fragments with alpha >= 1.0 are handled in the first (opaque)
    // render pass.
    if (color.a < 1.0 && color.a > 0.0001) {
        RenderOutputImpl(Peye, Neye, color, patchCoord);
    }
}

-- glsl HdxRenderPass.WriteOitLayersToBufferVolume

void RenderOutput(vec4 Peye, vec3 Neye, vec4 color, vec4 patchCoord)
{
    // Unlike ordinary OIT geometry, volumes have only one render pass,
    // so insert into OIT buffers even if alpha is 1.
    if (any(greaterThan(color, vec4(0.0001)))) {
        RenderOutputImpl(Peye, Neye, color, patchCoord);
    }
}

-- layout HdxRenderPass.RenderPick

[
    ["out", "vec4", "primIdOut"],
    ["out", "vec4", "instanceIdOut"],
    ["out", "vec4", "elementIdOut"],
    ["out", "vec4", "edgeIdOut"],
    ["out", "vec4", "pointIdOut"],
    ["out", "vec4", "neyeOut"]
]

-- glsl HdxRenderPass.RenderPick

vec4 IntToVec4(int id)
{
    return vec4(((id >>  0) & 0xff) / 255.0,
                ((id >>  8) & 0xff) / 255.0,
                ((id >> 16) & 0xff) / 255.0,
                ((id >> 24) & 0xff) / 255.0);
}

// Fwd declare necessary methods to determine the subprim id of a fragment.
FORWARD_DECL(int GetElementID()); // generated via codeGen
FORWARD_DECL(int GetPrimitiveEdgeId()); // defined in edgeId.glslfx, or generated via codeGen
FORWARD_DECL(int GetPointId()); // defined in pointId.glslfx

void RenderOutput(vec4 Peye, vec3 Neye, vec4 color, vec4 patchCoord)
{
    int primId = HdGet_primID();
    primIdOut = IntToVec4(primId);

    // instanceIndex is a tuple of integers (num nested levels).
    // for picking, we store global instanceId (instanceIndex[0]) in the
    // selection framebuffer and then reconstruct the tuple in postprocess.
    int instanceId = GetDrawingCoord().instanceIndex[0];
    instanceIdOut = IntToVec4(instanceId);

    elementIdOut = IntToVec4(GetElementID());
    edgeIdOut = IntToVec4(GetPrimitiveEdgeId());
    pointIdOut = IntToVec4(GetPointId());
    
    neyeOut = IntToVec4(hd_vec4_2_10_10_10_set(vec4(Neye,0)));
}

-- layout HdxRenderPass.RenderColorAndSelection

[
    ["out", "vec4", "colorOut"],
    ["out", "float", "selectedOut"]
]

-- glsl HdxRenderPass.RenderColorAndSelection

// Note: This mixin expects color and selected color attachments in the bound
// FBO. It writes out the computed fragment color and whether it is selected
// (as a float, so, 1.0 or 0.0).

bool IsSelected(); // defined in selection.glslfx

void RenderOutput(vec4 Peye, vec3 Neye, vec4 color, vec4 patchCoord)
{
    colorOut = color;
    selectedOut = float(IsSelected());
}

-- layout HdxRenderPass.RenderColorWithOccludedSelection

[
    ["out", "vec4", "colorOut"]
]

-- glsl HdxRenderPass.RenderColorWithOccludedSelection

// Note: This mixin expects color and selected color attachments in the bound
// FBO. The alpha component of the computed fragment color is adjusted to blend
// the existing (destination) fragment color if it is selected.

bool
HasOccludedSelection(vec2 fragcoord)
{
#ifdef HD_HAS_selectedReadback
    const float isSelected = texelFetch(HdGetSampler_selectedReadback(),
                                        ivec2(fragcoord),
                                        0).x;
    return bool(isSelected);
#endif
    return false;
}

float
GetShowThroughOpacity()
{
#ifdef HD_HAS_occludedSelectionOpacity
    // Note: occludedSelectionOpacity flows in as a parameter to the selection
    // setup task (HdxSelectionTask)
    const float dstOpacity = HdGet_occludedSelectionOpacity();
    // adjust source alpha used to blend source and dst colors.
    return 1.0 - dstOpacity;
#else
    return 0.5;
#endif
}

// Input AOV textures are provided by the task.
void RenderOutput(vec4 Peye, vec3 Neye, vec4 color, vec4 patchCoord)
{
    if (HasOccludedSelection(gl_FragCoord.xy)) {
        color.a = GetShowThroughOpacity();
    }

    colorOut = color;
}
