Font Rendering #18
			
				
			
		
		
		
	|  | @ -7,3 +7,6 @@ | ||||||
| [submodule "lib/RefreshCS"] | [submodule "lib/RefreshCS"] | ||||||
| 	path = lib/RefreshCS | 	path = lib/RefreshCS | ||||||
| 	url = https://gitea.moonside.games/MoonsideGames/RefreshCS.git | 	url = https://gitea.moonside.games/MoonsideGames/RefreshCS.git | ||||||
|  | [submodule "lib/WellspringCS"] | ||||||
|  | 	path = lib/WellspringCS | ||||||
|  | 	url = https://gitea.moonside.games/MoonsideGames/WellspringCS.git | ||||||
|  |  | ||||||
|  | @ -14,6 +14,7 @@ | ||||||
| 		<ProjectReference Include=".\lib\SDL2-CS\SDL2-CS.Core.csproj" /> | 		<ProjectReference Include=".\lib\SDL2-CS\SDL2-CS.Core.csproj" /> | ||||||
| 		<ProjectReference Include=".\lib\RefreshCS\RefreshCS.csproj" /> | 		<ProjectReference Include=".\lib\RefreshCS\RefreshCS.csproj" /> | ||||||
| 		<ProjectReference Include=".\lib\FAudio\csharp\FAudio-CS.Core.csproj" /> | 		<ProjectReference Include=".\lib\FAudio\csharp\FAudio-CS.Core.csproj" /> | ||||||
|  | 		<ProjectReference Include=".\lib\WellspringCS\WellspringCS.csproj" /> | ||||||
| 	</ItemGroup> | 	</ItemGroup> | ||||||
| 
 | 
 | ||||||
| 	<ItemGroup> | 	<ItemGroup> | ||||||
|  |  | ||||||
|  | @ -11,4 +11,8 @@ | ||||||
| 	<dllmap dll="FAudio" os="windows" target="FAudio.dll"/> | 	<dllmap dll="FAudio" os="windows" target="FAudio.dll"/> | ||||||
| 	<dllmap dll="FAudio" os="osx" target="libFAudio.0.dylib"/> | 	<dllmap dll="FAudio" os="osx" target="libFAudio.0.dylib"/> | ||||||
| 	<dllmap dll="FAudio" os="linux,freebsd,netbsd" target="libFAudio.so.0"/> | 	<dllmap dll="FAudio" os="linux,freebsd,netbsd" target="libFAudio.so.0"/> | ||||||
|  | 
 | ||||||
|  | 	<dllmap dll="Wellspring" os="windows" target="Wellspring.dll"/> | ||||||
|  | 	<dllmap dll="Wellspring" os="osx" target="libWellspring.0.dylib"/> | ||||||
|  | 	<dllmap dll="Wellspring" os="linux,freebsd,netbsd" target="libWellspring.so.0"/> | ||||||
| </configuration> | </configuration> | ||||||
|  |  | ||||||
|  | @ -14,6 +14,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FAudio-CS.Core", "lib\FAudi | ||||||
| EndProject | EndProject | ||||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RefreshCS", "lib\RefreshCS\RefreshCS.csproj", "{AD7C94E4-0AFA-44CA-889C-110142369893}" | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RefreshCS", "lib\RefreshCS\RefreshCS.csproj", "{AD7C94E4-0AFA-44CA-889C-110142369893}" | ||||||
| EndProject | 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 | Global | ||||||
| 	GlobalSection(SolutionConfigurationPlatforms) = preSolution | 	GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||||
| 		Debug|x64 = Debug|x64 | 		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}.Debug|x64.Build.0 = Debug|x64 | ||||||
| 		{AD7C94E4-0AFA-44CA-889C-110142369893}.Release|x64.ActiveCfg = Release|x64 | 		{AD7C94E4-0AFA-44CA-889C-110142369893}.Release|x64.ActiveCfg = Release|x64 | ||||||
| 		{AD7C94E4-0AFA-44CA-889C-110142369893}.Release|x64.Build.0 = 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 | 	EndGlobalSection | ||||||
| 	GlobalSection(SolutionProperties) = preSolution | 	GlobalSection(SolutionProperties) = preSolution | ||||||
| 		HideSolutionNode = FALSE | 		HideSolutionNode = FALSE | ||||||
|  | @ -43,4 +51,7 @@ Global | ||||||
| 	GlobalSection(ExtensibilityGlobals) = postSolution | 	GlobalSection(ExtensibilityGlobals) = postSolution | ||||||
| 		SolutionGuid = {C3D68FAA-3165-43C7-95B3-D845F0DAA918} | 		SolutionGuid = {C3D68FAA-3165-43C7-95B3-D845F0DAA918} | ||||||
| 	EndGlobalSection | 	EndGlobalSection | ||||||
|  | 	GlobalSection(NestedProjects) = preSolution | ||||||
|  | 		{0DD7B866-773C-4A86-8580-F436DAA28989} = {69D3788D-6C57-44F7-A912-B201AE6D7C04} | ||||||
|  | 	EndGlobalSection | ||||||
| EndGlobal | EndGlobal | ||||||
|  |  | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | Subproject commit 4b21707900c6d75184f0d5373a0676fe31da956b | ||||||
|  | @ -21,6 +21,8 @@ namespace MoonWorks.Graphics | ||||||
| 			Handle = handle; | 			Handle = handle; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		// FIXME: we can probably use the NativeMemory functions to not have to generate arrays here | ||||||
|  | 
 | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// Begins a render pass. | 		/// Begins a render pass. | ||||||
| 		/// All render state, resource binding, and draw commands must be made within a render pass. | 		/// All render state, resource binding, and draw commands must be made within a render pass. | ||||||
|  |  | ||||||
|  | @ -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<Wellspring.FontRange>(); | ||||||
|  | 				void* fontRangeMemory = NativeMemory.Alloc((nuint) fontRanges.Length, (nuint) Marshal.SizeOf<Wellspring.FontRange>()); | ||||||
|  | 				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); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -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; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -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); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -195,6 +195,7 @@ namespace MoonWorks | ||||||
| 			NativeLibrary.SetDllImportResolver(typeof(SDL2.SDL).Assembly, MapAndLoad); | 			NativeLibrary.SetDllImportResolver(typeof(SDL2.SDL).Assembly, MapAndLoad); | ||||||
| 			NativeLibrary.SetDllImportResolver(typeof(RefreshCS.Refresh).Assembly, MapAndLoad); | 			NativeLibrary.SetDllImportResolver(typeof(RefreshCS.Refresh).Assembly, MapAndLoad); | ||||||
| 			NativeLibrary.SetDllImportResolver(typeof(FAudio).Assembly, MapAndLoad); | 			NativeLibrary.SetDllImportResolver(typeof(FAudio).Assembly, MapAndLoad); | ||||||
|  | 			NativeLibrary.SetDllImportResolver(typeof(WellspringCS.Wellspring).Assembly, MapAndLoad); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		#endregion | 		#endregion | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue