Support for drawing triangles

Has there been any consideration for extending SDL_Renderer with the
ability to render 2d triangles?

Since the Renderer has the ability to rasterize lines and quads it
doesn’t seem such a far stretch to add
triangle support, which would open up many interesting opportunities.

Does anyone have ideas about how feasible this is? Would the community
be open to this? What may
I have to consider if I was to add this to SDL2?

My initial idea would be something like this:

// draw a triangle on screen using the current draw colour
int SDL_RenderFillTriangle( SDL_Renderer* renderer, const SDL_Point point[3] );

// draw a collection of triangles stored in a vertex list
int SDL_RenderFillTriangles( SDL_Renderer* renderer, const SDL_Point* point, int count );

1 Like

Gabriel Jacobo proposed a direct triangle rendering function as
"SDL_RenderGeometry", which hasn’t been worked into the mainline SDL2 yet.
It is more useful than just flat-filled triangles. You might look at his
patch on this mailing list.

Also, SDL_gpu has the equivalent functionality already.

Jonny DOn Fri, Jun 13, 2014 at 9:18 AM, Aidan Dodds wrote:

Has there been any consideration for extending SDL_Renderer with the
ability to render 2d triangles?

Since the Renderer has the ability to rasterize lines and quads it doesn’t
seem such a far stretch to add
triangle support, which would open up many interesting opportunities.

Does anyone have ideas about how feasible this is? Would the community be
open to this? What may
I have to consider if I was to add this to SDL2?

My initial idea would be something like this:

// draw a triangle on screen using the current draw colour
int SDL_RenderFillTriangle( SDL_Renderer* renderer, const SDL_Point
point[3] );

// draw a collection of triangles stored in a vertex list
int SDL_RenderFillTriangles( SDL_Renderer* renderer, const SDL_Point*
point, int count );


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org

I know that this is an old post but because SDL has been updated I thought my example of a spinning triangle I wrote would be helpful. This is a complete program.

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <SDL.h>

int width=1280,height=720;
int loop,fps=60,delay,sdl_time,sdl_time1;
SDL_Window *window = NULL;
SDL_Renderer *renderer = NULL;
SDL_Event e;

struct polygon
{
 double cx,cy;
 int sides;
 double radius;
 double radians;
 SDL_Color color;
 int step; /*used only in star polygons of 5 or more sides*/
};

struct polygon main_polygon;

/*Define PI the same as M_PI in math.h*/
 #define PI 3.14159265358979323846f

void init_polygon()
{
 main_polygon.cx=width/2;
 main_polygon.cy=height/2;
 main_polygon.sides=3;
 main_polygon.step=2;
 main_polygon.radius=height/2;
 main_polygon.radians=0;

 main_polygon.color.r=255;
 main_polygon.color.g=255;
 main_polygon.color.b=255;
 main_polygon.color.a=255;
}

/*these point arrays are temporary and not included in the polygon structure*/
float polygon_xpoints[0x1000],polygon_ypoints[0x1000];

/*
function to get the points of a regular polygon and load them into the above arrays of x and y points
*/
void chaste_polygon_points()
{
 double angle,x,y;
 int i=0;
 while(i<main_polygon.sides)
 {
  angle=2*PI*i/main_polygon.sides+main_polygon.radians;
  x=main_polygon.cx+sin(angle)*main_polygon.radius;
  y=main_polygon.cy-cos(angle)*main_polygon.radius;
  polygon_xpoints[i]=x;
  polygon_ypoints[i]=y;
  i++;
 }
}

/* large array for any possible combination of vertices to make many triangles*/
SDL_Vertex vertices[0x1000];

/*
 this first function draws a series of triangles to make any kind of regular polygon
 this includes star polygons by taking into account the step value between points.
*/
void chaste_polygon_draw_star()
{
 int i,i1;
 chaste_polygon_points();
 
 vertices[0].color=main_polygon.color;
 vertices[1].color=main_polygon.color;
 vertices[2].color=main_polygon.color;
 
 /*chaste_triangle_color_rgb();*/
 
 i=0;
 while(i<main_polygon.sides)
 {
  i1=(i+main_polygon.step)%main_polygon.sides;
  
  /*for each part of this loop,construct a triangle*/
  vertices[0].position.x=polygon_xpoints[i];
  vertices[0].position.y=polygon_ypoints[i];
  vertices[1].position.x=polygon_xpoints[i1];
  vertices[1].position.y=polygon_ypoints[i1];
  vertices[2].position.x=main_polygon.cx;
  vertices[2].position.y=main_polygon.cy;
  
  SDL_RenderGeometry(renderer,NULL,vertices,3,NULL,0);

  i++;
 }
 
}


