WASM compile

This commit is contained in:
tovjemam 2025-08-05 14:01:26 +02:00
parent 35afe7722f
commit 3e94734887
10 changed files with 296 additions and 13 deletions

4
.gitignore vendored
View File

@ -1,3 +1,5 @@
build/ build/
build-wasm/
out/ out/
.vs/
.vscode/

3
.gitmodules vendored
View File

@ -2,3 +2,6 @@
path = external/SDL path = external/SDL
url = https://github.com/libsdl-org/SDL.git url = https://github.com/libsdl-org/SDL.git
branch = release-2.32.x branch = release-2.32.x
[submodule "external/glm"]
path = external/glm
url = https://github.com/g-truc/glm.git

View File

@ -6,19 +6,34 @@ set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Add src directory # Add src directory
add_executable(PortalGame src/main.cpp) add_executable(PortalGame
"src/main.cpp"
"src/app.hpp"
"src/app.cpp"
"src/gl.hpp"
)
# Platform-specific SDL2 handling # Platform-specific SDL2 handling
if(EMSCRIPTEN) if (CMAKE_SYSTEM_NAME STREQUAL Emscripten)
# Emscripten provides SDL2 via its system libraries # Emscripten provides SDL2 via its system libraries
message(STATUS "Target platform: WebAssembly (Emscripten)") message(STATUS "Target platform: WebAssembly (Emscripten)")
set(CMAKE_EXECUTABLE_SUFFIX ".html") # Optional: build HTML page set(CMAKE_EXECUTABLE_SUFFIX ".html") # Optional: build HTML page
target_compile_options(PortalGame PRIVATE
"-sUSE_SDL=2"
)
target_link_options(PortalGame PRIVATE target_link_options(PortalGame PRIVATE
"-sUSE_SDL=2" "-sUSE_SDL=2"
"-sASYNCIFY" "-sASYNCIFY"
"--preload-file assets" "-sUSE_WEBGL2=1"
) "--shell-file" "${CMAKE_SOURCE_DIR}/shell.html"
"--preload-file" "${CMAKE_SOURCE_DIR}/assets"
)
else() else()
message(STATUS "Target platform: Native")
# Native platform # Native platform
# find_package(SDL2 REQUIRED) # find_package(SDL2 REQUIRED)
# SDL2 build options to avoid unwanted components # SDL2 build options to avoid unwanted components
@ -28,4 +43,9 @@ else()
add_subdirectory(external/SDL) add_subdirectory(external/SDL)
target_include_directories(PortalGame PRIVATE "external/SDL/include") target_include_directories(PortalGame PRIVATE "external/SDL/include")
target_link_libraries(PortalGame PRIVATE SDL2main SDL2-static) target_link_libraries(PortalGame PRIVATE SDL2main SDL2-static)
endif() endif()
add_subdirectory(external/glm)
# target_include_directories(PortalGame PRIVATE "external/glm")
target_link_libraries(PortalGame PRIVATE glm)

53
CMakeUserPresets.json Normal file
View File

@ -0,0 +1,53 @@
{
"version": 3,
"cmakeMinimumRequired": {
"major": 3,
"minor": 21,
"patch": 0
},
"configurePresets": [
{
"name": "emscripten",
"displayName": "Emscripten",
"binaryDir": "build-wasm",
"generator": "Ninja Multi-Config",
"toolchainFile": "c:/dev/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake"
},
{
"name": "VS",
"displayName": "Visual Studio 17 2022",
"description": "Using compilers for Visual Studio 17 2022 (x64 architecture)",
"generator": "Visual Studio 17 2022",
"toolset": "host=x64",
"architecture": "x64",
"binaryDir": "${sourceDir}/out/build/${presetName}",
"cacheVariables": {
"CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}",
"CMAKE_C_COMPILER": "cl.exe",
"CMAKE_CXX_COMPILER": "cl.exe"
}
}
],
"buildPresets": [
{
"name": "Emscripten Debug",
"configurePreset": "emscripten",
"configuration": "Debug"
},
{
"name": "Emscripten Release",
"configurePreset": "emscripten",
"configuration": "Release"
},
{
"name": "VS Debug",
"configurePreset": "VS",
"configuration": "Debug"
},
{
"name": "VS Release",
"configurePreset": "VS",
"configuration": "Release"
}
]
}

1
assets/test.txt Normal file
View File

@ -0,0 +1 @@
Test

59
shell.html Normal file
View File

