Beispiel #1
0
        }     //buildRawContour

        /*
         *  以Span为s , direction 为 0 举例,这个函数要寻找的就是s,ds,ns,里面,floor最大值
         *      ds
         *      ns  s
         *
         *  如果direction为1的话
         *          ns ds
         *          s
         *
         */
        private static int getCornerHeight(OpenHeightSpan span, int direction)
        {
            //OpenHeightSpan的floor 就真的是floor了,而不是
            //像 SolidHeightSpan那样对应的其实是顶部
            int            maxFloor = span.floor();
            OpenHeightSpan dSpan    = null;
            //顺时针
            int            directionOffset = NMGenUtility.ClockwiseRotateDir(direction);
            OpenHeightSpan nSpan           = span.getNeighbor(direction);

            if (nSpan != null)
            {
                maxFloor = Math.Max(maxFloor, nSpan.floor());
                dSpan    = nSpan.getNeighbor(directionOffset);
            }

            nSpan = span.getNeighbor(directionOffset);
            if (nSpan != null)
            {
                maxFloor = Math.Max(maxFloor, nSpan.floor());
                if (null == dSpan)
                {
                    dSpan = nSpan.getNeighbor(direction);
                }
            }

            if (dSpan != null)
            {
                maxFloor = Math.Max(maxFloor, dSpan.floor());
            }

            return(maxFloor);
        }
Beispiel #2
0
        /// <summary>
        /// </summary>
        /// <param name="startSpan"></param>
        /// <param name="borderDirection"></param>
        /// <param name="newRegionID"></param>
        private void partialFloodRegion(OpenHeightSpan startSpan,
                                        int borderDirection,
                                        int newRegionID)
        {
            int antiBorderDirection = NMGenUtility.AntiDir(borderDirection);
            int regionID            = startSpan.regionID();

            startSpan.setRegionID(newRegionID);
            startSpan.setDistanceToRegionCore(0);  //???所以这个值 没啥卵用啊,一直都是0的
            mwOpenSpans.Push(startSpan);
            mwBorderDistance.Push(0);

            while (mwOpenSpans.Count > 0)
            {
                OpenHeightSpan span     = mwOpenSpans.Pop();
                int            distance = mwBorderDistance.Pop();

                for (int i = 0; i < 4; ++i)
                {
                    OpenHeightSpan nSpan = span.getNeighbor(i);
                    if (null == nSpan ||
                        nSpan.regionID() != regionID)
                    {
                        continue;
                    }

                    int nDistance = distance;
                    if (i == borderDirection)   //null region所在的方向
                    {
                        //这里是不是应该小于等于0呢?
                        // 以这个距离Border为0作为边界,将大于0那一侧的Span全部变成
                        // 新的Region。然后小于等于0那一侧的作为旧的Region保留下来。
                        if (0 == distance)
                        {
                            continue;
                        }
                        nDistance--;
                    }
                    else if (i == antiBorderDirection)
                    {
                        nDistance++;
                    }

                    //注意上面的if-else,如果都不是这两个方向的Span
                    //会直接被重新设置为新的Region


                    nSpan.setRegionID(newRegionID);
                    nSpan.setDistanceToRegionCore(0);

                    mwOpenSpans.Push(nSpan);
                    mwBorderDistance.Push(nDistance);
                }
            }
        }
