Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions .github/actions/create-native-build/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,32 @@ runs:
mv api/ windows
cd ../../

- name: Installing FMOD on Linux & Android
- name: Installing FMOD on Linux, Android & Web
if: runner.os == 'Linux'
shell: ${{ inputs.shell }}
run: |
cd ..
mkdir libs && cd libs
mkdir fmod && cd fmod
python ../../fmod-gdextension/get_fmod.py ${{inputs.fmod-user}} ${{inputs.fmod-password}} ${{inputs.platform}} ${{env.FMOD_VERSION}}
tar -xvf fmodstudioapi${{env.FMOD_VERSION}}${{inputs.fmod-executable-suffix}}
mv fmodstudioapi${{env.FMOD_VERSION}}${{inputs.platform}}/api ${{inputs.platform}}
PLATFORM_FOR_FMOD=${{ inputs.platform }}
if [ "$PLATFORM_FOR_FMOD" = "web" ]; then
PLATFORM_FOR_FMOD="html5"
fi
python ../../fmod-gdextension/get_fmod.py ${{inputs.fmod-user}} ${{inputs.fmod-password}} $PLATFORM_FOR_FMOD ${{env.FMOD_VERSION}}
if [ "$PLATFORM_FOR_FMOD" = "html5" ]; then
unzip fmodstudioapi${{env.FMOD_VERSION}}${{inputs.fmod-executable-suffix}}
else
tar -xvf fmodstudioapi${{env.FMOD_VERSION}}${{inputs.fmod-executable-suffix}}
fi
FMOD_EXTRACT_DIR=$(find . -maxdepth 3 -type d -name "fmodstudioapi${{env.FMOD_VERSION}}${PLATFORM_FOR_FMOD}" | head -n 1)
if [ -z "$FMOD_EXTRACT_DIR" ]; then
echo "FMOD archive directory not found" && exit 1
fi
if [ "${{ inputs.platform }}" = "web" ]; then
mv "$FMOD_EXTRACT_DIR/api" .
else
mv "$FMOD_EXTRACT_DIR/api" ${{inputs.platform}}
fi
cd ../../

- name: Installing FMOD on MacOS & iOS
Expand Down
23 changes: 22 additions & 1 deletion .github/workflows/check_pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ env:
NDK_VERSION: 27.3.13750724
TARGET_PATH: demo/addons/fmod/libs/
TARGET_NAME: libGodotFmod
FMOD_VERSION: 20306
FMOD_VERSION: 20311
jobs:
gate:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -152,6 +152,20 @@ jobs:
fmod-executable-suffix: ios.dmg
shell: bash

- name: Web Debug Compilation
os: "ubuntu-22.04"
platform: web
target: template_debug
fmod-executable-suffix: html5.zip
shell: bash

- name: Web Release Compilation
os: "ubuntu-22.04"
platform: web
target: template_release
fmod-executable-suffix: html5.zip
shell: bash

steps:
- name: Checkout (in-repo PR)
if: github.event_name == 'pull_request'
Expand All @@ -176,6 +190,13 @@ jobs:
ndk-version: r23c
link-to-sdk: true

- name: Install Emscripten SDK
if: matrix.platform == 'web'
uses: mymindstorm/setup-emsdk@v14
with:
version: 3.1.56
actions-cache-key: emsdk-cache

- name: Compile native plugin
uses: ./.github/actions/create-native-build
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ env:
NDK_VERSION: 27.3.13750724
TARGET_PATH: demo/addons/fmod/libs/
TARGET_NAME: libGodotFmod
FMOD_VERSION: 20306
FMOD_VERSION: 20311
jobs:
build:
name: ${{ matrix.name }}
Expand Down
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,6 @@ config.log
demo/addons/fmod/libs/android/aar/

*.pdb
.vs
.vs

*.wasm
28 changes: 25 additions & 3 deletions SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ target_path = ARGUMENTS.pop("target_path", "demo/addons/fmod/libs/")
target_name = ARGUMENTS.pop("target_name", "libGodotFmod")
fmod_lib_dir = ARGUMENTS.pop("fmod_lib_dir", "../libs/fmod/")

