more stuffs

This commit is contained in:
Nyx
2025-05-12 22:18:09 -06:00
parent 6dd1337c4c
commit a79a7551b3
20 changed files with 936 additions and 281 deletions

View File

@@ -6,8 +6,16 @@ target_sources( ${PROJECT_NAME}
register_types.h
steam_audio.cpp
steam_audio.h
steam_audio_scene.cpp
steam_audio_scene.h
steam_audio_material.cpp
steam_audio_material.h
steam_audio_listener.h
steam_audio_listener.cpp
steam_audio_source.cpp
steam_audio_source.h
steam_audio_dynamic_mesh.h
steam_audio_dynamic_mesh.cpp
steam_audio_static_mesh.cpp
steam_audio_static_mesh.h
)
target_include_directories( ${PROJECT_NAME}

View File

@@ -1,31 +1,71 @@
// src/register_types.cpp
#include "register_types.h"
#include "steam_audio.h" // your SteamAudio class declaration
#include "steam_audio_scene.h"
#include "register_types.h"
using namespace godot;
void initialize_steam_audio(ModuleInitializationLevel p_level) {
if (p_level!=MODULE_INITIALIZATION_LEVEL_SERVERS) {
if (p_level!=MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
/*if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) {
GDREGISTER_CLASS(SteamAudioEditorPlugin);
}*/
register_steam_audio_settings();
GDREGISTER_CLASS(SteamAudio);
GDREGISTER_CLASS(SteamAudioScene);
GDREGISTER_CLASS(SteamAudioMaterial);
GDREGISTER_RUNTIME_CLASS(SteamAudioListener);
GDREGISTER_RUNTIME_CLASS(SteamAudioSource);
GDREGISTER_RUNTIME_CLASS(SteamAudioStaticMesh)
GDREGISTER_RUNTIME_CLASS(SteamAudioDynamicMesh)
}
void uninitialize_steam_audio(ModuleInitializationLevel p_level) {
if (p_level!=MODULE_INITIALIZATION_LEVEL_SERVERS) {
if (p_level!=MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
}
void register_steam_audio_settings() {
ProjectSettings *settings = ProjectSettings::get_singleton();
{//Raytracer enum
settings->set("steam_audio/ray_tracer/RayTracer",0);
Dictionary info;
info["name"] = "steam_audio/ray_tracer/RayTracer";
info["type"] = Variant::INT;
info["hint"] = PROPERTY_HINT_ENUM;
info["hint_string"] = "Steam RT,Embree RT";
info["usage"] = PROPERTY_USAGE_DEFAULT;
settings->add_property_info(info);
settings->set("steam_audio/ray_tracer/RayTracer",1);
}
{//spatialization mode enum
settings->set("steam_audio/spatializer/spatializer",0);
Dictionary info;
info["name"] = "steam_audio/spatializer/spatializer";
info["type"] = Variant::INT;
info["hint"] = PROPERTY_HINT_ENUM;
info["hint_string"] = "Panning,HRTF,Ambisonic Pan,Ambisonic Binaural";
info["usage"] = PROPERTY_USAGE_DEFAULT;
settings->add_property_info(info);
settings->set_initial_value("steam_audio/spatializer/spatializer",1);
}
{
settings->set("steam_audio/spatializer/HRTF/Volume",0);
Dictionary info;
info["name"] = "steam_audio/spatializer/HRTF/Volume";
info["type"] = Variant::FLOAT;
info["hint"] = PROPERTY_HINT_RANGE;
info["hint_string"] = "0.0,1.0,.01,slider";
info["usage"] = PROPERTY_USAGE_DEFAULT;
settings->add_property_info(info);
settings->set("steam_audio/spatializer/HRTF/Volume",1);
}
}
extern "C" {
// Initialization.
GDExtensionBool GDE_EXPORT steam_audio_library_init(GDExtensionInterfaceGetProcAddress p_get_proc_address, const GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) {
godot::GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization);
GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization);
init_obj.register_initializer(initialize_steam_audio);
init_obj.register_terminator(uninitialize_steam_audio);

View File

@@ -1,11 +1,19 @@
//
// Created by bryce on 5/7/2025.
//
#pragma once
#pragma once
#include <godot_cpp/classes/project_settings.hpp>
#include <godot_cpp/core/class_db.hpp>
#include "steam_audio.h"
#include "steam_audio_listener.h"
#include "steam_audio_material.h"
#include "steam_audio_source.h"
#include "steam_audio_static_mesh.h"
#include "steam_audio_dynamic_mesh.h"
#include <godot_cpp/core/class_db.hpp> // for ClassDB
using namespace godot;
// Called by the InitObject below to register your SteamAudio class.
// Called by the InitObject to register your SteamAudio class.
void initialize_steam_audio(ModuleInitializationLevel p_level);
void uninitialize_steam_audio(ModuleInitializationLevel p_level);
void uninitialize_steam_audio(ModuleInitializationLevel p_level);
//registers settings in godot
void register_steam_audio_settings();

View File

@@ -1,43 +1,151 @@
//
// Created by bryce on 5/7/2025.
//
#include "steam_audio.h"
#include "steam_audio.h"
using namespace godot;
IPLContext global_context = nullptr;
IPLScene global_scene = nullptr;
IPLSimulator global_simulator = nullptr;
SteamAudio::SteamAudio() {
steam_audio=this;
initialize();
}
SteamAudio::~SteamAudio()
{
if (context)
{
iplContextRelease(&context);
context=nullptr;
}
if (embree_device) {
iplEmbreeDeviceRelease(&embree_device);
embree_device=nullptr;
}
steam_audio = nullptr;
shutdown();
}
void SteamAudio::_bind_methods() {
ClassDB::bind_method(D_METHOD("initialize"), &SteamAudio::initialize);
ClassDB::bind_method(D_METHOD("shutdown"), &SteamAudio::shutdown);
ClassDB::bind_method(D_METHOD("get_hrtf"), &SteamAudio::get_hrtf);
ClassDB::bind_method(D_METHOD("get_hrtf_settings"), &SteamAudio::get_hrtf_settings);
ClassDB::bind_method(D_METHOD("get_simulator"), &SteamAudio::get_simulator);
ClassDB::bind_method(D_METHOD("get_simulation_settings"), &SteamAudio::get_simulation_settings);
ClassDB::bind_method(D_METHOD("get_context"),&SteamAudio::get_context);
ClassDB::bind_method(D_METHOD("get_context_settings"),&SteamAudio::get_context_settings);
ClassDB::bind_method(D_METHOD("get_scene"), &SteamAudio::get_scene);
ClassDB::bind_method(D_METHOD("get_scene_settings"), &SteamAudio::get_scene_settings);
ClassDB::bind_method(D_METHOD("get_embree_device"), &SteamAudio::get_embree_device);
ClassDB::bind_method(D_METHOD("get_embree_device_settings"),&SteamAudio::get_embree_device_settings);
}
SteamAudio::SteamAudio() = default;
bool SteamAudio::initialize()
{
IPLContextSettings settings{};
settings.version = STEAMAUDIO_VERSION;
if ( IPLerror err = iplContextCreate( &settings, &context ); err != IPL_STATUS_SUCCESS)
{
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;
bool SteamAudio::initialize() {
ctx_settings.version=STEAMAUDIO_VERSION;
IPLContext ctx = nullptr;
iplContextCreate(&ctx_settings, &ctx);
iplSceneCreate(ctx,&scene_settings,&scene);
iplSimulatorCreate(ctx,&simulation_settings,&simulator);
int ray_mode = proj_settings->get_setting("steam_audio/ray_tracer");
switch (ray_mode) {
case 0://steam rt
break;
case 1: // embree rt
iplEmbreeDeviceCreate(ctx,&embree_device_settings,&embree_device);
break;
default:
ERR_PRINT("Unknown Raytracer");
return false;
}
return true;
}
int spat_mode = proj_settings->get_setting("steam_audio/spatializer");
switch (spat_mode) {
case 0://panning
break;
case 1://HRTF
iplHRTFCreate(ctx,&audio_settings,&hrtf_settings,&hrtf);
break;
case 2://ambisonic pan
break;
case 3://ambisonic binaural
break;
default:
ERR_PRINT("Unknown Spatializer");
return false;
}
print_line("Steam Audio Successfully initialized");
return true;
}
void SteamAudio::update_static_scene() {
}
void SteamAudio::update_dynamic_scene() {
}
void SteamAudio::shutdown() {
if (embree_device != nullptr) {
iplEmbreeDeviceRelease(&embree_device);
embree_device = nullptr;
}
if (ctx != nullptr) {
iplContextRelease(&ctx);
ctx = nullptr;
}
if (hrtf != nullptr) {
iplHRTFRelease(&hrtf);
hrtf = nullptr;
}
if (simulator != nullptr) {
iplSimulatorRelease(&simulator);
simulator = nullptr;
}
if (scene!= nullptr) {
iplSceneRelease(&scene);
scene = nullptr;
}
}
bool SteamAudio::build_scene() {
update_dynamic_scene();
update_static_scene();
}
Array<NodePath> SteamAudio::get_nodes_with_child() {
}
IPLCoordinateSpace3 SteamAudio::godot_to_ipl_space(const Transform3D &t) {
IPLCoordinateSpace3 s{};
// pull columns via Basis.xform()
Vector3 right = t.basis.xform(Vector3(1, 0, 0));
Vector3 up = t.basis.xform(Vector3(0, 1, 0));
Vector3 ahead = t.basis.xform(Vector3(0, 0, -1)); // Godot forward is -Z
s.right = { right.x, right.y, right.z };
s.up = { up.x, up.y, up.z };
s.ahead = { ahead.x, ahead.y, ahead.z };
s.origin = { t.origin.x, t.origin.y, t.origin.z };
return s;
}
// Steam Audio → Godot space
Transform3D SteamAudio::ipl_space_to_godot(const IPLCoordinateSpace3 &s) {
// construct a Basis from column vectors
Basis b(
Vector3(s.right .x, s.right .y, s.right .z),
Vector3(s.up .x, s.up .y, s.up .z),
Vector3(-s.ahead.x, -s.ahead.y, -s.ahead.z) // invert ahead back to -Z
);
return Transform3D(b, Vector3(s.origin.x, s.origin.y, s.origin.z));
}
void SteamAudio::_ready() {
reflections_thread;
build_scene();
iplSimulatorRunDirect(simulator);
}

View File

@@ -1,31 +1,81 @@
//
// Created by bryce on 5/7/2025.
//
#pragma once
#pragma once
#include <godot_cpp/godot.hpp>
#include<godot_cpp/core/class_db.hpp>
#include <godot_cpp/classes/os.hpp>
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/classes/node.hpp>
#include <godot_cpp/classes/project_settings.hpp>
#include <godot_cpp/classes/ref.hpp>
#include <godot_cpp/classes/scene_tree.hpp>
#include <godot_cpp/classes/thread.hpp>
#include <godot_cpp/classes/time.hpp>
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/godot.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
#include <phonon.h>
#include "steam_audio_dynamic_mesh.h"
#include "steam_audio_listener.h"
#include "steam_audio_material.h"
#include "steam_audio_source.h"
#include "steam_audio_static_mesh.h"
using namespace godot;
class SteamAudio : public RefCounted
class SteamAudio : public Node
{
GDCLASS( SteamAudio,RefCounted ) // NOLINT(readability-use-auto)
GDCLASS( SteamAudio, Node ) // NOLINT(modernize-use-auto, hicpp-use-auto)
private:
IPLContext context = nullptr;
IPLContextSettings ctx_settings{};
IPLAudioSettings audio_settings{};
IPLContext ctx = nullptr;
IPLEmbreeDeviceSettings embree_device_settings{};
IPLEmbreeDevice embree_device = nullptr;
IPLScene scene = nullptr;
IPLSceneSettings scene_settings{};
IPLSimulationSettings simulation_settings{};
IPLSimulator simulator = nullptr;
IPLStaticMesh mesh = nullptr;
IPLHRTFSettings hrtf_settings{};
IPLHRTF hrtf = nullptr;
ProjectSettings *proj_settings = ProjectSettings::get_singleton();
Array<SteamAudioDynamicMesh> dynamic_geometry;
Array<SteamAudioStaticMesh> static_geometry;
Array<SteamAudioSource> sources;
Ref<Thread> reflections_thread;
Ref<Thread> pathing_thread;
protected:
static void _bind_methods();
void _ready() override;
public:
static SteamAudio *steam_audio;
SteamAudio();
~SteamAudio() override;
bool initialize();
void shutdown();
bool build_scene();
void update_static_scene();
void update_dynamic_scene();
static Transform3D ipl_space_to_godot(const IPLCoordinateSpace3 &p_space);
static IPLCoordinateSpace3 godot_to_ipl_space(const Transform3D &p_transform);
[[nodiscard]] IPLContext get_context() const{return ctx;}
[[nodiscard]] IPLEmbreeDevice get_embree_device() const{return embree_device;}
[[nodiscard]] IPLEmbreeDeviceSettings get_embree_device_settings() const{return embree_device_settings;}
[[nodiscard]] IPLScene get_scene() const{return scene;}
[[nodiscard]] IPLSceneSettings get_scene_settings() const{return scene_settings;}
[[nodiscard]] IPLSimulator get_simulator() const{return simulator;}
[[nodiscard]] IPLSimulationSettings get_simulation_settings() const{return simulation_settings;}
[[nodiscard]] IPLHRTF get_hrtf() const{return hrtf;}
[[nodiscard]] IPLHRTFSettings get_hrtf_settings() const{return hrtf_settings;}
[[nodiscard]] IPLContextSettings get_context_settings() const{return ctx_settings;}
[[nodiscard]] IPLContext getContext() const{return context;}
[[nodiscard]] IPLEmbreeDevice getEmbreeDevice() const{return embree_device;}
};

View File

@@ -0,0 +1,92 @@
#include "steam_audio_dynamic_mesh.h"
#include "steam_audio_globals.h" // holds global_context & global_scene
using namespace godot;
SteamAudioDynamicMesh::SteamAudioDynamicMesh() {}
SteamAudioDynamicMesh::~SteamAudioDynamicMesh() {
if (instanced_mesh) {
// remove & release the instance
iplInstancedMeshRemove(instanced_mesh, global_scene);
iplInstancedMeshRelease(&instanced_mesh);
}
if (proxy_subscene) {
iplSceneRelease(&proxy_subscene);
}
}
void SteamAudioDynamicMesh::_bind_methods() {
// no exposed properties for now
}
void SteamAudioDynamicMesh::_ready() {
build_subscene();
// instance it in the main scene, with initial transform
IPLInstancedMeshSettings ims{};
ims.subScene = proxy_subscene;
ims.transform = to_ipl_matrix(get_global_transform());
iplInstancedMeshCreate(global_scene, &ims, &instanced_mesh);
iplInstancedMeshAdd(instanced_mesh, global_scene);
iplSceneCommit(global_scene);
}
void SteamAudioDynamicMesh::_process(double /*delta*/) {
if (!instanced_mesh) return;
// update the worldspace transform
IPLMatrix4x4 mat = to_ipl_matrix(get_global_transform());
iplInstancedMeshUpdateTransform(instanced_mesh, global_scene, mat);
// commit once per frame (best performance if after all updates) :contentReference[oaicite:0]{index=0}
iplSceneCommit(global_scene);
}
void SteamAudioDynamicMesh::build_subscene() {
// 1) create a fresh sub-scene
IPLSceneSettings ss{};
iplSceneCreate(global_context, &ss, &proxy_subscene);
// 2) pull raw verts/indices out of this MeshInstance3D
Ref<Mesh> mesh = cast_to<MeshInstance3D>(get_parent())->get_mesh();
if (mesh.is_null()) return;
// for simplicity, we assume a single surface:
Array arr = mesh->surface_get_arrays(0);
PackedVector3Array verts = arr[Mesh::ARRAY_VERTEX];
PackedInt32Array idxs = arr[Mesh::ARRAY_INDEX];
// fill out staticmesh settings
IPLStaticMeshSettings sms{};
sms.numVertices = verts.size();
sms.vertices = reinterpret_cast<IPLVector3*>(const_cast<Vector3*>(verts.ptr()));
sms.numTriangles = idxs.size() / 3;
// convert PoolIntArray → IPLTriangle*
// …youd build & fill an IPLTriangle array here…
// sms.triangles = your_triangle_buffer;
// add it to the proxy_subscene
IPLStaticMesh sm = nullptr;
iplStaticMeshCreate(proxy_subscene, &sms, &sm);
iplStaticMeshAdd(sm, proxy_subscene);
// finally commit the sub-scene so its ready to be instanced :contentReference[oaicite:1]{index=1}
iplSceneCommit(proxy_subscene);
}
IPLMatrix4x4 SteamAudioDynamicMesh::to_ipl_matrix(const Transform3D &t) {
// row-major: each m[i][j] is row i, column j
IPLMatrix4x4 m{};
// basis X column
m.elements[0][0] = t.basis[0][0]; m.elements[1][0] = t.basis[0][1]; m.elements[2][0] = t.basis[0][2];
// basis Y column
m.elements[0][1] = t.basis[1][0]; m.elements[1][1] = t.basis[1][1]; m.elements[2][1] = t.basis[1][2];
// basis Z column
m.elements[0][2] = t.basis[2][0]; m.elements[1][2] = t.basis[2][1]; m.elements[2][2] = t.basis[2][2];
// origin
m.elements[0][3] = t.origin.x; m.elements[1][3] = t.origin.y; m.elements[2][3] = t.origin.z;
// bottom row
m.elements[3][0] = 0; m.elements[3][1] = 0; m.elements[3][2] = 0; m.elements[3][3] = 1;
return m;
}

View File

@@ -0,0 +1,31 @@
#pragma once
#include <godot_cpp/classes/mesh_instance3d.hpp>
#include <godot_cpp/core/class_db.hpp>
#include <phonon.h>
using namespace godot;
class SteamAudioDynamicMesh : public Node3D {
GDCLASS(SteamAudioDynamicMesh, Node3D)
private:
IPLScene proxy_subscene = nullptr;
IPLInstancedMesh instanced_mesh = nullptr;
// helper: convert Godot Transform3D → IPLMatrix4x4
static IPLMatrix4x4 to_ipl_matrix(const Transform3D &t);
// builds a one-off IPLScene containing this meshs raw geometry
void build_subscene();
public:
SteamAudioDynamicMesh();
~SteamAudioDynamicMesh() override;
void _ready() override;
void _process(double delta) override;
protected:
static void _bind_methods();
};

View File

@@ -0,0 +1,7 @@
// steam_audio_globals.h
#pragma once
#include <phonon.h>
#include <godot_cpp/core/class_db.hpp>
extern IPLContext global_context;
extern IPLScene global_scene;
extern IPLSimulator global_simulator;

View File

@@ -1,32 +1,46 @@
//
// Created by bryce on 5/8/2025.
//
#include "steam_audio_listener.h"
#include "steam_audio_listener.h"
#include "phonon.h"
using namespace godot;
SteamAudioListener::SteamAudioListener() = default;
SteamAudioListener::~SteamAudioListener() = default;
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<SteamAudio> &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};
IPLVector3 pos = {
static_cast<float>(transform.origin.x),
static_cast<float>(transform.origin.y),
static_cast<float>(transform.origin.z)
};
Vector3 godot_fwd = transform.basis.xform(Vector3(0, 0, -1));
IPLVector3 fwd = {
static_cast<float>(godot_fwd.x),
static_cast<float>(godot_fwd.y),
static_cast<float>(godot_fwd.z)
};
Vector3 godot_up = transform.basis.xform(Vector3(0, 1, 0));
IPLVector3 up = {
static_cast<float>(godot_up.x),
static_cast<float>(godot_up.y),
static_cast<float>(godot_up.z)
};
IPLVector3 right = {
fwd.y * up.z - fwd.z * up.y,
fwd.z * up.x - fwd.x * up.z,
fwd.x * up.y - fwd.y * up.x
};
IPLCoordinateSpace3 listenerCS{};
listenerCS.origin = pos;
listenerCS.ahead = fwd;
listenerCS.up = up;
listenerCS.right = right;
IPLSimulationSharedInputs sharedInputs{};
sharedInputs.listener = listenerCS;
}

View File

@@ -1,22 +1,24 @@
//
// Created by bryce on 5/8/2025.
//
#pragma once
#pragma once
#include <godot_cpp/classes/node3d.hpp>
#include<godot_cpp/godot.hpp>
#include <godot_cpp/classes/audio_listener3d.hpp>
#include <godot_cpp/godot.hpp>
#include "steam_audio.h"
#include <phonon.h>
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/classes/wrapped.hpp>
using namespace godot;
class SteamAudioListener : public AudioListener3D {
class SteamAudioListener:public AudioListener3D
{
GDCLASS(SteamAudioListener,AudioListener3D)
Ref<SteamAudio> ctx;
protected:
static void _bind_methods();
void _process(double p_delta) override;
public:
void set_context(const Ref<SteamAudio> &p_ctx);
void _process(double delta) override;
SteamAudioListener();
~SteamAudioListener() override;
private:
};

View File

@@ -0,0 +1,39 @@
#include "steam_audio_material.h"
using namespace godot;
void SteamAudioMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_absorption"), &SteamAudioMaterial::get_absorption);
ClassDB::bind_method(D_METHOD("set_absorption","value"), &SteamAudioMaterial::set_absorption);
ClassDB::bind_method(D_METHOD("get_scattering"), &SteamAudioMaterial::get_scattering);
ClassDB::bind_method(D_METHOD("set_scattering","value"), &SteamAudioMaterial::set_scattering);
ClassDB::bind_method(D_METHOD("get_transmission"), &SteamAudioMaterial::get_transmission);
ClassDB::bind_method(D_METHOD("set_transmission","value"), &SteamAudioMaterial::set_transmission);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3,"absorption"), "set_absorption", "get_absorption");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT,"scattering"), "set_scattering", "get_scattering");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3,"transmission"), "set_transmission", "get_transmission");
}
Vector3 SteamAudioMaterial::get_absorption() const {return absorption;}
void SteamAudioMaterial::set_absorption(Vector3 value) { absorption = value; }
float SteamAudioMaterial::get_scattering() const {return scattering;}
void SteamAudioMaterial::set_scattering(float value) { scattering = value; }
Vector3 SteamAudioMaterial::get_transmission() const {return transmission;}
void SteamAudioMaterial::set_transmission(Vector3 value) { transmission = value; }
IPLMaterial SteamAudioMaterial::to_ipl_material() const {
IPLMaterial material ={};
material.absorption[0] = absorption.x;
material.absorption[1] = absorption.y;
material.absorption[2] = absorption.z;
material.scattering = scattering;
material.transmission[0] = transmission.x;
material.transmission[1] = transmission.y;
material.transmission[2] = transmission.z;
return material;
}

