This commit is contained in:
2025-11-13 17:40:28 +08:00
parent 962ab49609
commit 10156da245
5503 changed files with 805282 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 72b2687315f3a4375aceab7d9de97c79
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,39 @@
// Copyright © 2015-2021 Pico Technology Co., Ltd. All Rights Reserved.
using UnityEngine;
[RequireComponent(typeof(AudioSource))]
public class PXR_Audio_Spatializer_AmbisonicSource : MonoBehaviour
{
private AudioSource nativeSource;
private float playheadPosition = 0.0f;
private bool wasPlaying = false;
/// <summary>
/// Resume audio playing status.
/// </summary>
public void Resume()
{
if (nativeSource)
{
nativeSource.time = playheadPosition;
if (wasPlaying)
{
nativeSource.Play();
}
}
}
void Awake()
{
nativeSource = GetComponent<AudioSource>();
}
void Update()
{
if (nativeSource.isPlaying)
playheadPosition = nativeSource.time;
wasPlaying = nativeSource.isPlaying;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a36d16e4ff48444b8ac021c183d3e3fd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,117 @@
// Copyright © 2015-2022 Pico Technology Co., Ltd. All Rights Reserved.
using System.Collections;
using System.Runtime.InteropServices;
using PXR_Audio.Spatializer;
using UnityEngine;
[RequireComponent(typeof(AudioListener))]
public class PXR_Audio_Spatializer_AudioListener : MonoBehaviour
{
private enum OutputMethod
{
OnAudioFilterRead,
PicoAudioRouter
}
[Tooltip("Determine where the output spatial audio signal goes:\n" +
" - On Audio Filter Read: Spatial audio signal got mixed with the rest of the game.\n" +
" - Pico Audio Router: Spatial audio signal got transmitted to one or more 'Pico Audio Router' effects in the Unity Audio Mixer to gain more control to your mix.")]
[SerializeField]
private OutputMethod outputMethod = OutputMethod.OnAudioFilterRead;
private float[] temp_output_buffer = new float[2048];
private bool isActive;
private PXR_Audio_Spatializer_Context context;
private PXR_Audio_Spatializer_Context Context
{
get
{
if (context == null)
context = PXR_Audio_Spatializer_Context.Instance;
return context;
}
}
private float[] positionArray = new float[3] { 0.0f, 0.0f, 0.0f };
private float[] frontArray = new float[3] { 0.0f, 0.0f, 0.0f };
private float[] upArray = new float[3] { 0.0f, 0.0f, 0.0f };
private bool isAudioDSPInProgress = false;
public bool IsAudioDSPInProgress
{
get { return isAudioDSPInProgress; }
}
internal void RegisterInternal()
{
// Initialize listener pose
if (Context.spatializerApiImpl != SpatializerApiImpl.wwise)
{
UpdatePose();
}
isActive = true;
}
private void OnEnable()
{
// Wait for context to be initialized
if (Context != null && Context.Initialized)
RegisterInternal();
}
void Update()
{
if (isActive && context != null && context.Initialized && transform.hasChanged &&
context.spatializerApiImpl != SpatializerApiImpl.wwise)
{
UpdatePose();
}
}
private void OnDisable()
{
isActive = false;
isAudioDSPInProgress = false;
}
void UpdatePose()
{
positionArray[0] = transform.position.x;
positionArray[1] = transform.position.y;
positionArray[2] = -transform.position.z;
frontArray[0] = transform.forward.x;
frontArray[1] = transform.forward.y;
frontArray[2] = -transform.forward.z;
upArray[0] = transform.up.x;
upArray[1] = transform.up.y;
upArray[2] = -transform.up.z;
Context.SetListenerPose(positionArray, frontArray, upArray);
}
[DllImport("PicoAudioRouter", EntryPoint = "yggdrasil_audio_unity_audio_router_input")]
private static extern void PicoAudioRouterInput(float[] inBuffer, int inBufferSize, int inChannels);
private void OnAudioFilterRead(float[] data, int channels)
{
if (!isActive || context == null || !context.Initialized ||
Context.spatializerApiImpl == SpatializerApiImpl.wwise)
return;
isAudioDSPInProgress = true;
if (outputMethod == OutputMethod.OnAudioFilterRead)
context.GetInterleavedBinauralBuffer(data, (uint)(data.Length / channels), true);
else if (outputMethod == OutputMethod.PicoAudioRouter)
{
context.GetInterleavedBinauralBuffer(temp_output_buffer, (uint)(data.Length / channels), false);
PicoAudioRouterInput(temp_output_buffer, data.Length / channels, channels);
}
isAudioDSPInProgress = false;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 67f4b0fc61d6d4b6499b28a0a4461b6f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,586 @@
// Copyright © 2015-2022 Pico Technology Co., Ltd. All Rights Reserved.
using System;
using System.Collections;
using PXR_Audio.Spatializer;
using UnityEditor;
using UnityEngine;
[RequireComponent(typeof(AudioSource))]
public class PXR_Audio_Spatializer_AudioSource : MonoBehaviour
{
[SerializeField] [Range(0.0f, 24.0f)]
private float sourceGainDB = 0.0f;
private float sourceGainAmplitude = 1.0f;
[SerializeField] [Range(-120.0f, 48.0f)]
private float reflectionGainDB = 0.0f;
private float reflectionGainAmplitude = 1.0f;
[SerializeField] [Range(0.0f, 100000.0f)]
private float sourceSize = 0.0f;
[Tooltip(
"Whether Pico Doppler Simulation is enabled for this sound source, which affects both direct and reflection path of it.\n" +
" - If you disabled this option before run or build, you cannot turn it on during runtime, since doppler effect unit is not initialized to save memory.")]
[SerializeField]
private bool enableDoppler = true;
[Tooltip(
"Mode of distance attenuation of this sound source.\n" +
" - None && Fixed: Source volume doesn't decrease when source-listener distance increases.\n" +
" - Inversed Squared: Source volume decrease when source-listener distance increases, just like the real world.\n" +
" - Customized: Don't use it!!!!")]
[SerializeField]
public SourceAttenuationMode sourceAttenuationMode = SourceAttenuationMode.InverseSquare;
[Tooltip(
"Source volume will not further increase when source-listener distance is less than this.\n" +
" - Only effective when source attenuation mode == Inversed Squared")]
[SerializeField]
public float minAttenuationDistance = 1.0f;
[Tooltip(
"Source volume will not further decrease when source-listener distance is more than this.\n" +
" - Only effective when source attenuation mode == Inversed Squared")]
[SerializeField]
public float maxAttenuationDistance = 100.0f;
[Tooltip("Determine shape of the radiation polar pattern of this sound source.\n" +
" - Alpha = 0 gives you omnidirectional polar pattern\n" +
" - Alpha = 0.5 gives you cardioid polar pattern\n" +
" - Alpha = 1 gives you figure-8 polar pattern")]
[SerializeField] [Range(0.0f, 1.0f)] private float directivityAlpha = 0.0f;
[Tooltip("Determine width of the radiation polar pattern of this sound source.\n" +
" - Larger order gives you narrower radiation pattern.")]
[SerializeField] [Range(0.0f, 1000.0f)]
private float directivityOrder = 1.0f;
#if UNITY_EDITOR
private Mesh directivityDisplayMesh;
#endif
private SourceConfig sourceConfig;
private uint sourcePropertyMask = 0;
private bool isActive;
private bool isAudioDSPInProgress = false;
public bool IsAudioDSPInProgress
{
get { return isAudioDSPInProgress; }
}
private PXR_Audio_Spatializer_Context context;
private PXR_Audio_Spatializer_Context Context
{
get
{
if (context == null)
context = PXR_Audio_Spatializer_Context.Instance;
return context;
}
}
private AudioSource nativeSource;
private int sourceId = -1;
private int currentContextUuid = -2;
private float[] positionArray = new float[3] { 0.0f, 0.0f, 0.0f };
private float playheadPosition = 0.0f;
private bool wasPlaying = false;
private void OnEnable()
{
if (Context != null && Context.Initialized)
{
if (Context.UUID == currentContextUuid)
isActive = true;
else
RegisterInternal();
}
else
{
sourceId = -1;
currentContextUuid = -2;
}
}
/// <summary>
/// Register this audio source in spatializer
/// </summary>
internal void RegisterInternal()
{
nativeSource = GetComponent<AudioSource>();
positionArray[0] = transform.position.x;
positionArray[1] = transform.position.y;
positionArray[2] = -transform.position.z;
sourceConfig = new SourceConfig(PXR_Audio.Spatializer.SourceMode.Spatialize);
sourcePropertyMask = 0;
sourceConfig.position.x = positionArray[0];
sourceConfig.position.y = positionArray[1];
sourceConfig.position.z = positionArray[2];
sourceConfig.front.x = transform.forward.x;
sourceConfig.front.y = transform.forward.y;
sourceConfig.front.z = -transform.forward.z;
sourceConfig.up.x = transform.up.x;
sourceConfig.up.y = transform.up.y;
sourceConfig.up.z = -transform.up.z;
sourceConfig.enableDoppler = enableDoppler;
sourceGainAmplitude = DB2Mag(sourceGainDB);
sourceConfig.sourceGain = sourceGainAmplitude;
reflectionGainAmplitude = DB2Mag(reflectionGainDB);
sourceConfig.reflectionGain = reflectionGainAmplitude;
sourceConfig.radius = sourceSize;
sourceConfig.attenuationMode = sourceAttenuationMode;
sourceConfig.minAttenuationDistance = minAttenuationDistance;
sourceConfig.maxAttenuationDistance = maxAttenuationDistance;
sourceConfig.directivityAlpha = directivityAlpha;
sourceConfig.directivityOrder = directivityOrder;
PXR_Audio.Spatializer.Result ret = Context.AddSourceWithConfig(
ref sourceConfig,
ref sourceId,
false);
if (ret != PXR_Audio.Spatializer.Result.Success)
{
Debug.LogError("Failed to add source.");
return;
}
isActive = true;
currentContextUuid = Context.UUID;
Debug.Log("Source #" + sourceId + " is added.");
}
/// <summary>
/// Resume playing status of this source
/// </summary>
public void Resume()
{
nativeSource.time = playheadPosition;
if (wasPlaying)
{
nativeSource.Play();
}
}
/// <summary>
/// Setup source gain in dB
/// </summary>
/// <param name="gainDB">Gain in dB</param>
public void SetGainDB(float gainDB)
{
// if (Mathf.Abs(gainDB - sourceGainDB) < 1e-7) return;
sourceGainDB = gainDB;
sourceConfig.sourceGain = sourceGainAmplitude = DB2Mag(gainDB);
sourcePropertyMask |= (uint)SourceProperty.SourceGain;
}
/// <summary>
/// Get source gain in dB
/// </summary>
public float GetGainDB()
{
return sourceGainDB;
}
/// <summary>
/// Setup source gain in Amplitude
/// </summary>
/// <param name="gainAmplitude">Gain in Amplitude</param>
public void SetGainAmplitude(float gainAmplitude)
{
sourceConfig.sourceGain = sourceGainAmplitude = gainAmplitude;
sourceGainDB = Mag2DB(gainAmplitude);
sourcePropertyMask |= (uint)SourceProperty.SourceGain;
}
/// <summary>
/// Setup source reflection gain in dB
/// </summary>
/// <param name="gainDB">Gain in dB</param>
public void SetReflectionGainDB(float gainDB)
{
reflectionGainDB = gainDB;
sourceConfig.reflectionGain = reflectionGainAmplitude = DB2Mag(gainDB);
sourcePropertyMask |= (uint)SourceProperty.ReflectionGain;
}
/// <summary>
/// Get source reflection gain in dB
/// </summary>
public float GetReflectionGainDB()
{
return reflectionGainDB;
}
/// <summary>
/// Setup source radius in meters
/// </summary>
/// <param name="radius">source radius in meter</param>
public void SetSize(float radius)
{
sourceConfig.radius = sourceSize = radius;
sourcePropertyMask |= (uint)SourceProperty.VolumetricRadius;
}
/// <summary>
/// Get source radius in meters
/// </summary>
public float GetSize()
{
return sourceSize;
}
/// <summary>
/// Turn on/off in-engine doppler effect
/// </summary>
/// <param name="on">Turn doppler effect on/off </param>
public void SetDopplerStatus(bool on)
{
sourceConfig.enableDoppler = enableDoppler = on;
sourcePropertyMask |= (uint)SourceProperty.DopplerOnOff;
}
/// <summary>
/// Get in-engine doppler effect status
/// </summary>
public bool GetDopplerStatus()
{
return sourceConfig.enableDoppler;
}
/// <summary>
/// Get source attenuation mode
/// </summary>
public SourceAttenuationMode GetAttenuationMode()
{
return sourceConfig.attenuationMode;
}
/// <summary>
/// Setup min attenuation range
/// </summary>
/// <param name="min"> Minimum attenuation range. Source loudness would stop increasing when source-listener
/// distance is shorter than this </param>
public void SetMinAttenuationRange(float min)
{
sourceConfig.minAttenuationDistance = minAttenuationDistance = min;
sourcePropertyMask |= (uint)SourceProperty.RangeMin;
}
/// <summary>
/// Get min attenuation range
/// </summary>
public float GetMinAttenuationRange()
{
return sourceConfig.minAttenuationDistance;
}
/// <summary>
/// Setup max attenuation range
/// </summary>
/// <param name="max"> Maximum attenuation range. Source loudness would stop decreasing when source-listener
/// distance is further than this </param>
public void SetMaxAttenuationRange(float max)
{
sourceConfig.maxAttenuationDistance = maxAttenuationDistance = max;
sourcePropertyMask |= (uint)SourceProperty.RangeMax;
}
/// <summary>
/// Get max attenuation range
/// </summary>
public float GetMaxAttenuationRange()
{
return sourceConfig.maxAttenuationDistance;
}
/// <summary>
/// Setup the radiation polar pattern of source, which describes the gain of initial sound wave radiated towards
/// different directions. The relation between sound emission direction, alpha, and order can be described as
/// follows: Let theta equals the angle between radiation direction and source front direction, the directivity
/// gain g is:
/// g = (|1 - alpha| + alpha * cos(theta)) ^ order;
/// </summary>
/// <param name="alpha"> Define the shape of the directivity pattern.
/// <param name="order"> Indicates how sharp the source polar pattern is.
public void SetDirectivity(float alpha, float order)
{
sourceConfig.directivityAlpha = directivityAlpha = alpha;
sourceConfig.directivityOrder = directivityOrder = order;
sourcePropertyMask |= (uint)SourceProperty.Directivity;
}
public float GetDirectivityAlpha()
{
return sourceConfig.directivityAlpha;
}
public float GetDirectivityOrder()
{
return sourceConfig.directivityOrder;
}
void Update()
{
if (isActive && sourceId >= 0 && context != null && context.Initialized)
{
if (transform.hasChanged)
{
sourceConfig.position.x = transform.position.x;
sourceConfig.position.y = transform.position.y;
sourceConfig.position.z = -transform.position.z;
sourceConfig.front.x = transform.forward.x;
sourceConfig.front.y = transform.forward.y;
sourceConfig.front.z = -transform.forward.z;
sourceConfig.up.x = transform.up.x;
sourceConfig.up.y = transform.up.y;
sourceConfig.up.z = -transform.up.z;
sourcePropertyMask |= (uint)SourceProperty.Position | (uint)SourceProperty.Orientation;
transform.hasChanged = false;
}
if (sourcePropertyMask != 0)
{
var ret = Context.SetSourceConfig(sourceId, ref sourceConfig, sourcePropertyMask);
if (ret == Result.Success)
sourcePropertyMask = 0;
}
if (nativeSource.isPlaying)
playheadPosition = nativeSource.time;
wasPlaying = nativeSource.isPlaying;
}
}
private void OnDisable()
{
isActive = false;
isAudioDSPInProgress = false;
}
private void OnDestroy()
{
DestroyInternal();
}
#if UNITY_EDITOR
void OnValidate()
{
if (EditorApplication.isPlaying)
{
SetGainDB(sourceGainDB);
SetReflectionGainDB(reflectionGainDB);
SetSize(sourceSize);
SetDopplerStatus(enableDoppler);
SetDirectivity(directivityAlpha, directivityOrder);
}
}
#endif
private void DestroyInternal()
{
isActive = false;
if (context != null && context.Initialized)
{
var ret = context.RemoveSource(sourceId);
if (ret != PXR_Audio.Spatializer.Result.Success)
{
Debug.LogError("Failed to delete source #" + sourceId + ", error code is: " + ret);
}
else
{
Debug.Log("Source #" + sourceId + " is deleted.");
}
}
isAudioDSPInProgress = false;
sourceId = -1;
}
private void OnAudioFilterRead(float[] data, int channels)
{
if (!isActive || sourceId < 0 || context == null || !context.Initialized)
{
// Mute Original signal
for (int i = 0; i < data.Length; ++i)
data[i] = 0.0f;
return;
}
isAudioDSPInProgress = true;
int numFrames = data.Length / channels;
float oneOverChannelsF = 1.0f / ((float)channels);
// force to mono
if (channels > 1)
{
for (int frame = 0; frame < numFrames; ++frame)
{
float sample = 0.0f;
for (int channel = 0; channel < channels; ++channel)
{
sample += data[frame * channels + channel];
}
data[frame] = sample * oneOverChannelsF;
}
}
Context.SubmitSourceBuffer(sourceId, data, (uint)numFrames);
// Mute Original signal
for (int i = 0; i < data.Length; ++i)
data[i] = 0.0f;
isAudioDSPInProgress = false;
}
private float DB2Mag(float db)
{
return Mathf.Pow(10.0f, db / 20.0f);
}
private float Mag2DB(float mag)
{
return 20 * Mathf.Log10(mag);
}
void OnDrawGizmos()
{
Color c;
const float colorSolidAlpha = 0.1f;
// VolumetricRadius (purple)
c.r = 1.0f;
c.g = 0.0f;
c.b = 1.0f;
c.a = 1.0f;
Gizmos.color = c;
Gizmos.DrawWireSphere(transform.position, sourceSize);
c.a = colorSolidAlpha;
Gizmos.color = c;
Gizmos.DrawSphere(transform.position, sourceSize);
// Attenuation distance (min && max)
if (sourceAttenuationMode == SourceAttenuationMode.InverseSquare)
{
// min
c.r = 1.0f;
c.g = 0.35f;
c.b = 0.0f;
c.a = 1.0f;
Gizmos.color = c;
Gizmos.DrawWireSphere(transform.position, minAttenuationDistance);
c.a = colorSolidAlpha;
Gizmos.color = c;
Gizmos.DrawSphere(transform.position, minAttenuationDistance);
// max
c.r = 0.0f;
c.g = 1.0f;
c.b = 1.0f;
c.a = 1.0f;
Gizmos.color = c;
Gizmos.DrawWireSphere(transform.position, maxAttenuationDistance);
c.a = colorSolidAlpha;
Gizmos.color = c;
Gizmos.DrawSphere(transform.position, maxAttenuationDistance);
}
}
#if UNITY_EDITOR
private void OnDrawGizmosSelected()
{
// Draw directivity mesh
GeneratePolarPatternMesh(directivityDisplayMesh, directivityAlpha, directivityOrder);
}
private void GeneratePolarPatternMesh(Mesh mesh, float alpha, float order)
{
if (mesh == null)
mesh = new Mesh();
Vector2[] cardioidVertices2D = GeneratePolarPatternVertices2D(alpha, order, 90);
int numVertices = cardioidVertices2D.Length * 2;
Vector3[] vertices = new Vector3[numVertices];
for (int i = 0; i < cardioidVertices2D.Length; ++i)
{
var vertex2D = cardioidVertices2D[i];
vertices[i] = new Vector3(vertex2D.x, 0.0f, vertex2D.y);
vertices[cardioidVertices2D.Length + i] = Quaternion.AngleAxis(45, Vector3.forward) *
new Vector3(vertex2D.x, 0.0f, vertex2D.y);
}
int[] indices = new int[cardioidVertices2D.Length * 2 * 3];
int idx = 0;
for (idx = 0; idx < cardioidVertices2D.Length - 1; ++idx)
{
indices[idx * 6 + 0] = idx;
indices[idx * 6 + 1] = idx + 1;
indices[idx * 6 + 2] = idx + cardioidVertices2D.Length;
indices[idx * 6 + 3] = idx + 1;
indices[idx * 6 + 4] = idx + cardioidVertices2D.Length + 1;
indices[idx * 6 + 5] = idx + cardioidVertices2D.Length;
}
// Construct a new mesh for the gizmo.
mesh.vertices = vertices;
mesh.triangles = indices;
mesh.RecalculateNormals();
// Draw the mesh.
Vector3 scale = 2.0f * Mathf.Max(transform.lossyScale.x, transform.lossyScale.z) * Vector3.one;
Color c;
c.r = 0.2f;
c.g = 0.5f;
c.b = 0.7f;
c.a = 0.5f;
Gizmos.color = c;
Gizmos.DrawMesh(mesh, transform.position, transform.rotation, scale);
Gizmos.DrawMesh(mesh, transform.position, transform.rotation * Quaternion.AngleAxis(45, Vector3.forward),
scale);
Gizmos.DrawMesh(mesh, transform.position, transform.rotation * Quaternion.AngleAxis(90, Vector3.forward),
scale);
Gizmos.DrawMesh(mesh, transform.position, transform.rotation * Quaternion.AngleAxis(135, Vector3.forward),
scale);
Gizmos.DrawMesh(mesh, transform.position, transform.rotation * Quaternion.AngleAxis(180, Vector3.forward),
scale);
Gizmos.DrawMesh(mesh, transform.position, transform.rotation * Quaternion.AngleAxis(225, Vector3.forward),
scale);
Gizmos.DrawMesh(mesh, transform.position, transform.rotation * Quaternion.AngleAxis(270, Vector3.forward),
scale);
Gizmos.DrawMesh(mesh, transform.position, transform.rotation * Quaternion.AngleAxis(315, Vector3.forward),
scale);
}
private Vector2[] GeneratePolarPatternVertices2D(float alpha, float order, int numVertices)
{
Vector2[] points = new Vector2[numVertices];
float interval = Mathf.PI / (numVertices - 1);
for (int i = 0; i < numVertices; ++i)
{
float theta = 0.0f;
if (i != numVertices - 1)
theta = i * interval;
else
theta = Mathf.PI;
// Magnitude |r| for |theta| in radians.
float r = Mathf.Pow(Mathf.Abs((1 - alpha) + alpha * Mathf.Cos(theta)), order);
points[i] = new Vector2(r * Mathf.Sin(theta), r * Mathf.Cos(theta));
}
return points;
}
#endif
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 30f78ed5e5eb44c188a740990ce49414
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,654 @@
// Copyright © 2015-2022 Pico Technology Co., Ltd. All Rights Reserved.
using System;
using System.Collections;
using PXR_Audio.Spatializer;
using UnityEngine;
using UnityEngine.Events;
#if UNITY_EDITOR
using UnityEditor;
#endif
public partial class PXR_Audio_Spatializer_Context : MonoBehaviour
{
[Tooltip("Audio backend you prefer to use")] [SerializeField]
public SpatializerApiImpl spatializerApiImpl = SpatializerApiImpl.unity;
private static PXR_Audio.Spatializer.Api _api = null;
#if UNITY_EDITOR
private static SpatializerApiImpl _lastSpatializerApiImpl;
#endif
public PXR_Audio.Spatializer.Api PXR_Audio_Spatializer_Api
{
get
{
#if UNITY_EDITOR
if (_api == null ||
(_lastSpatializerApiImpl != spatializerApiImpl && !EditorApplication.isPlaying))
#else
if (_api == null)
#endif
{
if (spatializerApiImpl == SpatializerApiImpl.unity)
_api = new ApiUnityImpl();
else if (spatializerApiImpl == SpatializerApiImpl.wwise)
_api = new ApiWwiseImpl();
#if UNITY_EDITOR
_lastSpatializerApiImpl = spatializerApiImpl;
#endif
}
return _api;
}
}
private static PXR_Audio_Spatializer_Context _instance;
public static PXR_Audio_Spatializer_Context Instance => _instance;
private IntPtr context = IntPtr.Zero;
private bool initialized = false;
private bool isSceneDirty = false;
public bool Initialized
{
get => initialized;
}
[Tooltip(
"Rendering quality for Pico Spatial Audio system. Higher quality gives you better accuracy to real world, while lower quality saves you more computation.\n" +
" - You need to re-enable this component after your changed quality during runtime.")]
[SerializeField]
private PXR_Audio.Spatializer.RenderingMode renderingQuality = PXR_Audio.Spatializer.RenderingMode.MediumQuality;
#region EDITOR-ONLY SerializedFields
#if UNITY_EDITOR
[SerializeField, HideInInspector] private LayerMask meshBakingLayerMask = ~0;
#endif
#endregion
public PXR_Audio.Spatializer.RenderingMode RenderingQuality => renderingQuality;
[Tooltip("Customizable event executed right before Pico Spatial Audio system is initialized for this game.")]
[SerializeField]
private UnityEvent preInitEvent;
[Tooltip("Customizable event executed right after Pico Spatial Audio system is initialized for this game.")]
[SerializeField]
private UnityEvent lateInitEvent;
private AudioConfiguration audioConfig;
public AudioConfiguration AudioConfig => audioConfig;
private bool bypass = true;
private bool Bypass => bypass;
static int uuidCounter = 0;
private static int GetUuid()
{
var temp = uuidCounter;
uuidCounter = (uuidCounter == Int32.MaxValue) ? 0 : (uuidCounter + 1);
return temp;
}
private int uuid = -1;
public int UUID => uuid;
public PXR_Audio.Spatializer.Result SubmitMesh(
float[] vertices,
int verticesCount,
int[] indices,
int indicesCount,
PXR_Audio.Spatializer.AcousticsMaterial material,
ref int geometryId)
{
isSceneDirty = true;
return PXR_Audio_Spatializer_Api.SubmitMesh(
context,
vertices,
verticesCount,
indices,
indicesCount,
material,
ref geometryId);
}
public PXR_Audio.Spatializer.Result SubmitMeshAndMaterialFactor(
float[] vertices,
int verticesCount,
int[] indices,
int indicesCount,
float[] absorptionFactor,
float scatteringFactor,
float transmissionFactor,
ref int geometryId)
{
isSceneDirty = true;
return PXR_Audio_Spatializer_Api.SubmitMeshAndMaterialFactor(
context,
vertices,
verticesCount,
indices,
indicesCount,
absorptionFactor,
scatteringFactor,
transmissionFactor,
ref geometryId);
}
public Result SubmitMeshWithConfig(float[] vertices, int verticesCount, int[] indices, int indicesCount,
ref MeshConfig config, ref int geometryId)
{
isSceneDirty = true;
return PXR_Audio_Spatializer_Api.SubmitMeshWithConfig(context, vertices, verticesCount, indices, indicesCount,
ref config, ref geometryId);
}
public Result UpdateMesh(int geometryId, float[] newVertices, int newVerticesCount, int[] newIndices,
int newIndicesCount, ref MeshConfig config, ref int newGeometryId)
{
isSceneDirty = true;
return PXR_Audio_Spatializer_Api.UpdateMesh(context, geometryId, newVertices, newVerticesCount, newIndices,
newIndicesCount, ref config, ref newGeometryId);
}
public Result RemoveMesh(int geometryId)
{
isSceneDirty = true;
return PXR_Audio_Spatializer_Api.RemoveMesh(context, geometryId);
}
public int GetNumOfGeometries()
{
return PXR_Audio_Spatializer_Api.GetNumOfGeometries(context);
}
public Result SetMeshConfig(int geometryId, ref MeshConfig config, uint propertyMask)
{
isSceneDirty = true;
return PXR_Audio_Spatializer_Api.SetMeshConfig(context, geometryId, ref config, propertyMask);
}
public PXR_Audio.Spatializer.Result AddSource(
PXR_Audio.Spatializer.SourceMode sourceMode,
float[] position,
ref int sourceId,
bool isAsync = false)
{
return PXR_Audio_Spatializer_Api.AddSource(
context,
sourceMode,
position,
ref sourceId,
isAsync);
}
public PXR_Audio.Spatializer.Result AddSourceWithOrientation(
PXR_Audio.Spatializer.SourceMode mode,
float[] position,
float[] front,
float[] up,
float radius,
ref int sourceId,
bool isAsync)
{
return PXR_Audio_Spatializer_Api.AddSourceWithOrientation(
context,
mode,
position,
front,
up,
radius,
ref sourceId,
isAsync);
}
public PXR_Audio.Spatializer.Result AddSourceWithConfig(
ref PXR_Audio.Spatializer.SourceConfig sourceConfig,
ref int sourceId,
bool isAsync)
{
return PXR_Audio_Spatializer_Api.AddSourceWithConfig(context, ref sourceConfig, ref sourceId, isAsync);
}
public Result SetSourceConfig(int sourceId, ref SourceConfig sourceConfig, uint propertyMask)
{
return PXR_Audio_Spatializer_Api.SetSourceConfig(context, sourceId, ref sourceConfig, propertyMask);
}
public PXR_Audio.Spatializer.Result SetSourceAttenuationMode(int sourceId,
PXR_Audio.Spatializer.SourceAttenuationMode mode,
PXR_Audio.Spatializer.DistanceAttenuationCallback directDistanceAttenuationCallback = null,
PXR_Audio.Spatializer.DistanceAttenuationCallback indirectDistanceAttenuationCallback = null)
{
return PXR_Audio_Spatializer_Api.SetSourceAttenuationMode(context, sourceId, mode,
directDistanceAttenuationCallback, indirectDistanceAttenuationCallback);
}
public PXR_Audio.Spatializer.Result SetSourceRange(int sourceId, float rangeMin, float rangeMax)
{
return PXR_Audio_Spatializer_Api.SetSourceRange(context, sourceId, rangeMin, rangeMax);
}
public PXR_Audio.Spatializer.Result RemoveSource(int sourceId)
{
return PXR_Audio_Spatializer_Api.RemoveSource(context, sourceId);
}
public PXR_Audio.Spatializer.Result SubmitSourceBuffer(
int sourceId,
float[] inputBufferPtr,
uint numFrames)
{
return PXR_Audio_Spatializer_Api.SubmitSourceBuffer(
context,
sourceId,
inputBufferPtr,
numFrames);
}
public PXR_Audio.Spatializer.Result SubmitAmbisonicChannelBuffer(
float[] ambisonicChannelBuffer,
int order,
int degree,
PXR_Audio.Spatializer.AmbisonicNormalizationType normType,
float gain)
{
return PXR_Audio_Spatializer_Api.SubmitAmbisonicChannelBuffer(
context,
ambisonicChannelBuffer,
order,
degree,
normType,
gain);
}
public PXR_Audio.Spatializer.Result SubmitInterleavedAmbisonicBuffer(
float[] ambisonicBuffer,
int ambisonicOrder,
PXR_Audio.Spatializer.AmbisonicNormalizationType normType,
float gain)
{
return PXR_Audio_Spatializer_Api.SubmitInterleavedAmbisonicBuffer(
context,
ambisonicBuffer,
ambisonicOrder,
normType,
gain);
}
public PXR_Audio.Spatializer.Result SubmitMatrixInputBuffer(
float[] inputBuffer,
int inputChannelIndex)
{
return PXR_Audio_Spatializer_Api.SubmitMatrixInputBuffer(
context,
inputBuffer,
inputChannelIndex);
}
public PXR_Audio.Spatializer.Result GetInterleavedBinauralBuffer(
float[] outputBufferPtr,
uint numFrames,
bool isAccumulative)
{
return PXR_Audio_Spatializer_Api.GetInterleavedBinauralBuffer(
context,
outputBufferPtr,
numFrames,
isAccumulative);
}
public PXR_Audio.Spatializer.Result GetPlanarBinauralBuffer(
float[][] outputBufferPtr,
uint numFrames,
bool isAccumulative)
{
return PXR_Audio_Spatializer_Api.GetPlanarBinauralBuffer(
context,
outputBufferPtr,
numFrames,
isAccumulative);
}
public PXR_Audio.Spatializer.Result GetInterleavedLoudspeakersBuffer(
float[] outputBufferPtr,
uint numFrames)
{
return PXR_Audio_Spatializer_Api.GetInterleavedLoudspeakersBuffer(
context,
outputBufferPtr,
numFrames);
}
public PXR_Audio.Spatializer.Result GetPlanarLoudspeakersBuffer(
float[][] outputBufferPtr,
uint numFrames)
{
return PXR_Audio_Spatializer_Api.GetPlanarLoudspeakersBuffer(
context,
outputBufferPtr,
numFrames);
}
public PXR_Audio.Spatializer.Result SetPlaybackMode(
PXR_Audio.Spatializer.PlaybackMode playbackMode)
{
return PXR_Audio_Spatializer_Api.SetPlaybackMode(
context,
playbackMode);
}
public PXR_Audio.Spatializer.Result SetLoudspeakerArray(
float[] positions,
int numLoudspeakers)
{
return PXR_Audio_Spatializer_Api.SetLoudspeakerArray(
context,
positions,
numLoudspeakers);
}
public PXR_Audio.Spatializer.Result SetMappingMatrix(
float[] matrix,
int numInputChannels,
int numOutputChannels)
{
return PXR_Audio_Spatializer_Api.SetMappingMatrix(
context,
matrix,
numInputChannels,
numOutputChannels);
}
public PXR_Audio.Spatializer.Result SetListenerPosition(
float[] position)
{
return PXR_Audio_Spatializer_Api.SetListenerPosition(
context,
position);
}
public PXR_Audio.Spatializer.Result SetListenerOrientation(
float[] front,
float[] up)
{
return PXR_Audio_Spatializer_Api.SetListenerOrientation(
context,
front,
up);
}
public PXR_Audio.Spatializer.Result SetListenerPose(
float[] position,
float[] front,
float[] up)
{
return PXR_Audio_Spatializer_Api.SetListenerPose(
context,
position,
front,
up);
}
public PXR_Audio.Spatializer.Result SetSourcePosition(
int sourceId,
float[] position)
{
return PXR_Audio_Spatializer_Api.SetSourcePosition(
context,
sourceId,
position);
}
public PXR_Audio.Spatializer.Result SetSourceGain(
int sourceId,
float gain)
{
return PXR_Audio_Spatializer_Api.SetSourceGain(
context,
sourceId,
gain);
}
public PXR_Audio.Spatializer.Result SetSourceSize(
int sourceId,
float volumetricSize)
{
return PXR_Audio_Spatializer_Api.SetSourceSize(
context,
sourceId,
volumetricSize);
}
public PXR_Audio.Spatializer.Result UpdateSourceMode(
int sourceId,
PXR_Audio.Spatializer.SourceMode mode)
{
return PXR_Audio_Spatializer_Api.UpdateSourceMode(
context,
sourceId,
mode);
}
public PXR_Audio.Spatializer.Result SetDopplerEffect(int sourceId, bool on)
{
return PXR_Audio_Spatializer_Api.SetDopplerEffect(context, sourceId, on);
}
public Result GetAbsorptionFactors(AcousticsMaterial material,
float[] absorptionFactor)
{
return PXR_Audio_Spatializer_Api.GetAbsorptionFactor(material, absorptionFactor);
}
public Result GetScatteringFactors(AcousticsMaterial material,
ref float scatteringFactor)
{
return PXR_Audio_Spatializer_Api.GetScatteringFactor(material, ref scatteringFactor);
}
public Result GetTransmissionFactors(AcousticsMaterial material,
ref float transmissionFactor)
{
return PXR_Audio_Spatializer_Api.GetTransmissionFactor(material, ref transmissionFactor);
}
void OnAudioConfigurationChangedEventHandler(bool deviceWasChanged)
{
audioConfig = AudioSettings.GetConfiguration();
ResetContext(renderingQuality);
}
/// <summary>
/// Setup Spatializer rendering quality.
/// </summary>
/// <param name="quality">Rendering quality preset.</param>
public void SetRenderingQuality(PXR_Audio.Spatializer.RenderingMode quality)
{
renderingQuality = quality;
AudioSettings.Reset(AudioSettings.GetConfiguration());
Debug.Log("Pico Spatializer has set rendering quality to: " + renderingQuality);
}
private void OnEnable()
{
if (_instance == null)
{
_instance = this;
AudioSettings.OnAudioConfigurationChanged += OnAudioConfigurationChangedEventHandler;
// Create context
StartInternal(renderingQuality);
Debug.Log("Pico Spatializer Initialized.");
DontDestroyOnLoad(this);
}
else if (_instance != this)
{
Destroy(this);
}
}
private void StartInternal(PXR_Audio.Spatializer.RenderingMode quality)
{
preInitEvent.Invoke();
uuid = GetUuid();
PXR_Audio.Spatializer.Result ret = Result.Success;
audioConfig = AudioSettings.GetConfiguration();
ret = PXR_Audio_Spatializer_Api.CreateContext(
ref context,
quality,
(uint)audioConfig.dspBufferSize,
(uint)audioConfig.sampleRate);
if (ret != PXR_Audio.Spatializer.Result.Success)
{
Debug.LogError("Failed to create context, error code: " + ret);
}
ret = PXR_Audio_Spatializer_Api.InitializeContext(context);
if (ret != PXR_Audio.Spatializer.Result.Success)
{
Debug.LogError("Failed to initialize context, error code: " + ret);
}
// Add all the geometries back
PXR_Audio_Spatializer_SceneGeometry[] geometries = FindObjectsOfType<PXR_Audio_Spatializer_SceneGeometry>();
for (int geoId = 0; geoId < geometries.Length; ++geoId)
{
// For all found geometry and material pair, submit them into Pico spatializer
geometries[geoId].SubmitMeshToContext();
geometries[geoId].SubmitStaticMeshToContext();
if (ret != PXR_Audio.Spatializer.Result.Success)
{
Debug.LogError("Failed to submit geometry #" + geoId + ", error code: " + ret);
}
}
ret = PXR_Audio_Spatializer_Api.CommitScene(context);
if (ret != PXR_Audio.Spatializer.Result.Success)
{
Debug.LogError("Failed to commit scene, error code: " + ret);
}
lateInitEvent.Invoke();
initialized = true;
if (spatializerApiImpl != SpatializerApiImpl.wwise)
{
// Add all the sources back
PXR_Audio_Spatializer_AudioSource[] sources = FindObjectsOfType<PXR_Audio_Spatializer_AudioSource>();
for (int i = 0; i < sources.Length; ++i)
{
sources[i].RegisterInternal();
}
}
// Add listener back
PXR_Audio_Spatializer_AudioListener listener = FindObjectOfType<PXR_Audio_Spatializer_AudioListener>();
listener.RegisterInternal();
}
private void DestroyInternal()
{
initialized = false;
uuid = -1;
if (spatializerApiImpl == SpatializerApiImpl.wwise)
{
PXR_Audio_Spatializer_Api.Destroy(context);
context = IntPtr.Zero;
return;
}
// Wait until all sources and listener's on-going audio DSP process had finished
bool canContinue = true;
do
{
canContinue = true;
PXR_Audio_Spatializer_AudioListener[] listeners = FindObjectsOfType<PXR_Audio_Spatializer_AudioListener>();
foreach (var listener in listeners)
{
if (listener != null && listener.IsAudioDSPInProgress)
{
canContinue = false;
break;
}
}
PXR_Audio_Spatializer_AudioSource[] sources = FindObjectsOfType<PXR_Audio_Spatializer_AudioSource>();
foreach (var source in sources)
{
if (source != null && source.IsAudioDSPInProgress)
{
canContinue = false;
break;
}
}
} while (!canContinue);
PXR_Audio_Spatializer_Api.Destroy(context);
context = IntPtr.Zero;
}
private void OnDisable()
{
if (_instance != null && _instance == this)
{
_instance = null;
// Remove context reset handler when destructing context
// https://docs.microsoft.com/en-us/dotnet/desktop/winforms/controls/how-to-add-an-event-handler?view=netdesktop-6.0
AudioSettings.OnAudioConfigurationChanged -= OnAudioConfigurationChangedEventHandler;
DestroyInternal();
}
}
void Update()
{
if (isSceneDirty)
{
PXR_Audio_Spatializer_Api.CommitScene(context);
isSceneDirty = false;
}
PXR_Audio_Spatializer_Api.UpdateScene(context);
}
void ResetContext(PXR_Audio.Spatializer.RenderingMode quality)
{
DestroyInternal();
StartInternal(quality);
if (spatializerApiImpl == SpatializerApiImpl.wwise)
{
return;
}
// Resume all sources playback
var sources = FindObjectsOfType<PXR_Audio_Spatializer_AudioSource>();
foreach (var source in sources)
{
source.Resume();
}
// Resume all ambisonic sources playback
var ambisonicSources =
FindObjectsOfType<PXR_Audio_Spatializer_AmbisonicSource>();
foreach (var source in ambisonicSources)
{
source.Resume();
}
Debug.Log("Pico Spatializer Context restarted.");
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3083421a52eca4fb186aecb7e4f143a9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,165 @@
using System;
using PXR_Audio.Spatializer;
using UnityEngine;
using Unity.XR.PXR;
using UnityEngine.XR;
public class PXR_Audio_Spatializer_MRSceneGeometryManager : MonoBehaviour
{
public bool meshUpdate = true;
public bool ignoreMeshLabel = false;
private static PXR_Audio_Spatializer_MRSceneGeometryManager _instance = null;
void OnEnable()
{
PXR_SpatialMeshManager.MeshAdded += AddAcousticSceneGeometries;
if (meshUpdate)
PXR_SpatialMeshManager.MeshUpdated += UpdateAcousticSceneGeometries;
Debug.Log("PXR_Audio_Spatializer_MRSceneGeometryManager attached");
}
void OnDisable()
{
PXR_SpatialMeshManager.MeshAdded -= AddAcousticSceneGeometries;
if (meshUpdate)
PXR_SpatialMeshManager.MeshUpdated -= UpdateAcousticSceneGeometries;
Debug.Log("PXR_Audio_Spatializer_MRSceneGeometryManager detached");
}
private void AddAcousticSceneGeometries(Guid guid, GameObject o)
{
// Add acoustic mesh
var acousticMesh = o.GetComponent<PXR_Audio_Spatializer_SceneGeometry>();
var acousticMaterial = o.GetComponent<PXR_Audio_Spatializer_SceneMaterial>();
if (acousticMesh && acousticMaterial)
{
UpdateAcousticSceneGeometries(guid, o);
}
else if (PXR_Plugin.MixedReality.SpatialMeshData.TryGetValue(guid, out var spatialMesh))
{
var acousticMeshNew = o.AddComponent<PXR_Audio_Spatializer_SceneGeometry>();
var acousticMaterialNew = o.GetComponent<PXR_Audio_Spatializer_SceneMaterial>();
UpdateMaterialBasedOnLabel(spatialMesh.labels[0], ref acousticMaterialNew);
acousticMeshNew.UpdateMaterialType(acousticMaterialNew.materialPreset);
acousticMeshNew.UpdateAbsorptionMultiband(acousticMaterialNew.absorption);
acousticMeshNew.UpdateScattering(acousticMaterialNew.scattering);
acousticMeshNew.UpdateTransmission(acousticMaterialNew.transmission);
}
}
private void UpdateAcousticSceneGeometries(Guid guid, GameObject o)
{
var acousticMesh = o.GetComponent<PXR_Audio_Spatializer_SceneGeometry>();
var acousticMaterial = o.GetComponent<PXR_Audio_Spatializer_SceneMaterial>();
if (PXR_Plugin.MixedReality.SpatialMeshData.TryGetValue(guid, out var spatialMesh)
&& acousticMesh && acousticMaterial)
{
UpdateMaterialBasedOnLabel(spatialMesh.labels[0], ref acousticMaterial);
acousticMesh.UpdateMeshInContext();
}
}
private void UpdateMaterialBasedOnLabel(PxrSemanticLabel label, ref PXR_Audio_Spatializer_SceneMaterial material)
{
if (ignoreMeshLabel)
{
material.materialPreset = AcousticsMaterial.Custom;
material.absorption[0] = 0;
material.absorption[1] = 0;
material.absorption[2] = 0;
material.absorption[3] = 0;
material.scattering = 0.2f;
material.transmission = 0.5f;
}
else
{
AcousticsMaterial acousticsMaterial = AcousticsMaterial.AcousticTile;
switch (label)
{
case PxrSemanticLabel.Floor:
acousticsMaterial = AcousticsMaterial.WoodFloor;
break;
case PxrSemanticLabel.Ceiling:
case PxrSemanticLabel.Wall:
acousticsMaterial = AcousticsMaterial.PlasterOnConcreteBlock;
break;
case PxrSemanticLabel.Door:
acousticsMaterial = AcousticsMaterial.WoodThin;
break;
case PxrSemanticLabel.Window:
acousticsMaterial = AcousticsMaterial.Glass;
break;
case PxrSemanticLabel.Opening:
material.materialPreset = AcousticsMaterial.Custom;
material.absorption[0] = 1;
material.absorption[1] = 1;
material.absorption[2] = 1;
material.absorption[3] = 1;
material.scattering = 0;
material.transmission = 1;
break;
case PxrSemanticLabel.Table:
acousticsMaterial = AcousticsMaterial.WoodThick;
break;
case PxrSemanticLabel.Sofa:
acousticsMaterial = AcousticsMaterial.AcousticTile;
break;
case PxrSemanticLabel.Chair:
acousticsMaterial = AcousticsMaterial.WoodThin;
break;
case PxrSemanticLabel.Human:
acousticsMaterial = AcousticsMaterial.AcousticTile;
break;
case PxrSemanticLabel.VirtualWall:
acousticsMaterial = AcousticsMaterial.AcousticTile;
break;
case PxrSemanticLabel.Curtain:
acousticsMaterial = AcousticsMaterial.Curtain;
break;
case PxrSemanticLabel.Cabinet:
acousticsMaterial = AcousticsMaterial.WoodThick;
break;
case PxrSemanticLabel.Bed:
acousticsMaterial = AcousticsMaterial.AcousticTile;
break;
case PxrSemanticLabel.Plant:
acousticsMaterial = AcousticsMaterial.Foliage;
break;
case PxrSemanticLabel.Screen:
acousticsMaterial = AcousticsMaterial.Glass;
break;
case PxrSemanticLabel.Refrigerator:
case PxrSemanticLabel.WashingMachine:
case PxrSemanticLabel.AirConditioner:
acousticsMaterial = AcousticsMaterial.PlasterOnConcreteBlock;
break;
case PxrSemanticLabel.Lamp:
acousticsMaterial = AcousticsMaterial.WoodThin;
break;
case PxrSemanticLabel.WallArt:
acousticsMaterial = AcousticsMaterial.PlasterOnConcreteBlock;
break;
default:
acousticsMaterial = AcousticsMaterial.AcousticTile;
break;
}
UpdateMaterialBasedOnAcousticLabel(acousticsMaterial, ref material);
}
}
private void UpdateMaterialBasedOnAcousticLabel(AcousticsMaterial acousticLabel,
ref PXR_Audio_Spatializer_SceneMaterial material)
{
if (acousticLabel == AcousticsMaterial.Custom)
return;
if (PXR_Audio_Spatializer_Context.Instance == null)
return;
material.materialPreset = AcousticsMaterial.Custom;
PXR_Audio_Spatializer_Context.Instance.GetAbsorptionFactors(acousticLabel, material.absorption);
PXR_Audio_Spatializer_Context.Instance.GetScatteringFactors(acousticLabel, ref material.scattering);
PXR_Audio_Spatializer_Context.Instance.GetTransmissionFactors(acousticLabel, ref material.transmission);
material.transmission = Math.Min(material.transmission + 0.5f, 1.0f);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8bc043bac160f4079b5ff39f12a5cbcb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,478 @@
// Copyright © 2015-2022 Pico Technology Co., Ltd. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using PXR_Audio.Spatializer;
using UnityEditor;
using UnityEngine;
[RequireComponent(typeof(PXR_Audio_Spatializer_SceneMaterial))]
public class PXR_Audio_Spatializer_SceneGeometry : MonoBehaviour
{
[Tooltip("Whether include meshes in children game objects as the shape of this acoustic geometry.")]
[SerializeField] private bool includeChildren = false;
[Tooltip("Whether visualize meshes in current scene that are included as the shape of this acoustic geometry.\n" +
" - non-static meshes are visualized using white wire frames;\n" +
" - static meshes are visualized using green wire frames.")]
[SerializeField] private bool visualizeMeshInEditor = false;
[Tooltip("Baked static mesh used as the shape of this acoustic geometry.")]
[SerializeField] private Mesh bakedStaticMesh;
#region EDITOR-ONLY SerializedFields
#if UNITY_EDITOR
[SerializeField] private LayerMask meshBakingLayerMask = ~0;
[SerializeField, HideInInspector] private string currentBakedStaticMeshAssetPath = null;
#endif
#endregion
public bool isStaticMeshBaked => bakedStaticMesh != null;
private int geometryId = -1;
public int GeometryId
{
get => geometryId;
}
private int staticGeometryID = -1;
public int StaticGeometryId => staticGeometryID;
private PXR_Audio_Spatializer_SceneMaterial material;
public PXR_Audio_Spatializer_SceneMaterial Material
{
get
{
if (material == null)
{
material = GetComponent<PXR_Audio_Spatializer_SceneMaterial>();
}
return material;
}
}
private MeshConfig meshConfig;
private uint propertyMask = 0;
private int currentContextUuid = -2;
private void OnEnable()
{
if (PXR_Audio_Spatializer_Context.Instance == null) return;
// If geometries are added after context is initialized
if (PXR_Audio_Spatializer_Context.Instance.UUID != currentContextUuid)
{
var ret = SubmitMeshToContext();
var staticRet = SubmitStaticMeshToContext();
}
else
{
meshConfig = new MeshConfig(true, Material, transform.localToWorldMatrix);
if (geometryId >= 0)
PXR_Audio_Spatializer_Context.Instance.SetMeshConfig(geometryId, ref meshConfig,
(uint)MeshProperty.All);
if (staticGeometryID >= 0)
PXR_Audio_Spatializer_Context.Instance.SetMeshConfig(staticGeometryID, ref meshConfig,
(uint)MeshProperty.All);
}
}
private void OnDisable()
{
if (PXR_Audio_Spatializer_Context.Instance == null) return;
if (PXR_Audio_Spatializer_Context.Instance.UUID != currentContextUuid) return;
meshConfig.enabled = false;
if (geometryId >= 0)
PXR_Audio_Spatializer_Context.Instance.SetMeshConfig(geometryId, ref meshConfig,
(uint)MeshProperty.Enabled);
if (staticGeometryID >= 0)
PXR_Audio_Spatializer_Context.Instance.SetMeshConfig(staticGeometryID, ref meshConfig,
(uint)MeshProperty.Enabled);
}
private void OnDestroy()
{
RemoveMeshFromContext();
}
private void RemoveMeshFromContext()
{
if (PXR_Audio_Spatializer_Context.Instance == null) return;
if (PXR_Audio_Spatializer_Context.Instance.UUID != currentContextUuid) return;
if (geometryId >= 0)
{
PXR_Audio_Spatializer_Context.Instance.RemoveMesh(geometryId);
Debug.LogFormat("Removed geometry #{0}, gameObject name is {1}", geometryId.ToString(),
name);
}
if (staticGeometryID >= 0)
{
PXR_Audio_Spatializer_Context.Instance.RemoveMesh(staticGeometryID);
Debug.LogFormat("Removed static geometry #{0}, gameObject name is {1}", staticGeometryID.ToString(),
name);
}
}
private void Update()
{
if (PXR_Audio_Spatializer_Context.Instance == null) return;
// // If geometries are added after context is initialized
// if (PXR_Audio_Spatializer_Context.Instance.UUID != currentContextUuid)
// {
// var ret = SubmitMeshToContext();
// var staticRet = SubmitStaticMeshToContext();
// }
if (transform.hasChanged)
{
meshConfig.SetTransformMatrix4x4(transform.localToWorldMatrix);
propertyMask |= (uint)MeshProperty.ToWorldTransform;
transform.hasChanged = false;
}
if (propertyMask > 0)
{
if (geometryId >= 0)
PXR_Audio_Spatializer_Context.Instance.SetMeshConfig(geometryId, ref meshConfig,
propertyMask);
if (staticGeometryID >= 0)
PXR_Audio_Spatializer_Context.Instance.SetMeshConfig(staticGeometryID, ref meshConfig,
propertyMask);
propertyMask = 0;
}
}
public void UpdateAbsorptionMultiband(float[] absorptions)
{
meshConfig.materialType = AcousticsMaterial.Custom;
meshConfig.absorption.v0 = Material.absorption[0] = absorptions[0];
meshConfig.absorption.v1 = Material.absorption[1] = absorptions[1];
meshConfig.absorption.v2 = Material.absorption[2] = absorptions[2];
meshConfig.absorption.v3 = Material.absorption[3] = absorptions[3];
propertyMask |= (uint)MeshProperty.Material | (uint)MeshProperty.Absorption;
}
public void UpdateScattering(float scattering)
{
meshConfig.materialType = AcousticsMaterial.Custom;
meshConfig.scattering = Material.scattering = scattering;
propertyMask |= (uint)MeshProperty.Material | (uint)MeshProperty.Scattering;
}
public void UpdateTransmission(float transmission)
{
meshConfig.materialType = AcousticsMaterial.Custom;
meshConfig.transmission = Material.transmission = transmission;
propertyMask |= (uint)MeshProperty.Material | (uint)MeshProperty.Transmission;
}
public void UpdateMaterialType(PXR_Audio.Spatializer.AcousticsMaterial materialType)
{
meshConfig.materialType = materialType;
propertyMask |= (uint)MeshProperty.Material;
}
private void GetAllMeshFilter(Transform transform, bool includeChildren, List<MeshFilter> meshFilterList,
bool isStatic, LayerMask layerMask)
{
if (includeChildren)
{
int childCount = transform.childCount;
for (int i = 0; i < childCount; i++)
{
var childTransform = transform.GetChild(i);
if (childTransform.GetComponent<PXR_Audio_Spatializer_SceneGeometry>() == null)
{
GetAllMeshFilter(childTransform.transform, includeChildren, meshFilterList, isStatic, layerMask);
}
}
}
// Gather this mesh only when
// 1. Its isStatic flag is equal to our requirement
// 2. Its layer belongs to layerMask set
if (((1 << transform.gameObject.layer) & layerMask) != 0)
{
var meshFilterArray = transform.GetComponents<MeshFilter>();
// cases we don't add to mesh filter list
// 1. meshFilter.sharedmesh == null
// 2. meshFilter.sharedmesh.isReadable == false
if (meshFilterArray != null)
{
foreach (var meshFilter in meshFilterArray)
{
if (meshFilter != null && meshFilter.sharedMesh != null &&
(
(isStatic && (transform.gameObject.isStatic || !meshFilter.sharedMesh.isReadable)) ||
(!isStatic && (!transform.gameObject.isStatic && meshFilter.sharedMesh.isReadable))
))
{
meshFilterList.Add(meshFilter);
}
}
}
}
}
private static Mesh CombineMeshes(List<MeshFilter> meshFilterList, Transform rootTransform)
{
if (meshFilterList.Count == 1)
return meshFilterList[0].mesh;
Mesh combinedMesh = new Mesh
{
name = "combined meshes",
indexFormat = UnityEngine.Rendering.IndexFormat.UInt32
};
var combinedVertices = Array.Empty<Vector3>();
var combinedIndices = Array.Empty<int>();
// Accumulate combined vertices buffer size
foreach (var meshFilter in meshFilterList)
{
int vertexOffset = combinedVertices.Length;
combinedVertices = combinedVertices.Concat(meshFilter.sharedMesh.vertices).ToArray();
int vertexSegmentEnd = combinedVertices.Length;
var toWorld = rootTransform.worldToLocalMatrix *
meshFilter.transform.localToWorldMatrix;
for (int i = vertexOffset; i < vertexSegmentEnd; ++i)
{
combinedVertices[i] = toWorld.MultiplyPoint3x4(combinedVertices[i]);
}
var trianglesStartIdx = combinedIndices.Length;
combinedIndices = combinedIndices.Concat(meshFilter.sharedMesh.triangles).ToArray();
var trianglesEndIdx = combinedIndices.Length;
for (var i = trianglesStartIdx; i < trianglesEndIdx; ++i)
{
combinedIndices[i] += vertexOffset;
}
}
combinedMesh.vertices = combinedVertices;
combinedMesh.triangles = combinedIndices;
combinedMesh.RecalculateNormals();
return combinedMesh;
}
private static float[] FlattenVerticesBuffer(Vector3[] verticesBuffer)
{
float[] vertices = new float[verticesBuffer.Length * 3];
int index = 0;
foreach (Vector3 vertex in verticesBuffer)
{
vertices[index++] = vertex.x;
vertices[index++] = vertex.y;
vertices[index++] = vertex.z;
}
return vertices;
}
/// <summary>
/// Submit non-static mesh of this geometry and its material into spatializer engine context
/// </summary>
/// <returns>Result of static mesh submission</returns>
public PXR_Audio.Spatializer.Result SubmitMeshToContext(bool showLog = true)
{
// find all meshes
var meshFilterList = new List<MeshFilter>();
GetAllMeshFilter(transform, includeChildren, meshFilterList, false, ~0);
// Combine all meshes
Mesh combinedMesh = CombineMeshes(meshFilterList, transform);
// flatten vertices buffer into a float array
float[] vertices = FlattenVerticesBuffer(combinedMesh.vertices);
meshConfig = new MeshConfig(enabled, Material, transform.localToWorldMatrix);
// Submit all meshes
PXR_Audio.Spatializer.Result result = PXR_Audio_Spatializer_Context.Instance.SubmitMeshWithConfig(
vertices, vertices.Length / 3,
combinedMesh.triangles, combinedMesh.triangles.Length / 3,
ref meshConfig, ref geometryId);
if (showLog)
{
if (result != Result.Success)
Debug.LogError("Failed to submit audio mesh: " + gameObject.name + ", Error code is: " + result);
else
Debug.LogFormat("Submitted geometry #{0}, gameObject name is {1}", geometryId.ToString(),
name);
}
if (result == Result.Success)
currentContextUuid = PXR_Audio_Spatializer_Context.Instance.UUID;
return result;
}
/// <summary>
/// Submit static mesh of this geometry and its material into spatializer engine context
/// </summary>
/// <returns>Result of static mesh submission</returns>
public PXR_Audio.Spatializer.Result SubmitStaticMeshToContext(bool showLog = true)
{
PXR_Audio.Spatializer.Result result = Result.Success;
if (bakedStaticMesh != null)
{
float[] tempVertices = FlattenVerticesBuffer(bakedStaticMesh.vertices);
meshConfig = new MeshConfig(enabled, Material, transform.localToWorldMatrix);
result = PXR_Audio_Spatializer_Context.Instance.SubmitMeshWithConfig(tempVertices,
bakedStaticMesh.vertices.Length, bakedStaticMesh.triangles,
bakedStaticMesh.triangles.Length / 3, ref meshConfig,
ref staticGeometryID);
if (showLog)
{
if (result != Result.Success)
Debug.LogError("Failed to submit static audio mesh: " + gameObject.name + ", Error code is: " +
result);
else
Debug.LogFormat("Submitted static geometry #{0}, gameObject name is {1}", staticGeometryID.ToString(),
name);
}
}
if (result == Result.Success)
currentContextUuid = PXR_Audio_Spatializer_Context.Instance.UUID;
return result;
}
public Result UpdateMeshInContext()
{
// find all meshes
var meshFilterList = new List<MeshFilter>();
GetAllMeshFilter(transform, includeChildren, meshFilterList, false, ~0);
// Combine all meshes
Mesh combinedMesh = CombineMeshes(meshFilterList, transform);
// flatten vertices buffer into a float array
float[] vertices = FlattenVerticesBuffer(combinedMesh.vertices);
meshConfig = new MeshConfig(enabled, Material, transform.localToWorldMatrix);
// Submit all meshes
Result result = PXR_Audio_Spatializer_Context.Instance.UpdateMesh(geometryId,
vertices, vertices.Length / 3,
combinedMesh.triangles, combinedMesh.triangles.Length / 3,
ref meshConfig, ref geometryId);
if (result == Result.Success)
currentContextUuid = PXR_Audio_Spatializer_Context.Instance.UUID;
return result;
}
#if UNITY_EDITOR
public int BakeStaticMesh(LayerMask layerMask)
{
List<MeshFilter> meshList = new List<MeshFilter>();
GetAllMeshFilter(transform, includeChildren, meshList, true, meshBakingLayerMask);
SerializedObject serializedObject = new SerializedObject(this);
if (meshList.Count == 0)
{
bakedStaticMesh = null;
}
else
{
bakedStaticMesh = CombineMeshes(meshList, transform);
bakedStaticMesh.name = "baked mesh for ygg";
}
serializedObject.FindProperty("bakedStaticMesh").objectReferenceValue = bakedStaticMesh;
if (bakedStaticMesh != null)
{
System.IO.Directory.CreateDirectory("Assets/Resources/PxrAudioSpatializerBakedSceneMeshes/");
if (!string.IsNullOrEmpty(currentBakedStaticMeshAssetPath))
{
AssetDatabase.DeleteAsset(currentBakedStaticMeshAssetPath);
}
currentBakedStaticMeshAssetPath = "Assets/Resources/PxrAudioSpatializerBakedSceneMeshes/" + name + "_" +
GetInstanceID() + "_" +
System.DateTime.UtcNow.ToBinary() + ".yggmesh";
serializedObject.FindProperty("currentBakedStaticMeshAssetPath").stringValue =
currentBakedStaticMeshAssetPath;
AssetDatabase.CreateAsset(bakedStaticMesh, currentBakedStaticMeshAssetPath);
AssetDatabase.SaveAssets();
}
serializedObject.ApplyModifiedProperties();
return meshList.Count;
}
public void ClearBakeStaticMesh()
{
SerializedObject serializedObject = new SerializedObject(this);
bakedStaticMesh = null;
serializedObject.FindProperty("bakedStaticMesh").objectReferenceValue = null;
if (!string.IsNullOrEmpty(currentBakedStaticMeshAssetPath))
{
AssetDatabase.DeleteAsset(currentBakedStaticMeshAssetPath);
currentBakedStaticMeshAssetPath = null;
serializedObject.FindProperty("currentBakedStaticMeshAssetPath").stringValue =
currentBakedStaticMeshAssetPath;
}
serializedObject.ApplyModifiedProperties();
}
#endif
public void OnDrawGizmos()
{
if (visualizeMeshInEditor)
{
// Visualize non-static meshes
// find all MeshFilter
var meshFilterList = new List<MeshFilter>();
GetAllMeshFilter(transform, includeChildren, meshFilterList, false, ~0);
for (int i = 0; i < meshFilterList.Count; i++)
{
var mesh = meshFilterList[i].sharedMesh;
var transform = meshFilterList[i].transform;
Gizmos.DrawWireMesh(mesh,
transform.position, transform.rotation, transform.localScale);
}
// Visualize baked static meshes
if (isStaticMeshBaked)
{
Color colorBackUp = Gizmos.color;
Color c;
c.r = 0.0f;
c.g = 0.7f;
c.b = 0.0f;
c.a = 1.0f;
Gizmos.color = c;
var gizmosMatrixBackup = Gizmos.matrix;
Gizmos.matrix = transform.localToWorldMatrix;
Gizmos.DrawWireMesh(bakedStaticMesh);
Gizmos.color = colorBackUp;
Gizmos.matrix = gizmosMatrixBackup;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 555afd84b04c3471788b8eada321c090
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,84 @@
// Copyright © 2015-2022 Pico Technology Co., Ltd. All Rights Reserved.
using UnityEngine;
public class PXR_Audio_Spatializer_SceneMaterial : MonoBehaviour
{
[Tooltip("Acoustic material preset corresponding to real-world material types. " +
"Absorption, scattering, and transmission will be automatically set based on the physical nature of the selected material.\n" +
"Deviating acoustic parameters away from preset values will automatically set this field to 'Custom'")]
[SerializeField]
public PXR_Audio.Spatializer.AcousticsMaterial
materialPreset = PXR_Audio.Spatializer.AcousticsMaterial.AcousticTile;
private PXR_Audio.Spatializer.AcousticsMaterial lastMaterialPreset =
PXR_Audio.Spatializer.AcousticsMaterial.AcousticTile;
[SerializeField] [Range(0.0f, 1.0f)]
public float[] absorption = new float[4];
[Tooltip("Ratio of sound energy get scattered by each reflection.\n" +
" - Low scattering will result in a more echoic sound\n" +
" - high scattering will result in a more reverberant sound")]
[SerializeField]
[Range(0.0f, 1.0f)]
public float scattering = 0.0f;
[Tooltip("Ratio of sound energy get transmitted through this material.")] [SerializeField] [Range(0.0f, 1.0f)]
public float transmission = 0.0f;
private float[] absorptionForValidation = new float[4];
private float scatteringForValidation = 0.0f;
private float transmissionForValidation = 0.0f;
private static PXR_Audio_Spatializer_Context spatialAudioContextRef;
private void OnValidate()
{
if (spatialAudioContextRef == null)
spatialAudioContextRef = FindObjectOfType<PXR_Audio_Spatializer_Context>();
if (lastMaterialPreset != materialPreset) // material_preset is changed
{
if (materialPreset != PXR_Audio.Spatializer.AcousticsMaterial.Custom)
{
if (spatialAudioContextRef != null)
{
spatialAudioContextRef.PXR_Audio_Spatializer_Api.GetAbsorptionFactor(materialPreset,
absorption);
spatialAudioContextRef.PXR_Audio_Spatializer_Api.GetScatteringFactor(materialPreset,
ref scattering);
spatialAudioContextRef.PXR_Audio_Spatializer_Api.GetTransmissionFactor(
materialPreset, ref transmission);
lastMaterialPreset = materialPreset;
}
}
else
{
lastMaterialPreset = materialPreset;
}
}
else if (materialPreset != PXR_Audio.Spatializer.AcousticsMaterial.Custom &&
spatialAudioContextRef !=
null) // material_preset is not changed, but acoustic properties are changed manually
{
// Check if actual material parameters are different from current materialPreset
spatialAudioContextRef.PXR_Audio_Spatializer_Api.GetAbsorptionFactor(materialPreset,
absorptionForValidation);
spatialAudioContextRef.PXR_Audio_Spatializer_Api.GetScatteringFactor(materialPreset,
ref scatteringForValidation);
spatialAudioContextRef.PXR_Audio_Spatializer_Api.GetTransmissionFactor(materialPreset,
ref transmissionForValidation);
if (Mathf.Abs(absorption[0] - absorptionForValidation[0]) > float.Epsilon ||
Mathf.Abs(absorption[1] - absorptionForValidation[1]) > float.Epsilon ||
Mathf.Abs(absorption[2] - absorptionForValidation[2]) > float.Epsilon ||
Mathf.Abs(absorption[3] - absorptionForValidation[3]) > float.Epsilon ||
Mathf.Abs(scattering - scatteringForValidation) > float.Epsilon ||
Mathf.Abs(transmission - transmissionForValidation) > float.Epsilon)
{
materialPreset = PXR_Audio.Spatializer.AcousticsMaterial.Custom;
lastMaterialPreset = PXR_Audio.Spatializer.AcousticsMaterial.Custom;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 22d37993d4d3e4235b77205c1aa2ff46
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,364 @@
// Copyright © 2015-2022 Pico Technology Co., Ltd. All Rights Reserved.
using System;
using System.Runtime.InteropServices;
using UnityEngine;
namespace PXR_Audio
{
namespace Spatializer
{
public enum Result
{
Error = -1,
Success = 0,
SourceNotFound = -1001,
SourceDataNotFound = -1002,
SceneNotFound = -1003,
SceneMeshNotFound = -1004,
IllegalValue = -1005,
ContextNotCreated = -1006,
ContextNotReady = -1007,
ContextRepeatedInitialization = -1008,
EnvironmentalAcousticsDisabled = -1009,
ApiDisabled = -1010,
///< API is disabled in current build
SourceInuse = -1011,
};
public enum PlaybackMode
{
BinauralOut,
LoudspeakersOut,
};
public enum LateReverbUpdatingMode
{
RealtimeLateReverb = 0,
BakedLateReverb = 1,
SharedSpectralLateReverb = 2,
};
public enum LateReverbRenderingMode
{
IrLateReverb = 0,
SpectralLateReverb = 1,
};
public enum RenderingMode
{
LowQuality = 0, // 1st order ambisonic
MediumQuality = 1, // 3rd order ambisonic
HighQuality = 2, // 5th order ambisonic
AmbisonicFirstOrder,
AmbisonicSecondOrder,
AmbisonicThirdOrder,
AmbisonicFourthOrder,
AmbisonicFifthOrder,
AmbisonicSixthOrder,
AmbisonicSeventhOrder,
};
public enum SourceMode
{
Spatialize = 0,
Bypass = 1,
};
public enum IRUpdateMethod
{
PerPartitionSwapping = 0,
InterPartitionLinearCrossFade = 1,
InterPartitionPowerComplementaryCrossFade = 2
};
public enum AcousticsMaterial
{
AcousticTile,
Brick,
BrickPainted,
Carpet,
CarpetHeavy,
CarpetHeavyPadded,
CeramicTile,
Concrete,
ConcreteRough,
ConcreteBlock,
ConcreteBlockPainted,
Curtain,
Foliage,
Glass,
GlassHeavy,
Grass,
Gravel,
GypsumBoard,
PlasterOnBrick,
PlasterOnConcreteBlock,
Soil,
SoundProof,
Snow,
Steel,
Water,
WoodThin,
WoodThick,
WoodFloor,
WoodOnConcrete,
Custom
};
public enum AmbisonicNormalizationType
{
SN3D,
N3D
};
public enum SourceAttenuationMode
{
None = 0, // 引擎不依据距离计算衰减
Fixed = 1, // 与None完全一致
InverseSquare = 2, // 引擎 InverseSquare Law 计算距离衰减
Customized = 3, // 依据外部传入的 Callback 计算距离衰减
};
public enum SpatializerApiImpl
{
unity,
wwise,
}
public delegate float DistanceAttenuationCallback(float distance, float rangeMin, float rangeMax);
[StructLayout(LayoutKind.Sequential)]
public struct NativeVector3f
{
public float x; //float[3]
public float y;
public float z;
}
[StructLayout(LayoutKind.Sequential)]
public struct SourceConfig
{
[MarshalAs(UnmanagedType.U4)] public SourceMode mode;
public NativeVector3f position;
public NativeVector3f front;
public NativeVector3f up;
public float directivityAlpha; // Weighting balance between figure of eight pattern and circular pattern for
// source emission in range [0, 1].
// A value of 0 results in a circular pattern.
// A value of 0.5 results in a cardioid pattern.
// A value of 1 results in a figure of eight pattern.
public float
directivityOrder; // Order applied to computed directivity. Higher values will result in narrower and
public float radius;
// sharper directivity patterns. Range [1, inf).
[MarshalAs(UnmanagedType.U1)] public bool useDirectPathSpread;
public float directPathSpread; // Alternatively, we could use spread param directly.
// This is useful when audio middleware specifies spread value by itself.
public float sourceGain; // Master gain of sound source.
public float reflectionGain; // Reflection gain relative to default (master gain).
[MarshalAs(UnmanagedType.U1)] public bool enableDoppler;
[MarshalAs(UnmanagedType.U4)] public SourceAttenuationMode attenuationMode;
public IntPtr
directDistanceAttenuationCallback; // Native function pointer of direct sound distance attenuation
public IntPtr indirectDistanceAttenuationCallback;
// Attenuation range
public float minAttenuationDistance; // When distance < minAttenuationDistance, no attenuation.
public float
maxAttenuationDistance; // When distance > maxAttenuationDistance, attenuation = AttenuationFunc(range_max).
public SourceConfig(SourceMode inMode)
{
mode = inMode;
position.x = 0.0f;
position.y = 0.0f;
position.z = 0.0f;
front.x = 0.0f;
front.y = 0.0f;
front.z = -1.0f;
up.x = 0.0f;
up.y = 1.0f;
up.z = 0.0f;
radius = 0.1f;
directivityAlpha = 0.0f;
directivityOrder = 1.0f;
useDirectPathSpread = false;
directPathSpread = 0.0f;
sourceGain = 1.0f;
reflectionGain = 1.0f;
enableDoppler = false;
attenuationMode = SourceAttenuationMode.InverseSquare;
directDistanceAttenuationCallback = IntPtr.Zero;
indirectDistanceAttenuationCallback = IntPtr.Zero;
minAttenuationDistance = 0.25f;
maxAttenuationDistance = 250f;
}
}
public enum SourceProperty : uint
{
Mode = 1u,
Position = (1u << 1),
///< float[3]
Orientation = (1u << 2),
///< float[6]
Directivity = (1u << 3),
///< float[2], directivity alpha and directivity order
VolumetricRadius = (1u << 4),
VolumetricSpread = (1u << 5),
SourceGain = (1u << 6),
ReflectionGain = (1u << 7),
DopplerOnOff = (1u << 8),
AttenuationMode = (1u << 9),
///< Only after setting AttenuationMode will AttenuationCallback be applied
DirectAttenuationCallback = (1u << 10),
IndirectAttenuationCallback = (1u << 11),
RangeMin = (1u << 12),
RangeMax = (1u << 13),
All = ~0u,
None = 0u,
}
[StructLayout(LayoutKind.Sequential)]
public struct NativeVector4f
{
public float v0; //float[4]
public float v1;
public float v2;
public float v3;
}
[StructLayout(LayoutKind.Sequential)]
public struct NativeMatrix4x4f
{
public float v0; //float[16]
public float v1;
public float v2;
public float v3;
public float v4; //float[16]
public float v5;
public float v6;
public float v7;
public float v8; //float[16]
public float v9;
public float v10;
public float v11;
public float v12;
public float v13;
public float v14;
public float v15;
}
[StructLayout(LayoutKind.Sequential)]
public struct MeshConfig
{
[MarshalAs(UnmanagedType.U1)] public bool enabled;
[MarshalAs(UnmanagedType.U4)] public AcousticsMaterial materialType;
///< Material preset; If this equal to YGG_MATERIAL_Custom, the absorption,
///< scattering, and transmission coefficients below will be used
public NativeVector4f absorption;
///< Absorption of 4 bands
public float scattering;
///< Wide-band scattering
public float transmission;
///< Wide-band transmission
public NativeMatrix4x4f toWorldTransform;
///< Column-major 4x4 to-world transform matrix of this mesh, which
///< describes the position, rotation, and scale of it's default to
///< identity matrix, which represents a scene mesh positioned at
///< world origin, with no rotation and no scaling what's so ever
public MeshConfig(bool enabled, PXR_Audio_Spatializer_SceneMaterial material, Matrix4x4 toWorldMatrix4X4)
{
this.enabled = enabled;
materialType = material.materialPreset;
absorption.v0 = material.absorption[0];
absorption.v1 = material.absorption[1];
absorption.v2 = material.absorption[2];
absorption.v3 = material.absorption[3];
scattering = material.scattering;
transmission = material.transmission;
toWorldTransform.v0 = toWorldMatrix4X4[0];
toWorldTransform.v1 = toWorldMatrix4X4[1];
toWorldTransform.v2 = -toWorldMatrix4X4[2];
toWorldTransform.v3 = toWorldMatrix4X4[3];
toWorldTransform.v4 = toWorldMatrix4X4[4];
toWorldTransform.v5 = toWorldMatrix4X4[5];
toWorldTransform.v6 = -toWorldMatrix4X4[6];
toWorldTransform.v7 = toWorldMatrix4X4[7];
toWorldTransform.v8 = toWorldMatrix4X4[8];
toWorldTransform.v9 = toWorldMatrix4X4[9];
toWorldTransform.v10 = -toWorldMatrix4X4[10];
toWorldTransform.v11 = toWorldMatrix4X4[11];
toWorldTransform.v12 = toWorldMatrix4X4[12];
toWorldTransform.v13 = toWorldMatrix4X4[13];
toWorldTransform.v14 = -toWorldMatrix4X4[14];
toWorldTransform.v15 = toWorldMatrix4X4[15];
}
public void SetMaterial(PXR_Audio_Spatializer_SceneMaterial material)
{
materialType = material.materialPreset;
absorption.v0 = material.absorption[0];
absorption.v1 = material.absorption[1];
absorption.v2 = material.absorption[2];
absorption.v3 = material.absorption[3];
scattering = material.scattering;
transmission = material.transmission;
}
public void SetTransformMatrix4x4(Matrix4x4 toWorldMatrix4X4)
{
toWorldTransform.v0 = toWorldMatrix4X4[0];
toWorldTransform.v1 = toWorldMatrix4X4[1];
toWorldTransform.v2 = -toWorldMatrix4X4[2];
toWorldTransform.v3 = toWorldMatrix4X4[3];
toWorldTransform.v4 = toWorldMatrix4X4[4];
toWorldTransform.v5 = toWorldMatrix4X4[5];
toWorldTransform.v6 = -toWorldMatrix4X4[6];
toWorldTransform.v7 = toWorldMatrix4X4[7];
toWorldTransform.v8 = toWorldMatrix4X4[8];
toWorldTransform.v9 = toWorldMatrix4X4[9];
toWorldTransform.v10 = -toWorldMatrix4X4[10];
toWorldTransform.v11 = toWorldMatrix4X4[11];
toWorldTransform.v12 = toWorldMatrix4X4[12];
toWorldTransform.v13 = toWorldMatrix4X4[13];
toWorldTransform.v14 = -toWorldMatrix4X4[14];
toWorldTransform.v15 = toWorldMatrix4X4[15];
}
}
enum MeshProperty : uint
{
Enabled = 1u,
Material = (1u << 1),
Absorption = (1u << 2),
Scattering = (1u << 3),
Transmission = (1u << 4),
ToWorldTransform = (1u << 5),
All = ~0u,
None = 0u,
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4fb395b406d5b459293182f1bccadd42
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: