Tech/LxEngine/Sandbox/Primitive Buffer
From Athile
Contents |
What is a Primitive Buffer?
The primitive buffer is a generalzation of a data buffer in a graphics system. It can be used to represent geometric or image data buffer.
A primitive buffer is composed of an array of streams. These streams in turn are composed of one or more channels of interleaved data. Each channel can be optionally named and semantically labelled.
The semantic labels allow for algorithms to be smart in choosing the correct data channels to activate or operate on. Likewise, the semantic associations allow operations to find all associated data on, for example, interpolation of vertex data after a vertex split.
The primitive buffer also manages iteration over channels so that algorithms can operate without concern about whether relevant data is interleaved or multi-stream.
The representation of the stream is abstracted from the interface, allowing system or video memory buffers to be used in the particular implementation.
The primitive buffer is also a higher-level object than exposed by GPU-based systems since it is not constrained to the capabilities of the hardware; for example, it is capable of storing and maintaining meshes with connectivity data.
Interface at a Glance
Primitive Buffer
- type
- stream[]
- name
- stride
- size
- channels[]
- name
- semantic name
- type
types include point_list, line_list, tri_list, quad_list, image
Example: Quad List
primitive_desc desc; desc.type("quad_list"); desc.stream("", vertexCount); desc.channel("", "vertexPosition", "float3"); desc.channel("", "vertexNormal", "float3"); desc.stream(vertexCount, ""); desc.channel("colorSetA", "vertexColor", "float3"); desc.stream(vertexCount, ""); desc.channel("colorSetB", "vertexColor", "float4"); desc.stream(faceCount, ""); desc.channel("", "faceIndex", "int16"); primitive_buffer buffer(desc); auto spPosition = buffer.map_channel<float3>("position"); for (int i = 0; i < vertexCount; ++i) spPosition[i] = float3(src[i].x, src[i].y, src[i].z); spPosition.unmap(); auto spStream = buffer.map_stream(0); for (int i = 0; i < vertexCount; ++i) spStream[i][0] = float3(src[i].x, src[i].y, src[i].z); spStream.unmap();
Example: RGB Image
primitive_desc desc; desc.type("image"); desc.stream("", width, height); desc.channel("", "color", "byte4") primitive_buffer buffer(desc); { auto spImage = buffer.map_stream(0); auto spPixels = buffer.map_channel<byte4>(0); for (int y = 0; y < spImage->size(1); ++y) { for (int x = 0; x < spImage->size(0); ++x) { spPixels(x, y) = byte4(255, 255, 255, 255); } } spPixels.unmap(); spImage.unmap(); }
Example: Heightmap
This example is incomplete and suggests design issues with the primitive buffer: how is it possible to hide the interpolation of the dependent vertex data from the algorithm (i.e. how can this "just work" regardless of the whether or not the incoming buffer has vertex color or vertex normals or both or neither?). It may be worth considering if a 'mesh' object yields a better design.
primitive_desc; desc.type("image"); desc.stream("", width, height); desc.channel("", "color", "ubyte4"); primitive_desc; desc.type("triangle_list"); desc.stream("", width * height); primitive_desc desc; desc.type("quad_list"); desc.stream("", 4); desc.channel("", "position", "float3"); desc.stream("", 1); desc.channel("", "faceIndex", "ushort4"); primitive_buffer mesh(desc); subdivide_quad(mesh, 0, width, height); void subdivide_quad(primitive_buffer& mesh, int faceIndex, int subX, int subY) { lx_check_error(mesh.type() == "quad_list"); auto spIndices = mesh.map_channel<uint4>("faceIndex"); uint4 index = spIndices[faceIndex]; spIndices->unmap(); auto spPoints = mesh.map_channel<float3>("vertexPosition"); point3 p0 = spPoints[ index.e0 ]; point3 p1 = spPoints[ index.e1 ]; point3 p2 = spPoints[ index.e2 ]; point3 p3 = spPoints[ index.e3 ]; // What about normalizing other vertex data? buffer(subX * subY); for (int x = 0; x < subX; x++) { float s = float(x) / float(subX); point3 p01 = interpolate(p0, p1, s); point3 p32 = interpolate(p3, p2, s); for (int y = 0; y < subY; y++) { float t = float(y) / float(subY); point3 q = interpolate(p01, p32, t); buffer[y * subX + x] = q; } } }