My step by step guides for SDL3 to build for Android and WebAssembly

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)

2 Likes

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);
1 Like

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

Demo

image

1 Like

Tutorial: How to add shaders and shader problem files to the CMake project of sdl3-sample

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 and shader_probram.cpp files inside of the sdl3-sample/src folder
  • Add the sdl3-sample/src/shader_program.cpp line to the sdl3-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

  • Demo in C++, SDL3, OpenGL ES 2.0, and Bullet Physics
  • Demo in JavaScript, pure WebGL 1.0, and Ammo.js

image

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

image

Table of contents:

Demo: My first WebSocket app was deployed on the free Render hosting

Demo

image

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

Demo

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:

image

With antialiasing (MULTISAMPLESAMPLES = 2):

image

Tutorial: How to call a C++ function from JavaScript

  1. 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"

  1. 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"])
  1. Call your function in JavaScript:
Module["onRuntimeInitialized"] = function() {

    const sayHello = Module.cwrap("sayHello", null, []);
    sayHello();

};

My answer on Stack Overflow

Tutorial: Code that may be useful to create a progress bar

  1. 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"])
  1. Add this code to index.js:
function progressBar(val) {
   console.log(val);
}

Module["onRuntimeInitialized"] = function() {

    const calc = Module.cwrap("calc", null, []);
    calc();

};
  1. 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);
    }
}

My answer on Stack Overflow