Table of Contents

Struct VertexBufferHelper

Namespace
Stride.Graphics
Assembly
Stride.Graphics.dll
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

binding VertexBufferBinding

The bindings for this buffer

services IServiceRegistry

The service used to retrieve the buffer from disk/GPU if it wasn't found through other means

count int

The 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

binding VertexBufferBinding
dataOuter byte[]
count int

Exceptions

ArgumentException

dataOuter does not match the binding definition provided, dataOuter must be the entire vertex buffer

Fields

Binding

public readonly VertexBufferBinding Binding

Field Value

VertexBufferBinding

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

Span<byte>

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

destination Span<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 TDest was 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

buffer Span<TValue>

The buffer which will be written to, must have exactly the same amount of items as there are vertices in the buffer

semanticIndex int

The semantic to read with that index, starts at zero.
For example, to sample the second TextureCoordinate, you would use

helper.Copy<TextureCoordinateSemantic, Vector2>(myUvs, 1);

Returns

bool

True when this semantic exists in the vertex buffer, false otherwise

Type Parameters

TSemantic

The semantic to read, for example PositionSemantic

TValue

The value type to read, depends entirely on the TSemantic used

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

destination Span<TDest>

The destination span your TReader Read<TConverter, TSource>(byte*, int, int, Span<TDest>) method receives, you may pass Empty if you do not need one.

reader TReader

An implementation of VertexBufferHelper.IReader<TDest>, implement this interface to read directly from the vertex buffer while making use of the auto-conversion of the TSemantic provided
Preferably as a struct to ensure it is inlined by the JIT

semanticIndex int

The semantic to read with that index, starts at zero.
For example, to sample the second TextureCoordinate, you would use

helper.Read<TextureCoordinateSemantic, Vector2>(yourReader, 1);

Returns

bool

True when this semantic exists in the vertex buffer, false otherwise

Type Parameters

TSemantic

The semantic to read, PositionSemantic for example

TDest

The value type to read, depends on the TSemantic used, Vector3 when your TSemantic PositionSemantic for example

TReader

The 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

writer TWriter

An implementation of VertexBufferHelper.IWriter<TDest>, implement this interface to write directly into the vertex buffer while making use of the auto-conversion of the TSemantic provided
Preferably as a struct to ensure it is inlined by the JIT

semanticIndex int

The semantic to read with that index, starts at zero.
For example, to sample the second TextureCoordinate, you would use

helper.Write<TextureCoordinateSemantic, Vector2, YourWriter>(yourWriter, 1);

Returns

bool

True when this semantic exists in the vertex buffer, false otherwise

Type Parameters

TSemantic

The semantic to read, PositionSemantic for example

TDest

The concrete type this writer will work with

TWriter

A 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