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.
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 Renderer3D | triangulation |
|---|---|
| Triangles | Consecutive groups of 3. |
| TriangleStrip | Shared-edge strip, winding alternates by parity. |
| TriangleFan | All triangles share vertex 0. |
| Quads | Groups of 4, split into 2 triangles each. |
| QuadStrip | Shared-edge quad strip, split into 2 triangles each. |
| Polygon | Fan-triangulated from vertex 0 — correct for convex shapes, same limitation Processing itself documents for concave ones under P3D. |
| Points / Lines | Not supported — no meaningful normal to light them with. Throws NotSupportedException. |
// 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.
Setup().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()._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();
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.