diff --git a/README.md b/README.md index 3341b878..4a0c6f07 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Steam Audio 4.5.2 +# Steam Audio 4.5.3 Valve Corporation diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index bb11d571..8b091cf8 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -14,7 +14,7 @@ cmake_minimum_required(VERSION 3.17) -project(Phonon VERSION 4.5.2) +project(Phonon VERSION 4.5.3) set(CMAKE_CXX_STANDARD 14) set(CMAKE_MODULE_PATH "${CMAKE_HOME_DIRECTORY}/build") @@ -179,6 +179,9 @@ if (IPL_OS_LINUX) if (IPL_CPU_X86) add_compile_options(-m32 -mfpmath=sse -march=native) endif() + if (STEAMAUDIO_ENABLE_AVX) + add_compile_options(-fabi-version=6) + endif() endif() # macOS flags diff --git a/core/src/core/api_internal.cpp b/core/src/core/api_internal.cpp deleted file mode 100644 index 74681286..00000000 --- a/core/src/core/api_internal.cpp +++ /dev/null @@ -1,1310 +0,0 @@ -// -// Copyright 2017-2023 Valve Corporation. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#include "containers.h" -#include "direct_simulator.h" -#include "energy_field_factory.h" -#include "hrtf_database.h" -#include "hybrid_reverb_estimator.h" -#include "impulse_response_factory.h" -#include "opencl_energy_field.h" -#include "opencl_impulse_response.h" -#include "path_simulator.h" -#include "profiler.h" -#include "reconstructor_factory.h" -#include "reflection_simulator_factory.h" -#include "sh.h" -#include "simulation_data.h" -#include "simulation_manager.h" -using namespace ipl; - -#include "phonon.h" -#include "util.h" - -#define STEAMAUDIO_SKIP_API_FUNCTIONS -#include "phonon_interfaces.h" -#include "api_context.h" -#include "api_opencl_device.h" -#include "api_radeonrays_device.h" -#include "api_tan_device.h" -#include "api_scene.h" -#include "api_probes.h" -#include "api_simulator.h" - -#include "phonon_internal.h" - -// -------------------------------------------------------------------------------------------------------------------- -// Context -// -------------------------------------------------------------------------------------------------------------------- - -void IPLCALL iplContextSetVariableBool(IPLContext context, IPLstring name, IPLbool value) -{ - if (!context) - return; - - if (string(name) == "enable_dc_correction_for_phase_interpolation") - { - HRTFDatabase::sEnableDCCorrectionForPhaseInterpolation = value; - } - else if (string(name) == "enable_paths_from_all_source_probes") - { - PathSimulator::sEnablePathsFromAllSourceProbes = value; - } -} - -void IPLCALL iplContextSetVariableInt32(IPLContext context, IPLstring name, IPLint32 value) -{ - if (!context) - return; -} - -void IPLCALL iplContextSetVariableFloat32(IPLContext context, IPLstring name, IPLfloat32 value) -{ - if (!context) - return; - - if (string(name) == "max_hrtf_normalization_volume_gain_db") - { - Loudness::sMaxVolumeNormGainDB = value; - } -} - -void IPLCALL iplContextSetVariableString(IPLContext context, IPLstring name, IPLstring value) -{ - if (!context) - return; -} - -void IPLCALL iplContextSetProfilerContext(IPLContext context, void* profilerContext) -{ - if (!context) - return; - - reinterpret_cast(context)->setProfilerContext(profilerContext); -} - - -// -------------------------------------------------------------------------------------------------------------------- -// Distance Attenuation -// -------------------------------------------------------------------------------------------------------------------- - -void IPLCALL iplDistanceAttenuationGetCorrectionCurve(IPLDistanceAttenuationModel* model, IPLint32 numSamples, IPLint32 samplingRate, IPLfloat32* correctionCurve) -{ - if (!model || numSamples <= 0 || samplingRate <= 0 || !correctionCurve) - return; - - DistanceAttenuationModel _model{}; - switch (model->type) - { - case IPL_DISTANCEATTENUATIONTYPE_DEFAULT: - _model = DistanceAttenuationModel{}; - break; - case IPL_DISTANCEATTENUATIONTYPE_INVERSEDISTANCE: - _model = DistanceAttenuationModel(model->minDistance, nullptr, nullptr); - break; - case IPL_DISTANCEATTENUATIONTYPE_CALLBACK: - _model = DistanceAttenuationModel(1.0f, model->callback, model->userData); - break; - } - - _model.generateCorrectionCurve(DistanceAttenuationModel{}, _model, samplingRate, numSamples, correctionCurve); -} - - -// -------------------------------------------------------------------------------------------------------------------- -// Direct Simulation -// -------------------------------------------------------------------------------------------------------------------- - -DEFINE_OPAQUE_HANDLE(IPLDirectSimulator, DirectSimulator); - -IPLerror IPLCALL iplDirectSimulatorCreate(IPLContext context, IPLDirectSimulatorSettings* settings, IPLDirectSimulator* simulator) -{ - if (!context || !settings || !simulator) - return IPL_STATUS_FAILURE; - - auto _context = reinterpret_cast(context)->mHandle.get(); - if (!_context) - return IPL_STATUS_FAILURE; - - try - { - *simulator = createHandle(_context, ipl::make_shared(settings->maxNumOcclusionSamples)); - } - catch (Exception e) - { - return static_cast(e.status()); - } - - return IPL_STATUS_SUCCESS; -} - -IPLDirectSimulator IPLCALL iplDirectSimulatorRetain(IPLDirectSimulator simulator) -{ - return retainHandle(simulator); -} - -void IPLCALL iplDirectSimulatorRelease(IPLDirectSimulator* simulator) -{ - releaseHandle(*simulator); -} - -void IPLCALL iplDirectSimulatorSimulate(IPLDirectSimulator simulator, IPLScene scene, IPLDirectSimulatorParams* inputs, IPLDirectEffectParams* outputs) -{ - if (!simulator || !scene || !inputs || !outputs) - return; - - auto _simulator = derefHandle(simulator); - auto _scene = reinterpret_cast(scene)->mHandle.get(); - if (!_simulator || !_scene) - return; - - auto _flags = static_cast(inputs->flags); - auto _source = *reinterpret_cast(&inputs->source); - auto _listener = *reinterpret_cast(&inputs->listener); - auto _directivity = *reinterpret_cast(&inputs->directivity); - auto _occlusionType = static_cast(inputs->occlusionType); - - DistanceAttenuationModel _distanceAttenuationModel{}; - switch (inputs->distanceAttenuationModel.type) - { - case IPL_DISTANCEATTENUATIONTYPE_DEFAULT: - _distanceAttenuationModel = DistanceAttenuationModel{}; - break; - case IPL_DISTANCEATTENUATIONTYPE_INVERSEDISTANCE: - _distanceAttenuationModel = DistanceAttenuationModel(inputs->distanceAttenuationModel.minDistance, nullptr, nullptr); - break; - case IPL_DISTANCEATTENUATIONTYPE_CALLBACK: - _distanceAttenuationModel = DistanceAttenuationModel(1.0f, inputs->distanceAttenuationModel.callback, inputs->distanceAttenuationModel.userData); - break; - } - - AirAbsorptionModel _airAbsorptionModel{}; - switch (inputs->airAbsorptionModel.type) - { - case IPL_AIRABSORPTIONTYPE_DEFAULT: - _airAbsorptionModel = AirAbsorptionModel{}; - break; - case IPL_AIRABSORPTIONTYPE_EXPONENTIAL: - _airAbsorptionModel = AirAbsorptionModel(inputs->airAbsorptionModel.coefficients, nullptr, nullptr); - break; - case IPL_AIRABSORPTIONTYPE_CALLBACK: - _airAbsorptionModel = AirAbsorptionModel(nullptr, inputs->airAbsorptionModel.callback, inputs->airAbsorptionModel.userData); - break; - } - - DirectSoundPath _directPath{}; - - _simulator->simulate(_scene.get(), _flags, _source, _listener, _distanceAttenuationModel, _airAbsorptionModel, - _directivity, _occlusionType, inputs->occlusionRadius, inputs->numOcclusionSamples, - inputs->numTransmissionRays, _directPath); - - outputs->distanceAttenuation = _directPath.distanceAttenuation; - outputs->airAbsorption[0] = _directPath.airAbsorption[0]; - outputs->airAbsorption[1] = _directPath.airAbsorption[1]; - outputs->airAbsorption[2] = _directPath.airAbsorption[2]; - outputs->directivity = _directPath.directivity; - outputs->occlusion = _directPath.occlusion; - outputs->transmission[0] = _directPath.transmission[0]; - outputs->transmission[1] = _directPath.transmission[1]; - outputs->transmission[2] = _directPath.transmission[2]; -} - - -// -------------------------------------------------------------------------------------------------------------------- -// Energy Field -// -------------------------------------------------------------------------------------------------------------------- - -DEFINE_OPAQUE_HANDLE(IPLEnergyField, EnergyField); - -IPLerror IPLCALL iplEnergyFieldCreate(IPLContext context, IPLEnergyFieldSettings* settings, IPLEnergyField* energyField) -{ - if (!context || !settings || !energyField) - return IPL_STATUS_FAILURE; - - auto _sceneType = static_cast(settings->sceneType); - auto _openCLDevice = reinterpret_cast(settings->openCLDevice)->mHandle.get(); - - auto _context = reinterpret_cast(context)->mHandle.get(); - if (!_context) - return IPL_STATUS_FAILURE; - - try - { - *energyField = createHandle(_context, ipl::shared_ptr(EnergyFieldFactory::create(_sceneType, - settings->duration, settings->order, _openCLDevice))); - } - catch (Exception e) - { - return static_cast(e.status()); - } - - return IPL_STATUS_SUCCESS; -} - -IPLEnergyField IPLCALL iplEnergyFieldRetain(IPLEnergyField energyField) -{ - return retainHandle(energyField); -} - -void IPLCALL iplEnergyFieldRelease(IPLEnergyField* energyField) -{ - releaseHandle(*energyField); -} - -IPLint32 IPLCALL iplEnergyFieldGetNumChannels(IPLEnergyField energyField) -{ - if (!energyField) - return 0; - - auto _energyField = derefHandle(energyField); - if (!_energyField) - return 0; - - return _energyField->numChannels(); -} - -IPLint32 IPLCALL iplEnergyFieldGetNumBins(IPLEnergyField energyField) -{ - if (!energyField) - return 0; - - auto _energyField = derefHandle(energyField); - if (!_energyField) - return 0; - - return _energyField->numBins(); -} - -IPLint32 IPLCALL iplEnergyFieldGetSize(IPLEnergyField energyField) -{ - if (!energyField) - return 0; - - auto _energyField = derefHandle(energyField); - if (!_energyField) - return 0; - - return _energyField->numChannels() * Bands::kNumBands * _energyField->numBins(); -} - -IPLfloat32* IPLCALL iplEnergyFieldGetData(IPLEnergyField energyField) -{ - if (!energyField) - return nullptr; - - auto _energyField = derefHandle(energyField); - if (!_energyField) - return nullptr; - - return _energyField->flatData(); -} - -void IPLCALL iplEnergyFieldSetData(IPLEnergyField energyField, IPLfloat32* data) -{ - if (!energyField) - return; - - auto _energyField = derefHandle(energyField); - if (!_energyField) - return; - - auto size = _energyField->numChannels() * Bands::kNumBands * _energyField->numBins(); - memcpy(_energyField->flatData(), data, size * sizeof(float)); -} - -void IPLCALL iplEnergyFieldCopyHostToDevice(IPLEnergyField energyField) -{ - if (!energyField) - return; - - auto _energyField = derefHandle(energyField); - if (!_energyField) - return; - -#if defined(IPL_USES_RADEONRAYS) - static_cast(_energyField.get())->copyHostToDevice(); -#endif -} - -void IPLCALL iplEnergyFieldCopyDeviceToHost(IPLEnergyField energyField) -{ - if (!energyField) - return; - - auto _energyField = derefHandle(energyField); - if (!_energyField) - return; - -#if defined(IPL_USES_RADEONRAYS) - static_cast(_energyField.get())->copyDeviceToHost(); -#endif -} - - -// -------------------------------------------------------------------------------------------------------------------- -// Impulse Response -// -------------------------------------------------------------------------------------------------------------------- - -DEFINE_OPAQUE_HANDLE(IPLImpulseResponse, ImpulseResponse); - -IPLerror IPLCALL iplImpulseResponseCreate(IPLContext context, IPLImpulseResponseSettings* settings, IPLImpulseResponse* impulseResponse) -{ - if (!context || !settings || !impulseResponse) - return IPL_STATUS_FAILURE; - - auto _indirectType = static_cast(settings->indirectType); - auto _openCLDevice = reinterpret_cast(settings->openCLDevice)->mHandle.get(); - - auto _context = reinterpret_cast(context)->mHandle.get(); - if (!_context) - return IPL_STATUS_FAILURE; - - try - { - *impulseResponse = createHandle(_context, ipl::shared_ptr(ImpulseResponseFactory::create(_indirectType, - settings->duration, settings->order, settings->samplingRate, _openCLDevice))); - } - catch (Exception e) - { - return static_cast(e.status()); - } - - return IPL_STATUS_SUCCESS; -} - -IPLImpulseResponse IPLCALL iplImpulseResponseRetain(IPLImpulseResponse impulseResponse) -{ - return retainHandle(impulseResponse); -} - -void IPLCALL iplImpulseResponseRelease(IPLImpulseResponse* impulseResponse) -{ - releaseHandle(*impulseResponse); -} - -IPLint32 IPLCALL iplImpulseResponseGetNumChannels(IPLImpulseResponse impulseResponse) -{ - if (!impulseResponse) - return 0; - - auto _impulseResponse = derefHandle(impulseResponse); - if (!_impulseResponse) - return 0; - - return _impulseResponse->numChannels(); -} - -IPLint32 IPLCALL iplImpulseResponseGetNumSamples(IPLImpulseResponse impulseResponse) -{ - if (!impulseResponse) - return 0; - - auto _impulseResponse = derefHandle(impulseResponse); - if (!_impulseResponse) - return 0; - - return _impulseResponse->numSamples(); -} - -IPLint32 IPLCALL iplImpulseResponseGetSize(IPLImpulseResponse impulseResponse) -{ - if (!impulseResponse) - return 0; - - auto _impulseResponse = derefHandle(impulseResponse); - if (!_impulseResponse) - return 0; - - return _impulseResponse->numChannels() * _impulseResponse->numSamples(); -} - -IPLfloat32* IPLCALL iplImpulseResponseGetData(IPLImpulseResponse impulseResponse) -{ - if (!impulseResponse) - return nullptr; - - auto _impulseResponse = derefHandle(impulseResponse); - if (!_impulseResponse) - return nullptr; - - return _impulseResponse->data(); -} - -void IPLCALL iplImpulseResponseSetData(IPLImpulseResponse impulseResponse, IPLfloat32* data) -{ - if (!impulseResponse) - return; - - auto _impulseResponse = derefHandle(impulseResponse); - if (!_impulseResponse) - return; - - auto size = _impulseResponse->numChannels() * _impulseResponse->numSamples(); - memcpy(_impulseResponse->data(), data, size * sizeof(float)); -} - -void IPLCALL iplImpulseResponseCopyHostToDevice(IPLImpulseResponse impulseResponse) -{ - if (!impulseResponse) - return; - - auto _impulseResponse = derefHandle(impulseResponse); - if (!_impulseResponse) - return; - -#if defined(IPL_USES_OPENCL) - static_cast(_impulseResponse.get())->copyHostToDevice(); -#endif -} - -void IPLCALL iplImpulseResponseCopyDeviceToHost(IPLImpulseResponse impulseResponse) -{ - if (!impulseResponse) - return; - - auto _impulseResponse = derefHandle(impulseResponse); - if (!_impulseResponse) - return; - -#if defined(IPL_USES_OPENCL) - static_cast(_impulseResponse.get())->copyDeviceToHost(); -#endif -} - - -// -------------------------------------------------------------------------------------------------------------------- -// Indirect Effect IR -// -------------------------------------------------------------------------------------------------------------------- - -IPLerror IPLCALL iplIndirectEffectIRCreate(IPLContext context, IPLIndirectEffectIRSettings* settings, IPLReflectionEffectIR* ir) -{ - if (!context || !settings || !ir) - return IPL_STATUS_FAILURE; - - auto _context = reinterpret_cast(context)->mHandle.get(); - if (!_context) - return IPL_STATUS_FAILURE; - - try - { - auto _ir = ipl::make_shared>(); - - auto numChannels = SphericalHarmonics::numCoeffsForOrder(settings->order); - auto irSize = static_cast(ceilf(settings->duration * settings->samplingRate)); - - _ir->initBuffers(numChannels, irSize, settings->frameSize); - - *ir = createHandle(_context, _ir); - } - catch (Exception e) - { - return static_cast(e.status()); - } - - return IPL_STATUS_SUCCESS; -} - -IPLReflectionEffectIR IPLCALL iplIndirectEffectIRRetain(IPLReflectionEffectIR ir) -{ - return retainHandle>(ir); -} - -void IPLCALL iplIndirectEffectIRRelease(IPLReflectionEffectIR* ir) -{ - releaseHandle>(*ir); -} - - -// -------------------------------------------------------------------------------------------------------------------- -// Reflection Simulator -// -------------------------------------------------------------------------------------------------------------------- - -DEFINE_OPAQUE_HANDLE(IPLReflectionSimulator, IReflectionSimulator); - -IPLerror IPLCALL iplReflectionSimulatorCreate(IPLContext context, IPLReflectionSimulatorSettings* settings, IPLReflectionSimulator* simulator) -{ - if (!context || !settings || !simulator) - return IPL_STATUS_FAILURE; - - auto _sceneType = static_cast(settings->sceneType); - auto _radeonRaysDevice = reinterpret_cast(settings->radeonRaysDevice)->mHandle.get(); - - auto _context = reinterpret_cast(context)->mHandle.get(); - if (!_context) - return IPL_STATUS_FAILURE; - - try - { - *simulator = createHandle(_context, ipl::shared_ptr(ReflectionSimulatorFactory::create(_sceneType, - settings->maxNumRays, settings->numDiffuseSamples, settings->maxDuration, - settings->maxOrder, settings->maxNumSources, settings->maxNumListeners, - settings->numThreads, settings->rayBatchSize, _radeonRaysDevice))); - } - catch (Exception e) - { - return static_cast(e.status()); - } - - return IPL_STATUS_SUCCESS; -} - -IPLReflectionSimulator IPLCALL iplReflectionSimulatorRetain(IPLReflectionSimulator simulator) -{ - return retainHandle(simulator); -} - -void IPLCALL iplReflectionSimulatorRelease(IPLReflectionSimulator* simulator) -{ - releaseHandle(*simulator); -} - -void IPLCALL iplReflectionSimulatorSimulate(IPLReflectionSimulator simulator, IPLScene scene, IPLReflectionSimulatorParams* inputs, IPLReflectionSimulatorOutputs* outputs) -{ - if (!simulator || !scene || !inputs || !outputs) - return; - - auto _simulator = derefHandle(simulator); - auto _scene = reinterpret_cast(scene)->mHandle.get(); - if (!_simulator || !_scene) - return; - - auto _sources = reinterpret_cast(inputs->sources); - auto _listeners = reinterpret_cast(inputs->listeners); - auto _directivities = reinterpret_cast(inputs->directivities); - - // FIXME: Shouldn't have to allocate here. - auto numEnergyFields = std::max(inputs->numSources, inputs->numListeners); - Array _energyFields(numEnergyFields); - for (auto i = 0; i < numEnergyFields; ++i) - { - _energyFields[i] = derefHandle(outputs->energyFields[i]).get(); - } - - JobGraph jobGraph; - - _simulator->simulate(*_scene, inputs->numSources, _sources, inputs->numListeners, _listeners, _directivities, - inputs->numRays, inputs->numBounces, inputs->duration, inputs->order, - inputs->irradianceMinDistance, _energyFields.data(), jobGraph); - - // FIXME: Shouldn't have to recreate the thread pool here, ThreadPool should be exposed in the API. - ThreadPool threadPool(inputs->numThreads); - threadPool.process(jobGraph); -} - - -// -------------------------------------------------------------------------------------------------------------------- -// Reconstructor -// -------------------------------------------------------------------------------------------------------------------- - -DEFINE_OPAQUE_HANDLE(IPLReconstructor, IReconstructor); - -IPLerror IPLCALL iplReconstructorCreate(IPLContext context, IPLReconstructorSettings* settings, IPLReconstructor* reconstructor) -{ - if (!context || !settings || !reconstructor) - return IPL_STATUS_FAILURE; - - auto _sceneType = static_cast(settings->sceneType); - auto _indirectType = static_cast(settings->indirectType); - auto _radeonRaysDevice = reinterpret_cast(settings->radeonRaysDevice)->mHandle.get(); - - auto _context = reinterpret_cast(context)->mHandle.get(); - if (!_context) - return IPL_STATUS_FAILURE; - - try - { - *reconstructor = createHandle(_context, ipl::shared_ptr(ReconstructorFactory::create(_sceneType, - _indirectType, settings->maxDuration, settings->maxOrder, settings->samplingRate, - _radeonRaysDevice))); - } - catch (Exception e) - { - return static_cast(e.status()); - } - - return IPL_STATUS_SUCCESS; -} - -IPLReconstructor IPLCALL iplReconstructorRetain(IPLReconstructor reconstructor) -{ - return retainHandle(reconstructor); -} - -void IPLCALL iplReconstructorRelease(IPLReconstructor* reconstructor) -{ - releaseHandle(*reconstructor); -} - -void IPLCALL iplReconstructorReconstruct(IPLReconstructor reconstructor, IPLReconstructorParams* inputs, IPLReconstructorOutputs* outputs) -{ - if (!reconstructor || !inputs || !outputs) - return; - - auto _reconstructor = derefHandle(reconstructor); - if (!_reconstructor) - return; - - auto _reconstructionType = ReconstructionType::Linear; - - // FIXME: Shouldn't have to allocate here. - Array _energyFields(inputs->numIRs); - for (auto i = 0; i < inputs->numIRs; ++i) - { - _energyFields[i] = derefHandle(inputs->energyFields[i]).get(); - } - - // FIXME: Shouldn't have to allocate here. - Array _airAbsorptionModels(inputs->numIRs); - for (auto i = 0; i < inputs->numIRs; ++i) - { - switch (inputs->airAbsorptionModels[i].type) - { - case IPL_AIRABSORPTIONTYPE_DEFAULT: - _airAbsorptionModels[i] = AirAbsorptionModel{}; - break; - case IPL_AIRABSORPTIONTYPE_EXPONENTIAL: - _airAbsorptionModels[i] = AirAbsorptionModel(inputs->airAbsorptionModels[i].coefficients, nullptr, nullptr); - break; - case IPL_AIRABSORPTIONTYPE_CALLBACK: - _airAbsorptionModels[i] = AirAbsorptionModel(nullptr, inputs->airAbsorptionModels[i].callback, inputs->airAbsorptionModels[i].userData); - break; - } - } - - // FIXME: Shouldn't have to allocate here. - Array _impulseResponses(inputs->numIRs); - for (auto i = 0; i < inputs->numIRs; ++i) - { - _impulseResponses[i] = derefHandle(outputs->impulseResponses[i]).get(); - } - - _reconstructor->reconstruct(inputs->numIRs, _energyFields.data(), inputs->distanceAttenuationCorrectionCurves, - _airAbsorptionModels.data(), _impulseResponses.data(), _reconstructionType, - inputs->duration, inputs->order); -} - - -// -------------------------------------------------------------------------------------------------------------------- -// Reverb Estimator -// -------------------------------------------------------------------------------------------------------------------- - -void IPLCALL iplReverbEstimatorEstimate(IPLReverbEstimatorParams* inputs, IPLReverbEstimatorOutputs* outputs) -{ - if (!inputs || !outputs) - return; - - auto _energyField = derefHandle(inputs->energyField); - if (!_energyField) - return; - - AirAbsorptionModel _airAbsorptionModel{}; - switch (inputs->airAbsorptionModel.type) - { - case IPL_AIRABSORPTIONTYPE_DEFAULT: - _airAbsorptionModel = AirAbsorptionModel{}; - break; - case IPL_AIRABSORPTIONTYPE_EXPONENTIAL: - _airAbsorptionModel = AirAbsorptionModel(inputs->airAbsorptionModel.coefficients, nullptr, nullptr); - break; - case IPL_AIRABSORPTIONTYPE_CALLBACK: - _airAbsorptionModel = AirAbsorptionModel(nullptr, inputs->airAbsorptionModel.callback, inputs->airAbsorptionModel.userData); - break; - } - - Reverb _reverb; - ReverbEstimator::estimate(*_energyField, _airAbsorptionModel, _reverb); - - outputs->reverbTimes[0] = _reverb.reverbTimes[0]; - outputs->reverbTimes[1] = _reverb.reverbTimes[1]; - outputs->reverbTimes[2] = _reverb.reverbTimes[2]; -} - - -// -------------------------------------------------------------------------------------------------------------------- -// Hybrid Reverb Estimator -// -------------------------------------------------------------------------------------------------------------------- - -DEFINE_OPAQUE_HANDLE(IPLHybridReverbEstimator, HybridReverbEstimator); - -IPLerror IPLCALL iplHybridReverbEstimatorCreate(IPLContext context, IPLHybridReverbEstimatorSettings* settings, IPLHybridReverbEstimator* estimator) -{ - if (!context || !settings || !estimator) - return IPL_STATUS_FAILURE; - - auto _context = reinterpret_cast(context)->mHandle.get(); - if (!_context) - return IPL_STATUS_FAILURE; - - try - { - *estimator = createHandle(_context, ipl::make_shared(settings->maxDuration, settings->samplingRate, - settings->frameSize)); - } - catch (Exception e) - { - return static_cast(e.status()); - } - - return IPL_STATUS_SUCCESS; -} - -IPLHybridReverbEstimator IPLCALL iplHybridReverbEstimatorRetain(IPLHybridReverbEstimator estimator) -{ - return retainHandle(estimator); -} - -void IPLCALL iplHybridReverbEstimatorRelease(IPLHybridReverbEstimator* estimator) -{ - releaseHandle(*estimator); -} - -void IPLCALL iplHybridReverbEstimatorEstimate(IPLHybridReverbEstimator estimator, IPLHybridReverbEstimatorParams* inputs, IPLHybridReverbEstimatorOutputs* outputs) -{ - if (!estimator || !inputs || !outputs) - return; - - auto _estimator = derefHandle(estimator); - auto _energyField = derefHandle(inputs->energyField); - auto _impulseResponse = derefHandle(inputs->impulseResponse); - if (!_estimator || !_energyField || _impulseResponse) - return; - - Reverb _reverb; - _reverb.reverbTimes[0] = inputs->reverbTimes[0]; - _reverb.reverbTimes[1] = inputs->reverbTimes[1]; - _reverb.reverbTimes[2] = inputs->reverbTimes[2]; - - _estimator->estimate(*_energyField, _reverb, *_impulseResponse, inputs->transitionTime, inputs->overlapFraction, - inputs->order, outputs->eq); -} - - -// -------------------------------------------------------------------------------------------------------------------- -// Convolution Partitioner -// -------------------------------------------------------------------------------------------------------------------- - -DEFINE_OPAQUE_HANDLE(IPLConvolutionPartitioner, OverlapSavePartitioner); - -IPLerror IPLCALL iplConvolutionPartitionerCreate(IPLContext context, IPLConvolutionPartitionerSettings* settings, IPLConvolutionPartitioner* partitioner) -{ - if (!context || !settings || !partitioner) - return IPL_STATUS_FAILURE; - - auto _context = reinterpret_cast(context)->mHandle.get(); - if (!_context) - return IPL_STATUS_FAILURE; - - try - { - *partitioner = createHandle(_context, ipl::make_shared(settings->frameSize)); - } - catch (Exception e) - { - return static_cast(e.status()); - } - - return IPL_STATUS_SUCCESS; -} - -IPLConvolutionPartitioner IPLCALL iplConvolutionPartitionerRetain(IPLConvolutionPartitioner partitioner) -{ - return retainHandle(partitioner); -} - -void IPLCALL iplConvolutionPartitionerRelease(IPLConvolutionPartitioner* partitioner) -{ - releaseHandle(*partitioner); -} - -void IPLCALL iplConvolutionPartitionerPartition(IPLConvolutionPartitioner partitioner, IPLConvolutionPartitionerParams* inputs, IPLConvolutionPartitionerOutputs* outputs) -{ - if (!partitioner || !inputs || !outputs) - return; - - auto _partitioner = derefHandle(partitioner); - auto _impulseResponse = derefHandle(inputs->impulseResponse); - auto _ir = derefHandle>(outputs->ir); - if (!_partitioner || !_impulseResponse || !_ir) - return; - - auto numChannels = SphericalHarmonics::numCoeffsForOrder(inputs->order); - auto numSamples = static_cast(ceilf(inputs->duration * inputs->samplingRate)); - - _partitioner->partition(*_impulseResponse, numChannels, numSamples, *_ir->writeBuffer); - _ir->commitWriteBuffer(); -} - - -// -------------------------------------------------------------------------------------------------------------------- -// TrueAudio Next -// -------------------------------------------------------------------------------------------------------------------- - -IPLint32 IPLCALL iplTrueAudioNextDeviceAcquireSlot(IPLTrueAudioNextDevice tanDevice) -{ - if (!tanDevice) - return -1; - - auto _tanDevice = reinterpret_cast(tanDevice)->mHandle.get(); - if (!_tanDevice) - return -1; - -#if defined(IPL_USES_TRUEAUDIONEXT) - return _tanDevice->acquireSlot(); -#else - return -1; -#endif -} - -void IPLCALL iplTrueAudioNextReleaseSlot(IPLTrueAudioNextDevice tanDevice, IPLint32 slot) -{ - if (!tanDevice) - return; - - auto _tanDevice = reinterpret_cast(tanDevice)->mHandle.get(); - if (!_tanDevice) - return; - -#if defined(IPL_USES_TRUEAUDIONEXT) - _tanDevice->releaseSlot(slot); -#endif -} - -void IPLCALL iplTrueAudioNextSetImpulseResponse(IPLTrueAudioNextDevice tanDevice, IPLint32 slot, IPLImpulseResponse impulseResponse) -{ - if (!tanDevice || slot < 0 || !impulseResponse) - return; - - auto _tanDevice = reinterpret_cast(tanDevice)->mHandle.get(); - auto _impulseResponse = derefHandle(impulseResponse); - if (!_tanDevice || !_impulseResponse) - return; - -#if defined(IPL_USES_TRUEAUDIONEXT) - _tanDevice->setIR(slot, static_cast(_impulseResponse.get())->channelBuffers()); -#endif -} - -void IPLCALL iplTrueAudioNextUpdateIRs(IPLTrueAudioNextDevice tanDevice) -{ - if (!tanDevice) - return; - - auto _tanDevice = reinterpret_cast(tanDevice)->mHandle.get(); - if (!_tanDevice) - return; - -#if defined(IPL_USES_TRUEAUDIONEXT) - _tanDevice->updateIRs(); -#endif -} - - -// -------------------------------------------------------------------------------------------------------------------- -// Probes -// -------------------------------------------------------------------------------------------------------------------- - -DEFINE_OPAQUE_HANDLE(IPLProbeNeighborhood, ProbeNeighborhood); - -namespace api { - -class CProbeNeighborhood -{ -public: - Handle mHandle; - - CProbeNeighborhood(CContext* context) - { - auto _context = context->mHandle.get(); - if (!_context) - throw Exception(Status::Failure); - - new (&mHandle) Handle(ipl::make_shared(), _context); - } - - CProbeNeighborhood* retain() - { - mHandle.retain(); - return this; - } - - void release() - { - if (mHandle.release()) - { - this->~CProbeNeighborhood(); - gMemory().free(this); - } - } - - void resize(IPLint32 maxProbes) - { - auto _probeNeighborhood = mHandle.get(); - if (!_probeNeighborhood) - return; - - _probeNeighborhood->resize(maxProbes); - } - - void reset() - { - auto _probeNeighborhood = mHandle.get(); - if (!_probeNeighborhood) - return; - - _probeNeighborhood->reset(); - } - - IPLint32 numProbes() - { - auto _probeNeighborhood = mHandle.get(); - if (!_probeNeighborhood) - return 0; - - return _probeNeighborhood->numProbes(); - } - - IPLint32 numValidProbes() - { - auto _probeNeighborhood = mHandle.get(); - if (!_probeNeighborhood) - return 0; - - return _probeNeighborhood->numValidProbes(); - } - - IPLint32 findNearest(IPLVector3 position) - { - auto _probeNeighborhood = mHandle.get(); - if (!_probeNeighborhood) - return 0; - - const auto& _position = *reinterpret_cast(&position); - return _probeNeighborhood->findNearest(_position); - } - - void checkOcclusion(IScene* scene, IPLVector3 position) - { - auto _probeNeighborhood = mHandle.get(); - auto _scene = reinterpret_cast(scene)->mHandle.get(); - - if (!_probeNeighborhood || !_scene) - return; - - const auto& _position = *reinterpret_cast(&position); - _probeNeighborhood->checkOcclusion(*_scene, _position); - } - - void calcWeights(IPLVector3 position) - { - auto _probeNeighborhood = mHandle.get(); - if (!_probeNeighborhood) - return; - - const auto& _position = *reinterpret_cast(&position); - _probeNeighborhood->calcWeights(_position); - } -}; - -} - -IPLerror IPLCALL iplProbeNeighborhoodCreate(IPLContext context, IPLProbeNeighborhood* probeNeighborhood) -{ - if (!context) - return IPL_STATUS_FAILURE; - - if (!probeNeighborhood) - return IPL_STATUS_FAILURE; - - try - { - auto _probeNeighborhood = reinterpret_cast(gMemory().allocate(sizeof(api::CProbeNeighborhood), Memory::kDefaultAlignment)); - new (_probeNeighborhood) api::CProbeNeighborhood(reinterpret_cast(context)); - *probeNeighborhood = reinterpret_cast(_probeNeighborhood); - } - catch (Exception e) - { - return static_cast(e.status()); - } - - return IPL_STATUS_SUCCESS; -} - -void IPLCALL iplProbeNeighborhoodRelease(IPLProbeNeighborhood* probeNeighborhood) -{ - if (!probeNeighborhood || !*probeNeighborhood) - return; - - reinterpret_cast(*probeNeighborhood)->release(); - - *probeNeighborhood = nullptr; -} - -IPLProbeNeighborhood IPLCALL iplProbeNeighborhoodRetain(IPLProbeNeighborhood probeNeighborhood) -{ - if (!probeNeighborhood) - return nullptr; - - return reinterpret_cast(reinterpret_cast(probeNeighborhood)->retain()); -} - -void IPLCALL iplProbeNeighborhoodResize(IPLProbeNeighborhood probeNeighborhood, IPLint32 maxProbes) -{ - if (!probeNeighborhood) - return; - - reinterpret_cast(probeNeighborhood)->resize(maxProbes); -} - -void IPLCALL iplProbeNeighborhoodReset(IPLProbeNeighborhood probeNeighborhood) -{ - if (!probeNeighborhood) - return; - - reinterpret_cast(probeNeighborhood)->reset(); -} - -IPLint32 IPLCALL iplProbeNeighborhoodGetNumProbes(IPLProbeNeighborhood probeNeighborhood) -{ - if (!probeNeighborhood) - return 0; - - return reinterpret_cast(probeNeighborhood)->numProbes(); -} - -IPLint32 IPLCALL iplProbeNeighborhoodGetNumValidProbes(IPLProbeNeighborhood probeNeighborhood) -{ - if (!probeNeighborhood) - return 0; - - return reinterpret_cast(probeNeighborhood)->numValidProbes(); -} - -IPLint32 IPLCALL iplProbeNeighborhoodFindNearest(IPLProbeNeighborhood probeNeighborhood, IPLVector3 point) -{ - if (!probeNeighborhood) - return -1; - - return reinterpret_cast(probeNeighborhood)->findNearest(point); -} - -void IPLCALL iplProbeNeighborhoodCheckOcclusion(IPLProbeNeighborhood probeNeighborhood, IPLScene scene, IPLVector3 point) -{ - if (!probeNeighborhood || !scene) - return; - - api::CProbeNeighborhood* _probeNeighborhood = reinterpret_cast(probeNeighborhood); - api::IScene* _scene = reinterpret_cast(scene); - - _probeNeighborhood->checkOcclusion(_scene, point); -} - -void IPLCALL iplProbeNeighborhoodCalculateWeights(IPLProbeNeighborhood probeNeighborhood, IPLVector3 point) -{ - if (!probeNeighborhood) - return; - - api::CProbeNeighborhood* _probeNeighborhood = reinterpret_cast(probeNeighborhood); - - _probeNeighborhood->calcWeights(point); -} - -void IPLCALL iplProbeBatchUpdateProbeRadius(IPLProbeBatch probeBatch, IPLint32 index, IPLfloat32 radius) -{ - if (!probeBatch || index < 0 || radius < 0.0f) - return; - - auto _probeBatch = reinterpret_cast(probeBatch)->mHandle.get(); - if (!_probeBatch) - return; - - if (index < 0 || _probeBatch->numProbes() <= index) - return; - - _probeBatch->updateProbeRadius(index, radius); -} - -void IPLCALL iplProbeBatchUpdateProbePosition(IPLProbeBatch probeBatch, IPLint32 index, IPLVector3 position) -{ - if (!probeBatch || index < 0) - return; - - auto _probeBatch = reinterpret_cast(probeBatch)->mHandle.get(); - if (!_probeBatch) - return; - - if (index < 0 || _probeBatch->numProbes() <= index) - return; - - const auto& _position = *reinterpret_cast(&position); - - _probeBatch->updateProbePosition(index, _position); -} - -void IPLCALL iplProbeBatchUpdateEndpoint(IPLProbeBatch probeBatch, IPLBakedDataIdentifier identifier, IPLSphere endpointInfluence) -{ - if (!probeBatch) - return; - - auto _probeBatch = reinterpret_cast(probeBatch)->mHandle.get(); - if (!_probeBatch) - return; - - const auto& _identifier = *reinterpret_cast(&identifier); - const auto& _endpointInfluence = *reinterpret_cast(&endpointInfluence); - - _probeBatch->updateEndpoint(_identifier, _endpointInfluence); -} - -void IPLCALL iplProbeBatchGetInfluencingProbes(IPLProbeBatch probeBatch, IPLVector3 point, IPLProbeNeighborhood probeNeighborhood) -{ - if (!probeBatch || !probeNeighborhood) - return; - - auto _probeBatch = reinterpret_cast(probeBatch)->mHandle.get(); - auto _probeNeighborhood = derefHandle(probeNeighborhood); - if (!_probeBatch || !_probeNeighborhood) - return; - - const auto& _position = *reinterpret_cast(&point); - - _probeBatch->getInfluencingProbes(_position, *_probeNeighborhood); -} - -void IPLCALL iplProbeBatchGetProbeArray(IPLProbeBatch probeBatch, IPLProbeArray probeArray) -{ - if (!probeBatch || !probeArray) - return; - - auto _probeBatch = reinterpret_cast(probeBatch)->mHandle.get(); - auto _probeArray = reinterpret_cast(probeArray)->mHandle.get(); - if (!_probeBatch || !_probeArray) - return; - - _probeBatch->toProbeArray(*_probeArray); -} - -void IPLCALL iplSimulatorRunPathingPerSource(IPLSimulator simulator, IPLSource source) -{ - if (!simulator || !source) - return; - - auto _simulator = reinterpret_cast(simulator)->mHandle.get(); - auto _source = reinterpret_cast(source)->mHandle.get(); - if (!_simulator || !_source) - return; - - _simulator->simulatePathing(*_source); -} - -void IPLCALL iplSimulatorRunPathingPerSourceForNeighborhood(IPLSimulator simulator, IPLSource source, IPLProbeNeighborhood listenerProbeNeighborhood) -{ - if (!simulator || !source || !listenerProbeNeighborhood) - return; - - auto _simulator = reinterpret_cast(simulator)->mHandle.get(); - auto _source = reinterpret_cast(source)->mHandle.get(); - auto _listenerProbeNeighborhood = derefHandle(listenerProbeNeighborhood); - if (!_simulator || !_source || !_listenerProbeNeighborhood) - return; - - _simulator->simulatePathing(*_source, *_listenerProbeNeighborhood); -} - -void IPLCALL iplSourceGetOutputsAux(IPLSource source, IPLSimulationFlags flags, IPLSimulationOutputsAux* outputs) -{ - if (!source || !outputs) - return; - - auto _source = reinterpret_cast(source)->mHandle.get(); - if (!_source) - return; - - if (flags & IPL_SIMULATIONFLAGS_PATHING) - { - outputs->pathingAvgDirection = *reinterpret_cast(&_source->pathingOutputs.direction); - outputs->pathingDistanceRatio = _source->pathingOutputs.distanceRatio; - } -} - - -// -------------------------------------------------------------------------------------------------------------------- -// Path Simulator -// -------------------------------------------------------------------------------------------------------------------- - -DEFINE_OPAQUE_HANDLE(IPLPathSimulator, PathSimulator); - -IPLerror IPLCALL iplPathSimulatorCreate(IPLContext context, IPLPathSimulatorSettings* settings, IPLPathSimulator* simulator) -{ - if (!context || !settings || !simulator) - return IPL_STATUS_FAILURE; - - auto _probeBatch = reinterpret_cast(settings->probeBatch)->mHandle.get(); - auto _asymmetricVisRange = (settings->asymmetricVisRange == IPL_TRUE); - auto _down = *reinterpret_cast(&settings->down); - if (!_probeBatch) - return IPL_STATUS_FAILURE; - - auto _context = reinterpret_cast(context)->mHandle.get(); - if (!_context) - return IPL_STATUS_FAILURE; - - try - { - *simulator = createHandle(_context, ipl::make_shared(*_probeBatch, settings->numSamples, - _asymmetricVisRange, _down)); - } - catch (Exception e) - { - return static_cast(e.status()); - } - - return IPL_STATUS_SUCCESS; -} - -IPLPathSimulator IPLCALL iplPathSimulatorRetain(IPLPathSimulator simulator) -{ - return retainHandle(simulator); -} - -void IPLCALL iplPathSimulatorRelease(IPLPathSimulator* simulator) -{ - releaseHandle(*simulator); -} - -void IPLCALL iplPathSimulatorSimulate(IPLPathSimulator simulator, IPLScene scene, IPLPathSimulatorParams* inputs, IPLPathEffectParams* outputs) -{ - if (!simulator || !scene || !inputs || !outputs) - return; - - auto _simulator = derefHandle(simulator); - auto _scene = reinterpret_cast(scene)->mHandle.get(); - auto _probeBatch = reinterpret_cast(inputs->probeBatch)->mHandle.get(); - auto _sourceProbes = derefHandle(inputs->sourceProbes); - auto _listenerProbes = derefHandle(inputs->listenerProbes); - if (!_simulator || !_scene || !_probeBatch || !_sourceProbes || !_listenerProbes) - return; - - auto _source = *reinterpret_cast(&inputs->source); - auto _listener = *reinterpret_cast(&inputs->listener); - auto _enableValidation = (inputs->enableValidation == IPL_TRUE); - auto _findAlternatePaths = (inputs->findAlternatePaths == IPL_TRUE); - auto _simplifyPaths = (inputs->simplifyPaths == IPL_TRUE); - auto _realTimeVis = (inputs->realTimeVis == IPL_TRUE); - - _simulator->findPaths(_source, _listener, *_scene, *_probeBatch, *_sourceProbes, *_listenerProbes, inputs->radius, - inputs->threshold, inputs->visRange, inputs->order, _enableValidation, _findAlternatePaths, - _simplifyPaths, _realTimeVis, outputs->eqCoeffs, outputs->shCoeffs); -} diff --git a/core/src/core/embree_scene.cpp b/core/src/core/embree_scene.cpp index f5849750..9409a8b9 100644 --- a/core/src/core/embree_scene.cpp +++ b/core/src/core/embree_scene.cpp @@ -298,12 +298,30 @@ void EmbreeScene::anyHits(int numRays, void EmbreeScene::dumpObj(const string& fileName) const { - auto file = fopen(fileName.c_str(), "w"); - if (!file) + auto utf8fopen = [](const string& fileName) { - gLog().message(MessageSeverity::Error, "Unable to open file %s for OBJ dump.", fileName.c_str()); - return; - } +#if defined(IPL_OS_WINDOWS) + std::string utf8{ fileName.c_str() }; + std::wstring_convert> converter; + std::wstring utf16{ converter.from_bytes(utf8) }; + return _wfopen(utf16.c_str(), L"w"); +#else + return fopen(fileName.c_str(), "w"); +#endif + }; + + auto separatorPos = fileName.find_last_of("/\\"); + auto extensionPos = fileName.find_last_of("."); + auto baseName = string{ fileName, separatorPos + 1, extensionPos - separatorPos - 1 }; + auto path = string{ fileName, 0, separatorPos + 1 }; + + auto mtlFileName = path + baseName + ".mtl"; + auto mtlFile = utf8fopen(mtlFileName); + fprintf(mtlFile, "# Generated by Steam Audio\n"); + + auto objFile = utf8fopen(fileName); + fprintf(objFile, "# Generated by Steam Audio\n"); + fprintf(objFile, "mtllib %s.mtl\n", baseName.c_str()); vector scenes; vector staticMeshes; @@ -325,32 +343,78 @@ void EmbreeScene::dumpObj(const string& fileName) const } auto vertexOffset = 1; + auto materialOffset = 0; for (auto i = 0u; i < scenes.size(); ++i) { auto vertices = (float*) rtcMapBuffer(scenes[i], staticMeshes[i]->geometryIndex(), RTC_VERTEX_BUFFER); auto indices = (int32_t*) rtcMapBuffer(scenes[i], staticMeshes[i]->geometryIndex(), RTC_INDEX_BUFFER); + // The OBJ file format does not use absorption and scattering coefficients; instead it uses diffuse + // reflectivity (Kd) and specular reflectivity (Ks). They are defined by: + // + // Kd = (1 - absorption) * scattering + // Ks = (1 - absorption) * (1 - scattering) + // + // To recover these values from the .mtl file, use the following equations: + // + // scattering = Kd / (Kd + Ks) + // absorption = 1 - (Kd + Ks) + // + // The above equations hold for each band independently. Scattering coefficients will be equal for each + // band. Transmission coefficients are stored as-is in the transmission filter (Tf) component of the + // material. + for (auto k = 0; k < staticMeshes[i]->numMaterials(); ++k) + { + const auto materials = staticMeshes[i]->materials(); + + float diffuseReflectivity[3] = {}; + float specularReflectivity[3] = {}; + float transmission[3] = {}; + + for (auto j = 0; j < 3; ++j) + { + diffuseReflectivity[j] = (1.0f - materials[k].absorption[j]) * materials[k].scattering; + specularReflectivity[j] = (1.0f - materials[k].absorption[j]) * (1.0f - materials[k].scattering); + transmission[j] = materials[k].transmission[j]; + } + + fprintf(mtlFile, "newmtl material_%d\n", materialOffset + k); + fprintf(mtlFile, "Kd %f %f %f\n", diffuseReflectivity[0], diffuseReflectivity[1], diffuseReflectivity[2]); + fprintf(mtlFile, "Ks %f %f %f\n", specularReflectivity[0], specularReflectivity[1], specularReflectivity[2]); + fprintf(mtlFile, "Tf %f %f %f\n\n", transmission[0], transmission[1], transmission[2]); + } + for (auto j = 0; j < staticMeshes[i]->numVertices(); ++j) { auto vertex = Vector4f(vertices[4 * j + 0], vertices[4 * j + 1], vertices[4 * j + 2], 1.0f); auto transformedVertex = transforms[i].transposedCopy() * vertex; - fprintf(file, "v %f %f %f\n", transformedVertex.elements[0], transformedVertex.elements[1], + fprintf(objFile, "v %f %f %f\n", transformedVertex.elements[0], transformedVertex.elements[1], transformedVertex.elements[2]); } + auto previousMaterialIndex = -1; for (auto j = 0; j < staticMeshes[i]->numTriangles(); ++j) { - fprintf(file, "f %d %d %d\n", vertexOffset + indices[3 * j + 0], vertexOffset + indices[3 * j + 1], + auto materialIndex = staticMeshes[i]->materialIndices()[j]; + if (materialIndex != previousMaterialIndex) + { + fprintf(objFile, "usemtl material_%d\n", materialOffset + materialIndex); + previousMaterialIndex = materialIndex; + } + + fprintf(objFile, "f %d %d %d\n", vertexOffset + indices[3 * j + 0], vertexOffset + indices[3 * j + 1], vertexOffset + indices[3 * j + 2]); } vertexOffset += staticMeshes[i]->numVertices(); + materialOffset += staticMeshes[i]->numMaterials(); rtcUnmapBuffer(scenes[i], 0, RTC_INDEX_BUFFER); rtcUnmapBuffer(scenes[i], 0, RTC_VERTEX_BUFFER); } - fclose(file); + fclose(mtlFile); + fclose(objFile); } } diff --git a/core/src/core/instanced_mesh.h b/core/src/core/instanced_mesh.h index 2351823d..25442445 100644 --- a/core/src/core/instanced_mesh.h +++ b/core/src/core/instanced_mesh.h @@ -69,6 +69,16 @@ class InstancedMesh : public IInstancedMesh return mNumTriangles; } + const Scene& subScene() const + { + return *mSubScene; + } + + const Matrix4x4f& transform() const + { + return mTransform; + } + virtual void updateTransform(const IScene& scene, const Matrix4x4f& transform) override; diff --git a/core/src/core/scene.cpp b/core/src/core/scene.cpp index 247c3173..465195eb 100644 --- a/core/src/core/scene.cpp +++ b/core/src/core/scene.cpp @@ -295,13 +295,28 @@ void Scene::dumpObj(const string& fileName) const fprintf(objFile, "# Generated by Steam Audio\n"); fprintf(objFile, "mtllib %s.mtl\n", baseName.c_str()); - auto numVerticesDumped = 0; - auto numTrianglesDumped = 0; - auto numMaterialsDumped = 0; + vector staticMeshes; + vector transforms; for (const auto& staticMesh : mStaticMeshes[0]) { - const auto& _staticMesh = *static_cast(staticMesh.get()); + staticMeshes.push_back((StaticMesh*)staticMesh.get()); + transforms.push_back(Matrix4x4f::identityMatrix()); + } + + for (const auto& instancedMesh : mInstancedMeshes[0]) + { + auto phononInstancedMesh = (InstancedMesh*) instancedMesh.get(); + staticMeshes.push_back((StaticMesh*)phononInstancedMesh->subScene().mStaticMeshes[0].front().get()); + transforms.push_back(phononInstancedMesh->transform()); + } + + auto vertexOffset = 1; + auto materialOffset = 0; + + for (auto i = 0u; i < staticMeshes.size(); ++i) + { + const auto & _staticMesh = *staticMeshes[i]; // The OBJ file format does not use absorption and scattering coefficients; instead it uses diffuse // reflectivity (Kd) and specular reflectivity (Ks). They are defined by: @@ -317,7 +332,7 @@ void Scene::dumpObj(const string& fileName) const // The above equations hold for each band independently. Scattering coefficients will be equal for each // band. Transmission coefficients are stored as-is in the transmission filter (Tf) component of the // material. - for (auto i = 0; i < _staticMesh.numMaterials(); ++i) + for (auto k = 0; k < _staticMesh.numMaterials(); ++k) { const auto materials = _staticMesh.materials(); @@ -327,47 +342,41 @@ void Scene::dumpObj(const string& fileName) const for (auto j = 0; j < 3; ++j) { - diffuseReflectivity[j] = (1.0f - materials[i].absorption[j]) * materials[i].scattering; - specularReflectivity[j] = (1.0f - materials[i].absorption[j]) * (1.0f - materials[i].scattering); - transmission[j] = materials[i].transmission[j]; + diffuseReflectivity[j] = (1.0f - materials[k].absorption[j]) * materials[k].scattering; + specularReflectivity[j] = (1.0f - materials[k].absorption[j]) * (1.0f - materials[k].scattering); + transmission[j] = materials[k].transmission[j]; } - fprintf(mtlFile, "newmtl material_%d\n", numMaterialsDumped + i); + fprintf(mtlFile, "newmtl material_%d\n", materialOffset + k); fprintf(mtlFile, "Kd %f %f %f\n", diffuseReflectivity[0], diffuseReflectivity[1], diffuseReflectivity[2]); fprintf(mtlFile, "Ks %f %f %f\n", specularReflectivity[0], specularReflectivity[1], specularReflectivity[2]); fprintf(mtlFile, "Tf %f %f %f\n\n", transmission[0], transmission[1], transmission[2]); } - for (auto i = 0; i < _staticMesh.numVertices(); ++i) + for (auto k = 0; k < _staticMesh.numVertices(); ++k) { - const auto& vertex = _staticMesh.mesh().vertex(i); - fprintf(objFile, "v %f %f %f\n", vertex.x(), vertex.y(), vertex.z()); + const auto& vertex = _staticMesh.mesh().vertex(k); + auto transformedVertex = transforms[i].transposedCopy() * Vector4f(vertex.x(), vertex.y(), vertex.z(), 1.0f); + fprintf(objFile, "v %f %f %f\n", transformedVertex.elements[0], transformedVertex.elements[1], transformedVertex.elements[2]); } auto previousMaterialIndex = -1; - for (auto i = 0; i < _staticMesh.numTriangles(); ++i) + for (auto k = 0; k < _staticMesh.numTriangles(); ++k) { - auto materialIndex = _staticMesh.materialIndices()[i]; + auto materialIndex = _staticMesh.materialIndices()[k]; if (materialIndex != previousMaterialIndex) { - fprintf(objFile, "usemtl material_%d\n", numMaterialsDumped + materialIndex); + fprintf(objFile, "usemtl material_%d\n", materialOffset + materialIndex); previousMaterialIndex = materialIndex; } - const auto& triangle = _staticMesh.mesh().triangle(i); - - fprintf(objFile, "f "); - for (auto j = 0; j < 3; ++j) - { - const auto index = triangle.indices[j]; - fprintf(objFile, "%d ", numVerticesDumped + index + 1); - } - fprintf(objFile, "\n"); + const auto& triangle = _staticMesh.mesh().triangle(k); + fprintf(objFile, "f %d %d %d\n", vertexOffset + triangle.indices[0], vertexOffset + triangle.indices[1], + vertexOffset + triangle.indices[2]); } - numVerticesDumped += _staticMesh.numVertices(); - numTrianglesDumped += _staticMesh.numTriangles(); - numMaterialsDumped += _staticMesh.numMaterials(); + vertexOffset += _staticMesh.numVertices(); + materialOffset += _staticMesh.numMaterials(); } fclose(mtlFile); diff --git a/fmod/CMakeLists.txt b/fmod/CMakeLists.txt index e067ee38..9d5d5ea6 100644 --- a/fmod/CMakeLists.txt +++ b/fmod/CMakeLists.txt @@ -14,7 +14,7 @@ cmake_minimum_required(VERSION 3.17) -project(Phonon VERSION 4.5.2) +project(Phonon VERSION 4.5.3) set(CMAKE_CXX_STANDARD 14) set(CMAKE_MODULE_PATH "${CMAKE_HOME_DIRECTORY}/build") diff --git a/fmod/include/phonon/phonon_version.h b/fmod/include/phonon/phonon_version.h index 380399f3..3d2a0f66 100644 --- a/fmod/include/phonon/phonon_version.h +++ b/fmod/include/phonon/phonon_version.h @@ -19,7 +19,7 @@ #define STEAMAUDIO_VERSION_MAJOR 4 #define STEAMAUDIO_VERSION_MINOR 5 -#define STEAMAUDIO_VERSION_PATCH 2 +#define STEAMAUDIO_VERSION_PATCH 3 #define STEAMAUDIO_VERSION (((IPLuint32)(STEAMAUDIO_VERSION_MAJOR) << 16) | \ ((IPLuint32)(STEAMAUDIO_VERSION_MINOR) << 8) | \ ((IPLuint32)(STEAMAUDIO_VERSION_PATCH))) diff --git a/fmod/setup.py b/fmod/setup.py index c932a1bb..d943c99b 100644 --- a/fmod/setup.py +++ b/fmod/setup.py @@ -19,7 +19,7 @@ import urllib.request, urllib.error, urllib.parse import zipfile -version = "4.5.2" +version = "4.5.3" def download_file(url): remote_file = urllib.request.urlopen(url) diff --git a/unity/CMakeLists.txt b/unity/CMakeLists.txt index 6bf4124f..3b1fabc4 100644 --- a/unity/CMakeLists.txt +++ b/unity/CMakeLists.txt @@ -14,7 +14,7 @@ cmake_minimum_required(VERSION 3.17) -project(Phonon VERSION 4.5.2) +project(Phonon VERSION 4.5.3) set(CMAKE_CXX_STANDARD 14) set(CMAKE_MODULE_PATH "${CMAKE_HOME_DIRECTORY}/build") diff --git a/unity/include/phonon/phonon_version.h b/unity/include/phonon/phonon_version.h index 380399f3..3d2a0f66 100644 --- a/unity/include/phonon/phonon_version.h +++ b/unity/include/phonon/phonon_version.h @@ -19,7 +19,7 @@ #define STEAMAUDIO_VERSION_MAJOR 4 #define STEAMAUDIO_VERSION_MINOR 5 -#define STEAMAUDIO_VERSION_PATCH 2 +#define STEAMAUDIO_VERSION_PATCH 3 #define STEAMAUDIO_VERSION (((IPLuint32)(STEAMAUDIO_VERSION_MAJOR) << 16) | \ ((IPLuint32)(STEAMAUDIO_VERSION_MINOR) << 8) | \ ((IPLuint32)(STEAMAUDIO_VERSION_PATCH))) diff --git a/unity/setup.py b/unity/setup.py index 5dd04430..c8c19b11 100644 --- a/unity/setup.py +++ b/unity/setup.py @@ -19,7 +19,7 @@ import urllib.request, urllib.error, urllib.parse import zipfile -version = "4.5.2" +version = "4.5.3" def download_file(url): remote_file = urllib.request.urlopen(url) diff --git a/unreal/CMakeLists.txt b/unreal/CMakeLists.txt index 9137a8e7..71f9f573 100644 --- a/unreal/CMakeLists.txt +++ b/unreal/CMakeLists.txt @@ -14,7 +14,7 @@ cmake_minimum_required(VERSION 3.17) -project(Phonon VERSION 4.5.2) +project(Phonon VERSION 4.5.3) set(CMAKE_CXX_STANDARD 14) set(CMAKE_MODULE_PATH "${CMAKE_HOME_DIRECTORY}/build") diff --git a/unreal/setup.py b/unreal/setup.py index d2914b3a..04a82b07 100644 --- a/unreal/setup.py +++ b/unreal/setup.py @@ -19,7 +19,7 @@ import urllib.request, urllib.error, urllib.parse import zipfile -version = "4.5.2" +version = "4.5.3" def download_file(url): remote_file = urllib.request.urlopen(url) diff --git a/unreal/src/SteamAudioUnreal/Plugins/SteamAudio/Source/SteamAudioSDK/include/phonon_version.h b/unreal/src/SteamAudioUnreal/Plugins/SteamAudio/Source/SteamAudioSDK/include/phonon_version.h index 380399f3..3d2a0f66 100644 --- a/unreal/src/SteamAudioUnreal/Plugins/SteamAudio/Source/SteamAudioSDK/include/phonon_version.h +++ b/unreal/src/SteamAudioUnreal/Plugins/SteamAudio/Source/SteamAudioSDK/include/phonon_version.h @@ -19,7 +19,7 @@ #define STEAMAUDIO_VERSION_MAJOR 4 #define STEAMAUDIO_VERSION_MINOR 5 -#define STEAMAUDIO_VERSION_PATCH 2 +#define STEAMAUDIO_VERSION_PATCH 3 #define STEAMAUDIO_VERSION (((IPLuint32)(STEAMAUDIO_VERSION_MAJOR) << 16) | \ ((IPLuint32)(STEAMAUDIO_VERSION_MINOR) << 8) | \ ((IPLuint32)(STEAMAUDIO_VERSION_PATCH))) diff --git a/unreal/src/SteamAudioUnreal/Plugins/SteamAudio/SteamAudio.uplugin b/unreal/src/SteamAudioUnreal/Plugins/SteamAudio/SteamAudio.uplugin index bb8beebe..f48b5c0b 100644 --- a/unreal/src/SteamAudioUnreal/Plugins/SteamAudio/SteamAudio.uplugin +++ b/unreal/src/SteamAudioUnreal/Plugins/SteamAudio/SteamAudio.uplugin @@ -1,7 +1,7 @@ { "FileVersion" : 3, "Version" : 1, - "VersionName" : "4.5.2", + "VersionName" : "4.5.3", "FriendlyName" : "Steam Audio", "Description" : "Physically-based sound rendering.", "Category" : "Audio", diff --git a/unreal/src/SteamAudioUnreal/Plugins/SteamAudioFMODStudio/SteamAudioFMODStudio.uplugin b/unreal/src/SteamAudioUnreal/Plugins/SteamAudioFMODStudio/SteamAudioFMODStudio.uplugin index fd87ec79..3061cd97 100644 --- a/unreal/src/SteamAudioUnreal/Plugins/SteamAudioFMODStudio/SteamAudioFMODStudio.uplugin +++ b/unreal/src/SteamAudioUnreal/Plugins/SteamAudioFMODStudio/SteamAudioFMODStudio.uplugin @@ -1,7 +1,7 @@ { "FileVersion": 3, "Version": 1, - "VersionName": "4.5.2", + "VersionName": "4.5.3", "FriendlyName": "Steam Audio FMOD Studio Support", "Description": "Integrates the Steam Audio plugin for Unreal and the Steam Audio plugin for FMOD Studio.", "Category": "Audio",