Initialer Upload neues Unity-Projekt

This commit is contained in:
Daniel Ocks
2025-07-21 09:11:14 +02:00
commit eeca72985b
14558 changed files with 1508140 additions and 0 deletions

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f4a590547ee3d3348ac53ce36798e26b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 29ab7e927676ef74a93aeedf4146f1ac
folderAsset: yes
timeCreated: 1470780399
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 180c8a539f95cce428f820b0ba392c1f
folderAsset: yes
timeCreated: 1468506676
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,22 @@
fileFormatVersion: 2
guid: 485ac48a563e2bf44bb4de7ead5a1f68
timeCreated: 1461716881
licenseType: Store
AudioImporter:
serializedVersion: 6
defaultSettings:
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 1
quality: 1
conversionMode: 0
platformSettingOverrides: {}
forceToMono: 0
normalize: 1
preloadAudioData: 1
loadInBackground: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,22 @@
fileFormatVersion: 2
guid: d3994560fb567c34a821fd9355c10eef
timeCreated: 1461716882
licenseType: Store
AudioImporter:
serializedVersion: 6
defaultSettings:
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 1
quality: 1
conversionMode: 0
platformSettingOverrides: {}
forceToMono: 0
normalize: 1
preloadAudioData: 1
loadInBackground: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,22 @@
fileFormatVersion: 2
guid: 7000b4d67a1320940b363f3cf891dfff
timeCreated: 1461716881
licenseType: Store
AudioImporter:
serializedVersion: 6
defaultSettings:
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 1
quality: 1
conversionMode: 0
platformSettingOverrides: {}
forceToMono: 0
normalize: 1
preloadAudioData: 1
loadInBackground: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,22 @@
fileFormatVersion: 2
guid: 8198ccc08475a764daaf226b841a55f1
timeCreated: 1461716882
licenseType: Store
AudioImporter:
serializedVersion: 6
defaultSettings:
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 1
quality: 1
conversionMode: 0
platformSettingOverrides: {}
forceToMono: 0
normalize: 1
preloadAudioData: 1
loadInBackground: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,26 @@
{
"name": "Oculus.AudioManager",
"references": [
"Oculus.Spatializer"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [
{
"name": "com.unity.xr.management",
"expression": "",
"define": "USING_XR_MANAGEMENT"
},
{
"name": "com.unity.xr.oculus",
"expression": "",
"define": "USING_XR_SDK_OCULUS"
}
],
"noEngineReferences": false
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 21b850ac1b5452b46a20c637f002dbf9
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 6d597fe114807b54a99033a3dae9ce0d
folderAsset: yes
timeCreated: 1470780399
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 7ee6cbebddf12044c95f9d6eb6e40823
folderAsset: yes
timeCreated: 1470780399
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,36 @@
using UnityEngine;
using System.Collections;
namespace OVR
{
public class TestScript : MonoBehaviour {
[InspectorNote( "Sound Setup", "Press '1' to play testSound1 and '2' to play testSound2")]
public SoundFXRef testSound1;
public SoundFXRef testSound2;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update ()
{
// use attached game object location
if ( Input.GetKeyDown( KeyCode.Alpha1 ) )
{
testSound1.PlaySoundAt( transform.position );
}
// hard code information
if ( Input.GetKeyDown( KeyCode.Alpha2 ) ) {
testSound2.PlaySoundAt( new Vector3( 5.0f, 0.0f, 0.0f ) );
}
}
}
} // namespace OVR

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: cfae243ecd01edd49bd439c56a8b18cf
timeCreated: 1468506975
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

BIN
Assets/Oculus/AudioManager/Scenes/Test.unity (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 600e5d49b0a68254286400e32c53addc
timeCreated: 1468506358
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 25be027379f7fce4d97ba2bcaf313019
folderAsset: yes
timeCreated: 1470780399
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: b8c0d722519c64144a78f8fc99cd40b5
folderAsset: yes
timeCreated: 1468505670
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,118 @@
using UnityEngine;
using System.Collections;
namespace OVR
{
/*
-----------------------
AmbienceEmitter()
-----------------------
*/
public class AmbienceEmitter : MonoBehaviour {
public SoundFXRef[] ambientSounds = new SoundFXRef[0];
public bool autoActivate = true;
[Tooltip("Automatically play the sound randomly again when checked. Should be OFF for looping sounds")]
public bool autoRetrigger = true;
[MinMax( 2.0f, 4.0f, 0.1f, 10.0f )]
public Vector2 randomRetriggerDelaySecs = new Vector2( 2.0f, 4.0f );
[Tooltip( "If defined, the sounds will randomly play from these transform positions, otherwise the sound will play from this transform" )]
public Transform[] playPositions = new Transform[0];
private bool activated = false;
private int playingIdx = -1;
private float nextPlayTime = 0.0f;
private float fadeTime = 0.25f;
private int lastPosIdx = -1;
/*
-----------------------
Awake()
-----------------------
*/
void Awake() {
if ( autoActivate ) {
activated = true;
nextPlayTime = Time.time + Random.Range( randomRetriggerDelaySecs.x, randomRetriggerDelaySecs.y );
}
// verify all the play positions are valid
foreach ( Transform t in playPositions ) {
if ( t == null ) {
Debug.LogWarning( "[AmbienceEmitter] Invalid play positions in " + name );
playPositions = new Transform[0];
break;
}
}
}
/*
-----------------------
Update()
-----------------------
*/
void Update() {
if ( activated ) {
if ( ( playingIdx == -1 ) || autoRetrigger ) {
if ( Time.time >= nextPlayTime ) {
Play();
if ( !autoRetrigger ) {
activated = false;
}
}
}
}
}
/*
-----------------------
OnTriggerEnter()
-----------------------
*/
public void OnTriggerEnter( Collider col ) {
activated = !activated;
}
/*
-----------------------
Play()
-----------------------
*/
public void Play() {
Transform transformToPlayFrom = transform;
if ( playPositions.Length > 0 ) {
int idx = Random.Range( 0, playPositions.Length );
while ( ( playPositions.Length > 1 ) && ( idx == lastPosIdx ) ) {
idx = Random.Range( 0, playPositions.Length );
}
transformToPlayFrom = playPositions[idx];
lastPosIdx = idx;
}
playingIdx = ambientSounds[Random.Range(0, ambientSounds.Length)].PlaySoundAt( transformToPlayFrom.position );
if ( playingIdx != -1 ) {
AudioManager.FadeInSound( playingIdx, fadeTime );
nextPlayTime = Time.time + Random.Range( randomRetriggerDelaySecs.x, randomRetriggerDelaySecs.y );
}
}
/*
-----------------------
EnableEmitter()
-----------------------
*/
public void EnableEmitter( bool enable ) {
activated = enable;
if ( enable ) {
Play();
} else {
if ( playingIdx != -1 ) {
AudioManager.FadeOutSound( playingIdx, fadeTime );
}
}
}
}
} // namespace OVR

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: c11944691f6b9cf44a391c95cb3f7dea
timeCreated: 1455050294
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,429 @@
using UnityEngine;
using UnityEngine.Audio;
using System.Collections;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
using System;
using System.Reflection;
#endif
namespace OVR
{
public enum PreloadSounds {
Default, // default unity behavior
Preload, // audio clips are forced to preload
ManualPreload, // audio clips are forced to not preload, preloading must be done manually
}
public enum Fade
{
In,
Out
}
[System.Serializable]
public class SoundGroup {
public SoundGroup( string name ) {
this.name = name;
}
public SoundGroup() {
mixerGroup = null;
maxPlayingSounds = 0;
preloadAudio = PreloadSounds.Default;
volumeOverride = 1.0f;
}
public void IncrementPlayCount() {
playingSoundCount = Mathf.Clamp( ++playingSoundCount, 0, maxPlayingSounds );
}
public void DecrementPlayCount() {
playingSoundCount = Mathf.Clamp( --playingSoundCount, 0, maxPlayingSounds );
}
public bool CanPlaySound() {
return ( maxPlayingSounds == 0 ) || ( playingSoundCount < maxPlayingSounds );
}
public string name = string.Empty;
public SoundFX[] soundList = new SoundFX[0];
public AudioMixerGroup mixerGroup = null; // default = AudioManager.defaultMixerGroup
[Range(0,64)]
public int maxPlayingSounds = 0; // default = 0, unlimited
// TODO: this preload behavior is not yet implemented
public PreloadSounds preloadAudio = PreloadSounds.Default; // default = true, audio clip data will be preloaded
public float volumeOverride = 1.0f; // default = 1.0
[HideInInspector]
public int playingSoundCount = 0;
}
/*
-----------------------
AudioManager
-----------------------
*/
public partial class AudioManager : MonoBehaviour {
[Tooltip("Make the audio manager persistent across all scene loads")]
public bool makePersistent = true; // true = don't destroy on load
[Tooltip("Enable the OSP audio plugin features")]
public bool enableSpatializedAudio = true; // true = enable spatialized audio
[Tooltip("Always play spatialized sounds with no reflections (Default)")]
public bool enableSpatializedFastOverride = false; // true = disable spatialized reflections override
[Tooltip("The audio mixer asset used for snapshot blends, etc.")]
public AudioMixer audioMixer = null;
[Tooltip( "The audio mixer group used for the pooled emitters" )]
public AudioMixerGroup defaultMixerGroup = null;
[Tooltip( "The audio mixer group used for the reserved pool emitter" )]
public AudioMixerGroup reservedMixerGroup = null;
[Tooltip( "The audio mixer group used for voice chat" )]
public AudioMixerGroup voiceChatMixerGroup = null;
[Tooltip("Log all PlaySound calls to the Unity console")]
public bool verboseLogging = false; // true = log all PlaySounds
[Tooltip("Maximum sound emitters")]
public int maxSoundEmitters = 32; // total number of sound emitters created
[Tooltip("Default volume for all sounds modulated by individual sound FX volumes")]
public float volumeSoundFX = 1.0f; // user pref: volume of all sound FX
[Tooltip("Sound FX fade time")]
public float soundFxFadeSecs = 1.0f; // sound FX fade time
public float audioMinFallOffDistance = 1.0f; // minimum falloff distance
public float audioMaxFallOffDistance = 25.0f; // maximum falloff distance
public SoundGroup[] soundGroupings = new SoundGroup[0];
private Dictionary<string,SoundFX> soundFXCache = null;
static private AudioManager theAudioManager = null;
static private FastList<string> names = new FastList<string>();
static private string[] defaultSound = new string[1] { "Default Sound" };
static private SoundFX nullSound = new SoundFX();
static private bool hideWarnings = false;
static public bool enableSpatialization { get { return ( theAudioManager !=null ) ? theAudioManager.enableSpatializedAudio : false; } }
static public AudioManager Instance { get { return theAudioManager; } }
static public float NearFallOff { get { return theAudioManager.audioMinFallOffDistance; } }
static public float FarFallOff { get { return theAudioManager.audioMaxFallOffDistance; } }
static public AudioMixerGroup EmitterGroup { get { return theAudioManager.defaultMixerGroup; } }
static public AudioMixerGroup ReservedGroup { get { return theAudioManager.reservedMixerGroup; } }
static public AudioMixerGroup VoipGroup { get { return theAudioManager.voiceChatMixerGroup; } }
/*
-----------------------
Awake()
-----------------------
*/
void Awake() {
Init();
}
/*
-----------------------
OnDestroy()
-----------------------
*/
void OnDestroy() {
// we only want the initialized audio manager instance cleaning up the sound emitters
if ( theAudioManager == this ) {
if ( soundEmitterParent != null ) {
Destroy( soundEmitterParent );
}
}
///TODO - if you change scenes you'll want to call OnPreSceneLoad to detach the sound emitters
///from anything they might be parented to or they will get destroyed with that object
///there should only be one instance of the AudioManager across the life of the game/app
///GameManager.OnPreSceneLoad -= OnPreSceneLoad;
}
/*
-----------------------
Init()
-----------------------
*/
void Init() {
if ( theAudioManager != null ) {
if ( Application.isPlaying && ( theAudioManager != this ) ) {
enabled = false;
}
return;
}
theAudioManager = this;
///TODO - if you change scenes you'll want to call OnPreSceneLoad to detach the sound emitters
///from anything they might be parented to or they will get destroyed with that object
///there should only be one instance of the AudioManager across the life of the game/app
///GameManager.OnPreSceneLoad += OnPreSceneLoad;
// make sure the first one is a null sound
nullSound.name = "Default Sound";
// build the sound FX cache
RebuildSoundFXCache();
// create the sound emitters
if ( Application.isPlaying ) {
InitializeSoundSystem();
if ( makePersistent && ( transform.parent == null ) ) {
// don't destroy the audio manager on scene loads
DontDestroyOnLoad( gameObject );
}
}
#if UNITY_EDITOR
Debug.Log( "[AudioManager] Initialized..." );
#endif
}
/*
-----------------------
Update()
-----------------------
*/
void Update() {
// update the free and playing lists
UpdateFreeEmitters();
}
/*
-----------------------
RebuildSoundFXCache()
-----------------------
*/
void RebuildSoundFXCache() {
// build the SoundFX dictionary for quick name lookups
int count = 0;
for ( int group = 0; group < soundGroupings.Length; group++ ) {
count += soundGroupings[group].soundList.Length;
}
soundFXCache = new Dictionary<string,SoundFX>( count + 1 );
// add the null sound
soundFXCache.Add( nullSound.name, nullSound );
// add the rest
for ( int group = 0; group < soundGroupings.Length; group++ ) {
for ( int i = 0; i < soundGroupings[group].soundList.Length; i++ ) {
if ( soundFXCache.ContainsKey( soundGroupings[group].soundList[i].name ) ) {
Debug.LogError( "ERROR: Duplicate Sound FX name in the audio manager: '" + soundGroupings[group].name + "' > '" + soundGroupings[group].soundList[i].name + "'" );
} else {
soundGroupings[group].soundList[i].Group = soundGroupings[group];
soundFXCache.Add( soundGroupings[group].soundList[i].name, soundGroupings[group].soundList[i] );
}
}
soundGroupings[group].playingSoundCount = 0;
}
}
/*
-----------------------
FindSoundFX()
-----------------------
*/
static public SoundFX FindSoundFX( string name, bool rebuildCache = false ) {
#if UNITY_EDITOR
if ( theAudioManager == null ) {
Debug.LogError( "ERROR: audio manager not yet initialized or created!" + " Time: " + Time.time );
return null;
}
#endif
if ( string.IsNullOrEmpty( name ) ) {
return nullSound;
}
if ( rebuildCache ) {
theAudioManager.RebuildSoundFXCache();
}
if ( !theAudioManager.soundFXCache.ContainsKey( name ) ) {
#if DEBUG_BUILD || UNITY_EDITOR
Debug.LogError( "WARNING: Missing Sound FX in cache: " + name );
#endif
return nullSound;
}
return theAudioManager.soundFXCache[name];
}
/*
-----------------------
FindAudioManager()
-----------------------
*/
static private bool FindAudioManager() {
GameObject audioManagerObject = GameObject.Find( "AudioManager" );
if ( ( audioManagerObject == null ) || ( audioManagerObject.GetComponent<AudioManager>() == null ) ) {
if ( !hideWarnings ) {
Debug.LogError( "[ERROR] AudioManager object missing from hierarchy!" );
hideWarnings = true;
}
return false;
} else {
audioManagerObject.GetComponent<AudioManager>().Init();
}
return true;
}
/*
-----------------------
GetGameObject()
-----------------------
*/
static public GameObject GetGameObject() {
if ( theAudioManager == null ) {
if ( !FindAudioManager() ) {
return null;
}
}
return theAudioManager.gameObject;
}
/*
-----------------------
NameMinusGroup()
strip off the sound group from the inspector dropdown
-----------------------
*/
static public string NameMinusGroup( string name ) {
if ( name.IndexOf( "/" ) > -1 ) {
return name.Substring( name.IndexOf( "/" ) + 1 );
}
return name;
}
/*
-----------------------
GetSoundFXNames()
used by the inspector
-----------------------
*/
static public string[] GetSoundFXNames( string currentValue, out int currentIdx ) {
currentIdx = 0;
names.Clear();
if ( theAudioManager == null ) {
if ( !FindAudioManager() ) {
return defaultSound;
}
}
names.Add( nullSound.name );
for ( int group = 0; group < theAudioManager.soundGroupings.Length; group++ ) {
for ( int i = 0; i < theAudioManager.soundGroupings[group].soundList.Length; i++ ) {
if ( string.Compare( currentValue, theAudioManager.soundGroupings[group].soundList[i].name, true ) == 0 ) {
currentIdx = names.Count;
}
names.Add( theAudioManager.soundGroupings[group].name + "/" + theAudioManager.soundGroupings[group].soundList[i].name );
}
}
//names.Sort( delegate( string s1, string s2 ) { return s1.CompareTo( s2 ); } );
return names.ToArray();
}
#if UNITY_EDITOR
/*
-----------------------
OnPrefabReimported()
-----------------------
*/
static public void OnPrefabReimported() {
if ( theAudioManager != null ) {
Debug.Log( "[AudioManager] Reimporting the sound FX cache." );
theAudioManager.RebuildSoundFXCache();
}
}
/*
-----------------------
PlaySound()
used in the editor
-----------------------
*/
static public void PlaySound( string soundFxName ) {
if ( theAudioManager == null ) {
if ( !FindAudioManager() ) {
return;
}
}
SoundFX soundFX = FindSoundFX( soundFxName, true );
if ( soundFX == null ) {
return;
}
AudioClip clip = soundFX.GetClip();
if ( clip != null ) {
Assembly unityEditorAssembly = typeof(AudioImporter).Assembly;
Type audioUtilClass = unityEditorAssembly.GetType("UnityEditor.AudioUtil");
MethodInfo method = audioUtilClass.GetMethod(
"PlayClip",
BindingFlags.Static | BindingFlags.Public,
null,
new System.Type[] { typeof(AudioClip) },
null );
method.Invoke( null, new object[] { clip } );
}
}
/*
-----------------------
IsSoundPlaying()
used in the editor
-----------------------
*/
static public bool IsSoundPlaying( string soundFxName ) {
if ( theAudioManager == null ) {
if ( !FindAudioManager() ) {
return false;
}
}
SoundFX soundFX = FindSoundFX( soundFxName, true );
if ( soundFX == null ) {
return false;
}
AudioClip clip = soundFX.GetClip();
if ( clip != null ) {
Assembly unityEditorAssembly = typeof(AudioImporter).Assembly;
Type audioUtilClass = unityEditorAssembly.GetType("UnityEditor.AudioUtil");
MethodInfo method = audioUtilClass.GetMethod(
"IsClipPlaying",
BindingFlags.Static | BindingFlags.Public,
null,
new System.Type[] { typeof(AudioClip) },
null );
return Convert.ToBoolean( method.Invoke( null, new object[] { clip } ) );
}
return false;
}
/*
-----------------------
StopSound()
used in the editor
-----------------------
*/
static public void StopSound(string soundFxName)
{
if (theAudioManager == null)
{
if (!FindAudioManager())
{
return;
}
}
SoundFX soundFX = FindSoundFX(soundFxName, true);
if (soundFX == null)
{
return;
}
AudioClip clip = soundFX.GetClip();
if (clip != null)
{
Assembly unityEditorAssembly = typeof(AudioImporter).Assembly;
Type audioUtilClass = unityEditorAssembly.GetType("UnityEditor.AudioUtil");
MethodInfo method = audioUtilClass.GetMethod(
"StopClip",
BindingFlags.Static | BindingFlags.Public,
null,
new System.Type[] { typeof(AudioClip) },
null);
method.Invoke(null, new object[] { clip });
}
}
#endif
}
} // namespace OVR

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6d1d30b41806244fca035fdae2896fb7
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -0,0 +1,798 @@
using UnityEngine;
using UnityEngine.Audio;
namespace OVR
{
//-------------------------------------------------------------------------
// Types
//-------------------------------------------------------------------------
public enum EmitterChannel {
None = -1,
Reserved = 0, // plays on the single reserved emitter
Any // queues to the next available emitter
}
[System.Serializable]
public class MixerSnapshot {
public AudioMixerSnapshot snapshot = null;
public float transitionTime = 0.25f;
}
/*
-----------------------
GameManager Sound Routines
-----------------------
*/
public partial class AudioManager : MonoBehaviour {
public enum Fade {
In,
Out
}
private float audioMaxFallOffDistanceSqr = 25.0f * 25.0f; // past this distance, sounds are ignored for the local player
private SoundEmitter[] soundEmitters = null; // pool of sound emitters to play sounds through
private FastList<SoundEmitter> playingEmitters = new FastList<SoundEmitter>();
private FastList<SoundEmitter> nextFreeEmitters = new FastList<SoundEmitter>();
private MixerSnapshot currentSnapshot = null;
static private GameObject soundEmitterParent = null; // parent object for the sound emitters
static private Transform staticListenerPosition = null; // play position for regular 2D sounds
static private bool showPlayingEmitterCount = false;
static private bool forceShowEmitterCount = false;
static private bool soundEnabled = true;
static public bool SoundEnabled { get { return soundEnabled; } }
static readonly AnimationCurve defaultReverbZoneMix = new AnimationCurve( new Keyframe[2] { new Keyframe( 0f, 1.0f ), new Keyframe( 1f, 1f ) } );
// Calculate the maximum number of emitters that can be running at one time.
static private int CalculateMaxEmittersSize() {
return theAudioManager.maxSoundEmitters + (int)EmitterChannel.Any;
}
// Verify if this index is valid
static private bool ValidateEmitterIndex(int index) {
return index > -1 && index < CalculateMaxEmittersSize();
}
/*
-----------------------
InitializeSoundSystem()
initialize persistent sound emitter objects that live across scene loads
-----------------------
*/
void InitializeSoundSystem() {
int bufferLength = 960;
int numBuffers = 4;
AudioSettings.GetDSPBufferSize( out bufferLength, out numBuffers );
if ( Application.isPlaying ) {
Debug.Log( "[AudioManager] Audio Sample Rate: " + AudioSettings.outputSampleRate );
Debug.Log( "[AudioManager] Audio Buffer Length: " + bufferLength + " Size: " + numBuffers );
}
// find the audio listener for playing regular 2D sounds
AudioListener audioListenerObject = GameObject.FindObjectOfType<AudioListener>() as AudioListener;
if ( audioListenerObject == null ) {
Debug.LogError( "[AudioManager] Missing AudioListener object! Add one to the scene." );
} else {
staticListenerPosition = audioListenerObject.transform;
}
// we allocate maxSoundEmitters + reserved channels
soundEmitters = new SoundEmitter[CalculateMaxEmittersSize()];
// see if the sound emitters have already been created, if so, nuke it, it shouldn't exist in the scene upon load
soundEmitterParent = GameObject.Find( "__SoundEmitters__" );
if ( soundEmitterParent != null ) {
// delete any sound emitters hanging around
Destroy( soundEmitterParent );
}
// create them all
soundEmitterParent = new GameObject( "__SoundEmitters__" );
for ( int i = 0; i < CalculateMaxEmittersSize(); i++ ) {
GameObject emitterObject = new GameObject( "SoundEmitter_" + i );
emitterObject.transform.parent = soundEmitterParent.transform;
emitterObject.transform.position = Vector3.zero;
// don't ever save this to the scene
emitterObject.hideFlags = HideFlags.DontSaveInEditor;
// add the sound emitter components
soundEmitters[i] = emitterObject.AddComponent<SoundEmitter>();
soundEmitters[i].SetDefaultParent( soundEmitterParent.transform );
soundEmitters[i].SetChannel( i );
soundEmitters[i].Stop();
// save off the original index
soundEmitters[i].originalIdx = i;
}
// reset the free emitter lists
ResetFreeEmitters();
soundEmitterParent.hideFlags = HideFlags.DontSaveInEditor;
audioMaxFallOffDistanceSqr = audioMaxFallOffDistance * audioMaxFallOffDistance;
}
/*
-----------------------
UpdateFreeEmitters()
-----------------------
*/
void UpdateFreeEmitters() {
if ( verboseLogging ) {
if ( Input.GetKeyDown( KeyCode.A ) ) {
forceShowEmitterCount = !forceShowEmitterCount;
}
if ( forceShowEmitterCount ) {
showPlayingEmitterCount = true;
}
}
// display playing emitter count when the sound system is overwhelmed
int total = 0, veryLow = 0, low = 0, def = 0, high = 0, veryHigh = 0;
// find emitters that are done playing and add them to the nextFreeEmitters list
for ( int i = 0; i < playingEmitters.size; ) {
if ( playingEmitters[i] == null ) {
Debug.LogError( "[AudioManager] ERROR: playingEmitters list had a null emitter! Something nuked a sound emitter!!!" );
playingEmitters.RemoveAtFast( i );
return;
}
if ( !playingEmitters[i].IsPlaying() ) {
// add to the free list and remove from the playing list
if ( verboseLogging ) {
if ( nextFreeEmitters.Contains( playingEmitters[i] ) ) {
Debug.LogError( "[AudioManager] ERROR: playing sound emitter already in the free emitters list!" );
}
}
playingEmitters[i].Stop();
nextFreeEmitters.Add( playingEmitters[i] );
playingEmitters.RemoveAtFast( i );
continue;
}
// debugging/profiling
if ( verboseLogging && showPlayingEmitterCount ) {
total++;
switch ( playingEmitters[i].priority ) {
case SoundPriority.VeryLow: veryLow++; break;
case SoundPriority.Low: low++; break;
case SoundPriority.Default: def++; break;
case SoundPriority.High: high++; break;
case SoundPriority.VeryHigh: veryHigh++; break;
}
}
i++;
}
if ( verboseLogging && showPlayingEmitterCount ) {
Debug.LogWarning( string.Format( "[AudioManager] Playing sounds: Total {0} | VeryLow {1} | Low {2} | Default {3} | High {4} | VeryHigh {5} | Free {6}", Fmt( total ), Fmt( veryLow ), Fmt( low ), Fmt( def ), Fmt( high ), Fmt( veryHigh ), FmtFree( nextFreeEmitters.Count ) ) );
showPlayingEmitterCount = false;
}
}
/*
-----------------------
Fmt()
-----------------------
*/
string Fmt( int count ) {
float t = count / (float)theAudioManager.maxSoundEmitters;
if ( t < 0.5f ) {
return "<color=green>" + count.ToString() + "</color>";
} else if ( t < 0.7 ) {
return "<color=yellow>" + count.ToString() + "</color>";
} else {
return "<color=red>" + count.ToString() + "</color>";
}
}
/*
-----------------------
FmtFree()
-----------------------
*/
string FmtFree( int count ) {
float t = count / (float)theAudioManager.maxSoundEmitters;
if ( t < 0.2f ) {
return "<color=red>" + count.ToString() + "</color>";
} else if ( t < 0.3 ) {
return "<color=yellow>" + count.ToString() + "</color>";
} else {
return "<color=green>" + count.ToString() + "</color>";
}
}
/*
-----------------------
OnPreSceneLoad()
-----------------------
*/
void OnPreSceneLoad() {
// move any attached sounds back to the sound emitters parent before changing levels so they don't get destroyed
Debug.Log( "[AudioManager] OnPreSceneLoad cleanup" );
for ( int i = 0; i < soundEmitters.Length; i++ ) {
soundEmitters[i].Stop();
soundEmitters[i].ResetParent( soundEmitterParent.transform );
}
// reset our emitter lists
ResetFreeEmitters();
}
/*
-----------------------
ResetFreeEmitters()
-----------------------
*/
void ResetFreeEmitters() {
nextFreeEmitters.Clear();
playingEmitters.Clear();
for ( int i = (int)EmitterChannel.Any; i < soundEmitters.Length; i++ ) {
nextFreeEmitters.Add( soundEmitters[i] );
}
}
/*
-----------------------
FadeOutSoundChannel()
utility function to fade out a playing sound channel
-----------------------
*/
static public void FadeOutSoundChannel( int channel, float delaySecs, float fadeTime ) {
theAudioManager.soundEmitters[channel].FadeOutDelayed( delaySecs, fadeTime );
}
/*
-----------------------
StopSound()
-----------------------
*/
static public bool StopSound( int idx, bool fadeOut = true, bool stopReserved = false ) {
if ( !stopReserved && ( idx == (int)EmitterChannel.Reserved ) ) {
return false;
}
if ( !fadeOut ) {
theAudioManager.soundEmitters[idx].Stop();
}
else {
theAudioManager.soundEmitters[idx].FadeOut( theAudioManager.soundFxFadeSecs );
}
return true;
}
/*
-----------------------
FadeInSound()
-----------------------
*/
public static void FadeInSound( int idx, float fadeTime, float volume ) {
theAudioManager.soundEmitters[idx].FadeIn( fadeTime, volume );
}
/*
-----------------------
FadeInSound()
-----------------------
*/
public static void FadeInSound( int idx, float fadeTime ) {
theAudioManager.soundEmitters[idx].FadeIn( fadeTime );
}
/*
-----------------------
FadeOutSound()
-----------------------
*/
public static void FadeOutSound( int idx, float fadeTime ) {
theAudioManager.soundEmitters[idx].FadeOut( fadeTime );
}
/*
-----------------------
StopAllSounds()
-----------------------
*/
public static void StopAllSounds( bool fadeOut, bool stopReserved = false ) {
for ( int i = 0; i < theAudioManager.soundEmitters.Length; i++ ) {
StopSound( i, fadeOut, stopReserved );
}
}
/*
-----------------------
MuteAllSounds()
-----------------------
*/
public void MuteAllSounds( bool mute, bool muteReserved = false ) {
for ( int i = 0; i < soundEmitters.Length; i++ ) {
if ( !muteReserved && ( i == (int)EmitterChannel.Reserved ) ) {
continue;
}
soundEmitters[i].audioSource.mute = true;
}
}
/*
-----------------------
UnMuteAllSounds()
-----------------------
*/
public void UnMuteAllSounds( bool unmute, bool unmuteReserved = false ) {
for ( int i = 0; i < soundEmitters.Length; i++ ) {
if ( !unmuteReserved && ( i == (int)EmitterChannel.Reserved ) ) {
continue;
}
if ( soundEmitters[i].audioSource.isPlaying ) {
soundEmitters[i].audioSource.mute = false;
}
}
}
/*
-----------------------
GetEmitterEndTime()
-----------------------
*/
static public float GetEmitterEndTime( int idx ) {
return theAudioManager.soundEmitters[idx].endPlayTime;
}
/*
-----------------------
SetEmitterTime()
-----------------------
*/
static public float SetEmitterTime( int idx, float time ) {
return theAudioManager.soundEmitters[idx].time = time;
}
/*
-----------------------
PlaySound()
-----------------------
*/
static public int PlaySound( AudioClip clip, float volume, EmitterChannel src = EmitterChannel.Any, float delay = 0.0f, float pitchVariance = 1.0f, bool loop = false ) {
if ( !SoundEnabled ) {
return -1;
}
return PlaySoundAt( ( staticListenerPosition != null ) ? staticListenerPosition.position : Vector3.zero, clip, volume, src, delay, pitchVariance, loop );
}
/*
-----------------------
FindFreeEmitter()
-----------------------
*/
static private int FindFreeEmitter( EmitterChannel src, SoundPriority priority ) {
// default to the reserved emitter
SoundEmitter next = theAudioManager.soundEmitters[0];
if ( src == EmitterChannel.Any ) {
// pull from the free emitter list if possible
if ( theAudioManager.nextFreeEmitters.size > 0 ) {
// return the first in the list
next = theAudioManager.nextFreeEmitters[0];
// remove it from the free list
theAudioManager.nextFreeEmitters.RemoveAtFast( 0 );
} else {
// no free emitters available so pull from the lowest priority sound
if ( priority == SoundPriority.VeryLow ) {
// skip low priority sounds
return -1;
} else {
// find a playing emitter that has a lower priority than what we're requesting
// TODO - we could first search for Very Low, then Low, etc ... TBD if it's worth the effort
next = theAudioManager.playingEmitters.Find( item => item != null && item.priority < priority );
if ( next == null ) {
// last chance to find a free emitter
if ( priority < SoundPriority.Default ) {
// skip sounds less than the default priority
if ( theAudioManager.verboseLogging ) {
Debug.LogWarning( "[AudioManager] skipping sound " + priority );
}
return -1;
} else {
// grab a default priority emitter so that we don't cannabalize a high priority sound
next = theAudioManager.playingEmitters.Find( item => item != null && item.priority <= SoundPriority.Default ); ;
}
}
if ( next != null ) {
if ( theAudioManager.verboseLogging ) {
Debug.LogWarning( "[AudioManager] cannabalizing " + next.originalIdx + " Time: " + Time.time );
}
// remove it from the playing list
next.Stop();
theAudioManager.playingEmitters.RemoveFast( next );
}
}
}
}
if ( next == null ) {
Debug.LogError( "[AudioManager] ERROR - absolutely couldn't find a free emitter! Priority = " + priority + " TOO MANY PlaySound* calls!" );
showPlayingEmitterCount = true;
return -1;
}
return next.originalIdx;
}
/*
-----------------------
PlaySound()
-----------------------
*/
static public int PlaySound( SoundFX soundFX, EmitterChannel src = EmitterChannel.Any, float delay = 0.0f ) {
if ( !SoundEnabled ) {
return -1;
}
return PlaySoundAt( ( staticListenerPosition != null ) ? staticListenerPosition.position : Vector3.zero, soundFX, src, delay );
}
/*
-----------------------
PlaySoundAt()
-----------------------
*/
static public int PlaySoundAt( Vector3 position, SoundFX soundFX, EmitterChannel src = EmitterChannel.Any, float delay = 0.0f, float volumeOverride = 1.0f, float pitchMultiplier = 1.0f ) {
if ( !SoundEnabled ) {
return -1;
}
AudioClip clip = soundFX.GetClip();
if ( clip == null ) {
return -1;
}
// check the distance from the local player and ignore sounds out of range
if ( staticListenerPosition != null ) {
float distFromListener = ( staticListenerPosition.position - position ).sqrMagnitude;
if ( distFromListener > theAudioManager.audioMaxFallOffDistanceSqr ) {
return -1;
}
if ( distFromListener > soundFX.MaxFalloffDistSquared ) {
return -1;
}
}
// check max playing sounds
if ( soundFX.ReachedGroupPlayLimit() ) {
if ( theAudioManager.verboseLogging ) {
Debug.Log( "[AudioManager] PlaySoundAt() with " + soundFX.name + " skipped due to group play limit" );
}
return -1;
}
int idx = FindFreeEmitter( src, soundFX.priority );
if ( idx == -1 ) {
// no free emitters - should only happen on very low priority sounds
return -1;
}
SoundEmitter emitter = theAudioManager.soundEmitters[idx];
// make sure to detach the emitter from a previous parent
emitter.ResetParent( soundEmitterParent.transform );
emitter.gameObject.SetActive( true );
// set up the sound emitter
AudioSource audioSource = emitter.audioSource;
ONSPAudioSource osp = emitter.osp;
audioSource.enabled = true;
audioSource.volume = Mathf.Clamp01( Mathf.Clamp01( theAudioManager.volumeSoundFX * soundFX.volume ) * volumeOverride * soundFX.GroupVolumeOverride );
audioSource.pitch = soundFX.GetPitch() * pitchMultiplier;
audioSource.time = 0.0f;
audioSource.spatialBlend = 1.0f;
audioSource.rolloffMode = soundFX.falloffCurve;
if ( soundFX.falloffCurve == AudioRolloffMode.Custom ) {
audioSource.SetCustomCurve( AudioSourceCurveType.CustomRolloff, soundFX.volumeFalloffCurve );
}
audioSource.SetCustomCurve( AudioSourceCurveType.ReverbZoneMix, soundFX.reverbZoneMix );
audioSource.dopplerLevel = 0;
audioSource.clip = clip;
audioSource.spread = soundFX.spread;
audioSource.loop = soundFX.looping;
audioSource.mute = false;
audioSource.minDistance = soundFX.falloffDistance.x;
audioSource.maxDistance = soundFX.falloffDistance.y;
audioSource.outputAudioMixerGroup = soundFX.GetMixerGroup( AudioManager.EmitterGroup );
// set the play time so we can check when sounds are done
emitter.endPlayTime = Time.time + clip.length + delay;
// cache the default volume for fading
emitter.defaultVolume = audioSource.volume;
// sound priority
emitter.priority = soundFX.priority;
// reset this
emitter.onFinished = null;
// update the sound group limits
emitter.SetPlayingSoundGroup( soundFX.Group );
// add to the playing list
if ( src == EmitterChannel.Any ) {
theAudioManager.playingEmitters.AddUnique( emitter );
}
// OSP properties
if ( osp != null ) {
osp.EnableSpatialization = soundFX.ospProps.enableSpatialization;
osp.EnableRfl = theAudioManager.enableSpatializedFastOverride || soundFX.ospProps.useFastOverride ? true : false;
osp.Gain = soundFX.ospProps.gain;
osp.UseInvSqr = soundFX.ospProps.enableInvSquare;
osp.Near = soundFX.ospProps.invSquareFalloff.x;
osp.Far = soundFX.ospProps.invSquareFalloff.y;
audioSource.spatialBlend = (soundFX.ospProps.enableSpatialization) ? 1.0f : 0.8f;
// make sure to set the properties in the audio source before playing
osp.SetParameters(ref audioSource);
}
audioSource.transform.position = position;
if ( theAudioManager.verboseLogging ) {
Debug.Log( "[AudioManager] PlaySoundAt() channel = " + idx + " soundFX = " + soundFX.name + " volume = " + emitter.volume + " Delay = " + delay + " time = " + Time.time + "\n" );
}
// play the sound
if ( delay > 0f ) {
audioSource.PlayDelayed( delay );
} else {
audioSource.Play();
}
return idx;
}
/*
-----------------------
PlayRandomSoundAt()
-----------------------
*/
static public int PlayRandomSoundAt( Vector3 position, AudioClip[] clips, float volume, EmitterChannel src = EmitterChannel.Any, float delay = 0.0f, float pitch = 1.0f, bool loop = false ) {
if ( ( clips == null ) || ( clips.Length == 0 ) ) {
return -1;
}
int idx = Random.Range( 0, clips.Length );
return PlaySoundAt( position, clips[idx], volume, src, delay, pitch, loop );
}
/*
-----------------------
PlaySoundAt()
-----------------------
*/
static public int PlaySoundAt( Vector3 position, AudioClip clip, float volume = 1.0f, EmitterChannel src = EmitterChannel.Any, float delay = 0.0f, float pitch = 1.0f, bool loop = false ) {
if ( !SoundEnabled ) {
return -1;
}
if ( clip == null ) {
return -1;
}
// check the distance from the local player and ignore sounds out of range
if ( staticListenerPosition != null ) {
if ( ( staticListenerPosition.position - position ).sqrMagnitude > theAudioManager.audioMaxFallOffDistanceSqr ) {
// no chance of being heard
return -1;
}
}
int idx = FindFreeEmitter( src, 0 );
if ( idx == -1 ) {
// no free emitters - should only happen on very low priority sounds
return -1;
}
SoundEmitter emitter = theAudioManager.soundEmitters[idx];
// make sure to detach the emitter from a previous parent
emitter.ResetParent( soundEmitterParent.transform );
emitter.gameObject.SetActive( true );
// set up the sound emitter
AudioSource audioSource = emitter.audioSource;
ONSPAudioSource osp = emitter.osp;
audioSource.enabled = true;
audioSource.volume = Mathf.Clamp01( theAudioManager.volumeSoundFX * volume );
audioSource.pitch = pitch;
audioSource.spatialBlend = 0.8f;
audioSource.rolloffMode = AudioRolloffMode.Linear;
audioSource.SetCustomCurve( AudioSourceCurveType.ReverbZoneMix, defaultReverbZoneMix );
audioSource.dopplerLevel = 0.0f;
audioSource.clip = clip;
audioSource.spread = 0.0f;
audioSource.loop = loop;
audioSource.mute = false;
audioSource.minDistance = theAudioManager.audioMinFallOffDistance;
audioSource.maxDistance = theAudioManager.audioMaxFallOffDistance;
audioSource.outputAudioMixerGroup = AudioManager.EmitterGroup;
// set the play time so we can check when sounds are done
emitter.endPlayTime = Time.time + clip.length + delay;
// cache the default volume for fading
emitter.defaultVolume = audioSource.volume;
// default priority
emitter.priority = 0;
// reset this
emitter.onFinished = null;
// update the sound group limits
emitter.SetPlayingSoundGroup( null );
// add to the playing list
if ( src == EmitterChannel.Any ) {
theAudioManager.playingEmitters.AddUnique( emitter );
}
// disable spatialization (by default for regular AudioClips)
if ( osp != null ) {
osp.EnableSpatialization = false;
}
audioSource.transform.position = position;
if ( theAudioManager.verboseLogging ) {
Debug.Log( "[AudioManager] PlaySoundAt() channel = " + idx + " clip = " + clip.name + " volume = " + emitter.volume + " Delay = " + delay + " time = " + Time.time + "\n" );
}
// play the sound
if ( delay > 0f ) {
audioSource.PlayDelayed( delay );
} else {
audioSource.Play();
}
return idx;
}
/*
-----------------------
SetOnFinished()
-----------------------
*/
public static void SetOnFinished( int emitterIdx, System.Action onFinished ) {
if ( ValidateEmitterIndex(emitterIdx) ) {
theAudioManager.soundEmitters[emitterIdx].SetOnFinished( onFinished );
}
}
/*
-----------------------
SetOnFinished()
-----------------------
*/
public static void SetOnFinished( int emitterIdx, System.Action<object> onFinished, object obj ) {
if ( ValidateEmitterIndex(emitterIdx) ) {
theAudioManager.soundEmitters[emitterIdx].SetOnFinished( onFinished, obj );
}
}
/*
-----------------------
AttachSoundToParent()
-----------------------
*/
public static void AttachSoundToParent( int idx, Transform parent ) {
if ( theAudioManager.verboseLogging ) {
string parentName = parent.name;
if ( parent.parent != null ) {
parentName = parent.parent.name + "/" + parentName;
}
Debug.Log( "[AudioManager] ATTACHING INDEX " + idx + " to " + parentName );
}
theAudioManager.soundEmitters[idx].ParentTo( parent );
}
/*
-----------------------
DetachSoundFromParent()
-----------------------
*/
public static void DetachSoundFromParent( int idx ) {
if ( theAudioManager.verboseLogging ) {
Debug.Log( "[AudioManager] DETACHING INDEX " + idx );
}
theAudioManager.soundEmitters[idx].DetachFromParent();
}
/*
-----------------------
DetachSoundsFromParent()
-----------------------
*/
public static void DetachSoundsFromParent( SoundEmitter[] emitters, bool stopSounds = true ) {
if ( emitters == null ) {
return;
}
foreach ( SoundEmitter emitter in emitters ) {
if ( emitter.defaultParent != null ) {
if ( stopSounds ) {
emitter.Stop();
}
emitter.DetachFromParent();
// make sure it's active
emitter.gameObject.SetActive( true );
} else {
if ( stopSounds ) {
emitter.Stop();
}
}
}
}
/*
-----------------------
SetEmitterMixerGroup()
-----------------------
*/
public static void SetEmitterMixerGroup( int idx, AudioMixerGroup mixerGroup ) {
if ( ( theAudioManager != null ) && ( idx > -1 ) ) {
theAudioManager.soundEmitters[idx].SetAudioMixer( mixerGroup );
}
}
/*
-----------------------
GetActiveSnapshot()
-----------------------
*/
public static MixerSnapshot GetActiveSnapshot() {
return ( theAudioManager != null ) ? theAudioManager.currentSnapshot : null;
}
/*
-----------------------
SetCurrentSnapshot()
-----------------------
*/
public static void SetCurrentSnapshot( MixerSnapshot mixerSnapshot ) {
#if UNITY_EDITOR
if ( mixerSnapshot == null || mixerSnapshot.snapshot == null ) {
Debug.LogError( "[AudioManager] ERROR setting empty mixer snapshot!" );
} else {
Debug.Log( "[AudioManager] Setting audio mixer snapshot: " + ( ( mixerSnapshot != null && mixerSnapshot.snapshot != null ) ? mixerSnapshot.snapshot.name : "None" ) + " Time: " + Time.time );
}
#endif
if ( theAudioManager != null ) {
if ( ( mixerSnapshot != null ) && ( mixerSnapshot.snapshot != null ) ) {
mixerSnapshot.snapshot.TransitionTo( mixerSnapshot.transitionTime );
} else {
mixerSnapshot = null;
}
theAudioManager.currentSnapshot = mixerSnapshot;
}
}
/*
-----------------------
BlendWithCurrentSnapshot()
-----------------------
*/
public static void BlendWithCurrentSnapshot( MixerSnapshot blendSnapshot, float weight, float blendTime = 0.0f ) {
if ( theAudioManager != null ) {
if ( theAudioManager.audioMixer == null ) {
Debug.LogWarning( "[AudioManager] can't call BlendWithCurrentSnapshot if the audio mixer is not set!" );
return;
}
if ( blendTime == 0.0f ) {
blendTime = Time.deltaTime;
}
if ( ( theAudioManager.currentSnapshot != null ) && (theAudioManager.currentSnapshot.snapshot != null ) ) {
if ( ( blendSnapshot != null ) && ( blendSnapshot.snapshot != null ) ) {
weight = Mathf.Clamp01( weight );
if ( weight == 0.0f ) {
// revert to the default snapshot
theAudioManager.currentSnapshot.snapshot.TransitionTo( blendTime );
} else {
AudioMixerSnapshot[] snapshots = new AudioMixerSnapshot[] { theAudioManager.currentSnapshot.snapshot, blendSnapshot.snapshot };
float[] weights = new float[] { 1.0f - weight, weight };
theAudioManager.audioMixer.TransitionToSnapshots( snapshots, weights, blendTime );
}
}
}
}
}
}
} // namespace OVR

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 41cb0f893e9a44e83b09a66c55bd7856
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -0,0 +1,5 @@
fileFormatVersion: 2
guid: 02213405caaf04aeea7876974c06fa5a
folderAsset: yes
DefaultImporter:
userData:

View File

@ -0,0 +1,46 @@
using UnityEngine;
using UnityEditor;
namespace OVR
{
/*
-----------------------
AudioImportPostProcessor()
-----------------------
*/
public class AudioImportPostProcessor : AssetPostprocessor {
static void OnPostprocessAllAssets( string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths ) {
AudioManager audioManager = AudioManager.Instance;
if ( audioManager != null ) {
// find the asset path to the loaded audio manager prefab
#if UNITY_2018_2_OR_NEWER
Object prefabObject = PrefabUtility.GetCorrespondingObjectFromSource(audioManager);
#else
Object prefabObject = PrefabUtility.GetPrefabParent( audioManager );
#endif
if ( prefabObject != null ) {
string path = AssetDatabase.GetAssetPath( prefabObject );
// check to see if the AudioManager prefab has been reimported.
// if so, rebuild everything
foreach ( string asset in importedAssets ) {
if ( asset.ToLower() == path.ToLower() ) {
// in the event the audio manager is selected, deselect it first before reloading
Debug.Log( "[AudioManager] AudioManager prefab reloaded: " + path );
Selection.objects = new Object[0] { };
// unfortunately even saving the audio manager prefab will trigger this action
//string msg = "The Audio Manager was reloaded. If you are going to be making modifications to the Audio Manager, ";
//msg += "please verify you have the latest version before proceeding. If in doubt, restart Unity before making modifications.";
//EditorUtility.DisplayDialog( "Audio Manager Prefab Reloaded", msg, "OK" );
// do the actual reload
AudioManager.OnPrefabReimported();
break;
}
}
}
}
}
}
} // namespace OVR

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 48902580b26e3554d992bad48087eee5
timeCreated: 1471010515
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,631 @@
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
namespace OVR
{
/*
-----------------------
AudioManagerInspector
-----------------------
*/
[CustomEditor(typeof(AudioManager))]
public class AudioManagerInspector : Editor {
private AudioManager audioManager = null;
private string dragDropIdentifier = "MoveSoundFX";
private GUIStyle customDividerStyle = null;
/*
-----------------------
OnInspectorGUI()
-----------------------
*/
public override void OnInspectorGUI() {
audioManager = target as AudioManager;
Event e = Event.current;
// draw the default properties
DrawDefaultProperties();
// draw the categories section
DrawCategories( e );
serializedObject.Update();
// draw the sound effects for the selected category
DrawSoundEffects( e );
serializedObject.ApplyModifiedProperties();
CreateStyles();
}
/*
-----------------------
MarkDirty()
-----------------------
*/
void MarkDirty() {
serializedObject.SetIsDifferentCacheDirty();
EditorUtility.SetDirty( audioManager );
}
static private int selectedGroup = 0;
private int nextGroup = -1;
private int editGroup = -1;
private FastList<SoundGroup> soundGroups = new FastList<SoundGroup>();
private FastList<ItemRect> groups = new FastList<ItemRect>();
private Rect dropArea = new Rect();
private bool addSound = false;
private int deleteSoundIdx = -1;
private int dupeSoundIdx = -1;
private bool sortSounds = false;
private bool moveQueued = false;
private int origGroup = -1;
private int origIndex = -1;
private int moveToGroup = -1;
/*
-----------------------
DrawDefaultProperties()
-----------------------
*/
void DrawDefaultProperties() {
BeginContents();
if ( DrawHeader( "Default Properties", true ) ) {
EditorGUILayout.BeginVertical( GUI.skin.box );
EditorGUILayout.PropertyField( serializedObject.FindProperty( "makePersistent" ), new GUIContent( "Don't Destroy on Load" ) );
EditorGUILayout.PropertyField( serializedObject.FindProperty( "enableSpatializedAudio" ), new GUIContent( "Enable Spatialized Audio" ) );
EditorGUILayout.PropertyField( serializedObject.FindProperty( "enableSpatializedFastOverride" ), new GUIContent( "Force Disable Reflections" ) );
EditorGUILayout.PropertyField( serializedObject.FindProperty( "audioMixer" ), new GUIContent( "Master Audio Mixer" ) );
EditorGUILayout.PropertyField( serializedObject.FindProperty( "defaultMixerGroup" ), new GUIContent( "Pooled Emitter Mixer Group" ) );
EditorGUILayout.PropertyField( serializedObject.FindProperty( "reservedMixerGroup" ), new GUIContent( "Reserved Emitter Mixer Group" ) );
EditorGUILayout.PropertyField( serializedObject.FindProperty( "voiceChatMixerGroup" ), new GUIContent( "Voice Chat Mixer Group" ) );
EditorGUILayout.PropertyField( serializedObject.FindProperty( "verboseLogging" ), new GUIContent( "Verbose Logging" ) );
EditorGUILayout.PropertyField( serializedObject.FindProperty( "maxSoundEmitters" ), new GUIContent( "Max Sound Emitters" ) );
EditorGUILayout.PropertyField( serializedObject.FindProperty( "volumeSoundFX" ), new GUIContent( "Default Volume" ) );
EditorGUILayout.PropertyField( serializedObject.FindProperty( "soundFxFadeSecs" ), new GUIContent( "Sound FX Fade Secs" ) );
EditorGUILayout.PropertyField( serializedObject.FindProperty( "audioMinFallOffDistance" ), new GUIContent( "Minimum Falloff Distance" ) );
EditorGUILayout.PropertyField( serializedObject.FindProperty( "audioMaxFallOffDistance" ), new GUIContent( "Maximum Falloff Distance" ) );
EditorGUILayout.EndVertical();
serializedObject.ApplyModifiedProperties();
}
EndContents();
}
/*
-----------------------
DrawSoundGroupProperties()
-----------------------
*/
void DrawSoundGroupProperties() {
if ( selectedGroup == -1 ) {
return;
}
SerializedProperty soundGroupsArray = serializedObject.FindProperty( "soundGroupings" );
if ( selectedGroup >= soundGroupsArray.arraySize ) {
return;
}
SerializedProperty soundGroup = soundGroupsArray.GetArrayElementAtIndex( selectedGroup );
string soundGroupName = soundGroup.FindPropertyRelative( "name" ).stringValue;
if ( DrawHeader( string.Format( "{0} Properties", soundGroupName ), true ) ) {
EditorGUILayout.BeginVertical( GUI.skin.box );
EditorGUILayout.PropertyField( soundGroup.FindPropertyRelative( "mixerGroup" ), new GUIContent( "Override Mixer Group", "Leave empty to use the Audio Manager's default mixer group" ) );
if ( !Application.isPlaying ) {
EditorGUILayout.PropertyField( soundGroup.FindPropertyRelative( "maxPlayingSounds" ), new GUIContent( "Max Playing Sounds Limit", "Max playing sounds for this sound group, 0 = no limit" ) );
} else {
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField( soundGroup.FindPropertyRelative( "maxPlayingSounds" ), new GUIContent( "Max Playing Sounds Limit", "Max playing sounds for this sound group, 0 = no limit" ) );
// cast to the actual object
int playingSounds = soundGroup.FindPropertyRelative( "playingSoundCount" ).intValue;
EditorGUILayout.LabelField( string.Format( "Playing: {0}", playingSounds ), GUILayout.Width( 80.0f ) );
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.PropertyField( soundGroup.FindPropertyRelative( "preloadAudio" ), new GUIContent( "Preload Audio Clips", "Default = No special preloading, Preload = Audio clips are set to 'Preload', Manual Preload = Audio clips are set to not 'Preload'" ) );
EditorGUILayout.PropertyField( soundGroup.FindPropertyRelative( "volumeOverride" ), new GUIContent( "Volume Override", "All sounds played in this group will have volume scaled by this amount" ) );
if ( soundGroup.FindPropertyRelative( "volumeOverride" ).floatValue == 0.0f ) {
EditorGUILayout.HelpBox( "With a volumeOverride of 0.0, these sounds will not play!", MessageType.Warning );
}
EditorGUILayout.EndVertical();
serializedObject.ApplyModifiedProperties();
}
}
/*
-----------------------
DrawCategories()
-----------------------
*/
void DrawCategories( Event e ) {
// do any housework before we start drawing
if ( moveQueued ) {
// make a temp copy
List<SoundFX> origSoundList = new List<SoundFX>( audioManager.soundGroupings[origGroup].soundList );
SoundFX temp = origSoundList[origIndex];
List<SoundFX> moveToSoundList = new List<SoundFX>( audioManager.soundGroupings[moveToGroup].soundList );
// add it to the move to group
moveToSoundList.Add( temp );
audioManager.soundGroupings[moveToGroup].soundList = moveToSoundList.ToArray();
// and finally, remove it from the original group
origSoundList.RemoveAt( origIndex );
audioManager.soundGroupings[origGroup].soundList = origSoundList.ToArray();
Debug.Log( "> Moved '" + temp.name + "' from " + "'" + audioManager.soundGroupings[origGroup].name + "' to '" + audioManager.soundGroupings[moveToGroup].name );
MarkDirty();
moveQueued = false;
}
// switch to the next group
if ( nextGroup > -1 ) {
selectedGroup = nextGroup;
nextGroup = -1;
}
// add a sound
if ( addSound ) {
List<SoundFX> soundList = new List<SoundFX>( audioManager.soundGroupings[selectedGroup].soundList );
SoundFX soundFX = new SoundFX();
soundFX.name = audioManager.soundGroupings[selectedGroup].name.ToLower() + "_new_unnamed_sound_fx";
soundList.Add( soundFX );
audioManager.soundGroupings[selectedGroup].soundList = soundList.ToArray();
MarkDirty();
addSound = false;
}
// sort the sounds
if ( sortSounds ) {
List<SoundFX> soundList = new List<SoundFX>( audioManager.soundGroupings[selectedGroup].soundList );
soundList.Sort( delegate ( SoundFX sfx1, SoundFX sfx2 ) { return string.Compare( sfx1.name, sfx2.name ); } );
audioManager.soundGroupings[selectedGroup].soundList = soundList.ToArray();
MarkDirty();
sortSounds = false;
}
// delete a sound
if ( deleteSoundIdx > -1 ) {
List<SoundFX> soundList = new List<SoundFX>( audioManager.soundGroupings[selectedGroup].soundList );
soundList.RemoveAt( deleteSoundIdx );
audioManager.soundGroupings[selectedGroup].soundList = soundList.ToArray();
MarkDirty();
deleteSoundIdx = -1;
}
// duplicate a sound
if ( dupeSoundIdx > -1 ) {
List<SoundFX> soundList = new List<SoundFX>( audioManager.soundGroupings[selectedGroup].soundList );
SoundFX origSoundFX = soundList[dupeSoundIdx];
// clone this soundFX
string json = JsonUtility.ToJson( origSoundFX );
SoundFX soundFX = JsonUtility.FromJson<SoundFX>( json );
soundFX.name += "_duplicated";
soundList.Insert( dupeSoundIdx + 1, soundFX );
audioManager.soundGroupings[selectedGroup].soundList = soundList.ToArray();
MarkDirty();
dupeSoundIdx = -1;
}
if ( e.type == EventType.Repaint ) {
groups.Clear();
}
GUILayout.Space( 6f );
Color defaultColor = GUI.contentColor;
BeginContents();
if ( DrawHeader( "Sound FX Groups", true ) ) {
EditorGUILayout.BeginVertical( GUI.skin.box );
soundGroups.Clear();
for ( int i = 0; i < audioManager.soundGroupings.Length; i++ ) {
soundGroups.Add( audioManager.soundGroupings[i] );
}
for ( int i = 0; i < soundGroups.size; i++ ) {
EditorGUILayout.BeginHorizontal();
{
if ( i == selectedGroup ) {
GUI.contentColor = ( i == editGroup ) ? Color.white : Color.yellow;
} else {
GUI.contentColor = defaultColor;
}
if ( ( e.type == EventType.KeyDown ) && ( ( e.keyCode == KeyCode.Return ) || ( e.keyCode == KeyCode.KeypadEnter ) ) ) {
// toggle editing
if ( editGroup >= 0 ) {
editGroup = -1;
}
Event.current.Use();
}
if ( i == editGroup ) {
soundGroups[i].name = GUILayout.TextField( soundGroups[i].name, GUILayout.MinWidth( Screen.width - 80f ) );
} else {
GUILayout.Label( soundGroups[i].name, ( i == selectedGroup ) ? EditorStyles.whiteLabel : EditorStyles.label, GUILayout.ExpandWidth( true ) );
}
GUILayout.FlexibleSpace();
if ( GUILayout.Button( GUIContent.none, "OL Minus", GUILayout.Width(17f) ) ) { // minus button
if ( EditorUtility.DisplayDialog( "Delete '" + soundGroups[i].name + "'", "Are you sure you want to delete the selected sound group?", "Continue", "Cancel" ) ) {
soundGroups.RemoveAt( i );
MarkDirty();
}
}
}
EditorGUILayout.EndHorizontal();
// build a list of items
Rect lastRect = GUILayoutUtility.GetLastRect();
if ( e.type == EventType.Repaint ) {
groups.Add ( new ItemRect( i, lastRect, null ) );
}
if ( ( e.type == EventType.MouseDown ) && lastRect.Contains( e.mousePosition ) ) {
if ( ( i != selectedGroup ) || ( e.clickCount == 2 ) ) {
nextGroup = i;
if ( e.clickCount == 2 ) {
editGroup = i;
} else if ( editGroup != nextGroup ) {
editGroup = -1;
}
Repaint();
}
}
}
// add the final plus button
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if ( GUILayout.Button( GUIContent.none, "OL Plus", GUILayout.Width(17f) ) ) { // plus button
soundGroups.Add( new SoundGroup( "unnamed sound group" ) );
selectedGroup = editGroup = soundGroups.size - 1;
MarkDirty();
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
// reset the color
GUI.contentColor = defaultColor;
// the sort and import buttons
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if ( GUILayout.Button( "Sort", GUILayout.Width( 70f ) ) ) {
soundGroups.Sort( delegate( SoundGroup sg1, SoundGroup sg2 ) { return string.Compare( sg1.name, sg2.name ); } );
MarkDirty();
}
EditorGUILayout.EndHorizontal();
// draw a rect around the selected item
if ( ( selectedGroup >= 0 ) && ( selectedGroup < groups.size ) ) {
EditorGUI.DrawRect( groups[selectedGroup].rect, new Color( 1f, 1f, 1f, 0.06f ) );
}
// finally move the sound groups back into the audio manager
if ( soundGroups.size > 0 ) {
audioManager.soundGroupings = soundGroups.ToArray();
}
// calculate the drop area rect
if ( ( e.type == EventType.Repaint ) && ( groups.size > 0 ) ) {
dropArea.x = groups[0].rect.x;
dropArea.y = groups[0].rect.y;
dropArea.width = groups[0].rect.width;
dropArea.height = ( groups[groups.size-1].rect.y - groups[0].rect.y ) + groups[groups.size-1].rect.height;
}
}
// draw the sound group properties now
DrawSoundGroupProperties();
EndContents();
EditorGUILayout.HelpBox("Create and delete sound groups by clicking + and - respectively. Double click to rename sound groups. Drag and drop sounds from below to the groups above to move them.", MessageType.Info);
}
public class CustomDragData{
public int originalGroupIndex;
public int originalIndex;
public SerializedProperty originalProperty;
}
public class ItemRect {
public ItemRect( int index, Rect rect, SerializedProperty prop ) {
this.index = index;
this.rect = rect;
this.prop = prop;
}
public int index;
public Rect rect;
public SerializedProperty prop;
}
private FastList<ItemRect> items = new FastList<ItemRect>();
/*
-----------------------
CreateStyles()
-----------------------
*/
void CreateStyles() {
if ( customDividerStyle == null ) {
customDividerStyle = new GUIStyle( EditorStyles.label );
customDividerStyle.normal.background = MakeTex( 4, 4, new Color( 0.5f, 0.5f, 0.5f, 0.25f ) );
customDividerStyle.margin.right -= 16;
}
}
/*
-----------------------
MakeTex()
-----------------------
*/
private Texture2D MakeTex( int width, int height, Color col ) {
Color[] pix = new Color[width*height];
for ( int i = 0; i < pix.Length; i++ )
pix[i] = col;
Texture2D result = new Texture2D(width, height);
result.SetPixels( pix );
result.Apply();
return result;
}
/*
-----------------------
DrawSoundEffects()
-----------------------
*/
void DrawSoundEffects( Event e ) {
if ( ( selectedGroup < 0 ) || ( audioManager.soundGroupings.Length == 0 ) || ( selectedGroup >= audioManager.soundGroupings.Length ) ) {
return;
}
if ( e.type == EventType.Repaint ) {
items.Clear();
} else {
CheckStartDrag( e );
}
BeginContents();
if ( DrawHeader( "Sound Effects", true ) ) {
GUILayout.Space(3f);
GUILayout.BeginVertical( GUI.skin.box );
SerializedProperty soundGroupsArray = serializedObject.FindProperty( "soundGroupings" );
SerializedProperty soundGroup = soundGroupsArray.GetArrayElementAtIndex( selectedGroup );
SerializedProperty soundList = soundGroup.FindPropertyRelative( "soundList" );
CreateStyles();
Rect prevRect = new Rect();
if ( soundList.arraySize > 0 ) {
// show all the sounds
for ( int i = 0; i < soundList.arraySize; i++ ) {
EditorGUI.indentLevel = 1;
SerializedProperty soundFX = soundList.GetArrayElementAtIndex( i );
SerializedProperty visToggle = soundFX.FindPropertyRelative( "visibilityToggle" );
EditorGUILayout.BeginHorizontal( customDividerStyle );
{
string soundFXName = soundFX.FindPropertyRelative( "name" ).stringValue;
// save the visibility state
visToggle.boolValue = EditorGUILayout.Foldout( visToggle.boolValue, soundFXName );
// play button
if ( GUILayout.Button( "\u25BA", GUILayout.Width( 17f ), GUILayout.Height( 16f ) ) ) {
if ( AudioManager.IsSoundPlaying( soundFXName ) ) {
AudioManager.StopSound( soundFXName );
} else {
AudioManager.PlaySound( soundFXName );
}
}
}
EditorGUILayout.EndHorizontal();
if ( visToggle.boolValue ) {
EditorGUILayout.PropertyField( soundFX, true );
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if ( GUILayout.Button( "Delete FX", GUILayout.Width( Screen.width / 3.0f ) ) ) {
if ( EditorUtility.DisplayDialog( "Delete " + soundFX.displayName, "Are you sure?", "Yes", "No!" ) ) {
deleteSoundIdx = i;
}
}
if ( GUILayout.Button( "Duplicate FX", GUILayout.Width( Screen.width / 3.0f ) ) ) {
dupeSoundIdx = i;
}
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
GUILayout.Space( 10.0f );
}
if ( e.type == EventType.Repaint ) {
// GetLastRect() is now returning the last rect drawn in the property drawer,
// not the rect used for the entire SoundFX
Rect curRect = prevRect;
curRect.y = prevRect.y + EditorGUIUtility.singleLineHeight;
Rect lastRect = GUILayoutUtility.GetLastRect();
curRect.height = ( lastRect.y + lastRect.height ) - curRect.y;
curRect.width = Screen.width;
items.Add( new ItemRect( i, curRect, soundFX ) );
}
prevRect = GUILayoutUtility.GetLastRect();
}
} else {
EditorGUILayout.LabelField( " " );
}
GUILayout.EndVertical();
GUILayout.Space(3f);
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if ( GUILayout.Button( "Add FX", GUILayout.Width( 70f ) ) ) {
//soundList.InsertArrayElementAtIndex( soundList.arraySize );
//MarkDirty();
addSound = true;
}
if ( GUILayout.Button( "Sort", GUILayout.Width( 70f ) ) ) {
sortSounds = true;
}
EditorGUILayout.EndHorizontal();
}
EndContents();
UpdateDrag( e );
}
/*
-----------------------
CheckStartDrag()
-----------------------
*/
void CheckStartDrag( Event e ) {
if ( ( e.type == EventType.MouseDrag ) && ( e.button == 0 ) ) {
for ( int i = 0; i < items.size; i++ ) {
if ( items[i].rect.Contains( e.mousePosition ) ) {
DragAndDrop.PrepareStartDrag();// reset data
CustomDragData dragData = new CustomDragData();
dragData.originalGroupIndex = selectedGroup;
dragData.originalIndex = items[i].index;
dragData.originalProperty = items[i].prop;
DragAndDrop.SetGenericData( dragDropIdentifier, dragData );
DragAndDrop.objectReferences = new Object[0];
DragAndDrop.StartDrag( dragData.originalProperty.FindPropertyRelative( "name" ).stringValue );
e.Use();
}
}
}
}
/*
-----------------------
FindGroupIndex()
-----------------------
*/
int FindGroupIndex( Event e ) {
for ( int i = 0; i < groups.size; i++ ) {
if ( groups[i].rect.Contains( e.mousePosition ) ) {
return i;
}
}
return -1;
}
/*
-----------------------
UpdateDrag()
-----------------------
*/
void UpdateDrag( Event e ) {
CustomDragData dragData = DragAndDrop.GetGenericData( dragDropIdentifier ) as CustomDragData;
if ( dragData == null ) {
return;
}
int groupIndex = FindGroupIndex( e );
switch ( e.type ) {
case EventType.DragUpdated:
if ( ( groupIndex >= 0 ) && ( groupIndex != selectedGroup ) ) {
DragAndDrop.visualMode = DragAndDropVisualMode.Move;
} else {
DragAndDrop.visualMode = DragAndDropVisualMode.Rejected;
}
e.Use();
break;
case EventType.Repaint:
if ( ( DragAndDrop.visualMode == DragAndDropVisualMode.None ) ||
( DragAndDrop.visualMode == DragAndDropVisualMode.Rejected ) ) {
break;
}
if ( groupIndex >= 0 && groupIndex < groups.size ) {
EditorGUI.DrawRect( groups[groupIndex].rect, new Color( 0f, 1f, 0f, 0.1f ) );
}
break;
case EventType.DragPerform:
DragAndDrop.AcceptDrag();
// queue the sound FX move
QueueSoundFXMove( dragData.originalGroupIndex, dragData.originalIndex, groupIndex );
e.Use();
break;
case EventType.MouseUp:
// in case MouseDrag never occurred:
DragAndDrop.PrepareStartDrag();
break;
}
}
/*
-----------------------
QueueSoundFXMove()
-----------------------
*/
void QueueSoundFXMove( int origGroupIndex, int origSoundIndex, int newGroupIndex ) {
moveQueued = true;
origGroup = origGroupIndex;
origIndex = origSoundIndex;
moveToGroup = newGroupIndex;
}
/*
-----------------------
DrawHeader()
-----------------------
*/
static public bool DrawHeader (string text) { return DrawHeader(text, text, false); }
static public bool DrawHeader (string text, string key) { return DrawHeader(text, key, false); }
static public bool DrawHeader (string text, bool forceOn) { return DrawHeader(text, text, forceOn); }
static public bool DrawHeader( string text, string key, bool forceOn ) {
bool state = EditorPrefs.GetBool(key, true);
GUILayout.Space(3f);
if (!forceOn && !state) GUI.backgroundColor = new Color(0.8f, 0.8f, 0.8f);
GUILayout.BeginHorizontal();
GUILayout.Space(3f);
GUI.changed = false;
text = "<b><size=11>" + text + "</size></b>";
if (state) text = "\u25BC " + text;
else text = "\u25B6 " + text;
if (!GUILayout.Toggle(true, text, "dragtab", GUILayout.MinWidth(20f))) state = !state;
if (GUI.changed) EditorPrefs.SetBool(key, state);
GUILayout.Space(2f);
GUILayout.EndHorizontal();
GUI.backgroundColor = Color.white;
if (!forceOn && !state) GUILayout.Space(3f);
return state;
}
/*
-----------------------
BeginContents()
-----------------------
*/
static public void BeginContents() {
GUILayout.BeginHorizontal();
GUILayout.Space(4f);
EditorGUILayout.BeginHorizontal(GUILayout.MinHeight(10f));
GUILayout.BeginVertical();
GUILayout.Space(2f);
}
/*
-----------------------
EndContents()
-----------------------
*/
static public void EndContents() {
GUILayout.Space(3f);
GUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
GUILayout.Space(3f);
GUILayout.EndHorizontal();
GUILayout.Space(3f);
}
}
} // namespace OVR

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9f04a80514947486d9793cab0005447f
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -0,0 +1,70 @@
using UnityEditor;
using UnityEngine;
namespace OVR
{
/*
-----------------------
MinMaxPropertyDrawer
-----------------------
*/
[CustomPropertyDrawer (typeof (MinMaxAttribute))]
public class MinMaxPropertyDrawer : PropertyDrawer {
// Provide easy access to the MinMaxAttribute for reading information from it.
MinMaxAttribute minMax { get { return ((MinMaxAttribute)attribute); } }
/*
-----------------------
GetPropertyHeight()
-----------------------
*/
public override float GetPropertyHeight( SerializedProperty prop, GUIContent label ) {
return base.GetPropertyHeight( prop, label ) * 2f;
}
/*
-----------------------
OnGUI()
-----------------------
*/
public override void OnGUI( Rect position, SerializedProperty property, GUIContent label ) {
Rect sliderPosition = EditorGUI.PrefixLabel( position, label );
SerializedProperty min = property.FindPropertyRelative( "x" );
SerializedProperty max = property.FindPropertyRelative( "y" );
// draw the range and the reset button first so that the slider doesn't grab all the input
Rect rangePosition = sliderPosition;
rangePosition.y += rangePosition.height * 0.5f;
rangePosition.height *= 0.5f;
Rect contentPosition = rangePosition;
EditorGUI.indentLevel = 0;
EditorGUIUtility.labelWidth = 30f;
contentPosition.width *= 0.3f;
EditorGUI.PropertyField(contentPosition, min, new GUIContent( "Min" ) );
contentPosition.x += contentPosition.width + 20f;
EditorGUI.PropertyField( contentPosition, max, new GUIContent( "Max" ) );
contentPosition.x += contentPosition.width + 20f;
contentPosition.width = 50.0f;
if ( GUI.Button( contentPosition, "Reset" ) ) {
min.floatValue = minMax.minDefaultVal;
max.floatValue = minMax.maxDefaultVal;
}
float minValue = min.floatValue;
float maxValue = max.floatValue;
#if UNITY_2017_1_OR_NEWER
EditorGUI.MinMaxSlider( sliderPosition, GUIContent.none, ref minValue, ref maxValue, minMax.min, minMax.max );
#else
EditorGUI.MinMaxSlider( GUIContent.none, sliderPosition, ref minValue, ref maxValue, minMax.min, minMax.max );
#endif
// round to readable values
min.floatValue = Mathf.Round( minValue / 0.01f ) * 0.01f;
max.floatValue = Mathf.Round( maxValue / 0.01f ) * 0.01f;
}
}
} // namespace OVR

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 225aed143a64c4a6a93f3a07656ac5cd
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -0,0 +1,50 @@
using UnityEditor;
using UnityEngine;
namespace OVR
{
/*
-----------------------
MixerSnapshotPropertyDrawer
-----------------------
*/
[CustomPropertyDrawer( typeof( MixerSnapshot ) )]
public class MixerSnapshotPropertyDrawer : PropertyDrawer {
// Draw the property inside the given rect
public override void OnGUI( Rect position, SerializedProperty property, GUIContent label ) {
// Using BeginProperty / EndProperty on the parent property means that
// prefab override logic works on the entire property.
EditorGUI.BeginProperty( position, label, property );
// Draw label
position = EditorGUI.PrefixLabel( position, GUIUtility.GetControlID( FocusType.Passive ), label );
// Don't make child fields be indented
var indent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
EditorGUIUtility.labelWidth = 65;
float width = ( position.width - 15.0f ) / 2.0f;
// Calculate rects
var srcRect = new Rect( position.x, position.y, width + 20, position.height ); position.x += width + 25.0f;
var destRect = new Rect( position.x, position.y, width - 60, position.height ); position.x += width - 60.0f;
var secsRect = new Rect( position.x, position.y, 40, position.height );
// Draw fields - pass GUIContent.none to each so they are drawn without labels
EditorGUI.PropertyField( srcRect, property.FindPropertyRelative( "snapshot" ), GUIContent.none );
EditorGUI.PropertyField( destRect, property.FindPropertyRelative( "transitionTime" ), new GUIContent( "Transition" ) );
EditorGUI.LabelField( secsRect, new GUIContent( "sec(s)" ) );
// Set indent back to what it was
EditorGUI.indentLevel = indent;
EditorGUI.EndProperty();
}
}
} // namespace OVR

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 82a91f30f2305c14dbfd2cc3c289dc59
timeCreated: 1472247018
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,66 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
namespace OVR
{
/*
-----------------------
OSPPropsPropertyDrawer
-----------------------
*/
[CustomPropertyDrawer(typeof(OSPProps))]
public class OSPPropsPropertyDrawer : PropertyDrawer {
static float lineHeight = EditorGUIUtility.singleLineHeight + 2.0f;
static float indent = 32.0f;
// TODO - some day just enumerate these
static string[] props = new string[] { "useFastOverride", "gain", "enableInvSquare", "volumetric" , "invSquareFalloff" };
static string[] names = new string[] { "Reflections Enabled", "Gain", "Enable Oculus Atten.", "Volumetric", "Range" };
static int[] lines = new int[] { 1, 1, 1, 1, 2, 2 };
/*
-----------------------
OnGUI()
-----------------------
*/
public override void OnGUI( Rect position, SerializedProperty prop, GUIContent label ) {
SerializedProperty playSpatializedProp = prop.FindPropertyRelative("enableSpatialization");
position.height = lineHeight;
EditorGUI.PropertyField( position, playSpatializedProp );
if ( playSpatializedProp.boolValue ) {
position.y += lineHeight + 4.0f;
Rect posLine = position;
posLine.x += indent;
posLine.width -= indent;
posLine.height = 1f;
GUI.Box( posLine, "" );
position.y -= 10.0f;
for ( int i = 0; i < props.Length; i++ ) {
position.y += lineHeight;
position.height = ( lineHeight * lines[i] );
SerializedProperty sibling = prop.FindPropertyRelative( props[i] );
EditorGUI.PropertyField( position, sibling, new GUIContent( names[i] ) );
}
}
}
/*
-----------------------
GetPropertyHeight()
-----------------------
*/
public override float GetPropertyHeight (SerializedProperty prop, GUIContent label) {
SerializedProperty playSpatializedProp = prop.FindPropertyRelative("enableSpatialization");
if ( !playSpatializedProp.boolValue ) {
return base.GetPropertyHeight( prop, label );
} else {
return base.GetPropertyHeight( prop, label ) + ( lineHeight * ( props.Length + 1 ) ) + 16.0f;
}
}
}
} // namespace OVR

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9fc79251b168140d68851f1e8c283514
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -0,0 +1,18 @@
{
"name": "Oculus.AudioManager.Audio.Editor",
"rootNamespace": "",
"references": [
"Oculus.AudioManager"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: e2367297a6c431949a05cf8078ad917d
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,67 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
namespace OVR
{
/*
-----------------------
SoundFXPropertyDrawer
-----------------------
*/
[CustomPropertyDrawer(typeof(SoundFX))]
public class SoundFXPropertyDrawer : PropertyDrawer {
static float lineHeight = EditorGUIUtility.singleLineHeight + 2.0f;
static string[] props = new string[] { "name", "playback", "volume", "pitchVariance", "falloffDistance", "falloffCurve", "reverbZoneMix", "spread", "pctChanceToPlay", "priority", "delay", "looping", "ospProps", "soundClips" };
/*
-----------------------
OnGUI()
-----------------------
*/
public override void OnGUI( Rect position, SerializedProperty prop, GUIContent label ) {
EditorGUILayout.BeginVertical();
for ( int i = 0; i < props.Length; i++ ) {
EditorGUI.indentLevel = 2;
SerializedProperty property = prop.FindPropertyRelative( props[i] );
if ( props[i] == "reverbZoneMix" ) {
EditorGUILayout.BeginHorizontal();
SerializedProperty reverbCurve = prop.FindPropertyRelative( "reverbZoneMix" );
EditorGUILayout.PropertyField( reverbCurve, true, GUILayout.Width( Screen.width - 130.0f ) );
if ( GUILayout.Button( "Reset", GUILayout.Width( 50.0f ) ) ) {
reverbCurve.animationCurveValue = new AnimationCurve( new Keyframe[2] { new Keyframe( 0f, 1.0f ), new Keyframe( 1f, 1f ) } );
}
EditorGUILayout.EndHorizontal();
} else {
EditorGUILayout.PropertyField( property, true, GUILayout.Width( Screen.width - 80.0f ) );
position.y += lineHeight + 4.0f;
if ( props[i] == "falloffCurve" ) {
if ( property.enumValueIndex == (int)AudioRolloffMode.Custom ) {
EditorGUILayout.PropertyField( prop.FindPropertyRelative( "volumeFalloffCurve" ), true, GUILayout.Width( Screen.width - 80.0f ) );
position.y += lineHeight + 4.0f;
}
}
}
}
EditorGUILayout.EndVertical();
GUILayout.Space( 5.0f );
}
/*
-----------------------
GetPropertyHeight()
-----------------------
*/
public override float GetPropertyHeight (SerializedProperty prop, GUIContent label) {
return base.GetPropertyHeight( prop, label );
}
}
} // namespace OVR

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: e791ce392b6937f47b1f7c90c6b402db
timeCreated: 1468857307
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,62 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
namespace OVR
{
/*
-----------------------
SoundFXRefPropertyDrawer
-----------------------
*/
[CustomPropertyDrawer(typeof(SoundFXRef))]
public class SoundFXRefPropertyDrawer : PropertyDrawer {
static private GUIStyle disabledStyle = null;
/*
-----------------------
OnGUI()
-----------------------
*/
public override void OnGUI( Rect position, SerializedProperty prop, GUIContent label ) {
int idx = 0;
Rect buttonPosition = position;
buttonPosition.x = position.x + position.width - 40f;
buttonPosition.width = 20f;
position.width = buttonPosition.x - position.x - 2f;
SerializedProperty nameProp = prop.FindPropertyRelative( "soundFXName" );
if ( AudioManager.GetGameObject() == null ) {
if ( disabledStyle == null ) {
disabledStyle = new GUIStyle();
disabledStyle.normal.textColor = Color.gray;
}
EditorGUI.LabelField(position, label.text, nameProp.stringValue, disabledStyle );
}
else {
string[] soundFXNames = AudioManager.GetSoundFXNames( nameProp.stringValue, out idx );
idx = EditorGUI.Popup( position, label.text, idx, soundFXNames );
nameProp.stringValue = AudioManager.NameMinusGroup( soundFXNames[idx] );
// play button
if ( GUI.Button( buttonPosition, "\u25BA" ) ) {
if ( AudioManager.IsSoundPlaying( nameProp.stringValue ) ) {
AudioManager.StopSound( nameProp.stringValue );
} else {
AudioManager.PlaySound( nameProp.stringValue );
}
}
buttonPosition.x += 22.0f;
// select audio manager
if ( GUI.Button( buttonPosition, "\u2630" ) ) {
Selection.activeGameObject = AudioManager.GetGameObject();
}
}
}
}
} // namespace OVR

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6204bcaba636340b48858c9f10ab9016
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -0,0 +1,26 @@
using UnityEngine;
namespace OVR
{
/*
-----------------------
MinMaxAttribute
-----------------------
*/
public class MinMaxAttribute : PropertyAttribute {
public float minDefaultVal = 1.0f;
public float maxDefaultVal = 1.0f;
public float min = 0.0f;
public float max = 1.0f;
public MinMaxAttribute( float minDefaultVal, float maxDefaultVal, float min, float max ) {
this.minDefaultVal = minDefaultVal;
this.maxDefaultVal = maxDefaultVal;
this.min = min;
this.max = max;
}
}
} // namespace OVR

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 15126f023faf44286a08bdb5bdbdb6e7
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -0,0 +1,433 @@
using UnityEngine;
using UnityEngine.Audio;
using System.Collections;
namespace OVR
{
/*
-----------------------
SoundEmitter()
-----------------------
*/
public class SoundEmitter : MonoBehaviour {
public enum FadeState {
Null,
FadingIn,
FadingOut,
Ducking,
}
// OPTIMIZE
public float volume { get { return audioSource.volume; } set { audioSource.volume = value; } }
public float pitch { get { return audioSource.pitch; } set { audioSource.pitch = value; } }
public AudioClip clip { get { return audioSource.clip; } set { audioSource.clip = value; } }
public float time { get { return audioSource.time; } set { audioSource.time = value; } }
public float length { get { return ( audioSource.clip != null ) ? audioSource.clip.length : 0.0f; } }
public bool loop { get { return audioSource.loop; } set { audioSource.loop = value; } }
public bool mute { get { return audioSource.mute; } set { audioSource.mute = value; } }
public AudioVelocityUpdateMode velocityUpdateMode { get { return audioSource.velocityUpdateMode; } set { audioSource.velocityUpdateMode = value; } }
public bool isPlaying { get { return audioSource.isPlaying; } }
public EmitterChannel channel = EmitterChannel.Reserved;
public bool disableSpatialization = false;
private FadeState state = FadeState.Null;
[System.NonSerialized]
[HideInInspector]
public AudioSource audioSource = null;
[System.NonSerialized]
[HideInInspector]
public SoundPriority priority = SoundPriority.Default;
[System.NonSerialized]
[HideInInspector]
public ONSPAudioSource osp = null;
[System.NonSerialized]
[HideInInspector]
public float endPlayTime = 0.0f;
private Transform lastParentTransform = null;
[System.NonSerialized]
[HideInInspector]
public float defaultVolume = 1.0f;
[System.NonSerialized]
[HideInInspector]
public Transform defaultParent = null;
[System.NonSerialized]
[HideInInspector]
public int originalIdx = -1;
[System.NonSerialized]
[HideInInspector]
public System.Action onFinished = null;
[System.NonSerialized]
[HideInInspector]
public System.Action<object> onFinishedObject = null;
[System.NonSerialized]
[HideInInspector]
public object onFinishedParam;
[System.NonSerialized]
[HideInInspector]
public SoundGroup playingSoundGroup = null;
/*
-----------------------
Awake()
-----------------------
*/
void Awake() {
// unity defaults to 'playOnAwake = true'
audioSource = GetComponent<AudioSource>();
if ( audioSource == null ) {
audioSource = gameObject.AddComponent<AudioSource>();
}
// is the spatialized audio enabled?
if ( AudioManager.enableSpatialization && !disableSpatialization ) {
osp = GetComponent<ONSPAudioSource>();
if ( osp == null ) {
osp = gameObject.AddComponent<ONSPAudioSource>();
}
}
audioSource.playOnAwake = false;
audioSource.Stop();
}
/*
-----------------------
SetPlayingSoundGroup()
-----------------------
*/
public void SetPlayingSoundGroup( SoundGroup soundGroup ) {
playingSoundGroup = soundGroup;
if ( soundGroup != null ) {
soundGroup.IncrementPlayCount();
}
}
/*
-----------------------
SetOnFinished()
-----------------------
*/
public void SetOnFinished( System.Action onFinished ) {
this.onFinished = onFinished;
}
/*
-----------------------
SetOnFinished()
-----------------------
*/
public void SetOnFinished( System.Action<object> onFinished, object obj ) {
onFinishedObject = onFinished;
onFinishedParam = obj;
}
/*
-----------------------
SetChannel()
-----------------------
*/
public void SetChannel( int _channel ) {
channel = (EmitterChannel)_channel;
}
/*
-----------------------
SetDefaultParent()
-----------------------
*/
public void SetDefaultParent( Transform parent ) {
defaultParent = parent;
}
/*
-----------------------
SetAudioMixer()
-----------------------
*/
public void SetAudioMixer( AudioMixerGroup _mixer ) {
if ( audioSource != null ) {
audioSource.outputAudioMixerGroup = _mixer;
}
}
/*
-----------------------
IsPlaying()
-----------------------
*/
public bool IsPlaying() {
if ( loop && audioSource.isPlaying ) {
return true;
}
return endPlayTime > Time.time;
}
/*
-----------------------
Play()
-----------------------
*/
public void Play() {
// overrides everything
state = FadeState.Null;
endPlayTime = Time.time + length;
StopAllCoroutines();
audioSource.Play();
}
/*
-----------------------
Pause()
-----------------------
*/
public void Pause() {
// overrides everything
state = FadeState.Null;
StopAllCoroutines();
audioSource.Pause();
}
/*
-----------------------
Stop()
-----------------------
*/
public void Stop() {
// overrides everything
state = FadeState.Null;
StopAllCoroutines();
if ( audioSource != null ) {
audioSource.Stop();
}
if ( onFinished != null ) {
onFinished();
onFinished = null;
}
if ( onFinishedObject != null ) {
onFinishedObject( onFinishedParam );
onFinishedObject = null;
}
if ( playingSoundGroup != null ) {
playingSoundGroup.DecrementPlayCount();
playingSoundGroup = null;
}
}
/*
-----------------------
GetSampleTime()
-----------------------
*/
int GetSampleTime() {
return audioSource.clip.samples - audioSource.timeSamples;
}
/*
-----------------------
ParentTo()
-----------------------
*/
public void ParentTo( Transform parent ) {
if ( lastParentTransform != null ) {
Debug.LogError( "[SoundEmitter] You must detach the sound emitter before parenting to another object!" );
return;
}
lastParentTransform = transform.parent;
transform.parent = parent;
}
/*
-----------------------
DetachFromParent()
-----------------------
*/
public void DetachFromParent() {
if ( lastParentTransform == null ) {
transform.parent = defaultParent;
return;
}
transform.parent = lastParentTransform;
lastParentTransform = null;
}
/*
-----------------------
ResetParent()
-----------------------
*/
public void ResetParent( Transform parent ) {
transform.parent = parent;
lastParentTransform = null;
}
/*
-----------------------
SyncTo()
-----------------------
*/
public void SyncTo( SoundEmitter other, float fadeTime, float toVolume ) {
StartCoroutine( DelayedSyncTo( other, fadeTime, toVolume ) );
}
/*
-----------------------
DelayedSyncTo()
have to wait until the end of frame to do proper sync'ing
-----------------------
*/
IEnumerator DelayedSyncTo( SoundEmitter other, float fadeTime, float toVolume ) {
yield return new WaitForEndOfFrame();
//audio.timeSamples = other.GetSampleTime();
//audio.time = Mathf.Min( Mathf.Max( 0.0f, other.time - other.length ), other.time );
audioSource.time = other.time;
audioSource.Play();
FadeTo( fadeTime, toVolume );
}
/*
-----------------------
FadeTo()
-----------------------
*/
public void FadeTo( float fadeTime, float toVolume ) {
//Log.Print( ">>> FADE TO: " + channel );
// don't override a fade out
if ( state == FadeState.FadingOut ) {
//Log.Print( " ....ABORTED" );
return;
}
state = FadeState.Ducking;
StopAllCoroutines();
StartCoroutine( FadeSoundChannelTo( fadeTime, toVolume ) );
}
/*
-----------------------
FadeIn()
-----------------------
*/
public void FadeIn( float fadeTime, float defaultVolume ) {
//Log.Print( ">>> FADE IN: " + channel );
audioSource.volume = 0.0f;
state = FadeState.FadingIn;
StopAllCoroutines();
StartCoroutine( FadeSoundChannel( 0.0f, fadeTime, Fade.In, defaultVolume ) );
}
/*
-----------------------
FadeIn()
-----------------------
*/
public void FadeIn( float fadeTime ) {
//Log.Print( ">>> FADE IN: " + channel );
audioSource.volume = 0.0f;
state = FadeState.FadingIn;
StopAllCoroutines();
StartCoroutine( FadeSoundChannel( 0.0f, fadeTime, Fade.In, defaultVolume ) );
}
/*
-----------------------
FadeOut()
-----------------------
*/
public void FadeOut( float fadeTime ) {
//Log.Print( ">>> FADE OUT: " + channel );
if ( !audioSource.isPlaying ) {
//Log.Print( " ... SKIPPING" );
return;
}
state = FadeState.FadingOut;
StopAllCoroutines();
StartCoroutine( FadeSoundChannel( 0.0f, fadeTime, Fade.Out, audioSource.volume ) );
}
/*
-----------------------
FadeOutDelayed()
-----------------------
*/
public void FadeOutDelayed( float delayedSecs, float fadeTime ) {
//Log.Print( ">>> FADE OUT DELAYED: " + channel );
if ( !audioSource.isPlaying ) {
//Log.Print( " ... SKIPPING" );
return;
}
state = FadeState.FadingOut;
StopAllCoroutines();
StartCoroutine( FadeSoundChannel( delayedSecs, fadeTime, Fade.Out, audioSource.volume ) );
}
/*
-----------------------
FadeSoundChannelTo()
-----------------------
*/
IEnumerator FadeSoundChannelTo( float fadeTime, float toVolume ) {
float start = audioSource.volume;
float end = toVolume;
float startTime = Time.realtimeSinceStartup;
float elapsedTime = 0.0f;
while ( elapsedTime < fadeTime ) {
elapsedTime = Time.realtimeSinceStartup - startTime;
float t = elapsedTime / fadeTime;
audioSource.volume = Mathf.Lerp( start, end, t );
yield return 0;
}
state = FadeState.Null;
}
/*
-----------------------
FadeSoundChannel()
-----------------------
*/
IEnumerator FadeSoundChannel( float delaySecs, float fadeTime, Fade fadeType, float defaultVolume ) {
if ( delaySecs > 0.0f ) {
yield return new WaitForSeconds( delaySecs );
}
float start = ( fadeType == Fade.In ) ? 0.0f : defaultVolume;
float end = ( fadeType == Fade.In ) ? defaultVolume : 0.0f;
bool restartPlay = false;
if ( fadeType == Fade.In ) {
if ( Time.time == 0.0f ) {
restartPlay = true;
}
audioSource.volume = 0.0f;
audioSource.Play();
}
float startTime = Time.realtimeSinceStartup;
float elapsedTime = 0.0f;
while ( elapsedTime < fadeTime ) {
elapsedTime = Time.realtimeSinceStartup - startTime;
float t = elapsedTime / fadeTime;
audioSource.volume = Mathf.Lerp( start, end, t );
yield return 0;
if ( restartPlay && ( Time.time > 0.0f ) ) {
audioSource.Play();
restartPlay = false;
}
if ( !audioSource.isPlaying ) {
break;
}
}
if ( fadeType == Fade.Out ) {
Stop();
}
state = FadeState.Null;
}
}
} // namespace OVR

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ecd24e91b27c645fc95f6c42115c13cc
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -0,0 +1,311 @@
using UnityEngine;
using UnityEngine.Audio;
namespace OVR
{
public enum SoundFXNext {
Random = 0,
Sequential = 1,
}
public enum FreqHint {
None = 0,
Wide = 1,
Narrow = 2,
}
public enum SoundPriority {
VeryLow = -2,
Low = -1,
Default = 0,
High = 1,
VeryHigh = 2,
}
[System.Serializable]
public class OSPProps {
public OSPProps() {
enableSpatialization = false;
useFastOverride = false;
gain = 0.0f;
enableInvSquare = false;
volumetric = 0.0f;
invSquareFalloff = new Vector2( 1.0f, 25.0f );
}
[Tooltip( "Set to true to play the sound FX spatialized with binaural HRTF, default = false")]
public bool enableSpatialization = false;
[Tooltip( "Play the sound FX with reflections, default = false")]
public bool useFastOverride = false;
[Tooltip( "Boost the gain on the spatialized sound FX, default = 0.0")]
[Range( 0.0f, 24.0f )]
public float gain = 0.0f;
[Tooltip("Enable Inverse Square attenuation curve, default = false")]
public bool enableInvSquare = false;
[Tooltip("Change the sound from point source (0.0f) to a spherical volume, default = 0.0")]
[Range(0.0f, 1000.0f)]
public float volumetric = 0.0f;
[Tooltip("Set the near and far falloff value for the OSP attenuation curve, default = 1.0")]
[MinMax ( 1.0f, 25.0f, 0.0f, 250.0f )]
public Vector2 invSquareFalloff = new Vector2( 1.0f, 25.0f );
}
/*
-----------------------
SoundFX
-----------------------
*/
[System.Serializable]
public class SoundFX {
public SoundFX() {
playback = SoundFXNext.Random;
volume = 1.0f;
pitchVariance = Vector2.one;
falloffDistance = new Vector2( 1.0f, 25.0f );
falloffCurve = AudioRolloffMode.Linear;
volumeFalloffCurve = new AnimationCurve( new Keyframe[2] { new Keyframe( 0f, 1.0f ), new Keyframe( 1f, 1f ) } );
reverbZoneMix = new AnimationCurve( new Keyframe[2] { new Keyframe( 0f, 1.0f ), new Keyframe( 1f, 1f ) } );
spread = 0.0f;
pctChanceToPlay = 1.0f;
priority = SoundPriority.Default;
delay = Vector2.zero;
looping = false;
ospProps = new OSPProps();
}
[Tooltip( "Each sound FX should have a unique name")]
public string name = string.Empty;
[Tooltip( "Sound diversity playback option when multiple audio clips are defined, default = Random")]
public SoundFXNext playback = SoundFXNext.Random;
[Tooltip( "Default volume for this sound FX, default = 1.0")]
[Range (0.0f, 1.0f)]
public float volume = 1.0f;
[Tooltip( "Random pitch variance each time a sound FX is played, default = 1.0 (none)")]
[MinMax ( 1.0f, 1.0f, 0.0f, 2.0f )]
public Vector2 pitchVariance = Vector2.one;
[Tooltip( "Falloff distance for the sound FX, default = 1m min to 25m max")]
[MinMax ( 1.0f, 25.0f, 0.0f, 250.0f )]
public Vector2 falloffDistance = new Vector2( 1.0f, 25.0f );
[Tooltip( "Volume falloff curve - sets how the sound FX attenuates over distance, default = Linear")]
public AudioRolloffMode falloffCurve = AudioRolloffMode.Linear;
[Tooltip( "Defines the custom volume falloff curve")]
public AnimationCurve volumeFalloffCurve = new AnimationCurve( new Keyframe[2] { new Keyframe( 0f, 1.0f ), new Keyframe( 1f, 1f ) } );
[Tooltip( "The amount by which the signal from the AudioSource will be mixed into the global reverb associated with the Reverb Zones | Valid range is 0.0 - 1.1, default = 1.0" )]
public AnimationCurve reverbZoneMix = new AnimationCurve( new Keyframe[2] { new Keyframe( 0f, 1.0f ), new Keyframe( 1f, 1f ) } );
[Tooltip( "Sets the spread angle (in degrees) of a 3d stereo or multichannel sound in speaker space, default = 0")]
[Range (0.0f, 360.0f)]
public float spread = 0.0f;
[Tooltip( "The percentage chance that this sound FX will play | 0.0 = none, 1.0 = 100%, default = 1.0")]
[Range (0.0f, 1.0f)]
public float pctChanceToPlay = 1.0f;
[Tooltip( "Sets the priority for this sound to play and/or to override a currently playing sound FX, default = Default")]
public SoundPriority priority = SoundPriority.Default;
[Tooltip( "Specifies the default delay when this sound FX is played, default = 0.0 secs")]
[MinMax ( 0.0f, 0.0f, 0.0f, 2.0f )]
public Vector2 delay = Vector2.zero; // this overrides any delay passed into PlaySound() or PlaySoundAt()
[Tooltip( "Set to true for the sound to loop continuously, default = false")]
public bool looping = false;
public OSPProps ospProps = new OSPProps();
[Tooltip( "List of the audio clips assigned to this sound FX")]
public AudioClip[] soundClips = new AudioClip[1];
// editor only - unfortunately if we set it not to serialize, we can't query it from the editor
public bool visibilityToggle = false;
// runtime vars
[System.NonSerialized]
private SoundGroup soundGroup = null;
private int lastIdx = -1;
private int playingIdx = -1;
public int Length { get { return soundClips.Length; } }
public bool IsValid { get { return ( ( soundClips.Length != 0 ) && ( soundClips[0] != null ) ); } }
public SoundGroup Group { get { return soundGroup; } set { soundGroup = value; } }
public float MaxFalloffDistSquared { get { return falloffDistance.y * falloffDistance.y; } }
public float GroupVolumeOverride { get { return ( soundGroup != null ) ? soundGroup.volumeOverride : 1.0f; } }
/*
-----------------------
GetClip()
-----------------------
*/
public AudioClip GetClip() {
if ( soundClips.Length == 0 ) {
return null;
} else if ( soundClips.Length == 1 ) {
return soundClips[0];
}
if ( playback == SoundFXNext.Random ) {
// random, but don't pick the last one
int idx = Random.Range( 0, soundClips.Length );
while ( idx == lastIdx ) {
idx = Random.Range( 0, soundClips.Length );
}
lastIdx = idx;
return soundClips[idx];
} else {
// sequential
if ( ++lastIdx >= soundClips.Length ) {
lastIdx = 0;
}
return soundClips[lastIdx];
}
}
/*
-----------------------
GetMixerGroup()
-----------------------
*/
public AudioMixerGroup GetMixerGroup( AudioMixerGroup defaultMixerGroup ) {
if ( soundGroup != null ) {
return ( soundGroup.mixerGroup != null ) ? soundGroup.mixerGroup : defaultMixerGroup;
}
return defaultMixerGroup;
}
/*
-----------------------
ReachedGroupPlayLimit()
-----------------------
*/
public bool ReachedGroupPlayLimit() {
if ( soundGroup != null ) {
return !soundGroup.CanPlaySound();
}
return false;
}
/*
-----------------------
GetClipLength()
-----------------------
*/
public float GetClipLength( int idx ) {
if ( ( idx == -1 ) || ( soundClips.Length == 0 ) || ( idx >= soundClips.Length ) || ( soundClips[idx] == null ) ) {
return 0.0f;
} else {
return soundClips[idx].length;
}
}
/*
-----------------------
GetPitch()
-----------------------
*/
public float GetPitch() {
return Random.Range( pitchVariance.x, pitchVariance.y );
}
/*
-----------------------
PlaySound()
-----------------------
*/
public int PlaySound( float delaySecs = 0.0f ) {
playingIdx = -1;
if ( !IsValid ) {
return playingIdx;
}
// check the random chance to play here to save the function calls
if ( ( pctChanceToPlay > 0.99f ) || ( Random.value < pctChanceToPlay ) ) {
if ( delay.y > 0.0f ) {
delaySecs = Random.Range( delay.x, delay.y );
}
playingIdx = AudioManager.PlaySound( this, EmitterChannel.Any, delaySecs );
}
return playingIdx;
}
/*
-----------------------
PlaySoundAt()
-----------------------
*/
public int PlaySoundAt( Vector3 pos, float delaySecs = 0.0f, float volumeOverride = 1.0f, float pitchMultiplier = 1.0f ) {
playingIdx = -1;
if ( !IsValid ) {
return playingIdx;
}
// check the random chance to play here to save the function calls
if ( ( pctChanceToPlay > 0.99f ) || ( Random.value < pctChanceToPlay ) ) {
if ( delay.y > 0.0f ) {
delaySecs = Random.Range( delay.x, delay.y );
}
playingIdx = AudioManager.PlaySoundAt( pos, this, EmitterChannel.Any, delaySecs, volumeOverride, pitchMultiplier );
}
return playingIdx;
}
/*
-----------------------
SetOnFinished()
get a callback when the sound is finished playing
-----------------------
*/
public void SetOnFinished( System.Action onFinished ) {
if ( playingIdx > -1 ) {
AudioManager.SetOnFinished( playingIdx, onFinished );
}
}
/*
-----------------------
SetOnFinished()
get a callback with an object parameter when the sound is finished playing
-----------------------
*/
public void SetOnFinished( System.Action<object> onFinished, object obj ) {
if ( playingIdx > -1 ) {
AudioManager.SetOnFinished( playingIdx, onFinished, obj );
}
}
/*
-----------------------
StopSound()
-----------------------
*/
public bool StopSound() {
bool stopped = false;
if (playingIdx > -1){
stopped = AudioManager.StopSound(playingIdx);
playingIdx = -1;
}
return stopped;
}
/*
-----------------------
AttachToParent()
-----------------------
*/
public void AttachToParent( Transform parent) {
if (playingIdx > -1) {
AudioManager.AttachSoundToParent(playingIdx, parent);
}
}
/*
-----------------------
DetachFromParent()
-----------------------
*/
public void DetachFromParent() {
if (playingIdx > -1) {
AudioManager.DetachSoundFromParent(playingIdx);
}
}
}
} // namespace OVR

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b0a1e5e7b5cff46a187b02100f0e4a3c
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -0,0 +1,144 @@
using UnityEngine;
using System.Collections;
namespace OVR
{
/*
-----------------------
SoundFXRef
just a references to a SoundFX.. all the SoundFX methods are called indirectly from here
-----------------------
*/
[System.Serializable]
public class SoundFXRef {
public string soundFXName = string.Empty;
private bool initialized = false;
private SoundFX soundFXCached = null;
public SoundFX soundFX {
get {
if ( !initialized ) {
Init();
}
return soundFXCached;
}
}
public string name { get { return soundFXName; } set { soundFXName = value; Init(); } }
/*
-----------------------
Init()
-----------------------
*/
void Init() {
// look up the actual SoundFX object
soundFXCached = AudioManager.FindSoundFX( soundFXName );
if ( soundFXCached == null ) {
soundFXCached = AudioManager.FindSoundFX( string.Empty );
}
initialized = true;
}
/*
-----------------------
Length()
-----------------------
*/
public int Length { get { return soundFX.Length; } }
/*
-----------------------
IsValid()
-----------------------
*/
public bool IsValid { get { return soundFX.IsValid; } }
/*
-----------------------
GetClip()
-----------------------
*/
public AudioClip GetClip() {
return soundFX.GetClip();
}
/*
-----------------------
GetClipLength()
-----------------------
*/
public float GetClipLength( int idx ) {
return soundFX.GetClipLength( idx );
}
/*
-----------------------
PlaySound()
-----------------------
*/
public int PlaySound( float delaySecs = 0.0f ) {
return soundFX.PlaySound( delaySecs );
}
/*
-----------------------
PlaySoundAt()
-----------------------
*/
public int PlaySoundAt( Vector3 pos, float delaySecs = 0.0f, float volume = 1.0f, float pitchMultiplier = 1.0f ) {
return soundFX.PlaySoundAt( pos, delaySecs, volume, pitchMultiplier );
}
/*
-----------------------
SetOnFinished()
get a callback when the sound is finished playing
-----------------------
*/
public void SetOnFinished( System.Action onFinished ) {
soundFX.SetOnFinished( onFinished );
}
/*
-----------------------
SetOnFinished()
get a callback with an object parameter when the sound is finished playing
-----------------------
*/
public void SetOnFinished( System.Action<object> onFinished, object obj ) {
soundFX.SetOnFinished( onFinished, obj );
}
/*
-----------------------
StopSound()
-----------------------
*/
public bool StopSound() {
return soundFX.StopSound();
}
/*
-----------------------
AttachToParent()
-----------------------
*/
public void AttachToParent( Transform parent)
{
soundFX.AttachToParent( parent);
}
/*
-----------------------
DetachFromParent()
-----------------------
*/
public void DetachFromParent()
{
soundFX.DetachFromParent();
}
}
} // namespace OVR

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 48176edab546a48de9b146105d7c5f47
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 02268a883a27b9c4abac4ee978caec4d
folderAsset: yes
timeCreated: 1468506022
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: b43e7f73d4ffc2545a17b938f63e6bc0
folderAsset: yes
timeCreated: 1468507225
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,77 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
[CustomPropertyDrawer(typeof(InspectorNoteAttribute))]
public class DrawerInspectorNote : DecoratorDrawer
{
public override void OnGUI( Rect position )
{
InspectorNoteAttribute note = attribute as InspectorNoteAttribute;
// our header is always present
Rect posLabel = position;
posLabel.y += 13;
posLabel.x -= 2;
posLabel.height += 13;
EditorGUI.LabelField(posLabel, note.header, EditorStyles.whiteLargeLabel);
// do we have a message too?
if (!string.IsNullOrEmpty(note.message))
{
Color color = GUI.color;
Color faded = color;
faded.a = 0.6f;
Rect posExplain = posLabel;
posExplain.y += 15;
GUI.color = faded;
EditorGUI.LabelField(posExplain, note.message, EditorStyles.whiteMiniLabel);
GUI.color = color;
}
Rect posLine = position;
posLine.y += string.IsNullOrEmpty(note.message) ? 30 : 42;
posLine.height = 1f;
GUI.Box(posLine, "");
}
public override float GetHeight() {
InspectorNoteAttribute note = attribute as InspectorNoteAttribute;
return string.IsNullOrEmpty( note.message ) ? 38 : 50;
}
}
[CustomPropertyDrawer( typeof( InspectorCommentAttribute ) )]
public class DrawerInspectorComment : DecoratorDrawer {
public override void OnGUI( Rect position ) {
InspectorCommentAttribute comment = attribute as InspectorCommentAttribute;
// our header is always present
Rect posLabel = position;
//posLabel.y += 13;
//posLabel.x -= 2;
//posLabel.height += 13;
//EditorGUI.LabelField( posLabel, comment.header, EditorStyles.whiteLargeLabel );
// do we have a message too?
if ( !string.IsNullOrEmpty( comment.message ) ) {
Color color = GUI.color;
Color faded = color;
faded.a = 0.6f;
Rect posExplain = posLabel;
posExplain.y += 15;
GUI.color = faded;
EditorGUI.LabelField( posExplain, comment.message, EditorStyles.whiteMiniLabel );
GUI.color = color;
}
}
public override float GetHeight() {
InspectorNoteAttribute note = attribute as InspectorNoteAttribute;
return string.IsNullOrEmpty( note.message ) ? 38 : 50;
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f244e745a5bf8412d9d81d43dff35cf5
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -0,0 +1,18 @@
{
"name": "Oculus.AudioManager.Utils.Editor",
"rootNamespace": "",
"references": [
"Oculus.AudioManager"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 4e79697c263b8554db87efff0e7046ba
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,283 @@
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
public class FastList<T> {
/// <summary>
/// Comparison function should return -1 if left is less than right, 1 if left is greater than right, and 0 if they match.
/// </summary>
public delegate int CompareFunc(T left, T right);
public T[] array = null;
public int size = 0;
public FastList () {
}
public FastList(int size) {
if (size > 0) {
this.size = 0;
array = new T[size];
}
else {
this.size = 0;
}
}
public int Count {
get { return size;}
set { }
}
public T this[int i] {
get { return array[i];}
set { array[i] = value;}
}
//Add item to end of list.
public void Add(T item) {
if (array == null || size == array.Length) {
Allocate();
}
array[size] = item;
size++;
}
//Add item to end of list if it is unique.
public void AddUnique( T item ) {
if ( array == null || size == array.Length ) {
Allocate();
}
if ( !Contains( item ) ) {
array[size] = item;
size++;
}
}
//Add items to the end of the list
public void AddRange( IEnumerable<T> items ) {
foreach ( T item in items ) {
Add( item );
}
}
//Insert item at specified index
public void Insert(int index, T item) {
if (array == null || size == array.Length) {
Allocate();
}
if (index < size) {
//move things back 1
for (int i = size; i > index; i--) {
array[i] = array[i-1];
}
array[index] = item;
size++;
}
else Add(item);
}
//Removes specified item and keeps everything else in order
public bool Remove(T item) {
if (array != null) {
for (int i = 0; i < size; i++) {
if (item.Equals(array[i])) { //found it, push everything up
size--;
for (int j = i; j < size; j++) {
array[j] = array[j+1];
}
array[size] = default(T);
return true;
}
}
}
return false;
}
//Removes item at specified index while keeping everything else in order
//O(n)
public void RemoveAt(int index) {
if (array != null && size > 0 && index < size) {
size--;
for (int i = index; i < size; i++) {
array[i] = array[i+1];
}
array[size] = default(T);
}
}
//Removes the specified item from the list and replaces with last item. Return true if removed, false if not found.
public bool RemoveFast(T item) {
if (array != null) {
for (int i = 0; i < size; i++) {
if ( item.Equals( array[i] )) { //found
//Move last item here
if (i < (size - 1)) {
T lastItem = array[size-1];
array[size-1] = default(T);
array[i] = lastItem;
} else {
array[i] = default(T);
}
size--;
return true;
}
}
}
return false;
}
//Removes item at specified index and replace with last item.
public void RemoveAtFast(int index) {
if (array != null && index < size && index >= 0) {
//last element
if (index == size - 1) {
array[index] = default(T);
}
else {
T lastItem = array[size - 1];
array[index] = lastItem;
array[size - 1] = default(T);
}
size--;
}
}
//Return whether an item is contained within the list
//O(n)
public bool Contains(T item) {
if (array == null || size <= 0 ) return false;
for (int i = 0; i < size; i++) {
if (array[i].Equals(item)) { return true;}
}
return false;
}
//Returns index of specified item, or -1 if not found.
//O(n)
public int IndexOf(T item) {
if (size <= 0 || array == null) { return -1;}
for (int i = 0; i < size; i++) {
if (item.Equals(array[i])) { return i;}
}
return -1;
}
public T Pop() {
if (array != null && size > 0) {
T lastItem = array[size-1];
array[size-1] = default(T);
size--;
return lastItem;
}
return default(T);
}
public T[] ToArray() {
Trim();
return array;
}
public void Sort (CompareFunc comparer) {
int start = 0;
int end = size - 1;
bool changed = true;
while (changed) {
changed = false;
for (int i = start; i < end; i++) {
if (comparer(array[i], array[i + 1]) > 0) {
T temp = array[i];
array[i] = array[i+1];
array[i+1] = temp;
changed = true;
}
else if (!changed) {
start = (i==0) ? 0 : i-1;
}
}
}
}
public void InsertionSort(CompareFunc comparer) {
for (int i = 1; i < size; i++) {
T curr = array[i];
int j = i;
while (j > 0 && comparer(array[j - 1], curr) > 0) {
array[j] = array[j-1];
j--;
}
array[j] = curr;
}
}
public IEnumerator<T> GetEnumerator() {
if (array != null) {
for (int i = 0; i < size; i++) {
yield return array[i];
}
}
}
public T Find(Predicate<T> match) {
if (match != null) {
if (array != null) {
for (int i = 0; i < size; i++) {
if (match(array[i])) { return array[i];}
}
}
}
return default(T);
}
//Allocate more space to internal array.
void Allocate() {
T[] newArray;
if (array == null) {
newArray = new T[32];
}
else {
newArray = new T[Mathf.Max(array.Length << 1, 32)];
}
if (array != null && size > 0) {
array.CopyTo(newArray, 0);
}
array = newArray;
}
void Trim() {
if (size > 0) {
T[] newArray = new T[size];
for (int i = 0; i < size; i++) {
newArray[i] = array[i];
}
array = newArray;
}
else {
array = null;
}
}
//Set size to 0, does not delete array from memory
public void Clear() {
size = 0;
}
//Delete array from memory
public void Release() {
Clear();
array = null;
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 5ad95fb7eea735748bd34c963525ea21
timeCreated: 1432749689
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,35 @@
using UnityEngine;
/*
-----------------------
InspectorNoteAttribute()
-----------------------
*/
public class InspectorNoteAttribute : PropertyAttribute
{
public readonly string header;
public readonly string message;
public InspectorNoteAttribute(string header, string message = "")
{
this.header = header;
this.message = message;
}
}
/*
-----------------------
InspectorCommentAttribute()
-----------------------
*/
public class InspectorCommentAttribute : PropertyAttribute {
public readonly string message;
public InspectorCommentAttribute( string message = "" ) {
this.message = message;
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4616bbe65d311471f8d71174295f4986
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: cf553e34d44857f4fa17bde013a504b8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e628d552f8b252d44815ffae12fd3976
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: afc952163de48b04dbf0f968256241c0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,134 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* Licensed under the Oculus SDK License Agreement (the "License");
* you may not use the Oculus SDK except in compliance with the License,
* which is provided at the time of installation or download, or which
* otherwise accompanies this software in either electronic or hard copy form.
*
* You may obtain a copy of the License at
*
* https://developer.oculus.com/licenses/oculussdk/
*
* Unless required by applicable law or agreed to in writing, the Oculus SDK
* 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.
*/
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEditor;
using UnityEngine;
namespace Oculus.Interaction.Editor
{
[InitializeOnLoad]
public static class AutoWiring
{
static AutoWiring()
{
UnityObjectAddedBroadcaster.WhenComponentAdded += (component) =>
{
MonoBehaviour monoBehaviour = component as MonoBehaviour;
if (monoBehaviour == null) return;
if (!_configs.TryGetValue(component.GetType(), out ComponentWiringStrategyConfig[] configs))
{
return;
}
foreach (ComponentWiringStrategyConfig config in configs)
{
AutoWireField(monoBehaviour, config.FieldName, config.Methods);
}
};
}
private static readonly Dictionary<Type, ComponentWiringStrategyConfig[]> _configs = new
Dictionary<Type, ComponentWiringStrategyConfig[]>();
public static void Register(Type type, ComponentWiringStrategyConfig[] fieldConfigs)
{
_configs.Add(type, fieldConfigs);
}
public static void Unregister(Type type)
{
_configs.Remove(type);
}
public static bool AutoWireField(MonoBehaviour monoBehaviour,
string fieldName,
FieldWiringStrategy[] wiringMethods)
{
FieldInfo field = FindField(fieldName, monoBehaviour.GetType());
if (field == null)
{
return false;
}
UnityEngine.Object value = field.GetValue(monoBehaviour) as UnityEngine.Object;
if (value != null)
{
return false;
}
Undo.RecordObject(monoBehaviour, "Autowiring");
var interfaceAttribute = field.GetCustomAttribute<InterfaceAttribute>();
var wirableTypes = interfaceAttribute != null ?
interfaceAttribute.Types :
new[] { field.FieldType };
if (wirableTypes != null)
{
foreach (var method in wiringMethods)
{
foreach (Type type in wirableTypes)
{
if (method.Invoke(monoBehaviour, field, type))
{
Component component = field.GetValue(monoBehaviour) as Component;
Debug.Log("Auto-wiring succeeded: " + monoBehaviour.gameObject.name + "::" +
monoBehaviour.GetType().Name + "." + field.Name +
" was linked to " +
component.gameObject.name + "::" + component.GetType().Name,
monoBehaviour);
return true;
}
}
}
}
if (field.GetCustomAttribute<OptionalAttribute>() == null)
{
Debug.LogWarning("Auto-wiring failed: no suitable targets for " +
monoBehaviour.gameObject.name + "::" + monoBehaviour.GetType().Name +
"." + field.Name + " could be found.",
monoBehaviour);
}
return false;
}
private static FieldInfo FindField(string fieldName, Type type)
{
if (type == null)
{
return null;
}
FieldInfo field = type.GetField(fieldName,
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (field == null)
{
return FindField(fieldName, type.BaseType);
}
return field;
}
}
}

View File

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

View File

@ -0,0 +1,79 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* Licensed under the Oculus SDK License Agreement (the "License");
* you may not use the Oculus SDK except in compliance with the License,
* which is provided at the time of installation or download, or which
* otherwise accompanies this software in either electronic or hard copy form.
*
* You may obtain a copy of the License at
*
* https://developer.oculus.com/licenses/oculussdk/
*
* Unless required by applicable law or agreed to in writing, the Oculus SDK
* 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.
*/
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace Oculus.Interaction.Editor
{
public delegate Boolean FieldWiringStrategy(MonoBehaviour monoBehaviour, FieldInfo fieldInfo, Type type);
public struct ComponentWiringStrategyConfig
{
public string FieldName { get; }
public FieldWiringStrategy[] Methods { get; }
public ComponentWiringStrategyConfig(string fieldName, FieldWiringStrategy[] methods)
{
FieldName = fieldName;
Methods = methods;
}
}
public class FieldWiringStrategies
{
public static bool WireFieldToAncestors(MonoBehaviour monoBehaviour, FieldInfo field, Type targetType)
{
for (var transform = monoBehaviour.transform.parent; transform != null; transform = transform.parent)
{
var component = transform.gameObject.GetComponent(targetType);
if (component)
{
field.SetValue(monoBehaviour, component);
EditorUtility.SetDirty(monoBehaviour);
return true;
}
}
return false;
}
public static bool WireFieldToSceneComponent(MonoBehaviour monoBehaviour, FieldInfo field, Type targetType)
{
var rootObjs = SceneManager.GetActiveScene().GetRootGameObjects();
foreach (var rootGameObject in rootObjs)
{
var component = rootGameObject.GetComponentInChildren(targetType, true);
if (component != null)
{
field.SetValue(monoBehaviour, component);
EditorUtility.SetDirty(monoBehaviour);
return true;
}
}
return false;
}
}
}

View File

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

View File

@ -0,0 +1,114 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* Licensed under the Oculus SDK License Agreement (the "License");
* you may not use the Oculus SDK except in compliance with the License,
* which is provided at the time of installation or download, or which
* otherwise accompanies this software in either electronic or hard copy form.
*
* You may obtain a copy of the License at
*
* https://developer.oculus.com/licenses/oculussdk/
*
* Unless required by applicable law or agreed to in writing, the Oculus SDK
* 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.
*/
using Oculus.Interaction.Input;
using Oculus.Interaction.PoseDetection;
using UnityEditor;
namespace Oculus.Interaction.Editor
{
[InitializeOnLoad]
public static class InteractionAutoWiring
{
static InteractionAutoWiring()
{
AutoWiring.Register(
typeof(HandRef),
new[] {
new ComponentWiringStrategyConfig("_hand", new FieldWiringStrategy[]
{
FieldWiringStrategies.WireFieldToAncestors
})
}
);
AutoWiring.Register(
typeof(ControllerRef),
new[]
{
new ComponentWiringStrategyConfig("_controller", new FieldWiringStrategy[]
{
FieldWiringStrategies.WireFieldToAncestors
})
}
);
AutoWiring.Register(
typeof(FingerFeatureStateProvider),
new[] {
new ComponentWiringStrategyConfig("_hand", new FieldWiringStrategy[]
{
FieldWiringStrategies.WireFieldToAncestors
}),
}
);
AutoWiring.Register(
typeof(TransformFeatureStateProvider),
new[] {
new ComponentWiringStrategyConfig("_hand", new FieldWiringStrategy[]
{
FieldWiringStrategies.WireFieldToAncestors
}),
new ComponentWiringStrategyConfig("_trackingToWorldTransformer", new FieldWiringStrategy[]
{
FieldWiringStrategies.WireFieldToAncestors
}),
new ComponentWiringStrategyConfig("_hmd", new FieldWiringStrategy[]
{
FieldWiringStrategies.WireFieldToAncestors,
FieldWiringStrategies.WireFieldToSceneComponent
})
}
);
AutoWiring.Register(
typeof(JointDeltaProvider),
new[] {
new ComponentWiringStrategyConfig("_hand", new FieldWiringStrategy[]
{
FieldWiringStrategies.WireFieldToAncestors
}),
}
);
#region HandGrab
AutoWiring.Register(
typeof(HandGrab.HandGrabInteractable),
new[] {
new ComponentWiringStrategyConfig("_rigidbody", new FieldWiringStrategy[]
{
FieldWiringStrategies.WireFieldToAncestors
}),
new ComponentWiringStrategyConfig("_pointableElement", new FieldWiringStrategy[]
{
FieldWiringStrategies.WireFieldToAncestors
}),
new ComponentWiringStrategyConfig("_physicsGrabbable", new FieldWiringStrategy[]
{
FieldWiringStrategies.WireFieldToAncestors
})
}
);
#endregion
}
}
}

View File

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

View File

@ -0,0 +1,193 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* Licensed under the Oculus SDK License Agreement (the "License");
* you may not use the Oculus SDK except in compliance with the License,
* which is provided at the time of installation or download, or which
* otherwise accompanies this software in either electronic or hard copy form.
*
* You may obtain a copy of the License at
*
* https://developer.oculus.com/licenses/oculussdk/
*
* Unless required by applicable law or agreed to in writing, the Oculus SDK
* 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.
*/
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace Oculus.Interaction.Editor
{
[InitializeOnLoad]
public static class UnityObjectAddedBroadcaster
{
public static event Action<GameObject> WhenGameObjectHierarchyAdded = (_) => {};
public static event Action<Component> WhenComponentAdded = (_) => {};
private static int _objectAddedUndoNestingCounter = 0;
private static int _objectAddedUndoGroupId = -1;
static UnityObjectAddedBroadcaster()
{
HashSet<int> knownIds = new HashSet<int>();
EditorSceneManager.SceneOpenedCallback handleSceneOpened = (scene, mode) =>
{
UnityObjectAddedBroadcaster.HandleSceneOpened(scene, mode, knownIds);
};
Action handleHierarchyChanged = () =>
{
UnityObjectAddedBroadcaster.HandleHierarchyChanged(knownIds);
};
Action<Component> handleComponentWasAdded = (component) =>
{
UnityObjectAddedBroadcaster.HandleComponentWasAdded(component);
};
AssemblyReloadEvents.AssemblyReloadCallback handleBeforeAssemblyReload = null;
handleBeforeAssemblyReload = () =>
{
UnityObjectAddedBroadcaster.WhenGameObjectHierarchyAdded = (_) => { };
UnityObjectAddedBroadcaster.WhenComponentAdded = (_) => { };
EditorSceneManager.sceneOpened -= handleSceneOpened;
EditorApplication.hierarchyChanged -= handleHierarchyChanged;
ObjectFactory.componentWasAdded -= handleComponentWasAdded;
AssemblyReloadEvents.beforeAssemblyReload -= handleBeforeAssemblyReload;
};
EditorSceneManager.sceneOpened += handleSceneOpened;
EditorApplication.hierarchyChanged += handleHierarchyChanged;
ObjectFactory.componentWasAdded += handleComponentWasAdded;
AssemblyReloadEvents.beforeAssemblyReload += handleBeforeAssemblyReload;
for (int idx = 0; idx < SceneManager.loadedSceneCount; ++idx)
{
handleSceneOpened(EditorSceneManager.GetSceneAt(idx), OpenSceneMode.Additive);
}
}
private static void HandleSceneOpened(Scene scene, OpenSceneMode mode, HashSet<int> knownIds)
{
if (mode == OpenSceneMode.Single)
{
knownIds.Clear();
}
AddInstanceIdsFromSubHierarchyToCache(knownIds, scene.GetRootGameObjects());
}
/// <summary>
/// Fires signals for GameObjects and Components added through the addition of a prefab,
/// checking whether the selected GameObject at the moment of a hierarchy change is
/// unfamiliar (i.e., has an instance ID which is not already in known IDs) and signaling
/// appropriately. Note that this will NOT signal GameObjects or Components added to the
/// scene through prefab updating, which are added without modifying the Editor's
/// selection variable, upon which this handler relies.
/// </summary>
/// <param name="knownIds">Cache of known GameObject instance IDs</param>
private static void HandleHierarchyChanged(HashSet<int> knownIds)
{
if (EditorApplication.isPlaying)
{
return;
}
var selection = Selection.activeGameObject;
if (selection == null)
{
return;
}
if (!knownIds.Contains(selection.GetInstanceID()))
{
AddInstanceIdsFromSubHierarchyToCache(knownIds, selection);
StartUndoGroup();
UnityObjectAddedBroadcaster.WhenGameObjectHierarchyAdded(selection);
// ObjectFactory.componentWasAdded is not called for components added to the scene
// as part of a prefab, so we manually iterate them here so that
// Signaler.WhenComponentAdded presents a more complete picture of activity in
// the scene.
var addedComponents = selection.GetComponentsInChildren<Component>(true);
foreach (var component in addedComponents)
{
UnityObjectAddedBroadcaster.WhenComponentAdded(component);
}
EndUndoGroup();
}
}
/// <summary>
/// Fires signals for Components added to existing GameObjects. Note that this will
/// NOT signal Components added to the scene through prefab updating, which are added
/// without triggering the ObjectFactory, upon which this handler relies.
/// </summary>
/// <param name="component">The component added to the scene</param>
private static void HandleComponentWasAdded(Component component)
{
if (EditorApplication.isPlaying)
{
return;
}
StartUndoGroup();
UnityObjectAddedBroadcaster.WhenComponentAdded(component);
EndUndoGroup();
}
public static void HandleObjectWasAdded(GameObject gameObject)
{
StartUndoGroup();
var addedComponents = gameObject.GetComponentsInChildren<Component>(true);
foreach (var component in addedComponents)
{
UnityObjectAddedBroadcaster.WhenComponentAdded(component);
}
EndUndoGroup();
}
private static void AddInstanceIdsFromSubHierarchyToCache(HashSet<int> cache, params GameObject[] subHierarchyRoots)
{
foreach (var gameObject in subHierarchyRoots)
{
cache.Add(gameObject.GetInstanceID());
for (int idx = 0; idx < gameObject.transform.childCount; ++idx)
{
AddInstanceIdsFromSubHierarchyToCache(cache, gameObject.transform.GetChild(idx).gameObject);
}
}
}
private static void StartUndoGroup()
{
if (_objectAddedUndoNestingCounter == 0)
{
_objectAddedUndoGroupId = Undo.GetCurrentGroup() - 1;
}
_objectAddedUndoNestingCounter++;
}
private static void EndUndoGroup()
{
_objectAddedUndoNestingCounter--;
if (_objectAddedUndoNestingCounter == 0)
{
Undo.FlushUndoRecordObjects();
Undo.CollapseUndoOperations(_objectAddedUndoGroupId);
}
}
}
}

View File

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

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7b46659523f404646babada299c34172
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,132 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* Licensed under the Oculus SDK License Agreement (the "License");
* you may not use the Oculus SDK except in compliance with the License,
* which is provided at the time of installation or download, or which
* otherwise accompanies this software in either electronic or hard copy form.
*
* You may obtain a copy of the License at
*
* https://developer.oculus.com/licenses/oculussdk/
*
* Unless required by applicable law or agreed to in writing, the Oculus SDK
* 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.
*/
using UnityEngine;
using UnityEditor;
using System.Linq;
using System.Collections.Generic;
using Oculus.Interaction.Body.PoseDetection;
using Oculus.Interaction.Body.Input;
using System.Reflection;
namespace Oculus.Interaction.Body.Editor
{
using JointComparerConfig = BodyPoseComparerActiveState.JointComparerConfig;
[CustomPropertyDrawer(typeof(JointComparerConfig))]
public class BodyPoseComparerConfigPropertyDrawer : PropertyDrawer
{
private static GUIContent[] _jointNames;
private static BodyJointId[] _joints;
private int _controlCount = 0;
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return GetLineHeight() * _controlCount;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
if (_jointNames == null || _joints == null)
{
UpdateJointLists();
}
_controlCount = 0;
Rect pos = new Rect(position.x, position.y, position.width,
EditorGUIUtility.singleLineHeight);
EditorGUI.BeginProperty(position, label, property);
var joint = property.FindPropertyRelative(nameof(JointComparerConfig.Joint));
var maxAngle = property.FindPropertyRelative(nameof(JointComparerConfig.MaxDelta));
var width = property.FindPropertyRelative(nameof(JointComparerConfig.Width));
DrawJointPopup(joint, "Body Joint", ref pos);
DrawControl(maxAngle, "Max Angle Delta", ref pos);
DrawControl(width, "Threshold Width", ref pos);
EditorGUI.EndProperty();
}
private void DrawJointPopup(SerializedProperty jointProp, string name, ref Rect position)
{
int currentJoint = GetDisplayIndexFromJoint((BodyJointId)jointProp.intValue);
int selectedJoint = EditorGUI.Popup(position,
new GUIContent(name, jointProp.tooltip),
currentJoint, _jointNames);
position.y += GetLineHeight();
++_controlCount;
jointProp.intValue = (int)_joints[selectedJoint];
}
private void DrawControl(SerializedProperty property, string name, ref Rect position)
{
EditorGUI.PropertyField(position, property, new GUIContent(name, property.tooltip));
position.y += GetLineHeight();
++_controlCount;
}
private float GetLineHeight()
{
return EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
}
private static int GetDisplayIndexFromJoint(BodyJointId jointId)
{
for (int i = 0; i < _joints.Length; ++i)
{
if (jointId == _joints[i])
{
return i;
}
}
return 0;
}
private static void UpdateJointLists()
{
List<string> jointNames = new List<string>();
List<BodyJointId> joints = new List<BodyJointId>();
for (int i = (int)BodyJointId.Body_Hips; i < (int)BodyJointId.Body_End; ++i)
{
BodyJointId jointId = (BodyJointId)i;
InspectorNameAttribute inspectorNameAttribute =
typeof(BodyJointId)
.GetMember(jointId.ToString())
.First()
.GetCustomAttribute<InspectorNameAttribute>();
joints.Add(jointId);
jointNames.Add(inspectorNameAttribute != null ?
inspectorNameAttribute.displayName :
jointId.ToString());
}
_joints = joints.ToArray();
_jointNames = jointNames
.Select(s => new GUIContent(s))
.ToArray();
}
}
}

View File

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

View File

@ -0,0 +1,205 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* Licensed under the Oculus SDK License Agreement (the "License");
* you may not use the Oculus SDK except in compliance with the License,
* which is provided at the time of installation or download, or which
* otherwise accompanies this software in either electronic or hard copy form.
*
* You may obtain a copy of the License at
*
* https://developer.oculus.com/licenses/oculussdk/
*
* Unless required by applicable law or agreed to in writing, the Oculus SDK
* 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.
*/
using UnityEngine;
using UnityEditor;
using Oculus.Interaction.Body.PoseDetection;
using Oculus.Interaction.Body.Input;
using System.IO;
namespace Oculus.Interaction.Body.Editor
{
public class BodyPoseRecorder : EditorWindow
{
private const string POSE_DIRECTORY = "BodyPoses";
[Tooltip("The " + nameof(IBody) + " that provides joint " +
"data for pose creation.")]
[SerializeField, Interface(typeof(IBody))]
private UnityEngine.Object _body;
[Tooltip("The captured body pose will be written into this " +
nameof(BodyPoseData) + " asset. If this field is left " +
"unassigned, each capture will create a new asset in the " +
POSE_DIRECTORY + " directory.")]
[SerializeField, Optional]
private BodyPoseData _targetAsset;
private SerializedObject _serializedEditor;
private float _captureDelay = 5f;
private float _nextCaptureTime;
private bool _willCapture = false;
private bool _beepOnCapture = true;
private GUIStyle _richTextStyle;
private IBody Body => _body as IBody;
private float TimeUntilCapture =>
Mathf.Max(_nextCaptureTime - Time.unscaledTime, 0);
[MenuItem("Oculus/Interaction/Body Pose Recorder")]
private static void ShowWindow()
{
BodyPoseRecorder window = GetWindow<BodyPoseRecorder>();
window.titleContent = new GUIContent("Body Pose Recorder");
window._serializedEditor = null;
window.Show();
if (Application.isPlaying)
{
window.Initialize();
}
}
[InitializeOnEnterPlayMode]
private static void OnPlayModeEnter()
{
EditorApplication.delayCall += () =>
{
if (HasOpenInstances<BodyPoseRecorder>())
{
BodyPoseRecorder window = GetWindow<BodyPoseRecorder>();
window.Initialize();
}
};
}
private void Initialize()
{
_targetAsset = null;
_body = FindObjectOfType<Input.Body>();
_serializedEditor = new SerializedObject(this);
_richTextStyle = EditorGUIUtility.GetBuiltinSkin(
EditorGUIUtility.isProSkin ?
EditorSkin.Scene :
EditorSkin.Inspector).label;
_richTextStyle.richText = true;
_richTextStyle.wordWrap = true;
}
private void OnGUI()
{
if (!Application.isPlaying)
{
EditorGUILayout.LabelField("Body Pose Recorder only works in Play mode");
return;
}
if (_serializedEditor == null)
{
return;
}
HandleCapture();
DrawUI();
if (_willCapture)
{
Repaint();
}
}
private void DrawUI()
{
GUI.enabled = !_willCapture;
SerializedProperty bodyProp =
_serializedEditor.FindProperty(nameof(_body));
SerializedProperty targetAssetProp =
_serializedEditor.FindProperty(nameof(_targetAsset));
GUILayout.Label("<size=16>Source</size>\n" + bodyProp.tooltip, _richTextStyle);
EditorGUILayout.PropertyField(
bodyProp, new GUIContent("IBody", bodyProp.tooltip), true);
GUILayout.Space(20);
GUILayout.Label("<size=16>Target</size>\n" + targetAssetProp.tooltip, _richTextStyle);
EditorGUILayout.PropertyField(
targetAssetProp, new GUIContent("Target Asset", targetAssetProp.tooltip), true);
GUILayout.Space(20);
GUILayout.Label("<size=16>Capture Settings</size>", _richTextStyle);
_captureDelay = EditorGUILayout.FloatField("Capture Delay (Seconds)", _captureDelay);
_captureDelay = Mathf.Max(_captureDelay, 0);
_beepOnCapture = EditorGUILayout.Toggle("Play Sound on Capture", _beepOnCapture);
_serializedEditor.ApplyModifiedProperties();
GUILayout.Space(20);
GUI.enabled = Body != null;
string buttonLabel = _willCapture ?
$"Capturing in {TimeUntilCapture.ToString("#.#")} " +
$"seconds\n(Click To Cancel)" : "Capture Body Pose";
if (GUILayout.Button(buttonLabel, GUILayout.Height(36)))
{
_willCapture = !_willCapture;
_nextCaptureTime = Time.unscaledTime + _captureDelay;
}
GUI.enabled = true;
}
private void HandleCapture()
{
if (!_willCapture)
{
return;
}
if (TimeUntilCapture <= 0f)
{
_willCapture = false;
if (_beepOnCapture)
{
EditorApplication.Beep();
}
BodyPoseData assetToWrite = _targetAsset;
if (assetToWrite == null)
{
assetToWrite = GeneratePoseAsset();
}
assetToWrite.SetBodyPose(Body);
Debug.Log($"Captured Body Pose into " +
$"{AssetDatabase.GetAssetPath(assetToWrite)}");
EditorUtility.SetDirty(assetToWrite);
AssetDatabase.SaveAssetIfDirty(assetToWrite);
AssetDatabase.Refresh();
}
}
public BodyPoseData GeneratePoseAsset()
{
var poseDataAsset = ScriptableObject.CreateInstance<BodyPoseData>();
string parentDir = Path.Combine("Assets", POSE_DIRECTORY);
if (!Directory.Exists(parentDir))
{
Directory.CreateDirectory(parentDir);
}
string name = "BodyPose-" + $"{System.DateTime.Now.ToString("yyyyMMdd-HHmmss")}";
AssetDatabase.CreateAsset(poseDataAsset, Path.Combine(parentDir, $"{name}.asset"));
return poseDataAsset;
}
}
}

View File

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

View File

@ -0,0 +1,399 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* Licensed under the Oculus SDK License Agreement (the "License");
* you may not use the Oculus SDK except in compliance with the License,
* which is provided at the time of installation or download, or which
* otherwise accompanies this software in either electronic or hard copy form.
*
* You may obtain a copy of the License at
*
* https://developer.oculus.com/licenses/oculussdk/
*
* Unless required by applicable law or agreed to in writing, the Oculus SDK
* 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.
*/
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.Assertions;
namespace Oculus.Interaction.Editor
{
/// <summary>
/// A utility class for building custom editors with less work required.
/// </summary>
public class EditorBase
{
private SerializedObject _serializedObject;
private HashSet<string> _hiddenProperties = new HashSet<string>();
private HashSet<string> _skipProperties = new HashSet<string>();
private Dictionary<string, Action<SerializedProperty>> _customDrawers =
new Dictionary<string, Action<SerializedProperty>>();
private Dictionary<string, Section> _sections =
new Dictionary<string, Section>();
private List<string> _orderedSections = new List<string>();
public class Section
{
public string title;
public bool isFoldout;
public bool foldout;
public List<string> properties = new List<string>();
public bool HasSomethingToDraw()
{
return properties != null && properties.Count > 0;
}
}
public EditorBase(SerializedObject serializedObject)
{
_serializedObject = serializedObject;
}
#region Sections
private Section GetOrCreateSection(string sectionName)
{
if (_sections.TryGetValue(sectionName, out Section existingSection))
{
return existingSection;
}
Section section = CreateSection(sectionName, false);
return section;
}
public Section CreateSection(string sectionName, bool isFoldout)
{
if (_sections.TryGetValue(sectionName, out Section existingSection))
{
Debug.LogError($"Section {sectionName} already exists");
return null;
}
Section section = new Section() { title = sectionName, isFoldout = isFoldout };
_sections.Add(sectionName, section);
_orderedSections.Add(sectionName);
return section;
}
public void CreateSections(Dictionary<string, string[]> sections, bool isFoldout)
{
foreach (var sectionData in sections)
{
CreateSection(sectionData.Key, isFoldout);
AddToSection(sectionData.Key, sectionData.Value);
}
}
public void AddToSection(string sectionName, params string[] properties)
{
if (properties.Length == 0
|| !ValidateProperties(properties))
{
return;
}
Section section = GetOrCreateSection(sectionName);
foreach (var property in properties)
{
section.properties.Add(property);
_skipProperties.Add(property);
}
}
#endregion
#region API
/// <summary>
/// Call in OnEnable with one or more property names to hide them from the inspector.
///
/// This is preferable to using [HideInInspector] because it still allows the property to
/// be viewed when using the Inspector debug mode.
/// </summary>
public void Hide(params string[] properties)
{
Assert.IsTrue(properties.Length > 0, "Should always hide at least one property.");
if (!ValidateProperties(properties))
{
return;
}
_hiddenProperties.UnionWith(properties);
}
/// <summary>
/// Call in OnInit to specify a custom drawer for a single property. Whenever the property is drawn,
/// it will use the provided property drawer instead of the default one.
/// </summary>
public void Draw(string property, Action<SerializedProperty> drawer)
{
if (!ValidateProperties(property))
{
return;
}
_customDrawers.Add(property, drawer);
}
/// <summary>
/// Call in OnInit to specify a custom drawer for a single property. Include an extra property that gets
/// lumped in with the primary property. The extra property is not drawn normally, and is instead grouped in
/// with the primary property. Can be used in situations where a collection of properties need to be drawn together.
/// </summary>
public void Draw(string property,
string withExtra0,
Action<SerializedProperty, SerializedProperty> drawer)
{
if (!ValidateProperties(property, withExtra0))
{
return;
}
Hide(withExtra0);
Draw(property, p =>
{
drawer(p,
_serializedObject.FindProperty(withExtra0));
});
}
public void Draw(string property,
string withExtra0,
string withExtra1,
Action<SerializedProperty, SerializedProperty, SerializedProperty> drawer)
{
if (!ValidateProperties(property, withExtra0, withExtra1))
{
return;
}
Hide(withExtra0);
Hide(withExtra1);
Draw(property, p =>
{
drawer(p,
_serializedObject.FindProperty(withExtra0),
_serializedObject.FindProperty(withExtra1));
});
}
public void Draw(string property,
string withExtra0,
string withExtra1,
string withExtra2,
Action<SerializedProperty, SerializedProperty, SerializedProperty, SerializedProperty>
drawer)
{
if (!ValidateProperties(property, withExtra0, withExtra1, withExtra2))
{
return;
}
Hide(withExtra0);
Hide(withExtra1);
Hide(withExtra2);
Draw(property, p =>
{
drawer(p,
_serializedObject.FindProperty(withExtra0),
_serializedObject.FindProperty(withExtra1),
_serializedObject.FindProperty(withExtra2));
});
}
public void Draw(string property,
string withExtra0,
string withExtra1,
string withExtra2,
string withExtra3,
Action<SerializedProperty, SerializedProperty, SerializedProperty, SerializedProperty,
SerializedProperty> drawer)
{
if (!ValidateProperties(property, withExtra0, withExtra1, withExtra2, withExtra3))
{
return;
}
Hide(withExtra0);
Hide(withExtra1);
Hide(withExtra2);
Hide(withExtra3);
Draw(property, p =>
{
drawer(p,
_serializedObject.FindProperty(withExtra0),
_serializedObject.FindProperty(withExtra1),
_serializedObject.FindProperty(withExtra2),
_serializedObject.FindProperty(withExtra3));
});
}
#endregion
#region IMPLEMENTATION
/// <summary>
/// Indicates if the property in the serializedObject
/// has been assigned to a section
/// </summary>
/// <param name="property">The name of the property in the serialized object</param>
/// <returns>True if the property has been added to a section</returns>
public bool IsInSection(string property)
{
return _skipProperties.Contains(property);
}
/// <summary>
/// Indicates if the property in the serializedObject
/// needs to be hidden.
/// Hidden properties are typically drawn in a custom way
/// so they don't need to be drawn with the default methods.
/// </summary>
/// <param name="property">The name of the property in the serialized object</param>
/// <returns>True if the property has been hidden</returns>
public bool IsHidden(string property)
{
return _hiddenProperties.Contains(property);
}
/// <summary>
/// Draws all the visible (non hidden)properties in the serialized
/// object following this order:
/// First all properties that has not been added to sections,
/// in the order they appear in the Component.
/// Then all the sections (indented and in foldouts) in the order
/// they were created, with the internal properties ordered in the
/// order the properties were added to the section.
///
/// If a special property drawer was specified it will use it when
/// drawing said property.
///
/// If a property was hidden, it will not present it in any section.
/// </summary>
public void DrawFullInspector()
{
SerializedProperty it = _serializedObject.GetIterator();
it.NextVisible(enterChildren: true);
//Draw script header
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.PropertyField(it);
EditorGUI.EndDisabledGroup();
EditorGUI.BeginChangeCheck();
while (it.NextVisible(enterChildren: false))
{
//Don't draw skip properties in this pass, we will draw them after everything else
if (IsInSection(it.name))
{
continue;
}
DrawProperty(it);
}
foreach (string sectionKey in _orderedSections)
{
DrawSection(sectionKey);
}
_serializedObject.ApplyModifiedProperties();
}
/// <summary>
/// Draws all the properties in the section in the order
/// they were added.
/// </summary>
/// <param name="sectionName">The name of the section</param>
public void DrawSection(string sectionName)
{
Section section = _sections[sectionName];
DrawSection(section);
}
private void DrawSection(Section section)
{
if (!section.HasSomethingToDraw())
{
return;
}
if (section.isFoldout)
{
section.foldout = EditorGUILayout.Foldout(section.foldout, section.title);
if (!section.foldout)
{
return;
}
EditorGUI.indentLevel++;
}
foreach (string prop in section.properties)
{
DrawProperty(_serializedObject.FindProperty(prop));
}
if (section.isFoldout)
{
EditorGUI.indentLevel--;
}
}
private void DrawProperty(SerializedProperty property)
{
try
{
//Don't draw hidden properties
if (IsHidden(property.name))
{
return;
}
//Then draw the property itself, using a custom drawer if needed
Action<SerializedProperty> customDrawer;
if (_customDrawers.TryGetValue(property.name, out customDrawer))
{
customDrawer(property);
}
else
{
EditorGUILayout.PropertyField(property, includeChildren: true);
}
}
catch (Exception e)
{
Debug.LogError($"Error drawing property {e.Message}");
}
}
private bool ValidateProperties(params string[] properties)
{
foreach (var property in properties)
{
if (_serializedObject.FindProperty(property) == null)
{
Debug.LogWarning(
$"Could not find property {property}, maybe it was deleted or renamed?");
return false;
}
}
return true;
}
#endregion
}
}

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c93e89dd55b481b4aa6ed62d6c8227bd
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,103 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* Licensed under the Oculus SDK License Agreement (the "License");
* you may not use the Oculus SDK except in compliance with the License,
* which is provided at the time of installation or download, or which
* otherwise accompanies this software in either electronic or hard copy form.
*
* You may obtain a copy of the License at
*
* https://developer.oculus.com/licenses/oculussdk/
*
* Unless required by applicable law or agreed to in writing, the Oculus SDK
* 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.
*/
using Oculus.Interaction.Editor;
using Oculus.Interaction.Input;
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace Oculus.Interaction.GrabAPI
{
[CustomPropertyDrawer(typeof(GrabbingRule))]
public class GrabbingRuleEditor : PropertyDrawer
{
private static Dictionary<string, bool> _unfolds = new Dictionary<string, bool>();
private static readonly string[] FINGER_PROPERTY_NAMES = new string[]
{
"_thumbRequirement",
"_indexRequirement",
"_middleRequirement",
"_ringRequirement",
"_pinkyRequirement",
};
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
InitializeUnfold(property);
if (_unfolds[property.propertyPath])
{
return EditorConstants.ROW_HEIGHT * (Constants.NUM_FINGERS + 2);
}
else
{
return EditorConstants.ROW_HEIGHT * 1;
}
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginProperty(position, label, property);
InitializeUnfold(property);
Rect rowRect = new Rect(position.x, position.y, position.width, EditorConstants.ROW_HEIGHT);
_unfolds[property.propertyPath] = EditorGUI.Foldout(rowRect, _unfolds[property.propertyPath], label, true);
if (_unfolds[property.propertyPath])
{
EditorGUI.indentLevel++;
for (int i = 0; i < Constants.NUM_FINGERS; i++)
{
rowRect.y += EditorConstants.ROW_HEIGHT;
SerializedProperty finger = property.FindPropertyRelative(FINGER_PROPERTY_NAMES[i]);
HandFinger fingerID = (HandFinger)i;
FingerRequirement current = (FingerRequirement)finger.intValue;
FingerRequirement selected = (FingerRequirement)EditorGUI.EnumPopup(rowRect, $"{fingerID}: ", current);
finger.intValue = (int)selected;
}
rowRect.y += EditorConstants.ROW_HEIGHT;
DrawFlagProperty<FingerUnselectMode>(property, rowRect, "Unselect Mode", "_unselectMode", false);
EditorGUI.indentLevel--;
}
EditorGUI.EndProperty();
}
private void InitializeUnfold(SerializedProperty property)
{
if (!_unfolds.ContainsKey(property.propertyPath))
{
_unfolds.Add(property.propertyPath, false);
}
}
private void DrawFlagProperty<TEnum>(SerializedProperty parentProperty, Rect position, string title, string fieldName, bool isFlags) where TEnum : Enum
{
SerializedProperty fieldProperty = parentProperty.FindPropertyRelative(fieldName);
TEnum value = (TEnum)Enum.ToObject(typeof(TEnum), fieldProperty.intValue);
Enum selectedValue = isFlags ?
EditorGUI.EnumFlagsField(position, title, value)
: EditorGUI.EnumPopup(position, title, value);
fieldProperty.intValue = (int)Enum.ToObject(typeof(TEnum), selectedValue);
}
}
}

View File

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

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f0d69d4b88de08343adc54816c3220a1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* Licensed under the Oculus SDK License Agreement (the "License");
* you may not use the Oculus SDK except in compliance with the License,
* which is provided at the time of installation or download, or which
* otherwise accompanies this software in either electronic or hard copy form.
*
* You may obtain a copy of the License at
*
* https://developer.oculus.com/licenses/oculussdk/
*
* Unless required by applicable law or agreed to in writing, the Oculus SDK
* 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.
*/
using Oculus.Interaction.Editor;
using UnityEditor;
namespace Oculus.Interaction.HandGrab.Editor
{
[CanEditMultipleObjects]
[CustomEditor(typeof(DistanceHandGrabInteractable))]
public partial class DistanceHandGrabInteractableEditor : SimplifiedEditor
{
private DistanceHandGrabInteractable _target;
private HandGrabScaleKeysEditor<DistanceHandGrabInteractable> _listDrawer;
private void Awake()
{
_target = target as DistanceHandGrabInteractable;
}
protected override void OnEnable()
{
base.OnEnable();
_listDrawer = new HandGrabScaleKeysEditor<DistanceHandGrabInteractable>(serializedObject,
_target.HandGrabPoses, "_handGrabPoses", true);
_editorDrawer.Draw("_handGrabPoses", (modeProp) =>
{
_listDrawer.DrawInspector();
});
}
protected override void OnDisable()
{
base.OnDisable();
_listDrawer.TearDown();
}
}
}

View File

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

View File

@ -0,0 +1,50 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* Licensed under the Oculus SDK License Agreement (the "License");
* you may not use the Oculus SDK except in compliance with the License,
* which is provided at the time of installation or download, or which
* otherwise accompanies this software in either electronic or hard copy form.
*
* You may obtain a copy of the License at
*
* https://developer.oculus.com/licenses/oculussdk/
*
* Unless required by applicable law or agreed to in writing, the Oculus SDK
* 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.
*/
using UnityEditor;
using UnityEngine;
using Oculus.Interaction.HandGrab.Visuals;
namespace Oculus.Interaction.HandGrab.Editor
{
public class HandGhostProviderUtils
{
public static bool TryGetDefaultProvider(out HandGhostProvider provider)
{
provider = null;
HandGhostProvider[] providers = Resources.FindObjectsOfTypeAll<HandGhostProvider>();
if (providers != null && providers.Length > 0)
{
provider = providers[0];
return true;
}
string[] assets = AssetDatabase.FindAssets($"t:{nameof(HandGhostProvider)}");
if (assets != null && assets.Length > 0)
{
string pathPath = AssetDatabase.GUIDToAssetPath(assets[0]);
provider = AssetDatabase.LoadAssetAtPath<HandGhostProvider>(pathPath);
}
return provider != null;
}
}
}

View File

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

View File

@ -0,0 +1,85 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* Licensed under the Oculus SDK License Agreement (the "License");
* you may not use the Oculus SDK except in compliance with the License,
* which is provided at the time of installation or download, or which
* otherwise accompanies this software in either electronic or hard copy form.
*
* You may obtain a copy of the License at
*
* https://developer.oculus.com/licenses/oculussdk/
*
* Unless required by applicable law or agreed to in writing, the Oculus SDK
* 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.
*/
using Oculus.Interaction.Editor;
using UnityEditor;
using UnityEngine;
namespace Oculus.Interaction.HandGrab.Editor
{
[CanEditMultipleObjects]
[CustomEditor(typeof(HandGrabInteractable))]
public partial class HandGrabInteractableEditor : SimplifiedEditor
{
private HandGrabInteractable _target;
private HandGrabScaleKeysEditor<HandGrabInteractable> _listDrawer;
private void Awake()
{
_target = target as HandGrabInteractable;
}
protected override void OnEnable()
{
base.OnEnable();
_listDrawer = new HandGrabScaleKeysEditor<HandGrabInteractable>(serializedObject,
_target.HandGrabPoses, "_handGrabPoses", true);
_editorDrawer.Draw("_handGrabPoses", (modeProp) =>
{
_listDrawer.DrawInspector();
});
}
protected override void OnDisable()
{
base.OnDisable();
_listDrawer.TearDown();
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
if (GUILayout.Button("Create Mirrored HandGrabInteractable"))
{
Mirror();
}
}
private void Mirror()
{
HandGrabInteractable mirrorInteractable =
HandGrabUtils.CreateHandGrabInteractable(_target.RelativeTo,
$"{_target.gameObject.name}_mirror");
var data = HandGrabUtils.SaveData(_target);
data.poses = null;
HandGrabUtils.LoadData(mirrorInteractable, data);
foreach (HandGrabPose point in _target.HandGrabPoses)
{
HandGrabPose mirrorPose = HandGrabUtils.CreateHandGrabPose(mirrorInteractable.transform,
mirrorInteractable.RelativeTo);
HandGrabUtils.MirrorHandGrabPose(point, mirrorPose, _target.RelativeTo);
mirrorPose.transform.SetParent(mirrorInteractable.transform);
mirrorInteractable.HandGrabPoses.Add(mirrorPose);
}
}
}
}

View File

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

View File

@ -0,0 +1,273 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* Licensed under the Oculus SDK License Agreement (the "License");
* you may not use the Oculus SDK except in compliance with the License,
* which is provided at the time of installation or download, or which
* otherwise accompanies this software in either electronic or hard copy form.
*
* You may obtain a copy of the License at
*
* https://developer.oculus.com/licenses/oculussdk/
*
* Unless required by applicable law or agreed to in writing, the Oculus SDK
* 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.
*/
using Oculus.Interaction.Editor;
using Oculus.Interaction.HandGrab.Visuals;
using Oculus.Interaction.Input;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace Oculus.Interaction.HandGrab.Editor
{
[CustomEditor(typeof(HandGrabPose))]
public class HandGrabPoseEditor : UnityEditor.Editor
{
private HandGrabPose _handGrabPose;
private HandGhostProvider _ghostVisualsProvider;
private HandGhost _handGhost;
private Handedness _lastHandedness;
private Transform _relativeTo;
private int _editMode = 0;
private SerializedProperty _handPoseProperty;
private SerializedProperty _relativeToProperty;
private const float GIZMO_SCALE = 0.005f;
private static readonly string[] EDIT_MODES = new string[] { "Edit fingers", "Follow Surface" };
private void Awake()
{
_handGrabPose = target as HandGrabPose;
}
private void OnEnable()
{
_handPoseProperty = serializedObject.FindProperty("_handPose");
_relativeToProperty = serializedObject.FindProperty("_relativeTo");
AssignMissingGhostProvider();
}
private void OnDisable()
{
DestroyGhost();
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
_relativeTo = _relativeToProperty.objectReferenceValue as Transform;
if (_handGrabPose.UsesHandPose())
{
EditorGUILayout.PropertyField(_handPoseProperty);
}
GUIStyle boldStyle = new GUIStyle(GUI.skin.label) { fontStyle = FontStyle.Bold };
EditorGUILayout.LabelField("Interactive Edition (Editor only)", boldStyle);
if (_handGrabPose.UsesHandPose())
{
DrawGhostMenu(_handGrabPose.HandPose);
}
else
{
DestroyGhost();
}
serializedObject.ApplyModifiedProperties();
}
private void DrawGhostMenu(HandPose handPose)
{
HandGhostProvider provider = EditorGUILayout.ObjectField("Ghost Provider", _ghostVisualsProvider, typeof(HandGhostProvider), false) as HandGhostProvider;
if (_handGhost == null
|| _ghostVisualsProvider != provider
|| _lastHandedness != handPose.Handedness)
{
RegenerateGhost(provider);
}
_ghostVisualsProvider = provider;
_lastHandedness = handPose.Handedness;
if (_handGrabPose.SnapSurface == null)
{
_editMode = 0;
}
else
{
_editMode = GUILayout.Toolbar(_editMode, EDIT_MODES);
}
}
public void OnSceneGUI()
{
if (SceneView.currentDrawingSceneView == null)
{
return;
}
if (_handGhost == null)
{
return;
}
if (_editMode == 0)
{
GhostEditFingers();
}
else if (_editMode == 1)
{
GhostFollowSurface();
}
}
#region ghost
private void AssignMissingGhostProvider()
{
if (_ghostVisualsProvider != null)
{
return;
}
HandGhostProviderUtils.TryGetDefaultProvider(out _ghostVisualsProvider);
}
private void RegenerateGhost(HandGhostProvider provider)
{
DestroyGhost();
CreateGhost();
}
private void CreateGhost()
{
if (_ghostVisualsProvider == null)
{
return;
}
Transform relativeTo = _handGrabPose.RelativeTo;
HandGhost ghostPrototype = _ghostVisualsProvider.GetHand(_handGrabPose.HandPose.Handedness);
_handGhost = GameObject.Instantiate(ghostPrototype, _handGrabPose.transform);
_handGhost.gameObject.hideFlags = HideFlags.HideAndDontSave;
Pose relativePose = _handGrabPose.RelativePose;
Pose pose = PoseUtils.GlobalPoseScaled(relativeTo, relativePose);
_handGhost.SetPose(_handGrabPose.HandPose, pose);
}
private void DestroyGhost()
{
if (_handGhost == null)
{
return;
}
GameObject.DestroyImmediate(_handGhost.gameObject);
}
private void GhostFollowSurface()
{
if (_handGhost == null)
{
return;
}
Pose ghostTargetPose = _handGrabPose.RelativePose;
if (_handGrabPose.SnapSurface != null)
{
Vector3 mousePosition = Event.current.mousePosition;
Ray ray = HandleUtility.GUIPointToWorldRay(mousePosition);
if (_handGrabPose.SnapSurface.CalculateBestPoseAtSurface(ray, out Pose poseInSurface, _relativeTo))
{
ghostTargetPose = PoseUtils.DeltaScaled(_relativeTo, poseInSurface);
}
}
_handGhost.SetRootPose(ghostTargetPose, _relativeTo);
Handles.color = EditorConstants.PRIMARY_COLOR_DISABLED;
Handles.DrawSolidDisc(_handGhost.transform.position, _handGhost.transform.right, 0.01f);
}
private void GhostEditFingers()
{
HandPuppet puppet = _handGhost.GetComponent<HandPuppet>();
if (puppet != null && puppet.JointMaps != null)
{
DrawBonesRotator(puppet.JointMaps);
}
}
private void DrawBonesRotator(List<HandJointMap> bones)
{
bool anyChanged = false;
for (int i = 0; i < FingersMetadata.HAND_JOINT_IDS.Length; i++)
{
bool changed = false;
HandJointId joint = FingersMetadata.HAND_JOINT_IDS[i];
HandFinger finger = FingersMetadata.JOINT_TO_FINGER[(int)joint];
if (_handGrabPose.HandPose.FingersFreedom[(int)finger] == JointFreedom.Free)
{
continue;
}
HandJointMap jointMap = bones.Find(b => b.id == joint);
if (jointMap == null)
{
continue;
}
Transform transform = jointMap.transform;
transform.localRotation = jointMap.RotationOffset * _handGrabPose.HandPose.JointRotations[i];
float scale = GIZMO_SCALE * _handGrabPose.transform.lossyScale.x;
Handles.color = EditorConstants.PRIMARY_COLOR;
Quaternion entryRotation = transform.rotation;
Quaternion rotation = Handles.Disc(entryRotation, transform.position,
transform.forward, scale, false, 0);
if (rotation != entryRotation)
{
changed = true;
}
if (FingersMetadata.HAND_JOINT_CAN_SPREAD[i])
{
Handles.color = EditorConstants.SECONDARY_COLOR;
Quaternion curlRotation = rotation;
rotation = Handles.Disc(curlRotation, transform.position,
transform.up, scale, false, 0);
if (rotation != curlRotation)
{
changed = true;
}
}
if (!changed)
{
continue;
}
transform.rotation = rotation;
Undo.RecordObject(_handGrabPose, "Bone Rotation");
_handGrabPose.HandPose.JointRotations[i] = jointMap.TrackedRotation;
anyChanged = true;
}
if (anyChanged)
{
EditorUtility.SetDirty(_handGrabPose);
}
}
#endregion
}
}

View File

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

View File

@ -0,0 +1,324 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* Licensed under the Oculus SDK License Agreement (the "License");
* you may not use the Oculus SDK except in compliance with the License,
* which is provided at the time of installation or download, or which
* otherwise accompanies this software in either electronic or hard copy form.
*
* You may obtain a copy of the License at
*
* https://developer.oculus.com/licenses/oculussdk/
*
* Unless required by applicable law or agreed to in writing, the Oculus SDK
* 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.
*/
using Oculus.Interaction.HandGrab.Visuals;
using Oculus.Interaction.Input;
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
namespace Oculus.Interaction.HandGrab.Editor
{
public class HandGrabScaleKeysEditor<TInteractable>
where TInteractable : MonoBehaviour, IRelativeToRef
{
private MonoBehaviour _target;
private SerializedObject _serializedObject;
private List<HandGrabPose> _handGrabPoses;
private bool _markAsOptional;
private SerializedProperty _posesProperty;
private ReorderableList _list;
private HashSet<float> _scalesSet = new HashSet<float>();
private IRelativeToRef _relativeToRef;
private HandGhostProvider _ghostVisualsProvider;
private HandGhost _handGhost;
private HandPose _ghostHandPose = new HandPose();
private Handedness _lastHandedness;
private float _handScale = 1f;
private const float POSE_RECT_WIDTH = 40f;
private const float LEFT_MARGIN = 15;
private const float RIGHT_MARGIN = 65f;
private const float MIN_SCALE = 0.5f;
private const float MAX_SCALE = 2f;
public HandGrabScaleKeysEditor(SerializedObject serializedObject,
List<HandGrabPose> handGrabPoses, string collectionName, bool markAsOptional)
{
_target = serializedObject.targetObject as MonoBehaviour;
_relativeToRef = serializedObject.targetObject as IRelativeToRef;
_posesProperty = serializedObject.FindProperty(collectionName);
_handGrabPoses = handGrabPoses;
_markAsOptional = markAsOptional;
HandGhostProviderUtils.TryGetDefaultProvider(out _ghostVisualsProvider);
_list = new ReorderableList(serializedObject, _posesProperty,
draggable: true, displayHeader: false,
displayAddButton: true, displayRemoveButton: true);
InitializeListDrawers(_list);
}
public void TearDown()
{
DestroyGhost();
}
public void DrawInspector()
{
EditorGUILayout.LabelField($"{(_markAsOptional ? "[Optional]" : "")} Scaled Hand Grab Poses");
Rect startRect = GUILayoutUtility.GetLastRect();
_list.DoLayoutList();
ScaledHandPoseSlider();
CheckUniqueScales();
CheckUniqueHandedness();
DrawGenerationMenu();
Rect endRect = GUILayoutUtility.GetLastRect();
DrawBox(
new Vector2(startRect.position.x, startRect.position.y + startRect.size.y),
endRect.position + endRect.size);
UpdateGhost();
}
private void DrawBox(Vector2 start, Vector2 end)
{
float margin = 5f;
float height = end.y - start.y;
Rect topBar = new Rect(start.x - margin, start.y,
margin, 1f);
Rect bottomBar = new Rect(start.x- margin, end.y,
margin, 1f);
Rect leftBar = new Rect(start.x - margin, start.y,
1f, height);
EditorGUI.DrawRect(topBar, Color.gray);
EditorGUI.DrawRect(bottomBar, Color.gray);
EditorGUI.DrawRect(leftBar, Color.gray);
}
private void InitializeListDrawers(ReorderableList list)
{
list.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
{
rect.height = EditorGUIUtility.singleLineHeight;
SerializedProperty itemProperty = list.serializedProperty.GetArrayElementAtIndex(index);
HandGrabPose grabPose = itemProperty.objectReferenceValue as HandGrabPose;
float scale = float.NaN;
if (grabPose != null)
{
scale = grabPose.transform.lossyScale.x /
_relativeToRef.RelativeTo.lossyScale.x;
}
GUIContent objectLabel = new GUIContent($"Scale x{scale.ToString("F2")}");
EditorGUI.PropertyField(rect, itemProperty, objectLabel);
};
}
private void ScaledHandPoseSlider()
{
float minScale = MIN_SCALE;
float maxScale = MAX_SCALE;
for (int i = 0; i < _handGrabPoses.Count; i++)
{
HandGrabPose grabPose = _handGrabPoses[i];
float scale = grabPose.transform.lossyScale.x;
if (scale < minScale)
{
minScale = scale;
}
if (scale > maxScale)
{
maxScale = scale;
}
}
_handScale = EditorGUILayout.Slider(_handScale, minScale, maxScale);
Rect backRect = GUILayoutUtility.GetLastRect();
backRect.x += LEFT_MARGIN;
backRect.width -= RIGHT_MARGIN;
for (int i = 0; i < _handGrabPoses.Count; i++)
{
HandGrabPose grabPose = _handGrabPoses[i];
if (grabPose == null)
{
continue;
}
float x = backRect.x + Mathf.InverseLerp(minScale, maxScale, grabPose.transform.lossyScale.x) * backRect.width;
Rect poseRect = new Rect(x - POSE_RECT_WIDTH * 0.5f, backRect.y, POSE_RECT_WIDTH, backRect.height);
EditorGUI.LabelField(poseRect, EditorGUIUtility.IconContent("curvekeyframeselected"));
}
}
private void CheckUniqueScales()
{
_scalesSet.Clear();
for (int i = 0; i < _handGrabPoses.Count; i++)
{
HandGrabPose grabPose = _handGrabPoses[i];
if (grabPose == null)
{
continue;
}
float scale = grabPose.transform.lossyScale.x /
_relativeToRef.RelativeTo.lossyScale.x;
if (_scalesSet.Contains(scale))
{
EditorGUILayout.HelpBox(
$"Duplicated {nameof(HandGrabPose)} of scale {scale} at index {i}.",
MessageType.Warning);
}
_scalesSet.Add(scale);
}
}
private void CheckUniqueHandedness()
{
bool handednessSet = false;
Handedness validHandedness = Handedness.Left;
for (int i = 0; i < _handGrabPoses.Count; i++)
{
HandGrabPose grabPose = _handGrabPoses[i];
if (grabPose == null || grabPose.HandPose == null)
{
continue;
}
Handedness grabPoseHandedness = grabPose.HandPose.Handedness;
if (!handednessSet)
{
handednessSet = true;
validHandedness = grabPoseHandedness;
}
else if (grabPoseHandedness != validHandedness)
{
EditorGUILayout.HelpBox($"Different Handedness at index {i}. " +
$"Ensure all HandGrabPoses have the same Handedness", MessageType.Warning);
}
}
}
private void DrawGenerationMenu()
{
if (GUILayout.Button($"Add HandGrabPose Key with Scale {_handScale.ToString("F2")}"))
{
AddHandGrabPose(_handScale);
}
if (GUILayout.Button("Refresh HandGrab Poses"))
{
RefreshHandPoses();
}
}
private void AddHandGrabPose(float scale)
{
HandGrabPose handGrabPose = HandGrabUtils.CreateHandGrabPose(_target.transform,
_relativeToRef.RelativeTo);
float relativeScale = scale / _relativeToRef.RelativeTo.lossyScale.x;
bool rangeFound = GrabPoseFinder.FindInterpolationRange(relativeScale, _handGrabPoses,
out HandGrabPose from, out HandGrabPose to, out float t);
handGrabPose.transform.localScale = Vector3.one * relativeScale;
_handGrabPoses.Add(handGrabPose);
EditorUtility.SetDirty(_target);
if (!rangeFound)
{
return;
}
Pose relativePose = Pose.identity;
PoseUtils.Lerp(from.RelativePose, to.RelativePose, t, ref relativePose);
Pose rootPose = PoseUtils.GlobalPoseScaled(_relativeToRef.RelativeTo, relativePose);
HandPose resultHandPose = new HandPose(from.HandPose);
HandPose.Lerp(from.HandPose, to.HandPose, t, ref resultHandPose);
Grab.GrabSurfaces.IGrabSurface surface = from.SnapSurface?.CreateDuplicatedSurface(handGrabPose.gameObject);
handGrabPose.InjectAllHandGrabPose(_relativeToRef.RelativeTo);
handGrabPose.InjectOptionalHandPose(resultHandPose);
handGrabPose.InjectOptionalSurface(surface);
handGrabPose.transform.SetPose(rootPose);
}
private void RefreshHandPoses()
{
_handGrabPoses.Clear();
HandGrabPose[] handGrabPoses = _target.GetComponentsInChildren<HandGrabPose>();
_handGrabPoses.AddRange(handGrabPoses);
EditorUtility.SetDirty(_target);
}
#region Ghost
private void UpdateGhost()
{
if (_handGrabPoses.Count == 0
|| _handGrabPoses[0].HandPose == null)
{
DestroyGhost();
return;
}
Transform relativeTo = _relativeToRef.RelativeTo;
Pose rootPose = Pose.identity;
float relativeScale = _handScale / relativeTo.lossyScale.x;
bool rangeFound = GrabPoseFinder.FindInterpolationRange(relativeScale, _handGrabPoses,
out HandGrabPose from, out HandGrabPose to, out float t);
if (!rangeFound)
{
DestroyGhost();
return;
}
HandPose.Lerp(from.HandPose, to.HandPose, t, ref _ghostHandPose);
PoseUtils.Lerp(from.RelativePose, to.RelativePose, t, ref rootPose);
rootPose = PoseUtils.GlobalPoseScaled(relativeTo, rootPose);
DisplayGhost(_ghostHandPose, rootPose, relativeScale);
}
private void DisplayGhost(HandPose handPose, Pose rootPose, float scale)
{
if (_handGhost != null
&& _lastHandedness != handPose.Handedness)
{
DestroyGhost();
}
_lastHandedness = handPose.Handedness;
if (_handGhost == null)
{
HandGhost ghostPrototype = _ghostVisualsProvider.GetHand(_lastHandedness);
_handGhost = GameObject.Instantiate(ghostPrototype, _target.transform);
_handGhost.gameObject.hideFlags = HideFlags.HideAndDontSave;
}
_handGhost.transform.localScale = Vector3.one * scale;
_handGhost.SetPose(handPose, rootPose);
}
private void DestroyGhost()
{
if (_handGhost == null)
{
return;
}
GameObject.DestroyImmediate(_handGhost.gameObject);
_handGhost = null;
}
#endregion
}
}

View File

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

View File

@ -0,0 +1,121 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* Licensed under the Oculus SDK License Agreement (the "License");
* you may not use the Oculus SDK except in compliance with the License,
* which is provided at the time of installation or download, or which
* otherwise accompanies this software in either electronic or hard copy form.
*
* You may obtain a copy of the License at
*
* https://developer.oculus.com/licenses/oculussdk/
*
* Unless required by applicable law or agreed to in writing, the Oculus SDK
* 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.
*/
using Oculus.Interaction.Editor;
using Oculus.Interaction.Input;
using System;
using UnityEditor;
using UnityEngine;
namespace Oculus.Interaction.HandGrab.Editor
{
[CustomPropertyDrawer(typeof(HandPose))]
public class HandPoseEditor : PropertyDrawer
{
private bool _foldedFreedom = true;
private bool _foldedRotations = false;
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
float multiplier = 4;
if (_foldedFreedom)
{
multiplier += Constants.NUM_FINGERS;
}
if (_foldedRotations)
{
multiplier += FingersMetadata.HAND_JOINT_IDS.Length;
}
return EditorConstants.ROW_HEIGHT * multiplier;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginProperty(position, label, property);
Rect labelPos = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
EditorGUI.indentLevel++;
Rect rowRect = new Rect(position.x, labelPos.y + EditorConstants.ROW_HEIGHT, position.width, EditorConstants.ROW_HEIGHT);
DrawFlagProperty<Handedness>(property, rowRect, "Handedness:", "_handedness", false);
rowRect.y += EditorConstants.ROW_HEIGHT;
rowRect = DrawFingersFreedomMenu(property, rowRect);
rowRect = DrawJointAngles(property, rowRect);
EditorGUI.indentLevel--;
EditorGUI.EndProperty();
}
private Rect DrawFingersFreedomMenu(SerializedProperty property, Rect position)
{
_foldedFreedom = EditorGUI.Foldout(position, _foldedFreedom, "Fingers Freedom", true);
position.y += EditorConstants.ROW_HEIGHT;
if (_foldedFreedom)
{
SerializedProperty fingersFreedom = property.FindPropertyRelative("_fingersFreedom");
EditorGUI.indentLevel++;
for (int i = 0; i < Constants.NUM_FINGERS; i++)
{
SerializedProperty finger = fingersFreedom.GetArrayElementAtIndex(i);
HandFinger fingerID = (HandFinger)i;
JointFreedom current = (JointFreedom)finger.intValue;
JointFreedom selected = (JointFreedom)EditorGUI.EnumPopup(position, $"{fingerID}", current);
finger.intValue = (int)selected;
position.y += EditorConstants.ROW_HEIGHT;
}
EditorGUI.indentLevel--;
}
return position;
}
private Rect DrawJointAngles(SerializedProperty property, Rect position)
{
_foldedRotations = EditorGUI.Foldout(position, _foldedRotations, "Joint Angles", true);
position.y += EditorConstants.ROW_HEIGHT;
if (_foldedRotations)
{
SerializedProperty jointRotations = property.FindPropertyRelative("_jointRotations");
EditorGUI.indentLevel++;
for (int i = 0; i < FingersMetadata.HAND_JOINT_IDS.Length; i++)
{
SerializedProperty finger = jointRotations.GetArrayElementAtIndex(i);
HandJointId jointID = FingersMetadata.HAND_JOINT_IDS[i];
Vector3 current = finger.quaternionValue.eulerAngles;
Vector3 rotation = EditorGUI.Vector3Field(position, $"{jointID}", current);
finger.quaternionValue = Quaternion.Euler(rotation);
position.y += EditorConstants.ROW_HEIGHT;
}
EditorGUI.indentLevel--;
}
return position;
}
private void DrawFlagProperty<TEnum>(SerializedProperty parentProperty, Rect position, string title, string fieldName, bool isFlags) where TEnum : Enum
{
SerializedProperty fieldProperty = parentProperty.FindPropertyRelative(fieldName);
TEnum value = (TEnum)Enum.ToObject(typeof(TEnum), fieldProperty.intValue);
Enum selectedValue = isFlags ?
EditorGUI.EnumFlagsField(position, title, value)
: EditorGUI.EnumPopup(position, title, value);
fieldProperty.intValue = (int)Enum.ToObject(typeof(TEnum), selectedValue);
}
}
}

View File

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

View File

@ -0,0 +1,83 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* Licensed under the Oculus SDK License Agreement (the "License");
* you may not use the Oculus SDK except in compliance with the License,
* which is provided at the time of installation or download, or which
* otherwise accompanies this software in either electronic or hard copy form.
*
* You may obtain a copy of the License at
*
* https://developer.oculus.com/licenses/oculussdk/
*
* Unless required by applicable law or agreed to in writing, the Oculus SDK
* 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.
*/
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace Oculus.Interaction.HandGrab.Editor
{
public class HandWristOffsetUndoWizard : ScriptableWizard
{
[SerializeField]
private HandWristOffset _wristOffset;
[SerializeField]
private HandGrabPose _grabPose;
[MenuItem("Oculus/Interaction/HandWristOffset Undo Wizard")]
private static void CreateWizard()
{
ScriptableWizard.DisplayWizard<HandWristOffsetUndoWizard>("HandWristOffset Undo Wizard", "Close", "Undo Offset");
}
private void OnWizardCreate()
{
}
private void OnWizardOtherButton()
{
List<HandGrabPose> children = new List<HandGrabPose>(_grabPose.GetComponentsInChildren<HandGrabPose>());
children.Remove(_grabPose);
foreach (HandGrabPose childPoint in children)
{
if (childPoint == _grabPose)
{
continue;
}
childPoint.transform.SetParent(_grabPose.transform.parent, true);
UndoOffset(childPoint);
}
UndoOffset(_grabPose);
foreach (HandGrabPose childPoint in children)
{
childPoint.transform.SetParent(_grabPose.transform, true);
}
}
private void UndoOffset(HandGrabPose grabPose)
{
Pose offset = Pose.identity;
_wristOffset.GetOffset(ref offset, grabPose.HandPose.Handedness, grabPose.transform.localScale.x);
offset.Invert();
Undo.RecordObject(grabPose.transform, "Transform Changed");
Pose pose = grabPose.transform.GetPose(Space.Self);
pose.Premultiply(offset);
grabPose.transform.SetPose(pose, Space.Self);
EditorUtility.SetDirty(grabPose.transform);
}
}
}

View File

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

Some files were not shown because too many files have changed in this diff Show More