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
11class SCR_ShapeBrushTool : WorldEditorTool
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")]
21 protected float m_fMinStep;
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;
31 protected bool m_bIsShapeClosed;
33 protected bool m_bIsDrawing;
35 protected vector m_vLastCameraPosition;
36 protected vector m_vLastMousePosition;
37 protected ref Shape m_BrushShape;
38 protected int m_iBrushShapeColor;
40 protected ref array<vector> m_aShapePoints = {};
41 protected ref array<float> m_aShapeRadii = {};
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;
49 protected static const int BRUSH_COLOUR_DEFAULT = 0xFF00FF00;
50 protected static const int BRUSH_COLOUR_ACTIVE = 0xFFFF8800;
52 protected static const float CURSOR_RATIO = 0.25;
54 protected static const int ARC_STEP = 30;
55 protected static const int ARC_SAFETY = 0;
58 protected void DrawBrush()
61 m_iBrushShapeColor = BRUSH_COLOUR_ACTIVE;
63 m_iBrushShapeColor = BRUSH_COLOUR_DEFAULT;
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);
76 protected array<vector> GetShapeBorder()
78 int shapePointsCount = m_aShapePoints.Count();
79 if (shapePointsCount < 1)
82 array<vector> result = {};
83 if (shapePointsCount == 1)
85 for (
int i; i < 360; i += ARC_STEP)
87 result.Insert(GetPointAtAngle(m_aShapePoints[0], i, m_aShapeRadii[0],
true));
94 return SCR_ArrayHelperT<vector>.GetCopy(m_aShapePoints);
96 array<vector> rightPoints = {};
97 array<vector> rejectedPoints = {};
98 result.Reserve(shapePointsCount);
100 vector prevShapePoint;
101 foreach (
int shapePointIndex, vector currShapePoint : m_aShapePoints)
103 if (shapePointIndex == 0)
105 float angleDeg = vector.Direction(currShapePoint, m_aShapePoints[1]).ToYaw();
106 for (
int additionalAngleDeg = 90; additionalAngleDeg <= 270; additionalAngleDeg += ARC_STEP)
108 point = GetPointAtAngle(currShapePoint, angleDeg + additionalAngleDeg, m_aShapeRadii[shapePointIndex] + ARC_SAFETY,
false);
109 if (IsValidPoint(point, shapePointIndex))
110 result.Insert(point);
112 rejectedPoints.Insert(point);
115 else if (shapePointIndex == shapePointsCount - 1)
117 float angleDeg = vector.Direction(m_aShapePoints[shapePointIndex - 1], currShapePoint).ToYaw();
119 for (
int additionalAngleDeg = -90; additionalAngleDeg <= 90; additionalAngleDeg += ARC_STEP)
121 point = GetPointAtAngle(currShapePoint, angleDeg + additionalAngleDeg, m_aShapeRadii[shapePointIndex] + ARC_SAFETY,
false);
122 if (IsValidPoint(point, shapePointIndex))
123 result.Insert(point);
125 rejectedPoints.Insert(point);
130 float angleDeg = vector.Direction(prevShapePoint, m_aShapePoints[shapePointIndex + 1]).ToYaw();
132 point = GetPointAtAngle(currShapePoint, angleDeg - 90, m_aShapeRadii[shapePointIndex],
false);
133 if (IsValidPoint(point, shapePointIndex))
134 result.Insert(point);
136 rejectedPoints.Insert(point);
138 point = GetPointAtAngle(currShapePoint, angleDeg + 90, m_aShapeRadii[shapePointIndex],
false);
139 if (IsValidPoint(point, shapePointIndex))
140 rightPoints.Insert(point);
142 rejectedPoints.Insert(point);
145 prevShapePoint = currShapePoint;
148 SCR_ArrayHelperT<vector>.Reverse(rightPoints);
149 result.InsertAll(rightPoints);
152 foreach (
int i, vector resultPoint : result)
154 resultPoint[1] = m_API.GetTerrainSurfaceY(resultPoint[0], resultPoint[2]);
155 result[i] = resultPoint;
167 protected vector GetPointAtAngle(vector origin,
float angleDeg,
float distance,
bool setTerrainY)
172 float angleRad = -(angleDeg - 90) * Math.DEG2RAD;
175 vector point = origin + { Math.Cos(angleRad) *
distance, 0, Math.Sin(angleRad) *
distance };
176 point[1] = m_API.GetTerrainSurfaceY(point[0], point[2]);
181 return origin + { Math.Cos(angleRad) *
distance, 0, Math.Sin(angleRad) *
distance };
190 bool IsValidPoint(vector point,
int shapePointIndex)
192 foreach (
int otherPointIndex, vector otherShapePoint : m_aShapePoints)
194 if (shapePointIndex == otherPointIndex)
197 if (vector.DistanceXZ(point, otherShapePoint) < m_aShapeRadii[otherPointIndex])
205 protected void DrawShape()
210 foreach (
int i, vector shapePoint : m_aShapePoints)
218 array<vector> borderPoints = GetShapeBorder();
219 foreach (
int i, vector borderPoint : borderPoints)
228 protected IEntitySource CreateShapeEntity(notnull array<vector> shapePoints)
230 if (shapePoints.IsEmpty())
236 m_API.BeginEntityAction();
238 string shapeClassname;
239 if (m_iShapeType == 0)
240 shapeClassname =
"PolylineShapeEntity";
242 shapeClassname =
"SplineShapeEntity";
244 IEntitySource shapeEntitySource = m_API.CreateEntity(shapeClassname,
string.Empty, m_API.GetCurrentEntityLayerId(), null, shapePoints[0], vector.Zero);
245 if (!shapeEntitySource)
247 m_API.EndEntityAction();
250 Workbench.Dialog(
"Error",
"Cannot create " + shapeClassname +
" shape");
256 foreach (
int i, vector point : shapePoints)
258 if (!m_API.CreateObjectArrayVariableMember(shapeEntitySource, null,
"Points",
"ShapePoint", i))
271 m_API.SetVariableValue(shapeEntitySource, {
new ContainerIdPathEntry(
"Points", i) },
"Position",
string.Format(
"%1 %2 %3", point[0], point[1], point[2]));
274 if (m_bIsShapeClosed)
275 m_API.SetVariableValue(shapeEntitySource, null,
"IsClosed",
"1");
277 m_API.EndEntityAction();
279 return shapeEntitySource;
290 protected float AdjustValueUsingScrollwheel(
float delta,
float currentValue,
float min,
float max,
float step)
293 float value = currentValue + (delta / 120) * step;
305 override void OnLeaveEvent()
312 override void OnMouseMoveEvent(
float x,
float y)
314 vector traceStart, traceEnd, traceDir;
315 if (!m_API.TraceWorldPos(x, y,
TraceFlags.WORLD, traceStart, traceEnd, traceDir))
320 if (vector.DistanceSqXZ(traceEnd, m_aShapePoints[m_aShapePoints.Count() - 1]) >= m_fMinStep * m_fMinStep)
324 m_aShapePoints.Insert(m_vLastMousePosition);
332 m_vLastCameraPosition = traceStart;
333 m_vLastMousePosition = traceEnd;
339 override void OnMousePressEvent(
float x,
float y, WETMouseButtonFlag buttons)
341 if (buttons != WETMouseButtonFlag.LEFT)
346 vector traceStart, traceEnd, traceDir;
347 m_API.TraceWorldPos(x, y,
TraceFlags.WORLD, traceStart, traceEnd, traceDir);
349 m_vLastCameraPosition = traceStart;
350 m_vLastMousePosition = traceEnd;
352 m_aShapePoints.Clear();
353 m_aShapeRadii.Clear();
355 m_aShapePoints.Insert(m_vLastMousePosition);
364 override void OnMouseReleaseEvent(
float x,
float y, WETMouseButtonFlag buttons)
366 if (buttons != WETMouseButtonFlag.LEFT)
369 vector traceStart, traceEnd, traceDir;
370 if (!m_API.TraceWorldPos(x, y,
TraceFlags.WORLD, traceStart, traceEnd, traceDir))
373 m_bIsDrawing =
false;
374 if (m_aShapePoints.Count() > 1 || traceEnd != m_aShapePoints[0])
376 m_aShapePoints.Insert(m_vLastMousePosition);
380 array<vector> borderPoints = GetShapeBorder();
382 IEntitySource shapeEntitySource = CreateShapeEntity(borderPoints);
384 m_aShapePoints.Clear();
385 m_aShapeRadii.Clear();
388 if (shapeEntitySource)
389 m_API.SetEntitySelection(shapeEntitySource);
397 override void OnWheelEvent(
int delta)
400 if (GetModifierKeyState(ModifierKey.CONTROL))
407 m_fRadius = AdjustValueUsingScrollwheel(delta,
m_fRadius, RADIUS_MIN_WHILE_DRAWING, RADIUS_MAX, RADIUS_STEP);
411 m_fRadius = AdjustValueUsingScrollwheel(delta,
m_fRadius, RADIUS_MIN, RADIUS_MAX, RADIUS_STEP);
415 UpdatePropertyPanel();
422 m_aShapePoints.Clear();
423 m_aShapeRadii.Clear();
424 m_bIsDrawing =
false;
428 override void OnDeActivate()
void ContainerIdPathEntry(string propertyName, int index=-1)
Shape CreateCircle(vector pos, vector aroundDir, float radius, int color, int subdivisions, ShapeFlags flags)
override void OnActivate()
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.
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