View File

@@ -0,0 +1,27 @@
#pragma once
#include <godot_cpp/classes/resource.hpp>
#include "godot_cpp/godot.hpp"
#include <godot_cpp/core/class_db.hpp>
#include "phonon.h"
using namespace godot;
class SteamAudioMaterial : public Resource {
GDCLASS(SteamAudioMaterial,Resource) // NOLINT(modernize-use-auto, hicpp-use-auto)
protected:
static void _bind_methods();
private:
Vector3 absorption = Vector3(0.2, 0.2, 0.2);
float scattering = 0.0;
Vector3 transmission = Vector3(0.0, 0.0, 0.0);
public:
void set_absorption(const Vector3 value);
void set_scattering(const float value);
void set_transmission(const Vector3 value);
Vector3 get_absorption() const;
[[nodiscard]] float get_scattering() const;
Vector3 get_transmission() const;
[[nodiscard]] IPLMaterial to_ipl_material() const;
};

View File

@@ -1,95 +0,0 @@
//
// 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<SteamAudio> &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 = mesh_node->get_mesh();
ERR_FAIL_COND(!mesh.is_valid());
ArrayMesh *raw_am = Object::cast_to<ArrayMesh>(mesh.ptr());
ERR_FAIL_COND(!raw_am);
Ref<ArrayMesh> 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 Audios settings struct
IPLStaticMeshSettings sm{};
sm.numVertices = vertex_count;
sm.numTriangles = triangle_count;
sm.numMaterials = 0;
sm.vertices = reinterpret_cast<IPLVector3*>(
const_cast<Vector3*>(verts.ptr())
);
sm.materialIndices = nullptr;
sm.materials = nullptr;
// 4) Convert the flat index list into IPLTriangle[]
IPLTriangle *tris = static_cast<IPLTriangle*>(
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);
}

