Newer
Older
leafObj.vertex_groups.new(name=group)
leafObj.vertex_groups[group].add(vertexGroups[group], 1.0, 'ADD')
Brendon Murphy
committed
# Now we need the rotation mode to be 'XYZ' to ensure correct rotation
bpy.ops.object.mode_set(mode='OBJECT')
for p in armOb.pose.bones:
p.rotation_mode = 'XYZ'
treeOb.parent = armOb
def kickstart_trunk(addstem, levels, leaves, branches, cu, curve, curveRes,
curveV, attractUp, length, lengthV, ratio, ratioPower,
resU, scale0, scaleV0, scaleVal, taper, minRadius, rootFlare):
Brendon Murphy
committed
newSpline = cu.splines.new('BEZIER')
cu.resolution_u = resU
newPoint = newSpline.bezier_points[-1]
newPoint.co = Vector((0, 0, 0))
newPoint.handle_right = Vector((0, 0, 1))
newPoint.handle_left = Vector((0, 0, -1))
# (newPoint.handle_right_type, newPoint.handle_left_type) = ('VECTOR', 'VECTOR')
branchL = scaleVal * length[0]
curveVal = curve[0] / curveRes[0]
# curveVal = curveVal * (branchL / scaleVal)
if levels == 1:
childStems = leaves
else:
childStems = branches[1]
startRad = scaleVal * ratio * scale0 * uniform(1 - scaleV0, 1 + scaleV0) # * (scale0 + uniform(-scaleV0, scaleV0))
endRad = (startRad * (1 - taper[0])) ** ratioPower
startRad = max(startRad, minRadius)
endRad = max(endRad, minRadius)
newPoint.radius = startRad * rootFlare
Brendon Murphy
committed
addstem(
stemSpline(
newSpline, curveVal, curveV[0] / curveRes[0], attractUp[0],
0, curveRes[0], branchL / curveRes[0],
childStems, startRad, endRad, 0, 0, None
)
)
def 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):
# prevent baseSize from going to 1.0
baseSize = min(0.999, baseSize)
Brendon Murphy
committed
# Store the old rotation to allow new stems to be rotated away from the previous one.
oldRotate = 0
Brendon Murphy
committed
# use fancy child point selection / rotation
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
if (n == 1) and (rMode != "original"):
childP_T = OrderedDict()
childP_L = []
for p in childP:
if p.offset == 1:
childP_L.append(p)
else:
if p.offset not in childP_T:
childP_T[p.offset] = [p]
else:
childP_T[p.offset].append(p)
childP_T = [childP_T[k] for k in sorted(childP_T.keys())]
childP = []
rot_a = []
for p in childP_T:
if rMode == "rotate":
if rotate[n] < 0.0:
oldRotate = -copysign(rotate[n], oldRotate)
else:
oldRotate += rotate[n]
bRotate = oldRotate + uniform(-rotateV[n], rotateV[n])
# choose start point whose angle is closest to the rotate angle
a1 = bRotate % tau
a_diff = []
for a in p:
a2 = atan2(a.co[0], -a.co[1])
d = min((a1 - a2 + tau) % tau, (a2 - a1 + tau) % tau)
a_diff.append(d)
idx = a_diff.index(min(a_diff))
# find actual rotate angle from branch location
br = p[idx]
b = br.co
vx = sin(bRotate)
vy = cos(bRotate)
v = Vector((vx, vy))
bD = ((b[0] * b[0] + b[1] * b[1]) ** .5)
bL = br.lengthPar * length[1] * shapeRatio(shape, (1 - br.offset) / (1 - baseSize), custom=customShape)
if downAngleV[1] > 0:
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:
childP.extend(childP_L)
rot_a.extend([0] * len(childP_L))
oldRotate = 0
for i, p in enumerate(childP):
Brendon Murphy
committed
# 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])
Brendon Murphy
committed
else:
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
Brendon Murphy
committed
if rotate[n] < 0.0:
oldRotate = -copysign(rotate[n], oldRotate)
Brendon Murphy
committed
# 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')
Brendon Murphy
committed
# Rotate the direction of growth and set the new point coordinates
tempPos.rotate(downRotMat)
Brendon Murphy
committed
tempPos.rotate(rotMat)
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)
Brendon Murphy
committed
newPoint.handle_right = p.co + tempPos
# 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
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):
Brendon Murphy
committed
if leaves < 0:
childStems = False
else:
childStems = leaves * (0.1 + 0.9 * (branchL / maxbL)) * shapeRatio(leafDist, (1 - p.offset))
Brendon Murphy
committed
# print("n=%d, levels=%d, n'=%d, childStems=%s"%(n, levels, storeN, childStems))
Brendon Murphy
committed
# 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)
Brendon Murphy
committed
newPoint.radius = startRad
# stem curvature
curveVal = curve[n] / curveRes[n]
curveVar = curveV[n] / curveRes[n]
# curveVal = curveVal * (branchL / scaleVal)
Brendon Murphy
committed
# 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))
Brendon Murphy
committed
def perform_pruning(baseSize, baseSplits, childP, cu, currentMax, currentMin, currentScale, curve,
curveBack, curveRes, deleteSpline, forceSprout, handles, n, oldMax, originalSplineToBone,
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):
Brendon Murphy
committed
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
Brendon Murphy
committed
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
Brendon Murphy
committed
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 = originalSplineToBone
Brendon Murphy
committed
# 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
Brendon Murphy
committed
# 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))
splitValue = segSplits[n]
if n == 0:
splitValue = ((2 * splitBias) * (kp - .5) + 1) * splitValue
splitValue = max(splitValue, 0.0)
Brendon Murphy
committed
# For each of the splines in this list set the number of splits and then grow it
for spl in tempList:
lastsplit = getattr(spl, 'splitlast', 0)
splitVal = splitValue
if lastsplit == 0:
splitVal = splitValue * 1.33
elif lastsplit == 1:
splitVal = splitValue * splitValue
Brendon Murphy
committed
if k == 0:
numSplit = 0
elif (n == 0) and (k < ((curveRes[n] - 1) * splitHeight)) and (k != 1):
Brendon Murphy
committed
elif (k == 1) and (n == 0):
numSplit = baseSplits
# always split at splitHeight
elif (n == 0) and (k == int((curveRes[n] - 1) * splitHeight) + 1) and (splitVal > 0):
Brendon Murphy
committed
else:
if (n >= 1) and splitByLen:
L = ((spl.segL * curveRes[n]) / scaleVal)
lf = 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
)
Brendon Murphy
committed
# If pruning is enabled then we must check to see if the end of the spline is within the envelope
Brendon Murphy
committed
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))
Brendon Murphy
committed
# Don't think this if part is needed
if (n == 0) and (s.spline.bezier_points[-1].co.z < pruneBase * scaleVal):
insideBool = True # Init to avoid UnboundLocalError later
Brendon Murphy
committed
else:
insideBool = (
(coordMag / scaleVal) < pruneWidth * shapeRatio(9, ratio, pruneWidthPeak, prunePowerHigh,
Brendon Murphy
committed
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
Brendon Murphy
committed
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
Brendon Murphy
committed
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))
Brendon Murphy
committed
# 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:]
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]
Brendon Murphy
committed
if n == 0:
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]
Brendon Murphy
committed
# 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])
Brendon Murphy
committed
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))
Brendon Murphy
committed
# 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 automatically
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
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
Brendon Murphy
committed
Campbell Barton
committed
global splitError
Campbell Barton
committed
# Set the seed for repeatable results
Campbell Barton
committed
# 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
shape = int(props.shape)
shapeS = int(props.shapeS)
customShape = props.customShape
branchDist = props.branchDist
nrings = props.nrings
Campbell Barton
committed
baseSize = props.baseSize
baseSize_s = props.baseSize_s
splitHeight = props.splitHeight
splitBias = props.splitBias
Campbell Barton
committed
ratio = props.ratio
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
Campbell Barton
committed
leafShape = props.leafShape
leafDupliObj = props.leafDupliObj
leafangle = props.leafangle
horzLeaves = props.horzLeaves
leafDist = int(props.leafDist)
bevelRes = props.bevelRes
resU = props.resU
Campbell Barton
committed
useArm = props.useArm
previewArm = props.previewArm
Campbell Barton
committed
armAnim = props.armAnim
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]
if autoTaper:
taper = findtaper(length, taper, shape, shapeS, levels, customShape)
# pLevels = branches[0]
# taper = findtaper(length, taper, shape, shapeS, pLevels, customShape)
Campbell Barton
committed
leafObj = None
Campbell Barton
committed
# 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
Campbell Barton
committed
if not props.showLeaves:
leaves = 0
else:
leaves = props.leaves
Campbell Barton
committed
if props.handleType == '0':
handles = 'AUTO'
else:
handles = 'VECTOR'
for ob in bpy.context.view_layer.objects:
Campbell Barton
committed
# Initialise the tree object and curve and adjust the settings
cu = bpy.data.curves.new('tree', 'CURVE')
treeOb = bpy.data.objects.new('tree', cu)
bpy.context.scene.collection.objects.link(treeOb)
# treeOb.location=bpy.context.scene.cursor.location attractUp
Campbell Barton
committed
cu.dimensions = '3D'
cu.fill_mode = 'FULL'
cu.bevel_depth = bevelDepth
cu.bevel_resolution = bevelRes
# Fix the scale of the tree now
scaleVal = scale + uniform(-scaleV, scaleV)
scaleVal += copysign(1e-6, scaleVal) # Move away from zero to avoid div by zero
Campbell Barton
committed
pruneBase = min(pruneBase, baseSize)
Campbell Barton
committed
# 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)
Campbell Barton
committed
enOb.parent = treeOb
bpy.context.scene.collection.objects.link(enOb)
Campbell Barton
committed
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)
Campbell Barton
committed
# 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(1)
ratioVal = (c + 1) / (enNum)
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)
Campbell Barton
committed
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)
Campbell Barton
committed
# Create a second envelope but this time on the y-axis
for c in range(enNum):
newSpline.bezier_points.add(1)
ratioVal = (c + 1) / (enNum)
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)
Campbell Barton
committed
childP = []
stemList = []
levelCount = []
Campbell Barton
committed
splineToBone = deque([''])
addsplinetobone = splineToBone.append
# Each of the levels needed by the user we grow all the splines
Campbell Barton
committed
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
Campbell Barton
committed
splitError = 0.0
# closeTip only on last level
closeTipp = all([(n == levels - 1), closeTip])
Campbell Barton
committed
# 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 initialise
Campbell Barton
committed
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)
baseSize *= baseSize_s # decrease at each level
if (n == levels - 1):
baseSize = 0
Campbell Barton
committed
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
Campbell Barton
committed
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
Campbell Barton
committed
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
originalSplineToBone = copy.copy(splineToBone)
Campbell Barton
committed
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, originalSplineToBone, 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
)
Campbell Barton
committed
levelCount.append(len(cu.splines))
# 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
# 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
)
Campbell Barton
committed
leafVerts.extend(vertTemp)
leafFaces.extend(faceTemp)
leafNormals.extend(normal)
leafP.append(cp)
# Otherwise just add the leaves like splines
(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.collection.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.instance_type = "FACES"
leafObj.use_instance_faces_scale = True
leafObj.instance_faces_scale = 10.0
if leafDupliObj not in "NONE":
bpy.data.objects[leafDupliObj].parent = leafObj
except KeyError:
pass
elif leafShape == 'dVert':
leafObj.instance_type = "VERTS"
leafObj.use_instance_vertices_rotation = True
if leafDupliObj not in "NONE":
bpy.data.objects[leafDupliObj].parent = leafObj
except KeyError:
pass
leafMesh.uv_layers.new(name='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_layers.new(name='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
Campbell Barton
committed
if useArm:
# Create the armature and objects
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
)
if makeMesh:
t1 = time.time()
treeMesh = bpy.data.meshes.new('treemesh')
treeObj = bpy.data.objects.new('treemesh', treeMesh)
bpy.context.scene.collection.objects.link(treeObj)
treeVerts = []
treeEdges = []
root_vert = []
vert_radius = []
vertexGroups = OrderedDict()
lastVerts = []
for i, curve in enumerate(cu.splines):
points = curve.bezier_points
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]
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:
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:
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)
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]
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(name=group)
treeObj.vertex_groups[group].add(vertexGroups[group], 1.0, 'ADD')
if useArm:
armMod = treeObj.modifiers.new('windSway', 'ARMATURE')
if previewArm:
bpy.data.objects['treeArm'].hide_viewport = True
bpy.data.armatures['tree'].display_type = 'STICK'
armMod.object = bpy.data.objects['treeArm']