Repository navigation
Model files
loaders/obj.mojo, loaders/mtl.mojo, loaders/stl.mojo, loaders/ply.mojo, loaders/gltf.mojo, loaders/collada.mojo, loaders/fbx.mojo and loaders/font.mojo. read_obj reads a Wavefront OBJ file into named objects, each with a BufferGeometry. parse_obj reads the text of one.
read_obj_with_materials also reads the OBJ file's material libraries and gives each object a MaterialId. 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. read_collada and read_fbx read a Collada or an FBX file into a scene and its assets. read_font reads a typeface.js font, which lays text out as shapes.

three.js: OBJLoader, MTLLoader, STLLoader, PLYLoader, GLTFLoader, ColladaLoader, FBXLoader and FontLoader.
To write these files, see Exporters. To read a scene in three.js JSON, see Scene JSON.
The glTF and FBX byte readers require valid UTF-8 text. This includes the JSON chunk in a GLB file and binary FBX names and string properties. Malformed text raises an error before it becomes a string. Valid Unicode names are preserved. Binary payloads keep their own format rules.
The Gaussian splat glTF readers and PLY header reader use the same checked
UTF-8 conversion. Draco metadata names, VTK detection headers and UltraHDR
XMP also reject malformed encoding. UsdzFiles.text refuses bytes that are
not valid UTF-8. This does not change a format that explicitly uses lossy
decoding elsewhere.
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. |
OBJ polygon convexity is checked in translated, widened coordinates so small or large finite model scales do not change whether a face is accepted.
ObjModel.objects holds one ObjObject per object, in file order. count() says how many. ObjModel.material_libraries holds the file name of each mtllib line, in file order.
| 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 first name in materials. |
materials |
The names from usemtl, one for each run of faces. It holds one entry, which is empty when no usemtl comes before the faces. See Material libraries. |
geometry |
A non-indexed BufferGeometry with position, and with normal and uv when the faces name them. It has one group for each run when materials has more than one entry. Group i wears entry i. |
is_multi_material() is true when the object uses more than one material.
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 run of faces under that material, in the same object. A run with no faces is dropped. The first usemtl also takes the faces before it, as in three.js. |
mtllib name |
A material library. The rest of the line is one file 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. 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.
- An
mtllibline with no file name.
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/mtl.mojo. read_mtl(path, assets) reads a Wavefront .mtl file into an MtlLibrary. It adds one PHONG material for each newmtl to assets.materials, and each image it names to assets.textures. three.js: MTLLoader and OBJLoader.setMaterials.
var assets = Assets()
var read = read_obj_with_materials("assets/cube.obj", assets)
var shape = assets.geometries.add(read.model.objects[0].take_geometry())
scene.add_mesh(obj_mesh(read.materials[0], shape, node))| Function | Meaning |
|---|---|
read_mtl(path, assets, options) -> MtlLibrary |
Read a file. Texture file names are relative to its directory. |
parse_mtl(text, directory, assets, options) -> MtlLibrary |
Read the text of one. directory ends in /, or is empty. |
set_materials(model, library, assets) -> List[List[MaterialId]] |
One list for each object of an ObjModel, in its order, with one MaterialId for each entry of the object's materials. |
obj_mesh(materials, geometry, node) -> Mesh |
The mesh three.js builds for an object: one material, or a list when there are more. |
read_obj_with_materials(path, assets, options) -> ObjWithMaterials |
Read an OBJ file and each library its mtllib lines name. |
ObjWithMaterials has model, the ObjModel, library, the combined MtlLibrary, and materials, one list of MaterialIds for each object. The loader reads each library relative to the OBJ file. A material in a later library replaces a material of the same name in an earlier one.
MtlOptions holds the options of three.js's MTLLoader.setMaterialOptions. Each option is a keyword. The defaults read a file as three.js reads it with no options.
| Field | three.js | Default | Meaning |
|---|---|---|---|
side |
side |
FRONT_SIDE |
The side of each material, a default material too. |
wrap |
wrap |
REPEAT |
The wrap of each texture. A -clamp on still clamps. |
normalize_rgb |
normalizeRGB |
False |
Read Kd and Ks from 0 to 255, and divide them by 255. |
ignore_zero_rgbs |
ignoreZeroRGBs |
False |
Skip a Kd or Ks of three zeros. The default color stays. |
invert_tr_property |
invertTrProperty |
False |
Read Tr as the opacity. |
Neither normalize_rgb nor ignore_zero_rgbs changes Ke, as in three.js. The loader refuses a side or a wrap that is none of the named values.
var library = read_mtl(
"assets/cube.mtl", assets, MtlOptions(side=DOUBLE_SIDE, wrap=MIRROR)
)| Member | Meaning |
|---|---|
names, materials
|
The name and the MaterialId of each material, in the order the names first appear. |
count() |
How many materials the library has. |
find(name) -> Int |
The index of a name, or -1. |
get(name) -> MaterialId |
The material of a name. It raises for a name the library does not have. |
add(name, material) |
Add a material. It replaces a material of the same name. |
merge(other) |
Add each material of another library. |
create(name, assets) -> MaterialId |
The material of a name. For a name the library does not have, it adds a default PHONG material once and keeps it. This is three.js's MaterialCreator.create. |
An OBJ object with no usemtl, or with a name that no library has, gets a default material. Each such name gets its own default, as in three.js.
A keyword can be in any case. Each key keeps the last value that a material gives it. The loader applies the keys in the order they first appear, as three.js does.
| Key | Meaning |
|---|---|
newmtl name |
A new material. A second newmtl of the same name replaces the first. |
Kd r g b |
The color. Three numbers from zero to one, in sRGB. |
Ks r g b |
The specular color. |
Ke r g b |
The emissive color. |
Ns n |
The shininess. It must not be negative. |
d n |
The opacity, from zero to one. Below one, it sets opacity and transparent. |
Tr n |
The transparency, from zero to one. Above zero, it sets opacity to 1 - n and sets transparent. |
illum n |
The illumination model, a whole number from 0 to 10. The loader checks it and ignores it, as three.js does. |
map_Kd file |
The color map, in sRGB. |
map_Ke file |
The emissive map, in sRGB. |
map_d file |
The alpha map, as data. It also sets transparent. |
map_bump file, bump file
|
The bump map, as data. The first of the two is kept. |
norm file |
The normal map, as data. |
map_Ks file |
The specular map, as data. |
disp file |
The displacement map, as data. |
# |
A comment, to the end of the line. |
The defaults are those of three.js's MeshPhongMaterial: a white color, a specular of 0x111111, a shininess of 30, no emissive color, and opaque. Lines before the first newmtl are skipped. A key with no value is skipped. Ka, map_Ka and unknown keys are skipped, as three.js skips them.
A material that has a normal map and a bump map keeps the normal map. three.js ignores the bump map in that case, and Material refuses both.
A texture line has options, then a file name. The file name can contain spaces.
| Option | Meaning |
|---|---|
-s u v w |
The texture's repeat. w is ignored. When v is missing, it is one. |
-o u v w |
The texture's offset. w is ignored. When v is missing, it is zero. |
-bm n |
The material's bump_scale, on any texture line, as three.js reads it. |
-mm base gain |
The material's displacement bias and scale, in meters, on any texture line, as three.js reads them. They move nothing without a disp map. |
-clamp on, -clamp off
|
CLAMP, or the wrap of the options. three.js always uses the wrap of the options. |
A texture repeats by default, as in three.js. The loader decodes PNG, JPEG and TGA images, and tells them apart by their first bytes. A color map is SRGB, and its alpha is coverage. An emissive map is SRGB, and its alpha is IGNORED. An alpha map, a bump map, a normal map, a specular map and a displacement map are LINEAR, and their alpha is IGNORED. One image read the same way twice gives one texture.
- A displacement bias and scale without a
dispmap are dropped. three.js keeps them, and they move nothing there either. -
-clamp onclamps a texture. three.js ignores-clampand uses thewrapoption. - An unknown texture option is refused. three.js reads it as part of the file name, and then cannot load the file.
- A color above one is refused.
Colorholds eight bits for each channel.
The loader raises, and names the line, for:
- A
newmtlline with no name. - A color that is not three numbers from zero to one, or from 0 to 255 with
normalize_rgb. - A
sideor awrapin the options that is none of the named values. - An
Nsthat is not a number, is not finite, or is negative. - A
dorTroutside zero to one. - An
illumthat is not a whole number from 0 to 10. - A texture option that is not known, or does not have its numbers. A
-clampwithoutonoroff. - A texture line with no file name.
- An image that the loader cannot read or decode.
read_mtl and read_obj_with_materials also raise for a file that they cannot read.
assets/cube.mtl is the Brick material that assets/cube.obj names, with the texture assets/brick.png. assets/mtl/ holds an OBJ file with two libraries. tests/test_mtl.mojo reads them and draws the cube.
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. |
ASCII input can begin with one UTF-8 byte-order mark. Both byte and text parsers ignore that leading marker; embedded markers remain data.
A file exactly as long as its face count says is binary. Otherwise, a file with solid starting at byte offset zero through four 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. |
An ASCII file's geometry has one group for each solid. Solid i wears material i, as three.js's parseASCII adds them. A binary file's geometry has no group.
| 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, options) -> BufferGeometry |
Read a file. |
parse_ply(bytes, options) -> 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, or px, py, pz, or posx, posy, posz
|
position. A file must have them. |
vertex: nx, ny, nz, or normalx, normaly, normalz
|
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.
PlyOptions holds three.js's setPropertyNameMapping and setCustomPropertyNameMapping.
| Method | three.js | Meaning |
|---|---|---|
set_property_name(name, read_as) |
setPropertyNameMapping |
Read a property of the file as another name, in every element. |
set_custom_attribute(attribute, properties) |
setCustomPropertyNameMapping |
Read vertex properties into an attribute of their own. The item size is the count of properties. |
var options = PlyOptions()
options.set_property_name("diffuse_red", "red")
options.set_custom_attribute("quality", ["q1", "q2"])
var shape = read_ply("assets/scan.ply", options)A custom attribute names properties after the renames. It is read after the named attributes, so a custom position replaces the positions, as in three.js. An empty list of properties reads nothing, as in three.js. A property that the vertex element does not have is refused. three.js reads NaN for it.
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. - A custom attribute that names a property the
vertexelement does not have, or 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. It reads meshes, points, lines, materials, textures, nodes, skins, morph targets, animations, cameras, sparse accessors and twenty-one extensions. 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]Pass workers to read_gltf or load_gltf to decode core material maps
with the bounded image queue. One worker keeps lazy
serial decoding. Extension-only maps remain lazy.
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.
Accessor offsets and strides must stay inside their buffer views and meet alignment rules. Matrix columns include their required padding. The final column can omit trailing padding. Integer attributes, indices, and sparse indices keep their exact values; they do not pass through Float32 storage.
| 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. Other bytes are read as a TGA, which has no signature. |
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. |
first_skinned_mesh, skinned_mesh_count
|
Where the skinned meshes this file added begin in scene.skinned_meshes, and how many. |
first_instanced_mesh, instanced_mesh_count
|
Where the instanced meshes this file added begin in scene.instanced_meshes, and how many. |
first_line, line_count
|
Where the lines this file added begin in scene.lines, and how many. |
first_points, points_count
|
Where the points this file added begin in scene.points, and how many. |
first_light, light_count
|
Where the lights this file added begin in scene.lights, and how many. |
cameras |
One GltfCamera per node that carries a camera and that the loaded scene reaches. |
animations |
One AnimationClip per animation that drives something the loaded scene reaches, in file order. |
mesh_extras |
One UserData per glTF mesh: its extras, three.js's mesh.userData. |
material_extras |
One UserData per glTF material: its extras, three.js's material.userData. |
scene_extras |
The extras of the loaded scene, three.js's scene.userData. |
The loader reads extras into user data, as three.js's assignExtrasToUserData does. User data is a UserData from core/user_data.mojo, the same map that Scene JSON reads.
- A node's
extrasgo into itsuser_data. A named node also getsnamein itsuser_data, as three.js setsuserData.name. - In three.js, the node of a mesh with one primitive is the mesh itself. Thus such a node takes the mesh's
extrasfirst, thenname, then its ownextras. A node with a camera or a light does not, and a joint does not. three.js makes a group or a bone of it. - A primitive's
extrasgo into its geometry'suser_data, as three.js reads them intogeometry.userData. - A
Mesh, aMaterialand aScenehere hold no user data.mesh_extras,material_extrasandscene_extrashold theirextras. -
extrasthat are not an object are skipped, as three.js skips them.
To write the user data back, give it to GltfExportOptions. See Exporters.
| glTF | ThreeMojo |
|---|---|
| A primitive of triangles | A BufferGeometry with position, and normal, uv, uv1 and color when present, indexed when it is. TEXCOORD_1 becomes uv1. |
| A primitive of points or lines | A Points or a Line. See Points and lines. |
| A material | A STANDARD material, or a BASIC or PHYSICAL one when an extension asks. It reads 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, the occlusion texture, 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, normal or occlusion map is read as linear, with its alpha ignored. One glTF texture read both ways is two textures. |
| A node | An Object3D at its translation, rotation and scale, or at its matrix decomposed, with the node's name. Each primitive of its mesh is a Mesh. |
A node with a skin
|
Each primitive of its mesh is a SkinnedMesh. See Skins, morph targets and animations. |
A node with a camera
|
A PerspectiveCamera or an OrthographicCamera attached to the node. See Cameras. |
| A sparse accessor | Its values replace the elements that its indices name. The other elements come from the buffer view, or are zero when there is none. |
| 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. Each glTF texture has flip_y off, as three.js sets flipY = false. So v reads down from the first row, and the geometry's coordinates and any KHR_texture_transform are kept as the file has them. The writer writes a texture with flip_y upside down, as three.js's GLTFExporter writes a flipY texture, and one without it as it is.
A texture takes its sampler's wrapS, wrapT, magFilter and minFilter, as three.js's GLTFLoader reads them. A mipmap minFilter builds the chain. The reader refuses a mipmap magFilter and a filter that is none of glTF's six. The writer writes the four, and writes a mipmap minFilter on a texture without a chain as the filter it reads inside a level.
A primitive of points or lines draws as three.js's GLTFLoader.loadMesh draws it.
mode |
ThreeMojo |
|---|---|
POINTS, 0 |
Points. |
LINES, 1 |
Line with SEGMENTS. |
LINE_LOOP, 2 |
Line with LOOP. |
LINE_STRIP, 3 |
Line with STRIP. |
TRIANGLES, 4 |
Mesh. This is the default. |
A point or a line gets a BASIC material of its own, as three.js makes a PointsMaterial or a LineBasicMaterial. It takes the color, the opacity and the transparency of the file's material. It has vertex_colors on when the primitive has COLOR_0. Points also take the base color texture and the alpha test, at a size of one pixel that does not shrink with distance. A line takes no texture, because a line here has none.
An index here holds triangles. Thus the loader copies the vertices of an indexed point or line primitive out in index order, as three.js's toNonIndexed does. The morph targets are copied with them.
A point or a line on a skinned node or an instanced node is refused. A skinned or instanced line is not ported.
A material's occlusionTexture becomes its ao_map. Its strength becomes ao_map_intensity, or one when it is not there. This is what three.js reads into aoMap and aoMapIntensity. The red channel of the map dims the indirect light. See Materials.
Every map can read the second set of texture coordinates, the occlusion texture among them. A texCoord of one gives a copy of the texture the channel UV_CHANNEL_1. The renderer then samples it at the geometry's uv1. A texCoord in the map's KHR_texture_transform replaces the map's own.
An unlit material reads no occlusion, as in three.js.
The loader reads a skin into a Skeleton, a morph target into the geometry, and an animation into an AnimationClip. The names are three.js's, as in Skinning and Animation.
| glTF | ThreeMojo |
|---|---|
JOINTS_0 |
The skinIndex attribute. The components must be unsigned bytes or unsigned shorts. |
WEIGHTS_0 |
The skinWeight attribute. The loader divides each vertex's four weights by their sum, as three.js's normalizeSkinWeights does. Four zeros become one on the first bone. |
| A skin | A Skeleton with one Bone per joint. The inverse bind matrices come from inverseBindMatrices, or are the identity. The mesh is bound at the identity, in ATTACHED mode. |
A primitive's targets
|
Morph targets of the geometry, with morph_relative set, because glTF holds offsets. POSITION, NORMAL and COLOR_0 are read. A geometry can carry any number of targets. |
A mesh's extras.targetNames
|
The names of the targets. Each mesh gets a morph_target_dictionary from them. |
weights |
The morph influences of each mesh. A node's weights replace its mesh's weights. |
extras.targetNames |
The geometry's morph_names, one per target, as three.js reads them into morphTargetDictionary. extras of another form names nothing. |
| An animation | An AnimationClip, named by the file or animation_ and its index. |
A translation, rotation or scale channel |
A POSITION, QUATERNION or SCALE track on the node. |
A weights channel |
One morph influence track for each morph target of each mesh, plain or skinned, on the node and below it. |
STEP, LINEAR
|
The track's STEP or LINEAR interpolation. |
CUBICSPLINE |
The track's CUBIC_SPLINE interpolation. Each key's in-tangent, value and out-tangent go to in_tangents, values and out_tangents. |
A skinned mesh waits until the loader has placed every node. Thus a joint can come after the mesh in the file. Each primitive gets its own copy of the skeleton, because a SkinnedMesh owns its skeleton.
A node with a camera gets a camera attached to it. The camera looks down the node's -z axis, with its +y axis up. GltfCamera holds the camera.
| Member | Meaning |
|---|---|
kind |
GLTF_PERSPECTIVE or GLTF_ORTHOGRAPHIC, a GltfCameraKind. A bare integer does not compile. |
index, name
|
The camera's index in the file's cameras, and its name. |
node |
The scene node that the camera rides. |
perspective() |
The PerspectiveCamera. It raises for an orthographic camera, or a kind that is not valid. |
orthographic() |
The OrthographicCamera. It raises for a perspective camera, or a kind that is not valid. |
A perspective camera takes yfov in radians and znear. Without aspectRatio, the aspect is one. Without zfar, the far plane is at two million meters. These are three.js's values. An orthographic camera spans xmag and ymag on each side of its axis. One glTF camera on two nodes is two cameras.
- A morph target without
POSITIONmoves no position. three.js adds the base positions to it as offsets. - A channel on a node that the default scene does not reach is left out. An animation left with no track is left out.
- A skin joint that the scene does not reach is refused. three.js puts a new bone in its place.
- A morph target without
COLOR_0changes no color. three.js adds the base colors to it as offsets. - A rotation key must be of unit length. A track refuses one that is not.
- A map other than the occlusion texture must read the first set of texture coordinates. three.js reads any set. Here, only the ao map and the light map are sampled at a second set.
- A map that reads the third set of texture coordinates or a later set is refused. A geometry here has only
uvanduv1.
The loader reads twenty-one extensions. Twenty are the ones three.js's GLTFLoader reads that map onto a feature of this renderer. KHR_materials_variants is read as the plugin that three.js names for it reads it. is_supported_extension(name) tells if the loader reads an extension.
| Extension | ThreeMojo |
|---|---|
KHR_materials_unlit |
A BASIC material: the base color, its texture, doubleSided and the alpha mode. The loader ignores the emissive, normal and metallic-roughness terms and every other material extension, as three.js does. |
KHR_materials_emissive_strength |
emissiveStrength sets emissive_intensity. |
KHR_materials_ior |
A PHYSICAL material. ior sets ior, or 1.5 when it is not there. |
KHR_materials_specular |
A PHYSICAL material. specularFactor sets specular_intensity. specularColorFactor sets specular_color, converted from linear to sRGB. specularTexture sets specular_intensity_map, linear with its alpha kept. specularColorTexture sets specular_color_map, sRGB with its alpha ignored. |
KHR_materials_clearcoat |
A PHYSICAL material. clearcoatFactor and clearcoatRoughnessFactor set clearcoat and clearcoat_roughness. clearcoatTexture, clearcoatRoughnessTexture and clearcoatNormalTexture set the three coat maps, as data. The normal texture's scale sets clearcoat_normal_scale on both axes, as three.js sets it. |
KHR_materials_transmission |
A PHYSICAL material. transmissionFactor sets transmission, and transmissionTexture sets transmission_map, read as data. See Materials. |
KHR_materials_volume |
A PHYSICAL material. thicknessFactor sets thickness, and thicknessTexture sets thickness_map, read as data. attenuationDistance sets attenuation_distance, and a distance of zero or none is infinite, as three.js reads it. attenuationColor sets attenuation_color, converted from linear to sRGB. |
KHR_materials_dispersion |
A PHYSICAL material. dispersion sets dispersion. |
KHR_materials_sheen |
A PHYSICAL material with a sheen of one, as three.js sets it. sheenColorFactor sets sheen_color, converted from linear to sRGB, and sheenRoughnessFactor sets sheen_roughness. Both are zero when they are not there. sheenColorTexture and sheenRoughnessTexture set the two maps. The roughness texture keeps its alpha, because the roughness is stored there. See Materials. |
KHR_materials_iridescence |
A PHYSICAL material. iridescenceFactor, iridescenceIor, iridescenceThicknessMinimum and iridescenceThicknessMaximum set the film, in nanometers. iridescenceTexture and iridescenceThicknessTexture set its maps. See Materials. |
KHR_materials_anisotropy |
A PHYSICAL material. anisotropyStrength and anisotropyRotation set anisotropy and anisotropy_rotation, and anisotropyTexture sets its map. See Materials. |
KHR_texture_transform |
A copy of the texture with its offset, rotation and repeat set. See Texture transforms. |
KHR_lights_punctual |
A directional, point or spot Light on the node. See Punctual lights. |
KHR_mesh_quantization |
Nothing more. The loader reads every attribute at any component type, and a normalized one divides by its largest value. |
EXT_mesh_gpu_instancing |
One InstancedMesh for each primitive of the node's mesh. See Instancing. |
KHR_draco_mesh_compression |
The primitive's Draco data, decoded by loaders/draco.mojo. See Draco primitives. |
EXT_materials_bump |
A PHYSICAL material. bumpTexture sets bump_map, read as data, and bumpFactor sets bump_scale. See Bump maps. |
KHR_texture_basisu |
The texture reads the KTX 2.0 image that the extension names, before its own source. See KTX 2.0 textures. |
EXT_meshopt_compression |
The buffer view's compressed bytes, decoded by loaders/meshopt.mojo. See Compressed buffer views. |
EXT_texture_webp |
The texture reads the WebP image that the extension names, before its own source. See WebP images. |
KHR_materials_variants |
The variant names, and the material that each variant gives each mapped object. select_variant changes the materials. See Material variants. |
A file that lists another extension in extensionsRequired is refused, as three.js refuses it. A file that lists an extension only in extensionsUsed is read without that extension.
The transform is applied first, then the flip of v. So the copy gets an offset of (x, 1 - y), a repeat of (x, -y), and the rotation as the file gives it. The glTF coordinates then go where three.js's matrix puts them. A transform that names only texCoord makes no copy.
The transform's texCoord replaces the texture's own texCoord, as in three.js. It must be zero or one. Each map keeps its own transform and its own set, as in three.js.
| glTF | ThreeMojo |
|---|---|
directional |
directional_light. Its target is a new node one meter down the node's -z axis, as three.js adds its target. |
point |
point_light with distance set to range, or zero for no cutoff. |
spot |
spot_light with angle set to outerConeAngle and penumbra set to 1 - innerConeAngle / outerConeAngle. Its target is as for a directional light. |
The color is linear in the file and sRGB in the Light. The intensity is one when the file gives none. Point and spot lights use a decay of two, as three.js does.
Each instance matrix is TRANSLATION, ROTATION and SCALE composed. An attribute that is not there is the identity's part. _COLOR_0 colors the instances, as three.js reads it into instanceColor. The loader counts every other attribute but does not read it, because it is for a custom shader. A node with an empty attributes object draws plain meshes, as in three.js.
The loader decodes the buffer view that the extension names, as three.js's DRACOLoader decodes it. See Draco.
- Each attribute that the extension names comes from the Draco data. The extension gives its Draco unique id.
- The accessor gives the component type and
normalized. A float accessor reads the values as floats. An integer accessor reads them at its type, then the loader divides a normalized one by its largest value, as for any accessor. - The Draco attribute gives the number of components of each element.
- The triangles of a Draco mesh replace the accessor's
indices. A Draco point cloud keeps them. - An attribute that the extension does not name comes from its accessor.
The loader refuses an id that is not in the Draco data, and a value that does not fit its accessor's type. It also refuses a Draco float attribute that an integer accessor reads. Draco rounds that float. That file is not valid glTF.
EXT_materials_bump makes a PHYSICAL material, as three.js makes one. A material here holds a normal map or a bump map, not both. three.js draws the normal map of a material with both and ignores the bump map. Thus the loader drops the bump map of such a material. A bumpFactor without a bumpTexture draws nothing, and the loader keeps a bump_scale of one.
KHR_texture_basisu names a KTX 2.0 image, which render/ktx2.mojo decodes. A texture with the extension reads that image, and the texture's own source is not needed. The loader reads the first level of the first face.
| Image | Texture |
|---|---|
| UASTC, ETC1S or another format of bytes | A byte texture in the color space that the map needs: sRGB for a color map, linear for data. The loader builds the mip chain when the sampler reads one. |
| UASTC HDR, or a half or float format | A float texture, linear, whatever the map needs. |
The loader tells a KTX 2.0 image by its identifier, is_ktx2(bytes). ktx2_texture(bytes, sampling, space, alpha) builds the texture.
EXT_meshopt_compression stores a buffer view in a smaller form. The loader decodes each such view when it reads the buffers, as three.js does with its MeshoptDecoder set. Accessors, images and animations then read the decoded bytes.
| Mode | What it holds |
|---|---|
ATTRIBUTES |
Elements of any size, as byte deltas from the element before. A filter can follow it. |
TRIANGLES |
A triangle list, as one code for each triangle, from a cache of recent edges and vertices. |
INDICES |
Any list of indices, as deltas from one of two running values. |
| Filter | What it makes |
|---|---|
OCTAHEDRAL |
Unit vectors, from two octahedral coordinates. |
QUATERNION |
Unit quaternions, from their three smallest components. |
EXPONENTIAL |
Floats, from an 8-bit exponent and a 24-bit mantissa. |
The decoder is a port of meshoptimizer 0.22, which three.js 0.180 ships. Its output is the same bytes as three.js's decoder, filters included. The filters round each product before they add to it, as WebAssembly does.
A buffer that the extension marks as a fallback is not read. A buffer with no URI after the first is not read either. Only a compressed view can name such a buffer.
decode_gltf_buffer(count, stride, source, mode, filter) decodes one view. decode_vertex_buffer, decode_index_buffer and decode_index_sequence decode one mode each.
The loader refuses these:
- A compressed stream that is too short, has the wrong header, or does not end where its tail starts. The message has meshoptimizer's code, as three.js's does.
- A mode or a filter that is not known.
- A view whose
byteLengthis not itscounttimes itsbyteStride, or whosebyteStrideis not the extension's. - A stride, a count or a filter that the specification does not allow for the mode. meshoptimizer does not examine these.
KHR_materials_variants gives a model several sets of materials, for example one for each color of a product. three.js's GLTFLoader does not read it. Its documentation names a plugin that does: GLTFMaterialsVariantsExtension of takahirox/three-gltf-extensions. The loader reads the extension as that plugin reads it.
var model = read_gltf("chair.gltf", scene, assets)
select_variant(model, scene, String("red"))
select_variant(model, scene)-
model.variantsholds the variant names. A name that is already taken gets.1, then.2, until it is unique, as the plugin makes it. -
model.variant_objectsholds each drawn object whose primitive has mappings. Each has its scene list, its index there, its original material, and one material for each variant. - The loader makes every variant's material when it reads the file, as the plugin's
ensureLoadVariantsmakes them. A material is made final as three.js makes it: a copy with vertex colors, or a points or line material. - A later mapping of a variant replaces an earlier one, as in the plugin.
-
select_variant(model, scene, name)gives each mapped object the variant's material. A variant that does not map an object, an unknown name, and no name give the object its original material back. - The loader reads no mappings when the root does not have the extension, as the plugin does.
The plugin selects the variant under one object. select_variant selects it for the whole model. The plugin cannot restore the original material of a primitive that names no material, or of points or lines. select_variant restores them.
The loader refuses a variant with no name, a mapping with no material, and a mapping that names a variant that is not there.
EXT_texture_webp names a WebP image, which render/webp.mojo decodes. See Read a WebP.
- A texture with the extension reads that image, before its own
source. three.js registers its WebP plugin after its KTX 2.0 plugin, soKHR_texture_basisucomes first. - The texture's own
sourceis not needed, and a file can require the extension.
-
EXT_texture_avif. It needs an AV1 decoder, and this port has none. A texture with the extension reads its ownsource.
- A
specularColorFactoroutside zero to one is refused. AColorcannot hold it. three.js keeps it. - An
ioroutside 1 to 2.333 is refused, becauseMaterialrefuses it. - A clear coat texture with a
clearcoatFactorof zero is left out, with the normal texture'sscale. three.js keeps them, but draws none. - An
iridescenceTextureoriridescenceThicknessTexturewith aniridescenceFactorof zero is left out. So is ananisotropyTexturewith ananisotropyStrengthof zero. three.js keeps them, but draws neither. - A
sheenColorFactoroutside zero to one is refused, as aspecularColorFactoris. - A skinned node with
EXT_mesh_gpu_instancingis refused. An instanced skinned mesh is not ported. three.js drops the skin. - A KTX 2.0 byte image takes the color space that the map needs. three.js keeps the image's own color space for a data map.
- A KTX 2.0 image builds its own mip chain from its first level. three.js uploads the chain that the file holds.
A primitive of triangle strips or triangle fans is refused. three.js turns them into triangles. Only the first two sets of texture coordinates are read.
The loader raises for:
- A file it cannot read. A document that is not JSON or not glTF 2. A required extension that the loader does not read.
- A buffer shorter than its length. A buffer view or accessor that runs past its buffer. A buffer view that reads a buffer with no data.
- A compressed buffer view that is malformed. See Compressed buffer views.
- An unknown accessor type or component type. An attribute of the wrong width. Indices that are not unsigned integers.
- An image that is not PNG, JPEG, WebP, TGA or KTX 2.0. A texture or material that names something the file does not have. A texture with no image.
- A primitive mode that is not points, lines or triangles. A point or a line on a skinned or instanced node.
-
targetNamesthat do not hold one name per morph target. - An unknown wrap mode or alpha mode.
- A node reached twice. A node matrix that flattens an axis.
- A sparse accessor with indices that do not rise, that are not unsigned integers, or that name an element past the accessor.
- A skin without joints. A joint that the file does not have, or that the scene does not reach. Inverse bind matrices that are not one
MAT4per joint. - A skinned primitive without
JOINTS_0andWEIGHTS_0. - Primitives of one mesh with different numbers of targets.
weightsthat are not one number per target. - A morph target
COLOR_0that is not aVEC3or aVEC4. Anextras.targetNamesthat is not an array of strings. - A camera of an unknown type, or without the values it needs. A camera that
PerspectiveCameraorOrthographicCamerarefuses. - An animation channel with an unknown path, or a sampler with an unknown interpolation. A sampler output that does not hold one value per key, or three for
CUBICSPLINE. - A track or a clip that
KeyframeTrackorAnimationCliprefuses. - An
extensionsvalue, or an extension, that is not an object. - A texture reference or a texture transform on a third set of coordinates or past it. A
specularColorFactoroutside zero to one. - A node that names a light the file does not have. A light of an unknown type, a
rangethat is not above zero, or a spot light withoutspot. A light thatLightrefuses. - Instancing without an
attributesobject. Instancing attributes of different counts, or of the wrong width. A skinned node that is instanced.
loaders/collada.mojo. read_collada(path, scene, assets) reads a Collada .dae file into the scene and the assets it is handed. It reads the geometries, the materials and their textures, the node hierarchy, the cameras, the lights, the skins and the animations. three.js: ColladaLoader.
from loaders.collada import read_collada
var model = read_collada("assets/room.dae", scene, assets)
print(model.node_names[0], model.mesh_count)
var camera = model.perspective_cameras[0]The file is XML. XML reads it. An image is read from the directory of the file.
| Function | Meaning |
|---|---|
read_collada(path, scene, assets) -> ColladaModel |
Read a file. |
load_collada(text, directory, scene, assets) -> ColladaModel |
Read the text of one. directory ends in /, or is empty. |
up_axis_rotation(axis) -> Quaternion |
The rotation of the root node for an UpAxis. |
The model tells what went where.
| Field | Meaning |
|---|---|
root |
The node of the visual scene. Each root <node> is a child of it. |
up_axis, unit
|
The <up_axis> as an UpAxis, and <unit meter> as a Length. |
nodes, node_names
|
One NodeId and one name for each <node> placed, in the order placed. A JOINT node has its sid as its name, as in three.js. |
materials, material_ids, material_names
|
One MaterialId, id and name for each <material>, in file order. material(id) finds one. |
geometries |
One GeometryId for each primitive built. |
textures |
One TextureId for each map that a material reads. |
perspective_cameras, orthographic_cameras
|
Each camera that an <instance_camera> places. Each camera rides its node. |
first_mesh, mesh_count, first_line, line_count, first_light, light_count
|
Where the meshes, lines and lights of the file start in the scene, and how many there are. |
first_skinned_mesh, skinned_mesh_count
|
Where the skinned meshes of the file start in scene.skinned_meshes, and how many there are. |
animations |
One AnimationClip for each clip that makes a track. See Skins and animations. |
UpAxis is a type: X_UP, Y_UP or Z_UP. A bare integer does not compile. up_axis_rotation refuses a value that is none of the three.
| Collada | ThreeMojo |
|---|---|
<triangles>, <polylist>, <polygons>
|
One BufferGeometry for each kind, with position, and normal, uv and color when the inputs give them. Each primitive of the kind is a group. It is drawn as a Mesh. |
<lines>, <linestrips>
|
A geometry of point pairs, drawn as a Line in SEGMENTS mode. |
phong, blinn
|
A PHONG material, three.js's MeshPhongMaterial. |
lambert |
A LAMBERT material, three.js's MeshLambertMaterial. |
constant |
A BASIC material, three.js's MeshBasicMaterial. |
<node> |
An Object3D. Its <matrix>, <translate>, <rotate> and <scale> steps are multiplied in file order and decomposed. |
<instance_geometry> |
A mesh for each kind of surface primitive and a line for each line primitive, with the materials that <bind_material> binds to their symbols. |
Group i wears material i of the list of symbols that the primitives name. A primitive with no symbol adds a group and no material, as in three.js. A kind with more than one symbol is a mesh that wears a list. See Several materials. A skinned kind with more than one symbol is one SkinnedMesh for each group, because a skinned mesh here wears one material.
| <instance_node> | The node from <library_nodes> or the visual scene, placed again. |
| <instance_controller> of a <skin> | A SkinnedMesh for each primitive of the skin's geometry. See Skins and animations. |
| <animation>, <animation_clip> | An AnimationClip. |
| <instance_camera> | A PerspectiveCamera or an OrthographicCamera that rides the node. |
| <instance_light> | A directional, point, spot or ambient light at the node. |
| <visual_scene> | The root node. A Z_UP file turns it by -90 degrees about x. The root is scaled by <unit meter>. The vertices do not change, as in three.js. |
A quad of a <polylist> or a <polygons> is cut into the triangles (a, b, d) and (b, c, d), as in three.js. A larger polygon is cut into a fan from its first corner. A polygon of fewer than three corners gives no triangle.
A vertex color is sRGB in the file and linear in a geometry, so the loader decodes it. A fourth value is kept as alpha. Only the first two values of a texture coordinate are read.
A material reads the profile_COMMON technique of its effect. The last constant, lambert, blinn or phong element sets the kind.
| Parameter | Meaning |
|---|---|
diffuse |
The color, and a color map in sRGB. |
specular |
The specular color, on a PHONG material only. |
shininess |
The shininess, on a PHONG material only. Zero keeps the default of 30, as in three.js. |
emission |
The emissive color and an emissive map, on a PHONG or LAMBERT material only. |
bump |
A normal map, on a PHONG or LAMBERT material only. |
transparent, transparency
|
The opacity. See below. |
<extra> double_sided
|
DOUBLE_SIDE for 1, FRONT_SIDE for anything else. |
<extra> bump
|
A normal map. It replaces the bump parameter. |
The opacity comes from the opaque mode of <transparent>, its color and the <transparency> factor:
| Mode | Opacity |
|---|---|
A_ONE, the default |
alpha times the factor |
RGB_ZERO |
one minus red times the factor |
A_ZERO |
one minus alpha times the factor |
RGB_ONE |
red times the factor |
A missing <transparency> is 1. A missing <transparent> is a white of alpha 1 in A_ONE. An opacity below 1 sets transparent. A <transparent> with a texture sets transparent and no alpha map, as in three.js.
A <texture> names a sampler2D, the sampler names a surface, and the surface names an <image>. When no sampler has the name, the name is the image, as in three.js. A texture of an image that the file does not have is left out. The <extra><technique> of a texture can set wrapU, repeatU, repeatV, offsetU and offsetV. wrapU sets one wrap for both axes. A texture repeats by default.
A primitive with no material symbol draws with a white PHONG material, or a white BASIC material for lines. A symbol that <bind_material> does not bind draws with a magenta BASIC material, as in three.js. A line draws with a BASIC copy of a lit material. The copy keeps the color, the opacity and transparent.
A perspective camera reads yfov in degrees, aspect_ratio, znear and zfar. A number that is missing takes the three.js default: 50 degrees, an aspect of 1, 0.1 and 2000. An orthographic camera reads xmag and ymag. When one of them is missing, aspect_ratio gives it. The camera spans half of each on each side.
A light reads its color. A point or spot light reads quadratic_attenuation, and its distance is the square root of one over it. A spot light has a cone of 60 degrees, as in three.js, which does not read falloff_angle. A directional or spot light shines toward the world origin.
A node with no child nodes and one object becomes that object in three.js, at the transform of the node. A node with more objects becomes a group, and each object keeps its own transform. Only a directional or spot light shows the difference. When such a light is one object of several, three.js places it one unit up the y axis of the node. This loader does the same.
A skin controller makes a SkinnedMesh. The loader reads the controller after it places every node, so a bone can come after the mesh in the file. loaders/collada.mojo reads the skins, and loaders/collada_animation.mojo reads the animations. three.js: buildSkin, buildSkeleton, buildAnimation and buildAnimationClip.
| Collada | ThreeMojo |
|---|---|
<bind_shape_matrix> |
The bind matrix of the mesh. The identity when the skin has none. |
<joints> JOINT
|
The bone order. Each name matches the sid of a JOINT node. |
<joints> INV_BIND_MATRIX
|
The inverse bind of each bone. |
<vertex_weights> |
skinIndex and skinWeight. Each vertex keeps its four largest weights, largest first. The loader normalizes the weights. |
<skeleton> |
The root of the bones. Each JOINT node under it is a bone. A <skeleton> can also name the visual scene. Then each JOINT node at the top of the scene is a root. |
A channel on a <matrix> step |
A POSITION, a QUATERNION and a SCALE track on the node. |
<animation_clip> |
A clip that plays the animations it names. Its length is end - start. When that is not above zero, the tracks give the length. |
A bone that the controller does not name follows the named bones, with the identity as its inverse bind, as in three.js. A JOINT node with no child nodes and one object is not a bone, because three.js makes it that object.
A channel target is node-id/sid. sid(i)(j) gives entry i + 4j of the matrix, row by row. sid.member and sid give all 16 entries.
Each key is decomposed into a position, a rotation and a scale. When a key does not give an entry, the loader interpolates it from the keys around it. When no key gives an entry, the node's own matrix gives it. Every track is LINEAR.
A file with animations and no clips gives one clip, default, of every animation. An <animation> that holds other animations is a folder, and its own channels are not read, as in three.js.
- Each line primitive is its own geometry and its own
Line. three.js makes one geometry for each kind, with groups and an array of materials. A line here draws one material. - Two surface primitives of one kind that do not agree about an attribute are refused. three.js fills the attribute in part.
-
<polygons>is read. three.js does not read it. - A vertex color is decoded from sRGB in each of its three channels. three.js decodes the wrong three values of each color.
- A texture coordinate keeps its first two values. three.js keeps all the values of the source.
- A
<linestrips>element gives point pairs, so each strip is drawn alone. three.js joins every strip into one line. - An image path must be relative. A path with
:is refused. - A color channel outside zero to one is refused.
- three.js adds a skin to the geometry, so each instance of the geometry is skinned. Here only the instances of the controller are skinned.
- A bone inside an
<instance_node>copy is not a bone. three.js reads it. - A channel on a node that the scene does not reach makes no track. A clip that makes no track is not a clip. three.js makes a clip of no length.
- A controller joint that names no bone, and a joint index outside the joints, are refused. three.js reads a hole.
Kinematics and physics are not read. A <morph> controller is refused, because three.js does not read one. A channel on a <translate>, <rotate> or <scale> step makes no track, as in three.js.
<lookat> and <skew> steps are skipped. <trifans>, <tristrips> and polygons with holes are not read. A second set of texture coordinates is skipped. The specular map and the ambient map are skipped. three.js reads them as specularMap and lightMap. Material has both fields, but this loader does not fill them.
The loader raises for:
- A text that is not XML, or a root that is not
<COLLADA>. - A unit that is not one positive number. An up axis other than
X_UP,Y_UPandZ_UP. - No
<scene>, or a scene that names no visual scene. A reference that is not#id. - A material with no effect, or an effect with no
profile_COMMONtechnique and shading. - A number that is not a number or is not finite as a
Float32. A color with too few numbers. An opacity outside zero to one. Anopaquemode that is not known. - A sampler with no surface, or a surface or an image with no
init_from. An image that cannot be read. - A geometry that the file does not have, or one with no
<mesh>. A source with a stride below one. - A primitive with no inputs, an input with no offset, or an input that names no source. A
<p>or a<vcount>that does not fill whole corners and faces. An index outside its source. A primitive with corners and no positions, or two inputs that give one attribute. - A node that
<instance_node>names and the file does not have. Nodes that instance each other more thanMAX_INSTANCE_DEPTHdeep. - A step with the wrong count of numbers. A transform that flattens an axis.
- A camera or a light that its builder refuses, or a light with no technique.
- An
<instance_controller>that names no controller. A<morph>controller, or a controller with no<skin>. - A skin with no
<joints>or<vertex_weights>, or an input that is missing or names no source. Joint names that are not aName_array. A joint with no inverse bind matrix. A<v>that is too short. A joint or weight index outside its source. A position with no weights. - A
<skeleton>that names no node and no visual scene. A joint that names no bone. A bone that the scene does not reach. - A clip that names no animation. A channel that names no sampler or node, a sampler that names no source, or a target with no
sid. A matrix index outside the matrix. A key that flattens an axis.
loaders/fbx.mojo and loaders/fbx_tree.mojo. read_fbx(path, scene, assets) reads an FBX file into the scene and the assets it is handed. The file can be binary or ASCII. It reads the meshes, the materials and their textures, the model hierarchy, the cameras, the lights, the skins, the blend shapes and the animation stacks. three.js: FBXLoader.
from loaders.fbx import read_fbx
var model = read_fbx("assets/robot.fbx", scene, assets)
var arm = model.model("Arm")
print(model.mesh_count, len(model.materials))| Function | Meaning |
|---|---|
read_fbx(path, scene, assets) -> FbxModel |
Read a file. A texture is read from the directory of the file, or from the file when it is embedded. |
parse_fbx(bytes) -> FbxDocument |
Read the tree of a file. Bytes that start with the binary magic are binary. Other bytes are ASCII. |
parse_fbx_text(text) -> FbxDocument |
Read the tree of an ASCII file. |
load_fbx(document, directory, scene, assets) -> FbxModel |
Read a tree into a scene. |
| Field | Meaning |
|---|---|
root |
The node that each root model hangs on, three.js's sceneGraph group. |
format, version
|
FBX_ASCII or FBX_BINARY, and the version of the file. |
models, model_ids, model_names
|
One NodeId, id and name for each Model, parents first. model(name) finds one. |
materials, material_ids, material_names
|
One MaterialId, id and name for each Material that has a connection, in file order. |
geometries |
One GeometryId for each material index of each geometry. |
textures |
One TextureId for each texture and each use of it. |
cameras |
Each PerspectiveCamera, riding its model. |
unit |
The length of one unit of the file, a Length: UnitScaleFactor from GlobalSettings, in centimeters. Like three.js, the loader keeps it and does not apply it. |
first_mesh, mesh_count, first_light, light_count
|
Where the meshes and lights of the file start in the scene, and how many there are. |
first_skinned_mesh, skinned_mesh_count
|
Where the skinned meshes of the file start in scene.skinned_meshes, and how many there are. |
animations |
One AnimationClip for each animation stack that makes a track. See Skins, blend shapes and animation. |
A name is sanitized as three.js's PropertyBinding.sanitizeNodeName does it. White space becomes _, and [, ], ., : and / are removed.
An FBX file is a tree of nodes. Each node has a name, properties and children. FbxDocument holds each node in one list. Node zero has no name, and its children are the top-level nodes. FbxPropertyKind tells what a property holds: FBX_INTEGER, FBX_NUMBER, FBX_STRING, FBX_INTEGERS, FBX_NUMBERS or FBX_BYTES.
| Member | Meaning |
|---|---|
children(node), children_named(node, name), child(node, name)
|
The children of a node. child gives NO_FBX_NODE when there is none. |
property_count(node), property(node, index)
|
The properties of a node. property refuses a kind that is none of the six. |
integer, number, string
|
One property as a whole number, a number or a string. |
numbers(node), integers(node)
|
The array of a node, or its single numbers. |
The binary form is read at version 6400 and later, and the ASCII form at version 7000 and later, as in three.js. From version 7500, the three sizes of a record are 64 bits. An array can be raw or compressed with zlib. render/inflate.mojo expands a compressed array, as it does for a PNG. An ASCII array, Name: *count { a: ... }, becomes one array property of its node, so the two forms give the same tree.
A binary file writes an object name as Name\x00\x01Class. three.js stops the string at the zero byte, and so does this reader. An ASCII file writes "Class::Name". object_name(name, format) removes the Class::.
| FBX | ThreeMojo |
|---|---|
Model |
An Object3D under its parent model. |
Geometry of type Mesh
|
A BufferGeometry with position, and normal, uv and color when the layer elements give them. It has a group for each run of polygons of one material index, as three.js's genGeometry adds them. A layer mapped AllSame adds no group. |
Material |
A LAMBERT material for the lambert shading model, and a PHONG material for any other, as in three.js. |
Texture and Video
|
A Texture. |
NodeAttribute of a Camera model |
A PerspectiveCamera. |
NodeAttribute of a Light model |
A point, directional or spot light. |
GlobalSettings AmbientColor
|
An ambient light, when the color is not black. |
The transform of a model is three.js's generateTransform. The local chain is: translation, rotation offset, rotation pivot, pre-rotation, rotation, inverse post-rotation, inverse rotation pivot, scaling offset, scaling pivot, scale, inverse scaling pivot. The rotation and scale then combine with the parent in the order that InheritType names. RotationOrder sets the order of the rotation, and fbx_euler_order converts it. The pre-rotation and the post-rotation always turn in ZYX, as in three.js.
The geometric translation, rotation and scale of the first model of a geometry move its positions and normals, as in three.js.
PolygonVertexIndex lists the corners of each polygon. A negative index ends a polygon, and its position is -index - 1. A triangle is kept. A polygon of four or more corners is laid flat on the plane of its Newell normal and cut by ear clipping. A convex polygon is cut into a fan from its first corner. A polygon of fewer than three corners gives no triangle.
FbxLayer holds one layer element: the values, the indices, and how they map onto the corners. The first LayerElementNormal, LayerElementUV, LayerElementColor and LayerElementMaterial with values are read.
| Mapping | FbxMapping |
One value for each |
|---|---|---|
ByPolygonVertex |
BY_POLYGON_VERTEX |
corner |
ByPolygon |
BY_POLYGON |
polygon |
ByVertice, ByVertex
|
BY_VERTEX |
position |
AllSame |
ALL_SAME |
mesh |
A reference of Direct reads the values directly. IndexToDirect, or the older Index, reads them through the indices. FbxMapping and FbxReference are types. FbxLayer.start refuses a value that is none of the named constants.
A vertex color is decoded from sRGB. A material index below zero is zero, as in three.js. A mesh with no material draws with a gray PHONG material, 0xcccccc. When the geometry has colors, each material of the mesh gets vertex_colors, as in three.js.
A mesh with one material wears it on every polygon. A mesh with more is one Mesh that wears them as a list, in the order they connect. A group whose index is past the list draws nothing, as in three.js. A skinned mesh with more is one SkinnedMesh for each group, because a skinned mesh here wears one material. A geometry with no polygons gets no mesh. three.js adds a mesh of nothing.
| Property | Meaning |
|---|---|
Diffuse, or DiffuseColor of type Color or ColorRGB
|
The color. |
Emissive, or EmissiveColor of type Color or ColorRGB
|
The emissive color. |
EmissiveFactor |
The emissive intensity. |
Specular, or SpecularColor of type Color
|
The specular color, on a PHONG material. |
Shininess |
The shininess, on a PHONG material. |
ReflectionFactor |
The reflectivity. |
BumpFactor |
The bump scale, with a bump map only. |
TransparencyFactor, Opacity, TransparentColor
|
The opacity. See below. |
The opacity is one minus TransparencyFactor. When that is exactly 0 or 1, Opacity gives the opacity, or else one minus the red of TransparentColor. This is three.js's rule. An opacity below 1 sets transparent.
A texture connects to a material under the name of its map:
| Connection | Map |
|---|---|
DiffuseColor, Maya|TEX_color_map
|
map, sRGB |
EmissiveColor |
emissive_map, sRGB |
NormalMap, Maya|TEX_normal_map
|
normal_map, linear |
Bump |
bump_map, linear. A normal map replaces it. |
TransparentColor, TransparencyFactor
|
alpha_map, linear. It also sets transparent. |
A texture reads the first Video connected to it. The image is the Content of the video: raw bytes in a binary file, base64 in an ASCII file. When the video has no content, another video of the same name can give it, as in three.js. Otherwise the image is the file that RelativeFilename or Filename names, after its last \.
WrapModeU of 0 repeats, and any other value clamps. Scaling and Translation set repeat and offset. A layered texture gives its first layer. A texture with no video is left out.
A camera reads FieldOfView in degrees, 45 by default, and AspectWidth over AspectHeight. Without an aspect, three.js uses the shape of the window, and this loader uses a square. FocalLength sets the field of view through a 35 mm film gauge, three.js's setFocalLength.
NearPlane and FarPlane are divided by 1000, as in three.js. Without them, the planes are 1 and 1000.
A light reads LightType: 0 is a point light, 1 a directional light and 2 a spot light. Any other type is a point light with three.js's defaults. Color is sRGB. Intensity is divided by 100. CastLightOnObject of 0 sets the intensity to zero.
FarAttenuationEnd is the distance, unless EnableFarAttenuation is 0. The decay is 1. A spot light reads InnerAngle in degrees, 60 by default. OuterAngle sets the penumbra to 1. CastShadows of 1 casts a shadow from any light, a point light too, as in three.js.
A directional or spot light stands one unit up its own y axis before the model transform applies, as in three.js. It shines toward the world origin.
A Skin deformer on a geometry makes each mesh of the geometry a SkinnedMesh. loaders/fbx.mojo reads the skins and blend shapes, and loaders/fbx_animation.mojo reads the animation stacks. three.js: parseDeformers, bindSkeleton and AnimationParser.
| FBX | ThreeMojo |
|---|---|
Cluster of a Skin
|
A bone: the model that connects to the cluster. The inverse of TransformLink is its inverse bind. |
Indexes, Weights of a Cluster
|
skinIndex and skinWeight. A vertex with more than four weights keeps the four largest. The loader normalizes the weights. |
BindPose |
The matrix of the mesh model is the bind matrix. Without one, the bind matrix is the identity. |
BlendShapeChannel of a BlendShape
|
A morph target of the geometry, as offsets, so morph_relative is set. |
AnimationStack |
An AnimationClip, named after the stack. Its first AnimationLayer gives the tracks. |
AnimationCurveNode T, S
|
A POSITION or SCALE track on the model. |
AnimationCurveNode R
|
A QUATERNION track on the model. |
AnimationCurveNode DeformPercent
|
A morph influence track on each mesh of the model. The value is divided by 100. |
A geometry can carry any number of blend shape channels, as in three.js. The influences start at zero, as in three.js, which does not read DeformPercent.
A curve time is in FBX ticks, 46186158000 each second. A key comes at each time that any axis has a key. An axis with no key at that time keeps its last value, and the first value is the model's own.
The angles of an R curve node are in degrees and turn in the RotationOrder of the model. The pre-rotation turns first, and the post-rotation is undone last, in ZYX, as generateTransform does. When an angle changes by 180 degrees or more between two keys, three.js adds keys between them by slerp and drops the second key. This loader does the same. Every track is LINEAR, as in three.js.
The stacks come in the order that three.js meets them. A JavaScript object puts an id from 0 to 2^32 - 2 first, in numeric order. js_key_order gives that order.
- A polygon of four or more corners is cut by ear clipping. three.js uses earcut, which picks other diagonals. A flat polygon gives the same surface.
- The ASCII reader reads tokens, not lines and tabs. A file that three.js reads, this reader reads the same way.
- three.js sets the penumbra of a spot light to the outer angle in radians, at least 1. This loader uses 1, because a penumbra above 1 is refused.
-
ByVertexandIndexare read asByVerticeandIndexToDirect. three.js does not read them. - A texture with no image is
NO_TEXTURE. three.js makes an empty texture. - A bump scale without a bump map is dropped, because
Materialrefuses it. - An image path must be relative. A path with
:is refused. - A track drives the model that its curve node connects to. three.js finds the model by its name, so two models of one name differ.
- A morph track drives the target of its channel. three.js finds the target by the name of the channel.
- three.js adds a slerp key at the time of the key before it, a second time. This loader drops a key whose time does not rise. The rotation is the same.
- An
Rcurve node without a curve for each axis makes no track. three.js makes a track of one number, which is not a rotation. - A stack that makes no track is not a clip. three.js makes a clip of no length.
- A geometric translation moves the offsets of a blend shape, as in three.js.
Animation layers after the first of a stack are not read, as in three.js. NURBS curves, LookAtProperty, orthographic cameras, a second set of texture coordinates, and the ambient occlusion, displacement, reflection and specular maps are skipped.
The reader raises for:
- An ASCII file with no
FBXVersion, or one older than 7000. A binary file older than 6400. - A binary record or property that runs past the file, or children that run past their record. An unknown property type. An array encoding that is not 0 or 1. A zlib array of the wrong length.
- An ASCII node without
:, a{that is not closed, or a}that closes nothing. - An ASCII string that is not closed, or a
*countthat does not match itsa. - Nodes nested deeper than
MAX_FBX_DEPTH.
The loader raises for:
- A format that is neither ASCII nor binary. A file with no
Objects. - A material with no
ShadingModel. A color channel outside zero to one. - A connection to a texture that is not there. An image path that is not relative, or an image that cannot be read. A
Contentthat is neither bytes nor base64. - A mapping or a reference that is not known. A layer index outside its values.
- A position index outside
Vertices. A last polygon with no negative index. A polygon of four or more corners with no area, or one that crosses itself so that no ear is left. - A mesh model with no mesh geometry. Models connected in a loop, or nested deeper than
MAX_MODEL_DEPTH. A transform that flattens an axis. - A camera, a light or a material that its builder refuses.
- A skin that deforms no geometry. A cluster with no
TransformLink, aTransformLinkthat is not 16 numbers, or a cluster with not one weight for each index. A cluster with no bone. - A blend shape that holds something other than a
BlendShapeChannel, or a channel with no shape. A shape with not three numbers for each index, or an index outside the positions. - An animation curve with no
KeyTimeorKeyValueFloat, or not one value for each time. A curve on a curve node that is not read. A curve node that drives nothing. A stack with no layer. A blend shape channel with no model. A rotation curve with no keys.
from loaders.font import read_font
var font = read_font("assets/fonts/fixture.typeface.json")
var shapes = font.generate_shapes("AO i8\nD", Length(1, METER))loaders/font.mojo. read_font(path) reads a typeface.js JSON font into a Font. parse_font(text) reads the text of one. Font.generate_shapes(text, size) lays the text out as shapes with holes. Text extrudes them. three.js: FontLoader, Font and ShapePath.
| Key | Meaning |
|---|---|
resolution |
The font units in one em. It must be positive. |
boundingBox.yMin, boundingBox.yMax
|
The lowest and highest point of a glyph, in font units. |
underlineThickness |
The underline, in font units. |
familyName |
The name of the font. It is optional. |
glyphs |
One object for each character. The key must be one character. |
glyphs.X.ha |
The advance of the glyph: how far the next glyph starts to the right. |
glyphs.X.o |
The outline of the glyph. It is optional. A space has none. |
An outline is a string of commands, each with its numbers, in font units. Other keys are ignored.
| Command | Draws |
|---|---|
m x y |
A new outline that starts at the point. |
l x y |
A straight run to the point. |
q x y cx cy |
A quadratic Bezier curve to x y, with the control point cx cy. |
b x y c1x c1y c2x c2y |
A cubic Bezier curve to x y, with two control points. |
z |
Nothing. three.js's TTFLoader writes it to close an outline, and Font steps over it. |
The end point comes first in q and b. That is the order of the file, and three.js reads it the same way.
from loaders.ttf import read_ttf
var font = read_ttf("assets/ttf/kenpixel.ttf")loaders/ttf.mojo. read_ttf(path, reversed) reads a TrueType font into a Font. ttf_json(bytes, reversed) gives the typeface JSON that three.js's TTFLoader makes, which parse_font reads. three.js reads the font with opentype.js. This reader follows opentype.js for TrueType outlines:
- The
cmapsubtable is the last for Unicode or Windows Unicode, of format 4 or 12. A format 4 table's last segment is not read, as in opentype.js. - A simple glyph gives a line to each point on the curve, and a quadratic curve through each point off it. Two points off the curve in a row meet at their midpoint.
- A composite glyph places its parts by an offset, a scale, a 2 by 2 matrix or matched points.
- The advances come from
hmtx, and the full name fromname, in US English.
The numbers are scaled by 100000 / (unitsPerEm * 72) and rounded. A glyph with no outline has no x_min or x_max. With reversed, three.js reverses a glyph's commands once for each code point that maps to it. So a glyph of two code points comes back in its first order, without its z. The reader keeps this.
The reader refuses a font that is not TrueType, CFF outlines and WOFF. It also refuses a font with no glyf or cmap it can read, and a code point that is a surrogate. It does not write original_font_information: FontLoader does not read it. A lone surrogate in the full name becomes U+FFFD, because a Mojo string cannot hold one.
The tests compare six fonts with three.js 0.180, plain and reversed: kenpixel.ttf from three.js's examples, and five from assets/ttf/make_fonts.py. A font with Apple's true signature must read as its TrueType twin. The reader must refuse fourteen broken fonts.
One font unit is size / resolution meters. The glyphs run left to right from the origin. Each glyph starts at the advance of the glyph before it. A line break, \n, moves back to x zero and down by one line height:
(boundingBox.yMax - boundingBox.yMin + underlineThickness) * size / resolution
Font.line_height(size) gives it. A character with no glyph takes the ? glyph, as in three.js.
A font does not mark its holes. A solid runs clockwise and a hole runs counterclockwise. ShapePath.to_shapes sorts the outlines of a glyph by that rule, as three.js does:
- An outline that runs clockwise is a solid.
to_shapes(is_ccw=True)swaps the rule. - A hole goes to the solid before it. When the first outline is a hole, each hole goes to the solid after it.
- With two or more solids, a hole that lies inside a different solid moves to it. No hole moves when one hole lies inside two solids.
- When no outline is a solid, each outline is a shape of its own.
math/shape_path.mojo holds ShapePath, signed_area, is_clockwise and is_point_inside_polygon. The point test is the three.js ray test, and a point on an edge is inside.
- The file is checked when it is read. three.js reads a glyph when it draws it, and skips a command it does not know.
- A
Shapemust be closed.to_shapesadds the closing run when an outline does not end where it started. The surface is the same. - A step that draws nothing, such as a line to the current point, is skipped. three.js keeps it and then drops the repeated point.
- The layout is calculated in
Float64, as in three.js. Each point is stored as aFloat32.
parse_font raises, with the glyph when there is one, for:
- A document that is not a JSON object.
- A missing or non-positive
resolution, or a missingboundingBox,yMin,yMax,underlineThicknessorglyphs. A metric that is not a finiteFloat32. - A glyph key that is not one character. A glyph that is not an object, or has no
ha. Anothat is not a string. - An outline command other than
m,l,qandb. A command with too few numbers. A number that is not a finiteFloat32. - An outline that draws before its first
m. Anmthat draws nothing before the nextmor the end.
generate_shapes raises for a size that is not positive. It raises for a character with no glyph in a font with no ? glyph.
assets/fonts/fixture.typeface.json is a small font made for the tests. It has A, D, O, i, 8, ? and a space. tests/test_font.mojo compares its layout with three.js 0.180.
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.
loaders/xml.mojo. parse_xml(text) reads an XML text into an XmlDocument: one XmlElement for each element, with the root element at element zero. The Collada loader reads with it. three.js uses the DOMParser of the browser.
| Member | Meaning |
|---|---|
name(element), parent(element)
|
The name of an element, as written, and the element that holds it. The root has NO_ELEMENT as its parent. |
attribute(element, key, default), has_attribute(element, key)
|
An attribute, or default when there is none. |
children(element), children_named(element, name), child(element, name)
|
The child elements, in file order. child gives the first, or NO_ELEMENT. |
text(element), text_content(element)
|
The text directly inside an element, or all the text inside it. |
The reader reads elements, attributes, text, CDATA sections, comments and processing instructions. It resolves the five named entities and character references. It changes each line ending to a line feed. It skips a document type declaration, so an entity that the declaration names is not known. It does not resolve namespaces: a:b is the name of the element.
The reader refuses these malformed forms with their byte position:
- No root element, or text after it. A declaration, a comment, a CDATA section or an instruction that is not closed.
- A malformed name or tag. An attribute without a quoted value, a
<in a value, or an attribute given twice. - An end tag that does not match, or an element that is not closed.
- An entity that is not known, or a character reference that names no character.
- Elements nested deeper than
MAX_XML_DEPTH. - Forbidden XML characters, signed character references, a double hyphen inside a comment, or
]]>outside CDATA.
CRLF and CR become LF in text and CDATA. Raw whitespace in attributes becomes a space; a CRLF pair gives one space. Character references keep the character they name. Quoted delimiters and comments inside a document type do not close it early. No external data is read and declared entities are not expanded.
Names follow XML 1.0 fifth edition NameStartChar and NameChar, including their Unicode limits. A processing instruction needs a name and whitespace before its data. Its target cannot be xml in any letter case.
An XML declaration can appear only at the start, after an optional byte order mark. Its fields are version, optional encoding, then optional standalone. Version syntax accepts 1. followed by one or more digits. Each such version uses XML 1.0 character rules. At most one document type declaration can appear before the root. Its name, external identifier and closing delimiters are checked.
This is a limited reader, not a complete XML validator. It does not validate DTD contents or root-name agreement with the DTD. It does not expand DTD entities or load external data. Namespace constraints are not checked. Input is a UTF-8 String; an encoding declaration does not transcode it.
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
- 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