新增SensorToolkit
This commit is contained in:
126
Assets/SensorToolkit/Sensors/src/Accumulator.cs
Normal file
126
Assets/SensorToolkit/Sensors/src/Accumulator.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Micosmo.SensorToolkit {
|
||||
|
||||
[Serializable]
|
||||
public struct Accumulator<REF, T> : IEquatable<Accumulator<REF, T>>
|
||||
where REF : UnityEngine.Object
|
||||
where T : IAccumulated<REF, T> {
|
||||
[SerializeField] REF outputTarget;
|
||||
public REF OutputTarget => outputTarget;
|
||||
[SerializeField] List<REF> inputTargets;
|
||||
public List<REF> InputTargets => inputTargets;
|
||||
[SerializeField] List<T> inputs;
|
||||
public List<T> Inputs => inputs;
|
||||
[SerializeField] T output;
|
||||
public T RawOutput => output;
|
||||
[SerializeField] bool isDirty;
|
||||
public T PreviousOutput { get; private set; }
|
||||
|
||||
int Timestamp;
|
||||
|
||||
public void Initialize() {
|
||||
Timestamp = -1;
|
||||
inputTargets = new List<REF>();
|
||||
inputs = new List<T>();
|
||||
}
|
||||
|
||||
public T Output {
|
||||
get {
|
||||
if (isDirty) {
|
||||
Combine();
|
||||
isDirty = false;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
public void Spawn(REF target, int timestamp) {
|
||||
Timestamp = timestamp;
|
||||
outputTarget = target;
|
||||
isDirty = true;
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
inputs.Clear();
|
||||
outputTarget = null;
|
||||
PreviousOutput = default;
|
||||
output = default(T);
|
||||
isDirty = false;
|
||||
}
|
||||
|
||||
public bool UpdateInput(REF target, T input, int timestamp) {
|
||||
if (TryGetInput(target, out var found, out var index)) {
|
||||
if (found.Equals(input)) {
|
||||
return false;
|
||||
}
|
||||
SetTimestamp(timestamp);
|
||||
inputs[index] = input;
|
||||
isDirty = true;
|
||||
return true;
|
||||
} else {
|
||||
SetTimestamp(timestamp);
|
||||
inputs.Add(input);
|
||||
inputTargets.Add(target);
|
||||
isDirty = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool RemoveInput(REF target, int timestamp) {
|
||||
if (TryGetInput(target, out var found, out var index)) {
|
||||
SetTimestamp(timestamp);
|
||||
inputs.RemoveAt(index);
|
||||
inputTargets.RemoveAt(index);
|
||||
isDirty = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Equals(Accumulator<REF, T> other) {
|
||||
return outputTarget == other.outputTarget;
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
return outputTarget.GetHashCode();
|
||||
}
|
||||
|
||||
void SetTimestamp(int timestamp) {
|
||||
if (Timestamp != timestamp) {
|
||||
Timestamp = timestamp;
|
||||
PreviousOutput = Output;
|
||||
}
|
||||
}
|
||||
|
||||
bool TryGetInput(REF target, out T input, out int index) {
|
||||
for (int i = 0; i < inputTargets.Count; i++) {
|
||||
var t = inputTargets[i];
|
||||
if (ReferenceEquals(t, target)) {
|
||||
input = inputs[i];
|
||||
index = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
input = default(T);
|
||||
index = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
void Combine() {
|
||||
bool isFirst = true;
|
||||
foreach (var input in inputs) {
|
||||
if (isFirst) {
|
||||
output = input;
|
||||
isFirst = false;
|
||||
} else {
|
||||
output = output.Combine(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
11
Assets/SensorToolkit/Sensors/src/Accumulator.cs.meta
Normal file
11
Assets/SensorToolkit/Sensors/src/Accumulator.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fe0121fbe93c42545969b8c96860bb57
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
415
Assets/SensorToolkit/Sensors/src/AccumulatorPipeline.cs
Normal file
415
Assets/SensorToolkit/Sensors/src/AccumulatorPipeline.cs
Normal file
@@ -0,0 +1,415 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Micosmo.SensorToolkit {
|
||||
|
||||
[Serializable]
|
||||
public abstract class AccumulatorPipeline<REF, T> : ISerializationCallbackReceiver
|
||||
where REF : UnityEngine.Object
|
||||
where T : IAccumulated<REF, T> {
|
||||
|
||||
TargetsEnumerable targetsEnumerable;
|
||||
public TargetsEnumerable OutputTargets {
|
||||
get {
|
||||
if (targetsEnumerable == null) {
|
||||
targetsEnumerable = new TargetsEnumerable(this);
|
||||
}
|
||||
return targetsEnumerable;
|
||||
}
|
||||
}
|
||||
|
||||
OutputsEnumerable outputsEnumerable;
|
||||
public OutputsEnumerable Outputs {
|
||||
get {
|
||||
if (outputsEnumerable == null) {
|
||||
outputsEnumerable = new OutputsEnumerable(this);
|
||||
}
|
||||
return outputsEnumerable;
|
||||
}
|
||||
}
|
||||
|
||||
public delegate bool SignalProcessor(in T input, out T processed);
|
||||
|
||||
public SignalProcessor SignalProcessorCallback;
|
||||
|
||||
public event Action<T> OnAdd;
|
||||
public event Action<T, T> OnChange;
|
||||
public event Action<T> OnRemove;
|
||||
public event Action OnSome;
|
||||
public event Action OnNone;
|
||||
|
||||
Dictionary<REF, int> inputToMap = new Dictionary<REF, int>();
|
||||
Dictionary<REF, int> outputToMap = new Dictionary<REF, int>();
|
||||
[NonSerialized] Accumulator<REF, T>[] accumulators = new Accumulator<REF, T>[32];
|
||||
[NonSerialized] int accumulatorCount = 0;
|
||||
|
||||
[NonSerialized] HashSet<REF> toRemove = new HashSet<REF>();
|
||||
[NonSerialized] HashSet<Accumulator<REF, T>> changed = new HashSet<Accumulator<REF, T>>();
|
||||
[NonSerialized] List<T> removed = new List<T>();
|
||||
|
||||
struct Event {
|
||||
public enum Type { Add, Change, Remove }
|
||||
public Type type;
|
||||
public T target;
|
||||
}
|
||||
[NonSerialized] List<Event> eventQueue = new List<Event>();
|
||||
|
||||
int prevSignalCount = -1;
|
||||
int timestamp = 0;
|
||||
bool isPlayingEvents = false;
|
||||
|
||||
public T GetOutput(REF go) {
|
||||
return accumulators[outputToMap[go]].Output;
|
||||
}
|
||||
|
||||
public bool TryGetOutput(REF go, out T output) {
|
||||
if (outputToMap.TryGetValue(go, out var i)) {
|
||||
output = accumulators[i].Output;
|
||||
return true;
|
||||
}
|
||||
output = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<REF> GetInputObjects(REF go, List<REF> storeIn) {
|
||||
if (outputToMap.TryGetValue(go, out var i)) {
|
||||
foreach (var input in accumulators[i].InputTargets) {
|
||||
storeIn.Add(input);
|
||||
}
|
||||
}
|
||||
return storeIn;
|
||||
}
|
||||
|
||||
public bool ContainsOutput(REF go) {
|
||||
return outputToMap.ContainsKey(go);
|
||||
}
|
||||
|
||||
public void UpdateAllInputs(List<T> nextInputs) {
|
||||
if (!AssertRecursionLimitNotReached()) {
|
||||
return;
|
||||
}
|
||||
|
||||
timestamp += 1;
|
||||
|
||||
toRemove.Clear();
|
||||
foreach (var input in inputToMap) {
|
||||
toRemove.Add(input.Key);
|
||||
}
|
||||
|
||||
foreach (var signal in nextInputs) {
|
||||
toRemove.Remove(signal.Object);
|
||||
UpdateInputInternal(signal);
|
||||
}
|
||||
|
||||
foreach (var remaining in toRemove) {
|
||||
RemoveInputInternal(remaining);
|
||||
}
|
||||
|
||||
SerializeEvents();
|
||||
if (!isPlayingEvents) {
|
||||
PlayEvents();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateInput(T signal) {
|
||||
if (!AssertRecursionLimitNotReached()) {
|
||||
return;
|
||||
}
|
||||
|
||||
timestamp += 1;
|
||||
|
||||
UpdateInputInternal(signal);
|
||||
SerializeEvents();
|
||||
if (!isPlayingEvents) {
|
||||
PlayEvents();
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveInput(REF forObject) {
|
||||
if (!AssertRecursionLimitNotReached()) {
|
||||
return;
|
||||
}
|
||||
|
||||
timestamp += 1;
|
||||
|
||||
RemoveInputInternal(forObject);
|
||||
SerializeEvents();
|
||||
if (!isPlayingEvents) {
|
||||
PlayEvents();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnBeforeSerialize() { }
|
||||
|
||||
public void OnAfterDeserialize() {
|
||||
/*inputToMap.Clear();
|
||||
outputToMap.Clear();
|
||||
for (int i = 0; i < accumulatorCount; i++) {
|
||||
var acc = accumulators[i];
|
||||
outputToMap.Add(acc.OutputTarget, i);
|
||||
foreach (var input in acc.InputTargets) {
|
||||
inputToMap.Add(input, i);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
int recursionCount = 0;
|
||||
bool AssertRecursionLimitNotReached() {
|
||||
if (!isPlayingEvents) {
|
||||
recursionCount = 0;
|
||||
return true;
|
||||
}
|
||||
recursionCount += 1;
|
||||
if (recursionCount > 10) {
|
||||
Debug.LogError("It appears that a sensor is being pulsed recursively from a detection event handler. This will be ignored to avoid a stack overflow. To avoid this error make sure that any detection event handlers are not causing a sensor to change its list of detections, and therefore firing a new sequence of events.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void UpdateInputInternal(T signal) {
|
||||
if (ReferenceEquals(signal.Object, null)) {
|
||||
return;
|
||||
}
|
||||
var processed = signal;
|
||||
if (SignalProcessorCallback?.Invoke(in signal, out processed) ?? true) {
|
||||
UpdateProcessedInput(signal.Object, processed);
|
||||
} else {
|
||||
RemoveInputInternal(signal.Object);
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveInputInternal(REF forObject) {
|
||||
if (inputToMap.TryGetValue(forObject, out var accIndex)) {
|
||||
RemoveInputFromMap(forObject, accIndex);
|
||||
inputToMap.Remove(forObject);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateProcessedInput(REF inputTarget, T processed) {
|
||||
if (inputToMap.TryGetValue(inputTarget, out var accIndex)) {
|
||||
if (ReferenceEquals(accumulators[accIndex].OutputTarget, processed.Object)) {
|
||||
if (accumulators[accIndex].UpdateInput(inputTarget, processed, timestamp)) {
|
||||
OnChangedEvent(accumulators[accIndex]);
|
||||
}
|
||||
} else {
|
||||
RemoveInputFromMap(inputTarget, accIndex);
|
||||
NewProcessedInput(inputTarget, processed);
|
||||
}
|
||||
} else {
|
||||
NewProcessedInput(inputTarget, processed);
|
||||
}
|
||||
}
|
||||
|
||||
void NewProcessedInput(REF inputTarget, T processed) {
|
||||
if (!outputToMap.TryGetValue(processed.Object, out var accIndex)) {
|
||||
var acc = accumulatorCache.Get();
|
||||
acc.Spawn(processed.Object, timestamp);
|
||||
|
||||
// Add the accumulator to the array, resize if needed
|
||||
accIndex = accumulatorCount;
|
||||
if (accIndex == accumulators.Length) {
|
||||
Array.Resize(ref accumulators, accumulators.Length * 2);
|
||||
}
|
||||
accumulators[accIndex] = acc;
|
||||
accumulatorCount += 1;
|
||||
|
||||
outputToMap[acc.OutputTarget] = accIndex;
|
||||
}
|
||||
inputToMap[inputTarget] = accIndex;
|
||||
if (accumulators[accIndex].UpdateInput(inputTarget, processed, timestamp)) {
|
||||
OnChangedEvent(accumulators[accIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveInputFromMap(REF inputObject, int accIndex) {
|
||||
if (accumulators[accIndex].RemoveInput(inputObject, timestamp)) {
|
||||
if (accumulators[accIndex].Inputs.Count > 0) {
|
||||
OnChangedEvent(accumulators[accIndex]);
|
||||
} else {
|
||||
OnRemovedEvent(accumulators[accIndex]);
|
||||
outputToMap.Remove(accumulators[accIndex].OutputTarget);
|
||||
accumulatorCache.Dispose(accumulators[accIndex]);
|
||||
|
||||
// Swap with last accumulator and remove
|
||||
var lastIndex = accumulatorCount - 1;
|
||||
if (accIndex != lastIndex) {
|
||||
var lastAcc = accumulators[lastIndex];
|
||||
accumulators[accIndex] = lastAcc;
|
||||
outputToMap[lastAcc.OutputTarget] = accIndex;
|
||||
foreach (var input in lastAcc.InputTargets) {
|
||||
inputToMap[input] = accIndex;
|
||||
}
|
||||
}
|
||||
accumulatorCount -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnChangedEvent(Accumulator<REF, T> signal) {
|
||||
changed.Add(signal);
|
||||
}
|
||||
|
||||
void OnRemovedEvent(Accumulator<REF, T> signal) {
|
||||
changed.Remove(signal);
|
||||
removed.Add(signal.PreviousOutput);
|
||||
}
|
||||
|
||||
void SerializeEvents() {
|
||||
foreach (var change in changed) {
|
||||
var previousOutput = change.PreviousOutput;
|
||||
if (ReferenceEquals(previousOutput.Object, null)) {
|
||||
eventQueue.Add(new Event { type = Event.Type.Add, target = change.Output });
|
||||
} else {
|
||||
eventQueue.Add(new Event { type = Event.Type.Change, target = previousOutput });
|
||||
eventQueue.Add(new Event { type = Event.Type.Change, target = change.Output });
|
||||
}
|
||||
}
|
||||
foreach (var remove in removed) {
|
||||
eventQueue.Add(new Event { type = Event.Type.Remove, target = remove });
|
||||
}
|
||||
changed.Clear();
|
||||
removed.Clear();
|
||||
}
|
||||
|
||||
void PlayEvents() {
|
||||
isPlayingEvents = true;
|
||||
try {
|
||||
var eventIndex = 0;
|
||||
while (eventIndex < eventQueue.Count) {
|
||||
var evt = eventQueue[eventIndex];
|
||||
switch (evt.type) {
|
||||
case Event.Type.Add:
|
||||
OnAdd?.Invoke(evt.target);
|
||||
break;
|
||||
case Event.Type.Change:
|
||||
// We store the prevValue as current index. The next value in the next index.
|
||||
eventIndex++;
|
||||
var changeTo = eventQueue[eventIndex];
|
||||
OnChange?.Invoke(evt.target, changeTo.target);
|
||||
break;
|
||||
case Event.Type.Remove:
|
||||
OnRemove?.Invoke(evt.target);
|
||||
break;
|
||||
}
|
||||
eventIndex += 1;
|
||||
}
|
||||
eventQueue.Clear();
|
||||
|
||||
var signalCount = Outputs.Count;
|
||||
// prevSignalCount initialized to -1, so event always triggers on first update
|
||||
if (prevSignalCount <= 0 && signalCount > 0) {
|
||||
OnSome?.Invoke();
|
||||
} else if (prevSignalCount != 0 && signalCount == 0) {
|
||||
OnNone?.Invoke();
|
||||
}
|
||||
prevSignalCount = signalCount;
|
||||
} finally {
|
||||
isPlayingEvents = false;
|
||||
}
|
||||
}
|
||||
|
||||
AccumulatorCache accumulatorCache = new AccumulatorCache();
|
||||
|
||||
class AccumulatorCache : ObjectCache<Accumulator<REF, T>> {
|
||||
public override void Dispose(Accumulator<REF, T> obj) {
|
||||
obj.Dispose();
|
||||
base.Dispose(obj);
|
||||
}
|
||||
protected override Accumulator<REF, T> create() {
|
||||
var inst = base.create();
|
||||
inst.Initialize();
|
||||
return inst;
|
||||
}
|
||||
}
|
||||
|
||||
public class OutputsEnumerable : IEnumerable<T>, IEnumerable {
|
||||
AccumulatorPipeline<REF, T> source;
|
||||
public OutputsEnumerable(AccumulatorPipeline<REF, T> source) { this.source = source; }
|
||||
public Enumerator GetEnumerator() { return new Enumerator(source); }
|
||||
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
|
||||
IEnumerator<T> IEnumerable<T>.GetEnumerator() { return GetEnumerator(); }
|
||||
|
||||
public int Count {
|
||||
get {
|
||||
int n = 0;
|
||||
for (int i = 0; i < source.accumulatorCount; i++) {
|
||||
if (source.accumulators[i].OutputTarget != null) {
|
||||
n += 1;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
public struct Enumerator : IEnumerator<T>, IEnumerator {
|
||||
AccumulatorPipeline<REF, T> source;
|
||||
int index;
|
||||
public Enumerator(AccumulatorPipeline<REF, T> source) {
|
||||
this.source = source;
|
||||
index = -1;
|
||||
}
|
||||
public T Current { get { return source.accumulators[index].Output; } }
|
||||
object IEnumerator.Current => throw new NotImplementedException();
|
||||
public void Dispose() { }
|
||||
public bool MoveNext() {
|
||||
index += 1;
|
||||
if (index >= source.accumulatorCount) {
|
||||
return false;
|
||||
}
|
||||
if (Current.Object == null) {
|
||||
return MoveNext();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public void Reset() { index = -1; }
|
||||
}
|
||||
}
|
||||
|
||||
public class TargetsEnumerable : IEnumerable<REF>, IEnumerable {
|
||||
AccumulatorPipeline<REF, T> source;
|
||||
public TargetsEnumerable(AccumulatorPipeline<REF, T> source) { this.source = source; }
|
||||
public Enumerator GetEnumerator() { return new Enumerator(source); }
|
||||
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
|
||||
IEnumerator<REF> IEnumerable<REF>.GetEnumerator() { return GetEnumerator(); }
|
||||
|
||||
public int Count {
|
||||
get {
|
||||
int n = 0;
|
||||
for (int i = 0; i < source.accumulatorCount; i++) {
|
||||
if (source.accumulators[i].OutputTarget != null) {
|
||||
n += 1;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
public struct Enumerator : IEnumerator<REF>, IEnumerator {
|
||||
AccumulatorPipeline<REF, T> source;
|
||||
int index;
|
||||
public Enumerator(AccumulatorPipeline<REF, T> source) {
|
||||
this.source = source;
|
||||
index = -1;
|
||||
}
|
||||
public REF Current { get { return source.accumulators[index].OutputTarget; } }
|
||||
object IEnumerator.Current => throw new NotImplementedException();
|
||||
public void Dispose() { }
|
||||
public bool MoveNext() {
|
||||
index += 1;
|
||||
if (index >= source.accumulatorCount) {
|
||||
return false;
|
||||
}
|
||||
if (Current == null) {
|
||||
return MoveNext();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public void Reset() { index = -1; }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
11
Assets/SensorToolkit/Sensors/src/AccumulatorPipeline.cs.meta
Normal file
11
Assets/SensorToolkit/Sensors/src/AccumulatorPipeline.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 372a7af7ced857b46af861b11ad5b2a2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
97
Assets/SensorToolkit/Sensors/src/AngleEnumerator.cs
Normal file
97
Assets/SensorToolkit/Sensors/src/AngleEnumerator.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Micosmo.SensorToolkit {
|
||||
|
||||
public struct AngleEnumerator {
|
||||
public enum AngleMethodType { Center, Origin, BoundingBox }
|
||||
public enum SortByType { HorizontalAngle, CentralAngle }
|
||||
|
||||
public AngleMethodType AngleMethod;
|
||||
public SortByType SortBy;
|
||||
|
||||
public List<AngleResult> results { get; private set; }
|
||||
|
||||
public static AngleEnumerator Create() => new AngleEnumerator {
|
||||
results = new List<AngleResult>()
|
||||
};
|
||||
|
||||
public void Clear() {
|
||||
results.Clear();
|
||||
}
|
||||
|
||||
public void Calculate(ReferenceFrame frame, FOVRange fov, List<Signal> signals) {
|
||||
Clear();
|
||||
fov.Distance *= fov.Distance;
|
||||
foreach (var signal in signals) {
|
||||
var angles =
|
||||
AngleMethod == AngleMethodType.Origin ? frame.AngleTo(signal.Object.transform.position) :
|
||||
AngleMethod == AngleMethodType.Center ? frame.AngleTo(signal.Bounds.center) :
|
||||
AngleMethod == AngleMethodType.BoundingBox ? frame.AngleTo(signal.Bounds) :
|
||||
default;
|
||||
var quadrance = signal.Bounds.SqrDistance(frame.Position);
|
||||
if (!fov.Contains(angles, quadrance)) {
|
||||
continue;
|
||||
}
|
||||
var result = new AngleResult {
|
||||
Object = signal.Object,
|
||||
Angles = angles,
|
||||
CentralAngle = angles.GetCentralAngle(),
|
||||
Distance = quadrance
|
||||
};
|
||||
results.Add(result);
|
||||
}
|
||||
if (SortBy == SortByType.HorizontalAngle) {
|
||||
results.Sort(AngleResult.CompareHorizAngle);
|
||||
} else if (SortBy == SortByType.CentralAngle) {
|
||||
results.Sort(AngleResult.CompareCentralAngle);
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawGizmos() {
|
||||
SensorGizmos.PushColor(Color.black);
|
||||
int i = 0;
|
||||
foreach (var result in results) {
|
||||
SensorGizmos.Label(result.Object.transform.position, $"Index: {i}\n({result.Angles.HorizAngle.ToString("N1")},{result.Angles.VertAngle.ToString("N1")})");
|
||||
i++;
|
||||
}
|
||||
SensorGizmos.PopColor();
|
||||
}
|
||||
|
||||
public struct AngleResult {
|
||||
public GameObject Object;
|
||||
public ViewAngles Angles;
|
||||
public float CentralAngle;
|
||||
public float Distance;
|
||||
public static int CompareCentralAngle(AngleResult r1, AngleResult r2) {
|
||||
var angleDiff = r1.CentralAngle - r2.CentralAngle;
|
||||
if (angleDiff != 0f) {
|
||||
return angleDiff > 0f ? 1 : -1;
|
||||
}
|
||||
var distanceDiff = r1.Distance - r2.Distance;
|
||||
if (distanceDiff != 0f) {
|
||||
return distanceDiff > 0f ? 1 : -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
public static int CompareHorizAngle(AngleResult r1, AngleResult r2) {
|
||||
//var a1 = r1.Coords.HorizAngle >= 0 ? r1.Coords.HorizAngle : 360f + r1.Coords.HorizAngle;
|
||||
//var a2 = r2.Coords.HorizAngle >= 0 ? r2.Coords.HorizAngle : 360f + r2.Coords.HorizAngle;
|
||||
var a1 = r1.Angles.HorizAngle;
|
||||
var a2 = r2.Angles.HorizAngle;
|
||||
var angleDiff = a1 - a2;
|
||||
if (angleDiff != 0f) {
|
||||
return angleDiff > 0f ? 1 : -1;
|
||||
}
|
||||
var distanceDiff = r1.Distance - r2.Distance;
|
||||
if (distanceDiff != 0f) {
|
||||
return distanceDiff > 0f ? 1 : -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
11
Assets/SensorToolkit/Sensors/src/AngleEnumerator.cs.meta
Normal file
11
Assets/SensorToolkit/Sensors/src/AngleEnumerator.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e77fc9afd958c154c9e4cfe6dc9067ed
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
202
Assets/SensorToolkit/Sensors/src/BaseAreaSensor.cs
Normal file
202
Assets/SensorToolkit/Sensors/src/BaseAreaSensor.cs
Normal file
@@ -0,0 +1,202 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Micosmo.SensorToolkit
|
||||
{
|
||||
/*
|
||||
* Common functionality for 2D sensors that detect colliders within an area such as the Range2D and Trigger2D Sensors.
|
||||
*/
|
||||
public abstract class BaseAreaSensor : Sensor {
|
||||
|
||||
#region Configurations
|
||||
[SerializeField]
|
||||
SignalFilter signalFilter = new SignalFilter();
|
||||
|
||||
[Tooltip("In Collider mode the sensor detects GameObjects attached to colliders. In RigidBody mode it detects the RigidBody GameObject attached to colliders.")]
|
||||
public DetectionModes DetectionMode;
|
||||
#endregion
|
||||
|
||||
#region Public
|
||||
// Edit the IgnoreList at runtime. Anything in the list will not be detected
|
||||
public List<GameObject> IgnoreList => signalFilter.IgnoreList;
|
||||
|
||||
// Enable/Disable the tag filtering at runtime
|
||||
public bool EnableTagFilter {
|
||||
get => signalFilter.EnableTagFilter;
|
||||
set => signalFilter.EnableTagFilter = value;
|
||||
}
|
||||
|
||||
// Change the allowed tags at runtime
|
||||
public string[] AllowedTags {
|
||||
get => signalFilter.AllowedTags;
|
||||
set => signalFilter.AllowedTags = value;
|
||||
}
|
||||
|
||||
public override void Clear() {
|
||||
base.Clear();
|
||||
ClearColliders();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Protected
|
||||
protected override List<Collider2D> GetInputColliders(GameObject inputObject, List<Collider2D> storeIn) {
|
||||
List<Collider2D> clist;
|
||||
if (gameObjectColliders.TryGetValue(inputObject, out clist)) {
|
||||
foreach (var c in clist) {
|
||||
storeIn.Add(c);
|
||||
}
|
||||
}
|
||||
return storeIn;
|
||||
}
|
||||
|
||||
protected override void InitialiseSignalProcessors() {
|
||||
base.InitialiseSignalProcessors();
|
||||
MapToSignalProxy.Configure(true);
|
||||
SignalFilter = signalFilter;
|
||||
}
|
||||
|
||||
protected void UpdateAllSignals() {
|
||||
workList.Clear();
|
||||
|
||||
foreach (var cols in gameObjectColliders) {
|
||||
Signal signal;
|
||||
if (CalculateSignal(cols.Value, out signal)) {
|
||||
workList.Add(signal);
|
||||
}
|
||||
}
|
||||
MapToRigidBody.Configure(DetectionMode, true);
|
||||
UpdateAllSignals(workList);
|
||||
}
|
||||
|
||||
protected void AddCollider(Collider2D c, bool updateSignal) {
|
||||
var cols = AddColliderToMap(c, c.gameObject, gameObjectColliders);
|
||||
|
||||
if (!updateSignal) {
|
||||
return;
|
||||
}
|
||||
MapToRigidBody.Configure(DetectionMode, true);
|
||||
Signal signal;
|
||||
if (CalculateSignal(cols, out signal)) {
|
||||
UpdateSignalImmediate(signal);
|
||||
}
|
||||
}
|
||||
|
||||
protected void RemoveCollider(Collider2D c, bool updateSignal) {
|
||||
if (c == null) {
|
||||
ClearDestroyedGameObjects();
|
||||
return;
|
||||
}
|
||||
|
||||
var cols = RemoveColliderFromMap(c, c.gameObject, gameObjectColliders);
|
||||
|
||||
if (!updateSignal) {
|
||||
return;
|
||||
}
|
||||
MapToRigidBody.Configure(DetectionMode, true);
|
||||
if (cols == null) {
|
||||
LostSignalImmediate(c.gameObject);
|
||||
} else {
|
||||
Signal signal;
|
||||
if (CalculateSignal(cols, out signal)) {
|
||||
UpdateSignalImmediate(signal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void ClearColliders() {
|
||||
foreach (var set in gameObjectColliders) {
|
||||
colliderListCache.Dispose(set.Value);
|
||||
}
|
||||
gameObjectColliders.Clear();
|
||||
}
|
||||
|
||||
List<Collider2D> colliderList = new List<Collider2D>();
|
||||
protected List<Collider2D> GetColliders() {
|
||||
colliderList.Clear();
|
||||
foreach (var set in gameObjectColliders) {
|
||||
foreach (var collider in set.Value) {
|
||||
colliderList.Add(collider);
|
||||
}
|
||||
}
|
||||
return colliderList;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Internals
|
||||
// Maps a GameObject to a list of it's colliders that have been detected.
|
||||
Dictionary<GameObject, List<Collider2D>> gameObjectColliders = new Dictionary<GameObject, List<Collider2D>>();
|
||||
|
||||
// List of temporary values for modifying collections
|
||||
List<GameObject> gameObjectList = new List<GameObject>();
|
||||
List<Signal> workList = new List<Signal>();
|
||||
|
||||
static ListCache<Collider2D> colliderListCache = new ListCache<Collider2D>();
|
||||
|
||||
void ClearDestroyedGameObjects() {
|
||||
gameObjectList.Clear();
|
||||
foreach (var set in gameObjectColliders) {
|
||||
if (set.Key == null) {
|
||||
gameObjectList.Add(set.Key);
|
||||
}
|
||||
}
|
||||
foreach (var go in gameObjectList) {
|
||||
colliderListCache.Dispose(gameObjectColliders[go]);
|
||||
gameObjectColliders.Remove(go);
|
||||
}
|
||||
}
|
||||
|
||||
List<Collider2D> AddColliderToMap(Collider2D c, GameObject go, Dictionary<GameObject, List<Collider2D>> dict) {
|
||||
List<Collider2D> colliderList;
|
||||
if (!dict.TryGetValue(go, out colliderList)) {
|
||||
colliderList = colliderListCache.Get();
|
||||
dict[go] = colliderList;
|
||||
}
|
||||
if (!colliderList.Contains(c)) {
|
||||
colliderList.Add(c);
|
||||
}
|
||||
return colliderList;
|
||||
}
|
||||
|
||||
List<Collider2D> RemoveColliderFromMap(Collider2D c, GameObject go, Dictionary<GameObject, List<Collider2D>> dict) {
|
||||
List<Collider2D> colliderList = null;
|
||||
if (dict.TryGetValue(go, out colliderList)) {
|
||||
colliderList.Remove(c);
|
||||
if (colliderList.Count == 0) {
|
||||
dict.Remove(go);
|
||||
colliderListCache.Dispose(colliderList);
|
||||
colliderList = null;
|
||||
}
|
||||
}
|
||||
return colliderList;
|
||||
}
|
||||
|
||||
bool CalculateSignal(List<Collider2D> colliders, out Signal signal) {
|
||||
signal = default;
|
||||
|
||||
Bounds bounds = new Bounds();
|
||||
bool anyFound = false;
|
||||
foreach (var c in colliders) {
|
||||
if (!signalFilter.TestCollider(c)) {
|
||||
continue;
|
||||
}
|
||||
if (!anyFound) {
|
||||
bounds = c.bounds;
|
||||
anyFound = true;
|
||||
} else {
|
||||
bounds.Encapsulate(c.bounds);
|
||||
}
|
||||
}
|
||||
|
||||
var obj = colliders[0].gameObject;
|
||||
signal = new Signal {
|
||||
Object = obj,
|
||||
Shape = new Bounds(bounds.center - obj.transform.position, bounds.size),
|
||||
Strength = 1f
|
||||
};
|
||||
|
||||
return anyFound;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
12
Assets/SensorToolkit/Sensors/src/BaseAreaSensor.cs.meta
Normal file
12
Assets/SensorToolkit/Sensors/src/BaseAreaSensor.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2d2980a7f0c34d2199b27bb0ec29c6ff
|
||||
timeCreated: 1491308888
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
82
Assets/SensorToolkit/Sensors/src/BasePulsableSensor.cs
Normal file
82
Assets/SensorToolkit/Sensors/src/BasePulsableSensor.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Micosmo.SensorToolkit {
|
||||
|
||||
public abstract class BasePulsableSensor : MonoBehaviour {
|
||||
|
||||
// User-defined description for this sensor.
|
||||
// Enable in SensorToolkit/Settings to view in inspectors
|
||||
[SerializeField] string comment;
|
||||
|
||||
PulseHandle runningPulse;
|
||||
PulseHandle pendingPulse;
|
||||
|
||||
// Should cause the sensor to perform it's 'sensing' routine, so that its list of detected objects
|
||||
// is up to date at the time of calling. Each sensor can be configured to pulse automatically at
|
||||
// fixed intervals or each timestep, however, if you need more control over when this occurs then
|
||||
// you can call this method manually.
|
||||
public void Pulse() {
|
||||
if (!isActiveAndEnabled) {
|
||||
return;
|
||||
}
|
||||
if (!runningPulse.TryCancel()) {
|
||||
runningPulse.Complete();
|
||||
}
|
||||
if (!pendingPulse.TryCancel()) {
|
||||
pendingPulse.Complete();
|
||||
}
|
||||
GetPulseJob().Run();
|
||||
}
|
||||
|
||||
// If this sensor has input sensors, then the inputs are pulsed first and then this one is pulsed.
|
||||
public abstract void PulseAll();
|
||||
|
||||
public PulseHandle SchedulePulse() {
|
||||
if (!isActiveAndEnabled) {
|
||||
return default;
|
||||
}
|
||||
if (runningPulse.IsCompleted && !pendingPulse.IsCompleted) {
|
||||
runningPulse = pendingPulse;
|
||||
pendingPulse = default;
|
||||
}
|
||||
|
||||
if (!runningPulse.IsCompleted) {
|
||||
if (!pendingPulse.IsCompleted) {
|
||||
return pendingPulse;
|
||||
}
|
||||
pendingPulse = GetPulseJob().Schedule(runningPulse);
|
||||
pendingPulse.Tick();
|
||||
return pendingPulse;
|
||||
}
|
||||
|
||||
runningPulse = GetPulseJob().Schedule();
|
||||
runningPulse.Tick();
|
||||
return runningPulse;
|
||||
}
|
||||
|
||||
public abstract event System.Action OnPulsed;
|
||||
|
||||
public abstract void Clear();
|
||||
|
||||
public bool ShowDetectionGizmos { get; set; }
|
||||
|
||||
protected abstract PulseJob GetPulseJob();
|
||||
|
||||
protected void ClearPendingPulse() {
|
||||
if (!runningPulse.TryCancel()) {
|
||||
runningPulse.Complete();
|
||||
}
|
||||
if (!pendingPulse.TryCancel()) {
|
||||
pendingPulse.Complete();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnDisable() {
|
||||
ClearPendingPulse();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
11
Assets/SensorToolkit/Sensors/src/BasePulsableSensor.cs.meta
Normal file
11
Assets/SensorToolkit/Sensors/src/BasePulsableSensor.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7129a068598c426bb92759e418b30fdb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
202
Assets/SensorToolkit/Sensors/src/BaseVolumeSensor.cs
Normal file
202
Assets/SensorToolkit/Sensors/src/BaseVolumeSensor.cs
Normal file
@@ -0,0 +1,202 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Micosmo.SensorToolkit {
|
||||
/*
|
||||
* Common functionality for sensors that detect colliders within a volume such as the Range and Trigger Sensors.
|
||||
*/
|
||||
public abstract class BaseVolumeSensor : Sensor {
|
||||
|
||||
#region Configurations
|
||||
[SerializeField]
|
||||
SignalFilter signalFilter = new SignalFilter();
|
||||
|
||||
[Tooltip("In Collider mode the sensor detects GameObjects attached to colliders. In RigidBody mode it detects the RigidBody GameObject attached to colliders.")]
|
||||
public DetectionModes DetectionMode;
|
||||
#endregion
|
||||
|
||||
#region Public
|
||||
// Edit the IgnoreList at runtime. Anything in the list will not be detected
|
||||
public List<GameObject> IgnoreList => signalFilter.IgnoreList;
|
||||
|
||||
// Enable/Disable the tag filtering at runtime
|
||||
public bool EnableTagFilter {
|
||||
get => signalFilter.EnableTagFilter;
|
||||
set => signalFilter.EnableTagFilter = value;
|
||||
}
|
||||
|
||||
// Change the allowed tags at runtime
|
||||
public string[] AllowedTags {
|
||||
get => signalFilter.AllowedTags;
|
||||
set => signalFilter.AllowedTags = value;
|
||||
}
|
||||
|
||||
public override void Clear() {
|
||||
base.Clear();
|
||||
ClearColliders();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Protected
|
||||
protected override List<Collider> GetInputColliders(GameObject inputObject, List<Collider> storeIn) {
|
||||
List<Collider> clist;
|
||||
if (gameObjectColliders.TryGetValue(inputObject, out clist)) {
|
||||
foreach (var c in clist) {
|
||||
storeIn.Add(c);
|
||||
}
|
||||
}
|
||||
return storeIn;
|
||||
}
|
||||
|
||||
protected override void InitialiseSignalProcessors() {
|
||||
base.InitialiseSignalProcessors();
|
||||
MapToSignalProxy.Configure(true);
|
||||
SignalFilter = signalFilter;
|
||||
}
|
||||
|
||||
protected void UpdateAllSignals() {
|
||||
workList.Clear();
|
||||
foreach (var cols in gameObjectColliders) {
|
||||
Signal signal;
|
||||
if (CalculateSignal(cols.Value, out signal)) {
|
||||
workList.Add(signal);
|
||||
}
|
||||
}
|
||||
MapToRigidBody.Configure(DetectionMode, false);
|
||||
UpdateAllSignals(workList);
|
||||
}
|
||||
|
||||
protected void AddCollider(Collider c, bool updateSignal) {
|
||||
var cols = AddColliderToMap(c, c.gameObject, gameObjectColliders);
|
||||
|
||||
if (!updateSignal) {
|
||||
return;
|
||||
}
|
||||
MapToRigidBody.Configure(DetectionMode, false);
|
||||
Signal signal;
|
||||
if (CalculateSignal(cols, out signal)) {
|
||||
UpdateSignalImmediate(signal);
|
||||
}
|
||||
}
|
||||
|
||||
protected void RemoveCollider(Collider c, bool updateSignal) {
|
||||
if (c == null) {
|
||||
ClearDestroyedGameObjects();
|
||||
return;
|
||||
}
|
||||
|
||||
var cols = RemoveColliderFromMap(c, c.gameObject, gameObjectColliders);
|
||||
|
||||
if (!updateSignal) {
|
||||
return;
|
||||
}
|
||||
MapToRigidBody.Configure(DetectionMode, false);
|
||||
if (cols == null) {
|
||||
LostSignalImmediate(c.gameObject);
|
||||
} else {
|
||||
Signal signal;
|
||||
if (CalculateSignal(cols, out signal)) {
|
||||
UpdateSignalImmediate(signal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void ClearColliders() {
|
||||
foreach (var set in gameObjectColliders) {
|
||||
colliderListCache.Dispose(set.Value);
|
||||
}
|
||||
gameObjectColliders.Clear();
|
||||
}
|
||||
|
||||
List<Collider> colliderList = new List<Collider>();
|
||||
protected List<Collider> GetColliders() {
|
||||
colliderList.Clear();
|
||||
foreach (var set in gameObjectColliders) {
|
||||
foreach (var collider in set.Value) {
|
||||
colliderList.Add(collider);
|
||||
}
|
||||
}
|
||||
return colliderList;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Internals
|
||||
// Maps a GameObject to a list of it's colliders that have been detected.
|
||||
List<Collider> colliders = new List<Collider>();
|
||||
Dictionary<GameObject, List<Collider>> gameObjectColliders = new Dictionary<GameObject, List<Collider>>();
|
||||
|
||||
// List of temporary values for modifying collections
|
||||
List<GameObject> gameObjectList = new List<GameObject>();
|
||||
List<Signal> workList = new List<Signal>();
|
||||
|
||||
static ListCache<Collider> colliderListCache = new ListCache<Collider>();
|
||||
|
||||
void ClearDestroyedGameObjects() {
|
||||
gameObjectList.Clear();
|
||||
foreach (var set in gameObjectColliders) {
|
||||
if (set.Key == null) {
|
||||
gameObjectList.Add(set.Key);
|
||||
}
|
||||
}
|
||||
foreach (var go in gameObjectList) {
|
||||
colliderListCache.Dispose(gameObjectColliders[go]);
|
||||
gameObjectColliders.Remove(go);
|
||||
}
|
||||
}
|
||||
|
||||
List<Collider> AddColliderToMap(Collider c, GameObject go, Dictionary<GameObject, List<Collider>> dict) {
|
||||
List<Collider> colliderList;
|
||||
if (!dict.TryGetValue(go, out colliderList)) {
|
||||
colliderList = colliderListCache.Get();
|
||||
dict[go] = colliderList;
|
||||
}
|
||||
if (!colliderList.Contains(c)) {
|
||||
colliderList.Add(c);
|
||||
}
|
||||
return colliderList;
|
||||
}
|
||||
|
||||
List<Collider> RemoveColliderFromMap(Collider c, GameObject go, Dictionary<GameObject, List<Collider>> dict) {
|
||||
List<Collider> colliderList = null;
|
||||
if (dict.TryGetValue(go, out colliderList)) {
|
||||
colliderList.Remove(c);
|
||||
if (colliderList.Count == 0) {
|
||||
dict.Remove(go);
|
||||
colliderListCache.Dispose(colliderList);
|
||||
colliderList = null;
|
||||
}
|
||||
}
|
||||
return colliderList;
|
||||
}
|
||||
|
||||
bool CalculateSignal(List<Collider> colliders, out Signal signal) {
|
||||
signal = default;
|
||||
|
||||
Bounds bounds = new Bounds();
|
||||
bool anyFound = false;
|
||||
foreach (var c in colliders) {
|
||||
if (!signalFilter.TestCollider(c)) {
|
||||
continue;
|
||||
}
|
||||
if (!anyFound) {
|
||||
bounds = c.bounds;
|
||||
anyFound = true;
|
||||
} else {
|
||||
bounds.Encapsulate(c.bounds);
|
||||
}
|
||||
}
|
||||
|
||||
var obj = colliders[0].gameObject;
|
||||
var position = obj.transform.position;
|
||||
signal = new Signal {
|
||||
Object = obj,
|
||||
Shape = new Bounds(bounds.center - position, bounds.size),
|
||||
Strength = 1f
|
||||
};
|
||||
|
||||
return anyFound;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
12
Assets/SensorToolkit/Sensors/src/BaseVolumeSensor.cs.meta
Normal file
12
Assets/SensorToolkit/Sensors/src/BaseVolumeSensor.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d62bea7337d540409a9678c0a6e8f081
|
||||
timeCreated: 1489882031
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
40
Assets/SensorToolkit/Sensors/src/BufferedPhysics.cs
Normal file
40
Assets/SensorToolkit/Sensors/src/BufferedPhysics.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Micosmo.SensorToolkit {
|
||||
|
||||
public interface ITestNonAlloc<S, T> where S : Sensor {
|
||||
int Test(S sensor, T[] results);
|
||||
}
|
||||
|
||||
public class PhysicsNonAlloc<T> {
|
||||
public static int InitialSize = 20;
|
||||
public static bool DynamicallyIncreaseBufferSize = true;
|
||||
|
||||
public T[] Buffer { get; private set; }
|
||||
public int Count { get; private set; }
|
||||
|
||||
public bool IsAtCapacity { get { return Count == Buffer.Length; } }
|
||||
|
||||
public PhysicsNonAlloc() {
|
||||
Expand(Mathf.Max(InitialSize, 1));
|
||||
}
|
||||
|
||||
public int PerformTest<S>(S sensor, ITestNonAlloc<S, T> tester) where S : Sensor {
|
||||
Count = tester.Test(sensor, Buffer);
|
||||
|
||||
if (Count == Buffer.Length && DynamicallyIncreaseBufferSize) {
|
||||
Expand(Count * 2);
|
||||
return PerformTest(sensor, tester);
|
||||
}
|
||||
|
||||
return Count;
|
||||
}
|
||||
|
||||
void Expand(int toSize) {
|
||||
Buffer = new T[toSize];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
12
Assets/SensorToolkit/Sensors/src/BufferedPhysics.cs.meta
Normal file
12
Assets/SensorToolkit/Sensors/src/BufferedPhysics.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1514ea5919444691b9b9cde7cb0cf94a
|
||||
timeCreated: 1601161652
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
156
Assets/SensorToolkit/Sensors/src/DrawIfAttribute.cs
Normal file
156
Assets/SensorToolkit/Sensors/src/DrawIfAttribute.cs
Normal file
@@ -0,0 +1,156 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace Micosmo.SensorToolkit {
|
||||
|
||||
/// <summary>
|
||||
/// Draws the field/property ONLY if the compared property compared by the comparison type with the value of comparedValue returns true.
|
||||
/// Based on: https://forum.unity.com/threads/draw-a-field-only-if-a-condition-is-met.448855/
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]
|
||||
internal class DrawIfAttribute : PropertyAttribute {
|
||||
#region Fields
|
||||
|
||||
public string comparedPropertyName { get; private set; }
|
||||
public object comparedValue { get; private set; }
|
||||
public DisablingType disablingType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Types of comperisons.
|
||||
/// </summary>
|
||||
public enum DisablingType {
|
||||
ReadOnly = 2,
|
||||
DontDraw = 3
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Only draws the field only if a condition is met. Supports enum and bools.
|
||||
/// </summary>
|
||||
/// <param name="comparedPropertyName">The name of the property that is being compared (case sensitive).</param>
|
||||
/// <param name="comparedValue">The value the property is being compared to.</param>
|
||||
/// <param name="disablingType">The type of disabling that should happen if the condition is NOT met. Defaulted to DisablingType.DontDraw.</param>
|
||||
public DrawIfAttribute(string comparedPropertyName, object comparedValue, DisablingType disablingType = DisablingType.DontDraw) {
|
||||
this.comparedPropertyName = comparedPropertyName;
|
||||
this.comparedValue = comparedValue;
|
||||
this.disablingType = disablingType;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
namespace Micosmo.SensorToolkit.Editors {
|
||||
|
||||
/// <summary>
|
||||
/// Based on: https://forum.unity.com/threads/draw-a-field-only-if-a-condition-is-met.448855/
|
||||
/// </summary>
|
||||
[CustomPropertyDrawer(typeof(DrawIfAttribute))]
|
||||
public class DrawIfPropertyDrawer : PropertyDrawer {
|
||||
#region Fields
|
||||
|
||||
// Reference to the attribute on the property.
|
||||
DrawIfAttribute drawIf;
|
||||
|
||||
// Field that is being compared.
|
||||
SerializedProperty comparedField;
|
||||
|
||||
#endregion
|
||||
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
|
||||
if (!ShowMe(property) && drawIf.disablingType == DrawIfAttribute.DisablingType.DontDraw) {
|
||||
return -EditorGUIUtility.standardVerticalSpacing;
|
||||
} else {
|
||||
if (property.propertyType == SerializedPropertyType.Generic) {
|
||||
int numChildren = 0;
|
||||
float totalHeight = 0.0f;
|
||||
|
||||
var children = property.GetEnumerator();
|
||||
|
||||
while (children.MoveNext()) {
|
||||
SerializedProperty child = children.Current as SerializedProperty;
|
||||
|
||||
GUIContent childLabel = new GUIContent(child.displayName);
|
||||
|
||||
totalHeight += EditorGUI.GetPropertyHeight(child, childLabel) + EditorGUIUtility.standardVerticalSpacing;
|
||||
numChildren++;
|
||||
}
|
||||
|
||||
// Remove extra space at end, (we only want spaces between items)
|
||||
totalHeight -= EditorGUIUtility.standardVerticalSpacing;
|
||||
|
||||
return totalHeight;
|
||||
}
|
||||
|
||||
return EditorGUI.GetPropertyHeight(property, label);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Errors default to showing the property.
|
||||
/// </summary>
|
||||
private bool ShowMe(SerializedProperty property) {
|
||||
drawIf = attribute as DrawIfAttribute;
|
||||
// Replace propertyname to the value from the parameter
|
||||
string path = property.propertyPath.Contains(".") ? System.IO.Path.ChangeExtension(property.propertyPath, drawIf.comparedPropertyName) : drawIf.comparedPropertyName;
|
||||
|
||||
comparedField = property.serializedObject.FindProperty(path);
|
||||
|
||||
if (comparedField == null) {
|
||||
Debug.LogError("Cannot find property with name: " + path);
|
||||
return true;
|
||||
}
|
||||
|
||||
// get the value & compare based on types
|
||||
switch (comparedField.type) { // Possible extend cases to support your own type
|
||||
case "bool":
|
||||
return comparedField.boolValue.Equals(drawIf.comparedValue);
|
||||
case "Enum":
|
||||
return comparedField.enumValueIndex.Equals((int)drawIf.comparedValue);
|
||||
default:
|
||||
Debug.LogError("Error: " + comparedField.type + " is not supported of " + path);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
|
||||
// If the condition is met, simply draw the field.
|
||||
if (ShowMe(property)) {
|
||||
// A Generic type means a custom class...
|
||||
if (property.propertyType == SerializedPropertyType.Generic) {
|
||||
var children = property.GetEnumerator();
|
||||
|
||||
Rect offsetPosition = position;
|
||||
|
||||
while (children.MoveNext()) {
|
||||
SerializedProperty child = children.Current as SerializedProperty;
|
||||
|
||||
GUIContent childLabel = new GUIContent(child.displayName);
|
||||
|
||||
float childHeight = EditorGUI.GetPropertyHeight(child, childLabel);
|
||||
offsetPosition.height = childHeight;
|
||||
|
||||
EditorGUI.PropertyField(offsetPosition, child, childLabel);
|
||||
|
||||
offsetPosition.y += childHeight + EditorGUIUtility.standardVerticalSpacing;
|
||||
}
|
||||
} else {
|
||||
EditorGUI.PropertyField(position, property, label);
|
||||
}
|
||||
|
||||
} //...check if the disabling type is read only. If it is, draw it disabled
|
||||
else if (drawIf.disablingType == DrawIfAttribute.DisablingType.ReadOnly) {
|
||||
GUI.enabled = false;
|
||||
EditorGUI.PropertyField(position, property, label);
|
||||
GUI.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
11
Assets/SensorToolkit/Sensors/src/DrawIfAttribute.cs.meta
Normal file
11
Assets/SensorToolkit/Sensors/src/DrawIfAttribute.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 258e14d7ad06f12429ff0ebf28166fcd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
9
Assets/SensorToolkit/Sensors/src/Editor.meta
Normal file
9
Assets/SensorToolkit/Sensors/src/Editor.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 259cbc14df0e41919b6fa5f3c0020f64
|
||||
folderAsset: yes
|
||||
timeCreated: 1488607388
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
127
Assets/SensorToolkit/Sensors/src/Editor/ArcSensor2DEditor.cs
Normal file
127
Assets/SensorToolkit/Sensors/src/Editor/ArcSensor2DEditor.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Micosmo.SensorToolkit.Editors {
|
||||
|
||||
[CustomEditor(typeof(ArcSensor2D))]
|
||||
[CanEditMultipleObjects]
|
||||
public class ArcSensor2DEditor : BaseSensorEditor<ArcSensor2D> {
|
||||
SerializedProperty parameterisation;
|
||||
SerializedProperty bezier;
|
||||
SerializedProperty ballistic;
|
||||
SerializedProperty ignoreList;
|
||||
SerializedProperty tagFilterEnabled;
|
||||
SerializedProperty tagFilter;
|
||||
SerializedProperty detectsOnLayers;
|
||||
SerializedProperty detectionMode;
|
||||
SerializedProperty ignoreTriggerColliders;
|
||||
SerializedProperty signalProcessors;
|
||||
SerializedProperty obstructedByLayers;
|
||||
SerializedProperty worldSpace;
|
||||
SerializedProperty pulseMode;
|
||||
SerializedProperty pulseUpdateFunction;
|
||||
SerializedProperty pulseInterval;
|
||||
SerializedProperty onDetected;
|
||||
SerializedProperty onLostDetection;
|
||||
SerializedProperty onSomeDetection;
|
||||
SerializedProperty onNoDetection;
|
||||
SerializedProperty onObstructed;
|
||||
SerializedProperty onClear;
|
||||
|
||||
bool showEvents = false;
|
||||
|
||||
protected override bool canTest => true;
|
||||
|
||||
protected override void OnEnable() {
|
||||
base.OnEnable();
|
||||
|
||||
if (serializedObject == null) return;
|
||||
|
||||
parameterisation = serializedObject.FindProperty("Parameterisation");
|
||||
bezier = serializedObject.FindProperty("Bezier");
|
||||
ballistic = serializedObject.FindProperty("Ballistic");
|
||||
ignoreList = serializedObject.FindProperty("signalFilter.IgnoreList");
|
||||
tagFilterEnabled = serializedObject.FindProperty("signalFilter.EnableTagFilter");
|
||||
tagFilter = serializedObject.FindProperty("signalFilter.AllowedTags");
|
||||
detectsOnLayers = serializedObject.FindProperty("DetectsOnLayers");
|
||||
detectionMode = serializedObject.FindProperty("DetectionMode");
|
||||
ignoreTriggerColliders = serializedObject.FindProperty("IgnoreTriggerColliders");
|
||||
signalProcessors = serializedObject.FindProperty("signalProcessors");
|
||||
obstructedByLayers = serializedObject.FindProperty("ObstructedByLayers");
|
||||
worldSpace = serializedObject.FindProperty("WorldSpace");
|
||||
pulseMode = serializedObject.FindProperty("pulseRoutine.Mode");
|
||||
pulseUpdateFunction = serializedObject.FindProperty("pulseRoutine.UpdateFunction");
|
||||
pulseInterval = serializedObject.FindProperty("pulseRoutine.Interval");
|
||||
onDetected = serializedObject.FindProperty("OnDetected");
|
||||
onLostDetection = serializedObject.FindProperty("OnLostDetection");
|
||||
onSomeDetection = serializedObject.FindProperty("OnSomeDetection");
|
||||
onNoDetection = serializedObject.FindProperty("OnNoDetection");
|
||||
onObstructed = serializedObject.FindProperty("onObstruction");
|
||||
onClear = serializedObject.FindProperty("onClear");
|
||||
}
|
||||
|
||||
protected override void InspectorParameters() {
|
||||
EditorGUILayout.PropertyField(parameterisation);
|
||||
if (sensor.Parameterisation == ArcSensor2D.ParameterisationType.Bezier) {
|
||||
EditorUtils.InlinePropertyField(bezier);
|
||||
} else if (sensor.Parameterisation == ArcSensor2D.ParameterisationType.Ballistic) {
|
||||
EditorUtils.InlinePropertyField(ballistic);
|
||||
}
|
||||
EditorGUILayout.PropertyField(worldSpace);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(ignoreList, true);
|
||||
EditorGUILayout.PropertyField(tagFilterEnabled);
|
||||
if (tagFilterEnabled.boolValue) {
|
||||
EditorGUILayout.PropertyField(tagFilter, true);
|
||||
}
|
||||
EditorGUILayout.PropertyField(detectsOnLayers);
|
||||
EditorGUILayout.PropertyField(detectionMode);
|
||||
EditorGUILayout.PropertyField(ignoreTriggerColliders);
|
||||
EditorGUILayout.PropertyField(signalProcessors, new GUIContent("Signal Processors"));
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(obstructedByLayers);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(pulseMode, new GUIContent("Pulse Mode"));
|
||||
if (sensor.PulseMode != PulseRoutine.Modes.Manual) {
|
||||
EditorGUILayout.PropertyField(pulseUpdateFunction);
|
||||
}
|
||||
if (sensor.PulseMode == PulseRoutine.Modes.FixedInterval) {
|
||||
EditorGUILayout.PropertyField(pulseInterval, new GUIContent("Pulse Interval"));
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (showEvents = EditorGUILayout.Foldout(showEvents, "Events")) {
|
||||
EditorGUILayout.PropertyField(onDetected);
|
||||
EditorGUILayout.PropertyField(onLostDetection);
|
||||
EditorGUILayout.PropertyField(onSomeDetection);
|
||||
EditorGUILayout.PropertyField(onNoDetection);
|
||||
EditorGUILayout.PropertyField(onObstructed);
|
||||
EditorGUILayout.PropertyField(onClear);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
BufferSizeInfo(sensor.CurrentBufferSize);
|
||||
}
|
||||
|
||||
protected override void InspectorDetectedObjects() {
|
||||
base.InspectorDetectedObjects();
|
||||
|
||||
if (!sensor.IsObstructed) return;
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("Arc is Obstructed", new GUIStyle() { fontStyle = FontStyle.Bold, normal = new GUIStyleState() { textColor = STPrefs.RedEditorTextColour } });
|
||||
DetectedObjectFieldLayout(sensor.GetObstructionRayHit().GameObject);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3836000b75c34e6a94ea1d518fbef050
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
128
Assets/SensorToolkit/Sensors/src/Editor/ArcSensorEditor.cs
Normal file
128
Assets/SensorToolkit/Sensors/src/Editor/ArcSensorEditor.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Micosmo.SensorToolkit.Editors {
|
||||
|
||||
[CustomEditor(typeof(ArcSensor))]
|
||||
[CanEditMultipleObjects]
|
||||
public class ArcSensorEditor : BaseSensorEditor<ArcSensor> {
|
||||
SerializedProperty parameterisation;
|
||||
SerializedProperty bezier;
|
||||
SerializedProperty ballistic;
|
||||
SerializedProperty ignoreList;
|
||||
SerializedProperty tagFilterEnabled;
|
||||
SerializedProperty tagFilter;
|
||||
SerializedProperty detectsOnLayers;
|
||||
SerializedProperty detectionMode;
|
||||
SerializedProperty ignoreTriggerColliders;
|
||||
SerializedProperty signalProcessors;
|
||||
SerializedProperty obstructedByLayers;
|
||||
SerializedProperty worldSpace;
|
||||
SerializedProperty pulseMode;
|
||||
SerializedProperty pulseUpdateFunction;
|
||||
SerializedProperty pulseInterval;
|
||||
SerializedProperty onDetected;
|
||||
SerializedProperty onLostDetection;
|
||||
SerializedProperty onSomeDetection;
|
||||
SerializedProperty onNoDetection;
|
||||
SerializedProperty onObstructed;
|
||||
SerializedProperty onClear;
|
||||
|
||||
bool showEvents = false;
|
||||
|
||||
protected override bool canTest => true;
|
||||
|
||||
protected override void OnEnable() {
|
||||
base.OnEnable();
|
||||
|
||||
if (serializedObject == null) return;
|
||||
|
||||
parameterisation = serializedObject.FindProperty("Parameterisation");
|
||||
bezier = serializedObject.FindProperty("Bezier");
|
||||
ballistic = serializedObject.FindProperty("Ballistic");
|
||||
ignoreList = serializedObject.FindProperty("signalFilter.IgnoreList");
|
||||
tagFilterEnabled = serializedObject.FindProperty("signalFilter.EnableTagFilter");
|
||||
tagFilter = serializedObject.FindProperty("signalFilter.AllowedTags");
|
||||
detectsOnLayers = serializedObject.FindProperty("DetectsOnLayers");
|
||||
detectionMode = serializedObject.FindProperty("DetectionMode");
|
||||
ignoreTriggerColliders = serializedObject.FindProperty("IgnoreTriggerColliders");
|
||||
signalProcessors = serializedObject.FindProperty("signalProcessors");
|
||||
obstructedByLayers = serializedObject.FindProperty("ObstructedByLayers");
|
||||
worldSpace = serializedObject.FindProperty("WorldSpace");
|
||||
pulseMode = serializedObject.FindProperty("pulseRoutine.Mode");
|
||||
pulseUpdateFunction = serializedObject.FindProperty("pulseRoutine.UpdateFunction");
|
||||
pulseInterval = serializedObject.FindProperty("pulseRoutine.Interval");
|
||||
onDetected = serializedObject.FindProperty("OnDetected");
|
||||
onLostDetection = serializedObject.FindProperty("OnLostDetection");
|
||||
onSomeDetection = serializedObject.FindProperty("OnSomeDetection");
|
||||
onNoDetection = serializedObject.FindProperty("OnNoDetection");
|
||||
onObstructed = serializedObject.FindProperty("onObstruction");
|
||||
onClear = serializedObject.FindProperty("onClear");
|
||||
}
|
||||
|
||||
protected override void InspectorParameters() {
|
||||
EditorGUILayout.PropertyField(parameterisation);
|
||||
if (sensor.Parameterisation == ArcSensor.ParameterisationType.Bezier) {
|
||||
EditorUtils.InlinePropertyField(bezier);
|
||||
} else if (sensor.Parameterisation == ArcSensor.ParameterisationType.Ballistic) {
|
||||
EditorUtils.InlinePropertyField(ballistic);
|
||||
}
|
||||
EditorGUILayout.PropertyField(worldSpace);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(ignoreList, true);
|
||||
EditorGUILayout.PropertyField(tagFilterEnabled);
|
||||
if (tagFilterEnabled.boolValue) {
|
||||
EditorGUILayout.PropertyField(tagFilter, true);
|
||||
}
|
||||
EditorGUILayout.PropertyField(detectsOnLayers);
|
||||
EditorGUILayout.PropertyField(detectionMode);
|
||||
EditorGUILayout.PropertyField(ignoreTriggerColliders);
|
||||
EditorGUILayout.PropertyField(signalProcessors, new GUIContent("Signal Processors"));
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(obstructedByLayers);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(pulseMode, new GUIContent("Pulse Mode"));
|
||||
if (sensor.PulseMode != PulseRoutine.Modes.Manual) {
|
||||
EditorGUILayout.PropertyField(pulseUpdateFunction);
|
||||
}
|
||||
if (sensor.PulseMode == PulseRoutine.Modes.FixedInterval) {
|
||||
EditorGUILayout.PropertyField(pulseInterval, new GUIContent("Pulse Interval"));
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (showEvents = EditorGUILayout.Foldout(showEvents, "Events")) {
|
||||
EditorGUILayout.PropertyField(onDetected);
|
||||
EditorGUILayout.PropertyField(onLostDetection);
|
||||
EditorGUILayout.PropertyField(onSomeDetection);
|
||||
EditorGUILayout.PropertyField(onNoDetection);
|
||||
EditorGUILayout.PropertyField(onObstructed);
|
||||
EditorGUILayout.PropertyField(onClear);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
BufferSizeInfo(sensor.CurrentBufferSize);
|
||||
}
|
||||
|
||||
protected override void InspectorDetectedObjects() {
|
||||
base.InspectorDetectedObjects();
|
||||
|
||||
if (!sensor.IsObstructed) return;
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("Arc is Obstructed", new GUIStyle() { fontStyle = FontStyle.Bold, normal = new GUIStyleState() { textColor = STPrefs.RedEditorTextColour } });
|
||||
DetectedObjectFieldLayout(sensor.GetObstructionRayHit().GameObject);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4b59d8dc7c6f475dbafc42d1973b84aa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
142
Assets/SensorToolkit/Sensors/src/Editor/BasePulsableEditor.cs
Normal file
142
Assets/SensorToolkit/Sensors/src/Editor/BasePulsableEditor.cs
Normal file
@@ -0,0 +1,142 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Micosmo.SensorToolkit.Editors {
|
||||
|
||||
public static class EditorState {
|
||||
public static event System.Action OnStopTesting;
|
||||
public static Observable<BasePulsableSensor> ActivePulsable = new Observable<BasePulsableSensor>();
|
||||
public static void StopAllTesting() {
|
||||
if (EditorApplication.isPlaying || EditorApplication.isPaused) {
|
||||
return;
|
||||
}
|
||||
OnStopTesting?.Invoke();
|
||||
ActivePulsable.Value = null;
|
||||
}
|
||||
[InitializeOnEnterPlayMode]
|
||||
static void OnEnterPlayMode(EnterPlayModeOptions options) {
|
||||
if (options.HasFlag(EnterPlayModeOptions.DisableDomainReload)) {
|
||||
StopAllTesting();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class BasePulsableEditor<T> : Editor where T : BasePulsableSensor {
|
||||
|
||||
T pulsable;
|
||||
protected bool IsTesting = false;
|
||||
protected bool IsActivePulsable => EditorState.ActivePulsable.Value == pulsable;
|
||||
protected abstract bool canTest { get; }
|
||||
|
||||
bool isInGame => EditorApplication.isPlaying || EditorApplication.isPaused;
|
||||
protected bool showDetections => isInGame || IsTesting;
|
||||
|
||||
SerializedProperty comment;
|
||||
|
||||
protected virtual void OnEnable() {
|
||||
if (serializedObject == null) {
|
||||
return;
|
||||
}
|
||||
pulsable = serializedObject.targetObject as T;
|
||||
pulsable.OnPulsed += OnPulsedHandler;
|
||||
EditorState.OnStopTesting += OnStopTestingHandler;
|
||||
EditorState.ActivePulsable.OnChanged += ActivePulsableChangedHandler;
|
||||
|
||||
if ((EditorApplication.isPlaying || EditorApplication.isPaused) && EditorState.ActivePulsable.Value == null) {
|
||||
EditorState.ActivePulsable.Value = pulsable;
|
||||
}
|
||||
|
||||
comment = serializedObject.FindProperty("comment");
|
||||
}
|
||||
|
||||
protected virtual void OnDisable() {
|
||||
EditorState.StopAllTesting();
|
||||
if (IsActivePulsable) {
|
||||
EditorState.ActivePulsable.Value = null;
|
||||
}
|
||||
pulsable.OnPulsed -= OnPulsedHandler;
|
||||
EditorState.OnStopTesting -= OnStopTestingHandler;
|
||||
EditorState.ActivePulsable.OnChanged -= ActivePulsableChangedHandler;
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI() {
|
||||
var mb = pulsable as MonoBehaviour;
|
||||
if (mb != null && mb.transform.hasChanged) {
|
||||
EditorState.StopAllTesting();
|
||||
mb.transform.hasChanged = false;
|
||||
}
|
||||
|
||||
serializedObject.Update();
|
||||
|
||||
var rect = EditorGUILayout.BeginVertical();
|
||||
rect.xMin -= 12; rect.xMax += 2;
|
||||
if (IsActivePulsable) {
|
||||
DrawActive(rect);
|
||||
}
|
||||
if (STPrefs.ShowUserComments) {
|
||||
EditorGUILayout.PropertyField(comment);
|
||||
}
|
||||
OnPulsableGUI();
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (showDetections && !IsActivePulsable) {
|
||||
if (GUILayout.Button("Show Gizmos", GUILayout.Width(100))) {
|
||||
EditorState.ActivePulsable.Value = pulsable;
|
||||
}
|
||||
}
|
||||
if (canTest && !isInGame) {
|
||||
if (GUILayout.Button("Test", GUILayout.Width(100))) {
|
||||
StartTesting();
|
||||
}
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
void DrawActive(Rect rect) {
|
||||
var colour = STPrefs.ActiveSensorEditorColour;
|
||||
EditorGUI.DrawRect(rect, colour);
|
||||
}
|
||||
|
||||
protected abstract void OnPulsableGUI();
|
||||
|
||||
void OnPulsedHandler() {
|
||||
Repaint();
|
||||
if (Application.isPlaying || pulsable == null) {
|
||||
return;
|
||||
}
|
||||
IsTesting = true;
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
|
||||
void StartTesting() {
|
||||
if (Application.isPlaying || pulsable == null) {
|
||||
return;
|
||||
}
|
||||
if (IsTesting) {
|
||||
EditorState.StopAllTesting();
|
||||
}
|
||||
pulsable.PulseAll();
|
||||
EditorState.ActivePulsable.Value = pulsable;
|
||||
}
|
||||
|
||||
void OnStopTestingHandler() {
|
||||
if (!IsTesting || Application.isPlaying || pulsable == null) {
|
||||
return;
|
||||
}
|
||||
IsTesting = false;
|
||||
pulsable.Clear();
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
|
||||
void ActivePulsableChangedHandler() {
|
||||
pulsable.ShowDetectionGizmos = IsActivePulsable;
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3f7a263227ad44aba56cc981feac916b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
106
Assets/SensorToolkit/Sensors/src/Editor/BaseSensorEditor.cs
Normal file
106
Assets/SensorToolkit/Sensors/src/Editor/BaseSensorEditor.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Micosmo.SensorToolkit.Editors {
|
||||
|
||||
public class SensorEditorActions {
|
||||
static double prevPingTime;
|
||||
static GameObject prevPingObject;
|
||||
public static void PingOrSelectObject(GameObject o) {
|
||||
var time = EditorApplication.timeSinceStartup;
|
||||
var delta = time - prevPingTime;
|
||||
if (o != null && ReferenceEquals(o, prevPingObject) && delta <= .3f) {
|
||||
Selection.activeGameObject = o;
|
||||
} else if (o != null) {
|
||||
EditorGUIUtility.PingObject(o);
|
||||
}
|
||||
prevPingTime = time;
|
||||
prevPingObject = o;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class BaseSensorEditor<T> : BasePulsableEditor<T> where T : Sensor {
|
||||
|
||||
protected T sensor;
|
||||
|
||||
protected override void OnEnable() {
|
||||
base.OnEnable();
|
||||
sensor = serializedObject.targetObject as T;
|
||||
}
|
||||
|
||||
protected override void OnPulsableGUI() {
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
InspectorParameters();
|
||||
if (EditorGUI.EndChangeCheck()) {
|
||||
EditorState.StopAllTesting();
|
||||
}
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
|
||||
if (showDetections) {
|
||||
EditorUtils.HorizontalLine(new Color(0.5f, 0.5f, 0.5f, 1));
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
InspectorDetectedObjects();
|
||||
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void InspectorParameters();
|
||||
|
||||
protected virtual void InspectorDetectedObjects() {
|
||||
if (sensor.Signals.Count == 0) {
|
||||
EditorGUILayout.LabelField("Nothing Detected...", new GUIStyle() { fontStyle = FontStyle.Bold });
|
||||
return;
|
||||
}
|
||||
EditorGUILayout.LabelField("Detected Signals", new GUIStyle() { fontStyle = FontStyle.Bold });
|
||||
foreach (var signal in sensor.Signals) {
|
||||
SignalFieldLayout(signal);
|
||||
}
|
||||
}
|
||||
|
||||
protected void SignalFieldLayout(Signal signal) {
|
||||
var rect = EditorGUILayout.GetControlRect();
|
||||
SignalField(rect, signal);
|
||||
}
|
||||
|
||||
protected void SignalField(Rect rect, Signal signal) {
|
||||
var width = rect.width;
|
||||
var objRect = new Rect(rect.x, rect.y, width / 2, rect.height);
|
||||
var strengthRect = new Rect(objRect.max.x, rect.y, width / 4, rect.height);
|
||||
var shapeRect = new Rect(strengthRect.max.x, rect.y, width / 4, rect.height);
|
||||
DetectedObjectField(objRect, signal.Object);
|
||||
EditorGUI.LabelField(strengthRect, $"Str: {signal.Strength.ToString("N1")}", new GUIStyle() { alignment = TextAnchor.MiddleCenter });
|
||||
EditorGUI.LabelField(shapeRect, $"Size: {signal.Shape.size.magnitude.ToString("N1")}");
|
||||
}
|
||||
|
||||
protected void DetectedObjectFieldLayout(GameObject go) {
|
||||
var rect = EditorGUILayout.GetControlRect();
|
||||
DetectedObjectField(rect, go);
|
||||
}
|
||||
|
||||
protected void DetectedObjectField(Rect rect, GameObject go) {
|
||||
var guiContent = EditorGUIUtility.ObjectContent(go, typeof(GameObject));
|
||||
|
||||
var style = new GUIStyle("TextField");
|
||||
style.fixedHeight = 16;
|
||||
style.imagePosition = go ? ImagePosition.ImageLeft : ImagePosition.TextOnly;
|
||||
|
||||
if (GUI.Button(rect, guiContent, style) && go) {
|
||||
SensorEditorActions.PingOrSelectObject(go);
|
||||
}
|
||||
}
|
||||
|
||||
protected void BufferSizeInfo(int bufferSize) {
|
||||
if (bufferSize > PhysicsNonAlloc<object>.InitialSize && Application.isPlaying) {
|
||||
EditorGUILayout.HelpBox("Buffer size expanded to: " + bufferSize, MessageType.Info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 70eca8a011db432fb1fdebee224b01d3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,56 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Micosmo.SensorToolkit.Editors {
|
||||
|
||||
[CustomEditor(typeof(BooleanSensor))]
|
||||
[CanEditMultipleObjects]
|
||||
public class BooleanSensorEditor : BaseSensorEditor<BooleanSensor> {
|
||||
SerializedProperty inputSensors;
|
||||
SerializedProperty operation;
|
||||
SerializedProperty signalProcessors;
|
||||
SerializedProperty onDetected;
|
||||
SerializedProperty onLostDetection;
|
||||
SerializedProperty onSomeDetection;
|
||||
SerializedProperty onNoDetection;
|
||||
|
||||
bool showEvents = false;
|
||||
|
||||
protected override bool canTest { get { return true; } }
|
||||
|
||||
protected override void OnEnable() {
|
||||
base.OnEnable();
|
||||
|
||||
if (serializedObject == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
inputSensors = serializedObject.FindProperty("InputSensors");
|
||||
operation = serializedObject.FindProperty("operation");
|
||||
signalProcessors = serializedObject.FindProperty("signalProcessors");
|
||||
onDetected = serializedObject.FindProperty("OnDetected");
|
||||
onLostDetection = serializedObject.FindProperty("OnLostDetection");
|
||||
onSomeDetection = serializedObject.FindProperty("OnSomeDetection");
|
||||
onNoDetection = serializedObject.FindProperty("OnNoDetection");
|
||||
}
|
||||
|
||||
protected override void InspectorParameters() {
|
||||
EditorGUILayout.PropertyField(inputSensors, true);
|
||||
|
||||
EditorGUILayout.PropertyField(operation);
|
||||
EditorGUILayout.PropertyField(signalProcessors, new GUIContent("Signal Processors"));
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (showEvents = EditorGUILayout.Foldout(showEvents, "Events")) {
|
||||
EditorGUILayout.PropertyField(onDetected);
|
||||
EditorGUILayout.PropertyField(onLostDetection);
|
||||
EditorGUILayout.PropertyField(onSomeDetection);
|
||||
EditorGUILayout.PropertyField(onNoDetection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b2019cf5f0c448dba819243098ec8e49
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
40
Assets/SensorToolkit/Sensors/src/Editor/EditorUtils.cs
Normal file
40
Assets/SensorToolkit/Sensors/src/Editor/EditorUtils.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections;
|
||||
|
||||
namespace Micosmo.SensorToolkit.Editors {
|
||||
|
||||
public class EditorUtils {
|
||||
|
||||
public static void InlinePropertyField(SerializedProperty root) {
|
||||
if (!root.hasChildren) {
|
||||
return;
|
||||
}
|
||||
|
||||
SerializedProperty child = root.Copy();
|
||||
SerializedProperty nextSiblingProperty = root.Copy();
|
||||
nextSiblingProperty.NextVisible(false);
|
||||
|
||||
child.NextVisible(true);
|
||||
|
||||
while(!SerializedProperty.EqualContents(child, nextSiblingProperty)) {
|
||||
EditorGUILayout.PropertyField(child, true);
|
||||
child.NextVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void HorizontalLine(Color color) {
|
||||
GUIStyle horizontalLine;
|
||||
horizontalLine = new GUIStyle();
|
||||
horizontalLine.normal.background = EditorGUIUtility.whiteTexture;
|
||||
horizontalLine.margin = new RectOffset(0, 0, 4, 4);
|
||||
horizontalLine.fixedHeight = 1;
|
||||
var c = GUI.color;
|
||||
GUI.color = color;
|
||||
GUILayout.Box(GUIContent.none, horizontalLine);
|
||||
GUI.color = c;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
12
Assets/SensorToolkit/Sensors/src/Editor/EditorUtils.cs.meta
Normal file
12
Assets/SensorToolkit/Sensors/src/Editor/EditorUtils.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1f1d93a18b8749548f323705b3edaafb
|
||||
timeCreated: 1601171253
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,37 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections;
|
||||
|
||||
namespace Micosmo.SensorToolkit
|
||||
{
|
||||
[CustomEditor(typeof(FOVCollider2D))]
|
||||
[CanEditMultipleObjects]
|
||||
public class FOVCollider2DEditor : Editor
|
||||
{
|
||||
SerializedProperty length;
|
||||
SerializedProperty nearDistance;
|
||||
SerializedProperty fovAngle;
|
||||
SerializedProperty resolution;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
if (serializedObject == null) return;
|
||||
|
||||
length = serializedObject.FindProperty("Length");
|
||||
nearDistance = serializedObject.FindProperty("NearDistance");
|
||||
fovAngle = serializedObject.FindProperty("FOVAngle");
|
||||
resolution = serializedObject.FindProperty("Resolution");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
EditorGUILayout.PropertyField(length);
|
||||
EditorGUILayout.PropertyField(nearDistance);
|
||||
EditorGUILayout.PropertyField(fovAngle);
|
||||
EditorGUILayout.PropertyField(resolution);
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d78425a8bc834891a4e9419f126e0a83
|
||||
timeCreated: 1491385286
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
40
Assets/SensorToolkit/Sensors/src/Editor/FOVColliderEditor.cs
Normal file
40
Assets/SensorToolkit/Sensors/src/Editor/FOVColliderEditor.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections;
|
||||
|
||||
namespace Micosmo.SensorToolkit
|
||||
{
|
||||
[CustomEditor(typeof(FOVCollider))]
|
||||
[CanEditMultipleObjects]
|
||||
public class FOVColliderEditor : Editor
|
||||
{
|
||||
SerializedProperty length;
|
||||
SerializedProperty nearDistance;
|
||||
SerializedProperty fovAngle;
|
||||
SerializedProperty elevationAngle;
|
||||
SerializedProperty resolution;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
if (serializedObject == null) return;
|
||||
|
||||
length = serializedObject.FindProperty("Length");
|
||||
nearDistance = serializedObject.FindProperty("NearDistance");
|
||||
fovAngle = serializedObject.FindProperty("FOVAngle");
|
||||
elevationAngle = serializedObject.FindProperty("ElevationAngle");
|
||||
resolution = serializedObject.FindProperty("Resolution");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
EditorGUILayout.PropertyField(length);
|
||||
EditorGUILayout.PropertyField(nearDistance);
|
||||
EditorGUILayout.PropertyField(fovAngle);
|
||||
EditorGUILayout.PropertyField(elevationAngle);
|
||||
EditorGUILayout.PropertyField(resolution);
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9ec8d8335d1c46e9b4b17f9e90ec102a
|
||||
timeCreated: 1491114639
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
259
Assets/SensorToolkit/Sensors/src/Editor/LOSSensor2DEditor.cs
Normal file
259
Assets/SensorToolkit/Sensors/src/Editor/LOSSensor2DEditor.cs
Normal file
@@ -0,0 +1,259 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Micosmo.SensorToolkit.Editors {
|
||||
|
||||
[CustomEditor(typeof(LOSSensor2D))]
|
||||
[CanEditMultipleObjects]
|
||||
public class LOSSensor2DEditor : BaseSensorEditor<LOSSensor2D> {
|
||||
|
||||
static string horizAngleMessage =
|
||||
"Angle can't exceed 90 when Point Sampling Method is set to Quality. Recommend changing " +
|
||||
"Point Sampling Method to Fast. In most cases Fast is superior to Quality anyway.";
|
||||
|
||||
SerializedProperty inputSensor;
|
||||
SerializedProperty blocksLineOfSight;
|
||||
SerializedProperty ignoreTriggerColliders;
|
||||
SerializedProperty pulseMode;
|
||||
SerializedProperty pulseUpdateFunction;
|
||||
SerializedProperty pulseInterval;
|
||||
SerializedProperty pointSamplingMethod;
|
||||
SerializedProperty testLOSTargetsOnly;
|
||||
SerializedProperty numberOfRays;
|
||||
SerializedProperty minimumVisibility;
|
||||
|
||||
SerializedProperty movingAverage;
|
||||
SerializedProperty windowSize;
|
||||
|
||||
SerializedProperty limitDistance;
|
||||
SerializedProperty maxDistance;
|
||||
SerializedProperty visibilityByDistance;
|
||||
|
||||
SerializedProperty limitViewAngle;
|
||||
SerializedProperty maxViewAngle;
|
||||
SerializedProperty visibilityByViewAngle;
|
||||
SerializedProperty fovConstraintMethod;
|
||||
|
||||
SerializedProperty onDetected;
|
||||
SerializedProperty onLostDetection;
|
||||
SerializedProperty onSomeDetection;
|
||||
SerializedProperty onNoDetection;
|
||||
|
||||
bool showEvents = false;
|
||||
|
||||
protected override bool canTest { get { return true; } }
|
||||
|
||||
protected override void OnEnable() {
|
||||
base.OnEnable();
|
||||
|
||||
if (serializedObject == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
inputSensor = serializedObject.FindProperty("inputSensor");
|
||||
blocksLineOfSight = serializedObject.FindProperty("BlocksLineOfSight");
|
||||
ignoreTriggerColliders = serializedObject.FindProperty("IgnoreTriggerColliders");
|
||||
pulseMode = serializedObject.FindProperty("pulseRoutine.Mode");
|
||||
pulseUpdateFunction = serializedObject.FindProperty("pulseRoutine.UpdateFunction");
|
||||
pulseInterval = serializedObject.FindProperty("pulseRoutine.Interval");
|
||||
pointSamplingMethod = serializedObject.FindProperty("PointSamplingMethod");
|
||||
testLOSTargetsOnly = serializedObject.FindProperty("TestLOSTargetsOnly");
|
||||
numberOfRays = serializedObject.FindProperty("NumberOfRays");
|
||||
minimumVisibility = serializedObject.FindProperty("MinimumVisibility");
|
||||
|
||||
movingAverage = serializedObject.FindProperty("MovingAverageEnabled");
|
||||
windowSize = serializedObject.FindProperty("MovingAverageWindowSize");
|
||||
|
||||
limitDistance = serializedObject.FindProperty("LimitDistance");
|
||||
maxDistance = serializedObject.FindProperty("MaxDistance");
|
||||
visibilityByDistance = serializedObject.FindProperty("VisibilityByDistance");
|
||||
|
||||
limitViewAngle = serializedObject.FindProperty("LimitViewAngle");
|
||||
maxViewAngle = serializedObject.FindProperty("MaxViewAngle");
|
||||
visibilityByViewAngle = serializedObject.FindProperty("VisibilityByViewAngle");
|
||||
|
||||
fovConstraintMethod = serializedObject.FindProperty("FOVConstraintMethod");
|
||||
|
||||
onDetected = serializedObject.FindProperty("OnDetected");
|
||||
onLostDetection = serializedObject.FindProperty("OnLostDetection");
|
||||
onSomeDetection = serializedObject.FindProperty("OnSomeDetection");
|
||||
onNoDetection = serializedObject.FindProperty("OnNoDetection");
|
||||
|
||||
sensor.ShowRayCastDebug = new HashSet<GameObject>();
|
||||
}
|
||||
|
||||
protected override void OnDisable() {
|
||||
base.OnDisable();
|
||||
sensor.ShowRayCastDebug = null;
|
||||
}
|
||||
|
||||
protected override void InspectorParameters() {
|
||||
EditorGUILayout.PropertyField(inputSensor);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(blocksLineOfSight);
|
||||
EditorGUILayout.PropertyField(ignoreTriggerColliders);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(testLOSTargetsOnly);
|
||||
if (!testLOSTargetsOnly.boolValue) {
|
||||
EditorGUILayout.PropertyField(numberOfRays);
|
||||
EditorGUILayout.PropertyField(pointSamplingMethod);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(movingAverage, new GUIContent("Moving Average"));
|
||||
if (sensor.MovingAverageEnabled) {
|
||||
EditorGUILayout.PropertyField(windowSize, new GUIContent("Window Size"));
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(limitDistance);
|
||||
if (sensor.LimitDistance) {
|
||||
EditorGUILayout.PropertyField(maxDistance);
|
||||
ScalingFunctionProperty(visibilityByDistance);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(limitViewAngle);
|
||||
if (sensor.LimitViewAngle) {
|
||||
EditorGUILayout.PropertyField(maxViewAngle);
|
||||
if (sensor.MaxViewAngle > 90f && sensor.PointSamplingMethod == PointSamplingMethod.Quality) {
|
||||
EditorGUILayout.HelpBox(horizAngleMessage, MessageType.Error);
|
||||
}
|
||||
ScalingFunctionProperty(visibilityByViewAngle);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (sensor.LimitDistance || sensor.LimitViewAngle) {
|
||||
EditorGUILayout.PropertyField(fovConstraintMethod);
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
|
||||
EditorGUILayout.PropertyField(minimumVisibility);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(pulseMode, new GUIContent("Pulse Mode"));
|
||||
if (sensor.PulseMode != PulseRoutine.Modes.Manual) {
|
||||
EditorGUILayout.PropertyField(pulseUpdateFunction);
|
||||
}
|
||||
if (sensor.PulseMode == PulseRoutine.Modes.FixedInterval) {
|
||||
EditorGUILayout.PropertyField(pulseInterval, new GUIContent("Pulse Interval"));
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (showEvents = EditorGUILayout.Foldout(showEvents, "Events")) {
|
||||
EditorGUILayout.PropertyField(onDetected);
|
||||
EditorGUILayout.PropertyField(onLostDetection);
|
||||
EditorGUILayout.PropertyField(onSomeDetection);
|
||||
EditorGUILayout.PropertyField(onNoDetection);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void InspectorDetectedObjects() {
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
VisibilityTable();
|
||||
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
|
||||
void ScalingFunctionProperty(SerializedProperty property) {
|
||||
var mode = property.FindPropertyRelative("Mode");
|
||||
var curve = property.FindPropertyRelative("Curve");
|
||||
EditorGUILayout.PropertyField(mode, new GUIContent(property.displayName));
|
||||
var modeVal = (ScalingMode)mode.intValue;
|
||||
if (modeVal == ScalingMode.Curve) {
|
||||
EditorGUILayout.PropertyField(curve, new GUIContent(" "));
|
||||
}
|
||||
}
|
||||
|
||||
void VisibilityTable() {
|
||||
var headerRow = EditorGUILayout.GetControlRect();
|
||||
VisibilityHeaders(headerRow);
|
||||
|
||||
var losResults = sensor.GetAllResults();
|
||||
losResults.Sort(delegate (ILOSResult r1, ILOSResult r2) {
|
||||
if (r1.Visibility < r2.Visibility) {
|
||||
return 1;
|
||||
} else if (r1.Visibility == r2.Visibility) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
});
|
||||
|
||||
foreach (var result in losResults) {
|
||||
var rect = EditorGUILayout.GetControlRect();
|
||||
VisibilityRow(rect, result);
|
||||
}
|
||||
}
|
||||
|
||||
void VisibilityColumns(Rect rect, out Rect showRaysCol, out Rect visibilityCol, out Rect signalCol) {
|
||||
var width = rect.width;
|
||||
var showRaysWidth = Mathf.Min(50f, width / 3);
|
||||
showRaysCol = new Rect(rect.x, rect.y, showRaysWidth, rect.height);
|
||||
|
||||
width -= showRaysWidth;
|
||||
var visibilityWidth = Mathf.Min(80f, width / 2);
|
||||
visibilityCol = new Rect(showRaysCol.xMax, rect.y, visibilityWidth, rect.height);
|
||||
|
||||
width -= visibilityWidth;
|
||||
signalCol = new Rect(visibilityCol.xMax, rect.y, width, rect.height);
|
||||
}
|
||||
|
||||
void VisibilityHeaders(Rect rect) {
|
||||
Rect rRaysCol, rVisCol, rSigCol;
|
||||
VisibilityColumns(rect, out rRaysCol, out rVisCol, out rSigCol);
|
||||
|
||||
var headerColumnStyle = new GUIStyle(EditorStyles.label) {
|
||||
fontStyle = FontStyle.Bold,
|
||||
padding = new RectOffset(0, 16, 0, 0)
|
||||
};
|
||||
|
||||
if (IsActivePulsable) {
|
||||
GUI.Label(rRaysCol, "Show", new GUIStyle(headerColumnStyle) { alignment = TextAnchor.UpperCenter });
|
||||
}
|
||||
GUI.Label(rVisCol, "Visibility", new GUIStyle(headerColumnStyle) { alignment = TextAnchor.UpperRight });
|
||||
GUI.Label(rSigCol, "Output Signal", new GUIStyle(headerColumnStyle) { alignment = TextAnchor.UpperLeft });
|
||||
}
|
||||
|
||||
void VisibilityRow(Rect rect, ILOSResult losResult) {
|
||||
Rect rRaysCol, rVisCol, rSigCol;
|
||||
VisibilityColumns(rect, out rRaysCol, out rVisCol, out rSigCol);
|
||||
|
||||
rRaysCol.xMin += rRaysCol.width / 2f - 16;
|
||||
rVisCol.xMax -= 16;
|
||||
|
||||
var signal = losResult.OutputSignal;
|
||||
|
||||
if (IsActivePulsable) {
|
||||
var debug = sensor.ShowRayCastDebug.Contains(signal.Object);
|
||||
if (debug = EditorGUI.Toggle(rRaysCol, debug)) {
|
||||
sensor.ShowRayCastDebug.Add(signal.Object);
|
||||
} else {
|
||||
sensor.ShowRayCastDebug.Remove(signal.Object);
|
||||
}
|
||||
}
|
||||
|
||||
var visStyle = new GUIStyle(EditorStyles.label) { alignment = TextAnchor.MiddleRight };
|
||||
if (!losResult.IsVisible) {
|
||||
visStyle.normal.textColor = STPrefs.RedEditorTextColour;
|
||||
}
|
||||
GUI.Label(rVisCol, string.Format("{0:P1}", losResult.Visibility), visStyle);
|
||||
|
||||
SignalField(rSigCol, signal);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 34e4a36656384a5d8e94b847a7505ad3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
265
Assets/SensorToolkit/Sensors/src/Editor/LOSSensorEditor.cs
Normal file
265
Assets/SensorToolkit/Sensors/src/Editor/LOSSensorEditor.cs
Normal file
@@ -0,0 +1,265 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Micosmo.SensorToolkit.Editors {
|
||||
|
||||
[CustomEditor(typeof(LOSSensor))]
|
||||
[CanEditMultipleObjects]
|
||||
public class LOSSensorEditor : BaseSensorEditor<LOSSensor> {
|
||||
|
||||
static string horizAngleMessage =
|
||||
"Angle can't exceed 90 when Point Sampling Method is set to Quality. Recommend changing " +
|
||||
"Point Sampling Method to Fast. In most cases Fast is superior to Quality anyway.";
|
||||
|
||||
SerializedProperty inputSensor;
|
||||
SerializedProperty blocksLineOfSight;
|
||||
SerializedProperty ignoreTriggerColliders;
|
||||
SerializedProperty pulseMode;
|
||||
SerializedProperty pulseUpdateFunction;
|
||||
SerializedProperty pulseInterval;
|
||||
SerializedProperty pointSamplingMethod;
|
||||
SerializedProperty testLOSTargetsOnly;
|
||||
SerializedProperty numberOfRays;
|
||||
SerializedProperty minimumVisibility;
|
||||
|
||||
SerializedProperty movingAverage;
|
||||
SerializedProperty windowSize;
|
||||
|
||||
SerializedProperty limitDistance;
|
||||
SerializedProperty maxDistance;
|
||||
SerializedProperty visibilityByDistance;
|
||||
|
||||
SerializedProperty limitViewAngle;
|
||||
SerializedProperty maxHorizAngle;
|
||||
SerializedProperty visibilityByHorizAngle;
|
||||
SerializedProperty maxVertAngle;
|
||||
SerializedProperty visibilityByVertAngle;
|
||||
SerializedProperty fovConstraintMethod;
|
||||
|
||||
SerializedProperty onDetected;
|
||||
SerializedProperty onLostDetection;
|
||||
SerializedProperty onSomeDetection;
|
||||
SerializedProperty onNoDetection;
|
||||
|
||||
bool showEvents = false;
|
||||
|
||||
protected override bool canTest { get { return true; } }
|
||||
|
||||
protected override void OnEnable() {
|
||||
base.OnEnable();
|
||||
|
||||
if (serializedObject == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
inputSensor = serializedObject.FindProperty("inputSensor");
|
||||
blocksLineOfSight = serializedObject.FindProperty("BlocksLineOfSight");
|
||||
ignoreTriggerColliders = serializedObject.FindProperty("IgnoreTriggerColliders");
|
||||
pulseMode = serializedObject.FindProperty("pulseRoutine.Mode");
|
||||
pulseUpdateFunction = serializedObject.FindProperty("pulseRoutine.UpdateFunction");
|
||||
pulseInterval = serializedObject.FindProperty("pulseRoutine.Interval");
|
||||
pointSamplingMethod = serializedObject.FindProperty("PointSamplingMethod");
|
||||
testLOSTargetsOnly = serializedObject.FindProperty("TestLOSTargetsOnly");
|
||||
numberOfRays = serializedObject.FindProperty("NumberOfRays");
|
||||
minimumVisibility = serializedObject.FindProperty("MinimumVisibility");
|
||||
|
||||
movingAverage = serializedObject.FindProperty("MovingAverageEnabled");
|
||||
windowSize = serializedObject.FindProperty("MovingAverageWindowSize");
|
||||
|
||||
limitDistance = serializedObject.FindProperty("LimitDistance");
|
||||
maxDistance = serializedObject.FindProperty("MaxDistance");
|
||||
visibilityByDistance = serializedObject.FindProperty("VisibilityByDistance");
|
||||
|
||||
limitViewAngle = serializedObject.FindProperty("LimitViewAngle");
|
||||
maxHorizAngle = serializedObject.FindProperty("MaxHorizAngle");
|
||||
visibilityByHorizAngle = serializedObject.FindProperty("VisibilityByHorizAngle");
|
||||
maxVertAngle = serializedObject.FindProperty("MaxVertAngle");
|
||||
visibilityByVertAngle = serializedObject.FindProperty("VisibilityByVertAngle");
|
||||
|
||||
fovConstraintMethod = serializedObject.FindProperty("FOVConstraintMethod");
|
||||
|
||||
onDetected = serializedObject.FindProperty("OnDetected");
|
||||
onLostDetection = serializedObject.FindProperty("OnLostDetection");
|
||||
onSomeDetection = serializedObject.FindProperty("OnSomeDetection");
|
||||
onNoDetection = serializedObject.FindProperty("OnNoDetection");
|
||||
|
||||
sensor.ShowRayCastDebug = new HashSet<GameObject>();
|
||||
}
|
||||
|
||||
protected override void OnDisable() {
|
||||
base.OnDisable();
|
||||
sensor.ShowRayCastDebug = null;
|
||||
}
|
||||
|
||||
protected override void InspectorParameters() {
|
||||
EditorGUILayout.PropertyField(inputSensor);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(blocksLineOfSight);
|
||||
EditorGUILayout.PropertyField(ignoreTriggerColliders);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(testLOSTargetsOnly);
|
||||
if (!testLOSTargetsOnly.boolValue) {
|
||||
EditorGUILayout.PropertyField(numberOfRays);
|
||||
EditorGUILayout.PropertyField(pointSamplingMethod);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(movingAverage, new GUIContent("Moving Average"));
|
||||
if (sensor.MovingAverageEnabled) {
|
||||
EditorGUILayout.PropertyField(windowSize, new GUIContent("Window Size"));
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(limitDistance);
|
||||
if (sensor.LimitDistance) {
|
||||
EditorGUILayout.PropertyField(maxDistance);
|
||||
ScalingFunctionProperty(visibilityByDistance);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(limitViewAngle);
|
||||
if (sensor.LimitViewAngle) {
|
||||
EditorGUILayout.PropertyField(maxHorizAngle);
|
||||
if (sensor.MaxHorizAngle > 90f && sensor.PointSamplingMethod == PointSamplingMethod.Quality) {
|
||||
EditorGUILayout.HelpBox(horizAngleMessage, MessageType.Error);
|
||||
}
|
||||
ScalingFunctionProperty(visibilityByHorizAngle);
|
||||
EditorGUILayout.PropertyField(maxVertAngle);
|
||||
ScalingFunctionProperty(visibilityByVertAngle);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (sensor.LimitDistance || sensor.LimitViewAngle) {
|
||||
EditorGUILayout.PropertyField(fovConstraintMethod);
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
|
||||
EditorGUILayout.PropertyField(minimumVisibility);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(pulseMode, new GUIContent("Pulse Mode"));
|
||||
if (sensor.PulseMode != PulseRoutine.Modes.Manual) {
|
||||
EditorGUILayout.PropertyField(pulseUpdateFunction);
|
||||
}
|
||||
if (sensor.PulseMode == PulseRoutine.Modes.FixedInterval) {
|
||||
EditorGUILayout.PropertyField(pulseInterval, new GUIContent("Pulse Interval"));
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (showEvents = EditorGUILayout.Foldout(showEvents, "Events")) {
|
||||
EditorGUILayout.PropertyField(onDetected);
|
||||
EditorGUILayout.PropertyField(onLostDetection);
|
||||
EditorGUILayout.PropertyField(onSomeDetection);
|
||||
EditorGUILayout.PropertyField(onNoDetection);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void InspectorDetectedObjects() {
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
VisibilityTable();
|
||||
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
|
||||
void ScalingFunctionProperty(SerializedProperty property) {
|
||||
var mode = property.FindPropertyRelative("Mode");
|
||||
var curve = property.FindPropertyRelative("Curve");
|
||||
EditorGUILayout.PropertyField(mode, new GUIContent(property.displayName));
|
||||
var modeVal = (ScalingMode)mode.intValue;
|
||||
if (modeVal == ScalingMode.Curve) {
|
||||
EditorGUILayout.PropertyField(curve, new GUIContent(" "));
|
||||
}
|
||||
}
|
||||
|
||||
void VisibilityTable() {
|
||||
var headerRow = EditorGUILayout.GetControlRect();
|
||||
VisibilityHeaders(headerRow);
|
||||
|
||||
var losResults = sensor.GetAllResults();
|
||||
losResults.Sort(delegate (ILOSResult r1, ILOSResult r2) {
|
||||
if (r1.Visibility < r2.Visibility) {
|
||||
return 1;
|
||||
} else if (r1.Visibility == r2.Visibility) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
});
|
||||
|
||||
foreach (var result in losResults) {
|
||||
var rect = EditorGUILayout.GetControlRect();
|
||||
VisibilityRow(rect, result);
|
||||
}
|
||||
}
|
||||
|
||||
void VisibilityColumns(Rect rect, out Rect showRaysCol, out Rect visibilityCol, out Rect signalCol) {
|
||||
var width = rect.width;
|
||||
var showRaysWidth = Mathf.Min(50f, width / 3);
|
||||
showRaysCol = new Rect(rect.x, rect.y, showRaysWidth, rect.height);
|
||||
|
||||
width -= showRaysWidth;
|
||||
var visibilityWidth = Mathf.Min(80f, width / 2);
|
||||
visibilityCol = new Rect(showRaysCol.xMax, rect.y, visibilityWidth, rect.height);
|
||||
|
||||
width -= visibilityWidth;
|
||||
signalCol = new Rect(visibilityCol.xMax, rect.y, width, rect.height);
|
||||
}
|
||||
|
||||
void VisibilityHeaders(Rect rect) {
|
||||
Rect rRaysCol, rVisCol, rSigCol;
|
||||
VisibilityColumns(rect, out rRaysCol, out rVisCol, out rSigCol);
|
||||
|
||||
var headerColumnStyle = new GUIStyle(EditorStyles.label) {
|
||||
fontStyle = FontStyle.Bold,
|
||||
padding = new RectOffset(0,16,0,0)
|
||||
};
|
||||
|
||||
if (IsActivePulsable) {
|
||||
GUI.Label(rRaysCol, "Show", new GUIStyle(headerColumnStyle) { alignment = TextAnchor.UpperCenter });
|
||||
}
|
||||
GUI.Label(rVisCol, "Visibility", new GUIStyle(headerColumnStyle) { alignment = TextAnchor.UpperRight });
|
||||
GUI.Label(rSigCol, "Output Signal", new GUIStyle(headerColumnStyle) { alignment = TextAnchor.UpperLeft });
|
||||
}
|
||||
|
||||
void VisibilityRow(Rect rect, ILOSResult losResult) {
|
||||
Rect rRaysCol, rVisCol, rSigCol;
|
||||
VisibilityColumns(rect, out rRaysCol, out rVisCol, out rSigCol);
|
||||
|
||||
rRaysCol.xMin += rRaysCol.width / 2f - 16;
|
||||
rVisCol.xMax -= 16;
|
||||
|
||||
var signal = losResult.OutputSignal;
|
||||
|
||||
if (IsActivePulsable) {
|
||||
var debug = sensor.ShowRayCastDebug.Contains(signal.Object);
|
||||
if (debug = EditorGUI.Toggle(rRaysCol, debug)) {
|
||||
sensor.ShowRayCastDebug.Add(signal.Object);
|
||||
} else {
|
||||
sensor.ShowRayCastDebug.Remove(signal.Object);
|
||||
}
|
||||
}
|
||||
|
||||
var visStyle = new GUIStyle(EditorStyles.label) { alignment = TextAnchor.MiddleRight };
|
||||
if (!losResult.IsVisible) {
|
||||
visStyle.normal.textColor = STPrefs.RedEditorTextColour;
|
||||
}
|
||||
GUI.Label(rVisCol, string.Format("{0:P1}", losResult.Visibility), visStyle);
|
||||
|
||||
SignalField(rSigCol, signal);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2a9e2ee831754f5d9ed0f5f5521354ce
|
||||
timeCreated: 1600682780
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Micosmo.SensorToolkit.Editor",
|
||||
"references": [
|
||||
"GUID:b9d61b92870877a459c95c25c7d15074"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1f03c1aaa5578cf4c8db4bdb47ee1a7b
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
40
Assets/SensorToolkit/Sensors/src/Editor/NavMeshMaskDrawer.cs
Normal file
40
Assets/SensorToolkit/Sensors/src/Editor/NavMeshMaskDrawer.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using Micosmo.SensorToolkit;
|
||||
|
||||
namespace Micosmo.SensorToolkit.Editors {
|
||||
|
||||
[CustomPropertyDrawer(typeof(NavMeshMaskAttribute))]
|
||||
public class NavMeshMaskDrawer : PropertyDrawer {
|
||||
public override void OnGUI(Rect position, SerializedProperty serializedProperty, GUIContent label) {
|
||||
|
||||
using (new GUILayout.HorizontalScope()) {
|
||||
|
||||
position = EditorGUI.PrefixLabel(position, label);
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
string[] areaNames = GameObjectUtility.GetNavMeshAreaNames();
|
||||
List<string> completeAreaNames = new List<string>();
|
||||
|
||||
foreach (string name in areaNames) {
|
||||
var id = GameObjectUtility.GetNavMeshAreaFromName(name);
|
||||
while (id >= completeAreaNames.Count) {
|
||||
completeAreaNames.Add("");
|
||||
}
|
||||
completeAreaNames[id] = name;
|
||||
}
|
||||
|
||||
int mask = serializedProperty.intValue;
|
||||
|
||||
mask = EditorGUI.MaskField(position, mask, completeAreaNames.ToArray());
|
||||
if (EditorGUI.EndChangeCheck()) {
|
||||
serializedProperty.intValue = mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9e6ba988a66d424ea6597d3e437d3400
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,96 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Micosmo.SensorToolkit.Editors {
|
||||
|
||||
[CustomEditor(typeof(NavMeshSensor))]
|
||||
[CanEditMultipleObjects]
|
||||
public class NavMeshSensorEditor : BasePulsableEditor<NavMeshSensor> {
|
||||
SerializedProperty test;
|
||||
SerializedProperty ray;
|
||||
SerializedProperty sphere;
|
||||
SerializedProperty areaMask;
|
||||
SerializedProperty pulseMode;
|
||||
SerializedProperty pulseInterval;
|
||||
SerializedProperty onObstructed;
|
||||
SerializedProperty onClear;
|
||||
|
||||
bool showEvents = false;
|
||||
|
||||
protected NavMeshSensor sensor;
|
||||
|
||||
protected override bool canTest => true;
|
||||
|
||||
protected override void OnEnable() {
|
||||
base.OnEnable();
|
||||
|
||||
if (serializedObject == null) return;
|
||||
sensor = serializedObject.targetObject as NavMeshSensor;
|
||||
|
||||
test = serializedObject.FindProperty("Test");
|
||||
ray = serializedObject.FindProperty("Ray");
|
||||
sphere = serializedObject.FindProperty("Sphere");
|
||||
areaMask = serializedObject.FindProperty("AreaMask");
|
||||
pulseMode = serializedObject.FindProperty("pulseRoutine.Mode");
|
||||
pulseInterval = serializedObject.FindProperty("pulseRoutine.Interval");
|
||||
onObstructed = serializedObject.FindProperty("onObstruction");
|
||||
onClear = serializedObject.FindProperty("onClear");
|
||||
}
|
||||
|
||||
protected override void OnPulsableGUI() {
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
EditorGUILayout.PropertyField(test);
|
||||
if (sensor.Test == NavMeshSensor.TestType.Ray) {
|
||||
EditorUtils.InlinePropertyField(ray);
|
||||
} else if (sensor.Test == NavMeshSensor.TestType.Sample) {
|
||||
EditorUtils.InlinePropertyField(sphere);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(areaMask);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(pulseMode, new GUIContent("Pulse Mode"));
|
||||
if (sensor.PulseMode == PulseRoutine.Modes.FixedInterval) {
|
||||
EditorGUILayout.PropertyField(pulseInterval, new GUIContent("Pulse Interval"));
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (showEvents = EditorGUILayout.Foldout(showEvents, "Events")) {
|
||||
EditorGUILayout.PropertyField(onObstructed);
|
||||
EditorGUILayout.PropertyField(onClear);
|
||||
}
|
||||
|
||||
if (EditorGUI.EndChangeCheck()) {
|
||||
EditorState.StopAllTesting();
|
||||
}
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
|
||||
if (showDetections) {
|
||||
EditorUtils.HorizontalLine(new Color(0.5f, 0.5f, 0.5f, 1));
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
InspectorDetectedObjects();
|
||||
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
}
|
||||
|
||||
void InspectorDetectedObjects() {
|
||||
if (!sensor.IsObstructed) return;
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("Ray is Obstructed", new GUIStyle() { fontStyle = FontStyle.Bold, normal = new GUIStyleState() { textColor = STPrefs.RedEditorTextColour } });
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 152d28158e8a4f6c8472d50e383242fc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
107
Assets/SensorToolkit/Sensors/src/Editor/RangeSensor2DEditor.cs
Normal file
107
Assets/SensorToolkit/Sensors/src/Editor/RangeSensor2DEditor.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Linq;
|
||||
|
||||
namespace Micosmo.SensorToolkit.Editors {
|
||||
[CustomEditor(typeof(RangeSensor2D))]
|
||||
[CanEditMultipleObjects]
|
||||
public class RangeSensor2DEditor : BaseSensorEditor<RangeSensor2D> {
|
||||
SerializedProperty shape;
|
||||
SerializedProperty circle;
|
||||
SerializedProperty box;
|
||||
SerializedProperty capsule;
|
||||
SerializedProperty ignoreList;
|
||||
SerializedProperty tagFilterEnabled;
|
||||
SerializedProperty tagFilter;
|
||||
SerializedProperty detectsOnLayers;
|
||||
SerializedProperty pulseMode;
|
||||
SerializedProperty pulseUpdateFunction;
|
||||
SerializedProperty pulseInterval;
|
||||
SerializedProperty detectionMode;
|
||||
SerializedProperty ignoreTriggerColliders;
|
||||
SerializedProperty signalProcessors;
|
||||
SerializedProperty onDetected;
|
||||
SerializedProperty onLostDetection;
|
||||
SerializedProperty onSomeDetection;
|
||||
SerializedProperty onNoDetection;
|
||||
|
||||
bool showEvents = false;
|
||||
|
||||
protected override bool canTest { get { return true; } }
|
||||
|
||||
protected override void OnEnable() {
|
||||
base.OnEnable();
|
||||
|
||||
if (serializedObject == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
shape = serializedObject.FindProperty("Shape");
|
||||
circle = serializedObject.FindProperty("Circle");
|
||||
box = serializedObject.FindProperty("Box");
|
||||
capsule = serializedObject.FindProperty("Capsule");
|
||||
ignoreList = serializedObject.FindProperty("signalFilter.IgnoreList");
|
||||
tagFilterEnabled = serializedObject.FindProperty("signalFilter.EnableTagFilter");
|
||||
tagFilter = serializedObject.FindProperty("signalFilter.AllowedTags");
|
||||
detectsOnLayers = serializedObject.FindProperty("DetectsOnLayers");
|
||||
pulseMode = serializedObject.FindProperty("pulseRoutine.Mode");
|
||||
pulseUpdateFunction = serializedObject.FindProperty("pulseRoutine.UpdateFunction");
|
||||
pulseInterval = serializedObject.FindProperty("pulseRoutine.Interval");
|
||||
detectionMode = serializedObject.FindProperty("DetectionMode");
|
||||
ignoreTriggerColliders = serializedObject.FindProperty("IgnoreTriggerColliders");
|
||||
signalProcessors = serializedObject.FindProperty("signalProcessors");
|
||||
onDetected = serializedObject.FindProperty("OnDetected");
|
||||
onLostDetection = serializedObject.FindProperty("OnLostDetection");
|
||||
onSomeDetection = serializedObject.FindProperty("OnSomeDetection");
|
||||
onNoDetection = serializedObject.FindProperty("OnNoDetection");
|
||||
}
|
||||
|
||||
protected override void InspectorParameters() {
|
||||
EditorGUILayout.PropertyField(shape);
|
||||
if (sensor.Shape == RangeSensor2D.Shapes.Circle) {
|
||||
EditorUtils.InlinePropertyField(circle);
|
||||
} else if (sensor.Shape == RangeSensor2D.Shapes.Box) {
|
||||
EditorUtils.InlinePropertyField(box);
|
||||
} else if (sensor.Shape == RangeSensor2D.Shapes.Capsule) {
|
||||
EditorUtils.InlinePropertyField(capsule);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(ignoreList, true);
|
||||
EditorGUILayout.PropertyField(tagFilterEnabled);
|
||||
if (tagFilterEnabled.boolValue) {
|
||||
EditorGUILayout.PropertyField(tagFilter, true);
|
||||
}
|
||||
EditorGUILayout.PropertyField(detectsOnLayers);
|
||||
EditorGUILayout.PropertyField(detectionMode);
|
||||
EditorGUILayout.PropertyField(ignoreTriggerColliders);
|
||||
EditorGUILayout.PropertyField(signalProcessors, new GUIContent("Signal Processors"));
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(pulseMode, new GUIContent("Pulse Mode"));
|
||||
if (sensor.PulseMode != PulseRoutine.Modes.Manual) {
|
||||
EditorGUILayout.PropertyField(pulseUpdateFunction);
|
||||
}
|
||||
if (sensor.PulseMode == PulseRoutine.Modes.FixedInterval) {
|
||||
EditorGUILayout.PropertyField(pulseInterval, new GUIContent("Pulse Interval"));
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (showEvents = EditorGUILayout.Foldout(showEvents, "Events")) {
|
||||
EditorGUILayout.PropertyField(onDetected);
|
||||
EditorGUILayout.PropertyField(onLostDetection);
|
||||
EditorGUILayout.PropertyField(onSomeDetection);
|
||||
EditorGUILayout.PropertyField(onNoDetection);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
BufferSizeInfo(sensor.CurrentBufferSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 550d906f63c0467f9e9968050a56c67d
|
||||
timeCreated: 1491309471
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
107
Assets/SensorToolkit/Sensors/src/Editor/RangeSensorEditor.cs
Normal file
107
Assets/SensorToolkit/Sensors/src/Editor/RangeSensorEditor.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Linq;
|
||||
|
||||
namespace Micosmo.SensorToolkit.Editors {
|
||||
[CustomEditor(typeof(RangeSensor))]
|
||||
[CanEditMultipleObjects]
|
||||
public class RangeSensorEditor : BaseSensorEditor<RangeSensor> {
|
||||
SerializedProperty shape;
|
||||
SerializedProperty sphere;
|
||||
SerializedProperty box;
|
||||
SerializedProperty capsule;
|
||||
SerializedProperty ignoreList;
|
||||
SerializedProperty tagFilterEnabled;
|
||||
SerializedProperty tagFilter;
|
||||
SerializedProperty detectsOnLayers;
|
||||
SerializedProperty pulseMode;
|
||||
SerializedProperty pulseUpdateFunction;
|
||||
SerializedProperty pulseInterval;
|
||||
SerializedProperty detectionMode;
|
||||
SerializedProperty ignoreTriggerColliders;
|
||||
SerializedProperty signalProcessors;
|
||||
SerializedProperty onDetected;
|
||||
SerializedProperty onLostDetection;
|
||||
SerializedProperty onSomeDetection;
|
||||
SerializedProperty onNoDetection;
|
||||
|
||||
bool showEvents = false;
|
||||
|
||||
protected override bool canTest { get { return true; } }
|
||||
|
||||
protected override void OnEnable() {
|
||||
base.OnEnable();
|
||||
|
||||
if (serializedObject == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
shape = serializedObject.FindProperty("Shape");
|
||||
sphere = serializedObject.FindProperty("Sphere");
|
||||
box = serializedObject.FindProperty("Box");
|
||||
capsule = serializedObject.FindProperty("Capsule");
|
||||
ignoreList = serializedObject.FindProperty("signalFilter.IgnoreList");
|
||||
tagFilterEnabled = serializedObject.FindProperty("signalFilter.EnableTagFilter");
|
||||
tagFilter = serializedObject.FindProperty("signalFilter.AllowedTags");
|
||||
detectsOnLayers = serializedObject.FindProperty("DetectsOnLayers");
|
||||
pulseMode = serializedObject.FindProperty("pulseRoutine.Mode");
|
||||
pulseUpdateFunction = serializedObject.FindProperty("pulseRoutine.UpdateFunction");
|
||||
pulseInterval = serializedObject.FindProperty("pulseRoutine.Interval");
|
||||
detectionMode = serializedObject.FindProperty("DetectionMode");
|
||||
ignoreTriggerColliders = serializedObject.FindProperty("IgnoreTriggerColliders");
|
||||
signalProcessors = serializedObject.FindProperty("signalProcessors");
|
||||
onDetected = serializedObject.FindProperty("OnDetected");
|
||||
onLostDetection = serializedObject.FindProperty("OnLostDetection");
|
||||
onSomeDetection = serializedObject.FindProperty("OnSomeDetection");
|
||||
onNoDetection = serializedObject.FindProperty("OnNoDetection");
|
||||
}
|
||||
|
||||
protected override void InspectorParameters() {
|
||||
EditorGUILayout.PropertyField(shape);
|
||||
if (sensor.Shape == RangeSensor.Shapes.Sphere) {
|
||||
EditorUtils.InlinePropertyField(sphere);
|
||||
} else if (sensor.Shape == RangeSensor.Shapes.Box) {
|
||||
EditorUtils.InlinePropertyField(box);
|
||||
} else if (sensor.Shape == RangeSensor.Shapes.Capsule) {
|
||||
EditorUtils.InlinePropertyField(capsule);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(ignoreList, true);
|
||||
EditorGUILayout.PropertyField(tagFilterEnabled);
|
||||
if (tagFilterEnabled.boolValue) {
|
||||
EditorGUILayout.PropertyField(tagFilter, true);
|
||||
}
|
||||
EditorGUILayout.PropertyField(detectsOnLayers);
|
||||
EditorGUILayout.PropertyField(detectionMode);
|
||||
EditorGUILayout.PropertyField(ignoreTriggerColliders);
|
||||
EditorGUILayout.PropertyField(signalProcessors, new GUIContent("Signal Processors"));
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(pulseMode, new GUIContent("Pulse Mode"));
|
||||
if (sensor.PulseMode != PulseRoutine.Modes.Manual) {
|
||||
EditorGUILayout.PropertyField(pulseUpdateFunction);
|
||||
}
|
||||
if (sensor.PulseMode == PulseRoutine.Modes.FixedInterval) {
|
||||
EditorGUILayout.PropertyField(pulseInterval, new GUIContent("Pulse Interval"));
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (showEvents = EditorGUILayout.Foldout(showEvents, "Events")) {
|
||||
EditorGUILayout.PropertyField(onDetected);
|
||||
EditorGUILayout.PropertyField(onLostDetection);
|
||||
EditorGUILayout.PropertyField(onSomeDetection);
|
||||
EditorGUILayout.PropertyField(onNoDetection);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
BufferSizeInfo(sensor.CurrentBufferSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3554b5d938da480dafdfd6872234ffe6
|
||||
timeCreated: 1489796111
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
149
Assets/SensorToolkit/Sensors/src/Editor/RaySensor2DEditor.cs
Normal file
149
Assets/SensorToolkit/Sensors/src/Editor/RaySensor2DEditor.cs
Normal file
@@ -0,0 +1,149 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Micosmo.SensorToolkit.Editors {
|
||||
[CustomEditor(typeof(RaySensor2D))]
|
||||
[CanEditMultipleObjects]
|
||||
public class RaySensor2DEditor : BaseSensorEditor<RaySensor2D> {
|
||||
SerializedProperty length;
|
||||
SerializedProperty shape;
|
||||
SerializedProperty circle;
|
||||
SerializedProperty box;
|
||||
SerializedProperty capsule;
|
||||
SerializedProperty ignoreList;
|
||||
SerializedProperty tagFilterEnabled;
|
||||
SerializedProperty tagFilter;
|
||||
SerializedProperty detectsOnLayers;
|
||||
SerializedProperty detectionMode;
|
||||
SerializedProperty ignoreTriggerColliders;
|
||||
SerializedProperty signalProcessors;
|
||||
SerializedProperty minimumSlopeAngle;
|
||||
SerializedProperty slopeUpDirection;
|
||||
SerializedProperty obstructedByLayers;
|
||||
SerializedProperty direction;
|
||||
SerializedProperty worldSpace;
|
||||
SerializedProperty pulseMode;
|
||||
SerializedProperty pulseUpdateFunction;
|
||||
SerializedProperty pulseInterval;
|
||||
SerializedProperty onDetected;
|
||||
SerializedProperty onLostDetection;
|
||||
SerializedProperty onSomeDetection;
|
||||
SerializedProperty onNoDetection;
|
||||
SerializedProperty onObstructed;
|
||||
SerializedProperty onClear;
|
||||
|
||||
bool showEvents = false;
|
||||
|
||||
protected override bool canTest { get { return true; } }
|
||||
|
||||
protected override void OnEnable() {
|
||||
base.OnEnable();
|
||||
|
||||
if (serializedObject == null) return;
|
||||
|
||||
length = serializedObject.FindProperty("Length");
|
||||
shape = serializedObject.FindProperty("Shape");
|
||||
circle = serializedObject.FindProperty("Circle");
|
||||
box = serializedObject.FindProperty("Box");
|
||||
capsule = serializedObject.FindProperty("Capsule");
|
||||
ignoreList = serializedObject.FindProperty("signalFilter.IgnoreList");
|
||||
tagFilterEnabled = serializedObject.FindProperty("signalFilter.EnableTagFilter");
|
||||
tagFilter = serializedObject.FindProperty("signalFilter.AllowedTags");
|
||||
detectsOnLayers = serializedObject.FindProperty("DetectsOnLayers");
|
||||
detectionMode = serializedObject.FindProperty("DetectionMode");
|
||||
ignoreTriggerColliders = serializedObject.FindProperty("IgnoreTriggerColliders");
|
||||
signalProcessors = serializedObject.FindProperty("signalProcessors");
|
||||
minimumSlopeAngle = serializedObject.FindProperty("MinimumSlopeAngle");
|
||||
slopeUpDirection = serializedObject.FindProperty("SlopeUpDirection");
|
||||
obstructedByLayers = serializedObject.FindProperty("ObstructedByLayers");
|
||||
direction = serializedObject.FindProperty("Direction");
|
||||
worldSpace = serializedObject.FindProperty("WorldSpace");
|
||||
pulseMode = serializedObject.FindProperty("pulseRoutine.Mode");
|
||||
pulseUpdateFunction = serializedObject.FindProperty("pulseRoutine.UpdateFunction");
|
||||
pulseInterval = serializedObject.FindProperty("pulseRoutine.Interval");
|
||||
onDetected = serializedObject.FindProperty("OnDetected");
|
||||
onLostDetection = serializedObject.FindProperty("OnLostDetection");
|
||||
onSomeDetection = serializedObject.FindProperty("OnSomeDetection");
|
||||
onNoDetection = serializedObject.FindProperty("OnNoDetection");
|
||||
onObstructed = serializedObject.FindProperty("onObstruction");
|
||||
onClear = serializedObject.FindProperty("onClear");
|
||||
}
|
||||
|
||||
protected override void InspectorParameters() {
|
||||
EditorGUILayout.PropertyField(shape);
|
||||
if (sensor.Shape == RaySensor2D.CastShapeType.Circle) {
|
||||
EditorUtils.InlinePropertyField(circle);
|
||||
} else if (sensor.Shape == RaySensor2D.CastShapeType.Box) {
|
||||
EditorUtils.InlinePropertyField(box);
|
||||
} else if (sensor.Shape == RaySensor2D.CastShapeType.Capsule) {
|
||||
EditorUtils.InlinePropertyField(capsule);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(length);
|
||||
EditorGUILayout.PropertyField(direction);
|
||||
EditorGUILayout.PropertyField(worldSpace);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(ignoreList, true);
|
||||
EditorGUILayout.PropertyField(tagFilterEnabled);
|
||||
if (tagFilterEnabled.boolValue) {
|
||||
EditorGUILayout.PropertyField(tagFilter, true);
|
||||
}
|
||||
EditorGUILayout.PropertyField(detectsOnLayers);
|
||||
EditorGUILayout.PropertyField(detectionMode);
|
||||
EditorGUILayout.PropertyField(ignoreTriggerColliders);
|
||||
EditorGUILayout.PropertyField(signalProcessors, new GUIContent("Signal Processors"));
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(minimumSlopeAngle);
|
||||
if (sensor.MinimumSlopeAngle > 0f) {
|
||||
EditorGUILayout.PropertyField(slopeUpDirection);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(obstructedByLayers);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(pulseMode, new GUIContent("Pulse Mode"));
|
||||
if (sensor.PulseMode != PulseRoutine.Modes.Manual) {
|
||||
EditorGUILayout.PropertyField(pulseUpdateFunction);
|
||||
}
|
||||
if (sensor.PulseMode == PulseRoutine.Modes.FixedInterval) {
|
||||
EditorGUILayout.PropertyField(pulseInterval, new GUIContent("Pulse Interval"));
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (showEvents = EditorGUILayout.Foldout(showEvents, "Events")) {
|
||||
EditorGUILayout.PropertyField(onDetected);
|
||||
EditorGUILayout.PropertyField(onLostDetection);
|
||||
EditorGUILayout.PropertyField(onSomeDetection);
|
||||
EditorGUILayout.PropertyField(onNoDetection);
|
||||
EditorGUILayout.PropertyField(onObstructed);
|
||||
EditorGUILayout.PropertyField(onClear);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
BufferSizeInfo(sensor.CurrentBufferSize);
|
||||
}
|
||||
|
||||
protected override void InspectorDetectedObjects() {
|
||||
base.InspectorDetectedObjects();
|
||||
|
||||
if (!sensor.IsObstructed) return;
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("Ray is Obstructed", new GUIStyle() { fontStyle = FontStyle.Bold, normal = new GUIStyleState() { textColor = STPrefs.RedEditorTextColour } });
|
||||
DetectedObjectFieldLayout(sensor.GetObstructionRayHit().GameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dae438f4c18e49eaa664e023208bac2a
|
||||
timeCreated: 1491307096
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
150
Assets/SensorToolkit/Sensors/src/Editor/RaySensorEditor.cs
Normal file
150
Assets/SensorToolkit/Sensors/src/Editor/RaySensorEditor.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Micosmo.SensorToolkit.Editors
|
||||
{
|
||||
[CustomEditor(typeof(RaySensor))]
|
||||
[CanEditMultipleObjects]
|
||||
public class RaySensorEditor : BaseSensorEditor<RaySensor> {
|
||||
SerializedProperty length;
|
||||
SerializedProperty shape;
|
||||
SerializedProperty sphere;
|
||||
SerializedProperty box;
|
||||
SerializedProperty capsule;
|
||||
SerializedProperty ignoreList;
|
||||
SerializedProperty tagFilterEnabled;
|
||||
SerializedProperty tagFilter;
|
||||
SerializedProperty detectsOnLayers;
|
||||
SerializedProperty detectionMode;
|
||||
SerializedProperty ignoreTriggerColliders;
|
||||
SerializedProperty signalProcessors;
|
||||
SerializedProperty minimumSlopeAngle;
|
||||
SerializedProperty slopeUpDirection;
|
||||
SerializedProperty obstructedByLayers;
|
||||
SerializedProperty direction;
|
||||
SerializedProperty worldSpace;
|
||||
SerializedProperty pulseMode;
|
||||
SerializedProperty pulseUpdateFunction;
|
||||
SerializedProperty pulseInterval;
|
||||
SerializedProperty onDetected;
|
||||
SerializedProperty onLostDetection;
|
||||
SerializedProperty onSomeDetection;
|
||||
SerializedProperty onNoDetection;
|
||||
SerializedProperty onObstructed;
|
||||
SerializedProperty onClear;
|
||||
|
||||
bool showEvents = false;
|
||||
|
||||
protected override bool canTest { get { return true; } }
|
||||
|
||||
protected override void OnEnable() {
|
||||
base.OnEnable();
|
||||
|
||||
if (serializedObject == null) return;
|
||||
|
||||
length = serializedObject.FindProperty("Length");
|
||||
shape = serializedObject.FindProperty("Shape");
|
||||
sphere = serializedObject.FindProperty("Sphere");
|
||||
box = serializedObject.FindProperty("Box");
|
||||
capsule = serializedObject.FindProperty("Capsule");
|
||||
ignoreList = serializedObject.FindProperty("signalFilter.IgnoreList");
|
||||
tagFilterEnabled = serializedObject.FindProperty("signalFilter.EnableTagFilter");
|
||||
tagFilter = serializedObject.FindProperty("signalFilter.AllowedTags");
|
||||
detectsOnLayers = serializedObject.FindProperty("DetectsOnLayers");
|
||||
detectionMode = serializedObject.FindProperty("DetectionMode");
|
||||
ignoreTriggerColliders = serializedObject.FindProperty("IgnoreTriggerColliders");
|
||||
signalProcessors = serializedObject.FindProperty("signalProcessors");
|
||||
minimumSlopeAngle = serializedObject.FindProperty("MinimumSlopeAngle");
|
||||
slopeUpDirection = serializedObject.FindProperty("SlopeUpDirection");
|
||||
obstructedByLayers = serializedObject.FindProperty("ObstructedByLayers");
|
||||
direction = serializedObject.FindProperty("Direction");
|
||||
worldSpace = serializedObject.FindProperty("WorldSpace");
|
||||
pulseMode = serializedObject.FindProperty("pulseRoutine.Mode");
|
||||
pulseUpdateFunction = serializedObject.FindProperty("pulseRoutine.UpdateFunction");
|
||||
pulseInterval = serializedObject.FindProperty("pulseRoutine.Interval");
|
||||
onDetected = serializedObject.FindProperty("OnDetected");
|
||||
onLostDetection = serializedObject.FindProperty("OnLostDetection");
|
||||
onSomeDetection = serializedObject.FindProperty("OnSomeDetection");
|
||||
onNoDetection = serializedObject.FindProperty("OnNoDetection");
|
||||
onObstructed = serializedObject.FindProperty("onObstruction");
|
||||
onClear = serializedObject.FindProperty("onClear");
|
||||
}
|
||||
|
||||
protected override void InspectorParameters() {
|
||||
EditorGUILayout.PropertyField(shape);
|
||||
if (sensor.Shape == RaySensor.CastShapeType.Sphere) {
|
||||
EditorUtils.InlinePropertyField(sphere);
|
||||
} else if (sensor.Shape == RaySensor.CastShapeType.Box) {
|
||||
EditorUtils.InlinePropertyField(box);
|
||||
} else if (sensor.Shape == RaySensor.CastShapeType.Capsule) {
|
||||
EditorUtils.InlinePropertyField(capsule);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(length);
|
||||
EditorGUILayout.PropertyField(direction);
|
||||
EditorGUILayout.PropertyField(worldSpace);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(ignoreList, true);
|
||||
EditorGUILayout.PropertyField(tagFilterEnabled);
|
||||
if (tagFilterEnabled.boolValue) {
|
||||
EditorGUILayout.PropertyField(tagFilter, true);
|
||||
}
|
||||
EditorGUILayout.PropertyField(detectsOnLayers);
|
||||
EditorGUILayout.PropertyField(detectionMode);
|
||||
EditorGUILayout.PropertyField(ignoreTriggerColliders);
|
||||
EditorGUILayout.PropertyField(signalProcessors, new GUIContent("Signal Processors"));
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(minimumSlopeAngle);
|
||||
if (sensor.MinimumSlopeAngle > 0f) {
|
||||
EditorGUILayout.PropertyField(slopeUpDirection);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(obstructedByLayers);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(pulseMode, new GUIContent("Pulse Mode"));
|
||||
if (sensor.PulseMode != PulseRoutine.Modes.Manual) {
|
||||
EditorGUILayout.PropertyField(pulseUpdateFunction);
|
||||
}
|
||||
if (sensor.PulseMode == PulseRoutine.Modes.FixedInterval) {
|
||||
EditorGUILayout.PropertyField(pulseInterval, new GUIContent("Pulse Interval"));
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (showEvents = EditorGUILayout.Foldout(showEvents, "Events")) {
|
||||
EditorGUILayout.PropertyField(onDetected);
|
||||
EditorGUILayout.PropertyField(onLostDetection);
|
||||
EditorGUILayout.PropertyField(onSomeDetection);
|
||||
EditorGUILayout.PropertyField(onNoDetection);
|
||||
EditorGUILayout.PropertyField(onObstructed);
|
||||
EditorGUILayout.PropertyField(onClear);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
BufferSizeInfo(sensor.CurrentBufferSize);
|
||||
}
|
||||
|
||||
protected override void InspectorDetectedObjects() {
|
||||
base.InspectorDetectedObjects();
|
||||
|
||||
if (!sensor.IsObstructed) return;
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("Ray is Obstructed", new GUIStyle() { fontStyle = FontStyle.Bold, normal = new GUIStyleState() { textColor = STPrefs.RedEditorTextColour } });
|
||||
DetectedObjectFieldLayout(sensor.GetObstructionRayHit().GameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9ccb9305913f430a9726bd254afd4c15
|
||||
timeCreated: 1490954162
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
83
Assets/SensorToolkit/Sensors/src/Editor/SteerSeekDrawer.cs
Normal file
83
Assets/SensorToolkit/Sensors/src/Editor/SteerSeekDrawer.cs
Normal file
@@ -0,0 +1,83 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Micosmo.SensorToolkit.Editors {
|
||||
[CustomPropertyDrawer(typeof(SteerSeek))]
|
||||
public class SteerSeekDrawer : PropertyDrawer {
|
||||
|
||||
bool foldout = false;
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
|
||||
EditorGUI.BeginProperty(position, label, property);
|
||||
|
||||
var foldoutRect = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
|
||||
foldout = EditorGUI.Foldout(foldoutRect, foldout, label, true);
|
||||
|
||||
if (!foldout) {
|
||||
return;
|
||||
}
|
||||
// Indent child fields
|
||||
EditorGUI.indentLevel++;
|
||||
position.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
|
||||
|
||||
// Find properties
|
||||
var seekMode = property.FindPropertyRelative("SeekMode");
|
||||
var seekPosition = property.FindPropertyRelative("SeekPosition");
|
||||
var seekDirection = property.FindPropertyRelative("SeekDirection");
|
||||
var arriveDistanceThreshold = property.FindPropertyRelative("ArriveDistanceThreshold");
|
||||
var stoppingDistance = property.FindPropertyRelative("StoppingDistance");
|
||||
|
||||
var arriveDistanceThresholdPosition = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
|
||||
EditorGUI.PropertyField(arriveDistanceThresholdPosition, arriveDistanceThreshold);
|
||||
position.y += arriveDistanceThresholdPosition.height + EditorGUIUtility.standardVerticalSpacing;
|
||||
|
||||
var stoppingDistancePosition = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
|
||||
EditorGUI.PropertyField(stoppingDistancePosition, stoppingDistance);
|
||||
position.y += stoppingDistancePosition.height + EditorGUIUtility.standardVerticalSpacing;
|
||||
|
||||
var seekModePosition = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
|
||||
EditorGUI.PropertyField(seekModePosition, seekMode);
|
||||
position.y += seekModePosition.height + EditorGUIUtility.standardVerticalSpacing;
|
||||
|
||||
// Conditionally draw SeekPosition field based on SeekMode value
|
||||
if (seekMode.enumValueIndex == (int)SeekMode.Position) {
|
||||
var seekPositionPosition = new Rect(position.x, position.y, position.width, EditorGUI.GetPropertyHeight(seekPosition, label, true));
|
||||
EditorGUI.PropertyField(seekPositionPosition, seekPosition, true);
|
||||
position.y += seekPositionPosition.height + EditorGUIUtility.standardVerticalSpacing;
|
||||
} else if (seekMode.enumValueIndex == (int)SeekMode.Direction) {
|
||||
var seekDirectionPosition = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
|
||||
EditorGUI.PropertyField(seekDirectionPosition, seekDirection);
|
||||
position.y += seekDirectionPosition.height + EditorGUIUtility.standardVerticalSpacing;
|
||||
}
|
||||
|
||||
EditorGUI.indentLevel--;
|
||||
|
||||
EditorGUI.EndProperty();
|
||||
}
|
||||
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
|
||||
if (!foldout) {
|
||||
return EditorGUIUtility.singleLineHeight;
|
||||
}
|
||||
|
||||
var height = EditorGUIUtility.singleLineHeight;
|
||||
|
||||
var seekMode = property.FindPropertyRelative("SeekMode");
|
||||
height += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; // seekMode
|
||||
|
||||
if (seekMode.enumValueIndex == (int)SeekMode.Position) {
|
||||
var seekPosition = property.FindPropertyRelative("SeekPosition");
|
||||
height += EditorGUI.GetPropertyHeight(seekPosition, true) + EditorGUIUtility.standardVerticalSpacing;
|
||||
} else if (seekMode.enumValueIndex == (int)SeekMode.Direction) {
|
||||
height += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; // seekDirection
|
||||
}
|
||||
|
||||
height += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; // ArriveDistanceThreshold
|
||||
height += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; // StoppingDistance
|
||||
|
||||
return height;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bc62f67ad14e1664780363e945ae56e9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,147 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections;
|
||||
|
||||
namespace Micosmo.SensorToolkit.Editors {
|
||||
|
||||
[CustomEditor(typeof(SteeringSensor2D))]
|
||||
[CanEditMultipleObjects]
|
||||
public class SteeringSensor2DEditor : BasePulsableEditor<SteeringSensor2D> {
|
||||
SerializedProperty resolution;
|
||||
SerializedProperty seek;
|
||||
SerializedProperty interest;
|
||||
SerializedProperty danger;
|
||||
SerializedProperty vo;
|
||||
SerializedProperty decision;
|
||||
SerializedProperty pulseMode;
|
||||
SerializedProperty pulseUpdateFunction;
|
||||
SerializedProperty pulseInterval;
|
||||
SerializedProperty locomotionMode;
|
||||
SerializedProperty rigidBody;
|
||||
SerializedProperty locomotion;
|
||||
|
||||
protected SteeringSensor2D sensor;
|
||||
|
||||
protected override bool canTest => true;
|
||||
|
||||
protected override void OnEnable() {
|
||||
base.OnEnable();
|
||||
|
||||
if (serializedObject == null) return;
|
||||
sensor = serializedObject.targetObject as SteeringSensor2D;
|
||||
|
||||
resolution = serializedObject.FindProperty("resolution");
|
||||
seek = serializedObject.FindProperty("seek");
|
||||
interest = serializedObject.FindProperty("interest");
|
||||
danger = serializedObject.FindProperty("danger");
|
||||
vo = serializedObject.FindProperty("velocity");
|
||||
decision = serializedObject.FindProperty("decision");
|
||||
pulseMode = serializedObject.FindProperty("pulseRoutine.Mode");
|
||||
pulseUpdateFunction = serializedObject.FindProperty("pulseRoutine.UpdateFunction");
|
||||
pulseInterval = serializedObject.FindProperty("pulseRoutine.Interval");
|
||||
locomotionMode = serializedObject.FindProperty("LocomotionMode");
|
||||
rigidBody = serializedObject.FindProperty("RigidBody");
|
||||
locomotion = serializedObject.FindProperty("locomotion");
|
||||
}
|
||||
|
||||
protected override void OnPulsableGUI() {
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
EditorGUILayout.PropertyField(resolution);
|
||||
EditorGUILayout.PropertyField(pulseMode, new GUIContent("Pulse Mode"));
|
||||
if (sensor.PulseMode != PulseRoutine.Modes.Manual) {
|
||||
EditorGUILayout.PropertyField(pulseUpdateFunction);
|
||||
}
|
||||
if (sensor.PulseMode == PulseRoutine.Modes.FixedInterval) {
|
||||
EditorGUILayout.PropertyField(pulseInterval, new GUIContent("Pulse Interval"));
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(seek);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
SteeringSensor2D.ShowInterestGizmos = SteerSystemLayout("Interest", interest, SteeringSensor2D.ShowInterestGizmos);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
SteeringSensor2D.ShowDangerGizmos = SteerSystemLayout("Danger", danger, SteeringSensor2D.ShowDangerGizmos);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
SteeringSensor2D.ShowVelocityGizmos = SteerSystemLayout("Velocity", vo, SteeringSensor2D.ShowVelocityGizmos);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
SteeringSensor2D.ShowDecisionGizmos = SteerSystemLayout("Decision", decision, SteeringSensor2D.ShowDecisionGizmos);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(locomotionMode);
|
||||
|
||||
var locmode = (LocomotionMode2D)locomotionMode.enumValueIndex;
|
||||
|
||||
if (locmode != LocomotionMode2D.None) {
|
||||
if (locmode == LocomotionMode2D.RigidBody2D) {
|
||||
EditorGUILayout.PropertyField(rigidBody);
|
||||
}
|
||||
EditorGUILayout.PropertyField(locomotion, true);
|
||||
}
|
||||
|
||||
if (EditorGUI.EndChangeCheck()) {
|
||||
EditorState.StopAllTesting();
|
||||
}
|
||||
|
||||
displayErrors();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
void displayErrors() {
|
||||
|
||||
}
|
||||
|
||||
bool SteerSystemLayout(string labelText, SerializedProperty property, bool showGizmos) {
|
||||
const float gizmoWidth = 70;
|
||||
EditorGUILayout.BeginVertical(BackgroundStyle);
|
||||
var r = EditorGUILayout.BeginHorizontal();
|
||||
EditorGUI.PrefixLabel(r, new GUIContent(labelText));
|
||||
|
||||
// We don't want to stop testing after clicking the checkbox to show gizmos for a steering system. Suspend the change
|
||||
// detection first.
|
||||
if (EditorGUI.EndChangeCheck()) {
|
||||
EditorState.StopAllTesting();
|
||||
}
|
||||
var prevShowGizmos = showGizmos;
|
||||
showGizmos = GUI.Toggle(new Rect(r.width - gizmoWidth, r.y, gizmoWidth, EditorGUIUtility.singleLineHeight), showGizmos, " Gizmos");
|
||||
if (IsTesting && prevShowGizmos != showGizmos) {
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
r.width -= 30;
|
||||
EditorGUILayout.PropertyField(property, new GUIContent(""));
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.EndVertical();
|
||||
return showGizmos;
|
||||
}
|
||||
|
||||
static GUIStyle backgroundStyle;
|
||||
static GUIStyle BackgroundStyle {
|
||||
get {
|
||||
if (backgroundStyle?.normal?.background == null) {
|
||||
var texture = new Texture2D(1, 1);
|
||||
texture.SetPixel(0, 0, new Color(0.2f, 0.2f, 0.2f, 0.15f));
|
||||
texture.Apply();
|
||||
backgroundStyle = new GUIStyle();
|
||||
backgroundStyle.normal.background = texture;
|
||||
}
|
||||
return backgroundStyle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 73d4e924a248430e888b584bd22fce7e
|
||||
timeCreated: 1506248635
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
158
Assets/SensorToolkit/Sensors/src/Editor/SteeringSensorEditor.cs
Normal file
158
Assets/SensorToolkit/Sensors/src/Editor/SteeringSensorEditor.cs
Normal file
@@ -0,0 +1,158 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections;
|
||||
|
||||
namespace Micosmo.SensorToolkit.Editors {
|
||||
|
||||
[CustomEditor(typeof(SteeringSensor))]
|
||||
[CanEditMultipleObjects]
|
||||
public class SteeringSensorEditor : BasePulsableEditor<SteeringSensor> {
|
||||
SerializedProperty isSpherical;
|
||||
SerializedProperty upDirection;
|
||||
SerializedProperty resolution;
|
||||
SerializedProperty seek;
|
||||
SerializedProperty interest;
|
||||
SerializedProperty danger;
|
||||
SerializedProperty vo;
|
||||
SerializedProperty decision;
|
||||
SerializedProperty pulseMode;
|
||||
SerializedProperty pulseUpdateFunction;
|
||||
SerializedProperty pulseInterval;
|
||||
SerializedProperty locomotionMode;
|
||||
SerializedProperty rigidBody;
|
||||
SerializedProperty characterController;
|
||||
SerializedProperty locomotion;
|
||||
|
||||
protected SteeringSensor sensor;
|
||||
|
||||
protected override bool canTest => true;
|
||||
|
||||
protected override void OnEnable() {
|
||||
base.OnEnable();
|
||||
if (serializedObject == null) return;
|
||||
sensor = serializedObject.targetObject as SteeringSensor;
|
||||
|
||||
isSpherical = serializedObject.FindProperty("isSpherical");
|
||||
upDirection = serializedObject.FindProperty("upDirection");
|
||||
resolution = serializedObject.FindProperty("resolution");
|
||||
seek = serializedObject.FindProperty("seek");
|
||||
interest = serializedObject.FindProperty("interest");
|
||||
danger = serializedObject.FindProperty("danger");
|
||||
vo = serializedObject.FindProperty("velocity");
|
||||
decision = serializedObject.FindProperty("decision");
|
||||
pulseMode = serializedObject.FindProperty("pulseRoutine.Mode");
|
||||
pulseUpdateFunction = serializedObject.FindProperty("pulseRoutine.UpdateFunction");
|
||||
pulseInterval = serializedObject.FindProperty("pulseRoutine.Interval");
|
||||
locomotionMode = serializedObject.FindProperty("LocomotionMode");
|
||||
rigidBody = serializedObject.FindProperty("RigidBody");
|
||||
characterController = serializedObject.FindProperty("CharacterController");
|
||||
locomotion = serializedObject.FindProperty("locomotion");
|
||||
}
|
||||
|
||||
protected override void OnPulsableGUI() {
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
EditorGUILayout.PropertyField(isSpherical);
|
||||
if (!sensor.IsSpherical) {
|
||||
EditorGUILayout.PropertyField(upDirection);
|
||||
}
|
||||
EditorGUILayout.PropertyField(resolution);
|
||||
EditorGUILayout.PropertyField(pulseMode, new GUIContent("Pulse Mode"));
|
||||
if (sensor.PulseMode != PulseRoutine.Modes.Manual) {
|
||||
EditorGUILayout.PropertyField(pulseUpdateFunction);
|
||||
}
|
||||
if (sensor.PulseMode == PulseRoutine.Modes.FixedInterval) {
|
||||
EditorGUILayout.PropertyField(pulseInterval, new GUIContent("Pulse Interval"));
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(seek);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
SteeringSensor.ShowInterestGizmos = SteerSystemLayout("Interest", interest, SteeringSensor.ShowInterestGizmos);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
SteeringSensor.ShowDangerGizmos = SteerSystemLayout("Danger", danger, SteeringSensor.ShowDangerGizmos);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
SteeringSensor.ShowVelocityGizmos = SteerSystemLayout("Velocity", vo, SteeringSensor.ShowVelocityGizmos);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
SteeringSensor.ShowDecisionGizmos = SteerSystemLayout("Decision", decision, SteeringSensor.ShowDecisionGizmos);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(locomotionMode);
|
||||
|
||||
var locmode = (LocomotionMode)locomotionMode.enumValueIndex;
|
||||
|
||||
if (locmode != LocomotionMode.None) {
|
||||
if (locmode == LocomotionMode.RigidBodyFlying || locmode == LocomotionMode.RigidBodyCharacter) {
|
||||
EditorGUILayout.PropertyField(rigidBody);
|
||||
} else {
|
||||
EditorGUILayout.PropertyField(characterController);
|
||||
}
|
||||
EditorGUILayout.PropertyField(locomotion, true);
|
||||
}
|
||||
|
||||
if (EditorGUI.EndChangeCheck()) {
|
||||
EditorState.StopAllTesting();
|
||||
}
|
||||
|
||||
displayErrors();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
void displayErrors() {
|
||||
|
||||
}
|
||||
|
||||
bool SteerSystemLayout(string labelText, SerializedProperty property, bool showGizmos) {
|
||||
const float gizmoWidth = 70;
|
||||
EditorGUILayout.BeginVertical(BackgroundStyle);
|
||||
var r = EditorGUILayout.BeginHorizontal();
|
||||
EditorGUI.PrefixLabel(r, new GUIContent(labelText));
|
||||
|
||||
// We don't want to stop testing after clicking the checkbox to show gizmos for a steering system. Suspend the change
|
||||
// detection first.
|
||||
if (EditorGUI.EndChangeCheck()) {
|
||||
EditorState.StopAllTesting();
|
||||
}
|
||||
var prevShowGizmos = showGizmos;
|
||||
showGizmos = GUI.Toggle(new Rect(r.width - gizmoWidth, r.y, gizmoWidth, EditorGUIUtility.singleLineHeight), showGizmos, " Gizmos");
|
||||
if (IsTesting && prevShowGizmos != showGizmos) {
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
r.width -= 30;
|
||||
EditorGUILayout.PropertyField(property, new GUIContent(""));
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.EndVertical();
|
||||
return showGizmos;
|
||||
}
|
||||
|
||||
static GUIStyle backgroundStyle;
|
||||
static GUIStyle BackgroundStyle {
|
||||
get {
|
||||
if (backgroundStyle?.normal?.background == null) {
|
||||
var texture = new Texture2D(1, 1);
|
||||
texture.SetPixel(0, 0, new Color(0.2f, 0.2f, 0.2f, 0.15f));
|
||||
texture.Apply();
|
||||
backgroundStyle = new GUIStyle();
|
||||
backgroundStyle.normal.background = texture;
|
||||
}
|
||||
return backgroundStyle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6d30917a0ea44e538d691caba14ba15f
|
||||
timeCreated: 1506168822
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,24 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections;
|
||||
|
||||
namespace Micosmo.SensorToolkit
|
||||
{
|
||||
[CustomPropertyDrawer(typeof(TagSelectorAttribute))]
|
||||
public class TagSelectorPropertyDrawer : PropertyDrawer
|
||||
{
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
if (property.propertyType == SerializedPropertyType.String)
|
||||
{
|
||||
EditorGUI.BeginProperty(position, label, property);
|
||||
property.stringValue = EditorGUI.TagField(position, label, property.stringValue);
|
||||
EditorGUI.EndProperty();
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUI.PropertyField(position, property, label);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1034f3df53154711b139c1d96798a066
|
||||
timeCreated: 1498963683
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
133
Assets/SensorToolkit/Sensors/src/Editor/TriggerSensor2DEditor.cs
Normal file
133
Assets/SensorToolkit/Sensors/src/Editor/TriggerSensor2DEditor.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Linq;
|
||||
|
||||
namespace Micosmo.SensorToolkit.Editors {
|
||||
[CustomEditor(typeof(TriggerSensor2D))]
|
||||
[CanEditMultipleObjects]
|
||||
public class TriggerSensor2DEditor : BaseSensorEditor<TriggerSensor2D> {
|
||||
SerializedProperty ignoreList;
|
||||
SerializedProperty tagFilterEnabled;
|
||||
SerializedProperty tagFilter;
|
||||
SerializedProperty detectionMode;
|
||||
SerializedProperty signalProcessors;
|
||||
SerializedProperty runInSafeMode;
|
||||
SerializedProperty onDetected;
|
||||
SerializedProperty onLostDetection;
|
||||
SerializedProperty onSomeDetection;
|
||||
SerializedProperty onNoDetection;
|
||||
|
||||
bool showEvents = false;
|
||||
|
||||
protected override bool canTest { get { return false; } }
|
||||
|
||||
protected override void OnEnable() {
|
||||
base.OnEnable();
|
||||
|
||||
if (serializedObject == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ignoreList = serializedObject.FindProperty("signalFilter.IgnoreList");
|
||||
tagFilterEnabled = serializedObject.FindProperty("signalFilter.EnableTagFilter");
|
||||
tagFilter = serializedObject.FindProperty("signalFilter.AllowedTags");
|
||||
detectionMode = serializedObject.FindProperty("DetectionMode");
|
||||
signalProcessors = serializedObject.FindProperty("signalProcessors");
|
||||
runInSafeMode = serializedObject.FindProperty("runInSafeMode");
|
||||
onDetected = serializedObject.FindProperty("OnDetected");
|
||||
onLostDetection = serializedObject.FindProperty("OnLostDetection");
|
||||
onSomeDetection = serializedObject.FindProperty("OnSomeDetection");
|
||||
onNoDetection = serializedObject.FindProperty("OnNoDetection");
|
||||
|
||||
sensor.OnDetected.AddListener(detectionEventHandler);
|
||||
sensor.OnLostDetection.AddListener(detectionEventHandler);
|
||||
}
|
||||
|
||||
protected override void OnDisable() {
|
||||
base.OnDisable();
|
||||
|
||||
sensor.OnDetected.RemoveListener(detectionEventHandler);
|
||||
sensor.OnLostDetection.RemoveListener(detectionEventHandler);
|
||||
}
|
||||
|
||||
protected override void InspectorParameters() {
|
||||
EditorGUILayout.PropertyField(ignoreList, true);
|
||||
EditorGUILayout.PropertyField(tagFilterEnabled);
|
||||
if (tagFilterEnabled.boolValue) {
|
||||
EditorGUILayout.PropertyField(tagFilter, true);
|
||||
}
|
||||
EditorGUILayout.PropertyField(detectionMode);
|
||||
EditorGUILayout.PropertyField(signalProcessors, new GUIContent("Signal Processors"));
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(runInSafeMode);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (showEvents = EditorGUILayout.Foldout(showEvents, "Events")) {
|
||||
EditorGUILayout.PropertyField(onDetected);
|
||||
EditorGUILayout.PropertyField(onLostDetection);
|
||||
EditorGUILayout.PropertyField(onSomeDetection);
|
||||
EditorGUILayout.PropertyField(onNoDetection);
|
||||
}
|
||||
|
||||
displayErrors();
|
||||
}
|
||||
|
||||
void displayErrors() {
|
||||
var showTriggerError = !checkForTriggers();
|
||||
var rb = sensor.GetComponent<Rigidbody2D>();
|
||||
var showRigidbodyError = rb == null;
|
||||
var showSleepmodeError = !showRigidbodyError && rb.sleepMode != RigidbodySleepMode2D.NeverSleep;
|
||||
|
||||
if (showTriggerError || showRigidbodyError) {
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
if (showTriggerError) {
|
||||
EditorGUILayout.HelpBox("Needs active Trigger Collider to detect GameObjects!", MessageType.Warning);
|
||||
}
|
||||
if (showRigidbodyError) {
|
||||
EditorGUILayout.HelpBox("In order to detect GameObjects without RigidBodies the TriggerSensor must itself have a RigidBody! Recommend adding a kinematic RigidBody.", MessageType.Warning);
|
||||
}
|
||||
if (showSleepmodeError) {
|
||||
EditorGUILayout.HelpBox("The rigidbody which owns the trigger collider should have its 'Sleeping Mode' parameter set to 'Never Sleep'", MessageType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
bool checkForTriggers() {
|
||||
var hasRB = sensor.GetComponent<Rigidbody2D>() != null;
|
||||
if (hasRB) {
|
||||
foreach (Collider2D c in sensor.GetComponentsInChildren<Collider2D>()) {
|
||||
if (c.enabled && c.isTrigger) return true;
|
||||
}
|
||||
} else {
|
||||
foreach (Collider2D c in sensor.GetComponents<Collider2D>()) {
|
||||
if (c.enabled && c.isTrigger) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void detectionEventHandler(GameObject g, Sensor s) {
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
|
||||
[CustomEditor(typeof(TriggerSensor2D.Safety))]
|
||||
[CanEditMultipleObjects]
|
||||
public class TriggerSensor2DSafetyEditor : Editor {
|
||||
|
||||
static string msg =
|
||||
"This component was added because you have a Trigger Sensor using the 'Run In Safe Mode' " +
|
||||
"option. It handles some quirks in Unity regarding missed trigger events because a " +
|
||||
"collider is disabled. Its not efficient to use safe mode if you plan " +
|
||||
"to use many Trigger Sensors. Please read the manual to learn how to avoid these quirks.";
|
||||
|
||||
public override void OnInspectorGUI() {
|
||||
EditorGUILayout.HelpBox(msg, MessageType.Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aa0e59da74104ac795301aa622815811
|
||||
timeCreated: 1491309965
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
130
Assets/SensorToolkit/Sensors/src/Editor/TriggerSensorEditor.cs
Normal file
130
Assets/SensorToolkit/Sensors/src/Editor/TriggerSensorEditor.cs
Normal file
@@ -0,0 +1,130 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Linq;
|
||||
|
||||
namespace Micosmo.SensorToolkit.Editors {
|
||||
[CustomEditor(typeof(TriggerSensor))]
|
||||
[CanEditMultipleObjects]
|
||||
public class TriggerSensorEditor : BaseSensorEditor<TriggerSensor>
|
||||
{
|
||||
SerializedProperty ignoreList;
|
||||
SerializedProperty tagFilterEnabled;
|
||||
SerializedProperty tagFilter;
|
||||
SerializedProperty detectionMode;
|
||||
SerializedProperty signalProcessors;
|
||||
SerializedProperty runInSafeMode;
|
||||
SerializedProperty onDetected;
|
||||
SerializedProperty onLostDetection;
|
||||
SerializedProperty onSomeDetection;
|
||||
SerializedProperty onNoDetection;
|
||||
|
||||
bool showEvents = false;
|
||||
|
||||
protected override bool canTest { get { return false; } }
|
||||
|
||||
protected override void OnEnable() {
|
||||
base.OnEnable();
|
||||
|
||||
if (serializedObject == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ignoreList = serializedObject.FindProperty("signalFilter.IgnoreList");
|
||||
tagFilterEnabled = serializedObject.FindProperty("signalFilter.EnableTagFilter");
|
||||
tagFilter = serializedObject.FindProperty("signalFilter.AllowedTags");
|
||||
detectionMode = serializedObject.FindProperty("DetectionMode");
|
||||
signalProcessors = serializedObject.FindProperty("signalProcessors");
|
||||
runInSafeMode = serializedObject.FindProperty("runInSafeMode");
|
||||
onDetected = serializedObject.FindProperty("OnDetected");
|
||||
onLostDetection = serializedObject.FindProperty("OnLostDetection");
|
||||
onSomeDetection = serializedObject.FindProperty("OnSomeDetection");
|
||||
onNoDetection = serializedObject.FindProperty("OnNoDetection");
|
||||
|
||||
sensor.OnDetected.AddListener(detectionEventHandler);
|
||||
sensor.OnLostDetection.AddListener(detectionEventHandler);
|
||||
}
|
||||
|
||||
protected override void OnDisable() {
|
||||
base.OnDisable();
|
||||
|
||||
sensor.OnDetected.RemoveListener(detectionEventHandler);
|
||||
sensor.OnLostDetection.RemoveListener(detectionEventHandler);
|
||||
}
|
||||
|
||||
protected override void InspectorParameters() {
|
||||
EditorGUILayout.PropertyField(ignoreList, true);
|
||||
EditorGUILayout.PropertyField(tagFilterEnabled);
|
||||
if (tagFilterEnabled.boolValue) {
|
||||
EditorGUILayout.PropertyField(tagFilter, true);
|
||||
}
|
||||
EditorGUILayout.PropertyField(detectionMode);
|
||||
EditorGUILayout.PropertyField(signalProcessors, new GUIContent("Signal Processors"));
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(runInSafeMode);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (showEvents = EditorGUILayout.Foldout(showEvents, "Events")) {
|
||||
EditorGUILayout.PropertyField(onDetected);
|
||||
EditorGUILayout.PropertyField(onLostDetection);
|
||||
EditorGUILayout.PropertyField(onSomeDetection);
|
||||
EditorGUILayout.PropertyField(onNoDetection);
|
||||
}
|
||||
|
||||
displayErrors();
|
||||
}
|
||||
|
||||
void displayErrors() {
|
||||
var showTriggerError = !checkForTriggers();
|
||||
var showRigidbodyError = (sensor.DetectionMode == DetectionModes.Colliders || sensor.DetectionMode == DetectionModes.Either) && sensor.GetComponent<Rigidbody>() == null;
|
||||
|
||||
if (showTriggerError || showRigidbodyError) {
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
if (showTriggerError) {
|
||||
EditorGUILayout.HelpBox("Needs active Trigger Collider to detect GameObjects!", MessageType.Warning);
|
||||
}
|
||||
if (showRigidbodyError) {
|
||||
EditorGUILayout.HelpBox("In order to detect GameObjects without RigidBodies the TriggerSensor must itself have a RigidBody! Recommend adding a kinematic RigidBody.", MessageType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
bool checkForTriggers() {
|
||||
var hasRB = sensor.GetComponent<Rigidbody>() != null;
|
||||
if (hasRB) {
|
||||
foreach (Collider c in sensor.GetComponentsInChildren<Collider>()) {
|
||||
if (c.enabled && c.isTrigger) return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
foreach (Collider c in sensor.GetComponents<Collider>()) {
|
||||
if (c.enabled && c.isTrigger) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void detectionEventHandler(GameObject g, Sensor s) {
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
|
||||
[CustomEditor(typeof(TriggerSensor.Safety))]
|
||||
[CanEditMultipleObjects]
|
||||
public class TriggerSensorSafetyEditor : Editor {
|
||||
|
||||
static string msg =
|
||||
"This component was added because you have a Trigger Sensor using the 'Run In Safe Mode' " +
|
||||
"option. It handles some quirks in Unity regarding missed trigger events because a " +
|
||||
"collider is disabled. Its not efficient to use safe mode if you plan " +
|
||||
"to use many Trigger Sensors. Please read the manual to learn how to avoid these quirks.";
|
||||
|
||||
public override void OnInspectorGUI() {
|
||||
EditorGUILayout.HelpBox(msg, MessageType.Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 872102644ea14cd1a954ae9c03e52353
|
||||
timeCreated: 1489481290
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
30
Assets/SensorToolkit/Sensors/src/Editor/UserSignalsEditor.cs
Normal file
30
Assets/SensorToolkit/Sensors/src/Editor/UserSignalsEditor.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Micosmo.SensorToolkit.Editors {
|
||||
|
||||
[CustomEditor(typeof(UserSignals))]
|
||||
[CanEditMultipleObjects]
|
||||
public class UserSignalsEditor : BaseSensorEditor<UserSignals> {
|
||||
SerializedProperty inputSignals;
|
||||
|
||||
protected override bool canTest => true;
|
||||
|
||||
protected override void OnEnable() {
|
||||
base.OnEnable();
|
||||
|
||||
if (serializedObject == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
inputSignals = serializedObject.FindProperty("inputSignals");
|
||||
}
|
||||
|
||||
protected override void InspectorParameters() {
|
||||
EditorGUILayout.PropertyField(inputSignals, true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 506414bd9150484293af313c454b24e8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/SensorToolkit/Sensors/src/Gizmos.meta
Normal file
8
Assets/SensorToolkit/Sensors/src/Gizmos.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dd915a3f5ecc460a9fa2a23f58f13ca2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
301
Assets/SensorToolkit/Sensors/src/Gizmos/SensorGizmoAtoms.cs
Normal file
301
Assets/SensorToolkit/Sensors/src/Gizmos/SensorGizmoAtoms.cs
Normal file
@@ -0,0 +1,301 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace Micosmo.SensorToolkit {
|
||||
|
||||
public static partial class SensorGizmos {
|
||||
|
||||
public static void Label(Vector3 position, string text) {
|
||||
#if UNITY_EDITOR
|
||||
Handles.Label(position, text, EditorStyles.textField);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void Polyline(float width, params Vector3[] points) {
|
||||
#if UNITY_EDITOR
|
||||
Handles.DrawAAPolyLine(width, points);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void LineNoZTest(Vector3 from, Vector3 to) {
|
||||
#if UNITY_EDITOR
|
||||
var oldZTest = Handles.zTest;
|
||||
Handles.zTest = UnityEngine.Rendering.CompareFunction.Disabled;
|
||||
Handles.DrawLine(from, to);
|
||||
Handles.zTest = oldZTest;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void ThickLineNoZTest(Vector3 from, Vector3 to, float thickness) {
|
||||
#if UNITY_EDITOR
|
||||
var oldZTest = Handles.zTest;
|
||||
Handles.zTest = UnityEngine.Rendering.CompareFunction.Disabled;
|
||||
Handles.DrawAAPolyLine(thickness, from, to);
|
||||
Handles.zTest = oldZTest;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void SphereGizmo(Vector3 position, float radius) {
|
||||
#if UNITY_EDITOR
|
||||
PushMatrix(Matrix4x4.identity);
|
||||
BackfaceSphereHandle(position, radius);
|
||||
PopMatrix();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void CircleGizmo(Vector3 position, Vector3 normal, float radius) {
|
||||
#if UNITY_EDITOR
|
||||
PushMatrix(Matrix4x4.identity);
|
||||
Handles.DrawWireDisc(position, normal, radius);
|
||||
PopMatrix();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void CircleSector(Vector3 position, Vector3 direction, Vector3 normal, float angle, float radius) {
|
||||
#if UNITY_EDITOR
|
||||
PushMatrix(Matrix4x4.identity);
|
||||
|
||||
var r1 = Quaternion.AngleAxis(-angle, normal) * direction * radius;
|
||||
var r2 = Quaternion.AngleAxis(angle, normal) * direction * radius;
|
||||
|
||||
Handles.DrawWireArc(position, normal, r1, angle * 2, radius);
|
||||
//BackfaceArc(position, direction, normal, angle, radius);
|
||||
Handles.DrawLine(position, position + r1);
|
||||
Handles.DrawLine(position, position + r2);
|
||||
|
||||
PopMatrix();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void BackfaceArc(Vector3 position, Vector3 direction, Vector3 normal, float angle, float radius) {
|
||||
#if UNITY_EDITOR
|
||||
PushMatrix(Matrix4x4.identity);
|
||||
|
||||
if (Camera.current.orthographic) {
|
||||
Vector3 normalized = Vector3.Cross(normal, Camera.current.transform.forward).normalized;
|
||||
DrawTwoShadedWireDiscSector(position, normal, normalized, 180f,
|
||||
Vector3.SignedAngle(normalized, Quaternion.AngleAxis(-angle, normal) * direction, normal),
|
||||
Vector3.SignedAngle(normalized, Quaternion.AngleAxis(angle, normal) * direction, normal),
|
||||
radius);
|
||||
} else {
|
||||
var cam2pos = Matrix.MultiplyPoint(position) - Camera.current.transform.position;
|
||||
float sqrMagnitude = cam2pos.sqrMagnitude;
|
||||
float rad2 = radius * radius;
|
||||
float f1 = rad2 * rad2 / sqrMagnitude;
|
||||
float num3 = f1 / rad2;
|
||||
if (num3 < 1.0f) {
|
||||
float a = Vector3.Angle(cam2pos, normal);
|
||||
float num4 = Mathf.Tan((90f - Mathf.Min(a, 180f - a)) * (Mathf.PI / 180f));
|
||||
float f2 = Mathf.Sqrt(f1 + num4 * num4 * f1) / radius;
|
||||
if (f2 < 1.0f) {
|
||||
float grazeAngle = Mathf.Asin(f2) * 57.29578f;
|
||||
Vector3 normalized = Vector3.Cross(normal, cam2pos).normalized;
|
||||
Vector3 from = Quaternion.AngleAxis(grazeAngle, normal) * normalized;
|
||||
DrawTwoShadedWireDiscSector(position, normal, from, (90.0f - grazeAngle) * 2.0f,
|
||||
Vector3.SignedAngle(from, Quaternion.AngleAxis(-angle, normal) * direction, normal),
|
||||
Vector3.SignedAngle(from, Quaternion.AngleAxis(angle, normal) * direction, normal),
|
||||
radius);
|
||||
} else {
|
||||
PushColor(SetA(Color, .2f));
|
||||
var r1 = Quaternion.AngleAxis(-angle, normal) * direction * radius;
|
||||
Handles.DrawWireArc(position, normal, r1, angle * 2, radius);
|
||||
PopColor();
|
||||
}
|
||||
} else {
|
||||
PushColor(SetA(Color, .2f));
|
||||
var r1 = Quaternion.AngleAxis(-angle, normal) * direction * radius;
|
||||
Handles.DrawWireArc(position, normal, r1, angle * 2, radius);
|
||||
PopColor();
|
||||
}
|
||||
}
|
||||
|
||||
PopMatrix();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void CapsuleGizmo(Vector3 position, float radius, float height) {
|
||||
#if UNITY_EDITOR
|
||||
PushMatrix(Matrix * Matrix4x4.Translate(position));
|
||||
|
||||
height = Mathf.Abs(height);
|
||||
var extent = Vector3.up * height / 2f;
|
||||
|
||||
HalfSphere(extent, radius);
|
||||
PushMatrix(Matrix * Matrix4x4.Rotate(Quaternion.FromToRotation(Vector3.up, Vector3.down)));
|
||||
HalfSphere(extent, radius);
|
||||
PopMatrix();
|
||||
|
||||
Gizmos.DrawRay(extent + Vector3.right * radius, Vector3.down * height);
|
||||
Gizmos.DrawRay(extent - Vector3.right * radius, Vector3.down * height);
|
||||
Gizmos.DrawRay(extent + Vector3.forward * radius, Vector3.down * height);
|
||||
Gizmos.DrawRay(extent - Vector3.forward * radius, Vector3.down * height);
|
||||
|
||||
PopMatrix();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void Capsule2DGizmo(Vector3 position, float radius, float height) {
|
||||
#if UNITY_EDITOR
|
||||
PushMatrix(Matrix * Matrix4x4.Translate(position));
|
||||
|
||||
height = Mathf.Max(radius * 2, height);
|
||||
|
||||
var pt1 = Vector3.up * (height - 2 * radius) / 2f;
|
||||
var pt2 = -pt1;
|
||||
Handles.color = Gizmos.color;
|
||||
Handles.DrawWireArc(pt1, -Vector3.forward, Vector3.left, 180f, radius);
|
||||
Handles.DrawWireArc(pt2, -Vector3.forward, Vector3.right, 180f, radius);
|
||||
Gizmos.DrawRay(pt1 + Vector3.right * radius, Vector3.down * (height - 2 * radius));
|
||||
Gizmos.DrawRay(pt1 - Vector3.right * radius, Vector3.down * (height - 2 * radius));
|
||||
|
||||
PopMatrix();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void CameraFacingWireDisc(Vector3 position, float radius) {
|
||||
#if UNITY_EDITOR
|
||||
if (Camera.current.orthographic) {
|
||||
Handles.DrawWireDisc(position, position - Matrix.inverse.MultiplyPoint(Camera.current.transform.position), radius);
|
||||
} else {
|
||||
var cam2pos = position - Matrix.inverse.MultiplyPoint(Camera.current.transform.position);
|
||||
float sqrMagnitude = cam2pos.sqrMagnitude;
|
||||
float rad2 = radius * radius;
|
||||
float f1 = rad2 * rad2 / sqrMagnitude;
|
||||
float num3 = f1 / rad2;
|
||||
if (num3 < 1.0f) {
|
||||
float num4 = Mathf.Sqrt(rad2 - f1);
|
||||
Handles.DrawWireDisc(position - rad2 * cam2pos / sqrMagnitude, cam2pos, num4);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static Vector3[] vector3Array = new Vector3[6] {
|
||||
Vector3.right, Vector3.up, Vector3.forward,
|
||||
-Vector3.right, -Vector3.up, -Vector3.forward
|
||||
};
|
||||
static void BackfaceSphereHandle(Vector3 position, float radius) {
|
||||
#if UNITY_EDITOR
|
||||
CameraFacingWireDisc(position, radius);
|
||||
|
||||
var localCamPos = Matrix.inverse.MultiplyPoint(Camera.current.transform.position);
|
||||
var localCamForward = Matrix.inverse.MultiplyVector(Camera.current.transform.forward);
|
||||
|
||||
if (Camera.current.orthographic) {
|
||||
for (int index = 0; index < 3; ++index) {
|
||||
Vector3 normalized = Vector3.Cross(vector3Array[index], localCamForward).normalized;
|
||||
DrawTwoShadedWireDisc(position, vector3Array[index], normalized, 180f, radius);
|
||||
}
|
||||
} else {
|
||||
var cam2pos = position - localCamPos;
|
||||
float sqrMagnitude = cam2pos.sqrMagnitude;
|
||||
float rad2 = radius * radius;
|
||||
float f1 = rad2 * rad2 / sqrMagnitude;
|
||||
float num3 = f1 / rad2;
|
||||
for (int index = 0; index < 3; ++index) {
|
||||
if (num3 < 1.0f) {
|
||||
float a = Vector3.Angle(cam2pos, vector3Array[index]);
|
||||
float num4 = Mathf.Tan((90f - Mathf.Min(a, 180f - a)) * (Mathf.PI / 180f));
|
||||
float f2 = Mathf.Sqrt(f1 + num4 * num4 * f1) / radius;
|
||||
if (f2 < 1.0f) {
|
||||
float angle = Mathf.Asin(f2) * 57.29578f;
|
||||
Vector3 normalized = Vector3.Cross(vector3Array[index], cam2pos).normalized;
|
||||
Vector3 from = Quaternion.AngleAxis(angle, vector3Array[index]) * normalized;
|
||||
DrawTwoShadedWireDisc(position, vector3Array[index], from, (90.0f - angle) * 2.0f, radius);
|
||||
} else {
|
||||
DrawTwoShadedWireDisc(position, vector3Array[index], radius);
|
||||
}
|
||||
} else {
|
||||
DrawTwoShadedWireDisc(position, vector3Array[index], radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void HalfSphere(Vector3 position, float radius) {
|
||||
#if UNITY_EDITOR
|
||||
Handles.DrawWireDisc(position, Vector3.up, radius);
|
||||
Handles.DrawWireArc(position, Vector3.right, Vector3.back, 180f, radius);
|
||||
Handles.DrawWireArc(position, Vector3.forward, Vector3.right, 180f, radius);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void DrawTwoShadedWireDisc(Vector3 position, Vector3 axis, float radius) {
|
||||
#if UNITY_EDITOR
|
||||
PushColor(SetA(Color, .2f));
|
||||
Handles.DrawWireDisc(position, axis, radius);
|
||||
PopColor();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void DrawTwoShadedWireDisc(Vector3 position, Vector3 axis, Vector3 from, float degrees, float radius) {
|
||||
#if UNITY_EDITOR
|
||||
Handles.DrawWireArc(position, axis, from, degrees, radius);
|
||||
PushColor(SetA(Color, .2f));
|
||||
Handles.DrawWireArc(position, axis, from, degrees - 360f, radius);
|
||||
PopColor();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void DrawTwoShadedWireDiscSector(Vector3 position, Vector3 axis, Vector3 from, float degrees, float startDegrees, float endDegrees, float radius) {
|
||||
#if UNITY_EDITOR
|
||||
if (endDegrees < startDegrees) {
|
||||
endDegrees += 360f;
|
||||
}
|
||||
|
||||
var currPos = startDegrees;
|
||||
var i = 0;
|
||||
while (currPos < endDegrees && i < 4) {
|
||||
var isFront = currPos >= 0 && currPos < degrees;
|
||||
var isBack = !isFront;
|
||||
if (isBack) {
|
||||
PushColor(SetA(Color, .2f));
|
||||
}
|
||||
|
||||
var nextPos = currPos;
|
||||
if (currPos < 0f) {
|
||||
nextPos = Mathf.Min(0, endDegrees);
|
||||
} else if (currPos < 360f) {
|
||||
nextPos = isBack
|
||||
? Mathf.Min(endDegrees, 360f)
|
||||
: Mathf.Min(endDegrees, degrees);
|
||||
} else {
|
||||
nextPos = endDegrees;
|
||||
}
|
||||
|
||||
var delta = (nextPos - currPos);
|
||||
|
||||
Handles.DrawWireArc(position, axis, Quaternion.AngleAxis(currPos, axis) * from, delta, radius);
|
||||
|
||||
currPos = nextPos;
|
||||
i += 1;
|
||||
|
||||
if (isBack) {
|
||||
PopColor();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*Debug.Log($"start: {startDegrees} -- end: {endDegrees} -- degrees: {degrees}");
|
||||
|
||||
PushColor(Color.green);
|
||||
Gizmos.DrawCube(position + from*radius, Vector3.one * .2f);
|
||||
PopColor();
|
||||
PushColor(Color.red);
|
||||
Gizmos.DrawCube(position + Quaternion.AngleAxis(degrees, axis) * from*radius, Vector3.one * .2f);
|
||||
PopColor();
|
||||
|
||||
PushColor(Color.blue);
|
||||
Gizmos.DrawCube(position + Quaternion.AngleAxis(startDegrees, axis) * from * radius, Vector3.one * .2f);
|
||||
PopColor();*/
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 556127bc1b274ab4992983fa6b421428
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
96
Assets/SensorToolkit/Sensors/src/Gizmos/SensorGizmoUtils.cs
Normal file
96
Assets/SensorToolkit/Sensors/src/Gizmos/SensorGizmoUtils.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace Micosmo.SensorToolkit {
|
||||
|
||||
public static partial class SensorGizmos {
|
||||
|
||||
public static readonly Color DarkPurple = new Color(.2f, .169f, .255f);
|
||||
public static readonly Color Green = new Color(0.345f, 0.737f, 0.51f);
|
||||
public static readonly Color Red = new Color(0.922f, 0.318f, 0.376f);
|
||||
public static readonly Color Yellow = new Color(0.949f, 0.816f, 0.482f);
|
||||
public static readonly Color Blue = new Color(0.757f, 0.816f, 0.933f);
|
||||
public static readonly Color Cyan = new Color(0.698f, 0.125f, 0.467f);
|
||||
|
||||
static readonly List<Matrix4x4> matrixStack = new List<Matrix4x4>();
|
||||
public static Matrix4x4 Matrix => matrixStack.Count > 0 ? matrixStack[matrixStack.Count - 1] : Matrix4x4.identity;
|
||||
public static void PushMatrix(Matrix4x4 m) {
|
||||
matrixStack.Add(m);
|
||||
SetMatrix(m);
|
||||
}
|
||||
public static void PopMatrix() {
|
||||
if (matrixStack.Count > 0) {
|
||||
matrixStack.RemoveAt(matrixStack.Count - 1);
|
||||
}
|
||||
SetMatrix(Matrix);
|
||||
}
|
||||
static void SetMatrix(Matrix4x4 m) {
|
||||
if (matrixStack.Count > 0) {
|
||||
matrixStack[matrixStack.Count - 1] = m;
|
||||
}
|
||||
Gizmos.matrix = m;
|
||||
#if UNITY_EDITOR
|
||||
Handles.matrix = m;
|
||||
#endif
|
||||
}
|
||||
|
||||
static readonly List<Color> colorStack = new List<Color>();
|
||||
public static Color Color => colorStack.Count > 0 ? colorStack[colorStack.Count - 1] : Color.cyan;
|
||||
public static void PushColor(Color c) {
|
||||
colorStack.Add(c);
|
||||
SetColor(c);
|
||||
}
|
||||
public static void PopColor() {
|
||||
if (colorStack.Count > 0) {
|
||||
colorStack.RemoveAt(colorStack.Count - 1);
|
||||
}
|
||||
SetColor(Color);
|
||||
}
|
||||
public static void WithColor(Color c, System.Action action) {
|
||||
PushColor(c);
|
||||
action();
|
||||
PopColor();
|
||||
}
|
||||
static void SetColor(Color c) {
|
||||
if (colorStack.Count > 0) {
|
||||
colorStack[colorStack.Count - 1] = c;
|
||||
}
|
||||
Gizmos.color = c;
|
||||
#if UNITY_EDITOR
|
||||
Handles.color = c;
|
||||
#endif
|
||||
}
|
||||
static Color SetA(Color c, float a) => new Color(c.r, c.g, c.b, a);
|
||||
|
||||
public static Color LerpColour(Color[] pts, float t) {
|
||||
var i = Mathf.FloorToInt(t * pts.Length);
|
||||
var frac = (t * pts.Length) - i;
|
||||
if (i < 0) {
|
||||
return pts[0];
|
||||
}
|
||||
if (i >= pts.Length-1) {
|
||||
return pts[pts.Length - 1];
|
||||
}
|
||||
return Color.Lerp(pts[i], pts[i + 1], frac);
|
||||
}
|
||||
|
||||
public static Color ParseHexColour(string c) {
|
||||
var col = Color.magenta;
|
||||
ColorUtility.TryParseHtmlString(c, out col);
|
||||
return col;
|
||||
}
|
||||
|
||||
public static Color[] ParseHexColours(string[] colours) {
|
||||
var result = new Color[colours.Length];
|
||||
for (var i = 0; i < colours.Length; i++) {
|
||||
result[i] = ParseHexColour(colours[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 36d4944765754a92becd2e54904be20f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
209
Assets/SensorToolkit/Sensors/src/Gizmos/SensorGizmos.cs
Normal file
209
Assets/SensorToolkit/Sensors/src/Gizmos/SensorGizmos.cs
Normal file
@@ -0,0 +1,209 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace Micosmo.SensorToolkit {
|
||||
|
||||
public static partial class SensorGizmos {
|
||||
|
||||
public static void FOVGizmo(ReferenceFrame frame, float length, float horizAngle, float vertAngle) {
|
||||
var m = Matrix4x4.TRS(frame.Position, Quaternion.LookRotation(frame.Forward, frame.Up), Vector3.one);
|
||||
PushMatrix(m);
|
||||
|
||||
if (horizAngle != 0) {
|
||||
PushColor(STPrefs.LOSFovColour);
|
||||
CircleSector(frame.Position, frame.Forward, frame.Up, horizAngle, length);
|
||||
PopColor();
|
||||
}
|
||||
|
||||
if (vertAngle != 0) {
|
||||
PushColor(STPrefs.LOSFovColour);
|
||||
CircleSector(frame.Position, frame.Forward, frame.Right, vertAngle, length);
|
||||
PopColor();
|
||||
}
|
||||
|
||||
PopMatrix();
|
||||
}
|
||||
|
||||
public static void DetectedObjectGizmo(Bounds bounds) {
|
||||
#if UNITY_EDITOR
|
||||
PushMatrix(Matrix4x4.identity);
|
||||
PushColor(STPrefs.SignalBoundsColour);
|
||||
|
||||
if (bounds.extents != Vector3.zero) {
|
||||
Gizmos.DrawWireCube(bounds.center, bounds.size);
|
||||
}
|
||||
|
||||
var texture = AssetDatabase.LoadAssetAtPath<Texture>("Assets/Gizmos/SensorToolkit/LOS-VISIBLE.png");
|
||||
var size = new Vector2(texture.width, texture.height) / HandleUtility.GetHandleSize(bounds.center) / 3f;
|
||||
size = Vector2.Min(size, new Vector2(texture.width, texture.height));
|
||||
//size = Vector2.Max(size, Vector2.one * 12f);
|
||||
|
||||
if (size.x > 8f && STPrefs.ShowEyeIconInSignal) {
|
||||
Handles.Label(bounds.center, new GUIContent() { image = texture }, new GUIStyle() { contentOffset = new Vector2(-size.x / 2f, -size.y / 2f), fixedHeight = size.x, fixedWidth = size.y });
|
||||
}
|
||||
|
||||
PopColor();
|
||||
PopMatrix();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void RaycastHitGizmo(Vector3 point, Vector3 normal, bool isObstruction) {
|
||||
#if UNITY_EDITOR
|
||||
PushMatrix(Matrix4x4.identity);
|
||||
PushColor(isObstruction ? STPrefs.CastingBlockedRayColour : STPrefs.CastingRayColour);
|
||||
|
||||
Handles.zTest = UnityEngine.Rendering.CompareFunction.LessEqual;
|
||||
|
||||
var screenSize = HandleUtility.GetHandleSize(point);
|
||||
var offset = (normal * screenSize * 0.01f);
|
||||
|
||||
Handles.DrawSolidDisc(point + offset, normal, screenSize * 0.1f);
|
||||
SetColor(SetA(Color, 0.2f));
|
||||
Handles.zTest = UnityEngine.Rendering.CompareFunction.Greater;
|
||||
Handles.DrawSolidDisc(point + offset, normal, screenSize * 0.1f);
|
||||
|
||||
SetColor(STPrefs.RayHitNormalColour);
|
||||
Gizmos.DrawRay(point, normal * screenSize * 0.5f);
|
||||
|
||||
PopColor();
|
||||
PopMatrix();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void SpherecastGizmo(Ray ray, float length, Quaternion rotation, float radius, bool isObstructed) {
|
||||
#if UNITY_EDITOR
|
||||
PushMatrix(Matrix4x4.TRS(ray.origin, rotation, Vector3.one));
|
||||
|
||||
PushColor(isObstructed ? STPrefs.CastingBlockedRayColour : STPrefs.CastingShapeColour);
|
||||
BackfaceSphereHandle(Vector3.zero, radius);
|
||||
SetColor(isObstructed ? STPrefs.CastingBlockedRayColour : STPrefs.CastingRayColour);
|
||||
BackfaceSphereHandle(ray.direction * length, radius);
|
||||
|
||||
var localLeft = Vector3.Cross(Vector3.up, ray.direction).normalized;
|
||||
var localUp = Vector3.Cross(localLeft, ray.direction).normalized;
|
||||
|
||||
Gizmos.DrawRay(localLeft * radius, ray.direction * length);
|
||||
Gizmos.DrawRay(-localLeft * radius, ray.direction * length);
|
||||
Gizmos.DrawRay(localUp * radius, ray.direction * length);
|
||||
Gizmos.DrawRay(-localUp * radius, ray.direction * length);
|
||||
|
||||
PopColor();
|
||||
PopMatrix();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void CirclecastGizmo(Ray ray, float length, Quaternion rotation, float radius, bool isObstructed) {
|
||||
#if UNITY_EDITOR
|
||||
PushMatrix(Matrix4x4.TRS(ray.origin, rotation, Vector3.one));
|
||||
PushColor(isObstructed ? STPrefs.CastingBlockedRayColour : STPrefs.CastingShapeColour);
|
||||
|
||||
var height = length + radius;
|
||||
|
||||
var pt1 = Vector3.zero;
|
||||
var pt2 = ray.direction*length;
|
||||
Handles.DrawWireDisc(pt1, -Vector3.forward, radius);
|
||||
|
||||
SetColor(isObstructed ? STPrefs.CastingBlockedRayColour : STPrefs.CastingRayColour);
|
||||
|
||||
Handles.DrawWireDisc(pt2, -Vector3.forward, radius);
|
||||
var right = Quaternion.LookRotation(ray.direction, Vector3.forward) * Vector3.right;
|
||||
Gizmos.DrawRay(pt1 + right * radius, ray.direction * (height - radius));
|
||||
Gizmos.DrawRay(pt1 - right * radius, ray.direction * (height - radius));
|
||||
|
||||
PopColor();
|
||||
PopMatrix();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void BoxcastGizmo(Ray ray, float length, Quaternion rotation, Vector3 halfExtents, bool isObstructed) {
|
||||
#if UNITY_EDITOR
|
||||
PushMatrix(Matrix4x4.TRS(ray.origin, rotation, Vector3.one));
|
||||
PushColor(isObstructed ? STPrefs.CastingBlockedRayColour : STPrefs.CastingShapeColour);
|
||||
|
||||
Gizmos.DrawWireCube(Vector3.zero, halfExtents * 2f);
|
||||
|
||||
SetColor(isObstructed ? STPrefs.CastingBlockedRayColour : STPrefs.CastingRayColour);
|
||||
|
||||
Gizmos.DrawWireCube(ray.direction * length, halfExtents * 2f);
|
||||
|
||||
Gizmos.DrawRay(Vector3.up * halfExtents.y + Vector3.right * halfExtents.x + Vector3.forward * halfExtents.z, ray.direction * length);
|
||||
Gizmos.DrawRay(Vector3.up * halfExtents.y + Vector3.right * halfExtents.x - Vector3.forward * halfExtents.z, ray.direction * length);
|
||||
Gizmos.DrawRay(Vector3.up * halfExtents.y - Vector3.right * halfExtents.x + Vector3.forward * halfExtents.z, ray.direction * length);
|
||||
Gizmos.DrawRay(Vector3.up * halfExtents.y - Vector3.right * halfExtents.x - Vector3.forward * halfExtents.z, ray.direction * length);
|
||||
|
||||
Gizmos.DrawRay(-Vector3.up * halfExtents.y + Vector3.right * halfExtents.x + Vector3.forward * halfExtents.z, ray.direction * length);
|
||||
Gizmos.DrawRay(-Vector3.up * halfExtents.y + Vector3.right * halfExtents.x - Vector3.forward * halfExtents.z, ray.direction * length);
|
||||
Gizmos.DrawRay(-Vector3.up * halfExtents.y - Vector3.right * halfExtents.x + Vector3.forward * halfExtents.z, ray.direction * length);
|
||||
Gizmos.DrawRay(-Vector3.up * halfExtents.y - Vector3.right * halfExtents.x - Vector3.forward * halfExtents.z, ray.direction * length);
|
||||
|
||||
PopColor();
|
||||
PopMatrix();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void CapsulecastGizmo(Ray ray, float length, Quaternion rotation, float radius, float height, bool isObstructed) {
|
||||
#if UNITY_EDITOR
|
||||
PushMatrix(Matrix4x4.TRS(ray.origin, rotation, Vector3.one));
|
||||
PushColor(isObstructed ? STPrefs.CastingBlockedRayColour : STPrefs.CastingShapeColour);
|
||||
|
||||
height = Mathf.Abs(height);
|
||||
|
||||
CapsuleGizmo(Vector3.zero, radius, height);
|
||||
|
||||
SetColor(isObstructed ? STPrefs.CastingBlockedRayColour : STPrefs.CastingRayColour);
|
||||
CapsuleGizmo(ray.direction * length, radius, height);
|
||||
|
||||
var pt1 = Vector3.up * height / 2f;
|
||||
var pt2 = -pt1;
|
||||
|
||||
Gizmos.DrawRay(pt1 + Vector3.right * radius, ray.direction * length);
|
||||
Gizmos.DrawRay(pt1 - Vector3.right * radius, ray.direction * length);
|
||||
Gizmos.DrawRay(pt1 + Vector3.up * radius, ray.direction * length);
|
||||
Gizmos.DrawRay(pt1 + Vector3.forward * radius, ray.direction * length);
|
||||
Gizmos.DrawRay(pt1 - Vector3.forward * radius, ray.direction * length);
|
||||
|
||||
Gizmos.DrawRay(pt2 + Vector3.right * radius, ray.direction * length);
|
||||
Gizmos.DrawRay(pt2 - Vector3.right * radius, ray.direction * length);
|
||||
Gizmos.DrawRay(pt2 - Vector3.up * radius, ray.direction * length);
|
||||
Gizmos.DrawRay(pt2 + Vector3.forward * radius, ray.direction * length);
|
||||
Gizmos.DrawRay(pt2 - Vector3.forward * radius, ray.direction * length);
|
||||
|
||||
PopColor();
|
||||
PopMatrix();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void Capsule2DcastGizmo(Ray ray, float length, Quaternion rotation, float radius, float height, bool isObstructed) {
|
||||
#if UNITY_EDITOR
|
||||
PushMatrix(Matrix4x4.TRS(ray.origin, rotation, Vector3.one));
|
||||
PushColor(isObstructed ? STPrefs.CastingBlockedRayColour : STPrefs.CastingShapeColour);
|
||||
|
||||
height = Mathf.Max(radius * 2, height);
|
||||
|
||||
Capsule2DGizmo(Vector3.zero, radius, height);
|
||||
|
||||
SetColor(isObstructed ? STPrefs.CastingBlockedRayColour : STPrefs.CastingRayColour);
|
||||
Capsule2DGizmo(ray.direction * length, radius, height);
|
||||
|
||||
var pt1 = Vector3.up * (height / 2f - radius);
|
||||
var pt2 = -pt1;
|
||||
|
||||
Gizmos.DrawRay(pt1 + Vector3.right * radius, ray.direction * length);
|
||||
Gizmos.DrawRay(pt1 - Vector3.right * radius, ray.direction * length);
|
||||
Gizmos.DrawRay(pt1 + Vector3.up * radius, ray.direction * length);
|
||||
|
||||
Gizmos.DrawRay(pt2 + Vector3.right * radius, ray.direction * length);
|
||||
Gizmos.DrawRay(pt2 - Vector3.right * radius, ray.direction * length);
|
||||
Gizmos.DrawRay(pt2 - Vector3.up * radius, ray.direction * length);
|
||||
|
||||
PopColor();
|
||||
PopMatrix();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
12
Assets/SensorToolkit/Sensors/src/Gizmos/SensorGizmos.cs.meta
Normal file
12
Assets/SensorToolkit/Sensors/src/Gizmos/SensorGizmos.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: baf797366091494483913e6f9dbe701a
|
||||
timeCreated: 1601249413
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
59
Assets/SensorToolkit/Sensors/src/IRayCastingSensor.cs
Normal file
59
Assets/SensorToolkit/Sensors/src/IRayCastingSensor.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace Micosmo.SensorToolkit {
|
||||
|
||||
public interface IRayCastingSensor {
|
||||
|
||||
RayHit GetDetectionRayHit(GameObject detectedGameObject);
|
||||
|
||||
bool IsObstructed { get; }
|
||||
RayHit GetObstructionRayHit();
|
||||
|
||||
// Event fired at the time the sensor is obstructed when before it was unobstructed
|
||||
ObstructionEvent OnObstruction { get; }
|
||||
|
||||
// Event fired at the time the sensor is unobstructed when before it was obstructed
|
||||
ObstructionEvent OnClear { get; }
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class ObstructionEvent : UnityEvent<IRayCastingSensor> { }
|
||||
|
||||
/**
|
||||
* A common representation for ray hits that combines both RaycastHit and
|
||||
* RaycastHit2D. This exists to provide some consistency between the RaySensor
|
||||
* and RaySensor2D interfaces, and also the Arc Sensors.
|
||||
*/
|
||||
public struct RayHit : IEquatable<RayHit> {
|
||||
public static RayHit None => new RayHit() { Distance = -1 };
|
||||
|
||||
public bool IsObstructing;
|
||||
public Vector3 Point;
|
||||
public Vector3 Normal;
|
||||
public float Distance;
|
||||
public float DistanceFraction;
|
||||
|
||||
public Collider Collider;
|
||||
public Collider2D Collider2D;
|
||||
|
||||
public GameObject GameObject =>
|
||||
Collider != null ? Collider.gameObject :
|
||||
Collider2D != null ? Collider2D.gameObject :
|
||||
null;
|
||||
|
||||
public bool Equals(RayHit other) {
|
||||
return IsObstructing == other.IsObstructing &&
|
||||
Point.Equals(other.Point) &&
|
||||
Normal.Equals(other.Normal) &&
|
||||
Distance == other.Distance &&
|
||||
DistanceFraction == other.DistanceFraction &&
|
||||
Collider == other.Collider &&
|
||||
Collider2D == other.Collider2D;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
11
Assets/SensorToolkit/Sensors/src/IRayCastingSensor.cs.meta
Normal file
11
Assets/SensorToolkit/Sensors/src/IRayCastingSensor.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bc5e680df93e41b38a1cf4905da84b14
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
31
Assets/SensorToolkit/Sensors/src/ISteeringSensor.cs
Normal file
31
Assets/SensorToolkit/Sensors/src/ISteeringSensor.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Micosmo.SensorToolkit {
|
||||
|
||||
public interface ISteeringSensor {
|
||||
GameObject gameObject { get; }
|
||||
Transform transform { get; }
|
||||
SteerSeek Seek { get; }
|
||||
SteerInterest Interest { get; }
|
||||
SteerDanger Danger { get; }
|
||||
SteerVO Velocity { get; }
|
||||
SteerDecision Decision { get; }
|
||||
LocomotionSystem Locomotion { get; }
|
||||
|
||||
bool IsDestinationReached { get; }
|
||||
bool IsSeeking { get; }
|
||||
|
||||
void SeekTo(Transform destination, float distanceOffset = 0f);
|
||||
void SeekTo(Vector3 destination, float distanceOffset = 0f);
|
||||
void ArriveTo(Transform destination, float distanceOffset = 0f);
|
||||
void ArriveTo(Vector3 destination, float distanceOffset = 0f);
|
||||
void SeekDirection(Vector3 direction);
|
||||
void Wander();
|
||||
void Stop();
|
||||
Vector3 GetSteeringVector();
|
||||
float GetSpeedCandidate(Vector3 direction);
|
||||
}
|
||||
|
||||
}
|
||||
11
Assets/SensorToolkit/Sensors/src/ISteeringSensor.cs.meta
Normal file
11
Assets/SensorToolkit/Sensors/src/ISteeringSensor.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dce203f51e872c14bbbfd442e90725fa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/SensorToolkit/Sensors/src/LOS.meta
Normal file
8
Assets/SensorToolkit/Sensors/src/LOS.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 10c336ad5c4c43b4f86ea32f414cad03
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
215
Assets/SensorToolkit/Sensors/src/LOS/AngleUtils.cs
Normal file
215
Assets/SensorToolkit/Sensors/src/LOS/AngleUtils.cs
Normal file
@@ -0,0 +1,215 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Micosmo.SensorToolkit {
|
||||
[System.Serializable]
|
||||
public struct ReferenceFrame {
|
||||
public Vector3 Position;
|
||||
public Vector3 Forward, Right, Up;
|
||||
public ReferenceFrame(Vector3 position, Vector3 forward, Vector3 right, Vector3 up) {
|
||||
Position = position; Forward = forward; Right = right; Up = up;
|
||||
}
|
||||
public static ReferenceFrame Identity => new ReferenceFrame { Forward = Vector3.forward, Right = Vector3.right, Up = Vector3.up };
|
||||
public static ReferenceFrame Planar(Vector3 position, Vector3 forward, Vector3 right) =>
|
||||
new ReferenceFrame(position, forward, right, default);
|
||||
public static ReferenceFrame From(Transform transform) =>
|
||||
new ReferenceFrame(transform.position, transform.forward, transform.right, transform.up);
|
||||
public static ReferenceFrame From(Vector3 position, Quaternion rotation) =>
|
||||
new ReferenceFrame(position, rotation * Vector3.forward, rotation * Vector3.right, rotation * Vector3.up);
|
||||
public static ReferenceFrame From(Transform transform, Vector2 horizDir) {
|
||||
var forward = transform.forward;
|
||||
if (horizDir == Vector2.zero) {
|
||||
horizDir = Vector2.right;
|
||||
}
|
||||
horizDir = horizDir.normalized;
|
||||
var right = horizDir.x * transform.right + horizDir.y * transform.up;
|
||||
var up = Vector3.Cross(forward, right);
|
||||
return new ReferenceFrame(transform.position, forward, right, up);
|
||||
}
|
||||
public ViewAngles AngleTo(Bounds bounds) => AngleUtils.ViewAnglesToBounds(this, bounds);
|
||||
public SphericalCoords SphericalCoordsTo(Bounds bounds) => AngleUtils.SphericalCoordsToBounds(this, bounds);
|
||||
public ViewAngles AngleTo(Vector3 target) => AngleUtils.ViewAnglesToPoint(this, target);
|
||||
public SphericalCoords SphericalCoordsTo(Vector3 target) => AngleUtils.SphericalCoordsToPoint(this, target);
|
||||
public ReferenceFrame Push(Vector3 nextPosition, Vector3 nextForward) {
|
||||
// Minimizes twist rotation to push the frame forward and align with a new forward direction
|
||||
var n0 = Up;
|
||||
var t0 = Forward;
|
||||
var t1 = nextForward.normalized;
|
||||
var v1 = nextPosition - Position;
|
||||
var c1 = v1.sqrMagnitude;
|
||||
var n0_l = n0 - (2 / c1) * Vector3.Dot(v1, n0) * v1;
|
||||
var t0_l = t0 - (2 / c1) * Vector3.Dot(v1, t0) * v1;
|
||||
var v2 = t1 - t0_l;
|
||||
var c2 = v2.sqrMagnitude;
|
||||
var n1 = n0_l - (2 / c2) * Vector3.Dot(v2, n0_l) * v2;
|
||||
return From(nextPosition, Quaternion.LookRotation(t1, n1));
|
||||
}
|
||||
public Vector3 LocalToWorld(Vector3 localPosition) => Position + (Right * localPosition.x) + (Up * localPosition.y) + (Forward * localPosition.z);
|
||||
public Vector3 WorldToLocal(Vector3 worldPosition) {
|
||||
var delta = worldPosition - Position;
|
||||
return new Vector3(Vector3.Dot(delta, Right), Vector3.Dot(delta, Up), Vector3.Dot(delta, Forward));
|
||||
}
|
||||
public void DrawGizmos(float size) {
|
||||
var length = 10f * size;
|
||||
var thickness = 2f * size;
|
||||
var that = this;
|
||||
SensorGizmos.WithColor(Color.blue, () => SensorGizmos.ThickLineNoZTest(that.Position, that.Position + that.Forward * length, thickness));
|
||||
SensorGizmos.WithColor(Color.red, () => SensorGizmos.ThickLineNoZTest(that.Position, that.Position + that.Right * length, thickness));
|
||||
SensorGizmos.WithColor(Color.green, () => SensorGizmos.ThickLineNoZTest(that.Position, that.Position + that.Up * length, thickness));
|
||||
}
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public struct ViewAngles {
|
||||
public float HorizAngle;
|
||||
public float VertAngle;
|
||||
public ViewAngles Abs => new ViewAngles(Mathf.Abs(HorizAngle), Mathf.Abs(VertAngle));
|
||||
public ViewAngles(float horizAngle, float vertAngle) {
|
||||
HorizAngle = horizAngle; VertAngle = vertAngle;
|
||||
}
|
||||
public float GetCentralAngle() {
|
||||
var horiz = HorizAngle * Mathf.Deg2Rad;
|
||||
var vert = VertAngle * Mathf.Deg2Rad;
|
||||
return Mathf.Rad2Deg * Mathf.Acos(Mathf.Cos(horiz) * Mathf.Cos(vert));
|
||||
}
|
||||
public Vector3 ToCartesian(float distance) {
|
||||
var horiz = HorizAngle * Mathf.Deg2Rad;
|
||||
var vert = VertAngle * Mathf.Deg2Rad;
|
||||
var cosVert = Mathf.Cos(vert);
|
||||
var sinVert = Mathf.Sin(vert);
|
||||
return new Vector3(cosVert * Mathf.Sin(horiz), sinVert, cosVert * Mathf.Cos(horiz)) * distance;
|
||||
}
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public struct SphericalCoords {
|
||||
public ViewAngles Angles;
|
||||
public float Radius;
|
||||
public SphericalCoords(ViewAngles angles, float radius) {
|
||||
Angles = angles; Radius = radius;
|
||||
}
|
||||
public Vector3 ToCartesian() => Angles.ToCartesian(Radius);
|
||||
}
|
||||
|
||||
public class AngleUtils {
|
||||
|
||||
public static float PlanarAngleToPoint(ReferenceFrame frame, Vector3 target) {
|
||||
var delta = (target - frame.Position);
|
||||
var proj = Vector3.Dot(delta, frame.Right);
|
||||
var dist = Vector3.Dot(delta, frame.Forward);
|
||||
var tri = new CircleInscribedTriangle(new Vector2(proj, dist));
|
||||
return tri.GetAngle();
|
||||
}
|
||||
|
||||
public static float PlanarAngleToBounds(ReferenceFrame frame, Bounds bounds) {
|
||||
var center = bounds.center;
|
||||
var extents = bounds.extents;
|
||||
CircleInscribedTriangle cwTri = default;
|
||||
CircleInscribedTriangle acwTri = default;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
var xSign = (i & 1) == 0 ? 1 : -1;
|
||||
var ySign = (i & 2) == 0 ? 1 : -1;
|
||||
var zSign = (i & 4) == 0 ? 1 : -1;
|
||||
var point = center + new Vector3(extents.x * xSign, extents.y * ySign, extents.z * zSign);
|
||||
|
||||
var delta = (point - frame.Position);
|
||||
var proj = Vector3.Dot(delta, frame.Right);
|
||||
var dist = Vector3.Dot(delta, frame.Forward);
|
||||
|
||||
var tri = new CircleInscribedTriangle(new Vector2(proj, dist));
|
||||
|
||||
cwTri = CircleInscribedTriangle.NearestClockwise(cwTri, tri);
|
||||
acwTri = CircleInscribedTriangle.NearestAntiClockwise(acwTri, tri);
|
||||
}
|
||||
|
||||
var a1 = cwTri.GetAngle();
|
||||
var a2 = acwTri.GetAngle();
|
||||
|
||||
if (a1 >= 0 && a2 <= 0 && (a1 - a2 < 180f)) {
|
||||
return 0f;
|
||||
}
|
||||
|
||||
var nearest = (Mathf.Abs(a1) < Mathf.Abs(a2)) ? a1 : a2;
|
||||
return nearest;
|
||||
}
|
||||
|
||||
public static ViewAngles ViewAnglesToPoint(ReferenceFrame frame, Vector3 target) {
|
||||
var horizAngle = PlanarAngleToPoint(frame, target);
|
||||
var toTarget = target - frame.Position;
|
||||
var projToTarget = toTarget - (Vector3.Dot(toTarget, frame.Up) * frame.Up);
|
||||
var vertAngle = PlanarAngleToPoint(ReferenceFrame.Planar(frame.Position, projToTarget.normalized, frame.Up), target);
|
||||
return new ViewAngles(horizAngle, vertAngle);
|
||||
}
|
||||
|
||||
public static SphericalCoords SphericalCoordsToPoint(ReferenceFrame frame, Vector3 target) {
|
||||
var angles = ViewAnglesToPoint(frame, target);
|
||||
var radius = Vector3.Distance(frame.Position, target);
|
||||
return new SphericalCoords(angles, radius);
|
||||
}
|
||||
|
||||
public static ViewAngles ViewAnglesToBounds(ReferenceFrame frame, Bounds bounds) {
|
||||
var horizAngle = PlanarAngleToBounds(frame, bounds);
|
||||
var toTarget = (bounds.center - frame.Position);
|
||||
var projToTarget = toTarget - (Vector3.Dot(toTarget, frame.Up) * frame.Up);
|
||||
var vertAngle = PlanarAngleToBounds(ReferenceFrame.Planar(frame.Position, projToTarget.normalized, frame.Up), bounds);
|
||||
return new ViewAngles(horizAngle, vertAngle);
|
||||
}
|
||||
|
||||
public static SphericalCoords SphericalCoordsToBounds(ReferenceFrame frame, Bounds bounds) {
|
||||
var angles = ViewAnglesToBounds(frame, bounds);
|
||||
var radius = Mathf.Sqrt(bounds.SqrDistance(frame.Position));
|
||||
return new SphericalCoords(angles, radius);
|
||||
}
|
||||
|
||||
struct CircleInscribedTriangle {
|
||||
Vector2 coords;
|
||||
bool isValid => coords != Vector2.zero;
|
||||
public CircleInscribedTriangle(Vector2 coords) {
|
||||
this.coords = coords.normalized;
|
||||
}
|
||||
public float GetAngle() => -Mathf.Atan2(-coords.x, coords.y) * Mathf.Rad2Deg;
|
||||
public static CircleInscribedTriangle NearestClockwise(CircleInscribedTriangle tri1, CircleInscribedTriangle tri2) {
|
||||
if (!tri1.isValid) {
|
||||
return tri2;
|
||||
} else if (!tri2.isValid) {
|
||||
return tri1;
|
||||
}
|
||||
return tri1.IsNearestClockwise(tri2) ? tri1 : tri2;
|
||||
}
|
||||
public static CircleInscribedTriangle NearestAntiClockwise(CircleInscribedTriangle tri1, CircleInscribedTriangle tri2) {
|
||||
if (!tri1.isValid) {
|
||||
return tri2;
|
||||
} else if (!tri2.isValid) {
|
||||
return tri1;
|
||||
}
|
||||
return tri1.IsNearestClockwise(tri2) ? tri2 : tri1;
|
||||
}
|
||||
int GetClockwiseQuadrant() => coords.x > 0 ? (coords.y > 0 ? 0 : 1) : (coords.y > 0 ? 3 : 2);
|
||||
int GetAntiClockwiseQuadrant() => 3 - GetClockwiseQuadrant();
|
||||
bool IsNearestClockwise(CircleInscribedTriangle other) {
|
||||
var myQuad = GetClockwiseQuadrant();
|
||||
var otherQuad = other.GetClockwiseQuadrant();
|
||||
if (myQuad < otherQuad) {
|
||||
return true;
|
||||
} else if (myQuad > otherQuad) {
|
||||
return false;
|
||||
}
|
||||
switch (myQuad) {
|
||||
case 0:
|
||||
return coords.x < other.coords.x;
|
||||
case 1:
|
||||
return coords.y > other.coords.y;
|
||||
case 2:
|
||||
return coords.x > other.coords.x;
|
||||
case 3:
|
||||
return coords.y < other.coords.y;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
11
Assets/SensorToolkit/Sensors/src/LOS/AngleUtils.cs.meta
Normal file
11
Assets/SensorToolkit/Sensors/src/LOS/AngleUtils.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bcd6b545e44c45f48b19c363eb6497d5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
231
Assets/SensorToolkit/Sensors/src/LOS/BaseLOSTest.cs
Normal file
231
Assets/SensorToolkit/Sensors/src/LOS/BaseLOSTest.cs
Normal file
@@ -0,0 +1,231 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Micosmo.SensorToolkit {
|
||||
|
||||
public interface ILOSResult {
|
||||
Signal OutputSignal { get; }
|
||||
float Visibility { get; }
|
||||
bool IsVisible { get; }
|
||||
List<LOSRayResult> Rays { get; }
|
||||
}
|
||||
|
||||
public struct LOSRayResult {
|
||||
public Vector3 OriginPoint;
|
||||
public Vector3 TargetPoint;
|
||||
public Transform TargetTransform;
|
||||
public RayHit RayHit;
|
||||
public float VisibilityMultiplier;
|
||||
public bool IsObstructed => RayHit.IsObstructing;
|
||||
public float Visibility => IsObstructed ? 0f : VisibilityMultiplier;
|
||||
}
|
||||
|
||||
public enum ScalingMode { Step, LinearDecay, Curve }
|
||||
[System.Serializable]
|
||||
public struct ScalingFunction {
|
||||
public ScalingMode Mode;
|
||||
public AnimationCurve Curve;
|
||||
|
||||
public float Evaluate(float t) {
|
||||
if (Mode == ScalingMode.Step) {
|
||||
return t < 1f ? 1f : 0f;
|
||||
} if (Mode == ScalingMode.LinearDecay) {
|
||||
return 1f - Mathf.Clamp01(t);
|
||||
} else {
|
||||
return Curve.Evaluate(Mathf.Clamp01(t));
|
||||
}
|
||||
}
|
||||
|
||||
public static ScalingFunction Default() =>
|
||||
new ScalingFunction() {
|
||||
Mode = ScalingMode.Step,
|
||||
Curve = new AnimationCurve(new Keyframe(0,1), new Keyframe(.5f,1), new Keyframe(1f,0))
|
||||
};
|
||||
}
|
||||
|
||||
public enum FOVConstraintMethod { BoundingBox, PerRay }
|
||||
|
||||
public enum PointSamplingMethod { Fast, Quality }
|
||||
|
||||
public abstract class BaseLOSTest : ILOSResult {
|
||||
public class ConfigParams {
|
||||
public Signal InputSignal;
|
||||
public List<Collider> OwnedColliders;
|
||||
public List<Collider2D> OwnedCollider2Ds;
|
||||
|
||||
public ReferenceFrame Frame;
|
||||
|
||||
public float MinimumVisibility;
|
||||
|
||||
public LayerMask BlocksLineOfSight;
|
||||
public bool IgnoreTriggerColliders;
|
||||
public PointSamplingMethod PointSamplingMethod;
|
||||
public bool TestLOSTargetsOnly;
|
||||
public int NumberOfRays;
|
||||
|
||||
public bool MovingAverageEnabled;
|
||||
public int MovingAverageWindowSize;
|
||||
|
||||
public bool LimitDistance;
|
||||
public float MaxDistance;
|
||||
public ScalingFunction VisibilityByDistance;
|
||||
|
||||
public bool LimitViewAngle;
|
||||
public float MaxHorizAngle;
|
||||
public ScalingFunction VisibilityByHorizAngle;
|
||||
public float MaxVertAngle;
|
||||
public ScalingFunction VisibilityByVertAngle;
|
||||
|
||||
public FOVConstraintMethod FOVConstraintMethod;
|
||||
}
|
||||
public ConfigParams Config => config;
|
||||
protected ConfigParams config = new ConfigParams();
|
||||
|
||||
public Signal OutputSignal { get; private set; }
|
||||
public float Visibility { get; private set; }
|
||||
public bool IsVisible { get; private set; } // Possible to have Visibility=0 and still be visible
|
||||
public List<LOSRayResult> Rays { get; } = new List<LOSRayResult>();
|
||||
|
||||
Signal prevInputSignal;
|
||||
ComponentCache losTargetsCache;
|
||||
List<Vector3> generatedPoints = new List<Vector3>();
|
||||
MovingAverageFilter avgFilter = new MovingAverageFilter(1);
|
||||
|
||||
public void Reset() {
|
||||
avgFilter.Clear();
|
||||
Rays.Clear();
|
||||
generatedPoints.Clear();
|
||||
Visibility = 0f;
|
||||
Clear();
|
||||
}
|
||||
|
||||
public bool PerformTest() {
|
||||
Rays.Clear();
|
||||
generatedPoints.Clear();
|
||||
Clear();
|
||||
|
||||
// Need to initialize output signal so .Object is populated
|
||||
OutputSignal = new Signal() {
|
||||
Object = config.InputSignal.Object,
|
||||
Shape = config.InputSignal.Shape,
|
||||
Strength = 0f
|
||||
};
|
||||
IsVisible = false;
|
||||
Visibility = 0f;
|
||||
if (!ReferenceEquals(config.InputSignal.Object, prevInputSignal.Object)) {
|
||||
avgFilter.Clear();
|
||||
}
|
||||
prevInputSignal = config.InputSignal;
|
||||
|
||||
var visibilityScale = Config.FOVConstraintMethod == FOVConstraintMethod.BoundingBox ? GetVisibilityScale() : 1f;
|
||||
if (visibilityScale <= 0f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var isUsingGeneratedPoints = false;
|
||||
|
||||
var losTargets = losTargetsCache.GetComponent<LOSTargets>(config.InputSignal.Object);
|
||||
if (losTargets == null || losTargets.Targets == null || losTargets.Targets.Count == 0) {
|
||||
if (config.TestLOSTargetsOnly) {
|
||||
return IsVisible;
|
||||
}
|
||||
if (IsInsideSignal()) {
|
||||
// If I'm inside the bounds of the target signal then I can see it.
|
||||
Visibility = 1f;
|
||||
IsVisible = true;
|
||||
OutputSignal = new Signal {
|
||||
Object = config.InputSignal.Object,
|
||||
Shape = config.InputSignal.Shape,
|
||||
Strength = config.InputSignal.Strength
|
||||
};
|
||||
return IsVisible;
|
||||
}
|
||||
GenerateTestPoints(generatedPoints);
|
||||
isUsingGeneratedPoints = true;
|
||||
if (generatedPoints.Count == 0) {
|
||||
return false; // Couldn't generate any testpoints, therefore not visible.
|
||||
}
|
||||
}
|
||||
|
||||
if (isUsingGeneratedPoints) {
|
||||
foreach (var pt in generatedPoints) {
|
||||
var trans = config.InputSignal.Object.transform;
|
||||
var result = TestPoint(pt);
|
||||
result.VisibilityMultiplier = visibilityScale;
|
||||
if (Config.FOVConstraintMethod == FOVConstraintMethod.PerRay) {
|
||||
result.VisibilityMultiplier *= GetRayVisibilityScale(pt);
|
||||
}
|
||||
Rays.Add(result);
|
||||
}
|
||||
} else {
|
||||
foreach (var target in losTargets.Targets) {
|
||||
var result = TestTransform(target);
|
||||
result.VisibilityMultiplier = visibilityScale;
|
||||
if (Config.FOVConstraintMethod == FOVConstraintMethod.PerRay) {
|
||||
result.VisibilityMultiplier *= GetRayVisibilityScale(target.position);
|
||||
}
|
||||
Rays.Add(result);
|
||||
}
|
||||
}
|
||||
|
||||
var rayVisibilitySum = 0f;
|
||||
foreach (var ray in Rays) {
|
||||
rayVisibilitySum += ray.Visibility;
|
||||
}
|
||||
Visibility = (rayVisibilitySum / Rays.Count);
|
||||
|
||||
if (config.MovingAverageEnabled) {
|
||||
avgFilter.Size = config.MovingAverageWindowSize;
|
||||
avgFilter.AddSample(Visibility);
|
||||
Visibility = avgFilter.Value;
|
||||
} else {
|
||||
avgFilter.Clear();
|
||||
}
|
||||
|
||||
IsVisible = Visibility >= config.MinimumVisibility;
|
||||
|
||||
OutputSignal = new Signal() {
|
||||
Object = config.InputSignal.Object,
|
||||
Shape = config.InputSignal.Shape,
|
||||
Strength = IsVisible ? config.InputSignal.Strength * Visibility : 0f
|
||||
};
|
||||
|
||||
return IsVisible;
|
||||
}
|
||||
|
||||
public virtual void DrawGizmos() {
|
||||
foreach (var result in Rays) {
|
||||
Gizmos.color = SensorGizmos.LerpColour(STPrefs.RayVisibilityGradient, 1f - result.VisibilityMultiplier);
|
||||
if (result.IsObstructed) {
|
||||
Gizmos.DrawLine(result.OriginPoint, result.RayHit.Point);
|
||||
Gizmos.color = STPrefs.CastingBlockedRayColour;
|
||||
Gizmos.DrawLine(result.RayHit.Point, result.TargetPoint);
|
||||
Gizmos.DrawCube(result.TargetPoint, Vector3.one * 0.02f);
|
||||
} else {
|
||||
Gizmos.DrawLine(result.OriginPoint, result.TargetPoint);
|
||||
Gizmos.DrawCube(result.TargetPoint, Vector3.one * 0.02f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOSRayResult TestTransform(Transform testTransform) {
|
||||
var result = TestPoint(testTransform.position);
|
||||
result.TargetTransform = testTransform;
|
||||
return result;
|
||||
}
|
||||
|
||||
protected abstract LOSRayResult TestPoint(Vector3 testPoint);
|
||||
|
||||
protected abstract bool IsInsideSignal();
|
||||
|
||||
protected abstract void GenerateTestPoints(List<Vector3> storeIn);
|
||||
|
||||
protected abstract float GetVisibilityScale();
|
||||
|
||||
protected abstract float GetRayVisibilityScale(Vector3 target);
|
||||
|
||||
protected abstract void Clear();
|
||||
}
|
||||
|
||||
}
|
||||
11
Assets/SensorToolkit/Sensors/src/LOS/BaseLOSTest.cs.meta
Normal file
11
Assets/SensorToolkit/Sensors/src/LOS/BaseLOSTest.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5d1cad1996ab8c24b825fcc8b2af9f7d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
81
Assets/SensorToolkit/Sensors/src/LOS/CuttingPlane.cs
Normal file
81
Assets/SensorToolkit/Sensors/src/LOS/CuttingPlane.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Micosmo.SensorToolkit {
|
||||
|
||||
public struct CuttingPlane {
|
||||
public Vector3 Point;
|
||||
public Vector3 Normal;
|
||||
|
||||
public void Cut(List<Triangle> triangleList) {
|
||||
for (int i = triangleList.Count - 1; i >= 0; i--) {
|
||||
var tri = triangleList[i];
|
||||
Triangle slice1, slice2;
|
||||
var nSlices = tri.Slice(Point, Normal, out slice1, out slice2);
|
||||
if (nSlices == 0) {
|
||||
triangleList.RemoveAt(i);
|
||||
} else {
|
||||
triangleList[i] = slice1;
|
||||
if (nSlices > 1) {
|
||||
triangleList.Add(slice2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Cut(List<Edge2D> edgeList) {
|
||||
for (int i = edgeList.Count - 1; i >= 0; i--) {
|
||||
var edge = edgeList[i];
|
||||
Edge2D slicedEdge;
|
||||
var nSlices = edge.Slice(Point, ((Vector2)Normal).normalized, out slicedEdge);
|
||||
if (nSlices == 0) {
|
||||
edgeList.RemoveAt(i);
|
||||
} else {
|
||||
edgeList[i] = slicedEdge;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct FOVCuttingPlanes {
|
||||
CuttingPlane rightPlane, leftPlane, topPlane, bottomPlane;
|
||||
|
||||
public static FOVCuttingPlanes From(ReferenceFrame frame, FOVRange fov) {
|
||||
var horizRightRot = Quaternion.AngleAxis(fov.HorizAngle / 2f, frame.Up);
|
||||
var horizLeftRot = Quaternion.Inverse(horizRightRot);
|
||||
var vertUpRot = Quaternion.AngleAxis(fov.VertAngle / 2f, frame.Right);
|
||||
var vertDownRot = Quaternion.Inverse(vertUpRot);
|
||||
return new FOVCuttingPlanes {
|
||||
rightPlane = new CuttingPlane { Point = frame.Position, Normal = horizRightRot * (-frame.Right) },
|
||||
leftPlane = new CuttingPlane { Point = frame.Position, Normal = horizLeftRot * frame.Right },
|
||||
topPlane = new CuttingPlane { Point = frame.Position, Normal = vertUpRot * frame.Up },
|
||||
bottomPlane = new CuttingPlane { Point = frame.Position, Normal = vertDownRot * (-frame.Up) }
|
||||
};
|
||||
}
|
||||
public void Clip(List<Triangle> triangles) {
|
||||
rightPlane.Cut(triangles);
|
||||
leftPlane.Cut(triangles);
|
||||
topPlane.Cut(triangles);
|
||||
bottomPlane.Cut(triangles);
|
||||
}
|
||||
}
|
||||
|
||||
public struct FOVCuttingPlanes2D {
|
||||
CuttingPlane rightPlane, leftPlane;
|
||||
|
||||
public static FOVCuttingPlanes2D From(ReferenceFrame frame, FOVRange2D fov) {
|
||||
var rightRot = Quaternion.AngleAxis(fov.Angle / 2f, Vector3.back);
|
||||
var leftRot = Quaternion.Inverse(rightRot);
|
||||
return new FOVCuttingPlanes2D {
|
||||
rightPlane = new CuttingPlane { Point = frame.Position, Normal = rightRot * (-frame.Right) },
|
||||
leftPlane = new CuttingPlane { Point = frame.Position, Normal = leftRot * (frame.Right) }
|
||||
};
|
||||
}
|
||||
public void Clip(List<Edge2D> edges) {
|
||||
rightPlane.Cut(edges);
|
||||
leftPlane.Cut(edges);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
11
Assets/SensorToolkit/Sensors/src/LOS/CuttingPlane.cs.meta
Normal file
11
Assets/SensorToolkit/Sensors/src/LOS/CuttingPlane.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cc6af1cba09a80b43a3b1d78c9e42d26
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
75
Assets/SensorToolkit/Sensors/src/LOS/Edge2D.cs
Normal file
75
Assets/SensorToolkit/Sensors/src/LOS/Edge2D.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Micosmo.SensorToolkit {
|
||||
|
||||
[System.Serializable]
|
||||
public struct Edge2D {
|
||||
public Vector2 P1, P2;
|
||||
|
||||
public Edge2D(Vector2 p1, Vector2 p2) {
|
||||
P1 = p1; P2 = p2;
|
||||
}
|
||||
|
||||
public float GetLength() => (P2 - P1).magnitude;
|
||||
|
||||
public Edge2D ProjectCircle(Vector2 origin) => new Edge2D((P1 - origin).normalized + origin, (P2 - origin).normalized + origin);
|
||||
|
||||
public Vector2 GetRandomPoint(Vector3 sobolPosition) => Vector2.Lerp(P1, P2, sobolPosition.x);
|
||||
|
||||
public void DrawGizmos(float z) {
|
||||
Gizmos.DrawLine(new Vector3(P1.x, P1.y, z), new Vector3(P2.x, P2.y, z));
|
||||
}
|
||||
|
||||
public int Slice(Vector2 linePoint, Vector2 lineNormal, out Edge2D slice) {
|
||||
slice = default;
|
||||
|
||||
var plane = new Plane(lineNormal, linePoint);
|
||||
|
||||
var p1Dist = plane.GetDistanceToPoint(P1);
|
||||
var isP1Inside = p1Dist >= 0f;
|
||||
var p2Dist = plane.GetDistanceToPoint(P2);
|
||||
var isP2Inside = p2Dist >= 0f;
|
||||
|
||||
if (isP1Inside && isP2Inside) {
|
||||
slice = this;
|
||||
return 1;
|
||||
} else if (!isP1Inside && !isP2Inside) {
|
||||
return 0;
|
||||
}
|
||||
Vector2 intersectPoint;
|
||||
EdgePlaneIntersection(out intersectPoint, this, lineNormal, linePoint);
|
||||
slice = new Edge2D(isP1Inside ? P1 : P2, intersectPoint);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool EdgePlaneIntersection(out Vector2 intersection, Edge2D edge, Vector2 planeNormal, Vector2 planePoint) {
|
||||
float length;
|
||||
float dotNumerator;
|
||||
float dotDenominator;
|
||||
Vector2 vector;
|
||||
intersection = Vector3.zero;
|
||||
|
||||
var lineVec = (edge.P2 - edge.P1).normalized;
|
||||
|
||||
dotNumerator = Vector3.Dot((planePoint - edge.P1), planeNormal);
|
||||
dotDenominator = Vector3.Dot(lineVec, planeNormal);
|
||||
|
||||
if (dotDenominator != 0.0f) {
|
||||
length = dotNumerator / dotDenominator;
|
||||
vector = SetVectorLength(lineVec, length);
|
||||
intersection = edge.P1 + vector;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static Vector2 SetVectorLength(Vector2 vector, float size) {
|
||||
Vector2 vectorNormalized = vector.normalized;
|
||||
return vectorNormalized *= size;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
11
Assets/SensorToolkit/Sensors/src/LOS/Edge2D.cs.meta
Normal file
11
Assets/SensorToolkit/Sensors/src/LOS/Edge2D.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2498b42cb1733234693569bc906db078
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
39
Assets/SensorToolkit/Sensors/src/LOS/FOVRange.cs
Normal file
39
Assets/SensorToolkit/Sensors/src/LOS/FOVRange.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Micosmo.SensorToolkit {
|
||||
|
||||
[System.Serializable]
|
||||
public struct FOVRange {
|
||||
public float HorizAngle;
|
||||
public float VertAngle;
|
||||
public float Distance;
|
||||
public static FOVRange Of(float horizAngle, float vertAngle, float distance = float.PositiveInfinity) => new FOVRange {
|
||||
HorizAngle = Mathf.Clamp(horizAngle, 0f, 180f),
|
||||
VertAngle = Mathf.Clamp(vertAngle, 0f, 180f),
|
||||
Distance = distance
|
||||
};
|
||||
public bool ContainsAngles(ViewAngles angles) =>
|
||||
Mathf.Abs(angles.HorizAngle) <= HorizAngle && Mathf.Abs(angles.VertAngle) <= VertAngle;
|
||||
public bool ContainsDistance(float distance) => distance <= Distance;
|
||||
public bool Contains(ViewAngles angles, float distance) =>
|
||||
ContainsAngles(angles) && ContainsDistance(distance);
|
||||
public void DrawGizmos(ReferenceFrame frame) {
|
||||
SensorGizmos.FOVGizmo(frame, float.IsInfinity(Distance) ? 1f : Distance, HorizAngle, VertAngle);
|
||||
}
|
||||
}
|
||||
|
||||
public struct FOVRange2D {
|
||||
public float Angle;
|
||||
public float Distance;
|
||||
public static FOVRange2D Of(float angle, float distance = float.PositiveInfinity) => new FOVRange2D {
|
||||
Angle = Mathf.Clamp(angle, 0f, 180f),
|
||||
Distance = distance
|
||||
};
|
||||
public void DrawGizmos(ReferenceFrame frame) {
|
||||
SensorGizmos.FOVGizmo(frame, float.IsInfinity(Distance) ? 1f : Distance, Angle, 0f);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
11
Assets/SensorToolkit/Sensors/src/LOS/FOVRange.cs.meta
Normal file
11
Assets/SensorToolkit/Sensors/src/LOS/FOVRange.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1b4f5e6cc15b72b418fa101403eb87dc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
181
Assets/SensorToolkit/Sensors/src/LOS/LOSTest2D.cs
Normal file
181
Assets/SensorToolkit/Sensors/src/LOS/LOSTest2D.cs
Normal file
@@ -0,0 +1,181 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Micosmo.SensorToolkit {
|
||||
|
||||
public class LOSTest2D : BaseLOSTest {
|
||||
|
||||
List<Edge2D> edges = new List<Edge2D>();
|
||||
List<Edge2D> projectedEdges = new List<Edge2D>();
|
||||
ComponentCache losColliderOwnerCache;
|
||||
SobolSequence3D sobol = new SobolSequence3D();
|
||||
|
||||
public override void DrawGizmos() {
|
||||
base.DrawGizmos();
|
||||
SensorGizmos.PushColor(Color.blue);
|
||||
foreach (var edge in edges) {
|
||||
edge.DrawGizmos(config.Frame.Position.z);
|
||||
}
|
||||
/*foreach (var edge in projectedEdges) {
|
||||
edge.DrawGizmos(Config.Origin.z);
|
||||
}*/
|
||||
SensorGizmos.PopColor();
|
||||
}
|
||||
|
||||
protected override void Clear() {
|
||||
edges.Clear();
|
||||
projectedEdges.Clear();
|
||||
}
|
||||
|
||||
protected override LOSRayResult TestPoint(Vector3 testPoint) {
|
||||
var saveQHT = Physics2D.queriesHitTriggers;
|
||||
Physics2D.queriesHitTriggers = !config.IgnoreTriggerColliders;
|
||||
var result = DoTest(testPoint);
|
||||
Physics2D.queriesHitTriggers = saveQHT;
|
||||
return result;
|
||||
}
|
||||
|
||||
LOSRayResult DoTest(Vector3 testPoint) {
|
||||
var delta = (Vector2)testPoint - (Vector2)config.Frame.Position;
|
||||
|
||||
var ray = new Ray(config.Frame.Position, delta.normalized);
|
||||
var result = new LOSRayResult() { OriginPoint = ray.origin, TargetPoint = testPoint, VisibilityMultiplier = 1f };
|
||||
var hitInfo = Physics2D.Raycast(ray.origin, ray.direction, delta.magnitude, config.BlocksLineOfSight);
|
||||
if (hitInfo.collider != null) {
|
||||
// Ray hit something, check that it was the target.
|
||||
var isTarget = (hitInfo.rigidbody != null && hitInfo.rigidbody.gameObject == config.InputSignal.Object)
|
||||
|| hitInfo.collider.gameObject == config.InputSignal.Object;
|
||||
|
||||
isTarget = isTarget || config.OwnedCollider2Ds.Contains(hitInfo.collider);
|
||||
var losColliderOwner = losColliderOwnerCache.GetComponent<LOSColliderOwner>(config.InputSignal.Object);
|
||||
if (losColliderOwner != null) {
|
||||
isTarget = isTarget || losColliderOwner.IsColliderOwner(hitInfo.collider);
|
||||
}
|
||||
|
||||
if (!isTarget) {
|
||||
result.RayHit = new RayHit() {
|
||||
IsObstructing = true,
|
||||
Point = hitInfo.point,
|
||||
Normal = hitInfo.normal,
|
||||
Distance = hitInfo.distance,
|
||||
DistanceFraction = hitInfo.distance / delta.magnitude,
|
||||
Collider2D = hitInfo.collider
|
||||
};
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override bool IsInsideSignal() {
|
||||
var origin = Config.Frame.Position;
|
||||
var bounds = Config.InputSignal.Bounds;
|
||||
origin.Set(origin.x, origin.y, bounds.center.z);
|
||||
return bounds.Contains(origin);
|
||||
}
|
||||
|
||||
protected override void GenerateTestPoints(List<Vector3> storeIn) {
|
||||
if (Config.PointSamplingMethod == PointSamplingMethod.Fast) {
|
||||
FastGenerateTestPoints(storeIn);
|
||||
} else if (Config.PointSamplingMethod == PointSamplingMethod.Quality) {
|
||||
QualityGenerateTestPoints(storeIn);
|
||||
}
|
||||
}
|
||||
|
||||
void FastGenerateTestPoints(List<Vector3> storeIn) {
|
||||
var bounds = Config.InputSignal.Bounds;
|
||||
for (int i = 0; i < Config.NumberOfRays; i++) {
|
||||
var nextSobol = sobol.Next();
|
||||
var random3 = new Vector3(Mathf.Lerp(-1, 1, nextSobol.x), Mathf.Lerp(-1, 1, nextSobol.y), Mathf.Lerp(-1, 1, nextSobol.z));
|
||||
random3 *= .9f;
|
||||
var randomPoint = bounds.center + Vector3.Scale(bounds.extents, random3);
|
||||
storeIn.Add(randomPoint);
|
||||
}
|
||||
}
|
||||
|
||||
void QualityGenerateTestPoints(List<Vector3> storeIn) {
|
||||
edges.Clear();
|
||||
projectedEdges.Clear();
|
||||
|
||||
var bounds = config.InputSignal.Bounds;
|
||||
bounds.center = (Vector2)bounds.center;
|
||||
LOSUtils.MapBoundsToEdges(config.Frame.Position, bounds, edges);
|
||||
|
||||
if (config.LimitViewAngle) {
|
||||
var fov = FOVRange2D.Of(config.MaxHorizAngle * 2f);
|
||||
FOVCuttingPlanes2D.From(Config.Frame, fov).Clip(edges);
|
||||
}
|
||||
if (edges.Count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var edge in edges) {
|
||||
projectedEdges.Add(edge.ProjectCircle(config.Frame.Position));
|
||||
}
|
||||
|
||||
for (int i = 0; i < config.NumberOfRays; i++) {
|
||||
int nAttempts = 0;
|
||||
Start:
|
||||
|
||||
var nextSobol = sobol.Next();
|
||||
var randomPoint = LOSUtils.GetRandomPointOnEdges(projectedEdges, nextSobol);
|
||||
|
||||
float boundsDist;
|
||||
var ray = new Ray((Vector2)config.Frame.Position, ((Vector2)(randomPoint - config.Frame.Position)).normalized);
|
||||
bounds.IntersectRay(ray, out boundsDist);
|
||||
|
||||
if (boundsDist == 0f) {
|
||||
if (nAttempts < 2) {
|
||||
// Very rarely the random point will be outside the bounds, try again.
|
||||
nAttempts++;
|
||||
goto Start;
|
||||
}
|
||||
// Tried three times and still no good. Ignore this point. Doubt this will ever happen. But don't want to
|
||||
// search forever in case there's a configuration that would cause infinite loops.
|
||||
continue;
|
||||
}
|
||||
|
||||
var intBoundsInPoint = ray.origin + ray.direction * boundsDist + new Vector3(0f, 0f, config.Frame.Position.z);
|
||||
var intBoundsOutPoint = LOSUtils.RaycastBoundsOutPoint(intBoundsInPoint, (intBoundsInPoint - Config.Frame.Position).normalized, bounds);
|
||||
|
||||
var midpoint = (intBoundsOutPoint + intBoundsInPoint) / 2f;
|
||||
var penetration = midpoint - intBoundsInPoint;
|
||||
|
||||
if (config.LimitDistance) {
|
||||
penetration = Vector3.ClampMagnitude(penetration, config.MaxDistance / 100f);
|
||||
}
|
||||
|
||||
storeIn.Add(intBoundsInPoint + penetration);
|
||||
}
|
||||
}
|
||||
|
||||
protected override float GetVisibilityScale() {
|
||||
var visibilityScale = 1f;
|
||||
if (config.LimitDistance) {
|
||||
var bounds = config.InputSignal.Bounds;
|
||||
bounds.center.Set(bounds.center.x, bounds.center.y, config.Frame.Position.z);
|
||||
float distance = Mathf.Sqrt((bounds.SqrDistance(config.Frame.Position)));
|
||||
visibilityScale *= config.VisibilityByDistance.Evaluate(distance / config.MaxDistance);
|
||||
}
|
||||
if (config.LimitViewAngle) {
|
||||
var horizAngle = Mathf.Abs(AngleUtils.PlanarAngleToBounds(config.Frame, Config.InputSignal.Bounds));
|
||||
visibilityScale *= config.VisibilityByHorizAngle.Evaluate(horizAngle / config.MaxHorizAngle);
|
||||
}
|
||||
return visibilityScale;
|
||||
}
|
||||
|
||||
protected override float GetRayVisibilityScale(Vector3 target) {
|
||||
var visibilityScale = 1f;
|
||||
if (config.LimitDistance) {
|
||||
float distance = (config.Frame.Position - target).magnitude;
|
||||
visibilityScale *= config.VisibilityByDistance.Evaluate(distance / config.MaxDistance);
|
||||
}
|
||||
if (config.LimitViewAngle) {
|
||||
var horizAngle = Mathf.Abs(AngleUtils.PlanarAngleToPoint(config.Frame, target));
|
||||
visibilityScale *= config.VisibilityByHorizAngle.Evaluate(horizAngle / config.MaxHorizAngle);
|
||||
}
|
||||
return visibilityScale;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
11
Assets/SensorToolkit/Sensors/src/LOS/LOSTest2D.cs.meta
Normal file
11
Assets/SensorToolkit/Sensors/src/LOS/LOSTest2D.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 724697f57b16047448504ede355a12e1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
171
Assets/SensorToolkit/Sensors/src/LOS/LOSTest3D.cs
Normal file
171
Assets/SensorToolkit/Sensors/src/LOS/LOSTest3D.cs
Normal file
@@ -0,0 +1,171 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Micosmo.SensorToolkit {
|
||||
|
||||
public class LOSTest3D : BaseLOSTest {
|
||||
|
||||
List<Triangle> triangles = new List<Triangle>();
|
||||
List<Triangle> projectedTriangles = new List<Triangle>();
|
||||
ComponentCache losColliderOwnerCache;
|
||||
SobolSequence3D sobol = new SobolSequence3D();
|
||||
|
||||
QueryTriggerInteraction queryTriggerInteraction => Config.IgnoreTriggerColliders
|
||||
? QueryTriggerInteraction.Ignore
|
||||
: QueryTriggerInteraction.Collide;
|
||||
|
||||
public override void DrawGizmos() {
|
||||
base.DrawGizmos();
|
||||
SensorGizmos.PushColor(Color.blue);
|
||||
foreach (var triangle in triangles) {
|
||||
triangle.DrawGizmos();
|
||||
}
|
||||
/*foreach (var triangle in projectedTriangles) {
|
||||
triangle.DrawGizmos();
|
||||
}*/
|
||||
SensorGizmos.PopColor();
|
||||
}
|
||||
|
||||
protected override void Clear() {
|
||||
triangles.Clear();
|
||||
projectedTriangles.Clear();
|
||||
}
|
||||
|
||||
protected override LOSRayResult TestPoint(Vector3 testPoint) {
|
||||
var delta = testPoint - config.Frame.Position;
|
||||
|
||||
var ray = new Ray(config.Frame.Position, delta.normalized);
|
||||
RaycastHit hitInfo;
|
||||
var result = new LOSRayResult() { OriginPoint = ray.origin, TargetPoint = testPoint, VisibilityMultiplier = 1f };
|
||||
if (Physics.Raycast(ray, out hitInfo, delta.magnitude, config.BlocksLineOfSight, queryTriggerInteraction)) {
|
||||
// Ray hit something, check that it was the target.
|
||||
var isTarget = (hitInfo.rigidbody != null && hitInfo.rigidbody.gameObject == config.InputSignal.Object)
|
||||
|| hitInfo.collider.gameObject == config.InputSignal.Object;
|
||||
|
||||
isTarget = isTarget || (config.OwnedColliders?.Contains(hitInfo.collider) ?? false);
|
||||
var losColliderOwner = losColliderOwnerCache.GetComponent<LOSColliderOwner>(config.InputSignal.Object);
|
||||
if (losColliderOwner != null) {
|
||||
isTarget = isTarget || losColliderOwner.IsColliderOwner(hitInfo.collider);
|
||||
}
|
||||
|
||||
if (!isTarget) {
|
||||
result.RayHit = new RayHit() {
|
||||
IsObstructing = true,
|
||||
Point = hitInfo.point,
|
||||
Normal = hitInfo.normal,
|
||||
Distance = hitInfo.distance,
|
||||
DistanceFraction = hitInfo.distance / delta.magnitude,
|
||||
Collider = hitInfo.collider
|
||||
};
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override bool IsInsideSignal() => Config.InputSignal.Bounds.Contains(Config.Frame.Position);
|
||||
|
||||
protected override void GenerateTestPoints(List<Vector3> storeIn) {
|
||||
if (Config.PointSamplingMethod == PointSamplingMethod.Fast) {
|
||||
FastGenerateTestPoints(storeIn);
|
||||
} else if (Config.PointSamplingMethod == PointSamplingMethod.Quality) {
|
||||
QualityGenerateTestPoints(storeIn);
|
||||
}
|
||||
}
|
||||
|
||||
void FastGenerateTestPoints(List<Vector3> storeIn) {
|
||||
var bounds = Config.InputSignal.Bounds;
|
||||
for (int i = 0; i < Config.NumberOfRays; i++) {
|
||||
var nextSobol = sobol.Next();
|
||||
var random3 = new Vector3(Mathf.Lerp(-1,1, nextSobol.x), Mathf.Lerp(-1, 1, nextSobol.y), Mathf.Lerp(-1, 1, nextSobol.z));
|
||||
random3 *= .9f;
|
||||
var randomPoint = bounds.center + Vector3.Scale(bounds.extents, random3);
|
||||
storeIn.Add(randomPoint);
|
||||
}
|
||||
}
|
||||
|
||||
void QualityGenerateTestPoints(List<Vector3> storeIn) {
|
||||
triangles.Clear();
|
||||
projectedTriangles.Clear();
|
||||
|
||||
var bounds = config.InputSignal.Bounds;
|
||||
LOSUtils.MapBoundsToTriangles(config.Frame.Position, bounds, triangles);
|
||||
|
||||
if (config.LimitViewAngle) {
|
||||
var fov = FOVRange.Of(config.MaxHorizAngle * 2f, config.MaxVertAngle * 2f);
|
||||
FOVCuttingPlanes.From(config.Frame, fov).Clip(triangles);
|
||||
}
|
||||
if (triangles.Count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var triangle in triangles) {
|
||||
projectedTriangles.Add(triangle.ProjectSphere(config.Frame.Position));
|
||||
}
|
||||
|
||||
for (int i = 0; i < config.NumberOfRays; i++) {
|
||||
int nAttempts = 0;
|
||||
Start:
|
||||
|
||||
var nextSobol = sobol.Next();
|
||||
var randomPoint = LOSUtils.GetRandomPointInTriangles(projectedTriangles, nextSobol);
|
||||
|
||||
float boundsDist;
|
||||
var ray = new Ray(config.Frame.Position, (randomPoint - config.Frame.Position).normalized);
|
||||
bounds.IntersectRay(ray, out boundsDist);
|
||||
|
||||
if (boundsDist == 0f) {
|
||||
if (nAttempts < 2) {
|
||||
// Very rarely the random point will be outside the bounds, try again.
|
||||
nAttempts++;
|
||||
goto Start;
|
||||
}
|
||||
// Tried three times and still no good. Ignore this point. Doubt this will ever happen. But don't want to
|
||||
// search forever in case there's a configuration that would cause infinite loops.
|
||||
continue;
|
||||
}
|
||||
|
||||
var intBoundsInPoint = ray.origin + ray.direction * boundsDist;
|
||||
var intBoundsOutPoint = LOSUtils.RaycastBoundsOutPoint(intBoundsInPoint, (intBoundsInPoint - config.Frame.Position).normalized, bounds);
|
||||
|
||||
var midpoint = (intBoundsOutPoint + intBoundsInPoint) / 2f;
|
||||
var penetration = midpoint - intBoundsInPoint;
|
||||
|
||||
if (config.LimitDistance) {
|
||||
penetration = Vector3.ClampMagnitude(penetration, config.MaxDistance / 100f);
|
||||
}
|
||||
|
||||
storeIn.Add(intBoundsInPoint + penetration);
|
||||
}
|
||||
}
|
||||
|
||||
protected override float GetVisibilityScale() {
|
||||
var visibilityScale = 1f;
|
||||
if (config.LimitDistance) {
|
||||
float distance = Mathf.Sqrt((config.InputSignal.Bounds.SqrDistance(config.Frame.Position)));
|
||||
visibilityScale *= config.VisibilityByDistance.Evaluate(distance / config.MaxDistance);
|
||||
}
|
||||
if (config.LimitViewAngle) {
|
||||
var coords = AngleUtils.ViewAnglesToBounds(config.Frame, config.InputSignal.Bounds).Abs;
|
||||
visibilityScale *= config.VisibilityByHorizAngle.Evaluate(coords.HorizAngle / config.MaxHorizAngle)
|
||||
* config.VisibilityByVertAngle.Evaluate(coords.VertAngle / config.MaxVertAngle);
|
||||
}
|
||||
return visibilityScale;
|
||||
}
|
||||
|
||||
protected override float GetRayVisibilityScale(Vector3 target) {
|
||||
var visibilityScale = 1f;
|
||||
if (config.LimitDistance) {
|
||||
float distance = (config.Frame.Position - target).magnitude;
|
||||
visibilityScale *= config.VisibilityByDistance.Evaluate(distance / config.MaxDistance);
|
||||
}
|
||||
if (config.LimitViewAngle) {
|
||||
var coords = AngleUtils.ViewAnglesToPoint(config.Frame, target).Abs;
|
||||
visibilityScale *= config.VisibilityByHorizAngle.Evaluate(coords.HorizAngle / config.MaxHorizAngle)
|
||||
* config.VisibilityByVertAngle.Evaluate(coords.VertAngle / config.MaxVertAngle);
|
||||
}
|
||||
return visibilityScale;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
11
Assets/SensorToolkit/Sensors/src/LOS/LOSTest3D.cs.meta
Normal file
11
Assets/SensorToolkit/Sensors/src/LOS/LOSTest3D.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a3e288c1c7e4c2242b1db4fdca3c133c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
159
Assets/SensorToolkit/Sensors/src/LOS/LOSUtils.cs
Normal file
159
Assets/SensorToolkit/Sensors/src/LOS/LOSUtils.cs
Normal file
@@ -0,0 +1,159 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Micosmo.SensorToolkit {
|
||||
|
||||
public class LOSUtils {
|
||||
|
||||
public static void MapBoundsToEdges(Vector2 viewPos, Bounds bounds, List<Edge2D> storeIn) {
|
||||
var center = (Vector2)bounds.center;
|
||||
var extents = (Vector2)bounds.extents;
|
||||
|
||||
if (viewPos.x > bounds.max.x) {
|
||||
storeIn.Add(new Edge2D(center + new Vector2(extents.x, -extents.y), center + new Vector2(extents.x, extents.y)));
|
||||
} else if (viewPos.x < bounds.min.x) {
|
||||
storeIn.Add(new Edge2D(center - new Vector2(extents.x, -extents.y), center - new Vector2(extents.x, extents.y)));
|
||||
}
|
||||
|
||||
if (viewPos.y > bounds.max.y) {
|
||||
storeIn.Add(new Edge2D(center + new Vector2(-extents.x, extents.y), center + new Vector2(extents.x, extents.y)));
|
||||
} else if (viewPos.y < bounds.min.y) {
|
||||
storeIn.Add(new Edge2D(center - new Vector2(-extents.x, extents.y), center - new Vector2(extents.x, extents.y)));
|
||||
}
|
||||
}
|
||||
|
||||
public static void MapBoundsToTriangles(Vector3 viewPos, Bounds bounds, List<Triangle> storeIn) {
|
||||
var center = bounds.center;
|
||||
var extents = bounds.extents;
|
||||
var ltf = new Vector3(-extents.x, extents.y, extents.z) + center;
|
||||
var rtf = new Vector3(extents.x, extents.y, extents.z) + center;
|
||||
var rtb = new Vector3(extents.x, extents.y, -extents.z) + center;
|
||||
var ltb = new Vector3(-extents.x, extents.y, -extents.z) + center;
|
||||
var lbf = ltf + (Vector3.down * 2f * extents.y);
|
||||
var rbf = rtf + (Vector3.down * 2f * extents.y);
|
||||
var rbb = rtb + (Vector3.down * 2f * extents.y);
|
||||
var lbb = ltb + (Vector3.down * 2f * extents.y);
|
||||
|
||||
if (Vector3.Dot(viewPos - (bounds.center + Vector3.right * bounds.extents.x), Vector3.right) > 0) {
|
||||
storeIn.Add(new Triangle(rtb, rtf, rbf));
|
||||
storeIn.Add(new Triangle(rtb, rbf, rbb));
|
||||
} else if (Vector3.Dot(viewPos - (bounds.center - Vector3.right * bounds.extents.x), Vector3.left) > 0) {
|
||||
storeIn.Add(new Triangle(ltb, ltf, lbf));
|
||||
storeIn.Add(new Triangle(ltb, lbf, lbb));
|
||||
}
|
||||
|
||||
if (Vector3.Dot(viewPos - (bounds.center + Vector3.up * bounds.extents.y), Vector3.up) > 0) {
|
||||
storeIn.Add(new Triangle(ltb, ltf, rtf));
|
||||
storeIn.Add(new Triangle(ltb, rtf, rtb));
|
||||
} else if (Vector3.Dot(viewPos - (bounds.center - Vector3.up * bounds.extents.y), Vector3.down) > 0) {
|
||||
storeIn.Add(new Triangle(lbb, lbf, rbf));
|
||||
storeIn.Add(new Triangle(lbb, rbf, rbb));
|
||||
}
|
||||
|
||||
if (Vector3.Dot(viewPos - (bounds.center + Vector3.forward * bounds.extents.z), Vector3.forward) > 0) {
|
||||
storeIn.Add(new Triangle(rtf, ltf, lbf));
|
||||
storeIn.Add(new Triangle(rtf, lbf, rbf));
|
||||
} else if (Vector3.Dot(viewPos - (bounds.center - Vector3.forward * bounds.extents.z), Vector3.back) > 0) {
|
||||
storeIn.Add(new Triangle(rtb, ltb, lbb));
|
||||
storeIn.Add(new Triangle(rtb, lbb, rbb));
|
||||
}
|
||||
}
|
||||
|
||||
static List<float> triangleAreas = new List<float>();
|
||||
public static Vector3 GetRandomPointInTriangles(List<Triangle> triangles, Vector3 sobolPosition) {
|
||||
if (triangles.Count == 0) {
|
||||
return default;
|
||||
}
|
||||
|
||||
var totalArea = 0f;
|
||||
triangleAreas.Clear();
|
||||
foreach (var triangle in triangles) {
|
||||
totalArea += triangle.GetArea();
|
||||
triangleAreas.Add(totalArea);
|
||||
}
|
||||
var r = sobolPosition.z * totalArea;
|
||||
for (int i = 0; i < triangleAreas.Count; i++) {
|
||||
if (triangleAreas[i] >= r) {
|
||||
return triangles[i].GetRandomPoint(sobolPosition);
|
||||
}
|
||||
}
|
||||
return triangles[triangles.Count - 1].GetRandomPoint(sobolPosition);
|
||||
}
|
||||
|
||||
static List<float> edgeLengths = new List<float>();
|
||||
public static Vector3 GetRandomPointOnEdges(List<Edge2D> edges, Vector3 sobolPosition) {
|
||||
if (edges.Count == 0) {
|
||||
return default;
|
||||
}
|
||||
|
||||
var totalLength = 0f;
|
||||
edgeLengths.Clear();
|
||||
foreach (var edge in edges) {
|
||||
totalLength += edge.GetLength();
|
||||
edgeLengths.Add(totalLength);
|
||||
}
|
||||
var r = sobolPosition.y * totalLength;
|
||||
for (int i = 0; i < edgeLengths.Count; i++) {
|
||||
if (edgeLengths[i] >= r) {
|
||||
return edges[i].GetRandomPoint(sobolPosition);
|
||||
}
|
||||
}
|
||||
return edges[edges.Count - 1].GetRandomPoint(sobolPosition);
|
||||
}
|
||||
|
||||
public static Vector3 RaycastBoundsOutPoint(Vector3 rayPoint, Vector3 rayDir, Bounds bounds) {
|
||||
Vector3 intPoint = rayPoint;
|
||||
float bestDistance = Mathf.Infinity;
|
||||
|
||||
if (rayPoint.x < bounds.max.x && Vector3.Dot(rayDir, Vector3.right) > 0f) {
|
||||
var distance = DistanceRayToPlane(rayPoint, rayDir, bounds.max, Vector3.right);
|
||||
if (distance < bestDistance) {
|
||||
bestDistance = distance;
|
||||
intPoint = rayPoint + rayDir * distance;
|
||||
}
|
||||
} else if (rayPoint.x > bounds.min.x && Vector3.Dot(rayDir, Vector2.left) > 0f) {
|
||||
var distance = DistanceRayToPlane(rayPoint, rayDir, bounds.min, Vector3.left);
|
||||
if (distance < bestDistance) {
|
||||
bestDistance = distance;
|
||||
intPoint = rayPoint + rayDir * distance;
|
||||
}
|
||||
}
|
||||
|
||||
if (rayPoint.y < bounds.max.y && Vector3.Dot(rayDir, Vector3.up) > 0f) {
|
||||
var distance = DistanceRayToPlane(rayPoint, rayDir, bounds.max, Vector3.up);
|
||||
if (distance < bestDistance) {
|
||||
bestDistance = distance;
|
||||
intPoint = rayPoint + rayDir * distance;
|
||||
}
|
||||
} else if (rayPoint.y > bounds.min.y && Vector3.Dot(rayDir, Vector3.down) > 0f) {
|
||||
var distance = DistanceRayToPlane(rayPoint, rayDir, bounds.min, Vector3.down);
|
||||
if (distance < bestDistance) {
|
||||
bestDistance = distance;
|
||||
intPoint = rayPoint + rayDir * distance;
|
||||
}
|
||||
}
|
||||
|
||||
if (rayPoint.z < bounds.max.z && Vector3.Dot(rayDir, Vector3.forward) > 0f) {
|
||||
var distance = DistanceRayToPlane(rayPoint, rayDir, bounds.max, Vector3.forward);
|
||||
if (distance < bestDistance) {
|
||||
bestDistance = distance;
|
||||
intPoint = rayPoint + rayDir * distance;
|
||||
}
|
||||
} else if (rayPoint.z > bounds.min.z && Vector3.Dot(rayDir, Vector3.back) > 0f) {
|
||||
var distance = DistanceRayToPlane(rayPoint, rayDir, bounds.min, Vector3.back);
|
||||
if (distance < bestDistance) {
|
||||
bestDistance = distance;
|
||||
intPoint = rayPoint + rayDir * distance;
|
||||
}
|
||||
}
|
||||
return intPoint;
|
||||
}
|
||||
|
||||
static float DistanceRayToPlane(Vector3 rayPoint, Vector3 rayDir, Vector3 planePoint, Vector3 planeNormal) {
|
||||
var distToPlane = Vector3.Dot(rayPoint - planePoint, planeNormal);
|
||||
return distToPlane / (Vector3.Dot(-rayDir, planeNormal));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
11
Assets/SensorToolkit/Sensors/src/LOS/LOSUtils.cs.meta
Normal file
11
Assets/SensorToolkit/Sensors/src/LOS/LOSUtils.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 070ea86dbdbfe634d9d1854e071459bb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
47
Assets/SensorToolkit/Sensors/src/LOS/MovingAverageFilter.cs
Normal file
47
Assets/SensorToolkit/Sensors/src/LOS/MovingAverageFilter.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Micosmo.SensorToolkit {
|
||||
|
||||
public class MovingAverageFilter {
|
||||
|
||||
int _size;
|
||||
public int Size {
|
||||
get => _size;
|
||||
set {
|
||||
_size = Mathf.Max(value, 1);
|
||||
FitSize();
|
||||
}
|
||||
}
|
||||
|
||||
// Prefer to underestimate then overestimate
|
||||
public float Value => total / Mathf.Max(_size, samples.Count);
|
||||
|
||||
Queue<float> samples = new Queue<float>();
|
||||
float total;
|
||||
|
||||
MovingAverageFilter() { }
|
||||
public MovingAverageFilter(int initialSize) {
|
||||
Size = initialSize;
|
||||
}
|
||||
|
||||
public void AddSample(float x) {
|
||||
samples.Enqueue(x);
|
||||
total += x;
|
||||
FitSize();
|
||||
}
|
||||
|
||||
public void Clear() {
|
||||
samples.Clear();
|
||||
total = 0;
|
||||
}
|
||||
|
||||
void FitSize() {
|
||||
while (samples.Count > _size) {
|
||||
total -= samples.Dequeue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3f5fddd7d3d228d48837fa1426d37786
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
126
Assets/SensorToolkit/Sensors/src/LOS/Triangle.cs
Normal file
126
Assets/SensorToolkit/Sensors/src/LOS/Triangle.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Micosmo.SensorToolkit {
|
||||
|
||||
[System.Serializable]
|
||||
public struct Triangle {
|
||||
public Vector3 P1, P2, P3;
|
||||
public Vector3 N => Vector3.Cross(P2 - P1, P3 - P2).normalized;
|
||||
|
||||
public Triangle(Vector3 p1, Vector3 p2, Vector3 p3) {
|
||||
P1 = p1; P2 = p2; P3 = p3;
|
||||
}
|
||||
|
||||
public float GetArea() {
|
||||
float res = Mathf.Pow(((P2.x * P1.y) - (P3.x * P1.y) - (P1.x * P2.y) + (P3.x * P2.y) + (P1.x * P3.y) - (P2.x * P3.y)), 2.0f);
|
||||
res += Mathf.Pow(((P2.x * P1.z) - (P3.x * P1.z) - (P1.x * P2.z) + (P3.x * P2.z) + (P1.x * P3.z) - (P2.x * P3.z)), 2.0f);
|
||||
res += Mathf.Pow(((P2.y * P1.z) - (P3.y * P1.z) - (P1.y * P2.z) + (P3.y * P2.z) + (P1.y * P3.z) - (P2.y * P3.z)), 2.0f);
|
||||
return Mathf.Sqrt(res) * 0.5f;
|
||||
}
|
||||
|
||||
public Triangle ProjectSphere(Vector3 origin) {
|
||||
return new Triangle((P1 - origin).normalized + origin, (P2 - origin).normalized + origin, (P3 - origin).normalized + origin);
|
||||
}
|
||||
|
||||
public Vector3 GetRandomPoint(Vector3 sobolPosition) {
|
||||
var a = P2 - P1;
|
||||
var b = P3 - P1;
|
||||
var u1 = sobolPosition.x;
|
||||
var u2 = sobolPosition.y;
|
||||
if (u1 + u2 > 1) {
|
||||
u1 = 1 - u1;
|
||||
u2 = 1 - u2;
|
||||
}
|
||||
var w = (u1 * a) + (u2 * b);
|
||||
return w + P1;
|
||||
}
|
||||
|
||||
public void DrawGizmos() {
|
||||
Gizmos.DrawLine(P1, P2);
|
||||
Gizmos.DrawLine(P2, P3);
|
||||
Gizmos.DrawLine(P3, P1);
|
||||
}
|
||||
|
||||
public int Slice(Vector3 planePoint, Vector3 planeNormal, out Triangle slice1, out Triangle slice2) {
|
||||
slice1 = default;
|
||||
slice2 = default;
|
||||
|
||||
int nInside = 0, nOutside = 0;
|
||||
Vector3 inPt1 = default, inPt2 = default;
|
||||
Vector3 outPt1 = default, outPt2 = default;
|
||||
|
||||
var p1Dist = Vector3.Dot(P1 - planePoint, planeNormal);
|
||||
ClassifyPoint(P1, p1Dist, ref nInside, ref nOutside, ref inPt1, ref inPt2, ref outPt1, ref outPt2);
|
||||
var p2Dist = Vector3.Dot(P2 - planePoint, planeNormal);
|
||||
ClassifyPoint(P2, p2Dist, ref nInside, ref nOutside, ref inPt1, ref inPt2, ref outPt1, ref outPt2);
|
||||
var p3Dist = Vector3.Dot(P3 - planePoint, planeNormal);
|
||||
ClassifyPoint(P3, p3Dist, ref nInside, ref nOutside, ref inPt1, ref inPt2, ref outPt1, ref outPt2);
|
||||
|
||||
if (nOutside == 0) {
|
||||
// Completely inside plane
|
||||
slice1 = this;
|
||||
return 1;
|
||||
}
|
||||
if (nInside == 0) {
|
||||
// Completely outside plane
|
||||
return 0;
|
||||
}
|
||||
|
||||
Vector3 edgeInt1, edgeInt2;
|
||||
LinePlaneIntersection(out edgeInt1, inPt1, (outPt1 - inPt1).normalized, planeNormal, planePoint);
|
||||
if (nInside > 1) {
|
||||
LinePlaneIntersection(out edgeInt2, inPt2, (outPt1 - inPt2).normalized, planeNormal, planePoint);
|
||||
slice1 = new Triangle { P1 = inPt1, P2 = edgeInt1, P3 = edgeInt2 };
|
||||
slice2 = new Triangle { P1 = inPt1, P2 = inPt2, P3 = edgeInt2 };
|
||||
return 2;
|
||||
} else {
|
||||
LinePlaneIntersection(out edgeInt2, inPt1, (outPt2 - inPt1).normalized, planeNormal, planePoint);
|
||||
slice1 = new Triangle { P1 = inPt1, P2 = edgeInt1, P3 = edgeInt2 };
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// I had originally made all these refs private vars, but structs with >40 bytes have poor peformance.
|
||||
// So to get under 40 bytes I made them all refs.
|
||||
void ClassifyPoint(Vector3 p, float d, ref int nInside, ref int nOutside, ref Vector3 inPt1, ref Vector3 inPt2, ref Vector3 outPt1, ref Vector3 outPt2) {
|
||||
if (d >= 0f) {
|
||||
if (nInside == 0) inPt1 = p;
|
||||
else if (nInside == 1) inPt2 = p;
|
||||
nInside += 1;
|
||||
} else {
|
||||
if (nOutside == 0) outPt1 = p;
|
||||
else if (nOutside == 1) outPt2 = p;
|
||||
nOutside += 1;
|
||||
}
|
||||
}
|
||||
|
||||
static bool LinePlaneIntersection(out Vector3 intersection, Vector3 linePoint, Vector3 lineVec, Vector3 planeNormal, Vector3 planePoint) {
|
||||
float length;
|
||||
float dotNumerator;
|
||||
float dotDenominator;
|
||||
Vector3 vector;
|
||||
intersection = Vector3.zero;
|
||||
|
||||
dotNumerator = Vector3.Dot((planePoint - linePoint), planeNormal);
|
||||
dotDenominator = Vector3.Dot(lineVec, planeNormal);
|
||||
|
||||
if (dotDenominator != 0.0f) {
|
||||
length = dotNumerator / dotDenominator;
|
||||
vector = SetVectorLength(lineVec, length);
|
||||
intersection = linePoint + vector;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static Vector3 SetVectorLength(Vector3 vector, float size) {
|
||||
Vector3 vectorNormalized = Vector3.Normalize(vector);
|
||||
return vectorNormalized *= size;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
11
Assets/SensorToolkit/Sensors/src/LOS/Triangle.cs.meta
Normal file
11
Assets/SensorToolkit/Sensors/src/LOS/Triangle.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 63fbbd6c6ee9b3347938ecfe9f70adf3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
58
Assets/SensorToolkit/Sensors/src/MotionUtils.cs
Normal file
58
Assets/SensorToolkit/Sensors/src/MotionUtils.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Micosmo.SensorToolkit {
|
||||
|
||||
public static class MotionUtils {
|
||||
|
||||
public static float SeekAccel(float maxSpeed, float vSteer, float velocity) {
|
||||
var targetVelocity = Mathf.Clamp(vSteer, -maxSpeed, maxSpeed);
|
||||
var accel = (targetVelocity - velocity) / Time.deltaTime;
|
||||
return accel;
|
||||
}
|
||||
|
||||
public static Vector2 SeekAccel(float maxSpeed, Vector2 vSteer, Vector2 velocity) {
|
||||
var targetVelocity = Vector2.ClampMagnitude(vSteer, maxSpeed);
|
||||
var accel = (targetVelocity - velocity) / Time.deltaTime;
|
||||
return accel;
|
||||
}
|
||||
|
||||
public static Vector3 SeekAccel(float maxSpeed, Vector3 vSteer, Vector3 velocity) {
|
||||
var targetVelocity = Vector3.ClampMagnitude(vSteer, maxSpeed);
|
||||
var accel = (targetVelocity - velocity) / Time.deltaTime;
|
||||
return accel;
|
||||
}
|
||||
|
||||
public static Vector3 SeekAngularAccel(float maxAccel, float maxSpeed, Vector3 angularVelocity, Quaternion currRot, Quaternion targetRot) {
|
||||
var delta = targetRot * Quaternion.Inverse(currRot);
|
||||
delta.ToAngleAxis(out var angle, out var axis);
|
||||
if (float.IsNaN(axis.sqrMagnitude)) {
|
||||
axis = angularVelocity.normalized;
|
||||
}
|
||||
angle = Mathf.DeltaAngle(0f, angle);
|
||||
var distance = Mathf.Abs(angle);
|
||||
var stoppingDistance = StoppingDistance(maxSpeed, maxAccel);
|
||||
var targetAngularSpeed = Mathf.Min(maxSpeed, maxSpeed * (distance / stoppingDistance));
|
||||
var targetAngularVelocity = Mathf.Sign(angle) * targetAngularSpeed * axis;
|
||||
var torque = (targetAngularVelocity - angularVelocity) / Time.deltaTime;
|
||||
return Vector3.ClampMagnitude(torque, maxAccel);
|
||||
}
|
||||
|
||||
public static float SeekAngularAccel2D(float maxAccel, float maxSpeed, float angularVelocity, Vector2 currDir, Vector2 targetDir) {
|
||||
var dAngle = Vector2.SignedAngle(currDir, targetDir);
|
||||
var distance = Mathf.Abs(dAngle);
|
||||
var stoppingDistance = StoppingDistance(maxSpeed, maxAccel);
|
||||
var arrive = Mathf.Clamp01(distance / stoppingDistance);
|
||||
return SeekAccel(maxSpeed, (arrive * Mathf.Sign(dAngle)) * maxSpeed, angularVelocity);
|
||||
}
|
||||
|
||||
public static float StoppingDistance(float velocity, float accel) {
|
||||
if (accel == 0) return 0;
|
||||
var d = velocity * velocity / (2 * accel);
|
||||
return d * 1.1f;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
11
Assets/SensorToolkit/Sensors/src/MotionUtils.cs.meta
Normal file
11
Assets/SensorToolkit/Sensors/src/MotionUtils.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 76ba871a42f54880b622fab9f356d87a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
123
Assets/SensorToolkit/Sensors/src/ObjectCache.cs
Normal file
123
Assets/SensorToolkit/Sensors/src/ObjectCache.cs
Normal file
@@ -0,0 +1,123 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Micosmo.SensorToolkit
|
||||
{
|
||||
public struct WaitForSecondsCache {
|
||||
WaitForSeconds w;
|
||||
float s;
|
||||
public WaitForSeconds WaitForSeconds(float s) {
|
||||
if (this.s == s && w != null) {
|
||||
return w;
|
||||
}
|
||||
w = new WaitForSeconds(s);
|
||||
this.s = s;
|
||||
return w;
|
||||
}
|
||||
}
|
||||
|
||||
public struct ComponentCache {
|
||||
enum InvokeType { GetComponent, GetComponentInParent, GetComponentInChildren }
|
||||
|
||||
Component component;
|
||||
System.Type type;
|
||||
GameObject obj;
|
||||
InvokeType prevInvokeType;
|
||||
|
||||
public T GetComponent<T>(GameObject ofObj) where T : class {
|
||||
checkInvokeType(InvokeType.GetComponent);
|
||||
|
||||
if (type == typeof(T) && ReferenceEquals(ofObj, obj)) {
|
||||
return component as T;
|
||||
} else {
|
||||
T tc = null;
|
||||
if (ofObj) {
|
||||
ofObj.TryGetComponent(out tc);
|
||||
}
|
||||
component = tc as Component;
|
||||
type = typeof(T);
|
||||
obj = ofObj;
|
||||
return component as T;
|
||||
}
|
||||
}
|
||||
|
||||
public T GetComponentInParent<T>(GameObject ofObj) where T : class {
|
||||
checkInvokeType(InvokeType.GetComponentInParent);
|
||||
|
||||
if (type == typeof(T) && ReferenceEquals(ofObj, obj)) {
|
||||
return component as T;
|
||||
} else {
|
||||
component = null;
|
||||
if (ofObj) {
|
||||
component = ofObj.GetComponentInParent<T>() as Component;
|
||||
}
|
||||
type = typeof(T);
|
||||
obj = ofObj;
|
||||
return component as T;
|
||||
}
|
||||
}
|
||||
|
||||
public T GetComponentInChildren<T>(GameObject ofObj) where T : class {
|
||||
checkInvokeType(InvokeType.GetComponentInChildren);
|
||||
|
||||
if (type == typeof(T) && ReferenceEquals(ofObj, obj)) {
|
||||
return component as T;
|
||||
} else {
|
||||
component = null;
|
||||
if (ofObj) {
|
||||
component = ofObj.GetComponentInChildren<T>() as Component;
|
||||
}
|
||||
type = typeof(T);
|
||||
obj = ofObj;
|
||||
return component as T;
|
||||
}
|
||||
}
|
||||
|
||||
void checkInvokeType(InvokeType nextInvokeType) {
|
||||
if (nextInvokeType != prevInvokeType || !Application.isPlaying) {
|
||||
// We don't cache in edit mode. Because the user may add/remove components at any time.
|
||||
component = null;
|
||||
obj = null;
|
||||
}
|
||||
prevInvokeType = nextInvokeType;
|
||||
}
|
||||
}
|
||||
|
||||
public class ObjectCache<T> where T : new()
|
||||
{
|
||||
Stack<T> cache;
|
||||
|
||||
public ObjectCache() : this(10) { }
|
||||
public ObjectCache(int startSize)
|
||||
{
|
||||
cache = new Stack<T>();
|
||||
for (int i = 0; i < startSize; i++) { cache.Push(create()); }
|
||||
}
|
||||
|
||||
public T Get()
|
||||
{
|
||||
if (cache.Count > 0) return cache.Pop();
|
||||
else return create();
|
||||
}
|
||||
|
||||
public virtual void Dispose(T obj)
|
||||
{
|
||||
cache.Push(obj);
|
||||
}
|
||||
|
||||
protected virtual T create()
|
||||
{
|
||||
return System.Activator.CreateInstance<T>();
|
||||
}
|
||||
}
|
||||
|
||||
public class ListCache<T> : ObjectCache<List<T>>
|
||||
{
|
||||
public override void Dispose(List<T> obj)
|
||||
{
|
||||
obj.Clear();
|
||||
base.Dispose(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user