Exemplo n.º 1
0
        ///<summary>
        /// Removes a member from the simulation island.
        ///</summary>
        ///<param name="member">Member to remove.</param>
        ///<exception cref="Exception">Thrown when the member does not belong to this simulation island.</exception>
        public void Remove(SimulationIslandMember member)
        {
            //Is this method ever used?  What if old islands are simply cleared and a new one is repopulated instead?
            //More amenable to UFBRPC approach, probably quicker/simpler overall than removing even with lists
            //Consider a single block leaving a large island. BFS will quickly find out the necessary information to quickly
            //remove everything from the old island.
            //Event handlers will hold references still if not cleaned up via removal...

            //This method is not thread safe.
            //TODO: Should it wake the island up?
            if (member.simulationIsland == this)
            {
                memberCount--;
                member.simulationIsland                = null;
                member.Activated                      -= memberActivatedDelegate;
                member.BecameDeactivationCandidate    -= becameDeactivationCandidateDelegate;
                member.BecameNonDeactivationCandidate -= becameNonDeactivationCandidateDelegate;
                if (member.IsDeactivationCandidate)
                {
                    deactivationCandidateCount--;
                }
            }
            else
            {
                throw new ArgumentException("Member does not belong to island; cannot remove.");
            }
        }
 /// <summary>
 /// Adds the member to the connection.
 /// </summary>
 /// <param name="simulationIslandMember">Member to add.</param>
 internal void Add(SimulationIslandMember simulationIslandMember)
 {
     //Note that the simulation member does not yet know about this connection, so the index is assigned to -1.
     entries.Add(new Entry {
         Index = -1, Member = simulationIslandMember
     });
 }
 /// <summary>
 /// Searches the list of members related to this connection and sets the index associated with this connection to the given value.
 /// </summary>
 /// <param name="member">Member to change the index for.</param>
 /// <param name="index">New index of this connection in the member's connections list.</param>
 internal void SetListIndex(SimulationIslandMember member, int index)
 {
     for (int i = 0; i < entries.Count; i++)
     {
         if (member == entries.Elements[i].Member)
         {
             entries.Elements[i].Index = index;
             break;
         }
     }
 }
 /// <summary>
 /// Removes the member from this island.
 /// </summary>
 /// <param name="simulationIslandMember">Removes the member from the manager.</param>
 public void Remove(SimulationIslandMember simulationIslandMember)
 {
     if (simulationIslandMember.DeactivationManager == this)
     {
         simulationIslandMember.DeactivationManager = null;
         simulationIslandMembers.Remove(simulationIslandMember);
         RemoveSimulationIslandFromMember(simulationIslandMember);
     }
     else
     {
         throw new ArgumentException("Cannot remove that member from this DeactivationManager; it belongs to a different or no manager.");
     }
 }
Exemplo n.º 5
0
 ///<summary>
 /// Adds a member to the simulation island.
 ///</summary>
 ///<param name="member">Member to add.</param>
 ///<exception cref="Exception">Thrown when the member being added is either non-dynamic or already has a simulation island.</exception>
 public void Add(SimulationIslandMember member)
 {
     //This method is not thread safe.
     //TODO: Should it wake the island up?
     if (member.IsDynamic && member.simulationIsland == null)
     {
         member.simulationIsland = this;
         memberCount++;
         member.Activated += memberActivatedDelegate;
         member.BecameDeactivationCandidate    += becameDeactivationCandidateDelegate;
         member.BecameNonDeactivationCandidate += becameNonDeactivationCandidateDelegate;
         if (member.IsDeactivationCandidate)
         {
             deactivationCandidateCount++;
         }
     }
     else
     {
         throw new ArgumentException("Member either is not dynamic or already has a simulation island; cannot add.");
     }
 }
 ///<summary>
 /// Adds a simulation island member to the manager.
 ///</summary>
 ///<param name="simulationIslandMember">Member to add.</param>
 ///<exception cref="Exception">Thrown if the member already belongs to a manager.</exception>
 public void Add(SimulationIslandMember simulationIslandMember)
 {
     if (simulationIslandMember.DeactivationManager == null)
     {
         simulationIslandMember.Activate();
         simulationIslandMember.DeactivationManager = this;
         simulationIslandMembers.Add(simulationIslandMember);
         if (simulationIslandMember.IsDynamic)
         {
             AddSimulationIslandToMember(simulationIslandMember);
         }
         else
         {
             RemoveSimulationIslandFromMember(simulationIslandMember);
         }
     }
     else
     {
         throw new ArgumentException("Cannot add that member to this DeactivationManager; it already belongs to a manager.");
     }
 }
