Mesh and Vbo

Vertices, colours, texture coordinates, normals and attributes drawn in one call, .obj and .ply loading, and geometry kept on the GPU. Instanced drawing is on the Instancing page.

Mesh

Mesh(
    vertices=None,
    colors=None,
    tex_coords=None,
    indices=None,
    mode=PrimitiveMode.TRIANGLES,
    normals=None,
    attributes=None,
)

Vertices with optional colours, uv, normals, attributes and indices.

Build it incrementally with add_vertex and friends, or in one call from numpy arrays::

mesh = kiln.Mesh(vertices=xy, colors=rgb, indices=tri, mode=kiln.PrimitiveMode.TRIANGLES)
mesh.draw()

Triangle modes are filled, with colours interpolated across each triangle and uv sampling the texture bound with with texture:. Triangles are not anti-aliased. LINES, LINE_STRIP and LINE_LOOP are stroked stroke(width=) points wide whatever the transform, and POINTS are anti-aliased dots as wide as the line width. draw(wireframe=True) strokes the triangle edges. Wireframes and line modes use the tint colour, not the stroke colour. Vertices are in the current transform’s local coordinates, applied when draw is called. z is stored but ignored in 2D. A mesh is cheap to redraw every frame (10,000 triangles take about a millisecond).

Colours are 0-255 like fill, and are multiplied by the tint when drawn, so tint(255, 128) fades a mesh. A mesh with no colours is drawn in the tint. Texture coordinates are normalised: (0, 0) is the texture’s top-left corner and (1, 1) its bottom-right. A line or point mode with colours uses each segment’s first vertex colour; strips and loops with colours have no mitred joins.

Normals and named attributes (mesh.attributes["age"] = ages) are for raw shaders (Shader.load): under one, the mesh is drawn as Vbo(mesh) and each reaches the vertex input of the same name (in vec3 normal;, in float age;). Kiln’s own drawing ignores them, and z, since it is 2D. load reads .obj and .ply files.

Parameters

Name Type Description Default
vertices ArrayLike | None Optional (n, 2) or (n, 3) array of positions. None
colors ArrayLike | None Optional (n, 3) or (n, 4) array, 0-255. None
tex_coords ArrayLike | None Optional (n, 2) array of normalised uv. None
indices ArrayLike | None Optional integer array of vertex indices, any shape. None
mode PrimitiveMode | PrimitiveModeValue How the vertices form shapes. PrimitiveMode.TRIANGLES
normals ArrayLike | None Optional (n, 3) array of normals. None
attributes dict | None Optional named per-vertex attributes, {name: array} (see attributes). None

Attributes

Name Type Description
mode The PrimitiveMode.

Methods

Name Description
add_color Appends a vertex colour.
add_colors Appends many vertex colours.
add_index Appends a vertex index.
add_indices Appends many vertex indices.
add_normal Appends a vertex normal.
add_normals Appends many vertex normals.
add_tex_coord Appends a texture coordinate.
add_tex_coords Appends many texture coordinates.
add_vertex Appends a vertex.
add_vertices Appends many vertices.
clear Removes all vertex data, attributes and indices.
draw Draws the mesh in its mode.
has_colors Returns whether the mesh has vertex colours.
has_indices Returns whether the mesh has indices.
has_normals Returns whether the mesh has normals.
has_tex_coords Returns whether the mesh has texture coordinates.
load Replaces the mesh with one read from an .obj or .ply file.

add_color

Mesh.add_color(color)

Appends a vertex colour.

Parameters
Name Type Description Default
color Iterable[float] | float A kiln.Color or an (r, g, b[, a]) sequence, 0-255; a single number is a gray. required

add_colors

Mesh.add_colors(colors)

Appends many vertex colours.

Parameters
Name Type Description Default
colors ArrayLike An (n, 3) or (n, 4) array, 0-255. required

add_index

Mesh.add_index(i)

Appends a vertex index.

Parameters
Name Type Description Default
i int The index of a vertex. required

add_indices

Mesh.add_indices(indices)

Appends many vertex indices.

Parameters
Name Type Description Default
indices Iterable[int] Vertex indices. required
Raises
Name Type Description
TypeError The indices are not integers.

add_normal

Mesh.add_normal(x, y, z)

Appends a vertex normal.

Parameters
Name Type Description Default
x float Normal x. required
y float Normal y. required
z float Normal z. required

