/// <summary> /// Bind this proxy entity to an existing remote entity. /// <para> /// This function assumes the user will fully bind proxy entities for the remote entity parent, /// if a parent exists. /// /// Trying to bind an already bound remote entity will throw an exception. /// </para> /// </summary> /// <param name="remoteEntity">The entity to bind.</param> /// <param name="syncRemoteToLocal"> /// Whether or not to the sync the proxy entity to the remote entity properties. /// </param> public void Bind(ARREntity remoteEntity, bool syncRemoteToLocal) { if (this.IsRemoteEntityValid && !this.RemoteEntity.Equals(remoteEntity)) { throw new InvalidOperationException($"Trying to bind an already bound {nameof(ARREntitySync)}"); } else if (!this.IsAttached) { throw new InvalidOperationException($"{nameof(ARREntitySync)} is not attached."); } else { if (!this.IsRemoteEntityValid) { BindToLocalEntity(remoteEntity, this.Owner); this.RemoteEntity = remoteEntity; } if (syncRemoteToLocal) { this.SyncToLocal(); } } }
/// <summary> /// Find all matching entities by a regex pattern. This entity's and all children's names will be tested against /// the given regest pattern. /// </summary> public static Remote.Entity[] FindAllByPattern(this Remote.Entity entity, string pattern) { List <Remote.Entity> entities = new List <Entity>(); Regex regex = null; try { regex = new Regex(pattern); } catch (Exception) { Debug.LogFormat(LogType.Warning, LogOption.NoStacktrace, null, "{0}", $"Invalid regex pattern '{pattern}' given to FindAllByPattern."); } if (regex != null) { entity.VisitEntity((Remote.Entity child) => { if (regex.IsMatch(child.Name)) { entities.Add(child); } return(Entity.VisitorResult.ContinueVisit); }); } return(entities.ToArray()); }
/// <summary> /// Tries to find an existing <see cref="ARREntitySync"/> proxy component associated /// with remote <see cref="ARREntity"/>. /// </summary> /// <param name="remoteEntity">The remote <see cref="ARREntity"/>.</param> /// <param name="syncComponent">The associated <see cref="ARREntitySync"/> proxy component.</param> /// <returns> /// <c>true</c> if an associated <see cref="ARREntitySync"/> proxy component is found; /// otherwise, <c>false</c>. /// </returns> public static bool TryGetSyncComponent(ARREntity remoteEntity, out ARREntitySync syncComponent) { if (proxiesByRemoteId.TryGetValue(remoteEntity.InteropId, out var weak) && weak.TryGetTarget(out var localEntity) && localEntity != null) { // Proxy local entity is alive and well. syncComponent = localEntity.FindComponent <ARREntitySync>(); }
/// <summary> /// Get an existing proxy <see cref="EvergineEntity"/> for an remote <see cref="ARREntity"/>. /// If no proxy entity has been mapped to this remote entity then null. /// </summary> /// <param name="remoteEntity">The remote <see cref="ARREntity"/>.</param> /// <returns>An proxy <see cref="EvergineEntity"/> if exists; otherwise, <c>null</c>.</returns> public static EvergineEntity GetExistingProxyEntity(this ARREntity remoteEntity) { if (ARREntitySync.TryGetSyncComponent(remoteEntity, out var syncComponent)) { return(syncComponent.Owner); } return(null); }
/// <summary> /// Create an Azure Remote Rendering component of a given type on the given entity. /// </summary> public static T CreateComponentOfType <T>(this Remote.Entity entity) where T : ComponentBase { if (entity == null || !RemoteManagerUnity.IsInitialized) { return(default(T)); } T result = RemoteManagerUnity.CurrentSession?.Actions.CreateComponent(GetObjectType <T>(), entity) as T ?? null; return(result); }
/// <summary> /// Get the collider enable override. True if entity's colliders are enabled, false otherwise. /// </summary> public static bool GetColliderEnabledOverride(this Remote.Entity entity) { if (entity == null || !entity.Valid) { return(false); } var component = entity.EnsureComponentOfType <HierarchicalStateOverrideComponent>(); return(component.DisableCollisionState == HierarchicalEnableState.InheritFromParent); }
/// <summary> /// Get the visibility override /// </summary> public static bool GetVisibilityOverride(this Remote.Entity entity) { if (entity == null || !entity.Valid) { return(false); } var component = entity.EnsureComponentOfType <HierarchicalStateOverrideComponent>(); bool hidden = (component.Flags & HierarchicalStates.Hidden) == HierarchicalStates.Hidden; return(!hidden); }
/// <summary> /// Set the visibility override /// </summary> public static void SetVisibilityOverride(this Remote.Entity entity, bool value) { if (entity == null || !entity.Valid) { return; } var component = entity.EnsureComponentOfType <HierarchicalStateOverrideComponent>(); component.HiddenState = value ? HierarchicalEnableState.InheritFromParent : HierarchicalEnableState.ForceOn; component.DisableCollisionState = value ? HierarchicalEnableState.InheritFromParent : HierarchicalEnableState.ForceOn; }
/// <summary> /// Get the collider enablement override. True if entity's colliders are enabled, false otherwise. /// </summary> public static bool GetColliderEnabledOverride(this Remote.Entity entity) { if (entity == null || !entity.Valid) { return(false); } var component = entity.EnsureComponentOfType <HierarchicalStateOverrideComponent>(); bool disabled = (component.Flags & HierarchicalStates.DisableCollision) == HierarchicalStates.DisableCollision; return(!disabled); }
/// <summary> /// Replace all mesh materials with the given material. /// </summary> public static void ReplaceMaterials(this Remote.Entity entity, Remote.Material material) { IEnumerable <Remote.MeshComponent> meshComponents = entity?.FindComponentsOfType <MeshComponent>(); foreach (var mesh in meshComponents) { int materialCount = mesh.UsedMaterials.Count; for (int i = 0; i < materialCount; i++) { mesh.SetMaterial(i, material); } } }
/// <summary> /// Create an Azure Remote Rendering component of a given type on the given entity, if one doesn't already exist. /// </summary> public static T EnsureComponentOfType <T>(this Remote.Entity entity) where T : ComponentBase { if (entity == null) { return(default(T)); } T component = entity.FindComponentOfType <T>(); if (component == null) { component = entity.CreateComponentOfType <T>(); } return(component); }
/// <summary> /// Removes the proxy entity bound to this remote entity without destroying the remote entity. /// </summary> /// <param name="entityManager">The entity manager where the proxy entity is included.</param> /// <param name="remoteEntity">The remote entity.</param> /// <param name="removeFlags">Flags to indicate how to proceed with parent entities of the proxy entity.</param> public static void RemoveProxyEntity(this EntityManager entityManager, ARREntity remoteEntity, ARRRemoveProxyEntityFlags removeFlags) { var proxyEntity = remoteEntity.GetExistingProxyEntity(); if (proxyEntity == null) { return; } ARREntitySync sync; EvergineEntity previous = null; if (removeFlags.HasFlag(ARRRemoveProxyEntityFlags.DestroyEmptyParents)) { sync = proxyEntity.FindComponent <ARREntitySync>(); while (proxyEntity.Parent?.NumChildren == 1) { previous = proxyEntity; proxyEntity = proxyEntity.Parent; // Unbind on our way up the hierarchy for performance, keep the last sync bound in case keepRemoteRoot is true. var syncNext = proxyEntity.FindComponent <ARREntitySync>(); if (syncNext == null) { break; } // Unbind first entity recursively since it doesn't need to be leaf sync.Unbind(sync.RemoteEntity == remoteEntity); sync = syncNext; } } if (removeFlags.HasFlag(ARRRemoveProxyEntityFlags.KeepRemoteRoot)) { proxyEntity = previous; } else { proxyEntity?.FindComponent <ARREntitySync>().Unbind(true); } if (proxyEntity != null) { entityManager.Remove(proxyEntity); } }
/// <summary> /// This is the implementation of FindFirstParentEntity. Call FindFirstParentEntity(Remote.Entity) /// to correctly start the recursion. /// </summary> private static Entity.VisitorResult VisitParentEntityImpl(this Remote.Entity entity, Entity.VisitEntityDelegate visitor) { if (entity == null || visitor(entity) == Entity.VisitorResult.ExitVisit) { return(Entity.VisitorResult.ExitVisit); } if (entity.Parent != null) { if (entity.Parent.VisitParentEntityImpl(visitor) == Entity.VisitorResult.ExitVisit) { return(Entity.VisitorResult.ExitVisit); } } return(Entity.VisitorResult.ContinueVisit); }
/// <summary> /// This is a "no exception" version of QueryLocalBoundsAsync(). This will catch exceptions and return a default result. /// </summary> public static async Task <Bounds> SafeQueryLocalBoundsAsync(this Remote.Entity entity) { Bounds result = new Bounds(Vector3.positiveInfinity, Vector3.negativeInfinity); try { var arrBounds = await entity.QueryLocalBoundsAsync().AsTask(); result = arrBounds.toUnity(); } catch (Exception ex) { UnityEngine.Debug.LogFormat(LogType.Warning, LogOption.NoStacktrace, null, "{0}", $"Failed to get bounds of remote object. Reason: {ex.Message}"); } return(result); }
/// <summary> /// Find first entity in this entity's parents (inclusive of itself) that fulfills pred. /// </summary> public static Entity FindFirstParentEntity(this Remote.Entity entity, Entity.EntitySearchDelegate pred) { if (pred(entity)) { return(entity); } if (entity.Parent != null) { Entity res = entity.Parent.FindFirstParentEntity(pred); if (res != null) { return(res); } } return(null); }
/// <summary> /// Set the collider enable override /// </summary> public static void SetColliderEnabledOverride(this Remote.Entity entity, bool enable) { if (entity == null || !entity.Valid) { return; } var component = entity.EnsureComponentOfType <HierarchicalStateOverrideComponent>(); if (enable) { component.DisableCollisionState = HierarchicalEnableState.InheritFromParent; } else { component.DisableCollisionState = HierarchicalEnableState.ForceOn; } }
/// <summary> /// Synchronize local proxy <see cref="EvergineEntity"/> transform and name /// with the remote <see cref="ARREntity"/>. /// </summary> public void SyncToRemote() { if (this.IsRemoteEntityValid) { ARREntity currentRemoteParent = null; bool useGlobalTransform = false; var parent = this.Owner.Parent; if (parent != null) { var parentSync = parent.FindComponent <ARREntitySync>(); if (parentSync != null && parentSync.IsRemoteEntityValid) { currentRemoteParent = parentSync.RemoteEntity; } else { // Parent exists but is not a remote entity, compute global transform. useGlobalTransform = true; } } if (!object.Equals(this.RemoteEntity.Parent, currentRemoteParent)) { // Apply re-parent of entity. this.RemoteEntity.Parent = currentRemoteParent; } if (useGlobalTransform) { this.RemoteEntity.Position = this.transform.Position.ToRemoteDouble(); this.RemoteEntity.Rotation = this.transform.Orientation.ToRemote(); this.RemoteEntity.Scale = this.transform.Scale.ToRemoteFloat(); } else { this.RemoteEntity.Position = this.transform.LocalPosition.ToRemoteDouble(); this.RemoteEntity.Rotation = this.transform.LocalOrientation.ToRemote(); this.RemoteEntity.Scale = this.transform.LocalScale.ToRemoteFloat(); } this.RemoteEntity.Name = this.Owner.Name; } }
/// <summary> /// Find the Azure Remote Rendering components of a given type on the given entity. /// </summary> public static IEnumerable <T> FindComponentsOfType <T>(this Remote.Entity entity) where T : ComponentBase { if (entity == null) { yield break; } IReadOnlyList <ComponentBase> components = entity.Components; int count = components.Count; for (int i = 0; i < count; i++) { ComponentBase component = components[i]; if (component is T) { yield return((T)component); } } }
/// <summary> /// Set the visibility override /// </summary> public static void SetVisibilityOverride(this Remote.Entity entity, bool value) { if (entity == null || !entity.Valid) { return; } // Clear the hidden flag, or add it to the overrides var component = entity.EnsureComponentOfType <HierarchicalStateOverrideComponent>(); var newFlags = component.Flags & ~HierarchicalStates.Hidden & ~HierarchicalStates.DisableCollision; if (!value) { newFlags |= (HierarchicalStates.Hidden | HierarchicalStates.DisableCollision); } // Ensure remote is up-to-date if (component.Flags != newFlags) { component.Flags = newFlags; } }
/// <summary> /// Create a proxy <see cref="EvergineEntity"/> for a remote <see cref="ARREntity"/>. /// <para> /// /// When created, the path from the remote <see cref="ARREntity"/> to the remote scene root will have /// proxy entities created for it. /// These proxy entities must be created in order to appropriately set the /// <see cref="EvergineEntity.Parent"/> for the proxy entity. /// /// As a side effect, this means that, given a Remote hierarchy: /// /// ARR.Parent /// ARR.Child /// /// Calling <see cref="CreateProxyEntity"/> on ARR.Child will also create a proxy entity for ARR.Parent. /// </para> /// </summary> /// <param name="entityManager">The entity manager where the proxy entity is included.</param> /// <param name="remoteEntity">The remote entity.</param> /// <param name="mode">Whether the proxy components will be created.</param> /// <param name="recursive">Whether to create proxy entities for children of the remote entity.</param> /// <returns>A proxy <see cref="EvergineEntity"/> for a remote <see cref="ARREntity"/>.</returns> public static EvergineEntity CreateProxyEntity(this EntityManager entityManager, ARREntity remoteEntity, ARRCreationMode mode, bool recursive = false) { if (!remoteEntity.Valid) { return(null); } if (ARREntitySync.TryGetSyncComponent(remoteEntity, out _)) { throw new Exception("A proxy entity for this remote entity already exists!"); } var proxyEntity = new EvergineEntity(); proxyEntity.AddComponent(new Transform3D()); var remoteParentEntity = remoteEntity.Parent; if (remoteParentEntity != null) { var proxyParentEntity = GetExistingProxyEntity(remoteParentEntity); if (proxyParentEntity == null) { proxyParentEntity = entityManager.CreateProxyEntity(remoteParentEntity, ARRCreationMode.DoNotCreateProxyComponents, false); } proxyParentEntity.AddChild(proxyEntity); } else { entityManager.Add(proxyEntity); } var sync = proxyEntity.FindComponent <ARREntitySync>(); if (sync == null) { sync = new ARREntitySync(); proxyEntity.AddComponent(sync); } sync.Bind(remoteEntity, true); if (mode == ARRCreationMode.CreateProxyComponents) { proxyEntity.CreateARRComponentsFromRemoteEntity(remoteEntity); } if (recursive) { foreach (var remoteChild in remoteEntity.Children) { entityManager.CreateProxyEntity(remoteChild, mode, true); } } return(proxyEntity); }
/// <summary> /// Finds a proxy <see cref="EvergineEntity"/> for a remote <see cref="ARREntity"/>. /// If no proxy entity already exists, a new one will be created. /// </summary> /// <param name="entityManager">The entity manager where the proxy entity is included.</param> /// <param name="remoteEntity">The remote entity.</param> /// <param name="mode">Whether the proxy components will be created.</param> /// <param name="recursive">Whether to create proxy entities for children of the remote entity.</param> /// <returns>A proxy <see cref="EvergineEntity"/> for a remote <see cref="ARREntity"/>.</returns> public static EvergineEntity FindOrCreateProxyEntity(this EntityManager entityManager, ARREntity remoteEntity, ARRCreationMode mode, bool recursive = false) { var proxyEntity = remoteEntity.GetExistingProxyEntity(); if (proxyEntity == null) { proxyEntity = entityManager.CreateProxyEntity(remoteEntity, mode, false); } else if (mode == ARRCreationMode.CreateProxyComponents) { proxyEntity.CreateARRComponentsFromRemoteEntity(remoteEntity); } if (recursive) { foreach (var remoteChild in remoteEntity.Children) { entityManager.FindOrCreateProxyEntity(remoteChild, mode, true); } } return(proxyEntity); }
/// <summary> /// Create proxy components for the proxy entity for all ARR remote Components on the remote entity. /// </summary> /// <param name="proxyEntity">The local proxy <see cref="EvergineEntity"/>.</param> /// <param name="remoteEntity">The remote <see cref="ARREntity"/>.</param> public static void CreateARRComponentsFromRemoteEntity(this EvergineEntity proxyEntity, ARREntity remoteEntity) { foreach (var comp in remoteEntity.Components) { switch (comp.Type) { case ObjectType.MeshComponent: proxyEntity.BindARRComponent <ARRMeshComponent>(comp); break; case ObjectType.CutPlaneComponent: proxyEntity.BindARRComponent <ARRCutPlaneComponent>(comp); break; case ObjectType.HierarchicalStateOverrideComponent: proxyEntity.BindARRComponent <ARRHierarchicalStateOverrideComponent>(comp); break; case ObjectType.PointLightComponent: proxyEntity.BindARRComponent <ARRPointLightComponent>(comp); break; case ObjectType.SpotLightComponent: proxyEntity.BindARRComponent <ARRSpotLightComponent>(comp); break; case ObjectType.DirectionalLightComponent: proxyEntity.BindARRComponent <ARRDirectionalLightComponent>(comp); break; default: throw new InvalidOperationException("Unrecognized component type in ARR to Evergine translation."); } } }