Beispiel #1
0
            public PlanetBiome(MyPlanetMaterialGroup group)
            {
                Value = group.Value;

                Name = group.Name;

                Rules = new List <PlanetMaterialRule>(group.MaterialRules.Length);

                for (int i = 0; i < group.MaterialRules.Length; i++)
                {
                    Rules.Add(new PlanetMaterialRule(group.MaterialRules[i]));
                }

                MateriaTree = new MyDynamicAABBTree(Vector3.Zero);

                foreach (var rule in Rules)
                {
                    BoundingBox bb = new BoundingBox(new Vector3(rule.Height.Min, rule.Latitude.Min, rule.Longitude.Min), new Vector3(rule.Height.Max, rule.Latitude.Max, rule.Longitude.Max));
                    MateriaTree.AddProxy(ref bb, rule, 0);
                    if (rule.Latitude.Mirror)
                    {
                        float min = -bb.Max.Y;
                        bb.Max.Y = -bb.Min.Y;
                        bb.Min.Y = min;
                        MateriaTree.AddProxy(ref bb, rule, 0);
                    }
                }
            }
        public void AddRoom(ProceduralRoom room)
        {
            if (m_rooms.ContainsKey(room.RoomID))
            {
                throw new ArgumentException("Room ID already used");
            }
            m_rooms[room.RoomID] = room;
            var aabb = room.BoundingBoxBoth;

            room.m_aabbProxyID = m_roomTree.AddProxy(ref aabb, room, 0);
            m_roomsSafeOrder.Add(room);
            foreach (var k in room.MountPoints)
            {
                foreach (var p in k.MountLocations)
                {
                    if (m_mountPoints.ContainsKey(p))
                    {
                        Logger.Warning("Room {0} at {1} has mount point {4}:{5} that intersect with mount point {6}:{7} of room {2} at {3}", m_mountPoints[p].Owner.Part.Name, m_mountPoints[p].Owner.Transform.Translation,
                                       room.Part.Name, room.Transform.Translation, m_mountPoints[p].MountPoint.MountType, m_mountPoints[p].MountPoint.MountName,
                                       k.MountPoint.MountType, k.MountPoint.MountName);
                    }
                    else
                    {
                        m_mountPoints.Add(p, k);
                    }
                }
            }
            room.TakeOwnership(this);
            using (room.Part.LockSharedUsing())
                BlockSetInfo.AddToSelf(room.Part.BlockSetInfo);
            RoomAdded?.Invoke(room);
        }
        private void GetChunk(ref Vector3I coord, out VoxelChunk chunk, MyStorageDataTypeFlags required)
        {
            using (m_cacheLock.AcquireExclusiveUsing())
            {
                if (!m_cachedChunks.TryGetValue(coord, out chunk))
                {
                    chunk = new VoxelChunk(coord);

                    var rangeStart = coord << VoxelChunk.SizeBits;
                    var rangeEnd   = ((coord + 1) << VoxelChunk.SizeBits) - 1;

                    if (required != 0)
                    {
                        using (m_storageLock.AcquireSharedUsing())
                            ReadDatForChunk(chunk, required);
                    }

                    m_chunksbyAge.Enqueue(coord);
                    m_cachedChunks.Add(coord, chunk);

                    var bb = new BoundingBox(rangeStart, rangeEnd);

                    chunk.TreeProxy = m_cacheMap.AddProxy(ref bb, chunk, 0);
                }
                else if ((chunk.Cached & required) != required)
                {
                    using (m_storageLock.AcquireSharedUsing())
                        ReadDatForChunk(chunk, required & ~chunk.Cached);
                }
            }
        }
Beispiel #4
0
        private void CheckWaiting()
        {
            Dictionary <string, IMyModelDummy> tmp = null;
            var limit = _waitingInsert.Count;

            while (_waitingInsert.Count > 0 && (limit-- > 0))
            {
                var ins = _waitingInsert.Dequeue();
                if (tmp == null)
                {
                    tmp = new Dictionary <string, IMyModelDummy>();
                }
                if (ins.Entity.Model != null)
                {
                    tmp.Clear();
                    ins.Entity.Model.GetDummies(tmp);
                    IMyModelDummy dummy;
                    if (tmp.TryGetValue(ins.Path, out dummy))
                    {
                        var bb = new BoundingBox(-Vector3.Half, Vector3.Half).Transform(
                            dummy.Matrix * ins.Entity.LocalMatrix);
                        var proxyId = _detectorTree.AddProxy(ref bb, ins, 0);
                        ins.Update(proxyId, bb);
                        DoConnect(ins);
                        continue;
                    }
                }

                _waitingInsert.Enqueue(ins);
            }
        }