add_normals

Mesh.add_normals(normals)

Appends many vertex normals.

Parameters
Name Type Description Default
normals ArrayLike An (n, 3) array. required

add_tex_coord

Mesh.add_tex_coord(u, v)

Appends a texture coordinate.

Parameters
Name Type Description Default
u float Horizontal, 0 at the left edge and 1 at the right. required
v float Vertical, 0 at the top edge and 1 at the bottom. required

add_tex_coords

Mesh.add_tex_coords(uv)

Appends many texture coordinates.

Parameters
Name Type Description Default
uv ArrayLike An (n, 2) array. required

add_vertex

Mesh.add_vertex(x, y, z=0.0)

Appends a vertex.

Parameters
Name Type Description Default
x float Position x. required
y float Position y. required
z float Position z, stored and ignored in 2D. 0.0

add_vertices

Mesh.add_vertices(xy)

Appends many vertices.

Parameters
Name Type Description Default
xy ArrayLike An (n, 2) or (n, 3) array. required

clear

Mesh.clear()

Removes all vertex data, attributes and indices.

draw

Mesh.draw(wireframe=False)

Draws the mesh in its mode.

Triangle modes are filled and, if a texture is bound, textured. The current transform, colour and blend mode apply (see the class docstring for how colours combine). Under a raw shader (Shader.load) the mesh is drawn as Vbo(mesh).draw() would draw it: by the program, with lines and points as the GPU draws them.

wireframe=True strokes the edges of the triangles instead: each edge once, stroke(width=) points wide whatever the transform, in the tint colour (not the stroke colour). Line and point modes draw the same either way.

Parameters
Name Type Description Default
wireframe bool Stroke the triangles’ edges instead of filling them. False
Raises
Name Type Description
ValueError A colour or texture-coordinate count differs from the vertex count, or an index is out of range.

has_colors

Mesh.has_colors()

Returns whether the mesh has vertex colours.

has_indices

Mesh.has_indices()

Returns whether the mesh has indices.

has_normals

Mesh.has_normals()

Returns whether the mesh has normals.

has_tex_coords

Mesh.has_tex_coords()

Returns whether the mesh has texture coordinates.

load

Mesh.load(path)

Replaces the mesh with one read from an .obj or .ply file.

Reads positions, normals, texture coordinates (flipped, so v = 0 is the top as in Kiln) and PLY vertex colours; polygons become triangle fans, and the mode TRIANGLES. A file without faces is a point cloud: its points, without indices, in mode POINTS. OBJ is read as ASCII; PLY as ascii or binary_little_endian. As Image.load, a file that can’t be read, or is malformed or cut short, leaves the mesh as it was and returns False, with a warning. Kiln draws in 2D, so a model’s z is the shader’s to use (see the Meshes tutorial).

Parameters
Name Type Description Default
path str | PathLike The file to read. required
Returns
Name Type Description
bool True if the file was read.

PrimitiveMode

PrimitiveMode()

How a mesh’s vertices form shapes.

"lines" works as well as PrimitiveMode.LINES.

  • TRIANGLES: every three vertices are a triangle, the default.
  • TRIANGLE_STRIP: each vertex after the first two makes a triangle with the previous two.
  • TRIANGLE_FAN: each vertex after the first two makes a triangle with the first vertex and the previous one.
  • LINES: every two vertices are a line segment.
  • LINE_STRIP: a connected polyline through the vertices.
  • LINE_LOOP: a polyline that closes back to the first vertex.
  • POINTS: a dot at each vertex.

Vbo

Vbo(
    mesh=None,
    vertices=None,
    colors=None,
    tex_coords=None,
    indices=None,
    mode=None,
    usage=BufferUsage.STATIC,
    normals=None,
    attributes=None,
    instance_attributes=None,
)

Vertices, colours, texture coordinates and indices kept in GPU buffers.

A Mesh is uploaded again every time it is drawn. A Vbo is uploaded once, when you give it data, and drawing it afterwards costs one draw call and no transfer, however many vertices it holds. Use it for geometry that stays put: terrain, a big parametric surface, a particle sprite sheet, a million-point cloud. Draw it as often as you like (100 times a frame is cheap), each time under a different transform or colour::

vbo = kiln.Vbo(mesh)          # or Vbo(vertices=xy, colors=rgb, indices=tri)
...
kiln.rotate(angle)            # radians
vbo.draw()

