OpenGL rendering hooked up. Better platform layer abstraction

This commit is contained in:
Matt Thorson 2020-08-16 22:00:13 -07:00
parent 5d62a86947
commit 1c084b55f9
21 changed files with 669 additions and 450 deletions

View File

@ -1,4 +1,5 @@
using System;
using Strawberry.SDL2;
namespace Strawberry.Sample
{

View File

@ -423,7 +423,7 @@ namespace Strawberry
public void DrawHitbox(Color color)
{
Draw.Rect(SceneHitbox, color);
Game.Batcher.Rect(SceneHitbox, color);
}
static public int Compare(Entity a, Entity b)

View File

@ -29,7 +29,8 @@ namespace Strawberry
private Dictionary<Type, List<Type>> componentAssignableLists;
public PlatformLayer PlatformLayer { get; private set; }
public Color ClearColor = .Red;
public Batcher Batcher { get; private set; }
public Color ClearColor = .Black;
private bool* keyboardState;
private int32 updateCounter;
@ -53,6 +54,7 @@ namespace Strawberry
Directory.SetCurrentDirectory(exeDir);
platformLayer.Init();
Batcher = platformLayer.CreateBatcher();
VirtualInputs = new List<VirtualInput>();
Input.[Friend]Init();
@ -83,6 +85,9 @@ namespace Strawberry
DisposeTypeLists();
Input.[Friend]Dispose();
Strawberry.Console.Dispose();
delete Batcher;
Game = null;
}
@ -97,7 +102,6 @@ namespace Strawberry
uint32 tick = PlatformLayer.Ticks;
msCounter += (tick - prevTick);
prevTick = tick;
if (Time.FixedTimestep)
{
@ -112,13 +116,14 @@ namespace Strawberry
}
else
{
Time.RawDelta = msCounter / 1000;
Time.RawDelta = (tick - prevTick) / 1000f;
PlatformLayer.UpdateInput();
Update();
Input.AfterUpdate();
}
Render();
prevTick = tick;
}
}
@ -157,21 +162,18 @@ namespace Strawberry
private void Render()
{
PlatformLayer.RenderBegin();
//Draw();
Draw();
PlatformLayer.RenderEnd();
}
public virtual void Draw()
{
if (Scene != null)
{
Draw.PushCamera(Scene.Camera.Round());
Scene.Draw();
Draw.PopCamera();
}
Scene?.Draw();
if (Console.Enabled)
Strawberry.Console.[Friend]Draw();
Batcher.Draw();
}
public Scene Scene

View File

@ -8,7 +8,6 @@ namespace Strawberry
public float TimeStarted { get; private set; }
public Grid SolidGrid;
public Rect Bounds;
public Vector Camera;
private List<Entity> entities;
private HashSet<Entity> toRemove;

View File

@ -173,7 +173,7 @@ namespace Strawberry
for (let x < CellsX)
for (let y < CellsY)
if (this[x, y] != '0')
Draw.Rect(Rect(x, y, 1, 1) * CellSize + Offset, color);
Game.Batcher.Rect(Rect(x, y, 1, 1) * CellSize + Offset, color);
}
}
}

View File

@ -88,7 +88,7 @@ namespace Strawberry
public override void Draw()
{
DrawHitbox(.(255, 255, 255, 255));
DrawHitbox(.White);
}
}
}

View File

@ -1,110 +1,20 @@
using System.Collections;
using System;
namespace Strawberry
{
public class Batcher
public abstract class Batcher
{
static public int VertexSize => sizeof(Vertex);
public abstract void Draw();
private List<Batch> batchStack = new List<Batch>() ~ DeleteContainerAndItems!(_);
private Batch top => batchStack.Count > 0 ? batchStack[batchStack.Count - 1] : null;
protected abstract void PushQuad(Vertex a, Vertex b, Vertex c, Vertex d);
protected abstract void PushTri(Vertex a, Vertex b, Vertex c);
private List<Vertex> vertices = new .() ~ delete _;
private List<uint32> indices = new .() ~ delete _;
private uint32 vaoID;
private uint32 vertexBufferID;
private uint32 indexBufferID;
public this()
public void Rect(float x, float y, float w, float h, Color color)
{
GL.glGenVertexArrays(1, &vaoID);
GL.glBindVertexArray(vaoID);
GL.glGenBuffers(1, &vertexBufferID);
GL.glGenBuffers(1, &indexBufferID);
GL.glBindVertexArray(0);
PushQuad(.Shape(.(x, y), color), .Shape(.(x + w, y), color), .Shape(.(x + w, y + h), color), .Shape(.(x, y + h), color));
}
public ~this()
public void Rect(Rect rect, Color color)
{
GL.glDeleteBuffers(1, &vertexBufferID);
GL.glDeleteBuffers(1, &indexBufferID);
GL.glDeleteVertexArrays(1, &vaoID);
}
public void Draw()
{
GL.glDisable(GL.GL_CULL_FACE);
GL.glBindVertexArray(vaoID);
GL.glBindBuffer(GL.GL_ARRAY_BUFFER, vertexBufferID);
GL.glEnableVertexAttribArray(0);
GL.glVertexAttribPointer(0, 2, GL.GL_FLOAT, GL.GL_FALSE, Batcher.VertexSize, (void*)0);
GL.glEnableVertexAttribArray(1);
GL.glVertexAttribPointer(1, 2, GL.GL_FLOAT, GL.GL_FALSE, Batcher.VertexSize, (void*)8);
GL.glEnableVertexAttribArray(2);
GL.glVertexAttribPointer(2, 4, GL.GL_UNSIGNED_BYTE, GL.GL_TRUE, Batcher.VertexSize, (void*)16);
GL.glEnableVertexAttribArray(3);
GL.glVertexAttribPointer(3, 3, GL.GL_UNSIGNED_BYTE, GL.GL_TRUE, Batcher.VertexSize, (void*)20);
GL.glBufferData(GL.GL_ARRAY_BUFFER, vertices.Count * sizeof(Vertex), vertices.Ptr, GL.GL_DYNAMIC_DRAW);
GL.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, indexBufferID);
GL.glBufferData(GL.GL_ELEMENT_ARRAY_BUFFER, indices.Count * sizeof(uint32), indices.Ptr, GL.GL_DYNAMIC_DRAW);
GL.glDrawElements(GL.GL_TRIANGLES, indices.Count, GL.GL_UNSIGNED_INT, (void*)0);
GL.glBindVertexArray(0);
vertices.Clear();
indices.Clear();
}
public void PushQuad(Vector a, Vector b, Vector c, Vector d, Color color)
{
uint32 count = (uint32)vertices.Count;
vertices.Add(Vertex.Shape(a, color));
vertices.Add(Vertex.Shape(b, color));
vertices.Add(Vertex.Shape(c, color));
vertices.Add(Vertex.Shape(d, color));
indices.Add(count + 0);
indices.Add(count + 1);
indices.Add(count + 2);
indices.Add(count + 0);
indices.Add(count + 2);
indices.Add(count + 3);
}
private class Batch
{
uint32 bufferHandle;
public this()
{
//GL.glGenBuffers(1, &bufferHandle);
//GL.glBindBuffer(GL.GL_ARRAY_BUFFER, bufferHandle);
//GL.glDeleteBuffers(1, &bufferHandle);
}
}
[Ordered, Packed, CRepr]
private struct Vertex
{
public Vector Position;
public Vector TexCoord;
public Color Color;
public (uint8, uint8, uint8) Mode;
static public Vertex Shape(Vector pos, Color color)
{
Vertex v = Vertex();
v.Position = pos;
v.Color = color;
v.Mode = (0, 0, 255);
return v;
}
Rect(rect.X, rect.Y, rect.Width, rect.Height, color);
}
}
}