# Although FMOD's WebAssembly libraries are built with pthreads / shared memory support, Godot doesn't yet like it.
# For web builds, default to single-threaded godot-cpp (unless the user explicitly overrides it).
platform_arg = ARGUMENTS.get("platform")
if platform_arg == "web" and "threads" not in ARGUMENTS:
ARGUMENTS["threads"] = "no"

env = SConscript("godot-cpp/SConstruct")

# Add those directory manually, so we can skip the godot_cpp directory when including headers in C++ files
Expand Down Expand Up @@ -122,6 +128,21 @@ elif env["platform"] == "android":
env.Append(LIBPATH=[env['fmod_lib_dir'] + 'android/core/lib/' + arch_dir, env['fmod_lib_dir'] + 'android/studio/lib/' + arch_dir])
env.Append(LIBS=[libfmod, libfmodstudio])

elif env["platform"] == "web":
html_lib = os.path.join(fmod_lib_dir, 'api/studio/lib/w32/')
html_inc = os.path.join(fmod_lib_dir, 'api/studio/inc/')

html_core_lib = os.path.join(fmod_lib_dir, 'api/core/lib/w32/')
html_core_inc = os.path.join(fmod_lib_dir, 'api/core/inc/')

libfmodstudio_path = os.path.join(html_lib, 'fmodstudio%s_wasm.a' % lfix)

env.Append(CPPPATH=[html_inc, html_core_inc])
env.Append(LIBPATH=[html_lib])
# env.Append(LIBS=[libfmodstudio]) # ← REMOVE or comment this out
env.Append(LINKFLAGS=[libfmodstudio_path])
env.Append(LINKFLAGS=["-sEXPORTED_RUNTIME_METHODS=ccall,cwrap,setValue,getValue"])

