在统一3d填充多边形

时间:2014-09-16 02:52:17

标签: unity3d unityscript

我在团结2d画手册时遇到了一些问题。 我使用列表向量绘制多边形,但我无法填充它。 我还阅读了本教程:http://forum.unity3d.com/threads/draw-polygon.54092/ 但似乎我需要将多边形转换为三角形。(因为我的多边形很复杂,所以转换为三角形很难。我需要使用一些算法,比如Ear clip ...)。

请帮我填写一个简单的方法。 (我认为团结是游戏引擎的顶级,然后有一些方法可以做到最简单)。

非常感谢。

2 个答案:

答案 0 :(得分:0)

你很难转换为网格以使填充工作...... GPU(着色器)只能填充三角形的内部空间......如果你使用闭合的凸多边形,这相当容易。具有凹面部分的多边形将需要更复杂的算法来转换为网格,但似乎您已经对该主题进行了一些研究(您提到了耳朵剪裁)。

祝你将多边形列表实现为三角算法,祝你好运:)

答案 1 :(得分:0)

我可以提供Poisson-Disc algorithm重塑UniformPoissonDiskSampler.cs,例如:

using System;
using System.Collections.Generic;
using UnityEngine;

namespace AwesomeNamespace
{
    public static class UniformPoissonDiskSampler
    {
        public const int DefaultPointsPerIteration = 30;

        static readonly float SquareRootTwo = (float)Math.Sqrt(2);

        struct Settings
        {
            public UnityEngine.Vector2 TopLeft, LowerRight, Center;
            public UnityEngine.Vector2 Dimensions;
            public float? RejectionSqDistance;
            public float MinimumDistance;
            public float CellSize;
            public int GridWidth, GridHeight;
        }

        struct State
        {
            public UnityEngine.Vector2?[,] Grid;
            public List<UnityEngine.Vector2> ActivePoints, Points;
        }

        public static List<UnityEngine.Vector2> SampleCircle(UnityEngine.Vector2 center, float radius, float minimumDistance)
        {
            return SampleCircle(center, radius, minimumDistance, DefaultPointsPerIteration);
        }
        public static List<UnityEngine.Vector2> SampleCircle(UnityEngine.Vector2 center, float radius, float minimumDistance, int pointsPerIteration)
        {
            return Sample(center - new UnityEngine.Vector2(radius, radius), center + new UnityEngine.Vector2(radius, radius), radius, minimumDistance, pointsPerIteration, null);
        }

        public static List<UnityEngine.Vector2> SampleRectangle(UnityEngine.Vector2 topLeft, UnityEngine.Vector2 lowerRight, float minimumDistance)
        {
            return SampleRectangle(topLeft, lowerRight, minimumDistance, DefaultPointsPerIteration);
        }
        public static List<UnityEngine.Vector2> SampleRectangle(UnityEngine.Vector2 topLeft, UnityEngine.Vector2 lowerRight, float minimumDistance, int pointsPerIteration)
        {
            return Sample(topLeft, lowerRight, null, minimumDistance, pointsPerIteration, null);
        }

        public static List<UnityEngine.Vector2> SamplePolygon(UnityEditor.Experimental.TerrainAPI.Processing.InMetric metric, float minimumDistance)
        {
            return Sample(null, null, null, minimumDistance, DefaultPointsPerIteration, metric);
        }

        static List<UnityEngine.Vector2> Sample(UnityEngine.Vector2? topLeft, UnityEngine.Vector2? lowerRight, float? rejectionDistance, float minimumDistance, int pointsPerIteration, UnityEditor.Experimental.TerrainAPI.Processing.InMetric metric = null)
        {
            if (!topLeft.HasValue && !lowerRight.HasValue && metric != null)
            {
                topLeft = new Vector2(metric.minpointx, metric.minpointz);
                lowerRight = new Vector2(metric.maxpointx, metric.maxpointz);
            }
            var settings = new Settings
            {
                TopLeft = (Vector2)topLeft,
                LowerRight = (Vector2)lowerRight,
                Dimensions = (Vector2)lowerRight - (Vector2)topLeft,
                Center = ((Vector2)topLeft + (Vector2)lowerRight) / 2,
                CellSize = minimumDistance / SquareRootTwo,
                MinimumDistance = minimumDistance,
                RejectionSqDistance = rejectionDistance == null ? null : rejectionDistance * rejectionDistance
            };
            settings.GridWidth = (int)(settings.Dimensions.x / settings.CellSize) + 1;
            settings.GridHeight = (int)(settings.Dimensions.y / settings.CellSize) + 1;
            // Debug.Log("settings.GridWidth"+settings.GridWidth+"settings.GridHeight"+settings.GridHeight);
            var state = new State
            {
                Grid = new UnityEngine.Vector2?[settings.GridWidth, settings.GridHeight],
                ActivePoints = new List<UnityEngine.Vector2>(),
                Points = new List<UnityEngine.Vector2>()
            };

            AddFirstPoint(ref settings, ref state, (metric == null) ? null : metric);

            while (state.ActivePoints.Count != 0)
            {
                var listIndex = RandomHelper.Random.Next(state.ActivePoints.Count);

                var point = state.ActivePoints[listIndex];
                var found = false;

                for (var k = 0; k < pointsPerIteration; k++)
                    found |= AddNextPoint(point, ref settings, ref state, (metric == null) ? null : metric);

                if (!found)
                    state.ActivePoints.RemoveAt(listIndex);
            }

            return state.Points;
        }

        static void AddFirstPoint(ref Settings settings,
                                    ref State state,
                                    UnityEditor.Experimental.TerrainAPI.Processing.InMetric metric = null)
        {
            var added = false;
            while (!added)
            {
                var d = RandomHelper.Random.NextDouble();
                var xr = settings.TopLeft.x + settings.Dimensions.x * d;

                d = RandomHelper.Random.NextDouble();
                var yr = settings.TopLeft.y + settings.Dimensions.y * d;

                var p = new UnityEngine.Vector2((float)xr, (float)yr);
                if (settings.RejectionSqDistance != null && DistanceSquared(settings.Center, p) > settings.RejectionSqDistance)
                    continue;
                added = true;

                if (UnityEditor.Experimental.TerrainAPI.Processing.figures_Included(p.x, p.y, metric.metricIn, metric.count) == true)
                {
                    var index = Denormalize(p, settings.TopLeft, settings.CellSize);

                    state.Grid[(int)index.x, (int)index.y] = p;

                    state.ActivePoints.Add(p);
                    state.Points.Add(p);
                }
                else
                {
                    AddFirstPoint(ref settings, ref state, metric);
                }
            }
        }
        static float DistanceSquared(Vector2 A, Vector2 B)
        {
            return (float)Math.Pow(Math.Sqrt(Math.Pow((A.x - B.x), 2) + Math.Pow((A.y - B.y), 2)), 2);
        }

        static bool AddNextPoint(UnityEngine.Vector2 point,
                                ref Settings settings,
                                ref State state,
                                UnityEditor.Experimental.TerrainAPI.Processing.InMetric metric = null)
        {
            var found = false;
            var q = GenerateRandomAround(point, settings.MinimumDistance);
            if (metric != null)
            {
                if (UnityEditor.Experimental.TerrainAPI.Processing.figures_Included(q.x, q.y, metric.metricIn, metric.count) == true &&
                    q.x >= settings.TopLeft.x && q.x < settings.LowerRight.x &&
                    q.y > settings.TopLeft.y && q.y < settings.LowerRight.y &&
                    (settings.RejectionSqDistance == null || DistanceSquared(settings.Center, q) <= settings.RejectionSqDistance))
                {
                    var qIndex = Denormalize(q, settings.TopLeft, settings.CellSize);
                    var tooClose = false;

                    for (var i = (int)Math.Max(0, qIndex.x - 2); i < Math.Min(settings.GridWidth, qIndex.x + 3) && !tooClose; i++)
                        for (var j = (int)Math.Max(0, qIndex.y - 2); j < Math.Min(settings.GridHeight, qIndex.y + 3) && !tooClose; j++)
                            if (state.Grid[i, j].HasValue && Vector2.Distance(state.Grid[i, j].Value, q) < settings.MinimumDistance)
                                tooClose = true;

                    if (!tooClose)
                    {
                        found = true;
                        state.ActivePoints.Add(q);
                        state.Points.Add(q);
                        state.Grid[(int)qIndex.x, (int)qIndex.y] = q;
                    }
                }
            }
            else
            {
                if (q.x >= settings.TopLeft.x && q.x < settings.LowerRight.x &&
                    q.y > settings.TopLeft.y && q.y < settings.LowerRight.y &&
                    (settings.RejectionSqDistance == null || DistanceSquared(settings.Center, q) <= settings.RejectionSqDistance))
                {
                    var qIndex = Denormalize(q, settings.TopLeft, settings.CellSize);
                    var tooClose = false;

                    for (var i = (int)Math.Max(0, qIndex.x - 2); i < Math.Min(settings.GridWidth, qIndex.x + 3) && !tooClose; i++)
                        for (var j = (int)Math.Max(0, qIndex.y - 2); j < Math.Min(settings.GridHeight, qIndex.y + 3) && !tooClose; j++)
                            if (state.Grid[i, j].HasValue && Vector2.Distance(state.Grid[i, j].Value, q) < settings.MinimumDistance)
                                tooClose = true;

                    if (!tooClose)
                    {
                        found = true;
                        state.ActivePoints.Add(q);
                        state.Points.Add(q);
                        state.Grid[(int)qIndex.x, (int)qIndex.y] = q;
                    }
                }
            }
            return found;
        }

