12class ForestGeneratorEntity : SCR_AreaGeneratorBaseEntity
18 [
Attribute(defvalue:
"1",
category:
"Generation",
desc:
"Allow partial forest regeneration, regenerates the whole forest otherwise")]
19 protected bool m_bAllowPartialRegeneration;
31 [
Attribute(defvalue:
"0",
category:
"Debug",
desc:
"Print the count of entities spawned by this forest generator")]
49 [
Attribute(defvalue:
"1",
category:
"Debug",
desc:
"Make entities follow terrain level on shape move (keeping their relative Y)")]
56 [
Attribute(defvalue:
"",
category:
"Forest",
desc:
"Forest generator levels to spawn in this forest generator polygon")]
57 protected ref array<ref ForestGeneratorLevel>
m_aLevels;
59 [
Attribute(defvalue:
"",
category:
"Forest",
desc:
"Forest generator clusters to spawn in this forest generator polygon",
params:
"noDetails")]
62 [
Attribute(defvalue:
"",
category:
"Forest",
desc:
"Curve defining general outline scaling; from inside (left, forest core) to outside (right, forest outline)", uiwidget:
UIWidgets.CurveDialog,
params:
string.Format(
"%1 %2 0 %3", SCALE_CURVE_RANGE, SCALE_CURVE_MAX_VALUE - SCALE_CURVE_MIN_VALUE, SCALE_CURVE_MIN_VALUE))]
68 protected static const float SCALE_CURVE_RANGE = 100;
69 protected static const float SCALE_CURVE_MAX_VALUE = 1;
70 protected static const float SCALE_CURVE_MIN_VALUE = 0;
75 protected ref array<vector> m_aShapePoints;
77 protected ref array<ref SCR_ForestGeneratorLine>
m_aLines = {};
78 protected ref array<ref SCR_ForestGeneratorPoint> m_aMiddleOutlinePoints = {};
79 protected ref array<ref SCR_ForestGeneratorLine> m_aSmallOutlineLines = {};
80 protected ref array<ref SCR_ForestGeneratorPoint> m_aSmallOutlinePoints = {};
81 protected ref array<ref SCR_ForestGeneratorLine> m_aMiddleOutlineLines = {};
82 protected ref array<ref SCR_ForestGeneratorRectangle> m_aRectangles = {};
83 protected ref array<ref SCR_ForestGeneratorRectangle> m_aOutlineRectangles = {};
84 protected ref array<ref SCR_ForestGeneratorRectangle> m_aNonOutlineRectangles = {};
85 protected ref array<ref ForestGeneratorTreeBase> m_aGridEntries = {};
87 protected ref array<ref ForestGeneratorOutline> m_aOutlines = {};
88 protected float m_fMaxOutlinesWidth;
89 protected float m_fArea;
91 protected ref map<IEntitySource, float> m_mEntitySourceATLHeights;
93 protected static ref array<float> s_aPreviousPoints2D;
96 protected static const int MAX_CLUSTER_ATTEMPT = 10;
99 protected static ref SCR_DebugShapeManager s_DebugShapeManager;
101 protected static const int REGENERATION_DELETION_COLOUR = Color.RED;
102 protected static const int REGENERATION_CREATION_COLOUR = Color.GREEN;
103 protected static const vector DEBUG_VERTICAL_LINE =
"0 30 0";
105 protected static const float RECTANGULATION_SIZE = 50;
106 protected static const float HECTARE_CONVERSION_FACTOR = 0.0001;
107 protected static const float MIN_POSSIBLE_SCALE_VALUE = 0.001;
108 protected static const string POINTDATA_CLASSNAME = ((
typename)ForestGeneratorPointData).
ToString();
111 protected static const int TREE_GROUPS_OFFSET_HACK = 10;
113 protected static const int GRID_SIZE = 10;
119 protected bool OnLine(SCR_ForestGeneratorLine line, SCR_ForestGeneratorPoint point)
121 float a = Math.Max(line.p1.m_vPos[0], line.p2.m_vPos[0]);
122 float b = Math.Min(line.p1.m_vPos[0], line.p2.m_vPos[0]);
123 float c = Math.Max(line.p1.m_vPos[2], line.p2.m_vPos[2]);
124 float d = Math.Min(line.p1.m_vPos[2], line.p2.m_vPos[2]);
127 point.m_vPos[0] <= a &&
128 point.m_vPos[0] <= b &&
129 point.m_vPos[2] <= c &&
130 point.m_vPos[2] <= d;
138 protected int Direction(SCR_ForestGeneratorPoint a, SCR_ForestGeneratorPoint b, SCR_ForestGeneratorPoint c)
141 (b.m_vPos[2] - a.m_vPos[2]) * (c.m_vPos[0] - b.m_vPos[0]) -
142 (b.m_vPos[0] - a.m_vPos[0]) * (c.m_vPos[2] - b.m_vPos[2]);
157 protected bool IsIntersect(SCR_ForestGeneratorLine line1, SCR_ForestGeneratorLine line2)
160 int dir1 = Direction(line1.p1, line1.p2, line2.p1);
161 int dir2 = Direction(line1.p1, line1.p2, line2.p2);
162 int dir3 = Direction(line2.p1, line2.p2, line1.p1);
163 int dir4 = Direction(line2.p1, line2.p2, line1.p2);
166 (dir1 != dir2 && dir3 != dir4) ||
167 (dir1 == 0 && OnLine(line1, line2.p1)) ||
168 (dir2 == 0 && OnLine(line1, line2.p2)) ||
169 (dir3 == 0 && OnLine(line2, line1.p1)) ||
170 (dir4 == 0 && OnLine(line2, line1.p2));
177 protected bool IsIntersect(SCR_ForestGeneratorLine line, SCR_ForestGeneratorRectangle rectangle)
180 IsIntersect(line, rectangle.m_Line1) ||
181 IsIntersect(line, rectangle.m_Line2) ||
182 IsIntersect(line, rectangle.m_Line3) ||
183 IsIntersect(line, rectangle.m_Line4);
193 protected bool PreprocessTreeArray(notnull array<ref ForestGeneratorTree> trees,
int groupIdx,
SCR_ETreeType type,
int debugGroupIdx)
195 ForestGeneratorTree tree;
197 for (
int i = trees.Count() - 1; i >= 0; --i)
200 if (tree.m_fWeight <= 0 || tree.m_Prefab.IsEmpty())
202 trees.RemoveOrdered(i);
206 probaSum += tree.m_fWeight;
207 tree.m_iGroupIndex = groupIdx;
214 foreach (ForestGeneratorTree tree2 : trees)
216 tree2.m_fWeight = tree2.m_fWeight / probaSum;
220 return !trees.IsEmpty();
225 protected void PreprocessAllTrees()
228 foreach (ForestGeneratorLevel level :
m_aLevels)
230 level.m_aGroupProbas = {};
232 if (level.m_eType == SCR_EForestGeneratorLevelType.BOTTOM)
234 foreach (TreeGroupClass treeGroup : level.m_aTreeGroups)
236 PreprocessTreeArray(treeGroup.m_aTrees, 0,
SCR_ETreeType.BOTTOM, debugGroupIdx);
246 TreeGroupClass treeGroup;
248 for (
int i, groupCount = level.m_aTreeGroups.Count(); i < groupCount; i++)
250 treeGroup = level.m_aTreeGroups[i];
252 if (treeGroup.m_fWeight > 0 &&
253 treeGroup.m_aTrees &&
254 !treeGroup.m_aTrees.IsEmpty() &&
255 PreprocessTreeArray(treeGroup.m_aTrees, groupIdx,
SCR_ETreeType.TOP, debugGroupIdx))
257 groupProbaSum += treeGroup.m_fWeight;
263 level.m_aTreeGroups.RemoveOrdered(i);
269 foreach (TreeGroupClass treeGroup2 : level.m_aTreeGroups)
271 if (groupProbaSum > 0)
272 treeGroup2.m_fWeight = treeGroup2.m_fWeight / groupProbaSum;
274 level.m_aGroupProbas.Insert(treeGroup2.m_fWeight);
283 protected array<ref SCR_ForestGeneratorPoint> GetClockWisePoints(notnull IEntitySource shapeEntitySource)
285 BaseContainerList points = shapeEntitySource.GetObjectArray(
"Points");
289 WorldEditorAPI worldEditorAPI = _WB_GetEditorAPI();
291 array<ref SCR_ForestGeneratorPoint> result = {};
295 BaseContainerList dataArr;
297 SCR_ForestGeneratorPoint genPoint;
298 for (
int i, pointCount = points.Count(); i < pointCount; i++)
300 point = points.Get(i);
301 point.Get(
"Position", pos);
303 bool smallOutline =
true;
304 bool middleOutline =
true;
305 dataArr = point.GetObjectArray(
"Data");
307 bool hasPointData =
false;
308 for (
int j, dataCount = dataArr.Count(); j < dataCount; ++j)
310 data = dataArr.Get(j);
311 if (
data.GetClassName() == POINTDATA_CLASSNAME)
313 data.Get(
"m_bSmallOutline", smallOutline);
314 data.Get(
"m_bMiddleOutline", middleOutline);
320 if (!hasPointData && worldEditorAPI && !worldEditorAPI.UndoOrRedoIsRestoring())
321 worldEditorAPI.CreateObjectArrayVariableMember(point, null,
"Data", POINTDATA_CLASSNAME, dataArr.Count());
324 foreach (SCR_ForestGeneratorPoint curPoint : result)
326 if (curPoint.m_vPos == pos)
328 Print(
"Found two points on the same position: " + pos +
", Skipping",
LogLevel.WARNING);
337 genPoint =
new SCR_ForestGeneratorPoint();
339 genPoint.m_vPos = pos;
340 genPoint.m_bSmallOutline = smallOutline;
341 genPoint.m_bMiddleOutline = middleOutline;
343 result.Insert(genPoint);
347 int count = result.Count();
354 for (
int i; i < count; i++)
356 currentPoint = result[i].m_vPos;
358 nextPoint = result[0].m_vPos;
360 nextPoint = result[i + 1].m_vPos;
362 sum += (nextPoint[0] - currentPoint[0]) * (nextPoint[2] + currentPoint[2]);
367 for (
int i, iterNum = count * 0.5; i < iterNum; i++)
369 genPoint = result[i];
370 result[i] = result[count - 1 - i];
371 result[count - 1 - i] = genPoint;
381 protected void FillOutlineLinesAndPoints(notnull array<ref SCR_ForestGeneratorPoint> points)
383 SCR_ForestGeneratorLine line;
384 foreach (
int i, SCR_ForestGeneratorPoint point : points)
389 line.m_fLength = (line.p2.m_vPos - line.p1.m_vPos).Length();
390 point.m_Line1 = line;
393 if (point.m_bSmallOutline)
394 m_aSmallOutlinePoints.Insert(point);
396 if (point.m_bMiddleOutline)
397 m_aMiddleOutlinePoints.Insert(point);
399 if (line.p1.m_bSmallOutline)
400 m_aSmallOutlineLines.Insert(line);
402 if (line.p1.m_bMiddleOutline)
403 m_aMiddleOutlineLines.Insert(line);
406 line =
new SCR_ForestGeneratorLine();
408 point.m_Line2 = line;
411 SCR_ForestGeneratorPoint point = points[0];
413 point.m_Line1 = line;
414 line.m_fLength = (line.p2.m_vPos - line.p1.m_vPos).Length();
417 if (point.m_bSmallOutline)
418 m_aSmallOutlinePoints.Insert(point);
420 if (point.m_bMiddleOutline)
421 m_aMiddleOutlinePoints.Insert(point);
423 if (line.p1.m_bSmallOutline)
424 m_aSmallOutlineLines.Insert(line);
426 if (line.p1.m_bMiddleOutline)
427 m_aMiddleOutlineLines.Insert(line);
433 protected void CalculateOutlineAnglesForPoints(notnull array<ref SCR_ForestGeneratorPoint> points)
435 int count = points.Count();
439 SCR_ForestGeneratorPoint previousPoint = points[count - 1];
440 SCR_ForestGeneratorPoint currentPoint;
441 SCR_ForestGeneratorPoint nextPoint;
444 for (
int i; i < count; i++)
446 currentPoint = points[i];
449 nextPoint = points[i + 1];
451 nextPoint = points[0];
453 dir1 = previousPoint.m_vPos - currentPoint.m_vPos;
454 dir2 = nextPoint.m_vPos - currentPoint.m_vPos;
455 float yaw1 = dir1.ToYaw();
456 float yaw2 = dir2.ToYaw();
459 currentPoint.m_fMinAngle = yaw1 - 360;
460 currentPoint.m_fMaxAngle = yaw2;
464 currentPoint.m_fMinAngle = yaw1;
465 currentPoint.m_fMaxAngle = yaw2;
468 currentPoint.m_fAngle = Math.AbsFloat(currentPoint.m_fMaxAngle - currentPoint.m_fMinAngle);
470 previousPoint = currentPoint;
475 protected override bool _WB_OnKeyChanged(BaseContainer src,
string key, BaseContainerList ownerContainers,
IEntity parent)
477 if (!super._WB_OnKeyChanged(src, key, ownerContainers, parent))
480 WorldEditorAPI worldEditorAPI = _WB_GetEditorAPI();
481 if (!worldEditorAPI || worldEditorAPI.UndoOrRedoIsRestoring())
484 src = worldEditorAPI.EntityToSource(
this);
486 if (key ==
"m_bRegenerateEntireForest")
488 src.ClearVariable(
"m_bRegenerateEntireForest");
490 if (m_ParentShapeSource)
491 RegenerateForest(
true);
494 BaseContainerTools.WriteToInstance(
this, src);
496 if (m_ParentShapeSource && key ==
"m_iSeed")
497 RegenerateForest(
true);
504 protected override void OnShapeChangedInternal(IEntitySource shapeEntitySrc, ShapeEntity shapeEntity, array<vector> mins, array<vector> maxes)
506 super.OnShapeChangedInternal(shapeEntitySrc, shapeEntity, mins, maxes);
512 protected override void OnShapeInitInternal(IEntitySource shapeEntitySrc, ShapeEntity shapeEntity)
514 super.OnShapeInitInternal(shapeEntitySrc, shapeEntity);
515 RegenerateForest(
true);
519 protected override void BeforeShapeTransformInternal(IEntitySource shapeEntitySrc, ShapeEntity shapeEntity, inout vector oldTransform[4])
521 super.BeforeShapeTransformInternal(shapeEntitySrc, shapeEntity, oldTransform);
526 WorldEditorAPI worldEditorAPI = _WB_GetEditorAPI();
530 if (worldEditorAPI.UndoOrRedoIsRestoring())
533 vector localPos, worldPos;
535 shapeEntitySrc.Get(
"coords", parentPos);
537 m_mEntitySourceATLHeights =
new map<IEntitySource, float>();
538 IEntitySource childSource;
539 for (
int i = m_Source.GetNumChildren() - 1; i >= 0; i--)
541 childSource = m_Source.GetChild(i);
542 if (!childSource.Get(
"coords", localPos))
545 worldPos = localPos + parentPos;
548 if (!worldEditorAPI.TryGetTerrainSurfaceY(worldPos[0], worldPos[2], yTerrain))
551 m_mEntitySourceATLHeights.Insert(childSource, worldPos[1] - yTerrain);
556 protected override void OnShapeTransformInternal(IEntitySource shapeEntitySrc, ShapeEntity shapeEntity, array<vector> mins, array<vector> maxes)
558 super.OnShapeTransformInternal(shapeEntitySrc, shapeEntity, mins, maxes);
563 WorldEditorAPI worldEditorAPI = _WB_GetEditorAPI();
567 if (worldEditorAPI.UndoOrRedoIsRestoring())
570 if (m_mEntitySourceATLHeights.IsEmpty())
574 vector localPos, worldPos;
576 foreach (IEntitySource childSource,
float relativeY : m_mEntitySourceATLHeights)
578 if (!childSource.Get(
"coords", localPos))
581 worldPos = localPos + absPos;
584 if (!worldEditorAPI.TryGetTerrainSurfaceY(worldPos[0], worldPos[2], yTerrain))
587 float difference = relativeY - (worldPos[1] - yTerrain);
590 localPos[1] = localPos[1] + difference;
591 worldEditorAPI.SetVariableValue(childSource, null,
"coords", localPos.ToString(
false));
595 m_mEntitySourceATLHeights = null;
602 super.OnPointChangedInternal(shapeEntitySrc, shapeEntity, situation, pointIndex,
position);
603 if (s_aPreviousPoints2D)
606 array<vector> points3D = GetAnchorPoints(m_ParentShapeSource);
607 s_aPreviousPoints2D = {};
608 SCR_Math2D.Get2DPolygon(points3D, s_aPreviousPoints2D);
617 const int colCount = 11;
622 case 0: color = 0xFF56E3D7;
break;
623 case 1: color = 0xFF428AF5;
break;
624 case 2: color = 0xFFF57542;
break;
625 case 3: color = 0xFF8AE356;
break;
626 case 4: color = 0xFF2F636B;
break;
627 case 5: color = 0xFF818491;
break;
628 case 6: color = 0xFFED9DBB;
break;
629 case 7: color = 0xFF0009AB;
break;
630 case 8: color = 0xFFAB003C;
break;
631 case 9: color = 0xFFA8AB00;
break;
632 case 10: color = 0xFFFFFFFF;
break;
633 default: color = 0xFFFFFFFF;
break;
640 protected void MemoryCleanup()
644 m_aSmallOutlineLines.Clear();
645 m_aMiddleOutlineLines.Clear();
646 m_aRectangles.Clear();
647 m_aOutlineRectangles.Clear();
648 m_aSmallOutlinePoints.Clear();
649 m_aMiddleOutlinePoints.Clear();
650 m_aNonOutlineRectangles.Clear();
656 protected void RegenerateForest(
bool forceRegeneration =
false)
658 if (!m_bEnableGeneration)
660 Print(
"Forest generation is disabled for this shape - tick it back on before saving",
LogLevel.NORMAL);
664 float tick = System.GetTickCount();
665 WorldEditorAPI worldEditorAPI = _WB_GetEditorAPI();
672 if (worldEditorAPI.UndoOrRedoIsRestoring() || !worldEditorAPI.AreGeneratorEventsEnabled())
678 m_aSmallOutlinePoints.Clear();
679 m_aMiddleOutlinePoints.Clear();
680 m_aSmallOutlineLines.Clear();
681 m_aMiddleOutlineLines.Clear();
684 m_Grid =
new ForestGeneratorGrid(GRID_SIZE);
686 s_DebugShapeManager.Clear();
688 Debug.BeginTimeMeasure();
689 PreprocessAllTrees();
690 Debug.EndTimeMeasure(
"Provided data preprocess");
693 float outlineWidthToClear;
694 ForestGeneratorOutline outline;
695 foreach (ForestGeneratorLevel level :
m_aLevels)
697 if (outlineWidthToClear < level.m_fOutlineScaleCurveDistance)
698 outlineWidthToClear = level.m_fOutlineScaleCurveDistance;
700 outline = ForestGeneratorOutline.Cast(level);
704 if (m_fMaxOutlinesWidth < outline.m_fMaxDistance)
705 m_fMaxOutlinesWidth = outline.m_fMaxDistance;
707 if (m_fMaxOutlinesWidth < outline.m_fMinDistance)
708 m_fMaxOutlinesWidth = outline.m_fMinDistance;
710 m_aOutlines.Insert(outline);
713 if (outlineWidthToClear < m_fMaxOutlinesWidth)
714 outlineWidthToClear = m_fMaxOutlinesWidth;
716 array<ref SCR_ForestGeneratorPoint> generatorPoints = GetClockWisePoints(m_ParentShapeSource);
717 if (!generatorPoints || generatorPoints.IsEmpty())
720 Debug.BeginTimeMeasure();
721 FillOutlineLinesAndPoints(generatorPoints);
722 CalculateOutlineAnglesForPoints(generatorPoints);
723 Debug.EndTimeMeasure(
"Outline point calculations");
733 m_aShapePoints = GetAnchorPoints(m_ParentShapeSource);
734 m_aShapePoints.Insert(m_aShapePoints[0]);
737 array<vector> polygon3D = GetAnchorPoints(m_ParentShapeSource);
738 array<float> polygon2D = {};
739 SCR_Math2D.Get2DPolygon(polygon3D, polygon2D);
742 Debug.BeginTimeMeasure();
743 PopulateGrid(polygon2D, polygon3D);
744 Debug.EndTimeMeasure(
"ForestGenerator - Populating grid done");
753 SCR_ForestGeneratorOutlinePositionChecker outlinePositionChecker;
755 if (forceRegeneration)
757 s_aPreviousPoints2D = {};
761 if (!s_aPreviousPoints2D)
763 Print(
"No previous points! Fallback on current points (this edit does not do anything)",
LogLevel.WARNING);
764 s_aPreviousPoints2D = polygon2D;
767 outlinePositionChecker =
new SCR_ForestGeneratorOutlinePositionChecker(s_aPreviousPoints2D, polygon2D, outlineWidthToClear);
770 worldEditorAPI.BeginEditSequence(m_Source);
772 Print(
"ForestGenerator - Deleting previous entities",
LogLevel.DEBUG);
773 Debug.BeginTimeMeasure();
774 int entitiesCount = DeletePreviousEntities(polygon2D, outlinePositionChecker, forceRegeneration);
775 Debug.EndTimeMeasure(
"ForestGenerator - Deleting " + entitiesCount +
" previous entities done");
777 Print(
"ForestGenerator - Generating entities",
LogLevel.DEBUG);
778 Debug.BeginTimeMeasure();
779 entitiesCount = GenerateEntities(s_aPreviousPoints2D, outlinePositionChecker, forceRegeneration);
780 Debug.EndTimeMeasure(
"ForestGenerator - Generating " + entitiesCount +
" entities done");
782 worldEditorAPI.EndEditSequence(m_Source);
785 s_Benchmark.PrintAllMeasures();
787 s_aPreviousPoints2D = null;
790 int totalTime = System.GetTickCount(tick);
793 if (entitiesCount > 0)
794 msPerEntity = totalTime / entitiesCount;
796 msPerSqMetre = totalTime / m_fArea;
799 "Total time: %1 ms (~%2 ms/entity ratio / ~%3 ms/m²)",
801 msPerEntity.ToString(-1, 3),
802 msPerSqMetre.ToString(-1, 3),
812 protected int DeletePreviousEntities(notnull array<float> currentPoints2D, SCR_ForestGeneratorOutlinePositionChecker outlineChecker,
bool forceRegeneration =
false)
815 if (forceRegeneration || !m_bAllowPartialRegeneration)
817 result = m_Source.GetNumChildren();
824 WorldEditorAPI worldEditorAPI = _WB_GetEditorAPI();
825 IEntitySource entitySource;
828 for (
int i = m_Source.GetNumChildren() - 1; i >= 0; --i)
830 entitySource = m_Source.GetChild(i);
831 worldPos = worldEditorAPI.SourceToEntity(entitySource).
GetOrigin();
832 entityPos = CoordToLocal(worldPos);
835 if (!Math2D.IsPointInPolygon(currentPoints2D, entityPos[0], entityPos[2]))
837 worldEditorAPI.DeleteEntity(entitySource);
845 s_DebugShapeManager.AddLine(worldPos, worldPos + DEBUG_VERTICAL_LINE, REGENERATION_DELETION_COLOUR);
847 worldEditorAPI.DeleteEntity(entitySource);
861 protected int GenerateEntities(notnull array<float> previousPoints2D, SCR_ForestGeneratorOutlinePositionChecker outlineChecker,
bool forceRegeneration =
false)
863 WorldEditorAPI worldEditorAPI = _WB_GetEditorAPI();
865 BaseWorld world = worldEditorAPI.GetWorld();
871 Debug.BeginTimeMeasure();
873 Debug.EndTimeMeasure(
"Obstacles scan");
878 foreach (SCR_ObstacleDetectorSplineInfo info : s_ObstacleDetector.GetObstacles())
880 if (info.m_fClearance == 0)
882 s_DebugShapeManager.AddAABBRectangleXZ(info.m_vMinWithClearance, info.m_vMaxWithClearance);
886 vector maxWithClearance;
887 foreach (
int index, vector minWithClearance : info.m_aMinsWithClearance)
889 maxWithClearance = info.m_aMaxsWithClearance[
index];
890 s_DebugShapeManager.AddAABBRectangleXZ(minWithClearance, maxWithClearance);
896 bool partialGeneration = !forceRegeneration && m_bAllowPartialRegeneration && m_Source.GetNumChildren() > 0;
899 float scaleCurveDistanceDivisor;
900 array<float> curveKnots;
909 curveKnots.Insert(scalePoint[0]);
915 int topLevelEntitiesCount, bottomLevelEntitiesCount;
916 int smallOutlineEntitiesCount, middleOutlineEntitiesCount;
917 int clusterEntitiesCount, otherEntitiesCount;
918 int generatedEntitiesCount;
919 int setVariableValueCalls;
921 TraceParam traceParam =
new TraceParam();
924 map<ResourceName, ref SCR_RandomisationEditorData> randomValuesMap =
new map<ResourceName, ref SCR_RandomisationEditorData>();
925 SCR_RandomisationEditorData randomisationData;
926 SCR_ForestGeneratorTreeBase baseEntry;
927 IEntitySource entitySource;
928 vector entityMatrix[4];
929 FallenTree fallenTree;
930 WideForestGeneratorClusterObject wideObject;
932 for (
int i, count = m_Grid.GetEntryCount(); i < count; ++i)
935 baseEntry = m_Grid.GetEntry(i, worldPos);
936 if (!baseEntry.m_Prefab)
939 worldPos[1] = world.GetSurfaceY(worldPos[0], worldPos[2]);
940 vector localPos = CoordToLocal(worldPos);
941 localPos[1] = localPos[1] + baseEntry.m_fVerticalOffset;
943 if (partialGeneration)
946 Math2D.IsPointInPolygon(previousPoints2D, localPos[0], localPos[2]) &&
952 float scale = baseEntry.m_fScale;
953 if (useScaleCurve &&
scale > 0)
955 float distanceFromShape = SCR_Math3D.GetDistanceFromSplineXZ(m_aShapePoints, localPos);
961 if (scaleFactor < SCALE_CURVE_MIN_VALUE)
962 scaleFactor = SCALE_CURVE_MIN_VALUE;
964 if (scaleFactor > SCALE_CURVE_MAX_VALUE)
965 scaleFactor = SCALE_CURVE_MAX_VALUE;
967 scale *= scaleFactor;
971 if (
scale < MIN_POSSIBLE_SCALE_VALUE)
973 PrintFormat(
"Avoiding near-zero scale tree (scale = %1 < %2)",
scale, MIN_POSSIBLE_SCALE_VALUE, level:
LogLevel.DEBUG);
978 s_Benchmark.BeginMeasure(
"obstacleDetection");
981 bool hasObstacle = s_ObstacleDetector.HasObstacle(worldPos);
984 s_Benchmark.EndMeasure(
"obstacleDetection");
989 if (partialGeneration)
992 s_DebugShapeManager.AddLine(worldPos, worldPos + DEBUG_VERTICAL_LINE, REGENERATION_CREATION_COLOUR);
996 s_Benchmark.BeginMeasure(
"createEntity");
998 entitySource = worldEditorAPI.CreateEntity(baseEntry.m_Prefab,
string.Empty, m_iSourceLayerID, m_Source, localPos, vector.Zero);
1001 s_Benchmark.EndMeasure(
"createEntity");
1003 generatedEntitiesCount++;
1004 switch (baseEntry.m_eType)
1007 case SCR_ETreeType.BOTTOM: bottomLevelEntitiesCount++;
break;
1008 case SCR_ETreeType.MIDDLE_OUTLINE: middleOutlineEntitiesCount++;
break;
1009 case SCR_ETreeType.SMALL_OUTLINE: smallOutlineEntitiesCount++;
break;
1011 default: otherEntitiesCount++;
break;
1015 s_DebugShapeManager.AddSphere(worldPos, 1 + 5 - (
int)baseEntry.m_eType, GetColorForTree(baseEntry.m_iGroupIndex, baseEntry.m_eType),
ShapeFlags.NOOUTLINE);
1018 worldEditorAPI.SourceToEntity(entitySource).
GetTransform(entityMatrix);
1021 s_Benchmark.BeginMeasure(
"editSequenceCalculation");
1023 float randomVerticalOffset;
1024 vector randomAngles;
1027 if (!randomValuesMap.Find(baseEntry.m_Prefab, randomisationData))
1029 randomisationData = SCR_RandomisationEditorData.CreateFromEntitySource(entitySource);
1030 randomValuesMap.Insert(baseEntry.m_Prefab, randomisationData);
1033 if (randomisationData)
1035 randomVerticalOffset = SafeRandomFloatInclusive(randomisationData.m_vRandomVertOffset[0], randomisationData.m_vRandomVertOffset[1]);
1037 randomAngles[0] = SafeRandomFloatInclusive(-randomisationData.m_fRandomPitchAngle, randomisationData.m_fRandomPitchAngle);
1038 randomAngles[1] = SafeRandomFloatInclusive(0, 360);
1039 randomAngles[2] = SafeRandomFloatInclusive(-randomisationData.m_fRandomRollAngle, randomisationData.m_fRandomRollAngle);
1041 alignToNormal = randomisationData.m_bAlignToNormal;
1045 if (baseEntry.m_fVerticalOffset > 0)
1046 randomVerticalOffset = SafeRandomFloatInclusive(-baseEntry.m_fVerticalOffset, baseEntry.m_fVerticalOffset);
1048 if (baseEntry.m_fRandomPitchAngle > 0)
1049 randomAngles[0] = SafeRandomFloatInclusive(-baseEntry.m_fRandomPitchAngle, baseEntry.m_fRandomPitchAngle);
1051 randomAngles[1] = SafeRandomFloatInclusive(0, 360);
1053 if (baseEntry.m_fRandomRollAngle > 0)
1054 randomAngles[2] = SafeRandomFloatInclusive(-baseEntry.m_fRandomRollAngle, baseEntry.m_fRandomRollAngle);
1059 fallenTree = FallenTree.Cast(baseEntry);
1060 wideObject = WideForestGeneratorClusterObject.Cast(baseEntry);
1063 alignToNormal = wideObject.m_bAlignToNormal;
1066 alignToNormal = fallenTree.m_bAlignToNormal;
1068 if (randomAngles[1] != 0)
1069 SCR_Math3D.RotateAround(entityMatrix, entityMatrix[3], entityMatrix[1], -Math.DEG2RAD * randomAngles[1], entityMatrix);
1071 if (randomAngles[0] != 0)
1072 SCR_Math3D.RotateAround(entityMatrix, entityMatrix[3], entityMatrix[0], -Math.DEG2RAD * randomAngles[0], entityMatrix);
1074 if (randomAngles[2] != 0)
1075 SCR_Math3D.RotateAround(entityMatrix, entityMatrix[3], entityMatrix[2], -Math.DEG2RAD * randomAngles[2], entityMatrix);
1079 traceParam.Start = worldPos + vector.Up;
1080 traceParam.End = worldPos - vector.Up;
1081 world.TraceMove(traceParam, null);
1083 entityMatrix[1] = traceParam.TraceNorm.Normalized();
1084 entityMatrix[0] = (entityMatrix[1] * entityMatrix[2]).Normalized();
1085 entityMatrix[2] = (entityMatrix[0] * entityMatrix[1]).Normalized();
1088 vector
angles = Math3D.MatrixToAngles(entityMatrix);
1091 s_Benchmark.EndMeasure(
"editSequenceCalculation");
1094 s_Benchmark.BeginMeasure(
"editSequence");
1098 worldEditorAPI.BeginEditSequence(entitySource);
1100 if (
angles != vector.Zero)
1101 worldEditorAPI.SetVariableValue(entitySource, null,
"angles",
string.Format(
"%1 %2 %3",
angles[1],
angles[0],
angles[2]));
1104 worldEditorAPI.SetVariableValue(entitySource, null,
"scale",
scale.ToString());
1106 worldEditorAPI.EndEditSequence(entitySource);
1110 s_Benchmark.EndMeasure(
"editSequence");
1116 Print(
"Area of the polygon is: " + m_fArea.ToString(lenDec: 2) +
" square meters",
LogLevel.NORMAL);
1120 Print(
"Forest generator generated: " + topLevelEntitiesCount +
" entities in top level",
LogLevel.NORMAL);
1121 Print(
"Forest generator generated: " + bottomLevelEntitiesCount +
" entities in bottom level",
LogLevel.NORMAL);
1122 Print(
"Forest generator generated: " + middleOutlineEntitiesCount +
" entities in middle outline",
LogLevel.NORMAL);
1123 Print(
"Forest generator generated: " + smallOutlineEntitiesCount +
" entities in small outline",
LogLevel.NORMAL);
1124 Print(
"Forest generator generated: " + clusterEntitiesCount +
" entities in clusters",
LogLevel.NORMAL);
1125 if (otherEntitiesCount > 0)
1126 Print(
"Forest generator generated: " + clusterEntitiesCount +
" uncategorised entities",
LogLevel.WARNING);
1128 Print(
"Forest generator generated: " + generatedEntitiesCount +
" entities in total (" + setVariableValueCalls +
" WE API calls)",
LogLevel.NORMAL);
1131 return generatedEntitiesCount;
1138 protected void PopulateGrid(array<float> polygon2D, array<vector> polygon3D)
1141 m_aGridEntries.Clear();
1143 m_fArea = SCR_Math2D.GetPolygonArea(polygon2D);
1145 SCR_AABB bbox =
new SCR_AABB(polygon3D);
1146 m_Grid.Resize(bbox.m_vDimensions[0], bbox.m_vDimensions[2]);
1148 Debug.BeginTimeMeasure();
1149 Rectangulate(bbox, polygon2D);
1150 Debug.EndTimeMeasure(
"ForestGenerator - Rectangulation done");
1153 GetWorldTransform(worldMat);
1154 vector bboxMin = bbox.m_vMin;
1155 vector bboxMinWorld = bboxMin.Multiply4(worldMat);
1156 m_Grid.SetPointOffset(bboxMinWorld[0], bboxMinWorld[2]);
1158 Debug.BeginTimeMeasure();
1159 GenerateForestGeneratorTrees(polygon2D, bbox);
1160 Debug.EndTimeMeasure(
"ForestGenerator - Grid tree generation done");
1167 protected void Rectangulate(notnull SCR_AABB bbox, notnull array<float> polygon2D)
1169 vector
direction = bbox.m_vMax - bbox.m_vMin;
1170 float targetRectangleWidth = RECTANGULATION_SIZE;
1171 float targetRectangleLength = RECTANGULATION_SIZE;
1172 int targetRectangleCountW = Math.Ceil(
direction[0] / targetRectangleWidth);
1173 int targetRectangleCountL = Math.Ceil(
direction[2] / targetRectangleLength);
1177 SCR_ForestGeneratorRectangle rectangle;
1180 for (
int x; x < targetRectangleCountW; x++)
1182 for (
int y; y < targetRectangleCountL; y++)
1184 bool isInPolygon =
false;
1186 rectangle =
new SCR_ForestGeneratorRectangle();
1189 rectangle.m_fWidth = targetRectangleWidth;
1190 rectangle.m_fLength = targetRectangleLength;
1191 rectangle.m_fArea = rectangle.m_fLength * rectangle.m_fWidth;
1193 p1[0] = p1[0] + (x * targetRectangleWidth);
1194 p1[2] = p1[2] + (y * targetRectangleLength);
1195 rectangle.m_Line4.p2.m_vPos = p1;
1196 rectangle.m_Line1.p1.m_vPos = p1;
1198 isInPolygon = Math2D.IsPointInPolygon(polygon2D, p1[0], p1[2]);
1200 rectangle.m_aPoints.Insert(p1);
1202 p1[0] = p1[0] + targetRectangleWidth;
1203 rectangle.m_Line1.p2.m_vPos = p1;
1204 rectangle.m_Line2.p1.m_vPos = p1;
1206 isInPolygon = Math2D.IsPointInPolygon(polygon2D, p1[0], p1[2]);
1208 rectangle.m_aPoints.Insert(p1);
1210 p1[2] = p1[2] + targetRectangleLength;
1211 rectangle.m_Line2.p2.m_vPos = p1;
1212 rectangle.m_Line3.p1.m_vPos = p1;
1214 isInPolygon = Math2D.IsPointInPolygon(polygon2D, p1[0], p1[2]);
1216 rectangle.m_aPoints.Insert(p1);
1218 p1[0] = p1[0] - targetRectangleWidth;
1219 rectangle.m_Line3.p2.m_vPos = p1;
1220 rectangle.m_Line4.p1.m_vPos = p1;
1222 isInPolygon = Math2D.IsPointInPolygon(polygon2D, p1[0], p1[2]);
1224 rectangle.m_aPoints.Insert(p1);
1226 foreach (SCR_ForestGeneratorLine line :
m_aLines)
1228 if (!NeedsCheck(line, rectangle) || !IsIntersect(line, rectangle))
1232 foreach (SCR_ForestGeneratorLine rectLine : rectangle.m_aLines)
1234 if (rectLine == line)
1242 rectangle.m_aLines.Insert(line);
1245 bool areLinesEmpty = rectangle.m_aLines.IsEmpty();
1246 if (areLinesEmpty && !isInPolygon)
1249 m_aRectangles.Insert(rectangle);
1252 m_aNonOutlineRectangles.Insert(rectangle);
1254 m_aOutlineRectangles.Insert(rectangle);
1271 s_DebugShapeManager.AddBBox(ownerOrigin + rectangle.m_Line1.p1.m_vPos, ownerOrigin + rectangle.m_Line3.p1.m_vPos,
ARGB(63, red, green, blue));
1282 protected bool NeedsCheck(SCR_ForestGeneratorLine line, SCR_ForestGeneratorRectangle rectangle)
1284 vector linePoint1 = line.p1.m_vPos;
1285 vector linePoint2 = line.p2.m_vPos;
1288 rectangle.GetBounds(mins, maxs);
1290 float linePoint1x = linePoint1[0];
1291 float linePoint1z = linePoint1[2];
1292 float linePoint2x = linePoint2[0];
1293 float linePoint2z = linePoint2[2];
1294 float minsx = mins[0];
1295 float minsz = mins[2];
1296 float maxsx = maxs[0];
1297 float maxsz = maxs[2];
1300 (linePoint1x < minsx && linePoint2x < minsx) ||
1301 (linePoint1x > maxsx && linePoint2x > maxsx) ||
1302 (linePoint1z < minsz && linePoint2z < minsz) ||
1303 (linePoint1z > maxsz && linePoint2z > maxsz))
1313 protected void GenerateForestGeneratorTrees(array<float> polygon2D, SCR_AABB bbox)
1315 if (bbox.m_vDimensions[0] * bbox.m_vDimensions[2] <= 0.01)
1319 foreach (ForestGeneratorCluster cluster :
m_aClusters)
1321 if (!cluster.m_bGenerate || cluster.m_fRadius <= 0)
1324 if (cluster.m_Type == SCR_EForestGeneratorClusterType.CIRCLE)
1325 GenerateCircleCluster(ForestGeneratorCircleCluster.Cast(cluster), polygon2D, bbox);
1327 if (cluster.m_Type == SCR_EForestGeneratorClusterType.STRIP)
1328 GenerateStripCluster(ForestGeneratorStripCluster.Cast(cluster), polygon2D, bbox);
1332 foreach (ForestGeneratorLevel level :
m_aLevels)
1334 if (!level.m_bGenerate)
1337 switch (level.m_eType)
1339 case SCR_EForestGeneratorLevelType.TOP:
1340 GenerateTopTrees(polygon2D, bbox, ForestGeneratorTopLevel.Cast(level));
1343 case SCR_EForestGeneratorLevelType.OUTLINE:
1344 GenerateOutlineTrees(polygon2D, bbox, ForestGeneratorOutline.Cast(level));
1347 case SCR_EForestGeneratorLevelType.BOTTOM:
1348 GenerateBottomTrees(polygon2D, bbox, ForestGeneratorBottomLevel.Cast(level));
1360 protected int FindRectanglesInCircle(vector center,
float radius, out array<SCR_ForestGeneratorRectangle> rectangles)
1363 float deltaX, deltaY;
1364 float radiusSq = radius * radius;
1366 foreach (SCR_ForestGeneratorRectangle rectangle : m_aRectangles)
1368 foreach (vector point : rectangle.m_aPoints)
1370 deltaX = center[0] - Math.Max(rectangle.m_Line1.p1.m_vPos[0], Math.Min(center[0], rectangle.m_Line1.p1.m_vPos[0] + rectangle.m_fWidth));
1371 deltaY = center[2] - Math.Max(rectangle.m_Line1.p1.m_vPos[2], Math.Min(center[2], rectangle.m_Line1.p1.m_vPos[2] + rectangle.m_fLength));
1373 if (deltaX * deltaX + deltaY * deltaY < radiusSq)
1375 rectangles.Insert(rectangle);
1392 protected bool GetClusterPoint(notnull array<float> polygon2D, notnull SCR_AABB bbox, out vector clusterCenter,
float additionalDistance,
bool allowInForest,
bool allowInOutline)
1394 if (!allowInForest && !allowInOutline)
1397 if (allowInForest && allowInOutline)
1399 clusterCenter =
m_RandomGenerator.GenerateRandomPoint(polygon2D, bbox.m_vMin, bbox.m_vMax);
1403 array<bool> inOutlineList;
1407 array<SCR_ForestGeneratorRectangle> rectangles = {};
1408 for (
int i; i < MAX_CLUSTER_ATTEMPT; ++i)
1410 bool stopThisAttempt;
1412 vector possibleLocation =
m_RandomGenerator.GenerateRandomPoint(polygon2D, bbox.m_vMin, bbox.m_vMax);
1415 int rectanglesCount = FindRectanglesInCircle(possibleLocation, additionalDistance + m_fMaxOutlinesWidth, rectangles);
1418 inOutlineList.Clear();
1419 inOutlineList.Reserve(rectanglesCount);
1422 foreach (SCR_ForestGeneratorRectangle rectangle : rectangles)
1424 bool isInOutline = IsInOutline(rectangle, possibleLocation, additionalDistance);
1425 if (isInOutline && allowInForest)
1427 stopThisAttempt =
true;
1432 inOutlineList.Insert(isInOutline);
1435 if (stopThisAttempt)
1439 || (allowInOutline && inOutlineList.Contains(
true)))
1441 clusterCenter = possibleLocation;
1454 protected void GenerateCircleCluster(notnull ForestGeneratorCircleCluster cluster, notnull array<float> polygon2D, notnull SCR_AABB bbox)
1457 GetWorldTransform(worldMat);
1459 float CDENSHA = SafeRandomFloatInclusive(cluster.m_fMinCDENSHA, cluster.m_fMaxCDENSHA);
1461 vector clusterCenter;
1463 SmallForestGeneratorClusterObject newClusterObject;
1465 WideForestGeneratorClusterObject wideObject;
1466 for (
int c, clusterCount = Math.Ceil(m_fArea * HECTARE_CONVERSION_FACTOR * CDENSHA); c < clusterCount; c++)
1468 bool allowInForest = cluster.m_iPlacementArea != 2;
1469 bool allowInOutline = cluster.m_iPlacementArea != 1;
1470 if (!GetClusterPoint(polygon2D, bbox, clusterCenter, cluster.m_fRadius, allowInForest, allowInOutline))
1473 bool isPolygonCheckUseless = !SCR_Math3D.IsPointWithinSplineDistanceXZ(m_aShapePoints, clusterCenter, cluster.m_fRadius);
1475 foreach (SmallForestGeneratorClusterObject clusterObject : cluster.m_aObjects)
1477 for (
int o, objectCount = GetClusterObjectCount(clusterObject); o < objectCount; o++)
1479 pointLocal = GeneratePointInCircle(clusterObject.m_fMinRadius, clusterObject.m_fMaxRadius, clusterCenter);
1480 if (isPolygonCheckUseless || Math2D.IsPointInPolygon(polygon2D, pointLocal[0], pointLocal[2]))
1483 s_Benchmark.BeginMeasure(
"clone");
1485 newClusterObject = SmallForestGeneratorClusterObject.Cast(clusterObject.Clone());
1488 s_Benchmark.EndMeasure(
"clone");
1490 if (!newClusterObject)
1493 m_aGridEntries.Insert(newClusterObject);
1494 point = pointLocal.Multiply4(worldMat);
1496 SetObjectScale(newClusterObject);
1498 wideObject = WideForestGeneratorClusterObject.Cast(newClusterObject);
1502 wideObject.Rotate();
1505 if (m_Grid.IsColliding(point, newClusterObject))
1509 m_Grid.AddEntry(newClusterObject, point);
1521 protected void GenerateStripCluster(notnull ForestGeneratorStripCluster cluster, notnull array<float> polygon2D, notnull SCR_AABB bbox)
1524 GetWorldTransform(worldMat);
1527 vector perpendicular;
1531 float CDENSHA = SafeRandomFloatInclusive(cluster.m_fMinCDENSHA, cluster.m_fMaxCDENSHA);
1533 vector clusterCenter;
1537 SmallForestGeneratorClusterObject newClusterObject;
1538 WideForestGeneratorClusterObject wideObject;
1539 for (
int c, clusterCount = Math.Ceil(m_fArea * HECTARE_CONVERSION_FACTOR * CDENSHA); c < clusterCount; c++)
1541 bool allowInForest = cluster.m_iPlacementArea != 2;
1542 bool allowInOutline = cluster.m_iPlacementArea != 1;
1543 if (!GetClusterPoint(polygon2D, bbox, clusterCenter, cluster.m_fRadius, allowInForest, allowInOutline))
1546 bool isPolygonCheckUseless = !SCR_Math3D.IsPointWithinSplineDistanceXZ(m_aShapePoints, clusterCenter, cluster.m_fRadius);
1548 foreach (SmallForestGeneratorClusterObject clusterObject : cluster.m_aObjects)
1550 for (
int o, objectCount = GetClusterObjectCount(clusterObject); o < objectCount; o++)
1552 float distance = SafeRandomFloatInclusive(clusterObject.m_fMinRadius, clusterObject.m_fMaxRadius);
1558 float y01 =
distance / cluster.m_fRadius * cluster.m_fFrequency;
1559 float ySin = Math.Sin(y01 * 360 * Math.DEG2RAD);
1560 float y = ySin * cluster.m_fAmplitude;
1562 offset = vector.Zero;
1563 offset[0] = SafeRandomFloatInclusive(0, cluster.m_fMaxXOffset);
1564 offset[2] = SafeRandomFloatInclusive(0, cluster.m_fMaxYOffset);
1565 pointLocal = (
direction *
distance) + (y * perpendicular) + clusterCenter + offset;
1567 if (isPolygonCheckUseless || Math2D.IsPointInPolygon(polygon2D, pointLocal[0], pointLocal[2]))
1570 s_Benchmark.BeginMeasure(
"clone");
1572 newClusterObject = SmallForestGeneratorClusterObject.Cast(clusterObject.Clone());
1575 s_Benchmark.EndMeasure(
"clone");
1577 if (!newClusterObject)
1580 m_aGridEntries.Insert(newClusterObject);
1581 point = pointLocal.Multiply4(worldMat);
1583 SetObjectScale(newClusterObject);
1585 wideObject = WideForestGeneratorClusterObject.Cast(newClusterObject);
1589 wideObject.Rotate();
1592 if (m_Grid.IsColliding(point, newClusterObject))
1596 m_Grid.AddEntry(newClusterObject, point);
1606 protected int GetClusterObjectCount(notnull SmallForestGeneratorClusterObject clusterObject)
1608 int min = clusterObject.m_iMinCount;
1609 int max = clusterObject.m_iMaxCount;
1621 if (clusterObject.m_iRandomMidPercent < 0)
1626 float mid = min + (max - min) * clusterObject.m_iRandomMidPercent * 0.01;
1627 return GetGaussianDistributionRandomIntInclusive(min, mid, max);
1636 protected int GetGaussianDistributionRandomIntInclusive(
int min,
float mid,
int max)
1646 return Math.Round(result);
1654 protected float SafeRandomFloatInclusive(
float min,
float max)
1672 protected vector GeneratePointInCircle(
float innerRadius,
float outerRadius, vector circleCenter)
1675 float rand = SafeRandomFloatInclusive(innerRadius, outerRadius);
1685 protected vector GeneratePointInCircle(
float innerRadius,
float outerRadius, SCR_ForestGeneratorPoint point)
1688 float rand = SafeRandomFloatInclusive(innerRadius, outerRadius);
1696 protected vector GenerateRandomPointInRectangle(notnull SCR_ForestGeneratorRectangle rectangle)
1699 m_RandomGenerator.RandFloat01() * rectangle.m_fWidth + rectangle.m_Line1.p1.m_vPos[0],
1701 m_RandomGenerator.RandFloat01() * rectangle.m_fLength + rectangle.m_Line1.p1.m_vPos[2]
1708 protected bool GetIsAnyTreeValid(notnull array<ref TreeGroupClass> treeGroups)
1710 foreach (TreeGroupClass treeGroup : treeGroups)
1712 if (treeGroup.m_fWeight <= 0)
1715 foreach (ForestGeneratorTree tree : treeGroup.m_aTrees)
1717 if (tree.m_fWeight > 0 && !tree.m_Prefab.IsEmpty())
1730 protected void GenerateOutlineTrees(array<float> polygon, SCR_AABB bbox, ForestGeneratorOutline outline)
1732 if (!outline || !outline.m_aTreeGroups || outline.m_aTreeGroups.IsEmpty())
1735 if (!GetIsAnyTreeValid(outline.m_aTreeGroups))
1739 array<ref SCR_ForestGeneratorPoint> currentOutlinePoints;
1740 array<ref SCR_ForestGeneratorLine> currentOutlineLines;
1742 switch (outline.m_eOutlineType)
1744 case SCR_EForestGeneratorOutlineType.SMALL:
1747 currentOutlinePoints = m_aSmallOutlinePoints;
1748 currentOutlineLines = m_aSmallOutlineLines;
1752 case SCR_EForestGeneratorOutlineType.MIDDLE:
1755 currentOutlinePoints = m_aMiddleOutlinePoints;
1756 currentOutlineLines = m_aMiddleOutlineLines;
1761 if (!currentOutlinePoints || !currentOutlineLines)
1764 array<float> groupProbas = {};
1765 array<float> groupCounts = {};
1766 groupCounts.Resize(outline.m_aTreeGroups.Count());
1769 GetWorldTransform(worldMat);
1771 bool useScaleCurve = outline.m_fOutlineScaleCurveDistance > 0 && !outline.m_aOutlineScaleCurve.IsEmpty();
1772 float scaleCurveDistanceDivisor;
1773 array<float> curveKnots;
1777 scaleCurveDistanceDivisor = SCALE_CURVE_RANGE / outline.m_fOutlineScaleCurveDistance;
1779 foreach (vector scalePoint : outline.m_aOutlineScaleCurve)
1781 curveKnots.Insert(scalePoint[0]);
1787 vector
direction, perpendicular, pointLocal, point;
1788 float probaSumToNormalize, groupProba, probaSum;
1790 ForestGeneratorTree tree;
1791 foreach (SCR_ForestGeneratorLine line : currentOutlineLines)
1793 direction = line.p2.m_vPos - line.p1.m_vPos;
1795 perpendicular.Normalize();
1796 iterCount = outline.m_fDensity * (CalculateAreaForOutline(line, outline) * HECTARE_CONVERSION_FACTOR);
1797 for (
int treeIdx; treeIdx < iterCount; ++treeIdx)
1800 pointLocal = line.p1.m_vPos + (
direction *
m_RandomGenerator.RandFloat01()) + (perpendicular * SafeRandomFloatInclusive(outline.m_fMinDistance, outline.m_fMaxDistance));
1801 if (pointLocal == vector.Zero || !Math2D.IsPointInPolygon(polygon, pointLocal[0], pointLocal[2]))
1804 point = pointLocal.Multiply4(worldMat);
1807 int groupProbaCount = groupProbas.Copy(outline.m_aGroupProbas);
1808 for (
int i, count = groupCounts.Count(); i < count; i++)
1814 s_Benchmark.BeginMeasure(
"gridCountEntriesAround");
1816 m_Grid.CountEntriesAround(point, outline.m_fClusterRadius, groupCounts);
1819 s_Benchmark.EndMeasure(
"gridCountEntriesAround");
1822 probaSumToNormalize = 0;
1823 for (
int i; i < groupProbaCount; i++)
1825 groupProbas[i] = groupProbas[i] * Math.Pow(groupCounts[i], outline.m_fClusterStrength);
1826 probaSumToNormalize += groupProbas[i];
1829 if (probaSumToNormalize > 0)
1831 for (
int i; i < groupProbaCount; i++)
1833 groupProbas[i] = groupProbas[i] / probaSumToNormalize;
1838 groupIdx = groupProbas.Count() - 1;
1840 for (
int i, count = groupProbas.Count(); i < count; ++i)
1842 probaSum += groupProbas[i];
1843 if (groupProba < probaSum)
1850 tree = SelectTreeToSpawn(point, outline.m_aTreeGroups[groupIdx].m_aTrees);
1852 if (!IsEntryValid(tree, pointLocal))
1855 tree.m_eType = treeType;
1858 float distanceFromShape = SCR_Math3D.GetDistanceFromSplineXZ(m_aShapePoints, pointLocal);
1859 if (distanceFromShape <= outline.m_fOutlineScaleCurveDistance)
1862 float scaleFactor = LegacyCurve.Curve(
ECurveType.CatmullRom, (outline.m_fOutlineScaleCurveDistance - distanceFromShape) * scaleCurveDistanceDivisor, outline.m_aOutlineScaleCurve, curveKnots)[1];
1863 if (scaleFactor < ForestGeneratorLevel.SCALE_CURVE_MIN_VALUE)
1864 scaleFactor = ForestGeneratorLevel.SCALE_CURVE_MIN_VALUE;
1866 if (scaleFactor > ForestGeneratorLevel.SCALE_CURVE_MAX_VALUE)
1867 scaleFactor = ForestGeneratorLevel.SCALE_CURVE_MAX_VALUE;
1869 tree.m_fScale *= scaleFactor;
1873 m_Grid.AddEntry(tree, point);
1877 foreach (SCR_ForestGeneratorPoint currentPoint : currentOutlinePoints)
1879 iterCount = outline.m_fDensity * CalculateAreaForOutline(currentPoint, outline) * HECTARE_CONVERSION_FACTOR;
1880 for (
int treeIdx; treeIdx < iterCount; treeIdx++)
1882 pointLocal = GeneratePointInCircle(outline.m_fMinDistance, outline.m_fMaxDistance, currentPoint);
1884 bool lineDistance1 = IsPointInProperDistanceFromLine(pointLocal, currentPoint.m_Line1, outline.m_fMinDistance, outline.m_fMaxDistance);
1888 bool lineDistance2 = IsPointInProperDistanceFromLine(pointLocal, currentPoint.m_Line2, outline.m_fMinDistance, outline.m_fMaxDistance);
1892 if (!Math2D.IsPointInPolygon(polygon, pointLocal[0], pointLocal[2]))
1895 point = pointLocal.Multiply4(worldMat);
1898 groupProbas.Copy(outline.m_aGroupProbas);
1899 for (
int i, count = groupCounts.Count(); i < count; i++)
1905 s_Benchmark.BeginMeasure(
"gridCountEntriesAround");
1907 m_Grid.CountEntriesAround(point, outline.m_fClusterRadius, groupCounts);
1910 s_Benchmark.EndMeasure(
"gridCountEntriesAround");
1913 probaSumToNormalize = 0;
1914 for (
int i, count = groupProbas.Count(); i < count; i++)
1916 groupProbas[i] = groupProbas[i] * Math.Pow(groupCounts[i], outline.m_fClusterStrength);
1917 probaSumToNormalize += groupProbas[i];
1920 if (probaSumToNormalize != 0)
1922 for (
int i, count = groupProbas.Count(); i < count; i++)
1924 groupProbas[i] = groupProbas[i] / probaSumToNormalize;
1929 groupIdx = groupProbas.Count() - 1;
1931 for (
int i, count = groupProbas.Count(); i < count; i++)
1933 probaSum += groupProbas[i];
1934 if (groupProba < probaSum)
1941 tree = SelectTreeToSpawn(point, outline.m_aTreeGroups[groupIdx].m_aTrees);
1943 if (!IsEntryValid(tree, pointLocal))
1946 tree.m_eType = treeType;
1947 m_Grid.AddEntry(tree, point);
1958 protected bool IsPointInProperDistanceFromLine(vector point, SCR_ForestGeneratorLine line,
float minDistance,
float maxDistance)
1960 float distance = Math3D.PointLineSegmentDistance({ point[0], 0, point[2] }, { line.p1.m_vPos[0], 0, line.p1.m_vPos[2] }, { line.p2.m_vPos[0], 0, line.p2.m_vPos[2] });
1968 protected bool IsEntryValid(ForestGeneratorTree tree, vector pointLocal)
1973 FallenTree fallenTree = FallenTree.Cast(tree);
1978 foreach (SCR_ForestGeneratorLine line :
m_aLines)
1980 distance = Math3D.PointLineSegmentDistance(pointLocal, line.p1.m_vPos, line.p2.m_vPos);
1981 minDistance = fallenTree.GetMinDistanceFromLine();
1995 protected void GenerateBottomTrees(array<float> polygon, SCR_AABB bbox, ForestGeneratorBottomLevel bottomLevel)
1997 if (!bottomLevel || bottomLevel.m_aTreeGroups.IsEmpty())
2000 if (!GetIsAnyTreeValid(bottomLevel.m_aTreeGroups))
2003 array<float> groupProbas = {};
2004 float totalWeight = 0;
2005 int groupCount = bottomLevel.m_aTreeGroups.Count();
2006 groupProbas.Resize(groupCount);
2007 foreach (TreeGroupClass treeGroup : bottomLevel.m_aTreeGroups)
2009 totalWeight += treeGroup.m_fWeight;
2012 if (totalWeight != 0)
2014 for (
int i; i < groupCount; i++)
2016 groupProbas[i] = (bottomLevel.m_aTreeGroups[i].m_fWeight / totalWeight);
2021 GetWorldTransform(worldMat);
2023 bool useScaleCurve = bottomLevel.m_fOutlineScaleCurveDistance > 0 && !bottomLevel.m_aOutlineScaleCurve.IsEmpty();
2024 float scaleCurveDistanceDivisor;
2025 array<float> curveKnots;
2029 scaleCurveDistanceDivisor = SCALE_CURVE_RANGE / bottomLevel.m_fOutlineScaleCurveDistance;
2031 foreach (vector scalePoint : bottomLevel.m_aOutlineScaleCurve)
2033 curveKnots.Insert(scalePoint[0]);
2037 int expectedIterCount = m_fArea * HECTARE_CONVERSION_FACTOR * bottomLevel.m_fDensity;
2040 ForestGeneratorTree tree;
2041 foreach (SCR_ForestGeneratorRectangle rectangle : m_aRectangles)
2043 int iterCount = bottomLevel.m_fDensity * rectangle.m_fArea * HECTARE_CONVERSION_FACTOR;
2044 for (
int treeIdx; treeIdx < iterCount; ++treeIdx)
2046 expectedIterCount--;
2048 pointLocal = GenerateRandomPointInRectangle(rectangle);
2050 if (!rectangle.m_aLines.IsEmpty())
2052 if (!Math2D.IsPointInPolygon(polygon, pointLocal[0], pointLocal[2]))
2055 if (!IsBeforeOutlineBorder(rectangle, pointLocal, bottomLevel.m_fOutlineOverlap))
2059 float perlinValue = Math.PerlinNoise01(pointLocal[0], 0, pointLocal[2]);
2060 if (perlinValue > 1 || perlinValue < 0)
2062 Print(
"Perlin value is out of range <0,1>, something went wrong!",
LogLevel.ERROR);
2066 float rangeBeginning = 0;
2068 foreach (
int i,
float groupProba : groupProbas)
2070 if (perlinValue > rangeBeginning && perlinValue < (groupProba + rangeBeginning))
2075 rangeBeginning += groupProba;
2078 point = pointLocal.Multiply4(worldMat);
2079 tree = SelectTreeToSpawn(point, bottomLevel.m_aTreeGroups[groupIdx].m_aTrees);
2081 if (!IsEntryValid(tree, pointLocal))
2085 tree.m_iDebugGroupIndex = groupIdx;
2088 float distanceFromShape = SCR_Math3D.GetDistanceFromSplineXZ(m_aShapePoints, pointLocal);
2089 if (distanceFromShape <= bottomLevel.m_fOutlineScaleCurveDistance)
2092 float scaleFactor = LegacyCurve.Curve(
ECurveType.CatmullRom, (bottomLevel.m_fOutlineScaleCurveDistance - distanceFromShape) * scaleCurveDistanceDivisor, bottomLevel.m_aOutlineScaleCurve, curveKnots)[1];
2093 if (scaleFactor < ForestGeneratorLevel.SCALE_CURVE_MIN_VALUE)
2094 scaleFactor = ForestGeneratorLevel.SCALE_CURVE_MIN_VALUE;
2096 if (scaleFactor > ForestGeneratorLevel.SCALE_CURVE_MAX_VALUE)
2097 scaleFactor = ForestGeneratorLevel.SCALE_CURVE_MAX_VALUE;
2099 tree.m_fScale *= scaleFactor;
2103 m_Grid.AddEntry(tree, point);
2108 while (expectedIterCount > 0)
2111 GenerateTreeInsideRectangle(m_aRectangles[
index], bottomLevel, polygon, worldMat);
2112 expectedIterCount--;
2121 protected void GenerateTopTrees(array<float> polygon, SCR_AABB bbox, ForestGeneratorTopLevel topLevel)
2123 if (!topLevel || topLevel.m_aTreeGroups.IsEmpty())
2126 if (!GetIsAnyTreeValid(topLevel.m_aTreeGroups))
2129 array<float> groupProbas = {};
2130 array<float> groupCounts = {};
2131 groupCounts.Resize(topLevel.m_aTreeGroups.Count() + TREE_GROUPS_OFFSET_HACK);
2134 GetWorldTransform(worldMat);
2136 bool useScaleCurve = topLevel.m_fOutlineScaleCurveDistance > 0 && !topLevel.m_aOutlineScaleCurve.IsEmpty();
2137 float scaleCurveDistanceDivisor;
2138 array<float> curveKnots;
2142 scaleCurveDistanceDivisor = SCALE_CURVE_RANGE / topLevel.m_fOutlineScaleCurveDistance;
2144 foreach (vector scalePoint : topLevel.m_aOutlineScaleCurve)
2146 curveKnots.Insert(scalePoint[0]);
2151 int expectedIterCount = m_fArea * HECTARE_CONVERSION_FACTOR * topLevel.m_fDensity;
2153 ForestGeneratorTree tree;
2155 foreach (SCR_ForestGeneratorRectangle rectangle : m_aRectangles)
2157 float area = rectangle.m_fArea * HECTARE_CONVERSION_FACTOR;
2158 int iterCount = topLevel.m_fDensity * area;
2160 for (
int treeIdx; treeIdx < iterCount; ++treeIdx)
2163 pointLocal = GenerateRandomPointInRectangle(rectangle);
2164 expectedIterCount--;
2166 if (!rectangle.m_aLines.IsEmpty())
2168 if (!Math2D.IsPointInPolygon(polygon, pointLocal[0], pointLocal[2]))
2171 if (!IsBeforeOutlineBorder(rectangle, pointLocal, topLevel.m_fOutlineOverlap))
2175 point = pointLocal.Multiply4(worldMat);
2178 groupProbas.Copy(topLevel.m_aGroupProbas);
2179 for (
int i, count = groupCounts.Count(); i < count; i++)
2185 s_Benchmark.BeginMeasure(
"gridCountEntriesAround");
2188 m_Grid.CountEntriesAround(point, topLevel.m_fClusterRadius, groupCounts);
2191 s_Benchmark.EndMeasure(
"gridCountEntriesAround");
2194 float probaSumToNormalize = 0;
2195 for (
int i, count = groupProbas.Count(); i < count; i++)
2197 groupProbas[i] = groupProbas[i] * Math.Pow(groupCounts[i], topLevel.m_fClusterStrength);
2198 probaSumToNormalize += groupProbas[i];
2201 if (probaSumToNormalize != 0)
2203 for (
int i, count = groupProbas.Count(); i < count; i++)
2205 groupProbas[i] = groupProbas[i] / probaSumToNormalize;
2210 int groupIdx = groupProbas.Count() - 1;
2212 for (
int i, count = groupProbas.Count(); i < count; ++i)
2214 probaSum += groupProbas[i];
2215 if (groupProba < probaSum)
2222 tree = SelectTreeToSpawn(point, topLevel.m_aTreeGroups[groupIdx].m_aTrees);
2223 if (!IsEntryValid(tree, pointLocal))
2229 float distanceFromShape = SCR_Math3D.GetDistanceFromSplineXZ(m_aShapePoints, pointLocal);
2230 if (distanceFromShape <= topLevel.m_fOutlineScaleCurveDistance)
2233 float scaleFactor = LegacyCurve.Curve(
ECurveType.CatmullRom, (topLevel.m_fOutlineScaleCurveDistance - distanceFromShape) * scaleCurveDistanceDivisor, topLevel.m_aOutlineScaleCurve, curveKnots)[1];
2234 if (scaleFactor < ForestGeneratorLevel.SCALE_CURVE_MIN_VALUE)
2235 scaleFactor = ForestGeneratorLevel.SCALE_CURVE_MIN_VALUE;
2237 if (scaleFactor > ForestGeneratorLevel.SCALE_CURVE_MAX_VALUE)
2238 scaleFactor = ForestGeneratorLevel.SCALE_CURVE_MAX_VALUE;
2240 tree.m_fScale *= scaleFactor;
2244 m_Grid.AddEntry(tree, point);
2249 while (expectedIterCount > 0)
2252 GenerateTreeInsideRectangle(m_aRectangles[
index], topLevel, polygon, worldMat);
2253 expectedIterCount--;
2263 protected void GenerateTreeInsideRectangle(notnull SCR_ForestGeneratorRectangle rectangle, notnull SCR_ForestGeneratorTreeLevel level, notnull array<float> polygon, vector worldMat[4])
2265 array<float> groupProbas = {};
2266 array<float> groupCounts = {};
2267 groupCounts.Resize(level.m_aTreeGroups.Count());
2270 vector pointLocal = GenerateRandomPointInRectangle(rectangle);
2272 if (!rectangle.m_aLines.IsEmpty())
2274 if (!Math2D.IsPointInPolygon(polygon, pointLocal[0], pointLocal[2]))
2277 if (!IsBeforeOutlineBorder(rectangle, pointLocal, level.m_fOutlineOverlap))
2281 vector point = pointLocal.Multiply4(worldMat);
2284 groupProbas.Copy(level.m_aGroupProbas);
2285 for (
int i, count = groupCounts.Count(); i < count; i++)
2290 if (level.m_eType == SCR_EForestGeneratorLevelType.TOP)
2292 ForestGeneratorTopLevel topLevel = ForestGeneratorTopLevel.Cast(level);
2296 s_Benchmark.BeginMeasure(
"gridCountEntriesAround");
2298 m_Grid.CountEntriesAround(point, topLevel.m_fClusterRadius, groupCounts);
2301 s_Benchmark.EndMeasure(
"gridCountEntriesAround");
2304 float probaSumToNormalize = 0;
2305 for (
int i, count = groupProbas.Count(); i < count; i++)
2307 groupProbas[i] = groupProbas[i] * Math.Pow(groupCounts[i], topLevel.m_fClusterStrength);
2308 probaSumToNormalize += groupProbas[i];
2311 if (probaSumToNormalize != 0)
2313 for (
int i, count = groupProbas.Count(); i < count; i++)
2315 groupProbas[i] = groupProbas[i] / probaSumToNormalize;
2322 int groupIdx = groupProbas.Count() - 1;
2324 for (
int i, count = groupProbas.Count(); i < count; ++i)
2326 probaSum += groupProbas[i];
2327 if (groupProba < probaSum)
2334 if (!level.m_aTreeGroups.IsIndexValid(groupIdx))
2337 ForestGeneratorTree tree = SelectTreeToSpawn(point, level.m_aTreeGroups[groupIdx].m_aTrees);
2338 if (!IsEntryValid(tree, pointLocal))
2342 m_Grid.AddEntry(tree, point);
2350 protected ForestGeneratorTree SelectTreeToSpawn(vector point, array<ref ForestGeneratorTree> trees)
2353 int treeTypeIdx = trees.Count() - 1;
2355 foreach (
int i, ForestGeneratorTree tree : trees)
2357 probaSum += tree.m_fWeight;
2358 if (treeProba < probaSum)
2366 s_Benchmark.BeginMeasure(
"clone");
2368 ForestGeneratorTree tree = ForestGeneratorTree.Cast(trees[treeTypeIdx].
Clone());
2371 s_Benchmark.EndMeasure(
"clone");
2376 SetObjectScale(tree);
2377 FallenTree fallenTree = FallenTree.Cast(tree);
2381 fallenTree.Rotate();
2385 if (m_Grid.IsColliding(point, tree))
2402 m_aGridEntries.Insert(tree);
2409 protected void SetObjectScale(notnull SCR_ForestGeneratorTreeBase
object)
2411 object.m_fScale = SafeRandomFloatInclusive(
object.m_fMinScale,
object.
m_fMaxScale);
2412 object.AdjustScale();
2420 protected bool IsInOutline(notnull SCR_ForestGeneratorRectangle rectangle, vector pointLocal,
float additionalDistance = 0)
2422 foreach (SCR_ForestGeneratorLine line : rectangle.m_aLines)
2424 float distance = Math3D.PointLineSegmentDistance(pointLocal, line.p1.m_vPos, line.p2.m_vPos);
2426 foreach (ForestGeneratorOutline outline : m_aOutlines)
2428 if (!outline.m_bGenerate)
2431 if (outline.m_eOutlineType == SCR_EForestGeneratorOutlineType.SMALL && !line.p1.m_bSmallOutline)
2434 if (outline.m_eOutlineType == SCR_EForestGeneratorOutlineType.MIDDLE && !line.p1.m_bMiddleOutline)
2437 if (
distance > outline.m_fMinDistance - additionalDistance &&
distance < outline.m_fMaxDistance + additionalDistance)
2450 protected bool IsBeforeOutlineBorder(notnull SCR_ForestGeneratorRectangle rectangle, vector pointLocal,
float offset = 0)
2452 foreach (SCR_ForestGeneratorLine line : rectangle.m_aLines)
2454 float distance = Math3D.PointLineSegmentDistance(pointLocal, line.p1.m_vPos, line.p2.m_vPos);
2456 foreach (ForestGeneratorOutline outline : m_aOutlines)
2458 if (!outline.m_bGenerate)
2461 if (outline.m_eOutlineType == SCR_EForestGeneratorOutlineType.SMALL && !line.p1.m_bSmallOutline)
2464 if (outline.m_eOutlineType == SCR_EForestGeneratorOutlineType.MIDDLE && !line.p1.m_bMiddleOutline)
2467 if (
distance < outline.m_fMaxDistance - offset)
2479 protected float CalculateAreaForOutline(SCR_ForestGeneratorLine line, ForestGeneratorOutline outline)
2481 if (!line || !outline)
2484 return line.m_fLength * (outline.m_fMaxDistance - outline.m_fMinDistance);
2492 protected float CalculateAreaForOutline(SCR_ForestGeneratorPoint point, ForestGeneratorOutline outline)
2494 if (!point || !outline)
2497 float areaBigger = Math.PI * outline.m_fMaxDistance * outline.m_fMaxDistance;
2498 float areaSmaller = Math.PI * outline.m_fMinDistance * outline.m_fMinDistance;
2500 return (point.m_fAngle / 360) * (areaBigger - areaSmaller);
2504 protected override void OnRegenerate()
2506 super.OnRegenerate();
2508 RegenerateForest(
true);
2512 protected override void _WB_OnInit(inout vector mat[4], IEntitySource src)
2514 super._WB_OnInit(mat, src);
2515 if (!s_DebugShapeManager)
2516 s_DebugShapeManager =
new SCR_DebugShapeManager();
ref array< ref ForestGeneratorCluster > m_aClusters
bool m_bDrawDebugShapesRectangulation
bool m_bEntitiesFollowTerrainOnShapeMove
bool m_bDrawDebugShapesRegeneration
bool m_bPrintPerformanceDetails
ref Curve m_aGlobalOutlineScaleCurve
float m_fGlobalOutlineScaleCurveDistance
bool m_bDrawDebugShapesObstacles
bool m_bPrintEntitiesCount
bool m_bRegenerateEntireForest
not saved to layer (see _WB_OnKeyChanged), used as a button
ref array< ref ForestGeneratorLevel > m_aLevels
ref array< string > angles
ref RandomGenerator m_RandomGenerator
enum SCR_ECompassType EntityEditorProps(category:"GameScripted/Gadgets", description:"Compass", color:"0 0 255 255")
Prefab data class for compass component.
override void _WB_OnInit(IEntity owner, inout vector mat[4], IEntitySource src)
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 ...
SCR_DestructionSynchronizationComponentClass ScriptComponentClass int index
Get all prefabs that have the spawner data
ref array< ref MapLine > m_aLines
SCR_PossessingManagerComponentClass int
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
void SCR_TimeMeasurementHelper()
enum EVehicleType IEntity
proto external vector GetOrigin()
proto external void GetTransform(out vector mat[])
bool IsPosWithinSetDistance(vector pos)
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
void OnPointChangedInternal(IEntitySource shapeEntitySrc, ShapeEntity shapeEntity, PointChangedSituation situation, int pointIndex, vector position)
void OnShapeTransformInternal(IEntitySource shapeEntitySrc, ShapeEntity shapeEntity, array< vector > mins, array< vector > maxes)
void OnShapeInitInternal(IEntitySource shapeEntitySrc, ShapeEntity shapeEntity)
void BeforeShapeTransformInternal(IEntitySource shapeEntitySrc, ShapeEntity shapeEntity, inout vector oldTransform[4])
void OnShapeChangedInternal(IEntitySource shapeEntitySrc, ShapeEntity shapeEntity, array< vector > mins, array< vector > maxes)
class Class Clone()
Return shallow copy of object, or null if it is not allowed (not public constructor).
proto external string ToString()
Plain C++ pointer, no weak pointers, no memory management.
proto int ARGB(int a, int r, int g, int b)