243 lines
6.8 KiB
C++
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;
|
|
}
|