public static void ForceIvyGrowth(IvyGraph graph, IvyProfile ivyProfile, Vector3 newPos, Vector3 newNormal)
        {
            newPos -= graph.seedPos; // convert to local space

            var closestRoot = graph.roots[0];

            var lastNode   = closestRoot.nodes[closestRoot.nodes.Count - 1];
            var growVector = newPos - lastNode.p;

            var newNode = new IvyNode();

            newNode.p  = newPos;
            newNode.g  = (0.5f * lastNode.g + 0.5f * growVector.normalized).normalized;
            newNode.c  = -newNormal;
            newNode.s  = lastNode.s + growVector.magnitude;
            newNode.cS = lastNode.cS + growVector.magnitude;
            newNode.fS = 0f;
            newNode.cl = true;

            closestRoot.nodes.Add(newNode);
            closestRoot.useCachedBranchData = false;
            closestRoot.useCachedLeafData   = false;

            var cache = IvyRoot.GetMeshCacheFor(closestRoot);

            cache.debugLineSegmentsList.Add(lastNode.p + graph.seedPos);
            cache.debugLineSegmentsList.Add(newPos + graph.seedPos);
            cache.debugLineSegmentsArray = cache.debugLineSegmentsList.ToArray();

            if (graph.generateMeshDuringGrowth)
            {
                IvyMesh.GenerateMesh(graph, ivyProfile);
            }
        }
Exemple #2
0
        /** initialize a new ivy root */
        public void seed(Vector3 seedPos)
        {
            reset();
            roots.Clear();


            IvyNode tmpNode = new IvyNode();

            tmpNode.pos            = seedPos;
            tmpNode.primaryDir     = new Vector3(0.0f, 1.0f, 0.0f);
            tmpNode.adhesionVector = new Vector3(0.0f, 0.0f, 0.0f);

            tmpNode.length         = 0.0f;
            tmpNode.floatingLength = 0.0f;
            tmpNode.climb          = true;


            IvyRoot tmpRoot = new IvyRoot();

            tmpRoot.nodes.Add(tmpNode);

            tmpRoot.alive   = true;
            tmpRoot.parents = 0;

            roots.Add(tmpRoot);
        }
        static bool TryGrowIvyBranch(IvyGraph graph, IvyProfile ivyProfile, IvyRoot root, IvyNode fromNode, float forceMinLength = -1f)
        {
            //weight depending on ratio of node length to total length
            float weight          = 1f; //Mathf.PerlinNoise( fromNode.localPos.x + fromNode.lengthCumulative, fromNode.length + fromNode.localPos.y + fromNode.localPos.z); // - ( Mathf.Cos( fromNode.length / root.nodes[root.nodes.Count-1].length * 2.0f * Mathf.PI) * 0.5f + 0.5f );
            var   nearbyRootCount = graph.roots.Where(r => (r.nodes[0].p - fromNode.p).sqrMagnitude < ivyProfile.ivyStepDistance * ivyProfile.ivyStepDistance).Count();

            if (forceMinLength <= 0f)
            {
                if (graph.roots.Count >= ivyProfile.maxBranchesTotal ||
                    nearbyRootCount > ivyProfile.branchingProbability * 2.5f ||
                    root.childCount > ivyProfile.branchingProbability * 3.5f ||
                    root.nodes.Count < 3 ||
                    root.parents > ivyProfile.branchingProbability * 9f ||
                    ivyProfile.maxLength - fromNode.cS < ivyProfile.minLength ||
                    Random.value * Mathf.Clamp(weight, 0f, 1f - ivyProfile.branchingProbability) > ivyProfile.branchingProbability
                    )
                {
                    return(false);
                }
            }

            //new ivy node
            IvyNode newRootNode = new IvyNode();

            newRootNode.p  = fromNode.p;
            newRootNode.g  = Vector3.Lerp(fromNode.g, Vector3.up, 0.5f).normalized;
            newRootNode.c  = fromNode.c;
            newRootNode.s  = 0.0f;
            newRootNode.cS = forceMinLength > 0f ? 0f : fromNode.cS;
            newRootNode.fS = forceMinLength > 0f ? 0f : fromNode.fS;
            newRootNode.cl = true;

            //new ivy root
            IvyRoot newRoot = new IvyRoot();

            newRoot.nodes.Add(newRootNode);
            newRoot.isAlive        = true;
            newRoot.parents        = root.parents + 1;
            newRoot.forceMinLength = forceMinLength;

            graph.roots.Add(newRoot);
            root.childCount++;
            return(true);
        }
        public static IvyGraph SeedNewIvyGraph(IvyProfile ivyProfile, Vector3 seedPos, Vector3 primaryGrowDir, Vector3 adhesionVector, Transform root, bool generateMeshPreview = false)
        {
            var graph = new IvyGraph();

            graph.roots.Clear();
            graph.seedPos    = seedPos;
            graph.seedNormal = adhesionVector;
            graph.generateMeshDuringGrowth = generateMeshPreview;
            graph.rootBehavior             = root;

            IvyNode tmpNode = new IvyNode();

            tmpNode.p  = Vector3.zero; //seedPos;
            tmpNode.g  = primaryGrowDir;
            tmpNode.c  = adhesionVector;
            tmpNode.s  = 0.0f;
            tmpNode.cS = 0f;
            tmpNode.fS = 0.0f;
            tmpNode.cl = true;

            IvyRoot tmpRoot = new IvyRoot();

            tmpRoot.nodes.Add(tmpNode);
            tmpRoot.isAlive = true;
            graph.isGrowing = true;
            tmpRoot.parents = 0;
            graph.roots.Add(tmpRoot);

            if (graph.generateMeshDuringGrowth)
            {
                IvyMesh.GenerateMesh(graph, ivyProfile);
#if UNITY_EDITOR
                Undo.RegisterCreatedObjectUndo(graph.rootGO, "Hedera > Paint Ivy");
#endif
            }

            return(graph);
        }
