feat(primitives_gallery): demo de mesh_viewer (cubo procedural + .obj loader)
Genera cubo procedural in-line (mesh_obj_parse de string), permite cargar .obj desde un text input absoluto. Botones: Reload cube, Wireframe toggle, Load .obj. Status line con tris count y instrucciones (drag to orbit, wheel to zoom). issue 0029
This commit is contained in:
@@ -8,6 +8,7 @@ add_imgui_app(primitives_gallery
|
||||
demos_text_editor.cpp
|
||||
demos_gl_texture.cpp
|
||||
demos_extras.cpp
|
||||
demos_mesh.cpp
|
||||
# text_editor + file_watcher (issue 0025)
|
||||
${CMAKE_SOURCE_DIR}/functions/core/text_editor.cpp
|
||||
${CMAKE_SOURCE_DIR}/functions/core/file_watcher.cpp
|
||||
@@ -55,6 +56,11 @@ add_imgui_app(primitives_gallery
|
||||
# gl_texture_load (issue 0026) + stb_image
|
||||
${CMAKE_SOURCE_DIR}/functions/gfx/gl_texture_load.cpp
|
||||
${CMAKE_SOURCE_DIR}/vendor/stb/stb_image_impl.cpp
|
||||
# mesh_viewer stack (issue 0029)
|
||||
${CMAKE_SOURCE_DIR}/functions/gfx/mesh_obj_load.cpp
|
||||
${CMAKE_SOURCE_DIR}/functions/gfx/mesh_gpu.cpp
|
||||
${CMAKE_SOURCE_DIR}/functions/core/orbit_camera.cpp
|
||||
${CMAKE_SOURCE_DIR}/functions/viz/mesh_viewer.cpp
|
||||
)
|
||||
target_include_directories(primitives_gallery PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/vendor/imgui_text_edit
|
||||
|
||||
@@ -34,6 +34,7 @@ void demo_candlestick();
|
||||
void demo_gauge();
|
||||
void demo_heatmap();
|
||||
void demo_table_view();
|
||||
void demo_mesh_viewer(); // issue 0029
|
||||
|
||||
// --- Gfx ---
|
||||
void demo_shader_canvas();
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
// Demo del primitivo viz/mesh_viewer.
|
||||
// Genera un cubo procedural in-line, lo sube al GPU, y permite cargar un
|
||||
// .obj desde un path ingresado en un text input.
|
||||
|
||||
#include "demos.h"
|
||||
#include "demo.h"
|
||||
|
||||
#include "viz/mesh_viewer.h"
|
||||
#include "gfx/mesh_obj_load.h"
|
||||
#include "gfx/mesh_gpu.h"
|
||||
#include "core/orbit_camera.h"
|
||||
|
||||
#include <imgui.h>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
namespace gallery {
|
||||
|
||||
namespace {
|
||||
|
||||
const char* kCubeObj =
|
||||
"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 4 3 2 1\n" // back (-Z) — winding for outward normal
|
||||
"f 5 6 7 8\n" // front (+Z)
|
||||
"f 1 2 6 5\n" // bottom (-Y)
|
||||
"f 8 7 3 4\n" // top (+Y)
|
||||
"f 5 8 4 1\n" // left (-X)
|
||||
"f 2 3 7 6\n"; // right (+X)
|
||||
|
||||
struct State {
|
||||
fn::gfx::MeshGpu mesh{};
|
||||
fn::core::OrbitCamera cam{};
|
||||
char path[512] = "";
|
||||
std::string status;
|
||||
bool wireframe = false;
|
||||
bool initialized = false;
|
||||
};
|
||||
|
||||
State& state() {
|
||||
static State s;
|
||||
return s;
|
||||
}
|
||||
|
||||
void load_cube() {
|
||||
auto& s = state();
|
||||
if (s.mesh.ok()) fn::gfx::mesh_gpu_destroy(s.mesh);
|
||||
auto cpu = fn::gfx::mesh_obj_parse(kCubeObj, std::strlen(kCubeObj));
|
||||
s.mesh = fn::gfx::mesh_gpu_upload(cpu);
|
||||
s.status = s.mesh.ok()
|
||||
? ("loaded cube: " + std::to_string(s.mesh.index_count / 3) + " tris")
|
||||
: "cube upload failed";
|
||||
}
|
||||
|
||||
void load_from_path() {
|
||||
auto& s = state();
|
||||
if (!s.path[0]) { s.status = "path is empty"; return; }
|
||||
auto cpu = fn::gfx::mesh_obj_load(s.path);
|
||||
if (cpu.positions.empty()) { s.status = "parse/read failed"; return; }
|
||||
if (s.mesh.ok()) fn::gfx::mesh_gpu_destroy(s.mesh);
|
||||
s.mesh = fn::gfx::mesh_gpu_upload(cpu);
|
||||
s.status = s.mesh.ok()
|
||||
? ("loaded: " + std::to_string(s.mesh.index_count / 3) + " tris")
|
||||
: "upload failed";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void demo_mesh_viewer() {
|
||||
demo_header("mesh_viewer", "v1.0.0",
|
||||
"Visualizador 3D para inspeccion de geometria. Composicion de "
|
||||
"mesh_obj_load (parser .obj puro) + mesh_gpu (upload VAO/VBO/EBO) + "
|
||||
"orbit_camera (drag/wheel) + mesh_viewer (FBO + ImGui::Image + Lambert).");
|
||||
|
||||
auto& s = state();
|
||||
if (!s.initialized) {
|
||||
load_cube();
|
||||
s.initialized = true;
|
||||
}
|
||||
|
||||
// Controls row.
|
||||
if (ImGui::Button("Reload cube")) load_cube();
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Wireframe", &s.wireframe);
|
||||
ImGui::SameLine();
|
||||
ImGui::TextDisabled("|");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(360);
|
||||
ImGui::InputTextWithHint("##obj_path", "absolute path to .obj", s.path, sizeof(s.path));
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Load .obj")) load_from_path();
|
||||
|
||||
ImGui::TextDisabled("status: %s | tris: %d | drag to orbit, wheel to zoom",
|
||||
s.status.c_str(),
|
||||
s.mesh.ok() ? s.mesh.index_count / 3 : 0);
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
fn::viz::MeshViewerConfig cfg{};
|
||||
cfg.mesh = &s.mesh;
|
||||
cfg.cam = &s.cam;
|
||||
cfg.size = ImVec2(-1.0f, 480.0f);
|
||||
cfg.color = IM_COL32(160, 200, 255, 255);
|
||||
cfg.wireframe = s.wireframe;
|
||||
fn::viz::mesh_viewer("##gallery_mesh_viewer", cfg);
|
||||
}
|
||||
|
||||
} // namespace gallery
|
||||
@@ -61,6 +61,7 @@ static const DemoEntry k_demos[] = {
|
||||
{"gauge", "gauge", "Viz", &gallery::demo_gauge},
|
||||
{"heatmap", "heatmap", "Viz", &gallery::demo_heatmap},
|
||||
{"table_view", "table_view", "Viz", &gallery::demo_table_view},
|
||||
{"mesh_viewer", "mesh_viewer", "Viz", &gallery::demo_mesh_viewer},
|
||||
// Gfx (shaders_lab core)
|
||||
{"shader_canvas", "shader_canvas", "Gfx", &gallery::demo_shader_canvas},
|
||||
{"gl_texture", "gl_texture_load", "Gfx", &gallery::demo_gl_texture}, // wave 1
|
||||
|
||||
Reference in New Issue
Block a user