// Creates a fork and builds 2 branches off it // -> radiusRef: radius of trunk // -> angleRef: angle of the final trunk ring void BuildFork(MeshBuilder meshBuilder, Vector3 offset, float radiusRef, Quaternion angleRef, int lengthSegments, int radialSegments, Vector2 forkAngleBounds, float maxBendAngle, float branchLength, float lengthRandomness, float branchTwist, float twistRandomness) { // Calculate a random angle for branches to leave from float forkAngle = Random.Range(forkAngleBounds.x, forkAngleBounds.y) * (Random.Range(0, 1) == 1 ? 1:-1); // angle of the fork ring (vertices curving between branches) LODRandNums.Add(forkAngle); // Add offset to LODList for greater accuracy LODRandNums.Add(offset.x); LODRandNums.Add(offset.y); LODRandNums.Add(offset.z); float angle_A = (Mathf.PI / 4.0f) + (forkAngle * Mathf.Deg2Rad) / 2.0f; float angle_B = (Mathf.PI / 4.0f) - (forkAngle * Mathf.Deg2Rad) / 2.0f; float radius_A = Mathf.Cos(angle_A) * radiusRef; float radius_B = Mathf.Cos(angle_B) * radiusRef; Quaternion ringRotation_A = angleRef * Quaternion.Euler(angle_A * Mathf.Rad2Deg, 0.0f, 0.0f); Vector3 ringCentre_A = (ringRotation_A * (new Vector3(0.0f, radiusRef, 0.0f))) + offset; Quaternion ringRotation_B = angleRef * Quaternion.Euler(-angle_B * Mathf.Rad2Deg, 0.0f, 0.0f); Vector3 ringCentre_B = (ringRotation_B * (new Vector3(0.0f, radiusRef, 0.0f))) + offset; int segments_A = radialSegments; int segments_B = radialSegments; if (radius_A > radius_B) { ringCentre_B = offset; segments_B = Mathf.RoundToInt(radialSegments * (radius_B / radiusRef)); if (segments_B < 3) { segments_B = 3; } } if (radius_B > radius_A) { ringCentre_A = offset; segments_A = Mathf.RoundToInt(radialSegments * (radius_A / radiusRef)); if (segments_A < 3) { segments_A = 3; } } BuildBranch(meshBuilder, ringCentre_A, radius_A, ringRotation_A, lengthSegments, segments_A, forkAngleBounds, maxBendAngle, branchLength, lengthRandomness, branchTwist, twistRandomness, true); BuildBranch(meshBuilder, ringCentre_B, radius_B, ringRotation_B, lengthSegments, segments_B, forkAngleBounds, maxBendAngle, branchLength, lengthRandomness, branchTwist, twistRandomness, false); }
private Mesh BuildTrunk(bool setupLOD) { // Set variables from parent PGTreeBase p = transform.parent.GetComponent("PGTreeBase") as PGTreeBase; // Ensure leaf list is empty m_Leaves.Clear(); MeshBuilder meshBuilder = new MeshBuilder(); Vector3 offset = new Vector3(0.0f, 0.0f, 0.0f); int f1 = 0; int f2 = 0; float angle_offset1 = 0.0f; float angle_offset2 = 0.0f; // Generate 2 random harmonic frequencies to blend. f1 = Random.Range(1, 5) * 2; f2 = Random.Range(1, 3) * 2 + 1; angle_offset1 = Random.Range(0.0f, Mathf.PI * 2); angle_offset2 = Random.Range(0.0f, Mathf.PI * 2); Vector3 ring_centre = offset; Quaternion rotation = Quaternion.identity; Vector3 angle_rand; angle_rand = new Vector3(Random.Range(-p.m_max_bend, p.m_max_bend), Random.Range(-p.m_max_bend, p.m_max_bend), Random.Range(-p.m_max_bend, p.m_max_bend)); LODRandNums.Add(angle_rand.x); LODRandNums.Add(angle_rand.y); LODRandNums.Add(angle_rand.z); BuildCurve(meshBuilder, offset, p.m_start_radius, p.m_radial_segments, 0.0f, f1, f2, angle_offset1, angle_offset2, Quaternion.identity, 0.0f, p.m_start_irregularity, false); for (int i = 1; i <= p.m_height_segments; i++) { float ring_height = ((float)i / (float)p.m_height_segments) * p.m_height; float radius = 1.0f; if (p.m_trunk_curve_type_s == TrunkCurveType.Circular) { radius = p.m_circ_trunk_bulge * Mathf.Sin((Mathf.PI * p.m_circ_trunk_bulge_freq * ring_height) / p.m_height + p.m_circ_trunk_bulge_offset) + (p.m_end_radius - p.m_start_radius) * ring_height / p.m_height + p.m_start_radius; } else if (p.m_trunk_curve_type_s == TrunkCurveType.Exponential || p.m_trunk_curve_type_s == TrunkCurveType.ExpoLinear) { bool isExpoLin = p.m_trunk_curve_type_s == TrunkCurveType.ExpoLinear; if (isExpoLin) { radius = ExpoLinInterp(p.m_start_radius, p.m_exp_mid_radius, p.m_end_radius, p.m_height, ring_height, p.m_expolinear_blend); } else { radius = ExpoLinInterp(p.m_start_radius, p.m_exp_mid_radius, p.m_end_radius, p.m_height, ring_height); } } float irregularity = p.m_start_irregularity * Mathf.Exp(-ring_height * p.m_irregularity_falloff / p.m_height); float twist_angle = (ring_height * p.m_twist_angle) / p.m_height; float hr = (ring_height / p.m_height); rotation = Quaternion.Euler(angle_rand.x * hr, angle_rand.y * hr, angle_rand.z * hr); Vector3 new_ring_offset = new Vector3(0.0f, p.m_height / (float)p.m_height_segments, 0.0f); ring_centre += rotation * new_ring_offset; float v = ring_height / (2 * Mathf.PI * radius); BuildCurve(meshBuilder, ring_centre, radius, p.m_radial_segments, v, f1, f2, angle_offset1, angle_offset2, rotation, twist_angle, irregularity, true); if (i == p.m_height_segments) { // Add twist angle to rotation for complete angle reference Quaternion rotationRef = rotation * Quaternion.Euler(0.0f, -p.m_twist_angle, 0.0f); LODRandNums.Add(radius); BuildFork(meshBuilder, ring_centre, radius, rotationRef, p.m_branch_segments, p.m_radial_segments, new Vector2(p.m_branch_min_fork_angle, p.m_branch_max_fork_angle), p.m_branch_max_bend, p.m_branch_length, p.m_branch_length_randomness, p.m_branch_twist_angle, p.m_branch_twist_randomness); break; } } //Debug.Log("Number of Leaves: " + m_Leaves.Count); Mesh mesh = meshBuilder.CreateMesh(); //Debug.Log("Length of LODRandNums = " + LODRandNums.Count); mesh.RecalculateNormals(); return(mesh); }
void BuildBranch(MeshBuilder meshBuilder, Vector3 offset, float startRadius, Quaternion startAngle, int lengthSegments, int radialSegments, Vector2 forkAngleBounds, float maxBendAngle, float branchLength, float lengthRandomness, float branchTwist, float twistRandomness, bool startTriangles, bool useMinRadius, bool generateLOD) { PGTreeBase p = transform.parent.GetComponent("PGTreeBase") as PGTreeBase; // Calculate a random bend towards the sky Vector3 angleRand = Vector3.zero; Vector3 testVector = Vector3.down; float maxAngle = maxBendAngle; for (int i = 0; i < 100 && testVector.y <= p.m_branch_min_uprightness; i++) { angleRand = new Vector3(Random.Range(-maxAngle, maxAngle), Random.Range(-maxAngle, maxAngle), Random.Range(-maxAngle, maxAngle)); maxAngle += 30; Quaternion newRot = startAngle * Quaternion.Euler(angleRand.x, angleRand.y, angleRand.z); testVector = newRot * Vector3.up; if (i == 99) { Debug.LogError("Maximum iterations reached. Bend angle may not be upright!"); } } LODRandNums.Add(angleRand.x); LODRandNums.Add(angleRand.y); LODRandNums.Add(angleRand.z); Vector3 ringCentre = offset; Quaternion rotation = Quaternion.identity; float twistRand = branchTwist + Random.Range(-twistRandomness, twistRandomness); float lengthRand = branchLength + Random.Range(-lengthRandomness, lengthRandomness); LODRandNums.Add(twistRand); LODRandNums.Add(lengthRand); float radius = startRadius; for (int i = 0; i <= lengthSegments; i++) { float heightRatio = (float)i / (float)(lengthSegments); //float ringHeight = heightRatio*lengthRand; float twistAngle = heightRatio * twistRand; bool buildTriangles = !startTriangles && i == 0 ? false : true; radius = useMinRadius ? radius : radius * p.m_branch_radius_falloff; float v = (heightRatio * lengthRand) / (2 * Mathf.PI * radius) + 1.0f / (2 * Mathf.PI); if (i == lengthSegments && useMinRadius) { // Add new fork BuildFork(meshBuilder, ringCentre, radius, startAngle * rotation * Quaternion.Euler(0.0f, -twistRand, 0.0f), lengthSegments, radialSegments, forkAngleBounds, maxBendAngle * p.m_branch_bend_falloff, branchLength * p.m_branch_length_falloff, lengthRandomness, branchTwist * p.m_branch_twist_falloff, twistRandomness); break; } else if (radius < p.m_branch_min_radius && useMinRadius) { // Complete branch //rotation = Quaternion.Euler(angleRand*heightRatio); //ringCentre += startAngle*rotation*new Vector3(0.0f,lengthRand/lengthSegments,0.0f); v = (((float)(i + 1) / (float)lengthSegments) * lengthRand) / (2 * Mathf.PI * radius) + 1.0f / (2 * Mathf.PI); radius = 0; BuildCurve(meshBuilder, ringCentre, radius, radialSegments, v, 0, 0, 0.0f, 0.0f, startAngle * rotation, twistAngle, 0, buildTriangles); if (p.m_hasLeaves) { if (p.m_hasStems) { BuildStems(meshBuilder, ringCentre, startAngle * rotation * Quaternion.Euler(0.0f, twistRand, 0.0f), p.m_stem_length, p.m_stem_radius, p.m_stem_segments, p.m_stem_bend); } m_Leaves.Add(new LeafConstruct(ringCentre, startAngle * rotation * Quaternion.Euler(0.0f, twistRand, 0.0f), p.m_stem_length, p.m_stem_bend)); } break; } BuildCurve(meshBuilder, ringCentre, radius, radialSegments, v, 0, 0, 0.0f, 0.0f, startAngle * rotation, twistAngle, 0, buildTriangles); rotation = Quaternion.Euler(angleRand * ((float)i / (float)(lengthSegments + 1))); ringCentre += startAngle * rotation * new Vector3(0.0f, lengthRand / (float)lengthSegments, 0.0f); } }