-- glslfx version 0.1

//
// Copyright 2016 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/meshWire.glslfx

#import $TOOLS/hdx/shaders/selection.glslfx 

--- --------------------------------------------------------------------------
-- glsl MeshWire.Geometry.NoEdge

void ProcessEdgeDistance(vec4 Peye0, vec4 Peye1, vec4 Peye2, vec4 Peye3,
                         vec3 edgeClip)
{
    // do nothing.
}

void
ProcessTriEdgeDistance(vec4 Peye0, vec4 Peye1, vec4 Peye2, vec4 Peye3, int inEdgeFlag)
{
    // do nothing.
}

void
ProcessQuadEdgeDistance(vec4 Peye0, vec4 Peye1, vec4 Peye2, vec4 Peye3,
                        int triCounter)
{
    // do nothing.
}

--- --------------------------------------------------------------------------
-- glsl MeshWire.Geometry.Edge

out EdgeData
{
    noperspective out vec3 edgeDistance;
} outEdgeData;

vec3 edgeDistance(vec2 p, vec2 p0, vec2 p1, vec2 p2)
{
    vec3 r;
    r.x = abs((p.x - p0.x) * (p1.y - p0.y) -
              (p.y - p0.y) * (p1.x - p0.x)) / length(p1.xy - p0.xy);
    r.y = abs((p.x - p1.x) * (p2.y - p1.y) -
              (p.y - p1.y) * (p2.x - p1.x)) / length(p2.xy - p1.xy);
    r.z = abs((p.x - p2.x) * (p0.y - p2.y) -
              (p.y - p2.y) * (p0.x - p2.x)) / length(p0.xy - p2.xy);
    return r;
}

void ProcessEdgeDistance(vec4 Peye0, vec4 Peye1, vec4 Peye2, vec4 Peye3,
                         vec3 edgeClip)
{
    // note:
    // we expect the GLSL compiler optimizes out the redundant computations of
    // following projection.

    vec4 viewport = GetViewport();
    vec4 viewportScale = vec4(0.5*viewport[2], 0.5*viewport[3], 1, 1);

    // screen space position
    vec4 v0 = GetProjectionMatrix() * Peye0;
    vec4 v1 = GetProjectionMatrix() * Peye1;
    vec4 v2 = GetProjectionMatrix() * Peye2;
    vec4 v3 = GetProjectionMatrix() * Peye3;
    v0.xy = (v0.xy/v0.w) * viewportScale.xy;
    v1.xy = (v1.xy/v1.w) * viewportScale.xy;
    v2.xy = (v2.xy/v2.w) * viewportScale.xy;
    v3.xy = (v3.xy/v3.w) * viewportScale.xy;

    outEdgeData.edgeDistance =
        max(edgeDistance(v0.xy, v1.xy, v2.xy, v3.xy), edgeClip);
}

void ProcessTriEdgeDistance(vec4 Peye0, vec4 Peye1, vec4 Peye2, vec4 Peye3, int inEdgeFlag)
{
    // see hd/meshUtil.cpp
    //
    //          0__                0  0   0__
    //        _/|\ \_            _/.  ..   . \_
    //      _/  | \  \_   ->   _/  .  . .   .  \_
    //     /  A |C \ B \_     /  A .  .C .   . B \_
    //    1-----2---3----4   1-----2  1---2   1----2
    //
    //  Type   EdgeFlag    Draw
    //    -       0        show all edges
    //    A       1        hide [2-0]
    //    B       2        hide [0-1]
    //    C       3        hide [0-1] and [2-0]
    //    D       4        hide [1-2]
    //
    // edgeDistance returns 3 floats, distances from
    // given vertex to edge [0-1], [1-2], [2-0] as (x, y, z).
    // i.e. to remove edge [2-0], make edgeDistance.z big in viewport pixel.
    // similary edgeDistance.x can be used for edge [0-1].

    int edgeFlag = GetEdgeFlag(inEdgeFlag);
    vec3 edgeClip = vec3(0);
    // it might be nice to use the viewport scale, but constant is much faster.
    edgeClip.z = ((edgeFlag & 1) != 0) ? 1000.0 : 0;
    edgeClip.x = ((edgeFlag & 2) != 0) ? 1000.0 : 0;
    edgeClip.y = ((edgeFlag & 4) != 0) ? 1000.0 : 0;

    ProcessEdgeDistance(Peye0, Peye1, Peye2, Peye3, edgeClip);
}