Exemplo n.º 7
0
 void BecameNonDeactivationCandidate(SimulationIslandMember member)
 {
     Interlocked.Decrement(ref deactivationCandidateCount);
 }
Exemplo n.º 8
0
 void BecameDeactivationCandidate(SimulationIslandMember member)
 {
     Interlocked.Increment(ref deactivationCandidateCount);
     //The reason why this does not deactivate when count == members.count is that deactivation candidate count will go up and down in parallel.
     //The actual deactivation process is not designed to be thread safe.  Perhaps doable, but perhaps not worth the effort.
 }
Exemplo n.º 9
0
 void MemberActivated(SimulationIslandMember member)
 {
     IsActive = true;
 }
        ///<summary>
        /// Adds a simulation island to a member.
        ///</summary>
        ///<param name="member">Member to gain a simulation island.</param>
        ///<exception cref="Exception">Thrown if the member already has a simulation island.</exception>
        public void AddSimulationIslandToMember(SimulationIslandMember member)
        {
            if (member.SimulationIsland != null)
            {
                throw new ArgumentException("Cannot initialize member's simulation island; it already has one.");
            }
            if (member.connections.Count > 0)
            {
                SimulationIsland island = null;
                //Find a simulation starting island to live in.
                for (int i = 0; i < member.connections.Count; i++)
                {
                    for (int j = 0; j < member.connections.Elements[i].entries.Count; j++)
                    {
                        island = member.connections.Elements[i].entries.Elements[j].Member.SimulationIsland;
                        if (island != null)
                        {
                            island.Add(member);
                            break;
                        }
                    }
                    if (island != null)
                    {
                        break;
                    }
                }
                if (member.SimulationIsland == null)
                {
                    //No non-null entries in any connections.  That's weird.
                    //Maybe it's connected to a bunch of kinematics, or maybe it's a vehicle-like situation
                    //where the body is associated with a 'vehicle' connection which sometimes contains only the body.

                    //No friends to merge with.
                    SimulationIsland newIsland = islandPool.Take();
                    simulationIslands.Add(newIsland);
                    newIsland.Add(member);
                    return;
                }


                //Becoming dynamic adds a new path.
                //Merges must be attempted between its connected members.
                for (int i = 0; i < member.connections.Count; i++)
                {
                    for (int j = 0; j < member.connections.Elements[i].entries.Count; j++)
                    {
                        if (member.connections.Elements[i].entries.Elements[j].Member == member)
                        {
                            continue; //Don't bother trying to compare against ourselves.  That would cause an erroneous early-out sometimes.
                        }
                        SimulationIsland opposingIsland = member.connections.Elements[i].entries.Elements[j].Member.SimulationIsland;
                        if (opposingIsland != null)
                        {
                            if (island != opposingIsland)
                            {
                                island = Merge(island, opposingIsland);
                            }
                            //All non-null simulation islands in a single connection are guaranteed to be the same island due to previous merges.
                            //Once we find one, we can stop.
                            break;
                        }
                    }
                }
            }
            else
            {
                //No friends to merge with.
                SimulationIsland newIsland = islandPool.Take();
                simulationIslands.Add(newIsland);
                newIsland.Add(member);
            }
        }
        ///<summary>
        /// Strips a member of its simulation island.
        ///</summary>
        ///<param name="member">Member to be stripped.</param>
        public void RemoveSimulationIslandFromMember(SimulationIslandMember member)
        {
            //Becoming kinematic eliminates the member as a possible path.
            //Splits must be attempted between its connected members.
            //Don't need to split same-connection members.  Splitting one non-null entry against a non null entry in each of the other connections will do the trick.
            if (member.simulationIsland != null)
            {
                //Note that this is using the most immediate simulation island.  This is because the immediate simulation island
                //is the one who 'owns' the member; not the root parent.  The root parent will own the member in the next frame
                //after the deactivation candidacy loop runs.
                SimulationIsland island = member.simulationIsland;
                island.Remove(member);
                if (island.memberCount == 0)
                {
                    //Even though we appear to have connections, the island was only me!
                    //We can stop now.
                    //Note that we do NOT remove the island from the simulation islands list here.
                    //That would take an O(n) search.  Instead, orphan it and let the TryToDeactivate loop find it.
                    return;
                }
            }
            if (member.connections.Count > 0)
            {
                for (int i = 0; i < member.connections.Count; i++)
                {
                    //Find a member with a non-null island to represent connection i.
                    SimulationIslandMember representativeA = null;
                    for (int j = 0; j < member.connections.Elements[i].entries.Count; j++)
                    {
                        if (member.connections.Elements[i].entries.Elements[j].Member.SimulationIsland != null)
                        {
                            representativeA = member.connections.Elements[i].entries.Elements[j].Member;
                            break;
                        }
                    }

                    if (representativeA == null)
                    {
                        //There was no representative!  That means it was a connection in which
                        //no member had a simulation island.  Consider removing a dynamic box from the space
                        //while it sits on a kinematic box.  Neither object has a simulation island.
                        //In this case, simply try the next connection.
                        continue;
                    }
                    //Activate the representative. This must be performed even if no split occurs; connected objects must be activated!
                    representativeA.Activate();

                    //Split the representative against representatives from other connections.
                    for (int j = i + 1; j < member.connections.Count; j++)
                    {
                        //Find a representative for another connection.
                        SimulationIslandMember representativeB = null;
                        for (int k = 0; k < member.connections.Elements[j].entries.Count; k++)
                        {
                            if (member.connections.Elements[j].entries.Elements[k].Member.SimulationIsland != null)
                            {
                                representativeB = member.connections.Elements[j].entries.Elements[k].Member;
                                break;
                            }
                        }

                        if (representativeB == null)
                        {
                            //There was no representative!  Same idea as above.
                            //Try the next connection.
                            continue;
                        }
                        //Activate the representative. This must be performed even if no split occurs; connected objects must be activated!
                        representativeB.Activate();

                        //Try to split the representatives.
                        //Don't bother doing any deferring; this is a rare activity
                        //and it's best just to do it up front.
                        TryToSplit(representativeA, representativeB);
                    }
                }
            }
        }
        /// <summary>
        /// Tries to split connections between the two island members.
        /// </summary>
        /// <param name="member1">First island member.</param>
        /// <param name="member2">Second island member.</param>
        /// <returns>Whether a split operation was run.  This does not mean a split was
        /// successful, just that the expensive test was performed.</returns>
        private bool TryToSplit(SimulationIslandMember member1, SimulationIslandMember member2)
        {
            //Can't split if they aren't even in the same island.
            //This also covers the case where the connection involves a kinematic entity that has no
            //simulation island at all.
            if (member1.SimulationIsland != member2.SimulationIsland ||
                member1.SimulationIsland == null ||
                member2.SimulationIsland == null)
            {
                return(false);
            }


            //By now, we know the members belong to the same island and are not null.
            //Start a BFS starting from each member.
            //Two-way can complete the search quicker.

            member1Friends.Enqueue(member1);
            member2Friends.Enqueue(member2);
            searchedMembers1.Add(member1);
            searchedMembers2.Add(member2);
            member1.searchState = SimulationIslandSearchState.OwnedByFirst;
            member2.searchState = SimulationIslandSearchState.OwnedBySecond;

            while (member1Friends.Count > 0 && member2Friends.Count > 0)
            {
                SimulationIslandMember currentNode = member1Friends.Dequeue();
                for (int i = 0; i < currentNode.connections.Count; i++)
                {
                    for (int j = 0; j < currentNode.connections.Elements[i].entries.Count; j++)
                    {
                        SimulationIslandMember connectedNode;
                        if ((connectedNode = currentNode.connections.Elements[i].entries.Elements[j].Member) != currentNode &&
                            connectedNode.SimulationIsland != null) //The connection could be connected to something that isn't in the Space and has no island, or it's not dynamic.
                        {
                            switch (connectedNode.searchState)
                            {
                            case SimulationIslandSearchState.Unclaimed:
                                //Found a new friend :)
                                member1Friends.Enqueue(connectedNode);
                                connectedNode.searchState = SimulationIslandSearchState.OwnedByFirst;
                                searchedMembers1.Add(connectedNode);
                                break;

                            case SimulationIslandSearchState.OwnedBySecond:
                                //Found our way to member2Friends set; cannot split!
                                member1Friends.Clear();
                                member2Friends.Clear();
                                goto ResetSearchStates;
                            }
                        }
                    }
                }

                currentNode = member2Friends.Dequeue();
                for (int i = 0; i < currentNode.connections.Count; i++)
                {
                    for (int j = 0; j < currentNode.connections.Elements[i].entries.Count; j++)
                    {
                        SimulationIslandMember connectedNode;
                        if ((connectedNode = currentNode.connections.Elements[i].entries.Elements[j].Member) != currentNode &&
                            connectedNode.SimulationIsland != null) //The connection could be connected to something that isn't in the Space and has no island, or it's not dynamic.
                        {
                            switch (connectedNode.searchState)
                            {
                            case SimulationIslandSearchState.Unclaimed:
                                //Found a new friend :)
                                member2Friends.Enqueue(connectedNode);
                                connectedNode.searchState = SimulationIslandSearchState.OwnedBySecond;
                                searchedMembers2.Add(connectedNode);
                                break;

                            case SimulationIslandSearchState.OwnedByFirst:
                                //Found our way to member1Friends set; cannot split!
                                member1Friends.Clear();
                                member2Friends.Clear();
                                goto ResetSearchStates;
                            }
                        }
                    }
                }
            }
            //If one of the queues empties out without finding anything, it means it's isolated.  The other one will never find it.
            //Now we can do a split.  Grab a new Island, fill it with the isolated search stuff.  Remove the isolated search stuff from the old Island.


            SimulationIsland newIsland = islandPool.Take();

            simulationIslands.Add(newIsland);
            if (member1Friends.Count == 0)
            {
                //Member 1 is isolated, give it its own simulation island!
                for (int i = 0; i < searchedMembers1.Count; i++)
                {
                    searchedMembers1[i].simulationIsland.Remove(searchedMembers1[i]);
                    newIsland.Add(searchedMembers1[i]);
                }
                member2Friends.Clear();
            }
            else if (member2Friends.Count == 0)
            {
                //Member 2 is isolated, give it its own simulation island!
                for (int i = 0; i < searchedMembers2.Count; i++)
                {
                    searchedMembers2[i].simulationIsland.Remove(searchedMembers2[i]);
                    newIsland.Add(searchedMembers2[i]);
                }
                member1Friends.Clear();
            }

            //Force the system awake.
            //Technically, the members should already be awake.
            //However, calling Activate on them resets the members'
            //deactivation candidacy timers.  This prevents the island
            //from instantly going back to sleep, which could leave
            //objects hanging in mid-air.
            member1.Activate();
            member2.Activate();


ResetSearchStates:
            for (int i = 0; i < searchedMembers1.Count; i++)
            {
                searchedMembers1[i].searchState = SimulationIslandSearchState.Unclaimed;
            }
            for (int i = 0; i < searchedMembers2.Count; i++)
            {
                searchedMembers2[i].searchState = SimulationIslandSearchState.Unclaimed;
            }
            searchedMembers1.Clear();
            searchedMembers2.Clear();
            return(true);
        }