did things
This commit is contained in:
8
.run/SteamAudioGodot.run.xml
Normal file
8
.run/SteamAudioGodot.run.xml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="SteamAudioGodot" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="-e" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" WORKING_DIR="file://$PROJECT_DIR$/demo" PASS_PARENT_ENVS_2="true" PROJECT_NAME="SteamAudioGodot" TARGET_NAME="SteamAudioGodot" CONFIG_NAME="Debug" RUN_PATH="$PROJECT_DIR$/../godot-engine/bin/godot.windows.editor.dev.x86_64.console.exe">
|
||||||
|
<method v="2">
|
||||||
|
<option name="CMake.Install" enabled="true" />
|
||||||
|
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
@@ -134,7 +134,7 @@ target_include_directories(${PROJECT_NAME} PRIVATE
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Installation setup
|
# Installation setup
|
||||||
set(INSTALL_DIR "${CMAKE_SOURCE_DIR}/install/${PROJECT_NAME}/")
|
set(INSTALL_DIR "${CMAKE_SOURCE_DIR}/demo/addons/${PROJECT_NAME}/")
|
||||||
message(STATUS "Install directory: ${INSTALL_DIR}")
|
message(STATUS "Install directory: ${INSTALL_DIR}")
|
||||||
install(TARGETS ${PROJECT_NAME}
|
install(TARGETS ${PROJECT_NAME}
|
||||||
RUNTIME DESTINATION ${INSTALL_DIR}/${LIB_DIR}
|
RUNTIME DESTINATION ${INSTALL_DIR}/${LIB_DIR}
|
||||||
|
|||||||
4
demo/.editorconfig
Normal file
4
demo/.editorconfig
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
2
demo/.gitattributes
vendored
Normal file
2
demo/.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Normalize EOL for all files that Git considers text files.
|
||||||
|
* text=auto eol=lf
|
||||||
3
demo/.gitignore
vendored
Normal file
3
demo/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Godot 4+ specific ignores
|
||||||
|
.godot/
|
||||||
|
/android/
|
||||||
1
demo/icon.svg
Normal file
1
demo/icon.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><rect width="124" height="124" x="2" y="2" fill="#363d52" stroke="#212532" stroke-width="4" rx="14"/><g fill="#fff" transform="translate(12.322 12.322)scale(.101)"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 814 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H446l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c0 34 58 34 58 0v-86c0-34-58-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042" transform="translate(12.322 12.322)scale(.101)"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 995 B |
43
demo/icon.svg.import
Normal file
43
demo/icon.svg.import
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://ch0aljsxfwrd6"
|
||||||
|
path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://icon.svg"
|
||||||
|
dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
||||||
15
demo/project.godot
Normal file
15
demo/project.godot
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
; Engine configuration file.
|
||||||
|
; It's best edited using the editor UI and not directly,
|
||||||
|
; since the parameters that go here are not all obvious.
|
||||||
|
;
|
||||||
|
; Format:
|
||||||
|
; [section] ; section goes between []
|
||||||
|
; param=value ; assign values to parameters
|
||||||
|
|
||||||
|
config_version=5
|
||||||
|
|
||||||
|
[application]
|
||||||
|
|
||||||
|
config/name="demo"
|
||||||
|
config/features=PackedStringArray("4.5", "Forward Plus")
|
||||||
|
config/icon="res://icon.svg"
|
||||||
@@ -3,19 +3,14 @@
|
|||||||
target_sources( ${PROJECT_NAME}
|
target_sources( ${PROJECT_NAME}
|
||||||
PRIVATE
|
PRIVATE
|
||||||
register_types.cpp
|
register_types.cpp
|
||||||
register_types.h
|
|
||||||
steam_audio.cpp
|
steam_audio.cpp
|
||||||
steam_audio.h
|
steam_audio_server.cpp
|
||||||
steam_audio_material.cpp
|
steam_audio_material.cpp
|
||||||
steam_audio_material.h
|
|
||||||
steam_audio_listener.h
|
|
||||||
steam_audio_listener.cpp
|
steam_audio_listener.cpp
|
||||||
steam_audio_source.cpp
|
steam_audio_source.cpp
|
||||||
steam_audio_source.h
|
|
||||||
steam_audio_dynamic_mesh.h
|
|
||||||
steam_audio_dynamic_mesh.cpp
|
steam_audio_dynamic_mesh.cpp
|
||||||
steam_audio_static_mesh.cpp
|
steam_audio_static_mesh.cpp
|
||||||
steam_audio_static_mesh.h
|
steam_audio_source.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories( ${PROJECT_NAME}
|
target_include_directories( ${PROJECT_NAME}
|
||||||
|
|||||||
@@ -1,29 +1,36 @@
|
|||||||
#include "register_types.h"
|
#include "register_types.hpp"
|
||||||
|
|
||||||
using namespace godot;
|
using namespace godot;
|
||||||
|
|
||||||
|
SteamAudioServer *srv;
|
||||||
|
|
||||||
void initialize_steam_audio(ModuleInitializationLevel p_level) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) {
|
||||||
|
GDREGISTER_CLASS(SteamAudioServer);
|
||||||
|
srv = memnew(SteamAudioServer);
|
||||||
|
}
|
||||||
|
if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) {
|
||||||
register_steam_audio_settings();
|
register_steam_audio_settings();
|
||||||
GDREGISTER_CLASS(SteamAudio);
|
GDREGISTER_RUNTIME_CLASS(SteamAudioMaterial);
|
||||||
GDREGISTER_CLASS(SteamAudioMaterial);
|
|
||||||
GDREGISTER_RUNTIME_CLASS(SteamAudioListener);
|
GDREGISTER_RUNTIME_CLASS(SteamAudioListener);
|
||||||
GDREGISTER_RUNTIME_CLASS(SteamAudioSource);
|
GDREGISTER_RUNTIME_CLASS(SteamAudioSource);
|
||||||
GDREGISTER_RUNTIME_CLASS(SteamAudioStaticMesh)
|
GDREGISTER_RUNTIME_CLASS(SteamAudioStaticMesh);
|
||||||
GDREGISTER_RUNTIME_CLASS(SteamAudioDynamicMesh)
|
GDREGISTER_RUNTIME_CLASS(SteamAudioDynamicMesh);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void uninitialize_steam_audio(ModuleInitializationLevel p_level) {
|
void uninitialize_steam_audio(ModuleInitializationLevel p_level) {
|
||||||
if (p_level!=MODULE_INITIALIZATION_LEVEL_SCENE) {
|
if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) {
|
||||||
return;
|
memdelete(srv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void register_steam_audio_settings() {
|
void register_steam_audio_settings() {
|
||||||
ProjectSettings *settings = ProjectSettings::get_singleton();
|
|
||||||
|
|
||||||
|
ProjectSettings *settings = ProjectSettings::get_singleton();
|
||||||
{//Raytracer enum
|
{//Raytracer enum
|
||||||
settings->set("steam_audio/ray_tracer/RayTracer",0);
|
settings->set("steam_audio/ray_tracer/RayTracer",0);
|
||||||
Dictionary info;
|
Dictionary info;
|
||||||
@@ -33,7 +40,7 @@ void register_steam_audio_settings() {
|
|||||||
info["hint_string"] = "Steam RT,Embree RT";
|
info["hint_string"] = "Steam RT,Embree RT";
|
||||||
info["usage"] = PROPERTY_USAGE_DEFAULT;
|
info["usage"] = PROPERTY_USAGE_DEFAULT;
|
||||||
settings->add_property_info(info);
|
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
|
{//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;
|
Dictionary info;
|
||||||
info["name"] = "steam_audio/spatializer/HRTF/Volume";
|
info["name"] = "steam_audio/spatializer/HRTF/Volume";
|
||||||
info["type"] = Variant::FLOAT;
|
info["type"] = Variant::FLOAT;
|
||||||
@@ -57,9 +88,42 @@ void register_steam_audio_settings() {
|
|||||||
info["hint_string"] = "0.0,1.0,.01,slider";
|
info["hint_string"] = "0.0,1.0,.01,slider";
|
||||||
info["usage"] = PROPERTY_USAGE_DEFAULT;
|
info["usage"] = PROPERTY_USAGE_DEFAULT;
|
||||||
settings->add_property_info(info);
|
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" {
|
extern "C" {
|
||||||
|
|||||||
@@ -3,12 +3,13 @@
|
|||||||
#include <godot_cpp/classes/project_settings.hpp>
|
#include <godot_cpp/classes/project_settings.hpp>
|
||||||
#include <godot_cpp/core/class_db.hpp>
|
#include <godot_cpp/core/class_db.hpp>
|
||||||
|
|
||||||
#include "steam_audio.h"
|
#include "steam_audio.hpp"
|
||||||
#include "steam_audio_listener.h"
|
#include "steam_audio_dynamic_mesh.hpp"
|
||||||
#include "steam_audio_material.h"
|
#include "steam_audio_source.hpp"
|
||||||
#include "steam_audio_source.h"
|
#include "steam_audio_listener.hpp"
|
||||||
#include "steam_audio_static_mesh.h"
|
#include "steam_audio_server.hpp"
|
||||||
#include "steam_audio_dynamic_mesh.h"
|
#include "steam_audio_material.hpp"
|
||||||
|
#include "steam_audio_static_mesh.hpp"
|
||||||
|
|
||||||
using namespace godot;
|
using namespace godot;
|
||||||
|
|
||||||
@@ -1,118 +1,32 @@
|
|||||||
#include "steam_audio.h"
|
#include "steam_audio.hpp"
|
||||||
|
|
||||||
using namespace godot;
|
using namespace godot;
|
||||||
|
|
||||||
IPLContext global_context = nullptr;
|
|
||||||
IPLScene global_scene = nullptr;
|
|
||||||
IPLSimulator global_simulator = nullptr;
|
|
||||||
|
|
||||||
SteamAudio::SteamAudio() {
|
SteamAudio::SteamAudio() {
|
||||||
steam_audio=this;
|
|
||||||
initialize();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SteamAudio::~SteamAudio()
|
|
||||||
{
|
|
||||||
steam_audio = nullptr;
|
|
||||||
shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SteamAudio::_bind_methods() {
|
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 SteamAudio::godot_to_ipl_space(const Transform3D &t) {
|
||||||
IPLCoordinateSpace3 s{};
|
IPLCoordinateSpace3 s{};
|
||||||
@@ -129,7 +43,6 @@ IPLCoordinateSpace3 SteamAudio::godot_to_ipl_space(const Transform3D &t) {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Steam Audio → Godot space
|
// Steam Audio → Godot space
|
||||||
Transform3D SteamAudio::ipl_space_to_godot(const IPLCoordinateSpace3 &s) {
|
Transform3D SteamAudio::ipl_space_to_godot(const IPLCoordinateSpace3 &s) {
|
||||||
// construct a Basis from column vectors
|
// 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));
|
return Transform3D(b, Vector3(s.origin.x, s.origin.y, s.origin.z));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SteamAudio::_ready() {
|
IPLMatrix4x4 SteamAudio::transform_to_ipl_matrix(const Transform3D &t) {
|
||||||
reflections_thread;
|
// First compute the three axes using Godot’s forward = –Z
|
||||||
build_scene();
|
Vector3 right = t.basis.xform(Vector3(1, 0, 0));
|
||||||
iplSimulatorRunDirect(simulator);
|
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"
|
// steam_audio_static_mesh.cpp
|
||||||
#include "steam_audio_globals.h" // holds global_context & global_scene
|
#include "steam_audio_dynamic_mesh.hpp"
|
||||||
|
|
||||||
using namespace godot;
|
using namespace godot;
|
||||||
|
|
||||||
SteamAudioDynamicMesh::SteamAudioDynamicMesh() {}
|
Vector<SteamAudioDynamicMesh*> SteamAudioDynamicMesh::_instances;
|
||||||
SteamAudioDynamicMesh::~SteamAudioDynamicMesh() {
|
|
||||||
if (instanced_mesh) {
|
SteamAudioDynamicMesh::SteamAudioDynamicMesh() {
|
||||||
// remove & release the instance
|
_instances.push_back(this);
|
||||||
iplInstancedMeshRemove(instanced_mesh, global_scene);
|
|
||||||
iplInstancedMeshRelease(&instanced_mesh);
|
|
||||||
}
|
|
||||||
if (proxy_subscene) {
|
|
||||||
iplSceneRelease(&proxy_subscene);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SteamAudioDynamicMesh::~SteamAudioDynamicMesh() {
|
||||||
|
_instances.erase(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Vector<SteamAudioDynamicMesh *> &SteamAudioDynamicMesh::get_all_dynamic_meshes() {
|
||||||
|
return _instances;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void SteamAudioDynamicMesh::_bind_methods() {
|
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() {
|
// Setters & getters ---------------------------------------------------
|
||||||
build_subscene();
|
void SteamAudioDynamicMesh::set_proxy_mode(int mode) {
|
||||||
|
proxy_mode = mode;
|
||||||
// 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*/) {
|
int SteamAudioDynamicMesh::get_proxy_mode() const {
|
||||||
if (!instanced_mesh) return;
|
return proxy_mode;
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SteamAudioDynamicMesh::build_subscene() {
|
void SteamAudioDynamicMesh::set_custom_proxy_mesh(Ref<Mesh> mesh) {
|
||||||
// 1) create a fresh sub-scene
|
custom_proxy_mesh = mesh;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IPLMatrix4x4 SteamAudioDynamicMesh::to_ipl_matrix(const Transform3D &t) {
|
Ref<Mesh> SteamAudioDynamicMesh::get_custom_proxy_mesh() const {
|
||||||
// row-major: each m[i][j] is row i, column j
|
return custom_proxy_mesh;
|
||||||
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];
|
void SteamAudioDynamicMesh::_notification(int what) {
|
||||||
// basis Y column
|
Transform3D current;
|
||||||
m.elements[0][1] = t.basis[1][0]; m.elements[1][1] = t.basis[1][1]; m.elements[2][1] = t.basis[1][2];
|
switch (what) {
|
||||||
// basis Z column
|
case NOTIFICATION_ENTER_TREE:
|
||||||
m.elements[0][2] = t.basis[2][0]; m.elements[1][2] = t.basis[2][1]; m.elements[2][2] = t.basis[2][2];
|
needs_update=true;
|
||||||
// origin
|
break;
|
||||||
m.elements[0][3] = t.origin.x; m.elements[1][3] = t.origin.y; m.elements[2][3] = t.origin.z;
|
case NOTIFICATION_EXIT_TREE:
|
||||||
// bottom row
|
break;
|
||||||
m.elements[3][0] = 0; m.elements[3][1] = 0; m.elements[3][2] = 0; m.elements[3][3] = 1;
|
case NOTIFICATION_TRANSFORM_CHANGED:
|
||||||
return m;
|
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 "steam_audio_listener.hpp"
|
||||||
|
|
||||||
#include "phonon.h"
|
|
||||||
|
|
||||||
using namespace godot;
|
using namespace godot;
|
||||||
|
|
||||||
SteamAudioListener::SteamAudioListener() = default;
|
SteamAudioListener* SteamAudioListener::listener;
|
||||||
SteamAudioListener::~SteamAudioListener() = default;
|
|
||||||
|
SteamAudioListener::SteamAudioListener() {
|
||||||
|
listener=this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SteamAudioListener::~SteamAudioListener() {
|
||||||
|
listener=nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void SteamAudioListener::_bind_methods() {
|
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();
|
Transform3D transform = get_global_transform();
|
||||||
|
|
||||||
IPLVector3 pos = {
|
IPLCoordinateSpace3 space = SteamAudio::godot_to_ipl_space(transform);
|
||||||
static_cast<float>(transform.origin.x),
|
|
||||||
static_cast<float>(transform.origin.y),
|
iplsiminputs.listener=space;
|
||||||
static_cast<float>(transform.origin.z)
|
|
||||||
};
|
iplSimulatorSetSharedInputs(iplsim,IPL_SIMULATIONFLAGS_DIRECT,&iplsiminputs);
|
||||||
Vector3 godot_fwd = transform.basis.xform(Vector3(0, 0, -1));
|
iplSimulatorSetSharedInputs(iplsim,IPL_SIMULATIONFLAGS_PATHING,&iplsiminputs);
|
||||||
IPLVector3 fwd = {
|
iplSimulatorSetSharedInputs(iplsim,IPL_SIMULATIONFLAGS_REFLECTIONS,&iplsiminputs);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
@@ -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;
|
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::FLOAT,"scattering"), "set_scattering", "get_scattering");
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3,"transmission"), "set_transmission", "get_transmission");
|
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 {
|
SteamMaterial SteamAudioMaterial::default_material = {
|
||||||
IPLMaterial material ={};
|
Vector3(0.10f,0.20f,0.30f),
|
||||||
material.absorption[0] = absorption.x;
|
0.05f,
|
||||||
material.absorption[1] = absorption.y;
|
Vector3(0.100f,0.050f,0.030f)
|
||||||
material.absorption[2] = absorption.z;
|
};
|
||||||
|
|
||||||
material.scattering = scattering;
|
|
||||||
|
|
||||||
material.transmission[0] = transmission.x;
|
|
||||||
material.transmission[1] = transmission.y;
|
|
||||||
material.transmission[2] = transmission.z;
|
|
||||||
|
|
||||||
|
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;
|
return material;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,32 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include <godot_cpp/variant/vector3.hpp>
|
||||||
#include <godot_cpp/classes/resource.hpp>
|
#include <godot_cpp/classes/resource.hpp>
|
||||||
#include "godot_cpp/godot.hpp"
|
|
||||||
#include <godot_cpp/core/class_db.hpp>
|
#include <godot_cpp/core/class_db.hpp>
|
||||||
|
#include "godot_cpp/classes/wrapped.hpp"
|
||||||
|
#include "godot_cpp/godot.hpp"
|
||||||
#include "phonon.h"
|
#include "phonon.h"
|
||||||
|
|
||||||
using namespace godot;
|
using namespace godot;
|
||||||
|
struct SteamMaterial {
|
||||||
|
Vector3 absorption;
|
||||||
|
float scattering;
|
||||||
|
Vector3 transmission;
|
||||||
|
};
|
||||||
class SteamAudioMaterial : public Resource {
|
class SteamAudioMaterial : public Resource {
|
||||||
GDCLASS(SteamAudioMaterial,Resource) // NOLINT(modernize-use-auto, hicpp-use-auto)
|
GDCLASS(SteamAudioMaterial,Resource) // NOLINT(modernize-use-auto, hicpp-use-auto)
|
||||||
protected:
|
protected:
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
private:
|
private:
|
||||||
Vector3 absorption = Vector3(0.2, 0.2, 0.2);
|
SteamMaterial material;
|
||||||
float scattering = 0.0;
|
|
||||||
Vector3 transmission = Vector3(0.0, 0.0, 0.0);
|
|
||||||
public:
|
public:
|
||||||
|
static SteamMaterial default_material;
|
||||||
|
static const SteamMaterial& get_default_material();
|
||||||
void set_absorption(const Vector3 value);
|
void set_absorption(const Vector3 value);
|
||||||
void set_scattering(const float value);
|
void set_scattering(const float value);
|
||||||
void set_transmission(const Vector3 value);
|
void set_transmission(const Vector3 value);
|
||||||
Vector3 get_absorption() const;
|
Vector3 get_absorption() const;
|
||||||
[[nodiscard]] float get_scattering() const;
|
[[nodiscard]] float get_scattering() const;
|
||||||
Vector3 get_transmission() const;
|
Vector3 get_transmission() const;
|
||||||
|
SteamMaterial get_steam_material() const;
|
||||||
[[nodiscard]] IPLMaterial to_ipl_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_source.hpp"
|
||||||
|
|
||||||
#include "steam_audio_globals.h"
|
|
||||||
|
|
||||||
using namespace godot;
|
using namespace godot;
|
||||||
|
|
||||||
|
// Static vector to track all active audio sources
|
||||||
|
Vector<SteamAudioSource *> SteamAudioSource::_instances;
|
||||||
|
|
||||||
|
// Constructor/Destructor
|
||||||
SteamAudioSource::SteamAudioSource() {
|
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() {
|
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() {
|
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) {
|
// Hide default AudioStreamPlayer3D properties that we don't use
|
||||||
update_position();
|
void SteamAudioSource::_validate_property(PropertyInfo &property) const {
|
||||||
}
|
if (property.name == StringName("attenuation_model") || property.name == StringName("unit_size") ||
|
||||||
bool SteamAudioSource::update_position() {
|
property.name.begins_with("attenuation") || property.name.begins_with("emission_")) {
|
||||||
IPLCoordinateSpace3 current_pos = sa->godot_to_ipl_space(get_transform());
|
property.usage = PROPERTY_USAGE_NO_EDITOR;
|
||||||
|
}
|
||||||
iplSource
|
|
||||||
}
|
}
|
||||||
@@ -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
|
// steam_audio_static_mesh.cpp
|
||||||
#include "steam_audio_static_mesh.h"
|
#include "steam_audio_static_mesh.hpp"
|
||||||
|
|
||||||
#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;
|
using namespace godot;
|
||||||
|
|
||||||
SteamAudioStaticMesh::SteamAudioStaticMesh() = default;
|
Vector<SteamAudioStaticMesh*> SteamAudioStaticMesh::_instances;
|
||||||
|
|
||||||
|
SteamAudioStaticMesh::SteamAudioStaticMesh() {
|
||||||
|
_instances.push_back(this);
|
||||||
|
}
|
||||||
|
|
||||||
SteamAudioStaticMesh::~SteamAudioStaticMesh() {
|
SteamAudioStaticMesh::~SteamAudioStaticMesh() {
|
||||||
// release any previously created Steam Audio meshes
|
_instances.erase(this);
|
||||||
for (auto &sm : static_meshes) {
|
|
||||||
iplStaticMeshRelease(&sm);
|
|
||||||
}
|
|
||||||
static_meshes.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Vector<SteamAudioStaticMesh *> &SteamAudioStaticMesh::get_all_static_meshes() {
|
||||||
|
return _instances;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void SteamAudioStaticMesh::_bind_methods() {
|
void SteamAudioStaticMesh::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("set_proxy_mode", "mode"), &SteamAudioStaticMesh::set_proxy_mode);
|
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);
|
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);
|
ClassDB::bind_method(D_METHOD("get_custom_proxy_mesh"), &SteamAudioStaticMesh::get_custom_proxy_mesh);
|
||||||
ADD_PROPERTY(
|
ADD_PROPERTY(
|
||||||
PropertyInfo(Variant::OBJECT, "custom_proxy_mesh",PROPERTY_HINT_RESOURCE_TYPE,"Mesh"), "set_custom_proxy_mesh","get_custom_proxy_mesh");
|
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 ---------------------------------------------------
|
// Setters & getters ---------------------------------------------------
|
||||||
void SteamAudioStaticMesh::set_proxy_mode(int mode) {
|
void SteamAudioStaticMesh::set_proxy_mode(int mode) {
|
||||||
proxy_mode = mode;
|
proxy_mode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SteamAudioStaticMesh::get_proxy_mode() const {
|
int SteamAudioStaticMesh::get_proxy_mode() const {
|
||||||
return proxy_mode;
|
return proxy_mode;
|
||||||
}
|
}
|
||||||
@@ -50,132 +42,217 @@ int SteamAudioStaticMesh::get_proxy_mode() const {
|
|||||||
void SteamAudioStaticMesh::set_custom_proxy_mesh(Ref<Mesh> mesh) {
|
void SteamAudioStaticMesh::set_custom_proxy_mesh(Ref<Mesh> mesh) {
|
||||||
custom_proxy_mesh = mesh;
|
custom_proxy_mesh = mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<Mesh> SteamAudioStaticMesh::get_custom_proxy_mesh() const {
|
Ref<Mesh> SteamAudioStaticMesh::get_custom_proxy_mesh() const {
|
||||||
return custom_proxy_mesh;
|
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 --------------------------------------------------
|
// Core functionality --------------------------------------------------
|
||||||
void SteamAudioStaticMesh::generate_proxy_mesh() {
|
Ref<Mesh> SteamAudioStaticMesh::get_proxy_mesh() {
|
||||||
// 1) Clean up any old meshes
|
print_line("getting the proxy mesh");
|
||||||
for (auto &sm : static_meshes) {
|
print_line(vformat("proxy_mode: %f", proxy_mode));
|
||||||
iplStaticMeshRelease(&sm);
|
|
||||||
}
|
MeshInstance3D* parent = cast_to<MeshInstance3D>(get_parent());
|
||||||
static_meshes.clear();
|
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) {
|
switch (proxy_mode) {
|
||||||
case PROXY_NONE:
|
case PROXY_NONE:
|
||||||
target = cast_to<MeshInstance3D>(get_parent())->get_mesh();
|
target_mesh = source_mesh;
|
||||||
break;
|
break;
|
||||||
case PROXY_CONVEX: {
|
case PROXY_AUTO:
|
||||||
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;
|
break;
|
||||||
}
|
|
||||||
case PROXY_CUSTOM:
|
case PROXY_CUSTOM:
|
||||||
if (!custom_proxy_mesh.is_valid()) {
|
|
||||||
ERR_PRINT("SteamAudioStaticMesh: custom_proxy_mesh not set");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
target = custom_proxy_mesh;
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ERR_PRINT("SteamAudioStaticMesh: unknown proxy_mode");
|
print_error("Unknown proxy mode!");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return target_mesh;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void SteamAudioStaticMesh::build_mesh(IPLScene scene) {
|
||||||
|
if (!scene) {
|
||||||
|
ERR_PRINT("Invalid IPLScene passed to build_mesh");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!target.is_valid()) {
|
struct SurfaceData {
|
||||||
ERR_PRINT("SteamAudioStaticMesh: target mesh invalid");
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
print_line("got the mesh");
|
||||||
|
|
||||||
// 3) For each surface, build and add a Steam Audio static mesh
|
for (int surface_idx = 0; surface_idx < mesh->get_surface_count(); surface_idx++) {
|
||||||
const int surface_count = target->get_surface_count();
|
SurfaceData data;
|
||||||
for (int s = 0; s < surface_count; ++s) {
|
Array arrays = mesh->surface_get_arrays(surface_idx);
|
||||||
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 ---
|
if (arrays.is_empty()) {
|
||||||
Ref<Material> gm = target->surface_get_material(s);
|
ERR_PRINT(vformat("Surface %d has no arrays data", surface_idx));
|
||||||
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 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");
|
|
||||||
continue;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4) Commit all additions at once
|
// If indices array is empty, create one from vertices
|
||||||
// iplSceneCommit(global_scene);
|
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(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
|
// steam_audio_proxy_mesh.h
|
||||||
#pragma once
|
#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/array_mesh.hpp>
|
||||||
|
#include <godot_cpp/classes/convex_polygon_shape3d.hpp>
|
||||||
#include <godot_cpp/classes/material.hpp>
|
#include <godot_cpp/classes/material.hpp>
|
||||||
|
#include <godot_cpp/classes/mesh.hpp>
|
||||||
#include "steam_audio_material.h"
|
#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;
|
using namespace godot;
|
||||||
|
|
||||||
// Proxy generation modes
|
// Proxy generation modes
|
||||||
enum ProxyMode {
|
|
||||||
PROXY_NONE = 0,
|
|
||||||
PROXY_CONVEX = 1,
|
|
||||||
PROXY_CUSTOM = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
class SteamAudioStaticMesh : public Node3D {
|
class SteamAudioStaticMesh : public Node3D {
|
||||||
GDCLASS(SteamAudioStaticMesh, Node3D)
|
GDCLASS(SteamAudioStaticMesh, Node3D)
|
||||||
@@ -26,9 +25,10 @@ class SteamAudioStaticMesh : public Node3D {
|
|||||||
private:
|
private:
|
||||||
int proxy_mode = PROXY_NONE;
|
int proxy_mode = PROXY_NONE;
|
||||||
Ref<Mesh> custom_proxy_mesh;
|
Ref<Mesh> custom_proxy_mesh;
|
||||||
Ref<SteamAudioMaterial> default_material;
|
|
||||||
Vector<IPLStaticMesh> static_meshes;
|
Vector<IPLStaticMesh> static_meshes;
|
||||||
|
|
||||||
|
bool needs_update = false;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
|
|
||||||
@@ -44,9 +44,14 @@ public:
|
|||||||
void set_custom_proxy_mesh(Ref<Mesh> mesh);
|
void set_custom_proxy_mesh(Ref<Mesh> mesh);
|
||||||
Ref<Mesh> get_custom_proxy_mesh() const;
|
Ref<Mesh> get_custom_proxy_mesh() const;
|
||||||
|
|
||||||
void set_default_material(Ref<SteamAudioMaterial> mat);
|
bool get_needs_update() {
|
||||||
Ref<SteamAudioMaterial> get_default_material() const;
|
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
|
// Regenerates Steam Audio proxies
|
||||||
void generate_proxy_mesh();
|
Ref<Mesh> get_proxy_mesh();
|
||||||
};
|
};
|
||||||
18
support_files/SteamAudioEditorPlugin.gd
Normal file
18
support_files/SteamAudioEditorPlugin.gd
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
@tool
|
||||||
|
extends EditorPlugin
|
||||||
|
|
||||||
|
const AUTOLOAD_NAME = "STEAM_AUDIO"
|
||||||
|
func _enable_plugin() -> void:
|
||||||
|
add_autoload_singleton(AUTOLOAD_NAME,"res://addons/SteamAudioGodot/steam_audio_manager.tscn")
|
||||||
|
|
||||||
|
func _enter_tree() -> void:
|
||||||
|
# Initialization of the plugin goes here.
|
||||||
|
pass
|
||||||
|
|
||||||
|
func _exit_tree() -> void:
|
||||||
|
# Clean-up of the plugin goes here.
|
||||||
|
pass
|
||||||
|
|
||||||
|
func _disable_plugin() -> void:
|
||||||
|
remove_autoload_singleton(AUTOLOAD_NAME)
|
||||||
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
7
support_files/plugin.cfg
Normal file
7
support_files/plugin.cfg
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[plugin]
|
||||||
|
|
||||||
|
name="Steam Audio Godot"
|
||||||
|
description="adds Steam Audio to Godot"
|
||||||
|
author="The River Nyx"
|
||||||
|
version=""
|
||||||
|
script="SteamAudioEditorPlugin.gd"
|
||||||
3
support_files/steam_audio_manager.tscn
Normal file
3
support_files/steam_audio_manager.tscn
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[gd_scene load_steps=0 format=3 uid="uid://b28pa3dtdpc8d"]
|
||||||
|
|
||||||
|
[node name="SteamAudio" type="SteamAudio"]
|
||||||
Reference in New Issue
Block a user