namespace RasterizerTest;
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;
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 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;
rast.SetFrameBuffer(pixels, width, height);
rast.Clear(window);
float size = 120;
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 / 2) + 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.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:
Enjoy and happy coding on C#!sbyte*
, delegate* < ... >
and pass-thought.dotnet add package DeafMan1983.Interop.SDL2
dotnet add package DeafMan1983.Conversion
And open code ( Visual Studio Code )<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<PublishAOT>true</PublishAOT>
<StripSymbols>true</StripSymbols>
Open your Program.cs
namespace YourGame;
using static DeafMan1983.Conversion;
using DeafMan1983.Interop.SDL2;
using static DeafMan1983.Interop.SDL2.SDL;
unsafe class Program
{
private SDL_Window* window;
private SDL_Renderer* renderer;
private SDL_Event evt;
// Add init main(int, sbyte**)
}
And why do I add manual function like in C/C++ because somebody understands better.
public int main(int argc, sbyte** argv)
{
if (SDL_Init(SDL_INIT_EVERYTHING) != 0)
{
Console.WriteLine("Error: SDL initializing. {0}.\n", StringFromSBytePointer(SDL_GetError()));
return -1;
}
Console.WriteLine("Woohoo! SDL2 initializes.\n");
window = SDL_CreateWindow(SBytePointerFromString("Hello SDL2"), (int)SDL_WINDOWPOS_CENTERED, (int)SDL_WINDOWPOS_CENTERED, 600, 400, (uint)SDL_WindowFlags.SDL_WINDOW_SHOWN | (uint)SDL_WindowFlags.SDL_WINDOW_OPENGL);
if (window == null)
{
Console.WriteLine("Error: Creating SDL_Window {0}.\n", StringFromSBytePointer(SDL_GetError()));
return -1;
}
renderer = SDL_CreateRenderer(window, -1, (uint)SDL_RendererFlags.SDL_RENDERER_SOFTWARE);
if (renderer == null)
{
Console.WriteLine("Error: Creating SDL_Renderer {0}.\n", StringFromSBytePointer(SDL_GetError()));
return -1;
}
while (true)
{
/*
WARNING: You need add fixed statement = It works fine :D
*/
fixed (SDL_Event* evtptrs = &evt)
{
if (SDL_PollEvent(evtptrs) > 0)
{
if (evt.type == (uint)SDL_EventType.SDL_QUIT)
{
Console.WriteLine("Click with frame window!\n");
break;
}
if (evt.type == (uint)SDL_EventType.SDL_KEYDOWN)
{
if (evt.key.keysym.sym == SDL_KeyCode.SDLK_ESCAPE)
{
Console.WriteLine("Press key Escape!\n");
break;
}
}
}
}
SDL_SetRenderDrawColor(renderer, 255, 255 /3, 0, 255);
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
// static int Main(string[] args)
We add end of static Main function for C#.
static int Main(string[] args)
{
// Reforce with default main function into static int Main() for C#
Program game = new();
return game.main(args.Length, SByteDoublePointersFromStringArray(args));
}
Compile and check:
And try to use native executable:dotnet publish -c Release -r <rid> --self-contained=true
DeafMan1983.Interop.GLFW3
, DeafMan1983.Interop.SDL2
and X11'S GL ( glx ) and other desktop environments