feat(gfx): mesh_gpu — VAO/VBO/EBO upload para Mesh
mesh_gpu_upload sube positions+normals interleaved (stride 6 floats, location 0=a_pos, location 1=a_normal) + EBO uint32. mesh_gpu_destroy libera todo. GL_STATIC_DRAW (mesh inmutable post-upload). issue 0029
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
#include "gfx/mesh_gpu.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace fn::gfx {
|
||||
|
||||
MeshGpu mesh_gpu_upload(const Mesh& mesh) {
|
||||
MeshGpu g{};
|
||||
if (mesh.positions.empty() || mesh.indices.empty()) return g;
|
||||
if (mesh.normals.size() != mesh.positions.size()) return g;
|
||||
|
||||
const size_t nverts = mesh.positions.size() / 3;
|
||||
|
||||
// Interleave pos.xyz + normal.xyz, stride 6 floats.
|
||||
std::vector<float> interleaved;
|
||||
interleaved.resize(nverts * 6);
|
||||
for (size_t i = 0; i < nverts; ++i) {
|
||||
interleaved[i*6 + 0] = mesh.positions[i*3 + 0];
|
||||
interleaved[i*6 + 1] = mesh.positions[i*3 + 1];
|
||||
interleaved[i*6 + 2] = mesh.positions[i*3 + 2];
|
||||
interleaved[i*6 + 3] = mesh.normals[i*3 + 0];
|
||||
interleaved[i*6 + 4] = mesh.normals[i*3 + 1];
|
||||
interleaved[i*6 + 5] = mesh.normals[i*3 + 2];
|
||||
}
|
||||
|
||||
glGenVertexArrays(1, &g.vao);
|
||||
glGenBuffers(1, &g.vbo);
|
||||
glGenBuffers(1, &g.ebo);
|
||||
|
||||
glBindVertexArray(g.vao);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, g.vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER,
|
||||
(GLsizeiptr)(interleaved.size() * sizeof(float)),
|
||||
interleaved.data(),
|
||||
GL_STATIC_DRAW);
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g.ebo);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
|
||||
(GLsizeiptr)(mesh.indices.size() * sizeof(uint32_t)),
|
||||
mesh.indices.data(),
|
||||
GL_STATIC_DRAW);
|
||||
|
||||
// location 0 = a_pos (vec3)
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,
|
||||
(GLsizei)(6 * sizeof(float)),
|
||||
(const void*)0);
|
||||
// location 1 = a_normal (vec3)
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE,
|
||||
(GLsizei)(6 * sizeof(float)),
|
||||
(const void*)(3 * sizeof(float)));
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
|
||||
g.index_count = (int)mesh.indices.size();
|
||||
return g;
|
||||
}
|
||||
|
||||
void mesh_gpu_destroy(MeshGpu& g) {
|
||||
if (g.ebo) { glDeleteBuffers(1, &g.ebo); g.ebo = 0; }
|
||||
if (g.vbo) { glDeleteBuffers(1, &g.vbo); g.vbo = 0; }
|
||||
if (g.vao) { glDeleteVertexArrays(1, &g.vao); g.vao = 0; }
|
||||
g.index_count = 0;
|
||||
}
|
||||
|
||||
} // namespace fn::gfx
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "gfx/gl_loader.h"
|
||||
#include "gfx/mesh_obj_load.h"
|
||||
|
||||
namespace fn::gfx {
|
||||
|
||||
// VAO + VBO interleaved (pos.xyz, nrm.xyz) + EBO. ok() despues de upload.
|
||||
struct MeshGpu {
|
||||
GLuint vao = 0;
|
||||
GLuint vbo = 0;
|
||||
GLuint ebo = 0;
|
||||
int index_count = 0;
|
||||
bool ok() const { return vao != 0 && index_count > 0; }
|
||||
};
|
||||
|
||||
// Sube un Mesh CPU al GPU. Requiere contexto GL activo.
|
||||
// Si Mesh esta vacio o no es valido, devuelve MeshGpu{} (ok() == false).
|
||||
// Layout: location 0 = vec3 a_pos, location 1 = vec3 a_normal.
|
||||
MeshGpu mesh_gpu_upload(const Mesh& mesh);
|
||||
|
||||
// Libera los recursos GL. Seguro con mesh_gpu vacio.
|
||||
void mesh_gpu_destroy(MeshGpu& mesh_gpu);
|
||||
|
||||
} // namespace fn::gfx
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
name: mesh_gpu
|
||||
kind: function
|
||||
lang: cpp
|
||||
domain: gfx
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "MeshGpu mesh_gpu_upload(const Mesh&); void mesh_gpu_destroy(MeshGpu&)"
|
||||
description: "Sube un Mesh CPU a OpenGL como VAO + VBO interleaved (pos.xyz, normal.xyz) + EBO uint32. Layout: location 0 = a_pos vec3, location 1 = a_normal vec3, stride 6 floats."
|
||||
tags: [opengl, mesh, vao, vbo, ebo, gpu, gfx]
|
||||
uses_functions: [mesh_obj_load_cpp_gfx]
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [GL/gl.h, GL/glext.h]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "cpp/functions/gfx/mesh_gpu.cpp"
|
||||
framework: opengl
|
||||
params:
|
||||
- name: mesh
|
||||
desc: "Mesh CPU con positions/normals (mismo length, stride 3) e indices uint32. Si esta vacio o invalido, upload devuelve MeshGpu{} (ok()==false)."
|
||||
- name: mesh_gpu
|
||||
desc: "MeshGpu (vao/vbo/ebo, index_count). destroy libera todo y pone IDs a 0."
|
||||
output: "mesh_gpu_upload: MeshGpu listo para draw con glDrawElements(GL_TRIANGLES, index_count, GL_UNSIGNED_INT, 0). Si !ok(), no hubo upload."
|
||||
---
|
||||
|
||||
# mesh_gpu
|
||||
|
||||
CRUD GPU minimal para `Mesh`. Asume contexto OpenGL 3.3+ activo.
|
||||
|
||||
## Layout de attribs
|
||||
|
||||
```glsl
|
||||
#version 330 core
|
||||
layout(location = 0) in vec3 a_pos;
|
||||
layout(location = 1) in vec3 a_normal;
|
||||
```
|
||||
|
||||
Stride = `6 * sizeof(float)`, sin padding.
|
||||
|
||||
## Uso tipico
|
||||
|
||||
```cpp
|
||||
auto cpu = fn::gfx::mesh_obj_load("model.obj");
|
||||
auto gpu = fn::gfx::mesh_gpu_upload(cpu);
|
||||
if (!gpu.ok()) { /* falla */ return; }
|
||||
|
||||
// Draw:
|
||||
glUseProgram(prog);
|
||||
glBindVertexArray(gpu.vao);
|
||||
glDrawElements(GL_TRIANGLES, gpu.index_count, GL_UNSIGNED_INT, 0);
|
||||
glBindVertexArray(0);
|
||||
|
||||
// Cleanup:
|
||||
fn::gfx::mesh_gpu_destroy(gpu);
|
||||
```
|
||||
|
||||
## Validacion de input
|
||||
|
||||
`mesh_gpu_upload` exige que `mesh.normals.size() == mesh.positions.size()`. `mesh_obj_parse` siempre genera normales (per-face si faltan) → invariante natural.
|
||||
|
||||
## Notas
|
||||
|
||||
- Indices son `GL_UNSIGNED_INT` (32-bit) para soportar meshes grandes sin tener que decidir formato dinamicamente.
|
||||
- `GL_STATIC_DRAW`: el assumption es que la malla no cambia post-upload. Si necesitas streaming, crear otro helper con `GL_DYNAMIC_DRAW`.
|
||||
Reference in New Issue
Block a user