summaryrefslogtreecommitdiffstats
path: root/res/effectlib/tessellationPath.glsllib
blob: 4758a4108036bc8eb048896a9488fdc9c9538082 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#ifndef TESSELLATION_PATH_GLSLLIB
#define TESSELLATION_PATH_GLSLLIB


#if TESSELLATION_CONTROL_SHADER
layout (vertices = 5) out;

void tessShader ( in float edgeTessAmount, float innerTessAmount )
{
    gl_TessLevelOuter[0] = innerTessAmount;
    gl_TessLevelOuter[1] = edgeTessAmount;
    gl_TessLevelOuter[2] = innerTessAmount;
    gl_TessLevelOuter[3] = edgeTessAmount;

    gl_TessLevelInner[0] = edgeTessAmount / 2.0;
    gl_TessLevelInner[1] = innerTessAmount;
}

#endif

#if TESSELLATION_EVALUATION_SHADER
layout (quads, equal_spacing, cw) in;

vec2 getTangent(vec2 p0, vec2 p1, vec2 p2, vec2 p3, float t)
{
    // derivative
    float dbt0 = -3.0 * (1.0 - t) * (1.0 - t);
    float dbt1 = 3.0 * (1.0 - t) * (1.0 - 3.0 * t);
    float dbt2 = 3.0 * t * (2.0 - 3.0 * t);
    float dbt3 = 3.0 * t * t;

    // tangent on curve
    return normalize( dbt0 * p0 + dbt1 * p1 + dbt2 * p2 + dbt3 * p3 );
}
//An exact cross product would involve normalizing dx,dy.  Since
//this algorithm needs merely the sign, normalization is not necessary.
float roughCrossProd( vec2 prev, vec2 point, vec2 next )
{
    vec2 inDxDy = point - prev;
    vec2 outDxDy = next - point;
    return inDxDy.x * outDxDy.y - inDxDy.y * outDxDy.x;
}

//The incoming corss product tells us both if we should do a seam merge
//and if the merge is above or below point in gl_TessCoord.y space; we know the
//anchor point is at .5 gl_TessCoord.y space.
vec3 computeAdjoiningFactors( float cross, vec2 adjoining, vec2 point, float tessY )
{
    vec3 retval = vec3( 0.0, 0.0, 0.0 );
    float multiplier = cross < 0.0 ? 1.0 : -1.0;
    float weight = abs(cross) > 0.001 ? multiplier * ( ( tessY - .5 )/ .5 ) : 0.0;
    retval.z = weight > 0.0 ? 1.0 : 0.0;
    retval.xy = mix( point, adjoining, weight );
    return retval;
}

#define NO_TAPER 0
#define BEGIN_TAPER 1
#define END_TAPER 2

//Tapering is done by interpolating the path width somewhat cleverly.
float getTaperResult( float inIncomingValue, float inBeginValue, float inEndValue, vec2 taperData, uint inMode )
{
    float mixInfo = mix( taperData.x, taperData.y, gl_TessCoord.x );
    float theValueMixer = inMode == BEGIN_TAPER ? inBeginValue : inEndValue;
    return mix( theValueMixer, inIncomingValue, mixInfo );
}

uniform vec2 beginTaperInfo;
uniform vec2 endTaperInfo;

struct STessShaderResult
{
    vec3     m_Position;
    vec2    m_TexCoord;
    vec2    m_Tangent;
    vec2       m_Binormal;
    float     m_Opacity;
};

STessShaderResult tessShader ( float inPathWidth )
{
    vec2 p1 = gl_in[0].gl_Position.xy;
    vec2 c1 = gl_in[0].gl_Position.zw; //c1
    vec2 c2 = gl_in[1].gl_Position.xy; //c2
    vec2 p2 = gl_in[1].gl_Position.zw;
    vec4 taperData = gl_in[3].gl_Position;
    vec2 udata = gl_in[4].gl_Position.xy;

    //Adjust width for taper if necessary.
    inPathWidth = taperData.z > 0.0 ? getTaperResult( inPathWidth, beginTaperInfo.x, endTaperInfo.x, taperData.xy, uint(taperData.z) ) : inPathWidth;

    float adjoiningWeight = 0.0;
    vec2 adjoining = vec2( 0.0, 0.0 );
    if ( gl_TessCoord.x == floor(gl_TessCoord.x) )  // gl_TessCord.x either 0.0 or 1.0
    {
        vec2 point, cross1, cross2;
        if ( gl_TessCoord.x == 0.0 )
        {
            adjoining = gl_in[2].gl_Position.xy;
            point = p1;
            cross1 = adjoining;
            cross2 = c1;
        }
        else // gl_TessCoord.x == 1.0
        {
            adjoining = gl_in[2].gl_Position.zw;
            point = p2;
            cross1 = c2;
            cross2 = adjoining;
        }
        float cross = roughCrossProd( cross1, point, cross2 );
        vec3 adjoiningFactors = computeAdjoiningFactors( cross, adjoining, point, gl_TessCoord.y );
        adjoining = adjoiningFactors.xy;
        adjoiningWeight = adjoiningFactors.z;
    }

    float v = gl_TessCoord.x;
    // cubic basis function calculated from v.
    float bv0 = (1.0 - v) * (1.0 - v) * (1.0 - v);
    float bv1 = 3.0 * v * (1.0 - v) * (1.0 - v);
    float bv2 = 3.0 * v * v * (1.0 - v);
    float bv3 = v * v * v;

    //u ranges from 0 - 1.  What we want is to
    //have u range from -1,1.
    float u = 2.0 * ( gl_TessCoord.y - .5 );

    vec2 tangent = getTangent( p1, c1, c2, p2, v );
    vec2 normal = vec2( tangent.y, -tangent.x );

    vec2 offset = normal * inPathWidth * u;

    vec2 pointOnPath = bv0*p1 + bv1*c1 + bv2*c2 + bv3*p2;

    vec2 finalPosXY = offset + pointOnPath;

    STessShaderResult retval;
    retval.m_Position = vec3( mix( finalPosXY, adjoining, adjoiningWeight), 0.0 );
    retval.m_Opacity = taperData.z > 0.0 ? getTaperResult( 1.0, beginTaperInfo.y, endTaperInfo.y, taperData.xy, uint(taperData.z) ) : 1.0;

    // cubic interpolation of the texture coords
    retval.m_TexCoord.x = mix( udata.x, udata.y, v );
    retval.m_TexCoord.y = gl_TessCoord.y;
    retval.m_Tangent = tangent;
    retval.m_Binormal = normal;

    return retval;
}
#endif

#endif