Пример #1
0
    private int RandomMeshId(ShrineGenerationConfig config, bool isEnd)
    {
        int res = 0;

        for (int i = 0; i < triangles.Length - 1; i++)
        {
            if (Random.value < (isEnd ? config.AccentChanceEnd : config.AccentChanceMid))
            {
                res++;
            }
            else
            {
                return(res);
            }
        }
        return(res);
    }
Пример #2
0
    public Bounds GenerateShrine(ShrineGenerationConfig config, int seed, GameObject targetObject)
    {
        var oldState = Random.state;

        Random.InitState(seed);

        // start of setup stage

        // choose symmetry before anything else so that it can be shared by other generators
        int rotationalSymmetry = config.RotationalSymmetry.RandomValue();

        bool bilateralSymmetry = false;

        // create seeds to isolate static generation of various stages
        int plinthSeed = Random.Range(int.MinValue, int.MaxValue);
        int mainSeed   = Random.Range(int.MinValue, int.MaxValue);
        int capSeed    = Random.Range(int.MinValue, int.MaxValue);

        var targetMeshRenderer = targetObject.GetComponent <MeshRenderer>();

        vertices  = new List <Vector3>();
        triangles = new List <int> [targetMeshRenderer.sharedMaterials.Length];
        for (int i = 0; i < triangles.Length; i++)
        {
            triangles[i] = new List <int>();
        }

        float sectionAngle = (Mathf.PI * 2) / rotationalSymmetry;
        float faceAngle    = bilateralSymmetry ? sectionAngle * 0.5f : sectionAngle;
        float currentAngle = 0;

        float twistAngle = 0;

        if (Random.value < config.GlobalTwistChance)
        {
            twistAngle = config.TwistAngle.RandomValue() * (Random.value > 0.5 ? 1 : -1);
        }
        //Debug.Log($"twistAngle {twistAngle}");

        float tierRadius = 0;
        float tierHeight = 0;
        float topY       = 0;

        Vector3 lr = Vector3.zero;
        Vector3 ll = Vector3.zero;
        Vector3 ul = Vector3.zero;
        Vector3 ur = Vector3.zero;

        // end of setup stage, start of plinth stage

        Random.InitState(plinthSeed);

        currentMeshId = 0;

        tierRadius = config.PlinthBaseRadius.RandomValue();

        ul = RadialPoint(currentAngle, tierRadius, topY);
        ur = RadialPoint(currentAngle + faceAngle, tierRadius, topY);

        float tierRadiusFactor = config.PlinthRadiusFactor.RandomValue();

        tierHeight = config.PlinthTierHeight.RandomValue();
        int plinthTierCount = config.PlinthTierCount.RandomValue();

        for (var pti = 0; pti < plinthTierCount; pti++)
        {
            // add vertical face

            topY += tierHeight;

            lr = ur;
            ll = ul;

            ul = RadialPoint(currentAngle, tierRadius, topY);
            ur = RadialPoint(currentAngle + faceAngle, tierRadius, topY);

            AddFace(new Vector3[] { ul, ur, lr, ll });

            // add horizontal face
            if (pti < plinthTierCount - 1)
            {
                if (twistAngle != 0)
                {
                    // find the minimum radius with twists
                    float apotheum    = tierRadius * Mathf.Cos(Mathf.PI / rotationalSymmetry);
                    float innerAngle  = Mathf.PI / rotationalSymmetry - Mathf.Abs(twistAngle);
                    float radiusRatio = 1 / Mathf.Cos(innerAngle);
                    float minRadius   = apotheum * radiusRatio;
                    tierRadius = Mathf.Min(minRadius, tierRadius * tierRadiusFactor);

                    currentAngle += twistAngle;
                }
                else
                {
                    tierRadius *= tierRadiusFactor;
                }

                lr = ur;
                ll = ul;

                ul = RadialPoint(currentAngle, tierRadius, topY);
                ur = RadialPoint(currentAngle + faceAngle, tierRadius, topY);

                AddFace(new Vector3[] { ul, ur, lr, ll });
            }
        }

        ul = RadialPoint(currentAngle, tierRadius, topY);
        ur = RadialPoint(currentAngle + faceAngle, tierRadius, topY);

        Vector3 plinthTopVertex = new Vector3(0, topY, 0);

        AddFace(new Vector3[] { ur, ul, plinthTopVertex });

        // end of plinth stage, start of main stage

        Random.InitState(mainSeed);

        tierRadius = config.Radius.RandomValue();

        ul = RadialPoint(currentAngle, tierRadius, topY);
        ur = RadialPoint(currentAngle + faceAngle, tierRadius, topY);

        int tierCount = config.TierCount.RandomValue();

        for (var ti = 1; ti < tierCount; ti++)
        {
            bool offsetTier = Random.value < config.OffsetTierChance;

            tierRadius = config.Radius.RandomValue();
            if (offsetTier)
            {
                tierHeight = config.OffsetTierHeight.RandomValue();
            }
            else
            {
                tierHeight = config.TierHeight.RandomValue();
            }

            topY += tierHeight;

            lr = ur;
            ll = ul;

            currentMeshId = RandomMeshId(config, ti == tierCount);

            if (offsetTier)
            {
                currentAngle += sectionAngle * 0.5f;

                ul = RadialPoint(currentAngle, tierRadius, topY);
                ur = RadialPoint(currentAngle + faceAngle, tierRadius, topY);

                AddFace(new Vector3[] { lr, ul, ur });
                AddFace(new Vector3[] { ul, lr, ll });
            }
            else
            {
                if (Random.value < config.SegmentTwistChance)
                {
                    currentAngle += twistAngle;
                }

                ul = RadialPoint(currentAngle, tierRadius, topY);
                ur = RadialPoint(currentAngle + faceAngle, tierRadius, topY);

                Vector3[] facePoints = new Vector3[] { ul, ur, lr, ll };

                int extrudeCount = 0;
                if (Random.value < config.ExtrudeChance)
                {
                    extrudeCount = config.ExtrudeSteps.RandomValue();
                }

                for (int i = 0; i < extrudeCount; i++)
                {
                    currentMeshId = RandomMeshId(config, i == extrudeCount);

                    float   extrudeDist    = config.BranchSegmentLength.RandomValue();
                    Vector3 extrudeVec     = Normal(facePoints);
                    Vector3 noiseDirection = Random.insideUnitSphere;
                    extrudeVec  = Vector3.Lerp(extrudeVec, noiseDirection, config.BranchDirectionNoise);
                    extrudeVec  = Vector3.Lerp(extrudeVec, Vector3.up, config.BranchVerticalBias.RandomValue());
                    extrudeVec *= extrudeDist;

                    Vector3[] newFacePoints = ExtrudePoints(facePoints, extrudeVec);
                    Vector3   newFaceCenter = Center(newFacePoints);

                    float scaleAmount = config.BranchSegmentScale.RandomValue();
                    newFacePoints = ScalePoints(newFacePoints, scaleAmount, newFaceCenter);

                    if (Random.value < config.SegmentTwistChance)
                    {
                        newFacePoints = RotatePoints(newFacePoints, twistAngle, extrudeVec, newFaceCenter);
                    }

                    AddTubeFaces(facePoints, newFacePoints);

                    facePoints = newFacePoints;
                }

                if (Random.value < config.PeakChance)
                {
                    float peakDistance = config.BranchPeakSize.RandomValue();
                    AddPeak(facePoints, peakDistance);
                }
                else
                {
                    AddFace(facePoints);
                }
            }
        }

        // end of main stage, start of cap stage

        Random.InitState(capSeed);

        float   peakHeight = config.PeakSize.RandomValue();
        Vector3 peakVertex = new Vector3(0, topY + peakHeight, 0);

        AddFace(new Vector3[] { ur, ul, peakVertex });

        // end of cap stage, all randomization should be finished

        // copy mirrored / rotated segments to complete model

        //if (bilateralSymmetry)
        //    AddMirrored();

        AddRotations(sectionAngle, rotationalSymmetry);

        // create final mesh

        Mesh mesh = new Mesh();

        for (int i = 0; i < vertices.Count; i++)
        {
            vertices[i] *= config.FinalScale;
        }
        mesh.SetVertices(vertices);

        mesh.subMeshCount = triangles.Length;
        for (int meshId = 0; meshId < triangles.Length; meshId++)
        {
            mesh.SetTriangles(triangles[meshId].ToArray(), meshId);
        }

        mesh.RecalculateNormals();
        mesh.RecalculateTangents();
        mesh.RecalculateBounds();

        targetObject.GetComponent <MeshFilter>().sharedMesh = mesh;

        var collider = targetObject.GetComponent <MeshCollider>();

        if (collider != null)
        {
            collider.sharedMesh = null;
            collider.sharedMesh = mesh;
        }

        Random.state = oldState;

        return(mesh.bounds);
    }