View File

@ -23,50 +23,7 @@ namespace Strawberry
//Graphics
public abstract Texture LoadTexture(String path);
}
public abstract class Texture
{
public int Width { get; private set; }
public int Height { get; private set; }
public virtual this(int width, int height, uint8* pixels)
{
Width = width;
Height = height;
}
}
public abstract class Shader
{
public bool IsValid { get; protected set; }
public this(ShaderDef def)
{
}
}
public struct ShaderDef
{
public String Vertex;
public String Fragment;
public this(String vertex, String fragment)
{
Vertex = vertex;
Fragment = fragment;
}
public this(String[2] str)
{
Vertex = str[0];
Fragment = str[1];
}
static implicit public operator ShaderDef(String[2] str)
{
return ShaderDef(str);
}
public abstract Batcher CreateBatcher();
public abstract Shader CreateShader(ShaderDef def);
}
}

View File

@ -0,0 +1,88 @@
using System.Collections;
using System;
namespace Strawberry.SDL2
{
public class SDL2Batcher : Batcher
{
private List<Vertex> vertices = new .() ~ delete _;
private List<uint32> indices = new .() ~ delete _;
private uint32 vaoID;
private uint32 vertexBufferID;
private uint32 indexBufferID;
public this()
{
GL.glGenVertexArrays(1, &vaoID);
GL.glBindVertexArray(vaoID);
GL.glGenBuffers(1, &vertexBufferID);
GL.glGenBuffers(1, &indexBufferID);
GL.glBindVertexArray(0);
}
public ~this()
{
GL.glDeleteBuffers(1, &vertexBufferID);
GL.glDeleteBuffers(1, &indexBufferID);
GL.glDeleteVertexArrays(1, &vaoID);
}
public override void Draw()
{
GL.glDisable(GL.GL_CULL_FACE);
GL.glBindVertexArray(vaoID);
GL.glBindBuffer(GL.GL_ARRAY_BUFFER, vertexBufferID);
GL.glEnableVertexAttribArray(0);
GL.glVertexAttribPointer(0, 2, GL.GL_FLOAT, GL.GL_FALSE, sizeof(Vertex), (void*)0);
GL.glEnableVertexAttribArray(1);
GL.glVertexAttribPointer(1, 2, GL.GL_FLOAT, GL.GL_FALSE, sizeof(Vertex), (void*)8);
GL.glEnableVertexAttribArray(2);
GL.glVertexAttribPointer(2, 4, GL.GL_UNSIGNED_BYTE, GL.GL_TRUE, sizeof(Vertex), (void*)16);
GL.glEnableVertexAttribArray(3);
GL.glVertexAttribPointer(3, 3, GL.GL_UNSIGNED_BYTE, GL.GL_TRUE, sizeof(Vertex), (void*)20);
GL.glBufferData(GL.GL_ARRAY_BUFFER, vertices.Count * sizeof(Vertex), vertices.Ptr, GL.GL_DYNAMIC_DRAW);
GL.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, indexBufferID);
GL.glBufferData(GL.GL_ELEMENT_ARRAY_BUFFER, indices.Count * sizeof(uint32), indices.Ptr, GL.GL_DYNAMIC_DRAW);
GL.glDrawElements(GL.GL_TRIANGLES, indices.Count, GL.GL_UNSIGNED_INT, (void*)0);
GL.glBindVertexArray(0);
vertices.Clear();
indices.Clear();
}
protected override void PushQuad(Vertex a, Vertex b, Vertex c, Vertex d)
{
uint32 count = (uint32)vertices.Count;
vertices.Add(a);
vertices.Add(b);
vertices.Add(c);
vertices.Add(d);
indices.Add(count + 0);
indices.Add(count + 1);
indices.Add(count + 2);
indices.Add(count + 0);
indices.Add(count + 2);
indices.Add(count + 3);
}
protected override void PushTri(Vertex a, Vertex b, Vertex c)
{
uint32 count = (uint32)vertices.Count;
vertices.Add(a);
vertices.Add(b);
vertices.Add(c);
indices.Add(count + 0);
indices.Add(count + 1);
indices.Add(count + 2);
}
}
}

View File

@ -2,7 +2,7 @@ using SDL2;
using System;
using System.Diagnostics;
namespace Strawberry
namespace Strawberry.SDL2
{
public class SDL2PlatformLayer : PlatformLayer
{
@ -118,11 +118,6 @@ namespace Strawberry
}
}
static void* SdlGetProcAddress(StringView string)
{
return SDL.SDL_GL_GetProcAddress(string.ToScopeCStr!());
}
public ~this()
{
delete gamepads;
@ -134,6 +129,11 @@ namespace Strawberry
SDL.Quit();
}
static void* SdlGetProcAddress(StringView string)
{
return SDL.SDL_GL_GetProcAddress(string.ToScopeCStr!());
}
public override bool Closed()
{
SDL.Event event;
@ -149,21 +149,12 @@ namespace Strawberry
GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
GL.glUseProgram(glProgram);
float zNearPlane = 0;
float zFarPlane = 1000;
float[16] mat =
.(2.0f / Game.Width, 0, 0, 0,
0, 2.0f / Game.Height, 0, 0,
0, 0, 1.0f / (zNearPlane - zFarPlane), zNearPlane / (zNearPlane - zFarPlane),
0, 0, 0, 1);
Mat4x4 mat = Mat4x4.CreateOrthographic(Game.Width, Game.Height * 0.5f, 0, 1);
mat *= Mat4x4.CreateScale(.(1, -1, 1));
mat *= Mat4x4.CreateTranslation(.(-1, 1, 0));
let loc = GL.glGetUniformLocation(glProgram, "u_matrix");
GL.glUniformMatrix4fv(loc, 1, GL.GL_FALSE, &mat);
Batcher b = scope Batcher();
b.PushQuad(.(-40, -40), .(40, -40), .(40, 40), .(-40, 40), .Yellow);
b.Draw();
GL.glUniformMatrix4fv(loc, 1, GL.GL_FALSE, &mat.Values);
}
public override void RenderEnd()
@ -219,73 +210,15 @@ namespace Strawberry
else
return val / 32768f;
}
public override Batcher CreateBatcher()
{
return new SDL2Batcher();
}
class SDL2Texture : Texture
public override Shader CreateShader(ShaderDef def)
{
private uint32 handle;
public this(int width, int height, uint8* pixels)
: base(width, height, pixels)
{
GL.glGenTextures(1, &handle);
GL.glBindTexture(GL.GL_TEXTURE_2D, handle);
GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA, width, height, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, pixels);
}
public ~this()
{
GL.glDeleteTextures(1, &handle);
}
}
class SDL2Shader : Shader
{
public uint vertexHandle;
public uint fragmentHandle;
public this(ShaderDef def)
: base(def)
{
IsValid = true;
int32 logLen = 0;
char8[1024] log;
vertexHandle = GL.glCreateShader(GL.GL_VERTEX_SHADER);
{
int32 len = (int32)def.Vertex.Length;
char8* data = def.Vertex.CStr();
GL.glShaderSource(vertexHandle, 1, &data, &len);
GL.glCompileShader(vertexHandle);
GL.glGetShaderInfoLog(vertexHandle, 1024, &logLen, &log);
if (logLen > 0)
{
Calc.Log(&log, logLen);
IsValid = false;
}
}
fragmentHandle = GL.glCreateShader(GL.GL_FRAGMENT_SHADER);
{
int32 len = (int32)def.Fragment.Length;
char8* data = def.Fragment.CStr();
GL.glShaderSource(fragmentHandle, 1, &data, &len);
GL.glCompileShader(fragmentHandle);
GL.glGetShaderInfoLog(fragmentHandle, 1024, &logLen, &log);
if (logLen > 0)
{
Calc.Log(&log, logLen);
IsValid = false;
}
}
}
public ~this()
{
GL.glDeleteShader(vertexHandle);
GL.glDeleteShader(fragmentHandle);
return new SDL2Shader(def);
}
}
}

View File

@ -0,0 +1,52 @@
namespace Strawberry.SDL2
{
class SDL2Shader : Shader
{
public uint vertexHandle;
public uint fragmentHandle;
public this(ShaderDef def)
: base(def)
{
IsValid = true;
int32 logLen = 0;
char8[1024] log;
vertexHandle = GL.glCreateShader(GL.GL_VERTEX_SHADER);
{
int32 len = (int32)def.Vertex.Length;
char8* data = def.Vertex.CStr();
GL.glShaderSource(vertexHandle, 1, &data, &len);
GL.glCompileShader(vertexHandle);
GL.glGetShaderInfoLog(vertexHandle, 1024, &logLen, &log);
if (logLen > 0)
{
Calc.Log(&log, logLen);
IsValid = false;
}
}
fragmentHandle = GL.glCreateShader(GL.GL_FRAGMENT_SHADER);
{
int32 len = (int32)def.Fragment.Length;
char8* data = def.Fragment.CStr();
GL.glShaderSource(fragmentHandle, 1, &data, &len);
GL.glCompileShader(fragmentHandle);
GL.glGetShaderInfoLog(fragmentHandle, 1024, &logLen, &log);
if (logLen > 0)
{
Calc.Log(&log, logLen);
IsValid = false;
}
}
}
public ~this()
{
GL.glDeleteShader(vertexHandle);
GL.glDeleteShader(fragmentHandle);
}
}
}

