A header-only Wavefront OBJ parser for loading 3D models into forge-gpu.
#include "obj/forge_obj.h"
ForgeObjMesh mesh;
if (forge_obj_load("model.obj", &mesh)) {
// mesh.vertices is ready for GPU upload
// Every 3 consecutive vertices form one triangle
// Upload to a vertex buffer, then draw:
// SDL_DrawGPUPrimitives(pass, mesh.vertex_count, 1, 0, 0);
forge_obj_free(&mesh);
}ForgeObjVertex-- Position (vec3) + normal (vec3) + UV (vec2), interleaved and ready for GPU uploadForgeObjMesh-- A flat array of de-indexed vertices (no index buffer needed)
forge_obj_load(path, out_mesh)-- Load an OBJ file into a flat vertex array. Returnstrueon success,falseon error (logged viaSDL_Log)forge_obj_free(mesh)-- Free memory allocated byforge_obj_load
The vertex matches this GPU pipeline layout:
| Attribute | Type | HLSL Semantic | Content |
|---|---|---|---|
| location 0 | float3 |
TEXCOORD0 |
Position |
| location 1 | float3 |
TEXCOORD1 |
Normal |
| location 2 | float2 |
TEXCOORD2 |
UV |
This is the same layout as ForgeGltfVertex, so the same pipeline works for
both OBJ and glTF models.
- Positions (
v), texture coordinates (vt), normals (vn) - Triangular and quad faces (
f) withv/vt/vnindices - Quads are automatically triangulated into two triangles
- 1-based OBJ indices (converted internally to 0-based)
- Windows (
\r\n) and Unix (\n) line endings - Scientific notation in float values (e.g.
1.5e-3)
These are intentional simplifications for a learning library:
- Single-object files only -- ignores
g/ogrouping - No material library --
mtllib/usemtlare ignored - No negative indices -- relative indexing not supported
- Triangles and quads only -- no n-gons with more than 4 vertices
OBJ files allow separate index streams for position, UV, and normal. A vertex might use position 5 with UV 12 and normal 3 -- a combination that can't map 1:1 to a single GPU index buffer without duplication.
The parser solves this by "de-indexing": each triangle gets its own copy of each
vertex with all attributes baked in. This means no index buffer is needed -- just
draw with SDL_DrawGPUPrimitives.
For indexed drawing (shared vertices, smaller buffers), see the glTF parser
(common/gltf/) which supports SDL_DrawGPUIndexedPrimitives.
- SDL3 -- for file I/O, logging, memory allocation
- forge_math -- for
vec2,vec3types (common/math/)
lessons/gpu/08-mesh-loading/-- Full example loading an OBJ model with textures and mipmapstests/obj/-- Unit tests for the parser
- Readability over performance -- this code is meant to be learned from
- Header-only -- just include
forge_obj.h, no build config needed - No dependencies beyond SDL -- no external parsing libraries
- Two-pass parsing -- first pass counts elements, second pass reads data, so all memory is allocated up front with no dynamic resizing
zlib -- same as SDL and the rest of forge-gpu.