Exemple #1
0
        /// <summary>
        /// Due to the way the Add/Remove scheduled methods work it is not possible to call entity.RemoveScheduled(a) and entity.AddScheduled(a) within the same update.
        /// Either one waits at least one update between the 2 calls, or uses this method which will set the child to the new parent.
        /// This method is threadsafe.
        /// </summary>
        /// <param name="newParent"></param>
        public void ChangeParent(SceneGraphEntity newParent)
        {
            if (newParent == null)
            {
                throw new ArgumentNullException(nameof(newParent), "if you want to remove the entity from a scenegraph, call RemoveScheduled");
            }

            if (!Initialized)
            {
                throw new NotSupportedException("Changing parent is only possible if the entity was already Initialized");
            }

            // even though initialized is set to true, it will take one more update before the entity is actually added to the scenegraph
            // assert that said update has occured, otherwise parent is not properly set
            if (!Parent.IsRegistered(this))
            {
                throw new NotSupportedException("it takes at least one frame after the entity has been Initialized before the entity is actually registered with the parent.");
            }

            if (Parent == null)
            {
                throw new NotSupportedException("The current entity must have a parent, otherwise ChangingParent is invalid.");
            }

            // allow override of parent because we are switching parent
            _parentSet = false;
            SetParent(this, newParent);
        }
Exemple #2
0
 /// <summary>
 /// Adds the entity the scenegraph.
 /// Note that the item is not added directly but rather the next time update is called.
 /// Entities that are not yet initialized are also further delayed (until initialize has executed on a background thread).
 /// The initialize thread is executed on the first parent scenegraph root that is found.
 /// Due to this, entities are not guaranteed to be added in same order as method was called (e.g. a not yet initialized entity is added first
 /// and needs 3s to initialize on the background thread. In the meantime entities that are already Initialized can be added directly).
 /// To prevent this issue, never mix initialized and non-initialized entities. Both categories are guaranteed to be added in order, UNLESS they are mixed.
 /// This method is threadsafe.
 /// </summary>
 /// <param name="child"></param>
 public virtual void AddAsync(SceneGraphEntity child)
 {
     SetParent(child, this);
     // note that the child is not added directly but rather to another list, see UpdateCollection method for reason
     if (child.Initialized)
     {
         // already initialized, add right away
         lock (_sceneGraphEntitiesToBeAdded)
         {
             _sceneGraphEntitiesToBeAdded.Add(child);
         }
     }
     else
     {
         // need to initialize first, add when callback is executed
         // the initialize background worker guarantees us the same order as we called it, so if no entity is initialized yet they are all scheduled on the worker thread in order
         // and end up being added in the same order
         _root.InitializeInBackground(child, e =>
         {
             lock (_sceneGraphEntitiesToBeAdded)
             {
                 _sceneGraphEntitiesToBeAdded.Add(e);
             }
         });
     }
 }
Exemple #3
0
 /// <summary>
 /// Scenegraph internal helper that calls initialize on the child in a background thread and runs the action after initialization.
 /// This function returns immediately, the actual initialization of the child is thus deferred.
 /// </summary>
 /// <param name="child"></param>
 /// <param name="action"></param>
 internal void InitializeInBackground(SceneGraphEntity child, Action <SceneGraphEntity> action)
 {
     if (child == null)
     {
         throw new ArgumentNullException(nameof(child));
     }
     if (child.Initialized)
     {
         // no need to call initialize again, just call the function right away
         action(child);
     }
     _sceneGraphObjectInitializer.QueueItem(child, action);
 }
Exemple #4
0
        /// <summary>
        /// Sets the parent of the child entity.
        /// Will also navigate up the tree of the parent to find the root.
        /// If a null parent is provided the function will "unset" the parent.
        /// </summary>
        /// <param name="child"></param>
        /// <param name="parent">If null, will unset the parent, otherwise will set it. Note that a parent can only be set if it has not yet been set before.</param>
        private static void SetParent(SceneGraphEntity child, SceneGraphEntity parent)
        {
            if (parent != null && child._parentSet)
            {
                throw new NotSupportedException("parent has already been set for the provided node. Remove the node from the scene graph that it is attached to and then try to add it again.");
            }

            if (parent != null)
            {
                child.Parent = parent;
                child.FindAndSetRootNode();
                child._parentSet = true;
            }
            else
            {
                child.Parent     = null;
                child._root      = null;
                child._parentSet = false;
            }
        }
Exemple #5
0
        /// <summary>
        /// Call to find out if the specific entity is already registered as a child of the current node.
        /// Note that the *Scheduled functions will not execute directly and it may take a frame (or two) for an entity to actually be registered.
        /// This method is threadsafe.
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="searchEntireTree">Defaults to false. If false will only check the direct childrend of this entity. If true will search the entire tree until all leafes are reached.</param>
        /// <returns>True if the entity is registered, false otherwise.</returns>
        public bool IsRegistered(SceneGraphEntity entity, bool searchEntireTree = false)
        {
            // create a copy of the list as this function might be called on a different thread and each update can change the source collection
            var entities = _sceneGraphEntities.ToList();

            if (entities.Contains(entity))
            {
                return(true);
            }
            if (searchEntireTree)
            {
                foreach (var e in entities)
                {
                    if (e.IsRegistered(entity, true))
                    {
                        return(true);
                    }
                }
            }
            return(false);
        }
Exemple #6
0
        /// <summary>
        /// Looks for the scenegraph this node is attached to by going up the parent chain until a scene graph is found.
        /// </summary>
        protected void FindAndSetRootNode()
        {
            SceneGraphEntity root = this;

            while (!(root is SceneGraphRoot))
            {
                root = root.Parent;
                // don't look for the ultimate root, just look for the next highest root node
                // this allows each root to run its own scheduler for all its children
                //while (root is SceneGraph && root.Parent != null)
                //{
                //    // keep going, we support nested scene graphs
                //    root = root.Parent;
                //}
            }
            if (root == null)
            {
                throw new NotSupportedException("Could not find the root of the provided parent.");
            }

            _root = (SceneGraphRoot)root;
        }
Exemple #7
0
 /// <summary>
 /// Removes the entity from the scenegraph.
 /// Note that the item is not removed immediately but rather the next time update is called.
 /// This method is threadsafe.
 /// </summary>
 /// <param name="child"></param>
 /// <returns></returns>
 public void RemoveAsync(SceneGraphEntity child)
 {
     // note that the child is not removed directly but rather scheduled in another list, see UpdateCollection method for reason
     _sceneGraphEntitiesToBeRemoved.Add(child);
 }