-- glslfx version 0.1

//
// Copyright 2018 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/hdSt/shaders/edgeId.glslfx

--- --------------------------------------------------------------------------
-- glsl EdgeId.Geometry.None

// called from Mesh.Geometry.*
void ProcessEdgeId(int index, vec2 faceUV)
{
    // do nothing
}

--- --------------------------------------------------------------------------
-- glsl EdgeId.Geometry.EdgeParam

// Interpolate linearly since we want to be able to
// reason about the distance from the edge in fragment space.
noperspective out vec2 gsEdgeParam;

// called from Mesh.Geometry.*
void ProcessEdgeId(int index, vec2 faceUV)
{
    // The local triangular parameterization is a subset of the
    // local rectangular parameterization, so for both cases
    // we just record the uv parameterization at the face vertex
    // and can reconstruct the third coordinate when necessary.
    gsEdgeParam = faceUV;
}

--- --------------------------------------------------------------------------
-- glsl EdgeId.Fragment.Fallback
// Mixin version to use for meshes when the geometry shader stage is inactive.
// Note: When rendering a mesh as points, this mixin isn't used. We handle it
// in code gen.

int GetPrimitiveEdgeId() {
    return -1;
}

bool IsFragmentOnEdge() {
    return false;
}

float GetSelectedEdgeOpacity() {
    return 0.0;
}

--- --------------------------------------------------------------------------
-- glsl EdgeId.Fragment.Common

float GetMinEdgeDistance(); // meshWire.glslfx

// Returns whether a fragment is on or close to an unhidden triangle edge.
const float edgePickRadius = 2; // in viewport pixels
bool IsFragmentOnEdge()
{
    float d = GetMinEdgeDistance();
    if (d < edgePickRadius) return true;
    return false;
}

// For smooth looking edges, use an exponential falloff for the opacity.
// Note: We use a slower falloff fn than in meshWire.glslfx to make the selected
// edges stand out.
float GetSelectedEdgeOpacity() {
    float d = GetMinEdgeDistance();
    return exp2(-1 * d * d);
}

--- --------------------------------------------------------------------------
-- glsl EdgeId.Fragment.TriangleParam

vec3 ExpandBarycentricCoord(vec2 bc)
{
    return vec3(bc.x, bc.y, 1 - bc.x - bc.y);
}

noperspective in vec2 gsEdgeParam;

// called from hdx/renderPass.glslfx and selection.glslfx
int GetPrimitiveEdgeId()
{
    // Use edge distance to reject fragments that aren't on/near
    // (unhidden) triangle edges.
    if (!IsFragmentOnEdge()) return -1;

    // Find out which edge the fragment is on.
    vec3 bc = ExpandBarycentricCoord(gsEdgeParam.xy);

    //              2 (0,1,0)
    //              ^
    //         e2 /   \ e1
    //           /     \'
    // (0,0,1)  0 ----- 1 (1,0,0)
    //              e0

    int edgeId = -1;
    float delta = 0.02;
    bvec3 ltResult = lessThan(bc, vec3(delta));
    if (ltResult.y && (bc.y < bc.x) && (bc.y < bc.z)) {
        edgeId = 0;
    } else if (ltResult.z && (bc.z < bc.x)) {
        edgeId = 1;
    } else if (ltResult.x) {
        edgeId = 2;
    }

    return edgeId;
}

--- --------------------------------------------------------------------------
-- glsl EdgeId.Fragment.RectangleParam

noperspective in vec2 gsEdgeParam;

// called from hdx/renderPass.glslfx and selection.glslfx
int GetPrimitiveEdgeId()
{
    // Use edge distance to reject fragments that aren't on/near (unhidden)
    // triangle edges.
    if (!IsFragmentOnEdge()) return -1;

    //              e2
    //     (0,1) 3 ------ 2 (1,1)
    //           |        |
    //       e3  |        |  e1
    //           |        |
    //     (0,0) 0 ------ 1 (1,0)
    //               e0

    int edgeId = -1;
    const float delta = 0.02;
    const float delLow = delta;
    const float delHigh = 1 - delLow;
    vec2 uv = gsEdgeParam.xy;

    if (uv.y < delLow && uv.y < uv.x) {
        edgeId = 0;
    } else if (uv.x < delLow) {
        edgeId = 3;
    } else if (uv.x > delHigh && uv.x > uv.y) {
        edgeId = 1;
    } else if (uv.y > delHigh) {
        edgeId = 2;
    }

    return edgeId;
}