Files
engine_smoke/main.cpp
T
2026-05-11 16:28:43 +02:00

243 lines
6.8 KiB
C++

// engine_smoke — gamedev stack smoke test.
// Validates SDL3 + sokol_gfx + Dear ImGui (imgui_impl_sdl3 + imgui_impl_opengl3)
// builds and runs on PC (desktop GL 3.3) and WASM (WebGL2 via -sFULL_ES3=1).
//
// Issue 0072a. Standalone — does NOT use fn_framework / add_imgui_app.
#include <cstdio>
#include <cstdint>
#include <cmath>
#include <SDL3/SDL.h>
// sokol_gfx — single TU includes the implementation.
#define SOKOL_IMPL
#if defined(__EMSCRIPTEN__)
#define SOKOL_GLES3
#else
#define SOKOL_GLCORE
#endif
#include "sokol_gfx.h"
#include "sokol_log.h"
// sokol_glue.h is NOT used: it pulls in sokol_app symbols. We use SDL3 for
// windowing, so we build sg_environment / sg_swapchain manually.
#include "imgui.h"
#include "backends/imgui_impl_sdl3.h"
#include "backends/imgui_impl_opengl3.h"
#if defined(__EMSCRIPTEN__)
#include <emscripten/emscripten.h>
#endif
namespace {
struct App {
SDL_Window* win = nullptr;
SDL_GLContext gl = nullptr;
sg_pipeline pip{};
sg_bindings bind{};
sg_pass_action pass_action{};
bool running = true;
uint64_t frame = 0;
};
App g_app;
// Fullscreen-quad vertex shader (no inputs, gl_VertexID trick).
const char* VS_SRC =
#if defined(__EMSCRIPTEN__)
"#version 300 es\n"
#else
"#version 330 core\n"
#endif
"out vec2 v_uv;\n"
"void main() {\n"
" vec2 p = vec2((gl_VertexID == 1) ? 3.0 : -1.0, (gl_VertexID == 2) ? 3.0 : -1.0);\n"
" v_uv = p * 0.5 + 0.5;\n"
" gl_Position = vec4(p, 0.0, 1.0);\n"
"}\n";
const char* FS_SRC =
#if defined(__EMSCRIPTEN__)
"#version 300 es\n"
"precision mediump float;\n"
#else
"#version 330 core\n"
#endif
"in vec2 v_uv;\n"
"out vec4 frag;\n"
"uniform float u_time;\n"
"void main() {\n"
" float r = 0.5 + 0.5 * sin(u_time + v_uv.x * 6.2831);\n"
" float g = 0.5 + 0.5 * sin(u_time * 1.3 + v_uv.y * 6.2831);\n"
" float b = 0.5 + 0.5 * sin(u_time * 0.7 + (v_uv.x + v_uv.y) * 6.2831);\n"
" frag = vec4(r, g, b, 1.0);\n"
"}\n";
sg_environment make_environment() {
sg_environment env{};
env.defaults.color_format = SG_PIXELFORMAT_RGBA8;
env.defaults.depth_format = SG_PIXELFORMAT_DEPTH_STENCIL;
env.defaults.sample_count = 1;
return env;
}
sg_swapchain make_swapchain(int w, int h) {
sg_swapchain sw{};
sw.width = w;
sw.height = h;
sw.sample_count = 1;
sw.color_format = SG_PIXELFORMAT_RGBA8;
sw.depth_format = SG_PIXELFORMAT_DEPTH_STENCIL;
sw.gl.framebuffer = 0; // default framebuffer
return sw;
}
void setup_gfx() {
sg_desc d{};
d.environment = make_environment();
d.logger.func = slog_func;
sg_setup(&d);
sg_shader_desc sd{};
sd.vertex_func.source = VS_SRC;
sd.fragment_func.source = FS_SRC;
sd.uniform_blocks[0].stage = SG_SHADERSTAGE_FRAGMENT;
sd.uniform_blocks[0].size = sizeof(float);
sd.uniform_blocks[0].layout = SG_UNIFORMLAYOUT_NATIVE;
sd.uniform_blocks[0].glsl_uniforms[0].type = SG_UNIFORMTYPE_FLOAT;
sd.uniform_blocks[0].glsl_uniforms[0].glsl_name = "u_time";
sg_shader shd = sg_make_shader(&sd);
sg_pipeline_desc pd{};
pd.shader = shd;
pd.primitive_type = SG_PRIMITIVETYPE_TRIANGLES;
g_app.pip = sg_make_pipeline(&pd);
g_app.pass_action.colors[0].load_action = SG_LOADACTION_CLEAR;
g_app.pass_action.colors[0].clear_value = {0.05f, 0.05f, 0.08f, 1.0f};
}
void frame() {
SDL_Event ev;
while (SDL_PollEvent(&ev)) {
ImGui_ImplSDL3_ProcessEvent(&ev);
if (ev.type == SDL_EVENT_QUIT) g_app.running = false;
if (ev.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED) g_app.running = false;
}
int w, h;
SDL_GetWindowSizeInPixels(g_app.win, &w, &h);
sg_pass p{};
p.action = g_app.pass_action;
p.swapchain = make_swapchain(w, h);
sg_begin_pass(&p);
sg_apply_pipeline(g_app.pip);
float t = (float)g_app.frame * (1.0f / 60.0f);
sg_range time_range{ &t, sizeof(t) };
sg_apply_uniforms(0, &time_range);
sg_draw(0, 3, 1); // 3 vertices, fullscreen tri via gl_VertexID
// ImGui on top
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplSDL3_NewFrame();
ImGui::NewFrame();
ImGui::SetNextWindowPos(ImVec2(10, 10));
ImGui::Begin("engine_smoke", nullptr,
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove);
ImGui::Text("SDL3 + sokol_gfx + ImGui");
ImGui::Text("FPS: %.1f", ImGui::GetIO().Framerate);
ImGui::Text("Frame: %llu Size: %dx%d", (unsigned long long)g_app.frame, w, h);
if (ImGui::Button("Quit")) g_app.running = false;
ImGui::End();
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
sg_end_pass();
sg_commit();
SDL_GL_SwapWindow(g_app.win);
g_app.frame++;
}
#if defined(__EMSCRIPTEN__)
void emscripten_loop() {
if (!g_app.running) {
emscripten_cancel_main_loop();
return;
}
frame();
}
#endif
} // namespace
int main(int /*argc*/, char** /*argv*/) {
if (!SDL_Init(SDL_INIT_VIDEO)) {
std::fprintf(stderr, "SDL_Init failed: %s\n", SDL_GetError());
return 1;
}
#if defined(__EMSCRIPTEN__)
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
#else
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
#endif
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
g_app.win = SDL_CreateWindow("engine_smoke", 1280, 720,
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY);
if (!g_app.win) {
std::fprintf(stderr, "SDL_CreateWindow failed: %s\n", SDL_GetError());
SDL_Quit();
return 1;
}
g_app.gl = SDL_GL_CreateContext(g_app.win);
if (!g_app.gl) {
std::fprintf(stderr, "SDL_GL_CreateContext failed: %s\n", SDL_GetError());
SDL_DestroyWindow(g_app.win);
SDL_Quit();
return 1;
}
SDL_GL_MakeCurrent(g_app.win, g_app.gl);
SDL_GL_SetSwapInterval(1);
setup_gfx();
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGui::StyleColorsDark();
ImGui_ImplSDL3_InitForOpenGL(g_app.win, g_app.gl);
#if defined(__EMSCRIPTEN__)
ImGui_ImplOpenGL3_Init("#version 300 es");
#else
ImGui_ImplOpenGL3_Init("#version 330 core");
#endif
#if defined(__EMSCRIPTEN__)
emscripten_set_main_loop(emscripten_loop, 0, 1);
#else
while (g_app.running) frame();
#endif
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplSDL3_Shutdown();
ImGui::DestroyContext();
sg_shutdown();
SDL_GL_DestroyContext(g_app.gl);
SDL_DestroyWindow(g_app.win);
SDL_Quit();
return 0;
}