        static Vector2 GenerateRandomAround(Vector2 center, float minimumDistance)
        {
            var d = RandomHelper.Random.NextDouble();
            var radius = minimumDistance + minimumDistance * d;

            d = RandomHelper.Random.NextDouble();
            var angle = MathHelper.TwoPi * d;

            var newX = radius * Math.Sin(angle);
            var newY = radius * Math.Cos(angle);

            return new Vector2((float)(center.x + newX), (float)(center.y + newY));
        }

        static Vector2 Denormalize(Vector2 point, Vector2 origin, double cellSize)
        {
            return new Vector2((int)((point.x - origin.x) / cellSize), (int)((point.y - origin.y) / cellSize));
        }
    }

    public static class RandomHelper
    {
        public static readonly System.Random Random = new System.Random();
    }

    public static class MathHelper
    {
        public const float Pi = (float)Math.PI;
        public const float HalfPi = (float)(Math.PI / 2);
        public const float TwoPi = (float)(Math.PI * 2);
    }
}

人物包含:

public static bool figures_Included(float xPoint, float yPoint, float[] metricIn, int n)
{
    float X = xPoint;
    float Y = yPoint;
    int npol = n;
    int i, j;
    bool res = false;
    float[] XYpol = metricIn;
    for (i = 0, j = npol - 1; i < npol; j = i++)
    {
        if ((((XYpol[i * 2 + 1] <= Y) && (Y < XYpol[j * 2 + 1])) ||
                ((XYpol[j * 2 + 1] <= Y) && (Y < XYpol[i * 2 + 1]))) &&
            (X < (XYpol[j * 2] - XYpol[i * 2]) * (Y - XYpol[i * 2 + 1]) /
                (XYpol[j * 2 + 1] - XYpol[i * 2 + 1]) + XYpol[i * 2]))
        {
            res = !res;
        }
    }
    return res;
}

和InMetric:

    static public InMetric getmetricIn(List<Vector3> drawcoord, bool editingmode = true)
    {
        float mapoffsetx = 0;
        float mapoffsety = 0;
        if (editingmode == true)
        {
            mapoffsetx = Main.mainSatting.mapoffsetx;
            mapoffsety = Main.mainSatting.mapoffsetz;
        }
        else
        {
            mapoffsetx = 0;
            mapoffsety = 0;
        }

        if (drawcoord[0].x != drawcoord[drawcoord.Count - 1].x && drawcoord[0].z != drawcoord[drawcoord.Count - 1].z) //если линия, ограничивающая полигон не замкнута
            drawcoord.Add(drawcoord[0]); //добавляем замыкающую вершину

        float[] metricIn = new float[drawcoord.Count * 2]; //дополнительный массив вершин, пересчитанный для проверки нахождения точки внутри полигона            
        drawcoord[0] = new Vector3(drawcoord[0].x - mapoffsetx, 0, drawcoord[0].z - mapoffsety); //расчет 0-ой вершины в единицах Unity (метры)
        metricIn[0] = drawcoord[0].x;
        metricIn[1] = drawcoord[0].z; //запись 0-ой вершины в дополнительный массив. x-координаты под четными индексами, Z-координаты под нечетными индексами
        float minpointx = drawcoord[0].x; //минимальная x-координата
        float maxpointx = drawcoord[0].x; //максимальная х-координата
        float minpointz = drawcoord[0].z; //минимальная y-координата
        float maxpointz = drawcoord[0].z; //максимальная у-координата

        /*Цикл обработки вершин. начинается 1-ой вершины*/
        for (int i = 1; i < drawcoord.Count; i++)
        {
            drawcoord[i] = new Vector3(drawcoord[i].x - mapoffsetx, 0, drawcoord[i].z - mapoffsety); //расчет i-ой вершины в единицах Unity (метры)
            metricIn[i * 2] = drawcoord[i].x; //запись i-ой вершины в дополнительный массив. x-координаты под четными индексами
            metricIn[i * 2 + 1] = drawcoord[i].z; //запись i-ой вершины в дополнительный массив. z-координаты под нечетными индексами
            /*поиск максимальных и минимальных координат по x и максимальных и минимальных координат по z*/
            if (drawcoord[i].x < minpointx)
                minpointx = drawcoord[i].x;
            if (drawcoord[i].x > maxpointx)
                maxpointx = drawcoord[i].x;
            if (drawcoord[i].z < minpointz)
                minpointz = drawcoord[i].z;
            if (drawcoord[i].z > maxpointz)
                maxpointz = drawcoord[i].z;
        }
        InMetric metric = new InMetric();
        metric.metricIn = metricIn;
        metric.minpointx = minpointx;
        metric.maxpointx = maxpointx;
        metric.minpointz = minpointz;
        metric.maxpointz = maxpointz;
        metric.drawcoord = drawcoord;
        metric.count = drawcoord.Count;
        return metric;
    }

    public class InMetric
    {
        public float minpointx { get; set; }
        public float maxpointx { get; set; }
        public float minpointz { get; set; }
        public float maxpointz { get; set; }
        public float[] metricIn { get; set; }
        public List<Vector3> drawcoord { get; set; }
        public int count { get; set; }
    }