Custom 3D shapes

BeginShape()/Vertex()/EndShape() (from Custom shapes) work under Renderer3D too — they build and draw a real triangle mesh instead of an SKPath. Triangulation happens on the CPU; the result uploads as one GL_TRIANGLES draw call, the same way Box()/Sphere() already work.

3D renderer

On this page · 3 entries

Normal

  • Normal(float nx, ny, nz)

Sets the current normal vector. Affects vertices added after this call — call it once per face, before that face's vertices, for flat shading (the faceted-gem example below), or share one normal across several vertices for smooth shading. Default before any call: (0, 0, 1).

Vertex

  • Vertex(x, y, float z)
  • Vertex(x, y, z, u, v)

3D vertex overloads of Vertex(), only valid inside BeginShape()/EndShape() while running under Renderer3D — the plain Vertex(x, y) also works there, treated as z = 0. The 5-argument overload adds texture coordinates (u, v) in 0–1, looked up in whatever image Texture() set for this shape — the same Texture()/NoTexture() the 2D mesh path already uses.

ShapeKind under Renderer3Dtriangulation
TrianglesConsecutive groups of 3.
TriangleStripShared-edge strip, winding alternates by parity.
TriangleFanAll triangles share vertex 0.
QuadsGroups of 4, split into 2 triangles each.
QuadStripShared-edge quad strip, split into 2 triangles each.
PolygonFan-triangulated from vertex 0 — correct for convex shapes, same limitation Processing itself documents for concave ones under P3D.
Points / LinesNot supported — no meaningful normal to light them with. Throws NotSupportedException.
C#
// Faceted octahedron -- each face calls Normal() (via PVector.Cross()
// between two edges) before its 3 Vertex() calls, for flat shading.
BeginShape(ShapeKind.Triangles);
var faceNormal = PVector.Cross(PVector.Sub(b, a), PVector.Sub(c, a)).Normalize();
Normal(faceNormal.X, faceNormal.Y, faceNormal.Z);
Vertex(a.X, a.Y, a.Z);
Vertex(b.X, b.Y, b.Z);
Vertex(c.X, c.Y, c.Z);
// ... repeat per face ...
EndShape();

CreateShape3D

  • CreateShape3D(ShapeKind kind, Action buildVertices) → PShape

Builds a reusable 3D PShape once — like combining Processing's createShape() with its own beginShape()/endShape(). Call the same Vertex()/Normal() calls you'd use with immediate-mode BeginShape()/EndShape() inside buildVertices — don't call BeginShape()/EndShape() yourself in there, CreateShape3D() wraps both. The mesh uploads to the GPU exactly once (StaticDraw); draw it as many times as you want afterward with Shape(shape, x, y) instead of re-recording and re-uploading the same geometry every frame the way a bare BeginShape()/EndShape() call would.

No BeginDraw()/EndDraw() needed. This claims and releases the GL context on its own — call it loose, typically once in Setup().
Texture() goes inside the callback. BeginShape() (called internally by CreateShape3D(), before buildVertices runs) resets the shape's texture — so call Texture(img) as the first line inside buildVertices, not before CreateShape3D().
C#
_gemShape = CreateShape3D(ShapeKind.Triangles, () =>
{
    // ... Normal()/Vertex() per face, as above ...
});

// every frame, in Draw():
PushMatrix();
Translate(x, y, z);
Shape(_gemShape, 0, 0);   // no re-triangulating or re-uploading anything
PopMatrix();
Scaling. Unlike the 2D Shape() overload, w/h scaling isn't supported for a 3D mesh — a baked mesh's own coordinates already define its size. Wrap the call in your own PushMatrix()/Scale()/Rotate()/PopMatrix() instead.