You shouldn’t need special emscripten code for handling mouse events and resizing. If you’re not getting the normal SDL events for those, please file a bug on GitHub:
Issues · libsdl-org/SDL (github.com)
Wouldn’t this variant make more sense than calling SDL_GetWindowSurface
twice?
SDL_Surface *sur = SDL_GetWindowSurface(window);
SDL_Log("%i x %i", sur->w, sur->h);
Apparently emscripten is part of SDL3.
Therefore you can expect full support with SDL3.
Emscripten is also supported in SDL2.
Orbit Controls and Bullet Physics in the browser
Tutorial: How to add shaders and shader problem files to the CMake project of sdl3-sample
- Install the
sdl3-sample
template - Convert SDL rendering to OpenGL rendering
- Create two shader files:
color.vert
andcolor.frag
in thesdl3-sample/assets/shaders
folder - Open the
sdl3-sample/CMakeLists.txt
file and add this code:
if(CMAKE_SYSTEM_NAME MATCHES Emscripten)
set(CMAKE_EXECUTABLE_SUFFIX ".html" CACHE INTERNAL "")
target_link_options("${EXECUTABLE_NAME}" PRIVATE "SHELL:--embed-file ${CMAKE_CURRENT_SOURCE_DIR}/assets/shaders/color.vert@/assets/shaders/color.vert")
set_property(TARGET "${EXECUTABLE_NAME}" APPEND PROPERTY LINK_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/assets/shaders/color.vert")
target_link_options("${EXECUTABLE_NAME}" PRIVATE "SHELL:--embed-file ${CMAKE_CURRENT_SOURCE_DIR}/assets/shaders/color.frag@/assets/shaders/color.frag")
set_property(TARGET "${EXECUTABLE_NAME}" APPEND PROPERTY LINK_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/assets/shaders/color.frag")
endif()
- Create the
sdl3-sample/src/shader_program.cpp
andshader_probram.cpp
files inside of thesdl3-sample/src
folder - Add the
sdl3-sample/src/shader_program.cpp
line to thesdl3-sample/CMakeLists.txt
file:
target_sources(${EXECUTABLE_NAME}
PRIVATE
src/main.cpp
src/iosLaunchScreen.storyboard
src/Sample.appxManifest
src/shader_program.cpp
)
- Copy the next content to the files:
assets/shaders/color.vert
attribute vec3 aPosition;
uniform mat4 uMvpMatrix;
void main()
{
gl_Position = uMvpMatrix * vec4(aPosition, 1.0);
}
assets/shaders/color.frag
#ifdef GL_ES
precision mediump float;
#endif
uniform vec3 uColor;
void main()
{
gl_FragColor = vec4(uColor, 1.0);
}
src/shader_program.h
#ifndef SHADER_PROGRAM_H
#define SHADER_PROGRAM_H
#include <SDL3/SDL_opengles2.h>
#include <string>
// Helper function for creating shaders
GLuint createShader(const char *shaderSource, int shaderType);
// Helper function for creating a shader program
GLuint createShaderProgram(const std::string vShaderPath, const std::string fShaderPath);
#endif // SHADER_PROGRAM_H
src/shader_program.cpp
#include "shader_program.h"
#include <iostream>
#include <fstream>
#include <iostream>
#include <sstream>
// Helper function for creating shaders
GLuint createShader(const char *shaderSource, int shaderType)
{
GLuint shader = glCreateShader(shaderType);
glShaderSource(shader, 1, &shaderSource, NULL);
glCompileShader(shader);
GLint status;
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE)
{
GLint maxLength = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
std::vector<GLchar> errorLog(maxLength);
glGetShaderInfoLog(shader, maxLength, &maxLength, &errorLog[0]);
glDeleteShader(shader); // Don't leak the shader
std::cout << &(errorLog[0]) << std::endl;
std::cout << shaderSource << std::endl;
}
return shader;
}
// Helper function for creating a shader program
GLuint createShaderProgram(const std::string vShaderPath,
const std::string fShaderPath)
{
std::ifstream vf(vShaderPath);
if (!vf.is_open())
{
std::cout << "Failed to open the file: " << vShaderPath << std::endl;
return 0;
}
std::stringstream vsstr;
vf >> vsstr.rdbuf();
std::string vContent = vsstr.str();
vf.close();
std::ifstream ff(fShaderPath);
if (!ff.is_open())
{
std::cout << "Failed to open the file: " << fShaderPath << std::endl;
return 0;
}
std::stringstream fsstr;
ff >> fsstr.rdbuf();
std::string fContent = fsstr.str();
ff.close();
GLuint program = glCreateProgram();
GLuint vShader = createShader(vContent.c_str(), GL_VERTEX_SHADER);
GLuint fShader = createShader(fContent.c_str(), GL_FRAGMENT_SHADER);
glAttachShader(program, vShader);
glAttachShader(program, fShader);
glLinkProgram(program);
glUseProgram(program);
return program;
}
First person movement in the browser
Adding libraries to SDL3 project
I have moved libs to the separate folder. It requires to add 154 files for Bullet Physics:
target_include_directories(${EXECUTABLE_NAME} PRIVATE src/libs/bullet-physics-3.25)
target_include_directories(${EXECUTABLE_NAME} PRIVATE src/libs/glm-1.0.1-light)
target_include_directories(${EXECUTABLE_NAME} PRIVATE src/libs/tinyxml2-10.0.0)
target_sources(${EXECUTABLE_NAME}
PRIVATE
src/main.cpp
src/iosLaunchScreen.storyboard
src/Sample.appxManifest
src/collider.cpp
src/debug_drawer.cpp
src/orbit_controls.cpp
src/lightless.cpp
src/math_helper.cpp
src/shader_program.cpp
src/vertex_buffers.cpp
src/libs/tinyxml2-10.0.0/tinyxml2.cpp
src/libs/bullet-physics-3.25/BulletCollision/BroadphaseCollision/btAxisSweep3.cpp
src/libs/bullet-physics-3.25/BulletCollision/BroadphaseCollision/btBroadphaseProxy.cpp
src/libs/bullet-physics-3.25/BulletCollision/BroadphaseCollision/btCollisionAlgorithm.cpp
Table of contents:
- Tutorials: How to install SDL3 for Android and WebAssembly
- Tutorials: How to set up OpenGL ES for Android and WebAssembly
- Demo: Platformer demo using Qt WebAssembly and Box2D
- Example: Mouse click event handler using Emscripten
- Example: Mouse click event handler using SDL3
- Tutorial: Loading textures for WASM
- Example: Simple triangle using OpenGL ES 2.0 for WASM
- Example: Simple texture drawing using OpenGL ES 2.0 for WASM
- Example: Set up GLM for vector and matrix operations for WASM
- Tutorial: How to copy a configured project for WebAssembly
- Example: Universal key code that doesn’t depend on the system keyboard layout
- Example: Handling resizing of the canvas using SDL3
- Demo: Orbit Controls and Bullet Physics in the browser
- Tutorial: How to add shaders and shader problem files to the CMake project of sdl3-sample
- Demo: First person movement in the browser
- Tutorial: Adding libraries to SDL3 project
My example above doesn’t work on Glitch. I have created the following topic: Server doesn't print any messages to the console - Glitch Help - Glitch Community Forum
Are you gonna make this demo open source?
Is the code available for viewing somewhere?
If you mean the WebSocket demo I sell a source code of this demo for $1. Maybe I will make a step by step tutorial for this demo later and source code will be available on my website for free.
No, I mean the bullet physics demo. I assume it’s the same condition as the web socket demo.
I can help to install Bullet Physics for WASM and CMake for $1 per hour by talking in Discord in private. My spoken English is terrible but I can explain with primitive phrases “what to type” and “what to copy”. My native language is Russian. I want to make a step by step tutorial later how to install Bullet Physics and Box2D with Emscripten. Maybe I will make a few tutorials later about the first person movement using Bullet Physics.
Demo: Play a stereo sound by mouse click using OpenAL and Emscripten
Enabling antialiasing in the OpenGL application
The following code must be placed before SDL_GL_CreateContext(window)
:
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); // Enable MULTISAMPLE
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 2); // Can be 2, 4, 8 or 16
Stack Overflow: How to activate anti-aliasing via Emscripten
Without antialiasing:
With antialiasing (MULTISAMPLESAMPLES = 2):
Tutorial: How to call a C++ function from JavaScript
- Add
extern "C"
as the docs says:
main.cpp
extern "C"
{
void sayHello()
{
std::cout << "hello" << std::endl;
}
}
If you don’t add this, it will be an error: em++: error: undefined exported symbol: "_sayHello"
- Add this code to the
CMakeLists.txt
file:
target_link_options(${EXECUTABLE_NAME} PRIVATE
-sEXPORTED_RUNTIME_METHODS=["cwrap"])
target_link_options(${EXECUTABLE_NAME} PRIVATE
-sEXPORTED_FUNCTIONS=["_sayHello"])
- Call your function in JavaScript:
Module["onRuntimeInitialized"] = function() {
const sayHello = Module.cwrap("sayHello", null, []);
sayHello();
};
Tutorial: Code that may be useful to create a progress bar
- Add this code to
CMakeLists.txt
:
target_link_options(${EXECUTABLE_NAME} PRIVATE
-sEXPORTED_RUNTIME_METHODS=["cwrap"])
target_link_options(${EXECUTABLE_NAME} PRIVATE
-sEXPORTED_FUNCTIONS=["_calc"])
- Add this code to
index.js
:
function progressBar(val) {
console.log(val);
}
Module["onRuntimeInitialized"] = function() {
const calc = Module.cwrap("calc", null, []);
calc();
};
- Add this code to
main.cpp
:
#include <emscripten.h>
void calcfunction(void *)
{
std::cout << "calc something" << std::endl;
}
extern "C"
{
void calc()
{
EM_ASM({ progressBar($0); }, 10);
emscripten_async_call(calcfunction, NULL, 0);
}
}
or use this:
extern "C"
{
void calc()
{
EM_ASM({ progressBar($0); }, 10);
emscripten_async_call(+[](void *){
std::cout << "calc something" << std::endl;
}, NULL, 0);
}
}
Pick a colored and textured 2D object
Tools: SDL3, Emscripten, CMake, OpenGL ES 2.0, GLM, and stb_image