void ProcessQuadEdgeDistance(vec4 Peye0, vec4 Peye1, vec4 Peye2, vec4 Peye3,
                             int triCounter)
{
    // see hd/meshUtil.cpp
    //  Case                   EdgeFlag    Draw
    //  Quad/Refined face         0        hide common edge for the tri-pair
    //  Non-Quad face             1/2/3    hide common edge for the tri-pair & 
    //                                     hide interior quadrangulated edges
    //
    //  The first quad of a non-quad face is marked 1; the last as 2; and
    //  intermediate quads as 3.

    int edgeFlag = GetEdgeFlag(0);
    
    //  0---3
    //  |. 0|
    //  | . |
    //  |1 .|
    //  1---2
    //
    // triCounter == 0 : skip the second edge of (3,0,2), i.e., (0-2)
    // triCounter == 1 : skip the first  edge of (2,0,1), i.e., (2-0)
    vec3 edgeClip = triCounter == 0 ? vec3(0, 1000.0, 0) : vec3(1000.0, 0, 0);

    if (edgeFlag != 0) {
        // the quad face stems from quadrangulation
        // 0   - original (coarse) vertex
        // 1,3 - edge vertices
        // 2   - center vertex
        
        // skip the third edge (i.e. edge-center, 2-3 or 1-2 depending on
        // triCounter).
        edgeClip.z = 1000.0;
    }

    ProcessEdgeDistance(Peye0, Peye1, Peye2, Peye3, edgeClip);
}

--- --------------------------------------------------------------------------
-- glsl MeshWire.Fragment.NoFilter

void ApplySelectionFilter()
{
    // do nothing
}

--- --------------------------------------------------------------------------
-- glsl MeshWire.Fragment.FilterElementSelActive

// Fwd decl methods used (from selection.glslfx)
int GetActiveSelectionMode();
bool IsFaceSelected(int mode, int elementId);
int GetElementID(); // code gen

void ApplySelectionFilter()
{
    if (!IsFaceSelected(GetActiveSelectionMode(), GetElementID())) {
        discard;
    }
}

--- --------------------------------------------------------------------------
-- glsl MeshWire.Fragment.FilterElementSelRollover

// Fwd decl methods used (from selection.glslfx)
int GetRolloverSelectionMode();
bool IsFaceSelected(int mode, int elementId);
int GetElementID(); // code gen

void ApplySelectionFilter()
{
    if (!IsFaceSelected(GetRolloverSelectionMode(), GetElementID())) {
        discard;
    }
}

--- --------------------------------------------------------------------------
-- glsl MeshWire.Fragment.EdgeCommon

in EdgeData
{
    noperspective vec3 edgeDistance;
} inEdgeData;

// Returns the distance of the current fragment (in viewport pixel units) from
// the nearest edge.
float GetMinEdgeDistance()
{
    vec3 edgeDistance = inEdgeData.edgeDistance;
    return min(edgeDistance.x,
               min(edgeDistance.y,
                   edgeDistance.z));
}

// Use edge distance to compute a smooth opacity falloff for good looking edges.
float GetEdgeOpacity() {
    float d = GetMinEdgeDistance();
    float p = exp2(-4 * d * d);
    return p;
}