int main(int argc, char** argv)
{
 if(SDL_Init(SDL_INIT_VIDEO)){printf( "SDL could not initialize! SDL_Error: %s\n",SDL_GetError());return -1;}

 window = SDL_CreateWindow( "SDL Chaste Triangle",SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,width,height,0);
 if(window==NULL){printf( "Window could not be created! SDL_Error: %s\n", SDL_GetError() );return -1;}
 
 renderer = SDL_CreateRenderer(window,-1,0);
 if(renderer==NULL){printf( "Renderer could not be created! SDL_Error: %s\n", SDL_GetError() );return -1;}
 
 init_polygon();
 main_polygon.radius=350;
 main_polygon.sides=3;
 main_polygon.step=2;

 delay=1000/fps;
 loop=1;
 while(loop) /*wait until any key is pressed and then released*/
 {
  sdl_time = SDL_GetTicks();
  sdl_time1 = sdl_time+delay;
 
  SDL_SetRenderDrawColor(renderer,0,0,0,255);
  SDL_RenderClear(renderer);

  chaste_polygon_draw_star();

  main_polygon.radians+=PI/180;

  SDL_RenderPresent(renderer);
  
/*test for events and only process if they exist*/
  while(SDL_PollEvent(&e))
  {
   if( e.type == SDL_QUIT ){loop=0;}
   if(e.type == SDL_KEYUP)
   {
    if(e.key.keysym.sym==SDLK_ESCAPE){loop=0;}
   }
  }

  while(sdl_time<sdl_time1)
  {
   sdl_time=SDL_GetTicks();
  }
  
 }
 	
 SDL_DestroyRenderer(renderer);
 SDL_DestroyWindow(window);
 SDL_Quit();
 return 0;
}


But how does software rasterization with SDL_Texture and SDL_Renderer without SDL_Surface.

I have made lined triangle and clearcolor in C#

But I have problem with fillarea like GL_Triangle with fill-mode

namespace RasterizerTest;

using System.Runtime.InteropServices;
using DeafMan1983.Interop.SDL2;
using static DeafMan1983.Interop.SDL2.SDL;

unsafe class Program
{
    class Rasterizer
    {
        private uint* m_pixels;
        private int m_w, m_h;

        [DllImport("c")]
        public static extern void* memset(void* str, int c, nuint n);

        public void SetFrameBuffer(uint* pixels, int w, int h)
        {
            m_pixels = pixels;
            m_w = w;
            m_h = h;
        }

        // Without SDL_Surface and try to convert from SDL_Color to pixel (uint32)
        private uint ToPixel(SDL_Color color)
        {
            return ((uint)color.b << 16) | ((uint)color.g << 8) | (color.r);
        }

        public void ClearColor(SDL_Color color)
        {
            if (m_pixels == null)
                return;

            for (int x = 0; x < m_w; x++)
            {
                for (int y = 0; y < m_h; y++)
                {
                    SetPixel(x, y, color);
                }
            }
        }

        public void SetPixel(int x, int y, SDL_Color color)
        {
            if (x >= m_w || y >= m_h)
                return;

            m_pixels[y * m_w + x] = ToPixel(color);
        }

        public void SetPixel(float x, float y, SDL_Color color)
        {
            SetPixel((int)x, (int)y, color);
        }

        public void Clear(SDL_Window* window)
        {
            SDL_FreeSurface(SDL_GetWindowSurface(window));
        }

        public void DrawLine(SDL_Color color1, float x1, float y1,
                            SDL_Color color2, float x2, float y2)
        {
            float xdiff = x2 - x1;
            float ydiff = y2 - y1;

            if (xdiff == 0 && ydiff == 0)
            {
                SetPixel(x1, y1, color1);
                return;
            }

            if (MathF.Abs(xdiff) > MathF.Abs(ydiff))
            {
                float xmin, xmax;

                if (x1 < x2)
                {
                    xmin = x1;
                    xmax = x2;
                }
                else
                {
                    xmin = x2;
                    xmax = x1;
                }

                float slope = ydiff / xdiff;
                for (float x = xmin; x <= xmax; x += 1)
                {
                    float y = y1 + ((x - x1) * slope);
                    byte color_r = (byte)(color2.r + (color2.r - color1.r) * ((x - x1) / xdiff));
                    byte color_g = (byte)(color2.g + (color2.g - color1.g) * ((x - x1) / xdiff));
                    byte color_b = (byte)(color2.b + (color2.b - color1.b) * ((x - x1) / xdiff));
                    SDL_Color color = new SDL_Color{r = color_r, g = color_g, b = color_b};
                    SetPixel(x, y, color);
                }
            }
            else
            {
                float ymin, ymax;

                if (y1 < y2)
                {
                    ymin = y1;
                    ymax = y2;
                }
                else
                {
                    ymin = y2;
                    ymax = y1;
                }

                float slope = xdiff / ydiff;
                for (float y = ymin; y <= ymax; y += 1)
                {
                    float x = x1 + ((y - y1) * slope);
                    byte color_r = (byte)(color2.r + (color2.r - color1.r) * ((y - y1) / ydiff));
                    byte color_g = (byte)(color2.g + (color2.g - color1.g) * ((y - y1) / ydiff));
                    byte color_b = (byte)(color2.b + (color2.b - color1.b) * ((y - y1) / ydiff));
                    SDL_Color color = new SDL_Color{r = color_r, g = color_g, b = color_b};
                    SetPixel(x, y, color);
                }
            }
        }
    }