Beispiel #5
0
        public static void UpdateLightProxy(MyLight light)
        {
            if ((!light.LightOn || light.LightType == MyLight.LightTypeEnum.None) && light.ProxyId != MyDynamicAABBTree.NullNode)
            {
                m_tree.RemoveProxy(light.ProxyId);
                light.ProxyId = MyDynamicAABBTree.NullNode;
            }

            BoundingBox bbox = BoundingBoxHelper.InitialBox;

            if (light.IsTypePoint || light.IsTypeHemisphere)
            {
                bbox = BoundingBox.CreateFromSphere(light.PointBoundingSphere);
            }
            if (light.IsTypeSpot)
            {
                var box = light.SpotBoundingBox;
                BoundingBoxHelper.AddBBox(box, ref bbox);
            }

            if (light.ProxyId == MyDynamicAABBTree.NullNode)
            {
                light.ProxyId = m_tree.AddProxy(ref bbox, light, 0);
            }
            else
            {
                m_tree.MoveProxy(light.ProxyId, ref bbox, Vector3.Zero);
            }
        }
        public int Register(MySmallShipBot entity)
        {
            System.Diagnostics.Debug.Assert(entity == null || !entity.Closed);

            entity.OnPositionChanged += OnPositionChangedHandler;

            MyDangerZoneElement element = new MyDangerZoneElement(entity);

            BoundingBox bbox = new BoundingBox(entity.WorldAABB.Min - new Vector3(DANGER_ZONE_SIZE), entity.WorldAABB.Max + new Vector3(DANGER_ZONE_SIZE));

            int proxyId = m_dangerZoneStructure.AddProxy(ref bbox, element, 0);

            return(proxyId);
        }
Beispiel #7
0
        internal static int UpdateBvh(MyDynamicAABBTree bvh, LightId lid, bool enabled, int proxy, ref BoundingBox aabb)
        {
            if (enabled && proxy == -1)
            {
                return(bvh.AddProxy(ref aabb, lid, 0));
            }
            else if (enabled && proxy != -1)
            {
                bvh.MoveProxy(proxy, ref aabb, Vector3.Zero);
                return(proxy);
            }
            else
            {
                bvh.RemoveProxy(proxy);
            }

            return(-1);
        }
Beispiel #8
0
        public static void Add(MyEntity entity)
        {
            if (entity.GamePruningProxyId != MyConstants.GAME_PRUNING_STRUCTURE_PROXY_ID_NOT_INSERTED)
            {
                return;                                                                                         // already inserted
            }
            BoundingBox bbox = entity.WorldAABB;

            if (bbox.Size() == Vector3.Zero)
            {
                return;                               // don't add entities with zero bounding boxes
            }
            if (entity is MyWayPoint)
            {
                entity.GamePruningProxyId = m_waypoints.AddProxy(ref bbox, entity, 0);
            }
            else
            {
                entity.GamePruningProxyId = m_others.AddProxy(ref bbox, entity, 0);
            }
        }
