Beispiel #1
0
        static bool GenerateMeshData(IvyGraph ivyGraph, IvyProfile ivyProfile, bool forceGeneration = false)
        {
            var p = ivyProfile;

            //branches
            foreach (var root in ivyGraph.roots)
            {
                var cache = IvyRoot.GetMeshCacheFor(root);
                if (root.useCachedBranchData && !forceGeneration)
                {
                    combinedTriangleIndices.Clear();
                    cache.triangles.ForEach(localIndex => combinedTriangleIndices.Add(localIndex + verticesAll.Count));
                    trianglesAll.AddRange(combinedTriangleIndices);

                    verticesAll.AddRange(cache.vertices);
                    texCoordsAll.AddRange(cache.texCoords);
                    continue;
                }
                root.useCachedBranchData = true;

                //process only roots with more than one node
                if (root.nodes.Count < 2)
                {
                    continue;
                }

                cache.vertices.Clear();
                cache.texCoords.Clear();
                cache.triangles.Clear();

                //branch diameter depends on number of parents AND branch taper
                float local_ivyBranchDiameter = 1.0f / Mathf.Lerp(1f, 1f + root.parents, ivyProfile.branchTaper);

                // smooth the line... which increases points a lot
                allPoints = root.nodes.Select(node => node.p).ToList();
                var useThesePoints = allPoints;
                if (ivyProfile.branchSmooth > 1)
                {
                    SmoothLineCatmullRomNonAlloc(allPoints, smoothPoints, ivyProfile.branchSmooth);
                    useThesePoints = smoothPoints;
                }

                // generate simplified points for each root, to make it less wavy AND save tris
                if (!root.isAlive && ivyProfile.branchOptimize > 0f)
                {
                    newPoints.Clear();
                    newPoints.AddRange(SimplificationHelpers.Simplify <Vector3>(
                                           useThesePoints,
                                           (vec1, vec2) => vec1 == vec2,
                                           (vec) => vec.x,
                                           (vec) => vec.y,
                                           (vec) => vec.z,
                                           ivyProfile.branchOptimize * ivyProfile.ivyStepDistance * 0.5f,
                                           false
                                           ));
                    useThesePoints = newPoints;
                }

                // I'm not sure why there's this bug when we use Catmull Rom + line simplify, but let's do this hacky fix
                // if ( ivyProfile.branchSmooth > 1 && ivyProfile.branchOptimize > 0f ) {
                //     useThesePoints.ForEach( delegate(Vector3 point) {
                //         if ( float.IsInfinity(point.x) ) {point.x = 0f;}
                //         if ( float.IsInfinity(point.y) ) {point.y = 0f;}
                //         if ( float.IsInfinity(point.z) ) {point.z = 0f;}
                //     } );
                // }

                for (int n = 0; n < useThesePoints.Count; n++)
                {
                    if (verticesAll.Count >= 65531)
                    {
                        Debug.LogWarning("Hedera: ending branch generation early, reached ~65536 vertex limit on mesh " + ivyGraph.seedPos + "... but this could technically be solved in Unity 2017.3+ or later with 32-bit index formats for meshes? The exercise is left to the reader.");
                        break;
                    }
                    cache.meshSegments = n + 1;

                    //weight depending on ratio of node length to total length
                    float taper = 1f * n / useThesePoints.Count;
                    taper = Mathf.Lerp(1f, (1f - taper) * taper, ivyProfile.branchTaper);

                    //create trihedral vertices... TODO: let user specify how many sides?
                    Vector3 up    = Vector3.down;
                    Vector3 basis = Vector3.Normalize(n < useThesePoints.Count - 1 ? useThesePoints[n + 1] - useThesePoints[n] : -(useThesePoints[n] - useThesePoints[n - 1]));
                    // Debug.DrawLine( newPoints[node+1] + ivyGraph.seedPos, newPoints[node] + ivyGraph.seedPos, Color.cyan, 5f, false);

                    int   edges = 3;                        // TODO: finish this, make it configurable
                    float texV  = (n % 2 == 0 ? 1f : 0.0f); // vertical UV tiling
                    for (int b = 0; b < edges; b++)
                    {
                        // generate vertices
                        if (b == 0)
                        {
                            branchVertBasis[b] = Vector3.Cross(up, basis).normalized *Mathf.Max(0.001f, local_ivyBranchDiameter * p.ivyBranchSize * taper) + useThesePoints[n];
                        }
                        else
                        {
                            branchVertBasis[b] = RotateAroundAxis(branchVertBasis[0], useThesePoints[n], basis, 6.283f * b / edges);
                        }
                        cache.vertices.Add(branchVertBasis[b]);

                        // generate UVs
                        cache.texCoords.Add(new Vector2(1f * b / (edges - 1), texV));

                        // add triangles
                        // AddTriangle(root, 4, 1, 5);
                        // AddTriangle(root, 5, 1, 2);

                        // TODO: finish this
                    }

                    if (n == 0)   // start cap
                    {
                        if (taper > 0f)
                        {
                            AddTriangle(cache, 1, 2, 3);
                        }
                        continue;
                    }

                    AddTriangle(cache, 4, 1, 5);
                    AddTriangle(cache, 5, 1, 2);

                    AddTriangle(cache, 5, 2, 6);
                    AddTriangle(cache, 6, 2, 3);

                    AddTriangle(cache, 6, 3, 1);
                    AddTriangle(cache, 6, 1, 4);

                    if (n == useThesePoints.Count - 1 && taper > 0f) // end cap
                    {
                        AddTriangle(cache, 3, 2, 1);
                    }
                }

                combinedTriangleIndices.Clear();
                cache.triangles.ForEach(localIndex => combinedTriangleIndices.Add(localIndex + verticesAll.Count));
                trianglesAll.AddRange(combinedTriangleIndices);

                verticesAll.AddRange(cache.vertices);
                texCoordsAll.AddRange(cache.texCoords);
            }

            if (ivyProfile.ivyLeafSize <= 0.001f || ivyProfile.leafProbability <= 0.001f)
            {
                return(true);
            }

            //create leafs
            allLeafPoints.Clear();
            foreach (var root in ivyGraph.roots)
            {
                // don't bother on small roots
                if (root.nodes.Count <= 2)
                {
                    root.useCachedLeafData = false;
                    continue;
                }
                var cache = IvyRoot.GetMeshCacheFor(root);

                // use cached mesh data for leaves only if (a) we're supposed to, and (b) if not using vertex colors OR vertex colors seem valid
                if (root.useCachedLeafData && !forceGeneration && (!ivyProfile.useVertexColors || cache.leafVertices.Count == cache.leafVertexColors.Count))
                {
                    combinedTriangleIndices.Clear();
                    cache.leafTriangles.ForEach(index => combinedTriangleIndices.Add(index + leafVerticesAll.Count));
                    leafTrianglesAll.AddRange(combinedTriangleIndices);

                    allLeafPoints.AddRange(cache.leafPoints);
                    leafVerticesAll.AddRange(cache.leafVertices);
                    leafUVsAll.AddRange(cache.leafUVs);
                    if (ivyProfile.useVertexColors)
                    {
                        leafColorsAll.AddRange(cache.leafVertexColors);
                    }
                    continue;
                }
                root.useCachedLeafData = true;
                cache.leafPoints.Clear();
                cache.leafVertices.Clear();
                cache.leafUVs.Clear();
                cache.leafTriangles.Clear();
                cache.leafVertexColors.Clear();

                // simple multiplier, just to make it a more dense
                for (int i = 0; i < 1; ++i)
                {
                    var leafPositions = GetAllSamplePosAlongRoot(root, p.ivyLeafSize);

                    // for(int n=0; n<root.nodes.Count; n++)
                    foreach (var kvp in leafPositions)
                    {
                        if (leafVerticesAll.Count >= 65530)
                        {
                            Debug.LogWarning("Hedera: ending leaf generation early, reached ~65536 vertex limit on mesh " + ivyGraph.seedPos + "... but this could technically be solved in Unity 2017.3+ or later with 32-bit index formats for meshes? The exercise is left to the reader.");
                            break;
                        }

                        int     n          = kvp.Value;
                        Vector3 newLeafPos = kvp.Key;
                        var     node       = root.nodes[n];

                        // // do not generate a leaf on the first few nodes
                        // if ( n <= 1 ) { // || n >= root.nodes.Count
                        //     continue;
                        // }

                        // probability of leaves on the ground is increased
                        float groundedness = Vector3.Dot(Vector3.down, node.c.normalized);
                        if (groundedness < -0.02f)
                        {
                            groundedness -= 0.1f;
                            groundedness *= 3f;
                        }
                        else
                        {
                            groundedness = (groundedness - 0.25f) * 0.5f;
                        }
                        groundedness *= ivyProfile.leafSunlightBonus * p.leafProbability;

                        // don't spawn a leaf on top of another leaf
                        bool  badLeafPos  = false;
                        float leafSqrSize = p.ivyLeafSize * p.ivyLeafSize * Mathf.Clamp(1f - p.leafProbability - groundedness, 0.01f, 2f);
                        for (int f = 0; f < allLeafPoints.Count; f++)
                        {
                            if (Vector3.SqrMagnitude(allLeafPoints[f] - newLeafPos) < leafSqrSize)
                            {
                                badLeafPos = true;
                                break;
                            }
                        }
                        if (badLeafPos)
                        {
                            continue;
                        }

                        IvyNode previousNode     = root.nodes[Mathf.Max(0, n - 1)];
                        float   randomSpreadHack = 0.25f;
                        if (n <= 1 || n == root.nodes.Count - 1)
                        {
                            randomSpreadHack = 0f;
                        }

                        // randomize leaf probability // guarantee a leaf on the first or last node
                        if ((Random.value + groundedness > 1f - p.leafProbability) || randomSpreadHack == 0f)
                        {
                            cache.leafPoints.Add(node.p);
                            allLeafPoints.Add(node.p);

                            //center of leaf quad
                            Vector3 up     = (newLeafPos - previousNode.p).normalized;
                            Vector3 right  = Vector3.Cross(up, node.c);
                            Vector3 center = newLeafPos - node.c.normalized * 0.05f + (up * Random.Range(-1f, 1f) + right * Random.Range(-1f, 1f)) * randomSpreadHack * p.ivyLeafSize;

                            //size of leaf
                            float sizeWeight = 1.5f - (Mathf.Abs(Mathf.Cos(2.0f * Mathf.PI)) * 0.5f + 0.5f);
                            float leafSize   = p.ivyLeafSize * sizeWeight + Random.Range(-p.ivyLeafSize, p.ivyLeafSize) * 0.1f + (p.ivyLeafSize * groundedness);
                            leafSize = Mathf.Max(0.01f, leafSize);

                            Quaternion facing = node.c.sqrMagnitude < 0.001f ? Quaternion.identity : Quaternion.LookRotation(Vector3.Lerp(-node.c, Vector3.up, Mathf.Clamp01(0.68f - Mathf.Abs(groundedness)) * ivyProfile.leafSunlightBonus), Random.onUnitSphere);
                            AddLeafVertex(cache, center, new Vector3(-1f, 1f, 0f), leafSize, facing);
                            AddLeafVertex(cache, center, new Vector3(1f, 1f, 0f), leafSize, facing);
                            AddLeafVertex(cache, center, new Vector3(-1f, -1f, 0f), leafSize, facing);
                            AddLeafVertex(cache, center, new Vector3(1f, -1f, 0f), leafSize, facing);

                            cache.leafUVs.Add(new Vector2(1.0f, 1.0f));
                            cache.leafUVs.Add(new Vector2(0.0f, 1.0f));
                            cache.leafUVs.Add(new Vector2(1.0f, 0.0f));
                            cache.leafUVs.Add(new Vector2(0.0f, 0.0f));

                            if (ivyProfile.useVertexColors)
                            {
                                var randomColor = ivyProfile.leafVertexColors.Evaluate(Random.value);
                                cache.leafVertexColors.Add(randomColor);
                                cache.leafVertexColors.Add(randomColor);
                                cache.leafVertexColors.Add(randomColor);
                                cache.leafVertexColors.Add(randomColor);
                            }

                            // calculate normal of the leaf tri, and make it face outwards
                            // var normal = GetNormal(
                            //     ivyGraph.leafVertices[ivyGraph.leafVertices.Count - 2],
                            //     ivyGraph.leafVertices[ivyGraph.leafVertices.Count - 4],
                            //     ivyGraph.leafVertices[ivyGraph.leafVertices.Count - 3]
                            // );
                            // if ( Vector3.Dot( normal, node.adhesionVector) < 0f) {
                            //    AddLeafTriangle(ivyGraph, 2, 4, 3);
                            //    AddLeafTriangle(ivyGraph, 3, 1, 2);
                            // } else {
                            AddLeafTriangle(cache, 1, 3, 4);
                            AddLeafTriangle(cache, 4, 2, 1);
                            // }
                        }
                    }
                    combinedTriangleIndices.Clear();
                    cache.leafTriangles.ForEach(index => combinedTriangleIndices.Add(index + leafVerticesAll.Count));
                    leafTrianglesAll.AddRange(combinedTriangleIndices);

                    leafVerticesAll.AddRange(cache.leafVertices);
                    leafUVsAll.AddRange(cache.leafUVs);
                    if (ivyProfile.useVertexColors)
                    {
                        leafColorsAll.AddRange(cache.leafVertexColors);
                    }
                }
            }
            return(true);
        }
