Commit 20c86db7 authored by Maciej Suminski's avatar Maciej Suminski
Browse files

1 pixel wide lines using OpenGL shaders.

parent 64122ae0
Loading
Loading
Loading
Loading
+42 −49
Original line number Diff line number Diff line
@@ -783,22 +783,23 @@ void OPENGL_GAL::DrawCircle( const VECTOR2D& aCenterPoint, double aRadius )
            color4( fillColor.r, fillColor.g, fillColor.b, fillColor.a );

            /* Draw a triangle that contains the circle, then shade it leaving only the circle.
               Parameters given to setShader are relative coordinates of the triangle's vertices.
               Parameters given to setShader are indices of the triangle's vertices
               (if you want to understand more, check the vertex shader source [shader.vert]).
               Shader uses this coordinates to determine if fragments are inside the circle or not.
                    v2
                    /\
                   //\\
               v0 /_\/_\ v1
            */
            setShader( SHADER_FILLED_CIRCLE, -sqrt( 3.0f ), -1.0f );
            vertex3( aCenterPoint.x - aRadius * sqrt( 3.0f ),                           // v0
                     aCenterPoint.y - aRadius, layerDepth );
            setShader( SHADER_FILLED_CIRCLE, 1.0 );
            vertex3( aCenterPoint.x - aRadius * sqrt( 3.0f ),
                     aCenterPoint.y - aRadius, layerDepth );                        // v0

            setShader( SHADER_FILLED_CIRCLE, sqrt( 3.0f ), -1.0f );
            vertex3( aCenterPoint.x + aRadius * sqrt( 3.0f ),                           // v1
                     aCenterPoint.y - aRadius, layerDepth );
            setShader( SHADER_FILLED_CIRCLE, 2.0 );
            vertex3( aCenterPoint.x + aRadius * sqrt( 3.0f ),
                     aCenterPoint.y - aRadius, layerDepth );                        // v1

            setShader( SHADER_FILLED_CIRCLE, 0.0f, 2.0f );
            setShader( SHADER_FILLED_CIRCLE, 3.0 );
            vertex3( aCenterPoint.x, aCenterPoint.y + aRadius * 2.0f, layerDepth ); // v2
        }

@@ -807,30 +808,25 @@ void OPENGL_GAL::DrawCircle( const VECTOR2D& aCenterPoint, double aRadius )
            color4( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a );

            /* Draw a triangle that contains the circle, then shade it leaving only the circle.
               Parameters given to setShader are relative coordinates of the triangle's vertices
               Parameters given to setShader are indices of the triangle's vertices
               (if you want to understand more, check the vertex shader source [shader.vert]).
               and the line width. Shader uses this coordinates to determine if fragments are inside
               the circle or not. Width parameter has to be passed as a ratio of inner radius
               to outer radius.
               the circle or not.
                    v2
                    /\
                   //\\
               v0 /_\/_\ v1
            */
            float outerRadius = aRadius + ( lineWidth / 2.0f );
            float innerRadius = aRadius - ( lineWidth / 2.0f );
            float relWidth = innerRadius / outerRadius;
            setShader( SHADER_STROKED_CIRCLE, 1.0, aRadius, lineWidth );
            vertex3( aCenterPoint.x - aRadius * sqrt( 3.0f ),
                     aCenterPoint.y - aRadius, layerDepth );                        // v0

            setShader( SHADER_STROKED_CIRCLE, -sqrt( 3.0f ), -1.0f, relWidth );
            vertex3( aCenterPoint.x - outerRadius * sqrt( 3.0f ),                           // v0
                     aCenterPoint.y - outerRadius, layerDepth );
            setShader( SHADER_STROKED_CIRCLE, 2.0, aRadius, lineWidth );
            vertex3( aCenterPoint.x + aRadius * sqrt( 3.0f ),
                     aCenterPoint.y - aRadius, layerDepth );                        // v1

            setShader( SHADER_STROKED_CIRCLE, sqrt( 3.0f ), -1.0f, relWidth );
            vertex3( aCenterPoint.x + outerRadius * sqrt( 3.0f ),                           // v1
                     aCenterPoint.y - outerRadius, layerDepth );

            setShader( SHADER_STROKED_CIRCLE, 0.0f, 2.0f, relWidth );
            vertex3( aCenterPoint.x,                                                        // v2
                     aCenterPoint.y + outerRadius * 2.0f, layerDepth );
            setShader( SHADER_STROKED_CIRCLE, 3.0, aRadius, lineWidth );
            vertex3( aCenterPoint.x, aCenterPoint.y + aRadius * 2.0f, layerDepth ); // v2
        }

        return;
