Repository navigation
Model files
loaders/obj.mojo, loaders/stl.mojo, loaders/ply.mojo and loaders/gltf.mojo. read_obj reads a Wavefront OBJ file into named objects, each with a BufferGeometry. parse_obj reads the text of one. read_stl and read_ply read an STL or a PLY file into one BufferGeometry. read_gltf reads a glTF 2.0 file, .gltf or .glb, into a scene and its assets.

three.js: OBJLoader, STLLoader and PLYLoader.
var model = read_obj("assets/cube.obj")
for index in range(model.count()):
print(model.objects[index].name, model.objects[index].material)
var shape = assets.geometries.add(model.objects[0].take_geometry())| Function | Meaning |
|---|---|
read_obj(path) -> ObjModel |
Read a file. |
parse_obj(text) -> ObjModel |
Read the text of one. |
ObjModel.objects holds one ObjObject per object, in file order. count() says how many.
| Field | Meaning |
|---|---|
name |
From o or g. Empty when the file has neither. Faces read before the first o or g belong to it, as three.js has it. |
material |
The name from usemtl. Empty before one. The material library is not read. |
geometry |
A non-indexed BufferGeometry with position, and with normal and uv when the faces name them. |
take_geometry() swaps the geometry out for an empty one, so it can go into a store. A BufferGeometry moves and does not copy.
| Line | Meaning |
|---|---|
v x y z |
A position. A fourth number is ignored. |
vt u v |
A texture coordinate. |
vn x y z |
A normal. |
f a b c ... |
A face of three or more corners, cut into a fan of triangles. A polygon must be convex. Each corner is v, v/vt, v//vn or v/vt/vn. |
o name, g name
|
A new object. One with no faces is dropped. |
usemtl name |
A new object under that material, with the same name. |
# |
A comment, to the end of the line. |
An index counts from one. A negative index counts back from the last entry so far. mtllib, s, l, p and unknown lines are skipped.
Every face of one object must agree about normals and texture coordinates. A geometry without normals shades flat. See Renderer.
The parser raises, naming the line, for:
- A
v,vtorvnline with too few coordinates. A coordinate that is not a number, or is not finite as aFloat32. - A face with fewer than three corners, or a corner with more than three parts.
- A face of four or more corners that is not convex, or has no area. A fan covers a convex polygon and only that.
- An index that is not a whole number, is zero, or names an entry the file does not have.
- A face that names a normal or a texture coordinate where an earlier face of the object did not, or the other way round.
read_obj raises for a file it cannot read.
assets/cube.obj is a unit cube with normals and texture coordinates. tests/test_obj.mojo reads it and draws it.
loaders/stl.mojo. read_stl(path) reads an STL file, binary or ASCII, into an StlModel. three.js: STLLoader.
var model = read_stl("assets/tetrahedron.stl")
print(model.geometry.triangle_count(), model.has_colors(), model.alpha)
var shape = assets.geometries.add(model.take_geometry())| Function | Meaning |
|---|---|
read_stl(path) -> StlModel |
Read a file. |
parse_stl(bytes) -> StlModel |
Read the bytes of one, binary or ASCII. |
parse_stl_text(text) -> StlModel |
Read the text of an ASCII file. |
is_binary_stl(bytes) -> Bool |
Tell the two encodings apart, as three.js does. |
A file exactly as long as its face count says is binary. Otherwise, a file with solid in its first ten bytes is ASCII. Any other file is binary.
| Field | Meaning |
|---|---|
geometry |
A non-indexed BufferGeometry. It has position and normal, and color when the binary header has COLOR=. Each corner gets the normal of its face. |
solids |
One StlSolid for each solid, in file order: name, start and count. start and count count corners. A binary file has one solid with no name. These are the groups of three.js. |
alpha |
The alpha of COLOR=, from zero to one. It is one when the file has no header color. |
has_colors() is true when the geometry has a color attribute. take_geometry() swaps the geometry out for an empty one.
The binary format has no standard color. This loader reads the convention that three.js reads:
- A header with
COLOR=and four bytes gives a default color and an alpha. The lastCOLOR=in the header wins. - Each face then has a 16-bit color. Red is in bits 0 to 4, green in bits 5 to 9, and blue in bits 10 to 14.
- A face with bit 15 set uses the default color.
The colors are sRGB. The loader decodes them to linear light, as three.js does. A header without COLOR= gives no color attribute. Most writers put zeros in the 16 bits, and zero is black.
The loader raises for:
- A binary file shorter than 84 bytes, or shorter than its face count needs. The loader reads a binary file with bytes after its faces.
- A coordinate or a normal that is not a number, or is not finite as a
Float32. - An ASCII file with no solid, or a solid without
endsolid. A solid in a solid. - A keyword in the wrong place, such as
vertexoutside a facet. - A facet without
normal, or with other than three vertices.outerwithoutloop. - A word that STL does not have. Text that is not UTF-8.
loaders/ply.mojo. read_ply(path) reads a PLY file into one indexed BufferGeometry. three.js: PLYLoader.
var shape = assets.geometries.add(read_ply("assets/cube.ply"))| Function | Meaning |
|---|---|
read_ply(path) -> BufferGeometry |
Read a file. |
parse_ply(bytes) -> BufferGeometry |
Read the bytes of one. |
ply_format(name) -> PlyFormat |
The format a format line names. |
ply_scalar(name) -> PlyScalar |
The type a property line names. |
decode_ply_scalar(bytes, at, scalar, format) -> Float64 |
One binary value. |
The loader reads the three formats: ascii, binary_little_endian and binary_big_endian. It reads every scalar type: char, uchar, short, ushort, int, uint, float and double. It also reads the names int8 to float64.
PlyFormat and PlyScalar are types. A bare integer does not compile. PlyScalar.size() and decode_ply_scalar refuse a value that is not valid.
| Element and property | Attribute |
|---|---|
vertex: x, y, z
|
position. A file must have them. |
vertex: nx, ny, nz
|
normal. |
vertex: s, t, or u, v, or texture_u, texture_v, or tx, ty
|
uv. |
vertex: red, green, blue, or r, g, b, or diffuse_red and so on |
color, divided by 255 and decoded from sRGB. |
vertex: alpha or a
|
The fourth channel of color, divided by 255. three.js does not read it. |
face: vertex_indices or vertex_index
|
The index. |
A face of more than three corners becomes a fan of triangles from its first corner. Thus the face must be convex, as an OBJ face must be. three.js cuts a quad on the other diagonal, and it does not read a face of five or more corners.
The loader reads past all other elements and properties. A file without a face element is a point cloud, and its geometry has no index.
The loader raises, and names the element and the row, for:
- A file that does not start with
ply, or has noend_header. A header with noformatline. - A format or a type that is not known. An
elementorpropertyline with the wrong fields. - A property before any element. An element named twice. A list length that is not an integer type.
- A header line that is not
format,element,property,commentorobj_info. - No
vertexelement, or a vertex withoutx,yandz. A vertex property that is a list. - Only some channels of a normal, a texture coordinate or a color. An alpha without a color.
- A face without a list of integer vertex indices.
- An ASCII row with too few or too many values. A value that is not a number of its type, or is out of its range.
- A file that ends before its last row. A list with a negative length.
- A value of a vertex that is not finite as a
Float32. - A face with fewer than three corners, or one that names a vertex the file does not have. A face that is not convex, or has no area.
assets/cube.ply is a unit cube in ASCII, with a color at each corner and six quads. assets/tetrahedron.stl is a binary tetrahedron with a header color. tests/test_ply.mojo and tests/test_stl.mojo read them and draw them.
loaders/gltf.mojo. read_gltf(path, scene, assets) reads a glTF 2.0 file into the scene and the assets it is handed. three.js: GLTFLoader.
var model = read_gltf("assets/gltf/box.glb", scene, assets)
print(model.node_names[0], model.mesh_count)
var camera_node = model.nodes[2]A .glb is told by its magic. Anything else is read as JSON. A buffer or an image named by a relative URI is read from the file's own directory. A data: URI must be base64.
| Function | Meaning |
|---|---|
read_gltf(path, scene, assets) -> GltfModel |
Read a file. |
load_gltf(text, bin, directory, scene, assets) -> GltfModel |
Read the JSON of one, with a .glb's binary chunk or none. |
split_glb(bytes) -> (String, List[UInt8]) |
A .glb's JSON and its binary chunk. |
decode_base64(text) -> List[UInt8] |
The bytes of a base64 text. |
decode_image(bytes) -> DecodedImage |
A PNG or a JPEG, told by its first bytes. |
The model says what went where, by the file's own indices.
| Field | Meaning |
|---|---|
nodes |
One NodeId per glTF node. NO_PARENT for a node the loaded scene does not reach. |
node_names |
Each node's name, or empty. |
geometries |
One GeometryId per primitive, mesh by mesh. mesh_geometries(mesh) returns one mesh's. |
materials |
One MaterialId per glTF material. |
color_textures, data_textures
|
One TextureId per glTF texture, as read for a color map and as read for a data map. NO_TEXTURE when no material read it that way. |
first_mesh, mesh_count
|
Where the meshes this file added begin in scene.meshes, and how many. |
| glTF | ThreeMojo |
|---|---|
| A primitive of triangles | A BufferGeometry with position, and normal, uv and color when present, indexed when it is. |
| A material | A standard_material: base color and alpha, base color texture, metallic and roughness factors, the metallic-roughness texture as both roughness_map and metalness_map, normal texture and scale, emissive factor and texture, doubleSided. BLEND sets transparent. MASK sets alpha_test to alphaCutoff. |
| A texture | A Texture at its sampler's wrap and filters. A base color or emissive map is read as sRGB, a metallic-roughness or normal map as linear. One glTF texture read both ways is two textures. |
| A node | An Object3D at its translation, rotation and scale, or at its matrix decomposed. Each primitive of its mesh is a Mesh. |
| The default scene | Its roots and everything under them, each node after its parent. scene picks it. |
A primitive without a material draws with one default standard_material. A primitive with COLOR_0 draws with a copy of its material that has vertex_colors on, one copy per material. An accessor without a buffer view reads as zeros. A normalized integer accessor divides by its largest value, as the specification has it.
glTF's texture coordinates run down from an image's top left. This renderer's v runs up from the bottom. Each glTF texture is given a repeat of (1, -1) and an offset of (0, 1), which flips v, as three.js sets flipY = false. The geometry's coordinates are kept as the file has them.
Skins, animations, cameras, morph targets and sparse accessors are not read. No extension is read. A file whose extensionsRequired names one is refused. A file that only lists an extension under extensionsUsed is read without it. A primitive of points, lines or strips is refused. Only the first set of texture coordinates is read.
The loader raises for:
- A file it cannot read. A document that is not JSON or not glTF 2. A required extension.
- A buffer shorter than its length. A buffer view or accessor that runs past its buffer.
- An unknown accessor type or component type. An attribute of the wrong width. Indices that are not unsigned integers.
- An image that is neither PNG nor JPEG. A texture or material that names something the file does not have.
- An unknown wrap mode or alpha mode.
- A node reached twice. A node matrix that flattens an axis.
loaders/json.mojo. parse_json(text) reads a JSON text into a JsonDocument: one JsonNode per value, the root at node zero. The glTF loader reads with it. It is exactly RFC 8259. No comments, no trailing commas, no bare words.
| Member | Meaning |
|---|---|
kind(node) -> JsonKind |
OBJECT, ARRAY, STRING, NUMBER, BOOLEAN or NULL. |
length(node) |
How many children an object or an array has. |
get(node, key), has(node, key)
|
An object's child by key, or NO_NODE. The last of two equal keys wins. |
at(node, index), key(node, index)
|
A child, or an object's key, by position. |
number(node), integer(node), string(node), boolean(node), is_null(node)
|
The leaf values. Each raises for a node of another kind. integer refuses a fraction. |
A text that bends the grammar is refused with the byte it went wrong at. Nesting past MAX_DEPTH is refused. JsonKind is a type. A bare integer does not compile.
Tutorials
How-to
Reference
- Scene graph
- Rotations
- Cameras
- Geometry
- Geometry addons
- Lofts and clipping groups
- Sculptor
- Procedural generators
- Meshes and assets
- Lines
- Points and sprites
- Helpers
- Scene objects
- Animated objects
- Raycasting
- Curves and paths
- Animation
- Skinning
- Materials
- Node materials
- TSL functions
- Lights
- Lighting addons
- Fog
- Textures
- Renderer
- Renderer hooks and material flags
- Rasterization
- GPU backend
- Render target
- Post-processing
- GPU computation
- Compute nodes
- Progressive light map
- Gaussian splats
- Voxel global illumination
- SVG renderer
- Image files
- Image decode queue
- Model files
- More model files
- Exporters
- Scene JSON
- Windowing and controls
- Math
- Norm consumers
- Math addons
- Units
- Coverage tool
- Commands
- Examples
- Benchmarks
- Extensions
- Shared physics
- Physics query snapshots
- Continuous collision
- Tick force restoration
- Water
- Streamline
- Animals
- Buildings
- CARLA
- Femur
- Tibia
- Fibula
- Patella
- Knee
- Muscles
- Vessels
- Lymph
- Nerves
- Integument
- Leg
- Foot
- Pelvis
- Torso
- Arm
- Hand
- Head
- Genome
- Game humanoid
- Humanoid fidelity
- Audio-aligned game faces
- Converted assets
- Mesh quality
- Segment inertia
Explanation
- Software rasterizer
- Scene graph as an array
- Linear color
- Normals are not colors
- Perspective correction
- Fixed-point coverage
- Per-fragment shading
- Mipmaps
- Sorted transparency
- Types and checks
- Render hooks as a trait
- Shared CPU and GPU code
- Bands and threads
- PNG structure
- Extensions beside the port
- CUDA teardown hang
- Mojo compiler hang