6class SCR_ShapeAnalyserEntity : SCR_GeneratorBaseEntity
9 protected bool m_bDrawErrors;
12 protected float m_fMaxAngle;
15 protected float m_fMaxSlope;
21 protected bool m_bPrintDebugInfo;
25 protected static ref array<ref Shape> s_aErrorShapes;
26 protected static ref array<ref Shape> s_aDebugShapes;
28 protected static const int ERROR_SHAPE_ANGLE_ARRAY_LENGTH = 50;
29 protected static const float ERROR_SHAPE_ANGLE_CHECK_LENGTH = 10.0;
31 protected static float SLOPE_ERROR_SHAPE_LENGTH = 30.0;
32 protected static float ANGLE_ERROR_LINE_LENGTH = 5.0;
34 protected static vector NORMAL_POINT_LINE_VECTOR =
"0 20 0";
35 protected static float NORMAL_POINT_LINE_HAT_SIZE = 1.0;
36 protected static vector TESSELATED_POINT_LINE_VECTOR =
"0 10 0";
37 protected static vector MIDDLE_POINT_LINE_VECTOR =
"0 15 0";
38 protected static int ERROR_SHAPE_COLOUR =
Color.RED;
39 protected static int DEBUG_SHAPE_COLOUR =
Color.MAGENTA;
40 protected static int DEBUG_SHAPE_COLOUR_IMPORTANT =
Color.DARK_RED;
43 protected static int DEBUG_SHAPE_COLOUR_ALPHA = 0x66000000;
49 if (!super._WB_OnKeyChanged(src, key, ownerContainers, parent))
52 if (!m_ParentShapeSource)
56 if (!worldEditorAPI || worldEditorAPI.UndoOrRedoIsRestoring())
59 ShapeEntity parentShape = ShapeEntity.Cast(worldEditorAPI.SourceToEntity(m_ParentShapeSource));
63 src = worldEditorAPI.EntityToSource(
this);
66 Process(m_ParentShapeSource, parentShape);
72 protected override void OnShapeChangedInternal(IEntitySource shapeEntitySrc, ShapeEntity shapeEntity, array<vector> mins, array<vector> maxes)
74 super.OnShapeChangedInternal(shapeEntitySrc, shapeEntity, mins, maxes);
76 WorldEditorAPI worldEditorAPI = _WB_GetEditorAPI();
80 shapeEntity = ShapeEntity.Cast(worldEditorAPI.SourceToEntity(m_ParentShapeSource));
82 Process(shapeEntitySrc, shapeEntity);
86 protected void Process(notnull IEntitySource shapeEntitySrc, notnull ShapeEntity shapeEntity)
88 SCR_ShapeAnalyser shapeAnalyser = SCR_ShapeAnalyser.CreateFromShape(shapeEntity);
90 s_aDebugShapes = null;
91 s_aErrorShapes = null;
94 DrawDebugShapes(shapeAnalyser);
97 DrawErrorShapes(shapeAnalyser);
99 if (m_bPrintDebugInfo)
100 PrintDebugInfo(shapeAnalyser);
104 protected void DrawDebugShapes(notnull SCR_ShapeAnalyser shapeAnalyser)
108 array<ref SCR_Ray> pointRays = shapeAnalyser.GetAbsoluteAnchorPointRays();
109 array<ref SCR_Ray> middlePointRays = shapeAnalyser.GetAbsoluteMiddlePointRays();
110 array<ref SCR_Ray> tesselatedPointRays = shapeAnalyser.GetAbsoluteTesselatedPointRays();
112 int currentPointIndex = 0;
113 int currentMiddlePointIndex = 0;
114 int lastPointIndex = pointRays.Count() - 1;
115 int lastMiddlePointIndex = pointRays.Count() - 1;
116 SCR_Ray currentPoint = pointRays[0];
117 SCR_Ray currentMiddlePoint = middlePointRays[0];
119 foreach (SCR_Ray pointRay : tesselatedPointRays)
121 if (pointRay == currentPoint)
123 AddDebugLine(pointRay.m_vPosition, pointRay.m_vPosition + NORMAL_POINT_LINE_VECTOR,
true);
124 AddDebugLine(pointRay.m_vPosition - pointRay.m_vDirection * NORMAL_POINT_LINE_VECTOR[1], pointRay.m_vPosition + pointRay.m_vDirection * NORMAL_POINT_LINE_VECTOR[1],
true);
126 if (currentPointIndex < lastPointIndex)
127 currentPoint = pointRays[currentPointIndex];
129 else if (pointRay == currentMiddlePoint)
131 AddDebugLine(pointRay.m_vPosition, pointRay.m_vPosition + MIDDLE_POINT_LINE_VECTOR,
true);
132 AddDebugLine(pointRay.m_vPosition - pointRay.m_vDirection * MIDDLE_POINT_LINE_VECTOR[1], pointRay.m_vPosition + pointRay.m_vDirection * MIDDLE_POINT_LINE_VECTOR[1],
true);
133 currentMiddlePointIndex++;
134 if (currentMiddlePointIndex < lastMiddlePointIndex)
135 currentMiddlePoint = middlePointRays[currentMiddlePointIndex];
139 AddDebugLine(pointRay.m_vPosition, pointRay.m_vPosition + TESSELATED_POINT_LINE_VECTOR);
145 protected void AddDebugLine(vector pointA, vector pointB,
bool isImportant =
false)
147 vector pointRays[2] = { pointA, pointB };
149 s_aDebugShapes.Insert(Shape.CreateLines(DEBUG_SHAPE_COLOUR_IMPORTANT, DEBUG_SHAPE_FLAGS, pointRays, 2));
151 s_aDebugShapes.Insert(Shape.CreateLines(DEBUG_SHAPE_COLOUR, DEBUG_SHAPE_FLAGS, pointRays, 2));
155 protected void DrawErrorShapes(notnull SCR_ShapeAnalyser shapeAnalyser)
157 array<ref SCR_Ray> pointRays = shapeAnalyser.GetAbsoluteAnchorPointRays();
158 array<ref SCR_Ray> tesselatedPointRays = shapeAnalyser.GetAbsoluteTesselatedPointRays();
164 float firstAngleDeg = Math.Atan2(pointRays[0].m_vDirection[0], pointRays[0].m_vDirection[2]) * Math.RAD2DEG;
165 float lastAnglesDeg[ERROR_SHAPE_ANGLE_ARRAY_LENGTH];
166 float lastAnglesDist[ERROR_SHAPE_ANGLE_ARRAY_LENGTH];
167 for (
int i; i < ERROR_SHAPE_ANGLE_ARRAY_LENGTH; i++)
169 lastAnglesDeg[i] = firstAngleDeg;
172 int lastTesselatedIndex = tesselatedPointRays.Count() - 1;
174 foreach (
int i, SCR_Ray tesselatedPoint : tesselatedPointRays)
177 float slopeDeg = Math.Atan2(tesselatedPoint.m_vDirection[1], vector.DistanceXZ(tesselatedPoint.m_vDirection, vector.Zero)) * Math.RAD2DEG;
178 if (slopeDeg > m_fMaxSlope || slopeDeg < -m_fMaxSlope)
180 segment[0] = tesselatedPoint.m_vPosition;
181 segment[1] = segment[0] + vector.Up * SLOPE_ERROR_SHAPE_LENGTH;
182 s_aErrorShapes.Insert(Shape.CreateLines(Color.RED,
ShapeFlags.NOZBUFFER, segment, 2));
186 float angleDeg = Math.Atan2(tesselatedPoint.m_vDirection[0], tesselatedPoint.m_vDirection[2]) * Math.RAD2DEG;
187 float angleRad = Math.Atan2(tesselatedPoint.m_vDirection[2], tesselatedPoint.m_vDirection[0]);
190 for (
int j = ERROR_SHAPE_ANGLE_ARRAY_LENGTH - 1; j >= 0; j--)
192 if (lastAnglesDist[j] == 0)
195 float angleDiffDeg = lastAnglesDeg[j] - angleDeg;
197 if (angleDiffDeg <= -180 || angleDiffDeg > 180)
198 angleDiffDeg = Math.Repeat(180 + angleDiffDeg, 360) - 180;
200 if (angleDiffDeg < 0)
203 if (angleDiffDeg > m_fMaxAngle)
205 segment[0] = tesselatedPoint.m_vPosition + { Math.Cos(angleRad - Math.PI_HALF), 0, Math.Sin(angleRad - Math.PI_HALF) } * (ANGLE_ERROR_LINE_LENGTH * 0.5);
206 segment[1] = tesselatedPoint.m_vPosition + { Math.Cos(angleRad + Math.PI_HALF), 0, Math.Sin(angleRad + Math.PI_HALF) } * (ANGLE_ERROR_LINE_LENGTH * 0.5);
207 s_aErrorShapes.Insert(Shape.CreateLines(Color.RED,
ShapeFlags.NOZBUFFER, segment, 2));
211 totalDist += lastAnglesDist[j];
212 if (totalDist > ERROR_SHAPE_ANGLE_CHECK_LENGTH)
217 for (
int j, jMax = ERROR_SHAPE_ANGLE_ARRAY_LENGTH - 1; j < jMax; j++)
219 lastAnglesDeg[j] = lastAnglesDeg[j + 1];
220 lastAnglesDist[j] = lastAnglesDist[j + 1];
223 lastAnglesDeg[ERROR_SHAPE_ANGLE_ARRAY_LENGTH - 1] = angleDeg;
224 if (i != lastTesselatedIndex)
225 lastAnglesDist[ERROR_SHAPE_ANGLE_ARRAY_LENGTH - 1] = vector.DistanceXZ(tesselatedPoint.m_vPosition, tesselatedPointRays[i + 1].m_vPosition);
231 protected void PrintDebugInfo(notnull SCR_ShapeAnalyser shapeAnalyser)
233 array<ref SCR_Ray> pointRays = shapeAnalyser.GetAbsoluteAnchorPointRays();
234 array<ref SCR_Ray> tesselatedPointRays = shapeAnalyser.GetAbsoluteTesselatedPointRays();
235 array<float> polygon2D = {};
237 array<float> stats = shapeAnalyser.GetStats();
240 float length2D = stats[statIndex++];
241 float length3D = stats[statIndex++];
242 float surface = stats[statIndex++];
243 float minAlt = stats[statIndex++], maxAlt = stats[statIndex++];
244 float minSlope = stats[statIndex++], maxSlope = stats[statIndex++];
246 float minAltATL =
float.INFINITY, maxAltATL = -
float.INFINITY;
248 SCR_Ray prevPointRay;
249 foreach (
int i, SCR_Ray pointRay : tesselatedPointRays)
253 length2D += vector.DistanceXZ(prevPointRay.m_vPosition, pointRay.m_vPosition);
254 length3D += vector.Distance(prevPointRay.m_vPosition, pointRay.m_vPosition);
257 float tmp = pointRay.m_vPosition[1] - GetWorld().GetSurfaceY(pointRay.m_vPosition[0], pointRay.m_vPosition[2]);
265 prevPointRay = pointRay;
268 Print(
string.Format(
"%1 pointRays, %2 tesselated pointRays", pointRays.Count(), tesselatedPointRays.Count()),
LogLevel.NORMAL);
269 Print(
"Length 2D/3D : " + length2D.ToString(lenDec: 2) +
"m / " + length3D.ToString(lenDec: 2) +
"m",
LogLevel.NORMAL);
271 if (shapeAnalyser.IsClosed())
272 Print(
"Surface area : " + SCR_Math2D.GetPolygonArea(polygon2D).ToString(lenDec: 2) +
"m sq.",
LogLevel.NORMAL);
276 Print(
"Min/Max slope : [" + (minSlope * Math.RAD2DEG).ToString(lenDec: 2) +
"deg / " + (maxSlope * Math.RAD2DEG).ToString(lenDec: 2) +
"deg]",
LogLevel.NORMAL);
277 Print(
"Min/Max altitude : [" + minAlt.ToString(lenDec: 2) +
"m / " + maxAlt.ToString(lenDec: 2) +
"m]",
LogLevel.NORMAL);
278 Print(
"Min/Max alt ATL : [" + minAltATL.ToString(lenDec: 2) +
"m / " + maxAltATL.ToString(lenDec: 2) +
"m]",
LogLevel.NORMAL);
enum SCR_ECompassType EntityEditorProps(category:"GameScripted/Gadgets", description:"Compass", color:"0 0 255 255")
Prefab data class for compass component.
override bool _WB_OnKeyChanged(IEntity owner, BaseContainer src, string key, BaseContainerList ownerContainers, IEntity parent)
Any property value has been changed. You can use editor API here and do some additional edit actions ...
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
proto void Print(void var, LogLevel level=LogLevel.NORMAL)
Prints content of variable to console/log.
LogLevel
Enum with severity of the logging message.
SCR_FieldOfViewSettings Attribute
void OnShapeChangedInternal(IEntitySource shapeEntitySrc, ShapeEntity shapeEntity, array< vector > mins, array< vector > maxes)