2[WorkbenchToolAttribute(
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
20 protected string m_sSHPFilePath;
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;
32 [
Attribute(defvalue:
"1",
desc:
"Random direction for prefabs imported by points (point and multipoint)",
category:
"Prefab")]
33 protected bool m_bRandomYaw;
35 [
Attribute(defvalue:
"0",
desc:
"Generate ForestGenerator's Shape Point Data (polyline and polygon)",
category:
"Prefab")]
36 protected bool m_bGenerateForestGeneratorPointData;
38 [
Attribute(defvalue:
"0",
desc:
"Generate Powerline's Shape Point Data as \"per point\" (polyline only)",
category:
"Prefab")]
39 protected bool m_bGeneratePowerlinePerPointPointData;
41 [
Attribute(defvalue:
"",
desc:
"Name of the column with IDs - to match Prefabs' ID",
category:
"Prefab")]
42 protected string m_sIDColumnName;
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;
52 protected vector m_vShapeColor;
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;
57 protected static const float DUPLICATE_RADIUS_SQ = 0.1 * 0.1;
58 protected static const int RANDOM_PREFAB_ID = -1;
61 protected static const string COMMENT_ENTITY_CLASS =
"CommentEntity";
62 protected static const string EDITABLE_COMMENT_COMPONENT_CLASS =
"SCR_EditableCommentComponent";
63 protected static const string EDITABLE_COMMENT_UI_INFO_CLASS =
"SCR_EditableCommentUIInfo";
64 protected static const string MAP_DESCRIPTOR_COMPONENT_CLASS =
"SCR_MapDescriptorComponent";
65 protected static const string POLYLINE_SHAPE_ENTITY_CLASS =
"PolylineShapeEntity";
66 protected static const string SPLINE_SHAPE_ENTITY_CLASS =
"SplineShapeEntity";
69 protected static const vector COMMENT_TEXT_COLOUR = { 1, 0.960998, 0.297002 };
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;
74 protected static const string PLUGIN_NAME =
"Import Shapefile";
77 protected override void Run()
79 if (Workbench.ScriptDialog(PLUGIN_NAME,
"",
this) == 0)
90 if (m_sSHPFilePath.IsEmpty())
96 WorldEditorAPI worldEditorAPI = SCR_WorldEditorToolHelper.GetWorldEditorAPI();
97 if (worldEditorAPI.UndoOrRedoIsRestoring())
101 GeoShapeCollection shapeCollection = GeoShapeLoader.LoadShapeFile(m_sSHPFilePath);
102 if (!shapeCollection)
108 if (shapeCollection.Count() < 1)
114 Debug.BeginTimeMeasure();
116 if (!HasPrefabListPrefabs())
119 worldEditorAPI.BeginEntityAction();
121 array<ref SCR_GeneratorShapeImportData> shapeDataArray = {};
123 GeoShapeType shapeType;
125 Debug.BeginTimeMeasure();
126 ProcessShapes(shapeCollection, shapeDataArray, shapeType);
127 Debug.EndTimeMeasure(
"Shape process");
129 if (!shapeDataArray.IsEmpty())
132 if (m_bGenerateForestGeneratorPointData)
134 Debug.BeginTimeMeasure();
135 GenerateForestGeneratorPointData(shapeDataArray);
136 Debug.EndTimeMeasure(
"Generate forest generator's point data");
140 if (m_bGeneratePowerlinePerPointPointData && shapeType == GeoShapeType.POLYGON)
142 Debug.BeginTimeMeasure();
143 GeneratePowerLinePointData(shapeDataArray);
144 Debug.EndTimeMeasure(
"Generate powerline generator's point data");
147 Debug.BeginTimeMeasure();
148 AttachChildren(shapeDataArray);
149 Debug.EndTimeMeasure(
"Attach children");
152 worldEditorAPI.EndEntityAction();
153 Debug.EndTimeMeasure(
"Shape import");
161 protected void ProcessShapes(
162 notnull GeoShapeCollection shapeCollection,
163 out notnull array<ref SCR_GeneratorShapeImportData> shapeDataArray,
164 out GeoShapeType shapeType)
166 WorldEditor worldEditor = Workbench.GetModule(WorldEditor);
167 WBProgressDialog progress =
new WBProgressDialog(
"Importing geometries...", worldEditor);
168 array<ref SCR_GeneratorShapeImportData> shapeDataTempArray;
171 GeoMultiPoint geoMultiPoint;
172 GeoPolyline geoPolyline;
173 GeoPolygon geoPolygon;
175 float prevProgress, currProgress;
176 for (
int i, count = shapeCollection.Count(); i < count; i++)
178 shape = shapeCollection.Get(i);
185 shapeDataTempArray = null;
187 shapeType = shape.GetType();
190 case GeoShapeType.POINT:
191 geoPoint = GeoPoint.Cast(shape);
193 Load_Point(geoPoint);
197 case GeoShapeType.MULTI_POINT:
198 shapeType = GeoShapeType.MULTI_POINT;
199 geoMultiPoint = GeoMultiPoint.Cast(shape);
201 Load_Multipoint(geoMultiPoint);
205 case GeoShapeType.POLYLINE:
206 geoPolyline = GeoPolyline.Cast(shape);
208 shapeDataTempArray = Load_Polylines(GeoPolyline.Cast(shape));
212 case GeoShapeType.POLYGON:
213 geoPolygon = GeoPolygon.Cast(shape);
215 shapeDataTempArray = Load_Polygons(GeoPolygon.Cast(shape));
220 Print(
"Unsupported type " +
typename.EnumToString(GeoShapeType, shape.GetType()),
LogLevel.WARNING);
224 if (shapeDataTempArray)
226 foreach (SCR_GeneratorShapeImportData
data : shapeDataTempArray)
228 shapeDataArray.Insert(
data);
232 currProgress = i / count;
233 if (currProgress - prevProgress >= 0.01)
235 progress.SetProgress(currProgress);
236 prevProgress = currProgress;
243 protected void Load_Point(notnull GeoPoint point)
245 CreateFromPoints(point, { point.GetCoords() });
250 protected void Load_Multipoint(notnull GeoMultiPoint multipoint)
252 array<vector> points = {};
253 GeoVertexCollection vertices = multipoint.GetPoints();
254 for (
int i = 0, count = vertices.Count(); i < count; i++)
256 points.Insert(vertices[i]);
259 CreateFromPoints(multipoint, points);
264 protected array<ref SCR_GeneratorShapeImportData> Load_Polygons(notnull GeoPolygon polygon)
266 WorldEditorAPI worldEditorAPI = SCR_WorldEditorToolHelper.GetWorldEditorAPI();
268 bool hasIdAttribute = polygon.GetAttributes().HasAttrib(m_sIDColumnName);
270 array<ref SCR_GeneratorShapeImportData> result = {};
272 SCR_GeneratorShapeImportData
data;
274 int currentLayerId = worldEditorAPI.GetCurrentEntityLayerId();
276 GeoVertexCollection geoVertexCollectionJ;
278 for (
int j = 0, count = polygon.PartsCount(); j < count; j++)
280 geoVertexCollectionJ = polygon.GetPart(j);
281 int polygonPointsCount = geoVertexCollectionJ.Count();
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];
287 vector bb3Dmin = { traceX, pntY, traceZ };
288 vector bb3Dmax = { traceX, pntY, traceZ };
291 for (
int k = 0; k < polygonPointsCount; k++)
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];
297 vector pnt = { traceX, pntY, traceZ };
299 for (
int xyz = 0; xyz < 3; xyz++)
301 float val = pnt[xyz];
303 if (val < bb3Dmin[xyz])
306 if (val > bb3Dmax[xyz])
312 vector polygonOrigin = ((bb3Dmax - bb3Dmin) * 0.5) + bb3Dmin;
315 if (m_bCreateAsSpline)
316 entitySource = worldEditorAPI.CreateEntity(SPLINE_SHAPE_ENTITY_CLASS,
"", currentLayerId, null, polygonOrigin,
vector.Zero);
318 entitySource = worldEditorAPI.CreateEntity(POLYLINE_SHAPE_ENTITY_CLASS,
"", currentLayerId, null, polygonOrigin,
vector.Zero);
320 worldEditorAPI.SetVariableValue(entitySource, null,
"IsClosed",
"1");
322 data =
new SCR_GeneratorShapeImportData();
325 for (
int k = 0; k < polygonPointsCount - 1; k++)
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);
333 worldEditorAPI.CreateObjectArrayVariableMember(entitySource, null,
"Points",
"ShapePoint", k);
334 worldEditorAPI.SetVariableValue(entitySource, {
new ContainerIdPathEntry(
"Points", k) },
"Position", (worldCoords - polygonOrigin).ToString(
false));
338 float finalR = m_vShapeColor[0];
339 float finalG = m_vShapeColor[1];
340 float finalB = m_vShapeColor[2];
345 finalR =
Math.AbsFloat(finalR - 1);
346 finalG =
Math.AbsFloat(finalG - 1);
347 finalB =
Math.AbsFloat(finalB - 1);
350 worldEditorAPI.SetVariableValue(entitySource, null,
"LineColor",
string.Format(
"%1 %2 %3 1", finalR, finalG, finalB));
352 data.source = entitySource;
353 data.entity = worldEditorAPI.SourceToEntity(entitySource);
357 data.id = polygon.GetAttributes().GetIntByName(m_sIDColumnName);
359 data.id = RANDOM_PREFAB_ID;
369 protected array<ref SCR_GeneratorShapeImportData> Load_Polylines(notnull GeoPolyline polyline)
371 WorldEditorAPI worldEditorAPI = SCR_WorldEditorToolHelper.GetWorldEditorAPI();
373 GeoVertexCollection vertices = polyline.GetVertices();
374 int polylinePointsCount = vertices.Count();
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];
380 vector bb3Dmin = { traceX, pntY, traceZ };
381 vector bb3Dmax = { traceX, pntY, traceZ };
384 for (
int k = 0; k < polylinePointsCount; k++)
388 pntY = worldEditorAPI.GetTerrainSurfaceY(traceX, traceZ) +
m_vOffset[1];
390 vector pnt = { traceX, pntY, traceZ };
392 for (
int xyz = 0; xyz < 3; xyz++)
394 float val = pnt[xyz];
396 if (val < bb3Dmin[xyz])
399 if (val > bb3Dmax[xyz])
405 vector polylineOrigin = ((bb3Dmax - bb3Dmin) * 0.5) + bb3Dmin;
409 if (m_bCreateAsSpline)
410 entitySource = worldEditorAPI.CreateEntity(SPLINE_SHAPE_ENTITY_CLASS,
"", worldEditorAPI.GetCurrentEntityLayerId(), null, polylineOrigin,
vector.Zero);
412 entitySource = worldEditorAPI.CreateEntity(POLYLINE_SHAPE_ENTITY_CLASS,
"", worldEditorAPI.GetCurrentEntityLayerId(), null, polylineOrigin,
vector.Zero);
414 SCR_GeneratorShapeImportData
data =
new SCR_GeneratorShapeImportData();
417 for (
int k = 0; k < polylinePointsCount; k++)
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);
426 worldEditorAPI.CreateObjectArrayVariableMember(entitySource, null,
"Points",
"ShapePoint", k);
427 worldEditorAPI.SetVariableValue(entitySource, {
new ContainerIdPathEntry(
"Points", k) },
"Position", (worldCoords - polylineOrigin).ToString(
false));
430 worldEditorAPI.SetVariableValue(entitySource, null,
"LineColor", m_vShapeColor.ToString(
false) +
" 1");
432 data.source = entitySource;
433 data.entity = worldEditorAPI.SourceToEntity(entitySource);
436 if (polyline.GetAttributes().HasAttrib(m_sIDColumnName))
437 data.id = polyline.GetAttributes().GetIntByName(m_sIDColumnName);
439 data.id = RANDOM_PREFAB_ID;
446 protected void CreateFromPoints(notnull GeoShape shape, notnull array<vector> points)
449 if (!m_sCommentsColumnName.IsEmpty() && shape.GetAttributes().HasAttrib(m_sCommentsColumnName))
452 string comment = shape.GetAttributes().GetStringByName(m_sCommentsColumnName).Trim();
453 if (comment.IsEmpty())
459 foreach (
vector pos : points)
461 CreateComment(comment, pos +
m_vOffset, GetPrefab());
468 if (!HasPrefabListPrefabs())
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);
474 WorldEditorAPI worldEditorAPI = SCR_WorldEditorToolHelper.GetWorldEditorAPI();
478 for (
int i = 0, count = points.Count(); i < count; i++)
481 if (chosenPrefab.IsEmpty())
492 worldEditorAPI.CreateEntityExt(chosenPrefab,
"", worldEditorAPI.GetCurrentEntityLayerId(), null, pos,
angles,
TraceFlags.WORLD);
498 protected void CreateComment(
string comment,
vector pos,
ResourceName commentPrefab =
string.Empty)
500 WorldEditorAPI worldEditorAPI = SCR_WorldEditorToolHelper.GetWorldEditorAPI();
502 if (commentPrefab.IsEmpty())
504 IEntitySource entitySource = worldEditorAPI.CreateEntity(COMMENT_ENTITY_CLASS,
"", worldEditorAPI.GetCurrentEntityLayerId(), null, pos,
vector.Zero);
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());
515 IEntitySource entitySource = worldEditorAPI.CreateEntity(commentPrefab,
"", worldEditorAPI.GetCurrentEntityLayerId(), null, pos,
vector.Zero);
518 Print(
"Cannot create " + commentPrefab +
" comment entity",
LogLevel.ERROR);
524 array<ref ContainerIdPathEntry>
path = {};
526 worldEditorAPI.CreateObjectVariableMember(entitySource,
path,
"m_UIInfo", EDITABLE_COMMENT_UI_INFO_CLASS);
529 worldEditorAPI.SetVariableValue(entitySource,
path,
"Name", comment);
534 array<ref ContainerIdPathEntry>
path = {};
536 worldEditorAPI.SetVariableValue(entitySource,
path,
"DisplayName", comment);
542 protected bool HasPrefabListPrefabs()
544 if (!m_PrefabDataList || !m_PrefabDataList.m_aPrefabsData)
547 foreach (SCR_SHPPrefabData prefabData : m_PrefabDataList.m_aPrefabsData)
549 if (!prefabData.m_sPrefab.IsEmpty())
558 protected ResourceName GetPrefab(
int id = RANDOM_PREFAB_ID)
560 if (!m_PrefabDataList || !m_PrefabDataList.m_aPrefabsData || m_PrefabDataList.m_aPrefabsData.IsEmpty())
565 return m_PrefabDataList.m_aPrefabsData.GetRandomElement().m_sPrefab;
568 foreach (SCR_SHPPrefabData prefabData : m_PrefabDataList.m_aPrefabsData)
570 if (prefabData.m_iID ==
id)
571 return prefabData.m_sPrefab;
580 protected bool IsPointDuplicate(
vector toCheck, array<vector> points)
582 foreach (
vector point : points)
584 if (
vector.DistanceSq(toCheck, point) < DUPLICATE_RADIUS_SQ)
594 protected void GenerateForestGeneratorPointData(notnull array<ref SCR_GeneratorShapeImportData> forestShapeDataArray)
596 int count = forestShapeDataArray.Count();
600 WorldEditor worldEditor = Workbench.GetModule(WorldEditor);
602 WBProgressDialog progress =
new WBProgressDialog(
"Generating forest generator points data...", worldEditor);
605 array<SCR_GeneratorShapeImportData> collidedShapes;
606 SCR_GeneratorShapeImportData otherForestShapeData;
608 float prevProgress, currProgress;
609 foreach (
int i, SCR_GeneratorShapeImportData forestShapeData : forestShapeDataArray)
611 points = forestShapeData.source.GetObjectArray(
"Points");
614 for (
int y = 0; y < count; y++)
619 otherForestShapeData = forestShapeDataArray[y];
620 if (forestShapeData.bbox.DetectCollision2D(otherForestShapeData.bbox))
621 collidedShapes.Insert(otherForestShapeData);
624 bool wasPreviousDuplicate =
false;
626 forestShapeData.points.Insert(forestShapeData.points[0]);
628 for (
int p = 0, countPoints = forestShapeData.points.Count(); p < countPoints; p++)
630 bool isDuplicate =
false;
631 for (
int y = 0, otherCount = collidedShapes.Count(); y < otherCount; y++)
633 if (!IsPointDuplicate(forestShapeData.points[p], collidedShapes[y].points))
637 if (!wasPreviousDuplicate)
639 wasPreviousDuplicate =
true;
643 worldEditorAPI.CreateObjectArrayVariableMember(points[p - 1], null,
"Data",
"ForestGeneratorPointData", 0);
646 int dataCount = dataArr.Count();
647 for (
int j = 0; j < dataCount; ++j)
650 if (
data.GetClassName() !=
"ForestGeneratorPointData")
653 array<ref ContainerIdPathEntry> containerPath = {
658 worldEditorAPI.SetVariableValue(forestShapeData.source, containerPath,
"m_bSmallOutline",
"false");
659 worldEditorAPI.SetVariableValue(forestShapeData.source, containerPath,
"m_bMiddleOutline",
"false");
663 wasPreviousDuplicate =
true;
670 wasPreviousDuplicate =
false;
673 forestShapeData.points.Remove(forestShapeData.points.Count() - 1);
675 currProgress = i / count;
676 if (currProgress - prevProgress >= 0.01)
678 progress.SetProgress(currProgress);
679 prevProgress = currProgress;
685 protected void GeneratePowerLinePointData(notnull array<ref SCR_GeneratorShapeImportData> shapeDataArray)
687 WorldEditorAPI worldEditorAPI = SCR_WorldEditorToolHelper.GetWorldEditorAPI();
690 foreach (
int i, SCR_GeneratorShapeImportData forestShapeData : shapeDataArray)
692 if (!forestShapeData.source || !forestShapeData.points || forestShapeData.points.IsEmpty())
695 shapePoints = forestShapeData.source.GetObjectArray(
"Points");
699 if (!worldEditorAPI.CreateObjectArrayVariableMember(shapePoints[0], null,
"Data",
"SCR_PowerlineGeneratorPointData", 0))
702 worldEditorAPI.SetVariableValue(shapePoints[0], {
new ContainerIdPathEntry(
"Data", 0) },
"m_bGeneratePerPoint",
"1");
707 protected void AttachChildren(notnull array<ref SCR_GeneratorShapeImportData> shapeDataArray)
709 WorldEditor worldEditor = Workbench.GetModule(WorldEditor);
711 WBProgressDialog progress =
new WBProgressDialog(
"Attaching children...", worldEditor);
712 int count = shapeDataArray.Count();
714 float prevProgress, currProgress;
715 foreach (
int i, SCR_GeneratorShapeImportData shapeData : shapeDataArray)
718 if (dataPrefab.IsEmpty())
719 dataPrefab = GetPrefab();
721 if (!dataPrefab.IsEmpty())
722 worldEditorAPI.CreateEntity(dataPrefab,
"", 0, shapeData.source,
vector.Zero,
vector.Zero);
724 currProgress = i / count;
725 if (currProgress - prevProgress >= 0.01)
727 progress.SetProgress(currProgress);
728 prevProgress = currProgress;
735 protected int ButtonProcess()
752class SCR_SHPPrefabDataList
755 ref array<ref SCR_SHPPrefabData> m_aPrefabsData;
759class SCR_SHPPrefabData
768class SCR_GeneratorShapeImportData
770 IEntitySource source;
774 ref array<vector> points = {};
780 bbox =
new SCR_AABB(points);
void ContainerIdPathEntry(string propertyName, int index=-1)
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
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
enum EVehicleType IEntity
class WorkbenchDialog_AbortRetryIgnore ButtonAttribute("OK", true)
allow to define multiple fields (string or ResourceName) - up to 5 elements
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