Ejemplo n.º 1
0
    void Generate()
    {
        var     area     = Area;
        Vector2 position = transform.position;

        var calc = new VoronoiCalculator();
        var clip = new VoronoiClipper();

        var sites = new Vector2[maxNumberOfPieces];

        for (int i = 0; i < sites.Length; i++)
        {
            var dist  = Mathf.Abs(NormalizedRandom(0.0f, 8f));
            var angle = 2.0f * Mathf.PI * Random.value;

            sites[i] = position + new Vector2(
                dist * Mathf.Cos(angle),
                dist * Mathf.Sin(angle));
        }

        var diagram = calc.CalculateDiagram(sites);

        var clipped = new List <Vector2>();

        for (int i = 0; i < sites.Length; i++)
        {
            clip.ClipSite(diagram, Polygon, i, ref clipped);

            if (clipped.Count > 0)
            {
                var newGo = Instantiate(gameObject, transform.parent);

                newGo.transform.localPosition = transform.localPosition;
                newGo.transform.localRotation = transform.localRotation;

                var cdGenerator = newGo.GetComponent <CityDistrictsGenerator>();

                cdGenerator.shouldGenerate = false;

                cdGenerator.Thickness = Thickness;
                cdGenerator.Polygon.Clear();
                cdGenerator.Polygon.AddRange(clipped);

                var grArea = newGo.GetComponent <GroundArea>();

                grArea.props.name = "Area " + i;
                // Coloca una facción aleatoria a cada area creada desde un array de facciones (ScriptableObjects)
                grArea.props.faction = factionList[Random.Range(0, factionList.Length)];

                var childArea = cdGenerator.Area;
            }
        }

        gameObject.SetActive(false);
        Destroy(gameObject);
    }
Ejemplo n.º 2
0
    /// <summary>
    /// Given a triangle, subdivide it in multiple polygons using Voronoi diagrams.
    /// </summary>
    /// <param name="v0">First vertex of the triangle.</param>
    /// <param name="v1">Second vertex of the triangle.</param>
    /// <param name="v2">Third vertex of the triangle.</param>
    /// <param name="fragmentsProperties">Properties of the resulted fragments.</param>
    /// <returns>A list with all generated fragments' meshes. If the function failes to generate
    /// a fragment, it will the site of the fragment as a placeholder.</returns>
    private static List <GameObject> GenerateVoronoiFragments(
        Vertex v0, Vertex v1, Vertex v2, FragmentsProperties fragmentsProperties, bool areaInsteadOfSites = false)
    {
        if (fragmentsProperties == null)
        {
            throw new System.ArgumentNullException("fragmentsProperties");
        }

        float area = 0.5f * Vector3.Cross(v0.position - v1.position, v0.position - v2.position).magnitude;
        //Debug.Log($"Area {area}");
        //Debug.Log($"Max Area: {fragmentsProperties.maxArea}");

        // The Voronoi API suffers from innacurate floating-point approximations and provides erroneous
        // results when dealing with small numbers. A current solution to this problem is to upscale
        // the whole mesh, do the calculations and then downscale the result to the original scale.
        Vector3 scale = new Vector3(
            Constants.VoronoiScale, Constants.VoronoiScale, Constants.VoronoiScale
            );
        Vector3 reverseScale = new Vector3(
            1.0f / Constants.VoronoiScale, 1.0f / Constants.VoronoiScale, 1.0f / Constants.VoronoiScale
            );

        v0.position.Scale(scale);
        v1.position.Scale(scale);
        v2.position.Scale(scale);

        // The Voronoi API requires 2D points while the trianlge is in 3D space. In order to reduce
        // one dimension, all points must have the same z coordinate. Thus, rotate the triangle such
        // that its normal will become (0, 0, 1).
        Vector3    center          = (v0.position + v1.position + v2.position) / 3.0f;
        Vector3    triangleNorm    = Vector3.Cross(v1.position - v0.position, v2.position - v0.position).normalized;
        Quaternion rotation        = Quaternion.FromToRotation(triangleNorm, Vector3.forward);
        Quaternion reverseRotation = Quaternion.FromToRotation(Vector3.forward, triangleNorm);
        Vector3    p0rot           = (rotation * (v0.position - center) + center);
        Vector3    p1rot           = (rotation * (v1.position - center) + center);
        Vector3    p2rot           = (rotation * (v2.position - center) + center);
        float      z = p0rot.z;

        // Try to make all fragments have similar area
        int sitesNum = areaInsteadOfSites ? Math.Max(3, (int)(area / fragmentsProperties.maxArea)) : fragmentsProperties.sitesPerTriangle;//Math.Max(3, (int)(area / fragmentsProperties.maxArea));

        //Debug.Log($"Sites num: {sitesNum}");
        Vector2[] sites = new Vector2[sitesNum];

        // Generate random points inside the triangle. These will be the sites passed to the Voronoi API.
        for (int i = 0; i < sites.Length; ++i)
        {
            Vector3 p         = Vector3.zero;
            int     triesLeft = 50;
            while (triesLeft > 0)
            {
                float r1 = Random.Range(0.1f, 0.9f);
                float r2 = Random.Range(0.1f, 0.9f);

                p = (float)(1.0f - Math.Sqrt(r1)) * p0rot +
                    (float)(Math.Sqrt(r1) * (1.0f - r2)) * p1rot +
                    (float)(r2 * Math.Sqrt(r1)) * p2rot;
                if (PointInTriangle(p0rot, p1rot, p2rot, p))
                {
                    break;
                }
                Debug.Log($"Bad point {p:F5}\n" +
                          $" inside triangle, ({p0rot:F5}, {p1rot:F5}, {p2rot:F5})\n" +
                          $"{triesLeft} tries left");
                --triesLeft;
            }
            sites[i] = p;
        }

        // Calculate the Voronoi diagram containing the given sites
        VoronoiCalculator calc    = new VoronoiCalculator();
        VoronoiClipper    clip    = new VoronoiClipper();
        VoronoiDiagram    diagram = calc.CalculateDiagram(sites);

        Vector2[] triangleClipper = new Vector2[3]
        {
            new Vector2(p0rot.x, p0rot.y),
            new Vector2(p1rot.x, p1rot.y),
            new Vector2(p2rot.x, p2rot.y)
        };
        List <Vector2>    clipped   = new List <Vector2>();
        List <GameObject> fragments = new List <GameObject>(sites.Length);

        // Generate a mesh for each site's polygon
        for (int i = 0; i < sites.Length; ++i)
        {
            clipped.Clear();
            clip.ClipSite(diagram, triangleClipper, i, ref clipped);

            if (clipped.Count > 0)
            {
                // Rotate the points back to their original rotation
                Vector3[] originalSpacePoints = new Vector3[clipped.Count];
                for (int j = 0; j < clipped.Count; ++j)
                {
                    Vector3 v = new Vector3(clipped[j].x, clipped[j].y, z);
                    originalSpacePoints[j] = reverseRotation * (v - center) + center;
                }

                Vertex[] vertices = CalculateNormalsAndUVs(v0, v1, v2, originalSpacePoints);

                // Revert the upscaling and scale to original object scale
                for (int j = 0; j < clipped.Count; ++j)
                {
                    vertices[j].position.Scale(reverseScale);
                }

                float thickness = Random.Range(fragmentsProperties.minThickness, fragmentsProperties.maxThickness);
                fragments.Add(PolygonToFrustum(vertices, thickness));
            }
        }
        return(fragments);
    }