Pure-JavaScript CarbonEngineJS-facing package for the STL triangle-mesh format. It reads ASCII/binary STL, writes ASCII/binary STL from the shared CarbonEngineJS JSON mesh schema, and can inspect geometry for the common topology problems that block useful 3D prints.
CarbonEngine and Fenris Creations (CCP Games) are named in this package for interoperability and target-ecosystem context only. STL itself is not a CCP format, and this package contains no CarbonEngine or Fenris Creations (CCP Games) source code.
- npm: https://www.npmjs.com/package/@carbonenginejs/format-stl
- package:
@carbonenginejs/format-stl - version:
0.1.2 - license:
MIT - runtime: Node
>=18, modern browsers - module: ESM, package root exports
CjsFormatStl
npm install @carbonenginejs/format-stlThe package root exports one public class: CjsFormatStl. The Cjs prefix
marks this as a CarbonEngineJS format/construction boundary, not an engine
runtime class.
import CjsFormatStl from "@carbonenginejs/format-stl";
const stl = new CjsFormatStl({
emit: "shared", // "shared"/"json"/"stlJson" default | "gr2" | "cmf"
source: "mesh.stl", // name written to grannyFileSource
binary: true, // true writes binary STL; false writes ASCII STL
solidName: "mesh",
scale: 1,
recalculateNormals: true,
weldVertices: false, // false preserves STL facet normals on read
weldTolerance: 1e-5, // used by read welding and inspect topology
skipDegenerate: true,
requireWatertight: false, // true rejects writes with printability blockers
classes: {
Root: CjsGeometryRoot,
Mesh: CjsGeometryMesh,
IndexGroup: CjsGeometryIndexGroup
}
});
const json = stl.Read(stlBytes);
const report = stl.Inspect(json);
const bytes = stl.Write(json, { requireWatertight: true });The named export is the same class for callers that prefer named imports:
import { CjsFormatStl } from "@carbonenginejs/format-stl";- The package root exports one public format class:
CjsFormatStl. - Instance methods are PascalCase because format instances can hydrate or sit beside CarbonClasses without colliding with camelCase data fields.
- Static one-shot methods are camelCase because they live on
CjsFormatStlitself, not on hydrated CarbonClass instances. src/CjsFormatStl.jsis the public format boundary. STL parser, writer, topology, and hydration helpers live undersrc/core.Read/ staticreadreturn the shared JSON schema by default.emit: "shared"is the canonical semantic output token;"json"and"stlJson"remain compatibility spellings.emit: "gr2"andemit: "cmf"are explicit class-hydration targets and require caller classes. Parser state has no raw read output.Write/ staticwritereturnUint8Arrayfor binary STL andstringfor ASCII STL.Inspect/ staticinspectaccepts STL text, STL bytes, a shared JSON root, or one shared JSON mesh.ToJSON/ statictoJSONconverts format output to JSON-compatible data. It is not an STL writer and does not return JSON text.
STL is triangle-only. It cannot represent UVs, materials, bones, morph targets, animations, transforms, or tangent-space data. The read path emits the shared mesh-side schema with unsupported branches present as empty arrays:
Root
|-- grannyFileFormatRevision, grannyFileSource
|-- meshes: Mesh[]
| |-- name, minBounds, maxBounds
| |-- boneBindings: []
| |-- morphTargets: []
| |-- vertex: VertexChannels
| | |-- position, normal
| | |-- tangent: [], binormal: []
| | |-- texcoord0: [], texcoord1: []
| | `-- blendIndice: [], blendWeight: []
| `-- indices: IndexGroup[]
| `-- name, bytesPerIndex, faces
|-- models: []
`-- animations: []
IndexGroup.faces is a flat triangle-index array in groups of three.
- ASCII STL read/write
- Binary STL read/write
- Per-facet STL normals
- Optional vertex welding on read
- Shared JSON root or mesh input for writes
- Node convenience helpers:
readFile(path, options)andwriteFile(path, input, options) - Sniff helpers:
isStl(input)andisBinaryStl(input)
Inspect is deliberately conservative. It does not repair meshes, but it does
tell callers whether a mesh has the obvious blockers before writing or sending
it to a slicer:
- open edges
- non-manifold edges
- inconsistent winding edges
- degenerate triangles
- shell count
- unique vertex and edge counts
- axis-aligned bounds
- parsed STL format, solid name, and source byte length
requireWatertight: true runs the same checks during Write and throws if the
mesh is not closed and consistently wound.
emit:"shared"/"json"/"stlJson"default,"gr2", or"cmf".source: default"memory". Written togrannyFileSourceand reports.binary: defaulttrue.truewrites binary STL bytes;falsewrites ASCII STL text.solidName: default"carbonenginejs". Used for ASCIIsolidnames and the binary header.scale: default1. Applied when writing JSON geometry to STL.recalculateNormals: defaulttrue. Writer recomputes STL facet normals from triangle winding.falseuses averaged vertex normals when present.weldVertices: defaultfalse. On read,falsepreserves each STL facet as separate vertices so facet normals remain exact.truewelds matching positions byweldToleranceand regenerates vertex normals.weldTolerance: default1e-5. Used for read welding and inspect topology.skipDegenerate: defaulttrue. Writer skips zero-area triangles.requireWatertight: defaultfalse. Writer throws when inspection finds printability blockers.classes: optional fornew CjsFormatStl(options),Read, and staticread. Accepted keys areRoot,Mesh, andIndexGroup, exposed asCjsFormatStl.CLASS_KEYS. Supplied classes must be constructible with no required arguments and implementSetValues(values).
Cutting a model into printable pieces should be a higher-level geometry helper, not a half-hidden STL parser feature. The useful version needs plane or volume cuts, cap generation, consistent winding, optional alignment keys, and a fresh inspection pass for every produced piece.
Turning normal maps into real geometry is also a conversion helper rather than an STL feature. Normal maps describe surface direction, not direct height, so a good helper will need a base mesh or tessellated surface, UVs, tangent frames, height/displacement controls, and validation against the target print scale.
Both helpers should eventually live beside this package or in shared geometry
math once @carbonenginejs/core-math exists.
npm testTests are fully self-contained and use inline geometry. They do not require local asset folders, network access, or game files.
MIT (see LICENSE and NOTICE).
This package contains no CarbonEngine or Fenris Creations (CCP Games) code. Its input/output shape targets the shared CarbonEngineJS JSON mesh schema used for CarbonEngine interoperability.