Beispiel #3
0
        /// <summary>
        /// 重新设置对应的Region,根据拐点的情况
        /// </summary>
        /// <param name="referenceSpan"></param>
        /// <param name="borderDirection"></param>
        /// <param name="cornerDirection"></param>
        /// <returns></returns>
        private int selectedRegionID(OpenHeightSpan referenceSpan,
                                     int borderDirection,
                                     int cornerDirection)
        {
            referenceSpan.getDetailedRegionMap(ref mwNeighborRegions, 0);

            /*
             * Initial example state:
             *
             * a - Known region.
             * x - Null region.
             * u - Unknown, not checked yet.
             *
             *     u u u
             *     u a x
             *     u a a
             */

            int antiBorderDirection = NMGenUtility.AntiDir(borderDirection);
            int antiCornerDirection = NMGenUtility.AntiDir(cornerDirection);
            int regionID            = mwNeighborRegions[antiBorderDirection];

            if (regionID == referenceSpan.regionID() ||
                NULL_REGION == regionID)
            {
                /*
                 * The region away from the border is either a null region
                 * or the same region.  So we keep the current region.
                 *
                 *     u u u      u u u
                 *     a a x  or  x a x  <-- Potentially bad, but stuck with it.
                 *     u a a      u a a
                 */
                return(referenceSpan.regionID());
            }

            int potentialRegion = regionID;

            regionID = mwNeighborRegions[antiCornerDirection];
            if (regionID == referenceSpan.regionID() ||
                NULL_REGION == regionID)
            {
                /*
                 * The region opposite from the corner direction is
                 * either a null region or the same region.  So we
                 * keep the current region.
                 *
                 *     u a u      u x u
                 *     b a x  or  b a x
                 *     u a a      u a a
                 */
                return(referenceSpan.regionID());
            }

            int potentialCount = 0;
            int currentCount   = 0;

            for (int i = 0; i < 8; ++i)
            {
                if (mwNeighborRegions[i] == referenceSpan.regionID())
                {
                    currentCount++;
                }
                else if (mwNeighborRegions[i] == potentialRegion)
                {
                    potentialCount++;
                }
            }

            return(potentialCount < currentCount
                ? referenceSpan.regionID()
                : potentialRegion);
        }
Beispiel #4
0
        public void apply(OpenHeightfield field)
        {
            if (null == field)
            {
                Logger.LogError("[CLeanNullRegionBorders][apply]field null");
                return;
            }

            int nextRegionID = field.regionCount();

            OpenHeightfield.OpenHeightFieldIterator iter = field.GetEnumerator();
            while (iter.MoveNext())
            {
                OpenHeightSpan span = iter.Current;
                if (span.flags != 0)
                {
                    continue;
                }

                span.flags = 1;

                OpenHeightSpan workingSpan   = null;
                int            edgeDirection = -1;

                if (NULL_REGION == span.regionID())
                {
                    //找到Border Span第一个非空的邻居
                    edgeDirection = getNonNullBorderDrection(span);
                    if (-1 == edgeDirection)
                    {
                        continue;
                    }

                    //起点Span为有所属Region的Span
                    //而行走方向为 null Region所在的方向
                    workingSpan = span.getNeighbor(edgeDirection);
                    //转180度
                    edgeDirection = NMGenUtility.AntiDir(edgeDirection);
                }
                else if (!mUseOnlyNullSpans)
                {
                    //起点Span为 null Region的Span
                    //而行走方向即为有所属 Region 的 Span

                    edgeDirection = getNullBorderDrection(span);
                    if (-1 == edgeDirection)
                    {
                        continue;
                    }
                    workingSpan = span;
                }
                else
                {
                    continue;
                }

                //上面两个分支都会保证workingSpan是有所属Region的
                //而Dir 即是 Region 为 Null Region的Dir
                bool isEncompassedNullRegion = processNullRegion(workingSpan, edgeDirection);
                if (isEncompassedNullRegion)   //确定以这个Span为起点的Region,是一个单一Region,并且内含了一个Null Region
                {
                    //如果是完全包含了不可走的NullRegion ,就将这个Region分割成两个Region
                    partialFloodRegion(workingSpan, edgeDirection, nextRegionID);
                    nextRegionID++;
                }
            }

            field.setRegionCount(nextRegionID);

            iter.Reset();
            while (iter.MoveNext())
            {
                iter.Current.flags = 0;
            }
        }