View File

@ -0,0 +1,20 @@
namespace Strawberry.SDL2
{
class SDL2Texture : Texture
{
private uint32 handle;
public this(int width, int height, uint8* pixels)
: base(width, height, pixels)
{
GL.glGenTextures(1, &handle);
GL.glBindTexture(GL.GL_TEXTURE_2D, handle);
GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA, width, height, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, pixels);
}
public ~this()
{
GL.glDeleteTextures(1, &handle);
}
}
}

View File

@ -0,0 +1,12 @@
namespace Strawberry
{
public abstract class Shader
{
public bool IsValid { get; protected set; }
public this(ShaderDef def)
{
}
}
}

View File

@ -0,0 +1,27 @@
using System;
namespace Strawberry
{
public struct ShaderDef
{
public String Vertex;
public String Fragment;
public this(String vertex, String fragment)
{
Vertex = vertex;
Fragment = fragment;
}
public this(String[2] str)
{
Vertex = str[0];
Fragment = str[1];
}
static implicit public operator ShaderDef(String[2] str)
{
return ShaderDef(str);
}
}
}

View File

@ -0,0 +1,14 @@
namespace Strawberry
{
public abstract class Texture
{
public int Width { get; private set; }
public int Height { get; private set; }
public virtual this(int width, int height, uint8* pixels)
{
Width = width;
Height = height;
}
}
}

View File

@ -0,0 +1,22 @@
using System;
namespace Strawberry
{
[Ordered, Packed, CRepr]
public struct Vertex
{
public Vector Position;
public Vector TexCoord;
public Color Color;
public (uint8, uint8, uint8) Mode;
static public Vertex Shape(Vector pos, Color color)
{
Vertex v = Vertex();
v.Position = pos;
v.Color = color;
v.Mode = (0, 0, 255);
return v;
}
}
}

View File