@ -0,0 +1,59 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>PortalGame</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<style>
html,
body {
margin: 0;
padding: 0;
overflow: hidden;
height: 100%;
background: black;
}
canvas {
display: block;
width: 100vw;
height: 100vh;
}
</style>
</head>
<body>
<canvas id="canvas" oncontextmenu="event.preventDefault()"></canvas>
<script>
var Module = {
preRun: [],
postRun: [],
canvas: (function() {
return document.getElementById('canvas');
})()
};
// Resize canvas to fit the window and inform SDL (via Emscripten)
function resizeCanvas() {
const canvas = document.getElementById('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// // Notify Emscripten SDL2
// if (typeof Module !== 'undefined' && Module.canvas) {
// const evt = new CustomEvent('resize');
// window.dispatchEvent(evt);
// }
}
window.addEventListener('resize', resizeCanvas);
window.addEventListener('load', resizeCanvas);
</script>
{{{ SCRIPT }}}
</body>
</html>

14
src/app.cpp Normal file
View File

@ -0,0 +1,14 @@
#include "app.hpp"
#include <glm/glm.hpp>
#include "gl.hpp"
App::App()
{
}
void App::Frame()
{
glm::vec3 clear_color(glm::abs(glm::sin(m_time)), 0.2f, 0.3f);
glClearColor(clear_color.r, clear_color.g, clear_color.b, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
}

15
src/app.hpp Normal file
View File

@ -0,0 +1,15 @@
#pragma once
class App
{
float m_time = 0.0f;
public:
App();
void Frame();
void SetTime(float time) { m_time = time; }
};

3
src/gl.hpp Normal file
View File

@ -0,0 +1,3 @@
#ifdef EMSCRIPTEN
#include <GLES2/gl2.h>
#endif

View File

@ -1,21 +1,134 @@
#include <SDL.h> #include <SDL.h>
#include <iostream> #include <iostream>
#include <memory>
#include "app.hpp"
int main(int argc, char* argv[]) { #ifdef EMSCRIPTEN
if (SDL_Init(SDL_INIT_VIDEO) != 0) { #include <emscripten.h>
#endif
static SDL_Window *s_window = nullptr;
static SDL_GLContext s_context = nullptr;
static bool s_quit = false;
static std::unique_ptr<App> s_app;
static bool InitSDL()
{
if (SDL_Init(SDL_INIT_VIDEO) != 0)
{
std::cerr << "SDL_Init Error: " << SDL_GetError() << std::endl; std::cerr << "SDL_Init Error: " << SDL_GetError() << std::endl;
return 1; return false;
} }
SDL_Window* win = SDL_CreateWindow("Hello SDL2", 100, 100, 640, 480, SDL_WINDOW_SHOWN); s_window = SDL_CreateWindow("PortalGame", 100, 100, 640, 480, SDL_WINDOW_SHOWN | SDL_WINDOW_MAXIMIZED | SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
if (!win) { if (!s_window)
{
std::cerr << "SDL_CreateWindow Error: " << SDL_GetError() << std::endl; std::cerr << "SDL_CreateWindow Error: " << SDL_GetError() << std::endl;
SDL_Quit(); SDL_Quit();
return false;
}
return true;
}
static bool InitGL()
{
#ifdef EMSCRIPTEN
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
#else
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
#endif
s_context = SDL_GL_CreateContext(s_window);
if (!s_context)
{
std::cerr << "SDL_GL_CreateContext Error: " << SDL_GetError() << std::endl;
return false;
}
// Make context current
if (SDL_GL_MakeCurrent(s_window, s_context) != 0)
{
std::cerr << "SDL_GL_MakeCurrent Error: " << SDL_GetError() << std::endl;
SDL_GL_DeleteContext(s_context);
return false;
}
return true;
}
static void ShutdownGL()
{
if (s_context)
{
SDL_GL_DeleteContext(s_context);
s_context = nullptr;
}
}
static void ShutdownSDL()
{
if (s_window)
{
SDL_DestroyWindow(s_window);
s_window = nullptr;
}
SDL_Quit();
}
static void PollEvents()
{
SDL_Event event;
while (SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT)
{
s_quit = true;
return;
}
}
}
static void Frame()
{
Uint32 current_time = SDL_GetTicks();
s_app->SetTime(current_time / 1000.0f); // Set time in seconds
PollEvents();
s_app->Frame();
SDL_GL_SwapWindow(s_window);
}
int main(int argc, char *argv[])
{
if (!InitSDL())
{
return 1; return 1;
} }
SDL_Delay(2000); if (!InitGL())
SDL_DestroyWindow(win); {
SDL_Quit(); ShutdownSDL();
return 1;
}
s_app = std::make_unique<App>();
#ifdef EMSCRIPTEN
emscripten_set_main_loop(Frame, 0, true);
#else
while (!s_quit)
{
Frame();
}
s_app.reset();
ShutdownGL();
ShutdownSDL();
#endif
return 0; return 0;
} }