did things
This commit is contained in:
@@ -3,19 +3,14 @@
|
||||
target_sources( ${PROJECT_NAME}
|
||||
PRIVATE
|
||||
register_types.cpp
|
||||
register_types.h
|
||||
steam_audio.cpp
|
||||
steam_audio.h
|
||||
steam_audio_server.cpp
|
||||
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
|
||||
steam_audio_source.cpp
|
||||
)
|
||||
|
||||
target_include_directories( ${PROJECT_NAME}
|
||||
|
||||
@@ -1,29 +1,36 @@
|
||||
#include "register_types.h"
|
||||
#include "register_types.hpp"
|
||||
|
||||
using namespace godot;
|
||||
|
||||
SteamAudioServer *srv;
|
||||
|
||||
void initialize_steam_audio(ModuleInitializationLevel p_level) {
|
||||
if (p_level!=MODULE_INITIALIZATION_LEVEL_SCENE) {
|
||||
if (p_level!=MODULE_INITIALIZATION_LEVEL_SCENE && p_level != MODULE_INITIALIZATION_LEVEL_SERVERS) {
|
||||
return;
|
||||
}
|
||||
register_steam_audio_settings();
|
||||
GDREGISTER_CLASS(SteamAudio);
|
||||
GDREGISTER_CLASS(SteamAudioMaterial);
|
||||
GDREGISTER_RUNTIME_CLASS(SteamAudioListener);
|
||||
GDREGISTER_RUNTIME_CLASS(SteamAudioSource);
|
||||
GDREGISTER_RUNTIME_CLASS(SteamAudioStaticMesh)
|
||||
GDREGISTER_RUNTIME_CLASS(SteamAudioDynamicMesh)
|
||||
if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) {
|
||||
GDREGISTER_CLASS(SteamAudioServer);
|
||||
srv = memnew(SteamAudioServer);
|
||||
}
|
||||
if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) {
|
||||
register_steam_audio_settings();
|
||||
GDREGISTER_RUNTIME_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_SCENE) {
|
||||
return;
|
||||
if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) {
|
||||
memdelete(srv);
|
||||
}
|
||||
}
|
||||
|
||||
void register_steam_audio_settings() {
|
||||
ProjectSettings *settings = ProjectSettings::get_singleton();
|
||||
|
||||
ProjectSettings *settings = ProjectSettings::get_singleton();
|
||||
{//Raytracer enum
|
||||
settings->set("steam_audio/ray_tracer/RayTracer",0);
|
||||
Dictionary info;
|
||||
@@ -33,7 +40,7 @@ void register_steam_audio_settings() {
|
||||
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);
|
||||
settings->set_initial_value("steam_audio/ray_tracer/RayTracer",1);
|
||||
}
|
||||
|
||||
{//spatialization mode enum
|
||||
@@ -49,7 +56,31 @@ void register_steam_audio_settings() {
|
||||
}
|
||||
|
||||
{
|
||||
settings->set("steam_audio/spatializer/HRTF/Volume",0);
|
||||
settings->set("steam_audio/spatializer/HRTF/HRTF_Type",0);
|
||||
Dictionary info;
|
||||
info["name"] = "steam_audio/spatializer/HRTF/HRTF_Type";
|
||||
info["type"] = Variant::INT;
|
||||
info["hint"] = PROPERTY_HINT_ENUM;
|
||||
info["hint_string"] = "Default,Custom";
|
||||
info["usage"] = PROPERTY_USAGE_DEFAULT;
|
||||
settings->add_property_info(info);
|
||||
settings->set_initial_value("steam_audio/spatializer/HRTF/HRTF_Type",0);
|
||||
}
|
||||
|
||||
{
|
||||
settings->set("steam_audio/spatializer/HRTF/Sofa_File",0.0f);
|
||||
Dictionary info;
|
||||
info["name"] = "steam_audio/spatializer/HRTF/Sofa_File";
|
||||
info["type"] = Variant::STRING;
|
||||
info["hint"] = PROPERTY_HINT_FILE;
|
||||
info["hint_string"] = "";
|
||||
info["usage"] = PROPERTY_USAGE_DEFAULT;
|
||||
settings->add_property_info(info);
|
||||
settings->set_initial_value("steam_audio/spatializer/HRTF/Sofa_File","");
|
||||
}
|
||||
|
||||
{
|
||||
settings->set("steam_audio/spatializer/HRTF/Volume",1.0f);
|
||||
Dictionary info;
|
||||
info["name"] = "steam_audio/spatializer/HRTF/Volume";
|
||||
info["type"] = Variant::FLOAT;
|
||||
@@ -57,9 +88,42 @@ void register_steam_audio_settings() {
|
||||
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);
|
||||
settings->set_initial_value("steam_audio/spatializer/HRTF/Volume",1.0f);
|
||||
}
|
||||
|
||||
{
|
||||
settings->set("steam_audio/spatializer/HRTF/Norm_Type",0);
|
||||
Dictionary info;
|
||||
info["name"] = "steam_audio/spatializer/HRTF/Norm_Type";
|
||||
info["type"] = Variant::INT;
|
||||
info["hint"] = PROPERTY_HINT_ENUM;
|
||||
info["hint_string"] = "NONE,Root Mean Squared";
|
||||
info["usage"] = PROPERTY_USAGE_DEFAULT;
|
||||
settings->add_property_info(info);
|
||||
settings->set_initial_value("steam_audio/spatializer/HRTF/Norm_Type",0);
|
||||
}
|
||||
|
||||
{
|
||||
settings->set("steam_audio/sampling_rate",0);
|
||||
Dictionary info;
|
||||
info["name"] = "steam_audio/sampling_rate";
|
||||
info["type"] = Variant::INT;
|
||||
info["hint"] = PROPERTY_HINT_ENUM;
|
||||
info["hint_string"] = "44100:44100,48000:48000";
|
||||
settings->add_property_info(info);
|
||||
settings->set_initial_value("steam_audio/sampling_rate",48000);
|
||||
}
|
||||
|
||||
{
|
||||
settings->set("steam_audio/buffer_size",0);
|
||||
Dictionary info;
|
||||
info["name"] = "steam_audio/buffer_size";
|
||||
info["type"] = Variant::INT;
|
||||
info["hint"] = PROPERTY_HINT_ENUM;
|
||||
info["hint_string"] = "512:512,1024:1024";
|
||||
settings->add_property_info(info);
|
||||
settings->set_initial_value("steam_audio/buffer_size",1024);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
#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 "steam_audio.hpp"
|
||||
#include "steam_audio_dynamic_mesh.hpp"
|
||||
#include "steam_audio_source.hpp"
|
||||
#include "steam_audio_listener.hpp"
|
||||
#include "steam_audio_server.hpp"
|
||||
#include "steam_audio_material.hpp"
|
||||
#include "steam_audio_static_mesh.hpp"
|
||||
|
||||
using namespace godot;
|
||||
|
||||
@@ -1,118 +1,32 @@
|
||||
#include "steam_audio.h"
|
||||
#include "steam_audio.hpp"
|
||||
|
||||
using namespace godot;
|
||||
|
||||
IPLContext global_context = nullptr;
|
||||
IPLScene global_scene = nullptr;
|
||||
IPLSimulator global_simulator = nullptr;
|
||||
|
||||
SteamAudio::SteamAudio() {
|
||||
steam_audio=this;
|
||||
initialize();
|
||||
SteamAudio::SteamAudio() {
|
||||
|
||||
}
|
||||
|
||||
SteamAudio::~SteamAudio()
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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() {
|
||||
|
||||
}
|
||||
IPLContextSettings SteamAudio::ctx_default_settings = {
|
||||
STEAMAUDIO_VERSION,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
IPL_SIMDLEVEL_NEON,
|
||||
};
|
||||
IPLSimulationSettings SteamAudio::simulation_default_settings = {
|
||||
};
|
||||
IPLAudioSettings SteamAudio::audio_default_settings = {
|
||||
};
|
||||
IPLSceneSettings SteamAudio::scene_default_settings = {
|
||||
};
|
||||
IPLEmbreeDeviceSettings SteamAudio::embree_device_default_settings = {
|
||||
};
|
||||
IPLHRTFSettings SteamAudio::hrtf_default_settings = {
|
||||
};
|
||||
|
||||
IPLCoordinateSpace3 SteamAudio::godot_to_ipl_space(const Transform3D &t) {
|
||||
IPLCoordinateSpace3 s{};
|
||||
@@ -129,7 +43,6 @@ IPLCoordinateSpace3 SteamAudio::godot_to_ipl_space(const Transform3D &t) {
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
// Steam Audio → Godot space
|
||||
Transform3D SteamAudio::ipl_space_to_godot(const IPLCoordinateSpace3 &s) {
|
||||
// construct a Basis from column vectors
|
||||
@@ -142,10 +55,45 @@ Transform3D SteamAudio::ipl_space_to_godot(const IPLCoordinateSpace3 &s) {
|
||||
return Transform3D(b, Vector3(s.origin.x, s.origin.y, s.origin.z));
|
||||
}
|
||||
|
||||
void SteamAudio::_ready() {
|
||||
reflections_thread;
|
||||
build_scene();
|
||||
iplSimulatorRunDirect(simulator);
|
||||
IPLMatrix4x4 SteamAudio::transform_to_ipl_matrix(const Transform3D &t) {
|
||||
// First compute the three axes using Godot’s forward = –Z
|
||||
Vector3 right = t.basis.xform(Vector3(1, 0, 0));
|
||||
Vector3 up = t.basis.xform(Vector3(0, 1, 0));
|
||||
Vector3 forward = t.basis.xform(Vector3(0, 0, -1)); // ← same as .ahead above
|
||||
|
||||
IPLMatrix4x4 m{};
|
||||
// Row-major: m[row][col]
|
||||
|
||||
// X axis → column 0
|
||||
m.elements[0][0] = right.x; m.elements[1][0] = right.y; m.elements[2][0] = right.z;
|
||||
|
||||
// Y axis → column 1
|
||||
m.elements[0][1] = up.x; m.elements[1][1] = up.y; m.elements[2][1] = up.z;
|
||||
|
||||
// Forward (–Z) → column 2
|
||||
m.elements[0][2] = forward.x; m.elements[1][2] = forward.y; m.elements[2][2] = forward.z;
|
||||
|
||||
// Translation → column 3
|
||||
m.elements[0][3] = t.origin.x; m.elements[1][3] = t.origin.y; m.elements[2][3] = t.origin.z;
|
||||
|
||||
// Bottom row for homogeneous
|
||||
m.elements[3][0] = 0; m.elements[3][1] = 0; m.elements[3][2] = 0; m.elements[3][3] = 1;
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
IPLMaterial SteamAudio::to_ipl_material(const SteamMaterial &material) {
|
||||
IPLMaterial iplmaterial ={};
|
||||
|
||||
iplmaterial.absorption[0] = material.absorption.x;
|
||||
iplmaterial.absorption[1] = material.absorption.y;
|
||||
iplmaterial.absorption[2] = material.absorption.z;
|
||||
|
||||
iplmaterial.scattering = material.scattering;
|
||||
|
||||
iplmaterial.transmission[0] = material.transmission.x;
|
||||
iplmaterial.transmission[1] = material.transmission.y;
|
||||
iplmaterial.transmission[2] = material.transmission.z;
|
||||
|
||||
return iplmaterial;
|
||||
}
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#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 Node
|
||||
{
|
||||
GDCLASS( SteamAudio, Node ) // NOLINT(modernize-use-auto, hicpp-use-auto)
|
||||
|
||||
private:
|
||||
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;}
|
||||
|
||||
};
|
||||
61
src/steam_audio.hpp
Normal file
61
src/steam_audio.hpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#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/wrapped.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_listener.hpp"
|
||||
#include "steam_audio_material.hpp"
|
||||
|
||||
using namespace godot;
|
||||
|
||||
enum ProxyMode {
|
||||
PROXY_NONE = 0,
|
||||
PROXY_AUTO = 1,
|
||||
PROXY_CUSTOM = 2,
|
||||
};
|
||||
|
||||
class SteamAudio : public Resource
|
||||
{
|
||||
GDCLASS( SteamAudio, Resource ) // NOLINT(modernize-use-auto, hicpp-use-auto)
|
||||
|
||||
private:
|
||||
static IPLContextSettings ctx_default_settings;
|
||||
static IPLAudioSettings audio_default_settings;
|
||||
static IPLEmbreeDeviceSettings embree_device_default_settings;
|
||||
static IPLSceneSettings scene_default_settings;
|
||||
static IPLSimulationSettings simulation_default_settings;
|
||||
static IPLHRTFSettings hrtf_default_settings;
|
||||
ProjectSettings *proj_settings = ProjectSettings::get_singleton();
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
static SteamAudio *steam_audio;
|
||||
|
||||
SteamAudio();
|
||||
~SteamAudio() override = default;
|
||||
|
||||
static Transform3D ipl_space_to_godot(const IPLCoordinateSpace3 &p_space);
|
||||
static IPLMatrix4x4 transform_to_ipl_matrix(const Transform3D &t);
|
||||
static IPLMaterial to_ipl_material(const SteamMaterial &);
|
||||
|
||||
static IPLCoordinateSpace3 godot_to_ipl_space(const Transform3D &p_transform);
|
||||
|
||||
[[nodiscard]] static IPLEmbreeDeviceSettings get_embree_device_settings() {return embree_device_default_settings;}
|
||||
|
||||
[[nodiscard]] static IPLSceneSettings get_scene_settings() {return scene_default_settings;}
|
||||
|
||||
[[nodiscard]] static IPLSimulationSettings get_simulation_settings() {return simulation_default_settings;}
|
||||
|
||||
[[nodiscard]] static IPLHRTFSettings get_hrtf_settings() {return hrtf_default_settings;}
|
||||
|
||||
[[nodiscard]] static IPLContextSettings get_context_settings() {return ctx_default_settings;}
|
||||
|
||||
};
|
||||
@@ -1,92 +1,283 @@
|
||||
#include "steam_audio_dynamic_mesh.h"
|
||||
#include "steam_audio_globals.h" // holds global_context & global_scene
|
||||
// steam_audio_static_mesh.cpp
|
||||
#include "steam_audio_dynamic_mesh.hpp"
|
||||
|
||||
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);
|
||||
}
|
||||
Vector<SteamAudioDynamicMesh*> SteamAudioDynamicMesh::_instances;
|
||||
|
||||
SteamAudioDynamicMesh::SteamAudioDynamicMesh() {
|
||||
_instances.push_back(this);
|
||||
}
|
||||
|
||||
SteamAudioDynamicMesh::~SteamAudioDynamicMesh() {
|
||||
_instances.erase(this);
|
||||
}
|
||||
|
||||
const Vector<SteamAudioDynamicMesh *> &SteamAudioDynamicMesh::get_all_dynamic_meshes() {
|
||||
return _instances;
|
||||
}
|
||||
|
||||
|
||||
void SteamAudioDynamicMesh::_bind_methods() {
|
||||
// no exposed properties for now
|
||||
ClassDB::bind_method(D_METHOD("set_proxy_mode", "mode"), &SteamAudioDynamicMesh::set_proxy_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_proxy_mode"), &SteamAudioDynamicMesh::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"), &SteamAudioDynamicMesh::set_custom_proxy_mesh);
|
||||
ClassDB::bind_method(D_METHOD("get_custom_proxy_mesh"), &SteamAudioDynamicMesh::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");
|
||||
}
|
||||
|
||||
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);
|
||||
// Setters & getters ---------------------------------------------------
|
||||
void SteamAudioDynamicMesh::set_proxy_mode(int mode) {
|
||||
proxy_mode = mode;
|
||||
}
|
||||
|
||||
void SteamAudioDynamicMesh::_process(double /*delta*/) {
|
||||
if (!instanced_mesh) return;
|
||||
|
||||
// update the world‐space 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);
|
||||
int SteamAudioDynamicMesh::get_proxy_mode() const {
|
||||
return proxy_mode;
|
||||
}
|
||||
|
||||
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 static‐mesh 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*
|
||||
// …you’d 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 it’s ready to be instanced :contentReference[oaicite:1]{index=1}
|
||||
iplSceneCommit(proxy_subscene);
|
||||
void SteamAudioDynamicMesh::set_custom_proxy_mesh(Ref<Mesh> mesh) {
|
||||
custom_proxy_mesh = mesh;
|
||||
}
|
||||
|
||||
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;
|
||||
Ref<Mesh> SteamAudioDynamicMesh::get_custom_proxy_mesh() const {
|
||||
return custom_proxy_mesh;
|
||||
}
|
||||
|
||||
void SteamAudioDynamicMesh::_notification(int what) {
|
||||
Transform3D current;
|
||||
switch (what) {
|
||||
case NOTIFICATION_ENTER_TREE:
|
||||
needs_update=true;
|
||||
break;
|
||||
case NOTIFICATION_EXIT_TREE:
|
||||
break;
|
||||
case NOTIFICATION_TRANSFORM_CHANGED:
|
||||
current = get_global_transform();
|
||||
if (!last_transform.is_equal_approx(current)) {
|
||||
last_transform = current;
|
||||
needs_update=true;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Core functionality --------------------------------------------------
|
||||
Ref<Mesh> SteamAudioDynamicMesh::get_proxy_mesh() {
|
||||
print_line("getting the proxy mesh");
|
||||
print_line(vformat("proxy_mode: %f", proxy_mode));
|
||||
|
||||
MeshInstance3D *parent = cast_to<MeshInstance3D>(get_parent());
|
||||
if (!parent) {
|
||||
print_error("Parent is not a MeshInstance3D!");
|
||||
return {};
|
||||
}
|
||||
Ref<Mesh> target_mesh;
|
||||
Ref<Mesh> source_mesh = parent->get_mesh();
|
||||
if (!source_mesh.is_valid()) {
|
||||
print_error("Source mesh is invalid!");
|
||||
return {};
|
||||
}
|
||||
|
||||
print_line("using parent mesh");
|
||||
print_line(vformat("Source mesh type: %s", source_mesh->get_class()));
|
||||
|
||||
switch (proxy_mode) {
|
||||
case PROXY_NONE:
|
||||
target_mesh = source_mesh;
|
||||
break;
|
||||
case PROXY_AUTO:
|
||||
|
||||
break;
|
||||
|
||||
case PROXY_CUSTOM:
|
||||
|
||||
break;
|
||||
default:
|
||||
print_error("Unknown proxy mode!");
|
||||
return {};
|
||||
}
|
||||
return target_mesh;
|
||||
}
|
||||
void SteamAudioDynamicMesh::update_dynamic_mesh() {
|
||||
IPLMatrix4x4 transform = SteamAudio::transform_to_ipl_matrix(get_global_transform());
|
||||
iplInstancedMeshUpdateTransform(instanced_mesh,global_scene,transform);
|
||||
}
|
||||
|
||||
|
||||
void SteamAudioDynamicMesh::build_mesh(IPLScene scene,IPLContext ipl_context,IPLContextSettings ipl_context_settings) {
|
||||
global_scene=scene;
|
||||
iplSceneCreate(ipl_context,&scene_settings,&subscene);
|
||||
if (!scene) {
|
||||
ERR_PRINT("Invalid IPLScene passed to build_mesh");
|
||||
return;
|
||||
}
|
||||
|
||||
struct SurfaceData {
|
||||
PackedVector3Array vertices;
|
||||
PackedInt32Array indices;
|
||||
IPLMaterial material;
|
||||
};
|
||||
|
||||
Vector<SurfaceData> surface_data;
|
||||
print_line("building a static mesh");
|
||||
Ref<Mesh> mesh = get_proxy_mesh();
|
||||
|
||||
if (mesh.is_null()) {
|
||||
ERR_PRINT("Failed to get valid proxy mesh");
|
||||
return;
|
||||
}
|
||||
print_line("got the mesh");
|
||||
|
||||
for (int surface_idx = 0; surface_idx < mesh->get_surface_count(); surface_idx++) {
|
||||
SurfaceData data;
|
||||
Array arrays = mesh->surface_get_arrays(surface_idx);
|
||||
|
||||
if (arrays.is_empty()) {
|
||||
ERR_PRINT(vformat("Surface %d has no arrays data", surface_idx));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get vertices and indices
|
||||
PackedVector3Array vertices = arrays[Mesh::ARRAY_VERTEX];
|
||||
PackedInt32Array indices = arrays[Mesh::ARRAY_INDEX];
|
||||
|
||||
if (vertices.is_empty()) {
|
||||
ERR_PRINT(vformat("Surface %d has no vertices", surface_idx));
|
||||
continue;
|
||||
}
|
||||
|
||||
// If indices array is empty, create one from vertices
|
||||
if (indices.is_empty()) {
|
||||
indices.resize(vertices.size());
|
||||
for (int i = 0; i < vertices.size(); i++) {
|
||||
indices[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Create triangles if indices count is not a multiple of 3
|
||||
if (indices.size() % 3 != 0) {
|
||||
print_line("Surface " + itos(surface_idx) + " is not triangulated, converting to triangles");
|
||||
PackedInt32Array new_indices;
|
||||
|
||||
// Simple triangulation for convex polygons
|
||||
for (int i = 0; i < indices.size() - 2; i++) {
|
||||
new_indices.push_back(indices[0]);
|
||||
new_indices.push_back(indices[i + 1]);
|
||||
new_indices.push_back(indices[i + 2]);
|
||||
}
|
||||
|
||||
indices = new_indices;
|
||||
}
|
||||
|
||||
data.vertices = vertices;
|
||||
data.indices = indices;
|
||||
|
||||
// Get material for the surface
|
||||
Ref<Material> material = mesh->surface_get_material(surface_idx);
|
||||
if (material.is_valid() && material->has_meta("steam_audio_material")) {
|
||||
Ref<SteamAudioMaterial> steam_audio_material = material->get_meta("steam_audio_material");
|
||||
if (steam_audio_material.is_valid()) {
|
||||
SteamMaterial steam_material = steam_audio_material->get_steam_material();
|
||||
data.material = SteamAudio::to_ipl_material(steam_material);
|
||||
} else {
|
||||
WARN_PRINT(vformat("Invalid Steam Audio material for surface %d", surface_idx));
|
||||
data.material = SteamAudio::to_ipl_material(SteamAudioMaterial::get_default_material());
|
||||
}
|
||||
} else {
|
||||
data.material = SteamAudio::to_ipl_material(SteamAudioMaterial::get_default_material());
|
||||
}
|
||||
|
||||
surface_data.push_back(data);
|
||||
}
|
||||
|
||||
if (surface_data.is_empty()) {
|
||||
ERR_PRINT("No valid surfaces found in mesh");
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate total counts
|
||||
int total_vertices = 0;
|
||||
int total_triangles = 0;
|
||||
for (const SurfaceData &data: surface_data) {
|
||||
total_vertices += data.vertices.size();
|
||||
total_triangles += data.indices.size() / 3;
|
||||
}
|
||||
|
||||
if (total_vertices == 0 || total_triangles == 0) {
|
||||
ERR_PRINT("Mesh has no vertices or triangles");
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare final data arrays
|
||||
PackedFloat32Array final_vertices;
|
||||
PackedInt32Array final_triangles;
|
||||
Vector<IPLMaterial> final_materials;
|
||||
PackedInt32Array material_indices;
|
||||
|
||||
final_vertices.resize(total_vertices * 3);
|
||||
final_triangles.resize(total_triangles * 3);
|
||||
material_indices.resize(total_triangles);
|
||||
|
||||
// Combine all surfaces
|
||||
int vertex_offset = 0;
|
||||
int triangle_offset = 0;
|
||||
int material_index = 0;
|
||||
|
||||
for (const SurfaceData &data: surface_data) {
|
||||
// Copy vertices
|
||||
for (int i = 0; i < data.vertices.size(); i++) {
|
||||
const Vector3 &v = data.vertices[i];
|
||||
final_vertices[vertex_offset * 3 + 0] = v.x;
|
||||
final_vertices[vertex_offset * 3 + 1] = v.y;
|
||||
final_vertices[vertex_offset * 3 + 2] = v.z;
|
||||
vertex_offset++;
|
||||
}
|
||||
|
||||
// Copy triangles
|
||||
int base_vertex = vertex_offset - data.vertices.size();
|
||||
for (int i = 0; i < data.indices.size(); i += 3) {
|
||||
final_triangles[triangle_offset * 3 + 0] = base_vertex + data.indices[i + 0];
|
||||
final_triangles[triangle_offset * 3 + 1] = base_vertex + data.indices[i + 1];
|
||||
final_triangles[triangle_offset * 3 + 2] = base_vertex + data.indices[i + 2];
|
||||
|
||||
material_indices[triangle_offset] = material_index;
|
||||
triangle_offset++;
|
||||
}
|
||||
|
||||
final_materials.push_back(data.material);
|
||||
material_index++;
|
||||
}
|
||||
|
||||
// Create Steam Audio static mesh
|
||||
IPLStaticMeshSettings mesh_settings = {};
|
||||
mesh_settings.numVertices = total_vertices;
|
||||
mesh_settings.numTriangles = total_triangles;
|
||||
mesh_settings.numMaterials = final_materials.size();
|
||||
mesh_settings.vertices = const_cast<IPLVector3 *>(reinterpret_cast<const IPLVector3 *>(final_vertices.ptr()));
|
||||
mesh_settings.triangles = const_cast<IPLTriangle *>(reinterpret_cast<const IPLTriangle *>(final_triangles.ptr()));
|
||||
mesh_settings.materials = const_cast<IPLMaterial *>(final_materials.ptr());
|
||||
mesh_settings.materialIndices = const_cast<IPLint32 *>(material_indices.ptr());
|
||||
|
||||
IPLStaticMesh static_mesh;
|
||||
IPLerror error = iplStaticMeshCreate(subscene, &mesh_settings, &static_mesh);
|
||||
if (error != IPL_STATUS_SUCCESS) {
|
||||
ERR_PRINT(vformat("Failed to create static mesh. Error code: %d", error));
|
||||
return;
|
||||
}
|
||||
|
||||
print_line(vformat("Successfully created static mesh with %d vertices and %d triangles", total_vertices,
|
||||
total_triangles));
|
||||
instanced_mesh_settings.subScene = subscene;
|
||||
instanced_mesh_settings.transform = SteamAudio::transform_to_ipl_matrix(get_global_transform());
|
||||
|
||||
IPLerror imErr = iplInstancedMeshCreate(global_scene,&instanced_mesh_settings,&instanced_mesh);
|
||||
if (imErr != IPL_STATUS_SUCCESS) {
|
||||
ERR_PRINT(vformat("Failed to create instanced mesh. Error code: %d", imErr));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
#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 mesh’s raw geometry
|
||||
void build_subscene();
|
||||
|
||||
public:
|
||||
SteamAudioDynamicMesh();
|
||||
~SteamAudioDynamicMesh() override;
|
||||
|
||||
void _ready() override;
|
||||
void _process(double delta) override;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
};
|
||||
65
src/steam_audio_dynamic_mesh.hpp
Normal file
65
src/steam_audio_dynamic_mesh.hpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include <godot_cpp/classes/array_mesh.hpp>
|
||||
#include <godot_cpp/classes/audio_server.hpp>
|
||||
#include <godot_cpp/classes/convex_polygon_shape3d.hpp>
|
||||
#include <godot_cpp/classes/material.hpp>
|
||||
#include <godot_cpp/classes/mesh.hpp>
|
||||
#include <godot_cpp/classes/mesh_instance3d.hpp>
|
||||
#include <godot_cpp/classes/shape3d.hpp>
|
||||
#include <godot_cpp/core/class_db.hpp>
|
||||
#include <godot_cpp/variant/packed_int32_array.hpp>
|
||||
#include <godot_cpp/variant/packed_vector3_array.hpp>
|
||||
#include <phonon.h> // IPLStaticMesh, IPLStaticMeshSettings, IPLMaterial
|
||||
#include "steam_audio.hpp"
|
||||
#include "steam_audio_material.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
using namespace godot;
|
||||
|
||||
class SteamAudioDynamicMesh : public Node3D {
|
||||
GDCLASS(SteamAudioDynamicMesh, Node3D)
|
||||
|
||||
private:
|
||||
IPLScene subscene=nullptr;
|
||||
IPLScene global_scene=nullptr;
|
||||
IPLSceneSettings scene_settings;
|
||||
IPLInstancedMesh instanced_mesh=nullptr;
|
||||
IPLInstancedMeshSettings instanced_mesh_settings;
|
||||
int proxy_mode = PROXY_NONE;
|
||||
Ref<Mesh> custom_proxy_mesh;
|
||||
|
||||
Transform3D last_transform;
|
||||
StringName bus;
|
||||
|
||||
bool needs_update = false;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
|
||||
public:
|
||||
SteamAudioDynamicMesh();
|
||||
~SteamAudioDynamicMesh() override;
|
||||
|
||||
void _notification(int what);
|
||||
|
||||
// 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;
|
||||
|
||||
bool get_needs_update() {
|
||||
return needs_update;
|
||||
}
|
||||
void build_mesh(IPLScene scene, IPLContext ipl_context, IPLContextSettings ipl_context_settings);
|
||||
static Vector<SteamAudioDynamicMesh*> _instances;
|
||||
static const Vector<SteamAudioDynamicMesh*>& get_all_dynamic_meshes();
|
||||
|
||||
// Regenerates Steam Audio proxies
|
||||
Ref<Mesh> get_proxy_mesh();
|
||||
|
||||
void update_dynamic_mesh();
|
||||
};
|
||||
@@ -1,7 +0,0 @@
|
||||
// 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;
|
||||
@@ -1,46 +1,52 @@
|
||||
#include "steam_audio_listener.h"
|
||||
|
||||
#include "phonon.h"
|
||||
#include "steam_audio_listener.hpp"
|
||||
|
||||
using namespace godot;
|
||||
|
||||
SteamAudioListener::SteamAudioListener() = default;
|
||||
SteamAudioListener::~SteamAudioListener() = default;
|
||||
SteamAudioListener* SteamAudioListener::listener;
|
||||
|
||||
SteamAudioListener::SteamAudioListener() {
|
||||
listener=this;
|
||||
}
|
||||
|
||||
SteamAudioListener::~SteamAudioListener() {
|
||||
listener=nullptr;
|
||||
}
|
||||
|
||||
void SteamAudioListener::_bind_methods() {
|
||||
|
||||
}
|
||||
|
||||
void SteamAudioListener::_process(double delta) {
|
||||
SteamAudioListener* SteamAudioListener::get_listener() {
|
||||
return listener;
|
||||
}
|
||||
|
||||
void SteamAudioListener::_notification(int p_what) {
|
||||
Transform3D current_transform;
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_TRANSFORM_CHANGED:
|
||||
current_transform = get_global_transform();
|
||||
if (!last_transform.is_equal_approx(current_transform)) {
|
||||
needs_update = true;
|
||||
last_transform = current_transform;
|
||||
}
|
||||
break;
|
||||
case NOTIFICATION_ENTER_TREE:
|
||||
needs_update = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SteamAudioListener::update_listener(IPLSimulator iplsim, IPLSimulationSharedInputs iplsiminputs) {
|
||||
needs_update=false;
|
||||
|
||||
Transform3D transform = get_global_transform();
|
||||
|
||||
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;
|
||||
}
|
||||
IPLCoordinateSpace3 space = SteamAudio::godot_to_ipl_space(transform);
|
||||
|
||||
iplsiminputs.listener=space;
|
||||
|
||||
iplSimulatorSetSharedInputs(iplsim,IPL_SIMULATIONFLAGS_DIRECT,&iplsiminputs);
|
||||
iplSimulatorSetSharedInputs(iplsim,IPL_SIMULATIONFLAGS_PATHING,&iplsiminputs);
|
||||
iplSimulatorSetSharedInputs(iplsim,IPL_SIMULATIONFLAGS_REFLECTIONS,&iplsiminputs);
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include<godot_cpp/godot.hpp>
|
||||
#include <godot_cpp/classes/audio_listener3d.hpp>
|
||||
#include <godot_cpp/core/class_db.hpp>
|
||||
#include <godot_cpp/classes/wrapped.hpp>
|
||||
|
||||
using namespace godot;
|
||||
|
||||
class SteamAudioListener:public AudioListener3D
|
||||
{
|
||||
GDCLASS(SteamAudioListener,AudioListener3D)
|
||||
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _process(double p_delta) override;
|
||||
public:
|
||||
SteamAudioListener();
|
||||
~SteamAudioListener() override;
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
41
src/steam_audio_listener.hpp
Normal file
41
src/steam_audio_listener.hpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <godot_cpp/classes/audio_listener3d.hpp>
|
||||
#include <godot_cpp/classes/wrapped.hpp>
|
||||
#include <godot_cpp/core/class_db.hpp>
|
||||
#include <godot_cpp/godot.hpp>
|
||||
#include "steam_audio.hpp"
|
||||
#include "phonon.h"
|
||||
|
||||
using namespace godot;
|
||||
|
||||
class SteamAudioListener:public AudioListener3D
|
||||
{
|
||||
GDCLASS(SteamAudioListener,AudioListener3D)
|
||||
private:
|
||||
bool needs_update = false;
|
||||
Transform3D last_transform;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
public:
|
||||
bool get_needs_update() {
|
||||
return needs_update;
|
||||
}
|
||||
static SteamAudioListener *listener;
|
||||
SteamAudioListener();
|
||||
~SteamAudioListener() override;
|
||||
|
||||
void _notification(int p_what);
|
||||
|
||||
static SteamAudioListener* get_listener();
|
||||
void update_listener(IPLSimulator iplsim, IPLSimulationSharedInputs iplsiminputs);
|
||||
void set_listener_position(const Vector3 &p_position);
|
||||
void set_listener_orientation(const Vector3 &p_forward, const Vector3 &p_up);
|
||||
void set_listener_velocity(const Vector3 &p_velocity);
|
||||
void set_listener_gain(const float p_gain);
|
||||
void set_listener_doppler_factor(const float p_factor);
|
||||
void set_listener_distance_model(const int p_model);
|
||||
void set_listener_flags(const int p_flags);
|
||||
void set_listener_channel_count(const int p_count);
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "steam_audio_material.h"
|
||||
#include "steam_audio_material.hpp"
|
||||
|
||||
using namespace godot;
|
||||
|
||||
@@ -14,26 +14,22 @@ void SteamAudioMaterial::_bind_methods() {
|
||||
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;
|
||||
SteamMaterial SteamAudioMaterial::default_material = {
|
||||
Vector3(0.10f,0.20f,0.30f),
|
||||
0.05f,
|
||||
Vector3(0.100f,0.050f,0.030f)
|
||||
};
|
||||
|
||||
material.scattering = scattering;
|
||||
|
||||
material.transmission[0] = transmission.x;
|
||||
material.transmission[1] = transmission.y;
|
||||
material.transmission[2] = transmission.z;
|
||||
|
||||
return material;
|
||||
Vector3 SteamAudioMaterial::get_absorption() const {return material.absorption;}
|
||||
void SteamAudioMaterial::set_absorption(Vector3 value) { material.absorption = value; }
|
||||
float SteamAudioMaterial::get_scattering() const {return material.scattering;}
|
||||
void SteamAudioMaterial::set_scattering(float value) { material.scattering = value; }
|
||||
Vector3 SteamAudioMaterial::get_transmission() const {return material.transmission;}
|
||||
void SteamAudioMaterial::set_transmission(Vector3 value) { material.transmission = value; }
|
||||
const SteamMaterial &SteamAudioMaterial::get_default_material() {
|
||||
return default_material;
|
||||
}
|
||||
|
||||
|
||||
SteamMaterial SteamAudioMaterial::get_steam_material() const {
|
||||
return material;
|
||||
}
|
||||
@@ -1,27 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <godot_cpp/variant/vector3.hpp>
|
||||
#include <godot_cpp/classes/resource.hpp>
|
||||
#include "godot_cpp/godot.hpp"
|
||||
#include <godot_cpp/core/class_db.hpp>
|
||||
#include "godot_cpp/classes/wrapped.hpp"
|
||||
#include "godot_cpp/godot.hpp"
|
||||
#include "phonon.h"
|
||||
|
||||
using namespace godot;
|
||||
|
||||
struct SteamMaterial {
|
||||
Vector3 absorption;
|
||||
float scattering;
|
||||
Vector3 transmission;
|
||||
};
|
||||
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);
|
||||
SteamMaterial material;
|
||||
|
||||
public:
|
||||
static SteamMaterial default_material;
|
||||
static const SteamMaterial& get_default_material();
|
||||
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;
|
||||
SteamMaterial get_steam_material() const;
|
||||
};
|
||||
16
src/steam_audio_server.cpp
Normal file
16
src/steam_audio_server.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include "steam_audio_server.hpp"
|
||||
|
||||
using namespace godot;
|
||||
|
||||
SteamAudioServer::SteamAudioServer() {
|
||||
|
||||
}
|
||||
|
||||
SteamAudioServer::~SteamAudioServer() {
|
||||
|
||||
}
|
||||
|
||||
// Core Godot Methods
|
||||
void SteamAudioServer::_bind_methods() {
|
||||
|
||||
}
|
||||
28
src/steam_audio_server.hpp
Normal file
28
src/steam_audio_server.hpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <godot_cpp/classes/audio_frame.hpp>
|
||||
#include <godot_cpp/classes/scene_tree.hpp>
|
||||
#include <godot_cpp/classes/wrapped.hpp>
|
||||
#include <godot_cpp/core/class_db.hpp>
|
||||
#include <godot_cpp/godot.hpp>
|
||||
#include <phonon.h>
|
||||
#include <steam_audio.hpp>
|
||||
#include "steam_audio_dynamic_mesh.hpp"
|
||||
#include "steam_audio_source.hpp"
|
||||
#include "steam_audio_static_mesh.hpp"
|
||||
|
||||
using namespace godot;
|
||||
|
||||
|
||||
class SteamAudioServer : public Object {
|
||||
GDCLASS(SteamAudioServer, Object) // Godot class declaration macro
|
||||
private:
|
||||
|
||||
public:
|
||||
SteamAudioServer(); // Constructor
|
||||
~SteamAudioServer() override; // Destructor
|
||||
|
||||
protected:
|
||||
static void _bind_methods(); // Bind methods to Godot
|
||||
|
||||
};
|
||||
@@ -1,22 +1,51 @@
|
||||
#include "steam_audio_source.h"
|
||||
|
||||
#include "steam_audio_globals.h"
|
||||
#include "steam_audio_source.hpp"
|
||||
|
||||
using namespace godot;
|
||||
|
||||
// Static vector to track all active audio sources
|
||||
Vector<SteamAudioSource *> SteamAudioSource::_instances;
|
||||
|
||||
// Constructor/Destructor
|
||||
SteamAudioSource::SteamAudioSource() {
|
||||
iplSourceCreate(sa->get_simulator(),&settings,pSource);
|
||||
// Disable default attenuation since Steam Audio handles it
|
||||
set_attenuation_model(ATTENUATION_DISABLED);
|
||||
_instances.push_back(this);
|
||||
}
|
||||
|
||||
SteamAudioSource::~SteamAudioSource() {
|
||||
iplSourceRelease(pSource);
|
||||
// Remove this instance from tracking vector
|
||||
_instances.erase(this);
|
||||
}
|
||||
|
||||
// Static method to get all active audio sources
|
||||
const Vector<SteamAudioSource *> &SteamAudioSource::get_all_sources() {
|
||||
return _instances;
|
||||
}
|
||||
|
||||
// Godot virtual methods
|
||||
void SteamAudioSource::_bind_methods() {
|
||||
// Bind directivity methods to make them accessible from GDScript
|
||||
ClassDB::bind_method(D_METHOD("set_dipole_weight", "weight"), &SteamAudioSource::set_dipole_weight);
|
||||
ClassDB::bind_method(D_METHOD("get_dipole_weight"), &SteamAudioSource::get_dipole_weight);
|
||||
ClassDB::bind_method(D_METHOD("set_dipole_power", "power"), &SteamAudioSource::set_dipole_power);
|
||||
ClassDB::bind_method(D_METHOD("get_dipole_power"), &SteamAudioSource::get_dipole_power);
|
||||
ClassDB::bind_method(D_METHOD("set_directivity_enabled", "arg"), &SteamAudioSource::set_directivity_enabled);
|
||||
ClassDB::bind_method(D_METHOD("is_directivity_enabled"), &SteamAudioSource::get_is_directivity_enabled);
|
||||
|
||||
// Add properties to editor interface
|
||||
ADD_GROUP("Directivity", "directivity_");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "directivity_enabled"), "set_directivity_enabled",
|
||||
"is_directivity_enabled");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "directivity_dipole_weight", PROPERTY_HINT_RANGE, "0,1,0.01"),
|
||||
"set_dipole_weight", "get_dipole_weight");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "directivity_dipole_power", PROPERTY_HINT_RANGE, "0,10,0.1"),
|
||||
"set_dipole_power", "get_dipole_power");
|
||||
}
|
||||
|
||||
void SteamAudioSource::_process(double delta) {
|
||||
update_position();
|
||||
}
|
||||
bool SteamAudioSource::update_position() {
|
||||
IPLCoordinateSpace3 current_pos = sa->godot_to_ipl_space(get_transform());
|
||||
|
||||
iplSource
|
||||
}
|
||||
// Hide default AudioStreamPlayer3D properties that we don't use
|
||||
void SteamAudioSource::_validate_property(PropertyInfo &property) const {
|
||||
if (property.name == StringName("attenuation_model") || property.name == StringName("unit_size") ||
|
||||
property.name.begins_with("attenuation") || property.name.begins_with("emission_")) {
|
||||
property.usage = PROPERTY_USAGE_NO_EDITOR;
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#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: public Node3D
|
||||
{
|
||||
GDCLASS(SteamAudioSource,Node3D)
|
||||
|
||||
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();
|
||||
|
||||
};
|
||||
63
src/steam_audio_source.hpp
Normal file
63
src/steam_audio_source.hpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include <godot_cpp/classes/gd_extension.hpp>
|
||||
#include <godot_cpp/classes/audio_server.hpp>
|
||||
#include <godot_cpp/core/class_db.hpp>
|
||||
#include <godot_cpp/godot.hpp>
|
||||
#include "phonon.h"
|
||||
#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/classes/node3d.hpp>
|
||||
#include "steam_audio.hpp"
|
||||
|
||||
|
||||
using namespace godot;
|
||||
class SteamAudioSource: public AudioStreamPlayer3D
|
||||
{
|
||||
GDCLASS(SteamAudioSource,AudioStreamPlayer3D) // NOLINT(*-unhandled-self-assignment)
|
||||
|
||||
AudioServer *audio_server = AudioServer::get_singleton();
|
||||
bool is_directivity_enabled = true;
|
||||
float dipole_weight=0.0f;
|
||||
float dipole_power=1.0f;
|
||||
int mix_rate=0;
|
||||
int buffer_size=0;
|
||||
|
||||
IPLContext ipl_context = nullptr;
|
||||
IPLSource *pSource = nullptr;
|
||||
IPLSourceSettings settings{};
|
||||
IPLSimulationInputs inputs{};
|
||||
IPLDirectivity directivity{};
|
||||
|
||||
Transform3D last_transform;
|
||||
bool needs_update = false;
|
||||
|
||||
Ref<AudioStreamGeneratorPlayback> playback;
|
||||
Vector<AudioFrame> audio_buffer;
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _validate_property(PropertyInfo &property) const;
|
||||
public:
|
||||
void _notification(int p_what);
|
||||
void _process(double p_delta) override;
|
||||
|
||||
SteamAudioSource();
|
||||
~SteamAudioSource() override;
|
||||
|
||||
void update_source();
|
||||
|
||||
void set_directivity_enabled(bool enabled){is_directivity_enabled=enabled;}
|
||||
void set_dipole_weight(float w){dipole_weight=w;}
|
||||
void set_dipole_power(float p){dipole_power=p;}
|
||||
|
||||
[[nodiscard]] float get_dipole_weight() const{return dipole_weight;}
|
||||
[[nodiscard]] float get_dipole_power() const{return dipole_power;}
|
||||
[[nodiscard]] bool get_is_directivity_enabled() const{return is_directivity_enabled;}
|
||||
|
||||
static Vector<SteamAudioSource*> _instances;
|
||||
static const Vector<SteamAudioSource*>& get_all_sources();
|
||||
|
||||
[[nodiscard]] bool get_needs_update() const {
|
||||
return needs_update;}
|
||||
};
|
||||
@@ -1,25 +1,23 @@
|
||||
// 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
|
||||
#include "steam_audio_static_mesh.hpp"
|
||||
|
||||
using namespace godot;
|
||||
|
||||
SteamAudioStaticMesh::SteamAudioStaticMesh() = default;
|
||||
Vector<SteamAudioStaticMesh*> SteamAudioStaticMesh::_instances;
|
||||
|
||||
SteamAudioStaticMesh::SteamAudioStaticMesh() {
|
||||
_instances.push_back(this);
|
||||
}
|
||||
|
||||
SteamAudioStaticMesh::~SteamAudioStaticMesh() {
|
||||
// release any previously created Steam Audio meshes
|
||||
for (auto &sm : static_meshes) {
|
||||
iplStaticMeshRelease(&sm);
|
||||
}
|
||||
static_meshes.clear();
|
||||
_instances.erase(this);
|
||||
}
|
||||
|
||||
const Vector<SteamAudioStaticMesh *> &SteamAudioStaticMesh::get_all_static_meshes() {
|
||||
return _instances;
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
@@ -30,19 +28,13 @@ void SteamAudioStaticMesh::_bind_methods() {
|
||||
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;
|
||||
}
|
||||
@@ -50,132 +42,217 @@ int SteamAudioStaticMesh::get_proxy_mode() const {
|
||||
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();
|
||||
Ref<Mesh> SteamAudioStaticMesh::get_proxy_mesh() {
|
||||
print_line("getting the proxy mesh");
|
||||
print_line(vformat("proxy_mode: %f", proxy_mode));
|
||||
|
||||
MeshInstance3D* parent = cast_to<MeshInstance3D>(get_parent());
|
||||
if (!parent) {
|
||||
print_error("Parent is not a MeshInstance3D!");
|
||||
return {};
|
||||
}
|
||||
Ref<Mesh> target_mesh;
|
||||
Ref<Mesh> source_mesh = parent->get_mesh();
|
||||
if (!source_mesh.is_valid()) {
|
||||
print_error("Source mesh is invalid!");
|
||||
return {};
|
||||
}
|
||||
|
||||
print_line("using parent mesh");
|
||||
print_line(vformat("Source mesh type: %s", source_mesh->get_class()));
|
||||
|
||||
// 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();
|
||||
target_mesh = source_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;
|
||||
case PROXY_AUTO:
|
||||
|
||||
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;
|
||||
print_error("Unknown proxy mode!");
|
||||
return {};
|
||||
}
|
||||
return target_mesh;
|
||||
|
||||
if (!target.is_valid()) {
|
||||
ERR_PRINT("SteamAudioStaticMesh: target mesh invalid");
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SteamAudioStaticMesh::build_mesh(IPLScene scene) {
|
||||
if (!scene) {
|
||||
ERR_PRINT("Invalid IPLScene passed to build_mesh");
|
||||
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];
|
||||
struct SurfaceData {
|
||||
PackedVector3Array vertices;
|
||||
PackedInt32Array indices;
|
||||
IPLMaterial material;
|
||||
};
|
||||
|
||||
// --- 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;
|
||||
Vector<SurfaceData> surface_data;
|
||||
print_line("building a static mesh");
|
||||
Ref<Mesh> mesh = get_proxy_mesh();
|
||||
|
||||
// --- fill out static mesh settings ---
|
||||
IPLStaticMeshSettings settings{};
|
||||
if (mesh.is_null()) {
|
||||
ERR_PRINT("Failed to get valid proxy mesh");
|
||||
return;
|
||||
}
|
||||
print_line("got the mesh");
|
||||
|
||||
// Number of vertices
|
||||
settings.numVertices = verts.size();
|
||||
for (int surface_idx = 0; surface_idx < mesh->get_surface_count(); surface_idx++) {
|
||||
SurfaceData data;
|
||||
Array arrays = mesh->surface_get_arrays(surface_idx);
|
||||
|
||||
// reinterpret Godot’s Vector3 buffer as Steam Audio’s 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");
|
||||
if (arrays.is_empty()) {
|
||||
ERR_PRINT(vformat("Surface %d has no arrays data", surface_idx));
|
||||
continue;
|
||||
}
|
||||
iplStaticMeshAdd(sm, global_scene);
|
||||
static_meshes.push_back(sm);*/
|
||||
|
||||
// Get vertices and indices
|
||||
PackedVector3Array vertices = arrays[Mesh::ARRAY_VERTEX];
|
||||
PackedInt32Array indices = arrays[Mesh::ARRAY_INDEX];
|
||||
|
||||
if (vertices.is_empty()) {
|
||||
ERR_PRINT(vformat("Surface %d has no vertices", surface_idx));
|
||||
continue;
|
||||
}
|
||||
|
||||
// If indices array is empty, create one from vertices
|
||||
if (indices.is_empty()) {
|
||||
indices.resize(vertices.size());
|
||||
for (int i = 0; i < vertices.size(); i++) {
|
||||
indices[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Create triangles if indices count is not a multiple of 3
|
||||
if (indices.size() % 3 != 0) {
|
||||
print_line("Surface " + itos(surface_idx) + " is not triangulated, converting to triangles");
|
||||
PackedInt32Array new_indices;
|
||||
|
||||
// Simple triangulation for convex polygons
|
||||
for (int i = 0; i < indices.size() - 2; i++) {
|
||||
new_indices.push_back(indices[0]);
|
||||
new_indices.push_back(indices[i + 1]);
|
||||
new_indices.push_back(indices[i + 2]);
|
||||
}
|
||||
|
||||
indices = new_indices;
|
||||
}
|
||||
|
||||
data.vertices = vertices;
|
||||
data.indices = indices;
|
||||
|
||||
// Get material for the surface
|
||||
Ref<Material> material = mesh->surface_get_material(surface_idx);
|
||||
if (material.is_valid() && material->has_meta("steam_audio_material")) {
|
||||
Ref<SteamAudioMaterial> steam_audio_material = material->get_meta("steam_audio_material");
|
||||
if (steam_audio_material.is_valid()) {
|
||||
SteamMaterial steam_material = steam_audio_material->get_steam_material();
|
||||
data.material = SteamAudio::to_ipl_material(steam_material);
|
||||
} else {
|
||||
WARN_PRINT(vformat("Invalid Steam Audio material for surface %d", surface_idx));
|
||||
data.material = SteamAudio::to_ipl_material(SteamAudioMaterial::get_default_material());
|
||||
}
|
||||
} else {
|
||||
data.material = SteamAudio::to_ipl_material(SteamAudioMaterial::get_default_material());
|
||||
}
|
||||
|
||||
surface_data.push_back(data);
|
||||
}
|
||||
|
||||
// 4) Commit all additions at once
|
||||
// iplSceneCommit(global_scene);
|
||||
if (surface_data.is_empty()) {
|
||||
ERR_PRINT("No valid surfaces found in mesh");
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate total counts
|
||||
int total_vertices = 0;
|
||||
int total_triangles = 0;
|
||||
for (const SurfaceData &data: surface_data) {
|
||||
total_vertices += data.vertices.size();
|
||||
total_triangles += data.indices.size() / 3;
|
||||
}
|
||||
|
||||
if (total_vertices == 0 || total_triangles == 0) {
|
||||
ERR_PRINT("Mesh has no vertices or triangles");
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare final data arrays
|
||||
PackedFloat32Array final_vertices;
|
||||
PackedInt32Array final_triangles;
|
||||
Vector<IPLMaterial> final_materials;
|
||||
PackedInt32Array material_indices;
|
||||
|
||||
final_vertices.resize(total_vertices * 3);
|
||||
final_triangles.resize(total_triangles * 3);
|
||||
material_indices.resize(total_triangles);
|
||||
|
||||
// Combine all surfaces
|
||||
int vertex_offset = 0;
|
||||
int triangle_offset = 0;
|
||||
int material_index = 0;
|
||||
|
||||
for (const SurfaceData &data: surface_data) {
|
||||
// Copy vertices
|
||||
for (int i = 0; i < data.vertices.size(); i++) {
|
||||
const Vector3 &v = data.vertices[i];
|
||||
final_vertices[vertex_offset * 3 + 0] = v.x;
|
||||
final_vertices[vertex_offset * 3 + 1] = v.y;
|
||||
final_vertices[vertex_offset * 3 + 2] = v.z;
|
||||
vertex_offset++;
|
||||
}
|
||||
|
||||
// Copy triangles
|
||||
int base_vertex = vertex_offset - data.vertices.size();
|
||||
for (int i = 0; i < data.indices.size(); i += 3) {
|
||||
final_triangles[triangle_offset * 3 + 0] = base_vertex + data.indices[i + 0];
|
||||
final_triangles[triangle_offset * 3 + 1] = base_vertex + data.indices[i + 1];
|
||||
final_triangles[triangle_offset * 3 + 2] = base_vertex + data.indices[i + 2];
|
||||
|
||||
material_indices[triangle_offset] = material_index;
|
||||
triangle_offset++;
|
||||
}
|
||||
|
||||
final_materials.push_back(data.material);
|
||||
material_index++;
|
||||
}
|
||||
|
||||
// Create Steam Audio static mesh
|
||||
IPLStaticMeshSettings mesh_settings = {};
|
||||
mesh_settings.numVertices = total_vertices;
|
||||
mesh_settings.numTriangles = total_triangles;
|
||||
mesh_settings.numMaterials = final_materials.size();
|
||||
mesh_settings.vertices = const_cast<IPLVector3 *>(reinterpret_cast<const IPLVector3 *>(final_vertices.ptr()));
|
||||
mesh_settings.triangles = const_cast<IPLTriangle *>(reinterpret_cast<const IPLTriangle *>(final_triangles.ptr()));
|
||||
mesh_settings.materials = const_cast<IPLMaterial *>(final_materials.ptr());
|
||||
mesh_settings.materialIndices = const_cast<IPLint32 *>(material_indices.ptr());
|
||||
|
||||
IPLStaticMesh static_mesh;
|
||||
IPLerror error = iplStaticMeshCreate(scene, &mesh_settings, &static_mesh);
|
||||
if (error != IPL_STATUS_SUCCESS) {
|
||||
ERR_PRINT(vformat("Failed to create static mesh. Error code: %d", error));
|
||||
return;
|
||||
}
|
||||
|
||||
print_line(vformat("Successfully created static mesh with %d vertices and %d triangles", total_vertices,
|
||||
total_triangles));
|
||||
}
|
||||
|
||||
|
||||
void SteamAudioStaticMesh::update_static_mesh() {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
// 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/convex_polygon_shape3d.hpp>
|
||||
#include <godot_cpp/classes/material.hpp>
|
||||
|
||||
#include "steam_audio_material.h"
|
||||
#include <godot_cpp/classes/mesh.hpp>
|
||||
#include <godot_cpp/classes/mesh_instance3d.hpp>
|
||||
#include <godot_cpp/classes/shape3d.hpp>
|
||||
#include <godot_cpp/core/class_db.hpp>
|
||||
#include <godot_cpp/variant/packed_int32_array.hpp>
|
||||
#include <godot_cpp/variant/packed_vector3_array.hpp>
|
||||
#include <phonon.h> // IPLStaticMesh, IPLStaticMeshSettings, IPLMaterial
|
||||
#include "steam_audio.hpp"
|
||||
#include "steam_audio_material.hpp"
|
||||
|
||||
using namespace godot;
|
||||
|
||||
// Proxy generation modes
|
||||
enum ProxyMode {
|
||||
PROXY_NONE = 0,
|
||||
PROXY_CONVEX = 1,
|
||||
PROXY_CUSTOM = 2,
|
||||
};
|
||||
|
||||
|
||||
class SteamAudioStaticMesh : public Node3D {
|
||||
GDCLASS(SteamAudioStaticMesh, Node3D)
|
||||
@@ -26,9 +25,10 @@ class SteamAudioStaticMesh : public Node3D {
|
||||
private:
|
||||
int proxy_mode = PROXY_NONE;
|
||||
Ref<Mesh> custom_proxy_mesh;
|
||||
Ref<SteamAudioMaterial> default_material;
|
||||
Vector<IPLStaticMesh> static_meshes;
|
||||
|
||||
bool needs_update = false;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
@@ -44,9 +44,14 @@ public:
|
||||
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;
|
||||
bool get_needs_update() {
|
||||
return needs_update;
|
||||
}
|
||||
void update_static_mesh();
|
||||
void build_mesh(IPLScene scene);
|
||||
static Vector<SteamAudioStaticMesh*> _instances;
|
||||
static const Vector<SteamAudioStaticMesh*>& get_all_static_meshes();
|
||||
|
||||
// Regenerates Steam Audio proxies
|
||||
void generate_proxy_mesh();
|
||||
Ref<Mesh> get_proxy_mesh();
|
||||
};
|
||||
Reference in New Issue
Block a user