Beispiel #9
0
        internal static int UpdateBvh(MyDynamicAABBTree bvh, LightId lid, bool enabled, int proxy, ref BoundingBox aabb)
        {
            if(enabled && proxy == -1)
            {
                return bvh.AddProxy(ref aabb, lid, 0);
            }
            else if(enabled && proxy != -1)
            {
                bvh.MoveProxy(proxy, ref aabb, Vector3.Zero);
                return proxy;
            }
            else
            {
                bvh.RemoveProxy(proxy);
            }

            return -1;
        }
            public PlanetBiome(MyPlanetMaterialGroup group)
            {
                Value = group.Value;

                Name = group.Name;

                Rules = new List<PlanetMaterialRule>(group.MaterialRules.Length);

                for (int i = 0; i < group.MaterialRules.Length; i++)
                {
                    Rules.Add(new PlanetMaterialRule(group.MaterialRules[i]));
                }

                MateriaTree = new MyDynamicAABBTree(Vector3.Zero);

                foreach (var rule in Rules)
                {
                    BoundingBox bb = new BoundingBox(new Vector3(rule.Height.Min, rule.Latitude.Min, rule.Longitude.Min), new Vector3(rule.Height.Max, rule.Latitude.Max, rule.Longitude.Max));
                    MateriaTree.AddProxy(ref bb, rule, 0);
                    if (rule.Latitude.Mirror)
                    {
                        float min = -bb.Max.Y;
                        bb.Max.Y = -bb.Min.Y;
                        bb.Min.Y = min;
                        MateriaTree.AddProxy(ref bb, rule, 0);
                    }
                }
            }
        private void AddSectorEntities(MySectorObjectCounts asteroidCounts, MyMwcVector3Int sectorPosition, Random random, float entityMinimalSize, int maxEntityCount,  List<MySolarSystemMapEntity> entities, bool onlyStaticAsteroids)
        {
                      
            // Space around asteroid should be at least 1.2x - 2x it's size
            float asteroidSpacingCoeficient = 0.7f;

            // Asteroid count mean is 40%
            float asteroidCountMean = 0.4f;

            Dictionary<int, int> entityCounts = new Dictionary<int, int>();
            foreach (MySolarSystemEntityEnum t in Enum.GetValues(typeof(MySolarSystemEntityEnum)))
            {
                entityCounts.Add((int)t, 0);
            }

            MyDynamicAABBTree prunningStructure = new MyDynamicAABBTree(Vector3.Zero);

            foreach (BoundingSphere boundingSphere in m_safeAreas)
            {
                BoundingBox bb = BoundingBox.CreateFromSphere(boundingSphere);
                prunningStructure.AddProxy(ref bb, new Render.MyRenderObject(null, null), 0);
            }


            // Generate asteroids, check collisions (order asteroids by size)
            //var asteroids = GetAsteroids(asteroidCounts, entityMinimalSize);
            var asteroids = GetAsteroids(asteroidCounts, entityMinimalSize);

            foreach (var info in asteroids)
            {
                if (info.EntityType != MySolarSystemEntityEnum.StaticAsteroid && onlyStaticAsteroids)
                    continue;

                float radius = info.SizeInMeters / 2;
                float count = info.ObjectCount;
                float positionOffset = 1.3f;
                count = (float)Math.Round(count * random.Float(1 - asteroidCountMean, 1 + asteroidCountMean));

                if (info.EntityType == MySolarSystemEntityEnum.VoxelAsteroid)
                {
                    positionOffset = 0.6f; //generate voxels more in center
                }

                while (entityCounts[(int)info.EntityType] < count && entityCounts[(int)info.EntityType] < maxEntityCount)
                {
                    Vector3? pos = FindEntityPosition(prunningStructure, random, radius, positionOffset, asteroidSpacingCoeficient);

                    if (pos.HasValue)
                    {
                        MySolarSystemMapEntity entity = new MySolarSystemMapEntity(sectorPosition, pos.Value, info.SizeInMeters, info.EntityType.ToString(), info.EntityType);
                        entities.Add(entity);

                        if (!MySectorGenerator.IsOutsideSector(pos.Value, radius))
                        {
                            entityCounts[(int)info.EntityType]++;
                        }
                        
                        BoundingBox bb = new BoundingBox(pos.Value - new Vector3(radius), pos.Value + new Vector3(radius));
                        prunningStructure.AddProxy(ref bb, new Render.MyRenderObject(null, null), 0);
                    }
                    else
                        entityCounts[(int)info.EntityType]++;
                }
            }
        }
        /// <summary>
        /// Create and connect waypoints to enable navigation outside prefab containers.
        /// Needs to be called from the main thread (because it uses MyEntities.GetSafeIterationHelperForAll).
        /// </summary>
        public static void CreateWaypointsAroundLargeStaticObjects()
        {
            if (m_GPSWaypointsInited) return;

            MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().StartProfilingBlock("CreateWaypointsAroundLargeStaticObjects");

            // memory benchmark
            //GC.Collect(2);
            //MyMwcLog.WriteLine("#CreateWaypointsAroundLargeStaticObjects# before: working set " + MyValueFormatter.GetFormatedLong(Environment.WorkingSet) + ", gc " + MyValueFormatter.GetFormatedLong(GC.GetTotalMemory(false)));

            // counters for debugging
            int largeObjects = 0;
            int totalWaypointsOutside = 0;
            int bigSubdivisions = 0;
            int freeEnvelopes = 0;
            int edgesWithoutRaycasts = 0;
            int edgesWithRaycastsAdded = 0;
            int edgesWithRaycastsNotAdded = 0;
            int closed = 0;

            var envelopes = new List<MyWayPoint[, ,]>();
            var envelopeEntity = new List<MyEntity>();
            var envelopeBvh = new MyDynamicAABBTree(MyConstants.GAME_PRUNING_STRUCTURE_AABB_EXTENSION);

            var nonFree = new HashSet<MyWayPoint>();

            MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().StartProfilingBlock("create envelopes");

            var interestingBoxInflation = new Vector3(MyWaypointConstants.MAXIMUM_BOX_DISTANCE_TO_INTERESTING_STUFF_TO_GENERATE_WAYPOINTS);
            var interestingBoxes = new List<BoundingBox>();

            // find interesting stuff (only prefab containers for now)
            foreach (var entity in MyEntities.GetEntities())
            {
                if (!(entity is MyPrefabContainer)) continue;

                entity.UpdateAABBHr();
                BoundingBox box = entity.WorldAABBHr;
                if (box.Max - box.Min == Vector3.Zero)
                {
                    box = entity.WorldAABB;
                    if (box.Max - box.Min == Vector3.Zero) continue;  // no bounding box
                }

                BoundingBox extrudedBox = new BoundingBox(box.Min - interestingBoxInflation, box.Max + interestingBoxInflation);
                interestingBoxes.Add(extrudedBox);
            }

            int ss = 0;
            int madelynsBox = -1;
            // create envelopes
            foreach (var entity in MyEntities.GetSafeIterationHelperForAll())
            {
                if (!(entity is MyVoxelMap || entity is MyPrefabContainer || entity is MyStaticAsteroid)) continue;

                entity.UpdateAABBHr();
                BoundingBox box = entity.WorldAABBHr;
                if (box.Max - box.Min == Vector3.Zero)
                    box = entity.WorldAABB;
                if (entity is MyStaticAsteroid &&
                    (box.Max - box.Min).LengthSquared() < MyWaypointConstants.MINIMUM_ASTEROID_DIAGONAL_LENGTH_TO_GENERATE_WAYPOINTS * MyWaypointConstants.MINIMUM_ASTEROID_DIAGONAL_LENGTH_TO_GENERATE_WAYPOINTS)
                {
                    continue;  // small static asteroids: ignore
                }
                if (entity is MyStaticAsteroid)
                {
                    ss++;
                    bool inInteresting = false;
                    foreach (var iBox in interestingBoxes)
                    {
                        if (iBox.Contains(box) != ContainmentType.Disjoint)
                        {
                            inInteresting = true;
                            break;
                        }
                    }
                    if (!inInteresting) continue;  // static asteroids far from interesting stuff: ignore
                }
                // enlarge by 1% and 15 meters on each side
                BoundingBox extrudedBox = new BoundingBox(box.Min - (box.Max - box.Min) * 0.01f - new Vector3(15, 15, 15), box.Max + (box.Max - box.Min) * 0.01f + new Vector3(15, 15, 15));

                //diagonals.Add((float)Math.Sqrt((box.Max - box.Min).LengthSquared()));

                var waypointsOutside = new HashSet<MyWayPoint>();

                MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().StartProfilingBlock("find crossing");
                // add all edges that cross the non-extruded box from inside to outside (remember out-vertices)
                foreach (var waypoint in MyGamePruningStructure.GetAllEntitiesInBox(ref extrudedBox, MyGamePruningStructure.QueryFlags.Waypoints))
                {
                    var v = waypoint as MyWayPoint;
                    if (!v.Save) continue;
                    nonFree.Add(v);
                    using (MyWayPoint.NeighborsLock.AcquireSharedUsing())
                    {
                        foreach (var n in v.Neighbors) if (n.Save && extrudedBox.Contains(n.Position) != ContainmentType.Contains)
                            {
                                if (waypointsOutside.Add(n)) totalWaypointsOutside++;
                                nonFree.Add(n);
                            }
                    }
                }
                MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock();

                // create envelope
                int s = 1;
                if (waypointsOutside.Count > 0 || entity as MyStaticAsteroid == null)  // voxel maps and prefabs are automatically subdivided more
                {
                    s = 2;
                    bigSubdivisions++;
                }
                MyWayPoint[, ,] envelope = new MyWayPoint[s + 1, s + 1, s + 1];

                for (int i = 0; i <= s; i++) for (int j = 0; j <= s; j++) for (int k = 0; k <= s; k++)
                        {
                            if (s == 2 && i == 1 && j == 1 && k == 1) continue;
                            envelope[i, j, k] = CreateWaypoint(new Vector3(
                                extrudedBox.Min.X + i * (extrudedBox.Max.X - extrudedBox.Min.X) / s,
                                extrudedBox.Min.Y + j * (extrudedBox.Max.Y - extrudedBox.Min.Y) / s,
                                extrudedBox.Min.Z + k * (extrudedBox.Max.Z - extrudedBox.Min.Z) / s
                            ), null);
                            envelope[i, j, k].Save = false;  // don't save generated waypoints
                            nonFree.Add(envelope[i, j, k]);

                            // connect with neighbors
                            // use ConnectIfNoAABBBlockers only for non-static asteroids
                            // don't connect to the non-existing middle vertex
                            if (entity is MyStaticAsteroid)
                            {
                                if (i != 0) if (!(s == 2 && i - 1 == 1 && j == 1 && k == 1)) { MyWayPoint.Connect(envelope[i, j, k], envelope[i - 1, j, k]); edgesWithoutRaycasts++; }
                                if (j != 0) if (!(s == 2 && j - 1 == 1 && i == 1 && k == 1)) { MyWayPoint.Connect(envelope[i, j, k], envelope[i, j - 1, k]); edgesWithoutRaycasts++; }
                                if (k != 0) if (!(s == 2 && k - 1 == 1 && j == 1 && i == 1)) { MyWayPoint.Connect(envelope[i, j, k], envelope[i, j, k - 1]); edgesWithoutRaycasts++; }
                            }
                            else
                            {
                                if (i != 0) if (!(s == 2 && i - 1 == 1 && j == 1 && k == 1)) { if (MyWayPoint.ConnectIfNoAABBBlockers(envelope[i, j, k], envelope[i - 1, j, k])) edgesWithRaycastsAdded++; else edgesWithRaycastsNotAdded++; }
                                if (j != 0) if (!(s == 2 && j - 1 == 1 && i == 1 && k == 1)) { if (MyWayPoint.ConnectIfNoAABBBlockers(envelope[i, j, k], envelope[i, j - 1, k])) edgesWithRaycastsAdded++; else edgesWithRaycastsNotAdded++; }
                                if (k != 0) if (!(s == 2 && k - 1 == 1 && j == 1 && i == 1)) { if (MyWayPoint.ConnectIfNoAABBBlockers(envelope[i, j, k], envelope[i, j, k - 1])) edgesWithRaycastsAdded++; else edgesWithRaycastsNotAdded++; }
                            }

                            // if it's a part of a face that faces an out-vertex, connect it
                            foreach (var v in waypointsOutside)
                                if ((i == 0 && v.Position.X <= envelope[i, j, k].Position.X) ||
                                    (i == s && v.Position.X >= envelope[i, j, k].Position.X) ||
                                    (j == 0 && v.Position.Y <= envelope[i, j, k].Position.Y) ||
                                    (j == s && v.Position.Y >= envelope[i, j, k].Position.Y) ||
                                    (k == 0 && v.Position.Z <= envelope[i, j, k].Position.Z) ||
                                    (k == s && v.Position.Z >= envelope[i, j, k].Position.Z)
                                )
                                {
                                    if (MyWayPoint.ConnectIfNoAABBBlockers(v, envelope[i, j, k]))
                                        edgesWithRaycastsAdded++;
                                    else
                                        edgesWithRaycastsNotAdded++;
                                }
                        }

                envelopes.Add(envelope);
                envelopeEntity.Add(entity);
                envelopeBvh.AddProxy(ref extrudedBox, envelopes.Count - 1, 0);
                largeObjects++;

                if (entity.Name == "Madelyn")
                {
                    madelynsBox = envelopes.Count - 1;
                }
            }

            MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock();
            var componentsDone = new HashSet<int>();

            MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().StartProfilingBlock("connect free wps");

            // free waypoint: check whether it's connected to an envelope
            foreach (var v in GetVertexListForModification()) if (!nonFree.Contains(v))
                {
                    int id = GetConnectedComponentId(v);
                    if (componentsDone.Contains(id)) continue;

                    componentsDone.Add(id);

                    var connectedWaypoints = GetWaypointsWithConnectedComponentId(id);

                    // is this component already connected to an envelope?
                    var box = new BoundingBox(v.Position, v.Position);
                    foreach (var w in connectedWaypoints) if (w.Save)
                        {
                            var pos = w.Position;
                            box = box.Include(ref pos);
                            if (nonFree.Contains(w))
                                goto alreadyConnected;
                        }

                    BoundingBox extrudedBox = new BoundingBox(box.Min - (box.Max - box.Min) * 0.01f - new Vector3(5, 5, 5), box.Max + (box.Max - box.Min) * 0.01f + new Vector3(5, 5, 5));

                    // no - create a new one
                    int s = 1;
                    MyWayPoint[, ,] envelope = new MyWayPoint[s + 1, s + 1, s + 1];
                    for (int i = 0; i <= s; i++) for (int j = 0; j <= s; j++) for (int k = 0; k <= s; k++)
                            {
                                envelope[i, j, k] = CreateWaypoint(new Vector3(
                                    extrudedBox.Min.X + i * (extrudedBox.Max.X - extrudedBox.Min.X) / s,
                                    extrudedBox.Min.Y + j * (extrudedBox.Max.Y - extrudedBox.Min.Y) / s,
                                    extrudedBox.Min.Z + k * (extrudedBox.Max.Z - extrudedBox.Min.Z) / s
                                ), null);
                                envelope[i, j, k].Save = false;  // don't save generated waypoints
                                nonFree.Add(envelope[i, j, k]);

                                // connect with neighbors
                                // should use ConnectIfVisible, but it's slow and we can resolve it while computing the GPS
                                if (i != 0) { if (MyWayPoint.ConnectIfNoAABBBlockers(envelope[i, j, k], envelope[i - 1, j, k])) edgesWithRaycastsAdded++; else edgesWithRaycastsNotAdded++; }
                                if (j != 0) { if (MyWayPoint.ConnectIfNoAABBBlockers(envelope[i, j, k], envelope[i, j - 1, k])) edgesWithRaycastsAdded++; else edgesWithRaycastsNotAdded++; }
                                if (k != 0) { if (MyWayPoint.ConnectIfNoAABBBlockers(envelope[i, j, k], envelope[i, j, k - 1])) edgesWithRaycastsAdded++; else edgesWithRaycastsNotAdded++; }
                            }

                    // connect all waypoints to the closest corner of the new envelope
                    foreach (var w in connectedWaypoints)
                    {
                        var pos = w.Position;
                        if (MyWayPoint.ConnectIfNoAABBBlockers(w, envelope[pos.X < extrudedBox.GetCenter().X ? 0 : 1, pos.Y < extrudedBox.GetCenter().Y ? 0 : 1, pos.Z < extrudedBox.GetCenter().Z ? 0 : 1]))
                            edgesWithRaycastsAdded++;
                        else
                            edgesWithRaycastsNotAdded++;
                    }
                    envelopes.Add(envelope);
                    envelopeEntity.Add(null);
                    envelopeBvh.AddProxy(ref extrudedBox, envelopes.Count - 1, 0);
                    freeEnvelopes++;

                alreadyConnected: { }
                }

            MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock();
            MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().StartProfilingBlock("connect envelopes");

            // connect envelopes together
            for (int eIndex = 0; eIndex < envelopes.Count; eIndex++)
            {
                var e = envelopes[eIndex];
                int es = e.GetLength(0) - 1;
                var eCenter = 0.5f * (e[0, 0, 0].Position + e[es, es, es].Position);

                // get K closest indices
                var closestEnvelopeIndices = new List<int>();
                for (int i = 200; i <= 6400; i *= 2)  // try 200, 400, 800, 1600, 3200, 6400 m
                {
                    var halfExtent = new Vector3(i);
                    var bbox = new BoundingBox(eCenter - halfExtent, eCenter + halfExtent);
                    envelopeBvh.OverlapAllBoundingBox(ref bbox, closestEnvelopeIndices);
                    if (closestEnvelopeIndices.Count >= 16) break;
                }

                // connect them together
                int k = 0;
                foreach (var qIndex in closestEnvelopeIndices)
                {
                    if (++k == 16) break;  // take only 16 envelopes
                    if (qIndex == eIndex) continue;

                    var q = envelopes[qIndex];

                    int qs = q.GetLength(0) - 1;
                    var qCenter = 0.5f * (q[0, 0, 0].Position + q[qs, qs, qs].Position);

                    // connect the closest opposite vertices
                    int qx, qy, qz, ex, ey, ez;
                    if (qCenter.X < eCenter.X) { qx = qs; ex = 0; } else { qx = 0; ex = es; }
                    if (qCenter.Y < eCenter.Y) { qy = qs; ey = 0; } else { qy = 0; ey = es; }
                    if (qCenter.Z < eCenter.Z) { qz = qs; ez = 0; } else { qz = 0; ez = es; }

                    if (es > 1 || qs > 1)
                    {
                        if (MyWayPoint.ConnectIfNoAABBBlockers(e[ex, ey, ez], q[qx, qy, qz], envelopeEntity[eIndex], envelopeEntity[qIndex]))
                            edgesWithRaycastsAdded++;
                        else
                            edgesWithRaycastsNotAdded++;
                    }
                    else
                    {
                        // don't make a raycast if one of the envelopes isn't important
                        MyWayPoint.Connect(e[ex, ey, ez], q[qx, qy, qz]);
                        edgesWithoutRaycasts++;
                    }

                    // connect Madelyn's waypoint to envelopes
                    if (eIndex == madelynsBox)
                    {
                        MyEntity madelyn = envelopeEntity[madelynsBox];
                        foreach (var child in madelyn.Children)
                        {
                            var w = child as MyWayPoint;
                            if (w == null)
                                continue;
                            MyWayPoint.ConnectIfNoAABBBlockers(w, q[qx, qy, qz]);  // make a raycast
                        }
                    }
                }

                // connect Madelyn's waypoint to envelopes
                if (eIndex == madelynsBox)
                {
                    MyEntity madelyn = envelopeEntity[madelynsBox];

                    madelyn.UpdateAABBHr();
                    BoundingBox extrudedAABB = madelyn.WorldAABBHr;
                    extrudedAABB.Min -= new Vector3(500);
                    extrudedAABB.Max += new Vector3(500);

                    List<MyWayPoint> nearMadelynWaypoints = new List<MyWayPoint>();

                    foreach (var waypoint in MyGamePruningStructure.GetAllEntitiesInBox(ref extrudedAABB, MyGamePruningStructure.QueryFlags.Waypoints))
                    {
                        MyWayPoint v = waypoint as MyWayPoint;
                        if (v != null)
                        {
                            if (!v.Save)
                                continue;
                            nearMadelynWaypoints.Add(v);
                        }
                    }

                    foreach (var child in madelyn.Children)
                    {
                        var w = child as MyWayPoint;
                        if (w == null)
                            continue;

                        foreach (MyWayPoint v in nearMadelynWaypoints)
                        {
                            MyWayPoint.ConnectIfVisible(w, v);  // make a raycast
                        }
                    }
                }
            }

            // delete generated waypoints without edges
            foreach (var v in GetVertexListForModification())
            {
                if (!v.Save && v.Neighbors.Count == 0)
                {
                    v.MarkForClose();
                    closed++;
                }
            }

            m_GPSWaypointsInited = true;
            InvalidateStoredPathCache();

            // memory benchmark
            //GC.Collect(2);
            //MyMwcLog.WriteLine("#CreateWaypointsAroundLargeStaticObjects# after: working set " + MyValueFormatter.GetFormatedLong(Environment.WorkingSet) + ", gc " + MyValueFormatter.GetFormatedLong(GC.GetTotalMemory(false)));

            MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock();

            MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock();
        }
        /// <summary>
        /// Create and connect waypoints to enable navigation outside prefab containers.
        /// Needs to be called from the main thread (because it uses MyEntities.GetSafeIterationHelperForAll).
        /// </summary>
        public static void RecreateWaypointsAroundMadelyn()
        {
            MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().StartProfilingBlock("RecreateWaypointsAroundMadelyn");

            // counters for debugging
            int largeObjects = 0;
            int totalWaypointsOutside = 0;
            int bigSubdivisions = 0;
            int closed = 0;

            var envelopes = new List<MyWayPoint[, ,]>();
            var envelopeEntity = new List<MyEntity>();
            var envelopeBvh = new MyDynamicAABBTree(MyConstants.GAME_PRUNING_STRUCTURE_AABB_EXTENSION);

            var nonFree = new HashSet<MyWayPoint>();

            MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().StartProfilingBlock("create envelope");

            int madelynsBox = -1;
            // create envelopes
            {
                var entity = MyEntities.GetEntityByName("Madelyn");

                entity.UpdateAABBHr();
                BoundingBox box = entity.WorldAABBHr;
                if (box.Max - box.Min == Vector3.Zero)
                    box = entity.WorldAABB;

                // enlarge by 1% and 15 meters on each side
                BoundingBox extrudedBox = new BoundingBox(box.Min - (box.Max - box.Min) * 0.01f - new Vector3(15, 15, 15), box.Max + (box.Max - box.Min) * 0.01f + new Vector3(15, 15, 15));

                var waypointsOutside = new HashSet<MyWayPoint>();

                MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().StartProfilingBlock("find crossing");
                // add all edges that cross the non-extruded box from inside to outside (remember out-vertices)
                foreach (var waypoint in MyGamePruningStructure.GetAllEntitiesInBox(ref extrudedBox, MyGamePruningStructure.QueryFlags.Waypoints))
                {
                    var v = waypoint as MyWayPoint;
                    if (!v.Save) continue;
                    nonFree.Add(v);
                    using (MyWayPoint.NeighborsLock.AcquireSharedUsing())
                    {
                        foreach (var n in v.Neighbors)
                            if (n.Save && extrudedBox.Contains(n.Position) != ContainmentType.Contains)
                            {
                                if (waypointsOutside.Add(n)) totalWaypointsOutside++;
                                nonFree.Add(n);
                            }
                    }
                }
                MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock();

                // create envelope
                int s = 1;
                if (waypointsOutside.Count > 0 || entity as MyStaticAsteroid == null)  // voxel maps and prefabs are automatically subdivided more
                {
                    s = 2;
                    bigSubdivisions++;
                }
                MyWayPoint[, ,] envelope = new MyWayPoint[s + 1, s + 1, s + 1];

                for (int i = 0; i <= s; i++) for (int j = 0; j <= s; j++) for (int k = 0; k <= s; k++)
                {
                    if (s == 2 && i == 1 && j == 1 && k == 1) continue;
                    envelope[i, j, k] = CreateWaypoint(new Vector3(
                        extrudedBox.Min.X + i * (extrudedBox.Max.X - extrudedBox.Min.X) / s,
                        extrudedBox.Min.Y + j * (extrudedBox.Max.Y - extrudedBox.Min.Y) / s,
                        extrudedBox.Min.Z + k * (extrudedBox.Max.Z - extrudedBox.Min.Z) / s
                    ), null);
                    envelope[i, j, k].Save = false;  // don't save generated waypoints
                    nonFree.Add(envelope[i, j, k]);

                    // assume Madelyn's envelope has no blockers
                    if (i != 0) if (!(s == 2 && i - 1 == 1 && j == 1 && k == 1)) MyWayPoint.Connect(envelope[i, j, k], envelope[i - 1, j, k]);
                    if (j != 0) if (!(s == 2 && j - 1 == 1 && i == 1 && k == 1)) MyWayPoint.Connect(envelope[i, j, k], envelope[i, j - 1, k]);
                    if (k != 0) if (!(s == 2 && k - 1 == 1 && j == 1 && i == 1)) MyWayPoint.Connect(envelope[i, j, k], envelope[i, j, k - 1]);

                    // if it's a part of a face that faces an out-vertex, connect it
                    foreach (var v in waypointsOutside)
                        if ((i == 0 && v.Position.X <= envelope[i, j, k].Position.X) ||
                            (i == s && v.Position.X >= envelope[i, j, k].Position.X) ||
                            (j == 0 && v.Position.Y <= envelope[i, j, k].Position.Y) ||
                            (j == s && v.Position.Y >= envelope[i, j, k].Position.Y) ||
                            (k == 0 && v.Position.Z <= envelope[i, j, k].Position.Z) ||
                            (k == s && v.Position.Z >= envelope[i, j, k].Position.Z)
                        )
                        {
                            MyWayPoint.Connect(v, envelope[i, j, k]);
                        }
                }

                envelopes.Add(envelope);
                envelopeEntity.Add(entity);
                envelopeBvh.AddProxy(ref extrudedBox, envelopes.Count - 1, 0);
                largeObjects++;

                madelynsBox = envelopes.Count - 1;
            }

            MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock();

            MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().StartProfilingBlock("disconnect from old waypoints");
            var madelynWaypoints = new HashSet<MyWayPoint>();
            {
                var entity = MyEntities.GetEntityByName("Madelyn");

                foreach (var child in entity.Children)
                {
                    var w = child as MyWayPoint;
                    if (w == null) continue;
                    madelynWaypoints.Add(w);
                }
                foreach (var v in madelynWaypoints)
                {
                    var outsiders = new List<MyWayPoint>();
                    foreach (var n in v.Neighbors)
                        if (!madelynWaypoints.Contains(n)) outsiders.Add(n);
                    foreach (var n in outsiders)
                        MyWayPoint.Disconnect(v, n);
                }
            }

            MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock();

            MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().StartProfilingBlock("connect to new surrounding waypoints");

            for (int eIndex = 0; eIndex < envelopes.Count; eIndex++)
            {
                var e = envelopes[eIndex];
                int es = e.GetLength(0) - 1;
                var eCenter = 0.5f * (e[0, 0, 0].Position + e[es, es, es].Position);

                // connect Madelyn's inner waypoints to waypoints around her
                if (eIndex == madelynsBox)
                {
                    MyEntity madelyn = envelopeEntity[madelynsBox];

                    madelyn.UpdateAABBHr();
                    BoundingBox extrudedAABB = madelyn.WorldAABBHr;
                    extrudedAABB.Min -= new Vector3(500);
                    extrudedAABB.Max += new Vector3(500);

                    List<MyWayPoint> nearMadelynWaypoints = new List<MyWayPoint>();

                    foreach (var waypoint in MyGamePruningStructure.GetAllEntitiesInBox(ref extrudedAABB, MyGamePruningStructure.QueryFlags.Waypoints))
                    {
                        MyWayPoint v = waypoint as MyWayPoint;
                        if (v != null)
                        {
                            if (!v.Save) continue;
                            if (madelyn.Children.Contains(v)) continue;
                            nearMadelynWaypoints.Add(v);
                        }
                    }
                    int connected = 0, raycasts = 0;
                    foreach (MyWayPoint v in nearMadelynWaypoints)
                    {
                        foreach (var child in madelyn.Children)
                        {
                            var w = child as MyWayPoint;
                            if (w == null) continue;

                            if (v.Neighbors.Contains(w)) continue;
                            raycasts++;
                            if (MyWayPoint.ConnectIfVisible(w, v))  // make a raycast
                            {
                                connected++;
                                break;
                            }
                        }
                    }
                }
            }

            // delete generated waypoints without edges
            foreach (var v in GetVertexListForModification())
            {
                if (!v.Save && v.Neighbors.Count == 0)
                {
                    v.MarkForClose();
                    closed++;
                }
            }

            InvalidateStoredPathCache();

            MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock();

            MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock();
        }