191 lines
6.3 KiB
C#
191 lines
6.3 KiB
C#
|
using System.Numerics;
|
||
|
using System.Runtime.InteropServices;
|
||
|
using WellspringCS;
|
||
|
|
||
|
namespace Nerfed.Runtime.Graphics;
|
||
|
|
||
|
public unsafe class TextBatch : GraphicsResource
|
||
|
{
|
||
|
public const int INITIAL_CHAR_COUNT = 64;
|
||
|
public const int INITIAL_VERTEX_COUNT = INITIAL_CHAR_COUNT * 4;
|
||
|
public const int INITIAL_INDEX_COUNT = INITIAL_CHAR_COUNT * 6;
|
||
|
|
||
|
public IntPtr Handle { get; }
|
||
|
public Buffer VertexBuffer { get; protected set; } = null;
|
||
|
public Buffer IndexBuffer { get; protected set; } = null;
|
||
|
public uint PrimitiveCount { get; protected set; }
|
||
|
public Font CurrentFont { get; private set; }
|
||
|
|
||
|
private readonly GraphicsDevice graphicsDevice;
|
||
|
private readonly int stringBytesLength;
|
||
|
private TransferBuffer TransferBuffer;
|
||
|
private byte* stringBytes;
|
||
|
|
||
|
public TextBatch(GraphicsDevice device) : base(device)
|
||
|
{
|
||
|
graphicsDevice = device;
|
||
|
Handle = Wellspring.Wellspring_CreateTextBatch();
|
||
|
|
||
|
stringBytesLength = 128;
|
||
|
stringBytes = (byte*)NativeMemory.Alloc((nuint)stringBytesLength);
|
||
|
|
||
|
VertexBuffer = Buffer.Create<FontVertex>(graphicsDevice, BufferUsageFlags.Vertex, INITIAL_VERTEX_COUNT);
|
||
|
IndexBuffer = Buffer.Create<uint>(graphicsDevice, BufferUsageFlags.Index, INITIAL_INDEX_COUNT);
|
||
|
|
||
|
TransferBuffer = TransferBuffer.Create<byte>(
|
||
|
graphicsDevice,
|
||
|
TransferBufferUsage.Upload,
|
||
|
VertexBuffer.Size + IndexBuffer.Size
|
||
|
);
|
||
|
}
|
||
|
|
||
|
// Call this to initialize or reset the batch.
|
||
|
public void Start(Font font)
|
||
|
{
|
||
|
Wellspring.Wellspring_StartTextBatch(Handle, font.Handle);
|
||
|
CurrentFont = font;
|
||
|
PrimitiveCount = 0;
|
||
|
}
|
||
|
|
||
|
// Add text with size and color to the batch
|
||
|
public unsafe bool Add(
|
||
|
string text,
|
||
|
int pixelSize,
|
||
|
Color color,
|
||
|
HorizontalAlignment horizontalAlignment = HorizontalAlignment.Left,
|
||
|
VerticalAlignment verticalAlignment = VerticalAlignment.Baseline
|
||
|
)
|
||
|
{
|
||
|
int byteCount = System.Text.Encoding.UTF8.GetByteCount(text);
|
||
|
|
||
|
if (stringBytesLength < byteCount)
|
||
|
{
|
||
|
stringBytes = (byte*)NativeMemory.Realloc(stringBytes, (nuint)byteCount);
|
||
|
}
|
||
|
|
||
|
fixed (char* chars = text)
|
||
|
{
|
||
|
System.Text.Encoding.UTF8.GetBytes(chars, text.Length, stringBytes, byteCount);
|
||
|
|
||
|
byte result = Wellspring.Wellspring_AddToTextBatch(
|
||
|
Handle,
|
||
|
pixelSize,
|
||
|
new Wellspring.Color { R = color.R, G = color.G, B = color.B, A = color.A },
|
||
|
(Wellspring.HorizontalAlignment)horizontalAlignment,
|
||
|
(Wellspring.VerticalAlignment)verticalAlignment,
|
||
|
(IntPtr)stringBytes,
|
||
|
(uint)byteCount
|
||
|
);
|
||
|
|
||
|
if (result == 0)
|
||
|
{
|
||
|
Log.Warning("Could not decode string: " + text);
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Call this after you have made all the Add calls you want, but before beginning a render pass.
|
||
|
public unsafe void UploadBufferData(CommandBuffer commandBuffer)
|
||
|
{
|
||
|
Wellspring.Wellspring_GetBufferData(
|
||
|
Handle,
|
||
|
out uint vertexCount,
|
||
|
out IntPtr vertexDataPointer,
|
||
|
out uint vertexDataLengthInBytes,
|
||
|
out IntPtr indexDataPointer,
|
||
|
out uint indexDataLengthInBytes
|
||
|
);
|
||
|
|
||
|
Span<byte> vertexSpan = new Span<byte>((void*)vertexDataPointer, (int)vertexDataLengthInBytes);
|
||
|
Span<byte> indexSpan = new Span<byte>((void*)indexDataPointer, (int)indexDataLengthInBytes);
|
||
|
|
||
|
bool newTransferBufferNeeded = false;
|
||
|
|
||
|
if (VertexBuffer.Size < vertexDataLengthInBytes)
|
||
|
{
|
||
|
VertexBuffer.Dispose();
|
||
|
VertexBuffer = new Buffer(graphicsDevice, BufferUsageFlags.Vertex, vertexDataLengthInBytes);
|
||
|
newTransferBufferNeeded = true;
|
||
|
}
|
||
|
|
||
|
if (IndexBuffer.Size < indexDataLengthInBytes)
|
||
|
{
|
||
|
IndexBuffer.Dispose();
|
||
|
IndexBuffer = new Buffer(graphicsDevice, BufferUsageFlags.Index, vertexDataLengthInBytes);
|
||
|
newTransferBufferNeeded = true;
|
||
|
}
|
||
|
|
||
|
if (newTransferBufferNeeded)
|
||
|
{
|
||
|
TransferBuffer.Dispose();
|
||
|
TransferBuffer = new TransferBuffer(
|
||
|
graphicsDevice,
|
||
|
TransferBufferUsage.Upload,
|
||
|
VertexBuffer.Size + IndexBuffer.Size
|
||
|
);
|
||
|
}
|
||
|
|
||
|
if (vertexDataLengthInBytes > 0 && indexDataLengthInBytes > 0)
|
||
|
{
|
||
|
TransferBuffer.SetData(vertexSpan, true);
|
||
|
TransferBuffer.SetData(indexSpan, (uint)vertexSpan.Length, false);
|
||
|
|
||
|
CopyPass copyPass = commandBuffer.BeginCopyPass();
|
||
|
copyPass.UploadToBuffer(
|
||
|
new TransferBufferLocation(TransferBuffer),
|
||
|
new BufferRegion(VertexBuffer, 0, (uint)vertexSpan.Length),
|
||
|
true
|
||
|
);
|
||
|
copyPass.UploadToBuffer(
|
||
|
new TransferBufferLocation(TransferBuffer, (uint)vertexSpan.Length),
|
||
|
new BufferRegion(IndexBuffer, 0, (uint)indexSpan.Length),
|
||
|
true
|
||
|
);
|
||
|
commandBuffer.EndCopyPass(copyPass);
|
||
|
}
|
||
|
|
||
|
PrimitiveCount = vertexCount / 2;
|
||
|
}
|
||
|
|
||
|
// Call this AFTER binding your text pipeline!
|
||
|
public void Render(CommandBuffer commandBuffer, RenderPass renderPass, Matrix4x4 transformMatrix)
|
||
|
{
|
||
|
commandBuffer.PushVertexUniformData(transformMatrix);
|
||
|
commandBuffer.PushFragmentUniformData(CurrentFont.DistanceRange);
|
||
|
|
||
|
renderPass.BindFragmentSampler(
|
||
|
new TextureSamplerBinding(
|
||
|
CurrentFont.Texture,
|
||
|
graphicsDevice.LinearSampler
|
||
|
)
|
||
|
);
|
||
|
renderPass.BindVertexBuffer(VertexBuffer);
|
||
|
renderPass.BindIndexBuffer(IndexBuffer, IndexElementSize.ThirtyTwo);
|
||
|
|
||
|
renderPass.DrawIndexedPrimitives(
|
||
|
0,
|
||
|
0,
|
||
|
PrimitiveCount
|
||
|
);
|
||
|
}
|
||
|
|
||
|
protected override void Dispose(bool disposing)
|
||
|
{
|
||
|
if (!IsDisposed)
|
||
|
{
|
||
|
if (disposing)
|
||
|
{
|
||
|
VertexBuffer.Dispose();
|
||
|
IndexBuffer.Dispose();
|
||
|
}
|
||
|
|
||
|
NativeMemory.Free(stringBytes);
|
||
|
Wellspring.Wellspring_DestroyTextBatch(Handle);
|
||
|
}
|
||
|
|
||
|
base.Dispose(disposing);
|
||
|
}
|
||
|
}
|