@@ -931,20 +927,21 @@ void OPENGL_GAL::drawFilledSemiCircle( const VECTOR2D& aCenterPoint, double aRad
        Rotate( aAngle );

        /* Draw a triangle that contains the semicircle, then shade it to leave only the semicircle.
                       Parameters given to setShader are relative coordinates of the triangle's vertices.
           Parameters given to setShader are indices of the triangle's vertices
           (if you want to understand more, check the vertex shader source [shader.vert]).
           Shader uses this coordinates to determine if fragments are inside the semicircle or not.
                v2
                /\
               /__\
           v0 //__\\ v1
         */
        setShader( SHADER_FILLED_CIRCLE, -3.0f / sqrt( 3.0f ), 0.0f );
        setShader( SHADER_FILLED_CIRCLE, 4.0f );
        vertex3( -aRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth );                // v0

        setShader( SHADER_FILLED_CIRCLE, 3.0f / sqrt( 3.0f ), 0.0f );
        setShader( SHADER_FILLED_CIRCLE, 5.0f );
        vertex3( aRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth );                 // v1

        setShader( SHADER_FILLED_CIRCLE, 0.0f, 2.0f );
        setShader( SHADER_FILLED_CIRCLE, 6.0f );
        vertex3( 0.0f, aRadius * 2.0f, layerDepth );                                // v2

        Restore();
@@ -973,26 +970,22 @@ void OPENGL_GAL::drawStrokedSemiCircle( const VECTOR2D& aCenterPoint, double aRa
        Rotate( aAngle );

        /* Draw a triangle that contains the semicircle, then shade it to leave only the semicircle.
                   Parameters given to setShader are relative coordinates of the triangle's vertices
                   and the line width. Shader uses this coordinates to determine if fragments are inside
                   the semicircle or not. Width parameter has to be passed as a ratio of inner radius
                   to outer radius.
           Parameters given to setShader are indices of the triangle's vertices
           (if you want to understand more, check the vertex shader source [shader.vert]), the
           radius and the line width. Shader uses this coordinates to determine if fragments are
           inside the semicircle or not.
                v2
                /\
               /__\
           v0 //__\\ v1
         */
        float outerRadius = aRadius;
        float innerRadius = aRadius - lineWidth;
        float relWidth = innerRadius / outerRadius;

        setShader( SHADER_STROKED_CIRCLE, -3.0f / sqrt( 3.0f ), 0.0f, relWidth );
        setShader( SHADER_STROKED_CIRCLE, 4.0f, aRadius, lineWidth );
        vertex3( -aRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth );                // v0

        setShader( SHADER_STROKED_CIRCLE, 3.0f / sqrt( 3.0f ), 0.0f, relWidth );
        setShader( SHADER_STROKED_CIRCLE, 5.0f, aRadius, lineWidth );
        vertex3( aRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth );                 // v1

        setShader( SHADER_STROKED_CIRCLE, 0.0f, 2.0f, relWidth );
        setShader( SHADER_STROKED_CIRCLE, 6.0f, aRadius, lineWidth );
        vertex3( 0.0f, aRadius * 2.0f, layerDepth );                                // v2

        Restore();
+10 −5
Original line number Diff line number Diff line
@@ -32,20 +32,25 @@ const float SHADER_FILLED_CIRCLE = 2.0;
const float SHADER_STROKED_CIRCLE       = 3.0;

varying vec4 shaderParams;
varying vec2 circleCoords;

void filledCircle( vec2 aCoord )
{
    if( dot( aCoord, aCoord ) < 1.0 )
    if( dot( aCoord, aCoord ) < 1.0f )
        gl_FragColor = gl_Color;
    else
        discard;
}


void strokedCircle( vec2 aCoord, float aWidth )
void strokedCircle( vec2 aCoord, float aRadius, float aWidth )
{
    float outerRadius = aRadius;
    float innerRadius = aRadius - aWidth;
    float relWidth = innerRadius / outerRadius;

    if( ( dot( aCoord, aCoord ) < 1.0 ) && 
        ( dot( aCoord, aCoord ) > aWidth * aWidth ) )
        ( dot( aCoord, aCoord ) > relWidth * relWidth ) )
        gl_FragColor = gl_Color;
    else
        discard;
@@ -56,11 +61,11 @@ void main()
{
    if( shaderParams[0] == SHADER_FILLED_CIRCLE )
    {
        filledCircle( vec2( shaderParams[1], shaderParams[2] ) );
        filledCircle( circleCoords );
    }
    else if( shaderParams[0] == SHADER_STROKED_CIRCLE )
    {
        strokedCircle( vec2( shaderParams[1], shaderParams[2] ), shaderParams[3] );
        strokedCircle( circleCoords, shaderParams[2], shaderParams[3] );
    }
    else
    {
+37 −2
Original line number Diff line number Diff line
@@ -31,8 +31,12 @@ const float SHADER_LINE = 1.0;
const float SHADER_FILLED_CIRCLE        = 2.0;
const float SHADER_STROKED_CIRCLE       = 3.0;

// Minimum line width
const float MIN_WIDTH = 1.0;

attribute vec4 attrShaderParams;
varying vec4 shaderParams;
varying vec2 circleCoords;

void main()
{
@@ -45,8 +49,8 @@ void main()
        float worldScale = gl_ModelViewMatrix[0][0];
        float scale;
     
        // Make lines appear to be at least 1 pixel width
        if( worldScale * lineWidth < 1.0 )
        // Make lines appear to be at least 1 pixel wide
        if( worldScale * lineWidth < MIN_WIDTH )
            scale = 1.0 / ( worldScale * lineWidth );
        else
            scale = 1.0;
@@ -54,6 +58,37 @@ void main()
        gl_Position = gl_ModelViewProjectionMatrix * 
            ( gl_Vertex + vec4( shaderParams.yz * scale, 0.0, 0.0 ) );
    }
    else if( ( shaderParams[0] == SHADER_STROKED_CIRCLE ) || 
             ( shaderParams[0] == SHADER_FILLED_CIRCLE  ) )
    {
        // Compute relative circle coordinates basing on indices
        // Circle
        if( shaderParams[1] == 1.0f )
            circleCoords = vec2( -sqrt( 3.0f ), -1.0f );
        else if( shaderParams[1] == 2.0f )
            circleCoords = vec2( sqrt( 3.0f ), -1.0f );
        else if( shaderParams[1] == 3.0f )
            circleCoords = vec2( 0.0f, 2.0f );
            
        // Semicircle
        else if( shaderParams[1] == 4.0f )
            circleCoords = vec2( -3.0f / sqrt( 3.0f ), 0.0f );
        else if( shaderParams[1] == 5.0f )
            circleCoords = vec2( 3.0f / sqrt( 3.0f ), 0.0f );
        else if( shaderParams[1] == 6.0f )
            circleCoords = vec2( 0.0f, 2.0f );

        // Make the line appear to be at least 1 pixel wide
        float lineWidth = shaderParams[3];
        float worldScale = gl_ModelViewMatrix[0][0];
        float scale;
     
        // Make lines appear to be at least 1 pixel width
        if( worldScale * lineWidth < MIN_WIDTH )
            shaderParams[3] = shaderParams[3] / ( worldScale * lineWidth );

        gl_Position = ftransform();
    }
    else
    {
        // Pass through the coordinates like in the fixed pipeline