Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_ImportShapefilePlugin.c
Go to the documentation of this file.
1#ifdef WORKBENCH
2[WorkbenchToolAttribute(
3 name: PLUGIN_NAME,
4 description: "Import SHP file as forest, power line, lake, road, prefabs, comments etc.\n\n"
5 + "If Prefabs is filled, creates a prefab for each datapoint (point)\n"
6 + "or creates it as an imported shape's child (polyline/spline).\n"
7 + "- a comment prefab must have a SCR_EditableCommentComponent component\n"
8 + "- a prefab entry can be left empty in some cases (e.g comments)\n"
9 + "- a -1 id in this list prevents the SHP file to reference the entry\n"
10 + "- a negative or undefined id in the SHP file means a random prefab will be used",
11 wbModules: { "WorldEditor" },
12 awesomeFontCode: 0xF56F)]
13class SCR_ImportShapefilePlugin : WorkbenchPlugin
14{
15 /*
16 Import
17 */
18
19 [Attribute(defvalue: "", uiwidget: UIWidgets.FileNamePicker, desc: "SHP File", params: "shp", category: "Import")]
20 protected string m_sSHPFilePath;
21
22 [Attribute(defvalue: "0 0 0", desc: "Import offset", category: "Import")]
23 protected vector m_vOffset;
24
25 /*
26 Prefabs
27 */
28
29 [Attribute(desc: "List of prefabs to be used - use either 'set class' or drag and drop an adequate SCR_SHPPrefabDataList .conf file", category: "Prefab")]
30 protected ref SCR_SHPPrefabDataList m_PrefabDataList;
31
32 [Attribute(defvalue: "1", desc: "Random direction for prefabs imported by points (point and multipoint)", category: "Prefab")]
33 protected bool m_bRandomYaw;
34
35 [Attribute(defvalue: "0", desc: "Generate ForestGenerator's Shape Point Data (polyline and polygon)", category: "Prefab")]
36 protected bool m_bGenerateForestGeneratorPointData;
37
38 [Attribute(defvalue: "0", desc: "Generate Powerline's Shape Point Data as \"per point\" (polyline only)", category: "Prefab")]
39 protected bool m_bGeneratePowerlinePerPointPointData;
40
41 [Attribute(defvalue: "", desc: "Name of the column with IDs - to match Prefabs' ID", category: "Prefab")]
42 protected string m_sIDColumnName;
43
44 [Attribute(defvalue: "", desc: "Name of the column holding the comments' text - can be used with ID Column Name\nLeave empty to force Prefab import", category: "Prefab")]
45 protected string m_sCommentsColumnName;
46
47 /*
48 Shape
49 */
50
51 [Attribute(defvalue: "0.93 0.13 0.33 1", UIWidgets.ColorPicker, desc: "Colour the imported polyline/spline(s) will be", category: "Shape")]
52 protected vector m_vShapeColor;
53
54 [Attribute(defvalue: "0", desc: "Create a spline from the input vectors if ticked (otherwise a polyline is created)", category: "Shape")]
55 protected bool m_bCreateAsSpline;
56
57 protected static const float DUPLICATE_RADIUS_SQ = 0.1 * 0.1;
58 protected static const int RANDOM_PREFAB_ID = -1;
59
60 // used classes
61 protected static const string COMMENT_ENTITY_CLASS = "CommentEntity"; // ((typename)CommentEntity).ToString()
62 protected static const string EDITABLE_COMMENT_COMPONENT_CLASS = "SCR_EditableCommentComponent"; // ((typename)SCR_EditableCommentComponent).ToString();
63 protected static const string EDITABLE_COMMENT_UI_INFO_CLASS = "SCR_EditableCommentUIInfo"; // ((typename)SCR_EditableCommentUIInfo).ToString();
64 protected static const string MAP_DESCRIPTOR_COMPONENT_CLASS = "SCR_MapDescriptorComponent"; // ((typename)SCR_MapDescriptorComponent).ToString();
65 protected static const string POLYLINE_SHAPE_ENTITY_CLASS = "PolylineShapeEntity"; // ((typename)PolylineShapeEntity).ToString();
66 protected static const string SPLINE_SHAPE_ENTITY_CLASS = "SplineShapeEntity"; // ((typename)SplineShapeEntity).ToString();
67
68 // default comment's settings
69 protected static const vector COMMENT_TEXT_COLOUR = { 1, 0.960998, 0.297002 }; // debug yellow
70 protected static const float COMMENT_TEXT_SIZE = 2;
71 protected static const bool COMMENT_TEXT_BACKGROUND = true;
72 protected static const bool COMMENT_FACE_CAMERA = true;
73
74 protected static const string PLUGIN_NAME = "Import Shapefile";
75
76 //------------------------------------------------------------------------------------------------
77 protected override void Run()
78 {
79 if (Workbench.ScriptDialog(PLUGIN_NAME, "", this) == 0)
80 return;
81
82 Execute();
83 }
84
85 //------------------------------------------------------------------------------------------------
88 protected void Execute()
89 {
90 if (m_sSHPFilePath.IsEmpty())
91 {
92 Print("Please provide an SHP file", LogLevel.WARNING);
93 return;
94 }
95
96 WorldEditorAPI worldEditorAPI = SCR_WorldEditorToolHelper.GetWorldEditorAPI();
97 if (worldEditorAPI.UndoOrRedoIsRestoring())
98 return;
99
100 // one -must- keep reference to the collection otherwise chaos ensues: shapes become null, attribute names become empty, etc
101 GeoShapeCollection shapeCollection = GeoShapeLoader.LoadShapeFile(m_sSHPFilePath);
102 if (!shapeCollection)
103 {
104 Print("Shapefile cannot be loaded", LogLevel.ERROR);
105 return;
106 }
107
108 if (shapeCollection.Count() < 1)
109 {
110 Print("Shapefile is empty - leaving", LogLevel.NORMAL);
111 return;
112 }
113
114 Debug.BeginTimeMeasure();
115
116 if (!HasPrefabListPrefabs())
117 Print("[INFO] Prefab list is null/empty", LogLevel.NORMAL);
118
119 worldEditorAPI.BeginEntityAction();
120
121 array<ref SCR_GeneratorShapeImportData> shapeDataArray = {};
122
123 GeoShapeType shapeType;
124 // treat all shapes
125 Debug.BeginTimeMeasure();
126 ProcessShapes(shapeCollection, shapeDataArray, shapeType);
127 Debug.EndTimeMeasure("Shape process");
128
129 if (!shapeDataArray.IsEmpty())
130 {
131 // woodwork
132 if (m_bGenerateForestGeneratorPointData)
133 {
134 Debug.BeginTimeMeasure();
135 GenerateForestGeneratorPointData(shapeDataArray);
136 Debug.EndTimeMeasure("Generate forest generator's point data");
137 }
138
139 // shocking: electrical work
140 if (m_bGeneratePowerlinePerPointPointData && shapeType == GeoShapeType.POLYGON)
141 {
142 Debug.BeginTimeMeasure();
143 GeneratePowerLinePointData(shapeDataArray);
144 Debug.EndTimeMeasure("Generate powerline generator's point data");
145 }
146
147 Debug.BeginTimeMeasure();
148 AttachChildren(shapeDataArray);
149 Debug.EndTimeMeasure("Attach children");
150 }
151
152 worldEditorAPI.EndEntityAction();
153 Debug.EndTimeMeasure("Shape import");
154 }
155
156 //------------------------------------------------------------------------------------------------
161 protected void ProcessShapes(
162 notnull GeoShapeCollection shapeCollection,
163 out notnull array<ref SCR_GeneratorShapeImportData> shapeDataArray,
164 out GeoShapeType shapeType)
165 {
166 WorldEditor worldEditor = Workbench.GetModule(WorldEditor);
167 WBProgressDialog progress = new WBProgressDialog("Importing geometries...", worldEditor);
168 array<ref SCR_GeneratorShapeImportData> shapeDataTempArray;
169 GeoShape shape;
170 GeoPoint geoPoint;
171 GeoMultiPoint geoMultiPoint;
172 GeoPolyline geoPolyline;
173 GeoPolygon geoPolygon;
174
175 float prevProgress, currProgress;
176 for (int i, count = shapeCollection.Count(); i < count; i++)
177 {
178 shape = shapeCollection.Get(i);
179 if (!shape)
180 {
181 PrintFormat("Shape #%1/%2 is null?! Skipping", i + 1, count, level: LogLevel.WARNING);
182 continue;
183 }
184
185 shapeDataTempArray = null;
186
187 shapeType = shape.GetType();
188 switch (shapeType)
189 {
190 case GeoShapeType.POINT:
191 geoPoint = GeoPoint.Cast(shape);
192 if (geoPoint)
193 Load_Point(geoPoint);
194
195 break;
196
197 case GeoShapeType.MULTI_POINT:
198 shapeType = GeoShapeType.MULTI_POINT;
199 geoMultiPoint = GeoMultiPoint.Cast(shape);
200 if (geoMultiPoint)
201 Load_Multipoint(geoMultiPoint);
202
203 break;
204
205 case GeoShapeType.POLYLINE:
206 geoPolyline = GeoPolyline.Cast(shape);
207 if (geoPolyline)
208 shapeDataTempArray = Load_Polylines(GeoPolyline.Cast(shape));
209
210 break;
211
212 case GeoShapeType.POLYGON:
213 geoPolygon = GeoPolygon.Cast(shape);
214 if (geoPolygon)
215 shapeDataTempArray = Load_Polygons(GeoPolygon.Cast(shape));
216
217 break;
218
219 default:
220 Print("Unsupported type " + typename.EnumToString(GeoShapeType, shape.GetType()), LogLevel.WARNING);
221 break;
222 }
223
224 if (shapeDataTempArray)
225 {
226 foreach (SCR_GeneratorShapeImportData data : shapeDataTempArray)
227 {
228 shapeDataArray.Insert(data); // can't InsertAll with array of ref
229 }
230 }
231
232 currProgress = i / count;
233 if (currProgress - prevProgress >= 0.01) // min 1%
234 {
235 progress.SetProgress(currProgress); // expensive
236 prevProgress = currProgress;
237 }
238 }
239 }
240
241 //------------------------------------------------------------------------------------------------
243 protected void Load_Point(notnull GeoPoint point)
244 {
245 CreateFromPoints(point, { point.GetCoords() });
246 }
247
248 //------------------------------------------------------------------------------------------------
250 protected void Load_Multipoint(notnull GeoMultiPoint multipoint)
251 {
252 array<vector> points = {};
253 GeoVertexCollection vertices = multipoint.GetPoints();
254 for (int i = 0, count = vertices.Count(); i < count; i++)
255 {
256 points.Insert(vertices[i]);
257 }
258
259 CreateFromPoints(multipoint, points);
260 }
261
262 //------------------------------------------------------------------------------------------------
264 protected array<ref SCR_GeneratorShapeImportData> Load_Polygons(notnull GeoPolygon polygon)
265 {
266 WorldEditorAPI worldEditorAPI = SCR_WorldEditorToolHelper.GetWorldEditorAPI();
267
268 bool hasIdAttribute = polygon.GetAttributes().HasAttrib(m_sIDColumnName);
269
270 array<ref SCR_GeneratorShapeImportData> result = {};
271 IEntitySource entitySource;
272 SCR_GeneratorShapeImportData data;
273
274 int currentLayerId = worldEditorAPI.GetCurrentEntityLayerId();
275
276 GeoVertexCollection geoVertexCollectionJ;
277 // polygon's parts (in case polygon has holes)
278 for (int j = 0, count = polygon.PartsCount(); j < count; j++)
279 {
280 geoVertexCollectionJ = polygon.GetPart(j);
281 int polygonPointsCount = geoVertexCollectionJ.Count();
282
283 float traceX = geoVertexCollectionJ[0][0] + m_vOffset[0];
284 float traceZ = geoVertexCollectionJ[0][2] + m_vOffset[2];
285 float pntY = worldEditorAPI.GetTerrainSurfaceY(traceX, traceZ) + m_vOffset[1];
286
287 vector bb3Dmin = { traceX, pntY, traceZ };
288 vector bb3Dmax = { traceX, pntY, traceZ };
289
290 //Part's points - calculate polygon's 3D Bbox
291 for (int k = 0; k < polygonPointsCount; k++)
292 {
293 traceX = geoVertexCollectionJ[k][0] + m_vOffset[0];
294 traceZ = geoVertexCollectionJ[k][2] + m_vOffset[2];
295 pntY = worldEditorAPI.GetTerrainSurfaceY(traceX, traceZ) + m_vOffset[1];
296
297 vector pnt = { traceX, pntY, traceZ };
298
299 for (int xyz = 0; xyz < 3; xyz++)
300 {
301 float val = pnt[xyz];
302
303 if (val < bb3Dmin[xyz])
304 bb3Dmin[xyz] = val;
305
306 if (val > bb3Dmax[xyz])
307 bb3Dmax[xyz] = val;
308 }
309 }
310
311 // calculate polygon's origin(as center of 3D Bbox)
312 vector polygonOrigin = ((bb3Dmax - bb3Dmin) * 0.5) + bb3Dmin;
313
314 // create Polyline/Spline ShapeEntity
315 if (m_bCreateAsSpline)
316 entitySource = worldEditorAPI.CreateEntity(SPLINE_SHAPE_ENTITY_CLASS, "", currentLayerId, null, polygonOrigin, vector.Zero);
317 else
318 entitySource = worldEditorAPI.CreateEntity(POLYLINE_SHAPE_ENTITY_CLASS, "", currentLayerId, null, polygonOrigin, vector.Zero);
319
320 worldEditorAPI.SetVariableValue(entitySource, null, "IsClosed", "1");
321
322 data = new SCR_GeneratorShapeImportData();
323
324 // part's points - calculate point's local coords(center of 3D Bbox is the origin)
325 for (int k = 0; k < polygonPointsCount - 1; k++) // polygonPointsCount - 1 because the last points duplicates the first one
326 {
327 vector worldCoords;
328 worldCoords[0] = geoVertexCollectionJ[k][0] + m_vOffset[0];
329 worldCoords[2] = geoVertexCollectionJ[k][2] + m_vOffset[2];
330 worldCoords[1] = worldEditorAPI.GetTerrainSurfaceY(worldCoords[0], worldCoords[2]) + m_vOffset[1];
331 data.points.Insert(worldCoords);
332
333 worldEditorAPI.CreateObjectArrayVariableMember(entitySource, null, "Points", "ShapePoint", k);
334 worldEditorAPI.SetVariableValue(entitySource, { new ContainerIdPathEntry("Points", k) }, "Position", (worldCoords - polygonOrigin).ToString(false));
335 }
336
337 // setup polygons colors
338 float finalR = m_vShapeColor[0];
339 float finalG = m_vShapeColor[1];
340 float finalB = m_vShapeColor[2];
341
342 if (j != 0)
343 {
344 // invert default color to better distinguish inner polygons (holes) from outer (main) polygon
345 finalR = Math.AbsFloat(finalR - 1);
346 finalG = Math.AbsFloat(finalG - 1);
347 finalB = Math.AbsFloat(finalB - 1);
348 }
349
350 worldEditorAPI.SetVariableValue(entitySource, null, "LineColor", string.Format("%1 %2 %3 1", finalR, finalG, finalB));
351
352 data.source = entitySource;
353 data.entity = worldEditorAPI.SourceToEntity(entitySource);
354 data.GenerateAAB();
355
356 if (hasIdAttribute)
357 data.id = polygon.GetAttributes().GetIntByName(m_sIDColumnName);
358 else
359 data.id = RANDOM_PREFAB_ID;
360
361 result.Insert(data);
362 }
363
364 return result;
365 }
366
367 //------------------------------------------------------------------------------------------------
369 protected array<ref SCR_GeneratorShapeImportData> Load_Polylines(notnull GeoPolyline polyline)
370 {
371 WorldEditorAPI worldEditorAPI = SCR_WorldEditorToolHelper.GetWorldEditorAPI();
372
373 GeoVertexCollection vertices = polyline.GetVertices();
374 int polylinePointsCount = vertices.Count();
375
376 float traceX = vertices[0][0] + m_vOffset[0];
377 float traceZ = vertices[0][2] + m_vOffset[2];
378 float pntY = worldEditorAPI.GetTerrainSurfaceY(traceX, traceZ) + m_vOffset[1];
379
380 vector bb3Dmin = { traceX, pntY, traceZ };
381 vector bb3Dmax = { traceX, pntY, traceZ };
382
383 // polyline's points - calculate polyline's 3D Bbox
384 for (int k = 0; k < polylinePointsCount; k++)
385 {
386 traceX = vertices[k][0] + m_vOffset[0];
387 traceZ = vertices[k][2] + m_vOffset[2];
388 pntY = worldEditorAPI.GetTerrainSurfaceY(traceX, traceZ) + m_vOffset[1];
389
390 vector pnt = { traceX, pntY, traceZ };
391
392 for (int xyz = 0; xyz < 3; xyz++)
393 {
394 float val = pnt[xyz];
395
396 if (val < bb3Dmin[xyz])
397 bb3Dmin[xyz] = val;
398
399 if (val > bb3Dmax[xyz])
400 bb3Dmax[xyz] = val;
401 }
402 }
403
404 // calculate polygon's origin(as center of 3D Bbox)
405 vector polylineOrigin = ((bb3Dmax - bb3Dmin) * 0.5) + bb3Dmin;
406
407 // create Polyline/Spline ShapeEntity
408 IEntitySource entitySource;
409 if (m_bCreateAsSpline)
410 entitySource = worldEditorAPI.CreateEntity(SPLINE_SHAPE_ENTITY_CLASS, "", worldEditorAPI.GetCurrentEntityLayerId(), null, polylineOrigin, vector.Zero);
411 else
412 entitySource = worldEditorAPI.CreateEntity(POLYLINE_SHAPE_ENTITY_CLASS, "", worldEditorAPI.GetCurrentEntityLayerId(), null, polylineOrigin, vector.Zero);
413
414 SCR_GeneratorShapeImportData data = new SCR_GeneratorShapeImportData();
415
416 // polyline's points - calculate point's local coords(center of 3D Bbox is the origin)
417 for (int k = 0; k < polylinePointsCount; k++)
418 {
419 vector worldCoords;
420 worldCoords[0] = vertices[k][0] + m_vOffset[0];
421 worldCoords[2] = vertices[k][2] + m_vOffset[2];
422 worldCoords[1] = worldEditorAPI.GetTerrainSurfaceY(worldCoords[0], worldCoords[2]) + m_vOffset[1];
423 data.points.Insert(worldCoords);
424 data.GenerateAAB();
425
426 worldEditorAPI.CreateObjectArrayVariableMember(entitySource, null, "Points", "ShapePoint", k);
427 worldEditorAPI.SetVariableValue(entitySource, { new ContainerIdPathEntry("Points", k) }, "Position", (worldCoords - polylineOrigin).ToString(false));
428 }
429
430 worldEditorAPI.SetVariableValue(entitySource, null, "LineColor", m_vShapeColor.ToString(false) + " 1");
431
432 data.source = entitySource;
433 data.entity = worldEditorAPI.SourceToEntity(entitySource);
434 data.GenerateAAB();
435
436 if (polyline.GetAttributes().HasAttrib(m_sIDColumnName))
437 data.id = polyline.GetAttributes().GetIntByName(m_sIDColumnName);
438 else
439 data.id = RANDOM_PREFAB_ID;
440
441 return { data };
442 }
443
444 //------------------------------------------------------------------------------------------------
446 protected void CreateFromPoints(notnull GeoShape shape, notnull array<vector> points)
447 {
448 // comment import
449 if (!m_sCommentsColumnName.IsEmpty() && shape.GetAttributes().HasAttrib(m_sCommentsColumnName))
450 {
451 Print("Importing comment", LogLevel.NORMAL);
452 string comment = shape.GetAttributes().GetStringByName(m_sCommentsColumnName).Trim();
453 if (comment.IsEmpty())
454 {
455 Print("Empty comment, skipping...", LogLevel.NORMAL);
456 return;
457 }
458
459 foreach (vector pos : points)
460 {
461 CreateComment(comment, pos + m_vOffset, GetPrefab());
462 }
463
464 return;
465 }
466
467 // let's save some CPU cycles
468 if (!HasPrefabListPrefabs())
469 {
470 Print("Cannot import (multi)point as no Prefabs are available and the shape does not have comment column ID \"" + m_sCommentsColumnName + "\" defined", LogLevel.WARNING);
471 return;
472 }
473
474 WorldEditorAPI worldEditorAPI = SCR_WorldEditorToolHelper.GetWorldEditorAPI();
475
476 // prefab import
477 Print("Importing prefab(s)", LogLevel.NORMAL);
478 for (int i = 0, count = points.Count(); i < count; i++)
479 {
480 ResourceName chosenPrefab = GetPrefab();
481 if (chosenPrefab.IsEmpty())
482 {
483 Print("Prefab is empty, skipping", LogLevel.VERBOSE);
484 continue;
485 }
486
487 vector pos = points[0] + m_vOffset;
489 if (m_bRandomYaw)
490 angles[1] = Math.RandomFloat(0, 360);
491
492 worldEditorAPI.CreateEntityExt(chosenPrefab, "", worldEditorAPI.GetCurrentEntityLayerId(), null, pos, angles, TraceFlags.WORLD);
493 }
494 }
495
496 //------------------------------------------------------------------------------------------------
498 protected void CreateComment(string comment, vector pos, ResourceName commentPrefab = string.Empty)
499 {
500 WorldEditorAPI worldEditorAPI = SCR_WorldEditorToolHelper.GetWorldEditorAPI();
501
502 if (commentPrefab.IsEmpty()) // then generic comment
503 {
504 IEntitySource entitySource = worldEditorAPI.CreateEntity(COMMENT_ENTITY_CLASS, "", worldEditorAPI.GetCurrentEntityLayerId(), null, pos, vector.Zero);
505
506 worldEditorAPI.SetVariableValue(entitySource, null, "m_Comment", comment);
507 worldEditorAPI.SetVariableValue(entitySource, null, "m_Color", COMMENT_TEXT_COLOUR.ToString(false));
508 worldEditorAPI.SetVariableValue(entitySource, null, "m_FaceCamera", COMMENT_FACE_CAMERA.ToString(true));
509 worldEditorAPI.SetVariableValue(entitySource, null, "m_TextBackground", COMMENT_TEXT_BACKGROUND.ToString(true));
510 worldEditorAPI.SetVariableValue(entitySource, null, "m_Size", COMMENT_TEXT_SIZE.ToString());
511 return;
512 }
513
514 // otherwise, fancy comment
515 IEntitySource entitySource = worldEditorAPI.CreateEntity(commentPrefab, "", worldEditorAPI.GetCurrentEntityLayerId(), null, pos, vector.Zero);
516 if (!entitySource)
517 {
518 Print("Cannot create " + commentPrefab + " comment entity", LogLevel.ERROR);
519 return;
520 }
521
522 if (SCR_BaseContainerTools.FindComponentSource(entitySource, EDITABLE_COMMENT_COMPONENT_CLASS))
523 {
524 array<ref ContainerIdPathEntry> path = {};
525 path.Insert(new ContainerIdPathEntry(EDITABLE_COMMENT_COMPONENT_CLASS));
526 worldEditorAPI.CreateObjectVariableMember(entitySource, path, "m_UIInfo", EDITABLE_COMMENT_UI_INFO_CLASS);
527
528 path.Insert(new ContainerIdPathEntry("m_UIInfo"));
529 worldEditorAPI.SetVariableValue(entitySource, path, "Name", comment);
530 }
531
532 if (SCR_BaseContainerTools.FindComponentSource(entitySource, MAP_DESCRIPTOR_COMPONENT_CLASS))
533 {
534 array<ref ContainerIdPathEntry> path = {};
535 path.Insert(new ContainerIdPathEntry(MAP_DESCRIPTOR_COMPONENT_CLASS));
536 worldEditorAPI.SetVariableValue(entitySource, path, "DisplayName", comment);
537 }
538 }
539
540 //------------------------------------------------------------------------------------------------
542 protected bool HasPrefabListPrefabs()
543 {
544 if (!m_PrefabDataList || !m_PrefabDataList.m_aPrefabsData)
545 return false;
546
547 foreach (SCR_SHPPrefabData prefabData : m_PrefabDataList.m_aPrefabsData)
548 {
549 if (!prefabData.m_sPrefab.IsEmpty())
550 return true;
551 }
552
553 return false;
554 }
555
556 //------------------------------------------------------------------------------------------------
557 // \param[in] id -1 for a random value
558 protected ResourceName GetPrefab(int id = RANDOM_PREFAB_ID)
559 {
560 if (!m_PrefabDataList || !m_PrefabDataList.m_aPrefabsData || m_PrefabDataList.m_aPrefabsData.IsEmpty())
561 return string.Empty;
562
563 // if (id == RANDOM_PREFAB_ID)
564 if (id < 0)
565 return m_PrefabDataList.m_aPrefabsData.GetRandomElement().m_sPrefab;
566
567 // else it's a search
568 foreach (SCR_SHPPrefabData prefabData : m_PrefabDataList.m_aPrefabsData)
569 {
570 if (prefabData.m_iID == id)
571 return prefabData.m_sPrefab;
572 }
573
574 // not found = empty
575 return string.Empty;
576 }
577
578 //------------------------------------------------------------------------------------------------
580 protected bool IsPointDuplicate(vector toCheck, array<vector> points)
581 {
582 foreach (vector point : points)
583 {
584 if (vector.DistanceSq(toCheck, point) < DUPLICATE_RADIUS_SQ)
585 return true;
586 }
587
588 return false;
589 }
590
591 //------------------------------------------------------------------------------------------------
594 protected void GenerateForestGeneratorPointData(notnull array<ref SCR_GeneratorShapeImportData> forestShapeDataArray)
595 {
596 int count = forestShapeDataArray.Count();
597 if (count < 1)
598 return;
599
600 WorldEditor worldEditor = Workbench.GetModule(WorldEditor);
601 WorldEditorAPI worldEditorAPI = worldEditor.GetApi();
602 WBProgressDialog progress = new WBProgressDialog("Generating forest generator points data...", worldEditor);
603
604 BaseContainerList points;
605 array<SCR_GeneratorShapeImportData> collidedShapes;
606 SCR_GeneratorShapeImportData otherForestShapeData;
607
608 float prevProgress, currProgress;
609 foreach (int i, SCR_GeneratorShapeImportData forestShapeData : forestShapeDataArray)
610 {
611 points = forestShapeData.source.GetObjectArray("Points");
612 collidedShapes = {};
613
614 for (int y = 0; y < count; y++)
615 {
616 if (y == i)
617 continue;
618
619 otherForestShapeData = forestShapeDataArray[y];
620 if (forestShapeData.bbox.DetectCollision2D(otherForestShapeData.bbox))
621 collidedShapes.Insert(otherForestShapeData);
622 }
623
624 bool wasPreviousDuplicate = false;
625
626 forestShapeData.points.Insert(forestShapeData.points[0]); // Duplicate first point
627
628 for (int p = 0, countPoints = forestShapeData.points.Count(); p < countPoints; p++)
629 {
630 bool isDuplicate = false;
631 for (int y = 0, otherCount = collidedShapes.Count(); y < otherCount; y++)
632 {
633 if (!IsPointDuplicate(forestShapeData.points[p], collidedShapes[y].points))
634 continue;
635
636 isDuplicate = true;
637 if (!wasPreviousDuplicate)
638 {
639 wasPreviousDuplicate = true;
640 break;
641 }
642
643 worldEditorAPI.CreateObjectArrayVariableMember(points[p - 1], null, "Data", "ForestGeneratorPointData", 0);
644
645 BaseContainerList dataArr = points[p - 1].GetObjectArray("Data");
646 int dataCount = dataArr.Count();
647 for (int j = 0; j < dataCount; ++j)
648 {
649 BaseContainer data = dataArr.Get(j);
650 if (data.GetClassName() != "ForestGeneratorPointData")
651 continue;
652
653 array<ref ContainerIdPathEntry> containerPath = {
654 new ContainerIdPathEntry("Points", p - 1),
655 new ContainerIdPathEntry("Data", j),
656 };
657
658 worldEditorAPI.SetVariableValue(forestShapeData.source, containerPath, "m_bSmallOutline", "false");
659 worldEditorAPI.SetVariableValue(forestShapeData.source, containerPath, "m_bMiddleOutline", "false");
660 break;
661 }
662
663 wasPreviousDuplicate = true;
664 break;
665 }
666
667 if (isDuplicate)
668 continue;
669
670 wasPreviousDuplicate = false;
671 }
672
673 forestShapeData.points.Remove(forestShapeData.points.Count() - 1); // Remove the duplicate point
674
675 currProgress = i / count;
676 if (currProgress - prevProgress >= 0.01) // min 1%
677 {
678 progress.SetProgress(currProgress); // expensive
679 prevProgress = currProgress;
680 }
681 }
682 }
683
684 //------------------------------------------------------------------------------------------------
685 protected void GeneratePowerLinePointData(notnull array<ref SCR_GeneratorShapeImportData> shapeDataArray)
686 {
687 WorldEditorAPI worldEditorAPI = SCR_WorldEditorToolHelper.GetWorldEditorAPI();
688
689 BaseContainerList shapePoints;
690 foreach (int i, SCR_GeneratorShapeImportData forestShapeData : shapeDataArray)
691 {
692 if (!forestShapeData.source || !forestShapeData.points || forestShapeData.points.IsEmpty())
693 continue;
694
695 shapePoints = forestShapeData.source.GetObjectArray("Points");
696 if (!shapePoints)
697 continue;
698
699 if (!worldEditorAPI.CreateObjectArrayVariableMember(shapePoints[0], null, "Data", "SCR_PowerlineGeneratorPointData", 0))
700 continue;
701
702 worldEditorAPI.SetVariableValue(shapePoints[0], { new ContainerIdPathEntry("Data", 0) }, "m_bGeneratePerPoint", "1");
703 }
704 }
705
706 //------------------------------------------------------------------------------------------------
707 protected void AttachChildren(notnull array<ref SCR_GeneratorShapeImportData> shapeDataArray)
708 {
709 WorldEditor worldEditor = Workbench.GetModule(WorldEditor);
710 WorldEditorAPI worldEditorAPI = worldEditor.GetApi();
711 WBProgressDialog progress = new WBProgressDialog("Attaching children...", worldEditor);
712 int count = shapeDataArray.Count();
713
714 float prevProgress, currProgress;
715 foreach (int i, SCR_GeneratorShapeImportData shapeData : shapeDataArray)
716 {
717 ResourceName dataPrefab = GetPrefab(shapeData.id);
718 if (dataPrefab.IsEmpty()) // id not found? go random
719 dataPrefab = GetPrefab();
720
721 if (!dataPrefab.IsEmpty())
722 worldEditorAPI.CreateEntity(dataPrefab, "", 0, shapeData.source, vector.Zero, vector.Zero);
723
724 currProgress = i / count;
725 if (currProgress - prevProgress >= 0.01) // min 1%
726 {
727 progress.SetProgress(currProgress); // expensive
728 prevProgress = currProgress;
729 }
730 }
731 }
732
733 //------------------------------------------------------------------------------------------------
734 [ButtonAttribute("Process", true)]
735 protected int ButtonProcess()
736 {
737 return 1;
738 }
739
740 //------------------------------------------------------------------------------------------------
741 [ButtonAttribute("Cancel")]
742 protected int ButtonCancel()
743 {
744 return 0;
745 }
746}
747
752class SCR_SHPPrefabDataList
753{
754 [Attribute(desc: "ID-Prefab list")]
755 ref array<ref SCR_SHPPrefabData> m_aPrefabsData;
756}
757
758[BaseContainerProps(), SCR_BaseContainerCustomTitleFields({ "m_iID", "m_sPrefab" }, "%1 - %2")]
759class SCR_SHPPrefabData
760{
761 [Attribute(defvalue: "-1", desc: "Prefab's .shp ID")]
762 int m_iID;
763
764 [Attribute(desc: "Prefab assigned to this ID", params: "et")]
766}
767
768class SCR_GeneratorShapeImportData
769{
770 IEntitySource source;
771 IEntity entity;
772 int id;
773 ref SCR_AABB bbox;
774 ref array<vector> points = {};
775
776 //------------------------------------------------------------------------------------------------
778 void GenerateAAB()
779 {
780 bbox = new SCR_AABB(points);
781 }
782}
783#endif // WORKBENCH
string path
AddonBuildInfoTool id
void ContainerIdPathEntry(string propertyName, int index=-1)
Definition worldEditor.c:30
ref array< string > angles
bool Execute(notnull SCR_AIGroupUtilityComponent groupUtility, vector targetPosition, SCR_AIActivitySmokeCoverFeatureProperties smokeCoverProperties, notnull array< AIAgent > avoidAgents, notnull array< AIAgent > excludeAgents, int maxPositionCount=1, SCR_AIActivityBase contextActivity=null)
SCR_AIAnimation_Loitering BaseContainerProps
Commanding menu commanding element class.
Get all prefabs that have the spawner data
override void Run()
bool ButtonCancel()
vector m_vOffset
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
enum EVehicleType IEntity
class WorkbenchDialog_AbortRetryIgnore ButtonAttribute("OK", true)
Definition Debug.c:13
Definition Math.c:13
allow to define multiple fields (string or ResourceName) - up to 5 elements
Definition Attributes.c:74
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
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