@ -147,8 +147,8 @@ namespace Strawberry
{
if (enabled && Open)
{
Draw.Rect(0, 0, Game.Width, Game.Height, .Black * 0.6f);
Draw.Rect(0, Game.Height - 14, Game.Width, 14, .Black * 0.6f);
Game.Batcher.Rect(0, 0, Game.Width, Game.Height, .Black * 0.6f);
Game.Batcher.Rect(0, Game.Height - 14, Game.Width, 14, .Black * 0.6f);
//Entry
if (entry.Length > 0)
@ -168,8 +168,8 @@ namespace Strawberry
if (row > 0)
pos.Y -= 4;
Draw.Text(font, str, pos + .Down, .Black);
Draw.Text(font, str, pos, color);
//Draw.Text(font, str, pos + .Down, .Black);
//Draw.Text(font, str, pos, color);
}
static private int MessageRows => (int)Math.Ceiling((Game.Height - 14) / 10f);

View File

@ -1,161 +0,0 @@
using SDL2;
using System;
using System.Collections;
namespace Strawberry
{
static public class Draw
{
static public Point Camera => cameraStack.Count > 0 ? cameraStack.Back : Point.Zero;
static private List<Point> cameraStack = new List<Point>() ~ delete _;
static public void PushCamera(Point camera, bool relative = true)
{
if (relative)
cameraStack.Add(Camera + camera);
else
cameraStack.Add(camera);
}
static public void PopCamera()
{
if (cameraStack.Count == 0)
Runtime.FatalError("Cannot Pop empty Camera Stack!");
cameraStack.PopBack();
}
static public void Rect(int x, int y, int w, int h, Color color)
{
//SDL.SetRenderDrawBlendMode(Game.Renderer, .Blend);
//SDL.SetRenderDrawColor(Game.Renderer, color.R, color.G, color.B, color.A);
//SDL.RenderFillRect(Game.Renderer, &SDL.Rect((int32)(x - Camera.X), (int32)(y - Camera.Y), (int32)w, (int32)h));
}
static public void Rect(Rect rect, Color color)
{
Rect(rect.X, rect.Y, rect.Width, rect.Height, color);
}
static public void HollowRect(int x, int y, int w, int h, Color color)
{
//SDL.SetRenderDrawBlendMode(Game.Renderer, .Blend);
//SDL.SetRenderDrawColor(Game.Renderer, color.R, color.G, color.B, color.A);
//SDL.RenderDrawRect(Game.Renderer, &SDL.Rect((int32)(x - Camera.X), (int32)(y - Camera.Y), (int32)w, (int32)h));
}
static public void HollowRect(Rect rect, Color color)
{
HollowRect(rect.X, rect.Y, rect.Width, rect.Height, color);
}
static public void Line(Point from, Point to, Color color)
{
let fromn = (Point)(from - Camera);
let ton = (Point)(to - Camera);
//SDL.SetRenderDrawBlendMode(Game.Renderer, .Blend);
//SDL.SetRenderDrawColor(Game.Renderer, color.R, color.G, color.B, color.A);
//SDL.RenderDrawLine(Game.Renderer, (int32)fromn.X, (int32)fromn.Y, (int32)ton.X, (int32)ton.Y);
}
static public void Circle(Point at, float radius, Color color, int steps = 16)
{
let add = at - Camera;
SDL.Point[] points = scope SDL.Point[steps + 1];
points[0] = SDL.Point((int32)(radius + add.X), (int32)add.Y);
points[steps] = points[0];
float slice = Calc.Circle / steps;
for (int i = 1; i < steps; i++)
{
points[i] = SDL.Point(
(int32)(Math.Round(Math.Cos(slice * i) * radius) + add.X),
(int32)(Math.Round(Math.Sin(slice * i) * radius) + add.Y)
);
}
//SDL.SetRenderDrawBlendMode(Game.Renderer, .Blend);
//SDL.SetRenderDrawColor(Game.Renderer, color.R, color.G, color.B, color.A);
//SDL.RenderDrawLines(Game.Renderer, &points[0], (int32)steps + 1);
}
static public void Sprite(Sprite sprite, int frame, Point position)
{
SDL.Rect src = Strawberry.Rect(0, 0, sprite.Width, sprite.Height);
SDL.Rect dst = Strawberry.Rect(position.X - Camera.X, position.Y - Camera.Y, sprite.Width, sprite.Height);
//SDL.SetTextureBlendMode(sprite[frame].Texture, .Blend);
//SDL.RenderCopy(Game.Renderer, sprite[frame].Texture, &src, &dst);
}
static public void Sprite(Sprite sprite, int frame, Point position, Point origin)
{
SDL.Point cnt = origin;
SDL.Rect src = Strawberry.Rect(0, 0, sprite.Width, sprite.Height);
SDL.Rect dst = Strawberry.Rect(position.X - origin.X - Camera.X, position.Y - origin.Y - Camera.Y, sprite.Width, sprite.Height);
//SDL.SetTextureBlendMode(sprite[frame].Texture, .Blend);
//SDL.RenderCopyEx(Game.Renderer, sprite[frame].Texture, &src, &dst, 0, &cnt, .None);
}
static public void Sprite(Sprite sprite, int frame, Point position, Point origin, float rotation)
{
SDL.Point cnt = origin;
SDL.Rect src = Strawberry.Rect(0, 0, sprite.Width, sprite.Height);
SDL.Rect dst = Strawberry.Rect(position.X - origin.X - Camera.X, position.Y - origin.Y - Camera.Y, sprite.Width, sprite.Height);
//SDL.SetTextureBlendMode(sprite[frame].Texture, .Blend);
//SDL.RenderCopyEx(Game.Renderer, sprite[frame].Texture, &src, &dst, rotation, &cnt, .None);
}
static public void SpriteCentered(Sprite sprite, int frame, Point position)
{
Point origin = .(sprite.Width/2, sprite.Height/2);
SDL.Point cnt = origin;
SDL.Rect src = Strawberry.Rect(0, 0, sprite.Width, sprite.Height);
SDL.Rect dst = Strawberry.Rect(position.X - origin.X - Camera.X, position.Y - origin.Y - Camera.Y, sprite.Width, sprite.Height);
//SDL.SetTextureBlendMode(sprite[frame].Texture, .Blend);
//SDL.RenderCopyEx(Game.Renderer, sprite[frame].Texture, &src, &dst, 0, &cnt, .None);
}
static public void SpriteCentered(Sprite sprite, int frame, Point position, Color color)
{
Point origin = .(sprite.Width/2, sprite.Height/2);
SDL.Point cnt = origin;
SDL.Rect src = Strawberry.Rect(0, 0, sprite.Width, sprite.Height);
SDL.Rect dst = Strawberry.Rect(position.X - origin.X - Camera.X, position.Y - origin.Y - Camera.Y, sprite.Width, sprite.Height);
SDL.SetTextureBlendMode(sprite[frame].Texture, .Blend);
SDL.SetTextureColorMod(sprite[frame].Texture, color.R, color.G, color.B);
SDL.SetTextureAlphaMod(sprite[frame].Texture, color.A);
//SDL.RenderCopyEx(Game.Renderer, sprite[frame].Texture, &src, &dst, 0, &cnt, .None);
}
static public void SpriteCentered(Sprite sprite, int frame, Point position, float rotation)
{
Point origin = .(sprite.Width/2, sprite.Height/2);
SDL.Point cnt = origin;
SDL.Rect src = Strawberry.Rect(0, 0, sprite.Width, sprite.Height);
SDL.Rect dst = Strawberry.Rect(position.X - origin.X - Camera.X, position.Y - origin.Y - Camera.Y, sprite.Width, sprite.Height);
SDL.SetTextureBlendMode(sprite[frame].Texture, .Blend);
//SDL.RenderCopyEx(Game.Renderer, sprite[frame].Texture, &src, &dst, rotation, &cnt, .None);
}
static public void Text(SDL2.SDLTTF.Font* font, String text, Point position, Color color)
{
//SDL.SetRenderDrawColor(Game.Renderer, color.R, color.G, color.B, color.A);
let surface = SDLTTF.RenderUTF8_Solid(font, text, color);
//let texture = SDL.CreateTextureFromSurface(Game.Renderer, surface);
SDL.Rect srcRect = .(0, 0, surface.w, surface.h);
SDL.Rect destRect = .((int32)position.X, (int32)position.Y, surface.w, surface.h);
//SDL.RenderCopy(Game.Renderer, texture, &srcRect, &destRect);
SDL.FreeSurface(surface);
//SDL.DestroyTexture(texture);
}
}
}

View File

@ -10,9 +10,9 @@ namespace Strawberry
*/
[Ordered, Packed, CRepr]
public struct Matrix
public struct Mat3x2
{
static public readonly Matrix Identity = Matrix(1, 0, 0, 1, 0, 0);
static public readonly Mat3x2 Identity = Mat3x2(1, 0, 0, 1, 0, 0);
public float[6] Values = .(0, 0, 0, 0, 0, 0);
@ -153,7 +153,7 @@ namespace Strawberry
}
}
public Result<Matrix> Inverse
public Result<Mat3x2> Inverse
{
get => Invert(this);
@ -165,70 +165,70 @@ namespace Strawberry
//Static Helpers
public static Matrix CreateTranslation(Vector position)
public static Mat3x2 CreateTranslation(Vector position)
{
return Matrix(
return Mat3x2(
1, 0,
0, 1,
position.X, position.Y
);
}
public static Matrix CreateScale(Vector scale)
public static Mat3x2 CreateScale(Vector scale)
{
return Matrix(
return Mat3x2(
scale.X, 0,
0, scale.Y,
0, 0
);
}
public static Matrix CreateScale(Vector scale, Vector origin)
public static Mat3x2 CreateScale(Vector scale, Vector origin)
{
float tx = origin.X * (1 - scale.X);
float ty = origin.Y * (1 - scale.Y);
return Matrix(
return Mat3x2(
scale.X, 0,
0, scale.Y,
tx, ty
);
}
public static Matrix CreateScale(float scale)
public static Mat3x2 CreateScale(float scale)
{
return Matrix(
return Mat3x2(
scale, 0,
0, scale,
0, 0
);
}
public static Matrix CreateScale(float scale, Vector origin)
public static Mat3x2 CreateScale(float scale, Vector origin)
{
float tx = origin.X * (1 - scale);
float ty = origin.Y * (1 - scale);
return Matrix(
return Mat3x2(
scale, 0,
0, scale,
tx, ty
);
}
public static Matrix CreateSkew(float radiansX, float radiansY)
public static Mat3x2 CreateSkew(float radiansX, float radiansY)
{
float xTan = (float)Math.Tan(radiansX);
float yTan = (float)Math.Tan(radiansY);
return Matrix(
return Mat3x2(
1, yTan,
xTan, 1,
0, 0
);
}
public static Matrix CreateSkew(float radiansX, float radiansY, Vector origin)
public static Mat3x2 CreateSkew(float radiansX, float radiansY, Vector origin)
{
float xTan = (float)Math.Tan(radiansX);
float yTan = (float)Math.Tan(radiansY);
@ -236,14 +236,14 @@ namespace Strawberry
float tx = -origin.Y * xTan;
float ty = -origin.X * yTan;
return Matrix(
return Mat3x2(
1, yTan,
xTan, 1,
tx, ty
);
}
public static Matrix CreateRotation(float radians)
public static Mat3x2 CreateRotation(float radians)
{
let rad = (float)Math.IEEERemainder(radians, Math.PI_f * 2);
@ -282,14 +282,14 @@ namespace Strawberry
s = (float)Math.Sin(rad);
}
return Matrix(
return Mat3x2(
c, s,
-s, c,
0, 0
);
}
public static Matrix CreateRotation(float radians, Vector origin)
public static Mat3x2 CreateRotation(float radians, Vector origin)
{
let rad = (float)Math.IEEERemainder(radians, Math.PI_f * 2);
@ -331,7 +331,7 @@ namespace Strawberry
float x = origin.X * (1 - c) + origin.Y * s;
float y = origin.Y * (1 - c) - origin.X * s;
return Matrix(
return Mat3x2(
c, s,
-s, c,
x, y
@ -361,7 +361,7 @@ namespace Strawberry
return (M11 * M22) - (M21 * M12);
}
public static Result<Matrix> Invert(Matrix matrix)
public static Result<Mat3x2> Invert(Mat3x2 matrix)
{
let det = (matrix.M11 * matrix.M22) - (matrix.M21 * matrix.M12);
@ -370,7 +370,7 @@ namespace Strawberry
let invDet = 1.0f / det;
return Matrix(
return Mat3x2(
matrix.M22 * invDet,
-matrix.M12 * invDet,
@ -382,9 +382,9 @@ namespace Strawberry
);
}
public static Matrix Lerp(Matrix a, Matrix b, float t)
public static Mat3x2 Lerp(Mat3x2 a, Mat3x2 b, float t)
{
return Matrix(
return Mat3x2(
a.M11 + (b.M11 - a.M11) * t,
a.M12 + (b.M12 - a.M12) * t,
@ -396,36 +396,36 @@ namespace Strawberry
);
}
public static Matrix Negate(Matrix mat)
public static Mat3x2 Negate(Mat3x2 mat)
{
return Matrix(
return Mat3x2(
-mat.M11, -mat.M12,
-mat.M21, -mat.M22,
-mat.M31, -mat.M32
);
}
public static Matrix Add(Matrix a, Matrix b)
public static Mat3x2 Add(Mat3x2 a, Mat3x2 b)
{
return Matrix(
return Mat3x2(
a.M11 + b.M11, a.M12 + b.M12,
a.M21 + b.M21, a.M22 + b.M22,
a.M31 + b.M31, a.M32 + b.M32
);
}
public static Matrix Subtract(Matrix a, Matrix b)
public static Mat3x2 Subtract(Mat3x2 a, Mat3x2 b)
{
return Matrix(
return Mat3x2(
a.M11 - b.M11, a.M12 - b.M12,
a.M21 - b.M21, a.M22 - b.M22,
a.M31 - b.M31, a.M32 - b.M32
);
}
public static Matrix Multiply(Matrix a, Matrix b)
public static Mat3x2 Multiply(Mat3x2 a, Mat3x2 b)
{
return Matrix(
return Mat3x2(
a.M11 * b.M11 + a.M12 * b.M21,
a.M11 * b.M12 + a.M12 * b.M22,
@ -437,9 +437,9 @@ namespace Strawberry
);
}
public static Matrix Multiply(Matrix a, float scale)
public static Mat3x2 Multiply(Mat3x2 a, float scale)
{
return Matrix(
return Mat3x2(
a.M11 * scale, a.M12 * scale,
a.M21 * scale, a.M22 * scale,
a.M31 * scale, a.M32 * scale
@ -448,36 +448,36 @@ namespace Strawberry
// Operators
public static Matrix operator -(Matrix mat)
public static Mat3x2 operator -(Mat3x2 mat)
{
return Matrix(
return Mat3x2(
-mat.M11, -mat.M12,
-mat.M21, -mat.M22,
-mat.M31, -mat.M32
);
}
public static Matrix operator +(Matrix a, Matrix b)
public static Mat3x2 operator +(Mat3x2 a, Mat3x2 b)
{
return Matrix(
return Mat3x2(
a.M11 + b.M11, a.M12 + b.M12,
a.M21 + b.M21, a.M22 + b.M22,
a.M31 + b.M31, a.M32 + b.M32
);
}
public static Matrix operator -(Matrix a, Matrix b)
public static Mat3x2 operator -(Mat3x2 a, Mat3x2 b)
{
return Matrix(
return Mat3x2(
a.M11 - b.M11, a.M12 - b.M12,
a.M21 - b.M21, a.M22 - b.M22,
a.M31 - b.M31, a.M32 - b.M32
);
}
public static Matrix operator *(Matrix a, Matrix b)
public static Mat3x2 operator *(Mat3x2 a, Mat3x2 b)
{
return Matrix(
return Mat3x2(
a.M11 * b.M11 + a.M12 * b.M21,
a.M11 * b.M12 + a.M12 * b.M22,
@ -489,16 +489,16 @@ namespace Strawberry
);
}
public static Matrix operator *(Matrix mat, float scale)
public static Mat3x2 operator *(Mat3x2 mat, float scale)
{
return Matrix(
return Mat3x2(
mat.M11 * scale, mat.M12 * scale,
mat.M21 * scale, mat.M22 * scale,
mat.M31 * scale, mat.M32 * scale
);
}
public static bool operator ==(Matrix a, Matrix b)
public static bool operator ==(Mat3x2 a, Mat3x2 b)
{
return (a.M11 == b.M11 && a.M22 == b.M22 && // Check diagonal element first for early out.
a.M12 == b.M12 &&
@ -506,7 +506,7 @@ namespace Strawberry
a.M31 == b.M31 && a.M32 == b.M32);
}
public static bool operator !=(Matrix a, Matrix b)
public static bool operator !=(Mat3x2 a, Mat3x2 b)
{
return (a.M11 != b.M11 || a.M12 != b.M12 ||
a.M21 != b.M21 || a.M22 != b.M22 ||

327
src/Struct/Mat4x4.bf Normal file
View File

@ -0,0 +1,327 @@
using System;
namespace Strawberry
{
/*
Based on Matrix3x2.cs by Microsoft, under MIT license
Source: https://github.com/microsoft/referencesource/blob/master/System.Numerics/System/Numerics/Matrix4x4.cs
Copyright (c) Microsoft. All rights reserved.
Licensed under the MIT license.
*/
[Ordered, Packed, CRepr]
public struct Mat4x4
{
static public readonly Mat4x4 Identity = Mat4x4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
public float[16] Values = .(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
public this(float m11, float m12, float m13, float m14,
float m21, float m22, float m23, float m24,
float m31, float m32, float m33, float m34,
float m41, float m42, float m43, float m44)
{
Values = .(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44);
}
public this()
{
}
public float M11
{
get => Values[0];
set mut => Values[0] = value;
}
public float M12
{
get => Values[1];
set mut => Values[1] = value;
}
public float M13
{
get => Values[2];
set mut => Values[2] = value;
}
public float M14
{
get => Values[3];
set mut => Values[3] = value;
}
public float M21
{
get => Values[4];
set mut => Values[4] = value;
}
public float M22
{
get => Values[5];
set mut => Values[5] = value;
}
public float M23
{
get => Values[6];
set mut => Values[6] = value;
}
public float M24
{
get => Values[7];
set mut => Values[7] = value;
}
public float M31
{
get => Values[8];
set mut => Values[8] = value;
}
public float M32
{
get => Values[9];
set mut => Values[9] = value;
}
public float M33
{
get => Values[10];
set mut => Values[10] = value;
}
public float M34
{
get => Values[11];
set mut => Values[11] = value;
}
public float M41
{
get => Values[12];
set mut => Values[12] = value;
}
public float M42
{
get => Values[13];
set mut => Values[13] = value;
}
public float M43
{
get => Values[14];
set mut => Values[14] = value;
}
public float M44
{
get => Values[15];
set mut => Values[15] = value;
}
static public Mat4x4 CreateTranslation(Vector3 pos)
{
return .(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
pos.X, pos.Y, pos.Z, 1
);
}
public static Mat4x4 CreateScale(Vector3 scale)
{
return .(
scale.X, 0, 0, 0,
0, scale.Y, 0, 0,
0, 0, scale.Z, 0,
0, 0, 0, 1
);
}
public static Mat4x4 CreateScale(Vector3 scale, Vector3 origin)
{
float tx = origin.X * (1 - scale.X);
float ty = origin.Y * (1 - scale.Y);
float tz = origin.Z * (1 - scale.Z);
return .(
scale.X, 0, 0, 0,
0, scale.Y, 0, 0,
0, 0, scale.Z, 0,
tx, ty, tz, 1
);
}
public static Mat4x4 CreateRotationX(float radians)
{
float c = (float)Math.Cos(radians);
float s = (float)Math.Sin(radians);
// [ 1 0 0 0 ]
// [ 0 c s 0 ]
// [ 0 -s c 0 ]
// [ 0 0 0 1 ]
return .(
1, 0, 0, 0,
0, c, s, 0,
0, -s, c, 0,
0, 0, 0, 1
);
}
public static Mat4x4 CreateRotationX(float radians, Vector3 origin)
{
float c = (float)Math.Cos(radians);
float s = (float)Math.Sin(radians);
float y = origin.Y * (1 - c) + origin.Z * s;
float z = origin.Z * (1 - c) - origin.Y * s;
// [ 1 0 0 0 ]
// [ 0 c s 0 ]
// [ 0 -s c 0 ]
// [ 0 y z 1 ]
return .(
1, 0, 0, 0,
0, c, s, 0,
0, -s, c, 0,
0, y, z, 1
);
}
public static Mat4x4 CreateRotationY(float radians)
{
float c = (float)Math.Cos(radians);
float s = (float)Math.Sin(radians);
// [ c 0 -s 0 ]
// [ 0 1 0 0 ]
// [ s 0 c 0 ]
// [ 0 0 0 1 ]
return .(
c, 0, -s, 0,
0, 1, 0, 0,
s, 0, c, 0,
0, 0, 0, 1
);
}
public static Mat4x4 CreateRotationY(float radians, Vector3 origin)
{
float c = (float)Math.Cos(radians);
float s = (float)Math.Sin(radians);
float x = origin.X * (1 - c) - origin.Z * s;
float z = origin.Z * (1 - c) + origin.X * s;
// [ c 0 -s 0 ]
// [ 0 1 0 0 ]
// [ s 0 c 0 ]
// [ x 0 z 1 ]
return .(
c, 0, -s, 0,
0, 1, 0, 0,
s, 0, c, 0,
x, 0, z, 1
);
}
public static Mat4x4 CreateRotationZ(float radians)
{
float c = (float)Math.Cos(radians);
float s = (float)Math.Sin(radians);
// [ c s 0 0 ]
// [ -s c 0 0 ]
// [ 0 0 1 0 ]
// [ 0 0 0 1 ]
return .(
c, s, 0, 0,
-s, c, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
);
}
public static Mat4x4 CreateRotationZ(float radians, Vector3 centerPoint)
{
float c = (float)Math.Cos(radians);
float s = (float)Math.Sin(radians);
float x = centerPoint.X * (1 - c) + centerPoint.Y * s;
float y = centerPoint.Y * (1 - c) - centerPoint.X * s;
// [ c s 0 0 ]
// [ -s c 0 0 ]
// [ 0 0 1 0 ]
// [ x y 0 1 ]
return .(
c, s, 0, 0,
-s, c, 0, 0,
0, 0, 1, 0,
x, y, 0, 1
);
}
public static Mat4x4 CreateOrthographic(float width, float height, float zNearPlane, float zFarPlane)
{
return .(
2 / width, 0, 0, 0,
0, 1 / height, 0, 0,
0, 0, 1 / (zNearPlane - zFarPlane), 0,
0, zNearPlane / (zNearPlane - zFarPlane), 0, 1
);
}
static public implicit operator Mat4x4(Mat3x2 mat)
{
return Mat4x4(
mat.M11, mat.M12, 0, 0,
mat.M21, mat.M22, 0, 0,
0, 0, 1, 0,
mat.M31, mat.M32, 0, 1
);
}
static public Mat4x4 operator *(Mat4x4 a, Mat4x4 b)
{
Mat4x4 m = .();
// First row
m.M11 = a.M11 * b.M11 + a.M12 * b.M21 + a.M13 * b.M31 + a.M14 * b.M41;
m.M12 = a.M11 * b.M12 + a.M12 * b.M22 + a.M13 * b.M32 + a.M14 * b.M42;
m.M13 = a.M11 * b.M13 + a.M12 * b.M23 + a.M13 * b.M33 + a.M14 * b.M43;
m.M14 = a.M11 * b.M14 + a.M12 * b.M24 + a.M13 * b.M34 + a.M14 * b.M44;
// Second row
m.M21 = a.M21 * b.M11 + a.M22 * b.M21 + a.M23 * b.M31 + a.M24 * b.M41;
m.M22 = a.M21 * b.M12 + a.M22 * b.M22 + a.M23 * b.M32 + a.M24 * b.M42;
m.M23 = a.M21 * b.M13 + a.M22 * b.M23 + a.M23 * b.M33 + a.M24 * b.M43;
m.M24 = a.M21 * b.M14 + a.M22 * b.M24 + a.M23 * b.M34 + a.M24 * b.M44;
// Third row
m.M31 = a.M31 * b.M11 + a.M32 * b.M21 + a.M33 * b.M31 + a.M34 * b.M41;
m.M32 = a.M31 * b.M12 + a.M32 * b.M22 + a.M33 * b.M32 + a.M34 * b.M42;
m.M33 = a.M31 * b.M13 + a.M32 * b.M23 + a.M33 * b.M33 + a.M34 * b.M43;
m.M34 = a.M31 * b.M14 + a.M32 * b.M24 + a.M33 * b.M34 + a.M34 * b.M44;
// Fourth row
m.M41 = a.M41 * b.M11 + a.M42 * b.M21 + a.M43 * b.M31 + a.M44 * b.M41;
m.M42 = a.M41 * b.M12 + a.M42 * b.M22 + a.M43 * b.M32 + a.M44 * b.M42;
m.M43 = a.M41 * b.M13 + a.M42 * b.M23 + a.M43 * b.M33 + a.M44 * b.M43;
m.M44 = a.M41 * b.M14 + a.M42 * b.M24 + a.M43 * b.M34 + a.M44 * b.M44;
return m;
}
}
}

16
src/Struct/Vector3.bf Normal file
View File

@ -0,0 +1,16 @@
namespace Strawberry
{
public struct Vector3
{
public float X;
public float Y;
public float Z;
public this(float x, float y, float z)
{
X = x;
Y = y;
Z = z;
}
}
}