feat(gfx): mesh_obj_load — minimal Wavefront .obj parser
mesh_obj_parse (pure) + mesh_obj_load (impure file helper). Soporta v / vn / f (tris y quads). Genera normales per-face si faltan (flat shading). Quads se parten en 2 tris; n-gons (>4) se descartan silenciosamente. Indices 1-based positivos y negativos. issue 0029
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
---
|
||||
name: mesh_obj_load
|
||||
kind: function
|
||||
lang: cpp
|
||||
domain: gfx
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "Mesh mesh_obj_parse(const char* obj_text, size_t len); Mesh mesh_obj_load(const char* path)"
|
||||
description: "Parser minimal de Wavefront .obj — soporta v, vn, f (tris y quads). Genera normales por face si faltan. mesh_obj_parse es puro; mesh_obj_load es helper impuro que lee fichero y delega."
|
||||
tags: [obj, mesh, parser, wavefront, loader, geometry, 3d]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: [fstream, sstream, cmath]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "cpp/functions/gfx/mesh_obj_load.cpp"
|
||||
framework: opengl
|
||||
params:
|
||||
- name: obj_text
|
||||
desc: "Buffer de texto .obj. Lineas reconocidas: v, vn, f. Comentarios con #. UTF-8 plano."
|
||||
- name: len
|
||||
desc: "Longitud del buffer en bytes"
|
||||
- name: path
|
||||
desc: "Ruta absoluta o relativa del .obj a leer (mesh_obj_load impuro)"
|
||||
output: "Mesh con positions/normals (stride 3, mismo length) y indices (tri-list, multiplo de 3). Si no hay vn, normales por face (flat shading) y vertices duplicados por face. Mesh vacio si parse falla."
|
||||
---
|
||||
|
||||
# mesh_obj_load
|
||||
|
||||
Parser pequeno de Wavefront `.obj` para inspeccion de geometria. KISS: cubre el subconjunto que la mayoria de exporters genera (Blender default, MeshLab, etc).
|
||||
|
||||
## Soporta
|
||||
|
||||
- `v x y z` — vertice (xyz; w opcional ignorado)
|
||||
- `vn x y z` — normal
|
||||
- `f a b c` y `f a b c d` — tris y quads (los quads se dividen en 2 tris)
|
||||
- Indices `v`, `v/t`, `v//n`, `v/t/n` (vt se ignora; t no afecta)
|
||||
- Indices 1-based positivos y negativos (relative-to-end, conforme spec)
|
||||
- Lineas en blanco y comentarios con `#`
|
||||
|
||||
## NO soporta (en este issue)
|
||||
|
||||
- N-gons con mas de 4 vertices → silenciosamente descartados
|
||||
- `vt` (coordenadas de textura) — el indice se parsea pero el dato se ignora
|
||||
- `mtllib`, `usemtl`, `o`, `g`, `s` — se saltan
|
||||
- Lineas de polilineas (`l`)
|
||||
- Curvas, superficies, `vp`
|
||||
|
||||
## Generacion de normales
|
||||
|
||||
Si el `.obj` no tiene `vn` (o si alguna face no las referencia), se generan normales por face (flat shading). Esto duplica vertices entre faces que comparten posicion pero no normal — es el comportamiento esperado para inspeccion.
|
||||
|
||||
Cuando todas las faces tienen normales explicitas, se hace dedup `(v_idx, n_idx)` con un linear-scan simple. Para meshes inspeccion (<<100k verts unicos) es perf-OK; meshes mas grandes se beneficiarian de un hashmap.
|
||||
|
||||
## Errores
|
||||
|
||||
Si el archivo no tiene vertices o no tiene faces, devuelve `Mesh{}` (positions/indices vacios). El caller puede chequear con `mesh.positions.empty()`.
|
||||
|
||||
## Test inline (cubo)
|
||||
|
||||
```cpp
|
||||
const char* obj =
|
||||
"v -1 -1 -1\nv 1 -1 -1\nv 1 1 -1\nv -1 1 -1\n"
|
||||
"v -1 -1 1\nv 1 -1 1\nv 1 1 1\nv -1 1 1\n"
|
||||
"f 1 2 3 4\nf 5 6 7 8\nf 1 2 6 5\n"
|
||||
"f 4 3 7 8\nf 1 4 8 5\nf 2 3 7 6\n";
|
||||
Mesh m = mesh_obj_parse(obj, std::strlen(obj));
|
||||
// 6 quads -> 12 tris -> 36 indices.
|
||||
// Sin vn -> face normals -> 36 verts emitidos (duplicados por face).
|
||||
assert(m.indices.size() == 36);
|
||||
```
|
||||
Reference in New Issue
Block a user