AREnvironmentProbeManager.cs 19.4 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411
using System;
using System.Collections.Generic;
using System.ComponentModel;
using UnityEngine;
using UnityEngine.XR.ARSubsystems;

using Object = UnityEngine.Object;

namespace UnityEngine.XR.ARFoundation
{
    /// <summary>
    /// This class creates, maintains, and destroys environment probe game object components as the
    /// <c>XREnvironmentProbeSubsystem</c> provides updates from environment probes as they are detected in the
    /// environment.
    /// </summary>
    [DisallowMultipleComponent]
    [DefaultExecutionOrder(ARUpdateOrder.k_EnvironmentProbeManager)]
    [HelpURL(HelpUrls.ApiWithNamespace + nameof(AREnvironmentProbeManager) + ".html")]
    public sealed class AREnvironmentProbeManager : ARTrackableManager<
        XREnvironmentProbeSubsystem,
        XREnvironmentProbeSubsystemDescriptor,
#if UNITY_2020_2_OR_NEWER
        XREnvironmentProbeSubsystem.Provider,
#endif
        XREnvironmentProbe,
        AREnvironmentProbe>
    {
        /// <summary>
        /// A property of the environment probe subsystem that, if enabled, automatically generates environment probes
        /// for the scene.
        /// This method is obsolete.
        /// Use <see cref="automaticPlacementRequested"/> or <see cref="automaticPlacementEnabled"/> instead.
        /// </summary>
        /// <value>
        /// <c>true</c> if automatic environment probe placement is enabled. Otherwise, <c>false</c>.
        /// </value>
        [Obsolete("Use automaticPlacementRequested or automaticPlacementEnabled instead. (2020-01-16)")]
        public bool automaticPlacement
        {
            get => m_AutomaticPlacement;
            set => automaticPlacementRequested = value;
        }

        bool supportsAutomaticPlacement => descriptor?.supportsAutomaticPlacement == true;

        /// <summary>
        /// If enabled, requests automatic generation of environment probes for the scene.
        /// </summary>
        /// <value>
        /// <c>true</c> if automatic environment probe placement is requested. Otherwise, <c>false</c>.
        /// </value>
        public bool automaticPlacementRequested
        {
            get => supportsAutomaticPlacement ? subsystem.automaticPlacementRequested : m_AutomaticPlacement;
            set
            {
                m_AutomaticPlacement = value;
                SetAutomaticPlacementStateOnSubsystem();
             }
        }

        /// <summary>
        /// <c>true</c> if automatic placement is enabled on the subsystem.
        /// </summary>
        public bool automaticPlacementEnabled => supportsAutomaticPlacement ? subsystem.automaticPlacementEnabled : false;

        [SerializeField]
        [Tooltip("Whether environment probes should be automatically placed in the environment (if supported).")]
        bool m_AutomaticPlacement = true;

        /// <summary>
        /// Specifies the texture filter mode to be used with the environment texture.
        /// </summary>
        /// <value>
        /// The texture filter mode to be used with the environment texture.
        /// </value>
        public FilterMode environmentTextureFilterMode
        {
            get => m_EnvironmentTextureFilterMode;
            set => m_EnvironmentTextureFilterMode = value;
        }
        [SerializeField]
        [Tooltip("The texture filter mode to be used with the reflection probe environment texture.")]
        FilterMode m_EnvironmentTextureFilterMode = FilterMode.Trilinear;

        /// <summary>
        /// Specifies whether the environment textures should be returned as HDR textures.
        /// </summary>
        /// <value>
        /// <c>true</c> if the environment textures should be returned as HDR textures. Otherwise, <c>false</c>.
        /// </value>
        [Obsolete("Use environmentTextureHDRRequested or environmentTextureHDREnabled instead. (2020-01-16)")]
        public bool environmentTextureHDR
        {
            get => m_EnvironmentTextureHDR;
            set => environmentTextureHDRRequested = value;
        }
        [SerializeField]
        [Tooltip("Whether the environment textures should be returned as HDR textures.")]
        bool m_EnvironmentTextureHDR = true;

        bool supportsEnvironmentTextureHDR => descriptor?.supportsEnvironmentTextureHDR == true;

        /// <summary>
        /// Get or set whether high dynamic range environment textures are requested.
        /// </summary>
        /// <value></value>
        public bool environmentTextureHDRRequested
        {
            get => supportsEnvironmentTextureHDR ? subsystem.environmentTextureHDRRequested : m_EnvironmentTextureHDR;
            set
            {
                m_EnvironmentTextureHDR = value;
                SetEnvironmentTextureHDRStateOnSubsystem();
            }
        }

        /// <summary>
        /// Queries whether environment textures will be provided with high dynamic range.
        /// </summary>
        public bool environmentTextureHDREnabled => supportsEnvironmentTextureHDR ? subsystem.environmentTextureHDREnabled : false;

        /// <summary>
        /// Specifies a debug prefab that will be attached to all environment probes.
        /// </summary>
        /// <value>
        /// A debug prefab that will be attached to all environment probes.
        /// </value>
        /// <remarks>
        /// Setting a debug prefab allows for these environment probes to be more readily visualized but is not
        /// required for normal operation of this manager. This script will automatically create reflection probes for
        /// all environment probes reported by the <c>XREnvironmentProbeSubsystem</c>.
        /// </remarks>
        public GameObject debugPrefab
        {
            get => m_DebugPrefab;
            set => m_DebugPrefab = value;
        }
        [SerializeField]
        [Tooltip("A debug prefab that allows for these environment probes to be more readily visualized.")]
        GameObject m_DebugPrefab;

        /// <summary>
        /// Invoked once per frame with lists of environment probes that have been added, updated, and removed since the last frame.
        /// </summary>
        public event Action<AREnvironmentProbesChangedEvent> environmentProbesChanged;

        /// <summary>
        /// Attempts to find the environment probe matching the trackable ID currently in the scene.
        /// </summary>
        /// <param name='trackableId'>The trackable ID of an environment probe for which to search.</param>
        /// <returns>
        /// Environment probe in the scene matching the <paramref name="trackableId"/> or <c>null</c> if no matching
        /// environment probe is found.
        /// </returns>
        public AREnvironmentProbe GetEnvironmentProbe(TrackableId trackableId)
        {
            if (m_Trackables.TryGetValue(trackableId, out var environmentProbe))
                return environmentProbe;

            return null;
        }

        /// <summary>
        /// Creates a new environment probe at <paramref name="pose"/> with <paramref name="scale"/> and <paramref name="size"/>
        /// if supported by the subsystem. Use the <see cref="SubsystemLifecycleManager{TSubsystem, TSubsystemDescriptor}.descriptor"/>'s
        /// `supportsManualPlacement` property to determine support for this feature. If successful, a new
        /// <c>GameObject</c> with an <see cref="AREnvironmentProbe"/> will be created
        /// immediately; however, the provider may not report the environment probe as added until a future frame. Check the
        /// status of the probe by inspecting its
        /// <see cref="ARTrackable{TSessionRelativeData,TTrackable}.pending"/> property.
        /// </summary>
        /// <param name="pose">The position and rotation at which to create the new environment probe.</param>
        /// <param name="scale">The scale of the new environment probe.</param>
        /// <param name="size">The size (dimensions) of the new environment probe.</param>
        /// <returns>A new <see cref="AREnvironmentProbe"/> if successful, otherwise <c>null</c>.</returns>
        /// <exception cref="System.InvalidOperationException">Thrown if this manager is not enabled</exception>
        /// <exception cref="System.InvalidOperationException">Thrown if this manager has no subsystem.</exception>
        /// <exception cref="System.NotSupportedException">Thrown if manual placement is not supported by this subsystem.
        /// Check for support on the <see cref="SubsystemLifecycleManager{TSubsystem, TSubsystemDescriptor}.descriptor"/>'s
        ///     `supportsManualPlacement` property.</exception>
        [Obsolete("Add an environment probe using AddComponent<" + nameof(AREnvironmentProbe) + ">(). (2020-10-06)")]
        public AREnvironmentProbe AddEnvironmentProbe(Pose pose, Vector3 scale, Vector3 size)
        {
            if (!enabled)
                throw new InvalidOperationException("Cannot create an environment probe from a disabled environment probe manager.");

            if (subsystem == null)
                throw new InvalidOperationException("Environment probe manager has no subsystem. Enable the manager first.");

            if (!descriptor.supportsManualPlacement)
                throw new NotSupportedException("Manual environment probe placement is not supported by this subsystem.");

            var sessionRelativePose = sessionOrigin.trackablesParent.InverseTransformPose(pose);
            if (subsystem.TryAddEnvironmentProbe(sessionRelativePose, scale, size, out var sessionRelativeData))
            {
                var probe = CreateTrackableImmediate(sessionRelativeData);
                probe.placementType = AREnvironmentProbePlacementType.Manual;
                return probe;
            }

            return null;
        }

        internal bool TryAddEnvironmentProbe(AREnvironmentProbe probe)
        {
            if (!CanBeAddedToSubsystem(probe))
                return false;

            var reflectionProbe = probe.GetComponent<ReflectionProbe>();
            if (reflectionProbe == null)
                throw new InvalidOperationException($"Each {nameof(AREnvironmentProbe)} requires a {nameof(ReflectionProbe)} component.");

            if (!descriptor.supportsManualPlacement)
                throw new NotSupportedException("Manual environment probe placement is not supported by this subsystem.");

            var probeTransform = probe.transform;
            var trackablesParent = sessionOrigin.trackablesParent;
            var poseInSessionSpace = trackablesParent.InverseTransformPose(new Pose(probeTransform.position, probeTransform.rotation));

            var worldToLocalSession = trackablesParent.worldToLocalMatrix;
            var localToWorldProbe = probeTransform.localToWorldMatrix;

            // We want to calculate the "local-to-parent" of the probe if the session origin were its parent.
            //     LTW_session * LTP_probe = LTW_probe
            // =>  LTP_probe = inverse(LTW_session) * LTW_probe
            var localToParentProbe = worldToLocalSession * localToWorldProbe;
            var sessionSpaceScale = localToParentProbe.lossyScale;

            if (subsystem.TryAddEnvironmentProbe(poseInSessionSpace, sessionSpaceScale, reflectionProbe.size, out var sessionRelativeData))
            {
                CreateTrackableFromExisting(probe, sessionRelativeData);
                probe.placementType = AREnvironmentProbePlacementType.Manual;
                return probe;
            }

            return false;
        }

        /// <summary>
        /// Remove an existing environment probe. Support for this feature is provider-specific. Check for support with
        /// the <see cref="SubsystemLifecycleManager{TSubsystem, TSubsystemDescriptor}.descriptor"/>'s
        /// `supportsRemovalOfManual` and `supportsRemovalOfAutomatic` properties.
        /// </summary>
        /// <param name="probe">The environment probe to remove</param>
        /// <returns><c>true</c> if the environment probe was removed, otherwise <c>false</c>.</returns>
        /// <exception cref="System.InvalidOperationException">Thrown if this manager is not enabled.</exception>
        /// <exception cref="System.InvalidOperationException">Thrown if
        ///     <see cref="SubsystemLifecycleManager{TSubsystem, TSubsystemDescriptor}.subsystem"/> is `null`.</exception>
        /// <exception cref="System.ArgumentNullException">Thrown if <paramref name="probe"/> is `null`.</exception>
        /// <exception cref="System.InvalidOperationException">
        /// Thrown if the environment probe was manually placed, but removal of manually placed probes is not supported.
        /// You can check for this case with <see cref="AREnvironmentProbe.placementType"/> and the
        /// <see cref="SubsystemLifecycleManager{TSubsystem, TSubsystemDescriptor}.descriptor"/>'s
        /// 'supportsRemovalOfManual` property.
        /// </exception>
        /// <exception cref="System.InvalidOperationException">
        /// Thrown if the environment probe was automatically placed, but removal of automatically placed probes is not supported.
        /// You can check for this case with <see cref="AREnvironmentProbe.placementType"/> and the
        /// <see cref="SubsystemLifecycleManager{TSubsystem, TSubsystemDescriptor}.descriptor"/>'s
        /// `supportsRemovalOfAutomatic` property.
        /// </exception>
        [Obsolete("Call Destroy() on the " + nameof(AREnvironmentProbe) + " component to remove it. (2020-10-06)")]
        public bool RemoveEnvironmentProbe(AREnvironmentProbe probe)
        {
            if (!enabled)
                throw new InvalidOperationException("Cannot remove an environment probe from a disabled environment probe manager.");

            if (subsystem == null)
                throw new InvalidOperationException("Environment probe manager has no subsystem. Enable the manager first.");

            if (probe == null)
                throw new ArgumentNullException(nameof(probe));

            var desc = descriptor;

            if ((probe.placementType == AREnvironmentProbePlacementType.Manual) && !desc.supportsRemovalOfManual)
                throw new InvalidOperationException("Removal of manually placed environment probes are not supported by this subsystem.");

            if ((probe.placementType == AREnvironmentProbePlacementType.Automatic) && !desc.supportsRemovalOfAutomatic)
                throw new InvalidOperationException("Removal of automatically placed environment probes are not supported by this subsystem.");

            if (subsystem.RemoveEnvironmentProbe(probe.trackableId))
            {
                DestroyPendingTrackable(probe.trackableId);
                return true;
            }

            return false;
        }

        internal bool TryRemoveEnvironmentProbe(AREnvironmentProbe probe)
        {
            if (probe == null)
                throw new ArgumentNullException(nameof(probe));

            if (subsystem == null)
                return false;

            var desc = descriptor;

            if ((probe.placementType == AREnvironmentProbePlacementType.Manual) && !desc.supportsRemovalOfManual)
                throw new InvalidOperationException("Removal of manually placed environment probes are not supported by this subsystem.");

            if ((probe.placementType == AREnvironmentProbePlacementType.Automatic) && !desc.supportsRemovalOfAutomatic)
                throw new InvalidOperationException("Removal of automatically placed environment probes are not supported by this subsystem.");

            if (subsystem.RemoveEnvironmentProbe(probe.trackableId))
            {
                if (m_PendingAdds.ContainsKey(probe.trackableId))
                {
                    m_PendingAdds.Remove(probe.trackableId);
                    m_Trackables.Remove(probe.trackableId);
                }

                probe.pending = false;
                return true;
            }

            return false;
        }

        /// <summary>
        /// The name of the `GameObject` for each instantiated <see cref="AREnvironmentProbe"/>.
        /// </summary>
        protected override string gameObjectName => nameof(AREnvironmentProbe);

        /// <summary>
        /// Gets the prefab that should be instantiated for each <see cref="AREnvironmentProbe"/>. May be `null`.
        /// </summary>
        /// <returns>The prefab that should be instantiated for each <see cref="AREnvironmentProbe"/>.</returns>
        protected override GameObject GetPrefab() => m_DebugPrefab;

        /// <summary>
        /// Enables the environment probe functionality by registering listeners for the environment probe events, if
        /// the <c>XREnvironmentProbeSubsystem</c> exists, and enabling environment probes in the AR subsystem manager.
        /// </summary>
        protected override void OnBeforeStart()
        {
            SetAutomaticPlacementStateOnSubsystem();
            SetEnvironmentTextureHDRStateOnSubsystem();
        }

        /// <summary>
        /// Destroys any game objects created by this environment probe manager for each environment probe, and clears
        /// the mapping of environment probes.
        /// </summary>
        protected override void OnDestroy()
        {
            base.OnDestroy();
            foreach (var kvp in m_Trackables)
            {
                var environmentProbe = kvp.Value;
                Object.Destroy(environmentProbe.gameObject);
            }
        }

        /// <summary>
        /// Invoked when the base class detects trackable changes.
        /// </summary>
        /// <param name="added">The list of added <see cref="AREnvironmentProbe"/>.</param>
        /// <param name="updated">The list of updated <see cref="AREnvironmentProbe"/>.</param>
        /// <param name="removed">The list of removed <see cref="AREnvironmentProbe"/>.</param>
        protected override void OnTrackablesChanged(
            List<AREnvironmentProbe> added,
            List<AREnvironmentProbe> updated,
            List<AREnvironmentProbe> removed)
        {
            if (environmentProbesChanged != null)
            {
                using (new ScopedProfiler("OnEnvironmentProbesChanged"))
                {
                    environmentProbesChanged(new AREnvironmentProbesChangedEvent(added, updated, removed));
                }
            }
        }

        /// <summary>
        /// Invoked when an <see cref="AREnvironmentProbe"/> is created.
        /// </summary>
        /// <param name="probe">The <see cref="AREnvironmentProbe"/> that was just created.</param>
        protected override void OnCreateTrackable(AREnvironmentProbe probe)
        {
            probe.environmentTextureFilterMode = m_EnvironmentTextureFilterMode;
        }

        /// <summary>
        /// Sets the current state of the <see cref="automaticPlacement"/> property to the
        /// <c>XREnvironmentProbeSubsystem</c>, if the subsystem exists and supports automatic placement.
        /// </summary>
        void SetAutomaticPlacementStateOnSubsystem()
        {
            if (enabled && supportsAutomaticPlacement)
            {
                subsystem.automaticPlacementRequested = m_AutomaticPlacement;
            }
        }

        /// <summary>
        /// Sets the current state of the <see cref="environmentTextureHDR"/> property to the
        /// <c>XREnvironmentProbeSubsystem</c>, if the subsystem exists and supports HDR environment textures.
        /// </summary>
        void SetEnvironmentTextureHDRStateOnSubsystem()
        {
            if (enabled && supportsEnvironmentTextureHDR)
            {
                subsystem.environmentTextureHDRRequested = m_EnvironmentTextureHDR;
            }
        }
    }
}