A Vbo draws exactly as the Mesh it came from would: positions in the current transform’s local coordinates (z is stored and ignored in 2D), vertex colours times the current colour, a texture bound with with texture: sampled with the texture coordinates, any blend mode, inside an Fbo, and under a Shader material. The differences: lines are one pixel wide hairlines and points are squares stroke(width=) pixels across (a Mesh strokes real lines and round dots; the GPU draws only these), and triangles are not anti-aliased, as for Mesh. Vertex colours are stored as 8 bits per channel.

A texture bound to a Vbo that has no tex_coords is sampled at its texel (0, 0) everywhere, exactly as for a Mesh without them. Keywords given with a mesh override that mesh’s arrays: Vbo(mesh, vertices=xy) takes the mesh’s colours, texture coordinates and indices and the new vertices; mode overrides the mesh’s mode too.

Change data with the upload_* methods (they replace an attribute, possibly with a different size) or, for a few vertices, the write_* methods, which upload only the range you give. Earlier draws of the frame always keep the data they were drawn with. Replacing a whole attribute costs only its upload, even after the Vbo was drawn in the same frame (in update or draw): it goes into a fresh copy of the buffer. Two cases render the frame so far first instead: a rewrite once all 16 copies of an attribute were drawn in the frame, and new data that outgrows the Vbo’s buffers. A partial update of a Vbo already drawn in the frame first renders the frame so far and then writes into a buffer the GPU may still be reading, which on macOS stalls the CPU for about 0.1 ms per update; do those at the top of the frame (in update), or rewrite the whole attribute.

Memory is pooled: a released or garbage-collected Vbo gives its GPU buffers (and the pipelines built for them) to the next Vbo of a similar size, so making and dropping Vbos every frame builds nothing new.

For raw shaders (Shader.load) a Vbo also holds normals and named attributes, per-vertex values of your own (attributes={"age": ages} or upload_attribute("age", ages)), which reach the vertex inputs of the same name: in vec3 normal;, in float age;. Kiln’s own drawing ignores them, and z.

Instancing. Per-instance attributes (instance_attributes={"center": xy}, or divisor=1) hold one value per instance: draw() under a raw shader then draws the whole Vbo once per row of them, in one draw call, and the vertex shader places each copy with its values (and gl_InstanceID)::

swarm = kiln.Vbo(fish_mesh, instance_attributes={"center": xy, "tint": rgba})
with shader:
    swarm.draw()                   # len(xy) fish; or draw(instances=n)

Rewriting per-instance data every frame is cheap, even after a draw in the same frame: a whole rewrite (upload_attribute, or a write_attribute of all of it) goes into another copy of the buffer while the earlier draw keeps reading its own, so nothing waits for the GPU (within the two limits above).

Parameters

Name Type Description Default
mesh Mesh | None Optional Mesh to copy (see upload_mesh); its mode becomes the Vbo’s. None
vertices ArrayLike | None Optional (n, 2) or (n, 3) array of positions. None
colors ArrayLike | None Optional (n, 3) or (n, 4) array, 0-255. None
tex_coords ArrayLike | None Optional (n, 2) array of normalised uv. None
indices ArrayLike | None Optional integer array of vertex indices, any shape. None
mode PrimitiveMode | PrimitiveModeValue | None How the vertices form shapes; the mesh’s or TRIANGLES if omitted. None
usage BufferUsage | BufferUsageValue A BufferUsage hint. BufferUsage.STATIC
normals ArrayLike | None Optional (n, 3) array of normals. None
attributes dict | None Optional named per-vertex attributes, {name: array} (see upload_attribute). None
instance_attributes dict | None Optional named per-instance attributes, {name: array}, one row per instance (upload_attribute(..., divisor=1)). None

Attributes

Name Type Description
mode PrimitiveMode The default PrimitiveMode of draw.
usage The BufferUsage hint.
uploads How many GPU buffer writes this Vbo has made. Drawing never adds to it; tests and profiles can watch it.
uploaded_bytes How many bytes those writes moved.

Methods