Exemple #5
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);
        }
        public static void GrowIvyStep(IvyGraph graph, IvyProfile ivyProfile)
        {
            // if there are no longer any live roots, then we're dead
            if (graph.isGrowing)
            {
                graph.isGrowing = graph.roots.Where(root => root.isAlive).Count() > 0;
            }
            if (!graph.isGrowing)
            {
                return;
            }

            //lets grow
            foreach (var root in graph.roots)
            {
                //process only roots that are alive
                if (!root.isAlive)
                {
                    continue;
                }

                IvyNode lastNode = root.nodes[root.nodes.Count - 1];

                //let the ivy die, if the maximum float length is reached
                if (lastNode.cS > ivyProfile.maxLength || (lastNode.cS > Mathf.Max(root.forceMinLength, ivyProfile.minLength) && lastNode.fS > ivyProfile.maxFloatLength))
                {
                    // Debug.LogFormat("root death! cum dist: {0:F2}, floatLength {1:F2}", lastNode.lengthCumulative, lastNode.floatingLength);
                    root.isAlive = false;
                    SmoothGaussianAdhesion(root);
                    continue;
                }

                //grow vectors: primary direction, random influence, and adhesion of scene objectss

                //primary vector = weighted sum of previous grow vectors plus a little bit upwards
                Vector3 primaryVector = Vector3.Normalize(lastNode.g * 2f + Vector3.up);

                //random influence plus a little upright vector
                Vector3 exploreVector = lastNode.p - root.nodes[0].p;
                if (exploreVector.magnitude > 1f)
                {
                    exploreVector = exploreVector.normalized;
                }
                exploreVector *= Mathf.PingPong(root.nodes[0].p.sqrMagnitude * root.parents + lastNode.cS * 0.69f, 1f);
                Vector3 randomVector = (Random.onUnitSphere * 0.5f + exploreVector).normalized;

                //adhesion influence to the nearest triangle = weighted sum of previous adhesion vectors
                Vector3 adhesionVector = ComputeAdhesion(lastNode.p + graph.seedPos, ivyProfile);
                if (adhesionVector.sqrMagnitude <= 0.01f)
                {
                    adhesionVector = lastNode.c;
                }

                //compute grow vector
                Vector3 growVector = ivyProfile.ivyStepDistance *
                                     Vector3.Normalize(
                    primaryVector * ivyProfile.primaryWeight
                    + randomVector * Mathf.Max(0.01f, ivyProfile.randomWeight)
                    + adhesionVector * ivyProfile.adhesionWeight
                    );

                //gravity influence
                Vector3 gravityVector = ivyProfile.ivyStepDistance * Vector3.down * ivyProfile.gravityWeight;
                //gravity depends on the floating length
                gravityVector *= Mathf.Pow(lastNode.fS / ivyProfile.maxFloatLength, 0.7f);

                //next possible ivy node

                //climbing state of that ivy node, will be set during collision detection
                bool climbing = false;

                //compute position of next ivy node
                Vector3 newPos = lastNode.p + growVector + gravityVector;

                //combine alive state with result of the collision detection, e.g. let the ivy die in case of a collision detection problem
                Vector3 adhesionFromRaycast = adhesionVector;

                // convert newPos to world position, just for the collision calc
                newPos      += graph.seedPos;
                root.isAlive = root.isAlive && ComputeCollision(0.01f, lastNode.p + graph.seedPos, ref newPos, ref climbing, ref adhesionFromRaycast, ivyProfile.collisionMask);
                newPos      -= graph.seedPos;

                //update grow vector due to a changed newPos
                growVector = newPos - lastNode.p - gravityVector;

                // +graph.seedPos to convert back to world space
                var cache = IvyRoot.GetMeshCacheFor(root);
                cache.debugLineSegmentsList.Add(lastNode.p + graph.seedPos);
                cache.debugLineSegmentsList.Add(newPos + graph.seedPos);
                // cache line segments
                cache.debugLineSegmentsArray = cache.debugLineSegmentsList.ToArray();

                //create next ivy node
                IvyNode newNode = new IvyNode();

                newNode.p  = newPos;
                newNode.g  = (0.5f * lastNode.g + 0.5f * growVector.normalized).normalized;
                newNode.c  = adhesionVector; //Vector3.Lerp(adhesionVector, adhesionFromRaycast, 0.5f);
                newNode.s  = lastNode.s + (newPos - lastNode.p).magnitude;
                newNode.cS = lastNode.cS + (newPos - lastNode.p).magnitude;
                newNode.fS = climbing ? 0.0f : lastNode.fS + (newPos - lastNode.p).magnitude;
                newNode.cl = climbing;

                root.nodes.Add(newNode);
                root.useCachedBranchData = false;
                root.useCachedLeafData   = false;

                if (!root.isAlive)
                {
                    SmoothGaussianAdhesion(root);
                }

                var randomNode = root.nodes[Random.Range(0, root.nodes.Count)];
                if (TryGrowIvyBranch(graph, ivyProfile, root, randomNode))
                {
                    break;
                }
            }
        }
