Skip to content
Snippets Groups Projects
utils.py 73.8 KiB
Newer Older
  • Learn to ignore specific revisions
  •                     downA = downAngle[n] + (-downAngleV[n] * (1 - (1 - br.offset) / (1 - baseSize)) ** 2)
                    else:
                        downA = downAngle[n]
                    if downA < (.5 * pi):
                        downA = sin(downA) ** 2
                        bL *= downA
    
                    bL *= 0.33
                    v *= (bD + bL)
    
                    bv = Vector((b[0], -b[1]))
                    cv = v - bv
                    a = atan2(cv[0], cv[1])
                    #rot_a.append(a)
    
    #                # add fill points at top  #experimental
    #                fillHeight = 1 - degrees(rotateV[3])#0.8
    #                if fillHeight < 1:
    #                    w = (p[0].offset - fillHeight) / (1- fillHeight)
    #                    prob_b = random() < w
    #                else:
    #                    prob_b = False
    #
    #                if (p[0].offset > fillHeight): #prob_b and (len(p) > 1):  ##(p[0].offset > fillHeight) and
    #                    childP.append(p[randint(0, len(p)-1)])
    #                    rot_a.append(bRotate)# + pi)
    
                    childP.append(p[idx])
                    rot_a.append(a)
    
                else:
                    idx = randint(0, len(p)-1)
                    childP.append(p[idx])
                #childP.append(p[idx])
    
            childP.extend(childP_L)
            rot_a.extend([0] * len(childP_L))
    
            oldRotate = 0
    
        for i, p in enumerate(childP):
    
            # Add a spline and set the coordinate of the first point.
            newSpline = cu.splines.new('BEZIER')
            cu.resolution_u = resU
            newPoint = newSpline.bezier_points[-1]
            newPoint.co = p.co
            tempPos = zAxis.copy()
            # If the -ve flag for downAngle is used we need a special formula to find it
    
            if useOldDownAngle:
                if downAngleV[n] < 0.0:
                    downV = downAngleV[n] * (1 - 2 * (.2 + .8 * ((1 - p.offset) / (1 - baseSize))))
                # Otherwise just find a random value
                else:
                    downV = uniform(-downAngleV[n], downAngleV[n])
    
                if downAngleV[n] < 0.0:
                    downV = uniform(-downAngleV[n], downAngleV[n])
                else:
                    downV = -downAngleV[n] * (1 - (1 - p.offset) / (1 - baseSize)) ** 2 #(110, 80) = (60, -50)
    
            if p.offset == 1:
                downRotMat = Matrix.Rotation(0, 3, 'X')
            else:
                downRotMat = Matrix.Rotation(downAngle[n] + downV, 3, 'X')
    
    
            # If the -ve flag for rotate is used we need to find which side of the stem the last child point was and then grow in the opposite direction.
            if rotate[n] < 0.0:
    
                oldRotate = -copysign(rotate[n], oldRotate)
    
            # Otherwise just generate a random number in the specified range
            else:
    
                oldRotate += rotate[n]
            bRotate = oldRotate + uniform(-rotateV[n], rotateV[n])
    
            if (n == 1) and (rMode == "rotate"):
                bRotate = rot_a[i]
    
            rotMat = Matrix.Rotation(bRotate, 3, 'Z')
    
    
            # Rotate the direction of growth and set the new point coordinates
    
            tempPos.rotate(downRotMat)
    
    
            #use quat angle
            if (rMode == "rotate") and (n == 1) and (p.offset != 1):
                if useParentAngle:
                    edir = p.quat.to_euler('XYZ', Euler((0, 0, bRotate), 'XYZ'))
                    edir[0] = 0
                    edir[1] = 0
    
                    edir[2] = -edir[2]
                    tempPos.rotate(edir)
    
                    dec = declination(p.quat)
                    tempPos.rotate(Matrix.Rotation(radians(dec), 3, 'X'))
    
                    edir[2] = -edir[2]
                    tempPos.rotate(edir)
            else:
                tempPos.rotate(p.quat)
    
    
    
            # Make length variation inversely proportional to segSplits
            #lenV = (1 - min(segSplits[n], 1)) * lengthV[n]
    
            # Find branch length and the number of child stems.
            maxbL = scaleVal
            for l in length[:n+1]:
                maxbL *= l
            lMax = length[n] # * uniform(1 - lenV, 1 + lenV)
            if n == 1:
                lShape = shapeRatio(shape, (1 - p.stemOffset) / (1 - baseSize), custom=customShape)
            else:
                lShape = shapeRatio(shapeS, (1 - p.stemOffset) / (1 - baseSize))
            branchL = p.lengthPar * lMax * lShape
            childStems = branches[min(3, n + 1)] * (0.1 + 0.9 * (branchL / maxbL))
    
            # If this is the last level before leaves then we need to generate the child points differently
    
            if (storeN == levels - 1):
    
                    childStems = leaves * (0.1 + 0.9 * (branchL / maxbL)) * shapeRatio(leafDist, (1 - p.offset))
    
    
            #print("n=%d, levels=%d, n'=%d, childStems=%s"%(n, levels, storeN, childStems))
    
            # Determine the starting and ending radii of the stem using the tapering of the stem
    
            startRad = min((p.radiusPar[0] * ((branchL / p.lengthPar) ** ratioPower)) * radiusTweak[n], p.radiusPar[1])
            if p.offset == 1:
                startRad = p.radiusPar[1]
            endRad = (startRad * (1 - taper[n])) ** ratioPower
            startRad = max(startRad, minRadius)
            endRad = max(endRad, minRadius)
    
    
            # stem curvature
            curveVal = curve[n] / curveRes[n]
            curveVar = curveV[n] / curveRes[n]
    
            #curveVal = curveVal * (branchL / scaleVal)
    
    
            # Add the new stem to list of stems to grow and define which bone it will be parented to
            addstem(
    
                stemSpline(newSpline, curveVal, curveVar, attractUp[n], 0, curveRes[n], branchL / curveRes[n], childStems,
                           startRad, endRad, len(cu.splines) - 1, 0, p.quat))
    
            bone = roundBone(p.parBone, boneStep[n-1])
            if p.offset == 1:
                isend = True
            else:
                isend = False
            addsplinetobone((bone, isend))
    
    
    
    def perform_pruning(baseSize, baseSplits, childP, cu, currentMax, currentMin, currentScale, curve, curveBack, curveRes,
                        deleteSpline, forceSprout, handles, n, oldMax, orginalSplineToBone, originalCo, originalCurv,
                        originalCurvV, originalHandleL, originalHandleR, originalLength, originalSeg, prune, prunePowerHigh,
    
                        prunePowerLow, pruneRatio, pruneWidth, pruneBase, pruneWidthPeak, randState, ratio, scaleVal, segSplits,
                        splineToBone, splitAngle, splitAngleV, st, startPrune, branchDist, length, splitByLen, closeTip, nrings,
                        splitBias, splitHeight, attractOut, rMode, lengthV, taperCrown, boneStep, rotate, rotateV):
    
        while startPrune and ((currentMax - currentMin) > 0.005):
            setstate(randState)
    
            # If the search will halt after this iteration, then set the adjustment of stem length to take into account the pruning ratio
            if (currentMax - currentMin) < 0.01:
                currentScale = (currentScale - 1) * pruneRatio + 1
                startPrune = False
                forceSprout = True
            # Change the segment length of the stem by applying some scaling
            st.segL = originalLength * currentScale
            # To prevent millions of splines being created we delete any old ones and replace them with only their first points to begin the spline again
            if deleteSpline:
                for x in splineList:
                    cu.splines.remove(x.spline)
                newSpline = cu.splines.new('BEZIER')
                newPoint = newSpline.bezier_points[-1]
                newPoint.co = originalCo
                newPoint.handle_right = originalHandleR
                newPoint.handle_left = originalHandleL
                (newPoint.handle_left_type, newPoint.handle_right_type) = ('VECTOR', 'VECTOR')
                st.spline = newSpline
                st.curv = originalCurv
                st.curvV = originalCurvV
                st.seg = originalSeg
                st.p = newPoint
                newPoint.radius = st.radS
                splineToBone = orginalSplineToBone
    
            # Initialise the spline list for those contained in the current level of branching
            splineList = [st]
    
    
            #split length variation
            stemsegL = splineList[0].segL #initial segment length used for variation
            splineList[0].segL = stemsegL * uniform(1 - lengthV[n], 1 + lengthV[n]) #variation for first stem
    
    
            # For each of the segments of the stem which must be grown we have to add to each spline in splineList
            for k in range(curveRes[n]):
                # Make a copy of the current list to avoid continually adding to the list we're iterating over
                tempList = splineList[:]
    
                # print('Leng: ', len(tempList))
    
                #for curve variation
                if curveRes[n] > 1:
                    kp = (k / (curveRes[n] - 1)) # * 2
                else:
                    kp = 1.0
    
                #split bias
                splitValue = segSplits[n]
                if n == 0:
                    splitValue = ((2 * splitBias) * (kp - .5) + 1) * splitValue
                    splitValue = max(splitValue, 0.0)
    
    
                # For each of the splines in this list set the number of splits and then grow it
                for spl in tempList:
    
    
                    #adjust numSplit
                    lastsplit = getattr(spl, 'splitlast', 0)
                    splitVal = splitValue
                    if lastsplit == 0:
                        splitVal = splitValue * 1.33
                    elif lastsplit == 1:
                        splitVal = splitValue * splitValue
    
    
                    elif (n == 0) and (k < ((curveRes[n]-1) * splitHeight)) and (k != 1):
                        numSplit = 0
    
                    elif (n == 0) and (k == int((curveRes[n]-1) * splitHeight) + 1) and (splitVal > 0): #allways split at splitHeight
                        numSplit = 1
    
                        if (n >= 1) and splitByLen:
                            L = ((spl.segL * curveRes[n]) / scaleVal)
                            lf = 1
                            for l in length[:n+1]:
                                lf *= l
                            L = L / lf
                            numSplit = splits2(splitVal * L)
                        else:
                            numSplit = splits2(splitVal)
    
                    if (k == int(curveRes[n] / 2 + 0.5)) and (curveBack[n] != 0):
                        spl.curv += 2 * (curveBack[n] / curveRes[n]) #was -4 *
    
                    growSpline(n, spl, numSplit, splitAngle[n], splitAngleV[n], splineList, handles, splineToBone,
                               closeTip, kp, splitHeight, attractOut[n], stemsegL, lengthV[n], taperCrown, boneStep, rotate, rotateV)
    
    
            # If pruning is enabled then we must to the check to see if the end of the spline is within the evelope
            if prune:
                # Check each endpoint to see if it is inside
                for s in splineList:
                    coordMag = (s.spline.bezier_points[-1].co.xy).length
    
                    ratio = (scaleVal - s.spline.bezier_points[-1].co.z) / (scaleVal * max(1 - pruneBase, 1e-6))
    
                    if (n == 0) and (s.spline.bezier_points[-1].co.z < pruneBase * scaleVal):
    
                        insideBool = True  # Init to avoid UnboundLocalError later
    
                        (coordMag / scaleVal) < pruneWidth * shapeRatio(9, ratio, pruneWidthPeak, prunePowerHigh,
    
                                                                        prunePowerLow))
                    # If the point is not inside then we adjust the scale and current search bounds
                    if not insideBool:
                        oldMax = currentMax
                        currentMax = currentScale
                        currentScale = 0.5 * (currentMax + currentMin)
                        break
                # If the scale is the original size and the point is inside then we need to make sure it won't be pruned or extended to the edge of the envelope
                if insideBool and (currentScale != 1):
                    currentMin = currentScale
                    currentMax = oldMax
                    currentScale = 0.5 * (currentMax + currentMin)
                if insideBool and ((currentMax - currentMin) == 1):
                    currentMin = 1
    
            # If the search will halt on the next iteration then we need to make sure we sprout child points to grow the next splines or leaves
            if (((currentMax - currentMin) < 0.005) or not prune) or forceSprout:
    
                if (n == 0) and (rMode != "original"):
                    tVals = findChildPoints2(splineList, st.children)
                else:
                    tVals = findChildPoints(splineList, st.children)
    
                #print("debug tvals[%d] , splineList[%d], %s" % ( len(tVals), len(splineList), st.children))
                # If leaves is -ve then we need to make sure the only point which sprouts is the end of the spline
                if not st.children:
    
                    tVals = [1.0]
                # remove some of the points because of baseSize
                trimNum = int(baseSize * (len(tVals) + 1))
                tVals = tVals[trimNum:]
    
                #grow branches in rings
                if (n == 0) and (nrings > 0):
                    #tVals = [(floor(t * nrings)) / nrings for t in tVals[:-1]]
                    tVals = [(floor(t * nrings) / nrings) * uniform(.995, 1.005) for t in tVals[:-1]]
                    tVals.append(1)
                    tVals = [t for t in tVals if t > baseSize]
    
                #branch distribution
    
                    tVals = [((t - baseSize) / (1 - baseSize)) for t in tVals]
                    if branchDist < 1.0:
                        tVals = [t ** (1 / branchDist) for t in tVals]
                    else:
                        tVals = [1 - (1 - t) ** branchDist for t in tVals]
                    tVals = [t * (1 - baseSize) + baseSize for t in tVals]
    
    
                # For all the splines, we interpolate them and add the new points to the list of child points
    
                maxOffset = max([s.offsetLen + (len(s.spline.bezier_points) - 1) * s.segL for s in splineList])
    
                    #print(str(n)+'level: ', s.segMax*s.segL)
                    childP.extend(interpStem(s, tVals, s.segMax * s.segL, s.radS, maxOffset, baseSize))
    
    
            # Force the splines to be deleted
            deleteSpline = True
            # If pruning isn't enabled then make sure it doesn't loop
            if not prune:
                startPrune = False
        return ratio, splineToBone
    
    
    #calculate taper automaticly
    def findtaper(length, taper, shape, shapeS, levels, customShape):
        taperS = []
        for i, t in enumerate(length):
            if i == 0:
                shp = 1.0
            elif i == 1:
                shp = shapeRatio(shape, 0, custom=customShape)
            else:
                shp = shapeRatio(shapeS, 0)
            t = t * shp
            taperS.append(t)
    
        taperP = []
        for i, t in enumerate(taperS):
            pm = 1
            for x in range(i+1):
                pm *= taperS[x]
            taperP.append(pm)
    
        taperR = []
        for i, t in enumerate(taperP):
            t = sum(taperP[i:levels])
            taperR.append(t)
    
        taperT = []
        for i, t in enumerate(taperR):
            try:
                t = taperP[i] / taperR[i]
            except ZeroDivisionError:
                t = 1.0
            taperT.append(t)
    
        taperT = [t * taper[i] for i, t in enumerate(taperT)]
    
        return taperT
    
    
    Andrew Hale's avatar
    Andrew Hale committed
    def addTree(props):
    
        global splitError
        #startTime = time.time()
        # Set the seed for repeatable results
        seed(props.seed)#
    
        # Set all other variables
        levels = props.levels#
        length = props.length#
        lengthV = props.lengthV#
    
        taperCrown = props.taperCrown
    
        branches = props.branches#
        curveRes = props.curveRes#
        curve = toRad(props.curve)#
        curveV = toRad(props.curveV)#
        curveBack = toRad(props.curveBack)#
        baseSplits = props.baseSplits#
        segSplits = props.segSplits#
    
        splitByLen = props.splitByLen
        rMode = props.rMode
    
        splitAngle = toRad(props.splitAngle)#
        splitAngleV = toRad(props.splitAngleV)#
        scale = props.scale#
        scaleV = props.scaleV#
        attractUp = props.attractUp#
    
        attractOut = props.attractOut
    
        shapeS = int(props.shapeS)#
        customShape = props.customShape
        branchDist = props.branchDist
        nrings = props.nrings
    
        baseSize_s = props.baseSize_s
        splitHeight = props.splitHeight
        splitBias = props.splitBias
    
        minRadius = props.minRadius
        closeTip = props.closeTip
        rootFlare = props.rootFlare
        autoTaper = props.autoTaper
    
        radiusTweak = props.radiusTweak
    
        ratioPower = props.ratioPower#
        downAngle = toRad(props.downAngle)#
        downAngleV = toRad(props.downAngleV)#
        rotate = toRad(props.rotate)#
        rotateV = toRad(props.rotateV)#
        scale0 = props.scale0#
        scaleV0 = props.scaleV0#
        prune = props.prune#
        pruneWidth = props.pruneWidth#
    
        pruneBase = props.pruneBase
    
        pruneWidthPeak = props.pruneWidthPeak#
        prunePowerLow = props.prunePowerLow#
        prunePowerHigh = props.prunePowerHigh#
        pruneRatio = props.pruneRatio#
    
        leafDownAngle = radians(props.leafDownAngle)
        leafDownAngleV = radians(props.leafDownAngleV)
        leafRotate = radians(props.leafRotate)
        leafRotateV = radians(props.leafRotateV)
    
        leafScale = props.leafScale#
        leafScaleX = props.leafScaleX#
    
        leafScaleT = props.leafScaleT
        leafScaleV = props.leafScaleV
    
        leafDupliObj = props.leafDupliObj
    
        leafangle = props.leafangle
        horzLeaves = props.horzLeaves
    
        leafDist = int(props.leafDist)#
        bevelRes = props.bevelRes#
        resU = props.resU#
    
        previewArm = props.previewArm
    
        leafAnim = props.leafAnim
        frameRate = props.frameRate
        loopFrames = props.loopFrames
    
        #windSpeed = props.windSpeed
        #windGust = props.windGust
    
        wind = props.wind
        gust = props.gust
        gustF = props.gustF
    
        af1 = props.af1
        af2 = props.af2
        af3 = props.af3
    
        makeMesh = props.makeMesh
        armLevels = props.armLevels
        boneStep = props.boneStep
    
        useOldDownAngle = props.useOldDownAngle
        useParentAngle = props.useParentAngle
    
        if not makeMesh:
            boneStep = [1, 1, 1, 1]
    
        #taper
        if autoTaper:
            taper = findtaper(length, taper, shape, shapeS, levels, customShape)
            #pLevels = branches[0]
            #taper = findtaper(length, taper, shape, shapeS, pLevels, customShape)
    
    
        # Some effects can be turned ON and OFF, the necessary variables are changed here
        if not props.bevel:
            bevelDepth = 0.0
        else:
            bevelDepth = 1.0
    
        if not props.showLeaves:
            leaves = 0
        else:
            leaves = props.leaves
    
        if props.handleType == '0':
            handles = 'AUTO'
        else:
            handles = 'VECTOR'
    
        for ob in bpy.data.objects:
            ob.select = False
    
        # Initialise the tree object and curve and adjust the settings
    
        cu = bpy.data.curves.new('tree', 'CURVE')
        treeOb = bpy.data.objects.new('tree', cu)
    
    
    #    treeOb.location=bpy.context.scene.cursor_location attractUp
    
    
        cu.dimensions = '3D'
        cu.fill_mode = 'FULL'
        cu.bevel_depth = bevelDepth
        cu.bevel_resolution = bevelRes
    
        cu.use_uv_as_generated = True
    
        scaleVal = scale + uniform(-scaleV, scaleV)
    
        scaleVal += copysign(1e-6, scaleVal)  # Move away from zero to avoid div by zero
    
        pruneBase = min(pruneBase, baseSize)
    
        # If pruning is turned on we need to draw the pruning envelope
        if prune:
            enHandle = 'VECTOR'
            enNum = 128
    
            enCu = bpy.data.curves.new('envelope', 'CURVE')
            enOb = bpy.data.objects.new('envelope', enCu)
    
            enOb.parent = treeOb
            bpy.context.scene.objects.link(enOb)
            newSpline = enCu.splines.new('BEZIER')
            newPoint = newSpline.bezier_points[-1]
    
            newPoint.co = Vector((0, 0, scaleVal))
            (newPoint.handle_right_type, newPoint.handle_left_type) = (enHandle, enHandle)
    
            # Set the coordinates by varying the z value, envelope will be aligned to the x-axis
            for c in range(enNum):
                newSpline.bezier_points.add()
    
    Andrew Hale's avatar
    Andrew Hale committed
                newPoint = newSpline.bezier_points[-1]
    
                zVal = scaleVal - scaleVal*(1-pruneBase)*ratioVal
                newPoint.co = Vector((scaleVal*pruneWidth*shapeRatio(9, ratioVal, pruneWidthPeak, prunePowerHigh, prunePowerLow), 0, zVal))
                (newPoint.handle_right_type, newPoint.handle_left_type) = (enHandle, enHandle)
    
            newSpline = enCu.splines.new('BEZIER')
            newPoint = newSpline.bezier_points[-1]
    
            newPoint.co = Vector((0, 0, scaleVal))
            (newPoint.handle_right_type, newPoint.handle_left_type) = (enHandle, enHandle)
    
            # Create a second envelope but this time on the y-axis
            for c in range(enNum):
                newSpline.bezier_points.add()
    
    Andrew Hale's avatar
    Andrew Hale committed
                newPoint = newSpline.bezier_points[-1]
    
                zVal = scaleVal - scaleVal*(1-pruneBase)*ratioVal
                newPoint.co = Vector((0, scaleVal*pruneWidth*shapeRatio(9, ratioVal, pruneWidthPeak, prunePowerHigh, prunePowerLow), zVal))
                (newPoint.handle_right_type, newPoint.handle_left_type) = (enHandle, enHandle)
    
        childP = []
        stemList = []
    
        levelCount = []
    
        splineToBone = deque([''])
        addsplinetobone = splineToBone.append
    
    
        # Each of the levels needed by the user we grow all the splines
    
        for n in range(levels):
            storeN = n
            stemList = deque()
            addstem = stemList.append
            # If n is used as an index to access parameters for the tree it must be at most 3 or it will reference outside the array index
    
    
            #closeTip only on last level
            closeTipp = all([(n == levels-1), closeTip])
    
    
            # If this is the first level of growth (the trunk) then we need some special work to begin the tree
            if n == 0:
    
                kickstart_trunk(addstem, levels, leaves, branches, cu, curve, curveRes, curveV, attractUp, length, lengthV, ratio, ratioPower, resU,
                                scale0, scaleV0, scaleVal, taper, minRadius, rootFlare)
    
            # If this isn't the trunk then we may have multiple stem to intialise
            else:
                # For each of the points defined in the list of stem starting points we need to grow a stem.
    
                fabricate_stems(addsplinetobone, addstem, baseSize, branches, childP, cu, curve, curveBack,
                                curveRes, curveV, attractUp, downAngle, downAngleV, leafDist, leaves, length, lengthV,
                                levels, n, ratioPower, resU, rotate, rotateV, scaleVal, shape, storeN,
                                taper, shapeS, minRadius, radiusTweak, customShape, rMode, segSplits,
                                useOldDownAngle, useParentAngle, boneStep)
    
            #change base size for each level
            if n > 0:
                baseSize *= baseSize_s #decrease at each level
            if (n == levels - 1):
                baseSize = 0
    
    
            childP = []
            # Now grow each of the stems in the list of those to be extended
            for st in stemList:
                # When using pruning, we need to ensure that the random effects will be the same for each iteration to make sure the problem is linear.
                randState = getstate()
                startPrune = True
                lengthTest = 0.0
                # Store all the original values for the stem to make sure we have access after it has been modified by pruning
                originalLength = st.segL
                originalCurv = st.curv
                originalCurvV = st.curvV
                originalSeg = st.seg
                originalHandleR = st.p.handle_right.copy()
                originalHandleL = st.p.handle_left.copy()
                originalCo = st.p.co.copy()
                currentMax = 1.0
                currentMin = 0.0
                currentScale = 1.0
                oldMax = 1.0
                deleteSpline = False
                orginalSplineToBone = copy.copy(splineToBone)
                forceSprout = False
                # Now do the iterative pruning, this uses a binary search and halts once the difference between upper and lower bounds of the search are less than 0.005
    
                ratio, splineToBone = perform_pruning(baseSize, baseSplits, childP, cu, currentMax, currentMin,
                                                      currentScale, curve, curveBack, curveRes, deleteSpline, forceSprout,
                                                      handles, n, oldMax, orginalSplineToBone, originalCo, originalCurv,
                                                      originalCurvV, originalHandleL, originalHandleR, originalLength,
                                                      originalSeg, prune, prunePowerHigh, prunePowerLow, pruneRatio,
    
                                                      pruneWidth, pruneBase, pruneWidthPeak, randState, ratio, scaleVal, segSplits,
                                                      splineToBone, splitAngle, splitAngleV, st, startPrune,
                                                      branchDist, length, splitByLen, closeTipp, nrings, splitBias, splitHeight, attractOut, rMode, lengthV,
                                                      taperCrown, boneStep, rotate, rotateV)
    
    
        # If we need to add leaves, we do it here
        leafVerts = []
        leafFaces = []
        leafNormals = []
    
        leafMesh = None # in case we aren't creating leaves, we'll still have the variable
    
        leafP = []
        if leaves:
            oldRot = 0.0
            n = min(3, n+1)
            # For each of the child points we add leaves
            for cp in childP:
                # If the special flag is set then we need to add several leaves at the same location
                if leaves < 0:
                    oldRot = -leafRotate / 2
                    for g in range(abs(leaves)):
                        (vertTemp, faceTemp, normal, oldRot) = genLeafMesh(leafScale, leafScaleX, leafScaleT, leafScaleV, cp.co, cp.quat, cp.offset,
                                                                           len(leafVerts), leafDownAngle, leafDownAngleV, leafRotate, leafRotateV,
                                                                           oldRot, bend, leaves, leafShape, leafangle, horzLeaves)
    
                        leafVerts.extend(vertTemp)
                        leafFaces.extend(faceTemp)
    
                        leafNormals.extend(normal)
                        leafP.append(cp)
                # Otherwise just add the leaves like splines.
                else:
                    (vertTemp, faceTemp, normal, oldRot) = genLeafMesh(leafScale, leafScaleX, leafScaleT, leafScaleV, cp.co, cp.quat, cp.offset,
                                                                       len(leafVerts), leafDownAngle, leafDownAngleV, leafRotate, leafRotateV,
                                                                       oldRot, bend, leaves, leafShape, leafangle, horzLeaves)
                    leafVerts.extend(vertTemp)
                    leafFaces.extend(faceTemp)
                    leafNormals.extend(normal)
                    leafP.append(cp)
    
            # Create the leaf mesh and object, add geometry using from_pydata, edges are currently added by validating the mesh which isn't great
            leafMesh = bpy.data.meshes.new('leaves')
            leafObj = bpy.data.objects.new('leaves', leafMesh)
            bpy.context.scene.objects.link(leafObj)
            leafObj.parent = treeOb
            leafMesh.from_pydata(leafVerts, (), leafFaces)
    
            #set vertex normals for dupliVerts
            if leafShape == 'dVert':
                leafMesh.vertices.foreach_set('normal', leafNormals)
    
            # enable duplication
            if leafShape == 'dFace':
                leafObj.dupli_type = "FACES"
                leafObj.use_dupli_faces_scale = True
                leafObj.dupli_faces_scale = 10.0
                try:
                    bpy.data.objects[leafDupliObj].parent = leafObj
                except KeyError:
                    pass
            elif leafShape == 'dVert':
                leafObj.dupli_type = "VERTS"
                leafObj.use_dupli_vertices_rotation = True
                try:
                    bpy.data.objects[leafDupliObj].parent = leafObj
                except KeyError:
                    pass
    
            #add leaf UVs
            if leafShape == 'rect':
                leafMesh.uv_textures.new("leafUV")
                uvlayer = leafMesh.uv_layers.active.data
    
                u1 = .5 * (1 - leafScaleX)
                u2 = 1 - u1
    
                for i in range(0, len(leafFaces)):
                    uvlayer[i*4 + 0].uv = Vector((u2, 0))
                    uvlayer[i*4 + 1].uv = Vector((u2, 1))
                    uvlayer[i*4 + 2].uv = Vector((u1, 1))
                    uvlayer[i*4 + 3].uv = Vector((u1, 0))
    
            elif leafShape == 'hex':
                leafMesh.uv_textures.new("leafUV")
                uvlayer = leafMesh.uv_layers.active.data
    
                u1 = .5 * (1 - leafScaleX)
                u2 = 1 - u1
    
                for i in range(0, int(len(leafFaces) / 2)):
                    uvlayer[i*8 + 0].uv = Vector((.5, 0))
                    uvlayer[i*8 + 1].uv = Vector((u1, 1/3))
                    uvlayer[i*8 + 2].uv = Vector((u1, 2/3))
                    uvlayer[i*8 + 3].uv = Vector((.5, 1))
    
                    uvlayer[i*8 + 4].uv = Vector((.5, 0))
                    uvlayer[i*8 + 5].uv = Vector((.5, 1))
                    uvlayer[i*8 + 6].uv = Vector((u2, 2/3))
                    uvlayer[i*8 + 7].uv = Vector((u2, 1/3))
    
            leafMesh.validate()
    
        leafVertSize = {'hex': 6, 'rect': 4, 'dFace': 4, 'dVert': 1}[leafShape]
    
        armLevels = min(armLevels, levels)
        armLevels -= 1
    
        # unpack vars from splineToBone
        splineToBone1 = splineToBone
        splineToBone = [s[0] if len(s) > 1 else s for s in splineToBone1]
        isend = [s[1] if len(s) > 1 else False for s in splineToBone1]
        issplit = [s[2] if len(s) > 2 else False for s in splineToBone1]
        splitPidx = [s[3] if len(s) > 2 else 0 for s in splineToBone1]
    
        # If we need an armature we add it
    
            create_armature(armAnim, leafP, cu, frameRate, leafMesh, leafObj, leafVertSize, leaves, levelCount, splineToBone,
                            treeOb, wind, gust, gustF, af1, af2, af3, leafAnim, loopFrames, previewArm, armLevels, makeMesh, boneStep)
    
    
    
    
        #mesh branches
        if makeMesh:
            t1 = time.time()
    
            treeMesh = bpy.data.meshes.new('treemesh')
            treeObj = bpy.data.objects.new('treemesh', treeMesh)
            bpy.context.scene.objects.link(treeObj)
    
            treeVerts = []
            treeEdges = []
            root_vert = []
            vert_radius = []
            vertexGroups = OrderedDict()
            lastVerts = []
    
            for i, curve in enumerate(cu.splines):
                points = curve.bezier_points
    
                #find branching level
                level = 0
                for l, c in enumerate(levelCount):
                    if i < c:
                        level = l
                        break
                level = min(level, 3)
    
                step = boneStep[level]
                vindex = len(treeVerts)
    
                p1 = points[0]
    
                #add extra vertex for splits
                if issplit[i]:
                    pb = int(splineToBone[i][4:-4])
                    pn = splitPidx[i] #int(splineToBone[i][-3:])
                    p_1 = cu.splines[pb].bezier_points[pn]
                    p_2 = cu.splines[pb].bezier_points[pn+1]
                    p = evalBez(p_1.co, p_1.handle_right, p_2.handle_left, p_2.co, 1 - 1/(resU + 1))
                    treeVerts.append(p)
    
                    root_vert.append(False)
                    vert_radius.append((p1.radius * .75, p1.radius * .75))
                    treeEdges.append([vindex,vindex+1])
                    vindex += 1
    
                if isend[i]:
                    parent = lastVerts[int(splineToBone[i][4:-4])]
                    vindex -= 1
                else:
                    #add first point
                    treeVerts.append(p1.co)
                    root_vert.append(True)
                    vert_radius.append((p1.radius, p1.radius))
    
    #            #add extra vertex for splits
    #            if issplit[i]:
    #                p2 = points[1]
    #                p = evalBez(p1.co, p1.handle_right, p2.handle_left, p2.co, .001)
    #                treeVerts.append(p)
    #                root_vert.append(False)
    #                vert_radius.append((p1.radius, p1.radius)) #(p1.radius * .95, p1.radius * .95)
    #                treeEdges.append([vindex,vindex+1])
    #                vindex += 1
    
                #dont make vertex group if above armLevels
                if (i >= levelCount[armLevels]):
                    idx = i
                    groupName = splineToBone[idx]
                    g = True
                    while groupName not in vertexGroups:
                        #find parent bone of parent bone
                        b = splineToBone[idx]
                        idx = int(b[4:-4])
                        groupName = splineToBone[idx]
                else:
                    g = False
    
                for n, p2 in enumerate(points[1:]):
                    if not g:
                        groupName = 'bone' + (str(i)).rjust(3, '0') + '.' + (str(n)).rjust(3, '0')
                        groupName = roundBone(groupName, step)
                        if groupName not in vertexGroups:
                            vertexGroups[groupName] = []
    
                    # parent first vert in split to parent branch bone
                    if issplit[i] and n == 0:
                        if g:
                            vertexGroups[groupName].append(vindex - 1)
                        else:
                            vertexGroups[splineToBone[i]].append(vindex - 1)
    
                    for f in range(1, resU+1):
                        pos = f / resU
                        p = evalBez(p1.co, p1.handle_right, p2.handle_left, p2.co, pos)
                        radius = p1.radius + (p2.radius - p1.radius) * pos
    
                        treeVerts.append(p)
                        root_vert.append(False)
                        vert_radius.append((radius, radius))
    
                        if (isend[i]) and (n == 0) and (f == 1):
                            edge = [parent, n * resU + f + vindex]
                        else:
                            edge = [n * resU + f + vindex - 1, n * resU + f + vindex]
                            #add vert to group
                            vertexGroups[groupName].append(n * resU + f + vindex - 1)
                        treeEdges.append(edge)
    
                    vertexGroups[groupName].append(n * resU + resU + vindex)
    
                    p1 = p2
    
                lastVerts.append(len(treeVerts)-1)
    
            treeMesh.from_pydata(treeVerts, treeEdges, ())
    
            for group in vertexGroups:
                treeObj.vertex_groups.new(group)
                treeObj.vertex_groups[group].add(vertexGroups[group], 1.0, 'ADD')
    
            #add armature
            if useArm:
                armMod = treeObj.modifiers.new('windSway', 'ARMATURE')
                if previewArm:
                    bpy.data.objects['treeArm'].hide = True
                    bpy.data.armatures['tree'].draw_type = 'STICK'
                armMod.object = bpy.data.objects['treeArm']
                armMod.use_bone_envelopes = False
                armMod.use_vertex_groups = True
                treeObj.parent = bpy.data.objects['treeArm']
    
            #add skin modifier and set data
            skinMod = treeObj.modifiers.new('Skin', 'SKIN')
            skinMod.use_smooth_shade = True
            if previewArm:
                skinMod.show_viewport = False
            skindata = treeObj.data.skin_vertices[0].data
            for i, radius in enumerate(vert_radius):
                skindata[i].radius = radius
                skindata[i].use_root = root_vert[i]
    
            print("mesh time", time.time() - t1)