Skip to content

OBJ: consume authored vertex normals (vn) so OBJ models can be lit #1572

Description

@obiot

Summary

The OBJ parser reads vn and then discards it — src/loader/parsers/obj.js:43 lists it under "Parsed but ignored: vn (normals), g (groups), s (smooth shading)". Faces already carry the normal index (v//vn and v/vt/vn are both accepted at obj.js:28), so the data reaches the parser and is dropped on the floor.

Why it matters

The engine has a full lit-mesh path: Light3d directional / ambient / point / spot, half-Lambert diffuse, up to 32 lights in a uniform block, on both GPU backends. Mesh takes a normals setting and lit: true.

A glTF mesh gets all of that, because its NORMAL accessor is read. An OBJ mesh cannot, because its authored normals never arrive — so lit: true on an OBJ model shades against whatever the mesh path falls back to rather than the surface the artist modelled. That reads as a bug rather than a missing feature: the setting exists, the loader accepts the file, and the result is wrong.

This is the cheapest way to make an existing feature work for an existing loader, rather than adding new surface.

Proposal

  • Emit a normals array alongside vertices/uvs/indices, indexed the same way the UVs already are (the parser's vertex-deduplication key gains the normal index, exactly as it presumably handles vt today).
  • When a file supplies no vn, generate them: face normals accumulated per vertex, then normalized. Honour s (smoothing groups) if it is cheap to do so — smooth within a group, flat across group boundaries — otherwise generate flat normals and note the limitation. A model with no normals at all is common in the wild, so the fallback matters as much as the parse.
  • The winding/handedness bridge already applied to positions has to apply to normals too, or lighting will be inverted on one axis. Worth an explicit test rather than an assumption.

Acceptance

  • An OBJ + lit: true under a Light3d shades like the equivalent glTF import of the same model.
  • A vn-less OBJ still lights plausibly (generated normals), and a smooth sphere does not read as faceted when s says otherwise.
  • Normals survive the axis bridge — a light from above lights the top, on both rightHanded settings.

References

src/loader/parsers/obj.js, src/renderable/mesh.js (the normals setting and the lit path), src/video/webgl/shaders/mesh-lit.vert / mesh-lit.wgsl.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions