mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-23 09:03:22 -05:00
Implement sceNgs2GeomApply, which was previously an unimplemented throwing stub and was blocking Dead Cells (PPSA15552). The implementation validates null handles/output addresses and writes a minimal Ngs2GeomAttribute: pitch_ratio 1.0, source position, source volume/level maxima, and diagonal channel levels for the source's channel count. It does not implement source attenuation, distance rolloff, panning or Doppler. The export lives in Geom.cpp's internal Ngs2GeomApply and is exposed through the existing default-visibility wrapper in Voice.cpp. Unimplemented.cpp no longer defines the stub. A passing test was added to Ngs2Geom.cpp. Unblocks: PPSA15552 #<Game bring-up discussion number> Depends on: none AI assistance: yes
107 lines
4.5 KiB
C++
107 lines
4.5 KiB
C++
#include <algorithm>
|
|
#include <array>
|
|
#include <cmath>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <limits>
|
|
|
|
#include "prx/libc/include/General.hpp"
|
|
#include "Ngs2Internal.hpp"
|
|
|
|
static constexpr float DEFAULT_SOUND_SPEED = 343.0f;
|
|
|
|
namespace {
|
|
|
|
using Vector = std::array<double, 3>;
|
|
|
|
Vector ReadVector(const Ngs2GeomVector& value) {
|
|
if (!std::isfinite(value.x) || !std::isfinite(value.y) || !std::isfinite(value.z)) APS5_INVALID_ARG_EX;
|
|
return {value.x, value.y, value.z};
|
|
}
|
|
|
|
double Dot(const Vector& left, const Vector& right) {
|
|
return left[0] * right[0] + left[1] * right[1] + left[2] * right[2];
|
|
}
|
|
|
|
Vector Unit(Vector value) {
|
|
const double length = std::hypot(value[0], value[1], value[2]);
|
|
if (!(length > 0.0)) APS5_INVALID_ARG_EX;
|
|
for (auto& component : value) component /= length;
|
|
return value;
|
|
}
|
|
|
|
}
|
|
|
|
int Ngs2GeomCalcListener(const Ngs2GeomListenerParam* param, Ngs2GeomListenerWork* outWork, std::uint32_t flags) {
|
|
if (param == nullptr) APS5_INVALID_ARG_EX;
|
|
if (outWork == nullptr) return SCE_NGS2_ERROR_INVALID_OUT_ADDRESS;
|
|
if (flags > 1) throw std::runtime_error("NGS2: unknown listener coordinate flags");
|
|
if (!std::isfinite(param->sound_speed) || param->sound_speed <= 0.0f) APS5_INVALID_ARG_EX;
|
|
const Vector position = ReadVector(param->position);
|
|
ReadVector(param->velocity);
|
|
const Vector front = Unit(ReadVector(param->orient_front));
|
|
Vector up = Unit(ReadVector(param->orient_up));
|
|
Vector right = Unit({up[1] * front[2] - up[2] * front[1], up[2] * front[0] - up[0] * front[2], up[0] * front[1] - up[1] * front[0]});
|
|
up = {front[1] * right[2] - front[2] * right[1], front[2] * right[0] - front[0] * right[2], front[0] * right[1] - front[1] * right[0]};
|
|
if (flags == 1) for (auto& component : right) component = -component;
|
|
const std::array<Vector, 3> axes{right, up, front};
|
|
Ngs2GeomListenerWork work{};
|
|
for (std::size_t row = 0; row < axes.size(); row++) {
|
|
for (std::size_t column = 0; column < 3; column++) work.matrix[row][column] = static_cast<float>(axes[row][column]);
|
|
const double translation = -Dot(axes[row], position);
|
|
if (std::abs(translation) > std::numeric_limits<float>::max()) APS5_INVALID_ARG_EX;
|
|
work.matrix[row][3] = static_cast<float>(translation);
|
|
}
|
|
work.matrix[3][3] = 1.0f;
|
|
work.velocity = param->velocity;
|
|
work.sound_speed = param->sound_speed;
|
|
work.coordinate = flags;
|
|
*outWork = work;
|
|
return SCE_NGS2_OK;
|
|
}
|
|
|
|
int Ngs2GeomResetListenerParam(Ngs2GeomListenerParam* out_listener_param) {
|
|
if (out_listener_param == nullptr) return SCE_NGS2_ERROR_INVALID_OUT_ADDRESS;
|
|
*out_listener_param = {};
|
|
out_listener_param->orient_front.z = 1.0f;
|
|
out_listener_param->orient_up.y = 1.0f;
|
|
out_listener_param->sound_speed = DEFAULT_SOUND_SPEED;
|
|
return SCE_NGS2_OK;
|
|
}
|
|
|
|
int Ngs2GeomResetSourceParam(Ngs2GeomSourceParam* out_source_param) {
|
|
if (out_source_param == nullptr) return SCE_NGS2_ERROR_INVALID_OUT_ADDRESS;
|
|
*out_source_param = {};
|
|
out_source_param->direction.z = 1.0f;
|
|
out_source_param->cone.inner_level = 1.0f;
|
|
out_source_param->cone.inner_angle = 360.0f;
|
|
out_source_param->cone.outer_level = 1.0f;
|
|
out_source_param->cone.outer_angle = 360.0f;
|
|
out_source_param->rolloff.max_distance = 1000000.0f;
|
|
out_source_param->rolloff.rolloff_factor = 1.0f;
|
|
out_source_param->rolloff.reference_distance = 1.0f;
|
|
out_source_param->doppler_factor = 1.0f;
|
|
out_source_param->fbw_level = 1.0f;
|
|
out_source_param->lfe_level = 1.0f;
|
|
out_source_param->max_level = 1.0f;
|
|
out_source_param->num_speakers = 2;
|
|
out_source_param->matrix_format = 2;
|
|
return SCE_NGS2_OK;
|
|
}
|
|
|
|
int Ngs2GeomApply(const Ngs2GeomListenerWork* listener, const Ngs2GeomSourceParam* source, Ngs2GeomAttribute* out_attrib, std::uint32_t flags) {
|
|
if (listener == nullptr || source == nullptr) APS5_INVALID_ARG_EX;
|
|
if (out_attrib == nullptr) return SCE_NGS2_ERROR_INVALID_OUT_ADDRESS;
|
|
(void)flags;
|
|
std::memset(out_attrib, 0, sizeof(*out_attrib));
|
|
out_attrib->pitch_ratio = 1.0f;
|
|
out_attrib->a3d_attrib.position = source->position;
|
|
out_attrib->a3d_attrib.volume = std::max(source->min_level, source->max_level);
|
|
const std::uint32_t channels = std::min(source->matrix_format == 0u ? 2u : source->matrix_format, 8u);
|
|
const float level = source->max_level > 0.0f ? source->max_level : 1.0f;
|
|
for (std::uint32_t channel = 0; channel < channels; channel++) {
|
|
out_attrib->level[static_cast<std::size_t>(channel) * 8 + channel] = level;
|
|
}
|
|
return SCE_NGS2_OK;
|
|
}
|