View File

@@ -1,34 +0,0 @@
#pragma once
#include <godot_cpp/classes/array_mesh.hpp>
#include <godot_cpp/classes/mesh_instance3d.hpp>
#include <godot_cpp/classes/ref_counted.hpp>
#include <godot_cpp/variant/typed_array.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
#include <godot_cpp/variant/packed_vector3_array.hpp>
#include <godot_cpp/variant/packed_int32_array.hpp>
#include <godot_cpp/classes/object.hpp>
#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<SteamAudio> ctx;
protected:
static void _bind_methods();
public:
SteamAudioScene();
~SteamAudioScene() override;
void set_context(const Ref<SteamAudio> &p_ctx);
void add_mesh(MeshInstance3D *mesh_node);
void commit() const;
};

View File

@@ -1,30 +1,22 @@
//
// Created by bryce on 5/9/2025.
//
#include "steam_audio_source.h"
#include "steam_audio_source.h"
#include "steam_audio.h"
#include "steam_audio_globals.h"
using namespace godot;
SteamAudioSource::SteamAudioSource() {
SteamAudioSource::SteamAudioSource() {
iplSourceCreate(sa->get_simulator(),&settings,pSource);
}
SteamAudioSource::~SteamAudioSource() {
if (steam_audio_source) {
iplSourceRelease(&steam_audio_source);
steam_audio_source = nullptr;
}
SteamAudioSource::~SteamAudioSource() {
iplSourceRelease(pSource);
}
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);
}
void SteamAudioSource::_process(double delta) {
update_position();
}
bool SteamAudioSource::update_position() {
IPLCoordinateSpace3 current_pos = sa->godot_to_ipl_space(get_transform());
iplSource
}

