/
ZOLIDAR3D.cs
234 lines (181 loc) · 8.7 KB
/
ZOLIDAR3D.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using Unity.Collections;
using Unity.Jobs;
using Unity.Burst;
using ZO.Util;
namespace ZO.Sensors {
// this job creates a batch of RaycastCommands we are going to use for
// collision detection against the world. these can be sent to PhysX
// as a batch that will be executed in a job, rather than us having to
// call Physics.Raycast in a loop just on the main thread!
[BurstCompile(CompileSynchronously = true)]
struct ZOPrepareRaycastCommands : IJobParallelFor {
public Vector3 Position;
public Quaternion Orientation;
public float Distance;
public NativeArray<RaycastCommand> RaycastCommands;
[ReadOnly]
public NativeArray<Vector3> RaycastDirections;
public void Execute(int i) {
RaycastCommands[i] = new RaycastCommand(Position, Orientation * RaycastDirections[i], Distance);
}
};
[BurstCompile(CompileSynchronously = true)]
struct ZOTransformHits : IJobParallelFor {
[ReadOnly] public ZOLIDAR3D.ReferenceFrame ReferenceFrame;
[ReadOnly] public Vector3 Position;
[ReadOnly] public NativeArray<RaycastHit> RaycastHits;
public NativeArray<Vector3> HitPositions;
public NativeArray<Vector3> HitNormals;
public void Execute(int i) {
RaycastHit hit = RaycastHits[i];
if (ReferenceFrame == ZOLIDAR3D.ReferenceFrame.LeftHanded_XRight_YUp_ZForward) {
HitNormals[i] = hit.normal;
HitPositions[i] = hit.point - Position;
} else if (ReferenceFrame == ZOLIDAR3D.ReferenceFrame.RightHanded_XBackward_YLeft_ZUp) {
HitNormals[i] = new Vector3(-hit.normal.z, -hit.normal.x, hit.normal.y);
Vector3 pos = hit.point - Position;
HitPositions[i] = new Vector3(-pos.z, -pos.x, pos.y);
}
}
};
public class ZOLIDAR3D : ZOGameObjectBase {
/// <summary>
/// A generic LIDAR sensor.
/// </summary>
public enum ReferenceFrame {
RightHanded_XBackward_YLeft_ZUp,
LeftHanded_XRight_YUp_ZForward // Unity Standard
};
public ReferenceFrame _referenceFrame = ReferenceFrame.LeftHanded_XRight_YUp_ZForward;
[Header("FOV")]
public float _verticalUpFovDegrees = 21.0f;
public float _verticalDownFovDegrees = 74.0f;
public float _horizontalFovDegrees = 360.0f;
[Header("Resolution")]
public float _verticalResolutionDegrees = 0.76f;
public float _horizontalResolutionDegrees = 1.2f;
public float _minRange = 0.0f;
public float _maxRange = 20.0f;
// ~~~~~~ Delegate Callbacks ~~~~~~
/// <summary>
/// Called every frame passing in:
/// ZOLIDAR, string lidar id, int number of hits, Vector3 position, Vector3 normals
///
/// Note: is async so returns a task
/// </summary>
///
/// <value></value>
public Func<ZOLIDAR3D, string, NativeArray<Vector3>, NativeArray<Vector3>, Task> OnPublishDelegate { get; set; }
// Property Accessors
public float HorizontalFOVDegrees { get => _horizontalFovDegrees; }
public float HorizontalResolutionDegrees { get => _horizontalResolutionDegrees; }
public float MinRange { get => _minRange; }
public float MaxRange { get => _maxRange; }
private int _horizontalRayCount = -1;
private int _verticalScanCount = -1;
private int _totalRayCount = -1;
private ZORaycastJobBatch _raycastBatchJob;
private RaycastHit[] _rayCastHits;
private NativeArray<Vector3> _hitPositions;
private NativeArray<Vector3> _hitNormals;
/// Pre-calculated rays
NativeArray<Vector3> _rayDirections;
///
JobHandle _transformHitsJobHandle = default(JobHandle);
// Start is called before the first frame update
protected override void ZOStart() {
Debug.Log("INFO: ZOLIDAR::Start");
_horizontalRayCount = Mathf.RoundToInt(_horizontalFovDegrees / _horizontalResolutionDegrees);
_verticalScanCount = Mathf.RoundToInt((_verticalDownFovDegrees + _verticalUpFovDegrees) / _verticalResolutionDegrees);
_totalRayCount = _horizontalRayCount * _verticalScanCount;
_raycastBatchJob = new ZORaycastJobBatch(_totalRayCount, Allocator.TempJob);
_rayCastHits = new RaycastHit[_totalRayCount];
// build up the ray directions
_rayDirections = new NativeArray<Vector3>(_totalRayCount, Allocator.Persistent);
Vector3 rayDirection = Quaternion.AngleAxis(_verticalUpFovDegrees, transform.right) * transform.forward;
Quaternion horizontalRotationStep = Quaternion.AngleAxis(_horizontalResolutionDegrees, transform.up);
Quaternion verticalRotationStep = Quaternion.AngleAxis(-_verticalResolutionDegrees, transform.right);
int rayIndex = 0;
for (int verticalStep = 0; verticalStep < _verticalScanCount; verticalStep++) {
for (int horizontalStep = 0; horizontalStep < _horizontalRayCount; horizontalStep++) {
_rayDirections[rayIndex] = rayDirection;
rayIndex++;
rayDirection = horizontalRotationStep * rayDirection;
}
// BUGBUG:??? transform.right may change so do we need to have a rightDirection that moves with the rayDirection
rayDirection = verticalRotationStep * rayDirection;
}
_hitPositions = new NativeArray<Vector3>(_totalRayCount, Allocator.Persistent);
_hitNormals = new NativeArray<Vector3>(_totalRayCount, Allocator.Persistent);
}
private void ZOOnDestroy() {
base.ZOOnDestroy();
_transformHitsJobHandle.Complete();
_rayDirections.Dispose();
_hitNormals.Dispose();
_hitPositions.Dispose();
_raycastBatchJob.Dispose();
}
protected override void ZOOnValidate() {
base.ZOOnValidate();
if (UpdateRateHz == 0) {
UpdateRateHz = 10;
}
if (Name == "" || Name == null) {
Name = gameObject.name + "_" + Type;
}
}
protected override async void ZOFixedUpdateHzSynchronized() {
UnityEngine.Profiling.Profiler.BeginSample("ZOLIDAR::ZOUpdateHzSynchronized");
if (_transformHitsJobHandle.IsCompleted == true) {
_transformHitsJobHandle.Complete();
if (OnPublishDelegate != null) {
UnityEngine.Profiling.Profiler.BeginSample("ZOLIDAR::ZOUpdateHzSynchronized::Publish");
await OnPublishDelegate(this, Name, _hitPositions, _hitNormals);
UnityEngine.Profiling.Profiler.EndSample();
}
// Ref: https://github.com/LotteMakesStuff/SimplePhysicsDemo/blob/master/Assets/SimpleJobifiedPhysics.cs
// create new raycast job
_raycastBatchJob.Dispose();
_raycastBatchJob = new ZORaycastJobBatch(_totalRayCount, Allocator.TempJob);
// SetupRaycasts();
var setupRaycastsJob = new ZOPrepareRaycastCommands() {
Position = transform.position,
Orientation = transform.rotation,
Distance = _maxRange,
RaycastDirections = _rayDirections,
RaycastCommands = _raycastBatchJob.RaycastCommands
};
JobHandle setupRaycastsJobHandle = setupRaycastsJob.Schedule(_totalRayCount, 32);
_raycastBatchJob.Schedule(32, setupRaycastsJobHandle);
var transformHitJob = new ZOTransformHits() {
ReferenceFrame = _referenceFrame,
Position = transform.position,
RaycastHits = _raycastBatchJob.RaycastHitResults,
HitPositions = _hitPositions,
HitNormals = _hitNormals
};
_transformHitsJobHandle = transformHitJob.Schedule(_totalRayCount, 32, _raycastBatchJob.RaycastBatchJobHandle);
}
UnityEngine.Profiling.Profiler.EndSample();
}
#region ZOSerializationInterface
public string Type {
get { return "sensor.lidar3d"; }
}
[SerializeField] public string _name = "";
public string Name {
get {
return _name;
}
private set {
_name = value;
}
}
#endregion
}
}