// 木の枝を再帰的に辿り、全長の長さ(根元から枝先に至るまでの長さ)を返す static float TraverseMaxLength(TreeBranch branch) { float max = 0f; branch.Children.ForEach(c => { max = Mathf.Max(max, TraverseMaxLength(c)); }); return(branch.Length + max); }
// 木の枝を再帰的に辿る // 到達した枝に対してAction<TreeBranch> actionコールバックを実行する static void Traverse(TreeBranch from, Action <TreeBranch> action) { if (from.Children.Count > 0) { from.Children.ForEach(child => { Traverse(child, action); }); } action(from); }
public TreeBranch( int generation, int generations, Vector3 from, Vector3 tangent, Vector3 normal, Vector3 binormal, float length, float radius, float offset, float rotation_b, // binormal の回転量 ::TEST bool haveChildren, TreeData data ) { this.generation = generation; this.fromRadius = radius; // 枝先である場合は先端の太さが0になる this.toRadius = (generation == 0) ? 0f : radius *data.radiusAttenuation; this.from = from; // 枝先ほど分岐する角度が大きくなる var scale = Mathf.Lerp(1f, data.growthAngleScale, 1f - 1f * generation / generations); // normal方向の回転 var qn = Quaternion.AngleAxis(scale * data.GetRandomGrowthAngle(), normal); this.binormalRotaion = rotation_b; // binormal方向の回転 var qb = Quaternion.AngleAxis(scale * binormalRotaion, binormal); // 枝先が向いているtangent方向にqn * qbの回転をかけつつ、枝先の位置を決める this.to = from + (qn * qb) * tangent * length; this.length = length; this.offset = offset; // モデル生成に必要な節を構築 segments = BuildSegments(data, fromRadius, toRadius, normal, binormal); segmentForChild = segments[segments.Count - 1]; // 子を生成するときはこの フレネフレームを使う 先端から伸ばす children = new List <TreeBranch>(); if (generation > 0 && fromRadius <= 0.5f && haveChildren) { // 分岐する数を取得 int count = data.GetRandomBranches(); for (int i = 0; i < count; i++) { float ratio; // [0.0 ~ 1.0] if (count == 1) { // 分岐数が1である場合(0除算を回避) ratio = 1f; } else { ratio = Mathf.Lerp(0.5f, 1f, (1f * i) / (count - 1)); } // 分岐元の節を取得 var index = Mathf.FloorToInt(ratio * (segments.Count - 1)); var segment = segments[index]; // 分岐元の節が持つベクトルをTreeBranchに渡すことで滑らかな分岐を得る Vector3 nt = segment.Frame.Tangent; Vector3 nn = segment.Frame.Normal; Vector3 nb = segment.Frame.Binormal; var child = new TreeBranch( this.generation - 1, generations, segment.Position, nt, nn, nb, this.length * Mathf.Lerp(1f, data.lengthAttenuation, ratio), // -> length radius / 2, // -> fromRadius offset + length, // -> offset rotation_b, false, data ); children.Add(child); } } }
public Mesh Build(TreeData data, int generations, float length, float radius) { data.Setup(); generations = data.lsystem.MaxLength; var root = new TreeBranch( generations, length, radius, data ); /* * TreeBranch 再帰させないで,枝を一本生成して返すっていう風にする.対象となってる treebranch を持つようにして * それの子に枝を追加するかどうかみたいなのを作って行けばいい. * 子に追加するためには,親の情報を渡すようにすればいい.root だけは作った方がいいかな.結局ランダム性があるんだkら * 生成してみないとワカらないよね. */ string generator = data.lsystem.S_Brackets; // Debug.Log("Length is : " + generator.Length + " generator is: " + generator); var parent = root; List <TreeBranch> parents = new List <TreeBranch>(); // 親をスタックしていく List <float> rotations = new List <float>(); float rotation_b = 0f; branchNum = 0; for (int i = 1; i < generator.Length; i++) // root は除く { if (generator[i] == 'F') { TreeBranch newBranch = new TreeBranch( parent.Generation - 1, generations, parent.To, parent.SegmentForChild.Frame.Tangent, parent.SegmentForChild.Frame.Normal, parent.SegmentForChild.Frame.Binormal, parent.Length * data.lengthAttenuation, parent.ToRadius, parent.Offset + parent.Length, rotation_b, true, data ); if (newBranch.FromRadius <= 0.01f) { continue; } if (newBranch.FromRadius <= 0.5f) { branchDetail++; // TODO: もっと条件を追加する } if (Vector3.Distance(root.SegmentForChild.Position, parent.SegmentForChild.Position) > treeHeight) { // 見たいのは相対的な評価ならこれでも大丈夫 treeHeight = parent.SegmentForChild.Position.y; // (単純な高さじゃなくて,どれぐらい伸びたのかというのが正しい) } parent.Children.Add(newBranch); branchNum++; parent = newBranch; } else if (generator[i] == '+') { rotation_b = data.lsystem.Angle; // TODO: method 使う spreadDeg++; // FIXME: 今は回転したら広がっているとしている } else if (generator[i] == '-') { rotation_b = -data.lsystem.Angle; spreadDeg++; } else if (generator[i] == '[') { parents.Add(parent); rotations.Add(rotation_b); } else if (generator[i] == ']') { // ']' の時 // parent を pop する if (parents.Count <= 0) { Debug.LogError("At Close Bracket, somethin is wrong, maybe it's rule"); } parent = parents[parents.Count - 1]; parents.RemoveAt(parents.Count - 1); // 親の回転より回転を元に戻す // -> 親がもともと曲がっててて,それに対して F はその曲がったまま伸びるため rotation_b = rotations[rotations.Count - 1]; rotations.RemoveAt(rotations.Count - 1); } } Debug.Log(branchNum); data.lsystem.SetEvalInfo(treeHeight, spreadDeg, branchNum, data.radiusAttenuation, branchDetail); var vertices = new List <Vector3>(); var normals = new List <Vector3>(); var tangents = new List <Vector4>(); var uvs = new List <Vector2>(); var triangles = new List <int>(); // 木の全長を取得 // 枝の長さを全長で割ることで、uv座標の高さ(uv.y)が // 根元から枝先に至るまで[0.0 ~ 1.0]で変化するように設定する float maxLength = TraverseMaxLength(root); // 再帰的に全ての枝を辿り、一つ一つの枝に対応するMeshを生成する Traverse(root, (branch) => { var offset = vertices.Count; var vOffset = branch.Offset / maxLength; var vLength = branch.Length / maxLength; // 一本の枝から頂点データを生成する for (int i = 0, n = branch.Segments.Count; i < n; i++) { var t = 1f * i / (n - 1); var v = vOffset + vLength * t; var segment = branch.Segments[i]; var N = segment.Frame.Normal; var B = segment.Frame.Binormal; for (int j = 0; j <= data.radialSegments; j++) { // 0.0 ~ 2π var u = 1f * j / data.radialSegments; float rad = u * PI2; float cos = Mathf.Cos(rad), sin = Mathf.Sin(rad); var normal = (cos * N + sin * B).normalized; vertices.Add(segment.Position + segment.Radius * normal); normals.Add(normal); var tangent = segment.Frame.Tangent; tangents.Add(new Vector4(tangent.x, tangent.y, tangent.z, 0f)); uvs.Add(new Vector2(u, v)); } } // 一本の枝の三角形を構築する for (int j = 1; j <= data.heightSegments; j++) { for (int i = 1; i <= data.radialSegments; i++) { int a = (data.radialSegments + 1) * (j - 1) + (i - 1); int b = (data.radialSegments + 1) * j + (i - 1); int c = (data.radialSegments + 1) * j + i; int d = (data.radialSegments + 1) * (j - 1) + i; a += offset; b += offset; c += offset; d += offset; triangles.Add(a); triangles.Add(d); triangles.Add(b); triangles.Add(b); triangles.Add(d); triangles.Add(c); } } }); var mesh = new Mesh(); mesh.vertices = vertices.ToArray(); Debug.Log("vertices's count: " + vertices.Count); mesh.normals = normals.ToArray(); mesh.tangents = tangents.ToArray(); mesh.uv = uvs.ToArray(); mesh.triangles = triangles.ToArray(); mesh.RecalculateBounds(); return(mesh); }