public static void DetermineLevelChunking(Level level)
        {
            m_level      = level;
            m_num_chunks = 0;

            int num_segments = 0;

            SegmentType[] SegmentTypes = null;
            if (EnableAutomaticChunking)
            {
                SegmentTypes = new SegmentType[Level.MAX_SEGMENTS];
            }

            //Clear out chunk count array
            m_chunk_size.Init(0);

            if (!EnableAutomaticChunking)
            {
                //No automatic chunking, so put everything in one chunk
                foreach (int segmentIndex in level.EnumerateAliveSegmentIndices())
                {
                    level.segment[segmentIndex].m_chunk_num = 0;
                    num_segments++;
                }
                m_chunk_size[0] = num_segments;
                m_num_chunks    = 1;
            }
            else
            {
                //Loop through segment, assigning each to a chunk
                foreach (int segmentIndex in level.EnumerateAliveSegmentIndices())
                {
                    var segmentData = level.segment[segmentIndex];
                    num_segments++;

                    //Count the number of portals
                    int num_portals = 0;
                    for (int sideIdx = 0; sideIdx < 6; ++sideIdx)
                    {
                        if (segmentData.neighbor[sideIdx] != -1)
                        {
                            num_portals++;
                        }
                    }

                    //Figure out what kind of segment; default to complex
                    SegmentTypes[segmentIndex] = SegmentType.Complex;
                    if (num_portals == 1)
                    {
                        SegmentTypes[segmentIndex] = SegmentType.Single;
                    }
                    else if (num_portals == 2)
                    {
                        //If two portals, see if they're opposite
                        if (((segmentData.neighbor[0] != -1) && (segmentData.neighbor[2] != -1)) ||
                            ((segmentData.neighbor[1] != -1) && (segmentData.neighbor[3] != -1)) ||
                            ((segmentData.neighbor[4] != -1) && (segmentData.neighbor[5] != -1)))
                        {
                            //Ok, we've got two opposite sides.
                            SegmentTypes[segmentIndex] = SegmentType.Connector;
                        }
                    }

                    //Now decide what to do with this segment
                    int chunk_num = -1;
                    for (int sideIdx = 0; sideIdx < 6; ++sideIdx)
                    {
                        if (segmentData.neighbor[sideIdx] != -1)
                        {
                            int other = segmentData.neighbor[sideIdx];

                            //Only look at lower-numbered segments, which will already have type set
                            if (other < segmentIndex)
                            {
                                //Join with the other if it's the same as us, or if we're a signel or it's a single
                                if ((SegmentTypes[other] == SegmentTypes[segmentIndex]) || (SegmentTypes[segmentIndex] == SegmentType.Single) || (SegmentTypes[other] == SegmentType.Single))
                                {
                                    int other_chunk_num = level.segment[other].m_chunk_num;

                                    //We should join with the other segment.  See if we're already in a chunk
                                    if (chunk_num == -1)
                                    {
                                        //We're not in a chunk, so join with the other
                                        chunk_num = other_chunk_num;
                                    }
                                    else
                                    {
                                        //We're already in a chunk, so move all segments from other chunk to our chunk
                                        for (int t = 0; t < segmentIndex; t++)
                                        {
                                            if (level.segment[t].Alive)
                                            {
                                                if (level.segment[t].m_chunk_num == other_chunk_num)
                                                {
                                                    level.segment[t].m_chunk_num = chunk_num;
                                                    m_chunk_size[other_chunk_num]--;
                                                    m_chunk_size[chunk_num]++;
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }

                    //If we didn't find a chunk to join, start a new one
                    if (chunk_num == -1)
                    {
                        chunk_num = m_num_chunks++;
                    }

                    segmentData.m_chunk_num = chunk_num;
                    m_chunk_size[chunk_num]++;
                }

                //Now, go through and split up any overly big connector chunks
                //Sorry that this code is so ugly and hard to follow
                for (int chunknum = 0; chunknum < m_num_chunks; chunknum++)
                {
                    //If more than five, split
                    if (m_chunk_size[chunknum] > MAX_CONNECTOR_CHUNK_SIZE)
                    {
                        //Determine if this is a connector chunk, and if so find terminal segment
                        Segment seg = null;
                        int     segnum, sidenum = -1, other_segnum = -1;
                        for (segnum = 0; segnum < Level.MAX_SEGMENTS; segnum++)
                        {
                            seg = level.segment[segnum];
                            if (seg.Alive && (seg.m_chunk_num == chunknum))
                            {
                                //If this is a complex chunk, bail
                                if (SegmentTypes[segnum] == SegmentType.Complex)
                                {
                                    break;
                                }

                                //It's a connector; see if this one is the terminus
                                if (SegmentTypes[segnum] == SegmentType.Connector)
                                {
                                    for (sidenum = 0; sidenum < 6; sidenum++)
                                    {
                                        other_segnum = seg.neighbor[sidenum];
                                        if (other_segnum != -1)
                                        {
                                            if (SegmentTypes[other_segnum] != SegmentType.Connector)                                                    //Connected to something else, so this is terminus
                                            {
                                                break;
                                            }
                                        }
                                    }
                                }
                                if (sidenum < 6)
                                {
                                    break;
                                }
                            }
                        }
                        if ((segnum < Level.MAX_SEGMENTS) && (SegmentTypes[segnum] == SegmentType.Connector))                             //We've found a terminal connector

                        {
#if OVERLOAD_LEVEL_EDITOR
                            level.editor.AddOutputText("Spitting chunk " + chunknum + "( " + m_chunk_size[chunknum] + " segments)");
#endif
                            int exit_side = FindExitSide(seg, other_segnum);

                            //Compute size of new chunk(s)
                            int num_sub_chunks = (m_chunk_size[chunknum] + MAX_CONNECTOR_CHUNK_SIZE - 1) / MAX_CONNECTOR_CHUNK_SIZE;
                            int new_chunk_size = (m_chunk_size[chunknum] + num_sub_chunks / 2) / num_sub_chunks;

                            //The first batch of segments stay in the old chunk
                            for (int t = new_chunk_size; t > 0; t--)
                            {
                                exit_side = FindExitSide(seg, other_segnum);
                                if (exit_side != -1)
                                {
                                    other_segnum = segnum;
                                    segnum       = seg.neighbor[exit_side];
                                    seg          = level.segment[segnum];
                                }
                            }

                            //Walk through chain until halfway, moving segments to new chunk
                            int new_chunk_num = m_num_chunks++;
                            int moved_count   = 0;

                            while ((exit_side != -1) && ((SegmentTypes[segnum] == SegmentType.Connector) || (SegmentTypes[segnum] == SegmentType.Single)))
                            {
                                seg.m_chunk_num = new_chunk_num;
                                m_chunk_size[chunknum]--;
                                m_chunk_size[new_chunk_num]++;
                                moved_count++;
                                exit_side = FindExitSide(seg, other_segnum);
                                if (exit_side != -1)
                                {
                                    other_segnum = segnum;
                                    segnum       = seg.neighbor[exit_side];
                                    seg          = level.segment[segnum];
                                }
                                if (moved_count == new_chunk_size)
                                {
                                    new_chunk_num = m_num_chunks++;
                                    moved_count   = 0;
                                }
                            }
                        }
                    }
                }
            }             // if EnableAutomaticChunking

            //Get all the manually-added separation planes
            List <SplitPlane> planes = new List <SplitPlane>();
            foreach (int segnum in level.EnumerateAliveSegmentIndices())
            {
                Segment seg = level.segment[segnum];

                for (int sidenum = 0; sidenum < 6; sidenum++)
                {
                    Side side = seg.side[sidenum];
                    if (side.chunk_plane_order != -1)
                    {
                        planes.Add(new SplitPlane(segnum, sidenum, side.chunk_plane_order));
                    }
                }
            }

            //If we have any planes, sort them and do the splits
            if (planes.Count > 0)
            {
                //Sort planes
                planes.Sort((p1, p2) => p1.m_order.CompareTo(p2.m_order));

                //Process the planes in order
                foreach (SplitPlane plane in planes)
                {
                    Segment seg  = level.segment[plane.m_segnum];
                    Side    side = seg.side[plane.m_sidenum];

                    int connected_segnum = seg.neighbor[plane.m_sidenum];

                    //Ok, found one.  Split the chunk this segment is in.
                    int            old_chunk_num = seg.m_chunk_num;
                    int            new_chunk_num = m_num_chunks++;
                    OpenTK.Vector3 sep_normal    = side.FindNormal();
                    OpenTK.Vector3 sep_vert      = level.vertex[side.vert[0]].position;

                    foreach (int checksegnum in level.EnumerateAliveSegmentIndices())
                    {
                        Segment checkseg = level.segment[checksegnum];
                        if (checkseg.m_chunk_num != old_chunk_num)
                        {
                            continue;
                        }

                        if (OpenTK.Vector3.Dot(checkseg.FindCenter() - sep_vert, sep_normal) < 0.0f)
                        {
                            checkseg.m_chunk_num = new_chunk_num;
                            m_chunk_size[old_chunk_num]--;
                            m_chunk_size[new_chunk_num]++;
                        }
                    }

                    //Check to see if either of resulting chunks contain non-connected geometry, and if so add them back to the proper chunk
                    if (connected_segnum != -1)
                    {
                        RejoinDisconnectedChunks(plane.m_segnum, new_chunk_num);
                        RejoinDisconnectedChunks(connected_segnum, old_chunk_num);
                    }
                }
            }

            // Compact the chunks array
            int[] map_old_chunk_to_compact_chunk = new int[m_num_chunks];
            int   compact_chunk_count            = 0;
            for (int chunknum = 0; chunknum < m_num_chunks; ++chunknum)
            {
                if (m_chunk_size[chunknum] == 0)
                {
                    map_old_chunk_to_compact_chunk[chunknum] = -1;
                }
                else
                {
                    map_old_chunk_to_compact_chunk[chunknum] = compact_chunk_count++;
                }
            }

            foreach (int segnum in level.EnumerateAliveSegmentIndices())
            {
                Segment seg = level.segment[segnum];
                System.Diagnostics.Debug.Assert(seg.m_chunk_num >= 0 && seg.m_chunk_num < m_num_chunks);

                int compact_chunk_idx = map_old_chunk_to_compact_chunk[seg.m_chunk_num];
                System.Diagnostics.Debug.Assert(compact_chunk_idx != -1);

                seg.m_chunk_num = compact_chunk_idx;
            }

            level.m_num_chunks = compact_chunk_count;

#if OVERLOAD_LEVEL_EDITOR
            level.editor.AddOutputText("Num segments = " + num_segments + ", num chunks = " + level.m_num_chunks);

            //Generate colors for chunks
            for (int c = 0; c < m_num_chunks; c++)
            {
                level.ChunkColor[c] = System.Drawing.Color.FromArgb(255, Utility.RandomRange(0, 127), Utility.RandomRange(0, 127), Utility.RandomRange(0, 127));
            }
#endif  //OVERLOAD_LEVEL_EDITOR
        }