Beispiel #2
0
 private void pb_Draw_MouseUp(object sender, MouseEventArgs e)
 {
     isMouseDown = false;
     if (drawCheckbox.Checked)
     {
         try
         {
             IList <Point> simplifiedPointsLine = SimplificationHelpers.Simplify <Point>(
                 pointCollection,
                 (p1, p2) => p1 == p2,
                 (p) => p.X,
                 (p) => p.Y,
                 Convert.ToDouble(tolerance_textbox.Text),
                 hq_checkbox.Checked
                 );
             pointCollection        = new List <Point>();
             outputPointsLabel.Text = "Output points: " + simplifiedPointsLine.Count;
             if (tb_width.Text != "" && lv_charmap.SelectedItems.Count != 0)
             {
                 if (autoInputOutputPoints.Checked && simplifiedPointsLine.Count > 0) //if checked - adding input point as input point
                 {
                     lv_points.Items.Add(simplifiedPointsLine[0].X / 2 + "," + simplifiedPointsLine[0].Y / 2 + ",-1,-1");
                     segs++;
                 }
                 for (int i = 0; i < simplifiedPointsLine.Count - 1; i++) //iterating through every point except the last one because line builds of two points - this and next
                 {
                     lv_points.Items.Add(simplifiedPointsLine[i].X / 2 + "," + simplifiedPointsLine[i].Y / 2 + "," + (simplifiedPointsLine[i + 1].X / 2 + "," + simplifiedPointsLine[i + 1].Y / 2));
                     segs++;
                 }
                 selected_seg = segs - 1;
                 for (int i = 0; i < segs; i++)
                 {
                     lv_points.Items[i].Selected = false;
                     lv_points.Items[i].Focused  = false;
                 }
                 lv_points.Items[segs - 1].Selected = true;
                 lv_points.Items[segs - 1].Focused  = true;
                 if (autoInputOutputPoints.Checked && simplifiedPointsLine.Count > 0) //if checked - adding last point as output point
                 {
                     lv_points.Items.Add("-1,-1," + simplifiedPointsLine[simplifiedPointsLine.Count - 1].X / 2 + "," + simplifiedPointsLine[simplifiedPointsLine.Count - 1].Y / 2);
                     segs++;
                 }
                 if (autogapCheckbox.Checked) //autogapping on fly
                 {
                     remove_gaps(null, null); //it removes gapps and saves the array
                 }
                 else
                 {
                     setButton_Click(null, null); //it saves the array
                 }
             }
             else
             {
                 MaterialMessageBox.Show("Select the char and enter the width first!");
             }
         }
         catch (FormatException exep)
         {
             MaterialMessageBox.Show("Tolerance value is incorrect!");
         }
         //simplifying the pointcollection
     }
 }