Exemple #7
0
        /** creates the ivy triangle mesh */
        public void birth()
        {
            //evolve a gaussian filter over the adhesian vectors

            float [] gaussian = { 1.0f, 2.0f, 4.0f, 7.0f, 9.0f, 10.0f, 9.0f, 7.0f, 4.0f, 2.0f, 1.0f };

            foreach (var root in roots)
            {
                for (int g = 0; g < 5; ++g)
                {
                    for (int node = 0; node < root.nodes.Count; node++)
                    {
                        Vector3 e = Vector3.zero;

                        for (int i = -5; i <= 5; ++i)
                        {
                            Vector3 tmpAdhesion = Vector3.zero;

                            if ((node + i) < 0)
                            {
                                tmpAdhesion = root.nodes[0].adhesionVector;
                            }
                            if ((node + i) >= root.nodes.Count)
                            {
                                tmpAdhesion = root.nodes[root.nodes.Count - 1].adhesionVector;
                            }
                            if (((node + i) >= 0) && ((node + i) < root.nodes.Count))
                            {
                                tmpAdhesion = root.nodes[node + i].adhesionVector;
                            }

                            e += tmpAdhesion * gaussian[i + 5];
                        }

                        root.nodes[node].smoothAdhesionVector = e / 56.0f;
                    }

                    foreach (var _node in root.nodes)
                    {
                        _node.adhesionVector = _node.smoothAdhesionVector;
                    }
                }
            }


            //parameters that depend on the scene object bounding sphere
            float local_ivyLeafSize = IvyManager.SceneObjMesh.boundingSphereRadius * ivySize * ivyLeafSize;

            float local_ivyBranchSize = IvyManager.SceneObjMesh.boundingSphereRadius * ivySize * ivyBranchSize;


            //reset existing geometry
            reset();


            //set data path
            path = "../textures/";


            //create material for leafs
            BasicMaterial tmpMaterial = new BasicMaterial();

            tmpMaterial.id      = 1;
            tmpMaterial.name    = "leaf_adult";
            tmpMaterial.texFile = IvyManager.LeafAdultTexPathName;

            materials.Add(tmpMaterial);


            //create second material for leafs
            tmpMaterial         = new BasicMaterial();
            tmpMaterial.id      = 2;
            tmpMaterial.name    = "leaf_young";
            tmpMaterial.texFile = IvyManager.LeafYoungTexPathName;

            materials.Add(tmpMaterial);


            //create material for branches
            tmpMaterial         = new BasicMaterial();
            tmpMaterial.id      = 3;
            tmpMaterial.name    = "branch";
            tmpMaterial.texFile = IvyManager.branchTexPathName;

            materials.Add(tmpMaterial);


            //create leafs
            foreach (var root in roots)
            {
                //simple multiplier, just to make it a more dense
                for (int i = 0; i < 10; ++i)
                {
                    //srand(i + (root - roots.begin()) * 10);

                    foreach (var node in root.nodes)
                    {
                        IvyNode back_node = root.nodes[root.nodes.Count - 1];
                        //weight depending on ratio of node length to total length
                        float weight = Mathf.Pow(node.length / back_node.length, 0.7f);

                        //test: the probability of leaves on the ground is increased
                        float groundIvy = Mathf.Max(0.0f, -Vector3.Dot(new Vector3(0.0f, 1.0f, 0.0f), node.adhesionVector.normalized));
                        weight += groundIvy * Mathf.Pow(1.0f - node.length / back_node.length, 2.0f);

                        //random influence
                        float probability = Random.value;

                        if (probability * weight > leafProbability)
                        {
                            //alignment weight depends on the adhesion "strength"
                            float alignmentWeight = node.adhesionVector.magnitude;



                            //horizontal angle (+ an epsilon vector, otherwise there's a problem at 0?and 90?.. mmmh)
                            float phi = vector2ToPolar(new Vector2(node.adhesionVector.z, node.adhesionVector.x).normalized + new Vector2(Vector2.kEpsilon, Vector2.kEpsilon)) - Mathf.PI * 0.5f;

                            //vertical angle, trimmed by 0.5
                            float theta = Vector3.Angle(node.adhesionVector, new Vector3(0.0f, -1.0f, 0.0f)) * 0.5f;

                            //center of leaf quad
                            Vector3 center = node.pos + new Vector3(Random.Range(-0.5f, 0.5f), Random.Range(-0.5f, 0.5f), Random.Range(-0.5f, 0.5f)).normalized *local_ivyLeafSize;

                            //size of leaf
                            float sizeWeight = 1.5f - (Mathf.Cos(weight * 2.0f * Mathf.PI) * 0.5f + 0.5f);


                            //random influence
                            phi += Random.Range(-0.5f, 0.5f) * (1.3f - alignmentWeight);

                            theta += Random.Range(-0.5f, 0.5f) * (1.1f - alignmentWeight);



                            //create vertices
                            BasicVertex tmpVertex = new BasicVertex();


                            tmpVertex.pos  = center + new Vector3(-local_ivyLeafSize * sizeWeight, 0.0f, local_ivyLeafSize * sizeWeight);
                            tmpVertex.pos  = rotateAroundAxis(tmpVertex.pos, center, new Vector3(0.0f, 0.0f, 1.0f), theta);
                            tmpVertex.pos  = rotateAroundAxis(tmpVertex.pos, center, new Vector3(0.0f, 1.0f, 0.0f), phi);
                            tmpVertex.pos += new Vector3(Random.Range(-0.5f, 0.5f), Random.Range(-0.5f, 0.5f), Random.Range(-0.5f, 0.5f)).normalized *local_ivyLeafSize *sizeWeight * 0.5f;
                            vertices.Add(tmpVertex);

                            tmpVertex      = new BasicVertex();
                            tmpVertex.pos  = center + new Vector3(local_ivyLeafSize * sizeWeight, 0.0f, local_ivyLeafSize * sizeWeight);
                            tmpVertex.pos  = rotateAroundAxis(tmpVertex.pos, center, new Vector3(0.0f, 0.0f, 1.0f), theta);
                            tmpVertex.pos  = rotateAroundAxis(tmpVertex.pos, center, new Vector3(0.0f, 1.0f, 0.0f), phi);
                            tmpVertex.pos += new Vector3(Random.Range(-0.5f, 0.5f), Random.Range(-0.5f, 0.5f), Random.Range(-0.5f, 0.5f)).normalized *local_ivyLeafSize *sizeWeight * 0.5f;
                            vertices.Add(tmpVertex);

                            tmpVertex      = new BasicVertex();
                            tmpVertex.pos  = center + new Vector3(-local_ivyLeafSize * sizeWeight, 0.0f, -local_ivyLeafSize * sizeWeight);
                            tmpVertex.pos  = rotateAroundAxis(tmpVertex.pos, center, new Vector3(0.0f, 0.0f, 1.0f), theta);
                            tmpVertex.pos  = rotateAroundAxis(tmpVertex.pos, center, new Vector3(0.0f, 1.0f, 0.0f), phi);
                            tmpVertex.pos += new Vector3(Random.Range(-0.5f, 0.5f), Random.Range(-0.5f, 0.5f), Random.Range(-0.5f, 0.5f)).normalized *local_ivyLeafSize *sizeWeight * 0.5f;
                            vertices.Add(tmpVertex);

                            tmpVertex      = new BasicVertex();
                            tmpVertex.pos  = center + new Vector3(local_ivyLeafSize * sizeWeight, 0.0f, -local_ivyLeafSize * sizeWeight);
                            tmpVertex.pos  = rotateAroundAxis(tmpVertex.pos, center, new Vector3(0.0f, 0.0f, 1.0f), theta);
                            tmpVertex.pos  = rotateAroundAxis(tmpVertex.pos, center, new Vector3(0.0f, 1.0f, 0.0f), phi);
                            tmpVertex.pos += new Vector3(Random.Range(-0.5f, 0.5f), Random.Range(-0.5f, 0.5f), Random.Range(-0.5f, 0.5f)).normalized *local_ivyLeafSize *sizeWeight * 0.5f;
                            vertices.Add(tmpVertex);


                            //create texCoords
                            BasicTexCoord tmpTexCoord = new BasicTexCoord();
                            tmpTexCoord.uv = new Vector2(0.0f, 1.0f);
                            texCoords.Add(tmpTexCoord);

                            tmpTexCoord    = new BasicTexCoord();
                            tmpTexCoord.uv = new Vector2(1.0f, 1.0f);
                            texCoords.Add(tmpTexCoord);

                            tmpTexCoord    = new BasicTexCoord();
                            tmpTexCoord.uv = new Vector2(0.0f, 0.0f);
                            texCoords.Add(tmpTexCoord);

                            tmpTexCoord    = new BasicTexCoord();
                            tmpTexCoord.uv = new Vector2(1.0f, 0.0f);
                            texCoords.Add(tmpTexCoord);


                            //create triangle
                            BasicTriangle tmpTriangle = new BasicTriangle();
                            tmpTriangle.matid = 1;

                            float _probability = Random.value;
                            if (_probability * weight > leafProbability)
                            {
                                tmpTriangle.matid = 2;
                            }

                            tmpTriangle.v0id = (uint)vertices.Count - 1;
                            tmpTriangle.v1id = (uint)vertices.Count - 3;
                            tmpTriangle.v2id = (uint)vertices.Count - 2;

                            tmpTriangle.t0id = (uint)vertices.Count - 1;
                            tmpTriangle.t1id = (uint)vertices.Count - 3;
                            tmpTriangle.t2id = (uint)vertices.Count - 2;

                            triangles.Add(tmpTriangle);

                            BasicTriangle tmpTriangle2 = new BasicTriangle();
                            tmpTriangle2.matid = tmpTriangle.matid;

                            tmpTriangle2.v0id = (uint)vertices.Count - 2;
                            tmpTriangle2.v1id = (uint)vertices.Count - 0;
                            tmpTriangle2.v2id = (uint)vertices.Count - 1;

                            tmpTriangle2.t0id = (uint)vertices.Count - 2;
                            tmpTriangle2.t1id = (uint)vertices.Count - 0;
                            tmpTriangle2.t2id = (uint)vertices.Count - 1;

                            triangles.Add(tmpTriangle2);
                        }
                    }
                }
            }



            //branches
            foreach (var root in roots)
            {
                //process only roots with more than one node
                if (root.nodes.Count == 1)
                {
                    continue;
                }


                //branch diameter depends on number of parents
                float local_ivyBranchDiameter = 1.0f / (float)(root.parents + 1) + 1.0f;


                for (int node = 0; node < root.nodes.Count - 1; node++)
                {
                    //weight depending on ratio of node length to total length
                    float weight = root.nodes[node].length / root.nodes[root.nodes.Count - 1].length;


                    //create trihedral vertices
                    Vector3 up = new Vector3(0.0f, -1.0f, 0.0f);

                    Vector3 basis = (root.nodes[node + 1].pos - root.nodes[node].pos).normalized;

                    Vector3 b0 = Vector3.Cross(up, basis).normalized *local_ivyBranchDiameter *local_ivyBranchSize *(1.3f - weight) + root.nodes[node].pos;

                    Vector3 b1 = rotateAroundAxis(b0, root.nodes[node].pos, basis, 2.09f);

                    Vector3 b2 = rotateAroundAxis(b0, root.nodes[node].pos, basis, 4.18f);

                    //create vertices
                    BasicVertex tmpVertex = new BasicVertex();
                    tmpVertex.pos = b0;
                    vertices.Add(tmpVertex);

                    tmpVertex     = new BasicVertex();
                    tmpVertex.pos = b1;
                    vertices.Add(tmpVertex);

                    tmpVertex     = new BasicVertex();
                    tmpVertex.pos = b2;
                    vertices.Add(tmpVertex);


                    //create texCoords
                    BasicTexCoord tmpTexCoord = new BasicTexCoord();

                    float texV = (node % 2 == 0 ? 1.0f : 0.0f);

                    tmpTexCoord.uv = new Vector2(0.0f, texV);
                    texCoords.Add(tmpTexCoord);

                    tmpTexCoord    = new BasicTexCoord();
                    tmpTexCoord.uv = new Vector2(0.3f, texV);
                    texCoords.Add(tmpTexCoord);

                    tmpTexCoord    = new BasicTexCoord();
                    tmpTexCoord.uv = new Vector2(0.6f, texV);
                    texCoords.Add(tmpTexCoord);


                    if (node == 0)
                    {
                        continue;
                    }


                    //create triangle
                    BasicTriangle tmpTriangle = new BasicTriangle();
                    tmpTriangle.matid = 3;

                    tmpTriangle.v0id = (uint)vertices.Count - 3;
                    tmpTriangle.v1id = (uint)vertices.Count - 0;
                    tmpTriangle.v2id = (uint)vertices.Count - 4;

                    tmpTriangle.t0id = (uint)vertices.Count - 3;
                    tmpTriangle.t1id = (uint)vertices.Count - 0;
                    tmpTriangle.t2id = (uint)vertices.Count - 4;

                    triangles.Add(tmpTriangle);

                    tmpTriangle       = new BasicTriangle();
                    tmpTriangle.matid = 3;
                    tmpTriangle.v0id  = (uint)vertices.Count - 4;
                    tmpTriangle.v1id  = (uint)vertices.Count - 0;
                    tmpTriangle.v2id  = (uint)vertices.Count - 1;

                    tmpTriangle.t0id = (uint)vertices.Count - 4;
                    tmpTriangle.t1id = (uint)vertices.Count - 0;
                    tmpTriangle.t2id = (uint)vertices.Count - 1;

                    triangles.Add(tmpTriangle);

                    tmpTriangle       = new BasicTriangle();
                    tmpTriangle.matid = 3;
                    tmpTriangle.v0id  = (uint)vertices.Count - 4;
                    tmpTriangle.v1id  = (uint)vertices.Count - 1;
                    tmpTriangle.v2id  = (uint)vertices.Count - 5;

                    tmpTriangle.t0id = (uint)vertices.Count - 4;
                    tmpTriangle.t1id = (uint)vertices.Count - 1;
                    tmpTriangle.t2id = (uint)vertices.Count - 5;

                    triangles.Add(tmpTriangle);

                    tmpTriangle       = new BasicTriangle();
                    tmpTriangle.matid = 3;
                    tmpTriangle.v0id  = (uint)vertices.Count - 5;
                    tmpTriangle.v1id  = (uint)vertices.Count - 1;
                    tmpTriangle.v2id  = (uint)vertices.Count - 2;

                    tmpTriangle.t0id = (uint)vertices.Count - 5;
                    tmpTriangle.t1id = (uint)vertices.Count - 1;
                    tmpTriangle.t2id = (uint)vertices.Count - 2;

                    triangles.Add(tmpTriangle);

                    tmpTriangle       = new BasicTriangle();
                    tmpTriangle.matid = 3;
                    tmpTriangle.v0id  = (uint)vertices.Count - 5;
                    tmpTriangle.v1id  = (uint)vertices.Count - 2;
                    tmpTriangle.v2id  = (uint)vertices.Count - 0;

                    tmpTriangle.t0id = (uint)vertices.Count - 5;
                    tmpTriangle.t1id = (uint)vertices.Count - 2;
                    tmpTriangle.t2id = (uint)vertices.Count - 0;

                    triangles.Add(tmpTriangle);

                    tmpTriangle       = new BasicTriangle();
                    tmpTriangle.matid = 3;
                    tmpTriangle.v0id  = (uint)vertices.Count - 5;
                    tmpTriangle.v1id  = (uint)vertices.Count - 0;
                    tmpTriangle.v2id  = (uint)vertices.Count - 3;

                    tmpTriangle.t0id = (uint)vertices.Count - 5;
                    tmpTriangle.t1id = (uint)vertices.Count - 0;
                    tmpTriangle.t2id = (uint)vertices.Count - 3;

                    triangles.Add(tmpTriangle);
                }
            }


            //initialize ivy mesh
            //loadTextures();

            prepareData();

            calculateVertexNormals();

            prepareData();

            //createDisplayList(true);
        }
