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 System;
using Strawberry.SDL2;
namespace Strawberry.Sample namespace Strawberry.Sample
{ {

View File

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

View File

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

View File

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

View File

@ -173,7 +173,7 @@ namespace Strawberry
for (let x < CellsX) for (let x < CellsX)
for (let y < CellsY) for (let y < CellsY)
if (this[x, y] != '0') 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() public override void Draw()
{ {
DrawHitbox(.(255, 255, 255, 255)); DrawHitbox(.White);
} }
} }
} }

View File

@ -1,110 +1,20 @@
using System.Collections;
using System;
namespace Strawberry 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!(_); protected abstract void PushQuad(Vertex a, Vertex b, Vertex c, Vertex d);
private Batch top => batchStack.Count > 0 ? batchStack[batchStack.Count - 1] : null; protected abstract void PushTri(Vertex a, Vertex b, Vertex c);
private List<Vertex> vertices = new .() ~ delete _; public void Rect(float x, float y, float w, float h, Color color)
private List<uint32> indices = new .() ~ delete _;
private uint32 vaoID;
private uint32 vertexBufferID;
private uint32 indexBufferID;
public this()
{ {
GL.glGenVertexArrays(1, &vaoID); PushQuad(.Shape(.(x, y), color), .Shape(.(x + w, y), color), .Shape(.(x + w, y + h), color), .Shape(.(x, y + h), color));
GL.glBindVertexArray(vaoID);
GL.glGenBuffers(1, &vertexBufferID);
GL.glGenBuffers(1, &indexBufferID);
GL.glBindVertexArray(0);
} }
public ~this() public void Rect(Rect rect, Color color)
{ {
GL.glDeleteBuffers(1, &vertexBufferID); Rect(rect.X, rect.Y, rect.Width, rect.Height, color);
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;
}
} }
} }
} }

View File

