/// <summary> /// Call this method to begin the threaded Octree operations. /// </summary> /// <param name="otp"<see cref="OctreeThreadParameters"/></param> public void ThreadOctreeInit(OctreeThreadParameters otp, bool restrictToMainThread) { if (restrictToMainThread) { TrackedObjectStates = evaluateOctree(otp); return; } ThreadRunning = true; IsDone = false; base.StartOctree(otp); }
protected virtual void ThreadOctree(OctreeThreadParameters otp) { }
public virtual void StartOctree(OctreeThreadParameters otp) { JobThread = new Thread(new ParameterizedThreadStart(runOctree)); JobThread.Start(otp); }
public void Start() { usingTriggers = TrackingMode == TrackingMode.UnityTriggers; agentMonitors = GameObject.FindObjectsOfType <WorldMonitors>(); /* * Start procedure is O(i*j*k) and a faster solution may exist */ for (int i = 0; i < agentMonitors.Length; i++) { for (int j = 0; j < agentMonitors[i].TrackedObjects.Count; j++) { for (int k = 0; k < agentMonitors[i].TrackedObjects[j].TrackedObjects.Count; k++) { // double threshold size if user wishes for point octree to mimic trigger interaction distances float threshold = (OctreeMimicTriggerInteraction && TrackingMode == TrackingMode.Octree) ? agentMonitors[i].ThresholdSet[j] * 3 / 2 : agentMonitors[i].ThresholdSet[j]; GameObject go = agentMonitors[i].TrackedObjects[j].TrackedObjects[k]; if (go) // allows user to leave empty gameobject slots in tracked object inspector { int id; if (GameObjectIDReference.TryGetValue(go, out id)) { TrackedObjectData TOData; TrackedObjectDataRef.TryGetValue(id, out TOData); TOData.ObjectOwners.Add(agentMonitors[i]); TrackedObjectDataRef[id] = TOData; if (usingTriggers) { // WorldMonitors will declare ownership in its Start() method if (!go.GetComponent <TrackedObjectTriggers>()) { go.AddComponent <TrackedObjectTriggers>(); } go.GetComponent <TrackedObjectTriggers>().Initialize(); } } else { GameObjectIDReference.Add(go, TotalTrackedObjects); //object ID = current number of tracked objects gameObjectReference.Add(TotalTrackedObjects, go); //using IDs necessary to run aux thread TrackedObjectData TOData = new TrackedObjectData { Object = go, Threshold = threshold, ObjectOwners = new List <WorldMonitors>() }; TOData.ObjectOwners.Add(agentMonitors[i]); TrackedObjectDataRef.Add(TotalTrackedObjects, TOData); TrackedObjectAffiliations.Add(TotalTrackedObjects, OTIEditorBase._AlphabetAssembler(j)); if (usingTriggers) { if (!go.GetComponent <TrackedObjectTriggers>()) { go.AddComponent <TrackedObjectTriggers>(); } go.GetComponent <TrackedObjectTriggers>().Initialize(); } TotalTrackedObjects++; } } } } } AllocationSpace = TotalTrackedObjects; FreeSpace = MaximumObjectsAllowed - AllocationSpace >= 0; if (usingTriggers || ExhaustiveMethod) { return; // no more set up required; switching between modes is not allowed at runtime. } Octree = new Octree(); //configure tracked object states at start for (int i = 0; i < TotalTrackedObjects; i++) { List <int> locals = new List <int>(); if (Octree.MasterList.ContainsKey(i)) { Octree.MasterList[i] = locals; } else { Octree.MasterList.Add(i, locals); } } OctreeThreadParameters otp = new OctreeThreadParameters { ObjectIDs = new List <int>(TrackedObjectDataRef.Keys), TotalTrackedObjects = TotalTrackedObjects, Coordinates = getUpdatedPositions(new List <int>(TrackedObjectDataRef.Keys)), DynamicObjects = TrackedObjectAffiliations }; //construct initial octree Octree.Initialize(InitialWorldSize, WorldOrigin, MinimumObjectSize); Octree.IsDone = true; //allows an initial pass into job start Octree.ThreadOctreeInit(otp, RestrictToMainThread); while (!Octree.UpdateOctree()) { } //wait until conflict states are established TrackedObjectStates = Octree.TrackedObjectStates; // wipe initial results so conflicts existing before start register for (int i = 0; i < Octree.MasterList.Count; i++) { Octree.MasterList[i] = new List <int>(); } }
void Update() { if (ExhaustiveMethod || usingTriggers) // if you are not using these features you can remove this if statement { if (ExhaustiveMethod) { exhaustiveCalculation(); } return; } if (Octree.UpdateOctree()) // job has concluded { passedFrames = 0; // in sync with update TrackedObjectStates = Octree.TrackedObjectStates; OctreeThreadParameters otp = refreshThreadingParameters(); int numStates = TrackedObjectStates.ParentIDEnterers.Length; for (int tos = 0; tos < numStates; tos++) { int parentID = TrackedObjectStates.ParentIDEnterers[tos]; TrackedObjectData TOData; if (TrackedObjectDataRef.TryGetValue(parentID, out TOData)) { int numConflicts = TrackedObjectStates.EnteringIDs[tos].Length; GameObject[] conflictors = new GameObject[numConflicts]; // allocate arrays for conflict data string[] conflictorAffiliations = new string[numConflicts]; // fill conflict data for (int i = 0; i < numConflicts; i++) { int m = TrackedObjectStates.EnteringIDs[tos][i]; conflictors[i] = gameObjectReference[m]; conflictorAffiliations[i] = TrackedObjectAffiliations[m]; } foreach (WorldMonitors wm in TOData.ObjectOwners) //inform the agents monitoring this object} { wm.RaiseConflictEnterers(TOData.Object, conflictors, conflictorAffiliations); } } } // if user wishes for no end conflict events to be raised, the update has concluded. if (ConflictEndMode == ConflictEndMode.NoConflictEndEvents) { Octree.ThreadOctreeInit(otp, RestrictToMainThread); return; } handleEndConflicts(ConflictEndMode, TrackedObjectStates); Octree.ThreadOctreeInit(otp, RestrictToMainThread); } else { passedFrames++; } }
/// <summary> /// Runs current tracked object data through Octree. /// </summary> private TrackedObjectStates evaluateOctree(OctreeThreadParameters otp) { int alloc = WorldMonitor.Instance.AllocationSpace; updatePositions = new Vector3[alloc]; // otp.ObjectIDs.Count float[] thresholds = new float[alloc]; // otp.ObjectIDs.Count //for (int i = 0; i < otp.ObjectIDs.Count; i++) foreach (int id in otp.ObjectIDs) { KeyValuePair <float, Vector3> pos; if (!otp.Coordinates.TryGetValue(id, out pos)) { Debug.LogError("unknown object position request in octree eval"); } thresholds[id] = pos.Key; updatePositions[id] = pos.Value; PointOctree.Remove(id); PointOctree.Add(id, pos.Value); } List <int[]> enteringIDs = new List <int[]>(); List <int[]> leavingIDs = new List <int[]>(); List <int> parentIDLeavers = new List <int>(); List <int> parentIDEnterers = new List <int>(); int ind = 0; foreach (int id in otp.ObjectIDs) { List <int> validConflicts = new List <int>(); List <int> stayers = new List <int>(); int[] areaObjects = PointOctree.GetNearby(updatePositions[id], thresholds[id]); for (int j = 0; j < areaObjects.Length; j++) { string affiliateObj = WorldMonitor.Instance.TrackedObjectAffiliations[id]; string affiliateCompare = WorldMonitor.Instance.TrackedObjectAffiliations[areaObjects[j]]; /* * run conflict validity checks: if not the same object && not an object of the same class type && is a new conflict */ if (areaObjects[j] != id && !MasterList[id].Contains(areaObjects[j]) && string.Compare(affiliateObj, affiliateCompare) != 0) { if (!parentIDEnterers.Contains(id)) { parentIDEnterers.Add(id); } MasterList[id].Add(areaObjects[j]); // add conflicting object to master list of current conflicts validConflicts.Add(areaObjects[j]); // *new* conflicts stayers.Add(areaObjects[j]); // use to look for conflicts that have ended } else if (MasterList[id].Contains(areaObjects[j])) { stayers.Add(areaObjects[j]); // this is an object staying in conflict } } bool leaverDetected = false; List <int> leavers = new List <int>(); foreach (int _id in MasterList[id]) // look at master list's record of conflicts for this parent ID - if it isn't in stayers, it has left the conflict area or been destroyed { if (!stayers.Contains(_id)) { leaverDetected = true; leavers.Add(_id); } switch (WorldMonitor.Instance.ConflictEndMode) { case ConflictEndMode.OnAllConflictsEnded: if (leavers.Count == MasterList[id].Count) { parentIDLeavers.Add(id); } break; case ConflictEndMode.OnIndividualConflictEnded: if (leaverDetected && !parentIDEnterers.Contains(id) && !parentIDLeavers.Contains(id)) { parentIDLeavers.Add(id); } break; } } foreach (int leaver in leavers) { MasterList[id].Remove(leaver); } int numValid = leavers.ToArray().Length; if (numValid > 0) { leavingIDs.Add(leavers.ToArray()); } numValid = validConflicts.ToArray().Length; if (numValid > 0) { enteringIDs.Add(validConflicts.ToArray()); } ind++; } return(new TrackedObjectStates { ParentIDEnterers = parentIDEnterers.ToArray(), // parent IDs of new or increased conflict states ParentIDLeavers = parentIDLeavers.ToArray(), // parent IDs of expired or reduced conflict states LeavingIDs = leavingIDs, // child IDs - ended conflict(s) EnteringIDs = enteringIDs, // child IDs - the conflict(s) PriorConflictingIDs = parentIDLeavers // parent IDs that need informed all conflicts have ceased }); }
protected override void ThreadOctree(OctreeThreadParameters otp) { TrackedObjectStates = evaluateOctree(otp); IsDone = true; // TrackedObjectStates are defined - - eliminating race conditions }