Appearance
@carrot/engine-meshes ​
ts
CPU-side mesh data and procedural geometry generators - primitives, subdivided planes, and path-to-mesh ribbon generation.
Installation ​
bash
npm install @carrot/engine-meshesArchitecture / How It Works ​
Mesh ​
The core Mesh class is pure CPU data - vertex attribute buffers and triangle indices with no WebGL dependency. The renderer uploads mesh data to the GPU via its own pipeline.
Attribute layout follows a convention matching standard shader locations:
- 0: positions (vec3)
- 1: normals (vec3)
- 2: tangents (vec4)
- 3-8: uv1-uv6 (vec2)
- 9: colors (vec4)
All attributes are optional. Dirty tracking (isDirty) lets the renderer know when to re-upload.
VertexView ​
A zero-allocation view used by forEachVertex(). One VertexView instance is reused across all vertices - getters and setters read/write directly into the underlying FloatBuffers at stride offsets. This avoids per-vertex object allocation while providing a clean API for vertex manipulation.
Primitive generators ​
Five generators create common shapes: meshFromQuad, meshFromTriangle, meshFromCircle, meshFromPlane, meshFromCube. All return a Mesh with positions, normals, UV1, and indices populated.
Path-to-mesh generators ​
Five generators create ribbon/strip meshes from polylines and bezier curves:
meshFromStrip- open ribbon from a polyline (roads, trails, rivers)meshFromLoop- closed ribbon from a polyline loop (borders, fences)meshFromBezier- ribbon from a single bezier curvemeshFromBezierStrip- ribbon from a multi-segment bezier stripmeshFromBezierLoop- closed ribbon from a bezier loop
All path generators produce meshes in the XY plane (Z=0) with UV mapping: U across width (0 left, 1 right), V along length (normalised by cumulative distance).
Normal computation ​
computeNormals() calculates smooth normals from positions and indices - accumulates face normals at shared vertices and normalises. Works for any indexed triangle mesh.
Dependencies ​
| Package | Used For |
|---|---|
@carrot/engine-buffers | FloatBuffer, IndexBuffer for vertex/index data |
@carrot/maths-geometry | Vector2Like, Bezier2, BezierStrip2, BezierLoop2 for geometry types |
@carrot/colors | ColorRgbLike for setColorsAll() |
Build ​
bash
npm run build # runs tscUsage Guide ​
CPU-side mesh data and procedural geometry generators - create primitives, subdivided planes, and path-following ribbons.
Import ​
ts
import {
Mesh, VertexView,
meshFromQuad, meshFromTriangle, meshFromCircle, meshFromPlane, meshFromCube,
meshFromStrip, meshFromLoop, meshFromBezier, meshFromBezierStrip, meshFromBezierLoop,
} from '@carrot/engine-meshes';Common Patterns ​
1. Creating a quad ​
ts
const quad = meshFromQuad(2, 1); // 2 units wide, 1 unit tall, centered at origin2. Creating a triangle ​
ts
const tri = meshFromTriangle(
{ x: -1, y: 0 },
{ x: 1, y: 0 },
{ x: 0, y: 1.5 },
);3. Creating a circle ​
ts
const circle = meshFromCircle(0.5, 64); // radius 0.5, 64 segments4. Subdivided terrain plane ​
ts
const terrain = meshFromPlane(100, 100, 64, 64); // 100x100 units, 64x64 subdivisions5. Cube ​
ts
const cube = meshFromCube(2); // 2-unit cube, hard edges, 24 verts6. Road/trail from polyline ​
ts
const roadPoints = [
{ x: 0, y: 0 }, { x: 10, y: 5 }, { x: 20, y: 3 }, { x: 30, y: 8 },
];
const road = meshFromStrip(roadPoints, 2, 'Road'); // 2 units wide7. Closed border from loop ​
ts
const borderPoints = [
{ x: 0, y: 0 }, { x: 10, y: 0 }, { x: 10, y: 10 }, { x: 0, y: 10 },
];
const border = meshFromLoop(borderPoints, 0.5, 'Border');8. Bezier curve ribbon ​
ts
const ribbon = meshFromBezier(bezierCurve, 1.0, 64, 'CurveRibbon');9. Per-vertex manipulation ​
ts
const plane = meshFromPlane(10, 10, 32, 32);
plane.forEachVertex((v, i) => {
v.pz = noise(v.px, v.py); // displace height
v.u2 = v.px / 10; // project into UV2
v.v2 = v.py / 10;
});10. Setting vertex colours ​
ts
const mesh = meshFromQuad(1);
mesh.setColorsAll({ r: 1, g: 0.5, b: 0, a: 1 }); // orange11. Computing normals ​
ts
const mesh = meshFromPlane(10, 10, 32, 32);
// ... displace vertices ...
mesh.computeNormals(); // recalculate smooth normals from modified positions12. Building a mesh manually ​
ts
import { FloatBuffer, IndexBuffer } from '@carrot/engine-buffers';
const mesh = new Mesh('Custom', {
positions: new FloatBuffer([0, 0, 0, 1, 0, 0, 0.5, 1, 0]),
normals: new FloatBuffer([0, 0, 1, 0, 0, 1, 0, 0, 1]),
uv1: new FloatBuffer([0, 0, 1, 0, 0.5, 1]),
indices: new IndexBuffer([0, 1, 2]),
});13. Cloning ​
ts
const copy = mesh.clone('MyCopy');
// Independent copy - modifying copy doesn't affect original14. Dirty tracking (renderer integration) ​
ts
if (mesh.isDirty) {
uploadToGpu(mesh);
mesh.clearDirty();
}