Struct VertexBufferHelper
public readonly struct VertexBufferHelper
- Inherited Members
Examples
Reading the vertex positions of a mesh:
Model.Meshes[0].Draw.VertexBuffers[0].AsReadable(Services, out VertexBufferHelper helper, out int count);
var vertexPositions = new Vector3[count];
helper.Copy<PositionSemantic, Vector3>(vertexPositions);
Constructors
VertexBufferHelper(VertexBufferBinding, IServiceRegistry, out int)
Fetch this buffer and create a helper to read from it.
public VertexBufferHelper(VertexBufferBinding binding, IServiceRegistry services, out int count)
Parameters
bindingVertexBufferBindingThe bindings for this buffer
servicesIServiceRegistryThe service used to retrieve the buffer from disk/GPU if it wasn't found through other means
countintThe amount of vertices this buffer holds
Examples
Reading the vertex positions of a mesh:
Model.Meshes[0].Draw.VertexBuffers[0].AsReadable(Services, out VertexBufferHelper helper, out int count);
var vertexPositions = new Vector3[count];
helper.Copy<PositionSemantic, Vector3>(vertexPositions);
Remarks
This operation loads the buffer from disk, or directly from the gpu. It is very slow, avoid calling this too often if at all possible.
VertexBufferHelper(VertexBufferBinding, byte[], out int)
Create the helper from existing data instead of trying to fetch the buffer automatically
public VertexBufferHelper(VertexBufferBinding binding, byte[] dataOuter, out int count)
Parameters
bindingVertexBufferBindingdataOuterbyte[]countint
Exceptions
- ArgumentException
dataOuterdoes not match the binding definition provided,dataOutermust be the entire vertex buffer
Fields
Binding
public readonly VertexBufferBinding Binding
Field Value
DataOuter
Full vertex buffer, the start and end of this buffer may contain data that does not map to this Binding, use DataInner if you want to only read the data that is mapped to this binding.
public readonly byte[] DataOuter
Field Value
- byte[]
Properties
DataInner
Effective vertex buffer, accounts for the binding offset and length
public Span<byte> DataInner { get; }
Property Value
Methods
Copy<TDest>(Span<TDest>)
Copies this vertex buffer's data to the vertex-buffer-like span provided.
Any semantic data present in destination that is not present in this buffer is left untouched.
public bool Copy<TDest>(Span<TDest> destination) where TDest : unmanaged, IVertex
Parameters
destinationSpan<TDest>The buffer which will be written to, must be of the same length as the amount of vertices in this buffer
Returns
- bool
True if every semantic element of
TDestwas written to from this buffers' data, false if at least one semantic was missing
Type Parameters
TDest
Examples
Copying a mesh's vertex positions, colors and UVs:
Model.Meshes[0].Draw.VertexBuffers[0].AsReadable(Services, out VertexBufferHelper helper, out int count);
var vertexPositionsColorsAndUVs = new VertexPositionColorTexture[count];
helper.Copy(vertexPositionsColorsAndUVs);
Exceptions
- NotImplementedException
When the data format for this semantic is too arcane - no conversion logic is implemented for that type
Copy<TSemantic, TValue>(Span<TValue>, int)
Extract individual element from each vertex contained in this vertex buffer and copies them into buffer
public bool Copy<TSemantic, TValue>(Span<TValue> buffer, int semanticIndex = 0) where TSemantic : IConverter<Vector2, TValue>, IConverter<Vector3, TValue>, IConverter<Vector4, TValue>, IConverter<Half2, TValue>, IConverter<Half4, TValue>, IConverter<UShort4, TValue>, IConverter<Byte4, TValue>, IConverter<Color, TValue>, ISemantic where TValue : unmanaged
Parameters
bufferSpan<TValue>The buffer which will be written to, must have exactly the same amount of items as there are vertices in the buffer
semanticIndexintThe semantic to read with that index, starts at zero.
For example, to sample the second TextureCoordinate, you would usehelper.Copy<TextureCoordinateSemantic, Vector2>(myUvs, 1);
Returns
- bool
True when this semantic exists in the vertex buffer, false otherwise
Type Parameters
TSemanticThe semantic to read, for example PositionSemantic
TValueThe value type to read, depends entirely on the
TSemanticused
Examples
Reading the vertex positions of a mesh:
Model.Meshes[0].Draw.VertexBuffers[0].AsReadable(Services, out VertexBufferHelper helper, out int count);
var vertexPositions = new Vector3[count];
helper.Copy<PositionSemantic, Vector3>(vertexPositions);
Exceptions
- NotImplementedException
When the data format for this semantic is too arcane - no conversion logic is implemented for that type
Read<TSemantic, TDest, TReader>(Span<TDest>, TReader, int)
Lower level access to read into the vertex buffer
public bool Read<TSemantic, TDest, TReader>(Span<TDest> destination, TReader reader, int semanticIndex = 0) where TSemantic : IConverter<Vector2, TDest>, IConverter<Vector3, TDest>, IConverter<Vector4, TDest>, IConverter<Half2, TDest>, IConverter<Half4, TDest>, IConverter<UShort4, TDest>, IConverter<Byte4, TDest>, IConverter<Color, TDest>, ISemantic where TDest : unmanaged where TReader : VertexBufferHelper.IReader<TDest>, allows ref struct
Parameters
destinationSpan<TDest>The destination span your
TReaderRead<TConverter, TSource>(byte*, int, int, Span<TDest>) method receives, you may pass Empty if you do not need one.readerTReaderAn implementation of VertexBufferHelper.IReader<TDest>, implement this interface to read directly from the vertex buffer while making use of the auto-conversion of the
TSemanticprovided
Preferably as a struct to ensure it is inlined by the JITsemanticIndexintThe semantic to read with that index, starts at zero.
For example, to sample the second TextureCoordinate, you would usehelper.Read<TextureCoordinateSemantic, Vector2>(yourReader, 1);
Returns
- bool
True when this semantic exists in the vertex buffer, false otherwise
Type Parameters
TSemanticThe semantic to read, PositionSemantic for example
TDestThe value type to read, depends on the
TSemanticused, Vector3 when yourTSemanticPositionSemantic for exampleTReaderThe type of the reader you're providing
Examples
Implementing Copy<TSemantic, TValue>(Span<TValue>, int) manually:
Model.Meshes[0].Draw.VertexBuffers[0].AsReadable(Services, out VertexBufferHelper helper, out int count);
var vertexPositions = new Vector3[count];
var myReader = new CopyTo();
helper.Read<PositionSemantic, Vector3, CopyTo>(vertexPositions, myReader);
struct CopyTo : IReader<Vector3>
{
public unsafe void Read<TConverter, TSource>(byte* sourcePointer, int elementCount, int stride, Span<T> destination)
where TConverter : IConverter<TSource, T> where TSource : unmanaged
{
if (destination.Length != elementCount)
throw new ArgumentException($"{nameof(destination)} length does not match the amount of vertices contained within this vertex buffer ({destination.Length} / {elementCount})");
fixed (T* ptrDest = destination)
{
byte* end = sourcePointer + elementCount * stride;
T* dest = ptrDest;
for (; sourcePointer < end; sourcePointer += stride, dest++)
TConverter.Convert(*(TSource*)sourcePointer, out *dest);
}
}
}
Exceptions
- NotImplementedException
When the data format for this semantic is too arcane - no conversion logic is implemented for that type
Write<TSemantic, TDest, TWriter>(TWriter, int)
Lower level access to write directly to the vertex buffer
public bool Write<TSemantic, TDest, TWriter>(TWriter writer, int semanticIndex = 0) where TSemantic : IConverter<TDest, Vector2>, IConverter<TDest, Vector3>, IConverter<TDest, Vector4>, IConverter<TDest, Half2>, IConverter<TDest, Half4>, IConverter<TDest, UShort4>, IConverter<TDest, Byte4>, IConverter<TDest, Color>, IConverter<Vector2, TDest>, IConverter<Vector3, TDest>, IConverter<Vector4, TDest>, IConverter<Half2, TDest>, IConverter<Half4, TDest>, IConverter<UShort4, TDest>, IConverter<Byte4, TDest>, IConverter<Color, TDest>, ISemantic where TDest : unmanaged where TWriter : VertexBufferHelper.IWriter<TDest>
Parameters
writerTWriterAn implementation of VertexBufferHelper.IWriter<TDest>, implement this interface to write directly into the vertex buffer while making use of the auto-conversion of the
TSemanticprovided
Preferably as a struct to ensure it is inlined by the JITsemanticIndexintThe semantic to read with that index, starts at zero.
For example, to sample the second TextureCoordinate, you would usehelper.Write<TextureCoordinateSemantic, Vector2, YourWriter>(yourWriter, 1);
Returns
- bool
True when this semantic exists in the vertex buffer, false otherwise
Type Parameters
TSemanticThe semantic to read, PositionSemantic for example
TDestThe concrete type this writer will work with
TWriterA struct implementing VertexBufferHelper.IWriter<TDest> which will be called in turn to write into this buffer when this method is called.
Examples
Writing directly to mesh color:
Model.Meshes[0].Draw.VertexBuffers[0].AsReadable(Services, out VertexBufferHelper helper, out int count);
// Write to colors if that semantic already exist in the buffer, otherwise returns false
helper.Write<ColorSemantic, Vector4, MultColor>(new MultColor(){ Color = Color.Gray });
// Upload changes to the GPU
Model.Meshes[0].Draw.VertexBuffers[0].Buffer.Recreate(helper.DataOuter);
private struct MultColor : VertexBufferHelper.IWriter<Vector4>
{
public Color Color;
public unsafe void Write<TConverter, TSource>(byte* sourcePointer, int elementCount, int stride)
where TConverter : IConverter<TSource, Vector4>, IConverter<Vector4, TSource>
where TSource : unmanaged
{
for (byte* end = sourcePointer + elementCount * stride; sourcePointer < end; sourcePointer += stride)
{
TConverter.Convert(*(TSource*)sourcePointer, out var val);
val *= (Vector4)Color;
TConverter.Convert(val, out *(TSource*)sourcePointer);
}
}
}
Exceptions
- NotImplementedException
When the data format for this semantic is too arcane - no conversion logic is implemented for that type