// Override for subdivided faces to make the boundary of the face stand out.
float GetEdgeOpacity(vec2 patchCoord) {
    float d = GetMinEdgeDistance();

    // Is the fragment on a face boundary?
    if (patchCoord.x < 0.01 ||
        patchCoord.x > 0.99 ||
        patchCoord.y < 0.01 ||
        patchCoord.y > 0.99) d *= 0.25;

    float p = exp2(-4 * d * d);
    return p;
}

--- --------------------------------------------------------------------------
-- glsl MeshWire.Fragment.NoEdge

vec4 ApplyEdgeColor(vec4 Cfill, vec4 patchCoord)
{
    return Cfill;
}

// Return a large value, signifying that the fragment isn't near an edge.
float GetMinEdgeDistance()
{
    return 1000.0;
}

--- --------------------------------------------------------------------------
-- glsl MeshWire.Fragment.EdgeOnSurface

vec4 ApplyEdgeColor(vec4 Cfill, vec4 patchCoord)
{
    // Skip the selection filter since the line rasterization draw order issue 
    // isn't relevant.
    // XXX: We don't use the patchCoord below to make the face boundary
    // stand out. Should we?
    float p = GetEdgeOpacity(/*patchCoord.xy*/);
    
    vec4 wireColor = GetWireframeColor();
    
    // If wireColor is unset (zero), the fill color is just dimmed a bit.
    if (wireColor == vec4(0)) wireColor.a = 0.5;

    vec4 Cedge = vec4(mix(Cfill.rgb, wireColor.rgb, wireColor.a), 1);
    Cfill.rgb = mix(Cfill.rgb, Cedge.rgb, p);

    return Cfill;
}

--- --------------------------------------------------------------------------
-- glsl MeshWire.Fragment.EdgeOnlyBlendColor

void ApplySelectionFilter();

vec4 ApplyEdgeColor(vec4 Cfill, vec4 patchCoord)
{
    ApplySelectionFilter();

    float p = GetEdgeOpacity();
    if (p < 0.5) discard;

    vec4 wireColor = GetWireframeColor();

    // If wireColor is unset (zero), ignore it altogether
    
    Cfill.rgb = mix(Cfill.rgb, wireColor.rgb, wireColor.a);
    Cfill.a = 1.0; // edges ignore input opacity and are opaque.

    return Cfill;
}

--- --------------------------------------------------------------------------
-- glsl MeshWire.Fragment.EdgeOnlyNoBlend

void ApplySelectionFilter();

vec4 ApplyEdgeColor(vec4 Cfill, vec4 patchCoord)
{
    ApplySelectionFilter();

    float p = GetEdgeOpacity();
    if (p < 0.5) discard;

    Cfill.a = 1.0; // edges ignore input opacity and are opaque.
    return Cfill;
}

--- --------------------------------------------------------------------------
-- glsl MeshPatchWire.Fragment.EdgeOnSurface

vec4 ApplyEdgeColor(vec4 Cfill, vec4 patchCoord)
{
    // Skip the selection filter since the line rasterization draw order issue
    // isn't relevant.
    float p = GetEdgeOpacity(patchCoord.xy);
    
    vec4 wireColor = GetWireframeColor();

    // If wireColor is unset (zero), the fill color is just dimmed a bit.
    if (wireColor == vec4(0)) wireColor.a = 0.5;

    vec4 Cedge = vec4(mix(Cfill.rgb, wireColor.rgb, wireColor.a), 1);
    Cfill.rgb = mix(Cfill.rgb, Cedge.rgb, p);

    return Cfill;
}

--- --------------------------------------------------------------------------
-- glsl MeshPatchWire.Fragment.EdgeOnly

void ApplySelectionFilter();

vec4 ApplyEdgeColor(vec4 Cfill, vec4 patchCoord)
{
    ApplySelectionFilter();

    float p = GetEdgeOpacity();
    if (p < 0.5) discard;

    Cfill.a = 1.0; // edges ignore input opacity and are opaque.

    return Cfill;
}
