Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_ShapeBrushTool.c
Go to the documentation of this file.
1#ifdef WORKBENCH
2[WorkbenchToolAttribute(
3 name: "Shape Brush Tool",
4 description: "Draw an area or a line using a brush."
5 + "\n- click and drag to draw"
6 + "\n- a width of zero turns line mode on"
7 + "\n- Ctrl+ScrollWheel to modify the brush's radius on the fly"
8 + "\n- it is not possible to switch between line and brush mode while drawing",
9 awesomeFontCode: 0xF5CB // or 0xF55D?
10)]
11class SCR_ShapeBrushTool : WorldEditorTool
12{
13 /*
14 Category: Brush
15 */
16
17 [Attribute(defvalue: "100", uiwidget: UIWidgets.Slider, desc: "Brush radius\nCtrl+ScrollWheel to change this value", params: string.Format("%1 %2 %3", RADIUS_MIN, RADIUS_MAX, RADIUS_STEP), category: "Brush")]
18 protected float m_fRadius;
19
20 [Attribute(defvalue: "25", uiwidget: UIWidgets.Slider, desc: "Shape point's min step", params: "0 500 10", category: "Brush")]
21 protected float m_fMinStep;
22
23 /*
24 Category: Shape
25 */
26
27 [Attribute(defvalue: "0", uiwidget: UIWidgets.ComboBox, desc: "Type of shape to be drawn", enums: SCR_ParamEnumArray.FromString("Polyline,Multiple straight line segments;Spline,Bézier curve segments"), category: "Shape")]
28 protected int m_iShapeType;
29
30 [Attribute(defvalue: "1", desc: "Close the shape or not", category: "Shape")]
31 protected bool m_bIsShapeClosed;
32
33 protected bool m_bIsDrawing;
34
35 protected vector m_vLastCameraPosition;
36 protected vector m_vLastMousePosition;
37 protected ref Shape m_BrushShape;
38 protected int m_iBrushShapeColor;
39
40 protected ref array<vector> m_aShapePoints = {};
41 protected ref array<float> m_aShapeRadii = {};
42 protected ref SCR_DebugShapeManager m_DebugShapeManager = new SCR_DebugShapeManager();
43
44 protected static const float RADIUS_STEP = 10;
45 protected static const float RADIUS_MAX = 500;
46 protected static const float RADIUS_MIN_WHILE_DRAWING = 10;
47 protected static const float RADIUS_MIN = 0;
48
49 protected static const int BRUSH_COLOUR_DEFAULT = 0xFF00FF00; // green
50 protected static const int BRUSH_COLOUR_ACTIVE = 0xFFFF8800; // orange
51
52 protected static const float CURSOR_RATIO = 0.25;
53
54 protected static const int ARC_STEP = 30;
55 protected static const int ARC_SAFETY = 0; // .125;
56
57 //------------------------------------------------------------------------------------------------
58 protected void DrawBrush()
59 {
60 if (m_bIsDrawing)
61 m_iBrushShapeColor = BRUSH_COLOUR_ACTIVE;
62 else
63 m_iBrushShapeColor = BRUSH_COLOUR_DEFAULT;
64
65 if (m_fRadius > 0)
66 {
67 m_BrushShape = CreateCircle(m_vLastMousePosition, vector.Up, m_fRadius, m_iBrushShapeColor, 50, ShapeFlags.NOZBUFFER);
68 return;
69 }
70
71 float size = vector.Distance(m_vLastCameraPosition, m_vLastMousePosition) * CURSOR_RATIO;
72 m_BrushShape = Shape.CreateArrow(m_vLastMousePosition + vector.Up * size, m_vLastMousePosition, size * 0.25, m_iBrushShapeColor, 0);
73 }
74
75 //------------------------------------------------------------------------------------------------
76 protected array<vector> GetShapeBorder()
77 {
78 int shapePointsCount = m_aShapePoints.Count();
79 if (shapePointsCount < 1)
80 return {};
81
82 array<vector> result = {};
83 if (shapePointsCount == 1)
84 {
85 for (int i; i < 360; i += ARC_STEP)
86 {
87 result.Insert(GetPointAtAngle(m_aShapePoints[0], i, m_aShapeRadii[0], true));
88 }
89
90 return result;
91 }
92
93 if (m_fRadius == 0) // line only
94 return SCR_ArrayHelperT<vector>.GetCopy(m_aShapePoints);
95
96 array<vector> rightPoints = {};
97 array<vector> rejectedPoints = {};
98 result.Reserve(shapePointsCount);
99 vector point;
100 vector prevShapePoint;
101 foreach (int shapePointIndex, vector currShapePoint : m_aShapePoints)
102 {
103 if (shapePointIndex == 0) // first drawn point
104 {
105 float angleDeg = vector.Direction(currShapePoint, m_aShapePoints[1]).ToYaw();
106 for (int additionalAngleDeg = 90; additionalAngleDeg <= 270; additionalAngleDeg += ARC_STEP)
107 {
108 point = GetPointAtAngle(currShapePoint, angleDeg + additionalAngleDeg, m_aShapeRadii[shapePointIndex] + ARC_SAFETY, false);
109 if (IsValidPoint(point, shapePointIndex))
110 result.Insert(point);
111 else
112 rejectedPoints.Insert(point);
113 }
114 }
115 else if (shapePointIndex == shapePointsCount - 1) // last drawn point
116 {
117 float angleDeg = vector.Direction(m_aShapePoints[shapePointIndex - 1], currShapePoint).ToYaw();
118
119 for (int additionalAngleDeg = -90; additionalAngleDeg <= 90; additionalAngleDeg += ARC_STEP)
120 {
121 point = GetPointAtAngle(currShapePoint, angleDeg + additionalAngleDeg, m_aShapeRadii[shapePointIndex] + ARC_SAFETY, false);
122 if (IsValidPoint(point, shapePointIndex))
123 result.Insert(point);
124 else
125 rejectedPoints.Insert(point);
126 }
127 }
128 else // middle point
129 {
130 float angleDeg = vector.Direction(prevShapePoint, m_aShapePoints[shapePointIndex + 1]).ToYaw();
131
132 point = GetPointAtAngle(currShapePoint, angleDeg - 90, m_aShapeRadii[shapePointIndex], false); // left
133 if (IsValidPoint(point, shapePointIndex))
134 result.Insert(point);
135 else
136 rejectedPoints.Insert(point);
137
138 point = GetPointAtAngle(currShapePoint, angleDeg + 90, m_aShapeRadii[shapePointIndex], false); // right
139 if (IsValidPoint(point, shapePointIndex))
140 rightPoints.Insert(point);
141 else
142 rejectedPoints.Insert(point);
143 }
144
145 prevShapePoint = currShapePoint;
146 }
147
148 SCR_ArrayHelperT<vector>.Reverse(rightPoints);
149 result.InsertAll(rightPoints);
150
151 // set terrain Y here (and not for all including rejected points)
152 foreach (int i, vector resultPoint : result)
153 {
154 resultPoint[1] = m_API.GetTerrainSurfaceY(resultPoint[0], resultPoint[2]);
155 result[i] = resultPoint;
156 }
157
158 return result;
159 }
160
161 //------------------------------------------------------------------------------------------------
167 protected vector GetPointAtAngle(vector origin, float angleDeg, float distance, bool setTerrainY)
168 {
169 if (distance == 0)
170 return origin;
171
172 float angleRad = -(angleDeg - 90) * Math.DEG2RAD;
173 if (setTerrainY)
174 {
175 vector point = origin + { Math.Cos(angleRad) * distance, 0, Math.Sin(angleRad) * distance };
176 point[1] = m_API.GetTerrainSurfaceY(point[0], point[2]);
177 return point;
178 }
179 else
180 {
181 return origin + { Math.Cos(angleRad) * distance, 0, Math.Sin(angleRad) * distance };
182 }
183 }
184
185 //------------------------------------------------------------------------------------------------
190 bool IsValidPoint(vector point, int shapePointIndex)
191 {
192 foreach (int otherPointIndex, vector otherShapePoint : m_aShapePoints)
193 {
194 if (shapePointIndex == otherPointIndex)
195 continue;
196
197 if (vector.DistanceXZ(point, otherShapePoint) < m_aShapeRadii[otherPointIndex])
198 return false;
199 }
200
201 return true;
202 }
203
204 //------------------------------------------------------------------------------------------------
205 protected void DrawShape()
206 {
208
209 // draw axis
210 foreach (int i, vector shapePoint : m_aShapePoints)
211 {
212 m_DebugShapeManager.AddLine(shapePoint + 3 * vector.Up, shapePoint);
213 if (i > 0)
214 m_DebugShapeManager.AddLine(m_aShapePoints[i - 1], shapePoint, 0x88FFDD55);
215 }
216
217 // draw border
218 array<vector> borderPoints = GetShapeBorder();
219 foreach (int i, vector borderPoint : borderPoints)
220 {
221 m_DebugShapeManager.AddLine(borderPoint + 3 * vector.Up, borderPoint, 0xFF55FF55);
222 if (i > 0)
223 m_DebugShapeManager.AddLine(borderPoints[i - 1], borderPoint, 0xFF00FF00);
224 }
225 }
226
227 //------------------------------------------------------------------------------------------------
228 protected IEntitySource CreateShapeEntity(notnull array<vector> shapePoints)
229 {
230 if (shapePoints.IsEmpty())
231 {
232 PrintFormat("Empty points array provided to create shape", level: LogLevel.ERROR);
233 return null;
234 }
235
236 m_API.BeginEntityAction();
237
238 string shapeClassname;
239 if (m_iShapeType == 0)
240 shapeClassname = "PolylineShapeEntity";
241 else
242 shapeClassname = "SplineShapeEntity";
243
244 IEntitySource shapeEntitySource = m_API.CreateEntity(shapeClassname, string.Empty, m_API.GetCurrentEntityLayerId(), null, shapePoints[0], vector.Zero);
245 if (!shapeEntitySource)
246 {
247 m_API.EndEntityAction();
248
249 PrintFormat("Cannot create %1 shape", shapeClassname, level: LogLevel.ERROR);
250 Workbench.Dialog("Error", "Cannot create " + shapeClassname + " shape");
251
252 return null;
253 }
254
255 vector origin;
256 foreach (int i, vector point : shapePoints)
257 {
258 if (!m_API.CreateObjectArrayVariableMember(shapeEntitySource, null, "Points", "ShapePoint", i))
259 {
260 Print("Cannot create point #" + i, LogLevel.ERROR);
261 break;
262 }
263
264 if (i == 0) // first point = "0 0 0"
265 {
266 origin = point;
267 continue;
268 }
269
270 point -= origin;
271 m_API.SetVariableValue(shapeEntitySource, { new ContainerIdPathEntry("Points", i) }, "Position", string.Format("%1 %2 %3", point[0], point[1], point[2]));
272 }
273
274 if (m_bIsShapeClosed)
275 m_API.SetVariableValue(shapeEntitySource, null, "IsClosed", "1");
276
277 m_API.EndEntityAction();
278
279 return shapeEntitySource;
280 }
281
282 //------------------------------------------------------------------------------------------------
290 protected float AdjustValueUsingScrollwheel(float delta, float currentValue, float min, float max, float step)
291 {
292 // delta returns multiples of 120 - converting it into a more useable value of multiples of 1
293 float value = currentValue + (delta / 120) * step;
294
295 if (value < min)
296 return min;
297
298 if (value > max)
299 return max;
300
301 return value;
302 }
303
304 //------------------------------------------------------------------------------------------------
305 override void OnLeaveEvent()
306 {
307 if (!m_bIsDrawing)
308 m_BrushShape = null;
309 }
310
311 //------------------------------------------------------------------------------------------------
312 override void OnMouseMoveEvent(float x, float y)
313 {
314 vector traceStart, traceEnd, traceDir;
315 if (!m_API.TraceWorldPos(x, y, TraceFlags.WORLD, traceStart, traceEnd, traceDir))
316 return;
317
318 if (m_bIsDrawing)
319 {
320 if (vector.DistanceSqXZ(traceEnd, m_aShapePoints[m_aShapePoints.Count() - 1]) >= m_fMinStep * m_fMinStep)
321 {
322 if (m_bIsDrawing)
323 {
324 m_aShapePoints.Insert(m_vLastMousePosition);
325 m_aShapeRadii.Insert(m_fRadius);
326
327 DrawShape();
328 }
329 }
330 }
331
332 m_vLastCameraPosition = traceStart;
333 m_vLastMousePosition = traceEnd;
334
335 DrawBrush();
336 }
337
338 //------------------------------------------------------------------------------------------------
339 override void OnMousePressEvent(float x, float y, WETMouseButtonFlag buttons)
340 {
341 if (buttons != WETMouseButtonFlag.LEFT)
342 return;
343
344 m_bIsDrawing = true;
345
346 vector traceStart, traceEnd, traceDir;
347 m_API.TraceWorldPos(x, y, TraceFlags.WORLD, traceStart, traceEnd, traceDir);
348
349 m_vLastCameraPosition = traceStart;
350 m_vLastMousePosition = traceEnd;
351
352 m_aShapePoints.Clear();
353 m_aShapeRadii.Clear();
354
355 m_aShapePoints.Insert(m_vLastMousePosition);
356 m_aShapeRadii.Insert(m_fRadius);
357
358 DrawShape();
359
360 DrawBrush();
361 }
362
363 //------------------------------------------------------------------------------------------------
364 override void OnMouseReleaseEvent(float x, float y, WETMouseButtonFlag buttons)
365 {
366 if (buttons != WETMouseButtonFlag.LEFT)
367 return;
368
369 vector traceStart, traceEnd, traceDir;
370 if (!m_API.TraceWorldPos(x, y, TraceFlags.WORLD, traceStart, traceEnd, traceDir))
371 return;
372
373 m_bIsDrawing = false;
374 if (m_aShapePoints.Count() > 1 || traceEnd != m_aShapePoints[0])
375 {
376 m_aShapePoints.Insert(m_vLastMousePosition);
377 m_aShapeRadii.Insert(m_fRadius);
378 }
379
380 array<vector> borderPoints = GetShapeBorder();
381
382 IEntitySource shapeEntitySource = CreateShapeEntity(borderPoints);
383
384 m_aShapePoints.Clear();
385 m_aShapeRadii.Clear();
387
388 if (shapeEntitySource)
389 m_API.SetEntitySelection(shapeEntitySource);
390 else
391 Print("Cannot create Shape", LogLevel.ERROR);
392
393 DrawBrush();
394 }
395
396 //------------------------------------------------------------------------------------------------
397 override void OnWheelEvent(int delta)
398 {
399 // adjusts m_fRadius value using a CTRL + Scrollwheel keybind
400 if (GetModifierKeyState(ModifierKey.CONTROL))
401 {
402 if (m_bIsDrawing)
403 {
404 if (m_fRadius == 0)
405 return;
406
407 m_fRadius = AdjustValueUsingScrollwheel(delta, m_fRadius, RADIUS_MIN_WHILE_DRAWING, RADIUS_MAX, RADIUS_STEP); // cannot reach 0 if drawing
408 }
409 else
410 {
411 m_fRadius = AdjustValueUsingScrollwheel(delta, m_fRadius, RADIUS_MIN, RADIUS_MAX, RADIUS_STEP); // allow reaching 0
412 }
413
414 m_BrushShape = CreateCircle(m_vLastMousePosition, vector.Up, m_fRadius, m_iBrushShapeColor, 50, ShapeFlags.NOZBUFFER);
415 UpdatePropertyPanel();
416 }
417 }
418
419 //------------------------------------------------------------------------------------------------
420 override void OnActivate()
421 {
422 m_aShapePoints.Clear();
423 m_aShapeRadii.Clear();
424 m_bIsDrawing = false;
425 }
426
427 //------------------------------------------------------------------------------------------------
428 override void OnDeActivate()
429 {
430 m_BrushShape = null;
431 }
432}
433#endif
void ContainerIdPathEntry(string propertyName, int index=-1)
Definition worldEditor.c:30
Shape CreateCircle(vector pos, vector aroundDir, float radius, int color, int subdivisions, ShapeFlags flags)
Definition DebugShapes.c:91
int size
override void OnActivate()
float distance
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
ref SCR_DebugShapeManager m_DebugShapeManager
void Clear()
Remove all stored shapes and texts.
Shape AddLine(vector from, vector to, int colour=DEFAULT_SHAPE_COLOUR, ShapeFlags additionalFlags=0)
proto void Print(void var, LogLevel level=LogLevel.NORMAL)
Prints content of variable to console/log.
LogLevel
Enum with severity of the logging message.
Definition LogLevel.c:14
ShapeFlags
Definition ShapeFlags.c:13
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)
SCR_FieldOfViewSettings Attribute
TraceFlags
Definition TraceFlags.c:13