#Output is placed in the addons directory of the demo project directly
target = "{}{}/{}.{}.{}".format(
target_path, env["platform"], target_name, env["platform"], env["target"]
Expand Down Expand Up @@ -200,8 +221,9 @@ def copy_fmod_libraries(self, arg, env, executor = None):
source_files = [env.Glob(os.path.join(source_dir, '*.*')) for source_dir in [fmod_core_lib_dir, fmod_studio_lib_dir]]
[[shutil.copy(str(file), addon_fmod_libs_output) for file in files] for files in source_files]


copy_fmod_libraries_action = Action('', copy_fmod_libraries)
AddPostAction(library, copy_fmod_libraries_action)
# web bundles everything inside the final .wasm - no need to export libs
if env["platform"] != "web":
copy_fmod_libraries_action = Action('', copy_fmod_libraries)
AddPostAction(library, copy_fmod_libraries_action)

Default(library)
3 changes: 3 additions & 0 deletions demo/addons/fmod/fmod.gdextension
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ android.debug.arm64 = "res://addons/fmod/libs/android/arm64/libGodotFmod.android
android.release.arm64 = "res://addons/fmod/libs/android/arm64/libGodotFmod.android.template_release.arm64.so"
ios.debug = "res://addons/fmod/libs/ios/libGodotFmod.ios.template_debug.xcframework"
ios.release = "res://addons/fmod/libs/ios/libGodotFmod.ios.template_release.xcframework"
web.editor = "res://addons/fmod/libs/web/libGodotFmod.web.editor.wasm32.wasm"
web.debug = "res://addons/fmod/libs/web/libGodotFmod.web.template_debug.wasm32.wasm"
web.release = "res://addons/fmod/libs/web/libGodotFmod.web.template_release.wasm32.wasm"

[icons]
FmodEventEmitter2D = "res://addons/fmod/icons/fmod_icon.svg"
Expand Down
51 changes: 48 additions & 3 deletions demo/export_presets.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ package/signed=true
package/app_category=2
package/retain_data_on_uninstall=false
package/exclude_from_recents=false
package/show_in_android_tv=false
package/show_in_app_library=true
package/show_as_launcher_app=false
launcher_icons/main_192x192=""
launcher_icons/adaptive_foreground_432x432=""
launcher_icons/adaptive_background_432x432=""
graphics/opengl_debug=false
xr_features/xr_mode=0
xr_features/hand_tracking=0
xr_features/hand_tracking_frequency=0
xr_features/passthrough=0
screen/immersive_mode=true
screen/support_small=true
screen/support_normal=true
Expand Down Expand Up @@ -200,6 +200,9 @@ permissions/write_sms=false
permissions/write_social_stream=false
permissions/write_sync_settings=false
permissions/write_user_dictionary=false
xr_features/hand_tracking=0
xr_features/hand_tracking_frequency=0
xr_features/passthrough=0
graphics/32_bits_framebuffer=true
xr_features/degrees_of_freedom=0
one_click_deploy/clear_previous_install=false
Expand Down Expand Up @@ -251,6 +254,7 @@ application/product_name=""
application/file_description=""
application/copyright=""
application/trademarks=""
application/export_angle=0
ssh_remote_deploy/enabled=false
ssh_remote_deploy/host="user@host_ip"
ssh_remote_deploy/port="22"
Expand Down Expand Up @@ -304,6 +308,7 @@ application/short_version="1.0"
application/version="1.0"
application/icon_interpolation=4
application/launch_screens_interpolation=4
application/export_project_only=false
capabilities/access_wifi=false
capabilities/push_notifications=false
user_data/accessible_from_files_app=false
Expand Down Expand Up @@ -398,6 +403,7 @@ application/version="1.0"
application/copyright=""
application/copyright_localized={}
application/min_macos_version="10.12"
application/export_angle=0
display/high_res=true
xcode/platform_build="14C18"
xcode/sdk_version="13.1"
Expand Down Expand Up @@ -431,6 +437,7 @@ codesign/entitlements/app_sandbox/files_downloads=0
codesign/entitlements/app_sandbox/files_pictures=0
codesign/entitlements/app_sandbox/files_music=0
codesign/entitlements/app_sandbox/files_movies=0
codesign/entitlements/app_sandbox/files_user_selected=0
codesign/entitlements/app_sandbox/helper_executables=[]
codesign/custom_options=PackedStringArray()
notarization/notarization=0
Expand Down Expand Up @@ -467,3 +474,41 @@ open \"{temp_dir}/{exe_name}.app\" --args {cmd_args}"
ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash
kill $(pgrep -x -f \"{temp_dir}/{exe_name}.app/Contents/MacOS/{exe_name} {cmd_args}\")
rm -rf \"{temp_dir}\""

[preset.4]

name="Web"
platform="Web"
runnable=true
dedicated_server=false
custom_features=""
export_filter="all_resources"
include_filter=""
exclude_filter=""
export_path="../../../../Desktop/exp/test/index.html"
encryption_include_filters=""
encryption_exclude_filters=""
encrypt_pck=false
encrypt_directory=false

[preset.4.options]

custom_template/debug=""
custom_template/release=""
variant/extensions_support=true
vram_texture_compression/for_desktop=true
vram_texture_compression/for_mobile=false
html/export_icon=true
html/custom_html_shell=""
html/head_include=""
html/canvas_resize_policy=2
html/focus_canvas_on_start=true
html/experimental_virtual_keyboard=false
progressive_web_app/enabled=false
progressive_web_app/offline_page=""
progressive_web_app/display=1
progressive_web_app/orientation=0
progressive_web_app/icon_144x144=""
progressive_web_app/icon_180x180=""
progressive_web_app/icon_512x512=""
progressive_web_app/background_color=Color(0, 0, 0, 1)
4 changes: 2 additions & 2 deletions demo/test/unit/test_listener.gd
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@ class TestListener:
func test_assert_should_have_proper_weight():
var desiredValue: float = 1.0
assert_listener_weight(0, desiredValue)
desiredValue = 2.0
desiredValue = 0.5
FmodServer.set_listener_weight(0, desiredValue)
assert_listener_weight(0, desiredValue)
desiredValue = 1.0
FmodServer.set_listener_weight(0, desiredValue)
FmodServer.set_listener_number(2)
desiredValue = 2.0
desiredValue = 0.5
FmodServer.add_listener(1, sprite)
FmodServer.set_listener_weight(1, desiredValue)
assert_listener_weight(1, desiredValue)
Expand Down
4 changes: 4 additions & 0 deletions get_fmod.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
# iOS...
filename = f'fmodstudioapi{fmod_version}ios.dmg'
downloadlink = f'https://www.fmod.com/api-get-download-link?path=files/fmodstudio/api/iOS/&filename=fmodstudioapi{fmod_version}ios-installer.dmg&user=$1'
elif platform == 'html5':
# HTML5
filename = f'fmodstudioapi{fmod_version}html5.zip'
downloadlink = f'https://www.fmod.com/api-get-download-link?path=files/fmodstudio/api/HTML5/&filename=fmodstudioapi{fmod_version}html5.zip&user='

downloadlink += user

Expand Down
59 changes: 57 additions & 2 deletions src/callback/file_callbacks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ namespace Callbacks {
}

void GodotFileRunner::queueReadRequest(FMOD_ASYNCREADINFO* request, ReadPriority priority) {
#if !defined(WEB_ENABLED)
// High-priority requests have to be processed first.
if (priority == ReadPriority::HIGH) {
// lock so we can't add and remove elements from the queue at the same time.
Expand All @@ -19,9 +20,16 @@ namespace Callbacks {
requests.push_back(request);
}
read_cv.notify_one();
#else
// On Web, threaded async IO is disabled. These callbacks should not be
// used because we don't register a custom file system in that build.
(void)request;
(void)priority;
#endif
}

FMOD_RESULT GodotFileRunner::cancelReadRequest(FMOD_ASYNCREADINFO* request) {
#if !defined(WEB_ENABLED)
// lock so we can't add and remove elements from the queue at the same time.
{
std::lock_guard<std::mutex> lk(read_mut);
Expand All @@ -40,8 +48,14 @@ namespace Callbacks {
}

return FMOD_RESULT::FMOD_OK;
#else
(void)request;
return FMOD_RESULT::FMOD_OK;
#endif
}

// The worker thread entry point exists only on non-web platforms.
#if !defined(WEB_ENABLED)
void GodotFileRunner::run() {
while (!stop) {
// waiting for the container to have one request
Expand Down Expand Up @@ -88,17 +102,23 @@ namespace Callbacks {
}
}
}

#endif
void GodotFileRunner::start() {
#if !defined(WEB_ENABLED)
stop = false;
fileThread = std::thread(&GodotFileRunner::run, this);
#endif
}

void GodotFileRunner::finish() {
#if !defined(WEB_ENABLED)
stop = true;
// we need to notify the loop one last time, otherwise it will stay stuck in the wait method.
read_cv.notify_one();
fileThread.join();
if (fileThread.joinable()) {
fileThread.join();
}
#endif
}

FMOD_RESULT F_CALL godotFileOpen(const char* name, unsigned int* filesize, void** handle, void* userdata) {
Expand All @@ -119,6 +139,41 @@ namespace Callbacks {
return FMOD_RESULT::FMOD_OK;
}

FMOD_RESULT F_CALL godotFileRead(void* handle, void* buffer, unsigned int sizebytes, unsigned int* bytesread, void* userdata) {
GodotFileHandle* fileHandle {reinterpret_cast<GodotFileHandle*>(handle)};
godot::Ref<godot::FileAccess> file {fileHandle->file};

godot::PackedByteArray data = file->get_buffer(sizebytes);
const int read_size = static_cast<int>(data.size());

if (read_size > 0) {
memcpy(buffer, data.ptr(), read_size);
}

if (bytesread) {
*bytesread = static_cast<unsigned int>(read_size);
}

if (read_size < static_cast<int>(sizebytes)) {
return FMOD_RESULT::FMOD_ERR_FILE_EOF;
}

return FMOD_RESULT::FMOD_OK;
}

FMOD_RESULT F_CALL godotFileSeek(void* handle, unsigned int pos, void* userdata) {
GodotFileHandle* fileHandle {reinterpret_cast<GodotFileHandle*>(handle)};
godot::Ref<godot::FileAccess> file {fileHandle->file};

file->seek(pos);

if (file->get_position() != pos) {
return FMOD_RESULT::FMOD_ERR_FILE_COULDNOTSEEK;
}

return FMOD_RESULT::FMOD_OK;
}

FMOD_RESULT F_CALL godotSyncRead(FMOD_ASYNCREADINFO* info, void* userdata) {
GodotFileRunner* runner {GodotFileRunner::get_singleton()};
int priority {info->priority};
Expand Down
Loading
Loading