    const int width = 640;
    const int height = 480;

    static int Main(string[] args)
    {
        SDL_Init(SDL_INIT_VIDEO);

        SDL_Window* window = SDL_CreateWindow(null, (int)SDL_WINDOWPOS_CENTERED, (int)SDL_WINDOWPOS_CENTERED, width, height, (uint)SDL_WindowFlags.SDL_WINDOW_SHOWN);
        SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, (uint)SDL_RendererFlags.SDL_RENDERER_SOFTWARE);
        SDL_Texture* texture = SDL_CreateTexture(renderer, (uint)SDL_PixelFormatEnum.SDL_PIXELFORMAT_RGBA32, (int)SDL_TextureAccess.SDL_TEXTUREACCESS_STREAMING, width, height);

        Rasterizer rast = new();
        float r = 0.0f;
        
        while(true)
        {
            SDL_Event evt;
            SDL_PollEvent(&evt);
            if (evt.type == (uint)SDL_EventType.SDL_QUIT)
            {
                break;
            }

            if (evt.type == (uint)SDL_EventType.SDL_KEYDOWN)
            {
                if (evt.key.keysym.sym == SDL_KeyCode.SDLK_ESCAPE)
                {
                    break;
                }
            }

            uint* pixels;
            int pitch;

            if (SDL_LockTexture(texture, null, (void**)&pixels, &pitch) != 0)
                return -1;

            SDL_Color orange = new SDL_Color { r = 255 / 5, g = 255 / 5, b = 255 / 5};

            rast.SetFrameBuffer(pixels, width, height);
            rast.Clear(window);

            float size = 128;
            float x1 = (float)((width / 2) + MathF.Cos(r - MathF.PI / 6.0f) * size);
            float y1 = (float)((height / 2) + MathF.Sin(r - MathF.PI / 6.0f) * size);
            float x2 = (float)((width / 2) + MathF.Cos(r + MathF.PI / 2.0f) * size);
            float y2 = (float)((height / 3) + MathF.Sin(r + MathF.PI / 2.0f) * size);
            float x3 = (float)((width / 2) + MathF.Cos(r + MathF.PI + MathF.PI / 6.0f) * size);
            float y3 = (float)((height / 2) + MathF.Sin(r + MathF.PI + MathF.PI / 6.0f) * size);

            SDL_Color color1 = new SDL_Color { r = 255 , g = 0, b = 0};
            SDL_Color color2 = new SDL_Color { r = 0, g = 255, b = 0};
            SDL_Color color3 = new SDL_Color { r = 0, g = 0, b = 255};

            rast.ClearColor(orange);

            rast.DrawLine(color1, x1, y1, color2, x2, y2);
            rast.DrawLine(color2, x2, y2, color3, x3, y3);
            rast.DrawLine(color3, x3, y3, color1, x1, y1);

            SDL_UnlockTexture(texture);

            if (SDL_RenderClear(renderer) != 0)
                return -1;

            if (SDL_RenderCopy(renderer, texture, null, null) != 0)
                return -1;

            SDL_RenderPresent(renderer);
        }

        SDL_DestroyTexture(texture);
        SDL_DestroyRenderer(renderer);
        SDL_DestroyWindow(window);
        SDL_Quit();

        return 0;
    }
}

Result:

But I have problem with fill-area and adding textures ( loading image/picture)

Best regards, Jens

I haven’t done much in SDL geometry for myself, but if you have a copy of the SDL Library source code, then go to the main folder, then click into the test folder. From there check out the testgeometry.c file, it builds a filled-in triangle.

If you run the program using ./testgeometry --use-texture it will load a sprite into a SDL_Texture and display it inside the triangle.