Name Description
draw Draws the Vbo, or a range of it.
draw_elements Draws a range of the indices.
has_colors Returns whether the Vbo has vertex colours.
has_indices Returns whether the Vbo has indices.
has_normals Returns whether the Vbo has normals.
has_tex_coords Returns whether it has texture coordinates.
release Frees the GPU buffers and empties the Vbo.
upload_colors Replaces the vertex colours.
upload_indices Replaces the indices.
upload_mesh Replaces everything with a Mesh’s data and mode.
upload_normals Replaces the normals.
upload_tex_coords Replaces the texture coordinates.
upload_vertices Replaces the vertex positions.
write_attribute Overwrites some values of a named attribute.
write_colors Overwrites some vertex colours in place.
write_indices Overwrites some indices in place.
write_normals Overwrites some normals in place.
write_tex_coords Overwrites some texture coordinates in place.
write_vertices Overwrites some vertex positions in place.

draw

Vbo.draw(mode=None, first=0, count=None, instances=None)

Draws the Vbo, or a range of it.

With indices the range counts indices, otherwise vertices (draw_elements always reads indices). The current transform, colour, blend mode, target, bound texture and shader apply, as for Mesh.draw. Under a raw shader (Shader.load) the program draws it, reading the Vbo’s data through its attributes (see Shader), once per instance when it has per-instance attributes.

Parameters
Name Type Description Default
mode PrimitiveMode | PrimitiveModeValue | None How to read the vertices; the Vbo’s own mode if omitted. None
first int The first vertex (or index) to draw. 0
count int | None How many; all from first on if omitted. None
instances int | None How many copies a raw shader draws, in one draw call; if omitted, as many as the per-instance attributes have, or one without them. 0 draws nothing. None
Raises
Name Type Description
ValueError The range is outside the data, a colour or texture coordinate count differs from the vertex count, an index is out of range, or a per-instance attribute has fewer rows than instances (or, without it, they differ).
TypeError instances is not an integer.
RuntimeError No App is running, the Vbo’s window has closed, or instances is given without a raw shader.

draw_elements

Vbo.draw_elements(mode=None, count=None, offset=0, instances=None)

Draws a range of the indices.

Parameters
Name Type Description Default
mode PrimitiveMode | PrimitiveModeValue | None How to read the vertices; the Vbo’s own mode if omitted. None
count int | None How many indices; all from offset on if omitted. None
offset int The first index to draw. 0
instances int | None How many instances, as for draw. None
Raises
Name Type Description
ValueError The Vbo has no indices, or as for draw.
TypeError As for draw.
RuntimeError No App is running, or as for draw.

has_colors

Vbo.has_colors()

Returns whether the Vbo has vertex colours.

has_indices

Vbo.has_indices()

Returns whether the Vbo has indices.

has_normals

Vbo.has_normals()

Returns whether the Vbo has normals.

has_tex_coords

Vbo.has_tex_coords()

Returns whether it has texture coordinates.

release

Vbo.release()

Frees the GPU buffers and empties the Vbo.

Draws made earlier in the frame are kept. The buffers go back to a pool, with their pipelines, for the next Vbo of a similar size, so making and releasing Vbos often is cheap. Vbos are also released when garbage collected, and with their window. The Vbo can be filled again afterwards.

upload_colors

Vbo.upload_colors(colors, usage=None)

Replaces the vertex colours.

Parameters
Name Type Description Default
colors ArrayLike An (n, 3) or (n, 4) array, 0-255 (alpha 255 when omitted), n the vertex count. required
usage BufferUsage | BufferUsageValue | None A BufferUsage hint; the current one if omitted. None
Raises
Name Type Description
ValueError The array has another shape.
RuntimeError No App is running.

upload_indices

Vbo.upload_indices(indices, usage=None)

Replaces the indices.

Parameters
Name Type Description Default
indices ArrayLike An integer array of vertex indices, any shape (it is flattened). required
usage BufferUsage | BufferUsageValue | None A BufferUsage hint; the current one if omitted. None
Raises
Name Type Description
TypeError The indices are not integers.
ValueError An index is negative.
RuntimeError No App is running.

upload_mesh

Vbo.upload_mesh(mesh, usage=None)

Replaces everything with a Mesh’s data and mode.

Parameters
Name Type Description Default
mesh Mesh The Mesh to copy: its vertices, colours (if it has any), texture coordinates, normals, named attributes, indices and mode. required
usage BufferUsage | BufferUsageValue | None A BufferUsage hint; the current one if omitted. None
Raises
Name Type Description
ValueError The mesh’s data has an unsupported shape.
RuntimeError No App is running.

