ModuleBuildNode BuildLayoutRecursive(ModuleGrowthNode GrowthNode, List <Bounds> OccupiedBounds,
                                             int DepthFromStart, int DesiredDepth, bool bMainBranch, bool bForceIgnoreEndModule, SnapLayoutBuildState RecursiveState)
        {
            if (RecursiveState.NumTries >= snapConfig.MaxProcessingPower)
            {
                return(null);
            }
            RecursiveState.NumTries++;

            if (DepthFromStart > DesiredDepth)
            {
                return(null);
            }

            ModuleGrowthNode Top = GrowthNode;

            // Pick a door from this module to extend
            int             BestValidMainBranchDifference = int.MaxValue;
            ModuleBuildNode BestBuildNode = null;

            int SnapModuleListLength = RecursiveState.ModuleInfoList.Count;

            int[] ShuffledIndices = MathUtils.GetShuffledIndices(SnapModuleListLength, random);
            for (int si = 0; si < ShuffledIndices.Length; si++)
            {
                int Index = ShuffledIndices[si];

                ModuleInfo Module = RecursiveState.ModuleInfoList[Index];

                var AttachmentConfig = new SnapAttachmentConfiguration();
                if (!FindAttachmentConfiguration(Module, Top.IncomingModule, ref Top.ModuleTransform, Top.IncomingModuleDoorIndex, OccupiedBounds, ref AttachmentConfig))
                {
                    continue;
                }

                var BuildNode = new ModuleBuildNode();
                BuildNode.AttachmentConfig  = AttachmentConfig;
                BuildNode.IncomingDoorIndex = Top.IncomingModuleDoorIndex;
                BuildNode.Module            = Module;

                if (DepthFromStart == DesiredDepth)
                {
                    // This has to be the leaf node
                    return(BuildNode);
                }

                if (BestBuildNode == null)
                {
                    BestBuildNode = BuildNode;
                }

                // We found a valid module. Use this
                OccupiedBounds.Add(AttachmentConfig.AttachedModuleWorldBounds);

                int AttachmentDoorIndex = AttachmentConfig.AttachedModuleDoorIndex;

                // Extend from this door further
                for (int ExtensionDoorIndex = 0; ExtensionDoorIndex < Module.ConnectionTransforms.Length; ExtensionDoorIndex++)
                {
                    if (ExtensionDoorIndex == AttachmentDoorIndex && Top.IncomingModuleDoorIndex != -1)
                    {
                        // Don't want to extend from the door we came in through
                        continue;
                    }

                    int ModuleCountContribution = 1;

                    // Grow this branch further
                    var NextNode = new ModuleGrowthNode();
                    NextNode.IncomingModuleDoorIndex = ExtensionDoorIndex;
                    NextNode.ModuleTransform         = AttachmentConfig.AttachedModuleTransform;
                    NextNode.IncomingModule          = Module;
                    ModuleBuildNode ExtensionNode = BuildLayoutRecursive(NextNode, OccupiedBounds, DepthFromStart + ModuleCountContribution, DesiredDepth,
                                                                         bMainBranch, false, RecursiveState);

                    if (ExtensionNode != null)
                    {
                        int BranchLength            = DepthFromStart + ExtensionNode.DepthFromLeaf;
                        int ValidDistanceDifference = Mathf.Abs(BranchLength - DesiredDepth);
                        if (ValidDistanceDifference < BestValidMainBranchDifference || RecursiveState.bFoundBestBuild)
                        {
                            BestValidMainBranchDifference = ValidDistanceDifference;
                            BuildNode.Extensions.Clear();
                            ExtensionNode.Parent = BuildNode;
                            BuildNode.Extensions.Add(ExtensionNode);
                            BuildNode.DepthFromLeaf = Mathf.Max(BuildNode.DepthFromLeaf, ExtensionNode.DepthFromLeaf + ModuleCountContribution);

                            BestBuildNode = BuildNode;
                        }

                        if (BranchLength >= DesiredDepth)
                        {
                            // We found a branch with the desired length
                            RecursiveState.bFoundBestBuild = true;
                        }

                        if (RecursiveState.bFoundBestBuild)
                        {
                            break;
                        }
                    }
                }

                // Remove it since we move out
                OccupiedBounds.Remove(AttachmentConfig.AttachedModuleWorldBounds);

                if (RecursiveState.bFoundBestBuild)
                {
                    break;
                }
            }

            return(BestBuildNode);
        }
        bool FindAttachmentConfiguration(ModuleInfo TargetModule, ModuleInfo IncomingModule, ref Matrix4x4 IncomingModuleTransform,
                                         int IncomingDoorIndex, List <Bounds> OccupiedBounds, ref SnapAttachmentConfiguration OutAttachmentConfig)
        {
            int NumDoors = TargetModule.ConnectionTransforms.Length;

            if (IncomingDoorIndex >= NumDoors)
            {
                return(false);
            }

            if (IncomingDoorIndex < 0 || IncomingModule == null)
            {
                OutAttachmentConfig.AttachedModule            = TargetModule;
                OutAttachmentConfig.AttachedModuleDoorIndex   = random.Range(0, NumDoors - 1);
                OutAttachmentConfig.AttachedModuleWorldBounds = TargetModule.Bounds;
                OutAttachmentConfig.AttachedModuleTransform   = Matrix4x4.identity;
                return(true);
            }

            if (IncomingDoorIndex >= NumDoors)
            {
                return(false);
            }
            Matrix4x4 IncomingDoorTransform = IncomingModule.ConnectionTransforms[IncomingDoorIndex];
            bool      bFoundValid           = false;

            int[] ShuffledIndices = MathUtils.GetShuffledIndices(NumDoors, random);
            for (int si = 0; si < ShuffledIndices.Length; si++)
            {
                int       Index = ShuffledIndices[si];
                Matrix4x4 AttachmentDoorTransform = TargetModule.ConnectionTransforms[Index];

                // Align the module with a door that fits the incoming door
                Matrix4x4 ModuleTransform = FindAttachmentTransform(ref IncomingModuleTransform, ref IncomingDoorTransform, ref AttachmentDoorTransform);

                if (!snapConfig.RotateModulesToFit)
                {
                    // Rotation is not allowed. Check if we rotated the module
                    var moduleRotation = Matrix.GetRotation(ref ModuleTransform);
                    if (Mathf.Abs(moduleRotation.eulerAngles.y) > 0.1f)
                    {
                        // Module was rotated
                        continue;
                    }
                }

                {
                    // Calculate the bounds of the module
                    Bounds ModuleWorldBounds = TargetModule.Bounds;
                    ModuleWorldBounds = MathUtils.TransformBounds(ModuleTransform, ModuleWorldBounds);
                    Bounds ContractedModuleWorldBounds = ModuleWorldBounds;
                    ContractedModuleWorldBounds.Expand(-1 * (snapConfig.CollisionTestContraction));

                    // Check if this module would intersect with any of the existing modules
                    bool bIntersects = false;
                    foreach (var OccupiedBound in OccupiedBounds)
                    {
                        if (ContractedModuleWorldBounds.Intersects(OccupiedBound))
                        {
                            // intersects. Do not spawn a module here
                            bIntersects = true;
                            break;
                        }
                    }
                    if (bIntersects)
                    {
                        continue;
                    }

                    // We found a valid module. Use this
                    OutAttachmentConfig.AttachedModule            = TargetModule;
                    OutAttachmentConfig.AttachedModuleDoorIndex   = Index;
                    OutAttachmentConfig.AttachedModuleWorldBounds = ModuleWorldBounds;
                    OutAttachmentConfig.AttachedModuleTransform   = ModuleTransform;
                    bFoundValid = true;
                    break;
                }
            }

            return(bFoundValid);
        }