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.