@ -23,50 +23,7 @@ namespace Strawberry
//Graphics //Graphics
public abstract Texture LoadTexture(String path); public abstract Texture LoadTexture(String path);
} public abstract Batcher CreateBatcher();
public abstract Shader CreateShader(ShaderDef def);
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);
}
} }
} }

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;
using System.Diagnostics; using System.Diagnostics;
namespace Strawberry namespace Strawberry.SDL2
{ {
public class SDL2PlatformLayer : PlatformLayer public class SDL2PlatformLayer : PlatformLayer
{ {
@ -118,11 +118,6 @@ namespace Strawberry
} }
} }
static void* SdlGetProcAddress(StringView string)
{
return SDL.SDL_GL_GetProcAddress(string.ToScopeCStr!());
}
public ~this() public ~this()
{ {
delete gamepads; delete gamepads;
@ -134,6 +129,11 @@ namespace Strawberry
SDL.Quit(); SDL.Quit();
} }
static void* SdlGetProcAddress(StringView string)
{
return SDL.SDL_GL_GetProcAddress(string.ToScopeCStr!());
}
public override bool Closed() public override bool Closed()
{ {
SDL.Event event; SDL.Event event;
@ -149,21 +149,12 @@ namespace Strawberry
GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
GL.glUseProgram(glProgram); GL.glUseProgram(glProgram);
float zNearPlane = 0; Mat4x4 mat = Mat4x4.CreateOrthographic(Game.Width, Game.Height * 0.5f, 0, 1);
float zFarPlane = 1000; mat *= Mat4x4.CreateScale(.(1, -1, 1));
mat *= Mat4x4.CreateTranslation(.(-1, 1, 0));
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);
let loc = GL.glGetUniformLocation(glProgram, "u_matrix"); let loc = GL.glGetUniformLocation(glProgram, "u_matrix");
GL.glUniformMatrix4fv(loc, 1, GL.GL_FALSE, &mat); GL.glUniformMatrix4fv(loc, 1, GL.GL_FALSE, &mat.Values);
Batcher b = scope Batcher();
b.PushQuad(.(-40, -40), .(40, -40), .(40, 40), .(-40, 40), .Yellow);
b.Draw();
} }
public override void RenderEnd() public override void RenderEnd()
@ -219,73 +210,15 @@ namespace Strawberry
else else
return val / 32768f; return val / 32768f;
} }
public override Batcher CreateBatcher()
{
return new SDL2Batcher();
} }
class SDL2Texture : Texture public override Shader CreateShader(ShaderDef def)
{ {
private uint32 handle; return new SDL2Shader(def);
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);
} }
} }
} }

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) if (enabled && Open)
{ {
Draw.Rect(0, 0, Game.Width, Game.Height, .Black * 0.6f); Game.Batcher.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, Game.Height - 14, Game.Width, 14, .Black * 0.6f);
//Entry //Entry
if (entry.Length > 0) if (entry.Length > 0)
@ -168,8 +168,8 @@ namespace Strawberry
if (row > 0) if (row > 0)
pos.Y -= 4; pos.Y -= 4;
Draw.Text(font, str, pos + .Down, .Black); //Draw.Text(font, str, pos + .Down, .Black);
Draw.Text(font, str, pos, color); //Draw.Text(font, str, pos, color);
} }
static private int MessageRows => (int)Math.Ceiling((Game.Height - 14) / 10f); 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] [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); 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); get => Invert(this);
@ -165,70 +165,70 @@ namespace Strawberry
//Static Helpers //Static Helpers
public static Matrix CreateTranslation(Vector position) public static Mat3x2 CreateTranslation(Vector position)
{ {
return Matrix( return Mat3x2(
1, 0, 1, 0,
0, 1, 0, 1,
position.X, position.Y position.X, position.Y
); );
} }
public static Matrix CreateScale(Vector scale) public static Mat3x2 CreateScale(Vector scale)
{ {
return Matrix( return Mat3x2(
scale.X, 0, scale.X, 0,
0, scale.Y, 0, scale.Y,
0, 0 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 tx = origin.X * (1 - scale.X);
float ty = origin.Y * (1 - scale.Y); float ty = origin.Y * (1 - scale.Y);
return Matrix( return Mat3x2(
scale.X, 0, scale.X, 0,
0, scale.Y, 0, scale.Y,
tx, ty tx, ty
); );
} }
public static Matrix CreateScale(float scale) public static Mat3x2 CreateScale(float scale)
{ {
return Matrix( return Mat3x2(
scale, 0, scale, 0,
0, scale, 0, scale,
0, 0 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 tx = origin.X * (1 - scale);
float ty = origin.Y * (1 - scale); float ty = origin.Y * (1 - scale);
return Matrix( return Mat3x2(
scale, 0, scale, 0,
0, scale, 0, scale,
tx, ty 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 xTan = (float)Math.Tan(radiansX);
float yTan = (float)Math.Tan(radiansY); float yTan = (float)Math.Tan(radiansY);
return Matrix( return Mat3x2(
1, yTan, 1, yTan,
xTan, 1, xTan, 1,
0, 0 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 xTan = (float)Math.Tan(radiansX);
float yTan = (float)Math.Tan(radiansY); float yTan = (float)Math.Tan(radiansY);
@ -236,14 +236,14 @@ namespace Strawberry
float tx = -origin.Y * xTan; float tx = -origin.Y * xTan;
float ty = -origin.X * yTan; float ty = -origin.X * yTan;
return Matrix( return Mat3x2(
1, yTan, 1, yTan,
xTan, 1, xTan, 1,
tx, ty tx, ty
); );
} }
public static Matrix CreateRotation(float radians) public static Mat3x2 CreateRotation(float radians)
{ {
let rad = (float)Math.IEEERemainder(radians, Math.PI_f * 2); let rad = (float)Math.IEEERemainder(radians, Math.PI_f * 2);
@ -282,14 +282,14 @@ namespace Strawberry
s = (float)Math.Sin(rad); s = (float)Math.Sin(rad);
} }
return Matrix( return Mat3x2(
c, s, c, s,
-s, c, -s, c,
0, 0 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); 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 x = origin.X * (1 - c) + origin.Y * s;
float y = origin.Y * (1 - c) - origin.X * s; float y = origin.Y * (1 - c) - origin.X * s;
return Matrix( return Mat3x2(
c, s, c, s,
-s, c, -s, c,
x, y x, y
@ -361,7 +361,7 @@ namespace Strawberry
return (M11 * M22) - (M21 * M12); 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); let det = (matrix.M11 * matrix.M22) - (matrix.M21 * matrix.M12);
@ -370,7 +370,7 @@ namespace Strawberry
let invDet = 1.0f / det; let invDet = 1.0f / det;
return Matrix( return Mat3x2(
matrix.M22 * invDet, matrix.M22 * invDet,
-matrix.M12 * 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.M11 + (b.M11 - a.M11) * t,
a.M12 + (b.M12 - a.M12) * 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.M11, -mat.M12,
-mat.M21, -mat.M22, -mat.M21, -mat.M22,
-mat.M31, -mat.M32 -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.M11 + b.M11, a.M12 + b.M12,
a.M21 + b.M21, a.M22 + b.M22, a.M21 + b.M21, a.M22 + b.M22,
a.M31 + b.M31, a.M32 + b.M32 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.M11 - b.M11, a.M12 - b.M12,
a.M21 - b.M21, a.M22 - b.M22, a.M21 - b.M21, a.M22 - b.M22,
a.M31 - b.M31, a.M32 - b.M32 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.M11 + a.M12 * b.M21,
a.M11 * b.M12 + a.M12 * b.M22, 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.M11 * scale, a.M12 * scale,
a.M21 * scale, a.M22 * scale, a.M21 * scale, a.M22 * scale,
a.M31 * scale, a.M32 * scale a.M31 * scale, a.M32 * scale
@ -448,36 +448,36 @@ namespace Strawberry
// Operators // Operators
public static Matrix operator -(Matrix mat) public static Mat3x2 operator -(Mat3x2 mat)
{ {
return Matrix( return Mat3x2(
-mat.M11, -mat.M12, -mat.M11, -mat.M12,
-mat.M21, -mat.M22, -mat.M21, -mat.M22,
-mat.M31, -mat.M32 -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.M11 + b.M11, a.M12 + b.M12,
a.M21 + b.M21, a.M22 + b.M22, a.M21 + b.M21, a.M22 + b.M22,
a.M31 + b.M31, a.M32 + b.M32 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.M11 - b.M11, a.M12 - b.M12,
a.M21 - b.M21, a.M22 - b.M22, a.M21 - b.M21, a.M22 - b.M22,
a.M31 - b.M31, a.M32 - b.M32 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.M11 + a.M12 * b.M21,
a.M11 * b.M12 + a.M12 * b.M22, 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.M11 * scale, mat.M12 * scale,
mat.M21 * scale, mat.M22 * scale, mat.M21 * scale, mat.M22 * scale,
mat.M31 * scale, mat.M32 * 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. return (a.M11 == b.M11 && a.M22 == b.M22 && // Check diagonal element first for early out.
a.M12 == b.M12 && a.M12 == b.M12 &&
@ -506,7 +506,7 @@ namespace Strawberry
a.M31 == b.M31 && a.M32 == b.M32); 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 || return (a.M11 != b.M11 || a.M12 != b.M12 ||
a.M21 != b.M21 || a.M22 != b.M22 || 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;
}
}
}