Exemple #8
0
        /** one single grow iteration */
        public void grow()
        {
            //parameters that depend on the scene object bounding sphere
            float local_ivySize = IvyManager.SceneObjMesh.boundingSphereRadius * ivySize;

            float local_maxFloatLength = IvyManager.SceneObjMesh.boundingSphereRadius * maxFloatLength;


            //normalize weights of influence
            float sum = primaryWeight + randomWeight + adhesionWeight;

            primaryWeight  /= sum;
            randomWeight   /= sum;
            adhesionWeight /= sum;


            //lets grow
            foreach (var root in roots)
            {
                //process only roots that are alive
                if (!root.alive)
                {
                    continue;
                }

                IvyNode lastnode = root.nodes[root.nodes.Count - 1];
                //let the ivy die, if the maximum float length is reached
                if (lastnode.floatingLength > local_maxFloatLength)
                {
                    root.alive = false;
                }


                //grow vectors: primary direction, random influence, and adhesion of scene objectss

                //primary vector = weighted sum of previous grow vectors
                Vector3 primaryVector = lastnode.primaryDir;

                //random influence plus a little upright vector
                Vector3 randomVector = (new Vector3(Random.Range(-0.5f, 0.5f), Random.Range(-0.5f, 0.5f), Random.Range(-0.5f, 0.5f)) + new Vector3(0.0f, 0.2f, 0.0f)).normalized;

                //adhesion influence to the nearest triangle = weighted sum of previous adhesion vectors
                Vector3 adhesionVector = computeAdhesion(lastnode.pos);

                //compute grow vector
                Vector3 growVector = local_ivySize * (primaryVector * primaryWeight + randomVector * randomWeight + adhesionVector * adhesionWeight);


                //gravity influence

                //compute gravity vector
                Vector3 gravityVector = local_ivySize * new Vector3(0.0f, -1.0f, 0.0f) * gravityWeight;

                //gravity depends on the floating length
                gravityVector *= Mathf.Pow(lastnode.floatingLength / local_maxFloatLength, 0.7f);


                //next possible ivy node

                //climbing state of that ivy node, will be set during collision detection
                bool climbing = false;

                //compute position of next ivy node
                Vector3 newPos = lastnode.pos + growVector + gravityVector;

                //combine alive state with result of the collision detection, e.g. let the ivy die in case of a collision detection problem
                root.alive = root.alive && computeCollision(lastnode.pos, ref newPos, ref climbing);

                //update grow vector due to a changed newPos
                growVector = newPos - lastnode.pos - gravityVector;


                //create next ivy node
                IvyNode tmpNode = new IvyNode();

                tmpNode.pos = newPos;

                tmpNode.primaryDir = (0.5f * lastnode.primaryDir + 0.5f * growVector.normalized).normalized;

                tmpNode.adhesionVector = adhesionVector;

                tmpNode.length = lastnode.length + (newPos - lastnode.pos).magnitude;

                tmpNode.floatingLength = climbing ? 0.0f : lastnode.floatingLength + (newPos - lastnode.pos).magnitude;

                tmpNode.climb = climbing;

                root.nodes.Add(tmpNode);
            }



            //lets produce child ivys
            foreach (var root in roots)
            {
                //process only roots that are alive
                if (!root.alive)
                {
                    continue;
                }

                //process only roots up to hierarchy level 3, results in a maximum hierarchy level of 4
                if (root.parents > 3)
                {
                    continue;
                }


                //add child ivys on existing ivy nodes
                foreach (var node in root.nodes)
                {
                    //weight depending on ratio of node length to total length
                    float weight = 1.0f - (Mathf.Cos(node.length / root.nodes[root.nodes.Count - 1].length * 2.0f * Mathf.PI) * 0.5f + 0.5f);

                    //random influence
                    float probability = Random.value;

                    if (probability * weight > branchingProbability)
                    {
                        //new ivy node
                        IvyNode tmpNode = new IvyNode();
                        tmpNode.pos            = node.pos;
                        tmpNode.primaryDir     = new Vector3(0.0f, 1.0f, 0.0f);
                        tmpNode.adhesionVector = new Vector3(0.0f, 0.0f, 0.0f);
                        tmpNode.length         = 0.0f;
                        tmpNode.floatingLength = node.floatingLength;
                        tmpNode.climb          = true;


                        //new ivy root
                        IvyRoot tmpRoot = new IvyRoot();
                        tmpRoot.nodes.Add(tmpNode);
                        tmpRoot.alive   = true;
                        tmpRoot.parents = root.parents + 1;
                        roots.Add(tmpRoot);


                        //limit the branching to only one new root per iteration, so return
                        return;
                    }
                }
            }
        }