upload_normals

Vbo.upload_normals(normals, usage=None)

Replaces the normals.

A raw shader reads them as in vec3 normal;; Kiln’s 2D drawing ignores them.

Parameters
Name Type Description Default
normals ArrayLike An (n, 3) array, n the vertex count. required
usage BufferUsage | BufferUsageValue | None A BufferUsage hint; the current one if omitted. None
Raises
Name Type Description
ValueError The array has another shape.
RuntimeError No App is running.

upload_tex_coords

Vbo.upload_tex_coords(tex_coords, usage=None)

Replaces the texture coordinates.

Parameters
Name Type Description Default
tex_coords ArrayLike An (n, 2) array of normalised uv (0, 0 top-left), n the vertex count. required
usage BufferUsage | BufferUsageValue | None A BufferUsage hint; the current one if omitted. None
Raises
Name Type Description
ValueError The array has another shape.
RuntimeError No App is running.

upload_vertices

Vbo.upload_vertices(vertices, usage=None)

Replaces the vertex positions.

Parameters
Name Type Description Default
vertices ArrayLike An (n, 2) or (n, 3) array; z is stored, ignored in 2D. required
usage BufferUsage | BufferUsageValue | None A BufferUsage hint; the current one if omitted. None
Raises
Name Type Description
ValueError The array has another shape.
RuntimeError No App is running.

write_attribute

Vbo.write_attribute(name, data, offset=0)

Overwrites some values of a named attribute.

Parameters
Name Type Description Default
name str The attribute’s name. required
data ArrayLike An (m,) or (m, k) array of the attribute’s type and components. required
offset int The index of the first value to overwrite. 0
Raises
Name Type Description
ValueError The Vbo has no such attribute, the range is outside it, or the data’s components or type differ.
RuntimeError No App is running.

write_colors

Vbo.write_colors(colors, offset=0)

Overwrites some vertex colours in place.

Parameters
Name Type Description Default
colors ArrayLike An (m, 3) or (m, 4) array, 0-255. required
offset int The index of the first vertex to overwrite. 0
Raises
Name Type Description
ValueError The Vbo has no colours, or the range is outside them.
RuntimeError No App is running.

write_indices

Vbo.write_indices(indices, offset=0)

Overwrites some indices in place.

Parameters
Name Type Description Default
indices ArrayLike An integer array (flattened). required
offset int The position of the first index to overwrite. 0
Raises
Name Type Description
ValueError The Vbo has no indices, or the range is outside them.
TypeError The indices are not integers.
RuntimeError No App is running.

write_normals

Vbo.write_normals(normals, offset=0)

Overwrites some normals in place.

Parameters
Name Type Description Default
normals ArrayLike An (m, 3) array. required
offset int The index of the first vertex to overwrite. 0
Raises
Name Type Description
ValueError The Vbo has no normals, or the range is outside them.
RuntimeError No App is running.

write_tex_coords

Vbo.write_tex_coords(tex_coords, offset=0)

Overwrites some texture coordinates in place.

Parameters
Name Type Description Default
tex_coords ArrayLike An (m, 2) array. required
offset int The index of the first vertex to overwrite. 0
Raises
Name Type Description
ValueError The Vbo has no tex_coords, or the range is outside them.
RuntimeError No App is running.

write_vertices

Vbo.write_vertices(vertices, offset=0)

Overwrites some vertex positions in place.

Only the bytes of vertices are uploaded. See the class docstring for the cost of updating a Vbo that was drawn earlier in the same frame.

Parameters
Name Type Description Default
vertices ArrayLike An (m, 2) or (m, 3) array. required
offset int The index of the first vertex to overwrite. 0
Raises
Name Type Description
ValueError The range offset to offset + m is outside the vertices, or the array has another shape.
RuntimeError No App is running.

BufferUsage

BufferUsage()

How often a Vbo’s data changes, a hint to the driver. Equivalent to GL_*_DRAW.

The members stand for the OpenGL usage hints (GL_STATIC_DRAW and friends); "dynamic" works as well as BufferUsage.DYNAMIC. On Apple’s drivers the hint changes nothing you can measure; it is kept so ported GL code keeps its hints.

  • STATIC: set once, drawn many times (the default).
  • DYNAMIC: changed now and then, drawn many times.
  • STREAM: changed every frame.