aboutsummaryrefslogtreecommitdiffstats
path: root/Alc
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2018-03-19 06:41:38 -0700
committerChris Robinson <[email protected]>2018-03-19 06:41:38 -0700
commitdf6e4617e435e7e9a3fcf1647fafcb8df6c0ef4d (patch)
tree1c4cb1336f708f0797c45599317047a274006000 /Alc
parent325fca5215982da3267486f2605df47b6c6c826d (diff)
Fix the reverb panning behavior to better fit the spec
Previously it would attenuate the response from direction opposite to the vector, whereas the property descriptions say it should simply move all reflections toward the given direction.
Diffstat (limited to 'Alc')
-rw-r--r--Alc/effects/reverb.c121
1 files changed, 38 insertions, 83 deletions
diff --git a/Alc/effects/reverb.c b/Alc/effects/reverb.c
index 46129934..bd5553ad 100644
--- a/Alc/effects/reverb.c
+++ b/Alc/effects/reverb.c
@@ -1060,97 +1060,52 @@ static ALvoid UpdateLateLines(const ALfloat density, const ALfloat diffusion, co
}
}
-/* Creates a transform matrix given a reverb vector. This works by first
- * creating an inverse rotation around Y then X, applying a Z-focus transform,
- * then non-inverse rotations back around X then Y, to place the focal point in
- * the direction of the vector, using the vector length as a focus strength.
- *
- * This convoluted construction ultimately results in a B-Format transformation
- * matrix that retains its original orientation, but spatially focuses the
- * signal in the desired direction. There is probably a more efficient way to
- * do this, but let's see how good the optimizer is.
+/* Creates a transform matrix given a reverb vector. The vector pans the reverb
+ * reflections toward the given direction, using its magnitude (up to 1) as a
+ * focal strength. This function results in a B-Format transformation matrix
+ * that spatially focuses the signal in the desired direction.
*/
static aluMatrixf GetTransformFromVector(const ALfloat *vec)
{
const ALfloat sqrt_3 = 1.732050808f;
- aluMatrixf zfocus, xrot, yrot;
- aluMatrixf tmp1, tmp2;
- ALfloat length;
- ALfloat sa, a;
-
- length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
-
- /* Define a Z-focus (X in Ambisonics) transform, given the panning vector
- * length.
+ aluMatrixf focus;
+ ALfloat norm[3];
+ ALfloat mag;
+
+ /* Normalize the panning vector according to the N3D scale, which has an
+ * extra sqrt(3) term on the directional components. Converting from OpenAL
+ * to B-Format also requires negating X (ACN 1) and Z (ACN 3). Note however
+ * that the reverb panning vectors use right-handed coordinates, unlike the
+ * rest of OpenAL which use left-handed. This is fixed by negating Z, which
+ * cancels out with the B-Format Z negation.
*/
- sa = sinf(minf(length, 1.0f) * (F_PI/2.0f));
- aluMatrixfSet(&zfocus,
- 1.0f/(1.0f+sa), 0.0f, 0.0f, sa/(1.0f+sa)/sqrt_3,
- 0.0f, sqrtf((1.0f-sa)/(1.0f+sa)), 0.0f, 0.0f,
- 0.0f, 0.0f, sqrtf((1.0f-sa)/(1.0f+sa)), 0.0f,
- sa/(1.0f+sa)*sqrt_3, 0.0f, 0.0f, 1.0f/(1.0f+sa)
- );
-
- /* Define rotation around X (Y in Ambisonics) */
- a = atan2f(vec[1], sqrtf(vec[0]*vec[0] + vec[2]*vec[2]));
- aluMatrixfSet(&xrot,
- 1.0f, 0.0f, 0.0f, 0.0f,
- 0.0f, 1.0f, 0.0f, 0.0f,
- 0.0f, 0.0f, cosf(a), sinf(a),
- 0.0f, 0.0f, -sinf(a), cosf(a)
- );
+ mag = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
+ if(mag > 1.0f)
+ {
+ norm[0] = vec[0] / mag * -sqrt_3;
+ norm[1] = vec[1] / mag * sqrt_3;
+ norm[2] = vec[2] / mag * sqrt_3;
+ mag = 1.0f;
+ }
+ else
+ {
+ /* If the magnitude is less than or equal to 1, just apply the sqrt(3)
+ * term. There's no need to renormalize the magnitude since it would
+ * just be reapplied in the matrix.
+ */
+ norm[0] = vec[0] * -sqrt_3;
+ norm[1] = vec[1] * sqrt_3;
+ norm[2] = vec[2] * sqrt_3;
+ }
- /* Define rotation around Y (Z in Ambisonics). NOTE: EFX's reverb vectors
- * use a right-handled coordinate system, compared to the rest of OpenAL
- * which uses left-handed. This is fixed by negating Z, however it would
- * need to also be negated to get a proper Ambisonics angle, thus
- * cancelling it out.
- */
- a = atan2f(-vec[0], vec[2]);
- aluMatrixfSet(&yrot,
- 1.0f, 0.0f, 0.0f, 0.0f,
- 0.0f, cosf(a), 0.0f, sinf(a),
- 0.0f, 0.0f, 1.0f, 0.0f,
- 0.0f, -sinf(a), 0.0f, cosf(a)
+ aluMatrixfSet(&focus,
+ 1.0f, 0.0f, 0.0f, 0.0f,
+ norm[0], 1.0f-mag, 0.0f, 0.0f,
+ norm[1], 0.0f, 1.0f-mag, 0.0f,
+ norm[2], 0.0f, 0.0f, 1.0f-mag
);
- /* First, define a matrix that applies the inverse of the Y- then X-
- * rotation matrices, so that the desired direction lands on Z.
- */
-#define MATRIX_INVMULT(_res, _m1, _m2) do { \
- int row, col; \
- for(col = 0;col < 4;col++) \
- { \
- for(row = 0;row < 4;row++) \
- _res.m[row][col] = _m1.m[0][row]*_m2.m[col][0] + \
- _m1.m[1][row]*_m2.m[col][1] + \
- _m1.m[2][row]*_m2.m[col][2] + \
- _m1.m[3][row]*_m2.m[col][3]; \
- } \
-} while(0)
- MATRIX_INVMULT(tmp1, xrot, yrot);
-#undef MATRIX_INVMULT
-
-#define MATRIX_MULT(_res, _m1, _m2) do { \
- int row, col; \
- for(col = 0;col < 4;col++) \
- { \
- for(row = 0;row < 4;row++) \
- _res.m[row][col] = _m1.m[row][0]*_m2.m[0][col] + \
- _m1.m[row][1]*_m2.m[1][col] + \
- _m1.m[row][2]*_m2.m[2][col] + \
- _m1.m[row][3]*_m2.m[3][col]; \
- } \
-} while(0)
- /* Now apply matrices to focus on Z, then rotate back around X then Y, to
- * result in a focus in the direction of the vector.
- */
- MATRIX_MULT(tmp2, zfocus, tmp1);
- MATRIX_MULT(tmp1, xrot, tmp2);
- MATRIX_MULT(tmp2, yrot, tmp1);
-#undef MATRIX_MULT
-
- return tmp2;
+ return focus;
}
/* Update the early and late 3D panning gains. */