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); }
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); }