Beispiel #5
0
        /// <summary>
        /// 为了防止 Null Region 内含在有效Region内,这阻碍了凸包的生成
        /// </summary>
        /// <param name="startSpan"></param>
        /// <param name="startDirection"></param>
        /// <returns></returns>
        private bool processNullRegion(OpenHeightSpan startSpan, int startDirection)
        {
            /*
             *  这段直接翻译源码的:
             *  这个算法遍历这个轮廓。正如它所做的,这个算法检测并且修复一些危险的
             *  Span Configurations。
             *
             *  遍历轮廓:一个很好的可视化方向就是,想像一个机器人面向一堵墙,并坐
             *  在地板上。它会采取以下措施来绕过这堵墙:
             *  1. 如果有一堵墙位于它的前面,顺时针转90度,直到他前面不再是墙。
             *  2. 向前一步。
             *  3. 逆时针转向90度
             *  4. 重复从1开始的步骤 ,直到它发现自己位于原来的起点,还有原来的朝向。
             *
             */

            /*
             * 算法在遍历的同时,检测锐角(90度) 和 钝角(270)拐点。如果
             * 一个完整的轮廓被检测完整,并且 钝角拐点比锐角拐点多。那
             * 么 null Region 就在这个轮廓里面。否则就在轮廓外面。
             *
             */


            //环绕null region 走一圈,最后走回自己的起点
            int borderRegionID = startSpan.regionID();

            OpenHeightSpan span  = startSpan;
            OpenHeightSpan nSpan = null;
            int            dir   = startDirection;

            int  loopCount          = 0;
            int  acuteCornerCount   = 0;
            int  obtuseCornerCount  = 0;
            int  stepsWithoutBorder = 0;
            bool borderSeenLastLoop = false;
            bool isBorder           = true;

            bool hasSingleConnection = true;

            while (++loopCount < int.MaxValue)
            {
                //初始方向就是面向的Null Region,所以一开始是肯定是isBorder的
                nSpan = span.getNeighbor(dir);
                if (null == nSpan)
                {
                    isBorder = true;
                }
                else
                {
                    nSpan.flags = 1;
                    if (NULL_REGION == nSpan.regionID())
                    {
                        isBorder = true;
                    }
                    else
                    {
                        isBorder = false;
                        if (nSpan.regionID() != borderRegionID)
                        {
                            hasSingleConnection = false;
                        }
                    }
                } // else


                if (isBorder)
                {
                    if (borderSeenLastLoop)
                    {
                        /*
                         *  a x
                         *  x x
                         */

                        //其实这个应用用inner来描述更加准确 ,表明a被x包围了
                        acuteCornerCount++;
                    }
                    else if (stepsWithoutBorder > 1)
                    {
                        /*
                         *  a a
                         *  a x
                         *
                         */
                        obtuseCornerCount++;  //相对地,我觉得这个应该用outer来描述更加准确,表明a正在包围x
                        stepsWithoutBorder = 0;

                        //处理钝角的各种异常情况
                        if (processOuterCorner(span, dir))
                        {
                            hasSingleConnection = false;
                        }
                    }

                    dir = NMGenUtility.ClockwiseRotateDir(dir);
                    borderSeenLastLoop = true;
                    stepsWithoutBorder = 0;
                }
                else   //注意,不是边界,才会进行移动,如果是边界的话,只会进行转向
                {
                    span = nSpan;
                    dir  = NMGenUtility.CClockwiseRotateDir(dir); //逆时针转向一下
                    borderSeenLastLoop = false;
                    stepsWithoutBorder++;
                } // else

                //回到原方位了
                if (startSpan == span &&
                    startDirection == dir)
                {
                    return(hasSingleConnection &&
                           obtuseCornerCount > acuteCornerCount);
                }
            } // while

            return(false);
        }
