memononen / nanovg

Antialiased 2D vector drawing library on top of OpenGL for UI and visualizations.
zlib License
5.15k stars 771 forks source link

Failed to fill self-crossing polygon #569

Open jiandingzhe opened 4 years ago

jiandingzhe commented 4 years ago

I rendered a sandglass-like shape with following code:

    nvgBeginPath(nvg);
    nvgMoveTo(nvg, 0, 0);
    nvgLineTo(nvg, 100, 75);
    nvgLineTo(nvg, 100, 25);
    nvgLineTo(nvg, 0, 100);

    nvgFillColor( nvg, nvgRGB(255, 0, 0) );
    nvgFill(nvg);

It filled the area of whole square bounding box.

In addition, if I render such kind of thing with non-full alpha, it would also show a very dim stroked path of that shape.

Does nanovg allow to draw polygons like that? Or I have to manually separate it into more "healthy" polygons?

jiandingzhe commented 4 years ago

I made some more tests on different computers, and it looks like a platform-specific issue.

Incorrect filling:

Using GL2 or GL3 backend seems unrelated.

jiandingzhe commented 4 years ago

I made a more straightforward test. It also failed to fill non-convex shapes. The whole code is pasted below, it could be compiled with nanovg, SDL2 and glew:

#include <GL/glew.h>

#define SDL_MAIN_HANDLED
#include <SDL.h>

#include <nanovg.h>

#define NANOVG_GL3_IMPLEMENTATION
#include <nanovg_gl.h>

#ifdef _WIN32
int WinMain()
#else
int main()
#endif
{

    SDL_Init( SDL_INIT_EVERYTHING );

    auto win = SDL_CreateWindow( "nanovg sdl test", 100, 100, 600, 400, SDL_WINDOW_OPENGL );
    auto gl = SDL_GL_CreateContext( win );

    glewInit();

    auto nvg = nvgCreateGL3( NVG_ANTIALIAS | NVG_STENCIL_STROKES | NVG_DEBUG );

    while ( true )
    {
        int w, h;
        SDL_GetWindowSize(win, &w, &h);
        glClearColor( 1, 1, 1, 1 );
        glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_ACCUM_BUFFER_BIT );

        nvgBeginFrame(nvg, w, h, 1);

        // a timeglass-shape fill
        nvgBeginPath( nvg );
        nvgMoveTo( nvg, 50, 50 );
        nvgLineTo( nvg, 200, 200 );
        nvgLineTo( nvg, 200, 50 );
        nvgLineTo( nvg, 50, 200 );
        nvgFillColor( nvg, nvgRGBA( 255, 0, 0, 64 ) );
        nvgFill( nvg );

        // a non-convex fill
        nvgBeginPath(nvg);
        nvgMoveTo(nvg, 220, 50);
        nvgLineTo(nvg, 230, 40);
        nvgLineTo(nvg, 240, 45);
        nvgLineTo(nvg, 250, 70);
        nvgLineTo(nvg, 260, 20);
        nvgLineTo(nvg, 270, 100);
        nvgLineTo(nvg, 280, 80);
        nvgLineTo(nvg, 290, 30);
        nvgLineTo(nvg, 300, 60);
        nvgLineTo(nvg, 270, 250);
        nvgLineTo(nvg, 230, 250);
        nvgFillColor( nvg, nvgRGBA( 255, 0, 255, 64 ) );
        nvgFill( nvg );

        // simple triangle
        nvgBeginPath(nvg);
        nvgMoveTo(nvg, 330, 150);
        nvgLineTo(nvg, 450, 200);
        nvgLineTo(nvg, 320, 350);
        nvgFillColor( nvg, nvgRGBA( 0, 0, 255, 64 ) );
        nvgFill(nvg);

        nvgEndFrame(nvg);

        SDL_GL_SwapWindow( win );

        SDL_Event e;
        bool should_break = false;
        while ( SDL_PollEvent( &e ) )
        {
            if (e.type == SDL_WINDOWEVENT)
            {
                if (e.window.event == SDL_WINDOWEVENT_CLOSE)
                    should_break = true;
            }
        }

        if (should_break)
            break;
    }

    nvgDeleteGL3( nvg );
    SDL_GL_DeleteContext( gl );
    SDL_DestroyWindow( win );
}
jiandingzhe commented 4 years ago

Ok I've found the problem. Stencil buffer is not turned on by default, in both SDL and the actual environment I use (JUCE).