diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8db3685..0a86659 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,6 +6,8 @@ target_sources( ${PROJECT_NAME} register_types.h steam_audio.cpp steam_audio.h + steam_audio_scene.cpp + steam_audio_scene.h ) target_include_directories( ${PROJECT_NAME} diff --git a/src/register_types.cpp b/src/register_types.cpp index 4827a6d..c8d730f 100644 --- a/src/register_types.cpp +++ b/src/register_types.cpp @@ -1,6 +1,8 @@ // src/register_types.cpp #include "register_types.h" #include "steam_audio.h" // your SteamAudio class declaration +#include "steam_audio_scene.h" + using namespace godot; @@ -8,13 +10,16 @@ void initialize_steam_audio(ModuleInitializationLevel p_level) { if (p_level!=MODULE_INITIALIZATION_LEVEL_SERVERS) { return; } + /*if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) { + GDREGISTER_CLASS(SteamAudioEditorPlugin); + }*/ GDREGISTER_CLASS(SteamAudio); + GDREGISTER_CLASS(SteamAudioScene); } void uninitialize_steam_audio(ModuleInitializationLevel p_level) { if (p_level!=MODULE_INITIALIZATION_LEVEL_SERVERS) { return; } - } extern "C" { diff --git a/src/steam_audio.cpp b/src/steam_audio.cpp index 62a4774..0e38fac 100644 --- a/src/steam_audio.cpp +++ b/src/steam_audio.cpp @@ -13,6 +13,10 @@ using namespace godot; iplContextRelease(&context); context=nullptr; } + if (embree_device) { + iplEmbreeDeviceRelease(&embree_device); + embree_device=nullptr; + } } void SteamAudio::_bind_methods() { ClassDB::bind_method(D_METHOD("initialize"), &SteamAudio::initialize); @@ -30,5 +34,10 @@ bool SteamAudio::initialize() UtilityFunctions::printerr("SteamAudio::initialize()", err); return false; } + IPLEmbreeDeviceSettings emb_device_settings{}; + if (iplEmbreeDeviceCreate(context, &emb_device_settings, &embree_device) != IPL_STATUS_SUCCESS) { + UtilityFunctions::printerr("SteamAudio::initialize(): failed to create Embree device"); + return false; + } return true; } \ No newline at end of file diff --git a/src/steam_audio.h b/src/steam_audio.h index 2a12657..e4c14d8 100644 --- a/src/steam_audio.h +++ b/src/steam_audio.h @@ -16,12 +16,16 @@ class SteamAudio : public RefCounted { GDCLASS( SteamAudio,RefCounted ) // NOLINT(readability-use-auto) - private: - IPLContext context = nullptr; - protected: - static void _bind_methods(); - public: - SteamAudio(); - ~SteamAudio() override; +private: + IPLContext context = nullptr; + IPLEmbreeDevice embree_device = nullptr; +protected: + static void _bind_methods(); +public: + SteamAudio(); + ~SteamAudio() override; bool initialize(); + + [[nodiscard]] IPLContext getContext() const{return context;} + [[nodiscard]] IPLEmbreeDevice getEmbreeDevice() const{return embree_device;} }; \ No newline at end of file diff --git a/src/steam_audio_listener.cpp b/src/steam_audio_listener.cpp new file mode 100644 index 0000000..f021d0e --- /dev/null +++ b/src/steam_audio_listener.cpp @@ -0,0 +1,32 @@ +// +// Created by bryce on 5/8/2025. +// + +#include "steam_audio_listener.h" + +using namespace godot; + +void SteamAudioListener::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_context", "ctx"), &SteamAudioListener::set_context); + ClassDB::bind_method(D_METHOD("_process","delta"), &SteamAudioListener::_process); + + ADD_PROPERTY( + PropertyInfo(Variant::OBJECT, "context", PROPERTY_HINT_RESOURCE_TYPE, "SteamAudio"), + "set_context", "" + ); +} + +void SteamAudioListener::set_context(const Ref &p_ctx) { + ERR_FAIL_COND(!p_ctx.is_valid()); + ctx = p_ctx; +} + +void SteamAudioListener::_process(double delta) { + ERR_FAIL_COND(!ctx.is_valid()); + Transform3D transform = get_global_transform(); + IPLCoordinateSpace3 ls{}; + ls.origin = {transform.origin.x, transform.origin.y, transform.origin.z}; + Vector3 fwd = transform.basis.xform(Vector3(0,0,-1)), upv = transform.basis.xform(Vector3(0,1,0)); + ls.ahead = {fwd.x, fwd.y, fwd.z}; + ls.up = {upv.x, upv.y, upv.z}; +} diff --git a/src/steam_audio_listener.h b/src/steam_audio_listener.h new file mode 100644 index 0000000..9c3fd1e --- /dev/null +++ b/src/steam_audio_listener.h @@ -0,0 +1,22 @@ +// +// Created by bryce on 5/8/2025. +// + +#pragma once +#include +#include +#include +#include "steam_audio.h" +#include + +using namespace godot; + +class SteamAudioListener : public AudioListener3D { + GDCLASS(SteamAudioListener,AudioListener3D) + Ref ctx; +protected: + static void _bind_methods(); +public: + void set_context(const Ref &p_ctx); + void _process(double delta) override; +}; \ No newline at end of file diff --git a/src/steam_audio_scene.cpp b/src/steam_audio_scene.cpp new file mode 100644 index 0000000..ee04e66 --- /dev/null +++ b/src/steam_audio_scene.cpp @@ -0,0 +1,95 @@ +// +// Created by bryce on 5/8/2025. +// + +#include "steam_audio_scene.h" + +using namespace godot; + + SteamAudioScene::SteamAudioScene() = default; + SteamAudioScene::~SteamAudioScene() { + if (scene) { + iplSceneRelease(&scene); + } + } + +void SteamAudioScene::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_context","ctx"), &SteamAudioScene::set_context); + ClassDB::bind_method(D_METHOD("add_mesh","mesh_node"), &SteamAudioScene::add_mesh); + ClassDB::bind_method(D_METHOD("commit"), &SteamAudioScene::commit); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT,"context",PROPERTY_HINT_RESOURCE_TYPE,"SteamAudio"), "set_context",""); + } + +void SteamAudioScene::set_context(const Ref &p_ctx) { + ERR_FAIL_COND(!p_ctx.is_valid()); + ctx = p_ctx; + if (scene) { + iplSceneRelease(&scene); + scene = nullptr; + } + IPLSceneSettings settings{}; + + settings.type = ctx-> getEmbreeDevice() ? IPL_SCENETYPE_EMBREE : IPL_SCENETYPE_DEFAULT; + settings.embreeDevice = ctx-> getEmbreeDevice(); + iplSceneCreate(ctx->getContext(), &settings, &scene); + } + +void SteamAudioScene::commit() const { + ERR_FAIL_COND(!scene); + ERR_FAIL_COND(!ctx.is_valid()); + iplSceneCommit(scene) ; + } +void SteamAudioScene::add_mesh(MeshInstance3D *mesh_node) { + ERR_FAIL_COND(!ctx.is_valid()); + ERR_FAIL_COND(!scene); + ERR_FAIL_COND(!mesh_node); + + // 1) Fetch the ArrayMesh from the MeshInstance3D + Ref mesh = mesh_node->get_mesh(); + ERR_FAIL_COND(!mesh.is_valid()); + + ArrayMesh *raw_am = Object::cast_to(mesh.ptr()); + ERR_FAIL_COND(!raw_am); + Ref am(raw_am); + ERR_FAIL_COND(!am.is_valid()); + + // 2) Pull out the raw arrays + Array arrays = am->surface_get_arrays(0); + PackedVector3Array verts = arrays[ArrayMesh::ARRAY_VERTEX]; + PackedInt32Array idxs = arrays[ArrayMesh::ARRAY_INDEX]; + + int vertex_count = verts.size(); + int index_count = idxs.size(); + int triangle_count = index_count / 3; + + // 3) Build Steam Audio’s settings struct + IPLStaticMeshSettings sm{}; + sm.numVertices = vertex_count; + sm.numTriangles = triangle_count; + sm.numMaterials = 0; + sm.vertices = reinterpret_cast( + const_cast(verts.ptr()) + ); + sm.materialIndices = nullptr; + sm.materials = nullptr; + + // 4) Convert the flat index list into IPLTriangle[] + IPLTriangle *tris = static_cast( + malloc(sizeof(IPLTriangle) * triangle_count) + ); + for (int i = 0; i < triangle_count; ++i) { + tris[i].indices[0] = idxs.ptr()[3*i + 0]; + tris[i].indices[1] = idxs.ptr()[3*i + 1]; + tris[i].indices[2] = idxs.ptr()[3*i + 2]; + } + sm.triangles = tris; + + // 5) Create & add the static mesh to the scene + IPLStaticMesh static_mesh = nullptr; + iplStaticMeshCreate(scene, &sm, &static_mesh); + iplStaticMeshAdd (static_mesh, scene); + iplStaticMeshRelease(&static_mesh); + + // 6) Clean up our temporary triangle array + ::free(tris); + } \ No newline at end of file diff --git a/src/steam_audio_scene.h b/src/steam_audio_scene.h new file mode 100644 index 0000000..9f79d20 --- /dev/null +++ b/src/steam_audio_scene.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include "godot_cpp/godot.hpp" +#include "phonon.h" +#include "steam_audio.h" + +using namespace godot; + +class SteamAudioScene : public RefCounted { + GDCLASS(SteamAudioScene, RefCounted); + + IPLScene scene = nullptr; + Ref ctx; + +protected: + static void _bind_methods(); +public: + SteamAudioScene(); + ~SteamAudioScene() override; + + void set_context(const Ref &p_ctx); + + void add_mesh(MeshInstance3D *mesh_node); + + void commit() const; +}; \ No newline at end of file diff --git a/src/steam_audio_source.cpp b/src/steam_audio_source.cpp new file mode 100644 index 0000000..0b5b688 --- /dev/null +++ b/src/steam_audio_source.cpp @@ -0,0 +1,30 @@ +// +// Created by bryce on 5/9/2025. +// + +#include "steam_audio_source.h" + +#include "steam_audio.h" + +using namespace godot; + + SteamAudioSource::SteamAudioSource() { + +} + SteamAudioSource::~SteamAudioSource() { + if (steam_audio_source) { + iplSourceRelease(&steam_audio_source); + steam_audio_source = nullptr; + } +} + +void SteamAudioSource::_bind_methods() { + ClassDB::bind_method(D_METHOD("play"), &SteamAudioSource::play); + ClassDB::bind_method(D_METHOD("stop"), &SteamAudioSource::stop); + ClassDB::bind_method(D_METHOD("set_stream","stream"), &SteamAudioSource::set_stream); + } + +void SteamAudioSource::play() { + audio_player = memnew(AudioStreamPlayer3D); + add_child(audio_player); + } \ No newline at end of file diff --git a/src/steam_audio_source.h b/src/steam_audio_source.h new file mode 100644 index 0000000..0cadc58 --- /dev/null +++ b/src/steam_audio_source.h @@ -0,0 +1,32 @@ +// +// Created by bryce on 5/9/2025. +// + +#pragma once +#include +#include + +#include "phonon.h" + +using namespace godot; + +class SteamAudioSource:Node3D { + GDCLASS(SteamAudioSource, Node3D) +private: + AudioStreamPlayer3D* audio_player = nullptr; + IPLSource steam_audio_source = nullptr; + + bool initialized = false; +public: + SteamAudioSource(); + ~SteamAudioSource(); + + void _ready() override; + void _process(double p_delta) override; + + void play(); + void stop(); + void set_stream(const Ref& p_stream); +protected: + static void _bind_methods(); +}; \ No newline at end of file