Beispiel #6
0
        //遍历轮廓,找邻接的Region
        private static void findRegionConnections(OpenHeightSpan startSpan,
                                                  int startDirection,
                                                  ref List <int> outConnections)
        {
            OpenHeightSpan span             = startSpan;
            int            dir              = startDirection;
            int            lastEdgeRegionID = NULL_REGION;

            OpenHeightSpan nSpan = span.getNeighbor(dir);

            if (nSpan != null)
            {
                lastEdgeRegionID = nSpan.regionID();
            }

            //连接的Region,至少存在一个NULL_REGION
            outConnections.Add(lastEdgeRegionID);

            int loopCount = 0;

            while (++loopCount < ushort.MaxValue)
            {
                nSpan = span.getNeighbor(dir);
                int currEdgeRegionID = NULL_REGION;   //默认nSpan是null的
                if (null == nSpan ||
                    nSpan.regionID() != span.regionID())
                {
                    if (nSpan != null)
                    {
                        currEdgeRegionID = nSpan.regionID();
                    }
                    if (currEdgeRegionID != lastEdgeRegionID)
                    {
                        outConnections.Add(currEdgeRegionID);
                        lastEdgeRegionID = currEdgeRegionID;
                    }

                    //顺时针转向下一个
                    dir = NMGenUtility.ClockwiseRotateDir(dir);
                }
                else
                {
                    //这个分支代表 Region是相同的
                    span = nSpan;
                    //逆时针转一下
                    dir = NMGenUtility.CClockwiseRotateDir(dir);
                }

                if (startSpan == span &&
                    startDirection == dir)
                {
                    break;
                }
            } //while

            //TODO 为啥会存在首尾相同呢?因为退出条件是原来的那个点么
            int connectionsCnt = outConnections.Count;

            if (connectionsCnt > 1 &&
                outConnections[0] == outConnections[connectionsCnt - 1])
            {
                outConnections.RemoveAt(connectionsCnt - 1);
            }
        }
Beispiel #7
0
        private void buildRawContours(OpenHeightSpan startSpan,
                                      int startWidthIndex,
                                      int startDepthIndex,
                                      int startDirection,
                                      ref List <int> outContourVerts
                                      )
        {
            OpenHeightSpan span      = startSpan;
            int            dir       = startDirection;
            int            spanX     = startWidthIndex;
            int            spanZ     = startDepthIndex;
            int            loopCount = 0;

            while (++loopCount < ushort.MaxValue)
            {
                //这个Edge的一个Span
                if (!isSameRegion(span, dir))
                {
                    //span
                    int px = spanX;
                    int py = getCornerHeight(span, dir);
                    int pz = spanZ;

                    /*
                     * 这里取的是点,所以需要做这些偏移来记录点,而不是记录对应的Span
                     * 这里需要结合 :  方向 + 某个点 ,来理解 为什么要加这个偏移
                     *
                     *
                     *     * --- *
                     *     |  S  |
                     *     ----—-*
                     *
                     *    dir = 0 的时候,需要取左上角的点
                     *    dir = 1 的时候,需要取右上角的点
                     *    dir = 2 的时候,需要取右下角的点
                     *    dir = 3 的时候,取的就是参考点
                     *
                     *    以左下角的点为参考点,就是 dir方向对应的顺时针的点
                     *
                     */


                    /*
                     * Update the px and pz values based on current direction.
                     * The update is such that the corner being represented is
                     * clockwise from the edge the direction is currently pointing
                     * toward.
                     */

                    switch (dir)
                    {
                    case 0: pz++; break;

                    case 1: px++; pz++; break;

                    case 2: px++; break;
                    }

                    int            regionThisDirection = NULL_REGION;
                    OpenHeightSpan nSpan = span.getNeighbor(dir);
                    if (nSpan != null)
                    {
                        regionThisDirection = nSpan.regionID();
                    }

                    //这是轮廓的点
                    outContourVerts.Add(px);
                    outContourVerts.Add(py);
                    outContourVerts.Add(pz);
                    outContourVerts.Add(regionThisDirection);

                    span.flags &= ~(1 << dir);  //清除dir对应的位
                    dir         = NMGenUtility.ClockwiseRotateDir(dir);
                } //isSameRegion
                else
                {
                    //这段就是步进到下一个同Region的Span,很好理解。
                    span = span.getNeighbor(dir);
                    switch (dir)
                    {
                    case 0: spanX--; break;

                    case 1: spanZ++; break;

                    case 2: spanX++; break;

                    case 3: spanZ--; break;
                    }
                    dir = NMGenUtility.CClockwiseRotateDir(dir);
                } // no the SameRegion

                if (span == startSpan &&
                    dir == startDirection)
                {
                    break;
                }
            } //while
        }     //buildRawContour