more stuffs
This commit is contained in:
@@ -6,8 +6,16 @@ target_sources( ${PROJECT_NAME}
|
||||
register_types.h
|
||||
steam_audio.cpp
|
||||
steam_audio.h
|
||||
steam_audio_scene.cpp
|
||||
steam_audio_scene.h
|
||||
steam_audio_material.cpp
|
||||
steam_audio_material.h
|
||||
steam_audio_listener.h
|
||||
steam_audio_listener.cpp
|
||||
steam_audio_source.cpp
|
||||
steam_audio_source.h
|
||||
steam_audio_dynamic_mesh.h
|
||||
steam_audio_dynamic_mesh.cpp
|
||||
steam_audio_static_mesh.cpp
|
||||
steam_audio_static_mesh.h
|
||||
)
|
||||
|
||||
target_include_directories( ${PROJECT_NAME}
|
||||
|
||||
@@ -1,31 +1,71 @@
|
||||
// src/register_types.cpp
|
||||
#include "register_types.h"
|
||||
#include "steam_audio.h" // your SteamAudio class declaration
|
||||
#include "steam_audio_scene.h"
|
||||
|
||||
#include "register_types.h"
|
||||
|
||||
using namespace godot;
|
||||
|
||||
void initialize_steam_audio(ModuleInitializationLevel p_level) {
|
||||
if (p_level!=MODULE_INITIALIZATION_LEVEL_SERVERS) {
|
||||
if (p_level!=MODULE_INITIALIZATION_LEVEL_SCENE) {
|
||||
return;
|
||||
}
|
||||
/*if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) {
|
||||
GDREGISTER_CLASS(SteamAudioEditorPlugin);
|
||||
}*/
|
||||
register_steam_audio_settings();
|
||||
GDREGISTER_CLASS(SteamAudio);
|
||||
GDREGISTER_CLASS(SteamAudioScene);
|
||||
GDREGISTER_CLASS(SteamAudioMaterial);
|
||||
GDREGISTER_RUNTIME_CLASS(SteamAudioListener);
|
||||
GDREGISTER_RUNTIME_CLASS(SteamAudioSource);
|
||||
GDREGISTER_RUNTIME_CLASS(SteamAudioStaticMesh)
|
||||
GDREGISTER_RUNTIME_CLASS(SteamAudioDynamicMesh)
|
||||
}
|
||||
|
||||
void uninitialize_steam_audio(ModuleInitializationLevel p_level) {
|
||||
if (p_level!=MODULE_INITIALIZATION_LEVEL_SERVERS) {
|
||||
if (p_level!=MODULE_INITIALIZATION_LEVEL_SCENE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void register_steam_audio_settings() {
|
||||
ProjectSettings *settings = ProjectSettings::get_singleton();
|
||||
|
||||
{//Raytracer enum
|
||||
settings->set("steam_audio/ray_tracer/RayTracer",0);
|
||||
Dictionary info;
|
||||
info["name"] = "steam_audio/ray_tracer/RayTracer";
|
||||
info["type"] = Variant::INT;
|
||||
info["hint"] = PROPERTY_HINT_ENUM;
|
||||
info["hint_string"] = "Steam RT,Embree RT";
|
||||
info["usage"] = PROPERTY_USAGE_DEFAULT;
|
||||
settings->add_property_info(info);
|
||||
settings->set("steam_audio/ray_tracer/RayTracer",1);
|
||||
}
|
||||
|
||||
{//spatialization mode enum
|
||||
settings->set("steam_audio/spatializer/spatializer",0);
|
||||
Dictionary info;
|
||||
info["name"] = "steam_audio/spatializer/spatializer";
|
||||
info["type"] = Variant::INT;
|
||||
info["hint"] = PROPERTY_HINT_ENUM;
|
||||
info["hint_string"] = "Panning,HRTF,Ambisonic Pan,Ambisonic Binaural";
|
||||
info["usage"] = PROPERTY_USAGE_DEFAULT;
|
||||
settings->add_property_info(info);
|
||||
settings->set_initial_value("steam_audio/spatializer/spatializer",1);
|
||||
}
|
||||
|
||||
{
|
||||
settings->set("steam_audio/spatializer/HRTF/Volume",0);
|
||||
Dictionary info;
|
||||
info["name"] = "steam_audio/spatializer/HRTF/Volume";
|
||||
info["type"] = Variant::FLOAT;
|
||||
info["hint"] = PROPERTY_HINT_RANGE;
|
||||
info["hint_string"] = "0.0,1.0,.01,slider";
|
||||
info["usage"] = PROPERTY_USAGE_DEFAULT;
|
||||
settings->add_property_info(info);
|
||||
settings->set("steam_audio/spatializer/HRTF/Volume",1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
// Initialization.
|
||||
GDExtensionBool GDE_EXPORT steam_audio_library_init(GDExtensionInterfaceGetProcAddress p_get_proc_address, const GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) {
|
||||
godot::GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization);
|
||||
GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization);
|
||||
|
||||
init_obj.register_initializer(initialize_steam_audio);
|
||||
init_obj.register_terminator(uninitialize_steam_audio);
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
//
|
||||
// Created by bryce on 5/7/2025.
|
||||
//
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include <godot_cpp/classes/project_settings.hpp>
|
||||
#include <godot_cpp/core/class_db.hpp>
|
||||
|
||||
#include "steam_audio.h"
|
||||
#include "steam_audio_listener.h"
|
||||
#include "steam_audio_material.h"
|
||||
#include "steam_audio_source.h"
|
||||
#include "steam_audio_static_mesh.h"
|
||||
#include "steam_audio_dynamic_mesh.h"
|
||||
|
||||
#include <godot_cpp/core/class_db.hpp> // for ClassDB
|
||||
using namespace godot;
|
||||
|
||||
// Called by the InitObject below to register your SteamAudio class.
|
||||
// Called by the InitObject to register your SteamAudio class.
|
||||
void initialize_steam_audio(ModuleInitializationLevel p_level);
|
||||
void uninitialize_steam_audio(ModuleInitializationLevel p_level);
|
||||
void uninitialize_steam_audio(ModuleInitializationLevel p_level);
|
||||
//registers settings in godot
|
||||
void register_steam_audio_settings();
|
||||
@@ -1,43 +1,151 @@
|
||||
//
|
||||
// Created by bryce on 5/7/2025.
|
||||
//
|
||||
|
||||
#include "steam_audio.h"
|
||||
#include "steam_audio.h"
|
||||
|
||||
using namespace godot;
|
||||
|
||||
IPLContext global_context = nullptr;
|
||||
IPLScene global_scene = nullptr;
|
||||
IPLSimulator global_simulator = nullptr;
|
||||
|
||||
SteamAudio::SteamAudio() {
|
||||
steam_audio=this;
|
||||
initialize();
|
||||
|
||||
}
|
||||
|
||||
SteamAudio::~SteamAudio()
|
||||
{
|
||||
if (context)
|
||||
{
|
||||
iplContextRelease(&context);
|
||||
context=nullptr;
|
||||
}
|
||||
if (embree_device) {
|
||||
iplEmbreeDeviceRelease(&embree_device);
|
||||
embree_device=nullptr;
|
||||
}
|
||||
steam_audio = nullptr;
|
||||
shutdown();
|
||||
}
|
||||
|
||||
void SteamAudio::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("initialize"), &SteamAudio::initialize);
|
||||
ClassDB::bind_method(D_METHOD("shutdown"), &SteamAudio::shutdown);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_hrtf"), &SteamAudio::get_hrtf);
|
||||
ClassDB::bind_method(D_METHOD("get_hrtf_settings"), &SteamAudio::get_hrtf_settings);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_simulator"), &SteamAudio::get_simulator);
|
||||
ClassDB::bind_method(D_METHOD("get_simulation_settings"), &SteamAudio::get_simulation_settings);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_context"),&SteamAudio::get_context);
|
||||
ClassDB::bind_method(D_METHOD("get_context_settings"),&SteamAudio::get_context_settings);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_scene"), &SteamAudio::get_scene);
|
||||
ClassDB::bind_method(D_METHOD("get_scene_settings"), &SteamAudio::get_scene_settings);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_embree_device"), &SteamAudio::get_embree_device);
|
||||
ClassDB::bind_method(D_METHOD("get_embree_device_settings"),&SteamAudio::get_embree_device_settings);
|
||||
}
|
||||
|
||||
SteamAudio::SteamAudio() = default;
|
||||
|
||||
bool SteamAudio::initialize()
|
||||
{
|
||||
IPLContextSettings settings{};
|
||||
settings.version = STEAMAUDIO_VERSION;
|
||||
|
||||
if ( IPLerror err = iplContextCreate( &settings, &context ); err != IPL_STATUS_SUCCESS)
|
||||
{
|
||||
UtilityFunctions::printerr("SteamAudio::initialize()", err);
|
||||
return false;
|
||||
}
|
||||
IPLEmbreeDeviceSettings emb_device_settings{};
|
||||
if (iplEmbreeDeviceCreate(context, &emb_device_settings, &embree_device) != IPL_STATUS_SUCCESS) {
|
||||
UtilityFunctions::printerr("SteamAudio::initialize(): failed to create Embree device");
|
||||
return false;
|
||||
bool SteamAudio::initialize() {
|
||||
ctx_settings.version=STEAMAUDIO_VERSION;
|
||||
IPLContext ctx = nullptr;
|
||||
iplContextCreate(&ctx_settings, &ctx);
|
||||
iplSceneCreate(ctx,&scene_settings,&scene);
|
||||
iplSimulatorCreate(ctx,&simulation_settings,&simulator);
|
||||
int ray_mode = proj_settings->get_setting("steam_audio/ray_tracer");
|
||||
switch (ray_mode) {
|
||||
case 0://steam rt
|
||||
break;
|
||||
case 1: // embree rt
|
||||
iplEmbreeDeviceCreate(ctx,&embree_device_settings,&embree_device);
|
||||
break;
|
||||
default:
|
||||
ERR_PRINT("Unknown Raytracer");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
int spat_mode = proj_settings->get_setting("steam_audio/spatializer");
|
||||
switch (spat_mode) {
|
||||
case 0://panning
|
||||
break;
|
||||
case 1://HRTF
|
||||
iplHRTFCreate(ctx,&audio_settings,&hrtf_settings,&hrtf);
|
||||
break;
|
||||
case 2://ambisonic pan
|
||||
break;
|
||||
case 3://ambisonic binaural
|
||||
break;
|
||||
default:
|
||||
ERR_PRINT("Unknown Spatializer");
|
||||
return false;
|
||||
}
|
||||
print_line("Steam Audio Successfully initialized");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SteamAudio::update_static_scene() {
|
||||
|
||||
}
|
||||
|
||||
void SteamAudio::update_dynamic_scene() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
void SteamAudio::shutdown() {
|
||||
if (embree_device != nullptr) {
|
||||
iplEmbreeDeviceRelease(&embree_device);
|
||||
embree_device = nullptr;
|
||||
}
|
||||
if (ctx != nullptr) {
|
||||
iplContextRelease(&ctx);
|
||||
ctx = nullptr;
|
||||
}
|
||||
if (hrtf != nullptr) {
|
||||
iplHRTFRelease(&hrtf);
|
||||
hrtf = nullptr;
|
||||
}
|
||||
if (simulator != nullptr) {
|
||||
iplSimulatorRelease(&simulator);
|
||||
simulator = nullptr;
|
||||
}
|
||||
if (scene!= nullptr) {
|
||||
iplSceneRelease(&scene);
|
||||
scene = nullptr;
|
||||
}
|
||||
}
|
||||
bool SteamAudio::build_scene() {
|
||||
update_dynamic_scene();
|
||||
update_static_scene();
|
||||
}
|
||||
Array<NodePath> SteamAudio::get_nodes_with_child() {
|
||||
|
||||
}
|
||||
|
||||
IPLCoordinateSpace3 SteamAudio::godot_to_ipl_space(const Transform3D &t) {
|
||||
IPLCoordinateSpace3 s{};
|
||||
// pull columns via Basis.xform()
|
||||
Vector3 right = t.basis.xform(Vector3(1, 0, 0));
|
||||
Vector3 up = t.basis.xform(Vector3(0, 1, 0));
|
||||
Vector3 ahead = t.basis.xform(Vector3(0, 0, -1)); // Godot forward is -Z
|
||||
|
||||
s.right = { right.x, right.y, right.z };
|
||||
s.up = { up.x, up.y, up.z };
|
||||
s.ahead = { ahead.x, ahead.y, ahead.z };
|
||||
s.origin = { t.origin.x, t.origin.y, t.origin.z };
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
// Steam Audio → Godot space
|
||||
Transform3D SteamAudio::ipl_space_to_godot(const IPLCoordinateSpace3 &s) {
|
||||
// construct a Basis from column vectors
|
||||
Basis b(
|
||||
Vector3(s.right .x, s.right .y, s.right .z),
|
||||
Vector3(s.up .x, s.up .y, s.up .z),
|
||||
Vector3(-s.ahead.x, -s.ahead.y, -s.ahead.z) // invert ahead back to -Z
|
||||
);
|
||||
|
||||
return Transform3D(b, Vector3(s.origin.x, s.origin.y, s.origin.z));
|
||||
}
|
||||
|
||||
void SteamAudio::_ready() {
|
||||
reflections_thread;
|
||||
build_scene();
|
||||
iplSimulatorRunDirect(simulator);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,31 +1,81 @@
|
||||
//
|
||||
// Created by bryce on 5/7/2025.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <godot_cpp/godot.hpp>
|
||||
#include<godot_cpp/core/class_db.hpp>
|
||||
#include <godot_cpp/classes/os.hpp>
|
||||
#include <godot_cpp/classes/engine.hpp>
|
||||
#include <godot_cpp/classes/node.hpp>
|
||||
#include <godot_cpp/classes/project_settings.hpp>
|
||||
#include <godot_cpp/classes/ref.hpp>
|
||||
#include <godot_cpp/classes/scene_tree.hpp>
|
||||
#include <godot_cpp/classes/thread.hpp>
|
||||
#include <godot_cpp/classes/time.hpp>
|
||||
#include <godot_cpp/core/class_db.hpp>
|
||||
#include <godot_cpp/godot.hpp>
|
||||
#include <godot_cpp/variant/utility_functions.hpp>
|
||||
#include <phonon.h>
|
||||
#include "steam_audio_dynamic_mesh.h"
|
||||
#include "steam_audio_listener.h"
|
||||
#include "steam_audio_material.h"
|
||||
#include "steam_audio_source.h"
|
||||
#include "steam_audio_static_mesh.h"
|
||||
|
||||
|
||||
using namespace godot;
|
||||
|
||||
class SteamAudio : public RefCounted
|
||||
class SteamAudio : public Node
|
||||
{
|
||||
GDCLASS( SteamAudio,RefCounted ) // NOLINT(readability-use-auto)
|
||||
GDCLASS( SteamAudio, Node ) // NOLINT(modernize-use-auto, hicpp-use-auto)
|
||||
|
||||
private:
|
||||
IPLContext context = nullptr;
|
||||
IPLContextSettings ctx_settings{};
|
||||
IPLAudioSettings audio_settings{};
|
||||
IPLContext ctx = nullptr;
|
||||
IPLEmbreeDeviceSettings embree_device_settings{};
|
||||
IPLEmbreeDevice embree_device = nullptr;
|
||||
IPLScene scene = nullptr;
|
||||
IPLSceneSettings scene_settings{};
|
||||
IPLSimulationSettings simulation_settings{};
|
||||
IPLSimulator simulator = nullptr;
|
||||
IPLStaticMesh mesh = nullptr;
|
||||
IPLHRTFSettings hrtf_settings{};
|
||||
IPLHRTF hrtf = nullptr;
|
||||
ProjectSettings *proj_settings = ProjectSettings::get_singleton();
|
||||
Array<SteamAudioDynamicMesh> dynamic_geometry;
|
||||
Array<SteamAudioStaticMesh> static_geometry;
|
||||
Array<SteamAudioSource> sources;
|
||||
Ref<Thread> reflections_thread;
|
||||
Ref<Thread> pathing_thread;
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _ready() override;
|
||||
public:
|
||||
static SteamAudio *steam_audio;
|
||||
SteamAudio();
|
||||
~SteamAudio() override;
|
||||
bool initialize();
|
||||
void shutdown();
|
||||
|
||||
bool build_scene();
|
||||
|
||||
void update_static_scene();
|
||||
void update_dynamic_scene();
|
||||
|
||||
static Transform3D ipl_space_to_godot(const IPLCoordinateSpace3 &p_space);
|
||||
static IPLCoordinateSpace3 godot_to_ipl_space(const Transform3D &p_transform);
|
||||
|
||||
[[nodiscard]] IPLContext get_context() const{return ctx;}
|
||||
|
||||
[[nodiscard]] IPLEmbreeDevice get_embree_device() const{return embree_device;}
|
||||
[[nodiscard]] IPLEmbreeDeviceSettings get_embree_device_settings() const{return embree_device_settings;}
|
||||
|
||||
[[nodiscard]] IPLScene get_scene() const{return scene;}
|
||||
[[nodiscard]] IPLSceneSettings get_scene_settings() const{return scene_settings;}
|
||||
|
||||
[[nodiscard]] IPLSimulator get_simulator() const{return simulator;}
|
||||
[[nodiscard]] IPLSimulationSettings get_simulation_settings() const{return simulation_settings;}
|
||||
|
||||
[[nodiscard]] IPLHRTF get_hrtf() const{return hrtf;}
|
||||
[[nodiscard]] IPLHRTFSettings get_hrtf_settings() const{return hrtf_settings;}
|
||||
|
||||
[[nodiscard]] IPLContextSettings get_context_settings() const{return ctx_settings;}
|
||||
|
||||
[[nodiscard]] IPLContext getContext() const{return context;}
|
||||
[[nodiscard]] IPLEmbreeDevice getEmbreeDevice() const{return embree_device;}
|
||||
};
|
||||
92
src/steam_audio_dynamic_mesh.cpp
Normal file
92
src/steam_audio_dynamic_mesh.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
#include "steam_audio_dynamic_mesh.h"
|
||||
#include "steam_audio_globals.h" // holds global_context & global_scene
|
||||
|
||||
using namespace godot;
|
||||
|
||||
SteamAudioDynamicMesh::SteamAudioDynamicMesh() {}
|
||||
SteamAudioDynamicMesh::~SteamAudioDynamicMesh() {
|
||||
if (instanced_mesh) {
|
||||
// remove & release the instance
|
||||
iplInstancedMeshRemove(instanced_mesh, global_scene);
|
||||
iplInstancedMeshRelease(&instanced_mesh);
|
||||
}
|
||||
if (proxy_subscene) {
|
||||
iplSceneRelease(&proxy_subscene);
|
||||
}
|
||||
}
|
||||
|
||||
void SteamAudioDynamicMesh::_bind_methods() {
|
||||
// no exposed properties for now
|
||||
}
|
||||
|
||||
void SteamAudioDynamicMesh::_ready() {
|
||||
build_subscene();
|
||||
|
||||
// instance it in the main scene, with initial transform
|
||||
IPLInstancedMeshSettings ims{};
|
||||
ims.subScene = proxy_subscene;
|
||||
ims.transform = to_ipl_matrix(get_global_transform());
|
||||
|
||||
iplInstancedMeshCreate(global_scene, &ims, &instanced_mesh);
|
||||
iplInstancedMeshAdd(instanced_mesh, global_scene);
|
||||
iplSceneCommit(global_scene);
|
||||
}
|
||||
|
||||
void SteamAudioDynamicMesh::_process(double /*delta*/) {
|
||||
if (!instanced_mesh) return;
|
||||
|
||||
// update the 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() {
|
||||
// 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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
31
src/steam_audio_dynamic_mesh.h
Normal file
31
src/steam_audio_dynamic_mesh.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <godot_cpp/classes/mesh_instance3d.hpp>
|
||||
#include <godot_cpp/core/class_db.hpp>
|
||||
#include <phonon.h>
|
||||
|
||||
using namespace godot;
|
||||
|
||||
class SteamAudioDynamicMesh : public Node3D {
|
||||
GDCLASS(SteamAudioDynamicMesh, Node3D)
|
||||
|
||||
private:
|
||||
IPLScene proxy_subscene = nullptr;
|
||||
IPLInstancedMesh instanced_mesh = nullptr;
|
||||
|
||||
// helper: convert Godot Transform3D → IPLMatrix4x4
|
||||
static IPLMatrix4x4 to_ipl_matrix(const Transform3D &t);
|
||||
|
||||
// builds a one-off IPLScene containing this mesh’s raw geometry
|
||||
void build_subscene();
|
||||
|
||||
public:
|
||||
SteamAudioDynamicMesh();
|
||||
~SteamAudioDynamicMesh() override;
|
||||
|
||||
void _ready() override;
|
||||
void _process(double delta) override;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
};
|
||||
7
src/steam_audio_globals.h
Normal file
7
src/steam_audio_globals.h
Normal file
@@ -0,0 +1,7 @@
|
||||
// steam_audio_globals.h
|
||||
#pragma once
|
||||
#include <phonon.h>
|
||||
#include <godot_cpp/core/class_db.hpp>
|
||||
extern IPLContext global_context;
|
||||
extern IPLScene global_scene;
|
||||
extern IPLSimulator global_simulator;
|
||||
@@ -1,32 +1,46 @@
|
||||
//
|
||||
// Created by bryce on 5/8/2025.
|
||||
//
|
||||
#include "steam_audio_listener.h"
|
||||
|
||||
#include "steam_audio_listener.h"
|
||||
#include "phonon.h"
|
||||
|
||||
using namespace godot;
|
||||
|
||||
SteamAudioListener::SteamAudioListener() = default;
|
||||
SteamAudioListener::~SteamAudioListener() = default;
|
||||
|
||||
void SteamAudioListener::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_context", "ctx"), &SteamAudioListener::set_context);
|
||||
ClassDB::bind_method(D_METHOD("_process","delta"), &SteamAudioListener::_process);
|
||||
|
||||
ADD_PROPERTY(
|
||||
PropertyInfo(Variant::OBJECT, "context", PROPERTY_HINT_RESOURCE_TYPE, "SteamAudio"),
|
||||
"set_context", ""
|
||||
);
|
||||
}
|
||||
|
||||
void SteamAudioListener::set_context(const Ref<SteamAudio> &p_ctx) {
|
||||
ERR_FAIL_COND(!p_ctx.is_valid());
|
||||
ctx = p_ctx;
|
||||
}
|
||||
|
||||
void SteamAudioListener::_process(double delta) {
|
||||
ERR_FAIL_COND(!ctx.is_valid());
|
||||
Transform3D transform = get_global_transform();
|
||||
IPLCoordinateSpace3 ls{};
|
||||
ls.origin = {transform.origin.x, transform.origin.y, transform.origin.z};
|
||||
Vector3 fwd = transform.basis.xform(Vector3(0,0,-1)), upv = transform.basis.xform(Vector3(0,1,0));
|
||||
ls.ahead = {fwd.x, fwd.y, fwd.z};
|
||||
ls.up = {upv.x, upv.y, upv.z};
|
||||
|
||||
IPLVector3 pos = {
|
||||
static_cast<float>(transform.origin.x),
|
||||
static_cast<float>(transform.origin.y),
|
||||
static_cast<float>(transform.origin.z)
|
||||
};
|
||||
Vector3 godot_fwd = transform.basis.xform(Vector3(0, 0, -1));
|
||||
IPLVector3 fwd = {
|
||||
static_cast<float>(godot_fwd.x),
|
||||
static_cast<float>(godot_fwd.y),
|
||||
static_cast<float>(godot_fwd.z)
|
||||
};
|
||||
Vector3 godot_up = transform.basis.xform(Vector3(0, 1, 0));
|
||||
IPLVector3 up = {
|
||||
static_cast<float>(godot_up.x),
|
||||
static_cast<float>(godot_up.y),
|
||||
static_cast<float>(godot_up.z)
|
||||
};
|
||||
IPLVector3 right = {
|
||||
fwd.y * up.z - fwd.z * up.y,
|
||||
fwd.z * up.x - fwd.x * up.z,
|
||||
fwd.x * up.y - fwd.y * up.x
|
||||
};
|
||||
IPLCoordinateSpace3 listenerCS{};
|
||||
listenerCS.origin = pos;
|
||||
listenerCS.ahead = fwd;
|
||||
listenerCS.up = up;
|
||||
listenerCS.right = right;
|
||||
IPLSimulationSharedInputs sharedInputs{};
|
||||
sharedInputs.listener = listenerCS;
|
||||
}
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
//
|
||||
// Created by bryce on 5/8/2025.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#pragma once
|
||||
#include <godot_cpp/classes/node3d.hpp>
|
||||
#include<godot_cpp/godot.hpp>
|
||||
#include <godot_cpp/classes/audio_listener3d.hpp>
|
||||
#include <godot_cpp/godot.hpp>
|
||||
#include "steam_audio.h"
|
||||
#include <phonon.h>
|
||||
#include <godot_cpp/core/class_db.hpp>
|
||||
#include <godot_cpp/classes/wrapped.hpp>
|
||||
|
||||
using namespace godot;
|
||||
|
||||
class SteamAudioListener : public AudioListener3D {
|
||||
class SteamAudioListener:public AudioListener3D
|
||||
{
|
||||
GDCLASS(SteamAudioListener,AudioListener3D)
|
||||
Ref<SteamAudio> ctx;
|
||||
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _process(double p_delta) override;
|
||||
public:
|
||||
void set_context(const Ref<SteamAudio> &p_ctx);
|
||||
void _process(double delta) override;
|
||||
SteamAudioListener();
|
||||
~SteamAudioListener() override;
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
39
src/steam_audio_material.cpp
Normal file
39
src/steam_audio_material.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#include "steam_audio_material.h"
|
||||
|
||||
using namespace godot;
|
||||
|
||||
void SteamAudioMaterial::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("get_absorption"), &SteamAudioMaterial::get_absorption);
|
||||
ClassDB::bind_method(D_METHOD("set_absorption","value"), &SteamAudioMaterial::set_absorption);
|
||||
ClassDB::bind_method(D_METHOD("get_scattering"), &SteamAudioMaterial::get_scattering);
|
||||
ClassDB::bind_method(D_METHOD("set_scattering","value"), &SteamAudioMaterial::set_scattering);
|
||||
ClassDB::bind_method(D_METHOD("get_transmission"), &SteamAudioMaterial::get_transmission);
|
||||
ClassDB::bind_method(D_METHOD("set_transmission","value"), &SteamAudioMaterial::set_transmission);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3,"absorption"), "set_absorption", "get_absorption");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT,"scattering"), "set_scattering", "get_scattering");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3,"transmission"), "set_transmission", "get_transmission");
|
||||
}
|
||||
Vector3 SteamAudioMaterial::get_absorption() const {return absorption;}
|
||||
void SteamAudioMaterial::set_absorption(Vector3 value) { absorption = value; }
|
||||
float SteamAudioMaterial::get_scattering() const {return scattering;}
|
||||
void SteamAudioMaterial::set_scattering(float value) { scattering = value; }
|
||||
Vector3 SteamAudioMaterial::get_transmission() const {return transmission;}
|
||||
void SteamAudioMaterial::set_transmission(Vector3 value) { transmission = value; }
|
||||
|
||||
IPLMaterial SteamAudioMaterial::to_ipl_material() const {
|
||||
IPLMaterial material ={};
|
||||
material.absorption[0] = absorption.x;
|
||||
material.absorption[1] = absorption.y;
|
||||
material.absorption[2] = absorption.z;
|
||||
|
||||
material.scattering = scattering;
|
||||
|
||||
material.transmission[0] = transmission.x;
|
||||
material.transmission[1] = transmission.y;
|
||||
material.transmission[2] = transmission.z;
|
||||
|
||||
return material;
|
||||
}
|
||||
|
||||
|
||||
27
src/steam_audio_material.h
Normal file
27
src/steam_audio_material.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <godot_cpp/classes/resource.hpp>
|
||||
#include "godot_cpp/godot.hpp"
|
||||
#include <godot_cpp/core/class_db.hpp>
|
||||
#include "phonon.h"
|
||||
|
||||
using namespace godot;
|
||||
|
||||
class SteamAudioMaterial : public Resource {
|
||||
GDCLASS(SteamAudioMaterial,Resource) // NOLINT(modernize-use-auto, hicpp-use-auto)
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
private:
|
||||
Vector3 absorption = Vector3(0.2, 0.2, 0.2);
|
||||
float scattering = 0.0;
|
||||
Vector3 transmission = Vector3(0.0, 0.0, 0.0);
|
||||
public:
|
||||
void set_absorption(const Vector3 value);
|
||||
void set_scattering(const float value);
|
||||
void set_transmission(const Vector3 value);
|
||||
Vector3 get_absorption() const;
|
||||
[[nodiscard]] float get_scattering() const;
|
||||
Vector3 get_transmission() const;
|
||||
|
||||
[[nodiscard]] IPLMaterial to_ipl_material() const;
|
||||
};
|
||||
@@ -1,95 +0,0 @@
|
||||
//
|
||||
// Created by bryce on 5/8/2025.
|
||||
//
|
||||
|
||||
#include "steam_audio_scene.h"
|
||||
|
||||
using namespace godot;
|
||||
|
||||
SteamAudioScene::SteamAudioScene() = default;
|
||||
SteamAudioScene::~SteamAudioScene() {
|
||||
if (scene) {
|
||||
iplSceneRelease(&scene);
|
||||
}
|
||||
}
|
||||
|
||||
void SteamAudioScene::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_context","ctx"), &SteamAudioScene::set_context);
|
||||
ClassDB::bind_method(D_METHOD("add_mesh","mesh_node"), &SteamAudioScene::add_mesh);
|
||||
ClassDB::bind_method(D_METHOD("commit"), &SteamAudioScene::commit);
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT,"context",PROPERTY_HINT_RESOURCE_TYPE,"SteamAudio"), "set_context","");
|
||||
}
|
||||
|
||||
void SteamAudioScene::set_context(const Ref<SteamAudio> &p_ctx) {
|
||||
ERR_FAIL_COND(!p_ctx.is_valid());
|
||||
ctx = p_ctx;
|
||||
if (scene) {
|
||||
iplSceneRelease(&scene);
|
||||
scene = nullptr;
|
||||
}
|
||||
IPLSceneSettings settings{};
|
||||
|
||||
settings.type = ctx-> getEmbreeDevice() ? IPL_SCENETYPE_EMBREE : IPL_SCENETYPE_DEFAULT;
|
||||
settings.embreeDevice = ctx-> getEmbreeDevice();
|
||||
iplSceneCreate(ctx->getContext(), &settings, &scene);
|
||||
}
|
||||
|
||||
void SteamAudioScene::commit() const {
|
||||
ERR_FAIL_COND(!scene);
|
||||
ERR_FAIL_COND(!ctx.is_valid());
|
||||
iplSceneCommit(scene) ;
|
||||
}
|
||||
void SteamAudioScene::add_mesh(MeshInstance3D *mesh_node) {
|
||||
ERR_FAIL_COND(!ctx.is_valid());
|
||||
ERR_FAIL_COND(!scene);
|
||||
ERR_FAIL_COND(!mesh_node);
|
||||
|
||||
// 1) Fetch the ArrayMesh from the MeshInstance3D
|
||||
Ref<Mesh> mesh = mesh_node->get_mesh();
|
||||
ERR_FAIL_COND(!mesh.is_valid());
|
||||
|
||||
ArrayMesh *raw_am = Object::cast_to<ArrayMesh>(mesh.ptr());
|
||||
ERR_FAIL_COND(!raw_am);
|
||||
Ref<ArrayMesh> am(raw_am);
|
||||
ERR_FAIL_COND(!am.is_valid());
|
||||
|
||||
// 2) Pull out the raw arrays
|
||||
Array arrays = am->surface_get_arrays(0);
|
||||
PackedVector3Array verts = arrays[ArrayMesh::ARRAY_VERTEX];
|
||||
PackedInt32Array idxs = arrays[ArrayMesh::ARRAY_INDEX];
|
||||
|
||||
int vertex_count = verts.size();
|
||||
int index_count = idxs.size();
|
||||
int triangle_count = index_count / 3;
|
||||
|
||||
// 3) Build Steam Audio’s settings struct
|
||||
IPLStaticMeshSettings sm{};
|
||||
sm.numVertices = vertex_count;
|
||||
sm.numTriangles = triangle_count;
|
||||
sm.numMaterials = 0;
|
||||
sm.vertices = reinterpret_cast<IPLVector3*>(
|
||||
const_cast<Vector3*>(verts.ptr())
|
||||
);
|
||||
sm.materialIndices = nullptr;
|
||||
sm.materials = nullptr;
|
||||
|
||||
// 4) Convert the flat index list into IPLTriangle[]
|
||||
IPLTriangle *tris = static_cast<IPLTriangle*>(
|
||||
malloc(sizeof(IPLTriangle) * triangle_count)
|
||||
);
|
||||
for (int i = 0; i < triangle_count; ++i) {
|
||||
tris[i].indices[0] = idxs.ptr()[3*i + 0];
|
||||
tris[i].indices[1] = idxs.ptr()[3*i + 1];
|
||||
tris[i].indices[2] = idxs.ptr()[3*i + 2];
|
||||
}
|
||||
sm.triangles = tris;
|
||||
|
||||
// 5) Create & add the static mesh to the scene
|
||||
IPLStaticMesh static_mesh = nullptr;
|
||||
iplStaticMeshCreate(scene, &sm, &static_mesh);
|
||||
iplStaticMeshAdd (static_mesh, scene);
|
||||
iplStaticMeshRelease(&static_mesh);
|
||||
|
||||
// 6) Clean up our temporary triangle array
|
||||
::free(tris);
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <godot_cpp/classes/array_mesh.hpp>
|
||||
#include <godot_cpp/classes/mesh_instance3d.hpp>
|
||||
#include <godot_cpp/classes/ref_counted.hpp>
|
||||
#include <godot_cpp/variant/typed_array.hpp>
|
||||
#include <godot_cpp/variant/utility_functions.hpp>
|
||||
#include <godot_cpp/variant/packed_vector3_array.hpp>
|
||||
#include <godot_cpp/variant/packed_int32_array.hpp>
|
||||
#include <godot_cpp/classes/object.hpp>
|
||||
#include "godot_cpp/godot.hpp"
|
||||
#include "phonon.h"
|
||||
#include "steam_audio.h"
|
||||
|
||||
using namespace godot;
|
||||
|
||||
class SteamAudioScene : public RefCounted {
|
||||
GDCLASS(SteamAudioScene, RefCounted);
|
||||
|
||||
IPLScene scene = nullptr;
|
||||
Ref<SteamAudio> ctx;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
public:
|
||||
SteamAudioScene();
|
||||
~SteamAudioScene() override;
|
||||
|
||||
void set_context(const Ref<SteamAudio> &p_ctx);
|
||||
|
||||
void add_mesh(MeshInstance3D *mesh_node);
|
||||
|
||||
void commit() const;
|
||||
};
|
||||
@@ -1,30 +1,22 @@
|
||||
//
|
||||
// Created by bryce on 5/9/2025.
|
||||
//
|
||||
#include "steam_audio_source.h"
|
||||
|
||||
#include "steam_audio_source.h"
|
||||
|
||||
#include "steam_audio.h"
|
||||
#include "steam_audio_globals.h"
|
||||
|
||||
using namespace godot;
|
||||
|
||||
SteamAudioSource::SteamAudioSource() {
|
||||
|
||||
SteamAudioSource::SteamAudioSource() {
|
||||
iplSourceCreate(sa->get_simulator(),&settings,pSource);
|
||||
}
|
||||
SteamAudioSource::~SteamAudioSource() {
|
||||
if (steam_audio_source) {
|
||||
iplSourceRelease(&steam_audio_source);
|
||||
steam_audio_source = nullptr;
|
||||
}
|
||||
SteamAudioSource::~SteamAudioSource() {
|
||||
iplSourceRelease(pSource);
|
||||
}
|
||||
|
||||
void SteamAudioSource::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("play"), &SteamAudioSource::play);
|
||||
ClassDB::bind_method(D_METHOD("stop"), &SteamAudioSource::stop);
|
||||
ClassDB::bind_method(D_METHOD("set_stream","stream"), &SteamAudioSource::set_stream);
|
||||
}
|
||||
}
|
||||
|
||||
void SteamAudioSource::play() {
|
||||
audio_player = memnew(AudioStreamPlayer3D);
|
||||
add_child(audio_player);
|
||||
}
|
||||
void SteamAudioSource::_process(double delta) {
|
||||
update_position();
|
||||
}
|
||||
bool SteamAudioSource::update_position() {
|
||||
IPLCoordinateSpace3 current_pos = sa->godot_to_ipl_space(get_transform());
|
||||
|
||||
iplSource
|
||||
}
|
||||
|
||||
@@ -1,32 +1,29 @@
|
||||
//
|
||||
// Created by bryce on 5/9/2025.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#pragma once
|
||||
#include <godot_cpp/classes/audio_stream_player3d.hpp>
|
||||
#include <godot_cpp/classes/node3d.hpp>
|
||||
|
||||
#include <godot_cpp/classes/audio_stream_generator.hpp>
|
||||
#include <godot_cpp/classes/audio_stream_generator_playback.hpp>
|
||||
#include <godot_cpp/classes/audio_stream_player3d.hpp>
|
||||
#include <godot_cpp/godot.hpp>
|
||||
#include <godot_cpp/core/class_db.hpp>
|
||||
#include "steam_audio.h"
|
||||
#include "phonon.h"
|
||||
|
||||
using namespace godot;
|
||||
|
||||
class SteamAudioSource:Node3D {
|
||||
GDCLASS(SteamAudioSource, Node3D)
|
||||
private:
|
||||
AudioStreamPlayer3D* audio_player = nullptr;
|
||||
IPLSource steam_audio_source = nullptr;
|
||||
class SteamAudioSource: public Node3D
|
||||
{
|
||||
GDCLASS(SteamAudioSource,Node3D)
|
||||
|
||||
bool initialized = false;
|
||||
IPLSource *pSource = nullptr;
|
||||
SteamAudio *sa = SteamAudio::steam_audio;
|
||||
IPLSourceSettings settings{};
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _process(double p_delta) override;
|
||||
public:
|
||||
SteamAudioSource();
|
||||
~SteamAudioSource();
|
||||
bool update_position();
|
||||
|
||||
void _ready() override;
|
||||
void _process(double p_delta) override;
|
||||
|
||||
void play();
|
||||
void stop();
|
||||
void set_stream(const Ref<AudioStream>& p_stream);
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
};
|
||||
181
src/steam_audio_static_mesh.cpp
Normal file
181
src/steam_audio_static_mesh.cpp
Normal file
@@ -0,0 +1,181 @@
|
||||
// steam_audio_static_mesh.cpp
|
||||
#include "steam_audio_static_mesh.h"
|
||||
|
||||
#include <godot_cpp/classes/array_mesh.hpp>
|
||||
#include <godot_cpp/classes/convex_polygon_shape3d.hpp>
|
||||
#include <godot_cpp/classes/shape3d.hpp>
|
||||
#include <godot_cpp/variant/packed_int32_array.hpp>
|
||||
#include <godot_cpp/variant/packed_vector3_array.hpp>
|
||||
#include <phonon.h> // IPLStaticMesh, IPLStaticMeshSettings, IPLMaterial
|
||||
|
||||
using namespace godot;
|
||||
|
||||
SteamAudioStaticMesh::SteamAudioStaticMesh() = default;
|
||||
|
||||
SteamAudioStaticMesh::~SteamAudioStaticMesh() {
|
||||
// release any previously created Steam Audio meshes
|
||||
for (auto &sm : static_meshes) {
|
||||
iplStaticMeshRelease(&sm);
|
||||
}
|
||||
static_meshes.clear();
|
||||
}
|
||||
|
||||
void SteamAudioStaticMesh::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_proxy_mode", "mode"), &SteamAudioStaticMesh::set_proxy_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_proxy_mode"), &SteamAudioStaticMesh::get_proxy_mode);
|
||||
ADD_PROPERTY(
|
||||
PropertyInfo(Variant::INT, "proxy_mode",PROPERTY_HINT_ENUM,"None,Convex,Custom"), "set_proxy_mode","get_proxy_mode");
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_custom_proxy_mesh", "mesh"), &SteamAudioStaticMesh::set_custom_proxy_mesh);
|
||||
ClassDB::bind_method(D_METHOD("get_custom_proxy_mesh"), &SteamAudioStaticMesh::get_custom_proxy_mesh);
|
||||
ADD_PROPERTY(
|
||||
PropertyInfo(Variant::OBJECT, "custom_proxy_mesh",PROPERTY_HINT_RESOURCE_TYPE,"Mesh"), "set_custom_proxy_mesh","get_custom_proxy_mesh");
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_default_material", "mat"), &SteamAudioStaticMesh::set_default_material);
|
||||
ClassDB::bind_method(D_METHOD("get_default_material"), &SteamAudioStaticMesh::get_default_material);
|
||||
|
||||
// And still bind your proxy-generator:
|
||||
ClassDB::bind_method(D_METHOD("generate_proxy_mesh"), &SteamAudioStaticMesh::generate_proxy_mesh);
|
||||
|
||||
}
|
||||
|
||||
// Setters & getters ---------------------------------------------------
|
||||
void SteamAudioStaticMesh::set_proxy_mode(int mode) {
|
||||
proxy_mode = mode;
|
||||
}
|
||||
int SteamAudioStaticMesh::get_proxy_mode() const {
|
||||
return proxy_mode;
|
||||
}
|
||||
|
||||
void SteamAudioStaticMesh::set_custom_proxy_mesh(Ref<Mesh> mesh) {
|
||||
custom_proxy_mesh = mesh;
|
||||
}
|
||||
Ref<Mesh> SteamAudioStaticMesh::get_custom_proxy_mesh() const {
|
||||
return custom_proxy_mesh;
|
||||
}
|
||||
|
||||
void SteamAudioStaticMesh::set_default_material(Ref<SteamAudioMaterial> mat) {
|
||||
default_material = mat;
|
||||
}
|
||||
Ref<SteamAudioMaterial> SteamAudioStaticMesh::get_default_material() const {
|
||||
return default_material;
|
||||
}
|
||||
|
||||
// Core functionality --------------------------------------------------
|
||||
void SteamAudioStaticMesh::generate_proxy_mesh() {
|
||||
// 1) Clean up any old meshes
|
||||
for (auto &sm : static_meshes) {
|
||||
iplStaticMeshRelease(&sm);
|
||||
}
|
||||
static_meshes.clear();
|
||||
|
||||
// 2) Pick which Mesh to read triangles from
|
||||
Ref<Mesh> target;
|
||||
switch (proxy_mode) {
|
||||
case PROXY_NONE:
|
||||
target = cast_to<MeshInstance3D>(get_parent())->get_mesh();
|
||||
break;
|
||||
case PROXY_CONVEX: {
|
||||
Ref<Shape3D> hull_shape = cast_to<MeshInstance3D>(get_parent())->get_mesh()->create_convex_shape();
|
||||
ConvexPolygonShape3D *convex = cast_to<ConvexPolygonShape3D>(hull_shape.ptr());
|
||||
if (!convex) {
|
||||
ERR_PRINT("SteamAudioStaticMesh: convex cast failed");
|
||||
return;
|
||||
}
|
||||
Ref<ArrayMesh> debug_mesh = convex->get_debug_mesh();
|
||||
if (!debug_mesh.is_valid()) {
|
||||
ERR_PRINT("SteamAudioStaticMesh: debug mesh invalid");
|
||||
return;
|
||||
}
|
||||
target = debug_mesh;
|
||||
break;
|
||||
}
|
||||
case PROXY_CUSTOM:
|
||||
if (!custom_proxy_mesh.is_valid()) {
|
||||
ERR_PRINT("SteamAudioStaticMesh: custom_proxy_mesh not set");
|
||||
return;
|
||||
}
|
||||
target = custom_proxy_mesh;
|
||||
break;
|
||||
default:
|
||||
ERR_PRINT("SteamAudioStaticMesh: unknown proxy_mode");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!target.is_valid()) {
|
||||
ERR_PRINT("SteamAudioStaticMesh: target mesh invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
// 3) For each surface, build and add a Steam Audio static mesh
|
||||
const int surface_count = target->get_surface_count();
|
||||
for (int s = 0; s < surface_count; ++s) {
|
||||
Array arrays = target->surface_get_arrays(s);
|
||||
PackedVector3Array verts = arrays[Mesh::ARRAY_VERTEX];
|
||||
PackedInt32Array idxs = arrays[Mesh::ARRAY_INDEX];
|
||||
|
||||
// --- build IPLMaterial from metadata or default_material ---
|
||||
Ref<Material> gm = target->surface_get_material(s);
|
||||
Ref<SteamAudioMaterial> sa_mat;
|
||||
if (gm.is_valid() && gm->has_meta("steam_audio")) {
|
||||
sa_mat = gm->get_meta("steam_audio");
|
||||
}
|
||||
if (!sa_mat.is_valid()) {
|
||||
sa_mat = default_material;
|
||||
}
|
||||
IPLMaterial material{}; // zero initialize all fields
|
||||
// absorption[0..2]
|
||||
material.absorption[0] = sa_mat->get_absorption().x;
|
||||
material.absorption[1] = sa_mat->get_absorption().y;
|
||||
material.absorption[2] = sa_mat->get_absorption().z;
|
||||
// scattering
|
||||
material.scattering = sa_mat->get_scattering();
|
||||
// transmission[0..2]
|
||||
material.transmission[0] = sa_mat->get_transmission().x;
|
||||
material.transmission[1] = sa_mat->get_transmission().y;
|
||||
material.transmission[2] = sa_mat->get_transmission().z;
|
||||
|
||||
// --- fill out static mesh settings ---
|
||||
IPLStaticMeshSettings settings{};
|
||||
|
||||
// Number of vertices
|
||||
settings.numVertices = verts.size();
|
||||
|
||||
// reinterpret 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;
|
||||
}
|
||||
iplStaticMeshAdd(sm, global_scene);
|
||||
static_meshes.push_back(sm);*/
|
||||
}
|
||||
|
||||
// 4) Commit all additions at once
|
||||
// iplSceneCommit(global_scene);
|
||||
}
|
||||
52
src/steam_audio_static_mesh.h
Normal file
52
src/steam_audio_static_mesh.h
Normal file
@@ -0,0 +1,52 @@
|
||||
// steam_audio_proxy_mesh.h
|
||||
#pragma once
|
||||
|
||||
#include <godot_cpp/core/class_db.hpp>
|
||||
#include <godot_cpp/classes/mesh_instance3d.hpp>
|
||||
#include <godot_cpp/classes/mesh.hpp>
|
||||
#include <godot_cpp/classes/shape3d.hpp>
|
||||
#include <godot_cpp/classes/convex_polygon_shape3d.hpp>
|
||||
#include <godot_cpp/classes/array_mesh.hpp>
|
||||
#include <godot_cpp/classes/material.hpp>
|
||||
|
||||
#include "steam_audio_material.h"
|
||||
|
||||
using namespace godot;
|
||||
|
||||
// Proxy generation modes
|
||||
enum ProxyMode {
|
||||
PROXY_NONE = 0,
|
||||
PROXY_CONVEX = 1,
|
||||
PROXY_CUSTOM = 2,
|
||||
};
|
||||
|
||||
class SteamAudioStaticMesh : public Node3D {
|
||||
GDCLASS(SteamAudioStaticMesh, Node3D)
|
||||
|
||||
private:
|
||||
int proxy_mode = PROXY_NONE;
|
||||
Ref<Mesh> custom_proxy_mesh;
|
||||
Ref<SteamAudioMaterial> default_material;
|
||||
Vector<IPLStaticMesh> static_meshes;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
|
||||
public:
|
||||
SteamAudioStaticMesh();
|
||||
~SteamAudioStaticMesh() override;
|
||||
|
||||
// Setters & getters
|
||||
void set_proxy_mode(int mode);
|
||||
int get_proxy_mode() const;
|
||||
|
||||
void set_custom_proxy_mesh(Ref<Mesh> mesh);
|
||||
Ref<Mesh> get_custom_proxy_mesh() const;
|
||||
|
||||
void set_default_material(Ref<SteamAudioMaterial> mat);
|
||||
Ref<SteamAudioMaterial> get_default_material() const;
|
||||
|
||||
// Regenerates Steam Audio proxies
|
||||
void generate_proxy_mesh();
|
||||
};
|
||||
Reference in New Issue
Block a user