View File

@@ -1,32 +1,29 @@
//
// Created by bryce on 5/9/2025.
//
#pragma once
#pragma once
#include <godot_cpp/classes/audio_stream_player3d.hpp>
#include <godot_cpp/classes/node3d.hpp>
#include <godot_cpp/classes/audio_stream_generator.hpp>
#include <godot_cpp/classes/audio_stream_generator_playback.hpp>
#include <godot_cpp/classes/audio_stream_player3d.hpp>
#include <godot_cpp/godot.hpp>
#include <godot_cpp/core/class_db.hpp>
#include "steam_audio.h"
#include "phonon.h"
using namespace godot;
class SteamAudioSource:Node3D {
GDCLASS(SteamAudioSource, Node3D)
private:
AudioStreamPlayer3D* audio_player = nullptr;
IPLSource steam_audio_source = nullptr;
class SteamAudioSource: public Node3D
{
GDCLASS(SteamAudioSource,Node3D)
bool initialized = false;
IPLSource *pSource = nullptr;
SteamAudio *sa = SteamAudio::steam_audio;
IPLSourceSettings settings{};
protected:
static void _bind_methods();
void _process(double p_delta) override;
public:
SteamAudioSource();
~SteamAudioSource();
bool update_position();
void _ready() override;
void _process(double p_delta) override;
void play();
void stop();
void set_stream(const Ref<AudioStream>& p_stream);
protected:
static void _bind_methods();
};

View File

@@ -0,0 +1,181 @@
// steam_audio_static_mesh.cpp
#include "steam_audio_static_mesh.h"
#include <godot_cpp/classes/array_mesh.hpp>
#include <godot_cpp/classes/convex_polygon_shape3d.hpp>
#include <godot_cpp/classes/shape3d.hpp>
#include <godot_cpp/variant/packed_int32_array.hpp>
#include <godot_cpp/variant/packed_vector3_array.hpp>
#include <phonon.h> // IPLStaticMesh, IPLStaticMeshSettings, IPLMaterial
using namespace godot;
SteamAudioStaticMesh::SteamAudioStaticMesh() = default;
SteamAudioStaticMesh::~SteamAudioStaticMesh() {
// release any previously created Steam Audio meshes
for (auto &sm : static_meshes) {
iplStaticMeshRelease(&sm);
}
static_meshes.clear();
}
void SteamAudioStaticMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_proxy_mode", "mode"), &SteamAudioStaticMesh::set_proxy_mode);
ClassDB::bind_method(D_METHOD("get_proxy_mode"), &SteamAudioStaticMesh::get_proxy_mode);
ADD_PROPERTY(
PropertyInfo(Variant::INT, "proxy_mode",PROPERTY_HINT_ENUM,"None,Convex,Custom"), "set_proxy_mode","get_proxy_mode");
ClassDB::bind_method(D_METHOD("set_custom_proxy_mesh", "mesh"), &SteamAudioStaticMesh::set_custom_proxy_mesh);
ClassDB::bind_method(D_METHOD("get_custom_proxy_mesh"), &SteamAudioStaticMesh::get_custom_proxy_mesh);
ADD_PROPERTY(
PropertyInfo(Variant::OBJECT, "custom_proxy_mesh",PROPERTY_HINT_RESOURCE_TYPE,"Mesh"), "set_custom_proxy_mesh","get_custom_proxy_mesh");
ClassDB::bind_method(D_METHOD("set_default_material", "mat"), &SteamAudioStaticMesh::set_default_material);
ClassDB::bind_method(D_METHOD("get_default_material"), &SteamAudioStaticMesh::get_default_material);
// And still bind your proxy-generator:
ClassDB::bind_method(D_METHOD("generate_proxy_mesh"), &SteamAudioStaticMesh::generate_proxy_mesh);
}
// Setters & getters ---------------------------------------------------
void SteamAudioStaticMesh::set_proxy_mode(int mode) {
proxy_mode = mode;
}
int SteamAudioStaticMesh::get_proxy_mode() const {
return proxy_mode;
}
void SteamAudioStaticMesh::set_custom_proxy_mesh(Ref<Mesh> mesh) {
custom_proxy_mesh = mesh;
}
Ref<Mesh> SteamAudioStaticMesh::get_custom_proxy_mesh() const {
return custom_proxy_mesh;
}
void SteamAudioStaticMesh::set_default_material(Ref<SteamAudioMaterial> mat) {
default_material = mat;
}
Ref<SteamAudioMaterial> SteamAudioStaticMesh::get_default_material() const {
return default_material;
}
// Core functionality --------------------------------------------------
void SteamAudioStaticMesh::generate_proxy_mesh() {
// 1) Clean up any old meshes
for (auto &sm : static_meshes) {
iplStaticMeshRelease(&sm);
}
static_meshes.clear();
// 2) Pick which Mesh to read triangles from
Ref<Mesh> target;
switch (proxy_mode) {
case PROXY_NONE:
target = cast_to<MeshInstance3D>(get_parent())->get_mesh();
break;
case PROXY_CONVEX: {
Ref<Shape3D> hull_shape = cast_to<MeshInstance3D>(get_parent())->get_mesh()->create_convex_shape();
ConvexPolygonShape3D *convex = cast_to<ConvexPolygonShape3D>(hull_shape.ptr());
if (!convex) {
ERR_PRINT("SteamAudioStaticMesh: convex cast failed");
return;
}
Ref<ArrayMesh> debug_mesh = convex->get_debug_mesh();
if (!debug_mesh.is_valid()) {
ERR_PRINT("SteamAudioStaticMesh: debug mesh invalid");
return;
}
target = debug_mesh;
break;
}
case PROXY_CUSTOM:
if (!custom_proxy_mesh.is_valid()) {
ERR_PRINT("SteamAudioStaticMesh: custom_proxy_mesh not set");
return;
}
target = custom_proxy_mesh;
break;
default:
ERR_PRINT("SteamAudioStaticMesh: unknown proxy_mode");
return;
}
if (!target.is_valid()) {
ERR_PRINT("SteamAudioStaticMesh: target mesh invalid");
return;
}
// 3) For each surface, build and add a Steam Audio static mesh
const int surface_count = target->get_surface_count();
for (int s = 0; s < surface_count; ++s) {
Array arrays = target->surface_get_arrays(s);
PackedVector3Array verts = arrays[Mesh::ARRAY_VERTEX];
PackedInt32Array idxs = arrays[Mesh::ARRAY_INDEX];
// --- build IPLMaterial from metadata or default_material ---
Ref<Material> gm = target->surface_get_material(s);
Ref<SteamAudioMaterial> sa_mat;
if (gm.is_valid() && gm->has_meta("steam_audio")) {
sa_mat = gm->get_meta("steam_audio");
}
if (!sa_mat.is_valid()) {
sa_mat = default_material;
}
IPLMaterial material{}; // zero initialize all fields
// absorption[0..2]
material.absorption[0] = sa_mat->get_absorption().x;
material.absorption[1] = sa_mat->get_absorption().y;
material.absorption[2] = sa_mat->get_absorption().z;
// scattering
material.scattering = sa_mat->get_scattering();
// transmission[0..2]
material.transmission[0] = sa_mat->get_transmission().x;
material.transmission[1] = sa_mat->get_transmission().y;
material.transmission[2] = sa_mat->get_transmission().z;
// --- fill out static mesh settings ---
IPLStaticMeshSettings settings{};
// Number of vertices
settings.numVertices = verts.size();
// reinterpret Godots Vector3 buffer as Steam Audios IPLVector3[]
const IPLVector3 *raw_vertices = reinterpret_cast<const IPLVector3 *>(verts.ptr());
// drop the const so it matches IPLStaticMeshSettings::vertices (IPLVector3*)
IPLVector3 *writable_vertices = const_cast<IPLVector3 *>(raw_vertices);
settings.vertices = writable_vertices;
// Number of triangles
settings.numTriangles = idxs.size() / 3;
// build triangle array
std::vector<IPLTriangle> triangles;
triangles.reserve(settings.numTriangles);
for (int i = 0; i < settings.numTriangles; ++i) {
IPLTriangle tri{};
tri.indices[0] = static_cast<uint32_t>(idxs[3*i + 0]);
tri.indices[1] = static_cast<uint32_t>(idxs[3*i + 1]);
tri.indices[2] = static_cast<uint32_t>(idxs[3*i + 2]);
triangles.push_back(tri);
}
settings.triangles = triangles.data();
settings.numMaterials = 1;
settings.materials = &material;
// --- create & add to Steam Audio scene ---
/*IPLStaticMesh sm = nullptr;
IPLerror err = iplStaticMeshCreate(global_scene, &settings, &sm);
if (err != IPL_STATUS_SUCCESS) {
ERR_PRINT("SteamAudioStaticMesh: iplStaticMeshCreate failed");
continue;
}
iplStaticMeshAdd(sm, global_scene);
static_meshes.push_back(sm);*/
}
// 4) Commit all additions at once
// iplSceneCommit(global_scene);
}

