Search code examples
c++sdl-2

Generate the vertices of a circle in SDL2 (C++)


Is there a way to automagically generate the vertices of a circle? I know how to render it and all that I just need a way to input a set amount of vertices and then generate the circle's vertices (the number of them) based on that number.


Solution

  • One way you can generate the vertices to approximate a circle is as follows:

    void GenerateCircle(std::vector<SDL_Vertex> &outCircleVertices, size_t vertexCount, int radius, std::vector<int> &outIndices)
    {
        // Size our vector so it can hold all the requested vertices plus our center one
        outCircleVertices.resize(vertexCount + 1);
    
        // Calculate the angle we'll need to rotate by for each iteration (* (PI / 180) to convert it into radians)
        double segRotationAngle = (360.0 / vertexCount) * (3.14159265 / 180);
    
        // Here I set a center in the middle of the window as an example. The center point of the circle can be anywhere
        int centerX = WINDOW_WIDTH / 2;
        int centerY = WINDOW_HEIGHT / 2;
    
        // We need an initial vertex in the center as a point for all of the triangles we'll generate
        outCircleVertices[0].position.x = centerX;
        outCircleVertices[0].position.y = centerY;
    
        // Set the colour of the center point
        outCircleVertices[0].color.r = 255;
        outCircleVertices[0].color.g = 255;
        outCircleVertices[0].color.b = 255;
        outCircleVertices[0].color.a = 255;
    
        // Set the starting point for the initial generated vertex. We'll be rotating this point around the origin in the loop
        double startX = 0.0 - radius;
        double startY = 0.0;
    
        for (int i = 1; i < vertexCount + 1; i++)
        {
            // Calculate the angle to rotate the starting point around the origin
            double finalSegRotationAngle = (i * segRotationAngle);
    
            // Rotate the start point around the origin (0, 0) by the finalSegRotationAngle (see https://en.wikipedia.org/wiki/Rotation_(mathematics) section on two dimensional rotation)
            outCircleVertices[i].position.x = cos(finalSegRotationAngle) * startX - sin(finalSegRotationAngle) * startY;
            outCircleVertices[i].position.y = cos(finalSegRotationAngle) * startY + sin(finalSegRotationAngle) * startX;
    
            // Set the point relative to our defined center (in this case the center of the screen)
            outCircleVertices[i].position.x += centerX;
            outCircleVertices[i].position.y += centerY;
    
            // Set the colour for the vertex
            outCircleVertices[i].color.r = 255;
            outCircleVertices[i].color.g = 255;
            outCircleVertices[i].color.b = 255;
            outCircleVertices[i].color.a = 255;
    
            // Add centre point index
            outIndices.push_back(0);
    
            // Add generated point index
            outIndices.push_back(i);
    
            // Add next point index (with logic to wrap around when we reach the start)
            int index = (i + 1) % vertexCount;
            if (index == 0)
            {
                index = vertexCount;
            }
            outIndices.push_back(index);
        }
    }
    

    Each stage is commented up with an explanation to help with understanding. In my example here I have opted not to duplicate any vertices and instead use indices to describe to the drawing function how it should use each of the vertices to generate triangles. This is a common practice in graphics programming - you'd see this in OpenGL for example.

    To use this function simply add

    std::vector<SDL_Vertex> circleVertices;
    std::vector<int> indices;
    
    GenerateCircle(circleVertices, 20, 60, indices);
    

    in your initialisation and then add

    int rc = SDL_RenderGeometry(renderer, NULL, circleVertices.data(), circleVertices.size(), indices.data(), indices.size());
    if (rc < 0)
    {
        std::cout << SDL_GetError();
    }
    

    in your rendering loop between the SDL_RenderClear and SDL_RenderPresent calls. Note that you can change the vertexCount that you pass in to generate a circle with more or less triangles for a better or worse approximation. Please also note that if you use the function as is you'll be drawing a white circle, just in case you have a white background and wonder where it is!