Skip to content
Snippets Groups Projects
add_curve_ivygen.py 25.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • Andrew Hale's avatar
    Andrew Hale committed
    # ##### BEGIN GPL LICENSE BLOCK #####
    #
    #  This program is free software; you can redistribute it and/or
    #  modify it under the terms of the GNU General Public License
    #  as published by the Free Software Foundation; either version 2
    #  of the License, or (at your option) any later version.
    #
    #  This program is distributed in the hope that it will be useful,
    #  but WITHOUT ANY WARRANTY; without even the implied warranty of
    #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    #  GNU General Public License for more details.
    #
    #  You should have received a copy of the GNU General Public License
    #  along with this program; if not, write to the Free Software Foundation,
    #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    #
    # ##### END GPL LICENSE BLOCK #####
    
    # <pep8-80 compliant>
    
    bl_info = {
        "name": "IvyGen",
        "author": "testscreenings, PKHG, TrumanBlending",
    
    Andrew Hale's avatar
    Andrew Hale committed
        "version": (0, 1, 1),
    
        "blender": (2, 59, 0),
    
    Andrew Hale's avatar
    Andrew Hale committed
        "location": "View3D > Add > Curve",
    
        "description": "Adds generated ivy to a mesh object starting "
    
    Andrew Hale's avatar
    Andrew Hale committed
        "warning": "",
    
        "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
    
    Andrew Hale's avatar
    Andrew Hale committed
                    "Scripts/Curve/Ivy_Gen",
    
    Andrew Hale's avatar
    Andrew Hale committed
    
    
    import bpy
    from bpy.props import FloatProperty, IntProperty, BoolProperty
    from mathutils import Vector, Matrix
    from collections import deque
    from math import pow, cos, pi, atan2
    from random import random as rand_val, seed as rand_seed
    import time
    
    
    def createIvyGeometry(IVY, growLeaves):
    
        """Create the curve geometry for IVY"""
    
    Andrew Hale's avatar
    Andrew Hale committed
        # Compute the local size and the gauss weight filter
        #local_ivyBranchSize = IVY.ivyBranchSize  # * radius * IVY.ivySize
    
        gaussWeight = (1.0, 2.0, 4.0, 7.0, 9.0, 10.0, 9.0, 7.0, 4.0, 2.0, 1.0)
    
    Andrew Hale's avatar
    Andrew Hale committed
    
        # Create a new curve and intialise it
        curve = bpy.data.curves.new("IVY", type='CURVE')
        curve.dimensions = '3D'
        curve.bevel_depth = 1
    
        curve.fill_mode = 'FULL'
    
    Andrew Hale's avatar
    Andrew Hale committed
    
        if growLeaves:
            # Create the ivy leaves
            # Order location of the vertices
    
            signList = ((-1.0, +1.0),
                        (+1.0, +1.0),
                        (+1.0, -1.0),
                        (-1.0, -1.0),
                        )
    
    Andrew Hale's avatar
    Andrew Hale committed
    
            # Get the local size
            #local_ivyLeafSize = IVY.ivyLeafSize  # * radius * IVY.ivySize
    
            # Initialise the vertex and face lists
            vertList = deque()
    
            # Store the methods for faster calling
            addV = vertList.extend
            rotMat = Matrix.Rotation
    
        # Loop over all roots to generate its nodes
        for root in IVY.ivyRoots:
            # Only grow if more than one node
            numNodes = len(root.ivyNodes)
            if numNodes > 1:
                # Calculate the local radius
    
                local_ivyBranchRadius = 1.0 / (root.parents + 1) + 1.0
                prevIvyLength = 1.0 / root.ivyNodes[-1].length
    
    Andrew Hale's avatar
    Andrew Hale committed
                splineVerts = [ax for n in root.ivyNodes for ax in n.pos.to_4d()]
    
                radiusConstant = local_ivyBranchRadius * IVY.ivyBranchSize
                splineRadii = [radiusConstant * (1.3 - n.length * prevIvyLength)
                                                            for n in root.ivyNodes]
    
                # Add the poly curve and set coords and radii
                newSpline = curve.splines.new(type='POLY')
                newSpline.points.add(len(splineVerts) // 4 - 1)
                newSpline.points.foreach_set('co', splineVerts)
                newSpline.points.foreach_set('radius', splineRadii)
    
                # Loop over all nodes in the root
                for i, n in enumerate(root.ivyNodes):
                    for k in range(len(gaussWeight)):
                        idx = max(0, min(i + k - 5, numNodes - 1))
                        n.smoothAdhesionVector += (gaussWeight[k] *
                                                 root.ivyNodes[idx].adhesionVector)
                    n.smoothAdhesionVector /= 56.0
                    n.adhesionLength = n.smoothAdhesionVector.length
                    n.smoothAdhesionVector.normalize()
    
                    if growLeaves and (i < numNodes - 1):
                        node = root.ivyNodes[i]
                        nodeNext = root.ivyNodes[i + 1]
    
    
    Bastien Montagne's avatar
    Bastien Montagne committed
                        # Find the weight and normalize the smooth adhesion vector
    
    Andrew Hale's avatar
    Andrew Hale committed
                        weight = pow(node.length * prevIvyLength, 0.7)
    
                        # Calculate the ground ivy and the new weight
                        groundIvy = max(0.0, -node.smoothAdhesionVector.z)
                        weight += groundIvy * pow(1 - node.length *
                                                                  prevIvyLength, 2)
    
                        # Find the alignment weight
                        alignmentWeight = node.adhesionLength
    
                        # Calculate the needed angles
                        phi = atan2(node.smoothAdhesionVector.y,
    
    Andrew Hale's avatar
    Andrew Hale committed
    
                        theta = (0.5 *
                            node.smoothAdhesionVector.angle(Vector((0, 0, -1)), 0))
    
                        # Find the size weight
                        sizeWeight = 1.5 - (cos(2 * pi * weight) * 0.5 + 0.5)
    
                        # Randomise the angles
                        phi += (rand_val() - 0.5) * (1.3 - alignmentWeight)
                        theta += (rand_val() - 0.5) * (1.1 - alignmentWeight)
    
                        # Calculate the leaf size an append the face to the list
                        leafSize = IVY.ivyLeafSize * sizeWeight
    
                        for j in range(10):
                            # Generate the probability
                            probability = rand_val()
    
                            # If we need to grow a leaf, do so
                            if (probability * weight) > IVY.leafProbability:
    
                                # Generate the random vector
                                randomVector = Vector((rand_val() - 0.5,
    
    Andrew Hale's avatar
    Andrew Hale committed
    
                                # Find the leaf center
    
                                center = (node.pos.lerp(nodeNext.pos, j / 10.0) +
                                                   IVY.ivyLeafSize * randomVector)
    
    Andrew Hale's avatar
    Andrew Hale committed
    
                                # For each of the verts, rotate/scale and append
                                basisVecX = Vector((1, 0, 0))
                                basisVecY = Vector((0, 1, 0))
    
                                horiRot = rotMat(theta, 3, 'X')
                                vertRot = rotMat(phi, 3, 'Z')
    
                                basisVecX.rotate(horiRot)
                                basisVecY.rotate(horiRot)
    
                                basisVecX.rotate(vertRot)
                                basisVecY.rotate(vertRot)
    
                                basisVecX *= leafSize
                                basisVecY *= leafSize
    
                                addV([k1 * basisVecX + k2 * basisVecY + center for
                                                               k1, k2 in signList])
    
        # Add the object and link to scene
        newCurve = bpy.data.objects.new("IVY_Curve", curve)
        bpy.context.scene.objects.link(newCurve)
    
        if growLeaves:
            faceList = [[4 * i + l for l in range(4)] for i in
                                                         range(len(vertList) // 4)]
    
            # Generate the new leaf mesh and link
            me = bpy.data.meshes.new('IvyLeaf')
            me.from_pydata(vertList, [], faceList)
            me.update(calc_edges=True)
            ob = bpy.data.objects.new('IvyLeaf', me)
            bpy.context.scene.objects.link(ob)
    
    
    Campbell Barton's avatar
    Campbell Barton committed
            me.uv_textures.new("Leaves")
    
    Andrew Hale's avatar
    Andrew Hale committed
    
            # Set the uv texture coords
    
            # TODO, this is non-functional, default uvs are ok?
            '''
    
    Andrew Hale's avatar
    Andrew Hale committed
            for d in tex.data:
                uv1, uv2, uv3, uv4 = signList
    
    Andrew Hale's avatar
    Andrew Hale committed
    
            ob.parent = newCurve
    
    
    Andrew Hale's avatar
    Andrew Hale committed
    def computeBoundingSphere(ob):
        # Get the mesh data
        me = ob.data
        # Intialise the center
    
    Andrew Hale's avatar
    Andrew Hale committed
        # Add all vertex coords
        for v in me.vertices:
            center += v.co
        # Average over all verts
        center /= len(me.vertices)
        # Create the iterator and find its max
        length_iter = ((center - v.co).length for v in me.vertices)
        radius = max(length_iter)
        return radius
    
    Andrew Hale's avatar
    Andrew Hale committed
    
    
    class IvyNode:
        """ The basic class used for each point on the ivy which is grown."""
        __slots__ = ('pos', 'primaryDir', 'adhesionVector', 'adhesionLength',
                     'smoothAdhesionVector', 'length', 'floatingLength', 'climb')
    
        def __init__(self):
            self.pos = Vector((0, 0, 0))
            self.primaryDir = Vector((0, 0, 1))
            self.adhesionVector = Vector((0, 0, 0))
            self.smoothAdhesionVector = Vector((0, 0, 0))
            self.length = 0.0001
            self.floatingLength = 0.0
            self.climb = True
    
    
    class IvyRoot:
        """ The class used to hold all ivy nodes growing from this root point."""
        __slots__ = ('ivyNodes', 'alive', 'parents')
    
        def __init__(self):
            self.ivyNodes = deque()
            self.alive = True
            self.parents = 0
    
    
    class Ivy:
        """ The class holding all parameters and ivy roots."""
        __slots__ = ('ivyRoots', 'primaryWeight', 'randomWeight',
                     'gravityWeight', 'adhesionWeight', 'branchingProbability',
                     'leafProbability', 'ivySize', 'ivyLeafSize', 'ivyBranchSize',
                     'maxFloatLength', 'maxAdhesionDistance', 'maxLength')
    
        def __init__(self,
                     primaryWeight=0.5,
                     randomWeight=0.2,
                     gravityWeight=1.0,
                     adhesionWeight=0.1,
                     branchingProbability=0.05,
                     leafProbability=0.35,
                     ivySize=0.02,
                     ivyLeafSize=0.02,
                     ivyBranchSize=0.001,
                     maxFloatLength=0.5,
                     maxAdhesionDistance=1.0):
    
            self.ivyRoots = deque()
            self.primaryWeight = primaryWeight
            self.randomWeight = randomWeight
            self.gravityWeight = gravityWeight
            self.adhesionWeight = adhesionWeight
            self.branchingProbability = 1 - branchingProbability
            self.leafProbability = 1 - leafProbability
            self.ivySize = ivySize
            self.ivyLeafSize = ivyLeafSize
            self.ivyBranchSize = ivyBranchSize
            self.maxFloatLength = maxFloatLength
            self.maxAdhesionDistance = maxAdhesionDistance
            self.maxLength = 0.0
    
    
    Bastien Montagne's avatar
    Bastien Montagne committed
            # Normalize all the weights only on intialisation
    
    Andrew Hale's avatar
    Andrew Hale committed
            sum = self.primaryWeight + self.randomWeight + self.adhesionWeight
            self.primaryWeight /= sum
            self.randomWeight /= sum
            self.adhesionWeight /= sum
    
        def seed(self, seedPos):
            # Seed the Ivy by making a new root and first node
            tmpRoot = IvyRoot()
            tmpIvy = IvyNode()
            tmpIvy.pos = seedPos
    
            tmpRoot.ivyNodes.append(tmpIvy)
            self.ivyRoots.append(tmpRoot)
    
        def grow(self, ob):
            # Determine the local sizes
            #local_ivySize = self.ivySize  # * radius
            #local_maxFloatLength = self.maxFloatLength  # * radius
            #local_maxAdhesionDistance = self.maxAdhesionDistance  # * radius
    
            for root in self.ivyRoots:
                # Make sure the root is alive, if not, skip
                if not root.alive:
                    continue
    
                # Get the last node in the current root
                prevIvy = root.ivyNodes[-1]
    
                # If the node is floating for too long, kill the root
                if prevIvy.floatingLength > self.maxFloatLength:
                    root.alive = False
    
                # Set the primary direction from the last node
                primaryVector = prevIvy.primaryDir
    
    
    Bastien Montagne's avatar
    Bastien Montagne committed
                # Make the random vector and normalize
    
    Andrew Hale's avatar
    Andrew Hale committed
                randomVector = Vector((rand_val() - 0.5, rand_val() - 0.5,
                                       rand_val() - 0.5)) + Vector((0, 0, 0.2))
                randomVector.normalize()
    
                # Calculate the adhesion vector
                adhesionVector = adhesion(prevIvy.pos, ob,
                                                          self.maxAdhesionDistance)
    
                # Calculate the growing vector
                growVector = self.ivySize * (primaryVector * self.primaryWeight +
                                              randomVector * self.randomWeight +
                                              adhesionVector * self.adhesionWeight)
    
                # Find the gravity vector
                gravityVector = (self.ivySize * self.gravityWeight *
                                                                Vector((0, 0, -1)))
                gravityVector *= pow(prevIvy.floatingLength / self.maxFloatLength,
                                     0.7)
    
                # Determine the new position vector
                newPos = prevIvy.pos + growVector + gravityVector
    
                # Check for collisions with the object
                climbing = collision(ob, prevIvy.pos, newPos)
    
                # Update the growing vector for any collisions
                growVector = newPos - prevIvy.pos - gravityVector
                growVector.normalize()
    
                # Create a new IvyNode and set its properties
                tmpNode = IvyNode()
                tmpNode.climb = climbing
                tmpNode.pos = newPos
                tmpNode.primaryDir = prevIvy.primaryDir.lerp(growVector, 0.5)
                tmpNode.primaryDir.normalize()
                tmpNode.adhesionVector = adhesionVector
                tmpNode.length = prevIvy.length + (newPos - prevIvy.pos).length
    
                if tmpNode.length > self.maxLength:
                    self.maxLength = tmpNode.length
    
                # If the node isn't climbing, update it's floating length
                # Otherwise set it to 0
                if not climbing:
                    tmpNode.floatingLength = prevIvy.floatingLength + (newPos -
                                                                prevIvy.pos).length
                else:
                    tmpNode.floatingLength = 0.0
    
                root.ivyNodes.append(tmpNode)
    
            # Loop through all roots to check if a new root is generated
            for root in self.ivyRoots:
                # Check the root is alive and isn't at high level of recursion
                if (root.parents > 3) or (not root.alive):
                    continue
    
                # Check to make sure there's more than 1 node
                if len(root.ivyNodes) > 1:
                    # Loop through all nodes in root to check if new root is grown
                    for node in root.ivyNodes:
                        # Set the last node of the root and find the weighting
                        prevIvy = root.ivyNodes[-1]
                        weight = 1.0 - (cos(2.0 * pi * node.length /
                                            prevIvy.length) * 0.5 + 0.5)
    
                        probability = rand_val()
    
                        # Check if a new root is grown and if so, set its values
                        if (probability * weight > self.branchingProbability):
                            tmpNode = IvyNode()
                            tmpNode.pos = node.pos
                            tmpNode.floatingLength = node.floatingLength
    
                            tmpRoot = IvyRoot()
                            tmpRoot.parents = root.parents + 1
    
                            tmpRoot.ivyNodes.append(tmpNode)
                            self.ivyRoots.append(tmpRoot)
                            return
    
    
    def adhesion(loc, ob, max_l):
        # Get transfor vector and transformed loc
        tran_mat = ob.matrix_world.inverted()
        tran_loc = tran_mat * loc
    
        # Compute the adhesion vector by finding the nearest point
        nearest_result = ob.closest_point_on_mesh(tran_loc, max_l)
    
    Andrew Hale's avatar
    Andrew Hale committed
            # Compute the distance to the nearest point
    
            adhesion_vector = ob.matrix_world * nearest_result[1] - loc
    
    Andrew Hale's avatar
    Andrew Hale committed
            distance = adhesion_vector.length
            # If it's less than the maximum allowed and not 0, continue
            if distance:
                # Compute the direction vector between the closest point and loc
                adhesion_vector.normalize()
                adhesion_vector *= 1.0 - distance / max_l
    
                #adhesion_vector *= getFaceWeight(ob.data, nearest_result[3])
    
    Andrew Hale's avatar
    Andrew Hale committed
        return adhesion_vector
    
    
    def collision(ob, pos, new_pos):
        # Check for collision with the object
        climbing = False
    
        # Transform vecs
        tran_mat = ob.matrix_world.inverted()
        tran_pos = tran_mat * pos
        tran_new_pos = tran_mat * new_pos
    
        tran_dir = tran_new_pos - tran_pos
    
    Andrew Hale's avatar
    Andrew Hale committed
    
    
        ray_result = ob.ray_cast(tran_pos, tran_dir, tran_dir.length)
    
    Andrew Hale's avatar
    Andrew Hale committed
        # If there's a collision we need to check it
    
    Andrew Hale's avatar
    Andrew Hale committed
            # Check whether the collision is going into the object
    
            if tran_dir.dot(ray_result[2]) < 0.0:
    
    Andrew Hale's avatar
    Andrew Hale committed
                # Find projection of the piont onto the plane
                p0 = tran_new_pos - (tran_new_pos -
    
                                              ray_result[1]).project(ray_result[2])
    
    Andrew Hale's avatar
    Andrew Hale committed
                # Reflect in the plane
                tran_new_pos += 2 * (p0 - tran_new_pos)
                new_pos *= 0
                new_pos += ob.matrix_world * tran_new_pos
                climbing = True
        return climbing
    
    
    class IvyGen(bpy.types.Operator):
        bl_idname = "curve.ivy_gen"
        bl_label = "IvyGen"
        bl_options = {'REGISTER', 'UNDO'}
    
        maxIvyLength = FloatProperty(name="Max Ivy Length",
    
                        description="Maximum ivy length in Blender Units",
    
    Andrew Hale's avatar
    Andrew Hale committed
                        default=1.0,
                        min=0.0,
                        soft_max=3.0,
                        subtype='DISTANCE',
                        unit='LENGTH')
        primaryWeight = FloatProperty(name="Primary Weight",
    
                        description="Weighting given to the current direction",
    
    Andrew Hale's avatar
    Andrew Hale committed
                        default=0.5,
                        min=0.0,
                        soft_max=1.0)
        randomWeight = FloatProperty(name="Random Weight",
    
                        description="Weighting given to the random direction",
    
    Andrew Hale's avatar
    Andrew Hale committed
                        default=0.2,
                        min=0.0,
                        soft_max=1.0)
        gravityWeight = FloatProperty(name="Gravity Weight",
    
                        description="Weighting given to the gravity direction",
    
    Andrew Hale's avatar
    Andrew Hale committed
                        default=1.0,
                        min=0.0,
                        soft_max=1.0)
        adhesionWeight = FloatProperty(name="Adhesion Weight",
    
                        description="Weighting given to the adhesion direction",
    
    Andrew Hale's avatar
    Andrew Hale committed
                        default=0.1,
                        min=0.0,
                        soft_max=1.0)
        branchingProbability = FloatProperty(name="Branching Probability",
    
                        description="Probability of a new branch forming",
    
    Andrew Hale's avatar
    Andrew Hale committed
                        default=0.05,
                        min=0.0,
                        soft_max=1.0)
        leafProbability = FloatProperty(name="Leaf Probability",
    
                        description="Probability of a leaf forming",
    
    Andrew Hale's avatar
    Andrew Hale committed
                        default=0.35,
                        min=0.0,
                        soft_max=1.0)
        ivySize = FloatProperty(name="Ivy Size",
    
                        description=("The length of an ivy segment in Blender"
    
    Andrew Hale's avatar
    Andrew Hale committed
                        default=0.02,
                        min=0.0,
                        soft_max=1.0,
                        precision=3)
        ivyLeafSize = FloatProperty(name="Ivy Leaf Size",
                        description="The size of the ivy leaves",
                        default=0.02,
                        min=0.0,
                        soft_max=0.5,
                        precision=3)
        ivyBranchSize = FloatProperty(name="Ivy Branch Size",
                        description="The size of the ivy branches",
                        default=0.001,
                        min=0.0,
                        soft_max=0.1,
                        precision=4)
        maxFloatLength = FloatProperty(name="Max Float Length",
    
                        description=("The maximum distance that a branch "
    
                                     "can live while floating"),
    
    Andrew Hale's avatar
    Andrew Hale committed
                        default=0.5,
                        min=0.0,
                        soft_max=1.0)
        maxAdhesionDistance = FloatProperty(name="Max Adhesion Length",
    
                        description=("The maximum distance that a branch "
    
                                     "will feel the effects of adhesion"),
    
    Andrew Hale's avatar
    Andrew Hale committed
                        default=1.0,
                        min=0.0,
                        soft_max=2.0,
                        precision=2)
    
        randomSeed = IntProperty(name="Random Seed",
    
                        description="The seed governing random generation",
    
    Andrew Hale's avatar
    Andrew Hale committed
                        default=0,
    
    Andrew Hale's avatar
    Andrew Hale committed
        maxTime = FloatProperty(name="Maximum Time",
    
                        description=("The maximum time to run the generation for "
                                     "in seconds generation (0.0 = Disabled)"),
    
    Andrew Hale's avatar
    Andrew Hale committed
                        default=0.0,
                        min=0.0,
                        soft_max=10)
        growLeaves = BoolProperty(name="Grow Leaves",
    
                        description="Grow leaves or not",
    
    Andrew Hale's avatar
    Andrew Hale committed
                        default=True)
        updateIvy = BoolProperty(name="Update Ivy", default=False)
    
        @classmethod
        def poll(self, context):
            # Check if there's an object and whether it's a mesh
            ob = context.active_object
    
            return ((ob is not None) and
                    (ob.type == 'MESH') and
                    (context.mode == 'OBJECT'))
    
        def invoke(self, context, event):
            self.updateIvy = True
            return self.execute(context)
    
    Andrew Hale's avatar
    Andrew Hale committed
    
        def execute(self, context):
    
            if not self.updateIvy:
                return {'PASS_THROUGH'}
    
            bpy.ops.object.mode_set(mode='EDIT', toggle=False)
            bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
    
    Andrew Hale's avatar
    Andrew Hale committed
    
    
            # Get the selected object
            ob = context.active_object
    
    Andrew Hale's avatar
    Andrew Hale committed
    
    
            # Compute bounding sphere radius
            #radius = computeBoundingSphere(ob)  # Not needed anymore
    
    Andrew Hale's avatar
    Andrew Hale committed
    
    
            # Get the seeding point
            seedPoint = context.scene.cursor_location
    
    Andrew Hale's avatar
    Andrew Hale committed
    
    
            rand_seed(self.randomSeed)
    
    Andrew Hale's avatar
    Andrew Hale committed
    
    
            # Make the new ivy
            IVY = Ivy(**self.as_keywords(ignore=('randomSeed', 'growLeaves',
                                      'maxIvyLength', 'maxTime', 'updateIvy')))
    
    Andrew Hale's avatar
    Andrew Hale committed
    
    
    Andrew Hale's avatar
    Andrew Hale committed
    
    
            checkTime = False
            maxLength = self.maxIvyLength  # * radius
    
    Andrew Hale's avatar
    Andrew Hale committed
    
    
            # If we need to check time set the flag
            if self.maxTime != 0.0:
                checkTime = True
    
    Andrew Hale's avatar
    Andrew Hale committed
    
    
            t = time.time()
            startPercent = 0.0
            checkAliveIter = [True, ]
    
    Andrew Hale's avatar
    Andrew Hale committed
    
    
            # Grow until 200 roots is reached or backup counter exceeds limit
            while (any(checkAliveIter) and
                   (IVY.maxLength < maxLength) and
                   (not checkTime or (time.time() - t < self.maxTime))):
                # Grow the ivy for this iteration
                IVY.grow(ob)
    
    Andrew Hale's avatar
    Andrew Hale committed
    
    
                # Print the proportion of ivy growth to console
                if (IVY.maxLength / maxLength * 100) > 10 * startPercent // 10:
                    print('%0.2f%% of Ivy nodes have grown' %
                                             (IVY.maxLength / maxLength * 100))
                    startPercent += 10
                    if IVY.maxLength / maxLength > 1:
                        print("Halting Growth")
    
    Andrew Hale's avatar
    Andrew Hale committed
    
    
                # Make an iterator to check if all are alive
                checkAliveIter = (r.alive for r in IVY.ivyRoots)
    
    Andrew Hale's avatar
    Andrew Hale committed
    
    
            # Create the curve and leaf geometry
            createIvyGeometry(IVY, self.growLeaves)
            print("Geometry Generation Complete")
    
    Andrew Hale's avatar
    Andrew Hale committed
    
    
            print("Ivy generated in %0.2f s" % (time.time() - t))
    
    Andrew Hale's avatar
    Andrew Hale committed
    
    
    Andrew Hale's avatar
    Andrew Hale committed
    
    
    Andrew Hale's avatar
    Andrew Hale committed
    
        def draw(self, context):
            layout = self.layout
    
    Thomas Dinges's avatar
    Thomas Dinges committed
            layout.prop(self, 'updateIvy', icon='CURVE_DATA')
    
    Andrew Hale's avatar
    Andrew Hale committed
    
    
    Thomas Dinges's avatar
    Thomas Dinges committed
            properties = layout.operator('curve.ivy_gen', text="Add New Ivy")
    
    Andrew Hale's avatar
    Andrew Hale committed
            properties.randomSeed = self.randomSeed
            properties.maxTime = self.maxTime
            properties.maxIvyLength = self.maxIvyLength
            properties.ivySize = self.ivySize
            properties.maxFloatLength = self.maxFloatLength
            properties.maxAdhesionDistance = self.maxAdhesionDistance
            properties.primaryWeight = self.primaryWeight
            properties.randomWeight = self.randomWeight
            properties.gravityWeight = self.gravityWeight
            properties.adhesionWeight = self.adhesionWeight
            properties.branchingProbability = self.branchingProbability
            properties.leafProbability = self.leafProbability
            properties.ivyBranchSize = self.ivyBranchSize
            properties.ivyLeafSize = self.ivyLeafSize
    
    Andrew Hale's avatar
    Andrew Hale committed
            properties.updateIvy = True
    
    Andrew Hale's avatar
    Andrew Hale committed
    
    
    Thomas Dinges's avatar
    Thomas Dinges committed
            prop_def = layout.operator('curve.ivy_gen', text="Add New Default Ivy")
    
    Andrew Hale's avatar
    Andrew Hale committed
            prop_def.updateIvy = True
    
    Andrew Hale's avatar
    Andrew Hale committed
    
    
    Thomas Dinges's avatar
    Thomas Dinges committed
            layout.prop(self, 'growLeaves')
    
    Andrew Hale's avatar
    Andrew Hale committed
    
            box = layout.box()
    
    Thomas Dinges's avatar
    Thomas Dinges committed
            box.label("Generation Settings:")
            box.prop(self, 'randomSeed')
            box.prop(self, 'maxTime')
    
    Andrew Hale's avatar
    Andrew Hale committed
            box = layout.box()
    
    Thomas Dinges's avatar
    Thomas Dinges committed
            box.label("Size Settings:")
            box.prop(self, 'maxIvyLength')
            box.prop(self, 'ivySize')
            box.prop(self, 'maxFloatLength')
            box.prop(self, 'maxAdhesionDistance')
    
    Andrew Hale's avatar
    Andrew Hale committed
            box = layout.box()
    
    Thomas Dinges's avatar
    Thomas Dinges committed
            box.label("Weight Settings:")
            box.prop(self, 'primaryWeight')
            box.prop(self, 'randomWeight')
            box.prop(self, 'gravityWeight')
            box.prop(self, 'adhesionWeight')
    
    Andrew Hale's avatar
    Andrew Hale committed
            box = layout.box()
    
    Thomas Dinges's avatar
    Thomas Dinges committed
            box.label("Branch Settings:")
            box.prop(self, 'branchingProbability')
            box.prop(self, 'ivyBranchSize')
    
    Andrew Hale's avatar
    Andrew Hale committed
    
            if self.growLeaves:
                box = layout.box()
    
    Thomas Dinges's avatar
    Thomas Dinges committed
                box.label("Leaf Settings:")
                box.prop(self, 'ivyLeafSize')
                box.prop(self, 'leafProbability')
    
    Andrew Hale's avatar
    Andrew Hale committed
    
    
    def menu_func(self, context):
        self.layout.operator(IvyGen.bl_idname, text="Add Ivy to Mesh",
    
    meta-androcto's avatar
    meta-androcto committed
                                                    icon='OUTLINER_DATA_CURVE').updateIvy = True
    
    Andrew Hale's avatar
    Andrew Hale committed
    
    
    def register():
        bpy.utils.register_module(__name__)
        bpy.types.INFO_MT_curve_add.append(menu_func)
    
    
    def unregister():
        bpy.types.INFO_MT_curve_add.remove(menu_func)
        bpy.utils.unregister_module(__name__)
    
    
    if __name__ == "__main__":
    
    Campbell Barton's avatar
    Campbell Barton committed
        register()