View File

@@ -0,0 +1,52 @@
// steam_audio_proxy_mesh.h
#pragma once
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/classes/mesh_instance3d.hpp>
#include <godot_cpp/classes/mesh.hpp>
#include <godot_cpp/classes/shape3d.hpp>
#include <godot_cpp/classes/convex_polygon_shape3d.hpp>
#include <godot_cpp/classes/array_mesh.hpp>
#include <godot_cpp/classes/material.hpp>
#include "steam_audio_material.h"
using namespace godot;
// Proxy generation modes
enum ProxyMode {
PROXY_NONE = 0,
PROXY_CONVEX = 1,
PROXY_CUSTOM = 2,
};
class SteamAudioStaticMesh : public Node3D {
GDCLASS(SteamAudioStaticMesh, Node3D)
private:
int proxy_mode = PROXY_NONE;
Ref<Mesh> custom_proxy_mesh;
Ref<SteamAudioMaterial> default_material;
Vector<IPLStaticMesh> static_meshes;
protected:
static void _bind_methods();
public:
SteamAudioStaticMesh();
~SteamAudioStaticMesh() override;
// Setters & getters
void set_proxy_mode(int mode);
int get_proxy_mode() const;
void set_custom_proxy_mesh(Ref<Mesh> mesh);
Ref<Mesh> get_custom_proxy_mesh() const;
void set_default_material(Ref<SteamAudioMaterial> mat);
Ref<SteamAudioMaterial> get_default_material() const;
// Regenerates Steam Audio proxies
void generate_proxy_mesh();
};