Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_EmptyPositionHelper.c
Go to the documentation of this file.
1//#define SCR_EMPTY_POSITION_HELPER_DEBUG_SHAPES // Shows Successful and Unsuccessful positions and scans.
2
4{
5 static const float MAX_SCAN_LENGTH = 50;
6 static const int SPHERE_AMOUNT_LIMIT = 3; // About 109 Checks assuming half get cancelled for being underground or underwater.
7 static const float TRACE_EPSILON = 0.01; // Removes the issue where traces fail due to being too close to object or ground.
8 static const float CHARACTER_BOUNDING_RADIUS_MODIFIER = 0.75; // Characters are more flexible, reduce their bounding radius slightly.
9 static const float CHARACTER_SPACING_MODIFIER = 0.5; // Characters are more flexible, reduce their spacing slightly.
10 protected static const string DEBUG_TAG = "SCR_EmptyPositionHelper"; // Groups debug objects together.
11
12 //------------------------------------------------------------------------------------------------
20 static bool TryFindNearbyFloorPositionForEntity(BaseWorld world, IEntity entity, vector position = vector.Zero, out vector floorPos = vector.Zero)
21 {
22 if (position == vector.Zero)
23 position = entity.GetOrigin();
24
25 vector mins, maxs;
26 entity.GetBounds(mins, maxs);
27
28 // TraceBox is the correct trace to use. Currently however, the engine sometimes lets TraceBoxes travel through terrain.
29 // Switch back to TraceBox once that is fixed.
30 //TraceBox traceParam = new TraceBox();
31 //entity.GetBounds(traceParam.Mins, traceParam.Maxs);
32 //traceParam.Mins = mins;
33 //traceParam.Maxs = maxs;
34
35 float horizontalDiagonalLength = Math.Sqrt(Pow2(maxs[0] - mins[0]) + Pow2(maxs[2] - mins[2]));
36 float cuboidDiagonalLength = Math.Sqrt(Pow2(horizontalDiagonalLength) + Pow2(maxs[1] - mins[1]));
37
38 // Characters are more flexible, reduce their bounding radius slightly.
39 if (SCR_ChimeraCharacter.Cast(entity))
40 {
41 horizontalDiagonalLength *= CHARACTER_BOUNDING_RADIUS_MODIFIER;
42 cuboidDiagonalLength *= CHARACTER_SPACING_MODIFIER;
43 }
44
45 TraceSphere traceParam = new TraceSphere();
46 traceParam.Radius = 0.5 * horizontalDiagonalLength;
47 traceParam.Exclude = entity;
48 float entityHeight = maxs[1] - mins[1];
49 float spacing = 2 * cuboidDiagonalLength;
50 float maxHorizontalDistance = 10 * spacing;
51 float maxSubmersionDepth = 0.5 * entityHeight;
52
53 SCR_EditableEntityUIInfo editableEntityUIInfo = GetEntityUIInfo(entity);
54 bool isAmphibious = editableEntityUIInfo && editableEntityUIInfo.HasEntityLabel(EEditableEntityLabel.TRAIT_AMPHIBIOUS);
55
56 bool success = TryFindNearbyFloorPosition(world, position, traceParam, entityHeight, spacing, maxHorizontalDistance, isAmphibious, maxSubmersionDepth, floorPos);
57 if (!success)
58 PrintFormat("[SCR_EmptyPositionHelper] TryFindNearbyFloorPositionForEntity failed for find space nearby %1 for %2", position, entity, level: LogLevel.WARNING);
59
60 return success;
61 }
62
63 //------------------------------------------------------------------------------------------------
74 static bool TryFindNearbyFloorPosition(BaseWorld world, vector position, TraceParam traceParam, float minHeight, float spacing, float maxDistance = 100, bool isAmphibious = false, float maxSubmersionDepth = 0, out vector floorPos = "0 0 0")
75 {
76 if (!traceParam.Flags)
77 {
78 traceParam.Flags = TraceFlags.WORLD | TraceFlags.ENTS;
79 if (isAmphibious)
80 traceParam.Flags |= TraceFlags.OCEAN;
81 }
82
83 if (traceParam.LayerMask == -1)
84 traceParam.LayerMask = EPhysicsLayerDefs.Projectile;
85
86 float traceHeight = GetTraceParamHeight(traceParam);
87 if (float.AlmostEqual(traceHeight, 0))
88 traceHeight = 0.001;
89
90 position = RaiseToTerrain(world, position, 0.75 * traceHeight + TRACE_EPSILON);
91 float minFloorY = GetMinYOnTerrainOrOcean(world, position, maxSubmersionDepth);
92 TraceSphere preTraceParam = CreatePreTraceParam(traceParam);
93
94 vector ceilingPos;
95 if (TryGetValidFloorPositionWithPreTrace(world, position, minHeight, traceParam, minFloorY, preTraceParam, floorPos, ceilingPos))
96 return true;
97
98 float halfSpacing = 0.5 * spacing;
99 int maxSpheres = Math.Min(Math.Ceil(maxDistance / spacing), SPHERE_AMOUNT_LIMIT);
100 for (int sphereI = 1; sphereI <= maxSpheres; ++sphereI)
101 {
102 float unitSpacing = 1 / sphereI;
103 int pointsAmount = Math.Min(100, SCR_SpherePointGenerator.EstimatePointsFromSpacingOnUnitSphere(unitSpacing));
104 float radius = sphereI * spacing;
105 for (int pointI = 0; pointI < pointsAmount; ++pointI)
106 {
107 vector point = position + radius * SCR_SpherePointGenerator.GetPointOnUnitSphereFromEquator(pointsAmount, pointI);
108 float terrainHeight = world.GetSurfaceY(point[0], point[2]);
109 // Skip if underground.
110 if (point[1] < terrainHeight + TRACE_EPSILON)
111 {
112#ifdef SCR_EMPTY_POSITION_HELPER_DEBUG_SHAPES
113 SCR_DebugShapeHelperComponent.AddText(world, position, "Underground", tag: DEBUG_TAG);
114#endif
115 continue;
116 }
117
118 float oceanHeight = world.GetOceanHeight(point[0], point[2]);
119 if (isAmphibious)
120 {
121 point[1] = Math.Max(oceanHeight + TRACE_EPSILON, point[1]);
122 }
123 else
124 {
125 if (point[1] < oceanHeight + TRACE_EPSILON)
126 {
127#ifdef SCR_EMPTY_POSITION_HELPER_DEBUG_SHAPES
128 SCR_DebugShapeHelperComponent.AddText(world, position, "Underwater", tag: DEBUG_TAG);
129#endif
130 continue;
131 }
132
133 }
134 minFloorY = GetMinYOnTerrainOrOcean(world, point, maxSubmersionDepth);
135 if (TryGetValidFloorPositionWithPreTrace(world, point, minHeight, traceParam, minFloorY, preTraceParam, floorPos, ceilingPos))
136 return true;
137 }
138 }
139 // Failed to find valid position. Reset floorPos to position.
140 floorPos = position;
141 return false;
142 }
143
144 //------------------------------------------------------------------------------------------------
157 static bool TryGetValidFloorPosition(BaseWorld world, vector position, float minHeight, TraceParam traceParam, float minFloorY, float bonusTraceLength, out vector floorPos, out vector ceilingPos)
158 {
159 // Future feature goes here when engine function becomes accessible.
160 // If (position inside another entity's collision geometry)
161 // return false;
162
163 float traceHeight = GetTraceParamHeight(traceParam);
164 if (float.AlmostEqual(traceHeight, 0))
165 traceHeight = 0.001;
166 float halfTraceHeight = 0.5 * traceHeight;
167 bonusTraceLength = Math.Min(bonusTraceLength, MAX_SCAN_LENGTH);
168 float surfaceHeightWithBuffer = world.GetSurfaceY(position[0], position[2]) + traceHeight + TRACE_EPSILON;
169
170 traceParam.Start = position;
171 traceParam.End = position - Vector(0, Math.Max(MAX_SCAN_LENGTH, minHeight - traceHeight), 0);
172 bool floorFound = TryDetectSurface(world, traceParam, floorPos);
173
174 // Check if instant collision.
175 if (floorPos == traceParam.Start)
176 {
177 ceilingPos = position;
178#ifdef SCR_EMPTY_POSITION_HELPER_DEBUG_SHAPES
179 SCR_DebugShapeHelperComponent.CreateFloorCeilingTraceShapes(world, position, traceParam, floorPos, ceilingPos, false, "Floor clipped", tag: DEBUG_TAG);
180#endif
181 return false;
182 }
183
184 // Adjust for height of traced shape.
185 floorPos -= Vector(0, halfTraceHeight, 0);
186
187 if (!floorFound)
188 {
189 ceilingPos = position;
190#ifdef SCR_EMPTY_POSITION_HELPER_DEBUG_SHAPES
191 SCR_DebugShapeHelperComponent.CreateFloorCeilingTraceShapes(world, position, traceParam, floorPos, ceilingPos, false, "No floor", tag: DEBUG_TAG);
192#endif
193 return false;
194 }
195
196 if (floorPos[1] < minFloorY)
197 {
198 ceilingPos = position;
199#ifdef SCR_EMPTY_POSITION_HELPER_DEBUG_SHAPES
200 SCR_DebugShapeHelperComponent.CreateFloorCeilingTraceShapes(world, position, traceParam, floorPos, ceilingPos, false, "Floor too low", tag: DEBUG_TAG);
201#endif
202 return false;
203 }
204
205 float remainingScanLength = minHeight - (position[1] - floorPos[1]) - halfTraceHeight;
206 if (remainingScanLength + bonusTraceLength < 0) // intentially < rather than <=, if the trace instantly collides, that means there was no space.
207 {
208 ceilingPos = position + Vector(0, halfTraceHeight, 0);
209#ifdef SCR_EMPTY_POSITION_HELPER_DEBUG_SHAPES
210 SCR_DebugShapeHelperComponent.CreateFloorCeilingTraceShapes(world, position, traceParam, floorPos, ceilingPos, true, "Enough height, skipped ceiling", tag: DEBUG_TAG);
211#endif
212 return true;
213 }
214
215 traceParam.Start = position - Vector(0, halfTraceHeight, 0);
216 traceParam.End = position + Vector(0, remainingScanLength + bonusTraceLength, 0);
217 TryDetectSurface(world, traceParam, ceilingPos);
218
219 // Check if instant collision.
220 if (ceilingPos == traceParam.Start)
221 {
222#ifdef SCR_EMPTY_POSITION_HELPER_DEBUG_SHAPES
223 SCR_DebugShapeHelperComponent.CreateFloorCeilingTraceShapes(world, position, traceParam, floorPos, ceilingPos, false, "Ceiling clipped", tag: DEBUG_TAG);
224#endif
225 return false;
226 }
227
228 // Adjust for height of traced shape.
229 ceilingPos += Vector(0, halfTraceHeight, 0);
230
231 bool enoughSpace = (ceilingPos[1] - floorPos[1]) >= minHeight;
232#ifdef SCR_EMPTY_POSITION_HELPER_DEBUG_SHAPES
233 if (enoughSpace)
234 SCR_DebugShapeHelperComponent.CreateFloorCeilingTraceShapes(world, position, traceParam, floorPos, ceilingPos, enoughSpace, "Good position", tag: DEBUG_TAG);
235 else
236 SCR_DebugShapeHelperComponent.CreateFloorCeilingTraceShapes(world, position, traceParam, floorPos, ceilingPos, enoughSpace, "Insufficient height", tag: DEBUG_TAG);
237#endif
238 return enoughSpace;
239 }
240
241 //------------------------------------------------------------------------------------------------
247 static bool TryDetectSurface(BaseWorld world, TraceParam traceParam, out vector surfacePos)
248 {
249 float pathTravelled = world.TraceMove(traceParam, null);
250 surfacePos = vector.Lerp(traceParam.Start, traceParam.End, pathTravelled);
251 return pathTravelled < 1;
252 }
253
254 //------------------------------------------------------------------------------------------------
265 static bool TryGetValidFloorPositionWithPreTrace(BaseWorld world, vector position, float minHeight, TraceParam traceParam, float minFloorY, TraceParam preTraceParam, out vector floorPos, out vector ceilingPos)
266 {
267 // Pre-scan with thin radius allows better detection of floors and ceilings in cluttered enviroments like offices.
268 if (!TryGetValidFloorPosition(world, position, minHeight, preTraceParam, minFloorY, 3 * minHeight, floorPos, ceilingPos))
269 return false;
270
271 position = 0.5 * (floorPos + ceilingPos);
272 return TryGetValidFloorPosition(world, position, minHeight, traceParam, minFloorY, 0, floorPos, ceilingPos);
273 }
274
275 //------------------------------------------------------------------------------------------------
280 {
282 if (!editableEntity)
283 return null;
284 return SCR_EditableEntityUIInfo.Cast(editableEntity.GetInfo());
285 }
286
287 //------------------------------------------------------------------------------------------------
291 static float GetTraceParamHeight(TraceParam traceParam)
292 {
293 TraceBox traceBox = TraceBox.Cast(traceParam);
294 if (traceBox) // Include OBB
295 return traceBox.Maxs[1] - traceBox.Mins[1];
296
297 TraceSphere traceSphere = TraceSphere.Cast(traceParam);
298 if (traceSphere)
299 return 2 * traceSphere.Radius;
300
301 return 0;
302 }
303
304 //------------------------------------------------------------------------------------------------
311 static vector RaiseToTerrain(BaseWorld world, vector position, float offset = 0)
312 {
313 float oceanHeightAtPos = world.GetSurfaceY(position[0], position[2]);
314 position[1] = Math.Max(oceanHeightAtPos + offset, position[1]);
315 return position;
316 }
317
318 //------------------------------------------------------------------------------------------------
323 static float GetMinYOnTerrainOrOcean(BaseWorld world, vector position, float maxSubmersionDepth)
324 {
325 float terrainMin = world.GetSurfaceY(position[0], position[2]) - TRACE_EPSILON;
326 float oceanMin = world.GetOceanHeight(position[0], position[2]) - maxSubmersionDepth - TRACE_EPSILON;
327 return Math.Max(terrainMin, oceanMin);
328 }
329
330 //------------------------------------------------------------------------------------------------
334 protected static float Pow2(float base)
335 {
336 return base * base;
337 }
338
339 //------------------------------------------------------------------------------------------------
345 {
346 float traceMinRadius = 0.1;
347 TraceBox traceBox = TraceBox.Cast(traceParam);
348 if (traceBox) // Include OBB
349 {
350 traceMinRadius = 0.5 * Math.Min(Math.Min(traceBox.Maxs[0] - traceBox.Mins[0], traceBox.Maxs[1] - traceBox.Mins[1]), traceBox.Maxs[2] - traceBox.Mins[2]);
351 }
352 else
353 {
354 TraceSphere traceSphere = TraceSphere.Cast(traceParam);
355 if (traceSphere)
356 traceMinRadius = traceSphere.Radius;
357 }
358
359 TraceSphere preTraceParam = new TraceSphere();
360 preTraceParam.Flags = traceParam.Flags;
361 preTraceParam.LayerMask = traceParam.LayerMask;
362 preTraceParam.Radius = 0.1 * traceMinRadius;
363 preTraceParam.Exclude = traceParam.Exclude;
364 preTraceParam.ExcludeArray = traceParam.ExcludeArray;
365 preTraceParam.Include = traceParam.Include;
366 preTraceParam.IncludeArray = traceParam.IncludeArray;
367 return preTraceParam;
368 }
369}
EEditableEntityLabel
vector position
proto external Managed FindComponent(typename typeName)
proto external vector GetOrigin()
proto external void GetBounds(out vector mins, out vector maxs)
Definition Math.c:13
SCR_UIInfo GetInfo(IEntity owner=null)
bool HasEntityLabel(EEditableEntityLabel label)
static bool TryGetValidFloorPosition(BaseWorld world, vector position, float minHeight, TraceParam traceParam, float minFloorY, float bonusTraceLength, out vector floorPos, out vector ceilingPos)
static TraceSphere CreatePreTraceParam(TraceParam traceParam)
static float Pow2(float base)
static float GetTraceParamHeight(TraceParam traceParam)
static bool TryDetectSurface(BaseWorld world, TraceParam traceParam, out vector surfacePos)
static vector RaiseToTerrain(BaseWorld world, vector position, float offset=0)
static SCR_EditableEntityUIInfo GetEntityUIInfo(IEntity entity)
static bool TryGetValidFloorPositionWithPreTrace(BaseWorld world, vector position, float minHeight, TraceParam traceParam, float minFloorY, TraceParam preTraceParam, out vector floorPos, out vector ceilingPos)
static bool TryFindNearbyFloorPosition(BaseWorld world, vector position, TraceParam traceParam, float minHeight, float spacing, float maxDistance=100, bool isAmphibious=false, float maxSubmersionDepth=0, out vector floorPos="0 0 0")
static bool TryFindNearbyFloorPositionForEntity(BaseWorld world, IEntity entity, vector position=vector.Zero, out vector floorPos=vector.Zero)
static float GetMinYOnTerrainOrOcean(BaseWorld world, vector position, float maxSubmersionDepth)
LogLevel
Enum with severity of the logging message.
Definition LogLevel.c:14
proto void PrintFormat(string fmt, void param1=NULL, void param2=NULL, void param3=NULL, void param4=NULL, void param5=NULL, void param6=NULL, void param7=NULL, void param8=NULL, void param9=NULL, LogLevel level=LogLevel.NORMAL)
proto native vector Vector(float x, float y, float z)
TraceFlags
Definition TraceFlags.c:13