/// <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> /// 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 Exception("Member does not belong to island; cannot remove."); } }
/// <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) { if (simulationIslandMember.IsDynamic) { simulationIslandMember.Activate(); } else { //If the object was NOT dynamic, then simply calling activate will be insufficient //because it does not share any simulation island with connected objects. //We need to notify its connections directly. foreach (var connection in simulationIslandMember.connections) { foreach (var entry in connection.entries) { if (entry.Member != simulationIslandMember) { entry.Member.Activate(); } } } } simulationIslandMember.DeactivationManager = null; simulationIslandMembers.Remove(simulationIslandMember); RemoveSimulationIslandFromMember(simulationIslandMember); } else { throw new Exception("Cannot remove that member from this DeactivationManager; it belongs to a different or no manager."); } }
/// <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> /// 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."); } }
///<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 Exception("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."); } }
/// <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."); }
///<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> /// 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."); }
///<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); } }
void BecameNonDeactivationCandidate(SimulationIslandMember member) { Interlocked.Decrement(ref deactivationCandidateCount); }
void MemberActivated(SimulationIslandMember member) { Activate(); }
/// <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); }
///<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."); }
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. }
protected Entity() { InitializeId(); BufferedStates = new EntityBufferedStates(this); material = new Material(); materialChangedDelegate = OnMaterialChanged; material.MaterialChanged += materialChangedDelegate; shapeChangedDelegate = OnShapeChanged; activityInformation = new SimulationIslandMember(this); }
/// <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; }
///<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 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); } }
void MemberActivated(SimulationIslandMember member) { IsActive = true; }
/// <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) { if (simulationIslandMember.IsDynamic) simulationIslandMember.Activate(); else { //If the object was NOT dynamic, then simply calling activate will be insufficient //because it does not share any simulation island with connected objects. //We need to notify its connections directly. foreach (var connection in simulationIslandMember.connections) { foreach (var entry in connection.entries) { if (entry.Member != simulationIslandMember) entry.Member.Activate(); } } } simulationIslandMember.DeactivationManager = null; simulationIslandMembers.Remove(simulationIslandMember); RemoveSimulationIslandFromMember(simulationIslandMember); } else throw new Exception("Cannot remove that member from this DeactivationManager; it belongs to a different or no manager."); }