Skip to content

Plugin API Reference

Claude edited this page Nov 11, 2026 · 2 revisions

Plugin host API reference

Versions: this page covers plugin API 3, available since OxyBrush 3.0.7-beta (download the latest release). Releases before it run API 1 and 2; rows and sections marked (3) or "API 3" need 3.0.7-beta or later. This page is mirrored from docs/plugins in the repository.

An in-app plugin imports these functions from the module oxybrush. This is API version 3, which adds the rows marked (3), along with (2) from API 2. Older plugins keep working unchanged.

Conventions

  • Types: every argument and result is i32 or f32.
  • Pointers and lengths are byte offsets and counts in the plugin's exported memory. The host reads and writes only inside it, and checks the bounds.
  • Layouts:
    • positions, normals and colors are three little-endian f32 per vertex (x, y, z or r, g, b);
    • triangles are three u32 vertex indices, counter-clockwise seen from outside;
    • masks are one f32 per vertex;
    • face groups are one u32 per triangle;
    • hidden flags are one u8 per triangle.
  • Results: a negative result is an error code.
    • Reads return how many items they wrote, at most max.
    • Writes return 0.
  • Piece indices run from 0 to piece_count() - 1, in the order of the Pieces panel. active_piece() is the one selected.
  • The scene is a copy, taken when the command starts. Writes don't change what later reads return. All of a command's writes are applied together when it returns.

Exports the plugin provides

Export
memory required: the plugin's linear memory
run(command: i32) required: called with the index of the command clicked
overlay() optional: called each frame while the plugin's panel is open, to draw lines (needs overlay)

Functions

Always available

Function
log(ptr, len) UTF-8 text (up to 4096 bytes) for the status bar and the log file; the last line logged by a command is shown
param(index) -> f32 the current value of param index (see the manifest); 0 for an unknown index
param_color(index, ptr) -> i32 (3) a color param's value as three f32 (sRGB 0..=1) at ptr
progress(fraction: f32) -> i32 (3) report progress, 0..=1, shown as a bar; returns 1 once the user has clicked Stop, else 0
piece_count() -> i32 pieces in the scene
active_piece() -> i32 index of the active piece

Reading meshes: read_mesh

Function
vertex_count(piece) -> i32
triangle_count(piece) -> i32
read_positions(piece, ptr, max) -> i32 up to max positions
read_triangles(piece, ptr, max) -> i32 up to max triangles
read_mask(piece, ptr, max) -> i32 up to max mask values; 1 = fully protected
read_normals(piece, ptr, max) -> i32 (2) up to max unit vertex normals
read_colors(piece, ptr, max) -> i32 (2) up to max vertex colors, sRGB 0..=1, white where unpainted
read_groups(piece, ptr, max) -> i32 (3) up to max face groups
read_hidden(piece, ptr, max) -> i32 (3) up to max hidden flags, 1 = hidden

Changing meshes: edit_mesh

Each write replaces that attribute for the whole piece. count must be the piece's vertex count, or its triangle count for face groups and hidden flags. If a command writes the same attribute twice, the last write wins.

Function
write_positions(piece, ptr, count) -> i32 new positions for every vertex; non-finite values are refused
write_mask(piece, ptr, count) -> i32 (2) a mask value for every vertex, clamped to 0..=1
write_colors(piece, ptr, count) -> i32 (2) a color for every vertex, clamped to 0..=1
write_groups(piece, ptr, count) -> i32 (3) a face group for every triangle
write_hidden(piece, ptr, count) -> i32 (3) a hidden flag for every triangle; nonzero = hidden

Changing topology: edit_topology (3)

Function
write_mesh(piece, pos_ptr, vcount, tri_ptr, tcount) -> i32 replace the piece's vertices and triangles
  • Limits: 3 to 4,000,000 vertices, 1 to 4,000,000 triangles, finite positions, and indices below vcount.
  • Later writes use the new mesh. After write_mesh, this command's write_mask and write_colors take vcount, and write_groups and write_hidden take tcount. write_positions returns ERR_CONTEXT, since you just gave the positions.
  • Earlier writes are dropped. Anything written to the piece before write_mesh was for the old mesh, so it's discarded.
  • What isn't written starts fresh: unmasked, white, face group 0, all visible. To keep attributes, read them first and write them back after; the bundled Flip Faces plugin does this.
  • Subdivision levels of the piece are removed. Undo restores them, and the old mesh.

New pieces: add_pieces (2)

Function
add_piece(name_ptr, name_len, pos_ptr, vcount, tri_ptr, tcount) -> i32 a new piece, with the same limits as write_mesh; returns its index among this command's new pieces

All of a command's new pieces become one undo step, and the last one becomes active.

Scene: read_scene

Function
scene_state(ptr, cap) -> i32 the scene as JSON: pieces, their versions, the camera; writes up to cap bytes and returns the full length (call with cap 0 to size a buffer)

Overlay: overlay

Function
draw_line(x1, y1, z1, x2, y2, z2, rgba) -> i32 during overlay() only: a line in world space, color 0xRRGGBBAA; up to 20,000 lines a frame

Mesh functions return ERR_CONTEXT during overlay(), and draw_line returns it outside.

Error codes

Code Name Means
-1 ERR_DENIED the manifest doesn't declare the capability
-2 ERR_PIECE no piece with that index
-3 ERR_MEMORY a pointer or length outside the plugin's memory
-4 ERR_COUNT a count that doesn't match the piece, or values that aren't allowed (non-finite, indices out of range, over the limits)
-5 ERR_CONTEXT not allowed here: mesh functions in overlay(), draw_line outside it, write_positions after write_mesh

How edits are applied

When run returns, OxyBrush applies the command's writes:

  • each piece's edits as one undo step, labelled Plugin: <name> — <command>;
  • its new pieces as one more step.

If the piece changed while the command ran, its edits are skipped and the status bar says so. If the user clicked Stop, nothing is applied, even if the plugin finished anyway.

Limits

Fuel per command about 1,000,000,000 instructions, then the command is stopped
Fuel per overlay frame about 20,000,000 instructions
Memory 512 MB
Imports only the functions above; a module importing anything else doesn't load

Clone this wiki locally