diff --git a/.gitmodules b/.gitmodules
index b841a37..481755f 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -7,3 +7,6 @@
[submodule "lib/RefreshCS"]
path = lib/RefreshCS
url = https://gitea.moonside.games/MoonsideGames/RefreshCS.git
+[submodule "lib/WellspringCS"]
+ path = lib/WellspringCS
+ url = https://gitea.moonside.games/MoonsideGames/WellspringCS.git
diff --git a/MoonWorks.csproj b/MoonWorks.csproj
index 6b8a1b1..88105d4 100644
--- a/MoonWorks.csproj
+++ b/MoonWorks.csproj
@@ -14,6 +14,7 @@
+
diff --git a/MoonWorks.dll.config b/MoonWorks.dll.config
index 52020e4..d384162 100644
--- a/MoonWorks.dll.config
+++ b/MoonWorks.dll.config
@@ -11,4 +11,8 @@
+
+
+
+
diff --git a/MoonWorks.sln b/MoonWorks.sln
index 1c66aa8..e536276 100644
--- a/MoonWorks.sln
+++ b/MoonWorks.sln
@@ -14,6 +14,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FAudio-CS.Core", "lib\FAudi
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RefreshCS", "lib\RefreshCS\RefreshCS.csproj", "{AD7C94E4-0AFA-44CA-889C-110142369893}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "lib", "lib", "{69D3788D-6C57-44F7-A912-B201AE6D7C04}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WellspringCS", "lib\WellspringCS\WellspringCS.csproj", "{0DD7B866-773C-4A86-8580-F436DAA28989}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -36,6 +40,10 @@ Global
{AD7C94E4-0AFA-44CA-889C-110142369893}.Debug|x64.Build.0 = Debug|x64
{AD7C94E4-0AFA-44CA-889C-110142369893}.Release|x64.ActiveCfg = Release|x64
{AD7C94E4-0AFA-44CA-889C-110142369893}.Release|x64.Build.0 = Release|x64
+ {0DD7B866-773C-4A86-8580-F436DAA28989}.Debug|x64.ActiveCfg = Debug|x64
+ {0DD7B866-773C-4A86-8580-F436DAA28989}.Debug|x64.Build.0 = Debug|x64
+ {0DD7B866-773C-4A86-8580-F436DAA28989}.Release|x64.ActiveCfg = Release|x64
+ {0DD7B866-773C-4A86-8580-F436DAA28989}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -43,4 +51,7 @@ Global
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3D68FAA-3165-43C7-95B3-D845F0DAA918}
EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {0DD7B866-773C-4A86-8580-F436DAA28989} = {69D3788D-6C57-44F7-A912-B201AE6D7C04}
+ EndGlobalSection
EndGlobal
diff --git a/lib/WellspringCS b/lib/WellspringCS
new file mode 160000
index 0000000..4b21707
--- /dev/null
+++ b/lib/WellspringCS
@@ -0,0 +1 @@
+Subproject commit 4b21707900c6d75184f0d5373a0676fe31da956b
diff --git a/src/Graphics/CommandBuffer.cs b/src/Graphics/CommandBuffer.cs
index a296145..5afbb57 100644
--- a/src/Graphics/CommandBuffer.cs
+++ b/src/Graphics/CommandBuffer.cs
@@ -21,6 +21,8 @@ namespace MoonWorks.Graphics
Handle = handle;
}
+ // FIXME: we can probably use the NativeMemory functions to not have to generate arrays here
+
///
/// Begins a render pass.
/// All render state, resource binding, and draw commands must be made within a render pass.
diff --git a/src/Graphics/Font/Packer.cs b/src/Graphics/Font/Packer.cs
new file mode 100644
index 0000000..2005201
--- /dev/null
+++ b/src/Graphics/Font/Packer.cs
@@ -0,0 +1,76 @@
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using WellspringCS;
+
+namespace MoonWorks.Graphics.Font
+{
+ public class Packer : IDisposable
+ {
+ public IntPtr Handle { get; }
+ public Texture Texture { get; }
+
+ private bool IsDisposed;
+
+ public unsafe Packer(GraphicsDevice graphicsDevice, string path, uint textureWidth, uint textureHeight, uint padding = 1)
+ {
+ var bytes = File.ReadAllBytes(path);
+ fixed (byte* pByte = &bytes[0])
+ {
+ Handle = Wellspring.Wellspring_CreatePacker((IntPtr) pByte, (uint) bytes.Length, textureWidth, textureHeight, 0, padding);
+ }
+
+ Texture = Texture.CreateTexture2D(graphicsDevice, textureWidth, textureHeight, TextureFormat.R8, TextureUsageFlags.Sampler);
+ }
+
+ public unsafe bool PackFontRanges(params FontRange[] fontRanges)
+ {
+ fixed (FontRange *pFontRanges = &fontRanges[0])
+ {
+ var nativeSize = fontRanges.Length * Marshal.SizeOf();
+ void* fontRangeMemory = NativeMemory.Alloc((nuint) fontRanges.Length, (nuint) Marshal.SizeOf());
+ System.Buffer.MemoryCopy(pFontRanges, fontRangeMemory, nativeSize, nativeSize);
+
+ var result = Wellspring.Wellspring_PackFontRanges(Handle, (IntPtr) fontRangeMemory, (uint) fontRanges.Length);
+
+ NativeMemory.Free(fontRangeMemory);
+
+ return result > 0;
+ }
+ }
+
+ public unsafe void SetTextureData(CommandBuffer commandBuffer)
+ {
+ var pixelDataPointer = Wellspring.Wellspring_GetPixelDataPointer(Handle);
+ commandBuffer.SetTextureData(Texture, pixelDataPointer, Texture.Width * Texture.Height);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!IsDisposed)
+ {
+ if (disposing)
+ {
+ Texture.Dispose();
+ }
+
+ Wellspring.Wellspring_DestroyPacker(Handle);
+
+ IsDisposed = true;
+ }
+ }
+
+ ~Packer()
+ {
+ // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
+ Dispose(disposing: false);
+ }
+
+ public void Dispose()
+ {
+ // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
+ Dispose(disposing: true);
+ GC.SuppressFinalize(this);
+ }
+ }
+}
diff --git a/src/Graphics/Font/Structs.cs b/src/Graphics/Font/Structs.cs
new file mode 100644
index 0000000..37082f1
--- /dev/null
+++ b/src/Graphics/Font/Structs.cs
@@ -0,0 +1,23 @@
+using System.Runtime.InteropServices;
+using MoonWorks.Math;
+
+namespace MoonWorks.Graphics.Font
+{
+ [StructLayout(LayoutKind.Sequential)]
+ public struct FontRange
+ {
+ public uint FontSize;
+ public uint FirstCodepoint;
+ public uint NumChars;
+ public byte OversampleH;
+ public byte OversampleV;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct Vertex
+ {
+ public Vector3 Position;
+ public Vector2 TexCoord;
+ public Color Color;
+ }
+}
diff --git a/src/Graphics/Font/TextBatch.cs b/src/Graphics/Font/TextBatch.cs
new file mode 100644
index 0000000..ce246cd
--- /dev/null
+++ b/src/Graphics/Font/TextBatch.cs
@@ -0,0 +1,91 @@
+using System;
+using WellspringCS;
+
+namespace MoonWorks.Graphics.Font
+{
+ public class TextBatch
+ {
+ private GraphicsDevice GraphicsDevice { get; }
+ public IntPtr Handle { get; }
+
+ public Buffer VertexBuffer { get; protected set; } = null;
+ public Buffer IndexBuffer { get; protected set; } = null;
+ public Texture Texture { get; protected set; }
+ public uint PrimitiveCount { get; protected set; }
+
+ public TextBatch(GraphicsDevice graphicsDevice)
+ {
+ GraphicsDevice = graphicsDevice;
+ Handle = Wellspring.Wellspring_CreateTextBatch();
+ }
+
+ public void Start(Packer packer)
+ {
+ Wellspring.Wellspring_StartTextBatch(Handle, packer.Handle);
+ Texture = packer.Texture;
+ PrimitiveCount = 0;
+ }
+
+ public unsafe void Draw(float x, float y, float depth, Color color, string text)
+ {
+ fixed (char* chars = text)
+ {
+ var byteCount = System.Text.Encoding.UTF8.GetByteCount(text);
+ var bytes = stackalloc byte[byteCount];
+ System.Text.Encoding.UTF8.GetBytes(chars, text.Length, bytes, byteCount);
+
+ var result = Wellspring.Wellspring_Draw(
+ Handle,
+ x,
+ y,
+ depth,
+ new Wellspring.Color { R = color.R, G = color.G, B = color.B, A = color.A },
+ (IntPtr) bytes,
+ (uint) byteCount
+ );
+
+ if (result == 0)
+ {
+ throw new System.ArgumentException("Could not decode string!");
+ }
+
+ PrimitiveCount += (uint) (text.Length * 2);
+ }
+ }
+
+ // Call this after you have made all the Draw calls you want.
+ public unsafe void UploadBufferData(CommandBuffer commandBuffer)
+ {
+ Wellspring.Wellspring_GetBufferData(
+ Handle,
+ out IntPtr vertexDataPointer,
+ out uint vertexDataLengthInBytes,
+ out IntPtr indexDataPointer,
+ out uint indexDataLengthInBytes
+ );
+
+ if (VertexBuffer == null)
+ {
+ VertexBuffer = new Buffer(GraphicsDevice, BufferUsageFlags.Vertex, vertexDataLengthInBytes);
+ }
+ else if (VertexBuffer.Size < vertexDataLengthInBytes)
+ {
+ VertexBuffer.Dispose();
+ VertexBuffer = new Buffer(GraphicsDevice, BufferUsageFlags.Vertex, vertexDataLengthInBytes);
+ }
+
+ if (IndexBuffer == null)
+ {
+ IndexBuffer = new Buffer(GraphicsDevice, BufferUsageFlags.Index, indexDataLengthInBytes);
+ }
+ else if (IndexBuffer.Size < indexDataLengthInBytes)
+ {
+ IndexBuffer.Dispose();
+ IndexBuffer = new Buffer(GraphicsDevice, BufferUsageFlags.Index, indexDataLengthInBytes);
+ }
+
+ commandBuffer.SetBufferData(VertexBuffer, vertexDataPointer, 0, vertexDataLengthInBytes);
+ commandBuffer.SetBufferData(IndexBuffer, indexDataPointer, 0, indexDataLengthInBytes);
+ }
+ }
+}
diff --git a/src/MoonWorksDllMap.cs b/src/MoonWorksDllMap.cs
index 663cc58..5164838 100644
--- a/src/MoonWorksDllMap.cs
+++ b/src/MoonWorksDllMap.cs
@@ -195,6 +195,7 @@ namespace MoonWorks
NativeLibrary.SetDllImportResolver(typeof(SDL2.SDL).Assembly, MapAndLoad);
NativeLibrary.SetDllImportResolver(typeof(RefreshCS.Refresh).Assembly, MapAndLoad);
NativeLibrary.SetDllImportResolver(typeof(FAudio).Assembly, MapAndLoad);
+ NativeLibrary.SetDllImportResolver(typeof(WellspringCS.Wellspring).Assembly, MapAndLoad);
}
#endregion